diff -Nru atomicparsley-0.9.6/autogen.sh atomicparsley-20210715.151551.e7ad03a/autogen.sh --- atomicparsley-0.9.6/autogen.sh 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/autogen.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -#!/bin/sh -# vim:ts=2:sw=2:et: -aclocal -autoheader -automake --add-missing --foreign -autoconf diff -Nru atomicparsley-0.9.6/CMakeLists.txt atomicparsley-20210715.151551.e7ad03a/CMakeLists.txt --- atomicparsley-0.9.6/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/CMakeLists.txt 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.16) +project(AtomicParsley) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +option(ASAN "whether to enable ASAN" OFF) + +find_program(GIT git) +if(GIT) + execute_process( + COMMAND "${GIT}" "show" "-s" "--format=%H;%cd" "--date=format:%Y%m%d.%H%M%S.0" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE git_result + OUTPUT_VARIABLE git_data + ERROR_VARIABLE git_err + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(git_result EQUAL 0) + list(GET git_data 0 BUILD_INFO) + list(GET git_data 1 PACKAGE_VERSION) + endif() +endif() + +include(CheckSymbolExists) +check_symbol_exists(strsep "string.h" HAVE_STRSEP) +if(HAVE_STRSEP) + add_definitions(-DHAVE_STRSEP) +endif() +check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO) +if(HAVE_FSEEKO) + add_definitions(-DHAVE_FSEEKO) +endif() + +add_definitions( + -DPACKAGE_VERSION="${PACKAGE_VERSION}" + -DBUILD_INFO="${BUILD_INFO}" + -D_FILE_OFFSET_BITS=64 +) + +find_package(ZLIB) +if(ZLIB_FOUND) + include_directories(${ZLIB_INCLUDE_DIRS}) + add_definitions(-DHAVE_ZLIB_H) +endif() + +list(APPEND sources + src/CDtoc.cpp + src/arrays.cpp + src/compress.cpp + src/extracts.cpp + src/iconv.cpp + src/id3v2.cpp + src/main.cpp + src/metalist.cpp + src/parsley.cpp + src/sha1.cpp + src/util.cpp + src/uuid.cpp +) + +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + list(APPEND sources + src/nsfile.mm + src/nsimage.mm + ) +endif() + +if(WIN32) + list(APPEND sources + src/extras/getopt.c + src/extras/getopt1.c + ) +endif() + +add_executable( + AtomicParsley + ${sources} +) + +if(ZLIB_FOUND) + target_link_libraries( + AtomicParsley + ${ZLIB_LIBRARIES} + ) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + target_link_libraries( + AtomicParsley + "-framework Cocoa" + "-framework Foundation" + "-framework IOKit" + ) +endif() + +if (ASAN) + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() + +install(TARGETS AtomicParsley RUNTIME) diff -Nru atomicparsley-0.9.6/configure.ac atomicparsley-20210715.151551.e7ad03a/configure.ac --- atomicparsley-0.9.6/configure.ac 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/configure.ac 1970-01-01 00:00:00.000000000 +0000 @@ -1,193 +0,0 @@ -dnl vim:ts=2:sw=2:et: -dnl Process this file with autoconf to produce a configure script. - -dnl Some intro checks and defines - -AC_PREREQ(2.50) - -AC_INIT([atomicparsley],[0.9.6], - [http://bitbucket.org/wez/atomicparsley/issues/new/]) - -dnl Need to disable dependency tracking option so that the universal -dnl build option works for OSX -AM_INIT_AUTOMAKE([dist-bzip2 foreign no-dependencies subdir-objects -Wall]) - -AC_CANONICAL_HOST - -AC_ARG_ENABLE(universal, dnl -[ --enable-universal build a universal binary on Mac OS X [default=no]], - universal=$enableval, universal=no) - -case $host in - *darwin*) - - AP_NATIVE_ARCH=`arch` - AP_64_ARCH="x86_64" - case $AP_NATIVE_ARCH in - ppc*) - AP_CROSS_ARCH="i386" - ;; - *) - AP_CROSS_ARCH="ppc" - ;; - esac - AC_SUBST(AP_CROSS_ARCH) - AC_SUBST(AP_NATIVE_ARCH) - AC_SUBST(AP_64_ARCH) - - HAVE_DARWIN_PLATFORM="true" - AC_SUBST(HAVE_DARWIN_PLATFORM) - - AC_MSG_CHECKING([if we are os/x universal]) - if test "$universal" = "yes" ; then - AC_MSG_RESULT([building for $AP_NATIVE_ARCH, $AP_CROSS_ARCH, and $AP_64_ARCH]) - UNIVERSAL_FLAGS="-arch $AP_NATIVE_ARCH -arch $AP_CROSS_ARCH -arch $AP_64_ARCH" - AC_SUBST(UNIVERSAL_FLAGS) - fi - AC_MSG_RESULT([no]) - ;; - *) - universal="no" - HAVE_DARWIN_PLATFORM="false" - ;; -esac - -AC_SUBST(universal) - -AC_CONFIG_SRCDIR(src/parsley.cpp) -AC_CONFIG_HEADER(src/config.h) - -AM_PROG_CC_C_O -AC_PROG_CXX -if test "$GCC" = "yes" ; then - CFLAGS="$CFLAGS -Wall" - CXXFLAGS="$CXXFLAGS -Wall" -fi -AC_PROG_OBJC -# Thanks automake; we MUST invoke AC_PROG_OBJCXX even though -# we roll our own objc++ build support, because automake 1.12.1 -# throws a fatal error when it finds our .mm sources. -# We do this because there is no good reason to bump the minimum -# autoconf and automake dependencies. -m4_ifdef([AC_PROG_OBJCXX], [ -AC_PROG_OBJCXX -]) -AC_C_BIGENDIAN - -AC_HEADER_DIRENT -AC_HEADER_STDC -AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([\ -fcntl.h \ -inttypes.h \ -io.h \ -getopt.h \ -linux/cdrom.h \ -math.h \ -signal.h \ -stddef.h \ -stdint.h \ -stdio.h \ -stdlib.h \ -string.h \ -sys/ioctl.h \ -sys/param.h \ -sys/mount.h \ -sys/stat.h \ -sys/types.h \ -sys/time.h \ -time.h \ -errno.h \ -zlib.h \ -linux/cdrom.h \ -unistd.h \ -wchar.h \ -windows.h \ -]) - -AC_CHECK_HEADERS(getopt.h, [HAVE_GETOPT_H=1; AC_SUBST(HAVE_GETOPT_H)]) - -AC_C_CONST -AC_TYPE_UINT8_T -AC_TYPE_UINT16_T -AC_TYPE_UINT32_T -AC_TYPE_UINT64_T -AC_TYPE_UINTMAX_T -AC_TYPE_UINTPTR_T -AC_TYPE_SIZE_T -AC_TYPE_SSIZE_T -AC_TYPE_INT16_T - -AC_CHECK_FUNCS([\ -fseeko \ -fsetpos \ -lroundf \ -memset \ -memcmp \ -remove \ -rename \ -sranddev \ -sscanf \ -strdup \ -strerror \ -strftime \ -strncmp \ -strncasecmp \ -strrchr \ -strsep \ -strstr \ -strtol \ -wmemset \ -]) - -AC_FUNC_MALLOC - -AC_CHECK_LIB(z, deflateEnd, [ - HAVE_LIBZ=1 - AC_SUBST(HAVE_LIBZ) - LIBS="$LIBS -lz" -],[ - AC_MSG_ERROR([zlib is required]) -] -) - -CFLAGS="-D_FILE_OFFSET_BITS=64 $CFLAGS" -CXXFLAGS="-D_FILE_OFFSET_BITS=64 $CXXFLAGS" - -AC_ARG_ENABLE(debug, dnl -[ --disable-debug do not build a debug version [default=yes]], - debug=$enableval, debug=no) -if test "$debug" = "yes" ; then - AC_DEFINE_UNQUOTED(DEBUG, $debug, [build binary with debug output]) - AC_SUBST(debug) -fi - -AM_CONDITIONAL([NEED_GETOPT], [test x$HAVE_GETOPT_H = x]) -AM_CONDITIONAL([DARWIN], [test x$HAVE_DARWIN_PLATFORM = xtrue]) -AM_CONDITIONAL([UNIVERSAL], [test x$universal = xyes]) - -AC_OUTPUT([Makefile]) - -echo "+----------------------------------------------+" -echo "| SUCCESS |" -echo "+----------------------------------------------+" -echo " AtomicParsley has been configured, you should" -echo " now type 'make' to compile AtomicParsley." -echo -echo "+----------------------------------------------+" -echo "| YOUR CONFIGURATION |" -echo "+----------------------------------------------+" - -echo " Version: $PACKAGE_VERSION" - -if test "$universal" = "yes" ; then - echo " universal build: enabled" -fi - -if test "$debug" = "no" ; then - echo " debug build: disabled" -else - echo " debug build: enabled" -fi - -echo diff -Nru atomicparsley-0.9.6/debian/changelog atomicparsley-20210715.151551.e7ad03a/debian/changelog --- atomicparsley-0.9.6/debian/changelog 2020-03-22 15:33:37.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/changelog 2022-09-07 04:19:45.000000000 +0000 @@ -1,8 +1,42 @@ -atomicparsley (0.9.6-2build1) focal; urgency=medium +atomicparsley (20210715.151551.e7ad03a-1~20.04.sav0) focal; urgency=medium - * No-change rebuild for libgcc-s1 package name change. + * Backport to Focal + * debian/control: Set debhelper-compat (= 12) BD - -- Matthias Klose Sun, 22 Mar 2020 16:33:37 +0100 + -- Rob Savoury Tue, 06 Sep 2022 21:19:45 -0700 + +atomicparsley (20210715.151551.e7ad03a-1) unstable; urgency=medium + + [ upstream ] + * new release(s); + closes: bug#782187, thanks to Mathieu Malaterre; + closes: bug#993366, #993372, thanks to Neil Williams, + CVE-2021-37231 CVE-2021-37232 + + [ Jonas Smedegaard ] + * move maintenance to the Debian team on Salsa + * update git-buildpackage config: + + use DEP-14 branches upstream/latest debian/latest + + stop set explicit compression method + + avoid any .git* files + * update watch file: track GitHub source + * update copyright info: + + list GitHub as source + + list GitHub issue tracker as preferred upstream contact + + use Reference field (not License-Reference); + tighten lintian overrides + + update coverage + * declare compliance with Debian Policy 4.6.0 + * use debhelper compatibility level 13 (not 9); + build-depend on debhelper-compat (not debhelper); + stop build-depend explicitly on dh-autoreconf + * simplify source helper script copyright-check + * list GitHub as Homepage; + closes: bug#987034, thanks to Brian Sammon + * stop explicitly install executable; + build-depend on cmake + + -- Jonas Smedegaard Fri, 17 Sep 2021 11:26:19 +0200 atomicparsley (0.9.6-2) unstable; urgency=medium diff -Nru atomicparsley-0.9.6/debian/compat atomicparsley-20210715.151551.e7ad03a/debian/compat --- atomicparsley-0.9.6/debian/compat 2019-02-24 11:17:21.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -9 diff -Nru atomicparsley-0.9.6/debian/control atomicparsley-20210715.151551.e7ad03a/debian/control --- atomicparsley-0.9.6/debian/control 2019-02-24 13:43:55.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/control 2022-09-07 04:19:45.000000000 +0000 @@ -1,16 +1,16 @@ Source: atomicparsley Section: video Priority: optional -Homepage: http://atomicparsley.sourceforge.net/ -Maintainer: Debian Multimedia Maintainers -Uploaders:Jonas Smedegaard +Homepage: https://github.com/wez/atomicparsley +Maintainer: Jonas Smedegaard +Uploaders: Build-Depends: - debhelper, + cmake, + debhelper-compat (= 12), libz-dev, - dh-autoreconf -Vcs-Git: https://salsa.debian.org/multimedia-team/atomicparsley.git -Vcs-Browser: https://salsa.debian.org/multimedia-team/atomicparsley -Standards-Version: 4.3.0 +Vcs-Git: https://salsa.debian.org/debian/atomicparsley.git +Vcs-Browser: https://salsa.debian.org/debian/atomicparsley +Standards-Version: 4.6.0 Rules-Requires-Root: no Package: atomicparsley diff -Nru atomicparsley-0.9.6/debian/copyright atomicparsley-20210715.151551.e7ad03a/debian/copyright --- atomicparsley-0.9.6/debian/copyright 2019-02-24 13:43:55.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/copyright 2021-09-17 09:03:32.000000000 +0000 @@ -1,18 +1,18 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: AtomicParsley -Upstream-Contact: https://bitbucket.org/wez/atomicparsley/issues -Source: https://bitbucket.org/wez/atomicparsley - hg::https://bitbucket.org/wez/atomicparsley +Upstream-Contact: https://github.com/wez/atomicparsley/issues +Source: https://github.com/wez/atomicparsley Files: * -Copyright: 2005-2007, puck_lock@users.sourceforge.net - 2009, 2011, Santino Fuentes - 2009, Josh Aune - 2009-2012, Wez Furlong - 2010, Edriss Mirzadeh - 2010-2012, Oleg Oshmyan - 2011, John D Pell - 2014, Paul Foose +Copyright: + 2010 Edriss Mirzadeh + 2011 John D Pell + 2009 Josh Aune + 2010-2012 Oleg Oshmyan + 2014 Paul Foose + 2005-2007 puck_lock@users.sourceforge.net + 2009, 2011 Santino Fuentes + 2009-2012 Wez Furlong License-Grant: AtomicParsley is GPL software; you can freely distribute, redistribute, modify & use @@ -23,7 +23,8 @@ Files: src/extras/* src/ap_types.h src/sha1.cpp -Copyright: 1987-1998,2000-2001,2003-2005, Free Software Foundation, Inc. +Copyright: + 1987-1998, 2000-2001, 2003-2005 Free Software Foundation, Inc. License-Grant: This program is free software; you can redistribute it and/or modify it @@ -33,8 +34,9 @@ License: GPL-2+ Files: src/iconv.cpp -Copyright: 1998-2003, Daniel Veillard - 2005-2007, puck_lock@users.sourceforge.net +Copyright: + 1998-2003 Daniel Veillard + 2005-2007 puck_lock@users.sourceforge.net License-Grant: AtomicParsley is GPL software; you can freely distribute, redistribute, modify & use @@ -48,11 +50,12 @@ without prior written authorization from him. Files: src/uuid.cpp -Copyright: 1989, Digital Equipment Corporation, Maynard, Mass. - 1989, Hewlett-Packard Company, Palo Alto, Ca. - 1998, Microsoft - 1990-1993, 1996, Open Software Foundation, Inc - 2006-2007, puck_lock@users.sourceforge.net +Copyright: + 1989 Digital Equipment Corporation, Maynard, Mass. + 1989 Hewlett-Packard Company, Palo Alto, Ca. + 1998 Microsoft + 1990-1993, 1996 Open Software Foundation, Inc + 2006-2007 puck_lock@users.sourceforge.net License-Grant: AtomicParsley is GPL software; you can freely distribute, redistribute, modify & use @@ -67,14 +70,16 @@ under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. -Copyright: 2009-2010,2012-2013,2015-2019, Jonas Smedegaard +Copyright: + 2009-2010, 2012-2013, 2015-2021 Jonas Smedegaard License: GPL-3+ +Reference: debian/copyright License: GPL-2+ -License-Reference: /usr/share/common-licenses/GPL-2 +Reference: /usr/share/common-licenses/GPL-2 License: GPL-3+ -License-Reference: /usr/share/common-licenses/GPL-3 +Reference: /usr/share/common-licenses/GPL-3 License: Expat Permission is hereby granted, free of charge, diff -Nru atomicparsley-0.9.6/debian/copyright-check atomicparsley-20210715.151551.e7ad03a/debian/copyright-check --- atomicparsley-0.9.6/debian/copyright-check 2019-02-04 01:46:59.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/copyright-check 2021-08-31 14:09:37.000000000 +0000 @@ -1,29 +1,3 @@ #!/bin/sh -# Copyright 2016-2019, Jonas Smedegaard -# Description: helper script to update copyright_hints -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -set -eu - -# extract metadata from graphics files before copyright check -# - and skip data files unlikely to contain copyright/licensing info. -export DEB_COPYRIGHT_EXTRACT_EXTS="gif jpg png" -export DEB_COPYRIGHT_CHECK_IGNORE_EXTS="epgz ico" - -make -f /usr/share/cdbs/1/rules/utils.mk pre-build || true -make -f /usr/share/cdbs/1/rules/utils.mk clean DEB_COPYRIGHT_CHECK_STRICT=1 - -# unconditionally merge changes - safe to do with git-tracked package -[ ! -f debian/copyright_newhints ] || mv -f debian/copyright_newhints debian/copyright_hints +licensecheck --check '.*' --recursive --copyright --deb-machine --ignore '^(debian/(changelog|copyright(_hints)?))$' --lines 0 -- * > debian/copyright_hints diff -Nru atomicparsley-0.9.6/debian/copyright_hints atomicparsley-20210715.151551.e7ad03a/debian/copyright_hints --- atomicparsley-0.9.6/debian/copyright_hints 2019-02-24 13:43:55.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/copyright_hints 2021-09-17 08:56:23.000000000 +0000 @@ -2,110 +2,79 @@ Upstream-Name: FIXME Upstream-Contact: FIXME Source: FIXME -Disclaimer: Autogenerated by CDBS +Disclaimer: Autogenerated by licensecheck -Files: Makefile.am +Files: CMakeLists.txt README.md - autogen.sh - configure.ac debian/AtomicParsley debian/compat debian/control + debian/copyright-check + debian/docs debian/gbp.conf debian/install debian/rules debian/source/format debian/watch + src/.clang-format + tests/issue-32.mp4 + tests/test.sh + tools/format-code.sh tools/iTunMOVI-1.1.pl + tools/tag-release.sh Copyright: NONE License: UNKNOWN FIXME Files: src/AtomDefs.h src/AtomicParsley.h - src/CDtoc.cpp src/CDtoc.h - src/arrays.cpp - src/compress.cpp - src/extracts.cpp - src/id3v2.cpp src/id3v2.h src/nsfile.mm src/nsimage.mm src/util.h +Copyright: NONE +License: GPL-2 + FIXME + +Files: src/CDtoc.cpp + src/arrays.cpp + src/compress.cpp + src/extracts.cpp + src/id3v2.cpp + src/metalist.cpp + src/parsley.cpp Copyright: 2005-2007, puck_lock 2006-2007, puck_lock -License: GPL +License: GPL-2 FIXME Files: src/iconv.cpp Copyright: 1998-2003, Daniel Veillard. 2005-2007, puck_lock -License: Expat and/or GPL +License: Expat and/or GPL-2 FIXME Files: src/main.cpp -Copyright: (str) [movie|track|track=# - (string) Set the copyright tagn" - -x (str) Set the copyright tag: "moov.udta.meta.ilst.cprt.data"n" +Copyright: (str) [lang=3str] [UTF16] [area] ......... " + (str) [movie|track|track=#] [lang=3str] [UTF16] " + (string) Set the copyright tag " 2005-2007, puck_lock - ART.data", optarg, AtomFlags_Data_Text); - ART.data"n" - Data_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.cprt.data", optarg, AtomFlags_Data_Text); - Data_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - ISO copyright atom common to all files that are derivatives of the base media file format, identical to.... - alb.data", optarg, AtomFlags_Data_Text); - alb.data"n" - ard.data", optarg, AtomFlags_Data_Text); - arg.data", optarg, AtomFlags_Data_Text); - asset can potentially be altered by using the --ISO-copyright setting.n" - asset; this gets a test for major branding (but only with the cli arg --3gp-copyright). - aut.data", optarg, AtomFlags_Data_Text); - can potentially overwrite the 3gp copyrightn" - characters - cmt.data", optarg, AtomFlags_Data_Text); - cmt.data"n" - con.data", optarg, AtomFlags_Data_Text); - day.data", optarg, AtomFlags_Data_Text); - day.data"n" - dir.data", optarg, AtomFlags_Data_Text); - enc" atomn" - enc.data", optarg, AtomFlags_Data_Text); - foo" 1 'http:www.url.org' --meta-uuid "pdf1" - gen" (custom) or "gnre" (standard).n" - grp.data", optarg, AtomFlags_Data_Text); - grp.data"n" - help available with AtomicParsley --ISO-help)n" - inf", AtomFlags_Data_Text, optarg, false); - inf"n" - lyr.data", optarg, AtomFlags_Data_Text); - lyr.data"n" - nam.data", optarg, AtomFlags_Data_Text); - nam.data"n" - notifications.n" - ope.data", optarg, AtomFlags_Data_Text); - prd.data", optarg, AtomFlags_Data_Text); - prf.data", optarg, AtomFlags_Data_Text); - set the string to "" - the track and language must match the target.n" - sne.data", optarg, AtomFlags_Data_Text); - sol.data", optarg, AtomFlags_Data_Text); - st3.data", optarg, AtomFlags_Data_Text); - tag that can be set. This ISO tag is identical to then" - too" atomn" - too.data", optarg, AtomFlags_Data_Text); - url", AtomFlags_Data_Text, optarg, false); - url"n" - wrt.data", optarg, AtomFlags_Data_Text); - wrt.data"n" - xpd.data", optarg, AtomFlags_Data_Text); -License: GPL + asset can potentially be altered by using the " + asset; this gets a test for + atom common to all files that are + atom is " + can potentially " + help available with AtomicParsley --ISO-help) " + set the string to "" - the track and " + setting. " +License: GPL-2 FIXME Files: src/util.cpp Copyright: 1992, 93, 96, 97, 98, 99, 2004 Free Software Foundation, Inc. - 2005, 2006-2007, puck_lock -License: GPL +License: GPL-2 FIXME Files: CREDITS @@ -122,48 +91,31 @@ 2010-2012, Oleg Oshmyan 2011, John D Pell 2014, Paul Foose -License: GPL +License: GPL-2 FIXME -Files: src/uuid.cpp -Copyright: 1989, Hewlett-Packard Company, Palo Alto, Ca. & - 1990-1993, 1996, Open Software Foundation, Inc. - 1998, Microsoft. - 2006-2007, puck_lock -License: GPL +Files: COPYING +Copyright: 1989, 1991, Free Software Foundation, Inc. +License: GPL-2 FIXME Files: src/id3v2defs.h -Copyright: 2006-2007, puck_lock - ID3_URL_FRAME }, - message", "copyright", ID3v2_FRAME_COPYRIGHT, ID3_TEXT_FRAME }, -License: GPL +Copyright: ID3_URL_FRAME}, + message", +License: GPL-2 FIXME Files: src/id3v2types.h -Copyright: 2006-2007, puck_lock - ID3v2_FRAME_ENCODINGTIME, - ID3v2_FRAME_URLAUDIOFILE, -License: GPL - FIXME - -Files: src/parsley.cpp -Copyright: 2005-2007, puck_lock - gen") exists; erase the custom-string genre atom in favor of the standard genre atom - gen", 4) == 0) { - gen"; - gen' atom. Only one or the other can be present. So if atomPayload is a - gen.data"; - lyr.data", true, VERSIONED_ATOM, 0); - symbol which unicode.org's ConvertUTF16toUTF8 didn't -License: GPL +Copyright: ID3v2_FRAME_URLAUDIOFILE, +License: GPL-2 FIXME -Files: src/metalist.cpp -Copyright: 2006-2007, puck_lock / - in '©ART' to a 2byte utf8 © glyph - in '©ART' to a 2byte utf8 © glyph; replaces libiconv conversion -License: GPL +Files: src/uuid.cpp +Copyright: 1989, Hewlett-Packard Company, Palo Alto, Ca. & Digital + 1990-1993, 1996, Open Software Foundation, + 1998, Microsoft. To anyone + 2006-2007, puck_lock +License: GPL-2 and/or MIT~OSF FIXME Files: src/extras/getopt.c @@ -186,25 +138,11 @@ License: GPL-2+ FIXME -Files: debian/copyright-check -Copyright: 2016-2019, Jonas Smedegaard - check -License: GPL-3+ - FIXME - Files: src/ap_types.h Copyright: 2000-2001, 2003, 2005, Free Software Foundation, Inc. License: UNKNOWN FIXME -Files: COPYING -Copyright: 1989, 1991, Free Software Foundation, Inc. - ed by the Free - ed interfaces, the - the software, and -License: UNKNOWN - FIXME - Files: debian/source/lintian-overrides Copyright: GPL-2+ GPL-3+ @@ -214,8 +152,7 @@ FIXME Files: Changes.txt -Copyright: at movie or track level - ed1" (edit date-might only really +Copyright: ed1" (edit date-might only really gen/gnre nam' under Windows for trees & atom printouts url" to hold url diff -Nru atomicparsley-0.9.6/debian/gbp.conf atomicparsley-20210715.151551.e7ad03a/debian/gbp.conf --- atomicparsley-0.9.6/debian/gbp.conf 2019-02-24 13:16:23.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/gbp.conf 2021-09-17 08:50:09.000000000 +0000 @@ -3,5 +3,6 @@ [DEFAULT] pristine-tar = True sign-tags = True -compression = bzip2 -filter = */.hg* +filter = */.git* +upstream-branch = upstream/latest +debian-branch = debian/latest diff -Nru atomicparsley-0.9.6/debian/install atomicparsley-20210715.151551.e7ad03a/debian/install --- atomicparsley-0.9.6/debian/install 2019-02-24 11:17:21.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -AtomicParsley /usr/bin diff -Nru atomicparsley-0.9.6/debian/rules atomicparsley-20210715.151551.e7ad03a/debian/rules --- atomicparsley-0.9.6/debian/rules 2019-02-24 13:39:17.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/rules 2021-09-17 09:04:26.000000000 +0000 @@ -1,4 +1,4 @@ #!/usr/bin/make -f %: - dh $@ --with autoreconf + dh $@ diff -Nru atomicparsley-0.9.6/debian/source/lintian-overrides atomicparsley-20210715.151551.e7ad03a/debian/source/lintian-overrides --- atomicparsley-0.9.6/debian/source/lintian-overrides 2019-02-24 13:43:55.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/source/lintian-overrides 2021-09-17 09:00:21.000000000 +0000 @@ -1,5 +1,5 @@ -# License is in License-Reference field (see bug#786450) -missing-license-paragraph-in-dep5-copyright gpl-2\+ * -missing-license-paragraph-in-dep5-copyright gpl-3\+ * -missing-license-text-in-dep5-copyright GPL-2\+ * -missing-license-text-in-dep5-copyright GPL-3\+ * +# License is in Reference field (see bug#786450) +missing-license-paragraph-in-dep5-copyright debian/copyright gpl-2\+ * +missing-license-paragraph-in-dep5-copyright debian/copyright gpl-3\+ * +missing-license-text-in-dep5-copyright debian/copyright GPL-2\+ * +missing-license-text-in-dep5-copyright debian/copyright GPL-3\+ * diff -Nru atomicparsley-0.9.6/debian/watch atomicparsley-20210715.151551.e7ad03a/debian/watch --- atomicparsley-0.9.6/debian/watch 2019-02-24 13:43:55.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/debian/watch 2021-09-17 08:52:14.000000000 +0000 @@ -3,6 +3,7 @@ # update: gbp import-orig --upstream-vcs-tag=vX.Y.Z --uscan opts=\ -filenamemangle=s/.*?(@ANY_VERSION@@ARCHIVE_EXT@)/@PACKAGE@-$1/ \ -https://bitbucket.org/wez/atomicparsley/downloads/?tab=tags \ -.*/get/@ANY_VERSION@@ARCHIVE_EXT@ +filenamemangle=s/.*?(@ANY_VERSION@@ARCHIVE_EXT@)/@PACKAGE@-$1/,\ +dversionmangle=auto,repacksuffix=~dfsg \ +https://github.com/wez/atomicparsley/tags \ +.*?@ANY_VERSION@@ARCHIVE_EXT@ diff -Nru atomicparsley-0.9.6/.github/workflows/ci.yml atomicparsley-20210715.151551.e7ad03a/.github/workflows/ci.yml --- atomicparsley-0.9.6/.github/workflows/ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/.github/workflows/ci.yml 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,38 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +defaults: + run: + shell: bash + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v2 + - name: configure + run: cmake . + - name: build + run: cmake --build . --config Release + + asan: + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v2 + - name: configure + run: cmake -DASAN=on -DCMAKE_BUILD_TYPE=Debug . + - name: build + run: cmake --build . + - name: test + run: tests/test.sh diff -Nru atomicparsley-0.9.6/.github/workflows/greetings.yml atomicparsley-20210715.151551.e7ad03a/.github/workflows/greetings.yml --- atomicparsley-0.9.6/.github/workflows/greetings.yml 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/.github/workflows/greetings.yml 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,13 @@ +name: Greetings + +on: [pull_request, issues] + +jobs: + greeting: + runs-on: ubuntu-latest + steps: + - uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: 'Thanks for filing an issue! Please note that this project is only passively maintained, so your best bet for getting an issue resolved is through a pull request that is easy to verify! [Please read this for more information.](https://github.com/wez/atomicparsley#a-note-on-maintenance)' + pr-message: 'Thanks for your contribution! As this project is only passively maintained, it is preferable for PRs to be as simple as possible to verify and review. If feasible and appropriate, consider including a tiny test file to exercise your proposed changes! [Please read this for more information.](https://github.com/wez/atomicparsley#a-note-on-maintenance)' diff -Nru atomicparsley-0.9.6/.github/workflows/release.yml atomicparsley-20210715.151551.e7ad03a/.github/workflows/release.yml --- atomicparsley-0.9.6/.github/workflows/release.yml 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/.github/workflows/release.yml 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,52 @@ +name: Release + +on: + push: + tags: + - "20*" + +defaults: + run: + shell: bash + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + steps: + - uses: actions/checkout@v2 + - name: configure + run: cmake . + - name: build + run: cmake --build . --config Release + - name: build-musl + if: runner.os == 'Linux' + run: | + rm CMakeCache.txt + mv AtomicParsley AtomicParsleyLinux + docker run --rm -v "$(pwd):/mnt" -w '/mnt' alpine sh -c "\ + apk add cmake build-base linux-headers zlib-dev && \ + cmake . && \ + cmake --build . --config Release \ + " + - name: zip + if: runner.os == 'macOS' + run: zip AtomicParsleyMacOS.zip AtomicParsley + - name: zip + if: runner.os == 'Linux' + run: | + zip AtomicParsleyAlpine.zip AtomicParsley + mv AtomicParsleyLinux AtomicParsley + zip AtomicParsleyLinux.zip AtomicParsley + - name: zip + if: runner.os == 'Windows' + run: 7z a -tzip AtomicParsleyWindows.zip Release/AtomicParsley.exe + - name: "Upload to Tagged Release" + uses: softprops/action-gh-release@v1 + with: + files: "AtomicParsley*.zip" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff -Nru atomicparsley-0.9.6/.gitignore atomicparsley-20210715.151551.e7ad03a/.gitignore --- atomicparsley-0.9.6/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/.gitignore 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,33 @@ +.*.sw* +.DS_Store +obj_dir +/AtomicParsley* +Makefile +autom4te.cache +config.log +config.status +configure +src/Makefile +src/config.h +src/config.h.in* +Makefile.in +aclocal.m4 +depcomp +install-sh +missing +*.o +*.obj +.deps +compile +src/.deps +.dirstamp +config.guess +config.sub +src/stamp-h1 +a.out.dSYM +a.out +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +.ninja* +*.ninja diff -Nru atomicparsley-0.9.6/.hg_archival.txt atomicparsley-20210715.151551.e7ad03a/.hg_archival.txt --- atomicparsley-0.9.6/.hg_archival.txt 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/.hg_archival.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -repo: f4adbb51ab5a8d804be98d604a8665440db48e46 -node: da2f6e4fc1206a330b0324a80606c25e2e5ce7b6 -branch: wez_pull_request_cleaner -tag: 0.9.6 diff -Nru atomicparsley-0.9.6/.hgignore atomicparsley-20210715.151551.e7ad03a/.hgignore --- atomicparsley-0.9.6/.hgignore 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/.hgignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -syntax: glob -.DS_Store -obj_dir -AtomicParsley* -Makefile -autom4te.cache -config.log -config.status -configure -src/Makefile -src/config.h -src/config.h.in* -Makefile.in -aclocal.m4 -depcomp -install-sh -missing -src/*.o -src/extras/*.o -.deps -compile -src/.deps -.dirstamp -config.guess -config.sub -src/stamp-h1 -a.out.dSYM -a.out diff -Nru atomicparsley-0.9.6/.hgtags atomicparsley-20210715.151551.e7ad03a/.hgtags --- atomicparsley-0.9.6/.hgtags 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/.hgtags 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -7ef2c4f434cb85101ef9c5b7f4bf517c3287f1e0 0.9.3 -36099586d4c238c643db208496048799b98e3412 0.9.4 -d9f7d6ea607a09506a4537c5df2db06d8fd92e57 0.9.5 diff -Nru atomicparsley-0.9.6/Makefile.am atomicparsley-20210715.151551.e7ad03a/Makefile.am --- atomicparsley-0.9.6/Makefile.am 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/Makefile.am 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ - -bin_PROGRAMS = AtomicParsley -noinst_HEADERS = src/AtomDefs.h src/AtomicParsley.h src/CDtoc.h \ - src/ap_types.h src/id3v2.h src/id3v2defs.h src/id3v2types.h \ - src/util.h - -AM_CPPFLAGS = -DHAVE_CONFIG_H - -if DARWIN -AM_CPPFLAGS += -DDARWIN_PLATFORM -AM_OBJCFLAGS = -DDARWIN_PLATFORM $(UNIVERSAL_FLAGS) -AM_CFLAGS = -DDARWIN_PLATFORM $(UNIVERSAL_FLAGS) -AM_CXXFLAGS = -DDARWIN_PLATFORM $(UNIVERSAL_FLAGS) -AM_LDFLAGS = $(UNIVERSAL_FLAGS) \ - -framework Cocoa -framework Foundation -framework IOKit -endif - -AtomicParsley_SOURCES = \ - src/util.cpp \ - src/arrays.cpp \ - src/iconv.cpp \ - src/parsley.cpp \ - src/extracts.cpp \ - src/sha1.cpp \ - src/uuid.cpp \ - src/id3v2.cpp \ - src/metalist.cpp \ - src/CDtoc.cpp \ - src/compress.cpp \ - src/main.cpp - -# If you see a warning about this rule overriding an automake provided rule, -# that's fine; we do this because we want to build on versions of autoconf -# and automake prior to 1.12.1 -.mm.o: - $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -c -o $@ $< - -if DARWIN -AtomicParsley_SOURCES += \ - src/nsfile.mm \ - src/nsimage.mm -endif - -if NEED_GETOPT -AtomicParsley_SOURCES += \ - src/extras/getopt.c \ - src/extras/getopt1.c -endif - diff -Nru atomicparsley-0.9.6/README.md atomicparsley-20210715.151551.e7ad03a/README.md --- atomicparsley-0.9.6/README.md 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/README.md 2021-07-15 22:15:51.000000000 +0000 @@ -1,59 +1,70 @@ # AtomicParsley -## Basic Instructions +![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wez/atomicparsley/CI) -If you are building from source you will need autoconf & automake (you will -definitely need make): +AtomicParsley is a lightweight command line program for reading, parsing and +setting metadata into MPEG-4 files, in particular, iTunes-style metadata. - % ./autogen.sh - % ./configure - % make +## Installation -Use the program in situ or place it somewhere in your $PATH by using: +### macOS - % sudo make install +* Navigate to the [latest release](https://github.com/wez/atomicparsley/releases/latest) +* Download the `AtomicParsleyMacOS.zip` file and extract `AtomicParsley` +### Windows -### Dependencies: - -zlib - used to compress ID3 frames & expand already compressed frames - available from http://www.zlib.net +* Navigate to the [latest release](https://github.com/wez/atomicparsley/releases/latest) +* Download the `AtomicParsleyWindows.zip` file and extract `AtomicParsley.exe` -## For Mac OS X users: +### Linux (x86-64) -The default is to build a universal binary. +* Navigate to the [latest release](https://github.com/wez/atomicparsley/releases/latest) +* Download the `AtomicParsleyLinux.zip` file and extract `AtomicParsley` -switching between a universal build and a platform dependent build should be -accompanied by a "make maintainter-clean" and a ./configure between builds. +### Alpine Linux (x86-64 musl libc) -To *not* build a Mac OS X universal binary: +* Navigate to the [latest release](https://github.com/wez/atomicparsley/releases/latest) +* Download the `AtomicParsleyAlpine.zip` file and extract `AtomicParsley` +* And finally `apk add libstdc++` - % make maintainer-clean - % ./configure --disable-universal - % make +### Building from Source +If you are building from source you will need `cmake` and `make`. +On Windows systems you'll need Visual Studio or MingW. -## For Windows users: -AtomicParsley builds under cygwin and/or mingw using the same procedure as above. +``` +cmake . +cmake --build . --config Release +``` -Foosatraz built in Windows 8 using MinGW. +will generate an `AtomicParsley` executable. -MinGW-get version 0.6.2-beta-20131004-1 - -mingw-libz version 1.2.8-1 from MinGW Installation Manager +### Dependencies: - % ./autogen.sh - % ./configure --prefix=/mingw - % make LDFLAGS=-static - % strip AtomicParsley.exe +zlib - used to compress ID3 frames & expand already compressed frames + available from http://www.zlib.net -Full details [pdf](https://bitbucket.org/Foosatraz/wez-atomicparsley-foosatraz-fork/downloads/AtomicParsleyMinGWBuildNotebook.pdf) -To build with MSVC, you will need to create your own project file; look -at the list of source files in Makefile.am; you need to add all of the -source files *except* the .mm files. You will also need to provide your -own zlib. +## A note on maintenance! -If you don't want to build it yourself, [Jon Hedgrows' fork ](https://bitbucket.org/jonhedgerows/atomicparsley/wiki/Home) maintains pre-built Windows binaries of the Wez fork: -[Windows Downloads](https://bitbucket.org/jonhedgerows/atomicparsley/downloads) +> I made some fixes to the original project on sourceforge back in 2009 and +> became the de-facto fork of AtomicParsley as a result. However, I haven't +> used this tool myself in many years and have acted in a very loose guiding +> role since then. +> +> In 2020 Bitbucket decided to cease hosting Mercurial based repositories +> which meant that I had to move it in order to keep it alive, so you'll +> see a flurry of recent activity. +> +> I'll consider merging pull requests if they are easy to review, but because +> I don't use this tool myself I have no way to verify complex changes. +> If you'd like to make such a change, please consider contributing some +> kind of basic automated test with a corresponding small test file. +> +> This repo has GitHub Actions enabled for the three major platforms +> so bootstrapping some test coverage is feasible. +> +> You are welcome to report issues using the issue tracker, but I (@wez) +> am unlikely to act upon them. diff -Nru atomicparsley-0.9.6/src/ap_types.h atomicparsley-20210715.151551.e7ad03a/src/ap_types.h --- atomicparsley-0.9.6/src/ap_types.h 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/ap_types.h 2021-07-15 22:15:51.000000000 +0000 @@ -1,8 +1,6 @@ #ifndef AP_TYPES_H #define AP_TYPES_H - - // Atom version 1byte/ Atom flags 3 bytes; 0x00 00 00 00 #define AtomFlags_Data_Binary 0 @@ -21,46 +19,50 @@ // 0x58 for uuid atoms that contain files #define AtomFlags_Data_uuid_binary 88 - enum { - UTF8_iTunesStyle_256glyphLimited = 0, //no NULL termination - UTF8_iTunesStyle_Unlimited = 1, //no NULL termination - UTF8_iTunesStyle_Binary = 3, //no NULL termination, used in purl & egid - UTF8_3GP_Style = 8, //terminated with a NULL uint8_t - UTF16_3GP_Style = 16 //terminated with a NULL uint16_t + UTF8_iTunesStyle_256glyphLimited = 0, // no NULL termination + UTF8_iTunesStyle_Unlimited = 1, // no NULL termination + UTF8_iTunesStyle_Binary = 3, // no NULL termination, used in purl & egid + UTF8_3GP_Style = 8, // terminated with a NULL uint8_t + UTF16_3GP_Style = 16 // terminated with a NULL uint16_t }; enum { - UNDEFINED_STYLE = 0, - ITUNES_STYLE = 100, - THIRD_GEN_PARTNER = 300, //3gpp files prior to 3gp6 - THIRD_GEN_PARTNER_VER1_REL6 = 306, //3GPP Release6 the first spec to contain the complement of assets - THIRD_GEN_PARTNER_VER1_REL7 = 307, //3GPP Release7 introduces ID32 atoms - THIRD_GEN_PARTNER_VER2 = 320, //3gp2 files - THIRD_GEN_PARTNER_VER2_REL_A = 321, //3gp2 files, 3GPP2 C.S0050-A introduces 'gadi' - MOTIONJPEG2000 = 400 + UNDEFINED_STYLE = 0, + ITUNES_STYLE = 100, + THIRD_GEN_PARTNER = 300, // 3gpp files prior to 3gp6 + THIRD_GEN_PARTNER_VER1_REL6 = + 306, // 3GPP Release6 the first spec to contain the complement of assets + THIRD_GEN_PARTNER_VER1_REL7 = 307, // 3GPP Release7 introduces ID32 atoms + THIRD_GEN_PARTNER_VER2 = 320, // 3gp2 files + THIRD_GEN_PARTNER_VER2_REL_A = + 321, // 3gp2 files, 3GPP2 C.S0050-A introduces 'gadi' + MOTIONJPEG2000 = 400 }; #include "id3v2types.h" -struct AtomicInfo { - short AtomicNumber; - uint64_t AtomicStart; - uint64_t AtomicLength; - uint64_t AtomicLengthExtended; - char* AtomicName; - char* ReverseDNSname; - char* ReverseDNSdomain; - uint8_t AtomicContainerState; - uint8_t AtomicClassification; - uint32_t AtomicVerFlags; //used by versioned atoms and derivatives - uint16_t AtomicLanguage; //used by 3gp assets & ID32 atoms only - uint8_t AtomicLevel; - char* AtomicData; - int NextAtomNumber; //our first atom is numbered 0; the last points back to it - so watch it! - uint32_t ancillary_data; //just contains a simple number for atoms that contains some interesting info (like stsd codec used) - uint8_t uuid_style; - char* uuid_ap_atomname; - ID3v2Tag* ID32_TagInfo; +struct AtomicInfo { + short AtomicNumber; + uint64_t AtomicStart; + uint64_t AtomicLength; + uint64_t AtomicLengthExtended; + char *AtomicName; + char *ReverseDNSname; + char *ReverseDNSdomain; + uint8_t AtomicContainerState; + uint8_t AtomicClassification; + uint32_t AtomicVerFlags; // used by versioned atoms and derivatives + uint16_t AtomicLanguage; // used by 3gp assets & ID32 atoms only + uint8_t AtomicLevel; + char *AtomicData; + int NextAtomNumber; // our first atom is numbered 0; the last points back to + // it - so watch it! + uint32_t + ancillary_data; // just contains a simple number for atoms that contains + // some interesting info (like stsd codec used) + uint8_t uuid_style; + char *uuid_ap_atomname; + ID3v2Tag *ID32_TagInfo; }; #include "id3v2.h" @@ -75,52 +77,53 @@ bool has_alac; bool has_mp4a; bool has_drms; - bool has_timed_text; //carries the URL - in the mdat stream at a specific time - thus it too is timed. - bool has_timed_jpeg; //no idea of podcasts support 'png ' or 'tiff' - bool has_timed_tx3g; //this IS true timed text stream - bool has_mp4s; //MPEG-4 Systems - bool has_rtp_hint; //'rtp '; implies hinting + bool has_timed_text; // carries the URL - in the mdat stream at a specific + // time - thus it too is timed. + bool has_timed_jpeg; // no idea of podcasts support 'png ' or 'tiff' + bool has_timed_tx3g; // this IS true timed text stream + bool has_mp4s; // MPEG-4 Systems + bool has_rtp_hint; //'rtp '; implies hinting }; enum { MEDIADATA__PRECEDES__MOOV = 2, - ROOT_META__PRECEDES__MOOV = 4, + ROOT_META__PRECEDES__MOOV = 4, MOOV_META__PRECEDES__TRACKS = 8, MOOV_UDTA__PRECEDES__TRACKS = 16, - + PADDING_AT_EOF = 0x1000000 }; struct FreeAtomListing { - AtomicInfo* free_atom; - FreeAtomListing* next_free_listing; + AtomicInfo *free_atom; + FreeAtomListing *next_free_listing; }; -struct DynamicUpdateStat { +struct DynamicUpdateStat { bool updage_by_padding; bool reorder_moov; bool moov_was_mooved; bool prevent_dynamic_update; uint32_t optimization_flags; - + uint64_t padding_bytes; short consolidated_padding_insertion; - AtomicInfo* last_trak_child_atom; - AtomicInfo* moov_atom; - AtomicInfo* moov_udta_atom; - AtomicInfo* iTunes_list_handler_atom; - AtomicInfo* moov_meta_atom; - AtomicInfo* file_meta_atom; - AtomicInfo* first_mdat_atom; - AtomicInfo* first_movielevel_metadata_tagging_atom; - AtomicInfo* initial_update_atom; - AtomicInfo* first_otiose_freespace_atom; - AtomicInfo* padding_store; - AtomicInfo* padding_resevoir; - FreeAtomListing* first_padding_atom; - FreeAtomListing* last_padding_atom; + AtomicInfo *last_trak_child_atom; + AtomicInfo *moov_atom; + AtomicInfo *moov_udta_atom; + AtomicInfo *iTunes_list_handler_atom; + AtomicInfo *moov_meta_atom; + AtomicInfo *file_meta_atom; + AtomicInfo *first_mdat_atom; + AtomicInfo *first_movielevel_metadata_tagging_atom; + AtomicInfo *initial_update_atom; + AtomicInfo *first_otiose_freespace_atom; + AtomicInfo *padding_store; + AtomicInfo *padding_resevoir; + FreeAtomListing *first_padding_atom; + FreeAtomListing *last_padding_atom; }; struct padding_preferences { @@ -129,39 +132,49 @@ uint32_t maximum_present_padding_size; }; -// Structure that defines the known atoms used by mpeg-4 family of specifications. +// Structure that defines the known atoms used by mpeg-4 family of +// specifications. typedef struct { - const char* known_atom_name; - const char* known_parent_atoms[5]; //max known to be tested - uint32_t container_state; - int presence_requirements; - uint32_t box_type; + const char *known_atom_name; + const char *known_parent_atoms[5]; // max known to be tested + uint32_t container_state; + int presence_requirements; + uint32_t box_type; } atomDefinition; typedef struct { uint8_t uuid_form; - char* binary_uuid; - char* uuid_AP_atom_name; + char *binary_uuid; + char *uuid_AP_atom_name; } uuid_vitals; enum { - PARENT_ATOM = 0, //container atom - SIMPLE_PARENT_ATOM = 1, - DUAL_STATE_ATOM = 2, //acts as both parent (contains other atoms) & child (carries data) - CHILD_ATOM = 3, //atom that does NOT contain any children - UNKNOWN_ATOM_TYPE = 4 + PARENT_ATOM = 0, // container atom + SIMPLE_PARENT_ATOM = 1, + DUAL_STATE_ATOM = + 2, // acts as both parent (contains other atoms) & child (carries data) + CHILD_ATOM = 3, // atom that does NOT contain any children + UNKNOWN_ATOM_TYPE = 4 }; enum { - REQUIRED_ONCE = 30, //means total of 1 atom per file (or total of 1 if parent atom is required to be present) - REQUIRED_ONE = 31, //means 1 atom per container atom; totalling many per file (or required present if optional parent atom is present) - REQUIRED_VARIABLE = 32, //means 1 or more atoms per container atom are required to be present - PARENT_SPECIFIC = 33, //means (iTunes-style metadata) the atom defines how many are present; most are MAX 1 'data' atoms; 'covr' is ?unlimited? - OPTIONAL_ONCE = 34, //means total of 1 atom per file, but not required - OPTIONAL_ONE = 35, //means 1 atom per container atom but not required; many may be present in a file - OPTIONAL_MANY = 36, //means more than 1 occurrence per container atom - REQ_FAMILIAL_ONE = OPTIONAL_ONE, //means that one of the family of atoms defined by the spec is required by the parent atom - UKNOWN_REQUIREMENTS= 38 + REQUIRED_ONCE = 30, // means total of 1 atom per file (or total of 1 if + // parent atom is required to be present) + REQUIRED_ONE = 31, // means 1 atom per container atom; totalling many per file + // (or required present if optional parent atom is present) + REQUIRED_VARIABLE = + 32, // means 1 or more atoms per container atom are required to be present + PARENT_SPECIFIC = + 33, // means (iTunes-style metadata) the atom defines how many are + // present; most are MAX 1 'data' atoms; 'covr' is ?unlimited? + OPTIONAL_ONCE = 34, // means total of 1 atom per file, but not required + OPTIONAL_ONE = 35, // means 1 atom per container atom but not required; many + // may be present in a file + OPTIONAL_MANY = 36, // means more than 1 occurrence per container atom + REQ_FAMILIAL_ONE = + OPTIONAL_ONE, // means that one of the family of atoms defined by the spec + // is required by the parent atom + UKNOWN_REQUIREMENTS = 38 }; enum { @@ -183,52 +196,41 @@ }; typedef struct { - const char* stik_string; - uint8_t stik_number; + const char *stik_string; + uint8_t stik_number; } stiks; typedef struct { - const char* storefront_string; - uint32_t storefront_number; + const char *storefront_string; + uint32_t storefront_number; } sfIDs; typedef struct { - const char* iso639_2_code; - const char* iso639_1_code; - const char* language_in_english; + const char *iso639_2_code; + const char *iso639_1_code; + const char *language_in_english; } iso639_lang; typedef struct { - const char* media_rating; - const char* media_rating_cli_str; + const char *media_rating; + const char *media_rating_cli_str; } m_ratings; typedef struct { - const char* genre_id_movie_string; + const char *genre_id_movie_string; uint16_t genre_id_movie_value; } geIDMovie; typedef struct { - const char* genre_id_tv_string; + const char *genre_id_tv_string; uint16_t genre_id_tv_value; } geIDTV; -enum { - UNIVERSAL_UTF8, - WIN32_UTF16 -}; +enum { UNIVERSAL_UTF8, WIN32_UTF16 }; -enum { - FORCE_M4B_TYPE = 85, - NO_TYPE_FORCING = 90 -}; +enum { FORCE_M4B_TYPE = 85, NO_TYPE_FORCING = 90 }; -enum { - FILE_LEVEL_ATOM, - MOVIE_LEVEL_ATOM, - ALL_TRACKS_ATOM, - SINGLE_TRACK_ATOM -}; +enum { FILE_LEVEL_ATOM, MOVIE_LEVEL_ATOM, ALL_TRACKS_ATOM, SINGLE_TRACK_ATOM }; enum { UUID_DEPRECATED_FORM, @@ -237,26 +239,24 @@ UUID_OTHER }; - /* Declarations of functions and data types used for SHA1 sum library functions. Copyright (C) 2000, 2001, 2003, 2005 Free Software Foundation, Inc. */ typedef struct { - uint32_t time_low; - uint16_t time_mid; - uint16_t time_hi_and_version; - uint8_t clock_seq_hi_and_reserved; - uint8_t clock_seq_low; - unsigned char node[6]; + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + unsigned char node[6]; } ap_uuid_t; typedef uint32_t md5_uint32; /* Structure to save state of computation between the single steps. */ -struct sha1_ctx -{ +struct sha1_ctx { md5_uint32 A; md5_uint32 B; md5_uint32 C; @@ -265,10 +265,11 @@ md5_uint32 total[2]; md5_uint32 buflen; - char buffer[128]; //char buffer[128] __attribute__ ((__aligned__ (__alignof__ (md5_uint32)))); + char buffer[128]; // char buffer[128] __attribute__ ((__aligned__ (__alignof__ + // (md5_uint32)))); }; -typedef struct { //if any of these are unused, they are set to 0xFF +typedef struct { // if any of these are unused, they are set to 0xFF uint8_t od_profile_level; uint8_t scene_profile_level; uint8_t audio_profile; @@ -284,13 +285,13 @@ unsigned char unpacked_lang[4]; char track_hdlr_name[100]; char encoder_name[100]; - + uint32_t track_type; uint32_t track_codec; uint32_t protected_codec; - + bool contains_esds; - + uint64_t section3_length; uint64_t section4_length; uint8_t ObjectTypeIndication; @@ -299,9 +300,9 @@ uint64_t section5_length; uint8_t descriptor_object_typeID; uint16_t channels; - uint64_t section6_length; //unused + uint64_t section6_length; // unused - //specifics + // specifics uint8_t m4v_profile; uint8_t avc_version; uint8_t profile; @@ -311,9 +312,9 @@ uint32_t macroblocks; uint64_t sample_aggregate; uint16_t amr_modes; - + uint8_t type_of_track; - + } TrackInfo; typedef struct { @@ -321,12 +322,12 @@ uint64_t modified_time; uint32_t timescale; uint32_t duration; - uint32_t playback_rate; //fixed point 16.16 - uint16_t volume; //fixed 8.8 point - + uint32_t playback_rate; // fixed point 16.16 + uint16_t volume; // fixed 8.8 point + double seconds; double simple_bitrate_calc; - + bool contains_iods; } MovieInfo; @@ -361,12 +362,9 @@ SMV_TRACK = 71 }; -enum { - SHOW_TRACK_INFO = 2, - SHOW_DATE_INFO = 4 -}; +enum { SHOW_TRACK_INFO = 2, SHOW_DATE_INFO = 4 }; -struct PicPrefs { +struct PicPrefs { int max_dimension; int dpi; int max_Kbytes; @@ -380,11 +378,7 @@ int force_width; }; - - - #endif /* vim:ts=2:sw=2:et: */ - diff -Nru atomicparsley-0.9.6/src/arrays.cpp atomicparsley-20210715.151551.e7ad03a/src/arrays.cpp --- atomicparsley-0.9.6/src/arrays.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/arrays.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - arrays.cpp - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,17 +10,17 @@ 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 + 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 2005-2007 puck_lock + Copyright (C) 2005-2007 puck_lock with contributions from others; see the CREDITS file - - ---------------------- + + ---------------------- Code Contributions by: - + * Mellow_Flow - fix genre matching/verify genre limits */ //==================================================================// @@ -29,807 +29,917 @@ ////////////// -static const char* ID3v1GenreList[] = { - "Blues", "Classic Rock", "Country", "Dance", "Disco", - "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", - "New Age", "Oldies", "Other", "Pop", "R&B", - "Rap", "Reggae", "Rock", "Techno", "Industrial", - "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", - "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", - "Fusion", "Trance", "Classical", "Instrumental", "Acid", - "House", "Game", "Sound Clip", "Gospel", "Noise", - "AlternRock", "Bass", "Soul", "Punk", "Space", - "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", - "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", - "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", - "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", - "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", - "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", - "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", - "Folk", "Folk/Rock", "National Folk", "Swing", "Fast Fusion", - "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", - "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", - "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", - "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", - "Symphony", "Booty Bass", "Primus", "Porn Groove", - "Satire", "Slow Jam", "Club", "Tango", "Samba", - "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", - "Duet", "Punk Rock", "Drum Solo", "A Capella", "Euro-House", - "Dance Hall" }; - /* - "Goa", "Drum & Bass", "Club House", "Hardcore", - "Terror", "Indie", "BritPop", "NegerPunk", "Polsk Punk", - "Beat", "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", - "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", - "Anime", "JPop", "SynthPop", +static const char *ID3v1GenreList[] = {"Blues", + "Classic Rock", + "Country", + "Dance", + "Disco", + "Funk", + "Grunge", + "Hip-Hop", + "Jazz", + "Metal", + "New Age", + "Oldies", + "Other", + "Pop", + "R&B", + "Rap", + "Reggae", + "Rock", + "Techno", + "Industrial", + "Alternative", + "Ska", + "Death Metal", + "Pranks", + "Soundtrack", + "Euro-Techno", + "Ambient", + "Trip-Hop", + "Vocal", + "Jazz+Funk", + "Fusion", + "Trance", + "Classical", + "Instrumental", + "Acid", + "House", + "Game", + "Sound Clip", + "Gospel", + "Noise", + "AlternRock", + "Bass", + "Soul", + "Punk", + "Space", + "Meditative", + "Instrumental Pop", + "Instrumental Rock", + "Ethnic", + "Gothic", + "Darkwave", + "Techno-Industrial", + "Electronic", + "Pop-Folk", + "Eurodance", + "Dream", + "Southern Rock", + "Comedy", + "Cult", + "Gangsta", + "Top 40", + "Christian Rap", + "Pop/Funk", + "Jungle", + "Native American", + "Cabaret", + "New Wave", + "Psychadelic", + "Rave", + "Showtunes", + "Trailer", + "Lo-Fi", + "Tribal", + "Acid Punk", + "Acid Jazz", + "Polka", + "Retro", + "Musical", + "Rock & Roll", + "Hard Rock", + "Folk", + "Folk/Rock", + "National Folk", + "Swing", + "Fast Fusion", + "Bebob", + "Latin", + "Revival", + "Celtic", + "Bluegrass", + "Avantgarde", + "Gothic Rock", + "Progressive Rock", + "Psychedelic Rock", + "Symphonic Rock", + "Slow Rock", + "Big Band", + "Chorus", + "Easy Listening", + "Acoustic", + "Humour", + "Speech", + "Chanson", + "Opera", + "Chamber Music", + "Sonata", + "Symphony", + "Booty Bass", + "Primus", + "Porn Groove", + "Satire", + "Slow Jam", + "Club", + "Tango", + "Samba", + "Folklore", + "Ballad", + "Power Ballad", + "Rhythmic Soul", + "Freestyle", + "Duet", + "Punk Rock", + "Drum Solo", + "A Capella", + "Euro-House", + "Dance Hall"}; +/* +"Goa", "Drum & Bass", "Club House", "Hardcore", +"Terror", "Indie", "BritPop", "NegerPunk", "Polsk Punk", +"Beat", "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", +"Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", +"Anime", "JPop", "SynthPop", }; */ //apparently the other winamp id3v1 extensions aren't valid -stiks stikArray[] = { - { "Home Video", 0 }, - { "Normal", 1 }, - { "Audiobook", 2 }, - { "Whacked Bookmark", 5 }, - { "Music Video", 6 }, - { "Movie", 9 }, - { "Short Film", 9 }, - { "TV Show", 10 }, - { "Booklet", 11 } -}; - -geIDMovie genreidmovie[] = { - { "Action & Adventure", 4401 }, - { "Anime", 4402 }, - { "Classics", 4403 }, - { "Comedy", 4404 }, - { "Documentary", 4405 }, - { "Drama", 4406 }, - { "Foreign", 4407 }, - { "Horror", 4408 }, - { "Independent", 4409 }, - { "Kids & Family", 4410 }, - { "Musicals", 4411 }, - { "Romance", 4412 }, - { "Sci-Fi & Fantasy", 4413 }, - { "Short Films", 4414 }, - { "Special Interest", 4415 }, - { "Thriller", 4416 }, - { "Sports", 4417 }, - { "Western", 4418 }, - { "Urban", 4419 }, - { "Holiday", 4420 }, - { "Made for TV", 4421 }, - { "Concert Films", 4422 }, - { "Music Documentaries", 4423 }, - { "Music Feature Films", 4424 }, - { "Japanese Cinema", 4425 }, - { "Jidaigeki", 4426 }, - { "Tokusatsu", 4427 }, - { "Korean Cinema", 4428 } -}; - -geIDTV genreidtv[] = { - { "Comedy", 4000 }, - { "Drama", 4001 }, - { "Animation", 4002 }, - { "Action & Adventure", 4003 }, - { "Classic", 4004 }, - { "Kids", 4005 }, - { "Nonfiction", 4005 }, - { "Reality TV", 4007 }, - { "Sci-Fi & Fantasy", 4008 }, - { "Sports", 4009 }, - { "Teens", 4010 }, - { "Latino TV", 4011 } -}; +stiks stikArray[] = {{"Home Video", 0}, + {"Normal", 1}, + {"Audiobook", 2}, + {"Whacked Bookmark", 5}, + {"Music Video", 6}, + {"Movie", 9}, + {"Short Film", 9}, + {"TV Show", 10}, + {"Booklet", 11}}; + +geIDMovie genreidmovie[] = {{"Action & Adventure", 4401}, + {"Anime", 4402}, + {"Classics", 4403}, + {"Comedy", 4404}, + {"Documentary", 4405}, + {"Drama", 4406}, + {"Foreign", 4407}, + {"Horror", 4408}, + {"Independent", 4409}, + {"Kids & Family", 4410}, + {"Musicals", 4411}, + {"Romance", 4412}, + {"Sci-Fi & Fantasy", 4413}, + {"Short Films", 4414}, + {"Special Interest", 4415}, + {"Thriller", 4416}, + {"Sports", 4417}, + {"Western", 4418}, + {"Urban", 4419}, + {"Holiday", 4420}, + {"Made for TV", 4421}, + {"Concert Films", 4422}, + {"Music Documentaries", 4423}, + {"Music Feature Films", 4424}, + {"Japanese Cinema", 4425}, + {"Jidaigeki", 4426}, + {"Tokusatsu", 4427}, + {"Korean Cinema", 4428}}; + +geIDTV genreidtv[] = {{"Comedy", 4000}, + {"Drama", 4001}, + {"Animation", 4002}, + {"Action & Adventure", 4003}, + {"Classic", 4004}, + {"Kids", 4005}, + {"Nonfiction", 4005}, + {"Reality TV", 4007}, + {"Sci-Fi & Fantasy", 4008}, + {"Sports", 4009}, + {"Teens", 4010}, + {"Latino TV", 4011}}; -// from William Herrera: http://search.cpan.org/src/BILLH/LWP-UserAgent-iTMS_Client-0.16/lib/LWP/UserAgent/iTMS_Client.pm +// from William Herrera: +// http://search.cpan.org/src/BILLH/LWP-UserAgent-iTMS_Client-0.16/lib/LWP/UserAgent/iTMS_Client.pm sfIDs storefronts[] = { - { "United States", 143441 }, - { "France", 143442 }, - { "Germany", 143443 }, - { "United Kingdom", 143444 }, - { "Austria", 143445 }, - { "Belgium", 143446 }, - { "Finland", 143447 }, - { "Greece", 143448 }, - { "Ireland", 143449 }, - { "Italy", 143450 }, - { "Luxembourg", 143451 }, - { "Netherlands", 143452 }, - { "Portugal", 143453 }, - { "Spain", 143454 }, - { "Canada", 143455 }, - { "Sweden", 143456 }, - { "Norway", 143457 }, - { "Denmark", 143458 }, - { "Switzerland", 143459 }, - { "Australia", 143460 }, - { "New Zealand", 143461 }, - { "Japan", 143462 } -}; + {"United States", 143441}, {"France", 143442}, {"Germany", 143443}, + {"United Kingdom", 143444}, {"Austria", 143445}, {"Belgium", 143446}, + {"Finland", 143447}, {"Greece", 143448}, {"Ireland", 143449}, + {"Italy", 143450}, {"Luxembourg", 143451}, {"Netherlands", 143452}, + {"Portugal", 143453}, {"Spain", 143454}, {"Canada", 143455}, + {"Sweden", 143456}, {"Norway", 143457}, {"Denmark", 143458}, + {"Switzerland", 143459}, {"Australia", 143460}, {"New Zealand", 143461}, + {"Japan", 143462}}; iso639_lang known_languages[] = { - { "aar", "aa", "Afar" }, - { "abk", "ab", "Abkhazian" }, - { "ace", NULL, "Achinese" }, - { "ach", NULL, "Acoli" }, - { "ada", NULL, "Adangme" }, - { "ady", NULL, "Adyghe; Adygei" }, - { "afa", NULL, "Afro-Asiatic (Other)" }, - { "afh", NULL, "Afrihili" }, - { "afr", "af", "Afrikaans" }, - { "ain", NULL, "Ainu" }, - { "aka", "ak", "Akan" }, - { "akk", NULL, "Akkadian" }, - { "alb/sqi", "sq", "Albanian" }, //dual codes - { "ale", NULL, "Aleut" }, - { "alg", NULL, "Algonquian languages" }, - { "alt", NULL, "Southern Altai" }, - { "amh", "am", "Amharic" }, - { "ang", NULL, "English, Old (ca.450-1100)" }, - { "anp", NULL, "Angika" }, - { "apa", NULL, "Apache languages" }, - { "ara", "ar", "Arabic" }, - { "arc", NULL, "Aramaic" }, - { "arg", "an", "Aragonese" }, - { "arm/hye", "hy", "Armenian" }, //dual codes - { "arn", NULL, "Araucanian" }, - { "arp", NULL, "Arapaho" }, - { "art", NULL, "Artificial (Other)" }, - { "arw", NULL, "Arawak" }, - { "asm", "as", "Assamese" }, - { "ast", NULL, "Asturian; Bable" }, - { "ath", NULL, "Athapascan languages" }, - { "aus", NULL, "Australian languages" }, - { "ava", "av", "Avaric" }, - { "ave", "ae", "Avestan" }, - { "awa", NULL, "Awadhi" }, - { "aym", "ay", "Aymara" }, - { "aze", "az", "Azerbaijani" }, - { "bad", NULL, "Banda" }, - { "bai", NULL, "Bamileke languages" }, - { "bak", "ba", "Bashkir" }, - { "bal", NULL, "Baluchi" }, - { "bam", "bm", "Bambara" }, - { "ban", NULL, "Balinese" }, - { "baq/eus", "eu", "Basque" }, //dual codes - { "bas", NULL, "Basa" }, - { "bat", NULL, "Baltic (Other)" }, - { "bej", NULL, "Beja" }, - { "bel", "be", "Belarusian" }, - { "bem", NULL, "Bemba" }, - { "ben", "bn", "Bengali" }, - { "ber", NULL, "Berber (Other)" }, - { "bho", NULL, "Bhojpuri" }, - { "bih", "bh", "Bihari" }, - { "bik", NULL, "Bikol" }, - { "bin", NULL, "Bini" }, - { "bis", "bi", "Bislama" }, - { "bla", NULL, "Siksika" }, - { "bnt", NULL, "Bantu (Other)" }, - { "bos", "bs", "Bosnian" }, - { "bra", NULL, "Braj" }, - { "bre", "br", "Breton" }, - { "btk", NULL, "Batak (Indonesia)" }, - { "bua", NULL, "Buriat" }, - { "bug", NULL, "Buginese" }, - { "bul", "bg", "Bulgarian" }, - { "bur/mya", "my", "Burmese" }, //dual codes - { "byn", NULL, "Blin; Bilin" }, - { "cad", NULL, "Caddo" }, - { "cai", NULL, "Central American Indian (Other)" }, - { "car", NULL, "Carib" }, - { "cat", "ca", "Catalan; Valencian" }, - { "cau", NULL, "Caucasian (Other)" }, - { "ceb", NULL, "Cebuano" }, - { "cel", NULL, "Celtic (Other)" }, - { "cha", "ch", "Chamorro" }, - { "chb", NULL, "Chibcha" }, - { "che", "ce", "Chechen" }, - { "chg", NULL, "Chagatai" }, - { "chk", NULL, "Chuukese" }, - { "chm", NULL, "Mari" }, - { "chn", NULL, "Chinook jargon" }, - { "cho", NULL, "Choctaw" }, - { "chp", NULL, "Chipewyan" }, - { "chr", NULL, "Cherokee" }, - { "chu", "cu", "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" }, - { "chv", "cv", "Chuvash" }, - { "chy", NULL, "Cheyenne" }, - { "cmc", NULL, "Chamic languages" }, - { "cop", NULL, "Coptic" }, - { "cor", "kw", "Cornish" }, - { "cos", "co", "Corsican" }, - { "cpe", NULL, "Creoles and pidgins, English based (Other)" }, - { "cpf", NULL, "Creoles and pidgins, French-based (Other)" }, - { "cpp", NULL, "Creoles and pidgins, Portuguese-based (Other)" }, - { "cre", "cr", "Cree" }, - { "crh", NULL, "Crimean Tatar; Crimean Turkish" }, - { "crp", NULL, "Creoles and pidgins (Other)" }, - { "csb", NULL, "Kashubian" }, - { "cus", NULL, "Cushitic (Other)" }, - { "cze/ces", "cs", "Czech" }, //dual codes - { "dak", NULL, "Dakota" }, - { "dan", "da", "Danish" }, - { "dar", NULL, "Dargwa" }, - { "day", NULL, "Dayak" }, - { "del", NULL, "Delaware" }, - { "den", NULL, "Slave (Athapascan)" }, - { "dgr", NULL, "Dogrib" }, - { "din", NULL, "Dinka" }, - { "div", "dv", "Divehi; Dhivehi; Maldivian" }, - { "doi", NULL, "Dogri" }, - { "dra", NULL, "Dravidian (Other)" }, - { "dsb", NULL, "Lower Sorbian" }, - { "dua", NULL, "Duala" }, - { "dum", NULL, "Dutch, Middle (ca.1050-1350)" }, - { "dut/nld", "nl", "Dutch; Flemish" }, //dual codes - { "dyu", NULL, "Dyula" }, - { "dzo", "dz", "Dzongkha" }, - { "efi", NULL, "Efik" }, - { "egy", NULL, "Egyptian (Ancient)" }, - { "eka", NULL, "Ekajuk" }, - { "elx", NULL, "Elamite" }, - { "eng", "en", "English" }, - { "enm", NULL, "English, Middle (1100-1500)" }, - { "epo", "eo", "Esperanto" }, - { "est", "et", "Estonian" }, - { "ewe", "ee", "Ewe" }, - { "ewo", NULL, "Ewondo" }, - { "fan", NULL, "Fang" }, - { "fao", "fo", "Faroese" }, - { "fat", NULL, "Fanti" }, - { "fij", "fj", "Fijian" }, - { "fil", NULL, "Filipino; Pilipino" }, - { "fin", "fi", "Finnish" }, - { "fiu", NULL, "Finno-Ugrian (Other)" }, - { "fon", NULL, "Fon" }, - { "fre/fra", "fr", "French" }, //dual codes - { "frm", NULL, "French, Middle (ca.1400-1600)" }, - { "fro", NULL, "French, Old (842-ca.1400)" }, - { "frr", NULL, "Northern Frisian" }, - { "frs", NULL, "Eastern Frisian" }, - { "fry", "fy", "Western Frisian" }, - { "ful", "ff", "Fulah" }, - { "fur", NULL, "Friulian" }, - { "gaa", NULL, "Ga" }, - { "gay", NULL, "Gayo" }, - { "gba", NULL, "Gbaya" }, - { "gem", NULL, "Germanic (Other)" }, - { "geo/kat", "ka", "Georgian" }, //dual codes - { "ger/deu", "de", "German" }, //dual codes - { "gez", NULL, "Geez" }, - { "gil", NULL, "Gilbertese" }, - { "gla", "gd", "Gaelic; Scottish Gaelic" }, - { "gle", "ga", "Irish" }, - { "glg", "gl", "Galician" }, - { "glv", "gv", "Manx" }, - { "gmh", NULL, "German, Middle High (ca.1050-1500)" }, - { "goh", NULL, "German, Old High (ca.750-1050)" }, - { "gon", NULL, "Gondi" }, - { "gor", NULL, "Gorontalo" }, - { "got", NULL, "Gothic" }, - { "grb", NULL, "Grebo" }, - { "grc", NULL, "Greek, Ancient (to 1453)" }, - { "gre/ell", "el", "Greek, Modern (1453-)" }, //dual codes - { "grn", "gn", "Guarani" }, - { "gsw", NULL, "Alemanic; Swiss German" }, - { "guj", "gu", "Gujarati" }, - { "gwi", NULL, "Gwichin" }, - { "hai", NULL, "Haida" }, - { "hat", "ht", "Haitian; Haitian Creole" }, - { "hau", "ha", "Hausa" }, - { "haw", NULL, "Hawaiian" }, - { "heb", "he", "Hebrew" }, - { "her", "hz", "Herero" }, - { "hil", NULL, "Hiligaynon" }, - { "him", NULL, "Himachali" }, - { "hin", "hi", "Hindi" }, - { "hit", NULL, "Hittite" }, - { "hmn", NULL, "Hmong" }, - { "hmo", "ho", "Hiri Motu" }, - { "hsb", NULL, "Upper Sorbian" }, - { "hun", "hu", "Hungarian" }, - { "hup", NULL, "Hupa" }, - { "arm/hye", "hy", "Armenian" }, - { "iba", NULL, "Iban" }, - { "ibo", "ig", "Igbo" }, - { "ice/isl", "is", "Icelandic" }, //dual codes - { "ido", "io", "Ido" }, - { "iii", "ii", "Sichuan Yi" }, - { "ijo", NULL, "Ijo" }, - { "iku", "iu", "Inuktitut" }, - { "ile", "ie", "Interlingue" }, - { "ilo", NULL, "Iloko" }, - { "ina", "ia", "Interlingua (International Auxiliary, Language Association)" }, - { "inc", NULL, "Indic (Other)" }, - { "ind", "id", "Indonesian" }, - { "ine", NULL, "Indo-European (Other)" }, - { "inh", NULL, "Ingush" }, - { "ipk", "ik", "Inupiaq" }, - { "ira", NULL, "Iranian (Other)" }, - { "iro", NULL, "Iroquoian languages" }, - { "ita", "it", "Italian" }, - { "jav", "jv", "Javanese" }, - { "jbo", NULL, "Lojban" }, - { "jpn", "ja", "Japanese" }, - { "jpr", NULL, "Judeo-Persian" }, - { "jrb", NULL, "Judeo-Arabic" }, - { "kaa", NULL, "Kara-Kalpak" }, - { "kab", NULL, "Kabyle" }, - { "kac", NULL, "Kachin" }, - { "kal", "kl", "Kalaallisut; Greenlandic" }, - { "kam", NULL, "Kamba" }, - { "kan", "kn", "Kannada" }, - { "kar", NULL, "Karen" }, - { "kas", "ks", "Kashmiri" }, - { "kau", "kr", "Kanuri" }, - { "kaw", NULL, "Kawi" }, - { "kaz", "kk", "Kazakh" }, - { "kbd", NULL, "Kabardian" }, - { "kha", NULL, "Khasi" }, - { "khi", NULL, "Khoisan (Other)" }, - { "khm", "km", "Khmer" }, - { "kho", NULL, "Khotanese" }, - { "kik", "ki", "Kikuyu; Gikuyu" }, - { "kin", "rw", "Kinyarwanda" }, - { "kir", "ky", "Kirghiz" }, - { "kmb", NULL, "Kimbundu" }, - { "kok", NULL, "Konkani" }, - { "kom", "kv", "Komi" }, - { "kon", "kg", "Kongo" }, - { "kor", "ko", "Korean" }, - { "kos", NULL, "Kosraean" }, - { "kpe", NULL, "Kpelle" }, - { "krc", NULL, "Karachay-Balkar" }, - { "krl", NULL, "Karelian" }, - { "kro", NULL, "Kru" }, - { "kru", NULL, "Kurukh" }, - { "kua", "kj", "Kuanyama; Kwanyama" }, - { "kum", NULL, "Kumyk" }, - { "kur", "ku", "Kurdish" }, - { "kut", NULL, "Kutenai" }, - { "lad", NULL, "Ladino" }, - { "lah", NULL, "Lahnda" }, - { "lam", NULL, "Lamba" }, - { "lao", "lo", "Lao" }, - { "lat", "la", "Latin" }, - { "lav", "lv", "Latvian" }, - { "lez", NULL, "Lezghian" }, - { "lim", "li", "Limburgan; Limburger; Limburgish" }, - { "lin", "ln", "Lingala" }, - { "lit", "lt", "Lithuanian" }, - { "lol", NULL, "Mongo" }, - { "loz", NULL, "Lozi" }, - { "ltz", "lb", "Luxembourgish; Letzeburgesch" }, - { "lua", NULL, "Luba-Lulua" }, - { "lub", "lu", "Luba-Katanga" }, - { "lug", "lg", "Ganda" }, - { "lui", NULL, "Luiseno" }, - { "lun", NULL, "Lunda" }, - { "luo", NULL, "Luo (Kenya and Tanzania)" }, - { "lus", NULL, "Lushai" }, - { "mad", NULL, "Madurese" }, - { "mag", NULL, "Magahi" }, - { "mah", "mh", "Marshallese" }, - { "mai", NULL, "Maithili" }, - { "mak", NULL, "Makasar" }, - { "mal", "ml", "Malayalam" }, - { "man", NULL, "Mandingo" }, - { "map", NULL, "Austronesian (Other)" }, - { "mar", "mr", "Marathi" }, - { "mas", NULL, "Masai" }, - { "may/msa", "ms", "Malay" }, //dual codes - { "mdf", NULL, "Moksha" }, - { "mdr", NULL, "Mandar" }, - { "men", NULL, "Mende" }, - { "mga", NULL, "Irish, Middle (900-1200)" }, - { "mic", NULL, "Mi'kmaq; Micmac" }, - { "min", NULL, "Minangkabau" }, - { "mis", NULL, "Miscellaneous languages" }, - { "mac/mkd", "mk", "Macedonian" }, //dual codes - { "mkh", NULL, "Mon-Khmer (Other)" }, - { "mlg", "mg", "Malagasy" }, - { "mlt", "mt", "Maltese" }, - { "mnc", NULL, "Manchu" }, - { "mni", NULL, "Manipuri" }, - { "mno", NULL, "Manobo languages" }, - { "moh", NULL, "Mohawk" }, - { "mol", "mo", "Moldavian" }, - { "mon", "mn", "Mongolian" }, - { "mos", NULL, "Mossi" }, - { "mao/mri", "mi", "Maori" }, //dual codes - { "mul", NULL, "Multiple languages" }, - { "mun", NULL, "Munda languages" }, - { "mus", NULL, "Creek" }, - { "mwl", NULL, "Mirandese" }, - { "mwr", NULL, "Marwari" }, - { "myn", NULL, "Mayan languages" }, - { "myv", NULL, "Erzya" }, - { "nah", NULL, "Nahuatl" }, - { "nai", NULL, "North American Indian" }, - { "nap", NULL, "Neapolitan" }, - { "nau", "na", "Nauru" }, - { "nav", "nv", "Navajo; Navaho" }, - { "nbl", "nr", "Ndebele, South; South Ndebele" }, - { "nde", "nd", "Ndebele, North; North Ndebele" }, - { "ndo", "ng", "Ndonga" }, - { "nds", NULL, "Low German; Low Saxon; German, Low; Saxon, Low" }, - { "nep", "ne", "Nepali" }, - { "new", NULL, "Newari; Nepal Bhasa" }, - { "nia", NULL, "Nias" }, - { "nic", NULL, "Niger-Kordofanian (Other)" }, - { "niu", NULL, "Niuean" }, - { "nno", "nn", "Norwegian Nynorsk; Nynorsk, Norwegian" }, - { "nob", "nb", "Norwegian Bokml; Bokml, Norwegian" }, - { "nog", NULL, "Nogai" }, - { "non", NULL, "Norse, Old" }, - { "nor", "no", "Norwegian" }, - { "nqo", NULL, "N'ko" }, - { "nso", NULL, "Northern Sotho, Pedi; Sepedi" }, - { "nub", NULL, "Nubian languages" }, - { "nwc", NULL, "Classical Newari; Old Newari; Classical Nepal Bhasa" }, - { "nya", "ny", "Chichewa; Chewa; Nyanja" }, - { "nym", NULL, "Nyamwezi" }, - { "nyn", NULL, "Nyankole" }, - { "nyo", NULL, "Nyoro" }, - { "nzi", NULL, "Nzima" }, - { "oci", "oc", "Occitan (post 1500); Provenal" }, - { "oji", "oj", "Ojibwa" }, - { "ori", "or", "Oriya" }, - { "orm", "om", "Oromo" }, - { "osa", NULL, "Osage" }, - { "oss", "os", "Ossetian; Ossetic" }, - { "ota", NULL, "Turkish, Ottoman (1500-1928)" }, - { "oto", NULL, "Otomian languages" }, - { "paa", NULL, "Papuan (Other)" }, - { "pag", NULL, "Pangasinan" }, - { "pal", NULL, "Pahlavi" }, - { "pam", NULL, "Pampanga" }, - { "pan", "pa", "Panjabi; Punjabi" }, - { "pap", NULL, "Papiamento" }, - { "pau", NULL, "Palauan" }, - { "peo", NULL, "Persian, Old (ca.600-400 B.C.)" }, - { "per/fas", "fa", "Persian" }, //dual codes - { "phi", NULL, "Philippine (Other)" }, - { "phn", NULL, "Phoenician" }, - { "pli", "pi", "Pali" }, - { "pol", "pl", "Polish" }, - { "pon", NULL, "Pohnpeian" }, - { "por", "pt", "Portuguese" }, - { "pra", NULL, "Prakrit languages" }, - { "pro", NULL, "Provenal, Old (to 1500)" }, - { "pus", "ps", "Pushto" }, - //{ "qaa-qtz", NULL, "Reserved for local use" }, - { "que", "qu", "Quechua" }, - { "raj", NULL, "Rajasthani" }, - { "rap", NULL, "Rapanui" }, - { "rar", NULL, "Rarotongan" }, - { "roa", NULL, "Romance (Other)" }, - { "roh", "rm", "Raeto-Romance" }, - { "rom", NULL, "Romany" }, - { "rum/ron", "ro", "Romanian" }, //dual codes - { "run", "rn", "Rundi" }, - { "rup", NULL, "Aromanian; Arumanian; Macedo-Romanian" }, - { "rus", "ru", "Russian" }, - { "sad", NULL, "Sandawe" }, - { "sag", "sg", "Sango" }, - { "sah", NULL, "Yakut" }, - { "sai", NULL, "South American Indian (Other)" }, - { "sal", NULL, "Salishan languages" }, - { "sam", NULL, "Samaritan Aramaic" }, - { "san", "sa", "Sanskrit" }, - { "sas", NULL, "Sasak" }, - { "sat", NULL, "Santali" }, - { "scn", NULL, "Sicilian" }, - { "sco", NULL, "Scots" }, - { "scr/hrv", "hr", "Croatian" }, //dual codes - { "sel", NULL, "Selkup" }, - { "sem", NULL, "Semitic (Other)" }, - { "sga", NULL, "Irish, Old (to 900)" }, - { "sgn", NULL, "Sign Languages" }, - { "shn", NULL, "Shan" }, - { "sid", NULL, "Sidamo" }, - { "sin", "si", "Sinhala; Sinhalese" }, - { "sio", NULL, "Siouan languages" }, - { "sit", NULL, "Sino-Tibetan (Other)" }, - { "sla", NULL, "Slavic (Other)" }, - { "slo/slk", "sk", "Slovak" }, //dual codes - { "slv", "sl", "Slovenian" }, - { "sma", NULL, "Southern Sami" }, - { "sme", "se", "Northern Sami" }, - { "smi", NULL, "Sami languages (Other)" }, - { "smj", NULL, "Lule Sami" }, - { "smn", NULL, "Inari Sami" }, - { "smo", "sm", "Samoan" }, - { "sms", NULL, "Skolt Sami" }, - { "sna", "sn", "Shona" }, - { "snd", "sd", "Sindhi" }, - { "snk", NULL, "Soninke" }, - { "sog", NULL, "Sogdian" }, - { "som", "so", "Somali" }, - { "son", NULL, "Songhai" }, - { "sot", "st", "Sotho, Southern" }, - { "spa", "es", "Spanish; Castilian" }, - { "srd", "sc", "Sardinian" }, - { "srn", NULL, "Sranan Togo" }, - { "scc/srp", "sr", "Serbian" }, //dual codes - { "srr", NULL, "Serer" }, - { "ssa", NULL, "Nilo-Saharan (Other)" }, - { "ssw", "ss", "Swati" }, - { "suk", NULL, "Sukuma" }, - { "sun", "su", "Sundanese" }, - { "sus", NULL, "Susu" }, - { "sux", NULL, "Sumerian" }, - { "swa", "sw", "Swahili" }, - { "swe", "sv", "Swedish" }, - { "syr", NULL, "Syriac" }, - { "tah", "ty", "Tahitian" }, - { "tai", NULL, "Tai (Other)" }, - { "tam", "ta", "Tamil" }, - { "tat", "tt", "Tatar" }, - { "tel", "te", "Telugu" }, - { "tem", NULL, "Timne" }, - { "ter", NULL, "Tereno" }, - { "tet", NULL, "Tetum" }, - { "tgk", "tg", "Tajik" }, - { "tgl", "tl", "Tagalog" }, - { "tha", "th", "Thai" }, - { "tib/bod", "bo", "Tibetan" }, //dual codes - { "tig", NULL, "Tigre" }, - { "tir", "ti", "Tigrinya" }, - { "tiv", NULL, "Tiv" }, - { "tkl", NULL, "Tokelau" }, - { "tlh", NULL, "Klingon; tlhIngan-Hol" }, - { "tli", NULL, "Tlingit" }, - { "tmh", NULL, "Tamashek" }, - { "tog", NULL, "Tonga (Nyasa)" }, - { "ton", "to", "Tonga (Tonga Islands)" }, - { "tpi", NULL, "Tok Pisin" }, - { "tsi", NULL, "Tsimshian" }, - { "tsn", "tn", "Tswana" }, - { "tso", "ts", "Tsonga" }, - { "tuk", "tk", "Turkmen" }, - { "tum", NULL, "Tumbuka" }, - { "tup", NULL, "Tupi languages" }, - { "tur", "tr", "Turkish" }, - { "tut", NULL, "Altaic (Other)" }, - { "tvl", NULL, "Tuvalu" }, - { "twi", "tw", "Twi" }, - { "tyv", NULL, "Tuvinian" }, - { "udm", NULL, "Udmurt" }, - { "uga", NULL, "Ugaritic" }, - { "uig", "ug", "Uighur; Uyghur" }, - { "ukr", "uk", "Ukrainian" }, - { "umb", NULL, "Umbundu" }, - { "und", NULL, "Undetermined" }, - { "urd", "ur", "Urdu" }, - { "uzb", "uz", "Uzbek" }, - { "vai", NULL, "Vai" }, - { "ven", "ve", "Venda" }, - { "vie", "vi", "Vietnamese" }, - { "vol", "vo", "Volapk" }, - { "vot", NULL, "Votic" }, - { "wak", NULL, "Wakashan languages" }, - { "wal", NULL, "Walamo" }, - { "war", NULL, "Waray" }, - { "was", NULL, "Washo" }, - { "wel/cym", "cy", "Welsh" }, // //dual codes - { "wen", NULL, "Sorbian languages" }, - { "wln", "wa", "Walloon" }, - { "wol", "wo", "Wolof" }, - { "xal", NULL, "Kalmyk; Oirat" }, - { "xho", "xh", "Xhosa" }, - { "yao", NULL, "Yao" }, - { "yap", NULL, "Yapese" }, - { "yid", "yi", "Yiddish" }, - { "yor", "yo", "Yoruba" }, - { "ypk", NULL, "Yupik languages" }, - { "zap", NULL, "Zapotec" }, - { "zen", NULL, "Zenaga" }, - { "zha", "za", "Zhuang; Chuang" }, - { "chi/zho", "zh", "Chinese" }, //dual codes - { "znd", NULL, "Zande" }, - { "zul", "zu", "Zulu" }, - { "zun", NULL, "Zuni" }, - { "zxx", NULL, "No linguistic content" } -}; + {"aar", "aa", "Afar"}, + {"abk", "ab", "Abkhazian"}, + {"ace", NULL, "Achinese"}, + {"ach", NULL, "Acoli"}, + {"ada", NULL, "Adangme"}, + {"ady", NULL, "Adyghe; Adygei"}, + {"afa", NULL, "Afro-Asiatic (Other)"}, + {"afh", NULL, "Afrihili"}, + {"afr", "af", "Afrikaans"}, + {"ain", NULL, "Ainu"}, + {"aka", "ak", "Akan"}, + {"akk", NULL, "Akkadian"}, + {"alb/sqi", "sq", "Albanian"}, // dual codes + {"ale", NULL, "Aleut"}, + {"alg", NULL, "Algonquian languages"}, + {"alt", NULL, "Southern Altai"}, + {"amh", "am", "Amharic"}, + {"ang", NULL, "English, Old (ca.450-1100)"}, + {"anp", NULL, "Angika"}, + {"apa", NULL, "Apache languages"}, + {"ara", "ar", "Arabic"}, + {"arc", NULL, "Aramaic"}, + {"arg", "an", "Aragonese"}, + {"arm/hye", "hy", "Armenian"}, // dual codes + {"arn", NULL, "Araucanian"}, + {"arp", NULL, "Arapaho"}, + {"art", NULL, "Artificial (Other)"}, + {"arw", NULL, "Arawak"}, + {"asm", "as", "Assamese"}, + {"ast", NULL, "Asturian; Bable"}, + {"ath", NULL, "Athapascan languages"}, + {"aus", NULL, "Australian languages"}, + {"ava", "av", "Avaric"}, + {"ave", "ae", "Avestan"}, + {"awa", NULL, "Awadhi"}, + {"aym", "ay", "Aymara"}, + {"aze", "az", "Azerbaijani"}, + {"bad", NULL, "Banda"}, + {"bai", NULL, "Bamileke languages"}, + {"bak", "ba", "Bashkir"}, + {"bal", NULL, "Baluchi"}, + {"bam", "bm", "Bambara"}, + {"ban", NULL, "Balinese"}, + {"baq/eus", "eu", "Basque"}, // dual codes + {"bas", NULL, "Basa"}, + {"bat", NULL, "Baltic (Other)"}, + {"bej", NULL, "Beja"}, + {"bel", "be", "Belarusian"}, + {"bem", NULL, "Bemba"}, + {"ben", "bn", "Bengali"}, + {"ber", NULL, "Berber (Other)"}, + {"bho", NULL, "Bhojpuri"}, + {"bih", "bh", "Bihari"}, + {"bik", NULL, "Bikol"}, + {"bin", NULL, "Bini"}, + {"bis", "bi", "Bislama"}, + {"bla", NULL, "Siksika"}, + {"bnt", NULL, "Bantu (Other)"}, + {"bos", "bs", "Bosnian"}, + {"bra", NULL, "Braj"}, + {"bre", "br", "Breton"}, + {"btk", NULL, "Batak (Indonesia)"}, + {"bua", NULL, "Buriat"}, + {"bug", NULL, "Buginese"}, + {"bul", "bg", "Bulgarian"}, + {"bur/mya", "my", "Burmese"}, // dual codes + {"byn", NULL, "Blin; Bilin"}, + {"cad", NULL, "Caddo"}, + {"cai", NULL, "Central American Indian (Other)"}, + {"car", NULL, "Carib"}, + {"cat", "ca", "Catalan; Valencian"}, + {"cau", NULL, "Caucasian (Other)"}, + {"ceb", NULL, "Cebuano"}, + {"cel", NULL, "Celtic (Other)"}, + {"cha", "ch", "Chamorro"}, + {"chb", NULL, "Chibcha"}, + {"che", "ce", "Chechen"}, + {"chg", NULL, "Chagatai"}, + {"chk", NULL, "Chuukese"}, + {"chm", NULL, "Mari"}, + {"chn", NULL, "Chinook jargon"}, + {"cho", NULL, "Choctaw"}, + {"chp", NULL, "Chipewyan"}, + {"chr", NULL, "Cherokee"}, + {"chu", + "cu", + "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church " + "Slavonic"}, + {"chv", "cv", "Chuvash"}, + {"chy", NULL, "Cheyenne"}, + {"cmc", NULL, "Chamic languages"}, + {"cop", NULL, "Coptic"}, + {"cor", "kw", "Cornish"}, + {"cos", "co", "Corsican"}, + {"cpe", NULL, "Creoles and pidgins, English based (Other)"}, + {"cpf", NULL, "Creoles and pidgins, French-based (Other)"}, + {"cpp", NULL, "Creoles and pidgins, Portuguese-based (Other)"}, + {"cre", "cr", "Cree"}, + {"crh", NULL, "Crimean Tatar; Crimean Turkish"}, + {"crp", NULL, "Creoles and pidgins (Other)"}, + {"csb", NULL, "Kashubian"}, + {"cus", NULL, "Cushitic (Other)"}, + {"cze/ces", "cs", "Czech"}, // dual codes + {"dak", NULL, "Dakota"}, + {"dan", "da", "Danish"}, + {"dar", NULL, "Dargwa"}, + {"day", NULL, "Dayak"}, + {"del", NULL, "Delaware"}, + {"den", NULL, "Slave (Athapascan)"}, + {"dgr", NULL, "Dogrib"}, + {"din", NULL, "Dinka"}, + {"div", "dv", "Divehi; Dhivehi; Maldivian"}, + {"doi", NULL, "Dogri"}, + {"dra", NULL, "Dravidian (Other)"}, + {"dsb", NULL, "Lower Sorbian"}, + {"dua", NULL, "Duala"}, + {"dum", NULL, "Dutch, Middle (ca.1050-1350)"}, + {"dut/nld", "nl", "Dutch; Flemish"}, // dual codes + {"dyu", NULL, "Dyula"}, + {"dzo", "dz", "Dzongkha"}, + {"efi", NULL, "Efik"}, + {"egy", NULL, "Egyptian (Ancient)"}, + {"eka", NULL, "Ekajuk"}, + {"elx", NULL, "Elamite"}, + {"eng", "en", "English"}, + {"enm", NULL, "English, Middle (1100-1500)"}, + {"epo", "eo", "Esperanto"}, + {"est", "et", "Estonian"}, + {"ewe", "ee", "Ewe"}, + {"ewo", NULL, "Ewondo"}, + {"fan", NULL, "Fang"}, + {"fao", "fo", "Faroese"}, + {"fat", NULL, "Fanti"}, + {"fij", "fj", "Fijian"}, + {"fil", NULL, "Filipino; Pilipino"}, + {"fin", "fi", "Finnish"}, + {"fiu", NULL, "Finno-Ugrian (Other)"}, + {"fon", NULL, "Fon"}, + {"fre/fra", "fr", "French"}, // dual codes + {"frm", NULL, "French, Middle (ca.1400-1600)"}, + {"fro", NULL, "French, Old (842-ca.1400)"}, + {"frr", NULL, "Northern Frisian"}, + {"frs", NULL, "Eastern Frisian"}, + {"fry", "fy", "Western Frisian"}, + {"ful", "ff", "Fulah"}, + {"fur", NULL, "Friulian"}, + {"gaa", NULL, "Ga"}, + {"gay", NULL, "Gayo"}, + {"gba", NULL, "Gbaya"}, + {"gem", NULL, "Germanic (Other)"}, + {"geo/kat", "ka", "Georgian"}, // dual codes + {"ger/deu", "de", "German"}, // dual codes + {"gez", NULL, "Geez"}, + {"gil", NULL, "Gilbertese"}, + {"gla", "gd", "Gaelic; Scottish Gaelic"}, + {"gle", "ga", "Irish"}, + {"glg", "gl", "Galician"}, + {"glv", "gv", "Manx"}, + {"gmh", NULL, "German, Middle High (ca.1050-1500)"}, + {"goh", NULL, "German, Old High (ca.750-1050)"}, + {"gon", NULL, "Gondi"}, + {"gor", NULL, "Gorontalo"}, + {"got", NULL, "Gothic"}, + {"grb", NULL, "Grebo"}, + {"grc", NULL, "Greek, Ancient (to 1453)"}, + {"gre/ell", "el", "Greek, Modern (1453-)"}, // dual codes + {"grn", "gn", "Guarani"}, + {"gsw", NULL, "Alemanic; Swiss German"}, + {"guj", "gu", "Gujarati"}, + {"gwi", NULL, "Gwich\u00abin"}, + {"hai", NULL, "Haida"}, + {"hat", "ht", "Haitian; Haitian Creole"}, + {"hau", "ha", "Hausa"}, + {"haw", NULL, "Hawaiian"}, + {"heb", "he", "Hebrew"}, + {"her", "hz", "Herero"}, + {"hil", NULL, "Hiligaynon"}, + {"him", NULL, "Himachali"}, + {"hin", "hi", "Hindi"}, + {"hit", NULL, "Hittite"}, + {"hmn", NULL, "Hmong"}, + {"hmo", "ho", "Hiri Motu"}, + {"hsb", NULL, "Upper Sorbian"}, + {"hun", "hu", "Hungarian"}, + {"hup", NULL, "Hupa"}, + {"arm/hye", "hy", "Armenian"}, + {"iba", NULL, "Iban"}, + {"ibo", "ig", "Igbo"}, + {"ice/isl", "is", "Icelandic"}, // dual codes + {"ido", "io", "Ido"}, + {"iii", "ii", "Sichuan Yi"}, + {"ijo", NULL, "Ijo"}, + {"iku", "iu", "Inuktitut"}, + {"ile", "ie", "Interlingue"}, + {"ilo", NULL, "Iloko"}, + {"ina", + "ia", + "Interlingua (International Auxiliary, Language Association)"}, + {"inc", NULL, "Indic (Other)"}, + {"ind", "id", "Indonesian"}, + {"ine", NULL, "Indo-European (Other)"}, + {"inh", NULL, "Ingush"}, + {"ipk", "ik", "Inupiaq"}, + {"ira", NULL, "Iranian (Other)"}, + {"iro", NULL, "Iroquoian languages"}, + {"ita", "it", "Italian"}, + {"jav", "jv", "Javanese"}, + {"jbo", NULL, "Lojban"}, + {"jpn", "ja", "Japanese"}, + {"jpr", NULL, "Judeo-Persian"}, + {"jrb", NULL, "Judeo-Arabic"}, + {"kaa", NULL, "Kara-Kalpak"}, + {"kab", NULL, "Kabyle"}, + {"kac", NULL, "Kachin"}, + {"kal", "kl", "Kalaallisut; Greenlandic"}, + {"kam", NULL, "Kamba"}, + {"kan", "kn", "Kannada"}, + {"kar", NULL, "Karen"}, + {"kas", "ks", "Kashmiri"}, + {"kau", "kr", "Kanuri"}, + {"kaw", NULL, "Kawi"}, + {"kaz", "kk", "Kazakh"}, + {"kbd", NULL, "Kabardian"}, + {"kha", NULL, "Khasi"}, + {"khi", NULL, "Khoisan (Other)"}, + {"khm", "km", "Khmer"}, + {"kho", NULL, "Khotanese"}, + {"kik", "ki", "Kikuyu; Gikuyu"}, + {"kin", "rw", "Kinyarwanda"}, + {"kir", "ky", "Kirghiz"}, + {"kmb", NULL, "Kimbundu"}, + {"kok", NULL, "Konkani"}, + {"kom", "kv", "Komi"}, + {"kon", "kg", "Kongo"}, + {"kor", "ko", "Korean"}, + {"kos", NULL, "Kosraean"}, + {"kpe", NULL, "Kpelle"}, + {"krc", NULL, "Karachay-Balkar"}, + {"krl", NULL, "Karelian"}, + {"kro", NULL, "Kru"}, + {"kru", NULL, "Kurukh"}, + {"kua", "kj", "Kuanyama; Kwanyama"}, + {"kum", NULL, "Kumyk"}, + {"kur", "ku", "Kurdish"}, + {"kut", NULL, "Kutenai"}, + {"lad", NULL, "Ladino"}, + {"lah", NULL, "Lahnda"}, + {"lam", NULL, "Lamba"}, + {"lao", "lo", "Lao"}, + {"lat", "la", "Latin"}, + {"lav", "lv", "Latvian"}, + {"lez", NULL, "Lezghian"}, + {"lim", "li", "Limburgan; Limburger; Limburgish"}, + {"lin", "ln", "Lingala"}, + {"lit", "lt", "Lithuanian"}, + {"lol", NULL, "Mongo"}, + {"loz", NULL, "Lozi"}, + {"ltz", "lb", "Luxembourgish; Letzeburgesch"}, + {"lua", NULL, "Luba-Lulua"}, + {"lub", "lu", "Luba-Katanga"}, + {"lug", "lg", "Ganda"}, + {"lui", NULL, "Luiseno"}, + {"lun", NULL, "Lunda"}, + {"luo", NULL, "Luo (Kenya and Tanzania)"}, + {"lus", NULL, "Lushai"}, + {"mad", NULL, "Madurese"}, + {"mag", NULL, "Magahi"}, + {"mah", "mh", "Marshallese"}, + {"mai", NULL, "Maithili"}, + {"mak", NULL, "Makasar"}, + {"mal", "ml", "Malayalam"}, + {"man", NULL, "Mandingo"}, + {"map", NULL, "Austronesian (Other)"}, + {"mar", "mr", "Marathi"}, + {"mas", NULL, "Masai"}, + {"may/msa", "ms", "Malay"}, // dual codes + {"mdf", NULL, "Moksha"}, + {"mdr", NULL, "Mandar"}, + {"men", NULL, "Mende"}, + {"mga", NULL, "Irish, Middle (900-1200)"}, + {"mic", NULL, "Mi'kmaq; Micmac"}, + {"min", NULL, "Minangkabau"}, + {"mis", NULL, "Miscellaneous languages"}, + {"mac/mkd", "mk", "Macedonian"}, // dual codes + {"mkh", NULL, "Mon-Khmer (Other)"}, + {"mlg", "mg", "Malagasy"}, + {"mlt", "mt", "Maltese"}, + {"mnc", NULL, "Manchu"}, + {"mni", NULL, "Manipuri"}, + {"mno", NULL, "Manobo languages"}, + {"moh", NULL, "Mohawk"}, + {"mol", "mo", "Moldavian"}, + {"mon", "mn", "Mongolian"}, + {"mos", NULL, "Mossi"}, + {"mao/mri", "mi", "Maori"}, // dual codes + {"mul", NULL, "Multiple languages"}, + {"mun", NULL, "Munda languages"}, + {"mus", NULL, "Creek"}, + {"mwl", NULL, "Mirandese"}, + {"mwr", NULL, "Marwari"}, + {"myn", NULL, "Mayan languages"}, + {"myv", NULL, "Erzya"}, + {"nah", NULL, "Nahuatl"}, + {"nai", NULL, "North American Indian"}, + {"nap", NULL, "Neapolitan"}, + {"nau", "na", "Nauru"}, + {"nav", "nv", "Navajo; Navaho"}, + {"nbl", "nr", "Ndebele, South; South Ndebele"}, + {"nde", "nd", "Ndebele, North; North Ndebele"}, + {"ndo", "ng", "Ndonga"}, + {"nds", NULL, "Low German; Low Saxon; German, Low; Saxon, Low"}, + {"nep", "ne", "Nepali"}, + {"new", NULL, "Newari; Nepal Bhasa"}, + {"nia", NULL, "Nias"}, + {"nic", NULL, "Niger-Kordofanian (Other)"}, + {"niu", NULL, "Niuean"}, + {"nno", "nn", "Norwegian Nynorsk; Nynorsk, Norwegian"}, + {"nob", "nb", "Norwegian Bokm\x8cl; Bokm\x8cl, Norwegian"}, + {"nog", NULL, "Nogai"}, + {"non", NULL, "Norse, Old"}, + {"nor", "no", "Norwegian"}, + {"nqo", NULL, "N'ko"}, + {"nso", NULL, "Northern Sotho, Pedi; Sepedi"}, + {"nub", NULL, "Nubian languages"}, + {"nwc", NULL, "Classical Newari; Old Newari; Classical Nepal Bhasa"}, + {"nya", "ny", "Chichewa; Chewa; Nyanja"}, + {"nym", NULL, "Nyamwezi"}, + {"nyn", NULL, "Nyankole"}, + {"nyo", NULL, "Nyoro"}, + {"nzi", NULL, "Nzima"}, + {"oci", "oc", "Occitan (post 1500); Proven\u00c7al"}, + {"oji", "oj", "Ojibwa"}, + {"ori", "or", "Oriya"}, + {"orm", "om", "Oromo"}, + {"osa", NULL, "Osage"}, + {"oss", "os", "Ossetian; Ossetic"}, + {"ota", NULL, "Turkish, Ottoman (1500-1928)"}, + {"oto", NULL, "Otomian languages"}, + {"paa", NULL, "Papuan (Other)"}, + {"pag", NULL, "Pangasinan"}, + {"pal", NULL, "Pahlavi"}, + {"pam", NULL, "Pampanga"}, + {"pan", "pa", "Panjabi; Punjabi"}, + {"pap", NULL, "Papiamento"}, + {"pau", NULL, "Palauan"}, + {"peo", NULL, "Persian, Old (ca.600-400 B.C.)"}, + {"per/fas", "fa", "Persian"}, // dual codes + {"phi", NULL, "Philippine (Other)"}, + {"phn", NULL, "Phoenician"}, + {"pli", "pi", "Pali"}, + {"pol", "pl", "Polish"}, + {"pon", NULL, "Pohnpeian"}, + {"por", "pt", "Portuguese"}, + {"pra", NULL, "Prakrit languages"}, + {"pro", NULL, "Proven\u00c7al, Old (to 1500)"}, + {"pus", "ps", "Pushto"}, + //{ "qaa-qtz", NULL, "Reserved for local use" }, + {"que", "qu", "Quechua"}, + {"raj", NULL, "Rajasthani"}, + {"rap", NULL, "Rapanui"}, + {"rar", NULL, "Rarotongan"}, + {"roa", NULL, "Romance (Other)"}, + {"roh", "rm", "Raeto-Romance"}, + {"rom", NULL, "Romany"}, + {"rum/ron", "ro", "Romanian"}, // dual codes + {"run", "rn", "Rundi"}, + {"rup", NULL, "Aromanian; Arumanian; Macedo-Romanian"}, + {"rus", "ru", "Russian"}, + {"sad", NULL, "Sandawe"}, + {"sag", "sg", "Sango"}, + {"sah", NULL, "Yakut"}, + {"sai", NULL, "South American Indian (Other)"}, + {"sal", NULL, "Salishan languages"}, + {"sam", NULL, "Samaritan Aramaic"}, + {"san", "sa", "Sanskrit"}, + {"sas", NULL, "Sasak"}, + {"sat", NULL, "Santali"}, + {"scn", NULL, "Sicilian"}, + {"sco", NULL, "Scots"}, + {"scr/hrv", "hr", "Croatian"}, // dual codes + {"sel", NULL, "Selkup"}, + {"sem", NULL, "Semitic (Other)"}, + {"sga", NULL, "Irish, Old (to 900)"}, + {"sgn", NULL, "Sign Languages"}, + {"shn", NULL, "Shan"}, + {"sid", NULL, "Sidamo"}, + {"sin", "si", "Sinhala; Sinhalese"}, + {"sio", NULL, "Siouan languages"}, + {"sit", NULL, "Sino-Tibetan (Other)"}, + {"sla", NULL, "Slavic (Other)"}, + {"slo/slk", "sk", "Slovak"}, // dual codes + {"slv", "sl", "Slovenian"}, + {"sma", NULL, "Southern Sami"}, + {"sme", "se", "Northern Sami"}, + {"smi", NULL, "Sami languages (Other)"}, + {"smj", NULL, "Lule Sami"}, + {"smn", NULL, "Inari Sami"}, + {"smo", "sm", "Samoan"}, + {"sms", NULL, "Skolt Sami"}, + {"sna", "sn", "Shona"}, + {"snd", "sd", "Sindhi"}, + {"snk", NULL, "Soninke"}, + {"sog", NULL, "Sogdian"}, + {"som", "so", "Somali"}, + {"son", NULL, "Songhai"}, + {"sot", "st", "Sotho, Southern"}, + {"spa", "es", "Spanish; Castilian"}, + {"srd", "sc", "Sardinian"}, + {"srn", NULL, "Sranan Togo"}, + {"scc/srp", "sr", "Serbian"}, // dual codes + {"srr", NULL, "Serer"}, + {"ssa", NULL, "Nilo-Saharan (Other)"}, + {"ssw", "ss", "Swati"}, + {"suk", NULL, "Sukuma"}, + {"sun", "su", "Sundanese"}, + {"sus", NULL, "Susu"}, + {"sux", NULL, "Sumerian"}, + {"swa", "sw", "Swahili"}, + {"swe", "sv", "Swedish"}, + {"syr", NULL, "Syriac"}, + {"tah", "ty", "Tahitian"}, + {"tai", NULL, "Tai (Other)"}, + {"tam", "ta", "Tamil"}, + {"tat", "tt", "Tatar"}, + {"tel", "te", "Telugu"}, + {"tem", NULL, "Timne"}, + {"ter", NULL, "Tereno"}, + {"tet", NULL, "Tetum"}, + {"tgk", "tg", "Tajik"}, + {"tgl", "tl", "Tagalog"}, + {"tha", "th", "Thai"}, + {"tib/bod", "bo", "Tibetan"}, // dual codes + {"tig", NULL, "Tigre"}, + {"tir", "ti", "Tigrinya"}, + {"tiv", NULL, "Tiv"}, + {"tkl", NULL, "Tokelau"}, + {"tlh", NULL, "Klingon; tlhIngan-Hol"}, + {"tli", NULL, "Tlingit"}, + {"tmh", NULL, "Tamashek"}, + {"tog", NULL, "Tonga (Nyasa)"}, + {"ton", "to", "Tonga (Tonga Islands)"}, + {"tpi", NULL, "Tok Pisin"}, + {"tsi", NULL, "Tsimshian"}, + {"tsn", "tn", "Tswana"}, + {"tso", "ts", "Tsonga"}, + {"tuk", "tk", "Turkmen"}, + {"tum", NULL, "Tumbuka"}, + {"tup", NULL, "Tupi languages"}, + {"tur", "tr", "Turkish"}, + {"tut", NULL, "Altaic (Other)"}, + {"tvl", NULL, "Tuvalu"}, + {"twi", "tw", "Twi"}, + {"tyv", NULL, "Tuvinian"}, + {"udm", NULL, "Udmurt"}, + {"uga", NULL, "Ugaritic"}, + {"uig", "ug", "Uighur; Uyghur"}, + {"ukr", "uk", "Ukrainian"}, + {"umb", NULL, "Umbundu"}, + {"und", NULL, "Undetermined"}, + {"urd", "ur", "Urdu"}, + {"uzb", "uz", "Uzbek"}, + {"vai", NULL, "Vai"}, + {"ven", "ve", "Venda"}, + {"vie", "vi", "Vietnamese"}, + {"vol", "vo", "Volap\u00fck"}, + {"vot", NULL, "Votic"}, + {"wak", NULL, "Wakashan languages"}, + {"wal", NULL, "Walamo"}, + {"war", NULL, "Waray"}, + {"was", NULL, "Washo"}, + {"wel/cym", "cy", "Welsh"}, // //dual codes + {"wen", NULL, "Sorbian languages"}, + {"wln", "wa", "Walloon"}, + {"wol", "wo", "Wolof"}, + {"xal", NULL, "Kalmyk; Oirat"}, + {"xho", "xh", "Xhosa"}, + {"yao", NULL, "Yao"}, + {"yap", NULL, "Yapese"}, + {"yid", "yi", "Yiddish"}, + {"yor", "yo", "Yoruba"}, + {"ypk", NULL, "Yupik languages"}, + {"zap", NULL, "Zapotec"}, + {"zen", NULL, "Zenaga"}, + {"zha", "za", "Zhuang; Chuang"}, + {"chi/zho", "zh", "Chinese"}, // dual codes + {"znd", NULL, "Zande"}, + {"zul", "zu", "Zulu"}, + {"zun", NULL, "Zuni"}, + {"zxx", NULL, "No linguistic content"}}; m_ratings known_ratings[] = { - { "us-tv|TV-MA|600|", "TV-MA" }, - { "us-tv|TV-14|500|", "TV-14" }, - { "us-tv|TV-PG|400|", "TV-PG" }, - { "us-tv|TV-G|300|", "TV-G" }, - { "us-tv|TV-Y7|200|", "TV-Y7" }, - { "us-tv|TV-Y|100|", "TV-Y" }, - //{ "us-tv||0|", "not-applicable" }, //though its a valid flag & some files have this, AP won't be setting it. - { "mpaa|UNRATED|600|", "Unrated" }, - { "mpaa|NC-17|500|", "NC-17" }, - { "mpaa|R|400|", "R" }, - { "mpaa|PG-13|300|", "PG-13" }, - { "mpaa|PG|200|", "PG" }, - { "mpaa|G|100|", "G" } - //{ "mpaa||0|", "not-applicable" } //see above + {"us-tv|TV-MA|600|", "TV-MA"}, + {"us-tv|TV-14|500|", "TV-14"}, + {"us-tv|TV-PG|400|", "TV-PG"}, + {"us-tv|TV-G|300|", "TV-G"}, + {"us-tv|TV-Y7|200|", "TV-Y7"}, + {"us-tv|TV-Y|100|", "TV-Y"}, + //{ "us-tv||0|", "not-applicable" }, //though its a valid flag & + // some files have this, AP won't be setting it. + {"mpaa|UNRATED|600|", "Unrated"}, + {"mpaa|NC-17|500|", "NC-17"}, + {"mpaa|R|400|", "R"}, + {"mpaa|PG-13|300|", "PG-13"}, + {"mpaa|PG|200|", "PG"}, + {"mpaa|G|100|", "G"} + //{ "mpaa||0|", "not-applicable" } //see above }; -char* GenreIntToString(int genre) { - char* return_string = NULL; - if (genre > 0 && genre <= (int)(sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList))) { - return_string = (char*)ID3v1GenreList[genre-1]; - } - return return_string; -} - -uint8_t StringGenreToInt(const char* genre_string) { - uint8_t return_genre = 0; - uint8_t total_genres = (uint8_t)(sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList)); - - for(uint8_t i = 0; i < total_genres; i++) { - if ( strcmp(genre_string, ID3v1GenreList[i]) == 0) { - return_genre = i+1; //the list starts at 0; the embedded genres start at 1 - //fprintf(stdout, "Genre %s is %i\n", ID3v1GenreList[i], return_genre); - break; - } - } - if ( return_genre > total_genres ) { - return_genre = 0; - } - return return_genre; +char *GenreIntToString(int genre) { + char *return_string = NULL; + if (genre > 0 && + genre <= (int)(sizeof(ID3v1GenreList) / sizeof(*ID3v1GenreList))) { + return_string = (char *)ID3v1GenreList[genre - 1]; + } + return return_string; +} + +uint8_t StringGenreToInt(const char *genre_string) { + uint8_t return_genre = 0; + uint8_t total_genres = + (uint8_t)(sizeof(ID3v1GenreList) / sizeof(*ID3v1GenreList)); + + for (uint8_t i = 0; i < total_genres; i++) { + if (strcmp(genre_string, ID3v1GenreList[i]) == 0) { + return_genre = + i + 1; // the list starts at 0; the embedded genres start at 1 + // fprintf(stdout, "Genre %s is %i\n", ID3v1GenreList[i], return_genre); + break; + } + } + if (return_genre > total_genres) { + return_genre = 0; + } + return return_genre; } void ListGenresValues() { - uint8_t total_genres = (uint8_t)(sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList)); - fprintf(stdout, "\tAvailable standard genres - case sensitive.\n"); - - for (uint8_t i = 0; i < total_genres; i++) { - fprintf(stdout, "(%i.) %s\n", i+1, ID3v1GenreList[i]); - } - return; -} - -stiks* MatchStikString(const char* in_stik_string) { - stiks* matching_stik = NULL; - uint8_t total_known_stiks = (sizeof(stikArray)/sizeof(*stikArray)); - - for (uint8_t i = 0; i < total_known_stiks; i++) { - if ( strcmp(in_stik_string, stikArray[i].stik_string) == 0) { - matching_stik = &stikArray[i]; - break; - } - } - return matching_stik; -} - -stiks* MatchStikNumber(uint8_t in_stik_num) { - stiks* matching_stik = NULL; - uint8_t total_known_stiks = (sizeof(stikArray)/sizeof(*stikArray)); - - for (uint8_t i = 0; i < total_known_stiks; i++) { - if ( stikArray[i].stik_number == in_stik_num ) { - matching_stik = &stikArray[i]; - break; - } - } - return matching_stik; + uint8_t total_genres = + (uint8_t)(sizeof(ID3v1GenreList) / sizeof(*ID3v1GenreList)); + fprintf(stdout, "\tAvailable standard genres - case sensitive.\n"); + + for (uint8_t i = 0; i < total_genres; i++) { + fprintf(stdout, "(%i.) %s\n", i + 1, ID3v1GenreList[i]); + } + return; +} + +stiks *MatchStikString(const char *in_stik_string) { + stiks *matching_stik = NULL; + uint8_t total_known_stiks = (sizeof(stikArray) / sizeof(*stikArray)); + + for (uint8_t i = 0; i < total_known_stiks; i++) { + if (strcmp(in_stik_string, stikArray[i].stik_string) == 0) { + matching_stik = &stikArray[i]; + break; + } + } + return matching_stik; +} + +stiks *MatchStikNumber(uint8_t in_stik_num) { + stiks *matching_stik = NULL; + uint8_t total_known_stiks = (sizeof(stikArray) / sizeof(*stikArray)); + + for (uint8_t i = 0; i < total_known_stiks; i++) { + if (stikArray[i].stik_number == in_stik_num) { + matching_stik = &stikArray[i]; + break; + } + } + return matching_stik; } void ListStikValues() { - uint8_t total_known_stiks = (sizeof(stikArray)/sizeof(*stikArray)); - fprintf(stdout, "\tAvailable stik settings - case sensitive (number in parens shows the stik value).\n"); - - for (uint8_t i = 0; i < total_known_stiks; i++) { - fprintf(stdout, "(%u) %s\n", stikArray[i].stik_number, stikArray[i].stik_string); - } - return; -} - -sfIDs* MatchStoreFrontNumber(uint32_t storefrontnum) { - sfIDs* matching_sfID = NULL; - uint8_t total_known_sfs = (sizeof(storefronts)/sizeof(*storefronts)); - - for (uint8_t i = 0; i < total_known_sfs; i++) { - if ( storefronts[i].storefront_number == storefrontnum ) { - matching_sfID = &storefronts[i]; - break; - } - } - return matching_sfID; -} - -bool MatchLanguageCode(const char* in_code) { - bool matching_lang = false; - uint16_t total_known_langs = (uint16_t)(sizeof(known_languages)/sizeof(*known_languages)); - - for (uint16_t i = 0; i < total_known_langs; i++) { - if (strncmp(in_code, known_languages[i].iso639_2_code, 3) == 0) { - matching_lang = true; - break; - } - if (strlen(known_languages[i].iso639_2_code) > 3) { - if (strncmp(in_code, known_languages[i].iso639_2_code+4, 3) == 0) { - matching_lang = true; - break; - } - } - } + uint8_t total_known_stiks = (sizeof(stikArray) / sizeof(*stikArray)); + fprintf(stdout, + "\tAvailable stik settings - case sensitive (number in " + "parens shows the stik value).\n"); + + for (uint8_t i = 0; i < total_known_stiks; i++) { + fprintf(stdout, + "(%u) %s\n", + stikArray[i].stik_number, + stikArray[i].stik_string); + } + return; +} + +sfIDs *MatchStoreFrontNumber(uint32_t storefrontnum) { + sfIDs *matching_sfID = NULL; + uint8_t total_known_sfs = (sizeof(storefronts) / sizeof(*storefronts)); + + for (uint8_t i = 0; i < total_known_sfs; i++) { + if (storefronts[i].storefront_number == storefrontnum) { + matching_sfID = &storefronts[i]; + break; + } + } + return matching_sfID; +} + +bool MatchLanguageCode(const char *in_code) { + bool matching_lang = false; + uint16_t total_known_langs = + (uint16_t)(sizeof(known_languages) / sizeof(*known_languages)); + + for (uint16_t i = 0; i < total_known_langs; i++) { + if (strncmp(in_code, known_languages[i].iso639_2_code, 3) == 0) { + matching_lang = true; + break; + } + if (strlen(known_languages[i].iso639_2_code) > 3) { + if (strncmp(in_code, known_languages[i].iso639_2_code + 4, 3) == 0) { + matching_lang = true; + break; + } + } + } - return matching_lang; + return matching_lang; } void ListLanguageCodes() { - uint16_t total_known_langs = (uint16_t)(sizeof(known_languages)/sizeof(*known_languages)); - fprintf(stdout, "\tAvailable language codes\nISO639-2 code ... English name:\n"); - - for (uint16_t i = 0; i < total_known_langs; i++) { - fprintf(stdout, " %s ... %s\n", known_languages[i].iso639_2_code, known_languages[i].language_in_english); - } - return; + uint16_t total_known_langs = + (uint16_t)(sizeof(known_languages) / sizeof(*known_languages)); + fprintf(stdout, + "\tAvailable language codes\nISO639-2 code ... English name:\n"); + + for (uint16_t i = 0; i < total_known_langs; i++) { + fprintf(stdout, + " %s ... %s\n", + known_languages[i].iso639_2_code, + known_languages[i].language_in_english); + } + return; } void ListMediaRatings() { - uint16_t total_known_ratings = (uint16_t)(sizeof(known_ratings)/sizeof(*known_ratings)); - fprintf(stdout, "\tAvailable ratings for the U.S. rating system:\n"); - - for (uint16_t i = 0; i < total_known_ratings; i++) { - fprintf(stdout, " %s\n", known_ratings[i].media_rating_cli_str); - } - return; + uint16_t total_known_ratings = + (uint16_t)(sizeof(known_ratings) / sizeof(*known_ratings)); + fprintf(stdout, "\tAvailable ratings for the U.S. rating system:\n"); + + for (uint16_t i = 0; i < total_known_ratings; i++) { + fprintf(stdout, " %s\n", known_ratings[i].media_rating_cli_str); + } + return; } void ListTVGenreIDValues() { - uint16_t total_genreidtv = (uint16_t)(sizeof(genreidtv)/sizeof(*genreidtv)); - fprintf(stdout, "\tAvailable iTunes TV Genre IDs:\n"); + uint16_t total_genreidtv = (uint16_t)(sizeof(genreidtv) / sizeof(*genreidtv)); + fprintf(stdout, "\tAvailable iTunes TV Genre IDs:\n"); - for (uint16_t i = 0; i < total_genreidtv; i++) { - fprintf(stdout, "(%u) %s\n", genreidtv[i].genre_id_tv_value, genreidtv[i].genre_id_tv_string); - } - return; + for (uint16_t i = 0; i < total_genreidtv; i++) { + fprintf(stdout, + "(%u) %s\n", + genreidtv[i].genre_id_tv_value, + genreidtv[i].genre_id_tv_string); + } + return; } void ListMovieGenreIDValues() { - uint16_t total_genreidmovie = (uint16_t)(sizeof(genreidmovie)/sizeof(*genreidmovie)); - fprintf(stdout, "\tAvailable iTunes Movie Genre IDs:\n"); - - for (uint16_t i = 0; i < total_genreidmovie; i++) { - fprintf(stdout, "(%u) %s\n", genreidmovie[i].genre_id_movie_value, genreidmovie[i].genre_id_movie_string); - } - return; -} - -const char* Expand_cli_mediastring(const char* cli_rating) { - const char* media_rating = NULL; - uint16_t total_known_ratings = (uint16_t)(sizeof(known_ratings)/sizeof(*known_ratings)); - uint8_t rating_len = strlen(cli_rating); - - for (uint16_t i = 0; i < total_known_ratings; i++) { - if ( strncasecmp(known_ratings[i].media_rating_cli_str, cli_rating, rating_len+1) == 0 ) { - media_rating = known_ratings[i].media_rating; - break; - } - } - return media_rating; -} - -//ID32 for ID3 frame functions -char* ID3GenreIntToString(int genre) { - char* return_string = NULL; - if (genre >= 0 && genre <= 79) { - return_string = (char*)ID3v1GenreList[genre]; - } - return return_string; -} - -uint8_t ID3StringGenreToInt(const char* genre_string) { - uint8_t return_genre = 0xFF; - uint8_t total_genres = 80; - - for(uint8_t i = 0; i < total_genres; i++) { - if ( strcmp(genre_string, ID3v1GenreList[i]) == 0) { - return i; - } - } - if ( return_genre > total_genres ) { - return_genre = 0xFF; - } - return return_genre; + uint16_t total_genreidmovie = + (uint16_t)(sizeof(genreidmovie) / sizeof(*genreidmovie)); + fprintf(stdout, "\tAvailable iTunes Movie Genre IDs:\n"); + + for (uint16_t i = 0; i < total_genreidmovie; i++) { + fprintf(stdout, + "(%u) %s\n", + genreidmovie[i].genre_id_movie_value, + genreidmovie[i].genre_id_movie_string); + } + return; +} + +const char *Expand_cli_mediastring(const char *cli_rating) { + const char *media_rating = NULL; + uint16_t total_known_ratings = + (uint16_t)(sizeof(known_ratings) / sizeof(*known_ratings)); + uint8_t rating_len = strlen(cli_rating); + + for (uint16_t i = 0; i < total_known_ratings; i++) { + if (strncasecmp(known_ratings[i].media_rating_cli_str, + cli_rating, + rating_len + 1) == 0) { + media_rating = known_ratings[i].media_rating; + break; + } + } + return media_rating; +} + +// ID32 for ID3 frame functions +char *ID3GenreIntToString(int genre) { + char *return_string = NULL; + if (genre >= 0 && genre <= 79) { + return_string = (char *)ID3v1GenreList[genre]; + } + return return_string; +} + +uint8_t ID3StringGenreToInt(const char *genre_string) { + uint8_t return_genre = 0xFF; + uint8_t total_genres = 80; + + for (uint8_t i = 0; i < total_genres; i++) { + if (strcmp(genre_string, ID3v1GenreList[i]) == 0) { + return i; + } + } + if (return_genre > total_genres) { + return_genre = 0xFF; + } + return return_genre; } diff -Nru atomicparsley-0.9.6/src/AtomDefs.h atomicparsley-20210715.151551.e7ad03a/src/AtomDefs.h --- atomicparsley-0.9.6/src/AtomDefs.h 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/AtomDefs.h 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - AtomDefs.h - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,264 +10,380 @@ 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 + 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 2006-2007 puck_lock with contributions from others; see the CREDITS file - */ + */ //==================================================================// #include "AtomicParsley.h" atomDefinition KnownAtoms[] = { - //name parent atom(s) container number box_type - {"<()>", {"_ANY_LEVEL"}, UNKNOWN_ATOM_TYPE, UKNOWN_REQUIREMENTS, UNKNOWN_ATOM }, //our unknown atom (self-defined) - - {"ftyp", {"FILE_LEVEL"}, CHILD_ATOM, REQUIRED_ONCE, SIMPLE_ATOM }, - - {"moov", {"FILE_LEVEL"}, PARENT_ATOM, REQUIRED_ONCE, SIMPLE_ATOM }, - - {"mdat", {"FILE_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, - - {"pdin", {"FILE_LEVEL"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, - - {"moof", {"FILE_LEVEL"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, - {"mfhd", {"moof"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - {"traf", {"moof"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"tfhd", {"traf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - {"trun", {"traf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - - {"mfra", {"FILE_LEVEL"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM }, - {"tfra", {"mfra"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"mfro", {"mfra"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - - {"free", {"_ANY_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, - {"skip", {"_ANY_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, - - {"uuid", {"_ANY_LEVEL"}, CHILD_ATOM, REQUIRED_ONCE, EXTENDED_ATOM }, - - {"mvhd", {"moov"}, CHILD_ATOM, REQUIRED_ONCE, VERSIONED_ATOM }, - {"iods", {"moov"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, - {"drm ", {"moov"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, // 3gp/MobileMP4 - {"trak", {"moov"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, - - {"tkhd", {"trak"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, - {"tref", {"trak"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, - {"mdia", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - - {"tapt", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"clef", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"prof", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"enof", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - - {"mdhd", {"mdia"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"minf", {"mdia"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - - {"hdlr", {"mdia", "meta", "minf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, //minf parent present in chapterized - - {"vmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"smhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"hmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"nmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"gmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, //present in chapterized - - {"dinf", {"minf", "meta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //required in minf - - {"dref", {"dinf"}, DUAL_STATE_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - - {"url ", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, - {"urn ", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, - {"alis", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, - {"cios", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, - - {"stbl", {"minf"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"stts", {"stbl"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - {"ctts", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"stsd", {"stbl"}, DUAL_STATE_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - - {"stsz", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"stz2", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - - {"stsc", {"stbl"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - - {"stco", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"co64", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - - {"stss", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"stsh", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"stdp", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"padb", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"sdtp", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"sbgp", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, - {"sbgp", {"stbl"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, - {"stps", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - - {"edts", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"elst", {"edts"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - - {"udta", {"moov", "trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - - {"meta", {"FILE_LEVEL", "moov", "trak", "udta"}, DUAL_STATE_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //optionally contains info - - {"mvex", {"moov"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM }, - {"mehd", {"mvex"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, - {"trex", {"mvex"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - - //{"stsl", {"????"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //contained by a sample entry box - {"subs", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - - {"xml ", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"bxml", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"iloc", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"pitm", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"ipro", {"meta"}, PARENT_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"iinf", {"meta"}, DUAL_STATE_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"infe", {"iinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - - {"sinf", {"ipro", "drms", "drmi"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, //parent atom is also "Protected Sample Entry" - {"frma", {"sinf"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"imif", {"sinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"schm", {"sinf", "srpp"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"schi", {"sinf", "srpp"}, DUAL_STATE_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"skcr", {"sinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - - {"user", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"key ", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //could be required in 'drms'/'drmi' - {"iviv", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"righ", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"name", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"priv", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - - {"iKMS", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, // 'iAEC', '264b', 'iOMA', 'ICSD' - {"iSFM", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - {"iSLT", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //boxes with 'k***' are also here; reserved - {"IKEY", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"hint", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"dpnd", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"ipir", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"mpod", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"sync", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"chap", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //?possible versioned? - - {"ipmc", {"moov", "meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, - - {"tims", {"rtp "}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"tsro", {"rtp "}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"snro", {"rtp "}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - - {"srpp", {"srtp"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - - {"hnti", {"udta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"rtp ", {"hnti"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //'rtp ' is defined twice in different containers - {"sdp ", {"hnti"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - - {"hinf", {"udta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"name", {"udta"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"trpy", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"nump", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"tpyl", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"totl", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"npck", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"maxr", {"hinf"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, - {"dmed", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"dimm", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"drep", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"tmin", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"tmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"pmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"dmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"payt", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"tpay", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - - {"drms", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"drmi", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"alac", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"mp4a", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"mp4s", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"mp4v", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"avc1", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"avcp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"text", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"jpeg", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"tx3g", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"rtp ", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, //"rtp " occurs twice; disparate meanings - {"srtp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, SIMPLE_ATOM }, - {"enca", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"encv", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"enct", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"encs", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"samr", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"sawb", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"sawp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"s263", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"sevc", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"sqcp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"ssmv", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"tmcd", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, - {"mjp2", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, //mjpeg2000 - - {"alac", {"alac"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"avcC", {"avc1", "drmi"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"damr", {"samr", "sawb"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"d263", {"s263"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"dawp", {"sawp"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"devc", {"sevc"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"dqcp", {"sqcp"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"dsmv", {"ssmv"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"bitr", {"d263"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, - {"btrt", {"avc1"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //found in NeroAVC - {"m4ds", {"avc1"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //?possible versioned? - {"ftab", {"tx3g"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, - {"jp2h", {"mjp2"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000 - - {"ihdr", {"jp2h"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000 - {"colr", {"jp2h"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //mjpeg2000 - {"fiel", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000 - {"jp2p", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //mjpeg2000 - {"jsub", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000 - {"orfo", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000 - - {"cprt", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, //the only ISO defined metadata tag; also a 3gp asset - {"titl", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, //3gp assets - {"auth", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - {"perf", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - {"gnre", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - {"dscp", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - {"albm", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - {"yrrc", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, - {"rtng", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - {"clsf", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - {"kywd", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - {"loci", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, - - {"ID32", {"meta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, //id3v2 tag - {"tsel", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, //but only at track level in a 3gp file - - //{"chpl", {"udta"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, //Nero - seems to be versioned - //{"ndrm", {"udta"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, //Nero - seems to be versioned - //{"tags", {"udta"}, CHILD_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM }, //Another Nero-Creation - // ...so if they claim that "tags doesn't have any children", - // why does nerotags.exe say "tshd atom"? If 'tags' doesn't - // have any children, then tshd can't be an atom.... - // Clearly, they are EternallyRight and everyone else is - // always wrong. - - //Pish! Seems that Nero is simply unable to register any atoms. - - {"ilst", {"meta"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM }, //iTunes metadata container - {"----", {"ilst"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, //reverse dns metadata - {"mean", {"----"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - {"name", {"----"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, - - {".><.", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, //support any future named child to dref; keep 4th from end; manual return - - {"esds", {"SAMPLE_DESC"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, //multiple parents; keep 3rd from end; manual return - - {"(..)", {"ilst"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //multiple parents; keep 2nd from end; manual return - {"data", {"ITUNES_METADATA"}, CHILD_ATOM, PARENT_SPECIFIC, VERSIONED_ATOM } //multiple parents - -}; + // name parent atom(s) container + // number + // box_type + {"<()>", + {"_ANY_LEVEL"}, + UNKNOWN_ATOM_TYPE, + UKNOWN_REQUIREMENTS, + UNKNOWN_ATOM}, // our unknown atom (self-defined) + + {"ftyp", {"FILE_LEVEL"}, CHILD_ATOM, REQUIRED_ONCE, SIMPLE_ATOM}, + + {"moov", {"FILE_LEVEL"}, PARENT_ATOM, REQUIRED_ONCE, SIMPLE_ATOM}, + + {"mdat", {"FILE_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM}, + + {"pdin", {"FILE_LEVEL"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM}, + + {"moof", {"FILE_LEVEL"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM}, + {"mfhd", {"moof"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + {"traf", {"moof"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"tfhd", {"traf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + {"trun", {"traf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + + {"mfra", {"FILE_LEVEL"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM}, + {"tfra", {"mfra"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"mfro", {"mfra"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + + {"free", {"_ANY_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM}, + {"skip", {"_ANY_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM}, + + {"uuid", {"_ANY_LEVEL"}, CHILD_ATOM, REQUIRED_ONCE, EXTENDED_ATOM}, + + {"mvhd", {"moov"}, CHILD_ATOM, REQUIRED_ONCE, VERSIONED_ATOM}, + {"iods", {"moov"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM}, + {"drm ", + {"moov"}, + CHILD_ATOM, + OPTIONAL_ONCE, + VERSIONED_ATOM}, // 3gp/MobileMP4 + {"trak", {"moov"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM}, + + {"tkhd", {"trak"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM}, + {"tref", {"trak"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM}, + {"mdia", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + + {"tapt", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"clef", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"prof", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"enof", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + + {"mdhd", {"mdia"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"minf", {"mdia"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + + {"hdlr", + {"mdia", "meta", "minf"}, + CHILD_ATOM, + REQUIRED_ONE, + VERSIONED_ATOM}, // minf parent present in chapterized + + {"vmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"smhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"hmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"nmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"gmhd", + {"minf"}, + CHILD_ATOM, + REQ_FAMILIAL_ONE, + VERSIONED_ATOM}, // present in chapterized + + {"dinf", + {"minf", "meta"}, + PARENT_ATOM, + OPTIONAL_ONE, + SIMPLE_ATOM}, // required in minf + + {"dref", {"dinf"}, DUAL_STATE_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + + {"url ", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM}, + {"urn ", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM}, + {"alis", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM}, + {"cios", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM}, + + {"stbl", {"minf"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"stts", {"stbl"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + {"ctts", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"stsd", {"stbl"}, DUAL_STATE_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + + {"stsz", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"stz2", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + + {"stsc", {"stbl"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + + {"stco", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"co64", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + + {"stss", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"stsh", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"stdp", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"padb", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"sdtp", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"sbgp", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM}, + {"sbgp", {"stbl"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM}, + {"stps", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + + {"edts", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"elst", {"edts"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + + {"udta", {"moov", "trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + + {"meta", + {"FILE_LEVEL", "moov", "trak", "udta"}, + DUAL_STATE_ATOM, + OPTIONAL_ONE, + VERSIONED_ATOM}, // optionally contains info + + {"mvex", {"moov"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM}, + {"mehd", {"mvex"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM}, + {"trex", {"mvex"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + + //{"stsl", {"????"}, CHILD_ATOM, + // OPTIONAL_ONE, + // VERSIONED_ATOM }, //contained by a sample + // entry + // box + {"subs", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + + {"xml ", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"bxml", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"iloc", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"pitm", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"ipro", {"meta"}, PARENT_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"iinf", {"meta"}, DUAL_STATE_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"infe", {"iinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + + {"sinf", + {"ipro", "drms", "drmi"}, + PARENT_ATOM, + REQUIRED_ONE, + SIMPLE_ATOM}, // parent atom is also "Protected Sample Entry" + {"frma", {"sinf"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"imif", {"sinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"schm", {"sinf", "srpp"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"schi", {"sinf", "srpp"}, DUAL_STATE_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"skcr", {"sinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + + {"user", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"key ", + {"schi"}, + CHILD_ATOM, + OPTIONAL_ONE, + VERSIONED_ATOM}, // could be required in 'drms'/'drmi' + {"iviv", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"righ", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"name", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"priv", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + + {"iKMS", + {"schi"}, + CHILD_ATOM, + OPTIONAL_ONE, + VERSIONED_ATOM}, // 'iAEC', '264b', 'iOMA', 'ICSD' + {"iSFM", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + {"iSLT", + {"schi"}, + CHILD_ATOM, + OPTIONAL_ONE, + SIMPLE_ATOM}, // boxes with 'k***' are also here; reserved + {"IKEY", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"hint", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"dpnd", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"ipir", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"mpod", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"sync", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"chap", + {"tref"}, + CHILD_ATOM, + OPTIONAL_ONE, + SIMPLE_ATOM}, //?possible versioned? + + {"ipmc", {"moov", "meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, + + {"tims", {"rtp "}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"tsro", {"rtp "}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"snro", {"rtp "}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + + {"srpp", {"srtp"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + + {"hnti", {"udta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"rtp ", + {"hnti"}, + CHILD_ATOM, + OPTIONAL_ONE, + SIMPLE_ATOM}, //'rtp ' is defined twice in different containers + {"sdp ", {"hnti"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + + {"hinf", {"udta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"name", {"udta"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"trpy", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"nump", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"tpyl", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"totl", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"npck", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"maxr", {"hinf"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM}, + {"dmed", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"dimm", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"drep", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"tmin", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"tmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"pmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"dmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"payt", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"tpay", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + + {"drms", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"drmi", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"alac", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"mp4a", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"mp4s", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"mp4v", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"avc1", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"avcp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"text", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"jpeg", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"tx3g", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"rtp ", + {"stsd"}, + DUAL_STATE_ATOM, + REQ_FAMILIAL_ONE, + VERSIONED_ATOM}, //"rtp " occurs twice; disparate meanings + {"srtp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, SIMPLE_ATOM}, + {"enca", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"encv", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"enct", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"encs", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"samr", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"sawb", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"sawp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"s263", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"sevc", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"sqcp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"ssmv", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"tmcd", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM}, + {"mjp2", + {"stsd"}, + DUAL_STATE_ATOM, + REQ_FAMILIAL_ONE, + VERSIONED_ATOM}, // mjpeg2000 + + {"alac", {"alac"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"avcC", {"avc1", "drmi"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"damr", {"samr", "sawb"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"d263", {"s263"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"dawp", {"sawp"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"devc", {"sevc"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"dqcp", {"sqcp"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"dsmv", {"ssmv"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"bitr", {"d263"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM}, + {"btrt", + {"avc1"}, + CHILD_ATOM, + OPTIONAL_ONE, + SIMPLE_ATOM}, // found in NeroAVC + {"m4ds", + {"avc1"}, + CHILD_ATOM, + OPTIONAL_ONE, + SIMPLE_ATOM}, //?possible versioned? + {"ftab", {"tx3g"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, + {"jp2h", {"mjp2"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000 + + {"ihdr", {"jp2h"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000 + {"colr", {"jp2h"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, // mjpeg2000 + {"fiel", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000 + {"jp2p", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, // mjpeg2000 + {"jsub", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000 + {"orfo", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000 + + {"cprt", + {"udta"}, + CHILD_ATOM, + OPTIONAL_MANY, + PACKED_LANG_ATOM}, // the only ISO defined metadata tag; also a 3gp asset + {"titl", + {"udta"}, + CHILD_ATOM, + OPTIONAL_MANY, + PACKED_LANG_ATOM}, // 3gp assets + {"auth", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + {"perf", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + {"gnre", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + {"dscp", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + {"albm", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + {"yrrc", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM}, + {"rtng", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + {"clsf", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + {"kywd", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + {"loci", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM}, + + {"ID32", + {"meta"}, + CHILD_ATOM, + OPTIONAL_MANY, + PACKED_LANG_ATOM}, // id3v2 tag + {"tsel", + {"udta"}, + CHILD_ATOM, + OPTIONAL_MANY, + SIMPLE_ATOM}, // but only at track level in a 3gp file + + //{"chpl", {"udta"}, CHILD_ATOM, + // OPTIONAL_ONCE, + // VERSIONED_ATOM }, //Nero - seems to be versioned + //{"ndrm", {"udta"}, CHILD_ATOM, + // OPTIONAL_ONCE, + // VERSIONED_ATOM }, //Nero - seems to be versioned + //{"tags", {"udta"}, CHILD_ATOM, + // OPTIONAL_ONCE, + // SIMPLE_ATOM }, //Another Nero-Creation + // ...so if they claim that "tags doesn't have any children", + // why does nerotags.exe say "tshd atom"? If 'tags' doesn't + // have any children, then tshd can't be an atom.... + // Clearly, they are EternallyRight and everyone else is + // always wrong. + + // Pish! Seems that Nero is simply unable to register any atoms. + + {"ilst", + {"meta"}, + PARENT_ATOM, + OPTIONAL_ONCE, + SIMPLE_ATOM}, // iTunes metadata container + {"----", + {"ilst"}, + PARENT_ATOM, + OPTIONAL_MANY, + SIMPLE_ATOM}, // reverse dns metadata + {"mean", {"----"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + {"name", {"----"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM}, + + {".><.", + {"dref"}, + CHILD_ATOM, + OPTIONAL_MANY, + VERSIONED_ATOM}, // support any future named child to dref; keep 4th from + // end; manual return + + {"esds", + {"SAMPLE_DESC"}, + CHILD_ATOM, + REQUIRED_ONE, + SIMPLE_ATOM}, // multiple parents; keep 3rd from end; manual return + + {"(..)", + {"ilst"}, + PARENT_ATOM, + OPTIONAL_ONE, + SIMPLE_ATOM}, // multiple parents; keep 2nd from end; manual return + {"data", + {"ITUNES_METADATA"}, + CHILD_ATOM, + PARENT_SPECIFIC, + VERSIONED_ATOM} // multiple parents +}; diff -Nru atomicparsley-0.9.6/src/AtomicParsley.h atomicparsley-20210715.151551.e7ad03a/src/AtomicParsley.h --- atomicparsley-0.9.6/src/AtomicParsley.h 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/AtomicParsley.h 2021-07-15 22:15:51.000000000 +0000 @@ -5,7 +5,7 @@ /* AtomicParsley - AtomicParsley.h - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -13,7 +13,7 @@ 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 + 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 @@ -31,124 +31,102 @@ #ifndef _UNICODE #define _UNICODE #endif -#if defined (_MSC_VER) +#if defined(_MSC_VER) #define strncasecmp _strnicmp #define _CRT_SECURE_NO_WARNINGS -#pragma warning(disable: 4244) // int64_t assignments to int32_t etc. +#pragma warning(disable : 4244) // int64_t assignments to int32_t etc. #endif #endif -#include "config.h" - #define __STDC_LIMIT_MACROS #define __STDC_FORMAT_MACROS #define __STDC_CONSTANT_MACROS #include #ifdef __GLIBC__ -# define HAVE_LROUNDF 1 +#define HAVE_LROUNDF 1 #endif #include -#include #include -#if HAVE_SYS_TIME_H -# include +#include +#ifndef _WIN32 +#include #endif -#include -#include #include +#include +#include -#if HAVE_STDDEF_H -# include -#endif -#if HAVE_STDINT_H -# include -#endif -#if HAVE_INTTYPES_H -# include -#endif -#if HAVE_FCNTL_H -# include -#endif -#if HAVE_SYS_IOCTL_H -# include -#endif -#if HAVE_LINUX_CDROM_H -# include -#endif -#if HAVE_SYS_MOUNT_H -# include -#endif -#if HAVE_SYS_PARAM_H -# include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#endif +#ifdef __linux__ +#include +#include +#include #endif -#if HAVE_WINDOWS_H -# include -#endif -#if HAVE_WCHAR_H -# include -#endif -#if HAVE_SYS_STAT_H -# include -#endif -#if HAVE_UNISTD_H -# include -#endif -#if HAVE_IO_H -# include +#ifdef _WIN32 +// Don't break std::min! +#define NOMINMAX +#include +#endif +#include +#ifndef _WIN32 +#include +#include #endif -#if HAVE_SIGNAL_H -# include +#ifdef _WIN32 +#include #endif -#if HAVE_GETOPT_H -# include + +#include +#ifndef _WIN32 +#include #else -# include "extras/getopt.h" +#include "extras/getopt.h" #endif #ifndef PRIu64 -# ifdef _WIN32 -# define PRIu64 "I64u" -# else -# define PRIu64 "llu" -# endif +#ifdef _WIN32 +#define PRIu64 "I64u" +#else +#define PRIu64 "llu" +#endif #endif #ifndef PRIu32 -# define PRIu32 "u" +#define PRIu32 "u" #endif #ifndef PRIx32 -# define PRIx32 "x" +#define PRIx32 "x" #endif #ifndef SCNu64 -# ifdef _WIN32 -# define SCNu64 "I64u" -# else -# define SCNu64 "llu" -# endif +#ifdef _WIN32 +#define SCNu64 "I64u" +#else +#define SCNu64 "llu" +#endif #endif #ifndef SCNu32 -# define SCNu32 "u" +#define SCNu32 "u" #endif #ifndef SCNu16 -# define SCNu16 "hu" +#define SCNu16 "hu" #endif -#ifndef MIN -//#define MIN(X,Y) ((X) < (Y) ? : (X) : (Y)) -#define MIN min -#endif - - #ifndef MAXPATHLEN -# define MAXPATHLEN 255 +#define MAXPATHLEN 255 #endif #include "util.h" +#include -#define MAX_ATOMS 1024 +#define MAX_ATOMS 2048 #define MAXDATA_PAYLOAD 1256 -#define DEFAULT_PADDING_LENGTH 2048; +#define DEFAULT_PADDING_LENGTH 2048; #define MINIMUM_REQUIRED_PADDING_LENGTH 0; #define MAXIMUM_REQUIRED_PADDING_LENGTH 5000; @@ -176,8 +154,8 @@ extern AtomicInfo parsedAtoms[]; extern short atom_number; -extern char* ISObasemediafile; -extern FILE* source_file; +extern char *ISObasemediafile; +extern FILE *source_file; extern padding_preferences pad_prefs; @@ -185,7 +163,7 @@ extern uint8_t forced_suffix_type; -extern char* twenty_byte_buffer; +extern char *twenty_byte_buffer; extern DynamicUpdateStat dynUpd; extern ID3FrameDefinition KnownFrames[]; @@ -198,111 +176,143 @@ short APar_FindParentAtom(int order_in_tree, uint8_t this_atom_level); -AtomicInfo* APar_FindAtomInTrack(uint8_t &total_tracks, uint8_t &track_num, - const char* search_atom_str); - -AtomicInfo* APar_FindAtom(const char* atom_name, bool createMissing, - uint8_t atom_type, uint16_t atom_lang, bool match_full_uuids = false, - const char* reverseDNSdomain = NULL); - -int APar_MatchToKnownAtom(const char* atom_name, const char* atom_container, - bool fromFile, const char* find_atom_path); +AtomicInfo *APar_FindAtomInTrack(uint8_t &total_tracks, + uint8_t &track_num, + const char *search_atom_str); + +AtomicInfo *APar_FindAtom(const char *atom_name, + bool createMissing, + uint8_t atom_type, + uint16_t atom_lang, + bool match_full_uuids = false, + const char *reverseDNSdomain = NULL); + +int APar_MatchToKnownAtom(const char *atom_name, + const char *atom_container, + bool fromFile, + const char *find_atom_path); void APar_ScanAtoms(const char *path, bool deepscan_REQ = false); -void APar_IdentifyBrand(char* file_brand); - -AtomicInfo* APar_CreateSparseAtom(AtomicInfo* surrogate_atom, - AtomicInfo* parent_atom, short preceding_atom); - -void APar_Unified_atom_Put(AtomicInfo* target_atom, const char* unicode_data, - uint8_t text_tag_style, uint64_t ancillary_data, uint8_t anc_bit_width); +void APar_IdentifyBrand(char *file_brand); -void APar_atom_Binary_Put(AtomicInfo* target_atom, const char* binary_data, - uint32_t bytecount, uint64_t atomic_data_offset); +AtomicInfo *APar_CreateSparseAtom(AtomicInfo *surrogate_atom, + AtomicInfo *parent_atom, + short preceding_atom); + +void APar_Unified_atom_Put(AtomicInfo *target_atom, + const char *unicode_data, + uint8_t text_tag_style, + uint64_t ancillary_data, + uint8_t anc_bit_width); + +void APar_atom_Binary_Put(AtomicInfo *target_atom, + const char *binary_data, + uint32_t bytecount, + uint64_t atomic_data_offset); /* iTunes-style metadata */ -void APar_MetaData_atomArtwork_Set(const char* artworkPath, - char* env_PicOptions); +void APar_MetaData_atomArtwork_Set(const char *artworkPath, + char *env_PicOptions); -void APar_MetaData_atomGenre_Set(const char* atomPayload); -void APar_MetaData_atomLyrics_Set(const char* lyricsPath); -void APar_MetaData_atom_QuickInit(short atom_num, const uint32_t atomFlags, - uint32_t supplemental_length, uint32_t allotment = MAXDATA_PAYLOAD + 1); - -AtomicInfo* APar_MetaData_atom_Init(const char* atom_path, - const char* MD_Payload, const uint32_t atomFlags); - -AtomicInfo* APar_reverseDNS_atom_Init(const char* rDNS_atom_name, - const char* rDNS_payload, const uint32_t* atomFlags, const char* rDNS_domain); +void APar_MetaData_atomGenre_Set(const char *atomPayload); +void APar_MetaData_atomLyrics_Set(const char *lyricsPath); +void APar_MetaData_atom_QuickInit(short atom_num, + const uint32_t atomFlags, + uint32_t supplemental_length, + uint32_t allotment = MAXDATA_PAYLOAD + 1); + +AtomicInfo *APar_MetaData_atom_Init(const char *atom_path, + const char *MD_Payload, + const uint32_t atomFlags); + +AtomicInfo *APar_reverseDNS_atom_Init(const char *rDNS_atom_name, + const char *rDNS_payload, + const uint32_t *atomFlags, + const char *rDNS_domain); /* uuid user extension metadata; made to look much like iTunes-style metadata * with a 4byte NULL */ -AtomicInfo* APar_uuid_atom_Init(const char* atom_path, const char* uuidName, - const uint32_t dataType, const char* uuidValue, bool shellAtom); +AtomicInfo *APar_uuid_atom_Init(const char *atom_path, + const char *uuidName, + const uint32_t dataType, + const char *uuidValue, + bool shellAtom); + +// test whether the ipod uuid can be added for a video track +uint16_t APar_TestVideoDescription(AtomicInfo *video_desc_atom, + FILE *ISObmff_file); -//test whether the ipod uuid can be added for a video track -uint16_t APar_TestVideoDescription(AtomicInfo* video_desc_atom, - FILE* ISObmff_file); - -void APar_Generate_iPod_uuid(char* atom_path); +void APar_Generate_iPod_uuid(char *atom_path); /* 3GP-style metadata */ -uint32_t APar_3GP_Keyword_atom_Format(char* keywords_globbed, - uint8_t keyword_count, bool set_UTF16_text, char* &formed_keyword_struct); - -AtomicInfo* APar_UserData_atom_Init(const char* userdata_atom_name, - const char* atom_payload, uint8_t udta_container, uint8_t track_idx, - uint16_t userdata_lang); +uint32_t APar_3GP_Keyword_atom_Format(char *keywords_globbed, + uint8_t keyword_count, + bool set_UTF16_text, + char *&formed_keyword_struct); + +AtomicInfo *APar_UserData_atom_Init(const char *userdata_atom_name, + const char *atom_payload, + uint8_t udta_container, + uint8_t track_idx, + uint16_t userdata_lang); /* ID3v2 (2.4) style metadata, non-external form */ -AtomicInfo* APar_ID32_atom_Init(const char* frameID_str, char meta_area, - const char* lang_str, uint16_t id32_lang); - -void APar_RemoveAtom(const char* atom_path, uint8_t atom_type, - uint16_t UD_lang, const char* rDNS_domain = NULL); +AtomicInfo *APar_ID32_atom_Init(const char *frameID_str, + char meta_area, + const char *lang_str, + uint16_t id32_lang); + +void APar_RemoveAtom(const char *atom_path, + uint8_t atom_type, + uint16_t UD_lang, + const char *rDNS_domain = NULL); void APar_freefree(int purge_level); -void APar_MetadataFileDump(const char* ISObasemediafile); +void APar_MetadataFileDump(const char *ISObasemediafile); void APar_Optimize(bool mdat_test_only); void APar_DetermineAtomLengths(); -void APar_WriteFile(const char* ISObasemediafile, const char* outfile, - bool rewrite_original); - -void APar_zlib_inflate(char* in_buffer, uint32_t in_buf_len, - char* out_buffer, uint32_t out_buf_len); - -uint32_t APar_zlib_deflate(char* in_buffer, uint32_t in_buf_len, - char* out_buffer, uint32_t out_buf_len); - - -void APar_print_uuid(ap_uuid_t* uuid, bool new_line = true); -void APar_sprintf_uuid(ap_uuid_t* uuid, char* destination); -uint8_t APar_uuid_scanf(char* in_formed_uuid, const char* raw_uuid); - -void APar_endian_uuid_bin_str_conversion(char* raw_uuid); - -uint8_t APar_extract_uuid_version(ap_uuid_t* uuid, char* binary_uuid_str); -void APar_generate_uuid_from_atomname(char* atom_name, char* uuid_binary_str); -void APar_generate_random_uuid(char* uuid_binary_str); +void APar_WriteFile(const char *ISObasemediafile, + const char *outfile, + bool rewrite_original); + +void APar_zlib_inflate(char *in_buffer, + uint32_t in_buf_len, + char *out_buffer, + uint32_t out_buf_len); + +uint32_t APar_zlib_deflate(char *in_buffer, + uint32_t in_buf_len, + char *out_buffer, + uint32_t out_buf_len); + +void APar_print_uuid(ap_uuid_t *uuid, bool new_line = true); +void APar_sprintf_uuid(ap_uuid_t *uuid, char *destination); +uint8_t APar_uuid_scanf(char *in_formed_uuid, const char *raw_uuid); + +void APar_endian_uuid_bin_str_conversion(char *raw_uuid); + +uint8_t APar_extract_uuid_version(ap_uuid_t *uuid, char *binary_uuid_str); +void APar_generate_uuid_from_atomname(char *atom_name, char *uuid_binary_str); +void APar_generate_random_uuid(char *uuid_binary_str); /* Initialize structure containing state of computation. */ -extern void sha1_init_ctx (struct sha1_ctx *ctx); +extern void sha1_init_ctx(struct sha1_ctx *ctx); /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes starting at BUFFER. It is necessary that LEN is a multiple of 64!!! */ -extern void sha1_process_block (const void *buffer, size_t len, - struct sha1_ctx *ctx); +extern void +sha1_process_block(const void *buffer, size_t len, struct sha1_ctx *ctx); /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes starting at BUFFER. It is NOT required that LEN is a multiple of 64. */ -extern void sha1_process_bytes (const void *buffer, size_t len, - struct sha1_ctx *ctx); +extern void +sha1_process_bytes(const void *buffer, size_t len, struct sha1_ctx *ctx); /* Process the remaining bytes in the buffer and put result from CTX in first 20 bytes following RESBUF. The result is always in little @@ -311,8 +321,7 @@ IMPORTANT: On some systems it is required that RESBUF be correctly aligned for a 32 bits value. */ -extern void *sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf); - +extern void *sha1_finish_ctx(struct sha1_ctx *ctx, void *resbuf); /* Put result from CTX in first 20 bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields @@ -320,107 +329,131 @@ IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ -extern void *sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf); - +extern void *sha1_read_ctx(const struct sha1_ctx *ctx, void *resbuf); /* Compute SHA1 message digest for bytes read from STREAM. The resulting message digest number will be written into the 20 bytes beginning at RESBLOCK. */ -extern int sha1_stream (FILE *stream, void *resblock); +extern int sha1_stream(FILE *stream, void *resblock); /* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ -extern void *sha1_buffer (const char *buffer, size_t len, void *resblock); - -int isolat1ToUTF8(unsigned char* out, int outlen, - const unsigned char* in, int inlen); +extern void *sha1_buffer(const char *buffer, size_t len, void *resblock); -int UTF8Toisolat1(unsigned char* out, int outlen, - const unsigned char* in, int inlen); +int isolat1ToUTF8(unsigned char *out, + int outlen, + const unsigned char *in, + int inlen); + +int UTF8Toisolat1(unsigned char *out, + int outlen, + const unsigned char *in, + int inlen); + +int UTF16BEToUTF8(unsigned char *out, + int outlen, + const unsigned char *inb, + int inlenb); + +int UTF8ToUTF16BE(unsigned char *outb, + int outlen, + const unsigned char *in, + int inlen); + +int UTF16LEToUTF8(unsigned char *out, + int outlen, + const unsigned char *inb, + int inlenb); + +int UTF8ToUTF16LE(unsigned char *outb, + int outlen, + const unsigned char *in, + int inlen); -int UTF16BEToUTF8(unsigned char* out, int outlen, - const unsigned char* inb, int inlenb); - -int UTF8ToUTF16BE(unsigned char* outb, int outlen, - const unsigned char* in, int inlen); - -int UTF16LEToUTF8(unsigned char* out, int outlen, - const unsigned char* inb, int inlenb); - -int UTF8ToUTF16LE(unsigned char* outb, int outlen, - const unsigned char* in, int inlen); - -int isUTF8(const char* in_string); +int isUTF8(const char *in_string); unsigned int utf8_length(const char *in_string, unsigned int char_limit); -int strip_bogusUTF16toRawUTF8(unsigned char* out, int inlen, - wchar_t* in, int outlen); +int strip_bogusUTF16toRawUTF8(unsigned char *out, + int inlen, + wchar_t *in, + int outlen); -int test_conforming_alpha_string(char* in_string); -bool test_limited_ascii(char* in_string, unsigned int str_len); +int test_conforming_alpha_string(char *in_string); +bool test_limited_ascii(char *in_string, unsigned int str_len); -void APar_ExtractDetails(FILE* isofile, uint8_t optional_output); -void APar_ExtractBrands(char* filepath); +void APar_ExtractDetails(FILE *isofile, uint8_t optional_output); +void APar_ExtractBrands(char *filepath); void printBOM(); -void APar_fprintf_UTF8_data(const char* utf8_encoded_data); -void APar_unicode_win32Printout(wchar_t* unicode_out, char* utf8_out); - -void APar_Extract_uuid_binary_file(AtomicInfo* uuid_atom, - const char* originating_file, char* output_path); - -void APar_Print_APuuid_atoms(const char *path, char* output_path, - uint8_t target_information); +void APar_fprintf_UTF8_data(const char *utf8_encoded_data); +void APar_unicode_win32Printout(wchar_t *unicode_out, char *utf8_out); -void APar_Print_iTunesData(const char *path, char* output_path, - uint8_t supplemental_info, uint8_t target_information, - AtomicInfo* ilstAtom = NULL); +void APar_Extract_uuid_binary_file(AtomicInfo *uuid_atom, + const char *originating_file, + char *output_path); + +void APar_Print_APuuid_atoms(const char *path, + char *output_path, + uint8_t target_information); + +void APar_Print_iTunesData(const char *path, + char *output_path, + uint8_t supplemental_info, + uint8_t target_information, + AtomicInfo *ilstAtom = NULL); void APar_PrintUserDataAssests(bool quantum_listing = false); -void APar_Extract_ID3v2_file(AtomicInfo* id32_atom, const char* frame_str, - const char* originfile, const char* destination_folder, AdjunctArgs* id3args); +void APar_Extract_ID3v2_file(AtomicInfo *id32_atom, + const char *frame_str, + const char *originfile, + const char *destination_folder, + AdjunctArgs *id3args); -void APar_Print_ID3v2_tags(AtomicInfo* id32_atom); +void APar_Print_ID3v2_tags(AtomicInfo *id32_atom); void APar_Print_ISO_UserData_per_track(); -void APar_Mark_UserData_area(uint8_t track_num, short userdata_atom, - bool quantum_listing); +void APar_Mark_UserData_area(uint8_t track_num, + short userdata_atom, + bool quantum_listing); -//trees +// trees void APar_PrintAtomicTree(); void APar_SimpleAtomPrintout(); -uint32_t APar_4CC_CreatorCode(const char* filepath, uint32_t new_type_code); +uint32_t APar_4CC_CreatorCode(const char *filepath, uint32_t new_type_code); void APar_SupplySelectiveTypeCreatorCodes(const char *inputPath, - const char *outputPath, uint8_t forced_type_code); + const char *outputPath, + uint8_t forced_type_code); -bool ResizeGivenImage(const char* filePath, PicPrefs myPicPrefs, - char* resized_path); +bool ResizeGivenImage(const char *filePath, + PicPrefs myPicPrefs, + char *resized_path, + size_t resized_path_len); -char* GenreIntToString(int genre); -uint8_t StringGenreToInt(const char* genre_string); +char *GenreIntToString(int genre); +uint8_t StringGenreToInt(const char *genre_string); void ListGenresValues(); -stiks* MatchStikString(const char* stik_string); -stiks* MatchStikNumber(uint8_t in_stik_num); +stiks *MatchStikString(const char *stik_string); +stiks *MatchStikNumber(uint8_t in_stik_num); void ListStikValues(); -sfIDs* MatchStoreFrontNumber(uint32_t storefrontnum); +sfIDs *MatchStoreFrontNumber(uint32_t storefrontnum); -bool MatchLanguageCode(const char* in_code); +bool MatchLanguageCode(const char *in_code); void ListLanguageCodes(); void ListMediaRatings(); void ListTVGenreIDValues(); void ListMovieGenreIDValues(); -const char* Expand_cli_mediastring(const char* cli_rating); +const char *Expand_cli_mediastring(const char *cli_rating); -char* ID3GenreIntToString(int genre); -uint8_t ID3StringGenreToInt(const char* genre_string); +char *ID3GenreIntToString(int genre); +uint8_t ID3StringGenreToInt(const char *genre_string); #endif /* ATOMIC_PARSLEY_H */ diff -Nru atomicparsley-0.9.6/src/CDtoc.cpp atomicparsley-20210715.151551.e7ad03a/src/CDtoc.cpp --- atomicparsley-0.9.6/src/CDtoc.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/CDtoc.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - CDtoc.cpp - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,72 +10,74 @@ 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 + 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 2006-2007 puck_lock + Copyright (C)2006-2007 puck_lock with contributions from others; see the CREDITS file */ //==================================================================// -//gathering of a CD's Table of Contents is going to be hardware specific -//currently only Mac OS X is implemented - using IOKit framework. -//another avenue (applicable to other *nix platforms): ioctl +// gathering of a CD's Table of Contents is going to be hardware specific +// currently only Mac OS X is implemented - using IOKit framework. +// another avenue (applicable to other *nix platforms): ioctl #include "AtomicParsley.h" #include "CDtoc.h" -#if defined (DARWIN_PLATFORM) +#if defined(__APPLE__) #include +#include #include -#include #include -#include +#include const uint8_t MACOSX_LEADOUT_TRACK = 0xA2; #endif - const uint8_t CDOBJECT_DATACD = 0; const uint8_t CDOBJECT_AUDIOCD = 1; struct CD_TDesc { - uint8_t session; - uint8_t controladdress; - uint8_t unused1; //refered to as 'tno' which equates to "track number" - but... its 'point' that actually is the tracknumber in mode1 TOC. set to zero for all mode1 TOC - uint8_t tracknumber; //refered to as 'point', but this is actually the tracknumber in mode1 audio toc entries. - uint8_t rel_minutes; - uint8_t rel_seconds; - uint8_t rel_frames; - uint8_t zero_space; - uint8_t abs_minutes; - uint8_t abs_seconds; - uint8_t abs_frames; - void* next_description; + uint8_t session; + uint8_t controladdress; + uint8_t unused1; // refered to as 'tno' which equates to "track number" - + // but... its 'point' that actually is the tracknumber in + // mode1 TOC. set to zero for all mode1 TOC + uint8_t tracknumber; // refered to as 'point', but this is actually the + // tracknumber in mode1 audio toc entries. + uint8_t rel_minutes; + uint8_t rel_seconds; + uint8_t rel_frames; + uint8_t zero_space; + uint8_t abs_minutes; + uint8_t abs_seconds; + uint8_t abs_frames; + void *next_description; }; typedef struct CD_TDesc CD_TDesc; struct CD_TOC_ { - uint16_t toc_length; - uint8_t first_session; - uint8_t last_session; - CD_TDesc* track_description; //entry to the first track in the linked list + uint16_t toc_length; + uint8_t first_session; + uint8_t last_session; + CD_TDesc *track_description; // entry to the first track in the linked list }; typedef struct CD_TOC_ CD_TOC_; -CD_TOC_* cdTOC = NULL; +CD_TOC_ *cdTOC = NULL; -#if defined (DARWIN_PLATFORM) - uint8_t LEADOUT_TRACK_NUMBER = MACOSX_LEADOUT_TRACK; -#elif defined (HAVE_LINUX_CDROM_H) - uint8_t LEADOUT_TRACK_NUMBER = CDROM_LEADOUT; -#elif defined (_WIN32) - uint8_t LEADOUT_TRACK_NUMBER = 0xAA; //NOTE: for WinXP IOCTL_CDROM_READ_TOC_EX code, its 0xA2 +#if defined(__APPLE__) +uint8_t LEADOUT_TRACK_NUMBER = MACOSX_LEADOUT_TRACK; +#elif defined(__linux__) +uint8_t LEADOUT_TRACK_NUMBER = CDROM_LEADOUT; +#elif defined(_WIN32) +uint8_t LEADOUT_TRACK_NUMBER = + 0xAA; // NOTE: for WinXP IOCTL_CDROM_READ_TOC_EX code, its 0xA2 #endif - /* MCDI describes the CD TOC - actually talks about "a binary dump of the TOC". So, a TOC is made up of: a 4 byte TOC header (2 bytes length of the entire TOC, @@ -95,16 +97,17 @@ uint8_t cd_toc_session; uint8_t cd_toc_controladdress; //bitpacked uint4_t of control & address uint8_t cd_toc_TNO = 0; //hardcoded to 0 for mode1 audio tracks in the TOC - uint8_t cd_toc_tracknumber; //this is the 1-99 tracknumber (listed in mmc-2 as POINT) - uint32_t cd_frame_address; //converted from the 3byte mm:ss:frame absolute duration + uint8_t cd_toc_tracknumber; //this is the 1-99 tracknumber (listed in mmc-2 +as POINT) uint32_t cd_frame_address; //converted from the 3byte mm:ss:frame +absolute duration }; struct toc_header { - uint16_t toc_length; - uin8_t first_track; - uint8_t last_track; + uint16_t toc_length; + uin8_t first_track; + uint8_t last_track; }; struct mcdi_frame { - struct toc_header; + struct toc_header; struct mcdi_track_entry[total_audio_tracks]; struct mcdi_track_entry lead_out; }; @@ -130,380 +133,442 @@ /////////////////////////////////////////////////////////////////////////// uint8_t DataControlField(uint8_t controlfield) { -#if defined (__ppc__) || defined (__ppc64__) - if (controlfield & 0x04) { // data uninterrupted or increment OR reserved; this field is already bitpacked as controlfield - return 1; - } +#if defined(__ppc__) || defined(__ppc64__) + if (controlfield & 0x04) { // data uninterrupted or increment OR reserved; + // this field is already bitpacked as controlfield + return 1; + } #else - if (controlfield & 0x40) { // data uninterrupted or increment OR reserved; bitpacked already - return 1; - } + if (controlfield & + 0x40) { // data uninterrupted or increment OR reserved; bitpacked already + return 1; + } #endif - return 0; + return 0; } -uint8_t DetermineCDType(CD_TOC_* cdTOCdata) { - CD_TDesc* track_TOC_desc = cdTOCdata->track_description; - while (track_TOC_desc != NULL) { - if (track_TOC_desc->tracknumber >= 1 && track_TOC_desc->tracknumber <= 99 && !DataControlField(track_TOC_desc->controladdress)) { - return CDOBJECT_AUDIOCD; - } - track_TOC_desc = (CD_TDesc*)track_TOC_desc->next_description; - } - return CDOBJECT_DATACD; -} - -CD_TDesc* LeadOutTrack(CD_TOC_* cdTOCdata) { - CD_TDesc* track_TOC_desc = cdTOCdata->track_description; - while (track_TOC_desc != NULL) { - if (track_TOC_desc->tracknumber == LEADOUT_TRACK_NUMBER) { - return track_TOC_desc; - } - track_TOC_desc = (CD_TDesc*)track_TOC_desc->next_description; - } - return NULL; -} - -uint8_t FillSingleMCDIentry(CD_TDesc* atrack, char* mcdi_data_entry) { - mcdi_data_entry[0] = atrack->session; - mcdi_data_entry[1] = atrack->controladdress; - mcdi_data_entry[2] = 0; - mcdi_data_entry[3] = atrack->tracknumber; - //LBA=(M*60+S)*75+F - 150 (table 374) - uint32_t frameduration = ((((atrack->abs_minutes*60) + atrack->abs_seconds) * 75) + atrack->abs_frames)-150; - UInt32_TO_String4(frameduration, mcdi_data_entry + 4); - return 8; -} - -uint16_t FormMCDIdata(char* mcdi_data) { - uint16_t mcdi_len = 0; - uint8_t first_track = 0; - uint8_t last_track = 0; - - CD_TDesc* track_TOC_desc = cdTOC->track_description; - - if (cdTOC->track_description != NULL) { - mcdi_len +=4; - - while (track_TOC_desc != NULL) { - if (track_TOC_desc->tracknumber >= 1 && track_TOC_desc->tracknumber <= 99 && !DataControlField(track_TOC_desc->controladdress)) { - mcdi_len += FillSingleMCDIentry(track_TOC_desc, mcdi_data+mcdi_len); - if (first_track == 0) { - first_track = track_TOC_desc->tracknumber; - } - last_track = track_TOC_desc->tracknumber; - } - track_TOC_desc = (CD_TDesc*)track_TOC_desc->next_description; - } - if (mcdi_len > 0) { - CD_TDesc* leadout = LeadOutTrack(cdTOC); - if (leadout != NULL) { - mcdi_len += FillSingleMCDIentry(leadout, mcdi_data+mcdi_len); - } - } - //backtrack & fill in the header - UInt16_TO_String2(mcdi_len, mcdi_data); - mcdi_data[2] = first_track; - mcdi_data[3] = last_track; - } - return mcdi_len; +uint8_t DetermineCDType(CD_TOC_ *cdTOCdata) { + CD_TDesc *track_TOC_desc = cdTOCdata->track_description; + while (track_TOC_desc != NULL) { + if (track_TOC_desc->tracknumber >= 1 && track_TOC_desc->tracknumber <= 99 && + !DataControlField(track_TOC_desc->controladdress)) { + return CDOBJECT_AUDIOCD; + } + track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description; + } + return CDOBJECT_DATACD; +} + +CD_TDesc *LeadOutTrack(CD_TOC_ *cdTOCdata) { + CD_TDesc *track_TOC_desc = cdTOCdata->track_description; + while (track_TOC_desc != NULL) { + if (track_TOC_desc->tracknumber == LEADOUT_TRACK_NUMBER) { + return track_TOC_desc; + } + track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description; + } + return NULL; +} + +uint8_t FillSingleMCDIentry(CD_TDesc *atrack, char *mcdi_data_entry) { + mcdi_data_entry[0] = atrack->session; + mcdi_data_entry[1] = atrack->controladdress; + mcdi_data_entry[2] = 0; + mcdi_data_entry[3] = atrack->tracknumber; + // LBA=(M*60+S)*75+F - 150 (table 374) + uint32_t frameduration = + ((((atrack->abs_minutes * 60) + atrack->abs_seconds) * 75) + + atrack->abs_frames) - + 150; + UInt32_TO_String4(frameduration, mcdi_data_entry + 4); + return 8; +} + +uint16_t FormMCDIdata(char *mcdi_data) { + uint16_t mcdi_len = 0; + uint8_t first_track = 0; + uint8_t last_track = 0; + + CD_TDesc *track_TOC_desc = cdTOC->track_description; + + if (cdTOC->track_description != NULL) { + mcdi_len += 4; + + while (track_TOC_desc != NULL) { + if (track_TOC_desc->tracknumber >= 1 && + track_TOC_desc->tracknumber <= 99 && + !DataControlField(track_TOC_desc->controladdress)) { + mcdi_len += FillSingleMCDIentry(track_TOC_desc, mcdi_data + mcdi_len); + if (first_track == 0) { + first_track = track_TOC_desc->tracknumber; + } + last_track = track_TOC_desc->tracknumber; + } + track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description; + } + if (mcdi_len > 0) { + CD_TDesc *leadout = LeadOutTrack(cdTOC); + if (leadout != NULL) { + mcdi_len += FillSingleMCDIentry(leadout, mcdi_data + mcdi_len); + } + } + // backtrack & fill in the header + UInt16_TO_String2(mcdi_len, mcdi_data); + mcdi_data[2] = first_track; + mcdi_data[3] = last_track; + } + return mcdi_len; } ///////////////////////////////////////////////////////////////////////////// // Platform Specifics // ///////////////////////////////////////////////////////////////////////////// -#if defined (HAVE_LINUX_CDROM_H) +#if defined(__linux__) void Linux_ReadCDTOC(int cd_fd) { - cdrom_tochdr toc_header; - cdrom_tocentry toc_entry; - CD_TDesc* a_TOC_desc = NULL; - CD_TDesc* prev_desc = NULL; - - if (ioctl(cd_fd, CDROMREADTOCHDR, &toc_header) == -1) { - fprintf(stderr, "AtomicParsley error: there was an error reading the CD Table of Contents header.\n"); - return; - } - cdTOC = (CD_TOC_*)calloc(1, sizeof(CD_TOC_)); - cdTOC->track_description = NULL; - - for (uint8_t i = toc_header.cdth_trk0; i <= toc_header.cdth_trk1+1; i++) { - memset(&toc_entry, 0, sizeof(toc_entry)); - if (i == toc_header.cdth_trk1+1) { - toc_entry.cdte_track = CDROM_LEADOUT; - } else { - toc_entry.cdte_track = i; - } - toc_entry.cdte_format = CDROM_MSF; //although it could just be easier to use CDROM_LBA - - if (ioctl(cd_fd, CDROMREADTOCENTRY, &toc_entry) == -1) { - fprintf(stderr, - "AtomicParsley error: there was an error reading a " - "CD Table of Contents entry (linux cdrom).\n"); - return; - } - - if (cdTOC->track_description == NULL) { - cdTOC->track_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc))); - a_TOC_desc = cdTOC->track_description; - prev_desc = a_TOC_desc; - } else { - prev_desc->next_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc))); - a_TOC_desc = (CD_TDesc*)prev_desc->next_description; - prev_desc = a_TOC_desc; - } - - a_TOC_desc->session = 1; //and for vanilla audio CDs it is session 1, but for multi-session... -#if defined (__ppc__) || defined (__ppc64__) - a_TOC_desc->controladdress = (toc_entry.cdte_ctrl << 4) | toc_entry.cdte_adr; + cdrom_tochdr toc_header; + cdrom_tocentry toc_entry; + CD_TDesc *a_TOC_desc = NULL; + CD_TDesc *prev_desc = NULL; + + if (ioctl(cd_fd, CDROMREADTOCHDR, &toc_header) == -1) { + fprintf(stderr, + "AtomicParsley error: there was an error reading the CD " + "Table of Contents header.\n"); + return; + } + cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_)); + cdTOC->track_description = NULL; + + for (uint8_t i = toc_header.cdth_trk0; i <= toc_header.cdth_trk1 + 1; i++) { + memset(&toc_entry, 0, sizeof(toc_entry)); + if (i == toc_header.cdth_trk1 + 1) { + toc_entry.cdte_track = CDROM_LEADOUT; + } else { + toc_entry.cdte_track = i; + } + toc_entry.cdte_format = + CDROM_MSF; // although it could just be easier to use CDROM_LBA + + if (ioctl(cd_fd, CDROMREADTOCENTRY, &toc_entry) == -1) { + fprintf(stderr, + "AtomicParsley error: there was an error reading a " + "CD Table of Contents entry (linux cdrom).\n"); + return; + } + + if (cdTOC->track_description == NULL) { + cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc))); + a_TOC_desc = cdTOC->track_description; + prev_desc = a_TOC_desc; + } else { + prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc))); + a_TOC_desc = (CD_TDesc *)prev_desc->next_description; + prev_desc = a_TOC_desc; + } + + a_TOC_desc->session = 1; // and for vanilla audio CDs it is session 1, but + // for multi-session... +#if defined(__ppc__) || defined(__ppc64__) + a_TOC_desc->controladdress = + (toc_entry.cdte_ctrl << 4) | toc_entry.cdte_adr; #else - a_TOC_desc->controladdress = (toc_entry.cdte_adr << 4) | toc_entry.cdte_ctrl; + a_TOC_desc->controladdress = + (toc_entry.cdte_adr << 4) | toc_entry.cdte_ctrl; #endif - a_TOC_desc->unused1 = 0; - a_TOC_desc->tracknumber = toc_entry.cdte_track; - a_TOC_desc->rel_minutes = 0; //is there anyway to even find this out on linux without playing the track? //cdmsf_min0 - a_TOC_desc->rel_seconds = 0; - a_TOC_desc->rel_frames = 0; - a_TOC_desc->zero_space = 0; - a_TOC_desc->abs_minutes = toc_entry.cdte_addr.msf.minute; - a_TOC_desc->abs_seconds = toc_entry.cdte_addr.msf.second; - a_TOC_desc->abs_frames = toc_entry.cdte_addr.msf.frame; - } - return; -} - -uint16_t Linux_ioctlProbeTargetDrive(const char* id3args_drive, char* mcdi_data) { - uint16_t mcdi_data_len = 0; - int cd_fd = 0; - - cd_fd = open(id3args_drive, O_RDONLY | O_NONBLOCK); - - if (cd_fd != -1) { - int cd_mode = ioctl(cd_fd, CDROM_DISC_STATUS); - if (cd_mode != CDS_AUDIO || cd_mode != CDS_MIXED) { - Linux_ReadCDTOC(cd_fd); - mcdi_data_len = FormMCDIdata(mcdi_data); - } else { - //scan for available devices - } - } else { - //scan for available devices - } - - return mcdi_data_len; + a_TOC_desc->unused1 = 0; + a_TOC_desc->tracknumber = toc_entry.cdte_track; + a_TOC_desc->rel_minutes = + 0; // is there anyway to even find this out on + // linux without playing the track? //cdmsf_min0 + a_TOC_desc->rel_seconds = 0; + a_TOC_desc->rel_frames = 0; + a_TOC_desc->zero_space = 0; + a_TOC_desc->abs_minutes = toc_entry.cdte_addr.msf.minute; + a_TOC_desc->abs_seconds = toc_entry.cdte_addr.msf.second; + a_TOC_desc->abs_frames = toc_entry.cdte_addr.msf.frame; + } + return; +} + +uint16_t Linux_ioctlProbeTargetDrive(const char *id3args_drive, + char *mcdi_data) { + uint16_t mcdi_data_len = 0; + int cd_fd = 0; + + cd_fd = open(id3args_drive, O_RDONLY | O_NONBLOCK); + + if (cd_fd != -1) { + int cd_mode = ioctl(cd_fd, CDROM_DISC_STATUS); + if (cd_mode != CDS_AUDIO || cd_mode != CDS_MIXED) { + Linux_ReadCDTOC(cd_fd); + mcdi_data_len = FormMCDIdata(mcdi_data); + } else { + // scan for available devices + } + } else { + // scan for available devices + } + + return mcdi_data_len; } #endif -#if defined (DARWIN_PLATFORM) -uint16_t Extract_cdTOCrawdata(CFDataRef cdTOCdata, char* cdTOCrawdata) { - CFRange cdrange; - CFIndex cdTOClen = CFDataGetLength (cdTOCdata); - cdTOCrawdata = (char*)calloc(1, sizeof(char)*cdTOClen+1); - cdrange = CFRangeMake(0, cdTOClen+1); - CFDataGetBytes(cdTOCdata, cdrange, (unsigned char*)cdTOCrawdata); - - cdTOC = (CD_TOC_*)calloc(1, sizeof(CD_TOC_)); - cdTOC->toc_length = UInt16FromBigEndian(cdTOCrawdata); - cdTOC->first_session = cdTOCrawdata[2]; - cdTOC->first_session = cdTOCrawdata[3]; - cdTOC->track_description = NULL; - - CD_TDesc* a_TOC_desc = NULL; - CD_TDesc* prev_desc = NULL; - - uint16_t toc_offset = 0; - for (toc_offset = 4; toc_offset <= cdTOClen; toc_offset +=11) { - if (cdTOC->track_description == NULL) { - cdTOC->track_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc))); - a_TOC_desc = cdTOC->track_description; - prev_desc = a_TOC_desc; - } else { - prev_desc->next_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc))); - a_TOC_desc = (CD_TDesc*)prev_desc->next_description; - prev_desc = a_TOC_desc; - } - a_TOC_desc->session = cdTOCrawdata[toc_offset]; - a_TOC_desc->controladdress = cdTOCrawdata[toc_offset+1]; - a_TOC_desc->unused1 = cdTOCrawdata[toc_offset+2]; - a_TOC_desc->tracknumber = cdTOCrawdata[toc_offset+3]; - a_TOC_desc->rel_minutes = cdTOCrawdata[toc_offset+4]; - a_TOC_desc->rel_seconds = cdTOCrawdata[toc_offset+5]; - a_TOC_desc->rel_frames = cdTOCrawdata[toc_offset+6]; - a_TOC_desc->zero_space = 0; - a_TOC_desc->abs_minutes = cdTOCrawdata[toc_offset+8]; - a_TOC_desc->abs_seconds = cdTOCrawdata[toc_offset+9]; - a_TOC_desc->abs_frames = cdTOCrawdata[toc_offset+10]; - } - - return (uint16_t)cdTOClen; +#if defined(__APPLE__) +uint16_t Extract_cdTOCrawdata(CFDataRef cdTOCdata, char *cdTOCrawdata) { + CFRange cdrange; + CFIndex cdTOClen = CFDataGetLength(cdTOCdata); + cdTOCrawdata = (char *)calloc(1, sizeof(char) * cdTOClen + 1); + cdrange = CFRangeMake(0, cdTOClen + 1); + CFDataGetBytes(cdTOCdata, cdrange, (unsigned char *)cdTOCrawdata); + + cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_)); + cdTOC->toc_length = UInt16FromBigEndian(cdTOCrawdata); + cdTOC->first_session = cdTOCrawdata[2]; + cdTOC->first_session = cdTOCrawdata[3]; + cdTOC->track_description = NULL; + + CD_TDesc *a_TOC_desc = NULL; + CD_TDesc *prev_desc = NULL; + + uint16_t toc_offset = 0; + for (toc_offset = 4; toc_offset <= cdTOClen; toc_offset += 11) { + if (cdTOC->track_description == NULL) { + cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc))); + a_TOC_desc = cdTOC->track_description; + prev_desc = a_TOC_desc; + } else { + prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc))); + a_TOC_desc = (CD_TDesc *)prev_desc->next_description; + prev_desc = a_TOC_desc; + } + a_TOC_desc->session = cdTOCrawdata[toc_offset]; + a_TOC_desc->controladdress = cdTOCrawdata[toc_offset + 1]; + a_TOC_desc->unused1 = cdTOCrawdata[toc_offset + 2]; + a_TOC_desc->tracknumber = cdTOCrawdata[toc_offset + 3]; + a_TOC_desc->rel_minutes = cdTOCrawdata[toc_offset + 4]; + a_TOC_desc->rel_seconds = cdTOCrawdata[toc_offset + 5]; + a_TOC_desc->rel_frames = cdTOCrawdata[toc_offset + 6]; + a_TOC_desc->zero_space = 0; + a_TOC_desc->abs_minutes = cdTOCrawdata[toc_offset + 8]; + a_TOC_desc->abs_seconds = cdTOCrawdata[toc_offset + 9]; + a_TOC_desc->abs_frames = cdTOCrawdata[toc_offset + 10]; + } + + return (uint16_t)cdTOClen; } void OSX_ReadCDTOC(io_object_t cdobject) { - CFMutableDictionaryRef cd_props = 0; - CFDataRef cdTOCdata = NULL; - char* cdTOCrawdata = NULL; - - if (IORegistryEntryCreateCFProperties(cdobject, &cd_props, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) return; - - cdTOCdata = (CFDataRef)CFDictionaryGetValue(cd_props, CFSTR (kIOCDMediaTOCKey)); - if (cdTOCdata != NULL) { - Extract_cdTOCrawdata(cdTOCdata, cdTOCrawdata); - } - CFRelease(cd_props); - cd_props = NULL; - return; + CFMutableDictionaryRef cd_props = 0; + CFDataRef cdTOCdata = NULL; + char *cdTOCrawdata = NULL; + + if (IORegistryEntryCreateCFProperties( + cdobject, &cd_props, kCFAllocatorDefault, kNilOptions) != + kIOReturnSuccess) + return; + + cdTOCdata = + (CFDataRef)CFDictionaryGetValue(cd_props, CFSTR(kIOCDMediaTOCKey)); + if (cdTOCdata != NULL) { + Extract_cdTOCrawdata(cdTOCdata, cdTOCrawdata); + } + CFRelease(cd_props); + cd_props = NULL; + return; } void OSX_ScanForCDDrive() { - io_iterator_t drive_iter = MACH_PORT_NULL; - io_object_t driveobject = MACH_PORT_NULL; - CFTypeRef drive_path = NULL; - char drive_path_str[20]; - if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(kIOCDMediaClass), &drive_iter) != kIOReturnSuccess) { //create the iterator - fprintf(stdout, "No device capable of reading cd media present\n"); - } - - driveobject = IOIteratorNext(drive_iter); - while ( driveobject != MACH_PORT_NULL ) { - drive_path = IORegistryEntryCreateCFProperty(driveobject, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); - if (drive_path != NULL) { - CFStringGetCString((CFStringRef)drive_path, (char*)&drive_path_str, 20, kCFStringEncodingASCII); - fprintf(stdout, "Device '%s' contains cd media\n", drive_path_str); - OSX_ReadCDTOC(driveobject); - if (cdTOC != NULL) { - uint8_t cdType = DetermineCDType(cdTOC); - if (cdType == CDOBJECT_AUDIOCD) { - fprintf(stdout, "Good news, device '%s' is an Audio CD and can be used for 'MCDI' setting\n", drive_path_str); - } else { - fprintf(stdout, "Tragically, it was a data CD.\n"); - } - free(cdTOC); //the other malloced members should be freed also - } - } - IOObjectRelease(driveobject); - driveobject = IOIteratorNext(drive_iter); - } - - if (drive_path_str[0] == (uint8_t)0x00) { - fprintf(stdout, "No CD media was found in any device\n"); - } - IOObjectRelease(drive_iter); - drive_iter = MACH_PORT_NULL; - exit(0); -} - -uint16_t OSX_ProbeTargetDrive(const char* id3args_drive, char* mcdi_data) { - uint16_t mcdi_data_len = 0; - io_object_t cdobject = MACH_PORT_NULL; - - if (strncmp(id3args_drive, "disk", 4) != 0) { - OSX_ScanForCDDrive(); - exit(0); - } - cdobject = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching (kIOMasterPortDefault, 0, id3args_drive) ); - - if (cdobject == MACH_PORT_NULL) { - fprintf(stdout, "No device found at %s; searching for possible drives...\n", id3args_drive); - OSX_ScanForCDDrive(); - - } else if (IOObjectConformsTo(cdobject, kIOCDMediaClass) == false) { - fprintf (stdout, "No cd present in drive at %s\n", id3args_drive ); - IOObjectRelease(cdobject); - cdobject = MACH_PORT_NULL; - OSX_ScanForCDDrive(); - } else { - //we now have a cd object - OSX_ReadCDTOC(cdobject); - if (cdTOC != NULL) { - uint8_t cdType = DetermineCDType(cdTOC); - if (cdType == CDOBJECT_AUDIOCD) { - mcdi_data_len = FormMCDIdata(mcdi_data); - } - } - } - - IOObjectRelease(cdobject); - cdobject = MACH_PORT_NULL; - return mcdi_data_len; + io_iterator_t drive_iter = MACH_PORT_NULL; + io_object_t driveobject = MACH_PORT_NULL; + CFTypeRef drive_path = NULL; + char drive_path_str[20]; + if (IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceMatching(kIOCDMediaClass), + &drive_iter) != + kIOReturnSuccess) { // create the iterator + fprintf(stdout, "No device capable of reading cd media present\n"); + } + + driveobject = IOIteratorNext(drive_iter); + while (driveobject != MACH_PORT_NULL) { + drive_path = IORegistryEntryCreateCFProperty( + driveobject, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); + if (drive_path != NULL) { + CFStringGetCString((CFStringRef)drive_path, + (char *)&drive_path_str, + 20, + kCFStringEncodingASCII); + fprintf(stdout, "Device '%s' contains cd media\n", drive_path_str); + OSX_ReadCDTOC(driveobject); + if (cdTOC != NULL) { + uint8_t cdType = DetermineCDType(cdTOC); + if (cdType == CDOBJECT_AUDIOCD) { + fprintf(stdout, + "Good news, device '%s' is an Audio CD and can be used for " + "'MCDI' setting\n", + drive_path_str); + } else { + fprintf(stdout, "Tragically, it was a data CD.\n"); + } + free(cdTOC); // the other malloced members should be freed also + } + } + IOObjectRelease(driveobject); + driveobject = IOIteratorNext(drive_iter); + } + + if (drive_path_str[0] == (uint8_t)0x00) { + fprintf(stdout, "No CD media was found in any device\n"); + } + IOObjectRelease(drive_iter); + drive_iter = MACH_PORT_NULL; + exit(0); +} + +uint16_t OSX_ProbeTargetDrive(const char *id3args_drive, char *mcdi_data) { + uint16_t mcdi_data_len = 0; + io_object_t cdobject = MACH_PORT_NULL; + + if (strncmp(id3args_drive, "disk", 4) != 0) { + OSX_ScanForCDDrive(); + exit(0); + } + cdobject = IOServiceGetMatchingService( + kIOMasterPortDefault, + IOBSDNameMatching(kIOMasterPortDefault, 0, id3args_drive)); + + if (cdobject == MACH_PORT_NULL) { + fprintf(stdout, + "No device found at %s; searching for possible drives...\n", + id3args_drive); + OSX_ScanForCDDrive(); + + } else if (IOObjectConformsTo(cdobject, kIOCDMediaClass) == false) { + fprintf(stdout, "No cd present in drive at %s\n", id3args_drive); + IOObjectRelease(cdobject); + cdobject = MACH_PORT_NULL; + OSX_ScanForCDDrive(); + } else { + // we now have a cd object + OSX_ReadCDTOC(cdobject); + if (cdTOC != NULL) { + uint8_t cdType = DetermineCDType(cdTOC); + if (cdType == CDOBJECT_AUDIOCD) { + mcdi_data_len = FormMCDIdata(mcdi_data); + } + } + } + + IOObjectRelease(cdobject); + cdobject = MACH_PORT_NULL; + return mcdi_data_len; } #endif -#if defined (_WIN32) +#if defined(_WIN32) void Windows_ioctlReadCDTOC(HANDLE cdrom_device) { - DWORD bytes_returned; - CDROM_TOC win_cdrom_toc; - - //WARNING: "This IOCTL is obsolete beginning with the Microsoft Windows Vista. Do not use this IOCTL to develop drivers in Microsoft Windows Vista." - if (DeviceIoControl(cdrom_device, IOCTL_CDROM_READ_TOC, NULL, 0, &win_cdrom_toc, sizeof(CDROM_TOC), &bytes_returned, NULL) == 0) { - fprintf(stderr, "AtomicParsley error: there was an error reading the CD Table of Contents header (win32).\n"); - return; - } - - - cdTOC = (CD_TOC_*)calloc(1, sizeof(CD_TOC_)); - cdTOC->toc_length = ((win_cdrom_toc.Length[0] & 0xFF) << 8) | (win_cdrom_toc.Length[1] & 0xFF); - //cdTOC->first_session = 0; //seems windows doesn't store session info with IOCTL_CDROM_READ_TOC, all tracks from all sessions are available - //cdTOC->last_session = 0; //not used anyway; IOCTL_CDROM_READ_TOC_EX could be used to get session information, but its only available on XP - //...............and interestingly enough in XP, IOCTL_CDROM_TOC_EX returns the leadout track as 0xA2, which makes a Win2k MCDI & a WinXP MCDI different (by 1 byte) - cdTOC->track_description = NULL; - - CD_TDesc* a_TOC_desc = NULL; - CD_TDesc* prev_desc = NULL; - - for (uint8_t i = win_cdrom_toc.FirstTrack; i <= win_cdrom_toc.LastTrack+1; i++) { - TRACK_DATA* thisTrackData = &(win_cdrom_toc.TrackData[i-1]); - - if (cdTOC->track_description == NULL) { - cdTOC->track_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc))); - a_TOC_desc = cdTOC->track_description; - prev_desc = a_TOC_desc; - } else { - prev_desc->next_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc))); - a_TOC_desc = (CD_TDesc*)prev_desc->next_description; - prev_desc = a_TOC_desc; - } - - a_TOC_desc->session = 1; //and for vanilla audio CDs it is session 1, but for multi-session... -#if defined (__ppc__) || defined (__ppc64__) - a_TOC_desc->controladdress = (thisTrackData->Control << 4) | thisTrackData->Adr; + DWORD bytes_returned; + CDROM_TOC win_cdrom_toc; + + // WARNING: "This IOCTL is obsolete beginning with the Microsoft Windows + // Vista. Do not use this IOCTL to develop drivers in Microsoft Windows + // Vista." + if (DeviceIoControl(cdrom_device, + IOCTL_CDROM_READ_TOC, + NULL, + 0, + &win_cdrom_toc, + sizeof(CDROM_TOC), + &bytes_returned, + NULL) == 0) { + fprintf(stderr, + "AtomicParsley error: there was an error reading the CD " + "Table of Contents header (win32).\n"); + return; + } + + cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_)); + cdTOC->toc_length = ((win_cdrom_toc.Length[0] & 0xFF) << 8) | + (win_cdrom_toc.Length[1] & 0xFF); + // cdTOC->first_session = 0; //seems windows doesn't store session info with + // IOCTL_CDROM_READ_TOC, all tracks from all sessions are available + // cdTOC->last_session = 0; //not used anyway; IOCTL_CDROM_READ_TOC_EX could + // be used to get session information, but its only available on XP + //...............and interestingly enough in XP, IOCTL_CDROM_TOC_EX returns + // the leadout track as 0xA2, which makes a Win2k MCDI & a WinXP MCDI + // different (by 1 byte) + cdTOC->track_description = NULL; + + CD_TDesc *a_TOC_desc = NULL; + CD_TDesc *prev_desc = NULL; + + for (uint8_t i = win_cdrom_toc.FirstTrack; i <= win_cdrom_toc.LastTrack + 1; + i++) { + TRACK_DATA *thisTrackData = &(win_cdrom_toc.TrackData[i - 1]); + + if (cdTOC->track_description == NULL) { + cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc))); + a_TOC_desc = cdTOC->track_description; + prev_desc = a_TOC_desc; + } else { + prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc))); + a_TOC_desc = (CD_TDesc *)prev_desc->next_description; + prev_desc = a_TOC_desc; + } + + a_TOC_desc->session = 1; // and for vanilla audio CDs it is session 1, but + // for multi-session... +#if defined(__ppc__) || defined(__ppc64__) + a_TOC_desc->controladdress = + (thisTrackData->Control << 4) | thisTrackData->Adr; #else - a_TOC_desc->controladdress = (thisTrackData->Adr << 4) | thisTrackData->Control; + a_TOC_desc->controladdress = + (thisTrackData->Adr << 4) | thisTrackData->Control; #endif - a_TOC_desc->unused1 = 0; - a_TOC_desc->tracknumber = thisTrackData->TrackNumber; - a_TOC_desc->rel_minutes = 0; //didn't look too much into finding this since it is unused - a_TOC_desc->rel_seconds = 0; - a_TOC_desc->rel_frames = 0; - a_TOC_desc->zero_space = 0; - a_TOC_desc->abs_minutes = thisTrackData->Address[1]; - a_TOC_desc->abs_seconds = thisTrackData->Address[2]; - a_TOC_desc->abs_frames = thisTrackData->Address[3]; - } - - return; -} - -uint16_t Windows_ioctlProbeTargetDrive(const char* id3args_drive, char* mcdi_data) { - uint16_t mcdi_data_len = 0; - char cd_device_path[16]; - - memset(cd_device_path, 0, 16); - sprintf(cd_device_path, "\\\\.\\%s:", id3args_drive); - - HANDLE cdrom_device = APar_OpenFileWin32(cd_device_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (cdrom_device != INVALID_HANDLE_VALUE) { - Windows_ioctlReadCDTOC(cdrom_device); - if (cdTOC != NULL) { - uint8_t cdType = DetermineCDType(cdTOC); - if (cdType == CDOBJECT_AUDIOCD) { - mcdi_data_len = FormMCDIdata(mcdi_data); - } - } - CloseHandle(cdrom_device); - } - - return mcdi_data_len; + a_TOC_desc->unused1 = 0; + a_TOC_desc->tracknumber = thisTrackData->TrackNumber; + a_TOC_desc->rel_minutes = + 0; // didn't look too much into finding this since it is unused + a_TOC_desc->rel_seconds = 0; + a_TOC_desc->rel_frames = 0; + a_TOC_desc->zero_space = 0; + a_TOC_desc->abs_minutes = thisTrackData->Address[1]; + a_TOC_desc->abs_seconds = thisTrackData->Address[2]; + a_TOC_desc->abs_frames = thisTrackData->Address[3]; + } + + return; +} + +uint16_t Windows_ioctlProbeTargetDrive(const char *id3args_drive, + char *mcdi_data) { + uint16_t mcdi_data_len = 0; + char cd_device_path[16]; + + memset(cd_device_path, 0, 16); + sprintf(cd_device_path, "\\\\.\\%s:", id3args_drive); + + HANDLE cdrom_device = APar_OpenFileWin32(cd_device_path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (cdrom_device != INVALID_HANDLE_VALUE) { + Windows_ioctlReadCDTOC(cdrom_device); + if (cdTOC != NULL) { + uint8_t cdType = DetermineCDType(cdTOC); + if (cdType == CDOBJECT_AUDIOCD) { + mcdi_data_len = FormMCDIdata(mcdi_data); + } + } + CloseHandle(cdrom_device); + } + + return mcdi_data_len; } #endif @@ -511,14 +576,14 @@ // CD TOC Entry Area // //////////////////////////////////////////////////////////////////////////// -uint16_t GenerateMCDIfromCD(const char* drive, char* dest_buffer) { - uint16_t mcdi_bytes = 0; -#if defined (DARWIN_PLATFORM) - mcdi_bytes = OSX_ProbeTargetDrive(drive, dest_buffer); -#elif defined (HAVE_LINUX_CDROM_H) - mcdi_bytes = Linux_ioctlProbeTargetDrive(drive, dest_buffer); -#elif defined (_WIN32) - mcdi_bytes = Windows_ioctlProbeTargetDrive(drive, dest_buffer); +uint16_t GenerateMCDIfromCD(const char *drive, char *dest_buffer) { + uint16_t mcdi_bytes = 0; +#if defined(__APPLE__) + mcdi_bytes = OSX_ProbeTargetDrive(drive, dest_buffer); +#elif defined(__linux__) + mcdi_bytes = Linux_ioctlProbeTargetDrive(drive, dest_buffer); +#elif defined(_WIN32) + mcdi_bytes = Windows_ioctlProbeTargetDrive(drive, dest_buffer); #endif - return mcdi_bytes; + return mcdi_bytes; } diff -Nru atomicparsley-0.9.6/src/CDtoc.h atomicparsley-20210715.151551.e7ad03a/src/CDtoc.h --- atomicparsley-0.9.6/src/CDtoc.h 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/CDtoc.h 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - CDtoc.h - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,37 +10,38 @@ 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 + 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 2006-2007 puck_lock - with contributions from others; see the CREDITS file + with contributions from others; see the CREDITS file */ //==================================================================// #include "ap_types.h" -#if defined (_WIN32) -//these #defines & structs are copied from the MS W2k DDK headers so the entire DDK isn't required to be installed to compile AP_CDTOC for MCDI support +#if defined(_WIN32) +// these #defines & structs are copied from the MS W2k DDK headers so the entire +// DDK isn't required to be installed to compile AP_CDTOC for MCDI support #ifndef DEVICE_TYPE #define DEVICE_TYPE ULONG #endif -#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM 0x00000002 -#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM +#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM -#define METHOD_BUFFERED 0 +#define METHOD_BUFFERED 0 -#define FILE_READ_ACCESS ( 0x0001 ) // file & pipe +#define FILE_READ_ACCESS (0x0001) // file & pipe -#define CTL_CODE( DeviceType, Function, Method, Access ) ( \ - ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ -) +#define CTL_CODE(DeviceType, Function, Method, Access) \ + (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) -#define IOCTL_CDROM_READ_TOC CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CDROM_READ_TOC \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) #define MAXIMUM_NUMBER_TRACKS 100 @@ -50,30 +51,30 @@ // typedef struct _TRACK_DATA { - UCHAR Reserved; - UCHAR Control : 4; - UCHAR Adr : 4; - UCHAR TrackNumber; - UCHAR Reserved1; - UCHAR Address[4]; + UCHAR Reserved; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR TrackNumber; + UCHAR Reserved1; + UCHAR Address[4]; } TRACK_DATA, *PTRACK_DATA; typedef struct _CDROM_TOC { - // - // Header - // - - UCHAR Length[2]; - UCHAR FirstTrack; - UCHAR LastTrack; - - // - // Track data - // + // + // Header + // + + UCHAR Length[2]; + UCHAR FirstTrack; + UCHAR LastTrack; + + // + // Track data + // - TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS]; + TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS]; } CDROM_TOC, *PCDROM_TOC; #endif -uint16_t GenerateMCDIfromCD(const char* drive, char* dest_buffer); +uint16_t GenerateMCDIfromCD(const char *drive, char *dest_buffer); diff -Nru atomicparsley-0.9.6/src/.clang-format atomicparsley-20210715.151551.e7ad03a/src/.clang-format --- atomicparsley-0.9.6/src/.clang-format 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/.clang-format 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,9 @@ +--- +BasedOnStyle: LLVM +IndentWidth: 2 +BinPackArguments: false +BinPackParameters: false +IncludeIsMainRegex: AtomicParse +--- +Language: Cpp +--- diff -Nru atomicparsley-0.9.6/src/compress.cpp atomicparsley-20210715.151551.e7ad03a/src/compress.cpp --- atomicparsley-0.9.6/src/compress.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/compress.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - compress.cpp - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,12 +10,12 @@ 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 + 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 2006-2007 puck_lock + Copyright (C) 2006-2007 puck_lock with contributions from others; see the CREDITS file */ //==================================================================// @@ -25,73 +25,71 @@ #include #endif -static void* zalloc(void *opaque, unsigned int items, unsigned int size) -{ - return calloc(items, size); +static void *zalloc(void *opaque, unsigned int items, unsigned int size) { + return calloc(items, size); } -static void zfree(void *opaque, void *ptr) -{ - free(ptr); -} +static void zfree(void *opaque, void *ptr) { free(ptr); } /*---------------------- APar_zlib_inflate in_buffer - pointer to already compressed data - in_buf_len - length of compressed data - out_buffer - pointer to a buffer to store decompressed/inflated data - out_buf_len - length of the out_buffer/max allowable decompressed size + in_buf_len - length of compressed data + out_buffer - pointer to a buffer to store decompressed/inflated data + out_buf_len - length of the out_buffer/max allowable decompressed size fill ----------------------*/ -void APar_zlib_inflate(char* in_buffer, uint32_t in_buf_len, - char* out_buffer, uint32_t out_buf_len) -{ +void APar_zlib_inflate(char *in_buffer, + uint32_t in_buf_len, + char *out_buffer, + uint32_t out_buf_len) { #if defined HAVE_ZLIB_H - z_stream zlib; + z_stream zlib; - memset(&zlib, 0, sizeof(zlib)); + memset(&zlib, 0, sizeof(zlib)); - // Decompress to another buffer - zlib.zalloc = zalloc; - zlib.zfree = zfree; - zlib.opaque = NULL; - zlib.avail_out = out_buf_len +1; - zlib.next_out = (unsigned char*)out_buffer; - zlib.avail_in = in_buf_len; - zlib.next_in = (unsigned char*)in_buffer; - inflateInit(&zlib); - inflate(&zlib, Z_PARTIAL_FLUSH); - inflateEnd(&zlib); + // Decompress to another buffer + zlib.zalloc = zalloc; + zlib.zfree = zfree; + zlib.opaque = NULL; + zlib.avail_out = out_buf_len + 1; + zlib.next_out = (unsigned char *)out_buffer; + zlib.avail_in = in_buf_len; + zlib.next_in = (unsigned char *)in_buffer; + inflateInit(&zlib); + inflate(&zlib, Z_PARTIAL_FLUSH); + inflateEnd(&zlib); #endif - return ; + return; } -uint32_t APar_zlib_deflate(char* in_buffer, uint32_t in_buf_len, - char* out_buffer, uint32_t out_buf_len) -{ - uint32_t compressed_bytes = 0; +uint32_t APar_zlib_deflate(char *in_buffer, + uint32_t in_buf_len, + char *out_buffer, + uint32_t out_buf_len) { + uint32_t compressed_bytes = 0; #if defined HAVE_ZLIB_H - z_stream zlib; + z_stream zlib; - memset(&zlib, 0, sizeof(zlib)); + memset(&zlib, 0, sizeof(zlib)); - // Compress(default level 6) to another buffer - zlib.zalloc = zalloc; - zlib.zfree = zfree; - zlib.opaque = NULL; - zlib.avail_out = out_buf_len +1; - zlib.next_out = (unsigned char*)out_buffer; - zlib.avail_in = in_buf_len; - zlib.next_in = (unsigned char*)in_buffer; - zlib.total_out = 0; - deflateInit(&zlib, Z_DEFAULT_COMPRESSION); - if (Z_STREAM_END == deflate(&zlib, Z_FINISH) ) { - compressed_bytes = zlib.total_out; - deflateEnd(&zlib); - } + // Compress(default level 6) to another buffer + zlib.zalloc = zalloc; + zlib.zfree = zfree; + zlib.opaque = NULL; + zlib.avail_out = out_buf_len + 1; + zlib.next_out = (unsigned char *)out_buffer; + zlib.avail_in = in_buf_len; + zlib.next_in = (unsigned char *)in_buffer; + zlib.total_out = 0; + deflateInit(&zlib, Z_DEFAULT_COMPRESSION); + if (Z_STREAM_END == deflate(&zlib, Z_FINISH)) { + compressed_bytes = zlib.total_out; + deflateEnd(&zlib); + } #endif - return compressed_bytes; + return compressed_bytes; } diff -Nru atomicparsley-0.9.6/src/extracts.cpp atomicparsley-20210715.151551.e7ad03a/src/extracts.cpp --- atomicparsley-0.9.6/src/extracts.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/extracts.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -15,9 +15,9 @@ cannot, write to the Free Software Foundation, 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org - Copyright 2006-2007 puck_lock + Copyright (C) 2006-2007 puck_lock with contributions from others; see the CREDITS file - */ + */ //==================================================================// #include "AtomicParsley.h" @@ -26,34 +26,38 @@ iods_OD iods_info = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /////////////////////////////////////////////////////////////////////////////////////// -// File reading routines // +// File reading routines // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_skip_filler isofile - the file to be scanned - start_position - the offset from the start of file where to commence possible skipping + start_position - the offset from the start of file where to commence possible +skipping - I can't remember where exactly I stumbled over what to skip, but this touches on it: http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt - (not that everything there is the gospel truth). + I can't remember where exactly I stumbled over what to skip, but this +touches on it: +http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt (not that +everything there is the gospel truth). ----------------------*/ -uint8_t APar_skip_filler(FILE* isofile, uint32_t start_position) { - uint8_t skip_bytes = 0; +uint8_t APar_skip_filler(FILE *isofile, uint32_t start_position) { + uint8_t skip_bytes = 0; - while (true) { - uint8_t eval_byte = APar_read8(isofile, start_position + skip_bytes); + while (true) { + uint8_t eval_byte = APar_read8(isofile, start_position + skip_bytes); - if (eval_byte == 0x80 || eval_byte == 0x81 || eval_byte == 0xFE) { //seems sometimes QT writes 0x81 - skip_bytes++; - } else { - break; - } - } - return skip_bytes; + if (eval_byte == 0x80 || eval_byte == 0x81 || + eval_byte == 0xFE) { // seems sometimes QT writes 0x81 + skip_bytes++; + } else { + break; + } + } + return skip_bytes; } /////////////////////////////////////////////////////////////////////////////////////// -// string routines // +// string routines // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- @@ -61,1320 +65,1713 @@ lnum - the number to convert to a string data - the string to hold the conversion - Returns a pointer to the originating string (used to print out a string; different from the other function which converts returning void) + Returns a pointer to the originating string (used to print out a string; +different from the other function which converts returning void) ----------------------*/ -char* uint32tochar4(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 data; +char *uint32tochar4(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 data; } /*---------------------- purge_extraneous_characters data - the string which may contain low or high ascii - Just change most non-textual characters (like a pesky new line char) to something less objectionable - for stdout formatting only + Just change most non-textual characters (like a pesky new line char) to +something less objectionable - for stdout formatting only ----------------------*/ -uint16_t purge_extraneous_characters(char* data) { - uint16_t purgings = 0; - uint16_t str_len = strlen(data); - for(uint16_t str_offset = 0; str_offset < str_len; str_offset++) { - if (data[str_offset] < 32 || data[str_offset] == 127) { - data[str_offset] = 19; - purgings++; - break; - } - } - return purgings; -} - -void mem_append(const char* add_string, char* dest_string) { - uint8_t str_len = strlen(dest_string); - if (str_len > 0) { - memcpy(dest_string+str_len, ", ", 2); - memcpy(dest_string+str_len+2, add_string, strlen(add_string) ); - } else { - memcpy(dest_string, add_string, strlen(add_string) ); - } - return; +uint16_t purge_extraneous_characters(char *data) { + uint16_t purgings = 0; + uint16_t str_len = strlen(data); + for (uint16_t str_offset = 0; str_offset < str_len; str_offset++) { + if (data[str_offset] < 32 || data[str_offset] == 127) { + data[str_offset] = 19; + purgings++; + break; + } + } + return purgings; +} + +void mem_append(const char *add_string, char *dest_string) { + uint8_t str_len = strlen(dest_string); + if (str_len > 0) { + memcpy(dest_string + str_len, ", ", 2); + memcpy(dest_string + str_len + 2, add_string, strlen(add_string)); + } else { + memcpy(dest_string, add_string, strlen(add_string)); + } + return; } /*---------------------- secsTOtime seconds - duration in seconds as a floating point number - Convert decimal seconds to hh:mm:ss.milliseconds. Take the whole seconds and manually separate out the hours, minutes and remaining seconds. For the milliseconds, - sprintf into a separate string because there doesn't seem to be a way to print without the leading zero; so copy form that string the digits we want then. + Convert decimal seconds to hh:mm:ss.milliseconds. Take the whole seconds and +manually separate out the hours, minutes and remaining seconds. For the +milliseconds, sprintf into a separate string because there doesn't seem to be a +way to print without the leading zero; so copy form that string the digits we +want then. ----------------------*/ -char* secsTOtime(double seconds) { - ap_time time_duration = {0}; - uint32_t whole_secs = (uint32_t)(seconds / 1); - - time_duration.rem_millisecs = seconds - (double)whole_secs; - time_duration.hours = whole_secs / 3600; - whole_secs -= time_duration.hours * 3600; - time_duration.minutes = whole_secs / 60; - whole_secs -= time_duration.minutes * 60; - time_duration.seconds = whole_secs; - - static char hhmmss_time[20]; - memset(hhmmss_time, 0, 20); - char milli[5]; - memset(milli, 0, 5); - - uint8_t time_offset = 0; - if (time_duration.hours > 0) { - if (time_duration.hours < 10) { - sprintf(hhmmss_time, "0%u:", time_duration.hours); - } else { - sprintf(hhmmss_time, "%u:", time_duration.hours); - } - time_offset+=3; - } - if (time_duration.minutes > 0) { - if (time_duration.minutes < 10) { - sprintf(hhmmss_time+time_offset, "0%u:", time_duration.minutes); - } else { - sprintf(hhmmss_time+time_offset, "%u:", time_duration.minutes); - } - time_offset+=3; - } else { - memcpy(hhmmss_time+time_offset, "0:", 2); - time_offset+=2; - } - if (time_duration.seconds > 0) { - if (time_duration.seconds < 10) { - sprintf(hhmmss_time+time_offset, "0%u", time_duration.seconds); - } else { - sprintf(hhmmss_time+time_offset, "%u", time_duration.seconds); - } - time_offset+=2; - } else { - memcpy(hhmmss_time+time_offset, "0.", 2); - time_offset+=1; - } +char *secsTOtime(double seconds) { + ap_time time_duration = {0}; + uint32_t whole_secs = (uint32_t)(seconds / 1); + + time_duration.rem_millisecs = seconds - (double)whole_secs; + time_duration.hours = whole_secs / 3600; + whole_secs -= time_duration.hours * 3600; + time_duration.minutes = whole_secs / 60; + whole_secs -= time_duration.minutes * 60; + time_duration.seconds = whole_secs; + + static char hhmmss_time[20]; + memset(hhmmss_time, 0, 20); + char milli[5]; + memset(milli, 0, 5); + + uint8_t time_offset = 0; + if (time_duration.hours > 0) { + if (time_duration.hours < 10) { + sprintf(hhmmss_time, "0%u:", time_duration.hours); + } else { + sprintf(hhmmss_time, "%u:", time_duration.hours); + } + time_offset += 3; + } + if (time_duration.minutes > 0) { + if (time_duration.minutes < 10) { + sprintf(hhmmss_time + time_offset, "0%u:", time_duration.minutes); + } else { + sprintf(hhmmss_time + time_offset, "%u:", time_duration.minutes); + } + time_offset += 3; + } else { + memcpy(hhmmss_time + time_offset, "0:", 2); + time_offset += 2; + } + if (time_duration.seconds > 0) { + if (time_duration.seconds < 10) { + sprintf(hhmmss_time + time_offset, "0%u", time_duration.seconds); + } else { + sprintf(hhmmss_time + time_offset, "%u", time_duration.seconds); + } + time_offset += 2; + } else { + memcpy(hhmmss_time + time_offset, "0.", 2); + time_offset += 1; + } + + sprintf( + milli, + "%.2lf", + time_duration.rem_millisecs); // sprintf the double float into a new + // string because I don't know if there is a + // way to print without a leading zero + memcpy(hhmmss_time + time_offset, milli + 1, 3); - sprintf(milli, "%.2lf", time_duration.rem_millisecs); //sprintf the double float into a new string because I don't know if there is a way to print without a leading zero - memcpy(hhmmss_time+time_offset, milli+1, 3); - - return *&hhmmss_time; + return *&hhmmss_time; } /////////////////////////////////////////////////////////////////////////////////////// -// Print Profile Info // +// Print Profile Info // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_ShowMPEG4VisualProfileInfo - track_info - a pointer to the struct holding all the information gathered as a single 'trak' atom was traversed + track_info - a pointer to the struct holding all the information +gathered as a single 'trak' atom was traversed - If a movie-level iods (containing profiles on a movie-level basis), prefer that mechanism for choosing which profile, otherwise fall back to 'esds' profiles. - Much of this was garnered from ISO 14496-2:2001 - up to 'Simple Studio'. + If a movie-level iods (containing profiles on a movie-level basis), prefer +that mechanism for choosing which profile, otherwise fall back to 'esds' +profiles. Much of this was garnered from ISO 14496-2:2001 - up to 'Simple +Studio'. ----------------------*/ -void APar_ShowMPEG4VisualProfileInfo(TrackInfo* track_info) { - fprintf(stdout, " MPEG-4 Visual "); - uint8_t mp4v_profile = 0; - if (movie_info.contains_iods) { - mp4v_profile = iods_info.video_profile_level; - } else { - mp4v_profile = track_info->m4v_profile; - } - - //unparalleled joy - Annex G table g1 - a binary listing (this from 14496-2:2001) - if (mp4v_profile == 0x01) { - fprintf(stdout, "Simple Profile, Level 1"); //00000001 - } else if (mp4v_profile == 0x02) { - fprintf(stdout, "Simple Profile, Level 2"); //00000010 - } else if (mp4v_profile == 0x03) { - fprintf(stdout, "Simple Profile, Level 3"); //most files will land here //00000011 - - } else if (mp4v_profile == 0x08) { //Compressor can create these in 3gp files - fprintf(stdout, "Simple Profile, Level 0"); //ISO 14496-2:2004(e) //00001000 - - //Reserved 00000100 - 00000111 - } else if (mp4v_profile == 0x10) { - fprintf(stdout, "Simple Scalable Profile, Level 0"); //00010000 - } else if (mp4v_profile == 0x11) { - fprintf(stdout, "Simple Scalable Profile, Level 1"); //00010001 - } else if (mp4v_profile == 0x12) { - fprintf(stdout, "Simple Scalable Profile, Level 2"); //00010010 - - //Reserved 00010011 - 00100000 - } else if (mp4v_profile == 0x21) { - fprintf(stdout, "Core Profile, Level 1"); //00100001 - } else if (mp4v_profile == 0x22) { - fprintf(stdout, "Core Profile, Level 2"); //00100010 - - //Reserved 00100011 - 00110001 - } else if (mp4v_profile == 0x32) { - fprintf(stdout, "Main Profile, Level 2"); //00110010 - } else if (mp4v_profile == 0x33) { - fprintf(stdout, "Main Profile, Level 3"); //00110011 - } else if (mp4v_profile == 0x34) { - fprintf(stdout, "Main Profile, Level 4"); //00110100 - - //Reserved 00110101 - 01000001 - } else if (mp4v_profile == 0x42) { - fprintf(stdout, "N-bit Profile, Level 2"); //01000010 - - //Reserved 01000011 - 01010000 - } else if (mp4v_profile == 0x51) { - fprintf(stdout, "Scalable Texture Profile, Level 1"); //01010001 - - //Reserved 01010010 - 01100000 - } else if (mp4v_profile == 0x61) { - fprintf(stdout, "Simple Face Animation, Level 1"); //01100001 - } else if (mp4v_profile == 0x62) { - fprintf(stdout, "Simple Face Animation, Level 2"); //01100010 - - } else if (mp4v_profile == 0x63) { - fprintf(stdout, "Simple FBA Profile, Level 1"); //01100011 - } else if (mp4v_profile == 0x64) { - fprintf(stdout, "Simple FBA Profile, Level 2"); //01100100 - - //Reserved 01100101 - 01110000 - } else if (mp4v_profile == 0x71) { - fprintf(stdout, "Basic Animated Texture Profile, Level 1"); //01110001 - } else if (mp4v_profile == 0x72) { - fprintf(stdout, "Basic Animated Texture Profile, Level 2"); //01110010 - - //Reserved 01110011 - 10000000 - } else if (mp4v_profile == 0x81) { - fprintf(stdout, "Hybrid Profile, Level 1"); //10000001 - } else if (mp4v_profile == 0x82) { - fprintf(stdout, "Hybrid Profile, Level 2"); //10000010 - - //Reserved 10000011 - 10010000 - } else if (mp4v_profile == 0x91) { - fprintf(stdout, "Advanced Real Time Simple Profile, Level 1"); //10010001 - } else if (mp4v_profile == 0x92) { - fprintf(stdout, "Advanced Real Time Simple Profile, Level 2"); //10010010 - } else if (mp4v_profile == 0x93) { - fprintf(stdout, "Advanced Real Time Simple Profile, Level 3"); //10010011 - } else if (mp4v_profile == 0x94) { - fprintf(stdout, "Advanced Real Time Simple Profile, Level 4"); //10010100 - - //Reserved 10010101 - 10100000 - } else if (mp4v_profile == 0xA1) { - fprintf(stdout, "Core Scalable Profile, Level 1"); //10100001 - } else if (mp4v_profile == 0xA2) { - fprintf(stdout, "Core Scalable Profile, Level 2"); //10100010 - } else if (mp4v_profile == 0xA3) { - fprintf(stdout, "Core Scalable Profile, Level 3"); //10100011 - - //Reserved 10100100 - 10110000 - } else if (mp4v_profile == 0xB1) { - fprintf(stdout, "Advanced Coding Efficiency Profile, Level 1"); //10110001 - } else if (mp4v_profile == 0xB2) { - fprintf(stdout, "Advanced Coding Efficiency Profile, Level 2"); //10110010 - } else if (mp4v_profile == 0xB3) { - fprintf(stdout, "Advanced Coding Efficiency Profile, Level 3"); //10110011 - } else if (mp4v_profile == 0xB4) { - fprintf(stdout, "Advanced Coding Efficiency Profile, Level 4"); //10110100 - - //Reserved 10110101 11000000 - } else if (mp4v_profile == 0xC1) { - fprintf(stdout, "Advanced Core Profile, Level 1"); //11000001 - } else if (mp4v_profile == 0xC2) { - fprintf(stdout, "Advanced Core Profile, Level 2"); //11000010 - - //Reserved 11000011 11010000 - } else if (mp4v_profile == 0xD1) { - fprintf(stdout, "Advanced Scalable Texture, Level 1"); //11010001 - } else if (mp4v_profile == 0xD2) { - fprintf(stdout, "Advanced Scalable Texture, Level 2"); //11010010 - } else if (mp4v_profile == 0xD2) { - fprintf(stdout, "Advanced Scalable Texture, Level 3"); //11010011 - - //from a draft document - 1999 (earlier than the 2000 above!!) - } else if (mp4v_profile == 0xE1) { - fprintf(stdout, "Simple Studio Profile, Level 1"); //11100001 - } else if (mp4v_profile == 0xE2) { - fprintf(stdout, "Simple Studio Profile, Level 2"); //11100010 - } else if (mp4v_profile == 0xE3) { - fprintf(stdout, "Simple Studio Profile, Level 3"); //11100011 - } else if (mp4v_profile == 0xE4) { - fprintf(stdout, "Simple Studio Profile, Level 4"); //11100100 - - } else if (mp4v_profile == 0xE5) { - fprintf(stdout, "Core Studio Profile, Level 1"); //11100101 - } else if (mp4v_profile == 0xE6) { - fprintf(stdout, "Core Studio Profile, Level 2"); //11100110 - } else if (mp4v_profile == 0xE7) { - fprintf(stdout, "Core Studio Profile, Level 3"); //11100111 - } else if (mp4v_profile == 0xE8) { - fprintf(stdout, "Core Studio Profile, Level 4"); //11101000 - - //Reserved 11101001 - 11101111 - //ISO 14496-2:2004(e) - } else if (mp4v_profile == 0xF0) { - fprintf(stdout, "Advanced Simple Profile, Level 0"); //11110000 - } else if (mp4v_profile == 0xF1) { - fprintf(stdout, "Advanced Simple Profile, Level 1"); //11110001 - } else if (mp4v_profile == 0xF2) { - fprintf(stdout, "Advanced Simple Profile, Level 2"); //11110010 ////3gp files that QT says is H.263 have esds to 0xF2 & their ObjectType set to 0x20 (mpeg-4 visual) - ////...and its been figured out - FILE EXTENSION of all things determines mpeg-4 ASP or H.263 - } else if (mp4v_profile == 0xF3) { - fprintf(stdout, "Advanced Simple Profile, Level 3"); //11110011 - } else if (mp4v_profile == 0xF4) { - fprintf(stdout, "Advanced Simple Profile, Level 4"); //11110100 - } else if (mp4v_profile == 0xF5) { - fprintf(stdout, "Advanced Simple Profile, Level 5"); //11110101 - - //Reserved 11110110 - } else if (mp4v_profile == 0xF7) { - fprintf(stdout, "Advanced Simple Profile, Level 3b"); //11110111 - - } else if (mp4v_profile == 0xF7) { - fprintf(stdout, "Fine Granularity Scalable Profile/Level 0"); //11111000 - } else if (mp4v_profile == 0xF7) { - fprintf(stdout, "Fine Granularity Scalable Profile/Level 1"); //11111001 - } else if (mp4v_profile == 0xF7) { - fprintf(stdout, "Fine Granularity Scalable Profile/Level 2"); //11111010 - } else if (mp4v_profile == 0xF7) { - fprintf(stdout, "Fine Granularity Scalable Profile/Level 3"); //11111011 - } else if (mp4v_profile == 0xF7) { - fprintf(stdout, "Fine Granularity Scalable Profile/Level 4"); //11111100 - } else if (mp4v_profile == 0xF7) { - fprintf(stdout, "Fine Granularity Scalable Profile/Level 5"); //11111101 - - //Reserved 11111110 - //Reserved for Escape 11111111 - - } else { - fprintf(stdout, "Unknown profile: 0x%X", mp4v_profile); - } - return; +void APar_ShowMPEG4VisualProfileInfo(TrackInfo *track_info) { + fprintf(stdout, " MPEG-4 Visual "); + uint8_t mp4v_profile = 0; + if (movie_info.contains_iods) { + mp4v_profile = iods_info.video_profile_level; + } else { + mp4v_profile = track_info->m4v_profile; + } + + // unparalleled joy - Annex G table g1 - a binary listing (this from + // 14496-2:2001) + if (mp4v_profile == 0x01) { + fprintf(stdout, "Simple Profile, Level 1"); // 00000001 + } else if (mp4v_profile == 0x02) { + fprintf(stdout, "Simple Profile, Level 2"); // 00000010 + } else if (mp4v_profile == 0x03) { + fprintf(stdout, + "Simple Profile, Level 3"); // most files will land here //00000011 + + } else if (mp4v_profile == 0x08) { // Compressor can create these in 3gp files + fprintf(stdout, "Simple Profile, Level 0"); // ISO 14496-2:2004(e) + // //00001000 + + // Reserved 00000100 - 00000111 + } else if (mp4v_profile == 0x10) { + fprintf(stdout, "Simple Scalable Profile, Level 0"); // 00010000 + } else if (mp4v_profile == 0x11) { + fprintf(stdout, "Simple Scalable Profile, Level 1"); // 00010001 + } else if (mp4v_profile == 0x12) { + fprintf(stdout, "Simple Scalable Profile, Level 2"); // 00010010 + + // Reserved 00010011 - 00100000 + } else if (mp4v_profile == 0x21) { + fprintf(stdout, "Core Profile, Level 1"); // 00100001 + } else if (mp4v_profile == 0x22) { + fprintf(stdout, "Core Profile, Level 2"); // 00100010 + + // Reserved 00100011 - 00110001 + } else if (mp4v_profile == 0x32) { + fprintf(stdout, "Main Profile, Level 2"); // 00110010 + } else if (mp4v_profile == 0x33) { + fprintf(stdout, "Main Profile, Level 3"); // 00110011 + } else if (mp4v_profile == 0x34) { + fprintf(stdout, "Main Profile, Level 4"); // 00110100 + + // Reserved 00110101 - 01000001 + } else if (mp4v_profile == 0x42) { + fprintf(stdout, "N-bit Profile, Level 2"); // 01000010 + + // Reserved 01000011 - 01010000 + } else if (mp4v_profile == 0x51) { + fprintf(stdout, "Scalable Texture Profile, Level 1"); // 01010001 + + // Reserved 01010010 - 01100000 + } else if (mp4v_profile == 0x61) { + fprintf(stdout, "Simple Face Animation, Level 1"); // 01100001 + } else if (mp4v_profile == 0x62) { + fprintf(stdout, "Simple Face Animation, Level 2"); // 01100010 + + } else if (mp4v_profile == 0x63) { + fprintf(stdout, "Simple FBA Profile, Level 1"); // 01100011 + } else if (mp4v_profile == 0x64) { + fprintf(stdout, "Simple FBA Profile, Level 2"); // 01100100 + + // Reserved 01100101 - 01110000 + } else if (mp4v_profile == 0x71) { + fprintf(stdout, "Basic Animated Texture Profile, Level 1"); // 01110001 + } else if (mp4v_profile == 0x72) { + fprintf(stdout, "Basic Animated Texture Profile, Level 2"); // 01110010 + + // Reserved 01110011 - 10000000 + } else if (mp4v_profile == 0x81) { + fprintf(stdout, "Hybrid Profile, Level 1"); // 10000001 + } else if (mp4v_profile == 0x82) { + fprintf(stdout, "Hybrid Profile, Level 2"); // 10000010 + + // Reserved 10000011 - 10010000 + } else if (mp4v_profile == 0x91) { + fprintf(stdout, "Advanced Real Time Simple Profile, Level 1"); // 10010001 + } else if (mp4v_profile == 0x92) { + fprintf(stdout, "Advanced Real Time Simple Profile, Level 2"); // 10010010 + } else if (mp4v_profile == 0x93) { + fprintf(stdout, "Advanced Real Time Simple Profile, Level 3"); // 10010011 + } else if (mp4v_profile == 0x94) { + fprintf(stdout, "Advanced Real Time Simple Profile, Level 4"); // 10010100 + + // Reserved 10010101 - 10100000 + } else if (mp4v_profile == 0xA1) { + fprintf(stdout, "Core Scalable Profile, Level 1"); // 10100001 + } else if (mp4v_profile == 0xA2) { + fprintf(stdout, "Core Scalable Profile, Level 2"); // 10100010 + } else if (mp4v_profile == 0xA3) { + fprintf(stdout, "Core Scalable Profile, Level 3"); // 10100011 + + // Reserved 10100100 - 10110000 + } else if (mp4v_profile == 0xB1) { + fprintf(stdout, "Advanced Coding Efficiency Profile, Level 1"); // 10110001 + } else if (mp4v_profile == 0xB2) { + fprintf(stdout, "Advanced Coding Efficiency Profile, Level 2"); // 10110010 + } else if (mp4v_profile == 0xB3) { + fprintf(stdout, "Advanced Coding Efficiency Profile, Level 3"); // 10110011 + } else if (mp4v_profile == 0xB4) { + fprintf(stdout, "Advanced Coding Efficiency Profile, Level 4"); // 10110100 + + // Reserved 10110101 11000000 + } else if (mp4v_profile == 0xC1) { + fprintf(stdout, "Advanced Core Profile, Level 1"); // 11000001 + } else if (mp4v_profile == 0xC2) { + fprintf(stdout, "Advanced Core Profile, Level 2"); // 11000010 + + // Reserved 11000011 11010000 + } else if (mp4v_profile == 0xD1) { + fprintf(stdout, "Advanced Scalable Texture, Level 1"); // 11010001 + } else if (mp4v_profile == 0xD2) { + fprintf(stdout, "Advanced Scalable Texture, Level 2"); // 11010010 + } else if (mp4v_profile == 0xD2) { + fprintf(stdout, "Advanced Scalable Texture, Level 3"); // 11010011 + + // from a draft document - 1999 (earlier than the 2000 above!!) + } else if (mp4v_profile == 0xE1) { + fprintf(stdout, "Simple Studio Profile, Level 1"); // 11100001 + } else if (mp4v_profile == 0xE2) { + fprintf(stdout, "Simple Studio Profile, Level 2"); // 11100010 + } else if (mp4v_profile == 0xE3) { + fprintf(stdout, "Simple Studio Profile, Level 3"); // 11100011 + } else if (mp4v_profile == 0xE4) { + fprintf(stdout, "Simple Studio Profile, Level 4"); // 11100100 + + } else if (mp4v_profile == 0xE5) { + fprintf(stdout, "Core Studio Profile, Level 1"); // 11100101 + } else if (mp4v_profile == 0xE6) { + fprintf(stdout, "Core Studio Profile, Level 2"); // 11100110 + } else if (mp4v_profile == 0xE7) { + fprintf(stdout, "Core Studio Profile, Level 3"); // 11100111 + } else if (mp4v_profile == 0xE8) { + fprintf(stdout, "Core Studio Profile, Level 4"); // 11101000 + + // Reserved 11101001 - 11101111 + // ISO 14496-2:2004(e) + } else if (mp4v_profile == 0xF0) { + fprintf(stdout, "Advanced Simple Profile, Level 0"); // 11110000 + } else if (mp4v_profile == 0xF1) { + fprintf(stdout, "Advanced Simple Profile, Level 1"); // 11110001 + } else if (mp4v_profile == 0xF2) { + fprintf( + stdout, + "Advanced Simple Profile, Level 2"); // 11110010 ////3gp files that QT + // says is H.263 have esds to 0xF2 + // & their ObjectType set to 0x20 + // (mpeg-4 visual) + ////...and its been figured out - + /// FILE EXTENSION of all things + /// determines mpeg-4 ASP or H.263 + } else if (mp4v_profile == 0xF3) { + fprintf(stdout, "Advanced Simple Profile, Level 3"); // 11110011 + } else if (mp4v_profile == 0xF4) { + fprintf(stdout, "Advanced Simple Profile, Level 4"); // 11110100 + } else if (mp4v_profile == 0xF5) { + fprintf(stdout, "Advanced Simple Profile, Level 5"); // 11110101 + + // Reserved 11110110 + } else if (mp4v_profile == 0xF7) { + fprintf(stdout, "Advanced Simple Profile, Level 3b"); // 11110111 + + } else if (mp4v_profile == 0xF7) { + fprintf(stdout, "Fine Granularity Scalable Profile/Level 0"); // 11111000 + } else if (mp4v_profile == 0xF7) { + fprintf(stdout, "Fine Granularity Scalable Profile/Level 1"); // 11111001 + } else if (mp4v_profile == 0xF7) { + fprintf(stdout, "Fine Granularity Scalable Profile/Level 2"); // 11111010 + } else if (mp4v_profile == 0xF7) { + fprintf(stdout, "Fine Granularity Scalable Profile/Level 3"); // 11111011 + } else if (mp4v_profile == 0xF7) { + fprintf(stdout, "Fine Granularity Scalable Profile/Level 4"); // 11111100 + } else if (mp4v_profile == 0xF7) { + fprintf(stdout, "Fine Granularity Scalable Profile/Level 5"); // 11111101 + + // Reserved 11111110 + // Reserved for Escape 11111111 + + } else { + fprintf(stdout, "Unknown profile: 0x%X", mp4v_profile); + } + return; } /*---------------------- APar_ShowMPEG4AACProfileInfo - track_info - a pointer to the struct holding all the information gathered as a single 'trak' atom was traversed + track_info - a pointer to the struct holding all the information +gathered as a single 'trak' atom was traversed ----------------------*/ -void APar_ShowMPEG4AACProfileInfo(TrackInfo* track_info) { - if (track_info->descriptor_object_typeID == 1) { - fprintf(stdout, " MPEG-4 AAC Main Profile"); - } else if (track_info->descriptor_object_typeID == 2) { - fprintf(stdout, " MPEG-4 AAC Low Complexity/LC Profile"); //most files will land here - } else if (track_info->descriptor_object_typeID == 3) { - fprintf(stdout, " MPEG-4 AAC Scaleable Sample Rate/SSR Profile"); - } else if (track_info->descriptor_object_typeID == 4) { - fprintf(stdout, " MPEG-4 AAC Long Term Prediction Profile"); - } else if (track_info->descriptor_object_typeID == 5) { - fprintf(stdout, " MPEG-4 AAC High Efficiency/HE Profile"); - } else if (track_info->descriptor_object_typeID == 6) { - fprintf(stdout, " MPEG-4 AAC Scalable Profile"); - } else if (track_info->descriptor_object_typeID == 7) { - fprintf(stdout, " MPEG-4 AAC Transform domain Weighted INterleave Vector Quantization/TwinVQ Profile"); - } else if (track_info->descriptor_object_typeID == 8) { - fprintf(stdout, " MPEG-4 AAC Code Excited Linear Predictive/CELP Profile"); - } else if (track_info->descriptor_object_typeID == 9) { - fprintf(stdout, " MPEG-4 AAC HVXC Profile"); - - } else if (track_info->descriptor_object_typeID == 12) { - fprintf(stdout, " MPEG-4 AAC TTSI Profile"); - } else if (track_info->descriptor_object_typeID == 13) { - fprintf(stdout, " MPEG-4 AAC Main Synthesis Profile"); - } else if (track_info->descriptor_object_typeID == 14) { - fprintf(stdout, " MPEG-4 AAC Wavetable Synthesis Profile"); - } else if (track_info->descriptor_object_typeID == 15) { - fprintf(stdout, " MPEG-4 AAC General MIDI Profile"); - } else if (track_info->descriptor_object_typeID == 16) { - fprintf(stdout, " MPEG-4 AAC Algorithmic Synthesis & Audio FX Profile"); - } else if (track_info->descriptor_object_typeID == 17) { - fprintf(stdout, " MPEG-4 AAC AAC Low Complexity/LC (+error recovery) Profile"); - - } else if (track_info->descriptor_object_typeID == 19) { - fprintf(stdout, " MPEG-4 AAC Long Term Prediction (+error recovery) Profile"); - } else if (track_info->descriptor_object_typeID == 20) { - fprintf(stdout, " MPEG-4 AAC Scalable (+error recovery) Profile"); - } else if (track_info->descriptor_object_typeID == 21) { - fprintf(stdout, " MPEG-4 AAC Transform domain Weighted INterleave Vector Quantization/TwinVQ (+error recovery) Profile"); - } else if (track_info->descriptor_object_typeID == 22) { - fprintf(stdout, " MPEG-4 AAC Bit Sliced Arithmetic Coding/BSAC (+error recovery) Profile"); - } else if (track_info->descriptor_object_typeID == 23) { - fprintf(stdout, " MPEG-4 AAC Low Delay/LD (+error recovery) Profile"); - } else if (track_info->descriptor_object_typeID == 24) { - fprintf(stdout, " MPEG-4 AAC Code Excited Linear Predictive/CELP (+error recovery) Profile"); - } else if (track_info->descriptor_object_typeID == 25) { - fprintf(stdout, " MPEG-4 AAC HXVC (+error recovery) Profile"); - } else if (track_info->descriptor_object_typeID == 26) { - fprintf(stdout, " MPEG-4 AAC Harmonic and Individual Lines plus Noise/HILN (+error recovery) Profile"); - } else if (track_info->descriptor_object_typeID == 27) { - fprintf(stdout, " MPEG-4 AAC Parametric (+error recovery) Profile"); - - } else if (track_info->descriptor_object_typeID == 31) { - fprintf(stdout, " MPEG-4 ALS Audio Lossless Coding"); //I think that mp4alsRM18 writes the channels wrong after objectedID: 0xF880 has 0 channels; 0xF890 is 2ch - } else { - fprintf(stdout, " MPEG-4 Unknown profile: 0x%X", track_info->descriptor_object_typeID); - } - return; +void APar_ShowMPEG4AACProfileInfo(TrackInfo *track_info) { + if (track_info->descriptor_object_typeID == 1) { + fprintf(stdout, " MPEG-4 AAC Main Profile"); + } else if (track_info->descriptor_object_typeID == 2) { + fprintf( + stdout, + " MPEG-4 AAC Low Complexity/LC Profile"); // most files will land here + } else if (track_info->descriptor_object_typeID == 3) { + fprintf(stdout, " MPEG-4 AAC Scaleable Sample Rate/SSR Profile"); + } else if (track_info->descriptor_object_typeID == 4) { + fprintf(stdout, " MPEG-4 AAC Long Term Prediction Profile"); + } else if (track_info->descriptor_object_typeID == 5) { + fprintf(stdout, " MPEG-4 AAC High Efficiency/HE Profile"); + } else if (track_info->descriptor_object_typeID == 6) { + fprintf(stdout, " MPEG-4 AAC Scalable Profile"); + } else if (track_info->descriptor_object_typeID == 7) { + fprintf(stdout, + " MPEG-4 AAC Transform domain Weighted INterleave Vector " + "Quantization/TwinVQ Profile"); + } else if (track_info->descriptor_object_typeID == 8) { + fprintf(stdout, " MPEG-4 AAC Code Excited Linear Predictive/CELP Profile"); + } else if (track_info->descriptor_object_typeID == 9) { + fprintf(stdout, " MPEG-4 AAC HVXC Profile"); + + } else if (track_info->descriptor_object_typeID == 12) { + fprintf(stdout, " MPEG-4 AAC TTSI Profile"); + } else if (track_info->descriptor_object_typeID == 13) { + fprintf(stdout, " MPEG-4 AAC Main Synthesis Profile"); + } else if (track_info->descriptor_object_typeID == 14) { + fprintf(stdout, " MPEG-4 AAC Wavetable Synthesis Profile"); + } else if (track_info->descriptor_object_typeID == 15) { + fprintf(stdout, " MPEG-4 AAC General MIDI Profile"); + } else if (track_info->descriptor_object_typeID == 16) { + fprintf(stdout, " MPEG-4 AAC Algorithmic Synthesis & Audio FX Profile"); + } else if (track_info->descriptor_object_typeID == 17) { + fprintf(stdout, + " MPEG-4 AAC AAC Low Complexity/LC (+error recovery) Profile"); + + } else if (track_info->descriptor_object_typeID == 19) { + fprintf(stdout, + " MPEG-4 AAC Long Term Prediction (+error recovery) Profile"); + } else if (track_info->descriptor_object_typeID == 20) { + fprintf(stdout, " MPEG-4 AAC Scalable (+error recovery) Profile"); + } else if (track_info->descriptor_object_typeID == 21) { + fprintf(stdout, + " MPEG-4 AAC Transform domain Weighted INterleave Vector " + "Quantization/TwinVQ (+error recovery) Profile"); + } else if (track_info->descriptor_object_typeID == 22) { + fprintf(stdout, + " MPEG-4 AAC Bit Sliced Arithmetic Coding/BSAC (+error " + "recovery) Profile"); + } else if (track_info->descriptor_object_typeID == 23) { + fprintf(stdout, " MPEG-4 AAC Low Delay/LD (+error recovery) Profile"); + } else if (track_info->descriptor_object_typeID == 24) { + fprintf(stdout, + " MPEG-4 AAC Code Excited Linear Predictive/CELP (+error " + "recovery) Profile"); + } else if (track_info->descriptor_object_typeID == 25) { + fprintf(stdout, " MPEG-4 AAC HXVC (+error recovery) Profile"); + } else if (track_info->descriptor_object_typeID == 26) { + fprintf(stdout, + " MPEG-4 AAC Harmonic and Individual Lines plus " + "Noise/HILN (+error recovery) Profile"); + } else if (track_info->descriptor_object_typeID == 27) { + fprintf(stdout, " MPEG-4 AAC Parametric (+error recovery) Profile"); + + } else if (track_info->descriptor_object_typeID == 31) { + fprintf( + stdout, + " MPEG-4 ALS Audio Lossless Coding"); // I think that mp4alsRM18 writes + // the channels wrong after + // objectedID: 0xF880 has 0 + // channels; 0xF890 is 2ch + } else { + fprintf(stdout, + " MPEG-4 Unknown profile: 0x%X", + track_info->descriptor_object_typeID); + } + return; } /*---------------------- APar_ShowObjectProfileInfo - track_type - broadly used to determine what types of information (like channels or avc1 profiles) to display - track_info - a pointer to the struct holding all the information gathered as a single 'trak' atom was traversed - - Based on the ObjectTypeIndication in 'esds', show the type of track. For mpeg-4 audio & mpeg-4 visual are handled in a subroutine because there are so many - enumerations. avc1 contains 'avcC' which supports a different mechanism. + track_type - broadly used to determine what types of information (like +channels or avc1 profiles) to display track_info - a pointer to the struct +holding all the information gathered as a single 'trak' atom was traversed + + Based on the ObjectTypeIndication in 'esds', show the type of track. For +mpeg-4 audio & mpeg-4 visual are handled in a subroutine because there are so +many enumerations. avc1 contains 'avcC' which supports a different mechanism. ----------------------*/ -void APar_ShowObjectProfileInfo(uint8_t track_type, TrackInfo* track_info) { - if (track_info->contains_esds) { - switch (track_info->ObjectTypeIndication) { - //0x00 es Lambada/Verboten/Forbidden - case 0x01: - case 0x02: { - fprintf(stdout, " MPEG-4 Systems (BIFS/ObjDesc)"); - break; - } - case 0x03: { - fprintf(stdout, " Interaction Stream"); - break; - } - case 0x04: { - fprintf(stdout, " MPEG-4 Systems Extended BIFS"); - break; - } - case 0x05: { - fprintf(stdout, " MPEG-4 Systems AFX"); - break; - } - case 0x06: { - fprintf(stdout, " Font Data Stream"); - break; - } - case 0x08: { - fprintf(stdout, " Synthesized Texture Stream"); - break; - } - case 0x07: { - fprintf(stdout, " Streaming Text Stream"); - break; - } - //0x09-0x1F reserved - case 0x20: { - APar_ShowMPEG4VisualProfileInfo(track_info); - break; - } - - case 0x40: { //vererable mpeg-4 aac - APar_ShowMPEG4AACProfileInfo(track_info); - break; - } - - //0x41-0x5F reserved - case 0x60: { - fprintf(stdout, " MPEG-2 Visual Simple Profile"); //'Visual ISO/IEC 13818-2 Simple Profile' - break; - } - case 0x61: { - fprintf(stdout, " MPEG-2 Visual Main Profile"); //'Visual ISO/IEC 13818-2 Main Profile' - break; - } - case 0x62: { - fprintf(stdout, " MPEG-2 Visual SNR Profile"); //'Visual ISO/IEC 13818-2 SNR Profile' - break; - } - case 0x63: { - fprintf(stdout, " MPEG-2 Visual Spatial Profile"); //'Visual ISO/IEC 13818-2 Spatial Profile' - break; - } - case 0x64: { - fprintf(stdout, " MPEG-2 Visual High Profile"); //'Visual ISO/IEC 13818-2 High Profile' - break; - } - case 0x65: { - fprintf(stdout, " MPEG-2 Visual 4:2:2 Profile"); //'Visual ISO/IEC 13818-2 422 Profile' - break; - } - case 0x66: { - fprintf(stdout, " MPEG-2 AAC Main Profile"); //'Audio ISO/IEC 13818-7 Main Profile' - break; - } - case 0x67: { - fprintf(stdout, " MPEG-2 AAC Low Complexity Profile"); //Audio ISO/IEC 13818-7 LowComplexity Profile - break; - } - case 0x68: { - fprintf(stdout, " MPEG-2 AAC Scaleable Sample Rate Profile"); //'Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile' - break; - } - case 0x69: { - fprintf(stdout, " MPEG-2 Audio"); //'Audio ISO/IEC 13818-3' - break; - } - case 0x6A: { - fprintf(stdout, " MPEG-1 Visual"); //'Visual ISO/IEC 11172-2' - break; - } - case 0x6B: { - fprintf(stdout, " MPEG-1 Audio"); //'Audio ISO/IEC 11172-3' - break; - } - case 0x6C: { - fprintf(stdout, " JPEG"); //'Visual ISO/IEC 10918-1' - break; - } - case 0x6D: { - fprintf(stdout, " PNG"); //http://www.mp4ra.org/object.html - break; - } - case 0x6E: { - fprintf(stdout, " JPEG2000"); //'Visual ISO/IEC 15444-1' - break; - } - case 0xA0: { - fprintf(stdout, " 3GPP2 EVRC Voice"); //http://www.mp4ra.org/object.html - break; - } - case 0xA1: { - fprintf(stdout, " 3GPP2 SMV Voice"); //http://www.mp4ra.org/object.html - break; - } - case 0xA2: { - fprintf(stdout, " 3GPP2 Compact Multimedia Format"); //http://www.mp4ra.org/object.html - break; - } - - //0xC0-0xE0 user private - case 0xE1: { - fprintf(stdout, " 3GPP2 QCELP (14K Voice)"); //http://www.mp4ra.org/object.html - break; - } - //0xE2-0xFE user private - //0xFF no object type specified - - default: { - //so many profiles, so little desire to list them all (in 14496-2 which I don't have) - if(movie_info.contains_iods && iods_info.audio_profile == 0xFE) { - fprintf(stdout, " Private user object: 0x%X", track_info->ObjectTypeIndication); - } else { - fprintf(stdout, " Object Type Indicator: 0x%X Description Ojbect Type ID: 0x%X\n", track_info->ObjectTypeIndication, track_info->descriptor_object_typeID); - } - break; - } - } - - } else if (track_type == AVC1_TRACK) { - //profiles & levels are in the 14496-10 pdf (which I don't have access to), so... - //http://lists.mpegif.org/pipermail/mp4-tech/2006-January/006255.html - //http://iphome.hhi.de/suehring/tml/doc/lenc/html/configfile_8c-source.html - //66=baseline, 77=main, 88=extended; 100=High, 110=High 10, 122=High 4:2:2, 144=High 4:4:4 - - switch(track_info->profile) { - case 66: { - fprintf(stdout, " AVC Baseline Profile"); - break; - } - case 77: { - fprintf(stdout, " AVC Main Profile"); - break; - } - case 88: { - fprintf(stdout, " AVC Extended Profile"); - break; - } - case 100: { - fprintf(stdout, " AVC High Profile"); - break; - } - case 110: { - fprintf(stdout, " AVC High 10 Profile"); - break; - } - case 122: { - fprintf(stdout, " AVC High 4:2:2 Profile"); - break; - } - case 144: { - fprintf(stdout, " AVC High 4:4:4 Profile"); - break; - } - default: { - fprintf(stdout, " Unknown Profile: %u", track_info->profile); - break; - } - } //end profile switch - - //Don't have access to levels either, but working off of: - //http://iphome.hhi.de/suehring/tml/doc/lenc/html/configfile_8c-source.html - - //and the 15 levels it says here: http://www.chiariglione.org/mpeg/technologies/mp04-avc/index.htm (1b in http://en.wikipedia.org/wiki/H.264 seems nonsensical) - //working backwards, we get... a simple 2 digit number (with '20' just drop the 0; with 21, put in a decimal) - if (track_info->level > 0) { - switch (track_info->level) { - case 10: - case 20: - case 30: - case 40: - case 50: { - fprintf(stdout, ", Level %u", track_info->level / 10); - break; - } - case 11: - case 12: - case 13: - case 21: - case 22: - case 31: - case 32: - case 41: - case 42: - case 51: { - fprintf(stdout, ", Level %u.%u", track_info->level / 10, track_info->level % 10); - break; - } - default: { - fprintf(stdout, ", Unknown level %u.%u", track_info->level / 10, track_info->level % 10); - } - - } //end switch - } //end level if - } else if (track_type == S_AMR_TRACK) { - char* amr_modes = (char*)calloc(1, sizeof(char)*500); - if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762) { - if (track_info->amr_modes & 0x0001) mem_append("0", amr_modes); - if (track_info->amr_modes & 0x0002) mem_append("1", amr_modes); - if (track_info->amr_modes & 0x0004) mem_append("2", amr_modes); - if (track_info->amr_modes & 0x0008) mem_append("3", amr_modes); - if (track_info->amr_modes & 0x0010) mem_append("4", amr_modes); - if (track_info->amr_modes & 0x0020) mem_append("5", amr_modes); - if (track_info->amr_modes & 0x0040) mem_append("6", amr_modes); - if (track_info->amr_modes & 0x0080) mem_append("7", amr_modes); - if (track_info->amr_modes & 0x0100) mem_append("8", amr_modes); - if (strlen(amr_modes) == 0) memcpy(amr_modes, "none", 4); - } else if (track_info->track_codec == 0x73766D72) { - if (track_info->amr_modes & 0x0001) mem_append("VMR-WB Mode 0, ", amr_modes); - if (track_info->amr_modes & 0x0002) mem_append("VMR-WB Mode 1, ", amr_modes); - if (track_info->amr_modes & 0x0004) mem_append("VMR-WB Mode 2, ", amr_modes); - if (track_info->amr_modes & 0x0008) mem_append("VMR-WB Mode 3 (AMR-WB interoperable mode), ", amr_modes); - if (track_info->amr_modes & 0x0010) mem_append("VMR-WB Mode 4, ", amr_modes); - if (track_info->amr_modes & 0x0020) mem_append("VMR-WB Mode 2 with maximum half-rate, ", amr_modes); - if (track_info->amr_modes & 0x0040) mem_append("VMR-WB Mode 4 with maximum half-rate, ", amr_modes); - uint16_t amr_modes_len = strlen(amr_modes); - if (amr_modes_len > 0) memset(amr_modes+(amr_modes_len-1), 0, 2); - } - - if (track_info->track_codec == 0x73616D72) { //samr - fprintf(stdout, " AMR Narrow-Band. Modes: %s. Encoder vendor code: %s\n", amr_modes, track_info->encoder_name); - } else if (track_info->track_codec == 0x73617762) { //sawb - fprintf(stdout, " AMR Wide-Band. Modes: %s. Encoder vendor code: %s\n", amr_modes, track_info->encoder_name); - } else if (track_info->track_codec == 0x73617770) { //sawp - fprintf(stdout, " AMR Wide-Band WB+. Encoder vendor code: %s\n", track_info->encoder_name); - } else if (track_info->track_codec == 0x73766D72) { //svmr - fprintf(stdout, " AMR VBR Wide-Band. Encoder vendor code: %s\n", track_info->encoder_name); - } - free(amr_modes); amr_modes=NULL; - - } else if (track_type == EVRC_TRACK) { - fprintf(stdout, " EVRC (Enhanced Variable Rate Coder). Encoder vendor code: %s\n", track_info->encoder_name); - - } else if (track_type == QCELP_TRACK) { - fprintf(stdout, " QCELP (Qualcomm Code Excited Linear Prediction). Encoder vendor code: %s\n", track_info->encoder_name); - - } else if (track_type == S263_TRACK) { - if (track_info->profile == 0) { - fprintf(stdout, " H.263 Baseline Profile, Level %u. Encoder vendor code: %s", track_info->level, track_info->encoder_name); - } else { - fprintf(stdout, " H.263 Profile: %u, Level %u. Encoder vendor code: %s", track_info->profile, track_info->level, track_info->encoder_name); - } - } - if (track_type == AUDIO_TRACK) { - if (track_info->section5_length == 0) { - fprintf(stdout, " channels: (%u)\n", track_info->channels ); - } else { - fprintf(stdout, " channels: [%u]\n", track_info->channels ); - } - } - return; +void APar_ShowObjectProfileInfo(uint8_t track_type, TrackInfo *track_info) { + if (track_info->contains_esds) { + switch (track_info->ObjectTypeIndication) { + // 0x00 es Lambada/Verboten/Forbidden + case 0x01: + case 0x02: { + fprintf(stdout, " MPEG-4 Systems (BIFS/ObjDesc)"); + break; + } + case 0x03: { + fprintf(stdout, " Interaction Stream"); + break; + } + case 0x04: { + fprintf(stdout, " MPEG-4 Systems Extended BIFS"); + break; + } + case 0x05: { + fprintf(stdout, " MPEG-4 Systems AFX"); + break; + } + case 0x06: { + fprintf(stdout, " Font Data Stream"); + break; + } + case 0x08: { + fprintf(stdout, " Synthesized Texture Stream"); + break; + } + case 0x07: { + fprintf(stdout, " Streaming Text Stream"); + break; + } + // 0x09-0x1F reserved + case 0x20: { + APar_ShowMPEG4VisualProfileInfo(track_info); + break; + } + + case 0x40: { // vererable mpeg-4 aac + APar_ShowMPEG4AACProfileInfo(track_info); + break; + } + + // 0x41-0x5F reserved + case 0x60: { + fprintf(stdout, + " MPEG-2 Visual Simple Profile"); //'Visual ISO/IEC 13818-2 + // Simple Profile' + break; + } + case 0x61: { + fprintf(stdout, " MPEG-2 Visual Main Profile"); //'Visual ISO/IEC 13818-2 + // Main Profile' + break; + } + case 0x62: { + fprintf( + stdout, + " MPEG-2 Visual SNR Profile"); //'Visual ISO/IEC 13818-2 SNR Profile' + break; + } + case 0x63: { + fprintf(stdout, + " MPEG-2 Visual Spatial Profile"); //'Visual ISO/IEC 13818-2 + // Spatial Profile' + break; + } + case 0x64: { + fprintf(stdout, " MPEG-2 Visual High Profile"); //'Visual ISO/IEC 13818-2 + // High Profile' + break; + } + case 0x65: { + fprintf(stdout, " MPEG-2 Visual 4:2:2 Profile"); //'Visual ISO/IEC + // 13818-2 422 Profile' + break; + } + case 0x66: { + fprintf( + stdout, + " MPEG-2 AAC Main Profile"); //'Audio ISO/IEC 13818-7 Main Profile' + break; + } + case 0x67: { + fprintf(stdout, + " MPEG-2 AAC Low Complexity Profile"); // Audio ISO/IEC 13818-7 + // LowComplexity Profile + break; + } + case 0x68: { + fprintf( + stdout, + " MPEG-2 AAC Scaleable Sample Rate Profile"); //'Audio ISO/IEC + // 13818-7 Scaleable + // Sampling Rate + // Profile' + break; + } + case 0x69: { + fprintf(stdout, " MPEG-2 Audio"); //'Audio ISO/IEC 13818-3' + break; + } + case 0x6A: { + fprintf(stdout, " MPEG-1 Visual"); //'Visual ISO/IEC 11172-2' + break; + } + case 0x6B: { + fprintf(stdout, " MPEG-1 Audio"); //'Audio ISO/IEC 11172-3' + break; + } + case 0x6C: { + fprintf(stdout, " JPEG"); //'Visual ISO/IEC 10918-1' + break; + } + case 0x6D: { + fprintf(stdout, " PNG"); // http://www.mp4ra.org/object.html + break; + } + case 0x6E: { + fprintf(stdout, " JPEG2000"); //'Visual ISO/IEC 15444-1' + break; + } + case 0xA0: { + fprintf(stdout, " 3GPP2 EVRC Voice"); // http://www.mp4ra.org/object.html + break; + } + case 0xA1: { + fprintf(stdout, " 3GPP2 SMV Voice"); // http://www.mp4ra.org/object.html + break; + } + case 0xA2: { + fprintf( + stdout, + " 3GPP2 Compact Multimedia Format"); // http://www.mp4ra.org/object.html + break; + } + + // 0xC0-0xE0 user private + case 0xE1: { + fprintf(stdout, + " 3GPP2 QCELP (14K Voice)"); // http://www.mp4ra.org/object.html + break; + } + // 0xE2-0xFE user private + // 0xFF no object type specified + + default: { + // so many profiles, so little desire to list them all (in 14496-2 which I + // don't have) + if (movie_info.contains_iods && iods_info.audio_profile == 0xFE) { + fprintf(stdout, + " Private user object: 0x%X", + track_info->ObjectTypeIndication); + } else { + fprintf( + stdout, + " Object Type Indicator: 0x%X Description Ojbect Type ID: 0x%X\n", + track_info->ObjectTypeIndication, + track_info->descriptor_object_typeID); + } + break; + } + } + + } else if (track_type == AVC1_TRACK) { + // profiles & levels are in the 14496-10 pdf (which I don't have access to), + // so... http://lists.mpegif.org/pipermail/mp4-tech/2006-January/006255.html + // http://iphome.hhi.de/suehring/tml/doc/lenc/html/configfile_8c-source.html + // 66=baseline, 77=main, 88=extended; 100=High, 110=High 10, 122=High 4:2:2, + // 144=High 4:4:4 + + switch (track_info->profile) { + case 66: { + fprintf(stdout, " AVC Baseline Profile"); + break; + } + case 77: { + fprintf(stdout, " AVC Main Profile"); + break; + } + case 88: { + fprintf(stdout, " AVC Extended Profile"); + break; + } + case 100: { + fprintf(stdout, " AVC High Profile"); + break; + } + case 110: { + fprintf(stdout, " AVC High 10 Profile"); + break; + } + case 122: { + fprintf(stdout, " AVC High 4:2:2 Profile"); + break; + } + case 144: { + fprintf(stdout, " AVC High 4:4:4 Profile"); + break; + } + default: { + fprintf(stdout, " Unknown Profile: %u", track_info->profile); + break; + } + } // end profile switch + + // Don't have access to levels either, but working off of: + // http://iphome.hhi.de/suehring/tml/doc/lenc/html/configfile_8c-source.html + + // and the 15 levels it says here: + // http://www.chiariglione.org/mpeg/technologies/mp04-avc/index.htm (1b in + // http://en.wikipedia.org/wiki/H.264 seems nonsensical) working backwards, + // we get... a simple 2 digit number (with '20' just drop the 0; with 21, + // put in a decimal) + if (track_info->level > 0) { + switch (track_info->level) { + case 10: + case 20: + case 30: + case 40: + case 50: { + fprintf(stdout, ", Level %u", track_info->level / 10); + break; + } + case 11: + case 12: + case 13: + case 21: + case 22: + case 31: + case 32: + case 41: + case 42: + case 51: { + fprintf(stdout, + ", Level %u.%u", + track_info->level / 10, + track_info->level % 10); + break; + } + default: { + fprintf(stdout, + ", Unknown level %u.%u", + track_info->level / 10, + track_info->level % 10); + } + + } // end switch + } // end level if + } else if (track_type == S_AMR_TRACK) { + char amr_modes[500] = {}; + if (track_info->track_codec == 0x73616D72 || + track_info->track_codec == 0x73617762) { + if (track_info->amr_modes & 0x0001) + mem_append("0", amr_modes); + if (track_info->amr_modes & 0x0002) + mem_append("1", amr_modes); + if (track_info->amr_modes & 0x0004) + mem_append("2", amr_modes); + if (track_info->amr_modes & 0x0008) + mem_append("3", amr_modes); + if (track_info->amr_modes & 0x0010) + mem_append("4", amr_modes); + if (track_info->amr_modes & 0x0020) + mem_append("5", amr_modes); + if (track_info->amr_modes & 0x0040) + mem_append("6", amr_modes); + if (track_info->amr_modes & 0x0080) + mem_append("7", amr_modes); + if (track_info->amr_modes & 0x0100) + mem_append("8", amr_modes); + if (strlen(amr_modes) == 0) + memcpy(amr_modes, "none", 4); + } else if (track_info->track_codec == 0x73766D72) { + if (track_info->amr_modes & 0x0001) + mem_append("VMR-WB Mode 0, ", amr_modes); + if (track_info->amr_modes & 0x0002) + mem_append("VMR-WB Mode 1, ", amr_modes); + if (track_info->amr_modes & 0x0004) + mem_append("VMR-WB Mode 2, ", amr_modes); + if (track_info->amr_modes & 0x0008) + mem_append("VMR-WB Mode 3 (AMR-WB interoperable mode), ", amr_modes); + if (track_info->amr_modes & 0x0010) + mem_append("VMR-WB Mode 4, ", amr_modes); + if (track_info->amr_modes & 0x0020) + mem_append("VMR-WB Mode 2 with maximum half-rate, ", amr_modes); + if (track_info->amr_modes & 0x0040) + mem_append("VMR-WB Mode 4 with maximum half-rate, ", amr_modes); + uint16_t amr_modes_len = strlen(amr_modes); + if (amr_modes_len > 0) + memset(amr_modes + (amr_modes_len - 1), 0, 2); + } + + if (track_info->track_codec == 0x73616D72) { // samr + fprintf(stdout, + " AMR Narrow-Band. Modes: %s. Encoder vendor code: %s\n", + amr_modes, + track_info->encoder_name); + } else if (track_info->track_codec == 0x73617762) { // sawb + fprintf(stdout, + " AMR Wide-Band. Modes: %s. Encoder vendor code: %s\n", + amr_modes, + track_info->encoder_name); + } else if (track_info->track_codec == 0x73617770) { // sawp + fprintf(stdout, + " AMR Wide-Band WB+. Encoder vendor code: %s\n", + track_info->encoder_name); + } else if (track_info->track_codec == 0x73766D72) { // svmr + fprintf(stdout, + " AMR VBR Wide-Band. Encoder vendor code: %s\n", + track_info->encoder_name); + } + } else if (track_type == EVRC_TRACK) { + fprintf(stdout, + " EVRC (Enhanced Variable Rate Coder). Encoder vendor code: %s\n", + track_info->encoder_name); + + } else if (track_type == QCELP_TRACK) { + fprintf(stdout, + " QCELP (Qualcomm Code Excited Linear Prediction). Encoder vendor " + "code: %s\n", + track_info->encoder_name); + + } else if (track_type == S263_TRACK) { + if (track_info->profile == 0) { + fprintf(stdout, + " H.263 Baseline Profile, Level %u. Encoder vendor code: %s", + track_info->level, + track_info->encoder_name); + } else { + fprintf(stdout, + " H.263 Profile: %u, Level %u. Encoder vendor code: %s", + track_info->profile, + track_info->level, + track_info->encoder_name); + } + } + if (track_type == AUDIO_TRACK) { + if (track_info->section5_length == 0) { + fprintf(stdout, " channels: (%u)\n", track_info->channels); + } else { + fprintf(stdout, " channels: [%u]\n", track_info->channels); + } + } } /////////////////////////////////////////////////////////////////////////////////////// -// Movie & Track Level Info // +// Movie & Track Level Info // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- calcuate_sample_size - uint32_buffer - a buffer to read bytes in from the file - isofile - the file to be scanned - stsz_atom - the atom number of the stsz atom - - This will get aggregate a number of the size of all chunks in the track. The stsz atom holds a table of these sizes along with a count of how many there are. - Loop through the count, summing in the sizes. This is called called for all tracks, but used only when a hardcoded bitrate (in esds) isn't found (like avc1) - and is displayed with the asterisk* at track-level. + uint32_buffer - a buffer to read bytes in from the file + isofile - the file to be scanned + stsz_atom - the atom number of the stsz atom + + This will get aggregate a number of the size of all chunks in the track. The +stsz atom holds a table of these sizes along with a count of how many there are. + Loop through the count, summing in the sizes. This is called +called for all tracks, but used only when a hardcoded bitrate (in esds) isn't +found (like avc1) and is displayed with the asterisk* at track-level. ----------------------*/ -uint64_t calcuate_sample_size(char* uint32_buffer, FILE* isofile, short stsz_atom) { - uint32_t sample_size = 0; - uint32_t sample_count = 0; - uint64_t total_size = 0; - - sample_size = APar_read32(uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + 12); - sample_count = APar_read32(uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + 16); - - if (sample_size == 0) { - for (uint64_t atom_offset = 20; atom_offset < parsedAtoms[stsz_atom].AtomicLength; atom_offset +=4) { - total_size += APar_read32(uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + atom_offset); - } - } else { - total_size = sample_size * sample_count; - } - return total_size; +uint64_t +calcuate_sample_size(char *uint32_buffer, FILE *isofile, short stsz_atom) { + uint32_t sample_size = 0; + uint32_t sample_count = 0; + uint64_t total_size = 0; + + sample_size = APar_read32( + uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + 12); + sample_count = APar_read32( + uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + 16); + + if (sample_size == 0) { + for (uint64_t atom_offset = 20; + atom_offset < parsedAtoms[stsz_atom].AtomicLength; + atom_offset += 4) { + total_size += + APar_read32(uint32_buffer, + isofile, + parsedAtoms[stsz_atom].AtomicStart + atom_offset); + } + } else { + total_size = sample_size * sample_count; + } + return total_size; } /*---------------------- APar_TrackLevelInfo - track - pointer to a struct providing the track we are looking for - track_search_atom_name - the name of the atom to be found in this track + track - pointer to a struct providing the track we are looking for + track_search_atom_name - the name of the atom to be found in this track - Looping through the atoms one by one, note a 'trak' atom. If we are looking for the total amount of tracks (by setting the track_num to 0), simply return the - count of tracks back in the same struct to that later functions can loop through each track individually, looking for a specific atom. - If track's track_num is a non-zero number, then find that atom that *matches* the atom name. Set track's track_atom to that atom for later use + Looping through the atoms one by one, note a 'trak' atom. If we are looking +for the total amount of tracks (by setting the track_num to 0), simply return +the count of tracks back in the same struct to that later functions can loop +through each track individually, looking for a specific atom. If track's +track_num is a non-zero number, then find that atom that *matches* the atom +name. Set track's track_atom to that atom for later use ----------------------*/ -void APar_TrackLevelInfo(Trackage* track, const char* track_search_atom_name) { - uint8_t track_tally = 0; - short iter = 0; - - while (parsedAtoms[iter].NextAtomNumber != 0) { - - if ( strncmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0) { - track_tally += 1; - if (track->track_num == 0) { - track->total_tracks += 1; - - } else if (track->track_num == track_tally) { - - short next_atom = parsedAtoms[iter].NextAtomNumber; - while (parsedAtoms[next_atom].AtomicLevel > parsedAtoms[iter].AtomicLevel) { - - if (strncmp(parsedAtoms[next_atom].AtomicName, track_search_atom_name, 4) == 0) { - - track->track_atom = parsedAtoms[next_atom].AtomicNumber; - return; - } else { - next_atom = parsedAtoms[next_atom].NextAtomNumber; - } - if (parsedAtoms[next_atom].AtomicLevel == parsedAtoms[iter].AtomicLevel) { - track->track_atom = 0; - } - } - } - } - iter=parsedAtoms[iter].NextAtomNumber; - } - return; +void APar_TrackLevelInfo(Trackage *track, const char *track_search_atom_name) { + uint8_t track_tally = 0; + short iter = 0; + + while (parsedAtoms[iter].NextAtomNumber != 0) { + + if (strncmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0) { + track_tally += 1; + if (track->track_num == 0) { + track->total_tracks += 1; + + } else if (track->track_num == track_tally) { + + short next_atom = parsedAtoms[iter].NextAtomNumber; + while (parsedAtoms[next_atom].AtomicLevel > + parsedAtoms[iter].AtomicLevel) { + + if (strncmp(parsedAtoms[next_atom].AtomicName, + track_search_atom_name, + 4) == 0) { + + track->track_atom = parsedAtoms[next_atom].AtomicNumber; + return; + } else { + next_atom = parsedAtoms[next_atom].NextAtomNumber; + } + if (parsedAtoms[next_atom].AtomicLevel == + parsedAtoms[iter].AtomicLevel) { + track->track_atom = 0; + } + } + } + } + iter = parsedAtoms[iter].NextAtomNumber; + } + return; } /*---------------------- APar_ExtractChannelInfo - isofile - the file to be scanned - pos - the position within the file that carries the channel info (in esds) + isofile - the file to be scanned + pos - the position within the file that carries the channel info (in +esds) - The channel info in esds is bitpacked, so read it in isolation and shift the bits around to get at it + The channel info in esds is bitpacked, so read it in isolation and shift the +bits around to get at it ----------------------*/ -uint8_t APar_ExtractChannelInfo(FILE* isofile, uint32_t pos) { - uint8_t packed_channels = APar_read8(isofile, pos); - uint8_t unpacked_channels = (packed_channels << 1); //just shift the first bit off the table - unpacked_channels = (unpacked_channels >> 4); //and slide it on over back on the uint8_t - return unpacked_channels; +uint8_t APar_ExtractChannelInfo(FILE *isofile, uint32_t pos) { + uint8_t packed_channels = APar_read8(isofile, pos); + uint8_t unpacked_channels = + (packed_channels << 1); // just shift the first bit off the table + unpacked_channels = + (unpacked_channels >> 4); // and slide it on over back on the uint8_t + return unpacked_channels; } /*---------------------- APar_Extract_iods_Info - isofile - the file to be scanned - iods_atom - a pointer to the struct that will store the profile levels found in iods - - 'iods' info mostly comes from: http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt Just as 'esds' has 'filler' bytes to skip over, so does this. - Mercifully, the profiles come one right after another. The only problem is that in many files, the iods profiles don't match the esds profiles. This is resolved - by ignoring it for audio (mostly, unless is 0xFE user defined). For MPEG-4 Visual, it is preferred over 'esds' (occurs in APar_ShowMPEG4VisualProfileInfo); for - all other video types it is ignored. + isofile - the file to be scanned + iods_atom - a pointer to the struct that will store the profile levels +found in iods + + 'iods' info mostly comes from: +http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt Just as +'esds' has 'filler' bytes to skip over, so does this. Mercifully, the profiles +come one right after another. The only problem is that in many files, the iods +profiles don't match the esds profiles. This is resolved by ignoring it for +audio (mostly, unless is 0xFE user defined). For MPEG-4 Visual, it is preferred +over 'esds' (occurs in APar_ShowMPEG4VisualProfileInfo); for all other video +types it is ignored. ----------------------*/ -void APar_Extract_iods_Info(FILE* isofile, AtomicInfo* iods_atom) { - uint64_t iods_offset = iods_atom->AtomicStart+8; - if (iods_atom->AtomicVerFlags == 0 && APar_read8(isofile, iods_offset+4) == 0x10) { - iods_offset+=5; - iods_offset += APar_skip_filler(isofile, iods_offset); - uint8_t iods_objdescrip_len = APar_read8(isofile, iods_offset); - iods_offset++; - if (iods_objdescrip_len >= 7) { - iods_info.od_profile_level = APar_read8(isofile, iods_offset+2); - iods_info.scene_profile_level = APar_read8(isofile, iods_offset+3); - iods_info.audio_profile = APar_read8(isofile, iods_offset+4); - iods_info.video_profile_level = APar_read8(isofile, iods_offset+5); - iods_info.graphics_profile_level = APar_read8(isofile, iods_offset+6); - } - } - return; +void APar_Extract_iods_Info(FILE *isofile, AtomicInfo *iods_atom) { + uint64_t iods_offset = iods_atom->AtomicStart + 8; + if (iods_atom->AtomicVerFlags == 0 && + APar_read8(isofile, iods_offset + 4) == 0x10) { + iods_offset += 5; + iods_offset += APar_skip_filler(isofile, iods_offset); + uint8_t iods_objdescrip_len = APar_read8(isofile, iods_offset); + iods_offset++; + if (iods_objdescrip_len >= 7) { + iods_info.od_profile_level = APar_read8(isofile, iods_offset + 2); + iods_info.scene_profile_level = APar_read8(isofile, iods_offset + 3); + iods_info.audio_profile = APar_read8(isofile, iods_offset + 4); + iods_info.video_profile_level = APar_read8(isofile, iods_offset + 5); + iods_info.graphics_profile_level = APar_read8(isofile, iods_offset + 6); + } + } + return; } /*---------------------- APar_Extract_AMR_Info - uint32_buffer - a buffer to read bytes in from the file - isofile - the file to be scanned - track_level_atom - the number of the 'esds' atom in the linked list of parsed atoms - track_info - a pointer to the struct carrying track-level info to be filled with information + uint32_buffer - a buffer to read bytes in from the file + isofile - the file to be scanned + track_level_atom - the number of the 'esds' atom in the linked list of +parsed atoms track_info - a pointer to the struct carrying track-level info to +be filled with information - The only interesting info here is the encoding tool & the amr modes used. ffmpeg's amr output seems to lack some compliance - no damr atom for sawb + The only interesting info here is the encoding tool & the amr modes used. +ffmpeg's amr output seems to lack some compliance - no damr atom for sawb ----------------------*/ -void APar_Extract_AMR_Info(char* uint32_buffer, FILE* isofile, short track_level_atom, TrackInfo* track_info) { - uint32_t amr_specific_offet = 8; - APar_readX(track_info->encoder_name, isofile, parsedAtoms[track_level_atom].AtomicStart + amr_specific_offet, 4); - if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762 || track_info->track_codec == 0x73766D72) { //samr,sawb & svmr contain modes only - track_info->amr_modes = APar_read16(uint32_buffer, isofile, parsedAtoms[track_level_atom].AtomicStart + amr_specific_offet + 4+1); - } - return; +void APar_Extract_AMR_Info(char *uint32_buffer, + FILE *isofile, + short track_level_atom, + TrackInfo *track_info) { + uint32_t amr_specific_offet = 8; + APar_readX(track_info->encoder_name, + isofile, + parsedAtoms[track_level_atom].AtomicStart + amr_specific_offet, + 4); + if (track_info->track_codec == 0x73616D72 || + track_info->track_codec == 0x73617762 || + track_info->track_codec == + 0x73766D72) { // samr,sawb & svmr contain modes only + track_info->amr_modes = APar_read16( + uint32_buffer, + isofile, + parsedAtoms[track_level_atom].AtomicStart + amr_specific_offet + 4 + 1); + } + return; } /*---------------------- APar_Extract_d263_Info - uint32_buffer - a buffer to read bytes in from the file - isofile - the file to be scanned - track_level_atom - the number of the 'esds' atom in the linked list of parsed atoms - track_info - a pointer to the struct carrying track-level info to be filled with information - - 'd263' only holds 4 things; the 3 of interest are gathered here. Its possible that a 'bitr' atom follows 'd263', which would hold bitrates, but isn't parsed here + uint32_buffer - a buffer to read bytes in from the file + isofile - the file to be scanned + track_level_atom - the number of the 'esds' atom in the linked list of +parsed atoms track_info - a pointer to the struct carrying track-level info to +be filled with information + + 'd263' only holds 4 things; the 3 of interest are gathered here. Its +possible that a 'bitr' atom follows 'd263', which would hold bitrates, but isn't +parsed here ----------------------*/ -void APar_Extract_d263_Info(char* uint32_buffer, FILE* isofile, short track_level_atom, TrackInfo* track_info) { - uint64_t offset_into_d263 = 8; - APar_readX(track_info->encoder_name, isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_d263, 4); - track_info->level = APar_read8(isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_d263 + 4+1); - track_info->profile = APar_read8(isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_d263 + 4+2); - //possible 'bitr' bitrate box afterwards - return; +void APar_Extract_d263_Info(char *uint32_buffer, + FILE *isofile, + short track_level_atom, + TrackInfo *track_info) { + uint64_t offset_into_d263 = 8; + APar_readX(track_info->encoder_name, + isofile, + parsedAtoms[track_level_atom].AtomicStart + offset_into_d263, + 4); + track_info->level = APar_read8(isofile, + parsedAtoms[track_level_atom].AtomicStart + + offset_into_d263 + 4 + 1); + track_info->profile = APar_read8(isofile, + parsedAtoms[track_level_atom].AtomicStart + + offset_into_d263 + 4 + 2); + // possible 'bitr' bitrate box afterwards + return; } /*---------------------- APar_Extract_devc_Info - isofile - the file to be scanned - track_level_atom - the number of the 'esds' atom in the linked list of parsed atoms - track_info - a pointer to the struct carrying track-level info to be filled with information + isofile - the file to be scanned + track_level_atom - the number of the 'esds' atom in the linked list of +parsed atoms track_info - a pointer to the struct carrying track-level info to +be filled with information - 'devc' only holds 3 things: encoder vendor, decoder version & frames per sample; only encoder vendor is gathered + 'devc' only holds 3 things: encoder vendor, decoder version & frames per +sample; only encoder vendor is gathered ----------------------*/ -void APar_Extract_devc_Info(FILE* isofile, short track_level_atom, TrackInfo* track_info) { - uint64_t offset_into_devc = 8; - APar_readX(track_info->encoder_name, isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_devc, 4); - return; +void APar_Extract_devc_Info(FILE *isofile, + short track_level_atom, + TrackInfo *track_info) { + uint64_t offset_into_devc = 8; + APar_readX(track_info->encoder_name, + isofile, + parsedAtoms[track_level_atom].AtomicStart + offset_into_devc, + 4); + return; } /*---------------------- APar_Extract_esds_Info - uint32_buffer - a buffer to read bytes in from the file - isofile - the file to be scanned - track_level_atom - the number of the 'esds' atom in the linked list of parsed atoms - track_info - a pointer to the struct carrying track-level info to be filled with information - - 'esds' contains a wealth of information. Memory fails where I figured out how to parse this atom, but this seems like a decent outline in retrospect: - http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt - but its misleading in lots of places too. For those tracks that support 'esds' - (notably not avc1 or alac), this atom comes in at most 4 sections (section 3 to section 6). Each section is numbered (3 is 0x03) followed by an optional - amount of filler (see APar_skip_filler), then the length of the section to the end of the atom or the end of another section. + uint32_buffer - a buffer to read bytes in from the file + isofile - the file to be scanned + track_level_atom - the number of the 'esds' atom in the linked list of +parsed atoms track_info - a pointer to the struct carrying track-level info to +be filled with information + + 'esds' contains a wealth of information. Memory fails where I figured out +how to parse this atom, but this seems like a decent outline in retrospect: + http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt +- but its misleading in lots of places too. For those tracks that support 'esds' + (notably not avc1 or alac), this atom comes in at most 4 +sections (section 3 to section 6). Each section is numbered (3 is 0x03) followed +by an optional amount of filler (see APar_skip_filler), then the length of the +section to the end of the atom or the end of another section. ----------------------*/ -void APar_Extract_esds_Info(char* uint32_buffer, FILE* isofile, short track_level_atom, TrackInfo* track_info) { - uint64_t offset_into_stsd = 0; - - while (offset_into_stsd < parsedAtoms[track_level_atom].AtomicLength) { - offset_into_stsd ++; - if ( APar_read32(uint32_buffer, isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_stsd) == 0x65736473 ) { - track_info->contains_esds = true; - - uint64_t esds_start = parsedAtoms[track_level_atom].AtomicStart + offset_into_stsd - 4; - uint64_t esds_length = APar_read32(uint32_buffer, isofile, esds_start); - uint64_t offset_into_esds = 12; //4bytes length + 4 bytes name + 4bytes null - - if ( APar_read8(isofile, esds_start + offset_into_esds) == 0x03 ) { - offset_into_esds++; - offset_into_esds += APar_skip_filler(isofile, esds_start + offset_into_esds); - } - - uint8_t section3_length = APar_read8(isofile, esds_start + offset_into_esds); - if ( section3_length <= esds_length && section3_length != 0) { - track_info->section3_length = section3_length; - } else { - break; - } - - //for whatever reason, when mp4box muxes in ogg into an mp4 container, section 3 gets a 0x9D byte (which doesn't fall inline with what AP considers 'filler') - //then again, I haven't *completely* read the ISO specifications, so I could just be missing it the the ->voluminous<- 14496-X specifications. - uint8_t test_byte = APar_read8(isofile, esds_start + offset_into_esds+1); - if (test_byte != 0) { - offset_into_esds++; - } - - offset_into_esds+= 4; //1 bytes section 0x03 length + 2 bytes + 1 byte - - if ( APar_read8(isofile, esds_start + offset_into_esds) == 0x04 ) { - offset_into_esds++; - offset_into_esds += APar_skip_filler(isofile, esds_start + offset_into_esds); - } - - uint8_t section4_length = APar_read8(isofile, esds_start + offset_into_esds); - if ( section4_length <= section3_length && section4_length != 0) { - track_info->section4_length = section4_length; - - if (section4_length == 0x9D) offset_into_esds++; //upper limit? when gpac puts an ogg in, section 3 is 9D - so is sec4 (section 4 real length with ogg = 0x0E86) - - offset_into_esds++; - track_info->ObjectTypeIndication = APar_read8(isofile, esds_start + offset_into_esds); - - //this is just so that ogg in mp4 won't have some bizarre high bitrate of like 2.8megabits/sec - uint8_t a_v_flag = APar_read8(isofile, esds_start + offset_into_esds + 1); //mp4box with ogg will set this to DD, mp4a has it as 0x40, mp4v has 0x20 - - if (track_info->ObjectTypeIndication < 0xC0 && a_v_flag < 0xA0) {//0xC0 marks user streams; but things below that might still be wrong (like 0x6D - png) - offset_into_esds+= 5; - track_info->max_bitrate = APar_read32(uint32_buffer, isofile, esds_start + offset_into_esds); - offset_into_esds+= 4; - track_info->avg_bitrate = APar_read32(uint32_buffer, isofile, esds_start + offset_into_esds); - offset_into_esds+= 4; - } - } else { - break; - } - - if ( APar_read8(isofile, esds_start + offset_into_esds) == 0x05 ) { - offset_into_esds++; - offset_into_esds += APar_skip_filler(isofile, esds_start + offset_into_esds); - - uint8_t section5_length = APar_read8(isofile, esds_start + offset_into_esds); - if ( (section5_length <= section4_length || section4_length == 1) && section5_length != 0) { - track_info->section5_length = section5_length; - offset_into_esds+=1; - - if (track_info->type_of_track & AUDIO_TRACK) { - uint8_t packed_objID = APar_read8(isofile, esds_start + offset_into_esds); //its packed with channel, but channel is fetched separately - track_info->descriptor_object_typeID = packed_objID >> 3; - offset_into_esds+=1; - - track_info->channels = (uint16_t)APar_ExtractChannelInfo(isofile, esds_start + offset_into_esds); - - } else if (track_info->type_of_track & VIDEO_TRACK) { - //technically, visual_object_sequence_start_code should be tested aginst 0x000001B0 - if (APar_read16(uint32_buffer, isofile, esds_start + offset_into_esds+2) == 0x01B0) { - track_info->m4v_profile = APar_read8(isofile, esds_start + offset_into_esds+2+2); - } - } - } - break; //uh, I've extracted the pertinent info - } - - } - if (offset_into_stsd > parsedAtoms[track_level_atom].AtomicLength) { - break; - } - } - if ( (track_info->section5_length == 0 && track_info->type_of_track & AUDIO_TRACK) || track_info->channels == 0) { - track_info->channels = APar_read16(uint32_buffer, isofile, parsedAtoms[track_level_atom].AtomicStart + 40); - } - return; +void APar_Extract_esds_Info(char *uint32_buffer, + FILE *isofile, + short track_level_atom, + TrackInfo *track_info) { + uint64_t offset_into_stsd = 0; + + while (offset_into_stsd < parsedAtoms[track_level_atom].AtomicLength) { + offset_into_stsd++; + if (APar_read32(uint32_buffer, + isofile, + parsedAtoms[track_level_atom].AtomicStart + + offset_into_stsd) == 0x65736473) { + track_info->contains_esds = true; + + uint64_t esds_start = + parsedAtoms[track_level_atom].AtomicStart + offset_into_stsd - 4; + uint64_t esds_length = APar_read32(uint32_buffer, isofile, esds_start); + uint64_t offset_into_esds = + 12; // 4bytes length + 4 bytes name + 4bytes null + + if (APar_read8(isofile, esds_start + offset_into_esds) == 0x03) { + offset_into_esds++; + offset_into_esds += + APar_skip_filler(isofile, esds_start + offset_into_esds); + } + + uint8_t section3_length = + APar_read8(isofile, esds_start + offset_into_esds); + if (section3_length <= esds_length && section3_length != 0) { + track_info->section3_length = section3_length; + } else { + break; + } + + // for whatever reason, when mp4box muxes in ogg into an mp4 container, + // section 3 gets a 0x9D byte (which doesn't fall inline with what AP + // considers 'filler') then again, I haven't *completely* read the ISO + // specifications, so I could just be missing it the the ->voluminous<- + // 14496-X specifications. + uint8_t test_byte = + APar_read8(isofile, esds_start + offset_into_esds + 1); + if (test_byte != 0) { + offset_into_esds++; + } + + offset_into_esds += 4; // 1 bytes section 0x03 length + 2 bytes + 1 byte + + if (APar_read8(isofile, esds_start + offset_into_esds) == 0x04) { + offset_into_esds++; + offset_into_esds += + APar_skip_filler(isofile, esds_start + offset_into_esds); + } + + uint8_t section4_length = + APar_read8(isofile, esds_start + offset_into_esds); + if (section4_length <= section3_length && section4_length != 0) { + track_info->section4_length = section4_length; + + if (section4_length == 0x9D) + offset_into_esds++; // upper limit? when gpac puts an ogg in, section + // 3 is 9D - so is sec4 (section 4 real length + // with ogg = 0x0E86) + + offset_into_esds++; + track_info->ObjectTypeIndication = + APar_read8(isofile, esds_start + offset_into_esds); + + // this is just so that ogg in mp4 won't have some bizarre high bitrate + // of like 2.8megabits/sec + uint8_t a_v_flag = + APar_read8(isofile, + esds_start + offset_into_esds + + 1); // mp4box with ogg will set this to DD, + // mp4a has it as 0x40, mp4v has 0x20 + + if (track_info->ObjectTypeIndication < 0xC0 && + a_v_flag < 0xA0) { // 0xC0 marks user streams; but things below that + // might still be wrong (like 0x6D - png) + offset_into_esds += 5; + track_info->max_bitrate = APar_read32( + uint32_buffer, isofile, esds_start + offset_into_esds); + offset_into_esds += 4; + track_info->avg_bitrate = APar_read32( + uint32_buffer, isofile, esds_start + offset_into_esds); + offset_into_esds += 4; + } + } else { + break; + } + + if (APar_read8(isofile, esds_start + offset_into_esds) == 0x05) { + offset_into_esds++; + offset_into_esds += + APar_skip_filler(isofile, esds_start + offset_into_esds); + + uint8_t section5_length = + APar_read8(isofile, esds_start + offset_into_esds); + if ((section5_length <= section4_length || section4_length == 1) && + section5_length != 0) { + track_info->section5_length = section5_length; + offset_into_esds += 1; + + if (track_info->type_of_track & AUDIO_TRACK) { + uint8_t packed_objID = APar_read8( + isofile, + esds_start + offset_into_esds); // its packed with channel, but + // channel is fetched separately + track_info->descriptor_object_typeID = packed_objID >> 3; + offset_into_esds += 1; + + track_info->channels = (uint16_t)APar_ExtractChannelInfo( + isofile, esds_start + offset_into_esds); + + } else if (track_info->type_of_track & VIDEO_TRACK) { + // technically, visual_object_sequence_start_code should be tested + // aginst 0x000001B0 + if (APar_read16(uint32_buffer, + isofile, + esds_start + offset_into_esds + 2) == 0x01B0) { + track_info->m4v_profile = + APar_read8(isofile, esds_start + offset_into_esds + 2 + 2); + } + } + } + break; // uh, I've extracted the pertinent info + } + } + if (offset_into_stsd > parsedAtoms[track_level_atom].AtomicLength) { + break; + } + } + if ((track_info->section5_length == 0 && + track_info->type_of_track & AUDIO_TRACK) || + track_info->channels == 0) { + track_info->channels = APar_read16( + uint32_buffer, isofile, parsedAtoms[track_level_atom].AtomicStart + 40); + } + return; } /*---------------------- APar_ExtractTrackDetails - uint32_buffer - a buffer to read bytes in from the file - isofile - the file to be scanned - track - the struct proving tracking of this 'trak' atom so we can jump around in this track - track_info - a pointer to the struct carrying track-level info to be filled with information - - This function jumps all around in a single 'trak' atom gathering information from different child constituent atoms except 'esds' which is handled - on its own. + uint32_buffer - a buffer to read bytes in from the file + isofile - the file to be scanned + track - the struct proving tracking of this 'trak' atom so we can jump +around in this track track_info - a pointer to the struct carrying track-level +info to be filled with information + + This function jumps all around in a single 'trak' atom gathering information +from different child constituent atoms except 'esds' which is handled on its +own. ----------------------*/ -void APar_ExtractTrackDetails(char* uint32_buffer, FILE* isofile, Trackage* track, TrackInfo* track_info) { - uint64_t _offset = 0; - - APar_TrackLevelInfo(track, "tkhd"); - if ( APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 8) == 0) { - if (APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 11) & 1) { - track_info->track_enabled = true; - } - track_info->creation_time = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 12); - track_info->modified_time = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 16); - track_info->duration = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 28); - } else { - track_info->creation_time = APar_read64(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 12); - track_info->modified_time = APar_read64(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 20); - track_info->duration = APar_read64(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 36); - } - - //language code - APar_TrackLevelInfo(track, "mdhd"); - memset(uint32_buffer, 0, 5); - uint16_t packed_language = APar_read16(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 28); - memset(track_info->unpacked_lang, 0, 4); - APar_UnpackLanguage(track_info->unpacked_lang, packed_language); //http://www.w3.org/WAI/ER/IG/ert/iso639.htm - - //track handler type - APar_TrackLevelInfo(track, "hdlr"); - memset(uint32_buffer, 0, 5); - track_info->track_type = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 16); - if (track_info->track_type == 0x736F756E ) { //soun - track_info->type_of_track = AUDIO_TRACK; - } else if (track_info->track_type == 0x76696465 ) { //vide - track_info->type_of_track = VIDEO_TRACK; - } - if ( parsedAtoms[track->track_atom].AtomicLength > 34) { - memset(track_info->track_hdlr_name, 0, 100); - APar_readX(track_info->track_hdlr_name, isofile, parsedAtoms[track->track_atom].AtomicStart + 32, parsedAtoms[track->track_atom].AtomicLength - 32); - } - - //codec section - APar_TrackLevelInfo(track, "stsd"); - memset(uint32_buffer, 0, 5); - track_info->track_codec = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 20); - - if (track_info->type_of_track & VIDEO_TRACK ) { //vide - track_info->video_width = APar_read16(uint32_buffer, isofile, parsedAtoms[track->track_atom+1].AtomicStart + 32); - track_info->video_height = APar_read16(uint32_buffer, isofile, parsedAtoms[track->track_atom+1].AtomicStart + 34); - track_info->macroblocks = (track_info->video_width / 16) * (track_info->video_height / 16); - - //avc profile & level - if ( track_info->track_codec == 0x61766331 || track_info->track_codec == 0x64726D69) { //avc1 or drmi - track_info->contains_esds = false; - APar_TrackLevelInfo(track, "avcC"); - //get avc1 profile/level; atom 'avcC' is : - //byte 1 configurationVersion byte 2 AVCProfileIndication byte 3 profile_compatibility byte 4 AVCLevelIndication - track_info->avc_version = APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 8); - if (track_info->avc_version == 1) { - track_info->profile = APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 9); - //uint8_t profile_compatibility = APar_read8(isofile, parsedAtoms[track.track_atom].AtomicStart + 10); /* is this reserved ?? */ - track_info->level = APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 11); - } - - //avc1 doesn't have a hardcoded bitrate, so calculate it (off of stsz table summing) later - } else if (track_info->track_codec == 0x73323633) { //s263 - APar_TrackLevelInfo(track, "d263"); - if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "d263", 4) == 0) { - APar_Extract_d263_Info(uint32_buffer, isofile, track->track_atom, track_info); - } - - } else { //mp4v - APar_TrackLevelInfo(track, "esds"); - if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "esds", 4) == 0) { - APar_Extract_esds_Info(uint32_buffer, isofile, track->track_atom-1, track_info); //right, backtrack to the atom before 'esds' so we can offset_into_stsd++ - } else if (track_info->track_codec == 0x73323633) { //s263 - track_info->type_of_track = VIDEO_TRACK; - } else if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762 - || track_info->track_codec == 0x73617770 || track_info->track_codec == 0x73766D72) { //samr, sawb, sawp & svmr - track_info->type_of_track = AUDIO_TRACK; - } else { - track_info->type_of_track = OTHER_TRACK; //a 'jpeg' track will fall here - } - } - - } else if ( track_info->type_of_track & AUDIO_TRACK) { - if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762 - || track_info->track_codec == 0x73617770 || track_info->track_codec == 0x73766D72) { //samr,sawb, svmr (sawp doesn't contain modes) - APar_Extract_AMR_Info(uint32_buffer, isofile, track->track_atom+2, track_info); - - } else if (track_info->track_codec == 0x73657663) { //sevc - APar_TrackLevelInfo(track, "devc"); - if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "devc", 4) == 0) { - APar_Extract_devc_Info(isofile, track->track_atom, track_info); - } - - } else if (track_info->track_codec == 0x73716370) { //sqcp - APar_TrackLevelInfo(track, "dqcp"); - if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "dqcp", 4) == 0) { - APar_Extract_devc_Info(isofile, track->track_atom, track_info); //its the same thing - } - - } else if (track_info->track_codec == 0x73736D76) { //ssmv - APar_TrackLevelInfo(track, "dsmv"); - if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "dsmv", 4) == 0) { - APar_Extract_devc_Info(isofile, track->track_atom, track_info); //its the same thing - } - - } else { - APar_Extract_esds_Info(uint32_buffer, isofile, track->track_atom, track_info); - } - } - - //in case bitrate isn't found, manually determine it off of stsz summing - if ( (track_info->type_of_track & AUDIO_TRACK || track_info->type_of_track & VIDEO_TRACK ) && track_info->avg_bitrate == 0) { - if (track_info->track_codec == 0x616C6163 ) { //alac - track_info->channels = APar_read16(uint32_buffer, isofile, parsedAtoms[track->track_atom+1].AtomicStart + 24); - } - } - - APar_TrackLevelInfo(track, "stsz"); - if (memcmp(parsedAtoms[track->track_atom].AtomicName, "stsz", 4) == 0) { - track_info->sample_aggregate = calcuate_sample_size(uint32_buffer, isofile, track->track_atom); - } - - //get what exactly 'drmX' stands in for - if (track_info->track_codec >= 0x64726D00 && track_info->track_codec <= 0x64726DFF) { - track_info->type_of_track += DRM_PROTECTED_TRACK; - APar_TrackLevelInfo(track, "frma"); - memset(uint32_buffer, 0, 5); - track_info->protected_codec = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 8); - } - - //Encoder string; occasionally, it appears under stsd for a video track; it is typcally preceded by ' ' (1st char is unprintable) or 0x01B2 - if (track_info->contains_esds) { - APar_TrackLevelInfo(track, "esds"); - - //technically, user_data_start_code should be tested aginst 0x000001B2; TODO: it should only be read up to section 3's length too - _offset = APar_FindValueInAtom(uint32_buffer, isofile, track->track_atom, 24, 0x01B2); - - if (_offset > 0 && _offset < parsedAtoms[track->track_atom].AtomicLength) { - _offset +=2; - memset(track_info->encoder_name, 0, parsedAtoms[track->track_atom].AtomicLength - _offset); - APar_readX(track_info->encoder_name, isofile, parsedAtoms[track->track_atom].AtomicStart + _offset, parsedAtoms[track->track_atom].AtomicLength - _offset); - } - } - return; +void APar_ExtractTrackDetails(char *uint32_buffer, + FILE *isofile, + Trackage *track, + TrackInfo *track_info) { + uint64_t _offset = 0; + + APar_TrackLevelInfo(track, "tkhd"); + if (APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 8) == + 0) { + if (APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 11) & + 1) { + track_info->track_enabled = true; + } + track_info->creation_time = + APar_read32(uint32_buffer, + isofile, + parsedAtoms[track->track_atom].AtomicStart + 12); + track_info->modified_time = + APar_read32(uint32_buffer, + isofile, + parsedAtoms[track->track_atom].AtomicStart + 16); + track_info->duration = + APar_read32(uint32_buffer, + isofile, + parsedAtoms[track->track_atom].AtomicStart + 28); + } else { + track_info->creation_time = + APar_read64(uint32_buffer, + isofile, + parsedAtoms[track->track_atom].AtomicStart + 12); + track_info->modified_time = + APar_read64(uint32_buffer, + isofile, + parsedAtoms[track->track_atom].AtomicStart + 20); + track_info->duration = + APar_read64(uint32_buffer, + isofile, + parsedAtoms[track->track_atom].AtomicStart + 36); + } + + // language code + APar_TrackLevelInfo(track, "mdhd"); + memset(uint32_buffer, 0, 5); + uint16_t packed_language = APar_read16( + uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 28); + memset(track_info->unpacked_lang, 0, 4); + APar_UnpackLanguage( + track_info->unpacked_lang, + packed_language); // http://www.w3.org/WAI/ER/IG/ert/iso639.htm + + // track handler type + APar_TrackLevelInfo(track, "hdlr"); + memset(uint32_buffer, 0, 5); + track_info->track_type = APar_read32( + uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 16); + if (track_info->track_type == 0x736F756E) { // soun + track_info->type_of_track = AUDIO_TRACK; + } else if (track_info->track_type == 0x76696465) { // vide + track_info->type_of_track = VIDEO_TRACK; + } + if (parsedAtoms[track->track_atom].AtomicLength > 34) { + memset(track_info->track_hdlr_name, 0, sizeof(track_info->track_hdlr_name)); + APar_readX(track_info->track_hdlr_name, + isofile, + parsedAtoms[track->track_atom].AtomicStart + 32, + std::min((uint64_t)sizeof(track_info->track_hdlr_name), + parsedAtoms[track->track_atom].AtomicLength - 32)); + } + + // codec section + APar_TrackLevelInfo(track, "stsd"); + memset(uint32_buffer, 0, 5); + track_info->track_codec = APar_read32( + uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 20); + + if (track_info->type_of_track & VIDEO_TRACK) { // vide + track_info->video_width = + APar_read16(uint32_buffer, + isofile, + parsedAtoms[track->track_atom + 1].AtomicStart + 32); + track_info->video_height = + APar_read16(uint32_buffer, + isofile, + parsedAtoms[track->track_atom + 1].AtomicStart + 34); + track_info->macroblocks = + (track_info->video_width / 16) * (track_info->video_height / 16); + + // avc profile & level + if (track_info->track_codec == 0x61766331 || + track_info->track_codec == 0x64726D69) { // avc1 or drmi + track_info->contains_esds = false; + APar_TrackLevelInfo(track, "avcC"); + // get avc1 profile/level; atom 'avcC' is : + // byte 1 configurationVersion byte 2 AVCProfileIndication byte 3 + // profile_compatibility byte 4 AVCLevelIndication + track_info->avc_version = + APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 8); + if (track_info->avc_version == 1) { + track_info->profile = + APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 9); + // uint8_t profile_compatibility = APar_read8(isofile, + // parsedAtoms[track.track_atom].AtomicStart + 10); /* is this reserved + // ?? */ + track_info->level = APar_read8( + isofile, parsedAtoms[track->track_atom].AtomicStart + 11); + } + + // avc1 doesn't have a hardcoded bitrate, so calculate it (off of stsz + // table summing) later + } else if (track_info->track_codec == 0x73323633) { // s263 + APar_TrackLevelInfo(track, "d263"); + if (memcmp(parsedAtoms[track->track_atom].AtomicName, "d263", 4) == 0) { + APar_Extract_d263_Info( + uint32_buffer, isofile, track->track_atom, track_info); + } + + } else { // mp4v + APar_TrackLevelInfo(track, "esds"); + if (memcmp(parsedAtoms[track->track_atom].AtomicName, "esds", 4) == 0) { + APar_Extract_esds_Info( + uint32_buffer, + isofile, + track->track_atom - 1, + track_info); // right, backtrack to the atom before 'esds' so we can + // offset_into_stsd++ + } else if (track_info->track_codec == 0x73323633) { // s263 + track_info->type_of_track = VIDEO_TRACK; + } else if (track_info->track_codec == 0x73616D72 || + track_info->track_codec == 0x73617762 || + track_info->track_codec == 0x73617770 || + track_info->track_codec == + 0x73766D72) { // samr, sawb, sawp & svmr + track_info->type_of_track = AUDIO_TRACK; + } else { + track_info->type_of_track = OTHER_TRACK; // a 'jpeg' track will fall + // here + } + } + + } else if (track_info->type_of_track & AUDIO_TRACK) { + if (track_info->track_codec == 0x73616D72 || + track_info->track_codec == 0x73617762 || + track_info->track_codec == 0x73617770 || + track_info->track_codec == + 0x73766D72) { // samr,sawb, svmr (sawp doesn't contain modes) + APar_Extract_AMR_Info( + uint32_buffer, isofile, track->track_atom + 2, track_info); + + } else if (track_info->track_codec == 0x73657663) { // sevc + APar_TrackLevelInfo(track, "devc"); + if (memcmp(parsedAtoms[track->track_atom].AtomicName, "devc", 4) == 0) { + APar_Extract_devc_Info(isofile, track->track_atom, track_info); + } + + } else if (track_info->track_codec == 0x73716370) { // sqcp + APar_TrackLevelInfo(track, "dqcp"); + if (memcmp(parsedAtoms[track->track_atom].AtomicName, "dqcp", 4) == 0) { + APar_Extract_devc_Info(isofile, + track->track_atom, + track_info); // its the same thing + } + + } else if (track_info->track_codec == 0x73736D76) { // ssmv + APar_TrackLevelInfo(track, "dsmv"); + if (memcmp(parsedAtoms[track->track_atom].AtomicName, "dsmv", 4) == 0) { + APar_Extract_devc_Info(isofile, + track->track_atom, + track_info); // its the same thing + } + + } else { + APar_Extract_esds_Info( + uint32_buffer, isofile, track->track_atom, track_info); + } + } + + // in case bitrate isn't found, manually determine it off of stsz summing + if ((track_info->type_of_track & AUDIO_TRACK || + track_info->type_of_track & VIDEO_TRACK) && + track_info->avg_bitrate == 0) { + if (track_info->track_codec == 0x616C6163) { // alac + track_info->channels = + APar_read16(uint32_buffer, + isofile, + parsedAtoms[track->track_atom + 1].AtomicStart + 24); + } + } + + APar_TrackLevelInfo(track, "stsz"); + if (memcmp(parsedAtoms[track->track_atom].AtomicName, "stsz", 4) == 0) { + track_info->sample_aggregate = + calcuate_sample_size(uint32_buffer, isofile, track->track_atom); + } + + // get what exactly 'drmX' stands in for + if (track_info->track_codec >= 0x64726D00 && + track_info->track_codec <= 0x64726DFF) { + track_info->type_of_track += DRM_PROTECTED_TRACK; + APar_TrackLevelInfo(track, "frma"); + memset(uint32_buffer, 0, 5); + track_info->protected_codec = APar_read32( + uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 8); + } + + // Encoder string; occasionally, it appears under stsd for a video track; it + // is typcally preceded by ' ' (1st char is unprintable) or 0x01B2 + if (track_info->contains_esds) { + APar_TrackLevelInfo(track, "esds"); + + // technically, user_data_start_code should be tested aginst 0x000001B2; + // TODO: it should only be read up to section 3's length too + _offset = APar_FindValueInAtom( + uint32_buffer, isofile, track->track_atom, 24, 0x01B2); + + if (_offset > 0 && _offset < parsedAtoms[track->track_atom].AtomicLength) { + _offset += 2; + memset(track_info->encoder_name, + 0, + parsedAtoms[track->track_atom].AtomicLength - _offset); + APar_readX(track_info->encoder_name, + isofile, + parsedAtoms[track->track_atom].AtomicStart + _offset, + parsedAtoms[track->track_atom].AtomicLength - _offset); + } + } + return; } /*---------------------- APar_ExtractMovieDetails - uint32_buffer - a buffer to read bytes in from the file - isofile - the file to be scanned - mvhd_atom - pointer to the 'mvhd' atom and where in the file it can be found - - Get information out of 'mvhd' - most important of which are timescale & duration which get used to calcuate bitrate if needed and determine duration - of a track in seconds. A rough approximation of the overall bitrate is done off this too using the sum of the mdat lengths. + uint32_buffer - a buffer to read bytes in from the file + isofile - the file to be scanned + mvhd_atom - pointer to the 'mvhd' atom and where in the file it can be +found + + Get information out of 'mvhd' - most important of which are timescale & +duration which get used to calcuate bitrate if needed and determine duration of +a track in seconds. A rough approximation of the overall bitrate is done off +this too using the sum of the mdat lengths. ----------------------*/ -void APar_ExtractMovieDetails(char* uint32_buffer, FILE* isofile, AtomicInfo* mvhd_atom) { - if (mvhd_atom->AtomicVerFlags & 0x01000000) { - movie_info.creation_time = APar_read64(uint32_buffer, isofile, mvhd_atom->AtomicStart + 12); - movie_info.modified_time = APar_read64(uint32_buffer, isofile, mvhd_atom->AtomicStart + 20); - movie_info.timescale = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 28); - movie_info.duration = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 32); - movie_info.timescale = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 36); - movie_info.duration = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 40); - movie_info.playback_rate = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 44); - movie_info.volume = APar_read16(uint32_buffer, isofile, mvhd_atom->AtomicStart + 48); - } else { - movie_info.creation_time = (uint64_t)APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 12); - movie_info.modified_time = (uint64_t)APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 16); - movie_info.timescale = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 20); - movie_info.duration = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 24); - movie_info.playback_rate = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 28); - movie_info.volume = APar_read16(uint32_buffer, isofile, mvhd_atom->AtomicStart + 32); - } - - movie_info.seconds = (float)movie_info.duration / (float)movie_info.timescale; -#if defined (_MSC_VER) - __int64 media_bits = (__int64)mdatData * 8; +void APar_ExtractMovieDetails(char *uint32_buffer, + FILE *isofile, + AtomicInfo *mvhd_atom) { + if (mvhd_atom->AtomicVerFlags & 0x01000000) { + movie_info.creation_time = + APar_read64(uint32_buffer, isofile, mvhd_atom->AtomicStart + 12); + movie_info.modified_time = + APar_read64(uint32_buffer, isofile, mvhd_atom->AtomicStart + 20); + movie_info.timescale = + APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 28); + movie_info.duration = + APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 32); + movie_info.timescale = + APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 36); + movie_info.duration = + APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 40); + movie_info.playback_rate = + APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 44); + movie_info.volume = + APar_read16(uint32_buffer, isofile, mvhd_atom->AtomicStart + 48); + } else { + movie_info.creation_time = (uint64_t)APar_read32( + uint32_buffer, isofile, mvhd_atom->AtomicStart + 12); + movie_info.modified_time = (uint64_t)APar_read32( + uint32_buffer, isofile, mvhd_atom->AtomicStart + 16); + movie_info.timescale = + APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 20); + movie_info.duration = + APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 24); + movie_info.playback_rate = + APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 28); + movie_info.volume = + APar_read16(uint32_buffer, isofile, mvhd_atom->AtomicStart + 32); + } + + movie_info.seconds = (float)movie_info.duration / (float)movie_info.timescale; +#if defined(_MSC_VER) + __int64 media_bits = (__int64)mdatData * 8; #else - uint64_t media_bits = (uint64_t)mdatData * 8; + uint64_t media_bits = (uint64_t)mdatData * 8; #endif - movie_info.simple_bitrate_calc = ( (double)media_bits / movie_info.seconds) / 1000.0; + movie_info.simple_bitrate_calc = + ((double)media_bits / movie_info.seconds) / 1000.0; - return; + return; } /////////////////////////////////////////////////////////////////////////////////////// -// Get at some track-level info // +// Get at some track-level info // /////////////////////////////////////////////////////////////////////////////////////// -void APar_Print_TrackDetails(TrackInfo* track_info) { - if (track_info->max_bitrate > 0 && track_info->avg_bitrate > 0) { - fprintf(stdout, " %.2f kbp/s", (float)track_info->avg_bitrate/1000.0); - } else { //some ffmpeg encodings have avg_bitrate set to 0, but an inexact max_bitrate - actually, their esds seems a mess to me -#if defined (_MSC_VER) - fprintf(stdout, " %.2lf* kbp/s", ( (double)((__int64)track_info->sample_aggregate) / - ( (double)((__int64)track_info->duration) / (double)((__int64)movie_info.timescale)) ) / 1000.0 * 8); - fprintf(stdout, " %.3f sec", (float)track_info->duration / (float)movie_info.timescale); +void APar_Print_TrackDetails(TrackInfo *track_info) { + if (track_info->max_bitrate > 0 && track_info->avg_bitrate > 0) { + fprintf(stdout, " %.2f kbp/s", (float)track_info->avg_bitrate / 1000.0); + } else { // some ffmpeg encodings have avg_bitrate set to 0, but an inexact + // max_bitrate - actually, their esds seems a mess to me +#if defined(_MSC_VER) + fprintf(stdout, + " %.2lf* kbp/s", + ((double)((__int64)track_info->sample_aggregate) / + ((double)((__int64)track_info->duration) / + (double)((__int64)movie_info.timescale))) / + 1000.0 * 8); + fprintf(stdout, + " %.3f sec", + (float)track_info->duration / (float)movie_info.timescale); #else - fprintf(stdout, " %.2lf* kbp/s", ( (double)track_info->sample_aggregate / - ( (double)track_info->duration / (double)movie_info.timescale) ) / 1000.0 * 8); - fprintf(stdout, " %.3f sec", (float)track_info->duration / (float)movie_info.timescale); + fprintf(stdout, + " %.2lf* kbp/s", + ((double)track_info->sample_aggregate / + ((double)track_info->duration / (double)movie_info.timescale)) / + 1000.0 * 8); + fprintf(stdout, + " %.3f sec", + (float)track_info->duration / (float)movie_info.timescale); #endif - } - - if (track_info->track_codec == 0x6D703476 ) { //mp4v profile - APar_ShowObjectProfileInfo(MP4V_TRACK, track_info); - } else if (track_info->track_codec == 0x6D703461 || track_info->protected_codec == 0x6D703461 ) { //mp4a profile - APar_ShowObjectProfileInfo(AUDIO_TRACK, track_info); - } else if (track_info->track_codec == 0x616C6163) { //alac - can't figure out a hardcoded bitrate either - fprintf(stdout, " Apple Lossless channels: [%u]\n", track_info->channels); - } else if (track_info->track_codec == 0x61766331 || track_info->protected_codec == 0x61766331) { - if (track_info->avc_version == 1) { //avc profile & level - APar_ShowObjectProfileInfo(AVC1_TRACK, track_info); - } - } else if (track_info->track_codec == 0x73323633) { //s263 in 3gp - APar_ShowObjectProfileInfo(S263_TRACK, track_info); - } else if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762 - || track_info->track_codec == 0x73617770 || track_info->track_codec == 0x73766D72) { //samr,sawb,sawp & svmr in 3gp - track_info->type_of_track = S_AMR_TRACK; - APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); - } else if (track_info->track_codec == 0x73657663) { //evrc in 3gp - track_info->type_of_track = EVRC_TRACK; - APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); - } else if (track_info->track_codec == 0x73716370) { //qcelp in 3gp - track_info->type_of_track = QCELP_TRACK; - APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); - } else if (track_info->track_codec == 0x73736D76) { //smv in 3gp - track_info->type_of_track = SMV_TRACK; - APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); - } else { //unknown everything, 0 hardcoded bitrate - APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); - fprintf(stdout, "\n"); - } - - if (track_info->type_of_track & VIDEO_TRACK && - ( ( track_info->max_bitrate > 0 && track_info->ObjectTypeIndication == 0x20) || track_info->avc_version == 1 || track_info->protected_codec != 0) ) { - fprintf(stdout, " %ux%u (%" PRIu32 " macroblocks)\n", track_info->video_width, track_info->video_height, track_info->macroblocks); - } else if (track_info->type_of_track & VIDEO_TRACK) { - fprintf(stdout, "\n"); - } - return; -} - -void APar_ExtractDetails(FILE* isofile, uint8_t optional_output) { - char* uint32_buffer=(char*)malloc( sizeof(char)*5 ); - Trackage track = {0}; - - AtomicInfo* mvhdAtom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0); - if (mvhdAtom != NULL) { - APar_ExtractMovieDetails(uint32_buffer, isofile, mvhdAtom); - fprintf(stdout, "Movie duration: %.3lf seconds (%s) - %.2lf* kbp/sec bitrate (*=approximate)\n", movie_info.seconds, secsTOtime(movie_info.seconds), movie_info.simple_bitrate_calc); - if (optional_output & SHOW_DATE_INFO) { - fprintf(stdout, " Presentation Creation Date (UTC): %s\n", APar_extract_UTC(movie_info.creation_time) ); - fprintf(stdout, " Presentation Modification Date (UTC): %s\n", APar_extract_UTC(movie_info.modified_time) ); - } - } - - AtomicInfo* iodsAtom = APar_FindAtom("moov.iods", false, VERSIONED_ATOM, 0); - if (iodsAtom != NULL) { - movie_info.contains_iods = true; - APar_Extract_iods_Info(isofile, iodsAtom); - } - - if (optional_output & SHOW_TRACK_INFO) { - APar_TrackLevelInfo(&track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here. - - fprintf(stdout, "Low-level details. Total tracks: %u\n", track.total_tracks); - fprintf(stdout, "Trk Type Handler Kind Lang Bytes\n"); - - if (track.total_tracks > 0) { - while (track.total_tracks > track.track_num) { - track.track_num+= 1; - TrackInfo track_info = {0}; - - //tracknum, handler type, handler name - APar_ExtractTrackDetails(uint32_buffer, isofile, &track, &track_info); - uint16_t more_whitespace = purge_extraneous_characters(track_info.track_hdlr_name); - - if (strlen(track_info.track_hdlr_name) == 0) { - memcpy(track_info.track_hdlr_name, "[none listed]", 13); - } - fprintf(stdout, "%u %s %s", track.track_num, uint32tochar4(track_info.track_type, uint32_buffer), track_info.track_hdlr_name); - - uint16_t handler_len = strlen(track_info.track_hdlr_name); - if (handler_len < 25 + more_whitespace) { - for (uint16_t i=handler_len; i < 25 + more_whitespace; i++) { - fprintf(stdout, " "); - } - } - - //codec, language - fprintf(stdout, " %s %s %" PRIu64, uint32tochar4(track_info.track_codec, uint32_buffer), track_info.unpacked_lang, track_info.sample_aggregate); - - if (track_info.encoder_name[0] != 0 && track_info.contains_esds) { - purge_extraneous_characters(track_info.encoder_name); - fprintf(stdout, " Encoder: %s", track_info.encoder_name); - } - if (track_info.type_of_track & DRM_PROTECTED_TRACK) { - fprintf(stdout, " (protected %s)", uint32tochar4(track_info.protected_codec, uint32_buffer) ); - } - - fprintf(stdout, "\n"); - /*---------------------------------*/ - - if (track_info.type_of_track & VIDEO_TRACK || track_info.type_of_track & AUDIO_TRACK) { - APar_Print_TrackDetails(&track_info); - } - - if (optional_output & SHOW_DATE_INFO) { - fprintf(stdout, " Creation Date (UTC): %s\n", APar_extract_UTC(track_info.creation_time) ); - fprintf(stdout, " Modification Date (UTC): %s\n", APar_extract_UTC(track_info.modified_time) ); - } - - } - } - } - return; -} - -//provided as a convenience function so that 3rd party utilities can know beforehand -void APar_ExtractBrands(char* filepath) { - FILE* a_file = APar_OpenISOBaseMediaFile(filepath, true); - char* buffer = (char *)calloc(1, sizeof(char)*16); - uint32_t atom_length = 0; - uint8_t file_type_offset = 0; - uint32_t compatible_brand = 0; - bool cb_V2ISOBMFF = false; - - APar_read32(buffer, a_file, 4); - if (memcmp(buffer, "ftyp", 4) == 0) { - atom_length = APar_read32(buffer, a_file, 0); - } else { - APar_readX(buffer, a_file, 0, 12); - if (memcmp(buffer, "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A", 12) == 0 ) { - APar_readX(buffer, a_file, 12, 12); - if (memcmp(buffer+4, "ftypmjp2", 8) == 0 || memcmp(buffer+4, "ftypmj2s", 8) == 0) { - atom_length = UInt32FromBigEndian(buffer); - file_type_offset = 12; - } - } - } - - if (atom_length > 0) { - memset(buffer, 0, 16); - APar_readX(buffer, a_file, 8+file_type_offset, 4); - printBOM(); - fprintf(stdout, " Major Brand: %s", buffer); - APar_IdentifyBrand(buffer); - - if (memcmp(buffer, "isom", 4) == 0) { - APar_ScanAtoms(filepath); //scan_file = true; - } - - uint32_t minor_version = APar_read32(buffer, a_file, 12+file_type_offset); - fprintf(stdout, " - version %" PRIu32 "\n", minor_version); - - fprintf(stdout, " Compatible Brands:"); - for (uint64_t i = 16+file_type_offset; i < atom_length; i+=4) { - APar_readX(buffer, a_file, i, 4); - compatible_brand = UInt32FromBigEndian(buffer); - if (compatible_brand != 0) { - fprintf(stdout, " %s", buffer); - if (compatible_brand == 0x6D703432 || compatible_brand == 0x69736F32) { - cb_V2ISOBMFF = true; - } - } - } - fprintf(stdout, "\n"); - } - - APar_OpenISOBaseMediaFile(filepath, false); - - fprintf(stdout, " Tagging schemes available:\n"); - switch(metadata_style) { - case ITUNES_STYLE: { - fprintf(stdout, " iTunes-style metadata allowed.\n"); - break; - } - case THIRD_GEN_PARTNER: - case THIRD_GEN_PARTNER_VER1_REL6: - case THIRD_GEN_PARTNER_VER1_REL7: - case THIRD_GEN_PARTNER_VER2: { - fprintf(stdout, " 3GP-style asset metadata allowed.\n"); - break; - } - case THIRD_GEN_PARTNER_VER2_REL_A: { - fprintf(stdout, " 3GP-style asset metadata allowed [& unimplemented GAD (Geographical Area Description) asset].\n"); - break; - } - } - if (cb_V2ISOBMFF || metadata_style == THIRD_GEN_PARTNER_VER1_REL7) { - fprintf(stdout, " ID3 tags on ID32 atoms @ file/movie/track level allowed.\n"); - } - fprintf(stdout, " ISO-copyright notices @ movie and/or track level allowed.\n uuid private user extension tags allowed.\n"); + } - free(buffer); buffer=NULL; - return; + if (track_info->track_codec == 0x6D703476) { // mp4v profile + APar_ShowObjectProfileInfo(MP4V_TRACK, track_info); + } else if (track_info->track_codec == 0x6D703461 || + track_info->protected_codec == 0x6D703461) { // mp4a profile + APar_ShowObjectProfileInfo(AUDIO_TRACK, track_info); + } else if (track_info->track_codec == + 0x616C6163) { // alac - can't figure out a hardcoded bitrate either + fprintf( + stdout, " Apple Lossless channels: [%u]\n", track_info->channels); + } else if (track_info->track_codec == 0x61766331 || + track_info->protected_codec == 0x61766331) { + if (track_info->avc_version == 1) { // avc profile & level + APar_ShowObjectProfileInfo(AVC1_TRACK, track_info); + } + } else if (track_info->track_codec == 0x73323633) { // s263 in 3gp + APar_ShowObjectProfileInfo(S263_TRACK, track_info); + } else if (track_info->track_codec == 0x73616D72 || + track_info->track_codec == 0x73617762 || + track_info->track_codec == 0x73617770 || + track_info->track_codec == + 0x73766D72) { // samr,sawb,sawp & svmr in 3gp + track_info->type_of_track = S_AMR_TRACK; + APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); + } else if (track_info->track_codec == 0x73657663) { // evrc in 3gp + track_info->type_of_track = EVRC_TRACK; + APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); + } else if (track_info->track_codec == 0x73716370) { // qcelp in 3gp + track_info->type_of_track = QCELP_TRACK; + APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); + } else if (track_info->track_codec == 0x73736D76) { // smv in 3gp + track_info->type_of_track = SMV_TRACK; + APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); + } else { // unknown everything, 0 hardcoded bitrate + APar_ShowObjectProfileInfo(track_info->type_of_track, track_info); + fprintf(stdout, "\n"); + } + + if (track_info->type_of_track & VIDEO_TRACK && + ((track_info->max_bitrate > 0 && + track_info->ObjectTypeIndication == 0x20) || + track_info->avc_version == 1 || track_info->protected_codec != 0)) { + fprintf(stdout, + " %ux%u (%" PRIu32 " macroblocks)\n", + track_info->video_width, + track_info->video_height, + track_info->macroblocks); + } else if (track_info->type_of_track & VIDEO_TRACK) { + fprintf(stdout, "\n"); + } + return; +} + +void APar_ExtractDetails(FILE *isofile, uint8_t optional_output) { + char uint32_buffer[8]; + Trackage track = {0}; + + AtomicInfo *mvhdAtom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0); + if (mvhdAtom != NULL) { + APar_ExtractMovieDetails(uint32_buffer, isofile, mvhdAtom); + fprintf(stdout, + "Movie duration: %.3lf seconds (%s) - %.2lf* kbp/sec bitrate " + "(*=approximate)\n", + movie_info.seconds, + secsTOtime(movie_info.seconds), + movie_info.simple_bitrate_calc); + if (optional_output & SHOW_DATE_INFO) { + fprintf(stdout, + " Presentation Creation Date (UTC): %s\n", + APar_extract_UTC(movie_info.creation_time)); + fprintf(stdout, + " Presentation Modification Date (UTC): %s\n", + APar_extract_UTC(movie_info.modified_time)); + } + } + + AtomicInfo *iodsAtom = APar_FindAtom("moov.iods", false, VERSIONED_ATOM, 0); + if (iodsAtom != NULL) { + movie_info.contains_iods = true; + APar_Extract_iods_Info(isofile, iodsAtom); + } + + if (optional_output & SHOW_TRACK_INFO) { + APar_TrackLevelInfo(&track, + NULL); // With track_num set to 0, it will return the + // total trak atom into total_tracks here. + + fprintf( + stdout, "Low-level details. Total tracks: %u\n", track.total_tracks); + fprintf(stdout, + "Trk Type Handler Kind Lang Bytes\n"); + + if (track.total_tracks > 0) { + while (track.total_tracks > track.track_num) { + track.track_num += 1; + TrackInfo track_info = {0}; + + // tracknum, handler type, handler name + APar_ExtractTrackDetails(uint32_buffer, isofile, &track, &track_info); + uint16_t more_whitespace = + purge_extraneous_characters(track_info.track_hdlr_name); + + if (strlen(track_info.track_hdlr_name) == 0) { + memcpy(track_info.track_hdlr_name, "[none listed]", 13); + } + fprintf(stdout, + "%u %s %s", + track.track_num, + uint32tochar4(track_info.track_type, uint32_buffer), + track_info.track_hdlr_name); + + uint16_t handler_len = strlen(track_info.track_hdlr_name); + if (handler_len < 25 + more_whitespace) { + for (uint16_t i = handler_len; i < 25 + more_whitespace; i++) { + fprintf(stdout, " "); + } + } + + // codec, language + fprintf(stdout, + " %s %s %" PRIu64, + uint32tochar4(track_info.track_codec, uint32_buffer), + track_info.unpacked_lang, + track_info.sample_aggregate); + + if (track_info.encoder_name[0] != 0 && track_info.contains_esds) { + purge_extraneous_characters(track_info.encoder_name); + fprintf(stdout, " Encoder: %s", track_info.encoder_name); + } + if (track_info.type_of_track & DRM_PROTECTED_TRACK) { + fprintf(stdout, + " (protected %s)", + uint32tochar4(track_info.protected_codec, uint32_buffer)); + } + + fprintf(stdout, "\n"); + /*---------------------------------*/ + + if (track_info.type_of_track & VIDEO_TRACK || + track_info.type_of_track & AUDIO_TRACK) { + APar_Print_TrackDetails(&track_info); + } + + if (optional_output & SHOW_DATE_INFO) { + fprintf(stdout, + " Creation Date (UTC): %s\n", + APar_extract_UTC(track_info.creation_time)); + fprintf(stdout, + " Modification Date (UTC): %s\n", + APar_extract_UTC(track_info.modified_time)); + } + } + } + } +} + +// provided as a convenience function so that 3rd party utilities can know +// beforehand +void APar_ExtractBrands(char *filepath) { + FILE *a_file = APar_OpenISOBaseMediaFile(filepath, true); + char buffer[16] = {}; + uint32_t atom_length = 0; + uint8_t file_type_offset = 0; + uint32_t compatible_brand = 0; + bool cb_V2ISOBMFF = false; + + APar_read32(buffer, a_file, 4); + if (memcmp(buffer, "ftyp", 4) == 0) { + atom_length = APar_read32(buffer, a_file, 0); + } else { + APar_readX(buffer, a_file, 0, 12); + if (memcmp(buffer, + "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A", + 12) == 0) { + APar_readX(buffer, a_file, 12, 12); + if (memcmp(buffer + 4, "ftypmjp2", 8) == 0 || + memcmp(buffer + 4, "ftypmj2s", 8) == 0) { + atom_length = UInt32FromBigEndian(buffer); + file_type_offset = 12; + } + } + } + + if (atom_length > 0) { + memset(buffer, 0, 16); + APar_readX(buffer, a_file, 8 + file_type_offset, 4); + printBOM(); + fprintf(stdout, " Major Brand: %s", buffer); + APar_IdentifyBrand(buffer); + + if (memcmp(buffer, "isom", 4) == 0) { + APar_ScanAtoms(filepath); // scan_file = true; + } + + uint32_t minor_version = APar_read32(buffer, a_file, 12 + file_type_offset); + fprintf(stdout, " - version %" PRIu32 "\n", minor_version); + + fprintf(stdout, " Compatible Brands:"); + for (uint64_t i = 16 + file_type_offset; i < atom_length; i += 4) { + APar_readX(buffer, a_file, i, 4); + compatible_brand = UInt32FromBigEndian(buffer); + if (compatible_brand != 0) { + fprintf(stdout, " %s", buffer); + if (compatible_brand == 0x6D703432 || compatible_brand == 0x69736F32) { + cb_V2ISOBMFF = true; + } + } + } + fprintf(stdout, "\n"); + } + + APar_OpenISOBaseMediaFile(filepath, false); + + fprintf(stdout, " Tagging schemes available:\n"); + switch (metadata_style) { + case ITUNES_STYLE: { + fprintf(stdout, " iTunes-style metadata allowed.\n"); + break; + } + case THIRD_GEN_PARTNER: + case THIRD_GEN_PARTNER_VER1_REL6: + case THIRD_GEN_PARTNER_VER1_REL7: + case THIRD_GEN_PARTNER_VER2: { + fprintf(stdout, " 3GP-style asset metadata allowed.\n"); + break; + } + case THIRD_GEN_PARTNER_VER2_REL_A: { + fprintf(stdout, + " 3GP-style asset metadata allowed [& unimplemented GAD " + "(Geographical Area Description) asset].\n"); + break; + } + } + if (cb_V2ISOBMFF || metadata_style == THIRD_GEN_PARTNER_VER1_REL7) { + fprintf(stdout, + " ID3 tags on ID32 atoms @ file/movie/track level allowed.\n"); + } + fprintf(stdout, + " ISO-copyright notices @ movie and/or track level " + "allowed.\n uuid private user extension tags allowed.\n"); } diff -Nru atomicparsley-0.9.6/src/iconv.cpp atomicparsley-20210715.151551.e7ad03a/src/iconv.cpp --- atomicparsley-0.9.6/src/iconv.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/iconv.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - iconv.cpp - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,12 +10,12 @@ 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 + 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 2005-2007 puck_lock + Copyright (C) 2005-2007 puck_lock with contributions from others; see the CREDITS file */ //==================================================================// @@ -53,99 +53,89 @@ #include "AtomicParsley.h" const unsigned short cp437upperbytes[128] = { - 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, - 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, - 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, - 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, - 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, - 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, - 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, - 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, - 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, - 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, - 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, - 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, - 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, - 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, - 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 -}; + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, + 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, + 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, + 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, + 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, + 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, + 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, + 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, + 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, + 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, + 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, + 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, + 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, + 0x25A0, 0x00A0}; const unsigned short cp850upperbytes[128] = { - 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, - 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, - 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, - 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, - 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, - 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, - 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, - 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, - 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, - 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, - 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, - 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, - 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, - 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, - 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 -}; + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, + 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, + 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, + 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, + 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, + 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, + 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, + 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x00F0, + 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, + 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 0x00D3, 0x00DF, 0x00D4, + 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, + 0x00FD, 0x00DD, 0x00AF, 0x00B4, 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, + 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, + 0x25A0, 0x00A0}; const unsigned short cp852upperbytes[128] = { - 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, - 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, - 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, - 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, - 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, - 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, - 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, - 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, - 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, - 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, - 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, - 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, - 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, - 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, - 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 -}; + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, + 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, 0x00C9, 0x0139, + 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, + 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, 0x00E1, 0x00ED, 0x00F3, 0x00FA, + 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, + 0x015F, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, + 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, + 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x0111, + 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, + 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, 0x00D3, 0x00DF, 0x00D4, + 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, + 0x00FD, 0x00DD, 0x0163, 0x00B4, 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, + 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, + 0x25A0, 0x00A0}; const unsigned short cp855upperbytes[128] = { - 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, - 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, - 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, - 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, - 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, - 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, - 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, - 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, - 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, - 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, - 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, - 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, - 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, - 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, - 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 -}; + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, + 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, 0x0459, 0x0409, + 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, + 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, 0x0430, 0x0410, 0x0431, 0x0411, + 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, + 0x0413, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, + 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, + 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x043B, + 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, + 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, 0x042F, 0x0440, 0x0420, + 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, + 0x0412, 0x044C, 0x042C, 0x2116, 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, + 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, + 0x25A0, 0x00A0}; const unsigned short cp858upperbytes[128] = { - 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, - 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, - 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, - 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, - 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, - 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, - 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, - 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, - 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, - 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE, - 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, - 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, - 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, - 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, - 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 -}; + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, + 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6, + 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, + 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA, + 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, + 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, + 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, + 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x00F0, + 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE, 0x00CF, 0x2518, + 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 0x00D3, 0x00DF, 0x00D4, + 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, + 0x00FD, 0x00DD, 0x00AF, 0x00B4, 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, + 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, + 0x25A0, 0x00A0}; //==================================================================// // utf conversion functions from libxml2 @@ -178,13 +168,13 @@ // Original code for IsoLatin1 and UTF-16 by "Martin J. Duerst" -static int xmlLittleEndian = +static int xmlLittleEndian = #ifdef WORDS_BIGENDIAN - 0 + 0 #else - 1 + 1 #endif - ; + ; /** * isolat1ToUTF8: @@ -200,37 +190,41 @@ * if the return value is positive, else unpredictable. * The value of @outlen after return is the number of octets consumed. */ -int isolat1ToUTF8(unsigned char* out, int outlen, const unsigned char* in, int inlen) { - unsigned char* outstart = out; - const unsigned char* base = in; - unsigned char* outend; - const unsigned char* inend; - const unsigned char* instop; - - if ((out == NULL) || (in == NULL) || (outlen == 0) || (inlen == 0)) - return(-1); - - outend = out + outlen; - inend = in + (inlen); - instop = inend; - - while (in < inend && out < outend - 1) { - if (*in >= 0x80) { - *out++ = (((*in) >> 6) & 0x1F) | 0xC0; - *out++ = ((*in) & 0x3F) | 0x80; - ++in; - } - if (instop - in > outend - out) instop = in + (outend - out); - while (in < instop && *in < 0x80) { - *out++ = *in++; - } - } - if (in < inend && out < outend && *in < 0x80) { - *out++ = *in++; - } - outlen = out - outstart; - inlen = in - base; - return(outlen); +int isolat1ToUTF8(unsigned char *out, + int outlen, + const unsigned char *in, + int inlen) { + unsigned char *outstart = out; + const unsigned char *base = in; + unsigned char *outend; + const unsigned char *inend; + const unsigned char *instop; + + if ((out == NULL) || (in == NULL) || (outlen == 0) || (inlen == 0)) + return (-1); + + outend = out + outlen; + inend = in + (inlen); + instop = inend; + + while (in < inend && out < outend - 1) { + if (*in >= 0x80) { + *out++ = (((*in) >> 6) & 0x1F) | 0xC0; + *out++ = ((*in) & 0x3F) | 0x80; + ++in; + } + if (instop - in > outend - out) + instop = in + (outend - out); + while (in < instop && *in < 0x80) { + *out++ = *in++; + } + } + if (in < inend && out < outend && *in < 0x80) { + *out++ = *in++; + } + outlen = out - outstart; + inlen = in - base; + return (outlen); } /** @@ -249,76 +243,88 @@ * if the return value is positive, else unpredictable. * The value of @outlen after return is the number of octets consumed. */ -int UTF8Toisolat1(unsigned char* out, int outlen, const unsigned char* in, int inlen) { - const unsigned char* processed = in; - const unsigned char* outend; - const unsigned char* outstart = out; - const unsigned char* instart = in; - const unsigned char* inend; - unsigned int c, d; - int trailing; - - if ((out == NULL) || (outlen == 0) || (inlen == 0)) return(-1); - if (in == NULL) { - /* - * initialization nothing to do - */ - outlen = 0; - inlen = 0; - return(0); - } - inend = in + (inlen); - outend = out + (outlen); - while (in < inend) { - d = *in++; - if (d < 0x80) { c= d; trailing= 0; } - else if (d < 0xC0) { - /* trailing byte in leading position */ - outlen = out - outstart; - inlen = processed - instart; - return(-2); - } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; } - else if (d < 0xF0) { c= d & 0x0F; trailing= 2; } - else if (d < 0xF8) { c= d & 0x07; trailing= 3; } - else { - /* no chance for this in IsoLat1 */ - outlen = out - outstart; - inlen = processed - instart; - return(-2); - } - - if (inend - in < trailing) { - break; - } - - for ( ; trailing; trailing--) { - if (in >= inend) - break; - if (((d= *in++) & 0xC0) != 0x80) { - outlen = out - outstart; - inlen = processed - instart; - return(-2); - } - c <<= 6; - c |= d & 0x3F; - } - - /* assertion: c is a single UTF-4 value */ - if (c <= 0xFF) { - if (out >= outend) - break; - *out++ = c; - } else { - /* no chance for this in IsoLat1 */ - outlen = out - outstart; - inlen = processed - instart; - return(-2); - } - processed = in; - } - outlen = out - outstart; - inlen = processed - instart; - return(outlen); +int UTF8Toisolat1(unsigned char *out, + int outlen, + const unsigned char *in, + int inlen) { + const unsigned char *processed = in; + const unsigned char *outend; + const unsigned char *outstart = out; + const unsigned char *instart = in; + const unsigned char *inend; + unsigned int c, d; + int trailing; + + if ((out == NULL) || (outlen == 0) || (inlen == 0)) + return (-1); + if (in == NULL) { + /* + * initialization nothing to do + */ + outlen = 0; + inlen = 0; + return (0); + } + inend = in + (inlen); + outend = out + (outlen); + while (in < inend) { + d = *in++; + if (d < 0x80) { + c = d; + trailing = 0; + } else if (d < 0xC0) { + /* trailing byte in leading position */ + outlen = out - outstart; + inlen = processed - instart; + return (-2); + } else if (d < 0xE0) { + c = d & 0x1F; + trailing = 1; + } else if (d < 0xF0) { + c = d & 0x0F; + trailing = 2; + } else if (d < 0xF8) { + c = d & 0x07; + trailing = 3; + } else { + /* no chance for this in IsoLat1 */ + outlen = out - outstart; + inlen = processed - instart; + return (-2); + } + + if (inend - in < trailing) { + break; + } + + for (; trailing; trailing--) { + if (in >= inend) + break; + if (((d = *in++) & 0xC0) != 0x80) { + outlen = out - outstart; + inlen = processed - instart; + return (-2); + } + c <<= 6; + c |= d & 0x3F; + } + + /* assertion: c is a single UTF-4 value */ + if (c <= 0xFF) { + if (out >= outend) + break; + *out++ = c; + } else { + /* no chance for this in IsoLat1 */ + outlen = out - outstart; + inlen = processed - instart; + return (-2); + } + processed = in; + } + outlen = out - outstart; + inlen = processed - instart; + return (outlen); } /** @@ -338,83 +344,93 @@ * The value of *inlen after return is the number of octets consumed * if the return value is positive, else unpredictable. */ -int UTF16BEToUTF8(unsigned char* out, int outlen, const unsigned char* inb, int inlenb) -{ - unsigned char* outstart = out; - const unsigned char* processed = inb; - unsigned char* outend = out + outlen; - unsigned short* in = (unsigned short*) inb; - unsigned short* inend; - unsigned int c, d, inlen; - unsigned char *tmp; - int bits; - - if ((inlenb % 2) == 1) - (inlenb)--; - inlen = inlenb / 2; - inend= in + inlen; - while (in < inend) { - if (xmlLittleEndian) { - tmp = (unsigned char *) in; - c = *tmp++; - c = c << 8; - c = c | (unsigned int) *tmp; - in++; - } else { - c= *in++; - - if (c == 0xFEFF) { - c= *in++; //skip BOM - } - } - if ((c & 0xFC00) == 0xD800) { /* surrogates */ - if (in >= inend) { /* (in > inend) shouldn't happens */ - outlen = out - outstart; - inlenb = processed - inb; - return(-2); - } - if (xmlLittleEndian) { - tmp = (unsigned char *) in; - d = *tmp++; - d = d << 8; - d = d | (unsigned int) *tmp; - in++; - } else { - d= *in++; - } - if ((d & 0xFC00) == 0xDC00) { - c &= 0x03FF; - c <<= 10; - c |= d & 0x03FF; - c += 0x10000; - } - else { - outlen = out - outstart; - inlenb = processed - inb; - return(-2); - } - } +int UTF16BEToUTF8(unsigned char *out, + int outlen, + const unsigned char *inb, + int inlenb) { + unsigned char *outstart = out; + const unsigned char *processed = inb; + unsigned char *outend = out + outlen; + unsigned short *in = (unsigned short *)inb; + unsigned short *inend; + unsigned int c, d, inlen; + unsigned char *tmp; + int bits; + + if ((inlenb % 2) == 1) + (inlenb)--; + inlen = inlenb / 2; + inend = in + inlen; + while (in < inend) { + if (xmlLittleEndian) { + tmp = (unsigned char *)in; + c = *tmp++; + c = c << 8; + c = c | (unsigned int)*tmp; + in++; + } else { + c = *in++; - /* assertion: c is a single UTF-4 value */ - if (out >= outend) - break; - if (c < 0x80) { *out++= c; bits= -6; } - else if (c < 0x800) { *out++= ((c >> 6) & 0x1F) | 0xC0; bits= 0; } - else if (c < 0x10000) { *out++= ((c >> 12) & 0x0F) | 0xE0; bits= 6; } - else { *out++= ((c >> 18) & 0x07) | 0xF0; bits= 12; } - - for ( ; bits >= 0; bits-= 6) { - if (out >= outend) - break; - *out++= ((c >> bits) & 0x3F) | 0x80; - } - processed = (const unsigned char*) in; + if (c == 0xFEFF) { + c = *in++; // skip BOM + } + } + if ((c & 0xFC00) == 0xD800) { /* surrogates */ + if (in >= inend) { /* (in > inend) shouldn't happens */ + outlen = out - outstart; + inlenb = processed - inb; + return (-2); + } + if (xmlLittleEndian) { + tmp = (unsigned char *)in; + d = *tmp++; + d = d << 8; + d = d | (unsigned int)*tmp; + in++; + } else { + d = *in++; + } + if ((d & 0xFC00) == 0xDC00) { + c &= 0x03FF; + c <<= 10; + c |= d & 0x03FF; + c += 0x10000; + } else { + outlen = out - outstart; + inlenb = processed - inb; + return (-2); + } + } + + /* assertion: c is a single UTF-4 value */ + if (out >= outend) + break; + if (c < 0x80) { + *out++ = c; + bits = -6; + } else if (c < 0x800) { + *out++ = ((c >> 6) & 0x1F) | 0xC0; + bits = 0; + } else if (c < 0x10000) { + *out++ = ((c >> 12) & 0x0F) | 0xE0; + bits = 6; + } else { + *out++ = ((c >> 18) & 0x07) | 0xF0; + bits = 12; + } + + for (; bits >= 0; bits -= 6) { + if (out >= outend) + break; + *out++ = ((c >> bits) & 0x3F) | 0x80; } - outlen = out - outstart; - inlenb = processed - inb; - return(outlen); + processed = (const unsigned char *)in; + } + outlen = out - outstart; + inlenb = processed - inb; + return (outlen); } - + /** * UTF8ToUTF16BE: * @outb: a pointer to an array of bytes to store the result @@ -426,96 +442,108 @@ * block of chars out. * * Returns the number of byte written, or -1 by lack of space, or -2 - * if the transcoding failed. + * if the transcoding failed. */ -int UTF8ToUTF16BE(unsigned char* outb, int outlen, const unsigned char* in, int inlen) -{ - unsigned short* out = (unsigned short*) outb; - const unsigned char* processed = in; - const unsigned char *const instart = in; - unsigned short* outstart= out; - unsigned short* outend; - const unsigned char* inend= in+inlen; - unsigned int c, d; - int trailing; - unsigned char *tmp; - unsigned short tmp1, tmp2; - - /* UTF-16BE has no BOM */ - if ((outb == NULL) || (outlen == 0) || (inlen == 0)) return(-1); - if (in == NULL) { - outlen = 0; - inlen = 0; - return(0); - } - outend = out + (outlen / 2); - while (in < inend) { - d= *in++; - if (d < 0x80) { c= d; trailing= 0; } - else if (d < 0xC0) { - /* trailing byte in leading position */ - outlen = out - outstart; - inlen = processed - instart; - return(-2); - } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; } - else if (d < 0xF0) { c= d & 0x0F; trailing= 2; } - else if (d < 0xF8) { c= d & 0x07; trailing= 3; } - else { - /* no chance for this in UTF-16 */ - outlen = out - outstart; - inlen = processed - instart; - return(-2); - } - - if (inend - in < trailing) { - break; - } - - for ( ; trailing; trailing--) { - if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80)) break; - c <<= 6; - c |= d & 0x3F; - } - - /* assertion: c is a single UTF-4 value */ - if (c < 0x10000) { - if (out >= outend) break; - if (xmlLittleEndian) { - tmp = (unsigned char *) out; - *tmp = c >> 8; - *(tmp + 1) = c; - out++; - } else { - *out++ = c; - } - } - else if (c < 0x110000) { - if (out+1 >= outend) break; - c -= 0x10000; - if (xmlLittleEndian) { - tmp1 = 0xD800 | (c >> 10); - tmp = (unsigned char *) out; - *tmp = tmp1 >> 8; - *(tmp + 1) = (unsigned char) tmp1; - out++; - - tmp2 = 0xDC00 | (c & 0x03FF); - tmp = (unsigned char *) out; - *tmp = tmp2 >> 8; - *(tmp + 1) = (unsigned char) tmp2; - out++; - } else { - *out++ = 0xD800 | (c >> 10); - *out++ = 0xDC00 | (c & 0x03FF); - } - } - else - break; - processed = in; - } - outlen = (out - outstart) * 2; - inlen = processed - instart; - return(outlen); +int UTF8ToUTF16BE(unsigned char *outb, + int outlen, + const unsigned char *in, + int inlen) { + unsigned short *out = (unsigned short *)outb; + const unsigned char *processed = in; + const unsigned char *const instart = in; + unsigned short *outstart = out; + unsigned short *outend; + const unsigned char *inend = in + inlen; + unsigned int c, d; + int trailing; + unsigned char *tmp; + unsigned short tmp1, tmp2; + + /* UTF-16BE has no BOM */ + if ((outb == NULL) || (outlen == 0) || (inlen == 0)) + return (-1); + if (in == NULL) { + outlen = 0; + inlen = 0; + return (0); + } + outend = out + (outlen / 2); + while (in < inend) { + d = *in++; + if (d < 0x80) { + c = d; + trailing = 0; + } else if (d < 0xC0) { + /* trailing byte in leading position */ + outlen = out - outstart; + inlen = processed - instart; + return (-2); + } else if (d < 0xE0) { + c = d & 0x1F; + trailing = 1; + } else if (d < 0xF0) { + c = d & 0x0F; + trailing = 2; + } else if (d < 0xF8) { + c = d & 0x07; + trailing = 3; + } else { + /* no chance for this in UTF-16 */ + outlen = out - outstart; + inlen = processed - instart; + return (-2); + } + + if (inend - in < trailing) { + break; + } + + for (; trailing; trailing--) { + if ((in >= inend) || (((d = *in++) & 0xC0) != 0x80)) + break; + c <<= 6; + c |= d & 0x3F; + } + + /* assertion: c is a single UTF-4 value */ + if (c < 0x10000) { + if (out >= outend) + break; + if (xmlLittleEndian) { + tmp = (unsigned char *)out; + *tmp = c >> 8; + *(tmp + 1) = c; + out++; + } else { + *out++ = c; + } + } else if (c < 0x110000) { + if (out + 1 >= outend) + break; + c -= 0x10000; + if (xmlLittleEndian) { + tmp1 = 0xD800 | (c >> 10); + tmp = (unsigned char *)out; + *tmp = tmp1 >> 8; + *(tmp + 1) = (unsigned char)tmp1; + out++; + + tmp2 = 0xDC00 | (c & 0x03FF); + tmp = (unsigned char *)out; + *tmp = tmp2 >> 8; + *(tmp + 1) = (unsigned char)tmp2; + out++; + } else { + *out++ = 0xD800 | (c >> 10); + *out++ = 0xDC00 | (c & 0x03FF); + } + } else + break; + processed = in; + } + outlen = (out - outstart) * 2; + inlen = processed - instart; + return (outlen); } /** @@ -535,73 +563,83 @@ * The value of *inlen after return is the number of octets consumed * if the return value is positive, else unpredictable. */ -int UTF16LEToUTF8(unsigned char* out, int outlen, const unsigned char* inb, int inlenb) -{ - unsigned char* outstart = out; - const unsigned char* processed = inb; - unsigned char* outend = out + outlen; - unsigned short* in = (unsigned short*) inb; - unsigned short* inend; - unsigned int c, d, inlen; - unsigned char *tmp; - int bits; - - if ((inlenb % 2) == 1) - (inlenb)--; - inlen = inlenb / 2; - inend = in + inlen; - while ((in < inend) && (out - outstart + 5 < outlen)) { - if (xmlLittleEndian) { - c= *in++; - } else { - tmp = (unsigned char *) in; - c = *tmp++; - c = c | (((unsigned int)*tmp) << 8); - in++; - } - if ((c & 0xFC00) == 0xD800) { /* surrogates */ - if (in >= inend) { /* (in > inend) shouldn't happens */ - break; - } - if (xmlLittleEndian) { - d = *in++; - } else { - tmp = (unsigned char *) in; - d = *tmp++; - d = d | (((unsigned int)*tmp) << 8); - in++; - } - if ((d & 0xFC00) == 0xDC00) { - c &= 0x03FF; - c <<= 10; - c |= d & 0x03FF; - c += 0x10000; - } - else { - outlen = out - outstart; - inlenb = processed - inb; - return(-2); - } - } +int UTF16LEToUTF8(unsigned char *out, + int outlen, + const unsigned char *inb, + int inlenb) { + unsigned char *outstart = out; + const unsigned char *processed = inb; + unsigned char *outend = out + outlen; + unsigned short *in = (unsigned short *)inb; + unsigned short *inend; + unsigned int c, d, inlen; + unsigned char *tmp; + int bits; + + if ((inlenb % 2) == 1) + (inlenb)--; + inlen = inlenb / 2; + inend = in + inlen; + while ((in < inend) && (out - outstart + 5 < outlen)) { + if (xmlLittleEndian) { + c = *in++; + } else { + tmp = (unsigned char *)in; + c = *tmp++; + c = c | (((unsigned int)*tmp) << 8); + in++; + } + if ((c & 0xFC00) == 0xD800) { /* surrogates */ + if (in >= inend) { /* (in > inend) shouldn't happens */ + break; + } + if (xmlLittleEndian) { + d = *in++; + } else { + tmp = (unsigned char *)in; + d = *tmp++; + d = d | (((unsigned int)*tmp) << 8); + in++; + } + if ((d & 0xFC00) == 0xDC00) { + c &= 0x03FF; + c <<= 10; + c |= d & 0x03FF; + c += 0x10000; + } else { + outlen = out - outstart; + inlenb = processed - inb; + return (-2); + } + } - /* assertion: c is a single UTF-4 value */ - if (out >= outend) - break; - if (c < 0x80) { *out++= c; bits= -6; } - else if (c < 0x800) { *out++= ((c >> 6) & 0x1F) | 0xC0; bits= 0; } - else if (c < 0x10000) { *out++= ((c >> 12) & 0x0F) | 0xE0; bits= 6; } - else { *out++= ((c >> 18) & 0x07) | 0xF0; bits= 12; } - - for ( ; bits >= 0; bits-= 6) { - if (out >= outend) - break; - *out++= ((c >> bits) & 0x3F) | 0x80; - } - processed = (const unsigned char*) in; + /* assertion: c is a single UTF-4 value */ + if (out >= outend) + break; + if (c < 0x80) { + *out++ = c; + bits = -6; + } else if (c < 0x800) { + *out++ = ((c >> 6) & 0x1F) | 0xC0; + bits = 0; + } else if (c < 0x10000) { + *out++ = ((c >> 12) & 0x0F) | 0xE0; + bits = 6; + } else { + *out++ = ((c >> 18) & 0x07) | 0xF0; + bits = 12; } - outlen = out - outstart; - inlenb = processed - inb; - return(outlen); + + for (; bits >= 0; bits -= 6) { + if (out >= outend) + break; + *out++ = ((c >> bits) & 0x3F) | 0x80; + } + processed = (const unsigned char *)in; + } + outlen = out - outstart; + inlenb = processed - inb; + return (outlen); } /** @@ -615,280 +653,302 @@ * block of chars out. * * Returns the number of bytes written, or -1 if lack of space, or -2 - * if the transcoding failed. + * if the transcoding failed. */ -int UTF8ToUTF16LE(unsigned char* outb, int outlen, const unsigned char* in, int inlen) -{ - unsigned short* out = (unsigned short*) outb; - const unsigned char* processed = in; - const unsigned char *const instart = in; - unsigned short* outstart= out; - unsigned short* outend; - const unsigned char* inend= in+inlen; - unsigned int c, d; - int trailing; - unsigned char *tmp; - unsigned short tmp1, tmp2; - - /* UTF16LE encoding has no BOM */ - if ((out == NULL) || (outlen == 0) || (inlen == 0)) return(-1); - if (in == NULL) { - outlen = 0; - inlen = 0; - return(0); - } - outend = out + (outlen / 2); - while (in < inend) { - d= *in++; - if (d < 0x80) { c= d; trailing= 0; } - else if (d < 0xC0) { - /* trailing byte in leading position */ - outlen = (out - outstart) * 2; - inlen = processed - instart; - return(-2); - } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; } - else if (d < 0xF0) { c= d & 0x0F; trailing= 2; } - else if (d < 0xF8) { c= d & 0x07; trailing= 3; } - else { - /* no chance for this in UTF-16 */ - outlen = (out - outstart) * 2; - inlen = processed - instart; - return(-2); - } - - if (inend - in < trailing) { - break; - } - - for ( ; trailing; trailing--) { - if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80)) - break; - c <<= 6; - c |= d & 0x3F; - } - - /* assertion: c is a single UTF-4 value */ - if (c < 0x10000) { - if (out >= outend) - break; - if (xmlLittleEndian) { - *out++ = c; - } else { - tmp = (unsigned char *) out; - *tmp = c ; - *(tmp + 1) = c >> 8 ; - out++; - } - } - else if (c < 0x110000) { - if (out+1 >= outend) - break; - c -= 0x10000; - if (xmlLittleEndian) { - *out++ = 0xD800 | (c >> 10); - *out++ = 0xDC00 | (c & 0x03FF); - } else { - tmp1 = 0xD800 | (c >> 10); - tmp = (unsigned char *) out; - *tmp = (unsigned char) tmp1; - *(tmp + 1) = tmp1 >> 8; - out++; - - tmp2 = 0xDC00 | (c & 0x03FF); - tmp = (unsigned char *) out; - *tmp = (unsigned char) tmp2; - *(tmp + 1) = tmp2 >> 8; - out++; - } - } - else - break; - processed = in; - } - outlen = (out - outstart) * 2; - inlen = processed - instart; - return(outlen); -} - -int isUTF8(const char* in_string) { - //fprintf(stdout, "utf8 test-> %s\n", in_string); - int str_bytes = 0; - if (in_string != NULL ) { - str_bytes = strlen(in_string); - } else { - return -1; - } - - bool is_validUTF8 = true; - bool is_high_ascii= false; - - int index = 0; - while (index < str_bytes && is_validUTF8) { - char achar = in_string[index]; - int supplemental_bytes = 0; - - if ( (unsigned char)achar > 0x80) { - is_high_ascii = true; - } - - if ((achar & 0x80) == 0) { // 0xxxxxxx - ++index; - - } else if ((achar & 0xF8) == 0xF0) { // 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx - ++index; - supplemental_bytes = 3; - is_high_ascii = true; - - } else if ((achar & 0xE0) == 0xE0) { // 1110zzzz 10yyyyyy 10xxxxxx - ++index; - supplemental_bytes = 2; - is_high_ascii = true; - - } else if ((achar & 0xE0) == 0xC0) { // 110yyyyy 10xxxxxx - ++index; - supplemental_bytes = 1; - is_high_ascii = true; - - } else { - is_validUTF8 = false; - } - - while (is_validUTF8 && supplemental_bytes--) { - if (index >= str_bytes) { - is_validUTF8 = false; - } else if ((in_string[index++] & 0xC0) != 0x80) { // 10uuzzzz - is_validUTF8 = false; - } - } - } - - if (is_high_ascii) { - return 8; - } else if (is_validUTF8) { - return 1; - } else { - return 0; - } +int UTF8ToUTF16LE(unsigned char *outb, + int outlen, + const unsigned char *in, + int inlen) { + unsigned short *out = (unsigned short *)outb; + const unsigned char *processed = in; + const unsigned char *const instart = in; + unsigned short *outstart = out; + unsigned short *outend; + const unsigned char *inend = in + inlen; + unsigned int c, d; + int trailing; + unsigned char *tmp; + unsigned short tmp1, tmp2; + + /* UTF16LE encoding has no BOM */ + if ((out == NULL) || (outlen == 0) || (inlen == 0)) + return (-1); + if (in == NULL) { + outlen = 0; + inlen = 0; + return (0); + } + outend = out + (outlen / 2); + while (in < inend) { + d = *in++; + if (d < 0x80) { + c = d; + trailing = 0; + } else if (d < 0xC0) { + /* trailing byte in leading position */ + outlen = (out - outstart) * 2; + inlen = processed - instart; + return (-2); + } else if (d < 0xE0) { + c = d & 0x1F; + trailing = 1; + } else if (d < 0xF0) { + c = d & 0x0F; + trailing = 2; + } else if (d < 0xF8) { + c = d & 0x07; + trailing = 3; + } else { + /* no chance for this in UTF-16 */ + outlen = (out - outstart) * 2; + inlen = processed - instart; + return (-2); + } + + if (inend - in < trailing) { + break; + } + + for (; trailing; trailing--) { + if ((in >= inend) || (((d = *in++) & 0xC0) != 0x80)) + break; + c <<= 6; + c |= d & 0x3F; + } + + /* assertion: c is a single UTF-4 value */ + if (c < 0x10000) { + if (out >= outend) + break; + if (xmlLittleEndian) { + *out++ = c; + } else { + tmp = (unsigned char *)out; + *tmp = c; + *(tmp + 1) = c >> 8; + out++; + } + } else if (c < 0x110000) { + if (out + 1 >= outend) + break; + c -= 0x10000; + if (xmlLittleEndian) { + *out++ = 0xD800 | (c >> 10); + *out++ = 0xDC00 | (c & 0x03FF); + } else { + tmp1 = 0xD800 | (c >> 10); + tmp = (unsigned char *)out; + *tmp = (unsigned char)tmp1; + *(tmp + 1) = tmp1 >> 8; + out++; + + tmp2 = 0xDC00 | (c & 0x03FF); + tmp = (unsigned char *)out; + *tmp = (unsigned char)tmp2; + *(tmp + 1) = tmp2 >> 8; + out++; + } + } else + break; + processed = in; + } + outlen = (out - outstart) * 2; + inlen = processed - instart; + return (outlen); +} + +int isUTF8(const char *in_string) { + // fprintf(stdout, "utf8 test-> %s\n", in_string); + int str_bytes = 0; + if (in_string != NULL) { + str_bytes = strlen(in_string); + } else { + return -1; + } + + bool is_validUTF8 = true; + bool is_high_ascii = false; + + int index = 0; + while (index < str_bytes && is_validUTF8) { + char achar = in_string[index]; + int supplemental_bytes = 0; + + if ((unsigned char)achar > 0x80) { + is_high_ascii = true; + } + + if ((achar & 0x80) == 0) { // 0xxxxxxx + ++index; + + } else if ((achar & 0xF8) == 0xF0) { // 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + ++index; + supplemental_bytes = 3; + is_high_ascii = true; + + } else if ((achar & 0xE0) == 0xE0) { // 1110zzzz 10yyyyyy 10xxxxxx + ++index; + supplemental_bytes = 2; + is_high_ascii = true; + + } else if ((achar & 0xE0) == 0xC0) { // 110yyyyy 10xxxxxx + ++index; + supplemental_bytes = 1; + is_high_ascii = true; + + } else { + is_validUTF8 = false; + } + + while (is_validUTF8 && supplemental_bytes--) { + if (index >= str_bytes) { + is_validUTF8 = false; + } else if ((in_string[index++] & 0xC0) != 0x80) { // 10uuzzzz + is_validUTF8 = false; + } + } + } + + if (is_high_ascii) { + return 8; + } else if (is_validUTF8) { + return 1; + } else { + return 0; + } } /*---------------------- utf8_length in_string - pointer to location of a utf8 string - char_limit - either 0 (count all characters) or non-zero (limit utf8 to that character count) + char_limit - either 0 (count all characters) or non-zero (limit utf8 to +that character count) - Because of the lovely way utf8 is aligned, test only the first byte in each. If char_limit is 0, return the number of CHARACTERS in the string, if the - char_limit is not zero (the char_limit will equal utf_string_leghth because of the break), so change gears, save space and just return the byte_count. + Because of the lovely way utf8 is aligned, test only the first byte in each. +If char_limit is 0, return the number of CHARACTERS in the string, if the + char_limit is not zero (the char_limit will equal +utf_string_leghth because of the break), so change gears, save space and just +return the byte_count. ----------------------*/ #include unsigned int utf8_length(const char *in_string, unsigned int char_limit) { - const char *utf8_str = in_string; - unsigned int utf8_string_length = 0; - unsigned int in_str_len = strlen(in_string); - unsigned int byte_count = 0; - unsigned int bytes_in_char = 0; - - if (in_string == NULL) return 0; - - while ( byte_count < in_str_len) { - bytes_in_char = 0; - if ((*utf8_str & 0x80) == 0x00) - bytes_in_char = 1; - else if ((*utf8_str & 0xE0) == 0xC0) - bytes_in_char = 2; - else if ((*utf8_str & 0xF0) == 0xE0) - bytes_in_char = 3; - else if ((*utf8_str & 0xF8) == 0xF0) - bytes_in_char = 4; - - if (bytes_in_char > 0) { - utf8_string_length++; - utf8_str += bytes_in_char; - byte_count += bytes_in_char; - } else { - break; - } - - if (char_limit != 0 && char_limit == utf8_string_length) { - utf8_string_length = byte_count; - break; - } - } - return utf8_string_length; + const char *utf8_str = in_string; + unsigned int utf8_string_length = 0; + unsigned int in_str_len = strlen(in_string); + unsigned int byte_count = 0; + unsigned int bytes_in_char = 0; + + if (in_string == NULL) + return 0; + + while (byte_count < in_str_len) { + bytes_in_char = 0; + if ((*utf8_str & 0x80) == 0x00) + bytes_in_char = 1; + else if ((*utf8_str & 0xE0) == 0xC0) + bytes_in_char = 2; + else if ((*utf8_str & 0xF0) == 0xE0) + bytes_in_char = 3; + else if ((*utf8_str & 0xF8) == 0xF0) + bytes_in_char = 4; + + if (bytes_in_char > 0) { + utf8_string_length++; + utf8_str += bytes_in_char; + byte_count += bytes_in_char; + } else { + break; + } + + if (char_limit != 0 && char_limit == utf8_string_length) { + utf8_string_length = byte_count; + break; + } + } + return utf8_string_length; } -#if defined (_WIN32) && !defined (__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) unsigned char APar_Return_rawutf8_CP(unsigned short cp_bound_glyph) { - unsigned short total_known_points = 0; - unsigned int win32cp = GetConsoleCP(); - - if (win32cp == 437 || win32cp == 850 || win32cp == 852 || win32cp == 855 || win32cp == 858) { - total_known_points = 128; - } else { - if (cp_bound_glyph >= 0x0080) { - exit(win32cp); - } - } - if (cp_bound_glyph < 0x0080) { - return cp_bound_glyph << 0; - } else if (total_known_points) { - if (win32cp == 437) { - for (uint16_t i = 0; i < total_known_points; i++) { - if (cp_bound_glyph == cp437upperbytes[i]) { - return i+128; - } - } - } else if (win32cp == 850) { - for (uint16_t i = 0; i < total_known_points; i++) { - if (cp_bound_glyph == cp850upperbytes[i]) { - return i+128; - } - } - } else if (win32cp == 852) { - for (uint16_t i = 0; i < total_known_points; i++) { - if (cp_bound_glyph == cp852upperbytes[i]) { - return i+128; - } - } - } else if (win32cp == 855) { - for (uint16_t i = 0; i < total_known_points; i++) { - if (cp_bound_glyph == cp855upperbytes[i]) { - return i+128; - } - } - } else if (win32cp == 858) { - for (uint16_t i = 0; i < total_known_points; i++) { - if (cp_bound_glyph == cp858upperbytes[i]) { - return i+128; - } - } - } else { - fprintf(stderr, "AtomicParsley error: this windows codepage(%u) is unsupported.\nProvide the output of the 'CPTester' utility run from the bat script\n", win32cp); - exit(win32cp); - } - } - return 0; -} - -int strip_bogusUTF16toRawUTF8 (unsigned char* out, int inlen, wchar_t* in, int outlen) { - - unsigned char* outstart = out; - unsigned char* outend; - const wchar_t* inend; - const wchar_t* instop; - - if ((out == NULL) || (in == NULL) || (outlen == 0) || (inlen == 0)) - return(-1); - - outend = out + outlen; - inend = in + (inlen); - instop = inend; - - while (in < inend && out < outend - 1) { - *out++ = APar_Return_rawutf8_CP(*in); //*in << 0; - ++in; - } - outlen = out - outstart; - return(outlen); + unsigned short total_known_points = 0; + unsigned int win32cp = GetConsoleCP(); + + if (win32cp == 437 || win32cp == 850 || win32cp == 852 || win32cp == 855 || + win32cp == 858) { + total_known_points = 128; + } else { + if (cp_bound_glyph >= 0x0080) { + exit(win32cp); + } + } + if (cp_bound_glyph < 0x0080) { + return cp_bound_glyph << 0; + } else if (total_known_points) { + if (win32cp == 437) { + for (uint16_t i = 0; i < total_known_points; i++) { + if (cp_bound_glyph == cp437upperbytes[i]) { + return i + 128; + } + } + } else if (win32cp == 850) { + for (uint16_t i = 0; i < total_known_points; i++) { + if (cp_bound_glyph == cp850upperbytes[i]) { + return i + 128; + } + } + } else if (win32cp == 852) { + for (uint16_t i = 0; i < total_known_points; i++) { + if (cp_bound_glyph == cp852upperbytes[i]) { + return i + 128; + } + } + } else if (win32cp == 855) { + for (uint16_t i = 0; i < total_known_points; i++) { + if (cp_bound_glyph == cp855upperbytes[i]) { + return i + 128; + } + } + } else if (win32cp == 858) { + for (uint16_t i = 0; i < total_known_points; i++) { + if (cp_bound_glyph == cp858upperbytes[i]) { + return i + 128; + } + } + } else { + fprintf(stderr, + "AtomicParsley error: this windows codepage(%u) is " + "unsupported.\nProvide the output of the 'CPTester' utility run " + "from the bat script\n", + win32cp); + exit(win32cp); + } + } + return 0; +} + +int strip_bogusUTF16toRawUTF8(unsigned char *out, + int inlen, + wchar_t *in, + int outlen) { + + unsigned char *outstart = out; + unsigned char *outend; + const wchar_t *inend; + const wchar_t *instop; + + if ((out == NULL) || (in == NULL) || (outlen == 0) || (inlen == 0)) + return (-1); + + outend = out + outlen; + inend = in + (inlen); + instop = inend; + + while (in < inend && out < outend - 1) { + *out++ = APar_Return_rawutf8_CP(*in); //*in << 0; + ++in; + } + outlen = out - outstart; + return (outlen); } #endif @@ -898,34 +958,36 @@ limit string to A-Z or a-z ----------------------*/ -int test_conforming_alpha_string(char* in_string) { - int valid_bytes = 0; - int string_len = 0; - char* test_str = in_string; - if (in_string != NULL ) { - string_len = strlen(in_string); - } else { - return -1; - } - - while (valid_bytes < string_len) { - if ( (*test_str >= 65 && *test_str <= 90) || (*test_str >= 97 && *test_str <= 122) || *test_str == 95 || (*test_str >= 48 && *test_str <= 57) ) { - valid_bytes++; - } else { - break; - } - test_str++; - } - return valid_bytes; -} - -bool test_limited_ascii(char* in_string, unsigned int str_len) { - char* test_str = in_string; - while (test_str < in_string+str_len) { - if ( *test_str < 32 || *test_str > 126) { - return false; - } - test_str++; - } - return true; +int test_conforming_alpha_string(char *in_string) { + int valid_bytes = 0; + int string_len = 0; + char *test_str = in_string; + if (in_string != NULL) { + string_len = strlen(in_string); + } else { + return -1; + } + + while (valid_bytes < string_len) { + if ((*test_str >= 65 && *test_str <= 90) || + (*test_str >= 97 && *test_str <= 122) || *test_str == 95 || + (*test_str >= 48 && *test_str <= 57)) { + valid_bytes++; + } else { + break; + } + test_str++; + } + return valid_bytes; +} + +bool test_limited_ascii(char *in_string, unsigned int str_len) { + char *test_str = in_string; + while (test_str < in_string + str_len) { + if (*test_str < 32 || *test_str > 126) { + return false; + } + test_str++; + } + return true; } diff -Nru atomicparsley-0.9.6/src/id3v2.cpp atomicparsley-20210715.151551.e7ad03a/src/id3v2.cpp --- atomicparsley-0.9.6/src/id3v2.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/id3v2.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - id3v2.cpp - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,12 +10,12 @@ 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 + 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 2006-2007 puck_lock + Copyright (C)2006-2007 puck_lock with contributions from others; see the CREDITS file */ //==================================================================// @@ -24,2165 +24,3096 @@ #include "CDtoc.h" #include "id3v2defs.h" -ID3v2Tag* GlobalID3Tag = NULL; +ID3v2Tag *GlobalID3Tag = NULL; -//prefs +// prefs uint8_t AtomicParsley_ID3v2Tag_MajorVersion = 4; uint8_t AtomicParsley_ID3v2Tag_RevisionVersion = 0; uint8_t AtomicParsley_ID3v2Tag_Flags = 0; -bool ID3v2Tag_Flag_Footer = false; //bit4; MPEG-4 'ID32' requires this to be false -bool ID3v2Tag_Flag_Experimental = true; //bit5 -bool ID3v2Tag_Flag_ExtendedHeader = true; //bit6 -bool ID3v2Tag_Flag_Unsyncronization = false; //bit7 +bool ID3v2Tag_Flag_Footer = + false; // bit4; MPEG-4 'ID32' requires this to be false +bool ID3v2Tag_Flag_Experimental = true; // bit5 +bool ID3v2Tag_Flag_ExtendedHeader = true; // bit6 +bool ID3v2Tag_Flag_Unsyncronization = false; // bit7 /////////////////////////////////////////////////////////////////////////////////////// -// id3 number conversion functions // +// id3 number conversion functions // /////////////////////////////////////////////////////////////////////////////////////// -uint64_t syncsafeXX_to_UInt64(char* syncsafe_int, uint8_t syncsafe_len) { - if (syncsafe_len == 5) { - if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || syncsafe_int[4] & 0x80) return 0; - return ((uint64_t)syncsafe_int[0] << 28) | (syncsafe_int[1] << 21) | (syncsafe_int[2] << 14) | (syncsafe_int[3] << 7) | syncsafe_int[4]; - } else if (syncsafe_len == 6) { - if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80) return 0; - return ((uint64_t)syncsafe_int[0] << 35) | ((uint64_t)syncsafe_int[1] << 28) | (syncsafe_int[2] << 21) | (syncsafe_int[3] << 14) | - (syncsafe_int[4] << 7) | syncsafe_int[5]; - } else if (syncsafe_len == 7) { - if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || - syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 || syncsafe_int[6] & 0x80) return 0; - return ((uint64_t)syncsafe_int[0] << 42) | ((uint64_t)syncsafe_int[1] << 35) | ((uint64_t)syncsafe_int[2] << 28) | (syncsafe_int[3] << 21) | - (syncsafe_int[3] << 14) | (syncsafe_int[5] << 7) | syncsafe_int[6]; - } else if (syncsafe_len == 8) { - if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || - syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 || syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80) return 0; - return ((uint64_t)syncsafe_int[0] << 49) | ((uint64_t)syncsafe_int[1] << 42) | ((uint64_t)syncsafe_int[2] << 35) | ((uint64_t)syncsafe_int[3] << 28) | - (syncsafe_int[4] << 21) | (syncsafe_int[5] << 14) | (syncsafe_int[6] << 7) | syncsafe_int[7]; - } else if (syncsafe_len == 9) { - if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || syncsafe_int[4] & 0x80 || - syncsafe_int[5] & 0x80 || syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80 || syncsafe_int[8] & 0x80) return 0; - return ((uint64_t)syncsafe_int[0] << 56) | ((uint64_t)syncsafe_int[1] << 49) | ((uint64_t)syncsafe_int[2] << 42) | ((uint64_t)syncsafe_int[3] << 35) | - ((uint64_t)syncsafe_int[4] << 28) | (syncsafe_int[5] << 21) | (syncsafe_int[6] << 14) | (syncsafe_int[7] << 7) | syncsafe_int[8]; - } - return 0; -} - -uint32_t syncsafe32_to_UInt32(char* syncsafe_int) { - if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80) return 0; - return (syncsafe_int[0] << 21) | (syncsafe_int[1] << 14) | (syncsafe_int[2] << 7) | syncsafe_int[3]; -} - -uint16_t syncsafe16_to_UInt16(char* syncsafe_int) { - if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80) return 0; - return (syncsafe_int[0] << 7) | syncsafe_int[1]; -} - -void convert_to_syncsafe32(uint32_t in_uint, char* buffer) { - buffer[0] = (in_uint >> 21) & 0x7F; - buffer[1] = (in_uint >> 14) & 0x7F; - buffer[2] = (in_uint >> 7) & 0x7F; - buffer[3] = (in_uint >> 0) & 0x7F; - return; -} - -uint8_t convert_to_syncsafeXX(uint64_t in_uint, char* buffer) { - if -#if defined (_MSC_VER) - (in_uint <= (uint64_t)34359738367) +uint64_t syncsafeXX_to_UInt64(char *syncsafe_int, uint8_t syncsafe_len) { + if (syncsafe_len == 5) { + if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || + syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || + syncsafe_int[4] & 0x80) + return 0; + return ((uint64_t)syncsafe_int[0] << 28) | (syncsafe_int[1] << 21) | + (syncsafe_int[2] << 14) | (syncsafe_int[3] << 7) | syncsafe_int[4]; + } else if (syncsafe_len == 6) { + if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || + syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || + syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80) + return 0; + return ((uint64_t)syncsafe_int[0] << 35) | + ((uint64_t)syncsafe_int[1] << 28) | (syncsafe_int[2] << 21) | + (syncsafe_int[3] << 14) | (syncsafe_int[4] << 7) | syncsafe_int[5]; + } else if (syncsafe_len == 7) { + if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || + syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || + syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 || + syncsafe_int[6] & 0x80) + return 0; + return ((uint64_t)syncsafe_int[0] << 42) | + ((uint64_t)syncsafe_int[1] << 35) | + ((uint64_t)syncsafe_int[2] << 28) | (syncsafe_int[3] << 21) | + (syncsafe_int[3] << 14) | (syncsafe_int[5] << 7) | syncsafe_int[6]; + } else if (syncsafe_len == 8) { + if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || + syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || + syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 || + syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80) + return 0; + return ((uint64_t)syncsafe_int[0] << 49) | + ((uint64_t)syncsafe_int[1] << 42) | + ((uint64_t)syncsafe_int[2] << 35) | + ((uint64_t)syncsafe_int[3] << 28) | (syncsafe_int[4] << 21) | + (syncsafe_int[5] << 14) | (syncsafe_int[6] << 7) | syncsafe_int[7]; + } else if (syncsafe_len == 9) { + if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || + syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || + syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 || + syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80 || + syncsafe_int[8] & 0x80) + return 0; + return ((uint64_t)syncsafe_int[0] << 56) | + ((uint64_t)syncsafe_int[1] << 49) | + ((uint64_t)syncsafe_int[2] << 42) | + ((uint64_t)syncsafe_int[3] << 35) | + ((uint64_t)syncsafe_int[4] << 28) | (syncsafe_int[5] << 21) | + (syncsafe_int[6] << 14) | (syncsafe_int[7] << 7) | syncsafe_int[8]; + } + return 0; +} + +uint32_t syncsafe32_to_UInt32(char *syncsafe_int) { + if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || + syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80) + return 0; + return (syncsafe_int[0] << 21) | (syncsafe_int[1] << 14) | + (syncsafe_int[2] << 7) | syncsafe_int[3]; +} + +uint16_t syncsafe16_to_UInt16(char *syncsafe_int) { + if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80) + return 0; + return (syncsafe_int[0] << 7) | syncsafe_int[1]; +} + +void convert_to_syncsafe32(uint32_t in_uint, char *buffer) { + buffer[0] = (in_uint >> 21) & 0x7F; + buffer[1] = (in_uint >> 14) & 0x7F; + buffer[2] = (in_uint >> 7) & 0x7F; + buffer[3] = (in_uint >> 0) & 0x7F; + return; +} + +uint8_t convert_to_syncsafeXX(uint64_t in_uint, char *buffer) { + if +#if defined(_MSC_VER) + (in_uint <= (uint64_t)34359738367) #else - (in_uint <= 34359738367ULL) + (in_uint <= 34359738367ULL) #endif - { - buffer[0] = (in_uint >> 28) & 0x7F; - buffer[1] = (in_uint >> 21) & 0x7F; - buffer[2] = (in_uint >> 14) & 0x7F; - buffer[3] = (in_uint >> 7) & 0x7F; - buffer[4] = (in_uint >> 0) & 0x7F; - return 5; -#if defined (_MSC_VER) - } else if (in_uint <= (uint64_t)4398046511103) { + { + buffer[0] = (in_uint >> 28) & 0x7F; + buffer[1] = (in_uint >> 21) & 0x7F; + buffer[2] = (in_uint >> 14) & 0x7F; + buffer[3] = (in_uint >> 7) & 0x7F; + buffer[4] = (in_uint >> 0) & 0x7F; + return 5; +#if defined(_MSC_VER) + } else if (in_uint <= (uint64_t)4398046511103) { #else - } else if (in_uint <= 4398046511103ULL) { + } else if (in_uint <= 4398046511103ULL) { #endif - buffer[0] = (in_uint >> 35) & 0x7F; - buffer[1] = (in_uint >> 28) & 0x7F; - buffer[2] = (in_uint >> 21) & 0x7F; - buffer[3] = (in_uint >> 14) & 0x7F; - buffer[4] = (in_uint >> 7) & 0x7F; - buffer[5] = (in_uint >> 0) & 0x7F; - return 6; -#if defined (_MSC_VER) - } else if (in_uint <= (uint64_t)562949953421311) { + buffer[0] = (in_uint >> 35) & 0x7F; + buffer[1] = (in_uint >> 28) & 0x7F; + buffer[2] = (in_uint >> 21) & 0x7F; + buffer[3] = (in_uint >> 14) & 0x7F; + buffer[4] = (in_uint >> 7) & 0x7F; + buffer[5] = (in_uint >> 0) & 0x7F; + return 6; +#if defined(_MSC_VER) + } else if (in_uint <= (uint64_t)562949953421311) { #else - } else if (in_uint <= 562949953421311ULL) { + } else if (in_uint <= 562949953421311ULL) { #endif - buffer[0] = (in_uint >> 42) & 0x7F; - buffer[1] = (in_uint >> 35) & 0x7F; - buffer[2] = (in_uint >> 28) & 0x7F; - buffer[3] = (in_uint >> 21) & 0x7F; - buffer[4] = (in_uint >> 14) & 0x7F; - buffer[5] = (in_uint >> 7) & 0x7F; - buffer[6] = (in_uint >> 0) & 0x7F; - return 7; -#if defined (_MSC_VER) - } else if (in_uint <= (uint64_t)72057594037927935) { + buffer[0] = (in_uint >> 42) & 0x7F; + buffer[1] = (in_uint >> 35) & 0x7F; + buffer[2] = (in_uint >> 28) & 0x7F; + buffer[3] = (in_uint >> 21) & 0x7F; + buffer[4] = (in_uint >> 14) & 0x7F; + buffer[5] = (in_uint >> 7) & 0x7F; + buffer[6] = (in_uint >> 0) & 0x7F; + return 7; +#if defined(_MSC_VER) + } else if (in_uint <= (uint64_t)72057594037927935) { #else - } else if (in_uint <= 72057594037927935ULL) { + } else if (in_uint <= 72057594037927935ULL) { #endif - buffer[0] = (in_uint >> 49) & 0x7F; - buffer[1] = (in_uint >> 42) & 0x7F; - buffer[2] = (in_uint >> 35) & 0x7F; - buffer[3] = (in_uint >> 28) & 0x7F; - buffer[4] = (in_uint >> 21) & 0x7F; - buffer[5] = (in_uint >> 14) & 0x7F; - buffer[6] = (in_uint >> 7) & 0x7F; - buffer[7] = (in_uint >> 0) & 0x7F; - return 8; -#if defined (_MSC_VER) - } else if (in_uint <= (uint64_t)9223372036854775807) { + buffer[0] = (in_uint >> 49) & 0x7F; + buffer[1] = (in_uint >> 42) & 0x7F; + buffer[2] = (in_uint >> 35) & 0x7F; + buffer[3] = (in_uint >> 28) & 0x7F; + buffer[4] = (in_uint >> 21) & 0x7F; + buffer[5] = (in_uint >> 14) & 0x7F; + buffer[6] = (in_uint >> 7) & 0x7F; + buffer[7] = (in_uint >> 0) & 0x7F; + return 8; +#if defined(_MSC_VER) + } else if (in_uint <= (uint64_t)9223372036854775807) { #else - } else if (in_uint <= 9223372036854775807ULL) { //that is some hardcore lovin' + } else if (in_uint <= 9223372036854775807ULL) { // that is some hardcore + // lovin' #endif - buffer[0] = (in_uint >> 56) & 0x7F; - buffer[1] = (in_uint >> 49) & 0x7F; - buffer[2] = (in_uint >> 42) & 0x7F; - buffer[3] = (in_uint >> 35) & 0x7F; - buffer[4] = (in_uint >> 28) & 0x7F; - buffer[5] = (in_uint >> 21) & 0x7F; - buffer[6] = (in_uint >> 14) & 0x7F; - buffer[7] = (in_uint >> 7) & 0x7F; - buffer[8] = (in_uint >> 0) & 0x7F; - return 9; - } - return 0; -} - -uint32_t UInt24FromBigEndian(const char *string) { //v2.2 frame lengths - return ((0 << 24) | ((string[0] & 0xff) << 16) | ((string[1] & 0xff) << 8) | (string[2] & 0xff) << 0); -} - -uint32_t ID3v2_desynchronize(char* buffer, uint32_t bufferlen) { - char* buf_ptr = buffer; - uint32_t desync_count = 0; - - for (uint32_t i = 0; i < bufferlen; i++) { - if ((unsigned char)buffer[i] == 0xFF && (unsigned char)buffer[i+1] == 0x00) { - buf_ptr[desync_count] = buffer[i]; - i++; - } else { - buf_ptr[desync_count] = buffer[i]; - } - desync_count++; - } - return desync_count; + buffer[0] = (in_uint >> 56) & 0x7F; + buffer[1] = (in_uint >> 49) & 0x7F; + buffer[2] = (in_uint >> 42) & 0x7F; + buffer[3] = (in_uint >> 35) & 0x7F; + buffer[4] = (in_uint >> 28) & 0x7F; + buffer[5] = (in_uint >> 21) & 0x7F; + buffer[6] = (in_uint >> 14) & 0x7F; + buffer[7] = (in_uint >> 7) & 0x7F; + buffer[8] = (in_uint >> 0) & 0x7F; + return 9; + } + return 0; +} + +uint32_t UInt24FromBigEndian(const char *string) { // v2.2 frame lengths + return ((0 << 24) | ((string[0] & 0xff) << 16) | ((string[1] & 0xff) << 8) | + (string[2] & 0xff) << 0); +} + +uint32_t ID3v2_desynchronize(char *buffer, uint32_t bufferlen) { + char *buf_ptr = buffer; + uint32_t desync_count = 0; + + for (uint32_t i = 0; i < bufferlen; i++) { + if ((unsigned char)buffer[i] == 0xFF && + (unsigned char)buffer[i + 1] == 0x00) { + buf_ptr[desync_count] = buffer[i]; + i++; + } else { + buf_ptr[desync_count] = buffer[i]; + } + desync_count++; + } + return desync_count; } /////////////////////////////////////////////////////////////////////////////////////// -// bit tests & generic functions // +// bit tests & generic functions // /////////////////////////////////////////////////////////////////////////////////////// -bool ID3v2_PaddingTest(char* buffer) { - if (buffer[0] & 0x00 || buffer[1] & 0x00 || buffer[2] & 0x00 || buffer[3] & 0x00) return true; - return false; +bool ID3v2_PaddingTest(char *buffer) { + if (buffer[0] & 0x00 || buffer[1] & 0x00 || buffer[2] & 0x00 || + buffer[3] & 0x00) + return true; + return false; } -bool ID3v2_TestFrameID_NonConformance(char* frameid) { - for (uint8_t i=0; i < 4; i++) { - if ( !((frameid[i] >= '0' && frameid[i] <= '9') || ( frameid[i] >= 'A' && frameid[i] <= 'Z' )) ) { - return true; - } - } - return false; +bool ID3v2_TestFrameID_NonConformance(char *frameid) { + for (uint8_t i = 0; i < 4; i++) { + if (!((frameid[i] >= '0' && frameid[i] <= '9') || + (frameid[i] >= 'A' && frameid[i] <= 'Z'))) { + return true; + } + } + return false; } bool ID3v2_TestTagFlag(uint8_t TagFlag, uint8_t TagBit) { - if (TagFlag & TagBit) return true; - return false; + if (TagFlag & TagBit) + return true; + return false; } bool ID3v2_TestFrameFlag(uint16_t FrameFlag, uint16_t FrameBit) { - if (FrameFlag & FrameBit) return true; - return false; + if (FrameFlag & FrameBit) + return true; + return false; } -uint8_t TextField_TestBOM(char* astring) { - if (((unsigned char*)astring)[0] == 0xFE && ((unsigned char*)astring)[1] == 0xFF) return 13; //13 looks like a B for BE - if (((unsigned char*)astring)[0] == 0xFF && ((unsigned char*)astring)[1] == 0xFE) return 1; //1 looks like a l for LE - return 0; +uint8_t TextField_TestBOM(char *astring) { + if (((unsigned char *)astring)[0] == 0xFE && + ((unsigned char *)astring)[1] == 0xFF) + return 13; // 13 looks like a B for BE + if (((unsigned char *)astring)[0] == 0xFF && + ((unsigned char *)astring)[1] == 0xFE) + return 1; // 1 looks like a l for LE + return 0; } void APar_LimitBufferRange(uint32_t max_allowed, uint32_t target_amount) { - if (target_amount > max_allowed) { - fprintf(stderr, "AtomicParsley error: insufficient memory to process ID3 tags (%" PRIu32 ">%" PRIu32 "). Exiting.\n", target_amount, max_allowed); - exit( target_amount - max_allowed ); - } - return; -} - -void APar_ValidateNULLTermination8bit(ID3v2Fields* this_field) { - if (this_field->field_string[0] == 0) { - this_field->field_length = 1; - } else if (this_field->field_string[this_field->field_length-1] != 0) { - this_field->field_length += 1; - } - return; -} - -void APar_ValidateNULLTermination16bit(ID3v2Fields* this_field, uint8_t encoding) { - if (this_field->field_string[0] == 0 && this_field->field_string[1] == 0) { - this_field->field_length = 2; - if (encoding == TE_UTF16LE_WITH_BOM) { - if ( ((uint8_t)(this_field->field_string[0]) != 0xFF && (uint8_t)(this_field->field_string[1]) != 0xFE) || - ((uint8_t)(this_field->field_string[0]) != 0xFE && (uint8_t)(this_field->field_string[1]) != 0xFF) ) { - memcpy(this_field->field_string, "\xFF\xFE", 2); - this_field->field_length = 4; - } - } - } else if (this_field->field_string[this_field->field_length-2] != 0 && this_field->field_string[this_field->field_length-1] != 0) { - this_field->field_length += 2; - } - return; + if (target_amount > max_allowed) { + fprintf( + stderr, + "AtomicParsley error: insufficient memory to process ID3 tags (%" PRIu32 + ">%" PRIu32 "). Exiting.\n", + target_amount, + max_allowed); + exit(target_amount - max_allowed); + } + return; +} + +void APar_ValidateNULLTermination8bit(ID3v2Fields *this_field) { + if (this_field->field_string[0] == 0) { + this_field->field_length = 1; + } else if (this_field->field_string[this_field->field_length - 1] != 0) { + this_field->field_length += 1; + } + return; +} + +void APar_ValidateNULLTermination16bit(ID3v2Fields *this_field, + uint8_t encoding) { + if (this_field->field_string[0] == 0 && this_field->field_string[1] == 0) { + this_field->field_length = 2; + if (encoding == TE_UTF16LE_WITH_BOM) { + if (((uint8_t)(this_field->field_string[0]) != 0xFF && + (uint8_t)(this_field->field_string[1]) != 0xFE) || + ((uint8_t)(this_field->field_string[0]) != 0xFE && + (uint8_t)(this_field->field_string[1]) != 0xFF)) { + memcpy(this_field->field_string, "\xFF\xFE", 2); + this_field->field_length = 4; + } + } + } else if (this_field->field_string[this_field->field_length - 2] != 0 && + this_field->field_string[this_field->field_length - 1] != 0) { + this_field->field_length += 2; + } + return; } bool APar_EvalFrame_for_Field(int frametype, int fieldtype) { - uint8_t frametype_idx = GetFrameCompositionDescription(frametype); - - for (uint8_t fld_i = 0; fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount; fld_i++) { - if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == fieldtype) { - return true; - } - } - return false; -} + uint8_t frametype_idx = GetFrameCompositionDescription(frametype); -uint8_t TestCharInRange(uint8_t testchar, uint8_t lowerlimit, uint8_t upperlimit) { - if (testchar >= lowerlimit && testchar <= upperlimit) { - return 1; - } - return 0; + for (uint8_t fld_i = 0; + fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount; + fld_i++) { + if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == + fieldtype) { + return true; + } + } + return false; +} + +uint8_t +TestCharInRange(uint8_t testchar, uint8_t lowerlimit, uint8_t upperlimit) { + if (testchar >= lowerlimit && testchar <= upperlimit) { + return 1; + } + return 0; } uint8_t ImageListMembers() { - return (uint8_t)(sizeof(ImageList)/sizeof(*ImageList)); + return (uint8_t)(sizeof(ImageList) / sizeof(*ImageList)); } /////////////////////////////////////////////////////////////////////////////////////// -// test functions // +// test functions // /////////////////////////////////////////////////////////////////////////////////////// -void WriteZlibData(char* buffer, uint32_t buff_len) { - char* indy_atom_path = (char *)malloc(sizeof(char)*MAXPATHLEN); //this malloc can escape memset because its only for in-house testing - strcat(indy_atom_path, "/Users/"); - strcat(indy_atom_path, getenv("USER") ); - strcat(indy_atom_path, "/Desktop/id3framedata.txt"); - - FILE* test_file = fopen(indy_atom_path, "wb"); - if (test_file != NULL) { - - fwrite(buffer, (size_t)buff_len, 1, test_file); - fclose(test_file); - } - free(indy_atom_path); - return; +void WriteZlibData(char *buffer, uint32_t buff_len) { + char *indy_atom_path = (char *)malloc( + sizeof(char) * MAXPATHLEN); // this malloc can escape memset because its + // only for in-house testing + strcat(indy_atom_path, "/Users/"); + strcat(indy_atom_path, getenv("USER")); + strcat(indy_atom_path, "/Desktop/id3framedata.txt"); + + FILE *test_file = fopen(indy_atom_path, "wb"); + if (test_file != NULL) { + + fwrite(buffer, (size_t)buff_len, 1, test_file); + fclose(test_file); + } + free(indy_atom_path); + return; } /////////////////////////////////////////////////////////////////////////////////////// -// cli functions // +// cli functions // /////////////////////////////////////////////////////////////////////////////////////// -static const char* ReturnFrameTypeStr(int frametype) { - if (frametype == ID3_TEXT_FRAME) { - return "text frame "; - } else if (frametype == ID3_TEXT_FRAME_USERDEF) { - return "user defined text frame"; - } else if (frametype == ID3_URL_FRAME) { - return "url frame "; - } else if (frametype == ID3_URL_FRAME_USERDEF) { - return "user defined url frame "; - } else if (frametype == ID3_UNIQUE_FILE_ID_FRAME) { - return "file ID "; - } else if (frametype == ID3_CD_ID_FRAME ) { - return "AudioCD ID frame "; - } else if (frametype == ID3_DESCRIBED_TEXT_FRAME) { - return "described text frame "; - } else if (frametype == ID3_ATTACHED_PICTURE_FRAME) { - return "picture frame "; - } else if (frametype == ID3_ATTACHED_OBJECT_FRAME) { - return "encapuslated object frm"; - } else if (frametype == ID3_GROUP_ID_FRAME) { - return "group ID frame "; - } else if (frametype == ID3_SIGNATURE_FRAME) { - return "signature frame "; - } else if (frametype == ID3_PRIVATE_FRAME) { - return "private frame "; - } else if (frametype == ID3_PLAYCOUNTER_FRAME) { - return "playcounter "; - } else if (frametype == ID3_POPULAR_FRAME) { - return "popularimeter "; - } - return ""; -} +static const char *ReturnFrameTypeStr(int frametype) { + if (frametype == ID3_TEXT_FRAME) { + return "text frame "; + } else if (frametype == ID3_TEXT_FRAME_USERDEF) { + return "user defined text frame"; + } else if (frametype == ID3_URL_FRAME) { + return "url frame "; + } else if (frametype == ID3_URL_FRAME_USERDEF) { + return "user defined url frame "; + } else if (frametype == ID3_UNIQUE_FILE_ID_FRAME) { + return "file ID "; + } else if (frametype == ID3_CD_ID_FRAME) { + return "AudioCD ID frame "; + } else if (frametype == ID3_DESCRIBED_TEXT_FRAME) { + return "described text frame "; + } else if (frametype == ID3_ATTACHED_PICTURE_FRAME) { + return "picture frame "; + } else if (frametype == ID3_ATTACHED_OBJECT_FRAME) { + return "encapuslated object frm"; + } else if (frametype == ID3_GROUP_ID_FRAME) { + return "group ID frame "; + } else if (frametype == ID3_SIGNATURE_FRAME) { + return "signature frame "; + } else if (frametype == ID3_PRIVATE_FRAME) { + return "private frame "; + } else if (frametype == ID3_PLAYCOUNTER_FRAME) { + return "playcounter "; + } else if (frametype == ID3_POPULAR_FRAME) { + return "popularimeter "; + } + return ""; +} void ListID3FrameIDstrings() { - const char* frametypestr = NULL; - const char* presetpadding = NULL; - uint16_t total_known_frames = (uint16_t)(sizeof(KnownFrames)/sizeof(*KnownFrames)); - fprintf(stdout, "ID3v2.4 Implemented Frames:\nframeID type alias Description\n--------------------------------------------------------------------------\n"); - for (uint16_t i = 1; i < total_known_frames; i++) { - if (strlen(KnownFrames[i].ID3V2p4_FrameID) != 4) continue; - frametypestr = ReturnFrameTypeStr(KnownFrames[i].ID3v2_FrameType); - - int strpad = 12 - strlen(KnownFrames[i].CLI_frameIDpreset); - if (strpad == 12) { - presetpadding = " "; - } else if (strpad == 11) { - presetpadding = " "; - } else if (strpad == 10) { - presetpadding = " "; - } else if (strpad == 9) { - presetpadding = " "; - } else if (strpad == 8) { - presetpadding = " "; - } else if (strpad == 7) { - presetpadding = " "; - } else if (strpad == 6) { - presetpadding = " "; - } else if (strpad == 5) { - presetpadding = " "; - } else if (strpad == 4) { - presetpadding = " "; - } else if (strpad == 3) { - presetpadding = " "; - } else if (strpad == 2) { - presetpadding = " "; - } else if (strpad == 1) { - presetpadding = " "; - } else if (strpad <= 0) { - presetpadding = ""; - } - - fprintf(stdout, "%s %s %s%s | %s\n", KnownFrames[i].ID3V2p4_FrameID, frametypestr, KnownFrames[i].CLI_frameIDpreset, presetpadding, KnownFrames[i].ID3V2_FrameDescription); - } - fprintf(stdout, -"--------------------------------------------------------------------------\n" -"For each frame type, these parameters are available:\n" -" text frames: (str) [encoding]\n" -" user defined text frame : (str) [desc=(str)] [encoding]\n" -" url frame : (url)\n" -" user defined url frame : (url) [desc=(str)] [encoding]\n" -" file ID frame : (owner) [uniqueID={\"randomUUIDstamp\",(str)}]\n" -#if defined (DARWIN_PLATFORM) -" AudioCD ID frame : disk(num)\n" -#elif defined (HAVE_LINUX_CDROM_H) -" AudioCD ID frame : (/path)\n" -#elif defined (_WIN32) -" AudioCD ID frame : (letter)\n" + const char *frametypestr = NULL; + const char *presetpadding = NULL; + uint16_t total_known_frames = + (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames)); + fprintf(stdout, + "ID3v2.4 Implemented Frames:\nframeID type " + " alias " + "Description\n-----------------------------------------------" + "---------------------------\n"); + for (uint16_t i = 1; i < total_known_frames; i++) { + if (strlen(KnownFrames[i].ID3V2p4_FrameID) != 4) + continue; + frametypestr = ReturnFrameTypeStr(KnownFrames[i].ID3v2_FrameType); + + int strpad = 12 - strlen(KnownFrames[i].CLI_frameIDpreset); + if (strpad == 12) { + presetpadding = " "; + } else if (strpad == 11) { + presetpadding = " "; + } else if (strpad == 10) { + presetpadding = " "; + } else if (strpad == 9) { + presetpadding = " "; + } else if (strpad == 8) { + presetpadding = " "; + } else if (strpad == 7) { + presetpadding = " "; + } else if (strpad == 6) { + presetpadding = " "; + } else if (strpad == 5) { + presetpadding = " "; + } else if (strpad == 4) { + presetpadding = " "; + } else if (strpad == 3) { + presetpadding = " "; + } else if (strpad == 2) { + presetpadding = " "; + } else if (strpad == 1) { + presetpadding = " "; + } else if (strpad <= 0) { + presetpadding = ""; + } + + fprintf(stdout, + "%s %s %s%s | %s\n", + KnownFrames[i].ID3V2p4_FrameID, + frametypestr, + KnownFrames[i].CLI_frameIDpreset, + presetpadding, + KnownFrames[i].ID3V2_FrameDescription); + } + fprintf( + stdout, + "------------------------------------------------------------------------" + "--\n" + "For each frame type, these parameters are available:\n" + " text frames: (str) [encoding]\n" + " user defined text frame : (str) [desc=(str)] [encoding]\n" + " url frame : (url)\n" + " user defined url frame : (url) [desc=(str)] [encoding]\n" + " file ID frame : (owner) " + "[uniqueID={\"randomUUIDstamp\",(str)}]\n" +#if defined(__APPLE__) + " AudioCD ID frame : disk(num)\n" +#elif defined(__linux__) + " AudioCD ID frame : (/path)\n" +#elif defined(_WIN32) + " AudioCD ID frame : (letter)\n" #endif -" described text frame : (str) [desc=(str)] [encoding]\n" -" picture frame : (/path) [desc=(str)] [mimetype=(str)] [imagetype=(hex)] [encoding]\n" -" encapuslated object frame : (/path) [desc=(str)] [mimetype=(str)] [filename={\"FILENAMESTAMP\",(str)}] [encoding]\n" -" group ID frame : (owner) groupsymbol=(hex) [data=(str)]\n" -" signature frame : (str) groupsymbol=(hex)\n" -" private frame : (owner) data=(str)\n" -" playcounter : (num or \"+1\")\n" -" popularimeter : (owner) rating=(1...255) [counter=(num or \"+1\")]\n" -"\n" -" Legend:\n" -" parameters in brackets[] signal an optional parameter, parens() signal a required parameter\n" -" [encoding] may be one either the default UTF8, or one of { LATIN1 UTF16BE UTF16LE }\n" -" (str) signals a string - like \"Suzie\"\n" -" (num) means a number; +1 will increment a counter by 1; (hex) means a hexadecimal number - like 0x11)\n" -" (url) menas a url, in string form; (owner) means a url/email string\n" -" uniqueID=randomUUIDstamp will create a high quality random uuid\n" -" filename=FILENAMESTAMP will embed the name of the file given in the /path for GEOB\n" -"\n" -" All frames also take additional parameters:\n" -" [{root,track=(num)}] specifies file level, track level or (default) movie level for an ID32 atom\n" -" [compress] compresses the given frame using zlib deflate compression\n" -" [groupsymbol=(num)] associates a frame with a GRID frame of the same group symbol\n" -" [lang=(3char)] (default='eng') sets the language/ID32 atom to which the frame belongs\n" -" use AP --languages-list to see a list of available languages\n" -); - - - return; + " described text frame : (str) [desc=(str)] [encoding]\n" + " picture frame : (/path) [desc=(str)] [mimetype=(str)] " + "[imagetype=(hex)] [encoding]\n" + " encapuslated object frame : (/path) [desc=(str)] [mimetype=(str)] " + "[filename={\"FILENAMESTAMP\",(str)}] [encoding]\n" + " group ID frame : (owner) groupsymbol=(hex) [data=(str)]\n" + " signature frame : (str) groupsymbol=(hex)\n" + " private frame : (owner) data=(str)\n" + " playcounter : (num or \"+1\")\n" + " popularimeter : (owner) rating=(1...255) [counter=(num " + "or \"+1\")]\n" + "\n" + " Legend:\n" + " parameters in brackets[] signal an optional parameter, parens() " + "signal a required parameter\n" + " [encoding] may be one either the default UTF8, or one of { LATIN1 " + "UTF16BE UTF16LE }\n" + " (str) signals a string - like \"Suzie\"\n" + " (num) means a number; +1 will increment a counter by 1; (hex) " + "means a hexadecimal number - like 0x11)\n" + " (url) menas a url, in string form; (owner) means a url/email " + "string\n" + " uniqueID=randomUUIDstamp will create a high quality random uuid\n" + " filename=FILENAMESTAMP will embed the name of the file given in " + "the /path for GEOB\n" + "\n" + " All frames also take additional parameters:\n" + " [{root,track=(num)}] specifies file level, track level or " + "(default) movie level for an ID32 atom\n" + " [compress] compresses the given frame using zlib deflate " + "compression\n" + " [groupsymbol=(num)] associates a frame with a GRID frame of the " + "same group symbol\n" + " [lang=(3char)] (default='eng') sets the language/ID32 atom to " + "which the frame belongs\n" + " use AP --languages-list to see a list of available " + "languages\n"); + + return; } void List_imagtype_strings() { - uint8_t total_imgtyps = (uint8_t)(sizeof(ImageTypeList)/sizeof(*ImageTypeList)); - fprintf(stdout, "These 'image types' are used to identify pictures embedded in 'APIC' ID3 tags:\n usage is \"AP --ID3Tag APIC /path.jpg --imagetype=\"str\"\n str can be either the hex listing *or* the full string\n default is 0x00 - meaning 'Other'\n Hex Full String\n ----------------------------\n"); - for (uint8_t i=0; i < total_imgtyps; i++) { - fprintf(stdout, " %s \"%s\"\n", ImageTypeList[i].hexstring, ImageTypeList[i].imagetype_str); - } - return; -} - -const char* ConvertCLIFrameStr_TO_frameID(const char* frame_str) { - const char* discovered_frameID = NULL; - uint16_t total_known_frames = (uint16_t)(sizeof(KnownFrames)/sizeof(*KnownFrames)); - - for (uint16_t i = 0; i < total_known_frames; i++) { - if (strcmp(KnownFrames[i].CLI_frameIDpreset, frame_str) == 0) { - if (AtomicParsley_ID3v2Tag_MajorVersion == 2) discovered_frameID = KnownFrames[i].ID3V2p2_FrameID; - if (AtomicParsley_ID3v2Tag_MajorVersion == 3) discovered_frameID = KnownFrames[i].ID3V2p3_FrameID; - if (AtomicParsley_ID3v2Tag_MajorVersion == 4) discovered_frameID = KnownFrames[i].ID3V2p4_FrameID; - - if (strlen(discovered_frameID) == 0) discovered_frameID = NULL; - break; - } - } - return discovered_frameID; -} - -//0 = description -//1 = mimetype -//2 = type + uint8_t total_imgtyps = + (uint8_t)(sizeof(ImageTypeList) / sizeof(*ImageTypeList)); + fprintf(stdout, + "These 'image types' are used to identify pictures embedded in " + "'APIC' ID3 tags:\n usage is \"AP --ID3Tag APIC /path.jpg " + "--imagetype=\"str\"\n str can be either the hex listing *or* " + "the full string\n default is 0x00 - meaning 'Other'\n Hex " + " Full String\n ----------------------------\n"); + for (uint8_t i = 0; i < total_imgtyps; i++) { + fprintf(stdout, + " %s \"%s\"\n", + ImageTypeList[i].hexstring, + ImageTypeList[i].imagetype_str); + } + return; +} + +const char *ConvertCLIFrameStr_TO_frameID(const char *frame_str) { + const char *discovered_frameID = NULL; + uint16_t total_known_frames = + (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames)); + + for (uint16_t i = 0; i < total_known_frames; i++) { + if (strcmp(KnownFrames[i].CLI_frameIDpreset, frame_str) == 0) { + if (AtomicParsley_ID3v2Tag_MajorVersion == 2) + discovered_frameID = KnownFrames[i].ID3V2p2_FrameID; + if (AtomicParsley_ID3v2Tag_MajorVersion == 3) + discovered_frameID = KnownFrames[i].ID3V2p3_FrameID; + if (AtomicParsley_ID3v2Tag_MajorVersion == 4) + discovered_frameID = KnownFrames[i].ID3V2p4_FrameID; + + if (strlen(discovered_frameID) == 0) + discovered_frameID = NULL; + break; + } + } + return discovered_frameID; +} + +// 0 = description +// 1 = mimetype +// 2 = type bool TestCLI_for_FrameParams(int frametype, uint8_t testparam) { - if (frametype == ID3_URL_FRAME_USERDEF && testparam == 0) return true; - - if (frametype == ID3_UNIQUE_FILE_ID_FRAME && testparam == 3) { - return true; - } else if (frametype == ID3_ATTACHED_OBJECT_FRAME && testparam == 4) { - return true; - } else if (frametype == ID3_POPULAR_FRAME && testparam == 5) { - return true; - } else if (frametype == ID3_POPULAR_FRAME && testparam == 6) { - return true; - } else if (frametype == ID3_GROUP_ID_FRAME && testparam == 7) { - return true; - } else if (frametype == ID3_PRIVATE_FRAME && testparam == 8) { - return true; - } else { - uint8_t frametype_idx = GetFrameCompositionDescription(frametype); - - for (uint8_t fld_i = 0; fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount; fld_i++) { - if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == ID3_DESCRIPTION_FIELD && testparam == 0) { - return true; - } - if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == ID3_MIME_TYPE_FIELD && testparam == 1) { - return true; - } - if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == ID3_PIC_TYPE_FIELD && testparam == 2) { - return true; - } - if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == ID3_PIC_TYPE_FIELD && testparam == 3) { - return true; - } - } - } - return false; + if (frametype == ID3_URL_FRAME_USERDEF && testparam == 0) + return true; + + if (frametype == ID3_UNIQUE_FILE_ID_FRAME && testparam == 3) { + return true; + } else if (frametype == ID3_ATTACHED_OBJECT_FRAME && testparam == 4) { + return true; + } else if (frametype == ID3_POPULAR_FRAME && testparam == 5) { + return true; + } else if (frametype == ID3_POPULAR_FRAME && testparam == 6) { + return true; + } else if (frametype == ID3_GROUP_ID_FRAME && testparam == 7) { + return true; + } else if (frametype == ID3_PRIVATE_FRAME && testparam == 8) { + return true; + } else { + uint8_t frametype_idx = GetFrameCompositionDescription(frametype); + + for (uint8_t fld_i = 0; + fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount; + fld_i++) { + if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == + ID3_DESCRIPTION_FIELD && + testparam == 0) { + return true; + } + if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == + ID3_MIME_TYPE_FIELD && + testparam == 1) { + return true; + } + if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == + ID3_PIC_TYPE_FIELD && + testparam == 2) { + return true; + } + if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == + ID3_PIC_TYPE_FIELD && + testparam == 3) { + return true; + } + } + } + return false; } /////////////////////////////////////////////////////////////////////////////////////// -// frame identity functions // +// frame identity functions // /////////////////////////////////////////////////////////////////////////////////////// -int MatchID3FrameIDstr(const char* foundFrameID, uint8_t tagVersion) { - uint16_t total_known_frames = (uint16_t)(sizeof(KnownFrames)/sizeof(*KnownFrames)); - uint8_t frameLen = (tagVersion >= 3 ? 4 : 3) +1; - - for (int i = 0; i < total_known_frames; i++) { - const char* testFrameID = NULL; - if (tagVersion == 2) testFrameID = KnownFrames[i].ID3V2p2_FrameID; - if (tagVersion == 3) testFrameID = KnownFrames[i].ID3V2p3_FrameID; - if (tagVersion == 4) testFrameID = KnownFrames[i].ID3V2p4_FrameID; - - if (memcmp(foundFrameID, testFrameID, frameLen) == 0) { - return KnownFrames[i].ID3v2_InternalFrameID; - } - } - return ID3v2_UNKNOWN_FRAME; //return the UnknownFrame if it can't be found +int MatchID3FrameIDstr(const char *foundFrameID, uint8_t tagVersion) { + uint16_t total_known_frames = + (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames)); + uint8_t frameLen = (tagVersion >= 3 ? 4 : 3) + 1; + + for (int i = 0; i < total_known_frames; i++) { + const char *testFrameID = NULL; + if (tagVersion == 2) + testFrameID = KnownFrames[i].ID3V2p2_FrameID; + if (tagVersion == 3) + testFrameID = KnownFrames[i].ID3V2p3_FrameID; + if (tagVersion == 4) + testFrameID = KnownFrames[i].ID3V2p4_FrameID; + + if (memcmp(foundFrameID, testFrameID, frameLen) == 0) { + return KnownFrames[i].ID3v2_InternalFrameID; + } + } + return ID3v2_UNKNOWN_FRAME; // return the UnknownFrame if it can't be found } uint8_t GetFrameCompositionDescription(int ID3v2_FrameTypeID) { - uint8_t matchingFrameDescription = 0; //return the UnknownFrame/UnknownField if it can't be found - uint8_t total_frame_descrips = (uint8_t)(sizeof(FrameTypeConstructionList)/sizeof(*FrameTypeConstructionList)); - - for (uint8_t i = 0; i < total_frame_descrips; i++) { - if (FrameTypeConstructionList[i].ID3_FrameType == ID3v2_FrameTypeID) { - matchingFrameDescription = i; - break; - } - } - return matchingFrameDescription; -} - -int FrameStr_TO_FrameType(const char* frame_str) { - const char* eval_framestr = NULL; - int frame_type = 0; - uint16_t total_known_frames = (uint16_t)(sizeof(KnownFrames)/sizeof(*KnownFrames)); - - for (uint16_t i = 0; i < total_known_frames; i++) { - if (AtomicParsley_ID3v2Tag_MajorVersion == 2) eval_framestr = KnownFrames[i].ID3V2p2_FrameID; - if (AtomicParsley_ID3v2Tag_MajorVersion == 3) eval_framestr = KnownFrames[i].ID3V2p3_FrameID; - if (AtomicParsley_ID3v2Tag_MajorVersion == 4) eval_framestr = KnownFrames[i].ID3V2p4_FrameID; - - if (strcmp(frame_str, eval_framestr) == 0) { - frame_type = KnownFrames[i].ID3v2_FrameType; - break; - } - } - return frame_type; -} - -ID3v2Fields* APar_FindLastTextField(ID3v2Frame* aFrame) { - ID3v2Fields* lastusedtextfield = NULL; - if (aFrame->textfield_tally > 0) { - lastusedtextfield = aFrame->ID3v2_Frame_Fields+1; - while (true) { - if (lastusedtextfield->next_field == NULL) { - break; - } - lastusedtextfield = lastusedtextfield->next_field; - } - } - return lastusedtextfield; -} - -bool APar_ExtraTextFieldInit(ID3v2Fields* lastField, uint32_t utf8len, uint8_t textencoding) { - ID3v2Fields* extraField = NULL; - lastField->next_field = (ID3v2Fields*)calloc(1, sizeof(ID3v2Fields)*1); - if (lastField->next_field == NULL) { - fprintf(stdout, "There was insufficient memory to allocate another ID3 field\n"); - exit(12); - } - extraField = lastField->next_field; - extraField->ID3v2_Field_Type = ID3_TEXT_FIELD; - extraField->field_length = 0; - - if (textencoding == TE_UTF16LE_WITH_BOM || textencoding == TE_UTF16BE_NO_BOM) { - extraField->alloc_length = 2 + (utf8len * 2); - } else { - extraField->alloc_length = utf8len + 1; - } - if (extraField->alloc_length > 0) { - extraField->field_string = (char*)calloc(1, sizeof(char*) * extraField->alloc_length); - if (!APar_assert((extraField->field_string != NULL), 11, "while setting an extra text field") ) exit(11); - return true; - } - return false; + uint8_t matchingFrameDescription = + 0; // return the UnknownFrame/UnknownField if it can't be found + uint8_t total_frame_descrips = (uint8_t)(sizeof(FrameTypeConstructionList) / + sizeof(*FrameTypeConstructionList)); + + for (uint8_t i = 0; i < total_frame_descrips; i++) { + if (FrameTypeConstructionList[i].ID3_FrameType == ID3v2_FrameTypeID) { + matchingFrameDescription = i; + break; + } + } + return matchingFrameDescription; +} + +int FrameStr_TO_FrameType(const char *frame_str) { + const char *eval_framestr = NULL; + int frame_type = 0; + uint16_t total_known_frames = + (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames)); + + for (uint16_t i = 0; i < total_known_frames; i++) { + if (AtomicParsley_ID3v2Tag_MajorVersion == 2) + eval_framestr = KnownFrames[i].ID3V2p2_FrameID; + if (AtomicParsley_ID3v2Tag_MajorVersion == 3) + eval_framestr = KnownFrames[i].ID3V2p3_FrameID; + if (AtomicParsley_ID3v2Tag_MajorVersion == 4) + eval_framestr = KnownFrames[i].ID3V2p4_FrameID; + + if (strcmp(frame_str, eval_framestr) == 0) { + frame_type = KnownFrames[i].ID3v2_FrameType; + break; + } + } + return frame_type; +} + +ID3v2Fields *APar_FindLastTextField(ID3v2Frame *aFrame) { + ID3v2Fields *lastusedtextfield = NULL; + if (aFrame->textfield_tally > 0) { + lastusedtextfield = aFrame->ID3v2_Frame_Fields + 1; + while (true) { + if (lastusedtextfield->next_field == NULL) { + break; + } + lastusedtextfield = lastusedtextfield->next_field; + } + } + return lastusedtextfield; +} + +bool APar_ExtraTextFieldInit(ID3v2Fields *lastField, + uint32_t utf8len, + uint8_t textencoding) { + ID3v2Fields *extraField = NULL; + lastField->next_field = (ID3v2Fields *)calloc(1, sizeof(ID3v2Fields) * 1); + if (lastField->next_field == NULL) { + fprintf(stdout, + "There was insufficient memory to allocate another ID3 field\n"); + exit(12); + } + extraField = lastField->next_field; + extraField->ID3v2_Field_Type = ID3_TEXT_FIELD; + extraField->field_length = 0; + + if (textencoding == TE_UTF16LE_WITH_BOM || + textencoding == TE_UTF16BE_NO_BOM) { + extraField->alloc_length = 2 + (utf8len * 2); + } else { + extraField->alloc_length = utf8len + 1; + } + if (extraField->alloc_length > 0) { + extraField->field_string = + (char *)calloc(1, sizeof(char *) * extraField->alloc_length); + if (!APar_assert((extraField->field_string != NULL), + 11, + "while setting an extra text field")) + exit(11); + return true; + } + return false; } /////////////////////////////////////////////////////////////////////////////////////// -// id3 parsing functions // +// id3 parsing functions // /////////////////////////////////////////////////////////////////////////////////////// -uint32_t APar_ExtractField(char* buffer, uint32_t maxFieldLen, ID3v2Frame* thisFrame, ID3v2Fields* thisField, int fieldType, uint8_t textEncoding) { - uint32_t bytes_used = 0; - thisField->next_field = NULL; - switch(fieldType) { - case ID3_UNKNOWN_FIELD : { //the difference between this unknown field & say a binary data field is the unknown field is always the first (and only) field - thisField->ID3v2_Field_Type = ID3_UNKNOWN_FIELD; - thisField->field_length = maxFieldLen; - thisField->field_string = (char*)calloc(1, sizeof(char)*(maxFieldLen+1 > 16 ? maxFieldLen+1 : 16)); - thisField->alloc_length = sizeof(char)*(maxFieldLen+1 > 16 ? maxFieldLen+1 : 16); - memcpy(thisField->field_string, buffer, maxFieldLen); - - bytes_used = maxFieldLen; - break; - } - case ID3_PIC_TYPE_FIELD : - case ID3_GROUPSYMBOL_FIELD : - case ID3_TEXT_ENCODING_FIELD : { - thisField->ID3v2_Field_Type = fieldType; - thisField->field_length = 1; - thisField->field_string = (char*)calloc(1, sizeof(char)*16); - thisField->field_string[0] = buffer[0]; //memcpy(thisField->field_string, buffer, 1); - thisField->alloc_length = sizeof(char)*16; - - bytes_used = 1; - break; - } - case ID3_LANGUAGE_FIELD : { - thisField->ID3v2_Field_Type = ID3_LANGUAGE_FIELD; - thisField->field_length = 3; - thisField->field_string = (char*)calloc(1, sizeof(char)*16); - memcpy(thisField->field_string, buffer, 3); - thisField->alloc_length = sizeof(char)*16; - - bytes_used = 3; - break; - } - case ID3_TEXT_FIELD : - case ID3_URL_FIELD : - case ID3_COUNTER_FIELD : - case ID3_BINARY_DATA_FIELD : { //this class of fields may contains NULLs but is *NOT* NULL terminated in any form - thisField->ID3v2_Field_Type = fieldType; - thisField->field_length = maxFieldLen; - thisField->field_string = (char*)calloc(1, sizeof(char)*maxFieldLen+1 > 16 ? maxFieldLen+1 : 16); - memcpy(thisField->field_string, buffer, maxFieldLen); - thisField->alloc_length = (sizeof(char)*maxFieldLen+1 > 16 ? maxFieldLen+1 : 16); - - if (fieldType == ID3_TEXT_FIELD) { - bytes_used = findstringNULLterm(buffer, textEncoding, maxFieldLen); - } else { - bytes_used = maxFieldLen; - } - break; - } - case ID3_MIME_TYPE_FIELD : - case ID3_OWNER_FIELD : { //difference between ID3_OWNER_FIELD & ID3_DESCRIPTION_FIELD field classes is the owner field is always 8859-1 encoded (single NULL term) - thisField->ID3v2_Field_Type = fieldType; - thisField->field_length = findstringNULLterm(buffer, 0, maxFieldLen); - thisField->field_string = (char*)calloc(1, sizeof(char) * thisField->field_length +1 > 16 ? thisField->field_length +1 : 16); - memcpy(thisField->field_string, buffer, thisField->field_length); - thisField->alloc_length = (sizeof(char)*maxFieldLen+1 > 16 ? maxFieldLen+1 : 16); - - bytes_used = thisField->field_length; - break; - - } - case ID3_FILENAME_FIELD : - case ID3_DESCRIPTION_FIELD : { - thisField->ID3v2_Field_Type = fieldType; - thisField->field_length = findstringNULLterm(buffer, textEncoding, maxFieldLen); - thisField->field_string = (char*)calloc(1, sizeof(char) * thisField->field_length +1 > 16 ? thisField->field_length +1 : 16); - memcpy(thisField->field_string, buffer, thisField->field_length); - thisField->alloc_length = (sizeof(char) * thisField->field_length +1 > 16 ? thisField->field_length +1 : 16); - - bytes_used = thisField->field_length; - break; - } - } - //fprintf(stdout, "%" PRIu32 ", %s, %s\n", bytes_used, buffer, (thisFrame->ID3v2_Frame_Fields+fieldNum)->field_string); - return bytes_used; -} - -void APar_ScanID3Frame(ID3v2Frame* targetframe, char* frame_ptr, uint32_t frameLen) { - uint64_t offset_into_frame = 0; - - switch(targetframe->ID3v2_FrameType) { - case ID3_UNKNOWN_FRAME : { - APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_UNKNOWN_FIELD, 0); - break; - } - case ID3_TEXT_FRAME : { - uint8_t textencoding = 0xFF; - offset_into_frame += APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_TEXT_ENCODING_FIELD, 0); - - offset_into_frame += APar_ExtractField(frame_ptr + 1, frameLen - 1, targetframe, targetframe->ID3v2_Frame_Fields+1, - ID3_TEXT_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]); - targetframe->textfield_tally++; - - if (offset_into_frame >= frameLen) break; - textencoding = targetframe->ID3v2_Frame_Fields->field_string[0]; - - if (offset_into_frame < frameLen) { - while (true) { - if (offset_into_frame >= frameLen) break; - - //skip the required separator for multiple strings - if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) { - offset_into_frame += 1; - } else if (textencoding == TE_UTF16LE_WITH_BOM) { - offset_into_frame += 2; - } - - //multiple id3v2.4 strings should be separated with a single NULL byte; some implementations might terminate the string AND use a NULL separator - if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) { - if ((frame_ptr + offset_into_frame)[0] == 0) offset_into_frame+=1; - } else if (textencoding == TE_UTF16LE_WITH_BOM) { - if ((frame_ptr + offset_into_frame)[0] == 0 && (frame_ptr + offset_into_frame)[1] == 0) offset_into_frame+=2; - } - - //a 3rd NULL would not be good - if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) { - if ((frame_ptr + offset_into_frame)[0] == 0) break; - } else if (textencoding == TE_UTF16LE_WITH_BOM) { - if ((frame_ptr + offset_into_frame)[0] == 0 && (frame_ptr + offset_into_frame)[1] == 0) break; - } - - ID3v2Fields* last_textfield = APar_FindLastTextField(targetframe); - if (APar_ExtraTextFieldInit(last_textfield, frameLen - offset_into_frame, textencoding)) { - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, last_textfield->next_field, - ID3_TEXT_FIELD, textencoding); - targetframe->textfield_tally++; - } - //copy the string to the new field - break; - } - } - break; - } - case ID3_URL_FRAME : { - APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_URL_FIELD, 0); - break; - } - case ID3_TEXT_FRAME_USERDEF : - case ID3_URL_FRAME_USERDEF : { - offset_into_frame += APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_TEXT_ENCODING_FIELD, 0); - - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, - targetframe->ID3v2_Frame_Fields+1, ID3_DESCRIPTION_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]); - - offset_into_frame += skipNULLterm(frame_ptr + offset_into_frame, targetframe->ID3v2_Frame_Fields->field_string[0], frameLen - offset_into_frame); - - if (targetframe->ID3v2_FrameType == ID3_TEXT_FRAME_USERDEF) { - APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, - targetframe->ID3v2_Frame_Fields+2, ID3_TEXT_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]); - } else if (targetframe->ID3v2_FrameType == ID3_URL_FRAME_USERDEF) { - APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, - targetframe->ID3v2_Frame_Fields+2, ID3_URL_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]); - } - break; - } - case ID3_UNIQUE_FILE_ID_FRAME : { - offset_into_frame += APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_OWNER_FIELD, 0); - offset_into_frame++; //iso-8859-1 owner field is NULL terminated - - APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_BINARY_DATA_FIELD, 0); - break; - } - case ID3_CD_ID_FRAME : { - APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_BINARY_DATA_FIELD, 0); - break; - } - case ID3_DESCRIBED_TEXT_FRAME : { - offset_into_frame += APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_TEXT_ENCODING_FIELD, 0); - - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, 3, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_LANGUAGE_FIELD, 0); - - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, - targetframe->ID3v2_Frame_Fields+2, ID3_DESCRIPTION_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]); - - offset_into_frame += skipNULLterm(frame_ptr + offset_into_frame, targetframe->ID3v2_Frame_Fields->field_string[0], frameLen - offset_into_frame); - - if (frameLen > offset_into_frame) { - APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+3, - ID3_TEXT_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]); - } - break; - } - case ID3_ATTACHED_OBJECT_FRAME : - case ID3_ATTACHED_PICTURE_FRAME : { - offset_into_frame += APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_TEXT_ENCODING_FIELD, 0); - - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_MIME_TYPE_FIELD, 0); - - offset_into_frame += 1; //should only be 1 NULL - - if (targetframe->ID3v2_FrameType == ID3_ATTACHED_PICTURE_FRAME) { - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, 1, targetframe, targetframe->ID3v2_Frame_Fields+2, ID3_PIC_TYPE_FIELD, 0); - } else { - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, - targetframe->ID3v2_Frame_Fields+2, ID3_FILENAME_FIELD, 0); - - offset_into_frame+=skipNULLterm(frame_ptr + offset_into_frame, targetframe->ID3v2_Frame_Fields->field_string[0], frameLen - offset_into_frame); - } - - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+3, - ID3_DESCRIPTION_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]); - - offset_into_frame += skipNULLterm(frame_ptr + offset_into_frame, targetframe->ID3v2_Frame_Fields->field_string[0], frameLen - offset_into_frame); - - if (frameLen > offset_into_frame) { - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+4, - ID3_BINARY_DATA_FIELD, 0); - } - break; - } - case ID3_PRIVATE_FRAME : { //the only difference between the 'priv' frame & the 'ufid' frame is ufid is limited to 64 bytes - offset_into_frame += APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_OWNER_FIELD, 0); - offset_into_frame++; //iso-8859-1 owner field is NULL terminated - - APar_ExtractField(frame_ptr + offset_into_frame, frameLen - 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_BINARY_DATA_FIELD, 0); - break; - } - case ID3_GROUP_ID_FRAME : { - offset_into_frame += APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_OWNER_FIELD, 0); - offset_into_frame++; //iso-8859-1 owner field is NULL terminated - - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_GROUPSYMBOL_FIELD, 0); - - if (frameLen > offset_into_frame) { - APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+2, ID3_BINARY_DATA_FIELD, 0); - } - break; - } - case ID3_SIGNATURE_FRAME : { - APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_GROUPSYMBOL_FIELD, 0); - - APar_ExtractField(frame_ptr + 1, frameLen - 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_BINARY_DATA_FIELD, 0); - break; - } - case ID3_PLAYCOUNTER_FRAME : { - APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_COUNTER_FIELD, 0); - break; - } - case ID3_POPULAR_FRAME : { - offset_into_frame += APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_OWNER_FIELD, 0); //surrogate for 'emai to user' field - offset_into_frame++; //iso-8859-1 email address field is NULL terminated - - offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_BINARY_DATA_FIELD, 0); - - if (frameLen > offset_into_frame) { - APar_ExtractField(frame_ptr, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+2, ID3_COUNTER_FIELD, 0); - } - break; - } - case ID3_OLD_V2P2_PICTURE_FRAME : { - break; //unimplemented - } - } - return; -} - -void APar_ID32_ScanID3Tag(FILE* source_file, AtomicInfo* id32_atom) { - char* id32_fulltag = (char*)calloc(1, sizeof(char)*id32_atom->AtomicLength); - char* fulltag_ptr = id32_fulltag; - - if (id32_atom->AtomicLength < 20) return; - APar_readX(id32_fulltag, source_file, id32_atom->AtomicStart+14, id32_atom->AtomicLength-14); //+10 = 4bytes ID32 atom length + 4bytes ID32 atom name + 2 bytes packed lang - - if (memcmp(id32_fulltag, "ID3", 3) != 0) return; - fulltag_ptr+=3; - - id32_atom->ID32_TagInfo = (ID3v2Tag*)calloc(1, sizeof(ID3v2Tag)); - id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion = *fulltag_ptr; - fulltag_ptr++; - id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion = *fulltag_ptr; - fulltag_ptr++; - id32_atom->ID32_TagInfo->ID3v2Tag_Flags = *fulltag_ptr; - fulltag_ptr++; - - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4) { - fprintf(stdout, "AtomicParsley warning: an ID32 atom was encountered using an unsupported ID3v2 tag version: %u. Skipping\n", id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion); - return; - } - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4 && id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion != 0) { - fprintf(stdout, "AtomicParsley warning: an ID32 atom was encountered using an unsupported ID3v2.4 tag revision: %u. Skipping\n", id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion); - return; - } - - if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_BIT0+ID32_TAGFLAG_BIT1+ID32_TAGFLAG_BIT2+ID32_TAGFLAG_BIT3)) return; - - if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_FOOTER)) { - fprintf(stdout, "AtomicParsley error: an ID32 atom was encountered with a forbidden footer flag. Exiting.\n"); - free(id32_fulltag); - id32_fulltag = NULL; - return; - } - - if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_EXPERIMENTAL)) { +uint32_t APar_ExtractField(char *buffer, + uint32_t maxFieldLen, + ID3v2Frame *thisFrame, + ID3v2Fields *thisField, + int fieldType, + uint8_t textEncoding) { + uint32_t bytes_used = 0; + thisField->next_field = NULL; + switch (fieldType) { + case ID3_UNKNOWN_FIELD: { // the difference between this unknown field & say a + // binary data field is the unknown field is always + // the first (and only) field + thisField->ID3v2_Field_Type = ID3_UNKNOWN_FIELD; + thisField->field_length = maxFieldLen; + thisField->field_string = (char *)calloc( + 1, sizeof(char) * (maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16)); + thisField->alloc_length = + sizeof(char) * (maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16); + memcpy(thisField->field_string, buffer, maxFieldLen); + + bytes_used = maxFieldLen; + break; + } + case ID3_PIC_TYPE_FIELD: + case ID3_GROUPSYMBOL_FIELD: + case ID3_TEXT_ENCODING_FIELD: { + thisField->ID3v2_Field_Type = fieldType; + thisField->field_length = 1; + thisField->field_string = (char *)calloc(1, sizeof(char) * 16); + thisField->field_string[0] = + buffer[0]; // memcpy(thisField->field_string, buffer, 1); + thisField->alloc_length = sizeof(char) * 16; + + bytes_used = 1; + break; + } + case ID3_LANGUAGE_FIELD: { + thisField->ID3v2_Field_Type = ID3_LANGUAGE_FIELD; + thisField->field_length = 3; + thisField->field_string = (char *)calloc(1, sizeof(char) * 16); + memcpy(thisField->field_string, buffer, 3); + thisField->alloc_length = sizeof(char) * 16; + + bytes_used = 3; + break; + } + case ID3_TEXT_FIELD: + case ID3_URL_FIELD: + case ID3_COUNTER_FIELD: + case ID3_BINARY_DATA_FIELD: { // this class of fields may contains NULLs but + // is *NOT* NULL terminated in any form + thisField->ID3v2_Field_Type = fieldType; + thisField->field_length = maxFieldLen; + thisField->field_string = (char *)calloc( + 1, sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16); + memcpy(thisField->field_string, buffer, maxFieldLen); + thisField->alloc_length = + (sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16); + + if (fieldType == ID3_TEXT_FIELD) { + bytes_used = findstringNULLterm(buffer, textEncoding, maxFieldLen); + } else { + bytes_used = maxFieldLen; + } + break; + } + case ID3_MIME_TYPE_FIELD: + case ID3_OWNER_FIELD: { // difference between ID3_OWNER_FIELD & + // ID3_DESCRIPTION_FIELD field classes is the owner + // field is always 8859-1 encoded (single NULL term) + thisField->ID3v2_Field_Type = fieldType; + thisField->field_length = findstringNULLterm(buffer, 0, maxFieldLen); + thisField->field_string = + (char *)calloc(1, + sizeof(char) * thisField->field_length + 1 > 16 + ? thisField->field_length + 1 + : 16); + memcpy(thisField->field_string, buffer, thisField->field_length); + thisField->alloc_length = + (sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16); + + bytes_used = thisField->field_length; + break; + } + case ID3_FILENAME_FIELD: + case ID3_DESCRIPTION_FIELD: { + thisField->ID3v2_Field_Type = fieldType; + thisField->field_length = + findstringNULLterm(buffer, textEncoding, maxFieldLen); + thisField->field_string = + (char *)calloc(1, + sizeof(char) * thisField->field_length + 1 > 16 + ? thisField->field_length + 1 + : 16); + memcpy(thisField->field_string, buffer, thisField->field_length); + thisField->alloc_length = (sizeof(char) * thisField->field_length + 1 > 16 + ? thisField->field_length + 1 + : 16); + + bytes_used = thisField->field_length; + break; + } + } + // fprintf(stdout, "%" PRIu32 ", %s, %s\n", bytes_used, buffer, + // (thisFrame->ID3v2_Frame_Fields+fieldNum)->field_string); + return bytes_used; +} + +void APar_ScanID3Frame(ID3v2Frame *targetframe, + char *frame_ptr, + uint32_t frameLen) { + uint64_t offset_into_frame = 0; + + switch (targetframe->ID3v2_FrameType) { + case ID3_UNKNOWN_FRAME: { + APar_ExtractField(frame_ptr, + frameLen, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_UNKNOWN_FIELD, + 0); + break; + } + case ID3_TEXT_FRAME: { + uint8_t textencoding = 0xFF; + offset_into_frame += APar_ExtractField(frame_ptr, + 1, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_TEXT_ENCODING_FIELD, + 0); + + offset_into_frame += + APar_ExtractField(frame_ptr + 1, + frameLen - 1, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_TEXT_FIELD, + targetframe->ID3v2_Frame_Fields->field_string[0]); + targetframe->textfield_tally++; + + if (offset_into_frame >= frameLen) + break; + textencoding = targetframe->ID3v2_Frame_Fields->field_string[0]; + + if (offset_into_frame < frameLen) { + while (true) { + if (offset_into_frame >= frameLen) + break; + + // skip the required separator for multiple strings + if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) { + offset_into_frame += 1; + } else if (textencoding == TE_UTF16LE_WITH_BOM) { + offset_into_frame += 2; + } + + // multiple id3v2.4 strings should be separated with a single NULL byte; + // some implementations might terminate the string AND use a NULL + // separator + if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) { + if ((frame_ptr + offset_into_frame)[0] == 0) + offset_into_frame += 1; + } else if (textencoding == TE_UTF16LE_WITH_BOM) { + if ((frame_ptr + offset_into_frame)[0] == 0 && + (frame_ptr + offset_into_frame)[1] == 0) + offset_into_frame += 2; + } + + // a 3rd NULL would not be good + if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) { + if ((frame_ptr + offset_into_frame)[0] == 0) + break; + } else if (textencoding == TE_UTF16LE_WITH_BOM) { + if ((frame_ptr + offset_into_frame)[0] == 0 && + (frame_ptr + offset_into_frame)[1] == 0) + break; + } + + ID3v2Fields *last_textfield = APar_FindLastTextField(targetframe); + if (APar_ExtraTextFieldInit( + last_textfield, frameLen - offset_into_frame, textencoding)) { + offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + last_textfield->next_field, + ID3_TEXT_FIELD, + textencoding); + targetframe->textfield_tally++; + } + // copy the string to the new field + break; + } + } + break; + } + case ID3_URL_FRAME: { + APar_ExtractField(frame_ptr, + frameLen, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_URL_FIELD, + 0); + break; + } + case ID3_TEXT_FRAME_USERDEF: + case ID3_URL_FRAME_USERDEF: { + offset_into_frame += APar_ExtractField(frame_ptr, + 1, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_TEXT_ENCODING_FIELD, + 0); + + offset_into_frame += + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_DESCRIPTION_FIELD, + targetframe->ID3v2_Frame_Fields->field_string[0]); + + offset_into_frame += + skipNULLterm(frame_ptr + offset_into_frame, + targetframe->ID3v2_Frame_Fields->field_string[0], + frameLen - offset_into_frame); + + if (targetframe->ID3v2_FrameType == ID3_TEXT_FRAME_USERDEF) { + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 2, + ID3_TEXT_FIELD, + targetframe->ID3v2_Frame_Fields->field_string[0]); + } else if (targetframe->ID3v2_FrameType == ID3_URL_FRAME_USERDEF) { + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 2, + ID3_URL_FIELD, + targetframe->ID3v2_Frame_Fields->field_string[0]); + } + break; + } + case ID3_UNIQUE_FILE_ID_FRAME: { + offset_into_frame += APar_ExtractField(frame_ptr, + frameLen, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_OWNER_FIELD, + 0); + offset_into_frame++; // iso-8859-1 owner field is NULL terminated + + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_BINARY_DATA_FIELD, + 0); + break; + } + case ID3_CD_ID_FRAME: { + APar_ExtractField(frame_ptr, + frameLen, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_BINARY_DATA_FIELD, + 0); + break; + } + case ID3_DESCRIBED_TEXT_FRAME: { + offset_into_frame += APar_ExtractField(frame_ptr, + 1, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_TEXT_ENCODING_FIELD, + 0); + + offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, + 3, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_LANGUAGE_FIELD, + 0); + + offset_into_frame += + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 2, + ID3_DESCRIPTION_FIELD, + targetframe->ID3v2_Frame_Fields->field_string[0]); + + offset_into_frame += + skipNULLterm(frame_ptr + offset_into_frame, + targetframe->ID3v2_Frame_Fields->field_string[0], + frameLen - offset_into_frame); + + if (frameLen > offset_into_frame) { + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 3, + ID3_TEXT_FIELD, + targetframe->ID3v2_Frame_Fields->field_string[0]); + } + break; + } + case ID3_ATTACHED_OBJECT_FRAME: + case ID3_ATTACHED_PICTURE_FRAME: { + offset_into_frame += APar_ExtractField(frame_ptr, + 1, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_TEXT_ENCODING_FIELD, + 0); + + offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - 1, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_MIME_TYPE_FIELD, + 0); + + offset_into_frame += 1; // should only be 1 NULL + + if (targetframe->ID3v2_FrameType == ID3_ATTACHED_PICTURE_FRAME) { + offset_into_frame += + APar_ExtractField(frame_ptr + offset_into_frame, + 1, + targetframe, + targetframe->ID3v2_Frame_Fields + 2, + ID3_PIC_TYPE_FIELD, + 0); + } else { + offset_into_frame += + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 2, + ID3_FILENAME_FIELD, + 0); + + offset_into_frame += + skipNULLterm(frame_ptr + offset_into_frame, + targetframe->ID3v2_Frame_Fields->field_string[0], + frameLen - offset_into_frame); + } + + offset_into_frame += + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 3, + ID3_DESCRIPTION_FIELD, + targetframe->ID3v2_Frame_Fields->field_string[0]); + + offset_into_frame += + skipNULLterm(frame_ptr + offset_into_frame, + targetframe->ID3v2_Frame_Fields->field_string[0], + frameLen - offset_into_frame); + + if (frameLen > offset_into_frame) { + offset_into_frame += + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 4, + ID3_BINARY_DATA_FIELD, + 0); + } + break; + } + case ID3_PRIVATE_FRAME: { // the only difference between the 'priv' frame & + // the 'ufid' frame is ufid is limited to 64 bytes + offset_into_frame += APar_ExtractField(frame_ptr, + frameLen, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_OWNER_FIELD, + 0); + offset_into_frame++; // iso-8859-1 owner field is NULL terminated + + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - 1, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_BINARY_DATA_FIELD, + 0); + break; + } + case ID3_GROUP_ID_FRAME: { + offset_into_frame += APar_ExtractField(frame_ptr, + frameLen, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_OWNER_FIELD, + 0); + offset_into_frame++; // iso-8859-1 owner field is NULL terminated + + offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, + 1, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_GROUPSYMBOL_FIELD, + 0); + + if (frameLen > offset_into_frame) { + APar_ExtractField(frame_ptr + offset_into_frame, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 2, + ID3_BINARY_DATA_FIELD, + 0); + } + break; + } + case ID3_SIGNATURE_FRAME: { + APar_ExtractField(frame_ptr, + 1, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_GROUPSYMBOL_FIELD, + 0); + + APar_ExtractField(frame_ptr + 1, + frameLen - 1, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_BINARY_DATA_FIELD, + 0); + break; + } + case ID3_PLAYCOUNTER_FRAME: { + APar_ExtractField(frame_ptr, + frameLen, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_COUNTER_FIELD, + 0); + break; + } + case ID3_POPULAR_FRAME: { + offset_into_frame += + APar_ExtractField(frame_ptr, + frameLen, + targetframe, + targetframe->ID3v2_Frame_Fields, + ID3_OWNER_FIELD, + 0); // surrogate for 'emai to user' field + offset_into_frame++; // iso-8859-1 email address field is NULL terminated + + offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, + 1, + targetframe, + targetframe->ID3v2_Frame_Fields + 1, + ID3_BINARY_DATA_FIELD, + 0); + + if (frameLen > offset_into_frame) { + APar_ExtractField(frame_ptr, + frameLen - offset_into_frame, + targetframe, + targetframe->ID3v2_Frame_Fields + 2, + ID3_COUNTER_FIELD, + 0); + } + break; + } + case ID3_OLD_V2P2_PICTURE_FRAME: { + break; // unimplemented + } + } + return; +} + +void APar_ID32_ScanID3Tag(FILE *source_file, AtomicInfo *id32_atom) { + char *id32_fulltag = + (char *)calloc(1, sizeof(char) * id32_atom->AtomicLength); + char *fulltag_ptr = id32_fulltag; + + if (id32_atom->AtomicLength < 20) + return; + APar_readX(id32_fulltag, + source_file, + id32_atom->AtomicStart + 14, + id32_atom->AtomicLength - + 14); //+10 = 4bytes ID32 atom length + 4bytes ID32 atom name + + // 2 bytes packed lang + + if (memcmp(id32_fulltag, "ID3", 3) != 0) + return; + fulltag_ptr += 3; + + id32_atom->ID32_TagInfo = (ID3v2Tag *)calloc(1, sizeof(ID3v2Tag)); + id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion = *fulltag_ptr; + fulltag_ptr++; + id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion = *fulltag_ptr; + fulltag_ptr++; + id32_atom->ID32_TagInfo->ID3v2Tag_Flags = *fulltag_ptr; + fulltag_ptr++; + + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4) { + fprintf(stdout, + "AtomicParsley warning: an ID32 atom was encountered using an " + "unsupported ID3v2 tag version: %u. Skipping\n", + id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion); + return; + } + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4 && + id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion != 0) { + fprintf(stdout, + "AtomicParsley warning: an ID32 atom was encountered using an " + "unsupported ID3v2.4 tag revision: %u. Skipping\n", + id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion); + return; + } + + if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, + ID32_TAGFLAG_BIT0 + ID32_TAGFLAG_BIT1 + + ID32_TAGFLAG_BIT2 + ID32_TAGFLAG_BIT3)) + return; + + if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, + ID32_TAGFLAG_FOOTER)) { + fprintf(stdout, + "AtomicParsley error: an ID32 atom was encountered with a " + "forbidden footer flag. Exiting.\n"); + free(id32_fulltag); + id32_fulltag = NULL; + return; + } + + if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, + ID32_TAGFLAG_EXPERIMENTAL)) { #if defined(DEBUG_V) - fprintf(stdout, "AtomicParsley warning: an ID32 atom was encountered with an experimental flag set.\n"); + fprintf(stdout, + "AtomicParsley warning: an ID32 atom was encountered with " + "an experimental flag set.\n"); #endif - } - - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { - id32_atom->ID32_TagInfo->ID3v2Tag_Length = syncsafe32_to_UInt32(fulltag_ptr); - fulltag_ptr+=4; - } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) { - id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt32FromBigEndian(fulltag_ptr); //TODO: when testing ends, this switches to syncsafe - fulltag_ptr+=4; - } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { - id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt24FromBigEndian(fulltag_ptr); - fulltag_ptr+=3; - } - - if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_UNSYNCRONIZATION)) { - //uint32_t newtagsize = ID3v2_desynchronize(id32_fulltag, id32_atom->ID32_TagInfo->ID3v2Tag_Length); - //fprintf(stdout, "New tag size is %" PRIu32 "\n", newtagsize); - //WriteZlibData(id32_fulltag, newtagsize); - //exit(0); - fprintf(stdout, "AtomicParsley error: an ID3 tag with the unsynchronized flag set which is not supported. Skipping.\n"); - free(id32_fulltag); - id32_fulltag = NULL; - return; - } - - if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_EXTENDEDHEADER)) { - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { - id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = syncsafe32_to_UInt32(fulltag_ptr); - } else { - id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = UInt32FromBigEndian(fulltag_ptr); //TODO: when testing ends, this switches to syncsafe; 2.2 doesn't have it - } - fulltag_ptr+= id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length; - } - - id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL; - id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL; - - //loop through parsing frames - while (fulltag_ptr < id32_fulltag + (id32_atom->AtomicLength-14) ) { - uint32_t fullframesize = 0; - - if (ID3v2_PaddingTest(fulltag_ptr)) break; - if (ID3v2_TestFrameID_NonConformance(fulltag_ptr)) break; - - ID3v2Frame* target_list_frameinfo = (ID3v2Frame*)calloc(1, sizeof(ID3v2Frame)); - target_list_frameinfo->ID3v2_NextFrame = NULL; - target_list_frameinfo->ID3v2_Frame_ID = MatchID3FrameIDstr(fulltag_ptr, id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion); - target_list_frameinfo->ID3v2_FrameType = KnownFrames[target_list_frameinfo->ID3v2_Frame_ID+1].ID3v2_FrameType; - - uint8_t FrameCompositionList = GetFrameCompositionDescription(target_list_frameinfo->ID3v2_FrameType); - target_list_frameinfo->ID3v2_FieldCount = FrameTypeConstructionList[FrameCompositionList].ID3_FieldCount; - target_list_frameinfo->ID3v2_Frame_ExpandedLength = 0; - target_list_frameinfo->textfield_tally = 0; - target_list_frameinfo->eliminate_frame = false; - uint8_t frame_offset = 0; - - if (id32_atom->ID32_TagInfo->ID3v2_FrameList != NULL) id32_atom->ID32_TagInfo->ID3v2_FrameList->ID3v2_NextFrame = target_list_frameinfo; - - //need to lookup how many components this Frame_ID is associated with. Do this by using the corresponding KnownFrames.ID3v2_FrameType - //ID3v2_FrameType describes the general form this frame takes (text, text with description, attached object, attached picture) - //the general form is composed of several fields; that number of fields needs to be malloced to target_list_frameinfo->ID3v2_Frame_Fields - //and each target_list_frameinfo->ID3v2_Frame_Fields+num->field_string needs to be malloced and copied from id32_fulltag - - memset(target_list_frameinfo->ID3v2_Frame_Namestr, 0, 5); - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { - memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 3); - fulltag_ptr+= 3; - } else { - memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 4); - fulltag_ptr+= 4; - } - - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { - target_list_frameinfo->ID3v2_Frame_Length = syncsafe32_to_UInt32(fulltag_ptr); - fulltag_ptr+=4; - } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) { - target_list_frameinfo->ID3v2_Frame_Length = UInt32FromBigEndian(fulltag_ptr); //TODO: when testing ends, this switches to syncsafe - fulltag_ptr+=4; - } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { - target_list_frameinfo->ID3v2_Frame_Length = UInt24FromBigEndian(fulltag_ptr); - fulltag_ptr+=3; - } - - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion >= 3) { - target_list_frameinfo->ID3v2_Frame_Flags = UInt16FromBigEndian(fulltag_ptr); //v2.2 doesn't have frame level flags (but it does have field level flags) - fulltag_ptr+=2; - - if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_UNSYNCED)) { - //DE-UNSYNC frame - fullframesize = target_list_frameinfo->ID3v2_Frame_Length; - target_list_frameinfo->ID3v2_Frame_Length = ID3v2_desynchronize(fulltag_ptr+frame_offset, target_list_frameinfo->ID3v2_Frame_Length); - target_list_frameinfo->ID3v2_Frame_Flags -= ID32_FRAMEFLAG_UNSYNCED; - } - - //info based on frame flags (order based on the order of flags defined by the frame flags - if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_GROUPING)) { + } + + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { + id32_atom->ID32_TagInfo->ID3v2Tag_Length = + syncsafe32_to_UInt32(fulltag_ptr); + fulltag_ptr += 4; + } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) { + id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt32FromBigEndian( + fulltag_ptr); // TODO: when testing ends, this switches to syncsafe + fulltag_ptr += 4; + } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { + id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt24FromBigEndian(fulltag_ptr); + fulltag_ptr += 3; + } + + if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, + ID32_TAGFLAG_UNSYNCRONIZATION)) { + // uint32_t newtagsize = ID3v2_desynchronize(id32_fulltag, + // id32_atom->ID32_TagInfo->ID3v2Tag_Length); fprintf(stdout, "New tag size + // is %" PRIu32 "\n", newtagsize); WriteZlibData(id32_fulltag, newtagsize); + // exit(0); + fprintf(stdout, + "AtomicParsley error: an ID3 tag with the unsynchronized " + "flag set which is not supported. Skipping.\n"); + free(id32_fulltag); + id32_fulltag = NULL; + return; + } + + if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, + ID32_TAGFLAG_EXTENDEDHEADER)) { + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { + id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = + syncsafe32_to_UInt32(fulltag_ptr); + } else { + id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = + UInt32FromBigEndian( + fulltag_ptr); // TODO: when testing ends, this switches to + // syncsafe; 2.2 doesn't have it + } + fulltag_ptr += id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length; + } + + id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL; + id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL; + + // loop through parsing frames + while (fulltag_ptr < id32_fulltag + (id32_atom->AtomicLength - 14)) { + uint32_t fullframesize = 0; + + if (ID3v2_PaddingTest(fulltag_ptr)) + break; + if (ID3v2_TestFrameID_NonConformance(fulltag_ptr)) + break; + + ID3v2Frame *target_list_frameinfo = + (ID3v2Frame *)calloc(1, sizeof(ID3v2Frame)); + target_list_frameinfo->ID3v2_NextFrame = NULL; + target_list_frameinfo->ID3v2_Frame_ID = MatchID3FrameIDstr( + fulltag_ptr, id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion); + target_list_frameinfo->ID3v2_FrameType = + KnownFrames[target_list_frameinfo->ID3v2_Frame_ID + 1].ID3v2_FrameType; + + uint8_t FrameCompositionList = + GetFrameCompositionDescription(target_list_frameinfo->ID3v2_FrameType); + target_list_frameinfo->ID3v2_FieldCount = + FrameTypeConstructionList[FrameCompositionList].ID3_FieldCount; + target_list_frameinfo->ID3v2_Frame_ExpandedLength = 0; + target_list_frameinfo->textfield_tally = 0; + target_list_frameinfo->eliminate_frame = false; + uint8_t frame_offset = 0; + + if (id32_atom->ID32_TagInfo->ID3v2_FrameList != NULL) + id32_atom->ID32_TagInfo->ID3v2_FrameList->ID3v2_NextFrame = + target_list_frameinfo; + + // need to lookup how many components this Frame_ID is associated with. Do + // this by using the corresponding KnownFrames.ID3v2_FrameType + // ID3v2_FrameType describes the general form this frame takes (text, text + // with description, attached object, attached picture) the general form is + // composed of several fields; that number of fields needs to be malloced to + // target_list_frameinfo->ID3v2_Frame_Fields and each + // target_list_frameinfo->ID3v2_Frame_Fields+num->field_string needs to be + // malloced and copied from id32_fulltag + + memset(target_list_frameinfo->ID3v2_Frame_Namestr, 0, 5); + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { + memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 3); + fulltag_ptr += 3; + } else { + memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 4); + fulltag_ptr += 4; + } + + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { + target_list_frameinfo->ID3v2_Frame_Length = + syncsafe32_to_UInt32(fulltag_ptr); + fulltag_ptr += 4; + } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) { + target_list_frameinfo->ID3v2_Frame_Length = UInt32FromBigEndian( + fulltag_ptr); // TODO: when testing ends, this switches to syncsafe + fulltag_ptr += 4; + } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { + target_list_frameinfo->ID3v2_Frame_Length = + UInt24FromBigEndian(fulltag_ptr); + fulltag_ptr += 3; + } + + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion >= 3) { + target_list_frameinfo->ID3v2_Frame_Flags = UInt16FromBigEndian( + fulltag_ptr); // v2.2 doesn't have frame level flags (but it does have + // field level flags) + fulltag_ptr += 2; + + if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, + ID32_FRAMEFLAG_UNSYNCED)) { + // DE-UNSYNC frame + fullframesize = target_list_frameinfo->ID3v2_Frame_Length; + target_list_frameinfo->ID3v2_Frame_Length = + ID3v2_desynchronize(fulltag_ptr + frame_offset, + target_list_frameinfo->ID3v2_Frame_Length); + target_list_frameinfo->ID3v2_Frame_Flags -= ID32_FRAMEFLAG_UNSYNCED; + } + + // info based on frame flags (order based on the order of flags defined by + // the frame flags + if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, + ID32_FRAMEFLAG_GROUPING)) { #if defined(DEBUG_V) - fprintf(stdout, "Frame %s has a grouping flag set\n", target_list_frameinfo->ID3v2_Frame_Namestr); + fprintf(stdout, + "Frame %s has a grouping flag set\n", + target_list_frameinfo->ID3v2_Frame_Namestr); #endif - target_list_frameinfo->ID3v2_Frame_GroupingSymbol = *fulltag_ptr; //er, uh... wouldn't this also require ID32_FRAMEFLAG_LENINDICATED to be set??? - frame_offset++; - } - - if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED)) { // technically ID32_FRAMEFLAG_LENINDICATED should also be tested + target_list_frameinfo->ID3v2_Frame_GroupingSymbol = + *fulltag_ptr; // er, uh... wouldn't this also require + // ID32_FRAMEFLAG_LENINDICATED to be set??? + frame_offset++; + } + + if (ID3v2_TestFrameFlag( + target_list_frameinfo->ID3v2_Frame_Flags, + ID32_FRAMEFLAG_COMPRESSED)) { // technically + // ID32_FRAMEFLAG_LENINDICATED + // should also be tested #if defined(DEBUG_V) - fprintf(stdout, "Frame %s has a compressed flag set\n", target_list_frameinfo->ID3v2_Frame_Namestr); + fprintf(stdout, + "Frame %s has a compressed flag set\n", + target_list_frameinfo->ID3v2_Frame_Namestr); #endif - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { - target_list_frameinfo->ID3v2_Frame_ExpandedLength = syncsafe32_to_UInt32(fulltag_ptr+frame_offset); - frame_offset+=4; - } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) { - target_list_frameinfo->ID3v2_Frame_ExpandedLength = UInt32FromBigEndian(fulltag_ptr+frame_offset); //TODO: when testing ends, switch this to syncsafe - frame_offset+=4; - } - } - } - - - target_list_frameinfo->ID3v2_Frame_Fields = (ID3v2Fields*)calloc(1, sizeof(ID3v2Fields)*target_list_frameinfo->ID3v2_FieldCount); - char* expanded_frame = NULL; - char* frame_ptr = NULL; - uint32_t frameLen = 0; - - if (target_list_frameinfo->ID3v2_Frame_ExpandedLength != 0) { + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { + target_list_frameinfo->ID3v2_Frame_ExpandedLength = + syncsafe32_to_UInt32(fulltag_ptr + frame_offset); + frame_offset += 4; + } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) { + target_list_frameinfo + ->ID3v2_Frame_ExpandedLength = UInt32FromBigEndian( + fulltag_ptr + + frame_offset); // TODO: when testing ends, switch this to syncsafe + frame_offset += 4; + } + } + } + + target_list_frameinfo->ID3v2_Frame_Fields = (ID3v2Fields *)calloc( + 1, sizeof(ID3v2Fields) * target_list_frameinfo->ID3v2_FieldCount); + char *expanded_frame = NULL; + char *frame_ptr = NULL; + uint32_t frameLen = 0; + + if (target_list_frameinfo->ID3v2_Frame_ExpandedLength != 0) { #ifdef HAVE_ZLIB_H - expanded_frame = (char*)calloc(1, sizeof(char)*target_list_frameinfo->ID3v2_Frame_ExpandedLength + 1); - APar_zlib_inflate(fulltag_ptr+frame_offset, target_list_frameinfo->ID3v2_Frame_Length, expanded_frame, target_list_frameinfo->ID3v2_Frame_ExpandedLength); - - WriteZlibData(expanded_frame, target_list_frameinfo->ID3v2_Frame_ExpandedLength); - - frame_ptr = expanded_frame; - frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength; + expanded_frame = (char *)calloc( + 1, + sizeof(char) * target_list_frameinfo->ID3v2_Frame_ExpandedLength + 1); + APar_zlib_inflate(fulltag_ptr + frame_offset, + target_list_frameinfo->ID3v2_Frame_Length, + expanded_frame, + target_list_frameinfo->ID3v2_Frame_ExpandedLength); + + WriteZlibData(expanded_frame, + target_list_frameinfo->ID3v2_Frame_ExpandedLength); + + frame_ptr = expanded_frame; + frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength; #else - target_list_frameinfo->ID3v2_FrameType = ID3_UNKNOWN_FRAME; - frame_ptr = fulltag_ptr+frame_offset; - frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength; + target_list_frameinfo->ID3v2_FrameType = ID3_UNKNOWN_FRAME; + frame_ptr = fulltag_ptr + frame_offset; + frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength; #endif - } else { + } else { - frame_ptr = fulltag_ptr+frame_offset; - frameLen = target_list_frameinfo->ID3v2_Frame_Length; - } - - APar_ScanID3Frame(target_list_frameinfo, frame_ptr, frameLen); - - if (expanded_frame != NULL) { - free(expanded_frame); - expanded_frame = NULL; - } - - if (target_list_frameinfo != NULL) { - if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0) { - id32_atom->ID32_TagInfo->ID3v2_FirstFrame = target_list_frameinfo; //entrance to the linked list - } - id32_atom->ID32_TagInfo->ID3v2_FrameList = target_list_frameinfo; //this always points to the last frame that had the scan completed - } - - if (fullframesize != 0) { - fulltag_ptr+= fullframesize; - } else { - fulltag_ptr+= target_list_frameinfo->ID3v2_Frame_Length; - } - if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_GROUPING)) { - fulltag_ptr++; - } - id32_atom->ID32_TagInfo->ID3v2_FrameCount++; - } - - id32_atom->ID32_TagInfo->modified_tag = false; //if a frame is altered/added/removed, change this to true and render the tag & fill id32_atom-AtomicData with the tag - return; + frame_ptr = fulltag_ptr + frame_offset; + frameLen = target_list_frameinfo->ID3v2_Frame_Length; + } + + APar_ScanID3Frame(target_list_frameinfo, frame_ptr, frameLen); + + if (expanded_frame != NULL) { + free(expanded_frame); + expanded_frame = NULL; + } + + if (target_list_frameinfo != NULL) { + if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0) { + id32_atom->ID32_TagInfo->ID3v2_FirstFrame = + target_list_frameinfo; // entrance to the linked list + } + id32_atom->ID32_TagInfo->ID3v2_FrameList = + target_list_frameinfo; // this always points to the last frame that + // had the scan completed + } + + if (fullframesize != 0) { + fulltag_ptr += fullframesize; + } else { + fulltag_ptr += target_list_frameinfo->ID3v2_Frame_Length; + } + if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, + ID32_FRAMEFLAG_GROUPING)) { + fulltag_ptr++; + } + id32_atom->ID32_TagInfo->ID3v2_FrameCount++; + } + + id32_atom->ID32_TagInfo->modified_tag = + false; // if a frame is altered/added/removed, change this to true and + // render the tag & fill id32_atom-AtomicData with the tag + return; } /////////////////////////////////////////////////////////////////////////////////////// -// id3 rendering functions // +// id3 rendering functions // /////////////////////////////////////////////////////////////////////////////////////// -bool APar_LocateFrameSymbol(AtomicInfo* id32_atom, ID3v2Frame* targetFrame, uint8_t groupsymbol) { - ID3v2Frame* testFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame; - while (testFrame != NULL) { - if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID && testFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) { - if (testFrame->ID3v2_Frame_GroupingSymbol == groupsymbol) { - return true; - } - } else if (targetFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) { - if (testFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID && groupsymbol == (uint8_t)(testFrame->ID3v2_Frame_Fields+1)->field_string[0]) { - return true; - } - } - testFrame = testFrame->ID3v2_NextFrame; - } - return false; -} - -void APar_FrameFilter(AtomicInfo* id32_atom) { - ID3v2Frame* MCDI_frame = NULL; - ID3v2Frame* TRCK_frame = NULL; - ID3v2Frame* thisFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame; - while (thisFrame != NULL) { - if (!thisFrame->eliminate_frame) { - if (thisFrame->ID3v2_FrameType == ID3_CD_ID_FRAME) { - MCDI_frame = thisFrame; - } - if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_TRACKNUM) { - TRCK_frame = thisFrame; - } - if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID) { //find any frames containing this symbol; if none are present this frame will be discarded - thisFrame->eliminate_frame = !APar_LocateFrameSymbol(id32_atom, thisFrame, (uint8_t)(thisFrame->ID3v2_Frame_Fields+1)->field_string[0]); - if (!thisFrame->eliminate_frame) { - thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING; - } - - } else if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_SIGNATURE) { //find a GRID frame that contains this symbol (@ field_string, not ID3v2_Frame_GroupingSymbol) - thisFrame->eliminate_frame = !APar_LocateFrameSymbol(id32_atom, thisFrame, (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0]); - //since the group symbol is carried as a field for SIGN, no need to set the frame's grouping bit in the frame flags - - } else if (thisFrame->ID3v2_Frame_GroupingSymbol > 0) { //find a GRID frame that contains this symbol, otherwise discard it - thisFrame->eliminate_frame = !APar_LocateFrameSymbol(id32_atom, thisFrame, thisFrame->ID3v2_Frame_GroupingSymbol); - if (!thisFrame->eliminate_frame) { - thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING; - } - - } - } - thisFrame = thisFrame->ID3v2_NextFrame; - } - - if (MCDI_frame != NULL && TRCK_frame == NULL) { - fprintf(stderr, "AP warning: the MCDI frame was skipped due to a missing TRCK frame\n"); - MCDI_frame->eliminate_frame = true; - } - return; -} - -uint32_t APar_GetTagSize(AtomicInfo* id32_atom) { // a rough approximation of how much to malloc; this will be larger than will be ultimately required - uint32_t tag_len = 0; - uint16_t surviving_frame_count = 0; - if (id32_atom->ID32_TagInfo->modified_tag == false) return tag_len; - if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0) return tag_len; //but a frame isn't removed by AP; its just marked for elimination - if (id32_atom->ID32_TagInfo->ID3v2_FrameList == NULL) return tag_len; //something went wrong somewhere if this wasn't an entry to a linked list of frames - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4) return tag_len; //only id3 version 2.4 tags are written - - ID3v2Frame* eval_frame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame; - while (eval_frame != NULL) { - if (eval_frame->eliminate_frame == true) { - eval_frame = eval_frame->ID3v2_NextFrame; - continue; - } - tag_len += 15; //4bytes frameID 'TCON', 4bytes frame length (syncsafe int), 2 bytes frame flags; optional group symbol: 1byte + decompressed length 4bytes - tag_len += 2*eval_frame->ID3v2_FieldCount; //excess amount to ensure that text fields have utf16 BOMs & 2 byte NULL terminations as required - if (ID3v2_TestFrameFlag(eval_frame->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED)) { - tag_len += eval_frame->ID3v2_Frame_ExpandedLength; - } else { - tag_len += eval_frame->ID3v2_Frame_Length; - } - surviving_frame_count++; - eval_frame = eval_frame->ID3v2_NextFrame; - if (surviving_frame_count == 0 && eval_frame == NULL) { - } - } - if (surviving_frame_count == 0) return 0; //the 'ID3' header alone isn't going to be written with 0 existing frames - return tag_len; -} - -void APar_RenderFields(char* dest_buffer, uint32_t max_alloc, ID3v2Tag* id3_tag, ID3v2Frame* id3v2_frame, uint32_t* frame_header_len, uint32_t* frame_length) { - uint8_t encoding_val = 0; - if (id3v2_frame->ID3v2_Frame_Fields == NULL) { - *frame_header_len = 0; - *frame_length = 0; - return; - } - - for (uint8_t fld_idx = 0; fld_idx < id3v2_frame->ID3v2_FieldCount; fld_idx++) { - ID3v2Fields* this_field = id3v2_frame->ID3v2_Frame_Fields+fld_idx; - //fprintf(stdout, "Total Fields for %s: %u (this is %u, %u)\n", id3v2_frame->ID3v2_Frame_Namestr, id3v2_frame->ID3v2_FieldCount, fld_idx, this_field->ID3v2_Field_Type); - switch(this_field->ID3v2_Field_Type) { - - //these are raw data fields of variable/fixed length and are not NULL terminated - case ID3_UNKNOWN_FIELD : - case ID3_PIC_TYPE_FIELD : - case ID3_GROUPSYMBOL_FIELD : - case ID3_TEXT_ENCODING_FIELD : - case ID3_LANGUAGE_FIELD : - case ID3_COUNTER_FIELD : - case ID3_IMAGEFORMAT_FIELD : - case ID3_URL_FIELD : - case ID3_BINARY_DATA_FIELD : { - APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length); - if (this_field->field_string != NULL) { - memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length); - *frame_length += this_field->field_length; - //fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long (+%" PRIu32 ")\n", fld_idx, this_field->ID3v2_Field_Type, *frame_length, this_field->field_length); - } - break; - } - - //these fields are subject to NULL byte termination - based on what the text encoding field says the encoding of this string is - case ID3_TEXT_FIELD : - case ID3_FILENAME_FIELD : - case ID3_DESCRIPTION_FIELD : { - if (this_field->field_string == NULL) { - *frame_header_len = 0; - *frame_length = 0; - return; - } else { - APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length +2); //+2 for a possible extra NULLs - encoding_val = id3v2_frame->ID3v2_Frame_Fields->field_string[0]; //ID3_TEXT_ENCODING_FIELD is always the first field, and should have an encoding - if ( (id3_tag->ID3v2Tag_MajorVersion == 4 && encoding_val == TE_UTF8) || encoding_val == TE_LATIN1 ) { - if (this_field->ID3v2_Field_Type != ID3_TEXT_FIELD) APar_ValidateNULLTermination8bit(this_field); - - memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length); - *frame_length += this_field->field_length; - - } else if ((id3_tag->ID3v2Tag_MajorVersion == 4 && encoding_val == TE_UTF16LE_WITH_BOM) || encoding_val == TE_UTF16BE_NO_BOM) { - APar_ValidateNULLTermination16bit(this_field, encoding_val); //TODO: shouldn't this also exclude ID3_TEXT_FIELDs? - - memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length); - *frame_length += this_field->field_length; - - } else { //well, AP didn't set this frame, so just duplicate it. - memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length); - *frame_length += this_field->field_length; - } - } - //fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n", fld_idx, this_field->ID3v2_Field_Type, *frame_length); - break; - } - - //these are iso 8859-1 encoded with a single NULL terminator - //a 'LINK' url would also come here and be seperately enumerated (because it has a terminating NULL); but in 3gp assets, external references aren't allowed - //an 'OWNE'/'COMR' price field would also be here because of single byte NULL termination - case ID3_OWNER_FIELD : - case ID3_MIME_TYPE_FIELD : { - if (this_field->field_string == NULL) { - *frame_header_len = 0; - *frame_length = 0; - return; - } else { - APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length +1); //+2 for a possible extra NULLs - - APar_ValidateNULLTermination8bit(this_field); - memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length); - *frame_length += this_field->field_length; - - } - //fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n", fld_idx, this_field->ID3v2_Field_Type, *frame_length); - break; - } - default : { - //fprintf(stdout, "I was unable to determine the field class. I was provided with %u (i.e. text field: %u, text encoding: %u\n", this_field->ID3v2_Field_Type, ID3_TEXT_FIELD, ID3_TEXT_ENCODING_FIELD); - break; - } - - } //end switch - } - if (id3v2_frame->ID3v2_FrameType == ID3_TEXT_FRAME && id3v2_frame->textfield_tally > 1 && id3_tag->ID3v2Tag_MajorVersion == 4) { - ID3v2Fields* extra_textfield = (id3v2_frame->ID3v2_Frame_Fields+1)->next_field; - while (true) { - if (extra_textfield == NULL) break; - - if (encoding_val == TE_UTF8 || encoding_val == TE_LATIN1 ) { - *frame_length+=1; - } else if (encoding_val == TE_UTF16LE_WITH_BOM || encoding_val == TE_UTF16BE_NO_BOM) { - *frame_length+=2; - } - - memcpy(dest_buffer + *frame_length, extra_textfield->field_string, extra_textfield->field_length); - *frame_length += extra_textfield->field_length; - - extra_textfield = extra_textfield->next_field; - } - } - return; -} - -uint32_t APar_Render_ID32_Tag(AtomicInfo* id32_atom, uint32_t max_alloc) { - bool contains_rendered_frames = false; - APar_FrameFilter(id32_atom); - - UInt16_TO_String2(id32_atom->AtomicLanguage, id32_atom->AtomicData); //parsedAtoms[atom_idx].AtomicLanguage - uint64_t tag_offset = 2; //those first 2 bytes will hold the language - uint32_t frame_length, frame_header_len; //the length in bytes this frame consumes in AtomicData as rendered - uint64_t frame_length_pos, frame_compressed_length_pos; - - memcpy(id32_atom->AtomicData + tag_offset, "ID3", 3); - tag_offset+=3; - - id32_atom->AtomicData[tag_offset] = id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion; //should be 4 - id32_atom->AtomicData[tag_offset+1] = id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion; //should be 0 - id32_atom->AtomicData[tag_offset+2] = id32_atom->ID32_TagInfo->ID3v2Tag_Flags; - tag_offset+=3; - - //unknown full length; fill in later - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { - tag_offset+= 4; - if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_EXTENDEDHEADER)) { //currently unimplemented - tag_offset+=10; - } - } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { - tag_offset+= 3; - } - - id32_atom->ID32_TagInfo->ID3v2Tag_Length = tag_offset-2; - - ID3v2Frame* thisframe = id32_atom->ID32_TagInfo->ID3v2_FirstFrame; - while (thisframe != NULL) { - frame_header_len = 0; - frame_length_pos = 0; - frame_compressed_length_pos = 0; - - if (thisframe->eliminate_frame == true) { - thisframe = thisframe->ID3v2_NextFrame; - continue; - } - - contains_rendered_frames = true; - //this won't be able to convert from 1 tag version to another because it doesn't look up the frame id strings for the change - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { - memcpy(id32_atom->AtomicData + tag_offset, thisframe->ID3v2_Frame_Namestr, 4); - frame_header_len += 4; - - //the frame length won't be determined until the end of rendering this frame fully; for now just remember where its supposed to be: - frame_length_pos = tag_offset + frame_header_len; - frame_header_len+=4; - - } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { - memcpy(id32_atom->AtomicData + tag_offset, thisframe->ID3v2_Frame_Namestr, 3); - frame_header_len += 3; - - //the frame length won't be determined until the end of rendering this frame fully; for now just remember where its supposed to be: - frame_length_pos = tag_offset + frame_header_len; - frame_header_len+=3; - } - - //render frame flags //TODO: compression & group symbol are the only ones that can possibly be set here - if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { - UInt16_TO_String2(thisframe->ID3v2_Frame_Flags, id32_atom->AtomicData + tag_offset + frame_header_len); - frame_header_len+=2; - } - - //grouping flag? 1 byte; technically, its outside the header and before the fields begin - if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, ID32_FRAMEFLAG_GROUPING)) { - id32_atom->AtomicData[tag_offset + frame_header_len] = thisframe->ID3v2_Frame_GroupingSymbol; - frame_header_len++; - } - - //compression flag? 4bytes; technically, its outside the header and before the fields begin - if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED)) { - frame_compressed_length_pos = tag_offset + frame_header_len; //fill in later; remember where it is supposed to go - frame_header_len+=4; - } - - frame_length = 0; - APar_RenderFields(id32_atom->AtomicData + tag_offset+frame_header_len, - max_alloc-tag_offset, id32_atom->ID32_TagInfo, thisframe, - &frame_header_len, &frame_length); - +bool APar_LocateFrameSymbol(AtomicInfo *id32_atom, + ID3v2Frame *targetFrame, + uint8_t groupsymbol) { + ID3v2Frame *testFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame; + while (testFrame != NULL) { + if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID && + testFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) { + if (testFrame->ID3v2_Frame_GroupingSymbol == groupsymbol) { + return true; + } + } else if (targetFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) { + if (testFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID && + groupsymbol == + (uint8_t)(testFrame->ID3v2_Frame_Fields + 1)->field_string[0]) { + return true; + } + } + testFrame = testFrame->ID3v2_NextFrame; + } + return false; +} + +void APar_FrameFilter(AtomicInfo *id32_atom) { + ID3v2Frame *MCDI_frame = NULL; + ID3v2Frame *TRCK_frame = NULL; + ID3v2Frame *thisFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame; + while (thisFrame != NULL) { + if (!thisFrame->eliminate_frame) { + if (thisFrame->ID3v2_FrameType == ID3_CD_ID_FRAME) { + MCDI_frame = thisFrame; + } + if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_TRACKNUM) { + TRCK_frame = thisFrame; + } + if (thisFrame->ID3v2_Frame_ID == + ID3v2_FRAME_GRID) { // find any frames containing this symbol; if none + // are present this frame will be discarded + thisFrame->eliminate_frame = !APar_LocateFrameSymbol( + id32_atom, + thisFrame, + (uint8_t)(thisFrame->ID3v2_Frame_Fields + 1)->field_string[0]); + if (!thisFrame->eliminate_frame) { + thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING; + } + + } else if (thisFrame->ID3v2_Frame_ID == + ID3v2_FRAME_SIGNATURE) { // find a GRID frame that contains + // this symbol (@ field_string, not + // ID3v2_Frame_GroupingSymbol) + thisFrame->eliminate_frame = !APar_LocateFrameSymbol( + id32_atom, + thisFrame, + (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0]); + // since the group symbol is carried as a field for SIGN, no need to set + // the frame's grouping bit in the frame flags + + } else if (thisFrame->ID3v2_Frame_GroupingSymbol > + 0) { // find a GRID frame that contains this symbol, otherwise + // discard it + thisFrame->eliminate_frame = !APar_LocateFrameSymbol( + id32_atom, thisFrame, thisFrame->ID3v2_Frame_GroupingSymbol); + if (!thisFrame->eliminate_frame) { + thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING; + } + } + } + thisFrame = thisFrame->ID3v2_NextFrame; + } + + if (MCDI_frame != NULL && TRCK_frame == NULL) { + fprintf( + stderr, + "AP warning: the MCDI frame was skipped due to a missing TRCK frame\n"); + MCDI_frame->eliminate_frame = true; + } + return; +} + +uint32_t APar_GetTagSize( + AtomicInfo + *id32_atom) { // a rough approximation of how much to malloc; this will + // be larger than will be ultimately required + uint32_t tag_len = 0; + uint16_t surviving_frame_count = 0; + if (id32_atom->ID32_TagInfo->modified_tag == false) + return tag_len; + if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0) + return tag_len; // but a frame isn't removed by AP; its just marked for + // elimination + if (id32_atom->ID32_TagInfo->ID3v2_FrameList == NULL) + return tag_len; // something went wrong somewhere if this wasn't an entry to + // a linked list of frames + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4) + return tag_len; // only id3 version 2.4 tags are written + + ID3v2Frame *eval_frame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame; + while (eval_frame != NULL) { + if (eval_frame->eliminate_frame == true) { + eval_frame = eval_frame->ID3v2_NextFrame; + continue; + } + tag_len += 15; // 4bytes frameID 'TCON', 4bytes frame length (syncsafe int), + // 2 bytes frame flags; optional group symbol: 1byte + + // decompressed length 4bytes + tag_len += + 2 * eval_frame->ID3v2_FieldCount; // excess amount to ensure that text + // fields have utf16 BOMs & 2 byte + // NULL terminations as required + if (ID3v2_TestFrameFlag(eval_frame->ID3v2_Frame_Flags, + ID32_FRAMEFLAG_COMPRESSED)) { + tag_len += eval_frame->ID3v2_Frame_ExpandedLength; + } else { + tag_len += eval_frame->ID3v2_Frame_Length; + } + surviving_frame_count++; + eval_frame = eval_frame->ID3v2_NextFrame; + if (surviving_frame_count == 0 && eval_frame == NULL) { + } + } + if (surviving_frame_count == 0) + return 0; // the 'ID3' header alone isn't going to be written with 0 + // existing frames + return tag_len; +} + +void APar_RenderFields(char *dest_buffer, + uint32_t max_alloc, + ID3v2Tag *id3_tag, + ID3v2Frame *id3v2_frame, + uint32_t *frame_header_len, + uint32_t *frame_length) { + uint8_t encoding_val = 0; + if (id3v2_frame->ID3v2_Frame_Fields == NULL) { + *frame_header_len = 0; + *frame_length = 0; + return; + } + + for (uint8_t fld_idx = 0; fld_idx < id3v2_frame->ID3v2_FieldCount; + fld_idx++) { + ID3v2Fields *this_field = id3v2_frame->ID3v2_Frame_Fields + fld_idx; + // fprintf(stdout, "Total Fields for %s: %u (this is %u, %u)\n", + // id3v2_frame->ID3v2_Frame_Namestr, id3v2_frame->ID3v2_FieldCount, fld_idx, + // this_field->ID3v2_Field_Type); + switch (this_field->ID3v2_Field_Type) { + + // these are raw data fields of variable/fixed length and are not NULL + // terminated + case ID3_UNKNOWN_FIELD: + case ID3_PIC_TYPE_FIELD: + case ID3_GROUPSYMBOL_FIELD: + case ID3_TEXT_ENCODING_FIELD: + case ID3_LANGUAGE_FIELD: + case ID3_COUNTER_FIELD: + case ID3_IMAGEFORMAT_FIELD: + case ID3_URL_FIELD: + case ID3_BINARY_DATA_FIELD: { + APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length); + if (this_field->field_string != NULL) { + memcpy(dest_buffer + *frame_length, + this_field->field_string, + this_field->field_length); + *frame_length += this_field->field_length; + // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long (+%" + // PRIu32 ")\n", fld_idx, this_field->ID3v2_Field_Type, *frame_length, + // this_field->field_length); + } + break; + } + + // these fields are subject to NULL byte termination - based on what the + // text encoding field says the encoding of this string is + case ID3_TEXT_FIELD: + case ID3_FILENAME_FIELD: + case ID3_DESCRIPTION_FIELD: { + if (this_field->field_string == NULL) { + *frame_header_len = 0; + *frame_length = 0; + return; + } else { + APar_LimitBufferRange(max_alloc, + *frame_header_len + *frame_length + + 2); //+2 for a possible extra NULLs + encoding_val = + id3v2_frame->ID3v2_Frame_Fields + ->field_string[0]; // ID3_TEXT_ENCODING_FIELD is always the + // first field, and should have an encoding + if ((id3_tag->ID3v2Tag_MajorVersion == 4 && encoding_val == TE_UTF8) || + encoding_val == TE_LATIN1) { + if (this_field->ID3v2_Field_Type != ID3_TEXT_FIELD) + APar_ValidateNULLTermination8bit(this_field); + + memcpy(dest_buffer + *frame_length, + this_field->field_string, + this_field->field_length); + *frame_length += this_field->field_length; + + } else if ((id3_tag->ID3v2Tag_MajorVersion == 4 && + encoding_val == TE_UTF16LE_WITH_BOM) || + encoding_val == TE_UTF16BE_NO_BOM) { + APar_ValidateNULLTermination16bit( + this_field, encoding_val); // TODO: shouldn't this also exclude + // ID3_TEXT_FIELDs? + + memcpy(dest_buffer + *frame_length, + this_field->field_string, + this_field->field_length); + *frame_length += this_field->field_length; + + } else { // well, AP didn't set this frame, so just duplicate it. + memcpy(dest_buffer + *frame_length, + this_field->field_string, + this_field->field_length); + *frame_length += this_field->field_length; + } + } + // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n", + // fld_idx, this_field->ID3v2_Field_Type, *frame_length); + break; + } + + // these are iso 8859-1 encoded with a single NULL terminator + // a 'LINK' url would also come here and be seperately enumerated (because + // it has a terminating NULL); but in 3gp assets, external references aren't + // allowed an 'OWNE'/'COMR' price field would also be here because of single + // byte NULL termination + case ID3_OWNER_FIELD: + case ID3_MIME_TYPE_FIELD: { + if (this_field->field_string == NULL) { + *frame_header_len = 0; + *frame_length = 0; + return; + } else { + APar_LimitBufferRange(max_alloc, + *frame_header_len + *frame_length + + 1); //+2 for a possible extra NULLs + + APar_ValidateNULLTermination8bit(this_field); + memcpy(dest_buffer + *frame_length, + this_field->field_string, + this_field->field_length); + *frame_length += this_field->field_length; + } + // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n", + // fld_idx, this_field->ID3v2_Field_Type, *frame_length); + break; + } + default: { + // fprintf(stdout, "I was unable to determine the field class. I was + // provided with %u (i.e. text field: %u, text encoding: %u\n", + // this_field->ID3v2_Field_Type, ID3_TEXT_FIELD, ID3_TEXT_ENCODING_FIELD); + break; + } + + } // end switch + } + if (id3v2_frame->ID3v2_FrameType == ID3_TEXT_FRAME && + id3v2_frame->textfield_tally > 1 && id3_tag->ID3v2Tag_MajorVersion == 4) { + ID3v2Fields *extra_textfield = + (id3v2_frame->ID3v2_Frame_Fields + 1)->next_field; + while (true) { + if (extra_textfield == NULL) + break; + + if (encoding_val == TE_UTF8 || encoding_val == TE_LATIN1) { + *frame_length += 1; + } else if (encoding_val == TE_UTF16LE_WITH_BOM || + encoding_val == TE_UTF16BE_NO_BOM) { + *frame_length += 2; + } + + memcpy(dest_buffer + *frame_length, + extra_textfield->field_string, + extra_textfield->field_length); + *frame_length += extra_textfield->field_length; + + extra_textfield = extra_textfield->next_field; + } + } + return; +} + +uint32_t APar_Render_ID32_Tag(AtomicInfo *id32_atom, uint32_t max_alloc) { + bool contains_rendered_frames = false; + APar_FrameFilter(id32_atom); + + UInt16_TO_String2( + id32_atom->AtomicLanguage, + id32_atom->AtomicData); // parsedAtoms[atom_idx].AtomicLanguage + uint64_t tag_offset = 2; // those first 2 bytes will hold the language + uint32_t frame_length, frame_header_len; // the length in bytes this frame + // consumes in AtomicData as rendered + uint64_t frame_length_pos, frame_compressed_length_pos; + + memcpy(id32_atom->AtomicData + tag_offset, "ID3", 3); + tag_offset += 3; + + id32_atom->AtomicData[tag_offset] = + id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion; // should be 4 + id32_atom->AtomicData[tag_offset + 1] = + id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion; // should be 0 + id32_atom->AtomicData[tag_offset + 2] = + id32_atom->ID32_TagInfo->ID3v2Tag_Flags; + tag_offset += 3; + + // unknown full length; fill in later + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || + id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { + tag_offset += 4; + if (ID3v2_TestTagFlag( + id32_atom->ID32_TagInfo->ID3v2Tag_Flags, + ID32_TAGFLAG_EXTENDEDHEADER)) { // currently unimplemented + tag_offset += 10; + } + } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { + tag_offset += 3; + } + + id32_atom->ID32_TagInfo->ID3v2Tag_Length = tag_offset - 2; + + ID3v2Frame *thisframe = id32_atom->ID32_TagInfo->ID3v2_FirstFrame; + while (thisframe != NULL) { + frame_header_len = 0; + frame_length_pos = 0; + frame_compressed_length_pos = 0; + + if (thisframe->eliminate_frame == true) { + thisframe = thisframe->ID3v2_NextFrame; + continue; + } + + contains_rendered_frames = true; + // this won't be able to convert from 1 tag version to another because it + // doesn't look up the frame id strings for the change + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || + id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { + memcpy(id32_atom->AtomicData + tag_offset, + thisframe->ID3v2_Frame_Namestr, + 4); + frame_header_len += 4; + + // the frame length won't be determined until the end of rendering this + // frame fully; for now just remember where its supposed to be: + frame_length_pos = tag_offset + frame_header_len; + frame_header_len += 4; + + } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) { + memcpy(id32_atom->AtomicData + tag_offset, + thisframe->ID3v2_Frame_Namestr, + 3); + frame_header_len += 3; + + // the frame length won't be determined until the end of rendering this + // frame fully; for now just remember where its supposed to be: + frame_length_pos = tag_offset + frame_header_len; + frame_header_len += 3; + } + + // render frame flags //TODO: compression & group symbol are the only ones + // that can possibly be set here + if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || + id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) { + UInt16_TO_String2(thisframe->ID3v2_Frame_Flags, + id32_atom->AtomicData + tag_offset + frame_header_len); + frame_header_len += 2; + } + + // grouping flag? 1 byte; technically, its outside the header and before the + // fields begin + if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, + ID32_FRAMEFLAG_GROUPING)) { + id32_atom->AtomicData[tag_offset + frame_header_len] = + thisframe->ID3v2_Frame_GroupingSymbol; + frame_header_len++; + } + + // compression flag? 4bytes; technically, its outside the header and before + // the fields begin + if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, + ID32_FRAMEFLAG_COMPRESSED)) { + frame_compressed_length_pos = + tag_offset + frame_header_len; // fill in later; remember where it is + // supposed to go + frame_header_len += 4; + } + + frame_length = 0; + APar_RenderFields(id32_atom->AtomicData + tag_offset + frame_header_len, + max_alloc - tag_offset, + id32_atom->ID32_TagInfo, + thisframe, + &frame_header_len, + &frame_length); + #if defined HAVE_ZLIB_H - //and now that we have rendered the frame, its time to turn to compression, if set - if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED) ) { - uint32_t compressed_len = 0; - char* compress_buffer = (char*)calloc(1, sizeof(char)*frame_length + 20); - - compressed_len = APar_zlib_deflate(id32_atom->AtomicData + tag_offset+frame_header_len, frame_length, compress_buffer, frame_length + 20); - - if (compressed_len > 0) { - memcpy(id32_atom->AtomicData + tag_offset+frame_header_len, compress_buffer, compressed_len + 1); - convert_to_syncsafe32(frame_length, id32_atom->AtomicData + frame_compressed_length_pos); - frame_length = compressed_len; - - //WriteZlibData(id32_atom->AtomicData + tag_offset+frame_header_len, compressed_len); - } - } + // and now that we have rendered the frame, its time to turn to compression, + // if set + if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, + ID32_FRAMEFLAG_COMPRESSED)) { + uint32_t compressed_len = 0; + char *compress_buffer = + (char *)calloc(1, sizeof(char) * frame_length + 20); + + compressed_len = APar_zlib_deflate(id32_atom->AtomicData + tag_offset + + frame_header_len, + frame_length, + compress_buffer, + frame_length + 20); + + if (compressed_len > 0) { + memcpy(id32_atom->AtomicData + tag_offset + frame_header_len, + compress_buffer, + compressed_len + 1); + convert_to_syncsafe32( + frame_length, id32_atom->AtomicData + frame_compressed_length_pos); + frame_length = compressed_len; + + // WriteZlibData(id32_atom->AtomicData + tag_offset+frame_header_len, + // compressed_len); + } + } #endif - - convert_to_syncsafe32(frame_length, id32_atom->AtomicData + frame_length_pos); - tag_offset += frame_header_len + frame_length; //advance - id32_atom->ID32_TagInfo->ID3v2Tag_Length += frame_header_len + frame_length; - thisframe = thisframe->ID3v2_NextFrame; - - } - convert_to_syncsafe32(id32_atom->ID32_TagInfo->ID3v2Tag_Length - 10, id32_atom->AtomicData + 8); //-10 for a v2.4 tag with no extended header - - if (!contains_rendered_frames) id32_atom->ID32_TagInfo->ID3v2Tag_Length = 0; - return id32_atom->ID32_TagInfo->ID3v2Tag_Length; + convert_to_syncsafe32(frame_length, + id32_atom->AtomicData + frame_length_pos); + tag_offset += frame_header_len + frame_length; // advance + id32_atom->ID32_TagInfo->ID3v2Tag_Length += frame_header_len + frame_length; + thisframe = thisframe->ID3v2_NextFrame; + } + convert_to_syncsafe32(id32_atom->ID32_TagInfo->ID3v2Tag_Length - 10, + id32_atom->AtomicData + + 8); //-10 for a v2.4 tag with no extended header + + if (!contains_rendered_frames) + id32_atom->ID32_TagInfo->ID3v2Tag_Length = 0; + + return id32_atom->ID32_TagInfo->ID3v2Tag_Length; } /////////////////////////////////////////////////////////////////////////////////////// -// id3 initializing functions // +// id3 initializing functions // /////////////////////////////////////////////////////////////////////////////////////// -void APar_FieldInit(ID3v2Frame* aFrame, uint8_t a_field, uint8_t frame_comp_list, const char* frame_payload) { - uint32_t byte_allocation = 0; - ID3v2Fields* this_field = NULL; - int field_type = FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field]; - - switch(field_type) { - //case ID3_UNKNOWN_FIELD will not be handled - - //these are all 1 to less than 16 bytes. - case ID3_GROUPSYMBOL_FIELD : - case ID3_COUNTER_FIELD : - case ID3_PIC_TYPE_FIELD : - case ID3_LANGUAGE_FIELD : - case ID3_IMAGEFORMAT_FIELD : //PIC in v2.2 - case ID3_TEXT_ENCODING_FIELD : { - byte_allocation = 16; - break; - } - - //between 16 & 100 bytes. - case ID3_MIME_TYPE_FIELD : { - byte_allocation = 100; - break; - } - - //these are allocated with 2000 bytes - case ID3_FILENAME_FIELD : - case ID3_OWNER_FIELD : - case ID3_DESCRIPTION_FIELD : - case ID3_URL_FIELD : - case ID3_TEXT_FIELD : { - uint32_t string_len = strlen(frame_payload) + 1; - if (string_len * 2 > 2000) { - byte_allocation = string_len * 2; - } else { - byte_allocation = 2000; - } - break; - } - - case ID3_BINARY_DATA_FIELD : { - if (aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_PICTURE || aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_OBJECT ) { - //this will be left NULL because it would would probably have to be realloced, so just do it later to the right size //byte_allocation = findFileSize(frame_payload) + 1; //this should be limited to max_sync_safe_uint28_t - } else { - byte_allocation = 2000; - } - break; - } - - //default : { - // fprintf(stdout, "I am %d\n", FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field]); - // break; - //} - } - this_field = aFrame->ID3v2_Frame_Fields + a_field; - this_field->ID3v2_Field_Type = field_type; - if (byte_allocation > 0) { - this_field->field_string = (char*)calloc(1, sizeof(char*)*byte_allocation); - if (!APar_assert((this_field->field_string != NULL), 11, aFrame->ID3v2_Frame_Namestr) ) exit(11); - } else { - this_field->field_string = NULL; - } - this_field->field_length = 0; - this_field->alloc_length = byte_allocation; - this_field->next_field = NULL; - //fprintf(stdout, "For %u field, %" PRIu32 " bytes were allocated.\n", this_field->ID3v2_Field_Type, byte_allocation); - return; -} - -void APar_FrameInit(ID3v2Frame* aFrame, const char* frame_str, int frameID, uint8_t frame_comp_list, const char* frame_payload) { - aFrame->ID3v2_FieldCount = FrameTypeConstructionList[frame_comp_list].ID3_FieldCount; - if (aFrame->ID3v2_FieldCount > 0) { - aFrame->ID3v2_Frame_Fields = (ID3v2Fields*)calloc(1, sizeof(ID3v2Fields)*aFrame->ID3v2_FieldCount); - aFrame->ID3v2_Frame_ID = frameID; - aFrame->ID3v2_FrameType = FrameTypeConstructionList[frame_comp_list].ID3_FrameType; - aFrame->ID3v2_Frame_ExpandedLength = 0; - aFrame->ID3v2_Frame_GroupingSymbol = 0; - aFrame->ID3v2_Frame_Flags = 0; - aFrame->ID3v2_Frame_Length = 0; - aFrame->textfield_tally = 0; - aFrame->eliminate_frame = false; - memcpy(aFrame->ID3v2_Frame_Namestr, frame_str, 5); - - for (uint8_t fld = 0; fld < aFrame->ID3v2_FieldCount; fld++) { - APar_FieldInit(aFrame, fld, frame_comp_list, frame_payload); - } - - //fprintf(stdout, "(%u = %d) Type %d\n", frameID, KnownFrames[frameID+1].ID3v2_InternalFrameID, aFrame->ID3v2_FrameType); - } - //fprintf(stdout, "Retrieved frame for '%s': %s (%u fields)\n", frame_str, KnownFrames[frameID].ID3V2p4_FrameID, aFrame->ID3v2_FieldCount); - return; -} - -void APar_ID3Tag_Init(AtomicInfo* id32_atom) { - id32_atom->ID32_TagInfo = (ID3v2Tag*)calloc(1, sizeof(ID3v2Tag)); - id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion = AtomicParsley_ID3v2Tag_MajorVersion; - id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion = AtomicParsley_ID3v2Tag_RevisionVersion; - id32_atom->ID32_TagInfo->ID3v2Tag_Flags = AtomicParsley_ID3v2Tag_Flags; - id32_atom->ID32_TagInfo->ID3v2Tag_Length = 10; //this would be 9 for v2.2 - id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = 0; - id32_atom->ID32_TagInfo->ID3v2_FrameCount = 0; - id32_atom->ID32_TagInfo->modified_tag = false; //this will have to change when a frame is added/modified/removed because this id3 header won't be written with 0 frames - - id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL; - id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL; - return; -} - -void APar_realloc_memcpy(ID3v2Fields* thisField, uint32_t new_size) { - if (new_size > thisField->alloc_length) { - char* new_alloc = (char*)calloc(1, sizeof(char*)*new_size + 1); - //memcpy(new_alloc, thisField->field_string, thisField->field_length); - thisField->field_length = 0; - free(thisField->field_string); - thisField->field_string = new_alloc; - thisField->alloc_length = new_size; - } - return; +void APar_FieldInit(ID3v2Frame *aFrame, + uint8_t a_field, + uint8_t frame_comp_list, + const char *frame_payload) { + uint32_t byte_allocation = 0; + ID3v2Fields *this_field = NULL; + int field_type = + FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field]; + + switch (field_type) { + // case ID3_UNKNOWN_FIELD will not be handled + + // these are all 1 to less than 16 bytes. + case ID3_GROUPSYMBOL_FIELD: + case ID3_COUNTER_FIELD: + case ID3_PIC_TYPE_FIELD: + case ID3_LANGUAGE_FIELD: + case ID3_IMAGEFORMAT_FIELD: // PIC in v2.2 + case ID3_TEXT_ENCODING_FIELD: { + byte_allocation = 16; + break; + } + + // between 16 & 100 bytes. + case ID3_MIME_TYPE_FIELD: { + byte_allocation = 100; + break; + } + + // these are allocated with 2000 bytes + case ID3_FILENAME_FIELD: + case ID3_OWNER_FIELD: + case ID3_DESCRIPTION_FIELD: + case ID3_URL_FIELD: + case ID3_TEXT_FIELD: { + uint32_t string_len = strlen(frame_payload) + 1; + if (string_len * 2 > 2000) { + byte_allocation = string_len * 2; + } else { + byte_allocation = 2000; + } + break; + } + + case ID3_BINARY_DATA_FIELD: { + if (aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_PICTURE || + aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_OBJECT) { + // this will be left NULL because it would would probably have to be + // realloced, so just do it later to the right size //byte_allocation = + // findFileSize(frame_payload) + 1; //this should be limited to + // max_sync_safe_uint28_t + } else { + byte_allocation = 2000; + } + break; + } + + // default : { + // fprintf(stdout, "I am %d\n", + // FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field]); + // break; + //} + } + this_field = aFrame->ID3v2_Frame_Fields + a_field; + this_field->ID3v2_Field_Type = field_type; + if (byte_allocation > 0) { + this_field->field_string = + (char *)calloc(1, sizeof(char *) * byte_allocation); + if (!APar_assert((this_field->field_string != NULL), + 11, + aFrame->ID3v2_Frame_Namestr)) + exit(11); + } else { + this_field->field_string = NULL; + } + this_field->field_length = 0; + this_field->alloc_length = byte_allocation; + this_field->next_field = NULL; + // fprintf(stdout, "For %u field, %" PRIu32 " bytes were allocated.\n", + // this_field->ID3v2_Field_Type, byte_allocation); + return; +} + +void APar_FrameInit(ID3v2Frame *aFrame, + const char *frame_str, + int frameID, + uint8_t frame_comp_list, + const char *frame_payload) { + aFrame->ID3v2_FieldCount = + FrameTypeConstructionList[frame_comp_list].ID3_FieldCount; + if (aFrame->ID3v2_FieldCount > 0) { + aFrame->ID3v2_Frame_Fields = (ID3v2Fields *)calloc( + 1, sizeof(ID3v2Fields) * aFrame->ID3v2_FieldCount); + aFrame->ID3v2_Frame_ID = frameID; + aFrame->ID3v2_FrameType = + FrameTypeConstructionList[frame_comp_list].ID3_FrameType; + aFrame->ID3v2_Frame_ExpandedLength = 0; + aFrame->ID3v2_Frame_GroupingSymbol = 0; + aFrame->ID3v2_Frame_Flags = 0; + aFrame->ID3v2_Frame_Length = 0; + aFrame->textfield_tally = 0; + aFrame->eliminate_frame = false; + memcpy(aFrame->ID3v2_Frame_Namestr, frame_str, 5); + + for (uint8_t fld = 0; fld < aFrame->ID3v2_FieldCount; fld++) { + APar_FieldInit(aFrame, fld, frame_comp_list, frame_payload); + } + + // fprintf(stdout, "(%u = %d) Type %d\n", frameID, + // KnownFrames[frameID+1].ID3v2_InternalFrameID, aFrame->ID3v2_FrameType); + } + // fprintf(stdout, "Retrieved frame for '%s': %s (%u fields)\n", frame_str, + // KnownFrames[frameID].ID3V2p4_FrameID, aFrame->ID3v2_FieldCount); + return; +} + +void APar_ID3Tag_Init(AtomicInfo *id32_atom) { + id32_atom->ID32_TagInfo = (ID3v2Tag *)calloc(1, sizeof(ID3v2Tag)); + id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion = + AtomicParsley_ID3v2Tag_MajorVersion; + id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion = + AtomicParsley_ID3v2Tag_RevisionVersion; + id32_atom->ID32_TagInfo->ID3v2Tag_Flags = AtomicParsley_ID3v2Tag_Flags; + id32_atom->ID32_TagInfo->ID3v2Tag_Length = 10; // this would be 9 for v2.2 + id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = 0; + id32_atom->ID32_TagInfo->ID3v2_FrameCount = 0; + id32_atom->ID32_TagInfo->modified_tag = + false; // this will have to change when a frame is added/modified/removed + // because this id3 header won't be written with 0 frames + + id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL; + id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL; + return; +} + +void APar_realloc_memcpy(ID3v2Fields *thisField, uint32_t new_size) { + if (new_size > thisField->alloc_length) { + char *new_alloc = (char *)calloc(1, sizeof(char *) * new_size + 1); + // memcpy(new_alloc, thisField->field_string, thisField->field_length); + thisField->field_length = 0; + free(thisField->field_string); + thisField->field_string = new_alloc; + thisField->alloc_length = new_size; + } + return; } /////////////////////////////////////////////////////////////////////////////////////// -// id3 frame setting/finding functions // +// id3 frame setting/finding functions // /////////////////////////////////////////////////////////////////////////////////////// -uint32_t APar_TextFieldDataPut(ID3v2Fields* thisField, const char* this_payload, uint8_t str_encoding, bool multistringtext = false) { - uint32_t bytes_used = 0; - - if (multistringtext == false) { - thisField->field_length = 0; - } - - if (str_encoding == TE_UTF8) { - bytes_used = strlen(this_payload); //no NULL termination is provided until render time - if (bytes_used + thisField->field_length > thisField->alloc_length) { - APar_realloc_memcpy(thisField, (bytes_used > 2000 ? bytes_used : 2000) ); - } - memcpy(thisField->field_string + thisField->field_length, this_payload, bytes_used); - thisField->field_length += bytes_used; - - } else if (str_encoding == TE_LATIN1) { - int string_length = strlen(this_payload); - if (string_length + thisField->field_length > thisField->alloc_length) { - APar_realloc_memcpy(thisField, (string_length > 2000 ? string_length : 2000) ); - } - int converted_bytes = UTF8Toisolat1((unsigned char*)thisField->field_string + thisField->field_length, (int)thisField->alloc_length, - (unsigned char*)this_payload, string_length); - if (converted_bytes > 0) { - thisField->field_length += converted_bytes; - bytes_used = converted_bytes; - //fprintf(stdout, "string %s, %" PRIu32 "=%" PRIu32 "\n", thisField->field_string, thisField->field_length, bytes_used); - } - - } else if (str_encoding == TE_UTF16BE_NO_BOM) { - int string_length = (int)utf8_length(this_payload, strlen(this_payload)) + 1; - if (2 * string_length + thisField->field_length > thisField->alloc_length) { - APar_realloc_memcpy(thisField, (2 * string_length + thisField->field_length > 2000 ? 2 * string_length + thisField->field_length : 2000) ); - } - int converted_bytes = UTF8ToUTF16BE((unsigned char*)thisField->field_string + thisField->field_length, (int)thisField->alloc_length, - (unsigned char*)this_payload, string_length); - if (converted_bytes > 0) { - thisField->field_length += converted_bytes; - bytes_used = converted_bytes; - } - - } else if (str_encoding == TE_UTF16LE_WITH_BOM) { - int string_length = (int)utf8_length(this_payload, strlen(this_payload)) + 1; - uint64_t bom_offset = 0; - - if (2 * string_length + thisField->field_length > thisField->alloc_length) { //important: realloc before BOM testing!!! - APar_realloc_memcpy(thisField, (2 * string_length + thisField->field_length > 2000 ? 2 * string_length + thisField->field_length : 2000) ); - } - if (thisField->field_length == 0 && multistringtext == false) { - memcpy(thisField->field_string, "\xFF\xFE", 2); - } - - uint8_t field_encoding = TextField_TestBOM(thisField->field_string); - if (field_encoding > 0) { - bom_offset = 2; - } - int converted_bytes = UTF8ToUTF16LE((unsigned char*)thisField->field_string + thisField->field_length + bom_offset, (int)thisField->alloc_length, - (unsigned char*)this_payload, string_length); - if (converted_bytes > 0) { - thisField->field_length += converted_bytes + bom_offset; - bytes_used = converted_bytes; - } - } - - if (multistringtext != false) { - if (str_encoding == TE_UTF16LE_WITH_BOM || str_encoding == TE_UTF16BE_NO_BOM) { - bytes_used += 2; - } else { - bytes_used += 1; - } - } - return bytes_used; -} - -uint32_t APar_BinaryFieldPut(ID3v2Fields* thisField, uint32_t a_number, const char* this_payload, uint32_t payload_len) { - if (thisField->ID3v2_Field_Type == ID3_TEXT_ENCODING_FIELD || thisField->ID3v2_Field_Type == ID3_PIC_TYPE_FIELD || thisField->ID3v2_Field_Type == ID3_GROUPSYMBOL_FIELD) { - thisField->field_string[0] = (unsigned char)a_number; - thisField->field_length = 1; - //fprintf(stdout, "My (TE/PT) content is 0x%02X\n", thisField->field_string[0]); - return 1; - - } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD && payload_len == 0) { //contents of a file - uint64_t file_length = findFileSize(this_payload); - thisField->field_string = (char*)calloc(1, sizeof(char*)*file_length+16); - - FILE* binfile = APar_OpenFile(this_payload, "rb"); - APar_ReadFile(thisField->field_string, binfile, file_length); - fclose(binfile); - - thisField->field_length = file_length; - thisField->alloc_length = file_length+16; - thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD; - return file_length; - - } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD || thisField->ID3v2_Field_Type == ID3_COUNTER_FIELD) { - thisField->field_string = (char*)calloc(1, sizeof(char*)*payload_len+16); - memcpy(thisField->field_string, this_payload, payload_len); - - thisField->field_length = payload_len; - thisField->alloc_length = payload_len+16; - thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD; - return payload_len; - - } - return 0; -} - -void APar_FrameDataPut(ID3v2Frame* thisFrame, const char* frame_payload, AdjunctArgs* adjunct_payload, uint8_t str_encoding) { - if (adjunct_payload->multistringtext == false && !APar_EvalFrame_for_Field(thisFrame->ID3v2_FrameType, ID3_COUNTER_FIELD) ) thisFrame->ID3v2_Frame_Length = 0; - switch(thisFrame->ID3v2_FrameType) { - case ID3_TEXT_FRAME : { - if (adjunct_payload->multistringtext && thisFrame->textfield_tally >= 1) { - ID3v2Fields* last_textfield = APar_FindLastTextField (thisFrame); - if (APar_ExtraTextFieldInit(last_textfield, strlen(frame_payload), (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0])) { - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(last_textfield->next_field, frame_payload, (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0], true); - } - } else { - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, frame_payload, str_encoding, false); //text field - GlobalID3Tag->ID3v2_FrameCount++; - } - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - //GlobalID3Tag->ID3v2_FrameCount++; //don't do this for all text frames because the multiple text field support of id3v2.4; only when the frame is initially set - thisFrame->textfield_tally++; - break; - } - case ID3_TEXT_FRAME_USERDEF : { - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->descripArg, str_encoding); //language - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, frame_payload, str_encoding); //text field - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - case ID3_DESCRIBED_TEXT_FRAME : { - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->targetLang, 0); //language - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, adjunct_payload->descripArg, str_encoding); //description - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+3, frame_payload, str_encoding, adjunct_payload->multistringtext); //text field - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - case ID3_URL_FRAME : { - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //url field - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - case ID3_URL_FRAME_USERDEF : { - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->descripArg, str_encoding); //language - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, frame_payload, TE_LATIN1); //url field - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - case ID3_UNIQUE_FILE_ID_FRAME : { - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //owner field - - if (memcmp(adjunct_payload->dataArg, "randomUUIDstamp", 16) == 0) { - char uuid_binary_str[25]; memset(uuid_binary_str, 0, 25); - APar_generate_random_uuid(uuid_binary_str); - (thisFrame->ID3v2_Frame_Fields+1)->field_string = (char*)calloc(1, sizeof(char*)*40); - APar_sprintf_uuid((ap_uuid_t*)uuid_binary_str, (thisFrame->ID3v2_Frame_Fields+1)->field_string); - - (thisFrame->ID3v2_Frame_Fields+1)->field_length = 36; - (thisFrame->ID3v2_Frame_Fields+1)->alloc_length = 40; - (thisFrame->ID3v2_Frame_Fields+1)->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD; - thisFrame->ID3v2_Frame_Length += 36; - } else { - uint8_t uniqueIDlen = strlen(adjunct_payload->dataArg); - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, adjunct_payload->dataArg, (uniqueIDlen > 64 ? 64 : uniqueIDlen)); //unique file ID - } - - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - case ID3_CD_ID_FRAME : { - thisFrame->ID3v2_Frame_Fields->field_length = GenerateMCDIfromCD(frame_payload, thisFrame->ID3v2_Frame_Fields->field_string); - thisFrame->ID3v2_Frame_Length = thisFrame->ID3v2_Frame_Fields->field_length; - - if (thisFrame->ID3v2_Frame_Length < 12) { - free(thisFrame->ID3v2_Frame_Fields->field_string); - thisFrame->ID3v2_Frame_Fields->field_string = NULL; - thisFrame->ID3v2_Frame_Fields->alloc_length = 0; - thisFrame->ID3v2_Frame_Length = 0; - } else { - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - } - break; - } - case ID3_ATTACHED_PICTURE_FRAME : { - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->mimeArg, TE_LATIN1); //mimetype - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+2, adjunct_payload->pictype_uint8, NULL, 1); //picturetype - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+3, adjunct_payload->descripArg, str_encoding); //description - //(thisFrame->ID3v2_Frame_Fields+4)->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD; //because it wasn't malloced, this needs to be set now - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+4, 0, frame_payload, 0); //binary file (path) - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - case ID3_ATTACHED_OBJECT_FRAME : { - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->mimeArg, TE_LATIN1); //mimetype - if (memcmp(adjunct_payload->filenameArg, "FILENAMESTAMP", 13) == 0) { - const char* derived_filename = NULL; -#if defined (_WIN32) - derived_filename = strrchr(frame_payload, '\\'); -#if defined (__CYGWIN__) - const char* derived_filename2 = strrchr(frame_payload, '/'); - if (derived_filename2 > derived_filename) { - derived_filename = derived_filename2; - } +uint32_t APar_TextFieldDataPut(ID3v2Fields *thisField, + const char *this_payload, + uint8_t str_encoding, + bool multistringtext = false) { + uint32_t bytes_used = 0; + + if (multistringtext == false) { + thisField->field_length = 0; + } + + if (str_encoding == TE_UTF8) { + bytes_used = strlen( + this_payload); // no NULL termination is provided until render time + if (bytes_used + thisField->field_length > thisField->alloc_length) { + APar_realloc_memcpy(thisField, (bytes_used > 2000 ? bytes_used : 2000)); + } + memcpy(thisField->field_string + thisField->field_length, + this_payload, + bytes_used); + thisField->field_length += bytes_used; + + } else if (str_encoding == TE_LATIN1) { + int string_length = strlen(this_payload); + if (string_length + thisField->field_length > thisField->alloc_length) { + APar_realloc_memcpy(thisField, + (string_length > 2000 ? string_length : 2000)); + } + int converted_bytes = UTF8Toisolat1( + (unsigned char *)thisField->field_string + thisField->field_length, + (int)thisField->alloc_length, + (unsigned char *)this_payload, + string_length); + if (converted_bytes > 0) { + thisField->field_length += converted_bytes; + bytes_used = converted_bytes; + // fprintf(stdout, "string %s, %" PRIu32 "=%" PRIu32 "\n", + // thisField->field_string, thisField->field_length, bytes_used); + } + + } else if (str_encoding == TE_UTF16BE_NO_BOM) { + int string_length = + (int)utf8_length(this_payload, strlen(this_payload)) + 1; + if (2 * string_length + thisField->field_length > thisField->alloc_length) { + APar_realloc_memcpy(thisField, + (2 * string_length + thisField->field_length > 2000 + ? 2 * string_length + thisField->field_length + : 2000)); + } + int converted_bytes = UTF8ToUTF16BE( + (unsigned char *)thisField->field_string + thisField->field_length, + (int)thisField->alloc_length, + (unsigned char *)this_payload, + string_length); + if (converted_bytes > 0) { + thisField->field_length += converted_bytes; + bytes_used = converted_bytes; + } + + } else if (str_encoding == TE_UTF16LE_WITH_BOM) { + int string_length = + (int)utf8_length(this_payload, strlen(this_payload)) + 1; + uint64_t bom_offset = 0; + + if (2 * string_length + thisField->field_length > + thisField->alloc_length) { // important: realloc before BOM testing!!! + APar_realloc_memcpy(thisField, + (2 * string_length + thisField->field_length > 2000 + ? 2 * string_length + thisField->field_length + : 2000)); + } + if (thisField->field_length == 0 && multistringtext == false) { + memcpy(thisField->field_string, "\xFF\xFE", 2); + } + + uint8_t field_encoding = TextField_TestBOM(thisField->field_string); + if (field_encoding > 0) { + bom_offset = 2; + } + int converted_bytes = + UTF8ToUTF16LE((unsigned char *)thisField->field_string + + thisField->field_length + bom_offset, + (int)thisField->alloc_length, + (unsigned char *)this_payload, + string_length); + if (converted_bytes > 0) { + thisField->field_length += converted_bytes + bom_offset; + bytes_used = converted_bytes; + } + } + + if (multistringtext != false) { + if (str_encoding == TE_UTF16LE_WITH_BOM || + str_encoding == TE_UTF16BE_NO_BOM) { + bytes_used += 2; + } else { + bytes_used += 1; + } + } + return bytes_used; +} + +uint32_t APar_BinaryFieldPut(ID3v2Fields *thisField, + uint32_t a_number, + const char *this_payload, + uint32_t payload_len) { + if (thisField->ID3v2_Field_Type == ID3_TEXT_ENCODING_FIELD || + thisField->ID3v2_Field_Type == ID3_PIC_TYPE_FIELD || + thisField->ID3v2_Field_Type == ID3_GROUPSYMBOL_FIELD) { + thisField->field_string[0] = (unsigned char)a_number; + thisField->field_length = 1; + // fprintf(stdout, "My (TE/PT) content is 0x%02X\n", + // thisField->field_string[0]); + return 1; + + } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD && + payload_len == 0) { // contents of a file + uint64_t file_length = findFileSize(this_payload); + thisField->field_string = + (char *)calloc(1, sizeof(char *) * file_length + 16); + + FILE *binfile = APar_OpenFile(this_payload, "rb"); + APar_ReadFile(thisField->field_string, binfile, file_length); + fclose(binfile); + + thisField->field_length = file_length; + thisField->alloc_length = file_length + 16; + thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD; + return file_length; + + } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD || + thisField->ID3v2_Field_Type == ID3_COUNTER_FIELD) { + thisField->field_string = + (char *)calloc(1, sizeof(char *) * payload_len + 16); + memcpy(thisField->field_string, this_payload, payload_len); + + thisField->field_length = payload_len; + thisField->alloc_length = payload_len + 16; + thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD; + return payload_len; + } + return 0; +} + +void APar_FrameDataPut(ID3v2Frame *thisFrame, + const char *frame_payload, + AdjunctArgs *adjunct_payload, + uint8_t str_encoding) { + if (adjunct_payload->multistringtext == false && + !APar_EvalFrame_for_Field(thisFrame->ID3v2_FrameType, ID3_COUNTER_FIELD)) + thisFrame->ID3v2_Frame_Length = 0; + switch (thisFrame->ID3v2_FrameType) { + case ID3_TEXT_FRAME: { + if (adjunct_payload->multistringtext && thisFrame->textfield_tally >= 1) { + ID3v2Fields *last_textfield = APar_FindLastTextField(thisFrame); + if (APar_ExtraTextFieldInit( + last_textfield, + strlen(frame_payload), + (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0])) { + thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut( + last_textfield->next_field, + frame_payload, + (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0], + true); + } + } else { + thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut( + thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1, + frame_payload, + str_encoding, + false); // text field + GlobalID3Tag->ID3v2_FrameCount++; + } + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + // GlobalID3Tag->ID3v2_FrameCount++; //don't do this for all text frames + // because the multiple text field support of id3v2.4; only when the frame + // is initially set + thisFrame->textfield_tally++; + break; + } + case ID3_TEXT_FRAME_USERDEF: { + thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut( + thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1, + adjunct_payload->descripArg, + str_encoding); // language + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2, + frame_payload, + str_encoding); // text field + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + case ID3_DESCRIBED_TEXT_FRAME: { + thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut( + thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1, + adjunct_payload->targetLang, + 0); // language + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2, + adjunct_payload->descripArg, + str_encoding); // description + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3, + frame_payload, + str_encoding, + adjunct_payload->multistringtext); // text field + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + case ID3_URL_FRAME: { + thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut( + thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // url field + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + case ID3_URL_FRAME_USERDEF: { + thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut( + thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1, + adjunct_payload->descripArg, + str_encoding); // language + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2, + frame_payload, + TE_LATIN1); // url field + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + case ID3_UNIQUE_FILE_ID_FRAME: { + thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut( + thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // owner field + + if (memcmp(adjunct_payload->dataArg, "randomUUIDstamp", 16) == 0) { + char uuid_binary_str[25]; + memset(uuid_binary_str, 0, 25); + APar_generate_random_uuid(uuid_binary_str); + (thisFrame->ID3v2_Frame_Fields + 1)->field_string = + (char *)calloc(1, sizeof(char *) * 40); + APar_sprintf_uuid((ap_uuid_t *)uuid_binary_str, + (thisFrame->ID3v2_Frame_Fields + 1)->field_string); + + (thisFrame->ID3v2_Frame_Fields + 1)->field_length = 36; + (thisFrame->ID3v2_Frame_Fields + 1)->alloc_length = 40; + (thisFrame->ID3v2_Frame_Fields + 1)->ID3v2_Field_Type = + ID3_BINARY_DATA_FIELD; + thisFrame->ID3v2_Frame_Length += 36; + } else { + uint8_t uniqueIDlen = strlen(adjunct_payload->dataArg); + thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut( + thisFrame->ID3v2_Frame_Fields, + 0, + adjunct_payload->dataArg, + (uniqueIDlen > 64 ? 64 : uniqueIDlen)); // unique file ID + } + + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + case ID3_CD_ID_FRAME: { + thisFrame->ID3v2_Frame_Fields->field_length = GenerateMCDIfromCD( + frame_payload, thisFrame->ID3v2_Frame_Fields->field_string); + thisFrame->ID3v2_Frame_Length = thisFrame->ID3v2_Frame_Fields->field_length; + + if (thisFrame->ID3v2_Frame_Length < 12) { + free(thisFrame->ID3v2_Frame_Fields->field_string); + thisFrame->ID3v2_Frame_Fields->field_string = NULL; + thisFrame->ID3v2_Frame_Fields->alloc_length = 0; + thisFrame->ID3v2_Frame_Length = 0; + } else { + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + } + break; + } + case ID3_ATTACHED_PICTURE_FRAME: { + thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut( + thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1, + adjunct_payload->mimeArg, + TE_LATIN1); // mimetype + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 2, + adjunct_payload->pictype_uint8, + NULL, + 1); // picturetype + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3, + adjunct_payload->descripArg, + str_encoding); // description + //(thisFrame->ID3v2_Frame_Fields+4)->ID3v2_Field_Type = + // ID3_BINARY_DATA_FIELD; //because it wasn't malloced, this needs to be set + // now + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 4, + 0, + frame_payload, + 0); // binary file (path) + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + case ID3_ATTACHED_OBJECT_FRAME: { + thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut( + thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1, + adjunct_payload->mimeArg, + TE_LATIN1); // mimetype + if (memcmp(adjunct_payload->filenameArg, "FILENAMESTAMP", 13) == 0) { + const char *derived_filename = NULL; +#if defined(_WIN32) + derived_filename = strrchr(frame_payload, '\\'); +#if defined(__CYGWIN__) + const char *derived_filename2 = strrchr(frame_payload, '/'); + if (derived_filename2 > derived_filename) { + derived_filename = derived_filename2; + } #endif #else - derived_filename = strrchr(frame_payload, '/'); + derived_filename = strrchr(frame_payload, '/'); #endif - if (derived_filename == NULL) { - derived_filename = frame_payload; - } else { - derived_filename++; //get rid of the preceding slash - } - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, derived_filename, str_encoding); //filename - } else { - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, adjunct_payload->filenameArg, str_encoding); //filename - } - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+3, adjunct_payload->descripArg, str_encoding); //description - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+4, 0, frame_payload, 0); //binary file (path) - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - case ID3_GROUP_ID_FRAME : { - uint32_t groupdatalen = strlen(adjunct_payload->dataArg); - if (adjunct_payload->groupSymbol > 0) { - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //owner field - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->groupSymbol, NULL, 1); //group symbol - if (groupdatalen > 0) { //not quite binary (unless it were entered as hex & converted), but it will do - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+2, 0, adjunct_payload->dataArg, groupdatalen); //group symbol - } - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - } - break; - } - case ID3_SIGNATURE_FRAME : { - if (adjunct_payload->groupSymbol > 0) { - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, adjunct_payload->groupSymbol, NULL, 1); //group symbol - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+1, 0, frame_payload, strlen(frame_payload)); //signature - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - } - break; - } - case ID3_PRIVATE_FRAME : { - uint32_t datalen = strlen(adjunct_payload->dataArg); //kinda precludes a true "binary" sense, but whatever... - if (datalen > 0) { - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //owner field - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+1, 0, adjunct_payload->dataArg, datalen); //data - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - } - break; - } - case ID3_PLAYCOUNTER_FRAME : { - uint64_t playcount = 0; - char play_count_syncsafe[16]; - - memset(play_count_syncsafe, 0, sizeof(play_count_syncsafe)); - - if (strcmp(frame_payload, "+1") == 0) { - if (thisFrame->ID3v2_Frame_Length == 4) { - playcount = (uint64_t)syncsafe32_to_UInt32(thisFrame->ID3v2_Frame_Fields->field_string) + 1; - } else if (thisFrame->ID3v2_Frame_Length > 4) { - playcount = syncsafeXX_to_UInt64(thisFrame->ID3v2_Frame_Fields->field_string, thisFrame->ID3v2_Frame_Fields->field_length) +1; - } else { - playcount = 1; - } - } else { - sscanf(frame_payload, "%" SCNu64, &playcount); - } - - if (playcount < 268435455) { - convert_to_syncsafe32(playcount, play_count_syncsafe); - thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, play_count_syncsafe, 4); - } else { - uint8_t conversion_len = convert_to_syncsafeXX(playcount, play_count_syncsafe); - thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, play_count_syncsafe, conversion_len); - } - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - case ID3_POPULAR_FRAME : { - unsigned char popm_rating = 0; - uint64_t popm_playcount = 0; - char popm_play_count_syncsafe[16]; - - memset(popm_play_count_syncsafe, 0, - sizeof(popm_play_count_syncsafe)); - - if (adjunct_payload->ratingArg != NULL) { - popm_rating = strtoul(adjunct_payload->ratingArg, NULL, 10); - } - thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //owner field - thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+1, 0, (char*)&popm_rating, 1); //rating - - if (adjunct_payload->dataArg != NULL) { - if (strlen(adjunct_payload->dataArg) > 0) { - if (memcmp(adjunct_payload->dataArg, "+1", 3) == 0) { - if ((thisFrame->ID3v2_Frame_Fields+2)->field_length == 4) { - popm_playcount = (uint64_t)syncsafe32_to_UInt32((thisFrame->ID3v2_Frame_Fields+2)->field_string) + 1; - } else if ((thisFrame->ID3v2_Frame_Fields+2)->field_length > 4) { - popm_playcount = syncsafeXX_to_UInt64((thisFrame->ID3v2_Frame_Fields+2)->field_string, (thisFrame->ID3v2_Frame_Fields+2)->field_length) +1; - } else { - popm_playcount = 1; - } - } else { - sscanf(adjunct_payload->dataArg, "%" SCNu64, &popm_playcount); - } - } - } - if (popm_playcount > 0) { - if (popm_playcount < 268435455) { - convert_to_syncsafe32(popm_playcount, popm_play_count_syncsafe); - thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, popm_play_count_syncsafe, 4); //syncsafe32 counter - } else { - uint8_t conversion_len = convert_to_syncsafeXX(popm_playcount, popm_play_count_syncsafe); - thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, popm_play_count_syncsafe, conversion_len); //BIGsyncsafe counter - } - } - modified_atoms = true; - GlobalID3Tag->modified_tag = true; - GlobalID3Tag->ID3v2_FrameCount++; - break; - } - } //end switch - return; -} - -void APar_EmbeddedFileTests(const char* filepath, int frameType, AdjunctArgs* adjunct_payloads) { - if (frameType == ID3_ATTACHED_PICTURE_FRAME) { - - //get cli imagetype - uint8_t total_image_types = (uint8_t)(sizeof(ImageTypeList)/sizeof(*ImageTypeList)); - uint8_t img_typlen = strlen(adjunct_payloads->pictypeArg) + 1; - const char* img_comparison_str = NULL; - - for (uint8_t itest = 0; itest < total_image_types; itest++) { - if (img_typlen == 5) { - img_comparison_str = ImageTypeList[itest].hexstring; - } else { - img_comparison_str = ImageTypeList[itest].imagetype_str; - } - if (strcmp(adjunct_payloads->pictypeArg, img_comparison_str) == 0) { - adjunct_payloads->pictype_uint8 = ImageTypeList[itest].hexcode; - } - } - - if (strlen(filepath) > 0) { - //see if file even exists - TestFileExistence(filepath, true); - - char* image_headerbytes = (char*)calloc(1, (sizeof(char)*25)); - FILE* imagefile = APar_OpenFile(filepath, "rb"); - APar_ReadFile(image_headerbytes, imagefile, 24); - fclose(imagefile); - //test mimetype - if (strlen(adjunct_payloads->mimeArg) == 0 || memcmp(adjunct_payloads->mimeArg, "-->", 3) == 0) { - uint8_t total_image_tests = (uint8_t)(sizeof(ImageList)/sizeof(*ImageList)); - for (uint8_t itest = 0; itest < total_image_tests; itest++) { - if (ImageList[itest].image_testbytes == 0) { - adjunct_payloads->mimeArg = ImageList[itest].image_mimetype; - break; - } else if (memcmp(image_headerbytes, ImageList[itest].image_binaryheader, ImageList[itest].image_testbytes) == 0) { - adjunct_payloads->mimeArg = ImageList[itest].image_mimetype; - if (adjunct_payloads->pictype_uint8 == 0x01) { - if (memcmp(image_headerbytes+16, "\x00\x00\x00\x20\x00\x00\x00\x20", 8) != 0 && itest != 2) { - adjunct_payloads->pictype_uint8 = 0x02; - } - } - break; - } - } - } - free(image_headerbytes); - image_headerbytes = NULL; - } - - } else if (frameType == ID3_ATTACHED_OBJECT_FRAME) { - if (strlen(filepath) > 0) { - TestFileExistence(filepath, true); - FILE* embedfile = APar_OpenFile(filepath, "rb"); - fclose(embedfile); - } - } - return; -} - -char* APar_ConvertField_to_UTF8(ID3v2Frame* targetframe, int fieldtype) { - char* utf8str = NULL; - uint8_t targetfield = 0xFF; - uint8_t textencoding = 0; - - for (uint8_t frm_field = 0; frm_field < targetframe->ID3v2_FieldCount; frm_field++) { - if ( (targetframe->ID3v2_Frame_Fields+frm_field)->ID3v2_Field_Type == fieldtype) { - targetfield = frm_field; - break; - } - } - - if (targetfield != 0xFF) { - if (targetframe->ID3v2_Frame_Fields->ID3v2_Field_Type == ID3_TEXT_ENCODING_FIELD) { - textencoding = targetframe->ID3v2_Frame_Fields->field_string[0]; - } - - if (textencoding == TE_LATIN1) { - utf8str = (char*)calloc(1, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *2) +16); - isolat1ToUTF8((unsigned char*)utf8str, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *2) +16, - (unsigned char*)((targetframe->ID3v2_Frame_Fields+targetfield)->field_string), (targetframe->ID3v2_Frame_Fields+targetfield)->field_length); - - } else if (textencoding == TE_UTF8) { //just so things can be free()'d with testing; a small price to pay - utf8str = (char*)calloc(1, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length) +16); - memcpy(utf8str, (targetframe->ID3v2_Frame_Fields+targetfield)->field_string, (targetframe->ID3v2_Frame_Fields+targetfield)->field_length); - - } else if (textencoding == TE_UTF16BE_NO_BOM) { - utf8str = (char*)calloc(1, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16); - UTF16BEToUTF8((unsigned char*)utf8str, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16, - (unsigned char*)((targetframe->ID3v2_Frame_Fields+targetfield)->field_string), (targetframe->ID3v2_Frame_Fields+targetfield)->field_length); - - } else if (textencoding == TE_UTF16LE_WITH_BOM) { - utf8str = (char*)calloc(1, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16); - if (memcmp( (targetframe->ID3v2_Frame_Fields+targetfield)->field_string, "\xFF\xFE", 2) == 0) { - UTF16LEToUTF8((unsigned char*)utf8str, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16, - (unsigned char*)((targetframe->ID3v2_Frame_Fields+targetfield)->field_string+2), (targetframe->ID3v2_Frame_Fields+targetfield)->field_length-2); - } else { - UTF16BEToUTF8((unsigned char*)utf8str, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16, - (unsigned char*)((targetframe->ID3v2_Frame_Fields+targetfield)->field_string+2), (targetframe->ID3v2_Frame_Fields+targetfield)->field_length-2); - } - } - } - - return utf8str; + if (derived_filename == NULL) { + derived_filename = frame_payload; + } else { + derived_filename++; // get rid of the preceding slash + } + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2, + derived_filename, + str_encoding); // filename + } else { + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2, + adjunct_payload->filenameArg, + str_encoding); // filename + } + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3, + adjunct_payload->descripArg, + str_encoding); // description + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 4, + 0, + frame_payload, + 0); // binary file (path) + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + case ID3_GROUP_ID_FRAME: { + uint32_t groupdatalen = strlen(adjunct_payload->dataArg); + if (adjunct_payload->groupSymbol > 0) { + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, + frame_payload, + TE_LATIN1); // owner field + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1, + adjunct_payload->groupSymbol, + NULL, + 1); // group symbol + if (groupdatalen > 0) { // not quite binary (unless it were entered as hex + // & converted), but it will do + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 2, + 0, + adjunct_payload->dataArg, + groupdatalen); // group symbol + } + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + } + break; + } + case ID3_SIGNATURE_FRAME: { + if (adjunct_payload->groupSymbol > 0) { + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, + adjunct_payload->groupSymbol, + NULL, + 1); // group symbol + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1, + 0, + frame_payload, + strlen(frame_payload)); // signature + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + } + break; + } + case ID3_PRIVATE_FRAME: { + uint32_t datalen = + strlen(adjunct_payload->dataArg); // kinda precludes a true "binary" + // sense, but whatever... + if (datalen > 0) { + thisFrame->ID3v2_Frame_Length += + APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, + frame_payload, + TE_LATIN1); // owner field + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1, + 0, + adjunct_payload->dataArg, + datalen); // data + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + } + break; + } + case ID3_PLAYCOUNTER_FRAME: { + uint64_t playcount = 0; + char play_count_syncsafe[16]; + + memset(play_count_syncsafe, 0, sizeof(play_count_syncsafe)); + + if (strcmp(frame_payload, "+1") == 0) { + if (thisFrame->ID3v2_Frame_Length == 4) { + playcount = (uint64_t)syncsafe32_to_UInt32( + thisFrame->ID3v2_Frame_Fields->field_string) + + 1; + } else if (thisFrame->ID3v2_Frame_Length > 4) { + playcount = + syncsafeXX_to_UInt64(thisFrame->ID3v2_Frame_Fields->field_string, + thisFrame->ID3v2_Frame_Fields->field_length) + + 1; + } else { + playcount = 1; + } + } else { + sscanf(frame_payload, "%" SCNu64, &playcount); + } + + if (playcount < 268435455) { + convert_to_syncsafe32(playcount, play_count_syncsafe); + thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut( + thisFrame->ID3v2_Frame_Fields, 0, play_count_syncsafe, 4); + } else { + uint8_t conversion_len = + convert_to_syncsafeXX(playcount, play_count_syncsafe); + thisFrame->ID3v2_Frame_Length = + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, + 0, + play_count_syncsafe, + conversion_len); + } + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + case ID3_POPULAR_FRAME: { + unsigned char popm_rating = 0; + uint64_t popm_playcount = 0; + char popm_play_count_syncsafe[16]; + + memset(popm_play_count_syncsafe, 0, sizeof(popm_play_count_syncsafe)); + + if (adjunct_payload->ratingArg != NULL) { + popm_rating = strtoul(adjunct_payload->ratingArg, NULL, 10); + } + thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut( + thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // owner field + thisFrame->ID3v2_Frame_Length += + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1, + 0, + (char *)&popm_rating, + 1); // rating + + if (adjunct_payload->dataArg != NULL) { + if (strlen(adjunct_payload->dataArg) > 0) { + if (memcmp(adjunct_payload->dataArg, "+1", 3) == 0) { + if ((thisFrame->ID3v2_Frame_Fields + 2)->field_length == 4) { + popm_playcount = + (uint64_t)syncsafe32_to_UInt32( + (thisFrame->ID3v2_Frame_Fields + 2)->field_string) + + 1; + } else if ((thisFrame->ID3v2_Frame_Fields + 2)->field_length > 4) { + popm_playcount = + syncsafeXX_to_UInt64( + (thisFrame->ID3v2_Frame_Fields + 2)->field_string, + (thisFrame->ID3v2_Frame_Fields + 2)->field_length) + + 1; + } else { + popm_playcount = 1; + } + } else { + sscanf(adjunct_payload->dataArg, "%" SCNu64, &popm_playcount); + } + } + } + if (popm_playcount > 0) { + if (popm_playcount < 268435455) { + convert_to_syncsafe32(popm_playcount, popm_play_count_syncsafe); + thisFrame->ID3v2_Frame_Length = + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, + 0, + popm_play_count_syncsafe, + 4); // syncsafe32 counter + } else { + uint8_t conversion_len = + convert_to_syncsafeXX(popm_playcount, popm_play_count_syncsafe); + thisFrame->ID3v2_Frame_Length = + APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, + 0, + popm_play_count_syncsafe, + conversion_len); // BIGsyncsafe counter + } + } + modified_atoms = true; + GlobalID3Tag->modified_tag = true; + GlobalID3Tag->ID3v2_FrameCount++; + break; + } + } // end switch + return; +} + +void APar_EmbeddedFileTests(const char *filepath, + int frameType, + AdjunctArgs *adjunct_payloads) { + if (frameType == ID3_ATTACHED_PICTURE_FRAME) { + + // get cli imagetype + uint8_t total_image_types = + (uint8_t)(sizeof(ImageTypeList) / sizeof(*ImageTypeList)); + uint8_t img_typlen = strlen(adjunct_payloads->pictypeArg) + 1; + const char *img_comparison_str = NULL; + + for (uint8_t itest = 0; itest < total_image_types; itest++) { + if (img_typlen == 5) { + img_comparison_str = ImageTypeList[itest].hexstring; + } else { + img_comparison_str = ImageTypeList[itest].imagetype_str; + } + if (strcmp(adjunct_payloads->pictypeArg, img_comparison_str) == 0) { + adjunct_payloads->pictype_uint8 = ImageTypeList[itest].hexcode; + } + } + + if (strlen(filepath) > 0) { + // see if file even exists + TestFileExistence(filepath, true); + + char *image_headerbytes = (char *)calloc(1, (sizeof(char) * 25)); + FILE *imagefile = APar_OpenFile(filepath, "rb"); + APar_ReadFile(image_headerbytes, imagefile, 24); + fclose(imagefile); + // test mimetype + if (strlen(adjunct_payloads->mimeArg) == 0 || + memcmp(adjunct_payloads->mimeArg, "-->", 3) == 0) { + uint8_t total_image_tests = + (uint8_t)(sizeof(ImageList) / sizeof(*ImageList)); + for (uint8_t itest = 0; itest < total_image_tests; itest++) { + if (ImageList[itest].image_testbytes == 0) { + adjunct_payloads->mimeArg = ImageList[itest].image_mimetype; + break; + } else if (memcmp(image_headerbytes, + ImageList[itest].image_binaryheader, + ImageList[itest].image_testbytes) == 0) { + adjunct_payloads->mimeArg = ImageList[itest].image_mimetype; + if (adjunct_payloads->pictype_uint8 == 0x01) { + if (memcmp(image_headerbytes + 16, + "\x00\x00\x00\x20\x00\x00\x00\x20", + 8) != 0 && + itest != 2) { + adjunct_payloads->pictype_uint8 = 0x02; + } + } + break; + } + } + } + free(image_headerbytes); + image_headerbytes = NULL; + } + + } else if (frameType == ID3_ATTACHED_OBJECT_FRAME) { + if (strlen(filepath) > 0) { + TestFileExistence(filepath, true); + FILE *embedfile = APar_OpenFile(filepath, "rb"); + fclose(embedfile); + } + } + return; +} + +char *APar_ConvertField_to_UTF8(ID3v2Frame *targetframe, int fieldtype) { + char *utf8str = NULL; + uint8_t targetfield = 0xFF; + uint8_t textencoding = 0; + + for (uint8_t frm_field = 0; frm_field < targetframe->ID3v2_FieldCount; + frm_field++) { + if ((targetframe->ID3v2_Frame_Fields + frm_field)->ID3v2_Field_Type == + fieldtype) { + targetfield = frm_field; + break; + } + } + + if (targetfield != 0xFF) { + if (targetframe->ID3v2_Frame_Fields->ID3v2_Field_Type == + ID3_TEXT_ENCODING_FIELD) { + textencoding = targetframe->ID3v2_Frame_Fields->field_string[0]; + } + + if (textencoding == TE_LATIN1) { + utf8str = (char *)calloc( + 1, + sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_length * + 2) + + 16); + isolat1ToUTF8( + (unsigned char *)utf8str, + sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_length * + 2) + + 16, + (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_string), + (targetframe->ID3v2_Frame_Fields + targetfield)->field_length); + + } else if (textencoding == TE_UTF8) { // just so things can be free()'d with + // testing; a small price to pay + utf8str = (char *)calloc( + 1, + sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_length) + + 16); + memcpy(utf8str, + (targetframe->ID3v2_Frame_Fields + targetfield)->field_string, + (targetframe->ID3v2_Frame_Fields + targetfield)->field_length); + + } else if (textencoding == TE_UTF16BE_NO_BOM) { + utf8str = (char *)calloc( + 1, + sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_length * + 4) + + 16); + UTF16BEToUTF8( + (unsigned char *)utf8str, + sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_length * + 4) + + 16, + (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_string), + (targetframe->ID3v2_Frame_Fields + targetfield)->field_length); + + } else if (textencoding == TE_UTF16LE_WITH_BOM) { + utf8str = (char *)calloc( + 1, + sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_length * + 4) + + 16); + if (memcmp((targetframe->ID3v2_Frame_Fields + targetfield)->field_string, + "\xFF\xFE", + 2) == 0) { + UTF16LEToUTF8( + (unsigned char *)utf8str, + sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_length * + 4) + + 16, + (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_string + + 2), + (targetframe->ID3v2_Frame_Fields + targetfield)->field_length - 2); + } else { + UTF16BEToUTF8( + (unsigned char *)utf8str, + sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_length * + 4) + + 16, + (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield) + ->field_string + + 2), + (targetframe->ID3v2_Frame_Fields + targetfield)->field_length - 2); + } + } + } + + return utf8str; } /*---------------------- APar_FindFrame - id3v2tag - an already initialized ID3 tag (contained by an ID32 atom) with 0 or more frames as a linked list - frame_str - target frame string (like "TIT2") - frameID - a known frame in listed in AP_ID3v2_FrameDefinitions & enumerated in AP_ID3v2_Definitions.h - frametype - the type of frame (text, described text, picture, object...) to search for - adjunct_payloads - holds optional/required args for supplementary matching; example: described text matching on frame name & description; TODO more criteria - createframe - create the frame if not found to be existing; this initialzes the frame only - not its fields. + id3v2tag - an already initialized ID3 tag (contained by an ID32 atom) +with 0 or more frames as a linked list frame_str - target frame string (like +"TIT2") frameID - a known frame in listed in AP_ID3v2_FrameDefinitions & +enumerated in AP_ID3v2_Definitions.h frametype - the type of frame (text, +described text, picture, object...) to search for adjunct_payloads - holds +optional/required args for supplementary matching; example: described text +matching on frame name & description; TODO more criteria createframe - create +the frame if not found to be existing; this initialzes the frame only - not its +fields. - this provides 2 functions: actually searching while looping through the frames & creation of a frame at the end of the frame list. + this provides 2 functions: actually searching while looping through the +frames & creation of a frame at the end of the frame list. ----------------------*/ -ID3v2Frame* APar_FindFrame(ID3v2Tag* id3v2tag, const char* frame_str, int frameID, int frametype, AdjunctArgs* adjunct_payloads, bool createframe) { - ID3v2Frame* returnframe = NULL; - ID3v2Frame* evalframe = id3v2tag->ID3v2_FirstFrame; - uint8_t supplemental_matching = 0; - - if (createframe) { - ID3v2Frame* newframe = (ID3v2Frame*)calloc(1, sizeof(ID3v2Frame)); - newframe->ID3v2_NextFrame = NULL; - if (id3v2tag->ID3v2_FirstFrame == NULL) id3v2tag->ID3v2_FirstFrame = newframe; - if (id3v2tag->ID3v2_FrameList != NULL) id3v2tag->ID3v2_FrameList->ID3v2_NextFrame = newframe; - id3v2tag->ID3v2_FrameList = newframe; - return newframe; - } - - if (APar_EvalFrame_for_Field(frametype, ID3_DESCRIPTION_FIELD)) { - supplemental_matching = 0x01; - } - - while (evalframe != NULL) { - //if (trametype is a type containing a modifer like description or image type or symbol or such things - if (supplemental_matching != 0) { - - //match on description + frame name - if (supplemental_matching && 0x01 && evalframe->ID3v2_Frame_ID == frameID) { - char* utf8_descrip = APar_ConvertField_to_UTF8(evalframe, ID3_DESCRIPTION_FIELD); - if (utf8_descrip != NULL) { - if (strcmp(adjunct_payloads->descripArg, utf8_descrip) == 0) { - returnframe = evalframe; - free(utf8_descrip); - break; - } - free(utf8_descrip); - } - } - - } else if (evalframe->ID3v2_Frame_ID == ID3_UNKNOWN_FRAME) { - if (memcmp(frame_str, evalframe->ID3v2_Frame_Namestr, 4) == 0) { - returnframe = evalframe; - break; - } - - } else { - //fprintf(stdout, "frame is %s; eval frameID is %d ?= %d\n", frame_str, evalframe->ID3v2_Frame_ID, frameID); - if (evalframe->ID3v2_Frame_ID == frameID) { - returnframe = evalframe; - break; - } - } - evalframe = evalframe->ID3v2_NextFrame; - } - return returnframe; +ID3v2Frame *APar_FindFrame(ID3v2Tag *id3v2tag, + const char *frame_str, + int frameID, + int frametype, + AdjunctArgs *adjunct_payloads, + bool createframe) { + ID3v2Frame *returnframe = NULL; + ID3v2Frame *evalframe = id3v2tag->ID3v2_FirstFrame; + uint8_t supplemental_matching = 0; + + if (createframe) { + ID3v2Frame *newframe = (ID3v2Frame *)calloc(1, sizeof(ID3v2Frame)); + newframe->ID3v2_NextFrame = NULL; + if (id3v2tag->ID3v2_FirstFrame == NULL) + id3v2tag->ID3v2_FirstFrame = newframe; + if (id3v2tag->ID3v2_FrameList != NULL) + id3v2tag->ID3v2_FrameList->ID3v2_NextFrame = newframe; + id3v2tag->ID3v2_FrameList = newframe; + return newframe; + } + + if (APar_EvalFrame_for_Field(frametype, ID3_DESCRIPTION_FIELD)) { + supplemental_matching = 0x01; + } + + while (evalframe != NULL) { + // if (trametype is a type containing a modifer like description or image + // type or symbol or such things + if (supplemental_matching != 0) { + + // match on description + frame name + if (supplemental_matching & 0x01 && + evalframe->ID3v2_Frame_ID == frameID) { + char *utf8_descrip = + APar_ConvertField_to_UTF8(evalframe, ID3_DESCRIPTION_FIELD); + if (utf8_descrip != NULL) { + if (strcmp(adjunct_payloads->descripArg, utf8_descrip) == 0) { + returnframe = evalframe; + free(utf8_descrip); + break; + } + free(utf8_descrip); + } + } + + } else if (evalframe->ID3v2_Frame_ID == ID3_UNKNOWN_FRAME) { + if (memcmp(frame_str, evalframe->ID3v2_Frame_Namestr, 4) == 0) { + returnframe = evalframe; + break; + } + + } else { + // fprintf(stdout, "frame is %s; eval frameID is %d ?= %d\n", frame_str, + // evalframe->ID3v2_Frame_ID, frameID); + if (evalframe->ID3v2_Frame_ID == frameID) { + returnframe = evalframe; + break; + } + } + evalframe = evalframe->ID3v2_NextFrame; + } + return returnframe; } /*---------------------- APar_ID3FrameAmmend - id32_atom - the ID32 atom targeted to this language; the ID32 atom is already created, the ID3 tag is either created or containing already parsed ID3 frames - frame_str - the string for the frame (like TCON) that is desired. This string must be a known frame string in AP_ID3v2_FrameDefinitions.h - frame_payload - the major piece of metadata to be set (for APIC its the path, for MCDI its a device...), that can optionally be NULL (for removal of the frame) - adjunct_payloads - a structure holding a number of optional/required parameters for the frame (compression...) - str_encoding - the encoding to be used in the fields of the target frame when different encodings are allowed - - lookup what frame_str is supposed to look like in the KnownFrames[] array in AP_ID3v2_FrameDefinitions.h. First see if this frame exists at all - if it does & - the frame_str is NULL or blank (""), then mark this frame for elimination. if the frame is of a particular type (like TCON), run some tests on the frame_payload. - If all is well after the tests, and the frame does not exists, create it via APar_FindFrame(... true) & initialize the frame to hold data. Send the frame, payload & - adjunct payloads onto APar_FrameDataPut to actually place the data onto the frame + id32_atom - the ID32 atom targeted to this language; the ID32 atom is +already created, the ID3 tag is either created or containing already parsed ID3 +frames frame_str - the string for the frame (like TCON) that is desired. This +string must be a known frame string in AP_ID3v2_FrameDefinitions.h frame_payload +- the major piece of metadata to be set (for APIC its the path, for MCDI its a +device...), that can optionally be NULL (for removal of the frame) + adjunct_payloads - a structure holding a number of optional/required +parameters for the frame (compression...) str_encoding - the encoding to be used +in the fields of the target frame when different encodings are allowed + + lookup what frame_str is supposed to look like in the KnownFrames[] array in +AP_ID3v2_FrameDefinitions.h. First see if this frame exists at all - if it does +& the frame_str is NULL or blank (""), then mark this frame for elimination. if +the frame is of a particular type (like TCON), run some tests on the +frame_payload. If all is well after the tests, and the frame does not exists, +create it via APar_FindFrame(... true) & initialize the frame to hold data. Send +the frame, payload & adjunct payloads onto APar_FrameDataPut to actually place +the data onto the frame ----------------------*/ -void APar_ID3FrameAmmend(AtomicInfo* id32_atom, const char* frame_str, const char* frame_payload, AdjunctArgs* adjunct_payloads, uint8_t str_encoding) { - ID3v2Frame* targetFrame = NULL; - - if (id32_atom == NULL) return; - GlobalID3Tag = id32_atom->ID32_TagInfo; - //fprintf(stdout, "frame is %s; payload is %s; %s %s\n", frame_str, frame_payload, adjunct_payloads->descripArg, adjunct_payloads->targetLang); - - int frameID = MatchID3FrameIDstr(frame_str, GlobalID3Tag->ID3v2Tag_MajorVersion); - int frameType = KnownFrames[frameID+1].ID3v2_FrameType; - uint8_t frameCompositionList = GetFrameCompositionDescription(frameType); - - if (frameType == ID3_ATTACHED_PICTURE_FRAME || frameType == ID3_ATTACHED_OBJECT_FRAME) { - APar_EmbeddedFileTests(frame_payload, frameType, adjunct_payloads); - } - - targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo, frame_str, frameID, frameType, adjunct_payloads, false); - - if (frame_payload == NULL) { - if (targetFrame != NULL) { - targetFrame->eliminate_frame = true; - modified_atoms = true; - id32_atom->ID32_TagInfo->modified_tag = true; - } - return; - - } else if (strlen(frame_payload) == 0) { - if (targetFrame != NULL) { - targetFrame->eliminate_frame = true; //thats right, frames of empty text are removed - so be a doll and try to convey some info, eh? - modified_atoms = true; - id32_atom->ID32_TagInfo->modified_tag = true; - } - return; - - } else { - if (frameType == ID3_UNKNOWN_FRAME) { - APar_assert(false, 10, frame_str); - return; - } - - //check tags to be set so they conform to the id3v2 informal specification - if (frameType == ID3_TEXT_FRAME) { - if (targetFrame != NULL) { - if (!targetFrame->eliminate_frame) adjunct_payloads->multistringtext = true; //if a frame already exists and isn't marked for elimination, append a new string - } - - if (frameID == ID3v2_FRAME_COPYRIGHT || frameID == ID3v2_FRAME_PRODNOTICE) { - if ((TestCharInRange(frame_payload[0], '0', '9') + TestCharInRange(frame_payload[1], '0', '9') + TestCharInRange(frame_payload[2], '0', '9') + - TestCharInRange(frame_payload[3], '0', '9') != 4) || frame_payload[4] != ' ') { - fprintf(stderr, "AtomicParsley warning: frame %s was skipped because it did not start with a year followed by a space\n", KnownFrames[frameID].ID3V2p4_FrameID); - return; - } - - } else if (frameID == ID3v2_FRAME_PART_O_SET || frameID == ID3v2_FRAME_TRACKNUM) { - uint8_t pos_len = strlen(frame_payload); - for (uint8_t letter_idx = 0; letter_idx < pos_len; letter_idx++) { - if (frame_payload[letter_idx] == '/') continue; - if (TestCharInRange(frame_payload[letter_idx], '0', '9') != 1) { - if (frameID-1 == ID3v2_FRAME_PART_O_SET) { - fprintf(stderr, "AtomicParsley warning: frame %s was skipped because it had an extraneous character: %c\n", KnownFrames[frameID].ID3V2p4_FrameID, frame_payload[letter_idx]); - return; - } else { //okay this is to support the beloved vinyl - if (!(TestCharInRange(frame_payload[letter_idx], 'A', 'F') && TestCharInRange(frame_payload[letter_idx+1], '0', '9'))) { - fprintf(stderr, "AtomicParsley warning: frame %s was skipped because it had an extraneous character: %c\n", KnownFrames[frameID].ID3V2p4_FrameID, frame_payload[letter_idx]); - return; - } - } - } - } - } else if (frameID == ID3v2_FRAME_ISRC) { - uint8_t isrc_len = strlen(frame_payload); - if (isrc_len != 12) { - fprintf(stderr, "AtomicParsley warning: setting ISRC frame was skipped because it was not 12 characters long\n"); - return; - } - for (uint8_t isrc_ltr_idx = 0; isrc_ltr_idx < isrc_len; isrc_ltr_idx++) { - if (TestCharInRange(frame_payload[isrc_ltr_idx], '0', '9') + TestCharInRange(frame_payload[isrc_ltr_idx], 'A', 'Z') == 0) { - fprintf(stderr, "AtomicParsley warning: ISRC can only consist of A-Z & 0-9; letter %u was %c; skipping\n", isrc_ltr_idx+1, frame_payload[isrc_ltr_idx]); - return; - } - } - } - } - - - if (targetFrame == NULL) { - targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo, frame_str, frameID, frameType, adjunct_payloads, true); - if (targetFrame == NULL) { - fprintf(stdout, "NULL frame\n"); - exit(0); - } else { - APar_FrameInit(targetFrame, frame_str, frameID, frameCompositionList, frame_payload); - } - } - } - - if (targetFrame != NULL) { - if (adjunct_payloads->zlibCompressed) { - targetFrame->ID3v2_Frame_Flags |= (ID32_FRAMEFLAG_COMPRESSED + ID32_FRAMEFLAG_LENINDICATED); - } - - if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_LANGUAGE) { - APar_FrameDataPut(targetFrame, adjunct_payloads->targetLang, adjunct_payloads, str_encoding); - - } else if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_CONTENTTYPE) { - uint8_t genre_idx = ID3StringGenreToInt(frame_payload); - if (genre_idx != 0xFF) { - char genre_str_idx[2]; - genre_str_idx[0] = 0; genre_str_idx[1] = 0; genre_str_idx[1] = 0; - sprintf(genre_str_idx, "%u", genre_idx); - APar_FrameDataPut(targetFrame, genre_str_idx, adjunct_payloads, str_encoding); - } else { - APar_FrameDataPut(targetFrame, frame_payload, adjunct_payloads, str_encoding); - } - - } else { - APar_FrameDataPut(targetFrame, frame_payload, adjunct_payloads, str_encoding); - } - - if (adjunct_payloads->zlibCompressed) { - targetFrame->ID3v2_Frame_ExpandedLength = targetFrame->ID3v2_Frame_Length; - } - targetFrame->ID3v2_Frame_GroupingSymbol = adjunct_payloads->groupSymbol; - } - return; +void APar_ID3FrameAmmend(AtomicInfo *id32_atom, + const char *frame_str, + const char *frame_payload, + AdjunctArgs *adjunct_payloads, + uint8_t str_encoding) { + ID3v2Frame *targetFrame = NULL; + + if (id32_atom == NULL) + return; + GlobalID3Tag = id32_atom->ID32_TagInfo; + // fprintf(stdout, "frame is %s; payload is %s; %s %s\n", frame_str, + // frame_payload, adjunct_payloads->descripArg, adjunct_payloads->targetLang); + + int frameID = + MatchID3FrameIDstr(frame_str, GlobalID3Tag->ID3v2Tag_MajorVersion); + int frameType = KnownFrames[frameID + 1].ID3v2_FrameType; + uint8_t frameCompositionList = GetFrameCompositionDescription(frameType); + + if (frameType == ID3_ATTACHED_PICTURE_FRAME || + frameType == ID3_ATTACHED_OBJECT_FRAME) { + APar_EmbeddedFileTests(frame_payload, frameType, adjunct_payloads); + } + + targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo, + frame_str, + frameID, + frameType, + adjunct_payloads, + false); + + if (frame_payload == NULL) { + if (targetFrame != NULL) { + targetFrame->eliminate_frame = true; + modified_atoms = true; + id32_atom->ID32_TagInfo->modified_tag = true; + } + return; + + } else if (strlen(frame_payload) == 0) { + if (targetFrame != NULL) { + targetFrame->eliminate_frame = + true; // thats right, frames of empty text are removed - so be a doll + // and try to convey some info, eh? + modified_atoms = true; + id32_atom->ID32_TagInfo->modified_tag = true; + } + return; + + } else { + if (frameType == ID3_UNKNOWN_FRAME) { + APar_assert(false, 10, frame_str); + return; + } + + // check tags to be set so they conform to the id3v2 informal specification + if (frameType == ID3_TEXT_FRAME) { + if (targetFrame != NULL) { + if (!targetFrame->eliminate_frame) + adjunct_payloads->multistringtext = + true; // if a frame already exists and isn't marked for + // elimination, append a new string + } + + if (frameID == ID3v2_FRAME_COPYRIGHT || + frameID == ID3v2_FRAME_PRODNOTICE) { + if ((TestCharInRange(frame_payload[0], '0', '9') + + TestCharInRange(frame_payload[1], '0', '9') + + TestCharInRange(frame_payload[2], '0', '9') + + TestCharInRange(frame_payload[3], '0', '9') != + 4) || + frame_payload[4] != ' ') { + fprintf(stderr, + "AtomicParsley warning: frame %s was skipped because it did " + "not start with a year followed by a space\n", + KnownFrames[frameID].ID3V2p4_FrameID); + return; + } + + } else if (frameID == ID3v2_FRAME_PART_O_SET || + frameID == ID3v2_FRAME_TRACKNUM) { + uint8_t pos_len = strlen(frame_payload); + for (uint8_t letter_idx = 0; letter_idx < pos_len; letter_idx++) { + if (frame_payload[letter_idx] == '/') + continue; + if (TestCharInRange(frame_payload[letter_idx], '0', '9') != 1) { + if (frameID - 1 == ID3v2_FRAME_PART_O_SET) { + fprintf(stderr, + "AtomicParsley warning: frame %s was skipped because it " + "had an extraneous character: %c\n", + KnownFrames[frameID].ID3V2p4_FrameID, + frame_payload[letter_idx]); + return; + } else { // okay this is to support the beloved vinyl + if (!(TestCharInRange(frame_payload[letter_idx], 'A', 'F') && + TestCharInRange(frame_payload[letter_idx + 1], '0', '9'))) { + fprintf(stderr, + "AtomicParsley warning: frame %s was skipped because " + "it had an extraneous character: %c\n", + KnownFrames[frameID].ID3V2p4_FrameID, + frame_payload[letter_idx]); + return; + } + } + } + } + } else if (frameID == ID3v2_FRAME_ISRC) { + uint8_t isrc_len = strlen(frame_payload); + if (isrc_len != 12) { + fprintf(stderr, + "AtomicParsley warning: setting ISRC frame was " + "skipped because it was not 12 characters long\n"); + return; + } + for (uint8_t isrc_ltr_idx = 0; isrc_ltr_idx < isrc_len; + isrc_ltr_idx++) { + if (TestCharInRange(frame_payload[isrc_ltr_idx], '0', '9') + + TestCharInRange(frame_payload[isrc_ltr_idx], 'A', 'Z') == + 0) { + fprintf(stderr, + "AtomicParsley warning: ISRC can only consist of A-Z & " + "0-9; letter %u was %c; skipping\n", + isrc_ltr_idx + 1, + frame_payload[isrc_ltr_idx]); + return; + } + } + } + } + + if (targetFrame == NULL) { + targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo, + frame_str, + frameID, + frameType, + adjunct_payloads, + true); + if (targetFrame == NULL) { + fprintf(stdout, "NULL frame\n"); + exit(0); + } else { + APar_FrameInit(targetFrame, + frame_str, + frameID, + frameCompositionList, + frame_payload); + } + } + } + + if (targetFrame != NULL) { + if (adjunct_payloads->zlibCompressed) { + targetFrame->ID3v2_Frame_Flags |= + (ID32_FRAMEFLAG_COMPRESSED + ID32_FRAMEFLAG_LENINDICATED); + } + + if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_LANGUAGE) { + APar_FrameDataPut(targetFrame, + adjunct_payloads->targetLang, + adjunct_payloads, + str_encoding); + + } else if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_CONTENTTYPE) { + uint8_t genre_idx = ID3StringGenreToInt(frame_payload); + if (genre_idx != 0xFF) { + char genre_str_idx[2]; + genre_str_idx[0] = 0; + genre_str_idx[1] = 0; + genre_str_idx[1] = 0; + sprintf(genre_str_idx, "%u", genre_idx); + APar_FrameDataPut( + targetFrame, genre_str_idx, adjunct_payloads, str_encoding); + } else { + APar_FrameDataPut( + targetFrame, frame_payload, adjunct_payloads, str_encoding); + } + + } else { + APar_FrameDataPut( + targetFrame, frame_payload, adjunct_payloads, str_encoding); + } + + if (adjunct_payloads->zlibCompressed) { + targetFrame->ID3v2_Frame_ExpandedLength = targetFrame->ID3v2_Frame_Length; + } + targetFrame->ID3v2_Frame_GroupingSymbol = adjunct_payloads->groupSymbol; + } + return; } /////////////////////////////////////////////////////////////////////////////////////// -// id3 cleanup function // +// id3 cleanup function // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_FreeID32Memory - free all the little bits of allocated memory. Follow the ID3v2Frame pointers by each frame's ID3v2_NextFrame. Each frame has ID3v2_FieldCount number of field - strings (char*) that were malloced. + free all the little bits of allocated memory. Follow the ID3v2Frame pointers +by each frame's ID3v2_NextFrame. Each frame has ID3v2_FieldCount number of field + strings (char*) that were malloced. ----------------------*/ -void APar_FreeID32Memory(ID3v2Tag* id32tag) { - ID3v2Frame* aframe = id32tag->ID3v2_FirstFrame; - while (aframe != NULL) { - +void APar_FreeID32Memory(ID3v2Tag *id32tag) { + ID3v2Frame *aframe = id32tag->ID3v2_FirstFrame; + while (aframe != NULL) { + #if defined(DEBUG_V) - fprintf(stdout, "freeing frame %s of %u fields\n", aframe->ID3v2_Frame_Namestr, aframe->ID3v2_FieldCount); + fprintf(stdout, + "freeing frame %s of %u fields\n", + aframe->ID3v2_Frame_Namestr, + aframe->ID3v2_FieldCount); #endif - for(uint8_t id3fld = 0; id3fld < aframe->ID3v2_FieldCount; id3fld++) { + for (uint8_t id3fld = 0; id3fld < aframe->ID3v2_FieldCount; id3fld++) { #if defined(DEBUG_V) - fprintf(stdout, "freeing field %s ; %u of %u fields\n", (aframe->ID3v2_Frame_Fields+id3fld)->field_string, id3fld+1, aframe->ID3v2_FieldCount); + fprintf(stdout, + "freeing field %s ; %u of %u fields\n", + (aframe->ID3v2_Frame_Fields + id3fld)->field_string, + id3fld + 1, + aframe->ID3v2_FieldCount); #endif - ID3v2Fields* afield = aframe->ID3v2_Frame_Fields+id3fld; - ID3v2Fields* freefield = NULL; - while (true) { - if ( afield != NULL && afield->field_string != NULL ) { - free( afield->field_string ); - afield->field_string = NULL; - } - freefield = afield; - afield = afield->next_field; - if (afield == NULL) break; - if (aframe->ID3v2_Frame_Fields+id3fld != freefield) free(freefield); - } - } - free( aframe->ID3v2_Frame_Fields ); - aframe->ID3v2_Frame_Fields = NULL; - free(aframe); - aframe = aframe->ID3v2_NextFrame; - } - return; + ID3v2Fields *afield = aframe->ID3v2_Frame_Fields + id3fld; + ID3v2Fields *freefield = NULL; + while (true) { + if (afield != NULL && afield->field_string != NULL) { + free(afield->field_string); + afield->field_string = NULL; + } + freefield = afield; + afield = afield->next_field; + if (afield == NULL) + break; + if (aframe->ID3v2_Frame_Fields + id3fld != freefield) + free(freefield); + } + } + free(aframe->ID3v2_Frame_Fields); + aframe->ID3v2_Frame_Fields = NULL; + free(aframe); + aframe = aframe->ID3v2_NextFrame; + } + return; } diff -Nru atomicparsley-0.9.6/src/id3v2defs.h atomicparsley-20210715.151551.e7ad03a/src/id3v2defs.h --- atomicparsley-0.9.6/src/id3v2defs.h 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/id3v2defs.h 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - id3v2defs.h - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,7 +10,7 @@ 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 + 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 @@ -21,147 +21,577 @@ //==================================================================// ID3FrameDefinition KnownFrames[] = { - { "", "", "", "Unknown frame", "", ID3v2_UNKNOWN_FRAME, ID3_UNKNOWN_FRAME }, - { "TAL", "TALB", "TALB", "Album/Movie/Show title", "album", ID3v2_FRAME_ALBUM, ID3_TEXT_FRAME }, - { "TBP", "TBPM", "TBPM", "BPM (beats per minute)", "bpm", ID3v2_FRAME_BPM, ID3_TEXT_FRAME }, - { "TCM", "TCOM", "TCOM", "Composer", "composer", ID3v2_FRAME_COMPOSER, ID3_TEXT_FRAME }, - { "TCO", "TCON", "TCON", "Content Type/Genre", "genre", ID3v2_FRAME_CONTENTTYPE, ID3_TEXT_FRAME }, - { "TCP", "TCOP", "TCOP", "Copyright message", "copyright", ID3v2_FRAME_COPYRIGHT, ID3_TEXT_FRAME }, - { "", "", "TDEN", "Encoding time", "", ID3v2_FRAME_ENCODINGTIME, ID3_TEXT_FRAME }, - { "TDY", "TDLY", "TDLY", "Playlist delay", "", ID3v2_FRAME_PLAYLISTDELAY, ID3_TEXT_FRAME }, - { "", "", "TDOR", "Original release time", "", ID3v2_FRAME_ORIGRELTIME, ID3_TEXT_FRAME }, - { "", "", "TDRC", "Recording time", "date", ID3v2_FRAME_RECORDINGTIME, ID3_TEXT_FRAME }, - { "", "", "TDRL", "Release time", "released", ID3v2_FRAME_RELEASETIME, ID3_TEXT_FRAME }, - { "", "", "TDTG", "Tagging time", "tagged", ID3v2_FRAME_TAGGINGTIME, ID3_TEXT_FRAME }, - { "TEN", "TENC", "TENC", "Encoded by", "encoder", ID3v2_FRAME_ENCODER, ID3_TEXT_FRAME }, - { "TXT", "TEXT", "TEXT", "Lyricist/Text writer", "writer", ID3v2_FRAME_LYRICIST, ID3_TEXT_FRAME }, - { "TFT", "TFLT", "TFLT", "File type", "", ID3v2_FRAME_FILETYPE, ID3_TEXT_FRAME }, - { "", "", "TIPL", "Involved people list", "", ID3v2_FRAME_INVOLVEDPEOPLE, ID3_TEXT_FRAME }, - { "TT1", "TIT1", "TIT1", "Content group description", "grouping", ID3v2_FRAME_GROUP_DESC, ID3_TEXT_FRAME }, - { "TT2", "TIT2", "TIT2", "Title/songname/content description", "title", ID3v2_FRAME_TITLE, ID3_TEXT_FRAME }, - { "TT3", "TIT3", "TIT3", "Subtitle/Description refinement", "subtitle", ID3v2_FRAME_SUBTITLE, ID3_TEXT_FRAME }, - { "TKE", "TKEY", "TKEY", "Initial key", "", ID3v2_FRAME_INITIALKEY, ID3_TEXT_FRAME }, - { "TLA", "TLAN", "TLAN", "Language(s)", "", ID3v2_FRAME_LANGUAGE, ID3_TEXT_FRAME }, - { "TLE", "TLEN", "TLEN", "Length", "", ID3v2_FRAME_TIMELENGTH, ID3_TEXT_FRAME }, - { "", "", "TMCL", "Musician credits list", "credits", ID3v2_FRAME_MUSICIANLIST, ID3_TEXT_FRAME }, - { "TMT", "TMED", "TMED", "Media type", "media", ID3v2_FRAME_MEDIATYPE, ID3_TEXT_FRAME }, - { "", "", "TMOO", "Mood", "mood", ID3v2_FRAME_MOOD, ID3_TEXT_FRAME }, - { "TOT", "TOAL", "TOAL", "Original album/movie/show title", "", ID3v2_FRAME_ORIGALBUM, ID3_TEXT_FRAME }, - { "TOF", "TOFN", "TOFN", "Original filename", "", ID3v2_FRAME_ORIGFILENAME, ID3_TEXT_FRAME }, - { "TOL", "TOLY", "TOLY", "Original lyricist(s)/text writer(s)", "", ID3v2_FRAME_ORIGWRITER, ID3_TEXT_FRAME }, - { "TOA", "TOPE", "TOPE", "Original artist(s)/performer(s)", "", ID3v2_FRAME_ORIGARTIST, ID3_TEXT_FRAME }, - { "", "TOWN", "TOWN", "File owner/licensee", "", ID3v2_FRAME_FILEOWNER, ID3_TEXT_FRAME }, - { "TP1", "TPE1", "TPE1", "Artist/Lead performer(s)/Soloist(s)", "artist", ID3v2_FRAME_ARTIST, ID3_TEXT_FRAME }, - { "TP2", "TPE2", "TPE2", "Album artist/Band/orchestra/accompaniment", "album artist", ID3v2_FRAME_ALBUMARTIST, ID3_TEXT_FRAME }, - { "TP3", "TPE3", "TPE3", "Conductor/performer refinement", "conductor", ID3v2_FRAME_CONDUCTOR, ID3_TEXT_FRAME }, - { "TP4", "TPE4", "TPE4", "Interpreted or remixed by", "remixer", ID3v2_FRAME_REMIXER, ID3_TEXT_FRAME }, - { "TPA", "TPOS", "TPOS", "Part of a set", "", ID3v2_FRAME_PART_O_SET, ID3_TEXT_FRAME }, - { "", "", "TPRO", "Produced notice", "", ID3v2_FRAME_PRODNOTICE, ID3_TEXT_FRAME }, - { "TPB", "TPUB", "TPUB", "Publisher", "publisher", ID3v2_FRAME_PUBLISHER, ID3_TEXT_FRAME }, - { "TRK", "TRCK", "TRCK", "Track number/Position in set", "trk#", ID3v2_FRAME_TRACKNUM, ID3_TEXT_FRAME }, - { "", "TRSN", "TRSN", "Internet radio station name", "", ID3v2_FRAME_IRADIONAME, ID3_TEXT_FRAME }, - { "", "TRSO", "TRSO", "Internet radio station owner", "", ID3v2_FRAME_IRADIOOWNER, ID3_TEXT_FRAME }, - { "", "", "TSOA", "Album sort order", "", ID3v2_FRAME_ALBUMSORT, ID3_TEXT_FRAME }, - { "", "", "TSOP", "Performer sort order", "", ID3v2_FRAME_PERFORMERSORT, ID3_TEXT_FRAME }, - { "", "", "TSOT", "Title sort order", "", ID3v2_FRAME_TITLESORT, ID3_TEXT_FRAME }, - { "TRC", "TSRC", "TSRC", "ISRC", "", ID3v2_FRAME_ISRC, ID3_TEXT_FRAME }, - { "TSS", "TSSE", "TSSE", "Software/Hardware and settings used for encoding", "", ID3v2_FRAME_ENCODINGSETTINGS, ID3_TEXT_FRAME }, - { "", "", "TSST", "Set subtitle", "", ID3v2_FRAME_SETSUBTITLE, ID3_TEXT_FRAME }, - - { "TDA", "TDAT", "", "Date", "", ID3v2_DATE, ID3_TEXT_FRAME }, - { "TIM", "TIME", "", "TIME", "", ID3v2_TIME, ID3_TEXT_FRAME }, - { "TOR", "TORY", "", "Original Release Year", "", ID3v2_ORIGRELYEAR, ID3_TEXT_FRAME }, - { "TRD", "TRDA", "", "Recording dates", "", ID3v2_RECORDINGDATE, ID3_TEXT_FRAME }, - { "TSI", "TSIZ", "", "Size", "", ID3v2_FRAME_SIZE, ID3_TEXT_FRAME }, - { "TYE", "TYER", "", "YEAR", "", ID3v2_FRAME_YEAR, ID3_TEXT_FRAME }, - - { "TXX", "TXXX", "TXXX", "User defined text information frame", "", ID3v2_FRAME_USERDEF_TEXT, ID3_TEXT_FRAME_USERDEF }, - - //some of these (like WCOM, WOAF) allow for muliple frames - but (sigh) alas, such is not the case in AP. - { "WCM", "WCOM", "WCOM", "Commercial information", "", ID3v2_FRAME_URLCOMMINFO, ID3_URL_FRAME }, - { "WCP", "WCOP", "WCOP", "Copyright/Legal information", "", ID3v2_FRAME_URLCOPYRIGHT, ID3_URL_FRAME }, - { "WAF", "WOAF", "WOAF", "Official audio file webpage", "", ID3v2_FRAME_URLAUDIOFILE, ID3_URL_FRAME }, - { "WAR", "WOAR", "WOAR", "Official artist/performer webpage", "", ID3v2_FRAME_URLARTIST, ID3_URL_FRAME }, - { "WAS", "WOAS", "WOAS", "Official audio source webpage", "", ID3v2_FRAME_URLAUDIOSOURCE, ID3_URL_FRAME }, - { "", "WORS", "WORS", "Official Internet radio station homepage", "", ID3v2_FRAME_URLIRADIO, ID3_URL_FRAME }, - { "", "WPAY", "WPAY", "Payment", "", ID3v2_FRAME_URLPAYMENT, ID3_URL_FRAME }, - { "WPB", "WPUB", "WPUB", "Publishers official webpage", "", ID3v2_FRAME_URLPUBLISHER, ID3_URL_FRAME }, - { "WXX", "WXXX", "WXXX", "User defined URL link frame", "", ID3v2_FRAME_USERDEF_URL, ID3_URL_FRAME_USERDEF }, - - { "UFI", "UFID", "UFID", "Unique file identifier", "", ID3v2_FRAME_UFID, ID3_UNIQUE_FILE_ID_FRAME }, - { "MCI", "MCID", "MCDI", "Music CD Identifier", "", ID3v2_FRAME_MUSIC_CD_ID, ID3_CD_ID_FRAME }, - - { "COM", "COMM", "COMM", "Comment", "comment", ID3v2_FRAME_COMMENT, ID3_DESCRIBED_TEXT_FRAME }, - { "ULT", "USLT", "USLT", "Unsynchronised lyrics", "lyrics", ID3v2_FRAME_UNSYNCLYRICS, ID3_DESCRIBED_TEXT_FRAME }, - - { "", "APIC", "APIC", "Attached picture", "", ID3v2_EMBEDDED_PICTURE, ID3_ATTACHED_PICTURE_FRAME }, - { "PIC", "", "", "Attached picture", "", ID3v2_EMBEDDED_PICTURE_V2P2, ID3_OLD_V2P2_PICTURE_FRAME }, - { "GEO", "GEOB", "GEOB", "Attached object", "", ID3v2_EMBEDDED_OBJECT, ID3_ATTACHED_OBJECT_FRAME }, - - { "", "GRID", "GRID", "Group ID registration", "", ID3v2_FRAME_GRID, ID3_GROUP_ID_FRAME }, - { "", "", "SIGN", "Signature", "", ID3v2_FRAME_SIGNATURE, ID3_SIGNATURE_FRAME }, - { "", "PRIV", "PRIV", "Private frame", "", ID3v2_FRAME_PRIVATE, ID3_PRIVATE_FRAME }, - { "CNT", "PCNT", "PCNT", "Play counter", "", ID3v2_FRAME_PLAYCOUNTER, ID3_PLAYCOUNTER_FRAME }, - { "POP", "POPM", "POPM", "Popularimeter", "", ID3v2_FRAME_POPULARITY, ID3_POPULAR_FRAME } + {"", "", "", "Unknown frame", "", ID3v2_UNKNOWN_FRAME, ID3_UNKNOWN_FRAME}, + {"TAL", + "TALB", + "TALB", + "Album/Movie/Show title", + "album", + ID3v2_FRAME_ALBUM, + ID3_TEXT_FRAME}, + {"TBP", + "TBPM", + "TBPM", + "BPM (beats per minute)", + "bpm", + ID3v2_FRAME_BPM, + ID3_TEXT_FRAME}, + {"TCM", + "TCOM", + "TCOM", + "Composer", + "composer", + ID3v2_FRAME_COMPOSER, + ID3_TEXT_FRAME}, + {"TCO", + "TCON", + "TCON", + "Content Type/Genre", + "genre", + ID3v2_FRAME_CONTENTTYPE, + ID3_TEXT_FRAME}, + {"TCP", + "TCOP", + "TCOP", + "Copyright message", + "copyright", + ID3v2_FRAME_COPYRIGHT, + ID3_TEXT_FRAME}, + {"", + "", + "TDEN", + "Encoding time", + "", + ID3v2_FRAME_ENCODINGTIME, + ID3_TEXT_FRAME}, + {"TDY", + "TDLY", + "TDLY", + "Playlist delay", + "", + ID3v2_FRAME_PLAYLISTDELAY, + ID3_TEXT_FRAME}, + {"", + "", + "TDOR", + "Original release time", + "", + ID3v2_FRAME_ORIGRELTIME, + ID3_TEXT_FRAME}, + {"", + "", + "TDRC", + "Recording time", + "date", + ID3v2_FRAME_RECORDINGTIME, + ID3_TEXT_FRAME}, + {"", + "", + "TDRL", + "Release time", + "released", + ID3v2_FRAME_RELEASETIME, + ID3_TEXT_FRAME}, + {"", + "", + "TDTG", + "Tagging time", + "tagged", + ID3v2_FRAME_TAGGINGTIME, + ID3_TEXT_FRAME}, + {"TEN", + "TENC", + "TENC", + "Encoded by", + "encoder", + ID3v2_FRAME_ENCODER, + ID3_TEXT_FRAME}, + {"TXT", + "TEXT", + "TEXT", + "Lyricist/Text writer", + "writer", + ID3v2_FRAME_LYRICIST, + ID3_TEXT_FRAME}, + {"TFT", + "TFLT", + "TFLT", + "File type", + "", + ID3v2_FRAME_FILETYPE, + ID3_TEXT_FRAME}, + {"", + "", + "TIPL", + "Involved people list", + "", + ID3v2_FRAME_INVOLVEDPEOPLE, + ID3_TEXT_FRAME}, + {"TT1", + "TIT1", + "TIT1", + "Content group description", + "grouping", + ID3v2_FRAME_GROUP_DESC, + ID3_TEXT_FRAME}, + {"TT2", + "TIT2", + "TIT2", + "Title/songname/content description", + "title", + ID3v2_FRAME_TITLE, + ID3_TEXT_FRAME}, + {"TT3", + "TIT3", + "TIT3", + "Subtitle/Description refinement", + "subtitle", + ID3v2_FRAME_SUBTITLE, + ID3_TEXT_FRAME}, + {"TKE", + "TKEY", + "TKEY", + "Initial key", + "", + ID3v2_FRAME_INITIALKEY, + ID3_TEXT_FRAME}, + {"TLA", + "TLAN", + "TLAN", + "Language(s)", + "", + ID3v2_FRAME_LANGUAGE, + ID3_TEXT_FRAME}, + {"TLE", + "TLEN", + "TLEN", + "Length", + "", + ID3v2_FRAME_TIMELENGTH, + ID3_TEXT_FRAME}, + {"", + "", + "TMCL", + "Musician credits list", + "credits", + ID3v2_FRAME_MUSICIANLIST, + ID3_TEXT_FRAME}, + {"TMT", + "TMED", + "TMED", + "Media type", + "media", + ID3v2_FRAME_MEDIATYPE, + ID3_TEXT_FRAME}, + {"", "", "TMOO", "Mood", "mood", ID3v2_FRAME_MOOD, ID3_TEXT_FRAME}, + {"TOT", + "TOAL", + "TOAL", + "Original album/movie/show title", + "", + ID3v2_FRAME_ORIGALBUM, + ID3_TEXT_FRAME}, + {"TOF", + "TOFN", + "TOFN", + "Original filename", + "", + ID3v2_FRAME_ORIGFILENAME, + ID3_TEXT_FRAME}, + {"TOL", + "TOLY", + "TOLY", + "Original lyricist(s)/text writer(s)", + "", + ID3v2_FRAME_ORIGWRITER, + ID3_TEXT_FRAME}, + {"TOA", + "TOPE", + "TOPE", + "Original artist(s)/performer(s)", + "", + ID3v2_FRAME_ORIGARTIST, + ID3_TEXT_FRAME}, + {"", + "TOWN", + "TOWN", + "File owner/licensee", + "", + ID3v2_FRAME_FILEOWNER, + ID3_TEXT_FRAME}, + {"TP1", + "TPE1", + "TPE1", + "Artist/Lead performer(s)/Soloist(s)", + "artist", + ID3v2_FRAME_ARTIST, + ID3_TEXT_FRAME}, + {"TP2", + "TPE2", + "TPE2", + "Album artist/Band/orchestra/accompaniment", + "album artist", + ID3v2_FRAME_ALBUMARTIST, + ID3_TEXT_FRAME}, + {"TP3", + "TPE3", + "TPE3", + "Conductor/performer refinement", + "conductor", + ID3v2_FRAME_CONDUCTOR, + ID3_TEXT_FRAME}, + {"TP4", + "TPE4", + "TPE4", + "Interpreted or remixed by", + "remixer", + ID3v2_FRAME_REMIXER, + ID3_TEXT_FRAME}, + {"TPA", + "TPOS", + "TPOS", + "Part of a set", + "", + ID3v2_FRAME_PART_O_SET, + ID3_TEXT_FRAME}, + {"", + "", + "TPRO", + "Produced notice", + "", + ID3v2_FRAME_PRODNOTICE, + ID3_TEXT_FRAME}, + {"TPB", + "TPUB", + "TPUB", + "Publisher", + "publisher", + ID3v2_FRAME_PUBLISHER, + ID3_TEXT_FRAME}, + {"TRK", + "TRCK", + "TRCK", + "Track number/Position in set", + "trk#", + ID3v2_FRAME_TRACKNUM, + ID3_TEXT_FRAME}, + {"", + "TRSN", + "TRSN", + "Internet radio station name", + "", + ID3v2_FRAME_IRADIONAME, + ID3_TEXT_FRAME}, + {"", + "TRSO", + "TRSO", + "Internet radio station owner", + "", + ID3v2_FRAME_IRADIOOWNER, + ID3_TEXT_FRAME}, + {"", + "", + "TSOA", + "Album sort order", + "", + ID3v2_FRAME_ALBUMSORT, + ID3_TEXT_FRAME}, + {"", + "", + "TSOP", + "Performer sort order", + "", + ID3v2_FRAME_PERFORMERSORT, + ID3_TEXT_FRAME}, + {"", + "", + "TSOT", + "Title sort order", + "", + ID3v2_FRAME_TITLESORT, + ID3_TEXT_FRAME}, + {"TRC", "TSRC", "TSRC", "ISRC", "", ID3v2_FRAME_ISRC, ID3_TEXT_FRAME}, + {"TSS", + "TSSE", + "TSSE", + "Software/Hardware and settings used for encoding", + "", + ID3v2_FRAME_ENCODINGSETTINGS, + ID3_TEXT_FRAME}, + {"", + "", + "TSST", + "Set subtitle", + "", + ID3v2_FRAME_SETSUBTITLE, + ID3_TEXT_FRAME}, + + {"TDA", "TDAT", "", "Date", "", ID3v2_DATE, ID3_TEXT_FRAME}, + {"TIM", "TIME", "", "TIME", "", ID3v2_TIME, ID3_TEXT_FRAME}, + {"TOR", + "TORY", + "", + "Original Release Year", + "", + ID3v2_ORIGRELYEAR, + ID3_TEXT_FRAME}, + {"TRD", + "TRDA", + "", + "Recording dates", + "", + ID3v2_RECORDINGDATE, + ID3_TEXT_FRAME}, + {"TSI", "TSIZ", "", "Size", "", ID3v2_FRAME_SIZE, ID3_TEXT_FRAME}, + {"TYE", "TYER", "", "YEAR", "", ID3v2_FRAME_YEAR, ID3_TEXT_FRAME}, + + {"TXX", + "TXXX", + "TXXX", + "User defined text information frame", + "", + ID3v2_FRAME_USERDEF_TEXT, + ID3_TEXT_FRAME_USERDEF}, + + // some of these (like WCOM, WOAF) allow for muliple frames - but (sigh) + // alas, such is not the case in AP. + {"WCM", + "WCOM", + "WCOM", + "Commercial information", + "", + ID3v2_FRAME_URLCOMMINFO, + ID3_URL_FRAME}, + {"WCP", + "WCOP", + "WCOP", + "Copyright/Legal information", + "", + ID3v2_FRAME_URLCOPYRIGHT, + ID3_URL_FRAME}, + {"WAF", + "WOAF", + "WOAF", + "Official audio file webpage", + "", + ID3v2_FRAME_URLAUDIOFILE, + ID3_URL_FRAME}, + {"WAR", + "WOAR", + "WOAR", + "Official artist/performer webpage", + "", + ID3v2_FRAME_URLARTIST, + ID3_URL_FRAME}, + {"WAS", + "WOAS", + "WOAS", + "Official audio source webpage", + "", + ID3v2_FRAME_URLAUDIOSOURCE, + ID3_URL_FRAME}, + {"", + "WORS", + "WORS", + "Official Internet radio station homepage", + "", + ID3v2_FRAME_URLIRADIO, + ID3_URL_FRAME}, + {"", "WPAY", "WPAY", "Payment", "", ID3v2_FRAME_URLPAYMENT, ID3_URL_FRAME}, + {"WPB", + "WPUB", + "WPUB", + "Publishers official webpage", + "", + ID3v2_FRAME_URLPUBLISHER, + ID3_URL_FRAME}, + {"WXX", + "WXXX", + "WXXX", + "User defined URL link frame", + "", + ID3v2_FRAME_USERDEF_URL, + ID3_URL_FRAME_USERDEF}, + + {"UFI", + "UFID", + "UFID", + "Unique file identifier", + "", + ID3v2_FRAME_UFID, + ID3_UNIQUE_FILE_ID_FRAME}, + {"MCI", + "MCID", + "MCDI", + "Music CD Identifier", + "", + ID3v2_FRAME_MUSIC_CD_ID, + ID3_CD_ID_FRAME}, + + {"COM", + "COMM", + "COMM", + "Comment", + "comment", + ID3v2_FRAME_COMMENT, + ID3_DESCRIBED_TEXT_FRAME}, + {"ULT", + "USLT", + "USLT", + "Unsynchronised lyrics", + "lyrics", + ID3v2_FRAME_UNSYNCLYRICS, + ID3_DESCRIBED_TEXT_FRAME}, + + {"", + "APIC", + "APIC", + "Attached picture", + "", + ID3v2_EMBEDDED_PICTURE, + ID3_ATTACHED_PICTURE_FRAME}, + {"PIC", + "", + "", + "Attached picture", + "", + ID3v2_EMBEDDED_PICTURE_V2P2, + ID3_OLD_V2P2_PICTURE_FRAME}, + {"GEO", + "GEOB", + "GEOB", + "Attached object", + "", + ID3v2_EMBEDDED_OBJECT, + ID3_ATTACHED_OBJECT_FRAME}, + + {"", + "GRID", + "GRID", + "Group ID registration", + "", + ID3v2_FRAME_GRID, + ID3_GROUP_ID_FRAME}, + {"", + "", + "SIGN", + "Signature", + "", + ID3v2_FRAME_SIGNATURE, + ID3_SIGNATURE_FRAME}, + {"", + "PRIV", + "PRIV", + "Private frame", + "", + ID3v2_FRAME_PRIVATE, + ID3_PRIVATE_FRAME}, + {"CNT", + "PCNT", + "PCNT", + "Play counter", + "", + ID3v2_FRAME_PLAYCOUNTER, + ID3_PLAYCOUNTER_FRAME}, + {"POP", + "POPM", + "POPM", + "Popularimeter", + "", + ID3v2_FRAME_POPULARITY, + ID3_POPULAR_FRAME} }; -//the field listing array is mostly used for mental clarification instead of internal use - frames are parsed/rendered hardcoded irrespective of ordering here +// the field listing array is mostly used for mental clarification instead of +// internal use - frames are parsed/rendered hardcoded irrespective of ordering +// here ID3v2FieldDefinition FrameTypeConstructionList[] = { - { ID3_UNKNOWN_FRAME, 1, { ID3_UNKNOWN_FIELD } }, - { ID3_TEXT_FRAME, 2, { ID3_TEXT_ENCODING_FIELD, ID3_TEXT_FIELD } }, - { ID3_TEXT_FRAME_USERDEF, 3, { ID3_TEXT_ENCODING_FIELD, ID3_DESCRIPTION_FIELD, ID3_TEXT_FIELD } }, - { ID3_URL_FRAME, 1, { ID3_URL_FIELD } }, - { ID3_URL_FRAME_USERDEF, 3, { ID3_TEXT_ENCODING_FIELD, ID3_DESCRIPTION_FIELD, ID3_URL_FIELD } }, - { ID3_UNIQUE_FILE_ID_FRAME, 2, { ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD } }, - { ID3_CD_ID_FRAME, 1, { ID3_BINARY_DATA_FIELD } }, - { ID3_DESCRIBED_TEXT_FRAME, 4, { ID3_TEXT_ENCODING_FIELD, ID3_LANGUAGE_FIELD, ID3_DESCRIPTION_FIELD, ID3_TEXT_FIELD } }, - { ID3_ATTACHED_PICTURE_FRAME, 5, { ID3_TEXT_ENCODING_FIELD, ID3_MIME_TYPE_FIELD, ID3_PIC_TYPE_FIELD, ID3_DESCRIPTION_FIELD, ID3_BINARY_DATA_FIELD } }, - { ID3_ATTACHED_OBJECT_FRAME, 5, { ID3_TEXT_ENCODING_FIELD, ID3_MIME_TYPE_FIELD, ID3_FILENAME_FIELD, ID3_DESCRIPTION_FIELD, ID3_BINARY_DATA_FIELD } }, - { ID3_GROUP_ID_FRAME, 3, { ID3_OWNER_FIELD, ID3_GROUPSYMBOL_FIELD, ID3_BINARY_DATA_FIELD } }, - { ID3_SIGNATURE_FRAME, 2, { ID3_GROUPSYMBOL_FIELD, ID3_BINARY_DATA_FIELD } }, - { ID3_PRIVATE_FRAME, 2, { ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD } }, - { ID3_PLAYCOUNTER_FRAME, 1, { ID3_COUNTER_FIELD } }, - { ID3_POPULAR_FRAME, 3, { ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD, ID3_COUNTER_FIELD } }, - { ID3_OLD_V2P2_PICTURE_FRAME, 5, { ID3_TEXT_ENCODING_FIELD, ID3_IMAGEFORMAT_FIELD, ID3_PIC_TYPE_FIELD, ID3_DESCRIPTION_FIELD, ID3_BINARY_DATA_FIELD } } -}; + {ID3_UNKNOWN_FRAME, 1, {ID3_UNKNOWN_FIELD}}, + {ID3_TEXT_FRAME, 2, {ID3_TEXT_ENCODING_FIELD, ID3_TEXT_FIELD}}, + {ID3_TEXT_FRAME_USERDEF, + 3, + {ID3_TEXT_ENCODING_FIELD, ID3_DESCRIPTION_FIELD, ID3_TEXT_FIELD}}, + {ID3_URL_FRAME, 1, {ID3_URL_FIELD}}, + {ID3_URL_FRAME_USERDEF, + 3, + {ID3_TEXT_ENCODING_FIELD, ID3_DESCRIPTION_FIELD, ID3_URL_FIELD}}, + {ID3_UNIQUE_FILE_ID_FRAME, 2, {ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD}}, + {ID3_CD_ID_FRAME, 1, {ID3_BINARY_DATA_FIELD}}, + {ID3_DESCRIBED_TEXT_FRAME, + 4, + {ID3_TEXT_ENCODING_FIELD, + ID3_LANGUAGE_FIELD, + ID3_DESCRIPTION_FIELD, + ID3_TEXT_FIELD}}, + {ID3_ATTACHED_PICTURE_FRAME, + 5, + {ID3_TEXT_ENCODING_FIELD, + ID3_MIME_TYPE_FIELD, + ID3_PIC_TYPE_FIELD, + ID3_DESCRIPTION_FIELD, + ID3_BINARY_DATA_FIELD}}, + {ID3_ATTACHED_OBJECT_FRAME, + 5, + {ID3_TEXT_ENCODING_FIELD, + ID3_MIME_TYPE_FIELD, + ID3_FILENAME_FIELD, + ID3_DESCRIPTION_FIELD, + ID3_BINARY_DATA_FIELD}}, + {ID3_GROUP_ID_FRAME, + 3, + {ID3_OWNER_FIELD, ID3_GROUPSYMBOL_FIELD, ID3_BINARY_DATA_FIELD}}, + {ID3_SIGNATURE_FRAME, 2, {ID3_GROUPSYMBOL_FIELD, ID3_BINARY_DATA_FIELD}}, + {ID3_PRIVATE_FRAME, 2, {ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD}}, + {ID3_PLAYCOUNTER_FRAME, 1, {ID3_COUNTER_FIELD}}, + {ID3_POPULAR_FRAME, + 3, + {ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD, ID3_COUNTER_FIELD}}, + {ID3_OLD_V2P2_PICTURE_FRAME, + 5, + {ID3_TEXT_ENCODING_FIELD, + ID3_IMAGEFORMAT_FIELD, + ID3_PIC_TYPE_FIELD, + ID3_DESCRIPTION_FIELD, + ID3_BINARY_DATA_FIELD}}}; -//used to determine mimetype for APIC image writing +// used to determine mimetype for APIC image writing ImageFileFormatDefinition ImageList[] = { - { "image/jpeg", ".jpg", 4, "\xFF\xD8\xFF\xE0" }, - { "image/jpeg", ".jpg", 4, "\xFF\xD8\xFF\xE1" }, - { "image/png", ".png", 8, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" }, - { "image/pdf", ".pdf", 7, "%PDF-1." }, - { "image/jp2", ".jp2", 12, "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A\x00\x00\x00\x14\x66\x74\x79\x70\x6A\x70\x32\x20" }, - { "image/gif", ".gif", 6, "GIF89a" }, - { "image/tiff", ".tiff", 4, "\x4D\x4D\x00\x2A" }, - { "image/tiff", ".tiff", 4, "\x49\x49\x2A\x00" }, - { "image/bmp", ".bmp", 2, "\x42\x4D" }, - { "image/bmp", ".bmp", 2, "\x42\x41" }, - { "image/photoshop", ".psd", 4, "8BPS" }, - { "image/other", ".img", 0, "" } -}; + {"image/jpeg", ".jpg", 3, "\xFF\xD8\xFF"}, + {"image/png", ".png", 8, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"}, + {"image/pdf", ".pdf", 7, "%PDF-1."}, + {"image/jp2", + ".jp2", + 12, + "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A\x00\x00\x00\x14\x66\x74" + "\x79\x70\x6A\x70\x32\x20"}, + {"image/gif", ".gif", 6, "GIF89a"}, + {"image/tiff", ".tiff", 4, "\x4D\x4D\x00\x2A"}, + {"image/tiff", ".tiff", 4, "\x49\x49\x2A\x00"}, + {"image/bmp", ".bmp", 2, "\x42\x4D"}, + {"image/bmp", ".bmp", 2, "\x42\x41"}, + {"image/photoshop", ".psd", 4, "8BPS"}, + {"image/other", ".img", 0, ""}}; ID3ImageType ImageTypeList[] = { - { 0x00, "0x00", "Other" }, - { 0x01, "0x01", "32x32 pixels 'file icon' (PNG only)" }, - { 0x02, "0x02", "Other file icon" }, - { 0x03, "0x03", "Cover (front)" }, - { 0x04, "0x04", "Cover (back)" }, - { 0x05, "0x05", "Leaflet page" }, - { 0x06, "0x06", "Media (e.g. label side of CD)" }, - { 0x07, "0x07", "Lead artist/lead performer/soloist" }, - { 0x08, "0x08", "Artist/performer" }, - { 0x09, "0x09", "Conductor" }, - { 0x0A, "0x0A", "Band/Orchestra" }, - { 0x0B, "0x0B", "Composer" }, - { 0x0C, "0x0C", "Lyricist/text writer" }, - { 0x0D, "0x0D", "Recording Location" }, - { 0x0E, "0x0E", "During recording" }, - { 0x0F, "0x0F", "During performance" }, - { 0x10, "0x10", "Movie/video screen capture" }, - { 0x11, "0x11", "A bright coloured fish" }, - { 0x12, "0x12", "Illustration" }, - { 0x13, "0x13", "Band/artist logotype" }, - { 0x14, "0x14", "Publisher/Studio logotype" } -}; + {0x00, "0x00", "Other"}, + {0x01, "0x01", "32x32 pixels 'file icon' (PNG only)"}, + {0x02, "0x02", "Other file icon"}, + {0x03, "0x03", "Cover (front)"}, + {0x04, "0x04", "Cover (back)"}, + {0x05, "0x05", "Leaflet page"}, + {0x06, "0x06", "Media (e.g. label side of CD)"}, + {0x07, "0x07", "Lead artist/lead performer/soloist"}, + {0x08, "0x08", "Artist/performer"}, + {0x09, "0x09", "Conductor"}, + {0x0A, "0x0A", "Band/Orchestra"}, + {0x0B, "0x0B", "Composer"}, + {0x0C, "0x0C", "Lyricist/text writer"}, + {0x0D, "0x0D", "Recording Location"}, + {0x0E, "0x0E", "During recording"}, + {0x0F, "0x0F", "During performance"}, + {0x10, "0x10", "Movie/video screen capture"}, + {0x11, "0x11", "A bright coloured fish"}, + {0x12, "0x12", "Illustration"}, + {0x13, "0x13", "Band/artist logotype"}, + {0x14, "0x14", "Publisher/Studio logotype"}}; diff -Nru atomicparsley-0.9.6/src/id3v2.h atomicparsley-20210715.151551.e7ad03a/src/id3v2.h --- atomicparsley-0.9.6/src/id3v2.h 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/id3v2.h 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - id3v2.h - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,7 +10,7 @@ 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 + 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 @@ -20,22 +20,23 @@ */ //==================================================================// -struct AdjunctArgs { - const char* targetLang; - const char* descripArg; - const char* mimeArg; - const char* pictypeArg; - const char* filenameArg; - const char* ratingArg; - const char* dataArg; //multipurposed: PRIV's binary data, GRID's group data, UFID's binary data, POPM's counter field - uint8_t pictype_uint8; - uint8_t groupSymbol; - bool zlibCompressed; - bool multistringtext; +struct AdjunctArgs { + const char *targetLang; + const char *descripArg; + const char *mimeArg; + const char *pictypeArg; + const char *filenameArg; + const char *ratingArg; + const char *dataArg; // multipurposed: PRIV's binary data, GRID's group data, + // UFID's binary data, POPM's counter field + uint8_t pictype_uint8; + uint8_t groupSymbol; + bool zlibCompressed; + bool multistringtext; }; -uint64_t syncsafeXX_to_UInt64(char* syncsafe_int, uint8_t syncsafe_len); -uint32_t syncsafe32_to_UInt32(char* syncsafe_int); +uint64_t syncsafeXX_to_UInt64(char *syncsafe_int, uint8_t syncsafe_len); +uint32_t syncsafe32_to_UInt32(char *syncsafe_int); bool ID3v2_TestTagFlag(uint8_t TagFlag, uint8_t TagBit); bool ID3v2_TestFrameFlag(uint16_t FrameFlag, uint16_t FrameBit); @@ -43,21 +44,25 @@ void ListID3FrameIDstrings(); void List_imagtype_strings(); -const char* ConvertCLIFrameStr_TO_frameID(const char* frame_str); +const char *ConvertCLIFrameStr_TO_frameID(const char *frame_str); bool TestCLI_for_FrameParams(int frametype, uint8_t testparam); -int MatchID3FrameIDstr(const char* foundFrameID, uint8_t tagVersion); +int MatchID3FrameIDstr(const char *foundFrameID, uint8_t tagVersion); uint8_t GetFrameCompositionDescription(int ID3v2_FrameTypeID); -int FrameStr_TO_FrameType(const char* frame_str); +int FrameStr_TO_FrameType(const char *frame_str); -void APar_ID32_ScanID3Tag(FILE* source_file, AtomicInfo* id32_atom); +void APar_ID32_ScanID3Tag(FILE *source_file, AtomicInfo *id32_atom); -uint32_t APar_GetTagSize(AtomicInfo* id32_atom); -uint32_t APar_Render_ID32_Tag(AtomicInfo* id32_atom, uint32_t max_alloc); +uint32_t APar_GetTagSize(AtomicInfo *id32_atom); +uint32_t APar_Render_ID32_Tag(AtomicInfo *id32_atom, uint32_t max_alloc); -char* APar_ConvertField_to_UTF8(ID3v2Frame* targetframe, int fieldtype); +char *APar_ConvertField_to_UTF8(ID3v2Frame *targetframe, int fieldtype); -void APar_ID3Tag_Init(AtomicInfo* id32_atom); -void APar_ID3FrameAmmend(AtomicInfo* id32_atom, const char* frame_str, const char* frame_payload, AdjunctArgs* adjunct_payloads, uint8_t str_encoding); +void APar_ID3Tag_Init(AtomicInfo *id32_atom); +void APar_ID3FrameAmmend(AtomicInfo *id32_atom, + const char *frame_str, + const char *frame_payload, + AdjunctArgs *adjunct_payloads, + uint8_t str_encoding); -void APar_FreeID32Memory(ID3v2Tag* id32tag); +void APar_FreeID32Memory(ID3v2Tag *id32tag); diff -Nru atomicparsley-0.9.6/src/id3v2types.h atomicparsley-20210715.151551.e7ad03a/src/id3v2types.h --- atomicparsley-0.9.6/src/id3v2types.h 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/id3v2types.h 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - id3v2types.h - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,7 +10,7 @@ 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 + 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 @@ -21,140 +21,143 @@ //==================================================================// enum ID3_FieldTypes { - ID3_UNKNOWN_FIELD = -1, - ID3_TEXT_FIELD, - ID3_TEXT_ENCODING_FIELD, - ID3_OWNER_FIELD, //UFID,PRIV - ID3_DESCRIPTION_FIELD, //TXXX, WXXX - ID3_URL_FIELD, - ID3_LANGUAGE_FIELD, //USLT - ID3_MIME_TYPE_FIELD, //APIC - ID3_PIC_TYPE_FIELD, //APIC - ID3_BINARY_DATA_FIELD, //APIC,GEOB - ID3_FILENAME_FIELD, //GEOB - ID3_GROUPSYMBOL_FIELD, - ID3_COUNTER_FIELD, - ID3_IMAGEFORMAT_FIELD //PIC in v2.2 + ID3_UNKNOWN_FIELD = -1, + ID3_TEXT_FIELD, + ID3_TEXT_ENCODING_FIELD, + ID3_OWNER_FIELD, // UFID,PRIV + ID3_DESCRIPTION_FIELD, // TXXX, WXXX + ID3_URL_FIELD, + ID3_LANGUAGE_FIELD, // USLT + ID3_MIME_TYPE_FIELD, // APIC + ID3_PIC_TYPE_FIELD, // APIC + ID3_BINARY_DATA_FIELD, // APIC,GEOB + ID3_FILENAME_FIELD, // GEOB + ID3_GROUPSYMBOL_FIELD, + ID3_COUNTER_FIELD, + ID3_IMAGEFORMAT_FIELD // PIC in v2.2 }; -//the order of these frame types must exactly match the order listed in the FrameTypeConstructionList[] array!!! +// the order of these frame types must exactly match the order listed in the +// FrameTypeConstructionList[] array!!! enum ID3v2FrameType { - ID3_UNKNOWN_FRAME = -1, - ID3_TEXT_FRAME, - ID3_TEXT_FRAME_USERDEF, - ID3_URL_FRAME, - ID3_URL_FRAME_USERDEF, - ID3_UNIQUE_FILE_ID_FRAME, - ID3_CD_ID_FRAME, - ID3_DESCRIBED_TEXT_FRAME, //oy... these frames (COMM, USLT) can differ by description - ID3_ATTACHED_PICTURE_FRAME, - ID3_ATTACHED_OBJECT_FRAME, - ID3_GROUP_ID_FRAME, - ID3_SIGNATURE_FRAME, - ID3_PRIVATE_FRAME, - ID3_PLAYCOUNTER_FRAME, - ID3_POPULAR_FRAME, - ID3_OLD_V2P2_PICTURE_FRAME + ID3_UNKNOWN_FRAME = -1, + ID3_TEXT_FRAME, + ID3_TEXT_FRAME_USERDEF, + ID3_URL_FRAME, + ID3_URL_FRAME_USERDEF, + ID3_UNIQUE_FILE_ID_FRAME, + ID3_CD_ID_FRAME, + ID3_DESCRIBED_TEXT_FRAME, // oy... these frames (COMM, USLT) can differ by + // description + ID3_ATTACHED_PICTURE_FRAME, + ID3_ATTACHED_OBJECT_FRAME, + ID3_GROUP_ID_FRAME, + ID3_SIGNATURE_FRAME, + ID3_PRIVATE_FRAME, + ID3_PLAYCOUNTER_FRAME, + ID3_POPULAR_FRAME, + ID3_OLD_V2P2_PICTURE_FRAME }; -//the order of these frames must exactly match the order listed in the KnownFrames[] array!!! +// the order of these frames must exactly match the order listed in the +// KnownFrames[] array!!! enum ID3v2FrameIDs { - ID3v2_UNKNOWN_FRAME = -1, - ID3v2_FRAME_ALBUM, - ID3v2_FRAME_BPM, - ID3v2_FRAME_COMPOSER, - ID3v2_FRAME_CONTENTTYPE, - ID3v2_FRAME_COPYRIGHT, - ID3v2_FRAME_ENCODINGTIME, - ID3v2_FRAME_PLAYLISTDELAY, - ID3v2_FRAME_ORIGRELTIME, - ID3v2_FRAME_RECORDINGTIME, - ID3v2_FRAME_RELEASETIME, - ID3v2_FRAME_TAGGINGTIME, - ID3v2_FRAME_ENCODER, - ID3v2_FRAME_LYRICIST, - ID3v2_FRAME_FILETYPE, - ID3v2_FRAME_INVOLVEDPEOPLE, - ID3v2_FRAME_GROUP_DESC, - ID3v2_FRAME_TITLE, - ID3v2_FRAME_SUBTITLE, - ID3v2_FRAME_INITIALKEY, - ID3v2_FRAME_LANGUAGE, - ID3v2_FRAME_TIMELENGTH, - ID3v2_FRAME_MUSICIANLIST, - ID3v2_FRAME_MEDIATYPE, - ID3v2_FRAME_MOOD, - ID3v2_FRAME_ORIGALBUM, - ID3v2_FRAME_ORIGFILENAME, - ID3v2_FRAME_ORIGWRITER, - ID3v2_FRAME_ORIGARTIST, - ID3v2_FRAME_FILEOWNER, - ID3v2_FRAME_ARTIST, - ID3v2_FRAME_ALBUMARTIST, - ID3v2_FRAME_CONDUCTOR, - ID3v2_FRAME_REMIXER, - ID3v2_FRAME_PART_O_SET, - ID3v2_FRAME_PRODNOTICE, - ID3v2_FRAME_PUBLISHER, - ID3v2_FRAME_TRACKNUM, - ID3v2_FRAME_IRADIONAME, - ID3v2_FRAME_IRADIOOWNER, - ID3v2_FRAME_ALBUMSORT, - ID3v2_FRAME_PERFORMERSORT, - ID3v2_FRAME_TITLESORT, - ID3v2_FRAME_ISRC, - ID3v2_FRAME_ENCODINGSETTINGS, - ID3v2_FRAME_SETSUBTITLE, - ID3v2_DATE, - ID3v2_TIME, - ID3v2_ORIGRELYEAR, - ID3v2_RECORDINGDATE, - ID3v2_FRAME_SIZE, - ID3v2_FRAME_YEAR, - ID3v2_FRAME_USERDEF_TEXT, - ID3v2_FRAME_URLCOMMINFO, - ID3v2_FRAME_URLCOPYRIGHT, - ID3v2_FRAME_URLAUDIOFILE, - ID3v2_FRAME_URLARTIST, - ID3v2_FRAME_URLAUDIOSOURCE, - ID3v2_FRAME_URLIRADIO, - ID3v2_FRAME_URLPAYMENT, - ID3v2_FRAME_URLPUBLISHER, - ID3v2_FRAME_USERDEF_URL, - ID3v2_FRAME_UFID, - ID3v2_FRAME_MUSIC_CD_ID, - ID3v2_FRAME_COMMENT, - ID3v2_FRAME_UNSYNCLYRICS, - ID3v2_EMBEDDED_PICTURE, - ID3v2_EMBEDDED_PICTURE_V2P2, - ID3v2_EMBEDDED_OBJECT, - ID3v2_FRAME_GRID, - ID3v2_FRAME_SIGNATURE, - ID3v2_FRAME_PRIVATE, - ID3v2_FRAME_PLAYCOUNTER, - ID3v2_FRAME_POPULARITY + ID3v2_UNKNOWN_FRAME = -1, + ID3v2_FRAME_ALBUM, + ID3v2_FRAME_BPM, + ID3v2_FRAME_COMPOSER, + ID3v2_FRAME_CONTENTTYPE, + ID3v2_FRAME_COPYRIGHT, + ID3v2_FRAME_ENCODINGTIME, + ID3v2_FRAME_PLAYLISTDELAY, + ID3v2_FRAME_ORIGRELTIME, + ID3v2_FRAME_RECORDINGTIME, + ID3v2_FRAME_RELEASETIME, + ID3v2_FRAME_TAGGINGTIME, + ID3v2_FRAME_ENCODER, + ID3v2_FRAME_LYRICIST, + ID3v2_FRAME_FILETYPE, + ID3v2_FRAME_INVOLVEDPEOPLE, + ID3v2_FRAME_GROUP_DESC, + ID3v2_FRAME_TITLE, + ID3v2_FRAME_SUBTITLE, + ID3v2_FRAME_INITIALKEY, + ID3v2_FRAME_LANGUAGE, + ID3v2_FRAME_TIMELENGTH, + ID3v2_FRAME_MUSICIANLIST, + ID3v2_FRAME_MEDIATYPE, + ID3v2_FRAME_MOOD, + ID3v2_FRAME_ORIGALBUM, + ID3v2_FRAME_ORIGFILENAME, + ID3v2_FRAME_ORIGWRITER, + ID3v2_FRAME_ORIGARTIST, + ID3v2_FRAME_FILEOWNER, + ID3v2_FRAME_ARTIST, + ID3v2_FRAME_ALBUMARTIST, + ID3v2_FRAME_CONDUCTOR, + ID3v2_FRAME_REMIXER, + ID3v2_FRAME_PART_O_SET, + ID3v2_FRAME_PRODNOTICE, + ID3v2_FRAME_PUBLISHER, + ID3v2_FRAME_TRACKNUM, + ID3v2_FRAME_IRADIONAME, + ID3v2_FRAME_IRADIOOWNER, + ID3v2_FRAME_ALBUMSORT, + ID3v2_FRAME_PERFORMERSORT, + ID3v2_FRAME_TITLESORT, + ID3v2_FRAME_ISRC, + ID3v2_FRAME_ENCODINGSETTINGS, + ID3v2_FRAME_SETSUBTITLE, + ID3v2_DATE, + ID3v2_TIME, + ID3v2_ORIGRELYEAR, + ID3v2_RECORDINGDATE, + ID3v2_FRAME_SIZE, + ID3v2_FRAME_YEAR, + ID3v2_FRAME_USERDEF_TEXT, + ID3v2_FRAME_URLCOMMINFO, + ID3v2_FRAME_URLCOPYRIGHT, + ID3v2_FRAME_URLAUDIOFILE, + ID3v2_FRAME_URLARTIST, + ID3v2_FRAME_URLAUDIOSOURCE, + ID3v2_FRAME_URLIRADIO, + ID3v2_FRAME_URLPAYMENT, + ID3v2_FRAME_URLPUBLISHER, + ID3v2_FRAME_USERDEF_URL, + ID3v2_FRAME_UFID, + ID3v2_FRAME_MUSIC_CD_ID, + ID3v2_FRAME_COMMENT, + ID3v2_FRAME_UNSYNCLYRICS, + ID3v2_EMBEDDED_PICTURE, + ID3v2_EMBEDDED_PICTURE_V2P2, + ID3v2_EMBEDDED_OBJECT, + ID3v2_FRAME_GRID, + ID3v2_FRAME_SIGNATURE, + ID3v2_FRAME_PRIVATE, + ID3v2_FRAME_PLAYCOUNTER, + ID3v2_FRAME_POPULARITY }; enum ID3v2_TagFlags { - ID32_TAGFLAG_BIT0 = 0x01, - ID32_TAGFLAG_BIT1 = 0x02, - ID32_TAGFLAG_BIT2 = 0x04, - ID32_TAGFLAG_BIT3 = 0x08, - ID32_TAGFLAG_FOOTER = 0x10, - ID32_TAGFLAG_EXPERIMENTAL = 0x20, - ID32_TAGFLAG_EXTENDEDHEADER = 0x40, - ID32_TAGFLAG_UNSYNCRONIZATION = 0x80 + ID32_TAGFLAG_BIT0 = 0x01, + ID32_TAGFLAG_BIT1 = 0x02, + ID32_TAGFLAG_BIT2 = 0x04, + ID32_TAGFLAG_BIT3 = 0x08, + ID32_TAGFLAG_FOOTER = 0x10, + ID32_TAGFLAG_EXPERIMENTAL = 0x20, + ID32_TAGFLAG_EXTENDEDHEADER = 0x40, + ID32_TAGFLAG_UNSYNCRONIZATION = 0x80 }; enum ID3v2_FrameFlags { - ID32_FRAMEFLAG_STATUS = 0x4000, - ID32_FRAMEFLAG_PRESERVE = 0x2000, - ID32_FRAMEFLAG_READONLY = 0x1000, - ID32_FRAMEFLAG_GROUPING = 0x0040, - ID32_FRAMEFLAG_COMPRESSED = 0x0008, - ID32_FRAMEFLAG_ENCRYPTED = 0x0004, - ID32_FRAMEFLAG_UNSYNCED = 0x0002, - ID32_FRAMEFLAG_LENINDICATED = 0x0001 + ID32_FRAMEFLAG_STATUS = 0x4000, + ID32_FRAMEFLAG_PRESERVE = 0x2000, + ID32_FRAMEFLAG_READONLY = 0x1000, + ID32_FRAMEFLAG_GROUPING = 0x0040, + ID32_FRAMEFLAG_COMPRESSED = 0x0008, + ID32_FRAMEFLAG_ENCRYPTED = 0x0004, + ID32_FRAMEFLAG_UNSYNCED = 0x0002, + ID32_FRAMEFLAG_LENINDICATED = 0x0001 }; // the wording of the ID3 (v2.4 in this case) 'informal standard' is not always @@ -190,80 +193,88 @@ // http://sourceforge.net/project/showfiles.php?group_id=979&package_id=4679 enum text_encodings { - TE_LATIN1 = 0, - TE_UTF16LE_WITH_BOM = 1, - TE_UTF16BE_NO_BOM = 2, - TE_UTF8 = 3 + TE_LATIN1 = 0, + TE_UTF16LE_WITH_BOM = 1, + TE_UTF16BE_NO_BOM = 2, + TE_UTF8 = 3 }; -// Structure that defines the (subset) known ID3 frames defined by id3 informal specification. +// Structure that defines the (subset) known ID3 frames defined by id3 informal +// specification. typedef struct { - const char* ID3V2p2_FrameID; - const char* ID3V2p3_FrameID; - const char* ID3V2p4_FrameID; - const char* ID3V2_FrameDescription; - const char* CLI_frameIDpreset; - int ID3v2_InternalFrameID; - int ID3v2_FrameType; + const char *ID3V2p2_FrameID; + const char *ID3V2p3_FrameID; + const char *ID3V2p4_FrameID; + const char *ID3V2_FrameDescription; + const char *CLI_frameIDpreset; + int ID3v2_InternalFrameID; + int ID3v2_FrameType; } ID3FrameDefinition; typedef struct { - const char* image_mimetype; - const char* image_fileextn; - uint8_t image_testbytes; - const char* image_binaryheader; + const char *image_mimetype; + const char *image_fileextn; + uint8_t image_testbytes; + const char *image_binaryheader; } ImageFileFormatDefinition; typedef struct { - uint8_t hexcode; - const char* hexstring; - const char* imagetype_str; + uint8_t hexcode; + const char *hexstring; + const char *imagetype_str; } ID3ImageType; -// Structure that defines how any ID3v2FrameType is constructed, listing an array of its constituent ID3_FieldTypes +// Structure that defines how any ID3v2FrameType is constructed, listing an +// array of its constituent ID3_FieldTypes typedef struct { - ID3v2FrameType ID3_FrameType; - uint8_t ID3_FieldCount; - ID3_FieldTypes ID3_FieldComponents[5]; //max known to be tested + ID3v2FrameType ID3_FrameType; + uint8_t ID3_FieldCount; + ID3_FieldTypes ID3_FieldComponents[5]; // max known to be tested } ID3v2FieldDefinition; struct ID3v2Fields { - int ID3v2_Field_Type; - uint32_t field_length; - uint32_t alloc_length; - char* field_string; - ID3v2Fields* next_field; + int ID3v2_Field_Type; + uint32_t field_length; + uint32_t alloc_length; + char *field_string; + ID3v2Fields *next_field; }; struct ID3v2Frame { - char ID3v2_Frame_Namestr[5]; - uint32_t ID3v2_Frame_Length; //this is the real length, not a syncsafe int; note: does not include frame ID (like 'TIT2', 'TCO' - 3or4 bytes) or frame flags (2bytes) - uint16_t ID3v2_Frame_Flags; - - //these next 2 values can be potentially be stored based on bitsetting in frame flags; - uint8_t ID3v2_Frame_GroupingSymbol; - uint32_t ID3v2_Frame_ExpandedLength; - - int ID3v2_Frame_ID; - int ID3v2_FrameType; - uint8_t ID3v2_FieldCount; - uint8_t textfield_tally; - ID3v2Fields* ID3v2_Frame_Fields; //malloc - ID3v2Frame* ID3v2_NextFrame; - bool eliminate_frame; + char ID3v2_Frame_Namestr[5]; + uint32_t ID3v2_Frame_Length; // this is the real length, not a syncsafe int; + // note: does not include frame ID (like 'TIT2', + // 'TCO' - 3or4 bytes) or frame flags (2bytes) + uint16_t ID3v2_Frame_Flags; + + // these next 2 values can be potentially be stored based on bitsetting in + // frame flags; + uint8_t ID3v2_Frame_GroupingSymbol; + uint32_t ID3v2_Frame_ExpandedLength; + + int ID3v2_Frame_ID; + int ID3v2_FrameType; + uint8_t ID3v2_FieldCount; + uint8_t textfield_tally; + ID3v2Fields *ID3v2_Frame_Fields; // malloc + ID3v2Frame *ID3v2_NextFrame; + bool eliminate_frame; }; -struct ID3v2Tag { - uint8_t ID3v2Tag_MajorVersion; - uint8_t ID3v2Tag_RevisionVersion; - uint8_t ID3v2Tag_Flags; - uint32_t ID3v2Tag_Length; //this is a bonafide uint_32_t length, not a syncsafe int - - //this extended header section depends on a bitsetting in ID3v2Tag_Flags - uint32_t ID3v2_Tag_ExtendedHeader_Length; //the entire extended header section is unimplemented flags & flag frames - - ID3v2Frame* ID3v2_FirstFrame; - ID3v2Frame* ID3v2_FrameList; - uint16_t ID3v2_FrameCount; - bool modified_tag; +struct ID3v2Tag { + uint8_t ID3v2Tag_MajorVersion; + uint8_t ID3v2Tag_RevisionVersion; + uint8_t ID3v2Tag_Flags; + uint32_t ID3v2Tag_Length; // this is a bonafide uint_32_t length, not a + // syncsafe int + + // this extended header section depends on a bitsetting in ID3v2Tag_Flags + uint32_t + ID3v2_Tag_ExtendedHeader_Length; // the entire extended header section is + // unimplemented flags & flag frames + + ID3v2Frame *ID3v2_FirstFrame; + ID3v2Frame *ID3v2_FrameList; + uint16_t ID3v2_FrameCount; + bool modified_tag; }; diff -Nru atomicparsley-0.9.6/src/main.cpp atomicparsley-20210715.151551.e7ad03a/src/main.cpp --- atomicparsley-0.9.6/src/main.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/main.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -15,7 +15,7 @@ cannot, write to the Free Software Foundation, 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org - Copyright 2005-2007 puck_lock + Copyright (c) 2005-2007 puck_lock with contributions from others; see the CREDITS file ---------------------- @@ -29,3195 +29,4452 @@ #include "AtomicParsley.h" // define one-letter cli options for -#define OPT_HELP 'h' -#define OPT_TEST 'T' -#define OPT_ShowTextData 't' -#define OPT_ExtractPix 'E' -#define OPT_ExtractPixToPath 'e' -#define Meta_artist 'a' -#define Meta_artDirector 0xC6 -#define Meta_arranger 0xC8 -#define Meta_author 0xC9 -#define Meta_conductor 0xD0 -#define Meta_director 0xD1 -#define Meta_originalArtist 0xD2 -#define Meta_producer 0xD3 +#define OPT_HELP 'h' +#define OPT_TEST 'T' +#define OPT_ShowTextData 't' +#define OPT_ExtractPix 'E' +#define OPT_ExtractPixToPath 'e' +#define Meta_artist 'a' +#define Meta_artDirector 0xC6 +#define Meta_arranger 0xC8 +#define Meta_author 0xC9 +#define Meta_conductor 0xD0 +#define Meta_director 0xD1 +#define Meta_originalArtist 0xD2 +#define Meta_producer 0xD3 //#define Meta_performer 0xD4 -#define Meta_soundEngineer 0xD5 -#define Meta_soloist 0xD6 -#define Meta_executiveProducer 0xD7 -#define Meta_songtitle 's' -#define Meta_subtitle 0xC5 -#define Meta_album 'b' -#define Meta_tracknum 'k' -#define Meta_disknum 'd' -#define Meta_genre 'g' -#define Meta_comment 'c' -#define Meta_year 'y' -#define Meta_lyrics 'l' -#define Meta_lyrics_file 0xC7 -#define Meta_composer 'w' -#define Meta_copyright 'x' -#define Meta_grouping 'G' -#define Meta_album_artist 'A' -#define Meta_compilation 'C' -#define Meta_hdvideo 'O' -#define Meta_BPM 'B' -#define Meta_artwork 'r' -#define Meta_advisory 'V' -#define Meta_stik 'S' -#define Meta_description 'p' -#define Meta_Rating 0xCB -#define Meta_longdescription 'j' -#define Meta_TV_Network 'n' -#define Meta_TV_ShowName 'H' -#define Meta_TV_EpisodeNumber 'N' -#define Meta_TV_SeasonNumber 'U' -#define Meta_TV_Episode 'I' -#define Meta_podcastFlag 'f' -#define Meta_category 'q' -#define Meta_keyword 'K' -#define Meta_podcast_URL 'L' -#define Meta_podcast_GUID 'J' -#define Meta_PurchaseDate 'D' -#define Meta_apID 'Y' -#define Meta_cnID 0xC0 -#define Meta_geID 0xC2 -#define Meta_xID 0xC3 -#define Meta_storedescription 0xC4 -#define Meta_EncodingTool 0xB7 -#define Meta_EncodedBy 0xC1 -#define Meta_PlayGapless 0xBA -#define Meta_SortOrder 0xBF - -#define Meta_ReverseDNS_Form 'M' -#define Meta_rDNS_rating 0xBB - -#define Meta_StandardDate 'Z' -#define Meta_URL 'u' -#define Meta_Information 'i' -#define Meta_uuid 'z' -#define Opt_Extract_all_uuids 0xB8 -#define Opt_Extract_a_uuid 0xB9 -#define Opt_Ipod_AVC_uuid 0xBE - -#define Metadata_Purge 'P' -#define UserData_Purge 'X' -#define foobar_purge '.' -#define Meta_dump 'Q' -#define Manual_atom_removal 'R' -#define Opt_FreeFree 'F' -#define OPT_OutputFile 'o' -#define OPT_NoOptimize 0xBD +#define Meta_soundEngineer 0xD5 +#define Meta_soloist 0xD6 +#define Meta_executiveProducer 0xD7 +#define Meta_songtitle 's' +#define Meta_subtitle 0xC5 +#define Meta_album 'b' +#define Meta_tracknum 'k' +#define Meta_disknum 'd' +#define Meta_genre 'g' +#define Meta_comment 'c' +#define Meta_year 'y' +#define Meta_lyrics 'l' +#define Meta_lyrics_file 0xC7 +#define Meta_composer 'w' +#define Meta_copyright 'x' +#define Meta_grouping 'G' +#define Meta_album_artist 'A' +#define Meta_compilation 'C' +#define Meta_hdvideo 'O' +#define Meta_BPM 'B' +#define Meta_artwork 'r' +#define Meta_advisory 'V' +#define Meta_stik 'S' +#define Meta_description 'p' +#define Meta_Rating 0xCB +#define Meta_longdescription 'j' +#define Meta_TV_Network 'n' +#define Meta_TV_ShowName 'H' +#define Meta_TV_EpisodeNumber 'N' +#define Meta_TV_SeasonNumber 'U' +#define Meta_TV_Episode 'I' +#define Meta_podcastFlag 'f' +#define Meta_category 'q' +#define Meta_keyword 'K' +#define Meta_podcast_URL 'L' +#define Meta_podcast_GUID 'J' +#define Meta_PurchaseDate 'D' +#define Meta_apID 'Y' +#define Meta_cnID 0xC0 +#define Meta_geID 0xC2 +#define Meta_xID 0xC3 +#define Meta_storedescription 0xC4 +#define Meta_EncodingTool 0xB7 +#define Meta_EncodedBy 0xC1 +#define Meta_PlayGapless 0xBA +#define Meta_SortOrder 0xBF + +#define Meta_ReverseDNS_Form 'M' +#define Meta_rDNS_rating 0xBB + +#define Meta_StandardDate 'Z' +#define Meta_URL 'u' +#define Meta_Information 'i' +#define Meta_uuid 'z' +#define Opt_Extract_all_uuids 0xB8 +#define Opt_Extract_a_uuid 0xB9 +#define Opt_Ipod_AVC_uuid 0xBE + +#define Metadata_Purge 'P' +#define UserData_Purge 'X' +#define foobar_purge '.' +#define Meta_dump 'Q' +#define Manual_atom_removal 'R' +#define Opt_FreeFree 'F' +#define OPT_OutputFile 'o' +#define OPT_NoOptimize 0xBD -#define OPT_OverWrite 'W' +#define OPT_OverWrite 'W' -#if defined (_WIN32) -#define OPT_PreserveTimeStamps 0xCA +#if defined(_WIN32) +#define OPT_PreserveTimeStamps 0xCA #endif -#define ISO_Copyright 0xAA +#define ISO_Copyright 0xAA -#define _3GP_Title 0xAB -#define _3GP_Author 0xAC -#define _3GP_Performer 0xAD -#define _3GP_Genre 0xAE -#define _3GP_Description 0xAF -#define _3GP_Copyright 0xB0 -#define _3GP_Album 0xB1 -#define _3GP_Year 0xB2 -#define _3GP_Rating 0xB3 -#define _3GP_Classification 0xB4 -#define _3GP_Keyword 0xB5 -#define _3GP_Location 0xB6 - -#define Meta_ID3v2Tag 0xBC +#define _3GP_Title 0xAB +#define _3GP_Author 0xAC +#define _3GP_Performer 0xAD +#define _3GP_Genre 0xAE +#define _3GP_Description 0xAF +#define _3GP_Copyright 0xB0 +#define _3GP_Album 0xB1 +#define _3GP_Year 0xB2 +#define _3GP_Rating 0xB3 +#define _3GP_Classification 0xB4 +#define _3GP_Keyword 0xB5 +#define _3GP_Location 0xB6 + +#define Meta_ID3v2Tag 0xBC + +#define Meta_movementCount 0xE0 +#define Meta_movementName 0xE1 +#define Meta_movementNumber 0xE2 +#define Meta_showWorkMovement 0xE3 +#define Meta_work 0xE4 char *output_file; int total_args; -static void kill_signal ( int sig ); +static void kill_signal(int sig); -static void kill_signal (int sig) { - exit(0); -} +static void kill_signal(int sig) { exit(0); } -//less than 80 (max 78) char wide, giving a general (concise) overview -static const char* shortHelp_text = -"\n" -"AtomicParlsey sets metadata into MPEG-4 files & derivatives supporting 3 tag\n" -" schemes: iTunes-style, 3GPP assets & ISO defined copyright notifications.\n" -"\n" -"AtomicParlsey quick help for setting iTunes-style metadata into MPEG-4 files.\n" -"\n" -"General usage examples:\n" -" AtomicParsley /path/to.mp4 -T 1\n" -" AtomicParsley /path/to.mp4 -t +\n" -" AtomicParsley /path/to.mp4 --artist \"Me\" --artwork /path/to/art.jpg\n" -" Atomicparsley /path/to.mp4 --albumArtist \"You\" --podcastFlag true\n" -" Atomicparsley /path/to.mp4 --stik \"TV Show\" --advisory explicit\n" -"\n" -"Getting information about the file & tags:\n" -" -T --test Test file for mpeg4-ishness & print atom tree\n" -" -t --textdata Prints tags embedded within the file\n" -" -E --extractPix Extracts pix to the same folder as the mpeg-4 file\n" -"\n" -"Setting iTunes-style metadata tags\n" -" --artist (string) Set the artist tag\n" -" --title (string) Set the title tag\n" -" --album (string) Set the album tag\n" -" --genre (string) Genre tag (see --longhelp for more info)\n" -" --tracknum (num)[/tot] Track number (or track number/total tracks)\n" -" --disk (num)[/tot] Disk number (or disk number/total disks)\n" -" --comment (string) Set the comment tag\n" -" --year (num|UTC) Year tag (see --longhelp for \"Release Date\")\n" -" --lyrics (string) Set lyrics (not subject to 256 byte limit)\n" -" --lyricsFile (/path) Set lyrics to the content of a file\n" -" --composer (string) Set the composer tag\n" -" --copyright (string) Set the copyright tag\n" -" --grouping (string) Set the grouping tag\n" -" --artwork (/path) Set a piece of artwork (jpeg or png only)\n" -" --bpm (number) Set the tempo/bpm\n" -" --albumArtist (string) Set the album artist tag\n" -" --compilation (boolean) Set the compilation flag (true or false)\n" -" --hdvideo (boolean) Set the hdvideo flag (true or false)\n" -" --advisory (string*) Content advisory (*values: 'clean', 'explicit')\n" -" --stik (string*) Sets the iTunes \"stik\" atom (see --longhelp)\n" -" --description (string) Set the description tag\n" -" --longdesc (string) Set the long description tag\n" -" --storedesc (string) Set the store description tag\n" -" --TVNetwork (string) Set the TV Network name\n" -" --TVShowName (string) Set the TV Show name\n" -" --TVEpisode (string) Set the TV episode/production code\n" -" --TVSeasonNum (number) Set the TV Season number\n" -" --TVEpisodeNum (number) Set the TV Episode number\n" -" --podcastFlag (boolean) Set the podcast flag (true or false)\n" -" --category (string) Sets the podcast category\n" -" --keyword (string) Sets the podcast keyword\n" -" --podcastURL (URL) Set the podcast feed URL\n" -" --podcastGUID (URL) Set the episode's URL tag\n" -" --purchaseDate (UTC) Set time of purchase\n" -" --encodingTool (string) Set the name of the encoder\n" -" --encodedBy (string) Set the name of the Person/company who encoded the file\n" -" --apID (string) Set the Account Name\n" -" --cnID (number) Set the iTunes Catalog ID (see --longhelp)\n" -" --geID (number) Set the iTunes Genre ID (see --longhelp)\n" -" --xID (string) Set the vendor-supplied iTunes xID (see --longhelp)\n" -" --gapless (boolean) Set the gapless playback flag\n" -" --contentRating (string*) Set tv/mpaa rating (see -rDNS-help)\n" -"\n" -"Deleting tags\n" -" Set the value to \"\": --artist \"\" --stik \"\" --bpm \"\"\n" -" To delete (all) artwork: --artwork REMOVE_ALL\n" -" manually removal: --manualAtomRemove \"moov.udta.meta.ilst.ATOM\"\n" -"\n" -"More detailed iTunes help is available with AtomicParsley --longhelp\n" -"Setting reverse DNS forms for iTunes files: see --reverseDNS-help\n" -"Setting 3gp assets into 3GPP & derivative files: see --3gp-help\n" -"Setting copyright notices for all files: see --ISO-help\n" -"For file-level options & padding info: see --file-help\n" -"Setting custom private tag extensions: see --uuid-help\n" -"Setting ID3 tags onto mpeg-4 files: see --ID3-help\n" -"\n" -"----------------------------------------------------------------------" -; - -//an expansive, verbose, unconstrained (about 112 char wide) detailing of options -static const char* longHelp_text = -"AtomicParsley help page for setting iTunes-style metadata into MPEG-4 files. \n" -" (3gp help available with AtomicParsley --3gp-help)\n" -" (ISO copyright help available with AtomicParsley --ISO-help)\n" -" (reverse DNS form help available with AtomicParsley --reverseDNS-help)\n" -"Usage: AtomicParsley [mp4FILE]... [OPTION]... [ARGUMENT]... [ [OPTION2]...[ARGUMENT2]...] \n" -"\n" -"example: AtomicParsley /path/to.mp4 -e ~/Desktop/pix\n" -"example: AtomicParsley /path/to.mp4 --podcastURL \"http://www.url.net\" --tracknum 45/356\n" -"example: AtomicParsley /path/to.mp4 --copyright \"\342\204\227 \302\251 2006\"\n" -"example: AtomicParsley /path/to.mp4 --year \"2006-07-27T14:00:43Z\" --purchaseDate timestamp\n" -"example: AtomicParsley /path/to.mp4 --sortOrder artist \"Mighty Dub Cats, The\n" -"------------------------------------------------------------------------------------------------\n" -" Extract any pictures in user data \"covr\" atoms to separate files. \n" -" --extractPix , -E Extract to same folder (basename derived from file).\n" -" --extractPixToPath , -e (/path/basename) Extract to specific path (numbers added to basename).\n" -" example: --e ~/Desktop/SomeText\n" -" gives: SomeText_artwork_1.jpg SomeText_artwork_2.png\n" -" Note: extension comes from embedded image file format\n" -"------------------------------------------------------------------------------------------------\n" -" Tag setting options:\n" -"\n" -" --artist , -a (str) Set the artist tag: \"moov.udta.meta.ilst.\302ART.data\"\n" -" --title , -s (str) Set the title tag: \"moov.udta.meta.ilst.\302nam.data\"\n" -" --album , -b (str) Set the album tag: \"moov.udta.meta.ilst.\302alb.data\"\n" -" --genre , -g (str) Set the genre tag: \"\302gen\" (custom) or \"gnre\" (standard).\n" -" see the standard list with \"AtomicParsley --genre-list\"\n" -" --tracknum , -k (num)[/tot] Set the track number (or track number & total tracks).\n" -" --disk , -d (num)[/tot] Set the disk number (or disk number & total disks).\n" -" --comment , -c (str) Set the comment tag: \"moov.udta.meta.ilst.\302cmt.data\"\n" -" --year , -y (num|UTC) Set the year tag: \"moov.udta.meta.ilst.\302day.data\"\n" -" set with UTC \"2006-09-11T09:00:00Z\" for Release Date\n" -" --lyrics , -l (str) Set the lyrics tag: \"moov.udta.meta.ilst.\302lyr.data\"\n" -" --lyricsFile , (/path) Set the lyrics tag to the content of a file\n" -" --composer , -w (str) Set the composer tag: \"moov.udta.meta.ilst.\302wrt.data\"\n" -" --copyright , -x (str) Set the copyright tag: \"moov.udta.meta.ilst.cprt.data\"\n" -" --grouping , -G (str) Set the grouping tag: \"moov.udta.meta.ilst.\302grp.data\"\n" -" --artwork , -A (/path) Set a piece of artwork (jpeg or png) on \"covr.data\"\n" -" Note: multiple pieces are allowed with more --artwork args\n" -" --bpm , -B (num) Set the tempo/bpm tag: \"moov.udta.meta.ilst.tmpo.data\"\n" -" --albumArtist , -A (str) Set the album artist tag: \"moov.udta.meta.ilst.aART.data\"\n" -" --compilation , -C (bool) Sets the \"cpil\" atom (true or false to delete the atom)\n" -" --hdvideo , -V (bool) Sets the \"hdvd\" atom (true or false to delete the atom)\n" -" --advisory , -y (1of3) Sets the iTunes lyrics advisory ('remove', 'clean', 'explicit') \n" -" --stik , -S (1of7) Sets the iTunes \"stik\" atom (--stik \"remove\" to delete) \n" -" \"Movie\", \"Normal\", \"TV Show\" .... others: \n" -" see the full list with \"AtomicParsley --stik-list\"\n" -" or set in an integer value with --stik value=(num)\n" -" Note: --stik Audiobook will change file extension to '.m4b'\n" -" --description , -p (str) Sets the description on the \"desc\" atom\n" -" --Rating , (str) Sets the Rating on the \"rate\" atom\n" -" --longdesc , -j (str) Sets the long description on the \"ldes\" atom\n" -" --storedesc , (str) Sets the iTunes store description on the \"sdes\" atom\n" -" --TVNetwork , -n (str) Sets the TV Network name on the \"tvnn\" atom\n" -" --TVShowName , -H (str) Sets the TV Show name on the \"tvsh\" atom\n" -" --TVEpisode , -I (str) Sets the TV Episode on \"tven\":\"209\", but it is a string: \"209 Part 1\"\n" -" --TVSeasonNum , -U (num) Sets the TV Season number on the \"tvsn\" atom\n" -" --TVEpisodeNum , -N (num) Sets the TV Episode number on the \"tves\" atom\n" - -" --podcastFlag , -f (bool) Sets the podcast flag (values are \"true\" or \"false\")\n" -" --category , -q (str) Sets the podcast category; typically a duplicate of its genre\n" -" --keyword , -K (str) Sets the podcast keyword; invisible to MacOSX Spotlight\n" -" --podcastURL , -L (URL) Set the podcast feed URL on the \"purl\" atom\n" -" --podcastGUID , -J (URL) Set the episode's URL tag on the \"egid\" atom\n" -" --purchaseDate , -D (UTC) Set Universal Coordinated Time of purchase on a \"purd\" atom\n" -" (use \"timestamp\" to set UTC to now; can be akin to id3v2 TDTG tag)\n" -" --encodingTool , (str) Set the name of the encoder on the \"\302too\" atom\n" -" --encodedBy , (str) Set the name of the Person/company who encoded the file on the \"\302enc\" atom\n" -" --apID , -Y (str) Set the name of the Account Name on the \"apID\" atom\n" -" --cnID , (num) Set iTunes Catalog ID, used for combining SD and HD encodes in iTunes on the \"cnID\" atom\n" -"\n" -" To combine you must set \"hdvd\" atom on one file and must have same \"stik\" on both file\n" -" Must not use \"stik\" of value Home Video(0), use Movie(9)\n" -"\n" -" iTunes Catalog numbers can be obtained by finding the item in the iTunes Store. Once item\n" -" is found in the iTunes Store right click on picture of item and select copy link. Paste this link\n" -" into a document or web browser to display the catalog number ID.\n" -"\n" -" An example link for the video Street Kings is:\n" -" http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewMovie?id=278743714&s=143441\n" -" Here you can see the cnID is 278743714\n" -"\n" -" Alternatively you can use iMDB numbers, however these will not match the iTunes catalog.\n" -"\n" -" --geID , (num) Set iTunes Genre ID. This does not necessarily have to match genre.\n" -" See --genre-movie-id-list and --genre-tv-id-list\n" -"\n" -" --xID , (str) Set iTunes vendor-supplied xID, used to allow iTunes LPs and iTunes Extras to interact \n" -" with other content in your iTunes Library\n" -" --gapless , (bool) Sets the gapless playback flag for a track in a gapless album\n" - -" --sortOrder (type) (str) Sets the sort order string for that type of tag.\n" -" (available types are: \"name\", \"artist\", \"albumartist\",\n" -" \"album\", \"composer\", \"show\")\n" -"\n" -"NOTE: Except for artwork, only 1 of each tag is allowed; artwork allows multiple pieces.\n" -"NOTE: Tags that carry text(str) have a limit of 255 utf8 characters;\n" -"however lyrics and long descriptions have no limit.\n" -"------------------------------------------------------------------------------------------------\n" -" To delete a single atom, set the tag to null (except artwork):\n" -" --artist \"\" --lyrics \"\"\n" -" --artwork REMOVE_ALL \n" -" --metaEnema , -P Douches away every atom under \"moov.udta.meta.ilst\" \n" -" --foobar2000Enema , -2 Eliminates foobar2000's non-compliant so-out-o-spec tagging scheme\n" -" --manualAtomRemove \"some.atom.path\" where some.atom.path can be:\n" -" keys to using manualAtomRemove:\n" -" ilst.ATOM.data or ilst.ATOM target an iTunes-style metadata tag\n" -" ATOM:lang=foo target an atom with this language setting; like 3gp assets\n" -" ATOM.----.name:[foo] target a reverseDNS metadata tag; like iTunNORM\n" -" Note: these atoms show up with 'AP -t' as: Atom \"----\" [foo]\n" -" 'foo' is actually carried on the 'name' atom\n" -" ATOM[x] target an atom with an index other than 1; like trak[2]\n" -" ATOM.uuid=hex-hex-hex-hex targt a uuid atom with the uuid of hex string representation\n" -" examples:\n" -" moov.udta.meta.ilst.----.name:[iTunNORM] moov.trak[3].cprt:lang=urd\n" -" moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740\n" -"------------------------------------------------------------------------------------------------\n" - -#if defined (DARWIN_PLATFORM) -" Environmental Variables (affecting picture placement)\n" -"\n" -" set PIC_OPTIONS in your shell to set these flags; preferences are separated by colons (:)\n" -"\n" -" MaxDimensions=num (default: 0; unlimited); sets maximum pixel dimensions\n" -" DPI=num (default: 72); sets dpi\n" -" MaxKBytes=num (default: 0; unlimited); maximum kilobytes for file (jpeg only)\n" -" AddBothPix=bool (default: false); add original & converted pic (for archival purposes)\n" -" AllPixJPEG | AllPixPNG =bool (default: false); force conversion to a specific picture format\n" -" SquareUp (include to square images to largest dimension, allows an [ugly] 160x1200->1200x1200)\n" -" removeTempPix (include to delete temp pic files created when resizing images after tagging)\n" -" ForceHeight=num (must also specify width, below) force image pixel height\n" -" ForceWidth=num (must also specify height, above) force image pixel width\n" -"\n" -" Examples: (bash-style)\n" -" export PIC_OPTIONS=\"MaxDimensions=400:DPI=72:MaxKBytes=100:AddBothPix=true:AllPixJPEG=true\"\n" -" export PIC_OPTIONS=\"SquareUp:removeTempPix\"\n" -" export PIC_OPTIONS=\"ForceHeight=999:ForceWidth=333:removeTempPix\"\n" -"------------------------------------------------------------------------------------------------\n" +// less than 80 (max 78) char wide, giving a general (concise) overview +static const char *shortHelp_text = + "\n" + "AtomicParsley sets metadata into MPEG-4 files & derivatives supporting 3 " + "tag\n" + " schemes: iTunes-style, 3GPP assets & ISO defined copyright " + "notifications.\n" + "\n" + "AtomicParsley quick help for setting iTunes-style metadata into MPEG-4 " + "files.\n" + "\n" + "General usage examples:\n" + " AtomicParsley /path/to.mp4 -T 1\n" + " AtomicParsley /path/to.mp4 -t +\n" + " AtomicParsley /path/to.mp4 --artist \"Me\" --artwork /path/to/art.jpg\n" + " Atomicparsley /path/to.mp4 --albumArtist \"You\" --podcastFlag true\n" + " Atomicparsley /path/to.mp4 --stik \"TV Show\" --advisory explicit\n" + "\n" + "Getting information about the file & tags:\n" + " -T --test Test file for mpeg4-ishness & print atom tree\n" + " -t --textdata Prints tags embedded within the file\n" + " -E --extractPix Extracts pix to the same folder as the mpeg-4 file\n" + "\n" + "Setting iTunes-style metadata tags\n" + " --artist (string) Set the artist tag\n" + " --title (string) Set the title tag\n" + " --album (string) Set the album tag\n" + " --genre (string) Genre tag (see --longhelp for more info)\n" + " --tracknum (num)[/tot] Track number (or track number/total " + "tracks)\n" + " --disk (num)[/tot] Disk number (or disk number/total disks)\n" + " --comment (string) Set the comment tag\n" + " --year (num|UTC) Year tag (see --longhelp for \"Release " + "Date\")\n" + " --lyrics (string) Set lyrics (not subject to 256 byte limit)\n" + " --lyricsFile (/path) Set lyrics to the content of a file\n" + " --composer (string) Set the composer tag\n" + " --copyright (string) Set the copyright tag\n" + " --grouping (string) Set the grouping tag\n" + " --artwork (/path) Set a piece of artwork (jpeg or png only)\n" + " --bpm (number) Set the tempo/bpm\n" + " --albumArtist (string) Set the album artist tag\n" + " --compilation (boolean) Set the compilation flag (true or false)\n" + " --hdvideo (number) Set the hdvideo flag to one of:\n" + " false or 0 for standard definition\n" + " true or 1 for 720p\n" + " 2 for 1080p\n" + " --advisory (string*) Content advisory (*values: 'clean', " + "'explicit')\n" + " --stik (string*) Sets the iTunes \"stik\" atom (see " + "--longhelp)\n" + " --description (string) Set the description tag\n" + " --longdesc (string) Set the long description tag\n" + " --storedesc (string) Set the store description tag\n" + " --TVNetwork (string) Set the TV Network name\n" + " --TVShowName (string) Set the TV Show name\n" + " --TVEpisode (string) Set the TV episode/production code\n" + " --TVSeasonNum (number) Set the TV Season number\n" + " --TVEpisodeNum (number) Set the TV Episode number\n" + " --podcastFlag (boolean) Set the podcast flag (true or false)\n" + " --category (string) Sets the podcast category\n" + " --keyword (string) Sets the podcast keyword\n" + " --podcastURL (URL) Set the podcast feed URL\n" + " --podcastGUID (URL) Set the episode's URL tag\n" + " --purchaseDate (UTC) Set time of purchase\n" + " --encodingTool (string) Set the name of the encoder\n" + " --encodedBy (string) Set the name of the Person/company who " + "encoded the file\n" + " --apID (string) Set the Account Name\n" + " --cnID (number) Set the iTunes Catalog ID (see --longhelp)\n" + " --geID (number) Set the iTunes Genre ID (see --longhelp)\n" + " --xID (string) Set the vendor-supplied iTunes xID (see " + "--longhelp)\n" + " --gapless (boolean) Set the gapless playback flag\n" + " --contentRating (string*) Set tv/mpaa rating (see -rDNS-help)\n" + "\n" + "Deleting tags\n" + " Set the value to \"\": --artist \"\" --stik \"\" --bpm \"\"\n" + " To delete (all) artwork: --artwork REMOVE_ALL\n" + " manually removal: --manualAtomRemove " + "\"moov.udta.meta.ilst.ATOM\"\n" + "\n" + "More detailed iTunes help is available with AtomicParsley --longhelp\n" + "Setting reverse DNS forms for iTunes files: see --reverseDNS-help\n" + "Setting 3gp assets into 3GPP & derivative files: see --3gp-help\n" + "Setting copyright notices for all files: see --ISO-help\n" + "For file-level options & padding info: see --file-help\n" + "Setting custom private tag extensions: see --uuid-help\n" + "Setting ID3 tags onto mpeg-4 files: see --ID3-help\n" + "\n" + "----------------------------------------------------------------------"; + +// an expansive, verbose, unconstrained (about 112 char wide) detailing of +// options +static const char *longHelp_text = + "AtomicParsley help page for setting iTunes-style metadata into MPEG-4 " + "files. \n" + " (3gp help available with AtomicParsley --3gp-help)\n" + " (ISO copyright help available with AtomicParsley --ISO-help)\n" + " (reverse DNS form help available with AtomicParsley " + "--reverseDNS-help)\n" + "Usage: AtomicParsley [mp4FILE]... [OPTION]... [ARGUMENT]... [ " + "[OPTION2]...[ARGUMENT2]...] \n" + "\n" + "example: AtomicParsley /path/to.mp4 -e ~/Desktop/pix\n" + "example: AtomicParsley /path/to.mp4 --podcastURL \"http://www.url.net\" " + "--tracknum 45/356\n" + "example: AtomicParsley /path/to.mp4 --copyright \"\342\204\227 \302\251 " + "2006\"\n" + "example: AtomicParsley /path/to.mp4 --year \"2006-07-27T14:00:43Z\" " + "--purchaseDate timestamp\n" + "example: AtomicParsley /path/to.mp4 --sortOrder artist \"Mighty Dub Cats, " + "The\n" + "--------------------------------------------------------------------------" + "----------------------\n" + " Extract any pictures in user data \"covr\" atoms to separate files. \n" + " --extractPix , -E Extract to same folder " + "(basename derived from file).\n" + " --extractPixToPath , -e (/path/basename) Extract to specific path " + "(numbers added to basename).\n" + " example: --e " + "~/Desktop/SomeText\n" + " gives: " + "SomeText_artwork_1.jpg SomeText_artwork_2.png\n" + " Note: extension comes from " + "embedded image file format\n" + "--------------------------------------------------------------------------" + "----------------------\n" + " Tag setting options:\n" + "\n" + " --artist , -a (str) Set the artist tag: " + "\"moov.udta.meta.ilst.\302\251ART.data\"\n" + " --title , -s (str) Set the title tag: " + "\"moov.udta.meta.ilst.\302\251nam.data\"\n" + " --album , -b (str) Set the album tag: " + "\"moov.udta.meta.ilst.\302\251alb.data\"\n" + " --genre , -g (str) Set the genre tag: \"\302\251gen\" " + "(custom) or \"gnre\" (standard).\n" + " see the standard list with " + "\"AtomicParsley --genre-list\"\n" + " --tracknum , -k (num)[/tot] Set the track number (or track " + "number & total tracks).\n" + " --disk , -d (num)[/tot] Set the disk number (or disk " + "number & total disks).\n" + " --comment , -c (str) Set the comment tag: " + "\"moov.udta.meta.ilst.\302\251cmt.data\"\n" + " --year , -y (num|UTC) Set the year tag: " + "\"moov.udta.meta.ilst.\302\251day.data\"\n" + " set with UTC " + "\"2006-09-11T09:00:00Z\" for Release Date\n" + " --lyrics , -l (str) Set the lyrics tag: " + "\"moov.udta.meta.ilst.\302\251lyr.data\"\n" + " --lyricsFile , (/path) Set the lyrics tag to the content " + "of a file\n" + " --composer , -w (str) Set the composer tag: " + "\"moov.udta.meta.ilst.\302\251wrt.data\"\n" + " --copyright , -x (str) Set the copyright tag: " + "\"moov.udta.meta.ilst.cprt.data\"\n" + " --grouping , -G (str) Set the grouping tag: " + "\"moov.udta.meta.ilst.\302\251grp.data\"\n" + " --artwork , -A (/path) Set a piece of artwork (jpeg or " + "png) on \"covr.data\"\n" + " Note: multiple pieces are " + "allowed with more --artwork args\n" + " --bpm , -B (num) Set the tempo/bpm tag: " + "\"moov.udta.meta.ilst.tmpo.data\"\n" + " --albumArtist , -A (str) Set the album artist tag: " + "\"moov.udta.meta.ilst.aART.data\"\n" + " --compilation , -C (bool) Sets the \"cpil\" atom (true or " + "false to delete the atom)\n" + " --hdvideo , -V (bool) Sets the \"hdvd\" atom (true or " + "false to delete the atom)\n" + " --advisory , -y (1of3) Sets the iTunes lyrics advisory " + "('remove', 'clean', 'explicit') \n" + " --stik , -S (1of7) Sets the iTunes \"stik\" atom " + "(--stik \"remove\" to delete) \n" + " \"Movie\", \"Normal\", \"TV " + "Show\" .... others: \n" + " see the full list with " + "\"AtomicParsley --stik-list\"\n" + " or set in an integer value " + "with --stik value=(num)\n" + " Note: --stik Audiobook will change " + "file extension to '.m4b'\n" + " --description , -p (str) Sets the description on the " + "\"desc\" atom\n" + " --Rating , (str) Sets the Rating on the \"rate\" " + "atom\n" + " --longdesc , -j (str) Sets the long description on the " + "\"ldes\" atom\n" + " --storedesc , (str) Sets the iTunes store description " + "on the \"sdes\" atom\n" + " --TVNetwork , -n (str) Sets the TV Network name on the " + "\"tvnn\" atom\n" + " --TVShowName , -H (str) Sets the TV Show name on the " + "\"tvsh\" atom\n" + " --TVEpisode , -I (str) Sets the TV Episode on " + "\"tven\":\"209\", but it is a string: \"209 Part 1\"\n" + " --TVSeasonNum , -U (num) Sets the TV Season number on the " + "\"tvsn\" atom\n" + " --TVEpisodeNum , -N (num) Sets the TV Episode number on the " + "\"tves\" atom\n" + + " --podcastFlag , -f (bool) Sets the podcast flag (values are " + "\"true\" or \"false\")\n" + " --category , -q (str) Sets the podcast category; " + "typically a duplicate of its genre\n" + " --keyword , -K (str) Sets the podcast keyword; invisible " + "to MacOSX Spotlight\n" + " --podcastURL , -L (URL) Set the podcast feed URL on the " + "\"purl\" atom\n" + " --podcastGUID , -J (URL) Set the episode's URL tag on the " + "\"egid\" atom\n" + " --purchaseDate , -D (UTC) Set Universal Coordinated Time of " + "purchase on a \"purd\" atom\n" + " (use \"timestamp\" to set UTC to " + "now; can be akin to id3v2 TDTG tag)\n" + " --encodingTool , (str) Set the name of the encoder on the " + "\"\302\251too\" atom\n" + " --encodedBy , (str) Set the name of the Person/company " + "who encoded the file on the \"\302\251enc\" atom\n" + " --apID , -Y (str) Set the name of the Account Name on " + "the \"apID\" atom\n" + " --cnID , (num) Set iTunes Catalog ID, used for " + "combining SD and HD encodes in iTunes on the \"cnID\" atom\n" + "\n" + " To combine you must set \"hdvd\" " + "atom on one file and must have same \"stik\" on both file\n" + " Must not use \"stik\" of value Home " + "Video(0), use Movie(9)\n" + "\n" + " iTunes Catalog numbers can be " + "obtained by finding the item in the iTunes Store. Once item\n" + " is found in the iTunes Store right " + "click on picture of item and select copy link. Paste this link\n" + " into a document or web browser to " + "display the catalog number ID.\n" + "\n" + " An example link for the video " + "Street Kings is:\n" + " " + "http://itunes.apple.com/WebObjects/MZStore.woa/wa/" + "viewMovie?id=278743714&s=143441\n" + " Here you can see the cnID is " + "278743714\n" + "\n" + " Alternatively you can use iMDB " + "numbers, however these will not match the iTunes catalog.\n" + "\n" + " --geID , (num) Set iTunes Genre ID. This does not " + "necessarily have to match genre.\n" + " See --genre-movie-id-list and " + "--genre-tv-id-list\n" + "\n" + " --xID , (str) Set iTunes vendor-supplied xID, " + "used to allow iTunes LPs and iTunes Extras to interact \n" + " with other content in your " + "iTunes Library\n" + " --gapless , (bool) Sets the gapless playback flag for " + "a track in a gapless album\n" + + " --sortOrder (type) (str) Sets the sort order string for that " + "type of tag.\n" + " (available types are: \"name\", " + "\"artist\", \"albumartist\",\n" + " \"album\", \"composer\", " + "\"show\")\n" + "\n" + "NOTE: Except for artwork, only 1 of each tag is allowed; artwork allows " + "multiple pieces.\n" + "NOTE: Tags that carry text(str) have a limit of 255 utf8 characters;\n" + "however lyrics and long descriptions have no limit.\n" + "--------------------------------------------------------------------------" + "----------------------\n" + " To delete a single atom, set the tag to null (except artwork):\n" + " --artist \"\" --lyrics \"\"\n" + " --artwork REMOVE_ALL \n" + " --metaEnema , -P Douches away every atom under " + "\"moov.udta.meta.ilst\" \n" + " --foobar2000Enema , -2 Eliminates foobar2000's " + "non-compliant so-out-o-spec tagging scheme\n" + " --manualAtomRemove \"some.atom.path\" where some.atom.path can be:\n" + " keys to using manualAtomRemove:\n" + " ilst.ATOM.data or ilst.ATOM target an iTunes-style metadata tag\n" + " ATOM:lang=foo target an atom with this language " + "setting; like 3gp assets\n" + " ATOM.----.name:[foo] target a reverseDNS metadata tag; " + "like iTunNORM\n" + " Note: these atoms show up with 'AP " + "-t' as: Atom \"----\" [foo]\n" + " 'foo' is actually carried on the " + "'name' atom\n" + " ATOM[x] target an atom with an index other " + "than 1; like trak[2]\n" + " ATOM.uuid=hex-hex-hex-hex targt a uuid atom with the uuid of " + "hex string representation\n" + " examples:\n" + " moov.udta.meta.ilst.----.name:[iTunNORM] " + "moov.trak[3].cprt:lang=urd\n" + " moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740\n" + "--------------------------------------------------------------------------" + "----------------------\n" + +#if defined(__APPLE__) + " Environmental Variables (affecting picture placement)\n" + "\n" + " set PIC_OPTIONS in your shell to set these flags; preferences are " + "separated by colons (:)\n" + "\n" + " MaxDimensions=num (default: 0; unlimited); sets maximum pixel " + "dimensions\n" + " DPI=num (default: 72); sets dpi\n" + " MaxKBytes=num (default: 0; unlimited); maximum kilobytes for file " + "(jpeg only)\n" + " AddBothPix=bool (default: false); add original & converted pic (for " + "archival purposes)\n" + " AllPixJPEG | AllPixPNG =bool (default: false); force conversion to a " + "specific picture format\n" + " SquareUp (include to square images to largest dimension, allows " + "an [ugly] 160x1200->1200x1200)\n" + " removeTempPix (include to delete temp pic files created when " + "resizing images after tagging)\n" + " ForceHeight=num (must also specify width, below) force image pixel " + "height\n" + " ForceWidth=num (must also specify height, above) force image pixel " + "width\n" + "\n" + " Examples: (bash-style)\n" + " export " + "PIC_OPTIONS=\"MaxDimensions=400:DPI=72:MaxKBytes=100:AddBothPix=true:" + "AllPixJPEG=true\"\n" + " export PIC_OPTIONS=\"SquareUp:removeTempPix\"\n" + " export PIC_OPTIONS=\"ForceHeight=999:ForceWidth=333:removeTempPix\"\n" + "--------------------------------------------------------------------------" + "----------------------\n" #endif -; + ; -static const char* fileLevelHelp_text = -"AtomicParsley help page for general & file level options.\n" -#if defined (_WIN32) +static const char *fileLevelHelp_text = + "AtomicParsley help page for general & file level options.\n" +#if defined(_WIN32) #ifndef __CYGWIN__ -" Note: you can change the input/output behavior to raw 8-bit utf8 if the program name\n" -" is appended with \"-utf8\". AtomicParsley-utf8.exe will have problems with files/\n" -" folders with unicode characters in given paths.\n" + " Note: you can change the input/output behavior to raw 8-bit utf8 if the " + "program name\n" + " is appended with \"-utf8\". AtomicParsley-utf8.exe will have " + "problems with files/\n" + " folders with unicode characters in given paths.\n" #else -" Note: you can change the input/output behavior for MCDI functions to raw 8-bit utf8\n" -" if the program name is appended with \"-utf8\".\n" + " Note: you can change the input/output behavior for MCDI functions to " + "raw 8-bit utf8\n" + " if the program name is appended with \"-utf8\".\n" #endif -"\n" + "\n" #endif -"------------------------------------------------------------------------------------------------\n" -" Atom reading services:\n" -"\n" -" --test , -T Tests file to see if it is a valid MPEG-4 file.\n" -" Prints out the hierarchical atom tree.\n" -" -T 1 Supplemental track level info with \"-T 1\"\n" -" -T +dates Track level with creation/modified dates\n" -"\n" -" --textdata , -t print user data text metadata relevant to brand (inc. # of any pics).\n" -" -t + show supplemental info like free space, available padding, user data\n" -" length & media data length\n" -" -t 1 show all textual metadata (disregards brands, shows track copyright)\n" -"\n" -" --brands show the major & minor brands for the file & available tagging schemes\n" -"\n" -"------------------------------------------------------------------------------------------------\n" -" File services:\n" -"\n" -" --freefree [num] , Remove \"free\" atoms which only act as filler in the file\n" -" ?(num)? - optional integer argument to delete 'free's to desired level\n" -"\n" -" NOTE 1: levels begin at level 1 aka file level.\n" -" NOTE 2: Level 0 (which doesn't exist) deletes level 1 atoms that pre-\n" -" cede 'moov' & don't serve as padding. Typically, such atoms\n" -" are created by libmp4ff or libmp4v2 as a byproduct of tagging.\n" -" NOTE 3: When padding falls below MIN_PAD (typically zero), a default\n" -" amount of padding (typically 2048 bytes) will be added. To\n" -" achieve absolutely 0 bytes 'free' space with --freefree, set\n" -" DEFAULT_PAD to 0 via the AP_PADDING mechanism (see below).\n" -"\n" -" --preventOptimizing Prevents reorganizing the file to have file metadata before media data.\n" -" iTunes/Quicktime have so far *always* placed metadata first; many 3rd\n" -" party utilities do not (preventing streaming to the web, AirTunes, iTV).\n" -" Used in conjunction with --overWrite, files with metadata at the end\n" -" (most ffmpeg produced files) can have their tags rapidly updated without\n" -" requiring a full rewrite. Note: this does not un-optimize a file.\n" -" Note: this option will be canceled out if used with the --freefree option\n" -"\n" -" --metaDump Dumps out 'moov.udta' metadata out to a new file next to original\n" -" (for diagnostic purposes, please remove artwork before sending)\n" -" --output , -o (/path) Specify the filename of tempfile (voids overWrite)\n" -" --overWrite , -W Writes to temp file; deletes original, renames temp to original\n" -" If possible, padding will be used to update without a full rewrite.\n" -"\n" -#if defined (_WIN32) -" --preserveTime Will overwrite the original file in place (--overWrite forced),\n" -" but will also keep the original file's timestamps intact.\n" -"\n" + "--------------------------------------------------------------------------" + "----------------------\n" + " Atom reading services:\n" + "\n" + " --test , -T Tests file to see if it is a valid " + "MPEG-4 file.\n" + " Prints out the hierarchical atom " + "tree.\n" + " -T 1 Supplemental track level info with " + "\"-T 1\"\n" + " -T +dates Track level with creation/modified " + "dates\n" + "\n" + " --textdata , -t print user data text metadata relevant to " + "brand (inc. # of any pics).\n" + " -t + show supplemental info like free space, " + "available padding, user data\n" + " length & media data length\n" + " -t 1 show all textual metadata (disregards " + "brands, shows track copyright)\n" + "\n" + " --brands show the major & minor brands for the " + "file & available tagging schemes\n" + "\n" + "--------------------------------------------------------------------------" + "----------------------\n" + " File services:\n" + "\n" + " --freefree [num] , Remove \"free\" atoms which only " + "act as filler in the file\n" + " ?(num)? - optional integer argument " + "to delete 'free's to desired level\n" + "\n" + " NOTE 1: levels begin at level 1 aka " + "file level.\n" + " NOTE 2: Level 0 (which doesn't " + "exist) deletes level 1 atoms that pre-\n" + " cede 'moov' & don't serve " + "as padding. Typically, such atoms\n" + " are created by libmp4ff or " + "libmp4v2 as a byproduct of tagging.\n" + " NOTE 3: When padding falls below " + "MIN_PAD (typically zero), a default\n" + " amount of padding " + "(typically 2048 bytes) will be added. To\n" + " achieve absolutely 0 bytes " + "'free' space with --freefree, set\n" + " DEFAULT_PAD to 0 via the " + "AP_PADDING mechanism (see below).\n" + "\n" + " --preventOptimizing Prevents reorganizing the file to " + "have file metadata before media data.\n" + " iTunes/Quicktime have so far " + "*always* placed metadata first; many 3rd\n" + " party utilities do not (preventing " + "streaming to the web, AirTunes, iTV).\n" + " Used in conjunction with " + "--overWrite, files with metadata at the end\n" + " (most ffmpeg produced files) can " + "have their tags rapidly updated without\n" + " requiring a full rewrite. Note: " + "this does not un-optimize a file.\n" + " Note: this option will be canceled " + "out if used with the --freefree option\n" + "\n" + " --metaDump Dumps out 'moov.udta' metadata out " + "to a new file next to original\n" + " (for diagnostic purposes, " + "please remove artwork before sending)\n" + " --output , -o (/path) Specify the filename of tempfile " + "(voids overWrite)\n" + " --overWrite , -W Writes to temp file; deletes " + "original, renames temp to original\n" + " If possible, padding will be used " + "to update without a full rewrite.\n" + "\n" +#if defined(_WIN32) + " --preserveTime Will overwrite the original file in " + "place (--overWrite forced),\n" + " but will also keep the original " + "file's timestamps intact.\n" + "\n" #endif -" --DeepScan Parse areas of the file that are normally skipped (must be the 3rd arg)\n" -" --iPod-uuid (num) Place the ipod-required uuid for higher resolution avc video files\n" -" Currently, the only number used is 1200 - the maximum number of macro-\n" -" blocks allowed by the higher resolution iPod setting.\n" -" NOTE: this requires the \"--DeepScan\" option as the 3rd cli argument\n" -" NOTE2: only works on the first avc video track, not all avc tracks\n" -"\n" -"Examples: \n" -" --freefree 0 (deletes all top-level non-padding atoms preceding 'mooov') \n" -" --freefree 1 (deletes all non-padding atoms at the top most level) \n" -" --output ~/Desktop/newfile.mp4\n" -" AP /path/to/file.m4v --DeepScan --iPod-uuid 1200\n" - -"------------------------------------------------------------------------------------------------\n" -" Padding & 'free' atoms:\n" -"\n" -" A special type of atom called a 'free' atom is used for padding (all 'free' atoms contain NULL space).\n" -" When changes need to occur, these 'free' atom are used. They grows or shink, but the relative locations\n" -" of certain other atoms (stco/mdat) remain the same. If there is no 'free' space, a full rewrite will occur.\n" -" The locations of 'free' atom(s) that AP can use as padding must be follow 'moov.udta' & come before 'mdat'.\n" -" A 'free' preceding 'moov' or following 'mdat' won't be used as padding for example. \n" -"\n" -" Set the shell variable AP_PADDING with these values, separated by colons to alter padding behavior:\n" -"\n" -" DEFAULT_PADDING= - the amount of padding added if the minimum padding is non-existant in the file\n" -" default = 2048\n" -" MIN_PAD= - the minimum padding present before more padding will be added\n" -" default = 0\n" -" MAX_PAD= - the maximum allowable padding; excess padding will be eliminated\n" -" default = 5000\n" -"\n" -" If you use --freefree to eliminate 'free' atoms from the file, the DEFAULT_PADDING amount will still be\n" -" added to any newly written files. Set DEFAULT_PADDING=0 to prevent any 'free' padding added at rewrite.\n" -" You can set MIN_PAD to be assured that at least that amount of padding will be present - similarly,\n" -" MAX_PAD limits any excessive amount of padding. All 3 options will in all likelyhood produce a full\n" -" rewrite of the original file. Another case where a full rewrite will occur is when the original file\n" -" is not optimized and has 'mdat' preceding 'moov'.\n" -"\n" -#if defined (_WIN32) && !defined (__CYGWIN__) -"Examples:\n" -" c:> SET AP_PADDING=\"DEFAULT_PAD=0\" or c:> SET AP_PADDING=\"DEFAULT_PAD=3128\"\n" -" c:> SET AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n" + " --DeepScan Parse areas of the file that are " + "normally skipped (must be the 3rd arg)\n" + " --iPod-uuid (num) Place the ipod-required uuid for " + "higher resolution avc video files\n" + " Currently, the only number used is " + "1200 - the maximum number of macro-\n" + " blocks allowed by the higher " + "resolution iPod setting.\n" + " NOTE: this requires the " + "\"--DeepScan\" option as the 3rd cli argument\n" + " NOTE2: only works on the first avc " + "video track, not all avc tracks\n" + "\n" + "Examples: \n" + " --freefree 0 (deletes all top-level non-padding atoms preceding " + "'mooov') \n" + " --freefree 1 (deletes all non-padding atoms at the top most " + "level) \n" + " --output ~/Desktop/newfile.mp4\n" + " AP /path/to/file.m4v --DeepScan --iPod-uuid 1200\n" + + "--------------------------------------------------------------------------" + "----------------------\n" + " Padding & 'free' atoms:\n" + "\n" + " A special type of atom called a 'free' atom is used for padding (all " + "'free' atoms contain NULL space).\n" + " When changes need to occur, these 'free' atom are used. They grows or " + "shink, but the relative locations\n" + " of certain other atoms (stco/mdat) remain the same. If there is no " + "'free' space, a full rewrite will occur.\n" + " The locations of 'free' atom(s) that AP can use as padding must be " + "follow 'moov.udta' & come before 'mdat'.\n" + " A 'free' preceding 'moov' or following 'mdat' won't be used as padding " + "for example. \n" + "\n" + " Set the shell variable AP_PADDING with these values, separated by " + "colons to alter padding behavior:\n" + "\n" + " DEFAULT_PADDING= - the amount of padding added if the minimum padding " + "is non-existant in the file\n" + " default = 2048\n" + " MIN_PAD= - the minimum padding present before more padding " + "will be added\n" + " default = 0\n" + " MAX_PAD= - the maximum allowable padding; excess padding will " + "be eliminated\n" + " default = 5000\n" + "\n" + " If you use --freefree to eliminate 'free' atoms from the file, the " + "DEFAULT_PADDING amount will still be\n" + " added to any newly written files. Set DEFAULT_PADDING=0 to prevent any " + "'free' padding added at rewrite.\n" + " You can set MIN_PAD to be assured that at least that amount of padding " + "will be present - similarly,\n" + " MAX_PAD limits any excessive amount of padding. All 3 options will in " + "all likelyhood produce a full\n" + " rewrite of the original file. Another case where a full rewrite will " + "occur is when the original file\n" + " is not optimized and has 'mdat' preceding 'moov'.\n" + "\n" +#if defined(_WIN32) && !defined(__CYGWIN__) + "Examples:\n" + " c:> SET AP_PADDING=\"DEFAULT_PAD=0\" or c:> SET " + "AP_PADDING=\"DEFAULT_PAD=3128\"\n" + " c:> SET AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n" #else -"Examples (bash style):\n" -" $ export AP_PADDING=\"DEFAULT_PAD=0\" or $ export AP_PADDING=\"DEFAULT_PAD=3128\"\n" -" $ export AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n" + "Examples (bash style):\n" + " $ export AP_PADDING=\"DEFAULT_PAD=0\" or $ export " + "AP_PADDING=\"DEFAULT_PAD=3128\"\n" + " $ export AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n" #endif -"\n" -"Note: while AtomicParsley is still in the beta stage, the original file will always remain untouched - \n" -" unless given the --overWrite flag when if possible, utilizing available padding to update tags\n" -" will be tried (falling back to a full rewrite if changes are greater than the found padding).\n" -"----------------------------------------------------------------------------------------------------\n" -" iTunes 7 & Gapless playback:\n" -"\n" -" iTunes 7 adds NULL space at the ends of files (filled with zeroes). It is possble this is how iTunes\n" -" implements gapless playback - perhaps not. In any event, with AtomicParsley you can choose to preserve\n" -" that NULL space, or you can eliminate its presence (typically around 2,000 bytes). The default behavior\n" -" is to preserve it - if it is present at all. You can choose to eliminate it by setting the environ-\n" -" mental preference for AP_PADDING to have DEFAULT_PAD=0\n" -"\n" -#if defined (_WIN32) && !defined (__CYGWIN__) -"Example:\n" -" c:> SET AP_PADDING=\"DEFAULT_PAD=0\"\n" + "\n" + "Note: while AtomicParsley is still in the beta stage, the original file " + "will always remain untouched - \n" + " unless given the --overWrite flag when if possible, utilizing " + "available padding to update tags\n" + " will be tried (falling back to a full rewrite if changes are " + "greater than the found padding).\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " iTunes 7 & Gapless playback:\n" + "\n" + " iTunes 7 adds NULL space at the ends of files (filled with zeroes). It " + "is possble this is how iTunes\n" + " implements gapless playback - perhaps not. In any event, with " + "AtomicParsley you can choose to preserve\n" + " that NULL space, or you can eliminate its presence (typically around " + "2,000 bytes). The default behavior\n" + " is to preserve it - if it is present at all. You can choose to eliminate " + "it by setting the environ-\n" + " mental preference for AP_PADDING to have DEFAULT_PAD=0\n" + "\n" +#if defined(_WIN32) && !defined(__CYGWIN__) + "Example:\n" + " c:> SET AP_PADDING=\"DEFAULT_PAD=0\"\n" #else -"Example (bash style):\n" -" $ export AP_PADDING=\"DEFAULT_PAD=0\"\n" + "Example (bash style):\n" + " $ export AP_PADDING=\"DEFAULT_PAD=0\"\n" #endif -"----------------------------------------------------------------------------------------------------\n" -; + "--------------------------------------------------------------------------" + "--------------------------\n"; -//detailed options for 3gp branded files -static const char* _3gpHelp_text = -"AtomicParsley 3gp help page for setting 3GPP-style metadata.\n" -"----------------------------------------------------------------------------------------------------\n" -" 3GPP text tags can be encoded in either UTF-8 (default input encoding) or UTF-16 (converted from UTF-8)\n" -" Many 3GPP text tags can be set for a desired language by a 3-letter-lowercase code (default is \"eng\").\n" -" For tags that support the language attribute (all except year), more than one tag of the same name\n" -" (3 titles for example) differing in the language code is supported.\n" -"\n" -" iTunes-style metadata is not supported by the 3GPP TS 26.244 version 6.4.0 Release 6 specification.\n" -" 3GPP asset tags can be set at movie level or track level & are set in a different hierarchy: moov.udta \n" -" if at movie level (versus iTunes moov.udta.meta.ilst). Other 3rd party utilities may allow setting\n" -" iTunes-style metadata in 3gp files. When a 3gp file is detected (file extension doesn't matter), only\n" -" 3gp spec-compliant metadata will be read & written.\n" -"\n" -" Note1: there are a number of different 'brands' that 3GPP files come marked as. Some will not be \n" -" supported by AtomicParsley due simply to them being unknown and untested. You can compile your\n" -" own AtomicParsley to evaluate it by adding the hex code into the source of APar_IdentifyBrand.\n" -"\n" -" Note2: There are slight accuracy discrepancies in location's fixed point decimals set and retrieved.\n" -"\n" -" Note3: QuickTime Player can see a limited subset of these tags, but only in 1 language & there seems to\n" -" be an issue with not all unicode text displaying properly. This is an issue withing QuickTime -\n" -" the exact same text (in utf8) displays properly in an MPEG-4 file. Some languages can also display\n" -" more glyphs than others.\n" -"\n" -"----------------------------------------------------------------------------------------------------\n" -" Tag setting options (default user data area is movie level; default lang is 'eng'; default encoding is UTF8):\n" -" required arguments are in (parentheses); optional arguments are in [brackets]\n" -"\n" -" --3gp-title (str) [lang=3str] [UTF16] [area] ......... Set a 3gp media title tag\n" -" --3gp-author (str) [lang=3str] [UTF16] [area] ......... Set a 3gp author of the media tag\n" -" --3gp-performer (str) [lang=3str] [UTF16] [area] ......... Set a 3gp performer or artist tag\n" -" --3gp-genre (str) [lang=3str] [UTF16] [area] ......... Set a 3gp genre asset tag\n" -" --3gp-description (str) [lang=3str] [UTF16] [area] ......... Set a 3gp description or caption tag\n" -" --3gp-copyright (str) [lang=3str] [UTF16] [area] ......... Set a 3gp copyright notice tag*\n" -"\n" -" --3gp-album (str) [lang=3str] [UTF16] [trknum=int] [area] Set a 3gp album tag (& opt. tracknum)\n" -" --3gp-year (int) [area] ........................... Set a 3gp recording year tag (4 digit only)\n" -"\n" -" --3gp-rating (str) [entity=4str] [criteria=4str] [lang=3str] [UTF16] [area] Rating tag\n" -" --3gp-classification (str) [entity=4str] [index=int] [lang=3str] [UTF16] [area] Classification\n" -"\n" -" --3gp-keyword (str) [lang=3str] [UTF16] [area] Format of str: 'keywords=word1,word2,word3,word4'\n" -"\n" -" --3gp-location (str) [lang=3str] [UTF16] [area] Set a 3gp location tag (default: Central Park)\n" -" [longitude=fxd.pt] [latitude=fxd.pt] [altitude=fxd.pt]\n" -" [role=str] [body=str] [notes=str]\n" -" fxd.pt values are decimal coordinates (55.01209, 179.25W, 63)\n" -" 'role=' values: 'shooting location', 'real location', 'fictional location'\n" -" a negative value in coordinates will be seen as a cli flag\n" -" append 'S', 'W' or 'B': lat=55S, long=90.23W, alt=90.25B\n" -"\n" -" [area] can be \"movie\", \"track\" or \"track=num\" where 'num' is the number of the track. If not specificied,\n" -" assets will be placed at movie level. The \"track\" option sets the asset across all available tracks\n" -"\n" -"Note1: '4str' = a 4 letter string like \"PG13\"; 3str is a 3 letter string like \"eng\"; int is an integer\n" -"Note2: List all languages for '3str' with \"AtomicParsley --language-list (unknown languages become \"und\")\n" -"*Note3: The 3gp copyright asset can potentially be altered by using the --ISO-copyright setting.\n" - -"----------------------------------------------------------------------------------------------------\n" -"Usage: AtomicParsley [3gpFILE] --option [argument] [optional_arguments] [ --option2 [argument2]...] \n" -"\n" -"example: AtomicParsley /path/to.3gp -t \n" -"example: AtomicParsley /path/to.3gp -T 1 \n" -"example: Atomicparsley /path/to.3gp --3gp-performer \"Enjoy Yourself\" lang=pol UTF16\n" -"example: Atomicparsley /path/to.3gp --3gp-year 2006 --3gp-album \"White Label\" track=8 lang=fra\n" -"example: Atomicparsley /path/to.3gp --3gp-album \"Cow Cod Soup For Everyone\" track=10 lang=car\n" -"\n" -"example: Atomicparsley /path/to.3gp --3gp-classification \"Poor Sport\" entity=\"PTA \" index=12 UTF16\n" -"example: Atomicparsley /path/to.3gp --3gp-keyword keywords=\"foo1,foo2,foo 3\" UTF16 --3gp-keyword \"\"\n" -"example: Atomicparsley /path/to.3gp --3gp-location 'Bethesda Terrace' latitude=40.77 longitude=73.98W \n" -" altitude=4.3B role='real' body=Earth notes='Underground'\n" -"\n" -"example: Atomicparsley /path/to.3gp --3gp-title \"I see London.\" --3gp-title \"Veo Madrid.\" lang=spa \n" -" --3gp-title \"Widze Warsawa.\" lang=pol\n" -"\n" -; - -static const char* ISOHelp_text = -"AtomicParsley help page for setting ISO copyright notices at movie & track level.\n" -"----------------------------------------------------------------------------------------------------\n" -" The ISO specification allows for setting copyright in a number of places. This copyright atom is\n" -" independant of the iTunes-style --copyright tag that can be set. This ISO tag is identical to the\n" -" 3GP-style copyright. In fact, using --ISO-copyright can potentially overwrite the 3gp copyright\n" -" asset if set at movie level & given the same language to set the copyright on. This copyright\n" -" notice is the only metadata tag defined by the reference ISO 14496-12 specification.\n" -"\n" -" ISO copyright notices can be set at movie level, track level for a single track, or for all tracks.\n" -" Multiple copyright notices are allowed, but they must differ in the language setting. To see avail-\n" -" able languages use \"AtomicParsley --language-list\". Notices can be set in utf8 or utf16.\n" -"\n" -" --ISO-copyright (str) [movie|track|track=#] [lang=3str] [UTF16] Set a copyright notice\n" -" # in 'track=#' denotes the target track\n" -" 3str is the 3 letter ISO-639-2 language.\n" -" Brackets [] show optional parameters.\n" -" Defaults are: movie level, 'eng' in utf8.\n" -"\n" -"example: AtomicParsley /path/file.mp4 -t 1 Note: the only way to see all contents is with -t 1 \n" -"example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\"\n" -"example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" movie\n" -"example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track=2 lang=urd\n" -"example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track UTF16\n" -"example: AP --ISO-copyright \"Example\" track --ISO-copyright \"Por Exemplo\" track=2 lang=spa UTF16\n" -"\n" -"Note: to remove the copyright, set the string to \"\" - the track and language must match the target.\n" -"example: --ISO-copyright \"\" track --ISO-copyright \"\" track=2 lang=spa\n" -"\n" -"Note: (foo) denotes required arguments; [foo] denotes optional parameters & may have defaults.\n" -; - -static const char* uuidHelp_text = -"AtomicParsley help page for setting uuid user extension metadata tags.\n" -"----------------------------------------------------------------------------------------------------\n" -" Setting a user-defined 'uuid' private extention tags will appear in \"moov.udta.meta\"). These will\n" -" only be read by AtomicParsley & can be set irrespective of file branding. The form of uuid that AP\n" -" is a v5 uuid generated from a sha1 hash of an atom name in an 'AtomicParsley.sf.net' namespace.\n" -"\n" -" The uuid form is in some Sony & Compressor files, but of version 4 (random/pseudo-random). An example\n" -" uuid of 'cprt' in the 'AtomicParsley.sf.net' namespace is: \"4bd39a57-e2c8-5655-a4fb-7a19620ef151\".\n" -" 'cprt' in the same namespace will always create that uuid; uuid atoms will only print out if the\n" -" uuid generated is the same as discovered. Sony uuids don't for example show up with AP -t.\n" -"\n" -" --information , -i (str) Set an information tag on uuid atom name\"inf\"\n" -" --url , -u (URL) Set a URL tag on uuid atom name \"\302url\"\n" -" --tagtime , timestamp Set the Coordinated Univeral Time of tagging on \"tdtg\"\n" -"\n" -" Define & set an arbitrary atom with a text data or embed a file:\n" -" --meta-uuid There are two forms: 1 for text & 1 for file operations\n" -" setting text form:\n" -" --meta-uuid (atom) \"text\" (str) \"atom\" = 4 character atom name of your choice\n" -" str is whatever text you want to set\n" -" file embedding form:\n" -" --meta-uuid (atom) \"file\" (/path) [description=\"foo\"] [mime-type=\"foo/moof\"]\n" -" \"atom\" = 4 character atom name of your choice\n" -" /path = path to the file that will be embedded*\n" -" description = optional description of the file\n" -" default is \"[none]\"\n" -" mime-type = optional mime type for the file\n" -" default is \"none\"\n" -" Note: no auto-disocevery of mime type\n" -" if you know/want it: supply it.\n" -" *Note: a file extension (/path/file.ext) is required\n" -"\n" -"Note: (foo) denotes required arguments; [foo] denotes optional arguments & may have defaults.\n" -"\n" -"Examples: \n" -" --tagtime timestamp --information \"[psst]I see metadata\" --url http://www.bumperdumper.com\n" -" --meta-uuid tagr text \"Johnny Appleseed\" --meta-uuid \302\251sft text \"OpenShiiva encoded.\"\n" -" --meta-uuid scan file /usr/pix/scans.zip\n" -" --meta-uuid 1040 file ../../2006_taxes.pdf description=\"Fooled 'The Man' yet again.\"\n" -"can be removed with:\n" -" --tagtime \"\" --information \"\" --url \" \" --meta-uuid scan file ""\n" -" --manualAtomRemove \"moov.udta.meta.uuid=672c98cd-f11f-51fd-adec-b0ee7b4d215f\" \\\n" -" --manualAtomRemove \"moov.udta.meta.uuid=1fed6656-d911-5385-9cb2-cb2c100f06e7\"\n" -"Remove the Sony uuid atoms with:\n" -" --manualAtomRemove moov.trak[1].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n" -" --manualAtomRemove moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n" -" --manualAtomRemove uuid=50524f46-21d2-4fce-bb88-695cfac9c740\n" -"\n" -"Viewing the contents of uuid atoms:\n" -" -t or --textdata Shows the uuid atoms (both text & file) that AP sets:\n" -" Example output:\n" -" Atom uuid=ec0f...d7 (AP uuid for \"scan\") contains: FILE.zip; description=[none]\n" -" Atom uuid=672c...5f (AP uuid for \"tagr\") contains: Johnny Appleseed\n" -"\n" -"Extracting an embedded file in a uuid atom:\n" -" --extract1uuid (atom) Extract file embedded within uuid=atom into same folder\n" -" (file will be named with suffix shown in --textdata)\n" -" --extract-uuids [/path] Extract all files in uuid atoms under the moov.udta.meta\n" -" hierarchy. If no /path is given, files will be extracted\n" -" to the same folder as the originating file.\n" -"\n" -" Examples:\n" -" --extract1uuid scan\n" -" ... Extracted uuid=scan attachment to file: /some/path/FILE_scan_uuid.zip\n" -" --extract-uuids ~/Desktop/plops\n" -" ... Extracted uuid=pass attachment to file: /Users/me/Desktop/plops_pass_uuid.pdf\n" -" ... Extracted uuid=site attachment to file: /Users/me/Desktop/plops_site_uuid.html\n" -"\n" -"------------------------------------------------------------------------------------------------\n" -; - -static const char* rDNSHelp_text = -"AtomicParsley help page for setting reverse domain '----' metadata atoms.\n" -"----------------------------------------------------------------------------------------------------\n" -" Please note that the reverse DNS format supported here is not feature complete.\n" -"\n" -" Another style of metadata that iTunes uses is called the reverse DNS format. For all known tags,\n" -" iTunes offers no user-accessible exposure to these tags or their contents. This reverse DNS form has\n" -" a differnt form than other iTunes tags that have a atom name that describes the content of 'data'\n" -" atom it contains. In the reverseDNS format, the parent to the structure called the '----' atom, with\n" -" children atoms that describe & contain the metadata carried. The 'mean' child contains the reverse\n" -" domain itself ('com.apple.iTunes') & the 'name' child contains the descriptor ('iTunNORM'). A 'data'\n" -" atom follows that actually contains the contents of the tag.\n" -"\n" -" --contentRating (rating) Set the US TV/motion picture media content rating\n" -" for available ratings use \"AtomicParsley --ratings-list\n" - -" --rDNSatom (str) name=(name_str) domain=(reverse_domain) Manually set a reverseDNS atom.\n" -"\n" -" To set the form manually, 3 things are required: a domain, a name, and the desired text.\n" -" Note: multiple 'data' atoms are supported, but not in the com.apple.iTunes domain\n" -" Examples:\n" -" --contentRating \"NC-17\" --contentRating \"TV-Y7\"\n" -" --rDNSatom \"mpaa|PG-13|300|\" name=iTunEXTC domain=com.apple.iTunes\n" -" --contentRating \"\"\n" -" --rDNSatom \"\" name=iTunEXTC domain=com.apple.iTunes\n" -" --rDNSatom \"try1\" name=EVAL domain=org.fsf --rDNSatom \"try 2\" name=EVAL domain=org.fsf\n" -" --rDNSatom \"\" name=EVAL domain=org.fsf\n" -"----------------------------------------------------------------------------------------------------\n" -; - -static const char* ID3Help_text = -"AtomicParsley help page for ID32 atoms with ID3 tags.\n" -"----------------------------------------------------------------------------------------------------\n" -" ** Please note: ID3 tag support is not feature complete & is in an alpha state. **\n" -"----------------------------------------------------------------------------------------------------\n" -" ID3 tags are the tagging scheme used by mp3 files (where they are found typically at the start of the\n" -" file). In mpeg-4 files, ID3 version 2 tags are located in specific hierarchies at certain levels, at\n" -" file/movie/track level. The way that ID3 tags are carried on mpeg-4 files (carried by 'ID32' atoms)\n" -" was added in early 2006, but the ID3 tagging 'informal standard' was last updated (to v2.4) in 2000.\n" -" With few exceptions, ID3 tags in mpeg-4 files exist identically to their mp3 counterparts.\n" -"\n" -" The ID3 parlance, a frame contains an piece of metadata. A frame (like COMM for comment, or TIT1 for\n" -" title) contains the information, while the tag contains all the frames collectively. The 'informal\n" -" standard' for ID3 allows multiple langauges for frames like COMM (comment) & USLT (lyrics). In mpeg-4\n" -" this language setting is removed from the ID3 domain and exists in the mpeg-4 domain. That means that\n" -" when an english and a spanish comment are set, 2 separate ID32 atoms are created, each with a tag & 1\n" -" frame as in this example:\n" -" --ID3Tag COMM \"Primary\" --desc=AAA --ID3Tag COMM \"El Segundo\" UTF16LE lang=spa --desc=AAA\n" -" See available frames with \"AtomicParsley --ID3frames-list\"\n" -" See avilable imagetypes with \"AtomicParsley --imagetype-list\"\n" -"\n" -" AtomicParsley writes ID3 version 2.4.0 tags *only*. There is no up-converting from older versions.\n" -" Defaults are:\n" -" default to movie level (moov.meta.ID32); other options are [ \"root\", \"track=(num)\" ] (see WARNING)\n" -" UTF-8 text encoding when optional; other options are [ \"LATIN1\", \"UTF16BE\", \"UTF16LE\" ]\n" -" frames that require descriptions have a default of \"\"\n" -" for frames requiring a language setting, the ID32 language is used (currently defaulting to 'eng')\n" -" frames that require descriptions have a default of \"\"\n" -" image type defaults to 0x00 or Other; for image type 0x01, 32x32 png is enforced (switching to 0x02)\n" -" setting the image mimetype is generally not required as the file is tested, but can be overridden\n" -" zlib compression off\n" -"\n" -" WARNING:\n" -" Quicktime Player (up to v7.1.3 at least) will freeze opeing a file with ID32 tags at movie level.\n" -" Specifically, the parent atom, 'meta' is the source of the issue. You can set the tags at file or\n" -" track level which avoids the problem, but the default is movie level. iTunes is unaffected.\n" -"----------------------------------------------------------------------------------------------------\n" -" Current limitations:\n" -" - syncsafe integers are used as indicated by the id3 \"informal standard\". usage/reading of\n" -" nonstandard ordinary unsigned integers (uint32_t) is not/will not be implemented.\n" -" - externally referenced images (using mimetype '-->') are prohibited by the ID32 specification.\n" -" - the ID32 atom is only supported in a non-referenced context\n" -" - probably a raft of other limitations that my brain lost along the way...\n" -"----------------------------------------------------------------------------------------------------\n" -" Usage:\n" -" --ID3Tag (frameID or alias) (str) [desc=(str)] [mimetype=(str)] [imagetype=(str or hex)] [...]\n" -"\n" -" ... represents other arguments:\n" -" [compressed] zlib compress the frame\n" -" [UTF16BE, UTF16LE, LATIN1] alternative text encodings for frames that support different encodings\n" -"\n" -"Note: (foo) denotes required arguments; [foo] denotes optional parameters\n" -"\n" -" Examples:\n" -" --ID3Tag APIC /path/to/img.ext\n" -" --ID3Tag APIC /path/to/img.ext desc=\"something to say\" imagetype=0x08 UTF16LE compressed\n" -" --ID3Tag composer \"I, Claudius\" --ID3Tag TPUB \"Seneca the Roman\" --ID3Tag TMOO Imperial\n" -" --ID3Tag UFID look@me.org uniqueID=randomUUIDstamp\n" -"\n" -" Extracting embedded images in APIC frames:\n" -" --ID3Tag APIC extract\n" -" images are extracted into the same directory as the source mpeg-4 file\n" -"\n" -#if defined (DARWIN_PLATFORM) -" Setting MCDI (Music CD Identifier):\n" -" --ID3Tag MCDI disk4\n" -" Information to create this frame is taken directly off an Audio CD's TOC. If the target\n" -" disk is not found or is not an audio CD, a scan of all devices will occur. If an Audio CD\n" -" is present, the scan should yield what should be entered after 'MCDI':\n" -" % AP file --ID3Tag MCDI disk3\n" -" % No cd present in drive at disk3\n" -" % Device 'disk4' contains cd media\n" -" % Good news, device 'disk4' is an Audio CD and can be used for 'MCDI' setting\n" -#elif defined (HAVE_LINUX_CDROM_H) -" Setting MCDI (Music CD Identifier):\n" -" --ID3Tag MCDI /dev/hdc\n" -" Information to create this frame is taken directly off an Audio CD's TOC. An Audio CD\n" -" must be mounted & present.\n" -#elif defined (_WIN32) -" Setting MCDI (Music CD Identifier):\n" -" --ID3Tag MCDI D\n" -" Information to create this frame is taken directly off an Audio CD's TOC. The letter after\n" -" \"MCDI\" is the letter of the drive where the CD is present.\n" +// detailed options for 3gp branded files +static const char *_3gpHelp_text = + "AtomicParsley 3gp help page for setting 3GPP-style metadata.\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " 3GPP text tags can be encoded in either UTF-8 (default input encoding) " + "or UTF-16 (converted from UTF-8)\n" + " Many 3GPP text tags can be set for a desired language by a " + "3-letter-lowercase code (default is \"eng\").\n" + " For tags that support the language attribute (all except year), more " + "than one tag of the same name\n" + " (3 titles for example) differing in the language code is supported.\n" + "\n" + " iTunes-style metadata is not supported by the 3GPP TS 26.244 version " + "6.4.0 Release 6 specification.\n" + " 3GPP asset tags can be set at movie level or track level & are set in a " + "different hierarchy: moov.udta \n" + " if at movie level (versus iTunes moov.udta.meta.ilst). Other 3rd party " + "utilities may allow setting\n" + " iTunes-style metadata in 3gp files. When a 3gp file is detected (file " + "extension doesn't matter), only\n" + " 3gp spec-compliant metadata will be read & written.\n" + "\n" + " Note1: there are a number of different 'brands' that 3GPP files come " + "marked as. Some will not be \n" + " supported by AtomicParsley due simply to them being unknown and " + "untested. You can compile your\n" + " own AtomicParsley to evaluate it by adding the hex code into the " + "source of APar_IdentifyBrand.\n" + "\n" + " Note2: There are slight accuracy discrepancies in location's fixed " + "point decimals set and retrieved.\n" + "\n" + " Note3: QuickTime Player can see a limited subset of these tags, but " + "only in 1 language & there seems to\n" + " be an issue with not all unicode text displaying properly. This " + "is an issue withing QuickTime -\n" + " the exact same text (in utf8) displays properly in an MPEG-4 " + "file. Some languages can also display\n" + " more glyphs than others.\n" + "\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " Tag setting options (default user data area is movie level; default lang " + "is 'eng'; default encoding is UTF8):\n" + " required arguments are in (parentheses); optional arguments are in " + "[brackets]\n" + "\n" + " --3gp-title (str) [lang=3str] [UTF16] [area] ......... " + "Set a 3gp media title tag\n" + " --3gp-author (str) [lang=3str] [UTF16] [area] ......... " + "Set a 3gp author of the media tag\n" + " --3gp-performer (str) [lang=3str] [UTF16] [area] ......... " + "Set a 3gp performer or artist tag\n" + " --3gp-genre (str) [lang=3str] [UTF16] [area] ......... " + "Set a 3gp genre asset tag\n" + " --3gp-description (str) [lang=3str] [UTF16] [area] ......... " + "Set a 3gp description or caption tag\n" + " --3gp-copyright (str) [lang=3str] [UTF16] [area] ......... " + "Set a 3gp copyright notice tag*\n" + "\n" + " --3gp-album (str) [lang=3str] [UTF16] [trknum=int] [area] " + "Set a 3gp album tag (& opt. tracknum)\n" + " --3gp-year (int) [area] ........................... Set a " + "3gp recording year tag (4 digit only)\n" + "\n" + " --3gp-rating (str) [entity=4str] [criteria=4str] " + "[lang=3str] [UTF16] [area] Rating tag\n" + " --3gp-classification (str) [entity=4str] [index=int] " + "[lang=3str] [UTF16] [area] Classification\n" + "\n" + " --3gp-keyword (str) [lang=3str] [UTF16] [area] Format of " + "str: 'keywords=word1,word2,word3,word4'\n" + "\n" + " --3gp-location (str) [lang=3str] [UTF16] [area] Set a 3gp " + "location tag (default: Central Park)\n" + " [longitude=fxd.pt] [latitude=fxd.pt] " + "[altitude=fxd.pt]\n" + " [role=str] [body=str] [notes=str]\n" + " fxd.pt values are decimal coordinates " + "(55.01209, 179.25W, 63)\n" + " 'role=' values: 'shooting location', " + "'real location', 'fictional location'\n" + " a negative value in coordinates " + "will be seen as a cli flag\n" + " append 'S', 'W' or 'B': lat=55S, " + "long=90.23W, alt=90.25B\n" + "\n" + " [area] can be \"movie\", \"track\" or \"track=num\" where 'num' is the " + "number of the track. If not specificied,\n" + " assets will be placed at movie level. The \"track\" option sets the " + "asset across all available tracks\n" + "\n" + "Note1: '4str' = a 4 letter string like \"PG13\"; 3str is a 3 letter " + "string like \"eng\"; int is an integer\n" + "Note2: List all languages for '3str' with \"AtomicParsley --language-list " + "(unknown languages become \"und\")\n" + "*Note3: The 3gp copyright asset can potentially be altered by using the " + "--ISO-copyright setting.\n" + + "--------------------------------------------------------------------------" + "--------------------------\n" + "Usage: AtomicParsley [3gpFILE] --option [argument] [optional_arguments] " + "[ --option2 [argument2]...] \n" + "\n" + "example: AtomicParsley /path/to.3gp -t \n" + "example: AtomicParsley /path/to.3gp -T 1 \n" + "example: Atomicparsley /path/to.3gp --3gp-performer \"Enjoy Yourself\" " + "lang=pol UTF16\n" + "example: Atomicparsley /path/to.3gp --3gp-year 2006 --3gp-album \"White " + "Label\" track=8 lang=fra\n" + "example: Atomicparsley /path/to.3gp --3gp-album \"Cow Cod Soup For " + "Everyone\" track=10 lang=car\n" + "\n" + "example: Atomicparsley /path/to.3gp --3gp-classification \"Poor Sport\" " + "entity=\"PTA \" index=12 UTF16\n" + "example: Atomicparsley /path/to.3gp --3gp-keyword " + "keywords=\"foo1,foo2,foo 3\" UTF16 --3gp-keyword \"\"\n" + "example: Atomicparsley /path/to.3gp --3gp-location 'Bethesda Terrace' " + "latitude=40.77 longitude=73.98W \n" + " altitude=4.3B " + "role='real' body=Earth notes='Underground'\n" + "\n" + "example: Atomicparsley /path/to.3gp --3gp-title \"I see London.\" " + "--3gp-title \"Veo Madrid.\" lang=spa \n" + " --3gp-title \"Widze Warsawa.\" " + "lang=pol\n" + "\n"; + +static const char *ISOHelp_text = + "AtomicParsley help page for setting ISO copyright notices at movie & " + "track level.\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " The ISO specification allows for setting copyright in a number of " + "places. This copyright atom is\n" + " independant of the iTunes-style --copyright tag that can be set. This " + "ISO tag is identical to the\n" + " 3GP-style copyright. In fact, using --ISO-copyright can potentially " + "overwrite the 3gp copyright\n" + " asset if set at movie level & given the same language to set the " + "copyright on. This copyright\n" + " notice is the only metadata tag defined by the reference ISO 14496-12 " + "specification.\n" + "\n" + " ISO copyright notices can be set at movie level, track level for a " + "single track, or for all tracks.\n" + " Multiple copyright notices are allowed, but they must differ in the " + "language setting. To see avail-\n" + " able languages use \"AtomicParsley --language-list\". Notices can be " + "set in utf8 or utf16.\n" + "\n" + " --ISO-copyright (str) [movie|track|track=#] [lang=3str] [UTF16] " + "Set a copyright notice\n" + " # in 'track=#' " + "denotes the target track\n" + " 3str is the 3 " + "letter ISO-639-2 language.\n" + " Brackets [] " + "show optional parameters.\n" + " Defaults are: " + "movie level, 'eng' in utf8.\n" + "\n" + "example: AtomicParsley /path/file.mp4 -t 1 Note: the only way to see " + "all contents is with -t 1 \n" + "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\"\n" + "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" movie\n" + "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track=2 " + "lang=urd\n" + "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track " + "UTF16\n" + "example: AP --ISO-copyright \"Example\" track --ISO-copyright \"Por " + "Exemplo\" track=2 lang=spa UTF16\n" + "\n" + "Note: to remove the copyright, set the string to \"\" - the track and " + "language must match the target.\n" + "example: --ISO-copyright \"\" track --ISO-copyright \"\" track=2 " + "lang=spa\n" + "\n" + "Note: (foo) denotes required arguments; [foo] denotes optional parameters " + "& may have defaults.\n"; + +static const char *uuidHelp_text = + "AtomicParsley help page for setting uuid user extension metadata tags.\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " Setting a user-defined 'uuid' private extention tags will appear in " + "\"moov.udta.meta\"). These will\n" + " only be read by AtomicParsley & can be set irrespective of file " + "branding. The form of uuid that AP\n" + " is a v5 uuid generated from a sha1 hash of an atom name in an " + "'AtomicParsley.sf.net' namespace.\n" + "\n" + " The uuid form is in some Sony & Compressor files, but of version 4 " + "(random/pseudo-random). An example\n" + " uuid of 'cprt' in the 'AtomicParsley.sf.net' namespace is: " + "\"4bd39a57-e2c8-5655-a4fb-7a19620ef151\".\n" + " 'cprt' in the same namespace will always create that uuid; uuid atoms " + "will only print out if the\n" + " uuid generated is the same as discovered. Sony uuids don't for example " + "show up with AP -t.\n" + "\n" + " --information , -i (str) Set an information tag on uuid atom " + "name\"\251inf\"\n" + " --url , -u (URL) Set a URL tag on uuid atom name " + "\"\302\251url\"\n" + " --tagtime , timestamp Set the Coordinated Univeral Time " + "of tagging on \"tdtg\"\n" + "\n" + " Define & set an arbitrary atom with a text data or embed a file:\n" + " --meta-uuid There are two forms: 1 for text & 1 for file " + "operations\n" + " setting text form:\n" + " --meta-uuid (atom) \"text\" (str) \"atom\" = 4 " + "character atom name of your choice\n" + " str is whatever text " + "you want to set\n" + " file embedding form:\n" + " --meta-uuid (atom) \"file\" (/path) [description=\"foo\"] " + "[mime-type=\"foo/moof\"]\n" + " \"atom\" = 4 " + "character atom name of your choice\n" + " /path = path to the " + "file that will be embedded*\n" + " description = " + "optional description of the file\n" + " default is " + "\"[none]\"\n" + " mime-type = optional " + "mime type for the file\n" + " default is " + "\"none\"\n" + " Note: no " + "auto-disocevery of mime type\n" + " if " + "you know/want it: supply it.\n" + " *Note: a file extension " + "(/path/file.ext) is required\n" + "\n" + "Note: (foo) denotes required arguments; [foo] denotes optional arguments " + "& may have defaults.\n" + "\n" + "Examples: \n" + " --tagtime timestamp --information \"[psst]I see metadata\" --url " + "http://www.bumperdumper.com\n" + " --meta-uuid tagr text \"Johnny Appleseed\" --meta-uuid \302\251sft text " + "\"OpenShiiva encoded.\"\n" + " --meta-uuid scan file /usr/pix/scans.zip\n" + " --meta-uuid 1040 file ../../2006_taxes.pdf description=\"Fooled 'The " + "Man' yet again.\"\n" + "can be removed with:\n" + " --tagtime \"\" --information \"\" --url \" \" --meta-uuid scan file " + "\n" + " --manualAtomRemove " + "\"moov.udta.meta.uuid=672c98cd-f11f-51fd-adec-b0ee7b4d215f\" \\\n" + " --manualAtomRemove " + "\"moov.udta.meta.uuid=1fed6656-d911-5385-9cb2-cb2c100f06e7\"\n" + "Remove the Sony uuid atoms with:\n" + " --manualAtomRemove " + "moov.trak[1].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n" + " --manualAtomRemove " + "moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n" + " --manualAtomRemove uuid=50524f46-21d2-4fce-bb88-695cfac9c740\n" + "\n" + "Viewing the contents of uuid atoms:\n" + " -t or --textdata Shows the uuid atoms (both text & file) that " + "AP sets:\n" + " Example output:\n" + " Atom uuid=ec0f...d7 (AP uuid for \"scan\") contains: FILE.zip; " + "description=[none]\n" + " Atom uuid=672c...5f (AP uuid for \"tagr\") contains: Johnny " + "Appleseed\n" + "\n" + "Extracting an embedded file in a uuid atom:\n" + " --extract1uuid (atom) Extract file embedded within " + "uuid=atom into same folder\n" + " (file will be named with suffix " + "shown in --textdata)\n" + " --extract-uuids [/path] Extract all files in uuid atoms " + "under the moov.udta.meta\n" + " hierarchy. If no /path is given, " + "files will be extracted\n" + " to the same folder as the " + "originating file.\n" + "\n" + " Examples:\n" + " --extract1uuid scan\n" + " ... Extracted uuid=scan attachment to file: " + "/some/path/FILE_scan_uuid.zip\n" + " --extract-uuids ~/Desktop/plops\n" + " ... Extracted uuid=pass attachment to file: " + "/Users/me/Desktop/plops_pass_uuid.pdf\n" + " ... Extracted uuid=site attachment to file: " + "/Users/me/Desktop/plops_site_uuid.html\n" + "\n" + "--------------------------------------------------------------------------" + "----------------------\n"; + +static const char *rDNSHelp_text = + "AtomicParsley help page for setting reverse domain '----' metadata " + "atoms.\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " Please note that the reverse DNS format supported here is not " + "feature complete.\n" + "\n" + " Another style of metadata that iTunes uses is called the reverse DNS " + "format. For all known tags,\n" + " iTunes offers no user-accessible exposure to these tags or their " + "contents. This reverse DNS form has\n" + " a differnt form than other iTunes tags that have a atom name that " + "describes the content of 'data'\n" + " atom it contains. In the reverseDNS format, the parent to the structure " + "called the '----' atom, with\n" + " children atoms that describe & contain the metadata carried. The 'mean' " + "child contains the reverse\n" + " domain itself ('com.apple.iTunes') & the 'name' child contains the " + "descriptor ('iTunNORM'). A 'data'\n" + " atom follows that actually contains the contents of the tag.\n" + "\n" + " --contentRating (rating) Set the US TV/motion picture media " + "content rating\n" + " for available ratings use " + "\"AtomicParsley --ratings-list\n" + + " --rDNSatom (str) name=(name_str) domain=(reverse_domain) " + "Manually set a reverseDNS atom.\n" + "\n" + " To set the form manually, 3 things are required: a domain, a name, and " + "the desired text.\n" + " Note: multiple 'data' atoms are supported, but not in the " + "com.apple.iTunes domain\n" + " Examples:\n" + " --contentRating \"NC-17\" --contentRating \"TV-Y7\"\n" + " --rDNSatom \"mpaa|PG-13|300|\" name=iTunEXTC domain=com.apple.iTunes\n" + " --contentRating \"\"\n" + " --rDNSatom \"\" name=iTunEXTC domain=com.apple.iTunes\n" + " --rDNSatom \"try1\" name=EVAL domain=org.fsf --rDNSatom \"try 2\" " + "name=EVAL domain=org.fsf\n" + " --rDNSatom \"\" name=EVAL domain=org.fsf\n" + "--------------------------------------------------------------------------" + "--------------------------\n"; + +static const char *ID3Help_text = + "AtomicParsley help page for ID32 atoms with ID3 tags.\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " ** Please note: ID3 tag support is not feature complete & is in an " + "alpha state. **\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " ID3 tags are the tagging scheme used by mp3 files (where they are found " + "typically at the start of the\n" + " file). In mpeg-4 files, ID3 version 2 tags are located in specific " + "hierarchies at certain levels, at\n" + " file/movie/track level. The way that ID3 tags are carried on mpeg-4 " + "files (carried by 'ID32' atoms)\n" + " was added in early 2006, but the ID3 tagging 'informal standard' was " + "last updated (to v2.4) in 2000.\n" + " With few exceptions, ID3 tags in mpeg-4 files exist identically to their " + "mp3 counterparts.\n" + "\n" + " The ID3 parlance, a frame contains an piece of metadata. A frame (like " + "COMM for comment, or TIT1 for\n" + " title) contains the information, while the tag contains all the frames " + "collectively. The 'informal\n" + " standard' for ID3 allows multiple langauges for frames like COMM " + "(comment) & USLT (lyrics). In mpeg-4\n" + " this language setting is removed from the ID3 domain and exists in the " + "mpeg-4 domain. That means that\n" + " when an english and a spanish comment are set, 2 separate ID32 atoms are " + "created, each with a tag & 1\n" + " frame as in this example:\n" + " --ID3Tag COMM \"Primary\" --desc=AAA --ID3Tag COMM \"El Segundo\" " + "UTF16LE lang=spa --desc=AAA\n" + " See available frames with \"AtomicParsley --ID3frames-list\"\n" + " See avilable imagetypes with \"AtomicParsley --imagetype-list\"\n" + "\n" + " AtomicParsley writes ID3 version 2.4.0 tags *only*. There is no " + "up-converting from older versions.\n" + " Defaults are:\n" + " default to movie level (moov.meta.ID32); other options are [ \"root\", " + "\"track=(num)\" ] (see WARNING)\n" + " UTF-8 text encoding when optional; other options are [ \"LATIN1\", " + "\"UTF16BE\", \"UTF16LE\" ]\n" + " frames that require descriptions have a default of \"\"\n" + " for frames requiring a language setting, the ID32 language is used " + "(currently defaulting to 'eng')\n" + " frames that require descriptions have a default of \"\"\n" + " image type defaults to 0x00 or Other; for image type 0x01, 32x32 png " + "is enforced (switching to 0x02)\n" + " setting the image mimetype is generally not required as the file is " + "tested, but can be overridden\n" + " zlib compression off\n" + "\n" + " WARNING:\n" + " Quicktime Player (up to v7.1.3 at least) will freeze opeing a file " + "with ID32 tags at movie level.\n" + " Specifically, the parent atom, 'meta' is the source of the issue. " + "You can set the tags at file or\n" + " track level which avoids the problem, but the default is movie " + "level. iTunes is unaffected.\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " Current limitations:\n" + " - syncsafe integers are used as indicated by the id3 \"informal " + "standard\". usage/reading of\n" + " nonstandard ordinary unsigned integers (uint32_t) is not/will not be " + "implemented.\n" + " - externally referenced images (using mimetype '-->') are prohibited " + "by the ID32 specification.\n" + " - the ID32 atom is only supported in a non-referenced context\n" + " - probably a raft of other limitations that my brain lost along the " + "way...\n" + "--------------------------------------------------------------------------" + "--------------------------\n" + " Usage:\n" + " --ID3Tag (frameID or alias) (str) [desc=(str)] [mimetype=(str)] " + "[imagetype=(str or hex)] [...]\n" + "\n" + " ... represents other arguments:\n" + " [compressed] zlib compress the frame\n" + " [UTF16BE, UTF16LE, LATIN1] alternative text encodings for frames that " + "support different encodings\n" + "\n" + "Note: (foo) denotes required arguments; [foo] denotes optional " + "parameters\n" + "\n" + " Examples:\n" + " --ID3Tag APIC /path/to/img.ext\n" + " --ID3Tag APIC /path/to/img.ext desc=\"something to say\" imagetype=0x08 " + "UTF16LE compressed\n" + " --ID3Tag composer \"I, Claudius\" --ID3Tag TPUB \"Seneca the Roman\" " + "--ID3Tag TMOO Imperial\n" + " --ID3Tag UFID look@me.org uniqueID=randomUUIDstamp\n" + "\n" + " Extracting embedded images in APIC frames:\n" + " --ID3Tag APIC extract\n" + " images are extracted into the same directory as the source mpeg-4 " + "file\n" + "\n" +#if defined(__APPLE__) + " Setting MCDI (Music CD Identifier):\n" + " --ID3Tag MCDI disk4\n" + " Information to create this frame is taken directly off an Audio " + "CD's TOC. If the target\n" + " disk is not found or is not an audio CD, a scan of all devices " + "will occur. If an Audio CD\n" + " is present, the scan should yield what should be entered after " + "'MCDI':\n" + " % AP file --ID3Tag MCDI disk3\n" + " % No cd present in drive at disk3\n" + " % Device 'disk4' contains cd media\n" + " % Good news, device 'disk4' is an Audio CD and can be used for " + "'MCDI' setting\n" +#elif defined(__linux__) + " Setting MCDI (Music CD Identifier):\n" + " --ID3Tag MCDI /dev/hdc\n" + " Information to create this frame is taken directly off an Audio " + "CD's TOC. An Audio CD\n" + " must be mounted & present.\n" +#elif defined(_WIN32) + " Setting MCDI (Music CD Identifier):\n" + " --ID3Tag MCDI D\n" + " Information to create this frame is taken directly off an Audio " + "CD's TOC. The letter after\n" + " \"MCDI\" is the letter of the drive where the CD is present.\n" #endif -; - -void ExtractPaddingPrefs(char* env_padding_prefs) { - pad_prefs.default_padding_size = DEFAULT_PADDING_LENGTH; - pad_prefs.minimum_required_padding_size = MINIMUM_REQUIRED_PADDING_LENGTH; - pad_prefs.maximum_present_padding_size = MAXIMUM_REQUIRED_PADDING_LENGTH; + ; - if (env_padding_prefs != NULL) { - if (env_padding_prefs[0] == 0x22 || env_padding_prefs[0] == 0x27) env_padding_prefs++; +void ExtractPaddingPrefs(char *env_padding_prefs) { + pad_prefs.default_padding_size = DEFAULT_PADDING_LENGTH; + pad_prefs.minimum_required_padding_size = MINIMUM_REQUIRED_PADDING_LENGTH; + pad_prefs.maximum_present_padding_size = MAXIMUM_REQUIRED_PADDING_LENGTH; + + if (env_padding_prefs != NULL) { + if (env_padding_prefs[0] == 0x22 || env_padding_prefs[0] == 0x27) + env_padding_prefs++; + } + char *env_pad_prefs_ptr = env_padding_prefs; + + while (env_pad_prefs_ptr != NULL) { + env_pad_prefs_ptr = strsep(&env_padding_prefs, ":"); + + if (env_pad_prefs_ptr == NULL) + break; + + if (strncmp(env_pad_prefs_ptr, "DEFAULT_PAD=", 12) == 0) { + strsep(&env_pad_prefs_ptr, "="); + sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.default_padding_size); } - char* env_pad_prefs_ptr = env_padding_prefs; - - while (env_pad_prefs_ptr != NULL) { - env_pad_prefs_ptr = strsep(&env_padding_prefs,":"); - - if (env_pad_prefs_ptr == NULL) break; - - if (strncmp(env_pad_prefs_ptr, "DEFAULT_PAD=", 12) == 0) { - strsep(&env_pad_prefs_ptr,"="); - sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.default_padding_size); - } - if (strncmp(env_pad_prefs_ptr, "MIN_PAD=", 8) == 0) { - strsep(&env_pad_prefs_ptr,"="); - sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.minimum_required_padding_size); - } - if (strncmp(env_pad_prefs_ptr, "MAX_PAD=", 8) == 0) { - strsep(&env_pad_prefs_ptr,"="); - sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.maximum_present_padding_size); - } + if (strncmp(env_pad_prefs_ptr, "MIN_PAD=", 8) == 0) { + strsep(&env_pad_prefs_ptr, "="); + sscanf(env_pad_prefs_ptr, + "%" SCNu32, + &pad_prefs.minimum_required_padding_size); } - //fprintf(stdout, "Def %" PRIu32 "; Min %" PRIu32 "; Max %" PRIu32 "\n", pad_prefs.default_padding_size, pad_prefs.minimum_required_padding_size, pad_prefs.maximum_present_padding_size); - return; + if (strncmp(env_pad_prefs_ptr, "MAX_PAD=", 8) == 0) { + strsep(&env_pad_prefs_ptr, "="); + sscanf(env_pad_prefs_ptr, + "%" SCNu32, + &pad_prefs.maximum_present_padding_size); + } + } + // fprintf(stdout, "Def %" PRIu32 "; Min %" PRIu32 "; Max %" PRIu32 "\n", + // pad_prefs.default_padding_size, pad_prefs.minimum_required_padding_size, + // pad_prefs.maximum_present_padding_size); + return; } -void GetBasePath(const char *filepath, char* &basepath) { - //with a myriad of m4a, m4p, mp4, whatever else comes up... it might just be easiest to strip off the end. - int split_here = 0; - for (int i=strlen(filepath); i >= 0; i--) { - const char* this_char=&filepath[i]; - if ( *this_char == '.' ) { - split_here = i; - break; - } +void GetBasePath(const char *filepath, char *&basepath) { + // with a myriad of m4a, m4p, mp4, whatever else comes up... it might just be + // easiest to strip off the end. + int split_here = 0; + for (int i = strlen(filepath); i >= 0; i--) { + const char *this_char = &filepath[i]; + if (*this_char == '.') { + split_here = i; + break; } - memcpy(basepath, filepath, (size_t)split_here); + } + memcpy(basepath, filepath, (size_t)split_here); - return; + return; } -void find_optional_args(char *argv[], int start_optindargs, uint16_t &packed_lang, bool &asUTF16, uint8_t &udta_container, uint8_t &trk_idx, int max_optargs) { - asUTF16 = false; - packed_lang = 5575; //und = 0x55C4 = 21956, but QTPlayer doesn't like und //eng = 0x15C7 = 5575 - - for (int i= 0; i <= max_optargs-1; i++) { - if ( argv[start_optindargs + i] && start_optindargs + i <= total_args ) { - if ( strncmp(argv[start_optindargs + i], "lang=", 5) == 0 ) { - if (!MatchLanguageCode(argv[start_optindargs +i]+5) ) { - packed_lang = PackLanguage("und", 0); - } else { - packed_lang = PackLanguage(argv[start_optindargs +i], 5); - } - - } else if ( strcmp(argv[start_optindargs + i], "UTF16") == 0 ) { - asUTF16 = true; - } else if ( strcmp(argv[optind + i], "movie") == 0 ) { - udta_container = MOVIE_LEVEL_ATOM; - } else if ( strncmp(argv[optind + i], "track=", 6) == 0 ) { - char* track_index_str = argv[optind + i]; - strsep(&track_index_str, "="); - trk_idx = strtoul(track_index_str, NULL, 10); - udta_container = SINGLE_TRACK_ATOM; - } else if ( strcmp(argv[optind + i], "track") == 0 ) { - udta_container = ALL_TRACKS_ATOM; - } - if (*argv[start_optindargs + i] == '-') { - break; //we've hit another cli argument - } +void find_optional_args(char *argv[], + int start_optindargs, + uint16_t &packed_lang, + bool &asUTF16, + uint8_t &udta_container, + uint8_t &trk_idx, + int max_optargs) { + asUTF16 = false; + packed_lang = 5575; // und = 0x55C4 = 21956, but QTPlayer doesn't like und + // //eng = 0x15C7 = 5575 + + for (int i = 0; i <= max_optargs - 1; i++) { + if (argv[start_optindargs + i] && start_optindargs + i <= total_args) { + if (strncmp(argv[start_optindargs + i], "lang=", 5) == 0) { + if (!MatchLanguageCode(argv[start_optindargs + i] + 5)) { + packed_lang = PackLanguage("und", 0); + } else { + packed_lang = PackLanguage(argv[start_optindargs + i], 5); } + + } else if (strcmp(argv[start_optindargs + i], "UTF16") == 0) { + asUTF16 = true; + } else if (strcmp(argv[optind + i], "movie") == 0) { + udta_container = MOVIE_LEVEL_ATOM; + } else if (strncmp(argv[optind + i], "track=", 6) == 0) { + char *track_index_str = argv[optind + i]; + strsep(&track_index_str, "="); + trk_idx = strtoul(track_index_str, NULL, 10); + udta_container = SINGLE_TRACK_ATOM; + } else if (strcmp(argv[optind + i], "track") == 0) { + udta_container = ALL_TRACKS_ATOM; + } + if (*argv[start_optindargs + i] == '-') { + break; // we've hit another cli argument + } } - return; + } + return; } -void scan_ID3_optargs(char *argv[], int start_optargs, const char* &target_lang, uint16_t &packed_lang, uint8_t &char_encoding, char* meta_container, bool &multistring) { - packed_lang = 5575; //default ID32 lang is 'eng' - uint16_t i = 0; - - while (argv[start_optargs + i] != NULL) { - if ( argv[start_optargs + i] && start_optargs + i <= total_args ) { - - if ( strncmp(argv[start_optargs + i], "lang=", 5) == 0 ) { - if (!MatchLanguageCode(argv[start_optargs +i]+5) ) { - packed_lang = PackLanguage("und", 0); - target_lang = "und"; - } else { - packed_lang = PackLanguage(argv[start_optargs +i], 5); - target_lang = argv[start_optargs + i] + 5; - } - - } else if ( strcmp(argv[start_optargs + i], "UTF16LE") == 0 ) { - char_encoding = TE_UTF16LE_WITH_BOM; - } else if ( strcmp(argv[start_optargs + i], "UTF16BE") == 0 ) { - char_encoding = TE_UTF16BE_NO_BOM; - } else if ( strcmp(argv[start_optargs + i], "LATIN1") == 0 ) { - char_encoding = TE_LATIN1; - - } else if ( strcmp(argv[optind + i], "root") == 0 ) { - *meta_container = 0-FILE_LEVEL_ATOM; - } else if ( strncmp(argv[optind + i], "track=", 6) == 0 ) { - char* track_index_str = argv[optind + i]; - strsep(&track_index_str, "="); - *meta_container = strtoul(track_index_str, NULL, 10); - } +void scan_ID3_optargs(char *argv[], + int start_optargs, + const char *&target_lang, + uint16_t &packed_lang, + uint8_t &char_encoding, + char *meta_container, + bool &multistring) { + packed_lang = 5575; // default ID32 lang is 'eng' + uint16_t i = 0; + + while (argv[start_optargs + i] != NULL) { + if (argv[start_optargs + i] && start_optargs + i <= total_args) { + + if (strncmp(argv[start_optargs + i], "lang=", 5) == 0) { + if (!MatchLanguageCode(argv[start_optargs + i] + 5)) { + packed_lang = PackLanguage("und", 0); + target_lang = "und"; + } else { + packed_lang = PackLanguage(argv[start_optargs + i], 5); + target_lang = argv[start_optargs + i] + 5; } - if (*argv[start_optargs + i] == '-') { - break; //we've hit another cli argument or deleting some frame - } - i++; + } else if (strcmp(argv[start_optargs + i], "UTF16LE") == 0) { + char_encoding = TE_UTF16LE_WITH_BOM; + } else if (strcmp(argv[start_optargs + i], "UTF16BE") == 0) { + char_encoding = TE_UTF16BE_NO_BOM; + } else if (strcmp(argv[start_optargs + i], "LATIN1") == 0) { + char_encoding = TE_LATIN1; + + } else if (strcmp(argv[optind + i], "root") == 0) { + *meta_container = 0 - FILE_LEVEL_ATOM; + } else if (strncmp(argv[optind + i], "track=", 6) == 0) { + char *track_index_str = argv[optind + i]; + strsep(&track_index_str, "="); + *meta_container = strtoul(track_index_str, NULL, 10); + } } - return; + + if (*argv[start_optargs + i] == '-') { + break; // we've hit another cli argument or deleting some frame + } + i++; + } + return; } -const char* find_ID3_optarg(char *argv[], int start_optargs, const char* arg_string) { - const char* ret_val = ""; - uint16_t i = 0; - uint8_t arg_prefix_len = strlen(arg_string); - - while (argv[start_optargs + i] != NULL) { - if ( argv[start_optargs + i] && start_optargs + i <= total_args ) { - if (strcmp(arg_string, "compressed") == 0 && strcmp(argv[start_optargs + i], "compressed") == 0) { - return "1"; - } - //if (strcmp(arg_string, "text++") == 0 && strcmp(argv[start_optargs + i], "text++") == 0) { - // return "1"; - //} - if (strncmp(argv[start_optargs + i], arg_string, arg_prefix_len) == 0) { - ret_val = argv[start_optargs + i] + arg_prefix_len; - break; - } - } - if (*argv[start_optargs + i] == '-') { - break; //we've hit another cli argument or deleting some frame - } - i++; +const char * +find_ID3_optarg(char *argv[], int start_optargs, const char *arg_string) { + const char *ret_val = ""; + uint16_t i = 0; + uint8_t arg_prefix_len = strlen(arg_string); + + while (argv[start_optargs + i] != NULL) { + if (argv[start_optargs + i] && start_optargs + i <= total_args) { + if (strcmp(arg_string, "compressed") == 0 && + strcmp(argv[start_optargs + i], "compressed") == 0) { + return "1"; + } + // if (strcmp(arg_string, "text++") == 0 && strcmp(argv[start_optargs + + // i], "text++") == 0) { + // return "1"; + //} + if (strncmp(argv[start_optargs + i], arg_string, arg_prefix_len) == 0) { + ret_val = argv[start_optargs + i] + arg_prefix_len; + break; + } + } + if (*argv[start_optargs + i] == '-') { + break; // we've hit another cli argument or deleting some frame } - return ret_val; + i++; + } + return ret_val; } //*********************************************** -static void show_short_help(void) -{ - printf("%s\n", shortHelp_text); - ShowVersionInfo(); - printf("\nReport issues at %s\n", PACKAGE_BUGREPORT); +static void show_short_help(void) { + printf("%s\n", shortHelp_text); + ShowVersionInfo(); + printf("\nSubmit bug fixes to https://github.com/wez/atomicparsley\n"); } -int real_main(int argc, char *argv[]) -{ - if (argc == 1) { - show_short_help(); - exit(0); - } else if (argc == 2 && ( - (strcmp(argv[1],"-v") == 0) || - (strcmp(argv[1],"-version") == 0) || - (strcmp(argv[1],"--version") == 0)) ) { - ShowVersionInfo(); - exit(0); - } else if (argc == 2) { - if ((strcmp(argv[1],"-help") == 0) || - (strcmp(argv[1],"--help") == 0) || - (strcmp(argv[1],"-h") == 0 ) ) { - show_short_help(); - exit(0); - } else if ( (strcmp(argv[1],"--longhelp") == 0) || (strcmp(argv[1],"-longhelp") == 0) || (strcmp(argv[1],"-Lh") == 0) ) { -#if defined (_WIN32) && !defined (__CYGWIN__) - if (UnicodeOutputStatus == WIN32_UTF16) { //convert the helptext to utf16 to preserve characters - int help_len = strlen(longHelp_text)+1; - wchar_t* Lhelp_text = (wchar_t *)malloc(sizeof(wchar_t)*help_len); - wmemset(Lhelp_text, 0, help_len); - UTF8ToUTF16LE((unsigned char*)Lhelp_text, 2*help_len, (unsigned char*)longHelp_text, help_len); - APar_unicode_win32Printout(Lhelp_text, (char *) longHelp_text); - free(Lhelp_text); - } else +int real_main(int argc, char *argv[]) { + if (argc == 1) { + show_short_help(); + exit(0); + } else if (argc == 2 && ((strcmp(argv[1], "-v") == 0) || + (strcmp(argv[1], "-version") == 0) || + (strcmp(argv[1], "--version") == 0))) { + ShowVersionInfo(); + exit(0); + } else if (argc == 2) { + if ((strcmp(argv[1], "-help") == 0) || (strcmp(argv[1], "--help") == 0) || + (strcmp(argv[1], "-h") == 0)) { + show_short_help(); + exit(0); + } else if ((strcmp(argv[1], "--longhelp") == 0) || + (strcmp(argv[1], "-longhelp") == 0) || + (strcmp(argv[1], "-Lh") == 0)) { +#if defined(_WIN32) && !defined(__CYGWIN__) + if (UnicodeOutputStatus == WIN32_UTF16) { // convert the helptext to utf16 + // to preserve \251 characters + int help_len = strlen(longHelp_text) + 1; + wchar_t *Lhelp_text = (wchar_t *)malloc(sizeof(wchar_t) * help_len); + wmemset(Lhelp_text, 0, help_len); + UTF8ToUTF16LE((unsigned char *)Lhelp_text, + 2 * help_len, + (unsigned char *)longHelp_text, + help_len); + APar_unicode_win32Printout(Lhelp_text, (char *)longHelp_text); + free(Lhelp_text); + } else #endif - { - fprintf(stdout, "%s", longHelp_text); - } - exit(0); + { + fprintf(stdout, "%s", longHelp_text); + } + exit(0); + + } else if ((strcmp(argv[1], "--3gp-help") == 0) || + (strcmp(argv[1], "-3gp-help") == 0) || + (strcmp(argv[1], "--3gp-h") == 0)) { + fprintf(stdout, "%s\n", _3gpHelp_text); + exit(0); + + } else if ((strcmp(argv[1], "--ISO-help") == 0) || + (strcmp(argv[1], "--iso-help") == 0) || + (strcmp(argv[1], "-Ih") == 0)) { + fprintf(stdout, "%s\n", ISOHelp_text); + exit(0); + + } else if ((strcmp(argv[1], "--file-help") == 0) || + (strcmp(argv[1], "-file-help") == 0) || + (strcmp(argv[1], "-fh") == 0)) { + fprintf(stdout, "%s\n", fileLevelHelp_text); + exit(0); + + } else if ((strcmp(argv[1], "--uuid-help") == 0) || + (strcmp(argv[1], "-uuid-help") == 0) || + (strcmp(argv[1], "-uh") == 0)) { + fprintf(stdout, "%s\n", uuidHelp_text); + exit(0); + + } else if ((strcmp(argv[1], "--reverseDNS-help") == 0) || + (strcmp(argv[1], "-rDNS-help") == 0) || + (strcmp(argv[1], "-rh") == 0)) { + fprintf(stdout, "%s\n", rDNSHelp_text); + exit(0); + + } else if ((strcmp(argv[1], "--ID3-help") == 0) || + (strcmp(argv[1], "-ID3-help") == 0) || + (strcmp(argv[1], "-ID3h") == 0)) { + fprintf(stdout, "%s\n", ID3Help_text); + exit(0); + + } else if (strcmp(argv[1], "--genre-list") == 0) { + ListGenresValues(); + exit(0); + + } else if (strcmp(argv[1], "--stik-list") == 0) { + ListStikValues(); + exit(0); + + } else if (strcmp(argv[1], "--language-list") == 0 || + strcmp(argv[1], "--languages-list") == 0 || + strcmp(argv[1], "--list-language") == 0 || + strcmp(argv[1], "--list-languages") == 0 || + strcmp(argv[1], "-ll") == 0) { + ListLanguageCodes(); + exit(0); + + } else if (strcmp(argv[1], "--ratings-list") == 0) { + ListMediaRatings(); + exit(0); + + } else if (strcmp(argv[1], "--genre-movie-id-list") == 0) { + ListMovieGenreIDValues(); + exit(0); + + } else if (strcmp(argv[1], "--genre-tv-id-list") == 0) { + ListTVGenreIDValues(); + exit(0); + + } else if (strcmp(argv[1], "--ID3frames-list") == 0) { + ListID3FrameIDstrings(); + exit(0); + + } else if (strcmp(argv[1], "--imagetype-list") == 0) { + List_imagtype_strings(); + exit(0); + } + } - } else if ( (strcmp(argv[1],"--3gp-help") == 0) || (strcmp(argv[1],"-3gp-help") == 0) || (strcmp(argv[1],"--3gp-h") == 0) ) { - fprintf(stdout, "%s\n", _3gpHelp_text); exit(0); + if (argc == 3 && + (strcmp(argv[2], "--brands") == 0 || strcmp(argv[2], "-brands") == 0)) { + APar_ExtractBrands(argv[1]); + exit(0); + } + + int extr = 99; + total_args = argc; + char *ISObasemediafile = argv[1]; + + TestFileExistence(ISObasemediafile, true); + + char *padding_options = getenv("AP_PADDING"); + ExtractPaddingPrefs(padding_options); + + // it would probably be better to test output_file if provided & if + // --overWrite was provided.... probably only of use on Windows - and I'm not + // on it. + if (strlen(ISObasemediafile) + 11 > MAXPATHLEN) { + fprintf(stderr, + "%c %s", + '\a', + "AtomicParsley error: filename/filepath was too long.\n"); + exit(1); + } + + if (argc > 3 && strcmp(argv[2], "--DeepScan") == 0) { + deep_atom_scan = true; + APar_ScanAtoms(ISObasemediafile, true); + } - } else if ( (strcmp(argv[1],"--ISO-help") == 0) || (strcmp(argv[1],"--iso-help") == 0) || (strcmp(argv[1],"-Ih") == 0) ) { - fprintf(stdout, "%s\n", ISOHelp_text); exit(0); + while (1) { + static struct option long_options[] = { + {"help", 0, NULL, OPT_HELP}, + {"test", optional_argument, NULL, OPT_TEST}, + {"textdata", optional_argument, NULL, OPT_ShowTextData}, + {"extractPix", 0, NULL, OPT_ExtractPix}, + {"extractPixToPath", required_argument, NULL, OPT_ExtractPixToPath}, + {"artist", required_argument, NULL, Meta_artist}, + {"artDirector", required_argument, NULL, Meta_artDirector}, + {"arranger", required_argument, NULL, Meta_arranger}, + {"author", required_argument, NULL, Meta_author}, + {"conductor", required_argument, NULL, Meta_conductor}, + {"director", required_argument, NULL, Meta_director}, + {"originalArtist", required_argument, NULL, Meta_originalArtist}, + {"producer", required_argument, NULL, Meta_producer}, + // { "performer", required_argument, NULL, Meta_performer + // }, + {"soundEngineer", required_argument, NULL, Meta_soundEngineer}, + {"soloist", required_argument, NULL, Meta_soloist}, + {"executiveProducer", required_argument, NULL, Meta_executiveProducer}, + {"title", required_argument, NULL, Meta_songtitle}, + {"subtitle", required_argument, NULL, Meta_subtitle}, + {"album", required_argument, NULL, Meta_album}, + {"genre", required_argument, NULL, Meta_genre}, + {"tracknum", required_argument, NULL, Meta_tracknum}, + {"disknum", required_argument, NULL, Meta_disknum}, + {"comment", required_argument, NULL, Meta_comment}, + {"year", required_argument, NULL, Meta_year}, + {"lyrics", required_argument, NULL, Meta_lyrics}, + {"lyricsFile", required_argument, NULL, Meta_lyrics_file}, + {"composer", required_argument, NULL, Meta_composer}, + {"copyright", required_argument, NULL, Meta_copyright}, + {"grouping", required_argument, NULL, Meta_grouping}, + {"albumArtist", required_argument, NULL, Meta_album_artist}, + {"compilation", required_argument, NULL, Meta_compilation}, + {"hdvideo", required_argument, NULL, Meta_hdvideo}, + {"advisory", required_argument, NULL, Meta_advisory}, + {"bpm", required_argument, NULL, Meta_BPM}, + {"artwork", required_argument, NULL, Meta_artwork}, + {"stik", required_argument, NULL, Meta_stik}, + {"description", required_argument, NULL, Meta_description}, + {"longdesc", required_argument, NULL, Meta_longdescription}, + {"storedesc", required_argument, NULL, Meta_storedescription}, + {"Rating", required_argument, NULL, Meta_Rating}, + {"TVNetwork", required_argument, NULL, Meta_TV_Network}, + {"TVShowName", required_argument, NULL, Meta_TV_ShowName}, + {"TVEpisode", required_argument, NULL, Meta_TV_Episode}, + {"TVEpisodeNum", required_argument, NULL, Meta_TV_EpisodeNumber}, + {"TVSeasonNum", required_argument, NULL, Meta_TV_SeasonNumber}, + {"podcastFlag", required_argument, NULL, Meta_podcastFlag}, + {"keyword", required_argument, NULL, Meta_keyword}, + {"category", required_argument, NULL, Meta_category}, + {"podcastURL", required_argument, NULL, Meta_podcast_URL}, + {"podcastGUID", required_argument, NULL, Meta_podcast_GUID}, + {"purchaseDate", required_argument, NULL, Meta_PurchaseDate}, + {"encodingTool", required_argument, NULL, Meta_EncodingTool}, + {"encodedBy", required_argument, NULL, Meta_EncodedBy}, + {"apID", required_argument, NULL, Meta_apID}, + {"cnID", required_argument, NULL, Meta_cnID}, + {"geID", required_argument, NULL, Meta_geID}, + {"xID", required_argument, NULL, Meta_xID}, + {"gapless", required_argument, NULL, Meta_PlayGapless}, + {"sortOrder", required_argument, NULL, Meta_SortOrder}, + + {"rDNSatom", required_argument, NULL, Meta_ReverseDNS_Form}, + {"contentRating", required_argument, NULL, Meta_rDNS_rating}, + + {"tagtime", optional_argument, NULL, Meta_StandardDate}, + {"information", required_argument, NULL, Meta_Information}, + {"url", required_argument, NULL, Meta_URL}, + {"meta-uuid", required_argument, NULL, Meta_uuid}, + {"extract-uuids", optional_argument, NULL, Opt_Extract_all_uuids}, + {"extract1uuid", required_argument, NULL, Opt_Extract_a_uuid}, + {"iPod-uuid", required_argument, NULL, Opt_Ipod_AVC_uuid}, + + {"freefree", optional_argument, NULL, Opt_FreeFree}, + {"metaEnema", 0, NULL, Metadata_Purge}, + {"manualAtomRemove", required_argument, NULL, Manual_atom_removal}, + {"udtaEnema", 0, NULL, UserData_Purge}, + {"foobar2000Enema", 0, NULL, foobar_purge}, + {"metaDump", 0, NULL, Meta_dump}, + {"output", required_argument, NULL, OPT_OutputFile}, + {"preventOptimizing", 0, NULL, OPT_NoOptimize}, + {"overWrite", 0, NULL, OPT_OverWrite}, +#if defined(_WIN32) + {"preserveTime", 0, NULL, OPT_PreserveTimeStamps}, +#endif + {"ISO-copyright", required_argument, NULL, ISO_Copyright}, - } else if ( (strcmp(argv[1],"--file-help") == 0) || (strcmp(argv[1],"-file-help") == 0) || (strcmp(argv[1],"-fh") == 0) ) { - fprintf(stdout, "%s\n", fileLevelHelp_text); exit(0); + {"3gp-title", required_argument, NULL, _3GP_Title}, + {"3gp-author", required_argument, NULL, _3GP_Author}, + {"3gp-performer", required_argument, NULL, _3GP_Performer}, + {"3gp-genre", required_argument, NULL, _3GP_Genre}, + {"3gp-description", required_argument, NULL, _3GP_Description}, + {"3gp-copyright", required_argument, NULL, _3GP_Copyright}, + {"3gp-album", required_argument, NULL, _3GP_Album}, + {"3gp-year", required_argument, NULL, _3GP_Year}, + + {"3gp-rating", required_argument, NULL, _3GP_Rating}, + {"3gp-classification", required_argument, NULL, _3GP_Classification}, + {"3gp-keyword", required_argument, NULL, _3GP_Keyword}, + {"3gp-location", required_argument, NULL, _3GP_Location}, + + {"ID3Tag", required_argument, NULL, Meta_ID3v2Tag}, + + {"DeepScan", 0, &extr, 1}, + {"movementCount", required_argument, NULL, Meta_movementCount}, + {"movementName", required_argument, NULL, Meta_movementName}, + {"movementNumber", required_argument, NULL, Meta_movementNumber}, + {"showWorkMovement", required_argument, NULL, Meta_showWorkMovement}, + {"work", required_argument, NULL, Meta_work}, - } else if ( (strcmp(argv[1],"--uuid-help") == 0) || (strcmp(argv[1],"-uuid-help") == 0) || (strcmp(argv[1],"-uh") == 0) ) { - fprintf(stdout, "%s\n", uuidHelp_text); exit(0); + {0, 0, 0, 0} + }; - } else if ( (strcmp(argv[1],"--reverseDNS-help") == 0) || (strcmp(argv[1],"-rDNS-help") == 0) || (strcmp(argv[1],"-rh") == 0) ) { - fprintf(stdout, "%s\n", rDNSHelp_text); exit(0); + int c = -1; + int option_index = 0; - } else if ( (strcmp(argv[1],"--ID3-help") == 0) || (strcmp(argv[1],"-ID3-help") == 0) || (strcmp(argv[1],"-ID3h") == 0) ) { - fprintf(stdout, "%s\n", ID3Help_text); exit(0); + c = getopt_long(argc, + argv, + "hTtEe:a:b:c:d:f:g:i:k:l:n:o:p:q::u:w:x:y:z:A:B:C:D:F:G:H:" + "I:J:K:L:MN:QR:S:U:WXV:ZP", + long_options, + &option_index); - } else if ( strcmp(argv[1], "--genre-list") == 0 ) { - ListGenresValues(); exit(0); + if (c == -1) { + if (argc < 3 && argc > 2) { + APar_ScanAtoms(ISObasemediafile, true); + APar_PrintAtomicTree(); + } + break; + } - } else if ( strcmp(argv[1], "--stik-list") == 0 ) { - ListStikValues(); exit(0); + signal(SIGTERM, kill_signal); + signal(SIGINT, kill_signal); - } else if ( strcmp(argv[1], "--language-list") == 0 || - strcmp(argv[1], "--languages-list") == 0 || - strcmp(argv[1], "--list-language") == 0 || - strcmp(argv[1], "--list-languages") == 0 || - strcmp(argv[1], "-ll") == 0) { - ListLanguageCodes(); exit(0); + switch (c) { + // "optind" represents the count of arguments up to and including its + // optional flag: + + case '?': + return 1; + + case OPT_HELP: { + fprintf(stdout, "%s", longHelp_text); + return 0; + } - } else if (strcmp(argv[1], "--ratings-list") == 0) { - ListMediaRatings(); exit(0); + case OPT_TEST: { + deep_atom_scan = true; + APar_ScanAtoms(ISObasemediafile, true); + APar_PrintAtomicTree(); + if (argv[optind]) { + if (strcmp(argv[optind], "+dates") == 0) { + APar_ExtractDetails(APar_OpenISOBaseMediaFile(ISObasemediafile, true), + SHOW_TRACK_INFO + SHOW_DATE_INFO); + } else { + APar_ExtractDetails(APar_OpenISOBaseMediaFile(ISObasemediafile, true), + SHOW_TRACK_INFO); + } + } + APar_OpenISOBaseMediaFile(ISObasemediafile, false); + break; + } - } else if (strcmp(argv[1], "--genre-movie-id-list") == 0) { - ListMovieGenreIDValues(); exit(0); + case OPT_ShowTextData: { + if (argv[optind]) { // for utilities that write iTunes-style metadata into + // 3gp branded files + APar_ExtractBrands(ISObasemediafile); + deep_atom_scan = true; + APar_ScanAtoms(ISObasemediafile); - } else if (strcmp(argv[1], "--genre-tv-id-list") == 0) { - ListTVGenreIDValues(); exit(0); + APar_OpenISOBaseMediaFile(ISObasemediafile, true); - } else if (strcmp(argv[1], "--ID3frames-list") == 0) { - ListID3FrameIDstrings(); exit(0); + if (strcmp(argv[optind], "+") == 0) { + APar_Print_iTunesData(ISObasemediafile, + NULL, + PRINT_FREE_SPACE + PRINT_PADDING_SPACE + + PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE, + PRINT_DATA); + } else { + fprintf(stdout, "---------------------------\n"); + APar_Print_ISO_UserData_per_track(); - } else if (strcmp(argv[1], "--imagetype-list") == 0) { - List_imagtype_strings(); exit(0); + AtomicInfo *iTuneslistAtom = + APar_FindAtom("moov.udta.meta.ilst", false, SIMPLE_ATOM, 0); + if (iTuneslistAtom != NULL) { + fprintf( + stdout, + "---------------------------\n iTunes-style metadata tags:\n"); + APar_Print_iTunesData(ISObasemediafile, + NULL, + PRINT_FREE_SPACE + PRINT_PADDING_SPACE + + PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE, + PRINT_DATA, + iTuneslistAtom); + } + fprintf(stdout, "---------------------------\n"); } + + } else { + deep_atom_scan = true; + APar_ScanAtoms(ISObasemediafile); + APar_OpenISOBaseMediaFile(ISObasemediafile, true); + + if (metadata_style >= THIRD_GEN_PARTNER) { + APar_PrintUserDataAssests(); + } else if (metadata_style == ITUNES_STYLE) { + APar_Print_iTunesData(ISObasemediafile, + NULL, + 0, + PRINT_DATA); // false, don't try to extractPix + APar_Print_APuuid_atoms(ISObasemediafile, NULL, PRINT_DATA); + } + } + APar_OpenISOBaseMediaFile(ISObasemediafile, false); + break; } - if ( argc == 3 && (strcmp(argv[2], "--brands") == 0 || strcmp(argv[2], "-brands") == 0) ) { - APar_ExtractBrands(argv[1]); exit(0); + case OPT_ExtractPix: { + char *base_path = (char *)malloc(sizeof(char) * MAXPATHLEN + 1); + memset(base_path, 0, MAXPATHLEN + 1); + + GetBasePath(ISObasemediafile, base_path); + APar_ScanAtoms(ISObasemediafile); + APar_OpenISOBaseMediaFile(ISObasemediafile, true); + APar_Print_iTunesData( + ISObasemediafile, + base_path, + 0, + EXTRACT_ARTWORK); // exportPix to stripped ISObasemediafile path + APar_OpenISOBaseMediaFile(ISObasemediafile, false); + + free(base_path); + base_path = NULL; + break; } - int extr = 99; - total_args = argc; - char* ISObasemediafile = argv[1]; + case OPT_ExtractPixToPath: { + APar_ScanAtoms(ISObasemediafile); + APar_OpenISOBaseMediaFile(ISObasemediafile, true); + APar_Print_iTunesData(ISObasemediafile, + optarg, + 0, + EXTRACT_ARTWORK); // exportPix to a different path + APar_OpenISOBaseMediaFile(ISObasemediafile, false); + break; + } - TestFileExistence(ISObasemediafile, true); + case Meta_artist: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "artist")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251ART.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - char* padding_options = getenv("AP_PADDING"); - ExtractPaddingPrefs(padding_options); + case Meta_artDirector: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "artDirector")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251ard.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - //it would probably be better to test output_file if provided & if --overWrite was provided.... probably only of use on Windows - and I'm not on it. - if (strlen(ISObasemediafile) + 11 > MAXPATHLEN) { - fprintf(stderr, "%c %s", '\a', "AtomicParsley error: filename/filepath was too long.\n"); - exit(1); + case Meta_arranger: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "arranger")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251arg.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; } - if ( argc > 3 && strcmp(argv[2], "--DeepScan") == 0) { - deep_atom_scan = true; - APar_ScanAtoms(ISObasemediafile, true); + case Meta_author: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "author")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251aut.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; } - while (1) { - static struct option long_options[] = { - { "help", 0, NULL, OPT_HELP }, - { "test", optional_argument, NULL, OPT_TEST }, - { "textdata", optional_argument, NULL, OPT_ShowTextData }, - { "extractPix", 0, NULL, OPT_ExtractPix }, - { "extractPixToPath", required_argument, NULL, OPT_ExtractPixToPath }, - { "artist", required_argument, NULL, Meta_artist }, - { "artDirector", required_argument, NULL, Meta_artDirector }, - { "arranger", required_argument, NULL, Meta_arranger }, - { "author", required_argument, NULL, Meta_author }, - { "conductor", required_argument, NULL, Meta_conductor }, - { "director", required_argument, NULL, Meta_director }, - { "originalArtist", required_argument, NULL, Meta_originalArtist }, - { "producer", required_argument, NULL, Meta_producer }, -// { "performer", required_argument, NULL, Meta_performer }, - { "soundEngineer", required_argument, NULL, Meta_soundEngineer }, - { "soloist", required_argument, NULL, Meta_soloist }, - { "executiveProducer",required_argument, NULL, Meta_executiveProducer }, - { "title", required_argument, NULL, Meta_songtitle }, - { "subtitle", required_argument, NULL, Meta_subtitle }, - { "album", required_argument, NULL, Meta_album }, - { "genre", required_argument, NULL, Meta_genre }, - { "tracknum", required_argument, NULL, Meta_tracknum }, - { "disknum", required_argument, NULL, Meta_disknum }, - { "comment", required_argument, NULL, Meta_comment }, - { "year", required_argument, NULL, Meta_year }, - { "lyrics", required_argument, NULL, Meta_lyrics }, - { "lyricsFile", required_argument, NULL, Meta_lyrics_file }, - { "composer", required_argument, NULL, Meta_composer }, - { "copyright", required_argument, NULL, Meta_copyright }, - { "grouping", required_argument, NULL, Meta_grouping }, - { "albumArtist", required_argument, NULL, Meta_album_artist }, - { "compilation", required_argument, NULL, Meta_compilation }, - { "hdvideo", required_argument, NULL, Meta_hdvideo }, - { "advisory", required_argument, NULL, Meta_advisory }, - { "bpm", required_argument, NULL, Meta_BPM }, - { "artwork", required_argument, NULL, Meta_artwork }, - { "stik", required_argument, NULL, Meta_stik }, - { "description", required_argument, NULL, Meta_description }, - { "longdesc", required_argument, NULL, Meta_longdescription }, - { "storedesc", required_argument, NULL, Meta_storedescription }, - { "Rating", required_argument, NULL, Meta_Rating }, - { "TVNetwork", required_argument, NULL, Meta_TV_Network }, - { "TVShowName", required_argument, NULL, Meta_TV_ShowName }, - { "TVEpisode", required_argument, NULL, Meta_TV_Episode }, - { "TVEpisodeNum", required_argument, NULL, Meta_TV_EpisodeNumber }, - { "TVSeasonNum", required_argument, NULL, Meta_TV_SeasonNumber }, - { "podcastFlag", required_argument, NULL, Meta_podcastFlag }, - { "keyword", required_argument, NULL, Meta_keyword }, - { "category", required_argument, NULL, Meta_category }, - { "podcastURL", required_argument, NULL, Meta_podcast_URL }, - { "podcastGUID", required_argument, NULL, Meta_podcast_GUID }, - { "purchaseDate", required_argument, NULL, Meta_PurchaseDate }, - { "encodingTool", required_argument, NULL, Meta_EncodingTool }, - { "encodedBy", required_argument, NULL, Meta_EncodedBy }, - { "apID", required_argument, NULL, Meta_apID }, - { "cnID", required_argument, NULL, Meta_cnID }, - { "geID", required_argument, NULL, Meta_geID }, - { "xID", required_argument, NULL, Meta_xID }, - { "gapless", required_argument, NULL, Meta_PlayGapless }, - { "sortOrder", required_argument, NULL, Meta_SortOrder } , - - { "rDNSatom", required_argument, NULL, Meta_ReverseDNS_Form }, - { "contentRating", required_argument, NULL, Meta_rDNS_rating }, - - { "tagtime", optional_argument, NULL, Meta_StandardDate }, - { "information", required_argument, NULL, Meta_Information }, - { "url", required_argument, NULL, Meta_URL }, - { "meta-uuid", required_argument, NULL, Meta_uuid }, - { "extract-uuids", optional_argument, NULL, Opt_Extract_all_uuids }, - { "extract1uuid", required_argument, NULL, Opt_Extract_a_uuid }, - { "iPod-uuid", required_argument, NULL, Opt_Ipod_AVC_uuid }, - - { "freefree", optional_argument, NULL, Opt_FreeFree }, - { "metaEnema", 0, NULL, Metadata_Purge }, - { "manualAtomRemove", required_argument, NULL, Manual_atom_removal }, - { "udtaEnema", 0, NULL, UserData_Purge }, - { "foobar2000Enema", 0, NULL, foobar_purge }, - { "metaDump", 0, NULL, Meta_dump }, - { "output", required_argument, NULL, OPT_OutputFile }, - { "preventOptimizing",0, NULL, OPT_NoOptimize }, - { "overWrite", 0, NULL, OPT_OverWrite }, -#if defined (_WIN32) - { "preserveTime", 0, NULL, OPT_PreserveTimeStamps }, -#endif - { "ISO-copyright", required_argument, NULL, ISO_Copyright }, + case Meta_conductor: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "conductor")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251con.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - { "3gp-title", required_argument, NULL, _3GP_Title }, - { "3gp-author", required_argument, NULL, _3GP_Author }, - { "3gp-performer", required_argument, NULL, _3GP_Performer }, - { "3gp-genre", required_argument, NULL, _3GP_Genre }, - { "3gp-description", required_argument, NULL, _3GP_Description }, - { "3gp-copyright", required_argument, NULL, _3GP_Copyright }, - { "3gp-album", required_argument, NULL, _3GP_Album }, - { "3gp-year", required_argument, NULL, _3GP_Year }, - - { "3gp-rating", required_argument, NULL, _3GP_Rating }, - { "3gp-classification", required_argument, NULL, _3GP_Classification }, - { "3gp-keyword", required_argument, NULL, _3GP_Keyword }, - { "3gp-location", required_argument, NULL, _3GP_Location }, + case Meta_director: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "director")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251dir.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - { "ID3Tag", required_argument, NULL, Meta_ID3v2Tag }, + case Meta_originalArtist: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "originalArtist")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251ope.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - { "DeepScan", 0, &extr, 1 }, + case Meta_producer: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "producer")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251prd.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - { 0, 0, 0, 0 } - }; + /* + case Meta_performer : { + APar_ScanAtoms(ISObasemediafile); + if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, + "performer") ) { char major_brand[4]; UInt32_TO_String4(brand, + &*major_brand); APar_assert(false, 4, &*major_brand); break; + } + AtomicInfo* artistData_atom = + APar_MetaData_atom_Init("moov.udta.meta.ilst.\251prf.data", optarg, + AtomFlags_Data_Text); APar_Unified_atom_Put(artistData_atom, optarg, + UTF8_iTunesStyle_256glyphLimited, 0, 0); break; + } + */ + + case Meta_soundEngineer: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "soundEngineer")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251sne.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - int c = -1; - int option_index = 0; + case Meta_soloist: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "soloist")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251sol.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - c = getopt_long(argc, argv, "hTtEe:a:b:c:d:f:g:i:k:l:n:o:p:q::u:w:x:y:z:A:B:C:D:F:G:H:I:J:K:L:MN:QR:S:U:WXV:ZP", long_options, &option_index); + case Meta_executiveProducer: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert( + metadata_style == ITUNES_STYLE, 1, "executiveProducer")) { + char major_brand[4]; + UInt32_TO_String4(brand, &*major_brand); + APar_assert(false, 4, &*major_brand); + break; + } + AtomicInfo *artistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251xpd.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - if (c == -1) { - if (argc < 3 && argc > 2) { - APar_ScanAtoms(ISObasemediafile, true); - APar_PrintAtomicTree(); - } + case Meta_songtitle: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "title")) { break; + } + + AtomicInfo *titleData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251nam.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; } - signal(SIGTERM, kill_signal); - signal(SIGINT, kill_signal); + case Meta_subtitle: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "subtitle")) { + break; + } - switch(c) { - // "optind" represents the count of arguments up to and including its optional flag: + AtomicInfo *titleData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251st3.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case '?': return 1; + case Meta_album: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "album")) { + break; + } - case OPT_HELP: { - fprintf (stdout,"%s", longHelp_text); return 0; - } - - case OPT_TEST: { - deep_atom_scan = true; - APar_ScanAtoms(ISObasemediafile, true); - APar_PrintAtomicTree(); - if (argv[optind]) { - if (strcmp(argv[optind], "+dates") == 0) { - APar_ExtractDetails( APar_OpenISOBaseMediaFile(ISObasemediafile, true), SHOW_TRACK_INFO + SHOW_DATE_INFO ); - } else { - APar_ExtractDetails( APar_OpenISOBaseMediaFile(ISObasemediafile, true), SHOW_TRACK_INFO); - } - } - APar_OpenISOBaseMediaFile(ISObasemediafile, false); - break; - } + AtomicInfo *albumData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251alb.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + albumData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case OPT_ShowTextData: { - if (argv[optind]) { //for utilities that write iTunes-style metadata into 3gp branded files - APar_ExtractBrands(ISObasemediafile); - deep_atom_scan=true; - APar_ScanAtoms(ISObasemediafile); - - APar_OpenISOBaseMediaFile(ISObasemediafile, true); - - if (strcmp(argv[optind], "+") == 0) { - APar_Print_iTunesData(ISObasemediafile, NULL, PRINT_FREE_SPACE + PRINT_PADDING_SPACE + PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE, PRINT_DATA ); - } else { - fprintf(stdout, "---------------------------\n"); - APar_Print_ISO_UserData_per_track(); - - AtomicInfo* iTuneslistAtom = APar_FindAtom("moov.udta.meta.ilst", false, SIMPLE_ATOM, 0); - if (iTuneslistAtom != NULL) { - fprintf(stdout, "---------------------------\n iTunes-style metadata tags:\n"); - APar_Print_iTunesData(ISObasemediafile, NULL, PRINT_FREE_SPACE + PRINT_PADDING_SPACE + PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE, PRINT_DATA, iTuneslistAtom ); - } - fprintf(stdout, "---------------------------\n"); - } - - } else { - deep_atom_scan=true; - APar_ScanAtoms(ISObasemediafile); - APar_OpenISOBaseMediaFile(ISObasemediafile, true); - - if (metadata_style >= THIRD_GEN_PARTNER) { - APar_PrintUserDataAssests(); - } else if (metadata_style == ITUNES_STYLE) { - APar_Print_iTunesData(ISObasemediafile, NULL, 0, PRINT_DATA); //false, don't try to extractPix - APar_Print_APuuid_atoms(ISObasemediafile, NULL, PRINT_DATA); - } - } - APar_OpenISOBaseMediaFile(ISObasemediafile, false); - break; - } + case Meta_genre: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "genre")) { + break; + } - case OPT_ExtractPix: { - char* base_path=(char*)malloc(sizeof(char)*MAXPATHLEN+1); - memset(base_path, 0, MAXPATHLEN +1); - - GetBasePath( ISObasemediafile, base_path ); - APar_ScanAtoms(ISObasemediafile); - APar_OpenISOBaseMediaFile(ISObasemediafile, true); - APar_Print_iTunesData(ISObasemediafile, base_path, 0, EXTRACT_ARTWORK); //exportPix to stripped ISObasemediafile path - APar_OpenISOBaseMediaFile(ISObasemediafile, false); + APar_MetaData_atomGenre_Set(optarg); + break; + } - free(base_path); - base_path = NULL; - break; - } + case Meta_tracknum: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "track number")) { + break; + } - case OPT_ExtractPixToPath: { - APar_ScanAtoms(ISObasemediafile); - APar_OpenISOBaseMediaFile(ISObasemediafile, true); - APar_Print_iTunesData(ISObasemediafile, optarg, 0, EXTRACT_ARTWORK); //exportPix to a different path - APar_OpenISOBaseMediaFile(ISObasemediafile, false); - break; - } + uint16_t pos_in_total = 0; + uint16_t the_total = 0; + if (strrchr(optarg, '/') != NULL) { + + char *duplicate_info = optarg; + char *item_stat = strsep(&duplicate_info, "/"); + sscanf(item_stat, "%" SCNu16, &pos_in_total); + item_stat = strsep(&duplicate_info, "/"); + sscanf(item_stat, "%" SCNu16, &the_total); + } else { + sscanf(optarg, "%" SCNu16, &pos_in_total); + } + + AtomicInfo *tracknumData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.trkn.data", optarg, AtomFlags_Data_Binary); + // tracknum: [0, 0, 0, 0, 0, 0, 0, pos_in_total, 0, the_total, 0, 0]; + // BUT that first uint32_t is already accounted for in + // APar_MetaData_atom_Init + APar_Unified_atom_Put( + tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); + APar_Unified_atom_Put(tracknumData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + pos_in_total, + 16); + APar_Unified_atom_Put(tracknumData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + the_total, + 16); + APar_Unified_atom_Put( + tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); + break; + } - case Meta_artist : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "artist") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.ART.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + case Meta_disknum: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "disc number")) { + break; + } - case Meta_artDirector : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "artDirector") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.ard.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + uint16_t pos_in_total = 0; + uint16_t the_total = 0; + if (strrchr(optarg, '/') != NULL) { + + char *duplicate_info = optarg; + char *item_stat = strsep(&duplicate_info, "/"); + sscanf(item_stat, "%" SCNu16, &pos_in_total); + item_stat = strsep(&duplicate_info, "/"); + sscanf(item_stat, "%" SCNu16, &the_total); + + } else { + sscanf(optarg, "%" SCNu16, &pos_in_total); + } + + AtomicInfo *disknumData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.disk.data", optarg, AtomFlags_Data_Binary); + // disknum: [0, 0, 0, 0, 0, 0, 0, pos_in_total, 0, the_total]; BUT that + // first uint32_t is already accounted for in APar_MetaData_atom_Init + APar_Unified_atom_Put( + disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); + APar_Unified_atom_Put(disknumData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + pos_in_total, + 16); + APar_Unified_atom_Put(disknumData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + the_total, + 16); + break; + } - case Meta_arranger : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "arranger") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.arg.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + case Meta_comment: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "comment")) { + break; + } - case Meta_author : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "author") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.aut.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *commentData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251cmt.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + commentData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_conductor : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "conductor") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.con.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + case Meta_year: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "year")) { + break; + } - case Meta_director : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "director") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.dir.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *yearData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251day.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + yearData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_originalArtist : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "originalArtist") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.ope.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + case Meta_lyrics: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics")) { + break; + } - case Meta_producer : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "producer") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.prd.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *lyricsData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251lyr.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + lyricsData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); + break; + } -/* - case Meta_performer : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "performer") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.prf.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } -*/ + case Meta_lyrics_file: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics")) { + break; + } - case Meta_soundEngineer : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "soundEngineer") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.sne.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + APar_MetaData_atomLyrics_Set(optarg); + break; + } - case Meta_soloist : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "soloist") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.sol.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + case Meta_composer: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "composer")) { + break; + } - case Meta_executiveProducer : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "executiveProducer") ) { - char major_brand[4]; - UInt32_TO_String4(brand, &*major_brand); - APar_assert(false, 4, &*major_brand); - break; - } - AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.xpd.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *composerData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251wrt.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + composerData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_songtitle : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "title") ) { - break; - } + case Meta_copyright: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "copyright")) { + break; + } - AtomicInfo* titleData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.nam.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *copyrightData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.cprt.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + copyrightData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_subtitle : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "subtitle") ) { - break; - } + case Meta_grouping: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "grouping")) { + break; + } - AtomicInfo* titleData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.st3.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *groupingData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251grp.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + groupingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_album : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "album") ) { - break; - } + case Meta_compilation: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "compilation")) { + break; + } - AtomicInfo* albumData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.alb.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(albumData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) { + APar_RemoveAtom("moov.udta.meta.ilst.cpil.data", VERSIONED_ATOM, 0); + } else { + // compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t + // is already accounted for in APar_MetaData_atom_Init + AtomicInfo *compilationData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.cpil.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put(compilationData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + 1, + 8); // a hard coded uint8_t of: 1 is compilation + } + break; + } - case Meta_genre : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "genre") ) { - break; - } + case Meta_hdvideo: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "hdvideo")) { + break; + } - APar_MetaData_atomGenre_Set(optarg); - break; + if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0 || + strcmp(optarg, "0") == 0) { + APar_RemoveAtom("moov.udta.meta.ilst.hdvd.data", VERSIONED_ATOM, 0); + } else { + // compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t + // is already accounted for in APar_MetaData_atom_Init + AtomicInfo *hdvideoData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.hdvd.data", optarg, AtomFlags_Data_UInt); + + uint8_t hdvideo_value = 0; + if (strcmp(optarg, "true") == 0) { + hdvideo_value = 1; + } else { + sscanf(optarg, "%" SCNu8, &hdvideo_value); } - case Meta_tracknum : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "track number") ) { - break; - } + APar_Unified_atom_Put(hdvideoData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + hdvideo_value, + 8); + } + break; + } - uint16_t pos_in_total = 0; - uint16_t the_total = 0; - if (strrchr(optarg, '/') != NULL) { - - char* duplicate_info = optarg; - char* item_stat = strsep(&duplicate_info,"/"); - sscanf(item_stat, "%" SCNu16, &pos_in_total); - item_stat = strsep(&duplicate_info,"/"); - sscanf(item_stat, "%" SCNu16, &the_total); - } else { - sscanf(optarg, "%" SCNu16, &pos_in_total); - } + case Meta_BPM: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "BPM")) { + break; + } - AtomicInfo* tracknumData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.trkn.data", optarg, AtomFlags_Data_Binary); - //tracknum: [0, 0, 0, 0, 0, 0, 0, pos_in_total, 0, the_total, 0, 0]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); - APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, pos_in_total, 16); - APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, the_total, 16); - APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); - break; - } + if (strcmp(optarg, "0") == 0 || strlen(optarg) == 0) { + APar_RemoveAtom("moov.udta.meta.ilst.tmpo.data", VERSIONED_ATOM, 0); + } else { + uint16_t bpm_value = 0; + sscanf(optarg, "%" SCNu16, &bpm_value); + // bpm is [0, 0, 0, 0, 0, bpm_value]; BUT that first uint32_t is + // already accounted for in APar_MetaData_atom_Init + AtomicInfo *bpmData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.tmpo.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put(bpmData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + bpm_value, + 16); + } + break; + } - case Meta_disknum : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "disc number") ) { - break; - } + case Meta_advisory: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "content advisory")) { + break; + } - uint16_t pos_in_total = 0; - uint16_t the_total = 0; - if (strrchr(optarg, '/') != NULL) { - - char* duplicate_info = optarg; - char* item_stat = strsep(&duplicate_info,"/"); - sscanf(item_stat, "%" SCNu16, &pos_in_total); - item_stat = strsep(&duplicate_info,"/"); - sscanf(item_stat, "%" SCNu16, &the_total); + if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) { + APar_RemoveAtom("moov.udta.meta.ilst.rtng.data", VERSIONED_ATOM, 0); + } else { + uint8_t rating_value = 0; + if (strcmp(optarg, "clean") == 0) { + rating_value = 2; // only \02 is clean + } else if (strcmp(optarg, "explicit") == 0) { + rating_value = 4; // most non \00, \02 numbers are allowed + } + // rating is [0, 0, 0, 0, rating_value]; BUT that first uint32_t is + // already accounted for in APar_MetaData_atom_Init + AtomicInfo *advisoryData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.rtng.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put(advisoryData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + rating_value, + 8); + } + break; + } - } else { - sscanf(optarg, "%" SCNu16, &pos_in_total); - } + case Meta_artwork: { // handled differently: there can be multiple + // "moov.udta.meta.ilst.covr.data" atoms + char *env_PicOptions = getenv("PIC_OPTIONS"); + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "coverart")) { + break; + } - AtomicInfo* disknumData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.disk.data", optarg, AtomFlags_Data_Binary); - //disknum: [0, 0, 0, 0, 0, 0, 0, pos_in_total, 0, the_total]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); - APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, pos_in_total, 16); - APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, the_total, 16); - break; - } + APar_MetaData_atomArtwork_Set(optarg, env_PicOptions); + break; + } - case Meta_comment : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "comment") ) { - break; - } + case Meta_stik: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "'stik'")) { + break; + } - AtomicInfo* commentData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.cmt.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(commentData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) { + APar_RemoveAtom("moov.udta.meta.ilst.stik.data", VERSIONED_ATOM, 0); + } else { + uint8_t stik_value = 0; + + if (strncmp(optarg, "value=", 6) == 0) { + char *stik_val_str_ptr = optarg; + strsep(&stik_val_str_ptr, "="); + stik_value = strtoul(stik_val_str_ptr, NULL, 10); + } else { + stiks *return_stik = MatchStikString(optarg); + if (return_stik != NULL) { + stik_value = return_stik->stik_number; + if (strcmp(optarg, "Audiobook") == 0) { + forced_suffix_type = FORCE_M4B_TYPE; + } + } + } + // stik is [0, 0, 0, 0, stik_value]; BUT that first uint32_t is + // already accounted for in APar_MetaData_atom_Init + AtomicInfo *stikData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.stik.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put(stikData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + stik_value, + 8); + } + break; + } - case Meta_year : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "year") ) { - break; - } + case Meta_EncodingTool: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "encoding tool")) { + break; + } - AtomicInfo* yearData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.day.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(yearData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *encodingtoolData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251too.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put(encodingtoolData_atom, + optarg, + UTF8_iTunesStyle_256glyphLimited, + 0, + 0); + break; + } - case Meta_lyrics : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics") ) { - break; - } + case Meta_EncodedBy: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "encoded by")) { + break; + } - AtomicInfo* lyricsData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.lyr.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(lyricsData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); - break; - } + AtomicInfo *encodedbyData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251enc.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + encodedbyData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_lyrics_file : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics") ) { - break; - } + case Meta_apID: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "Account Name")) { + break; + } - APar_MetaData_atomLyrics_Set(optarg); - break; - } + AtomicInfo *apIDData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.apID.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + apIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_composer : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "composer") ) { - break; - } + case Meta_description: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "description")) { + break; + } - AtomicInfo* composerData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.wrt.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(composerData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.desc.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + descriptionData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_copyright : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "copyright") ) { - break; - } + case Meta_longdescription: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "longdesc")) { + break; + } - AtomicInfo* copyrightData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.cprt.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(copyrightData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.ldes.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); + break; + } - case Meta_grouping : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "grouping") ) { - break; - } + case Meta_storedescription: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "storedesc")) { + break; + } - AtomicInfo* groupingData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.grp.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(groupingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } + AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.sdes.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); + break; + } - case Meta_compilation : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "compilation") ) { - break; - } + case Meta_Rating: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "Rating")) { + break; + } - if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) { - APar_RemoveAtom("moov.udta.meta.ilst.cpil.data", VERSIONED_ATOM, 0); - } else { - //compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - AtomicInfo* compilationData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.cpil.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(compilationData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is compilation - } - break; - } + AtomicInfo *ratingData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.rate.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + ratingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_hdvideo : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "hdvideo") ) { - break; - } + case Meta_TV_Network: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Network")) { + break; + } - if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) { - APar_RemoveAtom("moov.udta.meta.ilst.hdvd.data", VERSIONED_ATOM, 0); - } else { - //compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - AtomicInfo* hdvideoData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.hdvd.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(hdvideoData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is compilation - } - break; - } + AtomicInfo *tvnetworkData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.tvnn.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + tvnetworkData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_BPM : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "BPM") ) { - break; - } + case Meta_TV_ShowName: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Show name")) { + break; + } - if (strcmp(optarg, "0") == 0 || strlen(optarg) == 0) { - APar_RemoveAtom("moov.udta.meta.ilst.tmpo.data", VERSIONED_ATOM, 0); - } else { - uint16_t bpm_value = 0; - sscanf(optarg, "%" SCNu16, &bpm_value ); - //bpm is [0, 0, 0, 0, 0, bpm_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - AtomicInfo* bpmData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tmpo.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(bpmData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, bpm_value, 16); - } - break; - } + AtomicInfo *tvshownameData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.tvsh.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + tvshownameData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_advisory : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "content advisory") ) { - break; - } + case Meta_TV_Episode: { // if the show "ABC Lost 209", its "209" + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert( + metadata_style == ITUNES_STYLE, 1, "TV Episode string")) { + break; + } - if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) { - APar_RemoveAtom("moov.udta.meta.ilst.rtng.data", VERSIONED_ATOM, 0); - } else { - uint8_t rating_value = 0; - if (strcmp(optarg, "clean") == 0) { - rating_value = 2; //only \02 is clean - } else if (strcmp(optarg, "explicit") == 0) { - rating_value = 4; //most non \00, \02 numbers are allowed - } - //rating is [0, 0, 0, 0, rating_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - AtomicInfo* advisoryData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.rtng.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(advisoryData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, rating_value, 8); - } - break; - } + AtomicInfo *tvepisodeData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.tven.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + tvepisodeData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case Meta_artwork : { //handled differently: there can be multiple "moov.udta.meta.ilst.covr.data" atoms - char* env_PicOptions = getenv("PIC_OPTIONS"); - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "coverart") ) { - break; - } + case Meta_TV_SeasonNumber: { // if the show "ABC Lost 209", its 2; integer 2 + // not char "2" + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Season")) { + break; + } - APar_MetaData_atomArtwork_Set(optarg, env_PicOptions); - break; - } + uint16_t data_value = 0; + sscanf(optarg, "%" SCNu16, &data_value); - case Meta_stik : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "'stik'") ) { - break; - } + AtomicInfo *tvseasonData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.tvsn.data", optarg, AtomFlags_Data_UInt); + // season is [0, 0, 0, 0, 0, 0, 0, data_value]; BUT that first uint32_t + // is already accounted for in APar_MetaData_atom_Init + APar_Unified_atom_Put( + tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); + APar_Unified_atom_Put(tvseasonData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + data_value, + 16); + break; + } - if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) { - APar_RemoveAtom("moov.udta.meta.ilst.stik.data", VERSIONED_ATOM, 0); - } else { - uint8_t stik_value = 0; - - if (strncmp(optarg, "value=", 6) == 0) { - char* stik_val_str_ptr = optarg; - strsep(&stik_val_str_ptr,"="); - stik_value = strtoul(stik_val_str_ptr, NULL, 10); - } else { - stiks* return_stik = MatchStikString(optarg); - if (return_stik != NULL) { - stik_value = return_stik->stik_number; - if (strcmp(optarg, "Audiobook") == 0) { - forced_suffix_type = FORCE_M4B_TYPE; - } - } - } - //stik is [0, 0, 0, 0, stik_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - AtomicInfo* stikData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.stik.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(stikData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, stik_value, 8); - } - break; - } - - case Meta_EncodingTool : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "encoding tool") ) { - break; - } - - AtomicInfo* encodingtoolData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.too.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(encodingtoolData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_EncodedBy : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "encoded by") ) { - break; - } - - AtomicInfo* encodedbyData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.enc.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(encodedbyData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_apID : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "Account Name") ) { - break; - } - - AtomicInfo* apIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.apID.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(apIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_description : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "description") ) { - break; - } - - AtomicInfo* descriptionData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.desc.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(descriptionData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_longdescription : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "longdesc") ) { - break; - } - - AtomicInfo* descriptionData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.ldes.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); - break; - } - - case Meta_storedescription : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "storedesc") ) { - break; - } - - AtomicInfo* descriptionData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.sdes.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); - break; - } - - case Meta_Rating : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "Rating") ) { - break; - } - - AtomicInfo* ratingData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.rate.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(ratingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_TV_Network : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Network") ) { - break; - } - - AtomicInfo* tvnetworkData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tvnn.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(tvnetworkData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_TV_ShowName : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Show name") ) { - break; - } - - AtomicInfo* tvshownameData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tvsh.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(tvshownameData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_TV_Episode : { //if the show "ABC Lost 209", its "209" - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Episode string") ) { - break; - } - - AtomicInfo* tvepisodeData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tven.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(tvepisodeData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_TV_SeasonNumber : { //if the show "ABC Lost 209", its 2; integer 2 not char "2" - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Season") ) { - break; - } - - uint16_t data_value = 0; - sscanf(optarg, "%" SCNu16, &data_value ); - - AtomicInfo* tvseasonData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tvsn.data", optarg, AtomFlags_Data_UInt); - //season is [0, 0, 0, 0, 0, 0, 0, data_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - APar_Unified_atom_Put(tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); - APar_Unified_atom_Put(tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 16); - break; - } - - case Meta_TV_EpisodeNumber : { //if the show "ABC Lost 209", its 9; integer 9 (0x09) not char "9"(0x39) - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Episode number") ) { - break; - } - - uint16_t data_value = 0; - sscanf(optarg, "%" SCNu16, &data_value ); - - AtomicInfo* tvepisodenumData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tves.data", optarg, AtomFlags_Data_UInt); - //episodenumber is [0, 0, 0, 0, 0, 0, 0, data_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - APar_Unified_atom_Put(tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); - APar_Unified_atom_Put(tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 16); - break; - } - - case Meta_cnID : { // the iTunes Catalog ID - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes Catalog ID") ) { - break; - } - - uint32_t data_value = 0; - sscanf(optarg, "%" SCNu32, &data_value ); - - AtomicInfo* cnIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.cnID.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(cnIDData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 32); - break; - } - - case Meta_geID : { // the iTunes Genre ID - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes Genre ID") ) { - break; - } - - uint32_t data_value = 0; - sscanf(optarg, "%" SCNu32, &data_value ); - - AtomicInfo* geIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.geID.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(geIDData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 32); - break; - } - - case Meta_xID : { // the vendor-supplied iTunes xID - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes xID") ) { - break; - } - - AtomicInfo* xIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.xid .data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(xIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_album_artist : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "album artist") ) { - break; - } - - AtomicInfo* albumartistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.aART.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(albumartistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_podcastFlag : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast flag") ) { - break; - } - - if (strcmp(optarg, "false") == 0) { - APar_RemoveAtom("moov.udta.meta.ilst.pcst.data", VERSIONED_ATOM, 0); - } else { - //podcastflag: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - AtomicInfo* podcastFlagData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.pcst.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(podcastFlagData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 denotes podcast flag - } - - break; - } - - case Meta_keyword : { //TODO to the end of iTunes-style metadata & uuid atoms - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "keyword") ) { - break; - } - - AtomicInfo* keywordData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.keyw.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(keywordData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_category : { // see http://www.apple.com/itunes/podcasts/techspecs.html for available categories - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "category") ) { - break; - } - - AtomicInfo* categoryData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.catg.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(categoryData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); - break; - } - - case Meta_podcast_URL : { // usually a read-only value, but useful for getting videos into the 'podcast' menu - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast URL") ) { - break; - } - - AtomicInfo* podcasturlData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.purl.data", optarg, AtomFlags_Data_Binary); - APar_Unified_atom_Put(podcasturlData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0); - break; - } + case Meta_TV_EpisodeNumber: { // if the show "ABC Lost 209", its 9; integer + // 9 (0x09) not char "9"(0x39) + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert( + metadata_style == ITUNES_STYLE, 1, "TV Episode number")) { + break; + } - case Meta_podcast_GUID : { // Global Unique IDentifier; it is *highly* doubtful that this would be useful... - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast GUID") ) { - break; - } + uint16_t data_value = 0; + sscanf(optarg, "%" SCNu16, &data_value); - AtomicInfo* globalidData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.egid.data", optarg, AtomFlags_Data_Binary); - APar_Unified_atom_Put(globalidData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0); - break; - } + AtomicInfo *tvepisodenumData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.tves.data", optarg, AtomFlags_Data_UInt); + // episodenumber is [0, 0, 0, 0, 0, 0, 0, data_value]; BUT that first + // uint32_t is already accounted for in APar_MetaData_atom_Init + APar_Unified_atom_Put( + tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); + APar_Unified_atom_Put(tvepisodenumData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + data_value, + 16); + break; + } - case Meta_PurchaseDate : { // might be useful to *remove* this, but adding it... although it could function like id3v2 tdtg... - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "purchase date") ) { - break; - } - char* purd_time; - bool free_memory = false; - if (optarg != NULL) { - if (strcmp(optarg, "timestamp") == 0) { - purd_time = (char *)malloc(sizeof(char)*255); - free_memory = true; - APar_StandardTime(purd_time); - } else { - purd_time = optarg; - } - } else { - purd_time = optarg; - } + case Meta_cnID: { // the iTunes Catalog ID + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert( + metadata_style == ITUNES_STYLE, 1, "iTunes Catalog ID")) { + break; + } - AtomicInfo* globalIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.purd.data", optarg, AtomFlags_Data_Text); - APar_Unified_atom_Put(globalIDData_atom, purd_time, UTF8_iTunesStyle_256glyphLimited, 0, 0); - if (free_memory) { - free(purd_time); - purd_time = NULL; - } - break; - } + uint32_t data_value = 0; + sscanf(optarg, "%" SCNu32, &data_value); - case Meta_PlayGapless : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "gapless playback") ) { - break; - } + AtomicInfo *cnIDData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.cnID.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put(cnIDData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + data_value, + 32); + break; + } - if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) { - APar_RemoveAtom("moov.udta.meta.ilst.pgap.data", VERSIONED_ATOM, 0); - } else { - //gapless playback: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init - AtomicInfo* gaplessData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.pgap.data", optarg, AtomFlags_Data_UInt); - APar_Unified_atom_Put(gaplessData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); - } - break; - } + case Meta_geID: { // the iTunes Genre ID + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes Genre ID")) { + break; + } - case Meta_SortOrder : { - AtomicInfo* sortOrder_atom = NULL; - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "sort order tags") ) { - break; - } + uint32_t data_value = 0; + sscanf(optarg, "%" SCNu32, &data_value); - if (argv[optind] == NULL) { - fprintf(stdout, "AP warning, skipping setting the sort order %s tag\n", optarg); - break; - } + AtomicInfo *geIDData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.geID.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put(geIDData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + data_value, + 32); + break; + } - if ( strcmp(optarg, "name") == 0 ) { - sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.sonm.data", argv[optind], AtomFlags_Data_Text); - } else if ( strcmp(optarg, "artist") == 0 ) { - sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.soar.data", argv[optind], AtomFlags_Data_Text); - } else if ( strcmp(optarg, "albumartist") == 0 ) { - sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.soaa.data", argv[optind], AtomFlags_Data_Text); - } else if ( strcmp(optarg, "album") == 0 ) { - sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.soal.data", argv[optind], AtomFlags_Data_Text); - } else if ( strcmp(optarg, "composer") == 0 ) { - sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.soco.data", argv[optind], AtomFlags_Data_Text); - } else if ( strcmp(optarg, "show") == 0 ) { - sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.sosn.data", argv[optind], AtomFlags_Data_Text); - } - APar_Unified_atom_Put(sortOrder_atom, argv[optind], UTF8_iTunesStyle_256glyphLimited, 0, 0); + case Meta_xID: { // the vendor-supplied iTunes xID + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes xID")) { + break; + } - break; - } + AtomicInfo *xIDData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.xid .data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + xIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - //uuid atoms + case Meta_album_artist: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "album artist")) { + break; + } - case Meta_StandardDate : { - APar_ScanAtoms(ISObasemediafile); - char* formed_time = (char *)malloc(sizeof(char)*110); - if (argv[optind]) { - if (strlen(argv[optind]) > 0) { - APar_StandardTime(formed_time); - } - } + AtomicInfo *albumartistData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.aART.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + albumartistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - AtomicInfo* tdtgUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", "tdtg", AtomFlags_Data_Text, formed_time, false); - APar_Unified_atom_Put(tdtgUUID, formed_time, UTF8_iTunesStyle_Unlimited, 0, 0); - free(formed_time); - break; - } + case Meta_podcastFlag: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast flag")) { + break; + } - case Meta_URL : { - APar_ScanAtoms(ISObasemediafile); - AtomicInfo* urlUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", "url", AtomFlags_Data_Text, optarg, false); - APar_Unified_atom_Put(urlUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); - break; - } + if (strcmp(optarg, "false") == 0) { + APar_RemoveAtom("moov.udta.meta.ilst.pcst.data", VERSIONED_ATOM, 0); + } else { + // podcastflag: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t + // is already accounted for in APar_MetaData_atom_Init + AtomicInfo *podcastFlagData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.pcst.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put( + podcastFlagData_atom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + 1, + 8); // a hard coded uint8_t of: 1 denotes podcast flag + } - case Meta_Information : { - APar_ScanAtoms(ISObasemediafile); - AtomicInfo* infoUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", "inf", AtomFlags_Data_Text, optarg, false); - APar_Unified_atom_Put(infoUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); - break; - } + break; + } - case Meta_uuid : { - APar_ScanAtoms(ISObasemediafile); - uint32_t uuid_dataType = 0; - uint32_t desc_len = 0; - uint8_t mime_len = 0; - char* uuid_file_path = NULL; - char* uuid_file_description = NULL; - char* uuid_file_extn = NULL; - char* uuid_file_mimetype = NULL; -// char* uuid_file_filename = NULL; - - // a uuid in AP is a version 5 uuid created by getting a sha1 hash - // of a string (the atom name) in a namespace ('AP.sf.net'). This - // is guaranteed to be reproducible, so later it can be verified - // that this uuid (which could come from anywhere), is in fact made - // by AtomicParsley. This is achieved by storing the atom name - // string right after the uuid, and is read back later and a new - // uuid is created to see if it matches the discovered uuid. If - // they match, it will print out or extract to a file; if not, only - // its name will be displayed in the tree. - // - // --meta-uuid "foo" 1 'http://www.url.org' --meta-uuid "pdf1" - // file /some/path/pic.pdf description="My Booty, Your Booty, - // Djbouti" - - if ( strcmp(argv[optind], "text") == 0 || strcmp(argv[optind], "1") == 0 ) uuid_dataType = AtomFlags_Data_Text; - if ( strcmp(argv[optind], "file") == 0 ) { - uuid_dataType = AtomFlags_Data_uuid_binary; - if (optind+1 < argc) { - if (true) { //TODO: test the file to see if it exists - uuid_file_path = argv[optind + 1]; - //get the file extension/suffix of the file to embed - uuid_file_extn = strrchr(uuid_file_path, '.'); //'.' inclusive; say goodbye to AP-0.8.8.tar.bz2 - -//#ifdef _WIN32 -//#define path_delim '\\' -//#else -//#define path_delim '/' -//#endif -// uuid_file_filename = strrchr(uuid_file_path, path_delim)+1; //includes whatever extensions - } - if (uuid_file_extn == NULL) { - fprintf(stdout, "AP warning: embedding a file onto a uuid atom requires a file extension. Skipping.\n"); - continue; - } - //copy a pointer to description - int more_optional_args = 2; - while (optind + more_optional_args < argc) { - if ( strncmp(argv[optind + more_optional_args], "description=", 12) == 0 && argv[optind + more_optional_args][12]) { - uuid_file_description = argv[optind + more_optional_args] + 12; - desc_len = strlen(uuid_file_description)+1; //+1 for the trailing 1 byte NULL terminator - } - if ( strncmp(argv[optind + more_optional_args], "mime-type=", 10) == 0 && argv[optind + more_optional_args][10]) { - uuid_file_mimetype = argv[optind + more_optional_args] + 10; - mime_len = strlen(uuid_file_mimetype)+1; //+1 for the trailing 1 byte NULL terminator - } - if (strncmp(argv[optind+more_optional_args], "--", 2) == 0) { - break; //we've hit another cli argument - } - more_optional_args++; - } - } - } + case Meta_keyword: { // TODO to the end of iTunes-style metadata & uuid + // atoms + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "keyword")) { + break; + } - AtomicInfo* genericUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", optarg, uuid_dataType, argv[optind +1], true); + AtomicInfo *keywordData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.keyw.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + keywordData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - if (uuid_dataType == AtomFlags_Data_uuid_binary && genericUUID != NULL) { - TestFileExistence(uuid_file_path, true); + case Meta_category: { // see + // http://www.apple.com/itunes/podcasts/techspecs.html + // for available categories + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "category")) { + break; + } -//format for a uuid atom set by AP: -//4 bytes - atom length as uin32_t -//4 bytes - atom name as iso 8859-1 atom name as a 4byte string set to 'uuid' -//16 bytes - the uuid; here a version 5 sha1-based hash derived from a name in a namespace of 'AtomicParsley.sf.net' -//4 bytes - the name of the desired atom to create a uuid for (this "name" of the uuid is the only cli accessible means of crafting the uuid) -//4 bytes - atom version & flags (currently 1 for 'text' or '88' for binary attachment) -//4 bytes - NULL space -/////////////text or 1 for version/flags - //X bytes - utf8 string, no null termination -/////////////binary attachment or 88 for version/flags - //4 bytes - length of utf8 string describing the attachment - //X bytes - utf8 string describing the attachment, null terminated - //1 byte - length of the file suffix (including the period) of the originating file - //X bytes - utf8 string of the file suffix, null terminated - //1 byte - length of the MIME-type - //X bytes - utf8 string holding the MIME-type, null terminated - //4 bytes - length of the attached binary data/file length - //X bytes - binary data - - uint32_t extn_len = strlen(uuid_file_extn)+1; //+1 for the trailing 1 byte NULL terminator - uint64_t file_len = findFileSize(uuid_file_path); - - APar_MetaData_atom_QuickInit(genericUUID->AtomicNumber, uuid_dataType, 20, extn_len + desc_len + file_len + 100); - genericUUID->AtomicClassification = EXTENDED_ATOM; //it gets reset in QuickInit Above; force its proper setting - - if (uuid_file_description == NULL || desc_len == 0) { - APar_Unified_atom_Put(genericUUID, "[none]", UTF8_3GP_Style, 7, 32); //sets 4bytes desc_len, then 7bytes description (forced to "[none]"=6 + 1 byte NULL =7) - } else { - APar_Unified_atom_Put(genericUUID, uuid_file_description, UTF8_3GP_Style, desc_len, 32); //sets 4bytes desc_len, then Xbytes description (in that order) - } - - APar_Unified_atom_Put(genericUUID, uuid_file_extn, UTF8_3GP_Style, extn_len, 8); //sets 1bytes desc_len, then Xbytes file extension string (in that order) - - if (uuid_file_mimetype == NULL || mime_len == 0) { - APar_Unified_atom_Put(genericUUID, "none", UTF8_3GP_Style, 5, 8); //sets 4bytes desc_len, then 5bytes description (forced to "none" + 1byte null) - } else { - APar_Unified_atom_Put(genericUUID, uuid_file_mimetype, UTF8_3GP_Style, mime_len, 8); //sets 1 byte mime len, then Xbytes mime type - } - - FILE* uuid_binfile = APar_OpenFile(uuid_file_path, "rb"); - APar_Unified_atom_Put(genericUUID, NULL, UTF8_3GP_Style, file_len, 32); - //store the data directly on the atom in AtomicData - uint32_t bin_bytes_read = APar_ReadFile(genericUUID->AtomicData + (genericUUID->AtomicLength - 32), uuid_binfile, file_len); - genericUUID->AtomicLength += bin_bytes_read; - fclose(uuid_binfile); + AtomicInfo *categoryData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.catg.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + categoryData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - } else { //text type - APar_Unified_atom_Put(genericUUID, argv[optind +1], UTF8_iTunesStyle_Unlimited, 0, 0); - } + case Meta_podcast_URL: { // usually a read-only value, but useful for + // getting videos into the 'podcast' menu + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast URL")) { + break; + } - break; - } + AtomicInfo *podcasturlData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.purl.data", optarg, AtomFlags_Data_Binary); + APar_Unified_atom_Put( + podcasturlData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0); + break; + } - case Opt_Extract_all_uuids : { - APar_ScanAtoms(ISObasemediafile); - char* output_path = NULL; - if (optind + 1 == argc) { - output_path = argv[optind]; - } + case Meta_podcast_GUID: { // Global Unique IDentifier; it is *highly* + // doubtful that this would be useful... + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast GUID")) { + break; + } - APar_OpenISOBaseMediaFile(ISObasemediafile, true); - APar_Print_APuuid_atoms(ISObasemediafile, output_path, EXTRACT_ALL_UUID_BINARYS); - APar_OpenISOBaseMediaFile(ISObasemediafile, false); + AtomicInfo *globalidData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.egid.data", optarg, AtomFlags_Data_Binary); + APar_Unified_atom_Put( + globalidData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0); + break; + } - exit(0);//never gets here - break; + case Meta_PurchaseDate: { // might be useful to *remove* this, but adding + // it... although it could function like id3v2 + // tdtg... + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "purchase date")) { + break; + } + char *purd_time; + bool free_memory = false; + if (optarg != NULL) { + if (strcmp(optarg, "timestamp") == 0) { + purd_time = (char *)malloc(sizeof(char) * 255); + free_memory = true; + APar_StandardTime(purd_time); + } else { + purd_time = optarg; } + } else { + purd_time = optarg; + } + + AtomicInfo *globalIDData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.purd.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + globalIDData_atom, purd_time, UTF8_iTunesStyle_256glyphLimited, 0, 0); + if (free_memory) { + free(purd_time); + purd_time = NULL; + } + break; + } - case Opt_Extract_a_uuid : { - APar_ScanAtoms(ISObasemediafile); - - char* uuid_path = (char*)calloc(1, sizeof(char)*256+1); - char* uuid_binary_str = (char*)calloc(1, sizeof(char)*20+1); - char uuid_4char_name[16]; memset(uuid_4char_name, 0, 16); - AtomicInfo* extractionAtom = NULL; - - UTF8Toisolat1((unsigned char*)&uuid_4char_name, 4, (unsigned char*)optarg, strlen(optarg) ); - APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str); - - //this will only append (and knock off) %s (anything) at the end of a string - uint16_t path_len = strlen("moov.udta.meta.uuid=%s"); - memcpy(uuid_path, "moov.udta.meta.uuid=%s", path_len-2); - memcpy(uuid_path + (path_len-2), uuid_binary_str, 16); - - extractionAtom = APar_FindAtom(uuid_path, false, EXTENDED_ATOM, 0, true); - if (extractionAtom != NULL) { - APar_OpenISOBaseMediaFile(ISObasemediafile, true); - APar_Extract_uuid_binary_file(extractionAtom, ISObasemediafile, NULL); - APar_OpenISOBaseMediaFile(ISObasemediafile, false); - } - - free(uuid_path); uuid_path = NULL; - free(uuid_binary_str); uuid_binary_str = NULL; - exit(0); - break; //never gets here - } - - case Opt_Ipod_AVC_uuid : { - if (deep_atom_scan == true) { - if (strcmp(optarg, "1200") != 0) { - fprintf(stdout, "the ipod-uuid has a single preset of '1200' which is required\n"); //1200 might not be the only max macroblock setting down the pike - break; - } - uint8_t total_tracks = 0; - uint8_t a_track = 0;//unused - char atom_path[100]; - AtomicInfo* video_desc_atom = NULL; - - memset(atom_path, 0, 100); - - APar_FindAtomInTrack(total_tracks, a_track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here. - - while (a_track < total_tracks) { - a_track++; - sprintf(atom_path, "moov.trak[%u].mdia.minf.stbl.stsd.avc1", a_track); - video_desc_atom = APar_FindAtom(atom_path, false, VERSIONED_ATOM, 0, false); - - if (video_desc_atom != NULL) { - uint16_t mb_t = APar_TestVideoDescription(video_desc_atom, APar_OpenFile(ISObasemediafile, "rb")); - if (mb_t > 0 && mb_t <= 1200) { - sprintf(atom_path, "moov.trak[%u].mdia.minf.stbl.stsd.avc1.uuid=", a_track); - uint8_t uuid_baselen = (uint8_t)strlen(atom_path); - APar_uuid_scanf(atom_path + uuid_baselen, "6b6840f2-5f24-4fc5-ba39-a51bcf0323f3"); - APar_endian_uuid_bin_str_conversion(atom_path + uuid_baselen); - APar_Generate_iPod_uuid(atom_path); - } - } - } + case Meta_PlayGapless: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "gapless playback")) { + break; + } - } else { - fprintf(stdout, "the --DeepScan option is required for this operation. Skipping\n"); - } - break; - } + if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) { + APar_RemoveAtom("moov.udta.meta.ilst.pgap.data", VERSIONED_ATOM, 0); + } else { + // gapless playback: [0, 0, 0, 0, boolean_value]; BUT that first + // uint32_t is already accounted for in APar_MetaData_atom_Init + AtomicInfo *gaplessData_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.pgap.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put( + gaplessData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); + } + break; + } - case Manual_atom_removal : { - APar_ScanAtoms(ISObasemediafile); + case Meta_SortOrder: { + AtomicInfo *sortOrder_atom = NULL; + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "sort order tags")) { + break; + } - char* compliant_name = (char*)malloc(sizeof(char)* strlen(optarg) +1); - memset(compliant_name, 0, strlen(optarg) +1); - UTF8Toisolat1((unsigned char*)compliant_name, strlen(optarg), (unsigned char*)optarg, strlen(optarg) ); - - if (strstr(optarg, "uuid=") != NULL) { - uint16_t uuid_path_pos = 0; - uint16_t uuid_path_len = strlen(optarg); - while (compliant_name+uuid_path_pos < compliant_name+uuid_path_len) { - if (strncmp(compliant_name+uuid_path_pos, "uuid=", 5) == 0) { - uuid_path_pos+=5; - break; - } - uuid_path_pos++; - } - if (strlen(compliant_name+uuid_path_pos) > 4) { //if =4 then it would be the deprecated form (or it should be, if not it just won't find anything; no harm done) - uint8_t uuid_len = APar_uuid_scanf(compliant_name+uuid_path_pos, optarg+uuid_path_pos); - compliant_name[uuid_path_pos+uuid_len] = 0; - } - APar_RemoveAtom(compliant_name, EXTENDED_ATOM, 0); - - } else if (strcmp(compliant_name + (strlen(compliant_name) - 4), "data") == 0) { - APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0); - - } else { - size_t string_len = strlen(compliant_name); - //reverseDNS atom path - if (strstr(optarg, ":[") != NULL && compliant_name[string_len-1] == ']' ) { - APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0); - - //packed language asset - } else if (strncmp(compliant_name + string_len - 9, ":lang=", 6) == 0 ) { - uint16_t packed_lang = PackLanguage(compliant_name + string_len - 3, 0); - memset(compliant_name + string_len - 9, 0, 1); - APar_RemoveAtom(compliant_name, PACKED_LANG_ATOM, packed_lang); - - } else { - APar_RemoveAtom(compliant_name, UNKNOWN_ATOM, 0); - } - } - free(compliant_name); - compliant_name = NULL; - break; - } + if (argv[optind] == NULL) { + fprintf(stdout, + "AP warning, skipping setting the sort order %s tag\n", + optarg); + break; + } - //3gp tags + if (strcmp(optarg, "name") == 0) { + sortOrder_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.sonm.data", argv[optind], AtomFlags_Data_Text); + } else if (strcmp(optarg, "artist") == 0) { + sortOrder_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.soar.data", argv[optind], AtomFlags_Data_Text); + } else if (strcmp(optarg, "albumartist") == 0) { + sortOrder_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.soaa.data", argv[optind], AtomFlags_Data_Text); + } else if (strcmp(optarg, "album") == 0) { + sortOrder_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.soal.data", argv[optind], AtomFlags_Data_Text); + } else if (strcmp(optarg, "composer") == 0) { + sortOrder_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.soco.data", argv[optind], AtomFlags_Data_Text); + } else if (strcmp(optarg, "show") == 0) { + sortOrder_atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.sosn.data", argv[optind], AtomFlags_Data_Text); + } + APar_Unified_atom_Put( + sortOrder_atom, argv[optind], UTF8_iTunesStyle_256glyphLimited, 0, 0); - /* - First, scan the file to get at atom tree (only happens once). Then take the cli args and look for optional arguments. All arguments begin with -- or -; other args - are optional and are determined by directly testing arguments. Optional arguments common to the 3gp asset group (language, unicode, track/movie userdata) are - extracted in find_optional_args. Setting assets in all tracks requires getting the number of tracks. Loop through either once (for movie & single track) or as - many tracks there are for all tracks. Each pass through the loop, set the individual pieces of metadata. - */ - case _3GP_Title : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "title") ) { - break; - } - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3); - - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; //otherwise, APar_UserData_atom_Init will shift to non-existing track 0 - } + break; + } - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* title_asset = APar_UserData_atom_Init("titl", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - APar_Unified_atom_Put(title_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - } - break; - } + // uuid atoms - case _3GP_Author : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "author") ) { - break; - } - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3); - - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + case Meta_StandardDate: { + APar_ScanAtoms(ISObasemediafile); + char *formed_time = (char *)malloc(sizeof(char) * 110); + if (argv[optind]) { + if (strlen(argv[optind]) > 0) { + APar_StandardTime(formed_time); + } + } + + AtomicInfo *tdtgUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", + "tdtg", + AtomFlags_Data_Text, + formed_time, + false); + APar_Unified_atom_Put( + tdtgUUID, formed_time, UTF8_iTunesStyle_Unlimited, 0, 0); + free(formed_time); + break; + } - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* author_asset = APar_UserData_atom_Init("auth", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - APar_Unified_atom_Put(author_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - } - break; - } + case Meta_URL: { + APar_ScanAtoms(ISObasemediafile); + AtomicInfo *urlUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", + "\251url", + AtomFlags_Data_Text, + optarg, + false); + APar_Unified_atom_Put(urlUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); + break; + } - case _3GP_Performer : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "performer") ) { - break; - } - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3); - - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + case Meta_Information: { + APar_ScanAtoms(ISObasemediafile); + AtomicInfo *infoUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", + "\251inf", + AtomFlags_Data_Text, + optarg, + false); + APar_Unified_atom_Put(infoUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); + break; + } - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* performer_asset = APar_UserData_atom_Init("perf", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - APar_Unified_atom_Put(performer_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - } - break; - } + case Meta_uuid: { + APar_ScanAtoms(ISObasemediafile); + uint32_t uuid_dataType = 0; + uint32_t desc_len = 0; + uint8_t mime_len = 0; + char *uuid_file_path = NULL; + char *uuid_file_description = NULL; + char *uuid_file_extn = NULL; + char *uuid_file_mimetype = NULL; + // char* uuid_file_filename = NULL; + + // a uuid in AP is a version 5 uuid created by getting a sha1 hash + // of a string (the atom name) in a namespace ('AP.sf.net'). This + // is guaranteed to be reproducible, so later it can be verified + // that this uuid (which could come from anywhere), is in fact made + // by AtomicParsley. This is achieved by storing the atom name + // string right after the uuid, and is read back later and a new + // uuid is created to see if it matches the discovered uuid. If + // they match, it will print out or extract to a file; if not, only + // its name will be displayed in the tree. + // + // --meta-uuid "\251foo" 1 'http://www.url.org' --meta-uuid "pdf1" + // file /some/path/pic.pdf description="My Booty, Your Booty, + // Djbouti" + + if (strcmp(argv[optind], "text") == 0 || strcmp(argv[optind], "1") == 0) + uuid_dataType = AtomFlags_Data_Text; + if (strcmp(argv[optind], "file") == 0) { + uuid_dataType = AtomFlags_Data_uuid_binary; + if (optind + 1 < argc) { + if (true) { // TODO: test the file to see if it exists + uuid_file_path = argv[optind + 1]; + // get the file extension/suffix of the file to embed + uuid_file_extn = + strrchr(uuid_file_path, + '.'); //'.' inclusive; say goodbye to AP-0.8.8.tar.bz2 + + //#ifdef _WIN32 + //#define path_delim '\\' + //#else + //#define path_delim '/' + //#endif + // uuid_file_filename = strrchr(uuid_file_path, + // path_delim)+1; //includes whatever + // extensions + } + if (uuid_file_extn == NULL) { + fprintf(stdout, + "AP warning: embedding a file onto a uuid atom " + "requires a file extension. Skipping.\n"); + continue; + } + // copy a pointer to description + int more_optional_args = 2; + while (optind + more_optional_args < argc) { + if (strncmp( + argv[optind + more_optional_args], "description=", 12) == + 0 && + argv[optind + more_optional_args][12]) { + uuid_file_description = argv[optind + more_optional_args] + 12; + desc_len = strlen(uuid_file_description) + + 1; //+1 for the trailing 1 byte NULL terminator + } + if (strncmp(argv[optind + more_optional_args], "mime-type=", 10) == + 0 && + argv[optind + more_optional_args][10]) { + uuid_file_mimetype = argv[optind + more_optional_args] + 10; + mime_len = strlen(uuid_file_mimetype) + + 1; //+1 for the trailing 1 byte NULL terminator + } + if (strncmp(argv[optind + more_optional_args], "--", 2) == 0) { + break; // we've hit another cli argument + } + more_optional_args++; + } + } + } + + AtomicInfo *genericUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", + optarg, + uuid_dataType, + argv[optind + 1], + true); + + if (uuid_dataType == AtomFlags_Data_uuid_binary && genericUUID != NULL) { + TestFileExistence(uuid_file_path, true); + + // format for a uuid atom set by AP: + // 4 bytes - atom length as uin32_t + // 4 bytes - atom name as iso 8859-1 atom name as a 4byte string set + // to 'uuid' 16 bytes - the uuid; here a version 5 sha1-based hash + // derived from a name in a namespace of 'AtomicParsley.sf.net' 4 bytes + // - the name of the desired atom to create a uuid for (this "name" of + // the uuid is the only cli accessible means of crafting the uuid) 4 + // bytes - atom version & flags (currently 1 for 'text' or '88' for + // binary attachment) 4 bytes - NULL space + /////////////text or 1 for version/flags + // X bytes - utf8 string, no null termination + /////////////binary attachment or 88 for version/flags + // 4 bytes - length of utf8 string describing the attachment + // X bytes - utf8 string describing the attachment, null terminated + // 1 byte - length of the file suffix (including the period) of the + // originating file X bytes - utf8 string of the file suffix, null + // terminated 1 byte - length of the MIME-type X bytes - utf8 string + // holding the MIME-type, null terminated 4 bytes - length of the + // attached binary data/file length X bytes - binary data + + uint32_t extn_len = strlen(uuid_file_extn) + + 1; //+1 for the trailing 1 byte NULL terminator + uint64_t file_len = findFileSize(uuid_file_path); + + APar_MetaData_atom_QuickInit(genericUUID->AtomicNumber, + uuid_dataType, + 20, + extn_len + desc_len + file_len + 100); + genericUUID->AtomicClassification = + EXTENDED_ATOM; // it gets reset in QuickInit Above; force its proper + // setting + + if (uuid_file_description == NULL || desc_len == 0) { + APar_Unified_atom_Put( + genericUUID, + "[none]", + UTF8_3GP_Style, + 7, + 32); // sets 4bytes desc_len, then 7bytes description (forced to + // "[none]"=6 + 1 byte NULL =7) + } else { + APar_Unified_atom_Put(genericUUID, + uuid_file_description, + UTF8_3GP_Style, + desc_len, + 32); // sets 4bytes desc_len, then Xbytes + // description (in that order) + } + + APar_Unified_atom_Put(genericUUID, + uuid_file_extn, + UTF8_3GP_Style, + extn_len, + 8); // sets 1bytes desc_len, then Xbytes file + // extension string (in that order) + + if (uuid_file_mimetype == NULL || mime_len == 0) { + APar_Unified_atom_Put( + genericUUID, + "none", + UTF8_3GP_Style, + 5, + 8); // sets 4bytes desc_len, then 5bytes description (forced to + // "none" + 1byte null) + } else { + APar_Unified_atom_Put( + genericUUID, + uuid_file_mimetype, + UTF8_3GP_Style, + mime_len, + 8); // sets 1 byte mime len, then Xbytes mime type + } + + FILE *uuid_binfile = APar_OpenFile(uuid_file_path, "rb"); + APar_Unified_atom_Put(genericUUID, NULL, UTF8_3GP_Style, file_len, 32); + // store the data directly on the atom in AtomicData + uint32_t bin_bytes_read = APar_ReadFile( + genericUUID->AtomicData + (genericUUID->AtomicLength - 32), + uuid_binfile, + file_len); + genericUUID->AtomicLength += bin_bytes_read; + fclose(uuid_binfile); + + } else { // text type + APar_Unified_atom_Put( + genericUUID, argv[optind + 1], UTF8_iTunesStyle_Unlimited, 0, 0); + } - case _3GP_Genre : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "genre") ) { - break; - } - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3); - - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + break; + } - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* genre_asset = APar_UserData_atom_Init("gnre", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - APar_Unified_atom_Put(genre_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - } - break; - } + case Opt_Extract_all_uuids: { + APar_ScanAtoms(ISObasemediafile); + char *output_path = NULL; + if (optind + 1 == argc) { + output_path = argv[optind]; + } + + APar_OpenISOBaseMediaFile(ISObasemediafile, true); + APar_Print_APuuid_atoms( + ISObasemediafile, output_path, EXTRACT_ALL_UUID_BINARYS); + APar_OpenISOBaseMediaFile(ISObasemediafile, false); - case _3GP_Description : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "description") ) { - break; - } - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3); - - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + exit(0); // never gets here + break; + } - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* description_asset = APar_UserData_atom_Init("dscp", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - APar_Unified_atom_Put(description_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - } - break; - } + case Opt_Extract_a_uuid: { + APar_ScanAtoms(ISObasemediafile); - case ISO_Copyright: //ISO copyright atom common to all files that are derivatives of the base media file format, identical to.... - case _3GP_Copyright : { //the 3gp copyright asset; this gets a test for major branding (but only with the cli arg --3gp-copyright). - APar_ScanAtoms(ISObasemediafile); - - if (c == _3GP_Copyright) { - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "copyright") ) { - break; - } - } + char *uuid_path = (char *)calloc(1, sizeof(char) * 256 + 1); + char *uuid_binary_str = (char *)calloc(1, sizeof(char) * 20 + 1); + char uuid_4char_name[16]; + memset(uuid_4char_name, 0, 16); + AtomicInfo *extractionAtom = NULL; + + UTF8Toisolat1((unsigned char *)&uuid_4char_name, + 4, + (unsigned char *)optarg, + strlen(optarg)); + APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str); + + // this will only append (and knock off) %s (anything) at the end of a + // string + uint16_t path_len = strlen("moov.udta.meta.uuid=%s"); + memcpy(uuid_path, "moov.udta.meta.uuid=%s", path_len - 2); + memcpy(uuid_path + (path_len - 2), uuid_binary_str, 16); - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3); - - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + extractionAtom = APar_FindAtom(uuid_path, false, EXTENDED_ATOM, 0, true); + if (extractionAtom != NULL) { + APar_OpenISOBaseMediaFile(ISObasemediafile, true); + APar_Extract_uuid_binary_file(extractionAtom, ISObasemediafile, NULL); + APar_OpenISOBaseMediaFile(ISObasemediafile, false); + } + + free(uuid_path); + uuid_path = NULL; + free(uuid_binary_str); + uuid_binary_str = NULL; + exit(0); + break; // never gets here + } - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* copyright_notice = APar_UserData_atom_Init("cprt", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - APar_Unified_atom_Put(copyright_notice, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - } - break; - } + case Opt_Ipod_AVC_uuid: { + if (deep_atom_scan == true) { + if (strcmp(optarg, "1200") != 0) { + fprintf(stdout, + "the ipod-uuid has a single preset of '1200' which " + "is required\n"); // 1200 might not be the only max + // macroblock setting down the pike + break; + } + uint8_t total_tracks = 0; + uint8_t a_track = 0; // unused + char atom_path[100]; + AtomicInfo *video_desc_atom = NULL; + + memset(atom_path, 0, 100); + + APar_FindAtomInTrack( + total_tracks, + a_track, + NULL); // With track_num set to 0, it will return the total trak + // atom into total_tracks here. + + while (a_track < total_tracks) { + a_track++; + sprintf(atom_path, "moov.trak[%u].mdia.minf.stbl.stsd.avc1", a_track); + video_desc_atom = + APar_FindAtom(atom_path, false, VERSIONED_ATOM, 0, false); + + if (video_desc_atom != NULL) { + uint16_t mb_t = APar_TestVideoDescription( + video_desc_atom, APar_OpenFile(ISObasemediafile, "rb")); + if (mb_t > 0 && mb_t <= 1200) { + sprintf(atom_path, + "moov.trak[%u].mdia.minf.stbl.stsd.avc1.uuid=", + a_track); + uint8_t uuid_baselen = (uint8_t)strlen(atom_path); + APar_uuid_scanf(atom_path + uuid_baselen, + "6b6840f2-5f24-4fc5-ba39-a51bcf0323f3"); + APar_endian_uuid_bin_str_conversion(atom_path + uuid_baselen); + APar_Generate_iPod_uuid(atom_path); + } + } + } + + } else { + fprintf( + stdout, + "the --DeepScan option is required for this operation. Skipping\n"); + } + break; + } - case _3GP_Album : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "album") ) { - break; - } - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - uint8_t tracknum = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 4); - - //cygle through the remaining independant arguments (before the next --cli_flag) and figure out if any are useful to us; already have lang & utf16 - for (int i= 0; i <= 4; i++) { //3 possible arguments for this tag (the first - which doesn't count - is the data for the tag itself) - if ( argv[optind + i] && optind + i <= total_args) { - if ( strncmp(argv[optind + i], "trknum=", 7) == 0 ) { - char* track_num = argv[optind + i]; - strsep(&track_num,"="); - tracknum = strtoul(track_num, NULL, 10); - } - if (*argv[optind + i] == '-') break; - } - } + case Manual_atom_removal: { + APar_ScanAtoms(ISObasemediafile); - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + char *compliant_name = (char *)malloc(sizeof(char) * strlen(optarg) + 1); + memset(compliant_name, 0, strlen(optarg) + 1); + UTF8Toisolat1((unsigned char *)compliant_name, + strlen(optarg), + (unsigned char *)optarg, + strlen(optarg)); + + if (strstr(optarg, "uuid=") != NULL) { + uint16_t uuid_path_pos = 0; + uint16_t uuid_path_len = strlen(optarg); + while (compliant_name + uuid_path_pos < + compliant_name + uuid_path_len) { + if (strncmp(compliant_name + uuid_path_pos, "uuid=", 5) == 0) { + uuid_path_pos += 5; + break; + } + uuid_path_pos++; + } + if (strlen(compliant_name + uuid_path_pos) > + 4) { // if =4 then it would be the deprecated form (or it should be, + // if not it just won't find anything; no harm done) + uint8_t uuid_len = APar_uuid_scanf(compliant_name + uuid_path_pos, + optarg + uuid_path_pos); + compliant_name[uuid_path_pos + uuid_len] = 0; + } + APar_RemoveAtom(compliant_name, EXTENDED_ATOM, 0); + + } else if (strcmp(compliant_name + (strlen(compliant_name) - 4), + "data") == 0) { + APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0); + + } else { + size_t string_len = strlen(compliant_name); + // reverseDNS atom path + if (strstr(optarg, ":[") != NULL && + compliant_name[string_len - 1] == ']') { + APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0); + + // packed language asset + } else if (strncmp(compliant_name + string_len - 9, ":lang=", 6) == 0) { + uint16_t packed_lang = + PackLanguage(compliant_name + string_len - 3, 0); + memset(compliant_name + string_len - 9, 0, 1); + APar_RemoveAtom(compliant_name, PACKED_LANG_ATOM, packed_lang); - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* album_asset = APar_UserData_atom_Init("albm", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - APar_Unified_atom_Put(album_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - if (tracknum != 0) { - APar_Unified_atom_Put(album_asset, NULL, UTF8_3GP_Style, tracknum, 8); - } - } - break; + } else { + APar_RemoveAtom(compliant_name, UNKNOWN_ATOM, 0); } + } + free(compliant_name); + compliant_name = NULL; + break; + } - case _3GP_Year : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "year") ) { - break; - } - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - uint16_t year_tag = 0; - - if ( argv[optind] && optind <= total_args) { - if ( strcmp(argv[optind], "movie") == 0 ) { - userdata_area = MOVIE_LEVEL_ATOM; } - if ( strncmp(argv[optind], "track=", 6) == 0 ) { - char* trak_idx = argv[optind]; - strsep(&trak_idx, "="); - selected_track = strtoul(trak_idx, NULL, 10); - userdata_area = SINGLE_TRACK_ATOM; - } else if ( strcmp(argv[optind], "track") == 0 ) { - userdata_area = ALL_TRACKS_ATOM; - } - } + // 3gp tags - sscanf(optarg, "%" SCNu16, &year_tag); + /* + First, scan the file to get at atom tree (only happens once). Then take the + cli args and look for optional arguments. All arguments begin with -- or -; + other args are optional and are determined by directly testing arguments. + Optional arguments common to the 3gp asset group (language, unicode, + track/movie userdata) are extracted in find_optional_args. Setting assets in + all tracks requires getting the number of tracks. Loop through either once + (for movie & single track) or as many tracks there are for all tracks. Each + pass through the loop, set the individual pieces of metadata. + */ + case _3GP_Title: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "title")) { + break; + } + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 3); + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; // otherwise, APar_UserData_atom_Init will shift + // to non-existing track 0 + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *title_asset = APar_UserData_atom_Init( + "titl", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + APar_Unified_atom_Put( + title_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + } + break; + } - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + case _3GP_Author: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "author")) { + break; + } + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 3); + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *author_asset = APar_UserData_atom_Init( + "auth", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + APar_Unified_atom_Put( + author_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + } + break; + } - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* recordingyear_asset = APar_UserData_atom_Init("yrrc", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, 0); - APar_Unified_atom_Put(recordingyear_asset, NULL, UTF8_3GP_Style, year_tag, 16); - } - break; - } + case _3GP_Performer: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "performer")) { + break; + } + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 3); + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *performer_asset = APar_UserData_atom_Init( + "perf", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + APar_Unified_atom_Put( + performer_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + } + break; + } - case _3GP_Rating : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "rating") ) { - break; - } - char rating_entity[5] = { 'N', 'O', 'N', 'E', 0 }; //'NONE' - thats what it will be if not provided - char rating_criteria[5] = { 'N', 'O', 'N', 'E', 0 }; - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 5); - - for (int i= 0; i < 5; i++) { //3 possible arguments for this tag (the first - which doesn't count - is the data for the tag itself) - if ( argv[optind + i] && optind + i <= total_args) { - if ( strncmp(argv[optind + i], "entity=", 7) == 0 ) { - char* entity = argv[optind + i]; - strsep(&entity,"="); - memcpy(&rating_entity, entity, 4); - } - if ( strncmp(argv[optind + i], "criteria=", 9) == 0 ) { - char* criteria = argv[optind + i]; - strsep(&criteria,"="); - memcpy(&rating_criteria, criteria, 4); - } - if (*argv[optind + i] == '-') break; //we've hit another cli argument - } - } + case _3GP_Genre: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "genre")) { + break; + } + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 3); + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *genre_asset = APar_UserData_atom_Init( + "gnre", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + APar_Unified_atom_Put( + genre_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + } + break; + } - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + case _3GP_Description: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "description")) { + break; + } + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 3); + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *description_asset = APar_UserData_atom_Init( + "dscp", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + APar_Unified_atom_Put( + description_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + } + break; + } - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* rating_asset = APar_UserData_atom_Init("rtng", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); + case ISO_Copyright: // ISO copyright atom common to all files that are + // derivatives of the base media file format, identical + // to.... + case _3GP_Copyright: { // the 3gp copyright asset; this gets a test for + // major branding (but only with the cli arg + // --3gp-copyright). + APar_ScanAtoms(ISObasemediafile); + + if (c == _3GP_Copyright) { + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "copyright")) { + break; + } + } + + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 3); + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *copyright_notice = APar_UserData_atom_Init( + "cprt", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + APar_Unified_atom_Put( + copyright_notice, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + } + break; + } - APar_Unified_atom_Put(rating_asset, NULL, UTF8_3GP_Style, UInt32FromBigEndian(rating_entity), 32); - APar_Unified_atom_Put(rating_asset, NULL, UTF8_3GP_Style, UInt32FromBigEndian(rating_criteria), 32); - APar_Unified_atom_Put(rating_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - } - break; + case _3GP_Album: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "album")) { + break; + } + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + uint8_t tracknum = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 4); + + // cygle through the remaining independant arguments (before the next + // --cli_flag) and figure out if any are useful to us; already have lang & + // utf16 + for (int i = 0; i <= 4; + i++) { // 3 possible arguments for this tag (the first - which + // doesn't count - is the data for the tag itself) + if (argv[optind + i] && optind + i <= total_args) { + if (strncmp(argv[optind + i], "trknum=", 7) == 0) { + char *track_num = argv[optind + i]; + strsep(&track_num, "="); + tracknum = strtoul(track_num, NULL, 10); + } + if (*argv[optind + i] == '-') + break; + } + } + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *album_asset = APar_UserData_atom_Init( + "albm", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + APar_Unified_atom_Put( + album_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + if (tracknum != 0) { + APar_Unified_atom_Put(album_asset, NULL, UTF8_3GP_Style, tracknum, 8); } + } + break; + } - case _3GP_Classification : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "classification") ) { - break; - } - char classification_entity[5] = { 'N', 'O', 'N', 'E', 0 }; //'NONE' - thats what it will be if not provided - uint16_t classification_index = 0; - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 5); - - for (int i= 0; i < 4; i++) { //3 possible arguments for this tag (the first - which doesn't count - is the data for the tag itself) - if ( argv[optind + i] && optind + i <= total_args) { - if ( strncmp(argv[optind + i], "entity=", 7) == 0 ) { - char* cls_entity = argv[optind + i]; - strsep(&cls_entity, "="); - memcpy(&classification_entity, cls_entity, 4); - } - if ( strncmp(argv[optind + i], "index=", 6) == 0 ) { - char* cls_idx = argv[optind + i]; - strsep(&cls_idx, "="); - sscanf(cls_idx, "%" SCNu16, &classification_index); - } - if (*argv[optind + i] == '-') break; //we've hit another cli argument - } - } - - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } - - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* classification_asset = APar_UserData_atom_Init("clsf", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); + case _3GP_Year: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "year")) { + break; + } + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + uint16_t year_tag = 0; + + if (argv[optind] && optind <= total_args) { + if (strcmp(argv[optind], "movie") == 0) { + userdata_area = MOVIE_LEVEL_ATOM; + } + if (strncmp(argv[optind], "track=", 6) == 0) { + char *trak_idx = argv[optind]; + strsep(&trak_idx, "="); + selected_track = strtoul(trak_idx, NULL, 10); + userdata_area = SINGLE_TRACK_ATOM; + } else if (strcmp(argv[optind], "track") == 0) { + userdata_area = ALL_TRACKS_ATOM; + } + } + + sscanf(optarg, "%" SCNu16, &year_tag); + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *recordingyear_asset = APar_UserData_atom_Init( + "yrrc", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + 0); + APar_Unified_atom_Put( + recordingyear_asset, NULL, UTF8_3GP_Style, year_tag, 16); + } + break; + } - APar_Unified_atom_Put(classification_asset, NULL, UTF8_3GP_Style, UInt32FromBigEndian(classification_entity), 32); - APar_Unified_atom_Put(classification_asset, NULL, UTF8_3GP_Style, classification_index, 16); - APar_Unified_atom_Put(classification_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - } - break; - } + case _3GP_Rating: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "rating")) { + break; + } + char rating_entity[5] = { + 'N', + 'O', + 'N', + 'E', + 0}; //'NONE' - thats what it will be if not provided + char rating_criteria[5] = {'N', 'O', 'N', 'E', 0}; + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 5); + + for (int i = 0; i < 5; + i++) { // 3 possible arguments for this tag (the first - which + // doesn't count - is the data for the tag itself) + if (argv[optind + i] && optind + i <= total_args) { + if (strncmp(argv[optind + i], "entity=", 7) == 0) { + char *entity = argv[optind + i]; + strsep(&entity, "="); + memcpy(&rating_entity, entity, 4); + } + if (strncmp(argv[optind + i], "criteria=", 9) == 0) { + char *criteria = argv[optind + i]; + strsep(&criteria, "="); + memcpy(&rating_criteria, criteria, 4); + } + if (*argv[optind + i] == '-') + break; // we've hit another cli argument + } + } + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *rating_asset = APar_UserData_atom_Init( + "rtng", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + + APar_Unified_atom_Put(rating_asset, + NULL, + UTF8_3GP_Style, + UInt32FromBigEndian(rating_entity), + 32); + APar_Unified_atom_Put(rating_asset, + NULL, + UTF8_3GP_Style, + UInt32FromBigEndian(rating_criteria), + 32); + APar_Unified_atom_Put( + rating_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + } + break; + } - case _3GP_Keyword : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "keyword") ) { - break; - } - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - char* formed_keyword_struct = NULL; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 4); - - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } + case _3GP_Classification: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "classification")) { + break; + } + char classification_entity[5] = { + 'N', + 'O', + 'N', + 'E', + 0}; //'NONE' - thats what it will be if not provided + uint16_t classification_index = 0; + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 5); + + for (int i = 0; i < 4; + i++) { // 3 possible arguments for this tag (the first - which + // doesn't count - is the data for the tag itself) + if (argv[optind + i] && optind + i <= total_args) { + if (strncmp(argv[optind + i], "entity=", 7) == 0) { + char *cls_entity = argv[optind + i]; + strsep(&cls_entity, "="); + memcpy(&classification_entity, cls_entity, 4); + } + if (strncmp(argv[optind + i], "index=", 6) == 0) { + char *cls_idx = argv[optind + i]; + strsep(&cls_idx, "="); + sscanf(cls_idx, "%" SCNu16, &classification_index); + } + if (*argv[optind + i] == '-') + break; // we've hit another cli argument + } + } + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *classification_asset = APar_UserData_atom_Init( + "clsf", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + + APar_Unified_atom_Put(classification_asset, + NULL, + UTF8_3GP_Style, + UInt32FromBigEndian(classification_entity), + 32); + APar_Unified_atom_Put(classification_asset, + NULL, + UTF8_3GP_Style, + classification_index, + 16); + APar_Unified_atom_Put( + classification_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + } + break; + } - if (strrchr(optarg, '=') != NULL) { //must be in the format of: keywords=foo1,foo2,foo3,foo4 - char* arg_keywords = optarg; - char* keywords_globbed = strsep(&arg_keywords,"="); //separate out 'keyword=' - keywords_globbed = strsep(&arg_keywords,"="); //this is what we want to work on: just the keywords - char* keyword_ptr = keywords_globbed; - uint32_t keyword_strlen = strlen(keywords_globbed); - uint8_t keyword_count = 0; - uint32_t key_index = 0; - - if (keyword_strlen > 0) { //if there is anything past the = then it counts as a keyword - keyword_count++; - } - - while (true) { //count occurrences of comma here - if (*keyword_ptr == ',') { - keyword_count++; - } - keyword_ptr++; - key_index++; - if (keyword_strlen == key_index) { - break; - } - } - - formed_keyword_struct = (char*)calloc(1, sizeof(char)* set_UTF16_text ? (keyword_strlen * 4) : (keyword_strlen * 2)); // *4 should carry utf16's BOM & TERM - uint32_t keyword_struct_bytes = APar_3GP_Keyword_atom_Format(keywords_globbed, keyword_count, set_UTF16_text, formed_keyword_struct); - - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - AtomicInfo* keyword_asset = APar_UserData_atom_Init("kywd", keyword_strlen ? "temporary" : "", userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); //just a "temporary" valid string to satisfy a test there - if (keyword_strlen > 0) { - APar_Unified_atom_Put(keyword_asset, NULL, UTF8_3GP_Style, packed_lang, 16); - APar_Unified_atom_Put(keyword_asset, NULL, UTF8_3GP_Style, keyword_count, 8); - APar_atom_Binary_Put(keyword_asset, formed_keyword_struct, keyword_struct_bytes, 3); - } - } - if (formed_keyword_struct != NULL) { - free(formed_keyword_struct); - formed_keyword_struct = NULL; - } - } else { - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - APar_UserData_atom_Init("kywd", "", userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - } - } - break; + case _3GP_Keyword: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "keyword")) { + break; + } + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + char *formed_keyword_struct = NULL; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 4); + + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + if (strrchr(optarg, '=') != + NULL) { // must be in the format of: keywords=foo1,foo2,foo3,foo4 + char *arg_keywords = optarg; + char *keywords_globbed = + strsep(&arg_keywords, "="); // separate out 'keyword=' + keywords_globbed = + strsep(&arg_keywords, + "="); // this is what we want to work on: just the keywords + char *keyword_ptr = keywords_globbed; + uint32_t keyword_strlen = strlen(keywords_globbed); + uint8_t keyword_count = 0; + uint32_t key_index = 0; + + if (keyword_strlen > + 0) { // if there is anything past the = then it counts as a keyword + keyword_count++; + } + + while (true) { // count occurrences of comma here + if (*keyword_ptr == ',') { + keyword_count++; + } + keyword_ptr++; + key_index++; + if (keyword_strlen == key_index) { + break; + } + } + + formed_keyword_struct = (char *)calloc( + 1, + sizeof(char) * set_UTF16_text + ? (keyword_strlen * 4) + : (keyword_strlen * 2)); // *4 should carry utf16's BOM & TERM + uint32_t keyword_struct_bytes = + APar_3GP_Keyword_atom_Format(keywords_globbed, + keyword_count, + set_UTF16_text, + formed_keyword_struct); + + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + AtomicInfo *keyword_asset = APar_UserData_atom_Init( + "kywd", + keyword_strlen ? "temporary" : "", + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); // just a "temporary" valid string to satisfy a test + // there + if (keyword_strlen > 0) { + APar_Unified_atom_Put( + keyword_asset, NULL, UTF8_3GP_Style, packed_lang, 16); + APar_Unified_atom_Put( + keyword_asset, NULL, UTF8_3GP_Style, keyword_count, 8); + APar_atom_Binary_Put( + keyword_asset, formed_keyword_struct, keyword_struct_bytes, 3); + } + } + if (formed_keyword_struct != NULL) { + free(formed_keyword_struct); + formed_keyword_struct = NULL; + } + } else { + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + APar_UserData_atom_Init("kywd", + "", + userdata_area, + asset_iterations == 1 ? selected_track + : i_asset, + packed_lang); } + } + break; + } - case _3GP_Location : { - APar_ScanAtoms(ISObasemediafile); - if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "location") ) { - break; - } - bool set_UTF16_text = false; - uint16_t packed_lang = 0; - uint8_t userdata_area = MOVIE_LEVEL_ATOM; - uint8_t selected_track = 0; - uint8_t a_track = 0;//unused - uint8_t asset_iterations = 0; - double longitude = -73.98; //if you don't provide a place, you WILL be put right into Central Park. Welcome to New York City... now go away. - double latitude = 40.77; - double altitude = 4.3; - uint8_t role = 0; - const char* astronomical_body = "Earth"; - const char* additional_notes = "no notes"; - - find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 10); - - for (int i= 0; i <= 10; i++) { //9 possible arguments for this tag (the first - which doesn't count - is the data for the tag itself) - if ( argv[optind + i] && optind + i <= total_args) { - if ( strncmp(argv[optind + i], "longitude=", 10) == 0 ) { - char* _long = argv[optind + i]; - strsep(&_long,"="); - sscanf(_long, "%lf", &longitude); - if (_long[strlen(_long)-1] == 'W') { - longitude*=-1; - } - } - if ( strncmp(argv[optind + i], "latitude=", 9) == 0 ) { - char* _latt = argv[optind + i]; - strsep(&_latt,"="); - sscanf(_latt, "%lf", &latitude); - if (_latt[strlen(_latt)-1] == 'S') { - latitude*=-1; - } - } - if ( strncmp(argv[optind + i], "altitude=", 9) == 0 ) { - char* _alti = argv[optind + i]; - strsep(&_alti,"="); - sscanf(_alti, "%lf", &altitude); - if (_alti[strlen(_alti)-1] == 'B') { - altitude*=-1; - } - } - if ( strncmp(argv[optind + i], "role=", 5) == 0 ) { - char* _role = argv[optind + i]; - strsep(&_role,"="); - if (strcmp(_role, "shooting location") == 0 || strcmp(_role, "shooting") == 0) { - role = 0; - } else if (strcmp(_role, "real location") == 0 || strcmp(_role, "real") == 0) { - role = 1; - } else if (strcmp(_role, "fictional location") == 0 || strcmp(_role, "fictional") == 0) { - role = 2; - } - } - if ( strncmp(argv[optind + i], "body=", 5) == 0 ) { - char* _astrobody = argv[optind + i]; - strsep(&_astrobody,"="); - astronomical_body = _astrobody; - } - if ( strncmp(argv[optind + i], "notes=", 6) == 0 ) { - char* _add_notes = argv[optind + i]; - strsep(&_add_notes,"="); - additional_notes = _add_notes; - } - if (*argv[optind + i] == '-') break; //we've hit another cli argument - } - } - - //fprintf(stdout, "long, lat, alt = %lf %lf %lf\n", longitude, latitude, altitude); - if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) { - asset_iterations = 1; - } else if (userdata_area == ALL_TRACKS_ATOM) { - APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here. - if (asset_iterations == 1) selected_track = 1; - } - - if (longitude < -180.0 || longitude > 180.0 || latitude < -90.0 || latitude > 90.0) { - fprintf(stdout, "AtomicParsley warning: longitude or latitude was invalid; skipping setting location\n"); - } else { - for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { - //short location_3GP_atom = APar_UserData_atom_Init("moov.udta.loci", optarg, packed_lang); - AtomicInfo* location_asset = APar_UserData_atom_Init("loci", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang); - APar_Unified_atom_Put(location_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16); - APar_Unified_atom_Put(location_asset, NULL, false, role, 8); - - APar_Unified_atom_Put(location_asset, NULL, false, float_to_16x16bit_fixed_point(longitude), 32); - APar_Unified_atom_Put(location_asset, NULL, false, float_to_16x16bit_fixed_point(latitude), 32); - APar_Unified_atom_Put(location_asset, NULL, false, float_to_16x16bit_fixed_point(altitude), 32); - APar_Unified_atom_Put(location_asset, astronomical_body, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), 0, 0); - APar_Unified_atom_Put(location_asset, additional_notes, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), 0, 0); - } - } - break; + case _3GP_Location: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER && + metadata_style < MOTIONJPEG2000, + 2, + "location")) { + break; + } + bool set_UTF16_text = false; + uint16_t packed_lang = 0; + uint8_t userdata_area = MOVIE_LEVEL_ATOM; + uint8_t selected_track = 0; + uint8_t a_track = 0; // unused + uint8_t asset_iterations = 0; + double longitude = + -73.98; // if you don't provide a place, you WILL be put right into + // Central Park. Welcome to New York City... now go away. + double latitude = 40.77; + double altitude = 4.3; + uint8_t role = 0; + const char *astronomical_body = "Earth"; + const char *additional_notes = "no notes"; + + find_optional_args(argv, + optind, + packed_lang, + set_UTF16_text, + userdata_area, + selected_track, + 10); + + for (int i = 0; i <= 10; + i++) { // 9 possible arguments for this tag (the first - which + // doesn't count - is the data for the tag itself) + if (argv[optind + i] && optind + i <= total_args) { + if (strncmp(argv[optind + i], "longitude=", 10) == 0) { + char *_long = argv[optind + i]; + strsep(&_long, "="); + sscanf(_long, "%lf", &longitude); + if (_long[strlen(_long) - 1] == 'W') { + longitude *= -1; + } + } + if (strncmp(argv[optind + i], "latitude=", 9) == 0) { + char *_latt = argv[optind + i]; + strsep(&_latt, "="); + sscanf(_latt, "%lf", &latitude); + if (_latt[strlen(_latt) - 1] == 'S') { + latitude *= -1; + } + } + if (strncmp(argv[optind + i], "altitude=", 9) == 0) { + char *_alti = argv[optind + i]; + strsep(&_alti, "="); + sscanf(_alti, "%lf", &altitude); + if (_alti[strlen(_alti) - 1] == 'B') { + altitude *= -1; + } + } + if (strncmp(argv[optind + i], "role=", 5) == 0) { + char *_role = argv[optind + i]; + strsep(&_role, "="); + if (strcmp(_role, "shooting location") == 0 || + strcmp(_role, "shooting") == 0) { + role = 0; + } else if (strcmp(_role, "real location") == 0 || + strcmp(_role, "real") == 0) { + role = 1; + } else if (strcmp(_role, "fictional location") == 0 || + strcmp(_role, "fictional") == 0) { + role = 2; + } + } + if (strncmp(argv[optind + i], "body=", 5) == 0) { + char *_astrobody = argv[optind + i]; + strsep(&_astrobody, "="); + astronomical_body = _astrobody; + } + if (strncmp(argv[optind + i], "notes=", 6) == 0) { + char *_add_notes = argv[optind + i]; + strsep(&_add_notes, "="); + additional_notes = _add_notes; + } + if (*argv[optind + i] == '-') + break; // we've hit another cli argument + } + } + + // fprintf(stdout, "long, lat, alt = %lf %lf %lf\n", longitude, latitude, + // altitude); + if (userdata_area == MOVIE_LEVEL_ATOM || + userdata_area == SINGLE_TRACK_ATOM) { + asset_iterations = 1; + } else if (userdata_area == ALL_TRACKS_ATOM) { + APar_FindAtomInTrack( + asset_iterations, + a_track, + NULL); // With asset_iterations set to 0, it will return the total + // trak atom into total_tracks here. + if (asset_iterations == 1) + selected_track = 1; + } + + if (longitude < -180.0 || longitude > 180.0 || latitude < -90.0 || + latitude > 90.0) { + fprintf(stdout, + "AtomicParsley warning: longitude or latitude was " + "invalid; skipping setting location\n"); + } else { + for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) { + // short location_3GP_atom = APar_UserData_atom_Init("moov.udta.loci", + // optarg, packed_lang); + AtomicInfo *location_asset = APar_UserData_atom_Init( + "loci", + optarg, + userdata_area, + asset_iterations == 1 ? selected_track : i_asset, + packed_lang); + APar_Unified_atom_Put( + location_asset, + optarg, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + packed_lang, + 16); + APar_Unified_atom_Put(location_asset, NULL, false, role, 8); + + APar_Unified_atom_Put(location_asset, + NULL, + false, + float_to_16x16bit_fixed_point(longitude), + 32); + APar_Unified_atom_Put(location_asset, + NULL, + false, + float_to_16x16bit_fixed_point(latitude), + 32); + APar_Unified_atom_Put(location_asset, + NULL, + false, + float_to_16x16bit_fixed_point(altitude), + 32); + APar_Unified_atom_Put( + location_asset, + astronomical_body, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + 0, + 0); + APar_Unified_atom_Put( + location_asset, + additional_notes, + (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), + 0, + 0); } + } + break; + } - case Meta_ReverseDNS_Form : { //--rDNSatom "mv-ma" name=iTuneEXTC domain=com.apple.iTunes - char* reverseDNS_atomname = NULL; - char* reverseDNS_atomdomain = NULL; - uint32_t rdns_atom_flags = AtomFlags_Data_Text; - - APar_ScanAtoms(ISObasemediafile); + case Meta_ReverseDNS_Form: { //--rDNSatom "mv-ma" name=iTuneEXTC + // domain=com.apple.iTunes + char *reverseDNS_atomname = NULL; + char *reverseDNS_atomdomain = NULL; + uint32_t rdns_atom_flags = AtomFlags_Data_Text; - if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "reverse DNS form") ) { - break; - } + APar_ScanAtoms(ISObasemediafile); - for (int i= 0; i <= 5-1; i++) { - if ( argv[optind + i] && optind + i <= argc ) { - if ( strncmp(argv[optind + i], "name=", 5) == 0 ) { - reverseDNS_atomname = argv[optind + i]+5; - } else if ( strncmp(argv[optind + i], "domain=", 7) == 0 ) { - reverseDNS_atomdomain = argv[optind + i]+7; - } else if ( strncmp(argv[optind + i], "datatype=", 9) == 0 ) { - sscanf(argv[optind + i]+9, "%" SCNu32, &rdns_atom_flags); - } - if (*argv[optind + i] == '-') { - break; //we've hit another cli argument - } - } - } + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "reverse DNS form")) { + break; + } - if (reverseDNS_atomname == NULL) { - fprintf(stdout, "AtomicParsley warning: no name for the reverseDNS form was found. Skipping.\n"); + for (int i = 0; i <= 5 - 1; i++) { + if (argv[optind + i] && optind + i <= argc) { + if (strncmp(argv[optind + i], "name=", 5) == 0) { + reverseDNS_atomname = argv[optind + i] + 5; + } else if (strncmp(argv[optind + i], "domain=", 7) == 0) { + reverseDNS_atomdomain = argv[optind + i] + 7; + } else if (strncmp(argv[optind + i], "datatype=", 9) == 0) { + sscanf(argv[optind + i] + 9, "%" SCNu32, &rdns_atom_flags); + } + if (*argv[optind + i] == '-') { + break; // we've hit another cli argument + } + } + } + + if (reverseDNS_atomname == NULL) { + fprintf(stdout, + "AtomicParsley warning: no name for the reverseDNS " + "form was found. Skipping.\n"); + + } else if ((int)strlen(reverseDNS_atomname) != + test_conforming_alpha_string(reverseDNS_atomname)) { + fprintf(stdout, + "AtomicParsley warning: Some part of the reverseDNS " + "atom name was non-conforming. Skipping.\n"); + + } else if (reverseDNS_atomdomain == NULL) { + fprintf(stdout, + "AtomicParsley warning: no domain for the reverseDNS " + "form was found. Skipping.\n"); + + } else if (rdns_atom_flags != AtomFlags_Data_Text) { + fprintf(stdout, + "AtomicParsley warning: currently, only the strings " + "are supported in reverseDNS atoms. Skipping.\n"); + + } else { + AtomicInfo *rDNS_data_atom = + APar_reverseDNS_atom_Init(reverseDNS_atomname, + optarg, + &rdns_atom_flags, + reverseDNS_atomdomain); + APar_Unified_atom_Put( + rDNS_data_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); + } - } else if ((int)strlen(reverseDNS_atomname) != test_conforming_alpha_string(reverseDNS_atomname) ) { - fprintf(stdout, "AtomicParsley warning: Some part of the reverseDNS atom name was non-conforming. Skipping.\n"); + break; + } - } else if (reverseDNS_atomdomain == NULL) { - fprintf(stdout, "AtomicParsley warning: no domain for the reverseDNS form was found. Skipping.\n"); + case Meta_rDNS_rating: { + const char *media_rating = Expand_cli_mediastring(optarg); + uint32_t rDNS_data_flags = AtomFlags_Data_Text; + + APar_ScanAtoms(ISObasemediafile); + + if (media_rating != NULL || strlen(optarg) == 0) { + AtomicInfo *rDNS_rating_data_atom = APar_reverseDNS_atom_Init( + "iTunEXTC", media_rating, &rDNS_data_flags, "com.apple.iTunes"); + APar_Unified_atom_Put(rDNS_rating_data_atom, + media_rating, + UTF8_iTunesStyle_Unlimited, + 0, + 0); + } - } else if (rdns_atom_flags != AtomFlags_Data_Text) { - fprintf(stdout, "AtomicParsley warning: currently, only the strings are supported in reverseDNS atoms. Skipping.\n"); + break; + } - } else { - AtomicInfo* rDNS_data_atom = APar_reverseDNS_atom_Init(reverseDNS_atomname, optarg, &rdns_atom_flags, reverseDNS_atomdomain); - APar_Unified_atom_Put(rDNS_data_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0); - } + case Meta_ID3v2Tag: { + const char *target_frame_ID = NULL; + uint16_t packed_lang = 0; + uint8_t char_encoding = TE_UTF8; // utf8 is the default encoding + char meta_container = 0 - MOVIE_LEVEL_ATOM; + bool multistring = false; + APar_ScanAtoms(ISObasemediafile); + + // limit the files that can be tagged with meta.ID32 atoms. The file has + // to conform to the ISO BMFFv2 in order for a 'meta' atom. This should + // exclude files branded as 3gp5 for example, except it doesn't always. + // The test is for a compatible brand (of a v2 ISO MBFF). Quicktime writes + // some 3GPP files as 3gp5 with a compatible brand of mp42, so tagging + // works on these files. Not when you use timed text though. + if (!APar_assert(parsedAtoms[0].ancillary_data != 0 || + (metadata_style >= THIRD_GEN_PARTNER_VER1_REL7 && + metadata_style < MOTIONJPEG2000), + 3, + NULL)) { + break; + } + AdjunctArgs *id3args = (AdjunctArgs *)malloc(sizeof(AdjunctArgs)); - break; + id3args->targetLang = NULL; // it will default later to "eng" + id3args->descripArg = NULL; + id3args->mimeArg = NULL; + id3args->pictypeArg = NULL; + id3args->ratingArg = NULL; + id3args->dataArg = NULL; + id3args->pictype_uint8 = 0; + id3args->groupSymbol = 0; + id3args->zlibCompressed = false; + id3args->multistringtext = false; + + target_frame_ID = ConvertCLIFrameStr_TO_frameID(optarg); + if (target_frame_ID == NULL) { + target_frame_ID = optarg; + } + + int frameType = FrameStr_TO_FrameType(target_frame_ID); + if (frameType >= 0) { + if (TestCLI_for_FrameParams(frameType, 0)) { + id3args->descripArg = find_ID3_optarg(argv, optind, "desc="); + } + if (TestCLI_for_FrameParams(frameType, 1)) { + id3args->mimeArg = find_ID3_optarg(argv, optind, "mimetype="); + } + if (TestCLI_for_FrameParams(frameType, 2)) { + id3args->pictypeArg = find_ID3_optarg(argv, optind, "imagetype="); + } + if (TestCLI_for_FrameParams(frameType, 3)) { + id3args->dataArg = find_ID3_optarg(argv, optind, "uniqueID="); + } + if (TestCLI_for_FrameParams(frameType, 4)) { + id3args->filenameArg = find_ID3_optarg(argv, optind, "filename="); + } + if (TestCLI_for_FrameParams(frameType, 5)) { + id3args->ratingArg = find_ID3_optarg(argv, optind, "rating="); + } + if (TestCLI_for_FrameParams(frameType, 6)) { + id3args->dataArg = find_ID3_optarg(argv, optind, "counter="); + } + if (TestCLI_for_FrameParams(frameType, 7)) { + id3args->dataArg = find_ID3_optarg(argv, optind, "data="); + } + if (TestCLI_for_FrameParams(frameType, 8)) { + id3args->dataArg = find_ID3_optarg(argv, optind, "data="); + } + if (*find_ID3_optarg(argv, optind, "compressed") == '1') { + id3args->zlibCompressed = true; + } + + const char *groupsymbol = find_ID3_optarg(argv, optind, "groupsymbol="); + if (groupsymbol[0] == '0' && groupsymbol[1] == 'x') { + id3args->groupSymbol = strtoul(groupsymbol, NULL, 16); + if (id3args->groupSymbol < 0x80 || id3args->groupSymbol > 0xF0) + id3args->groupSymbol = 0; } + } - case Meta_rDNS_rating : { - const char* media_rating = Expand_cli_mediastring(optarg); - uint32_t rDNS_data_flags = AtomFlags_Data_Text; - - APar_ScanAtoms(ISObasemediafile); - - if (media_rating != NULL || strlen(optarg) == 0) { - AtomicInfo* rDNS_rating_data_atom = APar_reverseDNS_atom_Init("iTunEXTC", media_rating, &rDNS_data_flags, "com.apple.iTunes"); - APar_Unified_atom_Put(rDNS_rating_data_atom, media_rating, UTF8_iTunesStyle_Unlimited, 0, 0); - } - - break; + scan_ID3_optargs(argv, + optind, + id3args->targetLang, + packed_lang, + char_encoding, + &meta_container, + multistring); + if (id3args->targetLang == NULL) + id3args->targetLang = "eng"; + + APar_OpenISOBaseMediaFile( + ISObasemediafile, + true); // if not already scanned, the whole tag for + // *this* ID32 atom needs to be read from file + AtomicInfo *id32_atom = APar_ID32_atom_Init( + target_frame_ID, meta_container, id3args->targetLang, packed_lang); + + if (strcmp(argv[optind + 0], "extract") == 0 && + (strcmp(target_frame_ID, "APIC") == 0 || + strcmp(target_frame_ID, "GEOB") == 0)) { + if (id32_atom != NULL) { + APar_Extract_ID3v2_file( + id32_atom, target_frame_ID, ISObasemediafile, NULL, id3args); + APar_OpenISOBaseMediaFile(ISObasemediafile, false); } + exit(0); + } - case Meta_ID3v2Tag : { - const char* target_frame_ID = NULL; - uint16_t packed_lang = 0; - uint8_t char_encoding = TE_UTF8; //utf8 is the default encoding - char meta_container = 0-MOVIE_LEVEL_ATOM; - bool multistring = false; - APar_ScanAtoms(ISObasemediafile); - - //limit the files that can be tagged with meta.ID32 atoms. The file has to conform to the ISO BMFFv2 in order for a 'meta' atom. - //This should exclude files branded as 3gp5 for example, except it doesn't always. The test is for a compatible brand (of a v2 ISO MBFF). - //Quicktime writes some 3GPP files as 3gp5 with a compatible brand of mp42, so tagging works on these files. Not when you use timed text though. - if ( !APar_assert(parsedAtoms[0].ancillary_data != 0 || (metadata_style >= THIRD_GEN_PARTNER_VER1_REL7 && metadata_style < MOTIONJPEG2000), 3, NULL) ) { - break; - } - AdjunctArgs* id3args = (AdjunctArgs*)malloc(sizeof(AdjunctArgs)); - - id3args->targetLang = NULL; //it will default later to "eng" - id3args->descripArg = NULL; - id3args->mimeArg = NULL; - id3args->pictypeArg = NULL; - id3args->ratingArg = NULL; - id3args->dataArg = NULL; - id3args->pictype_uint8 = 0; - id3args->groupSymbol = 0; - id3args->zlibCompressed = false; - id3args->multistringtext = false; - - target_frame_ID = ConvertCLIFrameStr_TO_frameID(optarg); - if (target_frame_ID == NULL) { - target_frame_ID = optarg; - } - - int frameType = FrameStr_TO_FrameType(target_frame_ID); - if (frameType >= 0) { - if (TestCLI_for_FrameParams(frameType, 0)) { - id3args->descripArg = find_ID3_optarg(argv, optind, "desc="); - } - if (TestCLI_for_FrameParams(frameType, 1)) { - id3args->mimeArg = find_ID3_optarg(argv, optind, "mimetype="); - } - if (TestCLI_for_FrameParams(frameType, 2)) { - id3args->pictypeArg = find_ID3_optarg(argv, optind, "imagetype="); - } - if (TestCLI_for_FrameParams(frameType, 3)) { - id3args->dataArg = find_ID3_optarg(argv, optind, "uniqueID="); - } - if (TestCLI_for_FrameParams(frameType, 4)) { - id3args->filenameArg = find_ID3_optarg(argv, optind, "filename="); - } - if (TestCLI_for_FrameParams(frameType, 5)) { - id3args->ratingArg = find_ID3_optarg(argv, optind, "rating="); - } - if (TestCLI_for_FrameParams(frameType, 6)) { - id3args->dataArg = find_ID3_optarg(argv, optind, "counter="); - } - if (TestCLI_for_FrameParams(frameType, 7)) { - id3args->dataArg = find_ID3_optarg(argv, optind, "data="); - } - if (TestCLI_for_FrameParams(frameType, 8)) { - id3args->dataArg = find_ID3_optarg(argv, optind, "data="); - } - if (*find_ID3_optarg(argv, optind, "compressed") == '1') { - id3args->zlibCompressed = true; - } - - const char* groupsymbol = find_ID3_optarg(argv, optind, "groupsymbol="); - if (groupsymbol[0] == '0' && groupsymbol[1] == 'x') { - id3args->groupSymbol = strtoul(groupsymbol, NULL, 16); - if (id3args->groupSymbol < 0x80 || id3args->groupSymbol > 0xF0) id3args->groupSymbol = 0; - } - } + APar_OpenISOBaseMediaFile(ISObasemediafile, false); + APar_ID3FrameAmmend( + id32_atom, target_frame_ID, argv[optind + 0], id3args, char_encoding); - scan_ID3_optargs(argv, optind, id3args->targetLang, packed_lang, char_encoding, &meta_container, multistring); - if (id3args->targetLang == NULL) id3args->targetLang = "eng"; + free(id3args); + id3args = NULL; - APar_OpenISOBaseMediaFile(ISObasemediafile, true); //if not already scanned, the whole tag for *this* ID32 atom needs to be read from file - AtomicInfo* id32_atom = APar_ID32_atom_Init(target_frame_ID, meta_container, id3args->targetLang, packed_lang); + break; + } - if (strcmp(argv[optind + 0], "extract") == 0 && (strcmp(target_frame_ID, "APIC") == 0 || strcmp(target_frame_ID, "GEOB") == 0)) { - if (id32_atom != NULL) { - APar_Extract_ID3v2_file(id32_atom, target_frame_ID, ISObasemediafile, NULL, id3args); - APar_OpenISOBaseMediaFile(ISObasemediafile, false); - } - exit(0); - } + // utility functions - APar_OpenISOBaseMediaFile(ISObasemediafile, false); - APar_ID3FrameAmmend(id32_atom, target_frame_ID, argv[optind + 0], id3args, char_encoding); + case Metadata_Purge: { + APar_ScanAtoms(ISObasemediafile); + APar_RemoveAtom("moov.udta.meta.ilst", SIMPLE_ATOM, 0); - free(id3args); - id3args = NULL; + break; + } - break; - } + case UserData_Purge: { + APar_ScanAtoms(ISObasemediafile); + APar_RemoveAtom("moov.udta", SIMPLE_ATOM, 0); - //utility functions + break; + } - case Metadata_Purge : { - APar_ScanAtoms(ISObasemediafile); - APar_RemoveAtom("moov.udta.meta.ilst", SIMPLE_ATOM, 0); + case foobar_purge: { + APar_ScanAtoms(ISObasemediafile); + APar_RemoveAtom("moov.udta.tags", UNKNOWN_ATOM, 0); - break; - } + break; + } - case UserData_Purge : { - APar_ScanAtoms(ISObasemediafile); - APar_RemoveAtom("moov.udta", SIMPLE_ATOM, 0); + case Opt_FreeFree: { + APar_ScanAtoms(ISObasemediafile); + int free_level = -1; + if (argv[optind]) { + sscanf(argv[optind], "%i", &free_level); + } + APar_freefree(free_level); - break; - } + break; + } - case foobar_purge : { - APar_ScanAtoms(ISObasemediafile); - APar_RemoveAtom("moov.udta.tags", UNKNOWN_ATOM, 0); + case OPT_OverWrite: { + alter_original = true; + break; + } - break; - } +#if defined(_WIN32) + case OPT_PreserveTimeStamps: { + alter_original = true; + preserve_timestamps = true; + break; + } +#endif - case Opt_FreeFree : { - APar_ScanAtoms(ISObasemediafile); - int free_level = -1; - if (argv[optind]) { - sscanf(argv[optind], "%i", &free_level); - } - APar_freefree(free_level); + case Meta_dump: { + APar_ScanAtoms(ISObasemediafile); + APar_OpenISOBaseMediaFile(ISObasemediafile, true); + APar_MetadataFileDump(ISObasemediafile); + APar_OpenISOBaseMediaFile(ISObasemediafile, false); - break; - } + APar_FreeMemory(); + exit(0); + } - case OPT_OverWrite : { - alter_original = true; - break; - } + case OPT_NoOptimize: { + force_existing_hierarchy = true; + break; + } -#if defined (_WIN32) - case OPT_PreserveTimeStamps : { - alter_original = true; - preserve_timestamps = true; - break; - } -#endif + case OPT_OutputFile: { + output_file = optarg; + break; + } - case Meta_dump : { - APar_ScanAtoms(ISObasemediafile); - APar_OpenISOBaseMediaFile(ISObasemediafile, true); - APar_MetadataFileDump(ISObasemediafile); - APar_OpenISOBaseMediaFile(ISObasemediafile, false); + case Meta_movementCount: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementCount")) { + break; + } + uint8_t data_value = 0; + sscanf(optarg, "%" SCNu8, &data_value); + AtomicInfo *atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251mvc.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put( + atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 8); + break; + } - APar_FreeMemory(); - exit(0); - } + case Meta_movementName: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementName")) { + break; + } + AtomicInfo *atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251mvn.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - case OPT_NoOptimize : { - force_existing_hierarchy = true; - break; - } + case Meta_movementNumber: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementNumber")) { + break; + } + uint8_t data_value = 0; + sscanf(optarg, "%" SCNu8, &data_value); + AtomicInfo *atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251mvi.data", optarg, AtomFlags_Data_UInt); + APar_Unified_atom_Put( + atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 8); + break; + } - case OPT_OutputFile : { - output_file = optarg; - break; - } + case Meta_showWorkMovement: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "showWorkMovement")) { + break; + } + AtomicInfo *atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.shwm.data", optarg, AtomFlags_Data_Binary); + APar_Unified_atom_Put( + atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } - } /* end switch */ - } /* end while */ + case Meta_work: { + APar_ScanAtoms(ISObasemediafile); + if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "work")) { + break; + } + AtomicInfo *atom = APar_MetaData_atom_Init( + "moov.udta.meta.ilst.\251wrk.data", optarg, AtomFlags_Data_Text); + APar_Unified_atom_Put( + atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0); + break; + } + } /* end switch */ + } /* end while */ - // after all the modifications are enacted on the tree in memory, THEN - // write out the changes + // after all the modifications are enacted on the tree in memory, THEN + // write out the changes - if (modified_atoms) { + if (modified_atoms) { -#if defined (_WIN32) - HANDLE hFile, hFileOut; - FILETIME createTime, accessTime, writeTime; - if (preserve_timestamps == true) - { - hFile = APar_OpenFileWin32(ISObasemediafile, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); - if (hFile != INVALID_HANDLE_VALUE) - { - GetFileTime(hFile,&createTime,&accessTime,&writeTime); - CloseHandle(hFile); - } - else - { - fprintf (stdout,"\n Invalid HANDLE!"); - } - } +#if defined(_WIN32) + HANDLE hFile, hFileOut; + FILETIME createTime, accessTime, writeTime; + if (preserve_timestamps == true) { + hFile = APar_OpenFileWin32(ISObasemediafile, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + 0); + if (hFile != INVALID_HANDLE_VALUE) { + GetFileTime(hFile, &createTime, &accessTime, &writeTime); + CloseHandle(hFile); + } else { + fprintf(stdout, "\n Invalid HANDLE!"); + } + } #endif - APar_DetermineAtomLengths(); - APar_OpenISOBaseMediaFile(ISObasemediafile, true); - APar_WriteFile(ISObasemediafile, output_file, alter_original); - -#if defined (_WIN32) - if (preserve_timestamps == true) - { - - hFileOut = APar_OpenFileWin32(ISObasemediafile, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); - - if (hFileOut != INVALID_HANDLE_VALUE) - { - SetFileTime(hFileOut,&createTime,&accessTime,&writeTime); - CloseHandle(hFileOut); - } - } + APar_DetermineAtomLengths(); + APar_OpenISOBaseMediaFile(ISObasemediafile, true); + APar_WriteFile(ISObasemediafile, output_file, alter_original); + +#if defined(_WIN32) + if (preserve_timestamps == true) { + + hFileOut = APar_OpenFileWin32(ISObasemediafile, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + 0); + + if (hFileOut != INVALID_HANDLE_VALUE) { + SetFileTime(hFileOut, &createTime, &accessTime, &writeTime); + CloseHandle(hFileOut); + } + } #endif - - if (!alter_original) { - // The file was opened orignally as read-only; when it came time to - // writeback into the original file, that FILE was closed, and a - // new one opened with write abilities, so to close a FILE that no - // longer exists would.... be retarded. - APar_OpenISOBaseMediaFile(ISObasemediafile, false); - } - } else { - if (ISObasemediafile != NULL && argc > 3 && !deep_atom_scan) { - fprintf(stdout, "No changes.\n"); - } + if (!alter_original) { + // The file was opened orignally as read-only; when it came time to + // writeback into the original file, that FILE was closed, and a + // new one opened with write abilities, so to close a FILE that no + // longer exists would.... be retarded. + APar_OpenISOBaseMediaFile(ISObasemediafile, false); + } + } else { + if (ISObasemediafile != NULL && argc > 3 && !deep_atom_scan) { + fprintf(stdout, "No changes.\n"); } - APar_FreeMemory(); - return 0; + } + APar_FreeMemory(); + return 0; } -#if defined (_WIN32) +#if defined(_WIN32) -#if !defined (__CYGWIN__) +#if !defined(__CYGWIN__) -int wmain( int argc, wchar_t *arguments[]) -{ - int return_val=0; - uint16_t name_len = wcslen(arguments[0]); - if (name_len >= 9 && _wcsicmp(arguments[0] + (name_len-9), L"-utf8.exe") == 0) { - UnicodeOutputStatus = UNIVERSAL_UTF8; +int wmain(int argc, wchar_t *arguments[]) { + int return_val = 0; + uint16_t name_len = wcslen(arguments[0]); + if (name_len >= 9 && + _wcsicmp(arguments[0] + (name_len - 9), L"-utf8.exe") == 0) { + UnicodeOutputStatus = UNIVERSAL_UTF8; + } else { + UnicodeOutputStatus = WIN32_UTF16; + } + + char **argv = (char **)calloc(argc + 1, sizeof(char *)); + + // for native Win32 & full unicode support (well, cli arguments anyway), + // take whatever 16bit unicode windows gives (utf16le), and convert + // EVERYTHING that is sends to utf8; use those utf8 strings (mercifully + // subject to familiar standby's like strlen) to pass around the program + // like getopt_long to get cli options; convert from utf8 filenames as + // required for unicode filename support on Windows using wide file + // functions. + for (int z = 0; z < argc; z++) { + uint32_t wchar_length = wcslen(arguments[z]) + 1; + argv[z] = (char *)malloc(sizeof(char) * 8 * wchar_length); + memset(argv[z], 0, 8 * wchar_length); + if (UnicodeOutputStatus == WIN32_UTF16) { + UTF16LEToUTF8((unsigned char *)argv[z], + 8 * wchar_length, + (unsigned char *)arguments[z], + wchar_length * 2); } else { - UnicodeOutputStatus = WIN32_UTF16; + strip_bogusUTF16toRawUTF8((unsigned char *)argv[z], + 8 * wchar_length, + arguments[z], + wchar_length); } + } - char **argv = (char**)calloc(argc + 1, sizeof(char*)); + argv[argc] = NULL; - // for native Win32 & full unicode support (well, cli arguments anyway), - // take whatever 16bit unicode windows gives (utf16le), and convert - // EVERYTHING that is sends to utf8; use those utf8 strings (mercifully - // subject to familiar standby's like strlen) to pass around the program - // like getopt_long to get cli options; convert from utf8 filenames as - // required for unicode filename support on Windows using wide file - // functions. - for(int z=0; z < argc; z++) { - uint32_t wchar_length = wcslen(arguments[z])+1; - argv[z] = (char *)malloc(sizeof(char)*8*wchar_length ); - memset(argv[z], 0, 8*wchar_length); - if (UnicodeOutputStatus == WIN32_UTF16) { - UTF16LEToUTF8((unsigned char*) argv[z], 8*wchar_length, (unsigned char*) arguments[z], wchar_length*2); - } else { - strip_bogusUTF16toRawUTF8((unsigned char*) argv[z], 8*wchar_length, arguments[z], wchar_length ); - } - } + return_val = real_main(argc, argv); - argv[argc] = NULL; + for (int free_cnt = 0; free_cnt < argc; free_cnt++) { + free(argv[free_cnt]); + argv[free_cnt] = NULL; + } - return_val = real_main(argc, argv); + free(argv); + argv = NULL; - for (int free_cnt=0; free_cnt= 5 && - (strcmp(argv[0] + (name_len-5), "-utf8") == 0 || - strcmp(argv[0] + (name_len-5), "-UTF8") == 0)) { - UnicodeOutputStatus = UNIVERSAL_UTF8; - } else { - UnicodeOutputStatus = WIN32_UTF16; - } - return real_main(argc, argv); +int main(int argc, char *argv[]) { + size_t name_len = strlen(argv[0]); + if (name_len >= 5 && (strcmp(argv[0] + (name_len - 5), "-utf8") == 0 || + strcmp(argv[0] + (name_len - 5), "-UTF8") == 0)) { + UnicodeOutputStatus = UNIVERSAL_UTF8; + } else { + UnicodeOutputStatus = WIN32_UTF16; + } + return real_main(argc, argv); } #endif #else -int main( int argc, char *argv[]) -{ - return real_main(argc, argv); -} +int main(int argc, char *argv[]) { return real_main(argc, argv); } #endif diff -Nru atomicparsley-0.9.6/src/metalist.cpp atomicparsley-20210715.151551.e7ad03a/src/metalist.cpp --- atomicparsley-0.9.6/src/metalist.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/metalist.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - metalist.cpp - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,12 +10,12 @@ 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 + 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 2006-2007 puck_lock + Copyright (C) 2006-2007 puck_lock with contributions from others; see the CREDITS file */ //==================================================================// @@ -24,1529 +24,2140 @@ 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; +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"); - } + 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 + fprintf(stdout, "\xEF\xBB\xBF"); // Default to output of a UTF-8 BOM #endif - BOM_printed = true; - return; + 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; +#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; - } +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); + fprintf(stdout, "%s", utf8_encoded_data); #endif - return; + 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; } -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; - } +// 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); + 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; + + free(utf8_data); + utf8_data = NULL; + + } else { // utf8 + if (print_encoding) { + fprintf(stdout, " (utf8): "); + } + + APar_fprintf_UTF8_data(unicode_string); + } + return; } /////////////////////////////////////////////////////////////////////////////////////// -// embedded file extraction // +// 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. + 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\xE0", 4) == 0 || memcmp(art_payload, "\xFF\xD8\xFF\xE1", 4) == 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); +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 + 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; +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. + 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; +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 // +// 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 in 'ART' to a 2byte utf8 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; + 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 in 'ART' to a 2byte utf8 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 // +// 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; +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 // +// 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; - } +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); + 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; + + 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 // +// 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; +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 // +// 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_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; + 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" + 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. + 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; + 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. + 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; + 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 // +// 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. + 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 in 'ART' to a 2byte utf8 glyph - } else { - isolat1ToUTF8((unsigned char*)twenty_byte_buffer, 10, (unsigned char*)thisAtom->AtomicName, 4); //converts iso8859 in 'ART' to a 2byte utf8 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; + 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 in 'ART' to a 2byte utf8 glyph + } else { + isolat1ToUTF8((unsigned char *)twenty_byte_buffer, + 10, + (unsigned char *)thisAtom->AtomicName, + 4); // converts iso8859 in 'ART' to a 2byte utf8 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 + 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); +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); } diff -Nru atomicparsley-0.9.6/src/nsfile.mm atomicparsley-20210715.151551.e7ad03a/src/nsfile.mm --- atomicparsley-0.9.6/src/nsfile.mm 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/nsfile.mm 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - nsfile.mm - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,7 +10,7 @@ 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 + 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 @@ -26,157 +26,192 @@ /*---------------------- APar_TestTracksForKind - By testing which tracks are contained within the file, for Mac OS X we can avoid having to change file extension by instead using Finder.app metadata to signal - the same info as file extension. For each trak atom, find the 'stsd' atom - its ancillary_data will contain the track type that is contained - the info is filled - in as the file was initially parsed in APar_ScanAtoms. Then using Mac OS X Cocoa calls (in AP_NSFile_utils), set the Finder TYPE/CREATOR codes to signal to the - OS/Finder/iTunes that this file is .m4a or .m4v without having to change its extension based on what the tracks actually contain. - - There are 2 issues with this - iTunes requires the Quicktime player type/creator codes for video that has multi-channel audio, and for chapterized video files. - TODO: address these issues. + By testing which tracks are contained within the file, for Mac OS X we can +avoid having to change file extension by instead using Finder.app metadata to +signal the same info as file extension. For each trak atom, find the 'stsd' atom +- its ancillary_data will contain the track type that is contained - the info is +filled in as the file was initially parsed in APar_ScanAtoms. Then using Mac OS +X Cocoa calls (in AP_NSFile_utils), set the Finder TYPE/CREATOR codes to signal +to the OS/Finder/iTunes that this file is .m4a or .m4v without having to change +its extension based on what the tracks actually contain. + + There are 2 issues with this - iTunes requires the Quicktime +player type/creator codes for video that has multi-channel audio, and for +chapterized video files. + TODO: address these issues. ----------------------*/ void APar_TestTracksForKind() { - uint8_t total_tracks = 0; - uint8_t track_num = 0; - AtomicInfo* codec_atom = NULL; //short codec_atom = 0; - - //With track_num set to 0, it will return the total trak atom into total_tracks here. - APar_FindAtomInTrack(total_tracks, track_num, NULL); - - if (total_tracks > 0) { - while (total_tracks > track_num) { - track_num+= 1; - - codec_atom = APar_FindAtomInTrack(total_tracks, track_num, "stsd"); - if (codec_atom == NULL) return; - - //now test this trak's stsd codec against these 4cc codes: - switch(codec_atom->ancillary_data) { - //video types - case 0x61766331 : // "avc1" - track_codecs.has_avc1 = true; - break; - case 0x6D703476 : // "mp4v" - track_codecs.has_mp4v = true; - break; - case 0x64726D69 : // "drmi" - track_codecs.has_drmi = true; - break; - - //audio types - case 0x616C6163 : // "alac" - track_codecs.has_alac = true; - break; - case 0x6D703461 : // "mp4a" - track_codecs.has_mp4a = true; - break; - case 0x64726D73 : // "drms" - track_codecs.has_drms = true; - break; - - //chapterized types (audio podcasts or movies) - case 0x74657874 : // "text" - track_codecs.has_timed_text = true; - break; - case 0x6A706567 : // "jpeg" - track_codecs.has_timed_jpeg = true; - break; - - //either podcast type (audio-only) or timed text subtitles - case 0x74783367 : // "tx3g" - track_codecs.has_timed_tx3g = true; - break; - - //other - case 0x6D703473 : // "mp4s" - track_codecs.has_mp4s = true; - break; - case 0x72747020 : // "rtp " - track_codecs.has_rtp_hint = true; - break; - } - } - } - return; + uint8_t total_tracks = 0; + uint8_t track_num = 0; + AtomicInfo *codec_atom = NULL; // short codec_atom = 0; + + // With track_num set to 0, it will return the total trak atom into + // total_tracks here. + APar_FindAtomInTrack(total_tracks, track_num, NULL); + + if (total_tracks > 0) { + while (total_tracks > track_num) { + track_num += 1; + + codec_atom = APar_FindAtomInTrack(total_tracks, track_num, "stsd"); + if (codec_atom == NULL) + return; + + // now test this trak's stsd codec against these 4cc codes: + switch (codec_atom->ancillary_data) { + // video types + case 0x61766331: // "avc1" + track_codecs.has_avc1 = true; + break; + case 0x6D703476: // "mp4v" + track_codecs.has_mp4v = true; + break; + case 0x64726D69: // "drmi" + track_codecs.has_drmi = true; + break; + + // audio types + case 0x616C6163: // "alac" + track_codecs.has_alac = true; + break; + case 0x6D703461: // "mp4a" + track_codecs.has_mp4a = true; + break; + case 0x64726D73: // "drms" + track_codecs.has_drms = true; + break; + + // chapterized types (audio podcasts or movies) + case 0x74657874: // "text" + track_codecs.has_timed_text = true; + break; + case 0x6A706567: // "jpeg" + track_codecs.has_timed_jpeg = true; + break; + + // either podcast type (audio-only) or timed text subtitles + case 0x74783367: // "tx3g" + track_codecs.has_timed_tx3g = true; + break; + + // other + case 0x6D703473: // "mp4s" + track_codecs.has_mp4s = true; + break; + case 0x72747020: // "rtp " + track_codecs.has_rtp_hint = true; + break; + } + } + } + return; } - -//TODO: there is a problem with this code seen in: "5.1channel audio-orig.mp4" -//it makes no difference what the file contains, iTunes won't see any (ANY) metadata if its hook/'M4A '. -//in fact, iTunes won't play the file at all -//changing the exact file (with all kinds of metadata) to TVOD/mpg4 - iTunes can play it fine, but doesn't fetch any metadata +// TODO: there is a problem with this code seen in: "5.1channel audio-orig.mp4" +// it makes no difference what the file contains, iTunes won't see any (ANY) +// metadata if its hook/'M4A '. in fact, iTunes won't play the file at all +// changing the exact file (with all kinds of metadata) to TVOD/mpg4 - iTunes +// can play it fine, but doesn't fetch any metadata // -//it might be beneficial to eval for channels and if its audio only & multichannel to NOT change the TYPE/creator codes +// it might be beneficial to eval for channels and if its audio only & +// multichannel to NOT change the TYPE/creator codes + +uint32_t APar_4CC_CreatorCode(const char *filepath, uint32_t new_type_code) { + uint32_t return_value = 0; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *inFile = [NSString stringWithUTF8String:filepath]; + + if (new_type_code) { + NSNumber *creator_code = [NSNumber numberWithUnsignedLong:'hook']; + NSNumber *type_code = [NSNumber numberWithUnsignedLong:new_type_code]; + NSDictionary *output_attributes = + [NSDictionary dictionaryWithObjectsAndKeys:creator_code, + NSFileHFSCreatorCode, + type_code, + NSFileHFSTypeCode, + nil]; + + if (![[NSFileManager defaultManager] changeFileAttributes:output_attributes + atPath:inFile]) { + NSLog(@" AtomicParsley error: setting type and creator code on %@", + inFile); + } + + } else { + NSDictionary *file_attributes = + [[NSFileManager defaultManager] fileAttributesAtPath:inFile + traverseLink:YES]; + return_value = + [[file_attributes objectForKey:NSFileHFSTypeCode] unsignedLongValue]; + + // NSLog(@"code: %@\n", [file_attributes objectForKey:NSFileHFSTypeCode] ); + } + + [pool release]; -uint32_t APar_4CC_CreatorCode(const char* filepath, uint32_t new_type_code) { - uint32_t return_value = 0; - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - NSString *inFile = [NSString stringWithUTF8String: filepath]; - - if (new_type_code) { - NSNumber* creator_code = [NSNumber numberWithUnsignedLong:'hook']; - NSNumber* type_code = [NSNumber numberWithUnsignedLong:new_type_code]; - NSDictionary* output_attributes = [NSDictionary dictionaryWithObjectsAndKeys:creator_code, NSFileHFSCreatorCode, - type_code, NSFileHFSTypeCode, nil]; - - if (![[NSFileManager defaultManager] changeFileAttributes:output_attributes atPath:inFile]) { - NSLog(@" AtomicParsley error: setting type and creator code on %@", inFile); - } - - } else { - NSDictionary* file_attributes = [[NSFileManager defaultManager] fileAttributesAtPath:inFile traverseLink:YES]; - return_value = [[file_attributes objectForKey:NSFileHFSTypeCode] unsignedLongValue ]; - - //NSLog(@"code: %@\n", [file_attributes objectForKey:NSFileHFSTypeCode] ); - } - - [pool release]; - - return return_value; + return return_value; } -//there is a scenario that is as of now unsupported (or botched, depending if you use the feature), although it would be easy to implement. To make a file bookmarkable, the TYPE code is set to 'M4B ' - which can be *also* done by changing the extension to ".m4b". However, due to the way that the file is tested here, a ".mp4" with 'M4B ' type code will get changed into a normal audio file (not-bookmarkable). +// there is a scenario that is as of now unsupported (or botched, depending if +// you use the feature), although it would be easy to implement. To make a file +// bookmarkable, the TYPE code is set to 'M4B ' - which can be *also* done by +// changing the extension to ".m4b". However, due to the way that the file is +// tested here, a ".mp4" with 'M4B ' type code will get changed into a normal +// audio file (not-bookmarkable). + +void APar_SupplySelectiveTypeCreatorCodes(const char *inputPath, + const char *outputPath, + uint8_t forced_type_code) { + if (forced_type_code != NO_TYPE_FORCING) { + if (forced_type_code == FORCE_M4B_TYPE) { + APar_4CC_CreatorCode(outputPath, 'M4B '); + } + return; + } + + const char *input_suffix = strrchr(inputPath, '.'); + // user-defined output paths may have the original file as ".m4a" & show up + // fine when output to ".m4a" output to ".mp4" and it becomes a generic (sans + // TYPE/CREATOR) file that defaults to Quicktime Player + const char *output_suffix = strrchr(outputPath, '.'); + + char *typecode = (char *)malloc(sizeof(char) * 4); + memset(typecode, 0, sizeof(char) * 4); + + uint32_t type_code = APar_4CC_CreatorCode(inputPath, 0); + + UInt32_TO_String4(type_code, typecode); + + // fprintf(stdout, "%s - %s\n", typecode, input_suffix); + APar_TestTracksForKind(); + + if (strncasecmp(input_suffix, ".mp4", 4) == 0 || + strncasecmp(output_suffix, ".mp4", 4) == + 0) { // only work on the generic .mp4 extension + if (track_codecs.has_avc1 || track_codecs.has_mp4v || + track_codecs.has_drmi) { + type_code = APar_4CC_CreatorCode(outputPath, 'M4V '); + + // for a podcast an audio track with either a text, jpeg or url track is + // required, otherwise it will fall through to generic m4a; files that are + // already .m4b or 'M4B ' don't even enter into this situation, so they + // are safe if the file had video with subtitles (tx3g), then it would get + // taken care of above in the video section - unsupported by QT currently + } else if (track_codecs.has_mp4a && + (track_codecs.has_timed_text || track_codecs.has_timed_jpeg || + track_codecs.has_timed_tx3g)) { + type_code = APar_4CC_CreatorCode(outputPath, 'M4B '); + + // default to audio; technically so would a drms iTMS drm audio file with + // ".mp4". But that would also mean it was renamed. They should be 'M4P ' + } else { + type_code = APar_4CC_CreatorCode(outputPath, 'M4A '); + } + } else if (track_codecs.has_avc1 || track_codecs.has_mp4v || + track_codecs.has_drmi) { + type_code = APar_4CC_CreatorCode(outputPath, 'M4V '); + } -void APar_SupplySelectiveTypeCreatorCodes(const char *inputPath, const char *outputPath, uint8_t forced_type_code) { - if (forced_type_code != NO_TYPE_FORCING) { - if (forced_type_code == FORCE_M4B_TYPE) { - APar_4CC_CreatorCode(outputPath, 'M4B '); - } - return; - } - - char* input_suffix = strrchr(inputPath, '.'); - //user-defined output paths may have the original file as ".m4a" & show up fine when output to ".m4a" - //output to ".mp4" and it becomes a generic (sans TYPE/CREATOR) file that defaults to Quicktime Player - char* output_suffix = strrchr(outputPath, '.'); - - char* typecode = (char*)malloc( sizeof(char)* 4 ); - memset(typecode, 0, sizeof(char)*4); - - uint32_t type_code = APar_4CC_CreatorCode(inputPath, 0); - - UInt32_TO_String4(type_code, typecode); - - //fprintf(stdout, "%s - %s\n", typecode, input_suffix); - APar_TestTracksForKind(); - - if (strncasecmp(input_suffix, ".mp4", 4) == 0 || strncasecmp(output_suffix, ".mp4", 4) == 0) { //only work on the generic .mp4 extension - if (track_codecs.has_avc1 || track_codecs.has_mp4v || track_codecs.has_drmi) { - type_code = APar_4CC_CreatorCode(outputPath, 'M4V '); - - //for a podcast an audio track with either a text, jpeg or url track is required, otherwise it will fall through to generic m4a; - //files that are already .m4b or 'M4B ' don't even enter into this situation, so they are safe - //if the file had video with subtitles (tx3g), then it would get taken care of above in the video section - unsupported by QT currently - } else if (track_codecs.has_mp4a && (track_codecs.has_timed_text || track_codecs.has_timed_jpeg || track_codecs.has_timed_tx3g) ) { - type_code = APar_4CC_CreatorCode(outputPath, 'M4B '); - - //default to audio; technically so would a drms iTMS drm audio file with ".mp4". But that would also mean it was renamed. They should be 'M4P ' - } else { - type_code = APar_4CC_CreatorCode(outputPath, 'M4A '); - } - } else if (track_codecs.has_avc1 || track_codecs.has_mp4v || track_codecs.has_drmi) { - type_code = APar_4CC_CreatorCode(outputPath, 'M4V '); - } - - return; + return; } diff -Nru atomicparsley-0.9.6/src/nsimage.mm atomicparsley-20210715.151551.e7ad03a/src/nsimage.mm --- atomicparsley-0.9.6/src/nsimage.mm 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/nsimage.mm 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - nsimage.mm - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,7 +10,7 @@ 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 + 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 @@ -23,222 +23,246 @@ #include "AtomicParsley.h" #import -bool isJPEG=false; -bool isPNG=false; +static void DetermineType(const char *picfilePath, bool &isJPEG, bool &isPNG) { + char picHeader[20]; -void DetermineType(const char *picfilePath) { - char* picHeader = (char*)calloc(1, sizeof(char)*20); - u_int64_t r; - - FILE *pic_file = NULL; - pic_file = fopen(picfilePath, "rb"); - r = fread(picHeader, 8, 1, pic_file); + FILE *pic_file = fopen(picfilePath, "rb"); + u_int64_t r = fread(picHeader, 8, 1, pic_file); fclose(pic_file); - - if (memcmp(picHeader, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) { - isPNG=true; - isJPEG=false; - } else if (memcmp(picHeader, "\xFF\xD8\xFF\xE0", 4) == 0) { - isJPEG=true; - isPNG=false; - } - free(picHeader); - picHeader=NULL; - return; + + if (memcmp(picHeader, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) { + isPNG = true; + isJPEG = false; + } else if (memcmp(picHeader, "\xFF\xD8\xFF", 3) == 0) { + isJPEG = true; + isPNG = false; + } else { + isPNG = false; + isJPEG = false; + } } -char* DeriveNewPath(const char *filePath, PicPrefs myPicPrefs, char* newpath) { - char* suffix = strrchr(filePath, '.'); - - size_t filepath_len = strlen(filePath); - memset(newpath, 0, MAXPATHLEN+1); - size_t base_len = filepath_len-strlen(suffix); - memcpy(newpath, filePath, base_len); - memcpy(newpath+base_len, "-resized-", 9); - - char* randstring = (char*)calloc(1, sizeof(char)*20); - struct timeval tv; - gettimeofday (&tv, NULL); - - srand( (int) tv.tv_usec / 1000 ); //Seeds rand() - int randNum = rand()%10000; - sprintf(randstring, "%i", randNum); - strcat(newpath, randstring); - - if (myPicPrefs.allJPEG) { - strcat(newpath, ".jpg"); - } else if (myPicPrefs.allPNG) { - strcat(newpath, ".png"); - } else { - strcat(newpath, suffix); - } - - if ( (strncmp(suffix,".jpg",4) == 0) || (strncmp(suffix,".jpeg",5) == 0) || (strncmp(suffix,".JPG",4) == 0) || (strncmp(suffix,".JPEG",5) == 0) ) { - isJPEG=true; - } else if ((strncmp(suffix,".png",4) == 0) || (strncmp(suffix,".PNG",4) == 0)) { - isPNG=true; - } - - free(randstring); - randstring=NULL; - return newpath; +static char *DeriveNewPath(const char *filePath, + PicPrefs myPicPrefs, + char *newpath, + size_t newpath_len) { + const char *suffix = strrchr(filePath, '.'); + + size_t filepath_len = strlen(filePath); + memset(newpath, 0, newpath_len); + size_t base_len = filepath_len - strlen(suffix); + memcpy(newpath, filePath, base_len); + memcpy(newpath + base_len, "-resized-", 9); + + char *randstring = (char *)calloc(1, sizeof(char) * 20); + struct timeval tv; + gettimeofday(&tv, NULL); + + srand((int)tv.tv_usec / 1000); // Seeds rand() + int randNum = rand() % 10000; + sprintf(randstring, "%i", randNum); + strcat(newpath, randstring); + + if (myPicPrefs.allJPEG) { + strcat(newpath, ".jpg"); + } else if (myPicPrefs.allPNG) { + strcat(newpath, ".png"); + } else { + strcat(newpath, suffix); + } + + free(randstring); + randstring = NULL; + return newpath; } -bool ResizeGivenImage(const char* filePath, PicPrefs myPicPrefs, char* resized_path) { - bool resize = false; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSImage* source = [ [NSImage alloc] initWithContentsOfFile: [NSString stringWithUTF8String: filePath] ]; - [source setScalesWhenResized: YES]; - if ( source == nil ) { - fprintf( stderr, "Image '%s' could not be loaded.\n", filePath ); - exit (1); - } - - NSSize sourceSize = [source size]; - float hmax, vmax, aspect; - hmax = sourceSize.width; - vmax = sourceSize.height; - aspect = sourceSize.height / sourceSize.width; - //fprintf(stdout, "aspect %f2.4\n", aspect); - if (myPicPrefs.max_dimension != 0) { - if ( ( (int)sourceSize.width > myPicPrefs.max_dimension) || ( (int)sourceSize.height > myPicPrefs.max_dimension) ) { - resize = true; //only if dimensions are LARGER than our max do we resize - if (hmax > vmax) { - hmax = myPicPrefs.max_dimension; - vmax = myPicPrefs.max_dimension * aspect; - } else { - hmax = myPicPrefs.max_dimension / aspect; - vmax = myPicPrefs.max_dimension; - } - } - } - - ///// determine dpi/ppi - float hres, vres, hdpi, vdpi; - NSImageRep *myRep = [[source representations] objectAtIndex:0]; - hres = [myRep pixelsWide]; //native pixel dimensions - vres = [myRep pixelsHigh]; - hdpi = hres/sourceSize.width; //in native resolution (multiply by 72 to get native dpi) - vdpi = vres/sourceSize.height; - - if ( ( (int)hdpi != 1 ) || ( (int)vdpi != 1) ) { - resize = true; - hmax = hres; - vmax = vres; - if (myPicPrefs.max_dimension != 0) { - //we also need to recheck we don't go over our max dimensions (again) - if ( ( (int)hres > myPicPrefs.max_dimension) || ( (int)vres > myPicPrefs.max_dimension) ) { - if (hmax > vmax) { - hmax = myPicPrefs.max_dimension; - vmax = myPicPrefs.max_dimension * aspect; - } else { - hmax = myPicPrefs.max_dimension / aspect; - vmax = myPicPrefs.max_dimension; - } - } - } - } - - if (myPicPrefs.squareUp) { - if (myPicPrefs.max_dimension != 0) { - vmax = myPicPrefs.max_dimension; - hmax = myPicPrefs.max_dimension; - resize = true; - } else { - //this will stretch the image to the largest dimension. Hope you don't try to scale a 160x1200 image... it could get ugly - if (hmax > vmax) { - vmax = hmax; - resize = true; - } else if (vmax > hmax) { - hmax = vmax; - resize = true; - } - } - } - - if (myPicPrefs.force_dimensions) { - if (myPicPrefs.force_height > 0 && myPicPrefs.force_width > 0) { - vmax = myPicPrefs.force_height; - hmax = myPicPrefs.force_width; - resize = true; - } - } - - uint64_t pic_file_size = findFileSize(filePath); - if ( ( (int)pic_file_size > myPicPrefs.max_Kbytes) && ( myPicPrefs.max_Kbytes != 0) ) { - resize = true; - } - - DetermineType(filePath); - if ( (isJPEG && myPicPrefs.allPNG) || (isPNG && myPicPrefs.allJPEG) ) { //handle jpeg->png & png->jpg conversion - resize = true; - - } - - NSRect destinationRect = NSMakeRect( 0, 0, hmax, vmax ); - NSSize size = NSMakeSize( hmax, vmax ); - - if (resize) { - [NSApplication sharedApplication]; - [[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationHigh]; - - [source setSize: size]; - - NSImage* image = [[NSImage alloc] initWithSize:size]; - [image lockFocus]; - - NSEraseRect( destinationRect ); - [source drawInRect: destinationRect - fromRect: destinationRect - operation: NSCompositeCopy fraction: 1.0]; - - NSBitmapImageRep* bitmap = [ [NSBitmapImageRep alloc] - initWithFocusedViewRect: destinationRect ]; - NSBitmapImageFileType filetype; - NSDictionary *props; - - if ( (isPNG && !myPicPrefs.allJPEG) || myPicPrefs.allPNG) { - filetype = NSPNGFileType; - props = nil; - - } else { - filetype = NSJPEGFileType; - props = [ NSDictionary dictionaryWithObject: - [NSNumber numberWithFloat: 0.7] forKey: NSImageCompressionFactor]; - } - NSData* data = [bitmap representationUsingType:filetype properties:props]; - - unsigned dataLength = [data length]; //holds the file length - - int iter = 0; - float compression = 0.65; - if ( (myPicPrefs.max_Kbytes != 0) && (filetype == NSJPEGFileType) ) { - while ( (dataLength > (unsigned)myPicPrefs.max_Kbytes) && (iter < 10) ) { - props = [ NSDictionary dictionaryWithObject: - [NSNumber numberWithFloat: compression] forKey: NSImageCompressionFactor]; - data = [bitmap representationUsingType:filetype properties:props]; - dataLength = [data length]; - compression = compression - 0.05; - iter++; - } - } - - [bitmap release]; - NSString *outFile= [NSString stringWithUTF8String: DeriveNewPath(filePath, myPicPrefs, resized_path)]; - //NSLog(outFile); - [[NSFileManager defaultManager] - createFileAtPath: outFile - contents: data - attributes: nil ]; - - [image unlockFocus]; - [image release]; - isJPEG=false; - isPNG=false; - memcpy(resized_path, [outFile cStringUsingEncoding: NSUTF8StringEncoding], [outFile lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); - } - [source release]; - [pool release]; - return resize; +static NSImage *DoResize(NSImage *sourceImage, NSSize newSize) { + if (!sourceImage.isValid) { + return nil; + } + + NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:NULL + pixelsWide:newSize.width + pixelsHigh:newSize.height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedRGBColorSpace + bytesPerRow:0 + bitsPerPixel:0]; + rep.size = newSize; + + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext + setCurrentContext:[NSGraphicsContext + graphicsContextWithBitmapImageRep:rep]]; + [sourceImage drawInRect:NSMakeRect(0, 0, newSize.width, newSize.height) + fromRect:NSZeroRect + operation:NSCompositingOperationCopy + fraction:1.0]; + [NSGraphicsContext restoreGraphicsState]; + + NSImage *newImage = [[NSImage alloc] initWithSize:newSize]; + [newImage addRepresentation:rep]; + return newImage; +} + +bool ResizeGivenImage(const char *filePath, + PicPrefs myPicPrefs, + char *resized_path, + size_t resized_path_len) { + bool resize = false; + + NSImage *source = [[NSImage alloc] + initWithContentsOfFile:[NSString stringWithUTF8String:filePath]]; + if (source == nil) { + fprintf(stderr, "Image '%s' could not be loaded.\n", filePath); + exit(1); + } + + NSSize sourceSize = [source size]; + float hmax, vmax, aspect; + hmax = sourceSize.width; + vmax = sourceSize.height; + aspect = sourceSize.height / sourceSize.width; + // fprintf(stdout, "aspect %f2.4\n", aspect); + if (myPicPrefs.max_dimension != 0) { + if (((int)sourceSize.width > myPicPrefs.max_dimension) || + ((int)sourceSize.height > myPicPrefs.max_dimension)) { + resize = true; // only if dimensions are LARGER than our max do we resize + if (hmax > vmax) { + hmax = myPicPrefs.max_dimension; + vmax = myPicPrefs.max_dimension * aspect; + } else { + hmax = myPicPrefs.max_dimension / aspect; + vmax = myPicPrefs.max_dimension; + } + } + } + + ///// determine dpi/ppi + float hres, vres, hdpi, vdpi; + NSImageRep *myRep = [[source representations] objectAtIndex:0]; + hres = [myRep pixelsWide]; // native pixel dimensions + vres = [myRep pixelsHigh]; + hdpi = hres / + sourceSize + .width; // in native resolution (multiply by 72 to get native dpi) + vdpi = vres / sourceSize.height; + + if (((int)hdpi != 1) || ((int)vdpi != 1)) { + resize = true; + hmax = hres; + vmax = vres; + if (myPicPrefs.max_dimension != 0) { + // we also need to recheck we don't go over our max dimensions (again) + if (((int)hres > myPicPrefs.max_dimension) || + ((int)vres > myPicPrefs.max_dimension)) { + if (hmax > vmax) { + hmax = myPicPrefs.max_dimension; + vmax = myPicPrefs.max_dimension * aspect; + } else { + hmax = myPicPrefs.max_dimension / aspect; + vmax = myPicPrefs.max_dimension; + } + } + } + } + + if (myPicPrefs.squareUp) { + if (myPicPrefs.max_dimension != 0) { + vmax = myPicPrefs.max_dimension; + hmax = myPicPrefs.max_dimension; + resize = true; + } else { + // this will stretch the image to the largest dimension. Hope you don't + // try to scale a 160x1200 image... it could get ugly + if (hmax > vmax) { + vmax = hmax; + resize = true; + } else if (vmax > hmax) { + hmax = vmax; + resize = true; + } + } + } + + if (myPicPrefs.force_dimensions) { + if (myPicPrefs.force_height > 0 && myPicPrefs.force_width > 0) { + vmax = myPicPrefs.force_height; + hmax = myPicPrefs.force_width; + resize = true; + } + } + + uint64_t pic_file_size = findFileSize(filePath); + if (((int)pic_file_size > myPicPrefs.max_Kbytes) && + (myPicPrefs.max_Kbytes != 0)) { + resize = true; + } + + bool isJPEG, isPNG; + DetermineType(filePath, isJPEG, isPNG); + if ((isJPEG && myPicPrefs.allPNG) || + (isPNG && myPicPrefs.allJPEG)) { // handle jpeg->png & png->jpg conversion + resize = true; + } + + NSRect destinationRect = NSMakeRect(0, 0, hmax, vmax); + NSSize size = NSMakeSize(hmax, vmax); + + if (resize) { + NSImage *image = DoResize(source, size); + + NSData *imageData = [image TIFFRepresentation]; + NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:imageData]; + + NSBitmapImageFileType filetype; + NSDictionary *props; + + if ((isPNG && !myPicPrefs.allJPEG) || myPicPrefs.allPNG) { + filetype = NSBitmapImageFileTypePNG; + props = nil; + } else { + filetype = NSBitmapImageFileTypeJPEG; + props = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.7] + forKey:NSImageCompressionFactor]; + } + NSData *data = [bitmap representationUsingType:filetype properties:props]; + unsigned dataLength = [data length]; // holds the file length + + int iter = 0; + float compression = 0.65; + if ((myPicPrefs.max_Kbytes != 0) && + (filetype == NSBitmapImageFileTypeJPEG)) { + while ((dataLength > (unsigned)myPicPrefs.max_Kbytes) && (iter < 10)) { + props = [NSDictionary + dictionaryWithObject:[NSNumber numberWithFloat:compression] + forKey:NSImageCompressionFactor]; + data = [bitmap representationUsingType:filetype properties:props]; + dataLength = [data length]; + compression = compression - 0.05; + iter++; + } + } + + NSString *outFile = + [NSString stringWithUTF8String:DeriveNewPath(filePath, + myPicPrefs, + resized_path, + resized_path_len)]; + [[NSFileManager defaultManager] createFileAtPath:outFile + contents:data + attributes:nil]; + + [image release]; + [bitmap release]; + memcpy(resized_path, + [outFile cStringUsingEncoding:NSUTF8StringEncoding], + [outFile lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + } + [source release]; + return resize; } diff -Nru atomicparsley-0.9.6/src/parsley.cpp atomicparsley-20210715.151551.e7ad03a/src/parsley.cpp --- atomicparsley-0.9.6/src/parsley.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/parsley.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -15,7 +15,7 @@ cannot, write to the Free Software Foundation, 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org - Copyright 2005-2007 puck_lock + Copyright (C) 2005-2007 puck_lock with contributions from others; see the CREDITS file ---------------------- @@ -23,12 +23,13 @@ * Mike Brancato - Debian patches & build support * Lowell Stewart - null-termination bugfix for Apple compliance - * Brian Story - native Win32 patches; memset/framing/leaks fixes + * Brian Story - native Win32 patches; memset/framing/leaks fixes */ //==================================================================// -#include "AtomicParsley.h" #include "AtomDefs.h" +#include "AtomicParsley.h" +#include //#define DEBUG_V @@ -40,8 +41,7 @@ bool alter_original = false; bool preserve_timestamps = false; - -FILE* source_file = NULL; +FILE *source_file = NULL; uint64_t file_size; struct AtomicInfo parsedAtoms[MAX_ATOMS]; @@ -52,48 +52,54 @@ bool parsedfile = false; bool move_moov_atom = true; bool moov_atom_was_mooved = false; -AtomicInfo* hdlrAtom = NULL; -AtomicInfo* movie_header_atom = NULL; +AtomicInfo *hdlrAtom = NULL; +AtomicInfo *movie_header_atom = NULL; bool complete_free_space_erasure = false; bool psp_brand = false; bool force_existing_hierarchy = false; int metadata_style = UNDEFINED_STYLE; bool deep_atom_scan = false; -uint32_t max_buffer = +uint64_t max_buffer = #ifdef __linux__ - 0.5 /* splice() allows us to use less buffer space */ + 0.5 /* splice() allows us to use less buffer space */ #else - 10 + 10 #endif - *1024*1024 -; + * 1024 * 1024; -uint64_t bytes_before_mdat=0; +uint64_t bytes_before_mdat = 0; uint64_t bytes_into_mdat = 0; uint64_t mdat_supplemental_offset = 0; uint64_t removed_bytes_tally = 0; -uint64_t new_file_size = 0; //used for the progressbar +uint64_t new_file_size = 0; // used for the progressbar uint32_t brand = 0; -uint64_t mdatData = 0; //now global, used in bitrate calcs +uint64_t mdatData = 0; // now global, used in bitrate calcs -uint64_t gapless_void_padding = 0; //possibly used in the context of gapless playback support by Apple +uint64_t gapless_void_padding = + 0; // possibly used in the context of gapless playback support by Apple struct DynamicUpdateStat dynUpd; struct padding_preferences pad_prefs; short max_display_width = 55; -char* file_progress_buffer=(char*)calloc(1, sizeof(char)* (max_display_width+50) ); //+50 for any overflow in "%100", or "|" +char *file_progress_buffer = (char *)calloc( + 1, + sizeof(char) * + (max_display_width + 50)); //+50 for any overflow in "%100", or "|" -#if defined (DARWIN_PLATFORM) +#if defined(__APPLE__) struct PicPrefs myPicturePrefs; #endif bool parsed_prefs = false; -char* twenty_byte_buffer = (char *)malloc(sizeof(char)*20); +char *twenty_byte_buffer = (char *)malloc(sizeof(char) * 20); -EmployedCodecs track_codecs = {false, false, false, false, false, false, false, false, false, false}; +EmployedCodecs track_codecs = { + false, false, false, false, false, false, false, false, false, false}; -uint8_t UnicodeOutputStatus = UNIVERSAL_UTF8; //on windows, controls whether input/output strings are utf16 or raw utf8; reset in wmain() +uint8_t UnicodeOutputStatus = + UNIVERSAL_UTF8; // on windows, controls whether input/output strings are + // utf16 or raw utf8; reset in wmain() uint8_t forced_suffix_type = NO_TYPE_FORCING; @@ -103,940 +109,1173 @@ void ShowVersionInfo() { -#if defined (_WIN32) - char *unicode_enabled; - if (UnicodeOutputStatus == WIN32_UTF16) { +#if defined(_WIN32) + char *unicode_enabled; + if (UnicodeOutputStatus == WIN32_UTF16) { #ifndef __CYGWIN__ - unicode_enabled = "(utf16)"; + unicode_enabled = "(utf16)"; #else - unicode_enabled = "(utf8 with utf16 CD access)"; + unicode_enabled = "(utf8 with utf16 CD access)"; #endif - // its utf16 in the sense that any text entering on a modern Win32 system - // enters as utf16le - but gets converted immediately after AP.exe starts - // to utf8 all arguments, strings, filenames, options are sent around as - // utf8. For modern Win32 systems, filenames get converted to utf16 for - // output as needed. Any strings to be set as utf16 in 3gp assets are - // converted to utf16be as needed (true for all OS implementations). - // Printing out to the console should be utf8. + // its utf16 in the sense that any text entering on a modern Win32 system + // enters as utf16le - but gets converted immediately after AP.exe starts + // to utf8 all arguments, strings, filenames, options are sent around as + // utf8. For modern Win32 systems, filenames get converted to utf16 for + // output as needed. Any strings to be set as utf16 in 3gp assets are + // converted to utf16be as needed (true for all OS implementations). + // Printing out to the console should be utf8. - } else if (UnicodeOutputStatus == UNIVERSAL_UTF8) { + } else if (UnicodeOutputStatus == UNIVERSAL_UTF8) { #ifndef __CYGWIN__ - unicode_enabled = "(raw utf8)"; + unicode_enabled = "(raw utf8)"; #else - unicode_enabled = "(utf8 with raw utf8 CD access)"; + unicode_enabled = "(utf8 with raw utf8 CD access)"; #endif - // utf8 in the sense that any text entered had its utf16 upper byte - // stripped and reduced to (unchecked) raw utf8 for utilities that work in - // utf8. Any unicode (utf16) filenames were clobbered in that processes are - // invalid now. Any intermediate folder with unicode in it will now likely - // cause an error of some sort. - } + // utf8 in the sense that any text entered had its utf16 upper byte + // stripped and reduced to (unchecked) raw utf8 for utilities that work in + // utf8. Any unicode (utf16) filenames were clobbered in that processes are + // invalid now. Any intermediate folder with unicode in it will now likely + // cause an error of some sort. + } #else -#define unicode_enabled "(utf8)" +#define unicode_enabled "(utf8)" #endif - fprintf(stdout, "AtomicParsley version: %s %s\n", - PACKAGE_VERSION, unicode_enabled); - + fprintf(stdout, + "AtomicParsley version: %s %s %s\n", + PACKAGE_VERSION, + BUILD_INFO, + unicode_enabled); } /////////////////////////////////////////////////////////////////////////////////////// -// Generic Functions // +// Generic Functions // /////////////////////////////////////////////////////////////////////////////////////// -int APar_TestArtworkBinaryData(const char* artworkPath) { - int artwork_dataType = 0; - FILE *artfile = APar_OpenFile(artworkPath, "rb"); - if (artfile != NULL) { - APar_read64(twenty_byte_buffer, artfile, 0); - if ( strncmp(twenty_byte_buffer, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0 ) { - artwork_dataType = AtomFlags_Data_PNGBinary; - } else if ( strncmp(twenty_byte_buffer, "\xFF\xD8\xFF\xE0", 4) == 0 || memcmp(twenty_byte_buffer, "\xFF\xD8\xFF\xE1", 4) == 0 ) { - artwork_dataType = AtomFlags_Data_JPEGBinary; - } else { - fprintf(stdout, "AtomicParsley error: %s\n\t image file is not jpg/png and cannot be embedded.\n", artworkPath); - exit(1); - } - fclose(artfile); - - } else { - fprintf(stdout, "AtomicParsley error: %s\n\t image file could not be opened.\n", artworkPath); - exit(1); - } - return artwork_dataType; +int APar_TestArtworkBinaryData(const char *artworkPath) { + int artwork_dataType = 0; + FILE *artfile = APar_OpenFile(artworkPath, "rb"); + if (artfile != NULL) { + APar_read64(twenty_byte_buffer, artfile, 0); + if (strncmp(twenty_byte_buffer, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == + 0) { + artwork_dataType = AtomFlags_Data_PNGBinary; + } else if (memcmp(twenty_byte_buffer, "\xFF\xD8\xFF", 3) == 0) { + artwork_dataType = AtomFlags_Data_JPEGBinary; + } else { + fprintf(stdout, + "AtomicParsley error: %s\n\t image file is not jpg/png and " + "cannot be embedded.\n", + artworkPath); + exit(1); + } + fclose(artfile); + + } else { + fprintf(stdout, + "AtomicParsley error: %s\n\t image file could not be opened.\n", + artworkPath); + exit(1); + } + return artwork_dataType; } void APar_FreeMemory() { - for(int iter=0; iter < atom_number; iter++) { - if (parsedAtoms[iter].AtomicName != NULL) { - free(parsedAtoms[iter].AtomicName); - parsedAtoms[iter].AtomicName=NULL; - } - if (parsedAtoms[iter].AtomicData != NULL) { - free(parsedAtoms[iter].AtomicData); - parsedAtoms[iter].AtomicData = NULL; - } - if (parsedAtoms[iter].ReverseDNSname != NULL) { - free(parsedAtoms[iter].ReverseDNSname); - parsedAtoms[iter].ReverseDNSname = NULL; - } - if (parsedAtoms[iter].ReverseDNSdomain != NULL) { - free(parsedAtoms[iter].ReverseDNSdomain); - parsedAtoms[iter].ReverseDNSdomain = NULL; - } - if (parsedAtoms[iter].uuid_ap_atomname != NULL) { - free(parsedAtoms[iter].uuid_ap_atomname); - parsedAtoms[iter].uuid_ap_atomname = NULL; - } - if (parsedAtoms[iter].ID32_TagInfo != NULL) { - //a cascade of tests & free-ing of all the accrued ID3v2 tag/frame/field data - APar_FreeID32Memory(parsedAtoms[iter].ID32_TagInfo); - - free(parsedAtoms[iter].ID32_TagInfo); - parsedAtoms[iter].ID32_TagInfo = NULL; - } - } - free(twenty_byte_buffer); - twenty_byte_buffer=NULL; - free(file_progress_buffer); - file_progress_buffer=NULL; + for (int iter = 0; iter < atom_number; iter++) { + if (parsedAtoms[iter].AtomicName != NULL) { + free(parsedAtoms[iter].AtomicName); + parsedAtoms[iter].AtomicName = NULL; + } + if (parsedAtoms[iter].AtomicData != NULL) { + free(parsedAtoms[iter].AtomicData); + parsedAtoms[iter].AtomicData = NULL; + } + if (parsedAtoms[iter].ReverseDNSname != NULL) { + free(parsedAtoms[iter].ReverseDNSname); + parsedAtoms[iter].ReverseDNSname = NULL; + } + if (parsedAtoms[iter].ReverseDNSdomain != NULL) { + free(parsedAtoms[iter].ReverseDNSdomain); + parsedAtoms[iter].ReverseDNSdomain = NULL; + } + if (parsedAtoms[iter].uuid_ap_atomname != NULL) { + free(parsedAtoms[iter].uuid_ap_atomname); + parsedAtoms[iter].uuid_ap_atomname = NULL; + } + if (parsedAtoms[iter].ID32_TagInfo != NULL) { + // a cascade of tests & free-ing of all the accrued ID3v2 tag/frame/field + // data + APar_FreeID32Memory(parsedAtoms[iter].ID32_TagInfo); + + free(parsedAtoms[iter].ID32_TagInfo); + parsedAtoms[iter].ID32_TagInfo = NULL; + } + } + free(twenty_byte_buffer); + twenty_byte_buffer = NULL; + free(file_progress_buffer); + file_progress_buffer = NULL; - return; + return; } /////////////////////////////////////////////////////////////////////////////////////// -// Picture Preferences Functions // +// Picture Preferences Functions // /////////////////////////////////////////////////////////////////////////////////////// -#if defined (DARWIN_PLATFORM) -PicPrefs APar_ExtractPicPrefs(char* env_PicOptions) { - if (!parsed_prefs) { - - parsed_prefs = true; //only set default values & parse once - - myPicturePrefs.max_dimension=0; //dimensions won't be used to alter image - myPicturePrefs.dpi = 72; - myPicturePrefs.max_Kbytes = 0; //no target size to shoot for - myPicturePrefs.allJPEG = false; - myPicturePrefs.allPNG = false; - myPicturePrefs.addBOTHpix = false; - myPicturePrefs.force_dimensions = false; - myPicturePrefs.force_height = 0; - myPicturePrefs.force_width = 0; - myPicturePrefs.removeTempPix = true; //we'll just make this the default - - char* unparsed_opts = env_PicOptions; - if (env_PicOptions == NULL) return myPicturePrefs; - - while (unparsed_opts[0] != 0) { - if (strncmp(unparsed_opts,"MaxDimensions=",14) == 0) { - unparsed_opts+=14; - myPicturePrefs.max_dimension = (int)strtol(unparsed_opts, NULL, 10); - - } else if (strncmp(unparsed_opts,"DPI=",4) == 0) { - unparsed_opts+=4; - myPicturePrefs.dpi = (int)strtol(unparsed_opts, NULL, 10); - - } else if (strncmp(unparsed_opts,"MaxKBytes=",10) == 0) { - unparsed_opts+=10; - myPicturePrefs.max_Kbytes = (int)strtol(unparsed_opts, NULL, 10)*1024; - - } else if (strncmp(unparsed_opts,"AllPixJPEG=",11) == 0) { - unparsed_opts+=11; - if (strcmp(unparsed_opts, "true") == 0) { - myPicturePrefs.allJPEG = true; - } - - } else if (strncmp(unparsed_opts,"AllPixPNG=",10) == 0) { - unparsed_opts+=10; - if (strcmp(unparsed_opts, "true") == 0) { - myPicturePrefs.allPNG = true; - } - - } else if (strncmp(unparsed_opts,"AddBothPix=",11) == 0) { - unparsed_opts+=11; - if (strcmp(unparsed_opts, "true") == 0) { - myPicturePrefs.addBOTHpix = true; - } - - } else if (strcmp(unparsed_opts,"SquareUp") == 0) { - unparsed_opts+=7; - myPicturePrefs.squareUp = true; - - } else if (strcmp(unparsed_opts,"removeTempPix") == 0) { - unparsed_opts+=13; - myPicturePrefs.removeTempPix = true; - - } else if (strcmp(unparsed_opts,"keepTempPix") == 0) { //NEW - unparsed_opts+=11; - myPicturePrefs.removeTempPix = false; - - } else if (strncmp(unparsed_opts,"ForceHeight=",12) == 0) { - unparsed_opts+=12; - myPicturePrefs.force_height = strtol(unparsed_opts, NULL, 10); - - } else if (strncmp(unparsed_opts,"ForceWidth=",11) == 0) { - unparsed_opts+=11; - myPicturePrefs.force_width = strtol(unparsed_opts, NULL, 10); - - } else { - unparsed_opts++; - } - } - } - - if (myPicturePrefs.force_height > 0 && myPicturePrefs.force_width > 0) myPicturePrefs.force_dimensions = true; - return myPicturePrefs; +#if defined(__APPLE__) +PicPrefs APar_ExtractPicPrefs(char *env_PicOptions) { + if (!parsed_prefs) { + + parsed_prefs = true; // only set default values & parse once + + myPicturePrefs.max_dimension = 0; // dimensions won't be used to alter image + myPicturePrefs.dpi = 72; + myPicturePrefs.max_Kbytes = 0; // no target size to shoot for + myPicturePrefs.allJPEG = false; + myPicturePrefs.allPNG = false; + myPicturePrefs.addBOTHpix = false; + myPicturePrefs.force_dimensions = false; + myPicturePrefs.force_height = 0; + myPicturePrefs.force_width = 0; + myPicturePrefs.removeTempPix = true; // we'll just make this the default + + char *unparsed_opts = env_PicOptions; + if (env_PicOptions == NULL) + return myPicturePrefs; + + while (unparsed_opts[0] != 0) { + if (strncmp(unparsed_opts, "MaxDimensions=", 14) == 0) { + unparsed_opts += 14; + myPicturePrefs.max_dimension = (int)strtol(unparsed_opts, NULL, 10); + + } else if (strncmp(unparsed_opts, "DPI=", 4) == 0) { + unparsed_opts += 4; + myPicturePrefs.dpi = (int)strtol(unparsed_opts, NULL, 10); + + } else if (strncmp(unparsed_opts, "MaxKBytes=", 10) == 0) { + unparsed_opts += 10; + myPicturePrefs.max_Kbytes = (int)strtol(unparsed_opts, NULL, 10) * 1024; + + } else if (strncmp(unparsed_opts, "AllPixJPEG=", 11) == 0) { + unparsed_opts += 11; + if (strcmp(unparsed_opts, "true") == 0) { + myPicturePrefs.allJPEG = true; + } + + } else if (strncmp(unparsed_opts, "AllPixPNG=", 10) == 0) { + unparsed_opts += 10; + if (strcmp(unparsed_opts, "true") == 0) { + myPicturePrefs.allPNG = true; + } + + } else if (strncmp(unparsed_opts, "AddBothPix=", 11) == 0) { + unparsed_opts += 11; + if (strcmp(unparsed_opts, "true") == 0) { + myPicturePrefs.addBOTHpix = true; + } + + } else if (strcmp(unparsed_opts, "SquareUp") == 0) { + unparsed_opts += 7; + myPicturePrefs.squareUp = true; + + } else if (strcmp(unparsed_opts, "removeTempPix") == 0) { + unparsed_opts += 13; + myPicturePrefs.removeTempPix = true; + + } else if (strcmp(unparsed_opts, "keepTempPix") == 0) { // NEW + unparsed_opts += 11; + myPicturePrefs.removeTempPix = false; + + } else if (strncmp(unparsed_opts, "ForceHeight=", 12) == 0) { + unparsed_opts += 12; + myPicturePrefs.force_height = strtol(unparsed_opts, NULL, 10); + + } else if (strncmp(unparsed_opts, "ForceWidth=", 11) == 0) { + unparsed_opts += 11; + myPicturePrefs.force_width = strtol(unparsed_opts, NULL, 10); + + } else { + unparsed_opts++; + } + } + } + + if (myPicturePrefs.force_height > 0 && myPicturePrefs.force_width > 0) + myPicturePrefs.force_dimensions = true; + return myPicturePrefs; } #endif /////////////////////////////////////////////////////////////////////////////////////// -// Locating/Finding Atoms // +// Locating/Finding Atoms // /////////////////////////////////////////////////////////////////////////////////////// -AtomicInfo* APar_FindAtomInTrack(uint8_t &total_tracks, uint8_t &track_num, const char* search_atom_str) { - uint8_t track_tally = 0; - short iter = 0; - - while (parsedAtoms[iter].NextAtomNumber != 0) { - if ( memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 && parsedAtoms[iter].AtomicLevel == 2) { - track_tally += 1; - if (track_num == 0) { - total_tracks += 1; - - } else if (track_num == track_tally) { - //drill down into stsd - short next_atom = parsedAtoms[iter].NextAtomNumber; - while (parsedAtoms[next_atom].AtomicLevel > parsedAtoms[iter].AtomicLevel) { - - if (strncmp(parsedAtoms[next_atom].AtomicName, search_atom_str, 4) == 0) { - return &parsedAtoms[next_atom]; - } else { - next_atom = parsedAtoms[next_atom].NextAtomNumber; - } - } - } - } - iter=parsedAtoms[iter].NextAtomNumber; - } - return NULL; +AtomicInfo *APar_FindAtomInTrack(uint8_t &total_tracks, + uint8_t &track_num, + const char *search_atom_str) { + uint8_t track_tally = 0; + short iter = 0; + + while (parsedAtoms[iter].NextAtomNumber != 0) { + if (memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 && + parsedAtoms[iter].AtomicLevel == 2) { + track_tally += 1; + if (track_num == 0) { + total_tracks += 1; + + } else if (track_num == track_tally) { + // drill down into stsd + short next_atom = parsedAtoms[iter].NextAtomNumber; + while (parsedAtoms[next_atom].AtomicLevel > + parsedAtoms[iter].AtomicLevel) { + + if (strncmp(parsedAtoms[next_atom].AtomicName, search_atom_str, 4) == + 0) { + return &parsedAtoms[next_atom]; + } else { + next_atom = parsedAtoms[next_atom].NextAtomNumber; + } + } + } + } + iter = parsedAtoms[iter].NextAtomNumber; + } + return NULL; } short APar_FindPrecedingAtom(short an_atom_num) { - short precedingAtom = 0; - short iter = 0; - while (parsedAtoms[iter].NextAtomNumber != 0) { - if (parsedAtoms[iter].NextAtomNumber == parsedAtoms[an_atom_num].NextAtomNumber) { - break; - } else { - precedingAtom = iter; - iter=parsedAtoms[iter].NextAtomNumber; - } - } - return precedingAtom; + short precedingAtom = 0; + short iter = 0; + while (parsedAtoms[iter].NextAtomNumber != 0) { + if (parsedAtoms[iter].NextAtomNumber == + parsedAtoms[an_atom_num].NextAtomNumber) { + break; + } else { + precedingAtom = iter; + iter = parsedAtoms[iter].NextAtomNumber; + } + } + return precedingAtom; } short APar_FindParentAtom(int order_in_tree, uint8_t this_atom_level) { - short thisAtom = 0; - short iter = order_in_tree; - while (parsedAtoms[iter].AtomicNumber != 0) { - iter = APar_FindPrecedingAtom(iter); - if (parsedAtoms[iter].AtomicLevel == this_atom_level-1) { - thisAtom = iter; - break; - } - } - return thisAtom; + short thisAtom = 0; + short iter = order_in_tree; + while (parsedAtoms[iter].AtomicNumber != 0) { + iter = APar_FindPrecedingAtom(iter); + if (parsedAtoms[iter].AtomicLevel == this_atom_level - 1) { + thisAtom = iter; + break; + } + } + return thisAtom; } /*---------------------- APar_ProvideAtomPath this_atom - index into array of parsedAtoms for the wanted path of an atom atom_path - string into which the path will be placed (working backwards) - fromFile - controls the manner of extracting parents (atom sizes from file, or a simpler atomic level if from memory) + fromFile - controls the manner of extracting parents (atom sizes from file, or +a simpler atomic level if from memory) - First, determine exactly how many atoms will constitute the full path and calculate where into the string to first start placing atom names. Start by - working off the current atom. Using fromFile, either use a more stringent atom start/length from a file, or a more relaxed atom level if from memory. - The array in memory won't have proper atom sizes except for the last child atom typically ('data' will have a proper size, but its parent and all - other parents will not have sizing automatically updated - which happens only at writeout time). -----------------------*/ -void APar_ProvideAtomPath(short this_atom, char* &atom_path, bool fromFile) { - short preceding_atom = this_atom; - uint8_t current_atomic_level = parsedAtoms[this_atom].AtomicLevel; - int str_offset = (parsedAtoms[this_atom].AtomicLevel-1) * 5; //5 = 'atom" + '.' - if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) { - str_offset+=5; //include a "uuid=" string; - } - - memcpy(atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4); - str_offset -=5; - if (parsedAtoms[preceding_atom].AtomicLevel != 1) { - memcpy(atom_path + str_offset +4, ".", 1); - } - if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) { - memcpy(atom_path + str_offset, "uuid=", 5); - str_offset-=5; - } - - while (parsedAtoms[preceding_atom].AtomicNumber != 0) { - - if (fromFile) { - if (parsedAtoms[preceding_atom].AtomicStart < parsedAtoms[this_atom].AtomicStart && - parsedAtoms[preceding_atom].AtomicLength > parsedAtoms[this_atom].AtomicLength && - parsedAtoms[preceding_atom].AtomicStart + parsedAtoms[preceding_atom].AtomicLength >= parsedAtoms[this_atom].AtomicStart + parsedAtoms[this_atom].AtomicLength && - parsedAtoms[preceding_atom].AtomicContainerState <= DUAL_STATE_ATOM ) { - memcpy(atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4); - str_offset -=5; - if (str_offset >= 0) { - memcpy(atom_path + str_offset +4, ".", 1); - } - - preceding_atom = APar_FindPrecedingAtom(preceding_atom); - - } else { - preceding_atom = APar_FindPrecedingAtom(preceding_atom); - } - } else { - if (parsedAtoms[preceding_atom].AtomicLevel < current_atomic_level) { - memcpy(atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4); - str_offset -=5; - if (str_offset >= 0) { - memcpy(atom_path + str_offset +4, ".", 1); - } - - current_atomic_level = parsedAtoms[preceding_atom].AtomicLevel; - preceding_atom = APar_FindPrecedingAtom(preceding_atom); - } else { - preceding_atom = APar_FindPrecedingAtom(preceding_atom); - } - } - if (preceding_atom == 0 || str_offset < 0) { - break; - } - } + First, determine exactly how many atoms will constitute the full path and +calculate where into the string to first start placing atom names. Start by + working off the current atom. Using fromFile, either use a more +stringent atom start/length from a file, or a more relaxed atom level if from +memory. The array in memory won't have proper atom sizes except for the last +child atom typically ('data' will have a proper size, but its parent and all + other parents will not have sizing automatically updated - which +happens only at writeout time). +----------------------*/ +void APar_ProvideAtomPath(short this_atom, char *&atom_path, bool fromFile) { + short preceding_atom = this_atom; + uint8_t current_atomic_level = parsedAtoms[this_atom].AtomicLevel; + int str_offset = + (parsedAtoms[this_atom].AtomicLevel - 1) * 5; // 5 = 'atom" + '.' + if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) { + str_offset += 5; // include a "uuid=" string; + } + + memcpy(atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4); + str_offset -= 5; + if (parsedAtoms[preceding_atom].AtomicLevel != 1) { + memcpy(atom_path + str_offset + 4, ".", 1); + } + if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) { + memcpy(atom_path + str_offset, "uuid=", 5); + str_offset -= 5; + } + + while (parsedAtoms[preceding_atom].AtomicNumber != 0) { + + if (fromFile) { + if (parsedAtoms[preceding_atom].AtomicStart < + parsedAtoms[this_atom].AtomicStart && + parsedAtoms[preceding_atom].AtomicLength > + parsedAtoms[this_atom].AtomicLength && + parsedAtoms[preceding_atom].AtomicStart + + parsedAtoms[preceding_atom].AtomicLength >= + parsedAtoms[this_atom].AtomicStart + + parsedAtoms[this_atom].AtomicLength && + parsedAtoms[preceding_atom].AtomicContainerState <= DUAL_STATE_ATOM) { + memcpy( + atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4); + str_offset -= 5; + if (str_offset >= 0) { + memcpy(atom_path + str_offset + 4, ".", 1); + } + + preceding_atom = APar_FindPrecedingAtom(preceding_atom); + + } else { + preceding_atom = APar_FindPrecedingAtom(preceding_atom); + } + } else { + if (parsedAtoms[preceding_atom].AtomicLevel < current_atomic_level) { + memcpy( + atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4); + str_offset -= 5; + if (str_offset >= 0) { + memcpy(atom_path + str_offset + 4, ".", 1); + } + + current_atomic_level = parsedAtoms[preceding_atom].AtomicLevel; + preceding_atom = APar_FindPrecedingAtom(preceding_atom); + } else { + preceding_atom = APar_FindPrecedingAtom(preceding_atom); + } + } + if (preceding_atom == 0 || str_offset < 0) { + break; + } + } - return; + return; } bool APar_Eval_ChunkOffsetImpact(short an_atom_num) { - bool impact_calculations_directly = false; - short iter = 0; - uint8_t found_desired_atom = 0; - - while (true) { - if ( strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0) { - if (found_desired_atom) { - impact_calculations_directly = true; - } - break; - } else { - iter=parsedAtoms[iter].NextAtomNumber; - } - if (iter == 0) { - break; - } - if (iter == an_atom_num) { - found_desired_atom = 1; - } - } - return impact_calculations_directly; + bool impact_calculations_directly = false; + short iter = 0; + uint8_t found_desired_atom = 0; + + while (true) { + if (strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0) { + if (found_desired_atom) { + impact_calculations_directly = true; + } + break; + } else { + iter = parsedAtoms[iter].NextAtomNumber; + } + if (iter == 0) { + break; + } + if (iter == an_atom_num) { + found_desired_atom = 1; + } + } + return impact_calculations_directly; } short APar_FindLastAtom() { - short this_atom_num = 0; //start our search with the first atom - while (parsedAtoms[this_atom_num].NextAtomNumber != 0) { - this_atom_num = parsedAtoms[this_atom_num].NextAtomNumber; - } - return this_atom_num; + short this_atom_num = 0; // start our search with the first atom + while (parsedAtoms[this_atom_num].NextAtomNumber != 0) { + this_atom_num = parsedAtoms[this_atom_num].NextAtomNumber; + } + return this_atom_num; } short APar_FindLastChild_of_ParentAtom(short thisAtom) { - short child_atom = parsedAtoms[thisAtom].NextAtomNumber; - short last_atom = thisAtom; //if there are no children, this will be the first and last atom in the hiearchy - while (true) { - if (parsedAtoms[ child_atom ].AtomicLevel > parsedAtoms[thisAtom].AtomicLevel) { - last_atom = child_atom; - } - child_atom = parsedAtoms[child_atom].NextAtomNumber; - if (child_atom == 0 || parsedAtoms[ child_atom ].AtomicLevel <= parsedAtoms[thisAtom].AtomicLevel) { - break; - } - } - return last_atom; + short child_atom = parsedAtoms[thisAtom].NextAtomNumber; + short last_atom = thisAtom; // if there are no children, this will be the + // first and last atom in the hiearchy + while (true) { + if (parsedAtoms[child_atom].AtomicLevel > + parsedAtoms[thisAtom].AtomicLevel) { + last_atom = child_atom; + } + child_atom = parsedAtoms[child_atom].NextAtomNumber; + if (child_atom == 0 || parsedAtoms[child_atom].AtomicLevel <= + parsedAtoms[thisAtom].AtomicLevel) { + break; + } + } + return last_atom; } /*---------------------- APar_ReturnChildrenAtoms - this_atom - the parent atom that contains any number of children atoms (that are currenly unknown) - atom_index - the index of the desired child. Passing 0 will return a count of the *total* number of children atoms under this_atom - - Working off of AtomicLevel, test the atoms that follow this_atom to see if they are immediately below this_atom. Increment total_children if is - if - total_children should match our index, return that desired child at index atom. + this_atom - the parent atom that contains any number of children atoms (that +are currenly unknown) atom_index - the index of the desired child. Passing 0 +will return a count of the *total* number of children atoms under this_atom + + Working off of AtomicLevel, test the atoms that follow this_atom to see if +they are immediately below this_atom. Increment total_children if is - if + total_children should match our index, return that desired child +at index atom. ----------------------*/ short APar_ReturnChildrenAtoms(short this_atom, uint8_t atom_index) { - short child_atom = 0; - uint8_t total_children = 0; - short iter = parsedAtoms[this_atom].NextAtomNumber; - - while (true) { - if ( (parsedAtoms[iter].AtomicLevel == parsedAtoms[this_atom].AtomicLevel + 1 && this_atom > 0) || - (this_atom == 0 && parsedAtoms[iter].AtomicLevel == 1) ) { - total_children++; - - if (atom_index == total_children) { - child_atom = iter; - break; - } - } - if (parsedAtoms[iter].AtomicLevel <= parsedAtoms[this_atom].AtomicLevel && this_atom != 0) { - break; - } else { - iter = parsedAtoms[iter].NextAtomNumber; - } - if (iter == 0) { - break; - } - } - if (atom_index == 0) { - child_atom = (short)total_children; - } - return child_atom; + short child_atom = 0; + uint8_t total_children = 0; + short iter = parsedAtoms[this_atom].NextAtomNumber; + + while (true) { + if ((parsedAtoms[iter].AtomicLevel == + parsedAtoms[this_atom].AtomicLevel + 1 && + this_atom > 0) || + (this_atom == 0 && parsedAtoms[iter].AtomicLevel == 1)) { + total_children++; + + if (atom_index == total_children) { + child_atom = iter; + break; + } + } + if (parsedAtoms[iter].AtomicLevel <= parsedAtoms[this_atom].AtomicLevel && + this_atom != 0) { + break; + } else { + iter = parsedAtoms[iter].NextAtomNumber; + } + if (iter == 0) { + break; + } + } + if (atom_index == 0) { + child_atom = (short)total_children; + } + return child_atom; } /*---------------------- APar_FindChildAtom - parent_atom - the parent atom that contains any number of children atoms (that are currenly unknown) - child_name - the name of the atom to search for under the parent_atom + parent_atom - the parent atom that contains any number of children atoms (that +are currenly unknown) child_name - the name of the atom to search for under the +parent_atom Given an atom, search its children for child_name ----------------------*/ -AtomicInfo* APar_FindChildAtom(short parent_atom, const char* child_name, uint8_t child_name_len = 4, uint16_t desired_index = 1) { - AtomicInfo* found_child = NULL; - short test_child_idx = parsedAtoms[parent_atom].NextAtomNumber; - uint16_t current_count = 0; - - while (parsedAtoms[test_child_idx].AtomicLevel > parsedAtoms[parent_atom].AtomicLevel) { - if ( (memcmp(parsedAtoms[test_child_idx].AtomicName, child_name, child_name_len) == 0 || memcmp(child_name, "any", 4) == 0) && - parsedAtoms[test_child_idx].AtomicLevel == parsedAtoms[parent_atom].AtomicLevel +1) { - current_count++; - if (desired_index == current_count) { - found_child = &parsedAtoms[test_child_idx]; - break; - } - } - test_child_idx = parsedAtoms[test_child_idx].NextAtomNumber; - } +AtomicInfo *APar_FindChildAtom(short parent_atom, + const char *child_name, + uint8_t child_name_len = 4, + uint16_t desired_index = 1) { + AtomicInfo *found_child = NULL; + short test_child_idx = parsedAtoms[parent_atom].NextAtomNumber; + uint16_t current_count = 0; + + while (parsedAtoms[test_child_idx].AtomicLevel > + parsedAtoms[parent_atom].AtomicLevel) { + if ((memcmp(parsedAtoms[test_child_idx].AtomicName, + child_name, + child_name_len) == 0 || + memcmp(child_name, "any", 4) == 0) && + parsedAtoms[test_child_idx].AtomicLevel == + parsedAtoms[parent_atom].AtomicLevel + 1) { + current_count++; + if (desired_index == current_count) { + found_child = &parsedAtoms[test_child_idx]; + break; + } + } + test_child_idx = parsedAtoms[test_child_idx].NextAtomNumber; + } - return found_child; + return found_child; } /*---------------------- APar_AtomicComparison - proto_atom - the temporary atom structure to run the tests on - test_atom - the exising atom to compare the proto_atom against - match_full_uuids - selects whether to match by atom names (4 bytes) or uuids(16 bytes) which are stored on AtomicName - reverseDNSdomain - the reverse DNS like com.foo.thing (only used with reverseDNS atoms: ----, mean, name) - - Test if proto_atom matches a single atom (test_atom) by name, level & classification (packed_lang_atom, extended atom...); for certain types of data - (like packed_lang & reverseDNS 'moov.udta.meta.ilst.----.name:[iTunNORM] atoms currently) add finer grained tests. The return result will be NULL - if not matched, or returns the atom it matches. -----------------------*/ -AtomicInfo* APar_AtomicComparison(AtomicInfo* proto_atom, short test_atom, bool match_full_uuids, const char* reverseDNSdomain) { - AtomicInfo* return_atom = NULL; - size_t ATOM_TEST_LEN = (match_full_uuids ? 16 : 4); - - if (parsedAtoms[test_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[test_atom].uuid_style == UUID_DEPRECATED_FORM) { //accommodate deprecated form - if (memcmp(parsedAtoms[test_atom].uuid_ap_atomname, proto_atom->AtomicName, 4) == 0) { - return &parsedAtoms[test_atom]; - } - } - - //can't do AtomicVerFlags because lots of utilities don't write the proper iTunes flags for iTunes metadata - if ( memcmp(proto_atom->AtomicName, parsedAtoms[test_atom].AtomicName, ATOM_TEST_LEN) == 0 && - proto_atom->AtomicLevel == parsedAtoms[test_atom].AtomicLevel && - (proto_atom->AtomicClassification == parsedAtoms[test_atom].AtomicClassification || proto_atom->AtomicClassification == UNKNOWN_ATOM) ) { - - if (proto_atom->AtomicClassification == PACKED_LANG_ATOM) { - //0x05D9 = 'any' and will be used (internally) to match on name,class,container state alone, disregarding AtomicLanguage - if (proto_atom->AtomicLanguage == parsedAtoms[test_atom].AtomicLanguage || proto_atom->AtomicLanguage == 0x05D9) { - return_atom = &parsedAtoms[test_atom]; - } - - } else if (proto_atom->ReverseDNSname != NULL && parsedAtoms[test_atom].ReverseDNSname != NULL) { - //match on moov.udta.meta.ilst.----.name:[something] (reverse DNS atom) - if (strcmp(proto_atom->ReverseDNSname, parsedAtoms[test_atom].ReverseDNSname) == 0) { - if (reverseDNSdomain == NULL) { //lock onto the first reverseDNS form irrespective of domain (TODO: manualAtomRemove will cause this to be NULL) - return_atom = &parsedAtoms[test_atom]; - } else { + proto_atom - the temporary atom structure to run the tests on + test_atom - the exising atom to compare the proto_atom against + match_full_uuids - selects whether to match by atom names (4 bytes) or +uuids(16 bytes) which are stored on AtomicName reverseDNSdomain - the reverse +DNS like com.foo.thing (only used with reverseDNS atoms: ----, mean, name) + + Test if proto_atom matches a single atom (test_atom) by name, level & +classification (packed_lang_atom, extended atom...); for certain types of data + (like packed_lang & reverseDNS +'moov.udta.meta.ilst.----.name:[iTunNORM] atoms currently) add finer grained +tests. The return result will be NULL if not matched, or returns the atom it +matches. +----------------------*/ +AtomicInfo *APar_AtomicComparison(AtomicInfo *proto_atom, + short test_atom, + bool match_full_uuids, + const char *reverseDNSdomain) { + AtomicInfo *return_atom = NULL; + size_t ATOM_TEST_LEN = (match_full_uuids ? 16 : 4); + + if (parsedAtoms[test_atom].AtomicClassification == EXTENDED_ATOM && + parsedAtoms[test_atom].uuid_style == + UUID_DEPRECATED_FORM) { // accommodate deprecated form + if (memcmp(parsedAtoms[test_atom].uuid_ap_atomname, + proto_atom->AtomicName, + 4) == 0) { + return &parsedAtoms[test_atom]; + } + } + + // can't do AtomicVerFlags because lots of utilities don't write the proper + // iTunes flags for iTunes metadata + if (memcmp(proto_atom->AtomicName, + parsedAtoms[test_atom].AtomicName, + ATOM_TEST_LEN) == 0 && + proto_atom->AtomicLevel == parsedAtoms[test_atom].AtomicLevel && + (proto_atom->AtomicClassification == + parsedAtoms[test_atom].AtomicClassification || + proto_atom->AtomicClassification == UNKNOWN_ATOM)) { + + if (proto_atom->AtomicClassification == PACKED_LANG_ATOM) { + // 0x05D9 = 'any' and will be used (internally) to match on + // name,class,container state alone, disregarding AtomicLanguage + if (proto_atom->AtomicLanguage == parsedAtoms[test_atom].AtomicLanguage || + proto_atom->AtomicLanguage == 0x05D9) { + return_atom = &parsedAtoms[test_atom]; + } + + } else if (proto_atom->ReverseDNSname != NULL && + parsedAtoms[test_atom].ReverseDNSname != NULL) { + // match on moov.udta.meta.ilst.----.name:[something] (reverse DNS atom) + if (strcmp(proto_atom->ReverseDNSname, + parsedAtoms[test_atom].ReverseDNSname) == 0) { + if (reverseDNSdomain == + NULL) { // lock onto the first reverseDNS form irrespective of + // domain (TODO: manualAtomRemove will cause this to be + // NULL) + return_atom = &parsedAtoms[test_atom]; + } else { #if defined(DEBUG_V) - fprintf(stdout, "AP_AtomicComparison testing wanted rDNS %s domain against atom '%s' %s rDNS domain\n", reverseDNSdomain, parsedAtoms[test_atom].AtomicName, - parsedAtoms[APar_FindPrecedingAtom(test_atom)].ReverseDNSdomain); + fprintf( + stdout, + "AP_AtomicComparison testing wanted rDNS %s domain against " + "atom '%s' %s rDNS domain\n", + reverseDNSdomain, + parsedAtoms[test_atom].AtomicName, + parsedAtoms[APar_FindPrecedingAtom(test_atom)].ReverseDNSdomain); #endif - if ( strcmp(reverseDNSdomain, parsedAtoms[APar_FindPrecedingAtom(test_atom)].ReverseDNSdomain) == 0 ) { - return_atom = &parsedAtoms[test_atom]; - } - } - } - } else { - return_atom = &parsedAtoms[test_atom]; - } - } - return return_atom; + if (strcmp(reverseDNSdomain, + parsedAtoms[APar_FindPrecedingAtom(test_atom)] + .ReverseDNSdomain) == 0) { + return_atom = &parsedAtoms[test_atom]; + } + } + } + } else { + return_atom = &parsedAtoms[test_atom]; + } + } + return return_atom; } /*---------------------- APar_FindLastLikeNamedAtom - atom_name - the name of the atom to search for; the string itself may have more than 4 bytes - containing_hierarchy - the parent hierarchy that is expected to carry multiply named atoms differing (in language for example) - - Follow through the atom tree; if a test atom is matched by name, and is a child to the container atom, remember that atom. If nothing matches, the index - of the container atom is returned; otherwise the last like named atom is returned. -----------------------*/ -short APar_FindLastLikeNamedAtom(char* atom_name, short containing_hierarchy) { - short last_identically_named_atom = APar_FindLastChild_of_ParentAtom(containing_hierarchy); //default returns the last atom in the parent, not the parent - short eval_atom = parsedAtoms[containing_hierarchy].NextAtomNumber; - - while (true) { - if (parsedAtoms[eval_atom].AtomicLevel < parsedAtoms[containing_hierarchy].AtomicLevel + 1 || eval_atom == 0) { - break; - } else { - if (memcmp(parsedAtoms[eval_atom].AtomicName, atom_name, 4) == 0 && - parsedAtoms[eval_atom].AtomicLevel == parsedAtoms[containing_hierarchy].AtomicLevel + 1) { - last_identically_named_atom = eval_atom; - } - eval_atom = parsedAtoms[eval_atom].NextAtomNumber; - } - } - return last_identically_named_atom; -} - -void APar_FreeSurrogateAtom(AtomicInfo* surrogate_atom) { - if (surrogate_atom->ReverseDNSname != NULL) { - free(surrogate_atom->ReverseDNSname); - surrogate_atom->ReverseDNSname = NULL; - } - return; + atom_name - the name of the atom to search for; the string itself may +have more than 4 bytes containing_hierarchy - the parent hierarchy that is +expected to carry multiply named atoms differing (in language for example) + + Follow through the atom tree; if a test atom is matched by name, and is a +child to the container atom, remember that atom. If nothing matches, the index + of the container atom is returned; otherwise the last like named +atom is returned. +----------------------*/ +short APar_FindLastLikeNamedAtom(char *atom_name, short containing_hierarchy) { + short last_identically_named_atom = APar_FindLastChild_of_ParentAtom( + containing_hierarchy); // default returns the last atom in the parent, not + // the parent + short eval_atom = parsedAtoms[containing_hierarchy].NextAtomNumber; + + while (true) { + if (parsedAtoms[eval_atom].AtomicLevel < + parsedAtoms[containing_hierarchy].AtomicLevel + 1 || + eval_atom == 0) { + break; + } else { + if (memcmp(parsedAtoms[eval_atom].AtomicName, atom_name, 4) == 0 && + parsedAtoms[eval_atom].AtomicLevel == + parsedAtoms[containing_hierarchy].AtomicLevel + 1) { + last_identically_named_atom = eval_atom; + } + eval_atom = parsedAtoms[eval_atom].NextAtomNumber; + } + } + return last_identically_named_atom; +} + +void APar_FreeSurrogateAtom(AtomicInfo *surrogate_atom) { + if (surrogate_atom->ReverseDNSname != NULL) { + free(surrogate_atom->ReverseDNSname); + surrogate_atom->ReverseDNSname = NULL; + } + return; } /*---------------------- APar_CreateSurrogateAtom - Make a temporary AtomicInfo structure to run comparisons against; currently comparisons are done on name, level, classification (versioned...), langauge - (3gp assets), and iTunes-style reverse dns 'name' carrying a string describing the purpose of the data (iTunNORM). This atom exists outside of a file's - atom hieararchy that resides in the parsedAtoms[] array. -----------------------*/ -void APar_CreateSurrogateAtom(AtomicInfo* surrogate_atom, const char* atom_name, uint8_t atom_level, uint8_t atom_class, uint16_t atom_lang, - char* revdns_name, uint8_t revdns_name_len) { - surrogate_atom->AtomicName = (char*)atom_name; - surrogate_atom->AtomicLevel = atom_level; - - if (revdns_name != NULL && revdns_name_len) { - surrogate_atom->ReverseDNSname = (char *)malloc(sizeof(char)*revdns_name_len > 8 ? revdns_name_len+1 : 9); - memset(surrogate_atom->ReverseDNSname, 0, sizeof(char)*revdns_name_len > 8 ? revdns_name_len+1 : 9); - memcpy(surrogate_atom->ReverseDNSname, revdns_name, revdns_name_len); - - } else { - APar_FreeSurrogateAtom(surrogate_atom); - } - surrogate_atom->AtomicClassification = atom_class; - surrogate_atom->AtomicLanguage = atom_lang; - return; + Make a temporary AtomicInfo structure to run comparisons against; currently +comparisons are done on name, level, classification (versioned...), langauge + (3gp assets), and iTunes-style reverse dns 'name' carrying a +string describing the purpose of the data (iTunNORM). This atom exists outside +of a file's atom hieararchy that resides in the parsedAtoms[] array. +----------------------*/ +void APar_CreateSurrogateAtom(AtomicInfo *surrogate_atom, + const char *atom_name, + uint8_t atom_level, + uint8_t atom_class, + uint16_t atom_lang, + char *revdns_name, + uint8_t revdns_name_len) { + surrogate_atom->AtomicName = (char *)atom_name; + surrogate_atom->AtomicLevel = atom_level; + + if (revdns_name != NULL && revdns_name_len) { + surrogate_atom->ReverseDNSname = (char *)malloc( + sizeof(char) * revdns_name_len > 8 ? revdns_name_len + 1 : 9); + memset(surrogate_atom->ReverseDNSname, + 0, + sizeof(char) * revdns_name_len > 8 ? revdns_name_len + 1 : 9); + memcpy(surrogate_atom->ReverseDNSname, revdns_name, revdns_name_len); + + } else { + APar_FreeSurrogateAtom(surrogate_atom); + } + surrogate_atom->AtomicClassification = atom_class; + surrogate_atom->AtomicLanguage = atom_lang; + return; } /*---------------------- APar_FindAtom - atom_name - the full path describing the hiearchy the desired atom can be found in - createMissing - either create the missing interim atoms as required, or return a NULL if not found - atom_type - the classification of the last atom (packed language, uuid extended atom...) - atom_lang - the language of the 3gp asset used when atom_type is packed language type - match_full_uuids - match 16byte full uuids (typically removing ( possibly non-AP) uuids via --manualAtomRemoval; AP uuids (the new ones) still work on 4bytes** - reverseDNSdomain - the reverse DNS like com.foo.thing (only used with reverseDNS atoms: ----, mean, name) - - Follow through the atom tree starting with the atom following 'ftyp'. Testing occurs on an atom level basis; a stand-in temporary skeletal atom - is created to evaluate. If they atoms are deemed matching, atom_name is advanced forward (it still contains the full path, but only 4bytes are - typically used at a time) and testing occurs until either the desired atom is found, or the last containing hiearchy with an exising atom is - exhausted without making new atoms. - - NOTE: atom_name can come in these forms: - classic/vanilla/ordinary atoms: moov.udta.meta.ilst.cprt.data - iTunes reverseDNS atoms: moov.udta.meta.ilst.----.name:[iTunNORM] - uuid user-extension atoms: moov.udta.meta.uuid=tdtg (the deprecated form) - uuid user-extension atoms: moov.udta.meta.uuid=ba45fcaa-7ef5-5201-8a63-78886495ab1f - index-based atoms: moov.trak[2].mdia.minf - - NOTE: On my computer it takes about .04 second to scan the file, .1 second to add about 2 dozen tags, and 1.0 second to copy a file. Updating a file - from start to finish takes 0.21 seconds. As many loops as this new APar_FindAtom eliminates, it is only marginally faster than the old code. - - ** the reason why the old deprecated uuid form & the new uuid full 16byte form work off of a 4byte value (the atom name) is that because we are using a version 5 - sha1 hashed uuid of a name in a given namespace, the identical name in the identical namespace will yield identical an identical uuid (if corrected for endianness). - This means that that matching by 4 bytes of atom name is the functional equvalent of matching by 16byte uuids. -----------------------*/ -AtomicInfo* APar_FindAtom(const char* atom_name, bool createMissing, uint8_t atom_type, uint16_t atom_lang, bool match_full_uuids, const char* reverseDNSdomain) { - AtomicInfo* thisAtom = NULL; - char* search_atom_name = (char*)atom_name; - char* reverse_dns_name = NULL; - uint8_t revdns_name_len = 0; - uint8_t atom_index = 0; // if there are atoms mutliple identically named at the same level, this is where to store the count as it occurs - uint8_t desired_index = 1; - uint8_t search_atom_type = UNKNOWN_ATOM; - int known_atom = -1; - short search_atom_start_num = parsedAtoms[0].NextAtomNumber; //don't test 'ftyp'; its atom_number[0] & will be used to know when we have hit the end of the tree; can't hardcode it to '1' because ftyp's following atom can change; only ftyp as parsedAtoms[0] is guaranteed. - uint8_t present_atomic_level = 1; - AtomicInfo* last_known_present_parent = NULL; - AtomicInfo atom_surrogate = { 0 }; + atom_name - the full path describing the hiearchy the desired atom can +be found in createMissing - either create the missing interim atoms as required, +or return a NULL if not found atom_type - the classification of the last atom +(packed language, uuid extended atom...) atom_lang - the language of the 3gp +asset used when atom_type is packed language type match_full_uuids - match +16byte full uuids (typically removing ( possibly non-AP) uuids via +--manualAtomRemoval; AP uuids (the new ones) still work on 4bytes** + reverseDNSdomain - the reverse DNS like com.foo.thing (only used with +reverseDNS atoms: ----, mean, name) + + Follow through the atom tree starting with the atom following 'ftyp'. +Testing occurs on an atom level basis; a stand-in temporary skeletal atom is +created to evaluate. If they atoms are deemed matching, atom_name is advanced +forward (it still contains the full path, but only 4bytes are typically used at +a time) and testing occurs until either the desired atom is found, or the last +containing hiearchy with an exising atom is exhausted without making new atoms. + + NOTE: atom_name can come in these forms: + classic/vanilla/ordinary atoms: +moov.udta.meta.ilst.cprt.data iTunes reverseDNS atoms: +moov.udta.meta.ilst.----.name:[iTunNORM] uuid user-extension atoms: +moov.udta.meta.uuid=tdtg (the deprecated form) uuid user-extension atoms: +moov.udta.meta.uuid=ba45fcaa-7ef5-5201-8a63-78886495ab1f index-based atoms: +moov.trak[2].mdia.minf + + NOTE: On my computer it takes about .04 second to scan the file, +.1 second to add about 2 dozen tags, and 1.0 second to copy a file. Updating a +file from start to finish takes 0.21 seconds. As many loops as this new +APar_FindAtom eliminates, it is only marginally faster than the old code. + + ** the reason why the old deprecated uuid form & the new uuid +full 16byte form work off of a 4byte value (the atom name) is that because we +are using a version 5 sha1 hashed uuid of a name in a given namespace, the +identical name in the identical namespace will yield identical an identical uuid +(if corrected for endianness). This means that that matching by 4 bytes of atom +name is the functional equvalent of matching by 16byte uuids. +----------------------*/ +AtomicInfo *APar_FindAtom(const char *atom_name, + bool createMissing, + uint8_t atom_type, + uint16_t atom_lang, + bool match_full_uuids, + const char *reverseDNSdomain) { + AtomicInfo *thisAtom = NULL; + char *search_atom_name = (char *)atom_name; + char *reverse_dns_name = NULL; + uint8_t revdns_name_len = 0; + uint8_t atom_index = + 0; // if there are atoms mutliple identically named at the same level, + // this is where to store the count as it occurs + uint8_t desired_index = 1; + uint8_t search_atom_type = UNKNOWN_ATOM; + int known_atom = -1; + short search_atom_start_num = + parsedAtoms[0] + .NextAtomNumber; // don't test 'ftyp'; its atom_number[0] & will be + // used to know when we have hit the end of the tree; + // can't hardcode it to '1' because ftyp's following + // atom can change; only ftyp as parsedAtoms[0] is + // guaranteed. + uint8_t present_atomic_level = 1; + AtomicInfo *last_known_present_parent = NULL; + AtomicInfo atom_surrogate = {0}; #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_FindAtom entry trying to find '%s'; create missing: %u\n", atom_name, createMissing); + fprintf(stdout, + "debug: AP_FindAtom entry trying to find '%s'; create missing: %u\n", + atom_name, + createMissing); #endif - while (search_atom_name != NULL) { - desired_index = 1; //reset the index + while (search_atom_name != NULL) { + desired_index = 1; // reset the index - if (atom_type == EXTENDED_ATOM && strncmp(search_atom_name, "uuid=", 5) == 0 ) { - search_atom_name+=5; - search_atom_type = atom_type; - } + if (atom_type == EXTENDED_ATOM && + strncmp(search_atom_name, "uuid=", 5) == 0) { + search_atom_name += 5; + search_atom_type = atom_type; + } #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_FindAtom loop evaluate test %s (index=%u)\n", search_atom_name, atom_index); + fprintf(stdout, + "debug: AP_FindAtom loop evaluate test %s (index=%u)\n", + search_atom_name, + atom_index); #endif - size_t portion_len = strlen(search_atom_name); - if (strncmp(search_atom_name+4, ":[", 2) == 0 && search_atom_name[portion_len-1] == ']') { - reverse_dns_name = search_atom_name + 4+2; //4bytes atom name 2bytes ":[" - revdns_name_len = portion_len-7; //4bytes atom name, 2 bytes ":[", 1 byte "]" - search_atom_type = atom_type; - } else if (search_atom_name[4] == '[') { - desired_index = strtoul(search_atom_name+5, NULL, 10); + size_t portion_len = strlen(search_atom_name); + if (strncmp(search_atom_name + 4, ":[", 2) == 0 && + search_atom_name[portion_len - 1] == ']') { + reverse_dns_name = + search_atom_name + 4 + 2; // 4bytes atom name 2bytes ":[" + revdns_name_len = + portion_len - 7; // 4bytes atom name, 2 bytes ":[", 1 byte "]" + search_atom_type = atom_type; + } else if (search_atom_name[4] == '[') { + desired_index = strtoul(search_atom_name + 5, NULL, 10); #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_FindAtom >##< '%s' at index=%u\n", search_atom_name, desired_index); + fprintf(stdout, + "debug: AP_FindAtom >##< '%s' at index=%u\n", + search_atom_name, + desired_index); #endif - } + } - if (strlen(search_atom_name) == 4) { - if (atom_type == UNKNOWN_ATOM) { - known_atom = APar_MatchToKnownAtom(search_atom_name, last_known_present_parent->AtomicName, false, atom_name); - search_atom_type = KnownAtoms[known_atom].box_type; - } else { - search_atom_type = atom_type; - } - } - - APar_CreateSurrogateAtom(&atom_surrogate, search_atom_name, present_atomic_level, search_atom_type, atom_lang, reverse_dns_name, revdns_name_len); - atom_index = 0; - - short iter = search_atom_start_num; - while (true) { - AtomicInfo* result = NULL; - - //if iter == 0, that means test against 'ftyp' - and since its always 0, don't test it; its to know that the end of the tree is reached - if (iter != 0 && (parsedAtoms[iter].AtomicLevel == present_atomic_level || reverse_dns_name != NULL) ) { - result = APar_AtomicComparison(&atom_surrogate, iter, (search_atom_type == EXTENDED_ATOM ? match_full_uuids : false), reverseDNSdomain ); + if (strlen(search_atom_name) == 4) { + if (atom_type == UNKNOWN_ATOM) { + known_atom = + APar_MatchToKnownAtom(search_atom_name, + last_known_present_parent->AtomicName, + false, + atom_name); + search_atom_type = KnownAtoms[known_atom].box_type; + } else { + search_atom_type = atom_type; + } + } + + APar_CreateSurrogateAtom(&atom_surrogate, + search_atom_name, + present_atomic_level, + search_atom_type, + atom_lang, + reverse_dns_name, + revdns_name_len); + atom_index = 0; + + short iter = search_atom_start_num; + while (true) { + AtomicInfo *result = NULL; + + // if iter == 0, that means test against 'ftyp' - and since its always 0, + // don't test it; its to know that the end of the tree is reached + if (iter != 0 && (parsedAtoms[iter].AtomicLevel == present_atomic_level || + reverse_dns_name != NULL)) { + result = APar_AtomicComparison( + &atom_surrogate, + iter, + (search_atom_type == EXTENDED_ATOM ? match_full_uuids : false), + reverseDNSdomain); #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_FindAtom compare %s(%u) against %s (wanted index=%u)\n", search_atom_name, atom_index, parsedAtoms[iter].AtomicName, desired_index); - } else { - fprintf(stdout, "debug: AP_FindAtom %s rejected against %s\n", search_atom_name, parsedAtoms[iter].AtomicName); + fprintf(stdout, + "debug: AP_FindAtom compare %s(%u) against %s (wanted " + "index=%u)\n", + search_atom_name, + atom_index, + parsedAtoms[iter].AtomicName, + desired_index); + } else { + fprintf(stdout, + "debug: AP_FindAtom %s rejected against %s\n", + search_atom_name, + parsedAtoms[iter].AtomicName); #endif - } - if (result != NULL) { //something matched - atom_index++; + } + if (result != NULL) { // something matched + atom_index++; #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_FindAtom ***matched*** current index=%u (want %u)\n", atom_index, desired_index); + fprintf(stdout, + "debug: AP_FindAtom ***matched*** current index=%u (want " + "%u)\n", + atom_index, + desired_index); #endif - if (search_atom_type != UNKNOWN_ATOM || (search_atom_type == UNKNOWN_ATOM && known_atom != -1) ) { - thisAtom = result; + if (search_atom_type != UNKNOWN_ATOM || + (search_atom_type == UNKNOWN_ATOM && known_atom != -1)) { + thisAtom = result; #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_FindAtom perfect match: %s(%u) == existing %s(%u)\n", search_atom_name, desired_index, parsedAtoms[iter].AtomicName, atom_index); + fprintf(stdout, + "debug: AP_FindAtom perfect match: %s(%u) == " + "existing %s(%u)\n", + search_atom_name, + desired_index, + parsedAtoms[iter].AtomicName, + atom_index); #endif - } else { - last_known_present_parent = result; //if not, then it isn't the last atom, and must be some form of parent - } - if (desired_index == atom_index) { - search_atom_start_num = parsedAtoms[iter].NextAtomNumber; - break; - } - } - - if (parsedAtoms[iter].AtomicLevel < present_atomic_level && reverse_dns_name == NULL) { - iter = 0; //force the ending determination of whether to make new atoms or not; - } - - if (iter == 0 && createMissing) { - //create that atom - if (last_known_present_parent != NULL) { - short last_hierarchical_atom = 0; + } else { + last_known_present_parent = + result; // if not, then it isn't the last atom, and must be some + // form of parent + } + if (desired_index == atom_index) { + search_atom_start_num = parsedAtoms[iter].NextAtomNumber; + break; + } + } + + if (parsedAtoms[iter].AtomicLevel < present_atomic_level && + reverse_dns_name == NULL) { + iter = 0; // force the ending determination of whether to make new atoms + // or not; + } + + if (iter == 0 && createMissing) { + // create that atom + if (last_known_present_parent != NULL) { + short last_hierarchical_atom = 0; #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_FindAtom-------missing atom, need to create '%s'\n", search_atom_name); + fprintf( + stdout, + "debug: AP_FindAtom-------missing atom, need to create '%s'\n", + search_atom_name); #endif - if (search_atom_type == PACKED_LANG_ATOM) { - last_hierarchical_atom = APar_FindLastLikeNamedAtom(atom_surrogate.AtomicName, last_known_present_parent->AtomicNumber); - } else { - last_hierarchical_atom = APar_FindLastChild_of_ParentAtom(last_known_present_parent->AtomicNumber); - } - thisAtom = APar_CreateSparseAtom(&atom_surrogate, last_known_present_parent, last_hierarchical_atom); - search_atom_start_num = thisAtom->AtomicNumber; - if (strlen(search_atom_name) >= 4) { - last_known_present_parent = thisAtom; - } - } else { - //its a file-level atom that needs to be created, so it won't have a last_known_present_parent - if (strlen(atom_name) == 4) { - short total_root_level_atoms = APar_ReturnChildrenAtoms (0, 0); - short test_root_atom = 0; - - //scan through all top level atoms - for(uint8_t root_atom_i = 1; root_atom_i <= total_root_level_atoms; root_atom_i++) { - test_root_atom = APar_ReturnChildrenAtoms (0, root_atom_i); - if (memcmp(parsedAtoms[test_root_atom].AtomicName, "moov", 4) == 0) { - break; - } - } - if (test_root_atom != 0) { - thisAtom = APar_CreateSparseAtom(&atom_surrogate, NULL, APar_FindLastChild_of_ParentAtom(test_root_atom)); - } - } - } - break; - } else if (iter == 0 && !createMissing) { - search_atom_name = NULL; //force the break; - break; - } - //fprintf(stdout, "while loop %s %u %u\n", parsedAtoms[iter].AtomicName, atom_index, desired_index); - iter = parsedAtoms[iter].NextAtomNumber; - } - - if (iter == 0 && (search_atom_name == NULL || search_atom_type == EXTENDED_ATOM) ) { - break; - } else { - uint8_t periodicity = 0; //allow atoms with periods in their names - while (true) { // search_atom_name = strsep(&atom_name,".") equivalent - if (search_atom_name[0] == 0) { - search_atom_name = NULL; - break; - } else if (search_atom_name[0] == '.' && periodicity > 3) { - search_atom_name++; - periodicity++; - break; - } else { - search_atom_name++; - periodicity++; - } - } - present_atomic_level++; - } - } - //APar_PrintAtomicTree(); //because PrintAtomicTree calls DetermineDynamicUpdate (which calls this FindAtom function) to print out padding space, an infinite loop occurs - APar_FreeSurrogateAtom(&atom_surrogate); - return thisAtom; + if (search_atom_type == PACKED_LANG_ATOM) { + last_hierarchical_atom = APar_FindLastLikeNamedAtom( + atom_surrogate.AtomicName, + last_known_present_parent->AtomicNumber); + } else { + last_hierarchical_atom = APar_FindLastChild_of_ParentAtom( + last_known_present_parent->AtomicNumber); + } + thisAtom = APar_CreateSparseAtom(&atom_surrogate, + last_known_present_parent, + last_hierarchical_atom); + search_atom_start_num = thisAtom->AtomicNumber; + if (strlen(search_atom_name) >= 4) { + last_known_present_parent = thisAtom; + } + } else { + // its a file-level atom that needs to be created, so it won't have a + // last_known_present_parent + if (strlen(atom_name) == 4) { + short total_root_level_atoms = APar_ReturnChildrenAtoms(0, 0); + short test_root_atom = 0; + + // scan through all top level atoms + for (uint8_t root_atom_i = 1; root_atom_i <= total_root_level_atoms; + root_atom_i++) { + test_root_atom = APar_ReturnChildrenAtoms(0, root_atom_i); + if (memcmp(parsedAtoms[test_root_atom].AtomicName, "moov", 4) == + 0) { + break; + } + } + if (test_root_atom != 0) { + thisAtom = APar_CreateSparseAtom( + &atom_surrogate, + NULL, + APar_FindLastChild_of_ParentAtom(test_root_atom)); + } + } + } + break; + } else if (iter == 0 && !createMissing) { + search_atom_name = NULL; // force the break; + break; + } + // fprintf(stdout, "while loop %s %u %u\n", parsedAtoms[iter].AtomicName, + // atom_index, desired_index); + iter = parsedAtoms[iter].NextAtomNumber; + } + + if (iter == 0 && + (search_atom_name == NULL || search_atom_type == EXTENDED_ATOM)) { + break; + } else { + uint8_t periodicity = 0; // allow atoms with periods in their names + while (true) { // search_atom_name = strsep(&atom_name,".") equivalent + if (search_atom_name[0] == 0) { + search_atom_name = NULL; + break; + } else if (search_atom_name[0] == '.' && periodicity > 3) { + search_atom_name++; + periodicity++; + break; + } else { + search_atom_name++; + periodicity++; + } + } + present_atomic_level++; + } + } + // APar_PrintAtomicTree(); //because PrintAtomicTree calls + // DetermineDynamicUpdate (which calls this FindAtom function) to print out + // padding space, an infinite loop occurs + APar_FreeSurrogateAtom(&atom_surrogate); + return thisAtom; } /////////////////////////////////////////////////////////////////////////////////////// -// File scanning & atom parsing // +// File scanning & atom parsing // /////////////////////////////////////////////////////////////////////////////////////// -void APar_AtomizeFileInfo(uint64_t Astart, uint64_t Alength, - uint64_t Aextendedlength, char* Astring, uint8_t Alevel, - uint8_t Acon_state, uint8_t Aclass, uint32_t Averflags, - uint16_t Alang, uuid_vitals* uuid_info) -{ - static bool passed_mdat = false; - AtomicInfo* thisAtom; - - if (atom_number < 0 || atom_number >= MAX_ATOMS) { - fprintf(stderr, "too many atoms\n"); - abort(); - } - - thisAtom = &parsedAtoms[atom_number]; - - thisAtom->AtomicStart = Astart; - thisAtom->AtomicLength = Alength; - thisAtom->AtomicLengthExtended = Aextendedlength; - thisAtom->AtomicNumber = atom_number; - thisAtom->AtomicLevel = Alevel; - thisAtom->AtomicContainerState = Acon_state; - thisAtom->AtomicClassification = Aclass; - - thisAtom->AtomicName = (char*)malloc(sizeof(char)*20); - memset(thisAtom->AtomicName, 0, sizeof(char)*20); - - if (Aclass == EXTENDED_ATOM) { - thisAtom->uuid_style = uuid_info->uuid_form; - if (uuid_info->uuid_form == UUID_DEPRECATED_FORM) { - memcpy(thisAtom->AtomicName, Astring, 4); - thisAtom->uuid_ap_atomname = (char*)calloc(1, sizeof(char)*16); - memcpy(thisAtom->uuid_ap_atomname, Astring, 4); - } else { - memcpy(thisAtom->AtomicName, uuid_info->binary_uuid, 16); - if (uuid_info->uuid_form == UUID_AP_SHA1_NAMESPACE) { - thisAtom->uuid_ap_atomname = (char*)calloc(1, sizeof(char)*16); - memcpy(thisAtom->uuid_ap_atomname, uuid_info->uuid_AP_atom_name, 4); - } - } - } else { - memcpy(thisAtom->AtomicName, Astring, 4); - } - - thisAtom->AtomicVerFlags = Averflags; - thisAtom->AtomicLanguage = Alang; - - thisAtom->ancillary_data = 0; - - //set the next atom number of the PREVIOUS atom (we didn't know there would be one until now); this is our default normal mode - if (atom_number > 0) { - parsedAtoms[atom_number-1].NextAtomNumber = atom_number; - } - thisAtom->NextAtomNumber=0; //this could be the end... (we just can't quite say until we find another atom) - - if (strncmp(Astring, "mdat", 4) == 0) { - passed_mdat = true; - } - - if (!passed_mdat && Alevel == 1) { - bytes_before_mdat += Alength; //this value gets used during FreeFree (for removed_bytes_tally) & chunk offset calculations - } - thisAtom->ID32_TagInfo = NULL; +void APar_AtomizeFileInfo(uint64_t Astart, + uint64_t Alength, + uint64_t Aextendedlength, + char *Astring, + uint8_t Alevel, + uint8_t Acon_state, + uint8_t Aclass, + uint32_t Averflags, + uint16_t Alang, + uuid_vitals *uuid_info) { + static bool passed_mdat = false; + AtomicInfo *thisAtom; + + if (atom_number < 0 || atom_number >= MAX_ATOMS) { + fprintf(stderr, "too many atoms\n"); + abort(); + } + + thisAtom = &parsedAtoms[atom_number]; + + thisAtom->AtomicStart = Astart; + thisAtom->AtomicLength = Alength; + thisAtom->AtomicLengthExtended = Aextendedlength; + thisAtom->AtomicNumber = atom_number; + thisAtom->AtomicLevel = Alevel; + thisAtom->AtomicContainerState = Acon_state; + thisAtom->AtomicClassification = Aclass; + + thisAtom->AtomicName = (char *)malloc(sizeof(char) * 20); + memset(thisAtom->AtomicName, 0, sizeof(char) * 20); + + if (Aclass == EXTENDED_ATOM) { + thisAtom->uuid_style = uuid_info->uuid_form; + if (uuid_info->uuid_form == UUID_DEPRECATED_FORM) { + memcpy(thisAtom->AtomicName, Astring, 4); + thisAtom->uuid_ap_atomname = (char *)calloc(1, sizeof(char) * 16); + memcpy(thisAtom->uuid_ap_atomname, Astring, 4); + } else { + memcpy(thisAtom->AtomicName, uuid_info->binary_uuid, 16); + if (uuid_info->uuid_form == UUID_AP_SHA1_NAMESPACE) { + thisAtom->uuid_ap_atomname = (char *)calloc(1, sizeof(char) * 16); + memcpy(thisAtom->uuid_ap_atomname, uuid_info->uuid_AP_atom_name, 4); + } + } + } else { + memcpy(thisAtom->AtomicName, Astring, 4); + } + + thisAtom->AtomicVerFlags = Averflags; + thisAtom->AtomicLanguage = Alang; + + thisAtom->ancillary_data = 0; + + // set the next atom number of the PREVIOUS atom (we didn't know there would + // be one until now); this is our default normal mode + if (atom_number > 0) { + parsedAtoms[atom_number - 1].NextAtomNumber = atom_number; + } + thisAtom->NextAtomNumber = 0; // this could be the end... (we just can't quite + // say until we find another atom) + + if (strncmp(Astring, "mdat", 4) == 0) { + passed_mdat = true; + } + + if (!passed_mdat && Alevel == 1) { + bytes_before_mdat += + Alength; // this value gets used during FreeFree (for + // removed_bytes_tally) & chunk offset calculations + } + thisAtom->ID32_TagInfo = NULL; - atom_number++; //increment to the next AtomicInfo array + atom_number++; // increment to the next AtomicInfo array - return; + return; } uint8_t APar_GetCurrentAtomDepth(uint64_t atom_start, uint64_t atom_length) { - uint8_t level = 1; - for (int i = 0; i < atom_number; i++) { - AtomicInfo* thisAtom = &parsedAtoms[i]; - if (atom_start == (thisAtom->AtomicStart + thisAtom->AtomicLength) ) { - return thisAtom->AtomicLevel; - } else { - if ( (atom_start < thisAtom->AtomicStart + thisAtom->AtomicLength) && (atom_start > thisAtom->AtomicStart) ) { - level++; - } - } - } - return level; -} - -void APar_IdentifyBrand(char* file_brand ) { - brand = UInt32FromBigEndian(file_brand); - switch (brand) { - //what ISN'T supported - case 0x71742020 : //'qt ' --this is listed at mp4ra, but there are features of the file that aren't supported (like the 4 NULL bytes after the last udta child atom - fprintf(stdout, "AtomicParsley error: Quicktime movie files are not supported.\n"); - exit(2); - break; - - // - //3GPP2 specification documents brands - // - - case 0x33673262 : //'3g2b' 3GPP2 release A - metadata_style = THIRD_GEN_PARTNER_VER2_REL_A; //3GPP2 C.S0050-A_v1.0_060403, Annex A.2 lists differences between 3GPP & 3GPP2 - assets are not listed - break; - - case 0x33673261 : //'3g2a' //3GPP2 release 0 - metadata_style = THIRD_GEN_PARTNER_VER2; - break; - - // - //3GPP specification documents brands, not all are listed at mp4ra - // - - case 0x33677037 : //'3gp7' //Release 7 introduces ID32; though it doesn't list a iso bmffv2 compatible brand. Technically, ID32 - //could be used on older 3gp brands, but iso2 would have to be added to the compatible brand list. - case 0x33677337 : //'3gs7' //I don't feel the need to do that, since other things might have to be done. And I'm not looking into it. - case 0x33677237 : //'3gr7' - case 0x33676537 : //'3ge7' - case 0x33676737 : //'3gg7' - metadata_style = THIRD_GEN_PARTNER_VER1_REL7; - break; - - case 0x33677036 : //'3gp6' //3gp assets which were introducted by NTT DoCoMo to the Rel6 workgroup on January 16, 2003 - //with S4-030005.zip from http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/ (! albm, loci) - case 0x33677236 : //'3gr6' progressive - case 0x33677336 : //'3gs6' streaming - case 0x33676536 : //'3ge6' extended presentations (jpeg images) - case 0x33676736 : //'3gg6' general (not yet suitable; superset) - metadata_style = THIRD_GEN_PARTNER_VER1_REL6; - break; - - case 0x33677034 : //'3gp4' //3gp assets (the full complement) are available: source clause is S5.5 of TS26.244 (Rel6.4 & later): - case 0x33677035 : //'3gp5' //"that the file conforms to the specification; it includes everything required by, - metadata_style = THIRD_GEN_PARTNER; //and nothing contrary to the specification (though there may be other material)" - break; //it stands to reason that 3gp assets aren't contrary since 'udta' is defined by iso bmffv1 - - // - //other brands that are have compatible brands relating to 3GPP/3GPP2 - // - - case 0x6B646469 : //'kddi' //3GPP2 EZmovie (optionally restricted) media; these have a 3GPP2 compatible brand - metadata_style = THIRD_GEN_PARTNER_VER2; - break; - case 0x6D6D7034 : //'mmp4' - metadata_style = THIRD_GEN_PARTNER; - break; - - // - //what IS supported for iTunes-style metadata - // - - case 0x4D534E56 : //'MSNV' (PSP) - this isn't actually listed at mp4ra, but since they are popular... - metadata_style = ITUNES_STYLE; - psp_brand = true; - break; - case 0x4D344120 : //'M4A ' -- these are all listed at http://www.mp4ra.org/filetype.html as registered brands - case 0x4D344220 : //'M4B ' - case 0x4D345020 : //'M4P ' - case 0x4D345620 : //'M4V ' - case 0x4D345648 : //'MV4H' - case 0x4D345650 : //'M4VP' - case 0x6D703432 : //'mp42' - case 0x6D703431 : //'mp41' - case 0x69736F6D : //'isom' - case 0x69736F32 : //'iso2' - case 0x61766331 : //'avc1' - - - metadata_style = ITUNES_STYLE; - break; - - // - //other brands that are derivatives of the ISO Base Media File Format - // - case 0x6D6A7032 : //'mjp2' - case 0x6D6A3273 : //'mj2s' - metadata_style = MOTIONJPEG2000; - break; - - //other lesser unsupported brands; http://www.mp4ra.org/filetype.html like dv, mp21 & ... whatever mpeg7 brand is - default : - fprintf(stdout, "AtomicParsley error: unsupported MPEG-4 file brand found '%s'\n", file_brand); - exit(2); - break; - } - return; -} - -void APar_TestCompatibleBrand(FILE* file, uint64_t atom_start, uint64_t atom_length) { - if (atom_length <= 16) return; - uint32_t compatible_brand = 0; - - for (uint32_t brand = 16; brand < atom_length; brand+=4) { - compatible_brand = APar_read32(twenty_byte_buffer, file, atom_start+brand); - if (compatible_brand == 0x6D703432 || compatible_brand == 0x69736F32) { - parsedAtoms[atom_number-1].ancillary_data = compatible_brand; - } - } - return; -} - -void APar_Extract_stsd_codec(FILE* file, uint64_t midJump) { - memset(twenty_byte_buffer, 0, 12); - APar_readX(twenty_byte_buffer, file, midJump, 12); - parsedAtoms[atom_number-1].ancillary_data = UInt32FromBigEndian( twenty_byte_buffer +4 ); + uint8_t level = 1; + for (int i = 0; i < atom_number; i++) { + AtomicInfo *thisAtom = &parsedAtoms[i]; + if (atom_start == (thisAtom->AtomicStart + thisAtom->AtomicLength)) { + return thisAtom->AtomicLevel; + } else { + if ((atom_start < thisAtom->AtomicStart + thisAtom->AtomicLength) && + (atom_start > thisAtom->AtomicStart)) { + level++; + } + } + } + return level; +} + +void APar_IdentifyBrand(char *file_brand) { + brand = UInt32FromBigEndian(file_brand); + switch (brand) { + // what ISN'T supported + case 0x71742020: //'qt ' --this is listed at mp4ra, but there are features + // of the file that aren't supported (like the 4 NULL bytes + // after the last udta child atom + fprintf(stdout, + "AtomicParsley error: Quicktime movie files are not supported.\n"); + exit(2); + break; + + // + // 3GPP2 specification documents brands + // + + case 0x33673262: //'3g2b' 3GPP2 release A + metadata_style = + THIRD_GEN_PARTNER_VER2_REL_A; // 3GPP2 C.S0050-A_v1.0_060403, Annex A.2 + // lists differences between 3GPP & 3GPP2 + // - assets are not listed + break; + + case 0x33673261: //'3g2a' //3GPP2 release 0 + metadata_style = THIRD_GEN_PARTNER_VER2; + break; + + // + // 3GPP specification documents brands, not all are listed at mp4ra + // + + case 0x33677037: //'3gp7' //Release 7 introduces + // ID32; though it doesn't list a iso bmffv2 compatible + // brand. Technically, ID32 could be used on older 3gp + // brands, but iso2 would have to be added to the compatible + // brand list. + case 0x33677337: //'3gs7' //I don't feel the need to + // do that, since other things might have to be done. And I'm + // not looking into it. + case 0x33677237: //'3gr7' + case 0x33676537: //'3ge7' + case 0x33676737: //'3gg7' + metadata_style = THIRD_GEN_PARTNER_VER1_REL7; + break; + + case 0x33677036: //'3gp6' //3gp assets which were + // introducted by NTT DoCoMo to the Rel6 workgroup on January + // 16, 2003 with S4-030005.zip from + // http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/ (! + // albm, loci) + case 0x33677236: //'3gr6' progressive + case 0x33677336: //'3gs6' streaming + case 0x33676536: //'3ge6' extended presentations (jpeg images) + case 0x33676736: //'3gg6' general (not yet suitable; superset) + metadata_style = THIRD_GEN_PARTNER_VER1_REL6; + break; + + case 0x33677034: //'3gp4' //3gp assets (the full + // complement) are available: source clause is S5.5 of + // TS26.244 (Rel6.4 & later): + case 0x33677035: //'3gp5' //"that the file conforms + // to the specification; it includes everything required by, + metadata_style = + THIRD_GEN_PARTNER; // and nothing contrary to the specification (though + // there may be other material)" + break; // it stands to reason that 3gp assets aren't contrary since 'udta' + // is defined by iso bmffv1 + + // + // other brands that are have compatible brands relating to 3GPP/3GPP2 + // + + case 0x6B646469: //'kddi' //3GPP2 EZmovie (optionally + // restricted) media; these have a 3GPP2 compatible brand + metadata_style = THIRD_GEN_PARTNER_VER2; + break; + case 0x6D6D7034: //'mmp4' + metadata_style = THIRD_GEN_PARTNER; + break; + + // + // what IS supported for iTunes-style metadata + // + + case 0x4D534E56: //'MSNV' (PSP) - this isn't actually listed at mp4ra, but + // since they are popular... + metadata_style = ITUNES_STYLE; + psp_brand = true; + break; + case 0x4D344120: //'M4A ' -- these are all listed at + // https://mp4ra.org/#/brands as registered brands + case 0x4D344220: //'M4B ' + case 0x4D345020: //'M4P ' + case 0x4D345620: //'M4V ' + case 0x4D345648: //'MV4H' + case 0x4D345650: //'M4VP' + case 0x64617368: //'dash' + case 0x66347620: //'f4v' + case 0x6D703432: //'mp42' + case 0x6D703431: //'mp41' + case 0x69736F6D: //'isom' + case 0x69736F32: //'iso2' + case 0x61766331: //'avc1' + + metadata_style = ITUNES_STYLE; + break; + + // + // other brands that are derivatives of the ISO Base Media File Format + // + case 0x6D6A7032: //'mjp2' + case 0x6D6A3273: //'mj2s' + metadata_style = MOTIONJPEG2000; + break; + + // other lesser unsupported brands; http://www.mp4ra.org/filetype.html like + // dv, mp21 & ... whatever mpeg7 brand is + default: + fprintf(stdout, + "AtomicParsley error: unsupported MPEG-4 file brand found '%s'\n", + file_brand); + exit(2); + break; + } + return; +} + +void APar_TestCompatibleBrand(FILE *file, + uint64_t atom_start, + uint64_t atom_length) { + if (atom_length <= 16) + return; + uint32_t compatible_brand = 0; + + for (uint32_t brand = 16; brand < atom_length; brand += 4) { + compatible_brand = + APar_read32(twenty_byte_buffer, file, atom_start + brand); + if (compatible_brand == 0x6D703432 || compatible_brand == 0x69736F32) { + parsedAtoms[atom_number - 1].ancillary_data = compatible_brand; + } + } + return; +} + +void APar_Extract_stsd_codec(FILE *file, uint64_t midJump) { + memset(twenty_byte_buffer, 0, 12); + APar_readX(twenty_byte_buffer, file, midJump, 12); + parsedAtoms[atom_number - 1].ancillary_data = + UInt32FromBigEndian(twenty_byte_buffer + 4); return; } @@ -1044,147 +1283,195 @@ APar_LocateDataReference fill ----------------------*/ -void APar_LocateDataReference(short chunk_offset_idx, FILE* file) { - uint32_t data_ref_idx = 0; - short sampletable_atom_idx = 0; - short minf_atom_idx = 0; - AtomicInfo *stsd_atom, *dinf_atom, *dref_atom, *target_reference_atom = NULL; - - sampletable_atom_idx = APar_FindParentAtom(chunk_offset_idx, parsedAtoms[chunk_offset_idx].AtomicLevel); - stsd_atom = APar_FindChildAtom(sampletable_atom_idx, "stsd"); - if (stsd_atom == NULL) { - return; - } - data_ref_idx = APar_read32(twenty_byte_buffer, file, stsd_atom->AtomicStart+28); - - minf_atom_idx = APar_FindParentAtom(sampletable_atom_idx, parsedAtoms[sampletable_atom_idx].AtomicLevel); - dinf_atom = APar_FindChildAtom(minf_atom_idx, "dinf"); - dref_atom = APar_FindChildAtom(dinf_atom->AtomicNumber, "dref"); - - target_reference_atom = APar_FindChildAtom(dref_atom->AtomicNumber, "any", 4, data_ref_idx); - - if (target_reference_atom != NULL) { - parsedAtoms[chunk_offset_idx].ancillary_data = target_reference_atom->AtomicVerFlags; - } - return; +void APar_LocateDataReference(short chunk_offset_idx, FILE *file) { + uint32_t data_ref_idx = 0; + short sampletable_atom_idx = 0; + short minf_atom_idx = 0; + AtomicInfo *stsd_atom, *dinf_atom, *dref_atom, *target_reference_atom = NULL; + + sampletable_atom_idx = APar_FindParentAtom( + chunk_offset_idx, parsedAtoms[chunk_offset_idx].AtomicLevel); + stsd_atom = APar_FindChildAtom(sampletable_atom_idx, "stsd"); + if (stsd_atom == NULL) { + return; + } + data_ref_idx = + APar_read32(twenty_byte_buffer, file, stsd_atom->AtomicStart + 28); + + minf_atom_idx = APar_FindParentAtom( + sampletable_atom_idx, parsedAtoms[sampletable_atom_idx].AtomicLevel); + dinf_atom = APar_FindChildAtom(minf_atom_idx, "dinf"); + dref_atom = APar_FindChildAtom(dinf_atom->AtomicNumber, "dref"); + + target_reference_atom = + APar_FindChildAtom(dref_atom->AtomicNumber, "any", 4, data_ref_idx); + + if (target_reference_atom != NULL) { + parsedAtoms[chunk_offset_idx].ancillary_data = + target_reference_atom->AtomicVerFlags; + } + return; } void APar_SampleTableIterator(FILE *file) { - uint8_t total_tracks = 0; - uint8_t a_track = 0; - char track_path[36]; - memset(track_path, 0, 36); - AtomicInfo* samples_parent = NULL; - AtomicInfo* chunk_offset_atom = NULL; - - APar_FindAtomInTrack(total_tracks, a_track, NULL); //gets the number of tracks - for (uint8_t trk_idx=1; trk_idx <= total_tracks; trk_idx++) { - sprintf(track_path, "moov.trak[%u].mdia.minf.stbl", trk_idx); - samples_parent = APar_FindAtom(track_path, false, SIMPLE_ATOM, 0, false); - if (samples_parent != NULL) { - chunk_offset_atom = APar_FindChildAtom(samples_parent->AtomicNumber, "stco"); - if (chunk_offset_atom == NULL) chunk_offset_atom = APar_FindChildAtom(samples_parent->AtomicNumber, "co64"); - if (chunk_offset_atom != NULL) { - APar_LocateDataReference(chunk_offset_atom->AtomicNumber, file); - } - } - } - return; + uint8_t total_tracks = 0; + uint8_t a_track = 0; + char track_path[36]; + memset(track_path, 0, 36); + AtomicInfo *samples_parent = NULL; + AtomicInfo *chunk_offset_atom = NULL; + + APar_FindAtomInTrack(total_tracks, a_track, NULL); // gets the number of + // tracks + for (uint8_t trk_idx = 1; trk_idx <= total_tracks; trk_idx++) { + sprintf(track_path, "moov.trak[%u].mdia.minf.stbl", trk_idx); + samples_parent = APar_FindAtom(track_path, false, SIMPLE_ATOM, 0, false); + if (samples_parent != NULL) { + chunk_offset_atom = + APar_FindChildAtom(samples_parent->AtomicNumber, "stco"); + if (chunk_offset_atom == NULL) + chunk_offset_atom = + APar_FindChildAtom(samples_parent->AtomicNumber, "co64"); + if (chunk_offset_atom != NULL) { + APar_LocateDataReference(chunk_offset_atom->AtomicNumber, file); + } + } + } + return; } /*---------------------- APar_MatchToKnownAtom atom_name - the name of our newly found atom atom_container - the name of the parent container atom - fromFile - controls the manner of extracting parents (passed thu to another function) + fromFile - controls the manner of extracting parents (passed thu to another +function) - Using the atom_name of this new atom, search through KnownAtoms, testing that the names match. If they do, move onto a finer grained sieve. - If the parent can be at any level (like "free"), just let it through; if the parent is "ilst" (iTunes-style metadata), or a uuid, return a generic match - The final test is the one most atoms will go through. Some atoms can have different parents - up to 5 different parents are allowed by this version of AP - Iterate through the known parents, and test it against atom_container. If they match, return the properties of the known atom -----------------------*/ -int APar_MatchToKnownAtom(const char* atom_name, const char* atom_container, bool fromFile, const char* find_atom_path) { - uint32_t total_known_atoms = (sizeof(KnownAtoms)/sizeof(*KnownAtoms)); - uint32_t return_known_atom = 0; - - //if this atom is contained by 'ilst', then it is *highly* likely an iTunes-style metadata parent atom - if ( memcmp(atom_container, "ilst", 4) == 0 && memcmp(atom_name, "uuid", 4) != 0) { - return_known_atom = total_known_atoms-2; //2nd to last KnowAtoms is a generic placeholder iTunes-parent atom - //fprintf(stdout, "found iTunes parent %s = atom %s\n", KnownAtoms[return_known_atom].known_atom_name, atom_name); - - //if this atom is "data" get the full path to it; we will take any atom under 'ilst' and consider it an iTunes metadata parent atom - } else if (memcmp(atom_name, "data", 4) == 0 && find_atom_path != NULL) { - if (strncmp(find_atom_path, "moov.udta.meta.ilst.", 20) == 0) { - return_known_atom = total_known_atoms-1; //last KnowAtoms is a generic placeholder iTunes-data atom - //fprintf(stdout, "found iTunes data child\n"); - } - - } else if (memcmp(atom_name, "data", 4) == 0) { - char* fullpath = (char *)malloc(sizeof(char) * 200); - memset(fullpath, 0, sizeof(char) * 200); - - if (fromFile) { - APar_ProvideAtomPath(parsedAtoms[atom_number-1].AtomicNumber, fullpath, fromFile); - } else { //find_atom_path only is NULL in APar_ScanAtoms (where fromFile is true) and in APar_CreateSparseAtom, where atom_number was just filled - APar_ProvideAtomPath(parsedAtoms[atom_number].AtomicNumber, fullpath, fromFile); - } - - //fprintf(stdout, "APar_ProvideAtomPath gives %s (%s-%s)\n", fullpath, atom_name, atom_container); - if (strncmp(fullpath, "moov.udta.meta.ilst.", 20) == 0) { - return_known_atom = total_known_atoms-1; //last KnowAtoms is a generic placeholder iTunes-data atom - //fprintf(stdout, "found iTunes data child\n"); - } - free(fullpath); - fullpath = NULL; - - //if this atom is "esds" get the full path to it; take any atom under 'stsd' as a parent to esds (that parent would be a 4CC codec; not all do have 'esds'...) - } else if (memcmp(atom_name, "esds", 4) == 0 ) { - char* fullpath = (char *)malloc(sizeof(char) * 300); - memset(fullpath, 0, sizeof(char) * 200); - - APar_ProvideAtomPath(parsedAtoms[atom_number-1].AtomicNumber, fullpath, fromFile); - - if (strncmp(fullpath, "moov.trak.mdia.minf.stbl.stsd.", 30) == 0) { - return_known_atom = total_known_atoms-3; //manually return the esds atom - } - free(fullpath); - fullpath = NULL; - - - } else { - //try matching the name of the atom - for(uint32_t i = 1; i < total_known_atoms; i++) { - if (memcmp(atom_name, KnownAtoms[i].known_atom_name, 4) == 0) { - //name matches, now see if the container atom matches any known container for that atom - if ( strncmp(KnownAtoms[i].known_parent_atoms[0], "_ANY_LEVEL", 10) == 0 ) { - return_known_atom = i; //the list starts at 0; the unknown atom is at 0; first known atom (ftyp) is at 1 - break; - - } else { - uint8_t total_known_containers = (uint8_t)(sizeof(KnownAtoms[i].known_parent_atoms)/sizeof(*KnownAtoms[i].known_parent_atoms)); //always 5 - for (uint8_t iii = 0; iii < total_known_containers; iii++) { - if (KnownAtoms[i].known_parent_atoms[iii] != NULL) { - if ( strncmp(atom_container, KnownAtoms[i].known_parent_atoms[iii], strlen(atom_container) ) == 0) { //strlen(atom_container) - return_known_atom = i; //the list starts at 0; the unknown atom is at 0; first known atom (ftyp) is at 1 - break; - } - } - } - } - if (return_known_atom) { - break; - } - } - } - } - if ( return_known_atom > total_known_atoms ) { - return_known_atom = 0; - } - //accommodate any future child to dref; force to being versioned - if (return_known_atom == 0 && memcmp(atom_container, "dref", 4) == 0) { - return_known_atom = total_known_atoms-4; //return a generic *VERSIONED* child atom; otherwise an atom without flags will be present & chunk offsets will not update - } - return return_known_atom; + Using the atom_name of this new atom, search through KnownAtoms, testing +that the names match. If they do, move onto a finer grained sieve. If the parent +can be at any level (like "free"), just let it through; if the parent is "ilst" +(iTunes-style metadata), or a uuid, return a generic match The final test is the +one most atoms will go through. Some atoms can have different parents - up to 5 +different parents are allowed by this version of AP Iterate through the known +parents, and test it against atom_container. If they match, return the +properties of the known atom +----------------------*/ +int APar_MatchToKnownAtom(const char *atom_name, + const char *atom_container, + bool fromFile, + const char *find_atom_path) { + uint32_t total_known_atoms = (sizeof(KnownAtoms) / sizeof(*KnownAtoms)); + uint32_t return_known_atom = 0; + + // if this atom is contained by 'ilst', then it is *highly* likely an + // iTunes-style metadata parent atom + if (memcmp(atom_container, "ilst", 4) == 0 && + memcmp(atom_name, "uuid", 4) != 0) { + return_known_atom = + total_known_atoms - + 2; // 2nd to last KnowAtoms is a generic placeholder iTunes-parent atom + // fprintf(stdout, "found iTunes parent %s = atom %s\n", + // KnownAtoms[return_known_atom].known_atom_name, atom_name); + + // if this atom is "data" get the full path to it; we will take any atom + // under 'ilst' and consider it an iTunes metadata parent atom + } else if (memcmp(atom_name, "data", 4) == 0 && find_atom_path != NULL) { + if (strncmp(find_atom_path, "moov.udta.meta.ilst.", 20) == 0) { + return_known_atom = + total_known_atoms - + 1; // last KnowAtoms is a generic placeholder iTunes-data atom + // fprintf(stdout, "found iTunes data child\n"); + } + + } else if (memcmp(atom_name, "data", 4) == 0) { + char *fullpath = (char *)malloc(sizeof(char) * 200); + memset(fullpath, 0, sizeof(char) * 200); + + if (fromFile) { + APar_ProvideAtomPath( + parsedAtoms[atom_number - 1].AtomicNumber, fullpath, fromFile); + } else { // find_atom_path only is NULL in APar_ScanAtoms (where fromFile is + // true) and in APar_CreateSparseAtom, where atom_number was just + // filled + APar_ProvideAtomPath( + parsedAtoms[atom_number].AtomicNumber, fullpath, fromFile); + } + + // fprintf(stdout, "APar_ProvideAtomPath gives %s (%s-%s)\n", fullpath, + // atom_name, atom_container); + if (strncmp(fullpath, "moov.udta.meta.ilst.", 20) == 0) { + return_known_atom = + total_known_atoms - + 1; // last KnowAtoms is a generic placeholder iTunes-data atom + // fprintf(stdout, "found iTunes data child\n"); + } + free(fullpath); + fullpath = NULL; + + // if this atom is "esds" get the full path to it; take any atom under + // 'stsd' as a parent to esds (that parent would be a 4CC codec; not all do + // have 'esds'...) + } else if (memcmp(atom_name, "esds", 4) == 0) { + char *fullpath = (char *)malloc(sizeof(char) * 300); + memset(fullpath, 0, sizeof(char) * 200); + + APar_ProvideAtomPath( + parsedAtoms[atom_number - 1].AtomicNumber, fullpath, fromFile); + + if (strncmp(fullpath, "moov.trak.mdia.minf.stbl.stsd.", 30) == 0) { + return_known_atom = total_known_atoms - 3; // manually return the esds + // atom + } + free(fullpath); + fullpath = NULL; + + } else { + // try matching the name of the atom + for (uint32_t i = 1; i < total_known_atoms; i++) { + if (memcmp(atom_name, KnownAtoms[i].known_atom_name, 4) == 0) { + // name matches, now see if the container atom matches any known + // container for that atom + if (strncmp(KnownAtoms[i].known_parent_atoms[0], "_ANY_LEVEL", 10) == + 0) { + return_known_atom = i; // the list starts at 0; the unknown atom is at + // 0; first known atom (ftyp) is at 1 + break; + + } else { + uint8_t total_known_containers = + (uint8_t)(sizeof(KnownAtoms[i].known_parent_atoms) / + sizeof(*KnownAtoms[i].known_parent_atoms)); // always 5 + for (uint8_t iii = 0; iii < total_known_containers; iii++) { + if (KnownAtoms[i].known_parent_atoms[iii] != NULL) { + if (strncmp(atom_container, + KnownAtoms[i].known_parent_atoms[iii], + strlen(atom_container)) == + 0) { // strlen(atom_container) + return_known_atom = + i; // the list starts at 0; the unknown atom is at 0; first + // known atom (ftyp) is at 1 + break; + } + } + } + } + if (return_known_atom) { + break; + } + } + } + } + if (return_known_atom > total_known_atoms) { + return_known_atom = 0; + } + // accommodate any future child to dref; force to being versioned + if (return_known_atom == 0 && memcmp(atom_container, "dref", 4) == 0) { + return_known_atom = + total_known_atoms - + 4; // return a generic *VERSIONED* child atom; otherwise an atom without + // flags will be present & chunk offsets will not update + } + return return_known_atom; } /*---------------------- @@ -1193,30 +1480,37 @@ atom_length - length of the eval atom container - a string of the last known container atom (or FILE_LEVEL) - given the next atom (unknown string at this point), run some tests using its starting point and length. Iterate backwards through the already parsed - atoms, and for each test if it could contain this atom. Tests include if the container starts before ours (which it would to contain the new atom), - that its length is longer than our length (any parent would need to be longer than us if even by 8 bytes), the start + length sum of the parent atom - (where it ends), needs to be greater than or equal to where this new atom ends, and finally, that the eval containing atom be some form of parent as - defined in KnownAtoms -----------------------*/ -void APar_Manually_Determine_Parent(uint64_t atom_start, uint64_t atom_length, char* container) { - short preceding_atom = atom_number-1; - while (parsedAtoms[preceding_atom].AtomicNumber != 0) { - - if (parsedAtoms[preceding_atom].AtomicStart < atom_start && - parsedAtoms[preceding_atom].AtomicLength > atom_length && - parsedAtoms[preceding_atom].AtomicStart + parsedAtoms[preceding_atom].AtomicLength >= atom_start + atom_length && - parsedAtoms[preceding_atom].AtomicContainerState <= DUAL_STATE_ATOM ) { - memcpy(container, parsedAtoms[preceding_atom].AtomicName, 5); - break; - - } else { - preceding_atom--; - } - if (preceding_atom == 0) { - strcpy(container, "FILE_LEVEL"); - } - } + given the next atom (unknown string at this point), run some tests using its +starting point and length. Iterate backwards through the already parsed atoms, +and for each test if it could contain this atom. Tests include if the container +starts before ours (which it would to contain the new atom), that its length is +longer than our length (any parent would need to be longer than us if even by 8 +bytes), the start + length sum of the parent atom (where it ends), needs to be +greater than or equal to where this new atom ends, and finally, that the eval +containing atom be some form of parent as defined in KnownAtoms +----------------------*/ +void APar_Manually_Determine_Parent(uint64_t atom_start, + uint64_t atom_length, + char *container) { + short preceding_atom = atom_number - 1; + while (parsedAtoms[preceding_atom].AtomicNumber != 0) { + + if (parsedAtoms[preceding_atom].AtomicStart < atom_start && + parsedAtoms[preceding_atom].AtomicLength > atom_length && + parsedAtoms[preceding_atom].AtomicStart + + parsedAtoms[preceding_atom].AtomicLength >= + atom_start + atom_length && + parsedAtoms[preceding_atom].AtomicContainerState <= DUAL_STATE_ATOM) { + memcpy(container, parsedAtoms[preceding_atom].AtomicName, 5); + break; + + } else { + preceding_atom--; + } + if (preceding_atom == 0) { + strcpy(container, "FILE_LEVEL"); + } + } } /*---------------------- @@ -1252,852 +1546,1115 @@ ----------------------*/ void APar_ScanAtoms(const char *path, bool deepscan_REQ) { - if (!parsedfile) { - file_size = findFileSize(path); + if (!parsedfile) { + file_size = findFileSize(path); - FILE *file = APar_OpenFile(path, "rb"); - if (file != NULL) { - char *data = (char *)calloc(1, 13); - char *container = (char *)calloc(1, 20); - memcpy(container, "FILE_LEVEL", 10); - bool corrupted_data_atom = false; - bool jpeg2000signature = false; - - uuid_vitals uuid_info = {0}; - uuid_info.binary_uuid=(char*)malloc(sizeof(char)*16 + 1); //this will hold any potential 16byte uuids - uuid_info.uuid_AP_atom_name=(char*)malloc(sizeof(char)*5); //this will hold any atom name that is written after the uuid written by AP - - if (data == NULL) return; - uint64_t dataSize = 0; - uint64_t jump = 0; - - APar_readX(data, file, 0, 12); - char *atom = data+4; - - if ( memcmp(data, "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A ", 12) == 0 ) { - jpeg2000signature = true; - } - - if ( memcmp(atom, "ftyp", 4) == 0 || jpeg2000signature) { - - dataSize = UInt32FromBigEndian(data); - jump = dataSize; - - APar_AtomizeFileInfo(0, jump, 0, atom, generalAtomicLevel, CHILD_ATOM, SIMPLE_ATOM, 0, 0 , &uuid_info); - - if (!jpeg2000signature) { - APar_IdentifyBrand( data + 8 ); - APar_TestCompatibleBrand(file, 0, dataSize); - } - - fseek(file, jump, SEEK_SET); - - while (jump < file_size) { - - uuid_info.uuid_form = UUID_DEPRECATED_FORM; //start with the assumption that any found atom is in the depracted uuid form - - APar_readX_noseek(data, file, 8); - char *atom = data+4; - dataSize = UInt32FromBigEndian(data); - - if (jpeg2000signature) { - if (memcmp(atom, "ftyp", 4) == 0) { - APar_readX_noseek(twenty_byte_buffer, file, 4); - APar_IdentifyBrand(twenty_byte_buffer); - } else { - exit(0); //the atom right after the jpeg2000/mjpeg2000 signature is *supposed* to be 'ftyp' - } - jpeg2000signature = false; - } - - if (dataSize > file_size - jump) { - dataSize = file_size - jump; - } - - if (dataSize == 0 && (atom[0] == 0 && atom[1] == 0 && atom[2] == 0 && atom[3] == 0) ) { - gapless_void_padding = file_size-jump; //Apple has decided to add around 2k of NULL space outside of any atom structure starting with iTunes 7.0.0 - break; //its possible this is part of gapless playback - but then why would it come after the 'free' at the end of a file like gpac writes? - } //after actual tested its elimination, it doesn't seem to be required for gapless playback - - //diagnose damage to 'cprt' by libmp4v2 in 1.4.1 & 1.5.0.1 - //typically, the length of this atom (dataSize) will exceeed it parent (which is reported as 17) - //true length ot this data will be 9 - impossible for iTunes-style 'data' atom. - if (memcmp(atom, "data", 4) == 0 && parsedAtoms[ atom_number-1].AtomicContainerState == PARENT_ATOM) { - if (dataSize > parsedAtoms[ atom_number-1].AtomicLength) { - dataSize = parsedAtoms[ atom_number-1].AtomicLength -8; //force its length to its true length - fprintf(stdout, "AtomicParsley warning: the 'data' child of the '%s' atom seems to be corrupted.\n", parsedAtoms[ atom_number-1].AtomicName); - corrupted_data_atom = true; - } - } - //end diagnosis; APar_Manually_Determine_Parent will still determine it to be a versioned atom (it tests by names), but at file write out, - //it will write with a length of 9 bytes - - APar_Manually_Determine_Parent(jump, dataSize, container); - int filtered_known_atom = APar_MatchToKnownAtom(atom, container, true, NULL); - - uint32_t atom_verflags = 0; - uint16_t atom_language = 0; - - if (memcmp(atom, "uuid", 4) == 0) { - memset(uuid_info.binary_uuid, 0, 17); - - APar_readX(uuid_info.binary_uuid, file, jump+8, 16); - - if (UInt32FromBigEndian(uuid_info.binary_uuid+8) == 0) { //the deperacted uuid form - memcpy(atom, uuid_info.binary_uuid, 4); - atom_verflags = APar_read32(uuid_info.binary_uuid, file, jump+12); - if (atom_verflags > AtomFlags_Data_UInt) { - atom_verflags = 0; - } - } else { - uint8_t uuid_version = APar_extract_uuid_version(NULL, uuid_info.binary_uuid); - APar_endian_uuid_bin_str_conversion(uuid_info.binary_uuid); - if (uuid_version == 5) { - uuid_info.uuid_form = UUID_SHA1_NAMESPACE; - //read in what AP would set the atom name to. The new uuid form is: - // 4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data - APar_readX(uuid_info.uuid_AP_atom_name, file, jump+24, 4); - - char uuid_of_foundname_in_AP_namesapce[20]; - APar_generate_uuid_from_atomname(uuid_info.uuid_AP_atom_name, uuid_of_foundname_in_AP_namesapce); - if (memcmp(uuid_info.binary_uuid, uuid_of_foundname_in_AP_namesapce, 16) == 0) { - uuid_info.uuid_form = UUID_AP_SHA1_NAMESPACE; //our own uuid ver5 atoms in the AtomicParsley.sf.net namespace - atom_verflags = APar_read32(twenty_byte_buffer, file, jump+28); - } - } else { - uuid_info.uuid_form = UUID_OTHER; - } - } - } - - if (KnownAtoms[filtered_known_atom].box_type == VERSIONED_ATOM && !corrupted_data_atom) { - atom_verflags = APar_read32(twenty_byte_buffer, file, jump+8); - } - - if (KnownAtoms[filtered_known_atom].box_type == PACKED_LANG_ATOM) { - atom_verflags = APar_read32(twenty_byte_buffer, file, jump+8); - - //the problem with storing the language is that the uint16_t 2 bytes that carry the actual language are in different places on different atoms - //some atoms have it right after the atom version/flags; some like rating/classification have it 8 bytes later; yrrc doesn't have it at all - char bitpacked_lang[4]; - memset(bitpacked_lang, 0, 4); - - uint32_t userdata_box = UInt32FromBigEndian(atom); - - switch (userdata_box) { - case 0x7469746C : //'titl' - case 0x64736370 : //'dscp' - case 0x63707274 : //'cprt' - case 0x70657266 : //'perf' - case 0x61757468 : //'auth' - case 0x676E7265 : //'gnre' - case 0x616C626D : //'albm' - case 0x6B797764 : //'kywd' - case 0x6C6F6369 : //'loci' - case 0x49443332 : //'ID32' ; technically not a 'user data box', but this only extracts the packed language (which ID32 does have) - { - atom_language = APar_read16(bitpacked_lang, file, jump + 12); - break; - } - case 0x636C7366 : //'clsf' - { - atom_language = APar_read16(bitpacked_lang, file, jump + 18); - break; - } - case 0x72746E67 : //'rtng' - { - atom_language = APar_read16(bitpacked_lang, file, jump + 20); - break; - } - //case 0x79727263 : //'yrrc' is the only 3gp tag that doesn't support multiple languages; won't even get here because != PACKED_LANG_ATOM - default : - { - break; //which means that any new/unknown packed language atoms will have their language of 0; AP will only support 1 of this atom name then - } - } - } - - //mdat.length=1; and ONLY supported for mdat atoms - no idea if the spec says "only mdat", but that's what I'm doing for now - if ( (strncmp(atom, "mdat", 4) == 0) && (generalAtomicLevel == 1) && (dataSize == 1) ) { - - uint64_t extended_dataSize = APar_read64( - twenty_byte_buffer, file, jump+8); - APar_AtomizeFileInfo(jump, 1, extended_dataSize, atom, generalAtomicLevel, KnownAtoms[filtered_known_atom].container_state, - KnownAtoms[filtered_known_atom].box_type, atom_verflags, atom_language, &uuid_info ); - - } else { - APar_AtomizeFileInfo(jump, dataSize, 0, atom, generalAtomicLevel, KnownAtoms[filtered_known_atom].container_state, - corrupted_data_atom ? SIMPLE_ATOM : KnownAtoms[filtered_known_atom].box_type, atom_verflags, atom_language, &uuid_info ); - } - corrupted_data_atom = false; - - //read in the name of an iTunes-style internal reverseDNS directly into parsedAtoms - if (memcmp(atom, "mean", 4) == 0 && memcmp(parsedAtoms[atom_number-2].AtomicName, "----", 4) == 0) { - parsedAtoms[atom_number-1].ReverseDNSdomain = (char *)calloc(1, sizeof(char) * dataSize); - - //jump + 12 because 'name' atom is the 2nd child - APar_readX(parsedAtoms[atom_number-1].ReverseDNSdomain, file, jump + 12, dataSize - 12); - } - if (memcmp(atom, "name", 4) == 0 && - memcmp(parsedAtoms[atom_number-2].AtomicName, "mean", 4) == 0 && - memcmp(parsedAtoms[atom_number-3].AtomicName, "----", 4) == 0) { - - parsedAtoms[atom_number-1].ReverseDNSname = (char *)calloc(1, sizeof(char) * dataSize); - - //jump + 12 because 'name' atom is the 2nd child - APar_readX(parsedAtoms[atom_number-1].ReverseDNSname, file, jump + 12, dataSize - 12); - } - - if (dataSize == 0) { // length = 0 means it reaches to EOF - break; - } - - switch (KnownAtoms[filtered_known_atom].container_state) { - case PARENT_ATOM : { - jump += 8; - break; - } - case CHILD_ATOM : { - if (memcmp(atom, "hdlr", 4) == 0) { - APar_readX(twenty_byte_buffer, file, jump+16, 4); - parsedAtoms[atom_number-1].ancillary_data = UInt32FromBigEndian(twenty_byte_buffer); - } - - if ((generalAtomicLevel == 1) && (dataSize == 1)) { //mdat.length =1 64-bit length that is more of a cludge. - jump += parsedAtoms[atom_number-1].AtomicLengthExtended; - } else { - jump += dataSize; - } - break; - } - case DUAL_STATE_ATOM : { - if (memcmp(atom, "meta", 4) == 0) { - jump += 12; - - } else if (memcmp(atom, "dref", 4) == 0) { - jump += 16; - - } else if (memcmp(atom, "iinf", 4) == 0) { - jump += 14; - - } else if (memcmp(atom, "stsd", 4) == 0) { - if (deepscan_REQ) { - //for a tree ONLY, we go all the way, parsing everything; for any other option, we leave this atom as a monolithic entity - jump += 16; - } else { - APar_Extract_stsd_codec(file, jump+16); //just get the codec used for this track - jump += dataSize; - } - - } else if (memcmp(atom, "schi", 4) == 0) { - if (memcmp(container, "sinf", 4) == 0) { //seems for iTMS drm files, schi is a simple parent atom, and 'user' comes right after it - jump += 8; - } else { - jump += dataSize; //no idea what it would be under srpp, so just skip over it - } - - } else if (memcmp(container, "stsd", 4) == 0) { - //each one is different, so list its size manually - //the beauty of this is that even if there is an error here or a new codec shows up, it only affects SHOWING the tree. - //Getting a tree for display ONLY purposes is different from when setting a tag - display ONLY goes into stsd; tagging makes 'stsd' monolithic. - //so setting metadata on unknown or improperly enumerated codecs (which might have different lengths) don't affect tagging. - uint32_t named_atom = UInt32FromBigEndian(atom); - switch(named_atom) { - case 0x6D703473 : { //mp4s - jump+= 16; - break; - } - case 0x73727470 : //srtp - case 0x72747020 : { //'rtp ' - jump+= 24; - break; - } - case 0x616C6163 : //alac - case 0x6D703461 : //mp4a - case 0x73616D72 : //samr - case 0x73617762 : //sawb - case 0x73617770 : //sawp - case 0x73657663 : //sevc - case 0x73716370 : //sqcp - case 0x73736D76 : //ssmv - case 0x64726D73 : { //drms - jump+= 36; - break; - } - case 0x74783367 : { //tx3g - jump+= 46; - break; - } - case 0x6D6A7032 : //mjp2 - case 0x6D703476 : //mp4v - case 0x61766331 : //avc1 - case 0x6A706567 : //jpeg - case 0x73323633 : //s263 - case 0x64726D69 : { //drmi - jump+= 86; - break; - } - default : { //anything else that isn't covered here will just jump past any child atoms (avcp, text, enc*) - jump += dataSize; - } - } - } - break; - } - case UNKNOWN_ATOM_TYPE : { - jump += dataSize; - break; - } - } //end swtich - - generalAtomicLevel = APar_GetCurrentAtomDepth(jump, dataSize); - - if ( (jump > 8 ? jump : 8) >= file_size) { //prevents jumping past EOF for the smallest of atoms - break; - } - - fseeko(file, jump, SEEK_SET); - } - - } else { - fprintf(stderr, "\nAtomicParsley error: bad mpeg4 file (ftyp atom missing or alignment error).\n\n"); - data = NULL; - exit(1); //return; - } - APar_SampleTableIterator(file); - - free(data); - data=NULL; - free(container); - container=NULL; - free(uuid_info.binary_uuid); - free(uuid_info.uuid_AP_atom_name); - fclose(file); - } - if (brand == 0x69736F6D) { //'isom' test for amc files & its (?always present?) uuid 0x63706764A88C11D48197009027087703 - char EZ_movie_uuid[100]; - memset(EZ_movie_uuid, 0, sizeof(EZ_movie_uuid)); - memcpy(EZ_movie_uuid, "uuid=\x63\x70\x67\x64\xA8\x8C\x11\xD4\x81\x97\x00\x90\x27\x08\x77\x03", 21); //this is in an endian form, so it needs to be converted - APar_endian_uuid_bin_str_conversion(EZ_movie_uuid+5); - if ( APar_FindAtom(EZ_movie_uuid, false, EXTENDED_ATOM, 0, true) != NULL) { - metadata_style = UNDEFINED_STYLE; - } - } - parsedfile = true; - } - if (!deep_atom_scan && !parsedfile && APar_FindAtom("moov", false, SIMPLE_ATOM, 0) == NULL) { - fprintf(stderr, "\nAtomicParsley error: bad mpeg4 file (no 'moov' atom).\n\n"); - exit(1); - } - return; + FILE *file = APar_OpenFile(path, "rb"); + if (file != NULL) { + char *data = (char *)calloc(1, 13); + char *container = (char *)calloc(1, 20); + memcpy(container, "FILE_LEVEL", 10); + bool corrupted_data_atom = false; + bool jpeg2000signature = false; + + uuid_vitals uuid_info = {0}; + uuid_info.binary_uuid = (char *)malloc( + sizeof(char) * 16 + 1); // this will hold any potential 16byte uuids + uuid_info.uuid_AP_atom_name = (char *)malloc( + sizeof(char) * 5); // this will hold any atom name that is written + // after the uuid written by AP + + if (data == NULL) + return; + uint64_t dataSize = 0; + uint64_t jump = 0; + + APar_readX(data, file, 0, 12); + char *atom = data + 4; + + if (memcmp(data, + "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A ", + 12) == 0) { + jpeg2000signature = true; + } + + if (memcmp(atom, "ftyp", 4) == 0 || jpeg2000signature) { + + dataSize = UInt32FromBigEndian(data); + jump = dataSize; + + APar_AtomizeFileInfo(0, + jump, + 0, + atom, + generalAtomicLevel, + CHILD_ATOM, + SIMPLE_ATOM, + 0, + 0, + &uuid_info); + + if (!jpeg2000signature) { + APar_IdentifyBrand(data + 8); + APar_TestCompatibleBrand(file, 0, dataSize); + } + + fseeko(file, jump, SEEK_SET); + + while (jump < file_size) { + + uuid_info.uuid_form = + UUID_DEPRECATED_FORM; // start with the assumption that any found + // atom is in the depracted uuid form + + APar_readX_noseek(data, file, 8); + char *atom = data + 4; + dataSize = UInt32FromBigEndian(data); + + if (jpeg2000signature) { + if (memcmp(atom, "ftyp", 4) == 0) { + APar_readX_noseek(twenty_byte_buffer, file, 4); + APar_IdentifyBrand(twenty_byte_buffer); + } else { + exit(0); // the atom right after the jpeg2000/mjpeg2000 signature + // is *supposed* to be 'ftyp' + } + jpeg2000signature = false; + } + + if (dataSize > file_size - jump) { + dataSize = file_size - jump; + } + + if (dataSize == 0 && + (atom[0] == 0 && atom[1] == 0 && atom[2] == 0 && atom[3] == 0)) { + gapless_void_padding = + file_size - jump; // Apple has decided to add around 2k of NULL + // space outside of any atom structure + // starting with iTunes 7.0.0 + break; // its possible this is part of gapless playback - but then + // why would it come after the 'free' at the end of a file + // like gpac writes? + } // after actual tested its elimination, it doesn't seem to be + // required for gapless playback + + // diagnose damage to 'cprt' by libmp4v2 in 1.4.1 & 1.5.0.1 + // typically, the length of this atom (dataSize) will exceeed it + // parent (which is reported as 17) true length ot this data will be 9 + // - impossible for iTunes-style 'data' atom. + if (memcmp(atom, "data", 4) == 0 && + parsedAtoms[atom_number - 1].AtomicContainerState == + PARENT_ATOM) { + if (dataSize > parsedAtoms[atom_number - 1].AtomicLength) { + dataSize = parsedAtoms[atom_number - 1].AtomicLength - + 8; // force its length to its true length + fprintf(stdout, + "AtomicParsley warning: the 'data' child of the '%s' " + "atom seems to be corrupted.\n", + parsedAtoms[atom_number - 1].AtomicName); + corrupted_data_atom = true; + } + } + // end diagnosis; APar_Manually_Determine_Parent will still determine + // it to be a versioned atom (it tests by names), but at file write + // out, it will write with a length of 9 bytes + + APar_Manually_Determine_Parent(jump, dataSize, container); + int filtered_known_atom = + APar_MatchToKnownAtom(atom, container, true, NULL); + + uint32_t atom_verflags = 0; + uint16_t atom_language = 0; + + if (memcmp(atom, "uuid", 4) == 0) { + memset(uuid_info.binary_uuid, 0, 17); + + APar_readX(uuid_info.binary_uuid, file, jump + 8, 16); + + if (UInt32FromBigEndian(uuid_info.binary_uuid + 8) == + 0) { // the deperacted uuid form + memcpy(atom, uuid_info.binary_uuid, 4); + atom_verflags = + APar_read32(uuid_info.binary_uuid, file, jump + 12); + if (atom_verflags > AtomFlags_Data_UInt) { + atom_verflags = 0; + } + } else { + uint8_t uuid_version = + APar_extract_uuid_version(NULL, uuid_info.binary_uuid); + APar_endian_uuid_bin_str_conversion(uuid_info.binary_uuid); + if (uuid_version == 5) { + uuid_info.uuid_form = UUID_SHA1_NAMESPACE; + // read in what AP would set the atom name to. The new uuid form + // is: + // 4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes + // name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, + // Xbytes data + APar_readX(uuid_info.uuid_AP_atom_name, file, jump + 24, 4); + + char uuid_of_foundname_in_AP_namesapce[20]; + APar_generate_uuid_from_atomname( + uuid_info.uuid_AP_atom_name, + uuid_of_foundname_in_AP_namesapce); + if (memcmp(uuid_info.binary_uuid, + uuid_of_foundname_in_AP_namesapce, + 16) == 0) { + uuid_info.uuid_form = + UUID_AP_SHA1_NAMESPACE; // our own uuid ver5 atoms in the + // AtomicParsley.sf.net namespace + atom_verflags = + APar_read32(twenty_byte_buffer, file, jump + 28); + } + } else { + uuid_info.uuid_form = UUID_OTHER; + } + } + } + + if (KnownAtoms[filtered_known_atom].box_type == VERSIONED_ATOM && + !corrupted_data_atom) { + atom_verflags = APar_read32(twenty_byte_buffer, file, jump + 8); + } + + if (KnownAtoms[filtered_known_atom].box_type == PACKED_LANG_ATOM) { + atom_verflags = APar_read32(twenty_byte_buffer, file, jump + 8); + + // the problem with storing the language is that the uint16_t 2 + // bytes that carry the actual language are in different places on + // different atoms some atoms have it right after the atom + // version/flags; some like rating/classification have it 8 bytes + // later; yrrc doesn't have it at all + char bitpacked_lang[4]; + memset(bitpacked_lang, 0, 4); + + uint32_t userdata_box = UInt32FromBigEndian(atom); + + switch (userdata_box) { + case 0x7469746C: //'titl' + case 0x64736370: //'dscp' + case 0x63707274: //'cprt' + case 0x70657266: //'perf' + case 0x61757468: //'auth' + case 0x676E7265: //'gnre' + case 0x616C626D: //'albm' + case 0x6B797764: //'kywd' + case 0x6C6F6369: //'loci' + case 0x49443332: //'ID32' ; technically not a 'user data box', but + // this only extracts the packed language (which + // ID32 does have) + { + atom_language = APar_read16(bitpacked_lang, file, jump + 12); + break; + } + case 0x636C7366: //'clsf' + { + atom_language = APar_read16(bitpacked_lang, file, jump + 18); + break; + } + case 0x72746E67: //'rtng' + { + atom_language = APar_read16(bitpacked_lang, file, jump + 20); + break; + } + // case 0x79727263 : //'yrrc' is the only 3gp tag that doesn't + // support multiple languages; won't even get here because != + // PACKED_LANG_ATOM + default: { + break; // which means that any new/unknown packed language atoms + // will have their language of 0; AP will only support 1 of + // this atom name then + } + } + } + + // mdat.length=1; and ONLY supported for mdat atoms - no idea if the + // spec says "only mdat", but that's what I'm doing for now + if ((strncmp(atom, "mdat", 4) == 0) && (generalAtomicLevel == 1) && + (dataSize == 1)) { + + uint64_t extended_dataSize = + APar_read64(twenty_byte_buffer, file, jump + 8); + APar_AtomizeFileInfo( + jump, + 1, + extended_dataSize, + atom, + generalAtomicLevel, + KnownAtoms[filtered_known_atom].container_state, + KnownAtoms[filtered_known_atom].box_type, + atom_verflags, + atom_language, + &uuid_info); + + } else { + APar_AtomizeFileInfo( + jump, + dataSize, + 0, + atom, + generalAtomicLevel, + KnownAtoms[filtered_known_atom].container_state, + corrupted_data_atom ? SIMPLE_ATOM + : KnownAtoms[filtered_known_atom].box_type, + atom_verflags, + atom_language, + &uuid_info); + } + corrupted_data_atom = false; + + // read in the name of an iTunes-style internal reverseDNS directly + // into parsedAtoms + if (memcmp(atom, "mean", 4) == 0 && + memcmp(parsedAtoms[atom_number - 2].AtomicName, "----", 4) == 0) { + parsedAtoms[atom_number - 1].ReverseDNSdomain = + (char *)calloc(1, sizeof(char) * dataSize); + + // jump + 12 because 'name' atom is the 2nd child + APar_readX(parsedAtoms[atom_number - 1].ReverseDNSdomain, + file, + jump + 12, + dataSize - 12); + } + if (memcmp(atom, "name", 4) == 0 && + memcmp(parsedAtoms[atom_number - 2].AtomicName, "mean", 4) == 0 && + memcmp(parsedAtoms[atom_number - 3].AtomicName, "----", 4) == 0) { + + parsedAtoms[atom_number - 1].ReverseDNSname = + (char *)calloc(1, sizeof(char) * dataSize); + + // jump + 12 because 'name' atom is the 2nd child + APar_readX(parsedAtoms[atom_number - 1].ReverseDNSname, + file, + jump + 12, + dataSize - 12); + } + + if (dataSize == 0) { // length = 0 means it reaches to EOF + break; + } + + switch (KnownAtoms[filtered_known_atom].container_state) { + case PARENT_ATOM: { + jump += 8; + break; + } + case CHILD_ATOM: { + if (memcmp(atom, "hdlr", 4) == 0) { + APar_readX(twenty_byte_buffer, file, jump + 16, 4); + parsedAtoms[atom_number - 1].ancillary_data = + UInt32FromBigEndian(twenty_byte_buffer); + } + + if ((generalAtomicLevel == 1) && + (dataSize == + 1)) { // mdat.length =1 64-bit length that is more of a cludge. + jump += parsedAtoms[atom_number - 1].AtomicLengthExtended; + } else { + jump += dataSize; + } + break; + } + case DUAL_STATE_ATOM: { + if (memcmp(atom, "meta", 4) == 0) { + jump += 12; + + } else if (memcmp(atom, "dref", 4) == 0) { + jump += 16; + + } else if (memcmp(atom, "iinf", 4) == 0) { + jump += 14; + + } else if (memcmp(atom, "stsd", 4) == 0) { + if (deepscan_REQ) { + // for a tree ONLY, we go all the way, parsing everything; for + // any other option, we leave this atom as a monolithic entity + jump += 16; + } else { + APar_Extract_stsd_codec( + file, jump + 16); // just get the codec used for this track + jump += dataSize; + } + + } else if (memcmp(atom, "schi", 4) == 0) { + if (memcmp(container, "sinf", 4) == + 0) { // seems for iTMS drm files, schi is a simple parent + // atom, and 'user' comes right after it + jump += 8; + } else { + jump += dataSize; // no idea what it would be under srpp, so + // just skip over it + } + + } else if (memcmp(container, "stsd", 4) == 0) { + // each one is different, so list its size manually + // the beauty of this is that even if there is an error here or a + // new codec shows up, it only affects SHOWING the tree. Getting a + // tree for display ONLY purposes is different from when setting a + // tag - display ONLY goes into stsd; tagging makes 'stsd' + // monolithic. so setting metadata on unknown or improperly + // enumerated codecs (which might have different lengths) don't + // affect tagging. + uint32_t named_atom = UInt32FromBigEndian(atom); + switch (named_atom) { + case 0x6D703473: { // mp4s + jump += 16; + break; + } + case 0x73727470: // srtp + case 0x72747020: { //'rtp ' + jump += 24; + break; + } + case 0x616C6163: // alac + case 0x6D703461: // mp4a + case 0x73616D72: // samr + case 0x73617762: // sawb + case 0x73617770: // sawp + case 0x73657663: // sevc + case 0x73716370: // sqcp + case 0x73736D76: // ssmv + case 0x64726D73: { // drms + jump += 36; + break; + } + case 0x74783367: { // tx3g + jump += 46; + break; + } + case 0x6D6A7032: // mjp2 + case 0x6D703476: // mp4v + case 0x61766331: // avc1 + case 0x6A706567: // jpeg + case 0x73323633: // s263 + case 0x64726D69: { // drmi + jump += 86; + break; + } + default: { // anything else that isn't covered here will just jump + // past any child atoms (avcp, text, enc*) + jump += dataSize; + } + } + } + break; + } + case UNKNOWN_ATOM_TYPE: { + jump += dataSize; + break; + } + } // end swtich + + generalAtomicLevel = APar_GetCurrentAtomDepth(jump, dataSize); + + if ((jump > 8 ? jump : 8) >= file_size) { // prevents jumping past EOF + // for the smallest of atoms + break; + } + + fseeko(file, jump, SEEK_SET); + } + + } else { + fprintf(stderr, + "\nAtomicParsley error: bad mpeg4 file (ftyp atom " + "missing or alignment error).\n\n"); + data = NULL; + exit(1); // return; + } + APar_SampleTableIterator(file); + + free(data); + data = NULL; + free(container); + container = NULL; + free(uuid_info.binary_uuid); + free(uuid_info.uuid_AP_atom_name); + fclose(file); + } + if (brand == + 0x69736F6D) { //'isom' test for amc files & its (?always present?) uuid + // 0x63706764A88C11D48197009027087703 + char EZ_movie_uuid[100]; + memset(EZ_movie_uuid, 0, sizeof(EZ_movie_uuid)); + memcpy(EZ_movie_uuid, + "uuid=" + "\x63\x70\x67\x64\xA8\x8C\x11\xD4\x81\x97\x00\x90\x27\x08\x77\x03", + 21); // this is in an endian form, so it needs to be converted + APar_endian_uuid_bin_str_conversion(EZ_movie_uuid + 5); + if (APar_FindAtom(EZ_movie_uuid, false, EXTENDED_ATOM, 0, true) != NULL) { + metadata_style = UNDEFINED_STYLE; + } + } + parsedfile = true; + } + if (!deep_atom_scan && !parsedfile && + APar_FindAtom("moov", false, SIMPLE_ATOM, 0) == NULL) { + fprintf(stderr, + "\nAtomicParsley error: bad mpeg4 file (no 'moov' atom).\n\n"); + exit(1); + } + return; } /////////////////////////////////////////////////////////////////////////////////////// -// mod time functions // +// mod time functions // /////////////////////////////////////////////////////////////////////////////////////// void APar_FlagMovieHeader() { - if (movie_header_atom == NULL) movie_header_atom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0); - if (movie_header_atom == NULL) return; - if (movie_header_atom != NULL) movie_header_atom->ancillary_data = 0x666C6167; - return; -} - -void APar_FlagTrackHeader(AtomicInfo* thisAtom) { - AtomicInfo* trak_atom = NULL; - AtomicInfo* track_header_atom = NULL; - short current_atom_idx = thisAtom->AtomicNumber; - short current_level = thisAtom->AtomicLevel; - - if (thisAtom->AtomicLevel >= 3) { - while (true) { - short parent_atom = APar_FindParentAtom(current_atom_idx, current_level); - current_atom_idx = parent_atom; - current_level = parsedAtoms[parent_atom].AtomicLevel; - - if (current_level == 2) { - if (memcmp(parsedAtoms[parent_atom].AtomicName, "trak", 4) == 0) { - trak_atom = &parsedAtoms[parent_atom]; - } - break; - } else if (current_level == 1) { - break; - } - } - if (trak_atom != NULL) { - track_header_atom = APar_FindChildAtom(trak_atom->AtomicNumber, "tkhd"); - if (track_header_atom != NULL) { - track_header_atom->ancillary_data = 0x666C6167; - } - } - } - APar_FlagMovieHeader(); - return; + if (movie_header_atom == NULL) + movie_header_atom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0); + if (movie_header_atom == NULL) + return; + if (movie_header_atom != NULL) + movie_header_atom->ancillary_data = 0x666C6167; + return; +} + +void APar_FlagTrackHeader(AtomicInfo *thisAtom) { + AtomicInfo *trak_atom = NULL; + AtomicInfo *track_header_atom = NULL; + short current_atom_idx = thisAtom->AtomicNumber; + short current_level = thisAtom->AtomicLevel; + + if (thisAtom->AtomicLevel >= 3) { + while (true) { + short parent_atom = APar_FindParentAtom(current_atom_idx, current_level); + current_atom_idx = parent_atom; + current_level = parsedAtoms[parent_atom].AtomicLevel; + + if (current_level == 2) { + if (memcmp(parsedAtoms[parent_atom].AtomicName, "trak", 4) == 0) { + trak_atom = &parsedAtoms[parent_atom]; + } + break; + } else if (current_level == 1) { + break; + } + } + if (trak_atom != NULL) { + track_header_atom = APar_FindChildAtom(trak_atom->AtomicNumber, "tkhd"); + if (track_header_atom != NULL) { + track_header_atom->ancillary_data = 0x666C6167; + } + } + } + APar_FlagMovieHeader(); + return; } /////////////////////////////////////////////////////////////////////////////////////// -// Atom Removal Functions // +// Atom Removal Functions // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_EliminateAtom this_atom_number - the index into parsedAtoms[] of the atom to be erased - resume_atom_number - the point in parsedAtoms[] where the tree will be picked up + resume_atom_number - the point in parsedAtoms[] where the tree will be +picked up - This manually removes the atoms from being used. The atom is still in parsedAtoms[] at the same location it was previously, but because parsedAtoms is used - as a linked list & followed by NextAtomNumber, effectively, this atom (and atoms leading to resume_atom_number) are no longer considered part of the tree. + This manually removes the atoms from being used. The atom is still in +parsedAtoms[] at the same location it was previously, but because parsedAtoms is +used as a linked list & followed by NextAtomNumber, effectively, this atom (and +atoms leading to resume_atom_number) are no longer considered part of the tree. ----------------------*/ void APar_EliminateAtom(short this_atom_number, int resume_atom_number) { - if ( this_atom_number > 0 && this_atom_number < atom_number && resume_atom_number >= 0 && resume_atom_number < atom_number) { - APar_FlagTrackHeader(&parsedAtoms[this_atom_number]); - - short preceding_atom_pos = APar_FindPrecedingAtom(this_atom_number); - if ( APar_Eval_ChunkOffsetImpact(this_atom_number) ) { - removed_bytes_tally+=parsedAtoms[this_atom_number].AtomicLength; //used in validation routine - } - parsedAtoms[preceding_atom_pos].NextAtomNumber = resume_atom_number; - - memset(parsedAtoms[this_atom_number].AtomicName, 0, 4); //blank out the name of the parent atom name - parsedAtoms[this_atom_number].AtomicNumber = -1; - parsedAtoms[this_atom_number].NextAtomNumber = -1; - } - return; + if (this_atom_number > 0 && this_atom_number < atom_number && + resume_atom_number >= 0 && resume_atom_number < atom_number) { + APar_FlagTrackHeader(&parsedAtoms[this_atom_number]); + + short preceding_atom_pos = APar_FindPrecedingAtom(this_atom_number); + if (APar_Eval_ChunkOffsetImpact(this_atom_number)) { + removed_bytes_tally += parsedAtoms[this_atom_number] + .AtomicLength; // used in validation routine + } + parsedAtoms[preceding_atom_pos].NextAtomNumber = resume_atom_number; + + memset(parsedAtoms[this_atom_number].AtomicName, + 0, + 4); // blank out the name of the parent atom name + parsedAtoms[this_atom_number].AtomicNumber = -1; + parsedAtoms[this_atom_number].NextAtomNumber = -1; + } + return; } /*---------------------- APar_RemoveAtom - atom_path - the "peri.od_d.elim.inat.ed__.atom.path" string that represents the target atom - atom_type - the type of atom to be eliminated (packed language, extended...) of the target atom - UD_lang - the language code for a packed language atom (ignored for non-packed language atoms) - rDNS_domain - the reverse DNS domain (com.foo.thing) of the atom (ignored for non reverse DNS '----' atoms) - - APar_RemoveAtom tries to find the atom in the string. If it exists, then depending on its atom_type, it or its last child will get passed along for elimination. - TODO: the last child part could use some more intelligence at some point; its relatively hardcoded. -----------------------*/ -void APar_RemoveAtom(const char* atom_path, uint8_t atom_type, uint16_t UD_lang, const char* rDNS_domain) { - - AtomicInfo* desiredAtom = APar_FindAtom(atom_path, false, atom_type, UD_lang, (atom_type == EXTENDED_ATOM ? true : false), rDNS_domain ); - - if (desiredAtom == NULL) return; //the atom didn't exist or wasn't found - if (desiredAtom->AtomicNumber == 0) return; //we got the default atom, ftyp - and since that can't be removed, it must not exist (or it was missed) - - modified_atoms = true; - if (atom_type != EXTENDED_ATOM) { - if (atom_type == PACKED_LANG_ATOM || desiredAtom->AtomicClassification == UNKNOWN_ATOM) { - APar_EliminateAtom(desiredAtom->AtomicNumber, desiredAtom->NextAtomNumber); - - //reverseDNS atom - } else if (desiredAtom->ReverseDNSname != NULL) { - short parent_atom = APar_FindParentAtom(desiredAtom->AtomicNumber, desiredAtom->AtomicLevel); - short last_elim_atom = APar_FindLastChild_of_ParentAtom(parent_atom); - APar_EliminateAtom( parent_atom, parsedAtoms[last_elim_atom].NextAtomNumber ); - - } else if (memcmp(desiredAtom->AtomicName, "data", 4) == 0 && desiredAtom->AtomicLevel == 6){ - short parent_atom = APar_FindParentAtom(desiredAtom->AtomicNumber, desiredAtom->AtomicLevel); - short last_elim_atom = APar_FindLastChild_of_ParentAtom(parent_atom); - APar_EliminateAtom( parent_atom, parsedAtoms[last_elim_atom].NextAtomNumber ); - - } else if (desiredAtom->AtomicContainerState <= DUAL_STATE_ATOM) { - short last_elim_atom = APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber); - APar_EliminateAtom( desiredAtom->AtomicNumber, parsedAtoms[last_elim_atom].NextAtomNumber ); - - } else if (UD_lang == 1) { //yrrc - APar_EliminateAtom(desiredAtom->AtomicNumber, desiredAtom->NextAtomNumber); - - } else { - short last_elim_atom = APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber); - APar_EliminateAtom(desiredAtom->AtomicNumber, last_elim_atom ); - } - - //this will only work for AtomicParsley created uuid atoms that don't have children, but since all uuid atoms are treaded as non-parent atoms... no problems - } else if (atom_type == EXTENDED_ATOM) { - APar_EliminateAtom(desiredAtom->AtomicNumber, desiredAtom->NextAtomNumber); - } - return; + atom_path - the "peri.od_d.elim.inat.ed__.atom.path" string that represents +the target atom atom_type - the type of atom to be eliminated (packed language, +extended...) of the target atom UD_lang - the language code for a packed +language atom (ignored for non-packed language atoms) rDNS_domain - the reverse +DNS domain (com.foo.thing) of the atom (ignored for non reverse DNS '----' +atoms) + + APar_RemoveAtom tries to find the atom in the string. If it exists, then +depending on its atom_type, it or its last child will get passed along for +elimination. + TODO: the last child part could use some more intelligence at +some point; its relatively hardcoded. +----------------------*/ +void APar_RemoveAtom(const char *atom_path, + uint8_t atom_type, + uint16_t UD_lang, + const char *rDNS_domain) { + + AtomicInfo *desiredAtom = + APar_FindAtom(atom_path, + false, + atom_type, + UD_lang, + (atom_type == EXTENDED_ATOM ? true : false), + rDNS_domain); + + if (desiredAtom == NULL) + return; // the atom didn't exist or wasn't found + if (desiredAtom->AtomicNumber == 0) + return; // we got the default atom, ftyp - and since that can't be removed, + // it must not exist (or it was missed) + + modified_atoms = true; + if (atom_type != EXTENDED_ATOM) { + if (atom_type == PACKED_LANG_ATOM || + desiredAtom->AtomicClassification == UNKNOWN_ATOM) { + APar_EliminateAtom(desiredAtom->AtomicNumber, + desiredAtom->NextAtomNumber); + + // reverseDNS atom + } else if (desiredAtom->ReverseDNSname != NULL) { + short parent_atom = APar_FindParentAtom(desiredAtom->AtomicNumber, + desiredAtom->AtomicLevel); + short last_elim_atom = APar_FindLastChild_of_ParentAtom(parent_atom); + APar_EliminateAtom(parent_atom, + parsedAtoms[last_elim_atom].NextAtomNumber); + + } else if (memcmp(desiredAtom->AtomicName, "data", 4) == 0 && + desiredAtom->AtomicLevel == 6) { + short parent_atom = APar_FindParentAtom(desiredAtom->AtomicNumber, + desiredAtom->AtomicLevel); + short last_elim_atom = APar_FindLastChild_of_ParentAtom(parent_atom); + APar_EliminateAtom(parent_atom, + parsedAtoms[last_elim_atom].NextAtomNumber); + + } else if (desiredAtom->AtomicContainerState <= DUAL_STATE_ATOM) { + short last_elim_atom = + APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber); + APar_EliminateAtom(desiredAtom->AtomicNumber, + parsedAtoms[last_elim_atom].NextAtomNumber); + + } else if (UD_lang == 1) { // yrrc + APar_EliminateAtom(desiredAtom->AtomicNumber, + desiredAtom->NextAtomNumber); + + } else { + short last_elim_atom = + APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber); + APar_EliminateAtom(desiredAtom->AtomicNumber, last_elim_atom); + } + + // this will only work for AtomicParsley created uuid atoms that don't have + // children, but since all uuid atoms are treaded as non-parent atoms... no + // problems + } else if (atom_type == EXTENDED_ATOM) { + APar_EliminateAtom(desiredAtom->AtomicNumber, desiredAtom->NextAtomNumber); + } + return; } /*---------------------- APar_freefree - purge_level - an integer ranging from -1 to some positive number of the level a 'free' atom must be on for it to be erased. + purge_level - an integer ranging from -1 to some positive number of the level +a 'free' atom must be on for it to be erased. - Some tagging utilities (things based on libmp4v2 & faac irrespective of which tagging system used) have a dirty little secret. They way the work is to copy the - 'moov' atom - in its entirety - to the end of the file, and make the changes there. The original 'moov' file is nulled out, but the file only increases in size. - Even if you eliminate the tag, the file grows. Only when the 'moov' atom is last do these taggers work efficiently - and they are blazingly fast, no doubt about - that - but they are incredibly wasteful. It is possible to switch between using AP and mp4tags and build a file with dozens of megabytes wasted just be changing - a single letter. - This function can be used to iterate through the atoms in the file, and selectively eliminate 'free' atoms. Pass a -1 and every last 'free' atom that exists will - be eliminated (but another will appear later as padding - to completely eliminate any resulting 'free' atoms, the environmental variable "AP_PADDING" needs to be - set with MID_PAD to 0). Pass a 0, and all 'free' atoms preceding 'moov' or after 'mdat' (including gpac's pesky "Place Your Ad Here" free-at-the-end) will be - removed. A value of >= 1 will eliminate 'free' atoms between those levels and level 1 (or file level). + Some tagging utilities (things based on libmp4v2 & faac irrespective of +which tagging system used) have a dirty little secret. They way the work is to +copy the 'moov' atom - in its entirety - to the end of the file, and make the +changes there. The original 'moov' file is nulled out, but the file only +increases in size. Even if you eliminate the tag, the file grows. Only when the +'moov' atom is last do these taggers work efficiently - and they are blazingly +fast, no doubt about that - but they are incredibly wasteful. It is possible to +switch between using AP and mp4tags and build a file with dozens of megabytes +wasted just be changing a single letter. This function can be used to iterate +through the atoms in the file, and selectively eliminate 'free' atoms. Pass a -1 +and every last 'free' atom that exists will be eliminated (but another will +appear later as padding - to completely eliminate any resulting 'free' atoms, +the environmental variable "AP_PADDING" needs to be set with MID_PAD to 0). Pass +a 0, and all 'free' atoms preceding 'moov' or after 'mdat' (including gpac's +pesky "Place Your Ad Here" free-at-the-end) will be removed. A value of >= 1 +will eliminate 'free' atoms between those levels and level 1 (or file level). ----------------------*/ void APar_freefree(int purge_level) { - modified_atoms = true; - short eval_atom = 0; - short prev_atom = 0; - short moov_atom = 0; //a moov atom has yet to be seen - short mdat_atom = 0; //any ol' mdat - - if (purge_level == -1) { - complete_free_space_erasure = true; //prevent any in situ dynamic updating when trying to remove all free atoms. Also triggers a more efficient means of forcing padding - } - - while (true) { - prev_atom = eval_atom; - eval_atom = parsedAtoms[eval_atom].NextAtomNumber; - if (eval_atom == 0) { //we've hit the last atom - break; - } - - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0 ) { - //fprintf(stdout, "i am of size %" PRIu64 " purge level %i (%u) -> %i\n", parsedAtoms[eval_atom].AtomicLength, purge_level, parsedAtoms[eval_atom].AtomicLevel, eval_atom); - if ( purge_level == -1 || purge_level >= parsedAtoms[eval_atom].AtomicLevel || - (purge_level == 0 && parsedAtoms[eval_atom].AtomicLevel == 1 && (moov_atom == 0 || mdat_atom != 0)) ) { - short prev_atom = APar_FindPrecedingAtom(eval_atom); - if (parsedAtoms[eval_atom].NextAtomNumber == 0) { //we've hit the last atom - APar_EliminateAtom(eval_atom, parsedAtoms[eval_atom].NextAtomNumber); - parsedAtoms[prev_atom].NextAtomNumber = 0; - } else { - APar_EliminateAtom(eval_atom, parsedAtoms[eval_atom].NextAtomNumber); - } - eval_atom = prev_atom; //go back to the previous atom and continue the search - } - } - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "moov", 4) == 0 ) { - moov_atom = eval_atom; - } - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0 ) { - mdat_atom = eval_atom; - } - } - return; + modified_atoms = true; + short eval_atom = 0; + short prev_atom = 0; + short moov_atom = 0; // a moov atom has yet to be seen + short mdat_atom = 0; // any ol' mdat + + if (purge_level == -1) { + complete_free_space_erasure = + true; // prevent any in situ dynamic updating when trying to remove all + // free atoms. Also triggers a more efficient means of forcing + // padding + } + + while (true) { + prev_atom = eval_atom; + eval_atom = parsedAtoms[eval_atom].NextAtomNumber; + if (eval_atom == 0) { // we've hit the last atom + break; + } + + if (memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || + memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) { + // fprintf(stdout, "i am of size %" PRIu64 " purge level %i (%u) -> %i\n", + // parsedAtoms[eval_atom].AtomicLength, purge_level, + // parsedAtoms[eval_atom].AtomicLevel, eval_atom); + if (purge_level == -1 || + purge_level >= parsedAtoms[eval_atom].AtomicLevel || + (purge_level == 0 && parsedAtoms[eval_atom].AtomicLevel == 1 && + (moov_atom == 0 || mdat_atom != 0))) { + short prev_atom = APar_FindPrecedingAtom(eval_atom); + if (parsedAtoms[eval_atom].NextAtomNumber == + 0) { // we've hit the last atom + APar_EliminateAtom(eval_atom, parsedAtoms[eval_atom].NextAtomNumber); + parsedAtoms[prev_atom].NextAtomNumber = 0; + } else { + APar_EliminateAtom(eval_atom, parsedAtoms[eval_atom].NextAtomNumber); + } + eval_atom = + prev_atom; // go back to the previous atom and continue the search + } + } + if (memcmp(parsedAtoms[eval_atom].AtomicName, "moov", 4) == 0) { + moov_atom = eval_atom; + } + if (memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0) { + mdat_atom = eval_atom; + } + } + return; } /////////////////////////////////////////////////////////////////////////////////////// -// Atom Moving Functions // +// Atom Moving Functions // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_MoveAtom this_atom_number - the atom that will follow the new_position atom - new_position - the atom that the atoms at that level will precede, followed by this_atom_number (including its hierarchy of child atoms) + new_position - the atom that the atoms at that level will precede, followed by +this_atom_number (including its hierarchy of child atoms) - first find which atoms lead to both atoms (precedingAtom flows to this_atom_number, lastStationaryAtom flows to new_position). Depending on whether there - are children, find the last atom in the atoms that will be moving. Shunt as required; reordering occurs by following NextAtomNumber (linked list) + first find which atoms lead to both atoms (precedingAtom flows to +this_atom_number, lastStationaryAtom flows to new_position). Depending on +whether there are children, find the last atom in the atoms that will be moving. +Shunt as required; reordering occurs by following NextAtomNumber (linked list) ----------------------*/ void APar_MoveAtom(short this_atom_number, short new_position) { - short precedingAtom = 0; - short lastStationaryAtom = 0; - short iter = 0; - - //look for the preceding atom (either directly before of the same level, or moov's last nth level child - while (parsedAtoms[iter].NextAtomNumber != 0) { - if (parsedAtoms[iter].NextAtomNumber == this_atom_number) { - precedingAtom = iter; - break; - } else { - if (parsedAtoms[iter].NextAtomNumber == 0) { //we found the last atom (which we end our search on) - break; - } - } - iter=parsedAtoms[iter].NextAtomNumber; - } - - iter = 0; - - //search where to insert our new atom - while (parsedAtoms[iter].NextAtomNumber != 0) { - if (parsedAtoms[iter].NextAtomNumber == new_position) { - lastStationaryAtom = iter; - break; - } - iter=parsedAtoms[iter].NextAtomNumber; - if (parsedAtoms[iter].NextAtomNumber == 0) { //we found the last atom - lastStationaryAtom = iter; - break; - } - - } - //fprintf(stdout, "%s preceded by %s, last would be %s\n", parsedAtoms[this_atom_number].AtomicName, parsedAtoms[precedingAtom].AtomicName, parsedAtoms[lastStationaryAtom].AtomicName); - - if (parsedAtoms[this_atom_number].AtomicContainerState <= DUAL_STATE_ATOM) { - if (parsedAtoms[new_position].AtomicContainerState <= DUAL_STATE_ATOM) { - short last_SwapChild = APar_FindLastChild_of_ParentAtom(this_atom_number); - short last_WiredChild = APar_FindLastChild_of_ParentAtom(new_position); - //fprintf(stdout, "moving %s, last child atom %s\n", parsedAtoms[this_atom_number].AtomicName, parsedAtoms[last_SwapChild].AtomicName); - //fprintf(stdout, "wired %s, last child atom %s\n", parsedAtoms[new_position].AtomicName, parsedAtoms[last_WiredChild].AtomicName); - //fprintf(stdout, "stationary atom %s , preceding atom %s\n", parsedAtoms[lastStationaryAtom].AtomicName, parsedAtoms[precedingAtom].AtomicName); - - short swap_resume = parsedAtoms[last_SwapChild].NextAtomNumber; - short wired_resume = parsedAtoms[last_WiredChild].NextAtomNumber; - - parsedAtoms[precedingAtom].NextAtomNumber = swap_resume; //shunt the main tree (over the [this_atom_number] atom to be move) to other tween atoms, - parsedAtoms[lastStationaryAtom].NextAtomNumber = new_position; //pick up with the 2nd to last hierarchy - parsedAtoms[last_WiredChild].NextAtomNumber = this_atom_number; //and route the 2nd to last hierarchy to wrap around to the this_atom_number atom - parsedAtoms[last_SwapChild].NextAtomNumber = wired_resume; //and continue with whatever was after the [new_position] atom - - - } else { - short last_child = APar_FindLastChild_of_ParentAtom(this_atom_number); - parsedAtoms[lastStationaryAtom].NextAtomNumber = this_atom_number; - parsedAtoms[precedingAtom].NextAtomNumber = parsedAtoms[last_child].NextAtomNumber; - parsedAtoms[last_child].NextAtomNumber = new_position; - } - - } else { - parsedAtoms[lastStationaryAtom].NextAtomNumber = this_atom_number; - parsedAtoms[precedingAtom].NextAtomNumber = parsedAtoms[this_atom_number].NextAtomNumber; - parsedAtoms[this_atom_number].NextAtomNumber = new_position; - } + short precedingAtom = 0; + short lastStationaryAtom = 0; + short iter = 0; + + // look for the preceding atom (either directly before of the same level, or + // moov's last nth level child + while (parsedAtoms[iter].NextAtomNumber != 0) { + if (parsedAtoms[iter].NextAtomNumber == this_atom_number) { + precedingAtom = iter; + break; + } else { + if (parsedAtoms[iter].NextAtomNumber == + 0) { // we found the last atom (which we end our search on) + break; + } + } + iter = parsedAtoms[iter].NextAtomNumber; + } + + iter = 0; + + // search where to insert our new atom + while (parsedAtoms[iter].NextAtomNumber != 0) { + if (parsedAtoms[iter].NextAtomNumber == new_position) { + lastStationaryAtom = iter; + break; + } + iter = parsedAtoms[iter].NextAtomNumber; + if (parsedAtoms[iter].NextAtomNumber == 0) { // we found the last atom + lastStationaryAtom = iter; + break; + } + } + // fprintf(stdout, "%s preceded by %s, last would be %s\n", + // parsedAtoms[this_atom_number].AtomicName, + // parsedAtoms[precedingAtom].AtomicName, + // parsedAtoms[lastStationaryAtom].AtomicName); + + if (parsedAtoms[this_atom_number].AtomicContainerState <= DUAL_STATE_ATOM) { + if (parsedAtoms[new_position].AtomicContainerState <= DUAL_STATE_ATOM) { + short last_SwapChild = APar_FindLastChild_of_ParentAtom(this_atom_number); + short last_WiredChild = APar_FindLastChild_of_ParentAtom(new_position); + // fprintf(stdout, "moving %s, last child atom %s\n", + // parsedAtoms[this_atom_number].AtomicName, + // parsedAtoms[last_SwapChild].AtomicName); fprintf(stdout, "wired %s, + // last child atom %s\n", parsedAtoms[new_position].AtomicName, + // parsedAtoms[last_WiredChild].AtomicName); fprintf(stdout, "stationary + // atom %s , preceding atom %s\n", + // parsedAtoms[lastStationaryAtom].AtomicName, + // parsedAtoms[precedingAtom].AtomicName); + + short swap_resume = parsedAtoms[last_SwapChild].NextAtomNumber; + short wired_resume = parsedAtoms[last_WiredChild].NextAtomNumber; + + parsedAtoms[precedingAtom].NextAtomNumber = + swap_resume; // shunt the main tree (over the [this_atom_number] atom + // to be move) to other tween atoms, + parsedAtoms[lastStationaryAtom].NextAtomNumber = + new_position; // pick up with the 2nd to last hierarchy + parsedAtoms[last_WiredChild].NextAtomNumber = + this_atom_number; // and route the 2nd to last hierarchy to wrap + // around to the this_atom_number atom + parsedAtoms[last_SwapChild].NextAtomNumber = + wired_resume; // and continue with whatever was after the + // [new_position] atom + + } else { + short last_child = APar_FindLastChild_of_ParentAtom(this_atom_number); + parsedAtoms[lastStationaryAtom].NextAtomNumber = this_atom_number; + parsedAtoms[precedingAtom].NextAtomNumber = + parsedAtoms[last_child].NextAtomNumber; + parsedAtoms[last_child].NextAtomNumber = new_position; + } + + } else { + parsedAtoms[lastStationaryAtom].NextAtomNumber = this_atom_number; + parsedAtoms[precedingAtom].NextAtomNumber = + parsedAtoms[this_atom_number].NextAtomNumber; + parsedAtoms[this_atom_number].NextAtomNumber = new_position; + } - return; + return; } /////////////////////////////////////////////////////////////////////////////////////// -// Atom Creation Functions // +// Atom Creation Functions // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_InterjectNewAtom atom_name - the 4 character name of the atom - cntr_state - the type of container it will be (child, parent, dual state) - atom_class - the atom type it will be (simple, versioned, uuid, versioned with packed language) - atom_length - the forced length of this atom (undefined beyond intrinsic length of the container type except for 'free' atoms) - atom_verflags - the 1 byte atom version & 3 bytes atom flags for the atom if versioned - packed_lang - the 2 byte packed language for the atom if versioned with packed language type - atom_level - the level of this atom (1 denotes file level, anything else denotes a child of the last preceding parent or dual state atom) - preceding_atom - the atom that precedes this newly created interjected atom - - Creates a single new atom (carrying NULLed data) inserted after preceding_atom -----------------------*/ -short APar_InterjectNewAtom(const char* atom_name, uint8_t cntr_state, - uint8_t atom_class, uint64_t atom_length, - uint32_t atom_verflags, uint16_t packed_lang, uint8_t atom_level, - short preceding_atom) -{ - - if (deep_atom_scan && !modified_atoms) { - return 0; - } - - if (atom_number >= MAX_ATOMS) { - fprintf(stderr, "too many atoms\n"); - abort(); - } - - AtomicInfo* new_atom = &parsedAtoms[atom_number]; - new_atom->AtomicNumber = atom_number; - new_atom->AtomicName = (char*)malloc(sizeof(char)*6); - memset(new_atom->AtomicName, 0, sizeof(char)*6); - memcpy(new_atom->AtomicName, atom_name, 4); - - new_atom->AtomicContainerState = cntr_state; - new_atom->AtomicClassification = atom_class; - new_atom->AtomicVerFlags = atom_verflags; - new_atom->AtomicLevel = atom_level; - new_atom->AtomicLength = atom_length; - new_atom->AtomicLanguage = packed_lang; - - new_atom->AtomicData = (char*)calloc(1, sizeof(char)* (atom_length > 16 ? atom_length : 16) ); //puts a hard limit on the length of strings (the spec doesn't) + cntr_state - the type of container it will be (child, parent, dual +state) atom_class - the atom type it will be (simple, versioned, uuid, versioned +with packed language) atom_length - the forced length of this atom (undefined +beyond intrinsic length of the container type except for 'free' atoms) + atom_verflags - the 1 byte atom version & 3 bytes atom flags for the +atom if versioned packed_lang - the 2 byte packed language for the atom if +versioned with packed language type atom_level - the level of this atom (1 +denotes file level, anything else denotes a child of the last preceding parent +or dual state atom) preceding_atom - the atom that precedes this newly created +interjected atom + + Creates a single new atom (carrying NULLed data) inserted after +preceding_atom +----------------------*/ +short APar_InterjectNewAtom(const char *atom_name, + uint8_t cntr_state, + uint8_t atom_class, + uint64_t atom_length, + uint32_t atom_verflags, + uint16_t packed_lang, + uint8_t atom_level, + short preceding_atom) { + + if (deep_atom_scan && !modified_atoms) { + return 0; + } + + if (atom_number >= MAX_ATOMS) { + fprintf(stderr, "too many atoms\n"); + abort(); + } + + AtomicInfo *new_atom = &parsedAtoms[atom_number]; + new_atom->AtomicNumber = atom_number; + new_atom->AtomicName = (char *)malloc(sizeof(char) * 6); + memset(new_atom->AtomicName, 0, sizeof(char) * 6); + memcpy(new_atom->AtomicName, atom_name, 4); + + new_atom->AtomicContainerState = cntr_state; + new_atom->AtomicClassification = atom_class; + new_atom->AtomicVerFlags = atom_verflags; + new_atom->AtomicLevel = atom_level; + new_atom->AtomicLength = atom_length; + new_atom->AtomicLanguage = packed_lang; + + new_atom->AtomicData = (char *)calloc( + 1, + sizeof(char) * (atom_length > 16 + ? atom_length + : 16)); // puts a hard limit on the length of + // strings (the spec doesn't) - new_atom->ID32_TagInfo = NULL; + new_atom->ID32_TagInfo = NULL; - new_atom->NextAtomNumber = parsedAtoms[ preceding_atom ].NextAtomNumber; - parsedAtoms[ preceding_atom ].NextAtomNumber = atom_number; + new_atom->NextAtomNumber = parsedAtoms[preceding_atom].NextAtomNumber; + parsedAtoms[preceding_atom].NextAtomNumber = atom_number; - atom_number++; - return new_atom->AtomicNumber; + atom_number++; + return new_atom->AtomicNumber; } /*---------------------- APar_CreateSparseAtom - surrogate_atom - an skeletal template of the atom to be created; currently name, level, lang, (if uuid/extended: container & class) are copied; other - stats should be filled in routines that called for their creation and know things like flags & if is to carry an data (this doesn't malloc) - parent_atom - the stats for the parent atom (used to match through the KnownAtoms array and get things like container & class (for most atoms) - preceding_atom - the new atom will follow this atom - - Create a single new atom (not carrying any data) copied from a template to follow preceding_atom -----------------------*/ -AtomicInfo* APar_CreateSparseAtom(AtomicInfo* surrogate_atom, AtomicInfo* parent_atom, short preceding_atom) { - - if (atom_number >= MAX_ATOMS) { - fprintf(stderr, "too many atoms\n"); - abort(); - } - AtomicInfo* new_atom = &parsedAtoms[atom_number]; - new_atom->AtomicNumber = atom_number; - new_atom->AtomicStart = 0; - int known_atom = 0; - - new_atom->AtomicName = (char*)malloc(sizeof(char)*20); - memset(new_atom->AtomicName, 0, sizeof(char)*20); - size_t copy_bytes_len = (surrogate_atom->AtomicClassification == EXTENDED_ATOM ? 16 : 4); - memcpy(new_atom->AtomicName, surrogate_atom->AtomicName, copy_bytes_len); - new_atom->AtomicLevel = surrogate_atom->AtomicLevel; - new_atom->AtomicLanguage = surrogate_atom->AtomicLanguage; - - //this is almost assuredly wrong for everything except a simple parent atom & needs to be handled properly afterwards; Note the use of 'Sparse' - new_atom->AtomicVerFlags = 0; - new_atom->AtomicLength = 8; - - new_atom->NextAtomNumber = parsedAtoms[ preceding_atom ].NextAtomNumber; - parsedAtoms[ preceding_atom ].NextAtomNumber = atom_number; - - //if 'uuid' atom, copy the info directly, otherwise use KnownAtoms to get the info - if (surrogate_atom->AtomicClassification == EXTENDED_ATOM) { - new_atom->AtomicContainerState = CHILD_ATOM; - new_atom->AtomicClassification = surrogate_atom->AtomicClassification; - - new_atom->uuid_style = UUID_AP_SHA1_NAMESPACE; - - } else { - //determine the type of atom from our array of KnownAtoms; this is a worst case scenario; it should be handled properly afterwards - //make sure the level & the atom gets integrated into NextAtomNumber before APar_MatchToKnownAtom because getting the fullpath will rely on that - known_atom = APar_MatchToKnownAtom(surrogate_atom->AtomicName, (parent_atom == NULL ? "FILE_LEVEL" : parent_atom->AtomicName), false, NULL); - - new_atom->AtomicContainerState = KnownAtoms[known_atom].container_state; - new_atom->AtomicClassification = KnownAtoms[known_atom].box_type; - } - new_atom->ID32_TagInfo = NULL; + surrogate_atom - an skeletal template of the atom to be created; +currently name, level, lang, (if uuid/extended: container & class) are copied; +other stats should be filled in routines that called for their creation and know +things like flags & if is to carry an data (this doesn't malloc) parent_atom - +the stats for the parent atom (used to match through the KnownAtoms array and +get things like container & class (for most atoms) preceding_atom - the new atom +will follow this atom + + Create a single new atom (not carrying any data) copied from a template to +follow preceding_atom +----------------------*/ +AtomicInfo *APar_CreateSparseAtom(AtomicInfo *surrogate_atom, + AtomicInfo *parent_atom, + short preceding_atom) { + + if (atom_number >= MAX_ATOMS) { + fprintf(stderr, "too many atoms\n"); + abort(); + } + AtomicInfo *new_atom = &parsedAtoms[atom_number]; + new_atom->AtomicNumber = atom_number; + new_atom->AtomicStart = 0; + int known_atom = 0; + + new_atom->AtomicName = (char *)malloc(sizeof(char) * 20); + memset(new_atom->AtomicName, 0, sizeof(char) * 20); + size_t copy_bytes_len = + (surrogate_atom->AtomicClassification == EXTENDED_ATOM ? 16 : 4); + memcpy(new_atom->AtomicName, surrogate_atom->AtomicName, copy_bytes_len); + new_atom->AtomicLevel = surrogate_atom->AtomicLevel; + new_atom->AtomicLanguage = surrogate_atom->AtomicLanguage; + + // this is almost assuredly wrong for everything except a simple parent atom & + // needs to be handled properly afterwards; Note the use of 'Sparse' + new_atom->AtomicVerFlags = 0; + new_atom->AtomicLength = 8; + + new_atom->NextAtomNumber = parsedAtoms[preceding_atom].NextAtomNumber; + parsedAtoms[preceding_atom].NextAtomNumber = atom_number; + + // if 'uuid' atom, copy the info directly, otherwise use KnownAtoms to get the + // info + if (surrogate_atom->AtomicClassification == EXTENDED_ATOM) { + new_atom->AtomicContainerState = CHILD_ATOM; + new_atom->AtomicClassification = surrogate_atom->AtomicClassification; + + new_atom->uuid_style = UUID_AP_SHA1_NAMESPACE; + + } else { + // determine the type of atom from our array of KnownAtoms; this is a worst + // case scenario; it should be handled properly afterwards make sure the + // level & the atom gets integrated into NextAtomNumber before + // APar_MatchToKnownAtom because getting the fullpath will rely on that + known_atom = APar_MatchToKnownAtom( + surrogate_atom->AtomicName, + (parent_atom == NULL ? "FILE_LEVEL" : parent_atom->AtomicName), + false, + NULL); + + new_atom->AtomicContainerState = KnownAtoms[known_atom].container_state; + new_atom->AtomicClassification = KnownAtoms[known_atom].box_type; + } + new_atom->ID32_TagInfo = NULL; - atom_number++; + atom_number++; - return new_atom; + return new_atom; } /*---------------------- APar_Unified_atom_Put target_atom - pointer to the structure describing the atom we are setting - unicode_data - a pointer to a string (possibly utf8 already); may go onto conversion to utf16 prior to the put - text_tag_style - flag to denote that unicode_data is to become utf-16, or stay the flavors of utf8 (iTunes style, 3gp style...) - ancillary_data - a (possibly cast) 32-bit number of any type of supplemental data to be set - anc_bit_width - controls the number of bytes to set for ancillary data [0 to skip, 8 (1byte) - 32 (4 bytes)] - - take any variety of data & tack it onto the malloced AtomicData at the next available spot (determined by its length) - priority is given to the numerical ancillary_data so that language can be set prior to setting whatever unicode data. Finally, advance - the length of the atom so that we can tack onto the end repeated times (up to the max malloced amount - which isn't checked [blush]) - if unicode_data is NULL itself, then only ancillary_data will be set - which is endian safe cuz o' bitshifting (or set 1 byte at a time) - - works on iTunes-style & 3GP asset style but NOT binary safe (use APar_atom_Binary_Put) - TODO: work past the max malloced amount onto a new larger array -----------------------*/ -void APar_Unified_atom_Put(AtomicInfo* target_atom, const char* unicode_data, - uint8_t text_tag_style, uint64_t ancillary_data, uint8_t anc_bit_width) -{ - uint64_t atom_data_pos = 0; - if (target_atom == NULL) { - return; - } - if (target_atom->AtomicClassification == EXTENDED_ATOM) { - if (target_atom->uuid_style == UUID_SHA1_NAMESPACE) atom_data_pos = target_atom->AtomicLength - 32; - else if (target_atom->uuid_style == UUID_OTHER) atom_data_pos = target_atom->AtomicLength - 24; - } else { - atom_data_pos = target_atom->AtomicLength - 12; - } - - switch (anc_bit_width) { - case 0 : { //aye, 'twas a false alarm; arg (I'm a pirate), we just wanted to set a text string - break; - } - - case 8 : { //compilation, podcast flag, advisory - target_atom->AtomicData[atom_data_pos] = (uint8_t)ancillary_data; - target_atom->AtomicLength++; - atom_data_pos++; - break; - } - - case 16 : { //lang & its ilk - target_atom->AtomicData[atom_data_pos] = (ancillary_data & 0xff00) >> 8; - target_atom->AtomicData[atom_data_pos + 1] = (ancillary_data & 0xff) << 0; - target_atom->AtomicLength+= 2; - atom_data_pos+=2; - break; - } - - case 32 : { //things like coordinates and.... stuff (ah, the prose) - target_atom->AtomicData[atom_data_pos] = (ancillary_data & 0xff000000) >> 24; - target_atom->AtomicData[atom_data_pos + 1] = (ancillary_data & 0xff0000) >> 16; - target_atom->AtomicData[atom_data_pos + 2] = (ancillary_data & 0xff00) >> 8; - target_atom->AtomicData[atom_data_pos + 3] = (ancillary_data & 0xff) << 0; - target_atom->AtomicLength+= 4; - atom_data_pos+=4; - break; - } - - default : { - break; - } - } - - if (unicode_data != NULL) { - if (text_tag_style == UTF16_3GP_Style) { - uint32_t string_length = strlen(unicode_data) + 1; - uint32_t glyphs_req_bytes = mbstowcs(NULL, unicode_data, string_length) * 2; //passing NULL pre-calculates the size of wchar_t needed; - - unsigned char* utf16_conversion = (unsigned char*)calloc(1, sizeof(unsigned char)* string_length * 2 ); - - UTF8ToUTF16BE(utf16_conversion, glyphs_req_bytes, (unsigned char*)unicode_data, string_length); - - target_atom->AtomicData[atom_data_pos] = (char)(0xFE); //BOM - target_atom->AtomicData[atom_data_pos+1] = (char)(0xFF); //BOM - atom_data_pos +=2; //BOM - - /* copy the string directly onto AtomicData at the address of the start of AtomicData + the current length in atom_data_pos */ - /* in marked contrast to iTunes-style metadata where a string is a single string, 3gp tags like keyword & classification are more complex */ - /* directly putting the text into memory and being able to tack on more becomes a necessary accommodation */ - memcpy(target_atom->AtomicData + atom_data_pos, utf16_conversion, glyphs_req_bytes ); - target_atom->AtomicLength += glyphs_req_bytes; - - //double check terminating NULL (don't want to double add them - blush.... or have them missing - blushing on the.... other side) - if (target_atom->AtomicData[atom_data_pos + (glyphs_req_bytes -1)] + target_atom->AtomicData[atom_data_pos + glyphs_req_bytes] != 0) { - target_atom->AtomicLength += 4; //+4 because add 2 bytes for the character we just found + 2bytes for the req. NULL - } - free(utf16_conversion); utf16_conversion = NULL; - - } else if (text_tag_style == UTF8_iTunesStyle_Binary) { //because this will be 'binary' data (a misnomer for purl & egid), memcpy 4 bytes into AtomicData, not at the start of it - uint32_t binary_bytes = strlen(unicode_data); - memcpy(target_atom->AtomicData + atom_data_pos, unicode_data, binary_bytes + 1 ); - target_atom->AtomicLength += binary_bytes; - - } else { - uint32_t total_bytes = 0; - - if (text_tag_style == UTF8_3GP_Style) { - total_bytes = strlen(unicode_data); - total_bytes++; //include the terminating NULL - - } else if (text_tag_style == UTF8_iTunesStyle_256glyphLimited) { - - uint32_t raw_bytes = strlen(unicode_data); - total_bytes = utf8_length(unicode_data, 256); //counts the number of characters, not bytes - - if (raw_bytes > total_bytes && total_bytes > 255) { - - fprintf(stdout, "AtomicParsley warning: %s was trimmed to 255 characters (%u characters over)\n", - parsedAtoms[ APar_FindParentAtom(target_atom->AtomicNumber, target_atom->AtomicLevel) ].AtomicName, - utf8_length(unicode_data+total_bytes, 0) ); - } else { - total_bytes = raw_bytes; - } - - } else if (text_tag_style == UTF8_iTunesStyle_Unlimited) { - total_bytes = strlen(unicode_data); - - if (atom_data_pos + total_bytes > MAXDATA_PAYLOAD) { - target_atom->AtomicData = (char*)realloc(target_atom->AtomicData, sizeof(char)* (atom_data_pos +total_bytes +1) ); - memset(target_atom->AtomicData + atom_data_pos, 0, total_bytes +1); - - } - } - - //if we are setting iTunes-style metadata, add 0 to the pointer; for 3gp user data atoms - add in the (length-default bare atom lenth): account for language uint16_t (plus any other crap we will set); unicodeWin32 with wchar_t was converted right after program started, so do a direct copy - - memcpy(target_atom->AtomicData + atom_data_pos, unicode_data, total_bytes + 1 ); - target_atom->AtomicLength += total_bytes; - } - } - return; + unicode_data - a pointer to a string (possibly utf8 already); may go onto +conversion to utf16 prior to the put text_tag_style - flag to denote that +unicode_data is to become utf-16, or stay the flavors of utf8 (iTunes style, 3gp +style...) ancillary_data - a (possibly cast) 32-bit number of any type of +supplemental data to be set anc_bit_width - controls the number of bytes to set +for ancillary data [0 to skip, 8 (1byte) - 32 (4 bytes)] + + take any variety of data & tack it onto the malloced AtomicData at the next +available spot (determined by its length) priority is given to the numerical +ancillary_data so that language can be set prior to setting whatever unicode +data. Finally, advance the length of the atom so that we can tack onto the end +repeated times (up to the max malloced amount - which isn't checked [blush]) if +unicode_data is NULL itself, then only ancillary_data will be set - which is +endian safe cuz o' bitshifting (or set 1 byte at a time) + + works on iTunes-style & 3GP asset style but NOT binary safe (use +APar_atom_Binary_Put) + TODO: work past the max malloced amount onto a new larger array +----------------------*/ +void APar_Unified_atom_Put(AtomicInfo *target_atom, + const char *unicode_data, + uint8_t text_tag_style, + uint64_t ancillary_data, + uint8_t anc_bit_width) { + uint64_t atom_data_pos = 0; + if (target_atom == NULL) { + return; + } + if (target_atom->AtomicClassification == EXTENDED_ATOM) { + if (target_atom->uuid_style == UUID_SHA1_NAMESPACE) + atom_data_pos = target_atom->AtomicLength - 32; + else if (target_atom->uuid_style == UUID_OTHER) + atom_data_pos = target_atom->AtomicLength - 24; + } else { + atom_data_pos = target_atom->AtomicLength - 12; + } + + switch (anc_bit_width) { + case 0: { // aye, 'twas a false alarm; arg (I'm a pirate), we just wanted to + // set a text string + break; + } + + case 8: { // compilation, podcast flag, advisory + target_atom->AtomicData[atom_data_pos] = (uint8_t)ancillary_data; + target_atom->AtomicLength++; + atom_data_pos++; + break; + } + + case 16: { // lang & its ilk + target_atom->AtomicData[atom_data_pos] = (ancillary_data & 0xff00) >> 8; + target_atom->AtomicData[atom_data_pos + 1] = (ancillary_data & 0xff) << 0; + target_atom->AtomicLength += 2; + atom_data_pos += 2; + break; + } + + case 32: { // things like coordinates and.... stuff (ah, the prose) + target_atom->AtomicData[atom_data_pos] = + (ancillary_data & 0xff000000) >> 24; + target_atom->AtomicData[atom_data_pos + 1] = + (ancillary_data & 0xff0000) >> 16; + target_atom->AtomicData[atom_data_pos + 2] = (ancillary_data & 0xff00) >> 8; + target_atom->AtomicData[atom_data_pos + 3] = (ancillary_data & 0xff) << 0; + target_atom->AtomicLength += 4; + atom_data_pos += 4; + break; + } + + default: { + break; + } + } + + if (unicode_data != NULL) { + if (text_tag_style == UTF16_3GP_Style) { + uint32_t string_length = strlen(unicode_data) + 1; + uint32_t glyphs_req_bytes = + mbstowcs(NULL, unicode_data, string_length) * + 2; // passing NULL pre-calculates the size of wchar_t needed; + + unsigned char *utf16_conversion = + (unsigned char *)calloc(1, sizeof(unsigned char) * string_length * 2); + + UTF8ToUTF16BE(utf16_conversion, + glyphs_req_bytes, + (unsigned char *)unicode_data, + string_length); + + target_atom->AtomicData[atom_data_pos] = (char)(0xFE); // BOM + target_atom->AtomicData[atom_data_pos + 1] = (char)(0xFF); // BOM + atom_data_pos += 2; // BOM + + /* copy the string directly onto AtomicData at the address of the start of + * AtomicData + the current length in atom_data_pos */ + /* in marked contrast to iTunes-style metadata where a string is a single + * string, 3gp tags like keyword & classification are more complex */ + /* directly putting the text into memory and being able to tack on more + * becomes a necessary accommodation */ + memcpy(target_atom->AtomicData + atom_data_pos, + utf16_conversion, + glyphs_req_bytes); + target_atom->AtomicLength += glyphs_req_bytes; + + // double check terminating NULL (don't want to double add them - + // blush.... or have them missing - blushing on the.... other side) + if (target_atom->AtomicData[atom_data_pos + (glyphs_req_bytes - 1)] + + target_atom->AtomicData[atom_data_pos + glyphs_req_bytes] != + 0) { + target_atom->AtomicLength += + 4; //+4 because add 2 bytes for the character we just found + 2bytes + // for the req. NULL + } + free(utf16_conversion); + utf16_conversion = NULL; + + } else if (text_tag_style == + UTF8_iTunesStyle_Binary) { // because this will be 'binary' data + // (a misnomer for purl & egid), + // memcpy 4 bytes into AtomicData, not + // at the start of it + uint32_t binary_bytes = strlen(unicode_data); + memcpy(target_atom->AtomicData + atom_data_pos, + unicode_data, + binary_bytes + 1); + target_atom->AtomicLength += binary_bytes; + + } else { + uint32_t total_bytes = 0; + + if (text_tag_style == UTF8_3GP_Style) { + total_bytes = strlen(unicode_data); + total_bytes++; // include the terminating NULL + + } else if (text_tag_style == UTF8_iTunesStyle_256glyphLimited) { + + uint32_t raw_bytes = strlen(unicode_data); + total_bytes = utf8_length( + unicode_data, 256); // counts the number of characters, not bytes + + if (raw_bytes > total_bytes && total_bytes > 255) { + + fprintf(stdout, + "AtomicParsley warning: %s was trimmed to 255 characters (%u " + "characters over)\n", + parsedAtoms[APar_FindParentAtom(target_atom->AtomicNumber, + target_atom->AtomicLevel)] + .AtomicName, + utf8_length(unicode_data + total_bytes, 0)); + } else { + total_bytes = raw_bytes; + } + + } else if (text_tag_style == UTF8_iTunesStyle_Unlimited) { + total_bytes = strlen(unicode_data); + + if (atom_data_pos + total_bytes > MAXDATA_PAYLOAD) { + target_atom->AtomicData = + (char *)realloc(target_atom->AtomicData, + sizeof(char) * (atom_data_pos + total_bytes + 1)); + memset(target_atom->AtomicData + atom_data_pos, 0, total_bytes + 1); + } + } + + // if we are setting iTunes-style metadata, add 0 to the pointer; for 3gp + // user data atoms - add in the (length-default bare atom lenth): account + // for language uint16_t (plus any other crap we will set); unicodeWin32 + // with wchar_t was converted right after program started, so do a direct + // copy + + memcpy(target_atom->AtomicData + atom_data_pos, + unicode_data, + total_bytes + 1); + target_atom->AtomicLength += total_bytes; + } + } + return; } /*---------------------- @@ -2105,1859 +2662,2504 @@ target_atom - pointer to the structure describing the atom we are setting binary_data - a pointer to a string of binary data bytecount - number of bytes to copy - atomic_data_offset - place binary data some bytes offset from the start of AtomicData + atomic_data_offset - place binary data some bytes offset from the start of +AtomicData Simple placement of binary data (perhaps containing NULLs) onto AtomicData. - TODO: if over MAXDATA_PAYLOAD malloc a new char string + TODO: if over MAXDATA_PAYLOAD malloc a new char string ----------------------*/ -void APar_atom_Binary_Put(AtomicInfo* target_atom, const char* binary_data, - uint32_t bytecount, uint64_t atomic_data_offset) -{ - if (target_atom == NULL) return; - - if (atomic_data_offset + bytecount + target_atom->AtomicLength <= MAXDATA_PAYLOAD) { - memcpy(target_atom->AtomicData + atomic_data_offset, binary_data, bytecount ); - target_atom->AtomicLength += bytecount; - } else { - fprintf(stdout, "AtomicParsley warning: some data was longer than the allotted space and was skipped\n"); - } - return; +void APar_atom_Binary_Put(AtomicInfo *target_atom, + const char *binary_data, + uint32_t bytecount, + uint64_t atomic_data_offset) { + if (target_atom == NULL) + return; + + if (atomic_data_offset + bytecount + target_atom->AtomicLength <= + MAXDATA_PAYLOAD) { + memcpy( + target_atom->AtomicData + atomic_data_offset, binary_data, bytecount); + target_atom->AtomicLength += bytecount; + } else { + fprintf(stdout, + "AtomicParsley warning: some data was longer than the " + "allotted space and was skipped\n"); + } + return; } /*---------------------- APar_Verify__udta_meta_hdlr__atom - only test if the atom is present for now, it will be created just before writeout time - to insure it only happens once. + only test if the atom is present for now, it will be created just before +writeout time - to insure it only happens once. ----------------------*/ void APar_Verify__udta_meta_hdlr__atom() { - bool Create__udta_meta_hdlr__atom = false; + bool Create__udta_meta_hdlr__atom = false; - if (metadata_style == ITUNES_STYLE && hdlrAtom == NULL) { - hdlrAtom = APar_FindAtom("moov.udta.meta.hdlr", false, VERSIONED_ATOM, 0); - if (hdlrAtom == NULL) { - Create__udta_meta_hdlr__atom = true; - } - } - if (Create__udta_meta_hdlr__atom ) { - - //if Quicktime (Player at the least) is used to create any type of mp4 file, the entire udta hierarchy is missing. If iTunes doesn't find - //this "moov.udta.meta.hdlr" atom (and its data), it refuses to let any information be changed & the dreaded "Album Artwork Not Modifiable" - //shows up. It's because this atom is missing. Oddly, QT Player can see the info, but this only works for mp4/m4a files. - - hdlrAtom = APar_FindAtom("moov.udta.meta.hdlr", true, VERSIONED_ATOM, 0); - - APar_MetaData_atom_QuickInit(hdlrAtom->AtomicNumber, 0, 0); - APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0x6D646972, 32); //'mdir' - APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0x6170706C, 32); //'appl' - APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); - APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); - APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); - } - return; + if (metadata_style == ITUNES_STYLE && hdlrAtom == NULL) { + hdlrAtom = APar_FindAtom("moov.udta.meta.hdlr", false, VERSIONED_ATOM, 0); + if (hdlrAtom == NULL) { + Create__udta_meta_hdlr__atom = true; + } + } + if (Create__udta_meta_hdlr__atom) { + + // if Quicktime (Player at the least) is used to create any type of mp4 + // file, the entire udta hierarchy is missing. If iTunes doesn't find this + // "moov.udta.meta.hdlr" atom (and its data), it refuses to let any + // information be changed & the dreaded "Album Artwork Not Modifiable" shows + // up. It's because this atom is missing. Oddly, QT Player can see the info, + // but this only works for mp4/m4a files. + + hdlrAtom = APar_FindAtom("moov.udta.meta.hdlr", true, VERSIONED_ATOM, 0); + + APar_MetaData_atom_QuickInit(hdlrAtom->AtomicNumber, 0, 0); + APar_Unified_atom_Put(hdlrAtom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + 0x6D646972, + 32); //'mdir' + APar_Unified_atom_Put(hdlrAtom, + NULL, + UTF8_iTunesStyle_256glyphLimited, + 0x6170706C, + 32); //'appl' + APar_Unified_atom_Put( + hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); + APar_Unified_atom_Put( + hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); + APar_Unified_atom_Put( + hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); + } + return; } /*---------------------- APar_MetaData_atomGenre_Set - atomPayload - the desired string value of the genre + atomPayload - the desired string value of the genre - genre is special in that it gets carried on 2 atoms. A standard genre (as listed in ID3v1GenreList) is represented as a number on a 'gnre' atom - any value other than those, and the genre is placed as a string onto a 'gen' atom. Only one or the other can be present. So if atomPayload is a - non-NULL value, first try and match the genre into the ID3v1GenreList standard genres. Try to remove the other type of genre atom, then find or - create the new genre atom and put the data manually onto the atom. -----------------------*/ -void APar_MetaData_atomGenre_Set(const char* atomPayload) { - if (metadata_style == ITUNES_STYLE) { - const char* standard_genre_atom = "moov.udta.meta.ilst.gnre"; - const char* std_genre_data_atom = "moov.udta.meta.ilst.gnre.data"; - const char* custom_genre_atom = "moov.udta.meta.ilst.gen"; - const char* cstm_genre_data_atom = "moov.udta.meta.ilst.gen.data"; - - if ( strlen(atomPayload) == 0) { - APar_RemoveAtom(std_genre_data_atom, VERSIONED_ATOM, 0); //find the atom; don't create if it's "" to remove - APar_RemoveAtom(cstm_genre_data_atom, VERSIONED_ATOM, 0); //find the atom; don't create if it's "" to remove - } else { - - uint8_t genre_number = StringGenreToInt(atomPayload); - AtomicInfo* genreAtom; - - APar_Verify__udta_meta_hdlr__atom(); - modified_atoms = true; - - if (genre_number != 0) { - //first find if a custom genre atom ("gen") exists; erase the custom-string genre atom in favor of the standard genre atom - - AtomicInfo* verboten_genre_atom = APar_FindAtom(custom_genre_atom, false, SIMPLE_ATOM, 0); - - if (verboten_genre_atom != NULL) { - if (strlen(verboten_genre_atom->AtomicName) > 0) { - if (strncmp(verboten_genre_atom->AtomicName, "gen", 4) == 0) { - APar_RemoveAtom(cstm_genre_data_atom, VERSIONED_ATOM, 0); - } - } - } - - genreAtom = APar_FindAtom(std_genre_data_atom, true, VERSIONED_ATOM, 0); - APar_MetaData_atom_QuickInit(genreAtom->AtomicNumber, AtomFlags_Data_Binary, 0); - APar_Unified_atom_Put(genreAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 8); - APar_Unified_atom_Put(genreAtom, NULL, UTF8_iTunesStyle_256glyphLimited, genre_number, 8); - - } else { - - AtomicInfo* verboten_genre_atom = APar_FindAtom(standard_genre_atom, false, SIMPLE_ATOM, 0); - - if (verboten_genre_atom != NULL) { - if (verboten_genre_atom->AtomicNumber > 5 && verboten_genre_atom->AtomicNumber < atom_number) { - if (strncmp(verboten_genre_atom->AtomicName, "gnre", 4) == 0) { - APar_RemoveAtom(std_genre_data_atom, VERSIONED_ATOM, 0); - } - } - } - genreAtom = APar_FindAtom(cstm_genre_data_atom, true, VERSIONED_ATOM, 0); - APar_MetaData_atom_QuickInit(genreAtom->AtomicNumber, AtomFlags_Data_Text, 0); - APar_Unified_atom_Put(genreAtom, atomPayload, UTF8_iTunesStyle_256glyphLimited, 0, 0); - } - } - APar_FlagMovieHeader(); - } //end if (metadata_style == ITUNES_STYLE) - return; + genre is special in that it gets carried on 2 atoms. A standard genre (as +listed in ID3v1GenreList) is represented as a number on a 'gnre' atom any value +other than those, and the genre is placed as a string onto a 'gen' atom. Only +one or the other can be present. So if atomPayload is a non-NULL value, first +try and match the genre into the ID3v1GenreList standard genres. Try to remove +the other type of genre atom, then find or create the new genre atom and put the +data manually onto the atom. +----------------------*/ +void APar_MetaData_atomGenre_Set(const char *atomPayload) { + if (metadata_style == ITUNES_STYLE) { + const char *standard_genre_atom = "moov.udta.meta.ilst.gnre"; + const char *std_genre_data_atom = "moov.udta.meta.ilst.gnre.data"; + const char *custom_genre_atom = "moov.udta.meta.ilst.gen"; + const char *cstm_genre_data_atom = "moov.udta.meta.ilst.gen.data"; + + if (strlen(atomPayload) == 0) { + APar_RemoveAtom(std_genre_data_atom, + VERSIONED_ATOM, + 0); // find the atom; don't create if it's "" to remove + APar_RemoveAtom(cstm_genre_data_atom, + VERSIONED_ATOM, + 0); // find the atom; don't create if it's "" to remove + } else { + + uint8_t genre_number = StringGenreToInt(atomPayload); + AtomicInfo *genreAtom; + + APar_Verify__udta_meta_hdlr__atom(); + modified_atoms = true; + + if (genre_number != 0) { + // first find if a custom genre atom ("gen") exists; erase the + // custom-string genre atom in favor of the standard genre atom + + AtomicInfo *verboten_genre_atom = + APar_FindAtom(custom_genre_atom, false, SIMPLE_ATOM, 0); + + if (verboten_genre_atom != NULL) { + if (strlen(verboten_genre_atom->AtomicName) > 0) { + if (strncmp(verboten_genre_atom->AtomicName, "gen", 4) == 0) { + APar_RemoveAtom(cstm_genre_data_atom, VERSIONED_ATOM, 0); + } + } + } + + genreAtom = APar_FindAtom(std_genre_data_atom, true, VERSIONED_ATOM, 0); + APar_MetaData_atom_QuickInit( + genreAtom->AtomicNumber, AtomFlags_Data_Binary, 0); + APar_Unified_atom_Put( + genreAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 8); + APar_Unified_atom_Put( + genreAtom, NULL, UTF8_iTunesStyle_256glyphLimited, genre_number, 8); + + } else { + + AtomicInfo *verboten_genre_atom = + APar_FindAtom(standard_genre_atom, false, SIMPLE_ATOM, 0); + + if (verboten_genre_atom != NULL) { + if (verboten_genre_atom->AtomicNumber > 5 && + verboten_genre_atom->AtomicNumber < atom_number) { + if (strncmp(verboten_genre_atom->AtomicName, "gnre", 4) == 0) { + APar_RemoveAtom(std_genre_data_atom, VERSIONED_ATOM, 0); + } + } + } + genreAtom = + APar_FindAtom(cstm_genre_data_atom, true, VERSIONED_ATOM, 0); + APar_MetaData_atom_QuickInit( + genreAtom->AtomicNumber, AtomFlags_Data_Text, 0); + APar_Unified_atom_Put( + genreAtom, atomPayload, UTF8_iTunesStyle_256glyphLimited, 0, 0); + } + } + APar_FlagMovieHeader(); + } // end if (metadata_style == ITUNES_STYLE) + return; } /*---------------------- APar_MetaData_atomLyrics_Set - lyricsPath - the path that was provided to a (hopefully) existent txt file + lyricsPath - the path that was provided to a (hopefully) existent txt +file - lyrics will be read from a file because they can contain multiple lines. Lines are stored with old Mac-style line endings (single carriage return). + lyrics will be read from a file because they can contain multiple lines. +Lines are stored with old Mac-style line endings (single carriage return). ----------------------*/ -void APar_MetaData_atomLyrics_Set(const char* lyricsPath) { - if (metadata_style == ITUNES_STYLE) { - TestFileExistence(lyricsPath, true); - uint64_t file_len = findFileSize(lyricsPath); - - APar_Verify__udta_meta_hdlr__atom(); - modified_atoms = true; - - AtomicInfo* lyricsData_atom = APar_FindAtom("moov.udta.meta.ilst.lyr.data", true, VERSIONED_ATOM, 0); - APar_MetaData_atom_QuickInit(lyricsData_atom->AtomicNumber, AtomFlags_Data_Text, 0, file_len + 1); - - FILE* lyrics_file = APar_OpenFile(lyricsPath, "rb"); - uint64_t remaining = file_len; - char* dest = lyricsData_atom->AtomicData + 4; - char* sol; - while (remaining && (sol = fgets(dest, remaining + 1, lyrics_file))) { - size_t len = strlen(sol); //NUL bytes in the file will cause parts of the text to be skipped - //normalize different EOL styles to carriage returns - if (sol[len-1] == '\n') { - if (sol[len-2] == '\r') sol[--len] = '\0'; - else sol[len-1] = '\r'; - } - remaining -= len; - dest += len; - } - lyricsData_atom->AtomicLength += dest - (lyricsData_atom->AtomicData + 4); - fclose(lyrics_file); - - APar_FlagMovieHeader(); - } //end if (metadata_style == ITUNES_STYLE) - return; +void APar_MetaData_atomLyrics_Set(const char *lyricsPath) { + if (metadata_style == ITUNES_STYLE) { + TestFileExistence(lyricsPath, true); + uint64_t file_len = findFileSize(lyricsPath); + + APar_Verify__udta_meta_hdlr__atom(); + modified_atoms = true; + + AtomicInfo *lyricsData_atom = + APar_FindAtom("moov.udta.meta.ilst.lyr.data", true, VERSIONED_ATOM, 0); + APar_MetaData_atom_QuickInit( + lyricsData_atom->AtomicNumber, AtomFlags_Data_Text, 0, file_len + 1); + + FILE *lyrics_file = APar_OpenFile(lyricsPath, "rb"); + uint64_t remaining = file_len; + char *dest = lyricsData_atom->AtomicData + 4; + char *sol; + while (remaining && (sol = fgets(dest, remaining + 1, lyrics_file))) { + size_t len = strlen(sol); // NUL bytes in the file will cause parts of the + // text to be skipped + // normalize different EOL styles to carriage returns + if (sol[len - 1] == '\n') { + if (sol[len - 2] == '\r') + sol[--len] = '\0'; + else + sol[len - 1] = '\r'; + } + remaining -= len; + dest += len; + } + lyricsData_atom->AtomicLength += dest - (lyricsData_atom->AtomicData + 4); + fclose(lyrics_file); + + APar_FlagMovieHeader(); + } // end if (metadata_style == ITUNES_STYLE) + return; } /*---------------------- APar_MetaData_atomArtwork_Init - atom_num - the AtomicNumber of the atom in the parsedAtoms array (probably newly created) - artworkPath - the path that was provided on a (hopefully) existant jpg/png file - - artwork will be inited differently because we need to test a) that the file exists and b) get its size in bytes. This info will be used at the size - of the 'data' atom under 'covr' - and the path will be carried on AtomicData until write-out time, when the binary contents of the original will be - copied onto the atom. -----------------------*/ -void APar_MetaData_atomArtwork_Init(short atom_num, const char* artworkPath) { - TestFileExistence(artworkPath, true); - uint64_t picture_size = findFileSize(artworkPath); - - if (picture_size > 0) { - APar_MetaData_atom_QuickInit(atom_num, APar_TestArtworkBinaryData(artworkPath), 0, picture_size ); - FILE* artfile = APar_OpenFile(artworkPath, "rb"); - uint32_t bytes_read = APar_ReadFile(parsedAtoms[atom_num].AtomicData + 4, artfile, picture_size); //+4 for the 4 null bytes - if (bytes_read > 0) parsedAtoms[atom_num].AtomicLength += bytes_read; - fclose(artfile); - } - return; + atom_num - the AtomicNumber of the atom in the parsedAtoms array +(probably newly created) artworkPath - the path that was provided on a +(hopefully) existant jpg/png file + + artwork will be inited differently because we need to test a) that the file +exists and b) get its size in bytes. This info will be used at the size of the +'data' atom under 'covr' - and the path will be carried on AtomicData until +write-out time, when the binary contents of the original will be copied onto the +atom. +----------------------*/ +void APar_MetaData_atomArtwork_Init(short atom_num, const char *artworkPath) { + TestFileExistence(artworkPath, true); + uint64_t picture_size = findFileSize(artworkPath); + + if (picture_size > 0) { + APar_MetaData_atom_QuickInit( + atom_num, APar_TestArtworkBinaryData(artworkPath), 0, picture_size); + FILE *artfile = APar_OpenFile(artworkPath, "rb"); + uint32_t bytes_read = APar_ReadFile(parsedAtoms[atom_num].AtomicData + 4, + artfile, + picture_size); //+4 for the 4 null bytes + if (bytes_read > 0) + parsedAtoms[atom_num].AtomicLength += bytes_read; + fclose(artfile); + } + return; } /*---------------------- APar_MetaData_atomArtwork_Set - artworkPath - the path that was provided on a (hopefully) existant jpg/png file - env_PicOptions - picture embedding preferences from a 'export PIC_OPTIONS=foo' setting - - artwork gets stored under a single 'covr' atom, but with many 'data' atoms - each 'data' atom contains the binary data for each picture. - When the 'covr' atom is found, we create a sparse atom at the end of the existing 'data' atoms, and then perform any of the image manipulation - features on the image. The path of the file (either original, modified artwork, or both) are returned to use for possible atom creation -----------------------*/ -void APar_MetaData_atomArtwork_Set(const char* artworkPath, char* env_PicOptions) { - if (metadata_style == ITUNES_STYLE) { - const char* artwork_atom = "moov.udta.meta.ilst.covr"; - if (strcmp(artworkPath, "REMOVE_ALL") == 0) { - APar_RemoveAtom(artwork_atom, SIMPLE_ATOM, 0); - - } else { - APar_Verify__udta_meta_hdlr__atom(); - - modified_atoms = true; - AtomicInfo* desiredAtom = APar_FindAtom(artwork_atom, true, SIMPLE_ATOM, 0); - AtomicInfo sample_data_atom = { 0 }; - -#if defined (DARWIN_PLATFORM) - // used on Darwin adding a 2nd image (the original) - short parent_atom = desiredAtom->AtomicNumber; + artworkPath - the path that was provided on a (hopefully) existant +jpg/png file env_PicOptions - picture embedding preferences from a 'export +PIC_OPTIONS=foo' setting + + artwork gets stored under a single 'covr' atom, but with many 'data' atoms - +each 'data' atom contains the binary data for each picture. When the 'covr' atom +is found, we create a sparse atom at the end of the existing 'data' atoms, and +then perform any of the image manipulation features on the image. The path of +the file (either original, modified artwork, or both) are returned to use for +possible atom creation +----------------------*/ +void APar_MetaData_atomArtwork_Set(const char *artworkPath, + char *env_PicOptions) { + if (metadata_style == ITUNES_STYLE) { + const char *artwork_atom = "moov.udta.meta.ilst.covr"; + if (strcmp(artworkPath, "REMOVE_ALL") == 0) { + APar_RemoveAtom(artwork_atom, SIMPLE_ATOM, 0); + + } else { + APar_Verify__udta_meta_hdlr__atom(); + + modified_atoms = true; + AtomicInfo *desiredAtom = + APar_FindAtom(artwork_atom, true, SIMPLE_ATOM, 0); + AtomicInfo sample_data_atom = {0}; + +#if defined(__APPLE__) + // used on Darwin adding a 2nd image (the original) + short parent_atom = desiredAtom->AtomicNumber; #endif - APar_CreateSurrogateAtom(&sample_data_atom, "data", 6, VERSIONED_ATOM, 0, NULL, 0); - desiredAtom = APar_CreateSparseAtom(&sample_data_atom, desiredAtom, APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber) ); - -#if defined (DARWIN_PLATFORM) - //determine if any picture preferences will impact the picture file in any way - myPicturePrefs = APar_ExtractPicPrefs(env_PicOptions); - - char* resized_filepath = (char*)calloc(1, sizeof(char)*MAXPATHLEN+1); - - if ( ResizeGivenImage(artworkPath , myPicturePrefs, resized_filepath) ) { - APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, resized_filepath); - - if (myPicturePrefs.addBOTHpix) { - //create another sparse atom to hold the new image data - desiredAtom = APar_CreateSparseAtom(&sample_data_atom, desiredAtom, APar_FindLastChild_of_ParentAtom(parent_atom) ); - APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath); - if (myPicturePrefs.removeTempPix) remove(resized_filepath); - } - } else { - APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath); - } - free(resized_filepath); - resized_filepath=NULL; + APar_CreateSurrogateAtom( + &sample_data_atom, "data", 6, VERSIONED_ATOM, 0, NULL, 0); + desiredAtom = APar_CreateSparseAtom( + &sample_data_atom, + desiredAtom, + APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber)); + +#if defined(__APPLE__) + // determine if any picture preferences will impact the picture file in + // any way + myPicturePrefs = APar_ExtractPicPrefs(env_PicOptions); + + size_t resized_filepath_len = MAXPATHLEN + 1; + char *resized_filepath = (char *)calloc(1, resized_filepath_len); + + if (ResizeGivenImage(artworkPath, + myPicturePrefs, + resized_filepath, + resized_filepath_len)) { + APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, + resized_filepath); + + if (myPicturePrefs.addBOTHpix) { + // create another sparse atom to hold the new image data + desiredAtom = APar_CreateSparseAtom( + &sample_data_atom, + desiredAtom, + APar_FindLastChild_of_ParentAtom(parent_atom)); + APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, + artworkPath); + if (myPicturePrefs.removeTempPix) + remove(resized_filepath); + } + } else { + APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath); + } + free(resized_filepath); + resized_filepath = NULL; #else - //perhaps some libjpeg based resizing/modification for non-Mac OS X based platforms - APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath); + // perhaps some libjpeg based resizing/modification for non-Mac OS X based + // platforms + APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath); #endif - } - APar_FlagMovieHeader(); - } ////end if (metadata_style == ITUNES_STYLE) - return; + } + APar_FlagMovieHeader(); + } ////end if (metadata_style == ITUNES_STYLE) + return; } /*---------------------- APar_sprintf_atompath - dest_path - the destination string that will hold the final path - target_atom_name - the name of the atom that will be used to fill the %s portion of the tokenized path - track_index - the index into the trak[X] that needs to be found; fills the %u portion of the tokenized path - udta_container - the area in which 'udta' will be: either movie level (the easiest) or track level (which requires an index into a specific 'trak' atom) - dest_len - the amount of malloc'ed bytes for dest_path - - Fill a destinaiton path with the fully expressed atom path taking track indexes into consideration -----------------------*/ -void APar_sprintf_atompath(char* dest_path, const char* target_atom_name, uint8_t track_index, uint8_t udta_container, uint16_t dest_len) { - memset(dest_path, 0, dest_len); - if (udta_container == MOVIE_LEVEL_ATOM) { - memcpy(dest_path, "moov.udta.", 10); - memcpy(dest_path +10, target_atom_name, 4); - } else { - sprintf(dest_path, "moov.trak[%u].udta.%s", track_index, target_atom_name); - } - return; + dest_path - the destination string that will hold the final path + target_atom_name - the name of the atom that will be used to fill the %s +portion of the tokenized path track_index - the index into the trak[X] that +needs to be found; fills the %u portion of the tokenized path udta_container - +the area in which 'udta' will be: either movie level (the easiest) or track +level (which requires an index into a specific 'trak' atom) dest_len - the +amount of malloc'ed bytes for dest_path + + Fill a destinaiton path with the fully expressed atom path taking track +indexes into consideration +----------------------*/ +void APar_sprintf_atompath(char *dest_path, + const char *target_atom_name, + uint8_t track_index, + uint8_t udta_container, + uint16_t dest_len) { + memset(dest_path, 0, dest_len); + if (udta_container == MOVIE_LEVEL_ATOM) { + memcpy(dest_path, "moov.udta.", 10); + memcpy(dest_path + 10, target_atom_name, 4); + } else { + sprintf(dest_path, "moov.trak[%u].udta.%s", track_index, target_atom_name); + } + return; } /*---------------------- APar_3GP_Keyword_atom_Format - keywords_globbed - the globbed string of keywords ('foo1,foo2,foo_you') - keyword_count - count of keywords in the above globbed string - set_UTF16_text - whether to encode as utf16 - formed_keyword_struct - the char string that will hold the converted keyword struct (manually formatted) - - 3gp keywords are a little more complicated. Since they will be entered separated by some form of punctuation, they need to be separated out - They also will possibly be converted to utf16 - and they NEED to start with the BOM then. Prefacing each keyword is the 8bit length of the string - And each keyword needs to be NULL terminated. Technically it would be possible to even have mixed encodings (not supported here). -----------------------*/ -uint32_t APar_3GP_Keyword_atom_Format(char* keywords_globbed, uint8_t keyword_count, bool set_UTF16_text, char* &formed_keyword_struct) { - uint64_t formed_string_offset = 0; - uint64_t string_len = 0; - - char* a_keyword = strsep(&keywords_globbed,","); - - for (uint8_t i=1; i <= keyword_count; i++) { - string_len = strlen(a_keyword); - if (set_UTF16_text) { - - uint32_t glyphs_req_bytes = mbstowcs(NULL, a_keyword, string_len+1) * 2; //passing NULL pre-calculates the size of wchar_t needed; - - formed_keyword_struct[formed_string_offset+1] = (char)(0xFE); //BOM - formed_keyword_struct[formed_string_offset+2] = (char)(0xFF); //BOM - formed_string_offset+=3; //BOM + keyword length that has yet to be set - - int bytes_converted = UTF8ToUTF16BE((unsigned char*)(formed_keyword_struct + formed_string_offset), glyphs_req_bytes, (unsigned char*)a_keyword, string_len); - - if (bytes_converted > 1) { - formed_keyword_struct[formed_string_offset-3] = (uint8_t)bytes_converted + 4; //keyword length is NOW set - formed_string_offset += bytes_converted + 2; //NULL terminator - } - } else { - - uint32_t string_len = strlen(a_keyword); - formed_keyword_struct[formed_string_offset] = (uint8_t)string_len + 1; //add the terminating NULL - formed_string_offset++; - memcpy(formed_keyword_struct + formed_string_offset, a_keyword, string_len ); - formed_string_offset+= (string_len +1); - } - a_keyword = strsep(&keywords_globbed,","); - } - return formed_string_offset; + keywords_globbed - the globbed string of keywords ('foo1,foo2,foo_you') + keyword_count - count of keywords in the above globbed string + set_UTF16_text - whether to encode as utf16 + formed_keyword_struct - the char string that will hold the converted +keyword struct (manually formatted) + + 3gp keywords are a little more complicated. Since they will be entered +separated by some form of punctuation, they need to be separated out They also +will possibly be converted to utf16 - and they NEED to start with the BOM then. +Prefacing each keyword is the 8bit length of the string And each keyword needs +to be NULL terminated. Technically it would be possible to even have mixed +encodings (not supported here). +----------------------*/ +uint32_t APar_3GP_Keyword_atom_Format(char *keywords_globbed, + uint8_t keyword_count, + bool set_UTF16_text, + char *&formed_keyword_struct) { + uint64_t formed_string_offset = 0; + uint64_t string_len = 0; + + char *a_keyword = strsep(&keywords_globbed, ","); + + for (uint8_t i = 1; i <= keyword_count; i++) { + string_len = strlen(a_keyword); + if (set_UTF16_text) { + + uint32_t glyphs_req_bytes = + mbstowcs(NULL, a_keyword, string_len + 1) * + 2; // passing NULL pre-calculates the size of wchar_t needed; + + formed_keyword_struct[formed_string_offset + 1] = (char)(0xFE); // BOM + formed_keyword_struct[formed_string_offset + 2] = (char)(0xFF); // BOM + formed_string_offset += 3; // BOM + keyword length that has yet to be set + + int bytes_converted = UTF8ToUTF16BE( + (unsigned char *)(formed_keyword_struct + formed_string_offset), + glyphs_req_bytes, + (unsigned char *)a_keyword, + string_len); + + if (bytes_converted > 1) { + formed_keyword_struct[formed_string_offset - 3] = + (uint8_t)bytes_converted + 4; // keyword length is NOW set + formed_string_offset += bytes_converted + 2; // NULL terminator + } + } else { + + uint32_t string_len = strlen(a_keyword); + formed_keyword_struct[formed_string_offset] = + (uint8_t)string_len + 1; // add the terminating NULL + formed_string_offset++; + memcpy( + formed_keyword_struct + formed_string_offset, a_keyword, string_len); + formed_string_offset += (string_len + 1); + } + a_keyword = strsep(&keywords_globbed, ","); + } + return formed_string_offset; } /*---------------------- APar_uuid_atom_Init - atom_path - the parent hierarchy of the desired atom (with the location of the specific uuid atom supplied as '=%s') - uuidName - the name of the atom (possibly provided in a forbidden utf8 - only latin1 aka iso8859 is acceptable) - dataType - for now text is only supported; really its atom version/flags as used by iTunes - uuidValue - the string that will get embedded onto the atom - shellAtom - flag to denote whether the atom may possibly come as utf8 encoded - - uuid atoms are user-supplied/user-defined atoms that allow for extended tagging support. Because a uuid atom is malleable, and defined by the utility - that created it, any information carried by a uuid is arbitrary, and cannot be guaranteed by a non-originating utility. In AtomicParsley uuid atoms, - the data is presented much like an iTunes-style atom - except that the information gets carried directly on the uuid atom - no 'data' child atom - exists. A uuid atom is a special longer type of traditional atom. As a traditional atom, it name is 'uuid' - and the 4 bytes after that represent - its uuid name. Because the data will be directly sitting on the atom, a different means of finding these atoms exists, as well as creating the - acutal uuidpath itself. Once created however, placing information on it is very much like any other atom - done via APar_Unified_atom_Put - - //4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data -----------------------*/ -AtomicInfo* APar_uuid_atom_Init(const char* atom_path, const char* uuidName, const uint32_t dataType, const char* uuidValue, bool shellAtom) { - AtomicInfo* desiredAtom = NULL; - char uuid_path[256]; - char uuid_binary_str[20]; - char uuid_4char_name[10]; - memset(uuid_path, 0, sizeof(uuid_path)); - memset(uuid_binary_str, 0, sizeof(uuid_binary_str)); - memset(uuid_4char_name, 0, sizeof(uuid_4char_name)); - uint16_t path_len = 0; - - if (shellAtom) { - UTF8Toisolat1((unsigned char*)&uuid_4char_name, 4, (unsigned char*)uuidName, strlen(uuidName) ); - } else { - memcpy(uuid_4char_name, uuidName, 4); - } - - APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str); - APar_endian_uuid_bin_str_conversion(uuid_binary_str); - - //this will only append (and knock off) %s (anything) at the end of a string - path_len = strlen(atom_path); - memcpy(uuid_path, atom_path, path_len-2); - memcpy(uuid_path + (path_len-2), uuid_binary_str, 16); + atom_path - the parent hierarchy of the desired atom (with the location +of the specific uuid atom supplied as '=%s') uuidName - the name of the atom +(possibly provided in a forbidden utf8 - only latin1 aka iso8859 is acceptable) + dataType - for now text is only supported; really its atom version/flags +as used by iTunes uuidValue - the string that will get embedded onto the atom + shellAtom - flag to denote whether the atom may possibly come as utf8 +encoded + + uuid atoms are user-supplied/user-defined atoms that allow for extended +tagging support. Because a uuid atom is malleable, and defined by the utility + that created it, any information carried by a uuid is arbitrary, +and cannot be guaranteed by a non-originating utility. In AtomicParsley uuid +atoms, the data is presented much like an iTunes-style atom - except that the +information gets carried directly on the uuid atom - no 'data' child atom + exists. A uuid atom is a special longer type of traditional +atom. As a traditional atom, it name is 'uuid' - and the 4 bytes after that +represent its uuid name. Because the data will be directly sitting on the atom, +a different means of finding these atoms exists, as well as creating the acutal +uuidpath itself. Once created however, placing information on it is very much +like any other atom - done via APar_Unified_atom_Put + + //4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes +name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data +----------------------*/ +AtomicInfo *APar_uuid_atom_Init(const char *atom_path, + const char *uuidName, + const uint32_t dataType, + const char *uuidValue, + bool shellAtom) { + AtomicInfo *desiredAtom = NULL; + char uuid_path[256]; + char uuid_binary_str[20]; + char uuid_4char_name[10]; + memset(uuid_path, 0, sizeof(uuid_path)); + memset(uuid_binary_str, 0, sizeof(uuid_binary_str)); + memset(uuid_4char_name, 0, sizeof(uuid_4char_name)); + uint16_t path_len = 0; + + if (shellAtom) { + UTF8Toisolat1((unsigned char *)&uuid_4char_name, + 4, + (unsigned char *)uuidName, + strlen(uuidName)); + } else { + memcpy(uuid_4char_name, uuidName, 4); + } + + APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str); + APar_endian_uuid_bin_str_conversion(uuid_binary_str); + + // this will only append (and knock off) %s (anything) at the end of a string + path_len = strlen(atom_path); + memcpy(uuid_path, atom_path, path_len - 2); + memcpy(uuid_path + (path_len - 2), uuid_binary_str, 16); #if defined(DEBUG_V) - fprintf(stdout, "debug: APar_uuid_atom_Init desired atom '%s' converted to uuidv5: ", uuidName); - APar_print_uuid( (ap_uuid_t*)(uuid_path + (path_len-2)) ); + fprintf(stdout, + "debug: APar_uuid_atom_Init desired atom '%s' converted to uuidv5: ", + uuidName); + APar_print_uuid((ap_uuid_t *)(uuid_path + (path_len - 2))); #endif - if ( uuidValue == NULL || strlen(uuidValue) == 0) { - APar_RemoveAtom(uuid_path, EXTENDED_ATOM, 0); //find the atom; don't create if it's "" to remove - APar_FlagMovieHeader(); - return NULL; - - } else { - if ( !(dataType == AtomFlags_Data_Text || dataType == AtomFlags_Data_uuid_binary) ) { //the only supported types - fprintf(stdout, "AP warning: only text or file types are allowed on uuid atom %s (%" PRIu32 "-%u); skipping\n", uuidName, dataType, AtomFlags_Data_Text); - return NULL; - } - //uuid atoms won't have 'data' child atoms - they will carry the data directly as opposed to traditional iTunes-style metadata that does store the information on 'data' atoms. But user-defined is user-defined, so that is how it will be defined here. - modified_atoms = true; - - desiredAtom = APar_FindAtom(uuid_path, true, EXTENDED_ATOM, 0, true); - desiredAtom->uuid_ap_atomname = (char*)calloc(1, sizeof(char)*10); //only useful to print out the atom tree midway through an operation - memcpy(desiredAtom->uuid_ap_atomname, uuid_4char_name, 4); //only useful to print out the atom tree midway through an operation - - if (dataType == AtomFlags_Data_Text) APar_MetaData_atom_QuickInit(desiredAtom->AtomicNumber, dataType, 20); //+20 including the 4 NULL bytes preceding any string we set - //NOTE: setting a file into a uuid atom (dataType == AtomFlags_Data_uuid_binary) is handled in main.cpp - the length of the file extension, description and file - //all add up to the amount to malloc AtomicData to, so handle that separately. - - parsedAtoms[desiredAtom->AtomicNumber].AtomicClassification = EXTENDED_ATOM; - APar_FlagMovieHeader(); - } - return desiredAtom; + if (uuidValue == NULL || strlen(uuidValue) == 0) { + APar_RemoveAtom(uuid_path, + EXTENDED_ATOM, + 0); // find the atom; don't create if it's "" to remove + APar_FlagMovieHeader(); + return NULL; + + } else { + if (!(dataType == AtomFlags_Data_Text || + dataType == AtomFlags_Data_uuid_binary)) { // the only supported types + fprintf(stdout, + "AP warning: only text or file types are allowed on uuid atom %s " + "(%" PRIu32 "-%u); skipping\n", + uuidName, + dataType, + AtomFlags_Data_Text); + return NULL; + } + // uuid atoms won't have 'data' child atoms - they will carry the data + // directly as opposed to traditional iTunes-style metadata that does store + // the information on 'data' atoms. But user-defined is user-defined, so + // that is how it will be defined here. + modified_atoms = true; + + desiredAtom = APar_FindAtom(uuid_path, true, EXTENDED_ATOM, 0, true); + desiredAtom->uuid_ap_atomname = (char *)calloc( + 1, sizeof(char) * 10); // only useful to print out the atom tree midway + // through an operation + memcpy(desiredAtom->uuid_ap_atomname, + uuid_4char_name, + 4); // only useful to print out the atom tree midway through an + // operation + + if (dataType == AtomFlags_Data_Text) + APar_MetaData_atom_QuickInit( + desiredAtom->AtomicNumber, + dataType, + 20); //+20 including the 4 NULL bytes preceding any string we set + // NOTE: setting a file into a uuid atom (dataType == + // AtomFlags_Data_uuid_binary) is handled in main.cpp - the length of the + // file extension, description and file all add up to the amount to malloc + // AtomicData to, so handle that separately. + + parsedAtoms[desiredAtom->AtomicNumber].AtomicClassification = EXTENDED_ATOM; + APar_FlagMovieHeader(); + } + return desiredAtom; } /*---------------------- APar_MetaData_atom_QuickInit - atom_num - the position in the parsedAtoms array (either found in the file or a newly created sparse atom) so AtomicData can be initialized - atomFlags - the AtomicVerFlags for the iTunes-style metadata atom - supplemental_length - iTunes-style metadata for 'data' atoms is >= 16bytes long; AtomicParsley created uuid atoms will be +4bytes directly on that atom - allotment - the bytes of AtomicData to malloc (defaults to MAXDATA_PAYLOAD + 1 (+50) unless changed - like uuids from file) - - Metadata_QuickInit will initialize a pre-found atom to MAXDATA_PAYLOAD so that it can carry info on AtomicData -----------------------*/ -void APar_MetaData_atom_QuickInit(short atom_num, const uint32_t atomFlags, uint32_t supplemental_length, uint32_t allotment) { - //this will skip the finding of atoms and just malloc the AtomicData; used by genre & artwork - - parsedAtoms[atom_num].AtomicData = (char*)calloc(1, sizeof(char)* allotment+50 ); - if (parsedAtoms[atom_num].AtomicData == NULL) { - fprintf(stdout, "AP error: there was insufficient memory available for allocation. Exiting.%c\n", '\a'); - exit(1); - } - - parsedAtoms[atom_num].AtomicLength = 16 + supplemental_length; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags, 4 bytes NULL - parsedAtoms[atom_num].AtomicVerFlags = atomFlags; - parsedAtoms[atom_num].AtomicContainerState = CHILD_ATOM; - parsedAtoms[atom_num].AtomicClassification = VERSIONED_ATOM; + atom_num - the position in the parsedAtoms array (either found in the +file or a newly created sparse atom) so AtomicData can be initialized atomFlags +- the AtomicVerFlags for the iTunes-style metadata atom supplemental_length - +iTunes-style metadata for 'data' atoms is >= 16bytes long; AtomicParsley created +uuid atoms will be +4bytes directly on that atom allotment - the bytes of +AtomicData to malloc (defaults to MAXDATA_PAYLOAD + 1 (+50) unless changed - +like uuids from file) + + Metadata_QuickInit will initialize a pre-found atom to MAXDATA_PAYLOAD so +that it can carry info on AtomicData +----------------------*/ +void APar_MetaData_atom_QuickInit(short atom_num, + const uint32_t atomFlags, + uint32_t supplemental_length, + uint32_t allotment) { + // this will skip the finding of atoms and just malloc the AtomicData; used by + // genre & artwork + + parsedAtoms[atom_num].AtomicData = + (char *)calloc(1, sizeof(char) * allotment + 50); + if (parsedAtoms[atom_num].AtomicData == NULL) { + fprintf(stdout, + "AP error: there was insufficient memory available for allocation. " + "Exiting.%c\n", + '\a'); + exit(1); + } + + parsedAtoms[atom_num].AtomicLength = + 16 + supplemental_length; // 4bytes atom length, 4 bytes atom length, 4 + // bytes version/flags, 4 bytes NULL + parsedAtoms[atom_num].AtomicVerFlags = atomFlags; + parsedAtoms[atom_num].AtomicContainerState = CHILD_ATOM; + parsedAtoms[atom_num].AtomicClassification = VERSIONED_ATOM; - return; + return; } /*---------------------- APar_MetaData_atom_Init - atom_path - the hierarchical path to the specific atom carrying iTunes-style metadata that will be found (and created if necessary) - MD_Payload - the information to be carried (also used as a test if NULL to remove the atom) - atomFlags - the AtomicVerFlags for the atom (text, integer or unsigned integer) - - Metadata_Init will search for and create the necessary hierarchy so that the atom can be initialized to carry the payload data on AtomicData. - This will provide a shell of an atom with 4bytes length, 4bytes name, 4bytes version/flags, 4bytes NULL + any other data -----------------------*/ -AtomicInfo* APar_MetaData_atom_Init(const char* atom_path, const char* MD_Payload, const uint32_t atomFlags) { - //this will handle the vanilla iTunes-style metadata atoms; genre will be handled elsewehere because it gets carried on 2 different atoms, and artwork gets special treatment because it can have multiple child data atoms - if (metadata_style != ITUNES_STYLE) return NULL; - bool retain_atom = true; - - if ( strlen(MD_Payload) == 0 ) { - retain_atom = false; - } - - if (retain_atom) { - APar_Verify__udta_meta_hdlr__atom(); - } - - AtomicInfo* desiredAtom = APar_FindAtom(atom_path, retain_atom, VERSIONED_ATOM, 0); //finds the atom; if not present, creates the atom - if (desiredAtom == NULL) return NULL; - modified_atoms = true; - - if (! retain_atom) { - AtomicInfo* parent_atom = &parsedAtoms[ APar_FindParentAtom(desiredAtom->AtomicNumber, desiredAtom->AtomicLevel) ]; - if (desiredAtom->AtomicNumber > 0 && parent_atom->AtomicNumber > 0) { - APar_EliminateAtom(parent_atom->AtomicNumber, desiredAtom->NextAtomNumber); - APar_FlagMovieHeader(); - return NULL; - } - - } else { - parsedAtoms[desiredAtom->AtomicNumber].AtomicData = (char*)malloc(sizeof(char)* MAXDATA_PAYLOAD + 1 ); //puts a hard limit on the length of strings (the spec doesn't) - memset(parsedAtoms[desiredAtom->AtomicNumber].AtomicData, 0, sizeof(char)* MAXDATA_PAYLOAD + 1 ); - - parsedAtoms[desiredAtom->AtomicNumber].AtomicLength = 16; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags, 4 bytes NULL - parsedAtoms[desiredAtom->AtomicNumber].AtomicVerFlags = atomFlags; - parsedAtoms[desiredAtom->AtomicNumber].AtomicContainerState = CHILD_ATOM; - parsedAtoms[desiredAtom->AtomicNumber].AtomicClassification = VERSIONED_ATOM; - APar_FlagMovieHeader(); - } - return desiredAtom; + atom_path - the hierarchical path to the specific atom carrying +iTunes-style metadata that will be found (and created if necessary) MD_Payload - +the information to be carried (also used as a test if NULL to remove the atom) + atomFlags - the AtomicVerFlags for the atom (text, integer or unsigned +integer) + + Metadata_Init will search for and create the necessary hierarchy so that the +atom can be initialized to carry the payload data on AtomicData. This will +provide a shell of an atom with 4bytes length, 4bytes name, 4bytes +version/flags, 4bytes NULL + any other data +----------------------*/ +AtomicInfo *APar_MetaData_atom_Init(const char *atom_path, + const char *MD_Payload, + const uint32_t atomFlags) { + // this will handle the vanilla iTunes-style metadata atoms; genre will be + // handled elsewehere because it gets carried on 2 different atoms, and + // artwork gets special treatment because it can have multiple child data + // atoms + if (metadata_style != ITUNES_STYLE) + return NULL; + bool retain_atom = true; + + if (strlen(MD_Payload) == 0) { + retain_atom = false; + } + + if (retain_atom) { + APar_Verify__udta_meta_hdlr__atom(); + } + + AtomicInfo *desiredAtom = + APar_FindAtom(atom_path, + retain_atom, + VERSIONED_ATOM, + 0); // finds the atom; if not present, creates the atom + if (desiredAtom == NULL) + return NULL; + modified_atoms = true; + + if (!retain_atom) { + AtomicInfo *parent_atom = &parsedAtoms[APar_FindParentAtom( + desiredAtom->AtomicNumber, desiredAtom->AtomicLevel)]; + if (desiredAtom->AtomicNumber > 0 && parent_atom->AtomicNumber > 0) { + APar_EliminateAtom(parent_atom->AtomicNumber, + desiredAtom->NextAtomNumber); + APar_FlagMovieHeader(); + return NULL; + } + + } else { + parsedAtoms[desiredAtom->AtomicNumber].AtomicData = (char *)malloc( + sizeof(char) * MAXDATA_PAYLOAD + + 1); // puts a hard limit on the length of strings (the spec doesn't) + memset(parsedAtoms[desiredAtom->AtomicNumber].AtomicData, + 0, + sizeof(char) * MAXDATA_PAYLOAD + 1); + + parsedAtoms[desiredAtom->AtomicNumber].AtomicLength = + 16; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags, 4 + // bytes NULL + parsedAtoms[desiredAtom->AtomicNumber].AtomicVerFlags = atomFlags; + parsedAtoms[desiredAtom->AtomicNumber].AtomicContainerState = CHILD_ATOM; + parsedAtoms[desiredAtom->AtomicNumber].AtomicClassification = + VERSIONED_ATOM; + APar_FlagMovieHeader(); + } + return desiredAtom; } /*---------------------- APar_UserData_atom_Init - userdata_atom_name - the name of the atom to be set ('titl', 'loci', 'cprt') - atom_payload - the information to be carried (also used as a test if NULL to remove the atom) - udta_container - determines whether to create an atom path at movie level or track level - track_idx - provide the track for the target atom if at track level - userdata_lang - the language for the tag (multiple tags with the same name, but different languages are supported for some of these atoms) - - UserData_Init can be called multiple times from a single cli invocation (if used to target at atom at track level for all tracks). Since it can be called - repeatedly, the atom path is created here based on the container for 'udta': 'moov' for movie level or 'trak' at track level. If there is no payload that - atom path is found & if found to exists is removed. If the payload does contain something, the created atom path is created, initialized & the language - setting is set (for internal AP use) for that atom. - - NOTE: the language setting (if supported - yrrc doesn't) occurs in different places in 3GP atoms. Most occur right after atom flags/versioning - but - in rtng/clsf they occur later. The language is instead stored in binary form amid the data for the atom, but is also put into the parsedAtoms[] array - (that information is only used in finding atoms not the actual writing of atoms out). Both (storing the language in AtomicData in binary form & put in - the parsedAtoms[] AtomicInfo array) forms are required to implement multiple language support for 3gp atoms. - - NOTE2: Perhaps there is something wrong with Apple's implementation of 3gp metadata, or I'm loosing my mind. The exact same utf8 string that shows up in a - 3gp file as ??? - ??? shows up *perfect* in an mp4 or mov container. Encoded as utf16 same problem a sample string using Polish glyphs in utf8 has some - gylphs missing with lang=eng. The same string with 'lang=pol', and different glyphs are missing. The problem occurs using unicode.org's ConvertUTF8toUTF16 - or using libxmls's UTF8ToUTF16BE (when converting to utf16) in the same places - except for the copyright symbol which unicode.org's ConvertUTF16toUTF8 didn't - properly convert - which was the reason why libxml's functions are now used. And at no point can I get the audio protected P-in-a-circle glyph to show up in - utf8 or utf16. To summarize, either I am completely overlooking some interplay (of lang somehow changing the utf8 or utf16 standard), the unicode translations - are off (which in the case of utf8 is embedded directly on Mac OS X, so that can't be it), or Apple's 3gp implementation is off. - - TODO NOTE: the track modification date should change if set at track level because of this -----------------------*/ -AtomicInfo* APar_UserData_atom_Init(const char* userdata_atom_name, const char* atom_payload, uint8_t udta_container, uint8_t track_idx, uint16_t userdata_lang) { - uint8_t atom_type = PACKED_LANG_ATOM; - uint8_t total_tracks = 0; - uint8_t a_track = 0;//unused - AtomicInfo* desiredAtom = NULL; - char* userdata_atom_path = NULL; - - if (userdata_lang == 0) atom_type = VERSIONED_ATOM; - - APar_FindAtomInTrack(total_tracks, a_track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here. - - if (track_idx > total_tracks || (track_idx == 0 && udta_container == SINGLE_TRACK_ATOM) ) { - APar_assert(false, 5, userdata_atom_name); - return NULL; - } - - userdata_atom_path = (char*)malloc(sizeof(char)* 400 ); - - if (udta_container == MOVIE_LEVEL_ATOM) { - APar_sprintf_atompath(userdata_atom_path, userdata_atom_name, 0xFF, MOVIE_LEVEL_ATOM, 400); - } else if (udta_container == ALL_TRACKS_ATOM) { - APar_sprintf_atompath(userdata_atom_path, userdata_atom_name, track_idx, ALL_TRACKS_ATOM, 400); - } else { - APar_sprintf_atompath(userdata_atom_path, userdata_atom_name, track_idx, SINGLE_TRACK_ATOM, 400); - } - - if ( strlen(atom_payload) == 0) { - - APar_RemoveAtom(userdata_atom_path, atom_type, atom_type == VERSIONED_ATOM ? 1 : userdata_lang); //find the atom; don't create if it's "" to remove - free(userdata_atom_path); userdata_atom_path = NULL; - return NULL; - } else { - modified_atoms = true; - - desiredAtom = APar_FindAtom(userdata_atom_path, true, atom_type, userdata_lang); - - desiredAtom->AtomicData = (char*)calloc(1, sizeof(char)* MAXDATA_PAYLOAD ); //puts a hard limit on the length of strings (the spec doesn't) - - desiredAtom->AtomicLength = 12; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags (NULLs) - desiredAtom->AtomicVerFlags = 0; - desiredAtom->AtomicContainerState = CHILD_ATOM; - desiredAtom->AtomicClassification = atom_type; - desiredAtom->AtomicLanguage = userdata_lang; - APar_FlagTrackHeader(desiredAtom); - } - free(userdata_atom_path); userdata_atom_path = NULL; - return desiredAtom; + userdata_atom_name - the name of the atom to be set ('titl', 'loci', +'cprt') atom_payload - the information to be carried (also used as a test if +NULL to remove the atom) udta_container - determines whether to create an atom +path at movie level or track level track_idx - provide the track for the target +atom if at track level userdata_lang - the language for the tag (multiple tags +with the same name, but different languages are supported for some of these +atoms) + + UserData_Init can be called multiple times from a single cli invocation (if +used to target at atom at track level for all tracks). Since it can be called + repeatedly, the atom path is created here based on the container +for 'udta': 'moov' for movie level or 'trak' at track level. If there is no +payload that atom path is found & if found to exists is removed. If the payload +does contain something, the created atom path is created, initialized & the +language setting is set (for internal AP use) for that atom. + + NOTE: the language setting (if supported - yrrc doesn't) +occurs in different places in 3GP atoms. Most occur right after atom +flags/versioning - but in rtng/clsf they occur later. The language is instead +stored in binary form amid the data for the atom, but is also put into the +parsedAtoms[] array (that information is only used in finding atoms not the +actual writing of atoms out). Both (storing the language in AtomicData in binary +form & put in the parsedAtoms[] AtomicInfo array) forms are required to +implement multiple language support for 3gp atoms. + + NOTE2: Perhaps there is something wrong with Apple's +implementation of 3gp metadata, or I'm loosing my mind. The exact same utf8 +string that shows up in a 3gp file as ??? - ??? shows up *perfect* in an mp4 or +mov container. Encoded as utf16 same problem a sample string using Polish glyphs +in utf8 has some gylphs missing with lang=eng. The same string with 'lang=pol', +and different glyphs are missing. The problem occurs using unicode.org's +ConvertUTF8toUTF16 or using libxmls's UTF8ToUTF16BE (when converting to utf16) +in the same places - except for the copyright symbol which unicode.org's +ConvertUTF16toUTF8 didn't properly convert - which was the reason why libxml's +functions are now used. And at no point can I get the audio protected +P-in-a-circle glyph to show up in utf8 or utf16. To summarize, either I am +completely overlooking some interplay (of lang somehow changing the utf8 or +utf16 standard), the unicode translations are off (which in the case of utf8 is +embedded directly on Mac OS X, so that can't be it), or Apple's 3gp +implementation is off. + + TODO NOTE: the track modification date should change if +set at track level because of this +----------------------*/ +AtomicInfo *APar_UserData_atom_Init(const char *userdata_atom_name, + const char *atom_payload, + uint8_t udta_container, + uint8_t track_idx, + uint16_t userdata_lang) { + uint8_t atom_type = PACKED_LANG_ATOM; + uint8_t total_tracks = 0; + uint8_t a_track = 0; // unused + AtomicInfo *desiredAtom = NULL; + char *userdata_atom_path = NULL; + + if (userdata_lang == 0) + atom_type = VERSIONED_ATOM; + + APar_FindAtomInTrack(total_tracks, + a_track, + NULL); // With track_num set to 0, it will return the + // total trak atom into total_tracks here. + + if (track_idx > total_tracks || + (track_idx == 0 && udta_container == SINGLE_TRACK_ATOM)) { + APar_assert(false, 5, userdata_atom_name); + return NULL; + } + + userdata_atom_path = (char *)malloc(sizeof(char) * 400); + + if (udta_container == MOVIE_LEVEL_ATOM) { + APar_sprintf_atompath( + userdata_atom_path, userdata_atom_name, 0xFF, MOVIE_LEVEL_ATOM, 400); + } else if (udta_container == ALL_TRACKS_ATOM) { + APar_sprintf_atompath(userdata_atom_path, + userdata_atom_name, + track_idx, + ALL_TRACKS_ATOM, + 400); + } else { + APar_sprintf_atompath(userdata_atom_path, + userdata_atom_name, + track_idx, + SINGLE_TRACK_ATOM, + 400); + } + + if (strlen(atom_payload) == 0) { + + APar_RemoveAtom(userdata_atom_path, + atom_type, + atom_type == VERSIONED_ATOM + ? 1 + : userdata_lang); // find the atom; don't create if it's + // "" to remove + free(userdata_atom_path); + userdata_atom_path = NULL; + return NULL; + } else { + modified_atoms = true; + + desiredAtom = + APar_FindAtom(userdata_atom_path, true, atom_type, userdata_lang); + + desiredAtom->AtomicData = (char *)calloc( + 1, sizeof(char) * MAXDATA_PAYLOAD); // puts a hard limit on the length + // of strings (the spec doesn't) + + desiredAtom->AtomicLength = 12; // 4bytes atom length, 4 bytes atom length, + // 4 bytes version/flags (NULLs) + desiredAtom->AtomicVerFlags = 0; + desiredAtom->AtomicContainerState = CHILD_ATOM; + desiredAtom->AtomicClassification = atom_type; + desiredAtom->AtomicLanguage = userdata_lang; + APar_FlagTrackHeader(desiredAtom); + } + free(userdata_atom_path); + userdata_atom_path = NULL; + return desiredAtom; } /*---------------------- APar_reverseDNS_atom_Init - rDNS_atom_name - the name of the descriptor for the reverseDNS atom form (like iTunNORM) - rDNS_payload - the information to be carried (also used as a test if NULL to remove the atom) - atomFlags - text, integer, binary flag of the data payload - rDNS_domain - the reverse domain itself (like net.sourceforge.atomicparsley or com.apple.iTunes) + rDNS_atom_name - the name of the descriptor for the reverseDNS atom form +(like iTunNORM) rDNS_payload - the information to be carried (also used as a +test if NULL to remove the atom) atomFlags - text, integer, binary flag of the +data payload rDNS_domain - the reverse domain itself (like +net.sourceforge.atomicparsley or com.apple.iTunes) FILL IN ----------------------*/ -AtomicInfo* APar_reverseDNS_atom_Init(const char* rDNS_atom_name, const char* rDNS_payload, const uint32_t* atomFlags, const char* rDNS_domain) { - AtomicInfo* desiredAtom = NULL; - char* reverseDNS_atompath = (char*)calloc(1, sizeof(char)*2001); - - if (metadata_style != ITUNES_STYLE) { - free(reverseDNS_atompath); reverseDNS_atompath = NULL; - return NULL; - } - - sprintf(reverseDNS_atompath, "moov.udta.meta.ilst.----.name:[%s]", rDNS_atom_name); //moov.udta.meta.ilst.----.name:[iTunNORM] - - if ( rDNS_payload != NULL ) { - if (strlen(rDNS_payload) == 0) { - APar_RemoveAtom(reverseDNS_atompath, VERSIONED_ATOM, 0, rDNS_domain); - free(reverseDNS_atompath); reverseDNS_atompath = NULL; - return NULL; - } - APar_Verify__udta_meta_hdlr__atom(); - } else { - APar_RemoveAtom(reverseDNS_atompath, VERSIONED_ATOM, 0, rDNS_domain); - APar_FlagMovieHeader(); - free(reverseDNS_atompath); reverseDNS_atompath = NULL; - return NULL; - } - - desiredAtom = APar_FindAtom(reverseDNS_atompath, false, VERSIONED_ATOM, 0, false, rDNS_domain); //finds the atom; do NOT create it if not found - manually create the hierarchy - - if (desiredAtom == NULL) { - AtomicInfo* ilst_atom = APar_FindAtom("moov.udta.meta.ilst", true, SIMPLE_ATOM, 0); - short last_iTunes_list_descriptor = APar_FindLastChild_of_ParentAtom(ilst_atom->AtomicNumber); //the *last* atom contained by ilst - even if its the 4th 'data' atom - - short rDNS_four_dash_parent = APar_InterjectNewAtom("----", PARENT_ATOM, SIMPLE_ATOM, 8, 0, 0, ilst_atom->AtomicLevel+1, last_iTunes_list_descriptor); - - short rDNS_mean_atom = APar_InterjectNewAtom("mean", CHILD_ATOM, VERSIONED_ATOM, 12, AtomFlags_Data_Binary, 0, ilst_atom->AtomicLevel+2, rDNS_four_dash_parent); - uint32_t domain_len = strlen(rDNS_domain); - parsedAtoms[rDNS_mean_atom].ReverseDNSdomain = (char*)calloc(1, sizeof(char)*101); - memcpy( parsedAtoms[rDNS_mean_atom].ReverseDNSdomain, rDNS_domain, domain_len ); - APar_atom_Binary_Put(&parsedAtoms[rDNS_mean_atom], rDNS_domain, domain_len, 0); - - short rDNS_name_atom = APar_InterjectNewAtom("name", CHILD_ATOM, VERSIONED_ATOM, 12, AtomFlags_Data_Binary, 0, ilst_atom->AtomicLevel+2, rDNS_mean_atom); - uint32_t name_len = strlen(rDNS_atom_name); - parsedAtoms[rDNS_name_atom].ReverseDNSname = (char*)calloc(1, sizeof(char)*101); - memcpy( parsedAtoms[rDNS_name_atom].ReverseDNSname, rDNS_atom_name, name_len ); - APar_atom_Binary_Put(&parsedAtoms[rDNS_name_atom], rDNS_atom_name, name_len, 0); - - AtomicInfo proto_rDNS_data_atom = { 0 }; - APar_CreateSurrogateAtom(&proto_rDNS_data_atom, "data", ilst_atom->AtomicLevel+2, VERSIONED_ATOM, 0, NULL, 0); - desiredAtom = APar_CreateSparseAtom(&proto_rDNS_data_atom, ilst_atom, rDNS_name_atom); - APar_MetaData_atom_QuickInit(desiredAtom->AtomicNumber, *atomFlags, 0, MAXDATA_PAYLOAD); - } else { - if (strcmp(rDNS_domain, "com.apple.iTunes") == 0) { //for the iTunes domain, only support 1 'data' entry - APar_MetaData_atom_QuickInit(desiredAtom->NextAtomNumber, *atomFlags, 0, MAXDATA_PAYLOAD); - desiredAtom = &parsedAtoms[desiredAtom->NextAtomNumber]; - - } else { //now create a 'data' atom at the end of the hierarchy (allowing multiple entries) - short rDNSparent_idx = APar_FindParentAtom(desiredAtom->AtomicNumber, desiredAtom->AtomicLevel); - short last_child = APar_FindLastChild_of_ParentAtom(rDNSparent_idx); - AtomicInfo proto_rDNS_data_atom = { 0 }; - APar_CreateSurrogateAtom(&proto_rDNS_data_atom, "data", parsedAtoms[last_child].AtomicLevel, VERSIONED_ATOM, 0, NULL, 0); - desiredAtom = APar_CreateSparseAtom(&proto_rDNS_data_atom, &parsedAtoms[rDNSparent_idx], last_child); - APar_MetaData_atom_QuickInit(desiredAtom->AtomicNumber, *atomFlags, 0, MAXDATA_PAYLOAD); - } - } - APar_FlagMovieHeader(); - free(reverseDNS_atompath); reverseDNS_atompath = NULL; - modified_atoms = true; - return desiredAtom; -} - -AtomicInfo* APar_ID32_atom_Init(const char* frameID_str, char meta_area, const char* lang_str, uint16_t id32_lang) { - uint8_t total_tracks = 0; - uint8_t a_track = 0;//unused - AtomicInfo* meta_atom = NULL; - AtomicInfo* hdlr_atom = NULL; - char* id32_trackpath = NULL; - AtomicInfo* ID32_atom = NULL; - bool non_referenced_data = false; - bool remove_ID32_atom = false; - - APar_FindAtomInTrack(total_tracks, a_track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here. - - if (meta_area > 0) { - if ((uint8_t)meta_area > total_tracks) { - APar_assert(false, 6, frameID_str); - return NULL; - } - } - - id32_trackpath = (char*)calloc(1, sizeof(char)*100); - - if (meta_area == 0-FILE_LEVEL_ATOM) { - meta_atom = APar_FindAtom("meta", false, DUAL_STATE_ATOM, 0); - } else if (meta_area == 0-MOVIE_LEVEL_ATOM) { - meta_atom = APar_FindAtom("moov.meta", false, DUAL_STATE_ATOM, 0); - //} else if (meta_area = 0) { - //setting id3tags for all tracks will *not* be supported; - } else if (meta_area > 0) { - sprintf(id32_trackpath, "moov.trak[%u].meta", meta_area); - meta_atom = APar_FindAtom(id32_trackpath, false, DUAL_STATE_ATOM, 0); - } - - if (meta_atom != NULL) { - hdlr_atom = APar_FindChildAtom(meta_atom->AtomicNumber, "hdlr"); - if (hdlr_atom != NULL) { - if (hdlr_atom->ancillary_data != 0x49443332) { - memset(id32_trackpath, 0, 5); //well, it won't be used for anything else since the handler type doesn't match, might as well convert the handler type using it - UInt32_TO_String4(hdlr_atom->ancillary_data, id32_trackpath); - APar_assert(false, 7, id32_trackpath); - free(id32_trackpath); - - return NULL; - } - } - } - - //its possible the ID32 atom targeted already exists - finding it in the traditional form (not external, and not locally referenced) is easiest. Locally referenced isn't. - if (meta_area == 0-FILE_LEVEL_ATOM) { - ID32_atom = APar_FindAtom("meta.ID32", false, PACKED_LANG_ATOM, id32_lang); - } else if (meta_area == 0-MOVIE_LEVEL_ATOM) { - ID32_atom = APar_FindAtom("moov.meta.ID32", false, PACKED_LANG_ATOM, id32_lang); - //} else if (meta_area = 0) { - //setting id3tags for all tracks will *not* be supported; - } else if (meta_area > 0) { - sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area); - ID32_atom = APar_FindAtom(id32_trackpath, false, PACKED_LANG_ATOM, id32_lang); - } - - if (ID32_atom != NULL) { - free(id32_trackpath); - id32_trackpath = NULL; - if (ID32_atom->ID32_TagInfo == NULL) { - APar_ID32_ScanID3Tag(source_file, ID32_atom); - } - return ID32_atom; //and that completes finding the ID32 atom and verifying that it was local and in a traditionally represented form. - - } else { - if (meta_atom != NULL) { - //if the primary item atom is present, it points to either a local data reference (flag of 0x000001) or external data which is unsupported. Either way skip it. - //..or probably another test would be if a data REFERENCE atom were present.... but you would have to match item_IDs - which are found in pitm (required for ID32). - if (APar_FindChildAtom(meta_atom->AtomicNumber, "pitm") != NULL) { - non_referenced_data = false; - APar_assert(false, 8, frameID_str); - free(id32_trackpath); - return NULL; - } else { //the inline/3gpp 'ID32' atom calls for referenced content to carry a 'pitm' atom. No worries - its just a 'meta'. - non_referenced_data = true; - } - } else { - //no 'meta' atom? Great - a blank slate. There won't be any jumping through a multitude of atoms to determine referencing - non_referenced_data = true; - } - } - - if (frameID_str == NULL) { - remove_ID32_atom = true; - } else if (strlen(frameID_str) == 0) { - remove_ID32_atom = true; - } - //this only gets executed if a pre-existing satisfactory ID32 atom was not found. Being able to find it by atom.path by definition means it was not referenced. - if (non_referenced_data && !remove_ID32_atom) { - if (meta_atom == NULL) { - if (meta_area == 0-FILE_LEVEL_ATOM) { - meta_atom = APar_FindAtom("meta", true, VERSIONED_ATOM, 0); - } else if (meta_area == 0-MOVIE_LEVEL_ATOM) { - meta_atom = APar_FindAtom("moov.meta", true, VERSIONED_ATOM, 0); - //} else if (meta_area = 0) { - //setting id3tags for all tracks will *not* be supported; - } else if (meta_area > 0) { - sprintf(id32_trackpath, "moov.trak[%u].meta", meta_area); - meta_atom = APar_FindAtom(id32_trackpath, true, VERSIONED_ATOM, 0); - } - } - - //create the required hdlr atom - if (hdlr_atom == NULL) { - if (meta_area == 0-FILE_LEVEL_ATOM) { - hdlr_atom = APar_FindAtom("meta.hdlr", true, VERSIONED_ATOM, 0); - } else if (meta_area == 0-MOVIE_LEVEL_ATOM) { - hdlr_atom = APar_FindAtom("moov.meta.hdlr", true, VERSIONED_ATOM, 0); - //} else if (meta_area = 0) { - //setting id3tags for all tracks will *not* be supported; - } else if (meta_area > 0) { - sprintf(id32_trackpath, "moov.trak[%u].meta.hdlr", meta_area); - hdlr_atom = APar_FindAtom(id32_trackpath, true, VERSIONED_ATOM, 0); - } - if (hdlr_atom == NULL) { - fprintf(stdout, "Uh, problem\n"); - exit(0); - } - APar_MetaData_atom_QuickInit(hdlr_atom->AtomicNumber, 0, 0); - APar_Unified_atom_Put(hdlr_atom, "ID32", UTF8_iTunesStyle_256glyphLimited, 0, 0); - APar_Unified_atom_Put(hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); - APar_Unified_atom_Put(hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); - APar_Unified_atom_Put(hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); - APar_Unified_atom_Put(hdlr_atom, "AtomicParsley ID3v2 Handler", UTF8_3GP_Style, 0, 0); - hdlr_atom->ancillary_data = 0x49443332; - } - - //and finally create the ID32 atom - if (meta_area == 0-FILE_LEVEL_ATOM) { - ID32_atom = APar_FindAtom("meta.ID32", true, PACKED_LANG_ATOM, id32_lang); - } else if (meta_area == 0-MOVIE_LEVEL_ATOM) { - ID32_atom = APar_FindAtom("moov.meta.ID32", true, PACKED_LANG_ATOM, id32_lang); - //} else if (meta_area = 0) { - //setting id3tags for all tracks will *not* be supported; - } else if (meta_area > 0) { - sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area); - ID32_atom = APar_FindAtom(id32_trackpath, true, PACKED_LANG_ATOM, id32_lang); - } - - if (id32_trackpath != NULL) { - free(id32_trackpath); - id32_trackpath = NULL; - } - - ID32_atom->AtomicLength = 12; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags (NULLs), 2 bytes lang - ID32_atom->AtomicVerFlags = 0; - ID32_atom->AtomicContainerState = CHILD_ATOM; - ID32_atom->AtomicClassification = PACKED_LANG_ATOM; - ID32_atom->AtomicLanguage = id32_lang; - - APar_ID3Tag_Init(ID32_atom); - //search for the desired frame - - //add the frame to an empty frame, copy data onto the id32_atom structure as required - //set modified _atoms to true - - return ID32_atom; - } else if (remove_ID32_atom) { - if (meta_area == 0-FILE_LEVEL_ATOM) { - APar_RemoveAtom("meta.ID32", PACKED_LANG_ATOM, id32_lang); - } else if (meta_area == 0-MOVIE_LEVEL_ATOM) { - APar_RemoveAtom("moov.meta.ID32", PACKED_LANG_ATOM, id32_lang); - //} else if (meta_area = 0) { - //setting id3tags for all tracks will *not* be supported; - } else if (meta_area > 0) { - sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area); - APar_RemoveAtom(id32_trackpath, PACKED_LANG_ATOM, id32_lang); - } - } - return NULL; +AtomicInfo *APar_reverseDNS_atom_Init(const char *rDNS_atom_name, + const char *rDNS_payload, + const uint32_t *atomFlags, + const char *rDNS_domain) { + AtomicInfo *desiredAtom = NULL; + char *reverseDNS_atompath = (char *)calloc(1, sizeof(char) * 2001); + + if (metadata_style != ITUNES_STYLE) { + free(reverseDNS_atompath); + reverseDNS_atompath = NULL; + return NULL; + } + + sprintf(reverseDNS_atompath, + "moov.udta.meta.ilst.----.name:[%s]", + rDNS_atom_name); // moov.udta.meta.ilst.----.name:[iTunNORM] + + if (rDNS_payload != NULL) { + if (strlen(rDNS_payload) == 0) { + APar_RemoveAtom(reverseDNS_atompath, VERSIONED_ATOM, 0, rDNS_domain); + free(reverseDNS_atompath); + reverseDNS_atompath = NULL; + return NULL; + } + APar_Verify__udta_meta_hdlr__atom(); + } else { + APar_RemoveAtom(reverseDNS_atompath, VERSIONED_ATOM, 0, rDNS_domain); + APar_FlagMovieHeader(); + free(reverseDNS_atompath); + reverseDNS_atompath = NULL; + return NULL; + } + + desiredAtom = + APar_FindAtom(reverseDNS_atompath, + false, + VERSIONED_ATOM, + 0, + false, + rDNS_domain); // finds the atom; do NOT create it if not + // found - manually create the hierarchy + + if (desiredAtom == NULL) { + AtomicInfo *ilst_atom = + APar_FindAtom("moov.udta.meta.ilst", true, SIMPLE_ATOM, 0); + short last_iTunes_list_descriptor = APar_FindLastChild_of_ParentAtom( + ilst_atom->AtomicNumber); // the *last* atom contained by ilst - even if + // its the 4th 'data' atom + + short rDNS_four_dash_parent = + APar_InterjectNewAtom("----", + PARENT_ATOM, + SIMPLE_ATOM, + 8, + 0, + 0, + ilst_atom->AtomicLevel + 1, + last_iTunes_list_descriptor); + + short rDNS_mean_atom = APar_InterjectNewAtom("mean", + CHILD_ATOM, + VERSIONED_ATOM, + 12, + AtomFlags_Data_Binary, + 0, + ilst_atom->AtomicLevel + 2, + rDNS_four_dash_parent); + uint32_t domain_len = strlen(rDNS_domain); + parsedAtoms[rDNS_mean_atom].ReverseDNSdomain = + (char *)calloc(1, sizeof(char) * 101); + memcpy( + parsedAtoms[rDNS_mean_atom].ReverseDNSdomain, rDNS_domain, domain_len); + APar_atom_Binary_Put( + &parsedAtoms[rDNS_mean_atom], rDNS_domain, domain_len, 0); + + short rDNS_name_atom = APar_InterjectNewAtom("name", + CHILD_ATOM, + VERSIONED_ATOM, + 12, + AtomFlags_Data_Binary, + 0, + ilst_atom->AtomicLevel + 2, + rDNS_mean_atom); + uint32_t name_len = strlen(rDNS_atom_name); + parsedAtoms[rDNS_name_atom].ReverseDNSname = + (char *)calloc(1, sizeof(char) * 101); + memcpy( + parsedAtoms[rDNS_name_atom].ReverseDNSname, rDNS_atom_name, name_len); + APar_atom_Binary_Put( + &parsedAtoms[rDNS_name_atom], rDNS_atom_name, name_len, 0); + + AtomicInfo proto_rDNS_data_atom = {0}; + APar_CreateSurrogateAtom(&proto_rDNS_data_atom, + "data", + ilst_atom->AtomicLevel + 2, + VERSIONED_ATOM, + 0, + NULL, + 0); + desiredAtom = + APar_CreateSparseAtom(&proto_rDNS_data_atom, ilst_atom, rDNS_name_atom); + APar_MetaData_atom_QuickInit( + desiredAtom->AtomicNumber, *atomFlags, 0, MAXDATA_PAYLOAD); + } else { + if (strcmp(rDNS_domain, "com.apple.iTunes") == + 0) { // for the iTunes domain, only support 1 'data' entry + APar_MetaData_atom_QuickInit( + desiredAtom->NextAtomNumber, *atomFlags, 0, MAXDATA_PAYLOAD); + desiredAtom = &parsedAtoms[desiredAtom->NextAtomNumber]; + + } else { // now create a 'data' atom at the end of the hierarchy (allowing + // multiple entries) + short rDNSparent_idx = APar_FindParentAtom(desiredAtom->AtomicNumber, + desiredAtom->AtomicLevel); + short last_child = APar_FindLastChild_of_ParentAtom(rDNSparent_idx); + AtomicInfo proto_rDNS_data_atom = {0}; + APar_CreateSurrogateAtom(&proto_rDNS_data_atom, + "data", + parsedAtoms[last_child].AtomicLevel, + VERSIONED_ATOM, + 0, + NULL, + 0); + desiredAtom = APar_CreateSparseAtom( + &proto_rDNS_data_atom, &parsedAtoms[rDNSparent_idx], last_child); + APar_MetaData_atom_QuickInit( + desiredAtom->AtomicNumber, *atomFlags, 0, MAXDATA_PAYLOAD); + } + } + APar_FlagMovieHeader(); + free(reverseDNS_atompath); + reverseDNS_atompath = NULL; + modified_atoms = true; + return desiredAtom; +} + +AtomicInfo *APar_ID32_atom_Init(const char *frameID_str, + char meta_area, + const char *lang_str, + uint16_t id32_lang) { + uint8_t total_tracks = 0; + uint8_t a_track = 0; // unused + AtomicInfo *meta_atom = NULL; + AtomicInfo *hdlr_atom = NULL; + char *id32_trackpath = NULL; + AtomicInfo *ID32_atom = NULL; + bool non_referenced_data = false; + bool remove_ID32_atom = false; + + APar_FindAtomInTrack(total_tracks, + a_track, + NULL); // With track_num set to 0, it will return the + // total trak atom into total_tracks here. + + if (meta_area > 0) { + if ((uint8_t)meta_area > total_tracks) { + APar_assert(false, 6, frameID_str); + return NULL; + } + } + + id32_trackpath = (char *)calloc(1, sizeof(char) * 100); + + if (meta_area == 0 - FILE_LEVEL_ATOM) { + meta_atom = APar_FindAtom("meta", false, DUAL_STATE_ATOM, 0); + } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) { + meta_atom = APar_FindAtom("moov.meta", false, DUAL_STATE_ATOM, 0); + //} else if (meta_area = 0) { + // setting id3tags for all tracks will *not* be supported; + } else if (meta_area > 0) { + sprintf(id32_trackpath, "moov.trak[%u].meta", meta_area); + meta_atom = APar_FindAtom(id32_trackpath, false, DUAL_STATE_ATOM, 0); + } + + if (meta_atom != NULL) { + hdlr_atom = APar_FindChildAtom(meta_atom->AtomicNumber, "hdlr"); + if (hdlr_atom != NULL) { + if (hdlr_atom->ancillary_data != 0x49443332) { + memset(id32_trackpath, + 0, + 5); // well, it won't be used for anything else since the handler + // type doesn't match, might as well convert the handler type + // using it + UInt32_TO_String4(hdlr_atom->ancillary_data, id32_trackpath); + APar_assert(false, 7, id32_trackpath); + free(id32_trackpath); + + return NULL; + } + } + } + + // its possible the ID32 atom targeted already exists - finding it in the + // traditional form (not external, and not locally referenced) is easiest. + // Locally referenced isn't. + if (meta_area == 0 - FILE_LEVEL_ATOM) { + ID32_atom = APar_FindAtom("meta.ID32", false, PACKED_LANG_ATOM, id32_lang); + } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) { + ID32_atom = + APar_FindAtom("moov.meta.ID32", false, PACKED_LANG_ATOM, id32_lang); + //} else if (meta_area = 0) { + // setting id3tags for all tracks will *not* be supported; + } else if (meta_area > 0) { + sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area); + ID32_atom = + APar_FindAtom(id32_trackpath, false, PACKED_LANG_ATOM, id32_lang); + } + + if (ID32_atom != NULL) { + free(id32_trackpath); + id32_trackpath = NULL; + if (ID32_atom->ID32_TagInfo == NULL) { + APar_ID32_ScanID3Tag(source_file, ID32_atom); + } + return ID32_atom; // and that completes finding the ID32 atom and verifying + // that it was local and in a traditionally represented + // form. + + } else { + if (meta_atom != NULL) { + // if the primary item atom is present, it points to either a local data + // reference (flag of 0x000001) or external data which is unsupported. + // Either way skip it. + //..or probably another test would be if a data REFERENCE atom were + // present.... but you would have to match item_IDs - which are found in + // pitm (required for ID32). + if (APar_FindChildAtom(meta_atom->AtomicNumber, "pitm") != NULL) { + non_referenced_data = false; + APar_assert(false, 8, frameID_str); + free(id32_trackpath); + return NULL; + } else { // the inline/3gpp 'ID32' atom calls for referenced content to + // carry a 'pitm' atom. No worries - its just a 'meta'. + non_referenced_data = true; + } + } else { + // no 'meta' atom? Great - a blank slate. There won't be any jumping + // through a multitude of atoms to determine referencing + non_referenced_data = true; + } + } + + if (frameID_str == NULL) { + remove_ID32_atom = true; + } else if (strlen(frameID_str) == 0) { + remove_ID32_atom = true; + } + // this only gets executed if a pre-existing satisfactory ID32 atom was not + // found. Being able to find it by atom.path by definition means it was not + // referenced. + if (non_referenced_data && !remove_ID32_atom) { + if (meta_atom == NULL) { + if (meta_area == 0 - FILE_LEVEL_ATOM) { + meta_atom = APar_FindAtom("meta", true, VERSIONED_ATOM, 0); + } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) { + meta_atom = APar_FindAtom("moov.meta", true, VERSIONED_ATOM, 0); + //} else if (meta_area = 0) { + // setting id3tags for all tracks will *not* be supported; + } else if (meta_area > 0) { + sprintf(id32_trackpath, "moov.trak[%u].meta", meta_area); + meta_atom = APar_FindAtom(id32_trackpath, true, VERSIONED_ATOM, 0); + } + } + + // create the required hdlr atom + if (hdlr_atom == NULL) { + if (meta_area == 0 - FILE_LEVEL_ATOM) { + hdlr_atom = APar_FindAtom("meta.hdlr", true, VERSIONED_ATOM, 0); + } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) { + hdlr_atom = APar_FindAtom("moov.meta.hdlr", true, VERSIONED_ATOM, 0); + //} else if (meta_area = 0) { + // setting id3tags for all tracks will *not* be supported; + } else if (meta_area > 0) { + sprintf(id32_trackpath, "moov.trak[%u].meta.hdlr", meta_area); + hdlr_atom = APar_FindAtom(id32_trackpath, true, VERSIONED_ATOM, 0); + } + if (hdlr_atom == NULL) { + fprintf(stdout, "Uh, problem\n"); + exit(0); + } + APar_MetaData_atom_QuickInit(hdlr_atom->AtomicNumber, 0, 0); + APar_Unified_atom_Put( + hdlr_atom, "ID32", UTF8_iTunesStyle_256glyphLimited, 0, 0); + APar_Unified_atom_Put( + hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); + APar_Unified_atom_Put( + hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); + APar_Unified_atom_Put( + hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32); + APar_Unified_atom_Put( + hdlr_atom, "AtomicParsley ID3v2 Handler", UTF8_3GP_Style, 0, 0); + hdlr_atom->ancillary_data = 0x49443332; + } + + // and finally create the ID32 atom + if (meta_area == 0 - FILE_LEVEL_ATOM) { + ID32_atom = APar_FindAtom("meta.ID32", true, PACKED_LANG_ATOM, id32_lang); + } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) { + ID32_atom = + APar_FindAtom("moov.meta.ID32", true, PACKED_LANG_ATOM, id32_lang); + //} else if (meta_area = 0) { + // setting id3tags for all tracks will *not* be supported; + } else if (meta_area > 0) { + sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area); + ID32_atom = + APar_FindAtom(id32_trackpath, true, PACKED_LANG_ATOM, id32_lang); + } + + if (id32_trackpath != NULL) { + free(id32_trackpath); + id32_trackpath = NULL; + } + + ID32_atom->AtomicLength = 12; // 4bytes atom length, 4 bytes atom length, 4 + // bytes version/flags (NULLs), 2 bytes lang + ID32_atom->AtomicVerFlags = 0; + ID32_atom->AtomicContainerState = CHILD_ATOM; + ID32_atom->AtomicClassification = PACKED_LANG_ATOM; + ID32_atom->AtomicLanguage = id32_lang; + + APar_ID3Tag_Init(ID32_atom); + // search for the desired frame + + // add the frame to an empty frame, copy data onto the id32_atom structure + // as required set modified _atoms to true + + return ID32_atom; + } else if (remove_ID32_atom) { + if (meta_area == 0 - FILE_LEVEL_ATOM) { + APar_RemoveAtom("meta.ID32", PACKED_LANG_ATOM, id32_lang); + } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) { + APar_RemoveAtom("moov.meta.ID32", PACKED_LANG_ATOM, id32_lang); + //} else if (meta_area = 0) { + // setting id3tags for all tracks will *not* be supported; + } else if (meta_area > 0) { + sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area); + APar_RemoveAtom(id32_trackpath, PACKED_LANG_ATOM, id32_lang); + } + } + return NULL; } void APar_RenderAllID32Atoms() { - short atom_idx = 0; - short meta_idx = -1; - //loop through each atom in the struct array (which holds the offset info/data) - while (true) { - if (memcmp(parsedAtoms[atom_idx].AtomicName, "ID32", 4) == 0) { - if (parsedAtoms[atom_idx].ID32_TagInfo != NULL) { - uint64_t id32tag_max_length = APar_GetTagSize(&parsedAtoms[atom_idx]); - if (id32tag_max_length > 0) { - parsedAtoms[atom_idx].AtomicData = (char*)calloc(1, sizeof(char)* id32tag_max_length + (16*parsedAtoms[atom_idx].ID32_TagInfo->ID3v2_FrameCount) ); - APar_Unified_atom_Put(&parsedAtoms[atom_idx], NULL, 0, parsedAtoms[atom_idx].AtomicLanguage, 16); - parsedAtoms[atom_idx].AtomicLength = APar_Render_ID32_Tag(&parsedAtoms[atom_idx], id32tag_max_length + (16*parsedAtoms[atom_idx].ID32_TagInfo->ID3v2_FrameCount) ) + 14; - if (parsedAtoms[atom_idx].AtomicLength < 12+10) { - meta_idx = APar_FindParentAtom(parsedAtoms[atom_idx].AtomicNumber, parsedAtoms[atom_idx].AtomicLevel); - APar_EliminateAtom(parsedAtoms[atom_idx].AtomicNumber, parsedAtoms[atom_idx].NextAtomNumber); - } else { - APar_FlagTrackHeader(&parsedAtoms[atom_idx]); - } - } else { - meta_idx = APar_FindParentAtom(parsedAtoms[atom_idx].AtomicNumber, parsedAtoms[atom_idx].AtomicLevel); - APar_EliminateAtom(parsedAtoms[atom_idx].AtomicNumber, parsedAtoms[atom_idx].NextAtomNumber); - } - } - } - if (meta_idx > 0) { - if (memcmp(parsedAtoms[meta_idx].AtomicName, "meta", 4) == 0) { - if ( APar_ReturnChildrenAtoms(meta_idx, 0) == 1 ) { - AtomicInfo* meta_handler = &parsedAtoms[ parsedAtoms[meta_idx].NextAtomNumber ]; - if (memcmp(meta_handler->AtomicName, "hdlr", 4) == 0 && meta_handler->ancillary_data == 0x49443332) { - APar_EliminateAtom(meta_idx, meta_handler->NextAtomNumber); - } - } - } - } - atom_idx = parsedAtoms[atom_idx].NextAtomNumber; - if (parsedAtoms[atom_idx].AtomicNumber == 0) break; - meta_idx = -1; - } - return; + short atom_idx = 0; + short meta_idx = -1; + // loop through each atom in the struct array (which holds the offset + // info/data) + while (true) { + if (memcmp(parsedAtoms[atom_idx].AtomicName, "ID32", 4) == 0) { + if (parsedAtoms[atom_idx].ID32_TagInfo != NULL) { + uint64_t id32tag_max_length = APar_GetTagSize(&parsedAtoms[atom_idx]); + if (id32tag_max_length > 0) { + parsedAtoms[atom_idx].AtomicData = (char *)calloc( + 1, + sizeof(char) * id32tag_max_length + + (16 * parsedAtoms[atom_idx].ID32_TagInfo->ID3v2_FrameCount)); + APar_Unified_atom_Put(&parsedAtoms[atom_idx], + NULL, + 0, + parsedAtoms[atom_idx].AtomicLanguage, + 16); + parsedAtoms[atom_idx].AtomicLength = + APar_Render_ID32_Tag( + &parsedAtoms[atom_idx], + id32tag_max_length + + (16 * + parsedAtoms[atom_idx].ID32_TagInfo->ID3v2_FrameCount)) + + 14; + if (parsedAtoms[atom_idx].AtomicLength < 12 + 10) { + meta_idx = APar_FindParentAtom(parsedAtoms[atom_idx].AtomicNumber, + parsedAtoms[atom_idx].AtomicLevel); + APar_EliminateAtom(parsedAtoms[atom_idx].AtomicNumber, + parsedAtoms[atom_idx].NextAtomNumber); + } else { + APar_FlagTrackHeader(&parsedAtoms[atom_idx]); + } + } else { + meta_idx = APar_FindParentAtom(parsedAtoms[atom_idx].AtomicNumber, + parsedAtoms[atom_idx].AtomicLevel); + APar_EliminateAtom(parsedAtoms[atom_idx].AtomicNumber, + parsedAtoms[atom_idx].NextAtomNumber); + } + } + } + if (meta_idx > 0) { + if (memcmp(parsedAtoms[meta_idx].AtomicName, "meta", 4) == 0) { + if (APar_ReturnChildrenAtoms(meta_idx, 0) == 1) { + AtomicInfo *meta_handler = + &parsedAtoms[parsedAtoms[meta_idx].NextAtomNumber]; + if (memcmp(meta_handler->AtomicName, "hdlr", 4) == 0 && + meta_handler->ancillary_data == 0x49443332) { + APar_EliminateAtom(meta_idx, meta_handler->NextAtomNumber); + } + } + } + } + atom_idx = parsedAtoms[atom_idx].NextAtomNumber; + if (parsedAtoms[atom_idx].AtomicNumber == 0) + break; + meta_idx = -1; + } + return; } /*---------------------- APar_TestVideoDescription - video_desc_atom - the avc1 atom after stsd that contains the height/width - ISObmff_file - the reopened source file - - read in the height, width, profile & level of an avc (non-drm) track. If the the macroblocks are between 300 & 1200, return a non-zero number to allow the ipod - uuid to be written + video_desc_atom - the avc1 atom after stsd that contains the +height/width ISObmff_file - the reopened source file - NOTE: this requires the deep scan cli flag to break out stsd from its normal monolithic form -----------------------*/ -uint16_t APar_TestVideoDescription(AtomicInfo* video_desc_atom, FILE* ISObmff_file) { - uint16_t video_width = 0; - uint16_t video_height = 0; - uint16_t video_macroblocks = 0; - uint8_t video_profile = 0; - uint8_t video_level = 0; - AtomicInfo* avcC_atom = NULL; - - if (ISObmff_file == NULL) return 0; - - char* avc1_contents = (char*)calloc(1, sizeof(char)* (size_t)video_desc_atom->AtomicLength); - if (avc1_contents == NULL) { - fclose(ISObmff_file); - return 0; - } - - APar_readX(avc1_contents, ISObmff_file, video_desc_atom->AtomicStart, video_desc_atom->AtomicLength); //actually reads in avcC as well, but is unused - video_width = UInt16FromBigEndian(avc1_contents+32); // well, iTunes only allows 640 max but the avc wiki says it *could* go up to 720, so I won't bother to check it - video_height = UInt16FromBigEndian(avc1_contents+34); - video_macroblocks = (video_width / 16) * (video_height / 16); - - avcC_atom = APar_FindChildAtom(video_desc_atom->AtomicNumber, "avcC"); - if (avcC_atom != NULL) { - uint64_t avcC_offset = avcC_atom->AtomicStart - video_desc_atom->AtomicStart; - video_profile = *(avc1_contents+avcC_offset+9); - video_level = *(avc1_contents+avcC_offset+11); - } - - if (video_profile == 66 && video_level <= 30) { - if (video_macroblocks > 300 && video_macroblocks <= 1200) { - - if (video_level <= 30 && avcC_atom != NULL) { - avcC_atom->AtomicData = (char*)calloc(1, sizeof(char)* (size_t)avcC_atom->AtomicLength); - APar_readX(avcC_atom->AtomicData, ISObmff_file, avcC_atom->AtomicStart+8, avcC_atom->AtomicLength-8); - if (video_macroblocks > 396 && video_macroblocks <= 792) { - *(avcC_atom->AtomicData + 3) = 21; - } else if (video_macroblocks > 792) { - *(avcC_atom->AtomicData + 3) = 22; - } - } - - fclose(ISObmff_file); - return video_macroblocks; - } else { - fprintf(stdout, "AtomicParsley warning: the AVC track macroblocks were not in the required range (300-1200). Skipping.\n"); - } - } else { - fprintf(stdout, "AtomicParsley warning: the AVC track profile/level was too high. The ipod hi-res uuid was not added.\n"); - } + read in the height, width, profile & level of an avc (non-drm) track. If the +the macroblocks are between 300 & 1200, return a non-zero number to allow the +ipod uuid to be written + + NOTE: this requires the deep scan cli flag to break out stsd from its normal +monolithic form +----------------------*/ +uint16_t APar_TestVideoDescription(AtomicInfo *video_desc_atom, + FILE *ISObmff_file) { + uint16_t video_width = 0; + uint16_t video_height = 0; + uint16_t video_macroblocks = 0; + uint8_t video_profile = 0; + uint8_t video_level = 0; + AtomicInfo *avcC_atom = NULL; + + if (ISObmff_file == NULL) + return 0; + + char *avc1_contents = + (char *)calloc(1, sizeof(char) * (size_t)video_desc_atom->AtomicLength); + if (avc1_contents == NULL) { + fclose(ISObmff_file); + return 0; + } + + APar_readX( + avc1_contents, + ISObmff_file, + video_desc_atom->AtomicStart, + video_desc_atom + ->AtomicLength); // actually reads in avcC as well, but is unused + video_width = UInt16FromBigEndian( + avc1_contents + + 32); // well, iTunes only allows 640 max but the avc wiki says it *could* + // go up to 720, so I won't bother to check it + video_height = UInt16FromBigEndian(avc1_contents + 34); + video_macroblocks = (video_width / 16) * (video_height / 16); + + avcC_atom = APar_FindChildAtom(video_desc_atom->AtomicNumber, "avcC"); + if (avcC_atom != NULL) { + uint64_t avcC_offset = + avcC_atom->AtomicStart - video_desc_atom->AtomicStart; + video_profile = *(avc1_contents + avcC_offset + 9); + video_level = *(avc1_contents + avcC_offset + 11); + } + + if (video_profile == 66 && video_level <= 30) { + if (video_macroblocks > 300 && video_macroblocks <= 1200) { + + if (video_level <= 30 && avcC_atom != NULL) { + avcC_atom->AtomicData = + (char *)calloc(1, sizeof(char) * (size_t)avcC_atom->AtomicLength); + APar_readX(avcC_atom->AtomicData, + ISObmff_file, + avcC_atom->AtomicStart + 8, + avcC_atom->AtomicLength - 8); + if (video_macroblocks > 396 && video_macroblocks <= 792) { + *(avcC_atom->AtomicData + 3) = 21; + } else if (video_macroblocks > 792) { + *(avcC_atom->AtomicData + 3) = 22; + } + } + + fclose(ISObmff_file); + return video_macroblocks; + } else { + fprintf(stdout, + "AtomicParsley warning: the AVC track macroblocks were " + "not in the required range (300-1200). Skipping.\n"); + } + } else { + fprintf(stdout, + "AtomicParsley warning: the AVC track profile/level was " + "too high. The ipod hi-res uuid was not added.\n"); + } - fclose(ISObmff_file); - return 0; + fclose(ISObmff_file); + return 0; } /*---------------------- APar_TestVideoDescription - atom_path - pointer to the string containing the atom.path already targeted to the right track + atom_path - pointer to the string containing the atom.path already +targeted to the right track - Find/Create the ipod hi-res (1200 macroblock) uuid for the avc1 track & set up its default parameters + Find/Create the ipod hi-res (1200 macroblock) uuid for the avc1 +track & set up its default parameters - NOTE: this requires the deep scan cli flag to break out stsd from its normal monolithic form + NOTE: this requires the deep scan cli flag to break out stsd from its normal +monolithic form ----------------------*/ -void APar_Generate_iPod_uuid(char* atom_path) { - AtomicInfo* ipod_uuid_atom = NULL; - - ipod_uuid_atom = APar_FindAtom(atom_path, false, EXTENDED_ATOM, 0, true); - if (ipod_uuid_atom == NULL) { - ipod_uuid_atom = APar_FindAtom(atom_path, true, EXTENDED_ATOM, 0, true); - if (ipod_uuid_atom == NULL) { - fprintf(stdout, "An error occured trying to create the ipod uuid atom for the avc track\n"); - return; - } - ipod_uuid_atom->AtomicData = (char*)calloc(1, sizeof(char)* 60 ); - ipod_uuid_atom->AtomicContainerState = CHILD_ATOM; - ipod_uuid_atom->AtomicClassification = EXTENDED_ATOM; - ipod_uuid_atom->uuid_style = UUID_OTHER; - ipod_uuid_atom->AtomicLength = 24; - APar_Unified_atom_Put(ipod_uuid_atom, NULL, UTF8_iTunesStyle_Unlimited, 1, 32); - modified_atoms = true; - - APar_FlagTrackHeader(ipod_uuid_atom); - APar_FlagMovieHeader(); - - track_codecs.has_avc1 = true; //only used on Mac OS X when setting the ipod uuid *only* (otherwise it gets set properly) - } else { - fprintf(stdout, "the ipod higher-resolution uuid is already present.\n"); - } +void APar_Generate_iPod_uuid(char *atom_path) { + AtomicInfo *ipod_uuid_atom = NULL; + + ipod_uuid_atom = APar_FindAtom(atom_path, false, EXTENDED_ATOM, 0, true); + if (ipod_uuid_atom == NULL) { + ipod_uuid_atom = APar_FindAtom(atom_path, true, EXTENDED_ATOM, 0, true); + if (ipod_uuid_atom == NULL) { + fprintf(stdout, + "An error occured trying to create the ipod uuid atom " + "for the avc track\n"); + return; + } + ipod_uuid_atom->AtomicData = (char *)calloc(1, sizeof(char) * 60); + ipod_uuid_atom->AtomicContainerState = CHILD_ATOM; + ipod_uuid_atom->AtomicClassification = EXTENDED_ATOM; + ipod_uuid_atom->uuid_style = UUID_OTHER; + ipod_uuid_atom->AtomicLength = 24; + APar_Unified_atom_Put( + ipod_uuid_atom, NULL, UTF8_iTunesStyle_Unlimited, 1, 32); + modified_atoms = true; + + APar_FlagTrackHeader(ipod_uuid_atom); + APar_FlagMovieHeader(); + + track_codecs.has_avc1 = + true; // only used on Mac OS X when setting the ipod + // uuid *only* (otherwise it gets set properly) + } else { + fprintf(stdout, "the ipod higher-resolution uuid is already present.\n"); + } - return; + return; } /////////////////////////////////////////////////////////////////////////////////////// -// offset calculations // +// offset calculations // /////////////////////////////////////////////////////////////////////////////////////// -//determine if our mdat atom has moved at all... +// determine if our mdat atom has moved at all... uint64_t APar_DetermineMediaData_AtomPosition() { - uint64_t mdat_position = 0; - short thisAtomNumber = 0; + uint64_t mdat_position = 0; + short thisAtomNumber = 0; - //loop through each atom in the struct array (which holds the offset info/data) - while (parsedAtoms[thisAtomNumber].NextAtomNumber != 0) { - - if ( strncmp(parsedAtoms[thisAtomNumber].AtomicName, "mdat", 4) == 0 && parsedAtoms[thisAtomNumber].AtomicLevel == 1 ) { - if (parsedAtoms[thisAtomNumber].AtomicLength <= 1 || parsedAtoms[thisAtomNumber].AtomicLength > 75) { - break; - } - } else if (parsedAtoms[thisAtomNumber].AtomicLevel == 1 && parsedAtoms[thisAtomNumber].AtomicLengthExtended == 0) { - mdat_position +=parsedAtoms[thisAtomNumber].AtomicLength; - } else { - //part of the pseudo 64-bit support - mdat_position +=parsedAtoms[thisAtomNumber].AtomicLengthExtended; - } - thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber; - } - return mdat_position; + // loop through each atom in the struct array (which holds the offset + // info/data) + while (parsedAtoms[thisAtomNumber].NextAtomNumber != 0) { + + if (strncmp(parsedAtoms[thisAtomNumber].AtomicName, "mdat", 4) == 0 && + parsedAtoms[thisAtomNumber].AtomicLevel == 1) { + if (parsedAtoms[thisAtomNumber].AtomicLength <= 1 || + parsedAtoms[thisAtomNumber].AtomicLength > 75) { + break; + } + } else if (parsedAtoms[thisAtomNumber].AtomicLevel == 1 && + parsedAtoms[thisAtomNumber].AtomicLengthExtended == 0) { + mdat_position += parsedAtoms[thisAtomNumber].AtomicLength; + } else { + // part of the pseudo 64-bit support + mdat_position += parsedAtoms[thisAtomNumber].AtomicLengthExtended; + } + thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber; + } + return mdat_position; } uint32_t APar_SimpleSumAtoms(short stop_atom) { - uint32_t byte_sum = 0; - //first, find the first mdat after this initial 'tfhd' atom to get the sum relative to that atom - while (true) { - if ( strncmp(parsedAtoms[stop_atom].AtomicName, "mdat", 4) == 0) { - stop_atom--; //don't include the fragment's mdat, just the atoms prior to it - break; - } else { - if (parsedAtoms[stop_atom].NextAtomNumber != 0) { - stop_atom = parsedAtoms[stop_atom].NextAtomNumber; - } else { - break; - } - } - } - byte_sum += 8; //the 'tfhd' points to the byte in mdat where the fragment data is - NOT the atom itself (should always be +8bytes with a fragment) - while (true) { - if (parsedAtoms[stop_atom].AtomicLevel == 1) { - byte_sum+= (parsedAtoms[stop_atom].AtomicLength == 1 ? parsedAtoms[stop_atom].AtomicLengthExtended : parsedAtoms[stop_atom].AtomicLength); - //fprintf(stdout, "%i %s (%" PRIu64 ")\n", stop_atom, parsedAtoms[stop_atom].AtomicName, parsedAtoms[stop_atom].AtomicLength); - } - if (stop_atom == 0) { - break; - } else { - stop_atom = APar_FindPrecedingAtom(stop_atom); - } - } - return byte_sum; + uint32_t byte_sum = 0; + // first, find the first mdat after this initial 'tfhd' atom to get the sum + // relative to that atom + while (true) { + if (strncmp(parsedAtoms[stop_atom].AtomicName, "mdat", 4) == 0) { + stop_atom--; // don't include the fragment's mdat, just the atoms prior to + // it + break; + } else { + if (parsedAtoms[stop_atom].NextAtomNumber != 0) { + stop_atom = parsedAtoms[stop_atom].NextAtomNumber; + } else { + break; + } + } + } + byte_sum += + 8; // the 'tfhd' points to the byte in mdat where the fragment data is - + // NOT the atom itself (should always be +8bytes with a fragment) + while (true) { + if (parsedAtoms[stop_atom].AtomicLevel == 1) { + byte_sum += (parsedAtoms[stop_atom].AtomicLength == 1 + ? parsedAtoms[stop_atom].AtomicLengthExtended + : parsedAtoms[stop_atom].AtomicLength); + // fprintf(stdout, "%i %s (%" PRIu64 ")\n", stop_atom, + // parsedAtoms[stop_atom].AtomicName, + // parsedAtoms[stop_atom].AtomicLength); + } + if (stop_atom == 0) { + break; + } else { + stop_atom = APar_FindPrecedingAtom(stop_atom); + } + } + return byte_sum; } /*---------------------- APar_QuickSumAtomicLengths - target_atom - pointer to the atom that will have its position within the *new* file determined; NOTE: only level 1 or level 2 atoms in moov + target_atom - pointer to the atom that will have its position within the +*new* file determined; NOTE: only level 1 or level 2 atoms in moov fill ----------------------*/ -uint64_t APar_QuickSumAtomicLengths(AtomicInfo* target_atom) { - uint64_t atom_pos = 0; - short atom_idx = 0; - short current_level = 0; - if (target_atom == NULL) return atom_pos; - if (target_atom->AtomicLevel > 2) return atom_pos; - - atom_idx = APar_FindPrecedingAtom(target_atom->AtomicNumber); - current_level = target_atom->AtomicLevel; - - while (true) { - if (parsedAtoms[atom_idx].AtomicLevel <= target_atom->AtomicLevel) { - if (parsedAtoms[atom_idx].AtomicContainerState >= DUAL_STATE_ATOM || parsedAtoms[atom_idx].AtomicLevel == 2) { - atom_pos += (parsedAtoms[atom_idx].AtomicLength == 1 ? parsedAtoms[atom_idx].AtomicLengthExtended : parsedAtoms[atom_idx].AtomicLength); - } else if (parsedAtoms[atom_idx].AtomicContainerState <= SIMPLE_PARENT_ATOM) { - if (target_atom->AtomicLevel == 1) { - atom_pos += (parsedAtoms[atom_idx].AtomicLength == 1 ? parsedAtoms[atom_idx].AtomicLengthExtended : parsedAtoms[atom_idx].AtomicLength); - } else { - atom_pos += 8; - } - } - } - if (atom_idx == 0) break; - atom_idx = APar_FindPrecedingAtom(atom_idx); - } - return atom_pos; +uint64_t APar_QuickSumAtomicLengths(AtomicInfo *target_atom) { + uint64_t atom_pos = 0; + short atom_idx = 0; + short current_level = 0; + if (target_atom == NULL) + return atom_pos; + if (target_atom->AtomicLevel > 2) + return atom_pos; + + atom_idx = APar_FindPrecedingAtom(target_atom->AtomicNumber); + current_level = target_atom->AtomicLevel; + + while (true) { + if (parsedAtoms[atom_idx].AtomicLevel <= target_atom->AtomicLevel) { + if (parsedAtoms[atom_idx].AtomicContainerState >= DUAL_STATE_ATOM || + parsedAtoms[atom_idx].AtomicLevel == 2) { + atom_pos += (parsedAtoms[atom_idx].AtomicLength == 1 + ? parsedAtoms[atom_idx].AtomicLengthExtended + : parsedAtoms[atom_idx].AtomicLength); + } else if (parsedAtoms[atom_idx].AtomicContainerState <= + SIMPLE_PARENT_ATOM) { + if (target_atom->AtomicLevel == 1) { + atom_pos += (parsedAtoms[atom_idx].AtomicLength == 1 + ? parsedAtoms[atom_idx].AtomicLengthExtended + : parsedAtoms[atom_idx].AtomicLength); + } else { + atom_pos += 8; + } + } + } + if (atom_idx == 0) + break; + atom_idx = APar_FindPrecedingAtom(atom_idx); + } + return atom_pos; } /*---------------------- APar_Constituent_mdat_data - desired_data_pos - the position in the file where the desired data begins - desired_data_len - the length of the desired data contained within the file + desired_data_pos - the position in the file where the desired data +begins desired_data_len - the length of the desired data contained within the +file fill ----------------------*/ -AtomicInfo* APar_Constituent_mdat_data(uint64_t desired_data_pos, uint64_t desired_data_len) { - AtomicInfo* target_mdat = NULL; - short eval_atom = 0; - - while (parsedAtoms[eval_atom].NextAtomNumber != 0) { - - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0 && parsedAtoms[eval_atom].AtomicLevel == 1 ) { - if (parsedAtoms[eval_atom].AtomicLength == 1) { - if ( (parsedAtoms[eval_atom].AtomicStart + parsedAtoms[eval_atom].AtomicLengthExtended >= desired_data_pos + desired_data_len) && - parsedAtoms[eval_atom].AtomicStart < desired_data_pos) { - target_mdat = &parsedAtoms[eval_atom]; - break; - } - } else { - if ( (parsedAtoms[eval_atom].AtomicStart + parsedAtoms[eval_atom].AtomicLength >= desired_data_pos + desired_data_len) && - parsedAtoms[eval_atom].AtomicStart < desired_data_pos) { - target_mdat = &parsedAtoms[eval_atom]; - break; - } - } - } - eval_atom = parsedAtoms[eval_atom].NextAtomNumber; - } - return target_mdat; +AtomicInfo *APar_Constituent_mdat_data(uint64_t desired_data_pos, + uint64_t desired_data_len) { + AtomicInfo *target_mdat = NULL; + short eval_atom = 0; + + while (parsedAtoms[eval_atom].NextAtomNumber != 0) { + + if (memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0 && + parsedAtoms[eval_atom].AtomicLevel == 1) { + if (parsedAtoms[eval_atom].AtomicLength == 1) { + if ((parsedAtoms[eval_atom].AtomicStart + + parsedAtoms[eval_atom].AtomicLengthExtended >= + desired_data_pos + desired_data_len) && + parsedAtoms[eval_atom].AtomicStart < desired_data_pos) { + target_mdat = &parsedAtoms[eval_atom]; + break; + } + } else { + if ((parsedAtoms[eval_atom].AtomicStart + + parsedAtoms[eval_atom].AtomicLength >= + desired_data_pos + desired_data_len) && + parsedAtoms[eval_atom].AtomicStart < desired_data_pos) { + target_mdat = &parsedAtoms[eval_atom]; + break; + } + } + } + eval_atom = parsedAtoms[eval_atom].NextAtomNumber; + } + return target_mdat; } /*---------------------- APar_Readjust_iloc_atom - iloc_number - the target iloc atom index in parsedAtoms + iloc_number - the target iloc atom index in parsedAtoms fill ----------------------*/ bool APar_Readjust_iloc_atom(short iloc_number) { - bool iloc_changed = false; - - parsedAtoms[iloc_number].AtomicData = (char*)calloc(1, sizeof(char)* (size_t)(parsedAtoms[iloc_number].AtomicLength) ); - APar_readX(parsedAtoms[iloc_number].AtomicData, source_file, parsedAtoms[iloc_number].AtomicStart+12, parsedAtoms[iloc_number].AtomicLength-12); + bool iloc_changed = false; - uint8_t offset_size = ( *parsedAtoms[iloc_number].AtomicData >> 4) & 0x0F; - uint8_t length_size = *parsedAtoms[iloc_number].AtomicData & 0x0F; - uint8_t base_offset_size = ( *(parsedAtoms[iloc_number].AtomicData+1) >> 4) & 0x0F; - uint16_t item_count = UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData+2); - uint64_t aggregate_offset = 4; - char* base_offset_ptr = NULL; + parsedAtoms[iloc_number].AtomicData = (char *)calloc( + 1, sizeof(char) * (size_t)(parsedAtoms[iloc_number].AtomicLength)); + APar_readX(parsedAtoms[iloc_number].AtomicData, + source_file, + parsedAtoms[iloc_number].AtomicStart + 12, + parsedAtoms[iloc_number].AtomicLength - 12); + + uint8_t offset_size = (*parsedAtoms[iloc_number].AtomicData >> 4) & 0x0F; + uint8_t length_size = *parsedAtoms[iloc_number].AtomicData & 0x0F; + uint8_t base_offset_size = + (*(parsedAtoms[iloc_number].AtomicData + 1) >> 4) & 0x0F; + uint16_t item_count = + UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData + 2); + uint64_t aggregate_offset = 4; + char *base_offset_ptr = NULL; #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_Readjust_iloc_atom Offset %X, len %X, base %X, item count %u\n", offset_size, length_size, base_offset_size, item_count); + fprintf(stdout, + "debug: AP_Readjust_iloc_atom Offset %X, len %X, base %X, item " + "count %u\n", + offset_size, + length_size, + base_offset_size, + item_count); #endif - for (uint16_t an_item=1; an_item <= item_count; an_item++) { + for (uint16_t an_item = 1; an_item <= item_count; an_item++) { #ifdef DEBUG_V - uint16_t an_item_ID = + uint16_t an_item_ID = #endif - UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset); - uint16_t a_data_ref_idx = UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset+2); - uint64_t base_offset = 0; - uint64_t curr_container_pos = 0; - uint64_t extent_len_sum = 0; - - aggregate_offset +=4; - - if (a_data_ref_idx != 0) { - continue; - } - - if (base_offset_size == 4 || base_offset_size == 8) { - base_offset_ptr = parsedAtoms[iloc_number].AtomicData+aggregate_offset; - if (base_offset_size == 4) { - base_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset); - aggregate_offset +=4; - } else { - base_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset); - aggregate_offset +=8; - } - } - - if (base_offset > 0) { - uint16_t this_item_extent_count = UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset); - aggregate_offset +=2; - - for (uint16_t an_extent=1; an_extent <= this_item_extent_count; an_extent++) { - uint64_t this_extent_offset = 0; - uint64_t this_extent_length = 0; - - if (offset_size == 4 || offset_size == 8) { - if (offset_size == 4) { - this_extent_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset); - aggregate_offset +=4; - } else { - this_extent_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset); - aggregate_offset +=8; - } - } - if (length_size == 4 || length_size == 8) { - if (length_size == 4) { - this_extent_length = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset); - aggregate_offset +=4; - } else { - this_extent_length = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset); - aggregate_offset +=8; - } - extent_len_sum+= this_extent_length; - } - } //for loop extent + UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData + + aggregate_offset); + uint16_t a_data_ref_idx = UInt16FromBigEndian( + parsedAtoms[iloc_number].AtomicData + aggregate_offset + 2); + uint64_t base_offset = 0; + uint64_t curr_container_pos = 0; + uint64_t extent_len_sum = 0; + + aggregate_offset += 4; + + if (a_data_ref_idx != 0) { + continue; + } + + if (base_offset_size == 4 || base_offset_size == 8) { + base_offset_ptr = parsedAtoms[iloc_number].AtomicData + aggregate_offset; + if (base_offset_size == 4) { + base_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData + + aggregate_offset); + aggregate_offset += 4; + } else { + base_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData + + aggregate_offset); + aggregate_offset += 8; + } + } + + if (base_offset > 0) { + uint16_t this_item_extent_count = UInt16FromBigEndian( + parsedAtoms[iloc_number].AtomicData + aggregate_offset); + aggregate_offset += 2; + + for (uint16_t an_extent = 1; an_extent <= this_item_extent_count; + an_extent++) { + uint64_t this_extent_offset = 0; + uint64_t this_extent_length = 0; + + if (offset_size == 4 || offset_size == 8) { + if (offset_size == 4) { + this_extent_offset = UInt32FromBigEndian( + parsedAtoms[iloc_number].AtomicData + aggregate_offset); + aggregate_offset += 4; + } else { + this_extent_offset = UInt32FromBigEndian( + parsedAtoms[iloc_number].AtomicData + aggregate_offset); + aggregate_offset += 8; + } + } + if (length_size == 4 || length_size == 8) { + if (length_size == 4) { + this_extent_length = UInt32FromBigEndian( + parsedAtoms[iloc_number].AtomicData + aggregate_offset); + aggregate_offset += 4; + } else { + this_extent_length = UInt32FromBigEndian( + parsedAtoms[iloc_number].AtomicData + aggregate_offset); + aggregate_offset += 8; + } + extent_len_sum += this_extent_length; + } + } // for loop extent #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_Readjust_iloc_atom iloc's %u index at base offset: %" PRIu64 ", total bytes %" PRIu64 "\n", an_item_ID, base_offset, extent_len_sum); + fprintf(stdout, + "debug: AP_Readjust_iloc_atom iloc's %u index at base offset: " + "%" PRIu64 ", total bytes %" PRIu64 "\n", + an_item_ID, + base_offset, + extent_len_sum); #endif - AtomicInfo* container_atom = APar_Constituent_mdat_data(base_offset, 0x013077 ); + AtomicInfo *container_atom = + APar_Constituent_mdat_data(base_offset, 0x013077); - if (container_atom != NULL) { - curr_container_pos = APar_QuickSumAtomicLengths(container_atom); - uint64_t exisiting_offset_into_atom = base_offset - container_atom->AtomicStart; - uint64_t new_item_offset = curr_container_pos + exisiting_offset_into_atom; + if (container_atom != NULL) { + curr_container_pos = APar_QuickSumAtomicLengths(container_atom); + uint64_t exisiting_offset_into_atom = + base_offset - container_atom->AtomicStart; + uint64_t new_item_offset = + curr_container_pos + exisiting_offset_into_atom; #if defined(DEBUG_V) - fprintf(stdout, "debug: AP_Readjust_iloc_atom item is contained on mdat started @ %" PRIu64 " (now at %" PRIu64 ")\n", container_atom->AtomicStart, curr_container_pos); - fprintf(stdout, "debug: AP_Readjust_iloc_atom item is %" PRIu64 " bytes offset into atom (was %" PRIu64 ", now %" PRIu64 ")\n", exisiting_offset_into_atom, base_offset, new_item_offset); + fprintf(stdout, + "debug: AP_Readjust_iloc_atom item is contained on mdat " + "started @ %" PRIu64 " (now at %" PRIu64 ")\n", + container_atom->AtomicStart, + curr_container_pos); + fprintf(stdout, + "debug: AP_Readjust_iloc_atom item is %" PRIu64 + " bytes offset into atom (was %" PRIu64 ", now %" PRIu64 ")\n", + exisiting_offset_into_atom, + base_offset, + new_item_offset); #endif - if (base_offset_size == 4) { - UInt32_TO_String4(new_item_offset, base_offset_ptr); - } else { - UInt64_TO_String8(new_item_offset, base_offset_ptr); - } - iloc_changed = true; - } - } - } + if (base_offset_size == 4) { + UInt32_TO_String4(new_item_offset, base_offset_ptr); + } else { + UInt64_TO_String8(new_item_offset, base_offset_ptr); + } + iloc_changed = true; + } + } + } - return iloc_changed; + return iloc_changed; } bool APar_Readjust_CO64_atom(uint64_t mdat_position, short co64_number) { - bool co64_changed = false; + bool co64_changed = false; - if (parsedAtoms[co64_number].ancillary_data != 1) { - return co64_changed; - } - - parsedAtoms[co64_number].AtomicData = (char*)calloc(1, sizeof(char)* (size_t)(parsedAtoms[co64_number].AtomicLength) ); - APar_readX(parsedAtoms[co64_number].AtomicData, source_file, parsedAtoms[co64_number].AtomicStart+12, parsedAtoms[co64_number].AtomicLength-12); - - parsedAtoms[co64_number].AtomicVerFlags = 0; - bool deduct = false; - //readjust - - char* co64_entries = (char *)malloc(sizeof(char)*4 + 1); - memset(co64_entries, 0, sizeof(char)*4 + 1); - - memcpy(co64_entries, parsedAtoms[co64_number].AtomicData, 4); - uint32_t entries = UInt32FromBigEndian(co64_entries); - - char* a_64bit_entry = (char *)malloc(sizeof(char)*8 + 1); - memset(a_64bit_entry, 0, sizeof(char)*8 + 1); - - for(uint32_t i=1; i<=entries; i++) { - //read 8 bytes of the atom into a 8 char uint64_t a_64bit_entry to eval it - for (int c = 0; c <=7; c++ ) { - //first co64 entry (32-bit uint32_t) is the number of entries; every other one is an actual offset value - a_64bit_entry[c] = parsedAtoms[co64_number].AtomicData[4 + (i-1)*8 + c]; - } - uint64_t this_entry = UInt64FromBigEndian(a_64bit_entry); - - if (i == 1 && mdat_supplemental_offset == 0) { //for the first chunk, and only for the first *ever* entry, make the global mdat supplemental offset - if (this_entry - removed_bytes_tally > mdat_position) { - mdat_supplemental_offset = (uint64_t)mdat_position - ((uint64_t)this_entry - (uint64_t)removed_bytes_tally); - bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally; - deduct=true; - } else { - mdat_supplemental_offset = mdat_position - (this_entry - removed_bytes_tally); - bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally; - } - - if (mdat_supplemental_offset == 0) { - break; - } - } - - if (mdat_supplemental_offset != 0) { - co64_changed = true; - } - - if (deduct) { //crap, uint32_t's were so nice to flip over by themselves to subtract nicely. going from 32-bit to 64-bit prevents that flipping - this_entry += mdat_supplemental_offset - (bytes_into_mdat * -1); // + bytes_into_mdat; - } else { - this_entry += mdat_supplemental_offset + bytes_into_mdat; //this is where we add our new mdat offset difference - } - UInt64_TO_String8(this_entry, a_64bit_entry); - //and put the data back into AtomicData... - for (int d = 0; d <=7; d++ ) { - //first stco entry is the number of entries; every other one is an actual offset value - parsedAtoms[co64_number].AtomicData[4 + (i-1)*8 + d] = a_64bit_entry[d]; - } - } - - free(a_64bit_entry); - free(co64_entries); - a_64bit_entry=NULL; - co64_entries=NULL; - //end readjustment - return co64_changed; -} - -bool APar_Readjust_TFHD_fragment_atom(uint64_t mdat_position, short tfhd_number) { - static bool tfhd_changed = false; - static bool determined_offset = false; - static uint64_t base_offset = 0; - - parsedAtoms[tfhd_number].AtomicData = (char*)calloc(1, sizeof(char)* (size_t)(parsedAtoms[tfhd_number].AtomicLength) ); - APar_readX(parsedAtoms[tfhd_number].AtomicData, source_file, parsedAtoms[tfhd_number].AtomicStart+12, parsedAtoms[tfhd_number].AtomicLength-12); - - char* tfhd_atomFlags_scrap = (char *)malloc(sizeof(char)*10); - memset(tfhd_atomFlags_scrap, 0, 10); - //parsedAtoms[tfhd_number].AtomicVerFlags = APar_read32(tfhd_atomFlags_scrap, source_file, parsedAtoms[tfhd_number].AtomicStart+8); - - if (parsedAtoms[tfhd_number].AtomicVerFlags & 0x01) { - // seems the atomflags suggest bitpacking, but the spec doesn't specify it; - // if the 1st bit is set... + if (parsedAtoms[co64_number].ancillary_data != 1) { + return co64_changed; + } + + parsedAtoms[co64_number].AtomicData = (char *)calloc( + 1, sizeof(char) * (size_t)(parsedAtoms[co64_number].AtomicLength)); + APar_readX(parsedAtoms[co64_number].AtomicData, + source_file, + parsedAtoms[co64_number].AtomicStart + 12, + parsedAtoms[co64_number].AtomicLength - 12); + + parsedAtoms[co64_number].AtomicVerFlags = 0; + bool deduct = false; + // readjust + + char *co64_entries = (char *)malloc(sizeof(char) * 4 + 1); + memset(co64_entries, 0, sizeof(char) * 4 + 1); + + memcpy(co64_entries, parsedAtoms[co64_number].AtomicData, 4); + uint32_t entries = UInt32FromBigEndian(co64_entries); + + char *a_64bit_entry = (char *)malloc(sizeof(char) * 8 + 1); + memset(a_64bit_entry, 0, sizeof(char) * 8 + 1); + + for (uint32_t i = 1; i <= entries; i++) { + // read 8 bytes of the atom into a 8 char uint64_t a_64bit_entry to eval it + for (int c = 0; c <= 7; c++) { + // first co64 entry (32-bit uint32_t) is the number of entries; every + // other one is an actual offset value + a_64bit_entry[c] = + parsedAtoms[co64_number].AtomicData[4 + (i - 1) * 8 + c]; + } + uint64_t this_entry = UInt64FromBigEndian(a_64bit_entry); + + if (i == 1 && mdat_supplemental_offset == + 0) { // for the first chunk, and only for the first *ever* + // entry, make the global mdat supplemental offset + if (this_entry - removed_bytes_tally > mdat_position) { + mdat_supplemental_offset = + (uint64_t)mdat_position - + ((uint64_t)this_entry - (uint64_t)removed_bytes_tally); + bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally; + deduct = true; + } else { + mdat_supplemental_offset = + mdat_position - (this_entry - removed_bytes_tally); + bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally; + } + + if (mdat_supplemental_offset == 0) { + break; + } + } + + if (mdat_supplemental_offset != 0) { + co64_changed = true; + } + + if (deduct) { // crap, uint32_t's were so nice to flip over by themselves to + // subtract nicely. going from 32-bit to 64-bit prevents that + // flipping + this_entry += mdat_supplemental_offset - + (bytes_into_mdat * -1); // + bytes_into_mdat; + } else { + this_entry += mdat_supplemental_offset + + bytes_into_mdat; // this is where we add our new mdat offset + // difference + } + UInt64_TO_String8(this_entry, a_64bit_entry); + // and put the data back into AtomicData... + for (int d = 0; d <= 7; d++) { + // first stco entry is the number of entries; every other one is an actual + // offset value + parsedAtoms[co64_number].AtomicData[4 + (i - 1) * 8 + d] = + a_64bit_entry[d]; + } + } + + free(a_64bit_entry); + free(co64_entries); + a_64bit_entry = NULL; + co64_entries = NULL; + // end readjustment + return co64_changed; +} + +bool APar_Readjust_TFHD_fragment_atom(uint64_t mdat_position, + short tfhd_number) { + static bool tfhd_changed = false; + static bool determined_offset = false; + static uint64_t base_offset = 0; + + parsedAtoms[tfhd_number].AtomicData = (char *)calloc( + 1, sizeof(char) * (size_t)(parsedAtoms[tfhd_number].AtomicLength)); + APar_readX(parsedAtoms[tfhd_number].AtomicData, + source_file, + parsedAtoms[tfhd_number].AtomicStart + 12, + parsedAtoms[tfhd_number].AtomicLength - 12); + + char *tfhd_atomFlags_scrap = (char *)malloc(sizeof(char) * 10); + memset(tfhd_atomFlags_scrap, 0, 10); + // parsedAtoms[tfhd_number].AtomicVerFlags = APar_read32(tfhd_atomFlags_scrap, + // source_file, parsedAtoms[tfhd_number].AtomicStart+8); + + if (parsedAtoms[tfhd_number].AtomicVerFlags & 0x01) { + // seems the atomflags suggest bitpacking, but the spec doesn't specify it; + // if the 1st bit is set... #if 0 /* not used */ memset(tfhd_atomFlags_scrap, 0, 10); memcpy(tfhd_atomFlags_scrap, parsedAtoms[tfhd_number].AtomicData, 4); uint32_t track_ID = UInt32FromBigEndian(tfhd_atomFlags_scrap); //unused #endif - uint64_t tfhd_offset = UInt64FromBigEndian(parsedAtoms[tfhd_number].AtomicData +4); + uint64_t tfhd_offset = + UInt64FromBigEndian(parsedAtoms[tfhd_number].AtomicData + 4); - if (!determined_offset) { - determined_offset = true; - base_offset = APar_SimpleSumAtoms(tfhd_number) - tfhd_offset; - if (base_offset != 0) { - tfhd_changed = true; - } - } - - tfhd_offset += base_offset; - UInt64_TO_String8(tfhd_offset, parsedAtoms[tfhd_number].AtomicData +4); - } - return tfhd_changed; + if (!determined_offset) { + determined_offset = true; + base_offset = APar_SimpleSumAtoms(tfhd_number) - tfhd_offset; + if (base_offset != 0) { + tfhd_changed = true; + } + } + + tfhd_offset += base_offset; + UInt64_TO_String8(tfhd_offset, parsedAtoms[tfhd_number].AtomicData + 4); + } + return tfhd_changed; } bool APar_Readjust_STCO_atom(uint64_t mdat_position, short stco_number) { - bool stco_changed = false; + bool stco_changed = false; - if (parsedAtoms[stco_number].ancillary_data != 1) { - return stco_changed; - } - - parsedAtoms[stco_number].AtomicData = (char*)calloc(1, sizeof(char)* (size_t)(parsedAtoms[stco_number].AtomicLength) ); - APar_readX(parsedAtoms[stco_number].AtomicData, source_file, parsedAtoms[stco_number].AtomicStart+12, parsedAtoms[stco_number].AtomicLength-12); - - parsedAtoms[stco_number].AtomicVerFlags = 0; - //readjust - - char* stco_entries = (char *)malloc(sizeof(char)*4 + 1); - memset(stco_entries, 0, sizeof(char)*4 + 1); - - memcpy(stco_entries, parsedAtoms[stco_number].AtomicData, 4); - uint32_t entries = UInt32FromBigEndian(stco_entries); - - char* an_entry = (char *)malloc(sizeof(char)*4 + 1); - memset(an_entry, 0, sizeof(char)*4 + 1); - - for(uint32_t i=1; i<=entries; i++) { - //read 4 bytes of the atom into a 4 char uint32_t an_entry to eval it - for (int c = 0; c <=3; c++ ) { - //first stco entry is the number of entries; every other one is an actual offset value - an_entry[c] = parsedAtoms[stco_number].AtomicData[i*4 + c]; - } - - uint32_t this_entry = UInt32FromBigEndian(an_entry); - - if (i == 1 && mdat_supplemental_offset == 0) { //for the first chunk, and only for the first *ever* entry, make the global mdat supplemental offset - - mdat_supplemental_offset = (uint64_t)(mdat_position - (this_entry - removed_bytes_tally) ); - bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally; - - if (mdat_supplemental_offset == 0) { - break; - } - } - - if (mdat_supplemental_offset != 0) { - stco_changed = true; - } - - this_entry += mdat_supplemental_offset + bytes_into_mdat; - UInt32_TO_String4(this_entry, an_entry); - //and put the data back into AtomicData... - for (int d = 0; d <=3; d++ ) { - //first stco entry is the number of entries; every other one is an actual offset value - parsedAtoms[stco_number].AtomicData[i*4 + d] = an_entry[d]; - } - } - - free(an_entry); - free(stco_entries); - an_entry=NULL; - stco_entries=NULL; - //end readjustment - return stco_changed; + if (parsedAtoms[stco_number].ancillary_data != 1) { + return stco_changed; + } + + parsedAtoms[stco_number].AtomicData = (char *)calloc( + 1, sizeof(char) * (size_t)(parsedAtoms[stco_number].AtomicLength)); + APar_readX(parsedAtoms[stco_number].AtomicData, + source_file, + parsedAtoms[stco_number].AtomicStart + 12, + parsedAtoms[stco_number].AtomicLength - 12); + + parsedAtoms[stco_number].AtomicVerFlags = 0; + // readjust + + char *stco_entries = (char *)malloc(sizeof(char) * 4 + 1); + memset(stco_entries, 0, sizeof(char) * 4 + 1); + + memcpy(stco_entries, parsedAtoms[stco_number].AtomicData, 4); + uint32_t entries = UInt32FromBigEndian(stco_entries); + + char *an_entry = (char *)malloc(sizeof(char) * 4 + 1); + memset(an_entry, 0, sizeof(char) * 4 + 1); + + for (uint32_t i = 1; i <= entries; i++) { + // read 4 bytes of the atom into a 4 char uint32_t an_entry to eval it + for (int c = 0; c <= 3; c++) { + // first stco entry is the number of entries; every other one is an actual + // offset value + an_entry[c] = parsedAtoms[stco_number].AtomicData[i * 4 + c]; + } + + uint32_t this_entry = UInt32FromBigEndian(an_entry); + + if (i == 1 && mdat_supplemental_offset == + 0) { // for the first chunk, and only for the first *ever* + // entry, make the global mdat supplemental offset + + mdat_supplemental_offset = + (uint64_t)(mdat_position - (this_entry - removed_bytes_tally)); + bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally; + + if (mdat_supplemental_offset == 0) { + break; + } + } + + if (mdat_supplemental_offset != 0) { + stco_changed = true; + } + + this_entry += mdat_supplemental_offset + bytes_into_mdat; + UInt32_TO_String4(this_entry, an_entry); + // and put the data back into AtomicData... + for (int d = 0; d <= 3; d++) { + // first stco entry is the number of entries; every other one is an actual + // offset value + parsedAtoms[stco_number].AtomicData[i * 4 + d] = an_entry[d]; + } + } + + free(an_entry); + free(stco_entries); + an_entry = NULL; + stco_entries = NULL; + // end readjustment + return stco_changed; } /////////////////////////////////////////////////////////////////////////////////////// -// Reorder / Padding // +// Reorder / Padding // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_CreatePadding - padding_length - the new length of padding + padding_length - the new length of padding - Create a 'free' atom at a pre-determined area of a given length & set that atom as the global padding store atom + Create a 'free' atom at a pre-determined area of a given length & set that +atom as the global padding store atom ----------------------*/ void APar_CreatePadding(uint64_t padding_length) { - AtomicInfo* next_atom = &parsedAtoms[ parsedAtoms[dynUpd.consolidated_padding_insertion].NextAtomNumber ]; - if (padding_length > 2000 && next_atom->AtomicLevel > 1) { - short padding_atom = APar_InterjectNewAtom("free", CHILD_ATOM, SIMPLE_ATOM, 2000, 0, 0, - (next_atom->AtomicLevel == 1 ? 1 : parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicLevel), dynUpd.consolidated_padding_insertion ); - dynUpd.padding_store = &parsedAtoms[padding_atom]; - - if (dynUpd.first_mdat_atom != NULL && padding_length - 2000 >= 8) { - short padding_res_atom = APar_InterjectNewAtom("free", CHILD_ATOM, SIMPLE_ATOM, padding_length - 2000, 0, 0, 1, - APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber) ); - dynUpd.padding_resevoir = &parsedAtoms[padding_res_atom]; - } - } else { - short padding_atom = APar_InterjectNewAtom("free", CHILD_ATOM, SIMPLE_ATOM, padding_length, 0, 0, - (next_atom->AtomicLevel == 1 ? 1 : parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicLevel), dynUpd.consolidated_padding_insertion ); - dynUpd.padding_store = &parsedAtoms[padding_atom]; - } - return; + AtomicInfo *next_atom = + &parsedAtoms[parsedAtoms[dynUpd.consolidated_padding_insertion] + .NextAtomNumber]; + if (padding_length > 2000 && next_atom->AtomicLevel > 1) { + short padding_atom = APar_InterjectNewAtom( + "free", + CHILD_ATOM, + SIMPLE_ATOM, + 2000, + 0, + 0, + (next_atom->AtomicLevel == 1 + ? 1 + : parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicLevel), + dynUpd.consolidated_padding_insertion); + dynUpd.padding_store = &parsedAtoms[padding_atom]; + + if (dynUpd.first_mdat_atom != NULL && padding_length - 2000 >= 8) { + short padding_res_atom = APar_InterjectNewAtom( + "free", + CHILD_ATOM, + SIMPLE_ATOM, + padding_length - 2000, + 0, + 0, + 1, + APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber)); + dynUpd.padding_resevoir = &parsedAtoms[padding_res_atom]; + } + } else { + short padding_atom = APar_InterjectNewAtom( + "free", + CHILD_ATOM, + SIMPLE_ATOM, + padding_length, + 0, + 0, + (next_atom->AtomicLevel == 1 + ? 1 + : parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicLevel), + dynUpd.consolidated_padding_insertion); + dynUpd.padding_store = &parsedAtoms[padding_atom]; + } + return; } /*---------------------- APar_AdjustPadding - new_padding_length - the new length of padding + new_padding_length - the new length of padding - Adjust the consolidated padding store atom to the new size - creating&splitting if necessary. + Adjust the consolidated padding store atom to the new size - +creating&splitting if necessary. ----------------------*/ void APar_AdjustPadding(uint64_t new_padding_length) { - uint64_t avail_padding = 0; + uint64_t avail_padding = 0; - if ( (psp_brand || force_existing_hierarchy) && (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) ) return; + if ((psp_brand || force_existing_hierarchy) && + (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) + return; + + if (dynUpd.padding_store == NULL) { + if (new_padding_length >= 8) { + APar_CreatePadding(new_padding_length); + } else { + return; + } + } + + if (new_padding_length < 8) { + APar_EliminateAtom(dynUpd.padding_store->AtomicNumber, + dynUpd.padding_store->NextAtomNumber); + dynUpd.updage_by_padding = false; + } + + if (dynUpd.padding_store != NULL) + avail_padding += dynUpd.padding_store->AtomicLength; + if (dynUpd.padding_resevoir != NULL) + avail_padding += dynUpd.padding_resevoir->AtomicLength; + + if ((new_padding_length > avail_padding && new_padding_length < 2000) || + dynUpd.padding_store->AtomicLevel == 1) { + free(dynUpd.padding_store->AtomicData); + dynUpd.padding_store->AtomicData = + (char *)calloc(1, sizeof(char) * new_padding_length); + dynUpd.padding_store->AtomicLength = new_padding_length; + } else { + free(dynUpd.padding_store->AtomicData); + dynUpd.padding_store->AtomicData = (char *)calloc(1, sizeof(char) * 2007); + dynUpd.padding_store->AtomicLength = 2000; + + /* since new_padding_length is an unsigned value, if its value is less than + * 1992, subtraction of 2000 from it will result in "underflow". That is + * what is causing the reported issue of atom detected larger than + * filesize. + */ + if (new_padding_length < 2008) { + dynUpd.padding_store->AtomicLength = new_padding_length; + return; + } + + if (dynUpd.padding_resevoir == NULL && dynUpd.first_mdat_atom != NULL) { + short pad_res_atom = APar_InterjectNewAtom( + "free", + CHILD_ATOM, + SIMPLE_ATOM, + new_padding_length - 2000, + 0, + 0, + 1, + APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber)); + dynUpd.padding_resevoir = &parsedAtoms[pad_res_atom]; + dynUpd.padding_resevoir->AtomicLength = new_padding_length - 2000; + } else if (dynUpd.padding_resevoir != NULL) { + free(dynUpd.padding_resevoir->AtomicData); + dynUpd.padding_resevoir->AtomicData = + (char *)calloc(1, sizeof(char) * (new_padding_length - 2000)); + dynUpd.padding_resevoir->AtomicLength = new_padding_length - 2000; + } + } - if (dynUpd.padding_store == NULL) { - if (new_padding_length >= 8) { - APar_CreatePadding(new_padding_length); - } else { - return; - } - } - - if (new_padding_length < 8) { - APar_EliminateAtom(dynUpd.padding_store->AtomicNumber, dynUpd.padding_store->NextAtomNumber); - dynUpd.updage_by_padding = false; - } - - if (dynUpd.padding_store != NULL) avail_padding += dynUpd.padding_store->AtomicLength; - if (dynUpd.padding_resevoir != NULL) avail_padding += dynUpd.padding_resevoir->AtomicLength; - - if ((new_padding_length > avail_padding && new_padding_length < 2000) || dynUpd.padding_store->AtomicLevel == 1 ) { - free(dynUpd.padding_store->AtomicData); - dynUpd.padding_store->AtomicData = (char*)calloc(1, sizeof(char)*new_padding_length ); - dynUpd.padding_store->AtomicLength = new_padding_length; - } else { - free(dynUpd.padding_store->AtomicData); - dynUpd.padding_store->AtomicData = (char*)calloc(1, sizeof(char)*2007 ); - dynUpd.padding_store->AtomicLength = 2000; - - /* since new_padding_length is an unsigned value, if its value is less than - * 1992, subtraction of 2000 from it will result in "underflow". That is - * what is causing the reported issue of atom detected larger than - * filesize. - */ - if (new_padding_length < 2008) { - dynUpd.padding_store->AtomicLength = new_padding_length; - return; - } - - if (dynUpd.padding_resevoir == NULL && dynUpd.first_mdat_atom != NULL) { - short pad_res_atom = APar_InterjectNewAtom("free", CHILD_ATOM, SIMPLE_ATOM, new_padding_length - 2000, 0, 0, 1, - APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber) ); - dynUpd.padding_resevoir = &parsedAtoms[pad_res_atom]; - dynUpd.padding_resevoir->AtomicLength = new_padding_length - 2000; - } else if (dynUpd.padding_resevoir != NULL) { - free(dynUpd.padding_resevoir->AtomicData); - dynUpd.padding_resevoir->AtomicData = (char*)calloc(1, sizeof(char)*(new_padding_length - 2000) ); - dynUpd.padding_resevoir->AtomicLength = new_padding_length - 2000; - } - } - - return; + return; } /*---------------------- APar_PaddingAmount - target_amount - the amount of padding that is requested - limit_by_prefs - whether to limit the amount of padding to the environmental preferences - - When a file will completely rewritten, limit the amount of padding according to the environmental prefs: min & max with a default amount substitued when padding is - found to fall outside those values. When a file will be updated by dynamic updating, the consolidated padding will be initially set to the amount that was present - before any modifications to tags. This amount of padding will in all likelyhood change when the final determination of whether a dynamic update can occur. + target_amount - the amount of padding that is requested + limit_by_prefs - whether to limit the amount of padding to the +environmental preferences + + When a file will completely rewritten, limit the amount of padding according +to the environmental prefs: min & max with a default amount substitued when +padding is found to fall outside those values. When a file will be updated by +dynamic updating, the consolidated padding will be initially set to the amount +that was present before any modifications to tags. This amount of padding will +in all likelyhood change when the final determination of whether a dynamic +update can occur. ----------------------*/ uint64_t APar_PaddingAmount(uint64_t target_amount, bool limit_by_prefs) { - uint64_t padding_allowance = 0; - if (limit_by_prefs) { - if (pad_prefs.maximum_present_padding_size == 0) { - return 0; - } else if (target_amount >= pad_prefs.maximum_present_padding_size) { - padding_allowance = pad_prefs.maximum_present_padding_size; - } else if (target_amount <= pad_prefs.minimum_required_padding_size) { - padding_allowance = pad_prefs.default_padding_size; - } else { - padding_allowance = target_amount; - } - } else { - padding_allowance = target_amount; - } - if (padding_allowance < 8 ) return 0; - if (!alter_original) return pad_prefs.default_padding_size; - return padding_allowance; + uint64_t padding_allowance = 0; + if (limit_by_prefs) { + if (pad_prefs.maximum_present_padding_size == 0) { + return 0; + } else if (target_amount >= pad_prefs.maximum_present_padding_size) { + padding_allowance = pad_prefs.maximum_present_padding_size; + } else if (target_amount <= pad_prefs.minimum_required_padding_size) { + padding_allowance = pad_prefs.default_padding_size; + } else { + padding_allowance = target_amount; + } + } else { + padding_allowance = target_amount; + } + if (padding_allowance < 8) + return 0; + if (!alter_original) + return pad_prefs.default_padding_size; + return padding_allowance; } /*---------------------- APar_DetermineDynamicUpdate - Find where the first 'mdat' atom will eventually wind up in any new file. If this movement is within the bounds of padding prefs, allow a dynamic update. Adjust - the amount of padding to accommodate the difference in positions (persuant to padding prefs). Otherwise prevent it, and make sure the default amount of padding - exists for the file for a full rewrite. + Find where the first 'mdat' atom will eventually wind up in any new file. If +this movement is within the bounds of padding prefs, allow a dynamic update. +Adjust the amount of padding to accommodate the difference in positions +(persuant to padding prefs). Otherwise prevent it, and make sure the default +amount of padding exists for the file for a full rewrite. ----------------------*/ void APar_DetermineDynamicUpdate() { - if (!force_existing_hierarchy && moov_atom_was_mooved) { - dynUpd.prevent_dynamic_update = true; - return; - } - uint64_t mdat_pos = 0; - //uint64_t moov_udta_pos = APar_QuickSumAtomicLengths(dynUpd.moov_udta_atom); - //uint64_t moov_last_trak_pos = APar_QuickSumAtomicLengths(dynUpd.last_trak_child_atom); - //uint64_t moov_meta_pos = APar_QuickSumAtomicLengths(dynUpd.moov_meta_atom); - uint64_t moov_pos = APar_QuickSumAtomicLengths(dynUpd.moov_atom); - uint64_t root_meta_pos = APar_QuickSumAtomicLengths(dynUpd.file_meta_atom); - - if (root_meta_pos > 0 && root_meta_pos != dynUpd.file_meta_atom->AtomicStart) { - - } else if (moov_pos > 0 && moov_pos != dynUpd.moov_atom->AtomicStart) { //this could reflect either a root meta being moved, or moov came after mdat & was rearranged - dynUpd.initial_update_atom = &parsedAtoms[0]; - - } else if (moov_pos > 0) { - dynUpd.initial_update_atom = dynUpd.moov_atom; - } - - - if (dynUpd.first_mdat_atom == NULL && alter_original) { - //work this out later - - } else if (dynUpd.first_mdat_atom != NULL) { - mdat_pos = APar_QuickSumAtomicLengths(dynUpd.first_mdat_atom); - if (mdat_pos >= dynUpd.first_mdat_atom->AtomicStart) { - uint64_t offset_increase = mdat_pos - dynUpd.first_mdat_atom->AtomicStart; - if (offset_increase > dynUpd.padding_bytes) { - if ( (psp_brand || force_existing_hierarchy) && (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) ) { - dynUpd.updage_by_padding = true; - } else { - dynUpd.prevent_dynamic_update = true; - } - } else { - uint64_t padding_remaining = dynUpd.padding_bytes - offset_increase; - uint64_t padding_allowed = APar_PaddingAmount(padding_remaining, true); - - if (padding_remaining == padding_allowed) { - if (alter_original) { - dynUpd.updage_by_padding = true; - } - APar_AdjustPadding(padding_allowed); - } else { - dynUpd.updage_by_padding = false; - APar_AdjustPadding(padding_allowed); - } - } - - } else { - uint64_t padding_replenishment = dynUpd.first_mdat_atom->AtomicStart - mdat_pos; - uint64_t padding_allowed = APar_PaddingAmount(padding_replenishment, true); - - if (padding_replenishment == padding_allowed) { - if (alter_original) { - dynUpd.updage_by_padding = true; - } - APar_AdjustPadding(padding_allowed); - } else { - dynUpd.updage_by_padding = false; - APar_AdjustPadding(padding_allowed); - } - } - } - if (!dynUpd.updage_by_padding && dynUpd.padding_bytes < pad_prefs.default_padding_size) { - APar_AdjustPadding(pad_prefs.default_padding_size); //if there is a full rewrite, add a default amount of padding - } - APar_DetermineAtomLengths(); - return; + if (!force_existing_hierarchy && moov_atom_was_mooved) { + dynUpd.prevent_dynamic_update = true; + return; + } + uint64_t mdat_pos = 0; + // uint64_t moov_udta_pos = APar_QuickSumAtomicLengths(dynUpd.moov_udta_atom); + // uint64_t moov_last_trak_pos = + // APar_QuickSumAtomicLengths(dynUpd.last_trak_child_atom); uint64_t + // moov_meta_pos = APar_QuickSumAtomicLengths(dynUpd.moov_meta_atom); + uint64_t moov_pos = APar_QuickSumAtomicLengths(dynUpd.moov_atom); + uint64_t root_meta_pos = APar_QuickSumAtomicLengths(dynUpd.file_meta_atom); + + if (root_meta_pos > 0 && + root_meta_pos != dynUpd.file_meta_atom->AtomicStart) { + + } else if (moov_pos > 0 && + moov_pos != + dynUpd.moov_atom + ->AtomicStart) { // this could reflect either a + // root meta being moved, or moov + // came after mdat & was rearranged + dynUpd.initial_update_atom = &parsedAtoms[0]; + + } else if (moov_pos > 0) { + dynUpd.initial_update_atom = dynUpd.moov_atom; + } + + if (dynUpd.first_mdat_atom == NULL && alter_original) { + // work this out later + + } else if (dynUpd.first_mdat_atom != NULL) { + mdat_pos = APar_QuickSumAtomicLengths(dynUpd.first_mdat_atom); + if (mdat_pos >= dynUpd.first_mdat_atom->AtomicStart) { + uint64_t offset_increase = mdat_pos - dynUpd.first_mdat_atom->AtomicStart; + if (offset_increase > dynUpd.padding_bytes) { + if ((psp_brand || force_existing_hierarchy) && + (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) { + dynUpd.updage_by_padding = true; + } else { + dynUpd.prevent_dynamic_update = true; + } + } else { + uint64_t padding_remaining = dynUpd.padding_bytes - offset_increase; + uint64_t padding_allowed = APar_PaddingAmount(padding_remaining, true); + + if (padding_remaining == padding_allowed) { + if (alter_original) { + dynUpd.updage_by_padding = true; + } + APar_AdjustPadding(padding_allowed); + } else { + dynUpd.updage_by_padding = false; + APar_AdjustPadding(padding_allowed); + } + } + + } else { + uint64_t padding_replenishment = + dynUpd.first_mdat_atom->AtomicStart - mdat_pos; + uint64_t padding_allowed = + APar_PaddingAmount(padding_replenishment, true); + + if (padding_replenishment == padding_allowed) { + if (alter_original) { + dynUpd.updage_by_padding = true; + } + APar_AdjustPadding(padding_allowed); + } else { + dynUpd.updage_by_padding = false; + APar_AdjustPadding(padding_allowed); + } + } + } + if (!dynUpd.updage_by_padding && + dynUpd.padding_bytes < pad_prefs.default_padding_size) { + APar_AdjustPadding( + pad_prefs.default_padding_size); // if there is a full rewrite, add a + // default amount of padding + } + APar_DetermineAtomLengths(); + return; } /*---------------------- APar_ConsolidatePadding - Work through all the atoms that were determined to be 'padding' in APar_FindPadding, sum up their lengths and remove them from the hieararchy. No bytes are added - or removed from the file because the total length of the padding is reformed as a single consolidated 'free' atom. This free atom will either form after the iTunes - handler or (probably) at level 1 (probably before mdat). - When doing a dynamic update in situ, the length of this consolidated padding atom will change (later) if metadata is altered. + Work through all the atoms that were determined to be 'padding' in +APar_FindPadding, sum up their lengths and remove them from the hieararchy. No +bytes are added or removed from the file because the total length of the padding +is reformed as a single consolidated 'free' atom. This free atom will either +form after the iTunes handler or (probably) at level 1 (probably before mdat). + When doing a dynamic update in situ, the length of this +consolidated padding atom will change (later) if metadata is altered. ----------------------*/ void APar_ConsolidatePadding() { - uint64_t bytes_o_padding = 0; - if (dynUpd.consolidated_padding_insertion == 0) return; - FreeAtomListing* padding_entry = dynUpd.first_padding_atom; - - while (true) { - if (padding_entry == NULL) break; - - APar_EliminateAtom(padding_entry->free_atom->AtomicNumber, padding_entry->free_atom->NextAtomNumber); - - FreeAtomListing* next_entry = padding_entry->next_free_listing; - free(padding_entry); - padding_entry = next_entry; - } - - bytes_o_padding = APar_PaddingAmount(dynUpd.padding_bytes, !alter_original); - - if (bytes_o_padding >= 8) { - if ( !(psp_brand || force_existing_hierarchy) && (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) ) { - APar_CreatePadding(bytes_o_padding); - } - } - return; + uint64_t bytes_o_padding = 0; + if (dynUpd.consolidated_padding_insertion == 0) + return; + FreeAtomListing *padding_entry = dynUpd.first_padding_atom; + + while (true) { + if (padding_entry == NULL) + break; + + APar_EliminateAtom(padding_entry->free_atom->AtomicNumber, + padding_entry->free_atom->NextAtomNumber); + + FreeAtomListing *next_entry = padding_entry->next_free_listing; + free(padding_entry); + padding_entry = next_entry; + } + + bytes_o_padding = APar_PaddingAmount(dynUpd.padding_bytes, !alter_original); + + if (bytes_o_padding >= 8) { + if (!(psp_brand || force_existing_hierarchy) && + (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) { + APar_CreatePadding(bytes_o_padding); + } + } + return; } /*---------------------- APar_FindPadding - listing_purposes_only - controls whether to just track free/skip atoms, or actually consolidate + listing_purposes_only - controls whether to just track free/skip atoms, +or actually consolidate - This is called before all the lengths of all atoms gets redetermined. The atoms at this point are already moved via optimization, and so the location of where - to place padding can be ascertained. It is not known howevever where the ultimate positions of these landmark atoms will be - which can change according to padding - prefs (which gets applied to all the padding in APar_ConsolidatePadding(). Any free/skip atoms found to exist between 'moov' & the first 'mdat' atom will be - be considered padding (excepting anything under 'stsd' which will be monolithing during *setting* of metadata tags). Each padding atom will be noted. A place where - padding can reside will also be found - the presence of iTunes tags will ultimately deposit all accumulated padding in in the iTunes hierarchy. + This is called before all the lengths of all atoms gets redetermined. The +atoms at this point are already moved via optimization, and so the location of +where to place padding can be ascertained. It is not known howevever where the +ultimate positions of these landmark atoms will be - which can change according +to padding prefs (which gets applied to all the padding in +APar_ConsolidatePadding(). Any free/skip atoms found to exist between 'moov' & +the first 'mdat' atom will be be considered padding (excepting anything under +'stsd' which will be monolithing during *setting* of metadata tags). Each +padding atom will be noted. A place where padding can reside will also be found +- the presence of iTunes tags will ultimately deposit all accumulated padding in +in the iTunes hierarchy. ----------------------*/ void APar_FindPadding(bool listing_purposes_only) { - AtomicInfo* DynamicUpdateRangeStart = NULL; - AtomicInfo* DynamicUpdateRangeEnd = dynUpd.first_mdat_atom; //could be NULL (a missing 'mdat' would mean externally referenced media that would not be self-contained) - short eval_atom = 0; - - if (dynUpd.moov_atom != NULL) { - DynamicUpdateRangeStart = &parsedAtoms[dynUpd.moov_atom->NextAtomNumber]; - } - if (DynamicUpdateRangeStart == NULL) return; - - eval_atom = DynamicUpdateRangeStart->AtomicNumber; - while (true) { - if (memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) { - if (dynUpd.first_padding_atom == NULL) { - dynUpd.first_padding_atom = (FreeAtomListing*)malloc(sizeof(FreeAtomListing)); - dynUpd.first_padding_atom->free_atom = &parsedAtoms[eval_atom]; - dynUpd.first_padding_atom->next_free_listing = NULL; - } else { - FreeAtomListing* free_listing = (FreeAtomListing*)malloc(sizeof(FreeAtomListing)); - free_listing->free_atom = &parsedAtoms[eval_atom]; - free_listing->next_free_listing = NULL; - if (dynUpd.first_padding_atom->next_free_listing == NULL) { - dynUpd.first_padding_atom->next_free_listing = free_listing; - } else if (dynUpd.last_padding_atom != NULL) { - dynUpd.last_padding_atom->next_free_listing = free_listing; - } - dynUpd.last_padding_atom = free_listing; - } - dynUpd.padding_bytes += (parsedAtoms[eval_atom].AtomicLength == 1 ? parsedAtoms[eval_atom].AtomicLengthExtended : parsedAtoms[eval_atom].AtomicLength); - } - eval_atom = parsedAtoms[eval_atom].NextAtomNumber; - if (eval_atom == 0) break; - if (DynamicUpdateRangeEnd != NULL) { - if (DynamicUpdateRangeEnd->AtomicNumber == eval_atom) break; - } - } - - //search for a suitable location where padding can accumulate - if (dynUpd.moov_udta_atom != NULL) { - if (dynUpd.iTunes_list_handler_atom == NULL) { - dynUpd.iTunes_list_handler_atom = APar_FindAtom("moov.udta.meta.hdlr", false, VERSIONED_ATOM, 0); - if (dynUpd.iTunes_list_handler_atom != NULL) { - if (parsedAtoms[dynUpd.iTunes_list_handler_atom->NextAtomNumber].AtomicLevel >= dynUpd.iTunes_list_handler_atom->AtomicLevel) { - dynUpd.consolidated_padding_insertion = dynUpd.iTunes_list_handler_atom->AtomicNumber; - } - } - } - } - if (dynUpd.consolidated_padding_insertion == 0) { - if (dynUpd.first_mdat_atom != NULL && !(dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) { - dynUpd.consolidated_padding_insertion = APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber); - } else { - dynUpd.consolidated_padding_insertion = APar_FindLastAtom(); - dynUpd.optimization_flags |= PADDING_AT_EOF; - } - } - - //if the place where padding will eventually wind up is just before a padding atom, that padding atom will be erased by APar_ConsolidatePadding when its - //AtomicNumber becomes -1; so work back through the hierarchy for the first non-padding atom - while (true) { - if (memcmp(parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicName, "free", 4) == 0 || - memcmp(parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicName, "skip", 4) == 0) { - dynUpd.consolidated_padding_insertion = APar_FindPrecedingAtom(dynUpd.consolidated_padding_insertion); - } else { - break; - } - } - return; + AtomicInfo *DynamicUpdateRangeStart = NULL; + AtomicInfo *DynamicUpdateRangeEnd = + dynUpd.first_mdat_atom; // could be NULL (a missing 'mdat' would mean + // externally referenced media that would not be + // self-contained) + short eval_atom = 0; + + if (dynUpd.moov_atom != NULL) { + DynamicUpdateRangeStart = &parsedAtoms[dynUpd.moov_atom->NextAtomNumber]; + } + if (DynamicUpdateRangeStart == NULL) + return; + + eval_atom = DynamicUpdateRangeStart->AtomicNumber; + while (true) { + if (memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || + memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) { + if (dynUpd.first_padding_atom == NULL) { + dynUpd.first_padding_atom = + (FreeAtomListing *)malloc(sizeof(FreeAtomListing)); + dynUpd.first_padding_atom->free_atom = &parsedAtoms[eval_atom]; + dynUpd.first_padding_atom->next_free_listing = NULL; + } else { + FreeAtomListing *free_listing = + (FreeAtomListing *)malloc(sizeof(FreeAtomListing)); + free_listing->free_atom = &parsedAtoms[eval_atom]; + free_listing->next_free_listing = NULL; + if (dynUpd.first_padding_atom->next_free_listing == NULL) { + dynUpd.first_padding_atom->next_free_listing = free_listing; + } else if (dynUpd.last_padding_atom != NULL) { + dynUpd.last_padding_atom->next_free_listing = free_listing; + } + dynUpd.last_padding_atom = free_listing; + } + dynUpd.padding_bytes += (parsedAtoms[eval_atom].AtomicLength == 1 + ? parsedAtoms[eval_atom].AtomicLengthExtended + : parsedAtoms[eval_atom].AtomicLength); + } + eval_atom = parsedAtoms[eval_atom].NextAtomNumber; + if (eval_atom == 0) + break; + if (DynamicUpdateRangeEnd != NULL) { + if (DynamicUpdateRangeEnd->AtomicNumber == eval_atom) + break; + } + } + + // search for a suitable location where padding can accumulate + if (dynUpd.moov_udta_atom != NULL) { + if (dynUpd.iTunes_list_handler_atom == NULL) { + dynUpd.iTunes_list_handler_atom = + APar_FindAtom("moov.udta.meta.hdlr", false, VERSIONED_ATOM, 0); + if (dynUpd.iTunes_list_handler_atom != NULL) { + if (parsedAtoms[dynUpd.iTunes_list_handler_atom->NextAtomNumber] + .AtomicLevel >= dynUpd.iTunes_list_handler_atom->AtomicLevel) { + dynUpd.consolidated_padding_insertion = + dynUpd.iTunes_list_handler_atom->AtomicNumber; + } + } + } + } + if (dynUpd.consolidated_padding_insertion == 0) { + if (dynUpd.first_mdat_atom != NULL && + !(dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) { + dynUpd.consolidated_padding_insertion = + APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber); + } else { + dynUpd.consolidated_padding_insertion = APar_FindLastAtom(); + dynUpd.optimization_flags |= PADDING_AT_EOF; + } + } + + // if the place where padding will eventually wind up is just before a padding + // atom, that padding atom will be erased by APar_ConsolidatePadding when its + // AtomicNumber becomes -1; so work back through the hierarchy for the first + // non-padding atom + while (true) { + if (memcmp(parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicName, + "free", + 4) == 0 || + memcmp(parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicName, + "skip", + 4) == 0) { + dynUpd.consolidated_padding_insertion = + APar_FindPrecedingAtom(dynUpd.consolidated_padding_insertion); + } else { + break; + } + } + return; } void APar_LocateAtomLandmarks() { - short total_file_level_atoms = APar_ReturnChildrenAtoms (0, 0); - short eval_atom = 0; + short total_file_level_atoms = APar_ReturnChildrenAtoms(0, 0); + short eval_atom = 0; - dynUpd.optimization_flags = 0; - dynUpd.padding_bytes = 0; //this *won't* get filled here; it is only tracked for the purposes of dynamic updating - dynUpd.consolidated_padding_insertion = 0; //this will eventually hold the point where to insert a new atom that will accumulate padding - - dynUpd.last_trak_child_atom = NULL; - dynUpd.moov_atom = NULL; - dynUpd.moov_udta_atom = NULL; - dynUpd.iTunes_list_handler_atom = NULL; //this *won't* get filled here; it is only tracked for the purposes of dynamic updating - dynUpd.moov_meta_atom = NULL; - dynUpd.file_meta_atom = NULL; - dynUpd.first_mdat_atom = NULL; - dynUpd.first_movielevel_metadata_tagging_atom = NULL; - dynUpd.initial_update_atom = NULL; - dynUpd.first_otiose_freespace_atom = NULL; - dynUpd.first_padding_atom = NULL; //this *won't* get filled here; it is only tracked for the purposes of dynamic updating - dynUpd.last_padding_atom = NULL; //this *won't* get filled here; it is only tracked for the purposes of dynamic updating - dynUpd.padding_store = NULL; //this *won't* get filled here; it gets filled in APar_ConsolidatePadding - dynUpd.padding_resevoir = NULL; - - //scan through all top level atoms; fragmented files won't be optimized - for(uint8_t iii = 1; iii <= total_file_level_atoms; iii++) { - eval_atom = APar_ReturnChildrenAtoms (0, iii); - //fprintf(stdout, "file level children - %s\n", parsedAtoms[eval_atom].AtomicName); - - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "moof", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "mfra", 4) == 0) { - move_moov_atom = false; //moov reordering won't be occurring on fragmented files, but it should have moov first anyway (QuickTime does at least) - } - - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0 ) { - if (dynUpd.first_mdat_atom == NULL) { - dynUpd.first_mdat_atom = &parsedAtoms[eval_atom]; - } - } - - if (dynUpd.first_otiose_freespace_atom == NULL) { - if ( (memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) && - dynUpd.first_mdat_atom == NULL && dynUpd.moov_atom == NULL ) { - dynUpd.first_otiose_freespace_atom = &parsedAtoms[eval_atom]; //the scourge of libmp4v2 - } - } - - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "moov", 4) == 0 ) { - dynUpd.moov_atom = &parsedAtoms[eval_atom]; - if (dynUpd.first_mdat_atom != NULL) { - dynUpd.optimization_flags |= MEDIADATA__PRECEDES__MOOV; //or mdat could be entirely missing as well; check later - } - } - - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "meta", 4) == 0 ) { - dynUpd.file_meta_atom = &parsedAtoms[eval_atom]; - if (dynUpd.moov_atom != NULL) { - dynUpd.optimization_flags |= ROOT_META__PRECEDES__MOOV; - }//meta before moov would require more rewrite of the portion of the file than I want to do - } //but it wouldn't be all that difficult to accommodate rewriting everything from ftyp down to the first mdat, but currently its limited to last 'trak' child to mdat - } - return; + dynUpd.optimization_flags = 0; + dynUpd.padding_bytes = 0; // this *won't* get filled here; it is only tracked + // for the purposes of dynamic updating + dynUpd.consolidated_padding_insertion = + 0; // this will eventually hold the point where to insert a new atom that + // will accumulate padding + + dynUpd.last_trak_child_atom = NULL; + dynUpd.moov_atom = NULL; + dynUpd.moov_udta_atom = NULL; + dynUpd.iTunes_list_handler_atom = + NULL; // this *won't* get filled here; it is only tracked for the purposes + // of dynamic updating + dynUpd.moov_meta_atom = NULL; + dynUpd.file_meta_atom = NULL; + dynUpd.first_mdat_atom = NULL; + dynUpd.first_movielevel_metadata_tagging_atom = NULL; + dynUpd.initial_update_atom = NULL; + dynUpd.first_otiose_freespace_atom = NULL; + dynUpd.first_padding_atom = + NULL; // this *won't* get filled here; it is only tracked for the purposes + // of dynamic updating + dynUpd.last_padding_atom = + NULL; // this *won't* get filled here; it is only tracked for the purposes + // of dynamic updating + dynUpd.padding_store = NULL; // this *won't* get filled here; it gets filled + // in APar_ConsolidatePadding + dynUpd.padding_resevoir = NULL; + + // scan through all top level atoms; fragmented files won't be optimized + for (uint8_t iii = 1; iii <= total_file_level_atoms; iii++) { + eval_atom = APar_ReturnChildrenAtoms(0, iii); + // fprintf(stdout, "file level children - %s\n", + // parsedAtoms[eval_atom].AtomicName); + + if (memcmp(parsedAtoms[eval_atom].AtomicName, "moof", 4) == 0 || + memcmp(parsedAtoms[eval_atom].AtomicName, "mfra", 4) == 0) { + move_moov_atom = + false; // moov reordering won't be occurring on fragmented files, but + // it should have moov first anyway (QuickTime does at least) + } + + if (memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0) { + if (dynUpd.first_mdat_atom == NULL) { + dynUpd.first_mdat_atom = &parsedAtoms[eval_atom]; + } + } + + if (dynUpd.first_otiose_freespace_atom == NULL) { + if ((memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || + memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) && + dynUpd.first_mdat_atom == NULL && dynUpd.moov_atom == NULL) { + dynUpd.first_otiose_freespace_atom = + &parsedAtoms[eval_atom]; // the scourge of libmp4v2 + } + } + + if (memcmp(parsedAtoms[eval_atom].AtomicName, "moov", 4) == 0) { + dynUpd.moov_atom = &parsedAtoms[eval_atom]; + if (dynUpd.first_mdat_atom != NULL) { + dynUpd.optimization_flags |= + MEDIADATA__PRECEDES__MOOV; // or mdat could be entirely missing as + // well; check later + } + } + + if (memcmp(parsedAtoms[eval_atom].AtomicName, "meta", 4) == 0) { + dynUpd.file_meta_atom = &parsedAtoms[eval_atom]; + if (dynUpd.moov_atom != NULL) { + dynUpd.optimization_flags |= ROOT_META__PRECEDES__MOOV; + } // meta before moov would require more rewrite of the portion of the + // file than I want to do + } // but it wouldn't be all that difficult to accommodate rewriting + // everything from ftyp down to the first mdat, but currently its limited + // to last 'trak' child to mdat + } + return; } /*---------------------- APar_Optimize - mdat_test_only - the only info desired (if true, when printing the tree) is to know whether mdat precedes moov (and nullifing the concept of padding) + mdat_test_only - the only info desired (if true, when printing the tree) +is to know whether mdat precedes moov (and nullifing the concept of padding) - The visual: - ftyp - moov - trak - trak - trak - udta - meta - hdlr - free (functions as padding store when there are iTunes tags present) - ilst - meta - hdlr - meta - hdlr - free (functions as padding store when there are *no* iTunes tags present) - mdat + The visual: + ftyp + moov + trak + trak + trak + udta + meta + hdlr + free (functions +as padding store when there are iTunes tags present) ilst meta hdlr meta hdlr + free (functions as padding store when there are *no* iTunes tags +present) mdat ----------------------*/ void APar_Optimize(bool mdat_test_only) { - short last_child_of_moov = 0; - short eval_atom = 0; + short last_child_of_moov = 0; + short eval_atom = 0; - APar_LocateAtomLandmarks(); + APar_LocateAtomLandmarks(); - /* -----------move moov to precede any media data (mdat)--------- */ - if (move_moov_atom && (dynUpd.first_mdat_atom != NULL && (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV))) { //first_mdat_atom > 0 && moov_atom > 0 && moov_atom > first_mdat_atom) { - if (mdat_test_only) { - moov_atom_was_mooved = true; //this is all the interesting info required (during APar_PrintAtomicTree) - APar_FindPadding(mdat_test_only); - return; - } else { - APar_MoveAtom(dynUpd.moov_atom->AtomicNumber, dynUpd.first_mdat_atom->AtomicNumber); - moov_atom_was_mooved = true; - } - } - - /* -----------move a file/root level 'meta' to follow 'moov', but precede any media data(mdat)--------- */ - if (dynUpd.file_meta_atom != NULL && (dynUpd.optimization_flags & ROOT_META__PRECEDES__MOOV)) { - last_child_of_moov = APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber); - APar_MoveAtom(dynUpd.file_meta_atom->AtomicNumber, parsedAtoms[last_child_of_moov].NextAtomNumber); - } - - /* -----------optiizing the layout of movie-level atoms--------- */ - if (dynUpd.moov_atom != NULL) { //it can't be null, but just in case... - uint8_t extra_atom_count = 0; - AtomicInfo* last_trak_atom = NULL; - short total_moov_child_atoms = APar_ReturnChildrenAtoms(dynUpd.moov_atom->AtomicNumber, 0); - AtomicInfo** other_track_level_atom = (AtomicInfo**)malloc(total_moov_child_atoms * sizeof(AtomicInfo*)); - for (uint8_t xi = 0; xi < total_moov_child_atoms; xi++) { - other_track_level_atom[xi] = NULL; - } - - for(uint8_t moov_i = 1; moov_i <= total_moov_child_atoms; moov_i ++) { - eval_atom = APar_ReturnChildrenAtoms(dynUpd.moov_atom->AtomicNumber, moov_i); - if ( memcmp(parsedAtoms[eval_atom].AtomicName, "udta", 4) == 0 && parsedAtoms[eval_atom].AtomicLevel == 2) { - dynUpd.moov_udta_atom = &parsedAtoms[eval_atom]; - if (dynUpd.first_movielevel_metadata_tagging_atom == NULL) dynUpd.first_movielevel_metadata_tagging_atom = &parsedAtoms[eval_atom]; - } else if ( memcmp(parsedAtoms[eval_atom].AtomicName, "meta", 4) == 0 && parsedAtoms[eval_atom].AtomicLevel == 2) { - dynUpd.moov_meta_atom = &parsedAtoms[eval_atom]; - if (dynUpd.first_movielevel_metadata_tagging_atom == NULL) dynUpd.first_movielevel_metadata_tagging_atom = &parsedAtoms[eval_atom]; - } else if (memcmp(parsedAtoms[eval_atom].AtomicName, "trak", 4) == 0) { - last_trak_atom = &parsedAtoms[eval_atom]; - if (dynUpd.first_movielevel_metadata_tagging_atom != NULL) { - if (dynUpd.moov_meta_atom != NULL) dynUpd.optimization_flags |= MOOV_META__PRECEDES__TRACKS; - else if (dynUpd.moov_udta_atom != NULL) dynUpd.optimization_flags |= MOOV_UDTA__PRECEDES__TRACKS; - } - } else if (!(memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0)) { - if (dynUpd.moov_meta_atom != NULL || dynUpd.moov_udta_atom != NULL) { - other_track_level_atom[extra_atom_count] = &parsedAtoms[eval_atom]; //anything else gets noted because it *follows* moov.meta or moov.udta and needs to precede it - extra_atom_count++; - } - } - } - - if (last_trak_atom != NULL) { - short last_child_of_last_track = APar_FindLastChild_of_ParentAtom(last_trak_atom->AtomicNumber); - if (last_child_of_last_track > 0) { - dynUpd.last_trak_child_atom = &parsedAtoms[last_child_of_last_track]; - } - } - - /* -----------moving extra movie-level atoms (![trak,free,skip,meta,udta]) to precede the first metadata tagging hierarchy (moov.meta or moov.udta)--------- */ - if (extra_atom_count > 0 && dynUpd.first_movielevel_metadata_tagging_atom != NULL) { - for (uint8_t xxi = 0; xxi < extra_atom_count; xxi++) { - APar_MoveAtom((*other_track_level_atom + xxi)->AtomicNumber, dynUpd.first_movielevel_metadata_tagging_atom->AtomicNumber); - } - } - - /* -----------moving udta or meta to follow the trak atom--------- */ - if (dynUpd.optimization_flags & MOOV_META__PRECEDES__TRACKS) { - if (last_child_of_moov == 0) last_child_of_moov = APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber); - APar_MoveAtom(dynUpd.moov_meta_atom->AtomicNumber, parsedAtoms[last_child_of_moov].NextAtomNumber); - } else if (dynUpd.optimization_flags & MOOV_UDTA__PRECEDES__TRACKS) { - if (last_child_of_moov == 0) last_child_of_moov = APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber); - APar_MoveAtom(dynUpd.moov_udta_atom->AtomicNumber, parsedAtoms[last_child_of_moov].NextAtomNumber); - } - - free(other_track_level_atom); - other_track_level_atom = NULL; - } - - if (mdat_test_only) { - APar_FindPadding(mdat_test_only); - return; - } - - /* -----------delete free/skip atoms that precede media data or meta data--------- */ - if (dynUpd.first_otiose_freespace_atom != NULL && !alter_original) { //as a courtesty, for a full rewrite, eliminate L1 pre-mdat free/skip atoms - AtomicInfo* free_space = dynUpd.first_otiose_freespace_atom; - while (true) { - if (free_space->AtomicLevel > 1) break; - if (memcmp(free_space->AtomicName, "free", 4) != 0) break; //only get the consecutive 'free' space - AtomicInfo* nextatom = &parsedAtoms[free_space->NextAtomNumber]; - APar_EliminateAtom(free_space->AtomicNumber, free_space->NextAtomNumber); - free_space = nextatom; - } - } - return; + /* -----------move moov to precede any media data (mdat)--------- */ + if (move_moov_atom && + (dynUpd.first_mdat_atom != NULL && + (dynUpd.optimization_flags & + MEDIADATA__PRECEDES__MOOV))) { // first_mdat_atom > 0 && moov_atom > 0 + // && moov_atom > first_mdat_atom) { + if (mdat_test_only) { + moov_atom_was_mooved = true; // this is all the interesting info required + // (during APar_PrintAtomicTree) + APar_FindPadding(mdat_test_only); + return; + } else { + APar_MoveAtom(dynUpd.moov_atom->AtomicNumber, + dynUpd.first_mdat_atom->AtomicNumber); + moov_atom_was_mooved = true; + } + } + + /* -----------move a file/root level 'meta' to follow 'moov', but precede any + * media data(mdat)--------- */ + if (dynUpd.file_meta_atom != NULL && + (dynUpd.optimization_flags & ROOT_META__PRECEDES__MOOV)) { + last_child_of_moov = + APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber); + APar_MoveAtom(dynUpd.file_meta_atom->AtomicNumber, + parsedAtoms[last_child_of_moov].NextAtomNumber); + } + + /* -----------optiizing the layout of movie-level atoms--------- */ + if (dynUpd.moov_atom != NULL) { // it can't be null, but just in case... + uint8_t extra_atom_count = 0; + AtomicInfo *last_trak_atom = NULL; + short total_moov_child_atoms = + APar_ReturnChildrenAtoms(dynUpd.moov_atom->AtomicNumber, 0); + AtomicInfo **other_track_level_atom = + (AtomicInfo **)malloc(total_moov_child_atoms * sizeof(AtomicInfo *)); + for (uint8_t xi = 0; xi < total_moov_child_atoms; xi++) { + other_track_level_atom[xi] = NULL; + } + + for (uint8_t moov_i = 1; moov_i <= total_moov_child_atoms; moov_i++) { + eval_atom = + APar_ReturnChildrenAtoms(dynUpd.moov_atom->AtomicNumber, moov_i); + if (memcmp(parsedAtoms[eval_atom].AtomicName, "udta", 4) == 0 && + parsedAtoms[eval_atom].AtomicLevel == 2) { + dynUpd.moov_udta_atom = &parsedAtoms[eval_atom]; + if (dynUpd.first_movielevel_metadata_tagging_atom == NULL) + dynUpd.first_movielevel_metadata_tagging_atom = + &parsedAtoms[eval_atom]; + } else if (memcmp(parsedAtoms[eval_atom].AtomicName, "meta", 4) == 0 && + parsedAtoms[eval_atom].AtomicLevel == 2) { + dynUpd.moov_meta_atom = &parsedAtoms[eval_atom]; + if (dynUpd.first_movielevel_metadata_tagging_atom == NULL) + dynUpd.first_movielevel_metadata_tagging_atom = + &parsedAtoms[eval_atom]; + } else if (memcmp(parsedAtoms[eval_atom].AtomicName, "trak", 4) == 0) { + last_trak_atom = &parsedAtoms[eval_atom]; + if (dynUpd.first_movielevel_metadata_tagging_atom != NULL) { + if (dynUpd.moov_meta_atom != NULL) + dynUpd.optimization_flags |= MOOV_META__PRECEDES__TRACKS; + else if (dynUpd.moov_udta_atom != NULL) + dynUpd.optimization_flags |= MOOV_UDTA__PRECEDES__TRACKS; + } + } else if (!(memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || + memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0)) { + if (dynUpd.moov_meta_atom != NULL || dynUpd.moov_udta_atom != NULL) { + other_track_level_atom[extra_atom_count] = + &parsedAtoms[eval_atom]; // anything else gets noted because it + // *follows* moov.meta or moov.udta and + // needs to precede it + extra_atom_count++; + } + } + } + + if (last_trak_atom != NULL) { + short last_child_of_last_track = + APar_FindLastChild_of_ParentAtom(last_trak_atom->AtomicNumber); + if (last_child_of_last_track > 0) { + dynUpd.last_trak_child_atom = &parsedAtoms[last_child_of_last_track]; + } + } + + /* -----------moving extra movie-level atoms (![trak,free,skip,meta,udta]) + * to precede the first metadata tagging hierarchy (moov.meta or + * moov.udta)--------- */ + if (extra_atom_count > 0 && + dynUpd.first_movielevel_metadata_tagging_atom != NULL) { + for (uint8_t xxi = 0; xxi < extra_atom_count; xxi++) { + APar_MoveAtom( + (*other_track_level_atom + xxi)->AtomicNumber, + dynUpd.first_movielevel_metadata_tagging_atom->AtomicNumber); + } + } + + /* -----------moving udta or meta to follow the trak atom--------- */ + if (dynUpd.optimization_flags & MOOV_META__PRECEDES__TRACKS) { + if (last_child_of_moov == 0) + last_child_of_moov = + APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber); + APar_MoveAtom(dynUpd.moov_meta_atom->AtomicNumber, + parsedAtoms[last_child_of_moov].NextAtomNumber); + } else if (dynUpd.optimization_flags & MOOV_UDTA__PRECEDES__TRACKS) { + if (last_child_of_moov == 0) + last_child_of_moov = + APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber); + APar_MoveAtom(dynUpd.moov_udta_atom->AtomicNumber, + parsedAtoms[last_child_of_moov].NextAtomNumber); + } + + free(other_track_level_atom); + other_track_level_atom = NULL; + } + + if (mdat_test_only) { + APar_FindPadding(mdat_test_only); + return; + } + + /* -----------delete free/skip atoms that precede media data or meta + * data--------- */ + if (dynUpd.first_otiose_freespace_atom != NULL && + !alter_original) { // as a courtesty, for a full rewrite, eliminate L1 + // pre-mdat free/skip atoms + AtomicInfo *free_space = dynUpd.first_otiose_freespace_atom; + while (true) { + if (free_space->AtomicLevel > 1) + break; + if (memcmp(free_space->AtomicName, "free", 4) != 0) + break; // only get the consecutive 'free' space + AtomicInfo *nextatom = &parsedAtoms[free_space->NextAtomNumber]; + APar_EliminateAtom(free_space->AtomicNumber, free_space->NextAtomNumber); + free_space = nextatom; + } + } + return; } /////////////////////////////////////////////////////////////////////////////////////// -// Determine Atom Length // +// Determine Atom Length // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_DetermineNewFileLength - Sum up level 1 atoms (excludes any extranous EOF null bytes that iTunes occasionally writes - or used to) + Sum up level 1 atoms (excludes any extranous EOF null bytes that iTunes +occasionally writes - or used to) ----------------------*/ void APar_DetermineNewFileLength() { - new_file_size = 0; - short thisAtomNumber = 0; - while (true) { - if (parsedAtoms[thisAtomNumber].AtomicLevel == 1) { - if (parsedAtoms[thisAtomNumber].AtomicLengthExtended == 0) { - //normal 32-bit number when AtomicLengthExtended == 0 (for run-o-the-mill mdat & mdat.length=0) - new_file_size += parsedAtoms[thisAtomNumber].AtomicLength; //used in progressbar - } else { - //pseudo 64-bit mdat length - new_file_size += parsedAtoms[thisAtomNumber].AtomicLengthExtended; //used in progressbar - } - if (parsedAtoms[thisAtomNumber].AtomicLength == 0) { - new_file_size += file_size - parsedAtoms[thisAtomNumber].AtomicStart; //used in progressbar; mdat.length = 1 - } - } - if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) { - break; - } - thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber; - } - return; + new_file_size = 0; + short thisAtomNumber = 0; + while (true) { + if (parsedAtoms[thisAtomNumber].AtomicLevel == 1) { + if (parsedAtoms[thisAtomNumber].AtomicLengthExtended == 0) { + // normal 32-bit number when AtomicLengthExtended == 0 (for + // run-o-the-mill mdat & mdat.length=0) + new_file_size += + parsedAtoms[thisAtomNumber].AtomicLength; // used in progressbar + } else { + // pseudo 64-bit mdat length + new_file_size += parsedAtoms[thisAtomNumber] + .AtomicLengthExtended; // used in progressbar + } + if (parsedAtoms[thisAtomNumber].AtomicLength == 0) { + new_file_size += + file_size - + parsedAtoms[thisAtomNumber] + .AtomicStart; // used in progressbar; mdat.length = 1 + } + } + if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) { + break; + } + thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber; + } + return; } /*---------------------- @@ -3974,1063 +5176,1350 @@ ----------------------*/ void APar_DetermineAtomLengths() { - short rev_atom_loop = APar_FindLastAtom(); - //fprintf(stdout, "Last atom is named %s, num:%i\n", parsedAtoms[last_atom].AtomicName, parsedAtoms[last_atom].AtomicNumber); - - while (true) { - short next_atom = 0; - uint64_t atom_size = 0; - short previous_atom = 0; //only gets used in testing for atom under stsd - - //fprintf(stdout, "current atom is named %s, num:%i\n", parsedAtoms[rev_atom_loop].AtomicName, parsedAtoms[rev_atom_loop].AtomicNumber); - - if (rev_atom_loop == 0) { - break; //we seem to have hit the first atom - } else { - previous_atom = APar_FindPrecedingAtom(rev_atom_loop); - } - - uint32_t _atom_ = UInt32FromBigEndian(parsedAtoms[rev_atom_loop].AtomicName); - switch (_atom_) { - //the enumerated atoms here are all of DUAL_STATE_ATOM type - case 0x6D657461 : //'meta' - atom_size += 12; - break; - - case 0x73747364 : //'stsd' - atom_size += 16; - break; - - case 0x64726566 : //'dref' - atom_size += 16; - break; - - case 0x69696E66 : //'iinf' - atom_size += 14; - break; - - //accommodate parsing of atoms under stsd when required - case 0x6D703473 : { //mp4s - if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) { - atom_size += 16; - } else { - atom_size += 8; - } - break; - } - case 0x73727470 : //srtp - case 0x72747020 : { //'rtp ' - if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) { - atom_size += 24; - } else { - atom_size += 8; - } - break; - } - case 0x616C6163 : //alac - case 0x6D703461 : //mp4a - case 0x73616D72 : //samr - case 0x73617762 : //sawb - case 0x73617770 : //sawp - case 0x73657663 : //sevc - case 0x73716370 : //sqcp - case 0x73736D76 : //ssmv - case 0x64726D73 : { //drms - if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) { - atom_size += 36; - } else { - atom_size += 8; - } - break; - } - case 0x74783367 : { //tx3g - if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) { - atom_size += 46; - } else { - atom_size += 8; - } - break; - } - case 0x6D6A7032 : //mjp2 - case 0x6D703476 : //mp4v - case 0x61766331 : //avc1 - case 0x6A706567 : //jpeg - case 0x73323633 : //s263 - case 0x64726D69 : { //drmi - if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) { - atom_size += 86; - } else { - atom_size += 8; - } - break; - } - - default : - if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM) { - atom_size += parsedAtoms[rev_atom_loop].AtomicLength; - } else { - atom_size += 8; //all atoms have *at least* 4bytes length & 4 bytes name - } - break; - } - - if (parsedAtoms[rev_atom_loop].NextAtomNumber != 0) { - next_atom = parsedAtoms[rev_atom_loop].NextAtomNumber; - } - - while (parsedAtoms[next_atom].AtomicLevel > parsedAtoms[rev_atom_loop].AtomicLevel) { // eval all child atoms.... - //fprintf(stdout, "\ttest child atom %s, level:%i (sum %" PRIu64 ")\n", parsedAtoms[next_atom].AtomicName, parsedAtoms[next_atom].AtomicLevel, atom_size); - if (parsedAtoms[rev_atom_loop].AtomicLevel == ( parsedAtoms[next_atom].AtomicLevel - 1) ) { // only child atoms 1 level down - atom_size += parsedAtoms[next_atom].AtomicLength; - //fprintf(stdout, "\t\teval child atom %s, level:%i (sum %" PRIu64 ")\n", parsedAtoms[next_atom].AtomicName, parsedAtoms[next_atom].AtomicLevel, atom_size); - //fprintf(stdout, "\t\teval %s's child atom %s, level:%i (sum %" PRIu64 ", added %" PRIu64 ")\n", parsedAtoms[previous_atom].AtomicName, parsedAtoms[next_atom].AtomicName, parsedAtoms[next_atom].AtomicLevel, atom_size, parsedAtoms[next_atom].AtomicLength); - } else if (parsedAtoms[next_atom].AtomicLevel < parsedAtoms[rev_atom_loop].AtomicLevel) { - break; - } - next_atom = parsedAtoms[next_atom].NextAtomNumber; //increment to eval next atom - parsedAtoms[rev_atom_loop].AtomicLength = atom_size; - } - - if (_atom_ == 0x75647461 && parsedAtoms[rev_atom_loop].AtomicLevel > parsedAtoms[ parsedAtoms[rev_atom_loop].NextAtomNumber ].AtomicLevel) { //udta with no child atoms; get here by erasing the last asset in a 3gp file, and it won't quite erase because udta thinks its the former AtomicLength - parsedAtoms[rev_atom_loop].AtomicLength = 8; - } - if (_atom_ == 0x6D657461 && parsedAtoms[rev_atom_loop].AtomicLevel != parsedAtoms[ parsedAtoms[rev_atom_loop].NextAtomNumber ].AtomicLevel - 1) { //meta with no child atoms; get here by erasing the last existing uuid atom. - parsedAtoms[rev_atom_loop].AtomicLength = 12; - } - if (_atom_ == 0x696C7374 && parsedAtoms[rev_atom_loop].AtomicLevel != parsedAtoms[ parsedAtoms[rev_atom_loop].NextAtomNumber ].AtomicLevel - 1) { //ilst with no child atoms; get here by erasing the last piece of iTunes style metadata - parsedAtoms[rev_atom_loop].AtomicLength = 8; - } - - rev_atom_loop = APar_FindPrecedingAtom(parsedAtoms[rev_atom_loop].AtomicNumber); - - } - APar_DetermineNewFileLength(); - //APar_SimpleAtomPrintout(); - //APar_PrintAtomicTree(); - return; + short rev_atom_loop = APar_FindLastAtom(); + // fprintf(stdout, "Last atom is named %s, num:%i\n", + // parsedAtoms[last_atom].AtomicName, parsedAtoms[last_atom].AtomicNumber); + + while (true) { + short next_atom = 0; + uint64_t atom_size = 0; + short previous_atom = 0; // only gets used in testing for atom under stsd + + // fprintf(stdout, "current atom is named %s, num:%i\n", + // parsedAtoms[rev_atom_loop].AtomicName, + // parsedAtoms[rev_atom_loop].AtomicNumber); + + if (rev_atom_loop == 0) { + break; // we seem to have hit the first atom + } else { + previous_atom = APar_FindPrecedingAtom(rev_atom_loop); + } + + uint32_t _atom_ = + UInt32FromBigEndian(parsedAtoms[rev_atom_loop].AtomicName); + switch (_atom_) { + // the enumerated atoms here are all of DUAL_STATE_ATOM type + case 0x6D657461: //'meta' + atom_size += 12; + break; + + case 0x73747364: //'stsd' + atom_size += 16; + break; + + case 0x64726566: //'dref' + atom_size += 16; + break; + + case 0x69696E66: //'iinf' + atom_size += 14; + break; + + // accommodate parsing of atoms under stsd when required + case 0x6D703473: { // mp4s + if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && + parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && + deep_atom_scan) { + atom_size += 16; + } else { + atom_size += 8; + } + break; + } + case 0x73727470: // srtp + case 0x72747020: { //'rtp ' + if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && + parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && + deep_atom_scan) { + atom_size += 24; + } else { + atom_size += 8; + } + break; + } + case 0x616C6163: // alac + case 0x6D703461: // mp4a + case 0x73616D72: // samr + case 0x73617762: // sawb + case 0x73617770: // sawp + case 0x73657663: // sevc + case 0x73716370: // sqcp + case 0x73736D76: // ssmv + case 0x64726D73: { // drms + if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && + parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && + deep_atom_scan) { + atom_size += 36; + } else { + atom_size += 8; + } + break; + } + case 0x74783367: { // tx3g + if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && + parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && + deep_atom_scan) { + atom_size += 46; + } else { + atom_size += 8; + } + break; + } + case 0x6D6A7032: // mjp2 + case 0x6D703476: // mp4v + case 0x61766331: // avc1 + case 0x6A706567: // jpeg + case 0x73323633: // s263 + case 0x64726D69: { // drmi + if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && + parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && + deep_atom_scan) { + atom_size += 86; + } else { + atom_size += 8; + } + break; + } + + default: + if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && + parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM) { + atom_size += parsedAtoms[rev_atom_loop].AtomicLength; + } else { + atom_size += 8; // all atoms have *at least* 4bytes length & 4 bytes + // name + } + break; + } + + if (parsedAtoms[rev_atom_loop].NextAtomNumber != 0) { + next_atom = parsedAtoms[rev_atom_loop].NextAtomNumber; + } + + while (parsedAtoms[next_atom].AtomicLevel > + parsedAtoms[rev_atom_loop].AtomicLevel) { // eval all child atoms.... + // fprintf(stdout, "\ttest child atom %s, level:%i (sum %" PRIu64 ")\n", + // parsedAtoms[next_atom].AtomicName, parsedAtoms[next_atom].AtomicLevel, + // atom_size); + if (parsedAtoms[rev_atom_loop].AtomicLevel == + (parsedAtoms[next_atom].AtomicLevel - + 1)) { // only child atoms 1 level down + atom_size += parsedAtoms[next_atom].AtomicLength; + // fprintf(stdout, "\t\teval child atom %s, level:%i (sum %" PRIu64 + // ")\n", parsedAtoms[next_atom].AtomicName, + // parsedAtoms[next_atom].AtomicLevel, atom_size); fprintf(stdout, + // "\t\teval %s's child atom %s, level:%i (sum %" PRIu64 ", added %" + // PRIu64 ")\n", parsedAtoms[previous_atom].AtomicName, + // parsedAtoms[next_atom].AtomicName, + // parsedAtoms[next_atom].AtomicLevel, atom_size, + // parsedAtoms[next_atom].AtomicLength); + } else if (parsedAtoms[next_atom].AtomicLevel < + parsedAtoms[rev_atom_loop].AtomicLevel) { + break; + } + next_atom = + parsedAtoms[next_atom].NextAtomNumber; // increment to eval next atom + parsedAtoms[rev_atom_loop].AtomicLength = atom_size; + } + + if (_atom_ == 0x75647461 && + parsedAtoms[rev_atom_loop].AtomicLevel > + parsedAtoms[parsedAtoms[rev_atom_loop].NextAtomNumber] + .AtomicLevel) { // udta with no child atoms; get here by erasing + // the last asset in a 3gp file, and it won't + // quite erase because udta thinks its the + // former AtomicLength + parsedAtoms[rev_atom_loop].AtomicLength = 8; + } + if (_atom_ == 0x6D657461 && + parsedAtoms[rev_atom_loop].AtomicLevel != + parsedAtoms[parsedAtoms[rev_atom_loop].NextAtomNumber].AtomicLevel - + 1) { // meta with no child atoms; get here by erasing the last + // existing uuid atom. + parsedAtoms[rev_atom_loop].AtomicLength = 12; + } + if (_atom_ == 0x696C7374 && + parsedAtoms[rev_atom_loop].AtomicLevel != + parsedAtoms[parsedAtoms[rev_atom_loop].NextAtomNumber].AtomicLevel - + 1) { // ilst with no child atoms; get here by erasing the last + // piece of iTunes style metadata + parsedAtoms[rev_atom_loop].AtomicLength = 8; + } + + rev_atom_loop = + APar_FindPrecedingAtom(parsedAtoms[rev_atom_loop].AtomicNumber); + } + APar_DetermineNewFileLength(); + // APar_SimpleAtomPrintout(); + // APar_PrintAtomicTree(); + return; } /////////////////////////////////////////////////////////////////////////////////////// -// Atom Writing Functions // +// Atom Writing Functions // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- APar_ValidateAtoms - A gaggle of tests go on here - to TRY to make sure that files are not corrupted. + A gaggle of tests go on here - to TRY to make sure that files are not +corrupted. - 1. because there is a limit to the number of atoms, test to make sure we haven't hit MAX_ATOMS (probably only likely on a 300MB fragmented file ever 2 secs) - 2. test that the atom name is at least 4 letters long. So far, only quicktime atoms have NULLs in their names. - 3. For files over 300k, make sure that no atom can present larger than the filesize (which would be bad); handy for when the file isn't parsed correctly - 4. Test to make sure 'mdat' is at file-level. That is the only place it should ever be. - 5. If its is a child atom that was set (and resides in memory), then its AtomicData should != NULL. - 6. (A crude) Test to see if 'trak' atoms have only a 'udta' child. If setting a copyright notice on a track at index built with some compilers faux 'trak's are made - 7. If the file shunk below 90% (after accounting for additions or removals), error out - something went awry. + 1. because there is a limit to the number of atoms, test to make +sure we haven't hit MAX_ATOMS (probably only likely on a 300MB fragmented file +ever 2 secs) + 2. test that the atom name is at least 4 letters long. So far, +only quicktime atoms have NULLs in their names. + 3. For files over 300k, make sure that no atom can present +larger than the filesize (which would be bad); handy for when the file isn't +parsed correctly + 4. Test to make sure 'mdat' is at file-level. That is the only +place it should ever be. + 5. If its is a child atom that was set (and resides in memory), +then its AtomicData should != NULL. + 6. (A crude) Test to see if 'trak' atoms have only a 'udta' +child. If setting a copyright notice on a track at index built with some +compilers faux 'trak's are made + 7. If the file shunk below 90% (after accounting for additions +or removals), error out - something went awry. ----------------------*/ void APar_ValidateAtoms() { - bool atom_name_with_4_characters = true; - short iter = 0; - uint64_t simple_tally = 0; - uint8_t atom_ftyp_count = 0; - uint16_t external_data = 0; - - //test1 - if (atom_number > MAX_ATOMS) { - fprintf(stderr, "AtomicParsley error: amount of atoms exceeds internal limit. Aborting.\n"); - exit(1); - } - - while (true) { - //test2 - // there are valid atom names that are 0x00000001 - but I haven't seen them in MPEG-4 files, but they could show up, so this isn't a hard error - if ( strlen(parsedAtoms[iter].AtomicName) < 4 && parsedAtoms[iter].AtomicClassification != EXTENDED_ATOM) { - atom_name_with_4_characters = false; - } - - //test3 - //test for atoms that are going to be greater than out current file size; problem is we could be adding a 1MB pix to a 200k 3gp file; only fail for a file > 300k file; otherwise there would have to be more checks (like artwork present, but a zealous tagger could make moov.lengt > filzesize) - if (parsedAtoms[iter].AtomicLength > file_size && file_size > 300000) { - if (parsedAtoms[iter].AtomicData == NULL) { - fprintf(stderr, "AtomicParsley error: an atom was detected that presents as larger than filesize. Aborting. %c\n", '\a'); - fprintf(stderr, "atom %s is %" PRIu64 " bytes long which is greater than the filesize of %" PRIu64 "\n", parsedAtoms[iter].AtomicName, parsedAtoms[iter].AtomicLength, file_size); - exit(1); //its conceivable to repair such an off length by the surrounding atoms constrained by file_size - just not anytime soon; probly would catch a foobar2000 0.9 tagged file - } - } - - if (parsedAtoms[iter].AtomicLevel == 1) { - if (parsedAtoms[iter].AtomicLength == 0 && strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0) { - simple_tally = file_size - parsedAtoms[iter].AtomicStart; - } else { - simple_tally += parsedAtoms[iter].AtomicLength == 1 ? parsedAtoms[iter].AtomicLengthExtended : parsedAtoms[iter].AtomicLength; - } - } - - //test4 - if (strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0 && parsedAtoms[iter].AtomicLevel != 1) { - fprintf(stderr, "AtomicParsley error: mdat atom was found not at file level. Aborting. %c\n", '\a'); - exit(1); //the error which forced this was some bad atom length redetermination; probably won't be fixed - } - //test5 - if (parsedAtoms[iter].AtomicStart == 0 && parsedAtoms[iter].AtomicData == NULL && - parsedAtoms[iter].AtomicNumber > 0 && parsedAtoms[iter].AtomicContainerState == CHILD_ATOM) { - fprintf(stderr, "AtomicParsley error: a '%s' atom was rendered to NULL length. Aborting. %c\n", parsedAtoms[iter].AtomicName, '\a'); - exit(1); //data was not written to AtomicData for this new atom. - } - - //test6 - if (memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 && parsedAtoms[iter+1].NextAtomNumber != 0) { //prevent writing any malformed tracks - if (!(memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "tkhd", 4) == 0 || - memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "tref", 4) == 0 || - memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "mdia", 4) == 0 || - memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "edts", 4) == 0 || - memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "meta", 4) == 0 || - memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "tapt", 4) == 0 )) { - if (APar_ReturnChildrenAtoms(iter, 0) < 2) { - //a 'trak' must contain 'tkhd' & 'mdia' at the very least - fprintf(stderr, "AtomicParsley error: incorrect track structure containing atom %s. %c\n", parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, '\a'); - exit(1); - } - } - } - - if (memcmp(parsedAtoms[iter].AtomicName, "ftyp", 4) == 0) { - atom_ftyp_count++; - } - - if ( (memcmp(parsedAtoms[iter].AtomicName, "stco", 4) == 0 || memcmp(parsedAtoms[iter].AtomicName, "co64", 4) == 0) && parsedAtoms[iter].ancillary_data != 1) { - external_data++; - } - iter=parsedAtoms[iter].NextAtomNumber; - if (iter == 0) { - break; - } - } - - //test7 - double perdiff = (double)((double)(simple_tally) * 100.0 / (double)(file_size-removed_bytes_tally) ); - int percentage_difference = (int)lroundf(perdiff); - - if (percentage_difference < 90 && file_size > 300000) { //only kick in when files are over 300k & 90% of the size - fprintf(stderr, "AtomicParsley error: total existing atoms present as larger than filesize. Aborting. %c\n", '\a'); - //APar_PrintAtomicTree(); - fprintf(stdout, "%i %" PRIu64 "\n", percentage_difference, simple_tally); - exit(1); - } - - if (atom_ftyp_count != 1) { - fprintf(stdout, "AtomicParsley error: unresolved looping of atoms. Aborting. %c\n", '\a'); - exit(1); - } - - if (!atom_name_with_4_characters) { - fprintf(stdout, "AtomicParsley warning: atom(s) were detected with atypical names containing NULLs\n"); - } - - if (external_data > 0) { - fprintf(stdout, "AtomicParsley warning: externally referenced data found."); - } - - return; -} - -void APar_DeriveNewPath(const char *filePath, char* temp_path, int output_type, const char* file_kind, const char* forced_suffix, bool random_filename = true) { - const char* suffix = NULL; - const char* file_name = NULL; - size_t file_name_len = 0; - bool relative_path = false; - - if (forced_suffix == NULL) { - suffix = strrchr(filePath, '.'); - } else { - suffix = forced_suffix; - } - - size_t filepath_len = strlen(filePath); - size_t base_len = filepath_len-strlen(suffix); - if (output_type >= 0) { - memcpy(temp_path, filePath, base_len); - memcpy(temp_path + base_len, file_kind, strlen(file_kind)); - - } else if (output_type == -1) { //make the output file invisible by prefacing the filename with '.' -#if defined (_WIN32) && !defined (__CYGWIN__) - memcpy(temp_path, filePath, base_len); - memcpy(temp_path + base_len, file_kind, strlen(file_kind)); + bool atom_name_with_4_characters = true; + short iter = 0; + uint64_t simple_tally = 0; + uint8_t atom_ftyp_count = 0; + uint16_t external_data = 0; + + // test1 + if (atom_number > MAX_ATOMS) { + fprintf(stderr, + "AtomicParsley error: amount of atoms exceeds internal " + "limit. Aborting.\n"); + exit(1); + } + + while (true) { + // test2 + // there are valid atom names that are 0x00000001 - but I haven't seen them + // in MPEG-4 files, but they could show up, so this isn't a hard error + if (strlen(parsedAtoms[iter].AtomicName) < 4 && + parsedAtoms[iter].AtomicClassification != EXTENDED_ATOM) { + atom_name_with_4_characters = false; + } + + // test3 + // test for atoms that are going to be greater than out current file size; + // problem is we could be adding a 1MB pix to a 200k 3gp file; only fail for + // a file > 300k file; otherwise there would have to be more checks (like + // artwork present, but a zealous tagger could make moov.lengt > filzesize) + if (parsedAtoms[iter].AtomicLength > file_size && file_size > 300000) { + if (parsedAtoms[iter].AtomicData == NULL) { + fprintf(stderr, + "AtomicParsley error: an atom was detected that presents as " + "larger than filesize. Aborting. %c\n", + '\a'); + fprintf(stderr, + "atom %s is %" PRIu64 + " bytes long which is greater than the filesize of %" PRIu64 + "\n", + parsedAtoms[iter].AtomicName, + parsedAtoms[iter].AtomicLength, + file_size); + exit( + 1); // its conceivable to repair such an off length by the + // surrounding atoms constrained by file_size - just not anytime + // soon; probly would catch a foobar2000 0.9 tagged file + } + } + + if (parsedAtoms[iter].AtomicLevel == 1) { + if (parsedAtoms[iter].AtomicLength == 0 && + strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0) { + simple_tally = file_size - parsedAtoms[iter].AtomicStart; + } else { + simple_tally += parsedAtoms[iter].AtomicLength == 1 + ? parsedAtoms[iter].AtomicLengthExtended + : parsedAtoms[iter].AtomicLength; + } + } + + // test4 + if (strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0 && + parsedAtoms[iter].AtomicLevel != 1) { + fprintf(stderr, + "AtomicParsley error: mdat atom was found not at file level. " + "Aborting. %c\n", + '\a'); + exit(1); // the error which forced this was some bad atom length + // redetermination; probably won't be fixed + } + // test5 + if (parsedAtoms[iter].AtomicStart == 0 && + parsedAtoms[iter].AtomicData == NULL && + parsedAtoms[iter].AtomicNumber > 0 && + parsedAtoms[iter].AtomicContainerState == CHILD_ATOM) { + fprintf(stderr, + "AtomicParsley error: a '%s' atom was rendered to NULL length. " + "Aborting. %c\n", + parsedAtoms[iter].AtomicName, + '\a'); + exit(1); // data was not written to AtomicData for this new atom. + } + + // test6 + if (memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 && + parsedAtoms[iter + 1].NextAtomNumber != + 0) { // prevent writing any malformed tracks + if (!(memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName, + "tkhd", + 4) == 0 || + memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName, + "tref", + 4) == 0 || + memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName, + "mdia", + 4) == 0 || + memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName, + "edts", + 4) == 0 || + memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName, + "meta", + 4) == 0 || + memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName, + "tapt", + 4) == 0)) { + if (APar_ReturnChildrenAtoms(iter, 0) < 2) { + // a 'trak' must contain 'tkhd' & 'mdia' at the very least + fprintf(stderr, + "AtomicParsley error: incorrect track structure containing " + "atom %s. %c\n", + parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName, + '\a'); + exit(1); + } + } + } + + if (memcmp(parsedAtoms[iter].AtomicName, "ftyp", 4) == 0) { + atom_ftyp_count++; + } + + if ((memcmp(parsedAtoms[iter].AtomicName, "stco", 4) == 0 || + memcmp(parsedAtoms[iter].AtomicName, "co64", 4) == 0) && + parsedAtoms[iter].ancillary_data != 1) { + external_data++; + } + iter = parsedAtoms[iter].NextAtomNumber; + if (iter == 0) { + break; + } + } + + // test7 + double perdiff = (double)((double)(simple_tally)*100.0 / + (double)(file_size - removed_bytes_tally)); + int percentage_difference = (int)lroundf(perdiff); + + if (percentage_difference < 90 && + file_size > + 300000) { // only kick in when files are over 300k & 90% of the size + fprintf(stderr, + "AtomicParsley error: total existing atoms present as larger than " + "filesize. Aborting. %c\n", + '\a'); + // APar_PrintAtomicTree(); + fprintf(stdout, "%i %" PRIu64 "\n", percentage_difference, simple_tally); + exit(1); + } + + if (atom_ftyp_count != 1) { + fprintf(stdout, + "AtomicParsley error: unresolved looping of atoms. Aborting. %c\n", + '\a'); + exit(1); + } + + if (!atom_name_with_4_characters) { + fprintf(stdout, + "AtomicParsley warning: atom(s) were detected with " + "atypical names containing NULLs\n"); + } + + if (external_data > 0) { + fprintf(stdout, "AtomicParsley warning: externally referenced data found."); + } + + return; +} + +void APar_DeriveNewPath(const char *filePath, + char *temp_path, + int output_type, + const char *file_kind, + const char *forced_suffix, + bool random_filename = true) { + const char *suffix = NULL; + const char *file_name = NULL; + size_t file_name_len = 0; + bool relative_path = false; + + if (forced_suffix == NULL) { + suffix = strrchr(filePath, '.'); + } else { + suffix = forced_suffix; + } + + size_t filepath_len = strlen(filePath); + size_t base_len = filepath_len - strlen(suffix); + if (output_type >= 0) { + memcpy(temp_path, filePath, base_len); + memcpy(temp_path + base_len, file_kind, strlen(file_kind)); + + } else if (output_type == -1) { // make the output file invisible by prefacing + // the filename with '.' +#if defined(_WIN32) && !defined(__CYGWIN__) + memcpy(temp_path, filePath, base_len); + memcpy(temp_path + base_len, file_kind, strlen(file_kind)); #else - file_name = strrchr(filePath, '/'); - if (file_name != NULL) { - file_name_len = strlen(file_name); - memcpy(temp_path, filePath, filepath_len-file_name_len+1); - } else { - if( getcwd(temp_path, MAXPATHLEN) == NULL ) { - printf("Error getting working directory: %s\n", strerror(errno)); - exit(1); - } - file_name = (char*)filePath; - file_name_len = strlen(file_name); - memcpy(temp_path + strlen(temp_path), "/", 1); - relative_path = true; - } - memcpy(temp_path + strlen(temp_path), ".", 1); - memcpy(temp_path + strlen(temp_path), (relative_path ? file_name : file_name+1), file_name_len - strlen(suffix) -1); - memcpy(temp_path + strlen(temp_path), file_kind, strlen(file_kind)); + file_name = strrchr(filePath, '/'); + if (file_name != NULL) { + file_name_len = strlen(file_name); + memcpy(temp_path, filePath, filepath_len - file_name_len + 1); + } else { + if (getcwd(temp_path, MAXPATHLEN) == NULL) { + printf("Error getting working directory: %s\n", strerror(errno)); + exit(1); + } + file_name = (char *)filePath; + file_name_len = strlen(file_name); + memcpy(temp_path + strlen(temp_path), "/", 1); + relative_path = true; + } + memcpy(temp_path + strlen(temp_path), ".", 1); + memcpy(temp_path + strlen(temp_path), + (relative_path ? file_name : file_name + 1), + file_name_len - strlen(suffix) - 1); + memcpy(temp_path + strlen(temp_path), file_kind, strlen(file_kind)); #endif - } + } - if (random_filename) { - char randstring[6]; - srand((int) time(NULL)); //Seeds rand() - int randNum = rand()%100000; - sprintf(randstring, "%i", randNum); - memcpy(temp_path + strlen(temp_path), randstring, strlen(randstring)); - } - - if (forced_suffix_type == FORCE_M4B_TYPE) { - memcpy(temp_path + strlen(temp_path), ".m4b", 4); - } else { - memcpy(temp_path + strlen(temp_path), suffix, strlen(suffix) ); - } - return; -} - -void APar_MetadataFileDump(const char* ISObasemediafile) { - char* dump_file_name=(char*)malloc( sizeof(char)* (strlen(ISObasemediafile) +12 +1) ); - memset(dump_file_name, 0, sizeof(char)* (strlen(ISObasemediafile) +12 +1) ); - - FILE* dump_file; - AtomicInfo* userdata_atom = APar_FindAtom("moov.udta", false, SIMPLE_ATOM, 0); - - //make sure that the atom really exists - if (userdata_atom != NULL) { - char* dump_buffer=(char*)malloc( sizeof(char)* userdata_atom->AtomicLength +1 ); - memset(dump_buffer, 0, sizeof(char)* userdata_atom->AtomicLength +1 ); - - APar_DeriveNewPath(ISObasemediafile, dump_file_name, 1, "-dump-", ".raw"); - dump_file = APar_OpenFile(dump_file_name, "wb"); - if (dump_file != NULL) { - //body of atom writing here - - APar_readX(dump_buffer, source_file, userdata_atom->AtomicStart, (size_t)userdata_atom->AtomicLength); - - fwrite(dump_buffer, (size_t)userdata_atom->AtomicLength, 1, dump_file); - fclose(dump_file); - - fprintf(stdout, " Metadata dumped to %s\n", dump_file_name); - } - free(dump_buffer); - dump_buffer=NULL; - - } else { - fprintf(stdout, "AtomicParsley error: no moov.udta atom was found to dump out to file.\n"); - } - - return; -} - -void APar_UpdateModTime(AtomicInfo* container_header_atom) { - container_header_atom->AtomicData = (char*)calloc(1, sizeof(char)* (size_t)container_header_atom->AtomicLength ); - APar_readX(container_header_atom->AtomicData, source_file, container_header_atom->AtomicStart+12, container_header_atom->AtomicLength-12); - - uint32_t current_time = APar_get_mpeg4_time(); - if ((container_header_atom->AtomicVerFlags & 0xFFFFFF) == 1) { - UInt64_TO_String8(current_time, container_header_atom->AtomicData+8); - } else { - UInt32_TO_String4(current_time, container_header_atom->AtomicData+4); - } - return; + if (random_filename) { + char randstring[6]; + srand((int)time(NULL)); // Seeds rand() + int randNum = rand() % 100000; + sprintf(randstring, "%i", randNum); + memcpy(temp_path + strlen(temp_path), randstring, strlen(randstring)); + } + + if (forced_suffix_type == FORCE_M4B_TYPE) { + memcpy(temp_path + strlen(temp_path), ".m4b", 4); + } else { + memcpy(temp_path + strlen(temp_path), suffix, strlen(suffix)); + } + return; } -void APar_ShellProgressBar(uint64_t bytes_written) { - if (dynUpd.updage_by_padding) { - return; - } - static int update_count = 0; - - if (update_count++ < 5) { - return; - } - update_count = 0; - - double dispprog = (double)bytes_written / (double)new_file_size - * max_display_width; - int display_progress = (int)lroundf(dispprog); - double percomp = 100.0 * (double)bytes_written / (double)new_file_size; - int percentage_complete = (int)lroundf(percomp); - - char *p = file_progress_buffer; - strcpy(p, " Progress: "); - p += strlen(p); - - memset(p, '=', display_progress); - p += display_progress; - - sprintf(p, ">%3d%% ", percentage_complete); - p += strlen(p); - - memset(p, '-', max_display_width - display_progress); - p += max_display_width - display_progress; - p[0] = '|'; - p[1] = '\0'; - - fprintf(stdout, "%s\r", file_progress_buffer); - fflush(stdout); -} - -void APar_MergeTempFile(FILE* dest_file, FILE *src_file, uint64_t src_file_size, uint64_t dest_position, char* &buffer) { - uint64_t file_pos = 0; - while (file_pos <= src_file_size) { - if (file_pos + max_buffer <= src_file_size ) { - APar_readX(buffer, src_file, file_pos, max_buffer); +void APar_MetadataFileDump(const char *ISObasemediafile) { + char *dump_file_name = + (char *)malloc(sizeof(char) * (strlen(ISObasemediafile) + 12 + 1)); + memset(dump_file_name, 0, sizeof(char) * (strlen(ISObasemediafile) + 12 + 1)); + + FILE *dump_file; + AtomicInfo *userdata_atom = APar_FindAtom("moov.udta", false, SIMPLE_ATOM, 0); + + // make sure that the atom really exists + if (userdata_atom != NULL) { + char *dump_buffer = + (char *)malloc(sizeof(char) * userdata_atom->AtomicLength + 1); + memset(dump_buffer, 0, sizeof(char) * userdata_atom->AtomicLength + 1); + + APar_DeriveNewPath(ISObasemediafile, dump_file_name, 1, "-dump-", ".raw"); + dump_file = APar_OpenFile(dump_file_name, "wb"); + if (dump_file != NULL) { + // body of atom writing here + + APar_readX(dump_buffer, + source_file, + userdata_atom->AtomicStart, + (size_t)userdata_atom->AtomicLength); + + fwrite(dump_buffer, (size_t)userdata_atom->AtomicLength, 1, dump_file); + fclose(dump_file); + + fprintf(stdout, " Metadata dumped to %s\n", dump_file_name); + } + free(dump_buffer); + dump_buffer = NULL; + + } else { + fprintf(stdout, + "AtomicParsley error: no moov.udta atom was found to dump " + "out to file.\n"); + } - //fseek(dest_file, dest_position + file_pos, SEEK_SET); -#if defined(_WIN32) - fpos_t file_offset = dest_position + file_pos; -#elif defined(__GLIBC__) - fpos_t file_offset = {0}; - file_offset.__pos = dest_position + file_pos; -#else - off_t file_offset = dest_position + file_pos; -#endif - fsetpos(dest_file, &file_offset); - fwrite(buffer, max_buffer, 1, dest_file); - file_pos += max_buffer; - - } else { - APar_readX(buffer, src_file, file_pos, src_file_size - file_pos); - //fprintf(stdout, "buff starts with %s\n", buffer+4); -#if defined(_WIN32) - fpos_t file_offset = dest_position + file_pos; -#elif defined(__GLIBC__) - fpos_t file_offset = {0}; - file_offset.__pos = dest_position + file_pos; -#else - off_t file_offset = dest_position + file_pos; -#endif - fsetpos(dest_file, &file_offset ); - fwrite(buffer, src_file_size - file_pos, 1, dest_file); - file_pos += src_file_size - file_pos; - break; - } - } - if (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) { -#if defined (_WIN32) && !defined (__CYGWIN__) - fflush(dest_file); - SetEndOfFile((HANDLE)_get_osfhandle(_fileno(dest_file))); + return; +} + +void APar_UpdateModTime(AtomicInfo *container_header_atom) { + container_header_atom->AtomicData = (char *)calloc( + 1, sizeof(char) * (size_t)container_header_atom->AtomicLength); + APar_readX(container_header_atom->AtomicData, + source_file, + container_header_atom->AtomicStart + 12, + container_header_atom->AtomicLength - 12); + + uint32_t current_time = APar_get_mpeg4_time(); + if ((container_header_atom->AtomicVerFlags & 0xFFFFFF) == 1) { + UInt64_TO_String8(current_time, container_header_atom->AtomicData + 8); + } else { + UInt32_TO_String4(current_time, container_header_atom->AtomicData + 4); + } + return; +} + +void APar_ShellProgressBar(uint64_t bytes_written) { + if (dynUpd.updage_by_padding) { + return; + } + static int update_count = 0; + + if (update_count++ < 5) { + return; + } + update_count = 0; + + double dispprog = + (double)bytes_written / (double)new_file_size * max_display_width; + int display_progress = (int)lroundf(dispprog); + double percomp = 100.0 * (double)bytes_written / (double)new_file_size; + int percentage_complete = (int)lroundf(percomp); + + char *p = file_progress_buffer; + strcpy(p, " Progress: "); + p += strlen(p); + + memset(p, '=', display_progress); + p += display_progress; + + sprintf(p, ">%3d%% ", percentage_complete); + p += strlen(p); + + memset(p, '-', max_display_width - display_progress); + p += max_display_width - display_progress; + p[0] = '|'; + p[1] = '\0'; + + fprintf(stdout, "%s\r", file_progress_buffer); + fflush(stdout); +} + +void APar_MergeTempFile(FILE *dest_file, + FILE *src_file, + uint64_t src_file_size, + uint64_t dest_position, + char *&buffer) { + uint64_t file_pos = 0; + while (file_pos <= src_file_size) { + if (file_pos + max_buffer <= src_file_size) { + APar_readX(buffer, src_file, file_pos, max_buffer); + + fseeko(dest_file, dest_position + file_pos, SEEK_SET); + fwrite(buffer, max_buffer, 1, dest_file); + file_pos += max_buffer; + + } else { + APar_readX(buffer, src_file, file_pos, src_file_size - file_pos); + // fprintf(stdout, "buff starts with %s\n", buffer+4); + fseeko(dest_file, dest_position + file_pos, SEEK_SET); + fwrite(buffer, src_file_size - file_pos, 1, dest_file); + file_pos += src_file_size - file_pos; + break; + } + } + if (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) { +#if defined(_WIN32) && !defined(__CYGWIN__) + fflush(dest_file); + SetEndOfFile((HANDLE)_get_osfhandle(_fileno(dest_file))); #else - if(ftruncate(fileno(dest_file), src_file_size+dest_position) == -1) { - perror("Failed to truncate file: "); - exit(1); - } + if (ftruncate(fileno(dest_file), src_file_size + dest_position) == -1) { + perror("Failed to truncate file: "); + exit(1); + } #endif - } - return; + } + return; } #ifdef __linux__ /* use kernel provided zero-copy interface to improve throughput * around the data passthru portions of our operation; no sense * copying multiple GB of data around in memory if we can avoid it */ -uint64_t splice_copy(int sfd, int ofd, uint64_t block_size, - uint64_t src_offset, uint64_t dest_offset, uint64_t tally) -{ - int pfd[2]; - int res; - uint64_t lim = LONG_MAX; - loff_t spos = src_offset; - loff_t dpos = dest_offset; - long didread; - long didwrite; - uint64_t bytes_written = 0; - - res = pipe(pfd); - if (res < 0) { - return 0; - } - - while (block_size) { - long toread = MIN(block_size, lim); - - /* splice source data into pipe. - * This will typically be 64k at a time */ - didread = splice(sfd, &spos, pfd[1], NULL, toread, - SPLICE_F_MORE|SPLICE_F_MOVE); - - if (didread <= 0) { - if (errno == EINVAL || errno == ENOSYS) { - /* splice is not supported by source */ - break; - } - fprintf(stderr, "splice(read): %ld of %lu (%s)\n", - didread, toread, strerror(errno)); - break; - } - - block_size -= didread; - - while (didread > 0) { - /* splice from pipe into dest */ - didwrite = splice(pfd[0], NULL, ofd, &dpos, didread, - SPLICE_F_MORE|SPLICE_F_MOVE); - - if (didwrite <= 0) { - if (errno == EINVAL || errno == ENOSYS) { - /* splice is not supported by dest */ - break; - } - fprintf(stderr, "splice(write): %ld of %lu (%s)\n", - didwrite, didread, strerror(errno)); - break; - } - - bytes_written += didwrite; - didread -= didwrite; - } - APar_ShellProgressBar(tally + bytes_written); - } - - close(pfd[0]); - close(pfd[1]); - return bytes_written; +uint64_t splice_copy(int sfd, + int ofd, + uint64_t block_size, + uint64_t src_offset, + uint64_t dest_offset, + uint64_t tally) { + int pfd[2]; + int res; + uint64_t lim = LONG_MAX; + loff_t spos = src_offset; + loff_t dpos = dest_offset; + long didread; + long didwrite; + uint64_t bytes_written = 0; + + res = pipe(pfd); + if (res < 0) { + return 0; + } + + while (block_size) { + long toread = std::min(block_size, lim); + + /* splice source data into pipe. + * This will typically be 64k at a time */ + didread = + splice(sfd, &spos, pfd[1], NULL, toread, SPLICE_F_MORE | SPLICE_F_MOVE); + + if (didread <= 0) { + if (errno == EINVAL || errno == ENOSYS) { + /* splice is not supported by source */ + break; + } + fprintf(stderr, + "splice(read): %ld of %lu (%s)\n", + didread, + toread, + strerror(errno)); + break; + } + + block_size -= didread; + + while (didread > 0) { + /* splice from pipe into dest */ + didwrite = splice( + pfd[0], NULL, ofd, &dpos, didread, SPLICE_F_MORE | SPLICE_F_MOVE); + + if (didwrite <= 0) { + if (errno == EINVAL || errno == ENOSYS) { + /* splice is not supported by dest */ + break; + } + fprintf(stderr, + "splice(write): %ld of %lu (%s)\n", + didwrite, + didread, + strerror(errno)); + break; + } + + bytes_written += didwrite; + didread -= didwrite; + } + APar_ShellProgressBar(tally + bytes_written); + } + + close(pfd[0]); + close(pfd[1]); + return bytes_written; } #endif -uint64_t block_copy(FILE *source_file, FILE *out_file, - char *&buffer, - uint64_t tally, uint64_t block_size, - uint64_t src_offset, uint64_t dest_offset) -{ - uint64_t toread = block_size; - uint64_t bytes_written = 0; - size_t didread; - size_t didwrite; +uint64_t block_copy(FILE *source_file, + FILE *out_file, + char *&buffer, + uint64_t tally, + uint64_t block_size, + uint64_t src_offset, + uint64_t dest_offset) { + uint64_t toread = block_size; + uint64_t bytes_written = 0; + size_t didread; + size_t didwrite; #ifdef __linux__ - if (block_size > 65536) { - fflush(out_file); - - bytes_written = splice_copy(fileno(source_file), fileno(out_file), - block_size, src_offset, dest_offset, tally); + if (block_size > 65536) { + fflush(out_file); - if (bytes_written != 0) { - return bytes_written; - } - } + bytes_written = splice_copy(fileno(source_file), + fileno(out_file), + block_size, + src_offset, + dest_offset, + tally); + + if (bytes_written != 0) { + return bytes_written; + } + } #endif - fseeko(source_file, src_offset, SEEK_SET); - fseeko(out_file, dest_offset, SEEK_SET); + fseeko(source_file, src_offset, SEEK_SET); + fseeko(out_file, dest_offset, SEEK_SET); - while (toread) { - char *bpos; + while (toread) { + char *bpos; - didread = fread(buffer, 1, MIN(max_buffer, toread), source_file); - if (didread == 0) { - fprintf(stderr, "read: eof=%d err=%d %s\n", - feof(source_file), - ferror(source_file), - strerror(errno)); - break; - } - toread -= didread; - - bpos = buffer; - - while (didread) { - didwrite = fwrite(bpos, 1, didread, out_file); - didread -= didwrite; - bpos += didwrite; - bytes_written += didwrite; - - APar_ShellProgressBar(tally + bytes_written); - } - } - return bytes_written; -} - -uint64_t APar_WriteAtomically(FILE* source_file, FILE* temp_file, - bool from_file, char* &buffer, uint64_t bytes_written_tally, - short this_atom) -{ - uint64_t bytes_written = 0; - - if (parsedAtoms[this_atom].AtomicLength > 1 && parsedAtoms[this_atom].AtomicLength < 8) { //prevents any spurious atoms from appearing - return bytes_written; - } - - //write the length of the atom first... taken from our tree in memory - UInt32_TO_String4(parsedAtoms[this_atom].AtomicLength, twenty_byte_buffer); - fseeko(temp_file, bytes_written_tally, SEEK_SET); - fwrite(twenty_byte_buffer, 4, 1, temp_file); - bytes_written += 4; - - //since we have already writen the length out to the file, it can be changed now with impunity - if (parsedAtoms[this_atom].AtomicLength == 0) { //the spec says if an atom has a length of 0, it extends to EOF - parsedAtoms[this_atom].AtomicLength = file_size - parsedAtoms[this_atom].AtomicLength; - } else if (parsedAtoms[this_atom].AtomicLength == 1) { - //part of the pseudo 64-bit support - parsedAtoms[this_atom].AtomicLength = parsedAtoms[this_atom].AtomicLengthExtended; - } else if (parsedAtoms[this_atom].AtomicContainerState == DUAL_STATE_ATOM) { - if (memcmp(parsedAtoms[this_atom].AtomicName, "dref", 4) == 0) { - parsedAtoms[this_atom].AtomicLength = 16; - } else if (memcmp(parsedAtoms[this_atom].AtomicName, "iinf", 4) == 0) { - parsedAtoms[this_atom].AtomicLength = 14; - } - } - - if (deep_atom_scan && parsedAtoms[this_atom].AtomicContainerState == DUAL_STATE_ATOM) { - uint32_t atom_val = UInt32FromBigEndian(parsedAtoms[this_atom].AtomicName); - if (atom_val == 0x73747364 ) { //stsd - parsedAtoms[this_atom].AtomicLength = 16; - } else if (atom_val == 0x6D703473 ) { //mp4s - parsedAtoms[this_atom].AtomicLength = 16; - - } else if (atom_val == 0x73727470 ) { //srtp - parsedAtoms[this_atom].AtomicLength = 24; - } else if (atom_val == 0x72747020 && parsedAtoms[this_atom].AtomicLevel == 7) { //'rtp ' - parsedAtoms[this_atom].AtomicLength = 24; - - } else if (atom_val == 0x616C6163 && parsedAtoms[this_atom].AtomicLevel == 7) { //alac - parsedAtoms[this_atom].AtomicLength = 36; - } else if (atom_val == 0x6D703461 ) { //mp4a - parsedAtoms[this_atom].AtomicLength = 36; - } else if (atom_val == 0x73616D72 ) { //samr - parsedAtoms[this_atom].AtomicLength = 36; - } else if (atom_val == 0x73617762 ) { //sawb - parsedAtoms[this_atom].AtomicLength = 36; - } else if (atom_val == 0x73617770 ) { //sawp - parsedAtoms[this_atom].AtomicLength = 36; - } else if (atom_val == 0x73657663 ) { //sevc - parsedAtoms[this_atom].AtomicLength = 36; - } else if (atom_val == 0x73716370 ) { //sqcp - parsedAtoms[this_atom].AtomicLength = 36; - } else if (atom_val == 0x73736D76 ) { //ssmv - parsedAtoms[this_atom].AtomicLength = 36; - - } else if (atom_val == 0x74783367 ) { //tx3g - parsedAtoms[this_atom].AtomicLength = 46; - - } else if (atom_val == 0x6D6A7032 ) { //mjp2 - parsedAtoms[this_atom].AtomicLength = 86; - } else if (atom_val == 0x6D703476 ) { //mp4v - parsedAtoms[this_atom].AtomicLength = 86; - } else if (atom_val == 0x61766331 ) { //avc1 - parsedAtoms[this_atom].AtomicLength = 86; - } else if (atom_val == 0x6A706567 ) { //jpeg - parsedAtoms[this_atom].AtomicLength = 86; - } else if (atom_val == 0x73323633 ) { //s263 - parsedAtoms[this_atom].AtomicLength = 86; - } - } - - if (from_file) { - // here we read in the original atom into the buffer. If the length is - // greater than our buffer length, we loop, reading in chunks of the - // atom's data into the buffer, and immediately write it out, reusing - // the buffer. - // - bytes_written += block_copy(source_file, temp_file, buffer, - bytes_written_tally, - parsedAtoms[this_atom].AtomicLength - bytes_written, - bytes_written + parsedAtoms[this_atom].AtomicStart, - bytes_written_tally + bytes_written); - - return bytes_written; - - } else { // we are going to be writing not from the file, but directly from the tree (in memory). - uint64_t atom_name_len = 4; - - //fprintf(stdout, "Writing atom %s from memory %u\n", parsedAtoms[this_atom].AtomicName, parsedAtoms[this_atom].AtomicClassification); - fseeko(temp_file, bytes_written_tally + bytes_written, SEEK_SET); - - if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) { - fwrite("uuid", 4, 1, temp_file); - atom_name_len = 16; //total of 20 bytes for a uuid atom - //fprintf(stdout, "%" PRIu64 "\n", parsedAtoms[this_atom].AtomicLength); - if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[this_atom].uuid_style == UUID_OTHER) bytes_written += 4; - } - - fwrite(parsedAtoms[this_atom].AtomicName, atom_name_len, 1, temp_file); - bytes_written += atom_name_len; - if (parsedAtoms[this_atom].AtomicClassification == VERSIONED_ATOM || parsedAtoms[this_atom].AtomicClassification == PACKED_LANG_ATOM) { - UInt32_TO_String4(parsedAtoms[this_atom].AtomicVerFlags, twenty_byte_buffer); - fwrite(twenty_byte_buffer, 4, 1, temp_file); - bytes_written += 4; - } - - uint64_t atom_data_size = 0; - switch (parsedAtoms[this_atom].AtomicContainerState) { - case PARENT_ATOM : - case SIMPLE_PARENT_ATOM : { - atom_data_size = 0; - break; - } - case DUAL_STATE_ATOM : { - switch ( UInt32FromBigEndian(parsedAtoms[this_atom].AtomicName) ) { - case 0x6D657461 : { //meta - break; - } - case 0x73747364 : { //stsd - atom_data_size = parsedAtoms[this_atom].AtomicLength - 12; - break; - } - case 0x73636869 : { //schi (code only executes when deep_atom_scan = true; otherwise schi is contained by the monolithic/unparsed 'stsd') - atom_data_size = parsedAtoms[this_atom].AtomicLength - 12; - } - } - break; - } - case UNKNOWN_ATOM_TYPE : - case CHILD_ATOM : { - if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[this_atom].uuid_style == UUID_AP_SHA1_NAMESPACE) { - //4bytes length, 4 bytes 'uuid', 4bytes name, 4bytes NULL (AP writes its own uuid atoms - not those copied - iTunes style with atom versioning) - atom_data_size = parsedAtoms[this_atom].AtomicLength - (16 + 12); //16 uuid; 16 = 4bytes * ('uuid', ap_uuid_name, verflag, 4 NULL bytes) - } else if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[this_atom].uuid_style != UUID_DEPRECATED_FORM) { - atom_data_size = parsedAtoms[this_atom].AtomicLength - (16 + 8); - } else if (parsedAtoms[this_atom].AtomicClassification == VERSIONED_ATOM || parsedAtoms[this_atom].AtomicClassification == PACKED_LANG_ATOM) { - //4bytes legnth, 4bytes name, 4bytes flag&versioning (language would be 2 bytes, but because its in different places, it gets stored as data) - atom_data_size = parsedAtoms[this_atom].AtomicLength - 12; - } else { - //just 4bytes length, 4bytes name and then whatever data - atom_data_size = parsedAtoms[this_atom].AtomicLength - 8; - } - break; - } - } - - if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[this_atom].uuid_style == UUID_AP_SHA1_NAMESPACE) { - //AP writes uuid atoms much like iTunes style metadata; with version/flags to connote what type of data is being carried - //4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data - fwrite(parsedAtoms[this_atom].uuid_ap_atomname, 4, 1, temp_file); - bytes_written += 4; - - UInt32_TO_String4(parsedAtoms[this_atom].AtomicVerFlags, twenty_byte_buffer); - fwrite(twenty_byte_buffer, 4, 1, temp_file); - bytes_written += 4; - } - - if (atom_data_size > 0) { - fwrite(parsedAtoms[this_atom].AtomicData, atom_data_size, 1, temp_file); - bytes_written += atom_data_size; - - APar_ShellProgressBar(bytes_written_tally + bytes_written); - } - } - return bytes_written; + didread = fread(buffer, 1, std::min(max_buffer, toread), source_file); + if (didread == 0) { + fprintf(stderr, + "read: eof=%d err=%d %s\n", + feof(source_file), + ferror(source_file), + strerror(errno)); + break; + } + toread -= didread; + + bpos = buffer; + + while (didread) { + didwrite = fwrite(bpos, 1, didread, out_file); + didread -= didwrite; + bpos += didwrite; + bytes_written += didwrite; + + APar_ShellProgressBar(tally + bytes_written); + } + } + return bytes_written; +} + +uint64_t APar_WriteAtomically(FILE *source_file, + FILE *temp_file, + bool from_file, + char *&buffer, + uint64_t bytes_written_tally, + short this_atom) { + uint64_t bytes_written = 0; + + if (parsedAtoms[this_atom].AtomicLength > 1 && + parsedAtoms[this_atom].AtomicLength < + 8) { // prevents any spurious atoms from appearing + return bytes_written; + } + + // write the length of the atom first... taken from our tree in memory + UInt32_TO_String4(parsedAtoms[this_atom].AtomicLength, twenty_byte_buffer); + fseeko(temp_file, bytes_written_tally, SEEK_SET); + fwrite(twenty_byte_buffer, 4, 1, temp_file); + bytes_written += 4; + + // since we have already writen the length out to the file, it can be changed + // now with impunity + if (parsedAtoms[this_atom].AtomicLength == + 0) { // the spec says if an atom has a length of 0, it extends to EOF + parsedAtoms[this_atom].AtomicLength = + file_size - parsedAtoms[this_atom].AtomicLength; + } else if (parsedAtoms[this_atom].AtomicLength == 1) { + // part of the pseudo 64-bit support + parsedAtoms[this_atom].AtomicLength = + parsedAtoms[this_atom].AtomicLengthExtended; + } else if (parsedAtoms[this_atom].AtomicContainerState == DUAL_STATE_ATOM) { + if (memcmp(parsedAtoms[this_atom].AtomicName, "dref", 4) == 0) { + parsedAtoms[this_atom].AtomicLength = 16; + } else if (memcmp(parsedAtoms[this_atom].AtomicName, "iinf", 4) == 0) { + parsedAtoms[this_atom].AtomicLength = 14; + } + } + + if (deep_atom_scan && + parsedAtoms[this_atom].AtomicContainerState == DUAL_STATE_ATOM) { + uint32_t atom_val = UInt32FromBigEndian(parsedAtoms[this_atom].AtomicName); + if (atom_val == 0x73747364) { // stsd + parsedAtoms[this_atom].AtomicLength = 16; + } else if (atom_val == 0x6D703473) { // mp4s + parsedAtoms[this_atom].AtomicLength = 16; + + } else if (atom_val == 0x73727470) { // srtp + parsedAtoms[this_atom].AtomicLength = 24; + } else if (atom_val == 0x72747020 && + parsedAtoms[this_atom].AtomicLevel == 7) { //'rtp ' + parsedAtoms[this_atom].AtomicLength = 24; + + } else if (atom_val == 0x616C6163 && + parsedAtoms[this_atom].AtomicLevel == 7) { // alac + parsedAtoms[this_atom].AtomicLength = 36; + } else if (atom_val == 0x6D703461) { // mp4a + parsedAtoms[this_atom].AtomicLength = 36; + } else if (atom_val == 0x73616D72) { // samr + parsedAtoms[this_atom].AtomicLength = 36; + } else if (atom_val == 0x73617762) { // sawb + parsedAtoms[this_atom].AtomicLength = 36; + } else if (atom_val == 0x73617770) { // sawp + parsedAtoms[this_atom].AtomicLength = 36; + } else if (atom_val == 0x73657663) { // sevc + parsedAtoms[this_atom].AtomicLength = 36; + } else if (atom_val == 0x73716370) { // sqcp + parsedAtoms[this_atom].AtomicLength = 36; + } else if (atom_val == 0x73736D76) { // ssmv + parsedAtoms[this_atom].AtomicLength = 36; + + } else if (atom_val == 0x74783367) { // tx3g + parsedAtoms[this_atom].AtomicLength = 46; + + } else if (atom_val == 0x6D6A7032) { // mjp2 + parsedAtoms[this_atom].AtomicLength = 86; + } else if (atom_val == 0x6D703476) { // mp4v + parsedAtoms[this_atom].AtomicLength = 86; + } else if (atom_val == 0x61766331) { // avc1 + parsedAtoms[this_atom].AtomicLength = 86; + } else if (atom_val == 0x6A706567) { // jpeg + parsedAtoms[this_atom].AtomicLength = 86; + } else if (atom_val == 0x73323633) { // s263 + parsedAtoms[this_atom].AtomicLength = 86; + } + } + + if (from_file) { + // here we read in the original atom into the buffer. If the length is + // greater than our buffer length, we loop, reading in chunks of the + // atom's data into the buffer, and immediately write it out, reusing + // the buffer. + // + bytes_written += + block_copy(source_file, + temp_file, + buffer, + bytes_written_tally, + parsedAtoms[this_atom].AtomicLength - bytes_written, + bytes_written + parsedAtoms[this_atom].AtomicStart, + bytes_written_tally + bytes_written); + + return bytes_written; + + } else { // we are going to be writing not from the file, but directly from + // the tree (in memory). + uint64_t atom_name_len = 4; + + // fprintf(stdout, "Writing atom %s from memory %u\n", + // parsedAtoms[this_atom].AtomicName, + // parsedAtoms[this_atom].AtomicClassification); + fseeko(temp_file, bytes_written_tally + bytes_written, SEEK_SET); + + if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) { + fwrite("uuid", 4, 1, temp_file); + atom_name_len = 16; // total of 20 bytes for a uuid atom + // fprintf(stdout, "%" PRIu64 "\n", parsedAtoms[this_atom].AtomicLength); + if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && + parsedAtoms[this_atom].uuid_style == UUID_OTHER) + bytes_written += 4; + } + + fwrite(parsedAtoms[this_atom].AtomicName, atom_name_len, 1, temp_file); + bytes_written += atom_name_len; + if (parsedAtoms[this_atom].AtomicClassification == VERSIONED_ATOM || + parsedAtoms[this_atom].AtomicClassification == PACKED_LANG_ATOM) { + UInt32_TO_String4(parsedAtoms[this_atom].AtomicVerFlags, + twenty_byte_buffer); + fwrite(twenty_byte_buffer, 4, 1, temp_file); + bytes_written += 4; + } + + uint64_t atom_data_size = 0; + switch (parsedAtoms[this_atom].AtomicContainerState) { + case PARENT_ATOM: + case SIMPLE_PARENT_ATOM: { + atom_data_size = 0; + break; + } + case DUAL_STATE_ATOM: { + switch (UInt32FromBigEndian(parsedAtoms[this_atom].AtomicName)) { + case 0x6D657461: { // meta + break; + } + case 0x73747364: { // stsd + atom_data_size = parsedAtoms[this_atom].AtomicLength - 12; + break; + } + case 0x73636869: { // schi (code only executes when deep_atom_scan = true; + // otherwise schi is contained by the + // monolithic/unparsed 'stsd') + atom_data_size = parsedAtoms[this_atom].AtomicLength - 12; + } + } + break; + } + case UNKNOWN_ATOM_TYPE: + case CHILD_ATOM: { + if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && + parsedAtoms[this_atom].uuid_style == UUID_AP_SHA1_NAMESPACE) { + // 4bytes length, 4 bytes 'uuid', 4bytes name, 4bytes NULL (AP writes + // its own uuid atoms - not those copied - iTunes style with atom + // versioning) + atom_data_size = parsedAtoms[this_atom].AtomicLength - + (16 + 12); // 16 uuid; 16 = 4bytes * ('uuid', + // ap_uuid_name, verflag, 4 NULL bytes) + } else if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && + parsedAtoms[this_atom].uuid_style != UUID_DEPRECATED_FORM) { + atom_data_size = parsedAtoms[this_atom].AtomicLength - (16 + 8); + } else if (parsedAtoms[this_atom].AtomicClassification == + VERSIONED_ATOM || + parsedAtoms[this_atom].AtomicClassification == + PACKED_LANG_ATOM) { + // 4bytes legnth, 4bytes name, 4bytes flag&versioning (language would be + // 2 bytes, but because its in different places, it gets stored as data) + atom_data_size = parsedAtoms[this_atom].AtomicLength - 12; + } else { + // just 4bytes length, 4bytes name and then whatever data + atom_data_size = parsedAtoms[this_atom].AtomicLength - 8; + } + break; + } + } + + if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && + parsedAtoms[this_atom].uuid_style == UUID_AP_SHA1_NAMESPACE) { + // AP writes uuid atoms much like iTunes style metadata; with + // version/flags to connote what type of data is being carried + // 4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes name of uuid + // in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data + fwrite(parsedAtoms[this_atom].uuid_ap_atomname, 4, 1, temp_file); + bytes_written += 4; + + UInt32_TO_String4(parsedAtoms[this_atom].AtomicVerFlags, + twenty_byte_buffer); + fwrite(twenty_byte_buffer, 4, 1, temp_file); + bytes_written += 4; + } + + if (atom_data_size > 0) { + fwrite(parsedAtoms[this_atom].AtomicData, atom_data_size, 1, temp_file); + bytes_written += atom_data_size; + + APar_ShellProgressBar(bytes_written_tally + bytes_written); + } + } + return bytes_written; } /*---------------------- APar_copy_gapless_padding - mp4file - destination file - last_atom_pos - the last byte in the destination file that is contained by any atom (in parsedAtoms[] array) - buffer - a buffer that will be used to set & write out from the NULLs used in gapless padding - - Add the discovered amount of already present gapless void padding at the end of the file (which is *not* contained by any atom at all) back into the destination - file. - - Update: it would seem that this gapless void padding at the end of the file is not critical to gapless playback. In my 1 test of the thing, it seemed to work - regardless of whether this NULL space was present or not, 'pgap' seemed to work. But, since Apple put it in for some reason, it will be left there unless explicity - directed not to (via AP_PADDING). Although tying ordinary padding to this gapless padding may reduce flexibility - the assumption is that someone interested in - squeezing out wasted space would want to eliminate this wasted space too (and so far, it does seem wasted). - - NOTE: Apple seems not to have seen this portion of the ISO 14496-12 Annex A, section A.2, para 6: - "All the data within a conforming file is encapsulated in boxes (called atoms in predecessors of this file format). There is no data outside the box structure." - And yet, Apple (donators of the file format) has caused iTunes to create non-conforming files with iTunes 7.x because of this NULL data outside of any box/atom - structure. - -----------------------*/ -void APar_copy_gapless_padding(FILE* mp4file, uint64_t last_atom_pos, char* buffer) { - uint64_t gapless_padding_bytes_written = 0; - while (gapless_padding_bytes_written < gapless_void_padding) { - if (gapless_padding_bytes_written + max_buffer <= gapless_void_padding ) { - memset(buffer, 0, max_buffer); - - fseeko(mp4file, last_atom_pos + gapless_padding_bytes_written, - SEEK_SET); - fwrite(buffer, max_buffer, 1, mp4file); - gapless_padding_bytes_written += max_buffer; - - } else { //less then 512k of gapless padding (here's hoping we get here always) - memset(buffer, 0, gapless_void_padding - gapless_padding_bytes_written); - - fseeko(mp4file, last_atom_pos + gapless_padding_bytes_written, SEEK_SET); - fwrite(buffer, gapless_void_padding - gapless_padding_bytes_written, 1, mp4file); - gapless_padding_bytes_written += gapless_void_padding - gapless_padding_bytes_written; - break; - } - } -} - -void APar_WriteFile(const char* ISObasemediafile, const char* outfile, bool rewrite_original) { - char* temp_file_name=(char*)calloc(1, sizeof(char)* 3500 ); - char* file_buffer=(char*)calloc(1, sizeof(char)* max_buffer + 1 ); - FILE* temp_file; - uint64_t temp_file_bytes_written = 0; - short thisAtomNumber = 0; - char* originating_file = NULL; - bool free_modified_name = false; - - APar_RenderAllID32Atoms(); - - if (!(psp_brand || force_existing_hierarchy)) { - APar_Optimize(false); - } else { - APar_LocateAtomLandmarks(); - } - - APar_FindPadding(false); - APar_ConsolidatePadding(); - APar_DetermineAtomLengths(); - - if (!complete_free_space_erasure) { - APar_DetermineDynamicUpdate(); - } - - if (!rewrite_original || dynUpd.prevent_dynamic_update) { - dynUpd.updage_by_padding = false; - } - - APar_ValidateAtoms(); - - //whatever atoms/space comes before mdat has to be added/removed before this point, or chunk offsets (in stco, co64, tfhd) won't be properly determined - uint64_t mdat_position = APar_DetermineMediaData_AtomPosition(); - - if (dynUpd.updage_by_padding) { - APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-data-", NULL); //APar_DeriveNewPath(ISObasemediafile, temp_file_name, -1, "-data-", NULL); - temp_file = APar_OpenFile(temp_file_name, "wb"); -#if defined (_WIN32) && !defined (__CYGWIN__) - char* invisi_command=(char*)malloc(sizeof(char)*2*MAXPATHLEN); - sprintf (invisi_command,"ATTRIB +S +H \"%s\"",temp_file_name); - - if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) { - wchar_t* invisi_command_long = Convert_multibyteUTF8_to_wchar(invisi_command); - - _wsystem(invisi_command_long); - - free(invisi_command_long); - invisi_command_long = NULL; - } else { - system(invisi_command); - } - free(invisi_command); - invisi_command = NULL; + mp4file - destination file + last_atom_pos - the last byte in the destination file that is contained +by any atom (in parsedAtoms[] array) buffer - a buffer that will be used to set +& write out from the NULLs used in gapless padding + + Add the discovered amount of already present gapless void padding at the end +of the file (which is *not* contained by any atom at all) back into the +destination file. + + Update: it would seem that this gapless void padding at the end +of the file is not critical to gapless playback. In my 1 test of the thing, it +seemed to work regardless of whether this NULL space was present or not, 'pgap' +seemed to work. But, since Apple put it in for some reason, it will be left +there unless explicity directed not to (via AP_PADDING). Although tying ordinary +padding to this gapless padding may reduce flexibility - the assumption is that +someone interested in squeezing out wasted space would want to eliminate this +wasted space too (and so far, it does seem wasted). + + NOTE: Apple seems not to have seen this portion of the ISO +14496-12 Annex A, section A.2, para 6: "All the data within a conforming file is +encapsulated in boxes (called atoms in predecessors of this file format). There +is no data outside the box structure." And yet, Apple (donators of the file +format) has caused iTunes to create non-conforming files with iTunes 7.x because +of this NULL data outside of any box/atom structure. + +----------------------*/ +void APar_copy_gapless_padding(FILE *mp4file, + uint64_t last_atom_pos, + char *buffer) { + uint64_t gapless_padding_bytes_written = 0; + while (gapless_padding_bytes_written < gapless_void_padding) { + if (gapless_padding_bytes_written + max_buffer <= gapless_void_padding) { + memset(buffer, 0, max_buffer); + + fseeko(mp4file, last_atom_pos + gapless_padding_bytes_written, SEEK_SET); + fwrite(buffer, max_buffer, 1, mp4file); + gapless_padding_bytes_written += max_buffer; + + } else { // less then 512k of gapless padding (here's hoping we get here + // always) + memset(buffer, 0, gapless_void_padding - gapless_padding_bytes_written); + + fseeko(mp4file, last_atom_pos + gapless_padding_bytes_written, SEEK_SET); + fwrite(buffer, + gapless_void_padding - gapless_padding_bytes_written, + 1, + mp4file); + gapless_padding_bytes_written += + gapless_void_padding - gapless_padding_bytes_written; + break; + } + } +} + +void APar_WriteFile(const char *ISObasemediafile, + const char *outfile, + bool rewrite_original) { + char *temp_file_name = (char *)calloc(1, sizeof(char) * 3500); + char *file_buffer = (char *)calloc(1, sizeof(char) * max_buffer + 1); + FILE *temp_file; + uint64_t temp_file_bytes_written = 0; + short thisAtomNumber = 0; + char *originating_file = NULL; + bool free_modified_name = false; + + APar_RenderAllID32Atoms(); + + if (!(psp_brand || force_existing_hierarchy)) { + APar_Optimize(false); + } else { + APar_LocateAtomLandmarks(); + } + + APar_FindPadding(false); + APar_ConsolidatePadding(); + APar_DetermineAtomLengths(); + + if (!complete_free_space_erasure) { + APar_DetermineDynamicUpdate(); + } + + if (!rewrite_original || dynUpd.prevent_dynamic_update) { + dynUpd.updage_by_padding = false; + } + + APar_ValidateAtoms(); + + // whatever atoms/space comes before mdat has to be added/removed before this + // point, or chunk offsets (in stco, co64, tfhd) won't be properly determined + uint64_t mdat_position = APar_DetermineMediaData_AtomPosition(); + + if (dynUpd.updage_by_padding) { + APar_DeriveNewPath(ISObasemediafile, + temp_file_name, + 0, + "-data-", + NULL); // APar_DeriveNewPath(ISObasemediafile, + // temp_file_name, -1, "-data-", NULL); + temp_file = APar_OpenFile(temp_file_name, "wb"); +#if defined(_WIN32) && !defined(__CYGWIN__) + char *invisi_command = (char *)malloc(sizeof(char) * 2 * MAXPATHLEN); + sprintf(invisi_command, "ATTRIB +S +H \"%s\"", temp_file_name); + + if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) { + wchar_t *invisi_command_long = + Convert_multibyteUTF8_to_wchar(invisi_command); + + _wsystem(invisi_command_long); + + free(invisi_command_long); + invisi_command_long = NULL; + } else { + system(invisi_command); + } + free(invisi_command); + invisi_command = NULL; #endif - } else if (!outfile) { - APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-temp-", NULL); - temp_file = APar_OpenFile(temp_file_name, "wb"); - -#if defined (DARWIN_PLATFORM) - APar_SupplySelectiveTypeCreatorCodes(ISObasemediafile, temp_file_name, forced_suffix_type); //provide type/creator codes for ".mp4" for randomly named temp files + } else if (!outfile) { + APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-temp-", NULL); + temp_file = APar_OpenFile(temp_file_name, "wb"); + +#if defined(__APPLE__) + APar_SupplySelectiveTypeCreatorCodes( + ISObasemediafile, + temp_file_name, + forced_suffix_type); // provide type/creator codes for ".mp4" for + // randomly named temp files #endif - } else { - //case-sensitive compare means "The.m4a" is different from "THe.m4a"; on certiain Mac OS X filesystems a case-preservative but case-insensitive FS exists & - //AP probably will have a problem there. Output to a uniquely named file as I'm not going to poll the OS for the type of FS employed on the target drive. - if (strcmp(ISObasemediafile,outfile) == 0) { - //er, nice try but you were trying to ouput to the exactly named file of the original. Y'all ain't so slick - APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-temp-", NULL); - temp_file = APar_OpenFile(temp_file_name, "wb"); - -#if defined (DARWIN_PLATFORM) - APar_SupplySelectiveTypeCreatorCodes(ISObasemediafile, temp_file_name, forced_suffix_type); //provide type/creator codes for ".mp4" for a fall-through randomly named temp files + } else { + // case-sensitive compare means "The.m4a" is different from "THe.m4a"; on + // certiain Mac OS X filesystems a case-preservative but case-insensitive FS + // exists & AP probably will have a problem there. Output to a uniquely + // named file as I'm not going to poll the OS for the type of FS employed on + // the target drive. + if (strcmp(ISObasemediafile, outfile) == 0) { + // er, nice try but you were trying to ouput to the exactly named file of + // the original. Y'all ain't so slick + APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-temp-", NULL); + temp_file = APar_OpenFile(temp_file_name, "wb"); + +#if defined(__APPLE__) + APar_SupplySelectiveTypeCreatorCodes( + ISObasemediafile, + temp_file_name, + forced_suffix_type); // provide type/creator codes for ".mp4" for a + // fall-through randomly named temp files #endif - } else { - temp_file = APar_OpenFile(outfile, "wb"); + } else { + temp_file = APar_OpenFile(outfile, "wb"); -#if defined (DARWIN_PLATFORM) - APar_SupplySelectiveTypeCreatorCodes(ISObasemediafile, outfile, forced_suffix_type); //provide type/creator codes for ".mp4" for a user-defined output file +#if defined(__APPLE__) + APar_SupplySelectiveTypeCreatorCodes( + ISObasemediafile, + outfile, + forced_suffix_type); // provide type/creator codes for ".mp4" for a + // user-defined output file #endif + } + } - } - } + if (temp_file != NULL) { // body of atom writing here - // turn off buffering on the output, since we have a big buffer ourselves - if (temp_file) { - setbuf(temp_file, NULL); - } - setbuf(source_file, NULL); - - if (temp_file != NULL) { //body of atom writing here - - if (dynUpd.updage_by_padding) { - thisAtomNumber = dynUpd.initial_update_atom->AtomicNumber; - fprintf(stdout, "\n Updating metadata... "); - } else { - fprintf(stdout, "\n Started writing to %s.\n", - outfile ? outfile : "temp file"); - } - - while (true) { - - AtomicInfo* thisAtom = &parsedAtoms[thisAtomNumber]; - if (thisAtom->AtomicNumber == -1) break; - - //the loop where the critical determination is made - if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 && dynUpd.updage_by_padding) break; - - if (thisAtom->ancillary_data == 0x666C6167) { //'flag' - APar_UpdateModTime(thisAtom); - } - - if (memcmp(thisAtom->AtomicName, "stco", 4) == 0) { - bool readjusted_stco = APar_Readjust_STCO_atom(mdat_position, thisAtomNumber); - - temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, !readjusted_stco, file_buffer, temp_file_bytes_written, thisAtomNumber); - - } else if (memcmp(thisAtom->AtomicName, "co64", 4) == 0) { - bool readjusted_co64 = APar_Readjust_CO64_atom(mdat_position, thisAtomNumber); - - temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, !readjusted_co64, file_buffer, temp_file_bytes_written, thisAtomNumber); - - } else if (memcmp(thisAtom->AtomicName, "tfhd", 4) == 0) { - bool readjusted_tfhd = APar_Readjust_TFHD_fragment_atom(mdat_position, thisAtomNumber); - - temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, !readjusted_tfhd, file_buffer, temp_file_bytes_written, thisAtomNumber); - - } else if (memcmp(thisAtom->AtomicName, "iloc", 4) == 0) { - bool readjusted_iloc = APar_Readjust_iloc_atom(thisAtomNumber); - - temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, !readjusted_iloc, file_buffer, temp_file_bytes_written, thisAtomNumber); - - } else if (thisAtom->AtomicData != NULL || memcmp(thisAtom->AtomicName, "meta", 4) == 0) { - temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, false, file_buffer, temp_file_bytes_written, thisAtomNumber); - - } else { - //write out parent atoms (the standard kind that are only offset & name from the tree in memory (total: 8bytes) - if ( thisAtom->AtomicContainerState <= SIMPLE_PARENT_ATOM ) { - temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, false, file_buffer, temp_file_bytes_written, thisAtomNumber); - //or its a child (they invariably contain some sort of data. - } else { - temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, true, file_buffer, temp_file_bytes_written, thisAtomNumber); - } - } - if (thisAtom->NextAtomNumber == 0) { //if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) { - //fprintf(stdout, "The loop is buh-rokin\n"); - break; - } - - //prevent any looping back to atoms already written - thisAtom->AtomicNumber = -1; - thisAtomNumber = thisAtom->NextAtomNumber; - } - if (!dynUpd.updage_by_padding) { - if (gapless_void_padding > 0 && pad_prefs.default_padding_size > 0) { //only when some sort of padding is wanted will the gapless null padding be copied - APar_copy_gapless_padding(temp_file, temp_file_bytes_written, file_buffer); - } - fprintf(stdout, "\n Finished writing to %s.\n", - outfile ? outfile : "temp file"); - fclose(temp_file); - } - - } else { - fprintf(stdout, "AtomicParsley error: an error occurred while trying to create a temp file.\n"); - exit(1); - } - - if (dynUpd.updage_by_padding && rewrite_original) { - fclose(temp_file); - uint64_t metadata_len = findFileSize(temp_file_name); - - temp_file = APar_OpenFile(temp_file_name, "rb"); - fclose(source_file); - source_file = APar_OpenFile(ISObasemediafile, "r+b"); - if (source_file == NULL) { - fclose(temp_file); - remove(temp_file_name); - fprintf(stdout, "AtomicParsley error: the original file was no longer found.\nExiting.\n"); - exit(1); - } else if (!(dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) && metadata_len != (dynUpd.first_mdat_atom->AtomicStart - dynUpd.initial_update_atom->AtomicStart)) { - fclose(temp_file); - remove(temp_file_name); - fprintf(stdout, - "AtomicParsley error: the insufficient space to retag the source file (%" PRIu64 "!=%" PRIu64 ").\nExiting.\n", metadata_len, dynUpd.first_mdat_atom->AtomicStart - dynUpd.initial_update_atom->AtomicStart); - exit(1); - } - - APar_MergeTempFile(source_file, temp_file, temp_file_bytes_written, dynUpd.initial_update_atom->AtomicStart, file_buffer); - - fclose(source_file); - fclose(temp_file); - remove(temp_file_name); - - } else if (rewrite_original && !outfile) { //disable overWrite when writing out to a specifically named file; presumably the enumerated output file was meant to be the final destination - fclose(source_file); - -#if defined (_WIN32) && !defined (__CYGWIN__) /* native Windows requires removing the file first; rename() on POSIX does the removing automatically as needed */ - if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) { - wchar_t* utf16_filepath = Convert_multibyteUTF8_to_wchar(ISObasemediafile); - - _wremove(utf16_filepath); - - free(utf16_filepath); - utf16_filepath = NULL; - } else { - remove(ISObasemediafile); - } + if (dynUpd.updage_by_padding) { + thisAtomNumber = dynUpd.initial_update_atom->AtomicNumber; + fprintf(stdout, "\n Updating metadata... "); + } else { + fprintf(stdout, + "\n Started writing to %s.\n", + outfile ? outfile : "temp file"); + } + + while (true) { + + AtomicInfo *thisAtom = &parsedAtoms[thisAtomNumber]; + if (thisAtom->AtomicNumber == -1) + break; + + // the loop where the critical determination is made + if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 && + dynUpd.updage_by_padding) + break; + + if (thisAtom->ancillary_data == 0x666C6167) { //'flag' + APar_UpdateModTime(thisAtom); + } + + if (memcmp(thisAtom->AtomicName, "stco", 4) == 0) { + bool readjusted_stco = + APar_Readjust_STCO_atom(mdat_position, thisAtomNumber); + + temp_file_bytes_written += APar_WriteAtomically(source_file, + temp_file, + !readjusted_stco, + file_buffer, + temp_file_bytes_written, + thisAtomNumber); + + } else if (memcmp(thisAtom->AtomicName, "co64", 4) == 0) { + bool readjusted_co64 = + APar_Readjust_CO64_atom(mdat_position, thisAtomNumber); + + temp_file_bytes_written += APar_WriteAtomically(source_file, + temp_file, + !readjusted_co64, + file_buffer, + temp_file_bytes_written, + thisAtomNumber); + + } else if (memcmp(thisAtom->AtomicName, "tfhd", 4) == 0) { + bool readjusted_tfhd = + APar_Readjust_TFHD_fragment_atom(mdat_position, thisAtomNumber); + + temp_file_bytes_written += APar_WriteAtomically(source_file, + temp_file, + !readjusted_tfhd, + file_buffer, + temp_file_bytes_written, + thisAtomNumber); + + } else if (memcmp(thisAtom->AtomicName, "iloc", 4) == 0) { + bool readjusted_iloc = APar_Readjust_iloc_atom(thisAtomNumber); + + temp_file_bytes_written += APar_WriteAtomically(source_file, + temp_file, + !readjusted_iloc, + file_buffer, + temp_file_bytes_written, + thisAtomNumber); + + } else if (thisAtom->AtomicData != NULL || + memcmp(thisAtom->AtomicName, "meta", 4) == 0) { + temp_file_bytes_written += APar_WriteAtomically(source_file, + temp_file, + false, + file_buffer, + temp_file_bytes_written, + thisAtomNumber); + + } else { + // write out parent atoms (the standard kind that are only offset & name + // from the tree in memory (total: 8bytes) + if (thisAtom->AtomicContainerState <= SIMPLE_PARENT_ATOM) { + temp_file_bytes_written += + APar_WriteAtomically(source_file, + temp_file, + false, + file_buffer, + temp_file_bytes_written, + thisAtomNumber); + // or its a child (they invariably contain some sort of data. + } else { + temp_file_bytes_written += + APar_WriteAtomically(source_file, + temp_file, + true, + file_buffer, + temp_file_bytes_written, + thisAtomNumber); + } + } + if (thisAtom->NextAtomNumber == + 0) { // if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) { + // fprintf(stdout, "The loop is buh-rokin\n"); + break; + } + + // prevent any looping back to atoms already written + thisAtom->AtomicNumber = -1; + thisAtomNumber = thisAtom->NextAtomNumber; + } + if (!dynUpd.updage_by_padding) { + if (gapless_void_padding > 0 && + pad_prefs.default_padding_size > + 0) { // only when some sort of padding is wanted will the gapless + // null padding be copied + APar_copy_gapless_padding( + temp_file, temp_file_bytes_written, file_buffer); + } + fprintf(stdout, + "\n Finished writing to %s.\n", + outfile ? outfile : "temp file"); + fclose(temp_file); + } + + } else { + fprintf(stdout, + "AtomicParsley error: an error occurred while trying to " + "create a temp file.\n"); + exit(1); + } + + if (dynUpd.updage_by_padding && rewrite_original) { + fclose(temp_file); + uint64_t metadata_len = findFileSize(temp_file_name); + + temp_file = APar_OpenFile(temp_file_name, "rb"); + fclose(source_file); + source_file = APar_OpenFile(ISObasemediafile, "r+b"); + if (source_file == NULL) { + fclose(temp_file); + remove(temp_file_name); + fprintf(stdout, + "AtomicParsley error: the original file was no longer " + "found.\nExiting.\n"); + exit(1); + } else if (!(dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) && + metadata_len != (dynUpd.first_mdat_atom->AtomicStart - + dynUpd.initial_update_atom->AtomicStart)) { + fclose(temp_file); + remove(temp_file_name); + fprintf(stdout, + "AtomicParsley error: the insufficient space to retag the source " + "file (%" PRIu64 "!=%" PRIu64 ").\nExiting.\n", + metadata_len, + dynUpd.first_mdat_atom->AtomicStart - + dynUpd.initial_update_atom->AtomicStart); + exit(1); + } + + APar_MergeTempFile(source_file, + temp_file, + temp_file_bytes_written, + dynUpd.initial_update_atom->AtomicStart, + file_buffer); + + fclose(source_file); + fclose(temp_file); + remove(temp_file_name); + + } else if (rewrite_original && + !outfile) { // disable overWrite when writing out to a specifically + // named file; presumably the enumerated output file + // was meant to be the final destination + fclose(source_file); + +#if defined(_WIN32) && \ + !defined(__CYGWIN__) /* native Windows requires removing the file first; \ + rename() on POSIX does the removing automatically \ + as needed */ + if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) { + wchar_t *utf16_filepath = + Convert_multibyteUTF8_to_wchar(ISObasemediafile); + + _wremove(utf16_filepath); + + free(utf16_filepath); + utf16_filepath = NULL; + } else { + remove(ISObasemediafile); + } #endif - int err = 0; + int err = 0; - if (forced_suffix_type != NO_TYPE_FORCING) { - originating_file = (char*)calloc( 1, sizeof(char)* 3500 ); - free_modified_name = true; - if (forced_suffix_type == FORCE_M4B_TYPE) { //using --stik Audiobook with --overWrite will change the original file's extension - uint16_t filename_len = strlen(ISObasemediafile); - const char* suffix = strrchr(ISObasemediafile, '.'); - memcpy(originating_file, ISObasemediafile, filename_len+1 ); - memcpy(originating_file + (filename_len - strlen(suffix) ), ".m4b", 5 ); - } - } else { - originating_file = (char*)ISObasemediafile; - } - -#if defined (_WIN32) && !defined (__CYGWIN__) - if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) { - wchar_t* utf16_filepath = Convert_multibyteUTF8_to_wchar(originating_file); - wchar_t* temp_utf16_filepath = Convert_multibyteUTF8_to_wchar(temp_file_name); - - err = _wrename(temp_utf16_filepath, utf16_filepath); - - free(utf16_filepath); - free(temp_utf16_filepath); - utf16_filepath = NULL; - temp_utf16_filepath = NULL; - } else + if (forced_suffix_type != NO_TYPE_FORCING) { + originating_file = (char *)calloc(1, sizeof(char) * 3500); + free_modified_name = true; + if (forced_suffix_type == + FORCE_M4B_TYPE) { // using --stik Audiobook with --overWrite will + // change the original file's extension + uint16_t filename_len = strlen(ISObasemediafile); + const char *suffix = strrchr(ISObasemediafile, '.'); + memcpy(originating_file, ISObasemediafile, filename_len + 1); + memcpy(originating_file + (filename_len - strlen(suffix)), ".m4b", 5); + } + } else { + originating_file = (char *)ISObasemediafile; + } + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) { + wchar_t *utf16_filepath = + Convert_multibyteUTF8_to_wchar(originating_file); + wchar_t *temp_utf16_filepath = + Convert_multibyteUTF8_to_wchar(temp_file_name); + + err = _wrename(temp_utf16_filepath, utf16_filepath); + + free(utf16_filepath); + free(temp_utf16_filepath); + utf16_filepath = NULL; + temp_utf16_filepath = NULL; + } else #endif - { - err = rename(temp_file_name, originating_file); - } - - if (err != 0) { - switch (errno) { - - case ENAMETOOLONG: { - fprintf (stdout, "Some or all of the orginal path was too long."); - exit (-1); - } - case ENOENT: { - fprintf (stdout, "Some part of the original path was missing."); - exit (-1); - } - case EACCES: { - fprintf (stdout, "Unable to write to a directory lacking write permission."); - exit (-1); - } - case ENOSPC: { - fprintf (stdout, "Out of space."); - exit (-1); - } - } - } - } - - free(temp_file_name); - if (free_modified_name) free(originating_file); - temp_file_name=NULL; - free(file_buffer); - file_buffer = NULL; + { + err = rename(temp_file_name, originating_file); + } + + if (err != 0) { + switch (errno) { + + case ENAMETOOLONG: { + fprintf(stdout, "Some or all of the orginal path was too long."); + exit(-1); + } + case ENOENT: { + fprintf(stdout, "Some part of the original path was missing."); + exit(-1); + } + case EACCES: { + fprintf(stdout, + "Unable to write to a directory lacking write permission."); + exit(-1); + } + case ENOSPC: { + fprintf(stdout, "Out of space."); + exit(-1); + } + } + } + } + + free(temp_file_name); + if (free_modified_name) + free(originating_file); + temp_file_name = NULL; + free(file_buffer); + file_buffer = NULL; - return; + return; } // vim:ts=2:sw=2:noet: diff -Nru atomicparsley-0.9.6/src/sha1.cpp atomicparsley-20210715.151551.e7ad03a/src/sha1.cpp --- atomicparsley-0.9.6/src/sha1.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/sha1.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -23,12 +23,11 @@ */ /* - This file has been modified from the original found in http://www.gnu.org/software/coreutils/ coreutils-5.97 for use within AtomicParsley. - Modifications are : endian detection change - a cast for compiling under g++ - file renaming - eliminated SWAP in favor of swap32 & swap16 in util.h - alignment macros (for msvc) + This file has been modified from the original found in + http://www.gnu.org/software/coreutils/ coreutils-5.97 for use within + AtomicParsley. Modifications are : endian detection change a cast for + compiling under g++ file renaming eliminated SWAP in favor of swap32 & swap16 + in util.h alignment macros (for msvc) */ #include "AtomicParsley.h" @@ -46,22 +45,19 @@ #define BLOCKSIZE 4096 #if BLOCKSIZE % 64 != 0 -# error "invalid BLOCKSIZE" +#error "invalid BLOCKSIZE" #endif /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (RFC 1321, 3.1: Step 1) */ -static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; - +static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; /* Takes a pointer to a 160 bit block of data (five 32 bit ints) and intializes it to the start constants of the SHA1 algorithm. This must be called before using hash in the call to sha1_hash. */ -void -sha1_init_ctx (struct sha1_ctx *ctx) -{ +void sha1_init_ctx(struct sha1_ctx *ctx) { ctx->A = 0x67452301; ctx->B = 0xefcdab89; ctx->C = 0x98badcfe; @@ -77,14 +73,12 @@ IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ -void * -sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) -{ - ((md5_uint32 *) resbuf)[0] = SWAP32 (ctx->A); - ((md5_uint32 *) resbuf)[1] = SWAP32 (ctx->B); - ((md5_uint32 *) resbuf)[2] = SWAP32 (ctx->C); - ((md5_uint32 *) resbuf)[3] = SWAP32 (ctx->D); - ((md5_uint32 *) resbuf)[4] = SWAP32 (ctx->E); +void *sha1_read_ctx(const struct sha1_ctx *ctx, void *resbuf) { + ((md5_uint32 *)resbuf)[0] = SWAP32(ctx->A); + ((md5_uint32 *)resbuf)[1] = SWAP32(ctx->B); + ((md5_uint32 *)resbuf)[2] = SWAP32(ctx->C); + ((md5_uint32 *)resbuf)[3] = SWAP32(ctx->D); + ((md5_uint32 *)resbuf)[4] = SWAP32(ctx->E); return resbuf; } @@ -94,9 +88,7 @@ IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ -void * -sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) -{ +void *sha1_finish_ctx(struct sha1_ctx *ctx, void *resbuf) { /* Take yet unprocessed bytes into account. */ md5_uint32 bytes = ctx->buflen; size_t pad; @@ -107,82 +99,77 @@ ++ctx->total[1]; pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; - memcpy (&ctx->buffer[bytes], fillbuf, pad); + memcpy(&ctx->buffer[bytes], fillbuf, pad); /* Put the 64-bit file length in *bits* at the end of the buffer. */ - *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP32 (ctx->total[0] << 3); - *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP32 ((ctx->total[1] << 3) | - (ctx->total[0] >> 29)); + *(md5_uint32 *)&ctx->buffer[bytes + pad + 4] = SWAP32(ctx->total[0] << 3); + *(md5_uint32 *)&ctx->buffer[bytes + pad] = + SWAP32((ctx->total[1] << 3) | (ctx->total[0] >> 29)); /* Process last bytes. */ - sha1_process_block (ctx->buffer, bytes + pad + 8, ctx); + sha1_process_block(ctx->buffer, bytes + pad + 8, ctx); - return sha1_read_ctx (ctx, resbuf); + return sha1_read_ctx(ctx, resbuf); } /* Compute SHA1 message digest for bytes read from STREAM. The resulting message digest number will be written into the 16 bytes beginning at RESBLOCK. */ -int -sha1_stream (FILE *stream, void *resblock) -{ +int sha1_stream(FILE *stream, void *resblock) { struct sha1_ctx ctx; char buffer[BLOCKSIZE + 72]; size_t sum; /* Initialize the computation context. */ - sha1_init_ctx (&ctx); + sha1_init_ctx(&ctx); /* Iterate over full file contents. */ - while (1) - { - /* We read the file in blocks of BLOCKSIZE bytes. One call of the - computation function processes the whole buffer so that with the - next round of the loop another block can be read. */ - size_t n; - sum = 0; - - /* Read block. Take care for partial reads. */ - while (1) - { - n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); - - sum += n; - - if (sum == BLOCKSIZE) - break; - - if (n == 0) - { - /* Check for the error flag IFF N == 0, so that we don't - exit the loop after a partial read due to e.g., EAGAIN - or EWOULDBLOCK. */ - if (ferror (stream)) - return 1; - goto process_partial_block; - } - - /* We've read at least one byte, so ignore errors. But always - check for EOF, since feof may be true even though N > 0. - Otherwise, we could end up calling fread after EOF. */ - if (feof (stream)) - goto process_partial_block; - } - - /* Process buffer with BLOCKSIZE bytes. Note that - BLOCKSIZE % 64 == 0 - */ - sha1_process_block (buffer, BLOCKSIZE, &ctx); + while (1) { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + while (1) { + n = fread(buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + + if (sum == BLOCKSIZE) + break; + + if (n == 0) { + /* Check for the error flag IFF N == 0, so that we don't + exit the loop after a partial read due to e.g., EAGAIN + or EWOULDBLOCK. */ + if (ferror(stream)) + return 1; + goto process_partial_block; + } + + /* We've read at least one byte, so ignore errors. But always + check for EOF, since feof may be true even though N > 0. + Otherwise, we could end up calling fread after EOF. */ + if (feof(stream)) + goto process_partial_block; } - process_partial_block:; + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + sha1_process_block(buffer, BLOCKSIZE, &ctx); + } + +process_partial_block:; /* Process any remaining bytes. */ if (sum > 0) - sha1_process_bytes (buffer, sum, &ctx); + sha1_process_bytes(buffer, sum, &ctx); /* Construct result in desired memory. */ - sha1_finish_ctx (&ctx, resblock); + sha1_finish_ctx(&ctx, resblock); return 0; } @@ -190,85 +177,82 @@ result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ -void * -sha1_buffer (const char *buffer, size_t len, void *resblock) -{ +void *sha1_buffer(const char *buffer, size_t len, void *resblock) { struct sha1_ctx ctx; /* Initialize the computation context. */ - sha1_init_ctx (&ctx); + sha1_init_ctx(&ctx); /* Process whole buffer but last len % 64 bytes. */ - sha1_process_bytes (buffer, len, &ctx); + sha1_process_bytes(buffer, len, &ctx); /* Put result in desired memory area. */ - return sha1_finish_ctx (&ctx, resblock); + return sha1_finish_ctx(&ctx, resblock); } -void -sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) -{ +void sha1_process_bytes(const void *buffer, size_t len, struct sha1_ctx *ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ - if (ctx->buflen != 0) - { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy (&ctx->buffer[left_over], buffer, add); - ctx->buflen += add; - - if (ctx->buflen > 64) - { - sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap. */ - memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], - ctx->buflen); - } - - buffer = (const char *) buffer + add; - len -= add; + if (ctx->buflen != 0) { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy(&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) { + sha1_process_block(ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63], ctx->buflen); } + buffer = (const char *)buffer + add; + len -= add; + } + /* Process available complete blocks. */ - if (len >= 64) - { + if (len >= 64) { #if !_STRING_ARCH_unaligned -# define alignof(type) offsetof (struct { char c; type x; }, x) -# define UNALIGNED_P(p) (((size_t) p) % 4 != 0) //# define UNALIGNED_P(p) (((size_t) p) % alignof (md5_uint32) != 0) - if (UNALIGNED_P (buffer)) - while (len > 64) - { - sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); - buffer = (const char *) buffer + 64; - len -= 64; - } - else +#define alignof(type) \ + offsetof( \ + struct { \ + char c; \ + type x; \ + }, \ + x) +#define UNALIGNED_P(p) \ + (((size_t)p) % 4 != \ + 0) //# define UNALIGNED_P(p) (((size_t) p) % alignof (md5_uint32) != 0) + if (UNALIGNED_P(buffer)) + while (len > 64) { + sha1_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *)buffer + 64; + len -= 64; + } + else #endif - { - sha1_process_block (buffer, len & ~63, ctx); - buffer = (const char *) buffer + (len & ~63); - len &= 63; - } + { + sha1_process_block(buffer, len & ~63, ctx); + buffer = (const char *)buffer + (len & ~63); + len &= 63; } + } /* Move remaining bytes in internal buffer. */ - if (len > 0) - { - size_t left_over = ctx->buflen; + if (len > 0) { + size_t left_over = ctx->buflen; - memcpy (&ctx->buffer[left_over], buffer, len); - left_over += len; - if (left_over >= 64) - { - sha1_process_block (ctx->buffer, 64, ctx); - left_over -= 64; - memcpy (ctx->buffer, &ctx->buffer[64], left_over); - } - ctx->buflen = left_over; + memcpy(&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) { + sha1_process_block(ctx->buffer, 64, ctx); + left_over -= 64; + memcpy(ctx->buffer, &ctx->buffer[64], left_over); } + ctx->buflen = left_over; + } } /* --- Code below is the primary difference between md5.c and sha1.c --- */ @@ -280,20 +264,18 @@ #define K4 0xca62c1d6L /* Round functions. Note that F2 is the same as F4. */ -#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) ) -#define F2(B,C,D) (B ^ C ^ D) -#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) ) -#define F4(B,C,D) (B ^ C ^ D) +#define F1(B, C, D) (D ^ (B & (C ^ D))) +#define F2(B, C, D) (B ^ C ^ D) +#define F3(B, C, D) ((B & C) | (D & (B | C))) +#define F4(B, C, D) (B ^ C ^ D) /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. Most of this code comes from GnuPG's cipher/sha1.c. */ -void -sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx) -{ - const md5_uint32 *words = (md5_uint32*)buffer; - size_t nwords = len / sizeof (md5_uint32); +void sha1_process_block(const void *buffer, size_t len, struct sha1_ctx *ctx) { + const md5_uint32 *words = (md5_uint32 *)buffer; + size_t nwords = len / sizeof(md5_uint32); const md5_uint32 *endp = words + nwords; md5_uint32 x[16]; md5_uint32 a = ctx->A; @@ -311,113 +293,110 @@ #define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) -#define M(I) ( tm = x[I&0x0f] ^ x[(I-14)&0x0f] \ - ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \ - , (x[I&0x0f] = rol(tm, 1)) ) - -#define R(A,B,C,D,E,F,K,M) do { E += rol( A, 5 ) \ - + F( B, C, D ) \ - + K \ - + M; \ - B = rol( B, 30 ); \ - } while(0) - - while (words < endp) - { - md5_uint32 tm; - int t; - for (t = 0; t < 16; t++) - { - x[t] = SWAP32 (*words); - words++; - } - - R( a, b, c, d, e, F1, K1, x[ 0] ); - R( e, a, b, c, d, F1, K1, x[ 1] ); - R( d, e, a, b, c, F1, K1, x[ 2] ); - R( c, d, e, a, b, F1, K1, x[ 3] ); - R( b, c, d, e, a, F1, K1, x[ 4] ); - R( a, b, c, d, e, F1, K1, x[ 5] ); - R( e, a, b, c, d, F1, K1, x[ 6] ); - R( d, e, a, b, c, F1, K1, x[ 7] ); - R( c, d, e, a, b, F1, K1, x[ 8] ); - R( b, c, d, e, a, F1, K1, x[ 9] ); - R( a, b, c, d, e, F1, K1, x[10] ); - R( e, a, b, c, d, F1, K1, x[11] ); - R( d, e, a, b, c, F1, K1, x[12] ); - R( c, d, e, a, b, F1, K1, x[13] ); - R( b, c, d, e, a, F1, K1, x[14] ); - R( a, b, c, d, e, F1, K1, x[15] ); - R( e, a, b, c, d, F1, K1, M(16) ); - R( d, e, a, b, c, F1, K1, M(17) ); - R( c, d, e, a, b, F1, K1, M(18) ); - R( b, c, d, e, a, F1, K1, M(19) ); - R( a, b, c, d, e, F2, K2, M(20) ); - R( e, a, b, c, d, F2, K2, M(21) ); - R( d, e, a, b, c, F2, K2, M(22) ); - R( c, d, e, a, b, F2, K2, M(23) ); - R( b, c, d, e, a, F2, K2, M(24) ); - R( a, b, c, d, e, F2, K2, M(25) ); - R( e, a, b, c, d, F2, K2, M(26) ); - R( d, e, a, b, c, F2, K2, M(27) ); - R( c, d, e, a, b, F2, K2, M(28) ); - R( b, c, d, e, a, F2, K2, M(29) ); - R( a, b, c, d, e, F2, K2, M(30) ); - R( e, a, b, c, d, F2, K2, M(31) ); - R( d, e, a, b, c, F2, K2, M(32) ); - R( c, d, e, a, b, F2, K2, M(33) ); - R( b, c, d, e, a, F2, K2, M(34) ); - R( a, b, c, d, e, F2, K2, M(35) ); - R( e, a, b, c, d, F2, K2, M(36) ); - R( d, e, a, b, c, F2, K2, M(37) ); - R( c, d, e, a, b, F2, K2, M(38) ); - R( b, c, d, e, a, F2, K2, M(39) ); - R( a, b, c, d, e, F3, K3, M(40) ); - R( e, a, b, c, d, F3, K3, M(41) ); - R( d, e, a, b, c, F3, K3, M(42) ); - R( c, d, e, a, b, F3, K3, M(43) ); - R( b, c, d, e, a, F3, K3, M(44) ); - R( a, b, c, d, e, F3, K3, M(45) ); - R( e, a, b, c, d, F3, K3, M(46) ); - R( d, e, a, b, c, F3, K3, M(47) ); - R( c, d, e, a, b, F3, K3, M(48) ); - R( b, c, d, e, a, F3, K3, M(49) ); - R( a, b, c, d, e, F3, K3, M(50) ); - R( e, a, b, c, d, F3, K3, M(51) ); - R( d, e, a, b, c, F3, K3, M(52) ); - R( c, d, e, a, b, F3, K3, M(53) ); - R( b, c, d, e, a, F3, K3, M(54) ); - R( a, b, c, d, e, F3, K3, M(55) ); - R( e, a, b, c, d, F3, K3, M(56) ); - R( d, e, a, b, c, F3, K3, M(57) ); - R( c, d, e, a, b, F3, K3, M(58) ); - R( b, c, d, e, a, F3, K3, M(59) ); - R( a, b, c, d, e, F4, K4, M(60) ); - R( e, a, b, c, d, F4, K4, M(61) ); - R( d, e, a, b, c, F4, K4, M(62) ); - R( c, d, e, a, b, F4, K4, M(63) ); - R( b, c, d, e, a, F4, K4, M(64) ); - R( a, b, c, d, e, F4, K4, M(65) ); - R( e, a, b, c, d, F4, K4, M(66) ); - R( d, e, a, b, c, F4, K4, M(67) ); - R( c, d, e, a, b, F4, K4, M(68) ); - R( b, c, d, e, a, F4, K4, M(69) ); - R( a, b, c, d, e, F4, K4, M(70) ); - R( e, a, b, c, d, F4, K4, M(71) ); - R( d, e, a, b, c, F4, K4, M(72) ); - R( c, d, e, a, b, F4, K4, M(73) ); - R( b, c, d, e, a, F4, K4, M(74) ); - R( a, b, c, d, e, F4, K4, M(75) ); - R( e, a, b, c, d, F4, K4, M(76) ); - R( d, e, a, b, c, F4, K4, M(77) ); - R( c, d, e, a, b, F4, K4, M(78) ); - R( b, c, d, e, a, F4, K4, M(79) ); - - a = ctx->A += a; - b = ctx->B += b; - c = ctx->C += c; - d = ctx->D += d; - e = ctx->E += e; +#define M(I) \ + (tm = x[I & 0x0f] ^ x[(I - 14) & 0x0f] ^ x[(I - 8) & 0x0f] ^ \ + x[(I - 3) & 0x0f], \ + (x[I & 0x0f] = rol(tm, 1))) + +#define R(A, B, C, D, E, F, K, M) \ + do { \ + E += rol(A, 5) + F(B, C, D) + K + M; \ + B = rol(B, 30); \ + } while (0) + + while (words < endp) { + md5_uint32 tm; + int t; + for (t = 0; t < 16; t++) { + x[t] = SWAP32(*words); + words++; } -} + R(a, b, c, d, e, F1, K1, x[0]); + R(e, a, b, c, d, F1, K1, x[1]); + R(d, e, a, b, c, F1, K1, x[2]); + R(c, d, e, a, b, F1, K1, x[3]); + R(b, c, d, e, a, F1, K1, x[4]); + R(a, b, c, d, e, F1, K1, x[5]); + R(e, a, b, c, d, F1, K1, x[6]); + R(d, e, a, b, c, F1, K1, x[7]); + R(c, d, e, a, b, F1, K1, x[8]); + R(b, c, d, e, a, F1, K1, x[9]); + R(a, b, c, d, e, F1, K1, x[10]); + R(e, a, b, c, d, F1, K1, x[11]); + R(d, e, a, b, c, F1, K1, x[12]); + R(c, d, e, a, b, F1, K1, x[13]); + R(b, c, d, e, a, F1, K1, x[14]); + R(a, b, c, d, e, F1, K1, x[15]); + R(e, a, b, c, d, F1, K1, M(16)); + R(d, e, a, b, c, F1, K1, M(17)); + R(c, d, e, a, b, F1, K1, M(18)); + R(b, c, d, e, a, F1, K1, M(19)); + R(a, b, c, d, e, F2, K2, M(20)); + R(e, a, b, c, d, F2, K2, M(21)); + R(d, e, a, b, c, F2, K2, M(22)); + R(c, d, e, a, b, F2, K2, M(23)); + R(b, c, d, e, a, F2, K2, M(24)); + R(a, b, c, d, e, F2, K2, M(25)); + R(e, a, b, c, d, F2, K2, M(26)); + R(d, e, a, b, c, F2, K2, M(27)); + R(c, d, e, a, b, F2, K2, M(28)); + R(b, c, d, e, a, F2, K2, M(29)); + R(a, b, c, d, e, F2, K2, M(30)); + R(e, a, b, c, d, F2, K2, M(31)); + R(d, e, a, b, c, F2, K2, M(32)); + R(c, d, e, a, b, F2, K2, M(33)); + R(b, c, d, e, a, F2, K2, M(34)); + R(a, b, c, d, e, F2, K2, M(35)); + R(e, a, b, c, d, F2, K2, M(36)); + R(d, e, a, b, c, F2, K2, M(37)); + R(c, d, e, a, b, F2, K2, M(38)); + R(b, c, d, e, a, F2, K2, M(39)); + R(a, b, c, d, e, F3, K3, M(40)); + R(e, a, b, c, d, F3, K3, M(41)); + R(d, e, a, b, c, F3, K3, M(42)); + R(c, d, e, a, b, F3, K3, M(43)); + R(b, c, d, e, a, F3, K3, M(44)); + R(a, b, c, d, e, F3, K3, M(45)); + R(e, a, b, c, d, F3, K3, M(46)); + R(d, e, a, b, c, F3, K3, M(47)); + R(c, d, e, a, b, F3, K3, M(48)); + R(b, c, d, e, a, F3, K3, M(49)); + R(a, b, c, d, e, F3, K3, M(50)); + R(e, a, b, c, d, F3, K3, M(51)); + R(d, e, a, b, c, F3, K3, M(52)); + R(c, d, e, a, b, F3, K3, M(53)); + R(b, c, d, e, a, F3, K3, M(54)); + R(a, b, c, d, e, F3, K3, M(55)); + R(e, a, b, c, d, F3, K3, M(56)); + R(d, e, a, b, c, F3, K3, M(57)); + R(c, d, e, a, b, F3, K3, M(58)); + R(b, c, d, e, a, F3, K3, M(59)); + R(a, b, c, d, e, F4, K4, M(60)); + R(e, a, b, c, d, F4, K4, M(61)); + R(d, e, a, b, c, F4, K4, M(62)); + R(c, d, e, a, b, F4, K4, M(63)); + R(b, c, d, e, a, F4, K4, M(64)); + R(a, b, c, d, e, F4, K4, M(65)); + R(e, a, b, c, d, F4, K4, M(66)); + R(d, e, a, b, c, F4, K4, M(67)); + R(c, d, e, a, b, F4, K4, M(68)); + R(b, c, d, e, a, F4, K4, M(69)); + R(a, b, c, d, e, F4, K4, M(70)); + R(e, a, b, c, d, F4, K4, M(71)); + R(d, e, a, b, c, F4, K4, M(72)); + R(c, d, e, a, b, F4, K4, M(73)); + R(b, c, d, e, a, F4, K4, M(74)); + R(a, b, c, d, e, F4, K4, M(75)); + R(e, a, b, c, d, F4, K4, M(76)); + R(d, e, a, b, c, F4, K4, M(77)); + R(c, d, e, a, b, F4, K4, M(78)); + R(b, c, d, e, a, F4, K4, M(79)); + + a = ctx->A += a; + b = ctx->B += b; + c = ctx->C += c; + d = ctx->D += d; + e = ctx->E += e; + } +} diff -Nru atomicparsley-0.9.6/src/util.cpp atomicparsley-20210715.151551.e7ad03a/src/util.cpp --- atomicparsley-0.9.6/src/util.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/util.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - util.cpp - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,466 +10,569 @@ 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 + 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 2006-2007 puck_lock + 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 - - */ + + * SLarew - prevent writing past array in Convert_multibyteUTF16_to_wchar + bugfix + + */ //==================================================================// #include "AtomicParsley.h" - /////////////////////////////////////////////////////////////////////////////////////// -// Filesytem routines // +// Filesytem routines // /////////////////////////////////////////////////////////////////////////////////////// /*---------------------- findFileSize - utf8_filepath - a pointer to a string (possibly utf8) of the full path to the file + 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). + 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?) +#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. + 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; +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) + 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 + 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 != NULL) { - file_opened = true; - } - } else { - fclose(source_file); - file_opened = false; - } - return 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); - } - } + 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 +#ifdef _WIN32 +int fseeko(FILE *stream, off_t pos, int whence) { + return _fseeki64(stream, pos, whence); +} -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; +off_t ftello(FILE *stream) { return _ftelli64(stream); } + +#else +int fseeko(FILE *stream, off_t pos, int whence) { + return fseek(stream, pos, whence); } +off_t ftello(FILE *stream) { return ftell(stream); } +#endif #endif +#if defined(_WIN32) + +/////////////////////////////////////////////////////////////////////////////////////// +// Win32 functions // +/////////////////////////////////////////////////////////////////////////////////////// + /*---------------------- 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 + 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); - } +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; +#if defined(_WIN32) + OSVERSIONINFOW os; memset(&os, 0, sizeof(OSVERSIONINFOW)); os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); return (GetVersionExW(&os) != 0); #else - return false; + return false; #endif } /////////////////////////////////////////////////////////////////////////////////////// -// File reading routines // +// 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; + 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 // +// 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 ' 2005' (with a NULL at the end) show up as ' 2005' in 'pol', but ' 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; + 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 ' 2005' (with a NULL at the end) show up as ' 2005' in + // 'pol', but ' 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 // -/////////////////////////////////////////////////////////////////////////////////////// - -#if !defined(HAVE_LROUNDF) -int lroundf(float a) { - return (int)(a/1); -} -#endif +// 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 +// 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 *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; + // 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 - { - if (*begin == ch) - end = begin; - else if (*begin == '\0') - end = NULL; - else - end = strchr (begin + 1, ch); - } + end = strchr(begin + 1, ch); } - else + } else - end = strpbrk (begin, delim); //Find the end of the token. + 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. + 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; + 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; } /*---------------------- @@ -478,332 +581,418 @@ 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; +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; +#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; + 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; + return 0; } - /*---------------------- APar_StandardTime - formed_time - the destination string + formed_time - the destination string - Print the ISO 8601 Coordinated Universal Time (UTC) timestamp (in YYYY-MM-DDTHH:MM:SSZ form) + Print the ISO 8601 Coordinated Universal Time (UTC) timestamp (in +YYYY-MM-DDTHH:MM:SSZ form) ----------------------*/ -void APar_StandardTime(char* &formed_time) { +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; + 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 +// 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 + 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; + } + 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 +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 + 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 + 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; +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 // +// generics // /////////////////////////////////////////////////////////////////////////////////////// uint16_t UInt16FromBigEndian(const char *string) { -#if defined (__ppc__) || defined (__ppc64__) - uint16_t test; - memcpy(&test,string,2); - return test; +#if defined(__ppc__) || defined(__ppc64__) + uint16_t test; + memcpy(&test, string, 2); + return test; #else - return (((string[0] & 0xff) << 8) | (string[1] & 0xff) << 0); + 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; +#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); + 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; +#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; + 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; +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') // +// 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; + 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; + 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 */ @@ -814,15 +1003,15 @@ typedef unsigned long xorgenUINT; unsigned long xor4096i() { - /* 32-bit or 64-bit integer random number generator + /* 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 + + 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. - + thereafter with zero seed. + One random number uniformly distributed in [0..2**wlen) is returned, where wlen = 8*sizeof(xorgenUINT) = 32 or 64. @@ -831,72 +1020,82 @@ /* 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; - +#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 */ + static int i = -1; /* i < 0 indicates first call */ int k; - - if (i < 0) { + + if (i < 0) { #if defined HAVE_SRANDDEV - sranddev(); + sranddev(); #else - srand((int) time(NULL)); + srand((int)time(NULL)); #endif - double doubleseed = ( (double)rand() / ((double)(RAND_MAX)+(double)(1)) ); - seed = (xorgenUINT)(doubleseed*rand()); - } - + 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 = 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 */ - } + 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<>b; - v = x[(i+(r-s))&(r-1)]; v ^= v<>d; - x[i] = t^v; - } + 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<>b; /* (I + L^a)(I + R^b) */ - 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 */ + 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 @@ -907,5 +1106,5 @@ #undef b #undef c #undef d -#undef ws +#undef ws } diff -Nru atomicparsley-0.9.6/src/util.h atomicparsley-20210715.151551.e7ad03a/src/util.h --- atomicparsley-0.9.6/src/util.h 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/util.h 2021-07-15 22:15:51.000000000 +0000 @@ -17,86 +17,104 @@ Copyright 2006-2007 puck_lock with contributions from others; see the CREDITS file - */ + */ //==================================================================// #include "ap_types.h" -#if defined (__ppc__) || defined (__ppc64__) +#if defined(__ppc__) || defined(__ppc64__) #define SWAP16(x) (x) #define SWAP32(x) (x) #else -#define SWAP16(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) -#define SWAP32(x) ((((x)&0xFF)<<24) | (((x)>>24)&0xFF) | (((x)&0x0000FF00)<<8) | (((x)&0x00FF0000)>>8) ) +#define SWAP16(x) ((((x)&0xFF) << 8) | (((x) >> 8) & 0xFF)) +#define SWAP32(x) \ + ((((x)&0xFF) << 24) | (((x) >> 24) & 0xFF) | (((x)&0x0000FF00) << 8) | \ + (((x)&0x00FF0000) >> 8)) #endif -#if defined (_WIN32) && defined (_MSC_VER) +#if defined(_WIN32) && defined(_MSC_VER) #undef HAVE_GETOPT_H #undef HAVE_LROUNDF #undef HAVE_STRSEP -//#undef HAVE_ZLIB_H //comment this IN when compiling on win32 withOUT zlib present -//#define HAVE_ZLIB_H 1 //and comment this OUT +//#undef HAVE_ZLIB_H //comment this IN when compiling on win32 withOUT zlib +// present #define HAVE_ZLIB_H 1 //and comment this OUT #undef HAVE_SRANDDEV #endif #define MAXTIME_32 6377812095ULL uint64_t findFileSize(const char *utf8_filepath); -FILE* APar_OpenFile(const char* utf8_filepath, const char* file_flags); -FILE* APar_OpenISOBaseMediaFile(const char* file, bool open); //openSomeFile +FILE *APar_OpenFile(const char *utf8_filepath, const char *file_flags); +FILE *APar_OpenISOBaseMediaFile(const char *file, bool open); // openSomeFile void TestFileExistence(const char *filePath, bool errorOut); -#if defined (_WIN32) #ifndef HAVE_FSEEKO -int fseeko(FILE *stream, uint64_t pos, int whence); +int fseeko(FILE *stream, off_t pos, int whence); +off_t ftello(FILE *stream); #endif -HANDLE APar_OpenFileWin32(const char* utf8_filepath, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); + +#if defined(_WIN32) +HANDLE APar_OpenFileWin32(const char *utf8_filepath, + DWORD dwDesiredAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); #endif bool IsUnicodeWinOS(); const char *APar_strferror(FILE *f); -uint8_t APar_read8(FILE* ISObasemediafile, uint64_t pos); -uint16_t APar_read16(char* buffer, FILE* ISObasemediafile, uint64_t pos); -uint32_t APar_read32(char* buffer, FILE* ISObasemediafile, uint64_t pos); -uint64_t APar_read64(char* buffer, FILE* ISObasemediafile, uint64_t pos); -void APar_readX_noseek(char* buffer, FILE* ISObasemediafile, uint32_t length); -void APar_readX(char* buffer, FILE* ISObasemediafile, uint64_t pos, uint32_t length); -uint32_t APar_ReadFile(char* destination_buffer, FILE* a_file, uint32_t bytes_to_read); -uint32_t APar_FindValueInAtom(char* uint32_buffer, FILE* ISObasemediafile, short an_atom, uint64_t start_position, uint32_t eval_number); +uint8_t APar_read8(FILE *ISObasemediafile, uint64_t pos); +uint16_t APar_read16(char *buffer, FILE *ISObasemediafile, uint64_t pos); +uint32_t APar_read32(char *buffer, FILE *ISObasemediafile, uint64_t pos); +uint64_t APar_read64(char *buffer, FILE *ISObasemediafile, uint64_t pos); +void APar_readX_noseek(char *buffer, FILE *ISObasemediafile, uint32_t length); +void APar_readX(char *buffer, + FILE *ISObasemediafile, + uint64_t pos, + uint32_t length); +uint32_t +APar_ReadFile(char *destination_buffer, FILE *a_file, uint32_t bytes_to_read); +uint32_t APar_FindValueInAtom(char *uint32_buffer, + FILE *ISObasemediafile, + short an_atom, + uint64_t start_position, + uint32_t eval_number); void APar_UnpackLanguage(unsigned char lang_code[], uint16_t packed_language); -uint16_t PackLanguage(const char* language_code, uint8_t lang_offset); - -#if !defined(HAVE_LROUNDF) -int lroundf(float a); -#endif +uint16_t PackLanguage(const char *language_code, uint8_t lang_offset); #ifndef HAVE_STRSEP -char *strsep (char **stringp, const char *delim); +char *strsep(char **stringp, const char *delim); #endif -char* APar_extract_UTC(uint64_t total_secs); +char *APar_extract_UTC(uint64_t total_secs); uint32_t APar_get_mpeg4_time(); -void APar_StandardTime(char* &formed_time); - -wchar_t* Convert_multibyteUTF16_to_wchar(char* input_unicode, size_t glyph_length, bool skip_BOM); -unsigned char* Convert_multibyteUTF16_to_UTF8(char* input_utf8, size_t glyph_length, size_t byte_count); -wchar_t* Convert_multibyteUTF8_to_wchar(const char* input_utf8); -uint32_t findstringNULLterm(char* in_string, uint8_t encodingFlag, uint32_t max_len); -uint32_t skipNULLterm(char* in_string, uint8_t encodingFlag, uint32_t max_len); +void APar_StandardTime(char *&formed_time); +wchar_t *Convert_multibyteUTF16_to_wchar(char *input_unicode, + size_t glyph_length, + bool skip_BOM); +unsigned char *Convert_multibyteUTF16_to_UTF8(char *input_utf8, + size_t glyph_length, + size_t byte_count); +wchar_t *Convert_multibyteUTF8_to_wchar(const char *input_utf8); +uint32_t +findstringNULLterm(char *in_string, uint8_t encodingFlag, uint32_t max_len); +uint32_t skipNULLterm(char *in_string, uint8_t encodingFlag, uint32_t max_len); uint16_t UInt16FromBigEndian(const char *string); uint32_t UInt32FromBigEndian(const char *string); uint64_t UInt64FromBigEndian(const char *string); -void UInt16_TO_String2(uint16_t snum, char* data); -void UInt32_TO_String4(uint32_t lnum, char* data); -void UInt64_TO_String8(uint64_t ullnum, char* data); +void UInt16_TO_String2(uint16_t snum, char *data); +void UInt32_TO_String4(uint32_t lnum, char *data); +void UInt64_TO_String8(uint64_t ullnum, char *data); uint32_t float_to_16x16bit_fixed_point(double floating_val); double fixed_point_16x16bit_to_double(uint32_t fixed_point); -uint32_t widechar_len(char* instring, uint32_t _bytes_); +uint32_t widechar_len(char *instring, uint32_t _bytes_); -bool APar_assert(bool expression, int error_msg, const char* supplemental_info); +bool APar_assert(bool expression, int error_msg, const char *supplemental_info); unsigned long xor4096i(); diff -Nru atomicparsley-0.9.6/src/uuid.cpp atomicparsley-20210715.151551.e7ad03a/src/uuid.cpp --- atomicparsley-0.9.6/src/uuid.cpp 2014-03-03 19:18:56.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/src/uuid.cpp 2021-07-15 22:15:51.000000000 +0000 @@ -2,7 +2,7 @@ /* AtomicParsley - uuid.cpp - AtomicParsley is GPL software; you can freely distribute, + 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. @@ -10,12 +10,12 @@ 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 + 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 2006-2007 puck_lock + Copyright (C) 2006-2007 puck_lock with contributions from others; see the CREDITS file */ //==================================================================// @@ -23,28 +23,26 @@ //==================================================================// /* Much of AP_Create_UUID_ver5_sha1_name was derived from - http://www.ietf.org/rfc/rfc4122.txt - which I don't believe conflicts with or restricts the GPL. - And this page: - http://home.famkruithof.net/guid-uuid-namebased.html - tells me I'm not on crack when I try to calculate the uuids myself. - - Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. - Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & - Digital Equipment Corporation, Maynard, Mass. - Copyright (c) 1998 Microsoft. - To anyone who acknowledges that this file is provided "AS IS" - without any express or implied warranty: permission to use, copy, - modify, and distribute this file for any purpose is hereby - granted without fee, provided that the above copyright notices and - this notice appears in all source code copies, and that none of - the names of Open Software Foundation, Inc., Hewlett-Packard - Company, Microsoft, or Digital Equipment Corporation be used in - advertising or publicity pertaining to distribution of the software - without specific, written prior permission. Neither Open Software - Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital - Equipment Corporation makes any representations about the - suitability of this software for any purpose. + http://www.ietf.org/rfc/rfc4122.txt + which I don't believe conflicts with or restricts the GPL. + And this page: + http://home.famkruithof.net/guid-uuid-namebased.html + tells me I'm not on crack when I try to calculate the uuids + myself. + + Copyright (c) 1990- 1993, 1996 Open Software Foundation, + Inc. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital + Equipment Corporation, Maynard, Mass. Copyright (c) 1998 Microsoft. To anyone + who acknowledges that this file is provided "AS IS" without any express or + implied warranty: permission to use, copy, modify, and distribute this file + for any purpose is hereby granted without fee, provided that the above + copyright notices and this notice appears in all source code copies, and that + none of the names of Open Software Foundation, Inc., Hewlett-Packard Company, + Microsoft, or Digital Equipment Corporation be used in advertising or + publicity pertaining to distribution of the software without specific, + written prior permission. Neither Open Software Foundation, Inc., + Hewlett-Packard Company, Microsoft, nor Digital Equipment Corporation makes + any representations about the suitability of this software for any purpose. */ //==================================================================// @@ -54,52 +52,58 @@ print_hash hash - the string array of the sha1 hash - prints out the hex representation of the 16 byte hash - this relates to sha1, but its here to keep the sha1 files as close to original as possible + prints out the hex representation of the 16 byte hash - this +relates to sha1, but its here to keep the sha1 files as close to original as +possible ----------------------*/ void print_hash(char hash[]) { - for (int i=0; i < 20; i++) { - fprintf(stdout,"%02x", (uint8_t)hash[i]); - } - fprintf(stdout, "\n"); - return; + for (int i = 0; i < 20; i++) { + fprintf(stdout, "%02x", (uint8_t)hash[i]); + } + fprintf(stdout, "\n"); + return; } /*---------------------- Swap_Char in_str - the string to have the swap operation performed on - str_len - the amount of bytes to swap in the string + str_len - the amount of bytes to swap in the string - Make a pointer to the start & end of the string, as well as a temporary string to hold the swapped byte. As the start increments up, the end decrements down. Copy - the byte at each advancing start position. Copy the byte of the diminishing end string into the start byte, then advance the start byte. Finaly, set each byte of - the decrementing end pointer to the temp string byte. + Make a pointer to the start & end of the string, as well as a +temporary string to hold the swapped byte. As the start increments up, the end +decrements down. Copy the byte at each advancing start position. Copy the byte +of the diminishing end string into the start byte, then advance the start byte. +Finaly, set each byte of the decrementing end pointer to the temp string byte. ----------------------*/ void Swap_Char(char *in_str, uint8_t str_len) { - char *start_str, *end_str, temp_str; + char *start_str, *end_str, temp_str; - start_str= in_str; - end_str=start_str+ str_len; - while ( start_str<--end_str ) { - temp_str=*start_str; - *start_str++=*end_str; - *end_str=temp_str; - } - return; + start_str = in_str; + end_str = start_str + str_len; + while (start_str < --end_str) { + temp_str = *start_str; + *start_str++ = *end_str; + *end_str = temp_str; + } + return; } /*---------------------- APar_endian_uuid_bin_str_conversion raw_uuid - a binary string representation of a uuid - As a string representation of a uuid, there is a 32-bit & 2 16-bit numbers in the uuid. These members need to be swapped on big endian systems. + As a string representation of a uuid, there is a 32-bit & 2 +16-bit numbers in the uuid. These members need to be swapped on big endian +systems. ----------------------*/ -void APar_endian_uuid_bin_str_conversion(char* raw_uuid) { -#if defined (__ppc__) || defined (__ppc64__) - return; //we are *naturally* network byte ordered - simplicity +void APar_endian_uuid_bin_str_conversion(char *raw_uuid) { +#if defined(__ppc__) || defined(__ppc64__) + return; // we are *naturally* network byte ordered - simplicity #else - Swap_Char(raw_uuid, 4); - Swap_Char(raw_uuid+4, 2); - Swap_Char(raw_uuid+4+2, 2); - return; + Swap_Char(raw_uuid, 4); + Swap_Char(raw_uuid + 4, 2); + Swap_Char(raw_uuid + 4 + 2, 2); + return; #endif } @@ -107,288 +111,357 @@ APar_print_uuid uuid - a uuid structure containing the uuid - Print out a full string representation of a uuid + Print out a full string representation of a uuid ----------------------*/ -void APar_print_uuid(ap_uuid_t* uuid, bool new_line) { - fprintf(stdout, "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - uuid->time_low, - uuid->time_mid, - uuid->time_hi_and_version, - uuid->clock_seq_hi_and_reserved, - uuid->clock_seq_low, - uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], uuid->node[4], uuid->node[5]); - if (new_line) fprintf(stdout, "\n"); - return; +void APar_print_uuid(ap_uuid_t *uuid, bool new_line) { + fprintf(stdout, + "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid->time_low, + uuid->time_mid, + uuid->time_hi_and_version, + uuid->clock_seq_hi_and_reserved, + uuid->clock_seq_low, + uuid->node[0], + uuid->node[1], + uuid->node[2], + uuid->node[3], + uuid->node[4], + uuid->node[5]); + if (new_line) + fprintf(stdout, "\n"); + return; } /*---------------------- APar_sprintf_uuid uuid - a uuid structure containing the uuid - destination - the end result uuid will be placed here + destination - the end result uuid will be placed here - Put a binary representation of a uuid to a human-readable ordered uuid string + Put a binary representation of a uuid to a human-readable +ordered uuid string ----------------------*/ -void APar_sprintf_uuid(ap_uuid_t* uuid, char* destination) { - sprintf(destination, "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - uuid->time_low, - uuid->time_mid, - uuid->time_hi_and_version, - uuid->clock_seq_hi_and_reserved, - uuid->clock_seq_low, - uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], uuid->node[4], uuid->node[5]); - return; +void APar_sprintf_uuid(ap_uuid_t *uuid, char *destination) { + sprintf(destination, + "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid->time_low, + uuid->time_mid, + uuid->time_hi_and_version, + uuid->clock_seq_hi_and_reserved, + uuid->clock_seq_low, + uuid->node[0], + uuid->node[1], + uuid->node[2], + uuid->node[3], + uuid->node[4], + uuid->node[5]); + return; } /*---------------------- APar_uuid_scanf - in_formed_uuid - pointer to a string (or a place in a string) where to place a binary (hex representation) string uuid of 16 bytes - raw_uuid - the string that contains a string representation of a uuid (from cli input for example). This string isn't 16 bytes - its 36 - - Skip past a hyphen, make any upper case characters lower (ahh, that hex 'Q') to do a manual scanf on the string. Add its hex representation as a number - for 1/2 of the bits (a single byte is 2 hex characters), shift it over to the upper bits, and repeat adding the lower bits. Repeat until done. + in_formed_uuid - pointer to a string (or a place in a string) where to place a +binary (hex representation) string uuid of 16 bytes raw_uuid - the string that +contains a string representation of a uuid (from cli input for example). This +string isn't 16 bytes - its 36 + + Skip past a hyphen, make any upper case characters lower (ahh, +that hex 'Q') to do a manual scanf on the string. Add its hex representation as +a number for 1/2 of the bits (a single byte is 2 hex characters), shift it over +to the upper bits, and repeat adding the lower bits. Repeat until done. ----------------------*/ -uint8_t APar_uuid_scanf(char* in_formed_uuid, const char* raw_uuid_in) { - char *uuid_str, *end_uuid_str, *uuid_byte; - uint8_t uuid_pos, uuid_len; - uint8_t keeprap = 0; -#if defined (_WIN32) && !defined (__CYGWIN__) - char *raw_uuid = _strdup(raw_uuid_in); +uint8_t APar_uuid_scanf(char *in_formed_uuid, const char *raw_uuid_in) { + char *uuid_str, *end_uuid_str, *uuid_byte; + uint8_t uuid_pos, uuid_len; + uint8_t keeprap = 0; +#if defined(_WIN32) && !defined(__CYGWIN__) + char *raw_uuid = _strdup(raw_uuid_in); #else - char *raw_uuid = strdup(raw_uuid_in); + char *raw_uuid = strdup(raw_uuid_in); #endif - uuid_len = strlen(raw_uuid); //it will be like "55534d54-21d2-4fce-bb88-695cfac9c740" - uuid_str = raw_uuid; - uuid_pos = 0; - end_uuid_str = uuid_str+uuid_len; - - while (uuid_str < end_uuid_str) { - uuid_byte = &in_formed_uuid[uuid_pos]; - if (uuid_str[0] == '-') uuid_str++; - if (uuid_str[0] >= 'A' && uuid_str[0] <= 90) uuid_str[0] +=32; - if (uuid_str[1] >= 'A' && uuid_str[1] <= 90) uuid_str[0] +=32; - - for (int i = 0; i <= 1; i++) { - switch(uuid_str[i]) { - case '0' : { - keeprap = 0; - break; - } - case '1' : { - keeprap = 1; - break; - } - case '2' : { - keeprap = 2; - break; - } - case '3' : { - keeprap = 3; - break; - } - case '4' : { - keeprap = 4; - break; - } - case '5' : { - keeprap = 5; - break; - } - case '6' : { - keeprap = 6; - break; - } - case '7' : { - keeprap = 7; - break; - } - case '8' : { - keeprap = 8; - break; - } - case '9' : { - keeprap = 9; - break; - } - case 'a' : { - keeprap = 10; - break; - } - case 'b' : { - keeprap = 11; - break; - } - case 'c' : { - keeprap = 12; - break; - } - case 'd' : { - keeprap = 13; - break; - } - case 'e' : { - keeprap = 14; - break; - } - case 'f' : { - keeprap = 15; - break; - } - } - - if (i == 0) { - *uuid_byte = keeprap << 4; - } else { - *uuid_byte |= keeprap; //(keeprap & 0xF0); - } - } - uuid_str+=2; - uuid_pos++; - } - APar_endian_uuid_bin_str_conversion(in_formed_uuid); - free(raw_uuid); - return uuid_pos; + uuid_len = strlen( + raw_uuid); // it will be like "55534d54-21d2-4fce-bb88-695cfac9c740" + uuid_str = raw_uuid; + uuid_pos = 0; + end_uuid_str = uuid_str + uuid_len; + + while (uuid_str < end_uuid_str) { + uuid_byte = &in_formed_uuid[uuid_pos]; + if (uuid_str[0] == '-') + uuid_str++; + if (uuid_str[0] >= 'A' && uuid_str[0] <= 90) + uuid_str[0] += 32; + if (uuid_str[1] >= 'A' && uuid_str[1] <= 90) + uuid_str[0] += 32; + + for (int i = 0; i <= 1; i++) { + switch (uuid_str[i]) { + case '0': { + keeprap = 0; + break; + } + case '1': { + keeprap = 1; + break; + } + case '2': { + keeprap = 2; + break; + } + case '3': { + keeprap = 3; + break; + } + case '4': { + keeprap = 4; + break; + } + case '5': { + keeprap = 5; + break; + } + case '6': { + keeprap = 6; + break; + } + case '7': { + keeprap = 7; + break; + } + case '8': { + keeprap = 8; + break; + } + case '9': { + keeprap = 9; + break; + } + case 'a': { + keeprap = 10; + break; + } + case 'b': { + keeprap = 11; + break; + } + case 'c': { + keeprap = 12; + break; + } + case 'd': { + keeprap = 13; + break; + } + case 'e': { + keeprap = 14; + break; + } + case 'f': { + keeprap = 15; + break; + } + } + + if (i == 0) { + *uuid_byte = keeprap << 4; + } else { + *uuid_byte |= keeprap; //(keeprap & 0xF0); + } + } + uuid_str += 2; + uuid_pos++; + } + APar_endian_uuid_bin_str_conversion(in_formed_uuid); + free(raw_uuid); + return uuid_pos; } /*---------------------- APar_extract_uuid_version uuid - a uuid structure containing the uuid - binary_uuid_str - a binary string rep of a uuid (without dashes, just the hex) + binary_uuid_str - a binary string rep of a uuid (without dashes, just +the hex) - Test the 6th byte in a str and push the bits to get the version or take the 3rd member of a uuid (uint16_t) and shift bits by 12 + Test the 6th byte in a str and push the bits to get the version +or take the 3rd member of a uuid (uint16_t) and shift bits by 12 ----------------------*/ -uint8_t APar_extract_uuid_version(ap_uuid_t* uuid, char* binary_uuid_str) { - uint8_t uuid_ver = 0; - if (binary_uuid_str != NULL) { - uuid_ver = (binary_uuid_str[6] >> 4); - } else if (uuid != NULL) { - uuid_ver = (uuid->time_hi_and_version >> 12); - } - return uuid_ver; +uint8_t APar_extract_uuid_version(ap_uuid_t *uuid, char *binary_uuid_str) { + uint8_t uuid_ver = 0; + if (binary_uuid_str != NULL) { + uuid_ver = (binary_uuid_str[6] >> 4); + } else if (uuid != NULL) { + uuid_ver = (uuid->time_hi_and_version >> 12); + } + return uuid_ver; } /*---------------------- AP_Create_UUID_ver5_sha1_name uuid - pointer to the final version 5 sha1 hash bashed uuid - desired_namespace - the input uuid used as a basis for the hash; a ver5 uuid is a namespace/name uuid. This is the namespace portion - name - this is the name portion used to make a v5 sha1 hash - namelen - length of name (currently a strlen() value) - - This will create a version 5 sha1 based uuid of a name in a namespace. The desired_namespace has its endian members swapped to newtwork byte ordering. The sha1 - hash algorithm is fed the reordered netord_namespace uuid to create a hash; the name is then added to the hash to create a hash of the name in the namespace. - The final hash is then copied into the out_uuid, and the endian members of the ap_uuid_t are swapped to endian ordering. + desired_namespace - the input uuid used as a basis for the hash; a ver5 +uuid is a namespace/name uuid. This is the namespace portion name - this is the +name portion used to make a v5 sha1 hash namelen - length of name (currently a +strlen() value) + + This will create a version 5 sha1 based uuid of a name in a +namespace. The desired_namespace has its endian members swapped to newtwork byte +ordering. The sha1 hash algorithm is fed the reordered netord_namespace uuid to +create a hash; the name is then added to the hash to create a hash of the name +in the namespace. The final hash is then copied into the out_uuid, and the +endian members of the ap_uuid_t are swapped to endian ordering. ----------------------*/ -void AP_Create_UUID_ver5_sha1_name(ap_uuid_t* out_uuid, ap_uuid_t desired_namespace, const char *name, int namelen) { - sha1_ctx sha_state; - char hash[20]; - ap_uuid_t networkorderd_namespace; - - //swap the endian members of uuid to network byte order for hash uniformity across platforms (the NULL or AP.sf.net uuid) - networkorderd_namespace = desired_namespace; - networkorderd_namespace.time_low = SWAP32(networkorderd_namespace.time_low); - networkorderd_namespace.time_mid = SWAP16(networkorderd_namespace.time_mid); - networkorderd_namespace.time_hi_and_version = SWAP16(networkorderd_namespace.time_hi_and_version); - - //make a hash of the input desired_namespace (as netord_ns); add the name (the AP.sf.net namespace as string or the atom name) - - sha1_init_ctx(&sha_state); - sha1_process_bytes( (char*)&networkorderd_namespace, sizeof networkorderd_namespace, &sha_state); - sha1_process_bytes(name, namelen, &sha_state); - sha1_finish_ctx(&sha_state, hash); - - // quasi uuid sha1hash is network byte ordered. swap the endian members of the uuid (uint32_t & uint16_t) to local byte order - // this creates additional requirements later that have to be APar_endian_uuid_bin_str_conversion() swapped, but leave this for now for coherence - memcpy(out_uuid, hash, sizeof *out_uuid); - out_uuid->time_low = SWAP32(out_uuid->time_low); - out_uuid->time_mid = SWAP16(out_uuid->time_mid); - out_uuid->time_hi_and_version = SWAP16(out_uuid->time_hi_and_version); - - out_uuid->time_hi_and_version &= 0x0FFF; //mask hash octes 6&7 - out_uuid->time_hi_and_version |= (5 << 12); //set bits 12-15 to the version (5 for sha1 namespace/name hash uuids) - out_uuid->clock_seq_hi_and_reserved &= 0x3F; //mask hash octet 8 - out_uuid->clock_seq_hi_and_reserved |= 0x80; //Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively. - return; +void AP_Create_UUID_ver5_sha1_name(ap_uuid_t *out_uuid, + ap_uuid_t desired_namespace, + const char *name, + int namelen) { + sha1_ctx sha_state; + char hash[20]; + ap_uuid_t networkorderd_namespace; + + // swap the endian members of uuid to network byte order for hash uniformity + // across platforms (the NULL or AP.sf.net uuid) + networkorderd_namespace = desired_namespace; + networkorderd_namespace.time_low = SWAP32(networkorderd_namespace.time_low); + networkorderd_namespace.time_mid = SWAP16(networkorderd_namespace.time_mid); + networkorderd_namespace.time_hi_and_version = + SWAP16(networkorderd_namespace.time_hi_and_version); + + // make a hash of the input desired_namespace (as netord_ns); add the name + // (the AP.sf.net namespace as string or the atom name) + + sha1_init_ctx(&sha_state); + sha1_process_bytes((char *)&networkorderd_namespace, + sizeof networkorderd_namespace, + &sha_state); + sha1_process_bytes(name, namelen, &sha_state); + sha1_finish_ctx(&sha_state, hash); + + // quasi uuid sha1hash is network byte ordered. swap the endian members of the + // uuid (uint32_t & uint16_t) to local byte order this creates additional + // requirements later that have to be APar_endian_uuid_bin_str_conversion() + // swapped, but leave this for now for coherence + memcpy(out_uuid, hash, sizeof *out_uuid); + out_uuid->time_low = SWAP32(out_uuid->time_low); + out_uuid->time_mid = SWAP16(out_uuid->time_mid); + out_uuid->time_hi_and_version = SWAP16(out_uuid->time_hi_and_version); + + out_uuid->time_hi_and_version &= 0x0FFF; // mask hash octes 6&7 + out_uuid->time_hi_and_version |= + (5 << 12); // set bits 12-15 to the version (5 for sha1 namespace/name + // hash uuids) + out_uuid->clock_seq_hi_and_reserved &= 0x3F; // mask hash octet 8 + out_uuid->clock_seq_hi_and_reserved |= + 0x80; // Set the two most significant bits (bits 6 and 7) of the + // clock_seq_hi_and_reserved to zero and one, respectively. + return; } /*---------------------- AP_Create_UUID_ver3_random out_uuid - pointer to the final version 3 randomly generated uuid - Use a high quality random number generator (still requiring a seed) to generate a random uuid. - In 2.5 million creations, all have been unique (at least on Mac OS X with sranddev providing the initial seed). + Use a high quality random number generator (still requiring a +seed) to generate a random uuid. In 2.5 million creations, all have been unique +(at least on Mac OS X with sranddev providing the initial seed). ----------------------*/ -void AP_Create_UUID_ver3_random(ap_uuid_t* out_uuid) { - uint32_t rand1 = 0; +void AP_Create_UUID_ver3_random(ap_uuid_t *out_uuid) { + uint32_t rand1 = 0; - out_uuid->time_low = xor4096i(); - rand1 = xor4096i(); - out_uuid->time_mid = (rand1 >> 16) & 0xFFFF; - out_uuid->node[0] = (rand1 >> 8) & 0xFF; - out_uuid->node[1] = (rand1 >> 0) & 0xFF; - rand1 = xor4096i(); - out_uuid->node[2] = (rand1 >> 24) & 0xFF; - out_uuid->node[3] = (rand1 >> 16) & 0xFF; - out_uuid->node[4] = (rand1 >> 8) & 0xFF; - out_uuid->node[5] = (rand1 >> 0) & 0xFF; - rand1 = xor4096i(); - out_uuid->time_hi_and_version = (rand1 >> 16) & 0xFFFF; - out_uuid->clock_seq_low = (rand1 >> 8) & 0xFF; - out_uuid->clock_seq_hi_and_reserved = (rand1 >> 0) & 0x3F; - out_uuid->clock_seq_hi_and_reserved |= 0x40; //bits 6 & 7 must be 0 & 1 respectively - - out_uuid->time_hi_and_version &= 0x0FFF; - out_uuid->time_hi_and_version |= (3 << 12); //set bits 12-15 to the version (3 for peusdo-random/random) - return; + out_uuid->time_low = xor4096i(); + rand1 = xor4096i(); + out_uuid->time_mid = (rand1 >> 16) & 0xFFFF; + out_uuid->node[0] = (rand1 >> 8) & 0xFF; + out_uuid->node[1] = (rand1 >> 0) & 0xFF; + rand1 = xor4096i(); + out_uuid->node[2] = (rand1 >> 24) & 0xFF; + out_uuid->node[3] = (rand1 >> 16) & 0xFF; + out_uuid->node[4] = (rand1 >> 8) & 0xFF; + out_uuid->node[5] = (rand1 >> 0) & 0xFF; + rand1 = xor4096i(); + out_uuid->time_hi_and_version = (rand1 >> 16) & 0xFFFF; + out_uuid->clock_seq_low = (rand1 >> 8) & 0xFF; + out_uuid->clock_seq_hi_and_reserved = (rand1 >> 0) & 0x3F; + out_uuid->clock_seq_hi_and_reserved |= + 0x40; // bits 6 & 7 must be 0 & 1 respectively + + out_uuid->time_hi_and_version &= 0x0FFF; + out_uuid->time_hi_and_version |= + (3 << 12); // set bits 12-15 to the version (3 for peusdo-random/random) + return; } /*---------------------- APar_generate_uuid_from_atomname atom_name - the 4 character atom name to create a uuid for - uuid_binary_str - the destination for the created uuid (as a hex string) + uuid_binary_str - the destination for the created uuid (as a hex string) - This will create a 16-byte universal unique identifier for any atom name in the AtomicParsley namespace. - 1. Make a namespace for all uuids to derive from (here its AP.sf.net) - 2. Make a sha1 hash of the namespace string; use that hash as the basis for a v5 uuid into a NULL/blank uuid to create an AP namespace uuid - 3. Make a sha2 hash of the atom_name string; use that hash as the basis for a v5 uuid into an AtomicParsley namespace uuid to create the final uuid. - 4. copy the uuid structure into a binary string representation + This will create a 16-byte universal unique identifier for any +atom name in the AtomicParsley namespace. + 1. Make a namespace for all uuids to derive from (here its +AP.sf.net) + 2. Make a sha1 hash of the namespace string; use that hash as +the basis for a v5 uuid into a NULL/blank uuid to create an AP namespace uuid + 3. Make a sha2 hash of the atom_name string; use that hash as +the basis for a v5 uuid into an AtomicParsley namespace uuid to create the final +uuid. + 4. copy the uuid structure into a binary string representation ----------------------*/ -void APar_generate_uuid_from_atomname(char* atom_name, char* uuid_binary_str) { - - ap_uuid_t blank_namespace = { 0x00000000, 0x0000, 0x0000, - 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; - ap_uuid_t APar_namespace_uuid; - ap_uuid_t AP_atom_uuid; - - AP_Create_UUID_ver5_sha1_name(&APar_namespace_uuid, blank_namespace, "AtomicParsley.sf.net", 20); - AP_Create_UUID_ver5_sha1_name(&AP_atom_uuid, APar_namespace_uuid, atom_name, 4); - - memset(uuid_binary_str, 0, 20); - memcpy(uuid_binary_str, &AP_atom_uuid, sizeof(AP_atom_uuid) ); - - return; -} - -void APar_generate_random_uuid(char* uuid_binary_str) { - ap_uuid_t rand_uuid = { 0x00000000, 0x0000, 0x0000, - 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; - AP_Create_UUID_ver3_random(&rand_uuid); - memcpy(uuid_binary_str, &rand_uuid, sizeof(rand_uuid) ); - return; +void APar_generate_uuid_from_atomname(char *atom_name, char *uuid_binary_str) { + + ap_uuid_t blank_namespace = {0x00000000, + 0x0000, + 0x0000, + 0x00, + 0x00, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + ap_uuid_t APar_namespace_uuid; + ap_uuid_t AP_atom_uuid; + + AP_Create_UUID_ver5_sha1_name( + &APar_namespace_uuid, blank_namespace, "AtomicParsley.sf.net", 20); + AP_Create_UUID_ver5_sha1_name( + &AP_atom_uuid, APar_namespace_uuid, atom_name, 4); + + memset(uuid_binary_str, 0, 20); + memcpy(uuid_binary_str, &AP_atom_uuid, sizeof(AP_atom_uuid)); + + return; +} + +void APar_generate_random_uuid(char *uuid_binary_str) { + ap_uuid_t rand_uuid = {0x00000000, + 0x0000, + 0x0000, + 0x00, + 0x00, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + AP_Create_UUID_ver3_random(&rand_uuid); + memcpy(uuid_binary_str, &rand_uuid, sizeof(rand_uuid)); + return; } void APar_generate_test_uuid() { - ap_uuid_t blank_ns = { 0x00000000, 0x0000, 0x0000, - 0x00, 0x00, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; - ap_uuid_t APar_ns_uuid; - ap_uuid_t APar_test_uuid; - - AP_Create_UUID_ver5_sha1_name(&APar_ns_uuid, blank_ns, "AtomicParsley.sf.net", 20); - APar_print_uuid(&APar_ns_uuid); //should be aa80eaf3-1f72-5575-9faa-de9388dc2a90 - fprintf(stdout, "uuid for 'cprt' in AP namespace: "); - AP_Create_UUID_ver5_sha1_name(&APar_test_uuid, APar_ns_uuid, "cprt", 4); - APar_print_uuid(&APar_test_uuid); //'cprt' should be 4bd39a57-e2c8-5655-a4fb-7a19620ef151 - //uuid_t domain_ns_uuid = { 0x6ba7b810, 0x9dad, 0x11d1, - // 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; // 6ba7b810-9dad-11d1-80b4-00c04fd430c8 a blank representation of a blank domain - return; + ap_uuid_t blank_ns = {0x00000000, + 0x0000, + 0x0000, + 0x00, + 0x00, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + ap_uuid_t APar_ns_uuid; + ap_uuid_t APar_test_uuid; + + AP_Create_UUID_ver5_sha1_name( + &APar_ns_uuid, blank_ns, "AtomicParsley.sf.net", 20); + APar_print_uuid( + &APar_ns_uuid); // should be aa80eaf3-1f72-5575-9faa-de9388dc2a90 + fprintf(stdout, "uuid for 'cprt' in AP namespace: "); + AP_Create_UUID_ver5_sha1_name(&APar_test_uuid, APar_ns_uuid, "cprt", 4); + APar_print_uuid( + &APar_test_uuid); //'cprt' should be 4bd39a57-e2c8-5655-a4fb-7a19620ef151 + // uuid_t domain_ns_uuid = { 0x6ba7b810, 0x9dad, 0x11d1, + // 0x80, + // 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; // + // 6ba7b810-9dad-11d1-80b4-00c04fd430c8 a blank representation of a blank + // domain + return; } Binary files /tmp/tmppkrqmcyz/hCEk_GJeST/atomicparsley-0.9.6/tests/issue-32.mp4 and /tmp/tmppkrqmcyz/StL_6tnX14/atomicparsley-20210715.151551.e7ad03a/tests/issue-32.mp4 differ diff -Nru atomicparsley-0.9.6/tests/test.sh atomicparsley-20210715.151551.e7ad03a/tests/test.sh --- atomicparsley-0.9.6/tests/test.sh 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/tests/test.sh 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/bash +set -xe +./AtomicParsley ./tests/issue-32.mp4 -T 1 -t + diff -Nru atomicparsley-0.9.6/tools/format-code.sh atomicparsley-20210715.151551.e7ad03a/tools/format-code.sh --- atomicparsley-0.9.6/tools/format-code.sh 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/tools/format-code.sh 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,2 @@ +#!/bin/bash +clang-format -i src/*.cpp src/*.h src/*.mm diff -Nru atomicparsley-0.9.6/tools/tag-release.sh atomicparsley-20210715.151551.e7ad03a/tools/tag-release.sh --- atomicparsley-0.9.6/tools/tag-release.sh 1970-01-01 00:00:00.000000000 +0000 +++ atomicparsley-20210715.151551.e7ad03a/tools/tag-release.sh 2021-07-15 22:15:51.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/bash +TAGNAME=$(git "show" "-s" "--format=%cd.%h" "--date=format:%Y%m%d.%H%M%S") +git tag $TAGNAME