Codebase list atomicparsley / ed25ff0
New upstream version 20210715.151551.e7ad03a Jonas Smedegaard 2 years ago
32 changed file(s) with 23062 addition(s) and 17341 deletion(s). Raw diff Collapse all Expand all
0 cmake_minimum_required(VERSION 3.16)
1 project(AtomicParsley)
2
3 if(NOT CMAKE_BUILD_TYPE)
4 set(CMAKE_BUILD_TYPE Release)
5 endif()
6
7 option(ASAN "whether to enable ASAN" OFF)
8
9 find_program(GIT git)
10 if(GIT)
11 execute_process(
12 COMMAND "${GIT}" "show" "-s" "--format=%H;%cd" "--date=format:%Y%m%d.%H%M%S.0"
13 WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
14 RESULT_VARIABLE git_result
15 OUTPUT_VARIABLE git_data
16 ERROR_VARIABLE git_err
17 OUTPUT_STRIP_TRAILING_WHITESPACE
18 )
19 if(git_result EQUAL 0)
20 list(GET git_data 0 BUILD_INFO)
21 list(GET git_data 1 PACKAGE_VERSION)
22 endif()
23 endif()
24
25 include(CheckSymbolExists)
26 check_symbol_exists(strsep "string.h" HAVE_STRSEP)
27 if(HAVE_STRSEP)
28 add_definitions(-DHAVE_STRSEP)
29 endif()
30 check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO)
31 if(HAVE_FSEEKO)
32 add_definitions(-DHAVE_FSEEKO)
33 endif()
34
35 add_definitions(
36 -DPACKAGE_VERSION="${PACKAGE_VERSION}"
37 -DBUILD_INFO="${BUILD_INFO}"
38 -D_FILE_OFFSET_BITS=64
39 )
40
41 find_package(ZLIB)
42 if(ZLIB_FOUND)
43 include_directories(${ZLIB_INCLUDE_DIRS})
44 add_definitions(-DHAVE_ZLIB_H)
45 endif()
46
47 list(APPEND sources
48 src/CDtoc.cpp
49 src/arrays.cpp
50 src/compress.cpp
51 src/extracts.cpp
52 src/iconv.cpp
53 src/id3v2.cpp
54 src/main.cpp
55 src/metalist.cpp
56 src/parsley.cpp
57 src/sha1.cpp
58 src/util.cpp
59 src/uuid.cpp
60 )
61
62 if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
63 list(APPEND sources
64 src/nsfile.mm
65 src/nsimage.mm
66 )
67 endif()
68
69 if(WIN32)
70 list(APPEND sources
71 src/extras/getopt.c
72 src/extras/getopt1.c
73 )
74 endif()
75
76 add_executable(
77 AtomicParsley
78 ${sources}
79 )
80
81 if(ZLIB_FOUND)
82 target_link_libraries(
83 AtomicParsley
84 ${ZLIB_LIBRARIES}
85 )
86 endif()
87
88 if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
89 target_link_libraries(
90 AtomicParsley
91 "-framework Cocoa"
92 "-framework Foundation"
93 "-framework IOKit"
94 )
95 endif()
96
97 if (ASAN)
98 set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
99 set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
100 endif()
101
102 install(TARGETS AtomicParsley RUNTIME)
+0
-49
Makefile.am less more
0
1 bin_PROGRAMS = AtomicParsley
2 noinst_HEADERS = src/AtomDefs.h src/AtomicParsley.h src/CDtoc.h \
3 src/ap_types.h src/id3v2.h src/id3v2defs.h src/id3v2types.h \
4 src/util.h
5
6 AM_CPPFLAGS = -DHAVE_CONFIG_H
7
8 if DARWIN
9 AM_CPPFLAGS += -DDARWIN_PLATFORM
10 AM_OBJCFLAGS = -DDARWIN_PLATFORM $(UNIVERSAL_FLAGS)
11 AM_CFLAGS = -DDARWIN_PLATFORM $(UNIVERSAL_FLAGS)
12 AM_CXXFLAGS = -DDARWIN_PLATFORM $(UNIVERSAL_FLAGS)
13 AM_LDFLAGS = $(UNIVERSAL_FLAGS) \
14 -framework Cocoa -framework Foundation -framework IOKit
15 endif
16
17 AtomicParsley_SOURCES = \
18 src/util.cpp \
19 src/arrays.cpp \
20 src/iconv.cpp \
21 src/parsley.cpp \
22 src/extracts.cpp \
23 src/sha1.cpp \
24 src/uuid.cpp \
25 src/id3v2.cpp \
26 src/metalist.cpp \
27 src/CDtoc.cpp \
28 src/compress.cpp \
29 src/main.cpp
30
31 # If you see a warning about this rule overriding an automake provided rule,
32 # that's fine; we do this because we want to build on versions of autoconf
33 # and automake prior to 1.12.1
34 .mm.o:
35 $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCFLAGS) $(OBJCFLAGS) -c -o $@ $<
36
37 if DARWIN
38 AtomicParsley_SOURCES += \
39 src/nsfile.mm \
40 src/nsimage.mm
41 endif
42
43 if NEED_GETOPT
44 AtomicParsley_SOURCES += \
45 src/extras/getopt.c \
46 src/extras/getopt1.c
47 endif
48
00 # AtomicParsley
11
2 ## Basic Instructions
2 ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wez/atomicparsley/CI)
33
4 If you are building from source you will need autoconf & automake (you will
5 definitely need make):
4 AtomicParsley is a lightweight command line program for reading, parsing and
5 setting metadata into MPEG-4 files, in particular, iTunes-style metadata.
66
7 % ./autogen.sh
8 % ./configure
9 % make
7 ## Installation
108
11 Use the program in situ or place it somewhere in your $PATH by using:
9 ### macOS
1210
13 % sudo make install
11 * Navigate to the [latest release](https://github.com/wez/atomicparsley/releases/latest)
12 * Download the `AtomicParsleyMacOS.zip` file and extract `AtomicParsley`
1413
14 ### Windows
15
16 * Navigate to the [latest release](https://github.com/wez/atomicparsley/releases/latest)
17 * Download the `AtomicParsleyWindows.zip` file and extract `AtomicParsley.exe`
18
19 ### Linux (x86-64)
20
21 * Navigate to the [latest release](https://github.com/wez/atomicparsley/releases/latest)
22 * Download the `AtomicParsleyLinux.zip` file and extract `AtomicParsley`
23
24 ### Alpine Linux (x86-64 musl libc)
25
26 * Navigate to the [latest release](https://github.com/wez/atomicparsley/releases/latest)
27 * Download the `AtomicParsleyAlpine.zip` file and extract `AtomicParsley`
28 * And finally `apk add libstdc++`
29
30 ### Building from Source
31
32 If you are building from source you will need `cmake` and `make`.
33 On Windows systems you'll need Visual Studio or MingW.
34
35 ```
36 cmake .
37 cmake --build . --config Release
38 ```
39
40 will generate an `AtomicParsley` executable.
1541
1642 ### Dependencies:
1743
1844 zlib - used to compress ID3 frames & expand already compressed frames
1945 available from http://www.zlib.net
2046
21 ## For Mac OS X users:
2247
23 The default is to build a universal binary.
48 ## A note on maintenance!
2449
25 switching between a universal build and a platform dependent build should be
26 accompanied by a "make maintainter-clean" and a ./configure between builds.
50 > I made some fixes to the original project on sourceforge back in 2009 and
51 > became the de-facto fork of AtomicParsley as a result. However, I haven't
52 > used this tool myself in many years and have acted in a very loose guiding
53 > role since then.
54 >
55 > In 2020 Bitbucket decided to cease hosting Mercurial based repositories
56 > which meant that I had to move it in order to keep it alive, so you'll
57 > see a flurry of recent activity.
58 >
59 > I'll consider merging pull requests if they are easy to review, but because
60 > I don't use this tool myself I have no way to verify complex changes.
61 > If you'd like to make such a change, please consider contributing some
62 > kind of basic automated test with a corresponding small test file.
63 >
64 > This repo has GitHub Actions enabled for the three major platforms
65 > so bootstrapping some test coverage is feasible.
66 >
67 > You are welcome to report issues using the issue tracker, but I (@wez)
68 > am unlikely to act upon them.
2769
28 To *not* build a Mac OS X universal binary:
29
30 % make maintainer-clean
31 % ./configure --disable-universal
32 % make
33
34
35 ## For Windows users:
36 AtomicParsley builds under cygwin and/or mingw using the same procedure as above.
37
38 Foosatraz built in Windows 8 using MinGW.
39
40 MinGW-get version 0.6.2-beta-20131004-1
41
42 mingw-libz version 1.2.8-1 from MinGW Installation Manager
43
44 % ./autogen.sh
45 % ./configure --prefix=/mingw
46 % make LDFLAGS=-static
47 % strip AtomicParsley.exe
48
49 Full details [pdf](https://bitbucket.org/Foosatraz/wez-atomicparsley-foosatraz-fork/downloads/AtomicParsleyMinGWBuildNotebook.pdf)
50
51 To build with MSVC, you will need to create your own project file; look
52 at the list of source files in Makefile.am; you need to add all of the
53 source files *except* the .mm files. You will also need to provide your
54 own zlib.
55
56 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:
57 [Windows Downloads](https://bitbucket.org/jonhedgerows/atomicparsley/downloads)
58
+0
-6
autogen.sh less more
0 #!/bin/sh
1 # vim:ts=2:sw=2:et:
2 aclocal
3 autoheader
4 automake --add-missing --foreign
5 autoconf
+0
-193
configure.ac less more
0 dnl vim:ts=2:sw=2:et:
1 dnl Process this file with autoconf to produce a configure script.
2
3 dnl Some intro checks and defines
4
5 AC_PREREQ(2.50)
6
7 AC_INIT([atomicparsley],[0.9.6],
8 [http://bitbucket.org/wez/atomicparsley/issues/new/])
9
10 dnl Need to disable dependency tracking option so that the universal
11 dnl build option works for OSX
12 AM_INIT_AUTOMAKE([dist-bzip2 foreign no-dependencies subdir-objects -Wall])
13
14 AC_CANONICAL_HOST
15
16 AC_ARG_ENABLE(universal, dnl
17 [ --enable-universal build a universal binary on Mac OS X [default=no]],
18 universal=$enableval, universal=no)
19
20 case $host in
21 *darwin*)
22
23 AP_NATIVE_ARCH=`arch`
24 AP_64_ARCH="x86_64"
25 case $AP_NATIVE_ARCH in
26 ppc*)
27 AP_CROSS_ARCH="i386"
28 ;;
29 *)
30 AP_CROSS_ARCH="ppc"
31 ;;
32 esac
33 AC_SUBST(AP_CROSS_ARCH)
34 AC_SUBST(AP_NATIVE_ARCH)
35 AC_SUBST(AP_64_ARCH)
36
37 HAVE_DARWIN_PLATFORM="true"
38 AC_SUBST(HAVE_DARWIN_PLATFORM)
39
40 AC_MSG_CHECKING([if we are os/x universal])
41 if test "$universal" = "yes" ; then
42 AC_MSG_RESULT([building for $AP_NATIVE_ARCH, $AP_CROSS_ARCH, and $AP_64_ARCH])
43 UNIVERSAL_FLAGS="-arch $AP_NATIVE_ARCH -arch $AP_CROSS_ARCH -arch $AP_64_ARCH"
44 AC_SUBST(UNIVERSAL_FLAGS)
45 fi
46 AC_MSG_RESULT([no])
47 ;;
48 *)
49 universal="no"
50 HAVE_DARWIN_PLATFORM="false"
51 ;;
52 esac
53
54 AC_SUBST(universal)
55
56 AC_CONFIG_SRCDIR(src/parsley.cpp)
57 AC_CONFIG_HEADER(src/config.h)
58
59 AM_PROG_CC_C_O
60 AC_PROG_CXX
61 if test "$GCC" = "yes" ; then
62 CFLAGS="$CFLAGS -Wall"
63 CXXFLAGS="$CXXFLAGS -Wall"
64 fi
65 AC_PROG_OBJC
66 # Thanks automake; we MUST invoke AC_PROG_OBJCXX even though
67 # we roll our own objc++ build support, because automake 1.12.1
68 # throws a fatal error when it finds our .mm sources.
69 # We do this because there is no good reason to bump the minimum
70 # autoconf and automake dependencies.
71 m4_ifdef([AC_PROG_OBJCXX], [
72 AC_PROG_OBJCXX
73 ])
74 AC_C_BIGENDIAN
75
76 AC_HEADER_DIRENT
77 AC_HEADER_STDC
78 AC_HEADER_SYS_WAIT
79 AC_CHECK_HEADERS([\
80 fcntl.h \
81 inttypes.h \
82 io.h \
83 getopt.h \
84 linux/cdrom.h \
85 math.h \
86 signal.h \
87 stddef.h \
88 stdint.h \
89 stdio.h \
90 stdlib.h \
91 string.h \
92 sys/ioctl.h \
93 sys/param.h \
94 sys/mount.h \
95 sys/stat.h \
96 sys/types.h \
97 sys/time.h \
98 time.h \
99 errno.h \
100 zlib.h \
101 linux/cdrom.h \
102 unistd.h \
103 wchar.h \
104 windows.h \
105 ])
106
107 AC_CHECK_HEADERS(getopt.h, [HAVE_GETOPT_H=1; AC_SUBST(HAVE_GETOPT_H)])
108
109 AC_C_CONST
110 AC_TYPE_UINT8_T
111 AC_TYPE_UINT16_T
112 AC_TYPE_UINT32_T
113 AC_TYPE_UINT64_T
114 AC_TYPE_UINTMAX_T
115 AC_TYPE_UINTPTR_T
116 AC_TYPE_SIZE_T
117 AC_TYPE_SSIZE_T
118 AC_TYPE_INT16_T
119
120 AC_CHECK_FUNCS([\
121 fseeko \
122 fsetpos \
123 lroundf \
124 memset \
125 memcmp \
126 remove \
127 rename \
128 sranddev \
129 sscanf \
130 strdup \
131 strerror \
132 strftime \
133 strncmp \
134 strncasecmp \
135 strrchr \
136 strsep \
137 strstr \
138 strtol \
139 wmemset \
140 ])
141
142 AC_FUNC_MALLOC
143
144 AC_CHECK_LIB(z, deflateEnd, [
145 HAVE_LIBZ=1
146 AC_SUBST(HAVE_LIBZ)
147 LIBS="$LIBS -lz"
148 ],[
149 AC_MSG_ERROR([zlib is required])
150 ]
151 )
152
153 CFLAGS="-D_FILE_OFFSET_BITS=64 $CFLAGS"
154 CXXFLAGS="-D_FILE_OFFSET_BITS=64 $CXXFLAGS"
155
156 AC_ARG_ENABLE(debug, dnl
157 [ --disable-debug do not build a debug version [default=yes]],
158 debug=$enableval, debug=no)
159 if test "$debug" = "yes" ; then
160 AC_DEFINE_UNQUOTED(DEBUG, $debug, [build binary with debug output])
161 AC_SUBST(debug)
162 fi
163
164 AM_CONDITIONAL([NEED_GETOPT], [test x$HAVE_GETOPT_H = x])
165 AM_CONDITIONAL([DARWIN], [test x$HAVE_DARWIN_PLATFORM = xtrue])
166 AM_CONDITIONAL([UNIVERSAL], [test x$universal = xyes])
167
168 AC_OUTPUT([Makefile])
169
170 echo "+----------------------------------------------+"
171 echo "| SUCCESS |"
172 echo "+----------------------------------------------+"
173 echo " AtomicParsley has been configured, you should"
174 echo " now type 'make' to compile AtomicParsley."
175 echo
176 echo "+----------------------------------------------+"
177 echo "| YOUR CONFIGURATION |"
178 echo "+----------------------------------------------+"
179
180 echo " Version: $PACKAGE_VERSION"
181
182 if test "$universal" = "yes" ; then
183 echo " universal build: enabled"
184 fi
185
186 if test "$debug" = "no" ; then
187 echo " debug build: disabled"
188 else
189 echo " debug build: enabled"
190 fi
191
192 echo
0 ---
1 BasedOnStyle: LLVM
2 IndentWidth: 2
3 BinPackArguments: false
4 BinPackParameters: false
5 IncludeIsMainRegex: AtomicParse
6 ---
7 Language: Cpp
8 ---
11 /*
22 AtomicParsley - AtomDefs.h
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
1717 Copyright ©2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
19 */
19 */
2020 //==================================================================//
2121
2222 #include "AtomicParsley.h"
2323
2424 atomDefinition KnownAtoms[] = {
25 //name parent atom(s) container number box_type
26 {"<()>", {"_ANY_LEVEL"}, UNKNOWN_ATOM_TYPE, UKNOWN_REQUIREMENTS, UNKNOWN_ATOM }, //our unknown atom (self-defined)
27
28 {"ftyp", {"FILE_LEVEL"}, CHILD_ATOM, REQUIRED_ONCE, SIMPLE_ATOM },
29
30 {"moov", {"FILE_LEVEL"}, PARENT_ATOM, REQUIRED_ONCE, SIMPLE_ATOM },
31
32 {"mdat", {"FILE_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM },
33
34 {"pdin", {"FILE_LEVEL"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM },
35
36 {"moof", {"FILE_LEVEL"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM },
37 {"mfhd", {"moof"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
38 {"traf", {"moof"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
39 {"tfhd", {"traf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
40 {"trun", {"traf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
41
42 {"mfra", {"FILE_LEVEL"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM },
43 {"tfra", {"mfra"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
44 {"mfro", {"mfra"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
45
46 {"free", {"_ANY_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM },
47 {"skip", {"_ANY_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM },
48
49 {"uuid", {"_ANY_LEVEL"}, CHILD_ATOM, REQUIRED_ONCE, EXTENDED_ATOM },
50
51 {"mvhd", {"moov"}, CHILD_ATOM, REQUIRED_ONCE, VERSIONED_ATOM },
52 {"iods", {"moov"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM },
53 {"drm ", {"moov"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, // 3gp/MobileMP4
54 {"trak", {"moov"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM },
55
56 {"tkhd", {"trak"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM },
57 {"tref", {"trak"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM },
58 {"mdia", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
59
60 {"tapt", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
61 {"clef", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
62 {"prof", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
63 {"enof", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
64
65 {"mdhd", {"mdia"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
66 {"minf", {"mdia"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
67
68 {"hdlr", {"mdia", "meta", "minf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM }, //minf parent present in chapterized
69
70 {"vmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
71 {"smhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
72 {"hmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
73 {"nmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
74 {"gmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, //present in chapterized
75
76 {"dinf", {"minf", "meta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //required in minf
77
78 {"dref", {"dinf"}, DUAL_STATE_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
79
80 {"url ", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM },
81 {"urn ", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM },
82 {"alis", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM },
83 {"cios", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM },
84
85 {"stbl", {"minf"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
86 {"stts", {"stbl"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
87 {"ctts", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
88 {"stsd", {"stbl"}, DUAL_STATE_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
89
90 {"stsz", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
91 {"stz2", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
92
93 {"stsc", {"stbl"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
94
95 {"stco", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
96 {"co64", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
97
98 {"stss", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
99 {"stsh", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
100 {"stdp", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
101 {"padb", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
102 {"sdtp", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
103 {"sbgp", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM },
104 {"sbgp", {"stbl"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM },
105 {"stps", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
106
107 {"edts", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
108 {"elst", {"edts"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
109
110 {"udta", {"moov", "trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
111
112 {"meta", {"FILE_LEVEL", "moov", "trak", "udta"}, DUAL_STATE_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //optionally contains info
113
114 {"mvex", {"moov"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM },
115 {"mehd", {"mvex"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM },
116 {"trex", {"mvex"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
117
118 //{"stsl", {"????"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //contained by a sample entry box
119 {"subs", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
120
121 {"xml ", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
122 {"bxml", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
123 {"iloc", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
124 {"pitm", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
125 {"ipro", {"meta"}, PARENT_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
126 {"iinf", {"meta"}, DUAL_STATE_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
127 {"infe", {"iinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
128
129 {"sinf", {"ipro", "drms", "drmi"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, //parent atom is also "Protected Sample Entry"
130 {"frma", {"sinf"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
131 {"imif", {"sinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
132 {"schm", {"sinf", "srpp"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
133 {"schi", {"sinf", "srpp"}, DUAL_STATE_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
134 {"skcr", {"sinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
135
136 {"user", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
137 {"key ", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //could be required in 'drms'/'drmi'
138 {"iviv", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
139 {"righ", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
140 {"name", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
141 {"priv", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
142
143 {"iKMS", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, // 'iAEC', '264b', 'iOMA', 'ICSD'
144 {"iSFM", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
145 {"iSLT", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //boxes with 'k***' are also here; reserved
146 {"IKEY", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
147 {"hint", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
148 {"dpnd", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
149 {"ipir", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
150 {"mpod", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
151 {"sync", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
152 {"chap", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //?possible versioned?
153
154 {"ipmc", {"moov", "meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM },
155
156 {"tims", {"rtp "}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
157 {"tsro", {"rtp "}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
158 {"snro", {"rtp "}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
159
160 {"srpp", {"srtp"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
161
162 {"hnti", {"udta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
163 {"rtp ", {"hnti"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //'rtp ' is defined twice in different containers
164 {"sdp ", {"hnti"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
165
166 {"hinf", {"udta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
167 {"name", {"udta"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
168 {"trpy", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
169 {"nump", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
170 {"tpyl", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
171 {"totl", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
172 {"npck", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
173 {"maxr", {"hinf"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM },
174 {"dmed", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
175 {"dimm", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
176 {"drep", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
177 {"tmin", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
178 {"tmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
179 {"pmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
180 {"dmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
181 {"payt", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
182 {"tpay", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
183
184 {"drms", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
185 {"drmi", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
186 {"alac", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
187 {"mp4a", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
188 {"mp4s", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
189 {"mp4v", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
190 {"avc1", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
191 {"avcp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
192 {"text", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
193 {"jpeg", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
194 {"tx3g", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
195 {"rtp ", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, //"rtp " occurs twice; disparate meanings
196 {"srtp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, SIMPLE_ATOM },
197 {"enca", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
198 {"encv", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
199 {"enct", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
200 {"encs", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
201 {"samr", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
202 {"sawb", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
203 {"sawp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
204 {"s263", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
205 {"sevc", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
206 {"sqcp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
207 {"ssmv", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
208 {"tmcd", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM },
209 {"mjp2", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM }, //mjpeg2000
210
211 {"alac", {"alac"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
212 {"avcC", {"avc1", "drmi"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
213 {"damr", {"samr", "sawb"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
214 {"d263", {"s263"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
215 {"dawp", {"sawp"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
216 {"devc", {"sevc"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
217 {"dqcp", {"sqcp"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
218 {"dsmv", {"ssmv"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
219 {"bitr", {"d263"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM },
220 {"btrt", {"avc1"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //found in NeroAVC
221 {"m4ds", {"avc1"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //?possible versioned?
222 {"ftab", {"tx3g"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM },
223 {"jp2h", {"mjp2"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000
224
225 {"ihdr", {"jp2h"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000
226 {"colr", {"jp2h"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //mjpeg2000
227 {"fiel", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000
228 {"jp2p", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM }, //mjpeg2000
229 {"jsub", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000
230 {"orfo", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //mjpeg2000
231
232 {"cprt", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, //the only ISO defined metadata tag; also a 3gp asset
233 {"titl", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, //3gp assets
234 {"auth", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
235 {"perf", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
236 {"gnre", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
237 {"dscp", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
238 {"albm", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
239 {"yrrc", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM },
240 {"rtng", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
241 {"clsf", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
242 {"kywd", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
243 {"loci", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM },
244
245 {"ID32", {"meta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM }, //id3v2 tag
246 {"tsel", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, //but only at track level in a 3gp file
247
248 //{"chpl", {"udta"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, //Nero - seems to be versioned
249 //{"ndrm", {"udta"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM }, //Nero - seems to be versioned
250 //{"tags", {"udta"}, CHILD_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM }, //Another Nero-Creationª
251 // ...so if they claim that "tags doesn't have any children",
252 // why does nerotags.exe say "tshd atom"? If 'tags' doesn't
253 // have any children, then tshd can't be an atom....
254 // Clearly, they are EternallyRightª and everyone else is
255 // always wrong.
256
257 //Pish! Seems that Nero is simply unable to register any atoms.
258
259 {"ilst", {"meta"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM }, //iTunes metadata container
260 {"----", {"ilst"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM }, //reverse dns metadata
261 {"mean", {"----"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
262 {"name", {"----"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM },
263
264 {".><.", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM }, //support any future named child to dref; keep 4th from end; manual return
265
266 {"esds", {"SAMPLE_DESC"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM }, //multiple parents; keep 3rd from end; manual return
267
268 {"(..)", {"ilst"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM }, //multiple parents; keep 2nd from end; manual return
269 {"data", {"ITUNES_METADATA"}, CHILD_ATOM, PARENT_SPECIFIC, VERSIONED_ATOM } //multiple parents
270
25 // name parent atom(s) container
26 // number
27 // box_type
28 {"<()>",
29 {"_ANY_LEVEL"},
30 UNKNOWN_ATOM_TYPE,
31 UKNOWN_REQUIREMENTS,
32 UNKNOWN_ATOM}, // our unknown atom (self-defined)
33
34 {"ftyp", {"FILE_LEVEL"}, CHILD_ATOM, REQUIRED_ONCE, SIMPLE_ATOM},
35
36 {"moov", {"FILE_LEVEL"}, PARENT_ATOM, REQUIRED_ONCE, SIMPLE_ATOM},
37
38 {"mdat", {"FILE_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM},
39
40 {"pdin", {"FILE_LEVEL"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM},
41
42 {"moof", {"FILE_LEVEL"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM},
43 {"mfhd", {"moof"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
44 {"traf", {"moof"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
45 {"tfhd", {"traf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
46 {"trun", {"traf"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
47
48 {"mfra", {"FILE_LEVEL"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM},
49 {"tfra", {"mfra"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
50 {"mfro", {"mfra"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
51
52 {"free", {"_ANY_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM},
53 {"skip", {"_ANY_LEVEL"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM},
54
55 {"uuid", {"_ANY_LEVEL"}, CHILD_ATOM, REQUIRED_ONCE, EXTENDED_ATOM},
56
57 {"mvhd", {"moov"}, CHILD_ATOM, REQUIRED_ONCE, VERSIONED_ATOM},
58 {"iods", {"moov"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM},
59 {"drm ",
60 {"moov"},
61 CHILD_ATOM,
62 OPTIONAL_ONCE,
63 VERSIONED_ATOM}, // 3gp/MobileMP4
64 {"trak", {"moov"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM},
65
66 {"tkhd", {"trak"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM},
67 {"tref", {"trak"}, PARENT_ATOM, OPTIONAL_MANY, SIMPLE_ATOM},
68 {"mdia", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
69
70 {"tapt", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
71 {"clef", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
72 {"prof", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
73 {"enof", {"tapt"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
74
75 {"mdhd", {"mdia"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
76 {"minf", {"mdia"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
77
78 {"hdlr",
79 {"mdia", "meta", "minf"},
80 CHILD_ATOM,
81 REQUIRED_ONE,
82 VERSIONED_ATOM}, // minf parent present in chapterized
83
84 {"vmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
85 {"smhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
86 {"hmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
87 {"nmhd", {"minf"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
88 {"gmhd",
89 {"minf"},
90 CHILD_ATOM,
91 REQ_FAMILIAL_ONE,
92 VERSIONED_ATOM}, // present in chapterized
93
94 {"dinf",
95 {"minf", "meta"},
96 PARENT_ATOM,
97 OPTIONAL_ONE,
98 SIMPLE_ATOM}, // required in minf
99
100 {"dref", {"dinf"}, DUAL_STATE_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
101
102 {"url ", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM},
103 {"urn ", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM},
104 {"alis", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM},
105 {"cios", {"dref"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM},
106
107 {"stbl", {"minf"}, PARENT_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
108 {"stts", {"stbl"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
109 {"ctts", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
110 {"stsd", {"stbl"}, DUAL_STATE_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
111
112 {"stsz", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
113 {"stz2", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
114
115 {"stsc", {"stbl"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
116
117 {"stco", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
118 {"co64", {"stbl"}, CHILD_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
119
120 {"stss", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
121 {"stsh", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
122 {"stdp", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
123 {"padb", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
124 {"sdtp", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
125 {"sbgp", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM},
126 {"sbgp", {"stbl"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM},
127 {"stps", {"stbl"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
128
129 {"edts", {"trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
130 {"elst", {"edts"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
131
132 {"udta", {"moov", "trak"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
133
134 {"meta",
135 {"FILE_LEVEL", "moov", "trak", "udta"},
136 DUAL_STATE_ATOM,
137 OPTIONAL_ONE,
138 VERSIONED_ATOM}, // optionally contains info
139
140 {"mvex", {"moov"}, PARENT_ATOM, OPTIONAL_ONCE, SIMPLE_ATOM},
141 {"mehd", {"mvex"}, CHILD_ATOM, OPTIONAL_ONCE, VERSIONED_ATOM},
142 {"trex", {"mvex"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
143
144 //{"stsl", {"????"}, CHILD_ATOM,
145 // OPTIONAL_ONE,
146 // VERSIONED_ATOM }, //contained by a sample
147 // entry
148 // box
149 {"subs", {"stbl", "traf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
150
151 {"xml ", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
152 {"bxml", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
153 {"iloc", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
154 {"pitm", {"meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
155 {"ipro", {"meta"}, PARENT_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
156 {"iinf", {"meta"}, DUAL_STATE_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
157 {"infe", {"iinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
158
159 {"sinf",
160 {"ipro", "drms", "drmi"},
161 PARENT_ATOM,
162 REQUIRED_ONE,
163 SIMPLE_ATOM}, // parent atom is also "Protected Sample Entry"
164 {"frma", {"sinf"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
165 {"imif", {"sinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
166 {"schm", {"sinf", "srpp"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
167 {"schi", {"sinf", "srpp"}, DUAL_STATE_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
168 {"skcr", {"sinf"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
169
170 {"user", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
171 {"key ",
172 {"schi"},
173 CHILD_ATOM,
174 OPTIONAL_ONE,
175 VERSIONED_ATOM}, // could be required in 'drms'/'drmi'
176 {"iviv", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
177 {"righ", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
178 {"name", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
179 {"priv", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
180
181 {"iKMS",
182 {"schi"},
183 CHILD_ATOM,
184 OPTIONAL_ONE,
185 VERSIONED_ATOM}, // 'iAEC', '264b', 'iOMA', 'ICSD'
186 {"iSFM", {"schi"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
187 {"iSLT",
188 {"schi"},
189 CHILD_ATOM,
190 OPTIONAL_ONE,
191 SIMPLE_ATOM}, // boxes with 'k***' are also here; reserved
192 {"IKEY", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
193 {"hint", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
194 {"dpnd", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
195 {"ipir", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
196 {"mpod", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
197 {"sync", {"tref"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
198 {"chap",
199 {"tref"},
200 CHILD_ATOM,
201 OPTIONAL_ONE,
202 SIMPLE_ATOM}, //?possible versioned?
203
204 {"ipmc", {"moov", "meta"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM},
205
206 {"tims", {"rtp "}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
207 {"tsro", {"rtp "}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
208 {"snro", {"rtp "}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
209
210 {"srpp", {"srtp"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
211
212 {"hnti", {"udta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
213 {"rtp ",
214 {"hnti"},
215 CHILD_ATOM,
216 OPTIONAL_ONE,
217 SIMPLE_ATOM}, //'rtp ' is defined twice in different containers
218 {"sdp ", {"hnti"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
219
220 {"hinf", {"udta"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
221 {"name", {"udta"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
222 {"trpy", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
223 {"nump", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
224 {"tpyl", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
225 {"totl", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
226 {"npck", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
227 {"maxr", {"hinf"}, CHILD_ATOM, OPTIONAL_MANY, SIMPLE_ATOM},
228 {"dmed", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
229 {"dimm", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
230 {"drep", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
231 {"tmin", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
232 {"tmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
233 {"pmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
234 {"dmax", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
235 {"payt", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
236 {"tpay", {"hinf"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
237
238 {"drms", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
239 {"drmi", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
240 {"alac", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
241 {"mp4a", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
242 {"mp4s", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
243 {"mp4v", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
244 {"avc1", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
245 {"avcp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
246 {"text", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
247 {"jpeg", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
248 {"tx3g", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
249 {"rtp ",
250 {"stsd"},
251 DUAL_STATE_ATOM,
252 REQ_FAMILIAL_ONE,
253 VERSIONED_ATOM}, //"rtp " occurs twice; disparate meanings
254 {"srtp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, SIMPLE_ATOM},
255 {"enca", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
256 {"encv", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
257 {"enct", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
258 {"encs", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
259 {"samr", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
260 {"sawb", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
261 {"sawp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
262 {"s263", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
263 {"sevc", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
264 {"sqcp", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
265 {"ssmv", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
266 {"tmcd", {"stsd"}, DUAL_STATE_ATOM, REQ_FAMILIAL_ONE, VERSIONED_ATOM},
267 {"mjp2",
268 {"stsd"},
269 DUAL_STATE_ATOM,
270 REQ_FAMILIAL_ONE,
271 VERSIONED_ATOM}, // mjpeg2000
272
273 {"alac", {"alac"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
274 {"avcC", {"avc1", "drmi"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
275 {"damr", {"samr", "sawb"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
276 {"d263", {"s263"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
277 {"dawp", {"sawp"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
278 {"devc", {"sevc"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
279 {"dqcp", {"sqcp"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
280 {"dsmv", {"ssmv"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
281 {"bitr", {"d263"}, CHILD_ATOM, REQUIRED_ONE, SIMPLE_ATOM},
282 {"btrt",
283 {"avc1"},
284 CHILD_ATOM,
285 OPTIONAL_ONE,
286 SIMPLE_ATOM}, // found in NeroAVC
287 {"m4ds",
288 {"avc1"},
289 CHILD_ATOM,
290 OPTIONAL_ONE,
291 SIMPLE_ATOM}, //?possible versioned?
292 {"ftab", {"tx3g"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM},
293 {"jp2h", {"mjp2"}, PARENT_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000
294
295 {"ihdr", {"jp2h"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000
296 {"colr", {"jp2h"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, // mjpeg2000
297 {"fiel", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000
298 {"jp2p", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, VERSIONED_ATOM}, // mjpeg2000
299 {"jsub", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000
300 {"orfo", {"mjp2"}, CHILD_ATOM, OPTIONAL_ONE, SIMPLE_ATOM}, // mjpeg2000
301
302 {"cprt",
303 {"udta"},
304 CHILD_ATOM,
305 OPTIONAL_MANY,
306 PACKED_LANG_ATOM}, // the only ISO defined metadata tag; also a 3gp asset
307 {"titl",
308 {"udta"},
309 CHILD_ATOM,
310 OPTIONAL_MANY,
311 PACKED_LANG_ATOM}, // 3gp assets
312 {"auth", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
313 {"perf", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
314 {"gnre", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
315 {"dscp", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
316 {"albm", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
317 {"yrrc", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, VERSIONED_ATOM},
318 {"rtng", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
319 {"clsf", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
320 {"kywd", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
321 {"loci", {"udta"}, CHILD_ATOM, OPTIONAL_MANY, PACKED_LANG_ATOM},
322
323 {"ID32",
324 {"meta"},
325 CHILD_ATOM,
326 OPTIONAL_MANY,
327 PACKED_LANG_ATOM}, // id3v2 tag
328 {"tsel",
329 {"udta"},
330 CHILD_ATOM,
331 OPTIONAL_MANY,
332 SIMPLE_ATOM}, // but only at track level in a 3gp file
333
334 //{"chpl", {"udta"}, CHILD_ATOM,
335 // OPTIONAL_ONCE,
336 // VERSIONED_ATOM }, //Nero - seems to be versioned
337 //{"ndrm", {"udta"}, CHILD_ATOM,
338 // OPTIONAL_ONCE,
339 // VERSIONED_ATOM }, //Nero - seems to be versioned
340 //{"tags", {"udta"}, CHILD_ATOM,
341 // OPTIONAL_ONCE,
342 // SIMPLE_ATOM }, //Another Nero-Creationª
343 // ...so if they claim that "tags doesn't have any children",
344 // why does nerotags.exe say "tshd atom"? If 'tags' doesn't
345 // have any children, then tshd can't be an atom....
346 // Clearly, they are EternallyRightª and everyone else is
347 // always wrong.
348
349 // Pish! Seems that Nero is simply unable to register any atoms.
350
351 {"ilst",
352 {"meta"},
353 PARENT_ATOM,
354 OPTIONAL_ONCE,
355 SIMPLE_ATOM}, // iTunes metadata container
356 {"----",
357 {"ilst"},
358 PARENT_ATOM,
359 OPTIONAL_MANY,
360 SIMPLE_ATOM}, // reverse dns metadata
361 {"mean", {"----"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
362 {"name", {"----"}, CHILD_ATOM, REQUIRED_ONE, VERSIONED_ATOM},
363
364 {".><.",
365 {"dref"},
366 CHILD_ATOM,
367 OPTIONAL_MANY,
368 VERSIONED_ATOM}, // support any future named child to dref; keep 4th from
369 // end; manual return
370
371 {"esds",
372 {"SAMPLE_DESC"},
373 CHILD_ATOM,
374 REQUIRED_ONE,
375 SIMPLE_ATOM}, // multiple parents; keep 3rd from end; manual return
376
377 {"(..)",
378 {"ilst"},
379 PARENT_ATOM,
380 OPTIONAL_ONE,
381 SIMPLE_ATOM}, // multiple parents; keep 2nd from end; manual return
382 {"data",
383 {"ITUNES_METADATA"},
384 CHILD_ATOM,
385 PARENT_SPECIFIC,
386 VERSIONED_ATOM} // multiple parents
387
271388 };
272
44 /*
55 AtomicParsley - AtomicParsley.h
66
7 AtomicParsley is GPL software; you can freely distribute,
7 AtomicParsley is GPL software; you can freely distribute,
88 redistribute, modify & use under the terms of the GNU General
99 Public License; either version 2 or its successor.
1010
1212 any warranty; without the implied warranty of merchantability
1313 or fitness for either an expressed or implied particular purpose.
1414
15 Please see the included GNU General Public License (GPL) for
15 Please see the included GNU General Public License (GPL) for
1616 your rights and further details; see the file COPYING. If you
1717 cannot, write to the Free Software Foundation, 59 Temple Place
1818 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
3030 #ifndef _UNICODE
3131 #define _UNICODE
3232 #endif
33 #if defined (_MSC_VER)
33 #if defined(_MSC_VER)
3434 #define strncasecmp _strnicmp
3535 #define _CRT_SECURE_NO_WARNINGS
36 #pragma warning(disable: 4244) // int64_t assignments to int32_t etc.
37 #endif
38 #endif
39
40 #include "config.h"
36 #pragma warning(disable : 4244) // int64_t assignments to int32_t etc.
37 #endif
38 #endif
4139
4240 #define __STDC_LIMIT_MACROS
4341 #define __STDC_FORMAT_MACROS
4543
4644 #include <sys/types.h>
4745 #ifdef __GLIBC__
48 # define HAVE_LROUNDF 1
46 #define HAVE_LROUNDF 1
4947 #endif
5048
5149 #include <stdio.h>
50 #include <stdlib.h>
5251 #include <string.h>
53 #include <stdlib.h>
54 #if HAVE_SYS_TIME_H
55 # include <sys/time.h>
56 #endif
52 #ifndef _WIN32
53 #include <sys/time.h>
54 #endif
55 #include <errno.h>
56 #include <math.h>
5757 #include <time.h>
58 #include <math.h>
59 #include <errno.h>
60
61 #if HAVE_STDDEF_H
62 # include <stddef.h>
63 #endif
64 #if HAVE_STDINT_H
65 # include <stdint.h>
66 #endif
67 #if HAVE_INTTYPES_H
68 # include <inttypes.h>
69 #endif
70 #if HAVE_FCNTL_H
71 # include <fcntl.h>
72 #endif
73 #if HAVE_SYS_IOCTL_H
74 # include <sys/ioctl.h>
75 #endif
76 #if HAVE_LINUX_CDROM_H
77 # include <linux/cdrom.h>
78 #endif
79 #if HAVE_SYS_MOUNT_H
80 # include <sys/mount.h>
81 #endif
82 #if HAVE_SYS_PARAM_H
83 # include <sys/param.h>
84 #endif
85 #if HAVE_WINDOWS_H
86 # include <windows.h>
87 #endif
88 #if HAVE_WCHAR_H
89 # include <wchar.h>
90 #endif
91 #if HAVE_SYS_STAT_H
92 # include <sys/stat.h>
93 #endif
94 #if HAVE_UNISTD_H
95 # include <unistd.h>
96 #endif
97 #if HAVE_IO_H
98 # include <io.h>
99 #endif
100 #if HAVE_SIGNAL_H
101 # include <signal.h>
102 #endif
103 #if HAVE_GETOPT_H
104 # include <getopt.h>
58
59 #include <inttypes.h>
60 #include <stddef.h>
61 #include <stdint.h>
62 #ifndef _WIN32
63 #include <fcntl.h>
64 #include <sys/ioctl.h>
65 #endif
66 #ifdef __linux__
67 #include <linux/cdrom.h>
68 #include <sys/mount.h>
69 #include <sys/param.h>
70 #endif
71 #ifdef _WIN32
72 // Don't break std::min!
73 #define NOMINMAX
74 #include <windows.h>
75 #endif
76 #include <wchar.h>
77 #ifndef _WIN32
78 #include <sys/stat.h>
79 #include <unistd.h>
80 #endif
81 #ifdef _WIN32
82 #include <io.h>
83 #endif
84
85 #include <signal.h>
86 #ifndef _WIN32
87 #include <getopt.h>
10588 #else
106 # include "extras/getopt.h"
89 #include "extras/getopt.h"
10790 #endif
10891
10992 #ifndef PRIu64
110 # ifdef _WIN32
111 # define PRIu64 "I64u"
112 # else
113 # define PRIu64 "llu"
114 # endif
93 #ifdef _WIN32
94 #define PRIu64 "I64u"
95 #else
96 #define PRIu64 "llu"
97 #endif
11598 #endif
11699 #ifndef PRIu32
117 # define PRIu32 "u"
100 #define PRIu32 "u"
118101 #endif
119102 #ifndef PRIx32
120 # define PRIx32 "x"
103 #define PRIx32 "x"
121104 #endif
122105 #ifndef SCNu64
123 # ifdef _WIN32
124 # define SCNu64 "I64u"
125 # else
126 # define SCNu64 "llu"
127 # endif
106 #ifdef _WIN32
107 #define SCNu64 "I64u"
108 #else
109 #define SCNu64 "llu"
110 #endif
128111 #endif
129112 #ifndef SCNu32
130 # define SCNu32 "u"
113 #define SCNu32 "u"
131114 #endif
132115 #ifndef SCNu16
133 # define SCNu16 "hu"
134 #endif
135
136 #ifndef MIN
137 //#define MIN(X,Y) ((X) < (Y) ? : (X) : (Y))
138 #define MIN min
139 #endif
140
116 #define SCNu16 "hu"
117 #endif
141118
142119 #ifndef MAXPATHLEN
143 # define MAXPATHLEN 255
120 #define MAXPATHLEN 255
144121 #endif
145122
146123 #include "util.h"
147
148 #define MAX_ATOMS 1024
124 #include <algorithm>
125
126 #define MAX_ATOMS 2048
149127 #define MAXDATA_PAYLOAD 1256
150 #define DEFAULT_PADDING_LENGTH 2048;
128 #define DEFAULT_PADDING_LENGTH 2048;
151129 #define MINIMUM_REQUIRED_PADDING_LENGTH 0;
152130 #define MAXIMUM_REQUIRED_PADDING_LENGTH 5000;
153131
175153
176154 extern AtomicInfo parsedAtoms[];
177155 extern short atom_number;
178 extern char* ISObasemediafile;
179 extern FILE* source_file;
156 extern char *ISObasemediafile;
157 extern FILE *source_file;
180158
181159 extern padding_preferences pad_prefs;
182160
184162
185163 extern uint8_t forced_suffix_type;
186164
187 extern char* twenty_byte_buffer;
165 extern char *twenty_byte_buffer;
188166 extern DynamicUpdateStat dynUpd;
189167
190168 extern ID3FrameDefinition KnownFrames[];
197175
198176 short APar_FindParentAtom(int order_in_tree, uint8_t this_atom_level);
199177
200 AtomicInfo* APar_FindAtomInTrack(uint8_t &total_tracks, uint8_t &track_num,
201 const char* search_atom_str);
202
203 AtomicInfo* APar_FindAtom(const char* atom_name, bool createMissing,
204 uint8_t atom_type, uint16_t atom_lang, bool match_full_uuids = false,
205 const char* reverseDNSdomain = NULL);
206
207 int APar_MatchToKnownAtom(const char* atom_name, const char* atom_container,
208 bool fromFile, const char* find_atom_path);
178 AtomicInfo *APar_FindAtomInTrack(uint8_t &total_tracks,
179 uint8_t &track_num,
180 const char *search_atom_str);
181
182 AtomicInfo *APar_FindAtom(const char *atom_name,
183 bool createMissing,
184 uint8_t atom_type,
185 uint16_t atom_lang,
186 bool match_full_uuids = false,
187 const char *reverseDNSdomain = NULL);
188
189 int APar_MatchToKnownAtom(const char *atom_name,
190 const char *atom_container,
191 bool fromFile,
192 const char *find_atom_path);
209193
210194 void APar_ScanAtoms(const char *path, bool deepscan_REQ = false);
211 void APar_IdentifyBrand(char* file_brand);
212
213 AtomicInfo* APar_CreateSparseAtom(AtomicInfo* surrogate_atom,
214 AtomicInfo* parent_atom, short preceding_atom);
215
216 void APar_Unified_atom_Put(AtomicInfo* target_atom, const char* unicode_data,
217 uint8_t text_tag_style, uint64_t ancillary_data, uint8_t anc_bit_width);
218
219 void APar_atom_Binary_Put(AtomicInfo* target_atom, const char* binary_data,
220 uint32_t bytecount, uint64_t atomic_data_offset);
195 void APar_IdentifyBrand(char *file_brand);
196
197 AtomicInfo *APar_CreateSparseAtom(AtomicInfo *surrogate_atom,
198 AtomicInfo *parent_atom,
199 short preceding_atom);
200
201 void APar_Unified_atom_Put(AtomicInfo *target_atom,
202 const char *unicode_data,
203 uint8_t text_tag_style,
204 uint64_t ancillary_data,
205 uint8_t anc_bit_width);
206
207 void APar_atom_Binary_Put(AtomicInfo *target_atom,
208 const char *binary_data,
209 uint32_t bytecount,
210 uint64_t atomic_data_offset);
221211
222212 /* iTunes-style metadata */
223 void APar_MetaData_atomArtwork_Set(const char* artworkPath,
224 char* env_PicOptions);
225
226 void APar_MetaData_atomGenre_Set(const char* atomPayload);
227 void APar_MetaData_atomLyrics_Set(const char* lyricsPath);
228 void APar_MetaData_atom_QuickInit(short atom_num, const uint32_t atomFlags,
229 uint32_t supplemental_length, uint32_t allotment = MAXDATA_PAYLOAD + 1);
230
231 AtomicInfo* APar_MetaData_atom_Init(const char* atom_path,
232 const char* MD_Payload, const uint32_t atomFlags);
233
234 AtomicInfo* APar_reverseDNS_atom_Init(const char* rDNS_atom_name,
235 const char* rDNS_payload, const uint32_t* atomFlags, const char* rDNS_domain);
213 void APar_MetaData_atomArtwork_Set(const char *artworkPath,
214 char *env_PicOptions);
215
216 void APar_MetaData_atomGenre_Set(const char *atomPayload);
217 void APar_MetaData_atomLyrics_Set(const char *lyricsPath);
218 void APar_MetaData_atom_QuickInit(short atom_num,
219 const uint32_t atomFlags,
220 uint32_t supplemental_length,
221 uint32_t allotment = MAXDATA_PAYLOAD + 1);
222
223 AtomicInfo *APar_MetaData_atom_Init(const char *atom_path,
224 const char *MD_Payload,
225 const uint32_t atomFlags);
226
227 AtomicInfo *APar_reverseDNS_atom_Init(const char *rDNS_atom_name,
228 const char *rDNS_payload,
229 const uint32_t *atomFlags,
230 const char *rDNS_domain);
236231
237232 /* uuid user extension metadata; made to look much like iTunes-style metadata
238233 * with a 4byte NULL */
239 AtomicInfo* APar_uuid_atom_Init(const char* atom_path, const char* uuidName,
240 const uint32_t dataType, const char* uuidValue, bool shellAtom);
241
242 //test whether the ipod uuid can be added for a video track
243 uint16_t APar_TestVideoDescription(AtomicInfo* video_desc_atom,
244 FILE* ISObmff_file);
245
246 void APar_Generate_iPod_uuid(char* atom_path);
234 AtomicInfo *APar_uuid_atom_Init(const char *atom_path,
235 const char *uuidName,
236 const uint32_t dataType,
237 const char *uuidValue,
238 bool shellAtom);
239
240 // test whether the ipod uuid can be added for a video track
241 uint16_t APar_TestVideoDescription(AtomicInfo *video_desc_atom,
242 FILE *ISObmff_file);
243
244 void APar_Generate_iPod_uuid(char *atom_path);
247245
248246 /* 3GP-style metadata */
249 uint32_t APar_3GP_Keyword_atom_Format(char* keywords_globbed,
250 uint8_t keyword_count, bool set_UTF16_text, char* &formed_keyword_struct);
251
252 AtomicInfo* APar_UserData_atom_Init(const char* userdata_atom_name,
253 const char* atom_payload, uint8_t udta_container, uint8_t track_idx,
254 uint16_t userdata_lang);
247 uint32_t APar_3GP_Keyword_atom_Format(char *keywords_globbed,
248 uint8_t keyword_count,
249 bool set_UTF16_text,
250 char *&formed_keyword_struct);
251
252 AtomicInfo *APar_UserData_atom_Init(const char *userdata_atom_name,
253 const char *atom_payload,
254 uint8_t udta_container,
255 uint8_t track_idx,
256 uint16_t userdata_lang);
255257
256258 /* ID3v2 (2.4) style metadata, non-external form */
257 AtomicInfo* APar_ID32_atom_Init(const char* frameID_str, char meta_area,
258 const char* lang_str, uint16_t id32_lang);
259
260 void APar_RemoveAtom(const char* atom_path, uint8_t atom_type,
261 uint16_t UD_lang, const char* rDNS_domain = NULL);
259 AtomicInfo *APar_ID32_atom_Init(const char *frameID_str,
260 char meta_area,
261 const char *lang_str,
262 uint16_t id32_lang);
263
264 void APar_RemoveAtom(const char *atom_path,
265 uint8_t atom_type,
266 uint16_t UD_lang,
267 const char *rDNS_domain = NULL);
262268
263269 void APar_freefree(int purge_level);
264270
265 void APar_MetadataFileDump(const char* ISObasemediafile);
271 void APar_MetadataFileDump(const char *ISObasemediafile);
266272
267273 void APar_Optimize(bool mdat_test_only);
268274 void APar_DetermineAtomLengths();
269 void APar_WriteFile(const char* ISObasemediafile, const char* outfile,
270 bool rewrite_original);
271
272 void APar_zlib_inflate(char* in_buffer, uint32_t in_buf_len,
273 char* out_buffer, uint32_t out_buf_len);
274
275 uint32_t APar_zlib_deflate(char* in_buffer, uint32_t in_buf_len,
276 char* out_buffer, uint32_t out_buf_len);
277
278
279 void APar_print_uuid(ap_uuid_t* uuid, bool new_line = true);
280 void APar_sprintf_uuid(ap_uuid_t* uuid, char* destination);
281 uint8_t APar_uuid_scanf(char* in_formed_uuid, const char* raw_uuid);
282
283 void APar_endian_uuid_bin_str_conversion(char* raw_uuid);
284
285 uint8_t APar_extract_uuid_version(ap_uuid_t* uuid, char* binary_uuid_str);
286 void APar_generate_uuid_from_atomname(char* atom_name, char* uuid_binary_str);
287 void APar_generate_random_uuid(char* uuid_binary_str);
275 void APar_WriteFile(const char *ISObasemediafile,
276 const char *outfile,
277 bool rewrite_original);
278
279 void APar_zlib_inflate(char *in_buffer,
280 uint32_t in_buf_len,
281 char *out_buffer,
282 uint32_t out_buf_len);
283
284 uint32_t APar_zlib_deflate(char *in_buffer,
285 uint32_t in_buf_len,
286 char *out_buffer,
287 uint32_t out_buf_len);
288
289 void APar_print_uuid(ap_uuid_t *uuid, bool new_line = true);
290 void APar_sprintf_uuid(ap_uuid_t *uuid, char *destination);
291 uint8_t APar_uuid_scanf(char *in_formed_uuid, const char *raw_uuid);
292
293 void APar_endian_uuid_bin_str_conversion(char *raw_uuid);
294
295 uint8_t APar_extract_uuid_version(ap_uuid_t *uuid, char *binary_uuid_str);
296 void APar_generate_uuid_from_atomname(char *atom_name, char *uuid_binary_str);
297 void APar_generate_random_uuid(char *uuid_binary_str);
288298
289299 /* Initialize structure containing state of computation. */
290 extern void sha1_init_ctx (struct sha1_ctx *ctx);
300 extern void sha1_init_ctx(struct sha1_ctx *ctx);
291301
292302 /* Starting with the result of former calls of this function (or the
293303 initialization function update the context for the next LEN bytes
294304 starting at BUFFER.
295305 It is necessary that LEN is a multiple of 64!!! */
296 extern void sha1_process_block (const void *buffer, size_t len,
297 struct sha1_ctx *ctx);
306 extern void
307 sha1_process_block(const void *buffer, size_t len, struct sha1_ctx *ctx);
298308
299309 /* Starting with the result of former calls of this function (or the
300310 initialization function update the context for the next LEN bytes
301311 starting at BUFFER.
302312 It is NOT required that LEN is a multiple of 64. */
303 extern void sha1_process_bytes (const void *buffer, size_t len,
304 struct sha1_ctx *ctx);
313 extern void
314 sha1_process_bytes(const void *buffer, size_t len, struct sha1_ctx *ctx);
305315
306316 /* Process the remaining bytes in the buffer and put result from CTX
307317 in first 20 bytes following RESBUF. The result is always in little
310320
311321 IMPORTANT: On some systems it is required that RESBUF be correctly
312322 aligned for a 32 bits value. */
313 extern void *sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf);
314
323 extern void *sha1_finish_ctx(struct sha1_ctx *ctx, void *resbuf);
315324
316325 /* Put result from CTX in first 20 bytes following RESBUF. The result is
317326 always in little endian byte order, so that a byte-wise output yields
319328
320329 IMPORTANT: On some systems it is required that RESBUF is correctly
321330 aligned for a 32 bits value. */
322 extern void *sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf);
323
331 extern void *sha1_read_ctx(const struct sha1_ctx *ctx, void *resbuf);
324332
325333 /* Compute SHA1 message digest for bytes read from STREAM. The
326334 resulting message digest number will be written into the 20 bytes
327335 beginning at RESBLOCK. */
328 extern int sha1_stream (FILE *stream, void *resblock);
336 extern int sha1_stream(FILE *stream, void *resblock);
329337
330338 /* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The
331339 result is always in little endian byte order, so that a byte-wise
332340 output yields to the wanted ASCII representation of the message
333341 digest. */
334 extern void *sha1_buffer (const char *buffer, size_t len, void *resblock);
335
336 int isolat1ToUTF8(unsigned char* out, int outlen,
337 const unsigned char* in, int inlen);
338
339 int UTF8Toisolat1(unsigned char* out, int outlen,
340 const unsigned char* in, int inlen);
341
342 int UTF16BEToUTF8(unsigned char* out, int outlen,
343 const unsigned char* inb, int inlenb);
344
345 int UTF8ToUTF16BE(unsigned char* outb, int outlen,
346 const unsigned char* in, int inlen);
347
348 int UTF16LEToUTF8(unsigned char* out, int outlen,
349 const unsigned char* inb, int inlenb);
350
351 int UTF8ToUTF16LE(unsigned char* outb, int outlen,
352 const unsigned char* in, int inlen);
353
354 int isUTF8(const char* in_string);
342 extern void *sha1_buffer(const char *buffer, size_t len, void *resblock);
343
344 int isolat1ToUTF8(unsigned char *out,
345 int outlen,
346 const unsigned char *in,
347 int inlen);
348
349 int UTF8Toisolat1(unsigned char *out,
350 int outlen,
351 const unsigned char *in,
352 int inlen);
353
354 int UTF16BEToUTF8(unsigned char *out,
355 int outlen,
356 const unsigned char *inb,
357 int inlenb);
358
359 int UTF8ToUTF16BE(unsigned char *outb,
360 int outlen,
361 const unsigned char *in,
362 int inlen);
363
364 int UTF16LEToUTF8(unsigned char *out,
365 int outlen,
366 const unsigned char *inb,
367 int inlenb);
368
369 int UTF8ToUTF16LE(unsigned char *outb,
370 int outlen,
371 const unsigned char *in,
372 int inlen);
373
374 int isUTF8(const char *in_string);
355375
356376 unsigned int utf8_length(const char *in_string, unsigned int char_limit);
357377
358 int strip_bogusUTF16toRawUTF8(unsigned char* out, int inlen,
359 wchar_t* in, int outlen);
360
361 int test_conforming_alpha_string(char* in_string);
362 bool test_limited_ascii(char* in_string, unsigned int str_len);
363
364 void APar_ExtractDetails(FILE* isofile, uint8_t optional_output);
365 void APar_ExtractBrands(char* filepath);
378 int strip_bogusUTF16toRawUTF8(unsigned char *out,
379 int inlen,
380 wchar_t *in,
381 int outlen);
382
383 int test_conforming_alpha_string(char *in_string);
384 bool test_limited_ascii(char *in_string, unsigned int str_len);
385
386 void APar_ExtractDetails(FILE *isofile, uint8_t optional_output);
387 void APar_ExtractBrands(char *filepath);
366388
367389 void printBOM();
368 void APar_fprintf_UTF8_data(const char* utf8_encoded_data);
369 void APar_unicode_win32Printout(wchar_t* unicode_out, char* utf8_out);
370
371 void APar_Extract_uuid_binary_file(AtomicInfo* uuid_atom,
372 const char* originating_file, char* output_path);
373
374 void APar_Print_APuuid_atoms(const char *path, char* output_path,
375 uint8_t target_information);
376
377 void APar_Print_iTunesData(const char *path, char* output_path,
378 uint8_t supplemental_info, uint8_t target_information,
379 AtomicInfo* ilstAtom = NULL);
390 void APar_fprintf_UTF8_data(const char *utf8_encoded_data);
391 void APar_unicode_win32Printout(wchar_t *unicode_out, char *utf8_out);
392
393 void APar_Extract_uuid_binary_file(AtomicInfo *uuid_atom,
394 const char *originating_file,
395 char *output_path);
396
397 void APar_Print_APuuid_atoms(const char *path,
398 char *output_path,
399 uint8_t target_information);
400
401 void APar_Print_iTunesData(const char *path,
402 char *output_path,
403 uint8_t supplemental_info,
404 uint8_t target_information,
405 AtomicInfo *ilstAtom = NULL);
380406
381407 void APar_PrintUserDataAssests(bool quantum_listing = false);
382408
383 void APar_Extract_ID3v2_file(AtomicInfo* id32_atom, const char* frame_str,
384 const char* originfile, const char* destination_folder, AdjunctArgs* id3args);
385
386 void APar_Print_ID3v2_tags(AtomicInfo* id32_atom);
409 void APar_Extract_ID3v2_file(AtomicInfo *id32_atom,
410 const char *frame_str,
411 const char *originfile,
412 const char *destination_folder,
413 AdjunctArgs *id3args);
414
415 void APar_Print_ID3v2_tags(AtomicInfo *id32_atom);
387416
388417 void APar_Print_ISO_UserData_per_track();
389 void APar_Mark_UserData_area(uint8_t track_num, short userdata_atom,
390 bool quantum_listing);
391
392 //trees
418 void APar_Mark_UserData_area(uint8_t track_num,
419 short userdata_atom,
420 bool quantum_listing);
421
422 // trees
393423 void APar_PrintAtomicTree();
394424 void APar_SimpleAtomPrintout();
395425
396 uint32_t APar_4CC_CreatorCode(const char* filepath, uint32_t new_type_code);
426 uint32_t APar_4CC_CreatorCode(const char *filepath, uint32_t new_type_code);
397427 void APar_SupplySelectiveTypeCreatorCodes(const char *inputPath,
398 const char *outputPath, uint8_t forced_type_code);
399
400 bool ResizeGivenImage(const char* filePath, PicPrefs myPicPrefs,
401 char* resized_path);
402
403 char* GenreIntToString(int genre);
404 uint8_t StringGenreToInt(const char* genre_string);
428 const char *outputPath,
429 uint8_t forced_type_code);
430
431 bool ResizeGivenImage(const char *filePath,
432 PicPrefs myPicPrefs,
433 char *resized_path,
434 size_t resized_path_len);
435
436 char *GenreIntToString(int genre);
437 uint8_t StringGenreToInt(const char *genre_string);
405438 void ListGenresValues();
406439
407 stiks* MatchStikString(const char* stik_string);
408 stiks* MatchStikNumber(uint8_t in_stik_num);
440 stiks *MatchStikString(const char *stik_string);
441 stiks *MatchStikNumber(uint8_t in_stik_num);
409442 void ListStikValues();
410443
411 sfIDs* MatchStoreFrontNumber(uint32_t storefrontnum);
412
413 bool MatchLanguageCode(const char* in_code);
444 sfIDs *MatchStoreFrontNumber(uint32_t storefrontnum);
445
446 bool MatchLanguageCode(const char *in_code);
414447 void ListLanguageCodes();
415448
416449 void ListMediaRatings();
417450 void ListTVGenreIDValues();
418451 void ListMovieGenreIDValues();
419 const char* Expand_cli_mediastring(const char* cli_rating);
420
421 char* ID3GenreIntToString(int genre);
422 uint8_t ID3StringGenreToInt(const char* genre_string);
452 const char *Expand_cli_mediastring(const char *cli_rating);
453
454 char *ID3GenreIntToString(int genre);
455 uint8_t ID3StringGenreToInt(const char *genre_string);
423456
424457 #endif /* ATOMIC_PARSLEY_H */
425458
11 /*
22 AtomicParsley - CDtoc.cpp
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2006-2007 puck_lock
17 Copyright (C)2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
1919 */
2020 //==================================================================//
2121
22 //gathering of a CD's Table of Contents is going to be hardware specific
23 //currently only Mac OS X is implemented - using IOKit framework.
24 //another avenue (applicable to other *nix platforms): ioctl
22 // gathering of a CD's Table of Contents is going to be hardware specific
23 // currently only Mac OS X is implemented - using IOKit framework.
24 // another avenue (applicable to other *nix platforms): ioctl
2525
2626 #include "AtomicParsley.h"
2727 #include "CDtoc.h"
2828
29 #if defined (DARWIN_PLATFORM)
29 #if defined(__APPLE__)
3030 #include <CoreFoundation/CoreFoundation.h>
31 #include <IOKit/IOBSD.h>
3132 #include <IOKit/IOKitLib.h>
33 #include <IOKit/storage/IOCDMedia.h>
3234 #include <IOKit/storage/IOCDTypes.h>
33 #include <IOKit/storage/IOCDMedia.h>
34 #include <IOKit/IOBSD.h>
3535
3636 const uint8_t MACOSX_LEADOUT_TRACK = 0xA2;
3737 #endif
38
3938
4039 const uint8_t CDOBJECT_DATACD = 0;
4140 const uint8_t CDOBJECT_AUDIOCD = 1;
4241
4342 struct CD_TDesc {
44 uint8_t session;
45 uint8_t controladdress;
46 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
47 uint8_t tracknumber; //refered to as 'point', but this is actually the tracknumber in mode1 audio toc entries.
48 uint8_t rel_minutes;
49 uint8_t rel_seconds;
50 uint8_t rel_frames;
51 uint8_t zero_space;
52 uint8_t abs_minutes;
53 uint8_t abs_seconds;
54 uint8_t abs_frames;
55 void* next_description;
43 uint8_t session;
44 uint8_t controladdress;
45 uint8_t unused1; // refered to as 'tno' which equates to "track number" -
46 // but... its 'point' that actually is the tracknumber in
47 // mode1 TOC. set to zero for all mode1 TOC
48 uint8_t tracknumber; // refered to as 'point', but this is actually the
49 // tracknumber in mode1 audio toc entries.
50 uint8_t rel_minutes;
51 uint8_t rel_seconds;
52 uint8_t rel_frames;
53 uint8_t zero_space;
54 uint8_t abs_minutes;
55 uint8_t abs_seconds;
56 uint8_t abs_frames;
57 void *next_description;
5658 };
5759 typedef struct CD_TDesc CD_TDesc;
5860
5961 struct CD_TOC_ {
60 uint16_t toc_length;
61 uint8_t first_session;
62 uint8_t last_session;
63 CD_TDesc* track_description; //entry to the first track in the linked list
62 uint16_t toc_length;
63 uint8_t first_session;
64 uint8_t last_session;
65 CD_TDesc *track_description; // entry to the first track in the linked list
6466 };
6567 typedef struct CD_TOC_ CD_TOC_;
6668
67 CD_TOC_* cdTOC = NULL;
68
69 #if defined (DARWIN_PLATFORM)
70 uint8_t LEADOUT_TRACK_NUMBER = MACOSX_LEADOUT_TRACK;
71 #elif defined (HAVE_LINUX_CDROM_H)
72 uint8_t LEADOUT_TRACK_NUMBER = CDROM_LEADOUT;
73 #elif defined (_WIN32)
74 uint8_t LEADOUT_TRACK_NUMBER = 0xAA; //NOTE: for WinXP IOCTL_CDROM_READ_TOC_EX code, its 0xA2
75 #endif
76
69 CD_TOC_ *cdTOC = NULL;
70
71 #if defined(__APPLE__)
72 uint8_t LEADOUT_TRACK_NUMBER = MACOSX_LEADOUT_TRACK;
73 #elif defined(__linux__)
74 uint8_t LEADOUT_TRACK_NUMBER = CDROM_LEADOUT;
75 #elif defined(_WIN32)
76 uint8_t LEADOUT_TRACK_NUMBER =
77 0xAA; // NOTE: for WinXP IOCTL_CDROM_READ_TOC_EX code, its 0xA2
78 #endif
7779
7880 /*
7981 MCDI describes the CD TOC - actually talks about "a binary dump of the TOC".
9496 uint8_t cd_toc_session;
9597 uint8_t cd_toc_controladdress; //bitpacked uint4_t of control & address
9698 uint8_t cd_toc_TNO = 0; //hardcoded to 0 for mode1 audio tracks in the TOC
97 uint8_t cd_toc_tracknumber; //this is the 1-99 tracknumber (listed in mmc-2 as POINT)
98 uint32_t cd_frame_address; //converted from the 3byte mm:ss:frame absolute duration
99 uint8_t cd_toc_tracknumber; //this is the 1-99 tracknumber (listed in mmc-2
100 as POINT) uint32_t cd_frame_address; //converted from the 3byte mm:ss:frame
101 absolute duration
99102 };
100103 struct toc_header {
101 uint16_t toc_length;
102 uin8_t first_track;
103 uint8_t last_track;
104 uint16_t toc_length;
105 uin8_t first_track;
106 uint8_t last_track;
104107 };
105108 struct mcdi_frame {
106 struct toc_header;
109 struct toc_header;
107110 struct mcdi_track_entry[total_audio_tracks];
108111 struct mcdi_track_entry lead_out;
109112 };
129132 ///////////////////////////////////////////////////////////////////////////
130133
131134 uint8_t DataControlField(uint8_t controlfield) {
132 #if defined (__ppc__) || defined (__ppc64__)
133 if (controlfield & 0x04) { // data uninterrupted or increment OR reserved; this field is already bitpacked as controlfield
134 return 1;
135 }
135 #if defined(__ppc__) || defined(__ppc64__)
136 if (controlfield & 0x04) { // data uninterrupted or increment OR reserved;
137 // this field is already bitpacked as controlfield
138 return 1;
139 }
136140 #else
137 if (controlfield & 0x40) { // data uninterrupted or increment OR reserved; bitpacked already
138 return 1;
139 }
140 #endif
141 return 0;
142 }
143
144 uint8_t DetermineCDType(CD_TOC_* cdTOCdata) {
145 CD_TDesc* track_TOC_desc = cdTOCdata->track_description;
146 while (track_TOC_desc != NULL) {
147 if (track_TOC_desc->tracknumber >= 1 && track_TOC_desc->tracknumber <= 99 && !DataControlField(track_TOC_desc->controladdress)) {
148 return CDOBJECT_AUDIOCD;
149 }
150 track_TOC_desc = (CD_TDesc*)track_TOC_desc->next_description;
151 }
152 return CDOBJECT_DATACD;
153 }
154
155 CD_TDesc* LeadOutTrack(CD_TOC_* cdTOCdata) {
156 CD_TDesc* track_TOC_desc = cdTOCdata->track_description;
157 while (track_TOC_desc != NULL) {
158 if (track_TOC_desc->tracknumber == LEADOUT_TRACK_NUMBER) {
159 return track_TOC_desc;
160 }
161 track_TOC_desc = (CD_TDesc*)track_TOC_desc->next_description;
162 }
163 return NULL;
164 }
165
166 uint8_t FillSingleMCDIentry(CD_TDesc* atrack, char* mcdi_data_entry) {
167 mcdi_data_entry[0] = atrack->session;
168 mcdi_data_entry[1] = atrack->controladdress;
169 mcdi_data_entry[2] = 0;
170 mcdi_data_entry[3] = atrack->tracknumber;
171 //LBA=(M*60+S)*75+F - 150 (table 374)
172 uint32_t frameduration = ((((atrack->abs_minutes*60) + atrack->abs_seconds) * 75) + atrack->abs_frames)-150;
173 UInt32_TO_String4(frameduration, mcdi_data_entry + 4);
174 return 8;
175 }
176
177 uint16_t FormMCDIdata(char* mcdi_data) {
178 uint16_t mcdi_len = 0;
179 uint8_t first_track = 0;
180 uint8_t last_track = 0;
181
182 CD_TDesc* track_TOC_desc = cdTOC->track_description;
183
184 if (cdTOC->track_description != NULL) {
185 mcdi_len +=4;
186
187 while (track_TOC_desc != NULL) {
188 if (track_TOC_desc->tracknumber >= 1 && track_TOC_desc->tracknumber <= 99 && !DataControlField(track_TOC_desc->controladdress)) {
189 mcdi_len += FillSingleMCDIentry(track_TOC_desc, mcdi_data+mcdi_len);
190 if (first_track == 0) {
191 first_track = track_TOC_desc->tracknumber;
192 }
193 last_track = track_TOC_desc->tracknumber;
194 }
195 track_TOC_desc = (CD_TDesc*)track_TOC_desc->next_description;
196 }
197 if (mcdi_len > 0) {
198 CD_TDesc* leadout = LeadOutTrack(cdTOC);
199 if (leadout != NULL) {
200 mcdi_len += FillSingleMCDIentry(leadout, mcdi_data+mcdi_len);
201 }
202 }
203 //backtrack & fill in the header
204 UInt16_TO_String2(mcdi_len, mcdi_data);
205 mcdi_data[2] = first_track;
206 mcdi_data[3] = last_track;
207 }
208 return mcdi_len;
141 if (controlfield &
142 0x40) { // data uninterrupted or increment OR reserved; bitpacked already
143 return 1;
144 }
145 #endif
146 return 0;
147 }
148
149 uint8_t DetermineCDType(CD_TOC_ *cdTOCdata) {
150 CD_TDesc *track_TOC_desc = cdTOCdata->track_description;
151 while (track_TOC_desc != NULL) {
152 if (track_TOC_desc->tracknumber >= 1 && track_TOC_desc->tracknumber <= 99 &&
153 !DataControlField(track_TOC_desc->controladdress)) {
154 return CDOBJECT_AUDIOCD;
155 }
156 track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description;
157 }
158 return CDOBJECT_DATACD;
159 }
160
161 CD_TDesc *LeadOutTrack(CD_TOC_ *cdTOCdata) {
162 CD_TDesc *track_TOC_desc = cdTOCdata->track_description;
163 while (track_TOC_desc != NULL) {
164 if (track_TOC_desc->tracknumber == LEADOUT_TRACK_NUMBER) {
165 return track_TOC_desc;
166 }
167 track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description;
168 }
169 return NULL;
170 }
171
172 uint8_t FillSingleMCDIentry(CD_TDesc *atrack, char *mcdi_data_entry) {
173 mcdi_data_entry[0] = atrack->session;
174 mcdi_data_entry[1] = atrack->controladdress;
175 mcdi_data_entry[2] = 0;
176 mcdi_data_entry[3] = atrack->tracknumber;
177 // LBA=(M*60+S)*75+F - 150 (table 374)
178 uint32_t frameduration =
179 ((((atrack->abs_minutes * 60) + atrack->abs_seconds) * 75) +
180 atrack->abs_frames) -
181 150;
182 UInt32_TO_String4(frameduration, mcdi_data_entry + 4);
183 return 8;
184 }
185
186 uint16_t FormMCDIdata(char *mcdi_data) {
187 uint16_t mcdi_len = 0;
188 uint8_t first_track = 0;
189 uint8_t last_track = 0;
190
191 CD_TDesc *track_TOC_desc = cdTOC->track_description;
192
193 if (cdTOC->track_description != NULL) {
194 mcdi_len += 4;
195
196 while (track_TOC_desc != NULL) {
197 if (track_TOC_desc->tracknumber >= 1 &&
198 track_TOC_desc->tracknumber <= 99 &&
199 !DataControlField(track_TOC_desc->controladdress)) {
200 mcdi_len += FillSingleMCDIentry(track_TOC_desc, mcdi_data + mcdi_len);
201 if (first_track == 0) {
202 first_track = track_TOC_desc->tracknumber;
203 }
204 last_track = track_TOC_desc->tracknumber;
205 }
206 track_TOC_desc = (CD_TDesc *)track_TOC_desc->next_description;
207 }
208 if (mcdi_len > 0) {
209 CD_TDesc *leadout = LeadOutTrack(cdTOC);
210 if (leadout != NULL) {
211 mcdi_len += FillSingleMCDIentry(leadout, mcdi_data + mcdi_len);
212 }
213 }
214 // backtrack & fill in the header
215 UInt16_TO_String2(mcdi_len, mcdi_data);
216 mcdi_data[2] = first_track;
217 mcdi_data[3] = last_track;
218 }
219 return mcdi_len;
209220 }
210221
211222 /////////////////////////////////////////////////////////////////////////////
212223 // Platform Specifics //
213224 /////////////////////////////////////////////////////////////////////////////
214225
215 #if defined (HAVE_LINUX_CDROM_H)
226 #if defined(__linux__)
216227 void Linux_ReadCDTOC(int cd_fd) {
217 cdrom_tochdr toc_header;
218 cdrom_tocentry toc_entry;
219 CD_TDesc* a_TOC_desc = NULL;
220 CD_TDesc* prev_desc = NULL;
221
222 if (ioctl(cd_fd, CDROMREADTOCHDR, &toc_header) == -1) {
223 fprintf(stderr, "AtomicParsley error: there was an error reading the CD Table of Contents header.\n");
224 return;
225 }
226 cdTOC = (CD_TOC_*)calloc(1, sizeof(CD_TOC_));
227 cdTOC->track_description = NULL;
228
229 for (uint8_t i = toc_header.cdth_trk0; i <= toc_header.cdth_trk1+1; i++) {
230 memset(&toc_entry, 0, sizeof(toc_entry));
231 if (i == toc_header.cdth_trk1+1) {
232 toc_entry.cdte_track = CDROM_LEADOUT;
233 } else {
234 toc_entry.cdte_track = i;
235 }
236 toc_entry.cdte_format = CDROM_MSF; //although it could just be easier to use CDROM_LBA
237
238 if (ioctl(cd_fd, CDROMREADTOCENTRY, &toc_entry) == -1) {
239 fprintf(stderr,
240 "AtomicParsley error: there was an error reading a "
241 "CD Table of Contents entry (linux cdrom).\n");
242 return;
243 }
244
245 if (cdTOC->track_description == NULL) {
246 cdTOC->track_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc)));
247 a_TOC_desc = cdTOC->track_description;
248 prev_desc = a_TOC_desc;
249 } else {
250 prev_desc->next_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc)));
251 a_TOC_desc = (CD_TDesc*)prev_desc->next_description;
252 prev_desc = a_TOC_desc;
253 }
254
255 a_TOC_desc->session = 1; //and for vanilla audio CDs it is session 1, but for multi-session...
256 #if defined (__ppc__) || defined (__ppc64__)
257 a_TOC_desc->controladdress = (toc_entry.cdte_ctrl << 4) | toc_entry.cdte_adr;
228 cdrom_tochdr toc_header;
229 cdrom_tocentry toc_entry;
230 CD_TDesc *a_TOC_desc = NULL;
231 CD_TDesc *prev_desc = NULL;
232
233 if (ioctl(cd_fd, CDROMREADTOCHDR, &toc_header) == -1) {
234 fprintf(stderr,
235 "AtomicParsley error: there was an error reading the CD "
236 "Table of Contents header.\n");
237 return;
238 }
239 cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_));
240 cdTOC->track_description = NULL;
241
242 for (uint8_t i = toc_header.cdth_trk0; i <= toc_header.cdth_trk1 + 1; i++) {
243 memset(&toc_entry, 0, sizeof(toc_entry));
244 if (i == toc_header.cdth_trk1 + 1) {
245 toc_entry.cdte_track = CDROM_LEADOUT;
246 } else {
247 toc_entry.cdte_track = i;
248 }
249 toc_entry.cdte_format =
250 CDROM_MSF; // although it could just be easier to use CDROM_LBA
251
252 if (ioctl(cd_fd, CDROMREADTOCENTRY, &toc_entry) == -1) {
253 fprintf(stderr,
254 "AtomicParsley error: there was an error reading a "
255 "CD Table of Contents entry (linux cdrom).\n");
256 return;
257 }
258
259 if (cdTOC->track_description == NULL) {
260 cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
261 a_TOC_desc = cdTOC->track_description;
262 prev_desc = a_TOC_desc;
263 } else {
264 prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
265 a_TOC_desc = (CD_TDesc *)prev_desc->next_description;
266 prev_desc = a_TOC_desc;
267 }
268
269 a_TOC_desc->session = 1; // and for vanilla audio CDs it is session 1, but
270 // for multi-session...
271 #if defined(__ppc__) || defined(__ppc64__)
272 a_TOC_desc->controladdress =
273 (toc_entry.cdte_ctrl << 4) | toc_entry.cdte_adr;
258274 #else
259 a_TOC_desc->controladdress = (toc_entry.cdte_adr << 4) | toc_entry.cdte_ctrl;
260 #endif
261 a_TOC_desc->unused1 = 0;
262 a_TOC_desc->tracknumber = toc_entry.cdte_track;
263 a_TOC_desc->rel_minutes = 0; //is there anyway to even find this out on linux without playing the track? //cdmsf_min0
264 a_TOC_desc->rel_seconds = 0;
265 a_TOC_desc->rel_frames = 0;
266 a_TOC_desc->zero_space = 0;
267 a_TOC_desc->abs_minutes = toc_entry.cdte_addr.msf.minute;
268 a_TOC_desc->abs_seconds = toc_entry.cdte_addr.msf.second;
269 a_TOC_desc->abs_frames = toc_entry.cdte_addr.msf.frame;
270 }
271 return;
272 }
273
274 uint16_t Linux_ioctlProbeTargetDrive(const char* id3args_drive, char* mcdi_data) {
275 uint16_t mcdi_data_len = 0;
276 int cd_fd = 0;
277
278 cd_fd = open(id3args_drive, O_RDONLY | O_NONBLOCK);
279
280 if (cd_fd != -1) {
281 int cd_mode = ioctl(cd_fd, CDROM_DISC_STATUS);
282 if (cd_mode != CDS_AUDIO || cd_mode != CDS_MIXED) {
283 Linux_ReadCDTOC(cd_fd);
284 mcdi_data_len = FormMCDIdata(mcdi_data);
285 } else {
286 //scan for available devices
287 }
288 } else {
289 //scan for available devices
290 }
291
292 return mcdi_data_len;
293 }
294 #endif
295
296 #if defined (DARWIN_PLATFORM)
297 uint16_t Extract_cdTOCrawdata(CFDataRef cdTOCdata, char* cdTOCrawdata) {
298 CFRange cdrange;
299 CFIndex cdTOClen = CFDataGetLength (cdTOCdata);
300 cdTOCrawdata = (char*)calloc(1, sizeof(char)*cdTOClen+1);
301 cdrange = CFRangeMake(0, cdTOClen+1);
302 CFDataGetBytes(cdTOCdata, cdrange, (unsigned char*)cdTOCrawdata);
303
304 cdTOC = (CD_TOC_*)calloc(1, sizeof(CD_TOC_));
305 cdTOC->toc_length = UInt16FromBigEndian(cdTOCrawdata);
306 cdTOC->first_session = cdTOCrawdata[2];
307 cdTOC->first_session = cdTOCrawdata[3];
308 cdTOC->track_description = NULL;
309
310 CD_TDesc* a_TOC_desc = NULL;
311 CD_TDesc* prev_desc = NULL;
312
313 uint16_t toc_offset = 0;
314 for (toc_offset = 4; toc_offset <= cdTOClen; toc_offset +=11) {
315 if (cdTOC->track_description == NULL) {
316 cdTOC->track_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc)));
317 a_TOC_desc = cdTOC->track_description;
318 prev_desc = a_TOC_desc;
319 } else {
320 prev_desc->next_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc)));
321 a_TOC_desc = (CD_TDesc*)prev_desc->next_description;
322 prev_desc = a_TOC_desc;
323 }
324 a_TOC_desc->session = cdTOCrawdata[toc_offset];
325 a_TOC_desc->controladdress = cdTOCrawdata[toc_offset+1];
326 a_TOC_desc->unused1 = cdTOCrawdata[toc_offset+2];
327 a_TOC_desc->tracknumber = cdTOCrawdata[toc_offset+3];
328 a_TOC_desc->rel_minutes = cdTOCrawdata[toc_offset+4];
329 a_TOC_desc->rel_seconds = cdTOCrawdata[toc_offset+5];
330 a_TOC_desc->rel_frames = cdTOCrawdata[toc_offset+6];
331 a_TOC_desc->zero_space = 0;
332 a_TOC_desc->abs_minutes = cdTOCrawdata[toc_offset+8];
333 a_TOC_desc->abs_seconds = cdTOCrawdata[toc_offset+9];
334 a_TOC_desc->abs_frames = cdTOCrawdata[toc_offset+10];
335 }
336
337 return (uint16_t)cdTOClen;
275 a_TOC_desc->controladdress =
276 (toc_entry.cdte_adr << 4) | toc_entry.cdte_ctrl;
277 #endif
278 a_TOC_desc->unused1 = 0;
279 a_TOC_desc->tracknumber = toc_entry.cdte_track;
280 a_TOC_desc->rel_minutes =
281 0; // is there anyway to even find this out on
282 // linux without playing the track? //cdmsf_min0
283 a_TOC_desc->rel_seconds = 0;
284 a_TOC_desc->rel_frames = 0;
285 a_TOC_desc->zero_space = 0;
286 a_TOC_desc->abs_minutes = toc_entry.cdte_addr.msf.minute;
287 a_TOC_desc->abs_seconds = toc_entry.cdte_addr.msf.second;
288 a_TOC_desc->abs_frames = toc_entry.cdte_addr.msf.frame;
289 }
290 return;
291 }
292
293 uint16_t Linux_ioctlProbeTargetDrive(const char *id3args_drive,
294 char *mcdi_data) {
295 uint16_t mcdi_data_len = 0;
296 int cd_fd = 0;
297
298 cd_fd = open(id3args_drive, O_RDONLY | O_NONBLOCK);
299
300 if (cd_fd != -1) {
301 int cd_mode = ioctl(cd_fd, CDROM_DISC_STATUS);
302 if (cd_mode != CDS_AUDIO || cd_mode != CDS_MIXED) {
303 Linux_ReadCDTOC(cd_fd);
304 mcdi_data_len = FormMCDIdata(mcdi_data);
305 } else {
306 // scan for available devices
307 }
308 } else {
309 // scan for available devices
310 }
311
312 return mcdi_data_len;
313 }
314 #endif
315
316 #if defined(__APPLE__)
317 uint16_t Extract_cdTOCrawdata(CFDataRef cdTOCdata, char *cdTOCrawdata) {
318 CFRange cdrange;
319 CFIndex cdTOClen = CFDataGetLength(cdTOCdata);
320 cdTOCrawdata = (char *)calloc(1, sizeof(char) * cdTOClen + 1);
321 cdrange = CFRangeMake(0, cdTOClen + 1);
322 CFDataGetBytes(cdTOCdata, cdrange, (unsigned char *)cdTOCrawdata);
323
324 cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_));
325 cdTOC->toc_length = UInt16FromBigEndian(cdTOCrawdata);
326 cdTOC->first_session = cdTOCrawdata[2];
327 cdTOC->first_session = cdTOCrawdata[3];
328 cdTOC->track_description = NULL;
329
330 CD_TDesc *a_TOC_desc = NULL;
331 CD_TDesc *prev_desc = NULL;
332
333 uint16_t toc_offset = 0;
334 for (toc_offset = 4; toc_offset <= cdTOClen; toc_offset += 11) {
335 if (cdTOC->track_description == NULL) {
336 cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
337 a_TOC_desc = cdTOC->track_description;
338 prev_desc = a_TOC_desc;
339 } else {
340 prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
341 a_TOC_desc = (CD_TDesc *)prev_desc->next_description;
342 prev_desc = a_TOC_desc;
343 }
344 a_TOC_desc->session = cdTOCrawdata[toc_offset];
345 a_TOC_desc->controladdress = cdTOCrawdata[toc_offset + 1];
346 a_TOC_desc->unused1 = cdTOCrawdata[toc_offset + 2];
347 a_TOC_desc->tracknumber = cdTOCrawdata[toc_offset + 3];
348 a_TOC_desc->rel_minutes = cdTOCrawdata[toc_offset + 4];
349 a_TOC_desc->rel_seconds = cdTOCrawdata[toc_offset + 5];
350 a_TOC_desc->rel_frames = cdTOCrawdata[toc_offset + 6];
351 a_TOC_desc->zero_space = 0;
352 a_TOC_desc->abs_minutes = cdTOCrawdata[toc_offset + 8];
353 a_TOC_desc->abs_seconds = cdTOCrawdata[toc_offset + 9];
354 a_TOC_desc->abs_frames = cdTOCrawdata[toc_offset + 10];
355 }
356
357 return (uint16_t)cdTOClen;
338358 }
339359
340360 void OSX_ReadCDTOC(io_object_t cdobject) {
341 CFMutableDictionaryRef cd_props = 0;
342 CFDataRef cdTOCdata = NULL;
343 char* cdTOCrawdata = NULL;
344
345 if (IORegistryEntryCreateCFProperties(cdobject, &cd_props, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) return;
346
347 cdTOCdata = (CFDataRef)CFDictionaryGetValue(cd_props, CFSTR (kIOCDMediaTOCKey));
348 if (cdTOCdata != NULL) {
349 Extract_cdTOCrawdata(cdTOCdata, cdTOCrawdata);
350 }
351 CFRelease(cd_props);
352 cd_props = NULL;
353 return;
361 CFMutableDictionaryRef cd_props = 0;
362 CFDataRef cdTOCdata = NULL;
363 char *cdTOCrawdata = NULL;
364
365 if (IORegistryEntryCreateCFProperties(
366 cdobject, &cd_props, kCFAllocatorDefault, kNilOptions) !=
367 kIOReturnSuccess)
368 return;
369
370 cdTOCdata =
371 (CFDataRef)CFDictionaryGetValue(cd_props, CFSTR(kIOCDMediaTOCKey));
372 if (cdTOCdata != NULL) {
373 Extract_cdTOCrawdata(cdTOCdata, cdTOCrawdata);
374 }
375 CFRelease(cd_props);
376 cd_props = NULL;
377 return;
354378 }
355379
356380 void OSX_ScanForCDDrive() {
357 io_iterator_t drive_iter = MACH_PORT_NULL;
358 io_object_t driveobject = MACH_PORT_NULL;
359 CFTypeRef drive_path = NULL;
360 char drive_path_str[20];
361 if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(kIOCDMediaClass), &drive_iter) != kIOReturnSuccess) { //create the iterator
362 fprintf(stdout, "No device capable of reading cd media present\n");
363 }
364
365 driveobject = IOIteratorNext(drive_iter);
366 while ( driveobject != MACH_PORT_NULL ) {
367 drive_path = IORegistryEntryCreateCFProperty(driveobject, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
368 if (drive_path != NULL) {
369 CFStringGetCString((CFStringRef)drive_path, (char*)&drive_path_str, 20, kCFStringEncodingASCII);
370 fprintf(stdout, "Device '%s' contains cd media\n", drive_path_str);
371 OSX_ReadCDTOC(driveobject);
372 if (cdTOC != NULL) {
373 uint8_t cdType = DetermineCDType(cdTOC);
374 if (cdType == CDOBJECT_AUDIOCD) {
375 fprintf(stdout, "Good news, device '%s' is an Audio CD and can be used for 'MCDI' setting\n", drive_path_str);
376 } else {
377 fprintf(stdout, "Tragically, it was a data CD.\n");
378 }
379 free(cdTOC); //the other malloced members should be freed also
380 }
381 }
382 IOObjectRelease(driveobject);
383 driveobject = IOIteratorNext(drive_iter);
384 }
385
386 if (drive_path_str[0] == (uint8_t)0x00) {
387 fprintf(stdout, "No CD media was found in any device\n");
388 }
389 IOObjectRelease(drive_iter);
390 drive_iter = MACH_PORT_NULL;
391 exit(0);
392 }
393
394 uint16_t OSX_ProbeTargetDrive(const char* id3args_drive, char* mcdi_data) {
395 uint16_t mcdi_data_len = 0;
396 io_object_t cdobject = MACH_PORT_NULL;
397
398 if (strncmp(id3args_drive, "disk", 4) != 0) {
399 OSX_ScanForCDDrive();
400 exit(0);
401 }
402 cdobject = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching (kIOMasterPortDefault, 0, id3args_drive) );
403
404 if (cdobject == MACH_PORT_NULL) {
405 fprintf(stdout, "No device found at %s; searching for possible drives...\n", id3args_drive);
406 OSX_ScanForCDDrive();
407
408 } else if (IOObjectConformsTo(cdobject, kIOCDMediaClass) == false) {
409 fprintf (stdout, "No cd present in drive at %s\n", id3args_drive );
410 IOObjectRelease(cdobject);
411 cdobject = MACH_PORT_NULL;
412 OSX_ScanForCDDrive();
413 } else {
414 //we now have a cd object
415 OSX_ReadCDTOC(cdobject);
416 if (cdTOC != NULL) {
417 uint8_t cdType = DetermineCDType(cdTOC);
418 if (cdType == CDOBJECT_AUDIOCD) {
419 mcdi_data_len = FormMCDIdata(mcdi_data);
420 }
421 }
422 }
423
424 IOObjectRelease(cdobject);
425 cdobject = MACH_PORT_NULL;
426 return mcdi_data_len;
427 }
428
429 #endif
430
431 #if defined (_WIN32)
381 io_iterator_t drive_iter = MACH_PORT_NULL;
382 io_object_t driveobject = MACH_PORT_NULL;
383 CFTypeRef drive_path = NULL;
384 char drive_path_str[20];
385 if (IOServiceGetMatchingServices(kIOMasterPortDefault,
386 IOServiceMatching(kIOCDMediaClass),
387 &drive_iter) !=
388 kIOReturnSuccess) { // create the iterator
389 fprintf(stdout, "No device capable of reading cd media present\n");
390 }
391
392 driveobject = IOIteratorNext(drive_iter);
393 while (driveobject != MACH_PORT_NULL) {
394 drive_path = IORegistryEntryCreateCFProperty(
395 driveobject, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
396 if (drive_path != NULL) {
397 CFStringGetCString((CFStringRef)drive_path,
398 (char *)&drive_path_str,
399 20,
400 kCFStringEncodingASCII);
401 fprintf(stdout, "Device '%s' contains cd media\n", drive_path_str);
402 OSX_ReadCDTOC(driveobject);
403 if (cdTOC != NULL) {
404 uint8_t cdType = DetermineCDType(cdTOC);
405 if (cdType == CDOBJECT_AUDIOCD) {
406 fprintf(stdout,
407 "Good news, device '%s' is an Audio CD and can be used for "
408 "'MCDI' setting\n",
409 drive_path_str);
410 } else {
411 fprintf(stdout, "Tragically, it was a data CD.\n");
412 }
413 free(cdTOC); // the other malloced members should be freed also
414 }
415 }
416 IOObjectRelease(driveobject);
417 driveobject = IOIteratorNext(drive_iter);
418 }
419
420 if (drive_path_str[0] == (uint8_t)0x00) {
421 fprintf(stdout, "No CD media was found in any device\n");
422 }
423 IOObjectRelease(drive_iter);
424 drive_iter = MACH_PORT_NULL;
425 exit(0);
426 }
427
428 uint16_t OSX_ProbeTargetDrive(const char *id3args_drive, char *mcdi_data) {
429 uint16_t mcdi_data_len = 0;
430 io_object_t cdobject = MACH_PORT_NULL;
431
432 if (strncmp(id3args_drive, "disk", 4) != 0) {
433 OSX_ScanForCDDrive();
434 exit(0);
435 }
436 cdobject = IOServiceGetMatchingService(
437 kIOMasterPortDefault,
438 IOBSDNameMatching(kIOMasterPortDefault, 0, id3args_drive));
439
440 if (cdobject == MACH_PORT_NULL) {
441 fprintf(stdout,
442 "No device found at %s; searching for possible drives...\n",
443 id3args_drive);
444 OSX_ScanForCDDrive();
445
446 } else if (IOObjectConformsTo(cdobject, kIOCDMediaClass) == false) {
447 fprintf(stdout, "No cd present in drive at %s\n", id3args_drive);
448 IOObjectRelease(cdobject);
449 cdobject = MACH_PORT_NULL;
450 OSX_ScanForCDDrive();
451 } else {
452 // we now have a cd object
453 OSX_ReadCDTOC(cdobject);
454 if (cdTOC != NULL) {
455 uint8_t cdType = DetermineCDType(cdTOC);
456 if (cdType == CDOBJECT_AUDIOCD) {
457 mcdi_data_len = FormMCDIdata(mcdi_data);
458 }
459 }
460 }
461
462 IOObjectRelease(cdobject);
463 cdobject = MACH_PORT_NULL;
464 return mcdi_data_len;
465 }
466
467 #endif
468
469 #if defined(_WIN32)
432470 void Windows_ioctlReadCDTOC(HANDLE cdrom_device) {
433 DWORD bytes_returned;
434 CDROM_TOC win_cdrom_toc;
435
436 //WARNING: "This IOCTL is obsolete beginning with the Microsoft Windows Vista. Do not use this IOCTL to develop drivers in Microsoft Windows Vista."
437 if (DeviceIoControl(cdrom_device, IOCTL_CDROM_READ_TOC, NULL, 0, &win_cdrom_toc, sizeof(CDROM_TOC), &bytes_returned, NULL) == 0) {
438 fprintf(stderr, "AtomicParsley error: there was an error reading the CD Table of Contents header (win32).\n");
439 return;
440 }
441
442
443 cdTOC = (CD_TOC_*)calloc(1, sizeof(CD_TOC_));
444 cdTOC->toc_length = ((win_cdrom_toc.Length[0] & 0xFF) << 8) | (win_cdrom_toc.Length[1] & 0xFF);
445 //cdTOC->first_session = 0; //seems windows doesn't store session info with IOCTL_CDROM_READ_TOC, all tracks from all sessions are available
446 //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
447 //...............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)
448 cdTOC->track_description = NULL;
449
450 CD_TDesc* a_TOC_desc = NULL;
451 CD_TDesc* prev_desc = NULL;
452
453 for (uint8_t i = win_cdrom_toc.FirstTrack; i <= win_cdrom_toc.LastTrack+1; i++) {
454 TRACK_DATA* thisTrackData = &(win_cdrom_toc.TrackData[i-1]);
455
456 if (cdTOC->track_description == NULL) {
457 cdTOC->track_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc)));
458 a_TOC_desc = cdTOC->track_description;
459 prev_desc = a_TOC_desc;
460 } else {
461 prev_desc->next_description = (CD_TDesc*)calloc(1, (sizeof(CD_TDesc)));
462 a_TOC_desc = (CD_TDesc*)prev_desc->next_description;
463 prev_desc = a_TOC_desc;
464 }
465
466 a_TOC_desc->session = 1; //and for vanilla audio CDs it is session 1, but for multi-session...
467 #if defined (__ppc__) || defined (__ppc64__)
468 a_TOC_desc->controladdress = (thisTrackData->Control << 4) | thisTrackData->Adr;
471 DWORD bytes_returned;
472 CDROM_TOC win_cdrom_toc;
473
474 // WARNING: "This IOCTL is obsolete beginning with the Microsoft Windows
475 // Vista. Do not use this IOCTL to develop drivers in Microsoft Windows
476 // Vista."
477 if (DeviceIoControl(cdrom_device,
478 IOCTL_CDROM_READ_TOC,
479 NULL,
480 0,
481 &win_cdrom_toc,
482 sizeof(CDROM_TOC),
483 &bytes_returned,
484 NULL) == 0) {
485 fprintf(stderr,
486 "AtomicParsley error: there was an error reading the CD "
487 "Table of Contents header (win32).\n");
488 return;
489 }
490
491 cdTOC = (CD_TOC_ *)calloc(1, sizeof(CD_TOC_));
492 cdTOC->toc_length = ((win_cdrom_toc.Length[0] & 0xFF) << 8) |
493 (win_cdrom_toc.Length[1] & 0xFF);
494 // cdTOC->first_session = 0; //seems windows doesn't store session info with
495 // IOCTL_CDROM_READ_TOC, all tracks from all sessions are available
496 // cdTOC->last_session = 0; //not used anyway; IOCTL_CDROM_READ_TOC_EX could
497 // be used to get session information, but its only available on XP
498 //...............and interestingly enough in XP, IOCTL_CDROM_TOC_EX returns
499 // the leadout track as 0xA2, which makes a Win2k MCDI & a WinXP MCDI
500 // different (by 1 byte)
501 cdTOC->track_description = NULL;
502
503 CD_TDesc *a_TOC_desc = NULL;
504 CD_TDesc *prev_desc = NULL;
505
506 for (uint8_t i = win_cdrom_toc.FirstTrack; i <= win_cdrom_toc.LastTrack + 1;
507 i++) {
508 TRACK_DATA *thisTrackData = &(win_cdrom_toc.TrackData[i - 1]);
509
510 if (cdTOC->track_description == NULL) {
511 cdTOC->track_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
512 a_TOC_desc = cdTOC->track_description;
513 prev_desc = a_TOC_desc;
514 } else {
515 prev_desc->next_description = (CD_TDesc *)calloc(1, (sizeof(CD_TDesc)));
516 a_TOC_desc = (CD_TDesc *)prev_desc->next_description;
517 prev_desc = a_TOC_desc;
518 }
519
520 a_TOC_desc->session = 1; // and for vanilla audio CDs it is session 1, but
521 // for multi-session...
522 #if defined(__ppc__) || defined(__ppc64__)
523 a_TOC_desc->controladdress =
524 (thisTrackData->Control << 4) | thisTrackData->Adr;
469525 #else
470 a_TOC_desc->controladdress = (thisTrackData->Adr << 4) | thisTrackData->Control;
471 #endif
472 a_TOC_desc->unused1 = 0;
473 a_TOC_desc->tracknumber = thisTrackData->TrackNumber;
474 a_TOC_desc->rel_minutes = 0; //didn't look too much into finding this since it is unused
475 a_TOC_desc->rel_seconds = 0;
476 a_TOC_desc->rel_frames = 0;
477 a_TOC_desc->zero_space = 0;
478 a_TOC_desc->abs_minutes = thisTrackData->Address[1];
479 a_TOC_desc->abs_seconds = thisTrackData->Address[2];
480 a_TOC_desc->abs_frames = thisTrackData->Address[3];
481 }
482
483 return;
484 }
485
486 uint16_t Windows_ioctlProbeTargetDrive(const char* id3args_drive, char* mcdi_data) {
487 uint16_t mcdi_data_len = 0;
488 char cd_device_path[16];
489
490 memset(cd_device_path, 0, 16);
491 sprintf(cd_device_path, "\\\\.\\%s:", id3args_drive);
492
493 HANDLE cdrom_device = APar_OpenFileWin32(cd_device_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
494 if (cdrom_device != INVALID_HANDLE_VALUE) {
495 Windows_ioctlReadCDTOC(cdrom_device);
496 if (cdTOC != NULL) {
497 uint8_t cdType = DetermineCDType(cdTOC);
498 if (cdType == CDOBJECT_AUDIOCD) {
499 mcdi_data_len = FormMCDIdata(mcdi_data);
500 }
501 }
502 CloseHandle(cdrom_device);
503 }
504
505 return mcdi_data_len;
526 a_TOC_desc->controladdress =
527 (thisTrackData->Adr << 4) | thisTrackData->Control;
528 #endif
529 a_TOC_desc->unused1 = 0;
530 a_TOC_desc->tracknumber = thisTrackData->TrackNumber;
531 a_TOC_desc->rel_minutes =
532 0; // didn't look too much into finding this since it is unused
533 a_TOC_desc->rel_seconds = 0;
534 a_TOC_desc->rel_frames = 0;
535 a_TOC_desc->zero_space = 0;
536 a_TOC_desc->abs_minutes = thisTrackData->Address[1];
537 a_TOC_desc->abs_seconds = thisTrackData->Address[2];
538 a_TOC_desc->abs_frames = thisTrackData->Address[3];
539 }
540
541 return;
542 }
543
544 uint16_t Windows_ioctlProbeTargetDrive(const char *id3args_drive,
545 char *mcdi_data) {
546 uint16_t mcdi_data_len = 0;
547 char cd_device_path[16];
548
549 memset(cd_device_path, 0, 16);
550 sprintf(cd_device_path, "\\\\.\\%s:", id3args_drive);
551
552 HANDLE cdrom_device = APar_OpenFileWin32(cd_device_path,
553 GENERIC_READ,
554 FILE_SHARE_READ,
555 NULL,
556 OPEN_EXISTING,
557 FILE_ATTRIBUTE_NORMAL,
558 NULL);
559 if (cdrom_device != INVALID_HANDLE_VALUE) {
560 Windows_ioctlReadCDTOC(cdrom_device);
561 if (cdTOC != NULL) {
562 uint8_t cdType = DetermineCDType(cdTOC);
563 if (cdType == CDOBJECT_AUDIOCD) {
564 mcdi_data_len = FormMCDIdata(mcdi_data);
565 }
566 }
567 CloseHandle(cdrom_device);
568 }
569
570 return mcdi_data_len;
506571 }
507572 #endif
508573
510575 // CD TOC Entry Area //
511576 ////////////////////////////////////////////////////////////////////////////
512577
513 uint16_t GenerateMCDIfromCD(const char* drive, char* dest_buffer) {
514 uint16_t mcdi_bytes = 0;
515 #if defined (DARWIN_PLATFORM)
516 mcdi_bytes = OSX_ProbeTargetDrive(drive, dest_buffer);
517 #elif defined (HAVE_LINUX_CDROM_H)
518 mcdi_bytes = Linux_ioctlProbeTargetDrive(drive, dest_buffer);
519 #elif defined (_WIN32)
520 mcdi_bytes = Windows_ioctlProbeTargetDrive(drive, dest_buffer);
521 #endif
522 return mcdi_bytes;
523 }
578 uint16_t GenerateMCDIfromCD(const char *drive, char *dest_buffer) {
579 uint16_t mcdi_bytes = 0;
580 #if defined(__APPLE__)
581 mcdi_bytes = OSX_ProbeTargetDrive(drive, dest_buffer);
582 #elif defined(__linux__)
583 mcdi_bytes = Linux_ioctlProbeTargetDrive(drive, dest_buffer);
584 #elif defined(_WIN32)
585 mcdi_bytes = Windows_ioctlProbeTargetDrive(drive, dest_buffer);
586 #endif
587 return mcdi_bytes;
588 }
11 /*
22 AtomicParsley - CDtoc.h
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
1717 Copyright ©2006-2007 puck_lock
18 with contributions from others; see the CREDITS file
18 with contributions from others; see the CREDITS file
1919 */
2020 //==================================================================//
2121
2222 #include "ap_types.h"
2323
24 #if defined (_WIN32)
25 //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
24 #if defined(_WIN32)
25 // these #defines & structs are copied from the MS W2k DDK headers so the entire
26 // DDK isn't required to be installed to compile AP_CDTOC for MCDI support
2627 #ifndef DEVICE_TYPE
2728 #define DEVICE_TYPE ULONG
2829 #endif
2930
30 #define FILE_DEVICE_CD_ROM 0x00000002
31 #define FILE_DEVICE_CD_ROM 0x00000002
3132
32 #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
33 #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
3334
34 #define METHOD_BUFFERED 0
35 #define METHOD_BUFFERED 0
3536
36 #define FILE_READ_ACCESS ( 0x0001 ) // file & pipe
37 #define FILE_READ_ACCESS (0x0001) // file & pipe
3738
38 #define CTL_CODE( DeviceType, Function, Method, Access ) ( \
39 ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
40 )
39 #define CTL_CODE(DeviceType, Function, Method, Access) \
40 (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
4141
42 #define IOCTL_CDROM_READ_TOC CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
42 #define IOCTL_CDROM_READ_TOC \
43 CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
4344
4445 #define MAXIMUM_NUMBER_TRACKS 100
4546
4950 //
5051
5152 typedef struct _TRACK_DATA {
52 UCHAR Reserved;
53 UCHAR Control : 4;
54 UCHAR Adr : 4;
55 UCHAR TrackNumber;
56 UCHAR Reserved1;
57 UCHAR Address[4];
53 UCHAR Reserved;
54 UCHAR Control : 4;
55 UCHAR Adr : 4;
56 UCHAR TrackNumber;
57 UCHAR Reserved1;
58 UCHAR Address[4];
5859 } TRACK_DATA, *PTRACK_DATA;
5960
6061 typedef struct _CDROM_TOC {
6162
62 //
63 // Header
64 //
63 //
64 // Header
65 //
6566
66 UCHAR Length[2];
67 UCHAR FirstTrack;
68 UCHAR LastTrack;
67 UCHAR Length[2];
68 UCHAR FirstTrack;
69 UCHAR LastTrack;
6970
70 //
71 // Track data
72 //
71 //
72 // Track data
73 //
7374
74 TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];
75 TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];
7576 } CDROM_TOC, *PCDROM_TOC;
7677 #endif
7778
78 uint16_t GenerateMCDIfromCD(const char* drive, char* dest_buffer);
79 uint16_t GenerateMCDIfromCD(const char *drive, char *dest_buffer);
00 #ifndef AP_TYPES_H
11 #define AP_TYPES_H
22
3
4
53 // Atom version 1byte/ Atom flags 3 bytes; 0x00 00 00 00
64 #define AtomFlags_Data_Binary 0
75
2018 // 0x58 for uuid atoms that contain files
2119 #define AtomFlags_Data_uuid_binary 88
2220
23
24 enum {
25 UTF8_iTunesStyle_256glyphLimited = 0, //no NULL termination
26 UTF8_iTunesStyle_Unlimited = 1, //no NULL termination
27 UTF8_iTunesStyle_Binary = 3, //no NULL termination, used in purl & egid
28 UTF8_3GP_Style = 8, //terminated with a NULL uint8_t
29 UTF16_3GP_Style = 16 //terminated with a NULL uint16_t
30 };
31
32 enum {
33 UNDEFINED_STYLE = 0,
34 ITUNES_STYLE = 100,
35 THIRD_GEN_PARTNER = 300, //3gpp files prior to 3gp6
36 THIRD_GEN_PARTNER_VER1_REL6 = 306, //3GPP Release6 the first spec to contain the complement of assets
37 THIRD_GEN_PARTNER_VER1_REL7 = 307, //3GPP Release7 introduces ID32 atoms
38 THIRD_GEN_PARTNER_VER2 = 320, //3gp2 files
39 THIRD_GEN_PARTNER_VER2_REL_A = 321, //3gp2 files, 3GPP2 C.S0050-A introduces 'gadi'
40 MOTIONJPEG2000 = 400
21 enum {
22 UTF8_iTunesStyle_256glyphLimited = 0, // no NULL termination
23 UTF8_iTunesStyle_Unlimited = 1, // no NULL termination
24 UTF8_iTunesStyle_Binary = 3, // no NULL termination, used in purl & egid
25 UTF8_3GP_Style = 8, // terminated with a NULL uint8_t
26 UTF16_3GP_Style = 16 // terminated with a NULL uint16_t
27 };
28
29 enum {
30 UNDEFINED_STYLE = 0,
31 ITUNES_STYLE = 100,
32 THIRD_GEN_PARTNER = 300, // 3gpp files prior to 3gp6
33 THIRD_GEN_PARTNER_VER1_REL6 =
34 306, // 3GPP Release6 the first spec to contain the complement of assets
35 THIRD_GEN_PARTNER_VER1_REL7 = 307, // 3GPP Release7 introduces ID32 atoms
36 THIRD_GEN_PARTNER_VER2 = 320, // 3gp2 files
37 THIRD_GEN_PARTNER_VER2_REL_A =
38 321, // 3gp2 files, 3GPP2 C.S0050-A introduces 'gadi'
39 MOTIONJPEG2000 = 400
4140 };
4241
4342 #include "id3v2types.h"
44 struct AtomicInfo {
45 short AtomicNumber;
46 uint64_t AtomicStart;
47 uint64_t AtomicLength;
48 uint64_t AtomicLengthExtended;
49 char* AtomicName;
50 char* ReverseDNSname;
51 char* ReverseDNSdomain;
52 uint8_t AtomicContainerState;
53 uint8_t AtomicClassification;
54 uint32_t AtomicVerFlags; //used by versioned atoms and derivatives
55 uint16_t AtomicLanguage; //used by 3gp assets & ID32 atoms only
56 uint8_t AtomicLevel;
57 char* AtomicData;
58 int NextAtomNumber; //our first atom is numbered 0; the last points back to it - so watch it!
59 uint32_t ancillary_data; //just contains a simple number for atoms that contains some interesting info (like stsd codec used)
60 uint8_t uuid_style;
61 char* uuid_ap_atomname;
62 ID3v2Tag* ID32_TagInfo;
43 struct AtomicInfo {
44 short AtomicNumber;
45 uint64_t AtomicStart;
46 uint64_t AtomicLength;
47 uint64_t AtomicLengthExtended;
48 char *AtomicName;
49 char *ReverseDNSname;
50 char *ReverseDNSdomain;
51 uint8_t AtomicContainerState;
52 uint8_t AtomicClassification;
53 uint32_t AtomicVerFlags; // used by versioned atoms and derivatives
54 uint16_t AtomicLanguage; // used by 3gp assets & ID32 atoms only
55 uint8_t AtomicLevel;
56 char *AtomicData;
57 int NextAtomNumber; // our first atom is numbered 0; the last points back to
58 // it - so watch it!
59 uint32_t
60 ancillary_data; // just contains a simple number for atoms that contains
61 // some interesting info (like stsd codec used)
62 uint8_t uuid_style;
63 char *uuid_ap_atomname;
64 ID3v2Tag *ID32_TagInfo;
6365 };
6466 #include "id3v2.h"
6567
7476 bool has_alac;
7577 bool has_mp4a;
7678 bool has_drms;
77 bool has_timed_text; //carries the URL - in the mdat stream at a specific time - thus it too is timed.
78 bool has_timed_jpeg; //no idea of podcasts support 'png ' or 'tiff'
79 bool has_timed_tx3g; //this IS true timed text stream
80 bool has_mp4s; //MPEG-4 Systems
81 bool has_rtp_hint; //'rtp '; implies hinting
79 bool has_timed_text; // carries the URL - in the mdat stream at a specific
80 // time - thus it too is timed.
81 bool has_timed_jpeg; // no idea of podcasts support 'png ' or 'tiff'
82 bool has_timed_tx3g; // this IS true timed text stream
83 bool has_mp4s; // MPEG-4 Systems
84 bool has_rtp_hint; //'rtp '; implies hinting
8285 };
8386
8487 enum {
8588 MEDIADATA__PRECEDES__MOOV = 2,
86 ROOT_META__PRECEDES__MOOV = 4,
89 ROOT_META__PRECEDES__MOOV = 4,
8790 MOOV_META__PRECEDES__TRACKS = 8,
8891 MOOV_UDTA__PRECEDES__TRACKS = 16,
89
92
9093 PADDING_AT_EOF = 0x1000000
9194 };
9295
9396 struct FreeAtomListing {
94 AtomicInfo* free_atom;
95 FreeAtomListing* next_free_listing;
96 };
97
98 struct DynamicUpdateStat {
97 AtomicInfo *free_atom;
98 FreeAtomListing *next_free_listing;
99 };
100
101 struct DynamicUpdateStat {
99102 bool updage_by_padding;
100103 bool reorder_moov;
101104 bool moov_was_mooved;
102105 bool prevent_dynamic_update;
103106
104107 uint32_t optimization_flags;
105
108
106109 uint64_t padding_bytes;
107110 short consolidated_padding_insertion;
108111
109 AtomicInfo* last_trak_child_atom;
110 AtomicInfo* moov_atom;
111 AtomicInfo* moov_udta_atom;
112 AtomicInfo* iTunes_list_handler_atom;
113 AtomicInfo* moov_meta_atom;
114 AtomicInfo* file_meta_atom;
115 AtomicInfo* first_mdat_atom;
116 AtomicInfo* first_movielevel_metadata_tagging_atom;
117 AtomicInfo* initial_update_atom;
118 AtomicInfo* first_otiose_freespace_atom;
119 AtomicInfo* padding_store;
120 AtomicInfo* padding_resevoir;
121 FreeAtomListing* first_padding_atom;
122 FreeAtomListing* last_padding_atom;
112 AtomicInfo *last_trak_child_atom;
113 AtomicInfo *moov_atom;
114 AtomicInfo *moov_udta_atom;
115 AtomicInfo *iTunes_list_handler_atom;
116 AtomicInfo *moov_meta_atom;
117 AtomicInfo *file_meta_atom;
118 AtomicInfo *first_mdat_atom;
119 AtomicInfo *first_movielevel_metadata_tagging_atom;
120 AtomicInfo *initial_update_atom;
121 AtomicInfo *first_otiose_freespace_atom;
122 AtomicInfo *padding_store;
123 AtomicInfo *padding_resevoir;
124 FreeAtomListing *first_padding_atom;
125 FreeAtomListing *last_padding_atom;
123126 };
124127
125128 struct padding_preferences {
128131 uint32_t maximum_present_padding_size;
129132 };
130133
131 // Structure that defines the known atoms used by mpeg-4 family of specifications.
132 typedef struct {
133 const char* known_atom_name;
134 const char* known_parent_atoms[5]; //max known to be tested
135 uint32_t container_state;
136 int presence_requirements;
137 uint32_t box_type;
134 // Structure that defines the known atoms used by mpeg-4 family of
135 // specifications.
136 typedef struct {
137 const char *known_atom_name;
138 const char *known_parent_atoms[5]; // max known to be tested
139 uint32_t container_state;
140 int presence_requirements;
141 uint32_t box_type;
138142 } atomDefinition;
139143
140144 typedef struct {
141145 uint8_t uuid_form;
142 char* binary_uuid;
143 char* uuid_AP_atom_name;
146 char *binary_uuid;
147 char *uuid_AP_atom_name;
144148 } uuid_vitals;
145149
146150 enum {
147 PARENT_ATOM = 0, //container atom
148 SIMPLE_PARENT_ATOM = 1,
149 DUAL_STATE_ATOM = 2, //acts as both parent (contains other atoms) & child (carries data)
150 CHILD_ATOM = 3, //atom that does NOT contain any children
151 UNKNOWN_ATOM_TYPE = 4
152 };
153
154 enum {
155 REQUIRED_ONCE = 30, //means total of 1 atom per file (or total of 1 if parent atom is required to be present)
156 REQUIRED_ONE = 31, //means 1 atom per container atom; totalling many per file (or required present if optional parent atom is present)
157 REQUIRED_VARIABLE = 32, //means 1 or more atoms per container atom are required to be present
158 PARENT_SPECIFIC = 33, //means (iTunes-style metadata) the atom defines how many are present; most are MAX 1 'data' atoms; 'covr' is ?unlimited?
159 OPTIONAL_ONCE = 34, //means total of 1 atom per file, but not required
160 OPTIONAL_ONE = 35, //means 1 atom per container atom but not required; many may be present in a file
161 OPTIONAL_MANY = 36, //means more than 1 occurrence per container atom
162 REQ_FAMILIAL_ONE = OPTIONAL_ONE, //means that one of the family of atoms defined by the spec is required by the parent atom
163 UKNOWN_REQUIREMENTS= 38
151 PARENT_ATOM = 0, // container atom
152 SIMPLE_PARENT_ATOM = 1,
153 DUAL_STATE_ATOM =
154 2, // acts as both parent (contains other atoms) & child (carries data)
155 CHILD_ATOM = 3, // atom that does NOT contain any children
156 UNKNOWN_ATOM_TYPE = 4
157 };
158
159 enum {
160 REQUIRED_ONCE = 30, // means total of 1 atom per file (or total of 1 if
161 // parent atom is required to be present)
162 REQUIRED_ONE = 31, // means 1 atom per container atom; totalling many per file
163 // (or required present if optional parent atom is present)
164 REQUIRED_VARIABLE =
165 32, // means 1 or more atoms per container atom are required to be present
166 PARENT_SPECIFIC =
167 33, // means (iTunes-style metadata) the atom defines how many are
168 // present; most are MAX 1 'data' atoms; 'covr' is ?unlimited?
169 OPTIONAL_ONCE = 34, // means total of 1 atom per file, but not required
170 OPTIONAL_ONE = 35, // means 1 atom per container atom but not required; many
171 // may be present in a file
172 OPTIONAL_MANY = 36, // means more than 1 occurrence per container atom
173 REQ_FAMILIAL_ONE =
174 OPTIONAL_ONE, // means that one of the family of atoms defined by the spec
175 // is required by the parent atom
176 UKNOWN_REQUIREMENTS = 38
164177 };
165178
166179 enum {
182195 };
183196
184197 typedef struct {
185 const char* stik_string;
186 uint8_t stik_number;
198 const char *stik_string;
199 uint8_t stik_number;
187200 } stiks;
188201
189202 typedef struct {
190 const char* storefront_string;
191 uint32_t storefront_number;
203 const char *storefront_string;
204 uint32_t storefront_number;
192205 } sfIDs;
193206
194207 typedef struct {
195 const char* iso639_2_code;
196 const char* iso639_1_code;
197 const char* language_in_english;
208 const char *iso639_2_code;
209 const char *iso639_1_code;
210 const char *language_in_english;
198211 } iso639_lang;
199212
200213 typedef struct {
201 const char* media_rating;
202 const char* media_rating_cli_str;
214 const char *media_rating;
215 const char *media_rating_cli_str;
203216 } m_ratings;
204217
205218 typedef struct {
206 const char* genre_id_movie_string;
219 const char *genre_id_movie_string;
207220 uint16_t genre_id_movie_value;
208221 } geIDMovie;
209222
210223 typedef struct {
211 const char* genre_id_tv_string;
224 const char *genre_id_tv_string;
212225 uint16_t genre_id_tv_value;
213226 } geIDTV;
214227
215 enum {
216 UNIVERSAL_UTF8,
217 WIN32_UTF16
218 };
219
220 enum {
221 FORCE_M4B_TYPE = 85,
222 NO_TYPE_FORCING = 90
223 };
224
225 enum {
226 FILE_LEVEL_ATOM,
227 MOVIE_LEVEL_ATOM,
228 ALL_TRACKS_ATOM,
229 SINGLE_TRACK_ATOM
230 };
228 enum { UNIVERSAL_UTF8, WIN32_UTF16 };
229
230 enum { FORCE_M4B_TYPE = 85, NO_TYPE_FORCING = 90 };
231
232 enum { FILE_LEVEL_ATOM, MOVIE_LEVEL_ATOM, ALL_TRACKS_ATOM, SINGLE_TRACK_ATOM };
231233
232234 enum {
233235 UUID_DEPRECATED_FORM,
236238 UUID_OTHER
237239 };
238240
239
240241 /* Declarations of functions and data types used for SHA1 sum
241242 library functions.
242243 Copyright (C) 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
243244 */
244245
245246 typedef struct {
246 uint32_t time_low;
247 uint16_t time_mid;
248 uint16_t time_hi_and_version;
249 uint8_t clock_seq_hi_and_reserved;
250 uint8_t clock_seq_low;
251 unsigned char node[6];
247 uint32_t time_low;
248 uint16_t time_mid;
249 uint16_t time_hi_and_version;
250 uint8_t clock_seq_hi_and_reserved;
251 uint8_t clock_seq_low;
252 unsigned char node[6];
252253 } ap_uuid_t;
253254
254255 typedef uint32_t md5_uint32;
255256
256257 /* Structure to save state of computation between the single steps. */
257 struct sha1_ctx
258 {
258 struct sha1_ctx {
259259 md5_uint32 A;
260260 md5_uint32 B;
261261 md5_uint32 C;
264264
265265 md5_uint32 total[2];
266266 md5_uint32 buflen;
267 char buffer[128]; //char buffer[128] __attribute__ ((__aligned__ (__alignof__ (md5_uint32))));
268 };
269
270 typedef struct { //if any of these are unused, they are set to 0xFF
267 char buffer[128]; // char buffer[128] __attribute__ ((__aligned__ (__alignof__
268 // (md5_uint32))));
269 };
270
271 typedef struct { // if any of these are unused, they are set to 0xFF
271272 uint8_t od_profile_level;
272273 uint8_t scene_profile_level;
273274 uint8_t audio_profile;
283284 unsigned char unpacked_lang[4];
284285 char track_hdlr_name[100];
285286 char encoder_name[100];
286
287
287288 uint32_t track_type;
288289 uint32_t track_codec;
289290 uint32_t protected_codec;
290
291
291292 bool contains_esds;
292
293
293294 uint64_t section3_length;
294295 uint64_t section4_length;
295296 uint8_t ObjectTypeIndication;
298299 uint64_t section5_length;
299300 uint8_t descriptor_object_typeID;
300301 uint16_t channels;
301 uint64_t section6_length; //unused
302
303 //specifics
302 uint64_t section6_length; // unused
303
304 // specifics
304305 uint8_t m4v_profile;
305306 uint8_t avc_version;
306307 uint8_t profile;
310311 uint32_t macroblocks;
311312 uint64_t sample_aggregate;
312313 uint16_t amr_modes;
313
314
314315 uint8_t type_of_track;
315
316
316317 } TrackInfo;
317318
318319 typedef struct {
320321 uint64_t modified_time;
321322 uint32_t timescale;
322323 uint32_t duration;
323 uint32_t playback_rate; //fixed point 16.16
324 uint16_t volume; //fixed 8.8 point
325
324 uint32_t playback_rate; // fixed point 16.16
325 uint16_t volume; // fixed 8.8 point
326
326327 double seconds;
327328 double simple_bitrate_calc;
328
329
329330 bool contains_iods;
330331 } MovieInfo;
331332
360361 SMV_TRACK = 71
361362 };
362363
363 enum {
364 SHOW_TRACK_INFO = 2,
365 SHOW_DATE_INFO = 4
366 };
367
368 struct PicPrefs {
364 enum { SHOW_TRACK_INFO = 2, SHOW_DATE_INFO = 4 };
365
366 struct PicPrefs {
369367 int max_dimension;
370368 int dpi;
371369 int max_Kbytes;
379377 int force_width;
380378 };
381379
382
383
384
385380 #endif
386381
387382 /* vim:ts=2:sw=2:et:
388383 */
389
11 /*
22 AtomicParsley - arrays.cpp
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2005-2007 puck_lock
17 Copyright (C) 2005-2007 puck_lock
1818 with contributions from others; see the CREDITS file
19
20 ----------------------
19
20 ----------------------
2121 Code Contributions by:
22
22
2323 * Mellow_Flow - fix genre matching/verify genre limits
2424 */
2525 //==================================================================//
2828
2929 //////////////
3030
31 static const char* ID3v1GenreList[] = {
32 "Blues", "Classic Rock", "Country", "Dance", "Disco",
33 "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal",
34 "New Age", "Oldies", "Other", "Pop", "R&B",
35 "Rap", "Reggae", "Rock", "Techno", "Industrial",
36 "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack",
37 "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
38 "Fusion", "Trance", "Classical", "Instrumental", "Acid",
39 "House", "Game", "Sound Clip", "Gospel", "Noise",
40 "AlternRock", "Bass", "Soul", "Punk", "Space",
41 "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
42 "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance",
43 "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
44 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American",
45 "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes",
46 "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz",
47 "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
48 "Folk", "Folk/Rock", "National Folk", "Swing", "Fast Fusion",
49 "Bebob", "Latin", "Revival", "Celtic", "Bluegrass",
50 "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock",
51 "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic",
52 "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata",
53 "Symphony", "Booty Bass", "Primus", "Porn Groove",
54 "Satire", "Slow Jam", "Club", "Tango", "Samba",
55 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
56 "Duet", "Punk Rock", "Drum Solo", "A Capella", "Euro-House",
57 "Dance Hall" };
58 /*
59 "Goa", "Drum & Bass", "Club House", "Hardcore",
60 "Terror", "Indie", "BritPop", "NegerPunk", "Polsk Punk",
61 "Beat", "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
62 "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
63 "Anime", "JPop", "SynthPop",
31 static const char *ID3v1GenreList[] = {"Blues",
32 "Classic Rock",
33 "Country",
34 "Dance",
35 "Disco",
36 "Funk",
37 "Grunge",
38 "Hip-Hop",
39 "Jazz",
40 "Metal",
41 "New Age",
42 "Oldies",
43 "Other",
44 "Pop",
45 "R&B",
46 "Rap",
47 "Reggae",
48 "Rock",
49 "Techno",
50 "Industrial",
51 "Alternative",
52 "Ska",
53 "Death Metal",
54 "Pranks",
55 "Soundtrack",
56 "Euro-Techno",
57 "Ambient",
58 "Trip-Hop",
59 "Vocal",
60 "Jazz+Funk",
61 "Fusion",
62 "Trance",
63 "Classical",
64 "Instrumental",
65 "Acid",
66 "House",
67 "Game",
68 "Sound Clip",
69 "Gospel",
70 "Noise",
71 "AlternRock",
72 "Bass",
73 "Soul",
74 "Punk",
75 "Space",
76 "Meditative",
77 "Instrumental Pop",
78 "Instrumental Rock",
79 "Ethnic",
80 "Gothic",
81 "Darkwave",
82 "Techno-Industrial",
83 "Electronic",
84 "Pop-Folk",
85 "Eurodance",
86 "Dream",
87 "Southern Rock",
88 "Comedy",
89 "Cult",
90 "Gangsta",
91 "Top 40",
92 "Christian Rap",
93 "Pop/Funk",
94 "Jungle",
95 "Native American",
96 "Cabaret",
97 "New Wave",
98 "Psychadelic",
99 "Rave",
100 "Showtunes",
101 "Trailer",
102 "Lo-Fi",
103 "Tribal",
104 "Acid Punk",
105 "Acid Jazz",
106 "Polka",
107 "Retro",
108 "Musical",
109 "Rock & Roll",
110 "Hard Rock",
111 "Folk",
112 "Folk/Rock",
113 "National Folk",
114 "Swing",
115 "Fast Fusion",
116 "Bebob",
117 "Latin",
118 "Revival",
119 "Celtic",
120 "Bluegrass",
121 "Avantgarde",
122 "Gothic Rock",
123 "Progressive Rock",
124 "Psychedelic Rock",
125 "Symphonic Rock",
126 "Slow Rock",
127 "Big Band",
128 "Chorus",
129 "Easy Listening",
130 "Acoustic",
131 "Humour",
132 "Speech",
133 "Chanson",
134 "Opera",
135 "Chamber Music",
136 "Sonata",
137 "Symphony",
138 "Booty Bass",
139 "Primus",
140 "Porn Groove",
141 "Satire",
142 "Slow Jam",
143 "Club",
144 "Tango",
145 "Samba",
146 "Folklore",
147 "Ballad",
148 "Power Ballad",
149 "Rhythmic Soul",
150 "Freestyle",
151 "Duet",
152 "Punk Rock",
153 "Drum Solo",
154 "A Capella",
155 "Euro-House",
156 "Dance Hall"};
157 /*
158 "Goa", "Drum & Bass", "Club House", "Hardcore",
159 "Terror", "Indie", "BritPop", "NegerPunk", "Polsk Punk",
160 "Beat", "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover",
161 "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
162 "Anime", "JPop", "SynthPop",
64163 }; */ //apparently the other winamp id3v1 extensions aren't valid
65164
66 stiks stikArray[] = {
67 { "Home Video", 0 },
68 { "Normal", 1 },
69 { "Audiobook", 2 },
70 { "Whacked Bookmark", 5 },
71 { "Music Video", 6 },
72 { "Movie", 9 },
73 { "Short Film", 9 },
74 { "TV Show", 10 },
75 { "Booklet", 11 }
165 stiks stikArray[] = {{"Home Video", 0},
166 {"Normal", 1},
167 {"Audiobook", 2},
168 {"Whacked Bookmark", 5},
169 {"Music Video", 6},
170 {"Movie", 9},
171 {"Short Film", 9},
172 {"TV Show", 10},
173 {"Booklet", 11}};
174
175 geIDMovie genreidmovie[] = {{"Action & Adventure", 4401},
176 {"Anime", 4402},
177 {"Classics", 4403},
178 {"Comedy", 4404},
179 {"Documentary", 4405},
180 {"Drama", 4406},
181 {"Foreign", 4407},
182 {"Horror", 4408},
183 {"Independent", 4409},
184 {"Kids & Family", 4410},
185 {"Musicals", 4411},
186 {"Romance", 4412},
187 {"Sci-Fi & Fantasy", 4413},
188 {"Short Films", 4414},
189 {"Special Interest", 4415},
190 {"Thriller", 4416},
191 {"Sports", 4417},
192 {"Western", 4418},
193 {"Urban", 4419},
194 {"Holiday", 4420},
195 {"Made for TV", 4421},
196 {"Concert Films", 4422},
197 {"Music Documentaries", 4423},
198 {"Music Feature Films", 4424},
199 {"Japanese Cinema", 4425},
200 {"Jidaigeki", 4426},
201 {"Tokusatsu", 4427},
202 {"Korean Cinema", 4428}};
203
204 geIDTV genreidtv[] = {{"Comedy", 4000},
205 {"Drama", 4001},
206 {"Animation", 4002},
207 {"Action & Adventure", 4003},
208 {"Classic", 4004},
209 {"Kids", 4005},
210 {"Nonfiction", 4005},
211 {"Reality TV", 4007},
212 {"Sci-Fi & Fantasy", 4008},
213 {"Sports", 4009},
214 {"Teens", 4010},
215 {"Latino TV", 4011}};
216
217 // from William Herrera:
218 // http://search.cpan.org/src/BILLH/LWP-UserAgent-iTMS_Client-0.16/lib/LWP/UserAgent/iTMS_Client.pm
219 sfIDs storefronts[] = {
220 {"United States", 143441}, {"France", 143442}, {"Germany", 143443},
221 {"United Kingdom", 143444}, {"Austria", 143445}, {"Belgium", 143446},
222 {"Finland", 143447}, {"Greece", 143448}, {"Ireland", 143449},
223 {"Italy", 143450}, {"Luxembourg", 143451}, {"Netherlands", 143452},
224 {"Portugal", 143453}, {"Spain", 143454}, {"Canada", 143455},
225 {"Sweden", 143456}, {"Norway", 143457}, {"Denmark", 143458},
226 {"Switzerland", 143459}, {"Australia", 143460}, {"New Zealand", 143461},
227 {"Japan", 143462}};
228
229 iso639_lang known_languages[] = {
230 {"aar", "aa", "Afar"},
231 {"abk", "ab", "Abkhazian"},
232 {"ace", NULL, "Achinese"},
233 {"ach", NULL, "Acoli"},
234 {"ada", NULL, "Adangme"},
235 {"ady", NULL, "Adyghe; Adygei"},
236 {"afa", NULL, "Afro-Asiatic (Other)"},
237 {"afh", NULL, "Afrihili"},
238 {"afr", "af", "Afrikaans"},
239 {"ain", NULL, "Ainu"},
240 {"aka", "ak", "Akan"},
241 {"akk", NULL, "Akkadian"},
242 {"alb/sqi", "sq", "Albanian"}, // dual codes
243 {"ale", NULL, "Aleut"},
244 {"alg", NULL, "Algonquian languages"},
245 {"alt", NULL, "Southern Altai"},
246 {"amh", "am", "Amharic"},
247 {"ang", NULL, "English, Old (ca.450-1100)"},
248 {"anp", NULL, "Angika"},
249 {"apa", NULL, "Apache languages"},
250 {"ara", "ar", "Arabic"},
251 {"arc", NULL, "Aramaic"},
252 {"arg", "an", "Aragonese"},
253 {"arm/hye", "hy", "Armenian"}, // dual codes
254 {"arn", NULL, "Araucanian"},
255 {"arp", NULL, "Arapaho"},
256 {"art", NULL, "Artificial (Other)"},
257 {"arw", NULL, "Arawak"},
258 {"asm", "as", "Assamese"},
259 {"ast", NULL, "Asturian; Bable"},
260 {"ath", NULL, "Athapascan languages"},
261 {"aus", NULL, "Australian languages"},
262 {"ava", "av", "Avaric"},
263 {"ave", "ae", "Avestan"},
264 {"awa", NULL, "Awadhi"},
265 {"aym", "ay", "Aymara"},
266 {"aze", "az", "Azerbaijani"},
267 {"bad", NULL, "Banda"},
268 {"bai", NULL, "Bamileke languages"},
269 {"bak", "ba", "Bashkir"},
270 {"bal", NULL, "Baluchi"},
271 {"bam", "bm", "Bambara"},
272 {"ban", NULL, "Balinese"},
273 {"baq/eus", "eu", "Basque"}, // dual codes
274 {"bas", NULL, "Basa"},
275 {"bat", NULL, "Baltic (Other)"},
276 {"bej", NULL, "Beja"},
277 {"bel", "be", "Belarusian"},
278 {"bem", NULL, "Bemba"},
279 {"ben", "bn", "Bengali"},
280 {"ber", NULL, "Berber (Other)"},
281 {"bho", NULL, "Bhojpuri"},
282 {"bih", "bh", "Bihari"},
283 {"bik", NULL, "Bikol"},
284 {"bin", NULL, "Bini"},
285 {"bis", "bi", "Bislama"},
286 {"bla", NULL, "Siksika"},
287 {"bnt", NULL, "Bantu (Other)"},
288 {"bos", "bs", "Bosnian"},
289 {"bra", NULL, "Braj"},
290 {"bre", "br", "Breton"},
291 {"btk", NULL, "Batak (Indonesia)"},
292 {"bua", NULL, "Buriat"},
293 {"bug", NULL, "Buginese"},
294 {"bul", "bg", "Bulgarian"},
295 {"bur/mya", "my", "Burmese"}, // dual codes
296 {"byn", NULL, "Blin; Bilin"},
297 {"cad", NULL, "Caddo"},
298 {"cai", NULL, "Central American Indian (Other)"},
299 {"car", NULL, "Carib"},
300 {"cat", "ca", "Catalan; Valencian"},
301 {"cau", NULL, "Caucasian (Other)"},
302 {"ceb", NULL, "Cebuano"},
303 {"cel", NULL, "Celtic (Other)"},
304 {"cha", "ch", "Chamorro"},
305 {"chb", NULL, "Chibcha"},
306 {"che", "ce", "Chechen"},
307 {"chg", NULL, "Chagatai"},
308 {"chk", NULL, "Chuukese"},
309 {"chm", NULL, "Mari"},
310 {"chn", NULL, "Chinook jargon"},
311 {"cho", NULL, "Choctaw"},
312 {"chp", NULL, "Chipewyan"},
313 {"chr", NULL, "Cherokee"},
314 {"chu",
315 "cu",
316 "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church "
317 "Slavonic"},
318 {"chv", "cv", "Chuvash"},
319 {"chy", NULL, "Cheyenne"},
320 {"cmc", NULL, "Chamic languages"},
321 {"cop", NULL, "Coptic"},
322 {"cor", "kw", "Cornish"},
323 {"cos", "co", "Corsican"},
324 {"cpe", NULL, "Creoles and pidgins, English based (Other)"},
325 {"cpf", NULL, "Creoles and pidgins, French-based (Other)"},
326 {"cpp", NULL, "Creoles and pidgins, Portuguese-based (Other)"},
327 {"cre", "cr", "Cree"},
328 {"crh", NULL, "Crimean Tatar; Crimean Turkish"},
329 {"crp", NULL, "Creoles and pidgins (Other)"},
330 {"csb", NULL, "Kashubian"},
331 {"cus", NULL, "Cushitic (Other)"},
332 {"cze/ces", "cs", "Czech"}, // dual codes
333 {"dak", NULL, "Dakota"},
334 {"dan", "da", "Danish"},
335 {"dar", NULL, "Dargwa"},
336 {"day", NULL, "Dayak"},
337 {"del", NULL, "Delaware"},
338 {"den", NULL, "Slave (Athapascan)"},
339 {"dgr", NULL, "Dogrib"},
340 {"din", NULL, "Dinka"},
341 {"div", "dv", "Divehi; Dhivehi; Maldivian"},
342 {"doi", NULL, "Dogri"},
343 {"dra", NULL, "Dravidian (Other)"},
344 {"dsb", NULL, "Lower Sorbian"},
345 {"dua", NULL, "Duala"},
346 {"dum", NULL, "Dutch, Middle (ca.1050-1350)"},
347 {"dut/nld", "nl", "Dutch; Flemish"}, // dual codes
348 {"dyu", NULL, "Dyula"},
349 {"dzo", "dz", "Dzongkha"},
350 {"efi", NULL, "Efik"},
351 {"egy", NULL, "Egyptian (Ancient)"},
352 {"eka", NULL, "Ekajuk"},
353 {"elx", NULL, "Elamite"},
354 {"eng", "en", "English"},
355 {"enm", NULL, "English, Middle (1100-1500)"},
356 {"epo", "eo", "Esperanto"},
357 {"est", "et", "Estonian"},
358 {"ewe", "ee", "Ewe"},
359 {"ewo", NULL, "Ewondo"},
360 {"fan", NULL, "Fang"},
361 {"fao", "fo", "Faroese"},
362 {"fat", NULL, "Fanti"},
363 {"fij", "fj", "Fijian"},
364 {"fil", NULL, "Filipino; Pilipino"},
365 {"fin", "fi", "Finnish"},
366 {"fiu", NULL, "Finno-Ugrian (Other)"},
367 {"fon", NULL, "Fon"},
368 {"fre/fra", "fr", "French"}, // dual codes
369 {"frm", NULL, "French, Middle (ca.1400-1600)"},
370 {"fro", NULL, "French, Old (842-ca.1400)"},
371 {"frr", NULL, "Northern Frisian"},
372 {"frs", NULL, "Eastern Frisian"},
373 {"fry", "fy", "Western Frisian"},
374 {"ful", "ff", "Fulah"},
375 {"fur", NULL, "Friulian"},
376 {"gaa", NULL, "Ga"},
377 {"gay", NULL, "Gayo"},
378 {"gba", NULL, "Gbaya"},
379 {"gem", NULL, "Germanic (Other)"},
380 {"geo/kat", "ka", "Georgian"}, // dual codes
381 {"ger/deu", "de", "German"}, // dual codes
382 {"gez", NULL, "Geez"},
383 {"gil", NULL, "Gilbertese"},
384 {"gla", "gd", "Gaelic; Scottish Gaelic"},
385 {"gle", "ga", "Irish"},
386 {"glg", "gl", "Galician"},
387 {"glv", "gv", "Manx"},
388 {"gmh", NULL, "German, Middle High (ca.1050-1500)"},
389 {"goh", NULL, "German, Old High (ca.750-1050)"},
390 {"gon", NULL, "Gondi"},
391 {"gor", NULL, "Gorontalo"},
392 {"got", NULL, "Gothic"},
393 {"grb", NULL, "Grebo"},
394 {"grc", NULL, "Greek, Ancient (to 1453)"},
395 {"gre/ell", "el", "Greek, Modern (1453-)"}, // dual codes
396 {"grn", "gn", "Guarani"},
397 {"gsw", NULL, "Alemanic; Swiss German"},
398 {"guj", "gu", "Gujarati"},
399 {"gwi", NULL, "Gwich\u00abin"},
400 {"hai", NULL, "Haida"},
401 {"hat", "ht", "Haitian; Haitian Creole"},
402 {"hau", "ha", "Hausa"},
403 {"haw", NULL, "Hawaiian"},
404 {"heb", "he", "Hebrew"},
405 {"her", "hz", "Herero"},
406 {"hil", NULL, "Hiligaynon"},
407 {"him", NULL, "Himachali"},
408 {"hin", "hi", "Hindi"},
409 {"hit", NULL, "Hittite"},
410 {"hmn", NULL, "Hmong"},
411 {"hmo", "ho", "Hiri Motu"},
412 {"hsb", NULL, "Upper Sorbian"},
413 {"hun", "hu", "Hungarian"},
414 {"hup", NULL, "Hupa"},
415 {"arm/hye", "hy", "Armenian"},
416 {"iba", NULL, "Iban"},
417 {"ibo", "ig", "Igbo"},
418 {"ice/isl", "is", "Icelandic"}, // dual codes
419 {"ido", "io", "Ido"},
420 {"iii", "ii", "Sichuan Yi"},
421 {"ijo", NULL, "Ijo"},
422 {"iku", "iu", "Inuktitut"},
423 {"ile", "ie", "Interlingue"},
424 {"ilo", NULL, "Iloko"},
425 {"ina",
426 "ia",
427 "Interlingua (International Auxiliary, Language Association)"},
428 {"inc", NULL, "Indic (Other)"},
429 {"ind", "id", "Indonesian"},
430 {"ine", NULL, "Indo-European (Other)"},
431 {"inh", NULL, "Ingush"},
432 {"ipk", "ik", "Inupiaq"},
433 {"ira", NULL, "Iranian (Other)"},
434 {"iro", NULL, "Iroquoian languages"},
435 {"ita", "it", "Italian"},
436 {"jav", "jv", "Javanese"},
437 {"jbo", NULL, "Lojban"},
438 {"jpn", "ja", "Japanese"},
439 {"jpr", NULL, "Judeo-Persian"},
440 {"jrb", NULL, "Judeo-Arabic"},
441 {"kaa", NULL, "Kara-Kalpak"},
442 {"kab", NULL, "Kabyle"},
443 {"kac", NULL, "Kachin"},
444 {"kal", "kl", "Kalaallisut; Greenlandic"},
445 {"kam", NULL, "Kamba"},
446 {"kan", "kn", "Kannada"},
447 {"kar", NULL, "Karen"},
448 {"kas", "ks", "Kashmiri"},
449 {"kau", "kr", "Kanuri"},
450 {"kaw", NULL, "Kawi"},
451 {"kaz", "kk", "Kazakh"},
452 {"kbd", NULL, "Kabardian"},
453 {"kha", NULL, "Khasi"},
454 {"khi", NULL, "Khoisan (Other)"},
455 {"khm", "km", "Khmer"},
456 {"kho", NULL, "Khotanese"},
457 {"kik", "ki", "Kikuyu; Gikuyu"},
458 {"kin", "rw", "Kinyarwanda"},
459 {"kir", "ky", "Kirghiz"},
460 {"kmb", NULL, "Kimbundu"},
461 {"kok", NULL, "Konkani"},
462 {"kom", "kv", "Komi"},
463 {"kon", "kg", "Kongo"},
464 {"kor", "ko", "Korean"},
465 {"kos", NULL, "Kosraean"},
466 {"kpe", NULL, "Kpelle"},
467 {"krc", NULL, "Karachay-Balkar"},
468 {"krl", NULL, "Karelian"},
469 {"kro", NULL, "Kru"},
470 {"kru", NULL, "Kurukh"},
471 {"kua", "kj", "Kuanyama; Kwanyama"},
472 {"kum", NULL, "Kumyk"},
473 {"kur", "ku", "Kurdish"},
474 {"kut", NULL, "Kutenai"},
475 {"lad", NULL, "Ladino"},
476 {"lah", NULL, "Lahnda"},
477 {"lam", NULL, "Lamba"},
478 {"lao", "lo", "Lao"},
479 {"lat", "la", "Latin"},
480 {"lav", "lv", "Latvian"},
481 {"lez", NULL, "Lezghian"},
482 {"lim", "li", "Limburgan; Limburger; Limburgish"},
483 {"lin", "ln", "Lingala"},
484 {"lit", "lt", "Lithuanian"},
485 {"lol", NULL, "Mongo"},
486 {"loz", NULL, "Lozi"},
487 {"ltz", "lb", "Luxembourgish; Letzeburgesch"},
488 {"lua", NULL, "Luba-Lulua"},
489 {"lub", "lu", "Luba-Katanga"},
490 {"lug", "lg", "Ganda"},
491 {"lui", NULL, "Luiseno"},
492 {"lun", NULL, "Lunda"},
493 {"luo", NULL, "Luo (Kenya and Tanzania)"},
494 {"lus", NULL, "Lushai"},
495 {"mad", NULL, "Madurese"},
496 {"mag", NULL, "Magahi"},
497 {"mah", "mh", "Marshallese"},
498 {"mai", NULL, "Maithili"},
499 {"mak", NULL, "Makasar"},
500 {"mal", "ml", "Malayalam"},
501 {"man", NULL, "Mandingo"},
502 {"map", NULL, "Austronesian (Other)"},
503 {"mar", "mr", "Marathi"},
504 {"mas", NULL, "Masai"},
505 {"may/msa", "ms", "Malay"}, // dual codes
506 {"mdf", NULL, "Moksha"},
507 {"mdr", NULL, "Mandar"},
508 {"men", NULL, "Mende"},
509 {"mga", NULL, "Irish, Middle (900-1200)"},
510 {"mic", NULL, "Mi'kmaq; Micmac"},
511 {"min", NULL, "Minangkabau"},
512 {"mis", NULL, "Miscellaneous languages"},
513 {"mac/mkd", "mk", "Macedonian"}, // dual codes
514 {"mkh", NULL, "Mon-Khmer (Other)"},
515 {"mlg", "mg", "Malagasy"},
516 {"mlt", "mt", "Maltese"},
517 {"mnc", NULL, "Manchu"},
518 {"mni", NULL, "Manipuri"},
519 {"mno", NULL, "Manobo languages"},
520 {"moh", NULL, "Mohawk"},
521 {"mol", "mo", "Moldavian"},
522 {"mon", "mn", "Mongolian"},
523 {"mos", NULL, "Mossi"},
524 {"mao/mri", "mi", "Maori"}, // dual codes
525 {"mul", NULL, "Multiple languages"},
526 {"mun", NULL, "Munda languages"},
527 {"mus", NULL, "Creek"},
528 {"mwl", NULL, "Mirandese"},
529 {"mwr", NULL, "Marwari"},
530 {"myn", NULL, "Mayan languages"},
531 {"myv", NULL, "Erzya"},
532 {"nah", NULL, "Nahuatl"},
533 {"nai", NULL, "North American Indian"},
534 {"nap", NULL, "Neapolitan"},
535 {"nau", "na", "Nauru"},
536 {"nav", "nv", "Navajo; Navaho"},
537 {"nbl", "nr", "Ndebele, South; South Ndebele"},
538 {"nde", "nd", "Ndebele, North; North Ndebele"},
539 {"ndo", "ng", "Ndonga"},
540 {"nds", NULL, "Low German; Low Saxon; German, Low; Saxon, Low"},
541 {"nep", "ne", "Nepali"},
542 {"new", NULL, "Newari; Nepal Bhasa"},
543 {"nia", NULL, "Nias"},
544 {"nic", NULL, "Niger-Kordofanian (Other)"},
545 {"niu", NULL, "Niuean"},
546 {"nno", "nn", "Norwegian Nynorsk; Nynorsk, Norwegian"},
547 {"nob", "nb", "Norwegian Bokm\x8cl; Bokm\x8cl, Norwegian"},
548 {"nog", NULL, "Nogai"},
549 {"non", NULL, "Norse, Old"},
550 {"nor", "no", "Norwegian"},
551 {"nqo", NULL, "N'ko"},
552 {"nso", NULL, "Northern Sotho, Pedi; Sepedi"},
553 {"nub", NULL, "Nubian languages"},
554 {"nwc", NULL, "Classical Newari; Old Newari; Classical Nepal Bhasa"},
555 {"nya", "ny", "Chichewa; Chewa; Nyanja"},
556 {"nym", NULL, "Nyamwezi"},
557 {"nyn", NULL, "Nyankole"},
558 {"nyo", NULL, "Nyoro"},
559 {"nzi", NULL, "Nzima"},
560 {"oci", "oc", "Occitan (post 1500); Proven\u00c7al"},
561 {"oji", "oj", "Ojibwa"},
562 {"ori", "or", "Oriya"},
563 {"orm", "om", "Oromo"},
564 {"osa", NULL, "Osage"},
565 {"oss", "os", "Ossetian; Ossetic"},
566 {"ota", NULL, "Turkish, Ottoman (1500-1928)"},
567 {"oto", NULL, "Otomian languages"},
568 {"paa", NULL, "Papuan (Other)"},
569 {"pag", NULL, "Pangasinan"},
570 {"pal", NULL, "Pahlavi"},
571 {"pam", NULL, "Pampanga"},
572 {"pan", "pa", "Panjabi; Punjabi"},
573 {"pap", NULL, "Papiamento"},
574 {"pau", NULL, "Palauan"},
575 {"peo", NULL, "Persian, Old (ca.600-400 B.C.)"},
576 {"per/fas", "fa", "Persian"}, // dual codes
577 {"phi", NULL, "Philippine (Other)"},
578 {"phn", NULL, "Phoenician"},
579 {"pli", "pi", "Pali"},
580 {"pol", "pl", "Polish"},
581 {"pon", NULL, "Pohnpeian"},
582 {"por", "pt", "Portuguese"},
583 {"pra", NULL, "Prakrit languages"},
584 {"pro", NULL, "Proven\u00c7al, Old (to 1500)"},
585 {"pus", "ps", "Pushto"},
586 //{ "qaa-qtz", NULL, "Reserved for local use" },
587 {"que", "qu", "Quechua"},
588 {"raj", NULL, "Rajasthani"},
589 {"rap", NULL, "Rapanui"},
590 {"rar", NULL, "Rarotongan"},
591 {"roa", NULL, "Romance (Other)"},
592 {"roh", "rm", "Raeto-Romance"},
593 {"rom", NULL, "Romany"},
594 {"rum/ron", "ro", "Romanian"}, // dual codes
595 {"run", "rn", "Rundi"},
596 {"rup", NULL, "Aromanian; Arumanian; Macedo-Romanian"},
597 {"rus", "ru", "Russian"},
598 {"sad", NULL, "Sandawe"},
599 {"sag", "sg", "Sango"},
600 {"sah", NULL, "Yakut"},
601 {"sai", NULL, "South American Indian (Other)"},
602 {"sal", NULL, "Salishan languages"},
603 {"sam", NULL, "Samaritan Aramaic"},
604 {"san", "sa", "Sanskrit"},
605 {"sas", NULL, "Sasak"},
606 {"sat", NULL, "Santali"},
607 {"scn", NULL, "Sicilian"},
608 {"sco", NULL, "Scots"},
609 {"scr/hrv", "hr", "Croatian"}, // dual codes
610 {"sel", NULL, "Selkup"},
611 {"sem", NULL, "Semitic (Other)"},
612 {"sga", NULL, "Irish, Old (to 900)"},
613 {"sgn", NULL, "Sign Languages"},
614 {"shn", NULL, "Shan"},
615 {"sid", NULL, "Sidamo"},
616 {"sin", "si", "Sinhala; Sinhalese"},
617 {"sio", NULL, "Siouan languages"},
618 {"sit", NULL, "Sino-Tibetan (Other)"},
619 {"sla", NULL, "Slavic (Other)"},
620 {"slo/slk", "sk", "Slovak"}, // dual codes
621 {"slv", "sl", "Slovenian"},
622 {"sma", NULL, "Southern Sami"},
623 {"sme", "se", "Northern Sami"},
624 {"smi", NULL, "Sami languages (Other)"},
625 {"smj", NULL, "Lule Sami"},
626 {"smn", NULL, "Inari Sami"},
627 {"smo", "sm", "Samoan"},
628 {"sms", NULL, "Skolt Sami"},
629 {"sna", "sn", "Shona"},
630 {"snd", "sd", "Sindhi"},
631 {"snk", NULL, "Soninke"},
632 {"sog", NULL, "Sogdian"},
633 {"som", "so", "Somali"},
634 {"son", NULL, "Songhai"},
635 {"sot", "st", "Sotho, Southern"},
636 {"spa", "es", "Spanish; Castilian"},
637 {"srd", "sc", "Sardinian"},
638 {"srn", NULL, "Sranan Togo"},
639 {"scc/srp", "sr", "Serbian"}, // dual codes
640 {"srr", NULL, "Serer"},
641 {"ssa", NULL, "Nilo-Saharan (Other)"},
642 {"ssw", "ss", "Swati"},
643 {"suk", NULL, "Sukuma"},
644 {"sun", "su", "Sundanese"},
645 {"sus", NULL, "Susu"},
646 {"sux", NULL, "Sumerian"},
647 {"swa", "sw", "Swahili"},
648 {"swe", "sv", "Swedish"},
649 {"syr", NULL, "Syriac"},
650 {"tah", "ty", "Tahitian"},
651 {"tai", NULL, "Tai (Other)"},
652 {"tam", "ta", "Tamil"},
653 {"tat", "tt", "Tatar"},
654 {"tel", "te", "Telugu"},
655 {"tem", NULL, "Timne"},
656 {"ter", NULL, "Tereno"},
657 {"tet", NULL, "Tetum"},
658 {"tgk", "tg", "Tajik"},
659 {"tgl", "tl", "Tagalog"},
660 {"tha", "th", "Thai"},
661 {"tib/bod", "bo", "Tibetan"}, // dual codes
662 {"tig", NULL, "Tigre"},
663 {"tir", "ti", "Tigrinya"},
664 {"tiv", NULL, "Tiv"},
665 {"tkl", NULL, "Tokelau"},
666 {"tlh", NULL, "Klingon; tlhIngan-Hol"},
667 {"tli", NULL, "Tlingit"},
668 {"tmh", NULL, "Tamashek"},
669 {"tog", NULL, "Tonga (Nyasa)"},
670 {"ton", "to", "Tonga (Tonga Islands)"},
671 {"tpi", NULL, "Tok Pisin"},
672 {"tsi", NULL, "Tsimshian"},
673 {"tsn", "tn", "Tswana"},
674 {"tso", "ts", "Tsonga"},
675 {"tuk", "tk", "Turkmen"},
676 {"tum", NULL, "Tumbuka"},
677 {"tup", NULL, "Tupi languages"},
678 {"tur", "tr", "Turkish"},
679 {"tut", NULL, "Altaic (Other)"},
680 {"tvl", NULL, "Tuvalu"},
681 {"twi", "tw", "Twi"},
682 {"tyv", NULL, "Tuvinian"},
683 {"udm", NULL, "Udmurt"},
684 {"uga", NULL, "Ugaritic"},
685 {"uig", "ug", "Uighur; Uyghur"},
686 {"ukr", "uk", "Ukrainian"},
687 {"umb", NULL, "Umbundu"},
688 {"und", NULL, "Undetermined"},
689 {"urd", "ur", "Urdu"},
690 {"uzb", "uz", "Uzbek"},
691 {"vai", NULL, "Vai"},
692 {"ven", "ve", "Venda"},
693 {"vie", "vi", "Vietnamese"},
694 {"vol", "vo", "Volap\u00fck"},
695 {"vot", NULL, "Votic"},
696 {"wak", NULL, "Wakashan languages"},
697 {"wal", NULL, "Walamo"},
698 {"war", NULL, "Waray"},
699 {"was", NULL, "Washo"},
700 {"wel/cym", "cy", "Welsh"}, // //dual codes
701 {"wen", NULL, "Sorbian languages"},
702 {"wln", "wa", "Walloon"},
703 {"wol", "wo", "Wolof"},
704 {"xal", NULL, "Kalmyk; Oirat"},
705 {"xho", "xh", "Xhosa"},
706 {"yao", NULL, "Yao"},
707 {"yap", NULL, "Yapese"},
708 {"yid", "yi", "Yiddish"},
709 {"yor", "yo", "Yoruba"},
710 {"ypk", NULL, "Yupik languages"},
711 {"zap", NULL, "Zapotec"},
712 {"zen", NULL, "Zenaga"},
713 {"zha", "za", "Zhuang; Chuang"},
714 {"chi/zho", "zh", "Chinese"}, // dual codes
715 {"znd", NULL, "Zande"},
716 {"zul", "zu", "Zulu"},
717 {"zun", NULL, "Zuni"},
718 {"zxx", NULL, "No linguistic content"}};
719
720 m_ratings known_ratings[] = {
721 {"us-tv|TV-MA|600|", "TV-MA"},
722 {"us-tv|TV-14|500|", "TV-14"},
723 {"us-tv|TV-PG|400|", "TV-PG"},
724 {"us-tv|TV-G|300|", "TV-G"},
725 {"us-tv|TV-Y7|200|", "TV-Y7"},
726 {"us-tv|TV-Y|100|", "TV-Y"},
727 //{ "us-tv||0|", "not-applicable" }, //though its a valid flag &
728 // some files have this, AP won't be setting it.
729 {"mpaa|UNRATED|600|", "Unrated"},
730 {"mpaa|NC-17|500|", "NC-17"},
731 {"mpaa|R|400|", "R"},
732 {"mpaa|PG-13|300|", "PG-13"},
733 {"mpaa|PG|200|", "PG"},
734 {"mpaa|G|100|", "G"}
735 //{ "mpaa||0|", "not-applicable" } //see above
76736 };
77737
78 geIDMovie genreidmovie[] = {
79 { "Action & Adventure", 4401 },
80 { "Anime", 4402 },
81 { "Classics", 4403 },
82 { "Comedy", 4404 },
83 { "Documentary", 4405 },
84 { "Drama", 4406 },
85 { "Foreign", 4407 },
86 { "Horror", 4408 },
87 { "Independent", 4409 },
88 { "Kids & Family", 4410 },
89 { "Musicals", 4411 },
90 { "Romance", 4412 },
91 { "Sci-Fi & Fantasy", 4413 },
92 { "Short Films", 4414 },
93 { "Special Interest", 4415 },
94 { "Thriller", 4416 },
95 { "Sports", 4417 },
96 { "Western", 4418 },
97 { "Urban", 4419 },
98 { "Holiday", 4420 },
99 { "Made for TV", 4421 },
100 { "Concert Films", 4422 },
101 { "Music Documentaries", 4423 },
102 { "Music Feature Films", 4424 },
103 { "Japanese Cinema", 4425 },
104 { "Jidaigeki", 4426 },
105 { "Tokusatsu", 4427 },
106 { "Korean Cinema", 4428 }
107 };
108
109 geIDTV genreidtv[] = {
110 { "Comedy", 4000 },
111 { "Drama", 4001 },
112 { "Animation", 4002 },
113 { "Action & Adventure", 4003 },
114 { "Classic", 4004 },
115 { "Kids", 4005 },
116 { "Nonfiction", 4005 },
117 { "Reality TV", 4007 },
118 { "Sci-Fi & Fantasy", 4008 },
119 { "Sports", 4009 },
120 { "Teens", 4010 },
121 { "Latino TV", 4011 }
122 };
123
124 // from William Herrera: http://search.cpan.org/src/BILLH/LWP-UserAgent-iTMS_Client-0.16/lib/LWP/UserAgent/iTMS_Client.pm
125 sfIDs storefronts[] = {
126 { "United States", 143441 },
127 { "France", 143442 },
128 { "Germany", 143443 },
129 { "United Kingdom", 143444 },
130 { "Austria", 143445 },
131 { "Belgium", 143446 },
132 { "Finland", 143447 },
133 { "Greece", 143448 },
134 { "Ireland", 143449 },
135 { "Italy", 143450 },
136 { "Luxembourg", 143451 },
137 { "Netherlands", 143452 },
138 { "Portugal", 143453 },
139 { "Spain", 143454 },
140 { "Canada", 143455 },
141 { "Sweden", 143456 },
142 { "Norway", 143457 },
143 { "Denmark", 143458 },
144 { "Switzerland", 143459 },
145 { "Australia", 143460 },
146 { "New Zealand", 143461 },
147 { "Japan", 143462 }
148 };
149
150 iso639_lang known_languages[] = {
151 { "aar", "aa", "Afar" },
152 { "abk", "ab", "Abkhazian" },
153 { "ace", NULL, "Achinese" },
154 { "ach", NULL, "Acoli" },
155 { "ada", NULL, "Adangme" },
156 { "ady", NULL, "Adyghe; Adygei" },
157 { "afa", NULL, "Afro-Asiatic (Other)" },
158 { "afh", NULL, "Afrihili" },
159 { "afr", "af", "Afrikaans" },
160 { "ain", NULL, "Ainu" },
161 { "aka", "ak", "Akan" },
162 { "akk", NULL, "Akkadian" },
163 { "alb/sqi", "sq", "Albanian" }, //dual codes
164 { "ale", NULL, "Aleut" },
165 { "alg", NULL, "Algonquian languages" },
166 { "alt", NULL, "Southern Altai" },
167 { "amh", "am", "Amharic" },
168 { "ang", NULL, "English, Old (ca.450-1100)" },
169 { "anp", NULL, "Angika" },
170 { "apa", NULL, "Apache languages" },
171 { "ara", "ar", "Arabic" },
172 { "arc", NULL, "Aramaic" },
173 { "arg", "an", "Aragonese" },
174 { "arm/hye", "hy", "Armenian" }, //dual codes
175 { "arn", NULL, "Araucanian" },
176 { "arp", NULL, "Arapaho" },
177 { "art", NULL, "Artificial (Other)" },
178 { "arw", NULL, "Arawak" },
179 { "asm", "as", "Assamese" },
180 { "ast", NULL, "Asturian; Bable" },
181 { "ath", NULL, "Athapascan languages" },
182 { "aus", NULL, "Australian languages" },
183 { "ava", "av", "Avaric" },
184 { "ave", "ae", "Avestan" },
185 { "awa", NULL, "Awadhi" },
186 { "aym", "ay", "Aymara" },
187 { "aze", "az", "Azerbaijani" },
188 { "bad", NULL, "Banda" },
189 { "bai", NULL, "Bamileke languages" },
190 { "bak", "ba", "Bashkir" },
191 { "bal", NULL, "Baluchi" },
192 { "bam", "bm", "Bambara" },
193 { "ban", NULL, "Balinese" },
194 { "baq/eus", "eu", "Basque" }, //dual codes
195 { "bas", NULL, "Basa" },
196 { "bat", NULL, "Baltic (Other)" },
197 { "bej", NULL, "Beja" },
198 { "bel", "be", "Belarusian" },
199 { "bem", NULL, "Bemba" },
200 { "ben", "bn", "Bengali" },
201 { "ber", NULL, "Berber (Other)" },
202 { "bho", NULL, "Bhojpuri" },
203 { "bih", "bh", "Bihari" },
204 { "bik", NULL, "Bikol" },
205 { "bin", NULL, "Bini" },
206 { "bis", "bi", "Bislama" },
207 { "bla", NULL, "Siksika" },
208 { "bnt", NULL, "Bantu (Other)" },
209 { "bos", "bs", "Bosnian" },
210 { "bra", NULL, "Braj" },
211 { "bre", "br", "Breton" },
212 { "btk", NULL, "Batak (Indonesia)" },
213 { "bua", NULL, "Buriat" },
214 { "bug", NULL, "Buginese" },
215 { "bul", "bg", "Bulgarian" },
216 { "bur/mya", "my", "Burmese" }, //dual codes
217 { "byn", NULL, "Blin; Bilin" },
218 { "cad", NULL, "Caddo" },
219 { "cai", NULL, "Central American Indian (Other)" },
220 { "car", NULL, "Carib" },
221 { "cat", "ca", "Catalan; Valencian" },
222 { "cau", NULL, "Caucasian (Other)" },
223 { "ceb", NULL, "Cebuano" },
224 { "cel", NULL, "Celtic (Other)" },
225 { "cha", "ch", "Chamorro" },
226 { "chb", NULL, "Chibcha" },
227 { "che", "ce", "Chechen" },
228 { "chg", NULL, "Chagatai" },
229 { "chk", NULL, "Chuukese" },
230 { "chm", NULL, "Mari" },
231 { "chn", NULL, "Chinook jargon" },
232 { "cho", NULL, "Choctaw" },
233 { "chp", NULL, "Chipewyan" },
234 { "chr", NULL, "Cherokee" },
235 { "chu", "cu", "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" },
236 { "chv", "cv", "Chuvash" },
237 { "chy", NULL, "Cheyenne" },
238 { "cmc", NULL, "Chamic languages" },
239 { "cop", NULL, "Coptic" },
240 { "cor", "kw", "Cornish" },
241 { "cos", "co", "Corsican" },
242 { "cpe", NULL, "Creoles and pidgins, English based (Other)" },
243 { "cpf", NULL, "Creoles and pidgins, French-based (Other)" },
244 { "cpp", NULL, "Creoles and pidgins, Portuguese-based (Other)" },
245 { "cre", "cr", "Cree" },
246 { "crh", NULL, "Crimean Tatar; Crimean Turkish" },
247 { "crp", NULL, "Creoles and pidgins (Other)" },
248 { "csb", NULL, "Kashubian" },
249 { "cus", NULL, "Cushitic (Other)" },
250 { "cze/ces", "cs", "Czech" }, //dual codes
251 { "dak", NULL, "Dakota" },
252 { "dan", "da", "Danish" },
253 { "dar", NULL, "Dargwa" },
254 { "day", NULL, "Dayak" },
255 { "del", NULL, "Delaware" },
256 { "den", NULL, "Slave (Athapascan)" },
257 { "dgr", NULL, "Dogrib" },
258 { "din", NULL, "Dinka" },
259 { "div", "dv", "Divehi; Dhivehi; Maldivian" },
260 { "doi", NULL, "Dogri" },
261 { "dra", NULL, "Dravidian (Other)" },
262 { "dsb", NULL, "Lower Sorbian" },
263 { "dua", NULL, "Duala" },
264 { "dum", NULL, "Dutch, Middle (ca.1050-1350)" },
265 { "dut/nld", "nl", "Dutch; Flemish" }, //dual codes
266 { "dyu", NULL, "Dyula" },
267 { "dzo", "dz", "Dzongkha" },
268 { "efi", NULL, "Efik" },
269 { "egy", NULL, "Egyptian (Ancient)" },
270 { "eka", NULL, "Ekajuk" },
271 { "elx", NULL, "Elamite" },
272 { "eng", "en", "English" },
273 { "enm", NULL, "English, Middle (1100-1500)" },
274 { "epo", "eo", "Esperanto" },
275 { "est", "et", "Estonian" },
276 { "ewe", "ee", "Ewe" },
277 { "ewo", NULL, "Ewondo" },
278 { "fan", NULL, "Fang" },
279 { "fao", "fo", "Faroese" },
280 { "fat", NULL, "Fanti" },
281 { "fij", "fj", "Fijian" },
282 { "fil", NULL, "Filipino; Pilipino" },
283 { "fin", "fi", "Finnish" },
284 { "fiu", NULL, "Finno-Ugrian (Other)" },
285 { "fon", NULL, "Fon" },
286 { "fre/fra", "fr", "French" }, //dual codes
287 { "frm", NULL, "French, Middle (ca.1400-1600)" },
288 { "fro", NULL, "French, Old (842-ca.1400)" },
289 { "frr", NULL, "Northern Frisian" },
290 { "frs", NULL, "Eastern Frisian" },
291 { "fry", "fy", "Western Frisian" },
292 { "ful", "ff", "Fulah" },
293 { "fur", NULL, "Friulian" },
294 { "gaa", NULL, "Ga" },
295 { "gay", NULL, "Gayo" },
296 { "gba", NULL, "Gbaya" },
297 { "gem", NULL, "Germanic (Other)" },
298 { "geo/kat", "ka", "Georgian" }, //dual codes
299 { "ger/deu", "de", "German" }, //dual codes
300 { "gez", NULL, "Geez" },
301 { "gil", NULL, "Gilbertese" },
302 { "gla", "gd", "Gaelic; Scottish Gaelic" },
303 { "gle", "ga", "Irish" },
304 { "glg", "gl", "Galician" },
305 { "glv", "gv", "Manx" },
306 { "gmh", NULL, "German, Middle High (ca.1050-1500)" },
307 { "goh", NULL, "German, Old High (ca.750-1050)" },
308 { "gon", NULL, "Gondi" },
309 { "gor", NULL, "Gorontalo" },
310 { "got", NULL, "Gothic" },
311 { "grb", NULL, "Grebo" },
312 { "grc", NULL, "Greek, Ancient (to 1453)" },
313 { "gre/ell", "el", "Greek, Modern (1453-)" }, //dual codes
314 { "grn", "gn", "Guarani" },
315 { "gsw", NULL, "Alemanic; Swiss German" },
316 { "guj", "gu", "Gujarati" },
317 { "gwi", NULL, "Gwich«in" },
318 { "hai", NULL, "Haida" },
319 { "hat", "ht", "Haitian; Haitian Creole" },
320 { "hau", "ha", "Hausa" },
321 { "haw", NULL, "Hawaiian" },
322 { "heb", "he", "Hebrew" },
323 { "her", "hz", "Herero" },
324 { "hil", NULL, "Hiligaynon" },
325 { "him", NULL, "Himachali" },
326 { "hin", "hi", "Hindi" },
327 { "hit", NULL, "Hittite" },
328 { "hmn", NULL, "Hmong" },
329 { "hmo", "ho", "Hiri Motu" },
330 { "hsb", NULL, "Upper Sorbian" },
331 { "hun", "hu", "Hungarian" },
332 { "hup", NULL, "Hupa" },
333 { "arm/hye", "hy", "Armenian" },
334 { "iba", NULL, "Iban" },
335 { "ibo", "ig", "Igbo" },
336 { "ice/isl", "is", "Icelandic" }, //dual codes
337 { "ido", "io", "Ido" },
338 { "iii", "ii", "Sichuan Yi" },
339 { "ijo", NULL, "Ijo" },
340 { "iku", "iu", "Inuktitut" },
341 { "ile", "ie", "Interlingue" },
342 { "ilo", NULL, "Iloko" },
343 { "ina", "ia", "Interlingua (International Auxiliary, Language Association)" },
344 { "inc", NULL, "Indic (Other)" },
345 { "ind", "id", "Indonesian" },
346 { "ine", NULL, "Indo-European (Other)" },
347 { "inh", NULL, "Ingush" },
348 { "ipk", "ik", "Inupiaq" },
349 { "ira", NULL, "Iranian (Other)" },
350 { "iro", NULL, "Iroquoian languages" },
351 { "ita", "it", "Italian" },
352 { "jav", "jv", "Javanese" },
353 { "jbo", NULL, "Lojban" },
354 { "jpn", "ja", "Japanese" },
355 { "jpr", NULL, "Judeo-Persian" },
356 { "jrb", NULL, "Judeo-Arabic" },
357 { "kaa", NULL, "Kara-Kalpak" },
358 { "kab", NULL, "Kabyle" },
359 { "kac", NULL, "Kachin" },
360 { "kal", "kl", "Kalaallisut; Greenlandic" },
361 { "kam", NULL, "Kamba" },
362 { "kan", "kn", "Kannada" },
363 { "kar", NULL, "Karen" },
364 { "kas", "ks", "Kashmiri" },
365 { "kau", "kr", "Kanuri" },
366 { "kaw", NULL, "Kawi" },
367 { "kaz", "kk", "Kazakh" },
368 { "kbd", NULL, "Kabardian" },
369 { "kha", NULL, "Khasi" },
370 { "khi", NULL, "Khoisan (Other)" },
371 { "khm", "km", "Khmer" },
372 { "kho", NULL, "Khotanese" },
373 { "kik", "ki", "Kikuyu; Gikuyu" },
374 { "kin", "rw", "Kinyarwanda" },
375 { "kir", "ky", "Kirghiz" },
376 { "kmb", NULL, "Kimbundu" },
377 { "kok", NULL, "Konkani" },
378 { "kom", "kv", "Komi" },
379 { "kon", "kg", "Kongo" },
380 { "kor", "ko", "Korean" },
381 { "kos", NULL, "Kosraean" },
382 { "kpe", NULL, "Kpelle" },
383 { "krc", NULL, "Karachay-Balkar" },
384 { "krl", NULL, "Karelian" },
385 { "kro", NULL, "Kru" },
386 { "kru", NULL, "Kurukh" },
387 { "kua", "kj", "Kuanyama; Kwanyama" },
388 { "kum", NULL, "Kumyk" },
389 { "kur", "ku", "Kurdish" },
390 { "kut", NULL, "Kutenai" },
391 { "lad", NULL, "Ladino" },
392 { "lah", NULL, "Lahnda" },
393 { "lam", NULL, "Lamba" },
394 { "lao", "lo", "Lao" },
395 { "lat", "la", "Latin" },
396 { "lav", "lv", "Latvian" },
397 { "lez", NULL, "Lezghian" },
398 { "lim", "li", "Limburgan; Limburger; Limburgish" },
399 { "lin", "ln", "Lingala" },
400 { "lit", "lt", "Lithuanian" },
401 { "lol", NULL, "Mongo" },
402 { "loz", NULL, "Lozi" },
403 { "ltz", "lb", "Luxembourgish; Letzeburgesch" },
404 { "lua", NULL, "Luba-Lulua" },
405 { "lub", "lu", "Luba-Katanga" },
406 { "lug", "lg", "Ganda" },
407 { "lui", NULL, "Luiseno" },
408 { "lun", NULL, "Lunda" },
409 { "luo", NULL, "Luo (Kenya and Tanzania)" },
410 { "lus", NULL, "Lushai" },
411 { "mad", NULL, "Madurese" },
412 { "mag", NULL, "Magahi" },
413 { "mah", "mh", "Marshallese" },
414 { "mai", NULL, "Maithili" },
415 { "mak", NULL, "Makasar" },
416 { "mal", "ml", "Malayalam" },
417 { "man", NULL, "Mandingo" },
418 { "map", NULL, "Austronesian (Other)" },
419 { "mar", "mr", "Marathi" },
420 { "mas", NULL, "Masai" },
421 { "may/msa", "ms", "Malay" }, //dual codes
422 { "mdf", NULL, "Moksha" },
423 { "mdr", NULL, "Mandar" },
424 { "men", NULL, "Mende" },
425 { "mga", NULL, "Irish, Middle (900-1200)" },
426 { "mic", NULL, "Mi'kmaq; Micmac" },
427 { "min", NULL, "Minangkabau" },
428 { "mis", NULL, "Miscellaneous languages" },
429 { "mac/mkd", "mk", "Macedonian" }, //dual codes
430 { "mkh", NULL, "Mon-Khmer (Other)" },
431 { "mlg", "mg", "Malagasy" },
432 { "mlt", "mt", "Maltese" },
433 { "mnc", NULL, "Manchu" },
434 { "mni", NULL, "Manipuri" },
435 { "mno", NULL, "Manobo languages" },
436 { "moh", NULL, "Mohawk" },
437 { "mol", "mo", "Moldavian" },
438 { "mon", "mn", "Mongolian" },
439 { "mos", NULL, "Mossi" },
440 { "mao/mri", "mi", "Maori" }, //dual codes
441 { "mul", NULL, "Multiple languages" },
442 { "mun", NULL, "Munda languages" },
443 { "mus", NULL, "Creek" },
444 { "mwl", NULL, "Mirandese" },
445 { "mwr", NULL, "Marwari" },
446 { "myn", NULL, "Mayan languages" },
447 { "myv", NULL, "Erzya" },
448 { "nah", NULL, "Nahuatl" },
449 { "nai", NULL, "North American Indian" },
450 { "nap", NULL, "Neapolitan" },
451 { "nau", "na", "Nauru" },
452 { "nav", "nv", "Navajo; Navaho" },
453 { "nbl", "nr", "Ndebele, South; South Ndebele" },
454 { "nde", "nd", "Ndebele, North; North Ndebele" },
455 { "ndo", "ng", "Ndonga" },
456 { "nds", NULL, "Low German; Low Saxon; German, Low; Saxon, Low" },
457 { "nep", "ne", "Nepali" },
458 { "new", NULL, "Newari; Nepal Bhasa" },
459 { "nia", NULL, "Nias" },
460 { "nic", NULL, "Niger-Kordofanian (Other)" },
461 { "niu", NULL, "Niuean" },
462 { "nno", "nn", "Norwegian Nynorsk; Nynorsk, Norwegian" },
463 { "nob", "nb", "Norwegian Bokmål; Bokmål, Norwegian" },
464 { "nog", NULL, "Nogai" },
465 { "non", NULL, "Norse, Old" },
466 { "nor", "no", "Norwegian" },
467 { "nqo", NULL, "N'ko" },
468 { "nso", NULL, "Northern Sotho, Pedi; Sepedi" },
469 { "nub", NULL, "Nubian languages" },
470 { "nwc", NULL, "Classical Newari; Old Newari; Classical Nepal Bhasa" },
471 { "nya", "ny", "Chichewa; Chewa; Nyanja" },
472 { "nym", NULL, "Nyamwezi" },
473 { "nyn", NULL, "Nyankole" },
474 { "nyo", NULL, "Nyoro" },
475 { "nzi", NULL, "Nzima" },
476 { "oci", "oc", "Occitan (post 1500); Provençal" },
477 { "oji", "oj", "Ojibwa" },
478 { "ori", "or", "Oriya" },
479 { "orm", "om", "Oromo" },
480 { "osa", NULL, "Osage" },
481 { "oss", "os", "Ossetian; Ossetic" },
482 { "ota", NULL, "Turkish, Ottoman (1500-1928)" },
483 { "oto", NULL, "Otomian languages" },
484 { "paa", NULL, "Papuan (Other)" },
485 { "pag", NULL, "Pangasinan" },
486 { "pal", NULL, "Pahlavi" },
487 { "pam", NULL, "Pampanga" },
488 { "pan", "pa", "Panjabi; Punjabi" },
489 { "pap", NULL, "Papiamento" },
490 { "pau", NULL, "Palauan" },
491 { "peo", NULL, "Persian, Old (ca.600-400 B.C.)" },
492 { "per/fas", "fa", "Persian" }, //dual codes
493 { "phi", NULL, "Philippine (Other)" },
494 { "phn", NULL, "Phoenician" },
495 { "pli", "pi", "Pali" },
496 { "pol", "pl", "Polish" },
497 { "pon", NULL, "Pohnpeian" },
498 { "por", "pt", "Portuguese" },
499 { "pra", NULL, "Prakrit languages" },
500 { "pro", NULL, "Provençal, Old (to 1500)" },
501 { "pus", "ps", "Pushto" },
502 //{ "qaa-qtz", NULL, "Reserved for local use" },
503 { "que", "qu", "Quechua" },
504 { "raj", NULL, "Rajasthani" },
505 { "rap", NULL, "Rapanui" },
506 { "rar", NULL, "Rarotongan" },
507 { "roa", NULL, "Romance (Other)" },
508 { "roh", "rm", "Raeto-Romance" },
509 { "rom", NULL, "Romany" },
510 { "rum/ron", "ro", "Romanian" }, //dual codes
511 { "run", "rn", "Rundi" },
512 { "rup", NULL, "Aromanian; Arumanian; Macedo-Romanian" },
513 { "rus", "ru", "Russian" },
514 { "sad", NULL, "Sandawe" },
515 { "sag", "sg", "Sango" },
516 { "sah", NULL, "Yakut" },
517 { "sai", NULL, "South American Indian (Other)" },
518 { "sal", NULL, "Salishan languages" },
519 { "sam", NULL, "Samaritan Aramaic" },
520 { "san", "sa", "Sanskrit" },
521 { "sas", NULL, "Sasak" },
522 { "sat", NULL, "Santali" },
523 { "scn", NULL, "Sicilian" },
524 { "sco", NULL, "Scots" },
525 { "scr/hrv", "hr", "Croatian" }, //dual codes
526 { "sel", NULL, "Selkup" },
527 { "sem", NULL, "Semitic (Other)" },
528 { "sga", NULL, "Irish, Old (to 900)" },
529 { "sgn", NULL, "Sign Languages" },
530 { "shn", NULL, "Shan" },
531 { "sid", NULL, "Sidamo" },
532 { "sin", "si", "Sinhala; Sinhalese" },
533 { "sio", NULL, "Siouan languages" },
534 { "sit", NULL, "Sino-Tibetan (Other)" },
535 { "sla", NULL, "Slavic (Other)" },
536 { "slo/slk", "sk", "Slovak" }, //dual codes
537 { "slv", "sl", "Slovenian" },
538 { "sma", NULL, "Southern Sami" },
539 { "sme", "se", "Northern Sami" },
540 { "smi", NULL, "Sami languages (Other)" },
541 { "smj", NULL, "Lule Sami" },
542 { "smn", NULL, "Inari Sami" },
543 { "smo", "sm", "Samoan" },
544 { "sms", NULL, "Skolt Sami" },
545 { "sna", "sn", "Shona" },
546 { "snd", "sd", "Sindhi" },
547 { "snk", NULL, "Soninke" },
548 { "sog", NULL, "Sogdian" },
549 { "som", "so", "Somali" },
550 { "son", NULL, "Songhai" },
551 { "sot", "st", "Sotho, Southern" },
552 { "spa", "es", "Spanish; Castilian" },
553 { "srd", "sc", "Sardinian" },
554 { "srn", NULL, "Sranan Togo" },
555 { "scc/srp", "sr", "Serbian" }, //dual codes
556 { "srr", NULL, "Serer" },
557 { "ssa", NULL, "Nilo-Saharan (Other)" },
558 { "ssw", "ss", "Swati" },
559 { "suk", NULL, "Sukuma" },
560 { "sun", "su", "Sundanese" },
561 { "sus", NULL, "Susu" },
562 { "sux", NULL, "Sumerian" },
563 { "swa", "sw", "Swahili" },
564 { "swe", "sv", "Swedish" },
565 { "syr", NULL, "Syriac" },
566 { "tah", "ty", "Tahitian" },
567 { "tai", NULL, "Tai (Other)" },
568 { "tam", "ta", "Tamil" },
569 { "tat", "tt", "Tatar" },
570 { "tel", "te", "Telugu" },
571 { "tem", NULL, "Timne" },
572 { "ter", NULL, "Tereno" },
573 { "tet", NULL, "Tetum" },
574 { "tgk", "tg", "Tajik" },
575 { "tgl", "tl", "Tagalog" },
576 { "tha", "th", "Thai" },
577 { "tib/bod", "bo", "Tibetan" }, //dual codes
578 { "tig", NULL, "Tigre" },
579 { "tir", "ti", "Tigrinya" },
580 { "tiv", NULL, "Tiv" },
581 { "tkl", NULL, "Tokelau" },
582 { "tlh", NULL, "Klingon; tlhIngan-Hol" },
583 { "tli", NULL, "Tlingit" },
584 { "tmh", NULL, "Tamashek" },
585 { "tog", NULL, "Tonga (Nyasa)" },
586 { "ton", "to", "Tonga (Tonga Islands)" },
587 { "tpi", NULL, "Tok Pisin" },
588 { "tsi", NULL, "Tsimshian" },
589 { "tsn", "tn", "Tswana" },
590 { "tso", "ts", "Tsonga" },
591 { "tuk", "tk", "Turkmen" },
592 { "tum", NULL, "Tumbuka" },
593 { "tup", NULL, "Tupi languages" },
594 { "tur", "tr", "Turkish" },
595 { "tut", NULL, "Altaic (Other)" },
596 { "tvl", NULL, "Tuvalu" },
597 { "twi", "tw", "Twi" },
598 { "tyv", NULL, "Tuvinian" },
599 { "udm", NULL, "Udmurt" },
600 { "uga", NULL, "Ugaritic" },
601 { "uig", "ug", "Uighur; Uyghur" },
602 { "ukr", "uk", "Ukrainian" },
603 { "umb", NULL, "Umbundu" },
604 { "und", NULL, "Undetermined" },
605 { "urd", "ur", "Urdu" },
606 { "uzb", "uz", "Uzbek" },
607 { "vai", NULL, "Vai" },
608 { "ven", "ve", "Venda" },
609 { "vie", "vi", "Vietnamese" },
610 { "vol", "vo", "Volapük" },
611 { "vot", NULL, "Votic" },
612 { "wak", NULL, "Wakashan languages" },
613 { "wal", NULL, "Walamo" },
614 { "war", NULL, "Waray" },
615 { "was", NULL, "Washo" },
616 { "wel/cym", "cy", "Welsh" }, // //dual codes
617 { "wen", NULL, "Sorbian languages" },
618 { "wln", "wa", "Walloon" },
619 { "wol", "wo", "Wolof" },
620 { "xal", NULL, "Kalmyk; Oirat" },
621 { "xho", "xh", "Xhosa" },
622 { "yao", NULL, "Yao" },
623 { "yap", NULL, "Yapese" },
624 { "yid", "yi", "Yiddish" },
625 { "yor", "yo", "Yoruba" },
626 { "ypk", NULL, "Yupik languages" },
627 { "zap", NULL, "Zapotec" },
628 { "zen", NULL, "Zenaga" },
629 { "zha", "za", "Zhuang; Chuang" },
630 { "chi/zho", "zh", "Chinese" }, //dual codes
631 { "znd", NULL, "Zande" },
632 { "zul", "zu", "Zulu" },
633 { "zun", NULL, "Zuni" },
634 { "zxx", NULL, "No linguistic content" }
635 };
636
637 m_ratings known_ratings[] = {
638 { "us-tv|TV-MA|600|", "TV-MA" },
639 { "us-tv|TV-14|500|", "TV-14" },
640 { "us-tv|TV-PG|400|", "TV-PG" },
641 { "us-tv|TV-G|300|", "TV-G" },
642 { "us-tv|TV-Y7|200|", "TV-Y7" },
643 { "us-tv|TV-Y|100|", "TV-Y" },
644 //{ "us-tv||0|", "not-applicable" }, //though its a valid flag & some files have this, AP won't be setting it.
645 { "mpaa|UNRATED|600|", "Unrated" },
646 { "mpaa|NC-17|500|", "NC-17" },
647 { "mpaa|R|400|", "R" },
648 { "mpaa|PG-13|300|", "PG-13" },
649 { "mpaa|PG|200|", "PG" },
650 { "mpaa|G|100|", "G" }
651 //{ "mpaa||0|", "not-applicable" } //see above
652 };
653
654 char* GenreIntToString(int genre) {
655 char* return_string = NULL;
656 if (genre > 0 && genre <= (int)(sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList))) {
657 return_string = (char*)ID3v1GenreList[genre-1];
658 }
659 return return_string;
660 }
661
662 uint8_t StringGenreToInt(const char* genre_string) {
663 uint8_t return_genre = 0;
664 uint8_t total_genres = (uint8_t)(sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList));
665
666 for(uint8_t i = 0; i < total_genres; i++) {
667 if ( strcmp(genre_string, ID3v1GenreList[i]) == 0) {
668 return_genre = i+1; //the list starts at 0; the embedded genres start at 1
669 //fprintf(stdout, "Genre %s is %i\n", ID3v1GenreList[i], return_genre);
670 break;
671 }
672 }
673 if ( return_genre > total_genres ) {
674 return_genre = 0;
675 }
676 return return_genre;
738 char *GenreIntToString(int genre) {
739 char *return_string = NULL;
740 if (genre > 0 &&
741 genre <= (int)(sizeof(ID3v1GenreList) / sizeof(*ID3v1GenreList))) {
742 return_string = (char *)ID3v1GenreList[genre - 1];
743 }
744 return return_string;
745 }
746
747 uint8_t StringGenreToInt(const char *genre_string) {
748 uint8_t return_genre = 0;
749 uint8_t total_genres =
750 (uint8_t)(sizeof(ID3v1GenreList) / sizeof(*ID3v1GenreList));
751
752 for (uint8_t i = 0; i < total_genres; i++) {
753 if (strcmp(genre_string, ID3v1GenreList[i]) == 0) {
754 return_genre =
755 i + 1; // the list starts at 0; the embedded genres start at 1
756 // fprintf(stdout, "Genre %s is %i\n", ID3v1GenreList[i], return_genre);
757 break;
758 }
759 }
760 if (return_genre > total_genres) {
761 return_genre = 0;
762 }
763 return return_genre;
677764 }
678765
679766 void ListGenresValues() {
680 uint8_t total_genres = (uint8_t)(sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList));
681 fprintf(stdout, "\tAvailable standard genres - case sensitive.\n");
682
683 for (uint8_t i = 0; i < total_genres; i++) {
684 fprintf(stdout, "(%i.) %s\n", i+1, ID3v1GenreList[i]);
685 }
686 return;
687 }
688
689 stiks* MatchStikString(const char* in_stik_string) {
690 stiks* matching_stik = NULL;
691 uint8_t total_known_stiks = (sizeof(stikArray)/sizeof(*stikArray));
692
693 for (uint8_t i = 0; i < total_known_stiks; i++) {
694 if ( strcmp(in_stik_string, stikArray[i].stik_string) == 0) {
695 matching_stik = &stikArray[i];
696 break;
697 }
698 }
699 return matching_stik;
700 }
701
702 stiks* MatchStikNumber(uint8_t in_stik_num) {
703 stiks* matching_stik = NULL;
704 uint8_t total_known_stiks = (sizeof(stikArray)/sizeof(*stikArray));
705
706 for (uint8_t i = 0; i < total_known_stiks; i++) {
707 if ( stikArray[i].stik_number == in_stik_num ) {
708 matching_stik = &stikArray[i];
709 break;
710 }
711 }
712 return matching_stik;
767 uint8_t total_genres =
768 (uint8_t)(sizeof(ID3v1GenreList) / sizeof(*ID3v1GenreList));
769 fprintf(stdout, "\tAvailable standard genres - case sensitive.\n");
770
771 for (uint8_t i = 0; i < total_genres; i++) {
772 fprintf(stdout, "(%i.) %s\n", i + 1, ID3v1GenreList[i]);
773 }
774 return;
775 }
776
777 stiks *MatchStikString(const char *in_stik_string) {
778 stiks *matching_stik = NULL;
779 uint8_t total_known_stiks = (sizeof(stikArray) / sizeof(*stikArray));
780
781 for (uint8_t i = 0; i < total_known_stiks; i++) {
782 if (strcmp(in_stik_string, stikArray[i].stik_string) == 0) {
783 matching_stik = &stikArray[i];
784 break;
785 }
786 }
787 return matching_stik;
788 }
789
790 stiks *MatchStikNumber(uint8_t in_stik_num) {
791 stiks *matching_stik = NULL;
792 uint8_t total_known_stiks = (sizeof(stikArray) / sizeof(*stikArray));
793
794 for (uint8_t i = 0; i < total_known_stiks; i++) {
795 if (stikArray[i].stik_number == in_stik_num) {
796 matching_stik = &stikArray[i];
797 break;
798 }
799 }
800 return matching_stik;
713801 }
714802
715803 void ListStikValues() {
716 uint8_t total_known_stiks = (sizeof(stikArray)/sizeof(*stikArray));
717 fprintf(stdout, "\tAvailable stik settings - case sensitive (number in parens shows the stik value).\n");
718
719 for (uint8_t i = 0; i < total_known_stiks; i++) {
720 fprintf(stdout, "(%u) %s\n", stikArray[i].stik_number, stikArray[i].stik_string);
721 }
722 return;
723 }
724
725 sfIDs* MatchStoreFrontNumber(uint32_t storefrontnum) {
726 sfIDs* matching_sfID = NULL;
727 uint8_t total_known_sfs = (sizeof(storefronts)/sizeof(*storefronts));
728
729 for (uint8_t i = 0; i < total_known_sfs; i++) {
730 if ( storefronts[i].storefront_number == storefrontnum ) {
731 matching_sfID = &storefronts[i];
732 break;
733 }
734 }
735 return matching_sfID;
736 }
737
738 bool MatchLanguageCode(const char* in_code) {
739 bool matching_lang = false;
740 uint16_t total_known_langs = (uint16_t)(sizeof(known_languages)/sizeof(*known_languages));
741
742 for (uint16_t i = 0; i < total_known_langs; i++) {
743 if (strncmp(in_code, known_languages[i].iso639_2_code, 3) == 0) {
744 matching_lang = true;
745 break;
746 }
747 if (strlen(known_languages[i].iso639_2_code) > 3) {
748 if (strncmp(in_code, known_languages[i].iso639_2_code+4, 3) == 0) {
749 matching_lang = true;
750 break;
751 }
752 }
753 }
754
755 return matching_lang;
804 uint8_t total_known_stiks = (sizeof(stikArray) / sizeof(*stikArray));
805 fprintf(stdout,
806 "\tAvailable stik settings - case sensitive (number in "
807 "parens shows the stik value).\n");
808
809 for (uint8_t i = 0; i < total_known_stiks; i++) {
810 fprintf(stdout,
811 "(%u) %s\n",
812 stikArray[i].stik_number,
813 stikArray[i].stik_string);
814 }
815 return;
816 }
817
818 sfIDs *MatchStoreFrontNumber(uint32_t storefrontnum) {
819 sfIDs *matching_sfID = NULL;
820 uint8_t total_known_sfs = (sizeof(storefronts) / sizeof(*storefronts));
821
822 for (uint8_t i = 0; i < total_known_sfs; i++) {
823 if (storefronts[i].storefront_number == storefrontnum) {
824 matching_sfID = &storefronts[i];
825 break;
826 }
827 }
828 return matching_sfID;
829 }
830
831 bool MatchLanguageCode(const char *in_code) {
832 bool matching_lang = false;
833 uint16_t total_known_langs =
834 (uint16_t)(sizeof(known_languages) / sizeof(*known_languages));
835
836 for (uint16_t i = 0; i < total_known_langs; i++) {
837 if (strncmp(in_code, known_languages[i].iso639_2_code, 3) == 0) {
838 matching_lang = true;
839 break;
840 }
841 if (strlen(known_languages[i].iso639_2_code) > 3) {
842 if (strncmp(in_code, known_languages[i].iso639_2_code + 4, 3) == 0) {
843 matching_lang = true;
844 break;
845 }
846 }
847 }
848
849 return matching_lang;
756850 }
757851
758852 void ListLanguageCodes() {
759 uint16_t total_known_langs = (uint16_t)(sizeof(known_languages)/sizeof(*known_languages));
760 fprintf(stdout, "\tAvailable language codes\nISO639-2 code ... English name:\n");
761
762 for (uint16_t i = 0; i < total_known_langs; i++) {
763 fprintf(stdout, " %s ... %s\n", known_languages[i].iso639_2_code, known_languages[i].language_in_english);
764 }
765 return;
853 uint16_t total_known_langs =
854 (uint16_t)(sizeof(known_languages) / sizeof(*known_languages));
855 fprintf(stdout,
856 "\tAvailable language codes\nISO639-2 code ... English name:\n");
857
858 for (uint16_t i = 0; i < total_known_langs; i++) {
859 fprintf(stdout,
860 " %s ... %s\n",
861 known_languages[i].iso639_2_code,
862 known_languages[i].language_in_english);
863 }
864 return;
766865 }
767866
768867 void ListMediaRatings() {
769 uint16_t total_known_ratings = (uint16_t)(sizeof(known_ratings)/sizeof(*known_ratings));
770 fprintf(stdout, "\tAvailable ratings for the U.S. rating system:\n");
771
772 for (uint16_t i = 0; i < total_known_ratings; i++) {
773 fprintf(stdout, " %s\n", known_ratings[i].media_rating_cli_str);
774 }
775 return;
868 uint16_t total_known_ratings =
869 (uint16_t)(sizeof(known_ratings) / sizeof(*known_ratings));
870 fprintf(stdout, "\tAvailable ratings for the U.S. rating system:\n");
871
872 for (uint16_t i = 0; i < total_known_ratings; i++) {
873 fprintf(stdout, " %s\n", known_ratings[i].media_rating_cli_str);
874 }
875 return;
776876 }
777877
778878 void ListTVGenreIDValues() {
779 uint16_t total_genreidtv = (uint16_t)(sizeof(genreidtv)/sizeof(*genreidtv));
780 fprintf(stdout, "\tAvailable iTunes TV Genre IDs:\n");
781
782 for (uint16_t i = 0; i < total_genreidtv; i++) {
783 fprintf(stdout, "(%u) %s\n", genreidtv[i].genre_id_tv_value, genreidtv[i].genre_id_tv_string);
784 }
785 return;
879 uint16_t total_genreidtv = (uint16_t)(sizeof(genreidtv) / sizeof(*genreidtv));
880 fprintf(stdout, "\tAvailable iTunes TV Genre IDs:\n");
881
882 for (uint16_t i = 0; i < total_genreidtv; i++) {
883 fprintf(stdout,
884 "(%u) %s\n",
885 genreidtv[i].genre_id_tv_value,
886 genreidtv[i].genre_id_tv_string);
887 }
888 return;
786889 }
787890
788891 void ListMovieGenreIDValues() {
789 uint16_t total_genreidmovie = (uint16_t)(sizeof(genreidmovie)/sizeof(*genreidmovie));
790 fprintf(stdout, "\tAvailable iTunes Movie Genre IDs:\n");
791
792 for (uint16_t i = 0; i < total_genreidmovie; i++) {
793 fprintf(stdout, "(%u) %s\n", genreidmovie[i].genre_id_movie_value, genreidmovie[i].genre_id_movie_string);
794 }
795 return;
796 }
797
798 const char* Expand_cli_mediastring(const char* cli_rating) {
799 const char* media_rating = NULL;
800 uint16_t total_known_ratings = (uint16_t)(sizeof(known_ratings)/sizeof(*known_ratings));
801 uint8_t rating_len = strlen(cli_rating);
802
803 for (uint16_t i = 0; i < total_known_ratings; i++) {
804 if ( strncasecmp(known_ratings[i].media_rating_cli_str, cli_rating, rating_len+1) == 0 ) {
805 media_rating = known_ratings[i].media_rating;
806 break;
807 }
808 }
809 return media_rating;
810 }
811
812 //ID32 for ID3 frame functions
813 char* ID3GenreIntToString(int genre) {
814 char* return_string = NULL;
815 if (genre >= 0 && genre <= 79) {
816 return_string = (char*)ID3v1GenreList[genre];
817 }
818 return return_string;
819 }
820
821 uint8_t ID3StringGenreToInt(const char* genre_string) {
822 uint8_t return_genre = 0xFF;
823 uint8_t total_genres = 80;
824
825 for(uint8_t i = 0; i < total_genres; i++) {
826 if ( strcmp(genre_string, ID3v1GenreList[i]) == 0) {
827 return i;
828 }
829 }
830 if ( return_genre > total_genres ) {
831 return_genre = 0xFF;
832 }
833 return return_genre;
834 }
892 uint16_t total_genreidmovie =
893 (uint16_t)(sizeof(genreidmovie) / sizeof(*genreidmovie));
894 fprintf(stdout, "\tAvailable iTunes Movie Genre IDs:\n");
895
896 for (uint16_t i = 0; i < total_genreidmovie; i++) {
897 fprintf(stdout,
898 "(%u) %s\n",
899 genreidmovie[i].genre_id_movie_value,
900 genreidmovie[i].genre_id_movie_string);
901 }
902 return;
903 }
904
905 const char *Expand_cli_mediastring(const char *cli_rating) {
906 const char *media_rating = NULL;
907 uint16_t total_known_ratings =
908 (uint16_t)(sizeof(known_ratings) / sizeof(*known_ratings));
909 uint8_t rating_len = strlen(cli_rating);
910
911 for (uint16_t i = 0; i < total_known_ratings; i++) {
912 if (strncasecmp(known_ratings[i].media_rating_cli_str,
913 cli_rating,
914 rating_len + 1) == 0) {
915 media_rating = known_ratings[i].media_rating;
916 break;
917 }
918 }
919 return media_rating;
920 }
921
922 // ID32 for ID3 frame functions
923 char *ID3GenreIntToString(int genre) {
924 char *return_string = NULL;
925 if (genre >= 0 && genre <= 79) {
926 return_string = (char *)ID3v1GenreList[genre];
927 }
928 return return_string;
929 }
930
931 uint8_t ID3StringGenreToInt(const char *genre_string) {
932 uint8_t return_genre = 0xFF;
933 uint8_t total_genres = 80;
934
935 for (uint8_t i = 0; i < total_genres; i++) {
936 if (strcmp(genre_string, ID3v1GenreList[i]) == 0) {
937 return i;
938 }
939 }
940 if (return_genre > total_genres) {
941 return_genre = 0xFF;
942 }
943 return return_genre;
944 }
11 /*
22 AtomicParsley - compress.cpp
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2006-2007 puck_lock
17 Copyright (C) 2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
1919 */
2020 //==================================================================//
2424 #include <zlib.h>
2525 #endif
2626
27 static void* zalloc(void *opaque, unsigned int items, unsigned int size)
28 {
29 return calloc(items, size);
27 static void *zalloc(void *opaque, unsigned int items, unsigned int size) {
28 return calloc(items, size);
3029 }
3130
32 static void zfree(void *opaque, void *ptr)
33 {
34 free(ptr);
35 }
31 static void zfree(void *opaque, void *ptr) { free(ptr); }
3632
3733 /*----------------------
3834 APar_zlib_inflate
3935 in_buffer - pointer to already compressed data
40 in_buf_len - length of compressed data
41 out_buffer - pointer to a buffer to store decompressed/inflated data
42 out_buf_len - length of the out_buffer/max allowable decompressed size
36 in_buf_len - length of compressed data
37 out_buffer - pointer to a buffer to store decompressed/inflated data
38 out_buf_len - length of the out_buffer/max allowable decompressed size
4339
4440 fill
4541 ----------------------*/
46 void APar_zlib_inflate(char* in_buffer, uint32_t in_buf_len,
47 char* out_buffer, uint32_t out_buf_len)
48 {
42 void APar_zlib_inflate(char *in_buffer,
43 uint32_t in_buf_len,
44 char *out_buffer,
45 uint32_t out_buf_len) {
4946 #if defined HAVE_ZLIB_H
50 z_stream zlib;
47 z_stream zlib;
5148
52 memset(&zlib, 0, sizeof(zlib));
49 memset(&zlib, 0, sizeof(zlib));
5350
54 // Decompress to another buffer
55 zlib.zalloc = zalloc;
56 zlib.zfree = zfree;
57 zlib.opaque = NULL;
58 zlib.avail_out = out_buf_len +1;
59 zlib.next_out = (unsigned char*)out_buffer;
60 zlib.avail_in = in_buf_len;
61 zlib.next_in = (unsigned char*)in_buffer;
62 inflateInit(&zlib);
63 inflate(&zlib, Z_PARTIAL_FLUSH);
64 inflateEnd(&zlib);
51 // Decompress to another buffer
52 zlib.zalloc = zalloc;
53 zlib.zfree = zfree;
54 zlib.opaque = NULL;
55 zlib.avail_out = out_buf_len + 1;
56 zlib.next_out = (unsigned char *)out_buffer;
57 zlib.avail_in = in_buf_len;
58 zlib.next_in = (unsigned char *)in_buffer;
59 inflateInit(&zlib);
60 inflate(&zlib, Z_PARTIAL_FLUSH);
61 inflateEnd(&zlib);
6562 #endif
6663
67 return ;
64 return;
6865 }
6966
70 uint32_t APar_zlib_deflate(char* in_buffer, uint32_t in_buf_len,
71 char* out_buffer, uint32_t out_buf_len)
72 {
73 uint32_t compressed_bytes = 0;
67 uint32_t APar_zlib_deflate(char *in_buffer,
68 uint32_t in_buf_len,
69 char *out_buffer,
70 uint32_t out_buf_len) {
71 uint32_t compressed_bytes = 0;
7472
7573 #if defined HAVE_ZLIB_H
76 z_stream zlib;
74 z_stream zlib;
7775
78 memset(&zlib, 0, sizeof(zlib));
76 memset(&zlib, 0, sizeof(zlib));
7977
80 // Compress(default level 6) to another buffer
81 zlib.zalloc = zalloc;
82 zlib.zfree = zfree;
83 zlib.opaque = NULL;
84 zlib.avail_out = out_buf_len +1;
85 zlib.next_out = (unsigned char*)out_buffer;
86 zlib.avail_in = in_buf_len;
87 zlib.next_in = (unsigned char*)in_buffer;
88 zlib.total_out = 0;
89 deflateInit(&zlib, Z_DEFAULT_COMPRESSION);
90 if (Z_STREAM_END == deflate(&zlib, Z_FINISH) ) {
91 compressed_bytes = zlib.total_out;
92 deflateEnd(&zlib);
93 }
78 // Compress(default level 6) to another buffer
79 zlib.zalloc = zalloc;
80 zlib.zfree = zfree;
81 zlib.opaque = NULL;
82 zlib.avail_out = out_buf_len + 1;
83 zlib.next_out = (unsigned char *)out_buffer;
84 zlib.avail_in = in_buf_len;
85 zlib.next_in = (unsigned char *)in_buffer;
86 zlib.total_out = 0;
87 deflateInit(&zlib, Z_DEFAULT_COMPRESSION);
88 if (Z_STREAM_END == deflate(&zlib, Z_FINISH)) {
89 compressed_bytes = zlib.total_out;
90 deflateEnd(&zlib);
91 }
9492 #endif
95 return compressed_bytes;
93 return compressed_bytes;
9694 }
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2006-2007 puck_lock
17 Copyright (C) 2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
19 */
19 */
2020 //==================================================================//
2121
2222 #include "AtomicParsley.h"
2525 iods_OD iods_info = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
2626
2727 ///////////////////////////////////////////////////////////////////////////////////////
28 // File reading routines //
28 // File reading routines //
2929 ///////////////////////////////////////////////////////////////////////////////////////
3030
3131 /*----------------------
3232 APar_skip_filler
3333 isofile - the file to be scanned
34 start_position - the offset from the start of file where to commence possible skipping
35
36 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
37 (not that everything there is the gospel truth).
34 start_position - the offset from the start of file where to commence possible
35 skipping
36
37 I can't remember where exactly I stumbled over what to skip, but this
38 touches on it:
39 http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt (not that
40 everything there is the gospel truth).
3841 ----------------------*/
39 uint8_t APar_skip_filler(FILE* isofile, uint32_t start_position) {
40 uint8_t skip_bytes = 0;
41
42 while (true) {
43 uint8_t eval_byte = APar_read8(isofile, start_position + skip_bytes);
44
45 if (eval_byte == 0x80 || eval_byte == 0x81 || eval_byte == 0xFE) { //seems sometimes QT writes 0x81
46 skip_bytes++;
47 } else {
48 break;
49 }
50 }
51 return skip_bytes;
42 uint8_t APar_skip_filler(FILE *isofile, uint32_t start_position) {
43 uint8_t skip_bytes = 0;
44
45 while (true) {
46 uint8_t eval_byte = APar_read8(isofile, start_position + skip_bytes);
47
48 if (eval_byte == 0x80 || eval_byte == 0x81 ||
49 eval_byte == 0xFE) { // seems sometimes QT writes 0x81
50 skip_bytes++;
51 } else {
52 break;
53 }
54 }
55 return skip_bytes;
5256 }
5357
5458 ///////////////////////////////////////////////////////////////////////////////////////
55 // string routines //
59 // string routines //
5660 ///////////////////////////////////////////////////////////////////////////////////////
5761
5862 /*----------------------
6064 lnum - the number to convert to a string
6165 data - the string to hold the conversion
6266
63 Returns a pointer to the originating string (used to print out a string; different from the other function which converts returning void)
67 Returns a pointer to the originating string (used to print out a string;
68 different from the other function which converts returning void)
6469 ----------------------*/
65 char* uint32tochar4(uint32_t lnum, char* data) {
66 data[0] = (lnum >> 24) & 0xff;
67 data[1] = (lnum >> 16) & 0xff;
68 data[2] = (lnum >> 8) & 0xff;
69 data[3] = (lnum >> 0) & 0xff;
70 return data;
70 char *uint32tochar4(uint32_t lnum, char *data) {
71 data[0] = (lnum >> 24) & 0xff;
72 data[1] = (lnum >> 16) & 0xff;
73 data[2] = (lnum >> 8) & 0xff;
74 data[3] = (lnum >> 0) & 0xff;
75 return data;
7176 }
7277
7378 /*----------------------
7479 purge_extraneous_characters
7580 data - the string which may contain low or high ascii
7681
77 Just change most non-textual characters (like a pesky new line char) to something less objectionable - for stdout formatting only
82 Just change most non-textual characters (like a pesky new line char) to
83 something less objectionable - for stdout formatting only
7884 ----------------------*/
79 uint16_t purge_extraneous_characters(char* data) {
80 uint16_t purgings = 0;
81 uint16_t str_len = strlen(data);
82 for(uint16_t str_offset = 0; str_offset < str_len; str_offset++) {
83 if (data[str_offset] < 32 || data[str_offset] == 127) {
84 data[str_offset] = 19;
85 purgings++;
86 break;
87 }
88 }
89 return purgings;
90 }
91
92 void mem_append(const char* add_string, char* dest_string) {
93 uint8_t str_len = strlen(dest_string);
94 if (str_len > 0) {
95 memcpy(dest_string+str_len, ", ", 2);
96 memcpy(dest_string+str_len+2, add_string, strlen(add_string) );
97 } else {
98 memcpy(dest_string, add_string, strlen(add_string) );
99 }
100 return;
85 uint16_t purge_extraneous_characters(char *data) {
86 uint16_t purgings = 0;
87 uint16_t str_len = strlen(data);
88 for (uint16_t str_offset = 0; str_offset < str_len; str_offset++) {
89 if (data[str_offset] < 32 || data[str_offset] == 127) {
90 data[str_offset] = 19;
91 purgings++;
92 break;
93 }
94 }
95 return purgings;
96 }
97
98 void mem_append(const char *add_string, char *dest_string) {
99 uint8_t str_len = strlen(dest_string);
100 if (str_len > 0) {
101 memcpy(dest_string + str_len, ", ", 2);
102 memcpy(dest_string + str_len + 2, add_string, strlen(add_string));
103 } else {
104 memcpy(dest_string, add_string, strlen(add_string));
105 }
106 return;
101107 }
102108
103109 /*----------------------
104110 secsTOtime
105111 seconds - duration in seconds as a floating point number
106112
107 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,
108 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.
113 Convert decimal seconds to hh:mm:ss.milliseconds. Take the whole seconds and
114 manually separate out the hours, minutes and remaining seconds. For the
115 milliseconds, sprintf into a separate string because there doesn't seem to be a
116 way to print without the leading zero; so copy form that string the digits we
117 want then.
109118 ----------------------*/
110 char* secsTOtime(double seconds) {
111 ap_time time_duration = {0};
112 uint32_t whole_secs = (uint32_t)(seconds / 1);
113
114 time_duration.rem_millisecs = seconds - (double)whole_secs;
115 time_duration.hours = whole_secs / 3600;
116 whole_secs -= time_duration.hours * 3600;
117 time_duration.minutes = whole_secs / 60;
118 whole_secs -= time_duration.minutes * 60;
119 time_duration.seconds = whole_secs;
120
121 static char hhmmss_time[20];
122 memset(hhmmss_time, 0, 20);
123 char milli[5];
124 memset(milli, 0, 5);
125
126 uint8_t time_offset = 0;
127 if (time_duration.hours > 0) {
128 if (time_duration.hours < 10) {
129 sprintf(hhmmss_time, "0%u:", time_duration.hours);
130 } else {
131 sprintf(hhmmss_time, "%u:", time_duration.hours);
132 }
133 time_offset+=3;
134 }
135 if (time_duration.minutes > 0) {
136 if (time_duration.minutes < 10) {
137 sprintf(hhmmss_time+time_offset, "0%u:", time_duration.minutes);
138 } else {
139 sprintf(hhmmss_time+time_offset, "%u:", time_duration.minutes);
140 }
141 time_offset+=3;
142 } else {
143 memcpy(hhmmss_time+time_offset, "0:", 2);
144 time_offset+=2;
145 }
146 if (time_duration.seconds > 0) {
147 if (time_duration.seconds < 10) {
148 sprintf(hhmmss_time+time_offset, "0%u", time_duration.seconds);
149 } else {
150 sprintf(hhmmss_time+time_offset, "%u", time_duration.seconds);
151 }
152 time_offset+=2;
153 } else {
154 memcpy(hhmmss_time+time_offset, "0.", 2);
155 time_offset+=1;
156 }
157
158 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
159 memcpy(hhmmss_time+time_offset, milli+1, 3);
160
161 return *&hhmmss_time;
119 char *secsTOtime(double seconds) {
120 ap_time time_duration = {0};
121 uint32_t whole_secs = (uint32_t)(seconds / 1);
122
123 time_duration.rem_millisecs = seconds - (double)whole_secs;
124 time_duration.hours = whole_secs / 3600;
125 whole_secs -= time_duration.hours * 3600;
126 time_duration.minutes = whole_secs / 60;
127 whole_secs -= time_duration.minutes * 60;
128 time_duration.seconds = whole_secs;
129
130 static char hhmmss_time[20];
131 memset(hhmmss_time, 0, 20);
132 char milli[5];
133 memset(milli, 0, 5);
134
135 uint8_t time_offset = 0;
136 if (time_duration.hours > 0) {
137 if (time_duration.hours < 10) {
138 sprintf(hhmmss_time, "0%u:", time_duration.hours);
139 } else {
140 sprintf(hhmmss_time, "%u:", time_duration.hours);
141 }
142 time_offset += 3;
143 }
144 if (time_duration.minutes > 0) {
145 if (time_duration.minutes < 10) {
146 sprintf(hhmmss_time + time_offset, "0%u:", time_duration.minutes);
147 } else {
148 sprintf(hhmmss_time + time_offset, "%u:", time_duration.minutes);
149 }
150 time_offset += 3;
151 } else {
152 memcpy(hhmmss_time + time_offset, "0:", 2);
153 time_offset += 2;
154 }
155 if (time_duration.seconds > 0) {
156 if (time_duration.seconds < 10) {
157 sprintf(hhmmss_time + time_offset, "0%u", time_duration.seconds);
158 } else {
159 sprintf(hhmmss_time + time_offset, "%u", time_duration.seconds);
160 }
161 time_offset += 2;
162 } else {
163 memcpy(hhmmss_time + time_offset, "0.", 2);
164 time_offset += 1;
165 }
166
167 sprintf(
168 milli,
169 "%.2lf",
170 time_duration.rem_millisecs); // sprintf the double float into a new
171 // string because I don't know if there is a
172 // way to print without a leading zero
173 memcpy(hhmmss_time + time_offset, milli + 1, 3);
174
175 return *&hhmmss_time;
162176 }
163177
164178 ///////////////////////////////////////////////////////////////////////////////////////
165 // Print Profile Info //
179 // Print Profile Info //
166180 ///////////////////////////////////////////////////////////////////////////////////////
167181
168182 /*----------------------
169183 APar_ShowMPEG4VisualProfileInfo
170 track_info - a pointer to the struct holding all the information gathered as a single 'trak' atom was traversed
171
172 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.
173 Much of this was garnered from ISO 14496-2:2001 - up to 'Simple Studio'.
184 track_info - a pointer to the struct holding all the information
185 gathered as a single 'trak' atom was traversed
186
187 If a movie-level iods (containing profiles on a movie-level basis), prefer
188 that mechanism for choosing which profile, otherwise fall back to 'esds'
189 profiles. Much of this was garnered from ISO 14496-2:2001 - up to 'Simple
190 Studio'.
174191 ----------------------*/
175 void APar_ShowMPEG4VisualProfileInfo(TrackInfo* track_info) {
176 fprintf(stdout, " MPEG-4 Visual ");
177 uint8_t mp4v_profile = 0;
178 if (movie_info.contains_iods) {
179 mp4v_profile = iods_info.video_profile_level;
180 } else {
181 mp4v_profile = track_info->m4v_profile;
182 }
183
184 //unparalleled joy - Annex G table g1 - a binary listing (this from 14496-2:2001)
185 if (mp4v_profile == 0x01) {
186 fprintf(stdout, "Simple Profile, Level 1"); //00000001
187 } else if (mp4v_profile == 0x02) {
188 fprintf(stdout, "Simple Profile, Level 2"); //00000010
189 } else if (mp4v_profile == 0x03) {
190 fprintf(stdout, "Simple Profile, Level 3"); //most files will land here //00000011
191
192 } else if (mp4v_profile == 0x08) { //Compressor can create these in 3gp files
193 fprintf(stdout, "Simple Profile, Level 0"); //ISO 14496-2:2004(e) //00001000
194
195 //Reserved 00000100 - 00000111
196 } else if (mp4v_profile == 0x10) {
197 fprintf(stdout, "Simple Scalable Profile, Level 0"); //00010000
198 } else if (mp4v_profile == 0x11) {
199 fprintf(stdout, "Simple Scalable Profile, Level 1"); //00010001
200 } else if (mp4v_profile == 0x12) {
201 fprintf(stdout, "Simple Scalable Profile, Level 2"); //00010010
202
203 //Reserved 00010011 - 00100000
204 } else if (mp4v_profile == 0x21) {
205 fprintf(stdout, "Core Profile, Level 1"); //00100001
206 } else if (mp4v_profile == 0x22) {
207 fprintf(stdout, "Core Profile, Level 2"); //00100010
208
209 //Reserved 00100011 - 00110001
210 } else if (mp4v_profile == 0x32) {
211 fprintf(stdout, "Main Profile, Level 2"); //00110010
212 } else if (mp4v_profile == 0x33) {
213 fprintf(stdout, "Main Profile, Level 3"); //00110011
214 } else if (mp4v_profile == 0x34) {
215 fprintf(stdout, "Main Profile, Level 4"); //00110100
216
217 //Reserved 00110101 - 01000001
218 } else if (mp4v_profile == 0x42) {
219 fprintf(stdout, "N-bit Profile, Level 2"); //01000010
220
221 //Reserved 01000011 - 01010000
222 } else if (mp4v_profile == 0x51) {
223 fprintf(stdout, "Scalable Texture Profile, Level 1"); //01010001
224
225 //Reserved 01010010 - 01100000
226 } else if (mp4v_profile == 0x61) {
227 fprintf(stdout, "Simple Face Animation, Level 1"); //01100001
228 } else if (mp4v_profile == 0x62) {
229 fprintf(stdout, "Simple Face Animation, Level 2"); //01100010
230
231 } else if (mp4v_profile == 0x63) {
232 fprintf(stdout, "Simple FBA Profile, Level 1"); //01100011
233 } else if (mp4v_profile == 0x64) {
234 fprintf(stdout, "Simple FBA Profile, Level 2"); //01100100
235
236 //Reserved 01100101 - 01110000
237 } else if (mp4v_profile == 0x71) {
238 fprintf(stdout, "Basic Animated Texture Profile, Level 1"); //01110001
239 } else if (mp4v_profile == 0x72) {
240 fprintf(stdout, "Basic Animated Texture Profile, Level 2"); //01110010
241
242 //Reserved 01110011 - 10000000
243 } else if (mp4v_profile == 0x81) {
244 fprintf(stdout, "Hybrid Profile, Level 1"); //10000001
245 } else if (mp4v_profile == 0x82) {
246 fprintf(stdout, "Hybrid Profile, Level 2"); //10000010
247
248 //Reserved 10000011 - 10010000
249 } else if (mp4v_profile == 0x91) {
250 fprintf(stdout, "Advanced Real Time Simple Profile, Level 1"); //10010001
251 } else if (mp4v_profile == 0x92) {
252 fprintf(stdout, "Advanced Real Time Simple Profile, Level 2"); //10010010
253 } else if (mp4v_profile == 0x93) {
254 fprintf(stdout, "Advanced Real Time Simple Profile, Level 3"); //10010011
255 } else if (mp4v_profile == 0x94) {
256 fprintf(stdout, "Advanced Real Time Simple Profile, Level 4"); //10010100
257
258 //Reserved 10010101 - 10100000
259 } else if (mp4v_profile == 0xA1) {
260 fprintf(stdout, "Core Scalable Profile, Level 1"); //10100001
261 } else if (mp4v_profile == 0xA2) {
262 fprintf(stdout, "Core Scalable Profile, Level 2"); //10100010
263 } else if (mp4v_profile == 0xA3) {
264 fprintf(stdout, "Core Scalable Profile, Level 3"); //10100011
265
266 //Reserved 10100100 - 10110000
267 } else if (mp4v_profile == 0xB1) {
268 fprintf(stdout, "Advanced Coding Efficiency Profile, Level 1"); //10110001
269 } else if (mp4v_profile == 0xB2) {
270 fprintf(stdout, "Advanced Coding Efficiency Profile, Level 2"); //10110010
271 } else if (mp4v_profile == 0xB3) {
272 fprintf(stdout, "Advanced Coding Efficiency Profile, Level 3"); //10110011
273 } else if (mp4v_profile == 0xB4) {
274 fprintf(stdout, "Advanced Coding Efficiency Profile, Level 4"); //10110100
275
276 //Reserved 10110101 Ð 11000000
277 } else if (mp4v_profile == 0xC1) {
278 fprintf(stdout, "Advanced Core Profile, Level 1"); //11000001
279 } else if (mp4v_profile == 0xC2) {
280 fprintf(stdout, "Advanced Core Profile, Level 2"); //11000010
281
282 //Reserved 11000011 Ð 11010000
283 } else if (mp4v_profile == 0xD1) {
284 fprintf(stdout, "Advanced Scalable Texture, Level 1"); //11010001
285 } else if (mp4v_profile == 0xD2) {
286 fprintf(stdout, "Advanced Scalable Texture, Level 2"); //11010010
287 } else if (mp4v_profile == 0xD2) {
288 fprintf(stdout, "Advanced Scalable Texture, Level 3"); //11010011
289
290 //from a draft document - 1999 (earlier than the 2000 above!!)
291 } else if (mp4v_profile == 0xE1) {
292 fprintf(stdout, "Simple Studio Profile, Level 1"); //11100001
293 } else if (mp4v_profile == 0xE2) {
294 fprintf(stdout, "Simple Studio Profile, Level 2"); //11100010
295 } else if (mp4v_profile == 0xE3) {
296 fprintf(stdout, "Simple Studio Profile, Level 3"); //11100011
297 } else if (mp4v_profile == 0xE4) {
298 fprintf(stdout, "Simple Studio Profile, Level 4"); //11100100
299
300 } else if (mp4v_profile == 0xE5) {
301 fprintf(stdout, "Core Studio Profile, Level 1"); //11100101
302 } else if (mp4v_profile == 0xE6) {
303 fprintf(stdout, "Core Studio Profile, Level 2"); //11100110
304 } else if (mp4v_profile == 0xE7) {
305 fprintf(stdout, "Core Studio Profile, Level 3"); //11100111
306 } else if (mp4v_profile == 0xE8) {
307 fprintf(stdout, "Core Studio Profile, Level 4"); //11101000
308
309 //Reserved 11101001 - 11101111
310 //ISO 14496-2:2004(e)
311 } else if (mp4v_profile == 0xF0) {
312 fprintf(stdout, "Advanced Simple Profile, Level 0"); //11110000
313 } else if (mp4v_profile == 0xF1) {
314 fprintf(stdout, "Advanced Simple Profile, Level 1"); //11110001
315 } else if (mp4v_profile == 0xF2) {
316 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)
317 ////...and its been figured out - FILE EXTENSION of all things determines mpeg-4 ASP or H.263
318 } else if (mp4v_profile == 0xF3) {
319 fprintf(stdout, "Advanced Simple Profile, Level 3"); //11110011
320 } else if (mp4v_profile == 0xF4) {
321 fprintf(stdout, "Advanced Simple Profile, Level 4"); //11110100
322 } else if (mp4v_profile == 0xF5) {
323 fprintf(stdout, "Advanced Simple Profile, Level 5"); //11110101
324
325 //Reserved 11110110
326 } else if (mp4v_profile == 0xF7) {
327 fprintf(stdout, "Advanced Simple Profile, Level 3b"); //11110111
328
329 } else if (mp4v_profile == 0xF7) {
330 fprintf(stdout, "Fine Granularity Scalable Profile/Level 0"); //11111000
331 } else if (mp4v_profile == 0xF7) {
332 fprintf(stdout, "Fine Granularity Scalable Profile/Level 1"); //11111001
333 } else if (mp4v_profile == 0xF7) {
334 fprintf(stdout, "Fine Granularity Scalable Profile/Level 2"); //11111010
335 } else if (mp4v_profile == 0xF7) {
336 fprintf(stdout, "Fine Granularity Scalable Profile/Level 3"); //11111011
337 } else if (mp4v_profile == 0xF7) {
338 fprintf(stdout, "Fine Granularity Scalable Profile/Level 4"); //11111100
339 } else if (mp4v_profile == 0xF7) {
340 fprintf(stdout, "Fine Granularity Scalable Profile/Level 5"); //11111101
341
342 //Reserved 11111110
343 //Reserved for Escape 11111111
344
345 } else {
346 fprintf(stdout, "Unknown profile: 0x%X", mp4v_profile);
347 }
348 return;
192 void APar_ShowMPEG4VisualProfileInfo(TrackInfo *track_info) {
193 fprintf(stdout, " MPEG-4 Visual ");
194 uint8_t mp4v_profile = 0;
195 if (movie_info.contains_iods) {
196 mp4v_profile = iods_info.video_profile_level;
197 } else {
198 mp4v_profile = track_info->m4v_profile;
199 }
200
201 // unparalleled joy - Annex G table g1 - a binary listing (this from
202 // 14496-2:2001)
203 if (mp4v_profile == 0x01) {
204 fprintf(stdout, "Simple Profile, Level 1"); // 00000001
205 } else if (mp4v_profile == 0x02) {
206 fprintf(stdout, "Simple Profile, Level 2"); // 00000010
207 } else if (mp4v_profile == 0x03) {
208 fprintf(stdout,
209 "Simple Profile, Level 3"); // most files will land here //00000011
210
211 } else if (mp4v_profile == 0x08) { // Compressor can create these in 3gp files
212 fprintf(stdout, "Simple Profile, Level 0"); // ISO 14496-2:2004(e)
213 // //00001000
214
215 // Reserved 00000100 - 00000111
216 } else if (mp4v_profile == 0x10) {
217 fprintf(stdout, "Simple Scalable Profile, Level 0"); // 00010000
218 } else if (mp4v_profile == 0x11) {
219 fprintf(stdout, "Simple Scalable Profile, Level 1"); // 00010001
220 } else if (mp4v_profile == 0x12) {
221 fprintf(stdout, "Simple Scalable Profile, Level 2"); // 00010010
222
223 // Reserved 00010011 - 00100000
224 } else if (mp4v_profile == 0x21) {
225 fprintf(stdout, "Core Profile, Level 1"); // 00100001
226 } else if (mp4v_profile == 0x22) {
227 fprintf(stdout, "Core Profile, Level 2"); // 00100010
228
229 // Reserved 00100011 - 00110001
230 } else if (mp4v_profile == 0x32) {
231 fprintf(stdout, "Main Profile, Level 2"); // 00110010
232 } else if (mp4v_profile == 0x33) {
233 fprintf(stdout, "Main Profile, Level 3"); // 00110011
234 } else if (mp4v_profile == 0x34) {
235 fprintf(stdout, "Main Profile, Level 4"); // 00110100
236
237 // Reserved 00110101 - 01000001
238 } else if (mp4v_profile == 0x42) {
239 fprintf(stdout, "N-bit Profile, Level 2"); // 01000010
240
241 // Reserved 01000011 - 01010000
242 } else if (mp4v_profile == 0x51) {
243 fprintf(stdout, "Scalable Texture Profile, Level 1"); // 01010001
244
245 // Reserved 01010010 - 01100000
246 } else if (mp4v_profile == 0x61) {
247 fprintf(stdout, "Simple Face Animation, Level 1"); // 01100001
248 } else if (mp4v_profile == 0x62) {
249 fprintf(stdout, "Simple Face Animation, Level 2"); // 01100010
250
251 } else if (mp4v_profile == 0x63) {
252 fprintf(stdout, "Simple FBA Profile, Level 1"); // 01100011
253 } else if (mp4v_profile == 0x64) {
254 fprintf(stdout, "Simple FBA Profile, Level 2"); // 01100100
255
256 // Reserved 01100101 - 01110000
257 } else if (mp4v_profile == 0x71) {
258 fprintf(stdout, "Basic Animated Texture Profile, Level 1"); // 01110001
259 } else if (mp4v_profile == 0x72) {
260 fprintf(stdout, "Basic Animated Texture Profile, Level 2"); // 01110010
261
262 // Reserved 01110011 - 10000000
263 } else if (mp4v_profile == 0x81) {
264 fprintf(stdout, "Hybrid Profile, Level 1"); // 10000001
265 } else if (mp4v_profile == 0x82) {
266 fprintf(stdout, "Hybrid Profile, Level 2"); // 10000010
267
268 // Reserved 10000011 - 10010000
269 } else if (mp4v_profile == 0x91) {
270 fprintf(stdout, "Advanced Real Time Simple Profile, Level 1"); // 10010001
271 } else if (mp4v_profile == 0x92) {
272 fprintf(stdout, "Advanced Real Time Simple Profile, Level 2"); // 10010010
273 } else if (mp4v_profile == 0x93) {
274 fprintf(stdout, "Advanced Real Time Simple Profile, Level 3"); // 10010011
275 } else if (mp4v_profile == 0x94) {
276 fprintf(stdout, "Advanced Real Time Simple Profile, Level 4"); // 10010100
277
278 // Reserved 10010101 - 10100000
279 } else if (mp4v_profile == 0xA1) {
280 fprintf(stdout, "Core Scalable Profile, Level 1"); // 10100001
281 } else if (mp4v_profile == 0xA2) {
282 fprintf(stdout, "Core Scalable Profile, Level 2"); // 10100010
283 } else if (mp4v_profile == 0xA3) {
284 fprintf(stdout, "Core Scalable Profile, Level 3"); // 10100011
285
286 // Reserved 10100100 - 10110000
287 } else if (mp4v_profile == 0xB1) {
288 fprintf(stdout, "Advanced Coding Efficiency Profile, Level 1"); // 10110001
289 } else if (mp4v_profile == 0xB2) {
290 fprintf(stdout, "Advanced Coding Efficiency Profile, Level 2"); // 10110010
291 } else if (mp4v_profile == 0xB3) {
292 fprintf(stdout, "Advanced Coding Efficiency Profile, Level 3"); // 10110011
293 } else if (mp4v_profile == 0xB4) {
294 fprintf(stdout, "Advanced Coding Efficiency Profile, Level 4"); // 10110100
295
296 // Reserved 10110101 Ð 11000000
297 } else if (mp4v_profile == 0xC1) {
298 fprintf(stdout, "Advanced Core Profile, Level 1"); // 11000001
299 } else if (mp4v_profile == 0xC2) {
300 fprintf(stdout, "Advanced Core Profile, Level 2"); // 11000010
301
302 // Reserved 11000011 Ð 11010000
303 } else if (mp4v_profile == 0xD1) {
304 fprintf(stdout, "Advanced Scalable Texture, Level 1"); // 11010001
305 } else if (mp4v_profile == 0xD2) {
306 fprintf(stdout, "Advanced Scalable Texture, Level 2"); // 11010010
307 } else if (mp4v_profile == 0xD2) {
308 fprintf(stdout, "Advanced Scalable Texture, Level 3"); // 11010011
309
310 // from a draft document - 1999 (earlier than the 2000 above!!)
311 } else if (mp4v_profile == 0xE1) {
312 fprintf(stdout, "Simple Studio Profile, Level 1"); // 11100001
313 } else if (mp4v_profile == 0xE2) {
314 fprintf(stdout, "Simple Studio Profile, Level 2"); // 11100010
315 } else if (mp4v_profile == 0xE3) {
316 fprintf(stdout, "Simple Studio Profile, Level 3"); // 11100011
317 } else if (mp4v_profile == 0xE4) {
318 fprintf(stdout, "Simple Studio Profile, Level 4"); // 11100100
319
320 } else if (mp4v_profile == 0xE5) {
321 fprintf(stdout, "Core Studio Profile, Level 1"); // 11100101
322 } else if (mp4v_profile == 0xE6) {
323 fprintf(stdout, "Core Studio Profile, Level 2"); // 11100110
324 } else if (mp4v_profile == 0xE7) {
325 fprintf(stdout, "Core Studio Profile, Level 3"); // 11100111
326 } else if (mp4v_profile == 0xE8) {
327 fprintf(stdout, "Core Studio Profile, Level 4"); // 11101000
328
329 // Reserved 11101001 - 11101111
330 // ISO 14496-2:2004(e)
331 } else if (mp4v_profile == 0xF0) {
332 fprintf(stdout, "Advanced Simple Profile, Level 0"); // 11110000
333 } else if (mp4v_profile == 0xF1) {
334 fprintf(stdout, "Advanced Simple Profile, Level 1"); // 11110001
335 } else if (mp4v_profile == 0xF2) {
336 fprintf(
337 stdout,
338 "Advanced Simple Profile, Level 2"); // 11110010 ////3gp files that QT
339 // says is H.263 have esds to 0xF2
340 // & their ObjectType set to 0x20
341 // (mpeg-4 visual)
342 ////...and its been figured out -
343 /// FILE EXTENSION of all things
344 /// determines mpeg-4 ASP or H.263
345 } else if (mp4v_profile == 0xF3) {
346 fprintf(stdout, "Advanced Simple Profile, Level 3"); // 11110011
347 } else if (mp4v_profile == 0xF4) {
348 fprintf(stdout, "Advanced Simple Profile, Level 4"); // 11110100
349 } else if (mp4v_profile == 0xF5) {
350 fprintf(stdout, "Advanced Simple Profile, Level 5"); // 11110101
351
352 // Reserved 11110110
353 } else if (mp4v_profile == 0xF7) {
354 fprintf(stdout, "Advanced Simple Profile, Level 3b"); // 11110111
355
356 } else if (mp4v_profile == 0xF7) {
357 fprintf(stdout, "Fine Granularity Scalable Profile/Level 0"); // 11111000
358 } else if (mp4v_profile == 0xF7) {
359 fprintf(stdout, "Fine Granularity Scalable Profile/Level 1"); // 11111001
360 } else if (mp4v_profile == 0xF7) {
361 fprintf(stdout, "Fine Granularity Scalable Profile/Level 2"); // 11111010
362 } else if (mp4v_profile == 0xF7) {
363 fprintf(stdout, "Fine Granularity Scalable Profile/Level 3"); // 11111011
364 } else if (mp4v_profile == 0xF7) {
365 fprintf(stdout, "Fine Granularity Scalable Profile/Level 4"); // 11111100
366 } else if (mp4v_profile == 0xF7) {
367 fprintf(stdout, "Fine Granularity Scalable Profile/Level 5"); // 11111101
368
369 // Reserved 11111110
370 // Reserved for Escape 11111111
371
372 } else {
373 fprintf(stdout, "Unknown profile: 0x%X", mp4v_profile);
374 }
375 return;
349376 }
350377
351378 /*----------------------
352379 APar_ShowMPEG4AACProfileInfo
353 track_info - a pointer to the struct holding all the information gathered as a single 'trak' atom was traversed
380 track_info - a pointer to the struct holding all the information
381 gathered as a single 'trak' atom was traversed
354382
355383 ----------------------*/
356 void APar_ShowMPEG4AACProfileInfo(TrackInfo* track_info) {
357 if (track_info->descriptor_object_typeID == 1) {
358 fprintf(stdout, " MPEG-4 AAC Main Profile");
359 } else if (track_info->descriptor_object_typeID == 2) {
360 fprintf(stdout, " MPEG-4 AAC Low Complexity/LC Profile"); //most files will land here
361 } else if (track_info->descriptor_object_typeID == 3) {
362 fprintf(stdout, " MPEG-4 AAC Scaleable Sample Rate/SSR Profile");
363 } else if (track_info->descriptor_object_typeID == 4) {
364 fprintf(stdout, " MPEG-4 AAC Long Term Prediction Profile");
365 } else if (track_info->descriptor_object_typeID == 5) {
366 fprintf(stdout, " MPEG-4 AAC High Efficiency/HE Profile");
367 } else if (track_info->descriptor_object_typeID == 6) {
368 fprintf(stdout, " MPEG-4 AAC Scalable Profile");
369 } else if (track_info->descriptor_object_typeID == 7) {
370 fprintf(stdout, " MPEG-4 AAC Transform domain Weighted INterleave Vector Quantization/TwinVQ Profile");
371 } else if (track_info->descriptor_object_typeID == 8) {
372 fprintf(stdout, " MPEG-4 AAC Code Excited Linear Predictive/CELP Profile");
373 } else if (track_info->descriptor_object_typeID == 9) {
374 fprintf(stdout, " MPEG-4 AAC HVXC Profile");
375
376 } else if (track_info->descriptor_object_typeID == 12) {
377 fprintf(stdout, " MPEG-4 AAC TTSI Profile");
378 } else if (track_info->descriptor_object_typeID == 13) {
379 fprintf(stdout, " MPEG-4 AAC Main Synthesis Profile");
380 } else if (track_info->descriptor_object_typeID == 14) {
381 fprintf(stdout, " MPEG-4 AAC Wavetable Synthesis Profile");
382 } else if (track_info->descriptor_object_typeID == 15) {
383 fprintf(stdout, " MPEG-4 AAC General MIDI Profile");
384 } else if (track_info->descriptor_object_typeID == 16) {
385 fprintf(stdout, " MPEG-4 AAC Algorithmic Synthesis & Audio FX Profile");
386 } else if (track_info->descriptor_object_typeID == 17) {
387 fprintf(stdout, " MPEG-4 AAC AAC Low Complexity/LC (+error recovery) Profile");
388
389 } else if (track_info->descriptor_object_typeID == 19) {
390 fprintf(stdout, " MPEG-4 AAC Long Term Prediction (+error recovery) Profile");
391 } else if (track_info->descriptor_object_typeID == 20) {
392 fprintf(stdout, " MPEG-4 AAC Scalable (+error recovery) Profile");
393 } else if (track_info->descriptor_object_typeID == 21) {
394 fprintf(stdout, " MPEG-4 AAC Transform domain Weighted INterleave Vector Quantization/TwinVQ (+error recovery) Profile");
395 } else if (track_info->descriptor_object_typeID == 22) {
396 fprintf(stdout, " MPEG-4 AAC Bit Sliced Arithmetic Coding/BSAC (+error recovery) Profile");
397 } else if (track_info->descriptor_object_typeID == 23) {
398 fprintf(stdout, " MPEG-4 AAC Low Delay/LD (+error recovery) Profile");
399 } else if (track_info->descriptor_object_typeID == 24) {
400 fprintf(stdout, " MPEG-4 AAC Code Excited Linear Predictive/CELP (+error recovery) Profile");
401 } else if (track_info->descriptor_object_typeID == 25) {
402 fprintf(stdout, " MPEG-4 AAC HXVC (+error recovery) Profile");
403 } else if (track_info->descriptor_object_typeID == 26) {
404 fprintf(stdout, " MPEG-4 AAC Harmonic and Individual Lines plus Noise/HILN (+error recovery) Profile");
405 } else if (track_info->descriptor_object_typeID == 27) {
406 fprintf(stdout, " MPEG-4 AAC Parametric (+error recovery) Profile");
407
408 } else if (track_info->descriptor_object_typeID == 31) {
409 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
410 } else {
411 fprintf(stdout, " MPEG-4 Unknown profile: 0x%X", track_info->descriptor_object_typeID);
412 }
413 return;
384 void APar_ShowMPEG4AACProfileInfo(TrackInfo *track_info) {
385 if (track_info->descriptor_object_typeID == 1) {
386 fprintf(stdout, " MPEG-4 AAC Main Profile");
387 } else if (track_info->descriptor_object_typeID == 2) {
388 fprintf(
389 stdout,
390 " MPEG-4 AAC Low Complexity/LC Profile"); // most files will land here
391 } else if (track_info->descriptor_object_typeID == 3) {
392 fprintf(stdout, " MPEG-4 AAC Scaleable Sample Rate/SSR Profile");
393 } else if (track_info->descriptor_object_typeID == 4) {
394 fprintf(stdout, " MPEG-4 AAC Long Term Prediction Profile");
395 } else if (track_info->descriptor_object_typeID == 5) {
396 fprintf(stdout, " MPEG-4 AAC High Efficiency/HE Profile");
397 } else if (track_info->descriptor_object_typeID == 6) {
398 fprintf(stdout, " MPEG-4 AAC Scalable Profile");
399 } else if (track_info->descriptor_object_typeID == 7) {
400 fprintf(stdout,
401 " MPEG-4 AAC Transform domain Weighted INterleave Vector "
402 "Quantization/TwinVQ Profile");
403 } else if (track_info->descriptor_object_typeID == 8) {
404 fprintf(stdout, " MPEG-4 AAC Code Excited Linear Predictive/CELP Profile");
405 } else if (track_info->descriptor_object_typeID == 9) {
406 fprintf(stdout, " MPEG-4 AAC HVXC Profile");
407
408 } else if (track_info->descriptor_object_typeID == 12) {
409 fprintf(stdout, " MPEG-4 AAC TTSI Profile");
410 } else if (track_info->descriptor_object_typeID == 13) {
411 fprintf(stdout, " MPEG-4 AAC Main Synthesis Profile");
412 } else if (track_info->descriptor_object_typeID == 14) {
413 fprintf(stdout, " MPEG-4 AAC Wavetable Synthesis Profile");
414 } else if (track_info->descriptor_object_typeID == 15) {
415 fprintf(stdout, " MPEG-4 AAC General MIDI Profile");
416 } else if (track_info->descriptor_object_typeID == 16) {
417 fprintf(stdout, " MPEG-4 AAC Algorithmic Synthesis & Audio FX Profile");
418 } else if (track_info->descriptor_object_typeID == 17) {
419 fprintf(stdout,
420 " MPEG-4 AAC AAC Low Complexity/LC (+error recovery) Profile");
421
422 } else if (track_info->descriptor_object_typeID == 19) {
423 fprintf(stdout,
424 " MPEG-4 AAC Long Term Prediction (+error recovery) Profile");
425 } else if (track_info->descriptor_object_typeID == 20) {
426 fprintf(stdout, " MPEG-4 AAC Scalable (+error recovery) Profile");
427 } else if (track_info->descriptor_object_typeID == 21) {
428 fprintf(stdout,
429 " MPEG-4 AAC Transform domain Weighted INterleave Vector "
430 "Quantization/TwinVQ (+error recovery) Profile");
431 } else if (track_info->descriptor_object_typeID == 22) {
432 fprintf(stdout,
433 " MPEG-4 AAC Bit Sliced Arithmetic Coding/BSAC (+error "
434 "recovery) Profile");
435 } else if (track_info->descriptor_object_typeID == 23) {
436 fprintf(stdout, " MPEG-4 AAC Low Delay/LD (+error recovery) Profile");
437 } else if (track_info->descriptor_object_typeID == 24) {
438 fprintf(stdout,
439 " MPEG-4 AAC Code Excited Linear Predictive/CELP (+error "
440 "recovery) Profile");
441 } else if (track_info->descriptor_object_typeID == 25) {
442 fprintf(stdout, " MPEG-4 AAC HXVC (+error recovery) Profile");
443 } else if (track_info->descriptor_object_typeID == 26) {
444 fprintf(stdout,
445 " MPEG-4 AAC Harmonic and Individual Lines plus "
446 "Noise/HILN (+error recovery) Profile");
447 } else if (track_info->descriptor_object_typeID == 27) {
448 fprintf(stdout, " MPEG-4 AAC Parametric (+error recovery) Profile");
449
450 } else if (track_info->descriptor_object_typeID == 31) {
451 fprintf(
452 stdout,
453 " MPEG-4 ALS Audio Lossless Coding"); // I think that mp4alsRM18 writes
454 // the channels wrong after
455 // objectedID: 0xF880 has 0
456 // channels; 0xF890 is 2ch
457 } else {
458 fprintf(stdout,
459 " MPEG-4 Unknown profile: 0x%X",
460 track_info->descriptor_object_typeID);
461 }
462 return;
414463 }
415464
416465 /*----------------------
417466 APar_ShowObjectProfileInfo
418 track_type - broadly used to determine what types of information (like channels or avc1 profiles) to display
419 track_info - a pointer to the struct holding all the information gathered as a single 'trak' atom was traversed
420
421 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
422 enumerations. avc1 contains 'avcC' which supports a different mechanism.
467 track_type - broadly used to determine what types of information (like
468 channels or avc1 profiles) to display track_info - a pointer to the struct
469 holding all the information gathered as a single 'trak' atom was traversed
470
471 Based on the ObjectTypeIndication in 'esds', show the type of track. For
472 mpeg-4 audio & mpeg-4 visual are handled in a subroutine because there are so
473 many enumerations. avc1 contains 'avcC' which supports a different mechanism.
423474 ----------------------*/
424 void APar_ShowObjectProfileInfo(uint8_t track_type, TrackInfo* track_info) {
425 if (track_info->contains_esds) {
426 switch (track_info->ObjectTypeIndication) {
427 //0x00 es Lambada/Verboten/Forbidden
428 case 0x01:
429 case 0x02: {
430 fprintf(stdout, " MPEG-4 Systems (BIFS/ObjDesc)");
431 break;
432 }
433 case 0x03: {
434 fprintf(stdout, " Interaction Stream");
435 break;
436 }
437 case 0x04: {
438 fprintf(stdout, " MPEG-4 Systems Extended BIFS");
439 break;
440 }
441 case 0x05: {
442 fprintf(stdout, " MPEG-4 Systems AFX");
443 break;
444 }
445 case 0x06: {
446 fprintf(stdout, " Font Data Stream");
447 break;
448 }
449 case 0x08: {
450 fprintf(stdout, " Synthesized Texture Stream");
451 break;
452 }
453 case 0x07: {
454 fprintf(stdout, " Streaming Text Stream");
455 break;
456 }
457 //0x09-0x1F reserved
458 case 0x20: {
459 APar_ShowMPEG4VisualProfileInfo(track_info);
460 break;
461 }
462
463 case 0x40: { //vererable mpeg-4 aac
464 APar_ShowMPEG4AACProfileInfo(track_info);
465 break;
466 }
467
468 //0x41-0x5F reserved
469 case 0x60: {
470 fprintf(stdout, " MPEG-2 Visual Simple Profile"); //'Visual ISO/IEC 13818-2 Simple Profile'
471 break;
472 }
473 case 0x61: {
474 fprintf(stdout, " MPEG-2 Visual Main Profile"); //'Visual ISO/IEC 13818-2 Main Profile'
475 break;
476 }
477 case 0x62: {
478 fprintf(stdout, " MPEG-2 Visual SNR Profile"); //'Visual ISO/IEC 13818-2 SNR Profile'
479 break;
480 }
481 case 0x63: {
482 fprintf(stdout, " MPEG-2 Visual Spatial Profile"); //'Visual ISO/IEC 13818-2 Spatial Profile'
483 break;
484 }
485 case 0x64: {
486 fprintf(stdout, " MPEG-2 Visual High Profile"); //'Visual ISO/IEC 13818-2 High Profile'
487 break;
488 }
489 case 0x65: {
490 fprintf(stdout, " MPEG-2 Visual 4:2:2 Profile"); //'Visual ISO/IEC 13818-2 422 Profile'
491 break;
492 }
493 case 0x66: {
494 fprintf(stdout, " MPEG-2 AAC Main Profile"); //'Audio ISO/IEC 13818-7 Main Profile'
495 break;
496 }
497 case 0x67: {
498 fprintf(stdout, " MPEG-2 AAC Low Complexity Profile"); //Audio ISO/IEC 13818-7 LowComplexity Profile
499 break;
500 }
501 case 0x68: {
502 fprintf(stdout, " MPEG-2 AAC Scaleable Sample Rate Profile"); //'Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile'
503 break;
504 }
505 case 0x69: {
506 fprintf(stdout, " MPEG-2 Audio"); //'Audio ISO/IEC 13818-3'
507 break;
508 }
509 case 0x6A: {
510 fprintf(stdout, " MPEG-1 Visual"); //'Visual ISO/IEC 11172-2'
511 break;
512 }
513 case 0x6B: {
514 fprintf(stdout, " MPEG-1 Audio"); //'Audio ISO/IEC 11172-3'
515 break;
516 }
517 case 0x6C: {
518 fprintf(stdout, " JPEG"); //'Visual ISO/IEC 10918-1'
519 break;
520 }
521 case 0x6D: {
522 fprintf(stdout, " PNG"); //http://www.mp4ra.org/object.html
523 break;
524 }
525 case 0x6E: {
526 fprintf(stdout, " JPEG2000"); //'Visual ISO/IEC 15444-1'
527 break;
528 }
529 case 0xA0: {
530 fprintf(stdout, " 3GPP2 EVRC Voice"); //http://www.mp4ra.org/object.html
531 break;
532 }
533 case 0xA1: {
534 fprintf(stdout, " 3GPP2 SMV Voice"); //http://www.mp4ra.org/object.html
535 break;
536 }
537 case 0xA2: {
538 fprintf(stdout, " 3GPP2 Compact Multimedia Format"); //http://www.mp4ra.org/object.html
539 break;
540 }
541
542 //0xC0-0xE0 user private
543 case 0xE1: {
544 fprintf(stdout, " 3GPP2 QCELP (14K Voice)"); //http://www.mp4ra.org/object.html
545 break;
546 }
547 //0xE2-0xFE user private
548 //0xFF no object type specified
549
550 default: {
551 //so many profiles, so little desire to list them all (in 14496-2 which I don't have)
552 if(movie_info.contains_iods && iods_info.audio_profile == 0xFE) {
553 fprintf(stdout, " Private user object: 0x%X", track_info->ObjectTypeIndication);
554 } else {
555 fprintf(stdout, " Object Type Indicator: 0x%X Description Ojbect Type ID: 0x%X\n", track_info->ObjectTypeIndication, track_info->descriptor_object_typeID);
556 }
557 break;
558 }
559 }
560
561 } else if (track_type == AVC1_TRACK) {
562 //profiles & levels are in the 14496-10 pdf (which I don't have access to), so...
563 //http://lists.mpegif.org/pipermail/mp4-tech/2006-January/006255.html
564 //http://iphome.hhi.de/suehring/tml/doc/lenc/html/configfile_8c-source.html
565 //66=baseline, 77=main, 88=extended; 100=High, 110=High 10, 122=High 4:2:2, 144=High 4:4:4
566
567 switch(track_info->profile) {
568 case 66: {
569 fprintf(stdout, " AVC Baseline Profile");
570 break;
571 }
572 case 77: {
573 fprintf(stdout, " AVC Main Profile");
574 break;
575 }
576 case 88: {
577 fprintf(stdout, " AVC Extended Profile");
578 break;
579 }
580 case 100: {
581 fprintf(stdout, " AVC High Profile");
582 break;
583 }
584 case 110: {
585 fprintf(stdout, " AVC High 10 Profile");
586 break;
587 }
588 case 122: {
589 fprintf(stdout, " AVC High 4:2:2 Profile");
590 break;
591 }
592 case 144: {
593 fprintf(stdout, " AVC High 4:4:4 Profile");
594 break;
595 }
596 default: {
597 fprintf(stdout, " Unknown Profile: %u", track_info->profile);
598 break;
599 }
600 } //end profile switch
601
602 //Don't have access to levels either, but working off of:
603 //http://iphome.hhi.de/suehring/tml/doc/lenc/html/configfile_8c-source.html
604
605 //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)
606 //working backwards, we get... a simple 2 digit number (with '20' just drop the 0; with 21, put in a decimal)
607 if (track_info->level > 0) {
608 switch (track_info->level) {
609 case 10:
610 case 20:
611 case 30:
612 case 40:
613 case 50: {
614 fprintf(stdout, ", Level %u", track_info->level / 10);
615 break;
616 }
617 case 11:
618 case 12:
619 case 13:
620 case 21:
621 case 22:
622 case 31:
623 case 32:
624 case 41:
625 case 42:
626 case 51: {
627 fprintf(stdout, ", Level %u.%u", track_info->level / 10, track_info->level % 10);
628 break;
629 }
630 default: {
631 fprintf(stdout, ", Unknown level %u.%u", track_info->level / 10, track_info->level % 10);
632 }
633
634 } //end switch
635 } //end level if
636 } else if (track_type == S_AMR_TRACK) {
637 char* amr_modes = (char*)calloc(1, sizeof(char)*500);
638 if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762) {
639 if (track_info->amr_modes & 0x0001) mem_append("0", amr_modes);
640 if (track_info->amr_modes & 0x0002) mem_append("1", amr_modes);
641 if (track_info->amr_modes & 0x0004) mem_append("2", amr_modes);
642 if (track_info->amr_modes & 0x0008) mem_append("3", amr_modes);
643 if (track_info->amr_modes & 0x0010) mem_append("4", amr_modes);
644 if (track_info->amr_modes & 0x0020) mem_append("5", amr_modes);
645 if (track_info->amr_modes & 0x0040) mem_append("6", amr_modes);
646 if (track_info->amr_modes & 0x0080) mem_append("7", amr_modes);
647 if (track_info->amr_modes & 0x0100) mem_append("8", amr_modes);
648 if (strlen(amr_modes) == 0) memcpy(amr_modes, "none", 4);
649 } else if (track_info->track_codec == 0x73766D72) {
650 if (track_info->amr_modes & 0x0001) mem_append("VMR-WB Mode 0, ", amr_modes);
651 if (track_info->amr_modes & 0x0002) mem_append("VMR-WB Mode 1, ", amr_modes);
652 if (track_info->amr_modes & 0x0004) mem_append("VMR-WB Mode 2, ", amr_modes);
653 if (track_info->amr_modes & 0x0008) mem_append("VMR-WB Mode 3 (AMR-WB interoperable mode), ", amr_modes);
654 if (track_info->amr_modes & 0x0010) mem_append("VMR-WB Mode 4, ", amr_modes);
655 if (track_info->amr_modes & 0x0020) mem_append("VMR-WB Mode 2 with maximum half-rate, ", amr_modes);
656 if (track_info->amr_modes & 0x0040) mem_append("VMR-WB Mode 4 with maximum half-rate, ", amr_modes);
657 uint16_t amr_modes_len = strlen(amr_modes);
658 if (amr_modes_len > 0) memset(amr_modes+(amr_modes_len-1), 0, 2);
659 }
660
661 if (track_info->track_codec == 0x73616D72) { //samr
662 fprintf(stdout, " AMR Narrow-Band. Modes: %s. Encoder vendor code: %s\n", amr_modes, track_info->encoder_name);
663 } else if (track_info->track_codec == 0x73617762) { //sawb
664 fprintf(stdout, " AMR Wide-Band. Modes: %s. Encoder vendor code: %s\n", amr_modes, track_info->encoder_name);
665 } else if (track_info->track_codec == 0x73617770) { //sawp
666 fprintf(stdout, " AMR Wide-Band WB+. Encoder vendor code: %s\n", track_info->encoder_name);
667 } else if (track_info->track_codec == 0x73766D72) { //svmr
668 fprintf(stdout, " AMR VBR Wide-Band. Encoder vendor code: %s\n", track_info->encoder_name);
669 }
670 free(amr_modes); amr_modes=NULL;
671
672 } else if (track_type == EVRC_TRACK) {
673 fprintf(stdout, " EVRC (Enhanced Variable Rate Coder). Encoder vendor code: %s\n", track_info->encoder_name);
674
675 } else if (track_type == QCELP_TRACK) {
676 fprintf(stdout, " QCELP (Qualcomm Code Excited Linear Prediction). Encoder vendor code: %s\n", track_info->encoder_name);
677
678 } else if (track_type == S263_TRACK) {
679 if (track_info->profile == 0) {
680 fprintf(stdout, " H.263 Baseline Profile, Level %u. Encoder vendor code: %s", track_info->level, track_info->encoder_name);
681 } else {
682 fprintf(stdout, " H.263 Profile: %u, Level %u. Encoder vendor code: %s", track_info->profile, track_info->level, track_info->encoder_name);
683 }
684 }
685 if (track_type == AUDIO_TRACK) {
686 if (track_info->section5_length == 0) {
687 fprintf(stdout, " channels: (%u)\n", track_info->channels );
688 } else {
689 fprintf(stdout, " channels: [%u]\n", track_info->channels );
690 }
691 }
692 return;
475 void APar_ShowObjectProfileInfo(uint8_t track_type, TrackInfo *track_info) {
476 if (track_info->contains_esds) {
477 switch (track_info->ObjectTypeIndication) {
478 // 0x00 es Lambada/Verboten/Forbidden
479 case 0x01:
480 case 0x02: {
481 fprintf(stdout, " MPEG-4 Systems (BIFS/ObjDesc)");
482 break;
483 }
484 case 0x03: {
485 fprintf(stdout, " Interaction Stream");
486 break;
487 }
488 case 0x04: {
489 fprintf(stdout, " MPEG-4 Systems Extended BIFS");
490 break;
491 }
492 case 0x05: {
493 fprintf(stdout, " MPEG-4 Systems AFX");
494 break;
495 }
496 case 0x06: {
497 fprintf(stdout, " Font Data Stream");
498 break;
499 }
500 case 0x08: {
501 fprintf(stdout, " Synthesized Texture Stream");
502 break;
503 }
504 case 0x07: {
505 fprintf(stdout, " Streaming Text Stream");
506 break;
507 }
508 // 0x09-0x1F reserved
509 case 0x20: {
510 APar_ShowMPEG4VisualProfileInfo(track_info);
511 break;
512 }
513
514 case 0x40: { // vererable mpeg-4 aac
515 APar_ShowMPEG4AACProfileInfo(track_info);
516 break;
517 }
518
519 // 0x41-0x5F reserved
520 case 0x60: {
521 fprintf(stdout,
522 " MPEG-2 Visual Simple Profile"); //'Visual ISO/IEC 13818-2
523 // Simple Profile'
524 break;
525 }
526 case 0x61: {
527 fprintf(stdout, " MPEG-2 Visual Main Profile"); //'Visual ISO/IEC 13818-2
528 // Main Profile'
529 break;
530 }
531 case 0x62: {
532 fprintf(
533 stdout,
534 " MPEG-2 Visual SNR Profile"); //'Visual ISO/IEC 13818-2 SNR Profile'
535 break;
536 }
537 case 0x63: {
538 fprintf(stdout,
539 " MPEG-2 Visual Spatial Profile"); //'Visual ISO/IEC 13818-2
540 // Spatial Profile'
541 break;
542 }
543 case 0x64: {
544 fprintf(stdout, " MPEG-2 Visual High Profile"); //'Visual ISO/IEC 13818-2
545 // High Profile'
546 break;
547 }
548 case 0x65: {
549 fprintf(stdout, " MPEG-2 Visual 4:2:2 Profile"); //'Visual ISO/IEC
550 // 13818-2 422 Profile'
551 break;
552 }
553 case 0x66: {
554 fprintf(
555 stdout,
556 " MPEG-2 AAC Main Profile"); //'Audio ISO/IEC 13818-7 Main Profile'
557 break;
558 }
559 case 0x67: {
560 fprintf(stdout,
561 " MPEG-2 AAC Low Complexity Profile"); // Audio ISO/IEC 13818-7
562 // LowComplexity Profile
563 break;
564 }
565 case 0x68: {
566 fprintf(
567 stdout,
568 " MPEG-2 AAC Scaleable Sample Rate Profile"); //'Audio ISO/IEC
569 // 13818-7 Scaleable
570 // Sampling Rate
571 // Profile'
572 break;
573 }
574 case 0x69: {
575 fprintf(stdout, " MPEG-2 Audio"); //'Audio ISO/IEC 13818-3'
576 break;
577 }
578 case 0x6A: {
579 fprintf(stdout, " MPEG-1 Visual"); //'Visual ISO/IEC 11172-2'
580 break;
581 }
582 case 0x6B: {
583 fprintf(stdout, " MPEG-1 Audio"); //'Audio ISO/IEC 11172-3'
584 break;
585 }
586 case 0x6C: {
587 fprintf(stdout, " JPEG"); //'Visual ISO/IEC 10918-1'
588 break;
589 }
590 case 0x6D: {
591 fprintf(stdout, " PNG"); // http://www.mp4ra.org/object.html
592 break;
593 }
594 case 0x6E: {
595 fprintf(stdout, " JPEG2000"); //'Visual ISO/IEC 15444-1'
596 break;
597 }
598 case 0xA0: {
599 fprintf(stdout, " 3GPP2 EVRC Voice"); // http://www.mp4ra.org/object.html
600 break;
601 }
602 case 0xA1: {
603 fprintf(stdout, " 3GPP2 SMV Voice"); // http://www.mp4ra.org/object.html
604 break;
605 }
606 case 0xA2: {
607 fprintf(
608 stdout,
609 " 3GPP2 Compact Multimedia Format"); // http://www.mp4ra.org/object.html
610 break;
611 }
612
613 // 0xC0-0xE0 user private
614 case 0xE1: {
615 fprintf(stdout,
616 " 3GPP2 QCELP (14K Voice)"); // http://www.mp4ra.org/object.html
617 break;
618 }
619 // 0xE2-0xFE user private
620 // 0xFF no object type specified
621
622 default: {
623 // so many profiles, so little desire to list them all (in 14496-2 which I
624 // don't have)
625 if (movie_info.contains_iods && iods_info.audio_profile == 0xFE) {
626 fprintf(stdout,
627 " Private user object: 0x%X",
628 track_info->ObjectTypeIndication);
629 } else {
630 fprintf(
631 stdout,
632 " Object Type Indicator: 0x%X Description Ojbect Type ID: 0x%X\n",
633 track_info->ObjectTypeIndication,
634 track_info->descriptor_object_typeID);
635 }
636 break;
637 }
638 }
639
640 } else if (track_type == AVC1_TRACK) {
641 // profiles & levels are in the 14496-10 pdf (which I don't have access to),
642 // so... http://lists.mpegif.org/pipermail/mp4-tech/2006-January/006255.html
643 // http://iphome.hhi.de/suehring/tml/doc/lenc/html/configfile_8c-source.html
644 // 66=baseline, 77=main, 88=extended; 100=High, 110=High 10, 122=High 4:2:2,
645 // 144=High 4:4:4
646
647 switch (track_info->profile) {
648 case 66: {
649 fprintf(stdout, " AVC Baseline Profile");
650 break;
651 }
652 case 77: {
653 fprintf(stdout, " AVC Main Profile");
654 break;
655 }
656 case 88: {
657 fprintf(stdout, " AVC Extended Profile");
658 break;
659 }
660 case 100: {
661 fprintf(stdout, " AVC High Profile");
662 break;
663 }
664 case 110: {
665 fprintf(stdout, " AVC High 10 Profile");
666 break;
667 }
668 case 122: {
669 fprintf(stdout, " AVC High 4:2:2 Profile");
670 break;
671 }
672 case 144: {
673 fprintf(stdout, " AVC High 4:4:4 Profile");
674 break;
675 }
676 default: {
677 fprintf(stdout, " Unknown Profile: %u", track_info->profile);
678 break;
679 }
680 } // end profile switch
681
682 // Don't have access to levels either, but working off of:
683 // http://iphome.hhi.de/suehring/tml/doc/lenc/html/configfile_8c-source.html
684
685 // and the 15 levels it says here:
686 // http://www.chiariglione.org/mpeg/technologies/mp04-avc/index.htm (1b in
687 // http://en.wikipedia.org/wiki/H.264 seems nonsensical) working backwards,
688 // we get... a simple 2 digit number (with '20' just drop the 0; with 21,
689 // put in a decimal)
690 if (track_info->level > 0) {
691 switch (track_info->level) {
692 case 10:
693 case 20:
694 case 30:
695 case 40:
696 case 50: {
697 fprintf(stdout, ", Level %u", track_info->level / 10);
698 break;
699 }
700 case 11:
701 case 12:
702 case 13:
703 case 21:
704 case 22:
705 case 31:
706 case 32:
707 case 41:
708 case 42:
709 case 51: {
710 fprintf(stdout,
711 ", Level %u.%u",
712 track_info->level / 10,
713 track_info->level % 10);
714 break;
715 }
716 default: {
717 fprintf(stdout,
718 ", Unknown level %u.%u",
719 track_info->level / 10,
720 track_info->level % 10);
721 }
722
723 } // end switch
724 } // end level if
725 } else if (track_type == S_AMR_TRACK) {
726 char amr_modes[500] = {};
727 if (track_info->track_codec == 0x73616D72 ||
728 track_info->track_codec == 0x73617762) {
729 if (track_info->amr_modes & 0x0001)
730 mem_append("0", amr_modes);
731 if (track_info->amr_modes & 0x0002)
732 mem_append("1", amr_modes);
733 if (track_info->amr_modes & 0x0004)
734 mem_append("2", amr_modes);
735 if (track_info->amr_modes & 0x0008)
736 mem_append("3", amr_modes);
737 if (track_info->amr_modes & 0x0010)
738 mem_append("4", amr_modes);
739 if (track_info->amr_modes & 0x0020)
740 mem_append("5", amr_modes);
741 if (track_info->amr_modes & 0x0040)
742 mem_append("6", amr_modes);
743 if (track_info->amr_modes & 0x0080)
744 mem_append("7", amr_modes);
745 if (track_info->amr_modes & 0x0100)
746 mem_append("8", amr_modes);
747 if (strlen(amr_modes) == 0)
748 memcpy(amr_modes, "none", 4);
749 } else if (track_info->track_codec == 0x73766D72) {
750 if (track_info->amr_modes & 0x0001)
751 mem_append("VMR-WB Mode 0, ", amr_modes);
752 if (track_info->amr_modes & 0x0002)
753 mem_append("VMR-WB Mode 1, ", amr_modes);
754 if (track_info->amr_modes & 0x0004)
755 mem_append("VMR-WB Mode 2, ", amr_modes);
756 if (track_info->amr_modes & 0x0008)
757 mem_append("VMR-WB Mode 3 (AMR-WB interoperable mode), ", amr_modes);
758 if (track_info->amr_modes & 0x0010)
759 mem_append("VMR-WB Mode 4, ", amr_modes);
760 if (track_info->amr_modes & 0x0020)
761 mem_append("VMR-WB Mode 2 with maximum half-rate, ", amr_modes);
762 if (track_info->amr_modes & 0x0040)
763 mem_append("VMR-WB Mode 4 with maximum half-rate, ", amr_modes);
764 uint16_t amr_modes_len = strlen(amr_modes);
765 if (amr_modes_len > 0)
766 memset(amr_modes + (amr_modes_len - 1), 0, 2);
767 }
768
769 if (track_info->track_codec == 0x73616D72) { // samr
770 fprintf(stdout,
771 " AMR Narrow-Band. Modes: %s. Encoder vendor code: %s\n",
772 amr_modes,
773 track_info->encoder_name);
774 } else if (track_info->track_codec == 0x73617762) { // sawb
775 fprintf(stdout,
776 " AMR Wide-Band. Modes: %s. Encoder vendor code: %s\n",
777 amr_modes,
778 track_info->encoder_name);
779 } else if (track_info->track_codec == 0x73617770) { // sawp
780 fprintf(stdout,
781 " AMR Wide-Band WB+. Encoder vendor code: %s\n",
782 track_info->encoder_name);
783 } else if (track_info->track_codec == 0x73766D72) { // svmr
784 fprintf(stdout,
785 " AMR VBR Wide-Band. Encoder vendor code: %s\n",
786 track_info->encoder_name);
787 }
788 } else if (track_type == EVRC_TRACK) {
789 fprintf(stdout,
790 " EVRC (Enhanced Variable Rate Coder). Encoder vendor code: %s\n",
791 track_info->encoder_name);
792
793 } else if (track_type == QCELP_TRACK) {
794 fprintf(stdout,
795 " QCELP (Qualcomm Code Excited Linear Prediction). Encoder vendor "
796 "code: %s\n",
797 track_info->encoder_name);
798
799 } else if (track_type == S263_TRACK) {
800 if (track_info->profile == 0) {
801 fprintf(stdout,
802 " H.263 Baseline Profile, Level %u. Encoder vendor code: %s",
803 track_info->level,
804 track_info->encoder_name);
805 } else {
806 fprintf(stdout,
807 " H.263 Profile: %u, Level %u. Encoder vendor code: %s",
808 track_info->profile,
809 track_info->level,
810 track_info->encoder_name);
811 }
812 }
813 if (track_type == AUDIO_TRACK) {
814 if (track_info->section5_length == 0) {
815 fprintf(stdout, " channels: (%u)\n", track_info->channels);
816 } else {
817 fprintf(stdout, " channels: [%u]\n", track_info->channels);
818 }
819 }
693820 }
694821
695822 ///////////////////////////////////////////////////////////////////////////////////////
696 // Movie & Track Level Info //
823 // Movie & Track Level Info //
697824 ///////////////////////////////////////////////////////////////////////////////////////
698825
699826 /*----------------------
700827 calcuate_sample_size
701 uint32_buffer - a buffer to read bytes in from the file
702 isofile - the file to be scanned
703 stsz_atom - the atom number of the stsz atom
704
705 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.
706 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)
707 and is displayed with the asterisk* at track-level.
828 uint32_buffer - a buffer to read bytes in from the file
829 isofile - the file to be scanned
830 stsz_atom - the atom number of the stsz atom
831
832 This will get aggregate a number of the size of all chunks in the track. The
833 stsz atom holds a table of these sizes along with a count of how many there are.
834 Loop through the count, summing in the sizes. This is called
835 called for all tracks, but used only when a hardcoded bitrate (in esds) isn't
836 found (like avc1) and is displayed with the asterisk* at track-level.
708837 ----------------------*/
709 uint64_t calcuate_sample_size(char* uint32_buffer, FILE* isofile, short stsz_atom) {
710 uint32_t sample_size = 0;
711 uint32_t sample_count = 0;
712 uint64_t total_size = 0;
713
714 sample_size = APar_read32(uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + 12);
715 sample_count = APar_read32(uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + 16);
716
717 if (sample_size == 0) {
718 for (uint64_t atom_offset = 20; atom_offset < parsedAtoms[stsz_atom].AtomicLength; atom_offset +=4) {
719 total_size += APar_read32(uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + atom_offset);
720 }
721 } else {
722 total_size = sample_size * sample_count;
723 }
724 return total_size;
838 uint64_t
839 calcuate_sample_size(char *uint32_buffer, FILE *isofile, short stsz_atom) {
840 uint32_t sample_size = 0;
841 uint32_t sample_count = 0;
842 uint64_t total_size = 0;
843
844 sample_size = APar_read32(
845 uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + 12);
846 sample_count = APar_read32(
847 uint32_buffer, isofile, parsedAtoms[stsz_atom].AtomicStart + 16);
848
849 if (sample_size == 0) {
850 for (uint64_t atom_offset = 20;
851 atom_offset < parsedAtoms[stsz_atom].AtomicLength;
852 atom_offset += 4) {
853 total_size +=
854 APar_read32(uint32_buffer,
855 isofile,
856 parsedAtoms[stsz_atom].AtomicStart + atom_offset);
857 }
858 } else {
859 total_size = sample_size * sample_count;
860 }
861 return total_size;
725862 }
726863
727864 /*----------------------
728865 APar_TrackLevelInfo
729 track - pointer to a struct providing the track we are looking for
730 track_search_atom_name - the name of the atom to be found in this track
731
732 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
733 count of tracks back in the same struct to that later functions can loop through each track individually, looking for a specific atom.
734 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
866 track - pointer to a struct providing the track we are looking for
867 track_search_atom_name - the name of the atom to be found in this track
868
869 Looping through the atoms one by one, note a 'trak' atom. If we are looking
870 for the total amount of tracks (by setting the track_num to 0), simply return
871 the count of tracks back in the same struct to that later functions can loop
872 through each track individually, looking for a specific atom. If track's
873 track_num is a non-zero number, then find that atom that *matches* the atom
874 name. Set track's track_atom to that atom for later use
735875 ----------------------*/
736 void APar_TrackLevelInfo(Trackage* track, const char* track_search_atom_name) {
737 uint8_t track_tally = 0;
738 short iter = 0;
739
740 while (parsedAtoms[iter].NextAtomNumber != 0) {
741
742 if ( strncmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0) {
743 track_tally += 1;
744 if (track->track_num == 0) {
745 track->total_tracks += 1;
746
747 } else if (track->track_num == track_tally) {
748
749 short next_atom = parsedAtoms[iter].NextAtomNumber;
750 while (parsedAtoms[next_atom].AtomicLevel > parsedAtoms[iter].AtomicLevel) {
751
752 if (strncmp(parsedAtoms[next_atom].AtomicName, track_search_atom_name, 4) == 0) {
753
754 track->track_atom = parsedAtoms[next_atom].AtomicNumber;
755 return;
756 } else {
757 next_atom = parsedAtoms[next_atom].NextAtomNumber;
758 }
759 if (parsedAtoms[next_atom].AtomicLevel == parsedAtoms[iter].AtomicLevel) {
760 track->track_atom = 0;
761 }
762 }
763 }
764 }
765 iter=parsedAtoms[iter].NextAtomNumber;
766 }
767 return;
876 void APar_TrackLevelInfo(Trackage *track, const char *track_search_atom_name) {
877 uint8_t track_tally = 0;
878 short iter = 0;
879
880 while (parsedAtoms[iter].NextAtomNumber != 0) {
881
882 if (strncmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0) {
883 track_tally += 1;
884 if (track->track_num == 0) {
885 track->total_tracks += 1;
886
887 } else if (track->track_num == track_tally) {
888
889 short next_atom = parsedAtoms[iter].NextAtomNumber;
890 while (parsedAtoms[next_atom].AtomicLevel >
891 parsedAtoms[iter].AtomicLevel) {
892
893 if (strncmp(parsedAtoms[next_atom].AtomicName,
894 track_search_atom_name,
895 4) == 0) {
896
897 track->track_atom = parsedAtoms[next_atom].AtomicNumber;
898 return;
899 } else {
900 next_atom = parsedAtoms[next_atom].NextAtomNumber;
901 }
902 if (parsedAtoms[next_atom].AtomicLevel ==
903 parsedAtoms[iter].AtomicLevel) {
904 track->track_atom = 0;
905 }
906 }
907 }
908 }
909 iter = parsedAtoms[iter].NextAtomNumber;
910 }
911 return;
768912 }
769913
770914 /*----------------------
771915 APar_ExtractChannelInfo
772 isofile - the file to be scanned
773 pos - the position within the file that carries the channel info (in esds)
774
775 The channel info in esds is bitpacked, so read it in isolation and shift the bits around to get at it
916 isofile - the file to be scanned
917 pos - the position within the file that carries the channel info (in
918 esds)
919
920 The channel info in esds is bitpacked, so read it in isolation and shift the
921 bits around to get at it
776922 ----------------------*/
777 uint8_t APar_ExtractChannelInfo(FILE* isofile, uint32_t pos) {
778 uint8_t packed_channels = APar_read8(isofile, pos);
779 uint8_t unpacked_channels = (packed_channels << 1); //just shift the first bit off the table
780 unpacked_channels = (unpacked_channels >> 4); //and slide it on over back on the uint8_t
781 return unpacked_channels;
923 uint8_t APar_ExtractChannelInfo(FILE *isofile, uint32_t pos) {
924 uint8_t packed_channels = APar_read8(isofile, pos);
925 uint8_t unpacked_channels =
926 (packed_channels << 1); // just shift the first bit off the table
927 unpacked_channels =
928 (unpacked_channels >> 4); // and slide it on over back on the uint8_t
929 return unpacked_channels;
782930 }
783931
784932 /*----------------------
785933 APar_Extract_iods_Info
786 isofile - the file to be scanned
787 iods_atom - a pointer to the struct that will store the profile levels found in iods
788
789 '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.
790 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
791 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
792 all other video types it is ignored.
934 isofile - the file to be scanned
935 iods_atom - a pointer to the struct that will store the profile levels
936 found in iods
937
938 'iods' info mostly comes from:
939 http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt Just as
940 'esds' has 'filler' bytes to skip over, so does this. Mercifully, the profiles
941 come one right after another. The only problem is that in many files, the iods
942 profiles don't match the esds profiles. This is resolved by ignoring it for
943 audio (mostly, unless is 0xFE user defined). For MPEG-4 Visual, it is preferred
944 over 'esds' (occurs in APar_ShowMPEG4VisualProfileInfo); for all other video
945 types it is ignored.
793946 ----------------------*/
794 void APar_Extract_iods_Info(FILE* isofile, AtomicInfo* iods_atom) {
795 uint64_t iods_offset = iods_atom->AtomicStart+8;
796 if (iods_atom->AtomicVerFlags == 0 && APar_read8(isofile, iods_offset+4) == 0x10) {
797 iods_offset+=5;
798 iods_offset += APar_skip_filler(isofile, iods_offset);
799 uint8_t iods_objdescrip_len = APar_read8(isofile, iods_offset);
800 iods_offset++;
801 if (iods_objdescrip_len >= 7) {
802 iods_info.od_profile_level = APar_read8(isofile, iods_offset+2);
803 iods_info.scene_profile_level = APar_read8(isofile, iods_offset+3);
804 iods_info.audio_profile = APar_read8(isofile, iods_offset+4);
805 iods_info.video_profile_level = APar_read8(isofile, iods_offset+5);
806 iods_info.graphics_profile_level = APar_read8(isofile, iods_offset+6);
807 }
808 }
809 return;
947 void APar_Extract_iods_Info(FILE *isofile, AtomicInfo *iods_atom) {
948 uint64_t iods_offset = iods_atom->AtomicStart + 8;
949 if (iods_atom->AtomicVerFlags == 0 &&
950 APar_read8(isofile, iods_offset + 4) == 0x10) {
951 iods_offset += 5;
952 iods_offset += APar_skip_filler(isofile, iods_offset);
953 uint8_t iods_objdescrip_len = APar_read8(isofile, iods_offset);
954 iods_offset++;
955 if (iods_objdescrip_len >= 7) {
956 iods_info.od_profile_level = APar_read8(isofile, iods_offset + 2);
957 iods_info.scene_profile_level = APar_read8(isofile, iods_offset + 3);
958 iods_info.audio_profile = APar_read8(isofile, iods_offset + 4);
959 iods_info.video_profile_level = APar_read8(isofile, iods_offset + 5);
960 iods_info.graphics_profile_level = APar_read8(isofile, iods_offset + 6);
961 }
962 }
963 return;
810964 }
811965
812966 /*----------------------
813967 APar_Extract_AMR_Info
814 uint32_buffer - a buffer to read bytes in from the file
815 isofile - the file to be scanned
816 track_level_atom - the number of the 'esds' atom in the linked list of parsed atoms
817 track_info - a pointer to the struct carrying track-level info to be filled with information
818
819 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
968 uint32_buffer - a buffer to read bytes in from the file
969 isofile - the file to be scanned
970 track_level_atom - the number of the 'esds' atom in the linked list of
971 parsed atoms track_info - a pointer to the struct carrying track-level info to
972 be filled with information
973
974 The only interesting info here is the encoding tool & the amr modes used.
975 ffmpeg's amr output seems to lack some compliance - no damr atom for sawb
820976 ----------------------*/
821 void APar_Extract_AMR_Info(char* uint32_buffer, FILE* isofile, short track_level_atom, TrackInfo* track_info) {
822 uint32_t amr_specific_offet = 8;
823 APar_readX(track_info->encoder_name, isofile, parsedAtoms[track_level_atom].AtomicStart + amr_specific_offet, 4);
824 if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762 || track_info->track_codec == 0x73766D72) { //samr,sawb & svmr contain modes only
825 track_info->amr_modes = APar_read16(uint32_buffer, isofile, parsedAtoms[track_level_atom].AtomicStart + amr_specific_offet + 4+1);
826 }
827 return;
977 void APar_Extract_AMR_Info(char *uint32_buffer,
978 FILE *isofile,
979 short track_level_atom,
980 TrackInfo *track_info) {
981 uint32_t amr_specific_offet = 8;
982 APar_readX(track_info->encoder_name,
983 isofile,
984 parsedAtoms[track_level_atom].AtomicStart + amr_specific_offet,
985 4);
986 if (track_info->track_codec == 0x73616D72 ||
987 track_info->track_codec == 0x73617762 ||
988 track_info->track_codec ==
989 0x73766D72) { // samr,sawb & svmr contain modes only
990 track_info->amr_modes = APar_read16(
991 uint32_buffer,
992 isofile,
993 parsedAtoms[track_level_atom].AtomicStart + amr_specific_offet + 4 + 1);
994 }
995 return;
828996 }
829997
830998 /*----------------------
831999 APar_Extract_d263_Info
832 uint32_buffer - a buffer to read bytes in from the file
833 isofile - the file to be scanned
834 track_level_atom - the number of the 'esds' atom in the linked list of parsed atoms
835 track_info - a pointer to the struct carrying track-level info to be filled with information
836
837 '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
1000 uint32_buffer - a buffer to read bytes in from the file
1001 isofile - the file to be scanned
1002 track_level_atom - the number of the 'esds' atom in the linked list of
1003 parsed atoms track_info - a pointer to the struct carrying track-level info to
1004 be filled with information
1005
1006 'd263' only holds 4 things; the 3 of interest are gathered here. Its
1007 possible that a 'bitr' atom follows 'd263', which would hold bitrates, but isn't
1008 parsed here
8381009 ----------------------*/
839 void APar_Extract_d263_Info(char* uint32_buffer, FILE* isofile, short track_level_atom, TrackInfo* track_info) {
840 uint64_t offset_into_d263 = 8;
841 APar_readX(track_info->encoder_name, isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_d263, 4);
842 track_info->level = APar_read8(isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_d263 + 4+1);
843 track_info->profile = APar_read8(isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_d263 + 4+2);
844 //possible 'bitr' bitrate box afterwards
845 return;
1010 void APar_Extract_d263_Info(char *uint32_buffer,
1011 FILE *isofile,
1012 short track_level_atom,
1013 TrackInfo *track_info) {
1014 uint64_t offset_into_d263 = 8;
1015 APar_readX(track_info->encoder_name,
1016 isofile,
1017 parsedAtoms[track_level_atom].AtomicStart + offset_into_d263,
1018 4);
1019 track_info->level = APar_read8(isofile,
1020 parsedAtoms[track_level_atom].AtomicStart +
1021 offset_into_d263 + 4 + 1);
1022 track_info->profile = APar_read8(isofile,
1023 parsedAtoms[track_level_atom].AtomicStart +
1024 offset_into_d263 + 4 + 2);
1025 // possible 'bitr' bitrate box afterwards
1026 return;
8461027 }
8471028
8481029 /*----------------------
8491030 APar_Extract_devc_Info
850 isofile - the file to be scanned
851 track_level_atom - the number of the 'esds' atom in the linked list of parsed atoms
852 track_info - a pointer to the struct carrying track-level info to be filled with information
853
854 'devc' only holds 3 things: encoder vendor, decoder version & frames per sample; only encoder vendor is gathered
1031 isofile - the file to be scanned
1032 track_level_atom - the number of the 'esds' atom in the linked list of
1033 parsed atoms track_info - a pointer to the struct carrying track-level info to
1034 be filled with information
1035
1036 'devc' only holds 3 things: encoder vendor, decoder version & frames per
1037 sample; only encoder vendor is gathered
8551038 ----------------------*/
856 void APar_Extract_devc_Info(FILE* isofile, short track_level_atom, TrackInfo* track_info) {
857 uint64_t offset_into_devc = 8;
858 APar_readX(track_info->encoder_name, isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_devc, 4);
859 return;
1039 void APar_Extract_devc_Info(FILE *isofile,
1040 short track_level_atom,
1041 TrackInfo *track_info) {
1042 uint64_t offset_into_devc = 8;
1043 APar_readX(track_info->encoder_name,
1044 isofile,
1045 parsedAtoms[track_level_atom].AtomicStart + offset_into_devc,
1046 4);
1047 return;
8601048 }
8611049
8621050 /*----------------------
8631051 APar_Extract_esds_Info
864 uint32_buffer - a buffer to read bytes in from the file
865 isofile - the file to be scanned
866 track_level_atom - the number of the 'esds' atom in the linked list of parsed atoms
867 track_info - a pointer to the struct carrying track-level info to be filled with information
868
869 '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:
870 http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt - but its misleading in lots of places too. For those tracks that support 'esds'
871 (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
872 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.
1052 uint32_buffer - a buffer to read bytes in from the file
1053 isofile - the file to be scanned
1054 track_level_atom - the number of the 'esds' atom in the linked list of
1055 parsed atoms track_info - a pointer to the struct carrying track-level info to
1056 be filled with information
1057
1058 'esds' contains a wealth of information. Memory fails where I figured out
1059 how to parse this atom, but this seems like a decent outline in retrospect:
1060 http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
1061 - but its misleading in lots of places too. For those tracks that support 'esds'
1062 (notably not avc1 or alac), this atom comes in at most 4
1063 sections (section 3 to section 6). Each section is numbered (3 is 0x03) followed
1064 by an optional amount of filler (see APar_skip_filler), then the length of the
1065 section to the end of the atom or the end of another section.
8731066 ----------------------*/
874 void APar_Extract_esds_Info(char* uint32_buffer, FILE* isofile, short track_level_atom, TrackInfo* track_info) {
875 uint64_t offset_into_stsd = 0;
876
877 while (offset_into_stsd < parsedAtoms[track_level_atom].AtomicLength) {
878 offset_into_stsd ++;
879 if ( APar_read32(uint32_buffer, isofile, parsedAtoms[track_level_atom].AtomicStart + offset_into_stsd) == 0x65736473 ) {
880 track_info->contains_esds = true;
881
882 uint64_t esds_start = parsedAtoms[track_level_atom].AtomicStart + offset_into_stsd - 4;
883 uint64_t esds_length = APar_read32(uint32_buffer, isofile, esds_start);
884 uint64_t offset_into_esds = 12; //4bytes length + 4 bytes name + 4bytes null
885
886 if ( APar_read8(isofile, esds_start + offset_into_esds) == 0x03 ) {
887 offset_into_esds++;
888 offset_into_esds += APar_skip_filler(isofile, esds_start + offset_into_esds);
889 }
890
891 uint8_t section3_length = APar_read8(isofile, esds_start + offset_into_esds);
892 if ( section3_length <= esds_length && section3_length != 0) {
893 track_info->section3_length = section3_length;
894 } else {
895 break;
896 }
897
898 //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')
899 //then again, I haven't *completely* read the ISO specifications, so I could just be missing it the the ->voluminous<- 14496-X specifications.
900 uint8_t test_byte = APar_read8(isofile, esds_start + offset_into_esds+1);
901 if (test_byte != 0) {
902 offset_into_esds++;
903 }
904
905 offset_into_esds+= 4; //1 bytes section 0x03 length + 2 bytes + 1 byte
906
907 if ( APar_read8(isofile, esds_start + offset_into_esds) == 0x04 ) {
908 offset_into_esds++;
909 offset_into_esds += APar_skip_filler(isofile, esds_start + offset_into_esds);
910 }
911
912 uint8_t section4_length = APar_read8(isofile, esds_start + offset_into_esds);
913 if ( section4_length <= section3_length && section4_length != 0) {
914 track_info->section4_length = section4_length;
915
916 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)
917
918 offset_into_esds++;
919 track_info->ObjectTypeIndication = APar_read8(isofile, esds_start + offset_into_esds);
920
921 //this is just so that ogg in mp4 won't have some bizarre high bitrate of like 2.8megabits/sec
922 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
923
924 if (track_info->ObjectTypeIndication < 0xC0 && a_v_flag < 0xA0) {//0xC0 marks user streams; but things below that might still be wrong (like 0x6D - png)
925 offset_into_esds+= 5;
926 track_info->max_bitrate = APar_read32(uint32_buffer, isofile, esds_start + offset_into_esds);
927 offset_into_esds+= 4;
928 track_info->avg_bitrate = APar_read32(uint32_buffer, isofile, esds_start + offset_into_esds);
929 offset_into_esds+= 4;
930 }
931 } else {
932 break;
933 }
934
935 if ( APar_read8(isofile, esds_start + offset_into_esds) == 0x05 ) {
936 offset_into_esds++;
937 offset_into_esds += APar_skip_filler(isofile, esds_start + offset_into_esds);
938
939 uint8_t section5_length = APar_read8(isofile, esds_start + offset_into_esds);
940 if ( (section5_length <= section4_length || section4_length == 1) && section5_length != 0) {
941 track_info->section5_length = section5_length;
942 offset_into_esds+=1;
943
944 if (track_info->type_of_track & AUDIO_TRACK) {
945 uint8_t packed_objID = APar_read8(isofile, esds_start + offset_into_esds); //its packed with channel, but channel is fetched separately
946 track_info->descriptor_object_typeID = packed_objID >> 3;
947 offset_into_esds+=1;
948
949 track_info->channels = (uint16_t)APar_ExtractChannelInfo(isofile, esds_start + offset_into_esds);
950
951 } else if (track_info->type_of_track & VIDEO_TRACK) {
952 //technically, visual_object_sequence_start_code should be tested aginst 0x000001B0
953 if (APar_read16(uint32_buffer, isofile, esds_start + offset_into_esds+2) == 0x01B0) {
954 track_info->m4v_profile = APar_read8(isofile, esds_start + offset_into_esds+2+2);
955 }
956 }
957 }
958 break; //uh, I've extracted the pertinent info
959 }
960
961 }
962 if (offset_into_stsd > parsedAtoms[track_level_atom].AtomicLength) {
963 break;
964 }
965 }
966 if ( (track_info->section5_length == 0 && track_info->type_of_track & AUDIO_TRACK) || track_info->channels == 0) {
967 track_info->channels = APar_read16(uint32_buffer, isofile, parsedAtoms[track_level_atom].AtomicStart + 40);
968 }
969 return;
1067 void APar_Extract_esds_Info(char *uint32_buffer,
1068 FILE *isofile,
1069 short track_level_atom,
1070 TrackInfo *track_info) {
1071 uint64_t offset_into_stsd = 0;
1072
1073 while (offset_into_stsd < parsedAtoms[track_level_atom].AtomicLength) {
1074 offset_into_stsd++;
1075 if (APar_read32(uint32_buffer,
1076 isofile,
1077 parsedAtoms[track_level_atom].AtomicStart +
1078 offset_into_stsd) == 0x65736473) {
1079 track_info->contains_esds = true;
1080
1081 uint64_t esds_start =
1082 parsedAtoms[track_level_atom].AtomicStart + offset_into_stsd - 4;
1083 uint64_t esds_length = APar_read32(uint32_buffer, isofile, esds_start);
1084 uint64_t offset_into_esds =
1085 12; // 4bytes length + 4 bytes name + 4bytes null
1086
1087 if (APar_read8(isofile, esds_start + offset_into_esds) == 0x03) {
1088 offset_into_esds++;
1089 offset_into_esds +=
1090 APar_skip_filler(isofile, esds_start + offset_into_esds);
1091 }
1092
1093 uint8_t section3_length =
1094 APar_read8(isofile, esds_start + offset_into_esds);
1095 if (section3_length <= esds_length && section3_length != 0) {
1096 track_info->section3_length = section3_length;
1097 } else {
1098 break;
1099 }
1100
1101 // for whatever reason, when mp4box muxes in ogg into an mp4 container,
1102 // section 3 gets a 0x9D byte (which doesn't fall inline with what AP
1103 // considers 'filler') then again, I haven't *completely* read the ISO
1104 // specifications, so I could just be missing it the the ->voluminous<-
1105 // 14496-X specifications.
1106 uint8_t test_byte =
1107 APar_read8(isofile, esds_start + offset_into_esds + 1);
1108 if (test_byte != 0) {
1109 offset_into_esds++;
1110 }
1111
1112 offset_into_esds += 4; // 1 bytes section 0x03 length + 2 bytes + 1 byte
1113
1114 if (APar_read8(isofile, esds_start + offset_into_esds) == 0x04) {
1115 offset_into_esds++;
1116 offset_into_esds +=
1117 APar_skip_filler(isofile, esds_start + offset_into_esds);
1118 }
1119
1120 uint8_t section4_length =
1121 APar_read8(isofile, esds_start + offset_into_esds);
1122 if (section4_length <= section3_length && section4_length != 0) {
1123 track_info->section4_length = section4_length;
1124
1125 if (section4_length == 0x9D)
1126 offset_into_esds++; // upper limit? when gpac puts an ogg in, section
1127 // 3 is 9D - so is sec4 (section 4 real length
1128 // with ogg = 0x0E86)
1129
1130 offset_into_esds++;
1131 track_info->ObjectTypeIndication =
1132 APar_read8(isofile, esds_start + offset_into_esds);
1133
1134 // this is just so that ogg in mp4 won't have some bizarre high bitrate
1135 // of like 2.8megabits/sec
1136 uint8_t a_v_flag =
1137 APar_read8(isofile,
1138 esds_start + offset_into_esds +
1139 1); // mp4box with ogg will set this to DD,
1140 // mp4a has it as 0x40, mp4v has 0x20
1141
1142 if (track_info->ObjectTypeIndication < 0xC0 &&
1143 a_v_flag < 0xA0) { // 0xC0 marks user streams; but things below that
1144 // might still be wrong (like 0x6D - png)
1145 offset_into_esds += 5;
1146 track_info->max_bitrate = APar_read32(
1147 uint32_buffer, isofile, esds_start + offset_into_esds);
1148 offset_into_esds += 4;
1149 track_info->avg_bitrate = APar_read32(
1150 uint32_buffer, isofile, esds_start + offset_into_esds);
1151 offset_into_esds += 4;
1152 }
1153 } else {
1154 break;
1155 }
1156
1157 if (APar_read8(isofile, esds_start + offset_into_esds) == 0x05) {
1158 offset_into_esds++;
1159 offset_into_esds +=
1160 APar_skip_filler(isofile, esds_start + offset_into_esds);
1161
1162 uint8_t section5_length =
1163 APar_read8(isofile, esds_start + offset_into_esds);
1164 if ((section5_length <= section4_length || section4_length == 1) &&
1165 section5_length != 0) {
1166 track_info->section5_length = section5_length;
1167 offset_into_esds += 1;
1168
1169 if (track_info->type_of_track & AUDIO_TRACK) {
1170 uint8_t packed_objID = APar_read8(
1171 isofile,
1172 esds_start + offset_into_esds); // its packed with channel, but
1173 // channel is fetched separately
1174 track_info->descriptor_object_typeID = packed_objID >> 3;
1175 offset_into_esds += 1;
1176
1177 track_info->channels = (uint16_t)APar_ExtractChannelInfo(
1178 isofile, esds_start + offset_into_esds);
1179
1180 } else if (track_info->type_of_track & VIDEO_TRACK) {
1181 // technically, visual_object_sequence_start_code should be tested
1182 // aginst 0x000001B0
1183 if (APar_read16(uint32_buffer,
1184 isofile,
1185 esds_start + offset_into_esds + 2) == 0x01B0) {
1186 track_info->m4v_profile =
1187 APar_read8(isofile, esds_start + offset_into_esds + 2 + 2);
1188 }
1189 }
1190 }
1191 break; // uh, I've extracted the pertinent info
1192 }
1193 }
1194 if (offset_into_stsd > parsedAtoms[track_level_atom].AtomicLength) {
1195 break;
1196 }
1197 }
1198 if ((track_info->section5_length == 0 &&
1199 track_info->type_of_track & AUDIO_TRACK) ||
1200 track_info->channels == 0) {
1201 track_info->channels = APar_read16(
1202 uint32_buffer, isofile, parsedAtoms[track_level_atom].AtomicStart + 40);
1203 }
1204 return;
9701205 }
9711206
9721207 /*----------------------
9731208 APar_ExtractTrackDetails
974 uint32_buffer - a buffer to read bytes in from the file
975 isofile - the file to be scanned
976 track - the struct proving tracking of this 'trak' atom so we can jump around in this track
977 track_info - a pointer to the struct carrying track-level info to be filled with information
978
979 This function jumps all around in a single 'trak' atom gathering information from different child constituent atoms except 'esds' which is handled
980 on its own.
1209 uint32_buffer - a buffer to read bytes in from the file
1210 isofile - the file to be scanned
1211 track - the struct proving tracking of this 'trak' atom so we can jump
1212 around in this track track_info - a pointer to the struct carrying track-level
1213 info to be filled with information
1214
1215 This function jumps all around in a single 'trak' atom gathering information
1216 from different child constituent atoms except 'esds' which is handled on its
1217 own.
9811218 ----------------------*/
982 void APar_ExtractTrackDetails(char* uint32_buffer, FILE* isofile, Trackage* track, TrackInfo* track_info) {
983 uint64_t _offset = 0;
984
985 APar_TrackLevelInfo(track, "tkhd");
986 if ( APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 8) == 0) {
987 if (APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 11) & 1) {
988 track_info->track_enabled = true;
989 }
990 track_info->creation_time = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 12);
991 track_info->modified_time = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 16);
992 track_info->duration = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 28);
993 } else {
994 track_info->creation_time = APar_read64(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 12);
995 track_info->modified_time = APar_read64(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 20);
996 track_info->duration = APar_read64(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 36);
997 }
998
999 //language code
1000 APar_TrackLevelInfo(track, "mdhd");
1001 memset(uint32_buffer, 0, 5);
1002 uint16_t packed_language = APar_read16(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 28);
1003 memset(track_info->unpacked_lang, 0, 4);
1004 APar_UnpackLanguage(track_info->unpacked_lang, packed_language); //http://www.w3.org/WAI/ER/IG/ert/iso639.htm
1005
1006 //track handler type
1007 APar_TrackLevelInfo(track, "hdlr");
1008 memset(uint32_buffer, 0, 5);
1009 track_info->track_type = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 16);
1010 if (track_info->track_type == 0x736F756E ) { //soun
1011 track_info->type_of_track = AUDIO_TRACK;
1012 } else if (track_info->track_type == 0x76696465 ) { //vide
1013 track_info->type_of_track = VIDEO_TRACK;
1014 }
1015 if ( parsedAtoms[track->track_atom].AtomicLength > 34) {
1016 memset(track_info->track_hdlr_name, 0, 100);
1017 APar_readX(track_info->track_hdlr_name, isofile, parsedAtoms[track->track_atom].AtomicStart + 32, parsedAtoms[track->track_atom].AtomicLength - 32);
1018 }
1019
1020 //codec section
1021 APar_TrackLevelInfo(track, "stsd");
1022 memset(uint32_buffer, 0, 5);
1023 track_info->track_codec = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 20);
1024
1025 if (track_info->type_of_track & VIDEO_TRACK ) { //vide
1026 track_info->video_width = APar_read16(uint32_buffer, isofile, parsedAtoms[track->track_atom+1].AtomicStart + 32);
1027 track_info->video_height = APar_read16(uint32_buffer, isofile, parsedAtoms[track->track_atom+1].AtomicStart + 34);
1028 track_info->macroblocks = (track_info->video_width / 16) * (track_info->video_height / 16);
1029
1030 //avc profile & level
1031 if ( track_info->track_codec == 0x61766331 || track_info->track_codec == 0x64726D69) { //avc1 or drmi
1032 track_info->contains_esds = false;
1033 APar_TrackLevelInfo(track, "avcC");
1034 //get avc1 profile/level; atom 'avcC' is :
1035 //byte 1 configurationVersion byte 2 AVCProfileIndication byte 3 profile_compatibility byte 4 AVCLevelIndication
1036 track_info->avc_version = APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 8);
1037 if (track_info->avc_version == 1) {
1038 track_info->profile = APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 9);
1039 //uint8_t profile_compatibility = APar_read8(isofile, parsedAtoms[track.track_atom].AtomicStart + 10); /* is this reserved ?? */
1040 track_info->level = APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 11);
1041 }
1042
1043 //avc1 doesn't have a hardcoded bitrate, so calculate it (off of stsz table summing) later
1044 } else if (track_info->track_codec == 0x73323633) { //s263
1045 APar_TrackLevelInfo(track, "d263");
1046 if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "d263", 4) == 0) {
1047 APar_Extract_d263_Info(uint32_buffer, isofile, track->track_atom, track_info);
1048 }
1049
1050 } else { //mp4v
1051 APar_TrackLevelInfo(track, "esds");
1052 if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "esds", 4) == 0) {
1053 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++
1054 } else if (track_info->track_codec == 0x73323633) { //s263
1055 track_info->type_of_track = VIDEO_TRACK;
1056 } else if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762
1057 || track_info->track_codec == 0x73617770 || track_info->track_codec == 0x73766D72) { //samr, sawb, sawp & svmr
1058 track_info->type_of_track = AUDIO_TRACK;
1059 } else {
1060 track_info->type_of_track = OTHER_TRACK; //a 'jpeg' track will fall here
1061 }
1062 }
1063
1064 } else if ( track_info->type_of_track & AUDIO_TRACK) {
1065 if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762
1066 || track_info->track_codec == 0x73617770 || track_info->track_codec == 0x73766D72) { //samr,sawb, svmr (sawp doesn't contain modes)
1067 APar_Extract_AMR_Info(uint32_buffer, isofile, track->track_atom+2, track_info);
1068
1069 } else if (track_info->track_codec == 0x73657663) { //sevc
1070 APar_TrackLevelInfo(track, "devc");
1071 if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "devc", 4) == 0) {
1072 APar_Extract_devc_Info(isofile, track->track_atom, track_info);
1073 }
1074
1075 } else if (track_info->track_codec == 0x73716370) { //sqcp
1076 APar_TrackLevelInfo(track, "dqcp");
1077 if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "dqcp", 4) == 0) {
1078 APar_Extract_devc_Info(isofile, track->track_atom, track_info); //its the same thing
1079 }
1080
1081 } else if (track_info->track_codec == 0x73736D76) { //ssmv
1082 APar_TrackLevelInfo(track, "dsmv");
1083 if ( memcmp(parsedAtoms[track->track_atom].AtomicName, "dsmv", 4) == 0) {
1084 APar_Extract_devc_Info(isofile, track->track_atom, track_info); //its the same thing
1085 }
1086
1087 } else {
1088 APar_Extract_esds_Info(uint32_buffer, isofile, track->track_atom, track_info);
1089 }
1090 }
1091
1092 //in case bitrate isn't found, manually determine it off of stsz summing
1093 if ( (track_info->type_of_track & AUDIO_TRACK || track_info->type_of_track & VIDEO_TRACK ) && track_info->avg_bitrate == 0) {
1094 if (track_info->track_codec == 0x616C6163 ) { //alac
1095 track_info->channels = APar_read16(uint32_buffer, isofile, parsedAtoms[track->track_atom+1].AtomicStart + 24);
1096 }
1097 }
1098
1099 APar_TrackLevelInfo(track, "stsz");
1100 if (memcmp(parsedAtoms[track->track_atom].AtomicName, "stsz", 4) == 0) {
1101 track_info->sample_aggregate = calcuate_sample_size(uint32_buffer, isofile, track->track_atom);
1102 }
1103
1104 //get what exactly 'drmX' stands in for
1105 if (track_info->track_codec >= 0x64726D00 && track_info->track_codec <= 0x64726DFF) {
1106 track_info->type_of_track += DRM_PROTECTED_TRACK;
1107 APar_TrackLevelInfo(track, "frma");
1108 memset(uint32_buffer, 0, 5);
1109 track_info->protected_codec = APar_read32(uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 8);
1110 }
1111
1112 //Encoder string; occasionally, it appears under stsd for a video track; it is typcally preceded by ' ²' (1st char is unprintable) or 0x01B2
1113 if (track_info->contains_esds) {
1114 APar_TrackLevelInfo(track, "esds");
1115
1116 //technically, user_data_start_code should be tested aginst 0x000001B2; TODO: it should only be read up to section 3's length too
1117 _offset = APar_FindValueInAtom(uint32_buffer, isofile, track->track_atom, 24, 0x01B2);
1118
1119 if (_offset > 0 && _offset < parsedAtoms[track->track_atom].AtomicLength) {
1120 _offset +=2;
1121 memset(track_info->encoder_name, 0, parsedAtoms[track->track_atom].AtomicLength - _offset);
1122 APar_readX(track_info->encoder_name, isofile, parsedAtoms[track->track_atom].AtomicStart + _offset, parsedAtoms[track->track_atom].AtomicLength - _offset);
1123 }
1124 }
1125 return;
1219 void APar_ExtractTrackDetails(char *uint32_buffer,
1220 FILE *isofile,
1221 Trackage *track,
1222 TrackInfo *track_info) {
1223 uint64_t _offset = 0;
1224
1225 APar_TrackLevelInfo(track, "tkhd");
1226 if (APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 8) ==
1227 0) {
1228 if (APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 11) &
1229 1) {
1230 track_info->track_enabled = true;
1231 }
1232 track_info->creation_time =
1233 APar_read32(uint32_buffer,
1234 isofile,
1235 parsedAtoms[track->track_atom].AtomicStart + 12);
1236 track_info->modified_time =
1237 APar_read32(uint32_buffer,
1238 isofile,
1239 parsedAtoms[track->track_atom].AtomicStart + 16);
1240 track_info->duration =
1241 APar_read32(uint32_buffer,
1242 isofile,
1243 parsedAtoms[track->track_atom].AtomicStart + 28);
1244 } else {
1245 track_info->creation_time =
1246 APar_read64(uint32_buffer,
1247 isofile,
1248 parsedAtoms[track->track_atom].AtomicStart + 12);
1249 track_info->modified_time =
1250 APar_read64(uint32_buffer,
1251 isofile,
1252 parsedAtoms[track->track_atom].AtomicStart + 20);
1253 track_info->duration =
1254 APar_read64(uint32_buffer,
1255 isofile,
1256 parsedAtoms[track->track_atom].AtomicStart + 36);
1257 }
1258
1259 // language code
1260 APar_TrackLevelInfo(track, "mdhd");
1261 memset(uint32_buffer, 0, 5);
1262 uint16_t packed_language = APar_read16(
1263 uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 28);
1264 memset(track_info->unpacked_lang, 0, 4);
1265 APar_UnpackLanguage(
1266 track_info->unpacked_lang,
1267 packed_language); // http://www.w3.org/WAI/ER/IG/ert/iso639.htm
1268
1269 // track handler type
1270 APar_TrackLevelInfo(track, "hdlr");
1271 memset(uint32_buffer, 0, 5);
1272 track_info->track_type = APar_read32(
1273 uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 16);
1274 if (track_info->track_type == 0x736F756E) { // soun
1275 track_info->type_of_track = AUDIO_TRACK;
1276 } else if (track_info->track_type == 0x76696465) { // vide
1277 track_info->type_of_track = VIDEO_TRACK;
1278 }
1279 if (parsedAtoms[track->track_atom].AtomicLength > 34) {
1280 memset(track_info->track_hdlr_name, 0, sizeof(track_info->track_hdlr_name));
1281 APar_readX(track_info->track_hdlr_name,
1282 isofile,
1283 parsedAtoms[track->track_atom].AtomicStart + 32,
1284 std::min((uint64_t)sizeof(track_info->track_hdlr_name),
1285 parsedAtoms[track->track_atom].AtomicLength - 32));
1286 }
1287
1288 // codec section
1289 APar_TrackLevelInfo(track, "stsd");
1290 memset(uint32_buffer, 0, 5);
1291 track_info->track_codec = APar_read32(
1292 uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 20);
1293
1294 if (track_info->type_of_track & VIDEO_TRACK) { // vide
1295 track_info->video_width =
1296 APar_read16(uint32_buffer,
1297 isofile,
1298 parsedAtoms[track->track_atom + 1].AtomicStart + 32);
1299 track_info->video_height =
1300 APar_read16(uint32_buffer,
1301 isofile,
1302 parsedAtoms[track->track_atom + 1].AtomicStart + 34);
1303 track_info->macroblocks =
1304 (track_info->video_width / 16) * (track_info->video_height / 16);
1305
1306 // avc profile & level
1307 if (track_info->track_codec == 0x61766331 ||
1308 track_info->track_codec == 0x64726D69) { // avc1 or drmi
1309 track_info->contains_esds = false;
1310 APar_TrackLevelInfo(track, "avcC");
1311 // get avc1 profile/level; atom 'avcC' is :
1312 // byte 1 configurationVersion byte 2 AVCProfileIndication byte 3
1313 // profile_compatibility byte 4 AVCLevelIndication
1314 track_info->avc_version =
1315 APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 8);
1316 if (track_info->avc_version == 1) {
1317 track_info->profile =
1318 APar_read8(isofile, parsedAtoms[track->track_atom].AtomicStart + 9);
1319 // uint8_t profile_compatibility = APar_read8(isofile,
1320 // parsedAtoms[track.track_atom].AtomicStart + 10); /* is this reserved
1321 // ?? */
1322 track_info->level = APar_read8(
1323 isofile, parsedAtoms[track->track_atom].AtomicStart + 11);
1324 }
1325
1326 // avc1 doesn't have a hardcoded bitrate, so calculate it (off of stsz
1327 // table summing) later
1328 } else if (track_info->track_codec == 0x73323633) { // s263
1329 APar_TrackLevelInfo(track, "d263");
1330 if (memcmp(parsedAtoms[track->track_atom].AtomicName, "d263", 4) == 0) {
1331 APar_Extract_d263_Info(
1332 uint32_buffer, isofile, track->track_atom, track_info);
1333 }
1334
1335 } else { // mp4v
1336 APar_TrackLevelInfo(track, "esds");
1337 if (memcmp(parsedAtoms[track->track_atom].AtomicName, "esds", 4) == 0) {
1338 APar_Extract_esds_Info(
1339 uint32_buffer,
1340 isofile,
1341 track->track_atom - 1,
1342 track_info); // right, backtrack to the atom before 'esds' so we can
1343 // offset_into_stsd++
1344 } else if (track_info->track_codec == 0x73323633) { // s263
1345 track_info->type_of_track = VIDEO_TRACK;
1346 } else if (track_info->track_codec == 0x73616D72 ||
1347 track_info->track_codec == 0x73617762 ||
1348 track_info->track_codec == 0x73617770 ||
1349 track_info->track_codec ==
1350 0x73766D72) { // samr, sawb, sawp & svmr
1351 track_info->type_of_track = AUDIO_TRACK;
1352 } else {
1353 track_info->type_of_track = OTHER_TRACK; // a 'jpeg' track will fall
1354 // here
1355 }
1356 }
1357
1358 } else if (track_info->type_of_track & AUDIO_TRACK) {
1359 if (track_info->track_codec == 0x73616D72 ||
1360 track_info->track_codec == 0x73617762 ||
1361 track_info->track_codec == 0x73617770 ||
1362 track_info->track_codec ==
1363 0x73766D72) { // samr,sawb, svmr (sawp doesn't contain modes)
1364 APar_Extract_AMR_Info(
1365 uint32_buffer, isofile, track->track_atom + 2, track_info);
1366
1367 } else if (track_info->track_codec == 0x73657663) { // sevc
1368 APar_TrackLevelInfo(track, "devc");
1369 if (memcmp(parsedAtoms[track->track_atom].AtomicName, "devc", 4) == 0) {
1370 APar_Extract_devc_Info(isofile, track->track_atom, track_info);
1371 }
1372
1373 } else if (track_info->track_codec == 0x73716370) { // sqcp
1374 APar_TrackLevelInfo(track, "dqcp");
1375 if (memcmp(parsedAtoms[track->track_atom].AtomicName, "dqcp", 4) == 0) {
1376 APar_Extract_devc_Info(isofile,
1377 track->track_atom,
1378 track_info); // its the same thing
1379 }
1380
1381 } else if (track_info->track_codec == 0x73736D76) { // ssmv
1382 APar_TrackLevelInfo(track, "dsmv");
1383 if (memcmp(parsedAtoms[track->track_atom].AtomicName, "dsmv", 4) == 0) {
1384 APar_Extract_devc_Info(isofile,
1385 track->track_atom,
1386 track_info); // its the same thing
1387 }
1388
1389 } else {
1390 APar_Extract_esds_Info(
1391 uint32_buffer, isofile, track->track_atom, track_info);
1392 }
1393 }
1394
1395 // in case bitrate isn't found, manually determine it off of stsz summing
1396 if ((track_info->type_of_track & AUDIO_TRACK ||
1397 track_info->type_of_track & VIDEO_TRACK) &&
1398 track_info->avg_bitrate == 0) {
1399 if (track_info->track_codec == 0x616C6163) { // alac
1400 track_info->channels =
1401 APar_read16(uint32_buffer,
1402 isofile,
1403 parsedAtoms[track->track_atom + 1].AtomicStart + 24);
1404 }
1405 }
1406
1407 APar_TrackLevelInfo(track, "stsz");
1408 if (memcmp(parsedAtoms[track->track_atom].AtomicName, "stsz", 4) == 0) {
1409 track_info->sample_aggregate =
1410 calcuate_sample_size(uint32_buffer, isofile, track->track_atom);
1411 }
1412
1413 // get what exactly 'drmX' stands in for
1414 if (track_info->track_codec >= 0x64726D00 &&
1415 track_info->track_codec <= 0x64726DFF) {
1416 track_info->type_of_track += DRM_PROTECTED_TRACK;
1417 APar_TrackLevelInfo(track, "frma");
1418 memset(uint32_buffer, 0, 5);
1419 track_info->protected_codec = APar_read32(
1420 uint32_buffer, isofile, parsedAtoms[track->track_atom].AtomicStart + 8);
1421 }
1422
1423 // Encoder string; occasionally, it appears under stsd for a video track; it
1424 // is typcally preceded by ' ²' (1st char is unprintable) or 0x01B2
1425 if (track_info->contains_esds) {
1426 APar_TrackLevelInfo(track, "esds");
1427
1428 // technically, user_data_start_code should be tested aginst 0x000001B2;
1429 // TODO: it should only be read up to section 3's length too
1430 _offset = APar_FindValueInAtom(
1431 uint32_buffer, isofile, track->track_atom, 24, 0x01B2);
1432
1433 if (_offset > 0 && _offset < parsedAtoms[track->track_atom].AtomicLength) {
1434 _offset += 2;
1435 memset(track_info->encoder_name,
1436 0,
1437 parsedAtoms[track->track_atom].AtomicLength - _offset);
1438 APar_readX(track_info->encoder_name,
1439 isofile,
1440 parsedAtoms[track->track_atom].AtomicStart + _offset,
1441 parsedAtoms[track->track_atom].AtomicLength - _offset);
1442 }
1443 }
1444 return;
11261445 }
11271446
11281447 /*----------------------
11291448 APar_ExtractMovieDetails
1130 uint32_buffer - a buffer to read bytes in from the file
1131 isofile - the file to be scanned
1132 mvhd_atom - pointer to the 'mvhd' atom and where in the file it can be found
1133
1134 Get information out of 'mvhd' - most important of which are timescale & duration which get used to calcuate bitrate if needed and determine duration
1135 of a track in seconds. A rough approximation of the overall bitrate is done off this too using the sum of the mdat lengths.
1449 uint32_buffer - a buffer to read bytes in from the file
1450 isofile - the file to be scanned
1451 mvhd_atom - pointer to the 'mvhd' atom and where in the file it can be
1452 found
1453
1454 Get information out of 'mvhd' - most important of which are timescale &
1455 duration which get used to calcuate bitrate if needed and determine duration of
1456 a track in seconds. A rough approximation of the overall bitrate is done off
1457 this too using the sum of the mdat lengths.
11361458 ----------------------*/
1137 void APar_ExtractMovieDetails(char* uint32_buffer, FILE* isofile, AtomicInfo* mvhd_atom) {
1138 if (mvhd_atom->AtomicVerFlags & 0x01000000) {
1139 movie_info.creation_time = APar_read64(uint32_buffer, isofile, mvhd_atom->AtomicStart + 12);
1140 movie_info.modified_time = APar_read64(uint32_buffer, isofile, mvhd_atom->AtomicStart + 20);
1141 movie_info.timescale = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 28);
1142 movie_info.duration = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 32);
1143 movie_info.timescale = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 36);
1144 movie_info.duration = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 40);
1145 movie_info.playback_rate = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 44);
1146 movie_info.volume = APar_read16(uint32_buffer, isofile, mvhd_atom->AtomicStart + 48);
1147 } else {
1148 movie_info.creation_time = (uint64_t)APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 12);
1149 movie_info.modified_time = (uint64_t)APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 16);
1150 movie_info.timescale = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 20);
1151 movie_info.duration = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 24);
1152 movie_info.playback_rate = APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 28);
1153 movie_info.volume = APar_read16(uint32_buffer, isofile, mvhd_atom->AtomicStart + 32);
1154 }
1155
1156 movie_info.seconds = (float)movie_info.duration / (float)movie_info.timescale;
1157 #if defined (_MSC_VER)
1158 __int64 media_bits = (__int64)mdatData * 8;
1459 void APar_ExtractMovieDetails(char *uint32_buffer,
1460 FILE *isofile,
1461 AtomicInfo *mvhd_atom) {
1462 if (mvhd_atom->AtomicVerFlags & 0x01000000) {
1463 movie_info.creation_time =
1464 APar_read64(uint32_buffer, isofile, mvhd_atom->AtomicStart + 12);
1465 movie_info.modified_time =
1466 APar_read64(uint32_buffer, isofile, mvhd_atom->AtomicStart + 20);
1467 movie_info.timescale =
1468 APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 28);
1469 movie_info.duration =
1470 APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 32);
1471 movie_info.timescale =
1472 APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 36);
1473 movie_info.duration =
1474 APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 40);
1475 movie_info.playback_rate =
1476 APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 44);
1477 movie_info.volume =
1478 APar_read16(uint32_buffer, isofile, mvhd_atom->AtomicStart + 48);
1479 } else {
1480 movie_info.creation_time = (uint64_t)APar_read32(
1481 uint32_buffer, isofile, mvhd_atom->AtomicStart + 12);
1482 movie_info.modified_time = (uint64_t)APar_read32(
1483 uint32_buffer, isofile, mvhd_atom->AtomicStart + 16);
1484 movie_info.timescale =
1485 APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 20);
1486 movie_info.duration =
1487 APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 24);
1488 movie_info.playback_rate =
1489 APar_read32(uint32_buffer, isofile, mvhd_atom->AtomicStart + 28);
1490 movie_info.volume =
1491 APar_read16(uint32_buffer, isofile, mvhd_atom->AtomicStart + 32);
1492 }
1493
1494 movie_info.seconds = (float)movie_info.duration / (float)movie_info.timescale;
1495 #if defined(_MSC_VER)
1496 __int64 media_bits = (__int64)mdatData * 8;
11591497 #else
1160 uint64_t media_bits = (uint64_t)mdatData * 8;
1498 uint64_t media_bits = (uint64_t)mdatData * 8;
11611499 #endif
1162 movie_info.simple_bitrate_calc = ( (double)media_bits / movie_info.seconds) / 1000.0;
1163
1164 return;
1500 movie_info.simple_bitrate_calc =
1501 ((double)media_bits / movie_info.seconds) / 1000.0;
1502
1503 return;
11651504 }
11661505
11671506 ///////////////////////////////////////////////////////////////////////////////////////
1168 // Get at some track-level info //
1507 // Get at some track-level info //
11691508 ///////////////////////////////////////////////////////////////////////////////////////
11701509
1171 void APar_Print_TrackDetails(TrackInfo* track_info) {
1172 if (track_info->max_bitrate > 0 && track_info->avg_bitrate > 0) {
1173 fprintf(stdout, " %.2f kbp/s", (float)track_info->avg_bitrate/1000.0);
1174 } else { //some ffmpeg encodings have avg_bitrate set to 0, but an inexact max_bitrate - actually, their esds seems a mess to me
1175 #if defined (_MSC_VER)
1176 fprintf(stdout, " %.2lf* kbp/s", ( (double)((__int64)track_info->sample_aggregate) /
1177 ( (double)((__int64)track_info->duration) / (double)((__int64)movie_info.timescale)) ) / 1000.0 * 8);
1178 fprintf(stdout, " %.3f sec", (float)track_info->duration / (float)movie_info.timescale);
1510 void APar_Print_TrackDetails(TrackInfo *track_info) {
1511 if (track_info->max_bitrate > 0 && track_info->avg_bitrate > 0) {
1512 fprintf(stdout, " %.2f kbp/s", (float)track_info->avg_bitrate / 1000.0);
1513 } else { // some ffmpeg encodings have avg_bitrate set to 0, but an inexact
1514 // max_bitrate - actually, their esds seems a mess to me
1515 #if defined(_MSC_VER)
1516 fprintf(stdout,
1517 " %.2lf* kbp/s",
1518 ((double)((__int64)track_info->sample_aggregate) /
1519 ((double)((__int64)track_info->duration) /
1520 (double)((__int64)movie_info.timescale))) /
1521 1000.0 * 8);
1522 fprintf(stdout,
1523 " %.3f sec",
1524 (float)track_info->duration / (float)movie_info.timescale);
11791525 #else
1180 fprintf(stdout, " %.2lf* kbp/s", ( (double)track_info->sample_aggregate /
1181 ( (double)track_info->duration / (double)movie_info.timescale) ) / 1000.0 * 8);
1182 fprintf(stdout, " %.3f sec", (float)track_info->duration / (float)movie_info.timescale);
1526 fprintf(stdout,
1527 " %.2lf* kbp/s",
1528 ((double)track_info->sample_aggregate /
1529 ((double)track_info->duration / (double)movie_info.timescale)) /
1530 1000.0 * 8);
1531 fprintf(stdout,
1532 " %.3f sec",
1533 (float)track_info->duration / (float)movie_info.timescale);
11831534 #endif
1184 }
1185
1186 if (track_info->track_codec == 0x6D703476 ) { //mp4v profile
1187 APar_ShowObjectProfileInfo(MP4V_TRACK, track_info);
1188 } else if (track_info->track_codec == 0x6D703461 || track_info->protected_codec == 0x6D703461 ) { //mp4a profile
1189 APar_ShowObjectProfileInfo(AUDIO_TRACK, track_info);
1190 } else if (track_info->track_codec == 0x616C6163) { //alac - can't figure out a hardcoded bitrate either
1191 fprintf(stdout, " Apple Lossless channels: [%u]\n", track_info->channels);
1192 } else if (track_info->track_codec == 0x61766331 || track_info->protected_codec == 0x61766331) {
1193 if (track_info->avc_version == 1) { //avc profile & level
1194 APar_ShowObjectProfileInfo(AVC1_TRACK, track_info);
1195 }
1196 } else if (track_info->track_codec == 0x73323633) { //s263 in 3gp
1197 APar_ShowObjectProfileInfo(S263_TRACK, track_info);
1198 } else if (track_info->track_codec == 0x73616D72 || track_info->track_codec == 0x73617762
1199 || track_info->track_codec == 0x73617770 || track_info->track_codec == 0x73766D72) { //samr,sawb,sawp & svmr in 3gp
1200 track_info->type_of_track = S_AMR_TRACK;
1201 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1202 } else if (track_info->track_codec == 0x73657663) { //evrc in 3gp
1203 track_info->type_of_track = EVRC_TRACK;
1204 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1205 } else if (track_info->track_codec == 0x73716370) { //qcelp in 3gp
1206 track_info->type_of_track = QCELP_TRACK;
1207 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1208 } else if (track_info->track_codec == 0x73736D76) { //smv in 3gp
1209 track_info->type_of_track = SMV_TRACK;
1210 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1211 } else { //unknown everything, 0 hardcoded bitrate
1212 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1213 fprintf(stdout, "\n");
1214 }
1215
1216 if (track_info->type_of_track & VIDEO_TRACK &&
1217 ( ( track_info->max_bitrate > 0 && track_info->ObjectTypeIndication == 0x20) || track_info->avc_version == 1 || track_info->protected_codec != 0) ) {
1218 fprintf(stdout, " %ux%u (%" PRIu32 " macroblocks)\n", track_info->video_width, track_info->video_height, track_info->macroblocks);
1219 } else if (track_info->type_of_track & VIDEO_TRACK) {
1220 fprintf(stdout, "\n");
1221 }
1222 return;
1223 }
1224
1225 void APar_ExtractDetails(FILE* isofile, uint8_t optional_output) {
1226 char* uint32_buffer=(char*)malloc( sizeof(char)*5 );
1227 Trackage track = {0};
1228
1229 AtomicInfo* mvhdAtom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0);
1230 if (mvhdAtom != NULL) {
1231 APar_ExtractMovieDetails(uint32_buffer, isofile, mvhdAtom);
1232 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);
1233 if (optional_output & SHOW_DATE_INFO) {
1234 fprintf(stdout, " Presentation Creation Date (UTC): %s\n", APar_extract_UTC(movie_info.creation_time) );
1235 fprintf(stdout, " Presentation Modification Date (UTC): %s\n", APar_extract_UTC(movie_info.modified_time) );
1236 }
1237 }
1238
1239 AtomicInfo* iodsAtom = APar_FindAtom("moov.iods", false, VERSIONED_ATOM, 0);
1240 if (iodsAtom != NULL) {
1241 movie_info.contains_iods = true;
1242 APar_Extract_iods_Info(isofile, iodsAtom);
1243 }
1244
1245 if (optional_output & SHOW_TRACK_INFO) {
1246 APar_TrackLevelInfo(&track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here.
1247
1248 fprintf(stdout, "Low-level details. Total tracks: %u\n", track.total_tracks);
1249 fprintf(stdout, "Trk Type Handler Kind Lang Bytes\n");
1250
1251 if (track.total_tracks > 0) {
1252 while (track.total_tracks > track.track_num) {
1253 track.track_num+= 1;
1254 TrackInfo track_info = {0};
1255
1256 //tracknum, handler type, handler name
1257 APar_ExtractTrackDetails(uint32_buffer, isofile, &track, &track_info);
1258 uint16_t more_whitespace = purge_extraneous_characters(track_info.track_hdlr_name);
1259
1260 if (strlen(track_info.track_hdlr_name) == 0) {
1261 memcpy(track_info.track_hdlr_name, "[none listed]", 13);
1262 }
1263 fprintf(stdout, "%u %s %s", track.track_num, uint32tochar4(track_info.track_type, uint32_buffer), track_info.track_hdlr_name);
1264
1265 uint16_t handler_len = strlen(track_info.track_hdlr_name);
1266 if (handler_len < 25 + more_whitespace) {
1267 for (uint16_t i=handler_len; i < 25 + more_whitespace; i++) {
1268 fprintf(stdout, " ");
1269 }
1270 }
1271
1272 //codec, language
1273 fprintf(stdout, " %s %s %" PRIu64, uint32tochar4(track_info.track_codec, uint32_buffer), track_info.unpacked_lang, track_info.sample_aggregate);
1274
1275 if (track_info.encoder_name[0] != 0 && track_info.contains_esds) {
1276 purge_extraneous_characters(track_info.encoder_name);
1277 fprintf(stdout, " Encoder: %s", track_info.encoder_name);
1278 }
1279 if (track_info.type_of_track & DRM_PROTECTED_TRACK) {
1280 fprintf(stdout, " (protected %s)", uint32tochar4(track_info.protected_codec, uint32_buffer) );
1281 }
1282
1283 fprintf(stdout, "\n");
1284 /*---------------------------------*/
1285
1286 if (track_info.type_of_track & VIDEO_TRACK || track_info.type_of_track & AUDIO_TRACK) {
1287 APar_Print_TrackDetails(&track_info);
1288 }
1289
1290 if (optional_output & SHOW_DATE_INFO) {
1291 fprintf(stdout, " Creation Date (UTC): %s\n", APar_extract_UTC(track_info.creation_time) );
1292 fprintf(stdout, " Modification Date (UTC): %s\n", APar_extract_UTC(track_info.modified_time) );
1293 }
1294
1295 }
1296 }
1297 }
1298 return;
1299 }
1300
1301 //provided as a convenience function so that 3rd party utilities can know beforehand
1302 void APar_ExtractBrands(char* filepath) {
1303 FILE* a_file = APar_OpenISOBaseMediaFile(filepath, true);
1304 char* buffer = (char *)calloc(1, sizeof(char)*16);
1305 uint32_t atom_length = 0;
1306 uint8_t file_type_offset = 0;
1307 uint32_t compatible_brand = 0;
1308 bool cb_V2ISOBMFF = false;
1309
1310 APar_read32(buffer, a_file, 4);
1311 if (memcmp(buffer, "ftyp", 4) == 0) {
1312 atom_length = APar_read32(buffer, a_file, 0);
1313 } else {
1314 APar_readX(buffer, a_file, 0, 12);
1315 if (memcmp(buffer, "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A", 12) == 0 ) {
1316 APar_readX(buffer, a_file, 12, 12);
1317 if (memcmp(buffer+4, "ftypmjp2", 8) == 0 || memcmp(buffer+4, "ftypmj2s", 8) == 0) {
1318 atom_length = UInt32FromBigEndian(buffer);
1319 file_type_offset = 12;
1320 }
1321 }
1322 }
1323
1324 if (atom_length > 0) {
1325 memset(buffer, 0, 16);
1326 APar_readX(buffer, a_file, 8+file_type_offset, 4);
1327 printBOM();
1328 fprintf(stdout, " Major Brand: %s", buffer);
1329 APar_IdentifyBrand(buffer);
1330
1331 if (memcmp(buffer, "isom", 4) == 0) {
1332 APar_ScanAtoms(filepath); //scan_file = true;
1333 }
1334
1335 uint32_t minor_version = APar_read32(buffer, a_file, 12+file_type_offset);
1336 fprintf(stdout, " - version %" PRIu32 "\n", minor_version);
1337
1338 fprintf(stdout, " Compatible Brands:");
1339 for (uint64_t i = 16+file_type_offset; i < atom_length; i+=4) {
1340 APar_readX(buffer, a_file, i, 4);
1341 compatible_brand = UInt32FromBigEndian(buffer);
1342 if (compatible_brand != 0) {
1343 fprintf(stdout, " %s", buffer);
1344 if (compatible_brand == 0x6D703432 || compatible_brand == 0x69736F32) {
1345 cb_V2ISOBMFF = true;
1346 }
1347 }
1348 }
1349 fprintf(stdout, "\n");
1350 }
1351
1352 APar_OpenISOBaseMediaFile(filepath, false);
1353
1354 fprintf(stdout, " Tagging schemes available:\n");
1355 switch(metadata_style) {
1356 case ITUNES_STYLE: {
1357 fprintf(stdout, " iTunes-style metadata allowed.\n");
1358 break;
1359 }
1360 case THIRD_GEN_PARTNER:
1361 case THIRD_GEN_PARTNER_VER1_REL6:
1362 case THIRD_GEN_PARTNER_VER1_REL7:
1363 case THIRD_GEN_PARTNER_VER2: {
1364 fprintf(stdout, " 3GP-style asset metadata allowed.\n");
1365 break;
1366 }
1367 case THIRD_GEN_PARTNER_VER2_REL_A: {
1368 fprintf(stdout, " 3GP-style asset metadata allowed [& unimplemented GAD (Geographical Area Description) asset].\n");
1369 break;
1370 }
1371 }
1372 if (cb_V2ISOBMFF || metadata_style == THIRD_GEN_PARTNER_VER1_REL7) {
1373 fprintf(stdout, " ID3 tags on ID32 atoms @ file/movie/track level allowed.\n");
1374 }
1375 fprintf(stdout, " ISO-copyright notices @ movie and/or track level allowed.\n uuid private user extension tags allowed.\n");
1376
1377 free(buffer); buffer=NULL;
1378 return;
1379 }
1535 }
1536
1537 if (track_info->track_codec == 0x6D703476) { // mp4v profile
1538 APar_ShowObjectProfileInfo(MP4V_TRACK, track_info);
1539 } else if (track_info->track_codec == 0x6D703461 ||
1540 track_info->protected_codec == 0x6D703461) { // mp4a profile
1541 APar_ShowObjectProfileInfo(AUDIO_TRACK, track_info);
1542 } else if (track_info->track_codec ==
1543 0x616C6163) { // alac - can't figure out a hardcoded bitrate either
1544 fprintf(
1545 stdout, " Apple Lossless channels: [%u]\n", track_info->channels);
1546 } else if (track_info->track_codec == 0x61766331 ||
1547 track_info->protected_codec == 0x61766331) {
1548 if (track_info->avc_version == 1) { // avc profile & level
1549 APar_ShowObjectProfileInfo(AVC1_TRACK, track_info);
1550 }
1551 } else if (track_info->track_codec == 0x73323633) { // s263 in 3gp
1552 APar_ShowObjectProfileInfo(S263_TRACK, track_info);
1553 } else if (track_info->track_codec == 0x73616D72 ||
1554 track_info->track_codec == 0x73617762 ||
1555 track_info->track_codec == 0x73617770 ||
1556 track_info->track_codec ==
1557 0x73766D72) { // samr,sawb,sawp & svmr in 3gp
1558 track_info->type_of_track = S_AMR_TRACK;
1559 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1560 } else if (track_info->track_codec == 0x73657663) { // evrc in 3gp
1561 track_info->type_of_track = EVRC_TRACK;
1562 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1563 } else if (track_info->track_codec == 0x73716370) { // qcelp in 3gp
1564 track_info->type_of_track = QCELP_TRACK;
1565 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1566 } else if (track_info->track_codec == 0x73736D76) { // smv in 3gp
1567 track_info->type_of_track = SMV_TRACK;
1568 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1569 } else { // unknown everything, 0 hardcoded bitrate
1570 APar_ShowObjectProfileInfo(track_info->type_of_track, track_info);
1571 fprintf(stdout, "\n");
1572 }
1573
1574 if (track_info->type_of_track & VIDEO_TRACK &&
1575 ((track_info->max_bitrate > 0 &&
1576 track_info->ObjectTypeIndication == 0x20) ||
1577 track_info->avc_version == 1 || track_info->protected_codec != 0)) {
1578 fprintf(stdout,
1579 " %ux%u (%" PRIu32 " macroblocks)\n",
1580 track_info->video_width,
1581 track_info->video_height,
1582 track_info->macroblocks);
1583 } else if (track_info->type_of_track & VIDEO_TRACK) {
1584 fprintf(stdout, "\n");
1585 }
1586 return;
1587 }
1588
1589 void APar_ExtractDetails(FILE *isofile, uint8_t optional_output) {
1590 char uint32_buffer[8];
1591 Trackage track = {0};
1592
1593 AtomicInfo *mvhdAtom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0);
1594 if (mvhdAtom != NULL) {
1595 APar_ExtractMovieDetails(uint32_buffer, isofile, mvhdAtom);
1596 fprintf(stdout,
1597 "Movie duration: %.3lf seconds (%s) - %.2lf* kbp/sec bitrate "
1598 "(*=approximate)\n",
1599 movie_info.seconds,
1600 secsTOtime(movie_info.seconds),
1601 movie_info.simple_bitrate_calc);
1602 if (optional_output & SHOW_DATE_INFO) {
1603 fprintf(stdout,
1604 " Presentation Creation Date (UTC): %s\n",
1605 APar_extract_UTC(movie_info.creation_time));
1606 fprintf(stdout,
1607 " Presentation Modification Date (UTC): %s\n",
1608 APar_extract_UTC(movie_info.modified_time));
1609 }
1610 }
1611
1612 AtomicInfo *iodsAtom = APar_FindAtom("moov.iods", false, VERSIONED_ATOM, 0);
1613 if (iodsAtom != NULL) {
1614 movie_info.contains_iods = true;
1615 APar_Extract_iods_Info(isofile, iodsAtom);
1616 }
1617
1618 if (optional_output & SHOW_TRACK_INFO) {
1619 APar_TrackLevelInfo(&track,
1620 NULL); // With track_num set to 0, it will return the
1621 // total trak atom into total_tracks here.
1622
1623 fprintf(
1624 stdout, "Low-level details. Total tracks: %u\n", track.total_tracks);
1625 fprintf(stdout,
1626 "Trk Type Handler Kind Lang Bytes\n");
1627
1628 if (track.total_tracks > 0) {
1629 while (track.total_tracks > track.track_num) {
1630 track.track_num += 1;
1631 TrackInfo track_info = {0};
1632
1633 // tracknum, handler type, handler name
1634 APar_ExtractTrackDetails(uint32_buffer, isofile, &track, &track_info);
1635 uint16_t more_whitespace =
1636 purge_extraneous_characters(track_info.track_hdlr_name);
1637
1638 if (strlen(track_info.track_hdlr_name) == 0) {
1639 memcpy(track_info.track_hdlr_name, "[none listed]", 13);
1640 }
1641 fprintf(stdout,
1642 "%u %s %s",
1643 track.track_num,
1644 uint32tochar4(track_info.track_type, uint32_buffer),
1645 track_info.track_hdlr_name);
1646
1647 uint16_t handler_len = strlen(track_info.track_hdlr_name);
1648 if (handler_len < 25 + more_whitespace) {
1649 for (uint16_t i = handler_len; i < 25 + more_whitespace; i++) {
1650 fprintf(stdout, " ");
1651 }
1652 }
1653
1654 // codec, language
1655 fprintf(stdout,
1656 " %s %s %" PRIu64,
1657 uint32tochar4(track_info.track_codec, uint32_buffer),
1658 track_info.unpacked_lang,
1659 track_info.sample_aggregate);
1660
1661 if (track_info.encoder_name[0] != 0 && track_info.contains_esds) {
1662 purge_extraneous_characters(track_info.encoder_name);
1663 fprintf(stdout, " Encoder: %s", track_info.encoder_name);
1664 }
1665 if (track_info.type_of_track & DRM_PROTECTED_TRACK) {
1666 fprintf(stdout,
1667 " (protected %s)",
1668 uint32tochar4(track_info.protected_codec, uint32_buffer));
1669 }
1670
1671 fprintf(stdout, "\n");
1672 /*---------------------------------*/
1673
1674 if (track_info.type_of_track & VIDEO_TRACK ||
1675 track_info.type_of_track & AUDIO_TRACK) {
1676 APar_Print_TrackDetails(&track_info);
1677 }
1678
1679 if (optional_output & SHOW_DATE_INFO) {
1680 fprintf(stdout,
1681 " Creation Date (UTC): %s\n",
1682 APar_extract_UTC(track_info.creation_time));
1683 fprintf(stdout,
1684 " Modification Date (UTC): %s\n",
1685 APar_extract_UTC(track_info.modified_time));
1686 }
1687 }
1688 }
1689 }
1690 }
1691
1692 // provided as a convenience function so that 3rd party utilities can know
1693 // beforehand
1694 void APar_ExtractBrands(char *filepath) {
1695 FILE *a_file = APar_OpenISOBaseMediaFile(filepath, true);
1696 char buffer[16] = {};
1697 uint32_t atom_length = 0;
1698 uint8_t file_type_offset = 0;
1699 uint32_t compatible_brand = 0;
1700 bool cb_V2ISOBMFF = false;
1701
1702 APar_read32(buffer, a_file, 4);
1703 if (memcmp(buffer, "ftyp", 4) == 0) {
1704 atom_length = APar_read32(buffer, a_file, 0);
1705 } else {
1706 APar_readX(buffer, a_file, 0, 12);
1707 if (memcmp(buffer,
1708 "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A",
1709 12) == 0) {
1710 APar_readX(buffer, a_file, 12, 12);
1711 if (memcmp(buffer + 4, "ftypmjp2", 8) == 0 ||
1712 memcmp(buffer + 4, "ftypmj2s", 8) == 0) {
1713 atom_length = UInt32FromBigEndian(buffer);
1714 file_type_offset = 12;
1715 }
1716 }
1717 }
1718
1719 if (atom_length > 0) {
1720 memset(buffer, 0, 16);
1721 APar_readX(buffer, a_file, 8 + file_type_offset, 4);
1722 printBOM();
1723 fprintf(stdout, " Major Brand: %s", buffer);
1724 APar_IdentifyBrand(buffer);
1725
1726 if (memcmp(buffer, "isom", 4) == 0) {
1727 APar_ScanAtoms(filepath); // scan_file = true;
1728 }
1729
1730 uint32_t minor_version = APar_read32(buffer, a_file, 12 + file_type_offset);
1731 fprintf(stdout, " - version %" PRIu32 "\n", minor_version);
1732
1733 fprintf(stdout, " Compatible Brands:");
1734 for (uint64_t i = 16 + file_type_offset; i < atom_length; i += 4) {
1735 APar_readX(buffer, a_file, i, 4);
1736 compatible_brand = UInt32FromBigEndian(buffer);
1737 if (compatible_brand != 0) {
1738 fprintf(stdout, " %s", buffer);
1739 if (compatible_brand == 0x6D703432 || compatible_brand == 0x69736F32) {
1740 cb_V2ISOBMFF = true;
1741 }
1742 }
1743 }
1744 fprintf(stdout, "\n");
1745 }
1746
1747 APar_OpenISOBaseMediaFile(filepath, false);
1748
1749 fprintf(stdout, " Tagging schemes available:\n");
1750 switch (metadata_style) {
1751 case ITUNES_STYLE: {
1752 fprintf(stdout, " iTunes-style metadata allowed.\n");
1753 break;
1754 }
1755 case THIRD_GEN_PARTNER:
1756 case THIRD_GEN_PARTNER_VER1_REL6:
1757 case THIRD_GEN_PARTNER_VER1_REL7:
1758 case THIRD_GEN_PARTNER_VER2: {
1759 fprintf(stdout, " 3GP-style asset metadata allowed.\n");
1760 break;
1761 }
1762 case THIRD_GEN_PARTNER_VER2_REL_A: {
1763 fprintf(stdout,
1764 " 3GP-style asset metadata allowed [& unimplemented GAD "
1765 "(Geographical Area Description) asset].\n");
1766 break;
1767 }
1768 }
1769 if (cb_V2ISOBMFF || metadata_style == THIRD_GEN_PARTNER_VER1_REL7) {
1770 fprintf(stdout,
1771 " ID3 tags on ID32 atoms @ file/movie/track level allowed.\n");
1772 }
1773 fprintf(stdout,
1774 " ISO-copyright notices @ movie and/or track level "
1775 "allowed.\n uuid private user extension tags allowed.\n");
1776 }
11 /*
22 AtomicParsley - iconv.cpp
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2005-2007 puck_lock
17 Copyright (C) 2005-2007 puck_lock
1818 with contributions from others; see the CREDITS file
1919 */
2020 //==================================================================//
5252 #include "AtomicParsley.h"
5353
5454 const unsigned short cp437upperbytes[128] = {
55 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
56 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
57 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
58 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
59 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
60 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
61 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
62 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
63 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
64 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
65 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
66 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
67 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
68 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
69 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
70 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
71 };
55 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA,
56 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6,
57 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC,
58 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA,
59 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC,
60 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561,
61 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B,
62 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
63 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568,
64 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518,
65 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393,
66 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4,
67 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320,
68 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2,
69 0x25A0, 0x00A0};
7270
7371 const unsigned short cp850upperbytes[128] = {
74 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
75 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
76 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
77 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
78 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
79 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
80 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
81 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
82 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
83 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
84 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
85 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
86 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
87 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
88 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
89 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
90 };
72 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA,
73 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6,
74 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC,
75 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA,
76 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC,
77 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1,
78 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5,
79 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
80 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x00F0,
81 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518,
82 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 0x00D3, 0x00DF, 0x00D4,
83 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9,
84 0x00FD, 0x00DD, 0x00AF, 0x00B4, 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6,
85 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2,
86 0x25A0, 0x00A0};
9187
9288 const unsigned short cp852upperbytes[128] = {
93 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7,
94 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
95 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A,
96 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
97 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E,
98 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
99 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A,
100 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
101 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103,
102 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
103 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE,
104 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
105 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161,
106 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
107 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8,
108 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
109 };
89 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142,
90 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, 0x00C9, 0x0139,
91 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC,
92 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, 0x00E1, 0x00ED, 0x00F3, 0x00FA,
93 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C,
94 0x015F, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1,
95 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C,
96 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103,
97 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x0111,
98 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518,
99 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, 0x00D3, 0x00DF, 0x00D4,
100 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170,
101 0x00FD, 0x00DD, 0x0163, 0x00B4, 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8,
102 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159,
103 0x25A0, 0x00A0};
110104
111105 const unsigned short cp855upperbytes[128] = {
112 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404,
113 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
114 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C,
115 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
116 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414,
117 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
118 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438,
119 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
120 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A,
121 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
122 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E,
123 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
124 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443,
125 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
126 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D,
127 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
128 };
106 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455,
107 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, 0x0459, 0x0409,
108 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F,
109 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, 0x0430, 0x0410, 0x0431, 0x0411,
110 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433,
111 0x0413, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445,
112 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419,
113 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A,
114 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x043B,
115 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518,
116 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, 0x042F, 0x0440, 0x0420,
117 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432,
118 0x0412, 0x044C, 0x042C, 0x2116, 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417,
119 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7,
120 0x25A0, 0x00A0};
129121
130122 const unsigned short cp858upperbytes[128] = {
131 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
132 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
133 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
134 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
135 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
136 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
137 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
138 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
139 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
140 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
141 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,
142 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
143 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
144 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
145 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
146 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
147 };
123 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA,
124 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 0x00C9, 0x00E6,
125 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC,
126 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 0x00E1, 0x00ED, 0x00F3, 0x00FA,
127 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC,
128 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1,
129 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5,
130 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
131 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 0x00F0,
132 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE, 0x00CF, 0x2518,
133 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 0x00D3, 0x00DF, 0x00D4,
134 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9,
135 0x00FD, 0x00DD, 0x00AF, 0x00B4, 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6,
136 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2,
137 0x25A0, 0x00A0};
148138
149139 //==================================================================//
150140 // utf conversion functions from libxml2
177167
178168 // Original code for IsoLatin1 and UTF-16 by "Martin J. Duerst" <duerst@w3.org>
179169
180 static int xmlLittleEndian =
170 static int xmlLittleEndian =
181171 #ifdef WORDS_BIGENDIAN
182 0
172 0
183173 #else
184 1
174 1
185175 #endif
186 ;
176 ;
187177
188178 /**
189179 * isolat1ToUTF8:
199189 * if the return value is positive, else unpredictable.
200190 * The value of @outlen after return is the number of octets consumed.
201191 */
202 int isolat1ToUTF8(unsigned char* out, int outlen, const unsigned char* in, int inlen) {
203 unsigned char* outstart = out;
204 const unsigned char* base = in;
205 unsigned char* outend;
206 const unsigned char* inend;
207 const unsigned char* instop;
208
209 if ((out == NULL) || (in == NULL) || (outlen == 0) || (inlen == 0))
210 return(-1);
211
212 outend = out + outlen;
213 inend = in + (inlen);
214 instop = inend;
215
216 while (in < inend && out < outend - 1) {
217 if (*in >= 0x80) {
218 *out++ = (((*in) >> 6) & 0x1F) | 0xC0;
219 *out++ = ((*in) & 0x3F) | 0x80;
220 ++in;
221 }
222 if (instop - in > outend - out) instop = in + (outend - out);
223 while (in < instop && *in < 0x80) {
224 *out++ = *in++;
225 }
226 }
227 if (in < inend && out < outend && *in < 0x80) {
228 *out++ = *in++;
229 }
230 outlen = out - outstart;
231 inlen = in - base;
232 return(outlen);
192 int isolat1ToUTF8(unsigned char *out,
193 int outlen,
194 const unsigned char *in,
195 int inlen) {
196 unsigned char *outstart = out;
197 const unsigned char *base = in;
198 unsigned char *outend;
199 const unsigned char *inend;
200 const unsigned char *instop;
201
202 if ((out == NULL) || (in == NULL) || (outlen == 0) || (inlen == 0))
203 return (-1);
204
205 outend = out + outlen;
206 inend = in + (inlen);
207 instop = inend;
208
209 while (in < inend && out < outend - 1) {
210 if (*in >= 0x80) {
211 *out++ = (((*in) >> 6) & 0x1F) | 0xC0;
212 *out++ = ((*in) & 0x3F) | 0x80;
213 ++in;
214 }
215 if (instop - in > outend - out)
216 instop = in + (outend - out);
217 while (in < instop && *in < 0x80) {
218 *out++ = *in++;
219 }
220 }
221 if (in < inend && out < outend && *in < 0x80) {
222 *out++ = *in++;
223 }
224 outlen = out - outstart;
225 inlen = in - base;
226 return (outlen);
233227 }
234228
235229 /**
248242 * if the return value is positive, else unpredictable.
249243 * The value of @outlen after return is the number of octets consumed.
250244 */
251 int UTF8Toisolat1(unsigned char* out, int outlen, const unsigned char* in, int inlen) {
252 const unsigned char* processed = in;
253 const unsigned char* outend;
254 const unsigned char* outstart = out;
255 const unsigned char* instart = in;
256 const unsigned char* inend;
257 unsigned int c, d;
258 int trailing;
259
260 if ((out == NULL) || (outlen == 0) || (inlen == 0)) return(-1);
261 if (in == NULL) {
262 /*
263 * initialization nothing to do
264 */
265 outlen = 0;
266 inlen = 0;
267 return(0);
268 }
269 inend = in + (inlen);
270 outend = out + (outlen);
271 while (in < inend) {
272 d = *in++;
273 if (d < 0x80) { c= d; trailing= 0; }
274 else if (d < 0xC0) {
275 /* trailing byte in leading position */
276 outlen = out - outstart;
277 inlen = processed - instart;
278 return(-2);
279 } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
280 else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
281 else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
282 else {
283 /* no chance for this in IsoLat1 */
284 outlen = out - outstart;
285 inlen = processed - instart;
286 return(-2);
287 }
288
289 if (inend - in < trailing) {
290 break;
291 }
292
293 for ( ; trailing; trailing--) {
294 if (in >= inend)
295 break;
296 if (((d= *in++) & 0xC0) != 0x80) {
297 outlen = out - outstart;
298 inlen = processed - instart;
299 return(-2);
300 }
301 c <<= 6;
302 c |= d & 0x3F;
303 }
304
305 /* assertion: c is a single UTF-4 value */
306 if (c <= 0xFF) {
307 if (out >= outend)
308 break;
309 *out++ = c;
310 } else {
311 /* no chance for this in IsoLat1 */
312 outlen = out - outstart;
313 inlen = processed - instart;
314 return(-2);
315 }
316 processed = in;
317 }
318 outlen = out - outstart;
319 inlen = processed - instart;
320 return(outlen);
245 int UTF8Toisolat1(unsigned char *out,
246 int outlen,
247 const unsigned char *in,
248 int inlen) {
249 const unsigned char *processed = in;
250 const unsigned char *outend;
251 const unsigned char *outstart = out;
252 const unsigned char *instart = in;
253 const unsigned char *inend;
254 unsigned int c, d;
255 int trailing;
256
257 if ((out == NULL) || (outlen == 0) || (inlen == 0))
258 return (-1);
259 if (in == NULL) {
260 /*
261 * initialization nothing to do
262 */
263 outlen = 0;
264 inlen = 0;
265 return (0);
266 }
267 inend = in + (inlen);
268 outend = out + (outlen);
269 while (in < inend) {
270 d = *in++;
271 if (d < 0x80) {
272 c = d;
273 trailing = 0;
274 } else if (d < 0xC0) {
275 /* trailing byte in leading position */
276 outlen = out - outstart;
277 inlen = processed - instart;
278 return (-2);
279 } else if (d < 0xE0) {
280 c = d & 0x1F;
281 trailing = 1;
282 } else if (d < 0xF0) {
283 c = d & 0x0F;
284 trailing = 2;
285 } else if (d < 0xF8) {
286 c = d & 0x07;
287 trailing = 3;
288 } else {
289 /* no chance for this in IsoLat1 */
290 outlen = out - outstart;
291 inlen = processed - instart;
292 return (-2);
293 }
294
295 if (inend - in < trailing) {
296 break;
297 }
298
299 for (; trailing; trailing--) {
300 if (in >= inend)
301 break;
302 if (((d = *in++) & 0xC0) != 0x80) {
303 outlen = out - outstart;
304 inlen = processed - instart;
305 return (-2);
306 }
307 c <<= 6;
308 c |= d & 0x3F;
309 }
310
311 /* assertion: c is a single UTF-4 value */
312 if (c <= 0xFF) {
313 if (out >= outend)
314 break;
315 *out++ = c;
316 } else {
317 /* no chance for this in IsoLat1 */
318 outlen = out - outstart;
319 inlen = processed - instart;
320 return (-2);
321 }
322 processed = in;
323 }
324 outlen = out - outstart;
325 inlen = processed - instart;
326 return (outlen);
321327 }
322328
323329 /**
337343 * The value of *inlen after return is the number of octets consumed
338344 * if the return value is positive, else unpredictable.
339345 */
340 int UTF16BEToUTF8(unsigned char* out, int outlen, const unsigned char* inb, int inlenb)
341 {
342 unsigned char* outstart = out;
343 const unsigned char* processed = inb;
344 unsigned char* outend = out + outlen;
345 unsigned short* in = (unsigned short*) inb;
346 unsigned short* inend;
347 unsigned int c, d, inlen;
348 unsigned char *tmp;
349 int bits;
350
351 if ((inlenb % 2) == 1)
352 (inlenb)--;
353 inlen = inlenb / 2;
354 inend= in + inlen;
355 while (in < inend) {
356 if (xmlLittleEndian) {
357 tmp = (unsigned char *) in;
358 c = *tmp++;
359 c = c << 8;
360 c = c | (unsigned int) *tmp;
361 in++;
362 } else {
363 c= *in++;
364
365 if (c == 0xFEFF) {
366 c= *in++; //skip BOM
367 }
368 }
369 if ((c & 0xFC00) == 0xD800) { /* surrogates */
370 if (in >= inend) { /* (in > inend) shouldn't happens */
371 outlen = out - outstart;
372 inlenb = processed - inb;
373 return(-2);
374 }
375 if (xmlLittleEndian) {
376 tmp = (unsigned char *) in;
377 d = *tmp++;
378 d = d << 8;
379 d = d | (unsigned int) *tmp;
380 in++;
381 } else {
382 d= *in++;
383 }
384 if ((d & 0xFC00) == 0xDC00) {
385 c &= 0x03FF;
386 c <<= 10;
387 c |= d & 0x03FF;
388 c += 0x10000;
389 }
390 else {
391 outlen = out - outstart;
392 inlenb = processed - inb;
393 return(-2);
394 }
395 }
396
397 /* assertion: c is a single UTF-4 value */
398 if (out >= outend)
399 break;
400 if (c < 0x80) { *out++= c; bits= -6; }
401 else if (c < 0x800) { *out++= ((c >> 6) & 0x1F) | 0xC0; bits= 0; }
402 else if (c < 0x10000) { *out++= ((c >> 12) & 0x0F) | 0xE0; bits= 6; }
403 else { *out++= ((c >> 18) & 0x07) | 0xF0; bits= 12; }
404
405 for ( ; bits >= 0; bits-= 6) {
406 if (out >= outend)
407 break;
408 *out++= ((c >> bits) & 0x3F) | 0x80;
409 }
410 processed = (const unsigned char*) in;
411 }
412 outlen = out - outstart;
413 inlenb = processed - inb;
414 return(outlen);
415 }
416
346 int UTF16BEToUTF8(unsigned char *out,
347 int outlen,
348 const unsigned char *inb,
349 int inlenb) {
350 unsigned char *outstart = out;
351 const unsigned char *processed = inb;
352 unsigned char *outend = out + outlen;
353 unsigned short *in = (unsigned short *)inb;
354 unsigned short *inend;
355 unsigned int c, d, inlen;
356 unsigned char *tmp;
357 int bits;
358
359 if ((inlenb % 2) == 1)
360 (inlenb)--;
361 inlen = inlenb / 2;
362 inend = in + inlen;
363 while (in < inend) {
364 if (xmlLittleEndian) {
365 tmp = (unsigned char *)in;
366 c = *tmp++;
367 c = c << 8;
368 c = c | (unsigned int)*tmp;
369 in++;
370 } else {
371 c = *in++;
372
373 if (c == 0xFEFF) {
374 c = *in++; // skip BOM
375 }
376 }
377 if ((c & 0xFC00) == 0xD800) { /* surrogates */
378 if (in >= inend) { /* (in > inend) shouldn't happens */
379 outlen = out - outstart;
380 inlenb = processed - inb;
381 return (-2);
382 }
383 if (xmlLittleEndian) {
384 tmp = (unsigned char *)in;
385 d = *tmp++;
386 d = d << 8;
387 d = d | (unsigned int)*tmp;
388 in++;
389 } else {
390 d = *in++;
391 }
392 if ((d & 0xFC00) == 0xDC00) {
393 c &= 0x03FF;
394 c <<= 10;
395 c |= d & 0x03FF;
396 c += 0x10000;
397 } else {
398 outlen = out - outstart;
399 inlenb = processed - inb;
400 return (-2);
401 }
402 }
403
404 /* assertion: c is a single UTF-4 value */
405 if (out >= outend)
406 break;
407 if (c < 0x80) {
408 *out++ = c;
409 bits = -6;
410 } else if (c < 0x800) {
411 *out++ = ((c >> 6) & 0x1F) | 0xC0;
412 bits = 0;
413 } else if (c < 0x10000) {
414 *out++ = ((c >> 12) & 0x0F) | 0xE0;
415 bits = 6;
416 } else {
417 *out++ = ((c >> 18) & 0x07) | 0xF0;
418 bits = 12;
419 }
420
421 for (; bits >= 0; bits -= 6) {
422 if (out >= outend)
423 break;
424 *out++ = ((c >> bits) & 0x3F) | 0x80;
425 }
426 processed = (const unsigned char *)in;
427 }
428 outlen = out - outstart;
429 inlenb = processed - inb;
430 return (outlen);
431 }
432
417433 /**
418434 * UTF8ToUTF16BE:
419435 * @outb: a pointer to an array of bytes to store the result
425441 * block of chars out.
426442 *
427443 * Returns the number of byte written, or -1 by lack of space, or -2
428 * if the transcoding failed.
444 * if the transcoding failed.
429445 */
430 int UTF8ToUTF16BE(unsigned char* outb, int outlen, const unsigned char* in, int inlen)
431 {
432 unsigned short* out = (unsigned short*) outb;
433 const unsigned char* processed = in;
434 const unsigned char *const instart = in;
435 unsigned short* outstart= out;
436 unsigned short* outend;
437 const unsigned char* inend= in+inlen;
438 unsigned int c, d;
439 int trailing;
440 unsigned char *tmp;
441 unsigned short tmp1, tmp2;
442
443 /* UTF-16BE has no BOM */
444 if ((outb == NULL) || (outlen == 0) || (inlen == 0)) return(-1);
445 if (in == NULL) {
446 outlen = 0;
447 inlen = 0;
448 return(0);
449 }
450 outend = out + (outlen / 2);
451 while (in < inend) {
452 d= *in++;
453 if (d < 0x80) { c= d; trailing= 0; }
454 else if (d < 0xC0) {
455 /* trailing byte in leading position */
456 outlen = out - outstart;
457 inlen = processed - instart;
458 return(-2);
459 } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
460 else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
461 else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
462 else {
463 /* no chance for this in UTF-16 */
464 outlen = out - outstart;
465 inlen = processed - instart;
466 return(-2);
467 }
468
469 if (inend - in < trailing) {
470 break;
471 }
472
473 for ( ; trailing; trailing--) {
474 if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80)) break;
475 c <<= 6;
476 c |= d & 0x3F;
477 }
478
479 /* assertion: c is a single UTF-4 value */
480 if (c < 0x10000) {
481 if (out >= outend) break;
482 if (xmlLittleEndian) {
483 tmp = (unsigned char *) out;
484 *tmp = c >> 8;
485 *(tmp + 1) = c;
486 out++;
487 } else {
488 *out++ = c;
489 }
490 }
491 else if (c < 0x110000) {
492 if (out+1 >= outend) break;
493 c -= 0x10000;
494 if (xmlLittleEndian) {
495 tmp1 = 0xD800 | (c >> 10);
496 tmp = (unsigned char *) out;
497 *tmp = tmp1 >> 8;
498 *(tmp + 1) = (unsigned char) tmp1;
499 out++;
500
501 tmp2 = 0xDC00 | (c & 0x03FF);
502 tmp = (unsigned char *) out;
503 *tmp = tmp2 >> 8;
504 *(tmp + 1) = (unsigned char) tmp2;
505 out++;
506 } else {
507 *out++ = 0xD800 | (c >> 10);
508 *out++ = 0xDC00 | (c & 0x03FF);
509 }
510 }
511 else
512 break;
513 processed = in;
514 }
515 outlen = (out - outstart) * 2;
516 inlen = processed - instart;
517 return(outlen);
446 int UTF8ToUTF16BE(unsigned char *outb,
447 int outlen,
448 const unsigned char *in,
449 int inlen) {
450 unsigned short *out = (unsigned short *)outb;
451 const unsigned char *processed = in;
452 const unsigned char *const instart = in;
453 unsigned short *outstart = out;
454 unsigned short *outend;
455 const unsigned char *inend = in + inlen;
456 unsigned int c, d;
457 int trailing;
458 unsigned char *tmp;
459 unsigned short tmp1, tmp2;
460
461 /* UTF-16BE has no BOM */
462 if ((outb == NULL) || (outlen == 0) || (inlen == 0))
463 return (-1);
464 if (in == NULL) {
465 outlen = 0;
466 inlen = 0;
467 return (0);
468 }
469 outend = out + (outlen / 2);
470 while (in < inend) {
471 d = *in++;
472 if (d < 0x80) {
473 c = d;
474 trailing = 0;
475 } else if (d < 0xC0) {
476 /* trailing byte in leading position */
477 outlen = out - outstart;
478 inlen = processed - instart;
479 return (-2);
480 } else if (d < 0xE0) {
481 c = d & 0x1F;
482 trailing = 1;
483 } else if (d < 0xF0) {
484 c = d & 0x0F;
485 trailing = 2;
486 } else if (d < 0xF8) {
487 c = d & 0x07;
488 trailing = 3;
489 } else {
490 /* no chance for this in UTF-16 */
491 outlen = out - outstart;
492 inlen = processed - instart;
493 return (-2);
494 }
495
496 if (inend - in < trailing) {
497 break;
498 }
499
500 for (; trailing; trailing--) {
501 if ((in >= inend) || (((d = *in++) & 0xC0) != 0x80))
502 break;
503 c <<= 6;
504 c |= d & 0x3F;
505 }
506
507 /* assertion: c is a single UTF-4 value */
508 if (c < 0x10000) {
509 if (out >= outend)
510 break;
511 if (xmlLittleEndian) {
512 tmp = (unsigned char *)out;
513 *tmp = c >> 8;
514 *(tmp + 1) = c;
515 out++;
516 } else {
517 *out++ = c;
518 }
519 } else if (c < 0x110000) {
520 if (out + 1 >= outend)
521 break;
522 c -= 0x10000;
523 if (xmlLittleEndian) {
524 tmp1 = 0xD800 | (c >> 10);
525 tmp = (unsigned char *)out;
526 *tmp = tmp1 >> 8;
527 *(tmp + 1) = (unsigned char)tmp1;
528 out++;
529
530 tmp2 = 0xDC00 | (c & 0x03FF);
531 tmp = (unsigned char *)out;
532 *tmp = tmp2 >> 8;
533 *(tmp + 1) = (unsigned char)tmp2;
534 out++;
535 } else {
536 *out++ = 0xD800 | (c >> 10);
537 *out++ = 0xDC00 | (c & 0x03FF);
538 }
539 } else
540 break;
541 processed = in;
542 }
543 outlen = (out - outstart) * 2;
544 inlen = processed - instart;
545 return (outlen);
518546 }
519547
520548 /**
534562 * The value of *inlen after return is the number of octets consumed
535563 * if the return value is positive, else unpredictable.
536564 */
537 int UTF16LEToUTF8(unsigned char* out, int outlen, const unsigned char* inb, int inlenb)
538 {
539 unsigned char* outstart = out;
540 const unsigned char* processed = inb;
541 unsigned char* outend = out + outlen;
542 unsigned short* in = (unsigned short*) inb;
543 unsigned short* inend;
544 unsigned int c, d, inlen;
545 unsigned char *tmp;
546 int bits;
547
548 if ((inlenb % 2) == 1)
549 (inlenb)--;
550 inlen = inlenb / 2;
551 inend = in + inlen;
552 while ((in < inend) && (out - outstart + 5 < outlen)) {
553 if (xmlLittleEndian) {
554 c= *in++;
555 } else {
556 tmp = (unsigned char *) in;
557 c = *tmp++;
558 c = c | (((unsigned int)*tmp) << 8);
559 in++;
560 }
561 if ((c & 0xFC00) == 0xD800) { /* surrogates */
562 if (in >= inend) { /* (in > inend) shouldn't happens */
563 break;
564 }
565 if (xmlLittleEndian) {
566 d = *in++;
567 } else {
568 tmp = (unsigned char *) in;
569 d = *tmp++;
570 d = d | (((unsigned int)*tmp) << 8);
571 in++;
572 }
573 if ((d & 0xFC00) == 0xDC00) {
574 c &= 0x03FF;
575 c <<= 10;
576 c |= d & 0x03FF;
577 c += 0x10000;
578 }
579 else {
580 outlen = out - outstart;
581 inlenb = processed - inb;
582 return(-2);
583 }
584 }
585
586 /* assertion: c is a single UTF-4 value */
587 if (out >= outend)
588 break;
589 if (c < 0x80) { *out++= c; bits= -6; }
590 else if (c < 0x800) { *out++= ((c >> 6) & 0x1F) | 0xC0; bits= 0; }
591 else if (c < 0x10000) { *out++= ((c >> 12) & 0x0F) | 0xE0; bits= 6; }
592 else { *out++= ((c >> 18) & 0x07) | 0xF0; bits= 12; }
593
594 for ( ; bits >= 0; bits-= 6) {
595 if (out >= outend)
596 break;
597 *out++= ((c >> bits) & 0x3F) | 0x80;
598 }
599 processed = (const unsigned char*) in;
600 }
601 outlen = out - outstart;
602 inlenb = processed - inb;
603 return(outlen);
565 int UTF16LEToUTF8(unsigned char *out,
566 int outlen,
567 const unsigned char *inb,
568 int inlenb) {
569 unsigned char *outstart = out;
570 const unsigned char *processed = inb;
571 unsigned char *outend = out + outlen;
572 unsigned short *in = (unsigned short *)inb;
573 unsigned short *inend;
574 unsigned int c, d, inlen;
575 unsigned char *tmp;
576 int bits;
577
578 if ((inlenb % 2) == 1)
579 (inlenb)--;
580 inlen = inlenb / 2;
581 inend = in + inlen;
582 while ((in < inend) && (out - outstart + 5 < outlen)) {
583 if (xmlLittleEndian) {
584 c = *in++;
585 } else {
586 tmp = (unsigned char *)in;
587 c = *tmp++;
588 c = c | (((unsigned int)*tmp) << 8);
589 in++;
590 }
591 if ((c & 0xFC00) == 0xD800) { /* surrogates */
592 if (in >= inend) { /* (in > inend) shouldn't happens */
593 break;
594 }
595 if (xmlLittleEndian) {
596 d = *in++;
597 } else {
598 tmp = (unsigned char *)in;
599 d = *tmp++;
600 d = d | (((unsigned int)*tmp) << 8);
601 in++;
602 }
603 if ((d & 0xFC00) == 0xDC00) {
604 c &= 0x03FF;
605 c <<= 10;
606 c |= d & 0x03FF;
607 c += 0x10000;
608 } else {
609 outlen = out - outstart;
610 inlenb = processed - inb;
611 return (-2);
612 }
613 }
614
615 /* assertion: c is a single UTF-4 value */
616 if (out >= outend)
617 break;
618 if (c < 0x80) {
619 *out++ = c;
620 bits = -6;
621 } else if (c < 0x800) {
622 *out++ = ((c >> 6) & 0x1F) | 0xC0;
623 bits = 0;
624 } else if (c < 0x10000) {
625 *out++ = ((c >> 12) & 0x0F) | 0xE0;
626 bits = 6;
627 } else {
628 *out++ = ((c >> 18) & 0x07) | 0xF0;
629 bits = 12;
630 }
631
632 for (; bits >= 0; bits -= 6) {
633 if (out >= outend)
634 break;
635 *out++ = ((c >> bits) & 0x3F) | 0x80;
636 }
637 processed = (const unsigned char *)in;
638 }
639 outlen = out - outstart;
640 inlenb = processed - inb;
641 return (outlen);
604642 }
605643
606644 /**
614652 * block of chars out.
615653 *
616654 * Returns the number of bytes written, or -1 if lack of space, or -2
617 * if the transcoding failed.
655 * if the transcoding failed.
618656 */
619 int UTF8ToUTF16LE(unsigned char* outb, int outlen, const unsigned char* in, int inlen)
620 {
621 unsigned short* out = (unsigned short*) outb;
622 const unsigned char* processed = in;
623 const unsigned char *const instart = in;
624 unsigned short* outstart= out;
625 unsigned short* outend;
626 const unsigned char* inend= in+inlen;
627 unsigned int c, d;
628 int trailing;
629 unsigned char *tmp;
630 unsigned short tmp1, tmp2;
631
632 /* UTF16LE encoding has no BOM */
633 if ((out == NULL) || (outlen == 0) || (inlen == 0)) return(-1);
634 if (in == NULL) {
635 outlen = 0;
636 inlen = 0;
637 return(0);
638 }
639 outend = out + (outlen / 2);
640 while (in < inend) {
641 d= *in++;
642 if (d < 0x80) { c= d; trailing= 0; }
643 else if (d < 0xC0) {
644 /* trailing byte in leading position */
645 outlen = (out - outstart) * 2;
646 inlen = processed - instart;
647 return(-2);
648 } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
649 else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
650 else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
651 else {
652 /* no chance for this in UTF-16 */
653 outlen = (out - outstart) * 2;
654 inlen = processed - instart;
655 return(-2);
656 }
657
658 if (inend - in < trailing) {
659 break;
660 }
661
662 for ( ; trailing; trailing--) {
663 if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80))
664 break;
665 c <<= 6;
666 c |= d & 0x3F;
667 }
668
669 /* assertion: c is a single UTF-4 value */
670 if (c < 0x10000) {
671 if (out >= outend)
672 break;
673 if (xmlLittleEndian) {
674 *out++ = c;
675 } else {
676 tmp = (unsigned char *) out;
677 *tmp = c ;
678 *(tmp + 1) = c >> 8 ;
679 out++;
680 }
681 }
682 else if (c < 0x110000) {
683 if (out+1 >= outend)
684 break;
685 c -= 0x10000;
686 if (xmlLittleEndian) {
687 *out++ = 0xD800 | (c >> 10);
688 *out++ = 0xDC00 | (c & 0x03FF);
689 } else {
690 tmp1 = 0xD800 | (c >> 10);
691 tmp = (unsigned char *) out;
692 *tmp = (unsigned char) tmp1;
693 *(tmp + 1) = tmp1 >> 8;
694 out++;
695
696 tmp2 = 0xDC00 | (c & 0x03FF);
697 tmp = (unsigned char *) out;
698 *tmp = (unsigned char) tmp2;
699 *(tmp + 1) = tmp2 >> 8;
700 out++;
701 }
702 }
703 else
704 break;
705 processed = in;
706 }
707 outlen = (out - outstart) * 2;
708 inlen = processed - instart;
709 return(outlen);
710 }
711
712 int isUTF8(const char* in_string) {
713 //fprintf(stdout, "utf8 test-> %s\n", in_string);
714 int str_bytes = 0;
715 if (in_string != NULL ) {
716 str_bytes = strlen(in_string);
717 } else {
718 return -1;
719 }
720
721 bool is_validUTF8 = true;
722 bool is_high_ascii= false;
723
724 int index = 0;
725 while (index < str_bytes && is_validUTF8) {
726 char achar = in_string[index];
727 int supplemental_bytes = 0;
728
729 if ( (unsigned char)achar > 0x80) {
730 is_high_ascii = true;
731 }
732
733 if ((achar & 0x80) == 0) { // 0xxxxxxx
734 ++index;
735
736 } else if ((achar & 0xF8) == 0xF0) { // 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
737 ++index;
738 supplemental_bytes = 3;
739 is_high_ascii = true;
740
741 } else if ((achar & 0xE0) == 0xE0) { // 1110zzzz 10yyyyyy 10xxxxxx
742 ++index;
743 supplemental_bytes = 2;
744 is_high_ascii = true;
745
746 } else if ((achar & 0xE0) == 0xC0) { // 110yyyyy 10xxxxxx
747 ++index;
748 supplemental_bytes = 1;
749 is_high_ascii = true;
750
751 } else {
752 is_validUTF8 = false;
753 }
754
755 while (is_validUTF8 && supplemental_bytes--) {
756 if (index >= str_bytes) {
757 is_validUTF8 = false;
758 } else if ((in_string[index++] & 0xC0) != 0x80) { // 10uuzzzz
759 is_validUTF8 = false;
760 }
761 }
762 }
763
764 if (is_high_ascii) {
765 return 8;
766 } else if (is_validUTF8) {
767 return 1;
768 } else {
769 return 0;
770 }
657 int UTF8ToUTF16LE(unsigned char *outb,
658 int outlen,
659 const unsigned char *in,
660 int inlen) {
661 unsigned short *out = (unsigned short *)outb;
662 const unsigned char *processed = in;
663 const unsigned char *const instart = in;
664 unsigned short *outstart = out;
665 unsigned short *outend;
666 const unsigned char *inend = in + inlen;
667 unsigned int c, d;
668 int trailing;
669 unsigned char *tmp;
670 unsigned short tmp1, tmp2;
671
672 /* UTF16LE encoding has no BOM */
673 if ((out == NULL) || (outlen == 0) || (inlen == 0))
674 return (-1);
675 if (in == NULL) {
676 outlen = 0;
677 inlen = 0;
678 return (0);
679 }
680 outend = out + (outlen / 2);
681 while (in < inend) {
682 d = *in++;
683 if (d < 0x80) {
684 c = d;
685 trailing = 0;
686 } else if (d < 0xC0) {
687 /* trailing byte in leading position */
688 outlen = (out - outstart) * 2;
689 inlen = processed - instart;
690 return (-2);
691 } else if (d < 0xE0) {
692 c = d & 0x1F;
693 trailing = 1;
694 } else if (d < 0xF0) {
695 c = d & 0x0F;
696 trailing = 2;
697 } else if (d < 0xF8) {
698 c = d & 0x07;
699 trailing = 3;
700 } else {
701 /* no chance for this in UTF-16 */
702 outlen = (out - outstart) * 2;
703 inlen = processed - instart;
704 return (-2);
705 }
706
707 if (inend - in < trailing) {
708 break;
709 }
710
711 for (; trailing; trailing--) {
712 if ((in >= inend) || (((d = *in++) & 0xC0) != 0x80))
713 break;
714 c <<= 6;
715 c |= d & 0x3F;
716 }
717
718 /* assertion: c is a single UTF-4 value */
719 if (c < 0x10000) {
720 if (out >= outend)
721 break;
722 if (xmlLittleEndian) {
723 *out++ = c;
724 } else {
725 tmp = (unsigned char *)out;
726 *tmp = c;
727 *(tmp + 1) = c >> 8;
728 out++;
729 }
730 } else if (c < 0x110000) {
731 if (out + 1 >= outend)
732 break;
733 c -= 0x10000;
734 if (xmlLittleEndian) {
735 *out++ = 0xD800 | (c >> 10);
736 *out++ = 0xDC00 | (c & 0x03FF);
737 } else {
738 tmp1 = 0xD800 | (c >> 10);
739 tmp = (unsigned char *)out;
740 *tmp = (unsigned char)tmp1;
741 *(tmp + 1) = tmp1 >> 8;
742 out++;
743
744 tmp2 = 0xDC00 | (c & 0x03FF);
745 tmp = (unsigned char *)out;
746 *tmp = (unsigned char)tmp2;
747 *(tmp + 1) = tmp2 >> 8;
748 out++;
749 }
750 } else
751 break;
752 processed = in;
753 }
754 outlen = (out - outstart) * 2;
755 inlen = processed - instart;
756 return (outlen);
757 }
758
759 int isUTF8(const char *in_string) {
760 // fprintf(stdout, "utf8 test-> %s\n", in_string);
761 int str_bytes = 0;
762 if (in_string != NULL) {
763 str_bytes = strlen(in_string);
764 } else {
765 return -1;
766 }
767
768 bool is_validUTF8 = true;
769 bool is_high_ascii = false;
770
771 int index = 0;
772 while (index < str_bytes && is_validUTF8) {
773 char achar = in_string[index];
774 int supplemental_bytes = 0;
775
776 if ((unsigned char)achar > 0x80) {
777 is_high_ascii = true;
778 }
779
780 if ((achar & 0x80) == 0) { // 0xxxxxxx
781 ++index;
782
783 } else if ((achar & 0xF8) == 0xF0) { // 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
784 ++index;
785 supplemental_bytes = 3;
786 is_high_ascii = true;
787
788 } else if ((achar & 0xE0) == 0xE0) { // 1110zzzz 10yyyyyy 10xxxxxx
789 ++index;
790 supplemental_bytes = 2;
791 is_high_ascii = true;
792
793 } else if ((achar & 0xE0) == 0xC0) { // 110yyyyy 10xxxxxx
794 ++index;
795 supplemental_bytes = 1;
796 is_high_ascii = true;
797
798 } else {
799 is_validUTF8 = false;
800 }
801
802 while (is_validUTF8 && supplemental_bytes--) {
803 if (index >= str_bytes) {
804 is_validUTF8 = false;
805 } else if ((in_string[index++] & 0xC0) != 0x80) { // 10uuzzzz
806 is_validUTF8 = false;
807 }
808 }
809 }
810
811 if (is_high_ascii) {
812 return 8;
813 } else if (is_validUTF8) {
814 return 1;
815 } else {
816 return 0;
817 }
771818 }
772819
773820 /*----------------------
774821 utf8_length
775822 in_string - pointer to location of a utf8 string
776 char_limit - either 0 (count all characters) or non-zero (limit utf8 to that character count)
777
778 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
779 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.
823 char_limit - either 0 (count all characters) or non-zero (limit utf8 to
824 that character count)
825
826 Because of the lovely way utf8 is aligned, test only the first byte in each.
827 If char_limit is 0, return the number of CHARACTERS in the string, if the
828 char_limit is not zero (the char_limit will equal
829 utf_string_leghth because of the break), so change gears, save space and just
830 return the byte_count.
780831 ----------------------*/
781832 #include <stdio.h>
782833 unsigned int utf8_length(const char *in_string, unsigned int char_limit) {
783 const char *utf8_str = in_string;
784 unsigned int utf8_string_length = 0;
785 unsigned int in_str_len = strlen(in_string);
786 unsigned int byte_count = 0;
787 unsigned int bytes_in_char = 0;
788
789 if (in_string == NULL) return 0;
790
791 while ( byte_count < in_str_len) {
792 bytes_in_char = 0;
793 if ((*utf8_str & 0x80) == 0x00)
794 bytes_in_char = 1;
795 else if ((*utf8_str & 0xE0) == 0xC0)
796 bytes_in_char = 2;
797 else if ((*utf8_str & 0xF0) == 0xE0)
798 bytes_in_char = 3;
799 else if ((*utf8_str & 0xF8) == 0xF0)
800 bytes_in_char = 4;
801
802 if (bytes_in_char > 0) {
803 utf8_string_length++;
804 utf8_str += bytes_in_char;
805 byte_count += bytes_in_char;
806 } else {
807 break;
808 }
809
810 if (char_limit != 0 && char_limit == utf8_string_length) {
811 utf8_string_length = byte_count;
812 break;
813 }
814 }
815 return utf8_string_length;
816 }
817
818 #if defined (_WIN32) && !defined (__CYGWIN__)
834 const char *utf8_str = in_string;
835 unsigned int utf8_string_length = 0;
836 unsigned int in_str_len = strlen(in_string);
837 unsigned int byte_count = 0;
838 unsigned int bytes_in_char = 0;
839
840 if (in_string == NULL)
841 return 0;
842
843 while (byte_count < in_str_len) {
844 bytes_in_char = 0;
845 if ((*utf8_str & 0x80) == 0x00)
846 bytes_in_char = 1;
847 else if ((*utf8_str & 0xE0) == 0xC0)
848 bytes_in_char = 2;
849 else if ((*utf8_str & 0xF0) == 0xE0)
850 bytes_in_char = 3;
851 else if ((*utf8_str & 0xF8) == 0xF0)
852 bytes_in_char = 4;
853
854 if (bytes_in_char > 0) {
855 utf8_string_length++;
856 utf8_str += bytes_in_char;
857 byte_count += bytes_in_char;
858 } else {
859 break;
860 }
861
862 if (char_limit != 0 && char_limit == utf8_string_length) {
863 utf8_string_length = byte_count;
864 break;
865 }
866 }
867 return utf8_string_length;
868 }
869
870 #if defined(_WIN32) && !defined(__CYGWIN__)
819871 unsigned char APar_Return_rawutf8_CP(unsigned short cp_bound_glyph) {
820 unsigned short total_known_points = 0;
821 unsigned int win32cp = GetConsoleCP();
822
823 if (win32cp == 437 || win32cp == 850 || win32cp == 852 || win32cp == 855 || win32cp == 858) {
824 total_known_points = 128;
825 } else {
826 if (cp_bound_glyph >= 0x0080) {
827 exit(win32cp);
828 }
829 }
830 if (cp_bound_glyph < 0x0080) {
831 return cp_bound_glyph << 0;
832 } else if (total_known_points) {
833 if (win32cp == 437) {
834 for (uint16_t i = 0; i < total_known_points; i++) {
835 if (cp_bound_glyph == cp437upperbytes[i]) {
836 return i+128;
837 }
838 }
839 } else if (win32cp == 850) {
840 for (uint16_t i = 0; i < total_known_points; i++) {
841 if (cp_bound_glyph == cp850upperbytes[i]) {
842 return i+128;
843 }
844 }
845 } else if (win32cp == 852) {
846 for (uint16_t i = 0; i < total_known_points; i++) {
847 if (cp_bound_glyph == cp852upperbytes[i]) {
848 return i+128;
849 }
850 }
851 } else if (win32cp == 855) {
852 for (uint16_t i = 0; i < total_known_points; i++) {
853 if (cp_bound_glyph == cp855upperbytes[i]) {
854 return i+128;
855 }
856 }
857 } else if (win32cp == 858) {
858 for (uint16_t i = 0; i < total_known_points; i++) {
859 if (cp_bound_glyph == cp858upperbytes[i]) {
860 return i+128;
861 }
862 }
863 } else {
864 fprintf(stderr, "AtomicParsley error: this windows codepage(%u) is unsupported.\nProvide the output of the 'CPTester' utility run from the bat script\n", win32cp);
865 exit(win32cp);
866 }
867 }
868 return 0;
869 }
870
871 int strip_bogusUTF16toRawUTF8 (unsigned char* out, int inlen, wchar_t* in, int outlen) {
872
873 unsigned char* outstart = out;
874 unsigned char* outend;
875 const wchar_t* inend;
876 const wchar_t* instop;
877
878 if ((out == NULL) || (in == NULL) || (outlen == 0) || (inlen == 0))
879 return(-1);
880
881 outend = out + outlen;
882 inend = in + (inlen);
883 instop = inend;
884
885 while (in < inend && out < outend - 1) {
886 *out++ = APar_Return_rawutf8_CP(*in); //*in << 0;
887 ++in;
888 }
889 outlen = out - outstart;
890 return(outlen);
872 unsigned short total_known_points = 0;
873 unsigned int win32cp = GetConsoleCP();
874
875 if (win32cp == 437 || win32cp == 850 || win32cp == 852 || win32cp == 855 ||
876 win32cp == 858) {
877 total_known_points = 128;
878 } else {
879 if (cp_bound_glyph >= 0x0080) {
880 exit(win32cp);
881 }
882 }
883 if (cp_bound_glyph < 0x0080) {
884 return cp_bound_glyph << 0;
885 } else if (total_known_points) {
886 if (win32cp == 437) {
887 for (uint16_t i = 0; i < total_known_points; i++) {
888 if (cp_bound_glyph == cp437upperbytes[i]) {
889 return i + 128;
890 }
891 }
892 } else if (win32cp == 850) {
893 for (uint16_t i = 0; i < total_known_points; i++) {
894 if (cp_bound_glyph == cp850upperbytes[i]) {
895 return i + 128;
896 }
897 }
898 } else if (win32cp == 852) {
899 for (uint16_t i = 0; i < total_known_points; i++) {
900 if (cp_bound_glyph == cp852upperbytes[i]) {
901 return i + 128;
902 }
903 }
904 } else if (win32cp == 855) {
905 for (uint16_t i = 0; i < total_known_points; i++) {
906 if (cp_bound_glyph == cp855upperbytes[i]) {
907 return i + 128;
908 }
909 }
910 } else if (win32cp == 858) {
911 for (uint16_t i = 0; i < total_known_points; i++) {
912 if (cp_bound_glyph == cp858upperbytes[i]) {
913 return i + 128;
914 }
915 }
916 } else {
917 fprintf(stderr,
918 "AtomicParsley error: this windows codepage(%u) is "
919 "unsupported.\nProvide the output of the 'CPTester' utility run "
920 "from the bat script\n",
921 win32cp);
922 exit(win32cp);
923 }
924 }
925 return 0;
926 }
927
928 int strip_bogusUTF16toRawUTF8(unsigned char *out,
929 int inlen,
930 wchar_t *in,
931 int outlen) {
932
933 unsigned char *outstart = out;
934 unsigned char *outend;
935 const wchar_t *inend;
936 const wchar_t *instop;
937
938 if ((out == NULL) || (in == NULL) || (outlen == 0) || (inlen == 0))
939 return (-1);
940
941 outend = out + outlen;
942 inend = in + (inlen);
943 instop = inend;
944
945 while (in < inend && out < outend - 1) {
946 *out++ = APar_Return_rawutf8_CP(*in); //*in << 0;
947 ++in;
948 }
949 outlen = out - outstart;
950 return (outlen);
891951 }
892952 #endif
893953
897957
898958 limit string to A-Z or a-z
899959 ----------------------*/
900 int test_conforming_alpha_string(char* in_string) {
901 int valid_bytes = 0;
902 int string_len = 0;
903 char* test_str = in_string;
904 if (in_string != NULL ) {
905 string_len = strlen(in_string);
906 } else {
907 return -1;
908 }
909
910 while (valid_bytes < string_len) {
911 if ( (*test_str >= 65 && *test_str <= 90) || (*test_str >= 97 && *test_str <= 122) || *test_str == 95 || (*test_str >= 48 && *test_str <= 57) ) {
912 valid_bytes++;
913 } else {
914 break;
915 }
916 test_str++;
917 }
918 return valid_bytes;
919 }
920
921 bool test_limited_ascii(char* in_string, unsigned int str_len) {
922 char* test_str = in_string;
923 while (test_str < in_string+str_len) {
924 if ( *test_str < 32 || *test_str > 126) {
925 return false;
926 }
927 test_str++;
928 }
929 return true;
930 }
960 int test_conforming_alpha_string(char *in_string) {
961 int valid_bytes = 0;
962 int string_len = 0;
963 char *test_str = in_string;
964 if (in_string != NULL) {
965 string_len = strlen(in_string);
966 } else {
967 return -1;
968 }
969
970 while (valid_bytes < string_len) {
971 if ((*test_str >= 65 && *test_str <= 90) ||
972 (*test_str >= 97 && *test_str <= 122) || *test_str == 95 ||
973 (*test_str >= 48 && *test_str <= 57)) {
974 valid_bytes++;
975 } else {
976 break;
977 }
978 test_str++;
979 }
980 return valid_bytes;
981 }
982
983 bool test_limited_ascii(char *in_string, unsigned int str_len) {
984 char *test_str = in_string;
985 while (test_str < in_string + str_len) {
986 if (*test_str < 32 || *test_str > 126) {
987 return false;
988 }
989 test_str++;
990 }
991 return true;
992 }
11 /*
22 AtomicParsley - id3v2.cpp
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2006-2007 puck_lock
17 Copyright (C)2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
1919 */
2020 //==================================================================//
2323 #include "CDtoc.h"
2424 #include "id3v2defs.h"
2525
26 ID3v2Tag* GlobalID3Tag = NULL;
27
28 //prefs
26 ID3v2Tag *GlobalID3Tag = NULL;
27
28 // prefs
2929 uint8_t AtomicParsley_ID3v2Tag_MajorVersion = 4;
3030 uint8_t AtomicParsley_ID3v2Tag_RevisionVersion = 0;
3131 uint8_t AtomicParsley_ID3v2Tag_Flags = 0;
3232
33 bool ID3v2Tag_Flag_Footer = false; //bit4; MPEG-4 'ID32' requires this to be false
34 bool ID3v2Tag_Flag_Experimental = true; //bit5
35 bool ID3v2Tag_Flag_ExtendedHeader = true; //bit6
36 bool ID3v2Tag_Flag_Unsyncronization = false; //bit7
33 bool ID3v2Tag_Flag_Footer =
34 false; // bit4; MPEG-4 'ID32' requires this to be false
35 bool ID3v2Tag_Flag_Experimental = true; // bit5
36 bool ID3v2Tag_Flag_ExtendedHeader = true; // bit6
37 bool ID3v2Tag_Flag_Unsyncronization = false; // bit7
3738
3839 ///////////////////////////////////////////////////////////////////////////////////////
39 // id3 number conversion functions //
40 // id3 number conversion functions //
4041 ///////////////////////////////////////////////////////////////////////////////////////
4142
42 uint64_t syncsafeXX_to_UInt64(char* syncsafe_int, uint8_t syncsafe_len) {
43 if (syncsafe_len == 5) {
44 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || syncsafe_int[4] & 0x80) return 0;
45 return ((uint64_t)syncsafe_int[0] << 28) | (syncsafe_int[1] << 21) | (syncsafe_int[2] << 14) | (syncsafe_int[3] << 7) | syncsafe_int[4];
46 } else if (syncsafe_len == 6) {
47 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;
48 return ((uint64_t)syncsafe_int[0] << 35) | ((uint64_t)syncsafe_int[1] << 28) | (syncsafe_int[2] << 21) | (syncsafe_int[3] << 14) |
49 (syncsafe_int[4] << 7) | syncsafe_int[5];
50 } else if (syncsafe_len == 7) {
51 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
52 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 || syncsafe_int[6] & 0x80) return 0;
53 return ((uint64_t)syncsafe_int[0] << 42) | ((uint64_t)syncsafe_int[1] << 35) | ((uint64_t)syncsafe_int[2] << 28) | (syncsafe_int[3] << 21) |
54 (syncsafe_int[3] << 14) | (syncsafe_int[5] << 7) | syncsafe_int[6];
55 } else if (syncsafe_len == 8) {
56 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
57 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 || syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80) return 0;
58 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) |
59 (syncsafe_int[4] << 21) | (syncsafe_int[5] << 14) | (syncsafe_int[6] << 7) | syncsafe_int[7];
60 } else if (syncsafe_len == 9) {
61 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 || syncsafe_int[4] & 0x80 ||
62 syncsafe_int[5] & 0x80 || syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80 || syncsafe_int[8] & 0x80) return 0;
63 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) |
64 ((uint64_t)syncsafe_int[4] << 28) | (syncsafe_int[5] << 21) | (syncsafe_int[6] << 14) | (syncsafe_int[7] << 7) | syncsafe_int[8];
65 }
66 return 0;
67 }
68
69 uint32_t syncsafe32_to_UInt32(char* syncsafe_int) {
70 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 || syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80) return 0;
71 return (syncsafe_int[0] << 21) | (syncsafe_int[1] << 14) | (syncsafe_int[2] << 7) | syncsafe_int[3];
72 }
73
74 uint16_t syncsafe16_to_UInt16(char* syncsafe_int) {
75 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80) return 0;
76 return (syncsafe_int[0] << 7) | syncsafe_int[1];
77 }
78
79 void convert_to_syncsafe32(uint32_t in_uint, char* buffer) {
80 buffer[0] = (in_uint >> 21) & 0x7F;
81 buffer[1] = (in_uint >> 14) & 0x7F;
82 buffer[2] = (in_uint >> 7) & 0x7F;
83 buffer[3] = (in_uint >> 0) & 0x7F;
84 return;
85 }
86
87 uint8_t convert_to_syncsafeXX(uint64_t in_uint, char* buffer) {
88 if
89 #if defined (_MSC_VER)
90 (in_uint <= (uint64_t)34359738367)
43 uint64_t syncsafeXX_to_UInt64(char *syncsafe_int, uint8_t syncsafe_len) {
44 if (syncsafe_len == 5) {
45 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
46 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
47 syncsafe_int[4] & 0x80)
48 return 0;
49 return ((uint64_t)syncsafe_int[0] << 28) | (syncsafe_int[1] << 21) |
50 (syncsafe_int[2] << 14) | (syncsafe_int[3] << 7) | syncsafe_int[4];
51 } else if (syncsafe_len == 6) {
52 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
53 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
54 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80)
55 return 0;
56 return ((uint64_t)syncsafe_int[0] << 35) |
57 ((uint64_t)syncsafe_int[1] << 28) | (syncsafe_int[2] << 21) |
58 (syncsafe_int[3] << 14) | (syncsafe_int[4] << 7) | syncsafe_int[5];
59 } else if (syncsafe_len == 7) {
60 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
61 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
62 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 ||
63 syncsafe_int[6] & 0x80)
64 return 0;
65 return ((uint64_t)syncsafe_int[0] << 42) |
66 ((uint64_t)syncsafe_int[1] << 35) |
67 ((uint64_t)syncsafe_int[2] << 28) | (syncsafe_int[3] << 21) |
68 (syncsafe_int[3] << 14) | (syncsafe_int[5] << 7) | syncsafe_int[6];
69 } else if (syncsafe_len == 8) {
70 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
71 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
72 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 ||
73 syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80)
74 return 0;
75 return ((uint64_t)syncsafe_int[0] << 49) |
76 ((uint64_t)syncsafe_int[1] << 42) |
77 ((uint64_t)syncsafe_int[2] << 35) |
78 ((uint64_t)syncsafe_int[3] << 28) | (syncsafe_int[4] << 21) |
79 (syncsafe_int[5] << 14) | (syncsafe_int[6] << 7) | syncsafe_int[7];
80 } else if (syncsafe_len == 9) {
81 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
82 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80 ||
83 syncsafe_int[4] & 0x80 || syncsafe_int[5] & 0x80 ||
84 syncsafe_int[6] & 0x80 || syncsafe_int[7] & 0x80 ||
85 syncsafe_int[8] & 0x80)
86 return 0;
87 return ((uint64_t)syncsafe_int[0] << 56) |
88 ((uint64_t)syncsafe_int[1] << 49) |
89 ((uint64_t)syncsafe_int[2] << 42) |
90 ((uint64_t)syncsafe_int[3] << 35) |
91 ((uint64_t)syncsafe_int[4] << 28) | (syncsafe_int[5] << 21) |
92 (syncsafe_int[6] << 14) | (syncsafe_int[7] << 7) | syncsafe_int[8];
93 }
94 return 0;
95 }
96
97 uint32_t syncsafe32_to_UInt32(char *syncsafe_int) {
98 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80 ||
99 syncsafe_int[2] & 0x80 || syncsafe_int[3] & 0x80)
100 return 0;
101 return (syncsafe_int[0] << 21) | (syncsafe_int[1] << 14) |
102 (syncsafe_int[2] << 7) | syncsafe_int[3];
103 }
104
105 uint16_t syncsafe16_to_UInt16(char *syncsafe_int) {
106 if (syncsafe_int[0] & 0x80 || syncsafe_int[1] & 0x80)
107 return 0;
108 return (syncsafe_int[0] << 7) | syncsafe_int[1];
109 }
110
111 void convert_to_syncsafe32(uint32_t in_uint, char *buffer) {
112 buffer[0] = (in_uint >> 21) & 0x7F;
113 buffer[1] = (in_uint >> 14) & 0x7F;
114 buffer[2] = (in_uint >> 7) & 0x7F;
115 buffer[3] = (in_uint >> 0) & 0x7F;
116 return;
117 }
118
119 uint8_t convert_to_syncsafeXX(uint64_t in_uint, char *buffer) {
120 if
121 #if defined(_MSC_VER)
122 (in_uint <= (uint64_t)34359738367)
91123 #else
92 (in_uint <= 34359738367ULL)
124 (in_uint <= 34359738367ULL)
93125 #endif
94 {
95 buffer[0] = (in_uint >> 28) & 0x7F;
96 buffer[1] = (in_uint >> 21) & 0x7F;
97 buffer[2] = (in_uint >> 14) & 0x7F;
98 buffer[3] = (in_uint >> 7) & 0x7F;
99 buffer[4] = (in_uint >> 0) & 0x7F;
100 return 5;
101 #if defined (_MSC_VER)
102 } else if (in_uint <= (uint64_t)4398046511103) {
126 {
127 buffer[0] = (in_uint >> 28) & 0x7F;
128 buffer[1] = (in_uint >> 21) & 0x7F;
129 buffer[2] = (in_uint >> 14) & 0x7F;
130 buffer[3] = (in_uint >> 7) & 0x7F;
131 buffer[4] = (in_uint >> 0) & 0x7F;
132 return 5;
133 #if defined(_MSC_VER)
134 } else if (in_uint <= (uint64_t)4398046511103) {
103135 #else
104 } else if (in_uint <= 4398046511103ULL) {
136 } else if (in_uint <= 4398046511103ULL) {
105137 #endif
106 buffer[0] = (in_uint >> 35) & 0x7F;
107 buffer[1] = (in_uint >> 28) & 0x7F;
108 buffer[2] = (in_uint >> 21) & 0x7F;
109 buffer[3] = (in_uint >> 14) & 0x7F;
110 buffer[4] = (in_uint >> 7) & 0x7F;
111 buffer[5] = (in_uint >> 0) & 0x7F;
112 return 6;
113 #if defined (_MSC_VER)
114 } else if (in_uint <= (uint64_t)562949953421311) {
138 buffer[0] = (in_uint >> 35) & 0x7F;
139 buffer[1] = (in_uint >> 28) & 0x7F;
140 buffer[2] = (in_uint >> 21) & 0x7F;
141 buffer[3] = (in_uint >> 14) & 0x7F;
142 buffer[4] = (in_uint >> 7) & 0x7F;
143 buffer[5] = (in_uint >> 0) & 0x7F;
144 return 6;
145 #if defined(_MSC_VER)
146 } else if (in_uint <= (uint64_t)562949953421311) {
115147 #else
116 } else if (in_uint <= 562949953421311ULL) {
148 } else if (in_uint <= 562949953421311ULL) {
117149 #endif
118 buffer[0] = (in_uint >> 42) & 0x7F;
119 buffer[1] = (in_uint >> 35) & 0x7F;
120 buffer[2] = (in_uint >> 28) & 0x7F;
121 buffer[3] = (in_uint >> 21) & 0x7F;
122 buffer[4] = (in_uint >> 14) & 0x7F;
123 buffer[5] = (in_uint >> 7) & 0x7F;
124 buffer[6] = (in_uint >> 0) & 0x7F;
125 return 7;
126 #if defined (_MSC_VER)
127 } else if (in_uint <= (uint64_t)72057594037927935) {
150 buffer[0] = (in_uint >> 42) & 0x7F;
151 buffer[1] = (in_uint >> 35) & 0x7F;
152 buffer[2] = (in_uint >> 28) & 0x7F;
153 buffer[3] = (in_uint >> 21) & 0x7F;
154 buffer[4] = (in_uint >> 14) & 0x7F;
155 buffer[5] = (in_uint >> 7) & 0x7F;
156 buffer[6] = (in_uint >> 0) & 0x7F;
157 return 7;
158 #if defined(_MSC_VER)
159 } else if (in_uint <= (uint64_t)72057594037927935) {
128160 #else
129 } else if (in_uint <= 72057594037927935ULL) {
161 } else if (in_uint <= 72057594037927935ULL) {
130162 #endif
131 buffer[0] = (in_uint >> 49) & 0x7F;
132 buffer[1] = (in_uint >> 42) & 0x7F;
133 buffer[2] = (in_uint >> 35) & 0x7F;
134 buffer[3] = (in_uint >> 28) & 0x7F;
135 buffer[4] = (in_uint >> 21) & 0x7F;
136 buffer[5] = (in_uint >> 14) & 0x7F;
137 buffer[6] = (in_uint >> 7) & 0x7F;
138 buffer[7] = (in_uint >> 0) & 0x7F;
139 return 8;
140 #if defined (_MSC_VER)
141 } else if (in_uint <= (uint64_t)9223372036854775807) {
163 buffer[0] = (in_uint >> 49) & 0x7F;
164 buffer[1] = (in_uint >> 42) & 0x7F;
165 buffer[2] = (in_uint >> 35) & 0x7F;
166 buffer[3] = (in_uint >> 28) & 0x7F;
167 buffer[4] = (in_uint >> 21) & 0x7F;
168 buffer[5] = (in_uint >> 14) & 0x7F;
169 buffer[6] = (in_uint >> 7) & 0x7F;
170 buffer[7] = (in_uint >> 0) & 0x7F;
171 return 8;
172 #if defined(_MSC_VER)
173 } else if (in_uint <= (uint64_t)9223372036854775807) {
142174 #else
143 } else if (in_uint <= 9223372036854775807ULL) { //that is some hardcore lovin'
175 } else if (in_uint <= 9223372036854775807ULL) { // that is some hardcore
176 // lovin'
144177 #endif
145 buffer[0] = (in_uint >> 56) & 0x7F;
146 buffer[1] = (in_uint >> 49) & 0x7F;
147 buffer[2] = (in_uint >> 42) & 0x7F;
148 buffer[3] = (in_uint >> 35) & 0x7F;
149 buffer[4] = (in_uint >> 28) & 0x7F;
150 buffer[5] = (in_uint >> 21) & 0x7F;
151 buffer[6] = (in_uint >> 14) & 0x7F;
152 buffer[7] = (in_uint >> 7) & 0x7F;
153 buffer[8] = (in_uint >> 0) & 0x7F;
154 return 9;
155 }
156 return 0;
157 }
158
159 uint32_t UInt24FromBigEndian(const char *string) { //v2.2 frame lengths
160 return ((0 << 24) | ((string[0] & 0xff) << 16) | ((string[1] & 0xff) << 8) | (string[2] & 0xff) << 0);
161 }
162
163 uint32_t ID3v2_desynchronize(char* buffer, uint32_t bufferlen) {
164 char* buf_ptr = buffer;
165 uint32_t desync_count = 0;
166
167 for (uint32_t i = 0; i < bufferlen; i++) {
168 if ((unsigned char)buffer[i] == 0xFF && (unsigned char)buffer[i+1] == 0x00) {
169 buf_ptr[desync_count] = buffer[i];
170 i++;
171 } else {
172 buf_ptr[desync_count] = buffer[i];
173 }
174 desync_count++;
175 }
176 return desync_count;
178 buffer[0] = (in_uint >> 56) & 0x7F;
179 buffer[1] = (in_uint >> 49) & 0x7F;
180 buffer[2] = (in_uint >> 42) & 0x7F;
181 buffer[3] = (in_uint >> 35) & 0x7F;
182 buffer[4] = (in_uint >> 28) & 0x7F;
183 buffer[5] = (in_uint >> 21) & 0x7F;
184 buffer[6] = (in_uint >> 14) & 0x7F;
185 buffer[7] = (in_uint >> 7) & 0x7F;
186 buffer[8] = (in_uint >> 0) & 0x7F;
187 return 9;
188 }
189 return 0;
190 }
191
192 uint32_t UInt24FromBigEndian(const char *string) { // v2.2 frame lengths
193 return ((0 << 24) | ((string[0] & 0xff) << 16) | ((string[1] & 0xff) << 8) |
194 (string[2] & 0xff) << 0);
195 }
196
197 uint32_t ID3v2_desynchronize(char *buffer, uint32_t bufferlen) {
198 char *buf_ptr = buffer;
199 uint32_t desync_count = 0;
200
201 for (uint32_t i = 0; i < bufferlen; i++) {
202 if ((unsigned char)buffer[i] == 0xFF &&
203 (unsigned char)buffer[i + 1] == 0x00) {
204 buf_ptr[desync_count] = buffer[i];
205 i++;
206 } else {
207 buf_ptr[desync_count] = buffer[i];
208 }
209 desync_count++;
210 }
211 return desync_count;
177212 }
178213
179214 ///////////////////////////////////////////////////////////////////////////////////////
180 // bit tests & generic functions //
215 // bit tests & generic functions //
181216 ///////////////////////////////////////////////////////////////////////////////////////
182217
183 bool ID3v2_PaddingTest(char* buffer) {
184 if (buffer[0] & 0x00 || buffer[1] & 0x00 || buffer[2] & 0x00 || buffer[3] & 0x00) return true;
185 return false;
186 }
187
188 bool ID3v2_TestFrameID_NonConformance(char* frameid) {
189 for (uint8_t i=0; i < 4; i++) {
190 if ( !((frameid[i] >= '0' && frameid[i] <= '9') || ( frameid[i] >= 'A' && frameid[i] <= 'Z' )) ) {
191 return true;
192 }
193 }
194 return false;
218 bool ID3v2_PaddingTest(char *buffer) {
219 if (buffer[0] & 0x00 || buffer[1] & 0x00 || buffer[2] & 0x00 ||
220 buffer[3] & 0x00)
221 return true;
222 return false;
223 }
224
225 bool ID3v2_TestFrameID_NonConformance(char *frameid) {
226 for (uint8_t i = 0; i < 4; i++) {
227 if (!((frameid[i] >= '0' && frameid[i] <= '9') ||
228 (frameid[i] >= 'A' && frameid[i] <= 'Z'))) {
229 return true;
230 }
231 }
232 return false;
195233 }
196234
197235 bool ID3v2_TestTagFlag(uint8_t TagFlag, uint8_t TagBit) {
198 if (TagFlag & TagBit) return true;
199 return false;
236 if (TagFlag & TagBit)
237 return true;
238 return false;
200239 }
201240
202241 bool ID3v2_TestFrameFlag(uint16_t FrameFlag, uint16_t FrameBit) {
203 if (FrameFlag & FrameBit) return true;
204 return false;
205 }
206
207 uint8_t TextField_TestBOM(char* astring) {
208 if (((unsigned char*)astring)[0] == 0xFE && ((unsigned char*)astring)[1] == 0xFF) return 13; //13 looks like a B for BE
209 if (((unsigned char*)astring)[0] == 0xFF && ((unsigned char*)astring)[1] == 0xFE) return 1; //1 looks like a l for LE
210 return 0;
242 if (FrameFlag & FrameBit)
243 return true;
244 return false;
245 }
246
247 uint8_t TextField_TestBOM(char *astring) {
248 if (((unsigned char *)astring)[0] == 0xFE &&
249 ((unsigned char *)astring)[1] == 0xFF)
250 return 13; // 13 looks like a B for BE
251 if (((unsigned char *)astring)[0] == 0xFF &&
252 ((unsigned char *)astring)[1] == 0xFE)
253 return 1; // 1 looks like a l for LE
254 return 0;
211255 }
212256
213257 void APar_LimitBufferRange(uint32_t max_allowed, uint32_t target_amount) {
214 if (target_amount > max_allowed) {
215 fprintf(stderr, "AtomicParsley error: insufficient memory to process ID3 tags (%" PRIu32 ">%" PRIu32 "). Exiting.\n", target_amount, max_allowed);
216 exit( target_amount - max_allowed );
217 }
218 return;
219 }
220
221 void APar_ValidateNULLTermination8bit(ID3v2Fields* this_field) {
222 if (this_field->field_string[0] == 0) {
223 this_field->field_length = 1;
224 } else if (this_field->field_string[this_field->field_length-1] != 0) {
225 this_field->field_length += 1;
226 }
227 return;
228 }
229
230 void APar_ValidateNULLTermination16bit(ID3v2Fields* this_field, uint8_t encoding) {
231 if (this_field->field_string[0] == 0 && this_field->field_string[1] == 0) {
232 this_field->field_length = 2;
233 if (encoding == TE_UTF16LE_WITH_BOM) {
234 if ( ((uint8_t)(this_field->field_string[0]) != 0xFF && (uint8_t)(this_field->field_string[1]) != 0xFE) ||
235 ((uint8_t)(this_field->field_string[0]) != 0xFE && (uint8_t)(this_field->field_string[1]) != 0xFF) ) {
236 memcpy(this_field->field_string, "\xFF\xFE", 2);
237 this_field->field_length = 4;
238 }
239 }
240 } else if (this_field->field_string[this_field->field_length-2] != 0 && this_field->field_string[this_field->field_length-1] != 0) {
241 this_field->field_length += 2;
242 }
243 return;
258 if (target_amount > max_allowed) {
259 fprintf(
260 stderr,
261 "AtomicParsley error: insufficient memory to process ID3 tags (%" PRIu32
262 ">%" PRIu32 "). Exiting.\n",
263 target_amount,
264 max_allowed);
265 exit(target_amount - max_allowed);
266 }
267 return;
268 }
269
270 void APar_ValidateNULLTermination8bit(ID3v2Fields *this_field) {
271 if (this_field->field_string[0] == 0) {
272 this_field->field_length = 1;
273 } else if (this_field->field_string[this_field->field_length - 1] != 0) {
274 this_field->field_length += 1;
275 }
276 return;
277 }
278
279 void APar_ValidateNULLTermination16bit(ID3v2Fields *this_field,
280 uint8_t encoding) {
281 if (this_field->field_string[0] == 0 && this_field->field_string[1] == 0) {
282 this_field->field_length = 2;
283 if (encoding == TE_UTF16LE_WITH_BOM) {
284 if (((uint8_t)(this_field->field_string[0]) != 0xFF &&
285 (uint8_t)(this_field->field_string[1]) != 0xFE) ||
286 ((uint8_t)(this_field->field_string[0]) != 0xFE &&
287 (uint8_t)(this_field->field_string[1]) != 0xFF)) {
288 memcpy(this_field->field_string, "\xFF\xFE", 2);
289 this_field->field_length = 4;
290 }
291 }
292 } else if (this_field->field_string[this_field->field_length - 2] != 0 &&
293 this_field->field_string[this_field->field_length - 1] != 0) {
294 this_field->field_length += 2;
295 }
296 return;
244297 }
245298
246299 bool APar_EvalFrame_for_Field(int frametype, int fieldtype) {
247 uint8_t frametype_idx = GetFrameCompositionDescription(frametype);
248
249 for (uint8_t fld_i = 0; fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount; fld_i++) {
250 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == fieldtype) {
251 return true;
252 }
253 }
254 return false;
255 }
256
257 uint8_t TestCharInRange(uint8_t testchar, uint8_t lowerlimit, uint8_t upperlimit) {
258 if (testchar >= lowerlimit && testchar <= upperlimit) {
259 return 1;
260 }
261 return 0;
300 uint8_t frametype_idx = GetFrameCompositionDescription(frametype);
301
302 for (uint8_t fld_i = 0;
303 fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount;
304 fld_i++) {
305 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
306 fieldtype) {
307 return true;
308 }
309 }
310 return false;
311 }
312
313 uint8_t
314 TestCharInRange(uint8_t testchar, uint8_t lowerlimit, uint8_t upperlimit) {
315 if (testchar >= lowerlimit && testchar <= upperlimit) {
316 return 1;
317 }
318 return 0;
262319 }
263320
264321 uint8_t ImageListMembers() {
265 return (uint8_t)(sizeof(ImageList)/sizeof(*ImageList));
322 return (uint8_t)(sizeof(ImageList) / sizeof(*ImageList));
266323 }
267324
268325 ///////////////////////////////////////////////////////////////////////////////////////
269 // test functions //
326 // test functions //
270327 ///////////////////////////////////////////////////////////////////////////////////////
271328
272 void WriteZlibData(char* buffer, uint32_t buff_len) {
273 char* indy_atom_path = (char *)malloc(sizeof(char)*MAXPATHLEN); //this malloc can escape memset because its only for in-house testing
274 strcat(indy_atom_path, "/Users/");
275 strcat(indy_atom_path, getenv("USER") );
276 strcat(indy_atom_path, "/Desktop/id3framedata.txt");
277
278 FILE* test_file = fopen(indy_atom_path, "wb");
279 if (test_file != NULL) {
280
281 fwrite(buffer, (size_t)buff_len, 1, test_file);
282 fclose(test_file);
283 }
284 free(indy_atom_path);
285 return;
329 void WriteZlibData(char *buffer, uint32_t buff_len) {
330 char *indy_atom_path = (char *)malloc(
331 sizeof(char) * MAXPATHLEN); // this malloc can escape memset because its
332 // only for in-house testing
333 strcat(indy_atom_path, "/Users/");
334 strcat(indy_atom_path, getenv("USER"));
335 strcat(indy_atom_path, "/Desktop/id3framedata.txt");
336
337 FILE *test_file = fopen(indy_atom_path, "wb");
338 if (test_file != NULL) {
339
340 fwrite(buffer, (size_t)buff_len, 1, test_file);
341 fclose(test_file);
342 }
343 free(indy_atom_path);
344 return;
286345 }
287346
288347 ///////////////////////////////////////////////////////////////////////////////////////
289 // cli functions //
348 // cli functions //
290349 ///////////////////////////////////////////////////////////////////////////////////////
291350
292 static const char* ReturnFrameTypeStr(int frametype) {
293 if (frametype == ID3_TEXT_FRAME) {
294 return "text frame ";
295 } else if (frametype == ID3_TEXT_FRAME_USERDEF) {
296 return "user defined text frame";
297 } else if (frametype == ID3_URL_FRAME) {
298 return "url frame ";
299 } else if (frametype == ID3_URL_FRAME_USERDEF) {
300 return "user defined url frame ";
301 } else if (frametype == ID3_UNIQUE_FILE_ID_FRAME) {
302 return "file ID ";
303 } else if (frametype == ID3_CD_ID_FRAME ) {
304 return "AudioCD ID frame ";
305 } else if (frametype == ID3_DESCRIBED_TEXT_FRAME) {
306 return "described text frame ";
307 } else if (frametype == ID3_ATTACHED_PICTURE_FRAME) {
308 return "picture frame ";
309 } else if (frametype == ID3_ATTACHED_OBJECT_FRAME) {
310 return "encapuslated object frm";
311 } else if (frametype == ID3_GROUP_ID_FRAME) {
312 return "group ID frame ";
313 } else if (frametype == ID3_SIGNATURE_FRAME) {
314 return "signature frame ";
315 } else if (frametype == ID3_PRIVATE_FRAME) {
316 return "private frame ";
317 } else if (frametype == ID3_PLAYCOUNTER_FRAME) {
318 return "playcounter ";
319 } else if (frametype == ID3_POPULAR_FRAME) {
320 return "popularimeter ";
321 }
322 return "";
323 }
351 static const char *ReturnFrameTypeStr(int frametype) {
352 if (frametype == ID3_TEXT_FRAME) {
353 return "text frame ";
354 } else if (frametype == ID3_TEXT_FRAME_USERDEF) {
355 return "user defined text frame";
356 } else if (frametype == ID3_URL_FRAME) {
357 return "url frame ";
358 } else if (frametype == ID3_URL_FRAME_USERDEF) {
359 return "user defined url frame ";
360 } else if (frametype == ID3_UNIQUE_FILE_ID_FRAME) {
361 return "file ID ";
362 } else if (frametype == ID3_CD_ID_FRAME) {
363 return "AudioCD ID frame ";
364 } else if (frametype == ID3_DESCRIBED_TEXT_FRAME) {
365 return "described text frame ";
366 } else if (frametype == ID3_ATTACHED_PICTURE_FRAME) {
367 return "picture frame ";
368 } else if (frametype == ID3_ATTACHED_OBJECT_FRAME) {
369 return "encapuslated object frm";
370 } else if (frametype == ID3_GROUP_ID_FRAME) {
371 return "group ID frame ";
372 } else if (frametype == ID3_SIGNATURE_FRAME) {
373 return "signature frame ";
374 } else if (frametype == ID3_PRIVATE_FRAME) {
375 return "private frame ";
376 } else if (frametype == ID3_PLAYCOUNTER_FRAME) {
377 return "playcounter ";
378 } else if (frametype == ID3_POPULAR_FRAME) {
379 return "popularimeter ";
380 }
381 return "";
382 }
324383
325384 void ListID3FrameIDstrings() {
326 const char* frametypestr = NULL;
327 const char* presetpadding = NULL;
328 uint16_t total_known_frames = (uint16_t)(sizeof(KnownFrames)/sizeof(*KnownFrames));
329 fprintf(stdout, "ID3v2.4 Implemented Frames:\nframeID type alias Description\n--------------------------------------------------------------------------\n");
330 for (uint16_t i = 1; i < total_known_frames; i++) {
331 if (strlen(KnownFrames[i].ID3V2p4_FrameID) != 4) continue;
332 frametypestr = ReturnFrameTypeStr(KnownFrames[i].ID3v2_FrameType);
333
334 int strpad = 12 - strlen(KnownFrames[i].CLI_frameIDpreset);
335 if (strpad == 12) {
336 presetpadding = " ";
337 } else if (strpad == 11) {
338 presetpadding = " ";
339 } else if (strpad == 10) {
340 presetpadding = " ";
341 } else if (strpad == 9) {
342 presetpadding = " ";
343 } else if (strpad == 8) {
344 presetpadding = " ";
345 } else if (strpad == 7) {
346 presetpadding = " ";
347 } else if (strpad == 6) {
348 presetpadding = " ";
349 } else if (strpad == 5) {
350 presetpadding = " ";
351 } else if (strpad == 4) {
352 presetpadding = " ";
353 } else if (strpad == 3) {
354 presetpadding = " ";
355 } else if (strpad == 2) {
356 presetpadding = " ";
357 } else if (strpad == 1) {
358 presetpadding = " ";
359 } else if (strpad <= 0) {
360 presetpadding = "";
361 }
362
363 fprintf(stdout, "%s %s %s%s | %s\n", KnownFrames[i].ID3V2p4_FrameID, frametypestr, KnownFrames[i].CLI_frameIDpreset, presetpadding, KnownFrames[i].ID3V2_FrameDescription);
364 }
365 fprintf(stdout,
366 "--------------------------------------------------------------------------\n"
367 "For each frame type, these parameters are available:\n"
368 " text frames: (str) [encoding]\n"
369 " user defined text frame : (str) [desc=(str)] [encoding]\n"
370 " url frame : (url)\n"
371 " user defined url frame : (url) [desc=(str)] [encoding]\n"
372 " file ID frame : (owner) [uniqueID={\"randomUUIDstamp\",(str)}]\n"
373 #if defined (DARWIN_PLATFORM)
374 " AudioCD ID frame : disk(num)\n"
375 #elif defined (HAVE_LINUX_CDROM_H)
376 " AudioCD ID frame : (/path)\n"
377 #elif defined (_WIN32)
378 " AudioCD ID frame : (letter)\n"
385 const char *frametypestr = NULL;
386 const char *presetpadding = NULL;
387 uint16_t total_known_frames =
388 (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames));
389 fprintf(stdout,
390 "ID3v2.4 Implemented Frames:\nframeID type "
391 " alias "
392 "Description\n-----------------------------------------------"
393 "---------------------------\n");
394 for (uint16_t i = 1; i < total_known_frames; i++) {
395 if (strlen(KnownFrames[i].ID3V2p4_FrameID) != 4)
396 continue;
397 frametypestr = ReturnFrameTypeStr(KnownFrames[i].ID3v2_FrameType);
398
399 int strpad = 12 - strlen(KnownFrames[i].CLI_frameIDpreset);
400 if (strpad == 12) {
401 presetpadding = " ";
402 } else if (strpad == 11) {
403 presetpadding = " ";
404 } else if (strpad == 10) {
405 presetpadding = " ";
406 } else if (strpad == 9) {
407 presetpadding = " ";
408 } else if (strpad == 8) {
409 presetpadding = " ";
410 } else if (strpad == 7) {
411 presetpadding = " ";
412 } else if (strpad == 6) {
413 presetpadding = " ";
414 } else if (strpad == 5) {
415 presetpadding = " ";
416 } else if (strpad == 4) {
417 presetpadding = " ";
418 } else if (strpad == 3) {
419 presetpadding = " ";
420 } else if (strpad == 2) {
421 presetpadding = " ";
422 } else if (strpad == 1) {
423 presetpadding = " ";
424 } else if (strpad <= 0) {
425 presetpadding = "";
426 }
427
428 fprintf(stdout,
429 "%s %s %s%s | %s\n",
430 KnownFrames[i].ID3V2p4_FrameID,
431 frametypestr,
432 KnownFrames[i].CLI_frameIDpreset,
433 presetpadding,
434 KnownFrames[i].ID3V2_FrameDescription);
435 }
436 fprintf(
437 stdout,
438 "------------------------------------------------------------------------"
439 "--\n"
440 "For each frame type, these parameters are available:\n"
441 " text frames: (str) [encoding]\n"
442 " user defined text frame : (str) [desc=(str)] [encoding]\n"
443 " url frame : (url)\n"
444 " user defined url frame : (url) [desc=(str)] [encoding]\n"
445 " file ID frame : (owner) "
446 "[uniqueID={\"randomUUIDstamp\",(str)}]\n"
447 #if defined(__APPLE__)
448 " AudioCD ID frame : disk(num)\n"
449 #elif defined(__linux__)
450 " AudioCD ID frame : (/path)\n"
451 #elif defined(_WIN32)
452 " AudioCD ID frame : (letter)\n"
379453 #endif
380 " described text frame : (str) [desc=(str)] [encoding]\n"
381 " picture frame : (/path) [desc=(str)] [mimetype=(str)] [imagetype=(hex)] [encoding]\n"
382 " encapuslated object frame : (/path) [desc=(str)] [mimetype=(str)] [filename={\"FILENAMESTAMP\",(str)}] [encoding]\n"
383 " group ID frame : (owner) groupsymbol=(hex) [data=(str)]\n"
384 " signature frame : (str) groupsymbol=(hex)\n"
385 " private frame : (owner) data=(str)\n"
386 " playcounter : (num or \"+1\")\n"
387 " popularimeter : (owner) rating=(1...255) [counter=(num or \"+1\")]\n"
388 "\n"
389 " Legend:\n"
390 " parameters in brackets[] signal an optional parameter, parens() signal a required parameter\n"
391 " [encoding] may be one either the default UTF8, or one of { LATIN1 UTF16BE UTF16LE }\n"
392 " (str) signals a string - like \"Suzie\"\n"
393 " (num) means a number; +1 will increment a counter by 1; (hex) means a hexadecimal number - like 0x11)\n"
394 " (url) menas a url, in string form; (owner) means a url/email string\n"
395 " uniqueID=randomUUIDstamp will create a high quality random uuid\n"
396 " filename=FILENAMESTAMP will embed the name of the file given in the /path for GEOB\n"
397 "\n"
398 " All frames also take additional parameters:\n"
399 " [{root,track=(num)}] specifies file level, track level or (default) movie level for an ID32 atom\n"
400 " [compress] compresses the given frame using zlib deflate compression\n"
401 " [groupsymbol=(num)] associates a frame with a GRID frame of the same group symbol\n"
402 " [lang=(3char)] (default='eng') sets the language/ID32 atom to which the frame belongs\n"
403 " use AP --languages-list to see a list of available languages\n"
404 );
405
406
407 return;
454 " described text frame : (str) [desc=(str)] [encoding]\n"
455 " picture frame : (/path) [desc=(str)] [mimetype=(str)] "
456 "[imagetype=(hex)] [encoding]\n"
457 " encapuslated object frame : (/path) [desc=(str)] [mimetype=(str)] "
458 "[filename={\"FILENAMESTAMP\",(str)}] [encoding]\n"
459 " group ID frame : (owner) groupsymbol=(hex) [data=(str)]\n"
460 " signature frame : (str) groupsymbol=(hex)\n"
461 " private frame : (owner) data=(str)\n"
462 " playcounter : (num or \"+1\")\n"
463 " popularimeter : (owner) rating=(1...255) [counter=(num "
464 "or \"+1\")]\n"
465 "\n"
466 " Legend:\n"
467 " parameters in brackets[] signal an optional parameter, parens() "
468 "signal a required parameter\n"
469 " [encoding] may be one either the default UTF8, or one of { LATIN1 "
470 "UTF16BE UTF16LE }\n"
471 " (str) signals a string - like \"Suzie\"\n"
472 " (num) means a number; +1 will increment a counter by 1; (hex) "
473 "means a hexadecimal number - like 0x11)\n"
474 " (url) menas a url, in string form; (owner) means a url/email "
475 "string\n"
476 " uniqueID=randomUUIDstamp will create a high quality random uuid\n"
477 " filename=FILENAMESTAMP will embed the name of the file given in "
478 "the /path for GEOB\n"
479 "\n"
480 " All frames also take additional parameters:\n"
481 " [{root,track=(num)}] specifies file level, track level or "
482 "(default) movie level for an ID32 atom\n"
483 " [compress] compresses the given frame using zlib deflate "
484 "compression\n"
485 " [groupsymbol=(num)] associates a frame with a GRID frame of the "
486 "same group symbol\n"
487 " [lang=(3char)] (default='eng') sets the language/ID32 atom to "
488 "which the frame belongs\n"
489 " use AP --languages-list to see a list of available "
490 "languages\n");
491
492 return;
408493 }
409494
410495 void List_imagtype_strings() {
411 uint8_t total_imgtyps = (uint8_t)(sizeof(ImageTypeList)/sizeof(*ImageTypeList));
412 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");
413 for (uint8_t i=0; i < total_imgtyps; i++) {
414 fprintf(stdout, " %s \"%s\"\n", ImageTypeList[i].hexstring, ImageTypeList[i].imagetype_str);
415 }
416 return;
417 }
418
419 const char* ConvertCLIFrameStr_TO_frameID(const char* frame_str) {
420 const char* discovered_frameID = NULL;
421 uint16_t total_known_frames = (uint16_t)(sizeof(KnownFrames)/sizeof(*KnownFrames));
422
423 for (uint16_t i = 0; i < total_known_frames; i++) {
424 if (strcmp(KnownFrames[i].CLI_frameIDpreset, frame_str) == 0) {
425 if (AtomicParsley_ID3v2Tag_MajorVersion == 2) discovered_frameID = KnownFrames[i].ID3V2p2_FrameID;
426 if (AtomicParsley_ID3v2Tag_MajorVersion == 3) discovered_frameID = KnownFrames[i].ID3V2p3_FrameID;
427 if (AtomicParsley_ID3v2Tag_MajorVersion == 4) discovered_frameID = KnownFrames[i].ID3V2p4_FrameID;
428
429 if (strlen(discovered_frameID) == 0) discovered_frameID = NULL;
430 break;
431 }
432 }
433 return discovered_frameID;
434 }
435
436 //0 = description
437 //1 = mimetype
438 //2 = type
496 uint8_t total_imgtyps =
497 (uint8_t)(sizeof(ImageTypeList) / sizeof(*ImageTypeList));
498 fprintf(stdout,
499 "These 'image types' are used to identify pictures embedded in "
500 "'APIC' ID3 tags:\n usage is \"AP --ID3Tag APIC /path.jpg "
501 "--imagetype=\"str\"\n str can be either the hex listing *or* "
502 "the full string\n default is 0x00 - meaning 'Other'\n Hex "
503 " Full String\n ----------------------------\n");
504 for (uint8_t i = 0; i < total_imgtyps; i++) {
505 fprintf(stdout,
506 " %s \"%s\"\n",
507 ImageTypeList[i].hexstring,
508 ImageTypeList[i].imagetype_str);
509 }
510 return;
511 }
512
513 const char *ConvertCLIFrameStr_TO_frameID(const char *frame_str) {
514 const char *discovered_frameID = NULL;
515 uint16_t total_known_frames =
516 (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames));
517
518 for (uint16_t i = 0; i < total_known_frames; i++) {
519 if (strcmp(KnownFrames[i].CLI_frameIDpreset, frame_str) == 0) {
520 if (AtomicParsley_ID3v2Tag_MajorVersion == 2)
521 discovered_frameID = KnownFrames[i].ID3V2p2_FrameID;
522 if (AtomicParsley_ID3v2Tag_MajorVersion == 3)
523 discovered_frameID = KnownFrames[i].ID3V2p3_FrameID;
524 if (AtomicParsley_ID3v2Tag_MajorVersion == 4)
525 discovered_frameID = KnownFrames[i].ID3V2p4_FrameID;
526
527 if (strlen(discovered_frameID) == 0)
528 discovered_frameID = NULL;
529 break;
530 }
531 }
532 return discovered_frameID;
533 }
534
535 // 0 = description
536 // 1 = mimetype
537 // 2 = type
439538 bool TestCLI_for_FrameParams(int frametype, uint8_t testparam) {
440 if (frametype == ID3_URL_FRAME_USERDEF && testparam == 0) return true;
441
442 if (frametype == ID3_UNIQUE_FILE_ID_FRAME && testparam == 3) {
443 return true;
444 } else if (frametype == ID3_ATTACHED_OBJECT_FRAME && testparam == 4) {
445 return true;
446 } else if (frametype == ID3_POPULAR_FRAME && testparam == 5) {
447 return true;
448 } else if (frametype == ID3_POPULAR_FRAME && testparam == 6) {
449 return true;
450 } else if (frametype == ID3_GROUP_ID_FRAME && testparam == 7) {
451 return true;
452 } else if (frametype == ID3_PRIVATE_FRAME && testparam == 8) {
453 return true;
454 } else {
455 uint8_t frametype_idx = GetFrameCompositionDescription(frametype);
456
457 for (uint8_t fld_i = 0; fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount; fld_i++) {
458 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == ID3_DESCRIPTION_FIELD && testparam == 0) {
459 return true;
460 }
461 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == ID3_MIME_TYPE_FIELD && testparam == 1) {
462 return true;
463 }
464 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == ID3_PIC_TYPE_FIELD && testparam == 2) {
465 return true;
466 }
467 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] == ID3_PIC_TYPE_FIELD && testparam == 3) {
468 return true;
469 }
470 }
471 }
472 return false;
539 if (frametype == ID3_URL_FRAME_USERDEF && testparam == 0)
540 return true;
541
542 if (frametype == ID3_UNIQUE_FILE_ID_FRAME && testparam == 3) {
543 return true;
544 } else if (frametype == ID3_ATTACHED_OBJECT_FRAME && testparam == 4) {
545 return true;
546 } else if (frametype == ID3_POPULAR_FRAME && testparam == 5) {
547 return true;
548 } else if (frametype == ID3_POPULAR_FRAME && testparam == 6) {
549 return true;
550 } else if (frametype == ID3_GROUP_ID_FRAME && testparam == 7) {
551 return true;
552 } else if (frametype == ID3_PRIVATE_FRAME && testparam == 8) {
553 return true;
554 } else {
555 uint8_t frametype_idx = GetFrameCompositionDescription(frametype);
556
557 for (uint8_t fld_i = 0;
558 fld_i < FrameTypeConstructionList[frametype_idx].ID3_FieldCount;
559 fld_i++) {
560 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
561 ID3_DESCRIPTION_FIELD &&
562 testparam == 0) {
563 return true;
564 }
565 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
566 ID3_MIME_TYPE_FIELD &&
567 testparam == 1) {
568 return true;
569 }
570 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
571 ID3_PIC_TYPE_FIELD &&
572 testparam == 2) {
573 return true;
574 }
575 if (FrameTypeConstructionList[frametype_idx].ID3_FieldComponents[fld_i] ==
576 ID3_PIC_TYPE_FIELD &&
577 testparam == 3) {
578 return true;
579 }
580 }
581 }
582 return false;
473583 }
474584
475585 ///////////////////////////////////////////////////////////////////////////////////////
476 // frame identity functions //
586 // frame identity functions //
477587 ///////////////////////////////////////////////////////////////////////////////////////
478588
479 int MatchID3FrameIDstr(const char* foundFrameID, uint8_t tagVersion) {
480 uint16_t total_known_frames = (uint16_t)(sizeof(KnownFrames)/sizeof(*KnownFrames));
481 uint8_t frameLen = (tagVersion >= 3 ? 4 : 3) +1;
482
483 for (int i = 0; i < total_known_frames; i++) {
484 const char* testFrameID = NULL;
485 if (tagVersion == 2) testFrameID = KnownFrames[i].ID3V2p2_FrameID;
486 if (tagVersion == 3) testFrameID = KnownFrames[i].ID3V2p3_FrameID;
487 if (tagVersion == 4) testFrameID = KnownFrames[i].ID3V2p4_FrameID;
488
489 if (memcmp(foundFrameID, testFrameID, frameLen) == 0) {
490 return KnownFrames[i].ID3v2_InternalFrameID;
491 }
492 }
493 return ID3v2_UNKNOWN_FRAME; //return the UnknownFrame if it can't be found
589 int MatchID3FrameIDstr(const char *foundFrameID, uint8_t tagVersion) {
590 uint16_t total_known_frames =
591 (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames));
592 uint8_t frameLen = (tagVersion >= 3 ? 4 : 3) + 1;
593
594 for (int i = 0; i < total_known_frames; i++) {
595 const char *testFrameID = NULL;
596 if (tagVersion == 2)
597 testFrameID = KnownFrames[i].ID3V2p2_FrameID;
598 if (tagVersion == 3)
599 testFrameID = KnownFrames[i].ID3V2p3_FrameID;
600 if (tagVersion == 4)
601 testFrameID = KnownFrames[i].ID3V2p4_FrameID;
602
603 if (memcmp(foundFrameID, testFrameID, frameLen) == 0) {
604 return KnownFrames[i].ID3v2_InternalFrameID;
605 }
606 }
607 return ID3v2_UNKNOWN_FRAME; // return the UnknownFrame if it can't be found
494608 }
495609
496610 uint8_t GetFrameCompositionDescription(int ID3v2_FrameTypeID) {
497 uint8_t matchingFrameDescription = 0; //return the UnknownFrame/UnknownField if it can't be found
498 uint8_t total_frame_descrips = (uint8_t)(sizeof(FrameTypeConstructionList)/sizeof(*FrameTypeConstructionList));
499
500 for (uint8_t i = 0; i < total_frame_descrips; i++) {
501 if (FrameTypeConstructionList[i].ID3_FrameType == ID3v2_FrameTypeID) {
502 matchingFrameDescription = i;
503 break;
504 }
505 }
506 return matchingFrameDescription;
507 }
508
509 int FrameStr_TO_FrameType(const char* frame_str) {
510 const char* eval_framestr = NULL;
511 int frame_type = 0;
512 uint16_t total_known_frames = (uint16_t)(sizeof(KnownFrames)/sizeof(*KnownFrames));
513
514 for (uint16_t i = 0; i < total_known_frames; i++) {
515 if (AtomicParsley_ID3v2Tag_MajorVersion == 2) eval_framestr = KnownFrames[i].ID3V2p2_FrameID;
516 if (AtomicParsley_ID3v2Tag_MajorVersion == 3) eval_framestr = KnownFrames[i].ID3V2p3_FrameID;
517 if (AtomicParsley_ID3v2Tag_MajorVersion == 4) eval_framestr = KnownFrames[i].ID3V2p4_FrameID;
518
519 if (strcmp(frame_str, eval_framestr) == 0) {
520 frame_type = KnownFrames[i].ID3v2_FrameType;
521 break;
522 }
523 }
524 return frame_type;
525 }
526
527 ID3v2Fields* APar_FindLastTextField(ID3v2Frame* aFrame) {
528 ID3v2Fields* lastusedtextfield = NULL;
529 if (aFrame->textfield_tally > 0) {
530 lastusedtextfield = aFrame->ID3v2_Frame_Fields+1;
531 while (true) {
532 if (lastusedtextfield->next_field == NULL) {
533 break;
534 }
535 lastusedtextfield = lastusedtextfield->next_field;
536 }
537 }
538 return lastusedtextfield;
539 }
540
541 bool APar_ExtraTextFieldInit(ID3v2Fields* lastField, uint32_t utf8len, uint8_t textencoding) {
542 ID3v2Fields* extraField = NULL;
543 lastField->next_field = (ID3v2Fields*)calloc(1, sizeof(ID3v2Fields)*1);
544 if (lastField->next_field == NULL) {
545 fprintf(stdout, "There was insufficient memory to allocate another ID3 field\n");
546 exit(12);
547 }
548 extraField = lastField->next_field;
549 extraField->ID3v2_Field_Type = ID3_TEXT_FIELD;
550 extraField->field_length = 0;
551
552 if (textencoding == TE_UTF16LE_WITH_BOM || textencoding == TE_UTF16BE_NO_BOM) {
553 extraField->alloc_length = 2 + (utf8len * 2);
554 } else {
555 extraField->alloc_length = utf8len + 1;
556 }
557 if (extraField->alloc_length > 0) {
558 extraField->field_string = (char*)calloc(1, sizeof(char*) * extraField->alloc_length);
559 if (!APar_assert((extraField->field_string != NULL), 11, "while setting an extra text field") ) exit(11);
560 return true;
561 }
562 return false;
611 uint8_t matchingFrameDescription =
612 0; // return the UnknownFrame/UnknownField if it can't be found
613 uint8_t total_frame_descrips = (uint8_t)(sizeof(FrameTypeConstructionList) /
614 sizeof(*FrameTypeConstructionList));
615
616 for (uint8_t i = 0; i < total_frame_descrips; i++) {
617 if (FrameTypeConstructionList[i].ID3_FrameType == ID3v2_FrameTypeID) {
618 matchingFrameDescription = i;
619 break;
620 }
621 }
622 return matchingFrameDescription;
623 }
624
625 int FrameStr_TO_FrameType(const char *frame_str) {
626 const char *eval_framestr = NULL;
627 int frame_type = 0;
628 uint16_t total_known_frames =
629 (uint16_t)(sizeof(KnownFrames) / sizeof(*KnownFrames));
630
631 for (uint16_t i = 0; i < total_known_frames; i++) {
632 if (AtomicParsley_ID3v2Tag_MajorVersion == 2)
633 eval_framestr = KnownFrames[i].ID3V2p2_FrameID;
634 if (AtomicParsley_ID3v2Tag_MajorVersion == 3)
635 eval_framestr = KnownFrames[i].ID3V2p3_FrameID;
636 if (AtomicParsley_ID3v2Tag_MajorVersion == 4)
637 eval_framestr = KnownFrames[i].ID3V2p4_FrameID;
638
639 if (strcmp(frame_str, eval_framestr) == 0) {
640 frame_type = KnownFrames[i].ID3v2_FrameType;
641 break;
642 }
643 }
644 return frame_type;
645 }
646
647 ID3v2Fields *APar_FindLastTextField(ID3v2Frame *aFrame) {
648 ID3v2Fields *lastusedtextfield = NULL;
649 if (aFrame->textfield_tally > 0) {
650 lastusedtextfield = aFrame->ID3v2_Frame_Fields + 1;
651 while (true) {
652 if (lastusedtextfield->next_field == NULL) {
653 break;
654 }
655 lastusedtextfield = lastusedtextfield->next_field;
656 }
657 }
658 return lastusedtextfield;
659 }
660
661 bool APar_ExtraTextFieldInit(ID3v2Fields *lastField,
662 uint32_t utf8len,
663 uint8_t textencoding) {
664 ID3v2Fields *extraField = NULL;
665 lastField->next_field = (ID3v2Fields *)calloc(1, sizeof(ID3v2Fields) * 1);
666 if (lastField->next_field == NULL) {
667 fprintf(stdout,
668 "There was insufficient memory to allocate another ID3 field\n");
669 exit(12);
670 }
671 extraField = lastField->next_field;
672 extraField->ID3v2_Field_Type = ID3_TEXT_FIELD;
673 extraField->field_length = 0;
674
675 if (textencoding == TE_UTF16LE_WITH_BOM ||
676 textencoding == TE_UTF16BE_NO_BOM) {
677 extraField->alloc_length = 2 + (utf8len * 2);
678 } else {
679 extraField->alloc_length = utf8len + 1;
680 }
681 if (extraField->alloc_length > 0) {
682 extraField->field_string =
683 (char *)calloc(1, sizeof(char *) * extraField->alloc_length);
684 if (!APar_assert((extraField->field_string != NULL),
685 11,
686 "while setting an extra text field"))
687 exit(11);
688 return true;
689 }
690 return false;
563691 }
564692
565693 ///////////////////////////////////////////////////////////////////////////////////////
566 // id3 parsing functions //
694 // id3 parsing functions //
567695 ///////////////////////////////////////////////////////////////////////////////////////
568696
569 uint32_t APar_ExtractField(char* buffer, uint32_t maxFieldLen, ID3v2Frame* thisFrame, ID3v2Fields* thisField, int fieldType, uint8_t textEncoding) {
570 uint32_t bytes_used = 0;
571 thisField->next_field = NULL;
572 switch(fieldType) {
573 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
574 thisField->ID3v2_Field_Type = ID3_UNKNOWN_FIELD;
575 thisField->field_length = maxFieldLen;
576 thisField->field_string = (char*)calloc(1, sizeof(char)*(maxFieldLen+1 > 16 ? maxFieldLen+1 : 16));
577 thisField->alloc_length = sizeof(char)*(maxFieldLen+1 > 16 ? maxFieldLen+1 : 16);
578 memcpy(thisField->field_string, buffer, maxFieldLen);
579
580 bytes_used = maxFieldLen;
581 break;
582 }
583 case ID3_PIC_TYPE_FIELD :
584 case ID3_GROUPSYMBOL_FIELD :
585 case ID3_TEXT_ENCODING_FIELD : {
586 thisField->ID3v2_Field_Type = fieldType;
587 thisField->field_length = 1;
588 thisField->field_string = (char*)calloc(1, sizeof(char)*16);
589 thisField->field_string[0] = buffer[0]; //memcpy(thisField->field_string, buffer, 1);
590 thisField->alloc_length = sizeof(char)*16;
591
592 bytes_used = 1;
593 break;
594 }
595 case ID3_LANGUAGE_FIELD : {
596 thisField->ID3v2_Field_Type = ID3_LANGUAGE_FIELD;
597 thisField->field_length = 3;
598 thisField->field_string = (char*)calloc(1, sizeof(char)*16);
599 memcpy(thisField->field_string, buffer, 3);
600 thisField->alloc_length = sizeof(char)*16;
601
602 bytes_used = 3;
603 break;
604 }
605 case ID3_TEXT_FIELD :
606 case ID3_URL_FIELD :
607 case ID3_COUNTER_FIELD :
608 case ID3_BINARY_DATA_FIELD : { //this class of fields may contains NULLs but is *NOT* NULL terminated in any form
609 thisField->ID3v2_Field_Type = fieldType;
610 thisField->field_length = maxFieldLen;
611 thisField->field_string = (char*)calloc(1, sizeof(char)*maxFieldLen+1 > 16 ? maxFieldLen+1 : 16);
612 memcpy(thisField->field_string, buffer, maxFieldLen);
613 thisField->alloc_length = (sizeof(char)*maxFieldLen+1 > 16 ? maxFieldLen+1 : 16);
614
615 if (fieldType == ID3_TEXT_FIELD) {
616 bytes_used = findstringNULLterm(buffer, textEncoding, maxFieldLen);
617 } else {
618 bytes_used = maxFieldLen;
619 }
620 break;
621 }
622 case ID3_MIME_TYPE_FIELD :
623 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)
624 thisField->ID3v2_Field_Type = fieldType;
625 thisField->field_length = findstringNULLterm(buffer, 0, maxFieldLen);
626 thisField->field_string = (char*)calloc(1, sizeof(char) * thisField->field_length +1 > 16 ? thisField->field_length +1 : 16);
627 memcpy(thisField->field_string, buffer, thisField->field_length);
628 thisField->alloc_length = (sizeof(char)*maxFieldLen+1 > 16 ? maxFieldLen+1 : 16);
629
630 bytes_used = thisField->field_length;
631 break;
632
633 }
634 case ID3_FILENAME_FIELD :
635 case ID3_DESCRIPTION_FIELD : {
636 thisField->ID3v2_Field_Type = fieldType;
637 thisField->field_length = findstringNULLterm(buffer, textEncoding, maxFieldLen);
638 thisField->field_string = (char*)calloc(1, sizeof(char) * thisField->field_length +1 > 16 ? thisField->field_length +1 : 16);
639 memcpy(thisField->field_string, buffer, thisField->field_length);
640 thisField->alloc_length = (sizeof(char) * thisField->field_length +1 > 16 ? thisField->field_length +1 : 16);
641
642 bytes_used = thisField->field_length;
643 break;
644 }
645 }
646 //fprintf(stdout, "%" PRIu32 ", %s, %s\n", bytes_used, buffer, (thisFrame->ID3v2_Frame_Fields+fieldNum)->field_string);
647 return bytes_used;
648 }
649
650 void APar_ScanID3Frame(ID3v2Frame* targetframe, char* frame_ptr, uint32_t frameLen) {
651 uint64_t offset_into_frame = 0;
652
653 switch(targetframe->ID3v2_FrameType) {
654 case ID3_UNKNOWN_FRAME : {
655 APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_UNKNOWN_FIELD, 0);
656 break;
657 }
658 case ID3_TEXT_FRAME : {
659 uint8_t textencoding = 0xFF;
660 offset_into_frame += APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_TEXT_ENCODING_FIELD, 0);
661
662 offset_into_frame += APar_ExtractField(frame_ptr + 1, frameLen - 1, targetframe, targetframe->ID3v2_Frame_Fields+1,
663 ID3_TEXT_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]);
664 targetframe->textfield_tally++;
665
666 if (offset_into_frame >= frameLen) break;
667 textencoding = targetframe->ID3v2_Frame_Fields->field_string[0];
668
669 if (offset_into_frame < frameLen) {
670 while (true) {
671 if (offset_into_frame >= frameLen) break;
672
673 //skip the required separator for multiple strings
674 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
675 offset_into_frame += 1;
676 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
677 offset_into_frame += 2;
678 }
679
680 //multiple id3v2.4 strings should be separated with a single NULL byte; some implementations might terminate the string AND use a NULL separator
681 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
682 if ((frame_ptr + offset_into_frame)[0] == 0) offset_into_frame+=1;
683 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
684 if ((frame_ptr + offset_into_frame)[0] == 0 && (frame_ptr + offset_into_frame)[1] == 0) offset_into_frame+=2;
685 }
686
687 //a 3rd NULL would not be good
688 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
689 if ((frame_ptr + offset_into_frame)[0] == 0) break;
690 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
691 if ((frame_ptr + offset_into_frame)[0] == 0 && (frame_ptr + offset_into_frame)[1] == 0) break;
692 }
693
694 ID3v2Fields* last_textfield = APar_FindLastTextField(targetframe);
695 if (APar_ExtraTextFieldInit(last_textfield, frameLen - offset_into_frame, textencoding)) {
696 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, last_textfield->next_field,
697 ID3_TEXT_FIELD, textencoding);
698 targetframe->textfield_tally++;
699 }
700 //copy the string to the new field
701 break;
702 }
703 }
704 break;
705 }
706 case ID3_URL_FRAME : {
707 APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_URL_FIELD, 0);
708 break;
709 }
710 case ID3_TEXT_FRAME_USERDEF :
711 case ID3_URL_FRAME_USERDEF : {
712 offset_into_frame += APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_TEXT_ENCODING_FIELD, 0);
713
714 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe,
715 targetframe->ID3v2_Frame_Fields+1, ID3_DESCRIPTION_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]);
716
717 offset_into_frame += skipNULLterm(frame_ptr + offset_into_frame, targetframe->ID3v2_Frame_Fields->field_string[0], frameLen - offset_into_frame);
718
719 if (targetframe->ID3v2_FrameType == ID3_TEXT_FRAME_USERDEF) {
720 APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe,
721 targetframe->ID3v2_Frame_Fields+2, ID3_TEXT_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]);
722 } else if (targetframe->ID3v2_FrameType == ID3_URL_FRAME_USERDEF) {
723 APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe,
724 targetframe->ID3v2_Frame_Fields+2, ID3_URL_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]);
725 }
726 break;
727 }
728 case ID3_UNIQUE_FILE_ID_FRAME : {
729 offset_into_frame += APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_OWNER_FIELD, 0);
730 offset_into_frame++; //iso-8859-1 owner field is NULL terminated
731
732 APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_BINARY_DATA_FIELD, 0);
733 break;
734 }
735 case ID3_CD_ID_FRAME : {
736 APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_BINARY_DATA_FIELD, 0);
737 break;
738 }
739 case ID3_DESCRIBED_TEXT_FRAME : {
740 offset_into_frame += APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_TEXT_ENCODING_FIELD, 0);
741
742 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, 3, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_LANGUAGE_FIELD, 0);
743
744 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe,
745 targetframe->ID3v2_Frame_Fields+2, ID3_DESCRIPTION_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]);
746
747 offset_into_frame += skipNULLterm(frame_ptr + offset_into_frame, targetframe->ID3v2_Frame_Fields->field_string[0], frameLen - offset_into_frame);
748
749 if (frameLen > offset_into_frame) {
750 APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+3,
751 ID3_TEXT_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]);
752 }
753 break;
754 }
755 case ID3_ATTACHED_OBJECT_FRAME :
756 case ID3_ATTACHED_PICTURE_FRAME : {
757 offset_into_frame += APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_TEXT_ENCODING_FIELD, 0);
758
759 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_MIME_TYPE_FIELD, 0);
760
761 offset_into_frame += 1; //should only be 1 NULL
762
763 if (targetframe->ID3v2_FrameType == ID3_ATTACHED_PICTURE_FRAME) {
764 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, 1, targetframe, targetframe->ID3v2_Frame_Fields+2, ID3_PIC_TYPE_FIELD, 0);
765 } else {
766 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe,
767 targetframe->ID3v2_Frame_Fields+2, ID3_FILENAME_FIELD, 0);
768
769 offset_into_frame+=skipNULLterm(frame_ptr + offset_into_frame, targetframe->ID3v2_Frame_Fields->field_string[0], frameLen - offset_into_frame);
770 }
771
772 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+3,
773 ID3_DESCRIPTION_FIELD, targetframe->ID3v2_Frame_Fields->field_string[0]);
774
775 offset_into_frame += skipNULLterm(frame_ptr + offset_into_frame, targetframe->ID3v2_Frame_Fields->field_string[0], frameLen - offset_into_frame);
776
777 if (frameLen > offset_into_frame) {
778 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+4,
779 ID3_BINARY_DATA_FIELD, 0);
780 }
781 break;
782 }
783 case ID3_PRIVATE_FRAME : { //the only difference between the 'priv' frame & the 'ufid' frame is ufid is limited to 64 bytes
784 offset_into_frame += APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_OWNER_FIELD, 0);
785 offset_into_frame++; //iso-8859-1 owner field is NULL terminated
786
787 APar_ExtractField(frame_ptr + offset_into_frame, frameLen - 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_BINARY_DATA_FIELD, 0);
788 break;
789 }
790 case ID3_GROUP_ID_FRAME : {
791 offset_into_frame += APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_OWNER_FIELD, 0);
792 offset_into_frame++; //iso-8859-1 owner field is NULL terminated
793
794 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_GROUPSYMBOL_FIELD, 0);
795
796 if (frameLen > offset_into_frame) {
797 APar_ExtractField(frame_ptr + offset_into_frame, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+2, ID3_BINARY_DATA_FIELD, 0);
798 }
799 break;
800 }
801 case ID3_SIGNATURE_FRAME : {
802 APar_ExtractField(frame_ptr, 1, targetframe, targetframe->ID3v2_Frame_Fields, ID3_GROUPSYMBOL_FIELD, 0);
803
804 APar_ExtractField(frame_ptr + 1, frameLen - 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_BINARY_DATA_FIELD, 0);
805 break;
806 }
807 case ID3_PLAYCOUNTER_FRAME : {
808 APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_COUNTER_FIELD, 0);
809 break;
810 }
811 case ID3_POPULAR_FRAME : {
812 offset_into_frame += APar_ExtractField(frame_ptr, frameLen, targetframe, targetframe->ID3v2_Frame_Fields, ID3_OWNER_FIELD, 0); //surrogate for 'emai to user' field
813 offset_into_frame++; //iso-8859-1 email address field is NULL terminated
814
815 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame, 1, targetframe, targetframe->ID3v2_Frame_Fields+1, ID3_BINARY_DATA_FIELD, 0);
816
817 if (frameLen > offset_into_frame) {
818 APar_ExtractField(frame_ptr, frameLen - offset_into_frame, targetframe, targetframe->ID3v2_Frame_Fields+2, ID3_COUNTER_FIELD, 0);
819 }
820 break;
821 }
822 case ID3_OLD_V2P2_PICTURE_FRAME : {
823 break; //unimplemented
824 }
825 }
826 return;
827 }
828
829 void APar_ID32_ScanID3Tag(FILE* source_file, AtomicInfo* id32_atom) {
830 char* id32_fulltag = (char*)calloc(1, sizeof(char)*id32_atom->AtomicLength);
831 char* fulltag_ptr = id32_fulltag;
832
833 if (id32_atom->AtomicLength < 20) return;
834 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
835
836 if (memcmp(id32_fulltag, "ID3", 3) != 0) return;
837 fulltag_ptr+=3;
838
839 id32_atom->ID32_TagInfo = (ID3v2Tag*)calloc(1, sizeof(ID3v2Tag));
840 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion = *fulltag_ptr;
841 fulltag_ptr++;
842 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion = *fulltag_ptr;
843 fulltag_ptr++;
844 id32_atom->ID32_TagInfo->ID3v2Tag_Flags = *fulltag_ptr;
845 fulltag_ptr++;
846
847 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4) {
848 fprintf(stdout, "AtomicParsley warning: an ID32 atom was encountered using an unsupported ID3v2 tag version: %u. Skipping\n", id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
849 return;
850 }
851 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4 && id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion != 0) {
852 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);
853 return;
854 }
855
856 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_BIT0+ID32_TAGFLAG_BIT1+ID32_TAGFLAG_BIT2+ID32_TAGFLAG_BIT3)) return;
857
858 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_FOOTER)) {
859 fprintf(stdout, "AtomicParsley error: an ID32 atom was encountered with a forbidden footer flag. Exiting.\n");
860 free(id32_fulltag);
861 id32_fulltag = NULL;
862 return;
863 }
864
865 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_EXPERIMENTAL)) {
697 uint32_t APar_ExtractField(char *buffer,
698 uint32_t maxFieldLen,
699 ID3v2Frame *thisFrame,
700 ID3v2Fields *thisField,
701 int fieldType,
702 uint8_t textEncoding) {
703 uint32_t bytes_used = 0;
704 thisField->next_field = NULL;
705 switch (fieldType) {
706 case ID3_UNKNOWN_FIELD: { // the difference between this unknown field & say a
707 // binary data field is the unknown field is always
708 // the first (and only) field
709 thisField->ID3v2_Field_Type = ID3_UNKNOWN_FIELD;
710 thisField->field_length = maxFieldLen;
711 thisField->field_string = (char *)calloc(
712 1, sizeof(char) * (maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16));
713 thisField->alloc_length =
714 sizeof(char) * (maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16);
715 memcpy(thisField->field_string, buffer, maxFieldLen);
716
717 bytes_used = maxFieldLen;
718 break;
719 }
720 case ID3_PIC_TYPE_FIELD:
721 case ID3_GROUPSYMBOL_FIELD:
722 case ID3_TEXT_ENCODING_FIELD: {
723 thisField->ID3v2_Field_Type = fieldType;
724 thisField->field_length = 1;
725 thisField->field_string = (char *)calloc(1, sizeof(char) * 16);
726 thisField->field_string[0] =
727 buffer[0]; // memcpy(thisField->field_string, buffer, 1);
728 thisField->alloc_length = sizeof(char) * 16;
729
730 bytes_used = 1;
731 break;
732 }
733 case ID3_LANGUAGE_FIELD: {
734 thisField->ID3v2_Field_Type = ID3_LANGUAGE_FIELD;
735 thisField->field_length = 3;
736 thisField->field_string = (char *)calloc(1, sizeof(char) * 16);
737 memcpy(thisField->field_string, buffer, 3);
738 thisField->alloc_length = sizeof(char) * 16;
739
740 bytes_used = 3;
741 break;
742 }
743 case ID3_TEXT_FIELD:
744 case ID3_URL_FIELD:
745 case ID3_COUNTER_FIELD:
746 case ID3_BINARY_DATA_FIELD: { // this class of fields may contains NULLs but
747 // is *NOT* NULL terminated in any form
748 thisField->ID3v2_Field_Type = fieldType;
749 thisField->field_length = maxFieldLen;
750 thisField->field_string = (char *)calloc(
751 1, sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16);
752 memcpy(thisField->field_string, buffer, maxFieldLen);
753 thisField->alloc_length =
754 (sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16);
755
756 if (fieldType == ID3_TEXT_FIELD) {
757 bytes_used = findstringNULLterm(buffer, textEncoding, maxFieldLen);
758 } else {
759 bytes_used = maxFieldLen;
760 }
761 break;
762 }
763 case ID3_MIME_TYPE_FIELD:
764 case ID3_OWNER_FIELD: { // difference between ID3_OWNER_FIELD &
765 // ID3_DESCRIPTION_FIELD field classes is the owner
766 // field is always 8859-1 encoded (single NULL term)
767 thisField->ID3v2_Field_Type = fieldType;
768 thisField->field_length = findstringNULLterm(buffer, 0, maxFieldLen);
769 thisField->field_string =
770 (char *)calloc(1,
771 sizeof(char) * thisField->field_length + 1 > 16
772 ? thisField->field_length + 1
773 : 16);
774 memcpy(thisField->field_string, buffer, thisField->field_length);
775 thisField->alloc_length =
776 (sizeof(char) * maxFieldLen + 1 > 16 ? maxFieldLen + 1 : 16);
777
778 bytes_used = thisField->field_length;
779 break;
780 }
781 case ID3_FILENAME_FIELD:
782 case ID3_DESCRIPTION_FIELD: {
783 thisField->ID3v2_Field_Type = fieldType;
784 thisField->field_length =
785 findstringNULLterm(buffer, textEncoding, maxFieldLen);
786 thisField->field_string =
787 (char *)calloc(1,
788 sizeof(char) * thisField->field_length + 1 > 16
789 ? thisField->field_length + 1
790 : 16);
791 memcpy(thisField->field_string, buffer, thisField->field_length);
792 thisField->alloc_length = (sizeof(char) * thisField->field_length + 1 > 16
793 ? thisField->field_length + 1
794 : 16);
795
796 bytes_used = thisField->field_length;
797 break;
798 }
799 }
800 // fprintf(stdout, "%" PRIu32 ", %s, %s\n", bytes_used, buffer,
801 // (thisFrame->ID3v2_Frame_Fields+fieldNum)->field_string);
802 return bytes_used;
803 }
804
805 void APar_ScanID3Frame(ID3v2Frame *targetframe,
806 char *frame_ptr,
807 uint32_t frameLen) {
808 uint64_t offset_into_frame = 0;
809
810 switch (targetframe->ID3v2_FrameType) {
811 case ID3_UNKNOWN_FRAME: {
812 APar_ExtractField(frame_ptr,
813 frameLen,
814 targetframe,
815 targetframe->ID3v2_Frame_Fields,
816 ID3_UNKNOWN_FIELD,
817 0);
818 break;
819 }
820 case ID3_TEXT_FRAME: {
821 uint8_t textencoding = 0xFF;
822 offset_into_frame += APar_ExtractField(frame_ptr,
823 1,
824 targetframe,
825 targetframe->ID3v2_Frame_Fields,
826 ID3_TEXT_ENCODING_FIELD,
827 0);
828
829 offset_into_frame +=
830 APar_ExtractField(frame_ptr + 1,
831 frameLen - 1,
832 targetframe,
833 targetframe->ID3v2_Frame_Fields + 1,
834 ID3_TEXT_FIELD,
835 targetframe->ID3v2_Frame_Fields->field_string[0]);
836 targetframe->textfield_tally++;
837
838 if (offset_into_frame >= frameLen)
839 break;
840 textencoding = targetframe->ID3v2_Frame_Fields->field_string[0];
841
842 if (offset_into_frame < frameLen) {
843 while (true) {
844 if (offset_into_frame >= frameLen)
845 break;
846
847 // skip the required separator for multiple strings
848 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
849 offset_into_frame += 1;
850 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
851 offset_into_frame += 2;
852 }
853
854 // multiple id3v2.4 strings should be separated with a single NULL byte;
855 // some implementations might terminate the string AND use a NULL
856 // separator
857 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
858 if ((frame_ptr + offset_into_frame)[0] == 0)
859 offset_into_frame += 1;
860 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
861 if ((frame_ptr + offset_into_frame)[0] == 0 &&
862 (frame_ptr + offset_into_frame)[1] == 0)
863 offset_into_frame += 2;
864 }
865
866 // a 3rd NULL would not be good
867 if (textencoding == TE_LATIN1 || textencoding == TE_UTF8) {
868 if ((frame_ptr + offset_into_frame)[0] == 0)
869 break;
870 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
871 if ((frame_ptr + offset_into_frame)[0] == 0 &&
872 (frame_ptr + offset_into_frame)[1] == 0)
873 break;
874 }
875
876 ID3v2Fields *last_textfield = APar_FindLastTextField(targetframe);
877 if (APar_ExtraTextFieldInit(
878 last_textfield, frameLen - offset_into_frame, textencoding)) {
879 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
880 frameLen - offset_into_frame,
881 targetframe,
882 last_textfield->next_field,
883 ID3_TEXT_FIELD,
884 textencoding);
885 targetframe->textfield_tally++;
886 }
887 // copy the string to the new field
888 break;
889 }
890 }
891 break;
892 }
893 case ID3_URL_FRAME: {
894 APar_ExtractField(frame_ptr,
895 frameLen,
896 targetframe,
897 targetframe->ID3v2_Frame_Fields,
898 ID3_URL_FIELD,
899 0);
900 break;
901 }
902 case ID3_TEXT_FRAME_USERDEF:
903 case ID3_URL_FRAME_USERDEF: {
904 offset_into_frame += APar_ExtractField(frame_ptr,
905 1,
906 targetframe,
907 targetframe->ID3v2_Frame_Fields,
908 ID3_TEXT_ENCODING_FIELD,
909 0);
910
911 offset_into_frame +=
912 APar_ExtractField(frame_ptr + offset_into_frame,
913 frameLen - offset_into_frame,
914 targetframe,
915 targetframe->ID3v2_Frame_Fields + 1,
916 ID3_DESCRIPTION_FIELD,
917 targetframe->ID3v2_Frame_Fields->field_string[0]);
918
919 offset_into_frame +=
920 skipNULLterm(frame_ptr + offset_into_frame,
921 targetframe->ID3v2_Frame_Fields->field_string[0],
922 frameLen - offset_into_frame);
923
924 if (targetframe->ID3v2_FrameType == ID3_TEXT_FRAME_USERDEF) {
925 APar_ExtractField(frame_ptr + offset_into_frame,
926 frameLen - offset_into_frame,
927 targetframe,
928 targetframe->ID3v2_Frame_Fields + 2,
929 ID3_TEXT_FIELD,
930 targetframe->ID3v2_Frame_Fields->field_string[0]);
931 } else if (targetframe->ID3v2_FrameType == ID3_URL_FRAME_USERDEF) {
932 APar_ExtractField(frame_ptr + offset_into_frame,
933 frameLen - offset_into_frame,
934 targetframe,
935 targetframe->ID3v2_Frame_Fields + 2,
936 ID3_URL_FIELD,
937 targetframe->ID3v2_Frame_Fields->field_string[0]);
938 }
939 break;
940 }
941 case ID3_UNIQUE_FILE_ID_FRAME: {
942 offset_into_frame += APar_ExtractField(frame_ptr,
943 frameLen,
944 targetframe,
945 targetframe->ID3v2_Frame_Fields,
946 ID3_OWNER_FIELD,
947 0);
948 offset_into_frame++; // iso-8859-1 owner field is NULL terminated
949
950 APar_ExtractField(frame_ptr + offset_into_frame,
951 frameLen - offset_into_frame,
952 targetframe,
953 targetframe->ID3v2_Frame_Fields + 1,
954 ID3_BINARY_DATA_FIELD,
955 0);
956 break;
957 }
958 case ID3_CD_ID_FRAME: {
959 APar_ExtractField(frame_ptr,
960 frameLen,
961 targetframe,
962 targetframe->ID3v2_Frame_Fields,
963 ID3_BINARY_DATA_FIELD,
964 0);
965 break;
966 }
967 case ID3_DESCRIBED_TEXT_FRAME: {
968 offset_into_frame += APar_ExtractField(frame_ptr,
969 1,
970 targetframe,
971 targetframe->ID3v2_Frame_Fields,
972 ID3_TEXT_ENCODING_FIELD,
973 0);
974
975 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
976 3,
977 targetframe,
978 targetframe->ID3v2_Frame_Fields + 1,
979 ID3_LANGUAGE_FIELD,
980 0);
981
982 offset_into_frame +=
983 APar_ExtractField(frame_ptr + offset_into_frame,
984 frameLen - offset_into_frame,
985 targetframe,
986 targetframe->ID3v2_Frame_Fields + 2,
987 ID3_DESCRIPTION_FIELD,
988 targetframe->ID3v2_Frame_Fields->field_string[0]);
989
990 offset_into_frame +=
991 skipNULLterm(frame_ptr + offset_into_frame,
992 targetframe->ID3v2_Frame_Fields->field_string[0],
993 frameLen - offset_into_frame);
994
995 if (frameLen > offset_into_frame) {
996 APar_ExtractField(frame_ptr + offset_into_frame,
997 frameLen - offset_into_frame,
998 targetframe,
999 targetframe->ID3v2_Frame_Fields + 3,
1000 ID3_TEXT_FIELD,
1001 targetframe->ID3v2_Frame_Fields->field_string[0]);
1002 }
1003 break;
1004 }
1005 case ID3_ATTACHED_OBJECT_FRAME:
1006 case ID3_ATTACHED_PICTURE_FRAME: {
1007 offset_into_frame += APar_ExtractField(frame_ptr,
1008 1,
1009 targetframe,
1010 targetframe->ID3v2_Frame_Fields,
1011 ID3_TEXT_ENCODING_FIELD,
1012 0);
1013
1014 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
1015 frameLen - 1,
1016 targetframe,
1017 targetframe->ID3v2_Frame_Fields + 1,
1018 ID3_MIME_TYPE_FIELD,
1019 0);
1020
1021 offset_into_frame += 1; // should only be 1 NULL
1022
1023 if (targetframe->ID3v2_FrameType == ID3_ATTACHED_PICTURE_FRAME) {
1024 offset_into_frame +=
1025 APar_ExtractField(frame_ptr + offset_into_frame,
1026 1,
1027 targetframe,
1028 targetframe->ID3v2_Frame_Fields + 2,
1029 ID3_PIC_TYPE_FIELD,
1030 0);
1031 } else {
1032 offset_into_frame +=
1033 APar_ExtractField(frame_ptr + offset_into_frame,
1034 frameLen - offset_into_frame,
1035 targetframe,
1036 targetframe->ID3v2_Frame_Fields + 2,
1037 ID3_FILENAME_FIELD,
1038 0);
1039
1040 offset_into_frame +=
1041 skipNULLterm(frame_ptr + offset_into_frame,
1042 targetframe->ID3v2_Frame_Fields->field_string[0],
1043 frameLen - offset_into_frame);
1044 }
1045
1046 offset_into_frame +=
1047 APar_ExtractField(frame_ptr + offset_into_frame,
1048 frameLen - offset_into_frame,
1049 targetframe,
1050 targetframe->ID3v2_Frame_Fields + 3,
1051 ID3_DESCRIPTION_FIELD,
1052 targetframe->ID3v2_Frame_Fields->field_string[0]);
1053
1054 offset_into_frame +=
1055 skipNULLterm(frame_ptr + offset_into_frame,
1056 targetframe->ID3v2_Frame_Fields->field_string[0],
1057 frameLen - offset_into_frame);
1058
1059 if (frameLen > offset_into_frame) {
1060 offset_into_frame +=
1061 APar_ExtractField(frame_ptr + offset_into_frame,
1062 frameLen - offset_into_frame,
1063 targetframe,
1064 targetframe->ID3v2_Frame_Fields + 4,
1065 ID3_BINARY_DATA_FIELD,
1066 0);
1067 }
1068 break;
1069 }
1070 case ID3_PRIVATE_FRAME: { // the only difference between the 'priv' frame &
1071 // the 'ufid' frame is ufid is limited to 64 bytes
1072 offset_into_frame += APar_ExtractField(frame_ptr,
1073 frameLen,
1074 targetframe,
1075 targetframe->ID3v2_Frame_Fields,
1076 ID3_OWNER_FIELD,
1077 0);
1078 offset_into_frame++; // iso-8859-1 owner field is NULL terminated
1079
1080 APar_ExtractField(frame_ptr + offset_into_frame,
1081 frameLen - 1,
1082 targetframe,
1083 targetframe->ID3v2_Frame_Fields + 1,
1084 ID3_BINARY_DATA_FIELD,
1085 0);
1086 break;
1087 }
1088 case ID3_GROUP_ID_FRAME: {
1089 offset_into_frame += APar_ExtractField(frame_ptr,
1090 frameLen,
1091 targetframe,
1092 targetframe->ID3v2_Frame_Fields,
1093 ID3_OWNER_FIELD,
1094 0);
1095 offset_into_frame++; // iso-8859-1 owner field is NULL terminated
1096
1097 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
1098 1,
1099 targetframe,
1100 targetframe->ID3v2_Frame_Fields + 1,
1101 ID3_GROUPSYMBOL_FIELD,
1102 0);
1103
1104 if (frameLen > offset_into_frame) {
1105 APar_ExtractField(frame_ptr + offset_into_frame,
1106 frameLen - offset_into_frame,
1107 targetframe,
1108 targetframe->ID3v2_Frame_Fields + 2,
1109 ID3_BINARY_DATA_FIELD,
1110 0);
1111 }
1112 break;
1113 }
1114 case ID3_SIGNATURE_FRAME: {
1115 APar_ExtractField(frame_ptr,
1116 1,
1117 targetframe,
1118 targetframe->ID3v2_Frame_Fields,
1119 ID3_GROUPSYMBOL_FIELD,
1120 0);
1121
1122 APar_ExtractField(frame_ptr + 1,
1123 frameLen - 1,
1124 targetframe,
1125 targetframe->ID3v2_Frame_Fields + 1,
1126 ID3_BINARY_DATA_FIELD,
1127 0);
1128 break;
1129 }
1130 case ID3_PLAYCOUNTER_FRAME: {
1131 APar_ExtractField(frame_ptr,
1132 frameLen,
1133 targetframe,
1134 targetframe->ID3v2_Frame_Fields,
1135 ID3_COUNTER_FIELD,
1136 0);
1137 break;
1138 }
1139 case ID3_POPULAR_FRAME: {
1140 offset_into_frame +=
1141 APar_ExtractField(frame_ptr,
1142 frameLen,
1143 targetframe,
1144 targetframe->ID3v2_Frame_Fields,
1145 ID3_OWNER_FIELD,
1146 0); // surrogate for 'emai to user' field
1147 offset_into_frame++; // iso-8859-1 email address field is NULL terminated
1148
1149 offset_into_frame += APar_ExtractField(frame_ptr + offset_into_frame,
1150 1,
1151 targetframe,
1152 targetframe->ID3v2_Frame_Fields + 1,
1153 ID3_BINARY_DATA_FIELD,
1154 0);
1155
1156 if (frameLen > offset_into_frame) {
1157 APar_ExtractField(frame_ptr,
1158 frameLen - offset_into_frame,
1159 targetframe,
1160 targetframe->ID3v2_Frame_Fields + 2,
1161 ID3_COUNTER_FIELD,
1162 0);
1163 }
1164 break;
1165 }
1166 case ID3_OLD_V2P2_PICTURE_FRAME: {
1167 break; // unimplemented
1168 }
1169 }
1170 return;
1171 }
1172
1173 void APar_ID32_ScanID3Tag(FILE *source_file, AtomicInfo *id32_atom) {
1174 char *id32_fulltag =
1175 (char *)calloc(1, sizeof(char) * id32_atom->AtomicLength);
1176 char *fulltag_ptr = id32_fulltag;
1177
1178 if (id32_atom->AtomicLength < 20)
1179 return;
1180 APar_readX(id32_fulltag,
1181 source_file,
1182 id32_atom->AtomicStart + 14,
1183 id32_atom->AtomicLength -
1184 14); //+10 = 4bytes ID32 atom length + 4bytes ID32 atom name +
1185 // 2 bytes packed lang
1186
1187 if (memcmp(id32_fulltag, "ID3", 3) != 0)
1188 return;
1189 fulltag_ptr += 3;
1190
1191 id32_atom->ID32_TagInfo = (ID3v2Tag *)calloc(1, sizeof(ID3v2Tag));
1192 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion = *fulltag_ptr;
1193 fulltag_ptr++;
1194 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion = *fulltag_ptr;
1195 fulltag_ptr++;
1196 id32_atom->ID32_TagInfo->ID3v2Tag_Flags = *fulltag_ptr;
1197 fulltag_ptr++;
1198
1199 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4) {
1200 fprintf(stdout,
1201 "AtomicParsley warning: an ID32 atom was encountered using an "
1202 "unsupported ID3v2 tag version: %u. Skipping\n",
1203 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
1204 return;
1205 }
1206 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4 &&
1207 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion != 0) {
1208 fprintf(stdout,
1209 "AtomicParsley warning: an ID32 atom was encountered using an "
1210 "unsupported ID3v2.4 tag revision: %u. Skipping\n",
1211 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion);
1212 return;
1213 }
1214
1215 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1216 ID32_TAGFLAG_BIT0 + ID32_TAGFLAG_BIT1 +
1217 ID32_TAGFLAG_BIT2 + ID32_TAGFLAG_BIT3))
1218 return;
1219
1220 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1221 ID32_TAGFLAG_FOOTER)) {
1222 fprintf(stdout,
1223 "AtomicParsley error: an ID32 atom was encountered with a "
1224 "forbidden footer flag. Exiting.\n");
1225 free(id32_fulltag);
1226 id32_fulltag = NULL;
1227 return;
1228 }
1229
1230 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1231 ID32_TAGFLAG_EXPERIMENTAL)) {
8661232 #if defined(DEBUG_V)
867 fprintf(stdout, "AtomicParsley warning: an ID32 atom was encountered with an experimental flag set.\n");
1233 fprintf(stdout,
1234 "AtomicParsley warning: an ID32 atom was encountered with "
1235 "an experimental flag set.\n");
8681236 #endif
869 }
870
871 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
872 id32_atom->ID32_TagInfo->ID3v2Tag_Length = syncsafe32_to_UInt32(fulltag_ptr);
873 fulltag_ptr+=4;
874 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
875 id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt32FromBigEndian(fulltag_ptr); //TODO: when testing ends, this switches to syncsafe
876 fulltag_ptr+=4;
877 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
878 id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt24FromBigEndian(fulltag_ptr);
879 fulltag_ptr+=3;
880 }
881
882 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_UNSYNCRONIZATION)) {
883 //uint32_t newtagsize = ID3v2_desynchronize(id32_fulltag, id32_atom->ID32_TagInfo->ID3v2Tag_Length);
884 //fprintf(stdout, "New tag size is %" PRIu32 "\n", newtagsize);
885 //WriteZlibData(id32_fulltag, newtagsize);
886 //exit(0);
887 fprintf(stdout, "AtomicParsley error: an ID3 tag with the unsynchronized flag set which is not supported. Skipping.\n");
888 free(id32_fulltag);
889 id32_fulltag = NULL;
890 return;
891 }
892
893 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_EXTENDEDHEADER)) {
894 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
895 id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = syncsafe32_to_UInt32(fulltag_ptr);
896 } else {
897 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
898 }
899 fulltag_ptr+= id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length;
900 }
901
902 id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL;
903 id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL;
904
905 //loop through parsing frames
906 while (fulltag_ptr < id32_fulltag + (id32_atom->AtomicLength-14) ) {
907 uint32_t fullframesize = 0;
908
909 if (ID3v2_PaddingTest(fulltag_ptr)) break;
910 if (ID3v2_TestFrameID_NonConformance(fulltag_ptr)) break;
911
912 ID3v2Frame* target_list_frameinfo = (ID3v2Frame*)calloc(1, sizeof(ID3v2Frame));
913 target_list_frameinfo->ID3v2_NextFrame = NULL;
914 target_list_frameinfo->ID3v2_Frame_ID = MatchID3FrameIDstr(fulltag_ptr, id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
915 target_list_frameinfo->ID3v2_FrameType = KnownFrames[target_list_frameinfo->ID3v2_Frame_ID+1].ID3v2_FrameType;
916
917 uint8_t FrameCompositionList = GetFrameCompositionDescription(target_list_frameinfo->ID3v2_FrameType);
918 target_list_frameinfo->ID3v2_FieldCount = FrameTypeConstructionList[FrameCompositionList].ID3_FieldCount;
919 target_list_frameinfo->ID3v2_Frame_ExpandedLength = 0;
920 target_list_frameinfo->textfield_tally = 0;
921 target_list_frameinfo->eliminate_frame = false;
922 uint8_t frame_offset = 0;
923
924 if (id32_atom->ID32_TagInfo->ID3v2_FrameList != NULL) id32_atom->ID32_TagInfo->ID3v2_FrameList->ID3v2_NextFrame = target_list_frameinfo;
925
926 //need to lookup how many components this Frame_ID is associated with. Do this by using the corresponding KnownFrames.ID3v2_FrameType
927 //ID3v2_FrameType describes the general form this frame takes (text, text with description, attached object, attached picture)
928 //the general form is composed of several fields; that number of fields needs to be malloced to target_list_frameinfo->ID3v2_Frame_Fields
929 //and each target_list_frameinfo->ID3v2_Frame_Fields+num->field_string needs to be malloced and copied from id32_fulltag
930
931 memset(target_list_frameinfo->ID3v2_Frame_Namestr, 0, 5);
932 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
933 memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 3);
934 fulltag_ptr+= 3;
935 } else {
936 memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 4);
937 fulltag_ptr+= 4;
938 }
939
940 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
941 target_list_frameinfo->ID3v2_Frame_Length = syncsafe32_to_UInt32(fulltag_ptr);
942 fulltag_ptr+=4;
943 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
944 target_list_frameinfo->ID3v2_Frame_Length = UInt32FromBigEndian(fulltag_ptr); //TODO: when testing ends, this switches to syncsafe
945 fulltag_ptr+=4;
946 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
947 target_list_frameinfo->ID3v2_Frame_Length = UInt24FromBigEndian(fulltag_ptr);
948 fulltag_ptr+=3;
949 }
950
951 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion >= 3) {
952 target_list_frameinfo->ID3v2_Frame_Flags = UInt16FromBigEndian(fulltag_ptr); //v2.2 doesn't have frame level flags (but it does have field level flags)
953 fulltag_ptr+=2;
954
955 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_UNSYNCED)) {
956 //DE-UNSYNC frame
957 fullframesize = target_list_frameinfo->ID3v2_Frame_Length;
958 target_list_frameinfo->ID3v2_Frame_Length = ID3v2_desynchronize(fulltag_ptr+frame_offset, target_list_frameinfo->ID3v2_Frame_Length);
959 target_list_frameinfo->ID3v2_Frame_Flags -= ID32_FRAMEFLAG_UNSYNCED;
960 }
961
962 //info based on frame flags (order based on the order of flags defined by the frame flags
963 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_GROUPING)) {
1237 }
1238
1239 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1240 id32_atom->ID32_TagInfo->ID3v2Tag_Length =
1241 syncsafe32_to_UInt32(fulltag_ptr);
1242 fulltag_ptr += 4;
1243 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
1244 id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt32FromBigEndian(
1245 fulltag_ptr); // TODO: when testing ends, this switches to syncsafe
1246 fulltag_ptr += 4;
1247 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1248 id32_atom->ID32_TagInfo->ID3v2Tag_Length = UInt24FromBigEndian(fulltag_ptr);
1249 fulltag_ptr += 3;
1250 }
1251
1252 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1253 ID32_TAGFLAG_UNSYNCRONIZATION)) {
1254 // uint32_t newtagsize = ID3v2_desynchronize(id32_fulltag,
1255 // id32_atom->ID32_TagInfo->ID3v2Tag_Length); fprintf(stdout, "New tag size
1256 // is %" PRIu32 "\n", newtagsize); WriteZlibData(id32_fulltag, newtagsize);
1257 // exit(0);
1258 fprintf(stdout,
1259 "AtomicParsley error: an ID3 tag with the unsynchronized "
1260 "flag set which is not supported. Skipping.\n");
1261 free(id32_fulltag);
1262 id32_fulltag = NULL;
1263 return;
1264 }
1265
1266 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1267 ID32_TAGFLAG_EXTENDEDHEADER)) {
1268 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1269 id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length =
1270 syncsafe32_to_UInt32(fulltag_ptr);
1271 } else {
1272 id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length =
1273 UInt32FromBigEndian(
1274 fulltag_ptr); // TODO: when testing ends, this switches to
1275 // syncsafe; 2.2 doesn't have it
1276 }
1277 fulltag_ptr += id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length;
1278 }
1279
1280 id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL;
1281 id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL;
1282
1283 // loop through parsing frames
1284 while (fulltag_ptr < id32_fulltag + (id32_atom->AtomicLength - 14)) {
1285 uint32_t fullframesize = 0;
1286
1287 if (ID3v2_PaddingTest(fulltag_ptr))
1288 break;
1289 if (ID3v2_TestFrameID_NonConformance(fulltag_ptr))
1290 break;
1291
1292 ID3v2Frame *target_list_frameinfo =
1293 (ID3v2Frame *)calloc(1, sizeof(ID3v2Frame));
1294 target_list_frameinfo->ID3v2_NextFrame = NULL;
1295 target_list_frameinfo->ID3v2_Frame_ID = MatchID3FrameIDstr(
1296 fulltag_ptr, id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
1297 target_list_frameinfo->ID3v2_FrameType =
1298 KnownFrames[target_list_frameinfo->ID3v2_Frame_ID + 1].ID3v2_FrameType;
1299
1300 uint8_t FrameCompositionList =
1301 GetFrameCompositionDescription(target_list_frameinfo->ID3v2_FrameType);
1302 target_list_frameinfo->ID3v2_FieldCount =
1303 FrameTypeConstructionList[FrameCompositionList].ID3_FieldCount;
1304 target_list_frameinfo->ID3v2_Frame_ExpandedLength = 0;
1305 target_list_frameinfo->textfield_tally = 0;
1306 target_list_frameinfo->eliminate_frame = false;
1307 uint8_t frame_offset = 0;
1308
1309 if (id32_atom->ID32_TagInfo->ID3v2_FrameList != NULL)
1310 id32_atom->ID32_TagInfo->ID3v2_FrameList->ID3v2_NextFrame =
1311 target_list_frameinfo;
1312
1313 // need to lookup how many components this Frame_ID is associated with. Do
1314 // this by using the corresponding KnownFrames.ID3v2_FrameType
1315 // ID3v2_FrameType describes the general form this frame takes (text, text
1316 // with description, attached object, attached picture) the general form is
1317 // composed of several fields; that number of fields needs to be malloced to
1318 // target_list_frameinfo->ID3v2_Frame_Fields and each
1319 // target_list_frameinfo->ID3v2_Frame_Fields+num->field_string needs to be
1320 // malloced and copied from id32_fulltag
1321
1322 memset(target_list_frameinfo->ID3v2_Frame_Namestr, 0, 5);
1323 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1324 memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 3);
1325 fulltag_ptr += 3;
1326 } else {
1327 memcpy(target_list_frameinfo->ID3v2_Frame_Namestr, fulltag_ptr, 4);
1328 fulltag_ptr += 4;
1329 }
1330
1331 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1332 target_list_frameinfo->ID3v2_Frame_Length =
1333 syncsafe32_to_UInt32(fulltag_ptr);
1334 fulltag_ptr += 4;
1335 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
1336 target_list_frameinfo->ID3v2_Frame_Length = UInt32FromBigEndian(
1337 fulltag_ptr); // TODO: when testing ends, this switches to syncsafe
1338 fulltag_ptr += 4;
1339 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1340 target_list_frameinfo->ID3v2_Frame_Length =
1341 UInt24FromBigEndian(fulltag_ptr);
1342 fulltag_ptr += 3;
1343 }
1344
1345 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion >= 3) {
1346 target_list_frameinfo->ID3v2_Frame_Flags = UInt16FromBigEndian(
1347 fulltag_ptr); // v2.2 doesn't have frame level flags (but it does have
1348 // field level flags)
1349 fulltag_ptr += 2;
1350
1351 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags,
1352 ID32_FRAMEFLAG_UNSYNCED)) {
1353 // DE-UNSYNC frame
1354 fullframesize = target_list_frameinfo->ID3v2_Frame_Length;
1355 target_list_frameinfo->ID3v2_Frame_Length =
1356 ID3v2_desynchronize(fulltag_ptr + frame_offset,
1357 target_list_frameinfo->ID3v2_Frame_Length);
1358 target_list_frameinfo->ID3v2_Frame_Flags -= ID32_FRAMEFLAG_UNSYNCED;
1359 }
1360
1361 // info based on frame flags (order based on the order of flags defined by
1362 // the frame flags
1363 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags,
1364 ID32_FRAMEFLAG_GROUPING)) {
9641365 #if defined(DEBUG_V)
965 fprintf(stdout, "Frame %s has a grouping flag set\n", target_list_frameinfo->ID3v2_Frame_Namestr);
1366 fprintf(stdout,
1367 "Frame %s has a grouping flag set\n",
1368 target_list_frameinfo->ID3v2_Frame_Namestr);
9661369 #endif
967 target_list_frameinfo->ID3v2_Frame_GroupingSymbol = *fulltag_ptr; //er, uh... wouldn't this also require ID32_FRAMEFLAG_LENINDICATED to be set???
968 frame_offset++;
969 }
970
971 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED)) { // technically ID32_FRAMEFLAG_LENINDICATED should also be tested
1370 target_list_frameinfo->ID3v2_Frame_GroupingSymbol =
1371 *fulltag_ptr; // er, uh... wouldn't this also require
1372 // ID32_FRAMEFLAG_LENINDICATED to be set???
1373 frame_offset++;
1374 }
1375
1376 if (ID3v2_TestFrameFlag(
1377 target_list_frameinfo->ID3v2_Frame_Flags,
1378 ID32_FRAMEFLAG_COMPRESSED)) { // technically
1379 // ID32_FRAMEFLAG_LENINDICATED
1380 // should also be tested
9721381 #if defined(DEBUG_V)
973 fprintf(stdout, "Frame %s has a compressed flag set\n", target_list_frameinfo->ID3v2_Frame_Namestr);
1382 fprintf(stdout,
1383 "Frame %s has a compressed flag set\n",
1384 target_list_frameinfo->ID3v2_Frame_Namestr);
9741385 #endif
975 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
976 target_list_frameinfo->ID3v2_Frame_ExpandedLength = syncsafe32_to_UInt32(fulltag_ptr+frame_offset);
977 frame_offset+=4;
978 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
979 target_list_frameinfo->ID3v2_Frame_ExpandedLength = UInt32FromBigEndian(fulltag_ptr+frame_offset); //TODO: when testing ends, switch this to syncsafe
980 frame_offset+=4;
981 }
982 }
983 }
984
985
986 target_list_frameinfo->ID3v2_Frame_Fields = (ID3v2Fields*)calloc(1, sizeof(ID3v2Fields)*target_list_frameinfo->ID3v2_FieldCount);
987 char* expanded_frame = NULL;
988 char* frame_ptr = NULL;
989 uint32_t frameLen = 0;
990
991 if (target_list_frameinfo->ID3v2_Frame_ExpandedLength != 0) {
1386 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1387 target_list_frameinfo->ID3v2_Frame_ExpandedLength =
1388 syncsafe32_to_UInt32(fulltag_ptr + frame_offset);
1389 frame_offset += 4;
1390 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3) {
1391 target_list_frameinfo
1392 ->ID3v2_Frame_ExpandedLength = UInt32FromBigEndian(
1393 fulltag_ptr +
1394 frame_offset); // TODO: when testing ends, switch this to syncsafe
1395 frame_offset += 4;
1396 }
1397 }
1398 }
1399
1400 target_list_frameinfo->ID3v2_Frame_Fields = (ID3v2Fields *)calloc(
1401 1, sizeof(ID3v2Fields) * target_list_frameinfo->ID3v2_FieldCount);
1402 char *expanded_frame = NULL;
1403 char *frame_ptr = NULL;
1404 uint32_t frameLen = 0;
1405
1406 if (target_list_frameinfo->ID3v2_Frame_ExpandedLength != 0) {
9921407 #ifdef HAVE_ZLIB_H
993 expanded_frame = (char*)calloc(1, sizeof(char)*target_list_frameinfo->ID3v2_Frame_ExpandedLength + 1);
994 APar_zlib_inflate(fulltag_ptr+frame_offset, target_list_frameinfo->ID3v2_Frame_Length, expanded_frame, target_list_frameinfo->ID3v2_Frame_ExpandedLength);
995
996 WriteZlibData(expanded_frame, target_list_frameinfo->ID3v2_Frame_ExpandedLength);
997
998 frame_ptr = expanded_frame;
999 frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength;
1408 expanded_frame = (char *)calloc(
1409 1,
1410 sizeof(char) * target_list_frameinfo->ID3v2_Frame_ExpandedLength + 1);
1411 APar_zlib_inflate(fulltag_ptr + frame_offset,
1412 target_list_frameinfo->ID3v2_Frame_Length,
1413 expanded_frame,
1414 target_list_frameinfo->ID3v2_Frame_ExpandedLength);
1415
1416 WriteZlibData(expanded_frame,
1417 target_list_frameinfo->ID3v2_Frame_ExpandedLength);
1418
1419 frame_ptr = expanded_frame;
1420 frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength;
10001421 #else
1001 target_list_frameinfo->ID3v2_FrameType = ID3_UNKNOWN_FRAME;
1002 frame_ptr = fulltag_ptr+frame_offset;
1003 frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength;
1422 target_list_frameinfo->ID3v2_FrameType = ID3_UNKNOWN_FRAME;
1423 frame_ptr = fulltag_ptr + frame_offset;
1424 frameLen = target_list_frameinfo->ID3v2_Frame_ExpandedLength;
10041425 #endif
1005 } else {
1006
1007 frame_ptr = fulltag_ptr+frame_offset;
1008 frameLen = target_list_frameinfo->ID3v2_Frame_Length;
1009 }
1010
1011 APar_ScanID3Frame(target_list_frameinfo, frame_ptr, frameLen);
1012
1013 if (expanded_frame != NULL) {
1014 free(expanded_frame);
1015 expanded_frame = NULL;
1016 }
1017
1018 if (target_list_frameinfo != NULL) {
1019 if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0) {
1020 id32_atom->ID32_TagInfo->ID3v2_FirstFrame = target_list_frameinfo; //entrance to the linked list
1021 }
1022 id32_atom->ID32_TagInfo->ID3v2_FrameList = target_list_frameinfo; //this always points to the last frame that had the scan completed
1023 }
1024
1025 if (fullframesize != 0) {
1026 fulltag_ptr+= fullframesize;
1027 } else {
1028 fulltag_ptr+= target_list_frameinfo->ID3v2_Frame_Length;
1029 }
1030 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_GROUPING)) {
1031 fulltag_ptr++;
1032 }
1033 id32_atom->ID32_TagInfo->ID3v2_FrameCount++;
1034 }
1035
1036 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
1037 return;
1426 } else {
1427
1428 frame_ptr = fulltag_ptr + frame_offset;
1429 frameLen = target_list_frameinfo->ID3v2_Frame_Length;
1430 }
1431
1432 APar_ScanID3Frame(target_list_frameinfo, frame_ptr, frameLen);
1433
1434 if (expanded_frame != NULL) {
1435 free(expanded_frame);
1436 expanded_frame = NULL;
1437 }
1438
1439 if (target_list_frameinfo != NULL) {
1440 if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0) {
1441 id32_atom->ID32_TagInfo->ID3v2_FirstFrame =
1442 target_list_frameinfo; // entrance to the linked list
1443 }
1444 id32_atom->ID32_TagInfo->ID3v2_FrameList =
1445 target_list_frameinfo; // this always points to the last frame that
1446 // had the scan completed
1447 }
1448
1449 if (fullframesize != 0) {
1450 fulltag_ptr += fullframesize;
1451 } else {
1452 fulltag_ptr += target_list_frameinfo->ID3v2_Frame_Length;
1453 }
1454 if (ID3v2_TestFrameFlag(target_list_frameinfo->ID3v2_Frame_Flags,
1455 ID32_FRAMEFLAG_GROUPING)) {
1456 fulltag_ptr++;
1457 }
1458 id32_atom->ID32_TagInfo->ID3v2_FrameCount++;
1459 }
1460
1461 id32_atom->ID32_TagInfo->modified_tag =
1462 false; // if a frame is altered/added/removed, change this to true and
1463 // render the tag & fill id32_atom-AtomicData with the tag
1464 return;
10381465 }
10391466
10401467 ///////////////////////////////////////////////////////////////////////////////////////
1041 // id3 rendering functions //
1468 // id3 rendering functions //
10421469 ///////////////////////////////////////////////////////////////////////////////////////
10431470
1044 bool APar_LocateFrameSymbol(AtomicInfo* id32_atom, ID3v2Frame* targetFrame, uint8_t groupsymbol) {
1045 ID3v2Frame* testFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1046 while (testFrame != NULL) {
1047 if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID && testFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) {
1048 if (testFrame->ID3v2_Frame_GroupingSymbol == groupsymbol) {
1049 return true;
1050 }
1051 } else if (targetFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) {
1052 if (testFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID && groupsymbol == (uint8_t)(testFrame->ID3v2_Frame_Fields+1)->field_string[0]) {
1053 return true;
1054 }
1055 }
1056 testFrame = testFrame->ID3v2_NextFrame;
1057 }
1058 return false;
1059 }
1060
1061 void APar_FrameFilter(AtomicInfo* id32_atom) {
1062 ID3v2Frame* MCDI_frame = NULL;
1063 ID3v2Frame* TRCK_frame = NULL;
1064 ID3v2Frame* thisFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1065 while (thisFrame != NULL) {
1066 if (!thisFrame->eliminate_frame) {
1067 if (thisFrame->ID3v2_FrameType == ID3_CD_ID_FRAME) {
1068 MCDI_frame = thisFrame;
1069 }
1070 if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_TRACKNUM) {
1071 TRCK_frame = thisFrame;
1072 }
1073 if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID) { //find any frames containing this symbol; if none are present this frame will be discarded
1074 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(id32_atom, thisFrame, (uint8_t)(thisFrame->ID3v2_Frame_Fields+1)->field_string[0]);
1075 if (!thisFrame->eliminate_frame) {
1076 thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING;
1077 }
1078
1079 } else if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_SIGNATURE) { //find a GRID frame that contains this symbol (@ field_string, not ID3v2_Frame_GroupingSymbol)
1080 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(id32_atom, thisFrame, (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0]);
1081 //since the group symbol is carried as a field for SIGN, no need to set the frame's grouping bit in the frame flags
1082
1083 } else if (thisFrame->ID3v2_Frame_GroupingSymbol > 0) { //find a GRID frame that contains this symbol, otherwise discard it
1084 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(id32_atom, thisFrame, thisFrame->ID3v2_Frame_GroupingSymbol);
1085 if (!thisFrame->eliminate_frame) {
1086 thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING;
1087 }
1088
1089 }
1090 }
1091 thisFrame = thisFrame->ID3v2_NextFrame;
1092 }
1093
1094 if (MCDI_frame != NULL && TRCK_frame == NULL) {
1095 fprintf(stderr, "AP warning: the MCDI frame was skipped due to a missing TRCK frame\n");
1096 MCDI_frame->eliminate_frame = true;
1097 }
1098 return;
1099 }
1100
1101 uint32_t APar_GetTagSize(AtomicInfo* id32_atom) { // a rough approximation of how much to malloc; this will be larger than will be ultimately required
1102 uint32_t tag_len = 0;
1103 uint16_t surviving_frame_count = 0;
1104 if (id32_atom->ID32_TagInfo->modified_tag == false) return tag_len;
1105 if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0) return tag_len; //but a frame isn't removed by AP; its just marked for elimination
1106 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
1107 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4) return tag_len; //only id3 version 2.4 tags are written
1108
1109 ID3v2Frame* eval_frame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1110 while (eval_frame != NULL) {
1111 if (eval_frame->eliminate_frame == true) {
1112 eval_frame = eval_frame->ID3v2_NextFrame;
1113 continue;
1114 }
1115 tag_len += 15; //4bytes frameID 'TCON', 4bytes frame length (syncsafe int), 2 bytes frame flags; optional group symbol: 1byte + decompressed length 4bytes
1116 tag_len += 2*eval_frame->ID3v2_FieldCount; //excess amount to ensure that text fields have utf16 BOMs & 2 byte NULL terminations as required
1117 if (ID3v2_TestFrameFlag(eval_frame->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED)) {
1118 tag_len += eval_frame->ID3v2_Frame_ExpandedLength;
1119 } else {
1120 tag_len += eval_frame->ID3v2_Frame_Length;
1121 }
1122 surviving_frame_count++;
1123 eval_frame = eval_frame->ID3v2_NextFrame;
1124 if (surviving_frame_count == 0 && eval_frame == NULL) {
1125 }
1126 }
1127 if (surviving_frame_count == 0) return 0; //the 'ID3' header alone isn't going to be written with 0 existing frames
1128 return tag_len;
1129 }
1130
1131 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) {
1132 uint8_t encoding_val = 0;
1133 if (id3v2_frame->ID3v2_Frame_Fields == NULL) {
1134 *frame_header_len = 0;
1135 *frame_length = 0;
1136 return;
1137 }
1138
1139 for (uint8_t fld_idx = 0; fld_idx < id3v2_frame->ID3v2_FieldCount; fld_idx++) {
1140 ID3v2Fields* this_field = id3v2_frame->ID3v2_Frame_Fields+fld_idx;
1141 //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);
1142 switch(this_field->ID3v2_Field_Type) {
1143
1144 //these are raw data fields of variable/fixed length and are not NULL terminated
1145 case ID3_UNKNOWN_FIELD :
1146 case ID3_PIC_TYPE_FIELD :
1147 case ID3_GROUPSYMBOL_FIELD :
1148 case ID3_TEXT_ENCODING_FIELD :
1149 case ID3_LANGUAGE_FIELD :
1150 case ID3_COUNTER_FIELD :
1151 case ID3_IMAGEFORMAT_FIELD :
1152 case ID3_URL_FIELD :
1153 case ID3_BINARY_DATA_FIELD : {
1154 APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length);
1155 if (this_field->field_string != NULL) {
1156 memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length);
1157 *frame_length += this_field->field_length;
1158 //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);
1159 }
1160 break;
1161 }
1162
1163 //these fields are subject to NULL byte termination - based on what the text encoding field says the encoding of this string is
1164 case ID3_TEXT_FIELD :
1165 case ID3_FILENAME_FIELD :
1166 case ID3_DESCRIPTION_FIELD : {
1167 if (this_field->field_string == NULL) {
1168 *frame_header_len = 0;
1169 *frame_length = 0;
1170 return;
1171 } else {
1172 APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length +2); //+2 for a possible extra NULLs
1173 encoding_val = id3v2_frame->ID3v2_Frame_Fields->field_string[0]; //ID3_TEXT_ENCODING_FIELD is always the first field, and should have an encoding
1174 if ( (id3_tag->ID3v2Tag_MajorVersion == 4 && encoding_val == TE_UTF8) || encoding_val == TE_LATIN1 ) {
1175 if (this_field->ID3v2_Field_Type != ID3_TEXT_FIELD) APar_ValidateNULLTermination8bit(this_field);
1176
1177 memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length);
1178 *frame_length += this_field->field_length;
1179
1180 } else if ((id3_tag->ID3v2Tag_MajorVersion == 4 && encoding_val == TE_UTF16LE_WITH_BOM) || encoding_val == TE_UTF16BE_NO_BOM) {
1181 APar_ValidateNULLTermination16bit(this_field, encoding_val); //TODO: shouldn't this also exclude ID3_TEXT_FIELDs?
1182
1183 memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length);
1184 *frame_length += this_field->field_length;
1185
1186 } else { //well, AP didn't set this frame, so just duplicate it.
1187 memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length);
1188 *frame_length += this_field->field_length;
1189 }
1190 }
1191 //fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n", fld_idx, this_field->ID3v2_Field_Type, *frame_length);
1192 break;
1193 }
1194
1195 //these are iso 8859-1 encoded with a single NULL terminator
1196 //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
1197 //an 'OWNE'/'COMR' price field would also be here because of single byte NULL termination
1198 case ID3_OWNER_FIELD :
1199 case ID3_MIME_TYPE_FIELD : {
1200 if (this_field->field_string == NULL) {
1201 *frame_header_len = 0;
1202 *frame_length = 0;
1203 return;
1204 } else {
1205 APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length +1); //+2 for a possible extra NULLs
1206
1207 APar_ValidateNULLTermination8bit(this_field);
1208 memcpy(dest_buffer + *frame_length, this_field->field_string, this_field->field_length);
1209 *frame_length += this_field->field_length;
1210
1211 }
1212 //fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n", fld_idx, this_field->ID3v2_Field_Type, *frame_length);
1213 break;
1214 }
1215 default : {
1216 //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);
1217 break;
1218 }
1219
1220 } //end switch
1221 }
1222 if (id3v2_frame->ID3v2_FrameType == ID3_TEXT_FRAME && id3v2_frame->textfield_tally > 1 && id3_tag->ID3v2Tag_MajorVersion == 4) {
1223 ID3v2Fields* extra_textfield = (id3v2_frame->ID3v2_Frame_Fields+1)->next_field;
1224 while (true) {
1225 if (extra_textfield == NULL) break;
1226
1227 if (encoding_val == TE_UTF8 || encoding_val == TE_LATIN1 ) {
1228 *frame_length+=1;
1229 } else if (encoding_val == TE_UTF16LE_WITH_BOM || encoding_val == TE_UTF16BE_NO_BOM) {
1230 *frame_length+=2;
1231 }
1232
1233 memcpy(dest_buffer + *frame_length, extra_textfield->field_string, extra_textfield->field_length);
1234 *frame_length += extra_textfield->field_length;
1235
1236 extra_textfield = extra_textfield->next_field;
1237 }
1238 }
1239 return;
1240 }
1241
1242 uint32_t APar_Render_ID32_Tag(AtomicInfo* id32_atom, uint32_t max_alloc) {
1243 bool contains_rendered_frames = false;
1244 APar_FrameFilter(id32_atom);
1245
1246 UInt16_TO_String2(id32_atom->AtomicLanguage, id32_atom->AtomicData); //parsedAtoms[atom_idx].AtomicLanguage
1247 uint64_t tag_offset = 2; //those first 2 bytes will hold the language
1248 uint32_t frame_length, frame_header_len; //the length in bytes this frame consumes in AtomicData as rendered
1249 uint64_t frame_length_pos, frame_compressed_length_pos;
1250
1251 memcpy(id32_atom->AtomicData + tag_offset, "ID3", 3);
1252 tag_offset+=3;
1253
1254 id32_atom->AtomicData[tag_offset] = id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion; //should be 4
1255 id32_atom->AtomicData[tag_offset+1] = id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion; //should be 0
1256 id32_atom->AtomicData[tag_offset+2] = id32_atom->ID32_TagInfo->ID3v2Tag_Flags;
1257 tag_offset+=3;
1258
1259 //unknown full length; fill in later
1260 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1261 tag_offset+= 4;
1262 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_EXTENDEDHEADER)) { //currently unimplemented
1263 tag_offset+=10;
1264 }
1265 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1266 tag_offset+= 3;
1267 }
1268
1269 id32_atom->ID32_TagInfo->ID3v2Tag_Length = tag_offset-2;
1270
1271 ID3v2Frame* thisframe = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1272 while (thisframe != NULL) {
1273 frame_header_len = 0;
1274 frame_length_pos = 0;
1275 frame_compressed_length_pos = 0;
1276
1277 if (thisframe->eliminate_frame == true) {
1278 thisframe = thisframe->ID3v2_NextFrame;
1279 continue;
1280 }
1281
1282 contains_rendered_frames = true;
1283 //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
1284 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1285 memcpy(id32_atom->AtomicData + tag_offset, thisframe->ID3v2_Frame_Namestr, 4);
1286 frame_header_len += 4;
1287
1288 //the frame length won't be determined until the end of rendering this frame fully; for now just remember where its supposed to be:
1289 frame_length_pos = tag_offset + frame_header_len;
1290 frame_header_len+=4;
1291
1292 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1293 memcpy(id32_atom->AtomicData + tag_offset, thisframe->ID3v2_Frame_Namestr, 3);
1294 frame_header_len += 3;
1295
1296 //the frame length won't be determined until the end of rendering this frame fully; for now just remember where its supposed to be:
1297 frame_length_pos = tag_offset + frame_header_len;
1298 frame_header_len+=3;
1299 }
1300
1301 //render frame flags //TODO: compression & group symbol are the only ones that can possibly be set here
1302 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 || id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1303 UInt16_TO_String2(thisframe->ID3v2_Frame_Flags, id32_atom->AtomicData + tag_offset + frame_header_len);
1304 frame_header_len+=2;
1305 }
1306
1307 //grouping flag? 1 byte; technically, its outside the header and before the fields begin
1308 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, ID32_FRAMEFLAG_GROUPING)) {
1309 id32_atom->AtomicData[tag_offset + frame_header_len] = thisframe->ID3v2_Frame_GroupingSymbol;
1310 frame_header_len++;
1311 }
1312
1313 //compression flag? 4bytes; technically, its outside the header and before the fields begin
1314 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED)) {
1315 frame_compressed_length_pos = tag_offset + frame_header_len; //fill in later; remember where it is supposed to go
1316 frame_header_len+=4;
1317 }
1318
1319 frame_length = 0;
1320 APar_RenderFields(id32_atom->AtomicData + tag_offset+frame_header_len,
1321 max_alloc-tag_offset, id32_atom->ID32_TagInfo, thisframe,
1322 &frame_header_len, &frame_length);
1323
1471 bool APar_LocateFrameSymbol(AtomicInfo *id32_atom,
1472 ID3v2Frame *targetFrame,
1473 uint8_t groupsymbol) {
1474 ID3v2Frame *testFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1475 while (testFrame != NULL) {
1476 if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID &&
1477 testFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) {
1478 if (testFrame->ID3v2_Frame_GroupingSymbol == groupsymbol) {
1479 return true;
1480 }
1481 } else if (targetFrame->ID3v2_Frame_ID != ID3v2_FRAME_GRID) {
1482 if (testFrame->ID3v2_Frame_ID == ID3v2_FRAME_GRID &&
1483 groupsymbol ==
1484 (uint8_t)(testFrame->ID3v2_Frame_Fields + 1)->field_string[0]) {
1485 return true;
1486 }
1487 }
1488 testFrame = testFrame->ID3v2_NextFrame;
1489 }
1490 return false;
1491 }
1492
1493 void APar_FrameFilter(AtomicInfo *id32_atom) {
1494 ID3v2Frame *MCDI_frame = NULL;
1495 ID3v2Frame *TRCK_frame = NULL;
1496 ID3v2Frame *thisFrame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1497 while (thisFrame != NULL) {
1498 if (!thisFrame->eliminate_frame) {
1499 if (thisFrame->ID3v2_FrameType == ID3_CD_ID_FRAME) {
1500 MCDI_frame = thisFrame;
1501 }
1502 if (thisFrame->ID3v2_Frame_ID == ID3v2_FRAME_TRACKNUM) {
1503 TRCK_frame = thisFrame;
1504 }
1505 if (thisFrame->ID3v2_Frame_ID ==
1506 ID3v2_FRAME_GRID) { // find any frames containing this symbol; if none
1507 // are present this frame will be discarded
1508 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(
1509 id32_atom,
1510 thisFrame,
1511 (uint8_t)(thisFrame->ID3v2_Frame_Fields + 1)->field_string[0]);
1512 if (!thisFrame->eliminate_frame) {
1513 thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING;
1514 }
1515
1516 } else if (thisFrame->ID3v2_Frame_ID ==
1517 ID3v2_FRAME_SIGNATURE) { // find a GRID frame that contains
1518 // this symbol (@ field_string, not
1519 // ID3v2_Frame_GroupingSymbol)
1520 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(
1521 id32_atom,
1522 thisFrame,
1523 (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0]);
1524 // since the group symbol is carried as a field for SIGN, no need to set
1525 // the frame's grouping bit in the frame flags
1526
1527 } else if (thisFrame->ID3v2_Frame_GroupingSymbol >
1528 0) { // find a GRID frame that contains this symbol, otherwise
1529 // discard it
1530 thisFrame->eliminate_frame = !APar_LocateFrameSymbol(
1531 id32_atom, thisFrame, thisFrame->ID3v2_Frame_GroupingSymbol);
1532 if (!thisFrame->eliminate_frame) {
1533 thisFrame->ID3v2_Frame_Flags |= ID32_FRAMEFLAG_GROUPING;
1534 }
1535 }
1536 }
1537 thisFrame = thisFrame->ID3v2_NextFrame;
1538 }
1539
1540 if (MCDI_frame != NULL && TRCK_frame == NULL) {
1541 fprintf(
1542 stderr,
1543 "AP warning: the MCDI frame was skipped due to a missing TRCK frame\n");
1544 MCDI_frame->eliminate_frame = true;
1545 }
1546 return;
1547 }
1548
1549 uint32_t APar_GetTagSize(
1550 AtomicInfo
1551 *id32_atom) { // a rough approximation of how much to malloc; this will
1552 // be larger than will be ultimately required
1553 uint32_t tag_len = 0;
1554 uint16_t surviving_frame_count = 0;
1555 if (id32_atom->ID32_TagInfo->modified_tag == false)
1556 return tag_len;
1557 if (id32_atom->ID32_TagInfo->ID3v2_FrameCount == 0)
1558 return tag_len; // but a frame isn't removed by AP; its just marked for
1559 // elimination
1560 if (id32_atom->ID32_TagInfo->ID3v2_FrameList == NULL)
1561 return tag_len; // something went wrong somewhere if this wasn't an entry to
1562 // a linked list of frames
1563 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion != 4)
1564 return tag_len; // only id3 version 2.4 tags are written
1565
1566 ID3v2Frame *eval_frame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1567 while (eval_frame != NULL) {
1568 if (eval_frame->eliminate_frame == true) {
1569 eval_frame = eval_frame->ID3v2_NextFrame;
1570 continue;
1571 }
1572 tag_len += 15; // 4bytes frameID 'TCON', 4bytes frame length (syncsafe int),
1573 // 2 bytes frame flags; optional group symbol: 1byte +
1574 // decompressed length 4bytes
1575 tag_len +=
1576 2 * eval_frame->ID3v2_FieldCount; // excess amount to ensure that text
1577 // fields have utf16 BOMs & 2 byte
1578 // NULL terminations as required
1579 if (ID3v2_TestFrameFlag(eval_frame->ID3v2_Frame_Flags,
1580 ID32_FRAMEFLAG_COMPRESSED)) {
1581 tag_len += eval_frame->ID3v2_Frame_ExpandedLength;
1582 } else {
1583 tag_len += eval_frame->ID3v2_Frame_Length;
1584 }
1585 surviving_frame_count++;
1586 eval_frame = eval_frame->ID3v2_NextFrame;
1587 if (surviving_frame_count == 0 && eval_frame == NULL) {
1588 }
1589 }
1590 if (surviving_frame_count == 0)
1591 return 0; // the 'ID3' header alone isn't going to be written with 0
1592 // existing frames
1593 return tag_len;
1594 }
1595
1596 void APar_RenderFields(char *dest_buffer,
1597 uint32_t max_alloc,
1598 ID3v2Tag *id3_tag,
1599 ID3v2Frame *id3v2_frame,
1600 uint32_t *frame_header_len,
1601 uint32_t *frame_length) {
1602 uint8_t encoding_val = 0;
1603 if (id3v2_frame->ID3v2_Frame_Fields == NULL) {
1604 *frame_header_len = 0;
1605 *frame_length = 0;
1606 return;
1607 }
1608
1609 for (uint8_t fld_idx = 0; fld_idx < id3v2_frame->ID3v2_FieldCount;
1610 fld_idx++) {
1611 ID3v2Fields *this_field = id3v2_frame->ID3v2_Frame_Fields + fld_idx;
1612 // fprintf(stdout, "Total Fields for %s: %u (this is %u, %u)\n",
1613 // id3v2_frame->ID3v2_Frame_Namestr, id3v2_frame->ID3v2_FieldCount, fld_idx,
1614 // this_field->ID3v2_Field_Type);
1615 switch (this_field->ID3v2_Field_Type) {
1616
1617 // these are raw data fields of variable/fixed length and are not NULL
1618 // terminated
1619 case ID3_UNKNOWN_FIELD:
1620 case ID3_PIC_TYPE_FIELD:
1621 case ID3_GROUPSYMBOL_FIELD:
1622 case ID3_TEXT_ENCODING_FIELD:
1623 case ID3_LANGUAGE_FIELD:
1624 case ID3_COUNTER_FIELD:
1625 case ID3_IMAGEFORMAT_FIELD:
1626 case ID3_URL_FIELD:
1627 case ID3_BINARY_DATA_FIELD: {
1628 APar_LimitBufferRange(max_alloc, *frame_header_len + *frame_length);
1629 if (this_field->field_string != NULL) {
1630 memcpy(dest_buffer + *frame_length,
1631 this_field->field_string,
1632 this_field->field_length);
1633 *frame_length += this_field->field_length;
1634 // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long (+%"
1635 // PRIu32 ")\n", fld_idx, this_field->ID3v2_Field_Type, *frame_length,
1636 // this_field->field_length);
1637 }
1638 break;
1639 }
1640
1641 // these fields are subject to NULL byte termination - based on what the
1642 // text encoding field says the encoding of this string is
1643 case ID3_TEXT_FIELD:
1644 case ID3_FILENAME_FIELD:
1645 case ID3_DESCRIPTION_FIELD: {
1646 if (this_field->field_string == NULL) {
1647 *frame_header_len = 0;
1648 *frame_length = 0;
1649 return;
1650 } else {
1651 APar_LimitBufferRange(max_alloc,
1652 *frame_header_len + *frame_length +
1653 2); //+2 for a possible extra NULLs
1654 encoding_val =
1655 id3v2_frame->ID3v2_Frame_Fields
1656 ->field_string[0]; // ID3_TEXT_ENCODING_FIELD is always the
1657 // first field, and should have an encoding
1658 if ((id3_tag->ID3v2Tag_MajorVersion == 4 && encoding_val == TE_UTF8) ||
1659 encoding_val == TE_LATIN1) {
1660 if (this_field->ID3v2_Field_Type != ID3_TEXT_FIELD)
1661 APar_ValidateNULLTermination8bit(this_field);
1662
1663 memcpy(dest_buffer + *frame_length,
1664 this_field->field_string,
1665 this_field->field_length);
1666 *frame_length += this_field->field_length;
1667
1668 } else if ((id3_tag->ID3v2Tag_MajorVersion == 4 &&
1669 encoding_val == TE_UTF16LE_WITH_BOM) ||
1670 encoding_val == TE_UTF16BE_NO_BOM) {
1671 APar_ValidateNULLTermination16bit(
1672 this_field, encoding_val); // TODO: shouldn't this also exclude
1673 // ID3_TEXT_FIELDs?
1674
1675 memcpy(dest_buffer + *frame_length,
1676 this_field->field_string,
1677 this_field->field_length);
1678 *frame_length += this_field->field_length;
1679
1680 } else { // well, AP didn't set this frame, so just duplicate it.
1681 memcpy(dest_buffer + *frame_length,
1682 this_field->field_string,
1683 this_field->field_length);
1684 *frame_length += this_field->field_length;
1685 }
1686 }
1687 // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n",
1688 // fld_idx, this_field->ID3v2_Field_Type, *frame_length);
1689 break;
1690 }
1691
1692 // these are iso 8859-1 encoded with a single NULL terminator
1693 // a 'LINK' url would also come here and be seperately enumerated (because
1694 // it has a terminating NULL); but in 3gp assets, external references aren't
1695 // allowed an 'OWNE'/'COMR' price field would also be here because of single
1696 // byte NULL termination
1697 case ID3_OWNER_FIELD:
1698 case ID3_MIME_TYPE_FIELD: {
1699 if (this_field->field_string == NULL) {
1700 *frame_header_len = 0;
1701 *frame_length = 0;
1702 return;
1703 } else {
1704 APar_LimitBufferRange(max_alloc,
1705 *frame_header_len + *frame_length +
1706 1); //+2 for a possible extra NULLs
1707
1708 APar_ValidateNULLTermination8bit(this_field);
1709 memcpy(dest_buffer + *frame_length,
1710 this_field->field_string,
1711 this_field->field_length);
1712 *frame_length += this_field->field_length;
1713 }
1714 // fprintf(stdout, "Field idx %u(%d) is now %" PRIu32 " bytes long\n",
1715 // fld_idx, this_field->ID3v2_Field_Type, *frame_length);
1716 break;
1717 }
1718 default: {
1719 // fprintf(stdout, "I was unable to determine the field class. I was
1720 // provided with %u (i.e. text field: %u, text encoding: %u\n",
1721 // this_field->ID3v2_Field_Type, ID3_TEXT_FIELD, ID3_TEXT_ENCODING_FIELD);
1722 break;
1723 }
1724
1725 } // end switch
1726 }
1727 if (id3v2_frame->ID3v2_FrameType == ID3_TEXT_FRAME &&
1728 id3v2_frame->textfield_tally > 1 && id3_tag->ID3v2Tag_MajorVersion == 4) {
1729 ID3v2Fields *extra_textfield =
1730 (id3v2_frame->ID3v2_Frame_Fields + 1)->next_field;
1731 while (true) {
1732 if (extra_textfield == NULL)
1733 break;
1734
1735 if (encoding_val == TE_UTF8 || encoding_val == TE_LATIN1) {
1736 *frame_length += 1;
1737 } else if (encoding_val == TE_UTF16LE_WITH_BOM ||
1738 encoding_val == TE_UTF16BE_NO_BOM) {
1739 *frame_length += 2;
1740 }
1741
1742 memcpy(dest_buffer + *frame_length,
1743 extra_textfield->field_string,
1744 extra_textfield->field_length);
1745 *frame_length += extra_textfield->field_length;
1746
1747 extra_textfield = extra_textfield->next_field;
1748 }
1749 }
1750 return;
1751 }
1752
1753 uint32_t APar_Render_ID32_Tag(AtomicInfo *id32_atom, uint32_t max_alloc) {
1754 bool contains_rendered_frames = false;
1755 APar_FrameFilter(id32_atom);
1756
1757 UInt16_TO_String2(
1758 id32_atom->AtomicLanguage,
1759 id32_atom->AtomicData); // parsedAtoms[atom_idx].AtomicLanguage
1760 uint64_t tag_offset = 2; // those first 2 bytes will hold the language
1761 uint32_t frame_length, frame_header_len; // the length in bytes this frame
1762 // consumes in AtomicData as rendered
1763 uint64_t frame_length_pos, frame_compressed_length_pos;
1764
1765 memcpy(id32_atom->AtomicData + tag_offset, "ID3", 3);
1766 tag_offset += 3;
1767
1768 id32_atom->AtomicData[tag_offset] =
1769 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion; // should be 4
1770 id32_atom->AtomicData[tag_offset + 1] =
1771 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion; // should be 0
1772 id32_atom->AtomicData[tag_offset + 2] =
1773 id32_atom->ID32_TagInfo->ID3v2Tag_Flags;
1774 tag_offset += 3;
1775
1776 // unknown full length; fill in later
1777 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 ||
1778 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1779 tag_offset += 4;
1780 if (ID3v2_TestTagFlag(
1781 id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1782 ID32_TAGFLAG_EXTENDEDHEADER)) { // currently unimplemented
1783 tag_offset += 10;
1784 }
1785 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1786 tag_offset += 3;
1787 }
1788
1789 id32_atom->ID32_TagInfo->ID3v2Tag_Length = tag_offset - 2;
1790
1791 ID3v2Frame *thisframe = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1792 while (thisframe != NULL) {
1793 frame_header_len = 0;
1794 frame_length_pos = 0;
1795 frame_compressed_length_pos = 0;
1796
1797 if (thisframe->eliminate_frame == true) {
1798 thisframe = thisframe->ID3v2_NextFrame;
1799 continue;
1800 }
1801
1802 contains_rendered_frames = true;
1803 // this won't be able to convert from 1 tag version to another because it
1804 // doesn't look up the frame id strings for the change
1805 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 ||
1806 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1807 memcpy(id32_atom->AtomicData + tag_offset,
1808 thisframe->ID3v2_Frame_Namestr,
1809 4);
1810 frame_header_len += 4;
1811
1812 // the frame length won't be determined until the end of rendering this
1813 // frame fully; for now just remember where its supposed to be:
1814 frame_length_pos = tag_offset + frame_header_len;
1815 frame_header_len += 4;
1816
1817 } else if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 2) {
1818 memcpy(id32_atom->AtomicData + tag_offset,
1819 thisframe->ID3v2_Frame_Namestr,
1820 3);
1821 frame_header_len += 3;
1822
1823 // the frame length won't be determined until the end of rendering this
1824 // frame fully; for now just remember where its supposed to be:
1825 frame_length_pos = tag_offset + frame_header_len;
1826 frame_header_len += 3;
1827 }
1828
1829 // render frame flags //TODO: compression & group symbol are the only ones
1830 // that can possibly be set here
1831 if (id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 3 ||
1832 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion == 4) {
1833 UInt16_TO_String2(thisframe->ID3v2_Frame_Flags,
1834 id32_atom->AtomicData + tag_offset + frame_header_len);
1835 frame_header_len += 2;
1836 }
1837
1838 // grouping flag? 1 byte; technically, its outside the header and before the
1839 // fields begin
1840 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags,
1841 ID32_FRAMEFLAG_GROUPING)) {
1842 id32_atom->AtomicData[tag_offset + frame_header_len] =
1843 thisframe->ID3v2_Frame_GroupingSymbol;
1844 frame_header_len++;
1845 }
1846
1847 // compression flag? 4bytes; technically, its outside the header and before
1848 // the fields begin
1849 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags,
1850 ID32_FRAMEFLAG_COMPRESSED)) {
1851 frame_compressed_length_pos =
1852 tag_offset + frame_header_len; // fill in later; remember where it is
1853 // supposed to go
1854 frame_header_len += 4;
1855 }
1856
1857 frame_length = 0;
1858 APar_RenderFields(id32_atom->AtomicData + tag_offset + frame_header_len,
1859 max_alloc - tag_offset,
1860 id32_atom->ID32_TagInfo,
1861 thisframe,
1862 &frame_header_len,
1863 &frame_length);
1864
13241865 #if defined HAVE_ZLIB_H
1325 //and now that we have rendered the frame, its time to turn to compression, if set
1326 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED) ) {
1327 uint32_t compressed_len = 0;
1328 char* compress_buffer = (char*)calloc(1, sizeof(char)*frame_length + 20);
1329
1330 compressed_len = APar_zlib_deflate(id32_atom->AtomicData + tag_offset+frame_header_len, frame_length, compress_buffer, frame_length + 20);
1331
1332 if (compressed_len > 0) {
1333 memcpy(id32_atom->AtomicData + tag_offset+frame_header_len, compress_buffer, compressed_len + 1);
1334 convert_to_syncsafe32(frame_length, id32_atom->AtomicData + frame_compressed_length_pos);
1335 frame_length = compressed_len;
1336
1337 //WriteZlibData(id32_atom->AtomicData + tag_offset+frame_header_len, compressed_len);
1338 }
1339 }
1866 // and now that we have rendered the frame, its time to turn to compression,
1867 // if set
1868 if (ID3v2_TestFrameFlag(thisframe->ID3v2_Frame_Flags,
1869 ID32_FRAMEFLAG_COMPRESSED)) {
1870 uint32_t compressed_len = 0;
1871 char *compress_buffer =
1872 (char *)calloc(1, sizeof(char) * frame_length + 20);
1873
1874 compressed_len = APar_zlib_deflate(id32_atom->AtomicData + tag_offset +
1875 frame_header_len,
1876 frame_length,
1877 compress_buffer,
1878 frame_length + 20);
1879
1880 if (compressed_len > 0) {
1881 memcpy(id32_atom->AtomicData + tag_offset + frame_header_len,
1882 compress_buffer,
1883 compressed_len + 1);
1884 convert_to_syncsafe32(
1885 frame_length, id32_atom->AtomicData + frame_compressed_length_pos);
1886 frame_length = compressed_len;
1887
1888 // WriteZlibData(id32_atom->AtomicData + tag_offset+frame_header_len,
1889 // compressed_len);
1890 }
1891 }
13401892 #endif
1341
1342 convert_to_syncsafe32(frame_length, id32_atom->AtomicData + frame_length_pos);
1343 tag_offset += frame_header_len + frame_length; //advance
1344 id32_atom->ID32_TagInfo->ID3v2Tag_Length += frame_header_len + frame_length;
1345 thisframe = thisframe->ID3v2_NextFrame;
1346
1347 }
1348 convert_to_syncsafe32(id32_atom->ID32_TagInfo->ID3v2Tag_Length - 10, id32_atom->AtomicData + 8); //-10 for a v2.4 tag with no extended header
1349
1350 if (!contains_rendered_frames) id32_atom->ID32_TagInfo->ID3v2Tag_Length = 0;
1351
1352 return id32_atom->ID32_TagInfo->ID3v2Tag_Length;
1893
1894 convert_to_syncsafe32(frame_length,
1895 id32_atom->AtomicData + frame_length_pos);
1896 tag_offset += frame_header_len + frame_length; // advance
1897 id32_atom->ID32_TagInfo->ID3v2Tag_Length += frame_header_len + frame_length;
1898 thisframe = thisframe->ID3v2_NextFrame;
1899 }
1900 convert_to_syncsafe32(id32_atom->ID32_TagInfo->ID3v2Tag_Length - 10,
1901 id32_atom->AtomicData +
1902 8); //-10 for a v2.4 tag with no extended header
1903
1904 if (!contains_rendered_frames)
1905 id32_atom->ID32_TagInfo->ID3v2Tag_Length = 0;
1906
1907 return id32_atom->ID32_TagInfo->ID3v2Tag_Length;
13531908 }
13541909
13551910 ///////////////////////////////////////////////////////////////////////////////////////
1356 // id3 initializing functions //
1911 // id3 initializing functions //
13571912 ///////////////////////////////////////////////////////////////////////////////////////
13581913
1359 void APar_FieldInit(ID3v2Frame* aFrame, uint8_t a_field, uint8_t frame_comp_list, const char* frame_payload) {
1360 uint32_t byte_allocation = 0;
1361 ID3v2Fields* this_field = NULL;
1362 int field_type = FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field];
1363
1364 switch(field_type) {
1365 //case ID3_UNKNOWN_FIELD will not be handled
1366
1367 //these are all 1 to less than 16 bytes.
1368 case ID3_GROUPSYMBOL_FIELD :
1369 case ID3_COUNTER_FIELD :
1370 case ID3_PIC_TYPE_FIELD :
1371 case ID3_LANGUAGE_FIELD :
1372 case ID3_IMAGEFORMAT_FIELD : //PIC in v2.2
1373 case ID3_TEXT_ENCODING_FIELD : {
1374 byte_allocation = 16;
1375 break;
1376 }
1377
1378 //between 16 & 100 bytes.
1379 case ID3_MIME_TYPE_FIELD : {
1380 byte_allocation = 100;
1381 break;
1382 }
1383
1384 //these are allocated with 2000 bytes
1385 case ID3_FILENAME_FIELD :
1386 case ID3_OWNER_FIELD :
1387 case ID3_DESCRIPTION_FIELD :
1388 case ID3_URL_FIELD :
1389 case ID3_TEXT_FIELD : {
1390 uint32_t string_len = strlen(frame_payload) + 1;
1391 if (string_len * 2 > 2000) {
1392 byte_allocation = string_len * 2;
1393 } else {
1394 byte_allocation = 2000;
1395 }
1396 break;
1397 }
1398
1399 case ID3_BINARY_DATA_FIELD : {
1400 if (aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_PICTURE || aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_OBJECT ) {
1401 //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
1402 } else {
1403 byte_allocation = 2000;
1404 }
1405 break;
1406 }
1407
1408 //default : {
1409 // fprintf(stdout, "I am %d\n", FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field]);
1410 // break;
1411 //}
1412 }
1413 this_field = aFrame->ID3v2_Frame_Fields + a_field;
1414 this_field->ID3v2_Field_Type = field_type;
1415 if (byte_allocation > 0) {
1416 this_field->field_string = (char*)calloc(1, sizeof(char*)*byte_allocation);
1417 if (!APar_assert((this_field->field_string != NULL), 11, aFrame->ID3v2_Frame_Namestr) ) exit(11);
1418 } else {
1419 this_field->field_string = NULL;
1420 }
1421 this_field->field_length = 0;
1422 this_field->alloc_length = byte_allocation;
1423 this_field->next_field = NULL;
1424 //fprintf(stdout, "For %u field, %" PRIu32 " bytes were allocated.\n", this_field->ID3v2_Field_Type, byte_allocation);
1425 return;
1426 }
1427
1428 void APar_FrameInit(ID3v2Frame* aFrame, const char* frame_str, int frameID, uint8_t frame_comp_list, const char* frame_payload) {
1429 aFrame->ID3v2_FieldCount = FrameTypeConstructionList[frame_comp_list].ID3_FieldCount;
1430 if (aFrame->ID3v2_FieldCount > 0) {
1431 aFrame->ID3v2_Frame_Fields = (ID3v2Fields*)calloc(1, sizeof(ID3v2Fields)*aFrame->ID3v2_FieldCount);
1432 aFrame->ID3v2_Frame_ID = frameID;
1433 aFrame->ID3v2_FrameType = FrameTypeConstructionList[frame_comp_list].ID3_FrameType;
1434 aFrame->ID3v2_Frame_ExpandedLength = 0;
1435 aFrame->ID3v2_Frame_GroupingSymbol = 0;
1436 aFrame->ID3v2_Frame_Flags = 0;
1437 aFrame->ID3v2_Frame_Length = 0;
1438 aFrame->textfield_tally = 0;
1439 aFrame->eliminate_frame = false;
1440 memcpy(aFrame->ID3v2_Frame_Namestr, frame_str, 5);
1441
1442 for (uint8_t fld = 0; fld < aFrame->ID3v2_FieldCount; fld++) {
1443 APar_FieldInit(aFrame, fld, frame_comp_list, frame_payload);
1444 }
1445
1446 //fprintf(stdout, "(%u = %d) Type %d\n", frameID, KnownFrames[frameID+1].ID3v2_InternalFrameID, aFrame->ID3v2_FrameType);
1447 }
1448 //fprintf(stdout, "Retrieved frame for '%s': %s (%u fields)\n", frame_str, KnownFrames[frameID].ID3V2p4_FrameID, aFrame->ID3v2_FieldCount);
1449 return;
1450 }
1451
1452 void APar_ID3Tag_Init(AtomicInfo* id32_atom) {
1453 id32_atom->ID32_TagInfo = (ID3v2Tag*)calloc(1, sizeof(ID3v2Tag));
1454 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion = AtomicParsley_ID3v2Tag_MajorVersion;
1455 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion = AtomicParsley_ID3v2Tag_RevisionVersion;
1456 id32_atom->ID32_TagInfo->ID3v2Tag_Flags = AtomicParsley_ID3v2Tag_Flags;
1457 id32_atom->ID32_TagInfo->ID3v2Tag_Length = 10; //this would be 9 for v2.2
1458 id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = 0;
1459 id32_atom->ID32_TagInfo->ID3v2_FrameCount = 0;
1460 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
1461
1462 id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL;
1463 id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL;
1464 return;
1465 }
1466
1467 void APar_realloc_memcpy(ID3v2Fields* thisField, uint32_t new_size) {
1468 if (new_size > thisField->alloc_length) {
1469 char* new_alloc = (char*)calloc(1, sizeof(char*)*new_size + 1);
1470 //memcpy(new_alloc, thisField->field_string, thisField->field_length);
1471 thisField->field_length = 0;
1472 free(thisField->field_string);
1473 thisField->field_string = new_alloc;
1474 thisField->alloc_length = new_size;
1475 }
1476 return;
1914 void APar_FieldInit(ID3v2Frame *aFrame,
1915 uint8_t a_field,
1916 uint8_t frame_comp_list,
1917 const char *frame_payload) {
1918 uint32_t byte_allocation = 0;
1919 ID3v2Fields *this_field = NULL;
1920 int field_type =
1921 FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field];
1922
1923 switch (field_type) {
1924 // case ID3_UNKNOWN_FIELD will not be handled
1925
1926 // these are all 1 to less than 16 bytes.
1927 case ID3_GROUPSYMBOL_FIELD:
1928 case ID3_COUNTER_FIELD:
1929 case ID3_PIC_TYPE_FIELD:
1930 case ID3_LANGUAGE_FIELD:
1931 case ID3_IMAGEFORMAT_FIELD: // PIC in v2.2
1932 case ID3_TEXT_ENCODING_FIELD: {
1933 byte_allocation = 16;
1934 break;
1935 }
1936
1937 // between 16 & 100 bytes.
1938 case ID3_MIME_TYPE_FIELD: {
1939 byte_allocation = 100;
1940 break;
1941 }
1942
1943 // these are allocated with 2000 bytes
1944 case ID3_FILENAME_FIELD:
1945 case ID3_OWNER_FIELD:
1946 case ID3_DESCRIPTION_FIELD:
1947 case ID3_URL_FIELD:
1948 case ID3_TEXT_FIELD: {
1949 uint32_t string_len = strlen(frame_payload) + 1;
1950 if (string_len * 2 > 2000) {
1951 byte_allocation = string_len * 2;
1952 } else {
1953 byte_allocation = 2000;
1954 }
1955 break;
1956 }
1957
1958 case ID3_BINARY_DATA_FIELD: {
1959 if (aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_PICTURE ||
1960 aFrame->ID3v2_Frame_ID == ID3v2_EMBEDDED_OBJECT) {
1961 // this will be left NULL because it would would probably have to be
1962 // realloced, so just do it later to the right size //byte_allocation =
1963 // findFileSize(frame_payload) + 1; //this should be limited to
1964 // max_sync_safe_uint28_t
1965 } else {
1966 byte_allocation = 2000;
1967 }
1968 break;
1969 }
1970
1971 // default : {
1972 // fprintf(stdout, "I am %d\n",
1973 // FrameTypeConstructionList[frame_comp_list].ID3_FieldComponents[a_field]);
1974 // break;
1975 //}
1976 }
1977 this_field = aFrame->ID3v2_Frame_Fields + a_field;
1978 this_field->ID3v2_Field_Type = field_type;
1979 if (byte_allocation > 0) {
1980 this_field->field_string =
1981 (char *)calloc(1, sizeof(char *) * byte_allocation);
1982 if (!APar_assert((this_field->field_string != NULL),
1983 11,
1984 aFrame->ID3v2_Frame_Namestr))
1985 exit(11);
1986 } else {
1987 this_field->field_string = NULL;
1988 }
1989 this_field->field_length = 0;
1990 this_field->alloc_length = byte_allocation;
1991 this_field->next_field = NULL;
1992 // fprintf(stdout, "For %u field, %" PRIu32 " bytes were allocated.\n",
1993 // this_field->ID3v2_Field_Type, byte_allocation);
1994 return;
1995 }
1996
1997 void APar_FrameInit(ID3v2Frame *aFrame,
1998 const char *frame_str,
1999 int frameID,
2000 uint8_t frame_comp_list,
2001 const char *frame_payload) {
2002 aFrame->ID3v2_FieldCount =
2003 FrameTypeConstructionList[frame_comp_list].ID3_FieldCount;
2004 if (aFrame->ID3v2_FieldCount > 0) {
2005 aFrame->ID3v2_Frame_Fields = (ID3v2Fields *)calloc(
2006 1, sizeof(ID3v2Fields) * aFrame->ID3v2_FieldCount);
2007 aFrame->ID3v2_Frame_ID = frameID;
2008 aFrame->ID3v2_FrameType =
2009 FrameTypeConstructionList[frame_comp_list].ID3_FrameType;
2010 aFrame->ID3v2_Frame_ExpandedLength = 0;
2011 aFrame->ID3v2_Frame_GroupingSymbol = 0;
2012 aFrame->ID3v2_Frame_Flags = 0;
2013 aFrame->ID3v2_Frame_Length = 0;
2014 aFrame->textfield_tally = 0;
2015 aFrame->eliminate_frame = false;
2016 memcpy(aFrame->ID3v2_Frame_Namestr, frame_str, 5);
2017
2018 for (uint8_t fld = 0; fld < aFrame->ID3v2_FieldCount; fld++) {
2019 APar_FieldInit(aFrame, fld, frame_comp_list, frame_payload);
2020 }
2021
2022 // fprintf(stdout, "(%u = %d) Type %d\n", frameID,
2023 // KnownFrames[frameID+1].ID3v2_InternalFrameID, aFrame->ID3v2_FrameType);
2024 }
2025 // fprintf(stdout, "Retrieved frame for '%s': %s (%u fields)\n", frame_str,
2026 // KnownFrames[frameID].ID3V2p4_FrameID, aFrame->ID3v2_FieldCount);
2027 return;
2028 }
2029
2030 void APar_ID3Tag_Init(AtomicInfo *id32_atom) {
2031 id32_atom->ID32_TagInfo = (ID3v2Tag *)calloc(1, sizeof(ID3v2Tag));
2032 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion =
2033 AtomicParsley_ID3v2Tag_MajorVersion;
2034 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion =
2035 AtomicParsley_ID3v2Tag_RevisionVersion;
2036 id32_atom->ID32_TagInfo->ID3v2Tag_Flags = AtomicParsley_ID3v2Tag_Flags;
2037 id32_atom->ID32_TagInfo->ID3v2Tag_Length = 10; // this would be 9 for v2.2
2038 id32_atom->ID32_TagInfo->ID3v2_Tag_ExtendedHeader_Length = 0;
2039 id32_atom->ID32_TagInfo->ID3v2_FrameCount = 0;
2040 id32_atom->ID32_TagInfo->modified_tag =
2041 false; // this will have to change when a frame is added/modified/removed
2042 // because this id3 header won't be written with 0 frames
2043
2044 id32_atom->ID32_TagInfo->ID3v2_FirstFrame = NULL;
2045 id32_atom->ID32_TagInfo->ID3v2_FrameList = NULL;
2046 return;
2047 }
2048
2049 void APar_realloc_memcpy(ID3v2Fields *thisField, uint32_t new_size) {
2050 if (new_size > thisField->alloc_length) {
2051 char *new_alloc = (char *)calloc(1, sizeof(char *) * new_size + 1);
2052 // memcpy(new_alloc, thisField->field_string, thisField->field_length);
2053 thisField->field_length = 0;
2054 free(thisField->field_string);
2055 thisField->field_string = new_alloc;
2056 thisField->alloc_length = new_size;
2057 }
2058 return;
14772059 }
14782060
14792061 ///////////////////////////////////////////////////////////////////////////////////////
1480 // id3 frame setting/finding functions //
2062 // id3 frame setting/finding functions //
14812063 ///////////////////////////////////////////////////////////////////////////////////////
14822064
1483 uint32_t APar_TextFieldDataPut(ID3v2Fields* thisField, const char* this_payload, uint8_t str_encoding, bool multistringtext = false) {
1484 uint32_t bytes_used = 0;
1485
1486 if (multistringtext == false) {
1487 thisField->field_length = 0;
1488 }
1489
1490 if (str_encoding == TE_UTF8) {
1491 bytes_used = strlen(this_payload); //no NULL termination is provided until render time
1492 if (bytes_used + thisField->field_length > thisField->alloc_length) {
1493 APar_realloc_memcpy(thisField, (bytes_used > 2000 ? bytes_used : 2000) );
1494 }
1495 memcpy(thisField->field_string + thisField->field_length, this_payload, bytes_used);
1496 thisField->field_length += bytes_used;
1497
1498 } else if (str_encoding == TE_LATIN1) {
1499 int string_length = strlen(this_payload);
1500 if (string_length + thisField->field_length > thisField->alloc_length) {
1501 APar_realloc_memcpy(thisField, (string_length > 2000 ? string_length : 2000) );
1502 }
1503 int converted_bytes = UTF8Toisolat1((unsigned char*)thisField->field_string + thisField->field_length, (int)thisField->alloc_length,
1504 (unsigned char*)this_payload, string_length);
1505 if (converted_bytes > 0) {
1506 thisField->field_length += converted_bytes;
1507 bytes_used = converted_bytes;
1508 //fprintf(stdout, "string %s, %" PRIu32 "=%" PRIu32 "\n", thisField->field_string, thisField->field_length, bytes_used);
1509 }
1510
1511 } else if (str_encoding == TE_UTF16BE_NO_BOM) {
1512 int string_length = (int)utf8_length(this_payload, strlen(this_payload)) + 1;
1513 if (2 * string_length + thisField->field_length > thisField->alloc_length) {
1514 APar_realloc_memcpy(thisField, (2 * string_length + thisField->field_length > 2000 ? 2 * string_length + thisField->field_length : 2000) );
1515 }
1516 int converted_bytes = UTF8ToUTF16BE((unsigned char*)thisField->field_string + thisField->field_length, (int)thisField->alloc_length,
1517 (unsigned char*)this_payload, string_length);
1518 if (converted_bytes > 0) {
1519 thisField->field_length += converted_bytes;
1520 bytes_used = converted_bytes;
1521 }
1522
1523 } else if (str_encoding == TE_UTF16LE_WITH_BOM) {
1524 int string_length = (int)utf8_length(this_payload, strlen(this_payload)) + 1;
1525 uint64_t bom_offset = 0;
1526
1527 if (2 * string_length + thisField->field_length > thisField->alloc_length) { //important: realloc before BOM testing!!!
1528 APar_realloc_memcpy(thisField, (2 * string_length + thisField->field_length > 2000 ? 2 * string_length + thisField->field_length : 2000) );
1529 }
1530 if (thisField->field_length == 0 && multistringtext == false) {
1531 memcpy(thisField->field_string, "\xFF\xFE", 2);
1532 }
1533
1534 uint8_t field_encoding = TextField_TestBOM(thisField->field_string);
1535 if (field_encoding > 0) {
1536 bom_offset = 2;
1537 }
1538 int converted_bytes = UTF8ToUTF16LE((unsigned char*)thisField->field_string + thisField->field_length + bom_offset, (int)thisField->alloc_length,
1539 (unsigned char*)this_payload, string_length);
1540 if (converted_bytes > 0) {
1541 thisField->field_length += converted_bytes + bom_offset;
1542 bytes_used = converted_bytes;
1543 }
1544 }
1545
1546 if (multistringtext != false) {
1547 if (str_encoding == TE_UTF16LE_WITH_BOM || str_encoding == TE_UTF16BE_NO_BOM) {
1548 bytes_used += 2;
1549 } else {
1550 bytes_used += 1;
1551 }
1552 }
1553 return bytes_used;
1554 }
1555
1556 uint32_t APar_BinaryFieldPut(ID3v2Fields* thisField, uint32_t a_number, const char* this_payload, uint32_t payload_len) {
1557 if (thisField->ID3v2_Field_Type == ID3_TEXT_ENCODING_FIELD || thisField->ID3v2_Field_Type == ID3_PIC_TYPE_FIELD || thisField->ID3v2_Field_Type == ID3_GROUPSYMBOL_FIELD) {
1558 thisField->field_string[0] = (unsigned char)a_number;
1559 thisField->field_length = 1;
1560 //fprintf(stdout, "My (TE/PT) content is 0x%02X\n", thisField->field_string[0]);
1561 return 1;
1562
1563 } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD && payload_len == 0) { //contents of a file
1564 uint64_t file_length = findFileSize(this_payload);
1565 thisField->field_string = (char*)calloc(1, sizeof(char*)*file_length+16);
1566
1567 FILE* binfile = APar_OpenFile(this_payload, "rb");
1568 APar_ReadFile(thisField->field_string, binfile, file_length);
1569 fclose(binfile);
1570
1571 thisField->field_length = file_length;
1572 thisField->alloc_length = file_length+16;
1573 thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD;
1574 return file_length;
1575
1576 } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD || thisField->ID3v2_Field_Type == ID3_COUNTER_FIELD) {
1577 thisField->field_string = (char*)calloc(1, sizeof(char*)*payload_len+16);
1578 memcpy(thisField->field_string, this_payload, payload_len);
1579
1580 thisField->field_length = payload_len;
1581 thisField->alloc_length = payload_len+16;
1582 thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD;
1583 return payload_len;
1584
1585 }
1586 return 0;
1587 }
1588
1589 void APar_FrameDataPut(ID3v2Frame* thisFrame, const char* frame_payload, AdjunctArgs* adjunct_payload, uint8_t str_encoding) {
1590 if (adjunct_payload->multistringtext == false && !APar_EvalFrame_for_Field(thisFrame->ID3v2_FrameType, ID3_COUNTER_FIELD) ) thisFrame->ID3v2_Frame_Length = 0;
1591 switch(thisFrame->ID3v2_FrameType) {
1592 case ID3_TEXT_FRAME : {
1593 if (adjunct_payload->multistringtext && thisFrame->textfield_tally >= 1) {
1594 ID3v2Fields* last_textfield = APar_FindLastTextField (thisFrame);
1595 if (APar_ExtraTextFieldInit(last_textfield, strlen(frame_payload), (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0])) {
1596 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(last_textfield->next_field, frame_payload, (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0], true);
1597 }
1598 } else {
1599 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding
1600 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, frame_payload, str_encoding, false); //text field
1601 GlobalID3Tag->ID3v2_FrameCount++;
1602 }
1603 modified_atoms = true;
1604 GlobalID3Tag->modified_tag = true;
1605 //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
1606 thisFrame->textfield_tally++;
1607 break;
1608 }
1609 case ID3_TEXT_FRAME_USERDEF : {
1610 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding
1611 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->descripArg, str_encoding); //language
1612 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, frame_payload, str_encoding); //text field
1613 modified_atoms = true;
1614 GlobalID3Tag->modified_tag = true;
1615 GlobalID3Tag->ID3v2_FrameCount++;
1616 break;
1617 }
1618 case ID3_DESCRIBED_TEXT_FRAME : {
1619 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding
1620 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->targetLang, 0); //language
1621 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, adjunct_payload->descripArg, str_encoding); //description
1622 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+3, frame_payload, str_encoding, adjunct_payload->multistringtext); //text field
1623 modified_atoms = true;
1624 GlobalID3Tag->modified_tag = true;
1625 GlobalID3Tag->ID3v2_FrameCount++;
1626 break;
1627 }
1628 case ID3_URL_FRAME : {
1629 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //url field
1630 modified_atoms = true;
1631 GlobalID3Tag->modified_tag = true;
1632 GlobalID3Tag->ID3v2_FrameCount++;
1633 break;
1634 }
1635 case ID3_URL_FRAME_USERDEF : {
1636 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding
1637 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->descripArg, str_encoding); //language
1638 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, frame_payload, TE_LATIN1); //url field
1639 modified_atoms = true;
1640 GlobalID3Tag->modified_tag = true;
1641 GlobalID3Tag->ID3v2_FrameCount++;
1642 break;
1643 }
1644 case ID3_UNIQUE_FILE_ID_FRAME : {
1645 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //owner field
1646
1647 if (memcmp(adjunct_payload->dataArg, "randomUUIDstamp", 16) == 0) {
1648 char uuid_binary_str[25]; memset(uuid_binary_str, 0, 25);
1649 APar_generate_random_uuid(uuid_binary_str);
1650 (thisFrame->ID3v2_Frame_Fields+1)->field_string = (char*)calloc(1, sizeof(char*)*40);
1651 APar_sprintf_uuid((ap_uuid_t*)uuid_binary_str, (thisFrame->ID3v2_Frame_Fields+1)->field_string);
1652
1653 (thisFrame->ID3v2_Frame_Fields+1)->field_length = 36;
1654 (thisFrame->ID3v2_Frame_Fields+1)->alloc_length = 40;
1655 (thisFrame->ID3v2_Frame_Fields+1)->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD;
1656 thisFrame->ID3v2_Frame_Length += 36;
1657 } else {
1658 uint8_t uniqueIDlen = strlen(adjunct_payload->dataArg);
1659 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, adjunct_payload->dataArg, (uniqueIDlen > 64 ? 64 : uniqueIDlen)); //unique file ID
1660 }
1661
1662 modified_atoms = true;
1663 GlobalID3Tag->modified_tag = true;
1664 GlobalID3Tag->ID3v2_FrameCount++;
1665 break;
1666 }
1667 case ID3_CD_ID_FRAME : {
1668 thisFrame->ID3v2_Frame_Fields->field_length = GenerateMCDIfromCD(frame_payload, thisFrame->ID3v2_Frame_Fields->field_string);
1669 thisFrame->ID3v2_Frame_Length = thisFrame->ID3v2_Frame_Fields->field_length;
1670
1671 if (thisFrame->ID3v2_Frame_Length < 12) {
1672 free(thisFrame->ID3v2_Frame_Fields->field_string);
1673 thisFrame->ID3v2_Frame_Fields->field_string = NULL;
1674 thisFrame->ID3v2_Frame_Fields->alloc_length = 0;
1675 thisFrame->ID3v2_Frame_Length = 0;
1676 } else {
1677 modified_atoms = true;
1678 GlobalID3Tag->modified_tag = true;
1679 GlobalID3Tag->ID3v2_FrameCount++;
1680 }
1681 break;
1682 }
1683 case ID3_ATTACHED_PICTURE_FRAME : {
1684 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding
1685 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->mimeArg, TE_LATIN1); //mimetype
1686 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+2, adjunct_payload->pictype_uint8, NULL, 1); //picturetype
1687 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+3, adjunct_payload->descripArg, str_encoding); //description
1688 //(thisFrame->ID3v2_Frame_Fields+4)->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD; //because it wasn't malloced, this needs to be set now
1689 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+4, 0, frame_payload, 0); //binary file (path)
1690 modified_atoms = true;
1691 GlobalID3Tag->modified_tag = true;
1692 GlobalID3Tag->ID3v2_FrameCount++;
1693 break;
1694 }
1695 case ID3_ATTACHED_OBJECT_FRAME : {
1696 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); //encoding
1697 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->mimeArg, TE_LATIN1); //mimetype
1698 if (memcmp(adjunct_payload->filenameArg, "FILENAMESTAMP", 13) == 0) {
1699 const char* derived_filename = NULL;
1700 #if defined (_WIN32)
1701 derived_filename = strrchr(frame_payload, '\\');
1702 #if defined (__CYGWIN__)
1703 const char* derived_filename2 = strrchr(frame_payload, '/');
1704 if (derived_filename2 > derived_filename) {
1705 derived_filename = derived_filename2;
1706 }
2065 uint32_t APar_TextFieldDataPut(ID3v2Fields *thisField,
2066 const char *this_payload,
2067 uint8_t str_encoding,
2068 bool multistringtext = false) {
2069 uint32_t bytes_used = 0;
2070
2071 if (multistringtext == false) {
2072 thisField->field_length = 0;
2073 }
2074
2075 if (str_encoding == TE_UTF8) {
2076 bytes_used = strlen(
2077 this_payload); // no NULL termination is provided until render time
2078 if (bytes_used + thisField->field_length > thisField->alloc_length) {
2079 APar_realloc_memcpy(thisField, (bytes_used > 2000 ? bytes_used : 2000));
2080 }
2081 memcpy(thisField->field_string + thisField->field_length,
2082 this_payload,
2083 bytes_used);
2084 thisField->field_length += bytes_used;
2085
2086 } else if (str_encoding == TE_LATIN1) {
2087 int string_length = strlen(this_payload);
2088 if (string_length + thisField->field_length > thisField->alloc_length) {
2089 APar_realloc_memcpy(thisField,
2090 (string_length > 2000 ? string_length : 2000));
2091 }
2092 int converted_bytes = UTF8Toisolat1(
2093 (unsigned char *)thisField->field_string + thisField->field_length,
2094 (int)thisField->alloc_length,
2095 (unsigned char *)this_payload,
2096 string_length);
2097 if (converted_bytes > 0) {
2098 thisField->field_length += converted_bytes;
2099 bytes_used = converted_bytes;
2100 // fprintf(stdout, "string %s, %" PRIu32 "=%" PRIu32 "\n",
2101 // thisField->field_string, thisField->field_length, bytes_used);
2102 }
2103
2104 } else if (str_encoding == TE_UTF16BE_NO_BOM) {
2105 int string_length =
2106 (int)utf8_length(this_payload, strlen(this_payload)) + 1;
2107 if (2 * string_length + thisField->field_length > thisField->alloc_length) {
2108 APar_realloc_memcpy(thisField,
2109 (2 * string_length + thisField->field_length > 2000
2110 ? 2 * string_length + thisField->field_length
2111 : 2000));
2112 }
2113 int converted_bytes = UTF8ToUTF16BE(
2114 (unsigned char *)thisField->field_string + thisField->field_length,
2115 (int)thisField->alloc_length,
2116 (unsigned char *)this_payload,
2117 string_length);
2118 if (converted_bytes > 0) {
2119 thisField->field_length += converted_bytes;
2120 bytes_used = converted_bytes;
2121 }
2122
2123 } else if (str_encoding == TE_UTF16LE_WITH_BOM) {
2124 int string_length =
2125 (int)utf8_length(this_payload, strlen(this_payload)) + 1;
2126 uint64_t bom_offset = 0;
2127
2128 if (2 * string_length + thisField->field_length >
2129 thisField->alloc_length) { // important: realloc before BOM testing!!!
2130 APar_realloc_memcpy(thisField,
2131 (2 * string_length + thisField->field_length > 2000
2132 ? 2 * string_length + thisField->field_length
2133 : 2000));
2134 }
2135 if (thisField->field_length == 0 && multistringtext == false) {
2136 memcpy(thisField->field_string, "\xFF\xFE", 2);
2137 }
2138
2139 uint8_t field_encoding = TextField_TestBOM(thisField->field_string);
2140 if (field_encoding > 0) {
2141 bom_offset = 2;
2142 }
2143 int converted_bytes =
2144 UTF8ToUTF16LE((unsigned char *)thisField->field_string +
2145 thisField->field_length + bom_offset,
2146 (int)thisField->alloc_length,
2147 (unsigned char *)this_payload,
2148 string_length);
2149 if (converted_bytes > 0) {
2150 thisField->field_length += converted_bytes + bom_offset;
2151 bytes_used = converted_bytes;
2152 }
2153 }
2154
2155 if (multistringtext != false) {
2156 if (str_encoding == TE_UTF16LE_WITH_BOM ||
2157 str_encoding == TE_UTF16BE_NO_BOM) {
2158 bytes_used += 2;
2159 } else {
2160 bytes_used += 1;
2161 }
2162 }
2163 return bytes_used;
2164 }
2165
2166 uint32_t APar_BinaryFieldPut(ID3v2Fields *thisField,
2167 uint32_t a_number,
2168 const char *this_payload,
2169 uint32_t payload_len) {
2170 if (thisField->ID3v2_Field_Type == ID3_TEXT_ENCODING_FIELD ||
2171 thisField->ID3v2_Field_Type == ID3_PIC_TYPE_FIELD ||
2172 thisField->ID3v2_Field_Type == ID3_GROUPSYMBOL_FIELD) {
2173 thisField->field_string[0] = (unsigned char)a_number;
2174 thisField->field_length = 1;
2175 // fprintf(stdout, "My (TE/PT) content is 0x%02X\n",
2176 // thisField->field_string[0]);
2177 return 1;
2178
2179 } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD &&
2180 payload_len == 0) { // contents of a file
2181 uint64_t file_length = findFileSize(this_payload);
2182 thisField->field_string =
2183 (char *)calloc(1, sizeof(char *) * file_length + 16);
2184
2185 FILE *binfile = APar_OpenFile(this_payload, "rb");
2186 APar_ReadFile(thisField->field_string, binfile, file_length);
2187 fclose(binfile);
2188
2189 thisField->field_length = file_length;
2190 thisField->alloc_length = file_length + 16;
2191 thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD;
2192 return file_length;
2193
2194 } else if (thisField->ID3v2_Field_Type == ID3_BINARY_DATA_FIELD ||
2195 thisField->ID3v2_Field_Type == ID3_COUNTER_FIELD) {
2196 thisField->field_string =
2197 (char *)calloc(1, sizeof(char *) * payload_len + 16);
2198 memcpy(thisField->field_string, this_payload, payload_len);
2199
2200 thisField->field_length = payload_len;
2201 thisField->alloc_length = payload_len + 16;
2202 thisField->ID3v2_Field_Type = ID3_BINARY_DATA_FIELD;
2203 return payload_len;
2204 }
2205 return 0;
2206 }
2207
2208 void APar_FrameDataPut(ID3v2Frame *thisFrame,
2209 const char *frame_payload,
2210 AdjunctArgs *adjunct_payload,
2211 uint8_t str_encoding) {
2212 if (adjunct_payload->multistringtext == false &&
2213 !APar_EvalFrame_for_Field(thisFrame->ID3v2_FrameType, ID3_COUNTER_FIELD))
2214 thisFrame->ID3v2_Frame_Length = 0;
2215 switch (thisFrame->ID3v2_FrameType) {
2216 case ID3_TEXT_FRAME: {
2217 if (adjunct_payload->multistringtext && thisFrame->textfield_tally >= 1) {
2218 ID3v2Fields *last_textfield = APar_FindLastTextField(thisFrame);
2219 if (APar_ExtraTextFieldInit(
2220 last_textfield,
2221 strlen(frame_payload),
2222 (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0])) {
2223 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(
2224 last_textfield->next_field,
2225 frame_payload,
2226 (uint8_t)thisFrame->ID3v2_Frame_Fields->field_string[0],
2227 true);
2228 }
2229 } else {
2230 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2231 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2232 thisFrame->ID3v2_Frame_Length +=
2233 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2234 frame_payload,
2235 str_encoding,
2236 false); // text field
2237 GlobalID3Tag->ID3v2_FrameCount++;
2238 }
2239 modified_atoms = true;
2240 GlobalID3Tag->modified_tag = true;
2241 // GlobalID3Tag->ID3v2_FrameCount++; //don't do this for all text frames
2242 // because the multiple text field support of id3v2.4; only when the frame
2243 // is initially set
2244 thisFrame->textfield_tally++;
2245 break;
2246 }
2247 case ID3_TEXT_FRAME_USERDEF: {
2248 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2249 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2250 thisFrame->ID3v2_Frame_Length +=
2251 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2252 adjunct_payload->descripArg,
2253 str_encoding); // language
2254 thisFrame->ID3v2_Frame_Length +=
2255 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2256 frame_payload,
2257 str_encoding); // text field
2258 modified_atoms = true;
2259 GlobalID3Tag->modified_tag = true;
2260 GlobalID3Tag->ID3v2_FrameCount++;
2261 break;
2262 }
2263 case ID3_DESCRIBED_TEXT_FRAME: {
2264 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2265 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2266 thisFrame->ID3v2_Frame_Length +=
2267 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2268 adjunct_payload->targetLang,
2269 0); // language
2270 thisFrame->ID3v2_Frame_Length +=
2271 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2272 adjunct_payload->descripArg,
2273 str_encoding); // description
2274 thisFrame->ID3v2_Frame_Length +=
2275 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3,
2276 frame_payload,
2277 str_encoding,
2278 adjunct_payload->multistringtext); // text field
2279 modified_atoms = true;
2280 GlobalID3Tag->modified_tag = true;
2281 GlobalID3Tag->ID3v2_FrameCount++;
2282 break;
2283 }
2284 case ID3_URL_FRAME: {
2285 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(
2286 thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // url field
2287 modified_atoms = true;
2288 GlobalID3Tag->modified_tag = true;
2289 GlobalID3Tag->ID3v2_FrameCount++;
2290 break;
2291 }
2292 case ID3_URL_FRAME_USERDEF: {
2293 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2294 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2295 thisFrame->ID3v2_Frame_Length +=
2296 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2297 adjunct_payload->descripArg,
2298 str_encoding); // language
2299 thisFrame->ID3v2_Frame_Length +=
2300 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2301 frame_payload,
2302 TE_LATIN1); // url field
2303 modified_atoms = true;
2304 GlobalID3Tag->modified_tag = true;
2305 GlobalID3Tag->ID3v2_FrameCount++;
2306 break;
2307 }
2308 case ID3_UNIQUE_FILE_ID_FRAME: {
2309 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(
2310 thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // owner field
2311
2312 if (memcmp(adjunct_payload->dataArg, "randomUUIDstamp", 16) == 0) {
2313 char uuid_binary_str[25];
2314 memset(uuid_binary_str, 0, 25);
2315 APar_generate_random_uuid(uuid_binary_str);
2316 (thisFrame->ID3v2_Frame_Fields + 1)->field_string =
2317 (char *)calloc(1, sizeof(char *) * 40);
2318 APar_sprintf_uuid((ap_uuid_t *)uuid_binary_str,
2319 (thisFrame->ID3v2_Frame_Fields + 1)->field_string);
2320
2321 (thisFrame->ID3v2_Frame_Fields + 1)->field_length = 36;
2322 (thisFrame->ID3v2_Frame_Fields + 1)->alloc_length = 40;
2323 (thisFrame->ID3v2_Frame_Fields + 1)->ID3v2_Field_Type =
2324 ID3_BINARY_DATA_FIELD;
2325 thisFrame->ID3v2_Frame_Length += 36;
2326 } else {
2327 uint8_t uniqueIDlen = strlen(adjunct_payload->dataArg);
2328 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2329 thisFrame->ID3v2_Frame_Fields,
2330 0,
2331 adjunct_payload->dataArg,
2332 (uniqueIDlen > 64 ? 64 : uniqueIDlen)); // unique file ID
2333 }
2334
2335 modified_atoms = true;
2336 GlobalID3Tag->modified_tag = true;
2337 GlobalID3Tag->ID3v2_FrameCount++;
2338 break;
2339 }
2340 case ID3_CD_ID_FRAME: {
2341 thisFrame->ID3v2_Frame_Fields->field_length = GenerateMCDIfromCD(
2342 frame_payload, thisFrame->ID3v2_Frame_Fields->field_string);
2343 thisFrame->ID3v2_Frame_Length = thisFrame->ID3v2_Frame_Fields->field_length;
2344
2345 if (thisFrame->ID3v2_Frame_Length < 12) {
2346 free(thisFrame->ID3v2_Frame_Fields->field_string);
2347 thisFrame->ID3v2_Frame_Fields->field_string = NULL;
2348 thisFrame->ID3v2_Frame_Fields->alloc_length = 0;
2349 thisFrame->ID3v2_Frame_Length = 0;
2350 } else {
2351 modified_atoms = true;
2352 GlobalID3Tag->modified_tag = true;
2353 GlobalID3Tag->ID3v2_FrameCount++;
2354 }
2355 break;
2356 }
2357 case ID3_ATTACHED_PICTURE_FRAME: {
2358 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2359 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2360 thisFrame->ID3v2_Frame_Length +=
2361 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2362 adjunct_payload->mimeArg,
2363 TE_LATIN1); // mimetype
2364 thisFrame->ID3v2_Frame_Length +=
2365 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 2,
2366 adjunct_payload->pictype_uint8,
2367 NULL,
2368 1); // picturetype
2369 thisFrame->ID3v2_Frame_Length +=
2370 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3,
2371 adjunct_payload->descripArg,
2372 str_encoding); // description
2373 //(thisFrame->ID3v2_Frame_Fields+4)->ID3v2_Field_Type =
2374 // ID3_BINARY_DATA_FIELD; //because it wasn't malloced, this needs to be set
2375 // now
2376 thisFrame->ID3v2_Frame_Length +=
2377 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 4,
2378 0,
2379 frame_payload,
2380 0); // binary file (path)
2381 modified_atoms = true;
2382 GlobalID3Tag->modified_tag = true;
2383 GlobalID3Tag->ID3v2_FrameCount++;
2384 break;
2385 }
2386 case ID3_ATTACHED_OBJECT_FRAME: {
2387 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(
2388 thisFrame->ID3v2_Frame_Fields, str_encoding, NULL, 1); // encoding
2389 thisFrame->ID3v2_Frame_Length +=
2390 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 1,
2391 adjunct_payload->mimeArg,
2392 TE_LATIN1); // mimetype
2393 if (memcmp(adjunct_payload->filenameArg, "FILENAMESTAMP", 13) == 0) {
2394 const char *derived_filename = NULL;
2395 #if defined(_WIN32)
2396 derived_filename = strrchr(frame_payload, '\\');
2397 #if defined(__CYGWIN__)
2398 const char *derived_filename2 = strrchr(frame_payload, '/');
2399 if (derived_filename2 > derived_filename) {
2400 derived_filename = derived_filename2;
2401 }
17072402 #endif
17082403 #else
1709 derived_filename = strrchr(frame_payload, '/');
2404 derived_filename = strrchr(frame_payload, '/');
17102405 #endif
1711 if (derived_filename == NULL) {
1712 derived_filename = frame_payload;
1713 } else {
1714 derived_filename++; //get rid of the preceding slash
1715 }
1716 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, derived_filename, str_encoding); //filename
1717 } else {
1718 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+2, adjunct_payload->filenameArg, str_encoding); //filename
1719 }
1720 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields+3, adjunct_payload->descripArg, str_encoding); //description
1721 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+4, 0, frame_payload, 0); //binary file (path)
1722 modified_atoms = true;
1723 GlobalID3Tag->modified_tag = true;
1724 GlobalID3Tag->ID3v2_FrameCount++;
1725 break;
1726 }
1727 case ID3_GROUP_ID_FRAME : {
1728 uint32_t groupdatalen = strlen(adjunct_payload->dataArg);
1729 if (adjunct_payload->groupSymbol > 0) {
1730 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //owner field
1731 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+1, adjunct_payload->groupSymbol, NULL, 1); //group symbol
1732 if (groupdatalen > 0) { //not quite binary (unless it were entered as hex & converted), but it will do
1733 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+2, 0, adjunct_payload->dataArg, groupdatalen); //group symbol
1734 }
1735 modified_atoms = true;
1736 GlobalID3Tag->modified_tag = true;
1737 GlobalID3Tag->ID3v2_FrameCount++;
1738 }
1739 break;
1740 }
1741 case ID3_SIGNATURE_FRAME : {
1742 if (adjunct_payload->groupSymbol > 0) {
1743 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, adjunct_payload->groupSymbol, NULL, 1); //group symbol
1744 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+1, 0, frame_payload, strlen(frame_payload)); //signature
1745 modified_atoms = true;
1746 GlobalID3Tag->modified_tag = true;
1747 GlobalID3Tag->ID3v2_FrameCount++;
1748 }
1749 break;
1750 }
1751 case ID3_PRIVATE_FRAME : {
1752 uint32_t datalen = strlen(adjunct_payload->dataArg); //kinda precludes a true "binary" sense, but whatever...
1753 if (datalen > 0) {
1754 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //owner field
1755 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+1, 0, adjunct_payload->dataArg, datalen); //data
1756 modified_atoms = true;
1757 GlobalID3Tag->modified_tag = true;
1758 GlobalID3Tag->ID3v2_FrameCount++;
1759 }
1760 break;
1761 }
1762 case ID3_PLAYCOUNTER_FRAME : {
1763 uint64_t playcount = 0;
1764 char play_count_syncsafe[16];
1765
1766 memset(play_count_syncsafe, 0, sizeof(play_count_syncsafe));
1767
1768 if (strcmp(frame_payload, "+1") == 0) {
1769 if (thisFrame->ID3v2_Frame_Length == 4) {
1770 playcount = (uint64_t)syncsafe32_to_UInt32(thisFrame->ID3v2_Frame_Fields->field_string) + 1;
1771 } else if (thisFrame->ID3v2_Frame_Length > 4) {
1772 playcount = syncsafeXX_to_UInt64(thisFrame->ID3v2_Frame_Fields->field_string, thisFrame->ID3v2_Frame_Fields->field_length) +1;
1773 } else {
1774 playcount = 1;
1775 }
1776 } else {
1777 sscanf(frame_payload, "%" SCNu64, &playcount);
1778 }
1779
1780 if (playcount < 268435455) {
1781 convert_to_syncsafe32(playcount, play_count_syncsafe);
1782 thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, play_count_syncsafe, 4);
1783 } else {
1784 uint8_t conversion_len = convert_to_syncsafeXX(playcount, play_count_syncsafe);
1785 thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, play_count_syncsafe, conversion_len);
1786 }
1787 modified_atoms = true;
1788 GlobalID3Tag->modified_tag = true;
1789 GlobalID3Tag->ID3v2_FrameCount++;
1790 break;
1791 }
1792 case ID3_POPULAR_FRAME : {
1793 unsigned char popm_rating = 0;
1794 uint64_t popm_playcount = 0;
1795 char popm_play_count_syncsafe[16];
1796
1797 memset(popm_play_count_syncsafe, 0,
1798 sizeof(popm_play_count_syncsafe));
1799
1800 if (adjunct_payload->ratingArg != NULL) {
1801 popm_rating = strtoul(adjunct_payload->ratingArg, NULL, 10);
1802 }
1803 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); //owner field
1804 thisFrame->ID3v2_Frame_Length += APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields+1, 0, (char*)&popm_rating, 1); //rating
1805
1806 if (adjunct_payload->dataArg != NULL) {
1807 if (strlen(adjunct_payload->dataArg) > 0) {
1808 if (memcmp(adjunct_payload->dataArg, "+1", 3) == 0) {
1809 if ((thisFrame->ID3v2_Frame_Fields+2)->field_length == 4) {
1810 popm_playcount = (uint64_t)syncsafe32_to_UInt32((thisFrame->ID3v2_Frame_Fields+2)->field_string) + 1;
1811 } else if ((thisFrame->ID3v2_Frame_Fields+2)->field_length > 4) {
1812 popm_playcount = syncsafeXX_to_UInt64((thisFrame->ID3v2_Frame_Fields+2)->field_string, (thisFrame->ID3v2_Frame_Fields+2)->field_length) +1;
1813 } else {
1814 popm_playcount = 1;
1815 }
1816 } else {
1817 sscanf(adjunct_payload->dataArg, "%" SCNu64, &popm_playcount);
1818 }
1819 }
1820 }
1821 if (popm_playcount > 0) {
1822 if (popm_playcount < 268435455) {
1823 convert_to_syncsafe32(popm_playcount, popm_play_count_syncsafe);
1824 thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, popm_play_count_syncsafe, 4); //syncsafe32 counter
1825 } else {
1826 uint8_t conversion_len = convert_to_syncsafeXX(popm_playcount, popm_play_count_syncsafe);
1827 thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields, 0, popm_play_count_syncsafe, conversion_len); //BIGsyncsafe counter
1828 }
1829 }
1830 modified_atoms = true;
1831 GlobalID3Tag->modified_tag = true;
1832 GlobalID3Tag->ID3v2_FrameCount++;
1833 break;
1834 }
1835 } //end switch
1836 return;
1837 }
1838
1839 void APar_EmbeddedFileTests(const char* filepath, int frameType, AdjunctArgs* adjunct_payloads) {
1840 if (frameType == ID3_ATTACHED_PICTURE_FRAME) {
1841
1842 //get cli imagetype
1843 uint8_t total_image_types = (uint8_t)(sizeof(ImageTypeList)/sizeof(*ImageTypeList));
1844 uint8_t img_typlen = strlen(adjunct_payloads->pictypeArg) + 1;
1845 const char* img_comparison_str = NULL;
1846
1847 for (uint8_t itest = 0; itest < total_image_types; itest++) {
1848 if (img_typlen == 5) {
1849 img_comparison_str = ImageTypeList[itest].hexstring;
1850 } else {
1851 img_comparison_str = ImageTypeList[itest].imagetype_str;
1852 }
1853 if (strcmp(adjunct_payloads->pictypeArg, img_comparison_str) == 0) {
1854 adjunct_payloads->pictype_uint8 = ImageTypeList[itest].hexcode;
1855 }
1856 }
1857
1858 if (strlen(filepath) > 0) {
1859 //see if file even exists
1860 TestFileExistence(filepath, true);
1861
1862 char* image_headerbytes = (char*)calloc(1, (sizeof(char)*25));
1863 FILE* imagefile = APar_OpenFile(filepath, "rb");
1864 APar_ReadFile(image_headerbytes, imagefile, 24);
1865 fclose(imagefile);
1866 //test mimetype
1867 if (strlen(adjunct_payloads->mimeArg) == 0 || memcmp(adjunct_payloads->mimeArg, "-->", 3) == 0) {
1868 uint8_t total_image_tests = (uint8_t)(sizeof(ImageList)/sizeof(*ImageList));
1869 for (uint8_t itest = 0; itest < total_image_tests; itest++) {
1870 if (ImageList[itest].image_testbytes == 0) {
1871 adjunct_payloads->mimeArg = ImageList[itest].image_mimetype;
1872 break;
1873 } else if (memcmp(image_headerbytes, ImageList[itest].image_binaryheader, ImageList[itest].image_testbytes) == 0) {
1874 adjunct_payloads->mimeArg = ImageList[itest].image_mimetype;
1875 if (adjunct_payloads->pictype_uint8 == 0x01) {
1876 if (memcmp(image_headerbytes+16, "\x00\x00\x00\x20\x00\x00\x00\x20", 8) != 0 && itest != 2) {
1877 adjunct_payloads->pictype_uint8 = 0x02;
1878 }
1879 }
1880 break;
1881 }
1882 }
1883 }
1884 free(image_headerbytes);
1885 image_headerbytes = NULL;
1886 }
1887
1888 } else if (frameType == ID3_ATTACHED_OBJECT_FRAME) {
1889 if (strlen(filepath) > 0) {
1890 TestFileExistence(filepath, true);
1891 FILE* embedfile = APar_OpenFile(filepath, "rb");
1892 fclose(embedfile);
1893 }
1894 }
1895 return;
1896 }
1897
1898 char* APar_ConvertField_to_UTF8(ID3v2Frame* targetframe, int fieldtype) {
1899 char* utf8str = NULL;
1900 uint8_t targetfield = 0xFF;
1901 uint8_t textencoding = 0;
1902
1903 for (uint8_t frm_field = 0; frm_field < targetframe->ID3v2_FieldCount; frm_field++) {
1904 if ( (targetframe->ID3v2_Frame_Fields+frm_field)->ID3v2_Field_Type == fieldtype) {
1905 targetfield = frm_field;
1906 break;
1907 }
1908 }
1909
1910 if (targetfield != 0xFF) {
1911 if (targetframe->ID3v2_Frame_Fields->ID3v2_Field_Type == ID3_TEXT_ENCODING_FIELD) {
1912 textencoding = targetframe->ID3v2_Frame_Fields->field_string[0];
1913 }
1914
1915 if (textencoding == TE_LATIN1) {
1916 utf8str = (char*)calloc(1, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *2) +16);
1917 isolat1ToUTF8((unsigned char*)utf8str, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *2) +16,
1918 (unsigned char*)((targetframe->ID3v2_Frame_Fields+targetfield)->field_string), (targetframe->ID3v2_Frame_Fields+targetfield)->field_length);
1919
1920 } else if (textencoding == TE_UTF8) { //just so things can be free()'d with testing; a small price to pay
1921 utf8str = (char*)calloc(1, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length) +16);
1922 memcpy(utf8str, (targetframe->ID3v2_Frame_Fields+targetfield)->field_string, (targetframe->ID3v2_Frame_Fields+targetfield)->field_length);
1923
1924 } else if (textencoding == TE_UTF16BE_NO_BOM) {
1925 utf8str = (char*)calloc(1, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16);
1926 UTF16BEToUTF8((unsigned char*)utf8str, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16,
1927 (unsigned char*)((targetframe->ID3v2_Frame_Fields+targetfield)->field_string), (targetframe->ID3v2_Frame_Fields+targetfield)->field_length);
1928
1929 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
1930 utf8str = (char*)calloc(1, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16);
1931 if (memcmp( (targetframe->ID3v2_Frame_Fields+targetfield)->field_string, "\xFF\xFE", 2) == 0) {
1932 UTF16LEToUTF8((unsigned char*)utf8str, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16,
1933 (unsigned char*)((targetframe->ID3v2_Frame_Fields+targetfield)->field_string+2), (targetframe->ID3v2_Frame_Fields+targetfield)->field_length-2);
1934 } else {
1935 UTF16BEToUTF8((unsigned char*)utf8str, sizeof(char*)*( (targetframe->ID3v2_Frame_Fields+targetfield)->field_length *4) +16,
1936 (unsigned char*)((targetframe->ID3v2_Frame_Fields+targetfield)->field_string+2), (targetframe->ID3v2_Frame_Fields+targetfield)->field_length-2);
1937 }
1938 }
1939 }
1940
1941 return utf8str;
2406 if (derived_filename == NULL) {
2407 derived_filename = frame_payload;
2408 } else {
2409 derived_filename++; // get rid of the preceding slash
2410 }
2411 thisFrame->ID3v2_Frame_Length +=
2412 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2413 derived_filename,
2414 str_encoding); // filename
2415 } else {
2416 thisFrame->ID3v2_Frame_Length +=
2417 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 2,
2418 adjunct_payload->filenameArg,
2419 str_encoding); // filename
2420 }
2421 thisFrame->ID3v2_Frame_Length +=
2422 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields + 3,
2423 adjunct_payload->descripArg,
2424 str_encoding); // description
2425 thisFrame->ID3v2_Frame_Length +=
2426 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 4,
2427 0,
2428 frame_payload,
2429 0); // binary file (path)
2430 modified_atoms = true;
2431 GlobalID3Tag->modified_tag = true;
2432 GlobalID3Tag->ID3v2_FrameCount++;
2433 break;
2434 }
2435 case ID3_GROUP_ID_FRAME: {
2436 uint32_t groupdatalen = strlen(adjunct_payload->dataArg);
2437 if (adjunct_payload->groupSymbol > 0) {
2438 thisFrame->ID3v2_Frame_Length +=
2439 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields,
2440 frame_payload,
2441 TE_LATIN1); // owner field
2442 thisFrame->ID3v2_Frame_Length +=
2443 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1,
2444 adjunct_payload->groupSymbol,
2445 NULL,
2446 1); // group symbol
2447 if (groupdatalen > 0) { // not quite binary (unless it were entered as hex
2448 // & converted), but it will do
2449 thisFrame->ID3v2_Frame_Length +=
2450 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 2,
2451 0,
2452 adjunct_payload->dataArg,
2453 groupdatalen); // group symbol
2454 }
2455 modified_atoms = true;
2456 GlobalID3Tag->modified_tag = true;
2457 GlobalID3Tag->ID3v2_FrameCount++;
2458 }
2459 break;
2460 }
2461 case ID3_SIGNATURE_FRAME: {
2462 if (adjunct_payload->groupSymbol > 0) {
2463 thisFrame->ID3v2_Frame_Length +=
2464 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields,
2465 adjunct_payload->groupSymbol,
2466 NULL,
2467 1); // group symbol
2468 thisFrame->ID3v2_Frame_Length +=
2469 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1,
2470 0,
2471 frame_payload,
2472 strlen(frame_payload)); // signature
2473 modified_atoms = true;
2474 GlobalID3Tag->modified_tag = true;
2475 GlobalID3Tag->ID3v2_FrameCount++;
2476 }
2477 break;
2478 }
2479 case ID3_PRIVATE_FRAME: {
2480 uint32_t datalen =
2481 strlen(adjunct_payload->dataArg); // kinda precludes a true "binary"
2482 // sense, but whatever...
2483 if (datalen > 0) {
2484 thisFrame->ID3v2_Frame_Length +=
2485 APar_TextFieldDataPut(thisFrame->ID3v2_Frame_Fields,
2486 frame_payload,
2487 TE_LATIN1); // owner field
2488 thisFrame->ID3v2_Frame_Length +=
2489 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1,
2490 0,
2491 adjunct_payload->dataArg,
2492 datalen); // data
2493 modified_atoms = true;
2494 GlobalID3Tag->modified_tag = true;
2495 GlobalID3Tag->ID3v2_FrameCount++;
2496 }
2497 break;
2498 }
2499 case ID3_PLAYCOUNTER_FRAME: {
2500 uint64_t playcount = 0;
2501 char play_count_syncsafe[16];
2502
2503 memset(play_count_syncsafe, 0, sizeof(play_count_syncsafe));
2504
2505 if (strcmp(frame_payload, "+1") == 0) {
2506 if (thisFrame->ID3v2_Frame_Length == 4) {
2507 playcount = (uint64_t)syncsafe32_to_UInt32(
2508 thisFrame->ID3v2_Frame_Fields->field_string) +
2509 1;
2510 } else if (thisFrame->ID3v2_Frame_Length > 4) {
2511 playcount =
2512 syncsafeXX_to_UInt64(thisFrame->ID3v2_Frame_Fields->field_string,
2513 thisFrame->ID3v2_Frame_Fields->field_length) +
2514 1;
2515 } else {
2516 playcount = 1;
2517 }
2518 } else {
2519 sscanf(frame_payload, "%" SCNu64, &playcount);
2520 }
2521
2522 if (playcount < 268435455) {
2523 convert_to_syncsafe32(playcount, play_count_syncsafe);
2524 thisFrame->ID3v2_Frame_Length = APar_BinaryFieldPut(
2525 thisFrame->ID3v2_Frame_Fields, 0, play_count_syncsafe, 4);
2526 } else {
2527 uint8_t conversion_len =
2528 convert_to_syncsafeXX(playcount, play_count_syncsafe);
2529 thisFrame->ID3v2_Frame_Length =
2530 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields,
2531 0,
2532 play_count_syncsafe,
2533 conversion_len);
2534 }
2535 modified_atoms = true;
2536 GlobalID3Tag->modified_tag = true;
2537 GlobalID3Tag->ID3v2_FrameCount++;
2538 break;
2539 }
2540 case ID3_POPULAR_FRAME: {
2541 unsigned char popm_rating = 0;
2542 uint64_t popm_playcount = 0;
2543 char popm_play_count_syncsafe[16];
2544
2545 memset(popm_play_count_syncsafe, 0, sizeof(popm_play_count_syncsafe));
2546
2547 if (adjunct_payload->ratingArg != NULL) {
2548 popm_rating = strtoul(adjunct_payload->ratingArg, NULL, 10);
2549 }
2550 thisFrame->ID3v2_Frame_Length += APar_TextFieldDataPut(
2551 thisFrame->ID3v2_Frame_Fields, frame_payload, TE_LATIN1); // owner field
2552 thisFrame->ID3v2_Frame_Length +=
2553 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields + 1,
2554 0,
2555 (char *)&popm_rating,
2556 1); // rating
2557
2558 if (adjunct_payload->dataArg != NULL) {
2559 if (strlen(adjunct_payload->dataArg) > 0) {
2560 if (memcmp(adjunct_payload->dataArg, "+1", 3) == 0) {
2561 if ((thisFrame->ID3v2_Frame_Fields + 2)->field_length == 4) {
2562 popm_playcount =
2563 (uint64_t)syncsafe32_to_UInt32(
2564 (thisFrame->ID3v2_Frame_Fields + 2)->field_string) +
2565 1;
2566 } else if ((thisFrame->ID3v2_Frame_Fields + 2)->field_length > 4) {
2567 popm_playcount =
2568 syncsafeXX_to_UInt64(
2569 (thisFrame->ID3v2_Frame_Fields + 2)->field_string,
2570 (thisFrame->ID3v2_Frame_Fields + 2)->field_length) +
2571 1;
2572 } else {
2573 popm_playcount = 1;
2574 }
2575 } else {
2576 sscanf(adjunct_payload->dataArg, "%" SCNu64, &popm_playcount);
2577 }
2578 }
2579 }
2580 if (popm_playcount > 0) {
2581 if (popm_playcount < 268435455) {
2582 convert_to_syncsafe32(popm_playcount, popm_play_count_syncsafe);
2583 thisFrame->ID3v2_Frame_Length =
2584 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields,
2585 0,
2586 popm_play_count_syncsafe,
2587 4); // syncsafe32 counter
2588 } else {
2589 uint8_t conversion_len =
2590 convert_to_syncsafeXX(popm_playcount, popm_play_count_syncsafe);
2591 thisFrame->ID3v2_Frame_Length =
2592 APar_BinaryFieldPut(thisFrame->ID3v2_Frame_Fields,
2593 0,
2594 popm_play_count_syncsafe,
2595 conversion_len); // BIGsyncsafe counter
2596 }
2597 }
2598 modified_atoms = true;
2599 GlobalID3Tag->modified_tag = true;
2600 GlobalID3Tag->ID3v2_FrameCount++;
2601 break;
2602 }
2603 } // end switch
2604 return;
2605 }
2606
2607 void APar_EmbeddedFileTests(const char *filepath,
2608 int frameType,
2609 AdjunctArgs *adjunct_payloads) {
2610 if (frameType == ID3_ATTACHED_PICTURE_FRAME) {
2611
2612 // get cli imagetype
2613 uint8_t total_image_types =
2614 (uint8_t)(sizeof(ImageTypeList) / sizeof(*ImageTypeList));
2615 uint8_t img_typlen = strlen(adjunct_payloads->pictypeArg) + 1;
2616 const char *img_comparison_str = NULL;
2617
2618 for (uint8_t itest = 0; itest < total_image_types; itest++) {
2619 if (img_typlen == 5) {
2620 img_comparison_str = ImageTypeList[itest].hexstring;
2621 } else {
2622 img_comparison_str = ImageTypeList[itest].imagetype_str;
2623 }
2624 if (strcmp(adjunct_payloads->pictypeArg, img_comparison_str) == 0) {
2625 adjunct_payloads->pictype_uint8 = ImageTypeList[itest].hexcode;
2626 }
2627 }
2628
2629 if (strlen(filepath) > 0) {
2630 // see if file even exists
2631 TestFileExistence(filepath, true);
2632
2633 char *image_headerbytes = (char *)calloc(1, (sizeof(char) * 25));
2634 FILE *imagefile = APar_OpenFile(filepath, "rb");
2635 APar_ReadFile(image_headerbytes, imagefile, 24);
2636 fclose(imagefile);
2637 // test mimetype
2638 if (strlen(adjunct_payloads->mimeArg) == 0 ||
2639 memcmp(adjunct_payloads->mimeArg, "-->", 3) == 0) {
2640 uint8_t total_image_tests =
2641 (uint8_t)(sizeof(ImageList) / sizeof(*ImageList));
2642 for (uint8_t itest = 0; itest < total_image_tests; itest++) {
2643 if (ImageList[itest].image_testbytes == 0) {
2644 adjunct_payloads->mimeArg = ImageList[itest].image_mimetype;
2645 break;
2646 } else if (memcmp(image_headerbytes,
2647 ImageList[itest].image_binaryheader,
2648 ImageList[itest].image_testbytes) == 0) {
2649 adjunct_payloads->mimeArg = ImageList[itest].image_mimetype;
2650 if (adjunct_payloads->pictype_uint8 == 0x01) {
2651 if (memcmp(image_headerbytes + 16,
2652 "\x00\x00\x00\x20\x00\x00\x00\x20",
2653 8) != 0 &&
2654 itest != 2) {
2655 adjunct_payloads->pictype_uint8 = 0x02;
2656 }
2657 }
2658 break;
2659 }
2660 }
2661 }
2662 free(image_headerbytes);
2663 image_headerbytes = NULL;
2664 }
2665
2666 } else if (frameType == ID3_ATTACHED_OBJECT_FRAME) {
2667 if (strlen(filepath) > 0) {
2668 TestFileExistence(filepath, true);
2669 FILE *embedfile = APar_OpenFile(filepath, "rb");
2670 fclose(embedfile);
2671 }
2672 }
2673 return;
2674 }
2675
2676 char *APar_ConvertField_to_UTF8(ID3v2Frame *targetframe, int fieldtype) {
2677 char *utf8str = NULL;
2678 uint8_t targetfield = 0xFF;
2679 uint8_t textencoding = 0;
2680
2681 for (uint8_t frm_field = 0; frm_field < targetframe->ID3v2_FieldCount;
2682 frm_field++) {
2683 if ((targetframe->ID3v2_Frame_Fields + frm_field)->ID3v2_Field_Type ==
2684 fieldtype) {
2685 targetfield = frm_field;
2686 break;
2687 }
2688 }
2689
2690 if (targetfield != 0xFF) {
2691 if (targetframe->ID3v2_Frame_Fields->ID3v2_Field_Type ==
2692 ID3_TEXT_ENCODING_FIELD) {
2693 textencoding = targetframe->ID3v2_Frame_Fields->field_string[0];
2694 }
2695
2696 if (textencoding == TE_LATIN1) {
2697 utf8str = (char *)calloc(
2698 1,
2699 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2700 ->field_length *
2701 2) +
2702 16);
2703 isolat1ToUTF8(
2704 (unsigned char *)utf8str,
2705 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2706 ->field_length *
2707 2) +
2708 16,
2709 (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield)
2710 ->field_string),
2711 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length);
2712
2713 } else if (textencoding == TE_UTF8) { // just so things can be free()'d with
2714 // testing; a small price to pay
2715 utf8str = (char *)calloc(
2716 1,
2717 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2718 ->field_length) +
2719 16);
2720 memcpy(utf8str,
2721 (targetframe->ID3v2_Frame_Fields + targetfield)->field_string,
2722 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length);
2723
2724 } else if (textencoding == TE_UTF16BE_NO_BOM) {
2725 utf8str = (char *)calloc(
2726 1,
2727 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2728 ->field_length *
2729 4) +
2730 16);
2731 UTF16BEToUTF8(
2732 (unsigned char *)utf8str,
2733 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2734 ->field_length *
2735 4) +
2736 16,
2737 (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield)
2738 ->field_string),
2739 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length);
2740
2741 } else if (textencoding == TE_UTF16LE_WITH_BOM) {
2742 utf8str = (char *)calloc(
2743 1,
2744 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2745 ->field_length *
2746 4) +
2747 16);
2748 if (memcmp((targetframe->ID3v2_Frame_Fields + targetfield)->field_string,
2749 "\xFF\xFE",
2750 2) == 0) {
2751 UTF16LEToUTF8(
2752 (unsigned char *)utf8str,
2753 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2754 ->field_length *
2755 4) +
2756 16,
2757 (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield)
2758 ->field_string +
2759 2),
2760 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length - 2);
2761 } else {
2762 UTF16BEToUTF8(
2763 (unsigned char *)utf8str,
2764 sizeof(char *) * ((targetframe->ID3v2_Frame_Fields + targetfield)
2765 ->field_length *
2766 4) +
2767 16,
2768 (unsigned char *)((targetframe->ID3v2_Frame_Fields + targetfield)
2769 ->field_string +
2770 2),
2771 (targetframe->ID3v2_Frame_Fields + targetfield)->field_length - 2);
2772 }
2773 }
2774 }
2775
2776 return utf8str;
19422777 }
19432778
19442779 /*----------------------
19452780 APar_FindFrame
1946 id3v2tag - an already initialized ID3 tag (contained by an ID32 atom) with 0 or more frames as a linked list
1947 frame_str - target frame string (like "TIT2")
1948 frameID - a known frame in listed in AP_ID3v2_FrameDefinitions & enumerated in AP_ID3v2_Definitions.h
1949 frametype - the type of frame (text, described text, picture, object...) to search for
1950 adjunct_payloads - holds optional/required args for supplementary matching; example: described text matching on frame name & description; TODO more criteria
1951 createframe - create the frame if not found to be existing; this initialzes the frame only - not its fields.
1952
1953 this provides 2 functions: actually searching while looping through the frames & creation of a frame at the end of the frame list.
2781 id3v2tag - an already initialized ID3 tag (contained by an ID32 atom)
2782 with 0 or more frames as a linked list frame_str - target frame string (like
2783 "TIT2") frameID - a known frame in listed in AP_ID3v2_FrameDefinitions &
2784 enumerated in AP_ID3v2_Definitions.h frametype - the type of frame (text,
2785 described text, picture, object...) to search for adjunct_payloads - holds
2786 optional/required args for supplementary matching; example: described text
2787 matching on frame name & description; TODO more criteria createframe - create
2788 the frame if not found to be existing; this initialzes the frame only - not its
2789 fields.
2790
2791 this provides 2 functions: actually searching while looping through the
2792 frames & creation of a frame at the end of the frame list.
19542793 ----------------------*/
1955 ID3v2Frame* APar_FindFrame(ID3v2Tag* id3v2tag, const char* frame_str, int frameID, int frametype, AdjunctArgs* adjunct_payloads, bool createframe) {
1956 ID3v2Frame* returnframe = NULL;
1957 ID3v2Frame* evalframe = id3v2tag->ID3v2_FirstFrame;
1958 uint8_t supplemental_matching = 0;
1959
1960 if (createframe) {
1961 ID3v2Frame* newframe = (ID3v2Frame*)calloc(1, sizeof(ID3v2Frame));
1962 newframe->ID3v2_NextFrame = NULL;
1963 if (id3v2tag->ID3v2_FirstFrame == NULL) id3v2tag->ID3v2_FirstFrame = newframe;
1964 if (id3v2tag->ID3v2_FrameList != NULL) id3v2tag->ID3v2_FrameList->ID3v2_NextFrame = newframe;
1965 id3v2tag->ID3v2_FrameList = newframe;
1966 return newframe;
1967 }
1968
1969 if (APar_EvalFrame_for_Field(frametype, ID3_DESCRIPTION_FIELD)) {
1970 supplemental_matching = 0x01;
1971 }
1972
1973 while (evalframe != NULL) {
1974 //if (trametype is a type containing a modifer like description or image type or symbol or such things
1975 if (supplemental_matching != 0) {
1976
1977 //match on description + frame name
1978 if (supplemental_matching && 0x01 && evalframe->ID3v2_Frame_ID == frameID) {
1979 char* utf8_descrip = APar_ConvertField_to_UTF8(evalframe, ID3_DESCRIPTION_FIELD);
1980 if (utf8_descrip != NULL) {
1981 if (strcmp(adjunct_payloads->descripArg, utf8_descrip) == 0) {
1982 returnframe = evalframe;
1983 free(utf8_descrip);
1984 break;
1985 }
1986 free(utf8_descrip);
1987 }
1988 }
1989
1990 } else if (evalframe->ID3v2_Frame_ID == ID3_UNKNOWN_FRAME) {
1991 if (memcmp(frame_str, evalframe->ID3v2_Frame_Namestr, 4) == 0) {
1992 returnframe = evalframe;
1993 break;
1994 }
1995
1996 } else {
1997 //fprintf(stdout, "frame is %s; eval frameID is %d ?= %d\n", frame_str, evalframe->ID3v2_Frame_ID, frameID);
1998 if (evalframe->ID3v2_Frame_ID == frameID) {
1999 returnframe = evalframe;
2000 break;
2001 }
2002 }
2003 evalframe = evalframe->ID3v2_NextFrame;
2004 }
2005 return returnframe;
2794 ID3v2Frame *APar_FindFrame(ID3v2Tag *id3v2tag,
2795 const char *frame_str,
2796 int frameID,
2797 int frametype,
2798 AdjunctArgs *adjunct_payloads,
2799 bool createframe) {
2800 ID3v2Frame *returnframe = NULL;
2801 ID3v2Frame *evalframe = id3v2tag->ID3v2_FirstFrame;
2802 uint8_t supplemental_matching = 0;
2803
2804 if (createframe) {
2805 ID3v2Frame *newframe = (ID3v2Frame *)calloc(1, sizeof(ID3v2Frame));
2806 newframe->ID3v2_NextFrame = NULL;
2807 if (id3v2tag->ID3v2_FirstFrame == NULL)
2808 id3v2tag->ID3v2_FirstFrame = newframe;
2809 if (id3v2tag->ID3v2_FrameList != NULL)
2810 id3v2tag->ID3v2_FrameList->ID3v2_NextFrame = newframe;
2811 id3v2tag->ID3v2_FrameList = newframe;
2812 return newframe;
2813 }
2814
2815 if (APar_EvalFrame_for_Field(frametype, ID3_DESCRIPTION_FIELD)) {
2816 supplemental_matching = 0x01;
2817 }
2818
2819 while (evalframe != NULL) {
2820 // if (trametype is a type containing a modifer like description or image
2821 // type or symbol or such things
2822 if (supplemental_matching != 0) {
2823
2824 // match on description + frame name
2825 if (supplemental_matching & 0x01 &&
2826 evalframe->ID3v2_Frame_ID == frameID) {
2827 char *utf8_descrip =
2828 APar_ConvertField_to_UTF8(evalframe, ID3_DESCRIPTION_FIELD);
2829 if (utf8_descrip != NULL) {
2830 if (strcmp(adjunct_payloads->descripArg, utf8_descrip) == 0) {
2831 returnframe = evalframe;
2832 free(utf8_descrip);
2833 break;
2834 }
2835 free(utf8_descrip);
2836 }
2837 }
2838
2839 } else if (evalframe->ID3v2_Frame_ID == ID3_UNKNOWN_FRAME) {
2840 if (memcmp(frame_str, evalframe->ID3v2_Frame_Namestr, 4) == 0) {
2841 returnframe = evalframe;
2842 break;
2843 }
2844
2845 } else {
2846 // fprintf(stdout, "frame is %s; eval frameID is %d ?= %d\n", frame_str,
2847 // evalframe->ID3v2_Frame_ID, frameID);
2848 if (evalframe->ID3v2_Frame_ID == frameID) {
2849 returnframe = evalframe;
2850 break;
2851 }
2852 }
2853 evalframe = evalframe->ID3v2_NextFrame;
2854 }
2855 return returnframe;
20062856 }
20072857
20082858 /*----------------------
20092859 APar_ID3FrameAmmend
2010 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
2011 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
2012 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)
2013 adjunct_payloads - a structure holding a number of optional/required parameters for the frame (compression...)
2014 str_encoding - the encoding to be used in the fields of the target frame when different encodings are allowed
2015
2016 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 &
2017 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.
2018 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 &
2019 adjunct payloads onto APar_FrameDataPut to actually place the data onto the frame
2860 id32_atom - the ID32 atom targeted to this language; the ID32 atom is
2861 already created, the ID3 tag is either created or containing already parsed ID3
2862 frames frame_str - the string for the frame (like TCON) that is desired. This
2863 string must be a known frame string in AP_ID3v2_FrameDefinitions.h frame_payload
2864 - the major piece of metadata to be set (for APIC its the path, for MCDI its a
2865 device...), that can optionally be NULL (for removal of the frame)
2866 adjunct_payloads - a structure holding a number of optional/required
2867 parameters for the frame (compression...) str_encoding - the encoding to be used
2868 in the fields of the target frame when different encodings are allowed
2869
2870 lookup what frame_str is supposed to look like in the KnownFrames[] array in
2871 AP_ID3v2_FrameDefinitions.h. First see if this frame exists at all - if it does
2872 & the frame_str is NULL or blank (""), then mark this frame for elimination. if
2873 the frame is of a particular type (like TCON), run some tests on the
2874 frame_payload. If all is well after the tests, and the frame does not exists,
2875 create it via APar_FindFrame(... true) & initialize the frame to hold data. Send
2876 the frame, payload & adjunct payloads onto APar_FrameDataPut to actually place
2877 the data onto the frame
20202878 ----------------------*/
2021 void APar_ID3FrameAmmend(AtomicInfo* id32_atom, const char* frame_str, const char* frame_payload, AdjunctArgs* adjunct_payloads, uint8_t str_encoding) {
2022 ID3v2Frame* targetFrame = NULL;
2023
2024 if (id32_atom == NULL) return;
2025 GlobalID3Tag = id32_atom->ID32_TagInfo;
2026 //fprintf(stdout, "frame is %s; payload is %s; %s %s\n", frame_str, frame_payload, adjunct_payloads->descripArg, adjunct_payloads->targetLang);
2027
2028 int frameID = MatchID3FrameIDstr(frame_str, GlobalID3Tag->ID3v2Tag_MajorVersion);
2029 int frameType = KnownFrames[frameID+1].ID3v2_FrameType;
2030 uint8_t frameCompositionList = GetFrameCompositionDescription(frameType);
2031
2032 if (frameType == ID3_ATTACHED_PICTURE_FRAME || frameType == ID3_ATTACHED_OBJECT_FRAME) {
2033 APar_EmbeddedFileTests(frame_payload, frameType, adjunct_payloads);
2034 }
2035
2036 targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo, frame_str, frameID, frameType, adjunct_payloads, false);
2037
2038 if (frame_payload == NULL) {
2039 if (targetFrame != NULL) {
2040 targetFrame->eliminate_frame = true;
2041 modified_atoms = true;
2042 id32_atom->ID32_TagInfo->modified_tag = true;
2043 }
2044 return;
2045
2046 } else if (strlen(frame_payload) == 0) {
2047 if (targetFrame != NULL) {
2048 targetFrame->eliminate_frame = true; //thats right, frames of empty text are removed - so be a doll and try to convey some info, eh?
2049 modified_atoms = true;
2050 id32_atom->ID32_TagInfo->modified_tag = true;
2051 }
2052 return;
2053
2054 } else {
2055 if (frameType == ID3_UNKNOWN_FRAME) {
2056 APar_assert(false, 10, frame_str);
2057 return;
2058 }
2059
2060 //check tags to be set so they conform to the id3v2 informal specification
2061 if (frameType == ID3_TEXT_FRAME) {
2062 if (targetFrame != NULL) {
2063 if (!targetFrame->eliminate_frame) adjunct_payloads->multistringtext = true; //if a frame already exists and isn't marked for elimination, append a new string
2064 }
2065
2066 if (frameID == ID3v2_FRAME_COPYRIGHT || frameID == ID3v2_FRAME_PRODNOTICE) {
2067 if ((TestCharInRange(frame_payload[0], '0', '9') + TestCharInRange(frame_payload[1], '0', '9') + TestCharInRange(frame_payload[2], '0', '9') +
2068 TestCharInRange(frame_payload[3], '0', '9') != 4) || frame_payload[4] != ' ') {
2069 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);
2070 return;
2071 }
2072
2073 } else if (frameID == ID3v2_FRAME_PART_O_SET || frameID == ID3v2_FRAME_TRACKNUM) {
2074 uint8_t pos_len = strlen(frame_payload);
2075 for (uint8_t letter_idx = 0; letter_idx < pos_len; letter_idx++) {
2076 if (frame_payload[letter_idx] == '/') continue;
2077 if (TestCharInRange(frame_payload[letter_idx], '0', '9') != 1) {
2078 if (frameID-1 == ID3v2_FRAME_PART_O_SET) {
2079 fprintf(stderr, "AtomicParsley warning: frame %s was skipped because it had an extraneous character: %c\n", KnownFrames[frameID].ID3V2p4_FrameID, frame_payload[letter_idx]);
2080 return;
2081 } else { //okay this is to support the beloved vinyl
2082 if (!(TestCharInRange(frame_payload[letter_idx], 'A', 'F') && TestCharInRange(frame_payload[letter_idx+1], '0', '9'))) {
2083 fprintf(stderr, "AtomicParsley warning: frame %s was skipped because it had an extraneous character: %c\n", KnownFrames[frameID].ID3V2p4_FrameID, frame_payload[letter_idx]);
2084 return;
2085 }
2086 }
2087 }
2088 }
2089 } else if (frameID == ID3v2_FRAME_ISRC) {
2090 uint8_t isrc_len = strlen(frame_payload);
2091 if (isrc_len != 12) {
2092 fprintf(stderr, "AtomicParsley warning: setting ISRC frame was skipped because it was not 12 characters long\n");
2093 return;
2094 }
2095 for (uint8_t isrc_ltr_idx = 0; isrc_ltr_idx < isrc_len; isrc_ltr_idx++) {
2096 if (TestCharInRange(frame_payload[isrc_ltr_idx], '0', '9') + TestCharInRange(frame_payload[isrc_ltr_idx], 'A', 'Z') == 0) {
2097 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]);
2098 return;
2099 }
2100 }
2101 }
2102 }
2103
2104
2105 if (targetFrame == NULL) {
2106 targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo, frame_str, frameID, frameType, adjunct_payloads, true);
2107 if (targetFrame == NULL) {
2108 fprintf(stdout, "NULL frame\n");
2109 exit(0);
2110 } else {
2111 APar_FrameInit(targetFrame, frame_str, frameID, frameCompositionList, frame_payload);
2112 }
2113 }
2114 }
2115
2116 if (targetFrame != NULL) {
2117 if (adjunct_payloads->zlibCompressed) {
2118 targetFrame->ID3v2_Frame_Flags |= (ID32_FRAMEFLAG_COMPRESSED + ID32_FRAMEFLAG_LENINDICATED);
2119 }
2120
2121 if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_LANGUAGE) {
2122 APar_FrameDataPut(targetFrame, adjunct_payloads->targetLang, adjunct_payloads, str_encoding);
2123
2124 } else if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_CONTENTTYPE) {
2125 uint8_t genre_idx = ID3StringGenreToInt(frame_payload);
2126 if (genre_idx != 0xFF) {
2127 char genre_str_idx[2];
2128 genre_str_idx[0] = 0; genre_str_idx[1] = 0; genre_str_idx[1] = 0;
2129 sprintf(genre_str_idx, "%u", genre_idx);
2130 APar_FrameDataPut(targetFrame, genre_str_idx, adjunct_payloads, str_encoding);
2131 } else {
2132 APar_FrameDataPut(targetFrame, frame_payload, adjunct_payloads, str_encoding);
2133 }
2134
2135 } else {
2136 APar_FrameDataPut(targetFrame, frame_payload, adjunct_payloads, str_encoding);
2137 }
2138
2139 if (adjunct_payloads->zlibCompressed) {
2140 targetFrame->ID3v2_Frame_ExpandedLength = targetFrame->ID3v2_Frame_Length;
2141 }
2142 targetFrame->ID3v2_Frame_GroupingSymbol = adjunct_payloads->groupSymbol;
2143 }
2144 return;
2879 void APar_ID3FrameAmmend(AtomicInfo *id32_atom,
2880 const char *frame_str,
2881 const char *frame_payload,
2882 AdjunctArgs *adjunct_payloads,
2883 uint8_t str_encoding) {
2884 ID3v2Frame *targetFrame = NULL;
2885
2886 if (id32_atom == NULL)
2887 return;
2888 GlobalID3Tag = id32_atom->ID32_TagInfo;
2889 // fprintf(stdout, "frame is %s; payload is %s; %s %s\n", frame_str,
2890 // frame_payload, adjunct_payloads->descripArg, adjunct_payloads->targetLang);
2891
2892 int frameID =
2893 MatchID3FrameIDstr(frame_str, GlobalID3Tag->ID3v2Tag_MajorVersion);
2894 int frameType = KnownFrames[frameID + 1].ID3v2_FrameType;
2895 uint8_t frameCompositionList = GetFrameCompositionDescription(frameType);
2896
2897 if (frameType == ID3_ATTACHED_PICTURE_FRAME ||
2898 frameType == ID3_ATTACHED_OBJECT_FRAME) {
2899 APar_EmbeddedFileTests(frame_payload, frameType, adjunct_payloads);
2900 }
2901
2902 targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo,
2903 frame_str,
2904 frameID,
2905 frameType,
2906 adjunct_payloads,
2907 false);
2908
2909 if (frame_payload == NULL) {
2910 if (targetFrame != NULL) {
2911 targetFrame->eliminate_frame = true;
2912 modified_atoms = true;
2913 id32_atom->ID32_TagInfo->modified_tag = true;
2914 }
2915 return;
2916
2917 } else if (strlen(frame_payload) == 0) {
2918 if (targetFrame != NULL) {
2919 targetFrame->eliminate_frame =
2920 true; // thats right, frames of empty text are removed - so be a doll
2921 // and try to convey some info, eh?
2922 modified_atoms = true;
2923 id32_atom->ID32_TagInfo->modified_tag = true;
2924 }
2925 return;
2926
2927 } else {
2928 if (frameType == ID3_UNKNOWN_FRAME) {
2929 APar_assert(false, 10, frame_str);
2930 return;
2931 }
2932
2933 // check tags to be set so they conform to the id3v2 informal specification
2934 if (frameType == ID3_TEXT_FRAME) {
2935 if (targetFrame != NULL) {
2936 if (!targetFrame->eliminate_frame)
2937 adjunct_payloads->multistringtext =
2938 true; // if a frame already exists and isn't marked for
2939 // elimination, append a new string
2940 }
2941
2942 if (frameID == ID3v2_FRAME_COPYRIGHT ||
2943 frameID == ID3v2_FRAME_PRODNOTICE) {
2944 if ((TestCharInRange(frame_payload[0], '0', '9') +
2945 TestCharInRange(frame_payload[1], '0', '9') +
2946 TestCharInRange(frame_payload[2], '0', '9') +
2947 TestCharInRange(frame_payload[3], '0', '9') !=
2948 4) ||
2949 frame_payload[4] != ' ') {
2950 fprintf(stderr,
2951 "AtomicParsley warning: frame %s was skipped because it did "
2952 "not start with a year followed by a space\n",
2953 KnownFrames[frameID].ID3V2p4_FrameID);
2954 return;
2955 }
2956
2957 } else if (frameID == ID3v2_FRAME_PART_O_SET ||
2958 frameID == ID3v2_FRAME_TRACKNUM) {
2959 uint8_t pos_len = strlen(frame_payload);
2960 for (uint8_t letter_idx = 0; letter_idx < pos_len; letter_idx++) {
2961 if (frame_payload[letter_idx] == '/')
2962 continue;
2963 if (TestCharInRange(frame_payload[letter_idx], '0', '9') != 1) {
2964 if (frameID - 1 == ID3v2_FRAME_PART_O_SET) {
2965 fprintf(stderr,
2966 "AtomicParsley warning: frame %s was skipped because it "
2967 "had an extraneous character: %c\n",
2968 KnownFrames[frameID].ID3V2p4_FrameID,
2969 frame_payload[letter_idx]);
2970 return;
2971 } else { // okay this is to support the beloved vinyl
2972 if (!(TestCharInRange(frame_payload[letter_idx], 'A', 'F') &&
2973 TestCharInRange(frame_payload[letter_idx + 1], '0', '9'))) {
2974 fprintf(stderr,
2975 "AtomicParsley warning: frame %s was skipped because "
2976 "it had an extraneous character: %c\n",
2977 KnownFrames[frameID].ID3V2p4_FrameID,
2978 frame_payload[letter_idx]);
2979 return;
2980 }
2981 }
2982 }
2983 }
2984 } else if (frameID == ID3v2_FRAME_ISRC) {
2985 uint8_t isrc_len = strlen(frame_payload);
2986 if (isrc_len != 12) {
2987 fprintf(stderr,
2988 "AtomicParsley warning: setting ISRC frame was "
2989 "skipped because it was not 12 characters long\n");
2990 return;
2991 }
2992 for (uint8_t isrc_ltr_idx = 0; isrc_ltr_idx < isrc_len;
2993 isrc_ltr_idx++) {
2994 if (TestCharInRange(frame_payload[isrc_ltr_idx], '0', '9') +
2995 TestCharInRange(frame_payload[isrc_ltr_idx], 'A', 'Z') ==
2996 0) {
2997 fprintf(stderr,
2998 "AtomicParsley warning: ISRC can only consist of A-Z & "
2999 "0-9; letter %u was %c; skipping\n",
3000 isrc_ltr_idx + 1,
3001 frame_payload[isrc_ltr_idx]);
3002 return;
3003 }
3004 }
3005 }
3006 }
3007
3008 if (targetFrame == NULL) {
3009 targetFrame = APar_FindFrame(id32_atom->ID32_TagInfo,
3010 frame_str,
3011 frameID,
3012 frameType,
3013 adjunct_payloads,
3014 true);
3015 if (targetFrame == NULL) {
3016 fprintf(stdout, "NULL frame\n");
3017 exit(0);
3018 } else {
3019 APar_FrameInit(targetFrame,
3020 frame_str,
3021 frameID,
3022 frameCompositionList,
3023 frame_payload);
3024 }
3025 }
3026 }
3027
3028 if (targetFrame != NULL) {
3029 if (adjunct_payloads->zlibCompressed) {
3030 targetFrame->ID3v2_Frame_Flags |=
3031 (ID32_FRAMEFLAG_COMPRESSED + ID32_FRAMEFLAG_LENINDICATED);
3032 }
3033
3034 if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_LANGUAGE) {
3035 APar_FrameDataPut(targetFrame,
3036 adjunct_payloads->targetLang,
3037 adjunct_payloads,
3038 str_encoding);
3039
3040 } else if (targetFrame->ID3v2_Frame_ID == ID3v2_FRAME_CONTENTTYPE) {
3041 uint8_t genre_idx = ID3StringGenreToInt(frame_payload);
3042 if (genre_idx != 0xFF) {
3043 char genre_str_idx[2];
3044 genre_str_idx[0] = 0;
3045 genre_str_idx[1] = 0;
3046 genre_str_idx[1] = 0;
3047 sprintf(genre_str_idx, "%u", genre_idx);
3048 APar_FrameDataPut(
3049 targetFrame, genre_str_idx, adjunct_payloads, str_encoding);
3050 } else {
3051 APar_FrameDataPut(
3052 targetFrame, frame_payload, adjunct_payloads, str_encoding);
3053 }
3054
3055 } else {
3056 APar_FrameDataPut(
3057 targetFrame, frame_payload, adjunct_payloads, str_encoding);
3058 }
3059
3060 if (adjunct_payloads->zlibCompressed) {
3061 targetFrame->ID3v2_Frame_ExpandedLength = targetFrame->ID3v2_Frame_Length;
3062 }
3063 targetFrame->ID3v2_Frame_GroupingSymbol = adjunct_payloads->groupSymbol;
3064 }
3065 return;
21453066 }
21463067
21473068 ///////////////////////////////////////////////////////////////////////////////////////
2148 // id3 cleanup function //
3069 // id3 cleanup function //
21493070 ///////////////////////////////////////////////////////////////////////////////////////
21503071
21513072 /*----------------------
21523073 APar_FreeID32Memory
21533074
2154 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
2155 strings (char*) that were malloced.
3075 free all the little bits of allocated memory. Follow the ID3v2Frame pointers
3076 by each frame's ID3v2_NextFrame. Each frame has ID3v2_FieldCount number of field
3077 strings (char*) that were malloced.
21563078 ----------------------*/
2157 void APar_FreeID32Memory(ID3v2Tag* id32tag) {
2158 ID3v2Frame* aframe = id32tag->ID3v2_FirstFrame;
2159 while (aframe != NULL) {
2160
3079 void APar_FreeID32Memory(ID3v2Tag *id32tag) {
3080 ID3v2Frame *aframe = id32tag->ID3v2_FirstFrame;
3081 while (aframe != NULL) {
3082
21613083 #if defined(DEBUG_V)
2162 fprintf(stdout, "freeing frame %s of %u fields\n", aframe->ID3v2_Frame_Namestr, aframe->ID3v2_FieldCount);
3084 fprintf(stdout,
3085 "freeing frame %s of %u fields\n",
3086 aframe->ID3v2_Frame_Namestr,
3087 aframe->ID3v2_FieldCount);
21633088 #endif
2164 for(uint8_t id3fld = 0; id3fld < aframe->ID3v2_FieldCount; id3fld++) {
3089 for (uint8_t id3fld = 0; id3fld < aframe->ID3v2_FieldCount; id3fld++) {
21653090 #if defined(DEBUG_V)
2166 fprintf(stdout, "freeing field %s ; %u of %u fields\n", (aframe->ID3v2_Frame_Fields+id3fld)->field_string, id3fld+1, aframe->ID3v2_FieldCount);
3091 fprintf(stdout,
3092 "freeing field %s ; %u of %u fields\n",
3093 (aframe->ID3v2_Frame_Fields + id3fld)->field_string,
3094 id3fld + 1,
3095 aframe->ID3v2_FieldCount);
21673096 #endif
2168 ID3v2Fields* afield = aframe->ID3v2_Frame_Fields+id3fld;
2169 ID3v2Fields* freefield = NULL;
2170 while (true) {
2171 if ( afield != NULL && afield->field_string != NULL ) {
2172 free( afield->field_string );
2173 afield->field_string = NULL;
2174 }
2175 freefield = afield;
2176 afield = afield->next_field;
2177 if (afield == NULL) break;
2178 if (aframe->ID3v2_Frame_Fields+id3fld != freefield) free(freefield);
2179 }
2180 }
2181 free( aframe->ID3v2_Frame_Fields );
2182 aframe->ID3v2_Frame_Fields = NULL;
2183 free(aframe);
2184 aframe = aframe->ID3v2_NextFrame;
2185 }
2186 return;
2187 }
3097 ID3v2Fields *afield = aframe->ID3v2_Frame_Fields + id3fld;
3098 ID3v2Fields *freefield = NULL;
3099 while (true) {
3100 if (afield != NULL && afield->field_string != NULL) {
3101 free(afield->field_string);
3102 afield->field_string = NULL;
3103 }
3104 freefield = afield;
3105 afield = afield->next_field;
3106 if (afield == NULL)
3107 break;
3108 if (aframe->ID3v2_Frame_Fields + id3fld != freefield)
3109 free(freefield);
3110 }
3111 }
3112 free(aframe->ID3v2_Frame_Fields);
3113 aframe->ID3v2_Frame_Fields = NULL;
3114 free(aframe);
3115 aframe = aframe->ID3v2_NextFrame;
3116 }
3117 return;
3118 }
11 /*
22 AtomicParsley - id3v2.h
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1919 */
2020 //==================================================================//
2121
22 struct AdjunctArgs {
23 const char* targetLang;
24 const char* descripArg;
25 const char* mimeArg;
26 const char* pictypeArg;
27 const char* filenameArg;
28 const char* ratingArg;
29 const char* dataArg; //multipurposed: PRIV's binary data, GRID's group data, UFID's binary data, POPM's counter field
30 uint8_t pictype_uint8;
31 uint8_t groupSymbol;
32 bool zlibCompressed;
33 bool multistringtext;
22 struct AdjunctArgs {
23 const char *targetLang;
24 const char *descripArg;
25 const char *mimeArg;
26 const char *pictypeArg;
27 const char *filenameArg;
28 const char *ratingArg;
29 const char *dataArg; // multipurposed: PRIV's binary data, GRID's group data,
30 // UFID's binary data, POPM's counter field
31 uint8_t pictype_uint8;
32 uint8_t groupSymbol;
33 bool zlibCompressed;
34 bool multistringtext;
3435 };
3536
36 uint64_t syncsafeXX_to_UInt64(char* syncsafe_int, uint8_t syncsafe_len);
37 uint32_t syncsafe32_to_UInt32(char* syncsafe_int);
37 uint64_t syncsafeXX_to_UInt64(char *syncsafe_int, uint8_t syncsafe_len);
38 uint32_t syncsafe32_to_UInt32(char *syncsafe_int);
3839
3940 bool ID3v2_TestTagFlag(uint8_t TagFlag, uint8_t TagBit);
4041 bool ID3v2_TestFrameFlag(uint16_t FrameFlag, uint16_t FrameBit);
4243
4344 void ListID3FrameIDstrings();
4445 void List_imagtype_strings();
45 const char* ConvertCLIFrameStr_TO_frameID(const char* frame_str);
46 const char *ConvertCLIFrameStr_TO_frameID(const char *frame_str);
4647 bool TestCLI_for_FrameParams(int frametype, uint8_t testparam);
4748
48 int MatchID3FrameIDstr(const char* foundFrameID, uint8_t tagVersion);
49 int MatchID3FrameIDstr(const char *foundFrameID, uint8_t tagVersion);
4950 uint8_t GetFrameCompositionDescription(int ID3v2_FrameTypeID);
50 int FrameStr_TO_FrameType(const char* frame_str);
51 int FrameStr_TO_FrameType(const char *frame_str);
5152
52 void APar_ID32_ScanID3Tag(FILE* source_file, AtomicInfo* id32_atom);
53 void APar_ID32_ScanID3Tag(FILE *source_file, AtomicInfo *id32_atom);
5354
54 uint32_t APar_GetTagSize(AtomicInfo* id32_atom);
55 uint32_t APar_Render_ID32_Tag(AtomicInfo* id32_atom, uint32_t max_alloc);
55 uint32_t APar_GetTagSize(AtomicInfo *id32_atom);
56 uint32_t APar_Render_ID32_Tag(AtomicInfo *id32_atom, uint32_t max_alloc);
5657
57 char* APar_ConvertField_to_UTF8(ID3v2Frame* targetframe, int fieldtype);
58 char *APar_ConvertField_to_UTF8(ID3v2Frame *targetframe, int fieldtype);
5859
59 void APar_ID3Tag_Init(AtomicInfo* id32_atom);
60 void APar_ID3FrameAmmend(AtomicInfo* id32_atom, const char* frame_str, const char* frame_payload, AdjunctArgs* adjunct_payloads, uint8_t str_encoding);
60 void APar_ID3Tag_Init(AtomicInfo *id32_atom);
61 void APar_ID3FrameAmmend(AtomicInfo *id32_atom,
62 const char *frame_str,
63 const char *frame_payload,
64 AdjunctArgs *adjunct_payloads,
65 uint8_t str_encoding);
6166
62 void APar_FreeID32Memory(ID3v2Tag* id32tag);
67 void APar_FreeID32Memory(ID3v2Tag *id32tag);
11 /*
22 AtomicParsley - id3v2defs.h
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
2020 //==================================================================//
2121
2222 ID3FrameDefinition KnownFrames[] = {
23 { "", "", "", "Unknown frame", "", ID3v2_UNKNOWN_FRAME, ID3_UNKNOWN_FRAME },
24 { "TAL", "TALB", "TALB", "Album/Movie/Show title", "album", ID3v2_FRAME_ALBUM, ID3_TEXT_FRAME },
25 { "TBP", "TBPM", "TBPM", "BPM (beats per minute)", "bpm", ID3v2_FRAME_BPM, ID3_TEXT_FRAME },
26 { "TCM", "TCOM", "TCOM", "Composer", "composer", ID3v2_FRAME_COMPOSER, ID3_TEXT_FRAME },
27 { "TCO", "TCON", "TCON", "Content Type/Genre", "genre", ID3v2_FRAME_CONTENTTYPE, ID3_TEXT_FRAME },
28 { "TCP", "TCOP", "TCOP", "Copyright message", "copyright", ID3v2_FRAME_COPYRIGHT, ID3_TEXT_FRAME },
29 { "", "", "TDEN", "Encoding time", "", ID3v2_FRAME_ENCODINGTIME, ID3_TEXT_FRAME },
30 { "TDY", "TDLY", "TDLY", "Playlist delay", "", ID3v2_FRAME_PLAYLISTDELAY, ID3_TEXT_FRAME },
31 { "", "", "TDOR", "Original release time", "", ID3v2_FRAME_ORIGRELTIME, ID3_TEXT_FRAME },
32 { "", "", "TDRC", "Recording time", "date", ID3v2_FRAME_RECORDINGTIME, ID3_TEXT_FRAME },
33 { "", "", "TDRL", "Release time", "released", ID3v2_FRAME_RELEASETIME, ID3_TEXT_FRAME },
34 { "", "", "TDTG", "Tagging time", "tagged", ID3v2_FRAME_TAGGINGTIME, ID3_TEXT_FRAME },
35 { "TEN", "TENC", "TENC", "Encoded by", "encoder", ID3v2_FRAME_ENCODER, ID3_TEXT_FRAME },
36 { "TXT", "TEXT", "TEXT", "Lyricist/Text writer", "writer", ID3v2_FRAME_LYRICIST, ID3_TEXT_FRAME },
37 { "TFT", "TFLT", "TFLT", "File type", "", ID3v2_FRAME_FILETYPE, ID3_TEXT_FRAME },
38 { "", "", "TIPL", "Involved people list", "", ID3v2_FRAME_INVOLVEDPEOPLE, ID3_TEXT_FRAME },
39 { "TT1", "TIT1", "TIT1", "Content group description", "grouping", ID3v2_FRAME_GROUP_DESC, ID3_TEXT_FRAME },
40 { "TT2", "TIT2", "TIT2", "Title/songname/content description", "title", ID3v2_FRAME_TITLE, ID3_TEXT_FRAME },
41 { "TT3", "TIT3", "TIT3", "Subtitle/Description refinement", "subtitle", ID3v2_FRAME_SUBTITLE, ID3_TEXT_FRAME },
42 { "TKE", "TKEY", "TKEY", "Initial key", "", ID3v2_FRAME_INITIALKEY, ID3_TEXT_FRAME },
43 { "TLA", "TLAN", "TLAN", "Language(s)", "", ID3v2_FRAME_LANGUAGE, ID3_TEXT_FRAME },
44 { "TLE", "TLEN", "TLEN", "Length", "", ID3v2_FRAME_TIMELENGTH, ID3_TEXT_FRAME },
45 { "", "", "TMCL", "Musician credits list", "credits", ID3v2_FRAME_MUSICIANLIST, ID3_TEXT_FRAME },
46 { "TMT", "TMED", "TMED", "Media type", "media", ID3v2_FRAME_MEDIATYPE, ID3_TEXT_FRAME },
47 { "", "", "TMOO", "Mood", "mood", ID3v2_FRAME_MOOD, ID3_TEXT_FRAME },
48 { "TOT", "TOAL", "TOAL", "Original album/movie/show title", "", ID3v2_FRAME_ORIGALBUM, ID3_TEXT_FRAME },
49 { "TOF", "TOFN", "TOFN", "Original filename", "", ID3v2_FRAME_ORIGFILENAME, ID3_TEXT_FRAME },
50 { "TOL", "TOLY", "TOLY", "Original lyricist(s)/text writer(s)", "", ID3v2_FRAME_ORIGWRITER, ID3_TEXT_FRAME },
51 { "TOA", "TOPE", "TOPE", "Original artist(s)/performer(s)", "", ID3v2_FRAME_ORIGARTIST, ID3_TEXT_FRAME },
52 { "", "TOWN", "TOWN", "File owner/licensee", "", ID3v2_FRAME_FILEOWNER, ID3_TEXT_FRAME },
53 { "TP1", "TPE1", "TPE1", "Artist/Lead performer(s)/Soloist(s)", "artist", ID3v2_FRAME_ARTIST, ID3_TEXT_FRAME },
54 { "TP2", "TPE2", "TPE2", "Album artist/Band/orchestra/accompaniment", "album artist", ID3v2_FRAME_ALBUMARTIST, ID3_TEXT_FRAME },
55 { "TP3", "TPE3", "TPE3", "Conductor/performer refinement", "conductor", ID3v2_FRAME_CONDUCTOR, ID3_TEXT_FRAME },
56 { "TP4", "TPE4", "TPE4", "Interpreted or remixed by", "remixer", ID3v2_FRAME_REMIXER, ID3_TEXT_FRAME },
57 { "TPA", "TPOS", "TPOS", "Part of a set", "", ID3v2_FRAME_PART_O_SET, ID3_TEXT_FRAME },
58 { "", "", "TPRO", "Produced notice", "", ID3v2_FRAME_PRODNOTICE, ID3_TEXT_FRAME },
59 { "TPB", "TPUB", "TPUB", "Publisher", "publisher", ID3v2_FRAME_PUBLISHER, ID3_TEXT_FRAME },
60 { "TRK", "TRCK", "TRCK", "Track number/Position in set", "trk#", ID3v2_FRAME_TRACKNUM, ID3_TEXT_FRAME },
61 { "", "TRSN", "TRSN", "Internet radio station name", "", ID3v2_FRAME_IRADIONAME, ID3_TEXT_FRAME },
62 { "", "TRSO", "TRSO", "Internet radio station owner", "", ID3v2_FRAME_IRADIOOWNER, ID3_TEXT_FRAME },
63 { "", "", "TSOA", "Album sort order", "", ID3v2_FRAME_ALBUMSORT, ID3_TEXT_FRAME },
64 { "", "", "TSOP", "Performer sort order", "", ID3v2_FRAME_PERFORMERSORT, ID3_TEXT_FRAME },
65 { "", "", "TSOT", "Title sort order", "", ID3v2_FRAME_TITLESORT, ID3_TEXT_FRAME },
66 { "TRC", "TSRC", "TSRC", "ISRC", "", ID3v2_FRAME_ISRC, ID3_TEXT_FRAME },
67 { "TSS", "TSSE", "TSSE", "Software/Hardware and settings used for encoding", "", ID3v2_FRAME_ENCODINGSETTINGS, ID3_TEXT_FRAME },
68 { "", "", "TSST", "Set subtitle", "", ID3v2_FRAME_SETSUBTITLE, ID3_TEXT_FRAME },
69
70 { "TDA", "TDAT", "", "Date", "", ID3v2_DATE, ID3_TEXT_FRAME },
71 { "TIM", "TIME", "", "TIME", "", ID3v2_TIME, ID3_TEXT_FRAME },
72 { "TOR", "TORY", "", "Original Release Year", "", ID3v2_ORIGRELYEAR, ID3_TEXT_FRAME },
73 { "TRD", "TRDA", "", "Recording dates", "", ID3v2_RECORDINGDATE, ID3_TEXT_FRAME },
74 { "TSI", "TSIZ", "", "Size", "", ID3v2_FRAME_SIZE, ID3_TEXT_FRAME },
75 { "TYE", "TYER", "", "YEAR", "", ID3v2_FRAME_YEAR, ID3_TEXT_FRAME },
76
77 { "TXX", "TXXX", "TXXX", "User defined text information frame", "", ID3v2_FRAME_USERDEF_TEXT, ID3_TEXT_FRAME_USERDEF },
78
79 //some of these (like WCOM, WOAF) allow for muliple frames - but (sigh) alas, such is not the case in AP.
80 { "WCM", "WCOM", "WCOM", "Commercial information", "", ID3v2_FRAME_URLCOMMINFO, ID3_URL_FRAME },
81 { "WCP", "WCOP", "WCOP", "Copyright/Legal information", "", ID3v2_FRAME_URLCOPYRIGHT, ID3_URL_FRAME },
82 { "WAF", "WOAF", "WOAF", "Official audio file webpage", "", ID3v2_FRAME_URLAUDIOFILE, ID3_URL_FRAME },
83 { "WAR", "WOAR", "WOAR", "Official artist/performer webpage", "", ID3v2_FRAME_URLARTIST, ID3_URL_FRAME },
84 { "WAS", "WOAS", "WOAS", "Official audio source webpage", "", ID3v2_FRAME_URLAUDIOSOURCE, ID3_URL_FRAME },
85 { "", "WORS", "WORS", "Official Internet radio station homepage", "", ID3v2_FRAME_URLIRADIO, ID3_URL_FRAME },
86 { "", "WPAY", "WPAY", "Payment", "", ID3v2_FRAME_URLPAYMENT, ID3_URL_FRAME },
87 { "WPB", "WPUB", "WPUB", "Publishers official webpage", "", ID3v2_FRAME_URLPUBLISHER, ID3_URL_FRAME },
88 { "WXX", "WXXX", "WXXX", "User defined URL link frame", "", ID3v2_FRAME_USERDEF_URL, ID3_URL_FRAME_USERDEF },
89
90 { "UFI", "UFID", "UFID", "Unique file identifier", "", ID3v2_FRAME_UFID, ID3_UNIQUE_FILE_ID_FRAME },
91 { "MCI", "MCID", "MCDI", "Music CD Identifier", "", ID3v2_FRAME_MUSIC_CD_ID, ID3_CD_ID_FRAME },
92
93 { "COM", "COMM", "COMM", "Comment", "comment", ID3v2_FRAME_COMMENT, ID3_DESCRIBED_TEXT_FRAME },
94 { "ULT", "USLT", "USLT", "Unsynchronised lyrics", "lyrics", ID3v2_FRAME_UNSYNCLYRICS, ID3_DESCRIBED_TEXT_FRAME },
95
96 { "", "APIC", "APIC", "Attached picture", "", ID3v2_EMBEDDED_PICTURE, ID3_ATTACHED_PICTURE_FRAME },
97 { "PIC", "", "", "Attached picture", "", ID3v2_EMBEDDED_PICTURE_V2P2, ID3_OLD_V2P2_PICTURE_FRAME },
98 { "GEO", "GEOB", "GEOB", "Attached object", "", ID3v2_EMBEDDED_OBJECT, ID3_ATTACHED_OBJECT_FRAME },
99
100 { "", "GRID", "GRID", "Group ID registration", "", ID3v2_FRAME_GRID, ID3_GROUP_ID_FRAME },
101 { "", "", "SIGN", "Signature", "", ID3v2_FRAME_SIGNATURE, ID3_SIGNATURE_FRAME },
102 { "", "PRIV", "PRIV", "Private frame", "", ID3v2_FRAME_PRIVATE, ID3_PRIVATE_FRAME },
103 { "CNT", "PCNT", "PCNT", "Play counter", "", ID3v2_FRAME_PLAYCOUNTER, ID3_PLAYCOUNTER_FRAME },
104 { "POP", "POPM", "POPM", "Popularimeter", "", ID3v2_FRAME_POPULARITY, ID3_POPULAR_FRAME }
23 {"", "", "", "Unknown frame", "", ID3v2_UNKNOWN_FRAME, ID3_UNKNOWN_FRAME},
24 {"TAL",
25 "TALB",
26 "TALB",
27 "Album/Movie/Show title",
28 "album",
29 ID3v2_FRAME_ALBUM,
30 ID3_TEXT_FRAME},
31 {"TBP",
32 "TBPM",
33 "TBPM",
34 "BPM (beats per minute)",
35 "bpm",
36 ID3v2_FRAME_BPM,
37 ID3_TEXT_FRAME},
38 {"TCM",
39 "TCOM",
40 "TCOM",
41 "Composer",
42 "composer",
43 ID3v2_FRAME_COMPOSER,
44 ID3_TEXT_FRAME},
45 {"TCO",
46 "TCON",
47 "TCON",
48 "Content Type/Genre",
49 "genre",
50 ID3v2_FRAME_CONTENTTYPE,
51 ID3_TEXT_FRAME},
52 {"TCP",
53 "TCOP",
54 "TCOP",
55 "Copyright message",
56 "copyright",
57 ID3v2_FRAME_COPYRIGHT,
58 ID3_TEXT_FRAME},
59 {"",
60 "",
61 "TDEN",
62 "Encoding time",
63 "",
64 ID3v2_FRAME_ENCODINGTIME,
65 ID3_TEXT_FRAME},
66 {"TDY",
67 "TDLY",
68 "TDLY",
69 "Playlist delay",
70 "",
71 ID3v2_FRAME_PLAYLISTDELAY,
72 ID3_TEXT_FRAME},
73 {"",
74 "",
75 "TDOR",
76 "Original release time",
77 "",
78 ID3v2_FRAME_ORIGRELTIME,
79 ID3_TEXT_FRAME},
80 {"",
81 "",
82 "TDRC",
83 "Recording time",
84 "date",
85 ID3v2_FRAME_RECORDINGTIME,
86 ID3_TEXT_FRAME},
87 {"",
88 "",
89 "TDRL",
90 "Release time",
91 "released",
92 ID3v2_FRAME_RELEASETIME,
93 ID3_TEXT_FRAME},
94 {"",
95 "",
96 "TDTG",
97 "Tagging time",
98 "tagged",
99 ID3v2_FRAME_TAGGINGTIME,
100 ID3_TEXT_FRAME},
101 {"TEN",
102 "TENC",
103 "TENC",
104 "Encoded by",
105 "encoder",
106 ID3v2_FRAME_ENCODER,
107 ID3_TEXT_FRAME},
108 {"TXT",
109 "TEXT",
110 "TEXT",
111 "Lyricist/Text writer",
112 "writer",
113 ID3v2_FRAME_LYRICIST,
114 ID3_TEXT_FRAME},
115 {"TFT",
116 "TFLT",
117 "TFLT",
118 "File type",
119 "",
120 ID3v2_FRAME_FILETYPE,
121 ID3_TEXT_FRAME},
122 {"",
123 "",
124 "TIPL",
125 "Involved people list",
126 "",
127 ID3v2_FRAME_INVOLVEDPEOPLE,
128 ID3_TEXT_FRAME},
129 {"TT1",
130 "TIT1",
131 "TIT1",
132 "Content group description",
133 "grouping",
134 ID3v2_FRAME_GROUP_DESC,
135 ID3_TEXT_FRAME},
136 {"TT2",
137 "TIT2",
138 "TIT2",
139 "Title/songname/content description",
140 "title",
141 ID3v2_FRAME_TITLE,
142 ID3_TEXT_FRAME},
143 {"TT3",
144 "TIT3",
145 "TIT3",
146 "Subtitle/Description refinement",
147 "subtitle",
148 ID3v2_FRAME_SUBTITLE,
149 ID3_TEXT_FRAME},
150 {"TKE",
151 "TKEY",
152 "TKEY",
153 "Initial key",
154 "",
155 ID3v2_FRAME_INITIALKEY,
156 ID3_TEXT_FRAME},
157 {"TLA",
158 "TLAN",
159 "TLAN",
160 "Language(s)",
161 "",
162 ID3v2_FRAME_LANGUAGE,
163 ID3_TEXT_FRAME},
164 {"TLE",
165 "TLEN",
166 "TLEN",
167 "Length",
168 "",
169 ID3v2_FRAME_TIMELENGTH,
170 ID3_TEXT_FRAME},
171 {"",
172 "",
173 "TMCL",
174 "Musician credits list",
175 "credits",
176 ID3v2_FRAME_MUSICIANLIST,
177 ID3_TEXT_FRAME},
178 {"TMT",
179 "TMED",
180 "TMED",
181 "Media type",
182 "media",
183 ID3v2_FRAME_MEDIATYPE,
184 ID3_TEXT_FRAME},
185 {"", "", "TMOO", "Mood", "mood", ID3v2_FRAME_MOOD, ID3_TEXT_FRAME},
186 {"TOT",
187 "TOAL",
188 "TOAL",
189 "Original album/movie/show title",
190 "",
191 ID3v2_FRAME_ORIGALBUM,
192 ID3_TEXT_FRAME},
193 {"TOF",
194 "TOFN",
195 "TOFN",
196 "Original filename",
197 "",
198 ID3v2_FRAME_ORIGFILENAME,
199 ID3_TEXT_FRAME},
200 {"TOL",
201 "TOLY",
202 "TOLY",
203 "Original lyricist(s)/text writer(s)",
204 "",
205 ID3v2_FRAME_ORIGWRITER,
206 ID3_TEXT_FRAME},
207 {"TOA",
208 "TOPE",
209 "TOPE",
210 "Original artist(s)/performer(s)",
211 "",
212 ID3v2_FRAME_ORIGARTIST,
213 ID3_TEXT_FRAME},
214 {"",
215 "TOWN",
216 "TOWN",
217 "File owner/licensee",
218 "",
219 ID3v2_FRAME_FILEOWNER,
220 ID3_TEXT_FRAME},
221 {"TP1",
222 "TPE1",
223 "TPE1",
224 "Artist/Lead performer(s)/Soloist(s)",
225 "artist",
226 ID3v2_FRAME_ARTIST,
227 ID3_TEXT_FRAME},
228 {"TP2",
229 "TPE2",
230 "TPE2",
231 "Album artist/Band/orchestra/accompaniment",
232 "album artist",
233 ID3v2_FRAME_ALBUMARTIST,
234 ID3_TEXT_FRAME},
235 {"TP3",
236 "TPE3",
237 "TPE3",
238 "Conductor/performer refinement",
239 "conductor",
240 ID3v2_FRAME_CONDUCTOR,
241 ID3_TEXT_FRAME},
242 {"TP4",
243 "TPE4",
244 "TPE4",
245 "Interpreted or remixed by",
246 "remixer",
247 ID3v2_FRAME_REMIXER,
248 ID3_TEXT_FRAME},
249 {"TPA",
250 "TPOS",
251 "TPOS",
252 "Part of a set",
253 "",
254 ID3v2_FRAME_PART_O_SET,
255 ID3_TEXT_FRAME},
256 {"",
257 "",
258 "TPRO",
259 "Produced notice",
260 "",
261 ID3v2_FRAME_PRODNOTICE,
262 ID3_TEXT_FRAME},
263 {"TPB",
264 "TPUB",
265 "TPUB",
266 "Publisher",
267 "publisher",
268 ID3v2_FRAME_PUBLISHER,
269 ID3_TEXT_FRAME},
270 {"TRK",
271 "TRCK",
272 "TRCK",
273 "Track number/Position in set",
274 "trk#",
275 ID3v2_FRAME_TRACKNUM,
276 ID3_TEXT_FRAME},
277 {"",
278 "TRSN",
279 "TRSN",
280 "Internet radio station name",
281 "",
282 ID3v2_FRAME_IRADIONAME,
283 ID3_TEXT_FRAME},
284 {"",
285 "TRSO",
286 "TRSO",
287 "Internet radio station owner",
288 "",
289 ID3v2_FRAME_IRADIOOWNER,
290 ID3_TEXT_FRAME},
291 {"",
292 "",
293 "TSOA",
294 "Album sort order",
295 "",
296 ID3v2_FRAME_ALBUMSORT,
297 ID3_TEXT_FRAME},
298 {"",
299 "",
300 "TSOP",
301 "Performer sort order",
302 "",
303 ID3v2_FRAME_PERFORMERSORT,
304 ID3_TEXT_FRAME},
305 {"",
306 "",
307 "TSOT",
308 "Title sort order",
309 "",
310 ID3v2_FRAME_TITLESORT,
311 ID3_TEXT_FRAME},
312 {"TRC", "TSRC", "TSRC", "ISRC", "", ID3v2_FRAME_ISRC, ID3_TEXT_FRAME},
313 {"TSS",
314 "TSSE",
315 "TSSE",
316 "Software/Hardware and settings used for encoding",
317 "",
318 ID3v2_FRAME_ENCODINGSETTINGS,
319 ID3_TEXT_FRAME},
320 {"",
321 "",
322 "TSST",
323 "Set subtitle",
324 "",
325 ID3v2_FRAME_SETSUBTITLE,
326 ID3_TEXT_FRAME},
327
328 {"TDA", "TDAT", "", "Date", "", ID3v2_DATE, ID3_TEXT_FRAME},
329 {"TIM", "TIME", "", "TIME", "", ID3v2_TIME, ID3_TEXT_FRAME},
330 {"TOR",
331 "TORY",
332 "",
333 "Original Release Year",
334 "",
335 ID3v2_ORIGRELYEAR,
336 ID3_TEXT_FRAME},
337 {"TRD",
338 "TRDA",
339 "",
340 "Recording dates",
341 "",
342 ID3v2_RECORDINGDATE,
343 ID3_TEXT_FRAME},
344 {"TSI", "TSIZ", "", "Size", "", ID3v2_FRAME_SIZE, ID3_TEXT_FRAME},
345 {"TYE", "TYER", "", "YEAR", "", ID3v2_FRAME_YEAR, ID3_TEXT_FRAME},
346
347 {"TXX",
348 "TXXX",
349 "TXXX",
350 "User defined text information frame",
351 "",
352 ID3v2_FRAME_USERDEF_TEXT,
353 ID3_TEXT_FRAME_USERDEF},
354
355 // some of these (like WCOM, WOAF) allow for muliple frames - but (sigh)
356 // alas, such is not the case in AP.
357 {"WCM",
358 "WCOM",
359 "WCOM",
360 "Commercial information",
361 "",
362 ID3v2_FRAME_URLCOMMINFO,
363 ID3_URL_FRAME},
364 {"WCP",
365 "WCOP",
366 "WCOP",
367 "Copyright/Legal information",
368 "",
369 ID3v2_FRAME_URLCOPYRIGHT,
370 ID3_URL_FRAME},
371 {"WAF",
372 "WOAF",
373 "WOAF",
374 "Official audio file webpage",
375 "",
376 ID3v2_FRAME_URLAUDIOFILE,
377 ID3_URL_FRAME},
378 {"WAR",
379 "WOAR",
380 "WOAR",
381 "Official artist/performer webpage",
382 "",
383 ID3v2_FRAME_URLARTIST,
384 ID3_URL_FRAME},
385 {"WAS",
386 "WOAS",
387 "WOAS",
388 "Official audio source webpage",
389 "",
390 ID3v2_FRAME_URLAUDIOSOURCE,
391 ID3_URL_FRAME},
392 {"",
393 "WORS",
394 "WORS",
395 "Official Internet radio station homepage",
396 "",
397 ID3v2_FRAME_URLIRADIO,
398 ID3_URL_FRAME},
399 {"", "WPAY", "WPAY", "Payment", "", ID3v2_FRAME_URLPAYMENT, ID3_URL_FRAME},
400 {"WPB",
401 "WPUB",
402 "WPUB",
403 "Publishers official webpage",
404 "",
405 ID3v2_FRAME_URLPUBLISHER,
406 ID3_URL_FRAME},
407 {"WXX",
408 "WXXX",
409 "WXXX",
410 "User defined URL link frame",
411 "",
412 ID3v2_FRAME_USERDEF_URL,
413 ID3_URL_FRAME_USERDEF},
414
415 {"UFI",
416 "UFID",
417 "UFID",
418 "Unique file identifier",
419 "",
420 ID3v2_FRAME_UFID,
421 ID3_UNIQUE_FILE_ID_FRAME},
422 {"MCI",
423 "MCID",
424 "MCDI",
425 "Music CD Identifier",
426 "",
427 ID3v2_FRAME_MUSIC_CD_ID,
428 ID3_CD_ID_FRAME},
429
430 {"COM",
431 "COMM",
432 "COMM",
433 "Comment",
434 "comment",
435 ID3v2_FRAME_COMMENT,
436 ID3_DESCRIBED_TEXT_FRAME},
437 {"ULT",
438 "USLT",
439 "USLT",
440 "Unsynchronised lyrics",
441 "lyrics",
442 ID3v2_FRAME_UNSYNCLYRICS,
443 ID3_DESCRIBED_TEXT_FRAME},
444
445 {"",
446 "APIC",
447 "APIC",
448 "Attached picture",
449 "",
450 ID3v2_EMBEDDED_PICTURE,
451 ID3_ATTACHED_PICTURE_FRAME},
452 {"PIC",
453 "",
454 "",
455 "Attached picture",
456 "",
457 ID3v2_EMBEDDED_PICTURE_V2P2,
458 ID3_OLD_V2P2_PICTURE_FRAME},
459 {"GEO",
460 "GEOB",
461 "GEOB",
462 "Attached object",
463 "",
464 ID3v2_EMBEDDED_OBJECT,
465 ID3_ATTACHED_OBJECT_FRAME},
466
467 {"",
468 "GRID",
469 "GRID",
470 "Group ID registration",
471 "",
472 ID3v2_FRAME_GRID,
473 ID3_GROUP_ID_FRAME},
474 {"",
475 "",
476 "SIGN",
477 "Signature",
478 "",
479 ID3v2_FRAME_SIGNATURE,
480 ID3_SIGNATURE_FRAME},
481 {"",
482 "PRIV",
483 "PRIV",
484 "Private frame",
485 "",
486 ID3v2_FRAME_PRIVATE,
487 ID3_PRIVATE_FRAME},
488 {"CNT",
489 "PCNT",
490 "PCNT",
491 "Play counter",
492 "",
493 ID3v2_FRAME_PLAYCOUNTER,
494 ID3_PLAYCOUNTER_FRAME},
495 {"POP",
496 "POPM",
497 "POPM",
498 "Popularimeter",
499 "",
500 ID3v2_FRAME_POPULARITY,
501 ID3_POPULAR_FRAME}
105502
106503 };
107504
108 //the field listing array is mostly used for mental clarification instead of internal use - frames are parsed/rendered hardcoded irrespective of ordering here
505 // the field listing array is mostly used for mental clarification instead of
506 // internal use - frames are parsed/rendered hardcoded irrespective of ordering
507 // here
109508 ID3v2FieldDefinition FrameTypeConstructionList[] = {
110 { ID3_UNKNOWN_FRAME, 1, { ID3_UNKNOWN_FIELD } },
111 { ID3_TEXT_FRAME, 2, { ID3_TEXT_ENCODING_FIELD, ID3_TEXT_FIELD } },
112 { ID3_TEXT_FRAME_USERDEF, 3, { ID3_TEXT_ENCODING_FIELD, ID3_DESCRIPTION_FIELD, ID3_TEXT_FIELD } },
113 { ID3_URL_FRAME, 1, { ID3_URL_FIELD } },
114 { ID3_URL_FRAME_USERDEF, 3, { ID3_TEXT_ENCODING_FIELD, ID3_DESCRIPTION_FIELD, ID3_URL_FIELD } },
115 { ID3_UNIQUE_FILE_ID_FRAME, 2, { ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD } },
116 { ID3_CD_ID_FRAME, 1, { ID3_BINARY_DATA_FIELD } },
117 { ID3_DESCRIBED_TEXT_FRAME, 4, { ID3_TEXT_ENCODING_FIELD, ID3_LANGUAGE_FIELD, ID3_DESCRIPTION_FIELD, ID3_TEXT_FIELD } },
118 { ID3_ATTACHED_PICTURE_FRAME, 5, { ID3_TEXT_ENCODING_FIELD, ID3_MIME_TYPE_FIELD, ID3_PIC_TYPE_FIELD, ID3_DESCRIPTION_FIELD, ID3_BINARY_DATA_FIELD } },
119 { ID3_ATTACHED_OBJECT_FRAME, 5, { ID3_TEXT_ENCODING_FIELD, ID3_MIME_TYPE_FIELD, ID3_FILENAME_FIELD, ID3_DESCRIPTION_FIELD, ID3_BINARY_DATA_FIELD } },
120 { ID3_GROUP_ID_FRAME, 3, { ID3_OWNER_FIELD, ID3_GROUPSYMBOL_FIELD, ID3_BINARY_DATA_FIELD } },
121 { ID3_SIGNATURE_FRAME, 2, { ID3_GROUPSYMBOL_FIELD, ID3_BINARY_DATA_FIELD } },
122 { ID3_PRIVATE_FRAME, 2, { ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD } },
123 { ID3_PLAYCOUNTER_FRAME, 1, { ID3_COUNTER_FIELD } },
124 { ID3_POPULAR_FRAME, 3, { ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD, ID3_COUNTER_FIELD } },
125 { ID3_OLD_V2P2_PICTURE_FRAME, 5, { ID3_TEXT_ENCODING_FIELD, ID3_IMAGEFORMAT_FIELD, ID3_PIC_TYPE_FIELD, ID3_DESCRIPTION_FIELD, ID3_BINARY_DATA_FIELD } }
126 };
127
128 //used to determine mimetype for APIC image writing
509 {ID3_UNKNOWN_FRAME, 1, {ID3_UNKNOWN_FIELD}},
510 {ID3_TEXT_FRAME, 2, {ID3_TEXT_ENCODING_FIELD, ID3_TEXT_FIELD}},
511 {ID3_TEXT_FRAME_USERDEF,
512 3,
513 {ID3_TEXT_ENCODING_FIELD, ID3_DESCRIPTION_FIELD, ID3_TEXT_FIELD}},
514 {ID3_URL_FRAME, 1, {ID3_URL_FIELD}},
515 {ID3_URL_FRAME_USERDEF,
516 3,
517 {ID3_TEXT_ENCODING_FIELD, ID3_DESCRIPTION_FIELD, ID3_URL_FIELD}},
518 {ID3_UNIQUE_FILE_ID_FRAME, 2, {ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD}},
519 {ID3_CD_ID_FRAME, 1, {ID3_BINARY_DATA_FIELD}},
520 {ID3_DESCRIBED_TEXT_FRAME,
521 4,
522 {ID3_TEXT_ENCODING_FIELD,
523 ID3_LANGUAGE_FIELD,
524 ID3_DESCRIPTION_FIELD,
525 ID3_TEXT_FIELD}},
526 {ID3_ATTACHED_PICTURE_FRAME,
527 5,
528 {ID3_TEXT_ENCODING_FIELD,
529 ID3_MIME_TYPE_FIELD,
530 ID3_PIC_TYPE_FIELD,
531 ID3_DESCRIPTION_FIELD,
532 ID3_BINARY_DATA_FIELD}},
533 {ID3_ATTACHED_OBJECT_FRAME,
534 5,
535 {ID3_TEXT_ENCODING_FIELD,
536 ID3_MIME_TYPE_FIELD,
537 ID3_FILENAME_FIELD,
538 ID3_DESCRIPTION_FIELD,
539 ID3_BINARY_DATA_FIELD}},
540 {ID3_GROUP_ID_FRAME,
541 3,
542 {ID3_OWNER_FIELD, ID3_GROUPSYMBOL_FIELD, ID3_BINARY_DATA_FIELD}},
543 {ID3_SIGNATURE_FRAME, 2, {ID3_GROUPSYMBOL_FIELD, ID3_BINARY_DATA_FIELD}},
544 {ID3_PRIVATE_FRAME, 2, {ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD}},
545 {ID3_PLAYCOUNTER_FRAME, 1, {ID3_COUNTER_FIELD}},
546 {ID3_POPULAR_FRAME,
547 3,
548 {ID3_OWNER_FIELD, ID3_BINARY_DATA_FIELD, ID3_COUNTER_FIELD}},
549 {ID3_OLD_V2P2_PICTURE_FRAME,
550 5,
551 {ID3_TEXT_ENCODING_FIELD,
552 ID3_IMAGEFORMAT_FIELD,
553 ID3_PIC_TYPE_FIELD,
554 ID3_DESCRIPTION_FIELD,
555 ID3_BINARY_DATA_FIELD}}};
556
557 // used to determine mimetype for APIC image writing
129558 ImageFileFormatDefinition ImageList[] = {
130 { "image/jpeg", ".jpg", 4, "\xFF\xD8\xFF\xE0" },
131 { "image/jpeg", ".jpg", 4, "\xFF\xD8\xFF\xE1" },
132 { "image/png", ".png", 8, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" },
133 { "image/pdf", ".pdf", 7, "%PDF-1." },
134 { "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" },
135 { "image/gif", ".gif", 6, "GIF89a" },
136 { "image/tiff", ".tiff", 4, "\x4D\x4D\x00\x2A" },
137 { "image/tiff", ".tiff", 4, "\x49\x49\x2A\x00" },
138 { "image/bmp", ".bmp", 2, "\x42\x4D" },
139 { "image/bmp", ".bmp", 2, "\x42\x41" },
140 { "image/photoshop", ".psd", 4, "8BPS" },
141 { "image/other", ".img", 0, "" }
142 };
559 {"image/jpeg", ".jpg", 3, "\xFF\xD8\xFF"},
560 {"image/png", ".png", 8, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"},
561 {"image/pdf", ".pdf", 7, "%PDF-1."},
562 {"image/jp2",
563 ".jp2",
564 12,
565 "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A\x00\x00\x00\x14\x66\x74"
566 "\x79\x70\x6A\x70\x32\x20"},
567 {"image/gif", ".gif", 6, "GIF89a"},
568 {"image/tiff", ".tiff", 4, "\x4D\x4D\x00\x2A"},
569 {"image/tiff", ".tiff", 4, "\x49\x49\x2A\x00"},
570 {"image/bmp", ".bmp", 2, "\x42\x4D"},
571 {"image/bmp", ".bmp", 2, "\x42\x41"},
572 {"image/photoshop", ".psd", 4, "8BPS"},
573 {"image/other", ".img", 0, ""}};
143574
144575 ID3ImageType ImageTypeList[] = {
145 { 0x00, "0x00", "Other" },
146 { 0x01, "0x01", "32x32 pixels 'file icon' (PNG only)" },
147 { 0x02, "0x02", "Other file icon" },
148 { 0x03, "0x03", "Cover (front)" },
149 { 0x04, "0x04", "Cover (back)" },
150 { 0x05, "0x05", "Leaflet page" },
151 { 0x06, "0x06", "Media (e.g. label side of CD)" },
152 { 0x07, "0x07", "Lead artist/lead performer/soloist" },
153 { 0x08, "0x08", "Artist/performer" },
154 { 0x09, "0x09", "Conductor" },
155 { 0x0A, "0x0A", "Band/Orchestra" },
156 { 0x0B, "0x0B", "Composer" },
157 { 0x0C, "0x0C", "Lyricist/text writer" },
158 { 0x0D, "0x0D", "Recording Location" },
159 { 0x0E, "0x0E", "During recording" },
160 { 0x0F, "0x0F", "During performance" },
161 { 0x10, "0x10", "Movie/video screen capture" },
162 { 0x11, "0x11", "A bright coloured fish" },
163 { 0x12, "0x12", "Illustration" },
164 { 0x13, "0x13", "Band/artist logotype" },
165 { 0x14, "0x14", "Publisher/Studio logotype" }
166 };
576 {0x00, "0x00", "Other"},
577 {0x01, "0x01", "32x32 pixels 'file icon' (PNG only)"},
578 {0x02, "0x02", "Other file icon"},
579 {0x03, "0x03", "Cover (front)"},
580 {0x04, "0x04", "Cover (back)"},
581 {0x05, "0x05", "Leaflet page"},
582 {0x06, "0x06", "Media (e.g. label side of CD)"},
583 {0x07, "0x07", "Lead artist/lead performer/soloist"},
584 {0x08, "0x08", "Artist/performer"},
585 {0x09, "0x09", "Conductor"},
586 {0x0A, "0x0A", "Band/Orchestra"},
587 {0x0B, "0x0B", "Composer"},
588 {0x0C, "0x0C", "Lyricist/text writer"},
589 {0x0D, "0x0D", "Recording Location"},
590 {0x0E, "0x0E", "During recording"},
591 {0x0F, "0x0F", "During performance"},
592 {0x10, "0x10", "Movie/video screen capture"},
593 {0x11, "0x11", "A bright coloured fish"},
594 {0x12, "0x12", "Illustration"},
595 {0x13, "0x13", "Band/artist logotype"},
596 {0x14, "0x14", "Publisher/Studio logotype"}};
11 /*
22 AtomicParsley - id3v2types.h
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
2020 //==================================================================//
2121
2222 enum ID3_FieldTypes {
23 ID3_UNKNOWN_FIELD = -1,
24 ID3_TEXT_FIELD,
25 ID3_TEXT_ENCODING_FIELD,
26 ID3_OWNER_FIELD, //UFID,PRIV
27 ID3_DESCRIPTION_FIELD, //TXXX, WXXX
28 ID3_URL_FIELD,
29 ID3_LANGUAGE_FIELD, //USLT
30 ID3_MIME_TYPE_FIELD, //APIC
31 ID3_PIC_TYPE_FIELD, //APIC
32 ID3_BINARY_DATA_FIELD, //APIC,GEOB
33 ID3_FILENAME_FIELD, //GEOB
34 ID3_GROUPSYMBOL_FIELD,
35 ID3_COUNTER_FIELD,
36 ID3_IMAGEFORMAT_FIELD //PIC in v2.2
37 };
38
39 //the order of these frame types must exactly match the order listed in the FrameTypeConstructionList[] array!!!
23 ID3_UNKNOWN_FIELD = -1,
24 ID3_TEXT_FIELD,
25 ID3_TEXT_ENCODING_FIELD,
26 ID3_OWNER_FIELD, // UFID,PRIV
27 ID3_DESCRIPTION_FIELD, // TXXX, WXXX
28 ID3_URL_FIELD,
29 ID3_LANGUAGE_FIELD, // USLT
30 ID3_MIME_TYPE_FIELD, // APIC
31 ID3_PIC_TYPE_FIELD, // APIC
32 ID3_BINARY_DATA_FIELD, // APIC,GEOB
33 ID3_FILENAME_FIELD, // GEOB
34 ID3_GROUPSYMBOL_FIELD,
35 ID3_COUNTER_FIELD,
36 ID3_IMAGEFORMAT_FIELD // PIC in v2.2
37 };
38
39 // the order of these frame types must exactly match the order listed in the
40 // FrameTypeConstructionList[] array!!!
4041 enum ID3v2FrameType {
41 ID3_UNKNOWN_FRAME = -1,
42 ID3_TEXT_FRAME,
43 ID3_TEXT_FRAME_USERDEF,
44 ID3_URL_FRAME,
45 ID3_URL_FRAME_USERDEF,
46 ID3_UNIQUE_FILE_ID_FRAME,
47 ID3_CD_ID_FRAME,
48 ID3_DESCRIBED_TEXT_FRAME, //oy... these frames (COMM, USLT) can differ by description
49 ID3_ATTACHED_PICTURE_FRAME,
50 ID3_ATTACHED_OBJECT_FRAME,
51 ID3_GROUP_ID_FRAME,
52 ID3_SIGNATURE_FRAME,
53 ID3_PRIVATE_FRAME,
54 ID3_PLAYCOUNTER_FRAME,
55 ID3_POPULAR_FRAME,
56 ID3_OLD_V2P2_PICTURE_FRAME
57 };
58
59 //the order of these frames must exactly match the order listed in the KnownFrames[] array!!!
42 ID3_UNKNOWN_FRAME = -1,
43 ID3_TEXT_FRAME,
44 ID3_TEXT_FRAME_USERDEF,
45 ID3_URL_FRAME,
46 ID3_URL_FRAME_USERDEF,
47 ID3_UNIQUE_FILE_ID_FRAME,
48 ID3_CD_ID_FRAME,
49 ID3_DESCRIBED_TEXT_FRAME, // oy... these frames (COMM, USLT) can differ by
50 // description
51 ID3_ATTACHED_PICTURE_FRAME,
52 ID3_ATTACHED_OBJECT_FRAME,
53 ID3_GROUP_ID_FRAME,
54 ID3_SIGNATURE_FRAME,
55 ID3_PRIVATE_FRAME,
56 ID3_PLAYCOUNTER_FRAME,
57 ID3_POPULAR_FRAME,
58 ID3_OLD_V2P2_PICTURE_FRAME
59 };
60
61 // the order of these frames must exactly match the order listed in the
62 // KnownFrames[] array!!!
6063 enum ID3v2FrameIDs {
61 ID3v2_UNKNOWN_FRAME = -1,
62 ID3v2_FRAME_ALBUM,
63 ID3v2_FRAME_BPM,
64 ID3v2_FRAME_COMPOSER,
65 ID3v2_FRAME_CONTENTTYPE,
66 ID3v2_FRAME_COPYRIGHT,
67 ID3v2_FRAME_ENCODINGTIME,
68 ID3v2_FRAME_PLAYLISTDELAY,
69 ID3v2_FRAME_ORIGRELTIME,
70 ID3v2_FRAME_RECORDINGTIME,
71 ID3v2_FRAME_RELEASETIME,
72 ID3v2_FRAME_TAGGINGTIME,
73 ID3v2_FRAME_ENCODER,
74 ID3v2_FRAME_LYRICIST,
75 ID3v2_FRAME_FILETYPE,
76 ID3v2_FRAME_INVOLVEDPEOPLE,
77 ID3v2_FRAME_GROUP_DESC,
78 ID3v2_FRAME_TITLE,
79 ID3v2_FRAME_SUBTITLE,
80 ID3v2_FRAME_INITIALKEY,
81 ID3v2_FRAME_LANGUAGE,
82 ID3v2_FRAME_TIMELENGTH,
83 ID3v2_FRAME_MUSICIANLIST,
84 ID3v2_FRAME_MEDIATYPE,
85 ID3v2_FRAME_MOOD,
86 ID3v2_FRAME_ORIGALBUM,
87 ID3v2_FRAME_ORIGFILENAME,
88 ID3v2_FRAME_ORIGWRITER,
89 ID3v2_FRAME_ORIGARTIST,
90 ID3v2_FRAME_FILEOWNER,
91 ID3v2_FRAME_ARTIST,
92 ID3v2_FRAME_ALBUMARTIST,
93 ID3v2_FRAME_CONDUCTOR,
94 ID3v2_FRAME_REMIXER,
95 ID3v2_FRAME_PART_O_SET,
96 ID3v2_FRAME_PRODNOTICE,
97 ID3v2_FRAME_PUBLISHER,
98 ID3v2_FRAME_TRACKNUM,
99 ID3v2_FRAME_IRADIONAME,
100 ID3v2_FRAME_IRADIOOWNER,
101 ID3v2_FRAME_ALBUMSORT,
102 ID3v2_FRAME_PERFORMERSORT,
103 ID3v2_FRAME_TITLESORT,
104 ID3v2_FRAME_ISRC,
105 ID3v2_FRAME_ENCODINGSETTINGS,
106 ID3v2_FRAME_SETSUBTITLE,
107 ID3v2_DATE,
108 ID3v2_TIME,
109 ID3v2_ORIGRELYEAR,
110 ID3v2_RECORDINGDATE,
111 ID3v2_FRAME_SIZE,
112 ID3v2_FRAME_YEAR,
113 ID3v2_FRAME_USERDEF_TEXT,
114 ID3v2_FRAME_URLCOMMINFO,
115 ID3v2_FRAME_URLCOPYRIGHT,
116 ID3v2_FRAME_URLAUDIOFILE,
117 ID3v2_FRAME_URLARTIST,
118 ID3v2_FRAME_URLAUDIOSOURCE,
119 ID3v2_FRAME_URLIRADIO,
120 ID3v2_FRAME_URLPAYMENT,
121 ID3v2_FRAME_URLPUBLISHER,
122 ID3v2_FRAME_USERDEF_URL,
123 ID3v2_FRAME_UFID,
124 ID3v2_FRAME_MUSIC_CD_ID,
125 ID3v2_FRAME_COMMENT,
126 ID3v2_FRAME_UNSYNCLYRICS,
127 ID3v2_EMBEDDED_PICTURE,
128 ID3v2_EMBEDDED_PICTURE_V2P2,
129 ID3v2_EMBEDDED_OBJECT,
130 ID3v2_FRAME_GRID,
131 ID3v2_FRAME_SIGNATURE,
132 ID3v2_FRAME_PRIVATE,
133 ID3v2_FRAME_PLAYCOUNTER,
134 ID3v2_FRAME_POPULARITY
64 ID3v2_UNKNOWN_FRAME = -1,
65 ID3v2_FRAME_ALBUM,
66 ID3v2_FRAME_BPM,
67 ID3v2_FRAME_COMPOSER,
68 ID3v2_FRAME_CONTENTTYPE,
69 ID3v2_FRAME_COPYRIGHT,
70 ID3v2_FRAME_ENCODINGTIME,
71 ID3v2_FRAME_PLAYLISTDELAY,
72 ID3v2_FRAME_ORIGRELTIME,
73 ID3v2_FRAME_RECORDINGTIME,
74 ID3v2_FRAME_RELEASETIME,
75 ID3v2_FRAME_TAGGINGTIME,
76 ID3v2_FRAME_ENCODER,
77 ID3v2_FRAME_LYRICIST,
78 ID3v2_FRAME_FILETYPE,
79 ID3v2_FRAME_INVOLVEDPEOPLE,
80 ID3v2_FRAME_GROUP_DESC,
81 ID3v2_FRAME_TITLE,
82 ID3v2_FRAME_SUBTITLE,
83 ID3v2_FRAME_INITIALKEY,
84 ID3v2_FRAME_LANGUAGE,
85 ID3v2_FRAME_TIMELENGTH,
86 ID3v2_FRAME_MUSICIANLIST,
87 ID3v2_FRAME_MEDIATYPE,
88 ID3v2_FRAME_MOOD,
89 ID3v2_FRAME_ORIGALBUM,
90 ID3v2_FRAME_ORIGFILENAME,
91 ID3v2_FRAME_ORIGWRITER,
92 ID3v2_FRAME_ORIGARTIST,
93 ID3v2_FRAME_FILEOWNER,
94 ID3v2_FRAME_ARTIST,
95 ID3v2_FRAME_ALBUMARTIST,
96 ID3v2_FRAME_CONDUCTOR,
97 ID3v2_FRAME_REMIXER,
98 ID3v2_FRAME_PART_O_SET,
99 ID3v2_FRAME_PRODNOTICE,
100 ID3v2_FRAME_PUBLISHER,
101 ID3v2_FRAME_TRACKNUM,
102 ID3v2_FRAME_IRADIONAME,
103 ID3v2_FRAME_IRADIOOWNER,
104 ID3v2_FRAME_ALBUMSORT,
105 ID3v2_FRAME_PERFORMERSORT,
106 ID3v2_FRAME_TITLESORT,
107 ID3v2_FRAME_ISRC,
108 ID3v2_FRAME_ENCODINGSETTINGS,
109 ID3v2_FRAME_SETSUBTITLE,
110 ID3v2_DATE,
111 ID3v2_TIME,
112 ID3v2_ORIGRELYEAR,
113 ID3v2_RECORDINGDATE,
114 ID3v2_FRAME_SIZE,
115 ID3v2_FRAME_YEAR,
116 ID3v2_FRAME_USERDEF_TEXT,
117 ID3v2_FRAME_URLCOMMINFO,
118 ID3v2_FRAME_URLCOPYRIGHT,
119 ID3v2_FRAME_URLAUDIOFILE,
120 ID3v2_FRAME_URLARTIST,
121 ID3v2_FRAME_URLAUDIOSOURCE,
122 ID3v2_FRAME_URLIRADIO,
123 ID3v2_FRAME_URLPAYMENT,
124 ID3v2_FRAME_URLPUBLISHER,
125 ID3v2_FRAME_USERDEF_URL,
126 ID3v2_FRAME_UFID,
127 ID3v2_FRAME_MUSIC_CD_ID,
128 ID3v2_FRAME_COMMENT,
129 ID3v2_FRAME_UNSYNCLYRICS,
130 ID3v2_EMBEDDED_PICTURE,
131 ID3v2_EMBEDDED_PICTURE_V2P2,
132 ID3v2_EMBEDDED_OBJECT,
133 ID3v2_FRAME_GRID,
134 ID3v2_FRAME_SIGNATURE,
135 ID3v2_FRAME_PRIVATE,
136 ID3v2_FRAME_PLAYCOUNTER,
137 ID3v2_FRAME_POPULARITY
135138 };
136139
137140 enum ID3v2_TagFlags {
138 ID32_TAGFLAG_BIT0 = 0x01,
139 ID32_TAGFLAG_BIT1 = 0x02,
140 ID32_TAGFLAG_BIT2 = 0x04,
141 ID32_TAGFLAG_BIT3 = 0x08,
142 ID32_TAGFLAG_FOOTER = 0x10,
143 ID32_TAGFLAG_EXPERIMENTAL = 0x20,
144 ID32_TAGFLAG_EXTENDEDHEADER = 0x40,
145 ID32_TAGFLAG_UNSYNCRONIZATION = 0x80
141 ID32_TAGFLAG_BIT0 = 0x01,
142 ID32_TAGFLAG_BIT1 = 0x02,
143 ID32_TAGFLAG_BIT2 = 0x04,
144 ID32_TAGFLAG_BIT3 = 0x08,
145 ID32_TAGFLAG_FOOTER = 0x10,
146 ID32_TAGFLAG_EXPERIMENTAL = 0x20,
147 ID32_TAGFLAG_EXTENDEDHEADER = 0x40,
148 ID32_TAGFLAG_UNSYNCRONIZATION = 0x80
146149 };
147150
148151 enum ID3v2_FrameFlags {
149 ID32_FRAMEFLAG_STATUS = 0x4000,
150 ID32_FRAMEFLAG_PRESERVE = 0x2000,
151 ID32_FRAMEFLAG_READONLY = 0x1000,
152 ID32_FRAMEFLAG_GROUPING = 0x0040,
153 ID32_FRAMEFLAG_COMPRESSED = 0x0008,
154 ID32_FRAMEFLAG_ENCRYPTED = 0x0004,
155 ID32_FRAMEFLAG_UNSYNCED = 0x0002,
156 ID32_FRAMEFLAG_LENINDICATED = 0x0001
152 ID32_FRAMEFLAG_STATUS = 0x4000,
153 ID32_FRAMEFLAG_PRESERVE = 0x2000,
154 ID32_FRAMEFLAG_READONLY = 0x1000,
155 ID32_FRAMEFLAG_GROUPING = 0x0040,
156 ID32_FRAMEFLAG_COMPRESSED = 0x0008,
157 ID32_FRAMEFLAG_ENCRYPTED = 0x0004,
158 ID32_FRAMEFLAG_UNSYNCED = 0x0002,
159 ID32_FRAMEFLAG_LENINDICATED = 0x0001
157160 };
158161
159162 // the wording of the ID3 (v2.4 in this case) 'informal standard' is not always
189192 // http://sourceforge.net/project/showfiles.php?group_id=979&package_id=4679
190193
191194 enum text_encodings {
192 TE_LATIN1 = 0,
193 TE_UTF16LE_WITH_BOM = 1,
194 TE_UTF16BE_NO_BOM = 2,
195 TE_UTF8 = 3
196 };
197
198 // Structure that defines the (subset) known ID3 frames defined by id3 informal specification.
199 typedef struct {
200 const char* ID3V2p2_FrameID;
201 const char* ID3V2p3_FrameID;
202 const char* ID3V2p4_FrameID;
203 const char* ID3V2_FrameDescription;
204 const char* CLI_frameIDpreset;
205 int ID3v2_InternalFrameID;
206 int ID3v2_FrameType;
195 TE_LATIN1 = 0,
196 TE_UTF16LE_WITH_BOM = 1,
197 TE_UTF16BE_NO_BOM = 2,
198 TE_UTF8 = 3
199 };
200
201 // Structure that defines the (subset) known ID3 frames defined by id3 informal
202 // specification.
203 typedef struct {
204 const char *ID3V2p2_FrameID;
205 const char *ID3V2p3_FrameID;
206 const char *ID3V2p4_FrameID;
207 const char *ID3V2_FrameDescription;
208 const char *CLI_frameIDpreset;
209 int ID3v2_InternalFrameID;
210 int ID3v2_FrameType;
207211 } ID3FrameDefinition;
208212
209213 typedef struct {
210 const char* image_mimetype;
211 const char* image_fileextn;
212 uint8_t image_testbytes;
213 const char* image_binaryheader;
214 const char *image_mimetype;
215 const char *image_fileextn;
216 uint8_t image_testbytes;
217 const char *image_binaryheader;
214218 } ImageFileFormatDefinition;
215219
216220 typedef struct {
217 uint8_t hexcode;
218 const char* hexstring;
219 const char* imagetype_str;
221 uint8_t hexcode;
222 const char *hexstring;
223 const char *imagetype_str;
220224 } ID3ImageType;
221225
222 // Structure that defines how any ID3v2FrameType is constructed, listing an array of its constituent ID3_FieldTypes
223 typedef struct {
224 ID3v2FrameType ID3_FrameType;
225 uint8_t ID3_FieldCount;
226 ID3_FieldTypes ID3_FieldComponents[5]; //max known to be tested
226 // Structure that defines how any ID3v2FrameType is constructed, listing an
227 // array of its constituent ID3_FieldTypes
228 typedef struct {
229 ID3v2FrameType ID3_FrameType;
230 uint8_t ID3_FieldCount;
231 ID3_FieldTypes ID3_FieldComponents[5]; // max known to be tested
227232 } ID3v2FieldDefinition;
228233
229234 struct ID3v2Fields {
230 int ID3v2_Field_Type;
231 uint32_t field_length;
232 uint32_t alloc_length;
233 char* field_string;
234 ID3v2Fields* next_field;
235 int ID3v2_Field_Type;
236 uint32_t field_length;
237 uint32_t alloc_length;
238 char *field_string;
239 ID3v2Fields *next_field;
235240 };
236241
237242 struct ID3v2Frame {
238 char ID3v2_Frame_Namestr[5];
239 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)
240 uint16_t ID3v2_Frame_Flags;
241
242 //these next 2 values can be potentially be stored based on bitsetting in frame flags;
243 uint8_t ID3v2_Frame_GroupingSymbol;
244 uint32_t ID3v2_Frame_ExpandedLength;
245
246 int ID3v2_Frame_ID;
247 int ID3v2_FrameType;
248 uint8_t ID3v2_FieldCount;
249 uint8_t textfield_tally;
250 ID3v2Fields* ID3v2_Frame_Fields; //malloc
251 ID3v2Frame* ID3v2_NextFrame;
252 bool eliminate_frame;
253 };
254
255 struct ID3v2Tag {
256 uint8_t ID3v2Tag_MajorVersion;
257 uint8_t ID3v2Tag_RevisionVersion;
258 uint8_t ID3v2Tag_Flags;
259 uint32_t ID3v2Tag_Length; //this is a bonafide uint_32_t length, not a syncsafe int
260
261 //this extended header section depends on a bitsetting in ID3v2Tag_Flags
262 uint32_t ID3v2_Tag_ExtendedHeader_Length; //the entire extended header section is unimplemented flags & flag frames
263
264 ID3v2Frame* ID3v2_FirstFrame;
265 ID3v2Frame* ID3v2_FrameList;
266 uint16_t ID3v2_FrameCount;
267 bool modified_tag;
268 };
243 char ID3v2_Frame_Namestr[5];
244 uint32_t ID3v2_Frame_Length; // this is the real length, not a syncsafe int;
245 // note: does not include frame ID (like 'TIT2',
246 // 'TCO' - 3or4 bytes) or frame flags (2bytes)
247 uint16_t ID3v2_Frame_Flags;
248
249 // these next 2 values can be potentially be stored based on bitsetting in
250 // frame flags;
251 uint8_t ID3v2_Frame_GroupingSymbol;
252 uint32_t ID3v2_Frame_ExpandedLength;
253
254 int ID3v2_Frame_ID;
255 int ID3v2_FrameType;
256 uint8_t ID3v2_FieldCount;
257 uint8_t textfield_tally;
258 ID3v2Fields *ID3v2_Frame_Fields; // malloc
259 ID3v2Frame *ID3v2_NextFrame;
260 bool eliminate_frame;
261 };
262
263 struct ID3v2Tag {
264 uint8_t ID3v2Tag_MajorVersion;
265 uint8_t ID3v2Tag_RevisionVersion;
266 uint8_t ID3v2Tag_Flags;
267 uint32_t ID3v2Tag_Length; // this is a bonafide uint_32_t length, not a
268 // syncsafe int
269
270 // this extended header section depends on a bitsetting in ID3v2Tag_Flags
271 uint32_t
272 ID3v2_Tag_ExtendedHeader_Length; // the entire extended header section is
273 // unimplemented flags & flag frames
274
275 ID3v2Frame *ID3v2_FirstFrame;
276 ID3v2Frame *ID3v2_FrameList;
277 uint16_t ID3v2_FrameCount;
278 bool modified_tag;
279 };
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2005-2007 puck_lock
17 Copyright (c) 2005-2007 puck_lock
1818 with contributions from others; see the CREDITS file
1919
2020 ----------------------
2828 #include "AtomicParsley.h"
2929
3030 // define one-letter cli options for
31 #define OPT_HELP 'h'
32 #define OPT_TEST 'T'
33 #define OPT_ShowTextData 't'
34 #define OPT_ExtractPix 'E'
35 #define OPT_ExtractPixToPath 'e'
36 #define Meta_artist 'a'
37 #define Meta_artDirector 0xC6
38 #define Meta_arranger 0xC8
39 #define Meta_author 0xC9
40 #define Meta_conductor 0xD0
41 #define Meta_director 0xD1
42 #define Meta_originalArtist 0xD2
43 #define Meta_producer 0xD3
31 #define OPT_HELP 'h'
32 #define OPT_TEST 'T'
33 #define OPT_ShowTextData 't'
34 #define OPT_ExtractPix 'E'
35 #define OPT_ExtractPixToPath 'e'
36 #define Meta_artist 'a'
37 #define Meta_artDirector 0xC6
38 #define Meta_arranger 0xC8
39 #define Meta_author 0xC9
40 #define Meta_conductor 0xD0
41 #define Meta_director 0xD1
42 #define Meta_originalArtist 0xD2
43 #define Meta_producer 0xD3
4444 //#define Meta_performer 0xD4
45 #define Meta_soundEngineer 0xD5
46 #define Meta_soloist 0xD6
47 #define Meta_executiveProducer 0xD7
48 #define Meta_songtitle 's'
49 #define Meta_subtitle 0xC5
50 #define Meta_album 'b'
51 #define Meta_tracknum 'k'
52 #define Meta_disknum 'd'
53 #define Meta_genre 'g'
54 #define Meta_comment 'c'
55 #define Meta_year 'y'
56 #define Meta_lyrics 'l'
57 #define Meta_lyrics_file 0xC7
58 #define Meta_composer 'w'
59 #define Meta_copyright 'x'
60 #define Meta_grouping 'G'
61 #define Meta_album_artist 'A'
62 #define Meta_compilation 'C'
63 #define Meta_hdvideo 'O'
64 #define Meta_BPM 'B'
65 #define Meta_artwork 'r'
66 #define Meta_advisory 'V'
67 #define Meta_stik 'S'
68 #define Meta_description 'p'
69 #define Meta_Rating 0xCB
70 #define Meta_longdescription 'j'
71 #define Meta_TV_Network 'n'
72 #define Meta_TV_ShowName 'H'
73 #define Meta_TV_EpisodeNumber 'N'
74 #define Meta_TV_SeasonNumber 'U'
75 #define Meta_TV_Episode 'I'
76 #define Meta_podcastFlag 'f'
77 #define Meta_category 'q'
78 #define Meta_keyword 'K'
79 #define Meta_podcast_URL 'L'
80 #define Meta_podcast_GUID 'J'
81 #define Meta_PurchaseDate 'D'
82 #define Meta_apID 'Y'
83 #define Meta_cnID 0xC0
84 #define Meta_geID 0xC2
85 #define Meta_xID 0xC3
86 #define Meta_storedescription 0xC4
87 #define Meta_EncodingTool 0xB7
88 #define Meta_EncodedBy 0xC1
89 #define Meta_PlayGapless 0xBA
90 #define Meta_SortOrder 0xBF
91
92 #define Meta_ReverseDNS_Form 'M'
93 #define Meta_rDNS_rating 0xBB
94
95 #define Meta_StandardDate 'Z'
96 #define Meta_URL 'u'
97 #define Meta_Information 'i'
98 #define Meta_uuid 'z'
99 #define Opt_Extract_all_uuids 0xB8
100 #define Opt_Extract_a_uuid 0xB9
101 #define Opt_Ipod_AVC_uuid 0xBE
102
103 #define Metadata_Purge 'P'
104 #define UserData_Purge 'X'
105 #define foobar_purge '.'
106 #define Meta_dump 'Q'
107 #define Manual_atom_removal 'R'
108 #define Opt_FreeFree 'F'
109 #define OPT_OutputFile 'o'
110 #define OPT_NoOptimize 0xBD
111
112 #define OPT_OverWrite 'W'
113
114 #if defined (_WIN32)
115 #define OPT_PreserveTimeStamps 0xCA
45 #define Meta_soundEngineer 0xD5
46 #define Meta_soloist 0xD6
47 #define Meta_executiveProducer 0xD7
48 #define Meta_songtitle 's'
49 #define Meta_subtitle 0xC5
50 #define Meta_album 'b'
51 #define Meta_tracknum 'k'
52 #define Meta_disknum 'd'
53 #define Meta_genre 'g'
54 #define Meta_comment 'c'
55 #define Meta_year 'y'
56 #define Meta_lyrics 'l'
57 #define Meta_lyrics_file 0xC7
58 #define Meta_composer 'w'
59 #define Meta_copyright 'x'
60 #define Meta_grouping 'G'
61 #define Meta_album_artist 'A'
62 #define Meta_compilation 'C'
63 #define Meta_hdvideo 'O'
64 #define Meta_BPM 'B'
65 #define Meta_artwork 'r'
66 #define Meta_advisory 'V'
67 #define Meta_stik 'S'
68 #define Meta_description 'p'
69 #define Meta_Rating 0xCB
70 #define Meta_longdescription 'j'
71 #define Meta_TV_Network 'n'
72 #define Meta_TV_ShowName 'H'
73 #define Meta_TV_EpisodeNumber 'N'
74 #define Meta_TV_SeasonNumber 'U'
75 #define Meta_TV_Episode 'I'
76 #define Meta_podcastFlag 'f'
77 #define Meta_category 'q'
78 #define Meta_keyword 'K'
79 #define Meta_podcast_URL 'L'
80 #define Meta_podcast_GUID 'J'
81 #define Meta_PurchaseDate 'D'
82 #define Meta_apID 'Y'
83 #define Meta_cnID 0xC0
84 #define Meta_geID 0xC2
85 #define Meta_xID 0xC3
86 #define Meta_storedescription 0xC4
87 #define Meta_EncodingTool 0xB7
88 #define Meta_EncodedBy 0xC1
89 #define Meta_PlayGapless 0xBA
90 #define Meta_SortOrder 0xBF
91
92 #define Meta_ReverseDNS_Form 'M'
93 #define Meta_rDNS_rating 0xBB
94
95 #define Meta_StandardDate 'Z'
96 #define Meta_URL 'u'
97 #define Meta_Information 'i'
98 #define Meta_uuid 'z'
99 #define Opt_Extract_all_uuids 0xB8
100 #define Opt_Extract_a_uuid 0xB9
101 #define Opt_Ipod_AVC_uuid 0xBE
102
103 #define Metadata_Purge 'P'
104 #define UserData_Purge 'X'
105 #define foobar_purge '.'
106 #define Meta_dump 'Q'
107 #define Manual_atom_removal 'R'
108 #define Opt_FreeFree 'F'
109 #define OPT_OutputFile 'o'
110 #define OPT_NoOptimize 0xBD
111
112 #define OPT_OverWrite 'W'
113
114 #if defined(_WIN32)
115 #define OPT_PreserveTimeStamps 0xCA
116116 #endif
117117
118 #define ISO_Copyright 0xAA
119
120 #define _3GP_Title 0xAB
121 #define _3GP_Author 0xAC
122 #define _3GP_Performer 0xAD
123 #define _3GP_Genre 0xAE
124 #define _3GP_Description 0xAF
125 #define _3GP_Copyright 0xB0
126 #define _3GP_Album 0xB1
127 #define _3GP_Year 0xB2
128 #define _3GP_Rating 0xB3
129 #define _3GP_Classification 0xB4
130 #define _3GP_Keyword 0xB5
131 #define _3GP_Location 0xB6
132
133 #define Meta_ID3v2Tag 0xBC
118 #define ISO_Copyright 0xAA
119
120 #define _3GP_Title 0xAB
121 #define _3GP_Author 0xAC
122 #define _3GP_Performer 0xAD
123 #define _3GP_Genre 0xAE
124 #define _3GP_Description 0xAF
125 #define _3GP_Copyright 0xB0
126 #define _3GP_Album 0xB1
127 #define _3GP_Year 0xB2
128 #define _3GP_Rating 0xB3
129 #define _3GP_Classification 0xB4
130 #define _3GP_Keyword 0xB5
131 #define _3GP_Location 0xB6
132
133 #define Meta_ID3v2Tag 0xBC
134
135 #define Meta_movementCount 0xE0
136 #define Meta_movementName 0xE1
137 #define Meta_movementNumber 0xE2
138 #define Meta_showWorkMovement 0xE3
139 #define Meta_work 0xE4
134140
135141 char *output_file;
136142
137143 int total_args;
138144
139 static void kill_signal ( int sig );
140
141 static void kill_signal (int sig) {
145 static void kill_signal(int sig);
146
147 static void kill_signal(int sig) { exit(0); }
148
149 // less than 80 (max 78) char wide, giving a general (concise) overview
150 static const char *shortHelp_text =
151 "\n"
152 "AtomicParsley sets metadata into MPEG-4 files & derivatives supporting 3 "
153 "tag\n"
154 " schemes: iTunes-style, 3GPP assets & ISO defined copyright "
155 "notifications.\n"
156 "\n"
157 "AtomicParsley quick help for setting iTunes-style metadata into MPEG-4 "
158 "files.\n"
159 "\n"
160 "General usage examples:\n"
161 " AtomicParsley /path/to.mp4 -T 1\n"
162 " AtomicParsley /path/to.mp4 -t +\n"
163 " AtomicParsley /path/to.mp4 --artist \"Me\" --artwork /path/to/art.jpg\n"
164 " Atomicparsley /path/to.mp4 --albumArtist \"You\" --podcastFlag true\n"
165 " Atomicparsley /path/to.mp4 --stik \"TV Show\" --advisory explicit\n"
166 "\n"
167 "Getting information about the file & tags:\n"
168 " -T --test Test file for mpeg4-ishness & print atom tree\n"
169 " -t --textdata Prints tags embedded within the file\n"
170 " -E --extractPix Extracts pix to the same folder as the mpeg-4 file\n"
171 "\n"
172 "Setting iTunes-style metadata tags\n"
173 " --artist (string) Set the artist tag\n"
174 " --title (string) Set the title tag\n"
175 " --album (string) Set the album tag\n"
176 " --genre (string) Genre tag (see --longhelp for more info)\n"
177 " --tracknum (num)[/tot] Track number (or track number/total "
178 "tracks)\n"
179 " --disk (num)[/tot] Disk number (or disk number/total disks)\n"
180 " --comment (string) Set the comment tag\n"
181 " --year (num|UTC) Year tag (see --longhelp for \"Release "
182 "Date\")\n"
183 " --lyrics (string) Set lyrics (not subject to 256 byte limit)\n"
184 " --lyricsFile (/path) Set lyrics to the content of a file\n"
185 " --composer (string) Set the composer tag\n"
186 " --copyright (string) Set the copyright tag\n"
187 " --grouping (string) Set the grouping tag\n"
188 " --artwork (/path) Set a piece of artwork (jpeg or png only)\n"
189 " --bpm (number) Set the tempo/bpm\n"
190 " --albumArtist (string) Set the album artist tag\n"
191 " --compilation (boolean) Set the compilation flag (true or false)\n"
192 " --hdvideo (number) Set the hdvideo flag to one of:\n"
193 " false or 0 for standard definition\n"
194 " true or 1 for 720p\n"
195 " 2 for 1080p\n"
196 " --advisory (string*) Content advisory (*values: 'clean', "
197 "'explicit')\n"
198 " --stik (string*) Sets the iTunes \"stik\" atom (see "
199 "--longhelp)\n"
200 " --description (string) Set the description tag\n"
201 " --longdesc (string) Set the long description tag\n"
202 " --storedesc (string) Set the store description tag\n"
203 " --TVNetwork (string) Set the TV Network name\n"
204 " --TVShowName (string) Set the TV Show name\n"
205 " --TVEpisode (string) Set the TV episode/production code\n"
206 " --TVSeasonNum (number) Set the TV Season number\n"
207 " --TVEpisodeNum (number) Set the TV Episode number\n"
208 " --podcastFlag (boolean) Set the podcast flag (true or false)\n"
209 " --category (string) Sets the podcast category\n"
210 " --keyword (string) Sets the podcast keyword\n"
211 " --podcastURL (URL) Set the podcast feed URL\n"
212 " --podcastGUID (URL) Set the episode's URL tag\n"
213 " --purchaseDate (UTC) Set time of purchase\n"
214 " --encodingTool (string) Set the name of the encoder\n"
215 " --encodedBy (string) Set the name of the Person/company who "
216 "encoded the file\n"
217 " --apID (string) Set the Account Name\n"
218 " --cnID (number) Set the iTunes Catalog ID (see --longhelp)\n"
219 " --geID (number) Set the iTunes Genre ID (see --longhelp)\n"
220 " --xID (string) Set the vendor-supplied iTunes xID (see "
221 "--longhelp)\n"
222 " --gapless (boolean) Set the gapless playback flag\n"
223 " --contentRating (string*) Set tv/mpaa rating (see -rDNS-help)\n"
224 "\n"
225 "Deleting tags\n"
226 " Set the value to \"\": --artist \"\" --stik \"\" --bpm \"\"\n"
227 " To delete (all) artwork: --artwork REMOVE_ALL\n"
228 " manually removal: --manualAtomRemove "
229 "\"moov.udta.meta.ilst.ATOM\"\n"
230 "\n"
231 "More detailed iTunes help is available with AtomicParsley --longhelp\n"
232 "Setting reverse DNS forms for iTunes files: see --reverseDNS-help\n"
233 "Setting 3gp assets into 3GPP & derivative files: see --3gp-help\n"
234 "Setting copyright notices for all files: see --ISO-help\n"
235 "For file-level options & padding info: see --file-help\n"
236 "Setting custom private tag extensions: see --uuid-help\n"
237 "Setting ID3 tags onto mpeg-4 files: see --ID3-help\n"
238 "\n"
239 "----------------------------------------------------------------------";
240
241 // an expansive, verbose, unconstrained (about 112 char wide) detailing of
242 // options
243 static const char *longHelp_text =
244 "AtomicParsley help page for setting iTunes-style metadata into MPEG-4 "
245 "files. \n"
246 " (3gp help available with AtomicParsley --3gp-help)\n"
247 " (ISO copyright help available with AtomicParsley --ISO-help)\n"
248 " (reverse DNS form help available with AtomicParsley "
249 "--reverseDNS-help)\n"
250 "Usage: AtomicParsley [mp4FILE]... [OPTION]... [ARGUMENT]... [ "
251 "[OPTION2]...[ARGUMENT2]...] \n"
252 "\n"
253 "example: AtomicParsley /path/to.mp4 -e ~/Desktop/pix\n"
254 "example: AtomicParsley /path/to.mp4 --podcastURL \"http://www.url.net\" "
255 "--tracknum 45/356\n"
256 "example: AtomicParsley /path/to.mp4 --copyright \"\342\204\227 \302\251 "
257 "2006\"\n"
258 "example: AtomicParsley /path/to.mp4 --year \"2006-07-27T14:00:43Z\" "
259 "--purchaseDate timestamp\n"
260 "example: AtomicParsley /path/to.mp4 --sortOrder artist \"Mighty Dub Cats, "
261 "The\n"
262 "--------------------------------------------------------------------------"
263 "----------------------\n"
264 " Extract any pictures in user data \"covr\" atoms to separate files. \n"
265 " --extractPix , -E Extract to same folder "
266 "(basename derived from file).\n"
267 " --extractPixToPath , -e (/path/basename) Extract to specific path "
268 "(numbers added to basename).\n"
269 " example: --e "
270 "~/Desktop/SomeText\n"
271 " gives: "
272 "SomeText_artwork_1.jpg SomeText_artwork_2.png\n"
273 " Note: extension comes from "
274 "embedded image file format\n"
275 "--------------------------------------------------------------------------"
276 "----------------------\n"
277 " Tag setting options:\n"
278 "\n"
279 " --artist , -a (str) Set the artist tag: "
280 "\"moov.udta.meta.ilst.\302\251ART.data\"\n"
281 " --title , -s (str) Set the title tag: "
282 "\"moov.udta.meta.ilst.\302\251nam.data\"\n"
283 " --album , -b (str) Set the album tag: "
284 "\"moov.udta.meta.ilst.\302\251alb.data\"\n"
285 " --genre , -g (str) Set the genre tag: \"\302\251gen\" "
286 "(custom) or \"gnre\" (standard).\n"
287 " see the standard list with "
288 "\"AtomicParsley --genre-list\"\n"
289 " --tracknum , -k (num)[/tot] Set the track number (or track "
290 "number & total tracks).\n"
291 " --disk , -d (num)[/tot] Set the disk number (or disk "
292 "number & total disks).\n"
293 " --comment , -c (str) Set the comment tag: "
294 "\"moov.udta.meta.ilst.\302\251cmt.data\"\n"
295 " --year , -y (num|UTC) Set the year tag: "
296 "\"moov.udta.meta.ilst.\302\251day.data\"\n"
297 " set with UTC "
298 "\"2006-09-11T09:00:00Z\" for Release Date\n"
299 " --lyrics , -l (str) Set the lyrics tag: "
300 "\"moov.udta.meta.ilst.\302\251lyr.data\"\n"
301 " --lyricsFile , (/path) Set the lyrics tag to the content "
302 "of a file\n"
303 " --composer , -w (str) Set the composer tag: "
304 "\"moov.udta.meta.ilst.\302\251wrt.data\"\n"
305 " --copyright , -x (str) Set the copyright tag: "
306 "\"moov.udta.meta.ilst.cprt.data\"\n"
307 " --grouping , -G (str) Set the grouping tag: "
308 "\"moov.udta.meta.ilst.\302\251grp.data\"\n"
309 " --artwork , -A (/path) Set a piece of artwork (jpeg or "
310 "png) on \"covr.data\"\n"
311 " Note: multiple pieces are "
312 "allowed with more --artwork args\n"
313 " --bpm , -B (num) Set the tempo/bpm tag: "
314 "\"moov.udta.meta.ilst.tmpo.data\"\n"
315 " --albumArtist , -A (str) Set the album artist tag: "
316 "\"moov.udta.meta.ilst.aART.data\"\n"
317 " --compilation , -C (bool) Sets the \"cpil\" atom (true or "
318 "false to delete the atom)\n"
319 " --hdvideo , -V (bool) Sets the \"hdvd\" atom (true or "
320 "false to delete the atom)\n"
321 " --advisory , -y (1of3) Sets the iTunes lyrics advisory "
322 "('remove', 'clean', 'explicit') \n"
323 " --stik , -S (1of7) Sets the iTunes \"stik\" atom "
324 "(--stik \"remove\" to delete) \n"
325 " \"Movie\", \"Normal\", \"TV "
326 "Show\" .... others: \n"
327 " see the full list with "
328 "\"AtomicParsley --stik-list\"\n"
329 " or set in an integer value "
330 "with --stik value=(num)\n"
331 " Note: --stik Audiobook will change "
332 "file extension to '.m4b'\n"
333 " --description , -p (str) Sets the description on the "
334 "\"desc\" atom\n"
335 " --Rating , (str) Sets the Rating on the \"rate\" "
336 "atom\n"
337 " --longdesc , -j (str) Sets the long description on the "
338 "\"ldes\" atom\n"
339 " --storedesc , (str) Sets the iTunes store description "
340 "on the \"sdes\" atom\n"
341 " --TVNetwork , -n (str) Sets the TV Network name on the "
342 "\"tvnn\" atom\n"
343 " --TVShowName , -H (str) Sets the TV Show name on the "
344 "\"tvsh\" atom\n"
345 " --TVEpisode , -I (str) Sets the TV Episode on "
346 "\"tven\":\"209\", but it is a string: \"209 Part 1\"\n"
347 " --TVSeasonNum , -U (num) Sets the TV Season number on the "
348 "\"tvsn\" atom\n"
349 " --TVEpisodeNum , -N (num) Sets the TV Episode number on the "
350 "\"tves\" atom\n"
351
352 " --podcastFlag , -f (bool) Sets the podcast flag (values are "
353 "\"true\" or \"false\")\n"
354 " --category , -q (str) Sets the podcast category; "
355 "typically a duplicate of its genre\n"
356 " --keyword , -K (str) Sets the podcast keyword; invisible "
357 "to MacOSX Spotlight\n"
358 " --podcastURL , -L (URL) Set the podcast feed URL on the "
359 "\"purl\" atom\n"
360 " --podcastGUID , -J (URL) Set the episode's URL tag on the "
361 "\"egid\" atom\n"
362 " --purchaseDate , -D (UTC) Set Universal Coordinated Time of "
363 "purchase on a \"purd\" atom\n"
364 " (use \"timestamp\" to set UTC to "
365 "now; can be akin to id3v2 TDTG tag)\n"
366 " --encodingTool , (str) Set the name of the encoder on the "
367 "\"\302\251too\" atom\n"
368 " --encodedBy , (str) Set the name of the Person/company "
369 "who encoded the file on the \"\302\251enc\" atom\n"
370 " --apID , -Y (str) Set the name of the Account Name on "
371 "the \"apID\" atom\n"
372 " --cnID , (num) Set iTunes Catalog ID, used for "
373 "combining SD and HD encodes in iTunes on the \"cnID\" atom\n"
374 "\n"
375 " To combine you must set \"hdvd\" "
376 "atom on one file and must have same \"stik\" on both file\n"
377 " Must not use \"stik\" of value Home "
378 "Video(0), use Movie(9)\n"
379 "\n"
380 " iTunes Catalog numbers can be "
381 "obtained by finding the item in the iTunes Store. Once item\n"
382 " is found in the iTunes Store right "
383 "click on picture of item and select copy link. Paste this link\n"
384 " into a document or web browser to "
385 "display the catalog number ID.\n"
386 "\n"
387 " An example link for the video "
388 "Street Kings is:\n"
389 " "
390 "http://itunes.apple.com/WebObjects/MZStore.woa/wa/"
391 "viewMovie?id=278743714&s=143441\n"
392 " Here you can see the cnID is "
393 "278743714\n"
394 "\n"
395 " Alternatively you can use iMDB "
396 "numbers, however these will not match the iTunes catalog.\n"
397 "\n"
398 " --geID , (num) Set iTunes Genre ID. This does not "
399 "necessarily have to match genre.\n"
400 " See --genre-movie-id-list and "
401 "--genre-tv-id-list\n"
402 "\n"
403 " --xID , (str) Set iTunes vendor-supplied xID, "
404 "used to allow iTunes LPs and iTunes Extras to interact \n"
405 " with other content in your "
406 "iTunes Library\n"
407 " --gapless , (bool) Sets the gapless playback flag for "
408 "a track in a gapless album\n"
409
410 " --sortOrder (type) (str) Sets the sort order string for that "
411 "type of tag.\n"
412 " (available types are: \"name\", "
413 "\"artist\", \"albumartist\",\n"
414 " \"album\", \"composer\", "
415 "\"show\")\n"
416 "\n"
417 "NOTE: Except for artwork, only 1 of each tag is allowed; artwork allows "
418 "multiple pieces.\n"
419 "NOTE: Tags that carry text(str) have a limit of 255 utf8 characters;\n"
420 "however lyrics and long descriptions have no limit.\n"
421 "--------------------------------------------------------------------------"
422 "----------------------\n"
423 " To delete a single atom, set the tag to null (except artwork):\n"
424 " --artist \"\" --lyrics \"\"\n"
425 " --artwork REMOVE_ALL \n"
426 " --metaEnema , -P Douches away every atom under "
427 "\"moov.udta.meta.ilst\" \n"
428 " --foobar2000Enema , -2 Eliminates foobar2000's "
429 "non-compliant so-out-o-spec tagging scheme\n"
430 " --manualAtomRemove \"some.atom.path\" where some.atom.path can be:\n"
431 " keys to using manualAtomRemove:\n"
432 " ilst.ATOM.data or ilst.ATOM target an iTunes-style metadata tag\n"
433 " ATOM:lang=foo target an atom with this language "
434 "setting; like 3gp assets\n"
435 " ATOM.----.name:[foo] target a reverseDNS metadata tag; "
436 "like iTunNORM\n"
437 " Note: these atoms show up with 'AP "
438 "-t' as: Atom \"----\" [foo]\n"
439 " 'foo' is actually carried on the "
440 "'name' atom\n"
441 " ATOM[x] target an atom with an index other "
442 "than 1; like trak[2]\n"
443 " ATOM.uuid=hex-hex-hex-hex targt a uuid atom with the uuid of "
444 "hex string representation\n"
445 " examples:\n"
446 " moov.udta.meta.ilst.----.name:[iTunNORM] "
447 "moov.trak[3].cprt:lang=urd\n"
448 " moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740\n"
449 "--------------------------------------------------------------------------"
450 "----------------------\n"
451
452 #if defined(__APPLE__)
453 " Environmental Variables (affecting picture placement)\n"
454 "\n"
455 " set PIC_OPTIONS in your shell to set these flags; preferences are "
456 "separated by colons (:)\n"
457 "\n"
458 " MaxDimensions=num (default: 0; unlimited); sets maximum pixel "
459 "dimensions\n"
460 " DPI=num (default: 72); sets dpi\n"
461 " MaxKBytes=num (default: 0; unlimited); maximum kilobytes for file "
462 "(jpeg only)\n"
463 " AddBothPix=bool (default: false); add original & converted pic (for "
464 "archival purposes)\n"
465 " AllPixJPEG | AllPixPNG =bool (default: false); force conversion to a "
466 "specific picture format\n"
467 " SquareUp (include to square images to largest dimension, allows "
468 "an [ugly] 160x1200->1200x1200)\n"
469 " removeTempPix (include to delete temp pic files created when "
470 "resizing images after tagging)\n"
471 " ForceHeight=num (must also specify width, below) force image pixel "
472 "height\n"
473 " ForceWidth=num (must also specify height, above) force image pixel "
474 "width\n"
475 "\n"
476 " Examples: (bash-style)\n"
477 " export "
478 "PIC_OPTIONS=\"MaxDimensions=400:DPI=72:MaxKBytes=100:AddBothPix=true:"
479 "AllPixJPEG=true\"\n"
480 " export PIC_OPTIONS=\"SquareUp:removeTempPix\"\n"
481 " export PIC_OPTIONS=\"ForceHeight=999:ForceWidth=333:removeTempPix\"\n"
482 "--------------------------------------------------------------------------"
483 "----------------------\n"
484 #endif
485 ;
486
487 static const char *fileLevelHelp_text =
488 "AtomicParsley help page for general & file level options.\n"
489 #if defined(_WIN32)
490 #ifndef __CYGWIN__
491 " Note: you can change the input/output behavior to raw 8-bit utf8 if the "
492 "program name\n"
493 " is appended with \"-utf8\". AtomicParsley-utf8.exe will have "
494 "problems with files/\n"
495 " folders with unicode characters in given paths.\n"
496 #else
497 " Note: you can change the input/output behavior for MCDI functions to "
498 "raw 8-bit utf8\n"
499 " if the program name is appended with \"-utf8\".\n"
500 #endif
501 "\n"
502 #endif
503 "--------------------------------------------------------------------------"
504 "----------------------\n"
505 " Atom reading services:\n"
506 "\n"
507 " --test , -T Tests file to see if it is a valid "
508 "MPEG-4 file.\n"
509 " Prints out the hierarchical atom "
510 "tree.\n"
511 " -T 1 Supplemental track level info with "
512 "\"-T 1\"\n"
513 " -T +dates Track level with creation/modified "
514 "dates\n"
515 "\n"
516 " --textdata , -t print user data text metadata relevant to "
517 "brand (inc. # of any pics).\n"
518 " -t + show supplemental info like free space, "
519 "available padding, user data\n"
520 " length & media data length\n"
521 " -t 1 show all textual metadata (disregards "
522 "brands, shows track copyright)\n"
523 "\n"
524 " --brands show the major & minor brands for the "
525 "file & available tagging schemes\n"
526 "\n"
527 "--------------------------------------------------------------------------"
528 "----------------------\n"
529 " File services:\n"
530 "\n"
531 " --freefree [num] , Remove \"free\" atoms which only "
532 "act as filler in the file\n"
533 " ?(num)? - optional integer argument "
534 "to delete 'free's to desired level\n"
535 "\n"
536 " NOTE 1: levels begin at level 1 aka "
537 "file level.\n"
538 " NOTE 2: Level 0 (which doesn't "
539 "exist) deletes level 1 atoms that pre-\n"
540 " cede 'moov' & don't serve "
541 "as padding. Typically, such atoms\n"
542 " are created by libmp4ff or "
543 "libmp4v2 as a byproduct of tagging.\n"
544 " NOTE 3: When padding falls below "
545 "MIN_PAD (typically zero), a default\n"
546 " amount of padding "
547 "(typically 2048 bytes) will be added. To\n"
548 " achieve absolutely 0 bytes "
549 "'free' space with --freefree, set\n"
550 " DEFAULT_PAD to 0 via the "
551 "AP_PADDING mechanism (see below).\n"
552 "\n"
553 " --preventOptimizing Prevents reorganizing the file to "
554 "have file metadata before media data.\n"
555 " iTunes/Quicktime have so far "
556 "*always* placed metadata first; many 3rd\n"
557 " party utilities do not (preventing "
558 "streaming to the web, AirTunes, iTV).\n"
559 " Used in conjunction with "
560 "--overWrite, files with metadata at the end\n"
561 " (most ffmpeg produced files) can "
562 "have their tags rapidly updated without\n"
563 " requiring a full rewrite. Note: "
564 "this does not un-optimize a file.\n"
565 " Note: this option will be canceled "
566 "out if used with the --freefree option\n"
567 "\n"
568 " --metaDump Dumps out 'moov.udta' metadata out "
569 "to a new file next to original\n"
570 " (for diagnostic purposes, "
571 "please remove artwork before sending)\n"
572 " --output , -o (/path) Specify the filename of tempfile "
573 "(voids overWrite)\n"
574 " --overWrite , -W Writes to temp file; deletes "
575 "original, renames temp to original\n"
576 " If possible, padding will be used "
577 "to update without a full rewrite.\n"
578 "\n"
579 #if defined(_WIN32)
580 " --preserveTime Will overwrite the original file in "
581 "place (--overWrite forced),\n"
582 " but will also keep the original "
583 "file's timestamps intact.\n"
584 "\n"
585 #endif
586 " --DeepScan Parse areas of the file that are "
587 "normally skipped (must be the 3rd arg)\n"
588 " --iPod-uuid (num) Place the ipod-required uuid for "
589 "higher resolution avc video files\n"
590 " Currently, the only number used is "
591 "1200 - the maximum number of macro-\n"
592 " blocks allowed by the higher "
593 "resolution iPod setting.\n"
594 " NOTE: this requires the "
595 "\"--DeepScan\" option as the 3rd cli argument\n"
596 " NOTE2: only works on the first avc "
597 "video track, not all avc tracks\n"
598 "\n"
599 "Examples: \n"
600 " --freefree 0 (deletes all top-level non-padding atoms preceding "
601 "'mooov') \n"
602 " --freefree 1 (deletes all non-padding atoms at the top most "
603 "level) \n"
604 " --output ~/Desktop/newfile.mp4\n"
605 " AP /path/to/file.m4v --DeepScan --iPod-uuid 1200\n"
606
607 "--------------------------------------------------------------------------"
608 "----------------------\n"
609 " Padding & 'free' atoms:\n"
610 "\n"
611 " A special type of atom called a 'free' atom is used for padding (all "
612 "'free' atoms contain NULL space).\n"
613 " When changes need to occur, these 'free' atom are used. They grows or "
614 "shink, but the relative locations\n"
615 " of certain other atoms (stco/mdat) remain the same. If there is no "
616 "'free' space, a full rewrite will occur.\n"
617 " The locations of 'free' atom(s) that AP can use as padding must be "
618 "follow 'moov.udta' & come before 'mdat'.\n"
619 " A 'free' preceding 'moov' or following 'mdat' won't be used as padding "
620 "for example. \n"
621 "\n"
622 " Set the shell variable AP_PADDING with these values, separated by "
623 "colons to alter padding behavior:\n"
624 "\n"
625 " DEFAULT_PADDING= - the amount of padding added if the minimum padding "
626 "is non-existant in the file\n"
627 " default = 2048\n"
628 " MIN_PAD= - the minimum padding present before more padding "
629 "will be added\n"
630 " default = 0\n"
631 " MAX_PAD= - the maximum allowable padding; excess padding will "
632 "be eliminated\n"
633 " default = 5000\n"
634 "\n"
635 " If you use --freefree to eliminate 'free' atoms from the file, the "
636 "DEFAULT_PADDING amount will still be\n"
637 " added to any newly written files. Set DEFAULT_PADDING=0 to prevent any "
638 "'free' padding added at rewrite.\n"
639 " You can set MIN_PAD to be assured that at least that amount of padding "
640 "will be present - similarly,\n"
641 " MAX_PAD limits any excessive amount of padding. All 3 options will in "
642 "all likelyhood produce a full\n"
643 " rewrite of the original file. Another case where a full rewrite will "
644 "occur is when the original file\n"
645 " is not optimized and has 'mdat' preceding 'moov'.\n"
646 "\n"
647 #if defined(_WIN32) && !defined(__CYGWIN__)
648 "Examples:\n"
649 " c:> SET AP_PADDING=\"DEFAULT_PAD=0\" or c:> SET "
650 "AP_PADDING=\"DEFAULT_PAD=3128\"\n"
651 " c:> SET AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n"
652 #else
653 "Examples (bash style):\n"
654 " $ export AP_PADDING=\"DEFAULT_PAD=0\" or $ export "
655 "AP_PADDING=\"DEFAULT_PAD=3128\"\n"
656 " $ export AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n"
657 #endif
658 "\n"
659 "Note: while AtomicParsley is still in the beta stage, the original file "
660 "will always remain untouched - \n"
661 " unless given the --overWrite flag when if possible, utilizing "
662 "available padding to update tags\n"
663 " will be tried (falling back to a full rewrite if changes are "
664 "greater than the found padding).\n"
665 "--------------------------------------------------------------------------"
666 "--------------------------\n"
667 " iTunes 7 & Gapless playback:\n"
668 "\n"
669 " iTunes 7 adds NULL space at the ends of files (filled with zeroes). It "
670 "is possble this is how iTunes\n"
671 " implements gapless playback - perhaps not. In any event, with "
672 "AtomicParsley you can choose to preserve\n"
673 " that NULL space, or you can eliminate its presence (typically around "
674 "2,000 bytes). The default behavior\n"
675 " is to preserve it - if it is present at all. You can choose to eliminate "
676 "it by setting the environ-\n"
677 " mental preference for AP_PADDING to have DEFAULT_PAD=0\n"
678 "\n"
679 #if defined(_WIN32) && !defined(__CYGWIN__)
680 "Example:\n"
681 " c:> SET AP_PADDING=\"DEFAULT_PAD=0\"\n"
682 #else
683 "Example (bash style):\n"
684 " $ export AP_PADDING=\"DEFAULT_PAD=0\"\n"
685 #endif
686 "--------------------------------------------------------------------------"
687 "--------------------------\n";
688
689 // detailed options for 3gp branded files
690 static const char *_3gpHelp_text =
691 "AtomicParsley 3gp help page for setting 3GPP-style metadata.\n"
692 "--------------------------------------------------------------------------"
693 "--------------------------\n"
694 " 3GPP text tags can be encoded in either UTF-8 (default input encoding) "
695 "or UTF-16 (converted from UTF-8)\n"
696 " Many 3GPP text tags can be set for a desired language by a "
697 "3-letter-lowercase code (default is \"eng\").\n"
698 " For tags that support the language attribute (all except year), more "
699 "than one tag of the same name\n"
700 " (3 titles for example) differing in the language code is supported.\n"
701 "\n"
702 " iTunes-style metadata is not supported by the 3GPP TS 26.244 version "
703 "6.4.0 Release 6 specification.\n"
704 " 3GPP asset tags can be set at movie level or track level & are set in a "
705 "different hierarchy: moov.udta \n"
706 " if at movie level (versus iTunes moov.udta.meta.ilst). Other 3rd party "
707 "utilities may allow setting\n"
708 " iTunes-style metadata in 3gp files. When a 3gp file is detected (file "
709 "extension doesn't matter), only\n"
710 " 3gp spec-compliant metadata will be read & written.\n"
711 "\n"
712 " Note1: there are a number of different 'brands' that 3GPP files come "
713 "marked as. Some will not be \n"
714 " supported by AtomicParsley due simply to them being unknown and "
715 "untested. You can compile your\n"
716 " own AtomicParsley to evaluate it by adding the hex code into the "
717 "source of APar_IdentifyBrand.\n"
718 "\n"
719 " Note2: There are slight accuracy discrepancies in location's fixed "
720 "point decimals set and retrieved.\n"
721 "\n"
722 " Note3: QuickTime Player can see a limited subset of these tags, but "
723 "only in 1 language & there seems to\n"
724 " be an issue with not all unicode text displaying properly. This "
725 "is an issue withing QuickTime -\n"
726 " the exact same text (in utf8) displays properly in an MPEG-4 "
727 "file. Some languages can also display\n"
728 " more glyphs than others.\n"
729 "\n"
730 "--------------------------------------------------------------------------"
731 "--------------------------\n"
732 " Tag setting options (default user data area is movie level; default lang "
733 "is 'eng'; default encoding is UTF8):\n"
734 " required arguments are in (parentheses); optional arguments are in "
735 "[brackets]\n"
736 "\n"
737 " --3gp-title (str) [lang=3str] [UTF16] [area] ......... "
738 "Set a 3gp media title tag\n"
739 " --3gp-author (str) [lang=3str] [UTF16] [area] ......... "
740 "Set a 3gp author of the media tag\n"
741 " --3gp-performer (str) [lang=3str] [UTF16] [area] ......... "
742 "Set a 3gp performer or artist tag\n"
743 " --3gp-genre (str) [lang=3str] [UTF16] [area] ......... "
744 "Set a 3gp genre asset tag\n"
745 " --3gp-description (str) [lang=3str] [UTF16] [area] ......... "
746 "Set a 3gp description or caption tag\n"
747 " --3gp-copyright (str) [lang=3str] [UTF16] [area] ......... "
748 "Set a 3gp copyright notice tag*\n"
749 "\n"
750 " --3gp-album (str) [lang=3str] [UTF16] [trknum=int] [area] "
751 "Set a 3gp album tag (& opt. tracknum)\n"
752 " --3gp-year (int) [area] ........................... Set a "
753 "3gp recording year tag (4 digit only)\n"
754 "\n"
755 " --3gp-rating (str) [entity=4str] [criteria=4str] "
756 "[lang=3str] [UTF16] [area] Rating tag\n"
757 " --3gp-classification (str) [entity=4str] [index=int] "
758 "[lang=3str] [UTF16] [area] Classification\n"
759 "\n"
760 " --3gp-keyword (str) [lang=3str] [UTF16] [area] Format of "
761 "str: 'keywords=word1,word2,word3,word4'\n"
762 "\n"
763 " --3gp-location (str) [lang=3str] [UTF16] [area] Set a 3gp "
764 "location tag (default: Central Park)\n"
765 " [longitude=fxd.pt] [latitude=fxd.pt] "
766 "[altitude=fxd.pt]\n"
767 " [role=str] [body=str] [notes=str]\n"
768 " fxd.pt values are decimal coordinates "
769 "(55.01209, 179.25W, 63)\n"
770 " 'role=' values: 'shooting location', "
771 "'real location', 'fictional location'\n"
772 " a negative value in coordinates "
773 "will be seen as a cli flag\n"
774 " append 'S', 'W' or 'B': lat=55S, "
775 "long=90.23W, alt=90.25B\n"
776 "\n"
777 " [area] can be \"movie\", \"track\" or \"track=num\" where 'num' is the "
778 "number of the track. If not specificied,\n"
779 " assets will be placed at movie level. The \"track\" option sets the "
780 "asset across all available tracks\n"
781 "\n"
782 "Note1: '4str' = a 4 letter string like \"PG13\"; 3str is a 3 letter "
783 "string like \"eng\"; int is an integer\n"
784 "Note2: List all languages for '3str' with \"AtomicParsley --language-list "
785 "(unknown languages become \"und\")\n"
786 "*Note3: The 3gp copyright asset can potentially be altered by using the "
787 "--ISO-copyright setting.\n"
788
789 "--------------------------------------------------------------------------"
790 "--------------------------\n"
791 "Usage: AtomicParsley [3gpFILE] --option [argument] [optional_arguments] "
792 "[ --option2 [argument2]...] \n"
793 "\n"
794 "example: AtomicParsley /path/to.3gp -t \n"
795 "example: AtomicParsley /path/to.3gp -T 1 \n"
796 "example: Atomicparsley /path/to.3gp --3gp-performer \"Enjoy Yourself\" "
797 "lang=pol UTF16\n"
798 "example: Atomicparsley /path/to.3gp --3gp-year 2006 --3gp-album \"White "
799 "Label\" track=8 lang=fra\n"
800 "example: Atomicparsley /path/to.3gp --3gp-album \"Cow Cod Soup For "
801 "Everyone\" track=10 lang=car\n"
802 "\n"
803 "example: Atomicparsley /path/to.3gp --3gp-classification \"Poor Sport\" "
804 "entity=\"PTA \" index=12 UTF16\n"
805 "example: Atomicparsley /path/to.3gp --3gp-keyword "
806 "keywords=\"foo1,foo2,foo 3\" UTF16 --3gp-keyword \"\"\n"
807 "example: Atomicparsley /path/to.3gp --3gp-location 'Bethesda Terrace' "
808 "latitude=40.77 longitude=73.98W \n"
809 " altitude=4.3B "
810 "role='real' body=Earth notes='Underground'\n"
811 "\n"
812 "example: Atomicparsley /path/to.3gp --3gp-title \"I see London.\" "
813 "--3gp-title \"Veo Madrid.\" lang=spa \n"
814 " --3gp-title \"Widze Warsawa.\" "
815 "lang=pol\n"
816 "\n";
817
818 static const char *ISOHelp_text =
819 "AtomicParsley help page for setting ISO copyright notices at movie & "
820 "track level.\n"
821 "--------------------------------------------------------------------------"
822 "--------------------------\n"
823 " The ISO specification allows for setting copyright in a number of "
824 "places. This copyright atom is\n"
825 " independant of the iTunes-style --copyright tag that can be set. This "
826 "ISO tag is identical to the\n"
827 " 3GP-style copyright. In fact, using --ISO-copyright can potentially "
828 "overwrite the 3gp copyright\n"
829 " asset if set at movie level & given the same language to set the "
830 "copyright on. This copyright\n"
831 " notice is the only metadata tag defined by the reference ISO 14496-12 "
832 "specification.\n"
833 "\n"
834 " ISO copyright notices can be set at movie level, track level for a "
835 "single track, or for all tracks.\n"
836 " Multiple copyright notices are allowed, but they must differ in the "
837 "language setting. To see avail-\n"
838 " able languages use \"AtomicParsley --language-list\". Notices can be "
839 "set in utf8 or utf16.\n"
840 "\n"
841 " --ISO-copyright (str) [movie|track|track=#] [lang=3str] [UTF16] "
842 "Set a copyright notice\n"
843 " # in 'track=#' "
844 "denotes the target track\n"
845 " 3str is the 3 "
846 "letter ISO-639-2 language.\n"
847 " Brackets [] "
848 "show optional parameters.\n"
849 " Defaults are: "
850 "movie level, 'eng' in utf8.\n"
851 "\n"
852 "example: AtomicParsley /path/file.mp4 -t 1 Note: the only way to see "
853 "all contents is with -t 1 \n"
854 "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\"\n"
855 "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" movie\n"
856 "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track=2 "
857 "lang=urd\n"
858 "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track "
859 "UTF16\n"
860 "example: AP --ISO-copyright \"Example\" track --ISO-copyright \"Por "
861 "Exemplo\" track=2 lang=spa UTF16\n"
862 "\n"
863 "Note: to remove the copyright, set the string to \"\" - the track and "
864 "language must match the target.\n"
865 "example: --ISO-copyright \"\" track --ISO-copyright \"\" track=2 "
866 "lang=spa\n"
867 "\n"
868 "Note: (foo) denotes required arguments; [foo] denotes optional parameters "
869 "& may have defaults.\n";
870
871 static const char *uuidHelp_text =
872 "AtomicParsley help page for setting uuid user extension metadata tags.\n"
873 "--------------------------------------------------------------------------"
874 "--------------------------\n"
875 " Setting a user-defined 'uuid' private extention tags will appear in "
876 "\"moov.udta.meta\"). These will\n"
877 " only be read by AtomicParsley & can be set irrespective of file "
878 "branding. The form of uuid that AP\n"
879 " is a v5 uuid generated from a sha1 hash of an atom name in an "
880 "'AtomicParsley.sf.net' namespace.\n"
881 "\n"
882 " The uuid form is in some Sony & Compressor files, but of version 4 "
883 "(random/pseudo-random). An example\n"
884 " uuid of 'cprt' in the 'AtomicParsley.sf.net' namespace is: "
885 "\"4bd39a57-e2c8-5655-a4fb-7a19620ef151\".\n"
886 " 'cprt' in the same namespace will always create that uuid; uuid atoms "
887 "will only print out if the\n"
888 " uuid generated is the same as discovered. Sony uuids don't for example "
889 "show up with AP -t.\n"
890 "\n"
891 " --information , -i (str) Set an information tag on uuid atom "
892 "name\"\251inf\"\n"
893 " --url , -u (URL) Set a URL tag on uuid atom name "
894 "\"\302\251url\"\n"
895 " --tagtime , timestamp Set the Coordinated Univeral Time "
896 "of tagging on \"tdtg\"\n"
897 "\n"
898 " Define & set an arbitrary atom with a text data or embed a file:\n"
899 " --meta-uuid There are two forms: 1 for text & 1 for file "
900 "operations\n"
901 " setting text form:\n"
902 " --meta-uuid (atom) \"text\" (str) \"atom\" = 4 "
903 "character atom name of your choice\n"
904 " str is whatever text "
905 "you want to set\n"
906 " file embedding form:\n"
907 " --meta-uuid (atom) \"file\" (/path) [description=\"foo\"] "
908 "[mime-type=\"foo/moof\"]\n"
909 " \"atom\" = 4 "
910 "character atom name of your choice\n"
911 " /path = path to the "
912 "file that will be embedded*\n"
913 " description = "
914 "optional description of the file\n"
915 " default is "
916 "\"[none]\"\n"
917 " mime-type = optional "
918 "mime type for the file\n"
919 " default is "
920 "\"none\"\n"
921 " Note: no "
922 "auto-disocevery of mime type\n"
923 " if "
924 "you know/want it: supply it.\n"
925 " *Note: a file extension "
926 "(/path/file.ext) is required\n"
927 "\n"
928 "Note: (foo) denotes required arguments; [foo] denotes optional arguments "
929 "& may have defaults.\n"
930 "\n"
931 "Examples: \n"
932 " --tagtime timestamp --information \"[psst]I see metadata\" --url "
933 "http://www.bumperdumper.com\n"
934 " --meta-uuid tagr text \"Johnny Appleseed\" --meta-uuid \302\251sft text "
935 "\"OpenShiiva encoded.\"\n"
936 " --meta-uuid scan file /usr/pix/scans.zip\n"
937 " --meta-uuid 1040 file ../../2006_taxes.pdf description=\"Fooled 'The "
938 "Man' yet again.\"\n"
939 "can be removed with:\n"
940 " --tagtime \"\" --information \"\" --url \" \" --meta-uuid scan file "
941 "\n"
942 " --manualAtomRemove "
943 "\"moov.udta.meta.uuid=672c98cd-f11f-51fd-adec-b0ee7b4d215f\" \\\n"
944 " --manualAtomRemove "
945 "\"moov.udta.meta.uuid=1fed6656-d911-5385-9cb2-cb2c100f06e7\"\n"
946 "Remove the Sony uuid atoms with:\n"
947 " --manualAtomRemove "
948 "moov.trak[1].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n"
949 " --manualAtomRemove "
950 "moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n"
951 " --manualAtomRemove uuid=50524f46-21d2-4fce-bb88-695cfac9c740\n"
952 "\n"
953 "Viewing the contents of uuid atoms:\n"
954 " -t or --textdata Shows the uuid atoms (both text & file) that "
955 "AP sets:\n"
956 " Example output:\n"
957 " Atom uuid=ec0f...d7 (AP uuid for \"scan\") contains: FILE.zip; "
958 "description=[none]\n"
959 " Atom uuid=672c...5f (AP uuid for \"tagr\") contains: Johnny "
960 "Appleseed\n"
961 "\n"
962 "Extracting an embedded file in a uuid atom:\n"
963 " --extract1uuid (atom) Extract file embedded within "
964 "uuid=atom into same folder\n"
965 " (file will be named with suffix "
966 "shown in --textdata)\n"
967 " --extract-uuids [/path] Extract all files in uuid atoms "
968 "under the moov.udta.meta\n"
969 " hierarchy. If no /path is given, "
970 "files will be extracted\n"
971 " to the same folder as the "
972 "originating file.\n"
973 "\n"
974 " Examples:\n"
975 " --extract1uuid scan\n"
976 " ... Extracted uuid=scan attachment to file: "
977 "/some/path/FILE_scan_uuid.zip\n"
978 " --extract-uuids ~/Desktop/plops\n"
979 " ... Extracted uuid=pass attachment to file: "
980 "/Users/me/Desktop/plops_pass_uuid.pdf\n"
981 " ... Extracted uuid=site attachment to file: "
982 "/Users/me/Desktop/plops_site_uuid.html\n"
983 "\n"
984 "--------------------------------------------------------------------------"
985 "----------------------\n";
986
987 static const char *rDNSHelp_text =
988 "AtomicParsley help page for setting reverse domain '----' metadata "
989 "atoms.\n"
990 "--------------------------------------------------------------------------"
991 "--------------------------\n"
992 " Please note that the reverse DNS format supported here is not "
993 "feature complete.\n"
994 "\n"
995 " Another style of metadata that iTunes uses is called the reverse DNS "
996 "format. For all known tags,\n"
997 " iTunes offers no user-accessible exposure to these tags or their "
998 "contents. This reverse DNS form has\n"
999 " a differnt form than other iTunes tags that have a atom name that "
1000 "describes the content of 'data'\n"
1001 " atom it contains. In the reverseDNS format, the parent to the structure "
1002 "called the '----' atom, with\n"
1003 " children atoms that describe & contain the metadata carried. The 'mean' "
1004 "child contains the reverse\n"
1005 " domain itself ('com.apple.iTunes') & the 'name' child contains the "
1006 "descriptor ('iTunNORM'). A 'data'\n"
1007 " atom follows that actually contains the contents of the tag.\n"
1008 "\n"
1009 " --contentRating (rating) Set the US TV/motion picture media "
1010 "content rating\n"
1011 " for available ratings use "
1012 "\"AtomicParsley --ratings-list\n"
1013
1014 " --rDNSatom (str) name=(name_str) domain=(reverse_domain) "
1015 "Manually set a reverseDNS atom.\n"
1016 "\n"
1017 " To set the form manually, 3 things are required: a domain, a name, and "
1018 "the desired text.\n"
1019 " Note: multiple 'data' atoms are supported, but not in the "
1020 "com.apple.iTunes domain\n"
1021 " Examples:\n"
1022 " --contentRating \"NC-17\" --contentRating \"TV-Y7\"\n"
1023 " --rDNSatom \"mpaa|PG-13|300|\" name=iTunEXTC domain=com.apple.iTunes\n"
1024 " --contentRating \"\"\n"
1025 " --rDNSatom \"\" name=iTunEXTC domain=com.apple.iTunes\n"
1026 " --rDNSatom \"try1\" name=EVAL domain=org.fsf --rDNSatom \"try 2\" "
1027 "name=EVAL domain=org.fsf\n"
1028 " --rDNSatom \"\" name=EVAL domain=org.fsf\n"
1029 "--------------------------------------------------------------------------"
1030 "--------------------------\n";
1031
1032 static const char *ID3Help_text =
1033 "AtomicParsley help page for ID32 atoms with ID3 tags.\n"
1034 "--------------------------------------------------------------------------"
1035 "--------------------------\n"
1036 " ** Please note: ID3 tag support is not feature complete & is in an "
1037 "alpha state. **\n"
1038 "--------------------------------------------------------------------------"
1039 "--------------------------\n"
1040 " ID3 tags are the tagging scheme used by mp3 files (where they are found "
1041 "typically at the start of the\n"
1042 " file). In mpeg-4 files, ID3 version 2 tags are located in specific "
1043 "hierarchies at certain levels, at\n"
1044 " file/movie/track level. The way that ID3 tags are carried on mpeg-4 "
1045 "files (carried by 'ID32' atoms)\n"
1046 " was added in early 2006, but the ID3 tagging 'informal standard' was "
1047 "last updated (to v2.4) in 2000.\n"
1048 " With few exceptions, ID3 tags in mpeg-4 files exist identically to their "
1049 "mp3 counterparts.\n"
1050 "\n"
1051 " The ID3 parlance, a frame contains an piece of metadata. A frame (like "
1052 "COMM for comment, or TIT1 for\n"
1053 " title) contains the information, while the tag contains all the frames "
1054 "collectively. The 'informal\n"
1055 " standard' for ID3 allows multiple langauges for frames like COMM "
1056 "(comment) & USLT (lyrics). In mpeg-4\n"
1057 " this language setting is removed from the ID3 domain and exists in the "
1058 "mpeg-4 domain. That means that\n"
1059 " when an english and a spanish comment are set, 2 separate ID32 atoms are "
1060 "created, each with a tag & 1\n"
1061 " frame as in this example:\n"
1062 " --ID3Tag COMM \"Primary\" --desc=AAA --ID3Tag COMM \"El Segundo\" "
1063 "UTF16LE lang=spa --desc=AAA\n"
1064 " See available frames with \"AtomicParsley --ID3frames-list\"\n"
1065 " See avilable imagetypes with \"AtomicParsley --imagetype-list\"\n"
1066 "\n"
1067 " AtomicParsley writes ID3 version 2.4.0 tags *only*. There is no "
1068 "up-converting from older versions.\n"
1069 " Defaults are:\n"
1070 " default to movie level (moov.meta.ID32); other options are [ \"root\", "
1071 "\"track=(num)\" ] (see WARNING)\n"
1072 " UTF-8 text encoding when optional; other options are [ \"LATIN1\", "
1073 "\"UTF16BE\", \"UTF16LE\" ]\n"
1074 " frames that require descriptions have a default of \"\"\n"
1075 " for frames requiring a language setting, the ID32 language is used "
1076 "(currently defaulting to 'eng')\n"
1077 " frames that require descriptions have a default of \"\"\n"
1078 " image type defaults to 0x00 or Other; for image type 0x01, 32x32 png "
1079 "is enforced (switching to 0x02)\n"
1080 " setting the image mimetype is generally not required as the file is "
1081 "tested, but can be overridden\n"
1082 " zlib compression off\n"
1083 "\n"
1084 " WARNING:\n"
1085 " Quicktime Player (up to v7.1.3 at least) will freeze opeing a file "
1086 "with ID32 tags at movie level.\n"
1087 " Specifically, the parent atom, 'meta' is the source of the issue. "
1088 "You can set the tags at file or\n"
1089 " track level which avoids the problem, but the default is movie "
1090 "level. iTunes is unaffected.\n"
1091 "--------------------------------------------------------------------------"
1092 "--------------------------\n"
1093 " Current limitations:\n"
1094 " - syncsafe integers are used as indicated by the id3 \"informal "
1095 "standard\". usage/reading of\n"
1096 " nonstandard ordinary unsigned integers (uint32_t) is not/will not be "
1097 "implemented.\n"
1098 " - externally referenced images (using mimetype '-->') are prohibited "
1099 "by the ID32 specification.\n"
1100 " - the ID32 atom is only supported in a non-referenced context\n"
1101 " - probably a raft of other limitations that my brain lost along the "
1102 "way...\n"
1103 "--------------------------------------------------------------------------"
1104 "--------------------------\n"
1105 " Usage:\n"
1106 " --ID3Tag (frameID or alias) (str) [desc=(str)] [mimetype=(str)] "
1107 "[imagetype=(str or hex)] [...]\n"
1108 "\n"
1109 " ... represents other arguments:\n"
1110 " [compressed] zlib compress the frame\n"
1111 " [UTF16BE, UTF16LE, LATIN1] alternative text encodings for frames that "
1112 "support different encodings\n"
1113 "\n"
1114 "Note: (foo) denotes required arguments; [foo] denotes optional "
1115 "parameters\n"
1116 "\n"
1117 " Examples:\n"
1118 " --ID3Tag APIC /path/to/img.ext\n"
1119 " --ID3Tag APIC /path/to/img.ext desc=\"something to say\" imagetype=0x08 "
1120 "UTF16LE compressed\n"
1121 " --ID3Tag composer \"I, Claudius\" --ID3Tag TPUB \"Seneca the Roman\" "
1122 "--ID3Tag TMOO Imperial\n"
1123 " --ID3Tag UFID look@me.org uniqueID=randomUUIDstamp\n"
1124 "\n"
1125 " Extracting embedded images in APIC frames:\n"
1126 " --ID3Tag APIC extract\n"
1127 " images are extracted into the same directory as the source mpeg-4 "
1128 "file\n"
1129 "\n"
1130 #if defined(__APPLE__)
1131 " Setting MCDI (Music CD Identifier):\n"
1132 " --ID3Tag MCDI disk4\n"
1133 " Information to create this frame is taken directly off an Audio "
1134 "CD's TOC. If the target\n"
1135 " disk is not found or is not an audio CD, a scan of all devices "
1136 "will occur. If an Audio CD\n"
1137 " is present, the scan should yield what should be entered after "
1138 "'MCDI':\n"
1139 " % AP file --ID3Tag MCDI disk3\n"
1140 " % No cd present in drive at disk3\n"
1141 " % Device 'disk4' contains cd media\n"
1142 " % Good news, device 'disk4' is an Audio CD and can be used for "
1143 "'MCDI' setting\n"
1144 #elif defined(__linux__)
1145 " Setting MCDI (Music CD Identifier):\n"
1146 " --ID3Tag MCDI /dev/hdc\n"
1147 " Information to create this frame is taken directly off an Audio "
1148 "CD's TOC. An Audio CD\n"
1149 " must be mounted & present.\n"
1150 #elif defined(_WIN32)
1151 " Setting MCDI (Music CD Identifier):\n"
1152 " --ID3Tag MCDI D\n"
1153 " Information to create this frame is taken directly off an Audio "
1154 "CD's TOC. The letter after\n"
1155 " \"MCDI\" is the letter of the drive where the CD is present.\n"
1156 #endif
1157 ;
1158
1159 void ExtractPaddingPrefs(char *env_padding_prefs) {
1160 pad_prefs.default_padding_size = DEFAULT_PADDING_LENGTH;
1161 pad_prefs.minimum_required_padding_size = MINIMUM_REQUIRED_PADDING_LENGTH;
1162 pad_prefs.maximum_present_padding_size = MAXIMUM_REQUIRED_PADDING_LENGTH;
1163
1164 if (env_padding_prefs != NULL) {
1165 if (env_padding_prefs[0] == 0x22 || env_padding_prefs[0] == 0x27)
1166 env_padding_prefs++;
1167 }
1168 char *env_pad_prefs_ptr = env_padding_prefs;
1169
1170 while (env_pad_prefs_ptr != NULL) {
1171 env_pad_prefs_ptr = strsep(&env_padding_prefs, ":");
1172
1173 if (env_pad_prefs_ptr == NULL)
1174 break;
1175
1176 if (strncmp(env_pad_prefs_ptr, "DEFAULT_PAD=", 12) == 0) {
1177 strsep(&env_pad_prefs_ptr, "=");
1178 sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.default_padding_size);
1179 }
1180 if (strncmp(env_pad_prefs_ptr, "MIN_PAD=", 8) == 0) {
1181 strsep(&env_pad_prefs_ptr, "=");
1182 sscanf(env_pad_prefs_ptr,
1183 "%" SCNu32,
1184 &pad_prefs.minimum_required_padding_size);
1185 }
1186 if (strncmp(env_pad_prefs_ptr, "MAX_PAD=", 8) == 0) {
1187 strsep(&env_pad_prefs_ptr, "=");
1188 sscanf(env_pad_prefs_ptr,
1189 "%" SCNu32,
1190 &pad_prefs.maximum_present_padding_size);
1191 }
1192 }
1193 // fprintf(stdout, "Def %" PRIu32 "; Min %" PRIu32 "; Max %" PRIu32 "\n",
1194 // pad_prefs.default_padding_size, pad_prefs.minimum_required_padding_size,
1195 // pad_prefs.maximum_present_padding_size);
1196 return;
1197 }
1198
1199 void GetBasePath(const char *filepath, char *&basepath) {
1200 // with a myriad of m4a, m4p, mp4, whatever else comes up... it might just be
1201 // easiest to strip off the end.
1202 int split_here = 0;
1203 for (int i = strlen(filepath); i >= 0; i--) {
1204 const char *this_char = &filepath[i];
1205 if (*this_char == '.') {
1206 split_here = i;
1207 break;
1208 }
1209 }
1210 memcpy(basepath, filepath, (size_t)split_here);
1211
1212 return;
1213 }
1214
1215 void find_optional_args(char *argv[],
1216 int start_optindargs,
1217 uint16_t &packed_lang,
1218 bool &asUTF16,
1219 uint8_t &udta_container,
1220 uint8_t &trk_idx,
1221 int max_optargs) {
1222 asUTF16 = false;
1223 packed_lang = 5575; // und = 0x55C4 = 21956, but QTPlayer doesn't like und
1224 // //eng = 0x15C7 = 5575
1225
1226 for (int i = 0; i <= max_optargs - 1; i++) {
1227 if (argv[start_optindargs + i] && start_optindargs + i <= total_args) {
1228 if (strncmp(argv[start_optindargs + i], "lang=", 5) == 0) {
1229 if (!MatchLanguageCode(argv[start_optindargs + i] + 5)) {
1230 packed_lang = PackLanguage("und", 0);
1231 } else {
1232 packed_lang = PackLanguage(argv[start_optindargs + i], 5);
1233 }
1234
1235 } else if (strcmp(argv[start_optindargs + i], "UTF16") == 0) {
1236 asUTF16 = true;
1237 } else if (strcmp(argv[optind + i], "movie") == 0) {
1238 udta_container = MOVIE_LEVEL_ATOM;
1239 } else if (strncmp(argv[optind + i], "track=", 6) == 0) {
1240 char *track_index_str = argv[optind + i];
1241 strsep(&track_index_str, "=");
1242 trk_idx = strtoul(track_index_str, NULL, 10);
1243 udta_container = SINGLE_TRACK_ATOM;
1244 } else if (strcmp(argv[optind + i], "track") == 0) {
1245 udta_container = ALL_TRACKS_ATOM;
1246 }
1247 if (*argv[start_optindargs + i] == '-') {
1248 break; // we've hit another cli argument
1249 }
1250 }
1251 }
1252 return;
1253 }
1254
1255 void scan_ID3_optargs(char *argv[],
1256 int start_optargs,
1257 const char *&target_lang,
1258 uint16_t &packed_lang,
1259 uint8_t &char_encoding,
1260 char *meta_container,
1261 bool &multistring) {
1262 packed_lang = 5575; // default ID32 lang is 'eng'
1263 uint16_t i = 0;
1264
1265 while (argv[start_optargs + i] != NULL) {
1266 if (argv[start_optargs + i] && start_optargs + i <= total_args) {
1267
1268 if (strncmp(argv[start_optargs + i], "lang=", 5) == 0) {
1269 if (!MatchLanguageCode(argv[start_optargs + i] + 5)) {
1270 packed_lang = PackLanguage("und", 0);
1271 target_lang = "und";
1272 } else {
1273 packed_lang = PackLanguage(argv[start_optargs + i], 5);
1274 target_lang = argv[start_optargs + i] + 5;
1275 }
1276
1277 } else if (strcmp(argv[start_optargs + i], "UTF16LE") == 0) {
1278 char_encoding = TE_UTF16LE_WITH_BOM;
1279 } else if (strcmp(argv[start_optargs + i], "UTF16BE") == 0) {
1280 char_encoding = TE_UTF16BE_NO_BOM;
1281 } else if (strcmp(argv[start_optargs + i], "LATIN1") == 0) {
1282 char_encoding = TE_LATIN1;
1283
1284 } else if (strcmp(argv[optind + i], "root") == 0) {
1285 *meta_container = 0 - FILE_LEVEL_ATOM;
1286 } else if (strncmp(argv[optind + i], "track=", 6) == 0) {
1287 char *track_index_str = argv[optind + i];
1288 strsep(&track_index_str, "=");
1289 *meta_container = strtoul(track_index_str, NULL, 10);
1290 }
1291 }
1292
1293 if (*argv[start_optargs + i] == '-') {
1294 break; // we've hit another cli argument or deleting some frame
1295 }
1296 i++;
1297 }
1298 return;
1299 }
1300
1301 const char *
1302 find_ID3_optarg(char *argv[], int start_optargs, const char *arg_string) {
1303 const char *ret_val = "";
1304 uint16_t i = 0;
1305 uint8_t arg_prefix_len = strlen(arg_string);
1306
1307 while (argv[start_optargs + i] != NULL) {
1308 if (argv[start_optargs + i] && start_optargs + i <= total_args) {
1309 if (strcmp(arg_string, "compressed") == 0 &&
1310 strcmp(argv[start_optargs + i], "compressed") == 0) {
1311 return "1";
1312 }
1313 // if (strcmp(arg_string, "text++") == 0 && strcmp(argv[start_optargs +
1314 // i], "text++") == 0) {
1315 // return "1";
1316 //}
1317 if (strncmp(argv[start_optargs + i], arg_string, arg_prefix_len) == 0) {
1318 ret_val = argv[start_optargs + i] + arg_prefix_len;
1319 break;
1320 }
1321 }
1322 if (*argv[start_optargs + i] == '-') {
1323 break; // we've hit another cli argument or deleting some frame
1324 }
1325 i++;
1326 }
1327 return ret_val;
1328 }
1329
1330 //***********************************************
1331
1332 static void show_short_help(void) {
1333 printf("%s\n", shortHelp_text);
1334 ShowVersionInfo();
1335 printf("\nSubmit bug fixes to https://github.com/wez/atomicparsley\n");
1336 }
1337
1338 int real_main(int argc, char *argv[]) {
1339 if (argc == 1) {
1340 show_short_help();
1421341 exit(0);
143 }
144
145 //less than 80 (max 78) char wide, giving a general (concise) overview
146 static const char* shortHelp_text =
147 "\n"
148 "AtomicParlsey sets metadata into MPEG-4 files & derivatives supporting 3 tag\n"
149 " schemes: iTunes-style, 3GPP assets & ISO defined copyright notifications.\n"
150 "\n"
151 "AtomicParlsey quick help for setting iTunes-style metadata into MPEG-4 files.\n"
152 "\n"
153 "General usage examples:\n"
154 " AtomicParsley /path/to.mp4 -T 1\n"
155 " AtomicParsley /path/to.mp4 -t +\n"
156 " AtomicParsley /path/to.mp4 --artist \"Me\" --artwork /path/to/art.jpg\n"
157 " Atomicparsley /path/to.mp4 --albumArtist \"You\" --podcastFlag true\n"
158 " Atomicparsley /path/to.mp4 --stik \"TV Show\" --advisory explicit\n"
159 "\n"
160 "Getting information about the file & tags:\n"
161 " -T --test Test file for mpeg4-ishness & print atom tree\n"
162 " -t --textdata Prints tags embedded within the file\n"
163 " -E --extractPix Extracts pix to the same folder as the mpeg-4 file\n"
164 "\n"
165 "Setting iTunes-style metadata tags\n"
166 " --artist (string) Set the artist tag\n"
167 " --title (string) Set the title tag\n"
168 " --album (string) Set the album tag\n"
169 " --genre (string) Genre tag (see --longhelp for more info)\n"
170 " --tracknum (num)[/tot] Track number (or track number/total tracks)\n"
171 " --disk (num)[/tot] Disk number (or disk number/total disks)\n"
172 " --comment (string) Set the comment tag\n"
173 " --year (num|UTC) Year tag (see --longhelp for \"Release Date\")\n"
174 " --lyrics (string) Set lyrics (not subject to 256 byte limit)\n"
175 " --lyricsFile (/path) Set lyrics to the content of a file\n"
176 " --composer (string) Set the composer tag\n"
177 " --copyright (string) Set the copyright tag\n"
178 " --grouping (string) Set the grouping tag\n"
179 " --artwork (/path) Set a piece of artwork (jpeg or png only)\n"
180 " --bpm (number) Set the tempo/bpm\n"
181 " --albumArtist (string) Set the album artist tag\n"
182 " --compilation (boolean) Set the compilation flag (true or false)\n"
183 " --hdvideo (boolean) Set the hdvideo flag (true or false)\n"
184 " --advisory (string*) Content advisory (*values: 'clean', 'explicit')\n"
185 " --stik (string*) Sets the iTunes \"stik\" atom (see --longhelp)\n"
186 " --description (string) Set the description tag\n"
187 " --longdesc (string) Set the long description tag\n"
188 " --storedesc (string) Set the store description tag\n"
189 " --TVNetwork (string) Set the TV Network name\n"
190 " --TVShowName (string) Set the TV Show name\n"
191 " --TVEpisode (string) Set the TV episode/production code\n"
192 " --TVSeasonNum (number) Set the TV Season number\n"
193 " --TVEpisodeNum (number) Set the TV Episode number\n"
194 " --podcastFlag (boolean) Set the podcast flag (true or false)\n"
195 " --category (string) Sets the podcast category\n"
196 " --keyword (string) Sets the podcast keyword\n"
197 " --podcastURL (URL) Set the podcast feed URL\n"
198 " --podcastGUID (URL) Set the episode's URL tag\n"
199 " --purchaseDate (UTC) Set time of purchase\n"
200 " --encodingTool (string) Set the name of the encoder\n"
201 " --encodedBy (string) Set the name of the Person/company who encoded the file\n"
202 " --apID (string) Set the Account Name\n"
203 " --cnID (number) Set the iTunes Catalog ID (see --longhelp)\n"
204 " --geID (number) Set the iTunes Genre ID (see --longhelp)\n"
205 " --xID (string) Set the vendor-supplied iTunes xID (see --longhelp)\n"
206 " --gapless (boolean) Set the gapless playback flag\n"
207 " --contentRating (string*) Set tv/mpaa rating (see -rDNS-help)\n"
208 "\n"
209 "Deleting tags\n"
210 " Set the value to \"\": --artist \"\" --stik \"\" --bpm \"\"\n"
211 " To delete (all) artwork: --artwork REMOVE_ALL\n"
212 " manually removal: --manualAtomRemove \"moov.udta.meta.ilst.ATOM\"\n"
213 "\n"
214 "More detailed iTunes help is available with AtomicParsley --longhelp\n"
215 "Setting reverse DNS forms for iTunes files: see --reverseDNS-help\n"
216 "Setting 3gp assets into 3GPP & derivative files: see --3gp-help\n"
217 "Setting copyright notices for all files: see --ISO-help\n"
218 "For file-level options & padding info: see --file-help\n"
219 "Setting custom private tag extensions: see --uuid-help\n"
220 "Setting ID3 tags onto mpeg-4 files: see --ID3-help\n"
221 "\n"
222 "----------------------------------------------------------------------"
223 ;
224
225 //an expansive, verbose, unconstrained (about 112 char wide) detailing of options
226 static const char* longHelp_text =
227 "AtomicParsley help page for setting iTunes-style metadata into MPEG-4 files. \n"
228 " (3gp help available with AtomicParsley --3gp-help)\n"
229 " (ISO copyright help available with AtomicParsley --ISO-help)\n"
230 " (reverse DNS form help available with AtomicParsley --reverseDNS-help)\n"
231 "Usage: AtomicParsley [mp4FILE]... [OPTION]... [ARGUMENT]... [ [OPTION2]...[ARGUMENT2]...] \n"
232 "\n"
233 "example: AtomicParsley /path/to.mp4 -e ~/Desktop/pix\n"
234 "example: AtomicParsley /path/to.mp4 --podcastURL \"http://www.url.net\" --tracknum 45/356\n"
235 "example: AtomicParsley /path/to.mp4 --copyright \"\342\204\227 \302\251 2006\"\n"
236 "example: AtomicParsley /path/to.mp4 --year \"2006-07-27T14:00:43Z\" --purchaseDate timestamp\n"
237 "example: AtomicParsley /path/to.mp4 --sortOrder artist \"Mighty Dub Cats, The\n"
238 "------------------------------------------------------------------------------------------------\n"
239 " Extract any pictures in user data \"covr\" atoms to separate files. \n"
240 " --extractPix , -E Extract to same folder (basename derived from file).\n"
241 " --extractPixToPath , -e (/path/basename) Extract to specific path (numbers added to basename).\n"
242 " example: --e ~/Desktop/SomeText\n"
243 " gives: SomeText_artwork_1.jpg SomeText_artwork_2.png\n"
244 " Note: extension comes from embedded image file format\n"
245 "------------------------------------------------------------------------------------------------\n"
246 " Tag setting options:\n"
247 "\n"
248 " --artist , -a (str) Set the artist tag: \"moov.udta.meta.ilst.\302©ART.data\"\n"
249 " --title , -s (str) Set the title tag: \"moov.udta.meta.ilst.\302©nam.data\"\n"
250 " --album , -b (str) Set the album tag: \"moov.udta.meta.ilst.\302©alb.data\"\n"
251 " --genre , -g (str) Set the genre tag: \"\302©gen\" (custom) or \"gnre\" (standard).\n"
252 " see the standard list with \"AtomicParsley --genre-list\"\n"
253 " --tracknum , -k (num)[/tot] Set the track number (or track number & total tracks).\n"
254 " --disk , -d (num)[/tot] Set the disk number (or disk number & total disks).\n"
255 " --comment , -c (str) Set the comment tag: \"moov.udta.meta.ilst.\302©cmt.data\"\n"
256 " --year , -y (num|UTC) Set the year tag: \"moov.udta.meta.ilst.\302©day.data\"\n"
257 " set with UTC \"2006-09-11T09:00:00Z\" for Release Date\n"
258 " --lyrics , -l (str) Set the lyrics tag: \"moov.udta.meta.ilst.\302©lyr.data\"\n"
259 " --lyricsFile , (/path) Set the lyrics tag to the content of a file\n"
260 " --composer , -w (str) Set the composer tag: \"moov.udta.meta.ilst.\302©wrt.data\"\n"
261 " --copyright , -x (str) Set the copyright tag: \"moov.udta.meta.ilst.cprt.data\"\n"
262 " --grouping , -G (str) Set the grouping tag: \"moov.udta.meta.ilst.\302©grp.data\"\n"
263 " --artwork , -A (/path) Set a piece of artwork (jpeg or png) on \"covr.data\"\n"
264 " Note: multiple pieces are allowed with more --artwork args\n"
265 " --bpm , -B (num) Set the tempo/bpm tag: \"moov.udta.meta.ilst.tmpo.data\"\n"
266 " --albumArtist , -A (str) Set the album artist tag: \"moov.udta.meta.ilst.aART.data\"\n"
267 " --compilation , -C (bool) Sets the \"cpil\" atom (true or false to delete the atom)\n"
268 " --hdvideo , -V (bool) Sets the \"hdvd\" atom (true or false to delete the atom)\n"
269 " --advisory , -y (1of3) Sets the iTunes lyrics advisory ('remove', 'clean', 'explicit') \n"
270 " --stik , -S (1of7) Sets the iTunes \"stik\" atom (--stik \"remove\" to delete) \n"
271 " \"Movie\", \"Normal\", \"TV Show\" .... others: \n"
272 " see the full list with \"AtomicParsley --stik-list\"\n"
273 " or set in an integer value with --stik value=(num)\n"
274 " Note: --stik Audiobook will change file extension to '.m4b'\n"
275 " --description , -p (str) Sets the description on the \"desc\" atom\n"
276 " --Rating , (str) Sets the Rating on the \"rate\" atom\n"
277 " --longdesc , -j (str) Sets the long description on the \"ldes\" atom\n"
278 " --storedesc , (str) Sets the iTunes store description on the \"sdes\" atom\n"
279 " --TVNetwork , -n (str) Sets the TV Network name on the \"tvnn\" atom\n"
280 " --TVShowName , -H (str) Sets the TV Show name on the \"tvsh\" atom\n"
281 " --TVEpisode , -I (str) Sets the TV Episode on \"tven\":\"209\", but it is a string: \"209 Part 1\"\n"
282 " --TVSeasonNum , -U (num) Sets the TV Season number on the \"tvsn\" atom\n"
283 " --TVEpisodeNum , -N (num) Sets the TV Episode number on the \"tves\" atom\n"
284
285 " --podcastFlag , -f (bool) Sets the podcast flag (values are \"true\" or \"false\")\n"
286 " --category , -q (str) Sets the podcast category; typically a duplicate of its genre\n"
287 " --keyword , -K (str) Sets the podcast keyword; invisible to MacOSX Spotlight\n"
288 " --podcastURL , -L (URL) Set the podcast feed URL on the \"purl\" atom\n"
289 " --podcastGUID , -J (URL) Set the episode's URL tag on the \"egid\" atom\n"
290 " --purchaseDate , -D (UTC) Set Universal Coordinated Time of purchase on a \"purd\" atom\n"
291 " (use \"timestamp\" to set UTC to now; can be akin to id3v2 TDTG tag)\n"
292 " --encodingTool , (str) Set the name of the encoder on the \"\302©too\" atom\n"
293 " --encodedBy , (str) Set the name of the Person/company who encoded the file on the \"\302©enc\" atom\n"
294 " --apID , -Y (str) Set the name of the Account Name on the \"apID\" atom\n"
295 " --cnID , (num) Set iTunes Catalog ID, used for combining SD and HD encodes in iTunes on the \"cnID\" atom\n"
296 "\n"
297 " To combine you must set \"hdvd\" atom on one file and must have same \"stik\" on both file\n"
298 " Must not use \"stik\" of value Home Video(0), use Movie(9)\n"
299 "\n"
300 " iTunes Catalog numbers can be obtained by finding the item in the iTunes Store. Once item\n"
301 " is found in the iTunes Store right click on picture of item and select copy link. Paste this link\n"
302 " into a document or web browser to display the catalog number ID.\n"
303 "\n"
304 " An example link for the video Street Kings is:\n"
305 " http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewMovie?id=278743714&s=143441\n"
306 " Here you can see the cnID is 278743714\n"
307 "\n"
308 " Alternatively you can use iMDB numbers, however these will not match the iTunes catalog.\n"
309 "\n"
310 " --geID , (num) Set iTunes Genre ID. This does not necessarily have to match genre.\n"
311 " See --genre-movie-id-list and --genre-tv-id-list\n"
312 "\n"
313 " --xID , (str) Set iTunes vendor-supplied xID, used to allow iTunes LPs and iTunes Extras to interact \n"
314 " with other content in your iTunes Library\n"
315 " --gapless , (bool) Sets the gapless playback flag for a track in a gapless album\n"
316
317 " --sortOrder (type) (str) Sets the sort order string for that type of tag.\n"
318 " (available types are: \"name\", \"artist\", \"albumartist\",\n"
319 " \"album\", \"composer\", \"show\")\n"
320 "\n"
321 "NOTE: Except for artwork, only 1 of each tag is allowed; artwork allows multiple pieces.\n"
322 "NOTE: Tags that carry text(str) have a limit of 255 utf8 characters;\n"
323 "however lyrics and long descriptions have no limit.\n"
324 "------------------------------------------------------------------------------------------------\n"
325 " To delete a single atom, set the tag to null (except artwork):\n"
326 " --artist \"\" --lyrics \"\"\n"
327 " --artwork REMOVE_ALL \n"
328 " --metaEnema , -P Douches away every atom under \"moov.udta.meta.ilst\" \n"
329 " --foobar2000Enema , -2 Eliminates foobar2000's non-compliant so-out-o-spec tagging scheme\n"
330 " --manualAtomRemove \"some.atom.path\" where some.atom.path can be:\n"
331 " keys to using manualAtomRemove:\n"
332 " ilst.ATOM.data or ilst.ATOM target an iTunes-style metadata tag\n"
333 " ATOM:lang=foo target an atom with this language setting; like 3gp assets\n"
334 " ATOM.----.name:[foo] target a reverseDNS metadata tag; like iTunNORM\n"
335 " Note: these atoms show up with 'AP -t' as: Atom \"----\" [foo]\n"
336 " 'foo' is actually carried on the 'name' atom\n"
337 " ATOM[x] target an atom with an index other than 1; like trak[2]\n"
338 " ATOM.uuid=hex-hex-hex-hex targt a uuid atom with the uuid of hex string representation\n"
339 " examples:\n"
340 " moov.udta.meta.ilst.----.name:[iTunNORM] moov.trak[3].cprt:lang=urd\n"
341 " moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740\n"
342 "------------------------------------------------------------------------------------------------\n"
343
344 #if defined (DARWIN_PLATFORM)
345 " Environmental Variables (affecting picture placement)\n"
346 "\n"
347 " set PIC_OPTIONS in your shell to set these flags; preferences are separated by colons (:)\n"
348 "\n"
349 " MaxDimensions=num (default: 0; unlimited); sets maximum pixel dimensions\n"
350 " DPI=num (default: 72); sets dpi\n"
351 " MaxKBytes=num (default: 0; unlimited); maximum kilobytes for file (jpeg only)\n"
352 " AddBothPix=bool (default: false); add original & converted pic (for archival purposes)\n"
353 " AllPixJPEG | AllPixPNG =bool (default: false); force conversion to a specific picture format\n"
354 " SquareUp (include to square images to largest dimension, allows an [ugly] 160x1200->1200x1200)\n"
355 " removeTempPix (include to delete temp pic files created when resizing images after tagging)\n"
356 " ForceHeight=num (must also specify width, below) force image pixel height\n"
357 " ForceWidth=num (must also specify height, above) force image pixel width\n"
358 "\n"
359 " Examples: (bash-style)\n"
360 " export PIC_OPTIONS=\"MaxDimensions=400:DPI=72:MaxKBytes=100:AddBothPix=true:AllPixJPEG=true\"\n"
361 " export PIC_OPTIONS=\"SquareUp:removeTempPix\"\n"
362 " export PIC_OPTIONS=\"ForceHeight=999:ForceWidth=333:removeTempPix\"\n"
363 "------------------------------------------------------------------------------------------------\n"
1342 } else if (argc == 2 && ((strcmp(argv[1], "-v") == 0) ||
1343 (strcmp(argv[1], "-version") == 0) ||
1344 (strcmp(argv[1], "--version") == 0))) {
1345 ShowVersionInfo();
1346 exit(0);
1347 } else if (argc == 2) {
1348 if ((strcmp(argv[1], "-help") == 0) || (strcmp(argv[1], "--help") == 0) ||
1349 (strcmp(argv[1], "-h") == 0)) {
1350 show_short_help();
1351 exit(0);
1352 } else if ((strcmp(argv[1], "--longhelp") == 0) ||
1353 (strcmp(argv[1], "-longhelp") == 0) ||
1354 (strcmp(argv[1], "-Lh") == 0)) {
1355 #if defined(_WIN32) && !defined(__CYGWIN__)
1356 if (UnicodeOutputStatus == WIN32_UTF16) { // convert the helptext to utf16
1357 // to preserve \251 characters
1358 int help_len = strlen(longHelp_text) + 1;
1359 wchar_t *Lhelp_text = (wchar_t *)malloc(sizeof(wchar_t) * help_len);
1360 wmemset(Lhelp_text, 0, help_len);
1361 UTF8ToUTF16LE((unsigned char *)Lhelp_text,
1362 2 * help_len,
1363 (unsigned char *)longHelp_text,
1364 help_len);
1365 APar_unicode_win32Printout(Lhelp_text, (char *)longHelp_text);
1366 free(Lhelp_text);
1367 } else
3641368 #endif
365 ;
366
367 static const char* fileLevelHelp_text =
368 "AtomicParsley help page for general & file level options.\n"
369 #if defined (_WIN32)
370 #ifndef __CYGWIN__
371 " Note: you can change the input/output behavior to raw 8-bit utf8 if the program name\n"
372 " is appended with \"-utf8\". AtomicParsley-utf8.exe will have problems with files/\n"
373 " folders with unicode characters in given paths.\n"
374 #else
375 " Note: you can change the input/output behavior for MCDI functions to raw 8-bit utf8\n"
376 " if the program name is appended with \"-utf8\".\n"
1369 {
1370 fprintf(stdout, "%s", longHelp_text);
1371 }
1372 exit(0);
1373
1374 } else if ((strcmp(argv[1], "--3gp-help") == 0) ||
1375 (strcmp(argv[1], "-3gp-help") == 0) ||
1376 (strcmp(argv[1], "--3gp-h") == 0)) {
1377 fprintf(stdout, "%s\n", _3gpHelp_text);
1378 exit(0);
1379
1380 } else if ((strcmp(argv[1], "--ISO-help") == 0) ||
1381 (strcmp(argv[1], "--iso-help") == 0) ||
1382 (strcmp(argv[1], "-Ih") == 0)) {
1383 fprintf(stdout, "%s\n", ISOHelp_text);
1384 exit(0);
1385
1386 } else if ((strcmp(argv[1], "--file-help") == 0) ||
1387 (strcmp(argv[1], "-file-help") == 0) ||
1388 (strcmp(argv[1], "-fh") == 0)) {
1389 fprintf(stdout, "%s\n", fileLevelHelp_text);
1390 exit(0);
1391
1392 } else if ((strcmp(argv[1], "--uuid-help") == 0) ||
1393 (strcmp(argv[1], "-uuid-help") == 0) ||
1394 (strcmp(argv[1], "-uh") == 0)) {
1395 fprintf(stdout, "%s\n", uuidHelp_text);
1396 exit(0);
1397
1398 } else if ((strcmp(argv[1], "--reverseDNS-help") == 0) ||
1399 (strcmp(argv[1], "-rDNS-help") == 0) ||
1400 (strcmp(argv[1], "-rh") == 0)) {
1401 fprintf(stdout, "%s\n", rDNSHelp_text);
1402 exit(0);
1403
1404 } else if ((strcmp(argv[1], "--ID3-help") == 0) ||
1405 (strcmp(argv[1], "-ID3-help") == 0) ||
1406 (strcmp(argv[1], "-ID3h") == 0)) {
1407 fprintf(stdout, "%s\n", ID3Help_text);
1408 exit(0);
1409
1410 } else if (strcmp(argv[1], "--genre-list") == 0) {
1411 ListGenresValues();
1412 exit(0);
1413
1414 } else if (strcmp(argv[1], "--stik-list") == 0) {
1415 ListStikValues();
1416 exit(0);
1417
1418 } else if (strcmp(argv[1], "--language-list") == 0 ||
1419 strcmp(argv[1], "--languages-list") == 0 ||
1420 strcmp(argv[1], "--list-language") == 0 ||
1421 strcmp(argv[1], "--list-languages") == 0 ||
1422 strcmp(argv[1], "-ll") == 0) {
1423 ListLanguageCodes();
1424 exit(0);
1425
1426 } else if (strcmp(argv[1], "--ratings-list") == 0) {
1427 ListMediaRatings();
1428 exit(0);
1429
1430 } else if (strcmp(argv[1], "--genre-movie-id-list") == 0) {
1431 ListMovieGenreIDValues();
1432 exit(0);
1433
1434 } else if (strcmp(argv[1], "--genre-tv-id-list") == 0) {
1435 ListTVGenreIDValues();
1436 exit(0);
1437
1438 } else if (strcmp(argv[1], "--ID3frames-list") == 0) {
1439 ListID3FrameIDstrings();
1440 exit(0);
1441
1442 } else if (strcmp(argv[1], "--imagetype-list") == 0) {
1443 List_imagtype_strings();
1444 exit(0);
1445 }
1446 }
1447
1448 if (argc == 3 &&
1449 (strcmp(argv[2], "--brands") == 0 || strcmp(argv[2], "-brands") == 0)) {
1450 APar_ExtractBrands(argv[1]);
1451 exit(0);
1452 }
1453
1454 int extr = 99;
1455 total_args = argc;
1456 char *ISObasemediafile = argv[1];
1457
1458 TestFileExistence(ISObasemediafile, true);
1459
1460 char *padding_options = getenv("AP_PADDING");
1461 ExtractPaddingPrefs(padding_options);
1462
1463 // it would probably be better to test output_file if provided & if
1464 // --overWrite was provided.... probably only of use on Windows - and I'm not
1465 // on it.
1466 if (strlen(ISObasemediafile) + 11 > MAXPATHLEN) {
1467 fprintf(stderr,
1468 "%c %s",
1469 '\a',
1470 "AtomicParsley error: filename/filepath was too long.\n");
1471 exit(1);
1472 }
1473
1474 if (argc > 3 && strcmp(argv[2], "--DeepScan") == 0) {
1475 deep_atom_scan = true;
1476 APar_ScanAtoms(ISObasemediafile, true);
1477 }
1478
1479 while (1) {
1480 static struct option long_options[] = {
1481 {"help", 0, NULL, OPT_HELP},
1482 {"test", optional_argument, NULL, OPT_TEST},
1483 {"textdata", optional_argument, NULL, OPT_ShowTextData},
1484 {"extractPix", 0, NULL, OPT_ExtractPix},
1485 {"extractPixToPath", required_argument, NULL, OPT_ExtractPixToPath},
1486 {"artist", required_argument, NULL, Meta_artist},
1487 {"artDirector", required_argument, NULL, Meta_artDirector},
1488 {"arranger", required_argument, NULL, Meta_arranger},
1489 {"author", required_argument, NULL, Meta_author},
1490 {"conductor", required_argument, NULL, Meta_conductor},
1491 {"director", required_argument, NULL, Meta_director},
1492 {"originalArtist", required_argument, NULL, Meta_originalArtist},
1493 {"producer", required_argument, NULL, Meta_producer},
1494 // { "performer", required_argument, NULL, Meta_performer
1495 // },
1496 {"soundEngineer", required_argument, NULL, Meta_soundEngineer},
1497 {"soloist", required_argument, NULL, Meta_soloist},
1498 {"executiveProducer", required_argument, NULL, Meta_executiveProducer},
1499 {"title", required_argument, NULL, Meta_songtitle},
1500 {"subtitle", required_argument, NULL, Meta_subtitle},
1501 {"album", required_argument, NULL, Meta_album},
1502 {"genre", required_argument, NULL, Meta_genre},
1503 {"tracknum", required_argument, NULL, Meta_tracknum},
1504 {"disknum", required_argument, NULL, Meta_disknum},
1505 {"comment", required_argument, NULL, Meta_comment},
1506 {"year", required_argument, NULL, Meta_year},
1507 {"lyrics", required_argument, NULL, Meta_lyrics},
1508 {"lyricsFile", required_argument, NULL, Meta_lyrics_file},
1509 {"composer", required_argument, NULL, Meta_composer},
1510 {"copyright", required_argument, NULL, Meta_copyright},
1511 {"grouping", required_argument, NULL, Meta_grouping},
1512 {"albumArtist", required_argument, NULL, Meta_album_artist},
1513 {"compilation", required_argument, NULL, Meta_compilation},
1514 {"hdvideo", required_argument, NULL, Meta_hdvideo},
1515 {"advisory", required_argument, NULL, Meta_advisory},
1516 {"bpm", required_argument, NULL, Meta_BPM},
1517 {"artwork", required_argument, NULL, Meta_artwork},
1518 {"stik", required_argument, NULL, Meta_stik},
1519 {"description", required_argument, NULL, Meta_description},
1520 {"longdesc", required_argument, NULL, Meta_longdescription},
1521 {"storedesc", required_argument, NULL, Meta_storedescription},
1522 {"Rating", required_argument, NULL, Meta_Rating},
1523 {"TVNetwork", required_argument, NULL, Meta_TV_Network},
1524 {"TVShowName", required_argument, NULL, Meta_TV_ShowName},
1525 {"TVEpisode", required_argument, NULL, Meta_TV_Episode},
1526 {"TVEpisodeNum", required_argument, NULL, Meta_TV_EpisodeNumber},
1527 {"TVSeasonNum", required_argument, NULL, Meta_TV_SeasonNumber},
1528 {"podcastFlag", required_argument, NULL, Meta_podcastFlag},
1529 {"keyword", required_argument, NULL, Meta_keyword},
1530 {"category", required_argument, NULL, Meta_category},
1531 {"podcastURL", required_argument, NULL, Meta_podcast_URL},
1532 {"podcastGUID", required_argument, NULL, Meta_podcast_GUID},
1533 {"purchaseDate", required_argument, NULL, Meta_PurchaseDate},
1534 {"encodingTool", required_argument, NULL, Meta_EncodingTool},
1535 {"encodedBy", required_argument, NULL, Meta_EncodedBy},
1536 {"apID", required_argument, NULL, Meta_apID},
1537 {"cnID", required_argument, NULL, Meta_cnID},
1538 {"geID", required_argument, NULL, Meta_geID},
1539 {"xID", required_argument, NULL, Meta_xID},
1540 {"gapless", required_argument, NULL, Meta_PlayGapless},
1541 {"sortOrder", required_argument, NULL, Meta_SortOrder},
1542
1543 {"rDNSatom", required_argument, NULL, Meta_ReverseDNS_Form},
1544 {"contentRating", required_argument, NULL, Meta_rDNS_rating},
1545
1546 {"tagtime", optional_argument, NULL, Meta_StandardDate},
1547 {"information", required_argument, NULL, Meta_Information},
1548 {"url", required_argument, NULL, Meta_URL},
1549 {"meta-uuid", required_argument, NULL, Meta_uuid},
1550 {"extract-uuids", optional_argument, NULL, Opt_Extract_all_uuids},
1551 {"extract1uuid", required_argument, NULL, Opt_Extract_a_uuid},
1552 {"iPod-uuid", required_argument, NULL, Opt_Ipod_AVC_uuid},
1553
1554 {"freefree", optional_argument, NULL, Opt_FreeFree},
1555 {"metaEnema", 0, NULL, Metadata_Purge},
1556 {"manualAtomRemove", required_argument, NULL, Manual_atom_removal},
1557 {"udtaEnema", 0, NULL, UserData_Purge},
1558 {"foobar2000Enema", 0, NULL, foobar_purge},
1559 {"metaDump", 0, NULL, Meta_dump},
1560 {"output", required_argument, NULL, OPT_OutputFile},
1561 {"preventOptimizing", 0, NULL, OPT_NoOptimize},
1562 {"overWrite", 0, NULL, OPT_OverWrite},
1563 #if defined(_WIN32)
1564 {"preserveTime", 0, NULL, OPT_PreserveTimeStamps},
3771565 #endif
378 "\n"
379 #endif
380 "------------------------------------------------------------------------------------------------\n"
381 " Atom reading services:\n"
382 "\n"
383 " --test , -T Tests file to see if it is a valid MPEG-4 file.\n"
384 " Prints out the hierarchical atom tree.\n"
385 " -T 1 Supplemental track level info with \"-T 1\"\n"
386 " -T +dates Track level with creation/modified dates\n"
387 "\n"
388 " --textdata , -t print user data text metadata relevant to brand (inc. # of any pics).\n"
389 " -t + show supplemental info like free space, available padding, user data\n"
390 " length & media data length\n"
391 " -t 1 show all textual metadata (disregards brands, shows track copyright)\n"
392 "\n"
393 " --brands show the major & minor brands for the file & available tagging schemes\n"
394 "\n"
395 "------------------------------------------------------------------------------------------------\n"
396 " File services:\n"
397 "\n"
398 " --freefree [num] , Remove \"free\" atoms which only act as filler in the file\n"
399 " ?(num)? - optional integer argument to delete 'free's to desired level\n"
400 "\n"
401 " NOTE 1: levels begin at level 1 aka file level.\n"
402 " NOTE 2: Level 0 (which doesn't exist) deletes level 1 atoms that pre-\n"
403 " cede 'moov' & don't serve as padding. Typically, such atoms\n"
404 " are created by libmp4ff or libmp4v2 as a byproduct of tagging.\n"
405 " NOTE 3: When padding falls below MIN_PAD (typically zero), a default\n"
406 " amount of padding (typically 2048 bytes) will be added. To\n"
407 " achieve absolutely 0 bytes 'free' space with --freefree, set\n"
408 " DEFAULT_PAD to 0 via the AP_PADDING mechanism (see below).\n"
409 "\n"
410 " --preventOptimizing Prevents reorganizing the file to have file metadata before media data.\n"
411 " iTunes/Quicktime have so far *always* placed metadata first; many 3rd\n"
412 " party utilities do not (preventing streaming to the web, AirTunes, iTV).\n"
413 " Used in conjunction with --overWrite, files with metadata at the end\n"
414 " (most ffmpeg produced files) can have their tags rapidly updated without\n"
415 " requiring a full rewrite. Note: this does not un-optimize a file.\n"
416 " Note: this option will be canceled out if used with the --freefree option\n"
417 "\n"
418 " --metaDump Dumps out 'moov.udta' metadata out to a new file next to original\n"
419 " (for diagnostic purposes, please remove artwork before sending)\n"
420 " --output , -o (/path) Specify the filename of tempfile (voids overWrite)\n"
421 " --overWrite , -W Writes to temp file; deletes original, renames temp to original\n"
422 " If possible, padding will be used to update without a full rewrite.\n"
423 "\n"
424 #if defined (_WIN32)
425 " --preserveTime Will overwrite the original file in place (--overWrite forced),\n"
426 " but will also keep the original file's timestamps intact.\n"
427 "\n"
428 #endif
429 " --DeepScan Parse areas of the file that are normally skipped (must be the 3rd arg)\n"
430 " --iPod-uuid (num) Place the ipod-required uuid for higher resolution avc video files\n"
431 " Currently, the only number used is 1200 - the maximum number of macro-\n"
432 " blocks allowed by the higher resolution iPod setting.\n"
433 " NOTE: this requires the \"--DeepScan\" option as the 3rd cli argument\n"
434 " NOTE2: only works on the first avc video track, not all avc tracks\n"
435 "\n"
436 "Examples: \n"
437 " --freefree 0 (deletes all top-level non-padding atoms preceding 'mooov') \n"
438 " --freefree 1 (deletes all non-padding atoms at the top most level) \n"
439 " --output ~/Desktop/newfile.mp4\n"
440 " AP /path/to/file.m4v --DeepScan --iPod-uuid 1200\n"
441
442 "------------------------------------------------------------------------------------------------\n"
443 " Padding & 'free' atoms:\n"
444 "\n"
445 " A special type of atom called a 'free' atom is used for padding (all 'free' atoms contain NULL space).\n"
446 " When changes need to occur, these 'free' atom are used. They grows or shink, but the relative locations\n"
447 " of certain other atoms (stco/mdat) remain the same. If there is no 'free' space, a full rewrite will occur.\n"
448 " The locations of 'free' atom(s) that AP can use as padding must be follow 'moov.udta' & come before 'mdat'.\n"
449 " A 'free' preceding 'moov' or following 'mdat' won't be used as padding for example. \n"
450 "\n"
451 " Set the shell variable AP_PADDING with these values, separated by colons to alter padding behavior:\n"
452 "\n"
453 " DEFAULT_PADDING= - the amount of padding added if the minimum padding is non-existant in the file\n"
454 " default = 2048\n"
455 " MIN_PAD= - the minimum padding present before more padding will be added\n"
456 " default = 0\n"
457 " MAX_PAD= - the maximum allowable padding; excess padding will be eliminated\n"
458 " default = 5000\n"
459 "\n"
460 " If you use --freefree to eliminate 'free' atoms from the file, the DEFAULT_PADDING amount will still be\n"
461 " added to any newly written files. Set DEFAULT_PADDING=0 to prevent any 'free' padding added at rewrite.\n"
462 " You can set MIN_PAD to be assured that at least that amount of padding will be present - similarly,\n"
463 " MAX_PAD limits any excessive amount of padding. All 3 options will in all likelyhood produce a full\n"
464 " rewrite of the original file. Another case where a full rewrite will occur is when the original file\n"
465 " is not optimized and has 'mdat' preceding 'moov'.\n"
466 "\n"
467 #if defined (_WIN32) && !defined (__CYGWIN__)
468 "Examples:\n"
469 " c:> SET AP_PADDING=\"DEFAULT_PAD=0\" or c:> SET AP_PADDING=\"DEFAULT_PAD=3128\"\n"
470 " c:> SET AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n"
471 #else
472 "Examples (bash style):\n"
473 " $ export AP_PADDING=\"DEFAULT_PAD=0\" or $ export AP_PADDING=\"DEFAULT_PAD=3128\"\n"
474 " $ export AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n"
475 #endif
476 "\n"
477 "Note: while AtomicParsley is still in the beta stage, the original file will always remain untouched - \n"
478 " unless given the --overWrite flag when if possible, utilizing available padding to update tags\n"
479 " will be tried (falling back to a full rewrite if changes are greater than the found padding).\n"
480 "----------------------------------------------------------------------------------------------------\n"
481 " iTunes 7 & Gapless playback:\n"
482 "\n"
483 " iTunes 7 adds NULL space at the ends of files (filled with zeroes). It is possble this is how iTunes\n"
484 " implements gapless playback - perhaps not. In any event, with AtomicParsley you can choose to preserve\n"
485 " that NULL space, or you can eliminate its presence (typically around 2,000 bytes). The default behavior\n"
486 " is to preserve it - if it is present at all. You can choose to eliminate it by setting the environ-\n"
487 " mental preference for AP_PADDING to have DEFAULT_PAD=0\n"
488 "\n"
489 #if defined (_WIN32) && !defined (__CYGWIN__)
490 "Example:\n"
491 " c:> SET AP_PADDING=\"DEFAULT_PAD=0\"\n"
492 #else
493 "Example (bash style):\n"
494 " $ export AP_PADDING=\"DEFAULT_PAD=0\"\n"
495 #endif
496 "----------------------------------------------------------------------------------------------------\n"
497 ;
498
499 //detailed options for 3gp branded files
500 static const char* _3gpHelp_text =
501 "AtomicParsley 3gp help page for setting 3GPP-style metadata.\n"
502 "----------------------------------------------------------------------------------------------------\n"
503 " 3GPP text tags can be encoded in either UTF-8 (default input encoding) or UTF-16 (converted from UTF-8)\n"
504 " Many 3GPP text tags can be set for a desired language by a 3-letter-lowercase code (default is \"eng\").\n"
505 " For tags that support the language attribute (all except year), more than one tag of the same name\n"
506 " (3 titles for example) differing in the language code is supported.\n"
507 "\n"
508 " iTunes-style metadata is not supported by the 3GPP TS 26.244 version 6.4.0 Release 6 specification.\n"
509 " 3GPP asset tags can be set at movie level or track level & are set in a different hierarchy: moov.udta \n"
510 " if at movie level (versus iTunes moov.udta.meta.ilst). Other 3rd party utilities may allow setting\n"
511 " iTunes-style metadata in 3gp files. When a 3gp file is detected (file extension doesn't matter), only\n"
512 " 3gp spec-compliant metadata will be read & written.\n"
513 "\n"
514 " Note1: there are a number of different 'brands' that 3GPP files come marked as. Some will not be \n"
515 " supported by AtomicParsley due simply to them being unknown and untested. You can compile your\n"
516 " own AtomicParsley to evaluate it by adding the hex code into the source of APar_IdentifyBrand.\n"
517 "\n"
518 " Note2: There are slight accuracy discrepancies in location's fixed point decimals set and retrieved.\n"
519 "\n"
520 " Note3: QuickTime Player can see a limited subset of these tags, but only in 1 language & there seems to\n"
521 " be an issue with not all unicode text displaying properly. This is an issue withing QuickTime -\n"
522 " the exact same text (in utf8) displays properly in an MPEG-4 file. Some languages can also display\n"
523 " more glyphs than others.\n"
524 "\n"
525 "----------------------------------------------------------------------------------------------------\n"
526 " Tag setting options (default user data area is movie level; default lang is 'eng'; default encoding is UTF8):\n"
527 " required arguments are in (parentheses); optional arguments are in [brackets]\n"
528 "\n"
529 " --3gp-title (str) [lang=3str] [UTF16] [area] ......... Set a 3gp media title tag\n"
530 " --3gp-author (str) [lang=3str] [UTF16] [area] ......... Set a 3gp author of the media tag\n"
531 " --3gp-performer (str) [lang=3str] [UTF16] [area] ......... Set a 3gp performer or artist tag\n"
532 " --3gp-genre (str) [lang=3str] [UTF16] [area] ......... Set a 3gp genre asset tag\n"
533 " --3gp-description (str) [lang=3str] [UTF16] [area] ......... Set a 3gp description or caption tag\n"
534 " --3gp-copyright (str) [lang=3str] [UTF16] [area] ......... Set a 3gp copyright notice tag*\n"
535 "\n"
536 " --3gp-album (str) [lang=3str] [UTF16] [trknum=int] [area] Set a 3gp album tag (& opt. tracknum)\n"
537 " --3gp-year (int) [area] ........................... Set a 3gp recording year tag (4 digit only)\n"
538 "\n"
539 " --3gp-rating (str) [entity=4str] [criteria=4str] [lang=3str] [UTF16] [area] Rating tag\n"
540 " --3gp-classification (str) [entity=4str] [index=int] [lang=3str] [UTF16] [area] Classification\n"
541 "\n"
542 " --3gp-keyword (str) [lang=3str] [UTF16] [area] Format of str: 'keywords=word1,word2,word3,word4'\n"
543 "\n"
544 " --3gp-location (str) [lang=3str] [UTF16] [area] Set a 3gp location tag (default: Central Park)\n"
545 " [longitude=fxd.pt] [latitude=fxd.pt] [altitude=fxd.pt]\n"
546 " [role=str] [body=str] [notes=str]\n"
547 " fxd.pt values are decimal coordinates (55.01209, 179.25W, 63)\n"
548 " 'role=' values: 'shooting location', 'real location', 'fictional location'\n"
549 " a negative value in coordinates will be seen as a cli flag\n"
550 " append 'S', 'W' or 'B': lat=55S, long=90.23W, alt=90.25B\n"
551 "\n"
552 " [area] can be \"movie\", \"track\" or \"track=num\" where 'num' is the number of the track. If not specificied,\n"
553 " assets will be placed at movie level. The \"track\" option sets the asset across all available tracks\n"
554 "\n"
555 "Note1: '4str' = a 4 letter string like \"PG13\"; 3str is a 3 letter string like \"eng\"; int is an integer\n"
556 "Note2: List all languages for '3str' with \"AtomicParsley --language-list (unknown languages become \"und\")\n"
557 "*Note3: The 3gp copyright asset can potentially be altered by using the --ISO-copyright setting.\n"
558
559 "----------------------------------------------------------------------------------------------------\n"
560 "Usage: AtomicParsley [3gpFILE] --option [argument] [optional_arguments] [ --option2 [argument2]...] \n"
561 "\n"
562 "example: AtomicParsley /path/to.3gp -t \n"
563 "example: AtomicParsley /path/to.3gp -T 1 \n"
564 "example: Atomicparsley /path/to.3gp --3gp-performer \"Enjoy Yourself\" lang=pol UTF16\n"
565 "example: Atomicparsley /path/to.3gp --3gp-year 2006 --3gp-album \"White Label\" track=8 lang=fra\n"
566 "example: Atomicparsley /path/to.3gp --3gp-album \"Cow Cod Soup For Everyone\" track=10 lang=car\n"
567 "\n"
568 "example: Atomicparsley /path/to.3gp --3gp-classification \"Poor Sport\" entity=\"PTA \" index=12 UTF16\n"
569 "example: Atomicparsley /path/to.3gp --3gp-keyword keywords=\"foo1,foo2,foo 3\" UTF16 --3gp-keyword \"\"\n"
570 "example: Atomicparsley /path/to.3gp --3gp-location 'Bethesda Terrace' latitude=40.77 longitude=73.98W \n"
571 " altitude=4.3B role='real' body=Earth notes='Underground'\n"
572 "\n"
573 "example: Atomicparsley /path/to.3gp --3gp-title \"I see London.\" --3gp-title \"Veo Madrid.\" lang=spa \n"
574 " --3gp-title \"Widze Warsawa.\" lang=pol\n"
575 "\n"
576 ;
577
578 static const char* ISOHelp_text =
579 "AtomicParsley help page for setting ISO copyright notices at movie & track level.\n"
580 "----------------------------------------------------------------------------------------------------\n"
581 " The ISO specification allows for setting copyright in a number of places. This copyright atom is\n"
582 " independant of the iTunes-style --copyright tag that can be set. This ISO tag is identical to the\n"
583 " 3GP-style copyright. In fact, using --ISO-copyright can potentially overwrite the 3gp copyright\n"
584 " asset if set at movie level & given the same language to set the copyright on. This copyright\n"
585 " notice is the only metadata tag defined by the reference ISO 14496-12 specification.\n"
586 "\n"
587 " ISO copyright notices can be set at movie level, track level for a single track, or for all tracks.\n"
588 " Multiple copyright notices are allowed, but they must differ in the language setting. To see avail-\n"
589 " able languages use \"AtomicParsley --language-list\". Notices can be set in utf8 or utf16.\n"
590 "\n"
591 " --ISO-copyright (str) [movie|track|track=#] [lang=3str] [UTF16] Set a copyright notice\n"
592 " # in 'track=#' denotes the target track\n"
593 " 3str is the 3 letter ISO-639-2 language.\n"
594 " Brackets [] show optional parameters.\n"
595 " Defaults are: movie level, 'eng' in utf8.\n"
596 "\n"
597 "example: AtomicParsley /path/file.mp4 -t 1 Note: the only way to see all contents is with -t 1 \n"
598 "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\"\n"
599 "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" movie\n"
600 "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track=2 lang=urd\n"
601 "example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track UTF16\n"
602 "example: AP --ISO-copyright \"Example\" track --ISO-copyright \"Por Exemplo\" track=2 lang=spa UTF16\n"
603 "\n"
604 "Note: to remove the copyright, set the string to \"\" - the track and language must match the target.\n"
605 "example: --ISO-copyright \"\" track --ISO-copyright \"\" track=2 lang=spa\n"
606 "\n"
607 "Note: (foo) denotes required arguments; [foo] denotes optional parameters & may have defaults.\n"
608 ;
609
610 static const char* uuidHelp_text =
611 "AtomicParsley help page for setting uuid user extension metadata tags.\n"
612 "----------------------------------------------------------------------------------------------------\n"
613 " Setting a user-defined 'uuid' private extention tags will appear in \"moov.udta.meta\"). These will\n"
614 " only be read by AtomicParsley & can be set irrespective of file branding. The form of uuid that AP\n"
615 " is a v5 uuid generated from a sha1 hash of an atom name in an 'AtomicParsley.sf.net' namespace.\n"
616 "\n"
617 " The uuid form is in some Sony & Compressor files, but of version 4 (random/pseudo-random). An example\n"
618 " uuid of 'cprt' in the 'AtomicParsley.sf.net' namespace is: \"4bd39a57-e2c8-5655-a4fb-7a19620ef151\".\n"
619 " 'cprt' in the same namespace will always create that uuid; uuid atoms will only print out if the\n"
620 " uuid generated is the same as discovered. Sony uuids don't for example show up with AP -t.\n"
621 "\n"
622 " --information , -i (str) Set an information tag on uuid atom name\"©inf\"\n"
623 " --url , -u (URL) Set a URL tag on uuid atom name \"\302©url\"\n"
624 " --tagtime , timestamp Set the Coordinated Univeral Time of tagging on \"tdtg\"\n"
625 "\n"
626 " Define & set an arbitrary atom with a text data or embed a file:\n"
627 " --meta-uuid There are two forms: 1 for text & 1 for file operations\n"
628 " setting text form:\n"
629 " --meta-uuid (atom) \"text\" (str) \"atom\" = 4 character atom name of your choice\n"
630 " str is whatever text you want to set\n"
631 " file embedding form:\n"
632 " --meta-uuid (atom) \"file\" (/path) [description=\"foo\"] [mime-type=\"foo/moof\"]\n"
633 " \"atom\" = 4 character atom name of your choice\n"
634 " /path = path to the file that will be embedded*\n"
635 " description = optional description of the file\n"
636 " default is \"[none]\"\n"
637 " mime-type = optional mime type for the file\n"
638 " default is \"none\"\n"
639 " Note: no auto-disocevery of mime type\n"
640 " if you know/want it: supply it.\n"
641 " *Note: a file extension (/path/file.ext) is required\n"
642 "\n"
643 "Note: (foo) denotes required arguments; [foo] denotes optional arguments & may have defaults.\n"
644 "\n"
645 "Examples: \n"
646 " --tagtime timestamp --information \"[psst]I see metadata\" --url http://www.bumperdumper.com\n"
647 " --meta-uuid tagr text \"Johnny Appleseed\" --meta-uuid \302\251sft text \"OpenShiiva encoded.\"\n"
648 " --meta-uuid scan file /usr/pix/scans.zip\n"
649 " --meta-uuid 1040 file ../../2006_taxes.pdf description=\"Fooled 'The Man' yet again.\"\n"
650 "can be removed with:\n"
651 " --tagtime \"\" --information \"\" --url \" \" --meta-uuid scan file ""\n"
652 " --manualAtomRemove \"moov.udta.meta.uuid=672c98cd-f11f-51fd-adec-b0ee7b4d215f\" \\\n"
653 " --manualAtomRemove \"moov.udta.meta.uuid=1fed6656-d911-5385-9cb2-cb2c100f06e7\"\n"
654 "Remove the Sony uuid atoms with:\n"
655 " --manualAtomRemove moov.trak[1].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n"
656 " --manualAtomRemove moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n"
657 " --manualAtomRemove uuid=50524f46-21d2-4fce-bb88-695cfac9c740\n"
658 "\n"
659 "Viewing the contents of uuid atoms:\n"
660 " -t or --textdata Shows the uuid atoms (both text & file) that AP sets:\n"
661 " Example output:\n"
662 " Atom uuid=ec0f...d7 (AP uuid for \"scan\") contains: FILE.zip; description=[none]\n"
663 " Atom uuid=672c...5f (AP uuid for \"tagr\") contains: Johnny Appleseed\n"
664 "\n"
665 "Extracting an embedded file in a uuid atom:\n"
666 " --extract1uuid (atom) Extract file embedded within uuid=atom into same folder\n"
667 " (file will be named with suffix shown in --textdata)\n"
668 " --extract-uuids [/path] Extract all files in uuid atoms under the moov.udta.meta\n"
669 " hierarchy. If no /path is given, files will be extracted\n"
670 " to the same folder as the originating file.\n"
671 "\n"
672 " Examples:\n"
673 " --extract1uuid scan\n"
674 " ... Extracted uuid=scan attachment to file: /some/path/FILE_scan_uuid.zip\n"
675 " --extract-uuids ~/Desktop/plops\n"
676 " ... Extracted uuid=pass attachment to file: /Users/me/Desktop/plops_pass_uuid.pdf\n"
677 " ... Extracted uuid=site attachment to file: /Users/me/Desktop/plops_site_uuid.html\n"
678 "\n"
679 "------------------------------------------------------------------------------------------------\n"
680 ;
681
682 static const char* rDNSHelp_text =
683 "AtomicParsley help page for setting reverse domain '----' metadata atoms.\n"
684 "----------------------------------------------------------------------------------------------------\n"
685 " Please note that the reverse DNS format supported here is not feature complete.\n"
686 "\n"
687 " Another style of metadata that iTunes uses is called the reverse DNS format. For all known tags,\n"
688 " iTunes offers no user-accessible exposure to these tags or their contents. This reverse DNS form has\n"
689 " a differnt form than other iTunes tags that have a atom name that describes the content of 'data'\n"
690 " atom it contains. In the reverseDNS format, the parent to the structure called the '----' atom, with\n"
691 " children atoms that describe & contain the metadata carried. The 'mean' child contains the reverse\n"
692 " domain itself ('com.apple.iTunes') & the 'name' child contains the descriptor ('iTunNORM'). A 'data'\n"
693 " atom follows that actually contains the contents of the tag.\n"
694 "\n"
695 " --contentRating (rating) Set the US TV/motion picture media content rating\n"
696 " for available ratings use \"AtomicParsley --ratings-list\n"
697
698 " --rDNSatom (str) name=(name_str) domain=(reverse_domain) Manually set a reverseDNS atom.\n"
699 "\n"
700 " To set the form manually, 3 things are required: a domain, a name, and the desired text.\n"
701 " Note: multiple 'data' atoms are supported, but not in the com.apple.iTunes domain\n"
702 " Examples:\n"
703 " --contentRating \"NC-17\" --contentRating \"TV-Y7\"\n"
704 " --rDNSatom \"mpaa|PG-13|300|\" name=iTunEXTC domain=com.apple.iTunes\n"
705 " --contentRating \"\"\n"
706 " --rDNSatom \"\" name=iTunEXTC domain=com.apple.iTunes\n"
707 " --rDNSatom \"try1\" name=EVAL domain=org.fsf --rDNSatom \"try 2\" name=EVAL domain=org.fsf\n"
708 " --rDNSatom \"\" name=EVAL domain=org.fsf\n"
709 "----------------------------------------------------------------------------------------------------\n"
710 ;
711
712 static const char* ID3Help_text =
713 "AtomicParsley help page for ID32 atoms with ID3 tags.\n"
714 "----------------------------------------------------------------------------------------------------\n"
715 " ** Please note: ID3 tag support is not feature complete & is in an alpha state. **\n"
716 "----------------------------------------------------------------------------------------------------\n"
717 " ID3 tags are the tagging scheme used by mp3 files (where they are found typically at the start of the\n"
718 " file). In mpeg-4 files, ID3 version 2 tags are located in specific hierarchies at certain levels, at\n"
719 " file/movie/track level. The way that ID3 tags are carried on mpeg-4 files (carried by 'ID32' atoms)\n"
720 " was added in early 2006, but the ID3 tagging 'informal standard' was last updated (to v2.4) in 2000.\n"
721 " With few exceptions, ID3 tags in mpeg-4 files exist identically to their mp3 counterparts.\n"
722 "\n"
723 " The ID3 parlance, a frame contains an piece of metadata. A frame (like COMM for comment, or TIT1 for\n"
724 " title) contains the information, while the tag contains all the frames collectively. The 'informal\n"
725 " standard' for ID3 allows multiple langauges for frames like COMM (comment) & USLT (lyrics). In mpeg-4\n"
726 " this language setting is removed from the ID3 domain and exists in the mpeg-4 domain. That means that\n"
727 " when an english and a spanish comment are set, 2 separate ID32 atoms are created, each with a tag & 1\n"
728 " frame as in this example:\n"
729 " --ID3Tag COMM \"Primary\" --desc=AAA --ID3Tag COMM \"El Segundo\" UTF16LE lang=spa --desc=AAA\n"
730 " See available frames with \"AtomicParsley --ID3frames-list\"\n"
731 " See avilable imagetypes with \"AtomicParsley --imagetype-list\"\n"
732 "\n"
733 " AtomicParsley writes ID3 version 2.4.0 tags *only*. There is no up-converting from older versions.\n"
734 " Defaults are:\n"
735 " default to movie level (moov.meta.ID32); other options are [ \"root\", \"track=(num)\" ] (see WARNING)\n"
736 " UTF-8 text encoding when optional; other options are [ \"LATIN1\", \"UTF16BE\", \"UTF16LE\" ]\n"
737 " frames that require descriptions have a default of \"\"\n"
738 " for frames requiring a language setting, the ID32 language is used (currently defaulting to 'eng')\n"
739 " frames that require descriptions have a default of \"\"\n"
740 " image type defaults to 0x00 or Other; for image type 0x01, 32x32 png is enforced (switching to 0x02)\n"
741 " setting the image mimetype is generally not required as the file is tested, but can be overridden\n"
742 " zlib compression off\n"
743 "\n"
744 " WARNING:\n"
745 " Quicktime Player (up to v7.1.3 at least) will freeze opeing a file with ID32 tags at movie level.\n"
746 " Specifically, the parent atom, 'meta' is the source of the issue. You can set the tags at file or\n"
747 " track level which avoids the problem, but the default is movie level. iTunes is unaffected.\n"
748 "----------------------------------------------------------------------------------------------------\n"
749 " Current limitations:\n"
750 " - syncsafe integers are used as indicated by the id3 \"informal standard\". usage/reading of\n"
751 " nonstandard ordinary unsigned integers (uint32_t) is not/will not be implemented.\n"
752 " - externally referenced images (using mimetype '-->') are prohibited by the ID32 specification.\n"
753 " - the ID32 atom is only supported in a non-referenced context\n"
754 " - probably a raft of other limitations that my brain lost along the way...\n"
755 "----------------------------------------------------------------------------------------------------\n"
756 " Usage:\n"
757 " --ID3Tag (frameID or alias) (str) [desc=(str)] [mimetype=(str)] [imagetype=(str or hex)] [...]\n"
758 "\n"
759 " ... represents other arguments:\n"
760 " [compressed] zlib compress the frame\n"
761 " [UTF16BE, UTF16LE, LATIN1] alternative text encodings for frames that support different encodings\n"
762 "\n"
763 "Note: (foo) denotes required arguments; [foo] denotes optional parameters\n"
764 "\n"
765 " Examples:\n"
766 " --ID3Tag APIC /path/to/img.ext\n"
767 " --ID3Tag APIC /path/to/img.ext desc=\"something to say\" imagetype=0x08 UTF16LE compressed\n"
768 " --ID3Tag composer \"I, Claudius\" --ID3Tag TPUB \"Seneca the Roman\" --ID3Tag TMOO Imperial\n"
769 " --ID3Tag UFID look@me.org uniqueID=randomUUIDstamp\n"
770 "\n"
771 " Extracting embedded images in APIC frames:\n"
772 " --ID3Tag APIC extract\n"
773 " images are extracted into the same directory as the source mpeg-4 file\n"
774 "\n"
775 #if defined (DARWIN_PLATFORM)
776 " Setting MCDI (Music CD Identifier):\n"
777 " --ID3Tag MCDI disk4\n"
778 " Information to create this frame is taken directly off an Audio CD's TOC. If the target\n"
779 " disk is not found or is not an audio CD, a scan of all devices will occur. If an Audio CD\n"
780 " is present, the scan should yield what should be entered after 'MCDI':\n"
781 " % AP file --ID3Tag MCDI disk3\n"
782 " % No cd present in drive at disk3\n"
783 " % Device 'disk4' contains cd media\n"
784 " % Good news, device 'disk4' is an Audio CD and can be used for 'MCDI' setting\n"
785 #elif defined (HAVE_LINUX_CDROM_H)
786 " Setting MCDI (Music CD Identifier):\n"
787 " --ID3Tag MCDI /dev/hdc\n"
788 " Information to create this frame is taken directly off an Audio CD's TOC. An Audio CD\n"
789 " must be mounted & present.\n"
790 #elif defined (_WIN32)
791 " Setting MCDI (Music CD Identifier):\n"
792 " --ID3Tag MCDI D\n"
793 " Information to create this frame is taken directly off an Audio CD's TOC. The letter after\n"
794 " \"MCDI\" is the letter of the drive where the CD is present.\n"
795 #endif
796 ;
797
798 void ExtractPaddingPrefs(char* env_padding_prefs) {
799 pad_prefs.default_padding_size = DEFAULT_PADDING_LENGTH;
800 pad_prefs.minimum_required_padding_size = MINIMUM_REQUIRED_PADDING_LENGTH;
801 pad_prefs.maximum_present_padding_size = MAXIMUM_REQUIRED_PADDING_LENGTH;
802
803 if (env_padding_prefs != NULL) {
804 if (env_padding_prefs[0] == 0x22 || env_padding_prefs[0] == 0x27) env_padding_prefs++;
805 }
806 char* env_pad_prefs_ptr = env_padding_prefs;
807
808 while (env_pad_prefs_ptr != NULL) {
809 env_pad_prefs_ptr = strsep(&env_padding_prefs,":");
810
811 if (env_pad_prefs_ptr == NULL) break;
812
813 if (strncmp(env_pad_prefs_ptr, "DEFAULT_PAD=", 12) == 0) {
814 strsep(&env_pad_prefs_ptr,"=");
815 sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.default_padding_size);
1566 {"ISO-copyright", required_argument, NULL, ISO_Copyright},
1567
1568 {"3gp-title", required_argument, NULL, _3GP_Title},
1569 {"3gp-author", required_argument, NULL, _3GP_Author},
1570 {"3gp-performer", required_argument, NULL, _3GP_Performer},
1571 {"3gp-genre", required_argument, NULL, _3GP_Genre},
1572 {"3gp-description", required_argument, NULL, _3GP_Description},
1573 {"3gp-copyright", required_argument, NULL, _3GP_Copyright},
1574 {"3gp-album", required_argument, NULL, _3GP_Album},
1575 {"3gp-year", required_argument, NULL, _3GP_Year},
1576
1577 {"3gp-rating", required_argument, NULL, _3GP_Rating},
1578 {"3gp-classification", required_argument, NULL, _3GP_Classification},
1579 {"3gp-keyword", required_argument, NULL, _3GP_Keyword},
1580 {"3gp-location", required_argument, NULL, _3GP_Location},
1581
1582 {"ID3Tag", required_argument, NULL, Meta_ID3v2Tag},
1583
1584 {"DeepScan", 0, &extr, 1},
1585 {"movementCount", required_argument, NULL, Meta_movementCount},
1586 {"movementName", required_argument, NULL, Meta_movementName},
1587 {"movementNumber", required_argument, NULL, Meta_movementNumber},
1588 {"showWorkMovement", required_argument, NULL, Meta_showWorkMovement},
1589 {"work", required_argument, NULL, Meta_work},
1590
1591 {0, 0, 0, 0}
1592 };
1593
1594 int c = -1;
1595 int option_index = 0;
1596
1597 c = getopt_long(argc,
1598 argv,
1599 "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:"
1600 "I:J:K:L:MN:QR:S:U:WXV:ZP",
1601 long_options,
1602 &option_index);
1603
1604 if (c == -1) {
1605 if (argc < 3 && argc > 2) {
1606 APar_ScanAtoms(ISObasemediafile, true);
1607 APar_PrintAtomicTree();
1608 }
1609 break;
1610 }
1611
1612 signal(SIGTERM, kill_signal);
1613 signal(SIGINT, kill_signal);
1614
1615 switch (c) {
1616 // "optind" represents the count of arguments up to and including its
1617 // optional flag:
1618
1619 case '?':
1620 return 1;
1621
1622 case OPT_HELP: {
1623 fprintf(stdout, "%s", longHelp_text);
1624 return 0;
1625 }
1626
1627 case OPT_TEST: {
1628 deep_atom_scan = true;
1629 APar_ScanAtoms(ISObasemediafile, true);
1630 APar_PrintAtomicTree();
1631 if (argv[optind]) {
1632 if (strcmp(argv[optind], "+dates") == 0) {
1633 APar_ExtractDetails(APar_OpenISOBaseMediaFile(ISObasemediafile, true),
1634 SHOW_TRACK_INFO + SHOW_DATE_INFO);
1635 } else {
1636 APar_ExtractDetails(APar_OpenISOBaseMediaFile(ISObasemediafile, true),
1637 SHOW_TRACK_INFO);
8161638 }
817 if (strncmp(env_pad_prefs_ptr, "MIN_PAD=", 8) == 0) {
818 strsep(&env_pad_prefs_ptr,"=");
819 sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.minimum_required_padding_size);
1639 }
1640 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
1641 break;
1642 }
1643
1644 case OPT_ShowTextData: {
1645 if (argv[optind]) { // for utilities that write iTunes-style metadata into
1646 // 3gp branded files
1647 APar_ExtractBrands(ISObasemediafile);
1648 deep_atom_scan = true;
1649 APar_ScanAtoms(ISObasemediafile);
1650
1651 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
1652
1653 if (strcmp(argv[optind], "+") == 0) {
1654 APar_Print_iTunesData(ISObasemediafile,
1655 NULL,
1656 PRINT_FREE_SPACE + PRINT_PADDING_SPACE +
1657 PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE,
1658 PRINT_DATA);
1659 } else {
1660 fprintf(stdout, "---------------------------\n");
1661 APar_Print_ISO_UserData_per_track();
1662
1663 AtomicInfo *iTuneslistAtom =
1664 APar_FindAtom("moov.udta.meta.ilst", false, SIMPLE_ATOM, 0);
1665 if (iTuneslistAtom != NULL) {
1666 fprintf(
1667 stdout,
1668 "---------------------------\n iTunes-style metadata tags:\n");
1669 APar_Print_iTunesData(ISObasemediafile,
1670 NULL,
1671 PRINT_FREE_SPACE + PRINT_PADDING_SPACE +
1672 PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE,
1673 PRINT_DATA,
1674 iTuneslistAtom);
1675 }
1676 fprintf(stdout, "---------------------------\n");
8201677 }
821 if (strncmp(env_pad_prefs_ptr, "MAX_PAD=", 8) == 0) {
822 strsep(&env_pad_prefs_ptr,"=");
823 sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.maximum_present_padding_size);
1678
1679 } else {
1680 deep_atom_scan = true;
1681 APar_ScanAtoms(ISObasemediafile);
1682 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
1683
1684 if (metadata_style >= THIRD_GEN_PARTNER) {
1685 APar_PrintUserDataAssests();
1686 } else if (metadata_style == ITUNES_STYLE) {
1687 APar_Print_iTunesData(ISObasemediafile,
1688 NULL,
1689 0,
1690 PRINT_DATA); // false, don't try to extractPix
1691 APar_Print_APuuid_atoms(ISObasemediafile, NULL, PRINT_DATA);
8241692 }
825 }
826 //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);
827 return;
828 }
829
830 void GetBasePath(const char *filepath, char* &basepath) {
831 //with a myriad of m4a, m4p, mp4, whatever else comes up... it might just be easiest to strip off the end.
832 int split_here = 0;
833 for (int i=strlen(filepath); i >= 0; i--) {
834 const char* this_char=&filepath[i];
835 if ( *this_char == '.' ) {
836 split_here = i;
1693 }
1694 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
1695 break;
1696 }
1697
1698 case OPT_ExtractPix: {
1699 char *base_path = (char *)malloc(sizeof(char) * MAXPATHLEN + 1);
1700 memset(base_path, 0, MAXPATHLEN + 1);
1701
1702 GetBasePath(ISObasemediafile, base_path);
1703 APar_ScanAtoms(ISObasemediafile);
1704 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
1705 APar_Print_iTunesData(
1706 ISObasemediafile,
1707 base_path,
1708 0,
1709 EXTRACT_ARTWORK); // exportPix to stripped ISObasemediafile path
1710 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
1711
1712 free(base_path);
1713 base_path = NULL;
1714 break;
1715 }
1716
1717 case OPT_ExtractPixToPath: {
1718 APar_ScanAtoms(ISObasemediafile);
1719 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
1720 APar_Print_iTunesData(ISObasemediafile,
1721 optarg,
1722 0,
1723 EXTRACT_ARTWORK); // exportPix to a different path
1724 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
1725 break;
1726 }
1727
1728 case Meta_artist: {
1729 APar_ScanAtoms(ISObasemediafile);
1730 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "artist")) {
1731 char major_brand[4];
1732 UInt32_TO_String4(brand, &*major_brand);
1733 APar_assert(false, 4, &*major_brand);
1734 break;
1735 }
1736 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1737 "moov.udta.meta.ilst.\251ART.data", optarg, AtomFlags_Data_Text);
1738 APar_Unified_atom_Put(
1739 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1740 break;
1741 }
1742
1743 case Meta_artDirector: {
1744 APar_ScanAtoms(ISObasemediafile);
1745 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "artDirector")) {
1746 char major_brand[4];
1747 UInt32_TO_String4(brand, &*major_brand);
1748 APar_assert(false, 4, &*major_brand);
1749 break;
1750 }
1751 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1752 "moov.udta.meta.ilst.\251ard.data", optarg, AtomFlags_Data_Text);
1753 APar_Unified_atom_Put(
1754 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1755 break;
1756 }
1757
1758 case Meta_arranger: {
1759 APar_ScanAtoms(ISObasemediafile);
1760 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "arranger")) {
1761 char major_brand[4];
1762 UInt32_TO_String4(brand, &*major_brand);
1763 APar_assert(false, 4, &*major_brand);
1764 break;
1765 }
1766 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1767 "moov.udta.meta.ilst.\251arg.data", optarg, AtomFlags_Data_Text);
1768 APar_Unified_atom_Put(
1769 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1770 break;
1771 }
1772
1773 case Meta_author: {
1774 APar_ScanAtoms(ISObasemediafile);
1775 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "author")) {
1776 char major_brand[4];
1777 UInt32_TO_String4(brand, &*major_brand);
1778 APar_assert(false, 4, &*major_brand);
1779 break;
1780 }
1781 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1782 "moov.udta.meta.ilst.\251aut.data", optarg, AtomFlags_Data_Text);
1783 APar_Unified_atom_Put(
1784 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1785 break;
1786 }
1787
1788 case Meta_conductor: {
1789 APar_ScanAtoms(ISObasemediafile);
1790 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "conductor")) {
1791 char major_brand[4];
1792 UInt32_TO_String4(brand, &*major_brand);
1793 APar_assert(false, 4, &*major_brand);
1794 break;
1795 }
1796 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1797 "moov.udta.meta.ilst.\251con.data", optarg, AtomFlags_Data_Text);
1798 APar_Unified_atom_Put(
1799 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1800 break;
1801 }
1802
1803 case Meta_director: {
1804 APar_ScanAtoms(ISObasemediafile);
1805 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "director")) {
1806 char major_brand[4];
1807 UInt32_TO_String4(brand, &*major_brand);
1808 APar_assert(false, 4, &*major_brand);
1809 break;
1810 }
1811 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1812 "moov.udta.meta.ilst.\251dir.data", optarg, AtomFlags_Data_Text);
1813 APar_Unified_atom_Put(
1814 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1815 break;
1816 }
1817
1818 case Meta_originalArtist: {
1819 APar_ScanAtoms(ISObasemediafile);
1820 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "originalArtist")) {
1821 char major_brand[4];
1822 UInt32_TO_String4(brand, &*major_brand);
1823 APar_assert(false, 4, &*major_brand);
1824 break;
1825 }
1826 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1827 "moov.udta.meta.ilst.\251ope.data", optarg, AtomFlags_Data_Text);
1828 APar_Unified_atom_Put(
1829 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1830 break;
1831 }
1832
1833 case Meta_producer: {
1834 APar_ScanAtoms(ISObasemediafile);
1835 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "producer")) {
1836 char major_brand[4];
1837 UInt32_TO_String4(brand, &*major_brand);
1838 APar_assert(false, 4, &*major_brand);
1839 break;
1840 }
1841 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1842 "moov.udta.meta.ilst.\251prd.data", optarg, AtomFlags_Data_Text);
1843 APar_Unified_atom_Put(
1844 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1845 break;
1846 }
1847
1848 /*
1849 case Meta_performer : {
1850 APar_ScanAtoms(ISObasemediafile);
1851 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1,
1852 "performer") ) { char major_brand[4]; UInt32_TO_String4(brand,
1853 &*major_brand); APar_assert(false, 4, &*major_brand); break;
1854 }
1855 AtomicInfo* artistData_atom =
1856 APar_MetaData_atom_Init("moov.udta.meta.ilst.\251prf.data", optarg,
1857 AtomFlags_Data_Text); APar_Unified_atom_Put(artistData_atom, optarg,
1858 UTF8_iTunesStyle_256glyphLimited, 0, 0); break;
1859 }
1860 */
1861
1862 case Meta_soundEngineer: {
1863 APar_ScanAtoms(ISObasemediafile);
1864 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "soundEngineer")) {
1865 char major_brand[4];
1866 UInt32_TO_String4(brand, &*major_brand);
1867 APar_assert(false, 4, &*major_brand);
1868 break;
1869 }
1870 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1871 "moov.udta.meta.ilst.\251sne.data", optarg, AtomFlags_Data_Text);
1872 APar_Unified_atom_Put(
1873 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1874 break;
1875 }
1876
1877 case Meta_soloist: {
1878 APar_ScanAtoms(ISObasemediafile);
1879 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "soloist")) {
1880 char major_brand[4];
1881 UInt32_TO_String4(brand, &*major_brand);
1882 APar_assert(false, 4, &*major_brand);
1883 break;
1884 }
1885 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1886 "moov.udta.meta.ilst.\251sol.data", optarg, AtomFlags_Data_Text);
1887 APar_Unified_atom_Put(
1888 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1889 break;
1890 }
1891
1892 case Meta_executiveProducer: {
1893 APar_ScanAtoms(ISObasemediafile);
1894 if (!APar_assert(
1895 metadata_style == ITUNES_STYLE, 1, "executiveProducer")) {
1896 char major_brand[4];
1897 UInt32_TO_String4(brand, &*major_brand);
1898 APar_assert(false, 4, &*major_brand);
1899 break;
1900 }
1901 AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
1902 "moov.udta.meta.ilst.\251xpd.data", optarg, AtomFlags_Data_Text);
1903 APar_Unified_atom_Put(
1904 artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1905 break;
1906 }
1907
1908 case Meta_songtitle: {
1909 APar_ScanAtoms(ISObasemediafile);
1910 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "title")) {
1911 break;
1912 }
1913
1914 AtomicInfo *titleData_atom = APar_MetaData_atom_Init(
1915 "moov.udta.meta.ilst.\251nam.data", optarg, AtomFlags_Data_Text);
1916 APar_Unified_atom_Put(
1917 titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1918 break;
1919 }
1920
1921 case Meta_subtitle: {
1922 APar_ScanAtoms(ISObasemediafile);
1923 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "subtitle")) {
1924 break;
1925 }
1926
1927 AtomicInfo *titleData_atom = APar_MetaData_atom_Init(
1928 "moov.udta.meta.ilst.\251st3.data", optarg, AtomFlags_Data_Text);
1929 APar_Unified_atom_Put(
1930 titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1931 break;
1932 }
1933
1934 case Meta_album: {
1935 APar_ScanAtoms(ISObasemediafile);
1936 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "album")) {
1937 break;
1938 }
1939
1940 AtomicInfo *albumData_atom = APar_MetaData_atom_Init(
1941 "moov.udta.meta.ilst.\251alb.data", optarg, AtomFlags_Data_Text);
1942 APar_Unified_atom_Put(
1943 albumData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1944 break;
1945 }
1946
1947 case Meta_genre: {
1948 APar_ScanAtoms(ISObasemediafile);
1949 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "genre")) {
1950 break;
1951 }
1952
1953 APar_MetaData_atomGenre_Set(optarg);
1954 break;
1955 }
1956
1957 case Meta_tracknum: {
1958 APar_ScanAtoms(ISObasemediafile);
1959 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "track number")) {
1960 break;
1961 }
1962
1963 uint16_t pos_in_total = 0;
1964 uint16_t the_total = 0;
1965 if (strrchr(optarg, '/') != NULL) {
1966
1967 char *duplicate_info = optarg;
1968 char *item_stat = strsep(&duplicate_info, "/");
1969 sscanf(item_stat, "%" SCNu16, &pos_in_total);
1970 item_stat = strsep(&duplicate_info, "/");
1971 sscanf(item_stat, "%" SCNu16, &the_total);
1972 } else {
1973 sscanf(optarg, "%" SCNu16, &pos_in_total);
1974 }
1975
1976 AtomicInfo *tracknumData_atom = APar_MetaData_atom_Init(
1977 "moov.udta.meta.ilst.trkn.data", optarg, AtomFlags_Data_Binary);
1978 // tracknum: [0, 0, 0, 0, 0, 0, 0, pos_in_total, 0, the_total, 0, 0];
1979 // BUT that first uint32_t is already accounted for in
1980 // APar_MetaData_atom_Init
1981 APar_Unified_atom_Put(
1982 tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
1983 APar_Unified_atom_Put(tracknumData_atom,
1984 NULL,
1985 UTF8_iTunesStyle_256glyphLimited,
1986 pos_in_total,
1987 16);
1988 APar_Unified_atom_Put(tracknumData_atom,
1989 NULL,
1990 UTF8_iTunesStyle_256glyphLimited,
1991 the_total,
1992 16);
1993 APar_Unified_atom_Put(
1994 tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
1995 break;
1996 }
1997
1998 case Meta_disknum: {
1999 APar_ScanAtoms(ISObasemediafile);
2000 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "disc number")) {
2001 break;
2002 }
2003
2004 uint16_t pos_in_total = 0;
2005 uint16_t the_total = 0;
2006 if (strrchr(optarg, '/') != NULL) {
2007
2008 char *duplicate_info = optarg;
2009 char *item_stat = strsep(&duplicate_info, "/");
2010 sscanf(item_stat, "%" SCNu16, &pos_in_total);
2011 item_stat = strsep(&duplicate_info, "/");
2012 sscanf(item_stat, "%" SCNu16, &the_total);
2013
2014 } else {
2015 sscanf(optarg, "%" SCNu16, &pos_in_total);
2016 }
2017
2018 AtomicInfo *disknumData_atom = APar_MetaData_atom_Init(
2019 "moov.udta.meta.ilst.disk.data", optarg, AtomFlags_Data_Binary);
2020 // disknum: [0, 0, 0, 0, 0, 0, 0, pos_in_total, 0, the_total]; BUT that
2021 // first uint32_t is already accounted for in APar_MetaData_atom_Init
2022 APar_Unified_atom_Put(
2023 disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
2024 APar_Unified_atom_Put(disknumData_atom,
2025 NULL,
2026 UTF8_iTunesStyle_256glyphLimited,
2027 pos_in_total,
2028 16);
2029 APar_Unified_atom_Put(disknumData_atom,
2030 NULL,
2031 UTF8_iTunesStyle_256glyphLimited,
2032 the_total,
2033 16);
2034 break;
2035 }
2036
2037 case Meta_comment: {
2038 APar_ScanAtoms(ISObasemediafile);
2039 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "comment")) {
2040 break;
2041 }
2042
2043 AtomicInfo *commentData_atom = APar_MetaData_atom_Init(
2044 "moov.udta.meta.ilst.\251cmt.data", optarg, AtomFlags_Data_Text);
2045 APar_Unified_atom_Put(
2046 commentData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2047 break;
2048 }
2049
2050 case Meta_year: {
2051 APar_ScanAtoms(ISObasemediafile);
2052 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "year")) {
2053 break;
2054 }
2055
2056 AtomicInfo *yearData_atom = APar_MetaData_atom_Init(
2057 "moov.udta.meta.ilst.\251day.data", optarg, AtomFlags_Data_Text);
2058 APar_Unified_atom_Put(
2059 yearData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2060 break;
2061 }
2062
2063 case Meta_lyrics: {
2064 APar_ScanAtoms(ISObasemediafile);
2065 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics")) {
2066 break;
2067 }
2068
2069 AtomicInfo *lyricsData_atom = APar_MetaData_atom_Init(
2070 "moov.udta.meta.ilst.\251lyr.data", optarg, AtomFlags_Data_Text);
2071 APar_Unified_atom_Put(
2072 lyricsData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
2073 break;
2074 }
2075
2076 case Meta_lyrics_file: {
2077 APar_ScanAtoms(ISObasemediafile);
2078 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics")) {
2079 break;
2080 }
2081
2082 APar_MetaData_atomLyrics_Set(optarg);
2083 break;
2084 }
2085
2086 case Meta_composer: {
2087 APar_ScanAtoms(ISObasemediafile);
2088 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "composer")) {
2089 break;
2090 }
2091
2092 AtomicInfo *composerData_atom = APar_MetaData_atom_Init(
2093 "moov.udta.meta.ilst.\251wrt.data", optarg, AtomFlags_Data_Text);
2094 APar_Unified_atom_Put(
2095 composerData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2096 break;
2097 }
2098
2099 case Meta_copyright: {
2100 APar_ScanAtoms(ISObasemediafile);
2101 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "copyright")) {
2102 break;
2103 }
2104
2105 AtomicInfo *copyrightData_atom = APar_MetaData_atom_Init(
2106 "moov.udta.meta.ilst.cprt.data", optarg, AtomFlags_Data_Text);
2107 APar_Unified_atom_Put(
2108 copyrightData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2109 break;
2110 }
2111
2112 case Meta_grouping: {
2113 APar_ScanAtoms(ISObasemediafile);
2114 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "grouping")) {
2115 break;
2116 }
2117
2118 AtomicInfo *groupingData_atom = APar_MetaData_atom_Init(
2119 "moov.udta.meta.ilst.\251grp.data", optarg, AtomFlags_Data_Text);
2120 APar_Unified_atom_Put(
2121 groupingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2122 break;
2123 }
2124
2125 case Meta_compilation: {
2126 APar_ScanAtoms(ISObasemediafile);
2127 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "compilation")) {
2128 break;
2129 }
2130
2131 if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) {
2132 APar_RemoveAtom("moov.udta.meta.ilst.cpil.data", VERSIONED_ATOM, 0);
2133 } else {
2134 // compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t
2135 // is already accounted for in APar_MetaData_atom_Init
2136 AtomicInfo *compilationData_atom = APar_MetaData_atom_Init(
2137 "moov.udta.meta.ilst.cpil.data", optarg, AtomFlags_Data_UInt);
2138 APar_Unified_atom_Put(compilationData_atom,
2139 NULL,
2140 UTF8_iTunesStyle_256glyphLimited,
2141 1,
2142 8); // a hard coded uint8_t of: 1 is compilation
2143 }
2144 break;
2145 }
2146
2147 case Meta_hdvideo: {
2148 APar_ScanAtoms(ISObasemediafile);
2149 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "hdvideo")) {
2150 break;
2151 }
2152
2153 if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0 ||
2154 strcmp(optarg, "0") == 0) {
2155 APar_RemoveAtom("moov.udta.meta.ilst.hdvd.data", VERSIONED_ATOM, 0);
2156 } else {
2157 // compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t
2158 // is already accounted for in APar_MetaData_atom_Init
2159 AtomicInfo *hdvideoData_atom = APar_MetaData_atom_Init(
2160 "moov.udta.meta.ilst.hdvd.data", optarg, AtomFlags_Data_UInt);
2161
2162 uint8_t hdvideo_value = 0;
2163 if (strcmp(optarg, "true") == 0) {
2164 hdvideo_value = 1;
2165 } else {
2166 sscanf(optarg, "%" SCNu8, &hdvideo_value);
2167 }
2168
2169 APar_Unified_atom_Put(hdvideoData_atom,
2170 NULL,
2171 UTF8_iTunesStyle_256glyphLimited,
2172 hdvideo_value,
2173 8);
2174 }
2175 break;
2176 }
2177
2178 case Meta_BPM: {
2179 APar_ScanAtoms(ISObasemediafile);
2180 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "BPM")) {
2181 break;
2182 }
2183
2184 if (strcmp(optarg, "0") == 0 || strlen(optarg) == 0) {
2185 APar_RemoveAtom("moov.udta.meta.ilst.tmpo.data", VERSIONED_ATOM, 0);
2186 } else {
2187 uint16_t bpm_value = 0;
2188 sscanf(optarg, "%" SCNu16, &bpm_value);
2189 // bpm is [0, 0, 0, 0, 0, bpm_value]; BUT that first uint32_t is
2190 // already accounted for in APar_MetaData_atom_Init
2191 AtomicInfo *bpmData_atom = APar_MetaData_atom_Init(
2192 "moov.udta.meta.ilst.tmpo.data", optarg, AtomFlags_Data_UInt);
2193 APar_Unified_atom_Put(bpmData_atom,
2194 NULL,
2195 UTF8_iTunesStyle_256glyphLimited,
2196 bpm_value,
2197 16);
2198 }
2199 break;
2200 }
2201
2202 case Meta_advisory: {
2203 APar_ScanAtoms(ISObasemediafile);
2204 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "content advisory")) {
2205 break;
2206 }
2207
2208 if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) {
2209 APar_RemoveAtom("moov.udta.meta.ilst.rtng.data", VERSIONED_ATOM, 0);
2210 } else {
2211 uint8_t rating_value = 0;
2212 if (strcmp(optarg, "clean") == 0) {
2213 rating_value = 2; // only \02 is clean
2214 } else if (strcmp(optarg, "explicit") == 0) {
2215 rating_value = 4; // most non \00, \02 numbers are allowed
2216 }
2217 // rating is [0, 0, 0, 0, rating_value]; BUT that first uint32_t is
2218 // already accounted for in APar_MetaData_atom_Init
2219 AtomicInfo *advisoryData_atom = APar_MetaData_atom_Init(
2220 "moov.udta.meta.ilst.rtng.data", optarg, AtomFlags_Data_UInt);
2221 APar_Unified_atom_Put(advisoryData_atom,
2222 NULL,
2223 UTF8_iTunesStyle_256glyphLimited,
2224 rating_value,
2225 8);
2226 }
2227 break;
2228 }
2229
2230 case Meta_artwork: { // handled differently: there can be multiple
2231 // "moov.udta.meta.ilst.covr.data" atoms
2232 char *env_PicOptions = getenv("PIC_OPTIONS");
2233 APar_ScanAtoms(ISObasemediafile);
2234 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "coverart")) {
2235 break;
2236 }
2237
2238 APar_MetaData_atomArtwork_Set(optarg, env_PicOptions);
2239 break;
2240 }
2241
2242 case Meta_stik: {
2243 APar_ScanAtoms(ISObasemediafile);
2244 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "'stik'")) {
2245 break;
2246 }
2247
2248 if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) {
2249 APar_RemoveAtom("moov.udta.meta.ilst.stik.data", VERSIONED_ATOM, 0);
2250 } else {
2251 uint8_t stik_value = 0;
2252
2253 if (strncmp(optarg, "value=", 6) == 0) {
2254 char *stik_val_str_ptr = optarg;
2255 strsep(&stik_val_str_ptr, "=");
2256 stik_value = strtoul(stik_val_str_ptr, NULL, 10);
2257 } else {
2258 stiks *return_stik = MatchStikString(optarg);
2259 if (return_stik != NULL) {
2260 stik_value = return_stik->stik_number;
2261 if (strcmp(optarg, "Audiobook") == 0) {
2262 forced_suffix_type = FORCE_M4B_TYPE;
2263 }
2264 }
2265 }
2266 // stik is [0, 0, 0, 0, stik_value]; BUT that first uint32_t is
2267 // already accounted for in APar_MetaData_atom_Init
2268 AtomicInfo *stikData_atom = APar_MetaData_atom_Init(
2269 "moov.udta.meta.ilst.stik.data", optarg, AtomFlags_Data_UInt);
2270 APar_Unified_atom_Put(stikData_atom,
2271 NULL,
2272 UTF8_iTunesStyle_256glyphLimited,
2273 stik_value,
2274 8);
2275 }
2276 break;
2277 }
2278
2279 case Meta_EncodingTool: {
2280 APar_ScanAtoms(ISObasemediafile);
2281 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "encoding tool")) {
2282 break;
2283 }
2284
2285 AtomicInfo *encodingtoolData_atom = APar_MetaData_atom_Init(
2286 "moov.udta.meta.ilst.\251too.data", optarg, AtomFlags_Data_Text);
2287 APar_Unified_atom_Put(encodingtoolData_atom,
2288 optarg,
2289 UTF8_iTunesStyle_256glyphLimited,
2290 0,
2291 0);
2292 break;
2293 }
2294
2295 case Meta_EncodedBy: {
2296 APar_ScanAtoms(ISObasemediafile);
2297 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "encoded by")) {
2298 break;
2299 }
2300
2301 AtomicInfo *encodedbyData_atom = APar_MetaData_atom_Init(
2302 "moov.udta.meta.ilst.\251enc.data", optarg, AtomFlags_Data_Text);
2303 APar_Unified_atom_Put(
2304 encodedbyData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2305 break;
2306 }
2307
2308 case Meta_apID: {
2309 APar_ScanAtoms(ISObasemediafile);
2310 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "Account Name")) {
2311 break;
2312 }
2313
2314 AtomicInfo *apIDData_atom = APar_MetaData_atom_Init(
2315 "moov.udta.meta.ilst.apID.data", optarg, AtomFlags_Data_Text);
2316 APar_Unified_atom_Put(
2317 apIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2318 break;
2319 }
2320
2321 case Meta_description: {
2322 APar_ScanAtoms(ISObasemediafile);
2323 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "description")) {
2324 break;
2325 }
2326
2327 AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init(
2328 "moov.udta.meta.ilst.desc.data", optarg, AtomFlags_Data_Text);
2329 APar_Unified_atom_Put(
2330 descriptionData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2331 break;
2332 }
2333
2334 case Meta_longdescription: {
2335 APar_ScanAtoms(ISObasemediafile);
2336 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "longdesc")) {
2337 break;
2338 }
2339
2340 AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init(
2341 "moov.udta.meta.ilst.ldes.data", optarg, AtomFlags_Data_Text);
2342 APar_Unified_atom_Put(
2343 descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
2344 break;
2345 }
2346
2347 case Meta_storedescription: {
2348 APar_ScanAtoms(ISObasemediafile);
2349 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "storedesc")) {
2350 break;
2351 }
2352
2353 AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init(
2354 "moov.udta.meta.ilst.sdes.data", optarg, AtomFlags_Data_Text);
2355 APar_Unified_atom_Put(
2356 descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
2357 break;
2358 }
2359
2360 case Meta_Rating: {
2361 APar_ScanAtoms(ISObasemediafile);
2362 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "Rating")) {
2363 break;
2364 }
2365
2366 AtomicInfo *ratingData_atom = APar_MetaData_atom_Init(
2367 "moov.udta.meta.ilst.rate.data", optarg, AtomFlags_Data_Text);
2368 APar_Unified_atom_Put(
2369 ratingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2370 break;
2371 }
2372
2373 case Meta_TV_Network: {
2374 APar_ScanAtoms(ISObasemediafile);
2375 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Network")) {
2376 break;
2377 }
2378
2379 AtomicInfo *tvnetworkData_atom = APar_MetaData_atom_Init(
2380 "moov.udta.meta.ilst.tvnn.data", optarg, AtomFlags_Data_Text);
2381 APar_Unified_atom_Put(
2382 tvnetworkData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2383 break;
2384 }
2385
2386 case Meta_TV_ShowName: {
2387 APar_ScanAtoms(ISObasemediafile);
2388 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Show name")) {
2389 break;
2390 }
2391
2392 AtomicInfo *tvshownameData_atom = APar_MetaData_atom_Init(
2393 "moov.udta.meta.ilst.tvsh.data", optarg, AtomFlags_Data_Text);
2394 APar_Unified_atom_Put(
2395 tvshownameData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2396 break;
2397 }
2398
2399 case Meta_TV_Episode: { // if the show "ABC Lost 209", its "209"
2400 APar_ScanAtoms(ISObasemediafile);
2401 if (!APar_assert(
2402 metadata_style == ITUNES_STYLE, 1, "TV Episode string")) {
2403 break;
2404 }
2405
2406 AtomicInfo *tvepisodeData_atom = APar_MetaData_atom_Init(
2407 "moov.udta.meta.ilst.tven.data", optarg, AtomFlags_Data_Text);
2408 APar_Unified_atom_Put(
2409 tvepisodeData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2410 break;
2411 }
2412
2413 case Meta_TV_SeasonNumber: { // if the show "ABC Lost 209", its 2; integer 2
2414 // not char "2"
2415 APar_ScanAtoms(ISObasemediafile);
2416 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Season")) {
2417 break;
2418 }
2419
2420 uint16_t data_value = 0;
2421 sscanf(optarg, "%" SCNu16, &data_value);
2422
2423 AtomicInfo *tvseasonData_atom = APar_MetaData_atom_Init(
2424 "moov.udta.meta.ilst.tvsn.data", optarg, AtomFlags_Data_UInt);
2425 // season is [0, 0, 0, 0, 0, 0, 0, data_value]; BUT that first uint32_t
2426 // is already accounted for in APar_MetaData_atom_Init
2427 APar_Unified_atom_Put(
2428 tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
2429 APar_Unified_atom_Put(tvseasonData_atom,
2430 NULL,
2431 UTF8_iTunesStyle_256glyphLimited,
2432 data_value,
2433 16);
2434 break;
2435 }
2436
2437 case Meta_TV_EpisodeNumber: { // if the show "ABC Lost 209", its 9; integer
2438 // 9 (0x09) not char "9"(0x39)
2439 APar_ScanAtoms(ISObasemediafile);
2440 if (!APar_assert(
2441 metadata_style == ITUNES_STYLE, 1, "TV Episode number")) {
2442 break;
2443 }
2444
2445 uint16_t data_value = 0;
2446 sscanf(optarg, "%" SCNu16, &data_value);
2447
2448 AtomicInfo *tvepisodenumData_atom = APar_MetaData_atom_Init(
2449 "moov.udta.meta.ilst.tves.data", optarg, AtomFlags_Data_UInt);
2450 // episodenumber is [0, 0, 0, 0, 0, 0, 0, data_value]; BUT that first
2451 // uint32_t is already accounted for in APar_MetaData_atom_Init
2452 APar_Unified_atom_Put(
2453 tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
2454 APar_Unified_atom_Put(tvepisodenumData_atom,
2455 NULL,
2456 UTF8_iTunesStyle_256glyphLimited,
2457 data_value,
2458 16);
2459 break;
2460 }
2461
2462 case Meta_cnID: { // the iTunes Catalog ID
2463 APar_ScanAtoms(ISObasemediafile);
2464 if (!APar_assert(
2465 metadata_style == ITUNES_STYLE, 1, "iTunes Catalog ID")) {
2466 break;
2467 }
2468
2469 uint32_t data_value = 0;
2470 sscanf(optarg, "%" SCNu32, &data_value);
2471
2472 AtomicInfo *cnIDData_atom = APar_MetaData_atom_Init(
2473 "moov.udta.meta.ilst.cnID.data", optarg, AtomFlags_Data_UInt);
2474 APar_Unified_atom_Put(cnIDData_atom,
2475 NULL,
2476 UTF8_iTunesStyle_256glyphLimited,
2477 data_value,
2478 32);
2479 break;
2480 }
2481
2482 case Meta_geID: { // the iTunes Genre ID
2483 APar_ScanAtoms(ISObasemediafile);
2484 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes Genre ID")) {
2485 break;
2486 }
2487
2488 uint32_t data_value = 0;
2489 sscanf(optarg, "%" SCNu32, &data_value);
2490
2491 AtomicInfo *geIDData_atom = APar_MetaData_atom_Init(
2492 "moov.udta.meta.ilst.geID.data", optarg, AtomFlags_Data_UInt);
2493 APar_Unified_atom_Put(geIDData_atom,
2494 NULL,
2495 UTF8_iTunesStyle_256glyphLimited,
2496 data_value,
2497 32);
2498 break;
2499 }
2500
2501 case Meta_xID: { // the vendor-supplied iTunes xID
2502 APar_ScanAtoms(ISObasemediafile);
2503 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes xID")) {
2504 break;
2505 }
2506
2507 AtomicInfo *xIDData_atom = APar_MetaData_atom_Init(
2508 "moov.udta.meta.ilst.xid .data", optarg, AtomFlags_Data_Text);
2509 APar_Unified_atom_Put(
2510 xIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2511 break;
2512 }
2513
2514 case Meta_album_artist: {
2515 APar_ScanAtoms(ISObasemediafile);
2516 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "album artist")) {
2517 break;
2518 }
2519
2520 AtomicInfo *albumartistData_atom = APar_MetaData_atom_Init(
2521 "moov.udta.meta.ilst.aART.data", optarg, AtomFlags_Data_Text);
2522 APar_Unified_atom_Put(
2523 albumartistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2524 break;
2525 }
2526
2527 case Meta_podcastFlag: {
2528 APar_ScanAtoms(ISObasemediafile);
2529 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast flag")) {
2530 break;
2531 }
2532
2533 if (strcmp(optarg, "false") == 0) {
2534 APar_RemoveAtom("moov.udta.meta.ilst.pcst.data", VERSIONED_ATOM, 0);
2535 } else {
2536 // podcastflag: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t
2537 // is already accounted for in APar_MetaData_atom_Init
2538 AtomicInfo *podcastFlagData_atom = APar_MetaData_atom_Init(
2539 "moov.udta.meta.ilst.pcst.data", optarg, AtomFlags_Data_UInt);
2540 APar_Unified_atom_Put(
2541 podcastFlagData_atom,
2542 NULL,
2543 UTF8_iTunesStyle_256glyphLimited,
2544 1,
2545 8); // a hard coded uint8_t of: 1 denotes podcast flag
2546 }
2547
2548 break;
2549 }
2550
2551 case Meta_keyword: { // TODO to the end of iTunes-style metadata & uuid
2552 // atoms
2553 APar_ScanAtoms(ISObasemediafile);
2554 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "keyword")) {
2555 break;
2556 }
2557
2558 AtomicInfo *keywordData_atom = APar_MetaData_atom_Init(
2559 "moov.udta.meta.ilst.keyw.data", optarg, AtomFlags_Data_Text);
2560 APar_Unified_atom_Put(
2561 keywordData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2562 break;
2563 }
2564
2565 case Meta_category: { // see
2566 // http://www.apple.com/itunes/podcasts/techspecs.html
2567 // for available categories
2568 APar_ScanAtoms(ISObasemediafile);
2569 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "category")) {
2570 break;
2571 }
2572
2573 AtomicInfo *categoryData_atom = APar_MetaData_atom_Init(
2574 "moov.udta.meta.ilst.catg.data", optarg, AtomFlags_Data_Text);
2575 APar_Unified_atom_Put(
2576 categoryData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2577 break;
2578 }
2579
2580 case Meta_podcast_URL: { // usually a read-only value, but useful for
2581 // getting videos into the 'podcast' menu
2582 APar_ScanAtoms(ISObasemediafile);
2583 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast URL")) {
2584 break;
2585 }
2586
2587 AtomicInfo *podcasturlData_atom = APar_MetaData_atom_Init(
2588 "moov.udta.meta.ilst.purl.data", optarg, AtomFlags_Data_Binary);
2589 APar_Unified_atom_Put(
2590 podcasturlData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0);
2591 break;
2592 }
2593
2594 case Meta_podcast_GUID: { // Global Unique IDentifier; it is *highly*
2595 // doubtful that this would be useful...
2596 APar_ScanAtoms(ISObasemediafile);
2597 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast GUID")) {
2598 break;
2599 }
2600
2601 AtomicInfo *globalidData_atom = APar_MetaData_atom_Init(
2602 "moov.udta.meta.ilst.egid.data", optarg, AtomFlags_Data_Binary);
2603 APar_Unified_atom_Put(
2604 globalidData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0);
2605 break;
2606 }
2607
2608 case Meta_PurchaseDate: { // might be useful to *remove* this, but adding
2609 // it... although it could function like id3v2
2610 // tdtg...
2611 APar_ScanAtoms(ISObasemediafile);
2612 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "purchase date")) {
2613 break;
2614 }
2615 char *purd_time;
2616 bool free_memory = false;
2617 if (optarg != NULL) {
2618 if (strcmp(optarg, "timestamp") == 0) {
2619 purd_time = (char *)malloc(sizeof(char) * 255);
2620 free_memory = true;
2621 APar_StandardTime(purd_time);
2622 } else {
2623 purd_time = optarg;
2624 }
2625 } else {
2626 purd_time = optarg;
2627 }
2628
2629 AtomicInfo *globalIDData_atom = APar_MetaData_atom_Init(
2630 "moov.udta.meta.ilst.purd.data", optarg, AtomFlags_Data_Text);
2631 APar_Unified_atom_Put(
2632 globalIDData_atom, purd_time, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2633 if (free_memory) {
2634 free(purd_time);
2635 purd_time = NULL;
2636 }
2637 break;
2638 }
2639
2640 case Meta_PlayGapless: {
2641 APar_ScanAtoms(ISObasemediafile);
2642 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "gapless playback")) {
2643 break;
2644 }
2645
2646 if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) {
2647 APar_RemoveAtom("moov.udta.meta.ilst.pgap.data", VERSIONED_ATOM, 0);
2648 } else {
2649 // gapless playback: [0, 0, 0, 0, boolean_value]; BUT that first
2650 // uint32_t is already accounted for in APar_MetaData_atom_Init
2651 AtomicInfo *gaplessData_atom = APar_MetaData_atom_Init(
2652 "moov.udta.meta.ilst.pgap.data", optarg, AtomFlags_Data_UInt);
2653 APar_Unified_atom_Put(
2654 gaplessData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8);
2655 }
2656 break;
2657 }
2658
2659 case Meta_SortOrder: {
2660 AtomicInfo *sortOrder_atom = NULL;
2661 APar_ScanAtoms(ISObasemediafile);
2662 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "sort order tags")) {
2663 break;
2664 }
2665
2666 if (argv[optind] == NULL) {
2667 fprintf(stdout,
2668 "AP warning, skipping setting the sort order %s tag\n",
2669 optarg);
2670 break;
2671 }
2672
2673 if (strcmp(optarg, "name") == 0) {
2674 sortOrder_atom = APar_MetaData_atom_Init(
2675 "moov.udta.meta.ilst.sonm.data", argv[optind], AtomFlags_Data_Text);
2676 } else if (strcmp(optarg, "artist") == 0) {
2677 sortOrder_atom = APar_MetaData_atom_Init(
2678 "moov.udta.meta.ilst.soar.data", argv[optind], AtomFlags_Data_Text);
2679 } else if (strcmp(optarg, "albumartist") == 0) {
2680 sortOrder_atom = APar_MetaData_atom_Init(
2681 "moov.udta.meta.ilst.soaa.data", argv[optind], AtomFlags_Data_Text);
2682 } else if (strcmp(optarg, "album") == 0) {
2683 sortOrder_atom = APar_MetaData_atom_Init(
2684 "moov.udta.meta.ilst.soal.data", argv[optind], AtomFlags_Data_Text);
2685 } else if (strcmp(optarg, "composer") == 0) {
2686 sortOrder_atom = APar_MetaData_atom_Init(
2687 "moov.udta.meta.ilst.soco.data", argv[optind], AtomFlags_Data_Text);
2688 } else if (strcmp(optarg, "show") == 0) {
2689 sortOrder_atom = APar_MetaData_atom_Init(
2690 "moov.udta.meta.ilst.sosn.data", argv[optind], AtomFlags_Data_Text);
2691 }
2692 APar_Unified_atom_Put(
2693 sortOrder_atom, argv[optind], UTF8_iTunesStyle_256glyphLimited, 0, 0);
2694
2695 break;
2696 }
2697
2698 // uuid atoms
2699
2700 case Meta_StandardDate: {
2701 APar_ScanAtoms(ISObasemediafile);
2702 char *formed_time = (char *)malloc(sizeof(char) * 110);
2703 if (argv[optind]) {
2704 if (strlen(argv[optind]) > 0) {
2705 APar_StandardTime(formed_time);
2706 }
2707 }
2708
2709 AtomicInfo *tdtgUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s",
2710 "tdtg",
2711 AtomFlags_Data_Text,
2712 formed_time,
2713 false);
2714 APar_Unified_atom_Put(
2715 tdtgUUID, formed_time, UTF8_iTunesStyle_Unlimited, 0, 0);
2716 free(formed_time);
2717 break;
2718 }
2719
2720 case Meta_URL: {
2721 APar_ScanAtoms(ISObasemediafile);
2722 AtomicInfo *urlUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s",
2723 "\251url",
2724 AtomFlags_Data_Text,
2725 optarg,
2726 false);
2727 APar_Unified_atom_Put(urlUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
2728 break;
2729 }
2730
2731 case Meta_Information: {
2732 APar_ScanAtoms(ISObasemediafile);
2733 AtomicInfo *infoUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s",
2734 "\251inf",
2735 AtomFlags_Data_Text,
2736 optarg,
2737 false);
2738 APar_Unified_atom_Put(infoUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
2739 break;
2740 }
2741
2742 case Meta_uuid: {
2743 APar_ScanAtoms(ISObasemediafile);
2744 uint32_t uuid_dataType = 0;
2745 uint32_t desc_len = 0;
2746 uint8_t mime_len = 0;
2747 char *uuid_file_path = NULL;
2748 char *uuid_file_description = NULL;
2749 char *uuid_file_extn = NULL;
2750 char *uuid_file_mimetype = NULL;
2751 // char* uuid_file_filename = NULL;
2752
2753 // a uuid in AP is a version 5 uuid created by getting a sha1 hash
2754 // of a string (the atom name) in a namespace ('AP.sf.net'). This
2755 // is guaranteed to be reproducible, so later it can be verified
2756 // that this uuid (which could come from anywhere), is in fact made
2757 // by AtomicParsley. This is achieved by storing the atom name
2758 // string right after the uuid, and is read back later and a new
2759 // uuid is created to see if it matches the discovered uuid. If
2760 // they match, it will print out or extract to a file; if not, only
2761 // its name will be displayed in the tree.
2762 //
2763 // --meta-uuid "\251foo" 1 'http://www.url.org' --meta-uuid "pdf1"
2764 // file /some/path/pic.pdf description="My Booty, Your Booty,
2765 // Djbouti"
2766
2767 if (strcmp(argv[optind], "text") == 0 || strcmp(argv[optind], "1") == 0)
2768 uuid_dataType = AtomFlags_Data_Text;
2769 if (strcmp(argv[optind], "file") == 0) {
2770 uuid_dataType = AtomFlags_Data_uuid_binary;
2771 if (optind + 1 < argc) {
2772 if (true) { // TODO: test the file to see if it exists
2773 uuid_file_path = argv[optind + 1];
2774 // get the file extension/suffix of the file to embed
2775 uuid_file_extn =
2776 strrchr(uuid_file_path,
2777 '.'); //'.' inclusive; say goodbye to AP-0.8.8.tar.bz2
2778
2779 //#ifdef _WIN32
2780 //#define path_delim '\\'
2781 //#else
2782 //#define path_delim '/'
2783 //#endif
2784 // uuid_file_filename = strrchr(uuid_file_path,
2785 // path_delim)+1; //includes whatever
2786 // extensions
2787 }
2788 if (uuid_file_extn == NULL) {
2789 fprintf(stdout,
2790 "AP warning: embedding a file onto a uuid atom "
2791 "requires a file extension. Skipping.\n");
2792 continue;
2793 }
2794 // copy a pointer to description
2795 int more_optional_args = 2;
2796 while (optind + more_optional_args < argc) {
2797 if (strncmp(
2798 argv[optind + more_optional_args], "description=", 12) ==
2799 0 &&
2800 argv[optind + more_optional_args][12]) {
2801 uuid_file_description = argv[optind + more_optional_args] + 12;
2802 desc_len = strlen(uuid_file_description) +
2803 1; //+1 for the trailing 1 byte NULL terminator
2804 }
2805 if (strncmp(argv[optind + more_optional_args], "mime-type=", 10) ==
2806 0 &&
2807 argv[optind + more_optional_args][10]) {
2808 uuid_file_mimetype = argv[optind + more_optional_args] + 10;
2809 mime_len = strlen(uuid_file_mimetype) +
2810 1; //+1 for the trailing 1 byte NULL terminator
2811 }
2812 if (strncmp(argv[optind + more_optional_args], "--", 2) == 0) {
2813 break; // we've hit another cli argument
2814 }
2815 more_optional_args++;
2816 }
2817 }
2818 }
2819
2820 AtomicInfo *genericUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s",
2821 optarg,
2822 uuid_dataType,
2823 argv[optind + 1],
2824 true);
2825
2826 if (uuid_dataType == AtomFlags_Data_uuid_binary && genericUUID != NULL) {
2827 TestFileExistence(uuid_file_path, true);
2828
2829 // format for a uuid atom set by AP:
2830 // 4 bytes - atom length as uin32_t
2831 // 4 bytes - atom name as iso 8859-1 atom name as a 4byte string set
2832 // to 'uuid' 16 bytes - the uuid; here a version 5 sha1-based hash
2833 // derived from a name in a namespace of 'AtomicParsley.sf.net' 4 bytes
2834 // - the name of the desired atom to create a uuid for (this "name" of
2835 // the uuid is the only cli accessible means of crafting the uuid) 4
2836 // bytes - atom version & flags (currently 1 for 'text' or '88' for
2837 // binary attachment) 4 bytes - NULL space
2838 /////////////text or 1 for version/flags
2839 // X bytes - utf8 string, no null termination
2840 /////////////binary attachment or 88 for version/flags
2841 // 4 bytes - length of utf8 string describing the attachment
2842 // X bytes - utf8 string describing the attachment, null terminated
2843 // 1 byte - length of the file suffix (including the period) of the
2844 // originating file X bytes - utf8 string of the file suffix, null
2845 // terminated 1 byte - length of the MIME-type X bytes - utf8 string
2846 // holding the MIME-type, null terminated 4 bytes - length of the
2847 // attached binary data/file length X bytes - binary data
2848
2849 uint32_t extn_len = strlen(uuid_file_extn) +
2850 1; //+1 for the trailing 1 byte NULL terminator
2851 uint64_t file_len = findFileSize(uuid_file_path);
2852
2853 APar_MetaData_atom_QuickInit(genericUUID->AtomicNumber,
2854 uuid_dataType,
2855 20,
2856 extn_len + desc_len + file_len + 100);
2857 genericUUID->AtomicClassification =
2858 EXTENDED_ATOM; // it gets reset in QuickInit Above; force its proper
2859 // setting
2860
2861 if (uuid_file_description == NULL || desc_len == 0) {
2862 APar_Unified_atom_Put(
2863 genericUUID,
2864 "[none]",
2865 UTF8_3GP_Style,
2866 7,
2867 32); // sets 4bytes desc_len, then 7bytes description (forced to
2868 // "[none]"=6 + 1 byte NULL =7)
2869 } else {
2870 APar_Unified_atom_Put(genericUUID,
2871 uuid_file_description,
2872 UTF8_3GP_Style,
2873 desc_len,
2874 32); // sets 4bytes desc_len, then Xbytes
2875 // description (in that order)
2876 }
2877
2878 APar_Unified_atom_Put(genericUUID,
2879 uuid_file_extn,
2880 UTF8_3GP_Style,
2881 extn_len,
2882 8); // sets 1bytes desc_len, then Xbytes file
2883 // extension string (in that order)
2884
2885 if (uuid_file_mimetype == NULL || mime_len == 0) {
2886 APar_Unified_atom_Put(
2887 genericUUID,
2888 "none",
2889 UTF8_3GP_Style,
2890 5,
2891 8); // sets 4bytes desc_len, then 5bytes description (forced to
2892 // "none" + 1byte null)
2893 } else {
2894 APar_Unified_atom_Put(
2895 genericUUID,
2896 uuid_file_mimetype,
2897 UTF8_3GP_Style,
2898 mime_len,
2899 8); // sets 1 byte mime len, then Xbytes mime type
2900 }
2901
2902 FILE *uuid_binfile = APar_OpenFile(uuid_file_path, "rb");
2903 APar_Unified_atom_Put(genericUUID, NULL, UTF8_3GP_Style, file_len, 32);
2904 // store the data directly on the atom in AtomicData
2905 uint32_t bin_bytes_read = APar_ReadFile(
2906 genericUUID->AtomicData + (genericUUID->AtomicLength - 32),
2907 uuid_binfile,
2908 file_len);
2909 genericUUID->AtomicLength += bin_bytes_read;
2910 fclose(uuid_binfile);
2911
2912 } else { // text type
2913 APar_Unified_atom_Put(
2914 genericUUID, argv[optind + 1], UTF8_iTunesStyle_Unlimited, 0, 0);
2915 }
2916
2917 break;
2918 }
2919
2920 case Opt_Extract_all_uuids: {
2921 APar_ScanAtoms(ISObasemediafile);
2922 char *output_path = NULL;
2923 if (optind + 1 == argc) {
2924 output_path = argv[optind];
2925 }
2926
2927 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
2928 APar_Print_APuuid_atoms(
2929 ISObasemediafile, output_path, EXTRACT_ALL_UUID_BINARYS);
2930 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
2931
2932 exit(0); // never gets here
2933 break;
2934 }
2935
2936 case Opt_Extract_a_uuid: {
2937 APar_ScanAtoms(ISObasemediafile);
2938
2939 char *uuid_path = (char *)calloc(1, sizeof(char) * 256 + 1);
2940 char *uuid_binary_str = (char *)calloc(1, sizeof(char) * 20 + 1);
2941 char uuid_4char_name[16];
2942 memset(uuid_4char_name, 0, 16);
2943 AtomicInfo *extractionAtom = NULL;
2944
2945 UTF8Toisolat1((unsigned char *)&uuid_4char_name,
2946 4,
2947 (unsigned char *)optarg,
2948 strlen(optarg));
2949 APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str);
2950
2951 // this will only append (and knock off) %s (anything) at the end of a
2952 // string
2953 uint16_t path_len = strlen("moov.udta.meta.uuid=%s");
2954 memcpy(uuid_path, "moov.udta.meta.uuid=%s", path_len - 2);
2955 memcpy(uuid_path + (path_len - 2), uuid_binary_str, 16);
2956
2957 extractionAtom = APar_FindAtom(uuid_path, false, EXTENDED_ATOM, 0, true);
2958 if (extractionAtom != NULL) {
2959 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
2960 APar_Extract_uuid_binary_file(extractionAtom, ISObasemediafile, NULL);
2961 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
2962 }
2963
2964 free(uuid_path);
2965 uuid_path = NULL;
2966 free(uuid_binary_str);
2967 uuid_binary_str = NULL;
2968 exit(0);
2969 break; // never gets here
2970 }
2971
2972 case Opt_Ipod_AVC_uuid: {
2973 if (deep_atom_scan == true) {
2974 if (strcmp(optarg, "1200") != 0) {
2975 fprintf(stdout,
2976 "the ipod-uuid has a single preset of '1200' which "
2977 "is required\n"); // 1200 might not be the only max
2978 // macroblock setting down the pike
2979 break;
2980 }
2981 uint8_t total_tracks = 0;
2982 uint8_t a_track = 0; // unused
2983 char atom_path[100];
2984 AtomicInfo *video_desc_atom = NULL;
2985
2986 memset(atom_path, 0, 100);
2987
2988 APar_FindAtomInTrack(
2989 total_tracks,
2990 a_track,
2991 NULL); // With track_num set to 0, it will return the total trak
2992 // atom into total_tracks here.
2993
2994 while (a_track < total_tracks) {
2995 a_track++;
2996 sprintf(atom_path, "moov.trak[%u].mdia.minf.stbl.stsd.avc1", a_track);
2997 video_desc_atom =
2998 APar_FindAtom(atom_path, false, VERSIONED_ATOM, 0, false);
2999
3000 if (video_desc_atom != NULL) {
3001 uint16_t mb_t = APar_TestVideoDescription(
3002 video_desc_atom, APar_OpenFile(ISObasemediafile, "rb"));
3003 if (mb_t > 0 && mb_t <= 1200) {
3004 sprintf(atom_path,
3005 "moov.trak[%u].mdia.minf.stbl.stsd.avc1.uuid=",
3006 a_track);
3007 uint8_t uuid_baselen = (uint8_t)strlen(atom_path);
3008 APar_uuid_scanf(atom_path + uuid_baselen,
3009 "6b6840f2-5f24-4fc5-ba39-a51bcf0323f3");
3010 APar_endian_uuid_bin_str_conversion(atom_path + uuid_baselen);
3011 APar_Generate_iPod_uuid(atom_path);
3012 }
3013 }
3014 }
3015
3016 } else {
3017 fprintf(
3018 stdout,
3019 "the --DeepScan option is required for this operation. Skipping\n");
3020 }
3021 break;
3022 }
3023
3024 case Manual_atom_removal: {
3025 APar_ScanAtoms(ISObasemediafile);
3026
3027 char *compliant_name = (char *)malloc(sizeof(char) * strlen(optarg) + 1);
3028 memset(compliant_name, 0, strlen(optarg) + 1);
3029 UTF8Toisolat1((unsigned char *)compliant_name,
3030 strlen(optarg),
3031 (unsigned char *)optarg,
3032 strlen(optarg));
3033
3034 if (strstr(optarg, "uuid=") != NULL) {
3035 uint16_t uuid_path_pos = 0;
3036 uint16_t uuid_path_len = strlen(optarg);
3037 while (compliant_name + uuid_path_pos <
3038 compliant_name + uuid_path_len) {
3039 if (strncmp(compliant_name + uuid_path_pos, "uuid=", 5) == 0) {
3040 uuid_path_pos += 5;
3041 break;
3042 }
3043 uuid_path_pos++;
3044 }
3045 if (strlen(compliant_name + uuid_path_pos) >
3046 4) { // if =4 then it would be the deprecated form (or it should be,
3047 // if not it just won't find anything; no harm done)
3048 uint8_t uuid_len = APar_uuid_scanf(compliant_name + uuid_path_pos,
3049 optarg + uuid_path_pos);
3050 compliant_name[uuid_path_pos + uuid_len] = 0;
3051 }
3052 APar_RemoveAtom(compliant_name, EXTENDED_ATOM, 0);
3053
3054 } else if (strcmp(compliant_name + (strlen(compliant_name) - 4),
3055 "data") == 0) {
3056 APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0);
3057
3058 } else {
3059 size_t string_len = strlen(compliant_name);
3060 // reverseDNS atom path
3061 if (strstr(optarg, ":[") != NULL &&
3062 compliant_name[string_len - 1] == ']') {
3063 APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0);
3064
3065 // packed language asset
3066 } else if (strncmp(compliant_name + string_len - 9, ":lang=", 6) == 0) {
3067 uint16_t packed_lang =
3068 PackLanguage(compliant_name + string_len - 3, 0);
3069 memset(compliant_name + string_len - 9, 0, 1);
3070 APar_RemoveAtom(compliant_name, PACKED_LANG_ATOM, packed_lang);
3071
3072 } else {
3073 APar_RemoveAtom(compliant_name, UNKNOWN_ATOM, 0);
3074 }
3075 }
3076 free(compliant_name);
3077 compliant_name = NULL;
3078 break;
3079 }
3080
3081 // 3gp tags
3082
3083 /*
3084 First, scan the file to get at atom tree (only happens once). Then take the
3085 cli args and look for optional arguments. All arguments begin with -- or -;
3086 other args are optional and are determined by directly testing arguments.
3087 Optional arguments common to the 3gp asset group (language, unicode,
3088 track/movie userdata) are extracted in find_optional_args. Setting assets in
3089 all tracks requires getting the number of tracks. Loop through either once
3090 (for movie & single track) or as many tracks there are for all tracks. Each
3091 pass through the loop, set the individual pieces of metadata.
3092 */
3093 case _3GP_Title: {
3094 APar_ScanAtoms(ISObasemediafile);
3095 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3096 metadata_style < MOTIONJPEG2000,
3097 2,
3098 "title")) {
3099 break;
3100 }
3101 bool set_UTF16_text = false;
3102 uint16_t packed_lang = 0;
3103 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3104 uint8_t selected_track = 0;
3105 uint8_t a_track = 0; // unused
3106 uint8_t asset_iterations = 0;
3107
3108 find_optional_args(argv,
3109 optind,
3110 packed_lang,
3111 set_UTF16_text,
3112 userdata_area,
3113 selected_track,
3114 3);
3115
3116 if (userdata_area == MOVIE_LEVEL_ATOM ||
3117 userdata_area == SINGLE_TRACK_ATOM) {
3118 asset_iterations = 1;
3119 } else if (userdata_area == ALL_TRACKS_ATOM) {
3120 APar_FindAtomInTrack(
3121 asset_iterations,
3122 a_track,
3123 NULL); // With asset_iterations set to 0, it will return the total
3124 // trak atom into total_tracks here.
3125 if (asset_iterations == 1)
3126 selected_track = 1; // otherwise, APar_UserData_atom_Init will shift
3127 // to non-existing track 0
3128 }
3129
3130 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3131 AtomicInfo *title_asset = APar_UserData_atom_Init(
3132 "titl",
3133 optarg,
3134 userdata_area,
3135 asset_iterations == 1 ? selected_track : i_asset,
3136 packed_lang);
3137 APar_Unified_atom_Put(
3138 title_asset,
3139 optarg,
3140 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3141 packed_lang,
3142 16);
3143 }
3144 break;
3145 }
3146
3147 case _3GP_Author: {
3148 APar_ScanAtoms(ISObasemediafile);
3149 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3150 metadata_style < MOTIONJPEG2000,
3151 2,
3152 "author")) {
3153 break;
3154 }
3155 bool set_UTF16_text = false;
3156 uint16_t packed_lang = 0;
3157 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3158 uint8_t selected_track = 0;
3159 uint8_t a_track = 0; // unused
3160 uint8_t asset_iterations = 0;
3161
3162 find_optional_args(argv,
3163 optind,
3164 packed_lang,
3165 set_UTF16_text,
3166 userdata_area,
3167 selected_track,
3168 3);
3169
3170 if (userdata_area == MOVIE_LEVEL_ATOM ||
3171 userdata_area == SINGLE_TRACK_ATOM) {
3172 asset_iterations = 1;
3173 } else if (userdata_area == ALL_TRACKS_ATOM) {
3174 APar_FindAtomInTrack(
3175 asset_iterations,
3176 a_track,
3177 NULL); // With asset_iterations set to 0, it will return the total
3178 // trak atom into total_tracks here.
3179 if (asset_iterations == 1)
3180 selected_track = 1;
3181 }
3182
3183 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3184 AtomicInfo *author_asset = APar_UserData_atom_Init(
3185 "auth",
3186 optarg,
3187 userdata_area,
3188 asset_iterations == 1 ? selected_track : i_asset,
3189 packed_lang);
3190 APar_Unified_atom_Put(
3191 author_asset,
3192 optarg,
3193 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3194 packed_lang,
3195 16);
3196 }
3197 break;
3198 }
3199
3200 case _3GP_Performer: {
3201 APar_ScanAtoms(ISObasemediafile);
3202 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3203 metadata_style < MOTIONJPEG2000,
3204 2,
3205 "performer")) {
3206 break;
3207 }
3208 bool set_UTF16_text = false;
3209 uint16_t packed_lang = 0;
3210 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3211 uint8_t selected_track = 0;
3212 uint8_t a_track = 0; // unused
3213 uint8_t asset_iterations = 0;
3214
3215 find_optional_args(argv,
3216 optind,
3217 packed_lang,
3218 set_UTF16_text,
3219 userdata_area,
3220 selected_track,
3221 3);
3222
3223 if (userdata_area == MOVIE_LEVEL_ATOM ||
3224 userdata_area == SINGLE_TRACK_ATOM) {
3225 asset_iterations = 1;
3226 } else if (userdata_area == ALL_TRACKS_ATOM) {
3227 APar_FindAtomInTrack(
3228 asset_iterations,
3229 a_track,
3230 NULL); // With asset_iterations set to 0, it will return the total
3231 // trak atom into total_tracks here.
3232 if (asset_iterations == 1)
3233 selected_track = 1;
3234 }
3235
3236 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3237 AtomicInfo *performer_asset = APar_UserData_atom_Init(
3238 "perf",
3239 optarg,
3240 userdata_area,
3241 asset_iterations == 1 ? selected_track : i_asset,
3242 packed_lang);
3243 APar_Unified_atom_Put(
3244 performer_asset,
3245 optarg,
3246 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3247 packed_lang,
3248 16);
3249 }
3250 break;
3251 }
3252
3253 case _3GP_Genre: {
3254 APar_ScanAtoms(ISObasemediafile);
3255 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3256 metadata_style < MOTIONJPEG2000,
3257 2,
3258 "genre")) {
3259 break;
3260 }
3261 bool set_UTF16_text = false;
3262 uint16_t packed_lang = 0;
3263 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3264 uint8_t selected_track = 0;
3265 uint8_t a_track = 0; // unused
3266 uint8_t asset_iterations = 0;
3267
3268 find_optional_args(argv,
3269 optind,
3270 packed_lang,
3271 set_UTF16_text,
3272 userdata_area,
3273 selected_track,
3274 3);
3275
3276 if (userdata_area == MOVIE_LEVEL_ATOM ||
3277 userdata_area == SINGLE_TRACK_ATOM) {
3278 asset_iterations = 1;
3279 } else if (userdata_area == ALL_TRACKS_ATOM) {
3280 APar_FindAtomInTrack(
3281 asset_iterations,
3282 a_track,
3283 NULL); // With asset_iterations set to 0, it will return the total
3284 // trak atom into total_tracks here.
3285 if (asset_iterations == 1)
3286 selected_track = 1;
3287 }
3288
3289 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3290 AtomicInfo *genre_asset = APar_UserData_atom_Init(
3291 "gnre",
3292 optarg,
3293 userdata_area,
3294 asset_iterations == 1 ? selected_track : i_asset,
3295 packed_lang);
3296 APar_Unified_atom_Put(
3297 genre_asset,
3298 optarg,
3299 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3300 packed_lang,
3301 16);
3302 }
3303 break;
3304 }
3305
3306 case _3GP_Description: {
3307 APar_ScanAtoms(ISObasemediafile);
3308 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3309 metadata_style < MOTIONJPEG2000,
3310 2,
3311 "description")) {
3312 break;
3313 }
3314 bool set_UTF16_text = false;
3315 uint16_t packed_lang = 0;
3316 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3317 uint8_t selected_track = 0;
3318 uint8_t a_track = 0; // unused
3319 uint8_t asset_iterations = 0;
3320
3321 find_optional_args(argv,
3322 optind,
3323 packed_lang,
3324 set_UTF16_text,
3325 userdata_area,
3326 selected_track,
3327 3);
3328
3329 if (userdata_area == MOVIE_LEVEL_ATOM ||
3330 userdata_area == SINGLE_TRACK_ATOM) {
3331 asset_iterations = 1;
3332 } else if (userdata_area == ALL_TRACKS_ATOM) {
3333 APar_FindAtomInTrack(
3334 asset_iterations,
3335 a_track,
3336 NULL); // With asset_iterations set to 0, it will return the total
3337 // trak atom into total_tracks here.
3338 if (asset_iterations == 1)
3339 selected_track = 1;
3340 }
3341
3342 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3343 AtomicInfo *description_asset = APar_UserData_atom_Init(
3344 "dscp",
3345 optarg,
3346 userdata_area,
3347 asset_iterations == 1 ? selected_track : i_asset,
3348 packed_lang);
3349 APar_Unified_atom_Put(
3350 description_asset,
3351 optarg,
3352 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3353 packed_lang,
3354 16);
3355 }
3356 break;
3357 }
3358
3359 case ISO_Copyright: // ISO copyright atom common to all files that are
3360 // derivatives of the base media file format, identical
3361 // to....
3362 case _3GP_Copyright: { // the 3gp copyright asset; this gets a test for
3363 // major branding (but only with the cli arg
3364 // --3gp-copyright).
3365 APar_ScanAtoms(ISObasemediafile);
3366
3367 if (c == _3GP_Copyright) {
3368 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3369 metadata_style < MOTIONJPEG2000,
3370 2,
3371 "copyright")) {
3372 break;
3373 }
3374 }
3375
3376 bool set_UTF16_text = false;
3377 uint16_t packed_lang = 0;
3378 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3379 uint8_t selected_track = 0;
3380 uint8_t a_track = 0; // unused
3381 uint8_t asset_iterations = 0;
3382
3383 find_optional_args(argv,
3384 optind,
3385 packed_lang,
3386 set_UTF16_text,
3387 userdata_area,
3388 selected_track,
3389 3);
3390
3391 if (userdata_area == MOVIE_LEVEL_ATOM ||
3392 userdata_area == SINGLE_TRACK_ATOM) {
3393 asset_iterations = 1;
3394 } else if (userdata_area == ALL_TRACKS_ATOM) {
3395 APar_FindAtomInTrack(
3396 asset_iterations,
3397 a_track,
3398 NULL); // With asset_iterations set to 0, it will return the total
3399 // trak atom into total_tracks here.
3400 if (asset_iterations == 1)
3401 selected_track = 1;
3402 }
3403
3404 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3405 AtomicInfo *copyright_notice = APar_UserData_atom_Init(
3406 "cprt",
3407 optarg,
3408 userdata_area,
3409 asset_iterations == 1 ? selected_track : i_asset,
3410 packed_lang);
3411 APar_Unified_atom_Put(
3412 copyright_notice,
3413 optarg,
3414 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3415 packed_lang,
3416 16);
3417 }
3418 break;
3419 }
3420
3421 case _3GP_Album: {
3422 APar_ScanAtoms(ISObasemediafile);
3423 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3424 metadata_style < MOTIONJPEG2000,
3425 2,
3426 "album")) {
3427 break;
3428 }
3429 bool set_UTF16_text = false;
3430 uint16_t packed_lang = 0;
3431 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3432 uint8_t selected_track = 0;
3433 uint8_t a_track = 0; // unused
3434 uint8_t asset_iterations = 0;
3435 uint8_t tracknum = 0;
3436
3437 find_optional_args(argv,
3438 optind,
3439 packed_lang,
3440 set_UTF16_text,
3441 userdata_area,
3442 selected_track,
3443 4);
3444
3445 // cygle through the remaining independant arguments (before the next
3446 // --cli_flag) and figure out if any are useful to us; already have lang &
3447 // utf16
3448 for (int i = 0; i <= 4;
3449 i++) { // 3 possible arguments for this tag (the first - which
3450 // doesn't count - is the data for the tag itself)
3451 if (argv[optind + i] && optind + i <= total_args) {
3452 if (strncmp(argv[optind + i], "trknum=", 7) == 0) {
3453 char *track_num = argv[optind + i];
3454 strsep(&track_num, "=");
3455 tracknum = strtoul(track_num, NULL, 10);
3456 }
3457 if (*argv[optind + i] == '-')
8373458 break;
8383459 }
839 }
840 memcpy(basepath, filepath, (size_t)split_here);
841
842 return;
3460 }
3461
3462 if (userdata_area == MOVIE_LEVEL_ATOM ||
3463 userdata_area == SINGLE_TRACK_ATOM) {
3464 asset_iterations = 1;
3465 } else if (userdata_area == ALL_TRACKS_ATOM) {
3466 APar_FindAtomInTrack(
3467 asset_iterations,
3468 a_track,
3469 NULL); // With asset_iterations set to 0, it will return the total
3470 // trak atom into total_tracks here.
3471 if (asset_iterations == 1)
3472 selected_track = 1;
3473 }
3474
3475 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3476 AtomicInfo *album_asset = APar_UserData_atom_Init(
3477 "albm",
3478 optarg,
3479 userdata_area,
3480 asset_iterations == 1 ? selected_track : i_asset,
3481 packed_lang);
3482 APar_Unified_atom_Put(
3483 album_asset,
3484 optarg,
3485 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3486 packed_lang,
3487 16);
3488 if (tracknum != 0) {
3489 APar_Unified_atom_Put(album_asset, NULL, UTF8_3GP_Style, tracknum, 8);
3490 }
3491 }
3492 break;
3493 }
3494
3495 case _3GP_Year: {
3496 APar_ScanAtoms(ISObasemediafile);
3497 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3498 metadata_style < MOTIONJPEG2000,
3499 2,
3500 "year")) {
3501 break;
3502 }
3503 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3504 uint8_t selected_track = 0;
3505 uint8_t a_track = 0; // unused
3506 uint8_t asset_iterations = 0;
3507 uint16_t year_tag = 0;
3508
3509 if (argv[optind] && optind <= total_args) {
3510 if (strcmp(argv[optind], "movie") == 0) {
3511 userdata_area = MOVIE_LEVEL_ATOM;
3512 }
3513 if (strncmp(argv[optind], "track=", 6) == 0) {
3514 char *trak_idx = argv[optind];
3515 strsep(&trak_idx, "=");
3516 selected_track = strtoul(trak_idx, NULL, 10);
3517 userdata_area = SINGLE_TRACK_ATOM;
3518 } else if (strcmp(argv[optind], "track") == 0) {
3519 userdata_area = ALL_TRACKS_ATOM;
3520 }
3521 }
3522
3523 sscanf(optarg, "%" SCNu16, &year_tag);
3524
3525 if (userdata_area == MOVIE_LEVEL_ATOM ||
3526 userdata_area == SINGLE_TRACK_ATOM) {
3527 asset_iterations = 1;
3528 } else if (userdata_area == ALL_TRACKS_ATOM) {
3529 APar_FindAtomInTrack(
3530 asset_iterations,
3531 a_track,
3532 NULL); // With asset_iterations set to 0, it will return the total
3533 // trak atom into total_tracks here.
3534 if (asset_iterations == 1)
3535 selected_track = 1;
3536 }
3537
3538 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3539 AtomicInfo *recordingyear_asset = APar_UserData_atom_Init(
3540 "yrrc",
3541 optarg,
3542 userdata_area,
3543 asset_iterations == 1 ? selected_track : i_asset,
3544 0);
3545 APar_Unified_atom_Put(
3546 recordingyear_asset, NULL, UTF8_3GP_Style, year_tag, 16);
3547 }
3548 break;
3549 }
3550
3551 case _3GP_Rating: {
3552 APar_ScanAtoms(ISObasemediafile);
3553 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3554 metadata_style < MOTIONJPEG2000,
3555 2,
3556 "rating")) {
3557 break;
3558 }
3559 char rating_entity[5] = {
3560 'N',
3561 'O',
3562 'N',
3563 'E',
3564 0}; //'NONE' - thats what it will be if not provided
3565 char rating_criteria[5] = {'N', 'O', 'N', 'E', 0};
3566 bool set_UTF16_text = false;
3567 uint16_t packed_lang = 0;
3568 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3569 uint8_t selected_track = 0;
3570 uint8_t a_track = 0; // unused
3571 uint8_t asset_iterations = 0;
3572
3573 find_optional_args(argv,
3574 optind,
3575 packed_lang,
3576 set_UTF16_text,
3577 userdata_area,
3578 selected_track,
3579 5);
3580
3581 for (int i = 0; i < 5;
3582 i++) { // 3 possible arguments for this tag (the first - which
3583 // doesn't count - is the data for the tag itself)
3584 if (argv[optind + i] && optind + i <= total_args) {
3585 if (strncmp(argv[optind + i], "entity=", 7) == 0) {
3586 char *entity = argv[optind + i];
3587 strsep(&entity, "=");
3588 memcpy(&rating_entity, entity, 4);
3589 }
3590 if (strncmp(argv[optind + i], "criteria=", 9) == 0) {
3591 char *criteria = argv[optind + i];
3592 strsep(&criteria, "=");
3593 memcpy(&rating_criteria, criteria, 4);
3594 }
3595 if (*argv[optind + i] == '-')
3596 break; // we've hit another cli argument
3597 }
3598 }
3599
3600 if (userdata_area == MOVIE_LEVEL_ATOM ||
3601 userdata_area == SINGLE_TRACK_ATOM) {
3602 asset_iterations = 1;
3603 } else if (userdata_area == ALL_TRACKS_ATOM) {
3604 APar_FindAtomInTrack(
3605 asset_iterations,
3606 a_track,
3607 NULL); // With asset_iterations set to 0, it will return the total
3608 // trak atom into total_tracks here.
3609 if (asset_iterations == 1)
3610 selected_track = 1;
3611 }
3612
3613 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3614 AtomicInfo *rating_asset = APar_UserData_atom_Init(
3615 "rtng",
3616 optarg,
3617 userdata_area,
3618 asset_iterations == 1 ? selected_track : i_asset,
3619 packed_lang);
3620
3621 APar_Unified_atom_Put(rating_asset,
3622 NULL,
3623 UTF8_3GP_Style,
3624 UInt32FromBigEndian(rating_entity),
3625 32);
3626 APar_Unified_atom_Put(rating_asset,
3627 NULL,
3628 UTF8_3GP_Style,
3629 UInt32FromBigEndian(rating_criteria),
3630 32);
3631 APar_Unified_atom_Put(
3632 rating_asset,
3633 optarg,
3634 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3635 packed_lang,
3636 16);
3637 }
3638 break;
3639 }
3640
3641 case _3GP_Classification: {
3642 APar_ScanAtoms(ISObasemediafile);
3643 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3644 metadata_style < MOTIONJPEG2000,
3645 2,
3646 "classification")) {
3647 break;
3648 }
3649 char classification_entity[5] = {
3650 'N',
3651 'O',
3652 'N',
3653 'E',
3654 0}; //'NONE' - thats what it will be if not provided
3655 uint16_t classification_index = 0;
3656 bool set_UTF16_text = false;
3657 uint16_t packed_lang = 0;
3658 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3659 uint8_t selected_track = 0;
3660 uint8_t a_track = 0; // unused
3661 uint8_t asset_iterations = 0;
3662
3663 find_optional_args(argv,
3664 optind,
3665 packed_lang,
3666 set_UTF16_text,
3667 userdata_area,
3668 selected_track,
3669 5);
3670
3671 for (int i = 0; i < 4;
3672 i++) { // 3 possible arguments for this tag (the first - which
3673 // doesn't count - is the data for the tag itself)
3674 if (argv[optind + i] && optind + i <= total_args) {
3675 if (strncmp(argv[optind + i], "entity=", 7) == 0) {
3676 char *cls_entity = argv[optind + i];
3677 strsep(&cls_entity, "=");
3678 memcpy(&classification_entity, cls_entity, 4);
3679 }
3680 if (strncmp(argv[optind + i], "index=", 6) == 0) {
3681 char *cls_idx = argv[optind + i];
3682 strsep(&cls_idx, "=");
3683 sscanf(cls_idx, "%" SCNu16, &classification_index);
3684 }
3685 if (*argv[optind + i] == '-')
3686 break; // we've hit another cli argument
3687 }
3688 }
3689
3690 if (userdata_area == MOVIE_LEVEL_ATOM ||
3691 userdata_area == SINGLE_TRACK_ATOM) {
3692 asset_iterations = 1;
3693 } else if (userdata_area == ALL_TRACKS_ATOM) {
3694 APar_FindAtomInTrack(
3695 asset_iterations,
3696 a_track,
3697 NULL); // With asset_iterations set to 0, it will return the total
3698 // trak atom into total_tracks here.
3699 if (asset_iterations == 1)
3700 selected_track = 1;
3701 }
3702
3703 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3704 AtomicInfo *classification_asset = APar_UserData_atom_Init(
3705 "clsf",
3706 optarg,
3707 userdata_area,
3708 asset_iterations == 1 ? selected_track : i_asset,
3709 packed_lang);
3710
3711 APar_Unified_atom_Put(classification_asset,
3712 NULL,
3713 UTF8_3GP_Style,
3714 UInt32FromBigEndian(classification_entity),
3715 32);
3716 APar_Unified_atom_Put(classification_asset,
3717 NULL,
3718 UTF8_3GP_Style,
3719 classification_index,
3720 16);
3721 APar_Unified_atom_Put(
3722 classification_asset,
3723 optarg,
3724 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3725 packed_lang,
3726 16);
3727 }
3728 break;
3729 }
3730
3731 case _3GP_Keyword: {
3732 APar_ScanAtoms(ISObasemediafile);
3733 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3734 metadata_style < MOTIONJPEG2000,
3735 2,
3736 "keyword")) {
3737 break;
3738 }
3739 bool set_UTF16_text = false;
3740 uint16_t packed_lang = 0;
3741 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3742 uint8_t selected_track = 0;
3743 uint8_t a_track = 0; // unused
3744 uint8_t asset_iterations = 0;
3745 char *formed_keyword_struct = NULL;
3746
3747 find_optional_args(argv,
3748 optind,
3749 packed_lang,
3750 set_UTF16_text,
3751 userdata_area,
3752 selected_track,
3753 4);
3754
3755 if (userdata_area == MOVIE_LEVEL_ATOM ||
3756 userdata_area == SINGLE_TRACK_ATOM) {
3757 asset_iterations = 1;
3758 } else if (userdata_area == ALL_TRACKS_ATOM) {
3759 APar_FindAtomInTrack(
3760 asset_iterations,
3761 a_track,
3762 NULL); // With asset_iterations set to 0, it will return the total
3763 // trak atom into total_tracks here.
3764 if (asset_iterations == 1)
3765 selected_track = 1;
3766 }
3767
3768 if (strrchr(optarg, '=') !=
3769 NULL) { // must be in the format of: keywords=foo1,foo2,foo3,foo4
3770 char *arg_keywords = optarg;
3771 char *keywords_globbed =
3772 strsep(&arg_keywords, "="); // separate out 'keyword='
3773 keywords_globbed =
3774 strsep(&arg_keywords,
3775 "="); // this is what we want to work on: just the keywords
3776 char *keyword_ptr = keywords_globbed;
3777 uint32_t keyword_strlen = strlen(keywords_globbed);
3778 uint8_t keyword_count = 0;
3779 uint32_t key_index = 0;
3780
3781 if (keyword_strlen >
3782 0) { // if there is anything past the = then it counts as a keyword
3783 keyword_count++;
3784 }
3785
3786 while (true) { // count occurrences of comma here
3787 if (*keyword_ptr == ',') {
3788 keyword_count++;
3789 }
3790 keyword_ptr++;
3791 key_index++;
3792 if (keyword_strlen == key_index) {
3793 break;
3794 }
3795 }
3796
3797 formed_keyword_struct = (char *)calloc(
3798 1,
3799 sizeof(char) * set_UTF16_text
3800 ? (keyword_strlen * 4)
3801 : (keyword_strlen * 2)); // *4 should carry utf16's BOM & TERM
3802 uint32_t keyword_struct_bytes =
3803 APar_3GP_Keyword_atom_Format(keywords_globbed,
3804 keyword_count,
3805 set_UTF16_text,
3806 formed_keyword_struct);
3807
3808 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3809 AtomicInfo *keyword_asset = APar_UserData_atom_Init(
3810 "kywd",
3811 keyword_strlen ? "temporary" : "",
3812 userdata_area,
3813 asset_iterations == 1 ? selected_track : i_asset,
3814 packed_lang); // just a "temporary" valid string to satisfy a test
3815 // there
3816 if (keyword_strlen > 0) {
3817 APar_Unified_atom_Put(
3818 keyword_asset, NULL, UTF8_3GP_Style, packed_lang, 16);
3819 APar_Unified_atom_Put(
3820 keyword_asset, NULL, UTF8_3GP_Style, keyword_count, 8);
3821 APar_atom_Binary_Put(
3822 keyword_asset, formed_keyword_struct, keyword_struct_bytes, 3);
3823 }
3824 }
3825 if (formed_keyword_struct != NULL) {
3826 free(formed_keyword_struct);
3827 formed_keyword_struct = NULL;
3828 }
3829 } else {
3830 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3831 APar_UserData_atom_Init("kywd",
3832 "",
3833 userdata_area,
3834 asset_iterations == 1 ? selected_track
3835 : i_asset,
3836 packed_lang);
3837 }
3838 }
3839 break;
3840 }
3841
3842 case _3GP_Location: {
3843 APar_ScanAtoms(ISObasemediafile);
3844 if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
3845 metadata_style < MOTIONJPEG2000,
3846 2,
3847 "location")) {
3848 break;
3849 }
3850 bool set_UTF16_text = false;
3851 uint16_t packed_lang = 0;
3852 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
3853 uint8_t selected_track = 0;
3854 uint8_t a_track = 0; // unused
3855 uint8_t asset_iterations = 0;
3856 double longitude =
3857 -73.98; // if you don't provide a place, you WILL be put right into
3858 // Central Park. Welcome to New York City... now go away.
3859 double latitude = 40.77;
3860 double altitude = 4.3;
3861 uint8_t role = 0;
3862 const char *astronomical_body = "Earth";
3863 const char *additional_notes = "no notes";
3864
3865 find_optional_args(argv,
3866 optind,
3867 packed_lang,
3868 set_UTF16_text,
3869 userdata_area,
3870 selected_track,
3871 10);
3872
3873 for (int i = 0; i <= 10;
3874 i++) { // 9 possible arguments for this tag (the first - which
3875 // doesn't count - is the data for the tag itself)
3876 if (argv[optind + i] && optind + i <= total_args) {
3877 if (strncmp(argv[optind + i], "longitude=", 10) == 0) {
3878 char *_long = argv[optind + i];
3879 strsep(&_long, "=");
3880 sscanf(_long, "%lf", &longitude);
3881 if (_long[strlen(_long) - 1] == 'W') {
3882 longitude *= -1;
3883 }
3884 }
3885 if (strncmp(argv[optind + i], "latitude=", 9) == 0) {
3886 char *_latt = argv[optind + i];
3887 strsep(&_latt, "=");
3888 sscanf(_latt, "%lf", &latitude);
3889 if (_latt[strlen(_latt) - 1] == 'S') {
3890 latitude *= -1;
3891 }
3892 }
3893 if (strncmp(argv[optind + i], "altitude=", 9) == 0) {
3894 char *_alti = argv[optind + i];
3895 strsep(&_alti, "=");
3896 sscanf(_alti, "%lf", &altitude);
3897 if (_alti[strlen(_alti) - 1] == 'B') {
3898 altitude *= -1;
3899 }
3900 }
3901 if (strncmp(argv[optind + i], "role=", 5) == 0) {
3902 char *_role = argv[optind + i];
3903 strsep(&_role, "=");
3904 if (strcmp(_role, "shooting location") == 0 ||
3905 strcmp(_role, "shooting") == 0) {
3906 role = 0;
3907 } else if (strcmp(_role, "real location") == 0 ||
3908 strcmp(_role, "real") == 0) {
3909 role = 1;
3910 } else if (strcmp(_role, "fictional location") == 0 ||
3911 strcmp(_role, "fictional") == 0) {
3912 role = 2;
3913 }
3914 }
3915 if (strncmp(argv[optind + i], "body=", 5) == 0) {
3916 char *_astrobody = argv[optind + i];
3917 strsep(&_astrobody, "=");
3918 astronomical_body = _astrobody;
3919 }
3920 if (strncmp(argv[optind + i], "notes=", 6) == 0) {
3921 char *_add_notes = argv[optind + i];
3922 strsep(&_add_notes, "=");
3923 additional_notes = _add_notes;
3924 }
3925 if (*argv[optind + i] == '-')
3926 break; // we've hit another cli argument
3927 }
3928 }
3929
3930 // fprintf(stdout, "long, lat, alt = %lf %lf %lf\n", longitude, latitude,
3931 // altitude);
3932 if (userdata_area == MOVIE_LEVEL_ATOM ||
3933 userdata_area == SINGLE_TRACK_ATOM) {
3934 asset_iterations = 1;
3935 } else if (userdata_area == ALL_TRACKS_ATOM) {
3936 APar_FindAtomInTrack(
3937 asset_iterations,
3938 a_track,
3939 NULL); // With asset_iterations set to 0, it will return the total
3940 // trak atom into total_tracks here.
3941 if (asset_iterations == 1)
3942 selected_track = 1;
3943 }
3944
3945 if (longitude < -180.0 || longitude > 180.0 || latitude < -90.0 ||
3946 latitude > 90.0) {
3947 fprintf(stdout,
3948 "AtomicParsley warning: longitude or latitude was "
3949 "invalid; skipping setting location\n");
3950 } else {
3951 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
3952 // short location_3GP_atom = APar_UserData_atom_Init("moov.udta.loci",
3953 // optarg, packed_lang);
3954 AtomicInfo *location_asset = APar_UserData_atom_Init(
3955 "loci",
3956 optarg,
3957 userdata_area,
3958 asset_iterations == 1 ? selected_track : i_asset,
3959 packed_lang);
3960 APar_Unified_atom_Put(
3961 location_asset,
3962 optarg,
3963 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3964 packed_lang,
3965 16);
3966 APar_Unified_atom_Put(location_asset, NULL, false, role, 8);
3967
3968 APar_Unified_atom_Put(location_asset,
3969 NULL,
3970 false,
3971 float_to_16x16bit_fixed_point(longitude),
3972 32);
3973 APar_Unified_atom_Put(location_asset,
3974 NULL,
3975 false,
3976 float_to_16x16bit_fixed_point(latitude),
3977 32);
3978 APar_Unified_atom_Put(location_asset,
3979 NULL,
3980 false,
3981 float_to_16x16bit_fixed_point(altitude),
3982 32);
3983 APar_Unified_atom_Put(
3984 location_asset,
3985 astronomical_body,
3986 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3987 0,
3988 0);
3989 APar_Unified_atom_Put(
3990 location_asset,
3991 additional_notes,
3992 (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
3993 0,
3994 0);
3995 }
3996 }
3997 break;
3998 }
3999
4000 case Meta_ReverseDNS_Form: { //--rDNSatom "mv-ma" name=iTuneEXTC
4001 // domain=com.apple.iTunes
4002 char *reverseDNS_atomname = NULL;
4003 char *reverseDNS_atomdomain = NULL;
4004 uint32_t rdns_atom_flags = AtomFlags_Data_Text;
4005
4006 APar_ScanAtoms(ISObasemediafile);
4007
4008 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "reverse DNS form")) {
4009 break;
4010 }
4011
4012 for (int i = 0; i <= 5 - 1; i++) {
4013 if (argv[optind + i] && optind + i <= argc) {
4014 if (strncmp(argv[optind + i], "name=", 5) == 0) {
4015 reverseDNS_atomname = argv[optind + i] + 5;
4016 } else if (strncmp(argv[optind + i], "domain=", 7) == 0) {
4017 reverseDNS_atomdomain = argv[optind + i] + 7;
4018 } else if (strncmp(argv[optind + i], "datatype=", 9) == 0) {
4019 sscanf(argv[optind + i] + 9, "%" SCNu32, &rdns_atom_flags);
4020 }
4021 if (*argv[optind + i] == '-') {
4022 break; // we've hit another cli argument
4023 }
4024 }
4025 }
4026
4027 if (reverseDNS_atomname == NULL) {
4028 fprintf(stdout,
4029 "AtomicParsley warning: no name for the reverseDNS "
4030 "form was found. Skipping.\n");
4031
4032 } else if ((int)strlen(reverseDNS_atomname) !=
4033 test_conforming_alpha_string(reverseDNS_atomname)) {
4034 fprintf(stdout,
4035 "AtomicParsley warning: Some part of the reverseDNS "
4036 "atom name was non-conforming. Skipping.\n");
4037
4038 } else if (reverseDNS_atomdomain == NULL) {
4039 fprintf(stdout,
4040 "AtomicParsley warning: no domain for the reverseDNS "
4041 "form was found. Skipping.\n");
4042
4043 } else if (rdns_atom_flags != AtomFlags_Data_Text) {
4044 fprintf(stdout,
4045 "AtomicParsley warning: currently, only the strings "
4046 "are supported in reverseDNS atoms. Skipping.\n");
4047
4048 } else {
4049 AtomicInfo *rDNS_data_atom =
4050 APar_reverseDNS_atom_Init(reverseDNS_atomname,
4051 optarg,
4052 &rdns_atom_flags,
4053 reverseDNS_atomdomain);
4054 APar_Unified_atom_Put(
4055 rDNS_data_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
4056 }
4057
4058 break;
4059 }
4060
4061 case Meta_rDNS_rating: {
4062 const char *media_rating = Expand_cli_mediastring(optarg);
4063 uint32_t rDNS_data_flags = AtomFlags_Data_Text;
4064
4065 APar_ScanAtoms(ISObasemediafile);
4066
4067 if (media_rating != NULL || strlen(optarg) == 0) {
4068 AtomicInfo *rDNS_rating_data_atom = APar_reverseDNS_atom_Init(
4069 "iTunEXTC", media_rating, &rDNS_data_flags, "com.apple.iTunes");
4070 APar_Unified_atom_Put(rDNS_rating_data_atom,
4071 media_rating,
4072 UTF8_iTunesStyle_Unlimited,
4073 0,
4074 0);
4075 }
4076
4077 break;
4078 }
4079
4080 case Meta_ID3v2Tag: {
4081 const char *target_frame_ID = NULL;
4082 uint16_t packed_lang = 0;
4083 uint8_t char_encoding = TE_UTF8; // utf8 is the default encoding
4084 char meta_container = 0 - MOVIE_LEVEL_ATOM;
4085 bool multistring = false;
4086 APar_ScanAtoms(ISObasemediafile);
4087
4088 // limit the files that can be tagged with meta.ID32 atoms. The file has
4089 // to conform to the ISO BMFFv2 in order for a 'meta' atom. This should
4090 // exclude files branded as 3gp5 for example, except it doesn't always.
4091 // The test is for a compatible brand (of a v2 ISO MBFF). Quicktime writes
4092 // some 3GPP files as 3gp5 with a compatible brand of mp42, so tagging
4093 // works on these files. Not when you use timed text though.
4094 if (!APar_assert(parsedAtoms[0].ancillary_data != 0 ||
4095 (metadata_style >= THIRD_GEN_PARTNER_VER1_REL7 &&
4096 metadata_style < MOTIONJPEG2000),
4097 3,
4098 NULL)) {
4099 break;
4100 }
4101 AdjunctArgs *id3args = (AdjunctArgs *)malloc(sizeof(AdjunctArgs));
4102
4103 id3args->targetLang = NULL; // it will default later to "eng"
4104 id3args->descripArg = NULL;
4105 id3args->mimeArg = NULL;
4106 id3args->pictypeArg = NULL;
4107 id3args->ratingArg = NULL;
4108 id3args->dataArg = NULL;
4109 id3args->pictype_uint8 = 0;
4110 id3args->groupSymbol = 0;
4111 id3args->zlibCompressed = false;
4112 id3args->multistringtext = false;
4113
4114 target_frame_ID = ConvertCLIFrameStr_TO_frameID(optarg);
4115 if (target_frame_ID == NULL) {
4116 target_frame_ID = optarg;
4117 }
4118
4119 int frameType = FrameStr_TO_FrameType(target_frame_ID);
4120 if (frameType >= 0) {
4121 if (TestCLI_for_FrameParams(frameType, 0)) {
4122 id3args->descripArg = find_ID3_optarg(argv, optind, "desc=");
4123 }
4124 if (TestCLI_for_FrameParams(frameType, 1)) {
4125 id3args->mimeArg = find_ID3_optarg(argv, optind, "mimetype=");
4126 }
4127 if (TestCLI_for_FrameParams(frameType, 2)) {
4128 id3args->pictypeArg = find_ID3_optarg(argv, optind, "imagetype=");
4129 }
4130 if (TestCLI_for_FrameParams(frameType, 3)) {
4131 id3args->dataArg = find_ID3_optarg(argv, optind, "uniqueID=");
4132 }
4133 if (TestCLI_for_FrameParams(frameType, 4)) {
4134 id3args->filenameArg = find_ID3_optarg(argv, optind, "filename=");
4135 }
4136 if (TestCLI_for_FrameParams(frameType, 5)) {
4137 id3args->ratingArg = find_ID3_optarg(argv, optind, "rating=");
4138 }
4139 if (TestCLI_for_FrameParams(frameType, 6)) {
4140 id3args->dataArg = find_ID3_optarg(argv, optind, "counter=");
4141 }
4142 if (TestCLI_for_FrameParams(frameType, 7)) {
4143 id3args->dataArg = find_ID3_optarg(argv, optind, "data=");
4144 }
4145 if (TestCLI_for_FrameParams(frameType, 8)) {
4146 id3args->dataArg = find_ID3_optarg(argv, optind, "data=");
4147 }
4148 if (*find_ID3_optarg(argv, optind, "compressed") == '1') {
4149 id3args->zlibCompressed = true;
4150 }
4151
4152 const char *groupsymbol = find_ID3_optarg(argv, optind, "groupsymbol=");
4153 if (groupsymbol[0] == '0' && groupsymbol[1] == 'x') {
4154 id3args->groupSymbol = strtoul(groupsymbol, NULL, 16);
4155 if (id3args->groupSymbol < 0x80 || id3args->groupSymbol > 0xF0)
4156 id3args->groupSymbol = 0;
4157 }
4158 }
4159
4160 scan_ID3_optargs(argv,
4161 optind,
4162 id3args->targetLang,
4163 packed_lang,
4164 char_encoding,
4165 &meta_container,
4166 multistring);
4167 if (id3args->targetLang == NULL)
4168 id3args->targetLang = "eng";
4169
4170 APar_OpenISOBaseMediaFile(
4171 ISObasemediafile,
4172 true); // if not already scanned, the whole tag for
4173 // *this* ID32 atom needs to be read from file
4174 AtomicInfo *id32_atom = APar_ID32_atom_Init(
4175 target_frame_ID, meta_container, id3args->targetLang, packed_lang);
4176
4177 if (strcmp(argv[optind + 0], "extract") == 0 &&
4178 (strcmp(target_frame_ID, "APIC") == 0 ||
4179 strcmp(target_frame_ID, "GEOB") == 0)) {
4180 if (id32_atom != NULL) {
4181 APar_Extract_ID3v2_file(
4182 id32_atom, target_frame_ID, ISObasemediafile, NULL, id3args);
4183 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
4184 }
4185 exit(0);
4186 }
4187
4188 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
4189 APar_ID3FrameAmmend(
4190 id32_atom, target_frame_ID, argv[optind + 0], id3args, char_encoding);
4191
4192 free(id3args);
4193 id3args = NULL;
4194
4195 break;
4196 }
4197
4198 // utility functions
4199
4200 case Metadata_Purge: {
4201 APar_ScanAtoms(ISObasemediafile);
4202 APar_RemoveAtom("moov.udta.meta.ilst", SIMPLE_ATOM, 0);
4203
4204 break;
4205 }
4206
4207 case UserData_Purge: {
4208 APar_ScanAtoms(ISObasemediafile);
4209 APar_RemoveAtom("moov.udta", SIMPLE_ATOM, 0);
4210
4211 break;
4212 }
4213
4214 case foobar_purge: {
4215 APar_ScanAtoms(ISObasemediafile);
4216 APar_RemoveAtom("moov.udta.tags", UNKNOWN_ATOM, 0);
4217
4218 break;
4219 }
4220
4221 case Opt_FreeFree: {
4222 APar_ScanAtoms(ISObasemediafile);
4223 int free_level = -1;
4224 if (argv[optind]) {
4225 sscanf(argv[optind], "%i", &free_level);
4226 }
4227 APar_freefree(free_level);
4228
4229 break;
4230 }
4231
4232 case OPT_OverWrite: {
4233 alter_original = true;
4234 break;
4235 }
4236
4237 #if defined(_WIN32)
4238 case OPT_PreserveTimeStamps: {
4239 alter_original = true;
4240 preserve_timestamps = true;
4241 break;
4242 }
4243 #endif
4244
4245 case Meta_dump: {
4246 APar_ScanAtoms(ISObasemediafile);
4247 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
4248 APar_MetadataFileDump(ISObasemediafile);
4249 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
4250
4251 APar_FreeMemory();
4252 exit(0);
4253 }
4254
4255 case OPT_NoOptimize: {
4256 force_existing_hierarchy = true;
4257 break;
4258 }
4259
4260 case OPT_OutputFile: {
4261 output_file = optarg;
4262 break;
4263 }
4264
4265 case Meta_movementCount: {
4266 APar_ScanAtoms(ISObasemediafile);
4267 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementCount")) {
4268 break;
4269 }
4270 uint8_t data_value = 0;
4271 sscanf(optarg, "%" SCNu8, &data_value);
4272 AtomicInfo *atom = APar_MetaData_atom_Init(
4273 "moov.udta.meta.ilst.\251mvc.data", optarg, AtomFlags_Data_UInt);
4274 APar_Unified_atom_Put(
4275 atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 8);
4276 break;
4277 }
4278
4279 case Meta_movementName: {
4280 APar_ScanAtoms(ISObasemediafile);
4281 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementName")) {
4282 break;
4283 }
4284 AtomicInfo *atom = APar_MetaData_atom_Init(
4285 "moov.udta.meta.ilst.\251mvn.data", optarg, AtomFlags_Data_Text);
4286 APar_Unified_atom_Put(
4287 atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
4288 break;
4289 }
4290
4291 case Meta_movementNumber: {
4292 APar_ScanAtoms(ISObasemediafile);
4293 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementNumber")) {
4294 break;
4295 }
4296 uint8_t data_value = 0;
4297 sscanf(optarg, "%" SCNu8, &data_value);
4298 AtomicInfo *atom = APar_MetaData_atom_Init(
4299 "moov.udta.meta.ilst.\251mvi.data", optarg, AtomFlags_Data_UInt);
4300 APar_Unified_atom_Put(
4301 atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 8);
4302 break;
4303 }
4304
4305 case Meta_showWorkMovement: {
4306 APar_ScanAtoms(ISObasemediafile);
4307 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "showWorkMovement")) {
4308 break;
4309 }
4310 AtomicInfo *atom = APar_MetaData_atom_Init(
4311 "moov.udta.meta.ilst.shwm.data", optarg, AtomFlags_Data_Binary);
4312 APar_Unified_atom_Put(
4313 atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
4314 break;
4315 }
4316
4317 case Meta_work: {
4318 APar_ScanAtoms(ISObasemediafile);
4319 if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "work")) {
4320 break;
4321 }
4322 AtomicInfo *atom = APar_MetaData_atom_Init(
4323 "moov.udta.meta.ilst.\251wrk.data", optarg, AtomFlags_Data_Text);
4324 APar_Unified_atom_Put(
4325 atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
4326 break;
4327 }
4328 } /* end switch */
4329 } /* end while */
4330
4331 // after all the modifications are enacted on the tree in memory, THEN
4332 // write out the changes
4333
4334 if (modified_atoms) {
4335
4336 #if defined(_WIN32)
4337 HANDLE hFile, hFileOut;
4338 FILETIME createTime, accessTime, writeTime;
4339 if (preserve_timestamps == true) {
4340 hFile = APar_OpenFileWin32(ISObasemediafile,
4341 GENERIC_WRITE | GENERIC_READ,
4342 FILE_SHARE_WRITE | FILE_SHARE_READ,
4343 NULL,
4344 OPEN_EXISTING,
4345 0,
4346 0);
4347 if (hFile != INVALID_HANDLE_VALUE) {
4348 GetFileTime(hFile, &createTime, &accessTime, &writeTime);
4349 CloseHandle(hFile);
4350 } else {
4351 fprintf(stdout, "\n Invalid HANDLE!");
4352 }
4353 }
4354 #endif
4355
4356 APar_DetermineAtomLengths();
4357 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
4358 APar_WriteFile(ISObasemediafile, output_file, alter_original);
4359
4360 #if defined(_WIN32)
4361 if (preserve_timestamps == true) {
4362
4363 hFileOut = APar_OpenFileWin32(ISObasemediafile,
4364 GENERIC_WRITE | GENERIC_READ,
4365 FILE_SHARE_WRITE | FILE_SHARE_READ,
4366 NULL,
4367 OPEN_EXISTING,
4368 0,
4369 0);
4370
4371 if (hFileOut != INVALID_HANDLE_VALUE) {
4372 SetFileTime(hFileOut, &createTime, &accessTime, &writeTime);
4373 CloseHandle(hFileOut);
4374 }
4375 }
4376 #endif
4377
4378 if (!alter_original) {
4379 // The file was opened orignally as read-only; when it came time to
4380 // writeback into the original file, that FILE was closed, and a
4381 // new one opened with write abilities, so to close a FILE that no
4382 // longer exists would.... be retarded.
4383 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
4384 }
4385 } else {
4386 if (ISObasemediafile != NULL && argc > 3 && !deep_atom_scan) {
4387 fprintf(stdout, "No changes.\n");
4388 }
4389 }
4390 APar_FreeMemory();
4391 return 0;
8434392 }
8444393
845 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) {
846 asUTF16 = false;
847 packed_lang = 5575; //und = 0x55C4 = 21956, but QTPlayer doesn't like und //eng = 0x15C7 = 5575
848
849 for (int i= 0; i <= max_optargs-1; i++) {
850 if ( argv[start_optindargs + i] && start_optindargs + i <= total_args ) {
851 if ( strncmp(argv[start_optindargs + i], "lang=", 5) == 0 ) {
852 if (!MatchLanguageCode(argv[start_optindargs +i]+5) ) {
853 packed_lang = PackLanguage("und", 0);
854 } else {
855 packed_lang = PackLanguage(argv[start_optindargs +i], 5);
856 }
857
858 } else if ( strcmp(argv[start_optindargs + i], "UTF16") == 0 ) {
859 asUTF16 = true;
860 } else if ( strcmp(argv[optind + i], "movie") == 0 ) {
861 udta_container = MOVIE_LEVEL_ATOM;
862 } else if ( strncmp(argv[optind + i], "track=", 6) == 0 ) {
863 char* track_index_str = argv[optind + i];
864 strsep(&track_index_str, "=");
865 trk_idx = strtoul(track_index_str, NULL, 10);
866 udta_container = SINGLE_TRACK_ATOM;
867 } else if ( strcmp(argv[optind + i], "track") == 0 ) {
868 udta_container = ALL_TRACKS_ATOM;
869 }
870 if (*argv[start_optindargs + i] == '-') {
871 break; //we've hit another cli argument
872 }
873 }
874 }
875 return;
4394 #if defined(_WIN32)
4395
4396 #if !defined(__CYGWIN__)
4397
4398 int wmain(int argc, wchar_t *arguments[]) {
4399 int return_val = 0;
4400 uint16_t name_len = wcslen(arguments[0]);
4401 if (name_len >= 9 &&
4402 _wcsicmp(arguments[0] + (name_len - 9), L"-utf8.exe") == 0) {
4403 UnicodeOutputStatus = UNIVERSAL_UTF8;
4404 } else {
4405 UnicodeOutputStatus = WIN32_UTF16;
4406 }
4407
4408 char **argv = (char **)calloc(argc + 1, sizeof(char *));
4409
4410 // for native Win32 & full unicode support (well, cli arguments anyway),
4411 // take whatever 16bit unicode windows gives (utf16le), and convert
4412 // EVERYTHING that is sends to utf8; use those utf8 strings (mercifully
4413 // subject to familiar standby's like strlen) to pass around the program
4414 // like getopt_long to get cli options; convert from utf8 filenames as
4415 // required for unicode filename support on Windows using wide file
4416 // functions.
4417 for (int z = 0; z < argc; z++) {
4418 uint32_t wchar_length = wcslen(arguments[z]) + 1;
4419 argv[z] = (char *)malloc(sizeof(char) * 8 * wchar_length);
4420 memset(argv[z], 0, 8 * wchar_length);
4421 if (UnicodeOutputStatus == WIN32_UTF16) {
4422 UTF16LEToUTF8((unsigned char *)argv[z],
4423 8 * wchar_length,
4424 (unsigned char *)arguments[z],
4425 wchar_length * 2);
4426 } else {
4427 strip_bogusUTF16toRawUTF8((unsigned char *)argv[z],
4428 8 * wchar_length,
4429 arguments[z],
4430 wchar_length);
4431 }
4432 }
4433
4434 argv[argc] = NULL;
4435
4436 return_val = real_main(argc, argv);
4437
4438 for (int free_cnt = 0; free_cnt < argc; free_cnt++) {
4439 free(argv[free_cnt]);
4440 argv[free_cnt] = NULL;
4441 }
4442
4443 free(argv);
4444 argv = NULL;
4445
4446 return return_val;
8764447 }
8774448
878 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) {
879 packed_lang = 5575; //default ID32 lang is 'eng'
880 uint16_t i = 0;
881
882 while (argv[start_optargs + i] != NULL) {
883 if ( argv[start_optargs + i] && start_optargs + i <= total_args ) {
884
885 if ( strncmp(argv[start_optargs + i], "lang=", 5) == 0 ) {
886 if (!MatchLanguageCode(argv[start_optargs +i]+5) ) {
887 packed_lang = PackLanguage("und", 0);
888 target_lang = "und";
889 } else {
890 packed_lang = PackLanguage(argv[start_optargs +i], 5);
891 target_lang = argv[start_optargs + i] + 5;
892 }
893
894 } else if ( strcmp(argv[start_optargs + i], "UTF16LE") == 0 ) {
895 char_encoding = TE_UTF16LE_WITH_BOM;
896 } else if ( strcmp(argv[start_optargs + i], "UTF16BE") == 0 ) {
897 char_encoding = TE_UTF16BE_NO_BOM;
898 } else if ( strcmp(argv[start_optargs + i], "LATIN1") == 0 ) {
899 char_encoding = TE_LATIN1;
900
901 } else if ( strcmp(argv[optind + i], "root") == 0 ) {
902 *meta_container = 0-FILE_LEVEL_ATOM;
903 } else if ( strncmp(argv[optind + i], "track=", 6) == 0 ) {
904 char* track_index_str = argv[optind + i];
905 strsep(&track_index_str, "=");
906 *meta_container = strtoul(track_index_str, NULL, 10);
907 }
908 }
909
910 if (*argv[start_optargs + i] == '-') {
911 break; //we've hit another cli argument or deleting some frame
912 }
913 i++;
914 }
915 return;
4449 #ifdef __MINGW32__
4450
4451 int main() {
4452 int argc;
4453 wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc);
4454 return wmain(argc, argv);
9164455 }
9174456
918 const char* find_ID3_optarg(char *argv[], int start_optargs, const char* arg_string) {
919 const char* ret_val = "";
920 uint16_t i = 0;
921 uint8_t arg_prefix_len = strlen(arg_string);
922
923 while (argv[start_optargs + i] != NULL) {
924 if ( argv[start_optargs + i] && start_optargs + i <= total_args ) {
925 if (strcmp(arg_string, "compressed") == 0 && strcmp(argv[start_optargs + i], "compressed") == 0) {
926 return "1";
927 }
928 //if (strcmp(arg_string, "text++") == 0 && strcmp(argv[start_optargs + i], "text++") == 0) {
929 // return "1";
930 //}
931 if (strncmp(argv[start_optargs + i], arg_string, arg_prefix_len) == 0) {
932 ret_val = argv[start_optargs + i] + arg_prefix_len;
933 break;
934 }
935 }
936 if (*argv[start_optargs + i] == '-') {
937 break; //we've hit another cli argument or deleting some frame
938 }
939 i++;
940 }
941 return ret_val;
4457 #endif
4458
4459 #else // defined __CYGWIN__
4460
4461 int main(int argc, char *argv[]) {
4462 size_t name_len = strlen(argv[0]);
4463 if (name_len >= 5 && (strcmp(argv[0] + (name_len - 5), "-utf8") == 0 ||
4464 strcmp(argv[0] + (name_len - 5), "-UTF8") == 0)) {
4465 UnicodeOutputStatus = UNIVERSAL_UTF8;
4466 } else {
4467 UnicodeOutputStatus = WIN32_UTF16;
4468 }
4469 return real_main(argc, argv);
9424470 }
9434471
944 //***********************************************
945
946 static void show_short_help(void)
947 {
948 printf("%s\n", shortHelp_text);
949 ShowVersionInfo();
950 printf("\nReport issues at %s\n", PACKAGE_BUGREPORT);
951 }
952
953 int real_main(int argc, char *argv[])
954 {
955 if (argc == 1) {
956 show_short_help();
957 exit(0);
958 } else if (argc == 2 && (
959 (strcmp(argv[1],"-v") == 0) ||
960 (strcmp(argv[1],"-version") == 0) ||
961 (strcmp(argv[1],"--version") == 0)) ) {
962 ShowVersionInfo();
963 exit(0);
964 } else if (argc == 2) {
965 if ((strcmp(argv[1],"-help") == 0) ||
966 (strcmp(argv[1],"--help") == 0) ||
967 (strcmp(argv[1],"-h") == 0 ) ) {
968 show_short_help();
969 exit(0);
970 } else if ( (strcmp(argv[1],"--longhelp") == 0) || (strcmp(argv[1],"-longhelp") == 0) || (strcmp(argv[1],"-Lh") == 0) ) {
971 #if defined (_WIN32) && !defined (__CYGWIN__)
972 if (UnicodeOutputStatus == WIN32_UTF16) { //convert the helptext to utf16 to preserve © characters
973 int help_len = strlen(longHelp_text)+1;
974 wchar_t* Lhelp_text = (wchar_t *)malloc(sizeof(wchar_t)*help_len);
975 wmemset(Lhelp_text, 0, help_len);
976 UTF8ToUTF16LE((unsigned char*)Lhelp_text, 2*help_len, (unsigned char*)longHelp_text, help_len);
977 APar_unicode_win32Printout(Lhelp_text, (char *) longHelp_text);
978 free(Lhelp_text);
979 } else
9804472 #endif
981 {
982 fprintf(stdout, "%s", longHelp_text);
983 }
984 exit(0);
985
986 } else if ( (strcmp(argv[1],"--3gp-help") == 0) || (strcmp(argv[1],"-3gp-help") == 0) || (strcmp(argv[1],"--3gp-h") == 0) ) {
987 fprintf(stdout, "%s\n", _3gpHelp_text); exit(0);
988
989 } else if ( (strcmp(argv[1],"--ISO-help") == 0) || (strcmp(argv[1],"--iso-help") == 0) || (strcmp(argv[1],"-Ih") == 0) ) {
990 fprintf(stdout, "%s\n", ISOHelp_text); exit(0);
991
992 } else if ( (strcmp(argv[1],"--file-help") == 0) || (strcmp(argv[1],"-file-help") == 0) || (strcmp(argv[1],"-fh") == 0) ) {
993 fprintf(stdout, "%s\n", fileLevelHelp_text); exit(0);
994
995 } else if ( (strcmp(argv[1],"--uuid-help") == 0) || (strcmp(argv[1],"-uuid-help") == 0) || (strcmp(argv[1],"-uh") == 0) ) {
996 fprintf(stdout, "%s\n", uuidHelp_text); exit(0);
997
998 } else if ( (strcmp(argv[1],"--reverseDNS-help") == 0) || (strcmp(argv[1],"-rDNS-help") == 0) || (strcmp(argv[1],"-rh") == 0) ) {
999 fprintf(stdout, "%s\n", rDNSHelp_text); exit(0);
1000
1001 } else if ( (strcmp(argv[1],"--ID3-help") == 0) || (strcmp(argv[1],"-ID3-help") == 0) || (strcmp(argv[1],"-ID3h") == 0) ) {
1002 fprintf(stdout, "%s\n", ID3Help_text); exit(0);
1003
1004 } else if ( strcmp(argv[1], "--genre-list") == 0 ) {
1005 ListGenresValues(); exit(0);
1006
1007 } else if ( strcmp(argv[1], "--stik-list") == 0 ) {
1008 ListStikValues(); exit(0);
1009
1010 } else if ( strcmp(argv[1], "--language-list") == 0 ||
1011 strcmp(argv[1], "--languages-list") == 0 ||
1012 strcmp(argv[1], "--list-language") == 0 ||
1013 strcmp(argv[1], "--list-languages") == 0 ||
1014 strcmp(argv[1], "-ll") == 0) {
1015 ListLanguageCodes(); exit(0);
1016
1017 } else if (strcmp(argv[1], "--ratings-list") == 0) {
1018 ListMediaRatings(); exit(0);
1019
1020 } else if (strcmp(argv[1], "--genre-movie-id-list") == 0) {
1021 ListMovieGenreIDValues(); exit(0);
1022
1023 } else if (strcmp(argv[1], "--genre-tv-id-list") == 0) {
1024 ListTVGenreIDValues(); exit(0);
1025
1026 } else if (strcmp(argv[1], "--ID3frames-list") == 0) {
1027 ListID3FrameIDstrings(); exit(0);
1028
1029 } else if (strcmp(argv[1], "--imagetype-list") == 0) {
1030 List_imagtype_strings(); exit(0);
1031 }
1032 }
1033
1034 if ( argc == 3 && (strcmp(argv[2], "--brands") == 0 || strcmp(argv[2], "-brands") == 0) ) {
1035 APar_ExtractBrands(argv[1]); exit(0);
1036 }
1037
1038 int extr = 99;
1039 total_args = argc;
1040 char* ISObasemediafile = argv[1];
1041
1042 TestFileExistence(ISObasemediafile, true);
1043
1044 char* padding_options = getenv("AP_PADDING");
1045 ExtractPaddingPrefs(padding_options);
1046
1047 //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.
1048 if (strlen(ISObasemediafile) + 11 > MAXPATHLEN) {
1049 fprintf(stderr, "%c %s", '\a', "AtomicParsley error: filename/filepath was too long.\n");
1050 exit(1);
1051 }
1052
1053 if ( argc > 3 && strcmp(argv[2], "--DeepScan") == 0) {
1054 deep_atom_scan = true;
1055 APar_ScanAtoms(ISObasemediafile, true);
1056 }
1057
1058 while (1) {
1059 static struct option long_options[] = {
1060 { "help", 0, NULL, OPT_HELP },
1061 { "test", optional_argument, NULL, OPT_TEST },
1062 { "textdata", optional_argument, NULL, OPT_ShowTextData },
1063 { "extractPix", 0, NULL, OPT_ExtractPix },
1064 { "extractPixToPath", required_argument, NULL, OPT_ExtractPixToPath },
1065 { "artist", required_argument, NULL, Meta_artist },
1066 { "artDirector", required_argument, NULL, Meta_artDirector },
1067 { "arranger", required_argument, NULL, Meta_arranger },
1068 { "author", required_argument, NULL, Meta_author },
1069 { "conductor", required_argument, NULL, Meta_conductor },
1070 { "director", required_argument, NULL, Meta_director },
1071 { "originalArtist", required_argument, NULL, Meta_originalArtist },
1072 { "producer", required_argument, NULL, Meta_producer },
1073 // { "performer", required_argument, NULL, Meta_performer },
1074 { "soundEngineer", required_argument, NULL, Meta_soundEngineer },
1075 { "soloist", required_argument, NULL, Meta_soloist },
1076 { "executiveProducer",required_argument, NULL, Meta_executiveProducer },
1077 { "title", required_argument, NULL, Meta_songtitle },
1078 { "subtitle", required_argument, NULL, Meta_subtitle },
1079 { "album", required_argument, NULL, Meta_album },
1080 { "genre", required_argument, NULL, Meta_genre },
1081 { "tracknum", required_argument, NULL, Meta_tracknum },
1082 { "disknum", required_argument, NULL, Meta_disknum },
1083 { "comment", required_argument, NULL, Meta_comment },
1084 { "year", required_argument, NULL, Meta_year },
1085 { "lyrics", required_argument, NULL, Meta_lyrics },
1086 { "lyricsFile", required_argument, NULL, Meta_lyrics_file },
1087 { "composer", required_argument, NULL, Meta_composer },
1088 { "copyright", required_argument, NULL, Meta_copyright },
1089 { "grouping", required_argument, NULL, Meta_grouping },
1090 { "albumArtist", required_argument, NULL, Meta_album_artist },
1091 { "compilation", required_argument, NULL, Meta_compilation },
1092 { "hdvideo", required_argument, NULL, Meta_hdvideo },
1093 { "advisory", required_argument, NULL, Meta_advisory },
1094 { "bpm", required_argument, NULL, Meta_BPM },
1095 { "artwork", required_argument, NULL, Meta_artwork },
1096 { "stik", required_argument, NULL, Meta_stik },
1097 { "description", required_argument, NULL, Meta_description },
1098 { "longdesc", required_argument, NULL, Meta_longdescription },
1099 { "storedesc", required_argument, NULL, Meta_storedescription },
1100 { "Rating", required_argument, NULL, Meta_Rating },
1101 { "TVNetwork", required_argument, NULL, Meta_TV_Network },
1102 { "TVShowName", required_argument, NULL, Meta_TV_ShowName },
1103 { "TVEpisode", required_argument, NULL, Meta_TV_Episode },
1104 { "TVEpisodeNum", required_argument, NULL, Meta_TV_EpisodeNumber },
1105 { "TVSeasonNum", required_argument, NULL, Meta_TV_SeasonNumber },
1106 { "podcastFlag", required_argument, NULL, Meta_podcastFlag },
1107 { "keyword", required_argument, NULL, Meta_keyword },
1108 { "category", required_argument, NULL, Meta_category },
1109 { "podcastURL", required_argument, NULL, Meta_podcast_URL },
1110 { "podcastGUID", required_argument, NULL, Meta_podcast_GUID },
1111 { "purchaseDate", required_argument, NULL, Meta_PurchaseDate },
1112 { "encodingTool", required_argument, NULL, Meta_EncodingTool },
1113 { "encodedBy", required_argument, NULL, Meta_EncodedBy },
1114 { "apID", required_argument, NULL, Meta_apID },
1115 { "cnID", required_argument, NULL, Meta_cnID },
1116 { "geID", required_argument, NULL, Meta_geID },
1117 { "xID", required_argument, NULL, Meta_xID },
1118 { "gapless", required_argument, NULL, Meta_PlayGapless },
1119 { "sortOrder", required_argument, NULL, Meta_SortOrder } ,
1120
1121 { "rDNSatom", required_argument, NULL, Meta_ReverseDNS_Form },
1122 { "contentRating", required_argument, NULL, Meta_rDNS_rating },
1123
1124 { "tagtime", optional_argument, NULL, Meta_StandardDate },
1125 { "information", required_argument, NULL, Meta_Information },
1126 { "url", required_argument, NULL, Meta_URL },
1127 { "meta-uuid", required_argument, NULL, Meta_uuid },
1128 { "extract-uuids", optional_argument, NULL, Opt_Extract_all_uuids },
1129 { "extract1uuid", required_argument, NULL, Opt_Extract_a_uuid },
1130 { "iPod-uuid", required_argument, NULL, Opt_Ipod_AVC_uuid },
1131
1132 { "freefree", optional_argument, NULL, Opt_FreeFree },
1133 { "metaEnema", 0, NULL, Metadata_Purge },
1134 { "manualAtomRemove", required_argument, NULL, Manual_atom_removal },
1135 { "udtaEnema", 0, NULL, UserData_Purge },
1136 { "foobar2000Enema", 0, NULL, foobar_purge },
1137 { "metaDump", 0, NULL, Meta_dump },
1138 { "output", required_argument, NULL, OPT_OutputFile },
1139 { "preventOptimizing",0, NULL, OPT_NoOptimize },
1140 { "overWrite", 0, NULL, OPT_OverWrite },
1141 #if defined (_WIN32)
1142 { "preserveTime", 0, NULL, OPT_PreserveTimeStamps },
1143 #endif
1144 { "ISO-copyright", required_argument, NULL, ISO_Copyright },
1145
1146 { "3gp-title", required_argument, NULL, _3GP_Title },
1147 { "3gp-author", required_argument, NULL, _3GP_Author },
1148 { "3gp-performer", required_argument, NULL, _3GP_Performer },
1149 { "3gp-genre", required_argument, NULL, _3GP_Genre },
1150 { "3gp-description", required_argument, NULL, _3GP_Description },
1151 { "3gp-copyright", required_argument, NULL, _3GP_Copyright },
1152 { "3gp-album", required_argument, NULL, _3GP_Album },
1153 { "3gp-year", required_argument, NULL, _3GP_Year },
1154
1155 { "3gp-rating", required_argument, NULL, _3GP_Rating },
1156 { "3gp-classification", required_argument, NULL, _3GP_Classification },
1157 { "3gp-keyword", required_argument, NULL, _3GP_Keyword },
1158 { "3gp-location", required_argument, NULL, _3GP_Location },
1159
1160 { "ID3Tag", required_argument, NULL, Meta_ID3v2Tag },
1161
1162 { "DeepScan", 0, &extr, 1 },
1163
1164 { 0, 0, 0, 0 }
1165 };
1166
1167 int c = -1;
1168 int option_index = 0;
1169
1170 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);
1171
1172 if (c == -1) {
1173 if (argc < 3 && argc > 2) {
1174 APar_ScanAtoms(ISObasemediafile, true);
1175 APar_PrintAtomicTree();
1176 }
1177 break;
1178 }
1179
1180 signal(SIGTERM, kill_signal);
1181 signal(SIGINT, kill_signal);
1182
1183 switch(c) {
1184 // "optind" represents the count of arguments up to and including its optional flag:
1185
1186 case '?': return 1;
1187
1188 case OPT_HELP: {
1189 fprintf (stdout,"%s", longHelp_text); return 0;
1190 }
1191
1192 case OPT_TEST: {
1193 deep_atom_scan = true;
1194 APar_ScanAtoms(ISObasemediafile, true);
1195 APar_PrintAtomicTree();
1196 if (argv[optind]) {
1197 if (strcmp(argv[optind], "+dates") == 0) {
1198 APar_ExtractDetails( APar_OpenISOBaseMediaFile(ISObasemediafile, true), SHOW_TRACK_INFO + SHOW_DATE_INFO );
1199 } else {
1200 APar_ExtractDetails( APar_OpenISOBaseMediaFile(ISObasemediafile, true), SHOW_TRACK_INFO);
1201 }
1202 }
1203 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
1204 break;
1205 }
1206
1207 case OPT_ShowTextData: {
1208 if (argv[optind]) { //for utilities that write iTunes-style metadata into 3gp branded files
1209 APar_ExtractBrands(ISObasemediafile);
1210 deep_atom_scan=true;
1211 APar_ScanAtoms(ISObasemediafile);
1212
1213 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
1214
1215 if (strcmp(argv[optind], "+") == 0) {
1216 APar_Print_iTunesData(ISObasemediafile, NULL, PRINT_FREE_SPACE + PRINT_PADDING_SPACE + PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE, PRINT_DATA );
1217 } else {
1218 fprintf(stdout, "---------------------------\n");
1219 APar_Print_ISO_UserData_per_track();
1220
1221 AtomicInfo* iTuneslistAtom = APar_FindAtom("moov.udta.meta.ilst", false, SIMPLE_ATOM, 0);
1222 if (iTuneslistAtom != NULL) {
1223 fprintf(stdout, "---------------------------\n iTunes-style metadata tags:\n");
1224 APar_Print_iTunesData(ISObasemediafile, NULL, PRINT_FREE_SPACE + PRINT_PADDING_SPACE + PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE, PRINT_DATA, iTuneslistAtom );
1225 }
1226 fprintf(stdout, "---------------------------\n");
1227 }
1228
1229 } else {
1230 deep_atom_scan=true;
1231 APar_ScanAtoms(ISObasemediafile);
1232 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
1233
1234 if (metadata_style >= THIRD_GEN_PARTNER) {
1235 APar_PrintUserDataAssests();
1236 } else if (metadata_style == ITUNES_STYLE) {
1237 APar_Print_iTunesData(ISObasemediafile, NULL, 0, PRINT_DATA); //false, don't try to extractPix
1238 APar_Print_APuuid_atoms(ISObasemediafile, NULL, PRINT_DATA);
1239 }
1240 }
1241 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
1242 break;
1243 }
1244
1245 case OPT_ExtractPix: {
1246 char* base_path=(char*)malloc(sizeof(char)*MAXPATHLEN+1);
1247 memset(base_path, 0, MAXPATHLEN +1);
1248
1249 GetBasePath( ISObasemediafile, base_path );
1250 APar_ScanAtoms(ISObasemediafile);
1251 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
1252 APar_Print_iTunesData(ISObasemediafile, base_path, 0, EXTRACT_ARTWORK); //exportPix to stripped ISObasemediafile path
1253 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
1254
1255 free(base_path);
1256 base_path = NULL;
1257 break;
1258 }
1259
1260 case OPT_ExtractPixToPath: {
1261 APar_ScanAtoms(ISObasemediafile);
1262 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
1263 APar_Print_iTunesData(ISObasemediafile, optarg, 0, EXTRACT_ARTWORK); //exportPix to a different path
1264 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
1265 break;
1266 }
1267
1268 case Meta_artist : {
1269 APar_ScanAtoms(ISObasemediafile);
1270 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "artist") ) {
1271 char major_brand[4];
1272 UInt32_TO_String4(brand, &*major_brand);
1273 APar_assert(false, 4, &*major_brand);
1274 break;
1275 }
1276 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©ART.data", optarg, AtomFlags_Data_Text);
1277 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1278 break;
1279 }
1280
1281 case Meta_artDirector : {
1282 APar_ScanAtoms(ISObasemediafile);
1283 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "artDirector") ) {
1284 char major_brand[4];
1285 UInt32_TO_String4(brand, &*major_brand);
1286 APar_assert(false, 4, &*major_brand);
1287 break;
1288 }
1289 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©ard.data", optarg, AtomFlags_Data_Text);
1290 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1291 break;
1292 }
1293
1294 case Meta_arranger : {
1295 APar_ScanAtoms(ISObasemediafile);
1296 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "arranger") ) {
1297 char major_brand[4];
1298 UInt32_TO_String4(brand, &*major_brand);
1299 APar_assert(false, 4, &*major_brand);
1300 break;
1301 }
1302 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©arg.data", optarg, AtomFlags_Data_Text);
1303 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1304 break;
1305 }
1306
1307 case Meta_author : {
1308 APar_ScanAtoms(ISObasemediafile);
1309 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "author") ) {
1310 char major_brand[4];
1311 UInt32_TO_String4(brand, &*major_brand);
1312 APar_assert(false, 4, &*major_brand);
1313 break;
1314 }
1315 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©aut.data", optarg, AtomFlags_Data_Text);
1316 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1317 break;
1318 }
1319
1320 case Meta_conductor : {
1321 APar_ScanAtoms(ISObasemediafile);
1322 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "conductor") ) {
1323 char major_brand[4];
1324 UInt32_TO_String4(brand, &*major_brand);
1325 APar_assert(false, 4, &*major_brand);
1326 break;
1327 }
1328 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©con.data", optarg, AtomFlags_Data_Text);
1329 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1330 break;
1331 }
1332
1333 case Meta_director : {
1334 APar_ScanAtoms(ISObasemediafile);
1335 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "director") ) {
1336 char major_brand[4];
1337 UInt32_TO_String4(brand, &*major_brand);
1338 APar_assert(false, 4, &*major_brand);
1339 break;
1340 }
1341 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©dir.data", optarg, AtomFlags_Data_Text);
1342 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1343 break;
1344 }
1345
1346 case Meta_originalArtist : {
1347 APar_ScanAtoms(ISObasemediafile);
1348 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "originalArtist") ) {
1349 char major_brand[4];
1350 UInt32_TO_String4(brand, &*major_brand);
1351 APar_assert(false, 4, &*major_brand);
1352 break;
1353 }
1354 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©ope.data", optarg, AtomFlags_Data_Text);
1355 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1356 break;
1357 }
1358
1359 case Meta_producer : {
1360 APar_ScanAtoms(ISObasemediafile);
1361 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "producer") ) {
1362 char major_brand[4];
1363 UInt32_TO_String4(brand, &*major_brand);
1364 APar_assert(false, 4, &*major_brand);
1365 break;
1366 }
1367 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©prd.data", optarg, AtomFlags_Data_Text);
1368 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1369 break;
1370 }
1371
1372 /*
1373 case Meta_performer : {
1374 APar_ScanAtoms(ISObasemediafile);
1375 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "performer") ) {
1376 char major_brand[4];
1377 UInt32_TO_String4(brand, &*major_brand);
1378 APar_assert(false, 4, &*major_brand);
1379 break;
1380 }
1381 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©prf.data", optarg, AtomFlags_Data_Text);
1382 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1383 break;
1384 }
1385 */
1386
1387 case Meta_soundEngineer : {
1388 APar_ScanAtoms(ISObasemediafile);
1389 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "soundEngineer") ) {
1390 char major_brand[4];
1391 UInt32_TO_String4(brand, &*major_brand);
1392 APar_assert(false, 4, &*major_brand);
1393 break;
1394 }
1395 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©sne.data", optarg, AtomFlags_Data_Text);
1396 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1397 break;
1398 }
1399
1400 case Meta_soloist : {
1401 APar_ScanAtoms(ISObasemediafile);
1402 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "soloist") ) {
1403 char major_brand[4];
1404 UInt32_TO_String4(brand, &*major_brand);
1405 APar_assert(false, 4, &*major_brand);
1406 break;
1407 }
1408 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©sol.data", optarg, AtomFlags_Data_Text);
1409 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1410 break;
1411 }
1412
1413 case Meta_executiveProducer : {
1414 APar_ScanAtoms(ISObasemediafile);
1415 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "executiveProducer") ) {
1416 char major_brand[4];
1417 UInt32_TO_String4(brand, &*major_brand);
1418 APar_assert(false, 4, &*major_brand);
1419 break;
1420 }
1421 AtomicInfo* artistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©xpd.data", optarg, AtomFlags_Data_Text);
1422 APar_Unified_atom_Put(artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1423 break;
1424 }
1425
1426 case Meta_songtitle : {
1427 APar_ScanAtoms(ISObasemediafile);
1428 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "title") ) {
1429 break;
1430 }
1431
1432 AtomicInfo* titleData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©nam.data", optarg, AtomFlags_Data_Text);
1433 APar_Unified_atom_Put(titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1434 break;
1435 }
1436
1437 case Meta_subtitle : {
1438 APar_ScanAtoms(ISObasemediafile);
1439 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "subtitle") ) {
1440 break;
1441 }
1442
1443 AtomicInfo* titleData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©st3.data", optarg, AtomFlags_Data_Text);
1444 APar_Unified_atom_Put(titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1445 break;
1446 }
1447
1448 case Meta_album : {
1449 APar_ScanAtoms(ISObasemediafile);
1450 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "album") ) {
1451 break;
1452 }
1453
1454 AtomicInfo* albumData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©alb.data", optarg, AtomFlags_Data_Text);
1455 APar_Unified_atom_Put(albumData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1456 break;
1457 }
1458
1459 case Meta_genre : {
1460 APar_ScanAtoms(ISObasemediafile);
1461 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "genre") ) {
1462 break;
1463 }
1464
1465 APar_MetaData_atomGenre_Set(optarg);
1466 break;
1467 }
1468
1469 case Meta_tracknum : {
1470 APar_ScanAtoms(ISObasemediafile);
1471 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "track number") ) {
1472 break;
1473 }
1474
1475 uint16_t pos_in_total = 0;
1476 uint16_t the_total = 0;
1477 if (strrchr(optarg, '/') != NULL) {
1478
1479 char* duplicate_info = optarg;
1480 char* item_stat = strsep(&duplicate_info,"/");
1481 sscanf(item_stat, "%" SCNu16, &pos_in_total);
1482 item_stat = strsep(&duplicate_info,"/");
1483 sscanf(item_stat, "%" SCNu16, &the_total);
1484 } else {
1485 sscanf(optarg, "%" SCNu16, &pos_in_total);
1486 }
1487
1488 AtomicInfo* tracknumData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.trkn.data", optarg, AtomFlags_Data_Binary);
1489 //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
1490 APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
1491 APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, pos_in_total, 16);
1492 APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, the_total, 16);
1493 APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
1494 break;
1495 }
1496
1497 case Meta_disknum : {
1498 APar_ScanAtoms(ISObasemediafile);
1499 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "disc number") ) {
1500 break;
1501 }
1502
1503 uint16_t pos_in_total = 0;
1504 uint16_t the_total = 0;
1505 if (strrchr(optarg, '/') != NULL) {
1506
1507 char* duplicate_info = optarg;
1508 char* item_stat = strsep(&duplicate_info,"/");
1509 sscanf(item_stat, "%" SCNu16, &pos_in_total);
1510 item_stat = strsep(&duplicate_info,"/");
1511 sscanf(item_stat, "%" SCNu16, &the_total);
1512
1513 } else {
1514 sscanf(optarg, "%" SCNu16, &pos_in_total);
1515 }
1516
1517 AtomicInfo* disknumData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.disk.data", optarg, AtomFlags_Data_Binary);
1518 //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
1519 APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
1520 APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, pos_in_total, 16);
1521 APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, the_total, 16);
1522 break;
1523 }
1524
1525 case Meta_comment : {
1526 APar_ScanAtoms(ISObasemediafile);
1527 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "comment") ) {
1528 break;
1529 }
1530
1531 AtomicInfo* commentData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©cmt.data", optarg, AtomFlags_Data_Text);
1532 APar_Unified_atom_Put(commentData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1533 break;
1534 }
1535
1536 case Meta_year : {
1537 APar_ScanAtoms(ISObasemediafile);
1538 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "year") ) {
1539 break;
1540 }
1541
1542 AtomicInfo* yearData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©day.data", optarg, AtomFlags_Data_Text);
1543 APar_Unified_atom_Put(yearData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1544 break;
1545 }
1546
1547 case Meta_lyrics : {
1548 APar_ScanAtoms(ISObasemediafile);
1549 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics") ) {
1550 break;
1551 }
1552
1553 AtomicInfo* lyricsData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©lyr.data", optarg, AtomFlags_Data_Text);
1554 APar_Unified_atom_Put(lyricsData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
1555 break;
1556 }
1557
1558 case Meta_lyrics_file : {
1559 APar_ScanAtoms(ISObasemediafile);
1560 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics") ) {
1561 break;
1562 }
1563
1564 APar_MetaData_atomLyrics_Set(optarg);
1565 break;
1566 }
1567
1568 case Meta_composer : {
1569 APar_ScanAtoms(ISObasemediafile);
1570 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "composer") ) {
1571 break;
1572 }
1573
1574 AtomicInfo* composerData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©wrt.data", optarg, AtomFlags_Data_Text);
1575 APar_Unified_atom_Put(composerData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1576 break;
1577 }
1578
1579 case Meta_copyright : {
1580 APar_ScanAtoms(ISObasemediafile);
1581 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "copyright") ) {
1582 break;
1583 }
1584
1585 AtomicInfo* copyrightData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.cprt.data", optarg, AtomFlags_Data_Text);
1586 APar_Unified_atom_Put(copyrightData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1587 break;
1588 }
1589
1590 case Meta_grouping : {
1591 APar_ScanAtoms(ISObasemediafile);
1592 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "grouping") ) {
1593 break;
1594 }
1595
1596 AtomicInfo* groupingData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©grp.data", optarg, AtomFlags_Data_Text);
1597 APar_Unified_atom_Put(groupingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1598 break;
1599 }
1600
1601 case Meta_compilation : {
1602 APar_ScanAtoms(ISObasemediafile);
1603 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "compilation") ) {
1604 break;
1605 }
1606
1607 if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) {
1608 APar_RemoveAtom("moov.udta.meta.ilst.cpil.data", VERSIONED_ATOM, 0);
1609 } else {
1610 //compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init
1611 AtomicInfo* compilationData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.cpil.data", optarg, AtomFlags_Data_UInt);
1612 APar_Unified_atom_Put(compilationData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is compilation
1613 }
1614 break;
1615 }
1616
1617 case Meta_hdvideo : {
1618 APar_ScanAtoms(ISObasemediafile);
1619 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "hdvideo") ) {
1620 break;
1621 }
1622
1623 if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) {
1624 APar_RemoveAtom("moov.udta.meta.ilst.hdvd.data", VERSIONED_ATOM, 0);
1625 } else {
1626 //compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init
1627 AtomicInfo* hdvideoData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.hdvd.data", optarg, AtomFlags_Data_UInt);
1628 APar_Unified_atom_Put(hdvideoData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is compilation
1629 }
1630 break;
1631 }
1632
1633 case Meta_BPM : {
1634 APar_ScanAtoms(ISObasemediafile);
1635 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "BPM") ) {
1636 break;
1637 }
1638
1639 if (strcmp(optarg, "0") == 0 || strlen(optarg) == 0) {
1640 APar_RemoveAtom("moov.udta.meta.ilst.tmpo.data", VERSIONED_ATOM, 0);
1641 } else {
1642 uint16_t bpm_value = 0;
1643 sscanf(optarg, "%" SCNu16, &bpm_value );
1644 //bpm is [0, 0, 0, 0, 0, bpm_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init
1645 AtomicInfo* bpmData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tmpo.data", optarg, AtomFlags_Data_UInt);
1646 APar_Unified_atom_Put(bpmData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, bpm_value, 16);
1647 }
1648 break;
1649 }
1650
1651 case Meta_advisory : {
1652 APar_ScanAtoms(ISObasemediafile);
1653 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "content advisory") ) {
1654 break;
1655 }
1656
1657 if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) {
1658 APar_RemoveAtom("moov.udta.meta.ilst.rtng.data", VERSIONED_ATOM, 0);
1659 } else {
1660 uint8_t rating_value = 0;
1661 if (strcmp(optarg, "clean") == 0) {
1662 rating_value = 2; //only \02 is clean
1663 } else if (strcmp(optarg, "explicit") == 0) {
1664 rating_value = 4; //most non \00, \02 numbers are allowed
1665 }
1666 //rating is [0, 0, 0, 0, rating_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init
1667 AtomicInfo* advisoryData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.rtng.data", optarg, AtomFlags_Data_UInt);
1668 APar_Unified_atom_Put(advisoryData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, rating_value, 8);
1669 }
1670 break;
1671 }
1672
1673 case Meta_artwork : { //handled differently: there can be multiple "moov.udta.meta.ilst.covr.data" atoms
1674 char* env_PicOptions = getenv("PIC_OPTIONS");
1675 APar_ScanAtoms(ISObasemediafile);
1676 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "coverart") ) {
1677 break;
1678 }
1679
1680 APar_MetaData_atomArtwork_Set(optarg, env_PicOptions);
1681 break;
1682 }
1683
1684 case Meta_stik : {
1685 APar_ScanAtoms(ISObasemediafile);
1686 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "'stik'") ) {
1687 break;
1688 }
1689
1690 if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) {
1691 APar_RemoveAtom("moov.udta.meta.ilst.stik.data", VERSIONED_ATOM, 0);
1692 } else {
1693 uint8_t stik_value = 0;
1694
1695 if (strncmp(optarg, "value=", 6) == 0) {
1696 char* stik_val_str_ptr = optarg;
1697 strsep(&stik_val_str_ptr,"=");
1698 stik_value = strtoul(stik_val_str_ptr, NULL, 10);
1699 } else {
1700 stiks* return_stik = MatchStikString(optarg);
1701 if (return_stik != NULL) {
1702 stik_value = return_stik->stik_number;
1703 if (strcmp(optarg, "Audiobook") == 0) {
1704 forced_suffix_type = FORCE_M4B_TYPE;
1705 }
1706 }
1707 }
1708 //stik is [0, 0, 0, 0, stik_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init
1709 AtomicInfo* stikData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.stik.data", optarg, AtomFlags_Data_UInt);
1710 APar_Unified_atom_Put(stikData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, stik_value, 8);
1711 }
1712 break;
1713 }
1714
1715 case Meta_EncodingTool : {
1716 APar_ScanAtoms(ISObasemediafile);
1717 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "encoding tool") ) {
1718 break;
1719 }
1720
1721 AtomicInfo* encodingtoolData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©too.data", optarg, AtomFlags_Data_Text);
1722 APar_Unified_atom_Put(encodingtoolData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1723 break;
1724 }
1725
1726 case Meta_EncodedBy : {
1727 APar_ScanAtoms(ISObasemediafile);
1728 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "encoded by") ) {
1729 break;
1730 }
1731
1732 AtomicInfo* encodedbyData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.©enc.data", optarg, AtomFlags_Data_Text);
1733 APar_Unified_atom_Put(encodedbyData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1734 break;
1735 }
1736
1737 case Meta_apID : {
1738 APar_ScanAtoms(ISObasemediafile);
1739 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "Account Name") ) {
1740 break;
1741 }
1742
1743 AtomicInfo* apIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.apID.data", optarg, AtomFlags_Data_Text);
1744 APar_Unified_atom_Put(apIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1745 break;
1746 }
1747
1748 case Meta_description : {
1749 APar_ScanAtoms(ISObasemediafile);
1750 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "description") ) {
1751 break;
1752 }
1753
1754 AtomicInfo* descriptionData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.desc.data", optarg, AtomFlags_Data_Text);
1755 APar_Unified_atom_Put(descriptionData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1756 break;
1757 }
1758
1759 case Meta_longdescription : {
1760 APar_ScanAtoms(ISObasemediafile);
1761 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "longdesc") ) {
1762 break;
1763 }
1764
1765 AtomicInfo* descriptionData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.ldes.data", optarg, AtomFlags_Data_Text);
1766 APar_Unified_atom_Put(descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
1767 break;
1768 }
1769
1770 case Meta_storedescription : {
1771 APar_ScanAtoms(ISObasemediafile);
1772 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "storedesc") ) {
1773 break;
1774 }
1775
1776 AtomicInfo* descriptionData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.sdes.data", optarg, AtomFlags_Data_Text);
1777 APar_Unified_atom_Put(descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
1778 break;
1779 }
1780
1781 case Meta_Rating : {
1782 APar_ScanAtoms(ISObasemediafile);
1783 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "Rating") ) {
1784 break;
1785 }
1786
1787 AtomicInfo* ratingData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.rate.data", optarg, AtomFlags_Data_Text);
1788 APar_Unified_atom_Put(ratingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1789 break;
1790 }
1791
1792 case Meta_TV_Network : {
1793 APar_ScanAtoms(ISObasemediafile);
1794 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Network") ) {
1795 break;
1796 }
1797
1798 AtomicInfo* tvnetworkData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tvnn.data", optarg, AtomFlags_Data_Text);
1799 APar_Unified_atom_Put(tvnetworkData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1800 break;
1801 }
1802
1803 case Meta_TV_ShowName : {
1804 APar_ScanAtoms(ISObasemediafile);
1805 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Show name") ) {
1806 break;
1807 }
1808
1809 AtomicInfo* tvshownameData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tvsh.data", optarg, AtomFlags_Data_Text);
1810 APar_Unified_atom_Put(tvshownameData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1811 break;
1812 }
1813
1814 case Meta_TV_Episode : { //if the show "ABC Lost 209", its "209"
1815 APar_ScanAtoms(ISObasemediafile);
1816 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Episode string") ) {
1817 break;
1818 }
1819
1820 AtomicInfo* tvepisodeData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tven.data", optarg, AtomFlags_Data_Text);
1821 APar_Unified_atom_Put(tvepisodeData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1822 break;
1823 }
1824
1825 case Meta_TV_SeasonNumber : { //if the show "ABC Lost 209", its 2; integer 2 not char "2"
1826 APar_ScanAtoms(ISObasemediafile);
1827 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Season") ) {
1828 break;
1829 }
1830
1831 uint16_t data_value = 0;
1832 sscanf(optarg, "%" SCNu16, &data_value );
1833
1834 AtomicInfo* tvseasonData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tvsn.data", optarg, AtomFlags_Data_UInt);
1835 //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
1836 APar_Unified_atom_Put(tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
1837 APar_Unified_atom_Put(tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 16);
1838 break;
1839 }
1840
1841 case Meta_TV_EpisodeNumber : { //if the show "ABC Lost 209", its 9; integer 9 (0x09) not char "9"(0x39)
1842 APar_ScanAtoms(ISObasemediafile);
1843 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Episode number") ) {
1844 break;
1845 }
1846
1847 uint16_t data_value = 0;
1848 sscanf(optarg, "%" SCNu16, &data_value );
1849
1850 AtomicInfo* tvepisodenumData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.tves.data", optarg, AtomFlags_Data_UInt);
1851 //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
1852 APar_Unified_atom_Put(tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
1853 APar_Unified_atom_Put(tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 16);
1854 break;
1855 }
1856
1857 case Meta_cnID : { // the iTunes Catalog ID
1858 APar_ScanAtoms(ISObasemediafile);
1859 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes Catalog ID") ) {
1860 break;
1861 }
1862
1863 uint32_t data_value = 0;
1864 sscanf(optarg, "%" SCNu32, &data_value );
1865
1866 AtomicInfo* cnIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.cnID.data", optarg, AtomFlags_Data_UInt);
1867 APar_Unified_atom_Put(cnIDData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 32);
1868 break;
1869 }
1870
1871 case Meta_geID : { // the iTunes Genre ID
1872 APar_ScanAtoms(ISObasemediafile);
1873 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes Genre ID") ) {
1874 break;
1875 }
1876
1877 uint32_t data_value = 0;
1878 sscanf(optarg, "%" SCNu32, &data_value );
1879
1880 AtomicInfo* geIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.geID.data", optarg, AtomFlags_Data_UInt);
1881 APar_Unified_atom_Put(geIDData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 32);
1882 break;
1883 }
1884
1885 case Meta_xID : { // the vendor-supplied iTunes xID
1886 APar_ScanAtoms(ISObasemediafile);
1887 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes xID") ) {
1888 break;
1889 }
1890
1891 AtomicInfo* xIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.xid .data", optarg, AtomFlags_Data_Text);
1892 APar_Unified_atom_Put(xIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1893 break;
1894 }
1895
1896 case Meta_album_artist : {
1897 APar_ScanAtoms(ISObasemediafile);
1898 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "album artist") ) {
1899 break;
1900 }
1901
1902 AtomicInfo* albumartistData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.aART.data", optarg, AtomFlags_Data_Text);
1903 APar_Unified_atom_Put(albumartistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1904 break;
1905 }
1906
1907 case Meta_podcastFlag : {
1908 APar_ScanAtoms(ISObasemediafile);
1909 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast flag") ) {
1910 break;
1911 }
1912
1913 if (strcmp(optarg, "false") == 0) {
1914 APar_RemoveAtom("moov.udta.meta.ilst.pcst.data", VERSIONED_ATOM, 0);
1915 } else {
1916 //podcastflag: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init
1917 AtomicInfo* podcastFlagData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.pcst.data", optarg, AtomFlags_Data_UInt);
1918 APar_Unified_atom_Put(podcastFlagData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 denotes podcast flag
1919 }
1920
1921 break;
1922 }
1923
1924 case Meta_keyword : { //TODO to the end of iTunes-style metadata & uuid atoms
1925 APar_ScanAtoms(ISObasemediafile);
1926 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "keyword") ) {
1927 break;
1928 }
1929
1930 AtomicInfo* keywordData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.keyw.data", optarg, AtomFlags_Data_Text);
1931 APar_Unified_atom_Put(keywordData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1932 break;
1933 }
1934
1935 case Meta_category : { // see http://www.apple.com/itunes/podcasts/techspecs.html for available categories
1936 APar_ScanAtoms(ISObasemediafile);
1937 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "category") ) {
1938 break;
1939 }
1940
1941 AtomicInfo* categoryData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.catg.data", optarg, AtomFlags_Data_Text);
1942 APar_Unified_atom_Put(categoryData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1943 break;
1944 }
1945
1946 case Meta_podcast_URL : { // usually a read-only value, but useful for getting videos into the 'podcast' menu
1947 APar_ScanAtoms(ISObasemediafile);
1948 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast URL") ) {
1949 break;
1950 }
1951
1952 AtomicInfo* podcasturlData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.purl.data", optarg, AtomFlags_Data_Binary);
1953 APar_Unified_atom_Put(podcasturlData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0);
1954 break;
1955 }
1956
1957 case Meta_podcast_GUID : { // Global Unique IDentifier; it is *highly* doubtful that this would be useful...
1958 APar_ScanAtoms(ISObasemediafile);
1959 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast GUID") ) {
1960 break;
1961 }
1962
1963 AtomicInfo* globalidData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.egid.data", optarg, AtomFlags_Data_Binary);
1964 APar_Unified_atom_Put(globalidData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0);
1965 break;
1966 }
1967
1968 case Meta_PurchaseDate : { // might be useful to *remove* this, but adding it... although it could function like id3v2 tdtg...
1969 APar_ScanAtoms(ISObasemediafile);
1970 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "purchase date") ) {
1971 break;
1972 }
1973 char* purd_time;
1974 bool free_memory = false;
1975 if (optarg != NULL) {
1976 if (strcmp(optarg, "timestamp") == 0) {
1977 purd_time = (char *)malloc(sizeof(char)*255);
1978 free_memory = true;
1979 APar_StandardTime(purd_time);
1980 } else {
1981 purd_time = optarg;
1982 }
1983 } else {
1984 purd_time = optarg;
1985 }
1986
1987 AtomicInfo* globalIDData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.purd.data", optarg, AtomFlags_Data_Text);
1988 APar_Unified_atom_Put(globalIDData_atom, purd_time, UTF8_iTunesStyle_256glyphLimited, 0, 0);
1989 if (free_memory) {
1990 free(purd_time);
1991 purd_time = NULL;
1992 }
1993 break;
1994 }
1995
1996 case Meta_PlayGapless : {
1997 APar_ScanAtoms(ISObasemediafile);
1998 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "gapless playback") ) {
1999 break;
2000 }
2001
2002 if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) {
2003 APar_RemoveAtom("moov.udta.meta.ilst.pgap.data", VERSIONED_ATOM, 0);
2004 } else {
2005 //gapless playback: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init
2006 AtomicInfo* gaplessData_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.pgap.data", optarg, AtomFlags_Data_UInt);
2007 APar_Unified_atom_Put(gaplessData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8);
2008 }
2009 break;
2010 }
2011
2012 case Meta_SortOrder : {
2013 AtomicInfo* sortOrder_atom = NULL;
2014 APar_ScanAtoms(ISObasemediafile);
2015 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "sort order tags") ) {
2016 break;
2017 }
2018
2019 if (argv[optind] == NULL) {
2020 fprintf(stdout, "AP warning, skipping setting the sort order %s tag\n", optarg);
2021 break;
2022 }
2023
2024 if ( strcmp(optarg, "name") == 0 ) {
2025 sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.sonm.data", argv[optind], AtomFlags_Data_Text);
2026 } else if ( strcmp(optarg, "artist") == 0 ) {
2027 sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.soar.data", argv[optind], AtomFlags_Data_Text);
2028 } else if ( strcmp(optarg, "albumartist") == 0 ) {
2029 sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.soaa.data", argv[optind], AtomFlags_Data_Text);
2030 } else if ( strcmp(optarg, "album") == 0 ) {
2031 sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.soal.data", argv[optind], AtomFlags_Data_Text);
2032 } else if ( strcmp(optarg, "composer") == 0 ) {
2033 sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.soco.data", argv[optind], AtomFlags_Data_Text);
2034 } else if ( strcmp(optarg, "show") == 0 ) {
2035 sortOrder_atom = APar_MetaData_atom_Init("moov.udta.meta.ilst.sosn.data", argv[optind], AtomFlags_Data_Text);
2036 }
2037 APar_Unified_atom_Put(sortOrder_atom, argv[optind], UTF8_iTunesStyle_256glyphLimited, 0, 0);
2038
2039 break;
2040 }
2041
2042 //uuid atoms
2043
2044 case Meta_StandardDate : {
2045 APar_ScanAtoms(ISObasemediafile);
2046 char* formed_time = (char *)malloc(sizeof(char)*110);
2047 if (argv[optind]) {
2048 if (strlen(argv[optind]) > 0) {
2049 APar_StandardTime(formed_time);
2050 }
2051 }
2052
2053 AtomicInfo* tdtgUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", "tdtg", AtomFlags_Data_Text, formed_time, false);
2054 APar_Unified_atom_Put(tdtgUUID, formed_time, UTF8_iTunesStyle_Unlimited, 0, 0);
2055 free(formed_time);
2056 break;
2057 }
2058
2059 case Meta_URL : {
2060 APar_ScanAtoms(ISObasemediafile);
2061 AtomicInfo* urlUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", "©url", AtomFlags_Data_Text, optarg, false);
2062 APar_Unified_atom_Put(urlUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
2063 break;
2064 }
2065
2066 case Meta_Information : {
2067 APar_ScanAtoms(ISObasemediafile);
2068 AtomicInfo* infoUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", "©inf", AtomFlags_Data_Text, optarg, false);
2069 APar_Unified_atom_Put(infoUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
2070 break;
2071 }
2072
2073 case Meta_uuid : {
2074 APar_ScanAtoms(ISObasemediafile);
2075 uint32_t uuid_dataType = 0;
2076 uint32_t desc_len = 0;
2077 uint8_t mime_len = 0;
2078 char* uuid_file_path = NULL;
2079 char* uuid_file_description = NULL;
2080 char* uuid_file_extn = NULL;
2081 char* uuid_file_mimetype = NULL;
2082 // char* uuid_file_filename = NULL;
2083
2084 // a uuid in AP is a version 5 uuid created by getting a sha1 hash
2085 // of a string (the atom name) in a namespace ('AP.sf.net'). This
2086 // is guaranteed to be reproducible, so later it can be verified
2087 // that this uuid (which could come from anywhere), is in fact made
2088 // by AtomicParsley. This is achieved by storing the atom name
2089 // string right after the uuid, and is read back later and a new
2090 // uuid is created to see if it matches the discovered uuid. If
2091 // they match, it will print out or extract to a file; if not, only
2092 // its name will be displayed in the tree.
2093 //
2094 // --meta-uuid "©foo" 1 'http://www.url.org' --meta-uuid "pdf1"
2095 // file /some/path/pic.pdf description="My Booty, Your Booty,
2096 // Djbouti"
2097
2098 if ( strcmp(argv[optind], "text") == 0 || strcmp(argv[optind], "1") == 0 ) uuid_dataType = AtomFlags_Data_Text;
2099 if ( strcmp(argv[optind], "file") == 0 ) {
2100 uuid_dataType = AtomFlags_Data_uuid_binary;
2101 if (optind+1 < argc) {
2102 if (true) { //TODO: test the file to see if it exists
2103 uuid_file_path = argv[optind + 1];
2104 //get the file extension/suffix of the file to embed
2105 uuid_file_extn = strrchr(uuid_file_path, '.'); //'.' inclusive; say goodbye to AP-0.8.8.tar.bz2
2106
2107 //#ifdef _WIN32
2108 //#define path_delim '\\'
2109 //#else
2110 //#define path_delim '/'
2111 //#endif
2112 // uuid_file_filename = strrchr(uuid_file_path, path_delim)+1; //includes whatever extensions
2113 }
2114 if (uuid_file_extn == NULL) {
2115 fprintf(stdout, "AP warning: embedding a file onto a uuid atom requires a file extension. Skipping.\n");
2116 continue;
2117 }
2118 //copy a pointer to description
2119 int more_optional_args = 2;
2120 while (optind + more_optional_args < argc) {
2121 if ( strncmp(argv[optind + more_optional_args], "description=", 12) == 0 && argv[optind + more_optional_args][12]) {
2122 uuid_file_description = argv[optind + more_optional_args] + 12;
2123 desc_len = strlen(uuid_file_description)+1; //+1 for the trailing 1 byte NULL terminator
2124 }
2125 if ( strncmp(argv[optind + more_optional_args], "mime-type=", 10) == 0 && argv[optind + more_optional_args][10]) {
2126 uuid_file_mimetype = argv[optind + more_optional_args] + 10;
2127 mime_len = strlen(uuid_file_mimetype)+1; //+1 for the trailing 1 byte NULL terminator
2128 }
2129 if (strncmp(argv[optind+more_optional_args], "--", 2) == 0) {
2130 break; //we've hit another cli argument
2131 }
2132 more_optional_args++;
2133 }
2134 }
2135 }
2136
2137 AtomicInfo* genericUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s", optarg, uuid_dataType, argv[optind +1], true);
2138
2139 if (uuid_dataType == AtomFlags_Data_uuid_binary && genericUUID != NULL) {
2140 TestFileExistence(uuid_file_path, true);
2141
2142 //format for a uuid atom set by AP:
2143 //4 bytes - atom length as uin32_t
2144 //4 bytes - atom name as iso 8859-1 atom name as a 4byte string set to 'uuid'
2145 //16 bytes - the uuid; here a version 5 sha1-based hash derived from a name in a namespace of 'AtomicParsley.sf.net'
2146 //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)
2147 //4 bytes - atom version & flags (currently 1 for 'text' or '88' for binary attachment)
2148 //4 bytes - NULL space
2149 /////////////text or 1 for version/flags
2150 //X bytes - utf8 string, no null termination
2151 /////////////binary attachment or 88 for version/flags
2152 //4 bytes - length of utf8 string describing the attachment
2153 //X bytes - utf8 string describing the attachment, null terminated
2154 //1 byte - length of the file suffix (including the period) of the originating file
2155 //X bytes - utf8 string of the file suffix, null terminated
2156 //1 byte - length of the MIME-type
2157 //X bytes - utf8 string holding the MIME-type, null terminated
2158 //4 bytes - length of the attached binary data/file length
2159 //X bytes - binary data
2160
2161 uint32_t extn_len = strlen(uuid_file_extn)+1; //+1 for the trailing 1 byte NULL terminator
2162 uint64_t file_len = findFileSize(uuid_file_path);
2163
2164 APar_MetaData_atom_QuickInit(genericUUID->AtomicNumber, uuid_dataType, 20, extn_len + desc_len + file_len + 100);
2165 genericUUID->AtomicClassification = EXTENDED_ATOM; //it gets reset in QuickInit Above; force its proper setting
2166
2167 if (uuid_file_description == NULL || desc_len == 0) {
2168 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)
2169 } else {
2170 APar_Unified_atom_Put(genericUUID, uuid_file_description, UTF8_3GP_Style, desc_len, 32); //sets 4bytes desc_len, then Xbytes description (in that order)
2171 }
2172
2173 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)
2174
2175 if (uuid_file_mimetype == NULL || mime_len == 0) {
2176 APar_Unified_atom_Put(genericUUID, "none", UTF8_3GP_Style, 5, 8); //sets 4bytes desc_len, then 5bytes description (forced to "none" + 1byte null)
2177 } else {
2178 APar_Unified_atom_Put(genericUUID, uuid_file_mimetype, UTF8_3GP_Style, mime_len, 8); //sets 1 byte mime len, then Xbytes mime type
2179 }
2180
2181 FILE* uuid_binfile = APar_OpenFile(uuid_file_path, "rb");
2182 APar_Unified_atom_Put(genericUUID, NULL, UTF8_3GP_Style, file_len, 32);
2183 //store the data directly on the atom in AtomicData
2184 uint32_t bin_bytes_read = APar_ReadFile(genericUUID->AtomicData + (genericUUID->AtomicLength - 32), uuid_binfile, file_len);
2185 genericUUID->AtomicLength += bin_bytes_read;
2186 fclose(uuid_binfile);
2187
2188 } else { //text type
2189 APar_Unified_atom_Put(genericUUID, argv[optind +1], UTF8_iTunesStyle_Unlimited, 0, 0);
2190 }
2191
2192 break;
2193 }
2194
2195 case Opt_Extract_all_uuids : {
2196 APar_ScanAtoms(ISObasemediafile);
2197 char* output_path = NULL;
2198 if (optind + 1 == argc) {
2199 output_path = argv[optind];
2200 }
2201
2202 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
2203 APar_Print_APuuid_atoms(ISObasemediafile, output_path, EXTRACT_ALL_UUID_BINARYS);
2204 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
2205
2206 exit(0);//never gets here
2207 break;
2208 }
2209
2210 case Opt_Extract_a_uuid : {
2211 APar_ScanAtoms(ISObasemediafile);
2212
2213 char* uuid_path = (char*)calloc(1, sizeof(char)*256+1);
2214 char* uuid_binary_str = (char*)calloc(1, sizeof(char)*20+1);
2215 char uuid_4char_name[16]; memset(uuid_4char_name, 0, 16);
2216 AtomicInfo* extractionAtom = NULL;
2217
2218 UTF8Toisolat1((unsigned char*)&uuid_4char_name, 4, (unsigned char*)optarg, strlen(optarg) );
2219 APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str);
2220
2221 //this will only append (and knock off) %s (anything) at the end of a string
2222 uint16_t path_len = strlen("moov.udta.meta.uuid=%s");
2223 memcpy(uuid_path, "moov.udta.meta.uuid=%s", path_len-2);
2224 memcpy(uuid_path + (path_len-2), uuid_binary_str, 16);
2225
2226 extractionAtom = APar_FindAtom(uuid_path, false, EXTENDED_ATOM, 0, true);
2227 if (extractionAtom != NULL) {
2228 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
2229 APar_Extract_uuid_binary_file(extractionAtom, ISObasemediafile, NULL);
2230 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
2231 }
2232
2233 free(uuid_path); uuid_path = NULL;
2234 free(uuid_binary_str); uuid_binary_str = NULL;
2235 exit(0);
2236 break; //never gets here
2237 }
2238
2239 case Opt_Ipod_AVC_uuid : {
2240 if (deep_atom_scan == true) {
2241 if (strcmp(optarg, "1200") != 0) {
2242 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
2243 break;
2244 }
2245 uint8_t total_tracks = 0;
2246 uint8_t a_track = 0;//unused
2247 char atom_path[100];
2248 AtomicInfo* video_desc_atom = NULL;
2249
2250 memset(atom_path, 0, 100);
2251
2252 APar_FindAtomInTrack(total_tracks, a_track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here.
2253
2254 while (a_track < total_tracks) {
2255 a_track++;
2256 sprintf(atom_path, "moov.trak[%u].mdia.minf.stbl.stsd.avc1", a_track);
2257 video_desc_atom = APar_FindAtom(atom_path, false, VERSIONED_ATOM, 0, false);
2258
2259 if (video_desc_atom != NULL) {
2260 uint16_t mb_t = APar_TestVideoDescription(video_desc_atom, APar_OpenFile(ISObasemediafile, "rb"));
2261 if (mb_t > 0 && mb_t <= 1200) {
2262 sprintf(atom_path, "moov.trak[%u].mdia.minf.stbl.stsd.avc1.uuid=", a_track);
2263 uint8_t uuid_baselen = (uint8_t)strlen(atom_path);
2264 APar_uuid_scanf(atom_path + uuid_baselen, "6b6840f2-5f24-4fc5-ba39-a51bcf0323f3");
2265 APar_endian_uuid_bin_str_conversion(atom_path + uuid_baselen);
2266 APar_Generate_iPod_uuid(atom_path);
2267 }
2268 }
2269 }
2270
2271 } else {
2272 fprintf(stdout, "the --DeepScan option is required for this operation. Skipping\n");
2273 }
2274 break;
2275 }
2276
2277 case Manual_atom_removal : {
2278 APar_ScanAtoms(ISObasemediafile);
2279
2280 char* compliant_name = (char*)malloc(sizeof(char)* strlen(optarg) +1);
2281 memset(compliant_name, 0, strlen(optarg) +1);
2282 UTF8Toisolat1((unsigned char*)compliant_name, strlen(optarg), (unsigned char*)optarg, strlen(optarg) );
2283
2284 if (strstr(optarg, "uuid=") != NULL) {
2285 uint16_t uuid_path_pos = 0;
2286 uint16_t uuid_path_len = strlen(optarg);
2287 while (compliant_name+uuid_path_pos < compliant_name+uuid_path_len) {
2288 if (strncmp(compliant_name+uuid_path_pos, "uuid=", 5) == 0) {
2289 uuid_path_pos+=5;
2290 break;
2291 }
2292 uuid_path_pos++;
2293 }
2294 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)
2295 uint8_t uuid_len = APar_uuid_scanf(compliant_name+uuid_path_pos, optarg+uuid_path_pos);
2296 compliant_name[uuid_path_pos+uuid_len] = 0;
2297 }
2298 APar_RemoveAtom(compliant_name, EXTENDED_ATOM, 0);
2299
2300 } else if (strcmp(compliant_name + (strlen(compliant_name) - 4), "data") == 0) {
2301 APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0);
2302
2303 } else {
2304 size_t string_len = strlen(compliant_name);
2305 //reverseDNS atom path
2306 if (strstr(optarg, ":[") != NULL && compliant_name[string_len-1] == ']' ) {
2307 APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0);
2308
2309 //packed language asset
2310 } else if (strncmp(compliant_name + string_len - 9, ":lang=", 6) == 0 ) {
2311 uint16_t packed_lang = PackLanguage(compliant_name + string_len - 3, 0);
2312 memset(compliant_name + string_len - 9, 0, 1);
2313 APar_RemoveAtom(compliant_name, PACKED_LANG_ATOM, packed_lang);
2314
2315 } else {
2316 APar_RemoveAtom(compliant_name, UNKNOWN_ATOM, 0);
2317 }
2318 }
2319 free(compliant_name);
2320 compliant_name = NULL;
2321 break;
2322 }
2323
2324 //3gp tags
2325
2326 /*
2327 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
2328 are optional and are determined by directly testing arguments. Optional arguments common to the 3gp asset group (language, unicode, track/movie userdata) are
2329 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
2330 many tracks there are for all tracks. Each pass through the loop, set the individual pieces of metadata.
2331 */
2332 case _3GP_Title : {
2333 APar_ScanAtoms(ISObasemediafile);
2334 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "title") ) {
2335 break;
2336 }
2337 bool set_UTF16_text = false;
2338 uint16_t packed_lang = 0;
2339 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2340 uint8_t selected_track = 0;
2341 uint8_t a_track = 0;//unused
2342 uint8_t asset_iterations = 0;
2343
2344 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3);
2345
2346 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2347 asset_iterations = 1;
2348 } else if (userdata_area == ALL_TRACKS_ATOM) {
2349 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2350 if (asset_iterations == 1) selected_track = 1; //otherwise, APar_UserData_atom_Init will shift to non-existing track 0
2351 }
2352
2353 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2354 AtomicInfo* title_asset = APar_UserData_atom_Init("titl", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2355 APar_Unified_atom_Put(title_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2356 }
2357 break;
2358 }
2359
2360 case _3GP_Author : {
2361 APar_ScanAtoms(ISObasemediafile);
2362 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "author") ) {
2363 break;
2364 }
2365 bool set_UTF16_text = false;
2366 uint16_t packed_lang = 0;
2367 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2368 uint8_t selected_track = 0;
2369 uint8_t a_track = 0;//unused
2370 uint8_t asset_iterations = 0;
2371
2372 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3);
2373
2374 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2375 asset_iterations = 1;
2376 } else if (userdata_area == ALL_TRACKS_ATOM) {
2377 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2378 if (asset_iterations == 1) selected_track = 1;
2379 }
2380
2381 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2382 AtomicInfo* author_asset = APar_UserData_atom_Init("auth", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2383 APar_Unified_atom_Put(author_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2384 }
2385 break;
2386 }
2387
2388 case _3GP_Performer : {
2389 APar_ScanAtoms(ISObasemediafile);
2390 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "performer") ) {
2391 break;
2392 }
2393 bool set_UTF16_text = false;
2394 uint16_t packed_lang = 0;
2395 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2396 uint8_t selected_track = 0;
2397 uint8_t a_track = 0;//unused
2398 uint8_t asset_iterations = 0;
2399
2400 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3);
2401
2402 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2403 asset_iterations = 1;
2404 } else if (userdata_area == ALL_TRACKS_ATOM) {
2405 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2406 if (asset_iterations == 1) selected_track = 1;
2407 }
2408
2409 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2410 AtomicInfo* performer_asset = APar_UserData_atom_Init("perf", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2411 APar_Unified_atom_Put(performer_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2412 }
2413 break;
2414 }
2415
2416 case _3GP_Genre : {
2417 APar_ScanAtoms(ISObasemediafile);
2418 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "genre") ) {
2419 break;
2420 }
2421 bool set_UTF16_text = false;
2422 uint16_t packed_lang = 0;
2423 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2424 uint8_t selected_track = 0;
2425 uint8_t a_track = 0;//unused
2426 uint8_t asset_iterations = 0;
2427
2428 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3);
2429
2430 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2431 asset_iterations = 1;
2432 } else if (userdata_area == ALL_TRACKS_ATOM) {
2433 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2434 if (asset_iterations == 1) selected_track = 1;
2435 }
2436
2437 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2438 AtomicInfo* genre_asset = APar_UserData_atom_Init("gnre", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2439 APar_Unified_atom_Put(genre_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2440 }
2441 break;
2442 }
2443
2444 case _3GP_Description : {
2445 APar_ScanAtoms(ISObasemediafile);
2446 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "description") ) {
2447 break;
2448 }
2449 bool set_UTF16_text = false;
2450 uint16_t packed_lang = 0;
2451 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2452 uint8_t selected_track = 0;
2453 uint8_t a_track = 0;//unused
2454 uint8_t asset_iterations = 0;
2455
2456 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3);
2457
2458 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2459 asset_iterations = 1;
2460 } else if (userdata_area == ALL_TRACKS_ATOM) {
2461 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2462 if (asset_iterations == 1) selected_track = 1;
2463 }
2464
2465 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2466 AtomicInfo* description_asset = APar_UserData_atom_Init("dscp", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2467 APar_Unified_atom_Put(description_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2468 }
2469 break;
2470 }
2471
2472 case ISO_Copyright: //ISO copyright atom common to all files that are derivatives of the base media file format, identical to....
2473 case _3GP_Copyright : { //the 3gp copyright asset; this gets a test for major branding (but only with the cli arg --3gp-copyright).
2474 APar_ScanAtoms(ISObasemediafile);
2475
2476 if (c == _3GP_Copyright) {
2477 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "copyright") ) {
2478 break;
2479 }
2480 }
2481
2482 bool set_UTF16_text = false;
2483 uint16_t packed_lang = 0;
2484 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2485 uint8_t selected_track = 0;
2486 uint8_t a_track = 0;//unused
2487 uint8_t asset_iterations = 0;
2488
2489 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 3);
2490
2491 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2492 asset_iterations = 1;
2493 } else if (userdata_area == ALL_TRACKS_ATOM) {
2494 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2495 if (asset_iterations == 1) selected_track = 1;
2496 }
2497
2498 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2499 AtomicInfo* copyright_notice = APar_UserData_atom_Init("cprt", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2500 APar_Unified_atom_Put(copyright_notice, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2501 }
2502 break;
2503 }
2504
2505 case _3GP_Album : {
2506 APar_ScanAtoms(ISObasemediafile);
2507 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "album") ) {
2508 break;
2509 }
2510 bool set_UTF16_text = false;
2511 uint16_t packed_lang = 0;
2512 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2513 uint8_t selected_track = 0;
2514 uint8_t a_track = 0;//unused
2515 uint8_t asset_iterations = 0;
2516 uint8_t tracknum = 0;
2517
2518 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 4);
2519
2520 //cygle through the remaining independant arguments (before the next --cli_flag) and figure out if any are useful to us; already have lang & utf16
2521 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)
2522 if ( argv[optind + i] && optind + i <= total_args) {
2523 if ( strncmp(argv[optind + i], "trknum=", 7) == 0 ) {
2524 char* track_num = argv[optind + i];
2525 strsep(&track_num,"=");
2526 tracknum = strtoul(track_num, NULL, 10);
2527 }
2528 if (*argv[optind + i] == '-') break;
2529 }
2530 }
2531
2532 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2533 asset_iterations = 1;
2534 } else if (userdata_area == ALL_TRACKS_ATOM) {
2535 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2536 if (asset_iterations == 1) selected_track = 1;
2537 }
2538
2539 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2540 AtomicInfo* album_asset = APar_UserData_atom_Init("albm", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2541 APar_Unified_atom_Put(album_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2542 if (tracknum != 0) {
2543 APar_Unified_atom_Put(album_asset, NULL, UTF8_3GP_Style, tracknum, 8);
2544 }
2545 }
2546 break;
2547 }
2548
2549 case _3GP_Year : {
2550 APar_ScanAtoms(ISObasemediafile);
2551 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "year") ) {
2552 break;
2553 }
2554 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2555 uint8_t selected_track = 0;
2556 uint8_t a_track = 0;//unused
2557 uint8_t asset_iterations = 0;
2558 uint16_t year_tag = 0;
2559
2560 if ( argv[optind] && optind <= total_args) {
2561 if ( strcmp(argv[optind], "movie") == 0 ) {
2562 userdata_area = MOVIE_LEVEL_ATOM; }
2563 if ( strncmp(argv[optind], "track=", 6) == 0 ) {
2564 char* trak_idx = argv[optind];
2565 strsep(&trak_idx, "=");
2566 selected_track = strtoul(trak_idx, NULL, 10);
2567 userdata_area = SINGLE_TRACK_ATOM;
2568 } else if ( strcmp(argv[optind], "track") == 0 ) {
2569 userdata_area = ALL_TRACKS_ATOM;
2570 }
2571 }
2572
2573 sscanf(optarg, "%" SCNu16, &year_tag);
2574
2575 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2576 asset_iterations = 1;
2577 } else if (userdata_area == ALL_TRACKS_ATOM) {
2578 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2579 if (asset_iterations == 1) selected_track = 1;
2580 }
2581
2582 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2583 AtomicInfo* recordingyear_asset = APar_UserData_atom_Init("yrrc", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, 0);
2584 APar_Unified_atom_Put(recordingyear_asset, NULL, UTF8_3GP_Style, year_tag, 16);
2585 }
2586 break;
2587 }
2588
2589 case _3GP_Rating : {
2590 APar_ScanAtoms(ISObasemediafile);
2591 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "rating") ) {
2592 break;
2593 }
2594 char rating_entity[5] = { 'N', 'O', 'N', 'E', 0 }; //'NONE' - thats what it will be if not provided
2595 char rating_criteria[5] = { 'N', 'O', 'N', 'E', 0 };
2596 bool set_UTF16_text = false;
2597 uint16_t packed_lang = 0;
2598 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2599 uint8_t selected_track = 0;
2600 uint8_t a_track = 0;//unused
2601 uint8_t asset_iterations = 0;
2602
2603 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 5);
2604
2605 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)
2606 if ( argv[optind + i] && optind + i <= total_args) {
2607 if ( strncmp(argv[optind + i], "entity=", 7) == 0 ) {
2608 char* entity = argv[optind + i];
2609 strsep(&entity,"=");
2610 memcpy(&rating_entity, entity, 4);
2611 }
2612 if ( strncmp(argv[optind + i], "criteria=", 9) == 0 ) {
2613 char* criteria = argv[optind + i];
2614 strsep(&criteria,"=");
2615 memcpy(&rating_criteria, criteria, 4);
2616 }
2617 if (*argv[optind + i] == '-') break; //we've hit another cli argument
2618 }
2619 }
2620
2621 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2622 asset_iterations = 1;
2623 } else if (userdata_area == ALL_TRACKS_ATOM) {
2624 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2625 if (asset_iterations == 1) selected_track = 1;
2626 }
2627
2628 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2629 AtomicInfo* rating_asset = APar_UserData_atom_Init("rtng", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2630
2631 APar_Unified_atom_Put(rating_asset, NULL, UTF8_3GP_Style, UInt32FromBigEndian(rating_entity), 32);
2632 APar_Unified_atom_Put(rating_asset, NULL, UTF8_3GP_Style, UInt32FromBigEndian(rating_criteria), 32);
2633 APar_Unified_atom_Put(rating_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2634 }
2635 break;
2636 }
2637
2638 case _3GP_Classification : {
2639 APar_ScanAtoms(ISObasemediafile);
2640 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "classification") ) {
2641 break;
2642 }
2643 char classification_entity[5] = { 'N', 'O', 'N', 'E', 0 }; //'NONE' - thats what it will be if not provided
2644 uint16_t classification_index = 0;
2645 bool set_UTF16_text = false;
2646 uint16_t packed_lang = 0;
2647 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2648 uint8_t selected_track = 0;
2649 uint8_t a_track = 0;//unused
2650 uint8_t asset_iterations = 0;
2651
2652 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 5);
2653
2654 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)
2655 if ( argv[optind + i] && optind + i <= total_args) {
2656 if ( strncmp(argv[optind + i], "entity=", 7) == 0 ) {
2657 char* cls_entity = argv[optind + i];
2658 strsep(&cls_entity, "=");
2659 memcpy(&classification_entity, cls_entity, 4);
2660 }
2661 if ( strncmp(argv[optind + i], "index=", 6) == 0 ) {
2662 char* cls_idx = argv[optind + i];
2663 strsep(&cls_idx, "=");
2664 sscanf(cls_idx, "%" SCNu16, &classification_index);
2665 }
2666 if (*argv[optind + i] == '-') break; //we've hit another cli argument
2667 }
2668 }
2669
2670 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2671 asset_iterations = 1;
2672 } else if (userdata_area == ALL_TRACKS_ATOM) {
2673 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2674 if (asset_iterations == 1) selected_track = 1;
2675 }
2676
2677 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2678 AtomicInfo* classification_asset = APar_UserData_atom_Init("clsf", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2679
2680 APar_Unified_atom_Put(classification_asset, NULL, UTF8_3GP_Style, UInt32FromBigEndian(classification_entity), 32);
2681 APar_Unified_atom_Put(classification_asset, NULL, UTF8_3GP_Style, classification_index, 16);
2682 APar_Unified_atom_Put(classification_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2683 }
2684 break;
2685 }
2686
2687 case _3GP_Keyword : {
2688 APar_ScanAtoms(ISObasemediafile);
2689 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "keyword") ) {
2690 break;
2691 }
2692 bool set_UTF16_text = false;
2693 uint16_t packed_lang = 0;
2694 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2695 uint8_t selected_track = 0;
2696 uint8_t a_track = 0;//unused
2697 uint8_t asset_iterations = 0;
2698 char* formed_keyword_struct = NULL;
2699
2700 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 4);
2701
2702 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2703 asset_iterations = 1;
2704 } else if (userdata_area == ALL_TRACKS_ATOM) {
2705 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2706 if (asset_iterations == 1) selected_track = 1;
2707 }
2708
2709 if (strrchr(optarg, '=') != NULL) { //must be in the format of: keywords=foo1,foo2,foo3,foo4
2710 char* arg_keywords = optarg;
2711 char* keywords_globbed = strsep(&arg_keywords,"="); //separate out 'keyword='
2712 keywords_globbed = strsep(&arg_keywords,"="); //this is what we want to work on: just the keywords
2713 char* keyword_ptr = keywords_globbed;
2714 uint32_t keyword_strlen = strlen(keywords_globbed);
2715 uint8_t keyword_count = 0;
2716 uint32_t key_index = 0;
2717
2718 if (keyword_strlen > 0) { //if there is anything past the = then it counts as a keyword
2719 keyword_count++;
2720 }
2721
2722 while (true) { //count occurrences of comma here
2723 if (*keyword_ptr == ',') {
2724 keyword_count++;
2725 }
2726 keyword_ptr++;
2727 key_index++;
2728 if (keyword_strlen == key_index) {
2729 break;
2730 }
2731 }
2732
2733 formed_keyword_struct = (char*)calloc(1, sizeof(char)* set_UTF16_text ? (keyword_strlen * 4) : (keyword_strlen * 2)); // *4 should carry utf16's BOM & TERM
2734 uint32_t keyword_struct_bytes = APar_3GP_Keyword_atom_Format(keywords_globbed, keyword_count, set_UTF16_text, formed_keyword_struct);
2735
2736 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2737 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
2738 if (keyword_strlen > 0) {
2739 APar_Unified_atom_Put(keyword_asset, NULL, UTF8_3GP_Style, packed_lang, 16);
2740 APar_Unified_atom_Put(keyword_asset, NULL, UTF8_3GP_Style, keyword_count, 8);
2741 APar_atom_Binary_Put(keyword_asset, formed_keyword_struct, keyword_struct_bytes, 3);
2742 }
2743 }
2744 if (formed_keyword_struct != NULL) {
2745 free(formed_keyword_struct);
2746 formed_keyword_struct = NULL;
2747 }
2748 } else {
2749 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2750 APar_UserData_atom_Init("kywd", "", userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2751 }
2752 }
2753 break;
2754 }
2755
2756 case _3GP_Location : {
2757 APar_ScanAtoms(ISObasemediafile);
2758 if ( !APar_assert(metadata_style >= THIRD_GEN_PARTNER && metadata_style < MOTIONJPEG2000, 2, "location") ) {
2759 break;
2760 }
2761 bool set_UTF16_text = false;
2762 uint16_t packed_lang = 0;
2763 uint8_t userdata_area = MOVIE_LEVEL_ATOM;
2764 uint8_t selected_track = 0;
2765 uint8_t a_track = 0;//unused
2766 uint8_t asset_iterations = 0;
2767 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.
2768 double latitude = 40.77;
2769 double altitude = 4.3;
2770 uint8_t role = 0;
2771 const char* astronomical_body = "Earth";
2772 const char* additional_notes = "no notes";
2773
2774 find_optional_args(argv, optind, packed_lang, set_UTF16_text, userdata_area, selected_track, 10);
2775
2776 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)
2777 if ( argv[optind + i] && optind + i <= total_args) {
2778 if ( strncmp(argv[optind + i], "longitude=", 10) == 0 ) {
2779 char* _long = argv[optind + i];
2780 strsep(&_long,"=");
2781 sscanf(_long, "%lf", &longitude);
2782 if (_long[strlen(_long)-1] == 'W') {
2783 longitude*=-1;
2784 }
2785 }
2786 if ( strncmp(argv[optind + i], "latitude=", 9) == 0 ) {
2787 char* _latt = argv[optind + i];
2788 strsep(&_latt,"=");
2789 sscanf(_latt, "%lf", &latitude);
2790 if (_latt[strlen(_latt)-1] == 'S') {
2791 latitude*=-1;
2792 }
2793 }
2794 if ( strncmp(argv[optind + i], "altitude=", 9) == 0 ) {
2795 char* _alti = argv[optind + i];
2796 strsep(&_alti,"=");
2797 sscanf(_alti, "%lf", &altitude);
2798 if (_alti[strlen(_alti)-1] == 'B') {
2799 altitude*=-1;
2800 }
2801 }
2802 if ( strncmp(argv[optind + i], "role=", 5) == 0 ) {
2803 char* _role = argv[optind + i];
2804 strsep(&_role,"=");
2805 if (strcmp(_role, "shooting location") == 0 || strcmp(_role, "shooting") == 0) {
2806 role = 0;
2807 } else if (strcmp(_role, "real location") == 0 || strcmp(_role, "real") == 0) {
2808 role = 1;
2809 } else if (strcmp(_role, "fictional location") == 0 || strcmp(_role, "fictional") == 0) {
2810 role = 2;
2811 }
2812 }
2813 if ( strncmp(argv[optind + i], "body=", 5) == 0 ) {
2814 char* _astrobody = argv[optind + i];
2815 strsep(&_astrobody,"=");
2816 astronomical_body = _astrobody;
2817 }
2818 if ( strncmp(argv[optind + i], "notes=", 6) == 0 ) {
2819 char* _add_notes = argv[optind + i];
2820 strsep(&_add_notes,"=");
2821 additional_notes = _add_notes;
2822 }
2823 if (*argv[optind + i] == '-') break; //we've hit another cli argument
2824 }
2825 }
2826
2827 //fprintf(stdout, "long, lat, alt = %lf %lf %lf\n", longitude, latitude, altitude);
2828 if (userdata_area == MOVIE_LEVEL_ATOM || userdata_area == SINGLE_TRACK_ATOM) {
2829 asset_iterations = 1;
2830 } else if (userdata_area == ALL_TRACKS_ATOM) {
2831 APar_FindAtomInTrack(asset_iterations, a_track, NULL); //With asset_iterations set to 0, it will return the total trak atom into total_tracks here.
2832 if (asset_iterations == 1) selected_track = 1;
2833 }
2834
2835 if (longitude < -180.0 || longitude > 180.0 || latitude < -90.0 || latitude > 90.0) {
2836 fprintf(stdout, "AtomicParsley warning: longitude or latitude was invalid; skipping setting location\n");
2837 } else {
2838 for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
2839 //short location_3GP_atom = APar_UserData_atom_Init("moov.udta.loci", optarg, packed_lang);
2840 AtomicInfo* location_asset = APar_UserData_atom_Init("loci", optarg, userdata_area, asset_iterations == 1 ? selected_track : i_asset, packed_lang);
2841 APar_Unified_atom_Put(location_asset, optarg, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), packed_lang, 16);
2842 APar_Unified_atom_Put(location_asset, NULL, false, role, 8);
2843
2844 APar_Unified_atom_Put(location_asset, NULL, false, float_to_16x16bit_fixed_point(longitude), 32);
2845 APar_Unified_atom_Put(location_asset, NULL, false, float_to_16x16bit_fixed_point(latitude), 32);
2846 APar_Unified_atom_Put(location_asset, NULL, false, float_to_16x16bit_fixed_point(altitude), 32);
2847 APar_Unified_atom_Put(location_asset, astronomical_body, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), 0, 0);
2848 APar_Unified_atom_Put(location_asset, additional_notes, (set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style), 0, 0);
2849 }
2850 }
2851 break;
2852 }
2853
2854 case Meta_ReverseDNS_Form : { //--rDNSatom "mv-ma" name=iTuneEXTC domain=com.apple.iTunes
2855 char* reverseDNS_atomname = NULL;
2856 char* reverseDNS_atomdomain = NULL;
2857 uint32_t rdns_atom_flags = AtomFlags_Data_Text;
2858
2859 APar_ScanAtoms(ISObasemediafile);
2860
2861 if ( !APar_assert(metadata_style == ITUNES_STYLE, 1, "reverse DNS form") ) {
2862 break;
2863 }
2864
2865 for (int i= 0; i <= 5-1; i++) {
2866 if ( argv[optind + i] && optind + i <= argc ) {
2867 if ( strncmp(argv[optind + i], "name=", 5) == 0 ) {
2868 reverseDNS_atomname = argv[optind + i]+5;
2869 } else if ( strncmp(argv[optind + i], "domain=", 7) == 0 ) {
2870 reverseDNS_atomdomain = argv[optind + i]+7;
2871 } else if ( strncmp(argv[optind + i], "datatype=", 9) == 0 ) {
2872 sscanf(argv[optind + i]+9, "%" SCNu32, &rdns_atom_flags);
2873 }
2874 if (*argv[optind + i] == '-') {
2875 break; //we've hit another cli argument
2876 }
2877 }
2878 }
2879
2880 if (reverseDNS_atomname == NULL) {
2881 fprintf(stdout, "AtomicParsley warning: no name for the reverseDNS form was found. Skipping.\n");
2882
2883 } else if ((int)strlen(reverseDNS_atomname) != test_conforming_alpha_string(reverseDNS_atomname) ) {
2884 fprintf(stdout, "AtomicParsley warning: Some part of the reverseDNS atom name was non-conforming. Skipping.\n");
2885
2886 } else if (reverseDNS_atomdomain == NULL) {
2887 fprintf(stdout, "AtomicParsley warning: no domain for the reverseDNS form was found. Skipping.\n");
2888
2889 } else if (rdns_atom_flags != AtomFlags_Data_Text) {
2890 fprintf(stdout, "AtomicParsley warning: currently, only the strings are supported in reverseDNS atoms. Skipping.\n");
2891
2892 } else {
2893 AtomicInfo* rDNS_data_atom = APar_reverseDNS_atom_Init(reverseDNS_atomname, optarg, &rdns_atom_flags, reverseDNS_atomdomain);
2894 APar_Unified_atom_Put(rDNS_data_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
2895 }
2896
2897 break;
2898 }
2899
2900 case Meta_rDNS_rating : {
2901 const char* media_rating = Expand_cli_mediastring(optarg);
2902 uint32_t rDNS_data_flags = AtomFlags_Data_Text;
2903
2904 APar_ScanAtoms(ISObasemediafile);
2905
2906 if (media_rating != NULL || strlen(optarg) == 0) {
2907 AtomicInfo* rDNS_rating_data_atom = APar_reverseDNS_atom_Init("iTunEXTC", media_rating, &rDNS_data_flags, "com.apple.iTunes");
2908 APar_Unified_atom_Put(rDNS_rating_data_atom, media_rating, UTF8_iTunesStyle_Unlimited, 0, 0);
2909 }
2910
2911 break;
2912 }
2913
2914 case Meta_ID3v2Tag : {
2915 const char* target_frame_ID = NULL;
2916 uint16_t packed_lang = 0;
2917 uint8_t char_encoding = TE_UTF8; //utf8 is the default encoding
2918 char meta_container = 0-MOVIE_LEVEL_ATOM;
2919 bool multistring = false;
2920 APar_ScanAtoms(ISObasemediafile);
2921
2922 //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.
2923 //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).
2924 //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.
2925 if ( !APar_assert(parsedAtoms[0].ancillary_data != 0 || (metadata_style >= THIRD_GEN_PARTNER_VER1_REL7 && metadata_style < MOTIONJPEG2000), 3, NULL) ) {
2926 break;
2927 }
2928 AdjunctArgs* id3args = (AdjunctArgs*)malloc(sizeof(AdjunctArgs));
2929
2930 id3args->targetLang = NULL; //it will default later to "eng"
2931 id3args->descripArg = NULL;
2932 id3args->mimeArg = NULL;
2933 id3args->pictypeArg = NULL;
2934 id3args->ratingArg = NULL;
2935 id3args->dataArg = NULL;
2936 id3args->pictype_uint8 = 0;
2937 id3args->groupSymbol = 0;
2938 id3args->zlibCompressed = false;
2939 id3args->multistringtext = false;
2940
2941 target_frame_ID = ConvertCLIFrameStr_TO_frameID(optarg);
2942 if (target_frame_ID == NULL) {
2943 target_frame_ID = optarg;
2944 }
2945
2946 int frameType = FrameStr_TO_FrameType(target_frame_ID);
2947 if (frameType >= 0) {
2948 if (TestCLI_for_FrameParams(frameType, 0)) {
2949 id3args->descripArg = find_ID3_optarg(argv, optind, "desc=");
2950 }
2951 if (TestCLI_for_FrameParams(frameType, 1)) {
2952 id3args->mimeArg = find_ID3_optarg(argv, optind, "mimetype=");
2953 }
2954 if (TestCLI_for_FrameParams(frameType, 2)) {
2955 id3args->pictypeArg = find_ID3_optarg(argv, optind, "imagetype=");
2956 }
2957 if (TestCLI_for_FrameParams(frameType, 3)) {
2958 id3args->dataArg = find_ID3_optarg(argv, optind, "uniqueID=");
2959 }
2960 if (TestCLI_for_FrameParams(frameType, 4)) {
2961 id3args->filenameArg = find_ID3_optarg(argv, optind, "filename=");
2962 }
2963 if (TestCLI_for_FrameParams(frameType, 5)) {
2964 id3args->ratingArg = find_ID3_optarg(argv, optind, "rating=");
2965 }
2966 if (TestCLI_for_FrameParams(frameType, 6)) {
2967 id3args->dataArg = find_ID3_optarg(argv, optind, "counter=");
2968 }
2969 if (TestCLI_for_FrameParams(frameType, 7)) {
2970 id3args->dataArg = find_ID3_optarg(argv, optind, "data=");
2971 }
2972 if (TestCLI_for_FrameParams(frameType, 8)) {
2973 id3args->dataArg = find_ID3_optarg(argv, optind, "data=");
2974 }
2975 if (*find_ID3_optarg(argv, optind, "compressed") == '1') {
2976 id3args->zlibCompressed = true;
2977 }
2978
2979 const char* groupsymbol = find_ID3_optarg(argv, optind, "groupsymbol=");
2980 if (groupsymbol[0] == '0' && groupsymbol[1] == 'x') {
2981 id3args->groupSymbol = strtoul(groupsymbol, NULL, 16);
2982 if (id3args->groupSymbol < 0x80 || id3args->groupSymbol > 0xF0) id3args->groupSymbol = 0;
2983 }
2984 }
2985
2986 scan_ID3_optargs(argv, optind, id3args->targetLang, packed_lang, char_encoding, &meta_container, multistring);
2987 if (id3args->targetLang == NULL) id3args->targetLang = "eng";
2988
2989 APar_OpenISOBaseMediaFile(ISObasemediafile, true); //if not already scanned, the whole tag for *this* ID32 atom needs to be read from file
2990 AtomicInfo* id32_atom = APar_ID32_atom_Init(target_frame_ID, meta_container, id3args->targetLang, packed_lang);
2991
2992 if (strcmp(argv[optind + 0], "extract") == 0 && (strcmp(target_frame_ID, "APIC") == 0 || strcmp(target_frame_ID, "GEOB") == 0)) {
2993 if (id32_atom != NULL) {
2994 APar_Extract_ID3v2_file(id32_atom, target_frame_ID, ISObasemediafile, NULL, id3args);
2995 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
2996 }
2997 exit(0);
2998 }
2999
3000 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
3001 APar_ID3FrameAmmend(id32_atom, target_frame_ID, argv[optind + 0], id3args, char_encoding);
3002
3003 free(id3args);
3004 id3args = NULL;
3005
3006 break;
3007 }
3008
3009 //utility functions
3010
3011 case Metadata_Purge : {
3012 APar_ScanAtoms(ISObasemediafile);
3013 APar_RemoveAtom("moov.udta.meta.ilst", SIMPLE_ATOM, 0);
3014
3015 break;
3016 }
3017
3018 case UserData_Purge : {
3019 APar_ScanAtoms(ISObasemediafile);
3020 APar_RemoveAtom("moov.udta", SIMPLE_ATOM, 0);
3021
3022 break;
3023 }
3024
3025 case foobar_purge : {
3026 APar_ScanAtoms(ISObasemediafile);
3027 APar_RemoveAtom("moov.udta.tags", UNKNOWN_ATOM, 0);
3028
3029 break;
3030 }
3031
3032 case Opt_FreeFree : {
3033 APar_ScanAtoms(ISObasemediafile);
3034 int free_level = -1;
3035 if (argv[optind]) {
3036 sscanf(argv[optind], "%i", &free_level);
3037 }
3038 APar_freefree(free_level);
3039
3040 break;
3041 }
3042
3043 case OPT_OverWrite : {
3044 alter_original = true;
3045 break;
3046 }
3047
3048 #if defined (_WIN32)
3049 case OPT_PreserveTimeStamps : {
3050 alter_original = true;
3051 preserve_timestamps = true;
3052 break;
3053 }
3054 #endif
3055
3056 case Meta_dump : {
3057 APar_ScanAtoms(ISObasemediafile);
3058 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
3059 APar_MetadataFileDump(ISObasemediafile);
3060 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
3061
3062 APar_FreeMemory();
3063 exit(0);
3064 }
3065
3066 case OPT_NoOptimize : {
3067 force_existing_hierarchy = true;
3068 break;
3069 }
3070
3071 case OPT_OutputFile : {
3072 output_file = optarg;
3073 break;
3074 }
3075
3076 } /* end switch */
3077 } /* end while */
3078
3079 // after all the modifications are enacted on the tree in memory, THEN
3080 // write out the changes
3081
3082 if (modified_atoms) {
3083
3084 #if defined (_WIN32)
3085 HANDLE hFile, hFileOut;
3086 FILETIME createTime, accessTime, writeTime;
3087 if (preserve_timestamps == true)
3088 {
3089 hFile = APar_OpenFileWin32(ISObasemediafile, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
3090 if (hFile != INVALID_HANDLE_VALUE)
3091 {
3092 GetFileTime(hFile,&createTime,&accessTime,&writeTime);
3093 CloseHandle(hFile);
3094 }
3095 else
3096 {
3097 fprintf (stdout,"\n Invalid HANDLE!");
3098 }
3099 }
3100 #endif
3101
3102 APar_DetermineAtomLengths();
3103 APar_OpenISOBaseMediaFile(ISObasemediafile, true);
3104 APar_WriteFile(ISObasemediafile, output_file, alter_original);
3105
3106 #if defined (_WIN32)
3107 if (preserve_timestamps == true)
3108 {
3109
3110 hFileOut = APar_OpenFileWin32(ISObasemediafile, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
3111
3112 if (hFileOut != INVALID_HANDLE_VALUE)
3113 {
3114 SetFileTime(hFileOut,&createTime,&accessTime,&writeTime);
3115 CloseHandle(hFileOut);
3116 }
3117 }
3118 #endif
3119
3120
3121 if (!alter_original) {
3122 // The file was opened orignally as read-only; when it came time to
3123 // writeback into the original file, that FILE was closed, and a
3124 // new one opened with write abilities, so to close a FILE that no
3125 // longer exists would.... be retarded.
3126 APar_OpenISOBaseMediaFile(ISObasemediafile, false);
3127 }
3128 } else {
3129 if (ISObasemediafile != NULL && argc > 3 && !deep_atom_scan) {
3130 fprintf(stdout, "No changes.\n");
3131 }
3132 }
3133 APar_FreeMemory();
3134 return 0;
3135 }
3136
3137 #if defined (_WIN32)
3138
3139 #if !defined (__CYGWIN__)
3140
3141 int wmain( int argc, wchar_t *arguments[])
3142 {
3143 int return_val=0;
3144 uint16_t name_len = wcslen(arguments[0]);
3145 if (name_len >= 9 && _wcsicmp(arguments[0] + (name_len-9), L"-utf8.exe") == 0) {
3146 UnicodeOutputStatus = UNIVERSAL_UTF8;
3147 } else {
3148 UnicodeOutputStatus = WIN32_UTF16;
3149 }
3150
3151 char **argv = (char**)calloc(argc + 1, sizeof(char*));
3152
3153 // for native Win32 & full unicode support (well, cli arguments anyway),
3154 // take whatever 16bit unicode windows gives (utf16le), and convert
3155 // EVERYTHING that is sends to utf8; use those utf8 strings (mercifully
3156 // subject to familiar standby's like strlen) to pass around the program
3157 // like getopt_long to get cli options; convert from utf8 filenames as
3158 // required for unicode filename support on Windows using wide file
3159 // functions.
3160 for(int z=0; z < argc; z++) {
3161 uint32_t wchar_length = wcslen(arguments[z])+1;
3162 argv[z] = (char *)malloc(sizeof(char)*8*wchar_length );
3163 memset(argv[z], 0, 8*wchar_length);
3164 if (UnicodeOutputStatus == WIN32_UTF16) {
3165 UTF16LEToUTF8((unsigned char*) argv[z], 8*wchar_length, (unsigned char*) arguments[z], wchar_length*2);
3166 } else {
3167 strip_bogusUTF16toRawUTF8((unsigned char*) argv[z], 8*wchar_length, arguments[z], wchar_length );
3168 }
3169 }
3170
3171 argv[argc] = NULL;
3172
3173 return_val = real_main(argc, argv);
3174
3175 for (int free_cnt=0; free_cnt<argc; free_cnt++) {
3176 free(argv[free_cnt]);
3177 argv[free_cnt]=NULL;
3178 }
3179
3180 free(argv);
3181 argv=NULL;
3182
3183 return return_val;
3184 }
3185
3186 #ifdef __MINGW32__
3187
3188 int main()
3189 {
3190 int argc;
3191 wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc);
3192 return wmain(argc, argv);
3193 }
3194
3195 #endif
3196
3197 #else // defined __CYGWIN__
3198
3199 int main( int argc, char *argv[])
3200 {
3201 size_t name_len = strlen(argv[0]);
3202 if (name_len >= 5 &&
3203 (strcmp(argv[0] + (name_len-5), "-utf8") == 0 ||
3204 strcmp(argv[0] + (name_len-5), "-UTF8") == 0)) {
3205 UnicodeOutputStatus = UNIVERSAL_UTF8;
3206 } else {
3207 UnicodeOutputStatus = WIN32_UTF16;
3208 }
3209 return real_main(argc, argv);
3210 }
3211
3212 #endif
32134473
32144474 #else
32154475
3216 int main( int argc, char *argv[])
3217 {
3218 return real_main(argc, argv);
3219 }
4476 int main(int argc, char *argv[]) { return real_main(argc, argv); }
32204477
32214478 #endif
32224479
11 /*
22 AtomicParsley - metalist.cpp
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2006-2007 puck_lock
17 Copyright (C) 2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
1919 */
2020 //==================================================================//
2323
2424 bool BOM_printed = false;
2525
26 uint32_t APar_ProvideTallyForAtom(const char* atom_name) {
27 uint32_t tally_for_atom = 0;
28 short iter = parsedAtoms[0].NextAtomNumber;
29 while (true) {
30 if (memcmp(parsedAtoms[iter].AtomicName, atom_name, 4) == 0) {
31 if (parsedAtoms[iter].AtomicLength == 0) {
32 tally_for_atom += file_size - parsedAtoms[iter].AtomicStart;
33 } else if (parsedAtoms[iter].AtomicLength == 1) {
34 tally_for_atom += parsedAtoms[iter].AtomicLengthExtended;
35 } else {
36 tally_for_atom += parsedAtoms[iter].AtomicLength;
37 }
38 }
39 if (iter == 0) {
40 break;
41 } else {
42 iter=parsedAtoms[iter].NextAtomNumber;
43 }
44 }
45 return tally_for_atom;
26 uint32_t APar_ProvideTallyForAtom(const char *atom_name) {
27 uint32_t tally_for_atom = 0;
28 short iter = parsedAtoms[0].NextAtomNumber;
29 while (true) {
30 if (memcmp(parsedAtoms[iter].AtomicName, atom_name, 4) == 0) {
31 if (parsedAtoms[iter].AtomicLength == 0) {
32 tally_for_atom += file_size - parsedAtoms[iter].AtomicStart;
33 } else if (parsedAtoms[iter].AtomicLength == 1) {
34 tally_for_atom += parsedAtoms[iter].AtomicLengthExtended;
35 } else {
36 tally_for_atom += parsedAtoms[iter].AtomicLength;
37 }
38 }
39 if (iter == 0) {
40 break;
41 } else {
42 iter = parsedAtoms[iter].NextAtomNumber;
43 }
44 }
45 return tally_for_atom;
4646 }
4747
4848 void printBOM() {
49 if (BOM_printed) return;
50
51 #if defined (_WIN32) && !defined (__CYGWIN__)
52 if (UnicodeOutputStatus == WIN32_UTF16) {
53 APar_unicode_win32Printout(L"\xEF\xBB\xBF", "\xEF\xBB\xBF");
54 }
49 if (BOM_printed)
50 return;
51
52 #if defined(_WIN32) && !defined(__CYGWIN__)
53 if (UnicodeOutputStatus == WIN32_UTF16) {
54 APar_unicode_win32Printout(L"\xEF\xBB\xBF", "\xEF\xBB\xBF");
55 }
5556 #else
56 fprintf(stdout, "\xEF\xBB\xBF"); //Default to output of a UTF-8 BOM
57 fprintf(stdout, "\xEF\xBB\xBF"); // Default to output of a UTF-8 BOM
5758 #endif
5859
59 BOM_printed = true;
60 return;
61 }
62
63 #if defined (_WIN32) && !defined (__CYGWIN__)
64 void APar_unicode_win32Printout(wchar_t* unicode_out, char* utf8_out) { //based on http://blogs.msdn.com/junfeng/archive/2004/02/25/79621.aspx
65 //its possible that this isn't even available on windows95
66 DWORD dwBytesWritten;
67 DWORD fdwMode;
68 HANDLE outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
69 // ThreadLocale adjustment, resource loading, etc. is skipped
70 if ( (GetFileType(outHandle) & FILE_TYPE_CHAR) && GetConsoleMode( outHandle, &fdwMode) ) {
71 if ( wcsncmp(unicode_out, L"\xEF\xBB\xBF", 3) != 0 ) { //skip BOM when writing directly to the console
72 WriteConsoleW( outHandle, unicode_out, wcslen(unicode_out), &dwBytesWritten, 0);
73 }
74 } else {
75 //writing out to a file. Everything will be written out in utf8 to the file.
76 fprintf(stdout, "%s", utf8_out);
77 }
78 return;
60 BOM_printed = true;
61 return;
62 }
63
64 #if defined(_WIN32) && !defined(__CYGWIN__)
65 void APar_unicode_win32Printout(
66 wchar_t *unicode_out,
67 char *
68 utf8_out) { // based on
69 // http://blogs.msdn.com/junfeng/archive/2004/02/25/79621.aspx
70 // its possible that this isn't even available on windows95
71 DWORD dwBytesWritten;
72 DWORD fdwMode;
73 HANDLE outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
74 // ThreadLocale adjustment, resource loading, etc. is skipped
75 if ((GetFileType(outHandle) & FILE_TYPE_CHAR) &&
76 GetConsoleMode(outHandle, &fdwMode)) {
77 if (wcsncmp(unicode_out, L"\xEF\xBB\xBF", 3) !=
78 0) { // skip BOM when writing directly to the console
79 WriteConsoleW(
80 outHandle, unicode_out, wcslen(unicode_out), &dwBytesWritten, 0);
81 }
82 } else {
83 // writing out to a file. Everything will be written out in utf8 to the
84 // file.
85 fprintf(stdout, "%s", utf8_out);
86 }
87 return;
7988 }
8089 #endif
8190
82 void APar_fprintf_UTF8_data(const char* utf8_encoded_data) {
83 #if defined (_WIN32) && !defined (__CYGWIN__)
84 if (GetVersion() & 0x80000000 || UnicodeOutputStatus == UNIVERSAL_UTF8) {
85 fprintf(stdout, "%s", utf8_encoded_data); //just printout the raw utf8 bytes (not characters) under pre-NT windows
86 } else {
87 wchar_t* utf16_data = Convert_multibyteUTF8_to_wchar(utf8_encoded_data);
88 fflush(stdout);
89
90 APar_unicode_win32Printout(utf16_data, (char *) utf8_encoded_data);
91
92 fflush(stdout);
93 free(utf16_data);
94 utf16_data = NULL;
95 }
91 void APar_fprintf_UTF8_data(const char *utf8_encoded_data) {
92 #if defined(_WIN32) && !defined(__CYGWIN__)
93 if (GetVersion() & 0x80000000 || UnicodeOutputStatus == UNIVERSAL_UTF8) {
94 fprintf(stdout,
95 "%s",
96 utf8_encoded_data); // just printout the raw utf8 bytes (not
97 // characters) under pre-NT windows
98 } else {
99 wchar_t *utf16_data = Convert_multibyteUTF8_to_wchar(utf8_encoded_data);
100 fflush(stdout);
101
102 APar_unicode_win32Printout(utf16_data, (char *)utf8_encoded_data);
103
104 fflush(stdout);
105 free(utf16_data);
106 utf16_data = NULL;
107 }
96108 #else
97 fprintf(stdout, "%s", utf8_encoded_data);
109 fprintf(stdout, "%s", utf8_encoded_data);
98110 #endif
99 return;
100 }
101
102 void APar_Mark_UserData_area(uint8_t track_num, short userdata_atom, bool quantum_listing) {
103 if (quantum_listing && track_num > 0) {
104 fprintf(stdout, "User data; level: track=%u; atom \"%s\" ", track_num, parsedAtoms[userdata_atom].AtomicName);
105 } else if (quantum_listing && track_num == 0) {
106 fprintf(stdout, "User data; level: movie; atom \"%s\" ", parsedAtoms[userdata_atom].AtomicName);
107 } else {
108 fprintf(stdout, "User data \"%s\" ", parsedAtoms[userdata_atom].AtomicName);
109 }
110 return;
111 }
112
113 //the difference between APar_PrintUnicodeAssest above and APar_SimplePrintUnicodeAssest below is:
114 //APar_PrintUnicodeAssest contains the entire contents of the atom, NULL bytes and all
115 //APar_SimplePrintUnicodeAssest contains a purely unicode string (either utf8 or utf16 with BOM)
116 //and slight output formatting differences
117
118 void APar_SimplePrintUnicodeAssest(char* unicode_string, int asset_length, bool print_encoding) { //3gp files
119 if (strncmp(unicode_string, "\xFE\xFF", 2) == 0 ) { //utf16
120 if (print_encoding) {
121 fprintf(stdout, " (utf16): ");
122 }
123 unsigned char* utf8_data = Convert_multibyteUTF16_to_UTF8(unicode_string, asset_length * 6, asset_length);
124
125 #if defined (_WIN32) && !defined (__CYGWIN__)
126 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)
127 unsigned char* utf8_data = Convert_multibyteUTF16_to_UTF8(unicode_string, asset_length * 6, asset_length-14);
128 fprintf(stdout, "%s", utf8_data);
129
130 free(utf8_data);
131 utf8_data = NULL;
132
133 } else {
134 wchar_t* utf16_data = Convert_multibyteUTF16_to_wchar(unicode_string, asset_length / 2, true);
135 //wchar_t* utf16_data = Convert_multibyteUTF16_to_wchar(unicode_string, (asset_length / 2) + 1, true);
136 APar_unicode_win32Printout(utf16_data, (char*)utf8_data);
137
138 free(utf16_data);
139 utf16_data = NULL;
140 }
111 return;
112 }
113
114 void APar_Mark_UserData_area(uint8_t track_num,
115 short userdata_atom,
116 bool quantum_listing) {
117 if (quantum_listing && track_num > 0) {
118 fprintf(stdout,
119 "User data; level: track=%u; atom \"%s\" ",
120 track_num,
121 parsedAtoms[userdata_atom].AtomicName);
122 } else if (quantum_listing && track_num == 0) {
123 fprintf(stdout,
124 "User data; level: movie; atom \"%s\" ",
125 parsedAtoms[userdata_atom].AtomicName);
126 } else {
127 fprintf(stdout, "User data \"%s\" ", parsedAtoms[userdata_atom].AtomicName);
128 }
129 return;
130 }
131
132 // the difference between APar_PrintUnicodeAssest above and
133 // APar_SimplePrintUnicodeAssest below is: APar_PrintUnicodeAssest contains the
134 // entire contents of the atom, NULL bytes and all APar_SimplePrintUnicodeAssest
135 // contains a purely unicode string (either utf8 or utf16 with BOM) and slight
136 // output formatting differences
137
138 void APar_SimplePrintUnicodeAssest(char *unicode_string,
139 int asset_length,
140 bool print_encoding) { // 3gp files
141 if (strncmp(unicode_string, "\xFE\xFF", 2) == 0) { // utf16
142 if (print_encoding) {
143 fprintf(stdout, " (utf16): ");
144 }
145 unsigned char *utf8_data = Convert_multibyteUTF16_to_UTF8(
146 unicode_string, asset_length * 6, asset_length);
147
148 #if defined(_WIN32) && !defined(__CYGWIN__)
149 if (GetVersion() & 0x80000000 ||
150 UnicodeOutputStatus ==
151 UNIVERSAL_UTF8) { // pre-NT or AP-utf8.exe (pish, thats my win98se,
152 // and without unicows support convert utf16toutf8
153 // and output raw bytes)
154 unsigned char *utf8_data = Convert_multibyteUTF16_to_UTF8(
155 unicode_string, asset_length * 6, asset_length - 14);
156 fprintf(stdout, "%s", utf8_data);
157
158 free(utf8_data);
159 utf8_data = NULL;
160
161 } else {
162 wchar_t *utf16_data = Convert_multibyteUTF16_to_wchar(
163 unicode_string, asset_length / 2, true);
164 // wchar_t* utf16_data = Convert_multibyteUTF16_to_wchar(unicode_string,
165 // (asset_length / 2) + 1, true);
166 APar_unicode_win32Printout(utf16_data, (char *)utf8_data);
167
168 free(utf16_data);
169 utf16_data = NULL;
170 }
141171 #else
142 fprintf(stdout, "%s", utf8_data);
172 fprintf(stdout, "%s", utf8_data);
143173 #endif
144
145 free(utf8_data);
146 utf8_data = NULL;
147
148 } else { //utf8
149 if (print_encoding) {
150 fprintf(stdout, " (utf8): ");
151 }
152
153 APar_fprintf_UTF8_data(unicode_string);
154
155 }
156 return;
174
175 free(utf8_data);
176 utf8_data = NULL;
177
178 } else { // utf8
179 if (print_encoding) {
180 fprintf(stdout, " (utf8): ");
181 }
182
183 APar_fprintf_UTF8_data(unicode_string);
184 }
185 return;
157186 }
158187
159188 ///////////////////////////////////////////////////////////////////////////////////////
160 // embedded file extraction //
189 // embedded file extraction //
161190 ///////////////////////////////////////////////////////////////////////////////////////
162191
163192 /*----------------------
164193 APar_Extract_uuid_binary_file
165 uuid_atom - pointer to the struct holding the information describing the target atom
166 originating_file - the full file path string to the parsed file
167 output_path - a (possibly null) string where the embedded file will be extracted to
168
169 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
170 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
171 the file to the now fully formed uuid_outfile path.
194 uuid_atom - pointer to the struct holding the information describing the
195 target atom originating_file - the full file path string to the parsed file
196 output_path - a (possibly null) string where the embedded file will be
197 extracted to
198
199 If the output path is a null pointer, create a new path derived from
200 originating file name & path - strip off the extension and use that as the base.
201 Read into memory the contents of that particular uuid atom. Glob onto the base
202 path the atom name and then the suffix that was embedded along with the file.
203 Write out the file to the now fully formed uuid_outfile path.
172204 ----------------------*/
173 void APar_Extract_uuid_binary_file(AtomicInfo* uuid_atom, const char* originating_file, char* output_path) {
174 uint32_t path_len = 0;
175 uint64_t atom_offsets = 0;
176 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
177 if (output_path == NULL) {
178 const char* orig_suffix = strrchr(originating_file, '.');
179 if (orig_suffix == NULL) {
180 fprintf(stdout, "AP warning: a file extension for the input file was not found.\n\tGlobbing onto original filename...\n");
181 path_len = strlen(originating_file);
182 memcpy(uuid_outfile, originating_file, path_len);
183 } else {
184 path_len = orig_suffix-originating_file;
185 memcpy(uuid_outfile, originating_file, path_len);
186 }
187
188 } else {
189 path_len = strlen(output_path);
190 memcpy(uuid_outfile, output_path, path_len);
191 }
192 char* uuid_payload = (char*)calloc(1, sizeof(char) * (uuid_atom->AtomicLength - 36 +1) );
193
194 APar_readX(uuid_payload, source_file, uuid_atom->AtomicStart + 36, uuid_atom->AtomicLength - 36);
195
196 uint32_t descrip_len = UInt32FromBigEndian(uuid_payload);
197 atom_offsets+=4+descrip_len;
198 uint8_t suffix_len = (uint8_t)uuid_payload[atom_offsets];
199
200 char* file_suffix = (char*)calloc(1, sizeof(char) * suffix_len+16 );
201 memcpy(file_suffix, uuid_payload+atom_offsets+1, suffix_len);
202 atom_offsets+=1+suffix_len;
203
204 uint8_t mime_len = (uint8_t)uuid_payload[atom_offsets];
205 uint64_t mimetype_string = atom_offsets+1;
206 atom_offsets+=1+mime_len;
207 uint64_t bin_len = UInt32FromBigEndian(uuid_payload+atom_offsets);
208 atom_offsets+=4;
209
210 sprintf(uuid_outfile+path_len, "-%s-uuid%s", uuid_atom->uuid_ap_atomname, file_suffix);
211
212 FILE *outfile = APar_OpenFile(uuid_outfile, "wb");
213 if (outfile != NULL) {
214 fwrite(uuid_payload+atom_offsets, (size_t)bin_len, 1, outfile);
215 fclose(outfile);
216 fprintf(stdout, "Extracted uuid=%s attachment (mime-type=%s) to file: ", uuid_atom->uuid_ap_atomname, uuid_payload+mimetype_string);
217 APar_fprintf_UTF8_data(uuid_outfile);
218 fprintf(stdout, "\n");
219 }
220
221 free(uuid_payload); uuid_payload = NULL;
222 free(uuid_outfile); uuid_outfile = NULL;
223 free(file_suffix); file_suffix = NULL;
224 return;
225 }
226
227 void APar_ExtractAAC_Artwork(short this_atom_num, char* pic_output_path, short artwork_count) {
228 char *base_outpath=(char *)malloc(sizeof(char)*MAXPATHLEN+1);
229
230 if (snprintf(base_outpath, MAXPATHLEN+1, "%s_artwork_%d", pic_output_path, artwork_count) > MAXPATHLEN) {
231 free(base_outpath);
232 return;
233 }
234
235 char* art_payload = (char*)malloc( sizeof(char) * (parsedAtoms[this_atom_num].AtomicLength-16) +1 );
236 memset(art_payload, 0, (parsedAtoms[this_atom_num].AtomicLength-16) +1 );
237
238 APar_readX(art_payload, source_file, parsedAtoms[this_atom_num].AtomicStart+16, parsedAtoms[this_atom_num].AtomicLength-16);
239
240 char* suffix = (char *)malloc(sizeof(char)*5);
241 memset(suffix, 0, sizeof(char)*5);
242
243 if (memcmp(art_payload, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) {
244 strcpy(suffix, ".png");
245 } else if (memcmp(art_payload, "\xFF\xD8\xFF\xE0", 4) == 0 || memcmp(art_payload, "\xFF\xD8\xFF\xE1", 4) == 0) {
246 strcpy(suffix, ".jpg");
247 }
248
249 strcat(base_outpath, suffix);
250
251 FILE *outfile = APar_OpenFile(base_outpath, "wb");
252 if (outfile != NULL) {
253 fwrite(art_payload, (size_t)(parsedAtoms[this_atom_num].AtomicLength-16), 1, outfile);
254 fclose(outfile);
255 fprintf(stdout, "Extracted artwork to file: ");
256 APar_fprintf_UTF8_data(base_outpath);
257 fprintf(stdout, "\n");
258 }
259 free(base_outpath);
260 free(art_payload);
261 free(suffix);
205 void APar_Extract_uuid_binary_file(AtomicInfo *uuid_atom,
206 const char *originating_file,
207 char *output_path) {
208 uint32_t path_len = 0;
209 uint64_t atom_offsets = 0;
210 char *uuid_outfile = (char *)calloc(
211 1,
212 sizeof(char) * MAXPATHLEN + 1); // malloc a new string because it may be a
213 // cli arg for a specific output path
214 if (output_path == NULL) {
215 const char *orig_suffix = strrchr(originating_file, '.');
216 if (orig_suffix == NULL) {
217 fprintf(stdout,
218 "AP warning: a file extension for the input file was not "
219 "found.\n\tGlobbing onto original filename...\n");
220 path_len = strlen(originating_file);
221 memcpy(uuid_outfile, originating_file, path_len);
222 } else {
223 path_len = orig_suffix - originating_file;
224 memcpy(uuid_outfile, originating_file, path_len);
225 }
226
227 } else {
228 path_len = strlen(output_path);
229 memcpy(uuid_outfile, output_path, path_len);
230 }
231 char *uuid_payload =
232 (char *)calloc(1, sizeof(char) * (uuid_atom->AtomicLength - 36 + 1));
233
234 APar_readX(uuid_payload,
235 source_file,
236 uuid_atom->AtomicStart + 36,
237 uuid_atom->AtomicLength - 36);
238
239 uint32_t descrip_len = UInt32FromBigEndian(uuid_payload);
240 atom_offsets += 4 + descrip_len;
241 uint8_t suffix_len = (uint8_t)uuid_payload[atom_offsets];
242
243 char *file_suffix = (char *)calloc(1, sizeof(char) * suffix_len + 16);
244 memcpy(file_suffix, uuid_payload + atom_offsets + 1, suffix_len);
245 atom_offsets += 1 + suffix_len;
246
247 uint8_t mime_len = (uint8_t)uuid_payload[atom_offsets];
248 uint64_t mimetype_string = atom_offsets + 1;
249 atom_offsets += 1 + mime_len;
250 uint64_t bin_len = UInt32FromBigEndian(uuid_payload + atom_offsets);
251 atom_offsets += 4;
252
253 sprintf(uuid_outfile + path_len,
254 "-%s-uuid%s",
255 uuid_atom->uuid_ap_atomname,
256 file_suffix);
257
258 FILE *outfile = APar_OpenFile(uuid_outfile, "wb");
259 if (outfile != NULL) {
260 fwrite(uuid_payload + atom_offsets, (size_t)bin_len, 1, outfile);
261 fclose(outfile);
262 fprintf(stdout,
263 "Extracted uuid=%s attachment (mime-type=%s) to file: ",
264 uuid_atom->uuid_ap_atomname,
265 uuid_payload + mimetype_string);
266 APar_fprintf_UTF8_data(uuid_outfile);
267 fprintf(stdout, "\n");
268 }
269
270 free(uuid_payload);
271 uuid_payload = NULL;
272 free(uuid_outfile);
273 uuid_outfile = NULL;
274 free(file_suffix);
275 file_suffix = NULL;
276 return;
277 }
278
279 void APar_ExtractAAC_Artwork(short this_atom_num,
280 char *pic_output_path,
281 short artwork_count) {
282 char *base_outpath = (char *)malloc(sizeof(char) * MAXPATHLEN + 1);
283
284 if (snprintf(base_outpath,
285 MAXPATHLEN + 1,
286 "%s_artwork_%d",
287 pic_output_path,
288 artwork_count) > MAXPATHLEN) {
289 free(base_outpath);
290 return;
291 }
292
293 char *art_payload = (char *)malloc(
294 sizeof(char) * (parsedAtoms[this_atom_num].AtomicLength - 16) + 1);
295 memset(art_payload, 0, (parsedAtoms[this_atom_num].AtomicLength - 16) + 1);
296
297 APar_readX(art_payload,
298 source_file,
299 parsedAtoms[this_atom_num].AtomicStart + 16,
300 parsedAtoms[this_atom_num].AtomicLength - 16);
301
302 char *suffix = (char *)malloc(sizeof(char) * 5);
303 memset(suffix, 0, sizeof(char) * 5);
304
305 if (memcmp(art_payload, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) {
306 strcpy(suffix, ".png");
307 } else if (memcmp(art_payload, "\xFF\xD8\xFF", 3) == 0) {
308 strcpy(suffix, ".jpg");
309 }
310
311 strcat(base_outpath, suffix);
312
313 FILE *outfile = APar_OpenFile(base_outpath, "wb");
314 if (outfile != NULL) {
315 fwrite(art_payload,
316 (size_t)(parsedAtoms[this_atom_num].AtomicLength - 16),
317 1,
318 outfile);
319 fclose(outfile);
320 fprintf(stdout, "Extracted artwork to file: ");
321 APar_fprintf_UTF8_data(base_outpath);
322 fprintf(stdout, "\n");
323 }
324 free(base_outpath);
325 free(art_payload);
326 free(suffix);
262327 return;
263328 }
264329
265330 /*----------------------
266331 APar_ImageExtractTest
267 buffer - pointer to raw image data
268 id3args - *currently unused* when testing raw image data from an image file, results like mimetype & imagetype will be placed here
269
270 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,
271 and its png, do a further test to see if its type 0x01 which requires it to be 32x32
332 buffer - pointer to raw image data
333 id3args - *currently unused* when testing raw image data from an image
334 file, results like mimetype & imagetype will be placed here
335
336 Loop through the ImageList array and see if the first few bytes in the image
337 data in buffer match any of the known image_binaryheader types listed. If it
338 does, and its png, do a further test to see if its type 0x01 which requires it
339 to be 32x32
272340 ----------------------*/
273 ImageFileFormatDefinition* APar_ImageExtractTest(char* buffer, AdjunctArgs* id3args) {
274 ImageFileFormatDefinition* thisImage = NULL;
275 uint8_t total_image_tests = ImageListMembers();
276
277 for (uint8_t itest = 0; itest < total_image_tests; itest++) {
278 if (ImageList[itest].image_testbytes == 0) {
279 if (id3args != NULL) {
280 id3args->mimeArg = ImageList[itest].image_mimetype;
281 }
282 return &ImageList[itest];
283 } else if (memcmp(buffer, ImageList[itest].image_binaryheader, ImageList[itest].image_testbytes) == 0) {
284 if (id3args != NULL) {
285 id3args->mimeArg = ImageList[itest].image_mimetype;
286 if (id3args->pictype_uint8 == 0x01) {
287 if (memcmp(buffer+16, "\x00\x00\x00\x20\x00\x00\x00\x20", 8) != 0 && itest != 2) {
288 id3args->pictype_uint8 = 0x02;
289 }
290 }
291 }
292 thisImage = &ImageList[itest];
293 break;
294 }
295 }
296 return thisImage;
341 ImageFileFormatDefinition *APar_ImageExtractTest(char *buffer,
342 AdjunctArgs *id3args) {
343 ImageFileFormatDefinition *thisImage = NULL;
344 uint8_t total_image_tests = ImageListMembers();
345
346 for (uint8_t itest = 0; itest < total_image_tests; itest++) {
347 if (ImageList[itest].image_testbytes == 0) {
348 if (id3args != NULL) {
349 id3args->mimeArg = ImageList[itest].image_mimetype;
350 }
351 return &ImageList[itest];
352 } else if (memcmp(buffer,
353 ImageList[itest].image_binaryheader,
354 ImageList[itest].image_testbytes) == 0) {
355 if (id3args != NULL) {
356 id3args->mimeArg = ImageList[itest].image_mimetype;
357 if (id3args->pictype_uint8 == 0x01) {
358 if (memcmp(buffer + 16, "\x00\x00\x00\x20\x00\x00\x00\x20", 8) != 0 &&
359 itest != 2) {
360 id3args->pictype_uint8 = 0x02;
361 }
362 }
363 }
364 thisImage = &ImageList[itest];
365 break;
366 }
367 }
368 return thisImage;
297369 }
298370
299371 /*----------------------
300372 APar_Extract_ID3v2_file
301 id32_atom - pointer to the AtomicInfo ID32 atom that contains this while ID3 tag (containing all the frames like APIC)
302 frame_str - either APIC or GEOB
303 originfile - the originating mpeg-4 file that contains the ID32 atom
304 destination_folder - *currently not used* TODO: extract to this folder
305 id3args - *currently not used* TODO: extract by mimetype or imagetype or description
306
307 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
308 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
309 extension. Loop through the linked list of ID3v2Frame and search for the internal frameID number.
310 When an image is found, test the data that the image contains and determine file extension from the ImageFileFormatDefinition structure (containing some popular
311 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.
312 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.
373 id32_atom - pointer to the AtomicInfo ID32 atom that contains this while
374 ID3 tag (containing all the frames like APIC) frame_str - either APIC or GEOB
375 originfile - the originating mpeg-4 file that contains the ID32 atom
376 destination_folder - *currently not used* TODO: extract to this folder
377 id3args - *currently not used* TODO: extract by mimetype or imagetype or
378 description
379
380 Extracts (all) files of a particular frame type (APIC or GEOB - GEOB is
381 currently not implemented) out to a file next to the originating mpeg-4 file.
382 First, match frame_str to get the internal frameID number for APIC/GEOB frame.
383 Locate the .ext of the origin file, duplicate the path including the basename
384 (excluding the extension. Loop through the linked list of ID3v2Frame and search
385 for the internal frameID number. When an image is found, test the data that the
386 image contains and determine file extension from the ImageFileFormatDefinition
387 structure (containing some popular image format/extension definitions). In
388 combination with the file extension, use the image description and image type to
389 create the name of the output file. The image (which if was compressed on disc
390 was expanded when read in) and simply write out its data (stored in the 5th
391 member of the frame's field strings.
313392 ----------------------*/
314 void APar_Extract_ID3v2_file(AtomicInfo* id32_atom, const char* frame_str, const char* originfile, const char* destination_folder, AdjunctArgs* id3args) {
315 uint16_t iter = 0;
316 ID3v2Frame* eval_frame = NULL;
317 uint32_t basepath_len = 0;
318 char* extract_filename = NULL;
319
320 int frameID = MatchID3FrameIDstr(frame_str, id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
321 int frameType = KnownFrames[frameID+1].ID3v2_FrameType;
322
323 if (destination_folder == NULL) {
324 basepath_len = (strrchr(originfile, '.') - originfile);
325 }
326
327 if (frameType == ID3_ATTACHED_PICTURE_FRAME || frameType == ID3_ATTACHED_OBJECT_FRAME) {
328 if (id32_atom->ID32_TagInfo->ID3v2_FirstFrame == NULL) return;
329
330 eval_frame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
331 extract_filename = (char*)malloc(sizeof(char*)*MAXPATHLEN+1);
332
333 while (eval_frame != NULL) {
334 if (frameType == eval_frame->ID3v2_FrameType) {
335 memset(extract_filename, 0, sizeof(char*)*MAXPATHLEN+1);
336 memcpy(extract_filename, originfile, basepath_len);
337 iter++;
338
339 if (eval_frame->ID3v2_FrameType == ID3_ATTACHED_PICTURE_FRAME) {
340 ImageFileFormatDefinition* thisimage = APar_ImageExtractTest((eval_frame->ID3v2_Frame_Fields+4)->field_string, NULL);
341 char* img_description = APar_ConvertField_to_UTF8(eval_frame, ID3_DESCRIPTION_FIELD);
342 sprintf(extract_filename+basepath_len, "-img#%u-(desc=%s)-0x%02X%s",
343 iter, img_description, (uint8_t)((eval_frame->ID3v2_Frame_Fields+2)->field_string[0]), thisimage->image_fileextn);
344
345 if (img_description != NULL) {
346 free(img_description);
347 img_description = NULL;
348 }
349 } else {
350 char* obj_description = APar_ConvertField_to_UTF8(eval_frame, ID3_DESCRIPTION_FIELD);
351 char* obj_filename = APar_ConvertField_to_UTF8(eval_frame, ID3_FILENAME_FIELD);
352 sprintf(extract_filename+basepath_len, "-obj#%u-(desc=%s)-%s", iter, obj_description, obj_filename);
353
354 if (obj_description != NULL) {
355 free(obj_description);
356 obj_description = NULL;
357 }
358 if (obj_filename != NULL) {
359 free(obj_filename);
360 obj_filename = NULL;
361 }
362 }
363
364 FILE *extractfile = APar_OpenFile(extract_filename, "wb");
365 if (extractfile != NULL) {
366 fwrite((eval_frame->ID3v2_Frame_Fields+4)->field_string, (size_t)((eval_frame->ID3v2_Frame_Fields+4)->field_length), 1, extractfile);
367 fclose(extractfile);
368 fprintf(stdout, "Extracted %s to file: %s\n", (frameType == ID3_ATTACHED_PICTURE_FRAME ? "artwork" : "object"), extract_filename);
369 }
370
371 }
372 eval_frame = eval_frame->ID3v2_NextFrame;
373 }
374 }
375 if (extract_filename != NULL) {
376 free(extract_filename);
377 extract_filename = NULL;
378 }
379 return;
393 void APar_Extract_ID3v2_file(AtomicInfo *id32_atom,
394 const char *frame_str,
395 const char *originfile,
396 const char *destination_folder,
397 AdjunctArgs *id3args) {
398 uint16_t iter = 0;
399 ID3v2Frame *eval_frame = NULL;
400 uint32_t basepath_len = 0;
401 char *extract_filename = NULL;
402
403 int frameID = MatchID3FrameIDstr(
404 frame_str, id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
405 int frameType = KnownFrames[frameID + 1].ID3v2_FrameType;
406
407 if (destination_folder == NULL) {
408 basepath_len = (strrchr(originfile, '.') - originfile);
409 }
410
411 if (frameType == ID3_ATTACHED_PICTURE_FRAME ||
412 frameType == ID3_ATTACHED_OBJECT_FRAME) {
413 if (id32_atom->ID32_TagInfo->ID3v2_FirstFrame == NULL)
414 return;
415
416 eval_frame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
417 extract_filename = (char *)malloc(sizeof(char *) * MAXPATHLEN + 1);
418
419 while (eval_frame != NULL) {
420 if (frameType == eval_frame->ID3v2_FrameType) {
421 memset(extract_filename, 0, sizeof(char *) * MAXPATHLEN + 1);
422 memcpy(extract_filename, originfile, basepath_len);
423 iter++;
424
425 if (eval_frame->ID3v2_FrameType == ID3_ATTACHED_PICTURE_FRAME) {
426 ImageFileFormatDefinition *thisimage = APar_ImageExtractTest(
427 (eval_frame->ID3v2_Frame_Fields + 4)->field_string, NULL);
428 char *img_description =
429 APar_ConvertField_to_UTF8(eval_frame, ID3_DESCRIPTION_FIELD);
430 sprintf(
431 extract_filename + basepath_len,
432 "-img#%u-(desc=%s)-0x%02X%s",
433 iter,
434 img_description,
435 (uint8_t)((eval_frame->ID3v2_Frame_Fields + 2)->field_string[0]),
436 thisimage->image_fileextn);
437
438 if (img_description != NULL) {
439 free(img_description);
440 img_description = NULL;
441 }
442 } else {
443 char *obj_description =
444 APar_ConvertField_to_UTF8(eval_frame, ID3_DESCRIPTION_FIELD);
445 char *obj_filename =
446 APar_ConvertField_to_UTF8(eval_frame, ID3_FILENAME_FIELD);
447 sprintf(extract_filename + basepath_len,
448 "-obj#%u-(desc=%s)-%s",
449 iter,
450 obj_description,
451 obj_filename);
452
453 if (obj_description != NULL) {
454 free(obj_description);
455 obj_description = NULL;
456 }
457 if (obj_filename != NULL) {
458 free(obj_filename);
459 obj_filename = NULL;
460 }
461 }
462
463 FILE *extractfile = APar_OpenFile(extract_filename, "wb");
464 if (extractfile != NULL) {
465 fwrite((eval_frame->ID3v2_Frame_Fields + 4)->field_string,
466 (size_t)((eval_frame->ID3v2_Frame_Fields + 4)->field_length),
467 1,
468 extractfile);
469 fclose(extractfile);
470 fprintf(
471 stdout,
472 "Extracted %s to file: %s\n",
473 (frameType == ID3_ATTACHED_PICTURE_FRAME ? "artwork" : "object"),
474 extract_filename);
475 }
476 }
477 eval_frame = eval_frame->ID3v2_NextFrame;
478 }
479 }
480 if (extract_filename != NULL) {
481 free(extract_filename);
482 extract_filename = NULL;
483 }
484 return;
380485 }
381486
382487 ///////////////////////////////////////////////////////////////////////////////////////
383 // iTunes-style metadata listings //
488 // iTunes-style metadata listings //
384489 ///////////////////////////////////////////////////////////////////////////////////////
385490
386491 void APar_ExtractDataAtom(int this_atom_number) {
387 if ( source_file != NULL ) {
388 AtomicInfo* thisAtom = &parsedAtoms[this_atom_number];
389 char* parent_atom_name;
390
391 AtomicInfo parent_atom_stats = parsedAtoms[this_atom_number-1];
392 parent_atom_name = parent_atom_stats.AtomicName;
393
394 uint32_t min_atom_datasize = 12;
395 uint32_t atom_header_size = 16;
396
397 if (thisAtom->AtomicClassification == EXTENDED_ATOM) {
398 if (thisAtom->uuid_style == UUID_DEPRECATED_FORM) {
399 min_atom_datasize +=4;
400 atom_header_size +=4;
401 } else {
402 min_atom_datasize = 36;
403 atom_header_size = 36;
404 }
405 }
406
407 if (thisAtom->AtomicLength > min_atom_datasize ) {
408 char* data_payload = (char*)malloc( sizeof(char) * (thisAtom->AtomicLength - atom_header_size +1) );
409 memset(data_payload, 0, sizeof(char) * (thisAtom->AtomicLength - atom_header_size +1) );
410
411 APar_readX(data_payload, source_file, thisAtom->AtomicStart + atom_header_size, thisAtom->AtomicLength - atom_header_size);
412
413 if (thisAtom->AtomicVerFlags == AtomFlags_Data_Text) {
414 if (thisAtom->AtomicLength < (atom_header_size + 4) ) {
415 //tvnn was showing up with 4 chars instead of 3; easier to null it out for now
416 data_payload[thisAtom->AtomicLength - atom_header_size] = '\00';
417 }
418
419 APar_fprintf_UTF8_data(data_payload);
420 fprintf(stdout,"\n");
421
422 } else {
423 if ( (memcmp(parent_atom_name, "trkn", 4) == 0) || (memcmp(parent_atom_name, "disk", 4) == 0) ) {
424 if (UInt16FromBigEndian(data_payload+4) != 0) {
425 fprintf(stdout, "%u of %u\n", UInt16FromBigEndian(data_payload+2), UInt16FromBigEndian(data_payload+4) );
426 } else {
427 fprintf(stdout, "%u\n", UInt16FromBigEndian(data_payload+2) );
428 }
429
430 } else if (strncmp(parent_atom_name, "gnre", 4) == 0) {
431 if ( thisAtom->AtomicLength - atom_header_size < 3 ) { //oh, a 1byte int for genre number
432 char* genre_string = GenreIntToString( UInt16FromBigEndian(data_payload) );
433 if (genre_string != NULL) {
434 fprintf(stdout,"%s\n", genre_string);
435 } else {
436 fprintf(stdout," out of bound value - %u\n", UInt16FromBigEndian(data_payload) );
437 }
438 } else {
439 fprintf(stdout," out of bound value - %u\n", UInt16FromBigEndian(data_payload) );
440 }
441
442 } else if ( (strncmp(parent_atom_name, "purl", 4) == 0) || (strncmp(parent_atom_name, "egid", 4) == 0) ) {
443 fprintf(stdout,"%s\n", data_payload);
444
445 } else {
446 if (thisAtom->AtomicVerFlags == AtomFlags_Data_UInt && (thisAtom->AtomicLength <= 20 || thisAtom->AtomicLength == 24) ) {
447 uint8_t bytes_rep = (uint8_t) (thisAtom->AtomicLength-atom_header_size);
448
449 switch(bytes_rep) {
450 case 1 : {
451 if ( (memcmp(parent_atom_name, "cpil", 4) == 0) || (memcmp(parent_atom_name, "pcst", 4) == 0) || (memcmp(parent_atom_name, "pgap", 4) == 0) ) {
452 if (data_payload[0] == 1) {
453 fprintf(stdout, "true\n");
454 } else {
455 fprintf(stdout, "false\n");
456 }
457
458 } else if (strncmp(parent_atom_name, "stik", 4) == 0) {
459 stiks* returned_stik = MatchStikNumber((uint8_t)data_payload[0]);
460 if (returned_stik != NULL) {
461 fprintf(stdout, "%s\n", returned_stik->stik_string);
462 } else {
463 fprintf(stdout, "Unknown value: %u\n", (uint8_t)data_payload[0]);
464 }
465
466 } else if (strncmp(parent_atom_name, "rtng", 4) == 0) { //okay, this is definitely an 8-bit number
467 if (data_payload[0] == 2) {
468 fprintf(stdout, "Clean Content\n");
469 } else if (data_payload[0] != 0 ) {
470 fprintf(stdout, "Explicit Content\n");
471 } else {
472 fprintf(stdout, "Inoffensive\n");
473 }
474
475 } else {
476 fprintf(stdout, "%u\n", (uint8_t)data_payload[0] );
477 }
478 break;
479 }
480 case 2 : { //tmpo
481 fprintf(stdout, "%u\n", UInt16FromBigEndian(data_payload) );
482 break;
483 }
484 case 4 : { //tves, tvsn
485 if (memcmp(parent_atom_name, "sfID", 4) == 0) {
486 sfIDs* this_store = MatchStoreFrontNumber( UInt32FromBigEndian(data_payload) );
487 if (this_store != NULL) {
488 fprintf(stdout, "%s (%" PRIu32 ")\n", this_store->storefront_string, this_store->storefront_number );
489 } else {
490 fprintf(stdout, "Unknown (%" PRIu32 ")\n", UInt32FromBigEndian(data_payload) );
491 }
492
493 } else {
494 fprintf(stdout, "%" PRIu32 "\n", UInt32FromBigEndian(data_payload) );
495 }
496 break;
497 }
498 case 8 : {
499 fprintf(stdout, "%" PRIu64 "\n", UInt64FromBigEndian(data_payload) );
500 break;
501 }
502 }
503
504 } else if (thisAtom->AtomicClassification == EXTENDED_ATOM &&
505 thisAtom->AtomicVerFlags == AtomFlags_Data_uuid_binary &&
506 thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE) {
507 uint64_t offset_into_uuiddata = 0;
508 uint64_t descrip_len = UInt32FromBigEndian(data_payload);
509 offset_into_uuiddata+=4;
510
511 char* uuid_description = (char*)calloc(1, sizeof(char) * descrip_len+16 ); //char uuid_description[descrip_len+1];
512 memcpy(uuid_description, data_payload+offset_into_uuiddata, descrip_len);
513 offset_into_uuiddata+=descrip_len;
514
515 uint8_t suffix_len = (uint8_t)data_payload[offset_into_uuiddata];
516 offset_into_uuiddata+=1;
517
518 char* file_suffix = (char*)calloc(1, sizeof(char) * suffix_len+16 ); //char file_suffix[suffix_len+1];
519 memcpy(file_suffix, data_payload+offset_into_uuiddata, suffix_len);
520 offset_into_uuiddata+=suffix_len;
521
522 uint8_t mime_len = (uint8_t)data_payload[offset_into_uuiddata];
523 offset_into_uuiddata+=1;
524
525 char* uuid_mimetype = (char*)calloc(1, sizeof(char) * mime_len+16 ); //char uuid_mimetype[mime_len+1];
526 memcpy(uuid_mimetype, data_payload+offset_into_uuiddata, mime_len);
527
528 fprintf(stdout, "FILE%s; mime-type=%s; description=%s\n", file_suffix, uuid_mimetype, uuid_description);
529
530 free(uuid_description);
531 uuid_description = NULL;
532 free(file_suffix);
533 file_suffix = NULL;
534 free(uuid_mimetype);
535 uuid_description = NULL;
536
537 } else { //purl & egid would end up here too, but Apple switched it to a text string (0x00), so gets taken care above explicitly
538 fprintf(stdout, "hex 0x");
539 for( int hexx = 1; hexx <= (int)(thisAtom->AtomicLength - atom_header_size); ++hexx) {
540 fprintf(stdout,"%02X", (uint8_t)data_payload[hexx-1]);
541 if ((hexx % 4) == 0 && hexx >= 4) {
542 fprintf(stdout," ");
543 }
544 if ((hexx % 16) == 0 && hexx > 16) {
545 fprintf(stdout,"\n\t\t\t");
546 }
547 if (hexx == (int)(thisAtom->AtomicLength - atom_header_size) ) {
548 fprintf(stdout,"\n");
549 }
550 }
551 } //end if AtomFlags_Data_UInt
552 }
553
554 free(data_payload);
555 data_payload = NULL;
556 }
557 }
558 }
559 return;
560 }
561
562 void APar_Print_iTunesData(const char *path, char* output_path, uint8_t supplemental_info, uint8_t target_information, AtomicInfo* ilstAtom) {
563 printBOM();
564
565 short artwork_count=0;
566
567 if (ilstAtom == NULL) {
568 ilstAtom = APar_FindAtom("moov.udta.meta.ilst", false, SIMPLE_ATOM, 0);
569 if (ilstAtom == NULL) return;
570 }
571
572 for (int i=ilstAtom->AtomicNumber; i < atom_number; i++) {
573 AtomicInfo* thisAtom = &parsedAtoms[i];
574
575 if ( strncmp(thisAtom->AtomicName, "data", 4) == 0) { //thisAtom->AtomicClassification == VERSIONED_ATOM) {
576
577 AtomicInfo* parent = &parsedAtoms[ APar_FindParentAtom(i, thisAtom->AtomicLevel) ];
578
579 if ( (thisAtom->AtomicVerFlags == AtomFlags_Data_Binary ||
580 thisAtom->AtomicVerFlags == AtomFlags_Data_Text ||
581 thisAtom->AtomicVerFlags == AtomFlags_Data_UInt) && target_information == PRINT_DATA ) {
582 if (strncmp(parent->AtomicName, "----", 4) == 0) {
583 if (memcmp(parsedAtoms[parent->AtomicNumber+2].AtomicName, "name", 4) == 0) {
584 fprintf(stdout, "Atom \"%s\" [%s;%s] contains: ", parent->AtomicName, parsedAtoms[parent->AtomicNumber+1].ReverseDNSdomain, parsedAtoms[parent->AtomicNumber+2].ReverseDNSname);
585 APar_ExtractDataAtom(i);
586 }
587
588 } else if (memcmp(parent->AtomicName, "covr", 4) == 0) { //libmp4v2 doesn't properly set artwork with the right flags (its all 0x00)
589 artwork_count++;
590
591 } else {
592 //converts iso8859 © in '©ART' to a 2byte utf8 © glyph; replaces libiconv conversion
593 memset(twenty_byte_buffer, 0, sizeof(char)*20);
594 isolat1ToUTF8((unsigned char*)twenty_byte_buffer, 10, (unsigned char*)parent->AtomicName, 4);
595
596 if (UnicodeOutputStatus == WIN32_UTF16) {
597 fprintf(stdout, "Atom \"");
598 APar_fprintf_UTF8_data(twenty_byte_buffer);
599 fprintf(stdout, "\" contains: ");
600 } else {
601 fprintf(stdout, "Atom \"%s\" contains: ", twenty_byte_buffer);
602 }
603
604 APar_ExtractDataAtom(i);
605 }
606
607 } else if (memcmp(parent->AtomicName, "covr", 4) == 0) {
608 artwork_count++;
609 if (target_information == EXTRACT_ARTWORK) {
610 APar_ExtractAAC_Artwork(thisAtom->AtomicNumber, output_path, artwork_count);
611 }
612 }
613 if ( thisAtom->AtomicLength <= 12 ) {
614 fprintf(stdout, "\n"); // (corrupted atom); libmp4v2 touching a file with copyright
615 }
616 }
617 }
618
619 if (artwork_count != 0 && target_information == PRINT_DATA) {
620 if (artwork_count == 1) {
621 fprintf(stdout, "Atom \"covr\" contains: %i piece of artwork\n", artwork_count);
622 } else {
623 fprintf(stdout, "Atom \"covr\" contains: %i pieces of artwork\n", artwork_count);
624 }
625 }
626
627 if (supplemental_info) {
628 fprintf(stdout, "---------------------------\n");
629 dynUpd.updage_by_padding = false;
630 //APar_DetermineDynamicUpdate(true); //gets the size of the padding
631 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'
632
633 if (supplemental_info && 0x02) { //PRINT_FREE_SPACE
634 fprintf(stdout, "free atom space: %" PRIu32 "\n", APar_ProvideTallyForAtom("free") );
635 }
636 if (supplemental_info && 0x04) { //PRINT_PADDING_SPACE
637 if (!moov_atom_was_mooved) {
638 fprintf(stdout, "padding available: %" PRIu64 " bytes\n", dynUpd.padding_bytes);
639 } else {
640 fprintf(stdout, "padding available: 0 (reorg)\n");
641 }
642 }
643 if (supplemental_info && 0x08 && dynUpd.moov_udta_atom != NULL) { //PRINT_USER_DATA_SPACE
644 fprintf(stdout, "user data space: %" PRIu64 "\n", dynUpd.moov_udta_atom->AtomicLength);
645 }
646 if (supplemental_info && 0x10) { //PRINT_USER_DATA_SPACE
647 fprintf(stdout, "media data space: %" PRIu32 "\n", APar_ProvideTallyForAtom("mdat") );
648 }
649 }
650
651 return;
492 if (source_file != NULL) {
493 AtomicInfo *thisAtom = &parsedAtoms[this_atom_number];
494 char *parent_atom_name;
495
496 AtomicInfo parent_atom_stats = parsedAtoms[this_atom_number - 1];
497 parent_atom_name = parent_atom_stats.AtomicName;
498
499 uint32_t min_atom_datasize = 12;
500 uint32_t atom_header_size = 16;
501
502 if (thisAtom->AtomicClassification == EXTENDED_ATOM) {
503 if (thisAtom->uuid_style == UUID_DEPRECATED_FORM) {
504 min_atom_datasize += 4;
505 atom_header_size += 4;
506 } else {
507 min_atom_datasize = 36;
508 atom_header_size = 36;
509 }
510 }
511
512 if (thisAtom->AtomicLength > min_atom_datasize) {
513 char *data_payload = (char *)malloc(
514 sizeof(char) * (thisAtom->AtomicLength - atom_header_size + 1));
515 memset(data_payload,
516 0,
517 sizeof(char) * (thisAtom->AtomicLength - atom_header_size + 1));
518
519 APar_readX(data_payload,
520 source_file,
521 thisAtom->AtomicStart + atom_header_size,
522 thisAtom->AtomicLength - atom_header_size);
523
524 if (thisAtom->AtomicVerFlags == AtomFlags_Data_Text) {
525 if (thisAtom->AtomicLength < (atom_header_size + 4)) {
526 // tvnn was showing up with 4 chars instead of 3; easier to null it
527 // out for now
528 data_payload[thisAtom->AtomicLength - atom_header_size] = '\00';
529 }
530
531 APar_fprintf_UTF8_data(data_payload);
532 fprintf(stdout, "\n");
533
534 } else {
535 if ((memcmp(parent_atom_name, "trkn", 4) == 0) ||
536 (memcmp(parent_atom_name, "disk", 4) == 0)) {
537 if (UInt16FromBigEndian(data_payload + 4) != 0) {
538 fprintf(stdout,
539 "%u of %u\n",
540 UInt16FromBigEndian(data_payload + 2),
541 UInt16FromBigEndian(data_payload + 4));
542 } else {
543 fprintf(stdout, "%u\n", UInt16FromBigEndian(data_payload + 2));
544 }
545
546 } else if (strncmp(parent_atom_name, "gnre", 4) == 0) {
547 if (thisAtom->AtomicLength - atom_header_size <
548 3) { // oh, a 1byte int for genre number
549 char *genre_string =
550 GenreIntToString(UInt16FromBigEndian(data_payload));
551 if (genre_string != NULL) {
552 fprintf(stdout, "%s\n", genre_string);
553 } else {
554 fprintf(stdout,
555 " out of bound value - %u\n",
556 UInt16FromBigEndian(data_payload));
557 }
558 } else {
559 fprintf(stdout,
560 " out of bound value - %u\n",
561 UInt16FromBigEndian(data_payload));
562 }
563
564 } else if ((strncmp(parent_atom_name, "purl", 4) == 0) ||
565 (strncmp(parent_atom_name, "egid", 4) == 0)) {
566 fprintf(stdout, "%s\n", data_payload);
567
568 } else {
569 if (thisAtom->AtomicVerFlags == AtomFlags_Data_UInt &&
570 (thisAtom->AtomicLength <= 20 || thisAtom->AtomicLength == 24)) {
571 uint8_t bytes_rep =
572 (uint8_t)(thisAtom->AtomicLength - atom_header_size);
573
574 switch (bytes_rep) {
575 case 1: {
576 if ((memcmp(parent_atom_name, "cpil", 4) == 0) ||
577 (memcmp(parent_atom_name, "pcst", 4) == 0) ||
578 (memcmp(parent_atom_name, "pgap", 4) == 0)) {
579 if (data_payload[0] == 1) {
580 fprintf(stdout, "true\n");
581 } else {
582 fprintf(stdout, "false\n");
583 }
584
585 } else if (strncmp(parent_atom_name, "stik", 4) == 0) {
586 stiks *returned_stik =
587 MatchStikNumber((uint8_t)data_payload[0]);
588 if (returned_stik != NULL) {
589 fprintf(stdout, "%s\n", returned_stik->stik_string);
590 } else {
591 fprintf(
592 stdout, "Unknown value: %u\n", (uint8_t)data_payload[0]);
593 }
594
595 } else if (strncmp(parent_atom_name, "rtng", 4) ==
596 0) { // okay, this is definitely an 8-bit number
597 if (data_payload[0] == 2) {
598 fprintf(stdout, "Clean Content\n");
599 } else if (data_payload[0] != 0) {
600 fprintf(stdout, "Explicit Content\n");
601 } else {
602 fprintf(stdout, "Inoffensive\n");
603 }
604
605 } else {
606 fprintf(stdout, "%u\n", (uint8_t)data_payload[0]);
607 }
608 break;
609 }
610 case 2: { // tmpo
611 fprintf(stdout, "%u\n", UInt16FromBigEndian(data_payload));
612 break;
613 }
614 case 4: { // tves, tvsn
615 if (memcmp(parent_atom_name, "sfID", 4) == 0) {
616 sfIDs *this_store =
617 MatchStoreFrontNumber(UInt32FromBigEndian(data_payload));
618 if (this_store != NULL) {
619 fprintf(stdout,
620 "%s (%" PRIu32 ")\n",
621 this_store->storefront_string,
622 this_store->storefront_number);
623 } else {
624 fprintf(stdout,
625 "Unknown (%" PRIu32 ")\n",
626 UInt32FromBigEndian(data_payload));
627 }
628
629 } else {
630 fprintf(
631 stdout, "%" PRIu32 "\n", UInt32FromBigEndian(data_payload));
632 }
633 break;
634 }
635 case 8: {
636 fprintf(
637 stdout, "%" PRIu64 "\n", UInt64FromBigEndian(data_payload));
638 break;
639 }
640 }
641
642 } else if (thisAtom->AtomicClassification == EXTENDED_ATOM &&
643 thisAtom->AtomicVerFlags == AtomFlags_Data_uuid_binary &&
644 thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE) {
645 uint64_t offset_into_uuiddata = 0;
646 uint64_t descrip_len = UInt32FromBigEndian(data_payload);
647 offset_into_uuiddata += 4;
648
649 char *uuid_description =
650 (char *)calloc(1,
651 sizeof(char) * descrip_len +
652 16); // char uuid_description[descrip_len+1];
653 memcpy(uuid_description,
654 data_payload + offset_into_uuiddata,
655 descrip_len);
656 offset_into_uuiddata += descrip_len;
657
658 uint8_t suffix_len = (uint8_t)data_payload[offset_into_uuiddata];
659 offset_into_uuiddata += 1;
660
661 char *file_suffix =
662 (char *)calloc(1,
663 sizeof(char) * suffix_len +
664 16); // char file_suffix[suffix_len+1];
665 memcpy(
666 file_suffix, data_payload + offset_into_uuiddata, suffix_len);
667 offset_into_uuiddata += suffix_len;
668
669 uint8_t mime_len = (uint8_t)data_payload[offset_into_uuiddata];
670 offset_into_uuiddata += 1;
671
672 char *uuid_mimetype =
673 (char *)calloc(1,
674 sizeof(char) * mime_len +
675 16); // char uuid_mimetype[mime_len+1];
676 memcpy(
677 uuid_mimetype, data_payload + offset_into_uuiddata, mime_len);
678
679 fprintf(stdout,
680 "FILE%s; mime-type=%s; description=%s\n",
681 file_suffix,
682 uuid_mimetype,
683 uuid_description);
684
685 free(uuid_description);
686 uuid_description = NULL;
687 free(file_suffix);
688 file_suffix = NULL;
689 free(uuid_mimetype);
690 uuid_description = NULL;
691
692 } else { // purl & egid would end up here too, but Apple switched it
693 // to a text string (0x00), so gets taken care above
694 // explicitly
695 fprintf(stdout, "hex 0x");
696 for (int hexx = 1;
697 hexx <= (int)(thisAtom->AtomicLength - atom_header_size);
698 ++hexx) {
699 fprintf(stdout, "%02X", (uint8_t)data_payload[hexx - 1]);
700 if ((hexx % 4) == 0 && hexx >= 4) {
701 fprintf(stdout, " ");
702 }
703 if ((hexx % 16) == 0 && hexx > 16) {
704 fprintf(stdout, "\n\t\t\t");
705 }
706 if (hexx == (int)(thisAtom->AtomicLength - atom_header_size)) {
707 fprintf(stdout, "\n");
708 }
709 }
710 } // end if AtomFlags_Data_UInt
711 }
712
713 free(data_payload);
714 data_payload = NULL;
715 }
716 }
717 }
718 return;
719 }
720
721 void APar_Print_iTunesData(const char *path,
722 char *output_path,
723 uint8_t supplemental_info,
724 uint8_t target_information,
725 AtomicInfo *ilstAtom) {
726 printBOM();
727
728 short artwork_count = 0;
729
730 if (ilstAtom == NULL) {
731 ilstAtom = APar_FindAtom("moov.udta.meta.ilst", false, SIMPLE_ATOM, 0);
732 if (ilstAtom == NULL)
733 return;
734 }
735
736 for (int i = ilstAtom->AtomicNumber; i < atom_number; i++) {
737 AtomicInfo *thisAtom = &parsedAtoms[i];
738
739 if (strncmp(thisAtom->AtomicName, "data", 4) ==
740 0) { // thisAtom->AtomicClassification == VERSIONED_ATOM) {
741
742 AtomicInfo *parent =
743 &parsedAtoms[APar_FindParentAtom(i, thisAtom->AtomicLevel)];
744
745 if ((thisAtom->AtomicVerFlags == AtomFlags_Data_Binary ||
746 thisAtom->AtomicVerFlags == AtomFlags_Data_Text ||
747 thisAtom->AtomicVerFlags == AtomFlags_Data_UInt) &&
748 target_information == PRINT_DATA) {
749 if (strncmp(parent->AtomicName, "----", 4) == 0) {
750 if (memcmp(parsedAtoms[parent->AtomicNumber + 2].AtomicName,
751 "name",
752 4) == 0) {
753 fprintf(stdout,
754 "Atom \"%s\" [%s;%s] contains: ",
755 parent->AtomicName,
756 parsedAtoms[parent->AtomicNumber + 1].ReverseDNSdomain,
757 parsedAtoms[parent->AtomicNumber + 2].ReverseDNSname);
758 APar_ExtractDataAtom(i);
759 }
760
761 } else if (memcmp(parent->AtomicName, "covr", 4) ==
762 0) { // libmp4v2 doesn't properly set artwork with the right
763 // flags (its all 0x00)
764 artwork_count++;
765
766 } else {
767 // converts iso8859 © in '©ART' to a 2byte utf8 © glyph; replaces
768 // libiconv conversion
769 memset(twenty_byte_buffer, 0, sizeof(char) * 20);
770 isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
771 10,
772 (unsigned char *)parent->AtomicName,
773 4);
774
775 if (UnicodeOutputStatus == WIN32_UTF16) {
776 fprintf(stdout, "Atom \"");
777 APar_fprintf_UTF8_data(twenty_byte_buffer);
778 fprintf(stdout, "\" contains: ");
779 } else {
780 fprintf(stdout, "Atom \"%s\" contains: ", twenty_byte_buffer);
781 }
782
783 APar_ExtractDataAtom(i);
784 }
785
786 } else if (memcmp(parent->AtomicName, "covr", 4) == 0) {
787 artwork_count++;
788 if (target_information == EXTRACT_ARTWORK) {
789 APar_ExtractAAC_Artwork(
790 thisAtom->AtomicNumber, output_path, artwork_count);
791 }
792 }
793 if (thisAtom->AtomicLength <= 12) {
794 fprintf(
795 stdout,
796 "\n"); // (corrupted atom); libmp4v2 touching a file with copyright
797 }
798 }
799 }
800
801 if (artwork_count != 0 && target_information == PRINT_DATA) {
802 if (artwork_count == 1) {
803 fprintf(stdout,
804 "Atom \"covr\" contains: %i piece of artwork\n",
805 artwork_count);
806 } else {
807 fprintf(stdout,
808 "Atom \"covr\" contains: %i pieces of artwork\n",
809 artwork_count);
810 }
811 }
812
813 if (supplemental_info) {
814 fprintf(stdout, "---------------------------\n");
815 dynUpd.updage_by_padding = false;
816 // APar_DetermineDynamicUpdate(true); //gets the size of the padding
817 APar_Optimize(
818 true); // just to know if 'free' atoms can be considered padding, or (in
819 // the case of say a faac file) it's *just* 'free'
820
821 if (supplemental_info & 0x02) { // PRINT_FREE_SPACE
822 fprintf(stdout,
823 "free atom space: %" PRIu32 "\n",
824 APar_ProvideTallyForAtom("free"));
825 }
826 if (supplemental_info & 0x04) { // PRINT_PADDING_SPACE
827 if (!moov_atom_was_mooved) {
828 fprintf(stdout,
829 "padding available: %" PRIu64 " bytes\n",
830 dynUpd.padding_bytes);
831 } else {
832 fprintf(stdout, "padding available: 0 (reorg)\n");
833 }
834 }
835 if (supplemental_info & 0x08 &&
836 dynUpd.moov_udta_atom != NULL) { // PRINT_USER_DATA_SPACE
837 fprintf(stdout,
838 "user data space: %" PRIu64 "\n",
839 dynUpd.moov_udta_atom->AtomicLength);
840 }
841 if (supplemental_info & 0x10) { // PRINT_USER_DATA_SPACE
842 fprintf(stdout,
843 "media data space: %" PRIu32 "\n",
844 APar_ProvideTallyForAtom("mdat"));
845 }
846 }
847
848 return;
652849 }
653850
654851 ///////////////////////////////////////////////////////////////////////////////////////
655 // AP uuid metadata listings //
852 // AP uuid metadata listings //
656853 ///////////////////////////////////////////////////////////////////////////////////////
657854
658 void APar_Print_APuuidv5_contents(AtomicInfo* thisAtom) {
659 memset(twenty_byte_buffer, 0, sizeof(char)*20);
660 isolat1ToUTF8((unsigned char*)twenty_byte_buffer, 10, (unsigned char*)thisAtom->uuid_ap_atomname, 4);
661
662 fprintf(stdout, "Atom uuid=");
663 APar_print_uuid((ap_uuid_t*) thisAtom->AtomicName, false);
664 fprintf(stdout, " (AP uuid for \"");
665 APar_fprintf_UTF8_data(twenty_byte_buffer);
666 fprintf(stdout, "\") contains: ");
667
668 APar_ExtractDataAtom(thisAtom->AtomicNumber);
669 return;
670 }
671
672 void APar_Print_APuuid_deprecated_contents(AtomicInfo* thisAtom) {
673 memset(twenty_byte_buffer, 0, sizeof(char)*20);
674 isolat1ToUTF8((unsigned char*)twenty_byte_buffer, 10, (unsigned char*)thisAtom->AtomicName, 4);
675
676 if (UnicodeOutputStatus == WIN32_UTF16) {
677 fprintf(stdout, "Atom uuid=\"");
678 APar_fprintf_UTF8_data(twenty_byte_buffer);
679 fprintf(stdout, "\" contains: ");
680 } else {
681 fprintf(stdout, "Atom uuid=\"%s\" contains: ", twenty_byte_buffer);
682 }
683
684 APar_ExtractDataAtom(thisAtom->AtomicNumber);
685 return;
686 }
687
688 void APar_Print_APuuid_atoms(const char *path, char* output_path, uint8_t target_information) {
689 AtomicInfo* thisAtom = NULL;
690
691 printBOM();
692
693 AtomicInfo* metaAtom = APar_FindAtom("moov.udta.meta", false, VERSIONED_ATOM, 0);
694
695 if (metaAtom == NULL) return;
696
697 for (int i=metaAtom->NextAtomNumber; i < atom_number; i++) {
698 thisAtom = &parsedAtoms[i];
699 if ( thisAtom->AtomicLevel <= metaAtom->AtomicLevel ) break; //we've gone too far
700 if (thisAtom->AtomicClassification == EXTENDED_ATOM) {
701 if ( thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE ) {
702 if (target_information == PRINT_DATA) APar_Print_APuuidv5_contents(thisAtom);
703 if (target_information == EXTRACT_ALL_UUID_BINARYS && thisAtom->AtomicVerFlags == AtomFlags_Data_uuid_binary) {
704 APar_Extract_uuid_binary_file(thisAtom, path, output_path);
705 }
706 }
707 if ( thisAtom->uuid_style == UUID_DEPRECATED_FORM && target_information == PRINT_DATA) APar_Print_APuuid_deprecated_contents(thisAtom);
708 }
709 }
710 return;
855 void APar_Print_APuuidv5_contents(AtomicInfo *thisAtom) {
856 memset(twenty_byte_buffer, 0, sizeof(char) * 20);
857 isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
858 10,
859 (unsigned char *)thisAtom->uuid_ap_atomname,
860 4);
861
862 fprintf(stdout, "Atom uuid=");
863 APar_print_uuid((ap_uuid_t *)thisAtom->AtomicName, false);
864 fprintf(stdout, " (AP uuid for \"");
865 APar_fprintf_UTF8_data(twenty_byte_buffer);
866 fprintf(stdout, "\") contains: ");
867
868 APar_ExtractDataAtom(thisAtom->AtomicNumber);
869 return;
870 }
871
872 void APar_Print_APuuid_deprecated_contents(AtomicInfo *thisAtom) {
873 memset(twenty_byte_buffer, 0, sizeof(char) * 20);
874 isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
875 10,
876 (unsigned char *)thisAtom->AtomicName,
877 4);
878
879 if (UnicodeOutputStatus == WIN32_UTF16) {
880 fprintf(stdout, "Atom uuid=\"");
881 APar_fprintf_UTF8_data(twenty_byte_buffer);
882 fprintf(stdout, "\" contains: ");
883 } else {
884 fprintf(stdout, "Atom uuid=\"%s\" contains: ", twenty_byte_buffer);
885 }
886
887 APar_ExtractDataAtom(thisAtom->AtomicNumber);
888 return;
889 }
890
891 void APar_Print_APuuid_atoms(const char *path,
892 char *output_path,
893 uint8_t target_information) {
894 AtomicInfo *thisAtom = NULL;
895
896 printBOM();
897
898 AtomicInfo *metaAtom =
899 APar_FindAtom("moov.udta.meta", false, VERSIONED_ATOM, 0);
900
901 if (metaAtom == NULL)
902 return;
903
904 for (int i = metaAtom->NextAtomNumber; i < atom_number; i++) {
905 thisAtom = &parsedAtoms[i];
906 if (thisAtom->AtomicLevel <= metaAtom->AtomicLevel)
907 break; // we've gone too far
908 if (thisAtom->AtomicClassification == EXTENDED_ATOM) {
909 if (thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE) {
910 if (target_information == PRINT_DATA)
911 APar_Print_APuuidv5_contents(thisAtom);
912 if (target_information == EXTRACT_ALL_UUID_BINARYS &&
913 thisAtom->AtomicVerFlags == AtomFlags_Data_uuid_binary) {
914 APar_Extract_uuid_binary_file(thisAtom, path, output_path);
915 }
916 }
917 if (thisAtom->uuid_style == UUID_DEPRECATED_FORM &&
918 target_information == PRINT_DATA)
919 APar_Print_APuuid_deprecated_contents(thisAtom);
920 }
921 }
922 return;
711923 }
712924
713925 ///////////////////////////////////////////////////////////////////////////////////////
714 // 3GP asset metadata listings //
926 // 3GP asset metadata listings //
715927 ///////////////////////////////////////////////////////////////////////////////////////
716928
717 void APar_PrintUnicodeAssest(char* unicode_string, int asset_length) { //3gp files
718 if (strncmp(unicode_string, "\xFE\xFF", 2) == 0 ) { //utf16
719 fprintf(stdout, " (utf16)] : ");
720
721 unsigned char* utf8_data = Convert_multibyteUTF16_to_UTF8(unicode_string, (asset_length-13) * 6, asset_length-14);
722
723 #if defined (_WIN32) && !defined (__CYGWIN__)
724 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)
725 unsigned char* utf8_data = Convert_multibyteUTF16_to_UTF8(unicode_string, (asset_length -13) * 6, asset_length-14);
726 fprintf(stdout, "%s", utf8_data);
727
728 free(utf8_data);
729 utf8_data = NULL;
730
731 } else {
732 wchar_t* utf16_data = Convert_multibyteUTF16_to_wchar(unicode_string, (asset_length - 16) / 2, true);
733 APar_unicode_win32Printout(utf16_data, (char*)utf8_data);
734
735 free(utf16_data);
736 utf16_data = NULL;
737 }
929 void APar_PrintUnicodeAssest(char *unicode_string,
930 int asset_length) { // 3gp files
931 if (strncmp(unicode_string, "\xFE\xFF", 2) == 0) { // utf16
932 fprintf(stdout, " (utf16)] : ");
933
934 unsigned char *utf8_data = Convert_multibyteUTF16_to_UTF8(
935 unicode_string, (asset_length - 13) * 6, asset_length - 14);
936
937 #if defined(_WIN32) && !defined(__CYGWIN__)
938 if (GetVersion() & 0x80000000 ||
939 UnicodeOutputStatus ==
940 UNIVERSAL_UTF8) { // pre-NT or AP-utf8.exe (pish, thats my win98se,
941 // and without unicows support convert utf16toutf8
942 // and output raw bytes)
943 unsigned char *utf8_data = Convert_multibyteUTF16_to_UTF8(
944 unicode_string, (asset_length - 13) * 6, asset_length - 14);
945 fprintf(stdout, "%s", utf8_data);
946
947 free(utf8_data);
948 utf8_data = NULL;
949
950 } else {
951 wchar_t *utf16_data = Convert_multibyteUTF16_to_wchar(
952 unicode_string, (asset_length - 16) / 2, true);
953 APar_unicode_win32Printout(utf16_data, (char *)utf8_data);
954
955 free(utf16_data);
956 utf16_data = NULL;
957 }
738958 #else
739 fprintf(stdout, "%s", utf8_data);
959 fprintf(stdout, "%s", utf8_data);
740960 #endif
741
742 free(utf8_data);
743 utf8_data = NULL;
744
745 } else { //utf8
746 fprintf(stdout, " (utf8)] : ");
747
748 APar_fprintf_UTF8_data(unicode_string);
749
750 }
751 return;
752 }
753
754 void APar_Print_single_userdata_atomcontents(uint8_t track_num, short userdata_atom, bool quantum_listing) {
755 uint32_t box = UInt32FromBigEndian(parsedAtoms[userdata_atom].AtomicName);
756
757 char bitpacked_lang[3];
758 memset(bitpacked_lang, 0, 3);
759 unsigned char unpacked_lang[3];
760
761 uint32_t box_length = parsedAtoms[userdata_atom].AtomicLength;
762 char* box_data = (char*)malloc(sizeof(char)*box_length);
763 memset(box_data, 0, sizeof(char)*box_length);
764
765 switch (box) {
766 case 0x7469746C : //'titl'
767 case 0x64736370 : //'dscp'
768 case 0x63707274 : //'cprt'
769 case 0x70657266 : //'perf'
770 case 0x61757468 : //'auth'
771 case 0x676E7265 : //'gnre'
772 case 0x616C626D : //'albm'
773 {
774 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
775
776 uint16_t packed_lang = APar_read16(bitpacked_lang, source_file, parsedAtoms[userdata_atom].AtomicStart + 12);
777 APar_UnpackLanguage(unpacked_lang, packed_lang);
778
779 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
780
781 //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
782 uint16_t track_num = 1000; //tracknum is a uint8_t, so setting it > 256 means a number wasn't found
783 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
784 if (box_data[box_length - 15] != 0) {
785 track_num = (uint16_t)box_data[box_length - 15];
786 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
787 }
788 }
789
790 fprintf(stdout, "[lang=%s", unpacked_lang);
791
792 APar_PrintUnicodeAssest(box_data, box_length);
793
794 if (box == 0x616C626D && track_num != 1000) {
795 fprintf(stdout, " | Track: %u", track_num);
796 }
797 fprintf(stdout, "\n");
798 break;
799 }
800
801 case 0x72746E67 : //'rtng'
802 {
803 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
804
805 APar_readX(box_data, source_file, parsedAtoms[userdata_atom].AtomicStart + 12, 4);
806
807 fprintf(stdout, "[Rating Entity=%s", box_data);
808 memset(box_data, 0, box_length);
809 APar_readX(box_data, source_file, parsedAtoms[userdata_atom].AtomicStart + 16, 4);
810 fprintf(stdout, " | Criteria=%s", box_data);
811
812 uint16_t packed_lang = APar_read16(bitpacked_lang, source_file, parsedAtoms[userdata_atom].AtomicStart + 20);
813 APar_UnpackLanguage(unpacked_lang, packed_lang);
814 fprintf(stdout, " lang=%s", unpacked_lang);
815
816 memset(box_data, 0, box_length);
817 APar_readX(box_data, source_file, parsedAtoms[userdata_atom].AtomicStart + 22, box_length-8);
818
819 APar_PrintUnicodeAssest(box_data, box_length-8);
820 fprintf(stdout, "\n");
821 break;
822 }
823
824 case 0x636C7366 : //'clsf'
825 {
826 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
827
828 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
829
830 fprintf(stdout, "[Classification Entity=%s", box_data);
831 fprintf(stdout, " | Index=%u", UInt16FromBigEndian(box_data + 4) );
832
833 uint16_t packed_lang = APar_read16(bitpacked_lang, source_file, parsedAtoms[userdata_atom].AtomicStart + 18);
834 APar_UnpackLanguage(unpacked_lang, packed_lang);
835 fprintf(stdout, " lang=%s", unpacked_lang);
836
837 APar_PrintUnicodeAssest(box_data +8, box_length-8);
838 fprintf(stdout, "\n");
839 break;
840 }
841
842 case 0x6B797764 : //'kywd'
843 {
844 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
845
846 uint64_t box_offset = 12;
847
848 uint16_t packed_lang = APar_read16(bitpacked_lang, source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
849 box_offset+=2;
850
851 APar_UnpackLanguage(unpacked_lang, packed_lang);
852
853 uint8_t keyword_count = APar_read8(source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
854 box_offset++;
855 fprintf(stdout, "[Keyword count=%u", keyword_count);
856 fprintf(stdout, " lang=%s]", unpacked_lang);
857
858 char* keyword_data = (char*)malloc(sizeof(char)* box_length * 2);
859
860 for(uint8_t x = 1; x <= keyword_count; x++) {
861 memset(keyword_data, 0, box_length * 2);
862 uint8_t keyword_length = APar_read8(source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
863 box_offset++;
864
865 APar_readX(keyword_data, source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset, keyword_length);
866 box_offset+=keyword_length;
867 APar_SimplePrintUnicodeAssest(keyword_data, keyword_length, true);
868 }
869 free(keyword_data);
870 keyword_data = NULL;
871
872 fprintf(stdout, "\n");
873 break;
874 }
875
876 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.
877 {
878 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
879
880 uint64_t box_offset = 12;
881 uint16_t packed_lang = APar_read16(bitpacked_lang, source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
882 box_offset+=2;
883
884 APar_UnpackLanguage(unpacked_lang, packed_lang);
885
886 APar_readX(box_data, source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset, box_length);
887 fprintf(stdout, "[lang=%s] ", unpacked_lang);
888
889 //the length of the location string is unknown (max is box lenth), but the long/lat/alt/body/notes needs to be retrieved.
890 //test if the location string is utf16; if so search for 0x0000 (or if utf8, find the first NULL).
891 if ( strncmp(box_data, "\xFE\xFF", 2) == 0 ) {
892 box_offset+= 2 * widechar_len(box_data, box_length) + 2; //*2 for utf16 (double-byte); +2 for the terminating NULL
893 fprintf(stdout, "(utf16) ");
894 } else {
895 fprintf(stdout, "(utf8) ");
896 box_offset+= strlen(box_data) + 1; //+1 for the terminating NULL
897 }
898 fprintf(stdout, "Location: ");
899 APar_SimplePrintUnicodeAssest(box_data, box_length, false);
900
901 uint8_t location_role = APar_read8(source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
902 box_offset++;
903 switch(location_role) {
904 case 0 : {
905 fprintf(stdout, " (Role: shooting location) ");
906 break;
907 }
908 case 1 : {
909 fprintf(stdout, " (Role: real location) ");
910 break;
911 }
912 case 2 : {
913 fprintf(stdout, " (Role: fictional location) ");
914 break;
915 }
916 default : {
917 fprintf(stdout, " (Role: [reserved]) ");
918 break;
919 }
920 }
921
922 char* float_buffer = (char*)malloc(sizeof(char)* 5);
923 memset(float_buffer, 0, 5);
924
925 fprintf(stdout, "[Long %lf", fixed_point_16x16bit_to_double( APar_read32(float_buffer, source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset) ) );
926 box_offset+=4;
927 fprintf(stdout, " Lat %lf", fixed_point_16x16bit_to_double( APar_read32(float_buffer, source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset) ) );
928 box_offset+=4;
929 fprintf(stdout, " Alt %lf ", fixed_point_16x16bit_to_double( APar_read32(float_buffer, source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset) ) );
930 box_offset+=4;
931 free(float_buffer);
932 float_buffer = NULL;
933
934 if (box_offset < box_length) {
935 fprintf(stdout, " Body: ");
936 APar_SimplePrintUnicodeAssest(box_data+box_offset-14, box_length-box_offset, false);
937 if ( strncmp(box_data+box_offset-14, "\xFE\xFF", 2) == 0 ) {
938 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
939 } else {
940 box_offset+= strlen(box_data+box_offset-14) + 1; //+1 for the terminating NULL
941 }
942 }
943 fprintf(stdout, "]");
944
945 if (box_offset < box_length) {
946 fprintf(stdout, " Notes: ");
947 APar_SimplePrintUnicodeAssest(box_data+box_offset-14, box_length-box_offset, false);
948 }
949
950 fprintf(stdout, "\n");
951 break;
952 }
953
954 case 0x79727263 : //'yrrc'
955 {
956 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
957
958 uint16_t recording_year = APar_read16(bitpacked_lang, source_file, parsedAtoms[userdata_atom].AtomicStart + 12);
959 fprintf(stdout, ": %u\n", recording_year);
960 break;
961 }
962
963 case 0x6E616D65 : //'name'
964 {
965 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
966 APar_fprintf_UTF8_data(": ");
967
968 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
969 APar_fprintf_UTF8_data(box_data);
970 APar_fprintf_UTF8_data("\n");
971 break;
972
973 }
974 case 0x686E7469 : //'hnti'
975 {
976 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
977
978 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
979 fprintf(stdout, "for %s:\n", parsedAtoms[userdata_atom+1].AtomicName);
980 APar_fprintf_UTF8_data(box_data);
981 break;
982 }
983
984 default :
985 {
986 break;
987 }
988 }
989 return;
961
962 free(utf8_data);
963 utf8_data = NULL;
964
965 } else { // utf8
966 fprintf(stdout, " (utf8)] : ");
967
968 APar_fprintf_UTF8_data(unicode_string);
969 }
970 return;
971 }
972
973 void APar_Print_single_userdata_atomcontents(uint8_t track_num,
974 short userdata_atom,
975 bool quantum_listing) {
976 uint32_t box = UInt32FromBigEndian(parsedAtoms[userdata_atom].AtomicName);
977
978 char bitpacked_lang[3];
979 memset(bitpacked_lang, 0, 3);
980 unsigned char unpacked_lang[3];
981
982 uint32_t box_length = parsedAtoms[userdata_atom].AtomicLength;
983 char *box_data = (char *)malloc(sizeof(char) * box_length);
984 memset(box_data, 0, sizeof(char) * box_length);
985
986 switch (box) {
987 case 0x7469746C: //'titl'
988 case 0x64736370: //'dscp'
989 case 0x63707274: //'cprt'
990 case 0x70657266: //'perf'
991 case 0x61757468: //'auth'
992 case 0x676E7265: //'gnre'
993 case 0x616C626D: //'albm'
994 {
995 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
996
997 uint16_t packed_lang =
998 APar_read16(bitpacked_lang,
999 source_file,
1000 parsedAtoms[userdata_atom].AtomicStart + 12);
1001 APar_UnpackLanguage(unpacked_lang, packed_lang);
1002
1003 APar_readX(
1004 box_data,
1005 source_file,
1006 parsedAtoms[userdata_atom].AtomicStart + 14,
1007 box_length -
1008 14); // 4bytes length, 4 bytes name, 4 bytes flags, 2 bytes lang
1009
1010 // get tracknumber *after* we read the whole tag; if we have a utf16 tag, it
1011 // will have a BOM, indicating if we have to search for 2 NULLs or a utf8
1012 // single NULL, then the ****optional**** tracknumber
1013 uint16_t track_num = 1000; // tracknum is a uint8_t, so setting it > 256
1014 // means a number wasn't found
1015 if (box ==
1016 0x616C626D) { //'albm' has an *optional* uint8_t at the end for
1017 // tracknumber; if the last byte in the tag is not
1018 // 0, then it must be the optional tracknum (or a
1019 // non-compliant, non-NULL-terminated string). This
1020 // byte is the length - (14 bytes +1tracknum) or -15
1021 if (box_data[box_length - 15] != 0) {
1022 track_num = (uint16_t)box_data[box_length - 15];
1023 box_data[box_length - 15] =
1024 0; // NULL out the last byte if found to be not 0 - it will impact
1025 // unicode conversion if it remains
1026 }
1027 }
1028
1029 fprintf(stdout, "[lang=%s", unpacked_lang);
1030
1031 APar_PrintUnicodeAssest(box_data, box_length);
1032
1033 if (box == 0x616C626D && track_num != 1000) {
1034 fprintf(stdout, " | Track: %u", track_num);
1035 }
1036 fprintf(stdout, "\n");
1037 break;
1038 }
1039
1040 case 0x72746E67: //'rtng'
1041 {
1042 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
1043
1044 APar_readX(
1045 box_data, source_file, parsedAtoms[userdata_atom].AtomicStart + 12, 4);
1046
1047 fprintf(stdout, "[Rating Entity=%s", box_data);
1048 memset(box_data, 0, box_length);
1049 APar_readX(
1050 box_data, source_file, parsedAtoms[userdata_atom].AtomicStart + 16, 4);
1051 fprintf(stdout, " | Criteria=%s", box_data);
1052
1053 uint16_t packed_lang =
1054 APar_read16(bitpacked_lang,
1055 source_file,
1056 parsedAtoms[userdata_atom].AtomicStart + 20);
1057 APar_UnpackLanguage(unpacked_lang, packed_lang);
1058 fprintf(stdout, " lang=%s", unpacked_lang);
1059
1060 memset(box_data, 0, box_length);
1061 APar_readX(box_data,
1062 source_file,
1063 parsedAtoms[userdata_atom].AtomicStart + 22,
1064 box_length - 8);
1065
1066 APar_PrintUnicodeAssest(box_data, box_length - 8);
1067 fprintf(stdout, "\n");
1068 break;
1069 }
1070
1071 case 0x636C7366: //'clsf'
1072 {
1073 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
1074
1075 APar_readX(
1076 box_data,
1077 source_file,
1078 parsedAtoms[userdata_atom].AtomicStart + 12,
1079 box_length -
1080 12); // 4bytes length, 4 bytes name, 4 bytes flags, 2 bytes lang
1081
1082 fprintf(stdout, "[Classification Entity=%s", box_data);
1083 fprintf(stdout, " | Index=%u", UInt16FromBigEndian(box_data + 4));
1084
1085 uint16_t packed_lang =
1086 APar_read16(bitpacked_lang,
1087 source_file,
1088 parsedAtoms[userdata_atom].AtomicStart + 18);
1089 APar_UnpackLanguage(unpacked_lang, packed_lang);
1090 fprintf(stdout, " lang=%s", unpacked_lang);
1091
1092 APar_PrintUnicodeAssest(box_data + 8, box_length - 8);
1093 fprintf(stdout, "\n");
1094 break;
1095 }
1096
1097 case 0x6B797764: //'kywd'
1098 {
1099 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
1100
1101 uint64_t box_offset = 12;
1102
1103 uint16_t packed_lang =
1104 APar_read16(bitpacked_lang,
1105 source_file,
1106 parsedAtoms[userdata_atom].AtomicStart + box_offset);
1107 box_offset += 2;
1108
1109 APar_UnpackLanguage(unpacked_lang, packed_lang);
1110
1111 uint8_t keyword_count = APar_read8(
1112 source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
1113 box_offset++;
1114 fprintf(stdout, "[Keyword count=%u", keyword_count);
1115 fprintf(stdout, " lang=%s]", unpacked_lang);
1116
1117 char *keyword_data = (char *)malloc(sizeof(char) * box_length * 2);
1118
1119 for (uint8_t x = 1; x <= keyword_count; x++) {
1120 memset(keyword_data, 0, box_length * 2);
1121 uint8_t keyword_length = APar_read8(
1122 source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
1123 box_offset++;
1124
1125 APar_readX(keyword_data,
1126 source_file,
1127 parsedAtoms[userdata_atom].AtomicStart + box_offset,
1128 keyword_length);
1129 box_offset += keyword_length;
1130 APar_SimplePrintUnicodeAssest(keyword_data, keyword_length, true);
1131 }
1132 free(keyword_data);
1133 keyword_data = NULL;
1134
1135 fprintf(stdout, "\n");
1136 break;
1137 }
1138
1139 case 0x6C6F6369: //'loci' aka The Most Heinous Metadata Atom Every Invented -
1140 // decimal meters? fictional location? Astromical Body? Say I
1141 // shoot it on the International Space Station? That isn't a
1142 // Astronimical Body. And 16.16 alt only goes up to 20.3
1143 // miles (because of negatives, its really 15.15) & the ISS
1144 // is
1145 // at 230 miles. Oh, pish.... what ever shall I do? I fear I
1146 // am on the horns of a dilema.
1147 {
1148 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
1149
1150 uint64_t box_offset = 12;
1151 uint16_t packed_lang =
1152 APar_read16(bitpacked_lang,
1153 source_file,
1154 parsedAtoms[userdata_atom].AtomicStart + box_offset);
1155 box_offset += 2;
1156
1157 APar_UnpackLanguage(unpacked_lang, packed_lang);
1158
1159 APar_readX(box_data,
1160 source_file,
1161 parsedAtoms[userdata_atom].AtomicStart + box_offset,
1162 box_length);
1163 fprintf(stdout, "[lang=%s] ", unpacked_lang);
1164
1165 // the length of the location string is unknown (max is box lenth), but the
1166 // long/lat/alt/body/notes needs to be retrieved. test if the location
1167 // string is utf16; if so search for 0x0000 (or if utf8, find the first
1168 // NULL).
1169 if (strncmp(box_data, "\xFE\xFF", 2) == 0) {
1170 box_offset += 2 * widechar_len(box_data, box_length) +
1171 2; //*2 for utf16 (double-byte); +2 for the terminating NULL
1172 fprintf(stdout, "(utf16) ");
1173 } else {
1174 fprintf(stdout, "(utf8) ");
1175 box_offset += strlen(box_data) + 1; //+1 for the terminating NULL
1176 }
1177 fprintf(stdout, "Location: ");
1178 APar_SimplePrintUnicodeAssest(box_data, box_length, false);
1179
1180 uint8_t location_role = APar_read8(
1181 source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
1182 box_offset++;
1183 switch (location_role) {
1184 case 0: {
1185 fprintf(stdout, " (Role: shooting location) ");
1186 break;
1187 }
1188 case 1: {
1189 fprintf(stdout, " (Role: real location) ");
1190 break;
1191 }
1192 case 2: {
1193 fprintf(stdout, " (Role: fictional location) ");
1194 break;
1195 }
1196 default: {
1197 fprintf(stdout, " (Role: [reserved]) ");
1198 break;
1199 }
1200 }
1201
1202 char *float_buffer = (char *)malloc(sizeof(char) * 5);
1203 memset(float_buffer, 0, 5);
1204
1205 fprintf(stdout,
1206 "[Long %lf",
1207 fixed_point_16x16bit_to_double(APar_read32(
1208 float_buffer,
1209 source_file,
1210 parsedAtoms[userdata_atom].AtomicStart + box_offset)));
1211 box_offset += 4;
1212 fprintf(stdout,
1213 " Lat %lf",
1214 fixed_point_16x16bit_to_double(APar_read32(
1215 float_buffer,
1216 source_file,
1217 parsedAtoms[userdata_atom].AtomicStart + box_offset)));
1218 box_offset += 4;
1219 fprintf(stdout,
1220 " Alt %lf ",
1221 fixed_point_16x16bit_to_double(APar_read32(
1222 float_buffer,
1223 source_file,
1224 parsedAtoms[userdata_atom].AtomicStart + box_offset)));
1225 box_offset += 4;
1226 free(float_buffer);
1227 float_buffer = NULL;
1228
1229 if (box_offset < box_length) {
1230 fprintf(stdout, " Body: ");
1231 APar_SimplePrintUnicodeAssest(
1232 box_data + box_offset - 14, box_length - box_offset, false);
1233 if (strncmp(box_data + box_offset - 14, "\xFE\xFF", 2) == 0) {
1234 box_offset +=
1235 2 * widechar_len(box_data + box_offset - 14,
1236 box_length - box_offset) +
1237 2; //*2 for utf16 (double-byte); +2 for the terminating NULL
1238 } else {
1239 box_offset += strlen(box_data + box_offset - 14) +
1240 1; //+1 for the terminating NULL
1241 }
1242 }
1243 fprintf(stdout, "]");
1244
1245 if (box_offset < box_length) {
1246 fprintf(stdout, " Notes: ");
1247 APar_SimplePrintUnicodeAssest(
1248 box_data + box_offset - 14, box_length - box_offset, false);
1249 }
1250
1251 fprintf(stdout, "\n");
1252 break;
1253 }
1254
1255 case 0x79727263: //'yrrc'
1256 {
1257 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
1258
1259 uint16_t recording_year =
1260 APar_read16(bitpacked_lang,
1261 source_file,
1262 parsedAtoms[userdata_atom].AtomicStart + 12);
1263 fprintf(stdout, ": %u\n", recording_year);
1264 break;
1265 }
1266
1267 case 0x6E616D65: //'name'
1268 {
1269 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
1270 APar_fprintf_UTF8_data(": ");
1271
1272 APar_readX(
1273 box_data,
1274 source_file,
1275 parsedAtoms[userdata_atom].AtomicStart + 8,
1276 box_length -
1277 8); // 4bytes length, 4 bytes name, 4 bytes flags, 2 bytes lang
1278 APar_fprintf_UTF8_data(box_data);
1279 APar_fprintf_UTF8_data("\n");
1280 break;
1281 }
1282 case 0x686E7469: //'hnti'
1283 {
1284 APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
1285
1286 APar_readX(
1287 box_data,
1288 source_file,
1289 parsedAtoms[userdata_atom + 1].AtomicStart + 8,
1290 box_length -
1291 8); // 4bytes length, 4 bytes name, 4 bytes flags, 2 bytes lang
1292 fprintf(stdout, "for %s:\n", parsedAtoms[userdata_atom + 1].AtomicName);
1293 APar_fprintf_UTF8_data(box_data);
1294 break;
1295 }
1296
1297 default: {
1298 break;
1299 }
1300 }
1301 return;
9901302 }
9911303
9921304 ///////////////////////////////////////////////////////////////////////////////////////
993 // id3 displaying functions //
1305 // id3 displaying functions //
9941306 ///////////////////////////////////////////////////////////////////////////////////////
9951307
996 void APar_Print_ID3TextField(ID3v2Frame* textframe, ID3v2Fields* textfield, bool linefeed = false) {
997 //this won't accommodate id3v2.4's multiple strings separated by NULLs
998 if (textframe->ID3v2_Frame_Fields->field_string[0] == TE_LATIN1) { //all frames that have text encodings have the encoding as the first field
999 if (textfield->field_length > 0) {
1000 char* conv_buffer = (char*)calloc(1, sizeof(char*)*(textfield->field_length *4) +2);
1001 isolat1ToUTF8((unsigned char*)conv_buffer, sizeof(char*)*(textfield->field_length *4) +2, (unsigned char*)textfield->field_string, textfield->field_length);
1002 fprintf(stdout, "%s", conv_buffer);
1003 free(conv_buffer);
1004 conv_buffer = NULL;
1005 }
1006
1007 } 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
1008 if (textfield->field_length > 2) {
1009 char* conv_buffer = (char*)calloc(1, sizeof(char*)*(textfield->field_length *2) +2);
1010 if (strncmp(textfield->field_string, "\xFF\xFE", 2) == 0) {
1011 UTF16LEToUTF8((unsigned char*)conv_buffer, sizeof(char*)*(textfield->field_length *4) +2, (unsigned char*)textfield->field_string+2, textfield->field_length);
1012 fprintf(stdout, "%s", conv_buffer);
1013 } else {
1014 UTF16BEToUTF8((unsigned char*)conv_buffer, sizeof(char*)*(textfield->field_length *4) +2, (unsigned char*)textfield->field_string+2, textfield->field_length);
1015 fprintf(stdout, "%s", conv_buffer);
1016 }
1017 free(conv_buffer);
1018 conv_buffer = NULL;
1019 }
1020
1021 } else if (textframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF16BE_NO_BOM) {
1022 if (textfield->field_length > 0) {
1023 char* conv_buffer = (char*)calloc(1, sizeof(char*)*(textfield->field_length *2) +2);
1024 UTF16BEToUTF8((unsigned char*)conv_buffer, sizeof(char*)*(textfield->field_length *4) +2, (unsigned char*)textfield->field_string, textfield->field_length);
1025 fprintf(stdout, "%s", conv_buffer);
1026 free(conv_buffer);
1027 conv_buffer = NULL;
1028 }
1029 } else if (textframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF8) {
1030 fprintf(stdout, "%s", textfield->field_string);
1031 } else {
1032 fprintf(stdout, "(unknown type: 0x%X", (uint8_t)textframe->ID3v2_Frame_Fields->field_string[0]);
1033 }
1034 if(linefeed) fprintf(stdout, "\n");
1035 return;
1036 }
1037
1038 const char* APar_GetTextEncoding(ID3v2Frame* aframe, ID3v2Fields* textfield) {
1039 const char* text_encoding = NULL;
1040 if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_LATIN1) text_encoding = "latin1";
1041 if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF16BE_NO_BOM) {
1042 if (strncmp(textfield->field_string, "\xFF\xFE", 2) == 0) {
1043 text_encoding = "utf16le";
1044 } else if (strncmp(textfield->field_string, "\xFE\xFF", 2) == 0) {
1045 text_encoding = "utf16be";
1046 }
1047 }
1048 if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF16LE_WITH_BOM) text_encoding = "utf16le";
1049 if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF8) text_encoding = "utf8";
1050 return text_encoding;
1051 }
1052
1053 void APar_Print_ID3v2_tags(AtomicInfo* id32_atom) {
1054 //TODO properly printout latin1 for fields like owner
1055 //TODO for binary fields (like GRID group data) scan through to see if it needs to be printed in hex
1056 //fprintf(stdout, "Maj.Min.Rev version was 2.%u.%u\n", id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion, id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion);
1057 char* id32_level = (char*)calloc(1, sizeof(char*)*16);
1058 if (id32_atom->AtomicLevel == 2) {
1059 memcpy(id32_level, "file level", 10);
1060 } else if (id32_atom->AtomicLevel == 3) {
1061 memcpy(id32_level, "movie level", 11);
1062 } else if (id32_atom->AtomicLevel == 4) {
1063 sprintf(id32_level, "track #%u", 1); //unimplemented; need to pass a variable here
1064 }
1065
1066 unsigned char unpacked_lang[3];
1067 APar_UnpackLanguage(unpacked_lang, id32_atom->AtomicLanguage);
1068
1069 if (id32_atom->ID32_TagInfo->ID3v2_FirstFrame != NULL) {
1070 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);
1071 } else {
1072 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);
1073 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags, ID32_TAGFLAG_UNSYNCRONIZATION)) {
1074 fprintf(stdout, "Unsynchronized flag set. Unsupported. No tags read. %" PRIu32 " bytes.\n", id32_atom->ID32_TagInfo->ID3v2Tag_Length);
1075 }
1076 }
1077
1078 ID3v2Frame* target_frameinfo = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1079 while (target_frameinfo != NULL) {
1080 if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_GROUPING) && target_frameinfo && target_frameinfo->ID3v2_FrameType != ID3_GROUP_ID_FRAME) {
1081 fprintf(stdout, " Tag: %s GID=0x%02X \"%s\" ", target_frameinfo->ID3v2_Frame_Namestr, target_frameinfo->ID3v2_Frame_GroupingSymbol,
1082 KnownFrames[target_frameinfo->ID3v2_Frame_ID+1].ID3V2_FrameDescription );
1083 } else {
1084 fprintf(stdout, " Tag: %s \"%s\" ", target_frameinfo->ID3v2_Frame_Namestr, KnownFrames[target_frameinfo->ID3v2_Frame_ID+1].ID3V2_FrameDescription );
1085 }
1086 uint8_t frame_comp_idx = GetFrameCompositionDescription(target_frameinfo->ID3v2_FrameType);
1087 if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_UNKNOWN_FRAME) {
1088 fprintf(stdout, "(unknown frame) %" PRIu32 " bytes\n", target_frameinfo->ID3v2_Frame_Fields->field_length);
1089
1090 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_TEXT_FRAME) {
1091 ID3v2Fields* atextfield = target_frameinfo->ID3v2_Frame_Fields+1;
1092
1093 if (target_frameinfo->textfield_tally > 1) {
1094 fprintf(stdout, "(%s) : { ", APar_GetTextEncoding(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+1) );
1095 } else {
1096 fprintf(stdout, "(%s) : ", APar_GetTextEncoding(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+1) );
1097 }
1098
1099 while (true) {
1100 if (target_frameinfo->textfield_tally > 1) {
1101 fprintf(stdout, "\"");
1102 }
1103
1104 if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_CONTENTTYPE) {
1105 char* genre_string = NULL;
1106 int genre_idx = (int)strtol(atextfield->field_string, &genre_string, 10);
1107 if (genre_string != atextfield->field_string) {
1108 genre_string = ID3GenreIntToString(genre_idx);
1109 if (target_frameinfo->textfield_tally == 1) {
1110 fprintf(stdout, "%s\n", ID3GenreIntToString(genre_idx));
1111 } else {
1112 fprintf(stdout, "%s", ID3GenreIntToString(genre_idx));
1113 }
1114 } else {
1115 APar_Print_ID3TextField(target_frameinfo, atextfield, target_frameinfo->textfield_tally == 1 ? true : false);
1116 }
1117
1118 } else if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_COPYRIGHT) {
1119 APar_fprintf_UTF8_data("\xC2\xA9 ");
1120 APar_Print_ID3TextField(target_frameinfo, atextfield, target_frameinfo->textfield_tally == 1 ? true : false);
1121
1122 } else if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_PRODNOTICE) {
1123 APar_fprintf_UTF8_data("\xE2\x84\x97 ");
1124 APar_Print_ID3TextField(target_frameinfo, atextfield, target_frameinfo->textfield_tally == 1 ? true : false);
1125
1126 } else {
1127 APar_Print_ID3TextField(target_frameinfo, atextfield, target_frameinfo->textfield_tally == 1 ? true : false);
1128 }
1129
1130 if (target_frameinfo->textfield_tally > 1) {
1131 fprintf(stdout, "\"");
1132 } else {
1133 break;
1134 }
1135
1136 atextfield = atextfield->next_field;
1137 if (atextfield == NULL) {
1138 fprintf(stdout, " }\n");
1139 break;
1140 } else {
1141 fprintf(stdout, ", ");
1142 }
1143 }
1144
1145 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_TEXT_FRAME_USERDEF) {
1146 fprintf(stdout, "(user-defined text frame) ");
1147 fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
1148
1149 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_URL_FRAME) {
1150 fprintf(stdout, "(url frame) : %s\n", (target_frameinfo->ID3v2_Frame_Fields+1)->field_string);
1151 fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
1152
1153 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_URL_FRAME_USERDEF) {
1154 fprintf(stdout, "(user-defined url frame) ");
1155 fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
1156
1157 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_UNIQUE_FILE_ID_FRAME) {
1158 if (test_limited_ascii( (target_frameinfo->ID3v2_Frame_Fields+1)->field_string, (target_frameinfo->ID3v2_Frame_Fields+1)->field_length)) {
1159 fprintf(stdout, "(owner='%s') : %s\n", target_frameinfo->ID3v2_Frame_Fields->field_string, (target_frameinfo->ID3v2_Frame_Fields+1)->field_string);
1160 } else {
1161 fprintf(stdout, "(owner='%s') : 0x", target_frameinfo->ID3v2_Frame_Fields->field_string);
1162 for (uint32_t hexidx = 0; hexidx < (target_frameinfo->ID3v2_Frame_Fields+1)->field_length; hexidx++) {
1163 fprintf(stdout, "%02X", (uint8_t)(target_frameinfo->ID3v2_Frame_Fields+1)->field_string[hexidx]);
1164 }
1165 fprintf(stdout, "\n");
1166 }
1167
1168 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_CD_ID_FRAME) { //TODO: print hex representation
1169 uint8_t tracklistings = 0;
1170 if (target_frameinfo->ID3v2_Frame_Fields->field_length >= 16) {
1171 tracklistings = target_frameinfo->ID3v2_Frame_Fields->field_length / 8;
1172 fprintf(stdout, "(Music CD Identifier) : Entries for %u tracks + leadout track.\n Hex: 0x", tracklistings-1);
1173 } else {
1174 fprintf(stdout, "(Music CD Identifier) : Unknown format (less then 16 bytes).\n Hex: 0x");
1175 }
1176 for (uint16_t hexidx = 1; hexidx < target_frameinfo->ID3v2_Frame_Fields->field_length+1; hexidx++) {
1177 fprintf(stdout, "%02X", (uint8_t)target_frameinfo->ID3v2_Frame_Fields->field_string[hexidx-1]);
1178 if (hexidx % 4 == 0) fprintf(stdout, " ");
1179 }
1180 fprintf(stdout, "\n");
1181
1182 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_DESCRIBED_TEXT_FRAME) {
1183 fprintf(stdout, "(%s, lang=%s, desc[", APar_GetTextEncoding(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+2),
1184 (target_frameinfo->ID3v2_Frame_Fields+1)->field_string );
1185 APar_Print_ID3TextField(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+2);
1186 fprintf(stdout, "]) : ");
1187 APar_Print_ID3TextField(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+3, true);
1188
1189 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType == ID3_ATTACHED_PICTURE_FRAME) {
1190 fprintf(stdout, "(type=0x%02X-'%s', mimetype=%s, %s, desc[", (uint8_t)(target_frameinfo->ID3v2_Frame_Fields+2)->field_string[0],
1191 ImageTypeList[ (uint8_t)(target_frameinfo->ID3v2_Frame_Fields+2)->field_string[0] ].imagetype_str, (target_frameinfo->ID3v2_Frame_Fields+1)->field_string,
1192 APar_GetTextEncoding(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+1) );
1193 APar_Print_ID3TextField(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+3);
1194 if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED)) {
1195 fprintf(stdout, "]) : %" PRIu32 " bytes (%" PRIu32 " compressed)\n",
1196 (target_frameinfo->ID3v2_Frame_Fields+4)->field_length, target_frameinfo->ID3v2_Frame_Length);
1197 } else {
1198 fprintf(stdout, "]) : %" PRIu32 " bytes\n", (target_frameinfo->ID3v2_Frame_Fields+4)->field_length);
1199 }
1200
1201 } else if (target_frameinfo->ID3v2_FrameType == ID3_ATTACHED_OBJECT_FRAME) {
1202 fprintf(stdout, "(filename=");
1203 APar_Print_ID3TextField(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+2);
1204 fprintf(stdout, ", mimetype=%s, desc[", (target_frameinfo->ID3v2_Frame_Fields+1)->field_string);
1205 APar_Print_ID3TextField(target_frameinfo, target_frameinfo->ID3v2_Frame_Fields+3);
1206 if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags, ID32_FRAMEFLAG_COMPRESSED)) {
1207 fprintf(stdout, "]) : %" PRIu32 " bytes (%" PRIu32 " compressed)\n",
1208 (target_frameinfo->ID3v2_Frame_Fields+4)->field_length, target_frameinfo->ID3v2_Frame_Length);
1209 } else {
1210 fprintf(stdout, "]) : %" PRIu32 " bytes\n", (target_frameinfo->ID3v2_Frame_Fields+4)->field_length);
1211 }
1212
1213 } else if (target_frameinfo->ID3v2_FrameType == ID3_GROUP_ID_FRAME) {
1214 fprintf(stdout, "(owner='%s') : 0x%02X", target_frameinfo->ID3v2_Frame_Fields->field_string, (uint8_t)(target_frameinfo->ID3v2_Frame_Fields+1)->field_string[0]);
1215 if ((target_frameinfo->ID3v2_Frame_Fields+2)->field_length > 0) {
1216 fprintf(stdout, "; groupdata='%s'\n", (target_frameinfo->ID3v2_Frame_Fields+2)->field_string);
1217 } else {
1218 fprintf(stdout, "\n");
1219 }
1220
1221 } else if (target_frameinfo->ID3v2_FrameType == ID3_PRIVATE_FRAME) {
1222 fprintf(stdout, "(owner='%s') : %s\n", target_frameinfo->ID3v2_Frame_Fields->field_string, (target_frameinfo->ID3v2_Frame_Fields+1)->field_string);
1223
1224 } else if (target_frameinfo->ID3v2_FrameType == ID3_SIGNATURE_FRAME) {
1225 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);
1226
1227 } else if (target_frameinfo->ID3v2_FrameType == ID3_PLAYCOUNTER_FRAME) {
1228 if (target_frameinfo->ID3v2_Frame_Fields->field_length == 4) {
1229 fprintf(stdout, ": %" PRIu32 "\n", syncsafe32_to_UInt32(target_frameinfo->ID3v2_Frame_Fields->field_string) );
1230 } else if (target_frameinfo->ID3v2_Frame_Fields->field_length > 4) {
1231 fprintf(stdout, ": %" PRIu64 "\n", syncsafeXX_to_UInt64(target_frameinfo->ID3v2_Frame_Fields->field_string, target_frameinfo->ID3v2_Frame_Fields->field_length) );
1232 }
1233
1234 } else if (target_frameinfo->ID3v2_FrameType == ID3_POPULAR_FRAME) {
1235 fprintf(stdout, "(owner='%s') : %u", target_frameinfo->ID3v2_Frame_Fields->field_string, (uint8_t)(target_frameinfo->ID3v2_Frame_Fields+1)->field_string[0]);
1236 if ((target_frameinfo->ID3v2_Frame_Fields+2)->field_length > 0) {
1237 if ((target_frameinfo->ID3v2_Frame_Fields+2)->field_length == 4) {
1238 fprintf(stdout, "; playcount=%" PRIu32 "\n", syncsafe32_to_UInt32((target_frameinfo->ID3v2_Frame_Fields+2)->field_string));
1239 } else if ((target_frameinfo->ID3v2_Frame_Fields+2)->field_length > 4) {
1240 fprintf(stdout, "; playcount=%" PRIu64 "\n", syncsafeXX_to_UInt64((target_frameinfo->ID3v2_Frame_Fields+2)->field_string, (target_frameinfo->ID3v2_Frame_Fields+2)->field_length));
1241 } else {
1242 fprintf(stdout, "\n"); //don't know what it was supposed to be, so skip it
1243 }
1244 } else {
1245 fprintf(stdout, "\n");
1246 }
1247
1248 } else {
1249 fprintf(stdout, " [idx=%u;%d]\n", frame_comp_idx, FrameTypeConstructionList[frame_comp_idx].ID3_FrameType);
1250 }
1251 target_frameinfo = target_frameinfo->ID3v2_NextFrame;
1252 }
1253 free(id32_level);
1254 id32_level = NULL;
1255 return;
1308 void APar_Print_ID3TextField(ID3v2Frame *textframe,
1309 ID3v2Fields *textfield,
1310 bool linefeed = false) {
1311 // this won't accommodate id3v2.4's multiple strings separated by NULLs
1312 if (textframe->ID3v2_Frame_Fields->field_string[0] ==
1313 TE_LATIN1) { // all frames that have text encodings have the encoding as
1314 // the first field
1315 if (textfield->field_length > 0) {
1316 char *conv_buffer =
1317 (char *)calloc(1, sizeof(char *) * (textfield->field_length * 4) + 2);
1318 isolat1ToUTF8((unsigned char *)conv_buffer,
1319 sizeof(char *) * (textfield->field_length * 4) + 2,
1320 (unsigned char *)textfield->field_string,
1321 textfield->field_length);
1322 fprintf(stdout, "%s", conv_buffer);
1323 free(conv_buffer);
1324 conv_buffer = NULL;
1325 }
1326
1327 } else if (textframe->ID3v2_Frame_Fields->field_string[0] ==
1328 TE_UTF16LE_WITH_BOM) { // technically AP *writes* uff16LE here, but
1329 // based on BOM, it could be utf16BE
1330 if (textfield->field_length > 2) {
1331 char *conv_buffer =
1332 (char *)calloc(1, sizeof(char *) * (textfield->field_length * 2) + 2);
1333 if (strncmp(textfield->field_string, "\xFF\xFE", 2) == 0) {
1334 UTF16LEToUTF8((unsigned char *)conv_buffer,
1335 sizeof(char *) * (textfield->field_length * 4) + 2,
1336 (unsigned char *)textfield->field_string + 2,
1337 textfield->field_length);
1338 fprintf(stdout, "%s", conv_buffer);
1339 } else {
1340 UTF16BEToUTF8((unsigned char *)conv_buffer,
1341 sizeof(char *) * (textfield->field_length * 4) + 2,
1342 (unsigned char *)textfield->field_string + 2,
1343 textfield->field_length);
1344 fprintf(stdout, "%s", conv_buffer);
1345 }
1346 free(conv_buffer);
1347 conv_buffer = NULL;
1348 }
1349
1350 } else if (textframe->ID3v2_Frame_Fields->field_string[0] ==
1351 TE_UTF16BE_NO_BOM) {
1352 if (textfield->field_length > 0) {
1353 char *conv_buffer =
1354 (char *)calloc(1, sizeof(char *) * (textfield->field_length * 2) + 2);
1355 UTF16BEToUTF8((unsigned char *)conv_buffer,
1356 sizeof(char *) * (textfield->field_length * 4) + 2,
1357 (unsigned char *)textfield->field_string,
1358 textfield->field_length);
1359 fprintf(stdout, "%s", conv_buffer);
1360 free(conv_buffer);
1361 conv_buffer = NULL;
1362 }
1363 } else if (textframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF8) {
1364 fprintf(stdout, "%s", textfield->field_string);
1365 } else {
1366 fprintf(stdout,
1367 "(unknown type: 0x%X",
1368 (uint8_t)textframe->ID3v2_Frame_Fields->field_string[0]);
1369 }
1370 if (linefeed)
1371 fprintf(stdout, "\n");
1372 return;
1373 }
1374
1375 const char *APar_GetTextEncoding(ID3v2Frame *aframe, ID3v2Fields *textfield) {
1376 const char *text_encoding = NULL;
1377 if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_LATIN1)
1378 text_encoding = "latin1";
1379 if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF16BE_NO_BOM) {
1380 if (strncmp(textfield->field_string, "\xFF\xFE", 2) == 0) {
1381 text_encoding = "utf16le";
1382 } else if (strncmp(textfield->field_string, "\xFE\xFF", 2) == 0) {
1383 text_encoding = "utf16be";
1384 }
1385 }
1386 if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF16LE_WITH_BOM)
1387 text_encoding = "utf16le";
1388 if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF8)
1389 text_encoding = "utf8";
1390 return text_encoding;
1391 }
1392
1393 void APar_Print_ID3v2_tags(AtomicInfo *id32_atom) {
1394 // TODO properly printout latin1 for fields like owner
1395 // TODO for binary fields (like GRID group data) scan through to see if it
1396 // needs to be printed in hex fprintf(stdout, "Maj.Min.Rev version
1397 // was 2.%u.%u\n", id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion,
1398 // id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion);
1399 char *id32_level = (char *)calloc(1, sizeof(char *) * 16);
1400 if (id32_atom->AtomicLevel == 2) {
1401 memcpy(id32_level, "file level", 10);
1402 } else if (id32_atom->AtomicLevel == 3) {
1403 memcpy(id32_level, "movie level", 11);
1404 } else if (id32_atom->AtomicLevel == 4) {
1405 sprintf(id32_level,
1406 "track #%u",
1407 1); // unimplemented; need to pass a variable here
1408 }
1409
1410 unsigned char unpacked_lang[3];
1411 APar_UnpackLanguage(unpacked_lang, id32_atom->AtomicLanguage);
1412
1413 if (id32_atom->ID32_TagInfo->ID3v2_FirstFrame != NULL) {
1414 fprintf(stdout,
1415 "ID32 atom [lang=%s] at %s contains an ID3v2.%u.%u tag (%u tags, "
1416 "%u bytes):\n",
1417 unpacked_lang,
1418 id32_level,
1419 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion,
1420 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion,
1421 id32_atom->ID32_TagInfo->ID3v2_FrameCount,
1422 id32_atom->ID32_TagInfo->ID3v2Tag_Length);
1423 } else {
1424 fprintf(stdout,
1425 "ID32 atom [lang=%s] at %s contains an ID3v2.%u.%u tag. ",
1426 unpacked_lang,
1427 id32_level,
1428 id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion,
1429 id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion);
1430 if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
1431 ID32_TAGFLAG_UNSYNCRONIZATION)) {
1432 fprintf(stdout,
1433 "Unsynchronized flag set. Unsupported. No tags read. %" PRIu32
1434 " bytes.\n",
1435 id32_atom->ID32_TagInfo->ID3v2Tag_Length);
1436 }
1437 }
1438
1439 ID3v2Frame *target_frameinfo = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
1440 while (target_frameinfo != NULL) {
1441 if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags,
1442 ID32_FRAMEFLAG_GROUPING) &&
1443 target_frameinfo &&
1444 target_frameinfo->ID3v2_FrameType != ID3_GROUP_ID_FRAME) {
1445 fprintf(stdout,
1446 " Tag: %s GID=0x%02X \"%s\" ",
1447 target_frameinfo->ID3v2_Frame_Namestr,
1448 target_frameinfo->ID3v2_Frame_GroupingSymbol,
1449 KnownFrames[target_frameinfo->ID3v2_Frame_ID + 1]
1450 .ID3V2_FrameDescription);
1451 } else {
1452 fprintf(stdout,
1453 " Tag: %s \"%s\" ",
1454 target_frameinfo->ID3v2_Frame_Namestr,
1455 KnownFrames[target_frameinfo->ID3v2_Frame_ID + 1]
1456 .ID3V2_FrameDescription);
1457 }
1458 uint8_t frame_comp_idx =
1459 GetFrameCompositionDescription(target_frameinfo->ID3v2_FrameType);
1460 if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1461 ID3_UNKNOWN_FRAME) {
1462 fprintf(stdout,
1463 "(unknown frame) %" PRIu32 " bytes\n",
1464 target_frameinfo->ID3v2_Frame_Fields->field_length);
1465
1466 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1467 ID3_TEXT_FRAME) {
1468 ID3v2Fields *atextfield = target_frameinfo->ID3v2_Frame_Fields + 1;
1469
1470 if (target_frameinfo->textfield_tally > 1) {
1471 fprintf(stdout,
1472 "(%s) : { ",
1473 APar_GetTextEncoding(target_frameinfo,
1474 target_frameinfo->ID3v2_Frame_Fields + 1));
1475 } else {
1476 fprintf(stdout,
1477 "(%s) : ",
1478 APar_GetTextEncoding(target_frameinfo,
1479 target_frameinfo->ID3v2_Frame_Fields + 1));
1480 }
1481
1482 while (true) {
1483 if (target_frameinfo->textfield_tally > 1) {
1484 fprintf(stdout, "\"");
1485 }
1486
1487 if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_CONTENTTYPE) {
1488 char *genre_string = NULL;
1489 int genre_idx =
1490 (int)strtol(atextfield->field_string, &genre_string, 10);
1491 if (genre_string != atextfield->field_string) {
1492 genre_string = ID3GenreIntToString(genre_idx);
1493 if (target_frameinfo->textfield_tally == 1) {
1494 fprintf(stdout, "%s\n", ID3GenreIntToString(genre_idx));
1495 } else {
1496 fprintf(stdout, "%s", ID3GenreIntToString(genre_idx));
1497 }
1498 } else {
1499 APar_Print_ID3TextField(
1500 target_frameinfo,
1501 atextfield,
1502 target_frameinfo->textfield_tally == 1 ? true : false);
1503 }
1504
1505 } else if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_COPYRIGHT) {
1506 APar_fprintf_UTF8_data("\xC2\xA9 ");
1507 APar_Print_ID3TextField(
1508 target_frameinfo,
1509 atextfield,
1510 target_frameinfo->textfield_tally == 1 ? true : false);
1511
1512 } else if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_PRODNOTICE) {
1513 APar_fprintf_UTF8_data("\xE2\x84\x97 ");
1514 APar_Print_ID3TextField(
1515 target_frameinfo,
1516 atextfield,
1517 target_frameinfo->textfield_tally == 1 ? true : false);
1518
1519 } else {
1520 APar_Print_ID3TextField(
1521 target_frameinfo,
1522 atextfield,
1523 target_frameinfo->textfield_tally == 1 ? true : false);
1524 }
1525
1526 if (target_frameinfo->textfield_tally > 1) {
1527 fprintf(stdout, "\"");
1528 } else {
1529 break;
1530 }
1531
1532 atextfield = atextfield->next_field;
1533 if (atextfield == NULL) {
1534 fprintf(stdout, " }\n");
1535 break;
1536 } else {
1537 fprintf(stdout, ", ");
1538 }
1539 }
1540
1541 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1542 ID3_TEXT_FRAME_USERDEF) {
1543 fprintf(stdout, "(user-defined text frame) ");
1544 fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
1545
1546 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1547 ID3_URL_FRAME) {
1548 fprintf(stdout,
1549 "(url frame) : %s\n",
1550 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
1551 fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
1552
1553 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1554 ID3_URL_FRAME_USERDEF) {
1555 fprintf(stdout, "(user-defined url frame) ");
1556 fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
1557
1558 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1559 ID3_UNIQUE_FILE_ID_FRAME) {
1560 if (test_limited_ascii(
1561 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_string,
1562 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_length)) {
1563 fprintf(stdout,
1564 "(owner='%s') : %s\n",
1565 target_frameinfo->ID3v2_Frame_Fields->field_string,
1566 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
1567 } else {
1568 fprintf(stdout,
1569 "(owner='%s') : 0x",
1570 target_frameinfo->ID3v2_Frame_Fields->field_string);
1571 for (uint32_t hexidx = 0;
1572 hexidx < (target_frameinfo->ID3v2_Frame_Fields + 1)->field_length;
1573 hexidx++) {
1574 fprintf(stdout,
1575 "%02X",
1576 (uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 1)
1577 ->field_string[hexidx]);
1578 }
1579 fprintf(stdout, "\n");
1580 }
1581
1582 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1583 ID3_CD_ID_FRAME) { // TODO: print hex representation
1584 uint8_t tracklistings = 0;
1585 if (target_frameinfo->ID3v2_Frame_Fields->field_length >= 16) {
1586 tracklistings = target_frameinfo->ID3v2_Frame_Fields->field_length / 8;
1587 fprintf(stdout,
1588 "(Music CD Identifier) : Entries for %u tracks + leadout "
1589 "track.\n Hex: 0x",
1590 tracklistings - 1);
1591 } else {
1592 fprintf(stdout,
1593 "(Music CD Identifier) : Unknown format (less then 16 "
1594 "bytes).\n Hex: 0x");
1595 }
1596 for (uint16_t hexidx = 1;
1597 hexidx < target_frameinfo->ID3v2_Frame_Fields->field_length + 1;
1598 hexidx++) {
1599 fprintf(stdout,
1600 "%02X",
1601 (uint8_t)target_frameinfo->ID3v2_Frame_Fields
1602 ->field_string[hexidx - 1]);
1603 if (hexidx % 4 == 0)
1604 fprintf(stdout, " ");
1605 }
1606 fprintf(stdout, "\n");
1607
1608 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1609 ID3_DESCRIBED_TEXT_FRAME) {
1610 fprintf(stdout,
1611 "(%s, lang=%s, desc[",
1612 APar_GetTextEncoding(target_frameinfo,
1613 target_frameinfo->ID3v2_Frame_Fields + 2),
1614 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
1615 APar_Print_ID3TextField(target_frameinfo,
1616 target_frameinfo->ID3v2_Frame_Fields + 2);
1617 fprintf(stdout, "]) : ");
1618 APar_Print_ID3TextField(
1619 target_frameinfo, target_frameinfo->ID3v2_Frame_Fields + 3, true);
1620
1621 } else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
1622 ID3_ATTACHED_PICTURE_FRAME) {
1623 fprintf(
1624 stdout,
1625 "(type=0x%02X-'%s', mimetype=%s, %s, desc[",
1626 (uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 2)->field_string[0],
1627 ImageTypeList[(uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 2)
1628 ->field_string[0]]
1629 .imagetype_str,
1630 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_string,
1631 APar_GetTextEncoding(target_frameinfo,
1632 target_frameinfo->ID3v2_Frame_Fields + 1));
1633 APar_Print_ID3TextField(target_frameinfo,
1634 target_frameinfo->ID3v2_Frame_Fields + 3);
1635 if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags,
1636 ID32_FRAMEFLAG_COMPRESSED)) {
1637 fprintf(stdout,
1638 "]) : %" PRIu32 " bytes (%" PRIu32 " compressed)\n",
1639 (target_frameinfo->ID3v2_Frame_Fields + 4)->field_length,
1640 target_frameinfo->ID3v2_Frame_Length);
1641 } else {
1642 fprintf(stdout,
1643 "]) : %" PRIu32 " bytes\n",
1644 (target_frameinfo->ID3v2_Frame_Fields + 4)->field_length);
1645 }
1646
1647 } else if (target_frameinfo->ID3v2_FrameType == ID3_ATTACHED_OBJECT_FRAME) {
1648 fprintf(stdout, "(filename=");
1649 APar_Print_ID3TextField(target_frameinfo,
1650 target_frameinfo->ID3v2_Frame_Fields + 2);
1651 fprintf(stdout,
1652 ", mimetype=%s, desc[",
1653 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
1654 APar_Print_ID3TextField(target_frameinfo,
1655 target_frameinfo->ID3v2_Frame_Fields + 3);
1656 if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags,
1657 ID32_FRAMEFLAG_COMPRESSED)) {
1658 fprintf(stdout,
1659 "]) : %" PRIu32 " bytes (%" PRIu32 " compressed)\n",
1660 (target_frameinfo->ID3v2_Frame_Fields + 4)->field_length,
1661 target_frameinfo->ID3v2_Frame_Length);
1662 } else {
1663 fprintf(stdout,
1664 "]) : %" PRIu32 " bytes\n",
1665 (target_frameinfo->ID3v2_Frame_Fields + 4)->field_length);
1666 }
1667
1668 } else if (target_frameinfo->ID3v2_FrameType == ID3_GROUP_ID_FRAME) {
1669 fprintf(
1670 stdout,
1671 "(owner='%s') : 0x%02X",
1672 target_frameinfo->ID3v2_Frame_Fields->field_string,
1673 (uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string[0]);
1674 if ((target_frameinfo->ID3v2_Frame_Fields + 2)->field_length > 0) {
1675 fprintf(stdout,
1676 "; groupdata='%s'\n",
1677 (target_frameinfo->ID3v2_Frame_Fields + 2)->field_string);
1678 } else {
1679 fprintf(stdout, "\n");
1680 }
1681
1682 } else if (target_frameinfo->ID3v2_FrameType == ID3_PRIVATE_FRAME) {
1683 fprintf(stdout,
1684 "(owner='%s') : %s\n",
1685 target_frameinfo->ID3v2_Frame_Fields->field_string,
1686 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
1687
1688 } else if (target_frameinfo->ID3v2_FrameType == ID3_SIGNATURE_FRAME) {
1689 fprintf(stdout,
1690 "{GID=0x%02X) : %s\n",
1691 (uint8_t)target_frameinfo->ID3v2_Frame_Fields->field_string[0],
1692 (target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
1693
1694 } else if (target_frameinfo->ID3v2_FrameType == ID3_PLAYCOUNTER_FRAME) {
1695 if (target_frameinfo->ID3v2_Frame_Fields->field_length == 4) {
1696 fprintf(stdout,
1697 ": %" PRIu32 "\n",
1698 syncsafe32_to_UInt32(
1699 target_frameinfo->ID3v2_Frame_Fields->field_string));
1700 } else if (target_frameinfo->ID3v2_Frame_Fields->field_length > 4) {
1701 fprintf(stdout,
1702 ": %" PRIu64 "\n",
1703 syncsafeXX_to_UInt64(
1704 target_frameinfo->ID3v2_Frame_Fields->field_string,
1705 target_frameinfo->ID3v2_Frame_Fields->field_length));
1706 }
1707
1708 } else if (target_frameinfo->ID3v2_FrameType == ID3_POPULAR_FRAME) {
1709 fprintf(
1710 stdout,
1711 "(owner='%s') : %u",
1712 target_frameinfo->ID3v2_Frame_Fields->field_string,
1713 (uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string[0]);
1714 if ((target_frameinfo->ID3v2_Frame_Fields + 2)->field_length > 0) {
1715 if ((target_frameinfo->ID3v2_Frame_Fields + 2)->field_length == 4) {
1716 fprintf(
1717 stdout,
1718 "; playcount=%" PRIu32 "\n",
1719 syncsafe32_to_UInt32(
1720 (target_frameinfo->ID3v2_Frame_Fields + 2)->field_string));
1721 } else if ((target_frameinfo->ID3v2_Frame_Fields + 2)->field_length >
1722 4) {
1723 fprintf(
1724 stdout,
1725 "; playcount=%" PRIu64 "\n",
1726 syncsafeXX_to_UInt64(
1727 (target_frameinfo->ID3v2_Frame_Fields + 2)->field_string,
1728 (target_frameinfo->ID3v2_Frame_Fields + 2)->field_length));
1729 } else {
1730 fprintf(stdout,
1731 "\n"); // don't know what it was supposed to be, so skip it
1732 }
1733 } else {
1734 fprintf(stdout, "\n");
1735 }
1736
1737 } else {
1738 fprintf(stdout,
1739 " [idx=%u;%d]\n",
1740 frame_comp_idx,
1741 FrameTypeConstructionList[frame_comp_idx].ID3_FrameType);
1742 }
1743 target_frameinfo = target_frameinfo->ID3v2_NextFrame;
1744 }
1745 free(id32_level);
1746 id32_level = NULL;
1747 return;
12561748 }
12571749
12581750 ///////////////////////////////////////////////////////////////////////////////////////
1259 // metadata scheme searches //
1751 // metadata scheme searches //
12601752 ///////////////////////////////////////////////////////////////////////////////////////
12611753
1262 void APar_Print_metachild_atomcontents(uint8_t track_num, short metachild_atom, bool quantum_listing) {
1263 if (memcmp(parsedAtoms[metachild_atom].AtomicName, "ID32", 4) == 0) {
1264 APar_ID32_ScanID3Tag(source_file, &parsedAtoms[metachild_atom]);
1265 APar_Print_ID3v2_tags(&parsedAtoms[metachild_atom]);
1266 }
1267 return;
1268 }
1269
1270 void APar_PrintMetaChildren(AtomicInfo* metaAtom, AtomicInfo* hdlrAtom, bool quantum_listing) {
1271 if (metaAtom != NULL && hdlrAtom != NULL) {
1272 if (hdlrAtom->ancillary_data == 0x49443332) {
1273 for (int i=metaAtom->NextAtomNumber; i < atom_number; i++) {
1274 if ( parsedAtoms[i].AtomicLevel <= metaAtom->AtomicLevel ) break; //we've gone too far
1275 if ( parsedAtoms[i].AtomicLevel == metaAtom->AtomicLevel + 1 ) APar_Print_metachild_atomcontents(0, i, quantum_listing);
1276 }
1277 }
1278 }
1279 return;
1754 void APar_Print_metachild_atomcontents(uint8_t track_num,
1755 short metachild_atom,
1756 bool quantum_listing) {
1757 if (memcmp(parsedAtoms[metachild_atom].AtomicName, "ID32", 4) == 0) {
1758 APar_ID32_ScanID3Tag(source_file, &parsedAtoms[metachild_atom]);
1759 APar_Print_ID3v2_tags(&parsedAtoms[metachild_atom]);
1760 }
1761 return;
1762 }
1763
1764 void APar_PrintMetaChildren(AtomicInfo *metaAtom,
1765 AtomicInfo *hdlrAtom,
1766 bool quantum_listing) {
1767 if (metaAtom != NULL && hdlrAtom != NULL) {
1768 if (hdlrAtom->ancillary_data == 0x49443332) {
1769 for (int i = metaAtom->NextAtomNumber; i < atom_number; i++) {
1770 if (parsedAtoms[i].AtomicLevel <= metaAtom->AtomicLevel)
1771 break; // we've gone too far
1772 if (parsedAtoms[i].AtomicLevel == metaAtom->AtomicLevel + 1)
1773 APar_Print_metachild_atomcontents(0, i, quantum_listing);
1774 }
1775 }
1776 }
1777 return;
12801778 }
12811779
12821780 void APar_PrintID32Metadata(bool quantum_listing) {
1283 uint8_t total_tracks = 0;
1284 uint8_t a_track = 0;
1285 AtomicInfo* metaAtom = NULL;
1286 AtomicInfo* metahandlerAtom = NULL;
1287 char trackmeta_atom_path[50];
1288
1289 printBOM();
1290
1291 //file level
1292 metaAtom = APar_FindAtom("meta", false, VERSIONED_ATOM, 0);
1293 metahandlerAtom = APar_FindAtom("meta.hdlr", false, VERSIONED_ATOM, 0);
1294 APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
1295
1296 //movie level
1297 metaAtom = APar_FindAtom("moov.meta", false, VERSIONED_ATOM, 0);
1298 metahandlerAtom = APar_FindAtom("moov.meta.hdlr", false, VERSIONED_ATOM, 0);
1299 APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
1300
1301 //track level
1302 APar_FindAtomInTrack(total_tracks, a_track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here.
1303 for (uint8_t i = 1; i <= total_tracks; i++) {
1304 memset(&trackmeta_atom_path, 0, 50);
1305 sprintf(trackmeta_atom_path, "moov.trak[%u].meta", i);
1306
1307 metaAtom = APar_FindAtom(trackmeta_atom_path, false, VERSIONED_ATOM, 0);
1308 sprintf(trackmeta_atom_path, "moov.trak[%u].meta.hdlr", i);
1309 metahandlerAtom = APar_FindAtom(trackmeta_atom_path, false, VERSIONED_ATOM, 0);
1310 APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
1311 }
1312 return;
1781 uint8_t total_tracks = 0;
1782 uint8_t a_track = 0;
1783 AtomicInfo *metaAtom = NULL;
1784 AtomicInfo *metahandlerAtom = NULL;
1785 char trackmeta_atom_path[50];
1786
1787 printBOM();
1788
1789 // file level
1790 metaAtom = APar_FindAtom("meta", false, VERSIONED_ATOM, 0);
1791 metahandlerAtom = APar_FindAtom("meta.hdlr", false, VERSIONED_ATOM, 0);
1792 APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
1793
1794 // movie level
1795 metaAtom = APar_FindAtom("moov.meta", false, VERSIONED_ATOM, 0);
1796 metahandlerAtom = APar_FindAtom("moov.meta.hdlr", false, VERSIONED_ATOM, 0);
1797 APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
1798
1799 // track level
1800 APar_FindAtomInTrack(total_tracks,
1801 a_track,
1802 NULL); // With track_num set to 0, it will return the
1803 // total trak atom into total_tracks here.
1804 for (uint8_t i = 1; i <= total_tracks; i++) {
1805 memset(&trackmeta_atom_path, 0, 50);
1806 sprintf(trackmeta_atom_path, "moov.trak[%u].meta", i);
1807
1808 metaAtom = APar_FindAtom(trackmeta_atom_path, false, VERSIONED_ATOM, 0);
1809 sprintf(trackmeta_atom_path, "moov.trak[%u].meta.hdlr", i);
1810 metahandlerAtom =
1811 APar_FindAtom(trackmeta_atom_path, false, VERSIONED_ATOM, 0);
1812 APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
1813 }
1814 return;
13131815 }
13141816
13151817 /*----------------------
13161818 APar_Print_ISO_UserData_per_track
1317 quantum_listing - controls whether to simply print each asset, or preface each asset with "movie level"
1318
1319 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,
1320 then if the atom after udta is of a greater level, read in from the file & print out what it contains.
1819 quantum_listing - controls whether to simply print each asset, or
1820 preface each asset with "movie level"
1821
1822 This will only show what is under moov.trak.udta atoms (not moov.udta). Get
1823 the total number of tracks; construct the moov.trak[index].udta path to find,
1824 then if the atom after udta is of a greater level, read in from
1825 the file & print out what it contains.
13211826 ----------------------*/
13221827 void APar_PrintUserDataAssests(bool quantum_listing) {
1323 printBOM();
1324
1325 AtomicInfo* udtaAtom = APar_FindAtom("moov.udta", false, SIMPLE_ATOM, 0);
1326
1327 if (udtaAtom != NULL) {
1328 for (int i=udtaAtom->NextAtomNumber; i < atom_number; i++) {
1329 if ( parsedAtoms[i].AtomicLevel <= udtaAtom->AtomicLevel ) break; //we've gone too far
1330 if ( parsedAtoms[i].AtomicLevel == udtaAtom->AtomicLevel + 1 ) APar_Print_single_userdata_atomcontents(0, i, quantum_listing);
1331 }
1332 }
1333 APar_PrintID32Metadata(quantum_listing);
1334 APar_Print_APuuid_atoms(NULL, NULL, PRINT_DATA);
1335 return;
1828 printBOM();
1829
1830 AtomicInfo *udtaAtom = APar_FindAtom("moov.udta", false, SIMPLE_ATOM, 0);
1831
1832 if (udtaAtom != NULL) {
1833 for (int i = udtaAtom->NextAtomNumber; i < atom_number; i++) {
1834 if (parsedAtoms[i].AtomicLevel <= udtaAtom->AtomicLevel)
1835 break; // we've gone too far
1836 if (parsedAtoms[i].AtomicLevel == udtaAtom->AtomicLevel + 1)
1837 APar_Print_single_userdata_atomcontents(0, i, quantum_listing);
1838 }
1839 }
1840 APar_PrintID32Metadata(quantum_listing);
1841 APar_Print_APuuid_atoms(NULL, NULL, PRINT_DATA);
1842 return;
13361843 }
13371844
13381845 /*----------------------
13391846 APar_Print_ISO_UserData_per_track
13401847
1341 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,
1342 then if the atom after udta is of a greater level, read in from the file & print out what it contains.
1848 This will only show what is under moov.trak.udta atoms (not moov.udta). Get
1849 the total number of tracks; construct the moov.trak[index].udta path to find,
1850 then if the atom after udta is of a greater level, read in from
1851 the file & print out what it contains.
13431852 ----------------------*/
13441853 void APar_Print_ISO_UserData_per_track() {
1345 uint8_t total_tracks = 0;
1346 uint8_t a_track = 0;//unused
1347 short a_trak_atom = 0;
1348 char iso_atom_path[400];
1349 AtomicInfo* trak_udtaAtom = NULL;
1350
1351 APar_FindAtomInTrack(total_tracks, a_track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here.
1352
1353 for (uint8_t i = 1; i <= total_tracks; i++) {
1354 memset(&iso_atom_path, 0, 400);
1355 sprintf(iso_atom_path, "moov.trak[%u].udta", i);
1356
1357 trak_udtaAtom = APar_FindAtom(iso_atom_path, false, SIMPLE_ATOM, 0);
1358
1359 if (trak_udtaAtom != NULL && parsedAtoms[trak_udtaAtom->NextAtomNumber].AtomicLevel == trak_udtaAtom->AtomicLevel+1) {
1360 a_trak_atom = trak_udtaAtom->NextAtomNumber;
1361 while (parsedAtoms[a_trak_atom].AtomicLevel > trak_udtaAtom->AtomicLevel) { //only work on moov.trak[i].udta's child atoms
1362
1363 if (parsedAtoms[a_trak_atom].AtomicLevel == trak_udtaAtom->AtomicLevel+1) APar_Print_single_userdata_atomcontents(i, a_trak_atom, true);
1364
1365 a_trak_atom = parsedAtoms[a_trak_atom].NextAtomNumber;
1366 }
1367 }
1368 }
1369 APar_PrintUserDataAssests(true);
1370 return;
1854 uint8_t total_tracks = 0;
1855 uint8_t a_track = 0; // unused
1856 short a_trak_atom = 0;
1857 char iso_atom_path[400];
1858 AtomicInfo *trak_udtaAtom = NULL;
1859
1860 APar_FindAtomInTrack(total_tracks,
1861 a_track,
1862 NULL); // With track_num set to 0, it will return the
1863 // total trak atom into total_tracks here.
1864
1865 for (uint8_t i = 1; i <= total_tracks; i++) {
1866 memset(&iso_atom_path, 0, 400);
1867 sprintf(iso_atom_path, "moov.trak[%u].udta", i);
1868
1869 trak_udtaAtom = APar_FindAtom(iso_atom_path, false, SIMPLE_ATOM, 0);
1870
1871 if (trak_udtaAtom != NULL &&
1872 parsedAtoms[trak_udtaAtom->NextAtomNumber].AtomicLevel ==
1873 trak_udtaAtom->AtomicLevel + 1) {
1874 a_trak_atom = trak_udtaAtom->NextAtomNumber;
1875 while (
1876 parsedAtoms[a_trak_atom].AtomicLevel >
1877 trak_udtaAtom
1878 ->AtomicLevel) { // only work on moov.trak[i].udta's child atoms
1879
1880 if (parsedAtoms[a_trak_atom].AtomicLevel ==
1881 trak_udtaAtom->AtomicLevel + 1)
1882 APar_Print_single_userdata_atomcontents(i, a_trak_atom, true);
1883
1884 a_trak_atom = parsedAtoms[a_trak_atom].NextAtomNumber;
1885 }
1886 }
1887 }
1888 APar_PrintUserDataAssests(true);
1889 return;
13711890 }
13721891
13731892 ///////////////////////////////////////////////////////////////////////////////////////
1374 // Atom Tree //
1893 // Atom Tree //
13751894 ///////////////////////////////////////////////////////////////////////////////////////
13761895
13771896 /*----------------------
13781897 APar_PrintAtomicTree
13791898
1380 Following the linked list (by NextAtomNumber), list each atom as they exist in the hieararchy, reflecting positions of moving, eliminating & additions.
1381 This listing can occur during the course of tagging as well to assist in diagnosing problems.
1899 Following the linked list (by NextAtomNumber), list each atom as they exist
1900 in the hieararchy, reflecting positions of moving, eliminating & additions. This
1901 listing can occur during the course of tagging as well to assist in diagnosing
1902 problems.
13821903 ----------------------*/
13831904 void APar_PrintAtomicTree() {
1384 bool unknown_atom = false;
1385 char* tree_padding = (char*)malloc(sizeof(char)*126); //for a 25-deep atom tree (4 spaces per atom)+single space+term.
1386 uint32_t freeSpace = 0;
1387 short thisAtomNumber = 0;
1388
1389 printBOM();
1390
1391 //loop through each atom in the struct array (which holds the offset info/data)
1392 while (true) {
1393 AtomicInfo* thisAtom = &parsedAtoms[thisAtomNumber];
1394 memset(tree_padding, 0, sizeof(char)*126);
1395 memset(twenty_byte_buffer, 0, sizeof(char)*20);
1396
1397 if (thisAtom->uuid_ap_atomname != NULL) {
1398 isolat1ToUTF8((unsigned char*)twenty_byte_buffer, 10, (unsigned char*)thisAtom->uuid_ap_atomname, 4); //converts iso8859 © in '©ART' to a 2byte utf8 © glyph
1399 } else {
1400 isolat1ToUTF8((unsigned char*)twenty_byte_buffer, 10, (unsigned char*)thisAtom->AtomicName, 4); //converts iso8859 © in '©ART' to a 2byte utf8 © glyph
1401 }
1402
1403
1404 strcpy(tree_padding, "");
1405 if ( thisAtom->AtomicLevel != 1 ) {
1406 for (uint8_t pad=1; pad < thisAtom->AtomicLevel; pad++) {
1407 strcat(tree_padding, " "); // if the atom depth is over 1, then add spaces before text starts to form the tree
1408 }
1409 strcat(tree_padding, " "); // add a single space
1410 }
1411
1412 if (thisAtom->AtomicLength == 0) {
1413 fprintf(stdout,
1414 "%sAtom %s @ %" PRIu64 " of size: %" PRIu64 " (%" PRIu64 "*), ends @ %" PRIu64 "\n",
1415 tree_padding, twenty_byte_buffer, thisAtom->AtomicStart,
1416 ( (uint64_t)file_size - thisAtom->AtomicStart),
1417 thisAtom->AtomicLength, (uint64_t)file_size );
1418 fprintf(stdout,
1419 "\t\t\t (*)denotes length of atom goes to End-of-File\n");
1420
1421 } else if (thisAtom->AtomicLength == 1) {
1422 fprintf(stdout, "%sAtom %s @ %" PRIu64 " of size: %" PRIu64 " (^), ends @ %" PRIu64 "\n", tree_padding, twenty_byte_buffer, thisAtom->AtomicStart, thisAtom->AtomicLengthExtended, (thisAtom->AtomicStart + thisAtom->AtomicLengthExtended) );
1423 fprintf(stdout, "\t\t\t (^)denotes a 64-bit atom length\n");
1424
1425 //uuid atoms of any sort
1426 } else if (thisAtom->AtomicClassification == EXTENDED_ATOM && thisAtom->uuid_style == UUID_DEPRECATED_FORM) {
1427
1428 if (UnicodeOutputStatus == WIN32_UTF16) {
1429 fprintf(stdout, "%sAtom uuid=", tree_padding);
1430 APar_fprintf_UTF8_data(twenty_byte_buffer);
1431 fprintf(stdout, " @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64 "\n", thisAtom->AtomicStart, thisAtom->AtomicLength, (thisAtom->AtomicStart + thisAtom->AtomicLength) );
1432 } else {
1433 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) );
1434 }
1435
1436 } else if (thisAtom->AtomicClassification == EXTENDED_ATOM && thisAtom->uuid_style != UUID_DEPRECATED_FORM) {
1437 if (thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE) {
1438 fprintf(stdout, "%sAtom uuid=", tree_padding);
1439 APar_print_uuid( (ap_uuid_t*)thisAtom->AtomicName, false);
1440 fprintf(stdout, "(APuuid=%s) @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64 "\n", twenty_byte_buffer, thisAtom->AtomicStart, thisAtom->AtomicLength, (thisAtom->AtomicStart + thisAtom->AtomicLength) );
1441 } else {
1442 fprintf(stdout, "%sAtom uuid=", tree_padding);
1443 APar_print_uuid( (ap_uuid_t*)thisAtom->AtomicName, false);
1444 fprintf(stdout, " @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64 "\n", thisAtom->AtomicStart, thisAtom->AtomicLength, (thisAtom->AtomicStart + thisAtom->AtomicLength) );
1445 }
1446
1447 //3gp assets (most of them anyway)
1448 } else if (thisAtom->AtomicClassification == PACKED_LANG_ATOM) {
1449 unsigned char unpacked_lang[3];
1450 APar_UnpackLanguage(unpacked_lang, thisAtom->AtomicLanguage);
1451
1452 if (UnicodeOutputStatus == WIN32_UTF16) {
1453 fprintf(stdout, "%sAtom ", tree_padding);
1454 APar_fprintf_UTF8_data(twenty_byte_buffer);
1455 fprintf(stdout, " [%s] @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64 "\n", unpacked_lang, thisAtom->AtomicStart, thisAtom->AtomicLength, (thisAtom->AtomicStart + thisAtom->AtomicLength) );
1456 } else {
1457 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) );
1458 }
1459
1460 //all other atoms (the bulk of them will fall here)
1461 } else {
1462
1463 if (UnicodeOutputStatus == WIN32_UTF16) {
1464 fprintf(stdout, "%sAtom ", tree_padding);
1465 APar_fprintf_UTF8_data(twenty_byte_buffer);
1466 fprintf(stdout, " @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64, thisAtom->AtomicStart, thisAtom->AtomicLength, (thisAtom->AtomicStart + thisAtom->AtomicLength) );
1467 } else {
1468 fprintf(stdout, "%sAtom %s @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64, tree_padding, twenty_byte_buffer, thisAtom->AtomicStart, thisAtom->AtomicLength, (thisAtom->AtomicStart + thisAtom->AtomicLength) );
1469 }
1470
1471 if (thisAtom->AtomicContainerState == UNKNOWN_ATOM_TYPE) {
1472 for (uint8_t i = 0; i < (5-thisAtom->AtomicLevel); i ++) {
1473 fprintf(stdout, "\t");
1474 }
1475 fprintf(stdout, "\t\t\t ~\n");
1476 unknown_atom = true;
1477 } else {
1478 fprintf(stdout, "\n");
1479 }
1480 }
1481
1482 //simple tally & percentage of free space info
1483 if (memcmp(thisAtom->AtomicName, "free", 4) == 0) {
1484 freeSpace = freeSpace+thisAtom->AtomicLength;
1485 }
1486 //this is where the *raw* audio/video file is, the rest is container-related fluff.
1487 if ( (memcmp(thisAtom->AtomicName, "mdat", 4) == 0) && (thisAtom->AtomicLength > 100) ) {
1488 mdatData+= thisAtom->AtomicLength;
1489 } else if ( memcmp(thisAtom->AtomicName, "mdat", 4) == 0 && thisAtom->AtomicLength == 0 ) { //mdat.length = 0 = ends at EOF
1490 mdatData = file_size - thisAtom->AtomicStart;
1491 } else if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 && thisAtom->AtomicLengthExtended != 0 ) {
1492 mdatData+= thisAtom->AtomicLengthExtended; //this is still adding a (limited) uint64_t into a uint32_t
1493 }
1494
1495 if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) {
1496 break;
1497 } else {
1498 thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber;
1499 }
1500 }
1501
1502 if (unknown_atom) {
1503 fprintf(stdout, "\n ~ denotes an unknown atom\n");
1504 }
1505
1506 fprintf(stdout, "------------------------------------------------------\n");
1507 fprintf(stdout, "Total size: %" PRIu64 " bytes; ", (uint64_t)file_size);
1508 fprintf(stdout, "%i atoms total.\n", atom_number-1);
1509 fprintf(stdout, "Media data: %" PRIu64 " bytes; %" PRIu64 " bytes all other atoms (%2.3lf%% atom overhead).\n",
1510 mdatData, file_size - mdatData,
1511 (double)(file_size - mdatData)/(double)file_size * 100.0 );
1512
1513 fprintf(stdout, "Total free atom space: %" PRIu32 " bytes; %2.3lf%% waste.",
1514 freeSpace, (double)freeSpace/(double)file_size * 100.0 );
1515
1516 if (freeSpace) {
1517 dynUpd.updage_by_padding = false;
1518 //APar_DetermineDynamicUpdate(true); //gets the size of the padding
1519 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'
1520 if (!moov_atom_was_mooved) {
1521 fprintf(stdout, " Padding available: %" PRIu64 " bytes.", dynUpd.padding_bytes);
1522 }
1523 }
1524 if (gapless_void_padding > 0) {
1525 fprintf(stdout, "\nGapless playback null space at end of file: %" PRIu64 " bytes.", gapless_void_padding);
1526 }
1527 fprintf(stdout, "\n------------------------------------------------------\n");
1528 ShowVersionInfo();
1529 fprintf(stdout, "------------------------------------------------------\n");
1530
1531 free(tree_padding);
1532 tree_padding = NULL;
1533
1534 return;
1905 bool unknown_atom = false;
1906 char *tree_padding = (char *)malloc(
1907 sizeof(char) *
1908 126); // for a 25-deep atom tree (4 spaces per atom)+single space+term.
1909 uint32_t freeSpace = 0;
1910 short thisAtomNumber = 0;
1911
1912 printBOM();
1913
1914 // loop through each atom in the struct array (which holds the offset
1915 // info/data)
1916 while (true) {
1917 AtomicInfo *thisAtom = &parsedAtoms[thisAtomNumber];
1918 memset(tree_padding, 0, sizeof(char) * 126);
1919 memset(twenty_byte_buffer, 0, sizeof(char) * 20);
1920
1921 if (thisAtom->uuid_ap_atomname != NULL) {
1922 isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
1923 10,
1924 (unsigned char *)thisAtom->uuid_ap_atomname,
1925 4); // converts iso8859 © in '©ART' to a 2byte utf8 © glyph
1926 } else {
1927 isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
1928 10,
1929 (unsigned char *)thisAtom->AtomicName,
1930 4); // converts iso8859 © in '©ART' to a 2byte utf8 © glyph
1931 }
1932
1933 strcpy(tree_padding, "");
1934 if (thisAtom->AtomicLevel != 1) {
1935 for (uint8_t pad = 1; pad < thisAtom->AtomicLevel; pad++) {
1936 strcat(tree_padding,
1937 " "); // if the atom depth is over 1, then add spaces before
1938 // text starts to form the tree
1939 }
1940 strcat(tree_padding, " "); // add a single space
1941 }
1942
1943 if (thisAtom->AtomicLength == 0) {
1944 fprintf(stdout,
1945 "%sAtom %s @ %" PRIu64 " of size: %" PRIu64 " (%" PRIu64
1946 "*), ends @ %" PRIu64 "\n",
1947 tree_padding,
1948 twenty_byte_buffer,
1949 thisAtom->AtomicStart,
1950 ((uint64_t)file_size - thisAtom->AtomicStart),
1951 thisAtom->AtomicLength,
1952 (uint64_t)file_size);
1953 fprintf(stdout, "\t\t\t (*)denotes length of atom goes to End-of-File\n");
1954
1955 } else if (thisAtom->AtomicLength == 1) {
1956 fprintf(stdout,
1957 "%sAtom %s @ %" PRIu64 " of size: %" PRIu64
1958 " (^), ends @ %" PRIu64 "\n",
1959 tree_padding,
1960 twenty_byte_buffer,
1961 thisAtom->AtomicStart,
1962 thisAtom->AtomicLengthExtended,
1963 (thisAtom->AtomicStart + thisAtom->AtomicLengthExtended));
1964 fprintf(stdout, "\t\t\t (^)denotes a 64-bit atom length\n");
1965
1966 // uuid atoms of any sort
1967 } else if (thisAtom->AtomicClassification == EXTENDED_ATOM &&
1968 thisAtom->uuid_style == UUID_DEPRECATED_FORM) {
1969
1970 if (UnicodeOutputStatus == WIN32_UTF16) {
1971 fprintf(stdout, "%sAtom uuid=", tree_padding);
1972 APar_fprintf_UTF8_data(twenty_byte_buffer);
1973 fprintf(stdout,
1974 " @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64 "\n",
1975 thisAtom->AtomicStart,
1976 thisAtom->AtomicLength,
1977 (thisAtom->AtomicStart + thisAtom->AtomicLength));
1978 } else {
1979 fprintf(stdout,
1980 "%sAtom uuid=%s @ %" PRIu64 " of size: %" PRIu64
1981 ", ends @ %" PRIu64 "\n",
1982 tree_padding,
1983 twenty_byte_buffer,
1984 thisAtom->AtomicStart,
1985 thisAtom->AtomicLength,
1986 (thisAtom->AtomicStart + thisAtom->AtomicLength));
1987 }
1988
1989 } else if (thisAtom->AtomicClassification == EXTENDED_ATOM &&
1990 thisAtom->uuid_style != UUID_DEPRECATED_FORM) {
1991 if (thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE) {
1992 fprintf(stdout, "%sAtom uuid=", tree_padding);
1993 APar_print_uuid((ap_uuid_t *)thisAtom->AtomicName, false);
1994 fprintf(stdout,
1995 "(APuuid=%s) @ %" PRIu64 " of size: %" PRIu64
1996 ", ends @ %" PRIu64 "\n",
1997 twenty_byte_buffer,
1998 thisAtom->AtomicStart,
1999 thisAtom->AtomicLength,
2000 (thisAtom->AtomicStart + thisAtom->AtomicLength));
2001 } else {
2002 fprintf(stdout, "%sAtom uuid=", tree_padding);
2003 APar_print_uuid((ap_uuid_t *)thisAtom->AtomicName, false);
2004 fprintf(stdout,
2005 " @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64 "\n",
2006 thisAtom->AtomicStart,
2007 thisAtom->AtomicLength,
2008 (thisAtom->AtomicStart + thisAtom->AtomicLength));
2009 }
2010
2011 // 3gp assets (most of them anyway)
2012 } else if (thisAtom->AtomicClassification == PACKED_LANG_ATOM) {
2013 unsigned char unpacked_lang[3];
2014 APar_UnpackLanguage(unpacked_lang, thisAtom->AtomicLanguage);
2015
2016 if (UnicodeOutputStatus == WIN32_UTF16) {
2017 fprintf(stdout, "%sAtom ", tree_padding);
2018 APar_fprintf_UTF8_data(twenty_byte_buffer);
2019 fprintf(stdout,
2020 " [%s] @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64
2021 "\n",
2022 unpacked_lang,
2023 thisAtom->AtomicStart,
2024 thisAtom->AtomicLength,
2025 (thisAtom->AtomicStart + thisAtom->AtomicLength));
2026 } else {
2027 fprintf(stdout,
2028 "%sAtom %s [%s] @ %" PRIu64 " of size: %" PRIu64
2029 ", ends @ %" PRIu64 "\n",
2030 tree_padding,
2031 twenty_byte_buffer,
2032 unpacked_lang,
2033 thisAtom->AtomicStart,
2034 thisAtom->AtomicLength,
2035 (thisAtom->AtomicStart + thisAtom->AtomicLength));
2036 }
2037
2038 // all other atoms (the bulk of them will fall here)
2039 } else {
2040
2041 if (UnicodeOutputStatus == WIN32_UTF16) {
2042 fprintf(stdout, "%sAtom ", tree_padding);
2043 APar_fprintf_UTF8_data(twenty_byte_buffer);
2044 fprintf(stdout,
2045 " @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64,
2046 thisAtom->AtomicStart,
2047 thisAtom->AtomicLength,
2048 (thisAtom->AtomicStart + thisAtom->AtomicLength));
2049 } else {
2050 fprintf(stdout,
2051 "%sAtom %s @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64,
2052 tree_padding,
2053 twenty_byte_buffer,
2054 thisAtom->AtomicStart,
2055 thisAtom->AtomicLength,
2056 (thisAtom->AtomicStart + thisAtom->AtomicLength));
2057 }
2058
2059 if (thisAtom->AtomicContainerState == UNKNOWN_ATOM_TYPE) {
2060 for (uint8_t i = 0; i < (5 - thisAtom->AtomicLevel); i++) {
2061 fprintf(stdout, "\t");
2062 }
2063 fprintf(stdout, "\t\t\t ~\n");
2064 unknown_atom = true;
2065 } else {
2066 fprintf(stdout, "\n");
2067 }
2068 }
2069
2070 // simple tally & percentage of free space info
2071 if (memcmp(thisAtom->AtomicName, "free", 4) == 0) {
2072 freeSpace = freeSpace + thisAtom->AtomicLength;
2073 }
2074 // this is where the *raw* audio/video file is, the rest is
2075 // container-related fluff.
2076 if ((memcmp(thisAtom->AtomicName, "mdat", 4) == 0) &&
2077 (thisAtom->AtomicLength > 100)) {
2078 mdatData += thisAtom->AtomicLength;
2079 } else if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 &&
2080 thisAtom->AtomicLength == 0) { // mdat.length = 0 = ends at EOF
2081 mdatData = file_size - thisAtom->AtomicStart;
2082 } else if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 &&
2083 thisAtom->AtomicLengthExtended != 0) {
2084 mdatData +=
2085 thisAtom->AtomicLengthExtended; // this is still adding a (limited)
2086 // uint64_t into a uint32_t
2087 }
2088
2089 if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) {
2090 break;
2091 } else {
2092 thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber;
2093 }
2094 }
2095
2096 if (unknown_atom) {
2097 fprintf(stdout, "\n ~ denotes an unknown atom\n");
2098 }
2099
2100 fprintf(stdout, "------------------------------------------------------\n");
2101 fprintf(stdout, "Total size: %" PRIu64 " bytes; ", (uint64_t)file_size);
2102 fprintf(stdout, "%i atoms total.\n", atom_number - 1);
2103 fprintf(stdout,
2104 "Media data: %" PRIu64 " bytes; %" PRIu64
2105 " bytes all other atoms (%2.3lf%% atom overhead).\n",
2106 mdatData,
2107 file_size - mdatData,
2108 (double)(file_size - mdatData) / (double)file_size * 100.0);
2109
2110 fprintf(stdout,
2111 "Total free atom space: %" PRIu32 " bytes; %2.3lf%% waste.",
2112 freeSpace,
2113 (double)freeSpace / (double)file_size * 100.0);
2114
2115 if (freeSpace) {
2116 dynUpd.updage_by_padding = false;
2117 // APar_DetermineDynamicUpdate(true); //gets the size of the padding
2118 APar_Optimize(
2119 true); // just to know if 'free' atoms can be considered padding, or (in
2120 // the case of say a faac file) it's *just* 'free'
2121 if (!moov_atom_was_mooved) {
2122 fprintf(stdout,
2123 " Padding available: %" PRIu64 " bytes.",
2124 dynUpd.padding_bytes);
2125 }
2126 }
2127 if (gapless_void_padding > 0) {
2128 fprintf(stdout,
2129 "\nGapless playback null space at end of file: %" PRIu64 " bytes.",
2130 gapless_void_padding);
2131 }
2132 fprintf(stdout, "\n------------------------------------------------------\n");
2133 ShowVersionInfo();
2134 fprintf(stdout, "------------------------------------------------------\n");
2135
2136 free(tree_padding);
2137 tree_padding = NULL;
2138
2139 return;
15352140 }
15362141
15372142 /*----------------------
15382143 APar_SimpleAtomPrintout
15392144
1540 print a simple flat list of atoms as they were created
2145 print a simple flat list of atoms as they were created
15412146 ----------------------*/
1542 void APar_SimpleAtomPrintout() { //loop through each atom in the struct array (which holds the offset info/data)
1543 printBOM();
1544
1545 for (int i=0; i < atom_number; i++) {
1546 AtomicInfo* thisAtom = &parsedAtoms[i];
1547
1548 fprintf(stdout, "%i - Atom \"%s\" (level %u) has next atom at #%i\n", i, thisAtom->AtomicName, thisAtom->AtomicLevel, thisAtom->NextAtomNumber);
1549 }
1550 fprintf(stdout, "Total of %i atoms.\n", atom_number-1);
1551 }
2147 void APar_SimpleAtomPrintout() { // loop through each atom in the struct array
2148 // (which holds the offset info/data)
2149 printBOM();
2150
2151 for (int i = 0; i < atom_number; i++) {
2152 AtomicInfo *thisAtom = &parsedAtoms[i];
2153
2154 fprintf(stdout,
2155 "%i - Atom \"%s\" (level %u) has next atom at #%i\n",
2156 i,
2157 thisAtom->AtomicName,
2158 thisAtom->AtomicLevel,
2159 thisAtom->NextAtomNumber);
2160 }
2161 fprintf(stdout, "Total of %i atoms.\n", atom_number - 1);
2162 }
11 /*
22 AtomicParsley - nsfile.mm
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
2525 /*----------------------
2626 APar_TestTracksForKind
2727
28 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
29 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
30 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
31 OS/Finder/iTunes that this file is .m4a or .m4v without having to change its extension based on what the tracks actually contain.
32
33 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.
34 TODO: address these issues.
28 By testing which tracks are contained within the file, for Mac OS X we can
29 avoid having to change file extension by instead using Finder.app metadata to
30 signal the same info as file extension. For each trak atom, find the 'stsd' atom
31 - its ancillary_data will contain the track type that is contained - the info is
32 filled in as the file was initially parsed in APar_ScanAtoms. Then using Mac OS
33 X Cocoa calls (in AP_NSFile_utils), set the Finder TYPE/CREATOR codes to signal
34 to the OS/Finder/iTunes that this file is .m4a or .m4v without having to change
35 its extension based on what the tracks actually contain.
36
37 There are 2 issues with this - iTunes requires the Quicktime
38 player type/creator codes for video that has multi-channel audio, and for
39 chapterized video files.
40 TODO: address these issues.
3541 ----------------------*/
3642 void APar_TestTracksForKind() {
37 uint8_t total_tracks = 0;
38 uint8_t track_num = 0;
39 AtomicInfo* codec_atom = NULL; //short codec_atom = 0;
40
41 //With track_num set to 0, it will return the total trak atom into total_tracks here.
42 APar_FindAtomInTrack(total_tracks, track_num, NULL);
43
44 if (total_tracks > 0) {
45 while (total_tracks > track_num) {
46 track_num+= 1;
47
48 codec_atom = APar_FindAtomInTrack(total_tracks, track_num, "stsd");
49 if (codec_atom == NULL) return;
50
51 //now test this trak's stsd codec against these 4cc codes:
52 switch(codec_atom->ancillary_data) {
53 //video types
54 case 0x61766331 : // "avc1"
55 track_codecs.has_avc1 = true;
56 break;
57 case 0x6D703476 : // "mp4v"
58 track_codecs.has_mp4v = true;
59 break;
60 case 0x64726D69 : // "drmi"
61 track_codecs.has_drmi = true;
62 break;
63
64 //audio types
65 case 0x616C6163 : // "alac"
66 track_codecs.has_alac = true;
67 break;
68 case 0x6D703461 : // "mp4a"
69 track_codecs.has_mp4a = true;
70 break;
71 case 0x64726D73 : // "drms"
72 track_codecs.has_drms = true;
73 break;
74
75 //chapterized types (audio podcasts or movies)
76 case 0x74657874 : // "text"
77 track_codecs.has_timed_text = true;
78 break;
79 case 0x6A706567 : // "jpeg"
80 track_codecs.has_timed_jpeg = true;
81 break;
82
83 //either podcast type (audio-only) or timed text subtitles
84 case 0x74783367 : // "tx3g"
85 track_codecs.has_timed_tx3g = true;
86 break;
87
88 //other
89 case 0x6D703473 : // "mp4s"
90 track_codecs.has_mp4s = true;
91 break;
92 case 0x72747020 : // "rtp "
93 track_codecs.has_rtp_hint = true;
94 break;
95 }
96 }
97 }
98 return;
43 uint8_t total_tracks = 0;
44 uint8_t track_num = 0;
45 AtomicInfo *codec_atom = NULL; // short codec_atom = 0;
46
47 // With track_num set to 0, it will return the total trak atom into
48 // total_tracks here.
49 APar_FindAtomInTrack(total_tracks, track_num, NULL);
50
51 if (total_tracks > 0) {
52 while (total_tracks > track_num) {
53 track_num += 1;
54
55 codec_atom = APar_FindAtomInTrack(total_tracks, track_num, "stsd");
56 if (codec_atom == NULL)
57 return;
58
59 // now test this trak's stsd codec against these 4cc codes:
60 switch (codec_atom->ancillary_data) {
61 // video types
62 case 0x61766331: // "avc1"
63 track_codecs.has_avc1 = true;
64 break;
65 case 0x6D703476: // "mp4v"
66 track_codecs.has_mp4v = true;
67 break;
68 case 0x64726D69: // "drmi"
69 track_codecs.has_drmi = true;
70 break;
71
72 // audio types
73 case 0x616C6163: // "alac"
74 track_codecs.has_alac = true;
75 break;
76 case 0x6D703461: // "mp4a"
77 track_codecs.has_mp4a = true;
78 break;
79 case 0x64726D73: // "drms"
80 track_codecs.has_drms = true;
81 break;
82
83 // chapterized types (audio podcasts or movies)
84 case 0x74657874: // "text"
85 track_codecs.has_timed_text = true;
86 break;
87 case 0x6A706567: // "jpeg"
88 track_codecs.has_timed_jpeg = true;
89 break;
90
91 // either podcast type (audio-only) or timed text subtitles
92 case 0x74783367: // "tx3g"
93 track_codecs.has_timed_tx3g = true;
94 break;
95
96 // other
97 case 0x6D703473: // "mp4s"
98 track_codecs.has_mp4s = true;
99 break;
100 case 0x72747020: // "rtp "
101 track_codecs.has_rtp_hint = true;
102 break;
103 }
104 }
105 }
106 return;
99107 }
100108
101
102 //TODO: there is a problem with this code seen in: "5.1channel audio-orig.mp4"
103 //it makes no difference what the file contains, iTunes won't see any (ANY) metadata if its hook/'M4A '.
104 //in fact, iTunes won't play the file at all
105 //changing the exact file (with all kinds of metadata) to TVOD/mpg4 - iTunes can play it fine, but doesn't fetch any metadata
109 // TODO: there is a problem with this code seen in: "5.1channel audio-orig.mp4"
110 // it makes no difference what the file contains, iTunes won't see any (ANY)
111 // metadata if its hook/'M4A '. in fact, iTunes won't play the file at all
112 // changing the exact file (with all kinds of metadata) to TVOD/mpg4 - iTunes
113 // can play it fine, but doesn't fetch any metadata
106114 //
107 //it might be beneficial to eval for channels and if its audio only & multichannel to NOT change the TYPE/creator codes
108
109 uint32_t APar_4CC_CreatorCode(const char* filepath, uint32_t new_type_code) {
110 uint32_t return_value = 0;
111 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
112
113 NSString *inFile = [NSString stringWithUTF8String: filepath];
114
115 if (new_type_code) {
116 NSNumber* creator_code = [NSNumber numberWithUnsignedLong:'hook'];
117 NSNumber* type_code = [NSNumber numberWithUnsignedLong:new_type_code];
118 NSDictionary* output_attributes = [NSDictionary dictionaryWithObjectsAndKeys:creator_code, NSFileHFSCreatorCode,
119 type_code, NSFileHFSTypeCode, nil];
120
121 if (![[NSFileManager defaultManager] changeFileAttributes:output_attributes atPath:inFile]) {
122 NSLog(@" AtomicParsley error: setting type and creator code on %@", inFile);
123 }
124
125 } else {
126 NSDictionary* file_attributes = [[NSFileManager defaultManager] fileAttributesAtPath:inFile traverseLink:YES];
127 return_value = [[file_attributes objectForKey:NSFileHFSTypeCode] unsignedLongValue ];
128
129 //NSLog(@"code: %@\n", [file_attributes objectForKey:NSFileHFSTypeCode] );
130 }
131
132 [pool release];
133
134 return return_value;
115 // it might be beneficial to eval for channels and if its audio only &
116 // multichannel to NOT change the TYPE/creator codes
117
118 uint32_t APar_4CC_CreatorCode(const char *filepath, uint32_t new_type_code) {
119 uint32_t return_value = 0;
120 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
121
122 NSString *inFile = [NSString stringWithUTF8String:filepath];
123
124 if (new_type_code) {
125 NSNumber *creator_code = [NSNumber numberWithUnsignedLong:'hook'];
126 NSNumber *type_code = [NSNumber numberWithUnsignedLong:new_type_code];
127 NSDictionary *output_attributes =
128 [NSDictionary dictionaryWithObjectsAndKeys:creator_code,
129 NSFileHFSCreatorCode,
130 type_code,
131 NSFileHFSTypeCode,
132 nil];
133
134 if (![[NSFileManager defaultManager] changeFileAttributes:output_attributes
135 atPath:inFile]) {
136 NSLog(@" AtomicParsley error: setting type and creator code on %@",
137 inFile);
138 }
139
140 } else {
141 NSDictionary *file_attributes =
142 [[NSFileManager defaultManager] fileAttributesAtPath:inFile
143 traverseLink:YES];
144 return_value =
145 [[file_attributes objectForKey:NSFileHFSTypeCode] unsignedLongValue];
146
147 // NSLog(@"code: %@\n", [file_attributes objectForKey:NSFileHFSTypeCode] );
148 }
149
150 [pool release];
151
152 return return_value;
135153 }
136154
137 //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).
138
139 void APar_SupplySelectiveTypeCreatorCodes(const char *inputPath, const char *outputPath, uint8_t forced_type_code) {
140 if (forced_type_code != NO_TYPE_FORCING) {
141 if (forced_type_code == FORCE_M4B_TYPE) {
142 APar_4CC_CreatorCode(outputPath, 'M4B ');
143 }
144 return;
145 }
146
147 char* input_suffix = strrchr(inputPath, '.');
148 //user-defined output paths may have the original file as ".m4a" & show up fine when output to ".m4a"
149 //output to ".mp4" and it becomes a generic (sans TYPE/CREATOR) file that defaults to Quicktime Player
150 char* output_suffix = strrchr(outputPath, '.');
151
152 char* typecode = (char*)malloc( sizeof(char)* 4 );
153 memset(typecode, 0, sizeof(char)*4);
154
155 uint32_t type_code = APar_4CC_CreatorCode(inputPath, 0);
156
157 UInt32_TO_String4(type_code, typecode);
158
159 //fprintf(stdout, "%s - %s\n", typecode, input_suffix);
160 APar_TestTracksForKind();
161
162 if (strncasecmp(input_suffix, ".mp4", 4) == 0 || strncasecmp(output_suffix, ".mp4", 4) == 0) { //only work on the generic .mp4 extension
163 if (track_codecs.has_avc1 || track_codecs.has_mp4v || track_codecs.has_drmi) {
164 type_code = APar_4CC_CreatorCode(outputPath, 'M4V ');
165
166 //for a podcast an audio track with either a text, jpeg or url track is required, otherwise it will fall through to generic m4a;
167 //files that are already .m4b or 'M4B ' don't even enter into this situation, so they are safe
168 //if the file had video with subtitles (tx3g), then it would get taken care of above in the video section - unsupported by QT currently
169 } else if (track_codecs.has_mp4a && (track_codecs.has_timed_text || track_codecs.has_timed_jpeg || track_codecs.has_timed_tx3g) ) {
170 type_code = APar_4CC_CreatorCode(outputPath, 'M4B ');
171
172 //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 '
173 } else {
174 type_code = APar_4CC_CreatorCode(outputPath, 'M4A ');
175 }
176 } else if (track_codecs.has_avc1 || track_codecs.has_mp4v || track_codecs.has_drmi) {
177 type_code = APar_4CC_CreatorCode(outputPath, 'M4V ');
178 }
179
180 return;
155 // there is a scenario that is as of now unsupported (or botched, depending if
156 // you use the feature), although it would be easy to implement. To make a file
157 // bookmarkable, the TYPE code is set to 'M4B ' - which can be *also* done by
158 // changing the extension to ".m4b". However, due to the way that the file is
159 // tested here, a ".mp4" with 'M4B ' type code will get changed into a normal
160 // audio file (not-bookmarkable).
161
162 void APar_SupplySelectiveTypeCreatorCodes(const char *inputPath,
163 const char *outputPath,
164 uint8_t forced_type_code) {
165 if (forced_type_code != NO_TYPE_FORCING) {
166 if (forced_type_code == FORCE_M4B_TYPE) {
167 APar_4CC_CreatorCode(outputPath, 'M4B ');
168 }
169 return;
170 }
171
172 const char *input_suffix = strrchr(inputPath, '.');
173 // user-defined output paths may have the original file as ".m4a" & show up
174 // fine when output to ".m4a" output to ".mp4" and it becomes a generic (sans
175 // TYPE/CREATOR) file that defaults to Quicktime Player
176 const char *output_suffix = strrchr(outputPath, '.');
177
178 char *typecode = (char *)malloc(sizeof(char) * 4);
179 memset(typecode, 0, sizeof(char) * 4);
180
181 uint32_t type_code = APar_4CC_CreatorCode(inputPath, 0);
182
183 UInt32_TO_String4(type_code, typecode);
184
185 // fprintf(stdout, "%s - %s\n", typecode, input_suffix);
186 APar_TestTracksForKind();
187
188 if (strncasecmp(input_suffix, ".mp4", 4) == 0 ||
189 strncasecmp(output_suffix, ".mp4", 4) ==
190 0) { // only work on the generic .mp4 extension
191 if (track_codecs.has_avc1 || track_codecs.has_mp4v ||
192 track_codecs.has_drmi) {
193 type_code = APar_4CC_CreatorCode(outputPath, 'M4V ');
194
195 // for a podcast an audio track with either a text, jpeg or url track is
196 // required, otherwise it will fall through to generic m4a; files that are
197 // already .m4b or 'M4B ' don't even enter into this situation, so they
198 // are safe if the file had video with subtitles (tx3g), then it would get
199 // taken care of above in the video section - unsupported by QT currently
200 } else if (track_codecs.has_mp4a &&
201 (track_codecs.has_timed_text || track_codecs.has_timed_jpeg ||
202 track_codecs.has_timed_tx3g)) {
203 type_code = APar_4CC_CreatorCode(outputPath, 'M4B ');
204
205 // default to audio; technically so would a drms iTMS drm audio file with
206 // ".mp4". But that would also mean it was renamed. They should be 'M4P '
207 } else {
208 type_code = APar_4CC_CreatorCode(outputPath, 'M4A ');
209 }
210 } else if (track_codecs.has_avc1 || track_codecs.has_mp4v ||
211 track_codecs.has_drmi) {
212 type_code = APar_4CC_CreatorCode(outputPath, 'M4V ');
213 }
214
215 return;
181216 }
11 /*
22 AtomicParsley - nsimage.mm
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
2222 #include "AtomicParsley.h"
2323 #import <Cocoa/Cocoa.h>
2424
25 bool isJPEG=false;
26 bool isPNG=false;
27
28 void DetermineType(const char *picfilePath) {
29 char* picHeader = (char*)calloc(1, sizeof(char)*20);
30 u_int64_t r;
31
32 FILE *pic_file = NULL;
33 pic_file = fopen(picfilePath, "rb");
34 r = fread(picHeader, 8, 1, pic_file);
25 static void DetermineType(const char *picfilePath, bool &isJPEG, bool &isPNG) {
26 char picHeader[20];
27
28 FILE *pic_file = fopen(picfilePath, "rb");
29 u_int64_t r = fread(picHeader, 8, 1, pic_file);
3530 fclose(pic_file);
36
37 if (memcmp(picHeader, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) {
38 isPNG=true;
39 isJPEG=false;
40 } else if (memcmp(picHeader, "\xFF\xD8\xFF\xE0", 4) == 0) {
41 isJPEG=true;
42 isPNG=false;
43 }
44 free(picHeader);
45 picHeader=NULL;
46 return;
47 }
48
49 char* DeriveNewPath(const char *filePath, PicPrefs myPicPrefs, char* newpath) {
50 char* suffix = strrchr(filePath, '.');
51
52 size_t filepath_len = strlen(filePath);
53 memset(newpath, 0, MAXPATHLEN+1);
54 size_t base_len = filepath_len-strlen(suffix);
55 memcpy(newpath, filePath, base_len);
56 memcpy(newpath+base_len, "-resized-", 9);
57
58 char* randstring = (char*)calloc(1, sizeof(char)*20);
59 struct timeval tv;
60 gettimeofday (&tv, NULL);
61
62 srand( (int) tv.tv_usec / 1000 ); //Seeds rand()
63 int randNum = rand()%10000;
64 sprintf(randstring, "%i", randNum);
65 strcat(newpath, randstring);
66
67 if (myPicPrefs.allJPEG) {
68 strcat(newpath, ".jpg");
69 } else if (myPicPrefs.allPNG) {
70 strcat(newpath, ".png");
71 } else {
72 strcat(newpath, suffix);
73 }
74
75 if ( (strncmp(suffix,".jpg",4) == 0) || (strncmp(suffix,".jpeg",5) == 0) || (strncmp(suffix,".JPG",4) == 0) || (strncmp(suffix,".JPEG",5) == 0) ) {
76 isJPEG=true;
77 } else if ((strncmp(suffix,".png",4) == 0) || (strncmp(suffix,".PNG",4) == 0)) {
78 isPNG=true;
79 }
80
81 free(randstring);
82 randstring=NULL;
83 return newpath;
84 }
85
86 bool ResizeGivenImage(const char* filePath, PicPrefs myPicPrefs, char* resized_path) {
87 bool resize = false;
88 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
89
90 NSImage* source = [ [NSImage alloc] initWithContentsOfFile: [NSString stringWithUTF8String: filePath] ];
91 [source setScalesWhenResized: YES];
92 if ( source == nil ) {
93 fprintf( stderr, "Image '%s' could not be loaded.\n", filePath );
94 exit (1);
95 }
96
97 NSSize sourceSize = [source size];
98 float hmax, vmax, aspect;
99 hmax = sourceSize.width;
100 vmax = sourceSize.height;
101 aspect = sourceSize.height / sourceSize.width;
102 //fprintf(stdout, "aspect %f2.4\n", aspect);
103 if (myPicPrefs.max_dimension != 0) {
104 if ( ( (int)sourceSize.width > myPicPrefs.max_dimension) || ( (int)sourceSize.height > myPicPrefs.max_dimension) ) {
105 resize = true; //only if dimensions are LARGER than our max do we resize
106 if (hmax > vmax) {
107 hmax = myPicPrefs.max_dimension;
108 vmax = myPicPrefs.max_dimension * aspect;
109 } else {
110 hmax = myPicPrefs.max_dimension / aspect;
111 vmax = myPicPrefs.max_dimension;
112 }
113 }
114 }
115
116 ///// determine dpi/ppi
117 float hres, vres, hdpi, vdpi;
118 NSImageRep *myRep = [[source representations] objectAtIndex:0];
119 hres = [myRep pixelsWide]; //native pixel dimensions
120 vres = [myRep pixelsHigh];
121 hdpi = hres/sourceSize.width; //in native resolution (multiply by 72 to get native dpi)
122 vdpi = vres/sourceSize.height;
123
124 if ( ( (int)hdpi != 1 ) || ( (int)vdpi != 1) ) {
125 resize = true;
126 hmax = hres;
127 vmax = vres;
128 if (myPicPrefs.max_dimension != 0) {
129 //we also need to recheck we don't go over our max dimensions (again)
130 if ( ( (int)hres > myPicPrefs.max_dimension) || ( (int)vres > myPicPrefs.max_dimension) ) {
131 if (hmax > vmax) {
132 hmax = myPicPrefs.max_dimension;
133 vmax = myPicPrefs.max_dimension * aspect;
134 } else {
135 hmax = myPicPrefs.max_dimension / aspect;
136 vmax = myPicPrefs.max_dimension;
137 }
138 }
139 }
140 }
141
142 if (myPicPrefs.squareUp) {
143 if (myPicPrefs.max_dimension != 0) {
144 vmax = myPicPrefs.max_dimension;
145 hmax = myPicPrefs.max_dimension;
146 resize = true;
147 } else {
148 //this will stretch the image to the largest dimension. Hope you don't try to scale a 160x1200 image... it could get ugly
149 if (hmax > vmax) {
150 vmax = hmax;
151 resize = true;
152 } else if (vmax > hmax) {
153 hmax = vmax;
154 resize = true;
155 }
156 }
157 }
158
159 if (myPicPrefs.force_dimensions) {
160 if (myPicPrefs.force_height > 0 && myPicPrefs.force_width > 0) {
161 vmax = myPicPrefs.force_height;
162 hmax = myPicPrefs.force_width;
163 resize = true;
164 }
165 }
166
167 uint64_t pic_file_size = findFileSize(filePath);
168 if ( ( (int)pic_file_size > myPicPrefs.max_Kbytes) && ( myPicPrefs.max_Kbytes != 0) ) {
169 resize = true;
170 }
171
172 DetermineType(filePath);
173 if ( (isJPEG && myPicPrefs.allPNG) || (isPNG && myPicPrefs.allJPEG) ) { //handle jpeg->png & png->jpg conversion
174 resize = true;
175
176 }
177
178 NSRect destinationRect = NSMakeRect( 0, 0, hmax, vmax );
179 NSSize size = NSMakeSize( hmax, vmax );
180
181 if (resize) {
182 [NSApplication sharedApplication];
183 [[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationHigh];
184
185 [source setSize: size];
186
187 NSImage* image = [[NSImage alloc] initWithSize:size];
188 [image lockFocus];
189
190 NSEraseRect( destinationRect );
191 [source drawInRect: destinationRect
192 fromRect: destinationRect
193 operation: NSCompositeCopy fraction: 1.0];
194
195 NSBitmapImageRep* bitmap = [ [NSBitmapImageRep alloc]
196 initWithFocusedViewRect: destinationRect ];
197 NSBitmapImageFileType filetype;
198 NSDictionary *props;
199
200 if ( (isPNG && !myPicPrefs.allJPEG) || myPicPrefs.allPNG) {
201 filetype = NSPNGFileType;
202 props = nil;
203
204 } else {
205 filetype = NSJPEGFileType;
206 props = [ NSDictionary dictionaryWithObject:
207 [NSNumber numberWithFloat: 0.7] forKey: NSImageCompressionFactor];
208 }
209 NSData* data = [bitmap representationUsingType:filetype properties:props];
210
211 unsigned dataLength = [data length]; //holds the file length
212
213 int iter = 0;
214 float compression = 0.65;
215 if ( (myPicPrefs.max_Kbytes != 0) && (filetype == NSJPEGFileType) ) {
216 while ( (dataLength > (unsigned)myPicPrefs.max_Kbytes) && (iter < 10) ) {
217 props = [ NSDictionary dictionaryWithObject:
218 [NSNumber numberWithFloat: compression] forKey: NSImageCompressionFactor];
219 data = [bitmap representationUsingType:filetype properties:props];
220 dataLength = [data length];
221 compression = compression - 0.05;
222 iter++;
223 }
224 }
225
226 [bitmap release];
227 NSString *outFile= [NSString stringWithUTF8String: DeriveNewPath(filePath, myPicPrefs, resized_path)];
228 //NSLog(outFile);
229 [[NSFileManager defaultManager]
230 createFileAtPath: outFile
231 contents: data
232 attributes: nil ];
233
234 [image unlockFocus];
235 [image release];
236 isJPEG=false;
237 isPNG=false;
238 memcpy(resized_path, [outFile cStringUsingEncoding: NSUTF8StringEncoding], [outFile lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
239 }
240 [source release];
241 [pool release];
242 return resize;
243 }
31
32 if (memcmp(picHeader, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) {
33 isPNG = true;
34 isJPEG = false;
35 } else if (memcmp(picHeader, "\xFF\xD8\xFF", 3) == 0) {
36 isJPEG = true;
37 isPNG = false;
38 } else {
39 isPNG = false;
40 isJPEG = false;
41 }
42 }
43
44 static char *DeriveNewPath(const char *filePath,
45 PicPrefs myPicPrefs,
46 char *newpath,
47 size_t newpath_len) {
48 const char *suffix = strrchr(filePath, '.');
49
50 size_t filepath_len = strlen(filePath);
51 memset(newpath, 0, newpath_len);
52 size_t base_len = filepath_len - strlen(suffix);
53 memcpy(newpath, filePath, base_len);
54 memcpy(newpath + base_len, "-resized-", 9);
55
56 char *randstring = (char *)calloc(1, sizeof(char) * 20);
57 struct timeval tv;
58 gettimeofday(&tv, NULL);
59
60 srand((int)tv.tv_usec / 1000); // Seeds rand()
61 int randNum = rand() % 10000;
62 sprintf(randstring, "%i", randNum);
63 strcat(newpath, randstring);
64
65 if (myPicPrefs.allJPEG) {
66 strcat(newpath, ".jpg");
67 } else if (myPicPrefs.allPNG) {
68 strcat(newpath, ".png");
69 } else {
70 strcat(newpath, suffix);
71 }
72
73 free(randstring);
74 randstring = NULL;
75 return newpath;
76 }
77
78 static NSImage *DoResize(NSImage *sourceImage, NSSize newSize) {
79 if (!sourceImage.isValid) {
80 return nil;
81 }
82
83 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
84 initWithBitmapDataPlanes:NULL
85 pixelsWide:newSize.width
86 pixelsHigh:newSize.height
87 bitsPerSample:8
88 samplesPerPixel:4
89 hasAlpha:YES
90 isPlanar:NO
91 colorSpaceName:NSCalibratedRGBColorSpace
92 bytesPerRow:0
93 bitsPerPixel:0];
94 rep.size = newSize;
95
96 [NSGraphicsContext saveGraphicsState];
97 [NSGraphicsContext
98 setCurrentContext:[NSGraphicsContext
99 graphicsContextWithBitmapImageRep:rep]];
100 [sourceImage drawInRect:NSMakeRect(0, 0, newSize.width, newSize.height)
101 fromRect:NSZeroRect
102 operation:NSCompositingOperationCopy
103 fraction:1.0];
104 [NSGraphicsContext restoreGraphicsState];
105
106 NSImage *newImage = [[NSImage alloc] initWithSize:newSize];
107 [newImage addRepresentation:rep];
108 return newImage;
109 }
110
111 bool ResizeGivenImage(const char *filePath,
112 PicPrefs myPicPrefs,
113 char *resized_path,
114 size_t resized_path_len) {
115 bool resize = false;
116
117 NSImage *source = [[NSImage alloc]
118 initWithContentsOfFile:[NSString stringWithUTF8String:filePath]];
119 if (source == nil) {
120 fprintf(stderr, "Image '%s' could not be loaded.\n", filePath);
121 exit(1);
122 }
123
124 NSSize sourceSize = [source size];
125 float hmax, vmax, aspect;
126 hmax = sourceSize.width;
127 vmax = sourceSize.height;
128 aspect = sourceSize.height / sourceSize.width;
129 // fprintf(stdout, "aspect %f2.4\n", aspect);
130 if (myPicPrefs.max_dimension != 0) {
131 if (((int)sourceSize.width > myPicPrefs.max_dimension) ||
132 ((int)sourceSize.height > myPicPrefs.max_dimension)) {
133 resize = true; // only if dimensions are LARGER than our max do we resize
134 if (hmax > vmax) {
135 hmax = myPicPrefs.max_dimension;
136 vmax = myPicPrefs.max_dimension * aspect;
137 } else {
138 hmax = myPicPrefs.max_dimension / aspect;
139 vmax = myPicPrefs.max_dimension;
140 }
141 }
142 }
143
144 ///// determine dpi/ppi
145 float hres, vres, hdpi, vdpi;
146 NSImageRep *myRep = [[source representations] objectAtIndex:0];
147 hres = [myRep pixelsWide]; // native pixel dimensions
148 vres = [myRep pixelsHigh];
149 hdpi = hres /
150 sourceSize
151 .width; // in native resolution (multiply by 72 to get native dpi)
152 vdpi = vres / sourceSize.height;
153
154 if (((int)hdpi != 1) || ((int)vdpi != 1)) {
155 resize = true;
156 hmax = hres;
157 vmax = vres;
158 if (myPicPrefs.max_dimension != 0) {
159 // we also need to recheck we don't go over our max dimensions (again)
160 if (((int)hres > myPicPrefs.max_dimension) ||
161 ((int)vres > myPicPrefs.max_dimension)) {
162 if (hmax > vmax) {
163 hmax = myPicPrefs.max_dimension;
164 vmax = myPicPrefs.max_dimension * aspect;
165 } else {
166 hmax = myPicPrefs.max_dimension / aspect;
167 vmax = myPicPrefs.max_dimension;
168 }
169 }
170 }
171 }
172
173 if (myPicPrefs.squareUp) {
174 if (myPicPrefs.max_dimension != 0) {
175 vmax = myPicPrefs.max_dimension;
176 hmax = myPicPrefs.max_dimension;
177 resize = true;
178 } else {
179 // this will stretch the image to the largest dimension. Hope you don't
180 // try to scale a 160x1200 image... it could get ugly
181 if (hmax > vmax) {
182 vmax = hmax;
183 resize = true;
184 } else if (vmax > hmax) {
185 hmax = vmax;
186 resize = true;
187 }
188 }
189 }
190
191 if (myPicPrefs.force_dimensions) {
192 if (myPicPrefs.force_height > 0 && myPicPrefs.force_width > 0) {
193 vmax = myPicPrefs.force_height;
194 hmax = myPicPrefs.force_width;
195 resize = true;
196 }
197 }
198
199 uint64_t pic_file_size = findFileSize(filePath);
200 if (((int)pic_file_size > myPicPrefs.max_Kbytes) &&
201 (myPicPrefs.max_Kbytes != 0)) {
202 resize = true;
203 }
204
205 bool isJPEG, isPNG;
206 DetermineType(filePath, isJPEG, isPNG);
207 if ((isJPEG && myPicPrefs.allPNG) ||
208 (isPNG && myPicPrefs.allJPEG)) { // handle jpeg->png & png->jpg conversion
209 resize = true;
210 }
211
212 NSRect destinationRect = NSMakeRect(0, 0, hmax, vmax);
213 NSSize size = NSMakeSize(hmax, vmax);
214
215 if (resize) {
216 NSImage *image = DoResize(source, size);
217
218 NSData *imageData = [image TIFFRepresentation];
219 NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:imageData];
220
221 NSBitmapImageFileType filetype;
222 NSDictionary *props;
223
224 if ((isPNG && !myPicPrefs.allJPEG) || myPicPrefs.allPNG) {
225 filetype = NSBitmapImageFileTypePNG;
226 props = nil;
227 } else {
228 filetype = NSBitmapImageFileTypeJPEG;
229 props = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.7]
230 forKey:NSImageCompressionFactor];
231 }
232 NSData *data = [bitmap representationUsingType:filetype properties:props];
233 unsigned dataLength = [data length]; // holds the file length
234
235 int iter = 0;
236 float compression = 0.65;
237 if ((myPicPrefs.max_Kbytes != 0) &&
238 (filetype == NSBitmapImageFileTypeJPEG)) {
239 while ((dataLength > (unsigned)myPicPrefs.max_Kbytes) && (iter < 10)) {
240 props = [NSDictionary
241 dictionaryWithObject:[NSNumber numberWithFloat:compression]
242 forKey:NSImageCompressionFactor];
243 data = [bitmap representationUsingType:filetype properties:props];
244 dataLength = [data length];
245 compression = compression - 0.05;
246 iter++;
247 }
248 }
249
250 NSString *outFile =
251 [NSString stringWithUTF8String:DeriveNewPath(filePath,
252 myPicPrefs,
253 resized_path,
254 resized_path_len)];
255 [[NSFileManager defaultManager] createFileAtPath:outFile
256 contents:data
257 attributes:nil];
258
259 [image release];
260 [bitmap release];
261 memcpy(resized_path,
262 [outFile cStringUsingEncoding:NSUTF8StringEncoding],
263 [outFile lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
264 }
265 [source release];
266 return resize;
267 }
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2005-2007 puck_lock
17 Copyright (C) 2005-2007 puck_lock
1818 with contributions from others; see the CREDITS file
1919
2020 ----------------------
2222
2323 * Mike Brancato - Debian patches & build support
2424 * Lowell Stewart - null-termination bugfix for Apple compliance
25 * Brian Story - native Win32 patches; memset/framing/leaks fixes
25 * Brian Story - native Win32 patches; memset/framing/leaks fixes
2626 */
2727 //==================================================================//
2828
29 #include "AtomDefs.h"
2930 #include "AtomicParsley.h"
30 #include "AtomDefs.h"
31 #include <algorithm>
3132
3233 //#define DEBUG_V
3334
3940 bool alter_original = false;
4041 bool preserve_timestamps = false;
4142
42
43 FILE* source_file = NULL;
43 FILE *source_file = NULL;
4444 uint64_t file_size;
4545
4646 struct AtomicInfo parsedAtoms[MAX_ATOMS];
5151 bool parsedfile = false;
5252 bool move_moov_atom = true;
5353 bool moov_atom_was_mooved = false;
54 AtomicInfo* hdlrAtom = NULL;
55 AtomicInfo* movie_header_atom = NULL;
54 AtomicInfo *hdlrAtom = NULL;
55 AtomicInfo *movie_header_atom = NULL;
5656 bool complete_free_space_erasure = false;
5757 bool psp_brand = false;
5858 bool force_existing_hierarchy = false;
5959 int metadata_style = UNDEFINED_STYLE;
6060 bool deep_atom_scan = false;
6161
62 uint32_t max_buffer =
62 uint64_t max_buffer =
6363 #ifdef __linux__
64 0.5 /* splice() allows us to use less buffer space */
64 0.5 /* splice() allows us to use less buffer space */
6565 #else
66 10
66 10
6767 #endif
68 *1024*1024
69 ;
70
71 uint64_t bytes_before_mdat=0;
68 * 1024 * 1024;
69
70 uint64_t bytes_before_mdat = 0;
7271 uint64_t bytes_into_mdat = 0;
7372 uint64_t mdat_supplemental_offset = 0;
7473 uint64_t removed_bytes_tally = 0;
75 uint64_t new_file_size = 0; //used for the progressbar
74 uint64_t new_file_size = 0; // used for the progressbar
7675 uint32_t brand = 0;
77 uint64_t mdatData = 0; //now global, used in bitrate calcs
78
79 uint64_t gapless_void_padding = 0; //possibly used in the context of gapless playback support by Apple
76 uint64_t mdatData = 0; // now global, used in bitrate calcs
77
78 uint64_t gapless_void_padding =
79 0; // possibly used in the context of gapless playback support by Apple
8080
8181 struct DynamicUpdateStat dynUpd;
8282 struct padding_preferences pad_prefs;
8383
8484 short max_display_width = 55;
85 char* file_progress_buffer=(char*)calloc(1, sizeof(char)* (max_display_width+50) ); //+50 for any overflow in "%100", or "|"
86
87 #if defined (DARWIN_PLATFORM)
85 char *file_progress_buffer = (char *)calloc(
86 1,
87 sizeof(char) *
88 (max_display_width + 50)); //+50 for any overflow in "%100", or "|"
89
90 #if defined(__APPLE__)
8891 struct PicPrefs myPicturePrefs;
8992 #endif
9093 bool parsed_prefs = false;
91 char* twenty_byte_buffer = (char *)malloc(sizeof(char)*20);
92
93 EmployedCodecs track_codecs = {false, false, false, false, false, false, false, false, false, false};
94
95 uint8_t UnicodeOutputStatus = UNIVERSAL_UTF8; //on windows, controls whether input/output strings are utf16 or raw utf8; reset in wmain()
94 char *twenty_byte_buffer = (char *)malloc(sizeof(char) * 20);
95
96 EmployedCodecs track_codecs = {
97 false, false, false, false, false, false, false, false, false, false};
98
99 uint8_t UnicodeOutputStatus =
100 UNIVERSAL_UTF8; // on windows, controls whether input/output strings are
101 // utf16 or raw utf8; reset in wmain()
96102
97103 uint8_t forced_suffix_type = NO_TYPE_FORCING;
98104
102108
103109 void ShowVersionInfo() {
104110
105 #if defined (_WIN32)
106 char *unicode_enabled;
107 if (UnicodeOutputStatus == WIN32_UTF16) {
111 #if defined(_WIN32)
112 char *unicode_enabled;
113 if (UnicodeOutputStatus == WIN32_UTF16) {
108114 #ifndef __CYGWIN__
109 unicode_enabled = "(utf16)";
115 unicode_enabled = "(utf16)";
110116 #else
111 unicode_enabled = "(utf8 with utf16 CD access)";
117 unicode_enabled = "(utf8 with utf16 CD access)";
112118 #endif
113119
114 // its utf16 in the sense that any text entering on a modern Win32 system
115 // enters as utf16le - but gets converted immediately after AP.exe starts
116 // to utf8 all arguments, strings, filenames, options are sent around as
117 // utf8. For modern Win32 systems, filenames get converted to utf16 for
118 // output as needed. Any strings to be set as utf16 in 3gp assets are
119 // converted to utf16be as needed (true for all OS implementations).
120 // Printing out to the console should be utf8.
121
122 } else if (UnicodeOutputStatus == UNIVERSAL_UTF8) {
120 // its utf16 in the sense that any text entering on a modern Win32 system
121 // enters as utf16le - but gets converted immediately after AP.exe starts
122 // to utf8 all arguments, strings, filenames, options are sent around as
123 // utf8. For modern Win32 systems, filenames get converted to utf16 for
124 // output as needed. Any strings to be set as utf16 in 3gp assets are
125 // converted to utf16be as needed (true for all OS implementations).
126 // Printing out to the console should be utf8.
127
128 } else if (UnicodeOutputStatus == UNIVERSAL_UTF8) {
123129 #ifndef __CYGWIN__
124 unicode_enabled = "(raw utf8)";
130 unicode_enabled = "(raw utf8)";
125131 #else
126 unicode_enabled = "(utf8 with raw utf8 CD access)";
132 unicode_enabled = "(utf8 with raw utf8 CD access)";
127133 #endif
128134
129 // utf8 in the sense that any text entered had its utf16 upper byte
130 // stripped and reduced to (unchecked) raw utf8 for utilities that work in
131 // utf8. Any unicode (utf16) filenames were clobbered in that processes are
132 // invalid now. Any intermediate folder with unicode in it will now likely
133 // cause an error of some sort.
134 }
135 // utf8 in the sense that any text entered had its utf16 upper byte
136 // stripped and reduced to (unchecked) raw utf8 for utilities that work in
137 // utf8. Any unicode (utf16) filenames were clobbered in that processes are
138 // invalid now. Any intermediate folder with unicode in it will now likely
139 // cause an error of some sort.
140 }
135141
136142 #else
137 #define unicode_enabled "(utf8)"
143 #define unicode_enabled "(utf8)"
138144 #endif
139145
140 fprintf(stdout, "AtomicParsley version: %s %s\n",
141 PACKAGE_VERSION, unicode_enabled);
142
146 fprintf(stdout,
147 "AtomicParsley version: %s %s %s\n",
148 PACKAGE_VERSION,
149 BUILD_INFO,
150 unicode_enabled);
143151 }
144152
145153 ///////////////////////////////////////////////////////////////////////////////////////
146 // Generic Functions //
154 // Generic Functions //
147155 ///////////////////////////////////////////////////////////////////////////////////////
148156
149 int APar_TestArtworkBinaryData(const char* artworkPath) {
150 int artwork_dataType = 0;
151 FILE *artfile = APar_OpenFile(artworkPath, "rb");
152 if (artfile != NULL) {
153 APar_read64(twenty_byte_buffer, artfile, 0);
154 if ( strncmp(twenty_byte_buffer, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0 ) {
155 artwork_dataType = AtomFlags_Data_PNGBinary;
156 } else if ( strncmp(twenty_byte_buffer, "\xFF\xD8\xFF\xE0", 4) == 0 || memcmp(twenty_byte_buffer, "\xFF\xD8\xFF\xE1", 4) == 0 ) {
157 artwork_dataType = AtomFlags_Data_JPEGBinary;
158 } else {
159 fprintf(stdout, "AtomicParsley error: %s\n\t image file is not jpg/png and cannot be embedded.\n", artworkPath);
160 exit(1);
161 }
162 fclose(artfile);
163
164 } else {
165 fprintf(stdout, "AtomicParsley error: %s\n\t image file could not be opened.\n", artworkPath);
166 exit(1);
167 }
168 return artwork_dataType;
157 int APar_TestArtworkBinaryData(const char *artworkPath) {
158 int artwork_dataType = 0;
159 FILE *artfile = APar_OpenFile(artworkPath, "rb");
160 if (artfile != NULL) {
161 APar_read64(twenty_byte_buffer, artfile, 0);
162 if (strncmp(twenty_byte_buffer, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) ==
163 0) {
164 artwork_dataType = AtomFlags_Data_PNGBinary;
165 } else if (memcmp(twenty_byte_buffer, "\xFF\xD8\xFF", 3) == 0) {
166 artwork_dataType = AtomFlags_Data_JPEGBinary;
167 } else {
168 fprintf(stdout,
169 "AtomicParsley error: %s\n\t image file is not jpg/png and "
170 "cannot be embedded.\n",
171 artworkPath);
172 exit(1);
173 }
174 fclose(artfile);
175
176 } else {
177 fprintf(stdout,
178 "AtomicParsley error: %s\n\t image file could not be opened.\n",
179 artworkPath);
180 exit(1);
181 }
182 return artwork_dataType;
169183 }
170184
171185 void APar_FreeMemory() {
172 for(int iter=0; iter < atom_number; iter++) {
173 if (parsedAtoms[iter].AtomicName != NULL) {
174 free(parsedAtoms[iter].AtomicName);
175 parsedAtoms[iter].AtomicName=NULL;
176 }
177 if (parsedAtoms[iter].AtomicData != NULL) {
178 free(parsedAtoms[iter].AtomicData);
179 parsedAtoms[iter].AtomicData = NULL;
180 }
181 if (parsedAtoms[iter].ReverseDNSname != NULL) {
182 free(parsedAtoms[iter].ReverseDNSname);
183 parsedAtoms[iter].ReverseDNSname = NULL;
184 }
185 if (parsedAtoms[iter].ReverseDNSdomain != NULL) {
186 free(parsedAtoms[iter].ReverseDNSdomain);
187 parsedAtoms[iter].ReverseDNSdomain = NULL;
188 }
189 if (parsedAtoms[iter].uuid_ap_atomname != NULL) {
190 free(parsedAtoms[iter].uuid_ap_atomname);
191 parsedAtoms[iter].uuid_ap_atomname = NULL;
192 }
193 if (parsedAtoms[iter].ID32_TagInfo != NULL) {
194 //a cascade of tests & free-ing of all the accrued ID3v2 tag/frame/field data
195 APar_FreeID32Memory(parsedAtoms[iter].ID32_TagInfo);
196
197 free(parsedAtoms[iter].ID32_TagInfo);
198 parsedAtoms[iter].ID32_TagInfo = NULL;
199 }
200 }
201 free(twenty_byte_buffer);
202 twenty_byte_buffer=NULL;
203 free(file_progress_buffer);
204 file_progress_buffer=NULL;
205
206 return;
186 for (int iter = 0; iter < atom_number; iter++) {
187 if (parsedAtoms[iter].AtomicName != NULL) {
188 free(parsedAtoms[iter].AtomicName);
189 parsedAtoms[iter].AtomicName = NULL;
190 }
191 if (parsedAtoms[iter].AtomicData != NULL) {
192 free(parsedAtoms[iter].AtomicData);
193 parsedAtoms[iter].AtomicData = NULL;
194 }
195 if (parsedAtoms[iter].ReverseDNSname != NULL) {
196 free(parsedAtoms[iter].ReverseDNSname);
197 parsedAtoms[iter].ReverseDNSname = NULL;
198 }
199 if (parsedAtoms[iter].ReverseDNSdomain != NULL) {
200 free(parsedAtoms[iter].ReverseDNSdomain);
201 parsedAtoms[iter].ReverseDNSdomain = NULL;
202 }
203 if (parsedAtoms[iter].uuid_ap_atomname != NULL) {
204 free(parsedAtoms[iter].uuid_ap_atomname);
205 parsedAtoms[iter].uuid_ap_atomname = NULL;
206 }
207 if (parsedAtoms[iter].ID32_TagInfo != NULL) {
208 // a cascade of tests & free-ing of all the accrued ID3v2 tag/frame/field
209 // data
210 APar_FreeID32Memory(parsedAtoms[iter].ID32_TagInfo);
211
212 free(parsedAtoms[iter].ID32_TagInfo);
213 parsedAtoms[iter].ID32_TagInfo = NULL;
214 }
215 }
216 free(twenty_byte_buffer);
217 twenty_byte_buffer = NULL;
218 free(file_progress_buffer);
219 file_progress_buffer = NULL;
220
221 return;
207222 }
208223
209224 ///////////////////////////////////////////////////////////////////////////////////////
210 // Picture Preferences Functions //
225 // Picture Preferences Functions //
211226 ///////////////////////////////////////////////////////////////////////////////////////
212227
213 #if defined (DARWIN_PLATFORM)
214 PicPrefs APar_ExtractPicPrefs(char* env_PicOptions) {
215 if (!parsed_prefs) {
216
217 parsed_prefs = true; //only set default values & parse once
218
219 myPicturePrefs.max_dimension=0; //dimensions won't be used to alter image
220 myPicturePrefs.dpi = 72;
221 myPicturePrefs.max_Kbytes = 0; //no target size to shoot for
222 myPicturePrefs.allJPEG = false;
223 myPicturePrefs.allPNG = false;
224 myPicturePrefs.addBOTHpix = false;
225 myPicturePrefs.force_dimensions = false;
226 myPicturePrefs.force_height = 0;
227 myPicturePrefs.force_width = 0;
228 myPicturePrefs.removeTempPix = true; //we'll just make this the default
229
230 char* unparsed_opts = env_PicOptions;
231 if (env_PicOptions == NULL) return myPicturePrefs;
232
233 while (unparsed_opts[0] != 0) {
234 if (strncmp(unparsed_opts,"MaxDimensions=",14) == 0) {
235 unparsed_opts+=14;
236 myPicturePrefs.max_dimension = (int)strtol(unparsed_opts, NULL, 10);
237
238 } else if (strncmp(unparsed_opts,"DPI=",4) == 0) {
239 unparsed_opts+=4;
240 myPicturePrefs.dpi = (int)strtol(unparsed_opts, NULL, 10);
241
242 } else if (strncmp(unparsed_opts,"MaxKBytes=",10) == 0) {
243 unparsed_opts+=10;
244 myPicturePrefs.max_Kbytes = (int)strtol(unparsed_opts, NULL, 10)*1024;
245
246 } else if (strncmp(unparsed_opts,"AllPixJPEG=",11) == 0) {
247 unparsed_opts+=11;
248 if (strcmp(unparsed_opts, "true") == 0) {
249 myPicturePrefs.allJPEG = true;
250 }
251
252 } else if (strncmp(unparsed_opts,"AllPixPNG=",10) == 0) {
253 unparsed_opts+=10;
254 if (strcmp(unparsed_opts, "true") == 0) {
255 myPicturePrefs.allPNG = true;
256 }
257
258 } else if (strncmp(unparsed_opts,"AddBothPix=",11) == 0) {
259 unparsed_opts+=11;
260 if (strcmp(unparsed_opts, "true") == 0) {
261 myPicturePrefs.addBOTHpix = true;
262 }
263
264 } else if (strcmp(unparsed_opts,"SquareUp") == 0) {
265 unparsed_opts+=7;
266 myPicturePrefs.squareUp = true;
267
268 } else if (strcmp(unparsed_opts,"removeTempPix") == 0) {
269 unparsed_opts+=13;
270 myPicturePrefs.removeTempPix = true;
271
272 } else if (strcmp(unparsed_opts,"keepTempPix") == 0) { //NEW
273 unparsed_opts+=11;
274 myPicturePrefs.removeTempPix = false;
275
276 } else if (strncmp(unparsed_opts,"ForceHeight=",12) == 0) {
277 unparsed_opts+=12;
278 myPicturePrefs.force_height = strtol(unparsed_opts, NULL, 10);
279
280 } else if (strncmp(unparsed_opts,"ForceWidth=",11) == 0) {
281 unparsed_opts+=11;
282 myPicturePrefs.force_width = strtol(unparsed_opts, NULL, 10);
283
284 } else {
285 unparsed_opts++;
286 }
287 }
288 }
289
290 if (myPicturePrefs.force_height > 0 && myPicturePrefs.force_width > 0) myPicturePrefs.force_dimensions = true;
291 return myPicturePrefs;
228 #if defined(__APPLE__)
229 PicPrefs APar_ExtractPicPrefs(char *env_PicOptions) {
230 if (!parsed_prefs) {
231
232 parsed_prefs = true; // only set default values & parse once
233
234 myPicturePrefs.max_dimension = 0; // dimensions won't be used to alter image
235 myPicturePrefs.dpi = 72;
236 myPicturePrefs.max_Kbytes = 0; // no target size to shoot for
237 myPicturePrefs.allJPEG = false;
238 myPicturePrefs.allPNG = false;
239 myPicturePrefs.addBOTHpix = false;
240 myPicturePrefs.force_dimensions = false;
241 myPicturePrefs.force_height = 0;
242 myPicturePrefs.force_width = 0;
243 myPicturePrefs.removeTempPix = true; // we'll just make this the default
244
245 char *unparsed_opts = env_PicOptions;
246 if (env_PicOptions == NULL)
247 return myPicturePrefs;
248
249 while (unparsed_opts[0] != 0) {
250 if (strncmp(unparsed_opts, "MaxDimensions=", 14) == 0) {
251 unparsed_opts += 14;
252 myPicturePrefs.max_dimension = (int)strtol(unparsed_opts, NULL, 10);
253
254 } else if (strncmp(unparsed_opts, "DPI=", 4) == 0) {
255 unparsed_opts += 4;
256 myPicturePrefs.dpi = (int)strtol(unparsed_opts, NULL, 10);
257
258 } else if (strncmp(unparsed_opts, "MaxKBytes=", 10) == 0) {
259 unparsed_opts += 10;
260 myPicturePrefs.max_Kbytes = (int)strtol(unparsed_opts, NULL, 10) * 1024;
261
262 } else if (strncmp(unparsed_opts, "AllPixJPEG=", 11) == 0) {
263 unparsed_opts += 11;
264 if (strcmp(unparsed_opts, "true") == 0) {
265 myPicturePrefs.allJPEG = true;
266 }
267
268 } else if (strncmp(unparsed_opts, "AllPixPNG=", 10) == 0) {
269 unparsed_opts += 10;
270 if (strcmp(unparsed_opts, "true") == 0) {
271 myPicturePrefs.allPNG = true;
272 }
273
274 } else if (strncmp(unparsed_opts, "AddBothPix=", 11) == 0) {
275 unparsed_opts += 11;
276 if (strcmp(unparsed_opts, "true") == 0) {
277 myPicturePrefs.addBOTHpix = true;
278 }
279
280 } else if (strcmp(unparsed_opts, "SquareUp") == 0) {
281 unparsed_opts += 7;
282 myPicturePrefs.squareUp = true;
283
284 } else if (strcmp(unparsed_opts, "removeTempPix") == 0) {
285 unparsed_opts += 13;
286 myPicturePrefs.removeTempPix = true;
287
288 } else if (strcmp(unparsed_opts, "keepTempPix") == 0) { // NEW
289 unparsed_opts += 11;
290 myPicturePrefs.removeTempPix = false;
291
292 } else if (strncmp(unparsed_opts, "ForceHeight=", 12) == 0) {
293 unparsed_opts += 12;
294 myPicturePrefs.force_height = strtol(unparsed_opts, NULL, 10);
295
296 } else if (strncmp(unparsed_opts, "ForceWidth=", 11) == 0) {
297 unparsed_opts += 11;
298 myPicturePrefs.force_width = strtol(unparsed_opts, NULL, 10);
299
300 } else {
301 unparsed_opts++;
302 }
303 }
304 }
305
306 if (myPicturePrefs.force_height > 0 && myPicturePrefs.force_width > 0)
307 myPicturePrefs.force_dimensions = true;
308 return myPicturePrefs;
292309 }
293310 #endif
294311
295312 ///////////////////////////////////////////////////////////////////////////////////////
296 // Locating/Finding Atoms //
313 // Locating/Finding Atoms //
297314 ///////////////////////////////////////////////////////////////////////////////////////
298315
299 AtomicInfo* APar_FindAtomInTrack(uint8_t &total_tracks, uint8_t &track_num, const char* search_atom_str) {
300 uint8_t track_tally = 0;
301 short iter = 0;
302
303 while (parsedAtoms[iter].NextAtomNumber != 0) {
304 if ( memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 && parsedAtoms[iter].AtomicLevel == 2) {
305 track_tally += 1;
306 if (track_num == 0) {
307 total_tracks += 1;
308
309 } else if (track_num == track_tally) {
310 //drill down into stsd
311 short next_atom = parsedAtoms[iter].NextAtomNumber;
312 while (parsedAtoms[next_atom].AtomicLevel > parsedAtoms[iter].AtomicLevel) {
313
314 if (strncmp(parsedAtoms[next_atom].AtomicName, search_atom_str, 4) == 0) {
315 return &parsedAtoms[next_atom];
316 } else {
317 next_atom = parsedAtoms[next_atom].NextAtomNumber;
318 }
319 }
320 }
321 }
322 iter=parsedAtoms[iter].NextAtomNumber;
323 }
324 return NULL;
316 AtomicInfo *APar_FindAtomInTrack(uint8_t &total_tracks,
317 uint8_t &track_num,
318 const char *search_atom_str) {
319 uint8_t track_tally = 0;
320 short iter = 0;
321
322 while (parsedAtoms[iter].NextAtomNumber != 0) {
323 if (memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 &&
324 parsedAtoms[iter].AtomicLevel == 2) {
325 track_tally += 1;
326 if (track_num == 0) {
327 total_tracks += 1;
328
329 } else if (track_num == track_tally) {
330 // drill down into stsd
331 short next_atom = parsedAtoms[iter].NextAtomNumber;
332 while (parsedAtoms[next_atom].AtomicLevel >
333 parsedAtoms[iter].AtomicLevel) {
334
335 if (strncmp(parsedAtoms[next_atom].AtomicName, search_atom_str, 4) ==
336 0) {
337 return &parsedAtoms[next_atom];
338 } else {
339 next_atom = parsedAtoms[next_atom].NextAtomNumber;
340 }
341 }
342 }
343 }
344 iter = parsedAtoms[iter].NextAtomNumber;
345 }
346 return NULL;
325347 }
326348
327349 short APar_FindPrecedingAtom(short an_atom_num) {
328 short precedingAtom = 0;
329 short iter = 0;
330 while (parsedAtoms[iter].NextAtomNumber != 0) {
331 if (parsedAtoms[iter].NextAtomNumber == parsedAtoms[an_atom_num].NextAtomNumber) {
332 break;
333 } else {
334 precedingAtom = iter;
335 iter=parsedAtoms[iter].NextAtomNumber;
336 }
337 }
338 return precedingAtom;
350 short precedingAtom = 0;
351 short iter = 0;
352 while (parsedAtoms[iter].NextAtomNumber != 0) {
353 if (parsedAtoms[iter].NextAtomNumber ==
354 parsedAtoms[an_atom_num].NextAtomNumber) {
355 break;
356 } else {
357 precedingAtom = iter;
358 iter = parsedAtoms[iter].NextAtomNumber;
359 }
360 }
361 return precedingAtom;
339362 }
340363
341364 short APar_FindParentAtom(int order_in_tree, uint8_t this_atom_level) {
342 short thisAtom = 0;
343 short iter = order_in_tree;
344 while (parsedAtoms[iter].AtomicNumber != 0) {
345 iter = APar_FindPrecedingAtom(iter);
346 if (parsedAtoms[iter].AtomicLevel == this_atom_level-1) {
347 thisAtom = iter;
348 break;
349 }
350 }
351 return thisAtom;
365 short thisAtom = 0;
366 short iter = order_in_tree;
367 while (parsedAtoms[iter].AtomicNumber != 0) {
368 iter = APar_FindPrecedingAtom(iter);
369 if (parsedAtoms[iter].AtomicLevel == this_atom_level - 1) {
370 thisAtom = iter;
371 break;
372 }
373 }
374 return thisAtom;
352375 }
353376
354377 /*----------------------
355378 APar_ProvideAtomPath
356379 this_atom - index into array of parsedAtoms for the wanted path of an atom
357380 atom_path - string into which the path will be placed (working backwards)
358 fromFile - controls the manner of extracting parents (atom sizes from file, or a simpler atomic level if from memory)
359
360 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
361 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.
362 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
363 other parents will not have sizing automatically updated - which happens only at writeout time).
381 fromFile - controls the manner of extracting parents (atom sizes from file, or
382 a simpler atomic level if from memory)
383
384 First, determine exactly how many atoms will constitute the full path and
385 calculate where into the string to first start placing atom names. Start by
386 working off the current atom. Using fromFile, either use a more
387 stringent atom start/length from a file, or a more relaxed atom level if from
388 memory. The array in memory won't have proper atom sizes except for the last
389 child atom typically ('data' will have a proper size, but its parent and all
390 other parents will not have sizing automatically updated - which
391 happens only at writeout time).
364392 ----------------------*/
365 void APar_ProvideAtomPath(short this_atom, char* &atom_path, bool fromFile) {
366 short preceding_atom = this_atom;
367 uint8_t current_atomic_level = parsedAtoms[this_atom].AtomicLevel;
368 int str_offset = (parsedAtoms[this_atom].AtomicLevel-1) * 5; //5 = 'atom" + '.'
369 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) {
370 str_offset+=5; //include a "uuid=" string;
371 }
372
373 memcpy(atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4);
374 str_offset -=5;
375 if (parsedAtoms[preceding_atom].AtomicLevel != 1) {
376 memcpy(atom_path + str_offset +4, ".", 1);
377 }
378 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) {
379 memcpy(atom_path + str_offset, "uuid=", 5);
380 str_offset-=5;
381 }
382
383 while (parsedAtoms[preceding_atom].AtomicNumber != 0) {
384
385 if (fromFile) {
386 if (parsedAtoms[preceding_atom].AtomicStart < parsedAtoms[this_atom].AtomicStart &&
387 parsedAtoms[preceding_atom].AtomicLength > parsedAtoms[this_atom].AtomicLength &&
388 parsedAtoms[preceding_atom].AtomicStart + parsedAtoms[preceding_atom].AtomicLength >= parsedAtoms[this_atom].AtomicStart + parsedAtoms[this_atom].AtomicLength &&
389 parsedAtoms[preceding_atom].AtomicContainerState <= DUAL_STATE_ATOM ) {
390 memcpy(atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4);
391 str_offset -=5;
392 if (str_offset >= 0) {
393 memcpy(atom_path + str_offset +4, ".", 1);
394 }
395
396 preceding_atom = APar_FindPrecedingAtom(preceding_atom);
397
398 } else {
399 preceding_atom = APar_FindPrecedingAtom(preceding_atom);
400 }
401 } else {
402 if (parsedAtoms[preceding_atom].AtomicLevel < current_atomic_level) {
403 memcpy(atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4);
404 str_offset -=5;
405 if (str_offset >= 0) {
406 memcpy(atom_path + str_offset +4, ".", 1);
407 }
408
409 current_atomic_level = parsedAtoms[preceding_atom].AtomicLevel;
410 preceding_atom = APar_FindPrecedingAtom(preceding_atom);
411 } else {
412 preceding_atom = APar_FindPrecedingAtom(preceding_atom);
413 }
414 }
415 if (preceding_atom == 0 || str_offset < 0) {
416 break;
417 }
418 }
419
420 return;
393 void APar_ProvideAtomPath(short this_atom, char *&atom_path, bool fromFile) {
394 short preceding_atom = this_atom;
395 uint8_t current_atomic_level = parsedAtoms[this_atom].AtomicLevel;
396 int str_offset =
397 (parsedAtoms[this_atom].AtomicLevel - 1) * 5; // 5 = 'atom" + '.'
398 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) {
399 str_offset += 5; // include a "uuid=" string;
400 }
401
402 memcpy(atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4);
403 str_offset -= 5;
404 if (parsedAtoms[preceding_atom].AtomicLevel != 1) {
405 memcpy(atom_path + str_offset + 4, ".", 1);
406 }
407 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) {
408 memcpy(atom_path + str_offset, "uuid=", 5);
409 str_offset -= 5;
410 }
411
412 while (parsedAtoms[preceding_atom].AtomicNumber != 0) {
413
414 if (fromFile) {
415 if (parsedAtoms[preceding_atom].AtomicStart <
416 parsedAtoms[this_atom].AtomicStart &&
417 parsedAtoms[preceding_atom].AtomicLength >
418 parsedAtoms[this_atom].AtomicLength &&
419 parsedAtoms[preceding_atom].AtomicStart +
420 parsedAtoms[preceding_atom].AtomicLength >=
421 parsedAtoms[this_atom].AtomicStart +
422 parsedAtoms[this_atom].AtomicLength &&
423 parsedAtoms[preceding_atom].AtomicContainerState <= DUAL_STATE_ATOM) {
424 memcpy(
425 atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4);
426 str_offset -= 5;
427 if (str_offset >= 0) {
428 memcpy(atom_path + str_offset + 4, ".", 1);
429 }
430
431 preceding_atom = APar_FindPrecedingAtom(preceding_atom);
432
433 } else {
434 preceding_atom = APar_FindPrecedingAtom(preceding_atom);
435 }
436 } else {
437 if (parsedAtoms[preceding_atom].AtomicLevel < current_atomic_level) {
438 memcpy(
439 atom_path + str_offset, parsedAtoms[preceding_atom].AtomicName, 4);
440 str_offset -= 5;
441 if (str_offset >= 0) {
442 memcpy(atom_path + str_offset + 4, ".", 1);
443 }
444
445 current_atomic_level = parsedAtoms[preceding_atom].AtomicLevel;
446 preceding_atom = APar_FindPrecedingAtom(preceding_atom);
447 } else {
448 preceding_atom = APar_FindPrecedingAtom(preceding_atom);
449 }
450 }
451 if (preceding_atom == 0 || str_offset < 0) {
452 break;
453 }
454 }
455
456 return;
421457 }
422458
423459 bool APar_Eval_ChunkOffsetImpact(short an_atom_num) {
424 bool impact_calculations_directly = false;
425 short iter = 0;
426 uint8_t found_desired_atom = 0;
427
428 while (true) {
429 if ( strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0) {
430 if (found_desired_atom) {
431 impact_calculations_directly = true;
432 }
433 break;
434 } else {
435 iter=parsedAtoms[iter].NextAtomNumber;
436 }
437 if (iter == 0) {
438 break;
439 }
440 if (iter == an_atom_num) {
441 found_desired_atom = 1;
442 }
443 }
444 return impact_calculations_directly;
460 bool impact_calculations_directly = false;
461 short iter = 0;
462 uint8_t found_desired_atom = 0;
463
464 while (true) {
465 if (strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0) {
466 if (found_desired_atom) {
467 impact_calculations_directly = true;
468 }
469 break;
470 } else {
471 iter = parsedAtoms[iter].NextAtomNumber;
472 }
473 if (iter == 0) {
474 break;
475 }
476 if (iter == an_atom_num) {
477 found_desired_atom = 1;
478 }
479 }
480 return impact_calculations_directly;
445481 }
446482
447483 short APar_FindLastAtom() {
448 short this_atom_num = 0; //start our search with the first atom
449 while (parsedAtoms[this_atom_num].NextAtomNumber != 0) {
450 this_atom_num = parsedAtoms[this_atom_num].NextAtomNumber;
451 }
452 return this_atom_num;
484 short this_atom_num = 0; // start our search with the first atom
485 while (parsedAtoms[this_atom_num].NextAtomNumber != 0) {
486 this_atom_num = parsedAtoms[this_atom_num].NextAtomNumber;
487 }
488 return this_atom_num;
453489 }
454490
455491 short APar_FindLastChild_of_ParentAtom(short thisAtom) {
456 short child_atom = parsedAtoms[thisAtom].NextAtomNumber;
457 short last_atom = thisAtom; //if there are no children, this will be the first and last atom in the hiearchy
458 while (true) {
459 if (parsedAtoms[ child_atom ].AtomicLevel > parsedAtoms[thisAtom].AtomicLevel) {
460 last_atom = child_atom;
461 }
462 child_atom = parsedAtoms[child_atom].NextAtomNumber;
463 if (child_atom == 0 || parsedAtoms[ child_atom ].AtomicLevel <= parsedAtoms[thisAtom].AtomicLevel) {
464 break;
465 }
466 }
467 return last_atom;
492 short child_atom = parsedAtoms[thisAtom].NextAtomNumber;
493 short last_atom = thisAtom; // if there are no children, this will be the
494 // first and last atom in the hiearchy
495 while (true) {
496 if (parsedAtoms[child_atom].AtomicLevel >
497 parsedAtoms[thisAtom].AtomicLevel) {
498 last_atom = child_atom;
499 }
500 child_atom = parsedAtoms[child_atom].NextAtomNumber;
501 if (child_atom == 0 || parsedAtoms[child_atom].AtomicLevel <=
502 parsedAtoms[thisAtom].AtomicLevel) {
503 break;
504 }
505 }
506 return last_atom;
468507 }
469508
470509 /*----------------------
471510 APar_ReturnChildrenAtoms
472 this_atom - the parent atom that contains any number of children atoms (that are currenly unknown)
473 atom_index - the index of the desired child. Passing 0 will return a count of the *total* number of children atoms under this_atom
474
475 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
476 total_children should match our index, return that desired child at index atom.
511 this_atom - the parent atom that contains any number of children atoms (that
512 are currenly unknown) atom_index - the index of the desired child. Passing 0
513 will return a count of the *total* number of children atoms under this_atom
514
515 Working off of AtomicLevel, test the atoms that follow this_atom to see if
516 they are immediately below this_atom. Increment total_children if is - if
517 total_children should match our index, return that desired child
518 at index atom.
477519 ----------------------*/
478520 short APar_ReturnChildrenAtoms(short this_atom, uint8_t atom_index) {
479 short child_atom = 0;
480 uint8_t total_children = 0;
481 short iter = parsedAtoms[this_atom].NextAtomNumber;
482
483 while (true) {
484 if ( (parsedAtoms[iter].AtomicLevel == parsedAtoms[this_atom].AtomicLevel + 1 && this_atom > 0) ||
485 (this_atom == 0 && parsedAtoms[iter].AtomicLevel == 1) ) {
486 total_children++;
487
488 if (atom_index == total_children) {
489 child_atom = iter;
490 break;
491 }
492 }
493 if (parsedAtoms[iter].AtomicLevel <= parsedAtoms[this_atom].AtomicLevel && this_atom != 0) {
494 break;
495 } else {
496 iter = parsedAtoms[iter].NextAtomNumber;
497 }
498 if (iter == 0) {
499 break;
500 }
501 }
502 if (atom_index == 0) {
503 child_atom = (short)total_children;
504 }
505 return child_atom;
521 short child_atom = 0;
522 uint8_t total_children = 0;
523 short iter = parsedAtoms[this_atom].NextAtomNumber;
524
525 while (true) {
526 if ((parsedAtoms[iter].AtomicLevel ==
527 parsedAtoms[this_atom].AtomicLevel + 1 &&
528 this_atom > 0) ||
529 (this_atom == 0 && parsedAtoms[iter].AtomicLevel == 1)) {
530 total_children++;
531
532 if (atom_index == total_children) {
533 child_atom = iter;
534 break;
535 }
536 }
537 if (parsedAtoms[iter].AtomicLevel <= parsedAtoms[this_atom].AtomicLevel &&
538 this_atom != 0) {
539 break;
540 } else {
541 iter = parsedAtoms[iter].NextAtomNumber;
542 }
543 if (iter == 0) {
544 break;
545 }
546 }
547 if (atom_index == 0) {
548 child_atom = (short)total_children;
549 }
550 return child_atom;
506551 }
507552
508553 /*----------------------
509554 APar_FindChildAtom
510 parent_atom - the parent atom that contains any number of children atoms (that are currenly unknown)
511 child_name - the name of the atom to search for under the parent_atom
555 parent_atom - the parent atom that contains any number of children atoms (that
556 are currenly unknown) child_name - the name of the atom to search for under the
557 parent_atom
512558
513559 Given an atom, search its children for child_name
514560 ----------------------*/
515 AtomicInfo* APar_FindChildAtom(short parent_atom, const char* child_name, uint8_t child_name_len = 4, uint16_t desired_index = 1) {
516 AtomicInfo* found_child = NULL;
517 short test_child_idx = parsedAtoms[parent_atom].NextAtomNumber;
518 uint16_t current_count = 0;
519
520 while (parsedAtoms[test_child_idx].AtomicLevel > parsedAtoms[parent_atom].AtomicLevel) {
521 if ( (memcmp(parsedAtoms[test_child_idx].AtomicName, child_name, child_name_len) == 0 || memcmp(child_name, "any", 4) == 0) &&
522 parsedAtoms[test_child_idx].AtomicLevel == parsedAtoms[parent_atom].AtomicLevel +1) {
523 current_count++;
524 if (desired_index == current_count) {
525 found_child = &parsedAtoms[test_child_idx];
526 break;
527 }
528 }
529 test_child_idx = parsedAtoms[test_child_idx].NextAtomNumber;
530 }
531
532 return found_child;
561 AtomicInfo *APar_FindChildAtom(short parent_atom,
562 const char *child_name,
563 uint8_t child_name_len = 4,
564 uint16_t desired_index = 1) {
565 AtomicInfo *found_child = NULL;
566 short test_child_idx = parsedAtoms[parent_atom].NextAtomNumber;
567 uint16_t current_count = 0;
568
569 while (parsedAtoms[test_child_idx].AtomicLevel >
570 parsedAtoms[parent_atom].AtomicLevel) {
571 if ((memcmp(parsedAtoms[test_child_idx].AtomicName,
572 child_name,
573 child_name_len) == 0 ||
574 memcmp(child_name, "any", 4) == 0) &&
575 parsedAtoms[test_child_idx].AtomicLevel ==
576 parsedAtoms[parent_atom].AtomicLevel + 1) {
577 current_count++;
578 if (desired_index == current_count) {
579 found_child = &parsedAtoms[test_child_idx];
580 break;
581 }
582 }
583 test_child_idx = parsedAtoms[test_child_idx].NextAtomNumber;
584 }
585
586 return found_child;
533587 }
534588
535589 /*----------------------
536590 APar_AtomicComparison
537 proto_atom - the temporary atom structure to run the tests on
538 test_atom - the exising atom to compare the proto_atom against
539 match_full_uuids - selects whether to match by atom names (4 bytes) or uuids(16 bytes) which are stored on AtomicName
540 reverseDNSdomain - the reverse DNS like com.foo.thing (only used with reverseDNS atoms: ----, mean, name)
541
542 Test if proto_atom matches a single atom (test_atom) by name, level & classification (packed_lang_atom, extended atom...); for certain types of data
543 (like packed_lang & reverseDNS 'moov.udta.meta.ilst.----.name:[iTunNORM] atoms currently) add finer grained tests. The return result will be NULL
544 if not matched, or returns the atom it matches.
591 proto_atom - the temporary atom structure to run the tests on
592 test_atom - the exising atom to compare the proto_atom against
593 match_full_uuids - selects whether to match by atom names (4 bytes) or
594 uuids(16 bytes) which are stored on AtomicName reverseDNSdomain - the reverse
595 DNS like com.foo.thing (only used with reverseDNS atoms: ----, mean, name)
596
597 Test if proto_atom matches a single atom (test_atom) by name, level &
598 classification (packed_lang_atom, extended atom...); for certain types of data
599 (like packed_lang & reverseDNS
600 'moov.udta.meta.ilst.----.name:[iTunNORM] atoms currently) add finer grained
601 tests. The return result will be NULL if not matched, or returns the atom it
602 matches.
545603 ----------------------*/
546 AtomicInfo* APar_AtomicComparison(AtomicInfo* proto_atom, short test_atom, bool match_full_uuids, const char* reverseDNSdomain) {
547 AtomicInfo* return_atom = NULL;
548 size_t ATOM_TEST_LEN = (match_full_uuids ? 16 : 4);
549
550 if (parsedAtoms[test_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[test_atom].uuid_style == UUID_DEPRECATED_FORM) { //accommodate deprecated form
551 if (memcmp(parsedAtoms[test_atom].uuid_ap_atomname, proto_atom->AtomicName, 4) == 0) {
552 return &parsedAtoms[test_atom];
553 }
554 }
555
556 //can't do AtomicVerFlags because lots of utilities don't write the proper iTunes flags for iTunes metadata
557 if ( memcmp(proto_atom->AtomicName, parsedAtoms[test_atom].AtomicName, ATOM_TEST_LEN) == 0 &&
558 proto_atom->AtomicLevel == parsedAtoms[test_atom].AtomicLevel &&
559 (proto_atom->AtomicClassification == parsedAtoms[test_atom].AtomicClassification || proto_atom->AtomicClassification == UNKNOWN_ATOM) ) {
560
561 if (proto_atom->AtomicClassification == PACKED_LANG_ATOM) {
562 //0x05D9 = 'any' and will be used (internally) to match on name,class,container state alone, disregarding AtomicLanguage
563 if (proto_atom->AtomicLanguage == parsedAtoms[test_atom].AtomicLanguage || proto_atom->AtomicLanguage == 0x05D9) {
564 return_atom = &parsedAtoms[test_atom];
565 }
566
567 } else if (proto_atom->ReverseDNSname != NULL && parsedAtoms[test_atom].ReverseDNSname != NULL) {
568 //match on moov.udta.meta.ilst.----.name:[something] (reverse DNS atom)
569 if (strcmp(proto_atom->ReverseDNSname, parsedAtoms[test_atom].ReverseDNSname) == 0) {
570 if (reverseDNSdomain == NULL) { //lock onto the first reverseDNS form irrespective of domain (TODO: manualAtomRemove will cause this to be NULL)
571 return_atom = &parsedAtoms[test_atom];
572 } else {
604 AtomicInfo *APar_AtomicComparison(AtomicInfo *proto_atom,
605 short test_atom,
606 bool match_full_uuids,
607 const char *reverseDNSdomain) {
608 AtomicInfo *return_atom = NULL;
609 size_t ATOM_TEST_LEN = (match_full_uuids ? 16 : 4);
610
611 if (parsedAtoms[test_atom].AtomicClassification == EXTENDED_ATOM &&
612 parsedAtoms[test_atom].uuid_style ==
613 UUID_DEPRECATED_FORM) { // accommodate deprecated form
614 if (memcmp(parsedAtoms[test_atom].uuid_ap_atomname,
615 proto_atom->AtomicName,
616 4) == 0) {
617 return &parsedAtoms[test_atom];
618 }
619 }
620
621 // can't do AtomicVerFlags because lots of utilities don't write the proper
622 // iTunes flags for iTunes metadata
623 if (memcmp(proto_atom->AtomicName,
624 parsedAtoms[test_atom].AtomicName,
625 ATOM_TEST_LEN) == 0 &&
626 proto_atom->AtomicLevel == parsedAtoms[test_atom].AtomicLevel &&
627 (proto_atom->AtomicClassification ==
628 parsedAtoms[test_atom].AtomicClassification ||
629 proto_atom->AtomicClassification == UNKNOWN_ATOM)) {
630
631 if (proto_atom->AtomicClassification == PACKED_LANG_ATOM) {
632 // 0x05D9 = 'any' and will be used (internally) to match on
633 // name,class,container state alone, disregarding AtomicLanguage
634 if (proto_atom->AtomicLanguage == parsedAtoms[test_atom].AtomicLanguage ||
635 proto_atom->AtomicLanguage == 0x05D9) {
636 return_atom = &parsedAtoms[test_atom];
637 }
638
639 } else if (proto_atom->ReverseDNSname != NULL &&
640 parsedAtoms[test_atom].ReverseDNSname != NULL) {
641 // match on moov.udta.meta.ilst.----.name:[something] (reverse DNS atom)
642 if (strcmp(proto_atom->ReverseDNSname,
643 parsedAtoms[test_atom].ReverseDNSname) == 0) {
644 if (reverseDNSdomain ==
645 NULL) { // lock onto the first reverseDNS form irrespective of
646 // domain (TODO: manualAtomRemove will cause this to be
647 // NULL)
648 return_atom = &parsedAtoms[test_atom];
649 } else {
573650 #if defined(DEBUG_V)
574 fprintf(stdout, "AP_AtomicComparison testing wanted rDNS %s domain against atom '%s' %s rDNS domain\n", reverseDNSdomain, parsedAtoms[test_atom].AtomicName,
575 parsedAtoms[APar_FindPrecedingAtom(test_atom)].ReverseDNSdomain);
651 fprintf(
652 stdout,
653 "AP_AtomicComparison testing wanted rDNS %s domain against "
654 "atom '%s' %s rDNS domain\n",
655 reverseDNSdomain,
656 parsedAtoms[test_atom].AtomicName,
657 parsedAtoms[APar_FindPrecedingAtom(test_atom)].ReverseDNSdomain);
576658 #endif
577 if ( strcmp(reverseDNSdomain, parsedAtoms[APar_FindPrecedingAtom(test_atom)].ReverseDNSdomain) == 0 ) {
578 return_atom = &parsedAtoms[test_atom];
579 }
580 }
581 }
582 } else {
583 return_atom = &parsedAtoms[test_atom];
584 }
585 }
586 return return_atom;
659 if (strcmp(reverseDNSdomain,
660 parsedAtoms[APar_FindPrecedingAtom(test_atom)]
661 .ReverseDNSdomain) == 0) {
662 return_atom = &parsedAtoms[test_atom];
663 }
664 }
665 }
666 } else {
667 return_atom = &parsedAtoms[test_atom];
668 }
669 }
670 return return_atom;
587671 }
588672
589673 /*----------------------
590674 APar_FindLastLikeNamedAtom
591 atom_name - the name of the atom to search for; the string itself may have more than 4 bytes
592 containing_hierarchy - the parent hierarchy that is expected to carry multiply named atoms differing (in language for example)
593
594 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
595 of the container atom is returned; otherwise the last like named atom is returned.
675 atom_name - the name of the atom to search for; the string itself may
676 have more than 4 bytes containing_hierarchy - the parent hierarchy that is
677 expected to carry multiply named atoms differing (in language for example)
678
679 Follow through the atom tree; if a test atom is matched by name, and is a
680 child to the container atom, remember that atom. If nothing matches, the index
681 of the container atom is returned; otherwise the last like named
682 atom is returned.
596683 ----------------------*/
597 short APar_FindLastLikeNamedAtom(char* atom_name, short containing_hierarchy) {
598 short last_identically_named_atom = APar_FindLastChild_of_ParentAtom(containing_hierarchy); //default returns the last atom in the parent, not the parent
599 short eval_atom = parsedAtoms[containing_hierarchy].NextAtomNumber;
600
601 while (true) {
602 if (parsedAtoms[eval_atom].AtomicLevel < parsedAtoms[containing_hierarchy].AtomicLevel + 1 || eval_atom == 0) {
603 break;
604 } else {
605 if (memcmp(parsedAtoms[eval_atom].AtomicName, atom_name, 4) == 0 &&
606 parsedAtoms[eval_atom].AtomicLevel == parsedAtoms[containing_hierarchy].AtomicLevel + 1) {
607 last_identically_named_atom = eval_atom;
608 }
609 eval_atom = parsedAtoms[eval_atom].NextAtomNumber;
610 }
611 }
612 return last_identically_named_atom;
613 }
614
615 void APar_FreeSurrogateAtom(AtomicInfo* surrogate_atom) {
616 if (surrogate_atom->ReverseDNSname != NULL) {
617 free(surrogate_atom->ReverseDNSname);
618 surrogate_atom->ReverseDNSname = NULL;
619 }
620 return;
684 short APar_FindLastLikeNamedAtom(char *atom_name, short containing_hierarchy) {
685 short last_identically_named_atom = APar_FindLastChild_of_ParentAtom(
686 containing_hierarchy); // default returns the last atom in the parent, not
687 // the parent
688 short eval_atom = parsedAtoms[containing_hierarchy].NextAtomNumber;
689
690 while (true) {
691 if (parsedAtoms[eval_atom].AtomicLevel <
692 parsedAtoms[containing_hierarchy].AtomicLevel + 1 ||
693 eval_atom == 0) {
694 break;
695 } else {
696 if (memcmp(parsedAtoms[eval_atom].AtomicName, atom_name, 4) == 0 &&
697 parsedAtoms[eval_atom].AtomicLevel ==
698 parsedAtoms[containing_hierarchy].AtomicLevel + 1) {
699 last_identically_named_atom = eval_atom;
700 }
701 eval_atom = parsedAtoms[eval_atom].NextAtomNumber;
702 }
703 }
704 return last_identically_named_atom;
705 }
706
707 void APar_FreeSurrogateAtom(AtomicInfo *surrogate_atom) {
708 if (surrogate_atom->ReverseDNSname != NULL) {
709 free(surrogate_atom->ReverseDNSname);
710 surrogate_atom->ReverseDNSname = NULL;
711 }
712 return;
621713 }
622714
623715 /*----------------------
624716 APar_CreateSurrogateAtom
625717
626 Make a temporary AtomicInfo structure to run comparisons against; currently comparisons are done on name, level, classification (versioned...), langauge
627 (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
628 atom hieararchy that resides in the parsedAtoms[] array.
718 Make a temporary AtomicInfo structure to run comparisons against; currently
719 comparisons are done on name, level, classification (versioned...), langauge
720 (3gp assets), and iTunes-style reverse dns 'name' carrying a
721 string describing the purpose of the data (iTunNORM). This atom exists outside
722 of a file's atom hieararchy that resides in the parsedAtoms[] array.
629723 ----------------------*/
630 void APar_CreateSurrogateAtom(AtomicInfo* surrogate_atom, const char* atom_name, uint8_t atom_level, uint8_t atom_class, uint16_t atom_lang,
631 char* revdns_name, uint8_t revdns_name_len) {
632 surrogate_atom->AtomicName = (char*)atom_name;
633 surrogate_atom->AtomicLevel = atom_level;
634
635 if (revdns_name != NULL && revdns_name_len) {
636 surrogate_atom->ReverseDNSname = (char *)malloc(sizeof(char)*revdns_name_len > 8 ? revdns_name_len+1 : 9);
637 memset(surrogate_atom->ReverseDNSname, 0, sizeof(char)*revdns_name_len > 8 ? revdns_name_len+1 : 9);
638 memcpy(surrogate_atom->ReverseDNSname, revdns_name, revdns_name_len);
639
640 } else {
641 APar_FreeSurrogateAtom(surrogate_atom);
642 }
643 surrogate_atom->AtomicClassification = atom_class;
644 surrogate_atom->AtomicLanguage = atom_lang;
645 return;
724 void APar_CreateSurrogateAtom(AtomicInfo *surrogate_atom,
725 const char *atom_name,
726 uint8_t atom_level,
727 uint8_t atom_class,
728 uint16_t atom_lang,
729 char *revdns_name,
730 uint8_t revdns_name_len) {
731 surrogate_atom->AtomicName = (char *)atom_name;
732 surrogate_atom->AtomicLevel = atom_level;
733
734 if (revdns_name != NULL && revdns_name_len) {
735 surrogate_atom->ReverseDNSname = (char *)malloc(
736 sizeof(char) * revdns_name_len > 8 ? revdns_name_len + 1 : 9);
737 memset(surrogate_atom->ReverseDNSname,
738 0,
739 sizeof(char) * revdns_name_len > 8 ? revdns_name_len + 1 : 9);
740 memcpy(surrogate_atom->ReverseDNSname, revdns_name, revdns_name_len);
741
742 } else {
743 APar_FreeSurrogateAtom(surrogate_atom);
744 }
745 surrogate_atom->AtomicClassification = atom_class;
746 surrogate_atom->AtomicLanguage = atom_lang;
747 return;
646748 }
647749
648750 /*----------------------
649751 APar_FindAtom
650 atom_name - the full path describing the hiearchy the desired atom can be found in
651 createMissing - either create the missing interim atoms as required, or return a NULL if not found
652 atom_type - the classification of the last atom (packed language, uuid extended atom...)
653 atom_lang - the language of the 3gp asset used when atom_type is packed language type
654 match_full_uuids - match 16byte full uuids (typically removing ( possibly non-AP) uuids via --manualAtomRemoval; AP uuids (the new ones) still work on 4bytes**
655 reverseDNSdomain - the reverse DNS like com.foo.thing (only used with reverseDNS atoms: ----, mean, name)
656
657 Follow through the atom tree starting with the atom following 'ftyp'. Testing occurs on an atom level basis; a stand-in temporary skeletal atom
658 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
659 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
660 exhausted without making new atoms.
661
662 NOTE: atom_name can come in these forms:
663 classic/vanilla/ordinary atoms: moov.udta.meta.ilst.cprt.data
664 iTunes reverseDNS atoms: moov.udta.meta.ilst.----.name:[iTunNORM]
665 uuid user-extension atoms: moov.udta.meta.uuid=tdtg (the deprecated form)
666 uuid user-extension atoms: moov.udta.meta.uuid=ba45fcaa-7ef5-5201-8a63-78886495ab1f
667 index-based atoms: moov.trak[2].mdia.minf
668
669 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
670 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.
671
672 ** 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
673 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).
674 This means that that matching by 4 bytes of atom name is the functional equvalent of matching by 16byte uuids.
752 atom_name - the full path describing the hiearchy the desired atom can
753 be found in createMissing - either create the missing interim atoms as required,
754 or return a NULL if not found atom_type - the classification of the last atom
755 (packed language, uuid extended atom...) atom_lang - the language of the 3gp
756 asset used when atom_type is packed language type match_full_uuids - match
757 16byte full uuids (typically removing ( possibly non-AP) uuids via
758 --manualAtomRemoval; AP uuids (the new ones) still work on 4bytes**
759 reverseDNSdomain - the reverse DNS like com.foo.thing (only used with
760 reverseDNS atoms: ----, mean, name)
761
762 Follow through the atom tree starting with the atom following 'ftyp'.
763 Testing occurs on an atom level basis; a stand-in temporary skeletal atom is
764 created to evaluate. If they atoms are deemed matching, atom_name is advanced
765 forward (it still contains the full path, but only 4bytes are typically used at
766 a time) and testing occurs until either the desired atom is found, or the last
767 containing hiearchy with an exising atom is exhausted without making new atoms.
768
769 NOTE: atom_name can come in these forms:
770 classic/vanilla/ordinary atoms:
771 moov.udta.meta.ilst.cprt.data iTunes reverseDNS atoms:
772 moov.udta.meta.ilst.----.name:[iTunNORM] uuid user-extension atoms:
773 moov.udta.meta.uuid=tdtg (the deprecated form) uuid user-extension atoms:
774 moov.udta.meta.uuid=ba45fcaa-7ef5-5201-8a63-78886495ab1f index-based atoms:
775 moov.trak[2].mdia.minf
776
777 NOTE: On my computer it takes about .04 second to scan the file,
778 .1 second to add about 2 dozen tags, and 1.0 second to copy a file. Updating a
779 file from start to finish takes 0.21 seconds. As many loops as this new
780 APar_FindAtom eliminates, it is only marginally faster than the old code.
781
782 ** the reason why the old deprecated uuid form & the new uuid
783 full 16byte form work off of a 4byte value (the atom name) is that because we
784 are using a version 5 sha1 hashed uuid of a name in a given namespace, the
785 identical name in the identical namespace will yield identical an identical uuid
786 (if corrected for endianness). This means that that matching by 4 bytes of atom
787 name is the functional equvalent of matching by 16byte uuids.
675788 ----------------------*/
676 AtomicInfo* APar_FindAtom(const char* atom_name, bool createMissing, uint8_t atom_type, uint16_t atom_lang, bool match_full_uuids, const char* reverseDNSdomain) {
677 AtomicInfo* thisAtom = NULL;
678 char* search_atom_name = (char*)atom_name;
679 char* reverse_dns_name = NULL;
680 uint8_t revdns_name_len = 0;
681 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
682 uint8_t desired_index = 1;
683 uint8_t search_atom_type = UNKNOWN_ATOM;
684 int known_atom = -1;
685 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.
686 uint8_t present_atomic_level = 1;
687 AtomicInfo* last_known_present_parent = NULL;
688 AtomicInfo atom_surrogate = { 0 };
789 AtomicInfo *APar_FindAtom(const char *atom_name,
790 bool createMissing,
791 uint8_t atom_type,
792 uint16_t atom_lang,
793 bool match_full_uuids,
794 const char *reverseDNSdomain) {
795 AtomicInfo *thisAtom = NULL;
796 char *search_atom_name = (char *)atom_name;
797 char *reverse_dns_name = NULL;
798 uint8_t revdns_name_len = 0;
799 uint8_t atom_index =
800 0; // if there are atoms mutliple identically named at the same level,
801 // this is where to store the count as it occurs
802 uint8_t desired_index = 1;
803 uint8_t search_atom_type = UNKNOWN_ATOM;
804 int known_atom = -1;
805 short search_atom_start_num =
806 parsedAtoms[0]
807 .NextAtomNumber; // don't test 'ftyp'; its atom_number[0] & will be
808 // used to know when we have hit the end of the tree;
809 // can't hardcode it to '1' because ftyp's following
810 // atom can change; only ftyp as parsedAtoms[0] is
811 // guaranteed.
812 uint8_t present_atomic_level = 1;
813 AtomicInfo *last_known_present_parent = NULL;
814 AtomicInfo atom_surrogate = {0};
689815
690816 #if defined(DEBUG_V)
691 fprintf(stdout, "debug: AP_FindAtom entry trying to find '%s'; create missing: %u\n", atom_name, createMissing);
817 fprintf(stdout,
818 "debug: AP_FindAtom entry trying to find '%s'; create missing: %u\n",
819 atom_name,
820 createMissing);
692821 #endif
693822
694 while (search_atom_name != NULL) {
695 desired_index = 1; //reset the index
696
697 if (atom_type == EXTENDED_ATOM && strncmp(search_atom_name, "uuid=", 5) == 0 ) {
698 search_atom_name+=5;
699 search_atom_type = atom_type;
700 }
823 while (search_atom_name != NULL) {
824 desired_index = 1; // reset the index
825
826 if (atom_type == EXTENDED_ATOM &&
827 strncmp(search_atom_name, "uuid=", 5) == 0) {
828 search_atom_name += 5;
829 search_atom_type = atom_type;
830 }
701831
702832 #if defined(DEBUG_V)
703 fprintf(stdout, "debug: AP_FindAtom loop evaluate test %s (index=%u)\n", search_atom_name, atom_index);
833 fprintf(stdout,
834 "debug: AP_FindAtom loop evaluate test %s (index=%u)\n",
835 search_atom_name,
836 atom_index);
704837 #endif
705838
706 size_t portion_len = strlen(search_atom_name);
707 if (strncmp(search_atom_name+4, ":[", 2) == 0 && search_atom_name[portion_len-1] == ']') {
708 reverse_dns_name = search_atom_name + 4+2; //4bytes atom name 2bytes ":["
709 revdns_name_len = portion_len-7; //4bytes atom name, 2 bytes ":[", 1 byte "]"
710 search_atom_type = atom_type;
711 } else if (search_atom_name[4] == '[') {
712 desired_index = strtoul(search_atom_name+5, NULL, 10);
839 size_t portion_len = strlen(search_atom_name);
840 if (strncmp(search_atom_name + 4, ":[", 2) == 0 &&
841 search_atom_name[portion_len - 1] == ']') {
842 reverse_dns_name =
843 search_atom_name + 4 + 2; // 4bytes atom name 2bytes ":["
844 revdns_name_len =
845 portion_len - 7; // 4bytes atom name, 2 bytes ":[", 1 byte "]"
846 search_atom_type = atom_type;
847 } else if (search_atom_name[4] == '[') {
848 desired_index = strtoul(search_atom_name + 5, NULL, 10);
713849 #if defined(DEBUG_V)
714 fprintf(stdout, "debug: AP_FindAtom >#<indexed atom>#< '%s' at index=%u\n", search_atom_name, desired_index);
850 fprintf(stdout,
851 "debug: AP_FindAtom >#<indexed atom>#< '%s' at index=%u\n",
852 search_atom_name,
853 desired_index);
715854 #endif
716 }
717
718 if (strlen(search_atom_name) == 4) {
719 if (atom_type == UNKNOWN_ATOM) {
720 known_atom = APar_MatchToKnownAtom(search_atom_name, last_known_present_parent->AtomicName, false, atom_name);
721 search_atom_type = KnownAtoms[known_atom].box_type;
722 } else {
723 search_atom_type = atom_type;
724 }
725 }
726
727 APar_CreateSurrogateAtom(&atom_surrogate, search_atom_name, present_atomic_level, search_atom_type, atom_lang, reverse_dns_name, revdns_name_len);
728 atom_index = 0;
729
730 short iter = search_atom_start_num;
731 while (true) {
732 AtomicInfo* result = NULL;
733
734 //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
735 if (iter != 0 && (parsedAtoms[iter].AtomicLevel == present_atomic_level || reverse_dns_name != NULL) ) {
736 result = APar_AtomicComparison(&atom_surrogate, iter, (search_atom_type == EXTENDED_ATOM ? match_full_uuids : false), reverseDNSdomain );
855 }
856
857 if (strlen(search_atom_name) == 4) {
858 if (atom_type == UNKNOWN_ATOM) {
859 known_atom =
860 APar_MatchToKnownAtom(search_atom_name,
861 last_known_present_parent->AtomicName,
862 false,
863 atom_name);
864 search_atom_type = KnownAtoms[known_atom].box_type;
865 } else {
866 search_atom_type = atom_type;
867 }
868 }
869
870 APar_CreateSurrogateAtom(&atom_surrogate,
871 search_atom_name,
872 present_atomic_level,
873 search_atom_type,
874 atom_lang,
875 reverse_dns_name,
876 revdns_name_len);
877 atom_index = 0;
878
879 short iter = search_atom_start_num;
880 while (true) {
881 AtomicInfo *result = NULL;
882
883 // if iter == 0, that means test against 'ftyp' - and since its always 0,
884 // don't test it; its to know that the end of the tree is reached
885 if (iter != 0 && (parsedAtoms[iter].AtomicLevel == present_atomic_level ||
886 reverse_dns_name != NULL)) {
887 result = APar_AtomicComparison(
888 &atom_surrogate,
889 iter,
890 (search_atom_type == EXTENDED_ATOM ? match_full_uuids : false),
891 reverseDNSdomain);
737892 #if defined(DEBUG_V)
738 fprintf(stdout, "debug: AP_FindAtom compare %s(%u) against %s (wanted index=%u)\n", search_atom_name, atom_index, parsedAtoms[iter].AtomicName, desired_index);
739 } else {
740 fprintf(stdout, "debug: AP_FindAtom %s rejected against %s\n", search_atom_name, parsedAtoms[iter].AtomicName);
893 fprintf(stdout,
894 "debug: AP_FindAtom compare %s(%u) against %s (wanted "
895 "index=%u)\n",
896 search_atom_name,
897 atom_index,
898 parsedAtoms[iter].AtomicName,
899 desired_index);
900 } else {
901 fprintf(stdout,
902 "debug: AP_FindAtom %s rejected against %s\n",
903 search_atom_name,
904 parsedAtoms[iter].AtomicName);
741905 #endif
742 }
743 if (result != NULL) { //something matched
744 atom_index++;
906 }
907 if (result != NULL) { // something matched
908 atom_index++;
745909 #if defined(DEBUG_V)
746 fprintf(stdout, "debug: AP_FindAtom ***matched*** current index=%u (want %u)\n", atom_index, desired_index);
910 fprintf(stdout,
911 "debug: AP_FindAtom ***matched*** current index=%u (want "
912 "%u)\n",
913 atom_index,
914 desired_index);
747915 #endif
748 if (search_atom_type != UNKNOWN_ATOM || (search_atom_type == UNKNOWN_ATOM && known_atom != -1) ) {
749 thisAtom = result;
916 if (search_atom_type != UNKNOWN_ATOM ||
917 (search_atom_type == UNKNOWN_ATOM && known_atom != -1)) {
918 thisAtom = result;
750919 #if defined(DEBUG_V)
751 fprintf(stdout, "debug: AP_FindAtom perfect match: %s(%u) == existing %s(%u)\n", search_atom_name, desired_index, parsedAtoms[iter].AtomicName, atom_index);
920 fprintf(stdout,
921 "debug: AP_FindAtom perfect match: %s(%u) == "
922 "existing %s(%u)\n",
923 search_atom_name,
924 desired_index,
925 parsedAtoms[iter].AtomicName,
926 atom_index);
752927 #endif
753 } else {
754 last_known_present_parent = result; //if not, then it isn't the last atom, and must be some form of parent
755 }
756 if (desired_index == atom_index) {
757 search_atom_start_num = parsedAtoms[iter].NextAtomNumber;
758 break;
759 }
760 }
761
762 if (parsedAtoms[iter].AtomicLevel < present_atomic_level && reverse_dns_name == NULL) {
763 iter = 0; //force the ending determination of whether to make new atoms or not;
764 }
765
766 if (iter == 0 && createMissing) {
767 //create that atom
768 if (last_known_present_parent != NULL) {
769 short last_hierarchical_atom = 0;
928 } else {
929 last_known_present_parent =
930 result; // if not, then it isn't the last atom, and must be some
931 // form of parent
932 }
933 if (desired_index == atom_index) {
934 search_atom_start_num = parsedAtoms[iter].NextAtomNumber;
935 break;
936 }
937 }
938
939 if (parsedAtoms[iter].AtomicLevel < present_atomic_level &&
940 reverse_dns_name == NULL) {
941 iter = 0; // force the ending determination of whether to make new atoms
942 // or not;
943 }
944
945 if (iter == 0 && createMissing) {
946 // create that atom
947 if (last_known_present_parent != NULL) {
948 short last_hierarchical_atom = 0;
770949 #if defined(DEBUG_V)
771 fprintf(stdout, "debug: AP_FindAtom-------missing atom, need to create '%s'\n", search_atom_name);
950 fprintf(
951 stdout,
952 "debug: AP_FindAtom-------missing atom, need to create '%s'\n",
953 search_atom_name);
772954 #endif
773 if (search_atom_type == PACKED_LANG_ATOM) {
774 last_hierarchical_atom = APar_FindLastLikeNamedAtom(atom_surrogate.AtomicName, last_known_present_parent->AtomicNumber);
775 } else {
776 last_hierarchical_atom = APar_FindLastChild_of_ParentAtom(last_known_present_parent->AtomicNumber);
777 }
778 thisAtom = APar_CreateSparseAtom(&atom_surrogate, last_known_present_parent, last_hierarchical_atom);
779 search_atom_start_num = thisAtom->AtomicNumber;
780 if (strlen(search_atom_name) >= 4) {
781 last_known_present_parent = thisAtom;
782 }
783 } else {
784 //its a file-level atom that needs to be created, so it won't have a last_known_present_parent
785 if (strlen(atom_name) == 4) {
786 short total_root_level_atoms = APar_ReturnChildrenAtoms (0, 0);
787 short test_root_atom = 0;
788
789 //scan through all top level atoms
790 for(uint8_t root_atom_i = 1; root_atom_i <= total_root_level_atoms; root_atom_i++) {
791 test_root_atom = APar_ReturnChildrenAtoms (0, root_atom_i);
792 if (memcmp(parsedAtoms[test_root_atom].AtomicName, "moov", 4) == 0) {
793 break;
794 }
795 }
796 if (test_root_atom != 0) {
797 thisAtom = APar_CreateSparseAtom(&atom_surrogate, NULL, APar_FindLastChild_of_ParentAtom(test_root_atom));
798 }
799 }
800 }
801 break;
802 } else if (iter == 0 && !createMissing) {
803 search_atom_name = NULL; //force the break;
804 break;
805 }
806 //fprintf(stdout, "while loop %s %u %u\n", parsedAtoms[iter].AtomicName, atom_index, desired_index);
807 iter = parsedAtoms[iter].NextAtomNumber;
808 }
809
810 if (iter == 0 && (search_atom_name == NULL || search_atom_type == EXTENDED_ATOM) ) {
811 break;
812 } else {
813 uint8_t periodicity = 0; //allow atoms with periods in their names
814 while (true) { // search_atom_name = strsep(&atom_name,".") equivalent
815 if (search_atom_name[0] == 0) {
816 search_atom_name = NULL;
817 break;
818 } else if (search_atom_name[0] == '.' && periodicity > 3) {
819 search_atom_name++;
820 periodicity++;
821 break;
822 } else {
823 search_atom_name++;
824 periodicity++;
825 }
826 }
827 present_atomic_level++;
828 }
829 }
830 //APar_PrintAtomicTree(); //because PrintAtomicTree calls DetermineDynamicUpdate (which calls this FindAtom function) to print out padding space, an infinite loop occurs
831 APar_FreeSurrogateAtom(&atom_surrogate);
832 return thisAtom;
955 if (search_atom_type == PACKED_LANG_ATOM) {
956 last_hierarchical_atom = APar_FindLastLikeNamedAtom(
957 atom_surrogate.AtomicName,
958 last_known_present_parent->AtomicNumber);
959 } else {
960 last_hierarchical_atom = APar_FindLastChild_of_ParentAtom(
961 last_known_present_parent->AtomicNumber);
962 }
963 thisAtom = APar_CreateSparseAtom(&atom_surrogate,
964 last_known_present_parent,
965 last_hierarchical_atom);
966 search_atom_start_num = thisAtom->AtomicNumber;
967 if (strlen(search_atom_name) >= 4) {
968 last_known_present_parent = thisAtom;
969 }
970 } else {
971 // its a file-level atom that needs to be created, so it won't have a
972 // last_known_present_parent
973 if (strlen(atom_name) == 4) {
974 short total_root_level_atoms = APar_ReturnChildrenAtoms(0, 0);
975 short test_root_atom = 0;
976
977 // scan through all top level atoms
978 for (uint8_t root_atom_i = 1; root_atom_i <= total_root_level_atoms;
979 root_atom_i++) {
980 test_root_atom = APar_ReturnChildrenAtoms(0, root_atom_i);
981 if (memcmp(parsedAtoms[test_root_atom].AtomicName, "moov", 4) ==
982 0) {
983 break;
984 }
985 }
986 if (test_root_atom != 0) {
987 thisAtom = APar_CreateSparseAtom(
988 &atom_surrogate,
989 NULL,
990 APar_FindLastChild_of_ParentAtom(test_root_atom));
991 }
992 }
993 }
994 break;
995 } else if (iter == 0 && !createMissing) {
996 search_atom_name = NULL; // force the break;
997 break;
998 }
999 // fprintf(stdout, "while loop %s %u %u\n", parsedAtoms[iter].AtomicName,
1000 // atom_index, desired_index);
1001 iter = parsedAtoms[iter].NextAtomNumber;
1002 }
1003
1004 if (iter == 0 &&
1005 (search_atom_name == NULL || search_atom_type == EXTENDED_ATOM)) {
1006 break;
1007 } else {
1008 uint8_t periodicity = 0; // allow atoms with periods in their names
1009 while (true) { // search_atom_name = strsep(&atom_name,".") equivalent
1010 if (search_atom_name[0] == 0) {
1011 search_atom_name = NULL;
1012 break;
1013 } else if (search_atom_name[0] == '.' && periodicity > 3) {
1014 search_atom_name++;
1015 periodicity++;
1016 break;
1017 } else {
1018 search_atom_name++;
1019 periodicity++;
1020 }
1021 }
1022 present_atomic_level++;
1023 }
1024 }
1025 // APar_PrintAtomicTree(); //because PrintAtomicTree calls
1026 // DetermineDynamicUpdate (which calls this FindAtom function) to print out
1027 // padding space, an infinite loop occurs
1028 APar_FreeSurrogateAtom(&atom_surrogate);
1029 return thisAtom;
8331030 }
8341031
8351032 ///////////////////////////////////////////////////////////////////////////////////////
836 // File scanning & atom parsing //
1033 // File scanning & atom parsing //
8371034 ///////////////////////////////////////////////////////////////////////////////////////
8381035
839 void APar_AtomizeFileInfo(uint64_t Astart, uint64_t Alength,
840 uint64_t Aextendedlength, char* Astring, uint8_t Alevel,
841 uint8_t Acon_state, uint8_t Aclass, uint32_t Averflags,
842 uint16_t Alang, uuid_vitals* uuid_info)
843 {
844 static bool passed_mdat = false;
845 AtomicInfo* thisAtom;
846
847 if (atom_number < 0 || atom_number >= MAX_ATOMS) {
848 fprintf(stderr, "too many atoms\n");
849 abort();
850 }
851
852 thisAtom = &parsedAtoms[atom_number];
853
854 thisAtom->AtomicStart = Astart;
855 thisAtom->AtomicLength = Alength;
856 thisAtom->AtomicLengthExtended = Aextendedlength;
857 thisAtom->AtomicNumber = atom_number;
858 thisAtom->AtomicLevel = Alevel;
859 thisAtom->AtomicContainerState = Acon_state;
860 thisAtom->AtomicClassification = Aclass;
861
862 thisAtom->AtomicName = (char*)malloc(sizeof(char)*20);
863 memset(thisAtom->AtomicName, 0, sizeof(char)*20);
864
865 if (Aclass == EXTENDED_ATOM) {
866 thisAtom->uuid_style = uuid_info->uuid_form;
867 if (uuid_info->uuid_form == UUID_DEPRECATED_FORM) {
868 memcpy(thisAtom->AtomicName, Astring, 4);
869 thisAtom->uuid_ap_atomname = (char*)calloc(1, sizeof(char)*16);
870 memcpy(thisAtom->uuid_ap_atomname, Astring, 4);
871 } else {
872 memcpy(thisAtom->AtomicName, uuid_info->binary_uuid, 16);
873 if (uuid_info->uuid_form == UUID_AP_SHA1_NAMESPACE) {
874 thisAtom->uuid_ap_atomname = (char*)calloc(1, sizeof(char)*16);
875 memcpy(thisAtom->uuid_ap_atomname, uuid_info->uuid_AP_atom_name, 4);
876 }
877 }
878 } else {
879 memcpy(thisAtom->AtomicName, Astring, 4);
880 }
881
882 thisAtom->AtomicVerFlags = Averflags;
883 thisAtom->AtomicLanguage = Alang;
884
885 thisAtom->ancillary_data = 0;
886
887 //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
888 if (atom_number > 0) {
889 parsedAtoms[atom_number-1].NextAtomNumber = atom_number;
890 }
891 thisAtom->NextAtomNumber=0; //this could be the end... (we just can't quite say until we find another atom)
892
893 if (strncmp(Astring, "mdat", 4) == 0) {
894 passed_mdat = true;
895 }
896
897 if (!passed_mdat && Alevel == 1) {
898 bytes_before_mdat += Alength; //this value gets used during FreeFree (for removed_bytes_tally) & chunk offset calculations
899 }
900 thisAtom->ID32_TagInfo = NULL;
901
902 atom_number++; //increment to the next AtomicInfo array
903
904 return;
1036 void APar_AtomizeFileInfo(uint64_t Astart,
1037 uint64_t Alength,
1038 uint64_t Aextendedlength,
1039 char *Astring,
1040 uint8_t Alevel,
1041 uint8_t Acon_state,
1042 uint8_t Aclass,
1043 uint32_t Averflags,
1044 uint16_t Alang,
1045 uuid_vitals *uuid_info) {
1046 static bool passed_mdat = false;
1047 AtomicInfo *thisAtom;
1048
1049 if (atom_number < 0 || atom_number >= MAX_ATOMS) {
1050 fprintf(stderr, "too many atoms\n");
1051 abort();
1052 }
1053
1054 thisAtom = &parsedAtoms[atom_number];
1055
1056 thisAtom->AtomicStart = Astart;
1057 thisAtom->AtomicLength = Alength;
1058 thisAtom->AtomicLengthExtended = Aextendedlength;
1059 thisAtom->AtomicNumber = atom_number;
1060 thisAtom->AtomicLevel = Alevel;
1061 thisAtom->AtomicContainerState = Acon_state;
1062 thisAtom->AtomicClassification = Aclass;
1063
1064 thisAtom->AtomicName = (char *)malloc(sizeof(char) * 20);
1065 memset(thisAtom->AtomicName, 0, sizeof(char) * 20);
1066
1067 if (Aclass == EXTENDED_ATOM) {
1068 thisAtom->uuid_style = uuid_info->uuid_form;
1069 if (uuid_info->uuid_form == UUID_DEPRECATED_FORM) {
1070 memcpy(thisAtom->AtomicName, Astring, 4);
1071 thisAtom->uuid_ap_atomname = (char *)calloc(1, sizeof(char) * 16);
1072 memcpy(thisAtom->uuid_ap_atomname, Astring, 4);
1073 } else {
1074 memcpy(thisAtom->AtomicName, uuid_info->binary_uuid, 16);
1075 if (uuid_info->uuid_form == UUID_AP_SHA1_NAMESPACE) {
1076 thisAtom->uuid_ap_atomname = (char *)calloc(1, sizeof(char) * 16);
1077 memcpy(thisAtom->uuid_ap_atomname, uuid_info->uuid_AP_atom_name, 4);
1078 }
1079 }
1080 } else {
1081 memcpy(thisAtom->AtomicName, Astring, 4);
1082 }
1083
1084 thisAtom->AtomicVerFlags = Averflags;
1085 thisAtom->AtomicLanguage = Alang;
1086
1087 thisAtom->ancillary_data = 0;
1088
1089 // set the next atom number of the PREVIOUS atom (we didn't know there would
1090 // be one until now); this is our default normal mode
1091 if (atom_number > 0) {
1092 parsedAtoms[atom_number - 1].NextAtomNumber = atom_number;
1093 }
1094 thisAtom->NextAtomNumber = 0; // this could be the end... (we just can't quite
1095 // say until we find another atom)
1096
1097 if (strncmp(Astring, "mdat", 4) == 0) {
1098 passed_mdat = true;
1099 }
1100
1101 if (!passed_mdat && Alevel == 1) {
1102 bytes_before_mdat +=
1103 Alength; // this value gets used during FreeFree (for
1104 // removed_bytes_tally) & chunk offset calculations
1105 }
1106 thisAtom->ID32_TagInfo = NULL;
1107
1108 atom_number++; // increment to the next AtomicInfo array
1109
1110 return;
9051111 }
9061112
9071113 uint8_t APar_GetCurrentAtomDepth(uint64_t atom_start, uint64_t atom_length) {
908 uint8_t level = 1;
909 for (int i = 0; i < atom_number; i++) {
910 AtomicInfo* thisAtom = &parsedAtoms[i];
911 if (atom_start == (thisAtom->AtomicStart + thisAtom->AtomicLength) ) {
912 return thisAtom->AtomicLevel;
913 } else {
914 if ( (atom_start < thisAtom->AtomicStart + thisAtom->AtomicLength) && (atom_start > thisAtom->AtomicStart) ) {
915 level++;
916 }
917 }
918 }
919 return level;
920 }
921
922 void APar_IdentifyBrand(char* file_brand ) {
923 brand = UInt32FromBigEndian(file_brand);
924 switch (brand) {
925 //what ISN'T supported
926 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
927 fprintf(stdout, "AtomicParsley error: Quicktime movie files are not supported.\n");
928 exit(2);
929 break;
930
931 //
932 //3GPP2 specification documents brands
933 //
934
935 case 0x33673262 : //'3g2b' 3GPP2 release A
936 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
937 break;
938
939 case 0x33673261 : //'3g2a' //3GPP2 release 0
940 metadata_style = THIRD_GEN_PARTNER_VER2;
941 break;
942
943 //
944 //3GPP specification documents brands, not all are listed at mp4ra
945 //
946
947 case 0x33677037 : //'3gp7' //Release 7 introduces ID32; though it doesn't list a iso bmffv2 compatible brand. Technically, ID32
948 //could be used on older 3gp brands, but iso2 would have to be added to the compatible brand list.
949 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.
950 case 0x33677237 : //'3gr7'
951 case 0x33676537 : //'3ge7'
952 case 0x33676737 : //'3gg7'
953 metadata_style = THIRD_GEN_PARTNER_VER1_REL7;
954 break;
955
956 case 0x33677036 : //'3gp6' //3gp assets which were introducted by NTT DoCoMo to the Rel6 workgroup on January 16, 2003
957 //with S4-030005.zip from http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/ (! albm, loci)
958 case 0x33677236 : //'3gr6' progressive
959 case 0x33677336 : //'3gs6' streaming
960 case 0x33676536 : //'3ge6' extended presentations (jpeg images)
961 case 0x33676736 : //'3gg6' general (not yet suitable; superset)
962 metadata_style = THIRD_GEN_PARTNER_VER1_REL6;
963 break;
964
965 case 0x33677034 : //'3gp4' //3gp assets (the full complement) are available: source clause is S5.5 of TS26.244 (Rel6.4 & later):
966 case 0x33677035 : //'3gp5' //"that the file conforms to the specification; it includes everything required by,
967 metadata_style = THIRD_GEN_PARTNER; //and nothing contrary to the specification (though there may be other material)"
968 break; //it stands to reason that 3gp assets aren't contrary since 'udta' is defined by iso bmffv1
969
970 //
971 //other brands that are have compatible brands relating to 3GPP/3GPP2
972 //
973
974 case 0x6B646469 : //'kddi' //3GPP2 EZmovie (optionally restricted) media; these have a 3GPP2 compatible brand
975 metadata_style = THIRD_GEN_PARTNER_VER2;
976 break;
977 case 0x6D6D7034 : //'mmp4'
978 metadata_style = THIRD_GEN_PARTNER;
979 break;
980
981 //
982 //what IS supported for iTunes-style metadata
983 //
984
985 case 0x4D534E56 : //'MSNV' (PSP) - this isn't actually listed at mp4ra, but since they are popular...
986 metadata_style = ITUNES_STYLE;
987 psp_brand = true;
988 break;
989 case 0x4D344120 : //'M4A ' -- these are all listed at http://www.mp4ra.org/filetype.html as registered brands
990 case 0x4D344220 : //'M4B '
991 case 0x4D345020 : //'M4P '
992 case 0x4D345620 : //'M4V '
993 case 0x4D345648 : //'MV4H'
994 case 0x4D345650 : //'M4VP'
995 case 0x6D703432 : //'mp42'
996 case 0x6D703431 : //'mp41'
997 case 0x69736F6D : //'isom'
998 case 0x69736F32 : //'iso2'
999 case 0x61766331 : //'avc1'
1000
1001
1002 metadata_style = ITUNES_STYLE;
1003 break;
1004
1005 //
1006 //other brands that are derivatives of the ISO Base Media File Format
1007 //
1008 case 0x6D6A7032 : //'mjp2'
1009 case 0x6D6A3273 : //'mj2s'
1010 metadata_style = MOTIONJPEG2000;
1011 break;
1012
1013 //other lesser unsupported brands; http://www.mp4ra.org/filetype.html like dv, mp21 & ... whatever mpeg7 brand is
1014 default :
1015 fprintf(stdout, "AtomicParsley error: unsupported MPEG-4 file brand found '%s'\n", file_brand);
1016 exit(2);
1017 break;
1018 }
1019 return;
1020 }
1021
1022 void APar_TestCompatibleBrand(FILE* file, uint64_t atom_start, uint64_t atom_length) {
1023 if (atom_length <= 16) return;
1024 uint32_t compatible_brand = 0;
1025
1026 for (uint32_t brand = 16; brand < atom_length; brand+=4) {
1027 compatible_brand = APar_read32(twenty_byte_buffer, file, atom_start+brand);
1028 if (compatible_brand == 0x6D703432 || compatible_brand == 0x69736F32) {
1029 parsedAtoms[atom_number-1].ancillary_data = compatible_brand;
1030 }
1031 }
1032 return;
1033 }
1034
1035 void APar_Extract_stsd_codec(FILE* file, uint64_t midJump) {
1036 memset(twenty_byte_buffer, 0, 12);
1037 APar_readX(twenty_byte_buffer, file, midJump, 12);
1038 parsedAtoms[atom_number-1].ancillary_data = UInt32FromBigEndian( twenty_byte_buffer +4 );
1114 uint8_t level = 1;
1115 for (int i = 0; i < atom_number; i++) {
1116 AtomicInfo *thisAtom = &parsedAtoms[i];
1117 if (atom_start == (thisAtom->AtomicStart + thisAtom->AtomicLength)) {
1118 return thisAtom->AtomicLevel;
1119 } else {
1120 if ((atom_start < thisAtom->AtomicStart + thisAtom->AtomicLength) &&
1121 (atom_start > thisAtom->AtomicStart)) {
1122 level++;
1123 }
1124 }
1125 }
1126 return level;
1127 }
1128
1129 void APar_IdentifyBrand(char *file_brand) {
1130 brand = UInt32FromBigEndian(file_brand);
1131 switch (brand) {
1132 // what ISN'T supported
1133 case 0x71742020: //'qt ' --this is listed at mp4ra, but there are features
1134 // of the file that aren't supported (like the 4 NULL bytes
1135 // after the last udta child atom
1136 fprintf(stdout,
1137 "AtomicParsley error: Quicktime movie files are not supported.\n");
1138 exit(2);
1139 break;
1140
1141 //
1142 // 3GPP2 specification documents brands
1143 //
1144
1145 case 0x33673262: //'3g2b' 3GPP2 release A
1146 metadata_style =
1147 THIRD_GEN_PARTNER_VER2_REL_A; // 3GPP2 C.S0050-A_v1.0_060403, Annex A.2
1148 // lists differences between 3GPP & 3GPP2
1149 // - assets are not listed
1150 break;
1151
1152 case 0x33673261: //'3g2a' //3GPP2 release 0
1153 metadata_style = THIRD_GEN_PARTNER_VER2;
1154 break;
1155
1156 //
1157 // 3GPP specification documents brands, not all are listed at mp4ra
1158 //
1159
1160 case 0x33677037: //'3gp7' //Release 7 introduces
1161 // ID32; though it doesn't list a iso bmffv2 compatible
1162 // brand. Technically, ID32 could be used on older 3gp
1163 // brands, but iso2 would have to be added to the compatible
1164 // brand list.
1165 case 0x33677337: //'3gs7' //I don't feel the need to
1166 // do that, since other things might have to be done. And I'm
1167 // not looking into it.
1168 case 0x33677237: //'3gr7'
1169 case 0x33676537: //'3ge7'
1170 case 0x33676737: //'3gg7'
1171 metadata_style = THIRD_GEN_PARTNER_VER1_REL7;
1172 break;
1173
1174 case 0x33677036: //'3gp6' //3gp assets which were
1175 // introducted by NTT DoCoMo to the Rel6 workgroup on January
1176 // 16, 2003 with S4-030005.zip from
1177 // http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/ (!
1178 // albm, loci)
1179 case 0x33677236: //'3gr6' progressive
1180 case 0x33677336: //'3gs6' streaming
1181 case 0x33676536: //'3ge6' extended presentations (jpeg images)
1182 case 0x33676736: //'3gg6' general (not yet suitable; superset)
1183 metadata_style = THIRD_GEN_PARTNER_VER1_REL6;
1184 break;
1185
1186 case 0x33677034: //'3gp4' //3gp assets (the full
1187 // complement) are available: source clause is S5.5 of
1188 // TS26.244 (Rel6.4 & later):
1189 case 0x33677035: //'3gp5' //"that the file conforms
1190 // to the specification; it includes everything required by,
1191 metadata_style =
1192 THIRD_GEN_PARTNER; // and nothing contrary to the specification (though
1193 // there may be other material)"
1194 break; // it stands to reason that 3gp assets aren't contrary since 'udta'
1195 // is defined by iso bmffv1
1196
1197 //
1198 // other brands that are have compatible brands relating to 3GPP/3GPP2
1199 //
1200
1201 case 0x6B646469: //'kddi' //3GPP2 EZmovie (optionally
1202 // restricted) media; these have a 3GPP2 compatible brand
1203 metadata_style = THIRD_GEN_PARTNER_VER2;
1204 break;
1205 case 0x6D6D7034: //'mmp4'
1206 metadata_style = THIRD_GEN_PARTNER;
1207 break;
1208
1209 //
1210 // what IS supported for iTunes-style metadata
1211 //
1212
1213 case 0x4D534E56: //'MSNV' (PSP) - this isn't actually listed at mp4ra, but
1214 // since they are popular...
1215 metadata_style = ITUNES_STYLE;
1216 psp_brand = true;
1217 break;
1218 case 0x4D344120: //'M4A ' -- these are all listed at
1219 // https://mp4ra.org/#/brands as registered brands
1220 case 0x4D344220: //'M4B '
1221 case 0x4D345020: //'M4P '
1222 case 0x4D345620: //'M4V '
1223 case 0x4D345648: //'MV4H'
1224 case 0x4D345650: //'M4VP'
1225 case 0x64617368: //'dash'
1226 case 0x66347620: //'f4v'
1227 case 0x6D703432: //'mp42'
1228 case 0x6D703431: //'mp41'
1229 case 0x69736F6D: //'isom'
1230 case 0x69736F32: //'iso2'
1231 case 0x61766331: //'avc1'
1232
1233 metadata_style = ITUNES_STYLE;
1234 break;
1235
1236 //
1237 // other brands that are derivatives of the ISO Base Media File Format
1238 //
1239 case 0x6D6A7032: //'mjp2'
1240 case 0x6D6A3273: //'mj2s'
1241 metadata_style = MOTIONJPEG2000;
1242 break;
1243
1244 // other lesser unsupported brands; http://www.mp4ra.org/filetype.html like
1245 // dv, mp21 & ... whatever mpeg7 brand is
1246 default:
1247 fprintf(stdout,
1248 "AtomicParsley error: unsupported MPEG-4 file brand found '%s'\n",
1249 file_brand);
1250 exit(2);
1251 break;
1252 }
1253 return;
1254 }
1255
1256 void APar_TestCompatibleBrand(FILE *file,
1257 uint64_t atom_start,
1258 uint64_t atom_length) {
1259 if (atom_length <= 16)
1260 return;
1261 uint32_t compatible_brand = 0;
1262
1263 for (uint32_t brand = 16; brand < atom_length; brand += 4) {
1264 compatible_brand =
1265 APar_read32(twenty_byte_buffer, file, atom_start + brand);
1266 if (compatible_brand == 0x6D703432 || compatible_brand == 0x69736F32) {
1267 parsedAtoms[atom_number - 1].ancillary_data = compatible_brand;
1268 }
1269 }
1270 return;
1271 }
1272
1273 void APar_Extract_stsd_codec(FILE *file, uint64_t midJump) {
1274 memset(twenty_byte_buffer, 0, 12);
1275 APar_readX(twenty_byte_buffer, file, midJump, 12);
1276 parsedAtoms[atom_number - 1].ancillary_data =
1277 UInt32FromBigEndian(twenty_byte_buffer + 4);
10391278 return;
10401279 }
10411280
10431282 APar_LocateDataReference
10441283 fill
10451284 ----------------------*/
1046 void APar_LocateDataReference(short chunk_offset_idx, FILE* file) {
1047 uint32_t data_ref_idx = 0;
1048 short sampletable_atom_idx = 0;
1049 short minf_atom_idx = 0;
1050 AtomicInfo *stsd_atom, *dinf_atom, *dref_atom, *target_reference_atom = NULL;
1051
1052 sampletable_atom_idx = APar_FindParentAtom(chunk_offset_idx, parsedAtoms[chunk_offset_idx].AtomicLevel);
1053 stsd_atom = APar_FindChildAtom(sampletable_atom_idx, "stsd");
1054 if (stsd_atom == NULL) {
1055 return;
1056 }
1057 data_ref_idx = APar_read32(twenty_byte_buffer, file, stsd_atom->AtomicStart+28);
1058
1059 minf_atom_idx = APar_FindParentAtom(sampletable_atom_idx, parsedAtoms[sampletable_atom_idx].AtomicLevel);
1060 dinf_atom = APar_FindChildAtom(minf_atom_idx, "dinf");
1061 dref_atom = APar_FindChildAtom(dinf_atom->AtomicNumber, "dref");
1062
1063 target_reference_atom = APar_FindChildAtom(dref_atom->AtomicNumber, "any", 4, data_ref_idx);
1064
1065 if (target_reference_atom != NULL) {
1066 parsedAtoms[chunk_offset_idx].ancillary_data = target_reference_atom->AtomicVerFlags;
1067 }
1068 return;
1285 void APar_LocateDataReference(short chunk_offset_idx, FILE *file) {
1286 uint32_t data_ref_idx = 0;
1287 short sampletable_atom_idx = 0;
1288 short minf_atom_idx = 0;
1289 AtomicInfo *stsd_atom, *dinf_atom, *dref_atom, *target_reference_atom = NULL;
1290
1291 sampletable_atom_idx = APar_FindParentAtom(
1292 chunk_offset_idx, parsedAtoms[chunk_offset_idx].AtomicLevel);
1293 stsd_atom = APar_FindChildAtom(sampletable_atom_idx, "stsd");
1294 if (stsd_atom == NULL) {
1295 return;
1296 }
1297 data_ref_idx =
1298 APar_read32(twenty_byte_buffer, file, stsd_atom->AtomicStart + 28);
1299
1300 minf_atom_idx = APar_FindParentAtom(
1301 sampletable_atom_idx, parsedAtoms[sampletable_atom_idx].AtomicLevel);
1302 dinf_atom = APar_FindChildAtom(minf_atom_idx, "dinf");
1303 dref_atom = APar_FindChildAtom(dinf_atom->AtomicNumber, "dref");
1304
1305 target_reference_atom =
1306 APar_FindChildAtom(dref_atom->AtomicNumber, "any", 4, data_ref_idx);
1307
1308 if (target_reference_atom != NULL) {
1309 parsedAtoms[chunk_offset_idx].ancillary_data =
1310 target_reference_atom->AtomicVerFlags;
1311 }
1312 return;
10691313 }
10701314
10711315 void APar_SampleTableIterator(FILE *file) {
1072 uint8_t total_tracks = 0;
1073 uint8_t a_track = 0;
1074 char track_path[36];
1075 memset(track_path, 0, 36);
1076 AtomicInfo* samples_parent = NULL;
1077 AtomicInfo* chunk_offset_atom = NULL;
1078
1079 APar_FindAtomInTrack(total_tracks, a_track, NULL); //gets the number of tracks
1080 for (uint8_t trk_idx=1; trk_idx <= total_tracks; trk_idx++) {
1081 sprintf(track_path, "moov.trak[%u].mdia.minf.stbl", trk_idx);
1082 samples_parent = APar_FindAtom(track_path, false, SIMPLE_ATOM, 0, false);
1083 if (samples_parent != NULL) {
1084 chunk_offset_atom = APar_FindChildAtom(samples_parent->AtomicNumber, "stco");
1085 if (chunk_offset_atom == NULL) chunk_offset_atom = APar_FindChildAtom(samples_parent->AtomicNumber, "co64");
1086 if (chunk_offset_atom != NULL) {
1087 APar_LocateDataReference(chunk_offset_atom->AtomicNumber, file);
1088 }
1089 }
1090 }
1091 return;
1316 uint8_t total_tracks = 0;
1317 uint8_t a_track = 0;
1318 char track_path[36];
1319 memset(track_path, 0, 36);
1320 AtomicInfo *samples_parent = NULL;
1321 AtomicInfo *chunk_offset_atom = NULL;
1322
1323 APar_FindAtomInTrack(total_tracks, a_track, NULL); // gets the number of
1324 // tracks
1325 for (uint8_t trk_idx = 1; trk_idx <= total_tracks; trk_idx++) {
1326 sprintf(track_path, "moov.trak[%u].mdia.minf.stbl", trk_idx);
1327 samples_parent = APar_FindAtom(track_path, false, SIMPLE_ATOM, 0, false);
1328 if (samples_parent != NULL) {
1329 chunk_offset_atom =
1330 APar_FindChildAtom(samples_parent->AtomicNumber, "stco");
1331 if (chunk_offset_atom == NULL)
1332 chunk_offset_atom =
1333 APar_FindChildAtom(samples_parent->AtomicNumber, "co64");
1334 if (chunk_offset_atom != NULL) {
1335 APar_LocateDataReference(chunk_offset_atom->AtomicNumber, file);
1336 }
1337 }
1338 }
1339 return;
10921340 }
10931341
10941342 /*----------------------
10951343 APar_MatchToKnownAtom
10961344 atom_name - the name of our newly found atom
10971345 atom_container - the name of the parent container atom
1098 fromFile - controls the manner of extracting parents (passed thu to another function)
1099
1100 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.
1101 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
1102 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
1103 Iterate through the known parents, and test it against atom_container. If they match, return the properties of the known atom
1346 fromFile - controls the manner of extracting parents (passed thu to another
1347 function)
1348
1349 Using the atom_name of this new atom, search through KnownAtoms, testing
1350 that the names match. If they do, move onto a finer grained sieve. If the parent
1351 can be at any level (like "free"), just let it through; if the parent is "ilst"
1352 (iTunes-style metadata), or a uuid, return a generic match The final test is the
1353 one most atoms will go through. Some atoms can have different parents - up to 5
1354 different parents are allowed by this version of AP Iterate through the known
1355 parents, and test it against atom_container. If they match, return the
1356 properties of the known atom
11041357 ----------------------*/
1105 int APar_MatchToKnownAtom(const char* atom_name, const char* atom_container, bool fromFile, const char* find_atom_path) {
1106 uint32_t total_known_atoms = (sizeof(KnownAtoms)/sizeof(*KnownAtoms));
1107 uint32_t return_known_atom = 0;
1108
1109 //if this atom is contained by 'ilst', then it is *highly* likely an iTunes-style metadata parent atom
1110 if ( memcmp(atom_container, "ilst", 4) == 0 && memcmp(atom_name, "uuid", 4) != 0) {
1111 return_known_atom = total_known_atoms-2; //2nd to last KnowAtoms is a generic placeholder iTunes-parent atom
1112 //fprintf(stdout, "found iTunes parent %s = atom %s\n", KnownAtoms[return_known_atom].known_atom_name, atom_name);
1113
1114 //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
1115 } else if (memcmp(atom_name, "data", 4) == 0 && find_atom_path != NULL) {
1116 if (strncmp(find_atom_path, "moov.udta.meta.ilst.", 20) == 0) {
1117 return_known_atom = total_known_atoms-1; //last KnowAtoms is a generic placeholder iTunes-data atom
1118 //fprintf(stdout, "found iTunes data child\n");
1119 }
1120
1121 } else if (memcmp(atom_name, "data", 4) == 0) {
1122 char* fullpath = (char *)malloc(sizeof(char) * 200);
1123 memset(fullpath, 0, sizeof(char) * 200);
1124
1125 if (fromFile) {
1126 APar_ProvideAtomPath(parsedAtoms[atom_number-1].AtomicNumber, fullpath, fromFile);
1127 } else { //find_atom_path only is NULL in APar_ScanAtoms (where fromFile is true) and in APar_CreateSparseAtom, where atom_number was just filled
1128 APar_ProvideAtomPath(parsedAtoms[atom_number].AtomicNumber, fullpath, fromFile);
1129 }
1130
1131 //fprintf(stdout, "APar_ProvideAtomPath gives %s (%s-%s)\n", fullpath, atom_name, atom_container);
1132 if (strncmp(fullpath, "moov.udta.meta.ilst.", 20) == 0) {
1133 return_known_atom = total_known_atoms-1; //last KnowAtoms is a generic placeholder iTunes-data atom
1134 //fprintf(stdout, "found iTunes data child\n");
1135 }
1136 free(fullpath);
1137 fullpath = NULL;
1138
1139 //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'...)
1140 } else if (memcmp(atom_name, "esds", 4) == 0 ) {
1141 char* fullpath = (char *)malloc(sizeof(char) * 300);
1142 memset(fullpath, 0, sizeof(char) * 200);
1143
1144 APar_ProvideAtomPath(parsedAtoms[atom_number-1].AtomicNumber, fullpath, fromFile);
1145
1146 if (strncmp(fullpath, "moov.trak.mdia.minf.stbl.stsd.", 30) == 0) {
1147 return_known_atom = total_known_atoms-3; //manually return the esds atom
1148 }
1149 free(fullpath);
1150 fullpath = NULL;
1151
1152
1153 } else {
1154 //try matching the name of the atom
1155 for(uint32_t i = 1; i < total_known_atoms; i++) {
1156 if (memcmp(atom_name, KnownAtoms[i].known_atom_name, 4) == 0) {
1157 //name matches, now see if the container atom matches any known container for that atom
1158 if ( strncmp(KnownAtoms[i].known_parent_atoms[0], "_ANY_LEVEL", 10) == 0 ) {
1159 return_known_atom = i; //the list starts at 0; the unknown atom is at 0; first known atom (ftyp) is at 1
1160 break;
1161
1162 } else {
1163 uint8_t total_known_containers = (uint8_t)(sizeof(KnownAtoms[i].known_parent_atoms)/sizeof(*KnownAtoms[i].known_parent_atoms)); //always 5
1164 for (uint8_t iii = 0; iii < total_known_containers; iii++) {
1165 if (KnownAtoms[i].known_parent_atoms[iii] != NULL) {
1166 if ( strncmp(atom_container, KnownAtoms[i].known_parent_atoms[iii], strlen(atom_container) ) == 0) { //strlen(atom_container)
1167 return_known_atom = i; //the list starts at 0; the unknown atom is at 0; first known atom (ftyp) is at 1
1168 break;
1169 }
1170 }
1171 }
1172 }
1173 if (return_known_atom) {
1174 break;
1175 }
1176 }
1177 }
1178 }
1179 if ( return_known_atom > total_known_atoms ) {
1180 return_known_atom = 0;
1181 }
1182 //accommodate any future child to dref; force to being versioned
1183 if (return_known_atom == 0 && memcmp(atom_container, "dref", 4) == 0) {
1184 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
1185 }
1186 return return_known_atom;
1358 int APar_MatchToKnownAtom(const char *atom_name,
1359 const char *atom_container,
1360 bool fromFile,
1361 const char *find_atom_path) {
1362 uint32_t total_known_atoms = (sizeof(KnownAtoms) / sizeof(*KnownAtoms));
1363 uint32_t return_known_atom = 0;
1364
1365 // if this atom is contained by 'ilst', then it is *highly* likely an
1366 // iTunes-style metadata parent atom
1367 if (memcmp(atom_container, "ilst", 4) == 0 &&
1368 memcmp(atom_name, "uuid", 4) != 0) {
1369 return_known_atom =
1370 total_known_atoms -
1371 2; // 2nd to last KnowAtoms is a generic placeholder iTunes-parent atom
1372 // fprintf(stdout, "found iTunes parent %s = atom %s\n",
1373 // KnownAtoms[return_known_atom].known_atom_name, atom_name);
1374
1375 // if this atom is "data" get the full path to it; we will take any atom
1376 // under 'ilst' and consider it an iTunes metadata parent atom
1377 } else if (memcmp(atom_name, "data", 4) == 0 && find_atom_path != NULL) {
1378 if (strncmp(find_atom_path, "moov.udta.meta.ilst.", 20) == 0) {
1379 return_known_atom =
1380 total_known_atoms -
1381 1; // last KnowAtoms is a generic placeholder iTunes-data atom
1382 // fprintf(stdout, "found iTunes data child\n");
1383 }
1384
1385 } else if (memcmp(atom_name, "data", 4) == 0) {
1386 char *fullpath = (char *)malloc(sizeof(char) * 200);
1387 memset(fullpath, 0, sizeof(char) * 200);
1388
1389 if (fromFile) {
1390 APar_ProvideAtomPath(
1391 parsedAtoms[atom_number - 1].AtomicNumber, fullpath, fromFile);
1392 } else { // find_atom_path only is NULL in APar_ScanAtoms (where fromFile is
1393 // true) and in APar_CreateSparseAtom, where atom_number was just
1394 // filled
1395 APar_ProvideAtomPath(
1396 parsedAtoms[atom_number].AtomicNumber, fullpath, fromFile);
1397 }
1398
1399 // fprintf(stdout, "APar_ProvideAtomPath gives %s (%s-%s)\n", fullpath,
1400 // atom_name, atom_container);
1401 if (strncmp(fullpath, "moov.udta.meta.ilst.", 20) == 0) {
1402 return_known_atom =
1403 total_known_atoms -
1404 1; // last KnowAtoms is a generic placeholder iTunes-data atom
1405 // fprintf(stdout, "found iTunes data child\n");
1406 }
1407 free(fullpath);
1408 fullpath = NULL;
1409
1410 // if this atom is "esds" get the full path to it; take any atom under
1411 // 'stsd' as a parent to esds (that parent would be a 4CC codec; not all do
1412 // have 'esds'...)
1413 } else if (memcmp(atom_name, "esds", 4) == 0) {
1414 char *fullpath = (char *)malloc(sizeof(char) * 300);
1415 memset(fullpath, 0, sizeof(char) * 200);
1416
1417 APar_ProvideAtomPath(
1418 parsedAtoms[atom_number - 1].AtomicNumber, fullpath, fromFile);
1419
1420 if (strncmp(fullpath, "moov.trak.mdia.minf.stbl.stsd.", 30) == 0) {
1421 return_known_atom = total_known_atoms - 3; // manually return the esds
1422 // atom
1423 }
1424 free(fullpath);
1425 fullpath = NULL;
1426
1427 } else {
1428 // try matching the name of the atom
1429 for (uint32_t i = 1; i < total_known_atoms; i++) {
1430 if (memcmp(atom_name, KnownAtoms[i].known_atom_name, 4) == 0) {
1431 // name matches, now see if the container atom matches any known
1432 // container for that atom
1433 if (strncmp(KnownAtoms[i].known_parent_atoms[0], "_ANY_LEVEL", 10) ==
1434 0) {
1435 return_known_atom = i; // the list starts at 0; the unknown atom is at
1436 // 0; first known atom (ftyp) is at 1
1437 break;
1438
1439 } else {
1440 uint8_t total_known_containers =
1441 (uint8_t)(sizeof(KnownAtoms[i].known_parent_atoms) /
1442 sizeof(*KnownAtoms[i].known_parent_atoms)); // always 5
1443 for (uint8_t iii = 0; iii < total_known_containers; iii++) {
1444 if (KnownAtoms[i].known_parent_atoms[iii] != NULL) {
1445 if (strncmp(atom_container,
1446 KnownAtoms[i].known_parent_atoms[iii],
1447 strlen(atom_container)) ==
1448 0) { // strlen(atom_container)
1449 return_known_atom =
1450 i; // the list starts at 0; the unknown atom is at 0; first
1451 // known atom (ftyp) is at 1
1452 break;
1453 }
1454 }
1455 }
1456 }
1457 if (return_known_atom) {
1458 break;
1459 }
1460 }
1461 }
1462 }
1463 if (return_known_atom > total_known_atoms) {
1464 return_known_atom = 0;
1465 }
1466 // accommodate any future child to dref; force to being versioned
1467 if (return_known_atom == 0 && memcmp(atom_container, "dref", 4) == 0) {
1468 return_known_atom =
1469 total_known_atoms -
1470 4; // return a generic *VERSIONED* child atom; otherwise an atom without
1471 // flags will be present & chunk offsets will not update
1472 }
1473 return return_known_atom;
11871474 }
11881475
11891476 /*----------------------
11921479 atom_length - length of the eval atom
11931480 container - a string of the last known container atom (or FILE_LEVEL)
11941481
1195 given the next atom (unknown string at this point), run some tests using its starting point and length. Iterate backwards through the already parsed
1196 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),
1197 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
1198 (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
1199 defined in KnownAtoms
1482 given the next atom (unknown string at this point), run some tests using its
1483 starting point and length. Iterate backwards through the already parsed atoms,
1484 and for each test if it could contain this atom. Tests include if the container
1485 starts before ours (which it would to contain the new atom), that its length is
1486 longer than our length (any parent would need to be longer than us if even by 8
1487 bytes), the start + length sum of the parent atom (where it ends), needs to be
1488 greater than or equal to where this new atom ends, and finally, that the eval
1489 containing atom be some form of parent as defined in KnownAtoms
12001490 ----------------------*/
1201 void APar_Manually_Determine_Parent(uint64_t atom_start, uint64_t atom_length, char* container) {
1202 short preceding_atom = atom_number-1;
1203 while (parsedAtoms[preceding_atom].AtomicNumber != 0) {
1204
1205 if (parsedAtoms[preceding_atom].AtomicStart < atom_start &&
1206 parsedAtoms[preceding_atom].AtomicLength > atom_length &&
1207 parsedAtoms[preceding_atom].AtomicStart + parsedAtoms[preceding_atom].AtomicLength >= atom_start + atom_length &&
1208 parsedAtoms[preceding_atom].AtomicContainerState <= DUAL_STATE_ATOM ) {
1209 memcpy(container, parsedAtoms[preceding_atom].AtomicName, 5);
1210 break;
1211
1212 } else {
1213 preceding_atom--;
1214 }
1215 if (preceding_atom == 0) {
1216 strcpy(container, "FILE_LEVEL");
1217 }
1218 }
1491 void APar_Manually_Determine_Parent(uint64_t atom_start,
1492 uint64_t atom_length,
1493 char *container) {
1494 short preceding_atom = atom_number - 1;
1495 while (parsedAtoms[preceding_atom].AtomicNumber != 0) {
1496
1497 if (parsedAtoms[preceding_atom].AtomicStart < atom_start &&
1498 parsedAtoms[preceding_atom].AtomicLength > atom_length &&
1499 parsedAtoms[preceding_atom].AtomicStart +
1500 parsedAtoms[preceding_atom].AtomicLength >=
1501 atom_start + atom_length &&
1502 parsedAtoms[preceding_atom].AtomicContainerState <= DUAL_STATE_ATOM) {
1503 memcpy(container, parsedAtoms[preceding_atom].AtomicName, 5);
1504 break;
1505
1506 } else {
1507 preceding_atom--;
1508 }
1509 if (preceding_atom == 0) {
1510 strcpy(container, "FILE_LEVEL");
1511 }
1512 }
12191513 }
12201514
12211515 /*----------------------
12511545
12521546 ----------------------*/
12531547 void APar_ScanAtoms(const char *path, bool deepscan_REQ) {
1254 if (!parsedfile) {
1255 file_size = findFileSize(path);
1256
1257 FILE *file = APar_OpenFile(path, "rb");
1258 if (file != NULL) {
1259 char *data = (char *)calloc(1, 13);
1260 char *container = (char *)calloc(1, 20);
1261 memcpy(container, "FILE_LEVEL", 10);
1262 bool corrupted_data_atom = false;
1263 bool jpeg2000signature = false;
1264
1265 uuid_vitals uuid_info = {0};
1266 uuid_info.binary_uuid=(char*)malloc(sizeof(char)*16 + 1); //this will hold any potential 16byte uuids
1267 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
1268
1269 if (data == NULL) return;
1270 uint64_t dataSize = 0;
1271 uint64_t jump = 0;
1272
1273 APar_readX(data, file, 0, 12);
1274 char *atom = data+4;
1275
1276 if ( memcmp(data, "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A ", 12) == 0 ) {
1277 jpeg2000signature = true;
1278 }
1279
1280 if ( memcmp(atom, "ftyp", 4) == 0 || jpeg2000signature) {
1281
1282 dataSize = UInt32FromBigEndian(data);
1283 jump = dataSize;
1284
1285 APar_AtomizeFileInfo(0, jump, 0, atom, generalAtomicLevel, CHILD_ATOM, SIMPLE_ATOM, 0, 0 , &uuid_info);
1286
1287 if (!jpeg2000signature) {
1288 APar_IdentifyBrand( data + 8 );
1289 APar_TestCompatibleBrand(file, 0, dataSize);
1290 }
1291
1292 fseek(file, jump, SEEK_SET);
1293
1294 while (jump < file_size) {
1295
1296 uuid_info.uuid_form = UUID_DEPRECATED_FORM; //start with the assumption that any found atom is in the depracted uuid form
1297
1298 APar_readX_noseek(data, file, 8);
1299 char *atom = data+4;
1300 dataSize = UInt32FromBigEndian(data);
1301
1302 if (jpeg2000signature) {
1303 if (memcmp(atom, "ftyp", 4) == 0) {
1304 APar_readX_noseek(twenty_byte_buffer, file, 4);
1305 APar_IdentifyBrand(twenty_byte_buffer);
1306 } else {
1307 exit(0); //the atom right after the jpeg2000/mjpeg2000 signature is *supposed* to be 'ftyp'
1308 }
1309 jpeg2000signature = false;
1310 }
1311
1312 if (dataSize > file_size - jump) {
1313 dataSize = file_size - jump;
1314 }
1315
1316 if (dataSize == 0 && (atom[0] == 0 && atom[1] == 0 && atom[2] == 0 && atom[3] == 0) ) {
1317 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
1318 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?
1319 } //after actual tested its elimination, it doesn't seem to be required for gapless playback
1320
1321 //diagnose damage to 'cprt' by libmp4v2 in 1.4.1 & 1.5.0.1
1322 //typically, the length of this atom (dataSize) will exceeed it parent (which is reported as 17)
1323 //true length ot this data will be 9 - impossible for iTunes-style 'data' atom.
1324 if (memcmp(atom, "data", 4) == 0 && parsedAtoms[ atom_number-1].AtomicContainerState == PARENT_ATOM) {
1325 if (dataSize > parsedAtoms[ atom_number-1].AtomicLength) {
1326 dataSize = parsedAtoms[ atom_number-1].AtomicLength -8; //force its length to its true length
1327 fprintf(stdout, "AtomicParsley warning: the 'data' child of the '%s' atom seems to be corrupted.\n", parsedAtoms[ atom_number-1].AtomicName);
1328 corrupted_data_atom = true;
1329 }
1330 }
1331 //end diagnosis; APar_Manually_Determine_Parent will still determine it to be a versioned atom (it tests by names), but at file write out,
1332 //it will write with a length of 9 bytes
1333
1334 APar_Manually_Determine_Parent(jump, dataSize, container);
1335 int filtered_known_atom = APar_MatchToKnownAtom(atom, container, true, NULL);
1336
1337 uint32_t atom_verflags = 0;
1338 uint16_t atom_language = 0;
1339
1340 if (memcmp(atom, "uuid", 4) == 0) {
1341 memset(uuid_info.binary_uuid, 0, 17);
1342
1343 APar_readX(uuid_info.binary_uuid, file, jump+8, 16);
1344
1345 if (UInt32FromBigEndian(uuid_info.binary_uuid+8) == 0) { //the deperacted uuid form
1346 memcpy(atom, uuid_info.binary_uuid, 4);
1347 atom_verflags = APar_read32(uuid_info.binary_uuid, file, jump+12);
1348 if (atom_verflags > AtomFlags_Data_UInt) {
1349 atom_verflags = 0;
1350 }
1351 } else {
1352 uint8_t uuid_version = APar_extract_uuid_version(NULL, uuid_info.binary_uuid);
1353 APar_endian_uuid_bin_str_conversion(uuid_info.binary_uuid);
1354 if (uuid_version == 5) {
1355 uuid_info.uuid_form = UUID_SHA1_NAMESPACE;
1356 //read in what AP would set the atom name to. The new uuid form is:
1357 // 4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data
1358 APar_readX(uuid_info.uuid_AP_atom_name, file, jump+24, 4);
1359
1360 char uuid_of_foundname_in_AP_namesapce[20];
1361 APar_generate_uuid_from_atomname(uuid_info.uuid_AP_atom_name, uuid_of_foundname_in_AP_namesapce);
1362 if (memcmp(uuid_info.binary_uuid, uuid_of_foundname_in_AP_namesapce, 16) == 0) {
1363 uuid_info.uuid_form = UUID_AP_SHA1_NAMESPACE; //our own uuid ver5 atoms in the AtomicParsley.sf.net namespace
1364 atom_verflags = APar_read32(twenty_byte_buffer, file, jump+28);
1365 }
1366 } else {
1367 uuid_info.uuid_form = UUID_OTHER;
1368 }
1369 }
1370 }
1371
1372 if (KnownAtoms[filtered_known_atom].box_type == VERSIONED_ATOM && !corrupted_data_atom) {
1373 atom_verflags = APar_read32(twenty_byte_buffer, file, jump+8);
1374 }
1375
1376 if (KnownAtoms[filtered_known_atom].box_type == PACKED_LANG_ATOM) {
1377 atom_verflags = APar_read32(twenty_byte_buffer, file, jump+8);
1378
1379 //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
1380 //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
1381 char bitpacked_lang[4];
1382 memset(bitpacked_lang, 0, 4);
1383
1384 uint32_t userdata_box = UInt32FromBigEndian(atom);
1385
1386 switch (userdata_box) {
1387 case 0x7469746C : //'titl'
1388 case 0x64736370 : //'dscp'
1389 case 0x63707274 : //'cprt'
1390 case 0x70657266 : //'perf'
1391 case 0x61757468 : //'auth'
1392 case 0x676E7265 : //'gnre'
1393 case 0x616C626D : //'albm'
1394 case 0x6B797764 : //'kywd'
1395 case 0x6C6F6369 : //'loci'
1396 case 0x49443332 : //'ID32' ; technically not a 'user data box', but this only extracts the packed language (which ID32 does have)
1397 {
1398 atom_language = APar_read16(bitpacked_lang, file, jump + 12);
1399 break;
1400 }
1401 case 0x636C7366 : //'clsf'
1402 {
1403 atom_language = APar_read16(bitpacked_lang, file, jump + 18);
1404 break;
1405 }
1406 case 0x72746E67 : //'rtng'
1407 {
1408 atom_language = APar_read16(bitpacked_lang, file, jump + 20);
1409 break;
1410 }
1411 //case 0x79727263 : //'yrrc' is the only 3gp tag that doesn't support multiple languages; won't even get here because != PACKED_LANG_ATOM
1412 default :
1413 {
1414 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
1415 }
1416 }
1417 }
1418
1419 //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
1420 if ( (strncmp(atom, "mdat", 4) == 0) && (generalAtomicLevel == 1) && (dataSize == 1) ) {
1421
1422 uint64_t extended_dataSize = APar_read64(
1423 twenty_byte_buffer, file, jump+8);
1424 APar_AtomizeFileInfo(jump, 1, extended_dataSize, atom, generalAtomicLevel, KnownAtoms[filtered_known_atom].container_state,
1425 KnownAtoms[filtered_known_atom].box_type, atom_verflags, atom_language, &uuid_info );
1426
1427 } else {
1428 APar_AtomizeFileInfo(jump, dataSize, 0, atom, generalAtomicLevel, KnownAtoms[filtered_known_atom].container_state,
1429 corrupted_data_atom ? SIMPLE_ATOM : KnownAtoms[filtered_known_atom].box_type, atom_verflags, atom_language, &uuid_info );
1430 }
1431 corrupted_data_atom = false;
1432
1433 //read in the name of an iTunes-style internal reverseDNS directly into parsedAtoms
1434 if (memcmp(atom, "mean", 4) == 0 && memcmp(parsedAtoms[atom_number-2].AtomicName, "----", 4) == 0) {
1435 parsedAtoms[atom_number-1].ReverseDNSdomain = (char *)calloc(1, sizeof(char) * dataSize);
1436
1437 //jump + 12 because 'name' atom is the 2nd child
1438 APar_readX(parsedAtoms[atom_number-1].ReverseDNSdomain, file, jump + 12, dataSize - 12);
1439 }
1440 if (memcmp(atom, "name", 4) == 0 &&
1441 memcmp(parsedAtoms[atom_number-2].AtomicName, "mean", 4) == 0 &&
1442 memcmp(parsedAtoms[atom_number-3].AtomicName, "----", 4) == 0) {
1443
1444 parsedAtoms[atom_number-1].ReverseDNSname = (char *)calloc(1, sizeof(char) * dataSize);
1445
1446 //jump + 12 because 'name' atom is the 2nd child
1447 APar_readX(parsedAtoms[atom_number-1].ReverseDNSname, file, jump + 12, dataSize - 12);
1448 }
1449
1450 if (dataSize == 0) { // length = 0 means it reaches to EOF
1451 break;
1452 }
1453
1454 switch (KnownAtoms[filtered_known_atom].container_state) {
1455 case PARENT_ATOM : {
1456 jump += 8;
1457 break;
1458 }
1459 case CHILD_ATOM : {
1460 if (memcmp(atom, "hdlr", 4) == 0) {
1461 APar_readX(twenty_byte_buffer, file, jump+16, 4);
1462 parsedAtoms[atom_number-1].ancillary_data = UInt32FromBigEndian(twenty_byte_buffer);
1463 }
1464
1465 if ((generalAtomicLevel == 1) && (dataSize == 1)) { //mdat.length =1 64-bit length that is more of a cludge.
1466 jump += parsedAtoms[atom_number-1].AtomicLengthExtended;
1467 } else {
1468 jump += dataSize;
1469 }
1470 break;
1471 }
1472 case DUAL_STATE_ATOM : {
1473 if (memcmp(atom, "meta", 4) == 0) {
1474 jump += 12;
1475
1476 } else if (memcmp(atom, "dref", 4) == 0) {
1477 jump += 16;
1478
1479 } else if (memcmp(atom, "iinf", 4) == 0) {
1480 jump += 14;
1481
1482 } else if (memcmp(atom, "stsd", 4) == 0) {
1483 if (deepscan_REQ) {
1484 //for a tree ONLY, we go all the way, parsing everything; for any other option, we leave this atom as a monolithic entity
1485 jump += 16;
1486 } else {
1487 APar_Extract_stsd_codec(file, jump+16); //just get the codec used for this track
1488 jump += dataSize;
1489 }
1490
1491 } else if (memcmp(atom, "schi", 4) == 0) {
1492 if (memcmp(container, "sinf", 4) == 0) { //seems for iTMS drm files, schi is a simple parent atom, and 'user' comes right after it
1493 jump += 8;
1494 } else {
1495 jump += dataSize; //no idea what it would be under srpp, so just skip over it
1496 }
1497
1498 } else if (memcmp(container, "stsd", 4) == 0) {
1499 //each one is different, so list its size manually
1500 //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.
1501 //Getting a tree for display ONLY purposes is different from when setting a tag - display ONLY goes into stsd; tagging makes 'stsd' monolithic.
1502 //so setting metadata on unknown or improperly enumerated codecs (which might have different lengths) don't affect tagging.
1503 uint32_t named_atom = UInt32FromBigEndian(atom);
1504 switch(named_atom) {
1505 case 0x6D703473 : { //mp4s
1506 jump+= 16;
1507 break;
1508 }
1509 case 0x73727470 : //srtp
1510 case 0x72747020 : { //'rtp '
1511 jump+= 24;
1512 break;
1513 }
1514 case 0x616C6163 : //alac
1515 case 0x6D703461 : //mp4a
1516 case 0x73616D72 : //samr
1517 case 0x73617762 : //sawb
1518 case 0x73617770 : //sawp
1519 case 0x73657663 : //sevc
1520 case 0x73716370 : //sqcp
1521 case 0x73736D76 : //ssmv
1522 case 0x64726D73 : { //drms
1523 jump+= 36;
1524 break;
1525 }
1526 case 0x74783367 : { //tx3g
1527 jump+= 46;
1528 break;
1529 }
1530 case 0x6D6A7032 : //mjp2
1531 case 0x6D703476 : //mp4v
1532 case 0x61766331 : //avc1
1533 case 0x6A706567 : //jpeg
1534 case 0x73323633 : //s263
1535 case 0x64726D69 : { //drmi
1536 jump+= 86;
1537 break;
1538 }
1539 default : { //anything else that isn't covered here will just jump past any child atoms (avcp, text, enc*)
1540 jump += dataSize;
1541 }
1542 }
1543 }
1544 break;
1545 }
1546 case UNKNOWN_ATOM_TYPE : {
1547 jump += dataSize;
1548 break;
1549 }
1550 } //end swtich
1551
1552 generalAtomicLevel = APar_GetCurrentAtomDepth(jump, dataSize);
1553
1554 if ( (jump > 8 ? jump : 8) >= file_size) { //prevents jumping past EOF for the smallest of atoms
1555 break;
1556 }
1557
1558 fseeko(file, jump, SEEK_SET);
1559 }
1560
1561 } else {
1562 fprintf(stderr, "\nAtomicParsley error: bad mpeg4 file (ftyp atom missing or alignment error).\n\n");
1563 data = NULL;
1564 exit(1); //return;
1565 }
1566 APar_SampleTableIterator(file);
1567
1568 free(data);
1569 data=NULL;
1570 free(container);
1571 container=NULL;
1572 free(uuid_info.binary_uuid);
1573 free(uuid_info.uuid_AP_atom_name);
1574 fclose(file);
1575 }
1576 if (brand == 0x69736F6D) { //'isom' test for amc files & its (?always present?) uuid 0x63706764A88C11D48197009027087703
1577 char EZ_movie_uuid[100];
1578 memset(EZ_movie_uuid, 0, sizeof(EZ_movie_uuid));
1579 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
1580 APar_endian_uuid_bin_str_conversion(EZ_movie_uuid+5);
1581 if ( APar_FindAtom(EZ_movie_uuid, false, EXTENDED_ATOM, 0, true) != NULL) {
1582 metadata_style = UNDEFINED_STYLE;
1583 }
1584 }
1585 parsedfile = true;
1586 }
1587 if (!deep_atom_scan && !parsedfile && APar_FindAtom("moov", false, SIMPLE_ATOM, 0) == NULL) {
1588 fprintf(stderr, "\nAtomicParsley error: bad mpeg4 file (no 'moov' atom).\n\n");
1589 exit(1);
1590 }
1591 return;
1548 if (!parsedfile) {
1549 file_size = findFileSize(path);
1550
1551 FILE *file = APar_OpenFile(path, "rb");
1552 if (file != NULL) {
1553 char *data = (char *)calloc(1, 13);
1554 char *container = (char *)calloc(1, 20);
1555 memcpy(container, "FILE_LEVEL", 10);
1556 bool corrupted_data_atom = false;
1557 bool jpeg2000signature = false;
1558
1559 uuid_vitals uuid_info = {0};
1560 uuid_info.binary_uuid = (char *)malloc(
1561 sizeof(char) * 16 + 1); // this will hold any potential 16byte uuids
1562 uuid_info.uuid_AP_atom_name = (char *)malloc(
1563 sizeof(char) * 5); // this will hold any atom name that is written
1564 // after the uuid written by AP
1565
1566 if (data == NULL)
1567 return;
1568 uint64_t dataSize = 0;
1569 uint64_t jump = 0;
1570
1571 APar_readX(data, file, 0, 12);
1572 char *atom = data + 4;
1573
1574 if (memcmp(data,
1575 "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A ",
1576 12) == 0) {
1577 jpeg2000signature = true;
1578 }
1579
1580 if (memcmp(atom, "ftyp", 4) == 0 || jpeg2000signature) {
1581
1582 dataSize = UInt32FromBigEndian(data);
1583 jump = dataSize;
1584
1585 APar_AtomizeFileInfo(0,
1586 jump,
1587 0,
1588 atom,
1589 generalAtomicLevel,
1590 CHILD_ATOM,
1591 SIMPLE_ATOM,
1592 0,
1593 0,
1594 &uuid_info);
1595
1596 if (!jpeg2000signature) {
1597 APar_IdentifyBrand(data + 8);
1598 APar_TestCompatibleBrand(file, 0, dataSize);
1599 }
1600
1601 fseeko(file, jump, SEEK_SET);
1602
1603 while (jump < file_size) {
1604
1605 uuid_info.uuid_form =
1606 UUID_DEPRECATED_FORM; // start with the assumption that any found
1607 // atom is in the depracted uuid form
1608
1609 APar_readX_noseek(data, file, 8);
1610 char *atom = data + 4;
1611 dataSize = UInt32FromBigEndian(data);
1612
1613 if (jpeg2000signature) {
1614 if (memcmp(atom, "ftyp", 4) == 0) {
1615 APar_readX_noseek(twenty_byte_buffer, file, 4);
1616 APar_IdentifyBrand(twenty_byte_buffer);
1617 } else {
1618 exit(0); // the atom right after the jpeg2000/mjpeg2000 signature
1619 // is *supposed* to be 'ftyp'
1620 }
1621 jpeg2000signature = false;
1622 }
1623
1624 if (dataSize > file_size - jump) {
1625 dataSize = file_size - jump;
1626 }
1627
1628 if (dataSize == 0 &&
1629 (atom[0] == 0 && atom[1] == 0 && atom[2] == 0 && atom[3] == 0)) {
1630 gapless_void_padding =
1631 file_size - jump; // Apple has decided to add around 2k of NULL
1632 // space outside of any atom structure
1633 // starting with iTunes 7.0.0
1634 break; // its possible this is part of gapless playback - but then
1635 // why would it come after the 'free' at the end of a file
1636 // like gpac writes?
1637 } // after actual tested its elimination, it doesn't seem to be
1638 // required for gapless playback
1639
1640 // diagnose damage to 'cprt' by libmp4v2 in 1.4.1 & 1.5.0.1
1641 // typically, the length of this atom (dataSize) will exceeed it
1642 // parent (which is reported as 17) true length ot this data will be 9
1643 // - impossible for iTunes-style 'data' atom.
1644 if (memcmp(atom, "data", 4) == 0 &&
1645 parsedAtoms[atom_number - 1].AtomicContainerState ==
1646 PARENT_ATOM) {
1647 if (dataSize > parsedAtoms[atom_number - 1].AtomicLength) {
1648 dataSize = parsedAtoms[atom_number - 1].AtomicLength -
1649 8; // force its length to its true length
1650 fprintf(stdout,
1651 "AtomicParsley warning: the 'data' child of the '%s' "
1652 "atom seems to be corrupted.\n",
1653 parsedAtoms[atom_number - 1].AtomicName);
1654 corrupted_data_atom = true;
1655 }
1656 }
1657 // end diagnosis; APar_Manually_Determine_Parent will still determine
1658 // it to be a versioned atom (it tests by names), but at file write
1659 // out, it will write with a length of 9 bytes
1660
1661 APar_Manually_Determine_Parent(jump, dataSize, container);
1662 int filtered_known_atom =
1663 APar_MatchToKnownAtom(atom, container, true, NULL);
1664
1665 uint32_t atom_verflags = 0;
1666 uint16_t atom_language = 0;
1667
1668 if (memcmp(atom, "uuid", 4) == 0) {
1669 memset(uuid_info.binary_uuid, 0, 17);
1670
1671 APar_readX(uuid_info.binary_uuid, file, jump + 8, 16);
1672
1673 if (UInt32FromBigEndian(uuid_info.binary_uuid + 8) ==
1674 0) { // the deperacted uuid form
1675 memcpy(atom, uuid_info.binary_uuid, 4);
1676 atom_verflags =
1677 APar_read32(uuid_info.binary_uuid, file, jump + 12);
1678 if (atom_verflags > AtomFlags_Data_UInt) {
1679 atom_verflags = 0;
1680 }
1681 } else {
1682 uint8_t uuid_version =
1683 APar_extract_uuid_version(NULL, uuid_info.binary_uuid);
1684 APar_endian_uuid_bin_str_conversion(uuid_info.binary_uuid);
1685 if (uuid_version == 5) {
1686 uuid_info.uuid_form = UUID_SHA1_NAMESPACE;
1687 // read in what AP would set the atom name to. The new uuid form
1688 // is:
1689 // 4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes
1690 // name of uuid in AP namespace, 4bytes versioning, 4bytes NULL,
1691 // Xbytes data
1692 APar_readX(uuid_info.uuid_AP_atom_name, file, jump + 24, 4);
1693
1694 char uuid_of_foundname_in_AP_namesapce[20];
1695 APar_generate_uuid_from_atomname(
1696 uuid_info.uuid_AP_atom_name,
1697 uuid_of_foundname_in_AP_namesapce);
1698 if (memcmp(uuid_info.binary_uuid,
1699 uuid_of_foundname_in_AP_namesapce,
1700 16) == 0) {
1701 uuid_info.uuid_form =
1702 UUID_AP_SHA1_NAMESPACE; // our own uuid ver5 atoms in the
1703 // AtomicParsley.sf.net namespace
1704 atom_verflags =
1705 APar_read32(twenty_byte_buffer, file, jump + 28);
1706 }
1707 } else {
1708 uuid_info.uuid_form = UUID_OTHER;
1709 }
1710 }
1711 }
1712
1713 if (KnownAtoms[filtered_known_atom].box_type == VERSIONED_ATOM &&
1714 !corrupted_data_atom) {
1715 atom_verflags = APar_read32(twenty_byte_buffer, file, jump + 8);
1716 }
1717
1718 if (KnownAtoms[filtered_known_atom].box_type == PACKED_LANG_ATOM) {
1719 atom_verflags = APar_read32(twenty_byte_buffer, file, jump + 8);
1720
1721 // the problem with storing the language is that the uint16_t 2
1722 // bytes that carry the actual language are in different places on
1723 // different atoms some atoms have it right after the atom
1724 // version/flags; some like rating/classification have it 8 bytes
1725 // later; yrrc doesn't have it at all
1726 char bitpacked_lang[4];
1727 memset(bitpacked_lang, 0, 4);
1728
1729 uint32_t userdata_box = UInt32FromBigEndian(atom);
1730
1731 switch (userdata_box) {
1732 case 0x7469746C: //'titl'
1733 case 0x64736370: //'dscp'
1734 case 0x63707274: //'cprt'
1735 case 0x70657266: //'perf'
1736 case 0x61757468: //'auth'
1737 case 0x676E7265: //'gnre'
1738 case 0x616C626D: //'albm'
1739 case 0x6B797764: //'kywd'
1740 case 0x6C6F6369: //'loci'
1741 case 0x49443332: //'ID32' ; technically not a 'user data box', but
1742 // this only extracts the packed language (which
1743 // ID32 does have)
1744 {
1745 atom_language = APar_read16(bitpacked_lang, file, jump + 12);
1746 break;
1747 }
1748 case 0x636C7366: //'clsf'
1749 {
1750 atom_language = APar_read16(bitpacked_lang, file, jump + 18);
1751 break;
1752 }
1753 case 0x72746E67: //'rtng'
1754 {
1755 atom_language = APar_read16(bitpacked_lang, file, jump + 20);
1756 break;
1757 }
1758 // case 0x79727263 : //'yrrc' is the only 3gp tag that doesn't
1759 // support multiple languages; won't even get here because !=
1760 // PACKED_LANG_ATOM
1761 default: {
1762 break; // which means that any new/unknown packed language atoms
1763 // will have their language of 0; AP will only support 1 of
1764 // this atom name then
1765 }
1766 }
1767 }
1768
1769 // mdat.length=1; and ONLY supported for mdat atoms - no idea if the
1770 // spec says "only mdat", but that's what I'm doing for now
1771 if ((strncmp(atom, "mdat", 4) == 0) && (generalAtomicLevel == 1) &&
1772 (dataSize == 1)) {
1773
1774 uint64_t extended_dataSize =
1775 APar_read64(twenty_byte_buffer, file, jump + 8);
1776 APar_AtomizeFileInfo(
1777 jump,
1778 1,
1779 extended_dataSize,
1780 atom,
1781 generalAtomicLevel,
1782 KnownAtoms[filtered_known_atom].container_state,
1783 KnownAtoms[filtered_known_atom].box_type,
1784 atom_verflags,
1785 atom_language,
1786 &uuid_info);
1787
1788 } else {
1789 APar_AtomizeFileInfo(
1790 jump,
1791 dataSize,
1792 0,
1793 atom,
1794 generalAtomicLevel,
1795 KnownAtoms[filtered_known_atom].container_state,
1796 corrupted_data_atom ? SIMPLE_ATOM
1797 : KnownAtoms[filtered_known_atom].box_type,
1798 atom_verflags,
1799 atom_language,
1800 &uuid_info);
1801 }
1802 corrupted_data_atom = false;
1803
1804 // read in the name of an iTunes-style internal reverseDNS directly
1805 // into parsedAtoms
1806 if (memcmp(atom, "mean", 4) == 0 &&
1807 memcmp(parsedAtoms[atom_number - 2].AtomicName, "----", 4) == 0) {
1808 parsedAtoms[atom_number - 1].ReverseDNSdomain =
1809 (char *)calloc(1, sizeof(char) * dataSize);
1810
1811 // jump + 12 because 'name' atom is the 2nd child
1812 APar_readX(parsedAtoms[atom_number - 1].ReverseDNSdomain,
1813 file,
1814 jump + 12,
1815 dataSize - 12);
1816 }
1817 if (memcmp(atom, "name", 4) == 0 &&
1818 memcmp(parsedAtoms[atom_number - 2].AtomicName, "mean", 4) == 0 &&
1819 memcmp(parsedAtoms[atom_number - 3].AtomicName, "----", 4) == 0) {
1820
1821 parsedAtoms[atom_number - 1].ReverseDNSname =
1822 (char *)calloc(1, sizeof(char) * dataSize);
1823
1824 // jump + 12 because 'name' atom is the 2nd child
1825 APar_readX(parsedAtoms[atom_number - 1].ReverseDNSname,
1826 file,
1827 jump + 12,
1828 dataSize - 12);
1829 }
1830
1831 if (dataSize == 0) { // length = 0 means it reaches to EOF
1832 break;
1833 }
1834
1835 switch (KnownAtoms[filtered_known_atom].container_state) {
1836 case PARENT_ATOM: {
1837 jump += 8;
1838 break;
1839 }
1840 case CHILD_ATOM: {
1841 if (memcmp(atom, "hdlr", 4) == 0) {
1842 APar_readX(twenty_byte_buffer, file, jump + 16, 4);
1843 parsedAtoms[atom_number - 1].ancillary_data =
1844 UInt32FromBigEndian(twenty_byte_buffer);
1845 }
1846
1847 if ((generalAtomicLevel == 1) &&
1848 (dataSize ==
1849 1)) { // mdat.length =1 64-bit length that is more of a cludge.
1850 jump += parsedAtoms[atom_number - 1].AtomicLengthExtended;
1851 } else {
1852 jump += dataSize;
1853 }
1854 break;
1855 }
1856 case DUAL_STATE_ATOM: {
1857 if (memcmp(atom, "meta", 4) == 0) {
1858 jump += 12;
1859
1860 } else if (memcmp(atom, "dref", 4) == 0) {
1861 jump += 16;
1862
1863 } else if (memcmp(atom, "iinf", 4) == 0) {
1864 jump += 14;
1865
1866 } else if (memcmp(atom, "stsd", 4) == 0) {
1867 if (deepscan_REQ) {
1868 // for a tree ONLY, we go all the way, parsing everything; for
1869 // any other option, we leave this atom as a monolithic entity
1870 jump += 16;
1871 } else {
1872 APar_Extract_stsd_codec(
1873 file, jump + 16); // just get the codec used for this track
1874 jump += dataSize;
1875 }
1876
1877 } else if (memcmp(atom, "schi", 4) == 0) {
1878 if (memcmp(container, "sinf", 4) ==
1879 0) { // seems for iTMS drm files, schi is a simple parent
1880 // atom, and 'user' comes right after it
1881 jump += 8;
1882 } else {
1883 jump += dataSize; // no idea what it would be under srpp, so
1884 // just skip over it
1885 }
1886
1887 } else if (memcmp(container, "stsd", 4) == 0) {
1888 // each one is different, so list its size manually
1889 // the beauty of this is that even if there is an error here or a
1890 // new codec shows up, it only affects SHOWING the tree. Getting a
1891 // tree for display ONLY purposes is different from when setting a
1892 // tag - display ONLY goes into stsd; tagging makes 'stsd'
1893 // monolithic. so setting metadata on unknown or improperly
1894 // enumerated codecs (which might have different lengths) don't
1895 // affect tagging.
1896 uint32_t named_atom = UInt32FromBigEndian(atom);
1897 switch (named_atom) {
1898 case 0x6D703473: { // mp4s
1899 jump += 16;
1900 break;
1901 }
1902 case 0x73727470: // srtp
1903 case 0x72747020: { //'rtp '
1904 jump += 24;
1905 break;
1906 }
1907 case 0x616C6163: // alac
1908 case 0x6D703461: // mp4a
1909 case 0x73616D72: // samr
1910 case 0x73617762: // sawb
1911 case 0x73617770: // sawp
1912 case 0x73657663: // sevc
1913 case 0x73716370: // sqcp
1914 case 0x73736D76: // ssmv
1915 case 0x64726D73: { // drms
1916 jump += 36;
1917 break;
1918 }
1919 case 0x74783367: { // tx3g
1920 jump += 46;
1921 break;
1922 }
1923 case 0x6D6A7032: // mjp2
1924 case 0x6D703476: // mp4v
1925 case 0x61766331: // avc1
1926 case 0x6A706567: // jpeg
1927 case 0x73323633: // s263
1928 case 0x64726D69: { // drmi
1929 jump += 86;
1930 break;
1931 }
1932 default: { // anything else that isn't covered here will just jump
1933 // past any child atoms (avcp, text, enc*)
1934 jump += dataSize;
1935 }
1936 }
1937 }
1938 break;
1939 }
1940 case UNKNOWN_ATOM_TYPE: {
1941 jump += dataSize;
1942 break;
1943 }
1944 } // end swtich
1945
1946 generalAtomicLevel = APar_GetCurrentAtomDepth(jump, dataSize);
1947
1948 if ((jump > 8 ? jump : 8) >= file_size) { // prevents jumping past EOF
1949 // for the smallest of atoms
1950 break;
1951 }
1952
1953 fseeko(file, jump, SEEK_SET);
1954 }
1955
1956 } else {
1957 fprintf(stderr,
1958 "\nAtomicParsley error: bad mpeg4 file (ftyp atom "
1959 "missing or alignment error).\n\n");
1960 data = NULL;
1961 exit(1); // return;
1962 }
1963 APar_SampleTableIterator(file);
1964
1965 free(data);
1966 data = NULL;
1967 free(container);
1968 container = NULL;
1969 free(uuid_info.binary_uuid);
1970 free(uuid_info.uuid_AP_atom_name);
1971 fclose(file);
1972 }
1973 if (brand ==
1974 0x69736F6D) { //'isom' test for amc files & its (?always present?) uuid
1975 // 0x63706764A88C11D48197009027087703
1976 char EZ_movie_uuid[100];
1977 memset(EZ_movie_uuid, 0, sizeof(EZ_movie_uuid));
1978 memcpy(EZ_movie_uuid,
1979 "uuid="
1980 "\x63\x70\x67\x64\xA8\x8C\x11\xD4\x81\x97\x00\x90\x27\x08\x77\x03",
1981 21); // this is in an endian form, so it needs to be converted
1982 APar_endian_uuid_bin_str_conversion(EZ_movie_uuid + 5);
1983 if (APar_FindAtom(EZ_movie_uuid, false, EXTENDED_ATOM, 0, true) != NULL) {
1984 metadata_style = UNDEFINED_STYLE;
1985 }
1986 }
1987 parsedfile = true;
1988 }
1989 if (!deep_atom_scan && !parsedfile &&
1990 APar_FindAtom("moov", false, SIMPLE_ATOM, 0) == NULL) {
1991 fprintf(stderr,
1992 "\nAtomicParsley error: bad mpeg4 file (no 'moov' atom).\n\n");
1993 exit(1);
1994 }
1995 return;
15921996 }
15931997
15941998 ///////////////////////////////////////////////////////////////////////////////////////
1595 // mod time functions //
1999 // mod time functions //
15962000 ///////////////////////////////////////////////////////////////////////////////////////
15972001
15982002 void APar_FlagMovieHeader() {
1599 if (movie_header_atom == NULL) movie_header_atom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0);
1600 if (movie_header_atom == NULL) return;
1601 if (movie_header_atom != NULL) movie_header_atom->ancillary_data = 0x666C6167;
1602 return;
1603 }
1604
1605 void APar_FlagTrackHeader(AtomicInfo* thisAtom) {
1606 AtomicInfo* trak_atom = NULL;
1607 AtomicInfo* track_header_atom = NULL;
1608 short current_atom_idx = thisAtom->AtomicNumber;
1609 short current_level = thisAtom->AtomicLevel;
1610
1611 if (thisAtom->AtomicLevel >= 3) {
1612 while (true) {
1613 short parent_atom = APar_FindParentAtom(current_atom_idx, current_level);
1614 current_atom_idx = parent_atom;
1615 current_level = parsedAtoms[parent_atom].AtomicLevel;
1616
1617 if (current_level == 2) {
1618 if (memcmp(parsedAtoms[parent_atom].AtomicName, "trak", 4) == 0) {
1619 trak_atom = &parsedAtoms[parent_atom];
1620 }
1621 break;
1622 } else if (current_level == 1) {
1623 break;
1624 }
1625 }
1626 if (trak_atom != NULL) {
1627 track_header_atom = APar_FindChildAtom(trak_atom->AtomicNumber, "tkhd");
1628 if (track_header_atom != NULL) {
1629 track_header_atom->ancillary_data = 0x666C6167;
1630 }
1631 }
1632 }
1633 APar_FlagMovieHeader();
1634 return;
2003 if (movie_header_atom == NULL)
2004 movie_header_atom = APar_FindAtom("moov.mvhd", false, VERSIONED_ATOM, 0);
2005 if (movie_header_atom == NULL)
2006 return;
2007 if (movie_header_atom != NULL)
2008 movie_header_atom->ancillary_data = 0x666C6167;
2009 return;
2010 }
2011
2012 void APar_FlagTrackHeader(AtomicInfo *thisAtom) {
2013 AtomicInfo *trak_atom = NULL;
2014 AtomicInfo *track_header_atom = NULL;
2015 short current_atom_idx = thisAtom->AtomicNumber;
2016 short current_level = thisAtom->AtomicLevel;
2017
2018 if (thisAtom->AtomicLevel >= 3) {
2019 while (true) {
2020 short parent_atom = APar_FindParentAtom(current_atom_idx, current_level);
2021 current_atom_idx = parent_atom;
2022 current_level = parsedAtoms[parent_atom].AtomicLevel;
2023
2024 if (current_level == 2) {
2025 if (memcmp(parsedAtoms[parent_atom].AtomicName, "trak", 4) == 0) {
2026 trak_atom = &parsedAtoms[parent_atom];
2027 }
2028 break;
2029 } else if (current_level == 1) {
2030 break;
2031 }
2032 }
2033 if (trak_atom != NULL) {
2034 track_header_atom = APar_FindChildAtom(trak_atom->AtomicNumber, "tkhd");
2035 if (track_header_atom != NULL) {
2036 track_header_atom->ancillary_data = 0x666C6167;
2037 }
2038 }
2039 }
2040 APar_FlagMovieHeader();
2041 return;
16352042 }
16362043
16372044 ///////////////////////////////////////////////////////////////////////////////////////
1638 // Atom Removal Functions //
2045 // Atom Removal Functions //
16392046 ///////////////////////////////////////////////////////////////////////////////////////
16402047
16412048 /*----------------------
16422049 APar_EliminateAtom
16432050 this_atom_number - the index into parsedAtoms[] of the atom to be erased
1644 resume_atom_number - the point in parsedAtoms[] where the tree will be picked up
1645
1646 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
1647 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.
2051 resume_atom_number - the point in parsedAtoms[] where the tree will be
2052 picked up
2053
2054 This manually removes the atoms from being used. The atom is still in
2055 parsedAtoms[] at the same location it was previously, but because parsedAtoms is
2056 used as a linked list & followed by NextAtomNumber, effectively, this atom (and
2057 atoms leading to resume_atom_number) are no longer considered part of the tree.
16482058 ----------------------*/
16492059 void APar_EliminateAtom(short this_atom_number, int resume_atom_number) {
1650 if ( this_atom_number > 0 && this_atom_number < atom_number && resume_atom_number >= 0 && resume_atom_number < atom_number) {
1651 APar_FlagTrackHeader(&parsedAtoms[this_atom_number]);
1652
1653 short preceding_atom_pos = APar_FindPrecedingAtom(this_atom_number);
1654 if ( APar_Eval_ChunkOffsetImpact(this_atom_number) ) {
1655 removed_bytes_tally+=parsedAtoms[this_atom_number].AtomicLength; //used in validation routine
1656 }
1657 parsedAtoms[preceding_atom_pos].NextAtomNumber = resume_atom_number;
1658
1659 memset(parsedAtoms[this_atom_number].AtomicName, 0, 4); //blank out the name of the parent atom name
1660 parsedAtoms[this_atom_number].AtomicNumber = -1;
1661 parsedAtoms[this_atom_number].NextAtomNumber = -1;
1662 }
1663 return;
2060 if (this_atom_number > 0 && this_atom_number < atom_number &&
2061 resume_atom_number >= 0 && resume_atom_number < atom_number) {
2062 APar_FlagTrackHeader(&parsedAtoms[this_atom_number]);
2063
2064 short preceding_atom_pos = APar_FindPrecedingAtom(this_atom_number);
2065 if (APar_Eval_ChunkOffsetImpact(this_atom_number)) {
2066 removed_bytes_tally += parsedAtoms[this_atom_number]
2067 .AtomicLength; // used in validation routine
2068 }
2069 parsedAtoms[preceding_atom_pos].NextAtomNumber = resume_atom_number;
2070
2071 memset(parsedAtoms[this_atom_number].AtomicName,
2072 0,
2073 4); // blank out the name of the parent atom name
2074 parsedAtoms[this_atom_number].AtomicNumber = -1;
2075 parsedAtoms[this_atom_number].NextAtomNumber = -1;
2076 }
2077 return;
16642078 }
16652079
16662080 /*----------------------
16672081 APar_RemoveAtom
1668 atom_path - the "peri.od_d.elim.inat.ed__.atom.path" string that represents the target atom
1669 atom_type - the type of atom to be eliminated (packed language, extended...) of the target atom
1670 UD_lang - the language code for a packed language atom (ignored for non-packed language atoms)
1671 rDNS_domain - the reverse DNS domain (com.foo.thing) of the atom (ignored for non reverse DNS '----' atoms)
1672
1673 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.
1674 TODO: the last child part could use some more intelligence at some point; its relatively hardcoded.
2082 atom_path - the "peri.od_d.elim.inat.ed__.atom.path" string that represents
2083 the target atom atom_type - the type of atom to be eliminated (packed language,
2084 extended...) of the target atom UD_lang - the language code for a packed
2085 language atom (ignored for non-packed language atoms) rDNS_domain - the reverse
2086 DNS domain (com.foo.thing) of the atom (ignored for non reverse DNS '----'
2087 atoms)
2088
2089 APar_RemoveAtom tries to find the atom in the string. If it exists, then
2090 depending on its atom_type, it or its last child will get passed along for
2091 elimination.
2092 TODO: the last child part could use some more intelligence at
2093 some point; its relatively hardcoded.
16752094 ----------------------*/
1676 void APar_RemoveAtom(const char* atom_path, uint8_t atom_type, uint16_t UD_lang, const char* rDNS_domain) {
1677
1678 AtomicInfo* desiredAtom = APar_FindAtom(atom_path, false, atom_type, UD_lang, (atom_type == EXTENDED_ATOM ? true : false), rDNS_domain );
1679
1680 if (desiredAtom == NULL) return; //the atom didn't exist or wasn't found
1681 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)
1682
1683 modified_atoms = true;
1684 if (atom_type != EXTENDED_ATOM) {
1685 if (atom_type == PACKED_LANG_ATOM || desiredAtom->AtomicClassification == UNKNOWN_ATOM) {
1686 APar_EliminateAtom(desiredAtom->AtomicNumber, desiredAtom->NextAtomNumber);
1687
1688 //reverseDNS atom
1689 } else if (desiredAtom->ReverseDNSname != NULL) {
1690 short parent_atom = APar_FindParentAtom(desiredAtom->AtomicNumber, desiredAtom->AtomicLevel);
1691 short last_elim_atom = APar_FindLastChild_of_ParentAtom(parent_atom);
1692 APar_EliminateAtom( parent_atom, parsedAtoms[last_elim_atom].NextAtomNumber );
1693
1694 } else if (memcmp(desiredAtom->AtomicName, "data", 4) == 0 && desiredAtom->AtomicLevel == 6){
1695 short parent_atom = APar_FindParentAtom(desiredAtom->AtomicNumber, desiredAtom->AtomicLevel);
1696 short last_elim_atom = APar_FindLastChild_of_ParentAtom(parent_atom);
1697 APar_EliminateAtom( parent_atom, parsedAtoms[last_elim_atom].NextAtomNumber );
1698
1699 } else if (desiredAtom->AtomicContainerState <= DUAL_STATE_ATOM) {
1700 short last_elim_atom = APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber);
1701 APar_EliminateAtom( desiredAtom->AtomicNumber, parsedAtoms[last_elim_atom].NextAtomNumber );
1702
1703 } else if (UD_lang == 1) { //yrrc
1704 APar_EliminateAtom(desiredAtom->AtomicNumber, desiredAtom->NextAtomNumber);
1705
1706 } else {
1707 short last_elim_atom = APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber);
1708 APar_EliminateAtom(desiredAtom->AtomicNumber, last_elim_atom );
1709 }
1710
1711 //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
1712 } else if (atom_type == EXTENDED_ATOM) {
1713 APar_EliminateAtom(desiredAtom->AtomicNumber, desiredAtom->NextAtomNumber);
1714 }
1715 return;
2095 void APar_RemoveAtom(const char *atom_path,
2096 uint8_t atom_type,
2097 uint16_t UD_lang,
2098 const char *rDNS_domain) {
2099
2100 AtomicInfo *desiredAtom =
2101 APar_FindAtom(atom_path,
2102 false,
2103 atom_type,
2104 UD_lang,
2105 (atom_type == EXTENDED_ATOM ? true : false),
2106 rDNS_domain);
2107
2108 if (desiredAtom == NULL)
2109 return; // the atom didn't exist or wasn't found
2110 if (desiredAtom->AtomicNumber == 0)
2111 return; // we got the default atom, ftyp - and since that can't be removed,
2112 // it must not exist (or it was missed)
2113
2114 modified_atoms = true;
2115 if (atom_type != EXTENDED_ATOM) {
2116 if (atom_type == PACKED_LANG_ATOM ||
2117 desiredAtom->AtomicClassification == UNKNOWN_ATOM) {
2118 APar_EliminateAtom(desiredAtom->AtomicNumber,
2119 desiredAtom->NextAtomNumber);
2120
2121 // reverseDNS atom
2122 } else if (desiredAtom->ReverseDNSname != NULL) {
2123 short parent_atom = APar_FindParentAtom(desiredAtom->AtomicNumber,
2124 desiredAtom->AtomicLevel);
2125 short last_elim_atom = APar_FindLastChild_of_ParentAtom(parent_atom);
2126 APar_EliminateAtom(parent_atom,
2127 parsedAtoms[last_elim_atom].NextAtomNumber);
2128
2129 } else if (memcmp(desiredAtom->AtomicName, "data", 4) == 0 &&
2130 desiredAtom->AtomicLevel == 6) {
2131 short parent_atom = APar_FindParentAtom(desiredAtom->AtomicNumber,
2132 desiredAtom->AtomicLevel);
2133 short last_elim_atom = APar_FindLastChild_of_ParentAtom(parent_atom);
2134 APar_EliminateAtom(parent_atom,
2135 parsedAtoms[last_elim_atom].NextAtomNumber);
2136
2137 } else if (desiredAtom->AtomicContainerState <= DUAL_STATE_ATOM) {
2138 short last_elim_atom =
2139 APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber);
2140 APar_EliminateAtom(desiredAtom->AtomicNumber,
2141 parsedAtoms[last_elim_atom].NextAtomNumber);
2142
2143 } else if (UD_lang == 1) { // yrrc
2144 APar_EliminateAtom(desiredAtom->AtomicNumber,
2145 desiredAtom->NextAtomNumber);
2146
2147 } else {
2148 short last_elim_atom =
2149 APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber);
2150 APar_EliminateAtom(desiredAtom->AtomicNumber, last_elim_atom);
2151 }
2152
2153 // this will only work for AtomicParsley created uuid atoms that don't have
2154 // children, but since all uuid atoms are treaded as non-parent atoms... no
2155 // problems
2156 } else if (atom_type == EXTENDED_ATOM) {
2157 APar_EliminateAtom(desiredAtom->AtomicNumber, desiredAtom->NextAtomNumber);
2158 }
2159 return;
17162160 }
17172161
17182162 /*----------------------
17192163 APar_freefree
1720 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.
1721
1722 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
1723 '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.
1724 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
1725 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
1726 a single letter.
1727 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
1728 be eliminated (but another will appear later as padding - to completely eliminate any resulting 'free' atoms, the environmental variable "AP_PADDING" needs to be
1729 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
1730 removed. A value of >= 1 will eliminate 'free' atoms between those levels and level 1 (or file level).
2164 purge_level - an integer ranging from -1 to some positive number of the level
2165 a 'free' atom must be on for it to be erased.
2166
2167 Some tagging utilities (things based on libmp4v2 & faac irrespective of
2168 which tagging system used) have a dirty little secret. They way the work is to
2169 copy the 'moov' atom - in its entirety - to the end of the file, and make the
2170 changes there. The original 'moov' file is nulled out, but the file only
2171 increases in size. Even if you eliminate the tag, the file grows. Only when the
2172 'moov' atom is last do these taggers work efficiently - and they are blazingly
2173 fast, no doubt about that - but they are incredibly wasteful. It is possible to
2174 switch between using AP and mp4tags and build a file with dozens of megabytes
2175 wasted just be changing a single letter. This function can be used to iterate
2176 through the atoms in the file, and selectively eliminate 'free' atoms. Pass a -1
2177 and every last 'free' atom that exists will be eliminated (but another will
2178 appear later as padding - to completely eliminate any resulting 'free' atoms,
2179 the environmental variable "AP_PADDING" needs to be set with MID_PAD to 0). Pass
2180 a 0, and all 'free' atoms preceding 'moov' or after 'mdat' (including gpac's
2181 pesky "Place Your Ad Here" free-at-the-end) will be removed. A value of >= 1
2182 will eliminate 'free' atoms between those levels and level 1 (or file level).
17312183 ----------------------*/
17322184 void APar_freefree(int purge_level) {
1733 modified_atoms = true;
1734 short eval_atom = 0;
1735 short prev_atom = 0;
1736 short moov_atom = 0; //a moov atom has yet to be seen
1737 short mdat_atom = 0; //any ol' mdat
1738
1739 if (purge_level == -1) {
1740 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
1741 }
1742
1743 while (true) {
1744 prev_atom = eval_atom;
1745 eval_atom = parsedAtoms[eval_atom].NextAtomNumber;
1746 if (eval_atom == 0) { //we've hit the last atom
1747 break;
1748 }
1749
1750 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0 ) {
1751 //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);
1752 if ( purge_level == -1 || purge_level >= parsedAtoms[eval_atom].AtomicLevel ||
1753 (purge_level == 0 && parsedAtoms[eval_atom].AtomicLevel == 1 && (moov_atom == 0 || mdat_atom != 0)) ) {
1754 short prev_atom = APar_FindPrecedingAtom(eval_atom);
1755 if (parsedAtoms[eval_atom].NextAtomNumber == 0) { //we've hit the last atom
1756 APar_EliminateAtom(eval_atom, parsedAtoms[eval_atom].NextAtomNumber);
1757 parsedAtoms[prev_atom].NextAtomNumber = 0;
1758 } else {
1759 APar_EliminateAtom(eval_atom, parsedAtoms[eval_atom].NextAtomNumber);
1760 }
1761 eval_atom = prev_atom; //go back to the previous atom and continue the search
1762 }
1763 }
1764 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "moov", 4) == 0 ) {
1765 moov_atom = eval_atom;
1766 }
1767 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0 ) {
1768 mdat_atom = eval_atom;
1769 }
1770 }
1771 return;
2185 modified_atoms = true;
2186 short eval_atom = 0;
2187 short prev_atom = 0;
2188 short moov_atom = 0; // a moov atom has yet to be seen
2189 short mdat_atom = 0; // any ol' mdat
2190
2191 if (purge_level == -1) {
2192 complete_free_space_erasure =
2193 true; // prevent any in situ dynamic updating when trying to remove all
2194 // free atoms. Also triggers a more efficient means of forcing
2195 // padding
2196 }
2197
2198 while (true) {
2199 prev_atom = eval_atom;
2200 eval_atom = parsedAtoms[eval_atom].NextAtomNumber;
2201 if (eval_atom == 0) { // we've hit the last atom
2202 break;
2203 }
2204
2205 if (memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 ||
2206 memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) {
2207 // fprintf(stdout, "i am of size %" PRIu64 " purge level %i (%u) -> %i\n",
2208 // parsedAtoms[eval_atom].AtomicLength, purge_level,
2209 // parsedAtoms[eval_atom].AtomicLevel, eval_atom);
2210 if (purge_level == -1 ||
2211 purge_level >= parsedAtoms[eval_atom].AtomicLevel ||
2212 (purge_level == 0 && parsedAtoms[eval_atom].AtomicLevel == 1 &&
2213 (moov_atom == 0 || mdat_atom != 0))) {
2214 short prev_atom = APar_FindPrecedingAtom(eval_atom);
2215 if (parsedAtoms[eval_atom].NextAtomNumber ==
2216 0) { // we've hit the last atom
2217 APar_EliminateAtom(eval_atom, parsedAtoms[eval_atom].NextAtomNumber);
2218 parsedAtoms[prev_atom].NextAtomNumber = 0;
2219 } else {
2220 APar_EliminateAtom(eval_atom, parsedAtoms[eval_atom].NextAtomNumber);
2221 }
2222 eval_atom =
2223 prev_atom; // go back to the previous atom and continue the search
2224 }
2225 }
2226 if (memcmp(parsedAtoms[eval_atom].AtomicName, "moov", 4) == 0) {
2227 moov_atom = eval_atom;
2228 }
2229 if (memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0) {
2230 mdat_atom = eval_atom;
2231 }
2232 }
2233 return;
17722234 }
17732235
17742236 ///////////////////////////////////////////////////////////////////////////////////////
1775 // Atom Moving Functions //
2237 // Atom Moving Functions //
17762238 ///////////////////////////////////////////////////////////////////////////////////////
17772239
17782240 /*----------------------
17792241 APar_MoveAtom
17802242 this_atom_number - the atom that will follow the new_position atom
1781 new_position - the atom that the atoms at that level will precede, followed by this_atom_number (including its hierarchy of child atoms)
1782
1783 first find which atoms lead to both atoms (precedingAtom flows to this_atom_number, lastStationaryAtom flows to new_position). Depending on whether there
1784 are children, find the last atom in the atoms that will be moving. Shunt as required; reordering occurs by following NextAtomNumber (linked list)
2243 new_position - the atom that the atoms at that level will precede, followed by
2244 this_atom_number (including its hierarchy of child atoms)
2245
2246 first find which atoms lead to both atoms (precedingAtom flows to
2247 this_atom_number, lastStationaryAtom flows to new_position). Depending on
2248 whether there are children, find the last atom in the atoms that will be moving.
2249 Shunt as required; reordering occurs by following NextAtomNumber (linked list)
17852250 ----------------------*/
17862251 void APar_MoveAtom(short this_atom_number, short new_position) {
1787 short precedingAtom = 0;
1788 short lastStationaryAtom = 0;
1789 short iter = 0;
1790
1791 //look for the preceding atom (either directly before of the same level, or moov's last nth level child
1792 while (parsedAtoms[iter].NextAtomNumber != 0) {
1793 if (parsedAtoms[iter].NextAtomNumber == this_atom_number) {
1794 precedingAtom = iter;
1795 break;
1796 } else {
1797 if (parsedAtoms[iter].NextAtomNumber == 0) { //we found the last atom (which we end our search on)
1798 break;
1799 }
1800 }
1801 iter=parsedAtoms[iter].NextAtomNumber;
1802 }
1803
1804 iter = 0;
1805
1806 //search where to insert our new atom
1807 while (parsedAtoms[iter].NextAtomNumber != 0) {
1808 if (parsedAtoms[iter].NextAtomNumber == new_position) {
1809 lastStationaryAtom = iter;
1810 break;
1811 }
1812 iter=parsedAtoms[iter].NextAtomNumber;
1813 if (parsedAtoms[iter].NextAtomNumber == 0) { //we found the last atom
1814 lastStationaryAtom = iter;
1815 break;
1816 }
1817
1818 }
1819 //fprintf(stdout, "%s preceded by %s, last would be %s\n", parsedAtoms[this_atom_number].AtomicName, parsedAtoms[precedingAtom].AtomicName, parsedAtoms[lastStationaryAtom].AtomicName);
1820
1821 if (parsedAtoms[this_atom_number].AtomicContainerState <= DUAL_STATE_ATOM) {
1822 if (parsedAtoms[new_position].AtomicContainerState <= DUAL_STATE_ATOM) {
1823 short last_SwapChild = APar_FindLastChild_of_ParentAtom(this_atom_number);
1824 short last_WiredChild = APar_FindLastChild_of_ParentAtom(new_position);
1825 //fprintf(stdout, "moving %s, last child atom %s\n", parsedAtoms[this_atom_number].AtomicName, parsedAtoms[last_SwapChild].AtomicName);
1826 //fprintf(stdout, "wired %s, last child atom %s\n", parsedAtoms[new_position].AtomicName, parsedAtoms[last_WiredChild].AtomicName);
1827 //fprintf(stdout, "stationary atom %s , preceding atom %s\n", parsedAtoms[lastStationaryAtom].AtomicName, parsedAtoms[precedingAtom].AtomicName);
1828
1829 short swap_resume = parsedAtoms[last_SwapChild].NextAtomNumber;
1830 short wired_resume = parsedAtoms[last_WiredChild].NextAtomNumber;
1831
1832 parsedAtoms[precedingAtom].NextAtomNumber = swap_resume; //shunt the main tree (over the [this_atom_number] atom to be move) to other tween atoms,
1833 parsedAtoms[lastStationaryAtom].NextAtomNumber = new_position; //pick up with the 2nd to last hierarchy
1834 parsedAtoms[last_WiredChild].NextAtomNumber = this_atom_number; //and route the 2nd to last hierarchy to wrap around to the this_atom_number atom
1835 parsedAtoms[last_SwapChild].NextAtomNumber = wired_resume; //and continue with whatever was after the [new_position] atom
1836
1837
1838 } else {
1839 short last_child = APar_FindLastChild_of_ParentAtom(this_atom_number);
1840 parsedAtoms[lastStationaryAtom].NextAtomNumber = this_atom_number;
1841 parsedAtoms[precedingAtom].NextAtomNumber = parsedAtoms[last_child].NextAtomNumber;
1842 parsedAtoms[last_child].NextAtomNumber = new_position;
1843 }
1844
1845 } else {
1846 parsedAtoms[lastStationaryAtom].NextAtomNumber = this_atom_number;
1847 parsedAtoms[precedingAtom].NextAtomNumber = parsedAtoms[this_atom_number].NextAtomNumber;
1848 parsedAtoms[this_atom_number].NextAtomNumber = new_position;
1849 }
1850
1851 return;
2252 short precedingAtom = 0;
2253 short lastStationaryAtom = 0;
2254 short iter = 0;
2255
2256 // look for the preceding atom (either directly before of the same level, or
2257 // moov's last nth level child
2258 while (parsedAtoms[iter].NextAtomNumber != 0) {
2259 if (parsedAtoms[iter].NextAtomNumber == this_atom_number) {
2260 precedingAtom = iter;
2261 break;
2262 } else {
2263 if (parsedAtoms[iter].NextAtomNumber ==
2264 0) { // we found the last atom (which we end our search on)
2265 break;
2266 }
2267 }
2268 iter = parsedAtoms[iter].NextAtomNumber;
2269 }
2270
2271 iter = 0;
2272
2273 // search where to insert our new atom
2274 while (parsedAtoms[iter].NextAtomNumber != 0) {
2275 if (parsedAtoms[iter].NextAtomNumber == new_position) {
2276 lastStationaryAtom = iter;
2277 break;
2278 }
2279 iter = parsedAtoms[iter].NextAtomNumber;
2280 if (parsedAtoms[iter].NextAtomNumber == 0) { // we found the last atom
2281 lastStationaryAtom = iter;
2282 break;
2283 }
2284 }
2285 // fprintf(stdout, "%s preceded by %s, last would be %s\n",
2286 // parsedAtoms[this_atom_number].AtomicName,
2287 // parsedAtoms[precedingAtom].AtomicName,
2288 // parsedAtoms[lastStationaryAtom].AtomicName);
2289
2290 if (parsedAtoms[this_atom_number].AtomicContainerState <= DUAL_STATE_ATOM) {
2291 if (parsedAtoms[new_position].AtomicContainerState <= DUAL_STATE_ATOM) {
2292 short last_SwapChild = APar_FindLastChild_of_ParentAtom(this_atom_number);
2293 short last_WiredChild = APar_FindLastChild_of_ParentAtom(new_position);
2294 // fprintf(stdout, "moving %s, last child atom %s\n",
2295 // parsedAtoms[this_atom_number].AtomicName,
2296 // parsedAtoms[last_SwapChild].AtomicName); fprintf(stdout, "wired %s,
2297 // last child atom %s\n", parsedAtoms[new_position].AtomicName,
2298 // parsedAtoms[last_WiredChild].AtomicName); fprintf(stdout, "stationary
2299 // atom %s , preceding atom %s\n",
2300 // parsedAtoms[lastStationaryAtom].AtomicName,
2301 // parsedAtoms[precedingAtom].AtomicName);
2302
2303 short swap_resume = parsedAtoms[last_SwapChild].NextAtomNumber;
2304 short wired_resume = parsedAtoms[last_WiredChild].NextAtomNumber;
2305
2306 parsedAtoms[precedingAtom].NextAtomNumber =
2307 swap_resume; // shunt the main tree (over the [this_atom_number] atom
2308 // to be move) to other tween atoms,
2309 parsedAtoms[lastStationaryAtom].NextAtomNumber =
2310 new_position; // pick up with the 2nd to last hierarchy
2311 parsedAtoms[last_WiredChild].NextAtomNumber =
2312 this_atom_number; // and route the 2nd to last hierarchy to wrap
2313 // around to the this_atom_number atom
2314 parsedAtoms[last_SwapChild].NextAtomNumber =
2315 wired_resume; // and continue with whatever was after the
2316 // [new_position] atom
2317
2318 } else {
2319 short last_child = APar_FindLastChild_of_ParentAtom(this_atom_number);
2320 parsedAtoms[lastStationaryAtom].NextAtomNumber = this_atom_number;
2321 parsedAtoms[precedingAtom].NextAtomNumber =
2322 parsedAtoms[last_child].NextAtomNumber;
2323 parsedAtoms[last_child].NextAtomNumber = new_position;
2324 }
2325
2326 } else {
2327 parsedAtoms[lastStationaryAtom].NextAtomNumber = this_atom_number;
2328 parsedAtoms[precedingAtom].NextAtomNumber =
2329 parsedAtoms[this_atom_number].NextAtomNumber;
2330 parsedAtoms[this_atom_number].NextAtomNumber = new_position;
2331 }
2332
2333 return;
18522334 }
18532335
18542336 ///////////////////////////////////////////////////////////////////////////////////////
1855 // Atom Creation Functions //
2337 // Atom Creation Functions //
18562338 ///////////////////////////////////////////////////////////////////////////////////////
18572339
18582340 /*----------------------
18592341 APar_InterjectNewAtom
18602342 atom_name - the 4 character name of the atom
1861 cntr_state - the type of container it will be (child, parent, dual state)
1862 atom_class - the atom type it will be (simple, versioned, uuid, versioned with packed language)
1863 atom_length - the forced length of this atom (undefined beyond intrinsic length of the container type except for 'free' atoms)
1864 atom_verflags - the 1 byte atom version & 3 bytes atom flags for the atom if versioned
1865 packed_lang - the 2 byte packed language for the atom if versioned with packed language type
1866 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)
1867 preceding_atom - the atom that precedes this newly created interjected atom
1868
1869 Creates a single new atom (carrying NULLed data) inserted after preceding_atom
2343 cntr_state - the type of container it will be (child, parent, dual
2344 state) atom_class - the atom type it will be (simple, versioned, uuid, versioned
2345 with packed language) atom_length - the forced length of this atom (undefined
2346 beyond intrinsic length of the container type except for 'free' atoms)
2347 atom_verflags - the 1 byte atom version & 3 bytes atom flags for the
2348 atom if versioned packed_lang - the 2 byte packed language for the atom if
2349 versioned with packed language type atom_level - the level of this atom (1
2350 denotes file level, anything else denotes a child of the last preceding parent
2351 or dual state atom) preceding_atom - the atom that precedes this newly created
2352 interjected atom
2353
2354 Creates a single new atom (carrying NULLed data) inserted after
2355 preceding_atom
18702356 ----------------------*/
1871 short APar_InterjectNewAtom(const char* atom_name, uint8_t cntr_state,
1872 uint8_t atom_class, uint64_t atom_length,
1873 uint32_t atom_verflags, uint16_t packed_lang, uint8_t atom_level,
1874 short preceding_atom)
1875 {
1876
1877 if (deep_atom_scan && !modified_atoms) {
1878 return 0;
1879 }
1880
1881 if (atom_number >= MAX_ATOMS) {
1882 fprintf(stderr, "too many atoms\n");
1883 abort();
1884 }
1885
1886 AtomicInfo* new_atom = &parsedAtoms[atom_number];
1887 new_atom->AtomicNumber = atom_number;
1888 new_atom->AtomicName = (char*)malloc(sizeof(char)*6);
1889 memset(new_atom->AtomicName, 0, sizeof(char)*6);
1890 memcpy(new_atom->AtomicName, atom_name, 4);
1891
1892 new_atom->AtomicContainerState = cntr_state;
1893 new_atom->AtomicClassification = atom_class;
1894 new_atom->AtomicVerFlags = atom_verflags;
1895 new_atom->AtomicLevel = atom_level;
1896 new_atom->AtomicLength = atom_length;
1897 new_atom->AtomicLanguage = packed_lang;
1898
1899 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)
1900
1901 new_atom->ID32_TagInfo = NULL;
1902
1903 new_atom->NextAtomNumber = parsedAtoms[ preceding_atom ].NextAtomNumber;
1904 parsedAtoms[ preceding_atom ].NextAtomNumber = atom_number;
1905
1906 atom_number++;
1907 return new_atom->AtomicNumber;
2357 short APar_InterjectNewAtom(const char *atom_name,
2358 uint8_t cntr_state,
2359 uint8_t atom_class,
2360 uint64_t atom_length,
2361 uint32_t atom_verflags,
2362 uint16_t packed_lang,
2363 uint8_t atom_level,
2364 short preceding_atom) {
2365
2366 if (deep_atom_scan && !modified_atoms) {
2367 return 0;
2368 }
2369
2370 if (atom_number >= MAX_ATOMS) {
2371 fprintf(stderr, "too many atoms\n");
2372 abort();
2373 }
2374
2375 AtomicInfo *new_atom = &parsedAtoms[atom_number];
2376 new_atom->AtomicNumber = atom_number;
2377 new_atom->AtomicName = (char *)malloc(sizeof(char) * 6);
2378 memset(new_atom->AtomicName, 0, sizeof(char) * 6);
2379 memcpy(new_atom->AtomicName, atom_name, 4);
2380
2381 new_atom->AtomicContainerState = cntr_state;
2382 new_atom->AtomicClassification = atom_class;
2383 new_atom->AtomicVerFlags = atom_verflags;
2384 new_atom->AtomicLevel = atom_level;
2385 new_atom->AtomicLength = atom_length;
2386 new_atom->AtomicLanguage = packed_lang;
2387
2388 new_atom->AtomicData = (char *)calloc(
2389 1,
2390 sizeof(char) * (atom_length > 16
2391 ? atom_length
2392 : 16)); // puts a hard limit on the length of
2393 // strings (the spec doesn't)
2394
2395 new_atom->ID32_TagInfo = NULL;
2396
2397 new_atom->NextAtomNumber = parsedAtoms[preceding_atom].NextAtomNumber;
2398 parsedAtoms[preceding_atom].NextAtomNumber = atom_number;
2399
2400 atom_number++;
2401 return new_atom->AtomicNumber;
19082402 }
19092403
19102404 /*----------------------
19112405 APar_CreateSparseAtom
19122406
1913 surrogate_atom - an skeletal template of the atom to be created; currently name, level, lang, (if uuid/extended: container & class) are copied; other
1914 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)
1915 parent_atom - the stats for the parent atom (used to match through the KnownAtoms array and get things like container & class (for most atoms)
1916 preceding_atom - the new atom will follow this atom
1917
1918 Create a single new atom (not carrying any data) copied from a template to follow preceding_atom
2407 surrogate_atom - an skeletal template of the atom to be created;
2408 currently name, level, lang, (if uuid/extended: container & class) are copied;
2409 other stats should be filled in routines that called for their creation and know
2410 things like flags & if is to carry an data (this doesn't malloc) parent_atom -
2411 the stats for the parent atom (used to match through the KnownAtoms array and
2412 get things like container & class (for most atoms) preceding_atom - the new atom
2413 will follow this atom
2414
2415 Create a single new atom (not carrying any data) copied from a template to
2416 follow preceding_atom
19192417 ----------------------*/
1920 AtomicInfo* APar_CreateSparseAtom(AtomicInfo* surrogate_atom, AtomicInfo* parent_atom, short preceding_atom) {
1921
1922 if (atom_number >= MAX_ATOMS) {
1923 fprintf(stderr, "too many atoms\n");
1924 abort();
1925 }
1926 AtomicInfo* new_atom = &parsedAtoms[atom_number];
1927 new_atom->AtomicNumber = atom_number;
1928 new_atom->AtomicStart = 0;
1929 int known_atom = 0;
1930
1931 new_atom->AtomicName = (char*)malloc(sizeof(char)*20);
1932 memset(new_atom->AtomicName, 0, sizeof(char)*20);
1933 size_t copy_bytes_len = (surrogate_atom->AtomicClassification == EXTENDED_ATOM ? 16 : 4);
1934 memcpy(new_atom->AtomicName, surrogate_atom->AtomicName, copy_bytes_len);
1935 new_atom->AtomicLevel = surrogate_atom->AtomicLevel;
1936 new_atom->AtomicLanguage = surrogate_atom->AtomicLanguage;
1937
1938 //this is almost assuredly wrong for everything except a simple parent atom & needs to be handled properly afterwards; Note the use of 'Sparse'
1939 new_atom->AtomicVerFlags = 0;
1940 new_atom->AtomicLength = 8;
1941
1942 new_atom->NextAtomNumber = parsedAtoms[ preceding_atom ].NextAtomNumber;
1943 parsedAtoms[ preceding_atom ].NextAtomNumber = atom_number;
1944
1945 //if 'uuid' atom, copy the info directly, otherwise use KnownAtoms to get the info
1946 if (surrogate_atom->AtomicClassification == EXTENDED_ATOM) {
1947 new_atom->AtomicContainerState = CHILD_ATOM;
1948 new_atom->AtomicClassification = surrogate_atom->AtomicClassification;
1949
1950 new_atom->uuid_style = UUID_AP_SHA1_NAMESPACE;
1951
1952 } else {
1953 //determine the type of atom from our array of KnownAtoms; this is a worst case scenario; it should be handled properly afterwards
1954 //make sure the level & the atom gets integrated into NextAtomNumber before APar_MatchToKnownAtom because getting the fullpath will rely on that
1955 known_atom = APar_MatchToKnownAtom(surrogate_atom->AtomicName, (parent_atom == NULL ? "FILE_LEVEL" : parent_atom->AtomicName), false, NULL);
1956
1957 new_atom->AtomicContainerState = KnownAtoms[known_atom].container_state;
1958 new_atom->AtomicClassification = KnownAtoms[known_atom].box_type;
1959 }
1960 new_atom->ID32_TagInfo = NULL;
1961
1962 atom_number++;
1963
1964 return new_atom;
2418 AtomicInfo *APar_CreateSparseAtom(AtomicInfo *surrogate_atom,
2419 AtomicInfo *parent_atom,
2420 short preceding_atom) {
2421
2422 if (atom_number >= MAX_ATOMS) {
2423 fprintf(stderr, "too many atoms\n");
2424 abort();
2425 }
2426 AtomicInfo *new_atom = &parsedAtoms[atom_number];
2427 new_atom->AtomicNumber = atom_number;
2428 new_atom->AtomicStart = 0;
2429 int known_atom = 0;
2430
2431 new_atom->AtomicName = (char *)malloc(sizeof(char) * 20);
2432 memset(new_atom->AtomicName, 0, sizeof(char) * 20);
2433 size_t copy_bytes_len =
2434 (surrogate_atom->AtomicClassification == EXTENDED_ATOM ? 16 : 4);
2435 memcpy(new_atom->AtomicName, surrogate_atom->AtomicName, copy_bytes_len);
2436 new_atom->AtomicLevel = surrogate_atom->AtomicLevel;
2437 new_atom->AtomicLanguage = surrogate_atom->AtomicLanguage;
2438
2439 // this is almost assuredly wrong for everything except a simple parent atom &
2440 // needs to be handled properly afterwards; Note the use of 'Sparse'
2441 new_atom->AtomicVerFlags = 0;
2442 new_atom->AtomicLength = 8;
2443
2444 new_atom->NextAtomNumber = parsedAtoms[preceding_atom].NextAtomNumber;
2445 parsedAtoms[preceding_atom].NextAtomNumber = atom_number;
2446
2447 // if 'uuid' atom, copy the info directly, otherwise use KnownAtoms to get the
2448 // info
2449 if (surrogate_atom->AtomicClassification == EXTENDED_ATOM) {
2450 new_atom->AtomicContainerState = CHILD_ATOM;
2451 new_atom->AtomicClassification = surrogate_atom->AtomicClassification;
2452
2453 new_atom->uuid_style = UUID_AP_SHA1_NAMESPACE;
2454
2455 } else {
2456 // determine the type of atom from our array of KnownAtoms; this is a worst
2457 // case scenario; it should be handled properly afterwards make sure the
2458 // level & the atom gets integrated into NextAtomNumber before
2459 // APar_MatchToKnownAtom because getting the fullpath will rely on that
2460 known_atom = APar_MatchToKnownAtom(
2461 surrogate_atom->AtomicName,
2462 (parent_atom == NULL ? "FILE_LEVEL" : parent_atom->AtomicName),
2463 false,
2464 NULL);
2465
2466 new_atom->AtomicContainerState = KnownAtoms[known_atom].container_state;
2467 new_atom->AtomicClassification = KnownAtoms[known_atom].box_type;
2468 }
2469 new_atom->ID32_TagInfo = NULL;
2470
2471 atom_number++;
2472
2473 return new_atom;
19652474 }
19662475
19672476 /*----------------------
19682477 APar_Unified_atom_Put
19692478 target_atom - pointer to the structure describing the atom we are setting
1970 unicode_data - a pointer to a string (possibly utf8 already); may go onto conversion to utf16 prior to the put
1971 text_tag_style - flag to denote that unicode_data is to become utf-16, or stay the flavors of utf8 (iTunes style, 3gp style...)
1972 ancillary_data - a (possibly cast) 32-bit number of any type of supplemental data to be set
1973 anc_bit_width - controls the number of bytes to set for ancillary data [0 to skip, 8 (1byte) - 32 (4 bytes)]
1974
1975 take any variety of data & tack it onto the malloced AtomicData at the next available spot (determined by its length)
1976 priority is given to the numerical ancillary_data so that language can be set prior to setting whatever unicode data. Finally, advance
1977 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])
1978 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)
1979
1980 works on iTunes-style & 3GP asset style but NOT binary safe (use APar_atom_Binary_Put)
1981 TODO: work past the max malloced amount onto a new larger array
2479 unicode_data - a pointer to a string (possibly utf8 already); may go onto
2480 conversion to utf16 prior to the put text_tag_style - flag to denote that
2481 unicode_data is to become utf-16, or stay the flavors of utf8 (iTunes style, 3gp
2482 style...) ancillary_data - a (possibly cast) 32-bit number of any type of
2483 supplemental data to be set anc_bit_width - controls the number of bytes to set
2484 for ancillary data [0 to skip, 8 (1byte) - 32 (4 bytes)]
2485
2486 take any variety of data & tack it onto the malloced AtomicData at the next
2487 available spot (determined by its length) priority is given to the numerical
2488 ancillary_data so that language can be set prior to setting whatever unicode
2489 data. Finally, advance the length of the atom so that we can tack onto the end
2490 repeated times (up to the max malloced amount - which isn't checked [blush]) if
2491 unicode_data is NULL itself, then only ancillary_data will be set - which is
2492 endian safe cuz o' bitshifting (or set 1 byte at a time)
2493
2494 works on iTunes-style & 3GP asset style but NOT binary safe (use
2495 APar_atom_Binary_Put)
2496 TODO: work past the max malloced amount onto a new larger array
19822497 ----------------------*/
1983 void APar_Unified_atom_Put(AtomicInfo* target_atom, const char* unicode_data,
1984 uint8_t text_tag_style, uint64_t ancillary_data, uint8_t anc_bit_width)
1985 {
1986 uint64_t atom_data_pos = 0;
1987 if (target_atom == NULL) {
1988 return;
1989 }
1990 if (target_atom->AtomicClassification == EXTENDED_ATOM) {
1991 if (target_atom->uuid_style == UUID_SHA1_NAMESPACE) atom_data_pos = target_atom->AtomicLength - 32;
1992 else if (target_atom->uuid_style == UUID_OTHER) atom_data_pos = target_atom->AtomicLength - 24;
1993 } else {
1994 atom_data_pos = target_atom->AtomicLength - 12;
1995 }
1996
1997 switch (anc_bit_width) {
1998 case 0 : { //aye, 'twas a false alarm; arg (I'm a pirate), we just wanted to set a text string
1999 break;
2000 }
2001
2002 case 8 : { //compilation, podcast flag, advisory
2003 target_atom->AtomicData[atom_data_pos] = (uint8_t)ancillary_data;
2004 target_atom->AtomicLength++;
2005 atom_data_pos++;
2006 break;
2007 }
2008
2009 case 16 : { //lang & its ilk
2010 target_atom->AtomicData[atom_data_pos] = (ancillary_data & 0xff00) >> 8;
2011 target_atom->AtomicData[atom_data_pos + 1] = (ancillary_data & 0xff) << 0;
2012 target_atom->AtomicLength+= 2;
2013 atom_data_pos+=2;
2014 break;
2015 }
2016
2017 case 32 : { //things like coordinates and.... stuff (ah, the prose)
2018 target_atom->AtomicData[atom_data_pos] = (ancillary_data & 0xff000000) >> 24;
2019 target_atom->AtomicData[atom_data_pos + 1] = (ancillary_data & 0xff0000) >> 16;
2020 target_atom->AtomicData[atom_data_pos + 2] = (ancillary_data & 0xff00) >> 8;
2021 target_atom->AtomicData[atom_data_pos + 3] = (ancillary_data & 0xff) << 0;
2022 target_atom->AtomicLength+= 4;
2023 atom_data_pos+=4;
2024 break;
2025 }
2026
2027 default : {
2028 break;
2029 }
2030 }
2031
2032 if (unicode_data != NULL) {
2033 if (text_tag_style == UTF16_3GP_Style) {
2034 uint32_t string_length = strlen(unicode_data) + 1;
2035 uint32_t glyphs_req_bytes = mbstowcs(NULL, unicode_data, string_length) * 2; //passing NULL pre-calculates the size of wchar_t needed;
2036
2037 unsigned char* utf16_conversion = (unsigned char*)calloc(1, sizeof(unsigned char)* string_length * 2 );
2038
2039 UTF8ToUTF16BE(utf16_conversion, glyphs_req_bytes, (unsigned char*)unicode_data, string_length);
2040
2041 target_atom->AtomicData[atom_data_pos] = (char)(0xFE); //BOM
2042 target_atom->AtomicData[atom_data_pos+1] = (char)(0xFF); //BOM
2043 atom_data_pos +=2; //BOM
2044
2045 /* copy the string directly onto AtomicData at the address of the start of AtomicData + the current length in atom_data_pos */
2046 /* in marked contrast to iTunes-style metadata where a string is a single string, 3gp tags like keyword & classification are more complex */
2047 /* directly putting the text into memory and being able to tack on more becomes a necessary accommodation */
2048 memcpy(target_atom->AtomicData + atom_data_pos, utf16_conversion, glyphs_req_bytes );
2049 target_atom->AtomicLength += glyphs_req_bytes;
2050
2051 //double check terminating NULL (don't want to double add them - blush.... or have them missing - blushing on the.... other side)
2052 if (target_atom->AtomicData[atom_data_pos + (glyphs_req_bytes -1)] + target_atom->AtomicData[atom_data_pos + glyphs_req_bytes] != 0) {
2053 target_atom->AtomicLength += 4; //+4 because add 2 bytes for the character we just found + 2bytes for the req. NULL
2054 }
2055 free(utf16_conversion); utf16_conversion = NULL;
2056
2057 } 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
2058 uint32_t binary_bytes = strlen(unicode_data);
2059 memcpy(target_atom->AtomicData + atom_data_pos, unicode_data, binary_bytes + 1 );
2060 target_atom->AtomicLength += binary_bytes;
2061
2062 } else {
2063 uint32_t total_bytes = 0;
2064
2065 if (text_tag_style == UTF8_3GP_Style) {
2066 total_bytes = strlen(unicode_data);
2067 total_bytes++; //include the terminating NULL
2068
2069 } else if (text_tag_style == UTF8_iTunesStyle_256glyphLimited) {
2070
2071 uint32_t raw_bytes = strlen(unicode_data);
2072 total_bytes = utf8_length(unicode_data, 256); //counts the number of characters, not bytes
2073
2074 if (raw_bytes > total_bytes && total_bytes > 255) {
2075
2076 fprintf(stdout, "AtomicParsley warning: %s was trimmed to 255 characters (%u characters over)\n",
2077 parsedAtoms[ APar_FindParentAtom(target_atom->AtomicNumber, target_atom->AtomicLevel) ].AtomicName,
2078 utf8_length(unicode_data+total_bytes, 0) );
2079 } else {
2080 total_bytes = raw_bytes;
2081 }
2082
2083 } else if (text_tag_style == UTF8_iTunesStyle_Unlimited) {
2084 total_bytes = strlen(unicode_data);
2085
2086 if (atom_data_pos + total_bytes > MAXDATA_PAYLOAD) {
2087 target_atom->AtomicData = (char*)realloc(target_atom->AtomicData, sizeof(char)* (atom_data_pos +total_bytes +1) );
2088 memset(target_atom->AtomicData + atom_data_pos, 0, total_bytes +1);
2089
2090 }
2091 }
2092
2093 //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
2094
2095 memcpy(target_atom->AtomicData + atom_data_pos, unicode_data, total_bytes + 1 );
2096 target_atom->AtomicLength += total_bytes;
2097 }
2098 }
2099 return;
2498 void APar_Unified_atom_Put(AtomicInfo *target_atom,
2499 const char *unicode_data,
2500 uint8_t text_tag_style,
2501 uint64_t ancillary_data,
2502 uint8_t anc_bit_width) {
2503 uint64_t atom_data_pos = 0;
2504 if (target_atom == NULL) {
2505 return;
2506 }
2507 if (target_atom->AtomicClassification == EXTENDED_ATOM) {
2508 if (target_atom->uuid_style == UUID_SHA1_NAMESPACE)
2509 atom_data_pos = target_atom->AtomicLength - 32;
2510 else if (target_atom->uuid_style == UUID_OTHER)
2511 atom_data_pos = target_atom->AtomicLength - 24;
2512 } else {
2513 atom_data_pos = target_atom->AtomicLength - 12;
2514 }
2515
2516 switch (anc_bit_width) {
2517 case 0: { // aye, 'twas a false alarm; arg (I'm a pirate), we just wanted to
2518 // set a text string
2519 break;
2520 }
2521
2522 case 8: { // compilation, podcast flag, advisory
2523 target_atom->AtomicData[atom_data_pos] = (uint8_t)ancillary_data;
2524 target_atom->AtomicLength++;
2525 atom_data_pos++;
2526 break;
2527 }
2528
2529 case 16: { // lang & its ilk
2530 target_atom->AtomicData[atom_data_pos] = (ancillary_data & 0xff00) >> 8;
2531 target_atom->AtomicData[atom_data_pos + 1] = (ancillary_data & 0xff) << 0;
2532 target_atom->AtomicLength += 2;
2533 atom_data_pos += 2;
2534 break;
2535 }
2536
2537 case 32: { // things like coordinates and.... stuff (ah, the prose)
2538 target_atom->AtomicData[atom_data_pos] =
2539 (ancillary_data & 0xff000000) >> 24;
2540 target_atom->AtomicData[atom_data_pos + 1] =
2541 (ancillary_data & 0xff0000) >> 16;
2542 target_atom->AtomicData[atom_data_pos + 2] = (ancillary_data & 0xff00) >> 8;
2543 target_atom->AtomicData[atom_data_pos + 3] = (ancillary_data & 0xff) << 0;
2544 target_atom->AtomicLength += 4;
2545 atom_data_pos += 4;
2546 break;
2547 }
2548
2549 default: {
2550 break;
2551 }
2552 }
2553
2554 if (unicode_data != NULL) {
2555 if (text_tag_style == UTF16_3GP_Style) {
2556 uint32_t string_length = strlen(unicode_data) + 1;
2557 uint32_t glyphs_req_bytes =
2558 mbstowcs(NULL, unicode_data, string_length) *
2559 2; // passing NULL pre-calculates the size of wchar_t needed;
2560
2561 unsigned char *utf16_conversion =
2562 (unsigned char *)calloc(1, sizeof(unsigned char) * string_length * 2);
2563
2564 UTF8ToUTF16BE(utf16_conversion,
2565 glyphs_req_bytes,
2566 (unsigned char *)unicode_data,
2567 string_length);
2568
2569 target_atom->AtomicData[atom_data_pos] = (char)(0xFE); // BOM
2570 target_atom->AtomicData[atom_data_pos + 1] = (char)(0xFF); // BOM
2571 atom_data_pos += 2; // BOM
2572
2573 /* copy the string directly onto AtomicData at the address of the start of
2574 * AtomicData + the current length in atom_data_pos */
2575 /* in marked contrast to iTunes-style metadata where a string is a single
2576 * string, 3gp tags like keyword & classification are more complex */
2577 /* directly putting the text into memory and being able to tack on more
2578 * becomes a necessary accommodation */
2579 memcpy(target_atom->AtomicData + atom_data_pos,
2580 utf16_conversion,
2581 glyphs_req_bytes);
2582 target_atom->AtomicLength += glyphs_req_bytes;
2583
2584 // double check terminating NULL (don't want to double add them -
2585 // blush.... or have them missing - blushing on the.... other side)
2586 if (target_atom->AtomicData[atom_data_pos + (glyphs_req_bytes - 1)] +
2587 target_atom->AtomicData[atom_data_pos + glyphs_req_bytes] !=
2588 0) {
2589 target_atom->AtomicLength +=
2590 4; //+4 because add 2 bytes for the character we just found + 2bytes
2591 // for the req. NULL
2592 }
2593 free(utf16_conversion);
2594 utf16_conversion = NULL;
2595
2596 } else if (text_tag_style ==
2597 UTF8_iTunesStyle_Binary) { // because this will be 'binary' data
2598 // (a misnomer for purl & egid),
2599 // memcpy 4 bytes into AtomicData, not
2600 // at the start of it
2601 uint32_t binary_bytes = strlen(unicode_data);
2602 memcpy(target_atom->AtomicData + atom_data_pos,
2603 unicode_data,
2604 binary_bytes + 1);
2605 target_atom->AtomicLength += binary_bytes;
2606
2607 } else {
2608 uint32_t total_bytes = 0;
2609
2610 if (text_tag_style == UTF8_3GP_Style) {
2611 total_bytes = strlen(unicode_data);
2612 total_bytes++; // include the terminating NULL
2613
2614 } else if (text_tag_style == UTF8_iTunesStyle_256glyphLimited) {
2615
2616 uint32_t raw_bytes = strlen(unicode_data);
2617 total_bytes = utf8_length(
2618 unicode_data, 256); // counts the number of characters, not bytes
2619
2620 if (raw_bytes > total_bytes && total_bytes > 255) {
2621
2622 fprintf(stdout,
2623 "AtomicParsley warning: %s was trimmed to 255 characters (%u "
2624 "characters over)\n",
2625 parsedAtoms[APar_FindParentAtom(target_atom->AtomicNumber,
2626 target_atom->AtomicLevel)]
2627 .AtomicName,
2628 utf8_length(unicode_data + total_bytes, 0));
2629 } else {
2630 total_bytes = raw_bytes;
2631 }
2632
2633 } else if (text_tag_style == UTF8_iTunesStyle_Unlimited) {
2634 total_bytes = strlen(unicode_data);
2635
2636 if (atom_data_pos + total_bytes > MAXDATA_PAYLOAD) {
2637 target_atom->AtomicData =
2638 (char *)realloc(target_atom->AtomicData,
2639 sizeof(char) * (atom_data_pos + total_bytes + 1));
2640 memset(target_atom->AtomicData + atom_data_pos, 0, total_bytes + 1);
2641 }
2642 }
2643
2644 // if we are setting iTunes-style metadata, add 0 to the pointer; for 3gp
2645 // user data atoms - add in the (length-default bare atom lenth): account
2646 // for language uint16_t (plus any other crap we will set); unicodeWin32
2647 // with wchar_t was converted right after program started, so do a direct
2648 // copy
2649
2650 memcpy(target_atom->AtomicData + atom_data_pos,
2651 unicode_data,
2652 total_bytes + 1);
2653 target_atom->AtomicLength += total_bytes;
2654 }
2655 }
2656 return;
21002657 }
21012658
21022659 /*----------------------
21042661 target_atom - pointer to the structure describing the atom we are setting
21052662 binary_data - a pointer to a string of binary data
21062663 bytecount - number of bytes to copy
2107 atomic_data_offset - place binary data some bytes offset from the start of AtomicData
2664 atomic_data_offset - place binary data some bytes offset from the start of
2665 AtomicData
21082666
21092667 Simple placement of binary data (perhaps containing NULLs) onto AtomicData.
2110 TODO: if over MAXDATA_PAYLOAD malloc a new char string
2668 TODO: if over MAXDATA_PAYLOAD malloc a new char string
21112669 ----------------------*/
2112 void APar_atom_Binary_Put(AtomicInfo* target_atom, const char* binary_data,
2113 uint32_t bytecount, uint64_t atomic_data_offset)
2114 {
2115 if (target_atom == NULL) return;
2116
2117 if (atomic_data_offset + bytecount + target_atom->AtomicLength <= MAXDATA_PAYLOAD) {
2118 memcpy(target_atom->AtomicData + atomic_data_offset, binary_data, bytecount );
2119 target_atom->AtomicLength += bytecount;
2120 } else {
2121 fprintf(stdout, "AtomicParsley warning: some data was longer than the allotted space and was skipped\n");
2122 }
2123 return;
2670 void APar_atom_Binary_Put(AtomicInfo *target_atom,
2671 const char *binary_data,
2672 uint32_t bytecount,
2673 uint64_t atomic_data_offset) {
2674 if (target_atom == NULL)
2675 return;
2676
2677 if (atomic_data_offset + bytecount + target_atom->AtomicLength <=
2678 MAXDATA_PAYLOAD) {
2679 memcpy(
2680 target_atom->AtomicData + atomic_data_offset, binary_data, bytecount);
2681 target_atom->AtomicLength += bytecount;
2682 } else {
2683 fprintf(stdout,
2684 "AtomicParsley warning: some data was longer than the "
2685 "allotted space and was skipped\n");
2686 }
2687 return;
21242688 }
21252689
21262690 /*----------------------
21272691 APar_Verify__udta_meta_hdlr__atom
21282692
2129 only test if the atom is present for now, it will be created just before writeout time - to insure it only happens once.
2693 only test if the atom is present for now, it will be created just before
2694 writeout time - to insure it only happens once.
21302695 ----------------------*/
21312696 void APar_Verify__udta_meta_hdlr__atom() {
2132 bool Create__udta_meta_hdlr__atom = false;
2133
2134 if (metadata_style == ITUNES_STYLE && hdlrAtom == NULL) {
2135 hdlrAtom = APar_FindAtom("moov.udta.meta.hdlr", false, VERSIONED_ATOM, 0);
2136 if (hdlrAtom == NULL) {
2137 Create__udta_meta_hdlr__atom = true;
2138 }
2139 }
2140 if (Create__udta_meta_hdlr__atom ) {
2141
2142 //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
2143 //this "moov.udta.meta.hdlr" atom (and its data), it refuses to let any information be changed & the dreaded "Album Artwork Not Modifiable"
2144 //shows up. It's because this atom is missing. Oddly, QT Player can see the info, but this only works for mp4/m4a files.
2145
2146 hdlrAtom = APar_FindAtom("moov.udta.meta.hdlr", true, VERSIONED_ATOM, 0);
2147
2148 APar_MetaData_atom_QuickInit(hdlrAtom->AtomicNumber, 0, 0);
2149 APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0x6D646972, 32); //'mdir'
2150 APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0x6170706C, 32); //'appl'
2151 APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
2152 APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
2153 APar_Unified_atom_Put(hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
2154 }
2155 return;
2697 bool Create__udta_meta_hdlr__atom = false;
2698
2699 if (metadata_style == ITUNES_STYLE && hdlrAtom == NULL) {
2700 hdlrAtom = APar_FindAtom("moov.udta.meta.hdlr", false, VERSIONED_ATOM, 0);
2701 if (hdlrAtom == NULL) {
2702 Create__udta_meta_hdlr__atom = true;
2703 }
2704 }
2705 if (Create__udta_meta_hdlr__atom) {
2706
2707 // if Quicktime (Player at the least) is used to create any type of mp4
2708 // file, the entire udta hierarchy is missing. If iTunes doesn't find this
2709 // "moov.udta.meta.hdlr" atom (and its data), it refuses to let any
2710 // information be changed & the dreaded "Album Artwork Not Modifiable" shows
2711 // up. It's because this atom is missing. Oddly, QT Player can see the info,
2712 // but this only works for mp4/m4a files.
2713
2714 hdlrAtom = APar_FindAtom("moov.udta.meta.hdlr", true, VERSIONED_ATOM, 0);
2715
2716 APar_MetaData_atom_QuickInit(hdlrAtom->AtomicNumber, 0, 0);
2717 APar_Unified_atom_Put(hdlrAtom,
2718 NULL,
2719 UTF8_iTunesStyle_256glyphLimited,
2720 0x6D646972,
2721 32); //'mdir'
2722 APar_Unified_atom_Put(hdlrAtom,
2723 NULL,
2724 UTF8_iTunesStyle_256glyphLimited,
2725 0x6170706C,
2726 32); //'appl'
2727 APar_Unified_atom_Put(
2728 hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
2729 APar_Unified_atom_Put(
2730 hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
2731 APar_Unified_atom_Put(
2732 hdlrAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
2733 }
2734 return;
21562735 }
21572736
21582737 /*----------------------
21592738 APar_MetaData_atomGenre_Set
2160 atomPayload - the desired string value of the genre
2161
2162 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
2163 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
2164 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
2165 create the new genre atom and put the data manually onto the atom.
2739 atomPayload - the desired string value of the genre
2740
2741 genre is special in that it gets carried on 2 atoms. A standard genre (as
2742 listed in ID3v1GenreList) is represented as a number on a 'gnre' atom any value
2743 other than those, and the genre is placed as a string onto a '©gen' atom. Only
2744 one or the other can be present. So if atomPayload is a non-NULL value, first
2745 try and match the genre into the ID3v1GenreList standard genres. Try to remove
2746 the other type of genre atom, then find or create the new genre atom and put the
2747 data manually onto the atom.
21662748 ----------------------*/
2167 void APar_MetaData_atomGenre_Set(const char* atomPayload) {
2168 if (metadata_style == ITUNES_STYLE) {
2169 const char* standard_genre_atom = "moov.udta.meta.ilst.gnre";
2170 const char* std_genre_data_atom = "moov.udta.meta.ilst.gnre.data";
2171 const char* custom_genre_atom = "moov.udta.meta.ilst.©gen";
2172 const char* cstm_genre_data_atom = "moov.udta.meta.ilst.©gen.data";
2173
2174 if ( strlen(atomPayload) == 0) {
2175 APar_RemoveAtom(std_genre_data_atom, VERSIONED_ATOM, 0); //find the atom; don't create if it's "" to remove
2176 APar_RemoveAtom(cstm_genre_data_atom, VERSIONED_ATOM, 0); //find the atom; don't create if it's "" to remove
2177 } else {
2178
2179 uint8_t genre_number = StringGenreToInt(atomPayload);
2180 AtomicInfo* genreAtom;
2181
2182 APar_Verify__udta_meta_hdlr__atom();
2183 modified_atoms = true;
2184
2185 if (genre_number != 0) {
2186 //first find if a custom genre atom ("©gen") exists; erase the custom-string genre atom in favor of the standard genre atom
2187
2188 AtomicInfo* verboten_genre_atom = APar_FindAtom(custom_genre_atom, false, SIMPLE_ATOM, 0);
2189
2190 if (verboten_genre_atom != NULL) {
2191 if (strlen(verboten_genre_atom->AtomicName) > 0) {
2192 if (strncmp(verboten_genre_atom->AtomicName, "©gen", 4) == 0) {
2193 APar_RemoveAtom(cstm_genre_data_atom, VERSIONED_ATOM, 0);
2194 }
2195 }
2196 }
2197
2198 genreAtom = APar_FindAtom(std_genre_data_atom, true, VERSIONED_ATOM, 0);
2199 APar_MetaData_atom_QuickInit(genreAtom->AtomicNumber, AtomFlags_Data_Binary, 0);
2200 APar_Unified_atom_Put(genreAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 8);
2201 APar_Unified_atom_Put(genreAtom, NULL, UTF8_iTunesStyle_256glyphLimited, genre_number, 8);
2202
2203 } else {
2204
2205 AtomicInfo* verboten_genre_atom = APar_FindAtom(standard_genre_atom, false, SIMPLE_ATOM, 0);
2206
2207 if (verboten_genre_atom != NULL) {
2208 if (verboten_genre_atom->AtomicNumber > 5 && verboten_genre_atom->AtomicNumber < atom_number) {
2209 if (strncmp(verboten_genre_atom->AtomicName, "gnre", 4) == 0) {
2210 APar_RemoveAtom(std_genre_data_atom, VERSIONED_ATOM, 0);
2211 }
2212 }
2213 }
2214 genreAtom = APar_FindAtom(cstm_genre_data_atom, true, VERSIONED_ATOM, 0);
2215 APar_MetaData_atom_QuickInit(genreAtom->AtomicNumber, AtomFlags_Data_Text, 0);
2216 APar_Unified_atom_Put(genreAtom, atomPayload, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2217 }
2218 }
2219 APar_FlagMovieHeader();
2220 } //end if (metadata_style == ITUNES_STYLE)
2221 return;
2749 void APar_MetaData_atomGenre_Set(const char *atomPayload) {
2750 if (metadata_style == ITUNES_STYLE) {
2751 const char *standard_genre_atom = "moov.udta.meta.ilst.gnre";
2752 const char *std_genre_data_atom = "moov.udta.meta.ilst.gnre.data";
2753 const char *custom_genre_atom = "moov.udta.meta.ilst.©gen";
2754 const char *cstm_genre_data_atom = "moov.udta.meta.ilst.©gen.data";
2755
2756 if (strlen(atomPayload) == 0) {
2757 APar_RemoveAtom(std_genre_data_atom,
2758 VERSIONED_ATOM,
2759 0); // find the atom; don't create if it's "" to remove
2760 APar_RemoveAtom(cstm_genre_data_atom,
2761 VERSIONED_ATOM,
2762 0); // find the atom; don't create if it's "" to remove
2763 } else {
2764
2765 uint8_t genre_number = StringGenreToInt(atomPayload);
2766 AtomicInfo *genreAtom;
2767
2768 APar_Verify__udta_meta_hdlr__atom();
2769 modified_atoms = true;
2770
2771 if (genre_number != 0) {
2772 // first find if a custom genre atom ("©gen") exists; erase the
2773 // custom-string genre atom in favor of the standard genre atom
2774
2775 AtomicInfo *verboten_genre_atom =
2776 APar_FindAtom(custom_genre_atom, false, SIMPLE_ATOM, 0);
2777
2778 if (verboten_genre_atom != NULL) {
2779 if (strlen(verboten_genre_atom->AtomicName) > 0) {
2780 if (strncmp(verboten_genre_atom->AtomicName, "©gen", 4) == 0) {
2781 APar_RemoveAtom(cstm_genre_data_atom, VERSIONED_ATOM, 0);
2782 }
2783 }
2784 }
2785
2786 genreAtom = APar_FindAtom(std_genre_data_atom, true, VERSIONED_ATOM, 0);
2787 APar_MetaData_atom_QuickInit(
2788 genreAtom->AtomicNumber, AtomFlags_Data_Binary, 0);
2789 APar_Unified_atom_Put(
2790 genreAtom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 8);
2791 APar_Unified_atom_Put(
2792 genreAtom, NULL, UTF8_iTunesStyle_256glyphLimited, genre_number, 8);
2793
2794 } else {
2795
2796 AtomicInfo *verboten_genre_atom =
2797 APar_FindAtom(standard_genre_atom, false, SIMPLE_ATOM, 0);
2798
2799 if (verboten_genre_atom != NULL) {
2800 if (verboten_genre_atom->AtomicNumber > 5 &&
2801 verboten_genre_atom->AtomicNumber < atom_number) {
2802 if (strncmp(verboten_genre_atom->AtomicName, "gnre", 4) == 0) {
2803 APar_RemoveAtom(std_genre_data_atom, VERSIONED_ATOM, 0);
2804 }
2805 }
2806 }
2807 genreAtom =
2808 APar_FindAtom(cstm_genre_data_atom, true, VERSIONED_ATOM, 0);
2809 APar_MetaData_atom_QuickInit(
2810 genreAtom->AtomicNumber, AtomFlags_Data_Text, 0);
2811 APar_Unified_atom_Put(
2812 genreAtom, atomPayload, UTF8_iTunesStyle_256glyphLimited, 0, 0);
2813 }
2814 }
2815 APar_FlagMovieHeader();
2816 } // end if (metadata_style == ITUNES_STYLE)
2817 return;
22222818 }
22232819
22242820 /*----------------------
22252821 APar_MetaData_atomLyrics_Set
2226 lyricsPath - the path that was provided to a (hopefully) existent txt file
2227
2228 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).
2822 lyricsPath - the path that was provided to a (hopefully) existent txt
2823 file
2824
2825 lyrics will be read from a file because they can contain multiple lines.
2826 Lines are stored with old Mac-style line endings (single carriage return).
22292827 ----------------------*/
2230 void APar_MetaData_atomLyrics_Set(const char* lyricsPath) {
2231 if (metadata_style == ITUNES_STYLE) {
2232 TestFileExistence(lyricsPath, true);
2233 uint64_t file_len = findFileSize(lyricsPath);
2234
2235 APar_Verify__udta_meta_hdlr__atom();
2236 modified_atoms = true;
2237
2238 AtomicInfo* lyricsData_atom = APar_FindAtom("moov.udta.meta.ilst.©lyr.data", true, VERSIONED_ATOM, 0);
2239 APar_MetaData_atom_QuickInit(lyricsData_atom->AtomicNumber, AtomFlags_Data_Text, 0, file_len + 1);
2240
2241 FILE* lyrics_file = APar_OpenFile(lyricsPath, "rb");
2242 uint64_t remaining = file_len;
2243 char* dest = lyricsData_atom->AtomicData + 4;
2244 char* sol;
2245 while (remaining && (sol = fgets(dest, remaining + 1, lyrics_file))) {
2246 size_t len = strlen(sol); //NUL bytes in the file will cause parts of the text to be skipped
2247 //normalize different EOL styles to carriage returns
2248 if (sol[len-1] == '\n') {
2249 if (sol[len-2] == '\r') sol[--len] = '\0';
2250 else sol[len-1] = '\r';
2251 }
2252 remaining -= len;
2253 dest += len;
2254 }
2255 lyricsData_atom->AtomicLength += dest - (lyricsData_atom->AtomicData + 4);
2256 fclose(lyrics_file);
2257
2258 APar_FlagMovieHeader();
2259 } //end if (metadata_style == ITUNES_STYLE)
2260 return;
2828 void APar_MetaData_atomLyrics_Set(const char *lyricsPath) {
2829 if (metadata_style == ITUNES_STYLE) {
2830 TestFileExistence(lyricsPath, true);
2831 uint64_t file_len = findFileSize(lyricsPath);
2832
2833 APar_Verify__udta_meta_hdlr__atom();
2834 modified_atoms = true;
2835
2836 AtomicInfo *lyricsData_atom =
2837 APar_FindAtom("moov.udta.meta.ilst.©lyr.data", true, VERSIONED_ATOM, 0);
2838 APar_MetaData_atom_QuickInit(
2839 lyricsData_atom->AtomicNumber, AtomFlags_Data_Text, 0, file_len + 1);
2840
2841 FILE *lyrics_file = APar_OpenFile(lyricsPath, "rb");
2842 uint64_t remaining = file_len;
2843 char *dest = lyricsData_atom->AtomicData + 4;
2844 char *sol;
2845 while (remaining && (sol = fgets(dest, remaining + 1, lyrics_file))) {
2846 size_t len = strlen(sol); // NUL bytes in the file will cause parts of the
2847 // text to be skipped
2848 // normalize different EOL styles to carriage returns
2849 if (sol[len - 1] == '\n') {
2850 if (sol[len - 2] == '\r')
2851 sol[--len] = '\0';
2852 else
2853 sol[len - 1] = '\r';
2854 }
2855 remaining -= len;
2856 dest += len;
2857 }
2858 lyricsData_atom->AtomicLength += dest - (lyricsData_atom->AtomicData + 4);
2859 fclose(lyrics_file);
2860
2861 APar_FlagMovieHeader();
2862 } // end if (metadata_style == ITUNES_STYLE)
2863 return;
22612864 }
22622865
22632866 /*----------------------
22642867 APar_MetaData_atomArtwork_Init
2265 atom_num - the AtomicNumber of the atom in the parsedAtoms array (probably newly created)
2266 artworkPath - the path that was provided on a (hopefully) existant jpg/png file
2267
2268 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
2269 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
2270 copied onto the atom.
2868 atom_num - the AtomicNumber of the atom in the parsedAtoms array
2869 (probably newly created) artworkPath - the path that was provided on a
2870 (hopefully) existant jpg/png file
2871
2872 artwork will be inited differently because we need to test a) that the file
2873 exists and b) get its size in bytes. This info will be used at the size of the
2874 'data' atom under 'covr' - and the path will be carried on AtomicData until
2875 write-out time, when the binary contents of the original will be copied onto the
2876 atom.
22712877 ----------------------*/
2272 void APar_MetaData_atomArtwork_Init(short atom_num, const char* artworkPath) {
2273 TestFileExistence(artworkPath, true);
2274 uint64_t picture_size = findFileSize(artworkPath);
2275
2276 if (picture_size > 0) {
2277 APar_MetaData_atom_QuickInit(atom_num, APar_TestArtworkBinaryData(artworkPath), 0, picture_size );
2278 FILE* artfile = APar_OpenFile(artworkPath, "rb");
2279 uint32_t bytes_read = APar_ReadFile(parsedAtoms[atom_num].AtomicData + 4, artfile, picture_size); //+4 for the 4 null bytes
2280 if (bytes_read > 0) parsedAtoms[atom_num].AtomicLength += bytes_read;
2281 fclose(artfile);
2282 }
2283 return;
2878 void APar_MetaData_atomArtwork_Init(short atom_num, const char *artworkPath) {
2879 TestFileExistence(artworkPath, true);
2880 uint64_t picture_size = findFileSize(artworkPath);
2881
2882 if (picture_size > 0) {
2883 APar_MetaData_atom_QuickInit(
2884 atom_num, APar_TestArtworkBinaryData(artworkPath), 0, picture_size);
2885 FILE *artfile = APar_OpenFile(artworkPath, "rb");
2886 uint32_t bytes_read = APar_ReadFile(parsedAtoms[atom_num].AtomicData + 4,
2887 artfile,
2888 picture_size); //+4 for the 4 null bytes
2889 if (bytes_read > 0)
2890 parsedAtoms[atom_num].AtomicLength += bytes_read;
2891 fclose(artfile);
2892 }
2893 return;
22842894 }
22852895
22862896 /*----------------------
22872897 APar_MetaData_atomArtwork_Set
2288 artworkPath - the path that was provided on a (hopefully) existant jpg/png file
2289 env_PicOptions - picture embedding preferences from a 'export PIC_OPTIONS=foo' setting
2290
2291 artwork gets stored under a single 'covr' atom, but with many 'data' atoms - each 'data' atom contains the binary data for each picture.
2292 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
2293 features on the image. The path of the file (either original, modified artwork, or both) are returned to use for possible atom creation
2898 artworkPath - the path that was provided on a (hopefully) existant
2899 jpg/png file env_PicOptions - picture embedding preferences from a 'export
2900 PIC_OPTIONS=foo' setting
2901
2902 artwork gets stored under a single 'covr' atom, but with many 'data' atoms -
2903 each 'data' atom contains the binary data for each picture. When the 'covr' atom
2904 is found, we create a sparse atom at the end of the existing 'data' atoms, and
2905 then perform any of the image manipulation features on the image. The path of
2906 the file (either original, modified artwork, or both) are returned to use for
2907 possible atom creation
22942908 ----------------------*/
2295 void APar_MetaData_atomArtwork_Set(const char* artworkPath, char* env_PicOptions) {
2296 if (metadata_style == ITUNES_STYLE) {
2297 const char* artwork_atom = "moov.udta.meta.ilst.covr";
2298 if (strcmp(artworkPath, "REMOVE_ALL") == 0) {
2299 APar_RemoveAtom(artwork_atom, SIMPLE_ATOM, 0);
2300
2301 } else {
2302 APar_Verify__udta_meta_hdlr__atom();
2303
2304 modified_atoms = true;
2305 AtomicInfo* desiredAtom = APar_FindAtom(artwork_atom, true, SIMPLE_ATOM, 0);
2306 AtomicInfo sample_data_atom = { 0 };
2307
2308 #if defined (DARWIN_PLATFORM)
2309 // used on Darwin adding a 2nd image (the original)
2310 short parent_atom = desiredAtom->AtomicNumber;
2909 void APar_MetaData_atomArtwork_Set(const char *artworkPath,
2910 char *env_PicOptions) {
2911 if (metadata_style == ITUNES_STYLE) {
2912 const char *artwork_atom = "moov.udta.meta.ilst.covr";
2913 if (strcmp(artworkPath, "REMOVE_ALL") == 0) {
2914 APar_RemoveAtom(artwork_atom, SIMPLE_ATOM, 0);
2915
2916 } else {
2917 APar_Verify__udta_meta_hdlr__atom();
2918
2919 modified_atoms = true;
2920 AtomicInfo *desiredAtom =
2921 APar_FindAtom(artwork_atom, true, SIMPLE_ATOM, 0);
2922 AtomicInfo sample_data_atom = {0};
2923
2924 #if defined(__APPLE__)
2925 // used on Darwin adding a 2nd image (the original)
2926 short parent_atom = desiredAtom->AtomicNumber;
23112927 #endif
23122928
2313 APar_CreateSurrogateAtom(&sample_data_atom, "data", 6, VERSIONED_ATOM, 0, NULL, 0);
2314 desiredAtom = APar_CreateSparseAtom(&sample_data_atom, desiredAtom, APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber) );
2315
2316 #if defined (DARWIN_PLATFORM)
2317 //determine if any picture preferences will impact the picture file in any way
2318 myPicturePrefs = APar_ExtractPicPrefs(env_PicOptions);
2319
2320 char* resized_filepath = (char*)calloc(1, sizeof(char)*MAXPATHLEN+1);
2321
2322 if ( ResizeGivenImage(artworkPath , myPicturePrefs, resized_filepath) ) {
2323 APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, resized_filepath);
2324
2325 if (myPicturePrefs.addBOTHpix) {
2326 //create another sparse atom to hold the new image data
2327 desiredAtom = APar_CreateSparseAtom(&sample_data_atom, desiredAtom, APar_FindLastChild_of_ParentAtom(parent_atom) );
2328 APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath);
2329 if (myPicturePrefs.removeTempPix) remove(resized_filepath);
2330 }
2331 } else {
2332 APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath);
2333 }
2334 free(resized_filepath);
2335 resized_filepath=NULL;
2929 APar_CreateSurrogateAtom(
2930 &sample_data_atom, "data", 6, VERSIONED_ATOM, 0, NULL, 0);
2931 desiredAtom = APar_CreateSparseAtom(
2932 &sample_data_atom,
2933 desiredAtom,
2934 APar_FindLastChild_of_ParentAtom(desiredAtom->AtomicNumber));
2935
2936 #if defined(__APPLE__)
2937 // determine if any picture preferences will impact the picture file in
2938 // any way
2939 myPicturePrefs = APar_ExtractPicPrefs(env_PicOptions);
2940
2941 size_t resized_filepath_len = MAXPATHLEN + 1;
2942 char *resized_filepath = (char *)calloc(1, resized_filepath_len);
2943
2944 if (ResizeGivenImage(artworkPath,
2945 myPicturePrefs,
2946 resized_filepath,
2947 resized_filepath_len)) {
2948 APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber,
2949 resized_filepath);
2950
2951 if (myPicturePrefs.addBOTHpix) {
2952 // create another sparse atom to hold the new image data
2953 desiredAtom = APar_CreateSparseAtom(
2954 &sample_data_atom,
2955 desiredAtom,
2956 APar_FindLastChild_of_ParentAtom(parent_atom));
2957 APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber,
2958 artworkPath);
2959 if (myPicturePrefs.removeTempPix)
2960 remove(resized_filepath);
2961 }
2962 } else {
2963 APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath);
2964 }
2965 free(resized_filepath);
2966 resized_filepath = NULL;
23362967 #else
2337 //perhaps some libjpeg based resizing/modification for non-Mac OS X based platforms
2338 APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath);
2968 // perhaps some libjpeg based resizing/modification for non-Mac OS X based
2969 // platforms
2970 APar_MetaData_atomArtwork_Init(desiredAtom->AtomicNumber, artworkPath);
23392971 #endif
2340 }
2341 APar_FlagMovieHeader();
2342 } ////end if (metadata_style == ITUNES_STYLE)
2343 return;
2972 }
2973 APar_FlagMovieHeader();
2974 } ////end if (metadata_style == ITUNES_STYLE)
2975 return;
23442976 }
23452977
23462978 /*----------------------
23472979 APar_sprintf_atompath
2348 dest_path - the destination string that will hold the final path
2349 target_atom_name - the name of the atom that will be used to fill the %s portion of the tokenized path
2350 track_index - the index into the trak[X] that needs to be found; fills the %u portion of the tokenized path
2351 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)
2352 dest_len - the amount of malloc'ed bytes for dest_path
2353
2354 Fill a destinaiton path with the fully expressed atom path taking track indexes into consideration
2980 dest_path - the destination string that will hold the final path
2981 target_atom_name - the name of the atom that will be used to fill the %s
2982 portion of the tokenized path track_index - the index into the trak[X] that
2983 needs to be found; fills the %u portion of the tokenized path udta_container -
2984 the area in which 'udta' will be: either movie level (the easiest) or track
2985 level (which requires an index into a specific 'trak' atom) dest_len - the
2986 amount of malloc'ed bytes for dest_path
2987
2988 Fill a destinaiton path with the fully expressed atom path taking track
2989 indexes into consideration
23552990 ----------------------*/
2356 void APar_sprintf_atompath(char* dest_path, const char* target_atom_name, uint8_t track_index, uint8_t udta_container, uint16_t dest_len) {
2357 memset(dest_path, 0, dest_len);
2358 if (udta_container == MOVIE_LEVEL_ATOM) {
2359 memcpy(dest_path, "moov.udta.", 10);
2360 memcpy(dest_path +10, target_atom_name, 4);
2361 } else {
2362 sprintf(dest_path, "moov.trak[%u].udta.%s", track_index, target_atom_name);
2363 }
2364 return;
2991 void APar_sprintf_atompath(char *dest_path,
2992 const char *target_atom_name,
2993 uint8_t track_index,
2994 uint8_t udta_container,
2995 uint16_t dest_len) {
2996 memset(dest_path, 0, dest_len);
2997 if (udta_container == MOVIE_LEVEL_ATOM) {
2998 memcpy(dest_path, "moov.udta.", 10);
2999 memcpy(dest_path + 10, target_atom_name, 4);
3000 } else {
3001 sprintf(dest_path, "moov.trak[%u].udta.%s", track_index, target_atom_name);
3002 }
3003 return;
23653004 }
23663005
23673006 /*----------------------
23683007 APar_3GP_Keyword_atom_Format
2369 keywords_globbed - the globbed string of keywords ('foo1,foo2,foo_you')
2370 keyword_count - count of keywords in the above globbed string
2371 set_UTF16_text - whether to encode as utf16
2372 formed_keyword_struct - the char string that will hold the converted keyword struct (manually formatted)
2373
2374 3gp keywords are a little more complicated. Since they will be entered separated by some form of punctuation, they need to be separated out
2375 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
2376 And each keyword needs to be NULL terminated. Technically it would be possible to even have mixed encodings (not supported here).
3008 keywords_globbed - the globbed string of keywords ('foo1,foo2,foo_you')
3009 keyword_count - count of keywords in the above globbed string
3010 set_UTF16_text - whether to encode as utf16
3011 formed_keyword_struct - the char string that will hold the converted
3012 keyword struct (manually formatted)
3013
3014 3gp keywords are a little more complicated. Since they will be entered
3015 separated by some form of punctuation, they need to be separated out They also
3016 will possibly be converted to utf16 - and they NEED to start with the BOM then.
3017 Prefacing each keyword is the 8bit length of the string And each keyword needs
3018 to be NULL terminated. Technically it would be possible to even have mixed
3019 encodings (not supported here).
23773020 ----------------------*/
2378 uint32_t APar_3GP_Keyword_atom_Format(char* keywords_globbed, uint8_t keyword_count, bool set_UTF16_text, char* &formed_keyword_struct) {
2379 uint64_t formed_string_offset = 0;
2380 uint64_t string_len = 0;
2381
2382 char* a_keyword = strsep(&keywords_globbed,",");
2383
2384 for (uint8_t i=1; i <= keyword_count; i++) {
2385 string_len = strlen(a_keyword);
2386 if (set_UTF16_text) {
2387
2388 uint32_t glyphs_req_bytes = mbstowcs(NULL, a_keyword, string_len+1) * 2; //passing NULL pre-calculates the size of wchar_t needed;
2389
2390 formed_keyword_struct[formed_string_offset+1] = (char)(0xFE); //BOM
2391 formed_keyword_struct[formed_string_offset+2] = (char)(0xFF); //BOM
2392 formed_string_offset+=3; //BOM + keyword length that has yet to be set
2393
2394 int bytes_converted = UTF8ToUTF16BE((unsigned char*)(formed_keyword_struct + formed_string_offset), glyphs_req_bytes, (unsigned char*)a_keyword, string_len);
2395
2396 if (bytes_converted > 1) {
2397 formed_keyword_struct[formed_string_offset-3] = (uint8_t)bytes_converted + 4; //keyword length is NOW set
2398 formed_string_offset += bytes_converted + 2; //NULL terminator
2399 }
2400 } else {
2401
2402 uint32_t string_len = strlen(a_keyword);
2403 formed_keyword_struct[formed_string_offset] = (uint8_t)string_len + 1; //add the terminating NULL
2404 formed_string_offset++;
2405 memcpy(formed_keyword_struct + formed_string_offset, a_keyword, string_len );
2406 formed_string_offset+= (string_len +1);
2407 }
2408 a_keyword = strsep(&keywords_globbed,",");
2409 }
2410 return formed_string_offset;
3021 uint32_t APar_3GP_Keyword_atom_Format(char *keywords_globbed,
3022 uint8_t keyword_count,
3023 bool set_UTF16_text,
3024 char *&formed_keyword_struct) {
3025 uint64_t formed_string_offset = 0;
3026 uint64_t string_len = 0;
3027
3028 char *a_keyword = strsep(&keywords_globbed, ",");
3029
3030 for (uint8_t i = 1; i <= keyword_count; i++) {
3031 string_len = strlen(a_keyword);
3032 if (set_UTF16_text) {
3033
3034 uint32_t glyphs_req_bytes =
3035 mbstowcs(NULL, a_keyword, string_len + 1) *
3036 2; // passing NULL pre-calculates the size of wchar_t needed;
3037
3038 formed_keyword_struct[formed_string_offset + 1] = (char)(0xFE); // BOM
3039 formed_keyword_struct[formed_string_offset + 2] = (char)(0xFF); // BOM
3040 formed_string_offset += 3; // BOM + keyword length that has yet to be set
3041
3042 int bytes_converted = UTF8ToUTF16BE(
3043 (unsigned char *)(formed_keyword_struct + formed_string_offset),
3044 glyphs_req_bytes,
3045 (unsigned char *)a_keyword,
3046 string_len);
3047
3048 if (bytes_converted > 1) {
3049 formed_keyword_struct[formed_string_offset - 3] =
3050 (uint8_t)bytes_converted + 4; // keyword length is NOW set
3051 formed_string_offset += bytes_converted + 2; // NULL terminator
3052 }
3053 } else {
3054
3055 uint32_t string_len = strlen(a_keyword);
3056 formed_keyword_struct[formed_string_offset] =
3057 (uint8_t)string_len + 1; // add the terminating NULL
3058 formed_string_offset++;
3059 memcpy(
3060 formed_keyword_struct + formed_string_offset, a_keyword, string_len);
3061 formed_string_offset += (string_len + 1);
3062 }
3063 a_keyword = strsep(&keywords_globbed, ",");
3064 }
3065 return formed_string_offset;
24113066 }
24123067
24133068 /*----------------------
24143069 APar_uuid_atom_Init
2415 atom_path - the parent hierarchy of the desired atom (with the location of the specific uuid atom supplied as '=%s')
2416 uuidName - the name of the atom (possibly provided in a forbidden utf8 - only latin1 aka iso8859 is acceptable)
2417 dataType - for now text is only supported; really its atom version/flags as used by iTunes
2418 uuidValue - the string that will get embedded onto the atom
2419 shellAtom - flag to denote whether the atom may possibly come as utf8 encoded
2420
2421 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
2422 that created it, any information carried by a uuid is arbitrary, and cannot be guaranteed by a non-originating utility. In AtomicParsley uuid atoms,
2423 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
2424 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
2425 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
2426 acutal uuidpath itself. Once created however, placing information on it is very much like any other atom - done via APar_Unified_atom_Put
2427
2428 //4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data
3070 atom_path - the parent hierarchy of the desired atom (with the location
3071 of the specific uuid atom supplied as '=%s') uuidName - the name of the atom
3072 (possibly provided in a forbidden utf8 - only latin1 aka iso8859 is acceptable)
3073 dataType - for now text is only supported; really its atom version/flags
3074 as used by iTunes uuidValue - the string that will get embedded onto the atom
3075 shellAtom - flag to denote whether the atom may possibly come as utf8
3076 encoded
3077
3078 uuid atoms are user-supplied/user-defined atoms that allow for extended
3079 tagging support. Because a uuid atom is malleable, and defined by the utility
3080 that created it, any information carried by a uuid is arbitrary,
3081 and cannot be guaranteed by a non-originating utility. In AtomicParsley uuid
3082 atoms, the data is presented much like an iTunes-style atom - except that the
3083 information gets carried directly on the uuid atom - no 'data' child atom
3084 exists. A uuid atom is a special longer type of traditional
3085 atom. As a traditional atom, it name is 'uuid' - and the 4 bytes after that
3086 represent its uuid name. Because the data will be directly sitting on the atom,
3087 a different means of finding these atoms exists, as well as creating the acutal
3088 uuidpath itself. Once created however, placing information on it is very much
3089 like any other atom - done via APar_Unified_atom_Put
3090
3091 //4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes
3092 name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data
24293093 ----------------------*/
2430 AtomicInfo* APar_uuid_atom_Init(const char* atom_path, const char* uuidName, const uint32_t dataType, const char* uuidValue, bool shellAtom) {
2431 AtomicInfo* desiredAtom = NULL;
2432 char uuid_path[256];
2433 char uuid_binary_str[20];
2434 char uuid_4char_name[10];
2435 memset(uuid_path, 0, sizeof(uuid_path));
2436 memset(uuid_binary_str, 0, sizeof(uuid_binary_str));
2437 memset(uuid_4char_name, 0, sizeof(uuid_4char_name));
2438 uint16_t path_len = 0;
2439
2440 if (shellAtom) {
2441 UTF8Toisolat1((unsigned char*)&uuid_4char_name, 4, (unsigned char*)uuidName, strlen(uuidName) );
2442 } else {
2443 memcpy(uuid_4char_name, uuidName, 4);
2444 }
2445
2446 APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str);
2447 APar_endian_uuid_bin_str_conversion(uuid_binary_str);
2448
2449 //this will only append (and knock off) %s (anything) at the end of a string
2450 path_len = strlen(atom_path);
2451 memcpy(uuid_path, atom_path, path_len-2);
2452 memcpy(uuid_path + (path_len-2), uuid_binary_str, 16);
3094 AtomicInfo *APar_uuid_atom_Init(const char *atom_path,
3095 const char *uuidName,
3096 const uint32_t dataType,
3097 const char *uuidValue,
3098 bool shellAtom) {
3099 AtomicInfo *desiredAtom = NULL;
3100 char uuid_path[256];
3101 char uuid_binary_str[20];
3102 char uuid_4char_name[10];
3103 memset(uuid_path, 0, sizeof(uuid_path));
3104 memset(uuid_binary_str, 0, sizeof(uuid_binary_str));
3105 memset(uuid_4char_name, 0, sizeof(uuid_4char_name));
3106 uint16_t path_len = 0;
3107
3108 if (shellAtom) {
3109 UTF8Toisolat1((unsigned char *)&uuid_4char_name,
3110 4,
3111 (unsigned char *)uuidName,
3112 strlen(uuidName));
3113 } else {
3114 memcpy(uuid_4char_name, uuidName, 4);
3115 }
3116
3117 APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str);
3118 APar_endian_uuid_bin_str_conversion(uuid_binary_str);
3119
3120 // this will only append (and knock off) %s (anything) at the end of a string
3121 path_len = strlen(atom_path);
3122 memcpy(uuid_path, atom_path, path_len - 2);
3123 memcpy(uuid_path + (path_len - 2), uuid_binary_str, 16);
24533124
24543125 #if defined(DEBUG_V)
2455 fprintf(stdout, "debug: APar_uuid_atom_Init desired atom '%s' converted to uuidv5: ", uuidName);
2456 APar_print_uuid( (ap_uuid_t*)(uuid_path + (path_len-2)) );
3126 fprintf(stdout,
3127 "debug: APar_uuid_atom_Init desired atom '%s' converted to uuidv5: ",
3128 uuidName);
3129 APar_print_uuid((ap_uuid_t *)(uuid_path + (path_len - 2)));
24573130 #endif
24583131
2459 if ( uuidValue == NULL || strlen(uuidValue) == 0) {
2460 APar_RemoveAtom(uuid_path, EXTENDED_ATOM, 0); //find the atom; don't create if it's "" to remove
2461 APar_FlagMovieHeader();
2462 return NULL;
2463
2464 } else {
2465 if ( !(dataType == AtomFlags_Data_Text || dataType == AtomFlags_Data_uuid_binary) ) { //the only supported types
2466 fprintf(stdout, "AP warning: only text or file types are allowed on uuid atom %s (%" PRIu32 "-%u); skipping\n", uuidName, dataType, AtomFlags_Data_Text);
2467 return NULL;
2468 }
2469 //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.
2470 modified_atoms = true;
2471
2472 desiredAtom = APar_FindAtom(uuid_path, true, EXTENDED_ATOM, 0, true);
2473 desiredAtom->uuid_ap_atomname = (char*)calloc(1, sizeof(char)*10); //only useful to print out the atom tree midway through an operation
2474 memcpy(desiredAtom->uuid_ap_atomname, uuid_4char_name, 4); //only useful to print out the atom tree midway through an operation
2475
2476 if (dataType == AtomFlags_Data_Text) APar_MetaData_atom_QuickInit(desiredAtom->AtomicNumber, dataType, 20); //+20 including the 4 NULL bytes preceding any string we set
2477 //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
2478 //all add up to the amount to malloc AtomicData to, so handle that separately.
2479
2480 parsedAtoms[desiredAtom->AtomicNumber].AtomicClassification = EXTENDED_ATOM;
2481 APar_FlagMovieHeader();
2482 }
2483 return desiredAtom;
3132 if (uuidValue == NULL || strlen(uuidValue) == 0) {
3133 APar_RemoveAtom(uuid_path,
3134 EXTENDED_ATOM,
3135 0); // find the atom; don't create if it's "" to remove
3136 APar_FlagMovieHeader();
3137 return NULL;
3138
3139 } else {
3140 if (!(dataType == AtomFlags_Data_Text ||
3141 dataType == AtomFlags_Data_uuid_binary)) { // the only supported types
3142 fprintf(stdout,
3143 "AP warning: only text or file types are allowed on uuid atom %s "
3144 "(%" PRIu32 "-%u); skipping\n",
3145 uuidName,
3146 dataType,
3147 AtomFlags_Data_Text);
3148 return NULL;
3149 }
3150 // uuid atoms won't have 'data' child atoms - they will carry the data
3151 // directly as opposed to traditional iTunes-style metadata that does store
3152 // the information on 'data' atoms. But user-defined is user-defined, so
3153 // that is how it will be defined here.
3154 modified_atoms = true;
3155
3156 desiredAtom = APar_FindAtom(uuid_path, true, EXTENDED_ATOM, 0, true);
3157 desiredAtom->uuid_ap_atomname = (char *)calloc(
3158 1, sizeof(char) * 10); // only useful to print out the atom tree midway
3159 // through an operation
3160 memcpy(desiredAtom->uuid_ap_atomname,
3161 uuid_4char_name,
3162 4); // only useful to print out the atom tree midway through an
3163 // operation
3164
3165 if (dataType == AtomFlags_Data_Text)
3166 APar_MetaData_atom_QuickInit(
3167 desiredAtom->AtomicNumber,
3168 dataType,
3169 20); //+20 including the 4 NULL bytes preceding any string we set
3170 // NOTE: setting a file into a uuid atom (dataType ==
3171 // AtomFlags_Data_uuid_binary) is handled in main.cpp - the length of the
3172 // file extension, description and file all add up to the amount to malloc
3173 // AtomicData to, so handle that separately.
3174
3175 parsedAtoms[desiredAtom->AtomicNumber].AtomicClassification = EXTENDED_ATOM;
3176 APar_FlagMovieHeader();
3177 }
3178 return desiredAtom;
24843179 }
24853180
24863181 /*----------------------
24873182 APar_MetaData_atom_QuickInit
2488 atom_num - the position in the parsedAtoms array (either found in the file or a newly created sparse atom) so AtomicData can be initialized
2489 atomFlags - the AtomicVerFlags for the iTunes-style metadata atom
2490 supplemental_length - iTunes-style metadata for 'data' atoms is >= 16bytes long; AtomicParsley created uuid atoms will be +4bytes directly on that atom
2491 allotment - the bytes of AtomicData to malloc (defaults to MAXDATA_PAYLOAD + 1 (+50) unless changed - like uuids from file)
2492
2493 Metadata_QuickInit will initialize a pre-found atom to MAXDATA_PAYLOAD so that it can carry info on AtomicData
3183 atom_num - the position in the parsedAtoms array (either found in the
3184 file or a newly created sparse atom) so AtomicData can be initialized atomFlags
3185 - the AtomicVerFlags for the iTunes-style metadata atom supplemental_length -
3186 iTunes-style metadata for 'data' atoms is >= 16bytes long; AtomicParsley created
3187 uuid atoms will be +4bytes directly on that atom allotment - the bytes of
3188 AtomicData to malloc (defaults to MAXDATA_PAYLOAD + 1 (+50) unless changed -
3189 like uuids from file)
3190
3191 Metadata_QuickInit will initialize a pre-found atom to MAXDATA_PAYLOAD so
3192 that it can carry info on AtomicData
24943193 ----------------------*/
2495 void APar_MetaData_atom_QuickInit(short atom_num, const uint32_t atomFlags, uint32_t supplemental_length, uint32_t allotment) {
2496 //this will skip the finding of atoms and just malloc the AtomicData; used by genre & artwork
2497
2498 parsedAtoms[atom_num].AtomicData = (char*)calloc(1, sizeof(char)* allotment+50 );
2499 if (parsedAtoms[atom_num].AtomicData == NULL) {
2500 fprintf(stdout, "AP error: there was insufficient memory available for allocation. Exiting.%c\n", '\a');
2501 exit(1);
2502 }
2503
2504 parsedAtoms[atom_num].AtomicLength = 16 + supplemental_length; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags, 4 bytes NULL
2505 parsedAtoms[atom_num].AtomicVerFlags = atomFlags;
2506 parsedAtoms[atom_num].AtomicContainerState = CHILD_ATOM;
2507 parsedAtoms[atom_num].AtomicClassification = VERSIONED_ATOM;
2508
2509 return;
3194 void APar_MetaData_atom_QuickInit(short atom_num,
3195 const uint32_t atomFlags,
3196 uint32_t supplemental_length,
3197 uint32_t allotment) {
3198 // this will skip the finding of atoms and just malloc the AtomicData; used by
3199 // genre & artwork
3200
3201 parsedAtoms[atom_num].AtomicData =
3202 (char *)calloc(1, sizeof(char) * allotment + 50);
3203 if (parsedAtoms[atom_num].AtomicData == NULL) {
3204 fprintf(stdout,
3205 "AP error: there was insufficient memory available for allocation. "
3206 "Exiting.%c\n",
3207 '\a');
3208 exit(1);
3209 }
3210
3211 parsedAtoms[atom_num].AtomicLength =
3212 16 + supplemental_length; // 4bytes atom length, 4 bytes atom length, 4
3213 // bytes version/flags, 4 bytes NULL
3214 parsedAtoms[atom_num].AtomicVerFlags = atomFlags;
3215 parsedAtoms[atom_num].AtomicContainerState = CHILD_ATOM;
3216 parsedAtoms[atom_num].AtomicClassification = VERSIONED_ATOM;
3217
3218 return;
25103219 }
25113220
25123221 /*----------------------
25133222 APar_MetaData_atom_Init
2514 atom_path - the hierarchical path to the specific atom carrying iTunes-style metadata that will be found (and created if necessary)
2515 MD_Payload - the information to be carried (also used as a test if NULL to remove the atom)
2516 atomFlags - the AtomicVerFlags for the atom (text, integer or unsigned integer)
2517
2518 Metadata_Init will search for and create the necessary hierarchy so that the atom can be initialized to carry the payload data on AtomicData.
2519 This will provide a shell of an atom with 4bytes length, 4bytes name, 4bytes version/flags, 4bytes NULL + any other data
3223 atom_path - the hierarchical path to the specific atom carrying
3224 iTunes-style metadata that will be found (and created if necessary) MD_Payload -
3225 the information to be carried (also used as a test if NULL to remove the atom)
3226 atomFlags - the AtomicVerFlags for the atom (text, integer or unsigned
3227 integer)
3228
3229 Metadata_Init will search for and create the necessary hierarchy so that the
3230 atom can be initialized to carry the payload data on AtomicData. This will
3231 provide a shell of an atom with 4bytes length, 4bytes name, 4bytes
3232 version/flags, 4bytes NULL + any other data
25203233 ----------------------*/
2521 AtomicInfo* APar_MetaData_atom_Init(const char* atom_path, const char* MD_Payload, const uint32_t atomFlags) {
2522 //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
2523 if (metadata_style != ITUNES_STYLE) return NULL;
2524 bool retain_atom = true;
2525
2526 if ( strlen(MD_Payload) == 0 ) {
2527 retain_atom = false;
2528 }
2529
2530 if (retain_atom) {
2531 APar_Verify__udta_meta_hdlr__atom();
2532 }
2533
2534 AtomicInfo* desiredAtom = APar_FindAtom(atom_path, retain_atom, VERSIONED_ATOM, 0); //finds the atom; if not present, creates the atom
2535 if (desiredAtom == NULL) return NULL;
2536 modified_atoms = true;
2537
2538 if (! retain_atom) {
2539 AtomicInfo* parent_atom = &parsedAtoms[ APar_FindParentAtom(desiredAtom->AtomicNumber, desiredAtom->AtomicLevel) ];
2540 if (desiredAtom->AtomicNumber > 0 && parent_atom->AtomicNumber > 0) {
2541 APar_EliminateAtom(parent_atom->AtomicNumber, desiredAtom->NextAtomNumber);
2542 APar_FlagMovieHeader();
2543 return NULL;
2544 }
2545
2546 } else {
2547 parsedAtoms[desiredAtom->AtomicNumber].AtomicData = (char*)malloc(sizeof(char)* MAXDATA_PAYLOAD + 1 ); //puts a hard limit on the length of strings (the spec doesn't)
2548 memset(parsedAtoms[desiredAtom->AtomicNumber].AtomicData, 0, sizeof(char)* MAXDATA_PAYLOAD + 1 );
2549
2550 parsedAtoms[desiredAtom->AtomicNumber].AtomicLength = 16; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags, 4 bytes NULL
2551 parsedAtoms[desiredAtom->AtomicNumber].AtomicVerFlags = atomFlags;
2552 parsedAtoms[desiredAtom->AtomicNumber].AtomicContainerState = CHILD_ATOM;
2553 parsedAtoms[desiredAtom->AtomicNumber].AtomicClassification = VERSIONED_ATOM;
2554 APar_FlagMovieHeader();
2555 }
2556 return desiredAtom;
3234 AtomicInfo *APar_MetaData_atom_Init(const char *atom_path,
3235 const char *MD_Payload,
3236 const uint32_t atomFlags) {
3237 // this will handle the vanilla iTunes-style metadata atoms; genre will be
3238 // handled elsewehere because it gets carried on 2 different atoms, and
3239 // artwork gets special treatment because it can have multiple child data
3240 // atoms
3241 if (metadata_style != ITUNES_STYLE)
3242 return NULL;
3243 bool retain_atom = true;
3244
3245 if (strlen(MD_Payload) == 0) {
3246 retain_atom = false;
3247 }
3248
3249 if (retain_atom) {
3250 APar_Verify__udta_meta_hdlr__atom();
3251 }
3252
3253 AtomicInfo *desiredAtom =
3254 APar_FindAtom(atom_path,
3255 retain_atom,
3256 VERSIONED_ATOM,
3257 0); // finds the atom; if not present, creates the atom
3258 if (desiredAtom == NULL)
3259 return NULL;
3260 modified_atoms = true;
3261
3262 if (!retain_atom) {
3263 AtomicInfo *parent_atom = &parsedAtoms[APar_FindParentAtom(
3264 desiredAtom->AtomicNumber, desiredAtom->AtomicLevel)];
3265 if (desiredAtom->AtomicNumber > 0 && parent_atom->AtomicNumber > 0) {
3266 APar_EliminateAtom(parent_atom->AtomicNumber,
3267 desiredAtom->NextAtomNumber);
3268 APar_FlagMovieHeader();
3269 return NULL;
3270 }
3271
3272 } else {
3273 parsedAtoms[desiredAtom->AtomicNumber].AtomicData = (char *)malloc(
3274 sizeof(char) * MAXDATA_PAYLOAD +
3275 1); // puts a hard limit on the length of strings (the spec doesn't)
3276 memset(parsedAtoms[desiredAtom->AtomicNumber].AtomicData,
3277 0,
3278 sizeof(char) * MAXDATA_PAYLOAD + 1);
3279
3280 parsedAtoms[desiredAtom->AtomicNumber].AtomicLength =
3281 16; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags, 4
3282 // bytes NULL
3283 parsedAtoms[desiredAtom->AtomicNumber].AtomicVerFlags = atomFlags;
3284 parsedAtoms[desiredAtom->AtomicNumber].AtomicContainerState = CHILD_ATOM;
3285 parsedAtoms[desiredAtom->AtomicNumber].AtomicClassification =
3286 VERSIONED_ATOM;
3287 APar_FlagMovieHeader();
3288 }
3289 return desiredAtom;
25573290 }
25583291
25593292 /*----------------------
25603293 APar_UserData_atom_Init
2561 userdata_atom_name - the name of the atom to be set ('titl', 'loci', 'cprt')
2562 atom_payload - the information to be carried (also used as a test if NULL to remove the atom)
2563 udta_container - determines whether to create an atom path at movie level or track level
2564 track_idx - provide the track for the target atom if at track level
2565 userdata_lang - the language for the tag (multiple tags with the same name, but different languages are supported for some of these atoms)
2566
2567 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
2568 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
2569 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
2570 setting is set (for internal AP use) for that atom.
2571
2572 NOTE: the language setting (if supported - yrrc doesn't) occurs in different places in 3GP atoms. Most occur right after atom flags/versioning - but
2573 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
2574 (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
2575 the parsedAtoms[] AtomicInfo array) forms are required to implement multiple language support for 3gp atoms.
2576
2577 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
2578 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
2579 gylphs missing with lang=eng. The same string with 'lang=pol', and different glyphs are missing. The problem occurs using unicode.org's ConvertUTF8toUTF16
2580 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
2581 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
2582 utf8 or utf16. To summarize, either I am completely overlooking some interplay (of lang somehow changing the utf8 or utf16 standard), the unicode translations
2583 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.
2584
2585 TODO NOTE: the track modification date should change if set at track level because of this
3294 userdata_atom_name - the name of the atom to be set ('titl', 'loci',
3295 'cprt') atom_payload - the information to be carried (also used as a test if
3296 NULL to remove the atom) udta_container - determines whether to create an atom
3297 path at movie level or track level track_idx - provide the track for the target
3298 atom if at track level userdata_lang - the language for the tag (multiple tags
3299 with the same name, but different languages are supported for some of these
3300 atoms)
3301
3302 UserData_Init can be called multiple times from a single cli invocation (if
3303 used to target at atom at track level for all tracks). Since it can be called
3304 repeatedly, the atom path is created here based on the container
3305 for 'udta': 'moov' for movie level or 'trak' at track level. If there is no
3306 payload that atom path is found & if found to exists is removed. If the payload
3307 does contain something, the created atom path is created, initialized & the
3308 language setting is set (for internal AP use) for that atom.
3309
3310 NOTE: the language setting (if supported - yrrc doesn't)
3311 occurs in different places in 3GP atoms. Most occur right after atom
3312 flags/versioning - but in rtng/clsf they occur later. The language is instead
3313 stored in binary form amid the data for the atom, but is also put into the
3314 parsedAtoms[] array (that information is only used in finding atoms not the
3315 actual writing of atoms out). Both (storing the language in AtomicData in binary
3316 form & put in the parsedAtoms[] AtomicInfo array) forms are required to
3317 implement multiple language support for 3gp atoms.
3318
3319 NOTE2: Perhaps there is something wrong with Apple's
3320 implementation of 3gp metadata, or I'm loosing my mind. The exact same utf8
3321 string that shows up in a 3gp file as ??? - ??? shows up *perfect* in an mp4 or
3322 mov container. Encoded as utf16 same problem a sample string using Polish glyphs
3323 in utf8 has some gylphs missing with lang=eng. The same string with 'lang=pol',
3324 and different glyphs are missing. The problem occurs using unicode.org's
3325 ConvertUTF8toUTF16 or using libxmls's UTF8ToUTF16BE (when converting to utf16)
3326 in the same places - except for the copyright symbol which unicode.org's
3327 ConvertUTF16toUTF8 didn't properly convert - which was the reason why libxml's
3328 functions are now used. And at no point can I get the audio protected
3329 P-in-a-circle glyph to show up in utf8 or utf16. To summarize, either I am
3330 completely overlooking some interplay (of lang somehow changing the utf8 or
3331 utf16 standard), the unicode translations are off (which in the case of utf8 is
3332 embedded directly on Mac OS X, so that can't be it), or Apple's 3gp
3333 implementation is off.
3334
3335 TODO NOTE: the track modification date should change if
3336 set at track level because of this
25863337 ----------------------*/
2587 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) {
2588 uint8_t atom_type = PACKED_LANG_ATOM;
2589 uint8_t total_tracks = 0;
2590 uint8_t a_track = 0;//unused
2591 AtomicInfo* desiredAtom = NULL;
2592 char* userdata_atom_path = NULL;
2593
2594 if (userdata_lang == 0) atom_type = VERSIONED_ATOM;
2595
2596 APar_FindAtomInTrack(total_tracks, a_track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here.
2597
2598 if (track_idx > total_tracks || (track_idx == 0 && udta_container == SINGLE_TRACK_ATOM) ) {
2599 APar_assert(false, 5, userdata_atom_name);
2600 return NULL;
2601 }
2602
2603 userdata_atom_path = (char*)malloc(sizeof(char)* 400 );
2604
2605 if (udta_container == MOVIE_LEVEL_ATOM) {
2606 APar_sprintf_atompath(userdata_atom_path, userdata_atom_name, 0xFF, MOVIE_LEVEL_ATOM, 400);
2607 } else if (udta_container == ALL_TRACKS_ATOM) {
2608 APar_sprintf_atompath(userdata_atom_path, userdata_atom_name, track_idx, ALL_TRACKS_ATOM, 400);
2609 } else {
2610 APar_sprintf_atompath(userdata_atom_path, userdata_atom_name, track_idx, SINGLE_TRACK_ATOM, 400);
2611 }
2612
2613 if ( strlen(atom_payload) == 0) {
2614
2615 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
2616 free(userdata_atom_path); userdata_atom_path = NULL;
2617 return NULL;
2618 } else {
2619 modified_atoms = true;
2620
2621 desiredAtom = APar_FindAtom(userdata_atom_path, true, atom_type, userdata_lang);
2622
2623 desiredAtom->AtomicData = (char*)calloc(1, sizeof(char)* MAXDATA_PAYLOAD ); //puts a hard limit on the length of strings (the spec doesn't)
2624
2625 desiredAtom->AtomicLength = 12; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags (NULLs)
2626 desiredAtom->AtomicVerFlags = 0;
2627 desiredAtom->AtomicContainerState = CHILD_ATOM;
2628 desiredAtom->AtomicClassification = atom_type;
2629 desiredAtom->AtomicLanguage = userdata_lang;
2630 APar_FlagTrackHeader(desiredAtom);
2631 }
2632 free(userdata_atom_path); userdata_atom_path = NULL;
2633 return desiredAtom;
3338 AtomicInfo *APar_UserData_atom_Init(const char *userdata_atom_name,
3339 const char *atom_payload,
3340 uint8_t udta_container,
3341 uint8_t track_idx,
3342 uint16_t userdata_lang) {
3343 uint8_t atom_type = PACKED_LANG_ATOM;
3344 uint8_t total_tracks = 0;
3345 uint8_t a_track = 0; // unused
3346 AtomicInfo *desiredAtom = NULL;
3347 char *userdata_atom_path = NULL;
3348
3349 if (userdata_lang == 0)
3350 atom_type = VERSIONED_ATOM;
3351
3352 APar_FindAtomInTrack(total_tracks,
3353 a_track,
3354 NULL); // With track_num set to 0, it will return the
3355 // total trak atom into total_tracks here.
3356
3357 if (track_idx > total_tracks ||
3358 (track_idx == 0 && udta_container == SINGLE_TRACK_ATOM)) {
3359 APar_assert(false, 5, userdata_atom_name);
3360 return NULL;
3361 }
3362
3363 userdata_atom_path = (char *)malloc(sizeof(char) * 400);
3364
3365 if (udta_container == MOVIE_LEVEL_ATOM) {
3366 APar_sprintf_atompath(
3367 userdata_atom_path, userdata_atom_name, 0xFF, MOVIE_LEVEL_ATOM, 400);
3368 } else if (udta_container == ALL_TRACKS_ATOM) {
3369 APar_sprintf_atompath(userdata_atom_path,
3370 userdata_atom_name,
3371 track_idx,
3372 ALL_TRACKS_ATOM,
3373 400);
3374 } else {
3375 APar_sprintf_atompath(userdata_atom_path,
3376 userdata_atom_name,
3377 track_idx,
3378 SINGLE_TRACK_ATOM,
3379 400);
3380 }
3381
3382 if (strlen(atom_payload) == 0) {
3383
3384 APar_RemoveAtom(userdata_atom_path,
3385 atom_type,
3386 atom_type == VERSIONED_ATOM
3387 ? 1
3388 : userdata_lang); // find the atom; don't create if it's
3389 // "" to remove
3390 free(userdata_atom_path);
3391 userdata_atom_path = NULL;
3392 return NULL;
3393 } else {
3394 modified_atoms = true;
3395
3396 desiredAtom =
3397 APar_FindAtom(userdata_atom_path, true, atom_type, userdata_lang);
3398
3399 desiredAtom->AtomicData = (char *)calloc(
3400 1, sizeof(char) * MAXDATA_PAYLOAD); // puts a hard limit on the length
3401 // of strings (the spec doesn't)
3402
3403 desiredAtom->AtomicLength = 12; // 4bytes atom length, 4 bytes atom length,
3404 // 4 bytes version/flags (NULLs)
3405 desiredAtom->AtomicVerFlags = 0;
3406 desiredAtom->AtomicContainerState = CHILD_ATOM;
3407 desiredAtom->AtomicClassification = atom_type;
3408 desiredAtom->AtomicLanguage = userdata_lang;
3409 APar_FlagTrackHeader(desiredAtom);
3410 }
3411 free(userdata_atom_path);
3412 userdata_atom_path = NULL;
3413 return desiredAtom;
26343414 }
26353415
26363416 /*----------------------
26373417 APar_reverseDNS_atom_Init
2638 rDNS_atom_name - the name of the descriptor for the reverseDNS atom form (like iTunNORM)
2639 rDNS_payload - the information to be carried (also used as a test if NULL to remove the atom)
2640 atomFlags - text, integer, binary flag of the data payload
2641 rDNS_domain - the reverse domain itself (like net.sourceforge.atomicparsley or com.apple.iTunes)
3418 rDNS_atom_name - the name of the descriptor for the reverseDNS atom form
3419 (like iTunNORM) rDNS_payload - the information to be carried (also used as a
3420 test if NULL to remove the atom) atomFlags - text, integer, binary flag of the
3421 data payload rDNS_domain - the reverse domain itself (like
3422 net.sourceforge.atomicparsley or com.apple.iTunes)
26423423
26433424 FILL IN
26443425 ----------------------*/
2645 AtomicInfo* APar_reverseDNS_atom_Init(const char* rDNS_atom_name, const char* rDNS_payload, const uint32_t* atomFlags, const char* rDNS_domain) {
2646 AtomicInfo* desiredAtom = NULL;
2647 char* reverseDNS_atompath = (char*)calloc(1, sizeof(char)*2001);
2648
2649 if (metadata_style != ITUNES_STYLE) {
2650 free(reverseDNS_atompath); reverseDNS_atompath = NULL;
2651 return NULL;
2652 }
2653
2654 sprintf(reverseDNS_atompath, "moov.udta.meta.ilst.----.name:[%s]", rDNS_atom_name); //moov.udta.meta.ilst.----.name:[iTunNORM]
2655
2656 if ( rDNS_payload != NULL ) {
2657 if (strlen(rDNS_payload) == 0) {
2658 APar_RemoveAtom(reverseDNS_atompath, VERSIONED_ATOM, 0, rDNS_domain);
2659 free(reverseDNS_atompath); reverseDNS_atompath = NULL;
2660 return NULL;
2661 }
2662 APar_Verify__udta_meta_hdlr__atom();
2663 } else {
2664 APar_RemoveAtom(reverseDNS_atompath, VERSIONED_ATOM, 0, rDNS_domain);
2665 APar_FlagMovieHeader();
2666 free(reverseDNS_atompath); reverseDNS_atompath = NULL;
2667 return NULL;
2668 }
2669
2670 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
2671
2672 if (desiredAtom == NULL) {
2673 AtomicInfo* ilst_atom = APar_FindAtom("moov.udta.meta.ilst", true, SIMPLE_ATOM, 0);
2674 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
2675
2676 short rDNS_four_dash_parent = APar_InterjectNewAtom("----", PARENT_ATOM, SIMPLE_ATOM, 8, 0, 0, ilst_atom->AtomicLevel+1, last_iTunes_list_descriptor);
2677
2678 short rDNS_mean_atom = APar_InterjectNewAtom("mean", CHILD_ATOM, VERSIONED_ATOM, 12, AtomFlags_Data_Binary, 0, ilst_atom->AtomicLevel+2, rDNS_four_dash_parent);
2679 uint32_t domain_len = strlen(rDNS_domain);
2680 parsedAtoms[rDNS_mean_atom].ReverseDNSdomain = (char*)calloc(1, sizeof(char)*101);
2681 memcpy( parsedAtoms[rDNS_mean_atom].ReverseDNSdomain, rDNS_domain, domain_len );
2682 APar_atom_Binary_Put(&parsedAtoms[rDNS_mean_atom], rDNS_domain, domain_len, 0);
2683
2684 short rDNS_name_atom = APar_InterjectNewAtom("name", CHILD_ATOM, VERSIONED_ATOM, 12, AtomFlags_Data_Binary, 0, ilst_atom->AtomicLevel+2, rDNS_mean_atom);
2685 uint32_t name_len = strlen(rDNS_atom_name);
2686 parsedAtoms[rDNS_name_atom].ReverseDNSname = (char*)calloc(1, sizeof(char)*101);
2687 memcpy( parsedAtoms[rDNS_name_atom].ReverseDNSname, rDNS_atom_name, name_len );
2688 APar_atom_Binary_Put(&parsedAtoms[rDNS_name_atom], rDNS_atom_name, name_len, 0);
2689
2690 AtomicInfo proto_rDNS_data_atom = { 0 };
2691 APar_CreateSurrogateAtom(&proto_rDNS_data_atom, "data", ilst_atom->AtomicLevel+2, VERSIONED_ATOM, 0, NULL, 0);
2692 desiredAtom = APar_CreateSparseAtom(&proto_rDNS_data_atom, ilst_atom, rDNS_name_atom);
2693 APar_MetaData_atom_QuickInit(desiredAtom->AtomicNumber, *atomFlags, 0, MAXDATA_PAYLOAD);
2694 } else {
2695 if (strcmp(rDNS_domain, "com.apple.iTunes") == 0) { //for the iTunes domain, only support 1 'data' entry
2696 APar_MetaData_atom_QuickInit(desiredAtom->NextAtomNumber, *atomFlags, 0, MAXDATA_PAYLOAD);
2697 desiredAtom = &parsedAtoms[desiredAtom->NextAtomNumber];
2698
2699 } else { //now create a 'data' atom at the end of the hierarchy (allowing multiple entries)
2700 short rDNSparent_idx = APar_FindParentAtom(desiredAtom->AtomicNumber, desiredAtom->AtomicLevel);
2701 short last_child = APar_FindLastChild_of_ParentAtom(rDNSparent_idx);
2702 AtomicInfo proto_rDNS_data_atom = { 0 };
2703 APar_CreateSurrogateAtom(&proto_rDNS_data_atom, "data", parsedAtoms[last_child].AtomicLevel, VERSIONED_ATOM, 0, NULL, 0);
2704 desiredAtom = APar_CreateSparseAtom(&proto_rDNS_data_atom, &parsedAtoms[rDNSparent_idx], last_child);
2705 APar_MetaData_atom_QuickInit(desiredAtom->AtomicNumber, *atomFlags, 0, MAXDATA_PAYLOAD);
2706 }
2707 }
2708 APar_FlagMovieHeader();
2709 free(reverseDNS_atompath); reverseDNS_atompath = NULL;
2710 modified_atoms = true;
2711 return desiredAtom;
2712 }
2713
2714 AtomicInfo* APar_ID32_atom_Init(const char* frameID_str, char meta_area, const char* lang_str, uint16_t id32_lang) {
2715 uint8_t total_tracks = 0;
2716 uint8_t a_track = 0;//unused
2717 AtomicInfo* meta_atom = NULL;
2718 AtomicInfo* hdlr_atom = NULL;
2719 char* id32_trackpath = NULL;
2720 AtomicInfo* ID32_atom = NULL;
2721 bool non_referenced_data = false;
2722 bool remove_ID32_atom = false;
2723
2724 APar_FindAtomInTrack(total_tracks, a_track, NULL); //With track_num set to 0, it will return the total trak atom into total_tracks here.
2725
2726 if (meta_area > 0) {
2727 if ((uint8_t)meta_area > total_tracks) {
2728 APar_assert(false, 6, frameID_str);
2729 return NULL;
2730 }
2731 }
2732
2733 id32_trackpath = (char*)calloc(1, sizeof(char)*100);
2734
2735 if (meta_area == 0-FILE_LEVEL_ATOM) {
2736 meta_atom = APar_FindAtom("meta", false, DUAL_STATE_ATOM, 0);
2737 } else if (meta_area == 0-MOVIE_LEVEL_ATOM) {
2738 meta_atom = APar_FindAtom("moov.meta", false, DUAL_STATE_ATOM, 0);
2739 //} else if (meta_area = 0) {
2740 //setting id3tags for all tracks will *not* be supported;
2741 } else if (meta_area > 0) {
2742 sprintf(id32_trackpath, "moov.trak[%u].meta", meta_area);
2743 meta_atom = APar_FindAtom(id32_trackpath, false, DUAL_STATE_ATOM, 0);
2744 }
2745
2746 if (meta_atom != NULL) {
2747 hdlr_atom = APar_FindChildAtom(meta_atom->AtomicNumber, "hdlr");
2748 if (hdlr_atom != NULL) {
2749 if (hdlr_atom->ancillary_data != 0x49443332) {
2750 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
2751 UInt32_TO_String4(hdlr_atom->ancillary_data, id32_trackpath);
2752 APar_assert(false, 7, id32_trackpath);
2753 free(id32_trackpath);
2754
2755 return NULL;
2756 }
2757 }
2758 }
2759
2760 //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.
2761 if (meta_area == 0-FILE_LEVEL_ATOM) {
2762 ID32_atom = APar_FindAtom("meta.ID32", false, PACKED_LANG_ATOM, id32_lang);
2763 } else if (meta_area == 0-MOVIE_LEVEL_ATOM) {
2764 ID32_atom = APar_FindAtom("moov.meta.ID32", false, PACKED_LANG_ATOM, id32_lang);
2765 //} else if (meta_area = 0) {
2766 //setting id3tags for all tracks will *not* be supported;
2767 } else if (meta_area > 0) {
2768 sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area);
2769 ID32_atom = APar_FindAtom(id32_trackpath, false, PACKED_LANG_ATOM, id32_lang);
2770 }
2771
2772 if (ID32_atom != NULL) {
2773 free(id32_trackpath);
2774 id32_trackpath = NULL;
2775 if (ID32_atom->ID32_TagInfo == NULL) {
2776 APar_ID32_ScanID3Tag(source_file, ID32_atom);
2777 }
2778 return ID32_atom; //and that completes finding the ID32 atom and verifying that it was local and in a traditionally represented form.
2779
2780 } else {
2781 if (meta_atom != NULL) {
2782 //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.
2783 //..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).
2784 if (APar_FindChildAtom(meta_atom->AtomicNumber, "pitm") != NULL) {
2785 non_referenced_data = false;
2786 APar_assert(false, 8, frameID_str);
2787 free(id32_trackpath);
2788 return NULL;
2789 } else { //the inline/3gpp 'ID32' atom calls for referenced content to carry a 'pitm' atom. No worries - its just a 'meta'.
2790 non_referenced_data = true;
2791 }
2792 } else {
2793 //no 'meta' atom? Great - a blank slate. There won't be any jumping through a multitude of atoms to determine referencing
2794 non_referenced_data = true;
2795 }
2796 }
2797
2798 if (frameID_str == NULL) {
2799 remove_ID32_atom = true;
2800 } else if (strlen(frameID_str) == 0) {
2801 remove_ID32_atom = true;
2802 }
2803 //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.
2804 if (non_referenced_data && !remove_ID32_atom) {
2805 if (meta_atom == NULL) {
2806 if (meta_area == 0-FILE_LEVEL_ATOM) {
2807 meta_atom = APar_FindAtom("meta", true, VERSIONED_ATOM, 0);
2808 } else if (meta_area == 0-MOVIE_LEVEL_ATOM) {
2809 meta_atom = APar_FindAtom("moov.meta", true, VERSIONED_ATOM, 0);
2810 //} else if (meta_area = 0) {
2811 //setting id3tags for all tracks will *not* be supported;
2812 } else if (meta_area > 0) {
2813 sprintf(id32_trackpath, "moov.trak[%u].meta", meta_area);
2814 meta_atom = APar_FindAtom(id32_trackpath, true, VERSIONED_ATOM, 0);
2815 }
2816 }
2817
2818 //create the required hdlr atom
2819 if (hdlr_atom == NULL) {
2820 if (meta_area == 0-FILE_LEVEL_ATOM) {
2821 hdlr_atom = APar_FindAtom("meta.hdlr", true, VERSIONED_ATOM, 0);
2822 } else if (meta_area == 0-MOVIE_LEVEL_ATOM) {
2823 hdlr_atom = APar_FindAtom("moov.meta.hdlr", true, VERSIONED_ATOM, 0);
2824 //} else if (meta_area = 0) {
2825 //setting id3tags for all tracks will *not* be supported;
2826 } else if (meta_area > 0) {
2827 sprintf(id32_trackpath, "moov.trak[%u].meta.hdlr", meta_area);
2828 hdlr_atom = APar_FindAtom(id32_trackpath, true, VERSIONED_ATOM, 0);
2829 }
2830 if (hdlr_atom == NULL) {
2831 fprintf(stdout, "Uh, problem\n");
2832 exit(0);
2833 }
2834 APar_MetaData_atom_QuickInit(hdlr_atom->AtomicNumber, 0, 0);
2835 APar_Unified_atom_Put(hdlr_atom, "ID32", UTF8_iTunesStyle_256glyphLimited, 0, 0);
2836 APar_Unified_atom_Put(hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
2837 APar_Unified_atom_Put(hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
2838 APar_Unified_atom_Put(hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
2839 APar_Unified_atom_Put(hdlr_atom, "AtomicParsley ID3v2 Handler", UTF8_3GP_Style, 0, 0);
2840 hdlr_atom->ancillary_data = 0x49443332;
2841 }
2842
2843 //and finally create the ID32 atom
2844 if (meta_area == 0-FILE_LEVEL_ATOM) {
2845 ID32_atom = APar_FindAtom("meta.ID32", true, PACKED_LANG_ATOM, id32_lang);
2846 } else if (meta_area == 0-MOVIE_LEVEL_ATOM) {
2847 ID32_atom = APar_FindAtom("moov.meta.ID32", true, PACKED_LANG_ATOM, id32_lang);
2848 //} else if (meta_area = 0) {
2849 //setting id3tags for all tracks will *not* be supported;
2850 } else if (meta_area > 0) {
2851 sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area);
2852 ID32_atom = APar_FindAtom(id32_trackpath, true, PACKED_LANG_ATOM, id32_lang);
2853 }
2854
2855 if (id32_trackpath != NULL) {
2856 free(id32_trackpath);
2857 id32_trackpath = NULL;
2858 }
2859
2860 ID32_atom->AtomicLength = 12; // 4bytes atom length, 4 bytes atom length, 4 bytes version/flags (NULLs), 2 bytes lang
2861 ID32_atom->AtomicVerFlags = 0;
2862 ID32_atom->AtomicContainerState = CHILD_ATOM;
2863 ID32_atom->AtomicClassification = PACKED_LANG_ATOM;
2864 ID32_atom->AtomicLanguage = id32_lang;
2865
2866 APar_ID3Tag_Init(ID32_atom);
2867 //search for the desired frame
2868
2869 //add the frame to an empty frame, copy data onto the id32_atom structure as required
2870 //set modified _atoms to true
2871
2872 return ID32_atom;
2873 } else if (remove_ID32_atom) {
2874 if (meta_area == 0-FILE_LEVEL_ATOM) {
2875 APar_RemoveAtom("meta.ID32", PACKED_LANG_ATOM, id32_lang);
2876 } else if (meta_area == 0-MOVIE_LEVEL_ATOM) {
2877 APar_RemoveAtom("moov.meta.ID32", PACKED_LANG_ATOM, id32_lang);
2878 //} else if (meta_area = 0) {
2879 //setting id3tags for all tracks will *not* be supported;
2880 } else if (meta_area > 0) {
2881 sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area);
2882 APar_RemoveAtom(id32_trackpath, PACKED_LANG_ATOM, id32_lang);
2883 }
2884 }
2885 return NULL;
3426 AtomicInfo *APar_reverseDNS_atom_Init(const char *rDNS_atom_name,
3427 const char *rDNS_payload,
3428 const uint32_t *atomFlags,
3429 const char *rDNS_domain) {
3430 AtomicInfo *desiredAtom = NULL;
3431 char *reverseDNS_atompath = (char *)calloc(1, sizeof(char) * 2001);
3432
3433 if (metadata_style != ITUNES_STYLE) {
3434 free(reverseDNS_atompath);
3435 reverseDNS_atompath = NULL;
3436 return NULL;
3437 }
3438
3439 sprintf(reverseDNS_atompath,
3440 "moov.udta.meta.ilst.----.name:[%s]",
3441 rDNS_atom_name); // moov.udta.meta.ilst.----.name:[iTunNORM]
3442
3443 if (rDNS_payload != NULL) {
3444 if (strlen(rDNS_payload) == 0) {
3445 APar_RemoveAtom(reverseDNS_atompath, VERSIONED_ATOM, 0, rDNS_domain);
3446 free(reverseDNS_atompath);
3447 reverseDNS_atompath = NULL;
3448 return NULL;
3449 }
3450 APar_Verify__udta_meta_hdlr__atom();
3451 } else {
3452 APar_RemoveAtom(reverseDNS_atompath, VERSIONED_ATOM, 0, rDNS_domain);
3453 APar_FlagMovieHeader();
3454 free(reverseDNS_atompath);
3455 reverseDNS_atompath = NULL;
3456 return NULL;
3457 }
3458
3459 desiredAtom =
3460 APar_FindAtom(reverseDNS_atompath,
3461 false,
3462 VERSIONED_ATOM,
3463 0,
3464 false,
3465 rDNS_domain); // finds the atom; do NOT create it if not
3466 // found - manually create the hierarchy
3467
3468 if (desiredAtom == NULL) {
3469 AtomicInfo *ilst_atom =
3470 APar_FindAtom("moov.udta.meta.ilst", true, SIMPLE_ATOM, 0);
3471 short last_iTunes_list_descriptor = APar_FindLastChild_of_ParentAtom(
3472 ilst_atom->AtomicNumber); // the *last* atom contained by ilst - even if
3473 // its the 4th 'data' atom
3474
3475 short rDNS_four_dash_parent =
3476 APar_InterjectNewAtom("----",
3477 PARENT_ATOM,
3478 SIMPLE_ATOM,
3479 8,
3480 0,
3481 0,
3482 ilst_atom->AtomicLevel + 1,
3483 last_iTunes_list_descriptor);
3484
3485 short rDNS_mean_atom = APar_InterjectNewAtom("mean",
3486 CHILD_ATOM,
3487 VERSIONED_ATOM,
3488 12,
3489 AtomFlags_Data_Binary,
3490 0,
3491 ilst_atom->AtomicLevel + 2,
3492 rDNS_four_dash_parent);
3493 uint32_t domain_len = strlen(rDNS_domain);
3494 parsedAtoms[rDNS_mean_atom].ReverseDNSdomain =
3495 (char *)calloc(1, sizeof(char) * 101);
3496 memcpy(
3497 parsedAtoms[rDNS_mean_atom].ReverseDNSdomain, rDNS_domain, domain_len);
3498 APar_atom_Binary_Put(
3499 &parsedAtoms[rDNS_mean_atom], rDNS_domain, domain_len, 0);
3500
3501 short rDNS_name_atom = APar_InterjectNewAtom("name",
3502 CHILD_ATOM,
3503 VERSIONED_ATOM,
3504 12,
3505 AtomFlags_Data_Binary,
3506 0,
3507 ilst_atom->AtomicLevel + 2,
3508 rDNS_mean_atom);
3509 uint32_t name_len = strlen(rDNS_atom_name);
3510 parsedAtoms[rDNS_name_atom].ReverseDNSname =
3511 (char *)calloc(1, sizeof(char) * 101);
3512 memcpy(
3513 parsedAtoms[rDNS_name_atom].ReverseDNSname, rDNS_atom_name, name_len);
3514 APar_atom_Binary_Put(
3515 &parsedAtoms[rDNS_name_atom], rDNS_atom_name, name_len, 0);
3516
3517 AtomicInfo proto_rDNS_data_atom = {0};
3518 APar_CreateSurrogateAtom(&proto_rDNS_data_atom,
3519 "data",
3520 ilst_atom->AtomicLevel + 2,
3521 VERSIONED_ATOM,
3522 0,
3523 NULL,
3524 0);
3525 desiredAtom =
3526 APar_CreateSparseAtom(&proto_rDNS_data_atom, ilst_atom, rDNS_name_atom);
3527 APar_MetaData_atom_QuickInit(
3528 desiredAtom->AtomicNumber, *atomFlags, 0, MAXDATA_PAYLOAD);
3529 } else {
3530 if (strcmp(rDNS_domain, "com.apple.iTunes") ==
3531 0) { // for the iTunes domain, only support 1 'data' entry
3532 APar_MetaData_atom_QuickInit(
3533 desiredAtom->NextAtomNumber, *atomFlags, 0, MAXDATA_PAYLOAD);
3534 desiredAtom = &parsedAtoms[desiredAtom->NextAtomNumber];
3535
3536 } else { // now create a 'data' atom at the end of the hierarchy (allowing
3537 // multiple entries)
3538 short rDNSparent_idx = APar_FindParentAtom(desiredAtom->AtomicNumber,
3539 desiredAtom->AtomicLevel);
3540 short last_child = APar_FindLastChild_of_ParentAtom(rDNSparent_idx);
3541 AtomicInfo proto_rDNS_data_atom = {0};
3542 APar_CreateSurrogateAtom(&proto_rDNS_data_atom,
3543 "data",
3544 parsedAtoms[last_child].AtomicLevel,
3545 VERSIONED_ATOM,
3546 0,
3547 NULL,
3548 0);
3549 desiredAtom = APar_CreateSparseAtom(
3550 &proto_rDNS_data_atom, &parsedAtoms[rDNSparent_idx], last_child);
3551 APar_MetaData_atom_QuickInit(
3552 desiredAtom->AtomicNumber, *atomFlags, 0, MAXDATA_PAYLOAD);
3553 }
3554 }
3555 APar_FlagMovieHeader();
3556 free(reverseDNS_atompath);
3557 reverseDNS_atompath = NULL;
3558 modified_atoms = true;
3559 return desiredAtom;
3560 }
3561
3562 AtomicInfo *APar_ID32_atom_Init(const char *frameID_str,
3563 char meta_area,
3564 const char *lang_str,
3565 uint16_t id32_lang) {
3566 uint8_t total_tracks = 0;
3567 uint8_t a_track = 0; // unused
3568 AtomicInfo *meta_atom = NULL;
3569 AtomicInfo *hdlr_atom = NULL;
3570 char *id32_trackpath = NULL;
3571 AtomicInfo *ID32_atom = NULL;
3572 bool non_referenced_data = false;
3573 bool remove_ID32_atom = false;
3574
3575 APar_FindAtomInTrack(total_tracks,
3576 a_track,
3577 NULL); // With track_num set to 0, it will return the
3578 // total trak atom into total_tracks here.
3579
3580 if (meta_area > 0) {
3581 if ((uint8_t)meta_area > total_tracks) {
3582 APar_assert(false, 6, frameID_str);
3583 return NULL;
3584 }
3585 }
3586
3587 id32_trackpath = (char *)calloc(1, sizeof(char) * 100);
3588
3589 if (meta_area == 0 - FILE_LEVEL_ATOM) {
3590 meta_atom = APar_FindAtom("meta", false, DUAL_STATE_ATOM, 0);
3591 } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) {
3592 meta_atom = APar_FindAtom("moov.meta", false, DUAL_STATE_ATOM, 0);
3593 //} else if (meta_area = 0) {
3594 // setting id3tags for all tracks will *not* be supported;
3595 } else if (meta_area > 0) {
3596 sprintf(id32_trackpath, "moov.trak[%u].meta", meta_area);
3597 meta_atom = APar_FindAtom(id32_trackpath, false, DUAL_STATE_ATOM, 0);
3598 }
3599
3600 if (meta_atom != NULL) {
3601 hdlr_atom = APar_FindChildAtom(meta_atom->AtomicNumber, "hdlr");
3602 if (hdlr_atom != NULL) {
3603 if (hdlr_atom->ancillary_data != 0x49443332) {
3604 memset(id32_trackpath,
3605 0,
3606 5); // well, it won't be used for anything else since the handler
3607 // type doesn't match, might as well convert the handler type
3608 // using it
3609 UInt32_TO_String4(hdlr_atom->ancillary_data, id32_trackpath);
3610 APar_assert(false, 7, id32_trackpath);
3611 free(id32_trackpath);
3612
3613 return NULL;
3614 }
3615 }
3616 }
3617
3618 // its possible the ID32 atom targeted already exists - finding it in the
3619 // traditional form (not external, and not locally referenced) is easiest.
3620 // Locally referenced isn't.
3621 if (meta_area == 0 - FILE_LEVEL_ATOM) {
3622 ID32_atom = APar_FindAtom("meta.ID32", false, PACKED_LANG_ATOM, id32_lang);
3623 } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) {
3624 ID32_atom =
3625 APar_FindAtom("moov.meta.ID32", false, PACKED_LANG_ATOM, id32_lang);
3626 //} else if (meta_area = 0) {
3627 // setting id3tags for all tracks will *not* be supported;
3628 } else if (meta_area > 0) {
3629 sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area);
3630 ID32_atom =
3631 APar_FindAtom(id32_trackpath, false, PACKED_LANG_ATOM, id32_lang);
3632 }
3633
3634 if (ID32_atom != NULL) {
3635 free(id32_trackpath);
3636 id32_trackpath = NULL;
3637 if (ID32_atom->ID32_TagInfo == NULL) {
3638 APar_ID32_ScanID3Tag(source_file, ID32_atom);
3639 }
3640 return ID32_atom; // and that completes finding the ID32 atom and verifying
3641 // that it was local and in a traditionally represented
3642 // form.
3643
3644 } else {
3645 if (meta_atom != NULL) {
3646 // if the primary item atom is present, it points to either a local data
3647 // reference (flag of 0x000001) or external data which is unsupported.
3648 // Either way skip it.
3649 //..or probably another test would be if a data REFERENCE atom were
3650 // present.... but you would have to match item_IDs - which are found in
3651 // pitm (required for ID32).
3652 if (APar_FindChildAtom(meta_atom->AtomicNumber, "pitm") != NULL) {
3653 non_referenced_data = false;
3654 APar_assert(false, 8, frameID_str);
3655 free(id32_trackpath);
3656 return NULL;
3657 } else { // the inline/3gpp 'ID32' atom calls for referenced content to
3658 // carry a 'pitm' atom. No worries - its just a 'meta'.
3659 non_referenced_data = true;
3660 }
3661 } else {
3662 // no 'meta' atom? Great - a blank slate. There won't be any jumping
3663 // through a multitude of atoms to determine referencing
3664 non_referenced_data = true;
3665 }
3666 }
3667
3668 if (frameID_str == NULL) {
3669 remove_ID32_atom = true;
3670 } else if (strlen(frameID_str) == 0) {
3671 remove_ID32_atom = true;
3672 }
3673 // this only gets executed if a pre-existing satisfactory ID32 atom was not
3674 // found. Being able to find it by atom.path by definition means it was not
3675 // referenced.
3676 if (non_referenced_data && !remove_ID32_atom) {
3677 if (meta_atom == NULL) {
3678 if (meta_area == 0 - FILE_LEVEL_ATOM) {
3679 meta_atom = APar_FindAtom("meta", true, VERSIONED_ATOM, 0);
3680 } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) {
3681 meta_atom = APar_FindAtom("moov.meta", true, VERSIONED_ATOM, 0);
3682 //} else if (meta_area = 0) {
3683 // setting id3tags for all tracks will *not* be supported;
3684 } else if (meta_area > 0) {
3685 sprintf(id32_trackpath, "moov.trak[%u].meta", meta_area);
3686 meta_atom = APar_FindAtom(id32_trackpath, true, VERSIONED_ATOM, 0);
3687 }
3688 }
3689
3690 // create the required hdlr atom
3691 if (hdlr_atom == NULL) {
3692 if (meta_area == 0 - FILE_LEVEL_ATOM) {
3693 hdlr_atom = APar_FindAtom("meta.hdlr", true, VERSIONED_ATOM, 0);
3694 } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) {
3695 hdlr_atom = APar_FindAtom("moov.meta.hdlr", true, VERSIONED_ATOM, 0);
3696 //} else if (meta_area = 0) {
3697 // setting id3tags for all tracks will *not* be supported;
3698 } else if (meta_area > 0) {
3699 sprintf(id32_trackpath, "moov.trak[%u].meta.hdlr", meta_area);
3700 hdlr_atom = APar_FindAtom(id32_trackpath, true, VERSIONED_ATOM, 0);
3701 }
3702 if (hdlr_atom == NULL) {
3703 fprintf(stdout, "Uh, problem\n");
3704 exit(0);
3705 }
3706 APar_MetaData_atom_QuickInit(hdlr_atom->AtomicNumber, 0, 0);
3707 APar_Unified_atom_Put(
3708 hdlr_atom, "ID32", UTF8_iTunesStyle_256glyphLimited, 0, 0);
3709 APar_Unified_atom_Put(
3710 hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
3711 APar_Unified_atom_Put(
3712 hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
3713 APar_Unified_atom_Put(
3714 hdlr_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 32);
3715 APar_Unified_atom_Put(
3716 hdlr_atom, "AtomicParsley ID3v2 Handler", UTF8_3GP_Style, 0, 0);
3717 hdlr_atom->ancillary_data = 0x49443332;
3718 }
3719
3720 // and finally create the ID32 atom
3721 if (meta_area == 0 - FILE_LEVEL_ATOM) {
3722 ID32_atom = APar_FindAtom("meta.ID32", true, PACKED_LANG_ATOM, id32_lang);
3723 } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) {
3724 ID32_atom =
3725 APar_FindAtom("moov.meta.ID32", true, PACKED_LANG_ATOM, id32_lang);
3726 //} else if (meta_area = 0) {
3727 // setting id3tags for all tracks will *not* be supported;
3728 } else if (meta_area > 0) {
3729 sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area);
3730 ID32_atom =
3731 APar_FindAtom(id32_trackpath, true, PACKED_LANG_ATOM, id32_lang);
3732 }
3733
3734 if (id32_trackpath != NULL) {
3735 free(id32_trackpath);
3736 id32_trackpath = NULL;
3737 }
3738
3739 ID32_atom->AtomicLength = 12; // 4bytes atom length, 4 bytes atom length, 4
3740 // bytes version/flags (NULLs), 2 bytes lang
3741 ID32_atom->AtomicVerFlags = 0;
3742 ID32_atom->AtomicContainerState = CHILD_ATOM;
3743 ID32_atom->AtomicClassification = PACKED_LANG_ATOM;
3744 ID32_atom->AtomicLanguage = id32_lang;
3745
3746 APar_ID3Tag_Init(ID32_atom);
3747 // search for the desired frame
3748
3749 // add the frame to an empty frame, copy data onto the id32_atom structure
3750 // as required set modified _atoms to true
3751
3752 return ID32_atom;
3753 } else if (remove_ID32_atom) {
3754 if (meta_area == 0 - FILE_LEVEL_ATOM) {
3755 APar_RemoveAtom("meta.ID32", PACKED_LANG_ATOM, id32_lang);
3756 } else if (meta_area == 0 - MOVIE_LEVEL_ATOM) {
3757 APar_RemoveAtom("moov.meta.ID32", PACKED_LANG_ATOM, id32_lang);
3758 //} else if (meta_area = 0) {
3759 // setting id3tags for all tracks will *not* be supported;
3760 } else if (meta_area > 0) {
3761 sprintf(id32_trackpath, "moov.trak[%u].meta.ID32", meta_area);
3762 APar_RemoveAtom(id32_trackpath, PACKED_LANG_ATOM, id32_lang);
3763 }
3764 }
3765 return NULL;
28863766 }
28873767
28883768 void APar_RenderAllID32Atoms() {
2889 short atom_idx = 0;
2890 short meta_idx = -1;
2891 //loop through each atom in the struct array (which holds the offset info/data)
2892 while (true) {
2893 if (memcmp(parsedAtoms[atom_idx].AtomicName, "ID32", 4) == 0) {
2894 if (parsedAtoms[atom_idx].ID32_TagInfo != NULL) {
2895 uint64_t id32tag_max_length = APar_GetTagSize(&parsedAtoms[atom_idx]);
2896 if (id32tag_max_length > 0) {
2897 parsedAtoms[atom_idx].AtomicData = (char*)calloc(1, sizeof(char)* id32tag_max_length + (16*parsedAtoms[atom_idx].ID32_TagInfo->ID3v2_FrameCount) );
2898 APar_Unified_atom_Put(&parsedAtoms[atom_idx], NULL, 0, parsedAtoms[atom_idx].AtomicLanguage, 16);
2899 parsedAtoms[atom_idx].AtomicLength = APar_Render_ID32_Tag(&parsedAtoms[atom_idx], id32tag_max_length + (16*parsedAtoms[atom_idx].ID32_TagInfo->ID3v2_FrameCount) ) + 14;
2900 if (parsedAtoms[atom_idx].AtomicLength < 12+10) {
2901 meta_idx = APar_FindParentAtom(parsedAtoms[atom_idx].AtomicNumber, parsedAtoms[atom_idx].AtomicLevel);
2902 APar_EliminateAtom(parsedAtoms[atom_idx].AtomicNumber, parsedAtoms[atom_idx].NextAtomNumber);
2903 } else {
2904 APar_FlagTrackHeader(&parsedAtoms[atom_idx]);
2905 }
2906 } else {
2907 meta_idx = APar_FindParentAtom(parsedAtoms[atom_idx].AtomicNumber, parsedAtoms[atom_idx].AtomicLevel);
2908 APar_EliminateAtom(parsedAtoms[atom_idx].AtomicNumber, parsedAtoms[atom_idx].NextAtomNumber);
2909 }
2910 }
2911 }
2912 if (meta_idx > 0) {
2913 if (memcmp(parsedAtoms[meta_idx].AtomicName, "meta", 4) == 0) {
2914 if ( APar_ReturnChildrenAtoms(meta_idx, 0) == 1 ) {
2915 AtomicInfo* meta_handler = &parsedAtoms[ parsedAtoms[meta_idx].NextAtomNumber ];
2916 if (memcmp(meta_handler->AtomicName, "hdlr", 4) == 0 && meta_handler->ancillary_data == 0x49443332) {
2917 APar_EliminateAtom(meta_idx, meta_handler->NextAtomNumber);
2918 }
2919 }
2920 }
2921 }
2922 atom_idx = parsedAtoms[atom_idx].NextAtomNumber;
2923 if (parsedAtoms[atom_idx].AtomicNumber == 0) break;
2924 meta_idx = -1;
2925 }
2926 return;
3769 short atom_idx = 0;
3770 short meta_idx = -1;
3771 // loop through each atom in the struct array (which holds the offset
3772 // info/data)
3773 while (true) {
3774 if (memcmp(parsedAtoms[atom_idx].AtomicName, "ID32", 4) == 0) {
3775 if (parsedAtoms[atom_idx].ID32_TagInfo != NULL) {
3776 uint64_t id32tag_max_length = APar_GetTagSize(&parsedAtoms[atom_idx]);
3777 if (id32tag_max_length > 0) {
3778 parsedAtoms[atom_idx].AtomicData = (char *)calloc(
3779 1,
3780 sizeof(char) * id32tag_max_length +
3781 (16 * parsedAtoms[atom_idx].ID32_TagInfo->ID3v2_FrameCount));
3782 APar_Unified_atom_Put(&parsedAtoms[atom_idx],
3783 NULL,
3784 0,
3785 parsedAtoms[atom_idx].AtomicLanguage,
3786 16);
3787 parsedAtoms[atom_idx].AtomicLength =
3788 APar_Render_ID32_Tag(
3789 &parsedAtoms[atom_idx],
3790 id32tag_max_length +
3791 (16 *
3792 parsedAtoms[atom_idx].ID32_TagInfo->ID3v2_FrameCount)) +
3793 14;
3794 if (parsedAtoms[atom_idx].AtomicLength < 12 + 10) {
3795 meta_idx = APar_FindParentAtom(parsedAtoms[atom_idx].AtomicNumber,
3796 parsedAtoms[atom_idx].AtomicLevel);
3797 APar_EliminateAtom(parsedAtoms[atom_idx].AtomicNumber,
3798 parsedAtoms[atom_idx].NextAtomNumber);
3799 } else {
3800 APar_FlagTrackHeader(&parsedAtoms[atom_idx]);
3801 }
3802 } else {
3803 meta_idx = APar_FindParentAtom(parsedAtoms[atom_idx].AtomicNumber,
3804 parsedAtoms[atom_idx].AtomicLevel);
3805 APar_EliminateAtom(parsedAtoms[atom_idx].AtomicNumber,
3806 parsedAtoms[atom_idx].NextAtomNumber);
3807 }
3808 }
3809 }
3810 if (meta_idx > 0) {
3811 if (memcmp(parsedAtoms[meta_idx].AtomicName, "meta", 4) == 0) {
3812 if (APar_ReturnChildrenAtoms(meta_idx, 0) == 1) {
3813 AtomicInfo *meta_handler =
3814 &parsedAtoms[parsedAtoms[meta_idx].NextAtomNumber];
3815 if (memcmp(meta_handler->AtomicName, "hdlr", 4) == 0 &&
3816 meta_handler->ancillary_data == 0x49443332) {
3817 APar_EliminateAtom(meta_idx, meta_handler->NextAtomNumber);
3818 }
3819 }
3820 }
3821 }
3822 atom_idx = parsedAtoms[atom_idx].NextAtomNumber;
3823 if (parsedAtoms[atom_idx].AtomicNumber == 0)
3824 break;
3825 meta_idx = -1;
3826 }
3827 return;
29273828 }
29283829
29293830 /*----------------------
29303831 APar_TestVideoDescription
2931 video_desc_atom - the avc1 atom after stsd that contains the height/width
2932 ISObmff_file - the reopened source file
2933
2934 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
2935 uuid to be written
2936
2937 NOTE: this requires the deep scan cli flag to break out stsd from its normal monolithic form
3832 video_desc_atom - the avc1 atom after stsd that contains the
3833 height/width ISObmff_file - the reopened source file
3834
3835 read in the height, width, profile & level of an avc (non-drm) track. If the
3836 the macroblocks are between 300 & 1200, return a non-zero number to allow the
3837 ipod uuid to be written
3838
3839 NOTE: this requires the deep scan cli flag to break out stsd from its normal
3840 monolithic form
29383841 ----------------------*/
2939 uint16_t APar_TestVideoDescription(AtomicInfo* video_desc_atom, FILE* ISObmff_file) {
2940 uint16_t video_width = 0;
2941 uint16_t video_height = 0;
2942 uint16_t video_macroblocks = 0;
2943 uint8_t video_profile = 0;
2944 uint8_t video_level = 0;
2945 AtomicInfo* avcC_atom = NULL;
2946
2947 if (ISObmff_file == NULL) return 0;
2948
2949 char* avc1_contents = (char*)calloc(1, sizeof(char)* (size_t)video_desc_atom->AtomicLength);
2950 if (avc1_contents == NULL) {
2951 fclose(ISObmff_file);
2952 return 0;
2953 }
2954
2955 APar_readX(avc1_contents, ISObmff_file, video_desc_atom->AtomicStart, video_desc_atom->AtomicLength); //actually reads in avcC as well, but is unused
2956 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
2957 video_height = UInt16FromBigEndian(avc1_contents+34);
2958 video_macroblocks = (video_width / 16) * (video_height / 16);
2959
2960 avcC_atom = APar_FindChildAtom(video_desc_atom->AtomicNumber, "avcC");
2961 if (avcC_atom != NULL) {
2962 uint64_t avcC_offset = avcC_atom->AtomicStart - video_desc_atom->AtomicStart;
2963 video_profile = *(avc1_contents+avcC_offset+9);
2964 video_level = *(avc1_contents+avcC_offset+11);
2965 }
2966
2967 if (video_profile == 66 && video_level <= 30) {
2968 if (video_macroblocks > 300 && video_macroblocks <= 1200) {
2969
2970 if (video_level <= 30 && avcC_atom != NULL) {
2971 avcC_atom->AtomicData = (char*)calloc(1, sizeof(char)* (size_t)avcC_atom->AtomicLength);
2972 APar_readX(avcC_atom->AtomicData, ISObmff_file, avcC_atom->AtomicStart+8, avcC_atom->AtomicLength-8);
2973 if (video_macroblocks > 396 && video_macroblocks <= 792) {
2974 *(avcC_atom->AtomicData + 3) = 21;
2975 } else if (video_macroblocks > 792) {
2976 *(avcC_atom->AtomicData + 3) = 22;
2977 }
2978 }
2979
2980 fclose(ISObmff_file);
2981 return video_macroblocks;
2982 } else {
2983 fprintf(stdout, "AtomicParsley warning: the AVC track macroblocks were not in the required range (300-1200). Skipping.\n");
2984 }
2985 } else {
2986 fprintf(stdout, "AtomicParsley warning: the AVC track profile/level was too high. The ipod hi-res uuid was not added.\n");
2987 }
2988
2989 fclose(ISObmff_file);
2990 return 0;
3842 uint16_t APar_TestVideoDescription(AtomicInfo *video_desc_atom,
3843 FILE *ISObmff_file) {
3844 uint16_t video_width = 0;
3845 uint16_t video_height = 0;
3846 uint16_t video_macroblocks = 0;
3847 uint8_t video_profile = 0;
3848 uint8_t video_level = 0;
3849 AtomicInfo *avcC_atom = NULL;
3850
3851 if (ISObmff_file == NULL)
3852 return 0;
3853
3854 char *avc1_contents =
3855 (char *)calloc(1, sizeof(char) * (size_t)video_desc_atom->AtomicLength);
3856 if (avc1_contents == NULL) {
3857 fclose(ISObmff_file);
3858 return 0;
3859 }
3860
3861 APar_readX(
3862 avc1_contents,
3863 ISObmff_file,
3864 video_desc_atom->AtomicStart,
3865 video_desc_atom
3866 ->AtomicLength); // actually reads in avcC as well, but is unused
3867 video_width = UInt16FromBigEndian(
3868 avc1_contents +
3869 32); // well, iTunes only allows 640 max but the avc wiki says it *could*
3870 // go up to 720, so I won't bother to check it
3871 video_height = UInt16FromBigEndian(avc1_contents + 34);
3872 video_macroblocks = (video_width / 16) * (video_height / 16);
3873
3874 avcC_atom = APar_FindChildAtom(video_desc_atom->AtomicNumber, "avcC");
3875 if (avcC_atom != NULL) {
3876 uint64_t avcC_offset =
3877 avcC_atom->AtomicStart - video_desc_atom->AtomicStart;
3878 video_profile = *(avc1_contents + avcC_offset + 9);
3879 video_level = *(avc1_contents + avcC_offset + 11);
3880 }
3881
3882 if (video_profile == 66 && video_level <= 30) {
3883 if (video_macroblocks > 300 && video_macroblocks <= 1200) {
3884
3885 if (video_level <= 30 && avcC_atom != NULL) {
3886 avcC_atom->AtomicData =
3887 (char *)calloc(1, sizeof(char) * (size_t)avcC_atom->AtomicLength);
3888 APar_readX(avcC_atom->AtomicData,
3889 ISObmff_file,
3890 avcC_atom->AtomicStart + 8,
3891 avcC_atom->AtomicLength - 8);
3892 if (video_macroblocks > 396 && video_macroblocks <= 792) {
3893 *(avcC_atom->AtomicData + 3) = 21;
3894 } else if (video_macroblocks > 792) {
3895 *(avcC_atom->AtomicData + 3) = 22;
3896 }
3897 }
3898
3899 fclose(ISObmff_file);
3900 return video_macroblocks;
3901 } else {
3902 fprintf(stdout,
3903 "AtomicParsley warning: the AVC track macroblocks were "
3904 "not in the required range (300-1200). Skipping.\n");
3905 }
3906 } else {
3907 fprintf(stdout,
3908 "AtomicParsley warning: the AVC track profile/level was "
3909 "too high. The ipod hi-res uuid was not added.\n");
3910 }
3911
3912 fclose(ISObmff_file);
3913 return 0;
29913914 }
29923915
29933916 /*----------------------
29943917 APar_TestVideoDescription
2995 atom_path - pointer to the string containing the atom.path already targeted to the right track
2996
2997 Find/Create the ipod hi-res (1200 macroblock) uuid for the avc1 track & set up its default parameters
2998
2999 NOTE: this requires the deep scan cli flag to break out stsd from its normal monolithic form
3918 atom_path - pointer to the string containing the atom.path already
3919 targeted to the right track
3920
3921 Find/Create the ipod hi-res (1200 macroblock) uuid for the avc1
3922 track & set up its default parameters
3923
3924 NOTE: this requires the deep scan cli flag to break out stsd from its normal
3925 monolithic form
30003926 ----------------------*/
3001 void APar_Generate_iPod_uuid(char* atom_path) {
3002 AtomicInfo* ipod_uuid_atom = NULL;
3003
3004 ipod_uuid_atom = APar_FindAtom(atom_path, false, EXTENDED_ATOM, 0, true);
3005 if (ipod_uuid_atom == NULL) {
3006 ipod_uuid_atom = APar_FindAtom(atom_path, true, EXTENDED_ATOM, 0, true);
3007 if (ipod_uuid_atom == NULL) {
3008 fprintf(stdout, "An error occured trying to create the ipod uuid atom for the avc track\n");
3009 return;
3010 }
3011 ipod_uuid_atom->AtomicData = (char*)calloc(1, sizeof(char)* 60 );
3012 ipod_uuid_atom->AtomicContainerState = CHILD_ATOM;
3013 ipod_uuid_atom->AtomicClassification = EXTENDED_ATOM;
3014 ipod_uuid_atom->uuid_style = UUID_OTHER;
3015 ipod_uuid_atom->AtomicLength = 24;
3016 APar_Unified_atom_Put(ipod_uuid_atom, NULL, UTF8_iTunesStyle_Unlimited, 1, 32);
3017 modified_atoms = true;
3018
3019 APar_FlagTrackHeader(ipod_uuid_atom);
3020 APar_FlagMovieHeader();
3021
3022 track_codecs.has_avc1 = true; //only used on Mac OS X when setting the ipod uuid *only* (otherwise it gets set properly)
3023 } else {
3024 fprintf(stdout, "the ipod higher-resolution uuid is already present.\n");
3025 }
3026
3027 return;
3927 void APar_Generate_iPod_uuid(char *atom_path) {
3928 AtomicInfo *ipod_uuid_atom = NULL;
3929
3930 ipod_uuid_atom = APar_FindAtom(atom_path, false, EXTENDED_ATOM, 0, true);
3931 if (ipod_uuid_atom == NULL) {
3932 ipod_uuid_atom = APar_FindAtom(atom_path, true, EXTENDED_ATOM, 0, true);
3933 if (ipod_uuid_atom == NULL) {
3934 fprintf(stdout,
3935 "An error occured trying to create the ipod uuid atom "
3936 "for the avc track\n");
3937 return;
3938 }
3939 ipod_uuid_atom->AtomicData = (char *)calloc(1, sizeof(char) * 60);
3940 ipod_uuid_atom->AtomicContainerState = CHILD_ATOM;
3941 ipod_uuid_atom->AtomicClassification = EXTENDED_ATOM;
3942 ipod_uuid_atom->uuid_style = UUID_OTHER;
3943 ipod_uuid_atom->AtomicLength = 24;
3944 APar_Unified_atom_Put(
3945 ipod_uuid_atom, NULL, UTF8_iTunesStyle_Unlimited, 1, 32);
3946 modified_atoms = true;
3947
3948 APar_FlagTrackHeader(ipod_uuid_atom);
3949 APar_FlagMovieHeader();
3950
3951 track_codecs.has_avc1 =
3952 true; // only used on Mac OS X when setting the ipod
3953 // uuid *only* (otherwise it gets set properly)
3954 } else {
3955 fprintf(stdout, "the ipod higher-resolution uuid is already present.\n");
3956 }
3957
3958 return;
30283959 }
30293960
30303961 ///////////////////////////////////////////////////////////////////////////////////////
3031 // offset calculations //
3962 // offset calculations //
30323963 ///////////////////////////////////////////////////////////////////////////////////////
30333964
3034 //determine if our mdat atom has moved at all...
3965 // determine if our mdat atom has moved at all...
30353966 uint64_t APar_DetermineMediaData_AtomPosition() {
3036 uint64_t mdat_position = 0;
3037 short thisAtomNumber = 0;
3038
3039 //loop through each atom in the struct array (which holds the offset info/data)
3040 while (parsedAtoms[thisAtomNumber].NextAtomNumber != 0) {
3041
3042 if ( strncmp(parsedAtoms[thisAtomNumber].AtomicName, "mdat", 4) == 0 && parsedAtoms[thisAtomNumber].AtomicLevel == 1 ) {
3043 if (parsedAtoms[thisAtomNumber].AtomicLength <= 1 || parsedAtoms[thisAtomNumber].AtomicLength > 75) {
3044 break;
3045 }
3046 } else if (parsedAtoms[thisAtomNumber].AtomicLevel == 1 && parsedAtoms[thisAtomNumber].AtomicLengthExtended == 0) {
3047 mdat_position +=parsedAtoms[thisAtomNumber].AtomicLength;
3048 } else {
3049 //part of the pseudo 64-bit support
3050 mdat_position +=parsedAtoms[thisAtomNumber].AtomicLengthExtended;
3051 }
3052 thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber;
3053 }
3054 return mdat_position;
3967 uint64_t mdat_position = 0;
3968 short thisAtomNumber = 0;
3969
3970 // loop through each atom in the struct array (which holds the offset
3971 // info/data)
3972 while (parsedAtoms[thisAtomNumber].NextAtomNumber != 0) {
3973
3974 if (strncmp(parsedAtoms[thisAtomNumber].AtomicName, "mdat", 4) == 0 &&
3975 parsedAtoms[thisAtomNumber].AtomicLevel == 1) {
3976 if (parsedAtoms[thisAtomNumber].AtomicLength <= 1 ||
3977 parsedAtoms[thisAtomNumber].AtomicLength > 75) {
3978 break;
3979 }
3980 } else if (parsedAtoms[thisAtomNumber].AtomicLevel == 1 &&
3981 parsedAtoms[thisAtomNumber].AtomicLengthExtended == 0) {
3982 mdat_position += parsedAtoms[thisAtomNumber].AtomicLength;
3983 } else {
3984 // part of the pseudo 64-bit support
3985 mdat_position += parsedAtoms[thisAtomNumber].AtomicLengthExtended;
3986 }
3987 thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber;
3988 }
3989 return mdat_position;
30553990 }
30563991
30573992 uint32_t APar_SimpleSumAtoms(short stop_atom) {
3058 uint32_t byte_sum = 0;
3059 //first, find the first mdat after this initial 'tfhd' atom to get the sum relative to that atom
3060 while (true) {
3061 if ( strncmp(parsedAtoms[stop_atom].AtomicName, "mdat", 4) == 0) {
3062 stop_atom--; //don't include the fragment's mdat, just the atoms prior to it
3063 break;
3064 } else {
3065 if (parsedAtoms[stop_atom].NextAtomNumber != 0) {
3066 stop_atom = parsedAtoms[stop_atom].NextAtomNumber;
3067 } else {
3068 break;
3069 }
3070 }
3071 }
3072 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)
3073 while (true) {
3074 if (parsedAtoms[stop_atom].AtomicLevel == 1) {
3075 byte_sum+= (parsedAtoms[stop_atom].AtomicLength == 1 ? parsedAtoms[stop_atom].AtomicLengthExtended : parsedAtoms[stop_atom].AtomicLength);
3076 //fprintf(stdout, "%i %s (%" PRIu64 ")\n", stop_atom, parsedAtoms[stop_atom].AtomicName, parsedAtoms[stop_atom].AtomicLength);
3077 }
3078 if (stop_atom == 0) {
3079 break;
3080 } else {
3081 stop_atom = APar_FindPrecedingAtom(stop_atom);
3082 }
3083 }
3084 return byte_sum;
3993 uint32_t byte_sum = 0;
3994 // first, find the first mdat after this initial 'tfhd' atom to get the sum
3995 // relative to that atom
3996 while (true) {
3997 if (strncmp(parsedAtoms[stop_atom].AtomicName, "mdat", 4) == 0) {
3998 stop_atom--; // don't include the fragment's mdat, just the atoms prior to
3999 // it
4000 break;
4001 } else {
4002 if (parsedAtoms[stop_atom].NextAtomNumber != 0) {
4003 stop_atom = parsedAtoms[stop_atom].NextAtomNumber;
4004 } else {
4005 break;
4006 }
4007 }
4008 }
4009 byte_sum +=
4010 8; // the 'tfhd' points to the byte in mdat where the fragment data is -
4011 // NOT the atom itself (should always be +8bytes with a fragment)
4012 while (true) {
4013 if (parsedAtoms[stop_atom].AtomicLevel == 1) {
4014 byte_sum += (parsedAtoms[stop_atom].AtomicLength == 1
4015 ? parsedAtoms[stop_atom].AtomicLengthExtended
4016 : parsedAtoms[stop_atom].AtomicLength);
4017 // fprintf(stdout, "%i %s (%" PRIu64 ")\n", stop_atom,
4018 // parsedAtoms[stop_atom].AtomicName,
4019 // parsedAtoms[stop_atom].AtomicLength);
4020 }
4021 if (stop_atom == 0) {
4022 break;
4023 } else {
4024 stop_atom = APar_FindPrecedingAtom(stop_atom);
4025 }
4026 }
4027 return byte_sum;
30854028 }
30864029
30874030 /*----------------------
30884031 APar_QuickSumAtomicLengths
3089 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
4032 target_atom - pointer to the atom that will have its position within the
4033 *new* file determined; NOTE: only level 1 or level 2 atoms in moov
30904034
30914035 fill
30924036 ----------------------*/
3093 uint64_t APar_QuickSumAtomicLengths(AtomicInfo* target_atom) {
3094 uint64_t atom_pos = 0;
3095 short atom_idx = 0;
3096 short current_level = 0;
3097 if (target_atom == NULL) return atom_pos;
3098 if (target_atom->AtomicLevel > 2) return atom_pos;
3099
3100 atom_idx = APar_FindPrecedingAtom(target_atom->AtomicNumber);
3101 current_level = target_atom->AtomicLevel;
3102
3103 while (true) {
3104 if (parsedAtoms[atom_idx].AtomicLevel <= target_atom->AtomicLevel) {
3105 if (parsedAtoms[atom_idx].AtomicContainerState >= DUAL_STATE_ATOM || parsedAtoms[atom_idx].AtomicLevel == 2) {
3106 atom_pos += (parsedAtoms[atom_idx].AtomicLength == 1 ? parsedAtoms[atom_idx].AtomicLengthExtended : parsedAtoms[atom_idx].AtomicLength);
3107 } else if (parsedAtoms[atom_idx].AtomicContainerState <= SIMPLE_PARENT_ATOM) {
3108 if (target_atom->AtomicLevel == 1) {
3109 atom_pos += (parsedAtoms[atom_idx].AtomicLength == 1 ? parsedAtoms[atom_idx].AtomicLengthExtended : parsedAtoms[atom_idx].AtomicLength);
3110 } else {
3111 atom_pos += 8;
3112 }
3113 }
3114 }
3115 if (atom_idx == 0) break;
3116 atom_idx = APar_FindPrecedingAtom(atom_idx);
3117 }
3118 return atom_pos;
4037 uint64_t APar_QuickSumAtomicLengths(AtomicInfo *target_atom) {
4038 uint64_t atom_pos = 0;
4039 short atom_idx = 0;
4040 short current_level = 0;
4041 if (target_atom == NULL)
4042 return atom_pos;
4043 if (target_atom->AtomicLevel > 2)
4044 return atom_pos;
4045
4046 atom_idx = APar_FindPrecedingAtom(target_atom->AtomicNumber);
4047 current_level = target_atom->AtomicLevel;
4048
4049 while (true) {
4050 if (parsedAtoms[atom_idx].AtomicLevel <= target_atom->AtomicLevel) {
4051 if (parsedAtoms[atom_idx].AtomicContainerState >= DUAL_STATE_ATOM ||
4052 parsedAtoms[atom_idx].AtomicLevel == 2) {
4053 atom_pos += (parsedAtoms[atom_idx].AtomicLength == 1
4054 ? parsedAtoms[atom_idx].AtomicLengthExtended
4055 : parsedAtoms[atom_idx].AtomicLength);
4056 } else if (parsedAtoms[atom_idx].AtomicContainerState <=
4057 SIMPLE_PARENT_ATOM) {
4058 if (target_atom->AtomicLevel == 1) {
4059 atom_pos += (parsedAtoms[atom_idx].AtomicLength == 1
4060 ? parsedAtoms[atom_idx].AtomicLengthExtended
4061 : parsedAtoms[atom_idx].AtomicLength);
4062 } else {
4063 atom_pos += 8;
4064 }
4065 }
4066 }
4067 if (atom_idx == 0)
4068 break;
4069 atom_idx = APar_FindPrecedingAtom(atom_idx);
4070 }
4071 return atom_pos;
31194072 }
31204073
31214074 /*----------------------
31224075 APar_Constituent_mdat_data
3123 desired_data_pos - the position in the file where the desired data begins
3124 desired_data_len - the length of the desired data contained within the file
4076 desired_data_pos - the position in the file where the desired data
4077 begins desired_data_len - the length of the desired data contained within the
4078 file
31254079
31264080 fill
31274081 ----------------------*/
3128 AtomicInfo* APar_Constituent_mdat_data(uint64_t desired_data_pos, uint64_t desired_data_len) {
3129 AtomicInfo* target_mdat = NULL;
3130 short eval_atom = 0;
3131
3132 while (parsedAtoms[eval_atom].NextAtomNumber != 0) {
3133
3134 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0 && parsedAtoms[eval_atom].AtomicLevel == 1 ) {
3135 if (parsedAtoms[eval_atom].AtomicLength == 1) {
3136 if ( (parsedAtoms[eval_atom].AtomicStart + parsedAtoms[eval_atom].AtomicLengthExtended >= desired_data_pos + desired_data_len) &&
3137 parsedAtoms[eval_atom].AtomicStart < desired_data_pos) {
3138 target_mdat = &parsedAtoms[eval_atom];
3139 break;
3140 }
3141 } else {
3142 if ( (parsedAtoms[eval_atom].AtomicStart + parsedAtoms[eval_atom].AtomicLength >= desired_data_pos + desired_data_len) &&
3143 parsedAtoms[eval_atom].AtomicStart < desired_data_pos) {
3144 target_mdat = &parsedAtoms[eval_atom];
3145 break;
3146 }
3147 }
3148 }
3149 eval_atom = parsedAtoms[eval_atom].NextAtomNumber;
3150 }
3151 return target_mdat;
4082 AtomicInfo *APar_Constituent_mdat_data(uint64_t desired_data_pos,
4083 uint64_t desired_data_len) {
4084 AtomicInfo *target_mdat = NULL;
4085 short eval_atom = 0;
4086
4087 while (parsedAtoms[eval_atom].NextAtomNumber != 0) {
4088
4089 if (memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0 &&
4090 parsedAtoms[eval_atom].AtomicLevel == 1) {
4091 if (parsedAtoms[eval_atom].AtomicLength == 1) {
4092 if ((parsedAtoms[eval_atom].AtomicStart +
4093 parsedAtoms[eval_atom].AtomicLengthExtended >=
4094 desired_data_pos + desired_data_len) &&
4095 parsedAtoms[eval_atom].AtomicStart < desired_data_pos) {
4096 target_mdat = &parsedAtoms[eval_atom];
4097 break;
4098 }
4099 } else {
4100 if ((parsedAtoms[eval_atom].AtomicStart +
4101 parsedAtoms[eval_atom].AtomicLength >=
4102 desired_data_pos + desired_data_len) &&
4103 parsedAtoms[eval_atom].AtomicStart < desired_data_pos) {
4104 target_mdat = &parsedAtoms[eval_atom];
4105 break;
4106 }
4107 }
4108 }
4109 eval_atom = parsedAtoms[eval_atom].NextAtomNumber;
4110 }
4111 return target_mdat;
31524112 }
31534113
31544114 /*----------------------
31554115 APar_Readjust_iloc_atom
3156 iloc_number - the target iloc atom index in parsedAtoms
4116 iloc_number - the target iloc atom index in parsedAtoms
31574117
31584118 fill
31594119 ----------------------*/
31604120 bool APar_Readjust_iloc_atom(short iloc_number) {
3161 bool iloc_changed = false;
3162
3163 parsedAtoms[iloc_number].AtomicData = (char*)calloc(1, sizeof(char)* (size_t)(parsedAtoms[iloc_number].AtomicLength) );
3164 APar_readX(parsedAtoms[iloc_number].AtomicData, source_file, parsedAtoms[iloc_number].AtomicStart+12, parsedAtoms[iloc_number].AtomicLength-12);
3165
3166 uint8_t offset_size = ( *parsedAtoms[iloc_number].AtomicData >> 4) & 0x0F;
3167 uint8_t length_size = *parsedAtoms[iloc_number].AtomicData & 0x0F;
3168 uint8_t base_offset_size = ( *(parsedAtoms[iloc_number].AtomicData+1) >> 4) & 0x0F;
3169 uint16_t item_count = UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData+2);
3170 uint64_t aggregate_offset = 4;
3171 char* base_offset_ptr = NULL;
4121 bool iloc_changed = false;
4122
4123 parsedAtoms[iloc_number].AtomicData = (char *)calloc(
4124 1, sizeof(char) * (size_t)(parsedAtoms[iloc_number].AtomicLength));
4125 APar_readX(parsedAtoms[iloc_number].AtomicData,
4126 source_file,
4127 parsedAtoms[iloc_number].AtomicStart + 12,
4128 parsedAtoms[iloc_number].AtomicLength - 12);
4129
4130 uint8_t offset_size = (*parsedAtoms[iloc_number].AtomicData >> 4) & 0x0F;
4131 uint8_t length_size = *parsedAtoms[iloc_number].AtomicData & 0x0F;
4132 uint8_t base_offset_size =
4133 (*(parsedAtoms[iloc_number].AtomicData + 1) >> 4) & 0x0F;
4134 uint16_t item_count =
4135 UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData + 2);
4136 uint64_t aggregate_offset = 4;
4137 char *base_offset_ptr = NULL;
31724138
31734139 #if defined(DEBUG_V)
3174 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);
4140 fprintf(stdout,
4141 "debug: AP_Readjust_iloc_atom Offset %X, len %X, base %X, item "
4142 "count %u\n",
4143 offset_size,
4144 length_size,
4145 base_offset_size,
4146 item_count);
31754147 #endif
31764148
3177 for (uint16_t an_item=1; an_item <= item_count; an_item++) {
4149 for (uint16_t an_item = 1; an_item <= item_count; an_item++) {
31784150 #ifdef DEBUG_V
3179 uint16_t an_item_ID =
4151 uint16_t an_item_ID =
31804152 #endif
3181 UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset);
3182 uint16_t a_data_ref_idx = UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset+2);
3183 uint64_t base_offset = 0;
3184 uint64_t curr_container_pos = 0;
3185 uint64_t extent_len_sum = 0;
3186
3187 aggregate_offset +=4;
3188
3189 if (a_data_ref_idx != 0) {
3190 continue;
3191 }
3192
3193 if (base_offset_size == 4 || base_offset_size == 8) {
3194 base_offset_ptr = parsedAtoms[iloc_number].AtomicData+aggregate_offset;
3195 if (base_offset_size == 4) {
3196 base_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset);
3197 aggregate_offset +=4;
3198 } else {
3199 base_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset);
3200 aggregate_offset +=8;
3201 }
3202 }
3203
3204 if (base_offset > 0) {
3205 uint16_t this_item_extent_count = UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset);
3206 aggregate_offset +=2;
3207
3208 for (uint16_t an_extent=1; an_extent <= this_item_extent_count; an_extent++) {
3209 uint64_t this_extent_offset = 0;
3210 uint64_t this_extent_length = 0;
3211
3212 if (offset_size == 4 || offset_size == 8) {
3213 if (offset_size == 4) {
3214 this_extent_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset);
3215 aggregate_offset +=4;
3216 } else {
3217 this_extent_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset);
3218 aggregate_offset +=8;
3219 }
3220 }
3221 if (length_size == 4 || length_size == 8) {
3222 if (length_size == 4) {
3223 this_extent_length = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset);
3224 aggregate_offset +=4;
3225 } else {
3226 this_extent_length = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData+aggregate_offset);
3227 aggregate_offset +=8;
3228 }
3229 extent_len_sum+= this_extent_length;
3230 }
3231 } //for loop extent
4153 UInt16FromBigEndian(parsedAtoms[iloc_number].AtomicData +
4154 aggregate_offset);
4155 uint16_t a_data_ref_idx = UInt16FromBigEndian(
4156 parsedAtoms[iloc_number].AtomicData + aggregate_offset + 2);
4157 uint64_t base_offset = 0;
4158 uint64_t curr_container_pos = 0;
4159 uint64_t extent_len_sum = 0;
4160
4161 aggregate_offset += 4;
4162
4163 if (a_data_ref_idx != 0) {
4164 continue;
4165 }
4166
4167 if (base_offset_size == 4 || base_offset_size == 8) {
4168 base_offset_ptr = parsedAtoms[iloc_number].AtomicData + aggregate_offset;
4169 if (base_offset_size == 4) {
4170 base_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData +
4171 aggregate_offset);
4172 aggregate_offset += 4;
4173 } else {
4174 base_offset = UInt32FromBigEndian(parsedAtoms[iloc_number].AtomicData +
4175 aggregate_offset);
4176 aggregate_offset += 8;
4177 }
4178 }
4179
4180 if (base_offset > 0) {
4181 uint16_t this_item_extent_count = UInt16FromBigEndian(
4182 parsedAtoms[iloc_number].AtomicData + aggregate_offset);
4183 aggregate_offset += 2;
4184
4185 for (uint16_t an_extent = 1; an_extent <= this_item_extent_count;
4186 an_extent++) {
4187 uint64_t this_extent_offset = 0;
4188 uint64_t this_extent_length = 0;
4189
4190 if (offset_size == 4 || offset_size == 8) {
4191 if (offset_size == 4) {
4192 this_extent_offset = UInt32FromBigEndian(
4193 parsedAtoms[iloc_number].AtomicData + aggregate_offset);
4194 aggregate_offset += 4;
4195 } else {
4196 this_extent_offset = UInt32FromBigEndian(
4197 parsedAtoms[iloc_number].AtomicData + aggregate_offset);
4198 aggregate_offset += 8;
4199 }
4200 }
4201 if (length_size == 4 || length_size == 8) {
4202 if (length_size == 4) {
4203 this_extent_length = UInt32FromBigEndian(
4204 parsedAtoms[iloc_number].AtomicData + aggregate_offset);
4205 aggregate_offset += 4;
4206 } else {
4207 this_extent_length = UInt32FromBigEndian(
4208 parsedAtoms[iloc_number].AtomicData + aggregate_offset);
4209 aggregate_offset += 8;
4210 }
4211 extent_len_sum += this_extent_length;
4212 }
4213 } // for loop extent
32324214
32334215 #if defined(DEBUG_V)
3234 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);
4216 fprintf(stdout,
4217 "debug: AP_Readjust_iloc_atom iloc's %u index at base offset: "
4218 "%" PRIu64 ", total bytes %" PRIu64 "\n",
4219 an_item_ID,
4220 base_offset,
4221 extent_len_sum);
32354222 #endif
3236 AtomicInfo* container_atom = APar_Constituent_mdat_data(base_offset, 0x013077 );
3237
3238 if (container_atom != NULL) {
3239 curr_container_pos = APar_QuickSumAtomicLengths(container_atom);
3240 uint64_t exisiting_offset_into_atom = base_offset - container_atom->AtomicStart;
3241 uint64_t new_item_offset = curr_container_pos + exisiting_offset_into_atom;
4223 AtomicInfo *container_atom =
4224 APar_Constituent_mdat_data(base_offset, 0x013077);
4225
4226 if (container_atom != NULL) {
4227 curr_container_pos = APar_QuickSumAtomicLengths(container_atom);
4228 uint64_t exisiting_offset_into_atom =
4229 base_offset - container_atom->AtomicStart;
4230 uint64_t new_item_offset =
4231 curr_container_pos + exisiting_offset_into_atom;
32424232
32434233 #if defined(DEBUG_V)
3244 fprintf(stdout, "debug: AP_Readjust_iloc_atom item is contained on mdat started @ %" PRIu64 " (now at %" PRIu64 ")\n", container_atom->AtomicStart, curr_container_pos);
3245 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);
4234 fprintf(stdout,
4235 "debug: AP_Readjust_iloc_atom item is contained on mdat "
4236 "started @ %" PRIu64 " (now at %" PRIu64 ")\n",
4237 container_atom->AtomicStart,
4238 curr_container_pos);
4239 fprintf(stdout,
4240 "debug: AP_Readjust_iloc_atom item is %" PRIu64
4241 " bytes offset into atom (was %" PRIu64 ", now %" PRIu64 ")\n",
4242 exisiting_offset_into_atom,
4243 base_offset,
4244 new_item_offset);
32464245 #endif
3247 if (base_offset_size == 4) {
3248 UInt32_TO_String4(new_item_offset, base_offset_ptr);
3249 } else {
3250 UInt64_TO_String8(new_item_offset, base_offset_ptr);
3251 }
3252 iloc_changed = true;
3253 }
3254 }
3255 }
3256
3257 return iloc_changed;
4246 if (base_offset_size == 4) {
4247 UInt32_TO_String4(new_item_offset, base_offset_ptr);
4248 } else {
4249 UInt64_TO_String8(new_item_offset, base_offset_ptr);
4250 }
4251 iloc_changed = true;
4252 }
4253 }
4254 }
4255
4256 return iloc_changed;
32584257 }
32594258
32604259 bool APar_Readjust_CO64_atom(uint64_t mdat_position, short co64_number) {
3261 bool co64_changed = false;
3262
3263 if (parsedAtoms[co64_number].ancillary_data != 1) {
3264 return co64_changed;
3265 }
3266
3267 parsedAtoms[co64_number].AtomicData = (char*)calloc(1, sizeof(char)* (size_t)(parsedAtoms[co64_number].AtomicLength) );
3268 APar_readX(parsedAtoms[co64_number].AtomicData, source_file, parsedAtoms[co64_number].AtomicStart+12, parsedAtoms[co64_number].AtomicLength-12);
3269
3270 parsedAtoms[co64_number].AtomicVerFlags = 0;
3271 bool deduct = false;
3272 //readjust
3273
3274 char* co64_entries = (char *)malloc(sizeof(char)*4 + 1);
3275 memset(co64_entries, 0, sizeof(char)*4 + 1);
3276
3277 memcpy(co64_entries, parsedAtoms[co64_number].AtomicData, 4);
3278 uint32_t entries = UInt32FromBigEndian(co64_entries);
3279
3280 char* a_64bit_entry = (char *)malloc(sizeof(char)*8 + 1);
3281 memset(a_64bit_entry, 0, sizeof(char)*8 + 1);
3282
3283 for(uint32_t i=1; i<=entries; i++) {
3284 //read 8 bytes of the atom into a 8 char uint64_t a_64bit_entry to eval it
3285 for (int c = 0; c <=7; c++ ) {
3286 //first co64 entry (32-bit uint32_t) is the number of entries; every other one is an actual offset value
3287 a_64bit_entry[c] = parsedAtoms[co64_number].AtomicData[4 + (i-1)*8 + c];
3288 }
3289 uint64_t this_entry = UInt64FromBigEndian(a_64bit_entry);
3290
3291 if (i == 1 && mdat_supplemental_offset == 0) { //for the first chunk, and only for the first *ever* entry, make the global mdat supplemental offset
3292 if (this_entry - removed_bytes_tally > mdat_position) {
3293 mdat_supplemental_offset = (uint64_t)mdat_position - ((uint64_t)this_entry - (uint64_t)removed_bytes_tally);
3294 bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally;
3295 deduct=true;
3296 } else {
3297 mdat_supplemental_offset = mdat_position - (this_entry - removed_bytes_tally);
3298 bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally;
3299 }
3300
3301 if (mdat_supplemental_offset == 0) {
3302 break;
3303 }
3304 }
3305
3306 if (mdat_supplemental_offset != 0) {
3307 co64_changed = true;
3308 }
3309
3310 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
3311 this_entry += mdat_supplemental_offset - (bytes_into_mdat * -1); // + bytes_into_mdat;
3312 } else {
3313 this_entry += mdat_supplemental_offset + bytes_into_mdat; //this is where we add our new mdat offset difference
3314 }
3315 UInt64_TO_String8(this_entry, a_64bit_entry);
3316 //and put the data back into AtomicData...
3317 for (int d = 0; d <=7; d++ ) {
3318 //first stco entry is the number of entries; every other one is an actual offset value
3319 parsedAtoms[co64_number].AtomicData[4 + (i-1)*8 + d] = a_64bit_entry[d];
3320 }
3321 }
3322
3323 free(a_64bit_entry);
3324 free(co64_entries);
3325 a_64bit_entry=NULL;
3326 co64_entries=NULL;
3327 //end readjustment
3328 return co64_changed;
3329 }
3330
3331 bool APar_Readjust_TFHD_fragment_atom(uint64_t mdat_position, short tfhd_number) {
3332 static bool tfhd_changed = false;
3333 static bool determined_offset = false;
3334 static uint64_t base_offset = 0;
3335
3336 parsedAtoms[tfhd_number].AtomicData = (char*)calloc(1, sizeof(char)* (size_t)(parsedAtoms[tfhd_number].AtomicLength) );
3337 APar_readX(parsedAtoms[tfhd_number].AtomicData, source_file, parsedAtoms[tfhd_number].AtomicStart+12, parsedAtoms[tfhd_number].AtomicLength-12);
3338
3339 char* tfhd_atomFlags_scrap = (char *)malloc(sizeof(char)*10);
3340 memset(tfhd_atomFlags_scrap, 0, 10);
3341 //parsedAtoms[tfhd_number].AtomicVerFlags = APar_read32(tfhd_atomFlags_scrap, source_file, parsedAtoms[tfhd_number].AtomicStart+8);
3342
3343 if (parsedAtoms[tfhd_number].AtomicVerFlags & 0x01) {
3344 // seems the atomflags suggest bitpacking, but the spec doesn't specify it;
3345 // if the 1st bit is set...
4260 bool co64_changed = false;
4261
4262 if (parsedAtoms[co64_number].ancillary_data != 1) {
4263 return co64_changed;
4264 }
4265
4266 parsedAtoms[co64_number].AtomicData = (char *)calloc(
4267 1, sizeof(char) * (size_t)(parsedAtoms[co64_number].AtomicLength));
4268 APar_readX(parsedAtoms[co64_number].AtomicData,
4269 source_file,
4270 parsedAtoms[co64_number].AtomicStart + 12,
4271 parsedAtoms[co64_number].AtomicLength - 12);
4272
4273 parsedAtoms[co64_number].AtomicVerFlags = 0;
4274 bool deduct = false;
4275 // readjust
4276
4277 char *co64_entries = (char *)malloc(sizeof(char) * 4 + 1);
4278 memset(co64_entries, 0, sizeof(char) * 4 + 1);
4279
4280 memcpy(co64_entries, parsedAtoms[co64_number].AtomicData, 4);
4281 uint32_t entries = UInt32FromBigEndian(co64_entries);
4282
4283 char *a_64bit_entry = (char *)malloc(sizeof(char) * 8 + 1);
4284 memset(a_64bit_entry, 0, sizeof(char) * 8 + 1);
4285
4286 for (uint32_t i = 1; i <= entries; i++) {
4287 // read 8 bytes of the atom into a 8 char uint64_t a_64bit_entry to eval it
4288 for (int c = 0; c <= 7; c++) {
4289 // first co64 entry (32-bit uint32_t) is the number of entries; every
4290 // other one is an actual offset value
4291 a_64bit_entry[c] =
4292 parsedAtoms[co64_number].AtomicData[4 + (i - 1) * 8 + c];
4293 }
4294 uint64_t this_entry = UInt64FromBigEndian(a_64bit_entry);
4295
4296 if (i == 1 && mdat_supplemental_offset ==
4297 0) { // for the first chunk, and only for the first *ever*
4298 // entry, make the global mdat supplemental offset
4299 if (this_entry - removed_bytes_tally > mdat_position) {
4300 mdat_supplemental_offset =
4301 (uint64_t)mdat_position -
4302 ((uint64_t)this_entry - (uint64_t)removed_bytes_tally);
4303 bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally;
4304 deduct = true;
4305 } else {
4306 mdat_supplemental_offset =
4307 mdat_position - (this_entry - removed_bytes_tally);
4308 bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally;
4309 }
4310
4311 if (mdat_supplemental_offset == 0) {
4312 break;
4313 }
4314 }
4315
4316 if (mdat_supplemental_offset != 0) {
4317 co64_changed = true;
4318 }
4319
4320 if (deduct) { // crap, uint32_t's were so nice to flip over by themselves to
4321 // subtract nicely. going from 32-bit to 64-bit prevents that
4322 // flipping
4323 this_entry += mdat_supplemental_offset -
4324 (bytes_into_mdat * -1); // + bytes_into_mdat;
4325 } else {
4326 this_entry += mdat_supplemental_offset +
4327 bytes_into_mdat; // this is where we add our new mdat offset
4328 // difference
4329 }
4330 UInt64_TO_String8(this_entry, a_64bit_entry);
4331 // and put the data back into AtomicData...
4332 for (int d = 0; d <= 7; d++) {
4333 // first stco entry is the number of entries; every other one is an actual
4334 // offset value
4335 parsedAtoms[co64_number].AtomicData[4 + (i - 1) * 8 + d] =
4336 a_64bit_entry[d];
4337 }
4338 }
4339
4340 free(a_64bit_entry);
4341 free(co64_entries);
4342 a_64bit_entry = NULL;
4343 co64_entries = NULL;
4344 // end readjustment
4345 return co64_changed;
4346 }
4347
4348 bool APar_Readjust_TFHD_fragment_atom(uint64_t mdat_position,
4349 short tfhd_number) {
4350 static bool tfhd_changed = false;
4351 static bool determined_offset = false;
4352 static uint64_t base_offset = 0;
4353
4354 parsedAtoms[tfhd_number].AtomicData = (char *)calloc(
4355 1, sizeof(char) * (size_t)(parsedAtoms[tfhd_number].AtomicLength));
4356 APar_readX(parsedAtoms[tfhd_number].AtomicData,
4357 source_file,
4358 parsedAtoms[tfhd_number].AtomicStart + 12,
4359 parsedAtoms[tfhd_number].AtomicLength - 12);
4360
4361 char *tfhd_atomFlags_scrap = (char *)malloc(sizeof(char) * 10);
4362 memset(tfhd_atomFlags_scrap, 0, 10);
4363 // parsedAtoms[tfhd_number].AtomicVerFlags = APar_read32(tfhd_atomFlags_scrap,
4364 // source_file, parsedAtoms[tfhd_number].AtomicStart+8);
4365
4366 if (parsedAtoms[tfhd_number].AtomicVerFlags & 0x01) {
4367 // seems the atomflags suggest bitpacking, but the spec doesn't specify it;
4368 // if the 1st bit is set...
33464369 #if 0 /* not used */
33474370 memset(tfhd_atomFlags_scrap, 0, 10);
33484371 memcpy(tfhd_atomFlags_scrap, parsedAtoms[tfhd_number].AtomicData, 4);
33494372
33504373 uint32_t track_ID = UInt32FromBigEndian(tfhd_atomFlags_scrap); //unused
33514374 #endif
3352 uint64_t tfhd_offset = UInt64FromBigEndian(parsedAtoms[tfhd_number].AtomicData +4);
3353
3354 if (!determined_offset) {
3355 determined_offset = true;
3356 base_offset = APar_SimpleSumAtoms(tfhd_number) - tfhd_offset;
3357 if (base_offset != 0) {
3358 tfhd_changed = true;
3359 }
3360 }
3361
3362 tfhd_offset += base_offset;
3363 UInt64_TO_String8(tfhd_offset, parsedAtoms[tfhd_number].AtomicData +4);
3364 }
3365 return tfhd_changed;
4375 uint64_t tfhd_offset =
4376 UInt64FromBigEndian(parsedAtoms[tfhd_number].AtomicData + 4);
4377
4378 if (!determined_offset) {
4379 determined_offset = true;
4380 base_offset = APar_SimpleSumAtoms(tfhd_number) - tfhd_offset;
4381 if (base_offset != 0) {
4382 tfhd_changed = true;
4383 }
4384 }
4385
4386 tfhd_offset += base_offset;
4387 UInt64_TO_String8(tfhd_offset, parsedAtoms[tfhd_number].AtomicData + 4);
4388 }
4389 return tfhd_changed;
33664390 }
33674391
33684392 bool APar_Readjust_STCO_atom(uint64_t mdat_position, short stco_number) {
3369 bool stco_changed = false;
3370
3371 if (parsedAtoms[stco_number].ancillary_data != 1) {
3372 return stco_changed;
3373 }
3374
3375 parsedAtoms[stco_number].AtomicData = (char*)calloc(1, sizeof(char)* (size_t)(parsedAtoms[stco_number].AtomicLength) );
3376 APar_readX(parsedAtoms[stco_number].AtomicData, source_file, parsedAtoms[stco_number].AtomicStart+12, parsedAtoms[stco_number].AtomicLength-12);
3377
3378 parsedAtoms[stco_number].AtomicVerFlags = 0;
3379 //readjust
3380
3381 char* stco_entries = (char *)malloc(sizeof(char)*4 + 1);
3382 memset(stco_entries, 0, sizeof(char)*4 + 1);
3383
3384 memcpy(stco_entries, parsedAtoms[stco_number].AtomicData, 4);
3385 uint32_t entries = UInt32FromBigEndian(stco_entries);
3386
3387 char* an_entry = (char *)malloc(sizeof(char)*4 + 1);
3388 memset(an_entry, 0, sizeof(char)*4 + 1);
3389
3390 for(uint32_t i=1; i<=entries; i++) {
3391 //read 4 bytes of the atom into a 4 char uint32_t an_entry to eval it
3392 for (int c = 0; c <=3; c++ ) {
3393 //first stco entry is the number of entries; every other one is an actual offset value
3394 an_entry[c] = parsedAtoms[stco_number].AtomicData[i*4 + c];
3395 }
3396
3397 uint32_t this_entry = UInt32FromBigEndian(an_entry);
3398
3399 if (i == 1 && mdat_supplemental_offset == 0) { //for the first chunk, and only for the first *ever* entry, make the global mdat supplemental offset
3400
3401 mdat_supplemental_offset = (uint64_t)(mdat_position - (this_entry - removed_bytes_tally) );
3402 bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally;
3403
3404 if (mdat_supplemental_offset == 0) {
3405 break;
3406 }
3407 }
3408
3409 if (mdat_supplemental_offset != 0) {
3410 stco_changed = true;
3411 }
3412
3413 this_entry += mdat_supplemental_offset + bytes_into_mdat;
3414 UInt32_TO_String4(this_entry, an_entry);
3415 //and put the data back into AtomicData...
3416 for (int d = 0; d <=3; d++ ) {
3417 //first stco entry is the number of entries; every other one is an actual offset value
3418 parsedAtoms[stco_number].AtomicData[i*4 + d] = an_entry[d];
3419 }
3420 }
3421
3422 free(an_entry);
3423 free(stco_entries);
3424 an_entry=NULL;
3425 stco_entries=NULL;
3426 //end readjustment
3427 return stco_changed;
4393 bool stco_changed = false;
4394
4395 if (parsedAtoms[stco_number].ancillary_data != 1) {
4396 return stco_changed;
4397 }
4398
4399 parsedAtoms[stco_number].AtomicData = (char *)calloc(
4400 1, sizeof(char) * (size_t)(parsedAtoms[stco_number].AtomicLength));
4401 APar_readX(parsedAtoms[stco_number].AtomicData,
4402 source_file,
4403 parsedAtoms[stco_number].AtomicStart + 12,
4404 parsedAtoms[stco_number].AtomicLength - 12);
4405
4406 parsedAtoms[stco_number].AtomicVerFlags = 0;
4407 // readjust
4408
4409 char *stco_entries = (char *)malloc(sizeof(char) * 4 + 1);
4410 memset(stco_entries, 0, sizeof(char) * 4 + 1);
4411
4412 memcpy(stco_entries, parsedAtoms[stco_number].AtomicData, 4);
4413 uint32_t entries = UInt32FromBigEndian(stco_entries);
4414
4415 char *an_entry = (char *)malloc(sizeof(char) * 4 + 1);
4416 memset(an_entry, 0, sizeof(char) * 4 + 1);
4417
4418 for (uint32_t i = 1; i <= entries; i++) {
4419 // read 4 bytes of the atom into a 4 char uint32_t an_entry to eval it
4420 for (int c = 0; c <= 3; c++) {
4421 // first stco entry is the number of entries; every other one is an actual
4422 // offset value
4423 an_entry[c] = parsedAtoms[stco_number].AtomicData[i * 4 + c];
4424 }
4425
4426 uint32_t this_entry = UInt32FromBigEndian(an_entry);
4427
4428 if (i == 1 && mdat_supplemental_offset ==
4429 0) { // for the first chunk, and only for the first *ever*
4430 // entry, make the global mdat supplemental offset
4431
4432 mdat_supplemental_offset =
4433 (uint64_t)(mdat_position - (this_entry - removed_bytes_tally));
4434 bytes_into_mdat = this_entry - bytes_before_mdat - removed_bytes_tally;
4435
4436 if (mdat_supplemental_offset == 0) {
4437 break;
4438 }
4439 }
4440
4441 if (mdat_supplemental_offset != 0) {
4442 stco_changed = true;
4443 }
4444
4445 this_entry += mdat_supplemental_offset + bytes_into_mdat;
4446 UInt32_TO_String4(this_entry, an_entry);
4447 // and put the data back into AtomicData...
4448 for (int d = 0; d <= 3; d++) {
4449 // first stco entry is the number of entries; every other one is an actual
4450 // offset value
4451 parsedAtoms[stco_number].AtomicData[i * 4 + d] = an_entry[d];
4452 }
4453 }
4454
4455 free(an_entry);
4456 free(stco_entries);
4457 an_entry = NULL;
4458 stco_entries = NULL;
4459 // end readjustment
4460 return stco_changed;
34284461 }
34294462
34304463 ///////////////////////////////////////////////////////////////////////////////////////
3431 // Reorder / Padding //
4464 // Reorder / Padding //
34324465 ///////////////////////////////////////////////////////////////////////////////////////
34334466
34344467 /*----------------------
34354468 APar_CreatePadding
3436 padding_length - the new length of padding
3437
3438 Create a 'free' atom at a pre-determined area of a given length & set that atom as the global padding store atom
4469 padding_length - the new length of padding
4470
4471 Create a 'free' atom at a pre-determined area of a given length & set that
4472 atom as the global padding store atom
34394473 ----------------------*/
34404474 void APar_CreatePadding(uint64_t padding_length) {
3441 AtomicInfo* next_atom = &parsedAtoms[ parsedAtoms[dynUpd.consolidated_padding_insertion].NextAtomNumber ];
3442 if (padding_length > 2000 && next_atom->AtomicLevel > 1) {
3443 short padding_atom = APar_InterjectNewAtom("free", CHILD_ATOM, SIMPLE_ATOM, 2000, 0, 0,
3444 (next_atom->AtomicLevel == 1 ? 1 : parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicLevel), dynUpd.consolidated_padding_insertion );
3445 dynUpd.padding_store = &parsedAtoms[padding_atom];
3446
3447 if (dynUpd.first_mdat_atom != NULL && padding_length - 2000 >= 8) {
3448 short padding_res_atom = APar_InterjectNewAtom("free", CHILD_ATOM, SIMPLE_ATOM, padding_length - 2000, 0, 0, 1,
3449 APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber) );
3450 dynUpd.padding_resevoir = &parsedAtoms[padding_res_atom];
3451 }
3452 } else {
3453 short padding_atom = APar_InterjectNewAtom("free", CHILD_ATOM, SIMPLE_ATOM, padding_length, 0, 0,
3454 (next_atom->AtomicLevel == 1 ? 1 : parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicLevel), dynUpd.consolidated_padding_insertion );
3455 dynUpd.padding_store = &parsedAtoms[padding_atom];
3456 }
3457 return;
4475 AtomicInfo *next_atom =
4476 &parsedAtoms[parsedAtoms[dynUpd.consolidated_padding_insertion]
4477 .NextAtomNumber];
4478 if (padding_length > 2000 && next_atom->AtomicLevel > 1) {
4479 short padding_atom = APar_InterjectNewAtom(
4480 "free",
4481 CHILD_ATOM,
4482 SIMPLE_ATOM,
4483 2000,
4484 0,
4485 0,
4486 (next_atom->AtomicLevel == 1
4487 ? 1
4488 : parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicLevel),
4489 dynUpd.consolidated_padding_insertion);
4490 dynUpd.padding_store = &parsedAtoms[padding_atom];
4491
4492 if (dynUpd.first_mdat_atom != NULL && padding_length - 2000 >= 8) {
4493 short padding_res_atom = APar_InterjectNewAtom(
4494 "free",
4495 CHILD_ATOM,
4496 SIMPLE_ATOM,
4497 padding_length - 2000,
4498 0,
4499 0,
4500 1,
4501 APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber));
4502 dynUpd.padding_resevoir = &parsedAtoms[padding_res_atom];
4503 }
4504 } else {
4505 short padding_atom = APar_InterjectNewAtom(
4506 "free",
4507 CHILD_ATOM,
4508 SIMPLE_ATOM,
4509 padding_length,
4510 0,
4511 0,
4512 (next_atom->AtomicLevel == 1
4513 ? 1
4514 : parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicLevel),
4515 dynUpd.consolidated_padding_insertion);
4516 dynUpd.padding_store = &parsedAtoms[padding_atom];
4517 }
4518 return;
34584519 }
34594520
34604521 /*----------------------
34614522 APar_AdjustPadding
3462 new_padding_length - the new length of padding
3463
3464 Adjust the consolidated padding store atom to the new size - creating&splitting if necessary.
4523 new_padding_length - the new length of padding
4524
4525 Adjust the consolidated padding store atom to the new size -
4526 creating&splitting if necessary.
34654527 ----------------------*/
34664528 void APar_AdjustPadding(uint64_t new_padding_length) {
3467 uint64_t avail_padding = 0;
3468
3469 if ( (psp_brand || force_existing_hierarchy) && (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) ) return;
3470
3471 if (dynUpd.padding_store == NULL) {
3472 if (new_padding_length >= 8) {
3473 APar_CreatePadding(new_padding_length);
3474 } else {
3475 return;
3476 }
3477 }
3478
3479 if (new_padding_length < 8) {
3480 APar_EliminateAtom(dynUpd.padding_store->AtomicNumber, dynUpd.padding_store->NextAtomNumber);
3481 dynUpd.updage_by_padding = false;
3482 }
3483
3484 if (dynUpd.padding_store != NULL) avail_padding += dynUpd.padding_store->AtomicLength;
3485 if (dynUpd.padding_resevoir != NULL) avail_padding += dynUpd.padding_resevoir->AtomicLength;
3486
3487 if ((new_padding_length > avail_padding && new_padding_length < 2000) || dynUpd.padding_store->AtomicLevel == 1 ) {
3488 free(dynUpd.padding_store->AtomicData);
3489 dynUpd.padding_store->AtomicData = (char*)calloc(1, sizeof(char)*new_padding_length );
3490 dynUpd.padding_store->AtomicLength = new_padding_length;
3491 } else {
3492 free(dynUpd.padding_store->AtomicData);
3493 dynUpd.padding_store->AtomicData = (char*)calloc(1, sizeof(char)*2007 );
3494 dynUpd.padding_store->AtomicLength = 2000;
3495
3496 /* since new_padding_length is an unsigned value, if its value is less than
3497 * 1992, subtraction of 2000 from it will result in "underflow". That is
3498 * what is causing the reported issue of atom detected larger than
3499 * filesize.
3500 */
3501 if (new_padding_length < 2008) {
3502 dynUpd.padding_store->AtomicLength = new_padding_length;
3503 return;
3504 }
3505
3506 if (dynUpd.padding_resevoir == NULL && dynUpd.first_mdat_atom != NULL) {
3507 short pad_res_atom = APar_InterjectNewAtom("free", CHILD_ATOM, SIMPLE_ATOM, new_padding_length - 2000, 0, 0, 1,
3508 APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber) );
3509 dynUpd.padding_resevoir = &parsedAtoms[pad_res_atom];
3510 dynUpd.padding_resevoir->AtomicLength = new_padding_length - 2000;
3511 } else if (dynUpd.padding_resevoir != NULL) {
3512 free(dynUpd.padding_resevoir->AtomicData);
3513 dynUpd.padding_resevoir->AtomicData = (char*)calloc(1, sizeof(char)*(new_padding_length - 2000) );
3514 dynUpd.padding_resevoir->AtomicLength = new_padding_length - 2000;
3515 }
3516 }
3517
3518 return;
4529 uint64_t avail_padding = 0;
4530
4531 if ((psp_brand || force_existing_hierarchy) &&
4532 (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV))
4533 return;
4534
4535 if (dynUpd.padding_store == NULL) {
4536 if (new_padding_length >= 8) {
4537 APar_CreatePadding(new_padding_length);
4538 } else {
4539 return;
4540 }
4541 }
4542
4543 if (new_padding_length < 8) {
4544 APar_EliminateAtom(dynUpd.padding_store->AtomicNumber,
4545 dynUpd.padding_store->NextAtomNumber);
4546 dynUpd.updage_by_padding = false;
4547 }
4548
4549 if (dynUpd.padding_store != NULL)
4550 avail_padding += dynUpd.padding_store->AtomicLength;
4551 if (dynUpd.padding_resevoir != NULL)
4552 avail_padding += dynUpd.padding_resevoir->AtomicLength;
4553
4554 if ((new_padding_length > avail_padding && new_padding_length < 2000) ||
4555 dynUpd.padding_store->AtomicLevel == 1) {
4556 free(dynUpd.padding_store->AtomicData);
4557 dynUpd.padding_store->AtomicData =
4558 (char *)calloc(1, sizeof(char) * new_padding_length);
4559 dynUpd.padding_store->AtomicLength = new_padding_length;
4560 } else {
4561 free(dynUpd.padding_store->AtomicData);
4562 dynUpd.padding_store->AtomicData = (char *)calloc(1, sizeof(char) * 2007);
4563 dynUpd.padding_store->AtomicLength = 2000;
4564
4565 /* since new_padding_length is an unsigned value, if its value is less than
4566 * 1992, subtraction of 2000 from it will result in "underflow". That is
4567 * what is causing the reported issue of atom detected larger than
4568 * filesize.
4569 */
4570 if (new_padding_length < 2008) {
4571 dynUpd.padding_store->AtomicLength = new_padding_length;
4572 return;
4573 }
4574
4575 if (dynUpd.padding_resevoir == NULL && dynUpd.first_mdat_atom != NULL) {
4576 short pad_res_atom = APar_InterjectNewAtom(
4577 "free",
4578 CHILD_ATOM,
4579 SIMPLE_ATOM,
4580 new_padding_length - 2000,
4581 0,
4582 0,
4583 1,
4584 APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber));
4585 dynUpd.padding_resevoir = &parsedAtoms[pad_res_atom];
4586 dynUpd.padding_resevoir->AtomicLength = new_padding_length - 2000;
4587 } else if (dynUpd.padding_resevoir != NULL) {
4588 free(dynUpd.padding_resevoir->AtomicData);
4589 dynUpd.padding_resevoir->AtomicData =
4590 (char *)calloc(1, sizeof(char) * (new_padding_length - 2000));
4591 dynUpd.padding_resevoir->AtomicLength = new_padding_length - 2000;
4592 }
4593 }
4594
4595 return;
35194596 }
35204597
35214598 /*----------------------
35224599 APar_PaddingAmount
3523 target_amount - the amount of padding that is requested
3524 limit_by_prefs - whether to limit the amount of padding to the environmental preferences
3525
3526 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
3527 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
3528 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.
4600 target_amount - the amount of padding that is requested
4601 limit_by_prefs - whether to limit the amount of padding to the
4602 environmental preferences
4603
4604 When a file will completely rewritten, limit the amount of padding according
4605 to the environmental prefs: min & max with a default amount substitued when
4606 padding is found to fall outside those values. When a file will be updated by
4607 dynamic updating, the consolidated padding will be initially set to the amount
4608 that was present before any modifications to tags. This amount of padding will
4609 in all likelyhood change when the final determination of whether a dynamic
4610 update can occur.
35294611 ----------------------*/
35304612 uint64_t APar_PaddingAmount(uint64_t target_amount, bool limit_by_prefs) {
3531 uint64_t padding_allowance = 0;
3532 if (limit_by_prefs) {
3533 if (pad_prefs.maximum_present_padding_size == 0) {
3534 return 0;
3535 } else if (target_amount >= pad_prefs.maximum_present_padding_size) {
3536 padding_allowance = pad_prefs.maximum_present_padding_size;
3537 } else if (target_amount <= pad_prefs.minimum_required_padding_size) {
3538 padding_allowance = pad_prefs.default_padding_size;
3539 } else {
3540 padding_allowance = target_amount;
3541 }
3542 } else {
3543 padding_allowance = target_amount;
3544 }
3545 if (padding_allowance < 8 ) return 0;
3546 if (!alter_original) return pad_prefs.default_padding_size;
3547 return padding_allowance;
4613 uint64_t padding_allowance = 0;
4614 if (limit_by_prefs) {
4615 if (pad_prefs.maximum_present_padding_size == 0) {
4616 return 0;
4617 } else if (target_amount >= pad_prefs.maximum_present_padding_size) {
4618 padding_allowance = pad_prefs.maximum_present_padding_size;
4619 } else if (target_amount <= pad_prefs.minimum_required_padding_size) {
4620 padding_allowance = pad_prefs.default_padding_size;
4621 } else {
4622 padding_allowance = target_amount;
4623 }
4624 } else {
4625 padding_allowance = target_amount;
4626 }
4627 if (padding_allowance < 8)
4628 return 0;
4629 if (!alter_original)
4630 return pad_prefs.default_padding_size;
4631 return padding_allowance;
35484632 }
35494633
35504634 /*----------------------
35514635 APar_DetermineDynamicUpdate
35524636
3553 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
3554 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
3555 exists for the file for a full rewrite.
4637 Find where the first 'mdat' atom will eventually wind up in any new file. If
4638 this movement is within the bounds of padding prefs, allow a dynamic update.
4639 Adjust the amount of padding to accommodate the difference in positions
4640 (persuant to padding prefs). Otherwise prevent it, and make sure the default
4641 amount of padding exists for the file for a full rewrite.
35564642 ----------------------*/
35574643 void APar_DetermineDynamicUpdate() {
3558 if (!force_existing_hierarchy && moov_atom_was_mooved) {
3559 dynUpd.prevent_dynamic_update = true;
3560 return;
3561 }
3562 uint64_t mdat_pos = 0;
3563 //uint64_t moov_udta_pos = APar_QuickSumAtomicLengths(dynUpd.moov_udta_atom);
3564 //uint64_t moov_last_trak_pos = APar_QuickSumAtomicLengths(dynUpd.last_trak_child_atom);
3565 //uint64_t moov_meta_pos = APar_QuickSumAtomicLengths(dynUpd.moov_meta_atom);
3566 uint64_t moov_pos = APar_QuickSumAtomicLengths(dynUpd.moov_atom);
3567 uint64_t root_meta_pos = APar_QuickSumAtomicLengths(dynUpd.file_meta_atom);
3568
3569 if (root_meta_pos > 0 && root_meta_pos != dynUpd.file_meta_atom->AtomicStart) {
3570
3571 } 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
3572 dynUpd.initial_update_atom = &parsedAtoms[0];
3573
3574 } else if (moov_pos > 0) {
3575 dynUpd.initial_update_atom = dynUpd.moov_atom;
3576 }
3577
3578
3579 if (dynUpd.first_mdat_atom == NULL && alter_original) {
3580 //work this out later
3581
3582 } else if (dynUpd.first_mdat_atom != NULL) {
3583 mdat_pos = APar_QuickSumAtomicLengths(dynUpd.first_mdat_atom);
3584 if (mdat_pos >= dynUpd.first_mdat_atom->AtomicStart) {
3585 uint64_t offset_increase = mdat_pos - dynUpd.first_mdat_atom->AtomicStart;
3586 if (offset_increase > dynUpd.padding_bytes) {
3587 if ( (psp_brand || force_existing_hierarchy) && (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) ) {
3588 dynUpd.updage_by_padding = true;
3589 } else {
3590 dynUpd.prevent_dynamic_update = true;
3591 }
3592 } else {
3593 uint64_t padding_remaining = dynUpd.padding_bytes - offset_increase;
3594 uint64_t padding_allowed = APar_PaddingAmount(padding_remaining, true);
3595
3596 if (padding_remaining == padding_allowed) {
3597 if (alter_original) {
3598 dynUpd.updage_by_padding = true;
3599 }
3600 APar_AdjustPadding(padding_allowed);
3601 } else {
3602 dynUpd.updage_by_padding = false;
3603 APar_AdjustPadding(padding_allowed);
3604 }
3605 }
3606
3607 } else {
3608 uint64_t padding_replenishment = dynUpd.first_mdat_atom->AtomicStart - mdat_pos;
3609 uint64_t padding_allowed = APar_PaddingAmount(padding_replenishment, true);
3610
3611 if (padding_replenishment == padding_allowed) {
3612 if (alter_original) {
3613 dynUpd.updage_by_padding = true;
3614 }
3615 APar_AdjustPadding(padding_allowed);
3616 } else {
3617 dynUpd.updage_by_padding = false;
3618 APar_AdjustPadding(padding_allowed);
3619 }
3620 }
3621 }
3622 if (!dynUpd.updage_by_padding && dynUpd.padding_bytes < pad_prefs.default_padding_size) {
3623 APar_AdjustPadding(pad_prefs.default_padding_size); //if there is a full rewrite, add a default amount of padding
3624 }
3625 APar_DetermineAtomLengths();
3626 return;
4644 if (!force_existing_hierarchy && moov_atom_was_mooved) {
4645 dynUpd.prevent_dynamic_update = true;
4646 return;
4647 }
4648 uint64_t mdat_pos = 0;
4649 // uint64_t moov_udta_pos = APar_QuickSumAtomicLengths(dynUpd.moov_udta_atom);
4650 // uint64_t moov_last_trak_pos =
4651 // APar_QuickSumAtomicLengths(dynUpd.last_trak_child_atom); uint64_t
4652 // moov_meta_pos = APar_QuickSumAtomicLengths(dynUpd.moov_meta_atom);
4653 uint64_t moov_pos = APar_QuickSumAtomicLengths(dynUpd.moov_atom);
4654 uint64_t root_meta_pos = APar_QuickSumAtomicLengths(dynUpd.file_meta_atom);
4655
4656 if (root_meta_pos > 0 &&
4657 root_meta_pos != dynUpd.file_meta_atom->AtomicStart) {
4658
4659 } else if (moov_pos > 0 &&
4660 moov_pos !=
4661 dynUpd.moov_atom
4662 ->AtomicStart) { // this could reflect either a
4663 // root meta being moved, or moov
4664 // came after mdat & was rearranged
4665 dynUpd.initial_update_atom = &parsedAtoms[0];
4666
4667 } else if (moov_pos > 0) {
4668 dynUpd.initial_update_atom = dynUpd.moov_atom;
4669 }
4670
4671 if (dynUpd.first_mdat_atom == NULL && alter_original) {
4672 // work this out later
4673
4674 } else if (dynUpd.first_mdat_atom != NULL) {
4675 mdat_pos = APar_QuickSumAtomicLengths(dynUpd.first_mdat_atom);
4676 if (mdat_pos >= dynUpd.first_mdat_atom->AtomicStart) {
4677 uint64_t offset_increase = mdat_pos - dynUpd.first_mdat_atom->AtomicStart;
4678 if (offset_increase > dynUpd.padding_bytes) {
4679 if ((psp_brand || force_existing_hierarchy) &&
4680 (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) {
4681 dynUpd.updage_by_padding = true;
4682 } else {
4683 dynUpd.prevent_dynamic_update = true;
4684 }
4685 } else {
4686 uint64_t padding_remaining = dynUpd.padding_bytes - offset_increase;
4687 uint64_t padding_allowed = APar_PaddingAmount(padding_remaining, true);
4688
4689 if (padding_remaining == padding_allowed) {
4690 if (alter_original) {
4691 dynUpd.updage_by_padding = true;
4692 }
4693 APar_AdjustPadding(padding_allowed);
4694 } else {
4695 dynUpd.updage_by_padding = false;
4696 APar_AdjustPadding(padding_allowed);
4697 }
4698 }
4699
4700 } else {
4701 uint64_t padding_replenishment =
4702 dynUpd.first_mdat_atom->AtomicStart - mdat_pos;
4703 uint64_t padding_allowed =
4704 APar_PaddingAmount(padding_replenishment, true);
4705
4706 if (padding_replenishment == padding_allowed) {
4707 if (alter_original) {
4708 dynUpd.updage_by_padding = true;
4709 }
4710 APar_AdjustPadding(padding_allowed);
4711 } else {
4712 dynUpd.updage_by_padding = false;
4713 APar_AdjustPadding(padding_allowed);
4714 }
4715 }
4716 }
4717 if (!dynUpd.updage_by_padding &&
4718 dynUpd.padding_bytes < pad_prefs.default_padding_size) {
4719 APar_AdjustPadding(
4720 pad_prefs.default_padding_size); // if there is a full rewrite, add a
4721 // default amount of padding
4722 }
4723 APar_DetermineAtomLengths();
4724 return;
36274725 }
36284726
36294727 /*----------------------
36304728 APar_ConsolidatePadding
36314729
3632 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
3633 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
3634 handler or (probably) at level 1 (probably before mdat).
3635 When doing a dynamic update in situ, the length of this consolidated padding atom will change (later) if metadata is altered.
4730 Work through all the atoms that were determined to be 'padding' in
4731 APar_FindPadding, sum up their lengths and remove them from the hieararchy. No
4732 bytes are added or removed from the file because the total length of the padding
4733 is reformed as a single consolidated 'free' atom. This free atom will either
4734 form after the iTunes handler or (probably) at level 1 (probably before mdat).
4735 When doing a dynamic update in situ, the length of this
4736 consolidated padding atom will change (later) if metadata is altered.
36364737 ----------------------*/
36374738 void APar_ConsolidatePadding() {
3638 uint64_t bytes_o_padding = 0;
3639 if (dynUpd.consolidated_padding_insertion == 0) return;
3640 FreeAtomListing* padding_entry = dynUpd.first_padding_atom;
3641
3642 while (true) {
3643 if (padding_entry == NULL) break;
3644
3645 APar_EliminateAtom(padding_entry->free_atom->AtomicNumber, padding_entry->free_atom->NextAtomNumber);
3646
3647 FreeAtomListing* next_entry = padding_entry->next_free_listing;
3648 free(padding_entry);
3649 padding_entry = next_entry;
3650 }
3651
3652 bytes_o_padding = APar_PaddingAmount(dynUpd.padding_bytes, !alter_original);
3653
3654 if (bytes_o_padding >= 8) {
3655 if ( !(psp_brand || force_existing_hierarchy) && (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) ) {
3656 APar_CreatePadding(bytes_o_padding);
3657 }
3658 }
3659 return;
4739 uint64_t bytes_o_padding = 0;
4740 if (dynUpd.consolidated_padding_insertion == 0)
4741 return;
4742 FreeAtomListing *padding_entry = dynUpd.first_padding_atom;
4743
4744 while (true) {
4745 if (padding_entry == NULL)
4746 break;
4747
4748 APar_EliminateAtom(padding_entry->free_atom->AtomicNumber,
4749 padding_entry->free_atom->NextAtomNumber);
4750
4751 FreeAtomListing *next_entry = padding_entry->next_free_listing;
4752 free(padding_entry);
4753 padding_entry = next_entry;
4754 }
4755
4756 bytes_o_padding = APar_PaddingAmount(dynUpd.padding_bytes, !alter_original);
4757
4758 if (bytes_o_padding >= 8) {
4759 if (!(psp_brand || force_existing_hierarchy) &&
4760 (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) {
4761 APar_CreatePadding(bytes_o_padding);
4762 }
4763 }
4764 return;
36604765 }
36614766
36624767 /*----------------------
36634768 APar_FindPadding
3664 listing_purposes_only - controls whether to just track free/skip atoms, or actually consolidate
3665
3666 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
3667 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
3668 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
3669 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
3670 padding can reside will also be found - the presence of iTunes tags will ultimately deposit all accumulated padding in in the iTunes hierarchy.
4769 listing_purposes_only - controls whether to just track free/skip atoms,
4770 or actually consolidate
4771
4772 This is called before all the lengths of all atoms gets redetermined. The
4773 atoms at this point are already moved via optimization, and so the location of
4774 where to place padding can be ascertained. It is not known howevever where the
4775 ultimate positions of these landmark atoms will be - which can change according
4776 to padding prefs (which gets applied to all the padding in
4777 APar_ConsolidatePadding(). Any free/skip atoms found to exist between 'moov' &
4778 the first 'mdat' atom will be be considered padding (excepting anything under
4779 'stsd' which will be monolithing during *setting* of metadata tags). Each
4780 padding atom will be noted. A place where padding can reside will also be found
4781 - the presence of iTunes tags will ultimately deposit all accumulated padding in
4782 in the iTunes hierarchy.
36714783 ----------------------*/
36724784 void APar_FindPadding(bool listing_purposes_only) {
3673 AtomicInfo* DynamicUpdateRangeStart = NULL;
3674 AtomicInfo* DynamicUpdateRangeEnd = dynUpd.first_mdat_atom; //could be NULL (a missing 'mdat' would mean externally referenced media that would not be self-contained)
3675 short eval_atom = 0;
3676
3677 if (dynUpd.moov_atom != NULL) {
3678 DynamicUpdateRangeStart = &parsedAtoms[dynUpd.moov_atom->NextAtomNumber];
3679 }
3680 if (DynamicUpdateRangeStart == NULL) return;
3681
3682 eval_atom = DynamicUpdateRangeStart->AtomicNumber;
3683 while (true) {
3684 if (memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) {
3685 if (dynUpd.first_padding_atom == NULL) {
3686 dynUpd.first_padding_atom = (FreeAtomListing*)malloc(sizeof(FreeAtomListing));
3687 dynUpd.first_padding_atom->free_atom = &parsedAtoms[eval_atom];
3688 dynUpd.first_padding_atom->next_free_listing = NULL;
3689 } else {
3690 FreeAtomListing* free_listing = (FreeAtomListing*)malloc(sizeof(FreeAtomListing));
3691 free_listing->free_atom = &parsedAtoms[eval_atom];
3692 free_listing->next_free_listing = NULL;
3693 if (dynUpd.first_padding_atom->next_free_listing == NULL) {
3694 dynUpd.first_padding_atom->next_free_listing = free_listing;
3695 } else if (dynUpd.last_padding_atom != NULL) {
3696 dynUpd.last_padding_atom->next_free_listing = free_listing;
3697 }
3698 dynUpd.last_padding_atom = free_listing;
3699 }
3700 dynUpd.padding_bytes += (parsedAtoms[eval_atom].AtomicLength == 1 ? parsedAtoms[eval_atom].AtomicLengthExtended : parsedAtoms[eval_atom].AtomicLength);
3701 }
3702 eval_atom = parsedAtoms[eval_atom].NextAtomNumber;
3703 if (eval_atom == 0) break;
3704 if (DynamicUpdateRangeEnd != NULL) {
3705 if (DynamicUpdateRangeEnd->AtomicNumber == eval_atom) break;
3706 }
3707 }
3708
3709 //search for a suitable location where padding can accumulate
3710 if (dynUpd.moov_udta_atom != NULL) {
3711 if (dynUpd.iTunes_list_handler_atom == NULL) {
3712 dynUpd.iTunes_list_handler_atom = APar_FindAtom("moov.udta.meta.hdlr", false, VERSIONED_ATOM, 0);
3713 if (dynUpd.iTunes_list_handler_atom != NULL) {
3714 if (parsedAtoms[dynUpd.iTunes_list_handler_atom->NextAtomNumber].AtomicLevel >= dynUpd.iTunes_list_handler_atom->AtomicLevel) {
3715 dynUpd.consolidated_padding_insertion = dynUpd.iTunes_list_handler_atom->AtomicNumber;
3716 }
3717 }
3718 }
3719 }
3720 if (dynUpd.consolidated_padding_insertion == 0) {
3721 if (dynUpd.first_mdat_atom != NULL && !(dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) {
3722 dynUpd.consolidated_padding_insertion = APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber);
3723 } else {
3724 dynUpd.consolidated_padding_insertion = APar_FindLastAtom();
3725 dynUpd.optimization_flags |= PADDING_AT_EOF;
3726 }
3727 }
3728
3729 //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
3730 //AtomicNumber becomes -1; so work back through the hierarchy for the first non-padding atom
3731 while (true) {
3732 if (memcmp(parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicName, "free", 4) == 0 ||
3733 memcmp(parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicName, "skip", 4) == 0) {
3734 dynUpd.consolidated_padding_insertion = APar_FindPrecedingAtom(dynUpd.consolidated_padding_insertion);
3735 } else {
3736 break;
3737 }
3738 }
3739 return;
4785 AtomicInfo *DynamicUpdateRangeStart = NULL;
4786 AtomicInfo *DynamicUpdateRangeEnd =
4787 dynUpd.first_mdat_atom; // could be NULL (a missing 'mdat' would mean
4788 // externally referenced media that would not be
4789 // self-contained)
4790 short eval_atom = 0;
4791
4792 if (dynUpd.moov_atom != NULL) {
4793 DynamicUpdateRangeStart = &parsedAtoms[dynUpd.moov_atom->NextAtomNumber];
4794 }
4795 if (DynamicUpdateRangeStart == NULL)
4796 return;
4797
4798 eval_atom = DynamicUpdateRangeStart->AtomicNumber;
4799 while (true) {
4800 if (memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 ||
4801 memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) {
4802 if (dynUpd.first_padding_atom == NULL) {
4803 dynUpd.first_padding_atom =
4804 (FreeAtomListing *)malloc(sizeof(FreeAtomListing));
4805 dynUpd.first_padding_atom->free_atom = &parsedAtoms[eval_atom];
4806 dynUpd.first_padding_atom->next_free_listing = NULL;
4807 } else {
4808 FreeAtomListing *free_listing =
4809 (FreeAtomListing *)malloc(sizeof(FreeAtomListing));
4810 free_listing->free_atom = &parsedAtoms[eval_atom];
4811 free_listing->next_free_listing = NULL;
4812 if (dynUpd.first_padding_atom->next_free_listing == NULL) {
4813 dynUpd.first_padding_atom->next_free_listing = free_listing;
4814 } else if (dynUpd.last_padding_atom != NULL) {
4815 dynUpd.last_padding_atom->next_free_listing = free_listing;
4816 }
4817 dynUpd.last_padding_atom = free_listing;
4818 }
4819 dynUpd.padding_bytes += (parsedAtoms[eval_atom].AtomicLength == 1
4820 ? parsedAtoms[eval_atom].AtomicLengthExtended
4821 : parsedAtoms[eval_atom].AtomicLength);
4822 }
4823 eval_atom = parsedAtoms[eval_atom].NextAtomNumber;
4824 if (eval_atom == 0)
4825 break;
4826 if (DynamicUpdateRangeEnd != NULL) {
4827 if (DynamicUpdateRangeEnd->AtomicNumber == eval_atom)
4828 break;
4829 }
4830 }
4831
4832 // search for a suitable location where padding can accumulate
4833 if (dynUpd.moov_udta_atom != NULL) {
4834 if (dynUpd.iTunes_list_handler_atom == NULL) {
4835 dynUpd.iTunes_list_handler_atom =
4836 APar_FindAtom("moov.udta.meta.hdlr", false, VERSIONED_ATOM, 0);
4837 if (dynUpd.iTunes_list_handler_atom != NULL) {
4838 if (parsedAtoms[dynUpd.iTunes_list_handler_atom->NextAtomNumber]
4839 .AtomicLevel >= dynUpd.iTunes_list_handler_atom->AtomicLevel) {
4840 dynUpd.consolidated_padding_insertion =
4841 dynUpd.iTunes_list_handler_atom->AtomicNumber;
4842 }
4843 }
4844 }
4845 }
4846 if (dynUpd.consolidated_padding_insertion == 0) {
4847 if (dynUpd.first_mdat_atom != NULL &&
4848 !(dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV)) {
4849 dynUpd.consolidated_padding_insertion =
4850 APar_FindPrecedingAtom(dynUpd.first_mdat_atom->AtomicNumber);
4851 } else {
4852 dynUpd.consolidated_padding_insertion = APar_FindLastAtom();
4853 dynUpd.optimization_flags |= PADDING_AT_EOF;
4854 }
4855 }
4856
4857 // if the place where padding will eventually wind up is just before a padding
4858 // atom, that padding atom will be erased by APar_ConsolidatePadding when its
4859 // AtomicNumber becomes -1; so work back through the hierarchy for the first
4860 // non-padding atom
4861 while (true) {
4862 if (memcmp(parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicName,
4863 "free",
4864 4) == 0 ||
4865 memcmp(parsedAtoms[dynUpd.consolidated_padding_insertion].AtomicName,
4866 "skip",
4867 4) == 0) {
4868 dynUpd.consolidated_padding_insertion =
4869 APar_FindPrecedingAtom(dynUpd.consolidated_padding_insertion);
4870 } else {
4871 break;
4872 }
4873 }
4874 return;
37404875 }
37414876
37424877 void APar_LocateAtomLandmarks() {
3743 short total_file_level_atoms = APar_ReturnChildrenAtoms (0, 0);
3744 short eval_atom = 0;
3745
3746 dynUpd.optimization_flags = 0;
3747 dynUpd.padding_bytes = 0; //this *won't* get filled here; it is only tracked for the purposes of dynamic updating
3748 dynUpd.consolidated_padding_insertion = 0; //this will eventually hold the point where to insert a new atom that will accumulate padding
3749
3750 dynUpd.last_trak_child_atom = NULL;
3751 dynUpd.moov_atom = NULL;
3752 dynUpd.moov_udta_atom = NULL;
3753 dynUpd.iTunes_list_handler_atom = NULL; //this *won't* get filled here; it is only tracked for the purposes of dynamic updating
3754 dynUpd.moov_meta_atom = NULL;
3755 dynUpd.file_meta_atom = NULL;
3756 dynUpd.first_mdat_atom = NULL;
3757 dynUpd.first_movielevel_metadata_tagging_atom = NULL;
3758 dynUpd.initial_update_atom = NULL;
3759 dynUpd.first_otiose_freespace_atom = NULL;
3760 dynUpd.first_padding_atom = NULL; //this *won't* get filled here; it is only tracked for the purposes of dynamic updating
3761 dynUpd.last_padding_atom = NULL; //this *won't* get filled here; it is only tracked for the purposes of dynamic updating
3762 dynUpd.padding_store = NULL; //this *won't* get filled here; it gets filled in APar_ConsolidatePadding
3763 dynUpd.padding_resevoir = NULL;
3764
3765 //scan through all top level atoms; fragmented files won't be optimized
3766 for(uint8_t iii = 1; iii <= total_file_level_atoms; iii++) {
3767 eval_atom = APar_ReturnChildrenAtoms (0, iii);
3768 //fprintf(stdout, "file level children - %s\n", parsedAtoms[eval_atom].AtomicName);
3769
3770 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "moof", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "mfra", 4) == 0) {
3771 move_moov_atom = false; //moov reordering won't be occurring on fragmented files, but it should have moov first anyway (QuickTime does at least)
3772 }
3773
3774 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0 ) {
3775 if (dynUpd.first_mdat_atom == NULL) {
3776 dynUpd.first_mdat_atom = &parsedAtoms[eval_atom];
3777 }
3778 }
3779
3780 if (dynUpd.first_otiose_freespace_atom == NULL) {
3781 if ( (memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) &&
3782 dynUpd.first_mdat_atom == NULL && dynUpd.moov_atom == NULL ) {
3783 dynUpd.first_otiose_freespace_atom = &parsedAtoms[eval_atom]; //the scourge of libmp4v2
3784 }
3785 }
3786
3787 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "moov", 4) == 0 ) {
3788 dynUpd.moov_atom = &parsedAtoms[eval_atom];
3789 if (dynUpd.first_mdat_atom != NULL) {
3790 dynUpd.optimization_flags |= MEDIADATA__PRECEDES__MOOV; //or mdat could be entirely missing as well; check later
3791 }
3792 }
3793
3794 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "meta", 4) == 0 ) {
3795 dynUpd.file_meta_atom = &parsedAtoms[eval_atom];
3796 if (dynUpd.moov_atom != NULL) {
3797 dynUpd.optimization_flags |= ROOT_META__PRECEDES__MOOV;
3798 }//meta before moov would require more rewrite of the portion of the file than I want to do
3799 } //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
3800 }
3801 return;
4878 short total_file_level_atoms = APar_ReturnChildrenAtoms(0, 0);
4879 short eval_atom = 0;
4880
4881 dynUpd.optimization_flags = 0;
4882 dynUpd.padding_bytes = 0; // this *won't* get filled here; it is only tracked
4883 // for the purposes of dynamic updating
4884 dynUpd.consolidated_padding_insertion =
4885 0; // this will eventually hold the point where to insert a new atom that
4886 // will accumulate padding
4887
4888 dynUpd.last_trak_child_atom = NULL;
4889 dynUpd.moov_atom = NULL;
4890 dynUpd.moov_udta_atom = NULL;
4891 dynUpd.iTunes_list_handler_atom =
4892 NULL; // this *won't* get filled here; it is only tracked for the purposes
4893 // of dynamic updating
4894 dynUpd.moov_meta_atom = NULL;
4895 dynUpd.file_meta_atom = NULL;
4896 dynUpd.first_mdat_atom = NULL;
4897 dynUpd.first_movielevel_metadata_tagging_atom = NULL;
4898 dynUpd.initial_update_atom = NULL;
4899 dynUpd.first_otiose_freespace_atom = NULL;
4900 dynUpd.first_padding_atom =
4901 NULL; // this *won't* get filled here; it is only tracked for the purposes
4902 // of dynamic updating
4903 dynUpd.last_padding_atom =
4904 NULL; // this *won't* get filled here; it is only tracked for the purposes
4905 // of dynamic updating
4906 dynUpd.padding_store = NULL; // this *won't* get filled here; it gets filled
4907 // in APar_ConsolidatePadding
4908 dynUpd.padding_resevoir = NULL;
4909
4910 // scan through all top level atoms; fragmented files won't be optimized
4911 for (uint8_t iii = 1; iii <= total_file_level_atoms; iii++) {
4912 eval_atom = APar_ReturnChildrenAtoms(0, iii);
4913 // fprintf(stdout, "file level children - %s\n",
4914 // parsedAtoms[eval_atom].AtomicName);
4915
4916 if (memcmp(parsedAtoms[eval_atom].AtomicName, "moof", 4) == 0 ||
4917 memcmp(parsedAtoms[eval_atom].AtomicName, "mfra", 4) == 0) {
4918 move_moov_atom =
4919 false; // moov reordering won't be occurring on fragmented files, but
4920 // it should have moov first anyway (QuickTime does at least)
4921 }
4922
4923 if (memcmp(parsedAtoms[eval_atom].AtomicName, "mdat", 4) == 0) {
4924 if (dynUpd.first_mdat_atom == NULL) {
4925 dynUpd.first_mdat_atom = &parsedAtoms[eval_atom];
4926 }
4927 }
4928
4929 if (dynUpd.first_otiose_freespace_atom == NULL) {
4930 if ((memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 ||
4931 memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0) &&
4932 dynUpd.first_mdat_atom == NULL && dynUpd.moov_atom == NULL) {
4933 dynUpd.first_otiose_freespace_atom =
4934 &parsedAtoms[eval_atom]; // the scourge of libmp4v2
4935 }
4936 }
4937
4938 if (memcmp(parsedAtoms[eval_atom].AtomicName, "moov", 4) == 0) {
4939 dynUpd.moov_atom = &parsedAtoms[eval_atom];
4940 if (dynUpd.first_mdat_atom != NULL) {
4941 dynUpd.optimization_flags |=
4942 MEDIADATA__PRECEDES__MOOV; // or mdat could be entirely missing as
4943 // well; check later
4944 }
4945 }
4946
4947 if (memcmp(parsedAtoms[eval_atom].AtomicName, "meta", 4) == 0) {
4948 dynUpd.file_meta_atom = &parsedAtoms[eval_atom];
4949 if (dynUpd.moov_atom != NULL) {
4950 dynUpd.optimization_flags |= ROOT_META__PRECEDES__MOOV;
4951 } // meta before moov would require more rewrite of the portion of the
4952 // file than I want to do
4953 } // but it wouldn't be all that difficult to accommodate rewriting
4954 // everything from ftyp down to the first mdat, but currently its limited
4955 // to last 'trak' child to mdat
4956 }
4957 return;
38024958 }
38034959
38044960 /*----------------------
38054961 APar_Optimize
3806 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)
3807
3808 The visual:
3809 ftyp
3810 moov
3811 trak
3812 trak
3813 trak
3814 udta
3815 meta
3816 hdlr
3817 free (functions as padding store when there are iTunes tags present)
3818 ilst
3819 meta
3820 hdlr
3821 meta
3822 hdlr
3823 free (functions as padding store when there are *no* iTunes tags present)
3824 mdat
4962 mdat_test_only - the only info desired (if true, when printing the tree)
4963 is to know whether mdat precedes moov (and nullifing the concept of padding)
4964
4965 The visual:
4966 ftyp
4967 moov
4968 trak
4969 trak
4970 trak
4971 udta
4972 meta
4973 hdlr
4974 free (functions
4975 as padding store when there are iTunes tags present) ilst meta hdlr meta hdlr
4976 free (functions as padding store when there are *no* iTunes tags
4977 present) mdat
38254978
38264979 ----------------------*/
38274980 void APar_Optimize(bool mdat_test_only) {
3828 short last_child_of_moov = 0;
3829 short eval_atom = 0;
3830
3831 APar_LocateAtomLandmarks();
3832
3833 /* -----------move moov to precede any media data (mdat)--------- */
3834 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) {
3835 if (mdat_test_only) {
3836 moov_atom_was_mooved = true; //this is all the interesting info required (during APar_PrintAtomicTree)
3837 APar_FindPadding(mdat_test_only);
3838 return;
3839 } else {
3840 APar_MoveAtom(dynUpd.moov_atom->AtomicNumber, dynUpd.first_mdat_atom->AtomicNumber);
3841 moov_atom_was_mooved = true;
3842 }
3843 }
3844
3845 /* -----------move a file/root level 'meta' to follow 'moov', but precede any media data(mdat)--------- */
3846 if (dynUpd.file_meta_atom != NULL && (dynUpd.optimization_flags & ROOT_META__PRECEDES__MOOV)) {
3847 last_child_of_moov = APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber);
3848 APar_MoveAtom(dynUpd.file_meta_atom->AtomicNumber, parsedAtoms[last_child_of_moov].NextAtomNumber);
3849 }
3850
3851 /* -----------optiizing the layout of movie-level atoms--------- */
3852 if (dynUpd.moov_atom != NULL) { //it can't be null, but just in case...
3853 uint8_t extra_atom_count = 0;
3854 AtomicInfo* last_trak_atom = NULL;
3855 short total_moov_child_atoms = APar_ReturnChildrenAtoms(dynUpd.moov_atom->AtomicNumber, 0);
3856 AtomicInfo** other_track_level_atom = (AtomicInfo**)malloc(total_moov_child_atoms * sizeof(AtomicInfo*));
3857 for (uint8_t xi = 0; xi < total_moov_child_atoms; xi++) {
3858 other_track_level_atom[xi] = NULL;
3859 }
3860
3861 for(uint8_t moov_i = 1; moov_i <= total_moov_child_atoms; moov_i ++) {
3862 eval_atom = APar_ReturnChildrenAtoms(dynUpd.moov_atom->AtomicNumber, moov_i);
3863 if ( memcmp(parsedAtoms[eval_atom].AtomicName, "udta", 4) == 0 && parsedAtoms[eval_atom].AtomicLevel == 2) {
3864 dynUpd.moov_udta_atom = &parsedAtoms[eval_atom];
3865 if (dynUpd.first_movielevel_metadata_tagging_atom == NULL) dynUpd.first_movielevel_metadata_tagging_atom = &parsedAtoms[eval_atom];
3866 } else if ( memcmp(parsedAtoms[eval_atom].AtomicName, "meta", 4) == 0 && parsedAtoms[eval_atom].AtomicLevel == 2) {
3867 dynUpd.moov_meta_atom = &parsedAtoms[eval_atom];
3868 if (dynUpd.first_movielevel_metadata_tagging_atom == NULL) dynUpd.first_movielevel_metadata_tagging_atom = &parsedAtoms[eval_atom];
3869 } else if (memcmp(parsedAtoms[eval_atom].AtomicName, "trak", 4) == 0) {
3870 last_trak_atom = &parsedAtoms[eval_atom];
3871 if (dynUpd.first_movielevel_metadata_tagging_atom != NULL) {
3872 if (dynUpd.moov_meta_atom != NULL) dynUpd.optimization_flags |= MOOV_META__PRECEDES__TRACKS;
3873 else if (dynUpd.moov_udta_atom != NULL) dynUpd.optimization_flags |= MOOV_UDTA__PRECEDES__TRACKS;
3874 }
3875 } else if (!(memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 || memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0)) {
3876 if (dynUpd.moov_meta_atom != NULL || dynUpd.moov_udta_atom != NULL) {
3877 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
3878 extra_atom_count++;
3879 }
3880 }
3881 }
3882
3883 if (last_trak_atom != NULL) {
3884 short last_child_of_last_track = APar_FindLastChild_of_ParentAtom(last_trak_atom->AtomicNumber);
3885 if (last_child_of_last_track > 0) {
3886 dynUpd.last_trak_child_atom = &parsedAtoms[last_child_of_last_track];
3887 }
3888 }
3889
3890 /* -----------moving extra movie-level atoms (![trak,free,skip,meta,udta]) to precede the first metadata tagging hierarchy (moov.meta or moov.udta)--------- */
3891 if (extra_atom_count > 0 && dynUpd.first_movielevel_metadata_tagging_atom != NULL) {
3892 for (uint8_t xxi = 0; xxi < extra_atom_count; xxi++) {
3893 APar_MoveAtom((*other_track_level_atom + xxi)->AtomicNumber, dynUpd.first_movielevel_metadata_tagging_atom->AtomicNumber);
3894 }
3895 }
3896
3897 /* -----------moving udta or meta to follow the trak atom--------- */
3898 if (dynUpd.optimization_flags & MOOV_META__PRECEDES__TRACKS) {
3899 if (last_child_of_moov == 0) last_child_of_moov = APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber);
3900 APar_MoveAtom(dynUpd.moov_meta_atom->AtomicNumber, parsedAtoms[last_child_of_moov].NextAtomNumber);
3901 } else if (dynUpd.optimization_flags & MOOV_UDTA__PRECEDES__TRACKS) {
3902 if (last_child_of_moov == 0) last_child_of_moov = APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber);
3903 APar_MoveAtom(dynUpd.moov_udta_atom->AtomicNumber, parsedAtoms[last_child_of_moov].NextAtomNumber);
3904 }
3905
3906 free(other_track_level_atom);
3907 other_track_level_atom = NULL;
3908 }
3909
3910 if (mdat_test_only) {
3911 APar_FindPadding(mdat_test_only);
3912 return;
3913 }
3914
3915 /* -----------delete free/skip atoms that precede media data or meta data--------- */
3916 if (dynUpd.first_otiose_freespace_atom != NULL && !alter_original) { //as a courtesty, for a full rewrite, eliminate L1 pre-mdat free/skip atoms
3917 AtomicInfo* free_space = dynUpd.first_otiose_freespace_atom;
3918 while (true) {
3919 if (free_space->AtomicLevel > 1) break;
3920 if (memcmp(free_space->AtomicName, "free", 4) != 0) break; //only get the consecutive 'free' space
3921 AtomicInfo* nextatom = &parsedAtoms[free_space->NextAtomNumber];
3922 APar_EliminateAtom(free_space->AtomicNumber, free_space->NextAtomNumber);
3923 free_space = nextatom;
3924 }
3925 }
3926 return;
4981 short last_child_of_moov = 0;
4982 short eval_atom = 0;
4983
4984 APar_LocateAtomLandmarks();
4985
4986 /* -----------move moov to precede any media data (mdat)--------- */
4987 if (move_moov_atom &&
4988 (dynUpd.first_mdat_atom != NULL &&
4989 (dynUpd.optimization_flags &
4990 MEDIADATA__PRECEDES__MOOV))) { // first_mdat_atom > 0 && moov_atom > 0
4991 // && moov_atom > first_mdat_atom) {
4992 if (mdat_test_only) {
4993 moov_atom_was_mooved = true; // this is all the interesting info required
4994 // (during APar_PrintAtomicTree)
4995 APar_FindPadding(mdat_test_only);
4996 return;
4997 } else {
4998 APar_MoveAtom(dynUpd.moov_atom->AtomicNumber,
4999 dynUpd.first_mdat_atom->AtomicNumber);
5000 moov_atom_was_mooved = true;
5001 }
5002 }
5003
5004 /* -----------move a file/root level 'meta' to follow 'moov', but precede any
5005 * media data(mdat)--------- */
5006 if (dynUpd.file_meta_atom != NULL &&
5007 (dynUpd.optimization_flags & ROOT_META__PRECEDES__MOOV)) {
5008 last_child_of_moov =
5009 APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber);
5010 APar_MoveAtom(dynUpd.file_meta_atom->AtomicNumber,
5011 parsedAtoms[last_child_of_moov].NextAtomNumber);
5012 }
5013
5014 /* -----------optiizing the layout of movie-level atoms--------- */
5015 if (dynUpd.moov_atom != NULL) { // it can't be null, but just in case...
5016 uint8_t extra_atom_count = 0;
5017 AtomicInfo *last_trak_atom = NULL;
5018 short total_moov_child_atoms =
5019 APar_ReturnChildrenAtoms(dynUpd.moov_atom->AtomicNumber, 0);
5020 AtomicInfo **other_track_level_atom =
5021 (AtomicInfo **)malloc(total_moov_child_atoms * sizeof(AtomicInfo *));
5022 for (uint8_t xi = 0; xi < total_moov_child_atoms; xi++) {
5023 other_track_level_atom[xi] = NULL;
5024 }
5025
5026 for (uint8_t moov_i = 1; moov_i <= total_moov_child_atoms; moov_i++) {
5027 eval_atom =
5028 APar_ReturnChildrenAtoms(dynUpd.moov_atom->AtomicNumber, moov_i);
5029 if (memcmp(parsedAtoms[eval_atom].AtomicName, "udta", 4) == 0 &&
5030 parsedAtoms[eval_atom].AtomicLevel == 2) {
5031 dynUpd.moov_udta_atom = &parsedAtoms[eval_atom];
5032 if (dynUpd.first_movielevel_metadata_tagging_atom == NULL)
5033 dynUpd.first_movielevel_metadata_tagging_atom =
5034 &parsedAtoms[eval_atom];
5035 } else if (memcmp(parsedAtoms[eval_atom].AtomicName, "meta", 4) == 0 &&
5036 parsedAtoms[eval_atom].AtomicLevel == 2) {
5037 dynUpd.moov_meta_atom = &parsedAtoms[eval_atom];
5038 if (dynUpd.first_movielevel_metadata_tagging_atom == NULL)
5039 dynUpd.first_movielevel_metadata_tagging_atom =
5040 &parsedAtoms[eval_atom];
5041 } else if (memcmp(parsedAtoms[eval_atom].AtomicName, "trak", 4) == 0) {
5042 last_trak_atom = &parsedAtoms[eval_atom];
5043 if (dynUpd.first_movielevel_metadata_tagging_atom != NULL) {
5044 if (dynUpd.moov_meta_atom != NULL)
5045 dynUpd.optimization_flags |= MOOV_META__PRECEDES__TRACKS;
5046 else if (dynUpd.moov_udta_atom != NULL)
5047 dynUpd.optimization_flags |= MOOV_UDTA__PRECEDES__TRACKS;
5048 }
5049 } else if (!(memcmp(parsedAtoms[eval_atom].AtomicName, "free", 4) == 0 ||
5050 memcmp(parsedAtoms[eval_atom].AtomicName, "skip", 4) == 0)) {
5051 if (dynUpd.moov_meta_atom != NULL || dynUpd.moov_udta_atom != NULL) {
5052 other_track_level_atom[extra_atom_count] =
5053 &parsedAtoms[eval_atom]; // anything else gets noted because it
5054 // *follows* moov.meta or moov.udta and
5055 // needs to precede it
5056 extra_atom_count++;
5057 }
5058 }
5059 }
5060
5061 if (last_trak_atom != NULL) {
5062 short last_child_of_last_track =
5063 APar_FindLastChild_of_ParentAtom(last_trak_atom->AtomicNumber);
5064 if (last_child_of_last_track > 0) {
5065 dynUpd.last_trak_child_atom = &parsedAtoms[last_child_of_last_track];
5066 }
5067 }
5068
5069 /* -----------moving extra movie-level atoms (![trak,free,skip,meta,udta])
5070 * to precede the first metadata tagging hierarchy (moov.meta or
5071 * moov.udta)--------- */
5072 if (extra_atom_count > 0 &&
5073 dynUpd.first_movielevel_metadata_tagging_atom != NULL) {
5074 for (uint8_t xxi = 0; xxi < extra_atom_count; xxi++) {
5075 APar_MoveAtom(
5076 (*other_track_level_atom + xxi)->AtomicNumber,
5077 dynUpd.first_movielevel_metadata_tagging_atom->AtomicNumber);
5078 }
5079 }
5080
5081 /* -----------moving udta or meta to follow the trak atom--------- */
5082 if (dynUpd.optimization_flags & MOOV_META__PRECEDES__TRACKS) {
5083 if (last_child_of_moov == 0)
5084 last_child_of_moov =
5085 APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber);
5086 APar_MoveAtom(dynUpd.moov_meta_atom->AtomicNumber,
5087 parsedAtoms[last_child_of_moov].NextAtomNumber);
5088 } else if (dynUpd.optimization_flags & MOOV_UDTA__PRECEDES__TRACKS) {
5089 if (last_child_of_moov == 0)
5090 last_child_of_moov =
5091 APar_FindLastChild_of_ParentAtom(dynUpd.moov_atom->AtomicNumber);
5092 APar_MoveAtom(dynUpd.moov_udta_atom->AtomicNumber,
5093 parsedAtoms[last_child_of_moov].NextAtomNumber);
5094 }
5095
5096 free(other_track_level_atom);
5097 other_track_level_atom = NULL;
5098 }
5099
5100 if (mdat_test_only) {
5101 APar_FindPadding(mdat_test_only);
5102 return;
5103 }
5104
5105 /* -----------delete free/skip atoms that precede media data or meta
5106 * data--------- */
5107 if (dynUpd.first_otiose_freespace_atom != NULL &&
5108 !alter_original) { // as a courtesty, for a full rewrite, eliminate L1
5109 // pre-mdat free/skip atoms
5110 AtomicInfo *free_space = dynUpd.first_otiose_freespace_atom;
5111 while (true) {
5112 if (free_space->AtomicLevel > 1)
5113 break;
5114 if (memcmp(free_space->AtomicName, "free", 4) != 0)
5115 break; // only get the consecutive 'free' space
5116 AtomicInfo *nextatom = &parsedAtoms[free_space->NextAtomNumber];
5117 APar_EliminateAtom(free_space->AtomicNumber, free_space->NextAtomNumber);
5118 free_space = nextatom;
5119 }
5120 }
5121 return;
39275122 }
39285123
39295124 ///////////////////////////////////////////////////////////////////////////////////////
3930 // Determine Atom Length //
5125 // Determine Atom Length //
39315126 ///////////////////////////////////////////////////////////////////////////////////////
39325127
39335128 /*----------------------
39345129 APar_DetermineNewFileLength
39355130
3936 Sum up level 1 atoms (excludes any extranous EOF null bytes that iTunes occasionally writes - or used to)
5131 Sum up level 1 atoms (excludes any extranous EOF null bytes that iTunes
5132 occasionally writes - or used to)
39375133 ----------------------*/
39385134 void APar_DetermineNewFileLength() {
3939 new_file_size = 0;
3940 short thisAtomNumber = 0;
3941 while (true) {
3942 if (parsedAtoms[thisAtomNumber].AtomicLevel == 1) {
3943 if (parsedAtoms[thisAtomNumber].AtomicLengthExtended == 0) {
3944 //normal 32-bit number when AtomicLengthExtended == 0 (for run-o-the-mill mdat & mdat.length=0)
3945 new_file_size += parsedAtoms[thisAtomNumber].AtomicLength; //used in progressbar
3946 } else {
3947 //pseudo 64-bit mdat length
3948 new_file_size += parsedAtoms[thisAtomNumber].AtomicLengthExtended; //used in progressbar
3949 }
3950 if (parsedAtoms[thisAtomNumber].AtomicLength == 0) {
3951 new_file_size += file_size - parsedAtoms[thisAtomNumber].AtomicStart; //used in progressbar; mdat.length = 1
3952 }
3953 }
3954 if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) {
3955 break;
3956 }
3957 thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber;
3958 }
3959 return;
5135 new_file_size = 0;
5136 short thisAtomNumber = 0;
5137 while (true) {
5138 if (parsedAtoms[thisAtomNumber].AtomicLevel == 1) {
5139 if (parsedAtoms[thisAtomNumber].AtomicLengthExtended == 0) {
5140 // normal 32-bit number when AtomicLengthExtended == 0 (for
5141 // run-o-the-mill mdat & mdat.length=0)
5142 new_file_size +=
5143 parsedAtoms[thisAtomNumber].AtomicLength; // used in progressbar
5144 } else {
5145 // pseudo 64-bit mdat length
5146 new_file_size += parsedAtoms[thisAtomNumber]
5147 .AtomicLengthExtended; // used in progressbar
5148 }
5149 if (parsedAtoms[thisAtomNumber].AtomicLength == 0) {
5150 new_file_size +=
5151 file_size -
5152 parsedAtoms[thisAtomNumber]
5153 .AtomicStart; // used in progressbar; mdat.length = 1
5154 }
5155 }
5156 if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) {
5157 break;
5158 }
5159 thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber;
5160 }
5161 return;
39605162 }
39615163
39625164 /*----------------------
39735175
39745176 ----------------------*/
39755177 void APar_DetermineAtomLengths() {
3976 short rev_atom_loop = APar_FindLastAtom();
3977 //fprintf(stdout, "Last atom is named %s, num:%i\n", parsedAtoms[last_atom].AtomicName, parsedAtoms[last_atom].AtomicNumber);
3978
3979 while (true) {
3980 short next_atom = 0;
3981 uint64_t atom_size = 0;
3982 short previous_atom = 0; //only gets used in testing for atom under stsd
3983
3984 //fprintf(stdout, "current atom is named %s, num:%i\n", parsedAtoms[rev_atom_loop].AtomicName, parsedAtoms[rev_atom_loop].AtomicNumber);
3985
3986 if (rev_atom_loop == 0) {
3987 break; //we seem to have hit the first atom
3988 } else {
3989 previous_atom = APar_FindPrecedingAtom(rev_atom_loop);
3990 }
3991
3992 uint32_t _atom_ = UInt32FromBigEndian(parsedAtoms[rev_atom_loop].AtomicName);
3993 switch (_atom_) {
3994 //the enumerated atoms here are all of DUAL_STATE_ATOM type
3995 case 0x6D657461 : //'meta'
3996 atom_size += 12;
3997 break;
3998
3999 case 0x73747364 : //'stsd'
4000 atom_size += 16;
4001 break;
4002
4003 case 0x64726566 : //'dref'
4004 atom_size += 16;
4005 break;
4006
4007 case 0x69696E66 : //'iinf'
4008 atom_size += 14;
4009 break;
4010
4011 //accommodate parsing of atoms under stsd when required
4012 case 0x6D703473 : { //mp4s
4013 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) {
4014 atom_size += 16;
4015 } else {
4016 atom_size += 8;
4017 }
4018 break;
4019 }
4020 case 0x73727470 : //srtp
4021 case 0x72747020 : { //'rtp '
4022 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) {
4023 atom_size += 24;
4024 } else {
4025 atom_size += 8;
4026 }
4027 break;
4028 }
4029 case 0x616C6163 : //alac
4030 case 0x6D703461 : //mp4a
4031 case 0x73616D72 : //samr
4032 case 0x73617762 : //sawb
4033 case 0x73617770 : //sawp
4034 case 0x73657663 : //sevc
4035 case 0x73716370 : //sqcp
4036 case 0x73736D76 : //ssmv
4037 case 0x64726D73 : { //drms
4038 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) {
4039 atom_size += 36;
4040 } else {
4041 atom_size += 8;
4042 }
4043 break;
4044 }
4045 case 0x74783367 : { //tx3g
4046 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) {
4047 atom_size += 46;
4048 } else {
4049 atom_size += 8;
4050 }
4051 break;
4052 }
4053 case 0x6D6A7032 : //mjp2
4054 case 0x6D703476 : //mp4v
4055 case 0x61766331 : //avc1
4056 case 0x6A706567 : //jpeg
4057 case 0x73323633 : //s263
4058 case 0x64726D69 : { //drmi
4059 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM && deep_atom_scan) {
4060 atom_size += 86;
4061 } else {
4062 atom_size += 8;
4063 }
4064 break;
4065 }
4066
4067 default :
4068 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 && parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM) {
4069 atom_size += parsedAtoms[rev_atom_loop].AtomicLength;
4070 } else {
4071 atom_size += 8; //all atoms have *at least* 4bytes length & 4 bytes name
4072 }
4073 break;
4074 }
4075
4076 if (parsedAtoms[rev_atom_loop].NextAtomNumber != 0) {
4077 next_atom = parsedAtoms[rev_atom_loop].NextAtomNumber;
4078 }
4079
4080 while (parsedAtoms[next_atom].AtomicLevel > parsedAtoms[rev_atom_loop].AtomicLevel) { // eval all child atoms....
4081 //fprintf(stdout, "\ttest child atom %s, level:%i (sum %" PRIu64 ")\n", parsedAtoms[next_atom].AtomicName, parsedAtoms[next_atom].AtomicLevel, atom_size);
4082 if (parsedAtoms[rev_atom_loop].AtomicLevel == ( parsedAtoms[next_atom].AtomicLevel - 1) ) { // only child atoms 1 level down
4083 atom_size += parsedAtoms[next_atom].AtomicLength;
4084 //fprintf(stdout, "\t\teval child atom %s, level:%i (sum %" PRIu64 ")\n", parsedAtoms[next_atom].AtomicName, parsedAtoms[next_atom].AtomicLevel, atom_size);
4085 //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);
4086 } else if (parsedAtoms[next_atom].AtomicLevel < parsedAtoms[rev_atom_loop].AtomicLevel) {
4087 break;
4088 }
4089 next_atom = parsedAtoms[next_atom].NextAtomNumber; //increment to eval next atom
4090 parsedAtoms[rev_atom_loop].AtomicLength = atom_size;
4091 }
4092
4093 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
4094 parsedAtoms[rev_atom_loop].AtomicLength = 8;
4095 }
4096 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.
4097 parsedAtoms[rev_atom_loop].AtomicLength = 12;
4098 }
4099 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
4100 parsedAtoms[rev_atom_loop].AtomicLength = 8;
4101 }
4102
4103 rev_atom_loop = APar_FindPrecedingAtom(parsedAtoms[rev_atom_loop].AtomicNumber);
4104
4105 }
4106 APar_DetermineNewFileLength();
4107 //APar_SimpleAtomPrintout();
4108 //APar_PrintAtomicTree();
4109 return;
5178 short rev_atom_loop = APar_FindLastAtom();
5179 // fprintf(stdout, "Last atom is named %s, num:%i\n",
5180 // parsedAtoms[last_atom].AtomicName, parsedAtoms[last_atom].AtomicNumber);
5181
5182 while (true) {
5183 short next_atom = 0;
5184 uint64_t atom_size = 0;
5185 short previous_atom = 0; // only gets used in testing for atom under stsd
5186
5187 // fprintf(stdout, "current atom is named %s, num:%i\n",
5188 // parsedAtoms[rev_atom_loop].AtomicName,
5189 // parsedAtoms[rev_atom_loop].AtomicNumber);
5190
5191 if (rev_atom_loop == 0) {
5192 break; // we seem to have hit the first atom
5193 } else {
5194 previous_atom = APar_FindPrecedingAtom(rev_atom_loop);
5195 }
5196
5197 uint32_t _atom_ =
5198 UInt32FromBigEndian(parsedAtoms[rev_atom_loop].AtomicName);
5199 switch (_atom_) {
5200 // the enumerated atoms here are all of DUAL_STATE_ATOM type
5201 case 0x6D657461: //'meta'
5202 atom_size += 12;
5203 break;
5204
5205 case 0x73747364: //'stsd'
5206 atom_size += 16;
5207 break;
5208
5209 case 0x64726566: //'dref'
5210 atom_size += 16;
5211 break;
5212
5213 case 0x69696E66: //'iinf'
5214 atom_size += 14;
5215 break;
5216
5217 // accommodate parsing of atoms under stsd when required
5218 case 0x6D703473: { // mp4s
5219 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 &&
5220 parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM &&
5221 deep_atom_scan) {
5222 atom_size += 16;
5223 } else {
5224 atom_size += 8;
5225 }
5226 break;
5227 }
5228 case 0x73727470: // srtp
5229 case 0x72747020: { //'rtp '
5230 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 &&
5231 parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM &&
5232 deep_atom_scan) {
5233 atom_size += 24;
5234 } else {
5235 atom_size += 8;
5236 }
5237 break;
5238 }
5239 case 0x616C6163: // alac
5240 case 0x6D703461: // mp4a
5241 case 0x73616D72: // samr
5242 case 0x73617762: // sawb
5243 case 0x73617770: // sawp
5244 case 0x73657663: // sevc
5245 case 0x73716370: // sqcp
5246 case 0x73736D76: // ssmv
5247 case 0x64726D73: { // drms
5248 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 &&
5249 parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM &&
5250 deep_atom_scan) {
5251 atom_size += 36;
5252 } else {
5253 atom_size += 8;
5254 }
5255 break;
5256 }
5257 case 0x74783367: { // tx3g
5258 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 &&
5259 parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM &&
5260 deep_atom_scan) {
5261 atom_size += 46;
5262 } else {
5263 atom_size += 8;
5264 }
5265 break;
5266 }
5267 case 0x6D6A7032: // mjp2
5268 case 0x6D703476: // mp4v
5269 case 0x61766331: // avc1
5270 case 0x6A706567: // jpeg
5271 case 0x73323633: // s263
5272 case 0x64726D69: { // drmi
5273 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 &&
5274 parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM &&
5275 deep_atom_scan) {
5276 atom_size += 86;
5277 } else {
5278 atom_size += 8;
5279 }
5280 break;
5281 }
5282
5283 default:
5284 if (parsedAtoms[rev_atom_loop].AtomicLevel == 7 &&
5285 parsedAtoms[rev_atom_loop].AtomicContainerState == DUAL_STATE_ATOM) {
5286 atom_size += parsedAtoms[rev_atom_loop].AtomicLength;
5287 } else {
5288 atom_size += 8; // all atoms have *at least* 4bytes length & 4 bytes
5289 // name
5290 }
5291 break;
5292 }
5293
5294 if (parsedAtoms[rev_atom_loop].NextAtomNumber != 0) {
5295 next_atom = parsedAtoms[rev_atom_loop].NextAtomNumber;
5296 }
5297
5298 while (parsedAtoms[next_atom].AtomicLevel >
5299 parsedAtoms[rev_atom_loop].AtomicLevel) { // eval all child atoms....
5300 // fprintf(stdout, "\ttest child atom %s, level:%i (sum %" PRIu64 ")\n",
5301 // parsedAtoms[next_atom].AtomicName, parsedAtoms[next_atom].AtomicLevel,
5302 // atom_size);
5303 if (parsedAtoms[rev_atom_loop].AtomicLevel ==
5304 (parsedAtoms[next_atom].AtomicLevel -
5305 1)) { // only child atoms 1 level down
5306 atom_size += parsedAtoms[next_atom].AtomicLength;
5307 // fprintf(stdout, "\t\teval child atom %s, level:%i (sum %" PRIu64
5308 // ")\n", parsedAtoms[next_atom].AtomicName,
5309 // parsedAtoms[next_atom].AtomicLevel, atom_size); fprintf(stdout,
5310 // "\t\teval %s's child atom %s, level:%i (sum %" PRIu64 ", added %"
5311 // PRIu64 ")\n", parsedAtoms[previous_atom].AtomicName,
5312 // parsedAtoms[next_atom].AtomicName,
5313 // parsedAtoms[next_atom].AtomicLevel, atom_size,
5314 // parsedAtoms[next_atom].AtomicLength);
5315 } else if (parsedAtoms[next_atom].AtomicLevel <
5316 parsedAtoms[rev_atom_loop].AtomicLevel) {
5317 break;
5318 }
5319 next_atom =
5320 parsedAtoms[next_atom].NextAtomNumber; // increment to eval next atom
5321 parsedAtoms[rev_atom_loop].AtomicLength = atom_size;
5322 }
5323
5324 if (_atom_ == 0x75647461 &&
5325 parsedAtoms[rev_atom_loop].AtomicLevel >
5326 parsedAtoms[parsedAtoms[rev_atom_loop].NextAtomNumber]
5327 .AtomicLevel) { // udta with no child atoms; get here by erasing
5328 // the last asset in a 3gp file, and it won't
5329 // quite erase because udta thinks its the
5330 // former AtomicLength
5331 parsedAtoms[rev_atom_loop].AtomicLength = 8;
5332 }
5333 if (_atom_ == 0x6D657461 &&
5334 parsedAtoms[rev_atom_loop].AtomicLevel !=
5335 parsedAtoms[parsedAtoms[rev_atom_loop].NextAtomNumber].AtomicLevel -
5336 1) { // meta with no child atoms; get here by erasing the last
5337 // existing uuid atom.
5338 parsedAtoms[rev_atom_loop].AtomicLength = 12;
5339 }
5340 if (_atom_ == 0x696C7374 &&
5341 parsedAtoms[rev_atom_loop].AtomicLevel !=
5342 parsedAtoms[parsedAtoms[rev_atom_loop].NextAtomNumber].AtomicLevel -
5343 1) { // ilst with no child atoms; get here by erasing the last
5344 // piece of iTunes style metadata
5345 parsedAtoms[rev_atom_loop].AtomicLength = 8;
5346 }
5347
5348 rev_atom_loop =
5349 APar_FindPrecedingAtom(parsedAtoms[rev_atom_loop].AtomicNumber);
5350 }
5351 APar_DetermineNewFileLength();
5352 // APar_SimpleAtomPrintout();
5353 // APar_PrintAtomicTree();
5354 return;
41105355 }
41115356
41125357 ///////////////////////////////////////////////////////////////////////////////////////
4113 // Atom Writing Functions //
5358 // Atom Writing Functions //
41145359 ///////////////////////////////////////////////////////////////////////////////////////
41155360
41165361 /*----------------------
41175362 APar_ValidateAtoms
41185363
4119 A gaggle of tests go on here - to TRY to make sure that files are not corrupted.
4120
4121 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)
4122 2. test that the atom name is at least 4 letters long. So far, only quicktime atoms have NULLs in their names.
4123 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
4124 4. Test to make sure 'mdat' is at file-level. That is the only place it should ever be.
4125 5. If its is a child atom that was set (and resides in memory), then its AtomicData should != NULL.
4126 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
4127 7. If the file shunk below 90% (after accounting for additions or removals), error out - something went awry.
5364 A gaggle of tests go on here - to TRY to make sure that files are not
5365 corrupted.
5366
5367 1. because there is a limit to the number of atoms, test to make
5368 sure we haven't hit MAX_ATOMS (probably only likely on a 300MB fragmented file
5369 ever 2 secs)
5370 2. test that the atom name is at least 4 letters long. So far,
5371 only quicktime atoms have NULLs in their names.
5372 3. For files over 300k, make sure that no atom can present
5373 larger than the filesize (which would be bad); handy for when the file isn't
5374 parsed correctly
5375 4. Test to make sure 'mdat' is at file-level. That is the only
5376 place it should ever be.
5377 5. If its is a child atom that was set (and resides in memory),
5378 then its AtomicData should != NULL.
5379 6. (A crude) Test to see if 'trak' atoms have only a 'udta'
5380 child. If setting a copyright notice on a track at index built with some
5381 compilers faux 'trak's are made
5382 7. If the file shunk below 90% (after accounting for additions
5383 or removals), error out - something went awry.
41285384 ----------------------*/
41295385 void APar_ValidateAtoms() {
4130 bool atom_name_with_4_characters = true;
4131 short iter = 0;
4132 uint64_t simple_tally = 0;
4133 uint8_t atom_ftyp_count = 0;
4134 uint16_t external_data = 0;
4135
4136 //test1
4137 if (atom_number > MAX_ATOMS) {
4138 fprintf(stderr, "AtomicParsley error: amount of atoms exceeds internal limit. Aborting.\n");
4139 exit(1);
4140 }
4141
4142 while (true) {
4143 //test2
4144 // 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
4145 if ( strlen(parsedAtoms[iter].AtomicName) < 4 && parsedAtoms[iter].AtomicClassification != EXTENDED_ATOM) {
4146 atom_name_with_4_characters = false;
4147 }
4148
4149 //test3
4150 //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)
4151 if (parsedAtoms[iter].AtomicLength > file_size && file_size > 300000) {
4152 if (parsedAtoms[iter].AtomicData == NULL) {
4153 fprintf(stderr, "AtomicParsley error: an atom was detected that presents as larger than filesize. Aborting. %c\n", '\a');
4154 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);
4155 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
4156 }
4157 }
4158
4159 if (parsedAtoms[iter].AtomicLevel == 1) {
4160 if (parsedAtoms[iter].AtomicLength == 0 && strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0) {
4161 simple_tally = file_size - parsedAtoms[iter].AtomicStart;
4162 } else {
4163 simple_tally += parsedAtoms[iter].AtomicLength == 1 ? parsedAtoms[iter].AtomicLengthExtended : parsedAtoms[iter].AtomicLength;
4164 }
4165 }
4166
4167 //test4
4168 if (strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0 && parsedAtoms[iter].AtomicLevel != 1) {
4169 fprintf(stderr, "AtomicParsley error: mdat atom was found not at file level. Aborting. %c\n", '\a');
4170 exit(1); //the error which forced this was some bad atom length redetermination; probably won't be fixed
4171 }
4172 //test5
4173 if (parsedAtoms[iter].AtomicStart == 0 && parsedAtoms[iter].AtomicData == NULL &&
4174 parsedAtoms[iter].AtomicNumber > 0 && parsedAtoms[iter].AtomicContainerState == CHILD_ATOM) {
4175 fprintf(stderr, "AtomicParsley error: a '%s' atom was rendered to NULL length. Aborting. %c\n", parsedAtoms[iter].AtomicName, '\a');
4176 exit(1); //data was not written to AtomicData for this new atom.
4177 }
4178
4179 //test6
4180 if (memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 && parsedAtoms[iter+1].NextAtomNumber != 0) { //prevent writing any malformed tracks
4181 if (!(memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "tkhd", 4) == 0 ||
4182 memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "tref", 4) == 0 ||
4183 memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "mdia", 4) == 0 ||
4184 memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "edts", 4) == 0 ||
4185 memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "meta", 4) == 0 ||
4186 memcmp(parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, "tapt", 4) == 0 )) {
4187 if (APar_ReturnChildrenAtoms(iter, 0) < 2) {
4188 //a 'trak' must contain 'tkhd' & 'mdia' at the very least
4189 fprintf(stderr, "AtomicParsley error: incorrect track structure containing atom %s. %c\n", parsedAtoms[ parsedAtoms[iter].NextAtomNumber ].AtomicName, '\a');
4190 exit(1);
4191 }
4192 }
4193 }
4194
4195 if (memcmp(parsedAtoms[iter].AtomicName, "ftyp", 4) == 0) {
4196 atom_ftyp_count++;
4197 }
4198
4199 if ( (memcmp(parsedAtoms[iter].AtomicName, "stco", 4) == 0 || memcmp(parsedAtoms[iter].AtomicName, "co64", 4) == 0) && parsedAtoms[iter].ancillary_data != 1) {
4200 external_data++;
4201 }
4202 iter=parsedAtoms[iter].NextAtomNumber;
4203 if (iter == 0) {
4204 break;
4205 }
4206 }
4207
4208 //test7
4209 double perdiff = (double)((double)(simple_tally) * 100.0 / (double)(file_size-removed_bytes_tally) );
4210 int percentage_difference = (int)lroundf(perdiff);
4211
4212 if (percentage_difference < 90 && file_size > 300000) { //only kick in when files are over 300k & 90% of the size
4213 fprintf(stderr, "AtomicParsley error: total existing atoms present as larger than filesize. Aborting. %c\n", '\a');
4214 //APar_PrintAtomicTree();
4215 fprintf(stdout, "%i %" PRIu64 "\n", percentage_difference, simple_tally);
4216 exit(1);
4217 }
4218
4219 if (atom_ftyp_count != 1) {
4220 fprintf(stdout, "AtomicParsley error: unresolved looping of atoms. Aborting. %c\n", '\a');
4221 exit(1);
4222 }
4223
4224 if (!atom_name_with_4_characters) {
4225 fprintf(stdout, "AtomicParsley warning: atom(s) were detected with atypical names containing NULLs\n");
4226 }
4227
4228 if (external_data > 0) {
4229 fprintf(stdout, "AtomicParsley warning: externally referenced data found.");
4230 }
4231
4232 return;
4233 }
4234
4235 void APar_DeriveNewPath(const char *filePath, char* temp_path, int output_type, const char* file_kind, const char* forced_suffix, bool random_filename = true) {
4236 const char* suffix = NULL;
4237 const char* file_name = NULL;
4238 size_t file_name_len = 0;
4239 bool relative_path = false;
4240
4241 if (forced_suffix == NULL) {
4242 suffix = strrchr(filePath, '.');
4243 } else {
4244 suffix = forced_suffix;
4245 }
4246
4247 size_t filepath_len = strlen(filePath);
4248 size_t base_len = filepath_len-strlen(suffix);
4249 if (output_type >= 0) {
4250 memcpy(temp_path, filePath, base_len);
4251 memcpy(temp_path + base_len, file_kind, strlen(file_kind));
4252
4253 } else if (output_type == -1) { //make the output file invisible by prefacing the filename with '.'
4254 #if defined (_WIN32) && !defined (__CYGWIN__)
4255 memcpy(temp_path, filePath, base_len);
4256 memcpy(temp_path + base_len, file_kind, strlen(file_kind));
5386 bool atom_name_with_4_characters = true;
5387 short iter = 0;
5388 uint64_t simple_tally = 0;
5389 uint8_t atom_ftyp_count = 0;
5390 uint16_t external_data = 0;
5391
5392 // test1
5393 if (atom_number > MAX_ATOMS) {
5394 fprintf(stderr,
5395 "AtomicParsley error: amount of atoms exceeds internal "
5396 "limit. Aborting.\n");
5397 exit(1);
5398 }
5399
5400 while (true) {
5401 // test2
5402 // there are valid atom names that are 0x00000001 - but I haven't seen them
5403 // in MPEG-4 files, but they could show up, so this isn't a hard error
5404 if (strlen(parsedAtoms[iter].AtomicName) < 4 &&
5405 parsedAtoms[iter].AtomicClassification != EXTENDED_ATOM) {
5406 atom_name_with_4_characters = false;
5407 }
5408
5409 // test3
5410 // test for atoms that are going to be greater than out current file size;
5411 // problem is we could be adding a 1MB pix to a 200k 3gp file; only fail for
5412 // a file > 300k file; otherwise there would have to be more checks (like
5413 // artwork present, but a zealous tagger could make moov.lengt > filzesize)
5414 if (parsedAtoms[iter].AtomicLength > file_size && file_size > 300000) {
5415 if (parsedAtoms[iter].AtomicData == NULL) {
5416 fprintf(stderr,
5417 "AtomicParsley error: an atom was detected that presents as "
5418 "larger than filesize. Aborting. %c\n",
5419 '\a');
5420 fprintf(stderr,
5421 "atom %s is %" PRIu64
5422 " bytes long which is greater than the filesize of %" PRIu64
5423 "\n",
5424 parsedAtoms[iter].AtomicName,
5425 parsedAtoms[iter].AtomicLength,
5426 file_size);
5427 exit(
5428 1); // its conceivable to repair such an off length by the
5429 // surrounding atoms constrained by file_size - just not anytime
5430 // soon; probly would catch a foobar2000 0.9 tagged file
5431 }
5432 }
5433
5434 if (parsedAtoms[iter].AtomicLevel == 1) {
5435 if (parsedAtoms[iter].AtomicLength == 0 &&
5436 strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0) {
5437 simple_tally = file_size - parsedAtoms[iter].AtomicStart;
5438 } else {
5439 simple_tally += parsedAtoms[iter].AtomicLength == 1
5440 ? parsedAtoms[iter].AtomicLengthExtended
5441 : parsedAtoms[iter].AtomicLength;
5442 }
5443 }
5444
5445 // test4
5446 if (strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0 &&
5447 parsedAtoms[iter].AtomicLevel != 1) {
5448 fprintf(stderr,
5449 "AtomicParsley error: mdat atom was found not at file level. "
5450 "Aborting. %c\n",
5451 '\a');
5452 exit(1); // the error which forced this was some bad atom length
5453 // redetermination; probably won't be fixed
5454 }
5455 // test5
5456 if (parsedAtoms[iter].AtomicStart == 0 &&
5457 parsedAtoms[iter].AtomicData == NULL &&
5458 parsedAtoms[iter].AtomicNumber > 0 &&
5459 parsedAtoms[iter].AtomicContainerState == CHILD_ATOM) {
5460 fprintf(stderr,
5461 "AtomicParsley error: a '%s' atom was rendered to NULL length. "
5462 "Aborting. %c\n",
5463 parsedAtoms[iter].AtomicName,
5464 '\a');
5465 exit(1); // data was not written to AtomicData for this new atom.
5466 }
5467
5468 // test6
5469 if (memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 &&
5470 parsedAtoms[iter + 1].NextAtomNumber !=
5471 0) { // prevent writing any malformed tracks
5472 if (!(memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName,
5473 "tkhd",
5474 4) == 0 ||
5475 memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName,
5476 "tref",
5477 4) == 0 ||
5478 memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName,
5479 "mdia",
5480 4) == 0 ||
5481 memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName,
5482 "edts",
5483 4) == 0 ||
5484 memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName,
5485 "meta",
5486 4) == 0 ||
5487 memcmp(parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName,
5488 "tapt",
5489 4) == 0)) {
5490 if (APar_ReturnChildrenAtoms(iter, 0) < 2) {
5491 // a 'trak' must contain 'tkhd' & 'mdia' at the very least
5492 fprintf(stderr,
5493 "AtomicParsley error: incorrect track structure containing "
5494 "atom %s. %c\n",
5495 parsedAtoms[parsedAtoms[iter].NextAtomNumber].AtomicName,
5496 '\a');
5497 exit(1);
5498 }
5499 }
5500 }
5501
5502 if (memcmp(parsedAtoms[iter].AtomicName, "ftyp", 4) == 0) {
5503 atom_ftyp_count++;
5504 }
5505
5506 if ((memcmp(parsedAtoms[iter].AtomicName, "stco", 4) == 0 ||
5507 memcmp(parsedAtoms[iter].AtomicName, "co64", 4) == 0) &&
5508 parsedAtoms[iter].ancillary_data != 1) {
5509 external_data++;
5510 }
5511 iter = parsedAtoms[iter].NextAtomNumber;
5512 if (iter == 0) {
5513 break;
5514 }
5515 }
5516
5517 // test7
5518 double perdiff = (double)((double)(simple_tally)*100.0 /
5519 (double)(file_size - removed_bytes_tally));
5520 int percentage_difference = (int)lroundf(perdiff);
5521
5522 if (percentage_difference < 90 &&
5523 file_size >
5524 300000) { // only kick in when files are over 300k & 90% of the size
5525 fprintf(stderr,
5526 "AtomicParsley error: total existing atoms present as larger than "
5527 "filesize. Aborting. %c\n",
5528 '\a');
5529 // APar_PrintAtomicTree();
5530 fprintf(stdout, "%i %" PRIu64 "\n", percentage_difference, simple_tally);
5531 exit(1);
5532 }
5533
5534 if (atom_ftyp_count != 1) {
5535 fprintf(stdout,
5536 "AtomicParsley error: unresolved looping of atoms. Aborting. %c\n",
5537 '\a');
5538 exit(1);
5539 }
5540
5541 if (!atom_name_with_4_characters) {
5542 fprintf(stdout,
5543 "AtomicParsley warning: atom(s) were detected with "
5544 "atypical names containing NULLs\n");
5545 }
5546
5547 if (external_data > 0) {
5548 fprintf(stdout, "AtomicParsley warning: externally referenced data found.");
5549 }
5550
5551 return;
5552 }
5553
5554 void APar_DeriveNewPath(const char *filePath,
5555 char *temp_path,
5556 int output_type,
5557 const char *file_kind,
5558 const char *forced_suffix,
5559 bool random_filename = true) {
5560 const char *suffix = NULL;
5561 const char *file_name = NULL;
5562 size_t file_name_len = 0;
5563 bool relative_path = false;
5564
5565 if (forced_suffix == NULL) {
5566 suffix = strrchr(filePath, '.');
5567 } else {
5568 suffix = forced_suffix;
5569 }
5570
5571 size_t filepath_len = strlen(filePath);
5572 size_t base_len = filepath_len - strlen(suffix);
5573 if (output_type >= 0) {
5574 memcpy(temp_path, filePath, base_len);
5575 memcpy(temp_path + base_len, file_kind, strlen(file_kind));
5576
5577 } else if (output_type == -1) { // make the output file invisible by prefacing
5578 // the filename with '.'
5579 #if defined(_WIN32) && !defined(__CYGWIN__)
5580 memcpy(temp_path, filePath, base_len);
5581 memcpy(temp_path + base_len, file_kind, strlen(file_kind));
42575582 #else
4258 file_name = strrchr(filePath, '/');
4259 if (file_name != NULL) {
4260 file_name_len = strlen(file_name);
4261 memcpy(temp_path, filePath, filepath_len-file_name_len+1);
4262 } else {
4263 if( getcwd(temp_path, MAXPATHLEN) == NULL ) {
4264 printf("Error getting working directory: %s\n", strerror(errno));
4265 exit(1);
4266 }
4267 file_name = (char*)filePath;
4268 file_name_len = strlen(file_name);
4269 memcpy(temp_path + strlen(temp_path), "/", 1);
4270 relative_path = true;
4271 }
4272 memcpy(temp_path + strlen(temp_path), ".", 1);
4273 memcpy(temp_path + strlen(temp_path), (relative_path ? file_name : file_name+1), file_name_len - strlen(suffix) -1);
4274 memcpy(temp_path + strlen(temp_path), file_kind, strlen(file_kind));
5583 file_name = strrchr(filePath, '/');
5584 if (file_name != NULL) {
5585 file_name_len = strlen(file_name);
5586 memcpy(temp_path, filePath, filepath_len - file_name_len + 1);
5587 } else {
5588 if (getcwd(temp_path, MAXPATHLEN) == NULL) {
5589 printf("Error getting working directory: %s\n", strerror(errno));
5590 exit(1);
5591 }
5592 file_name = (char *)filePath;
5593 file_name_len = strlen(file_name);
5594 memcpy(temp_path + strlen(temp_path), "/", 1);
5595 relative_path = true;
5596 }
5597 memcpy(temp_path + strlen(temp_path), ".", 1);
5598 memcpy(temp_path + strlen(temp_path),
5599 (relative_path ? file_name : file_name + 1),
5600 file_name_len - strlen(suffix) - 1);
5601 memcpy(temp_path + strlen(temp_path), file_kind, strlen(file_kind));
42755602 #endif
4276 }
4277
4278 if (random_filename) {
4279 char randstring[6];
4280 srand((int) time(NULL)); //Seeds rand()
4281 int randNum = rand()%100000;
4282 sprintf(randstring, "%i", randNum);
4283 memcpy(temp_path + strlen(temp_path), randstring, strlen(randstring));
4284 }
4285
4286 if (forced_suffix_type == FORCE_M4B_TYPE) {
4287 memcpy(temp_path + strlen(temp_path), ".m4b", 4);
4288 } else {
4289 memcpy(temp_path + strlen(temp_path), suffix, strlen(suffix) );
4290 }
4291 return;
4292 }
4293
4294 void APar_MetadataFileDump(const char* ISObasemediafile) {
4295 char* dump_file_name=(char*)malloc( sizeof(char)* (strlen(ISObasemediafile) +12 +1) );
4296 memset(dump_file_name, 0, sizeof(char)* (strlen(ISObasemediafile) +12 +1) );
4297
4298 FILE* dump_file;
4299 AtomicInfo* userdata_atom = APar_FindAtom("moov.udta", false, SIMPLE_ATOM, 0);
4300
4301 //make sure that the atom really exists
4302 if (userdata_atom != NULL) {
4303 char* dump_buffer=(char*)malloc( sizeof(char)* userdata_atom->AtomicLength +1 );
4304 memset(dump_buffer, 0, sizeof(char)* userdata_atom->AtomicLength +1 );
4305
4306 APar_DeriveNewPath(ISObasemediafile, dump_file_name, 1, "-dump-", ".raw");
4307 dump_file = APar_OpenFile(dump_file_name, "wb");
4308 if (dump_file != NULL) {
4309 //body of atom writing here
4310
4311 APar_readX(dump_buffer, source_file, userdata_atom->AtomicStart, (size_t)userdata_atom->AtomicLength);
4312
4313 fwrite(dump_buffer, (size_t)userdata_atom->AtomicLength, 1, dump_file);
4314 fclose(dump_file);
4315
4316 fprintf(stdout, " Metadata dumped to %s\n", dump_file_name);
4317 }
4318 free(dump_buffer);
4319 dump_buffer=NULL;
4320
4321 } else {
4322 fprintf(stdout, "AtomicParsley error: no moov.udta atom was found to dump out to file.\n");
4323 }
4324
4325 return;
4326 }
4327
4328 void APar_UpdateModTime(AtomicInfo* container_header_atom) {
4329 container_header_atom->AtomicData = (char*)calloc(1, sizeof(char)* (size_t)container_header_atom->AtomicLength );
4330 APar_readX(container_header_atom->AtomicData, source_file, container_header_atom->AtomicStart+12, container_header_atom->AtomicLength-12);
4331
4332 uint32_t current_time = APar_get_mpeg4_time();
4333 if ((container_header_atom->AtomicVerFlags & 0xFFFFFF) == 1) {
4334 UInt64_TO_String8(current_time, container_header_atom->AtomicData+8);
4335 } else {
4336 UInt32_TO_String4(current_time, container_header_atom->AtomicData+4);
4337 }
4338 return;
5603 }
5604
5605 if (random_filename) {
5606 char randstring[6];
5607 srand((int)time(NULL)); // Seeds rand()
5608 int randNum = rand() % 100000;
5609 sprintf(randstring, "%i", randNum);
5610 memcpy(temp_path + strlen(temp_path), randstring, strlen(randstring));
5611 }
5612
5613 if (forced_suffix_type == FORCE_M4B_TYPE) {
5614 memcpy(temp_path + strlen(temp_path), ".m4b", 4);
5615 } else {
5616 memcpy(temp_path + strlen(temp_path), suffix, strlen(suffix));
5617 }
5618 return;
5619 }
5620
5621 void APar_MetadataFileDump(const char *ISObasemediafile) {
5622 char *dump_file_name =
5623 (char *)malloc(sizeof(char) * (strlen(ISObasemediafile) + 12 + 1));
5624 memset(dump_file_name, 0, sizeof(char) * (strlen(ISObasemediafile) + 12 + 1));
5625
5626 FILE *dump_file;
5627 AtomicInfo *userdata_atom = APar_FindAtom("moov.udta", false, SIMPLE_ATOM, 0);
5628
5629 // make sure that the atom really exists
5630 if (userdata_atom != NULL) {
5631 char *dump_buffer =
5632 (char *)malloc(sizeof(char) * userdata_atom->AtomicLength + 1);
5633 memset(dump_buffer, 0, sizeof(char) * userdata_atom->AtomicLength + 1);
5634
5635 APar_DeriveNewPath(ISObasemediafile, dump_file_name, 1, "-dump-", ".raw");
5636 dump_file = APar_OpenFile(dump_file_name, "wb");
5637 if (dump_file != NULL) {
5638 // body of atom writing here
5639
5640 APar_readX(dump_buffer,
5641 source_file,
5642 userdata_atom->AtomicStart,
5643 (size_t)userdata_atom->AtomicLength);
5644
5645 fwrite(dump_buffer, (size_t)userdata_atom->AtomicLength, 1, dump_file);
5646 fclose(dump_file);
5647
5648 fprintf(stdout, " Metadata dumped to %s\n", dump_file_name);
5649 }
5650 free(dump_buffer);
5651 dump_buffer = NULL;
5652
5653 } else {
5654 fprintf(stdout,
5655 "AtomicParsley error: no moov.udta atom was found to dump "
5656 "out to file.\n");
5657 }
5658
5659 return;
5660 }
5661
5662 void APar_UpdateModTime(AtomicInfo *container_header_atom) {
5663 container_header_atom->AtomicData = (char *)calloc(
5664 1, sizeof(char) * (size_t)container_header_atom->AtomicLength);
5665 APar_readX(container_header_atom->AtomicData,
5666 source_file,
5667 container_header_atom->AtomicStart + 12,
5668 container_header_atom->AtomicLength - 12);
5669
5670 uint32_t current_time = APar_get_mpeg4_time();
5671 if ((container_header_atom->AtomicVerFlags & 0xFFFFFF) == 1) {
5672 UInt64_TO_String8(current_time, container_header_atom->AtomicData + 8);
5673 } else {
5674 UInt32_TO_String4(current_time, container_header_atom->AtomicData + 4);
5675 }
5676 return;
43395677 }
43405678
43415679 void APar_ShellProgressBar(uint64_t bytes_written) {
4342 if (dynUpd.updage_by_padding) {
4343 return;
4344 }
4345 static int update_count = 0;
4346
4347 if (update_count++ < 5) {
4348 return;
4349 }
4350 update_count = 0;
4351
4352 double dispprog = (double)bytes_written / (double)new_file_size
4353 * max_display_width;
4354 int display_progress = (int)lroundf(dispprog);
4355 double percomp = 100.0 * (double)bytes_written / (double)new_file_size;
4356 int percentage_complete = (int)lroundf(percomp);
4357
4358 char *p = file_progress_buffer;
4359 strcpy(p, " Progress: ");
4360 p += strlen(p);
4361
4362 memset(p, '=', display_progress);
4363 p += display_progress;
4364
4365 sprintf(p, ">%3d%% ", percentage_complete);
4366 p += strlen(p);
4367
4368 memset(p, '-', max_display_width - display_progress);
4369 p += max_display_width - display_progress;
4370 p[0] = '|';
4371 p[1] = '\0';
4372
4373 fprintf(stdout, "%s\r", file_progress_buffer);
4374 fflush(stdout);
4375 }
4376
4377 void APar_MergeTempFile(FILE* dest_file, FILE *src_file, uint64_t src_file_size, uint64_t dest_position, char* &buffer) {
4378 uint64_t file_pos = 0;
4379 while (file_pos <= src_file_size) {
4380 if (file_pos + max_buffer <= src_file_size ) {
4381 APar_readX(buffer, src_file, file_pos, max_buffer);
4382
4383 //fseek(dest_file, dest_position + file_pos, SEEK_SET);
4384 #if defined(_WIN32)
4385 fpos_t file_offset = dest_position + file_pos;
4386 #elif defined(__GLIBC__)
4387 fpos_t file_offset = {0};
4388 file_offset.__pos = dest_position + file_pos;
5680 if (dynUpd.updage_by_padding) {
5681 return;
5682 }
5683 static int update_count = 0;
5684
5685 if (update_count++ < 5) {
5686 return;
5687 }
5688 update_count = 0;
5689
5690 double dispprog =
5691 (double)bytes_written / (double)new_file_size * max_display_width;
5692 int display_progress = (int)lroundf(dispprog);
5693 double percomp = 100.0 * (double)bytes_written / (double)new_file_size;
5694 int percentage_complete = (int)lroundf(percomp);
5695
5696 char *p = file_progress_buffer;
5697 strcpy(p, " Progress: ");
5698 p += strlen(p);
5699
5700 memset(p, '=', display_progress);
5701 p += display_progress;
5702
5703 sprintf(p, ">%3d%% ", percentage_complete);
5704 p += strlen(p);
5705
5706 memset(p, '-', max_display_width - display_progress);
5707 p += max_display_width - display_progress;
5708 p[0] = '|';
5709 p[1] = '\0';
5710
5711 fprintf(stdout, "%s\r", file_progress_buffer);
5712 fflush(stdout);
5713 }
5714
5715 void APar_MergeTempFile(FILE *dest_file,
5716 FILE *src_file,
5717 uint64_t src_file_size,
5718 uint64_t dest_position,
5719 char *&buffer) {
5720 uint64_t file_pos = 0;
5721 while (file_pos <= src_file_size) {
5722 if (file_pos + max_buffer <= src_file_size) {
5723 APar_readX(buffer, src_file, file_pos, max_buffer);
5724
5725 fseeko(dest_file, dest_position + file_pos, SEEK_SET);
5726 fwrite(buffer, max_buffer, 1, dest_file);
5727 file_pos += max_buffer;
5728
5729 } else {
5730 APar_readX(buffer, src_file, file_pos, src_file_size - file_pos);
5731 // fprintf(stdout, "buff starts with %s\n", buffer+4);
5732 fseeko(dest_file, dest_position + file_pos, SEEK_SET);
5733 fwrite(buffer, src_file_size - file_pos, 1, dest_file);
5734 file_pos += src_file_size - file_pos;
5735 break;
5736 }
5737 }
5738 if (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) {
5739 #if defined(_WIN32) && !defined(__CYGWIN__)
5740 fflush(dest_file);
5741 SetEndOfFile((HANDLE)_get_osfhandle(_fileno(dest_file)));
43895742 #else
4390 off_t file_offset = dest_position + file_pos;
5743 if (ftruncate(fileno(dest_file), src_file_size + dest_position) == -1) {
5744 perror("Failed to truncate file: ");
5745 exit(1);
5746 }
43915747 #endif
4392 fsetpos(dest_file, &file_offset);
4393 fwrite(buffer, max_buffer, 1, dest_file);
4394 file_pos += max_buffer;
4395
4396 } else {
4397 APar_readX(buffer, src_file, file_pos, src_file_size - file_pos);
4398 //fprintf(stdout, "buff starts with %s\n", buffer+4);
4399 #if defined(_WIN32)
4400 fpos_t file_offset = dest_position + file_pos;
4401 #elif defined(__GLIBC__)
4402 fpos_t file_offset = {0};
4403 file_offset.__pos = dest_position + file_pos;
4404 #else
4405 off_t file_offset = dest_position + file_pos;
4406 #endif
4407 fsetpos(dest_file, &file_offset );
4408 fwrite(buffer, src_file_size - file_pos, 1, dest_file);
4409 file_pos += src_file_size - file_pos;
4410 break;
4411 }
4412 }
4413 if (dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) {
4414 #if defined (_WIN32) && !defined (__CYGWIN__)
4415 fflush(dest_file);
4416 SetEndOfFile((HANDLE)_get_osfhandle(_fileno(dest_file)));
4417 #else
4418 if(ftruncate(fileno(dest_file), src_file_size+dest_position) == -1) {
4419 perror("Failed to truncate file: ");
4420 exit(1);
4421 }
4422 #endif
4423 }
4424 return;
5748 }
5749 return;
44255750 }
44265751
44275752 #ifdef __linux__
44285753 /* use kernel provided zero-copy interface to improve throughput
44295754 * around the data passthru portions of our operation; no sense
44305755 * copying multiple GB of data around in memory if we can avoid it */
4431 uint64_t splice_copy(int sfd, int ofd, uint64_t block_size,
4432 uint64_t src_offset, uint64_t dest_offset, uint64_t tally)
4433 {
4434 int pfd[2];
4435 int res;
4436 uint64_t lim = LONG_MAX;
4437 loff_t spos = src_offset;
4438 loff_t dpos = dest_offset;
4439 long didread;
4440 long didwrite;
4441 uint64_t bytes_written = 0;
4442
4443 res = pipe(pfd);
4444 if (res < 0) {
4445 return 0;
4446 }
4447
4448 while (block_size) {
4449 long toread = MIN(block_size, lim);
4450
4451 /* splice source data into pipe.
4452 * This will typically be 64k at a time */
4453 didread = splice(sfd, &spos, pfd[1], NULL, toread,
4454 SPLICE_F_MORE|SPLICE_F_MOVE);
4455
4456 if (didread <= 0) {
4457 if (errno == EINVAL || errno == ENOSYS) {
4458 /* splice is not supported by source */
4459 break;
4460 }
4461 fprintf(stderr, "splice(read): %ld of %lu (%s)\n",
4462 didread, toread, strerror(errno));
4463 break;
4464 }
4465
4466 block_size -= didread;
4467
4468 while (didread > 0) {
4469 /* splice from pipe into dest */
4470 didwrite = splice(pfd[0], NULL, ofd, &dpos, didread,
4471 SPLICE_F_MORE|SPLICE_F_MOVE);
4472
4473 if (didwrite <= 0) {
4474 if (errno == EINVAL || errno == ENOSYS) {
4475 /* splice is not supported by dest */
4476 break;
4477 }
4478 fprintf(stderr, "splice(write): %ld of %lu (%s)\n",
4479 didwrite, didread, strerror(errno));
4480 break;
4481 }
4482
4483 bytes_written += didwrite;
4484 didread -= didwrite;
4485 }
4486 APar_ShellProgressBar(tally + bytes_written);
4487 }
4488
4489 close(pfd[0]);
4490 close(pfd[1]);
4491 return bytes_written;
5756 uint64_t splice_copy(int sfd,
5757 int ofd,
5758 uint64_t block_size,
5759 uint64_t src_offset,
5760 uint64_t dest_offset,
5761 uint64_t tally) {
5762 int pfd[2];
5763 int res;
5764 uint64_t lim = LONG_MAX;
5765 loff_t spos = src_offset;
5766 loff_t dpos = dest_offset;
5767 long didread;
5768 long didwrite;
5769 uint64_t bytes_written = 0;
5770
5771 res = pipe(pfd);
5772 if (res < 0) {
5773 return 0;
5774 }
5775
5776 while (block_size) {
5777 long toread = std::min(block_size, lim);
5778
5779 /* splice source data into pipe.
5780 * This will typically be 64k at a time */
5781 didread =
5782 splice(sfd, &spos, pfd[1], NULL, toread, SPLICE_F_MORE | SPLICE_F_MOVE);
5783
5784 if (didread <= 0) {
5785 if (errno == EINVAL || errno == ENOSYS) {
5786 /* splice is not supported by source */
5787 break;
5788 }
5789 fprintf(stderr,
5790 "splice(read): %ld of %lu (%s)\n",
5791 didread,
5792 toread,
5793 strerror(errno));
5794 break;
5795 }
5796
5797 block_size -= didread;
5798
5799 while (didread > 0) {
5800 /* splice from pipe into dest */
5801 didwrite = splice(
5802 pfd[0], NULL, ofd, &dpos, didread, SPLICE_F_MORE | SPLICE_F_MOVE);
5803
5804 if (didwrite <= 0) {
5805 if (errno == EINVAL || errno == ENOSYS) {
5806 /* splice is not supported by dest */
5807 break;
5808 }
5809 fprintf(stderr,
5810 "splice(write): %ld of %lu (%s)\n",
5811 didwrite,
5812 didread,
5813 strerror(errno));
5814 break;
5815 }
5816
5817 bytes_written += didwrite;
5818 didread -= didwrite;
5819 }
5820 APar_ShellProgressBar(tally + bytes_written);
5821 }
5822
5823 close(pfd[0]);
5824 close(pfd[1]);
5825 return bytes_written;
44925826 }
44935827 #endif
44945828
4495 uint64_t block_copy(FILE *source_file, FILE *out_file,
4496 char *&buffer,
4497 uint64_t tally, uint64_t block_size,
4498 uint64_t src_offset, uint64_t dest_offset)
4499 {
4500 uint64_t toread = block_size;
4501 uint64_t bytes_written = 0;
4502 size_t didread;
4503 size_t didwrite;
5829 uint64_t block_copy(FILE *source_file,
5830 FILE *out_file,
5831 char *&buffer,
5832 uint64_t tally,
5833 uint64_t block_size,
5834 uint64_t src_offset,
5835 uint64_t dest_offset) {
5836 uint64_t toread = block_size;
5837 uint64_t bytes_written = 0;
5838 size_t didread;
5839 size_t didwrite;
45045840
45055841 #ifdef __linux__
4506 if (block_size > 65536) {
4507 fflush(out_file);
4508
4509 bytes_written = splice_copy(fileno(source_file), fileno(out_file),
4510 block_size, src_offset, dest_offset, tally);
4511
4512 if (bytes_written != 0) {
4513 return bytes_written;
4514 }
4515 }
5842 if (block_size > 65536) {
5843 fflush(out_file);
5844
5845 bytes_written = splice_copy(fileno(source_file),
5846 fileno(out_file),
5847 block_size,
5848 src_offset,
5849 dest_offset,
5850 tally);
5851
5852 if (bytes_written != 0) {
5853 return bytes_written;
5854 }
5855 }
45165856 #endif
45175857
4518 fseeko(source_file, src_offset, SEEK_SET);
4519 fseeko(out_file, dest_offset, SEEK_SET);
4520
4521 while (toread) {
4522 char *bpos;
4523
4524 didread = fread(buffer, 1, MIN(max_buffer, toread), source_file);
4525 if (didread == 0) {
4526 fprintf(stderr, "read: eof=%d err=%d %s\n",
4527 feof(source_file),
4528 ferror(source_file),
4529 strerror(errno));
4530 break;
4531 }
4532 toread -= didread;
4533
4534 bpos = buffer;
4535
4536 while (didread) {
4537 didwrite = fwrite(bpos, 1, didread, out_file);
4538 didread -= didwrite;
4539 bpos += didwrite;
4540 bytes_written += didwrite;
4541
4542 APar_ShellProgressBar(tally + bytes_written);
4543 }
4544 }
4545 return bytes_written;
4546 }
4547
4548 uint64_t APar_WriteAtomically(FILE* source_file, FILE* temp_file,
4549 bool from_file, char* &buffer, uint64_t bytes_written_tally,
4550 short this_atom)
4551 {
4552 uint64_t bytes_written = 0;
4553
4554 if (parsedAtoms[this_atom].AtomicLength > 1 && parsedAtoms[this_atom].AtomicLength < 8) { //prevents any spurious atoms from appearing
4555 return bytes_written;
4556 }
4557
4558 //write the length of the atom first... taken from our tree in memory
4559 UInt32_TO_String4(parsedAtoms[this_atom].AtomicLength, twenty_byte_buffer);
4560 fseeko(temp_file, bytes_written_tally, SEEK_SET);
4561 fwrite(twenty_byte_buffer, 4, 1, temp_file);
4562 bytes_written += 4;
4563
4564 //since we have already writen the length out to the file, it can be changed now with impunity
4565 if (parsedAtoms[this_atom].AtomicLength == 0) { //the spec says if an atom has a length of 0, it extends to EOF
4566 parsedAtoms[this_atom].AtomicLength = file_size - parsedAtoms[this_atom].AtomicLength;
4567 } else if (parsedAtoms[this_atom].AtomicLength == 1) {
4568 //part of the pseudo 64-bit support
4569 parsedAtoms[this_atom].AtomicLength = parsedAtoms[this_atom].AtomicLengthExtended;
4570 } else if (parsedAtoms[this_atom].AtomicContainerState == DUAL_STATE_ATOM) {
4571 if (memcmp(parsedAtoms[this_atom].AtomicName, "dref", 4) == 0) {
4572 parsedAtoms[this_atom].AtomicLength = 16;
4573 } else if (memcmp(parsedAtoms[this_atom].AtomicName, "iinf", 4) == 0) {
4574 parsedAtoms[this_atom].AtomicLength = 14;
4575 }
4576 }
4577
4578 if (deep_atom_scan && parsedAtoms[this_atom].AtomicContainerState == DUAL_STATE_ATOM) {
4579 uint32_t atom_val = UInt32FromBigEndian(parsedAtoms[this_atom].AtomicName);
4580 if (atom_val == 0x73747364 ) { //stsd
4581 parsedAtoms[this_atom].AtomicLength = 16;
4582 } else if (atom_val == 0x6D703473 ) { //mp4s
4583 parsedAtoms[this_atom].AtomicLength = 16;
4584
4585 } else if (atom_val == 0x73727470 ) { //srtp
4586 parsedAtoms[this_atom].AtomicLength = 24;
4587 } else if (atom_val == 0x72747020 && parsedAtoms[this_atom].AtomicLevel == 7) { //'rtp '
4588 parsedAtoms[this_atom].AtomicLength = 24;
4589
4590 } else if (atom_val == 0x616C6163 && parsedAtoms[this_atom].AtomicLevel == 7) { //alac
4591 parsedAtoms[this_atom].AtomicLength = 36;
4592 } else if (atom_val == 0x6D703461 ) { //mp4a
4593 parsedAtoms[this_atom].AtomicLength = 36;
4594 } else if (atom_val == 0x73616D72 ) { //samr
4595 parsedAtoms[this_atom].AtomicLength = 36;
4596 } else if (atom_val == 0x73617762 ) { //sawb
4597 parsedAtoms[this_atom].AtomicLength = 36;
4598 } else if (atom_val == 0x73617770 ) { //sawp
4599 parsedAtoms[this_atom].AtomicLength = 36;
4600 } else if (atom_val == 0x73657663 ) { //sevc
4601 parsedAtoms[this_atom].AtomicLength = 36;
4602 } else if (atom_val == 0x73716370 ) { //sqcp
4603 parsedAtoms[this_atom].AtomicLength = 36;
4604 } else if (atom_val == 0x73736D76 ) { //ssmv
4605 parsedAtoms[this_atom].AtomicLength = 36;
4606
4607 } else if (atom_val == 0x74783367 ) { //tx3g
4608 parsedAtoms[this_atom].AtomicLength = 46;
4609
4610 } else if (atom_val == 0x6D6A7032 ) { //mjp2
4611 parsedAtoms[this_atom].AtomicLength = 86;
4612 } else if (atom_val == 0x6D703476 ) { //mp4v
4613 parsedAtoms[this_atom].AtomicLength = 86;
4614 } else if (atom_val == 0x61766331 ) { //avc1
4615 parsedAtoms[this_atom].AtomicLength = 86;
4616 } else if (atom_val == 0x6A706567 ) { //jpeg
4617 parsedAtoms[this_atom].AtomicLength = 86;
4618 } else if (atom_val == 0x73323633 ) { //s263
4619 parsedAtoms[this_atom].AtomicLength = 86;
4620 }
4621 }
4622
4623 if (from_file) {
4624 // here we read in the original atom into the buffer. If the length is
4625 // greater than our buffer length, we loop, reading in chunks of the
4626 // atom's data into the buffer, and immediately write it out, reusing
4627 // the buffer.
4628 //
4629 bytes_written += block_copy(source_file, temp_file, buffer,
4630 bytes_written_tally,
4631 parsedAtoms[this_atom].AtomicLength - bytes_written,
4632 bytes_written + parsedAtoms[this_atom].AtomicStart,
4633 bytes_written_tally + bytes_written);
4634
4635 return bytes_written;
4636
4637 } else { // we are going to be writing not from the file, but directly from the tree (in memory).
4638 uint64_t atom_name_len = 4;
4639
4640 //fprintf(stdout, "Writing atom %s from memory %u\n", parsedAtoms[this_atom].AtomicName, parsedAtoms[this_atom].AtomicClassification);
4641 fseeko(temp_file, bytes_written_tally + bytes_written, SEEK_SET);
4642
4643 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) {
4644 fwrite("uuid", 4, 1, temp_file);
4645 atom_name_len = 16; //total of 20 bytes for a uuid atom
4646 //fprintf(stdout, "%" PRIu64 "\n", parsedAtoms[this_atom].AtomicLength);
4647 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[this_atom].uuid_style == UUID_OTHER) bytes_written += 4;
4648 }
4649
4650 fwrite(parsedAtoms[this_atom].AtomicName, atom_name_len, 1, temp_file);
4651 bytes_written += atom_name_len;
4652 if (parsedAtoms[this_atom].AtomicClassification == VERSIONED_ATOM || parsedAtoms[this_atom].AtomicClassification == PACKED_LANG_ATOM) {
4653 UInt32_TO_String4(parsedAtoms[this_atom].AtomicVerFlags, twenty_byte_buffer);
4654 fwrite(twenty_byte_buffer, 4, 1, temp_file);
4655 bytes_written += 4;
4656 }
4657
4658 uint64_t atom_data_size = 0;
4659 switch (parsedAtoms[this_atom].AtomicContainerState) {
4660 case PARENT_ATOM :
4661 case SIMPLE_PARENT_ATOM : {
4662 atom_data_size = 0;
4663 break;
4664 }
4665 case DUAL_STATE_ATOM : {
4666 switch ( UInt32FromBigEndian(parsedAtoms[this_atom].AtomicName) ) {
4667 case 0x6D657461 : { //meta
4668 break;
4669 }
4670 case 0x73747364 : { //stsd
4671 atom_data_size = parsedAtoms[this_atom].AtomicLength - 12;
4672 break;
4673 }
4674 case 0x73636869 : { //schi (code only executes when deep_atom_scan = true; otherwise schi is contained by the monolithic/unparsed 'stsd')
4675 atom_data_size = parsedAtoms[this_atom].AtomicLength - 12;
4676 }
4677 }
4678 break;
4679 }
4680 case UNKNOWN_ATOM_TYPE :
4681 case CHILD_ATOM : {
4682 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[this_atom].uuid_style == UUID_AP_SHA1_NAMESPACE) {
4683 //4bytes length, 4 bytes 'uuid', 4bytes name, 4bytes NULL (AP writes its own uuid atoms - not those copied - iTunes style with atom versioning)
4684 atom_data_size = parsedAtoms[this_atom].AtomicLength - (16 + 12); //16 uuid; 16 = 4bytes * ('uuid', ap_uuid_name, verflag, 4 NULL bytes)
4685 } else if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[this_atom].uuid_style != UUID_DEPRECATED_FORM) {
4686 atom_data_size = parsedAtoms[this_atom].AtomicLength - (16 + 8);
4687 } else if (parsedAtoms[this_atom].AtomicClassification == VERSIONED_ATOM || parsedAtoms[this_atom].AtomicClassification == PACKED_LANG_ATOM) {
4688 //4bytes legnth, 4bytes name, 4bytes flag&versioning (language would be 2 bytes, but because its in different places, it gets stored as data)
4689 atom_data_size = parsedAtoms[this_atom].AtomicLength - 12;
4690 } else {
4691 //just 4bytes length, 4bytes name and then whatever data
4692 atom_data_size = parsedAtoms[this_atom].AtomicLength - 8;
4693 }
4694 break;
4695 }
4696 }
4697
4698 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM && parsedAtoms[this_atom].uuid_style == UUID_AP_SHA1_NAMESPACE) {
4699 //AP writes uuid atoms much like iTunes style metadata; with version/flags to connote what type of data is being carried
4700 //4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes name of uuid in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data
4701 fwrite(parsedAtoms[this_atom].uuid_ap_atomname, 4, 1, temp_file);
4702 bytes_written += 4;
4703
4704 UInt32_TO_String4(parsedAtoms[this_atom].AtomicVerFlags, twenty_byte_buffer);
4705 fwrite(twenty_byte_buffer, 4, 1, temp_file);
4706 bytes_written += 4;
4707 }
4708
4709 if (atom_data_size > 0) {
4710 fwrite(parsedAtoms[this_atom].AtomicData, atom_data_size, 1, temp_file);
4711 bytes_written += atom_data_size;
4712
4713 APar_ShellProgressBar(bytes_written_tally + bytes_written);
4714 }
4715 }
4716 return bytes_written;
5858 fseeko(source_file, src_offset, SEEK_SET);
5859 fseeko(out_file, dest_offset, SEEK_SET);
5860
5861 while (toread) {
5862 char *bpos;
5863
5864 didread = fread(buffer, 1, std::min(max_buffer, toread), source_file);
5865 if (didread == 0) {
5866 fprintf(stderr,
5867 "read: eof=%d err=%d %s\n",
5868 feof(source_file),
5869 ferror(source_file),
5870 strerror(errno));
5871 break;
5872 }
5873 toread -= didread;
5874
5875 bpos = buffer;
5876
5877 while (didread) {
5878 didwrite = fwrite(bpos, 1, didread, out_file);
5879 didread -= didwrite;
5880 bpos += didwrite;
5881 bytes_written += didwrite;
5882
5883 APar_ShellProgressBar(tally + bytes_written);
5884 }
5885 }
5886 return bytes_written;
5887 }
5888
5889 uint64_t APar_WriteAtomically(FILE *source_file,
5890 FILE *temp_file,
5891 bool from_file,
5892 char *&buffer,
5893 uint64_t bytes_written_tally,
5894 short this_atom) {
5895 uint64_t bytes_written = 0;
5896
5897 if (parsedAtoms[this_atom].AtomicLength > 1 &&
5898 parsedAtoms[this_atom].AtomicLength <
5899 8) { // prevents any spurious atoms from appearing
5900 return bytes_written;
5901 }
5902
5903 // write the length of the atom first... taken from our tree in memory
5904 UInt32_TO_String4(parsedAtoms[this_atom].AtomicLength, twenty_byte_buffer);
5905 fseeko(temp_file, bytes_written_tally, SEEK_SET);
5906 fwrite(twenty_byte_buffer, 4, 1, temp_file);
5907 bytes_written += 4;
5908
5909 // since we have already writen the length out to the file, it can be changed
5910 // now with impunity
5911 if (parsedAtoms[this_atom].AtomicLength ==
5912 0) { // the spec says if an atom has a length of 0, it extends to EOF
5913 parsedAtoms[this_atom].AtomicLength =
5914 file_size - parsedAtoms[this_atom].AtomicLength;
5915 } else if (parsedAtoms[this_atom].AtomicLength == 1) {
5916 // part of the pseudo 64-bit support
5917 parsedAtoms[this_atom].AtomicLength =
5918 parsedAtoms[this_atom].AtomicLengthExtended;
5919 } else if (parsedAtoms[this_atom].AtomicContainerState == DUAL_STATE_ATOM) {
5920 if (memcmp(parsedAtoms[this_atom].AtomicName, "dref", 4) == 0) {
5921 parsedAtoms[this_atom].AtomicLength = 16;
5922 } else if (memcmp(parsedAtoms[this_atom].AtomicName, "iinf", 4) == 0) {
5923 parsedAtoms[this_atom].AtomicLength = 14;
5924 }
5925 }
5926
5927 if (deep_atom_scan &&
5928 parsedAtoms[this_atom].AtomicContainerState == DUAL_STATE_ATOM) {
5929 uint32_t atom_val = UInt32FromBigEndian(parsedAtoms[this_atom].AtomicName);
5930 if (atom_val == 0x73747364) { // stsd
5931 parsedAtoms[this_atom].AtomicLength = 16;
5932 } else if (atom_val == 0x6D703473) { // mp4s
5933 parsedAtoms[this_atom].AtomicLength = 16;
5934
5935 } else if (atom_val == 0x73727470) { // srtp
5936 parsedAtoms[this_atom].AtomicLength = 24;
5937 } else if (atom_val == 0x72747020 &&
5938 parsedAtoms[this_atom].AtomicLevel == 7) { //'rtp '
5939 parsedAtoms[this_atom].AtomicLength = 24;
5940
5941 } else if (atom_val == 0x616C6163 &&
5942 parsedAtoms[this_atom].AtomicLevel == 7) { // alac
5943 parsedAtoms[this_atom].AtomicLength = 36;
5944 } else if (atom_val == 0x6D703461) { // mp4a
5945 parsedAtoms[this_atom].AtomicLength = 36;
5946 } else if (atom_val == 0x73616D72) { // samr
5947 parsedAtoms[this_atom].AtomicLength = 36;
5948 } else if (atom_val == 0x73617762) { // sawb
5949 parsedAtoms[this_atom].AtomicLength = 36;
5950 } else if (atom_val == 0x73617770) { // sawp
5951 parsedAtoms[this_atom].AtomicLength = 36;
5952 } else if (atom_val == 0x73657663) { // sevc
5953 parsedAtoms[this_atom].AtomicLength = 36;
5954 } else if (atom_val == 0x73716370) { // sqcp
5955 parsedAtoms[this_atom].AtomicLength = 36;
5956 } else if (atom_val == 0x73736D76) { // ssmv
5957 parsedAtoms[this_atom].AtomicLength = 36;
5958
5959 } else if (atom_val == 0x74783367) { // tx3g
5960 parsedAtoms[this_atom].AtomicLength = 46;
5961
5962 } else if (atom_val == 0x6D6A7032) { // mjp2
5963 parsedAtoms[this_atom].AtomicLength = 86;
5964 } else if (atom_val == 0x6D703476) { // mp4v
5965 parsedAtoms[this_atom].AtomicLength = 86;
5966 } else if (atom_val == 0x61766331) { // avc1
5967 parsedAtoms[this_atom].AtomicLength = 86;
5968 } else if (atom_val == 0x6A706567) { // jpeg
5969 parsedAtoms[this_atom].AtomicLength = 86;
5970 } else if (atom_val == 0x73323633) { // s263
5971 parsedAtoms[this_atom].AtomicLength = 86;
5972 }
5973 }
5974
5975 if (from_file) {
5976 // here we read in the original atom into the buffer. If the length is
5977 // greater than our buffer length, we loop, reading in chunks of the
5978 // atom's data into the buffer, and immediately write it out, reusing
5979 // the buffer.
5980 //
5981 bytes_written +=
5982 block_copy(source_file,
5983 temp_file,
5984 buffer,
5985 bytes_written_tally,
5986 parsedAtoms[this_atom].AtomicLength - bytes_written,
5987 bytes_written + parsedAtoms[this_atom].AtomicStart,
5988 bytes_written_tally + bytes_written);
5989
5990 return bytes_written;
5991
5992 } else { // we are going to be writing not from the file, but directly from
5993 // the tree (in memory).
5994 uint64_t atom_name_len = 4;
5995
5996 // fprintf(stdout, "Writing atom %s from memory %u\n",
5997 // parsedAtoms[this_atom].AtomicName,
5998 // parsedAtoms[this_atom].AtomicClassification);
5999 fseeko(temp_file, bytes_written_tally + bytes_written, SEEK_SET);
6000
6001 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM) {
6002 fwrite("uuid", 4, 1, temp_file);
6003 atom_name_len = 16; // total of 20 bytes for a uuid atom
6004 // fprintf(stdout, "%" PRIu64 "\n", parsedAtoms[this_atom].AtomicLength);
6005 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM &&
6006 parsedAtoms[this_atom].uuid_style == UUID_OTHER)
6007 bytes_written += 4;
6008 }
6009
6010 fwrite(parsedAtoms[this_atom].AtomicName, atom_name_len, 1, temp_file);
6011 bytes_written += atom_name_len;
6012 if (parsedAtoms[this_atom].AtomicClassification == VERSIONED_ATOM ||
6013 parsedAtoms[this_atom].AtomicClassification == PACKED_LANG_ATOM) {
6014 UInt32_TO_String4(parsedAtoms[this_atom].AtomicVerFlags,
6015 twenty_byte_buffer);
6016 fwrite(twenty_byte_buffer, 4, 1, temp_file);
6017 bytes_written += 4;
6018 }
6019
6020 uint64_t atom_data_size = 0;
6021 switch (parsedAtoms[this_atom].AtomicContainerState) {
6022 case PARENT_ATOM:
6023 case SIMPLE_PARENT_ATOM: {
6024 atom_data_size = 0;
6025 break;
6026 }
6027 case DUAL_STATE_ATOM: {
6028 switch (UInt32FromBigEndian(parsedAtoms[this_atom].AtomicName)) {
6029 case 0x6D657461: { // meta
6030 break;
6031 }
6032 case 0x73747364: { // stsd
6033 atom_data_size = parsedAtoms[this_atom].AtomicLength - 12;
6034 break;
6035 }
6036 case 0x73636869: { // schi (code only executes when deep_atom_scan = true;
6037 // otherwise schi is contained by the
6038 // monolithic/unparsed 'stsd')
6039 atom_data_size = parsedAtoms[this_atom].AtomicLength - 12;
6040 }
6041 }
6042 break;
6043 }
6044 case UNKNOWN_ATOM_TYPE:
6045 case CHILD_ATOM: {
6046 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM &&
6047 parsedAtoms[this_atom].uuid_style == UUID_AP_SHA1_NAMESPACE) {
6048 // 4bytes length, 4 bytes 'uuid', 4bytes name, 4bytes NULL (AP writes
6049 // its own uuid atoms - not those copied - iTunes style with atom
6050 // versioning)
6051 atom_data_size = parsedAtoms[this_atom].AtomicLength -
6052 (16 + 12); // 16 uuid; 16 = 4bytes * ('uuid',
6053 // ap_uuid_name, verflag, 4 NULL bytes)
6054 } else if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM &&
6055 parsedAtoms[this_atom].uuid_style != UUID_DEPRECATED_FORM) {
6056 atom_data_size = parsedAtoms[this_atom].AtomicLength - (16 + 8);
6057 } else if (parsedAtoms[this_atom].AtomicClassification ==
6058 VERSIONED_ATOM ||
6059 parsedAtoms[this_atom].AtomicClassification ==
6060 PACKED_LANG_ATOM) {
6061 // 4bytes legnth, 4bytes name, 4bytes flag&versioning (language would be
6062 // 2 bytes, but because its in different places, it gets stored as data)
6063 atom_data_size = parsedAtoms[this_atom].AtomicLength - 12;
6064 } else {
6065 // just 4bytes length, 4bytes name and then whatever data
6066 atom_data_size = parsedAtoms[this_atom].AtomicLength - 8;
6067 }
6068 break;
6069 }
6070 }
6071
6072 if (parsedAtoms[this_atom].AtomicClassification == EXTENDED_ATOM &&
6073 parsedAtoms[this_atom].uuid_style == UUID_AP_SHA1_NAMESPACE) {
6074 // AP writes uuid atoms much like iTunes style metadata; with
6075 // version/flags to connote what type of data is being carried
6076 // 4bytes atom length, 4 bytes 'uuid', 16bytes uuidv5, 4bytes name of uuid
6077 // in AP namespace, 4bytes versioning, 4bytes NULL, Xbytes data
6078 fwrite(parsedAtoms[this_atom].uuid_ap_atomname, 4, 1, temp_file);
6079 bytes_written += 4;
6080
6081 UInt32_TO_String4(parsedAtoms[this_atom].AtomicVerFlags,
6082 twenty_byte_buffer);
6083 fwrite(twenty_byte_buffer, 4, 1, temp_file);
6084 bytes_written += 4;
6085 }
6086
6087 if (atom_data_size > 0) {
6088 fwrite(parsedAtoms[this_atom].AtomicData, atom_data_size, 1, temp_file);
6089 bytes_written += atom_data_size;
6090
6091 APar_ShellProgressBar(bytes_written_tally + bytes_written);
6092 }
6093 }
6094 return bytes_written;
47176095 }
47186096
47196097 /*----------------------
47206098 APar_copy_gapless_padding
4721 mp4file - destination file
4722 last_atom_pos - the last byte in the destination file that is contained by any atom (in parsedAtoms[] array)
4723 buffer - a buffer that will be used to set & write out from the NULLs used in gapless padding
4724
4725 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
4726 file.
4727
4728 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
4729 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
4730 directed not to (via AP_PADDING). Although tying ordinary padding to this gapless padding may reduce flexibility - the assumption is that someone interested in
4731 squeezing out wasted space would want to eliminate this wasted space too (and so far, it does seem wasted).
4732
4733 NOTE: Apple seems not to have seen this portion of the ISO 14496-12 Annex A, section A.2, para 6:
4734 "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."
4735 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
4736 structure.
6099 mp4file - destination file
6100 last_atom_pos - the last byte in the destination file that is contained
6101 by any atom (in parsedAtoms[] array) buffer - a buffer that will be used to set
6102 & write out from the NULLs used in gapless padding
6103
6104 Add the discovered amount of already present gapless void padding at the end
6105 of the file (which is *not* contained by any atom at all) back into the
6106 destination file.
6107
6108 Update: it would seem that this gapless void padding at the end
6109 of the file is not critical to gapless playback. In my 1 test of the thing, it
6110 seemed to work regardless of whether this NULL space was present or not, 'pgap'
6111 seemed to work. But, since Apple put it in for some reason, it will be left
6112 there unless explicity directed not to (via AP_PADDING). Although tying ordinary
6113 padding to this gapless padding may reduce flexibility - the assumption is that
6114 someone interested in squeezing out wasted space would want to eliminate this
6115 wasted space too (and so far, it does seem wasted).
6116
6117 NOTE: Apple seems not to have seen this portion of the ISO
6118 14496-12 Annex A, section A.2, para 6: "All the data within a conforming file is
6119 encapsulated in boxes (called atoms in predecessors of this file format). There
6120 is no data outside the box structure." And yet, Apple (donators of the file
6121 format) has caused iTunes to create non-conforming files with iTunes 7.x because
6122 of this NULL data outside of any box/atom structure.
47376123
47386124 ----------------------*/
4739 void APar_copy_gapless_padding(FILE* mp4file, uint64_t last_atom_pos, char* buffer) {
4740 uint64_t gapless_padding_bytes_written = 0;
4741 while (gapless_padding_bytes_written < gapless_void_padding) {
4742 if (gapless_padding_bytes_written + max_buffer <= gapless_void_padding ) {
4743 memset(buffer, 0, max_buffer);
4744
4745 fseeko(mp4file, last_atom_pos + gapless_padding_bytes_written,
4746 SEEK_SET);
4747 fwrite(buffer, max_buffer, 1, mp4file);
4748 gapless_padding_bytes_written += max_buffer;
4749
4750 } else { //less then 512k of gapless padding (here's hoping we get here always)
4751 memset(buffer, 0, gapless_void_padding - gapless_padding_bytes_written);
4752
4753 fseeko(mp4file, last_atom_pos + gapless_padding_bytes_written, SEEK_SET);
4754 fwrite(buffer, gapless_void_padding - gapless_padding_bytes_written, 1, mp4file);
4755 gapless_padding_bytes_written += gapless_void_padding - gapless_padding_bytes_written;
4756 break;
4757 }
4758 }
4759 }
4760
4761 void APar_WriteFile(const char* ISObasemediafile, const char* outfile, bool rewrite_original) {
4762 char* temp_file_name=(char*)calloc(1, sizeof(char)* 3500 );
4763 char* file_buffer=(char*)calloc(1, sizeof(char)* max_buffer + 1 );
4764 FILE* temp_file;
4765 uint64_t temp_file_bytes_written = 0;
4766 short thisAtomNumber = 0;
4767 char* originating_file = NULL;
4768 bool free_modified_name = false;
4769
4770 APar_RenderAllID32Atoms();
4771
4772 if (!(psp_brand || force_existing_hierarchy)) {
4773 APar_Optimize(false);
4774 } else {
4775 APar_LocateAtomLandmarks();
4776 }
4777
4778 APar_FindPadding(false);
4779 APar_ConsolidatePadding();
4780 APar_DetermineAtomLengths();
4781
4782 if (!complete_free_space_erasure) {
4783 APar_DetermineDynamicUpdate();
4784 }
4785
4786 if (!rewrite_original || dynUpd.prevent_dynamic_update) {
4787 dynUpd.updage_by_padding = false;
4788 }
4789
4790 APar_ValidateAtoms();
4791
4792 //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
4793 uint64_t mdat_position = APar_DetermineMediaData_AtomPosition();
4794
4795 if (dynUpd.updage_by_padding) {
4796 APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-data-", NULL); //APar_DeriveNewPath(ISObasemediafile, temp_file_name, -1, "-data-", NULL);
4797 temp_file = APar_OpenFile(temp_file_name, "wb");
4798 #if defined (_WIN32) && !defined (__CYGWIN__)
4799 char* invisi_command=(char*)malloc(sizeof(char)*2*MAXPATHLEN);
4800 sprintf (invisi_command,"ATTRIB +S +H \"%s\"",temp_file_name);
4801
4802 if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
4803 wchar_t* invisi_command_long = Convert_multibyteUTF8_to_wchar(invisi_command);
4804
4805 _wsystem(invisi_command_long);
4806
4807 free(invisi_command_long);
4808 invisi_command_long = NULL;
4809 } else {
4810 system(invisi_command);
4811 }
4812 free(invisi_command);
4813 invisi_command = NULL;
6125 void APar_copy_gapless_padding(FILE *mp4file,
6126 uint64_t last_atom_pos,
6127 char *buffer) {
6128 uint64_t gapless_padding_bytes_written = 0;
6129 while (gapless_padding_bytes_written < gapless_void_padding) {
6130 if (gapless_padding_bytes_written + max_buffer <= gapless_void_padding) {
6131 memset(buffer, 0, max_buffer);
6132
6133 fseeko(mp4file, last_atom_pos + gapless_padding_bytes_written, SEEK_SET);
6134 fwrite(buffer, max_buffer, 1, mp4file);
6135 gapless_padding_bytes_written += max_buffer;
6136
6137 } else { // less then 512k of gapless padding (here's hoping we get here
6138 // always)
6139 memset(buffer, 0, gapless_void_padding - gapless_padding_bytes_written);
6140
6141 fseeko(mp4file, last_atom_pos + gapless_padding_bytes_written, SEEK_SET);
6142 fwrite(buffer,
6143 gapless_void_padding - gapless_padding_bytes_written,
6144 1,
6145 mp4file);
6146 gapless_padding_bytes_written +=
6147 gapless_void_padding - gapless_padding_bytes_written;
6148 break;
6149 }
6150 }
6151 }
6152
6153 void APar_WriteFile(const char *ISObasemediafile,
6154 const char *outfile,
6155 bool rewrite_original) {
6156 char *temp_file_name = (char *)calloc(1, sizeof(char) * 3500);
6157 char *file_buffer = (char *)calloc(1, sizeof(char) * max_buffer + 1);
6158 FILE *temp_file;
6159 uint64_t temp_file_bytes_written = 0;
6160 short thisAtomNumber = 0;
6161 char *originating_file = NULL;
6162 bool free_modified_name = false;
6163
6164 APar_RenderAllID32Atoms();
6165
6166 if (!(psp_brand || force_existing_hierarchy)) {
6167 APar_Optimize(false);
6168 } else {
6169 APar_LocateAtomLandmarks();
6170 }
6171
6172 APar_FindPadding(false);
6173 APar_ConsolidatePadding();
6174 APar_DetermineAtomLengths();
6175
6176 if (!complete_free_space_erasure) {
6177 APar_DetermineDynamicUpdate();
6178 }
6179
6180 if (!rewrite_original || dynUpd.prevent_dynamic_update) {
6181 dynUpd.updage_by_padding = false;
6182 }
6183
6184 APar_ValidateAtoms();
6185
6186 // whatever atoms/space comes before mdat has to be added/removed before this
6187 // point, or chunk offsets (in stco, co64, tfhd) won't be properly determined
6188 uint64_t mdat_position = APar_DetermineMediaData_AtomPosition();
6189
6190 if (dynUpd.updage_by_padding) {
6191 APar_DeriveNewPath(ISObasemediafile,
6192 temp_file_name,
6193 0,
6194 "-data-",
6195 NULL); // APar_DeriveNewPath(ISObasemediafile,
6196 // temp_file_name, -1, "-data-", NULL);
6197 temp_file = APar_OpenFile(temp_file_name, "wb");
6198 #if defined(_WIN32) && !defined(__CYGWIN__)
6199 char *invisi_command = (char *)malloc(sizeof(char) * 2 * MAXPATHLEN);
6200 sprintf(invisi_command, "ATTRIB +S +H \"%s\"", temp_file_name);
6201
6202 if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
6203 wchar_t *invisi_command_long =
6204 Convert_multibyteUTF8_to_wchar(invisi_command);
6205
6206 _wsystem(invisi_command_long);
6207
6208 free(invisi_command_long);
6209 invisi_command_long = NULL;
6210 } else {
6211 system(invisi_command);
6212 }
6213 free(invisi_command);
6214 invisi_command = NULL;
48146215 #endif
48156216
4816 } else if (!outfile) {
4817 APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-temp-", NULL);
4818 temp_file = APar_OpenFile(temp_file_name, "wb");
4819
4820 #if defined (DARWIN_PLATFORM)
4821 APar_SupplySelectiveTypeCreatorCodes(ISObasemediafile, temp_file_name, forced_suffix_type); //provide type/creator codes for ".mp4" for randomly named temp files
6217 } else if (!outfile) {
6218 APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-temp-", NULL);
6219 temp_file = APar_OpenFile(temp_file_name, "wb");
6220
6221 #if defined(__APPLE__)
6222 APar_SupplySelectiveTypeCreatorCodes(
6223 ISObasemediafile,
6224 temp_file_name,
6225 forced_suffix_type); // provide type/creator codes for ".mp4" for
6226 // randomly named temp files
48226227 #endif
48236228
4824 } else {
4825 //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 &
4826 //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.
4827 if (strcmp(ISObasemediafile,outfile) == 0) {
4828 //er, nice try but you were trying to ouput to the exactly named file of the original. Y'all ain't so slick
4829 APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-temp-", NULL);
4830 temp_file = APar_OpenFile(temp_file_name, "wb");
4831
4832 #if defined (DARWIN_PLATFORM)
4833 APar_SupplySelectiveTypeCreatorCodes(ISObasemediafile, temp_file_name, forced_suffix_type); //provide type/creator codes for ".mp4" for a fall-through randomly named temp files
6229 } else {
6230 // case-sensitive compare means "The.m4a" is different from "THe.m4a"; on
6231 // certiain Mac OS X filesystems a case-preservative but case-insensitive FS
6232 // exists & AP probably will have a problem there. Output to a uniquely
6233 // named file as I'm not going to poll the OS for the type of FS employed on
6234 // the target drive.
6235 if (strcmp(ISObasemediafile, outfile) == 0) {
6236 // er, nice try but you were trying to ouput to the exactly named file of
6237 // the original. Y'all ain't so slick
6238 APar_DeriveNewPath(ISObasemediafile, temp_file_name, 0, "-temp-", NULL);
6239 temp_file = APar_OpenFile(temp_file_name, "wb");
6240
6241 #if defined(__APPLE__)
6242 APar_SupplySelectiveTypeCreatorCodes(
6243 ISObasemediafile,
6244 temp_file_name,
6245 forced_suffix_type); // provide type/creator codes for ".mp4" for a
6246 // fall-through randomly named temp files
48346247 #endif
48356248
4836 } else {
4837 temp_file = APar_OpenFile(outfile, "wb");
4838
4839 #if defined (DARWIN_PLATFORM)
4840 APar_SupplySelectiveTypeCreatorCodes(ISObasemediafile, outfile, forced_suffix_type); //provide type/creator codes for ".mp4" for a user-defined output file
6249 } else {
6250 temp_file = APar_OpenFile(outfile, "wb");
6251
6252 #if defined(__APPLE__)
6253 APar_SupplySelectiveTypeCreatorCodes(
6254 ISObasemediafile,
6255 outfile,
6256 forced_suffix_type); // provide type/creator codes for ".mp4" for a
6257 // user-defined output file
48416258 #endif
4842
4843 }
4844 }
4845
4846 // turn off buffering on the output, since we have a big buffer ourselves
4847 if (temp_file) {
4848 setbuf(temp_file, NULL);
4849 }
4850 setbuf(source_file, NULL);
4851
4852 if (temp_file != NULL) { //body of atom writing here
4853
4854 if (dynUpd.updage_by_padding) {
4855 thisAtomNumber = dynUpd.initial_update_atom->AtomicNumber;
4856 fprintf(stdout, "\n Updating metadata... ");
4857 } else {
4858 fprintf(stdout, "\n Started writing to %s.\n",
4859 outfile ? outfile : "temp file");
4860 }
4861
4862 while (true) {
4863
4864 AtomicInfo* thisAtom = &parsedAtoms[thisAtomNumber];
4865 if (thisAtom->AtomicNumber == -1) break;
4866
4867 //the loop where the critical determination is made
4868 if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 && dynUpd.updage_by_padding) break;
4869
4870 if (thisAtom->ancillary_data == 0x666C6167) { //'flag'
4871 APar_UpdateModTime(thisAtom);
4872 }
4873
4874 if (memcmp(thisAtom->AtomicName, "stco", 4) == 0) {
4875 bool readjusted_stco = APar_Readjust_STCO_atom(mdat_position, thisAtomNumber);
4876
4877 temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, !readjusted_stco, file_buffer, temp_file_bytes_written, thisAtomNumber);
4878
4879 } else if (memcmp(thisAtom->AtomicName, "co64", 4) == 0) {
4880 bool readjusted_co64 = APar_Readjust_CO64_atom(mdat_position, thisAtomNumber);
4881
4882 temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, !readjusted_co64, file_buffer, temp_file_bytes_written, thisAtomNumber);
4883
4884 } else if (memcmp(thisAtom->AtomicName, "tfhd", 4) == 0) {
4885 bool readjusted_tfhd = APar_Readjust_TFHD_fragment_atom(mdat_position, thisAtomNumber);
4886
4887 temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, !readjusted_tfhd, file_buffer, temp_file_bytes_written, thisAtomNumber);
4888
4889 } else if (memcmp(thisAtom->AtomicName, "iloc", 4) == 0) {
4890 bool readjusted_iloc = APar_Readjust_iloc_atom(thisAtomNumber);
4891
4892 temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, !readjusted_iloc, file_buffer, temp_file_bytes_written, thisAtomNumber);
4893
4894 } else if (thisAtom->AtomicData != NULL || memcmp(thisAtom->AtomicName, "meta", 4) == 0) {
4895 temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, false, file_buffer, temp_file_bytes_written, thisAtomNumber);
4896
4897 } else {
4898 //write out parent atoms (the standard kind that are only offset & name from the tree in memory (total: 8bytes)
4899 if ( thisAtom->AtomicContainerState <= SIMPLE_PARENT_ATOM ) {
4900 temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, false, file_buffer, temp_file_bytes_written, thisAtomNumber);
4901 //or its a child (they invariably contain some sort of data.
4902 } else {
4903 temp_file_bytes_written += APar_WriteAtomically(source_file, temp_file, true, file_buffer, temp_file_bytes_written, thisAtomNumber);
4904 }
4905 }
4906 if (thisAtom->NextAtomNumber == 0) { //if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) {
4907 //fprintf(stdout, "The loop is buh-rokin\n");
4908 break;
4909 }
4910
4911 //prevent any looping back to atoms already written
4912 thisAtom->AtomicNumber = -1;
4913 thisAtomNumber = thisAtom->NextAtomNumber;
4914 }
4915 if (!dynUpd.updage_by_padding) {
4916 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
4917 APar_copy_gapless_padding(temp_file, temp_file_bytes_written, file_buffer);
4918 }
4919 fprintf(stdout, "\n Finished writing to %s.\n",
4920 outfile ? outfile : "temp file");
4921 fclose(temp_file);
4922 }
4923
4924 } else {
4925 fprintf(stdout, "AtomicParsley error: an error occurred while trying to create a temp file.\n");
4926 exit(1);
4927 }
4928
4929 if (dynUpd.updage_by_padding && rewrite_original) {
4930 fclose(temp_file);
4931 uint64_t metadata_len = findFileSize(temp_file_name);
4932
4933 temp_file = APar_OpenFile(temp_file_name, "rb");
4934 fclose(source_file);
4935 source_file = APar_OpenFile(ISObasemediafile, "r+b");
4936 if (source_file == NULL) {
4937 fclose(temp_file);
4938 remove(temp_file_name);
4939 fprintf(stdout, "AtomicParsley error: the original file was no longer found.\nExiting.\n");
4940 exit(1);
4941 } else if (!(dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) && metadata_len != (dynUpd.first_mdat_atom->AtomicStart - dynUpd.initial_update_atom->AtomicStart)) {
4942 fclose(temp_file);
4943 remove(temp_file_name);
4944 fprintf(stdout,
4945 "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);
4946 exit(1);
4947 }
4948
4949 APar_MergeTempFile(source_file, temp_file, temp_file_bytes_written, dynUpd.initial_update_atom->AtomicStart, file_buffer);
4950
4951 fclose(source_file);
4952 fclose(temp_file);
4953 remove(temp_file_name);
4954
4955 } 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
4956 fclose(source_file);
4957
4958 #if defined (_WIN32) && !defined (__CYGWIN__) /* native Windows requires removing the file first; rename() on POSIX does the removing automatically as needed */
4959 if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
4960 wchar_t* utf16_filepath = Convert_multibyteUTF8_to_wchar(ISObasemediafile);
4961
4962 _wremove(utf16_filepath);
4963
4964 free(utf16_filepath);
4965 utf16_filepath = NULL;
4966 } else {
4967 remove(ISObasemediafile);
4968 }
6259 }
6260 }
6261
6262 if (temp_file != NULL) { // body of atom writing here
6263
6264 if (dynUpd.updage_by_padding) {
6265 thisAtomNumber = dynUpd.initial_update_atom->AtomicNumber;
6266 fprintf(stdout, "\n Updating metadata... ");
6267 } else {
6268 fprintf(stdout,
6269 "\n Started writing to %s.\n",
6270 outfile ? outfile : "temp file");
6271 }
6272
6273 while (true) {
6274
6275 AtomicInfo *thisAtom = &parsedAtoms[thisAtomNumber];
6276 if (thisAtom->AtomicNumber == -1)
6277 break;
6278
6279 // the loop where the critical determination is made
6280 if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 &&
6281 dynUpd.updage_by_padding)
6282 break;
6283
6284 if (thisAtom->ancillary_data == 0x666C6167) { //'flag'
6285 APar_UpdateModTime(thisAtom);
6286 }
6287
6288 if (memcmp(thisAtom->AtomicName, "stco", 4) == 0) {
6289 bool readjusted_stco =
6290 APar_Readjust_STCO_atom(mdat_position, thisAtomNumber);
6291
6292 temp_file_bytes_written += APar_WriteAtomically(source_file,
6293 temp_file,
6294 !readjusted_stco,
6295 file_buffer,
6296 temp_file_bytes_written,
6297 thisAtomNumber);
6298
6299 } else if (memcmp(thisAtom->AtomicName, "co64", 4) == 0) {
6300 bool readjusted_co64 =
6301 APar_Readjust_CO64_atom(mdat_position, thisAtomNumber);
6302
6303 temp_file_bytes_written += APar_WriteAtomically(source_file,
6304 temp_file,
6305 !readjusted_co64,
6306 file_buffer,
6307 temp_file_bytes_written,
6308 thisAtomNumber);
6309
6310 } else if (memcmp(thisAtom->AtomicName, "tfhd", 4) == 0) {
6311 bool readjusted_tfhd =
6312 APar_Readjust_TFHD_fragment_atom(mdat_position, thisAtomNumber);
6313
6314 temp_file_bytes_written += APar_WriteAtomically(source_file,
6315 temp_file,
6316 !readjusted_tfhd,
6317 file_buffer,
6318 temp_file_bytes_written,
6319 thisAtomNumber);
6320
6321 } else if (memcmp(thisAtom->AtomicName, "iloc", 4) == 0) {
6322 bool readjusted_iloc = APar_Readjust_iloc_atom(thisAtomNumber);
6323
6324 temp_file_bytes_written += APar_WriteAtomically(source_file,
6325 temp_file,
6326 !readjusted_iloc,
6327 file_buffer,
6328 temp_file_bytes_written,
6329 thisAtomNumber);
6330
6331 } else if (thisAtom->AtomicData != NULL ||
6332 memcmp(thisAtom->AtomicName, "meta", 4) == 0) {
6333 temp_file_bytes_written += APar_WriteAtomically(source_file,
6334 temp_file,
6335 false,
6336 file_buffer,
6337 temp_file_bytes_written,
6338 thisAtomNumber);
6339
6340 } else {
6341 // write out parent atoms (the standard kind that are only offset & name
6342 // from the tree in memory (total: 8bytes)
6343 if (thisAtom->AtomicContainerState <= SIMPLE_PARENT_ATOM) {
6344 temp_file_bytes_written +=
6345 APar_WriteAtomically(source_file,
6346 temp_file,
6347 false,
6348 file_buffer,
6349 temp_file_bytes_written,
6350 thisAtomNumber);
6351 // or its a child (they invariably contain some sort of data.
6352 } else {
6353 temp_file_bytes_written +=
6354 APar_WriteAtomically(source_file,
6355 temp_file,
6356 true,
6357 file_buffer,
6358 temp_file_bytes_written,
6359 thisAtomNumber);
6360 }
6361 }
6362 if (thisAtom->NextAtomNumber ==
6363 0) { // if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) {
6364 // fprintf(stdout, "The loop is buh-rokin\n");
6365 break;
6366 }
6367
6368 // prevent any looping back to atoms already written
6369 thisAtom->AtomicNumber = -1;
6370 thisAtomNumber = thisAtom->NextAtomNumber;
6371 }
6372 if (!dynUpd.updage_by_padding) {
6373 if (gapless_void_padding > 0 &&
6374 pad_prefs.default_padding_size >
6375 0) { // only when some sort of padding is wanted will the gapless
6376 // null padding be copied
6377 APar_copy_gapless_padding(
6378 temp_file, temp_file_bytes_written, file_buffer);
6379 }
6380 fprintf(stdout,
6381 "\n Finished writing to %s.\n",
6382 outfile ? outfile : "temp file");
6383 fclose(temp_file);
6384 }
6385
6386 } else {
6387 fprintf(stdout,
6388 "AtomicParsley error: an error occurred while trying to "
6389 "create a temp file.\n");
6390 exit(1);
6391 }
6392
6393 if (dynUpd.updage_by_padding && rewrite_original) {
6394 fclose(temp_file);
6395 uint64_t metadata_len = findFileSize(temp_file_name);
6396
6397 temp_file = APar_OpenFile(temp_file_name, "rb");
6398 fclose(source_file);
6399 source_file = APar_OpenFile(ISObasemediafile, "r+b");
6400 if (source_file == NULL) {
6401 fclose(temp_file);
6402 remove(temp_file_name);
6403 fprintf(stdout,
6404 "AtomicParsley error: the original file was no longer "
6405 "found.\nExiting.\n");
6406 exit(1);
6407 } else if (!(dynUpd.optimization_flags & MEDIADATA__PRECEDES__MOOV) &&
6408 metadata_len != (dynUpd.first_mdat_atom->AtomicStart -
6409 dynUpd.initial_update_atom->AtomicStart)) {
6410 fclose(temp_file);
6411 remove(temp_file_name);
6412 fprintf(stdout,
6413 "AtomicParsley error: the insufficient space to retag the source "
6414 "file (%" PRIu64 "!=%" PRIu64 ").\nExiting.\n",
6415 metadata_len,
6416 dynUpd.first_mdat_atom->AtomicStart -
6417 dynUpd.initial_update_atom->AtomicStart);
6418 exit(1);
6419 }
6420
6421 APar_MergeTempFile(source_file,
6422 temp_file,
6423 temp_file_bytes_written,
6424 dynUpd.initial_update_atom->AtomicStart,
6425 file_buffer);
6426
6427 fclose(source_file);
6428 fclose(temp_file);
6429 remove(temp_file_name);
6430
6431 } else if (rewrite_original &&
6432 !outfile) { // disable overWrite when writing out to a specifically
6433 // named file; presumably the enumerated output file
6434 // was meant to be the final destination
6435 fclose(source_file);
6436
6437 #if defined(_WIN32) && \
6438 !defined(__CYGWIN__) /* native Windows requires removing the file first; \
6439 rename() on POSIX does the removing automatically \
6440 as needed */
6441 if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
6442 wchar_t *utf16_filepath =
6443 Convert_multibyteUTF8_to_wchar(ISObasemediafile);
6444
6445 _wremove(utf16_filepath);
6446
6447 free(utf16_filepath);
6448 utf16_filepath = NULL;
6449 } else {
6450 remove(ISObasemediafile);
6451 }
49696452 #endif
49706453
4971 int err = 0;
4972
4973 if (forced_suffix_type != NO_TYPE_FORCING) {
4974 originating_file = (char*)calloc( 1, sizeof(char)* 3500 );
4975 free_modified_name = true;
4976 if (forced_suffix_type == FORCE_M4B_TYPE) { //using --stik Audiobook with --overWrite will change the original file's extension
4977 uint16_t filename_len = strlen(ISObasemediafile);
4978 const char* suffix = strrchr(ISObasemediafile, '.');
4979 memcpy(originating_file, ISObasemediafile, filename_len+1 );
4980 memcpy(originating_file + (filename_len - strlen(suffix) ), ".m4b", 5 );
4981 }
4982 } else {
4983 originating_file = (char*)ISObasemediafile;
4984 }
4985
4986 #if defined (_WIN32) && !defined (__CYGWIN__)
4987 if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
4988 wchar_t* utf16_filepath = Convert_multibyteUTF8_to_wchar(originating_file);
4989 wchar_t* temp_utf16_filepath = Convert_multibyteUTF8_to_wchar(temp_file_name);
4990
4991 err = _wrename(temp_utf16_filepath, utf16_filepath);
4992
4993 free(utf16_filepath);
4994 free(temp_utf16_filepath);
4995 utf16_filepath = NULL;
4996 temp_utf16_filepath = NULL;
4997 } else
6454 int err = 0;
6455
6456 if (forced_suffix_type != NO_TYPE_FORCING) {
6457 originating_file = (char *)calloc(1, sizeof(char) * 3500);
6458 free_modified_name = true;
6459 if (forced_suffix_type ==
6460 FORCE_M4B_TYPE) { // using --stik Audiobook with --overWrite will
6461 // change the original file's extension
6462 uint16_t filename_len = strlen(ISObasemediafile);
6463 const char *suffix = strrchr(ISObasemediafile, '.');
6464 memcpy(originating_file, ISObasemediafile, filename_len + 1);
6465 memcpy(originating_file + (filename_len - strlen(suffix)), ".m4b", 5);
6466 }
6467 } else {
6468 originating_file = (char *)ISObasemediafile;
6469 }
6470
6471 #if defined(_WIN32) && !defined(__CYGWIN__)
6472 if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
6473 wchar_t *utf16_filepath =
6474 Convert_multibyteUTF8_to_wchar(originating_file);
6475 wchar_t *temp_utf16_filepath =
6476 Convert_multibyteUTF8_to_wchar(temp_file_name);
6477
6478 err = _wrename(temp_utf16_filepath, utf16_filepath);
6479
6480 free(utf16_filepath);
6481 free(temp_utf16_filepath);
6482 utf16_filepath = NULL;
6483 temp_utf16_filepath = NULL;
6484 } else
49986485 #endif
4999 {
5000 err = rename(temp_file_name, originating_file);
5001 }
5002
5003 if (err != 0) {
5004 switch (errno) {
5005
5006 case ENAMETOOLONG: {
5007 fprintf (stdout, "Some or all of the orginal path was too long.");
5008 exit (-1);
5009 }
5010 case ENOENT: {
5011 fprintf (stdout, "Some part of the original path was missing.");
5012 exit (-1);
5013 }
5014 case EACCES: {
5015 fprintf (stdout, "Unable to write to a directory lacking write permission.");
5016 exit (-1);
5017 }
5018 case ENOSPC: {
5019 fprintf (stdout, "Out of space.");
5020 exit (-1);
5021 }
5022 }
5023 }
5024 }
5025
5026 free(temp_file_name);
5027 if (free_modified_name) free(originating_file);
5028 temp_file_name=NULL;
5029 free(file_buffer);
5030 file_buffer = NULL;
5031
5032 return;
6486 {
6487 err = rename(temp_file_name, originating_file);
6488 }
6489
6490 if (err != 0) {
6491 switch (errno) {
6492
6493 case ENAMETOOLONG: {
6494 fprintf(stdout, "Some or all of the orginal path was too long.");
6495 exit(-1);
6496 }
6497 case ENOENT: {
6498 fprintf(stdout, "Some part of the original path was missing.");
6499 exit(-1);
6500 }
6501 case EACCES: {
6502 fprintf(stdout,
6503 "Unable to write to a directory lacking write permission.");
6504 exit(-1);
6505 }
6506 case ENOSPC: {
6507 fprintf(stdout, "Out of space.");
6508 exit(-1);
6509 }
6510 }
6511 }
6512 }
6513
6514 free(temp_file_name);
6515 if (free_modified_name)
6516 free(originating_file);
6517 temp_file_name = NULL;
6518 free(file_buffer);
6519 file_buffer = NULL;
6520
6521 return;
50336522 }
50346523
50356524 // vim:ts=2:sw=2:noet:
2222 */
2323
2424 /*
25 This file has been modified from the original found in http://www.gnu.org/software/coreutils/ coreutils-5.97 for use within AtomicParsley.
26 Modifications are : endian detection change
27 a cast for compiling under g++
28 file renaming
29 eliminated SWAP in favor of swap32 & swap16 in util.h
30 alignment macros (for msvc)
25 This file has been modified from the original found in
26 http://www.gnu.org/software/coreutils/ coreutils-5.97 for use within
27 AtomicParsley. Modifications are : endian detection change a cast for
28 compiling under g++ file renaming eliminated SWAP in favor of swap32 & swap16
29 in util.h alignment macros (for msvc)
3130 */
3231
3332 #include "AtomicParsley.h"
4544
4645 #define BLOCKSIZE 4096
4746 #if BLOCKSIZE % 64 != 0
48 # error "invalid BLOCKSIZE"
47 #error "invalid BLOCKSIZE"
4948 #endif
5049
5150 /* This array contains the bytes used to pad the buffer to the next
5251 64-byte boundary. (RFC 1321, 3.1: Step 1) */
53 static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
54
52 static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */};
5553
5654 /*
5755 Takes a pointer to a 160 bit block of data (five 32 bit ints) and
5856 intializes it to the start constants of the SHA1 algorithm. This
5957 must be called before using hash in the call to sha1_hash.
6058 */
61 void
62 sha1_init_ctx (struct sha1_ctx *ctx)
63 {
59 void sha1_init_ctx(struct sha1_ctx *ctx) {
6460 ctx->A = 0x67452301;
6561 ctx->B = 0xefcdab89;
6662 ctx->C = 0x98badcfe;
7672
7773 IMPORTANT: On some systems it is required that RESBUF is correctly
7874 aligned for a 32 bits value. */
79 void *
80 sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf)
81 {
82 ((md5_uint32 *) resbuf)[0] = SWAP32 (ctx->A);
83 ((md5_uint32 *) resbuf)[1] = SWAP32 (ctx->B);
84 ((md5_uint32 *) resbuf)[2] = SWAP32 (ctx->C);
85 ((md5_uint32 *) resbuf)[3] = SWAP32 (ctx->D);
86 ((md5_uint32 *) resbuf)[4] = SWAP32 (ctx->E);
75 void *sha1_read_ctx(const struct sha1_ctx *ctx, void *resbuf) {
76 ((md5_uint32 *)resbuf)[0] = SWAP32(ctx->A);
77 ((md5_uint32 *)resbuf)[1] = SWAP32(ctx->B);
78 ((md5_uint32 *)resbuf)[2] = SWAP32(ctx->C);
79 ((md5_uint32 *)resbuf)[3] = SWAP32(ctx->D);
80 ((md5_uint32 *)resbuf)[4] = SWAP32(ctx->E);
8781
8882 return resbuf;
8983 }
9387
9488 IMPORTANT: On some systems it is required that RESBUF is correctly
9589 aligned for a 32 bits value. */
96 void *
97 sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf)
98 {
90 void *sha1_finish_ctx(struct sha1_ctx *ctx, void *resbuf) {
9991 /* Take yet unprocessed bytes into account. */
10092 md5_uint32 bytes = ctx->buflen;
10193 size_t pad;
10698 ++ctx->total[1];
10799
108100 pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
109 memcpy (&ctx->buffer[bytes], fillbuf, pad);
101 memcpy(&ctx->buffer[bytes], fillbuf, pad);
110102
111103 /* Put the 64-bit file length in *bits* at the end of the buffer. */
112 *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP32 (ctx->total[0] << 3);
113 *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP32 ((ctx->total[1] << 3) |
114 (ctx->total[0] >> 29));
104 *(md5_uint32 *)&ctx->buffer[bytes + pad + 4] = SWAP32(ctx->total[0] << 3);
105 *(md5_uint32 *)&ctx->buffer[bytes + pad] =
106 SWAP32((ctx->total[1] << 3) | (ctx->total[0] >> 29));
115107
116108 /* Process last bytes. */
117 sha1_process_block (ctx->buffer, bytes + pad + 8, ctx);
118
119 return sha1_read_ctx (ctx, resbuf);
109 sha1_process_block(ctx->buffer, bytes + pad + 8, ctx);
110
111 return sha1_read_ctx(ctx, resbuf);
120112 }
121113
122114 /* Compute SHA1 message digest for bytes read from STREAM. The
123115 resulting message digest number will be written into the 16 bytes
124116 beginning at RESBLOCK. */
125 int
126 sha1_stream (FILE *stream, void *resblock)
127 {
117 int sha1_stream(FILE *stream, void *resblock) {
128118 struct sha1_ctx ctx;
129119 char buffer[BLOCKSIZE + 72];
130120 size_t sum;
131121
132122 /* Initialize the computation context. */
133 sha1_init_ctx (&ctx);
123 sha1_init_ctx(&ctx);
134124
135125 /* Iterate over full file contents. */
136 while (1)
137 {
138 /* We read the file in blocks of BLOCKSIZE bytes. One call of the
139 computation function processes the whole buffer so that with the
140 next round of the loop another block can be read. */
141 size_t n;
142 sum = 0;
143
144 /* Read block. Take care for partial reads. */
145 while (1)
146 {
147 n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
148
149 sum += n;
150
151 if (sum == BLOCKSIZE)
152 break;
153
154 if (n == 0)
155 {
156 /* Check for the error flag IFF N == 0, so that we don't
157 exit the loop after a partial read due to e.g., EAGAIN
158 or EWOULDBLOCK. */
159 if (ferror (stream))
160 return 1;
161 goto process_partial_block;
162 }
163
164 /* We've read at least one byte, so ignore errors. But always
165 check for EOF, since feof may be true even though N > 0.
166 Otherwise, we could end up calling fread after EOF. */
167 if (feof (stream))
168 goto process_partial_block;
169 }
170
171 /* Process buffer with BLOCKSIZE bytes. Note that
172 BLOCKSIZE % 64 == 0
173 */
174 sha1_process_block (buffer, BLOCKSIZE, &ctx);
126 while (1) {
127 /* We read the file in blocks of BLOCKSIZE bytes. One call of the
128 computation function processes the whole buffer so that with the
129 next round of the loop another block can be read. */
130 size_t n;
131 sum = 0;
132
133 /* Read block. Take care for partial reads. */
134 while (1) {
135 n = fread(buffer + sum, 1, BLOCKSIZE - sum, stream);
136
137 sum += n;
138
139 if (sum == BLOCKSIZE)
140 break;
141
142 if (n == 0) {
143 /* Check for the error flag IFF N == 0, so that we don't
144 exit the loop after a partial read due to e.g., EAGAIN
145 or EWOULDBLOCK. */
146 if (ferror(stream))
147 return 1;
148 goto process_partial_block;
149 }
150
151 /* We've read at least one byte, so ignore errors. But always
152 check for EOF, since feof may be true even though N > 0.
153 Otherwise, we could end up calling fread after EOF. */
154 if (feof(stream))
155 goto process_partial_block;
175156 }
176157
177 process_partial_block:;
158 /* Process buffer with BLOCKSIZE bytes. Note that
159 BLOCKSIZE % 64 == 0
160 */
161 sha1_process_block(buffer, BLOCKSIZE, &ctx);
162 }
163
164 process_partial_block:;
178165
179166 /* Process any remaining bytes. */
180167 if (sum > 0)
181 sha1_process_bytes (buffer, sum, &ctx);
168 sha1_process_bytes(buffer, sum, &ctx);
182169
183170 /* Construct result in desired memory. */
184 sha1_finish_ctx (&ctx, resblock);
171 sha1_finish_ctx(&ctx, resblock);
185172 return 0;
186173 }
187174
189176 result is always in little endian byte order, so that a byte-wise
190177 output yields to the wanted ASCII representation of the message
191178 digest. */
192 void *
193 sha1_buffer (const char *buffer, size_t len, void *resblock)
194 {
179 void *sha1_buffer(const char *buffer, size_t len, void *resblock) {
195180 struct sha1_ctx ctx;
196181
197182 /* Initialize the computation context. */
198 sha1_init_ctx (&ctx);
183 sha1_init_ctx(&ctx);
199184
200185 /* Process whole buffer but last len % 64 bytes. */
201 sha1_process_bytes (buffer, len, &ctx);
186 sha1_process_bytes(buffer, len, &ctx);
202187
203188 /* Put result in desired memory area. */
204 return sha1_finish_ctx (&ctx, resblock);
205 }
206
207 void
208 sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx)
209 {
189 return sha1_finish_ctx(&ctx, resblock);
190 }
191
192 void sha1_process_bytes(const void *buffer, size_t len, struct sha1_ctx *ctx) {
210193 /* When we already have some bits in our internal buffer concatenate
211194 both inputs first. */
212 if (ctx->buflen != 0)
195 if (ctx->buflen != 0) {
196 size_t left_over = ctx->buflen;
197 size_t add = 128 - left_over > len ? len : 128 - left_over;
198
199 memcpy(&ctx->buffer[left_over], buffer, add);
200 ctx->buflen += add;
201
202 if (ctx->buflen > 64) {
203 sha1_process_block(ctx->buffer, ctx->buflen & ~63, ctx);
204
205 ctx->buflen &= 63;
206 /* The regions in the following copy operation cannot overlap. */
207 memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63], ctx->buflen);
208 }
209
210 buffer = (const char *)buffer + add;
211 len -= add;
212 }
213
214 /* Process available complete blocks. */
215 if (len >= 64) {
216 #if !_STRING_ARCH_unaligned
217 #define alignof(type) \
218 offsetof( \
219 struct { \
220 char c; \
221 type x; \
222 }, \
223 x)
224 #define UNALIGNED_P(p) \
225 (((size_t)p) % 4 != \
226 0) //# define UNALIGNED_P(p) (((size_t) p) % alignof (md5_uint32) != 0)
227 if (UNALIGNED_P(buffer))
228 while (len > 64) {
229 sha1_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx);
230 buffer = (const char *)buffer + 64;
231 len -= 64;
232 }
233 else
234 #endif
213235 {
214 size_t left_over = ctx->buflen;
215 size_t add = 128 - left_over > len ? len : 128 - left_over;
216
217 memcpy (&ctx->buffer[left_over], buffer, add);
218 ctx->buflen += add;
219
220 if (ctx->buflen > 64)
221 {
222 sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
223
224 ctx->buflen &= 63;
225 /* The regions in the following copy operation cannot overlap. */
226 memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
227 ctx->buflen);
228 }
229
230 buffer = (const char *) buffer + add;
231 len -= add;
236 sha1_process_block(buffer, len & ~63, ctx);
237 buffer = (const char *)buffer + (len & ~63);
238 len &= 63;
232239 }
233
234 /* Process available complete blocks. */
235 if (len >= 64)
236 {
237 #if !_STRING_ARCH_unaligned
238 # define alignof(type) offsetof (struct { char c; type x; }, x)
239 # define UNALIGNED_P(p) (((size_t) p) % 4 != 0) //# define UNALIGNED_P(p) (((size_t) p) % alignof (md5_uint32) != 0)
240 if (UNALIGNED_P (buffer))
241 while (len > 64)
242 {
243 sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
244 buffer = (const char *) buffer + 64;
245 len -= 64;
246 }
247 else
248 #endif
249 {
250 sha1_process_block (buffer, len & ~63, ctx);
251 buffer = (const char *) buffer + (len & ~63);
252 len &= 63;
253 }
240 }
241
242 /* Move remaining bytes in internal buffer. */
243 if (len > 0) {
244 size_t left_over = ctx->buflen;
245
246 memcpy(&ctx->buffer[left_over], buffer, len);
247 left_over += len;
248 if (left_over >= 64) {
249 sha1_process_block(ctx->buffer, 64, ctx);
250 left_over -= 64;
251 memcpy(ctx->buffer, &ctx->buffer[64], left_over);
254252 }
255
256 /* Move remaining bytes in internal buffer. */
257 if (len > 0)
258 {
259 size_t left_over = ctx->buflen;
260
261 memcpy (&ctx->buffer[left_over], buffer, len);
262 left_over += len;
263 if (left_over >= 64)
264 {
265 sha1_process_block (ctx->buffer, 64, ctx);
266 left_over -= 64;
267 memcpy (ctx->buffer, &ctx->buffer[64], left_over);
268 }
269 ctx->buflen = left_over;
270 }
253 ctx->buflen = left_over;
254 }
271255 }
272256
273257 /* --- Code below is the primary difference between md5.c and sha1.c --- */
279263 #define K4 0xca62c1d6L
280264
281265 /* Round functions. Note that F2 is the same as F4. */
282 #define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) )
283 #define F2(B,C,D) (B ^ C ^ D)
284 #define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) )
285 #define F4(B,C,D) (B ^ C ^ D)
266 #define F1(B, C, D) (D ^ (B & (C ^ D)))
267 #define F2(B, C, D) (B ^ C ^ D)
268 #define F3(B, C, D) ((B & C) | (D & (B | C)))
269 #define F4(B, C, D) (B ^ C ^ D)
286270
287271 /* Process LEN bytes of BUFFER, accumulating context into CTX.
288272 It is assumed that LEN % 64 == 0.
289273 Most of this code comes from GnuPG's cipher/sha1.c. */
290274
291 void
292 sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx)
293 {
294 const md5_uint32 *words = (md5_uint32*)buffer;
295 size_t nwords = len / sizeof (md5_uint32);
275 void sha1_process_block(const void *buffer, size_t len, struct sha1_ctx *ctx) {
276 const md5_uint32 *words = (md5_uint32 *)buffer;
277 size_t nwords = len / sizeof(md5_uint32);
296278 const md5_uint32 *endp = words + nwords;
297279 md5_uint32 x[16];
298280 md5_uint32 a = ctx->A;
310292
311293 #define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
312294
313 #define M(I) ( tm = x[I&0x0f] ^ x[(I-14)&0x0f] \
314 ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \
315 , (x[I&0x0f] = rol(tm, 1)) )
316
317 #define R(A,B,C,D,E,F,K,M) do { E += rol( A, 5 ) \
318 + F( B, C, D ) \
319 + K \
320 + M; \
321 B = rol( B, 30 ); \
322 } while(0)
323
324 while (words < endp)
325 {
326 md5_uint32 tm;
327 int t;
328 for (t = 0; t < 16; t++)
329 {
330 x[t] = SWAP32 (*words);
331 words++;
332 }
333
334 R( a, b, c, d, e, F1, K1, x[ 0] );
335 R( e, a, b, c, d, F1, K1, x[ 1] );
336 R( d, e, a, b, c, F1, K1, x[ 2] );
337 R( c, d, e, a, b, F1, K1, x[ 3] );
338 R( b, c, d, e, a, F1, K1, x[ 4] );
339 R( a, b, c, d, e, F1, K1, x[ 5] );
340 R( e, a, b, c, d, F1, K1, x[ 6] );
341 R( d, e, a, b, c, F1, K1, x[ 7] );
342 R( c, d, e, a, b, F1, K1, x[ 8] );
343 R( b, c, d, e, a, F1, K1, x[ 9] );
344 R( a, b, c, d, e, F1, K1, x[10] );
345 R( e, a, b, c, d, F1, K1, x[11] );
346 R( d, e, a, b, c, F1, K1, x[12] );
347 R( c, d, e, a, b, F1, K1, x[13] );
348 R( b, c, d, e, a, F1, K1, x[14] );
349 R( a, b, c, d, e, F1, K1, x[15] );
350 R( e, a, b, c, d, F1, K1, M(16) );
351 R( d, e, a, b, c, F1, K1, M(17) );
352 R( c, d, e, a, b, F1, K1, M(18) );
353 R( b, c, d, e, a, F1, K1, M(19) );
354 R( a, b, c, d, e, F2, K2, M(20) );
355 R( e, a, b, c, d, F2, K2, M(21) );
356 R( d, e, a, b, c, F2, K2, M(22) );
357 R( c, d, e, a, b, F2, K2, M(23) );
358 R( b, c, d, e, a, F2, K2, M(24) );
359 R( a, b, c, d, e, F2, K2, M(25) );
360 R( e, a, b, c, d, F2, K2, M(26) );
361 R( d, e, a, b, c, F2, K2, M(27) );
362 R( c, d, e, a, b, F2, K2, M(28) );
363 R( b, c, d, e, a, F2, K2, M(29) );
364 R( a, b, c, d, e, F2, K2, M(30) );
365 R( e, a, b, c, d, F2, K2, M(31) );
366 R( d, e, a, b, c, F2, K2, M(32) );
367 R( c, d, e, a, b, F2, K2, M(33) );
368 R( b, c, d, e, a, F2, K2, M(34) );
369 R( a, b, c, d, e, F2, K2, M(35) );
370 R( e, a, b, c, d, F2, K2, M(36) );
371 R( d, e, a, b, c, F2, K2, M(37) );
372 R( c, d, e, a, b, F2, K2, M(38) );
373 R( b, c, d, e, a, F2, K2, M(39) );
374 R( a, b, c, d, e, F3, K3, M(40) );
375 R( e, a, b, c, d, F3, K3, M(41) );
376 R( d, e, a, b, c, F3, K3, M(42) );
377 R( c, d, e, a, b, F3, K3, M(43) );
378 R( b, c, d, e, a, F3, K3, M(44) );
379 R( a, b, c, d, e, F3, K3, M(45) );
380 R( e, a, b, c, d, F3, K3, M(46) );
381 R( d, e, a, b, c, F3, K3, M(47) );
382 R( c, d, e, a, b, F3, K3, M(48) );
383 R( b, c, d, e, a, F3, K3, M(49) );
384 R( a, b, c, d, e, F3, K3, M(50) );
385 R( e, a, b, c, d, F3, K3, M(51) );
386 R( d, e, a, b, c, F3, K3, M(52) );
387 R( c, d, e, a, b, F3, K3, M(53) );
388 R( b, c, d, e, a, F3, K3, M(54) );
389 R( a, b, c, d, e, F3, K3, M(55) );
390 R( e, a, b, c, d, F3, K3, M(56) );
391 R( d, e, a, b, c, F3, K3, M(57) );
392 R( c, d, e, a, b, F3, K3, M(58) );
393 R( b, c, d, e, a, F3, K3, M(59) );
394 R( a, b, c, d, e, F4, K4, M(60) );
395 R( e, a, b, c, d, F4, K4, M(61) );
396 R( d, e, a, b, c, F4, K4, M(62) );
397 R( c, d, e, a, b, F4, K4, M(63) );
398 R( b, c, d, e, a, F4, K4, M(64) );
399 R( a, b, c, d, e, F4, K4, M(65) );
400 R( e, a, b, c, d, F4, K4, M(66) );
401 R( d, e, a, b, c, F4, K4, M(67) );
402 R( c, d, e, a, b, F4, K4, M(68) );
403 R( b, c, d, e, a, F4, K4, M(69) );
404 R( a, b, c, d, e, F4, K4, M(70) );
405 R( e, a, b, c, d, F4, K4, M(71) );
406 R( d, e, a, b, c, F4, K4, M(72) );
407 R( c, d, e, a, b, F4, K4, M(73) );
408 R( b, c, d, e, a, F4, K4, M(74) );
409 R( a, b, c, d, e, F4, K4, M(75) );
410 R( e, a, b, c, d, F4, K4, M(76) );
411 R( d, e, a, b, c, F4, K4, M(77) );
412 R( c, d, e, a, b, F4, K4, M(78) );
413 R( b, c, d, e, a, F4, K4, M(79) );
414
415 a = ctx->A += a;
416 b = ctx->B += b;
417 c = ctx->C += c;
418 d = ctx->D += d;
419 e = ctx->E += e;
295 #define M(I) \
296 (tm = x[I & 0x0f] ^ x[(I - 14) & 0x0f] ^ x[(I - 8) & 0x0f] ^ \
297 x[(I - 3) & 0x0f], \
298 (x[I & 0x0f] = rol(tm, 1)))
299
300 #define R(A, B, C, D, E, F, K, M) \
301 do { \
302 E += rol(A, 5) + F(B, C, D) + K + M; \
303 B = rol(B, 30); \
304 } while (0)
305
306 while (words < endp) {
307 md5_uint32 tm;
308 int t;
309 for (t = 0; t < 16; t++) {
310 x[t] = SWAP32(*words);
311 words++;
420312 }
421 }
422
313
314 R(a, b, c, d, e, F1, K1, x[0]);
315 R(e, a, b, c, d, F1, K1, x[1]);
316 R(d, e, a, b, c, F1, K1, x[2]);
317 R(c, d, e, a, b, F1, K1, x[3]);
318 R(b, c, d, e, a, F1, K1, x[4]);
319 R(a, b, c, d, e, F1, K1, x[5]);
320 R(e, a, b, c, d, F1, K1, x[6]);
321 R(d, e, a, b, c, F1, K1, x[7]);
322 R(c, d, e, a, b, F1, K1, x[8]);
323 R(b, c, d, e, a, F1, K1, x[9]);
324 R(a, b, c, d, e, F1, K1, x[10]);
325 R(e, a, b, c, d, F1, K1, x[11]);
326 R(d, e, a, b, c, F1, K1, x[12]);
327 R(c, d, e, a, b, F1, K1, x[13]);
328 R(b, c, d, e, a, F1, K1, x[14]);
329 R(a, b, c, d, e, F1, K1, x[15]);
330 R(e, a, b, c, d, F1, K1, M(16));
331 R(d, e, a, b, c, F1, K1, M(17));
332 R(c, d, e, a, b, F1, K1, M(18));
333 R(b, c, d, e, a, F1, K1, M(19));
334 R(a, b, c, d, e, F2, K2, M(20));
335 R(e, a, b, c, d, F2, K2, M(21));
336 R(d, e, a, b, c, F2, K2, M(22));
337 R(c, d, e, a, b, F2, K2, M(23));
338 R(b, c, d, e, a, F2, K2, M(24));
339 R(a, b, c, d, e, F2, K2, M(25));
340 R(e, a, b, c, d, F2, K2, M(26));
341 R(d, e, a, b, c, F2, K2, M(27));
342 R(c, d, e, a, b, F2, K2, M(28));
343 R(b, c, d, e, a, F2, K2, M(29));
344 R(a, b, c, d, e, F2, K2, M(30));
345 R(e, a, b, c, d, F2, K2, M(31));
346 R(d, e, a, b, c, F2, K2, M(32));
347 R(c, d, e, a, b, F2, K2, M(33));
348 R(b, c, d, e, a, F2, K2, M(34));
349 R(a, b, c, d, e, F2, K2, M(35));
350 R(e, a, b, c, d, F2, K2, M(36));
351 R(d, e, a, b, c, F2, K2, M(37));
352 R(c, d, e, a, b, F2, K2, M(38));
353 R(b, c, d, e, a, F2, K2, M(39));
354 R(a, b, c, d, e, F3, K3, M(40));
355 R(e, a, b, c, d, F3, K3, M(41));
356 R(d, e, a, b, c, F3, K3, M(42));
357 R(c, d, e, a, b, F3, K3, M(43));
358 R(b, c, d, e, a, F3, K3, M(44));
359 R(a, b, c, d, e, F3, K3, M(45));
360 R(e, a, b, c, d, F3, K3, M(46));
361 R(d, e, a, b, c, F3, K3, M(47));
362 R(c, d, e, a, b, F3, K3, M(48));
363 R(b, c, d, e, a, F3, K3, M(49));
364 R(a, b, c, d, e, F3, K3, M(50));
365 R(e, a, b, c, d, F3, K3, M(51));
366 R(d, e, a, b, c, F3, K3, M(52));
367 R(c, d, e, a, b, F3, K3, M(53));
368 R(b, c, d, e, a, F3, K3, M(54));
369 R(a, b, c, d, e, F3, K3, M(55));
370 R(e, a, b, c, d, F3, K3, M(56));
371 R(d, e, a, b, c, F3, K3, M(57));
372 R(c, d, e, a, b, F3, K3, M(58));
373 R(b, c, d, e, a, F3, K3, M(59));
374 R(a, b, c, d, e, F4, K4, M(60));
375 R(e, a, b, c, d, F4, K4, M(61));
376 R(d, e, a, b, c, F4, K4, M(62));
377 R(c, d, e, a, b, F4, K4, M(63));
378 R(b, c, d, e, a, F4, K4, M(64));
379 R(a, b, c, d, e, F4, K4, M(65));
380 R(e, a, b, c, d, F4, K4, M(66));
381 R(d, e, a, b, c, F4, K4, M(67));
382 R(c, d, e, a, b, F4, K4, M(68));
383 R(b, c, d, e, a, F4, K4, M(69));
384 R(a, b, c, d, e, F4, K4, M(70));
385 R(e, a, b, c, d, F4, K4, M(71));
386 R(d, e, a, b, c, F4, K4, M(72));
387 R(c, d, e, a, b, F4, K4, M(73));
388 R(b, c, d, e, a, F4, K4, M(74));
389 R(a, b, c, d, e, F4, K4, M(75));
390 R(e, a, b, c, d, F4, K4, M(76));
391 R(d, e, a, b, c, F4, K4, M(77));
392 R(c, d, e, a, b, F4, K4, M(78));
393 R(b, c, d, e, a, F4, K4, M(79));
394
395 a = ctx->A += a;
396 b = ctx->B += b;
397 c = ctx->C += c;
398 d = ctx->D += d;
399 e = ctx->E += e;
400 }
401 }
11 /*
22 AtomicParsley - util.cpp
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2006-2007 puck_lock
17 Copyright (C) 2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
19
20 ----------------------
19
20 ----------------------
2121 Code Contributions by:
22
23 * SLarew - prevent writing past array in Convert_multibyteUTF16_to_wchar bugfix
24
25 */
22
23 * SLarew - prevent writing past array in Convert_multibyteUTF16_to_wchar
24 bugfix
25
26 */
2627 //==================================================================//
2728
2829 #include "AtomicParsley.h"
2930
30
31 ///////////////////////////////////////////////////////////////////////////////////////
32 // Filesytem routines //
31 ///////////////////////////////////////////////////////////////////////////////////////
32 // Filesytem routines //
3333 ///////////////////////////////////////////////////////////////////////////////////////
3434
3535 /*----------------------
3636 findFileSize
37 utf8_filepath - a pointer to a string (possibly utf8) of the full path to the file
38
39 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
40 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
41 (native-endian) filepath & pass that to a wide stat. Or stat it with a utf8 filepath on Unixen & win32 (stripped utf8).
37 utf8_filepath - a pointer to a string (possibly utf8) of the full path to the
38 file
39
40 take an ascii/utf8 filepath (which if under a unicode enabled Win32 OS was
41 already converted from utf16le to utf8 at program start) and test if AP is
42 running on a unicode enabled Win32 OS. If it is and converted to utf8 (rather
43 than just stripped), convert the utf8 filepath to a utf16 (native-endian)
44 filepath & pass that to a wide stat. Or stat it with a utf8 filepath on Unixen &
45 win32 (stripped utf8).
4246 ----------------------*/
4347 uint64_t findFileSize(const char *utf8_filepath) {
44 #if defined (_WIN32) && !defined (__CYGWIN__)
45 if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
46 wchar_t* utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
47
48 struct _stati64 fileStats;
49 _wstati64(utf16_filepath, &fileStats);
50
51 free(utf16_filepath);
52 utf16_filepath = NULL;
53 return fileStats.st_size;
54 } else
55 #endif
56 {
57 struct stat fileStats;
58 stat(utf8_filepath, &fileStats);
59 return fileStats.st_size;
60 }
61 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?)
48 #if defined(_WIN32) && !defined(__CYGWIN__)
49 if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
50 wchar_t *utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
51
52 struct _stati64 fileStats;
53 _wstati64(utf16_filepath, &fileStats);
54
55 free(utf16_filepath);
56 utf16_filepath = NULL;
57 return fileStats.st_size;
58 } else
59 #endif
60 {
61 struct stat fileStats;
62 stat(utf8_filepath, &fileStats);
63 return fileStats.st_size;
64 }
65 return 0; // won't ever get here.... unless this is win32, set to utf8 and the
66 // folder/file had unicode.... TODO (? use isUTF8() for high ascii?)
6267 }
6368
6469 /*----------------------
6570 APar_OpenFile
66 utf8_filepath - a pointer to a string (possibly utf8) of the full path to the file
67 file_flags - 3 bytes max for the flags to open the file with (read, write, binary mode....)
68
69 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
70 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
71 with the 8-bit file flags changed to 16-bit file flags. Or open a utf8 file with vanilla fopen on Unixen.
71 utf8_filepath - a pointer to a string (possibly utf8) of the full path to the
72 file file_flags - 3 bytes max for the flags to open the file with (read, write,
73 binary mode....)
74
75 take an ascii/utf8 filepath (which if under a unicode enabled Win32 OS was
76 already converted from utf16le to utf8 at program start) and test if AP is
77 running on a unicode enabled Win32 OS. If it is, convert the utf8 filepath to a
78 utf16 (native-endian) filepath & pass that to a wide fopen with the 8-bit file
79 flags changed to 16-bit file flags. Or open a utf8 file with vanilla fopen on
80 Unixen.
7281 ----------------------*/
73 FILE* APar_OpenFile(const char* utf8_filepath, const char* file_flags) {
74 FILE* aFile = NULL;
75 #if defined (_WIN32) && !defined (__CYGWIN__)
76 if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
77 wchar_t* Lfile_flags = (wchar_t *)malloc(sizeof(wchar_t)*4);
78 memset(Lfile_flags, 0, sizeof(wchar_t)*4);
79 mbstowcs(Lfile_flags, file_flags, strlen(file_flags) );
80
81 wchar_t* utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
82
83 aFile = _wfopen(utf16_filepath, Lfile_flags);
84
85 free(Lfile_flags); Lfile_flags=NULL;
86 free(utf16_filepath);
87 utf16_filepath = NULL;
88 } else
89 #endif
90 {
91 aFile = fopen(utf8_filepath, file_flags);
92 }
93
94 if (!aFile) {
95 fprintf(stdout, "AP error trying to fopen %s: %s\n", utf8_filepath, strerror(errno));
96 }
97 return aFile;
82 FILE *APar_OpenFile(const char *utf8_filepath, const char *file_flags) {
83 FILE *aFile = NULL;
84 #if defined(_WIN32) && !defined(__CYGWIN__)
85 if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
86 wchar_t *Lfile_flags = (wchar_t *)malloc(sizeof(wchar_t) * 4);
87 memset(Lfile_flags, 0, sizeof(wchar_t) * 4);
88 mbstowcs(Lfile_flags, file_flags, strlen(file_flags));
89
90 wchar_t *utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
91
92 aFile = _wfopen(utf16_filepath, Lfile_flags);
93
94 free(Lfile_flags);
95 Lfile_flags = NULL;
96 free(utf16_filepath);
97 utf16_filepath = NULL;
98 } else
99 #endif
100 {
101 aFile = fopen(utf8_filepath, file_flags);
102 }
103
104 if (!aFile) {
105 fprintf(stdout,
106 "AP error trying to fopen %s: %s\n",
107 utf8_filepath,
108 strerror(errno));
109 }
110 return aFile;
98111 }
99112
100113 /*----------------------
101114 openSomeFile
102 utf8_filepath - a pointer to a string (possibly utf8) of the full path to the file
103 open - flag to either open or close (function does both)
104
105 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
115 utf8_filepath - a pointer to a string (possibly utf8) of the full path to the
116 file open - flag to either open or close (function does both)
117
118 take an ascii/utf8 filepath and either open or close it; used for the main
119 ISO Base Media File; store the resulting FILE* in a global source_file
106120 ----------------------*/
107 FILE* APar_OpenISOBaseMediaFile(const char* utf8file, bool open) {
108 if ( open && !file_opened) {
109 source_file = APar_OpenFile(utf8file, "rb");
110 if (source_file != NULL) {
111 file_opened = true;
112 }
113 } else {
114 fclose(source_file);
115 file_opened = false;
116 }
117 return source_file;
121 FILE *APar_OpenISOBaseMediaFile(const char *utf8file, bool open) {
122 if (open && !file_opened) {
123 source_file = APar_OpenFile(utf8file, "rb");
124 if (source_file != nullptr) {
125 file_opened = true;
126 }
127 } else if (file_opened) {
128 fclose(source_file);
129 file_opened = false;
130 source_file = nullptr;
131 }
132 return source_file;
118133 }
119134
120135 void TestFileExistence(const char *filePath, bool errorOut) {
121 FILE *a_file = NULL;
122 a_file = APar_OpenFile(filePath, "rb");
123 if( (a_file == NULL) && errorOut ){
124 fprintf(stderr, "AtomicParsley error: can't open %s for reading: %s\n", filePath, strerror(errno));
125 exit(1);
126 } else {
127 if(a_file == NULL) {
128 fprintf(stderr, "AtomicParsley warning: can't open %s for reading but continuing anyway: %s\n", filePath, strerror(errno));
129 } else {
130 fclose(a_file);
131 }
132 }
133 }
134
135
136 #if defined (_WIN32)
137
138 ///////////////////////////////////////////////////////////////////////////////////////
139 // Win32 functions //
140 ///////////////////////////////////////////////////////////////////////////////////////
136 FILE *a_file = NULL;
137 a_file = APar_OpenFile(filePath, "rb");
138 if ((a_file == NULL) && errorOut) {
139 fprintf(stderr,
140 "AtomicParsley error: can't open %s for reading: %s\n",
141 filePath,
142 strerror(errno));
143 exit(1);
144 } else {
145 if (a_file == NULL) {
146 fprintf(stderr,
147 "AtomicParsley warning: can't open %s for reading but continuing "
148 "anyway: %s\n",
149 filePath,
150 strerror(errno));
151 } else {
152 fclose(a_file);
153 }
154 }
155 }
141156
142157 #ifndef HAVE_FSEEKO
143
144 int fseeko(FILE *stream, uint64_t pos, int whence) { //only using SEEK_SET here
145 if (whence == SEEK_SET) {
146 fpos_t fpos = pos;
147 return fsetpos(stream, &fpos);
148 } else {
149 return -1;
150 }
151 return -1;
152 }
153
154 #endif
158 #ifdef _WIN32
159 int fseeko(FILE *stream, off_t pos, int whence) {
160 return _fseeki64(stream, pos, whence);
161 }
162
163 off_t ftello(FILE *stream) { return _ftelli64(stream); }
164
165 #else
166 int fseeko(FILE *stream, off_t pos, int whence) {
167 return fseek(stream, pos, whence);
168 }
169
170 off_t ftello(FILE *stream) { return ftell(stream); }
171 #endif
172 #endif
173
174 #if defined(_WIN32)
175
176 ///////////////////////////////////////////////////////////////////////////////////////
177 // Win32 functions //
178 ///////////////////////////////////////////////////////////////////////////////////////
155179
156180 /*----------------------
157181 APar_OpenFileWin32
158 utf8_filepath - a pointer to a string (possibly utf8) of the full path to the file
159 ... - passed on to the CreateFile function
160
161 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
162 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
163 with the 8-bit file flags changed to 16-bit file flags, otherwise pass the utf8 filepath to an ANSI CreateFile
182 utf8_filepath - a pointer to a string (possibly utf8) of the full path
183 to the file
184 ... - passed on to the CreateFile function
185
186 take an ascii/utf8 filepath (which if under a unicode enabled Win32 OS
187 was already converted from utf16le to utf8 at program start) and test if AP is
188 running on a unicode enabled Win32 OS. If it is, convert the utf8 filepath to a
189 utf16 (native-endian) filepath & pass that to a wide CreateFile with the 8-bit
190 file flags changed to 16-bit file flags, otherwise pass the utf8 filepath to an
191 ANSI CreateFile
164192 ----------------------*/
165 HANDLE APar_OpenFileWin32(const char* utf8_filepath, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
166 if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
167 HANDLE hFile = NULL;
168 wchar_t* utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
169 hFile = CreateFileW(utf16_filepath, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
170 free(utf16_filepath);
171 return hFile;
172 } else {
173 return CreateFileA(utf8_filepath, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
174 }
175 }
176
177 #endif
178
193 HANDLE APar_OpenFileWin32(const char *utf8_filepath,
194 DWORD dwDesiredAccess,
195 DWORD dwShareMode,
196 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
197 DWORD dwCreationDisposition,
198 DWORD dwFlagsAndAttributes,
199 HANDLE hTemplateFile) {
200 if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
201 HANDLE hFile = NULL;
202 wchar_t *utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
203 hFile = CreateFileW(utf16_filepath,
204 dwDesiredAccess,
205 dwShareMode,
206 lpSecurityAttributes,
207 dwCreationDisposition,
208 dwFlagsAndAttributes,
209 hTemplateFile);
210 free(utf16_filepath);
211 return hFile;
212 } else {
213 return CreateFileA(utf8_filepath,
214 dwDesiredAccess,
215 dwShareMode,
216 lpSecurityAttributes,
217 dwCreationDisposition,
218 dwFlagsAndAttributes,
219 hTemplateFile);
220 }
221 }
222
223 #endif
179224
180225 // http://www.flipcode.com/articles/article_advstrings01.shtml
181226 bool IsUnicodeWinOS() {
182 #if defined (_WIN32)
183 OSVERSIONINFOW os;
227 #if defined(_WIN32)
228 OSVERSIONINFOW os;
184229 memset(&os, 0, sizeof(OSVERSIONINFOW));
185230 os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
186231 return (GetVersionExW(&os) != 0);
187232 #else
188 return false;
189 #endif
190 }
191
192 ///////////////////////////////////////////////////////////////////////////////////////
193 // File reading routines //
233 return false;
234 #endif
235 }
236
237 ///////////////////////////////////////////////////////////////////////////////////////
238 // File reading routines //
194239 ///////////////////////////////////////////////////////////////////////////////////////
195240
196241 const char *APar_strferror(FILE *f) {
197 if (feof(f) && ferror(f)) return "error and end of file";
198 else if (feof(f)) return "end of file";
199 else if (ferror(f)) return "error";
200 else return "neither error nor end of file";
201 }
202
203 uint8_t APar_read8(FILE* ISObasemediafile, uint64_t pos) {
204 uint8_t a_byte = 0;
205 size_t size;
206 fseeko(ISObasemediafile, pos, SEEK_SET);
207 size = fread(&a_byte, 1, 1, ISObasemediafile);
208 if(size != 1) {
209 printf("%s read failed, expect 1, got %u: %s\n", __FUNCTION__, (unsigned int)size, APar_strferror(ISObasemediafile));
210 exit(1);
211 }
212 return a_byte;
213 }
214
215 uint16_t APar_read16(char* buffer, FILE* ISObasemediafile, uint64_t pos) {
216 size_t size;
217 fseeko(ISObasemediafile, pos, SEEK_SET);
218 size = fread(buffer, 1, 2, ISObasemediafile);
219 if(size != 2) {
220 printf("%s read failed, expect 2, got %u: %s\n", __FUNCTION__, (unsigned int)size, APar_strferror(ISObasemediafile));
221 exit(1);
222 }
223 return UInt16FromBigEndian(buffer);
224 }
225
226 uint32_t APar_read32(char* buffer, FILE* ISObasemediafile, uint64_t pos) {
227 size_t size;
228 fseeko(ISObasemediafile, pos, SEEK_SET);
229 size = fread(buffer, 1, 4, ISObasemediafile);
230 if(size != 4) {
231 printf("%s read failed, expect 4, got %u: %s\n", __FUNCTION__, (unsigned int)size, APar_strferror(ISObasemediafile));
232 exit(1);
233 }
234 return UInt32FromBigEndian(buffer);
235 }
236
237 uint64_t APar_read64(char* buffer, FILE* ISObasemediafile, uint64_t pos) {
238 size_t size;
239 fseeko(ISObasemediafile, pos, SEEK_SET);
240 size = fread(buffer, 1, 8, ISObasemediafile);
241 if(size != 8) {
242 printf("%s read failed, expect 8, got %u: %s\n", __FUNCTION__, (unsigned int)size, APar_strferror(ISObasemediafile));
243 exit(1);
244 }
245 return UInt64FromBigEndian(buffer);
246 }
247
248 void APar_readX_noseek(char* buffer, FILE* ISObasemediafile, uint32_t length) {
249 size_t size;
250 size = fread(buffer, 1, length, ISObasemediafile);
251 if(size != length) {
252 printf("%s read failed, expect %" PRIu32 ", got %" PRIu32 ": %s\n", __FUNCTION__, length, (uint32_t)size, APar_strferror(ISObasemediafile));
253 exit(1);
254 }
255 return;
256 }
257
258 void APar_readX(char* buffer, FILE* ISObasemediafile, uint64_t pos, uint32_t length) {
259 size_t size;
260 fseeko(ISObasemediafile, pos, SEEK_SET);
261 size = fread(buffer, 1, length, ISObasemediafile);
262 if(size != length) {
263 printf("%s read failed, expect %" PRIu32 ", got %" PRIu32 ": %s\n", __FUNCTION__, length, (uint32_t)size, APar_strferror(ISObasemediafile));
264 exit(1);
265 }
266 return;
267 }
268
269 uint32_t APar_ReadFile(char* destination_buffer, FILE* a_file, uint32_t bytes_to_read) {
270 uint32_t bytes_read = 0;
271 if (destination_buffer != NULL) {
272 fseeko(a_file, 0, SEEK_SET); // not that 2gb support is required - malloc would probably have a few issues
273 bytes_read = fread(destination_buffer, 1, bytes_to_read, a_file);
274 file_size += bytes_read; //accommodate huge files embedded within small files for APar_Validate
275 }
276 return bytes_read;
277 }
278
279 uint32_t APar_FindValueInAtom(char* uint32_buffer, FILE* ISObasemediafile, short an_atom, uint64_t start_position, uint32_t eval_number)
280 {
281 uint64_t current_pos = start_position;
282 memset(uint32_buffer, 0, 5);
283 while (current_pos <= parsedAtoms[an_atom].AtomicLength) {
284 current_pos ++;
285 if (eval_number > 65535) {
286 //current_pos +=4;
287 if (APar_read32(uint32_buffer, ISObasemediafile, parsedAtoms[an_atom].AtomicStart + current_pos) == eval_number) {
288 break;
289 }
290 } else {
291 //current_pos +=2;
292 if (APar_read16(uint32_buffer, ISObasemediafile, parsedAtoms[an_atom].AtomicStart + current_pos) == (uint16_t)eval_number) {
293 break;
294 }
295 }
296 if (current_pos >= parsedAtoms[an_atom].AtomicLength) {
297 current_pos = 0;
298 break;
299 }
300 }
301 return (uint32_t) current_pos;
302 }
303
304 ///////////////////////////////////////////////////////////////////////////////////////
305 // Language specifics //
242 if (feof(f) && ferror(f))
243 return "error and end of file";
244 else if (feof(f))
245 return "end of file";
246 else if (ferror(f))
247 return "error";
248 else
249 return "neither error nor end of file";
250 }
251
252 uint8_t APar_read8(FILE *ISObasemediafile, uint64_t pos) {
253 uint8_t a_byte = 0;
254 size_t size;
255 fseeko(ISObasemediafile, pos, SEEK_SET);
256 size = fread(&a_byte, 1, 1, ISObasemediafile);
257 if (size != 1) {
258 printf("%s read failed, expect 1, got %u: %s\n",
259 __FUNCTION__,
260 (unsigned int)size,
261 APar_strferror(ISObasemediafile));
262 exit(1);
263 }
264 return a_byte;
265 }
266
267 uint16_t APar_read16(char *buffer, FILE *ISObasemediafile, uint64_t pos) {
268 size_t size;
269 fseeko(ISObasemediafile, pos, SEEK_SET);
270 size = fread(buffer, 1, 2, ISObasemediafile);
271 if (size != 2) {
272 printf("%s read failed, expect 2, got %u: %s\n",
273 __FUNCTION__,
274 (unsigned int)size,
275 APar_strferror(ISObasemediafile));
276 exit(1);
277 }
278 return UInt16FromBigEndian(buffer);
279 }
280
281 uint32_t APar_read32(char *buffer, FILE *ISObasemediafile, uint64_t pos) {
282 size_t size;
283 fseeko(ISObasemediafile, pos, SEEK_SET);
284 size = fread(buffer, 1, 4, ISObasemediafile);
285 if (size != 4) {
286 printf("%s read failed, expect 4, got %u: %s\n",
287 __FUNCTION__,
288 (unsigned int)size,
289 APar_strferror(ISObasemediafile));
290 exit(1);
291 }
292 return UInt32FromBigEndian(buffer);
293 }
294
295 uint64_t APar_read64(char *buffer, FILE *ISObasemediafile, uint64_t pos) {
296 size_t size;
297 fseeko(ISObasemediafile, pos, SEEK_SET);
298 size = fread(buffer, 1, 8, ISObasemediafile);
299 if (size != 8) {
300 printf("%s read failed, expect 8, got %u: %s\n",
301 __FUNCTION__,
302 (unsigned int)size,
303 APar_strferror(ISObasemediafile));
304 exit(1);
305 }
306 return UInt64FromBigEndian(buffer);
307 }
308
309 void APar_readX_noseek(char *buffer, FILE *ISObasemediafile, uint32_t length) {
310 size_t size;
311 size = fread(buffer, 1, length, ISObasemediafile);
312 if (size != length) {
313 printf("%s read failed, expect %" PRIu32 ", got %" PRIu32 ": %s\n",
314 __FUNCTION__,
315 length,
316 (uint32_t)size,
317 APar_strferror(ISObasemediafile));
318 exit(1);
319 }
320 return;
321 }
322
323 void APar_readX(char *buffer,
324 FILE *ISObasemediafile,
325 uint64_t pos,
326 uint32_t length) {
327 size_t size;
328 fseeko(ISObasemediafile, pos, SEEK_SET);
329 size = fread(buffer, 1, length, ISObasemediafile);
330 if (size != length) {
331 printf("%s read failed, expect %" PRIu32 ", got %" PRIu32 ": %s\n",
332 __FUNCTION__,
333 length,
334 (uint32_t)size,
335 APar_strferror(ISObasemediafile));
336 exit(1);
337 }
338 return;
339 }
340
341 uint32_t
342 APar_ReadFile(char *destination_buffer, FILE *a_file, uint32_t bytes_to_read) {
343 uint32_t bytes_read = 0;
344 if (destination_buffer != NULL) {
345 fseeko(a_file, 0, SEEK_SET); // not that 2gb support is required - malloc
346 // would probably have a few issues
347 bytes_read = fread(destination_buffer, 1, bytes_to_read, a_file);
348 file_size += bytes_read; // accommodate huge files embedded within small
349 // files for APar_Validate
350 }
351 return bytes_read;
352 }
353
354 uint32_t APar_FindValueInAtom(char *uint32_buffer,
355 FILE *ISObasemediafile,
356 short an_atom,
357 uint64_t start_position,
358 uint32_t eval_number) {
359 uint64_t current_pos = start_position;
360 memset(uint32_buffer, 0, 5);
361 while (current_pos <= parsedAtoms[an_atom].AtomicLength) {
362 current_pos++;
363 if (eval_number > 65535) {
364 // current_pos +=4;
365 if (APar_read32(uint32_buffer,
366 ISObasemediafile,
367 parsedAtoms[an_atom].AtomicStart + current_pos) ==
368 eval_number) {
369 break;
370 }
371 } else {
372 // current_pos +=2;
373 if (APar_read16(uint32_buffer,
374 ISObasemediafile,
375 parsedAtoms[an_atom].AtomicStart + current_pos) ==
376 (uint16_t)eval_number) {
377 break;
378 }
379 }
380 if (current_pos >= parsedAtoms[an_atom].AtomicLength) {
381 current_pos = 0;
382 break;
383 }
384 }
385 return (uint32_t)current_pos;
386 }
387
388 ///////////////////////////////////////////////////////////////////////////////////////
389 // Language specifics //
306390 ///////////////////////////////////////////////////////////////////////////////////////
307391
308392 void APar_UnpackLanguage(unsigned char lang_code[], uint16_t packed_language) {
309 lang_code[3] = 0;
310 lang_code[2] = (packed_language &0x1F) + 0x60;
311 lang_code[1] = ((packed_language >> 5) &0x1F) + 0x60;
312 lang_code[0] = ((packed_language >> 10) &0x1F) + 0x60;
313 return;
314 }
315
316 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
317 //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.
318 //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.
319
320 uint16_t packed_language = 0;
321
322 //fprintf(stdout, "%i, %i, %i\n", language_code[0+lang_offset], language_code[1+lang_offset], language_code[2+lang_offset]);
323
324 if (language_code[0+lang_offset] < 97 || language_code[0+lang_offset] > 122 ||
325 language_code[1+lang_offset] < 97 || language_code[1+lang_offset] > 122 ||
326 language_code[2+lang_offset] < 97 || language_code[2+lang_offset] > 122) {
327
328 return packed_language;
329 }
330
331 packed_language = (((language_code[0+lang_offset] - 0x60) & 0x1F) << 10 ) |
332 (((language_code[1+lang_offset] - 0x60) & 0x1F) << 5) |
333 ((language_code[2+lang_offset] - 0x60) & 0x1F);
334 return packed_language;
335 }
336
337 ///////////////////////////////////////////////////////////////////////////////////////
338 // platform specifics //
339 ///////////////////////////////////////////////////////////////////////////////////////
340
341 #if !defined(HAVE_LROUNDF)
342 int lroundf(float a) {
343 return (int)(a/1);
344 }
345 #endif
393 lang_code[3] = 0;
394 lang_code[2] = (packed_language & 0x1F) + 0x60;
395 lang_code[1] = ((packed_language >> 5) & 0x1F) + 0x60;
396 lang_code[0] = ((packed_language >> 10) & 0x1F) + 0x60;
397 return;
398 }
399
400 uint16_t PackLanguage(
401 const char *language_code,
402 uint8_t lang_offset) { //?? is there a problem here? und does't work
403 // http://www.w3.org/WAI/ER/IG/ert/iso639.htm
404 // I think Apple's 3gp asses decoder is a little off. First, it doesn't
405 // support a lot of those 3 letter language codes above on that page. for
406 // example 'zul' blocks *all* metadata from showing up. 'fre' is a no-no, but
407 // 'fra' is fine. then, the spec calls for all strings to be null terminated.
408 // So then why does a '© 2005' (with a NULL at the end) show up as '© 2005' in
409 // 'pol', but '© 2005 ?' in 'fas' Farsi? Must be Apple's implementation,
410 // because the files are identical except for the uint16_t lang setting.
411
412 uint16_t packed_language = 0;
413
414 // fprintf(stdout, "%i, %i, %i\n", language_code[0+lang_offset],
415 // language_code[1+lang_offset], language_code[2+lang_offset]);
416
417 if (language_code[0 + lang_offset] < 97 ||
418 language_code[0 + lang_offset] > 122 ||
419 language_code[1 + lang_offset] < 97 ||
420 language_code[1 + lang_offset] > 122 ||
421 language_code[2 + lang_offset] < 97 ||
422 language_code[2 + lang_offset] > 122) {
423
424 return packed_language;
425 }
426
427 packed_language = (((language_code[0 + lang_offset] - 0x60) & 0x1F) << 10) |
428 (((language_code[1 + lang_offset] - 0x60) & 0x1F) << 5) |
429 ((language_code[2 + lang_offset] - 0x60) & 0x1F);
430 return packed_language;
431 }
432
433 ///////////////////////////////////////////////////////////////////////////////////////
434 // platform specifics //
435 ///////////////////////////////////////////////////////////////////////////////////////
346436
347437 #ifndef HAVE_STRSEP
348 // use glibc's strsep only on windows when cygwin & libc are undefined; otherwise the internal strsep will be used
349 // This marks the point where a ./configure & makefile combo would make this easier
438 // use glibc's strsep only on windows when cygwin & libc are undefined;
439 // otherwise the internal strsep will be used This marks the point where a
440 // ./configure & makefile combo would make this easier
350441
351442 /* Copyright (C) 1992, 93, 96, 97, 98, 99, 2004 Free Software Foundation, Inc.
352443 This strsep function is part of the GNU C Library - v2.3.5; LGPL.
353444 */
354445
355 char *strsep (char **stringp, const char *delim)
356 {
446 char *strsep(char **stringp, const char *delim) {
357447 char *begin, *end;
358448
359449 begin = *stringp;
360450 if (begin == NULL)
361451 return NULL;
362452
363 //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'.
364 if (delim[0] == '\0' || delim[1] == '\0')
365 {
366 char ch = delim[0];
367
368 if (ch == '\0')
369 end = NULL;
453 // A frequent case is when the delimiter string contains only one character.
454 // Here we don't need to call the expensive `strpbrk' function and instead
455 // work using `strchr'.
456 if (delim[0] == '\0' || delim[1] == '\0') {
457 char ch = delim[0];
458
459 if (ch == '\0')
460 end = NULL;
461 else {
462 if (*begin == ch)
463 end = begin;
464 else if (*begin == '\0')
465 end = NULL;
370466 else
371 {
372 if (*begin == ch)
373 end = begin;
374 else if (*begin == '\0')
375 end = NULL;
376 else
377 end = strchr (begin + 1, ch);
378 }
379 }
380 else
381
382 end = strpbrk (begin, delim); //Find the end of the token.
383
384 if (end)
385 {
386 *end++ = '\0'; //Terminate the token and set *STRINGP past NUL character.
387 *stringp = end;
388 }
389 else
390 *stringp = NULL; //No more delimiters; this is the last token.
467 end = strchr(begin + 1, ch);
468 }
469 } else
470
471 end = strpbrk(begin, delim); // Find the end of the token.
472
473 if (end) {
474 *end++ = '\0'; // Terminate the token and set *STRINGP past NUL character.
475 *stringp = end;
476 } else
477 *stringp = NULL; // No more delimiters; this is the last token.
391478
392479 return begin;
393480 }
394481 #endif
395482
396483 void determine_MonthDay(int literal_day, int &month, int &day) {
397 if (literal_day <= 31) {
398 month = 1; day = literal_day;
399
400 } else if (literal_day <= 59) {
401 month = 2; day = literal_day - 31;
402
403 } else if (literal_day <= 90) {
404 month = 3; day = literal_day - 59;
405
406 } else if (literal_day <= 120) {
407 month = 4; day = literal_day - 90;
408
409 } else if (literal_day <= 151) {
410 month = 5; day = literal_day - 120;
411
412 } else if (literal_day <= 181) {
413 month = 6; day = literal_day - 151;
414
415 } else if (literal_day <= 212) {
416 month = 7; day = literal_day - 181;
417
418 } else if (literal_day <= 243) {
419 month = 8; day = literal_day - 212;
420
421 } else if (literal_day <= 273) {
422 month = 9; day = literal_day - 243;
423
424 } else if (literal_day <= 304) {
425 month = 10; day = literal_day - 273;
426
427 } else if (literal_day <= 334) {
428 month = 11; day = literal_day - 304;
429
430 } else if (literal_day <= 365) {
431 month = 12; day = literal_day - 334;
432 }
433 return;
434 }
435
436 char* APar_gmtime64(uint64_t total_secs, char* utc_time) {
437 //this will probably be off between Jan 1 & Feb 28 on a leap year by a day.... I'll somehow cope & deal.
438 struct tm timeinfo = {0,0,0,0,0};
439
440 int offset_year = (int) (total_secs / 31536000); //60 * 60 * 24 * 365 (ordinary year in seconds; doesn't account for leap year)
441 int literal_year = 1904 + offset_year;
442 int literal_days_into_year = ((total_secs % 31536000) / 86400) - (offset_year / 4); //accounts for the leap year
443
444 uint32_t literal_seconds_into_day = total_secs % 86400;
445
446 int month = 0;
447 int days = 0;
448
449 determine_MonthDay(literal_days_into_year, month, days);
450
451 if (literal_days_into_year < 0 ) {
452 literal_year -=1;
453 literal_days_into_year = 31 +literal_days_into_year;
454 month = 12;
455 days = literal_days_into_year;
456 }
457
458 int hours = literal_seconds_into_day / 3600;
459
460 timeinfo.tm_year = literal_year - 1900;
461 timeinfo.tm_yday = literal_days_into_year;
462 timeinfo.tm_mon = month - 1;
463 timeinfo.tm_mday = days;
464 timeinfo.tm_wday = (((total_secs / 86400) - (offset_year / 4)) - 5 ) % 7;
465
466 timeinfo.tm_hour = hours;
467 timeinfo.tm_min = (literal_seconds_into_day - (hours * 3600)) / 60;
468 timeinfo.tm_sec = (int)(literal_seconds_into_day % 60);
469
470 strftime(utc_time, 50 , "%a %b %d %H:%M:%S %Y", &timeinfo);
471 return utc_time;
484 if (literal_day <= 31) {
485 month = 1;
486 day = literal_day;
487
488 } else if (literal_day <= 59) {
489 month = 2;
490 day = literal_day - 31;
491
492 } else if (literal_day <= 90) {
493 month = 3;
494 day = literal_day - 59;
495
496 } else if (literal_day <= 120) {
497 month = 4;
498 day = literal_day - 90;
499
500 } else if (literal_day <= 151) {
501 month = 5;
502 day = literal_day - 120;
503
504 } else if (literal_day <= 181) {
505 month = 6;
506 day = literal_day - 151;
507
508 } else if (literal_day <= 212) {
509 month = 7;
510 day = literal_day - 181;
511
512 } else if (literal_day <= 243) {
513 month = 8;
514 day = literal_day - 212;
515
516 } else if (literal_day <= 273) {
517 month = 9;
518 day = literal_day - 243;
519
520 } else if (literal_day <= 304) {
521 month = 10;
522 day = literal_day - 273;
523
524 } else if (literal_day <= 334) {
525 month = 11;
526 day = literal_day - 304;
527
528 } else if (literal_day <= 365) {
529 month = 12;
530 day = literal_day - 334;
531 }
532 return;
533 }
534
535 char *APar_gmtime64(uint64_t total_secs, char *utc_time) {
536 // this will probably be off between Jan 1 & Feb 28 on a leap year by a
537 // day.... I'll somehow cope & deal.
538 struct tm timeinfo = {0, 0, 0, 0, 0};
539
540 int offset_year =
541 (int)(total_secs / 31536000); // 60 * 60 * 24 * 365 (ordinary year in
542 // seconds; doesn't account for leap year)
543 int literal_year = 1904 + offset_year;
544 int literal_days_into_year = ((total_secs % 31536000) / 86400) -
545 (offset_year / 4); // accounts for the leap year
546
547 uint32_t literal_seconds_into_day = total_secs % 86400;
548
549 int month = 0;
550 int days = 0;
551
552 determine_MonthDay(literal_days_into_year, month, days);
553
554 if (literal_days_into_year < 0) {
555 literal_year -= 1;
556 literal_days_into_year = 31 + literal_days_into_year;
557 month = 12;
558 days = literal_days_into_year;
559 }
560
561 int hours = literal_seconds_into_day / 3600;
562
563 timeinfo.tm_year = literal_year - 1900;
564 timeinfo.tm_yday = literal_days_into_year;
565 timeinfo.tm_mon = month - 1;
566 timeinfo.tm_mday = days;
567 timeinfo.tm_wday = (((total_secs / 86400) - (offset_year / 4)) - 5) % 7;
568
569 timeinfo.tm_hour = hours;
570 timeinfo.tm_min = (literal_seconds_into_day - (hours * 3600)) / 60;
571 timeinfo.tm_sec = (int)(literal_seconds_into_day % 60);
572
573 strftime(utc_time, 50, "%a %b %d %H:%M:%S %Y", &timeinfo);
574 return utc_time;
472575 }
473576
474577 /*----------------------
477580
478581 Convert the seconds to a calendar date with seconds.
479582 ----------------------*/
480 char* APar_extract_UTC(uint64_t total_secs) {
481 //2082844800 seconds between 01/01/1904 & 01/01/1970
482 // 2,081,376,000 (60 seconds * 60 minutes * 24 hours * 365 days * 66 years)
483 // + 1,468,800 (60 * 60 * 24 * 17 leap days in 01/01/1904 to 01/01/1970 duration)
484 //= 2,082,844,800
485 static char utc_time[50];
486 memset(utc_time, 0, 50);
487
488 if (total_secs > MAXTIME_32) {
489 return APar_gmtime64(total_secs, utc_time);
490 } else {
491 if (total_secs < 2082844800) {
492 return APar_gmtime64(total_secs, utc_time); //less than Unix epoch
493 } else {
494 total_secs -= 2082844800;
495 time_t reduced_seconds = (time_t)total_secs;
496 strftime(*&utc_time, 50 , "%a %b %d %H:%M:%S %Y", gmtime(&reduced_seconds) );
497 return *&utc_time;
498 }
499 }
500 return *&utc_time;
583 char *APar_extract_UTC(uint64_t total_secs) {
584 // 2082844800 seconds between 01/01/1904 & 01/01/1970
585 // 2,081,376,000 (60 seconds * 60 minutes * 24 hours * 365 days * 66 years)
586 // + 1,468,800 (60 * 60 * 24 * 17 leap days in 01/01/1904 to 01/01/1970
587 // duration)
588 //= 2,082,844,800
589 static char utc_time[50];
590 memset(utc_time, 0, 50);
591
592 if (total_secs > MAXTIME_32) {
593 return APar_gmtime64(total_secs, utc_time);
594 } else {
595 if (total_secs < 2082844800) {
596 return APar_gmtime64(total_secs, utc_time); // less than Unix epoch
597 } else {
598 total_secs -= 2082844800;
599 time_t reduced_seconds = (time_t)total_secs;
600 strftime(
601 *&utc_time, 50, "%a %b %d %H:%M:%S %Y", gmtime(&reduced_seconds));
602 return *&utc_time;
603 }
604 }
605 return *&utc_time;
501606 }
502607
503608 uint32_t APar_get_mpeg4_time() {
504 #if defined (_WIN32) && !defined (__CYGWIN__)
505 FILETIME file_time;
506 uint64_t wintime = 0;
507 GetSystemTimeAsFileTime (&file_time);
508 wintime = (((uint64_t) file_time.dwHighDateTime << 32) | file_time.dwLowDateTime) / 10000000;
509 wintime -= 9561628800ULL;
510 return (uint32_t)wintime;
609 #if defined(_WIN32) && !defined(__CYGWIN__)
610 FILETIME file_time;
611 uint64_t wintime = 0;
612 GetSystemTimeAsFileTime(&file_time);
613 wintime =
614 (((uint64_t)file_time.dwHighDateTime << 32) | file_time.dwLowDateTime) /
615 10000000;
616 wintime -= 9561628800ULL;
617 return (uint32_t)wintime;
511618
512619 #else
513 uint32_t current_time_in_seconds = 0;
514 struct timeval tv;
515 gettimeofday(&tv, NULL);
516 current_time_in_seconds = tv.tv_sec;
517 return current_time_in_seconds + 2082844800;
518
519 #endif
520 return 0;
521 }
522
620 uint32_t current_time_in_seconds = 0;
621 struct timeval tv;
622 gettimeofday(&tv, NULL);
623 current_time_in_seconds = tv.tv_sec;
624 return current_time_in_seconds + 2082844800;
625
626 #endif
627 return 0;
628 }
523629
524630 /*----------------------
525631 APar_StandardTime
526 formed_time - the destination string
527
528 Print the ISO 8601 Coordinated Universal Time (UTC) timestamp (in YYYY-MM-DDTHH:MM:SSZ form)
632 formed_time - the destination string
633
634 Print the ISO 8601 Coordinated Universal Time (UTC) timestamp (in
635 YYYY-MM-DDTHH:MM:SSZ form)
529636 ----------------------*/
530 void APar_StandardTime(char* &formed_time) {
637 void APar_StandardTime(char *&formed_time) {
531638 time_t rawtime;
532639 struct tm *timeinfo;
533640
534 time (&rawtime);
535 timeinfo = gmtime (&rawtime);
536 strftime(formed_time ,100 , "%Y-%m-%dT%H:%M:%SZ", timeinfo); //that hanging Z is there; denotes the UTC
537
538 return;
539 }
540
541 ///////////////////////////////////////////////////////////////////////////////////////
542 // strings //
543 ///////////////////////////////////////////////////////////////////////////////////////
544
545 wchar_t* Convert_multibyteUTF16_to_wchar(char* input_unicode, size_t glyph_length, bool skip_BOM) { //TODO: is this like wcstombs?
546 int BOM_mark_bytes = 0;
547 if (skip_BOM) {
548 BOM_mark_bytes = 2;
549 }
550
551 wchar_t* utf16_data = (wchar_t*)malloc( sizeof(wchar_t)* (glyph_length+1) ); //just to be sure there will be a trailing NULL
552 wmemset(utf16_data, 0, glyph_length + 1);
553
554 for(size_t i = 0; i < glyph_length; i++) {
555 #if defined (__ppc__) || defined (__ppc64__)
556 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
641 time(&rawtime);
642 timeinfo = gmtime(&rawtime);
643 strftime(formed_time,
644 100,
645 "%Y-%m-%dT%H:%M:%SZ",
646 timeinfo); // that hanging Z is there; denotes the UTC
647
648 return;
649 }
650
651 ///////////////////////////////////////////////////////////////////////////////////////
652 // strings //
653 ///////////////////////////////////////////////////////////////////////////////////////
654
655 wchar_t *
656 Convert_multibyteUTF16_to_wchar(char *input_unicode,
657 size_t glyph_length,
658 bool skip_BOM) { // TODO: is this like wcstombs?
659 int BOM_mark_bytes = 0;
660 if (skip_BOM) {
661 BOM_mark_bytes = 2;
662 }
663
664 wchar_t *utf16_data = (wchar_t *)malloc(
665 sizeof(wchar_t) *
666 (glyph_length + 1)); // just to be sure there will be a trailing NULL
667 wmemset(utf16_data, 0, glyph_length + 1);
668
669 for (size_t i = 0; i < glyph_length; i++) {
670 #if defined(__ppc__) || defined(__ppc64__)
671 utf16_data[i] = (input_unicode[2 * i + BOM_mark_bytes] & 0x00ff) << 8 |
672 (input_unicode[2 * i + 1 + BOM_mark_bytes])
673 << 0; //+2 & +3 to skip over the BOM
557674 #else
558 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
559 #endif
560 }
561 return utf16_data;
562 }
563
564 unsigned char* Convert_multibyteUTF16_to_UTF8(char* input_utf16, size_t glyph_length, size_t byte_count) {
565 unsigned char* utf8_data = (unsigned char*)malloc(sizeof(unsigned char)* glyph_length );
566 memset(utf8_data, 0, glyph_length);
567
568 UTF16BEToUTF8(utf8_data, glyph_length, (unsigned char*)input_utf16 + 2, byte_count);
569 return utf8_data;
570 }
571
572 wchar_t* Convert_multibyteUTF8_to_wchar(const char* input_utf8) { //TODO: is this like mbstowcs?
573 wchar_t *return_val=NULL;
574 size_t string_length = strlen(input_utf8) + 1; //account for terminating NULL
575 size_t char_glyphs = mbstowcs(NULL, input_utf8, string_length); //passing NULL pre-calculates the size of wchar_t needed
576
577 unsigned char* utf16_conversion = (unsigned char*)malloc( sizeof(unsigned char)* string_length * 2 );
578 memset(utf16_conversion, 0, string_length * 2 );
579
580 int utf_16_glyphs = UTF8ToUTF16BE(utf16_conversion, char_glyphs * 2, (unsigned char*)input_utf8, string_length) / 2; //returned value is in bytes
581 return_val = Convert_multibyteUTF16_to_wchar((char*)utf16_conversion, (size_t)utf_16_glyphs, false );
582 free(utf16_conversion); utf16_conversion=NULL;
583 return (return_val);
584 }
585
586 //these flags from id3v2 2.4
587 //0x00 = ISO-8859-1 & terminate with 0x00.
588 //0x01 = UTF-16 with BOM. All frames have same encoding & terminate with 0x0000.
589 //0x02 = UTF-16BE without BOM & terminate with 0x0000.
590 //0x03 = UTF-8 & terminated with 0x00.
591 //buffer can hold either ut8 or utf16 carried on 8-bit char which requires a cast
675 utf16_data[i] = (input_unicode[2 * i + BOM_mark_bytes] << 8) |
676 ((input_unicode[2 * i + 1 + BOM_mark_bytes]) & 0x00ff)
677 << 0; //+2 & +3 to skip over the BOM
678 #endif
679 }
680 return utf16_data;
681 }
682
683 unsigned char *Convert_multibyteUTF16_to_UTF8(char *input_utf16,
684 size_t glyph_length,
685 size_t byte_count) {
686 unsigned char *utf8_data =
687 (unsigned char *)malloc(sizeof(unsigned char) * glyph_length);
688 memset(utf8_data, 0, glyph_length);
689
690 UTF16BEToUTF8(
691 utf8_data, glyph_length, (unsigned char *)input_utf16 + 2, byte_count);
692 return utf8_data;
693 }
694
695 wchar_t *Convert_multibyteUTF8_to_wchar(
696 const char *input_utf8) { // TODO: is this like mbstowcs?
697 wchar_t *return_val = NULL;
698 size_t string_length = strlen(input_utf8) + 1; // account for terminating NULL
699 size_t char_glyphs = mbstowcs(
700 NULL,
701 input_utf8,
702 string_length); // passing NULL pre-calculates the size of wchar_t needed
703
704 unsigned char *utf16_conversion =
705 (unsigned char *)malloc(sizeof(unsigned char) * string_length * 2);
706 memset(utf16_conversion, 0, string_length * 2);
707
708 int utf_16_glyphs = UTF8ToUTF16BE(utf16_conversion,
709 char_glyphs * 2,
710 (unsigned char *)input_utf8,
711 string_length) /
712 2; // returned value is in bytes
713 return_val = Convert_multibyteUTF16_to_wchar(
714 (char *)utf16_conversion, (size_t)utf_16_glyphs, false);
715 free(utf16_conversion);
716 utf16_conversion = NULL;
717 return (return_val);
718 }
719
720 // these flags from id3v2 2.4
721 // 0x00 = ISO-8859-1 & terminate with 0x00.
722 // 0x01 = UTF-16 with BOM. All frames have same encoding & terminate with
723 // 0x0000. 0x02 = UTF-16BE without BOM & terminate with 0x0000. 0x03 = UTF-8 &
724 // terminated with 0x00. buffer can hold either ut8 or utf16 carried on 8-bit
725 // char which requires a cast
592726 /*----------------------
593727 findstringNULLterm
594 in_string - pointer to location of a string (can be either 8859-1, utf8 or utf16be/utf16be needing a cast to wchar)
595 encodingFlag - used to denote the encoding of instring (derived from id3v2 2.4 encoding flags)
596 max_len - the length of given string - there may be no NULL terminaiton, in which case it will only count to max_len
597
598 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
728 in_string - pointer to location of a string (can be either 8859-1, utf8 or
729 utf16be/utf16be needing a cast to wchar) encodingFlag - used to denote the
730 encoding of instring (derived from id3v2 2.4 encoding flags) max_len - the
731 length of given string - there may be no NULL terminaiton, in which case it will
732 only count to max_len
733
734 Either find the NULL if it exists and return how many bytes into in_string
735 that NULL exists, or it won't find a NULL and return max_len
599736 ----------------------*/
600 uint32_t findstringNULLterm (char* in_string, uint8_t encodingFlag, uint32_t max_len) {
601 uint32_t byte_count = 0;
602
603 if (encodingFlag == 0x00 || encodingFlag == 0x03) {
604 char* bufptr = in_string;
605 while (bufptr <= in_string+max_len) {
606 if (*bufptr == 0x00) {
607 break;
608 }
609 bufptr++;
610 byte_count++;
611 }
612 } else if ((encodingFlag == 0x01 || encodingFlag == 0x02) && max_len >= 2) {
613 short wbufptr;
614 while (byte_count <= max_len) {
615 wbufptr = (*(in_string+byte_count) << 8) | *(in_string+byte_count+1);
616 if (wbufptr == 0x0000) {
617 break;
618 }
619 byte_count+=2;
620 }
621 }
622 if (byte_count > max_len) return max_len;
623 return byte_count;
624 }
625
626 uint32_t skipNULLterm (char* in_string, uint8_t encodingFlag, uint32_t max_len) {
627 uint32_t byte_count = 0;
628
629 if (encodingFlag == 0x00 || encodingFlag == 0x03) {
630 char* bufptr = in_string;
631 while (bufptr <= in_string+max_len) {
632 if (*bufptr == 0x00) {
633 byte_count++;
634 break;
635 }
636 bufptr++;
637 }
638 } else if ((encodingFlag == 0x01 || encodingFlag == 0x02) && max_len >= 2) {
639 short wbufptr;
640 while (byte_count <= max_len) {
641 wbufptr = (*(in_string+byte_count) << 8) | *(in_string+byte_count+1);
642 if (wbufptr == 0x0000) {
643 byte_count+=2;
644 break;
645 }
646 }
647 }
648 return byte_count;
649 }
650
651 ///////////////////////////////////////////////////////////////////////////////////////
652 // generics //
737 uint32_t
738 findstringNULLterm(char *in_string, uint8_t encodingFlag, uint32_t max_len) {
739 uint32_t byte_count = 0;
740
741 if (encodingFlag == 0x00 || encodingFlag == 0x03) {
742 char *bufptr = in_string;
743 while (bufptr <= in_string + max_len) {
744 if (*bufptr == 0x00) {
745 break;
746 }
747 bufptr++;
748 byte_count++;
749 }
750 } else if ((encodingFlag == 0x01 || encodingFlag == 0x02) && max_len >= 2) {
751 short wbufptr;
752 while (byte_count <= max_len) {
753 wbufptr =
754 (*(in_string + byte_count) << 8) | *(in_string + byte_count + 1);
755 if (wbufptr == 0x0000) {
756 break;
757 }
758 byte_count += 2;
759 }
760 }
761 if (byte_count > max_len)
762 return max_len;
763 return byte_count;
764 }
765
766 uint32_t skipNULLterm(char *in_string, uint8_t encodingFlag, uint32_t max_len) {
767 uint32_t byte_count = 0;
768
769 if (encodingFlag == 0x00 || encodingFlag == 0x03) {
770 char *bufptr = in_string;
771 while (bufptr <= in_string + max_len) {
772 if (*bufptr == 0x00) {
773 byte_count++;
774 break;
775 }
776 bufptr++;
777 }
778 } else if ((encodingFlag == 0x01 || encodingFlag == 0x02) && max_len >= 2) {
779 short wbufptr;
780 while (byte_count <= max_len) {
781 wbufptr =
782 (*(in_string + byte_count) << 8) | *(in_string + byte_count + 1);
783 if (wbufptr == 0x0000) {
784 byte_count += 2;
785 break;
786 }
787 }
788 }
789 return byte_count;
790 }
791
792 ///////////////////////////////////////////////////////////////////////////////////////
793 // generics //
653794 ///////////////////////////////////////////////////////////////////////////////////////
654795
655796 uint16_t UInt16FromBigEndian(const char *string) {
656 #if defined (__ppc__) || defined (__ppc64__)
657 uint16_t test;
658 memcpy(&test,string,2);
659 return test;
797 #if defined(__ppc__) || defined(__ppc64__)
798 uint16_t test;
799 memcpy(&test, string, 2);
800 return test;
660801 #else
661 return (((string[0] & 0xff) << 8) | (string[1] & 0xff) << 0);
802 return (((string[0] & 0xff) << 8) | (string[1] & 0xff) << 0);
662803 #endif
663804 }
664805
665806 uint32_t UInt32FromBigEndian(const char *string) {
666 #if defined (__ppc__) || defined (__ppc64__)
667 uint32_t test;
668 memcpy(&test,string,4);
669 return test;
807 #if defined(__ppc__) || defined(__ppc64__)
808 uint32_t test;
809 memcpy(&test, string, 4);
810 return test;
670811 #else
671 return (((string[0] & 0xff) << 24) | ((string[1] & 0xff) << 16) | ((string[2] & 0xff) << 8) | (string[3] & 0xff) << 0);
812 return (((string[0] & 0xff) << 24) | ((string[1] & 0xff) << 16) |
813 ((string[2] & 0xff) << 8) | (string[3] & 0xff) << 0);
672814 #endif
673815 }
674816
675817 uint64_t UInt64FromBigEndian(const char *string) {
676 #if defined (__ppc__) || defined (__ppc64__)
677 uint64_t test;
678 memcpy(&test,string,8);
679 return test;
818 #if defined(__ppc__) || defined(__ppc64__)
819 uint64_t test;
820 memcpy(&test, string, 8);
821 return test;
680822 #else
681 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 |
682 (uint64_t)(string[4] & 0xff) << 24 | (uint64_t)(string[5] & 0xff) << 16 | (uint64_t)(string[6] & 0xff) << 8 | (uint64_t)(string[7] & 0xff) << 0;
683 #endif
684 }
685
686 void UInt16_TO_String2(uint16_t snum, char* data) {
687 data[0] = (snum >> 8) & 0xff;
688 data[1] = (snum >> 0) & 0xff;
689 return;
690 }
691
692 void UInt32_TO_String4(uint32_t lnum, char* data) {
693 data[0] = (lnum >> 24) & 0xff;
694 data[1] = (lnum >> 16) & 0xff;
695 data[2] = (lnum >> 8) & 0xff;
696 data[3] = (lnum >> 0) & 0xff;
697 return;
698 }
699
700 void UInt64_TO_String8(uint64_t ullnum, char* data) {
701 data[0] = (ullnum >> 56) & 0xff;
702 data[1] = (ullnum >> 48) & 0xff;
703 data[2] = (ullnum >> 40) & 0xff;
704 data[3] = (ullnum >> 32) & 0xff;
705 data[4] = (ullnum >> 24) & 0xff;
706 data[5] = (ullnum >> 16) & 0xff;
707 data[6] = (ullnum >> 8) & 0xff;
708 data[7] = (ullnum >> 0) & 0xff;
709 return;
710 }
711
712 ///////////////////////////////////////////////////////////////////////////////////////
713 // 3gp asset support (for 'loci') //
823 return (uint64_t)(string[0] & 0xff) << 54 |
824 (uint64_t)(string[1] & 0xff) << 48 |
825 (uint64_t)(string[2] & 0xff) << 40 |
826 (uint64_t)(string[3] & 0xff) << 32 |
827 (uint64_t)(string[4] & 0xff) << 24 |
828 (uint64_t)(string[5] & 0xff) << 16 |
829 (uint64_t)(string[6] & 0xff) << 8 | (uint64_t)(string[7] & 0xff) << 0;
830 #endif
831 }
832
833 void UInt16_TO_String2(uint16_t snum, char *data) {
834 data[0] = (snum >> 8) & 0xff;
835 data[1] = (snum >> 0) & 0xff;
836 return;
837 }
838
839 void UInt32_TO_String4(uint32_t lnum, char *data) {
840 data[0] = (lnum >> 24) & 0xff;
841 data[1] = (lnum >> 16) & 0xff;
842 data[2] = (lnum >> 8) & 0xff;
843 data[3] = (lnum >> 0) & 0xff;
844 return;
845 }
846
847 void UInt64_TO_String8(uint64_t ullnum, char *data) {
848 data[0] = (ullnum >> 56) & 0xff;
849 data[1] = (ullnum >> 48) & 0xff;
850 data[2] = (ullnum >> 40) & 0xff;
851 data[3] = (ullnum >> 32) & 0xff;
852 data[4] = (ullnum >> 24) & 0xff;
853 data[5] = (ullnum >> 16) & 0xff;
854 data[6] = (ullnum >> 8) & 0xff;
855 data[7] = (ullnum >> 0) & 0xff;
856 return;
857 }
858
859 ///////////////////////////////////////////////////////////////////////////////////////
860 // 3gp asset support (for 'loci') //
714861 ///////////////////////////////////////////////////////////////////////////////////////
715862
716863 uint32_t float_to_16x16bit_fixed_point(double floating_val) {
717 uint32_t fixedpoint_16bit = 0;
718 int16_t long_integer = (int16_t)floating_val;
719 //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
720 //now while the degrees can be negative (requiring a int16_6), the decimal portion is always positive (and thus requiring a uint16_t)
721 uint16_t long_decimal = (int16_t) ((floating_val - long_integer) * (double)(65536) );
722 fixedpoint_16bit = long_integer * 65536 + long_decimal; //same as bitshifting, less headache doing it
723 return fixedpoint_16bit;
864 uint32_t fixedpoint_16bit = 0;
865 int16_t long_integer = (int16_t)floating_val;
866 // to get a fixed 16-bit decimal, work on the decimal part along; multiply by
867 // (2^8 * 2) which moves the decimal over 16 bits to create our int16_t now
868 // while the degrees can be negative (requiring a int16_6), the decimal
869 // portion is always positive (and thus requiring a uint16_t)
870 uint16_t long_decimal =
871 (int16_t)((floating_val - long_integer) * (double)(65536));
872 fixedpoint_16bit =
873 long_integer * 65536 +
874 long_decimal; // same as bitshifting, less headache doing it
875 return fixedpoint_16bit;
724876 }
725877
726878 double fixed_point_16x16bit_to_double(uint32_t fixed_point) {
727 double return_val = 0.0;
728 int16_t long_integer = fixed_point / 65536;
729 uint16_t long_decimal = fixed_point - (long_integer * 65536) ;
730 return_val = long_integer + ( (double)long_decimal / 65536);
731
732 if (return_val < 0.0) {
733 return_val-=1.0;
734 }
735
736 return return_val;
737 }
738
739 uint32_t widechar_len(char* instring, uint32_t _bytes_) {
740 uint32_t wstring_len = 0;
741 for (uint32_t i = 0; i <= _bytes_/2 ; i++) {
742 if ( instring[0] == 0 && instring[1] == 0) {
743 break;
744 } else {
745 instring+=2;
746 wstring_len++;
747 }
748 }
749 return wstring_len;
750 }
751
752 bool APar_assert(bool expression, int error_msg, const char* supplemental_info) {
753 bool force_break = true;
754 if (!expression) {
755 force_break = false;
756 switch (error_msg) {
757 case 1 : { //trying to set an iTunes-style metadata tag on an 3GP/MobileMPEG-4
758 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);
759 break;
760 }
761
762 case 2 : { //trying to set a 3gp asset on an mpeg-4 file with the improper brand
763 fprintf(stdout, "AP warning:\n\tSetting the %s asset is only available on 3GPP files branded 3gp6 or later.\nSkipping\n", supplemental_info);
764 break;
765 }
766
767 case 3 : { //trying to set 'meta' on a file without a iso2 or mp42 compatible brand.
768 fprintf(stdout, "AtomicParsley warning: ID3 tags requires a v2 compatible file, which was not found.\nSkipping.\n");
769 break;
770 }
771 case 4 : { //trying to set a 3gp album asset on an early 3gp file that only came into being with 3gp6
772 fprintf(stdout, "Major brand of given file: %s\n", supplemental_info);
773 break;
774 }
775 case 5 : { //trying to set metadata on track 33 when there are only 3 tracks
776 fprintf(stdout, "AP warning: skipping non-existing track number setting user data atom: %s.\n", supplemental_info);
777 break;
778 }
779 case 6 : { //trying to set id3 metadata on track 33 when there are only 3 tracks
780 fprintf(stdout, "AP error: skipping non-existing track number setting frame %s for ID32 atom.\n", supplemental_info);
781 break;
782 }
783 case 7 : { //trying to set id3 metadata on track 33 when there are only 3 tracks
784 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);
785 break;
786 }
787 case 8 : { //trying to create an ID32 atom when there is a primary item atom present signaling referenced data (local or external)
788 fprintf(stdout, "AP warning: unsupported external or referenced items were detected. Skipping this frame: %s\n", supplemental_info);
789 break;
790 }
791 case 9 : { //trying to eliminate an id3 frame that doesn't exist
792 fprintf(stdout, "AP warning: id3 frame %s cannot be deleted because it does not exist.\n", supplemental_info);
793 break;
794 }
795 case 10 : { //trying to eliminate an id3 frame that doesn't exist
796 fprintf(stdout, "AP warning: skipping setting unknown %s frame\n", supplemental_info);
797 break;
798 }
799 case 11 : { //insuffient memory to malloc an id3 field (probably picture or encapuslated object)
800 fprintf(stdout, "AP error: memory was not alloctated for frame %s. Exiting.\n", supplemental_info);
801 break;
802 }
803 }
804 }
805 return force_break;
879 double return_val = 0.0;
880 int16_t long_integer = fixed_point / 65536;
881 uint16_t long_decimal = fixed_point - (long_integer * 65536);
882 return_val = long_integer + ((double)long_decimal / 65536);
883
884 if (return_val < 0.0) {
885 return_val -= 1.0;
886 }
887
888 return return_val;
889 }
890
891 uint32_t widechar_len(char *instring, uint32_t _bytes_) {
892 uint32_t wstring_len = 0;
893 for (uint32_t i = 0; i <= _bytes_ / 2; i++) {
894 if (instring[0] == 0 && instring[1] == 0) {
895 break;
896 } else {
897 instring += 2;
898 wstring_len++;
899 }
900 }
901 return wstring_len;
902 }
903
904 bool APar_assert(bool expression,
905 int error_msg,
906 const char *supplemental_info) {
907 bool force_break = true;
908 if (!expression) {
909 force_break = false;
910 switch (error_msg) {
911 case 1: { // trying to set an iTunes-style metadata tag on an
912 // 3GP/MobileMPEG-4
913 fprintf(stdout,
914 "AP warning:\n\tSetting the %s tag is for ordinary MPEG-4 "
915 "files.\n\tIt is not supported on 3gp/amc files.\nSkipping\n",
916 supplemental_info);
917 break;
918 }
919
920 case 2: { // trying to set a 3gp asset on an mpeg-4 file with the improper
921 // brand
922 fprintf(stdout,
923 "AP warning:\n\tSetting the %s asset is only available on 3GPP "
924 "files branded 3gp6 or later.\nSkipping\n",
925 supplemental_info);
926 break;
927 }
928
929 case 3: { // trying to set 'meta' on a file without a iso2 or mp42
930 // compatible brand.
931 fprintf(stdout,
932 "AtomicParsley warning: ID3 tags requires a v2 "
933 "compatible file, which was not found.\nSkipping.\n");
934 break;
935 }
936 case 4: { // trying to set a 3gp album asset on an early 3gp file that only
937 // came into being with 3gp6
938 fprintf(stdout, "Major brand of given file: %s\n", supplemental_info);
939 break;
940 }
941 case 5: { // trying to set metadata on track 33 when there are only 3 tracks
942 fprintf(stdout,
943 "AP warning: skipping non-existing track number setting user "
944 "data atom: %s.\n",
945 supplemental_info);
946 break;
947 }
948 case 6: { // trying to set id3 metadata on track 33 when there are only 3
949 // tracks
950 fprintf(stdout,
951 "AP error: skipping non-existing track number setting frame %s "
952 "for ID32 atom.\n",
953 supplemental_info);
954 break;
955 }
956 case 7: { // trying to set id3 metadata on track 33 when there are only 3
957 // tracks
958 fprintf(stdout,
959 "AP warning: the 'meta' atom is being hangled by a %s handler.\n "
960 " Remove the 'meta' atom and its contents and try again.\n",
961 supplemental_info);
962 break;
963 }
964 case 8: { // trying to create an ID32 atom when there is a primary item atom
965 // present signaling referenced data (local or external)
966 fprintf(stdout,
967 "AP warning: unsupported external or referenced items were "
968 "detected. Skipping this frame: %s\n",
969 supplemental_info);
970 break;
971 }
972 case 9: { // trying to eliminate an id3 frame that doesn't exist
973 fprintf(stdout,
974 "AP warning: id3 frame %s cannot be deleted because it does not "
975 "exist.\n",
976 supplemental_info);
977 break;
978 }
979 case 10: { // trying to eliminate an id3 frame that doesn't exist
980 fprintf(stdout,
981 "AP warning: skipping setting unknown %s frame\n",
982 supplemental_info);
983 break;
984 }
985 case 11: { // insuffient memory to malloc an id3 field (probably picture or
986 // encapuslated object)
987 fprintf(stdout,
988 "AP error: memory was not alloctated for frame %s. Exiting.\n",
989 supplemental_info);
990 break;
991 }
992 }
993 }
994 return force_break;
806995 }
807996
808997 /* http://wwwmaths.anu.edu.au/~brent/random.html */
8131002 typedef unsigned long xorgenUINT;
8141003
8151004 unsigned long xor4096i() {
816 /* 32-bit or 64-bit integer random number generator
1005 /* 32-bit or 64-bit integer random number generator
8171006 with period at least 2**4096-1.
818
819 It is assumed that "xorgenUINT" is a 32-bit or 64-bit integer
1007
1008 It is assumed that "xorgenUINT" is a 32-bit or 64-bit integer
8201009 (see typedef statements in xorgens.h).
821
1010
8221011 xor4096i should be called exactly once with nonzero seed, and
823 thereafter with zero seed.
824
1012 thereafter with zero seed.
1013
8251014 One random number uniformly distributed in [0..2**wlen) is returned,
8261015 where wlen = 8*sizeof(xorgenUINT) = 32 or 64.
8271016
8301019
8311020 /* UINT64 is TRUE if 64-bit xorgenUINT,
8321021 UINT32 is TRUE otherwise (assumed to be 32-bit xorgenUINT). */
833
834 #define UINT64 (sizeof(xorgenUINT)>>3)
835 #define UINT32 (1 - UINT64)
836
837 #define wlen (64*UINT64 + 32*UINT32)
838 #define r (64*UINT64 + 128*UINT32)
839 #define s (53*UINT64 + 95*UINT32)
840 #define a (33*UINT64 + 17*UINT32)
841 #define b (26*UINT64 + 12*UINT32)
842 #define c (27*UINT64 + 13*UINT32)
843 #define d (29*UINT64 + 15*UINT32)
844 #define ws (27*UINT64 + 16*UINT32)
845
846 xorgenUINT seed = 0;
847
1022
1023 #define UINT64 (sizeof(xorgenUINT) >> 3)
1024 #define UINT32 (1 - UINT64)
1025
1026 #define wlen (64 * UINT64 + 32 * UINT32)
1027 #define r (64 * UINT64 + 128 * UINT32)
1028 #define s (53 * UINT64 + 95 * UINT32)
1029 #define a (33 * UINT64 + 17 * UINT32)
1030 #define b (26 * UINT64 + 12 * UINT32)
1031 #define c (27 * UINT64 + 13 * UINT32)
1032 #define d (29 * UINT64 + 15 * UINT32)
1033 #define ws (27 * UINT64 + 16 * UINT32)
1034
1035 xorgenUINT seed = 0;
1036
8481037 static xorgenUINT w, weyl, zero = 0, x[r];
8491038 xorgenUINT t, v;
850 static int i = -1 ; /* i < 0 indicates first call */
1039 static int i = -1; /* i < 0 indicates first call */
8511040 int k;
852
853 if (i < 0) {
1041
1042 if (i < 0) {
8541043 #if defined HAVE_SRANDDEV
855 sranddev();
1044 sranddev();
8561045 #else
857 srand((int) time(NULL));
858 #endif
859 double doubleseed = ( (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
860 seed = (xorgenUINT)(doubleseed*rand());
861 }
862
1046 srand((int)time(NULL));
1047 #endif
1048 double doubleseed = ((double)rand() / ((double)(RAND_MAX) + (double)(1)));
1049 seed = (xorgenUINT)(doubleseed * rand());
1050 }
1051
8631052 if ((i < 0) || (seed != zero)) { /* Initialisation necessary */
864
865 /* weyl = odd approximation to 2**wlen*(sqrt(5)-1)/2. */
866
867 if (UINT32)
1053
1054 /* weyl = odd approximation to 2**wlen*(sqrt(5)-1)/2. */
1055
1056 if (UINT32)
8681057 weyl = 0x61c88647;
869 else
870 weyl = ((((xorgenUINT)0x61c88646)<<16)<<16) + (xorgenUINT)0x80b583eb;
871
872 v = (seed!=zero)? seed:~seed; /* v must be nonzero */
873
874 for (k = wlen; k > 0; k--) { /* Avoid correlations for close seeds */
875 v ^= v<<10; v ^= v>>15; /* Recurrence has period 2**wlen-1 */
876 v ^= v<<4; v ^= v>>13; /* for wlen = 32 or 64 */
877 }
1058 else
1059 weyl = ((((xorgenUINT)0x61c88646) << 16) << 16) + (xorgenUINT)0x80b583eb;
1060
1061 v = (seed != zero) ? seed : ~seed; /* v must be nonzero */
1062
1063 for (k = wlen; k > 0; k--) { /* Avoid correlations for close seeds */
1064 v ^= v << 10;
1065 v ^= v >> 15; /* Recurrence has period 2**wlen-1 */
1066 v ^= v << 4;
1067 v ^= v >> 13; /* for wlen = 32 or 64 */
1068 }
8781069 for (w = v, k = 0; (xorgenUINT)k < r; k++) { /* Initialise circular array */
879 v ^= v<<10; v ^= v>>15;
880 v ^= v<<4; v ^= v>>13;
881 x[k] = v + (w+=weyl);
882 }
883 for (i = r-1, k = 4*r; k > 0; k--) { /* Discard first 4*r results */
884 t = x[i = (i+1)&(r-1)]; t ^= t<<a; t ^= t>>b;
885 v = x[(i+(r-s))&(r-1)]; v ^= v<<c; v ^= v>>d;
886 x[i] = t^v;
887 }
888 }
889
1070 v ^= v << 10;
1071 v ^= v >> 15;
1072 v ^= v << 4;
1073 v ^= v >> 13;
1074 x[k] = v + (w += weyl);
1075 }
1076 for (i = r - 1, k = 4 * r; k > 0; k--) { /* Discard first 4*r results */
1077 t = x[i = (i + 1) & (r - 1)];
1078 t ^= t << a;
1079 t ^= t >> b;
1080 v = x[(i + (r - s)) & (r - 1)];
1081 v ^= v << c;
1082 v ^= v >> d;
1083 x[i] = t ^ v;
1084 }
1085 }
1086
8901087 /* Apart from initialisation (above), this is the generator */
8911088
892 t = x[i = (i+1)&(r-1)]; /* Assumes that r is a power of two */
893 v = x[(i+(r-s))&(r-1)]; /* Index is (i-s) mod r */
894 t ^= t<<a; t ^= t>>b; /* (I + L^a)(I + R^b) */
895 v ^= v<<c; v ^= v>>d; /* (I + L^c)(I + R^d) */
896 x[i] = (v ^= t); /* Update circular array */
897 w += weyl; /* Update Weyl generator */
898 return (v + (w^(w>>ws))); /* Return combination */
1089 t = x[i = (i + 1) & (r - 1)]; /* Assumes that r is a power of two */
1090 v = x[(i + (r - s)) & (r - 1)]; /* Index is (i-s) mod r */
1091 t ^= t << a;
1092 t ^= t >> b; /* (I + L^a)(I + R^b) */
1093 v ^= v << c;
1094 v ^= v >> d; /* (I + L^c)(I + R^d) */
1095 x[i] = (v ^= t); /* Update circular array */
1096 w += weyl; /* Update Weyl generator */
1097 return (v + (w ^ (w >> ws))); /* Return combination */
8991098
9001099 #undef UINT64
9011100 #undef UINT32
9061105 #undef b
9071106 #undef c
9081107 #undef d
909 #undef ws
910 }
1108 #undef ws
1109 }
1616
1717 Copyright ©2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
19 */
19 */
2020 //==================================================================//
2121 #include "ap_types.h"
2222
23 #if defined (__ppc__) || defined (__ppc64__)
23 #if defined(__ppc__) || defined(__ppc64__)
2424 #define SWAP16(x) (x)
2525 #define SWAP32(x) (x)
2626 #else
27 #define SWAP16(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
28 #define SWAP32(x) ((((x)&0xFF)<<24) | (((x)>>24)&0xFF) | (((x)&0x0000FF00)<<8) | (((x)&0x00FF0000)>>8) )
27 #define SWAP16(x) ((((x)&0xFF) << 8) | (((x) >> 8) & 0xFF))
28 #define SWAP32(x) \
29 ((((x)&0xFF) << 24) | (((x) >> 24) & 0xFF) | (((x)&0x0000FF00) << 8) | \
30 (((x)&0x00FF0000) >> 8))
2931 #endif
3032
31 #if defined (_WIN32) && defined (_MSC_VER)
33 #if defined(_WIN32) && defined(_MSC_VER)
3234 #undef HAVE_GETOPT_H
3335 #undef HAVE_LROUNDF
3436 #undef HAVE_STRSEP
35 //#undef HAVE_ZLIB_H //comment this IN when compiling on win32 withOUT zlib present
36 //#define HAVE_ZLIB_H 1 //and comment this OUT
37 //#undef HAVE_ZLIB_H //comment this IN when compiling on win32 withOUT zlib
38 // present #define HAVE_ZLIB_H 1 //and comment this OUT
3739 #undef HAVE_SRANDDEV
3840 #endif
3941
4042 #define MAXTIME_32 6377812095ULL
4143
4244 uint64_t findFileSize(const char *utf8_filepath);
43 FILE* APar_OpenFile(const char* utf8_filepath, const char* file_flags);
44 FILE* APar_OpenISOBaseMediaFile(const char* file, bool open); //openSomeFile
45 FILE *APar_OpenFile(const char *utf8_filepath, const char *file_flags);
46 FILE *APar_OpenISOBaseMediaFile(const char *file, bool open); // openSomeFile
4547 void TestFileExistence(const char *filePath, bool errorOut);
4648
47 #if defined (_WIN32)
4849 #ifndef HAVE_FSEEKO
49 int fseeko(FILE *stream, uint64_t pos, int whence);
50 int fseeko(FILE *stream, off_t pos, int whence);
51 off_t ftello(FILE *stream);
5052 #endif
51 HANDLE APar_OpenFileWin32(const char* utf8_filepath, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
53
54 #if defined(_WIN32)
55 HANDLE APar_OpenFileWin32(const char *utf8_filepath,
56 DWORD dwDesiredAccess,
57 DWORD dwShareMode,
58 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
59 DWORD dwCreationDisposition,
60 DWORD dwFlagsAndAttributes,
61 HANDLE hTemplateFile);
5262 #endif
5363 bool IsUnicodeWinOS();
5464
5565 const char *APar_strferror(FILE *f);
56 uint8_t APar_read8(FILE* ISObasemediafile, uint64_t pos);
57 uint16_t APar_read16(char* buffer, FILE* ISObasemediafile, uint64_t pos);
58 uint32_t APar_read32(char* buffer, FILE* ISObasemediafile, uint64_t pos);
59 uint64_t APar_read64(char* buffer, FILE* ISObasemediafile, uint64_t pos);
60 void APar_readX_noseek(char* buffer, FILE* ISObasemediafile, uint32_t length);
61 void APar_readX(char* buffer, FILE* ISObasemediafile, uint64_t pos, uint32_t length);
62 uint32_t APar_ReadFile(char* destination_buffer, FILE* a_file, uint32_t bytes_to_read);
63 uint32_t APar_FindValueInAtom(char* uint32_buffer, FILE* ISObasemediafile, short an_atom, uint64_t start_position, uint32_t eval_number);
66 uint8_t APar_read8(FILE *ISObasemediafile, uint64_t pos);
67 uint16_t APar_read16(char *buffer, FILE *ISObasemediafile, uint64_t pos);
68 uint32_t APar_read32(char *buffer, FILE *ISObasemediafile, uint64_t pos);
69 uint64_t APar_read64(char *buffer, FILE *ISObasemediafile, uint64_t pos);
70 void APar_readX_noseek(char *buffer, FILE *ISObasemediafile, uint32_t length);
71 void APar_readX(char *buffer,
72 FILE *ISObasemediafile,
73 uint64_t pos,
74 uint32_t length);
75 uint32_t
76 APar_ReadFile(char *destination_buffer, FILE *a_file, uint32_t bytes_to_read);
77 uint32_t APar_FindValueInAtom(char *uint32_buffer,
78 FILE *ISObasemediafile,
79 short an_atom,
80 uint64_t start_position,
81 uint32_t eval_number);
6482
6583 void APar_UnpackLanguage(unsigned char lang_code[], uint16_t packed_language);
66 uint16_t PackLanguage(const char* language_code, uint8_t lang_offset);
84 uint16_t PackLanguage(const char *language_code, uint8_t lang_offset);
6785
68 #if !defined(HAVE_LROUNDF)
69 int lroundf(float a);
86 #ifndef HAVE_STRSEP
87 char *strsep(char **stringp, const char *delim);
7088 #endif
7189
72 #ifndef HAVE_STRSEP
73 char *strsep (char **stringp, const char *delim);
74 #endif
90 char *APar_extract_UTC(uint64_t total_secs);
91 uint32_t APar_get_mpeg4_time();
92 void APar_StandardTime(char *&formed_time);
7593
76 char* APar_extract_UTC(uint64_t total_secs);
77 uint32_t APar_get_mpeg4_time();
78 void APar_StandardTime(char* &formed_time);
79
80 wchar_t* Convert_multibyteUTF16_to_wchar(char* input_unicode, size_t glyph_length, bool skip_BOM);
81 unsigned char* Convert_multibyteUTF16_to_UTF8(char* input_utf8, size_t glyph_length, size_t byte_count);
82 wchar_t* Convert_multibyteUTF8_to_wchar(const char* input_utf8);
83 uint32_t findstringNULLterm(char* in_string, uint8_t encodingFlag, uint32_t max_len);
84 uint32_t skipNULLterm(char* in_string, uint8_t encodingFlag, uint32_t max_len);
85
94 wchar_t *Convert_multibyteUTF16_to_wchar(char *input_unicode,
95 size_t glyph_length,
96 bool skip_BOM);
97 unsigned char *Convert_multibyteUTF16_to_UTF8(char *input_utf8,
98 size_t glyph_length,
99 size_t byte_count);
100 wchar_t *Convert_multibyteUTF8_to_wchar(const char *input_utf8);
101 uint32_t
102 findstringNULLterm(char *in_string, uint8_t encodingFlag, uint32_t max_len);
103 uint32_t skipNULLterm(char *in_string, uint8_t encodingFlag, uint32_t max_len);
86104
87105 uint16_t UInt16FromBigEndian(const char *string);
88106 uint32_t UInt32FromBigEndian(const char *string);
89107 uint64_t UInt64FromBigEndian(const char *string);
90 void UInt16_TO_String2(uint16_t snum, char* data);
91 void UInt32_TO_String4(uint32_t lnum, char* data);
92 void UInt64_TO_String8(uint64_t ullnum, char* data);
108 void UInt16_TO_String2(uint16_t snum, char *data);
109 void UInt32_TO_String4(uint32_t lnum, char *data);
110 void UInt64_TO_String8(uint64_t ullnum, char *data);
93111
94112 uint32_t float_to_16x16bit_fixed_point(double floating_val);
95113 double fixed_point_16x16bit_to_double(uint32_t fixed_point);
96114
97 uint32_t widechar_len(char* instring, uint32_t _bytes_);
115 uint32_t widechar_len(char *instring, uint32_t _bytes_);
98116
99 bool APar_assert(bool expression, int error_msg, const char* supplemental_info);
117 bool APar_assert(bool expression, int error_msg, const char *supplemental_info);
100118
101119 unsigned long xor4096i();
11 /*
22 AtomicParsley - uuid.cpp
33
4 AtomicParsley is GPL software; you can freely distribute,
4 AtomicParsley is GPL software; you can freely distribute,
55 redistribute, modify & use under the terms of the GNU General
66 Public License; either version 2 or its successor.
77
99 any warranty; without the implied warranty of merchantability
1010 or fitness for either an expressed or implied particular purpose.
1111
12 Please see the included GNU General Public License (GPL) for
12 Please see the included GNU General Public License (GPL) for
1313 your rights and further details; see the file COPYING. If you
1414 cannot, write to the Free Software Foundation, 59 Temple Place
1515 Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
1616
17 Copyright ©2006-2007 puck_lock
17 Copyright (C) 2006-2007 puck_lock
1818 with contributions from others; see the CREDITS file
1919 */
2020 //==================================================================//
2222 //==================================================================//
2323 /*
2424 Much of AP_Create_UUID_ver5_sha1_name was derived from
25 http://www.ietf.org/rfc/rfc4122.txt
26 which I don't believe conflicts with or restricts the GPL.
27 And this page:
28 http://home.famkruithof.net/guid-uuid-namebased.html
29 tells me I'm not on crack when I try to calculate the uuids myself.
30
31 Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
32 Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
33 Digital Equipment Corporation, Maynard, Mass.
34 Copyright (c) 1998 Microsoft.
35 To anyone who acknowledges that this file is provided "AS IS"
36 without any express or implied warranty: permission to use, copy,
37 modify, and distribute this file for any purpose is hereby
38 granted without fee, provided that the above copyright notices and
39 this notice appears in all source code copies, and that none of
40 the names of Open Software Foundation, Inc., Hewlett-Packard
41 Company, Microsoft, or Digital Equipment Corporation be used in
42 advertising or publicity pertaining to distribution of the software
43 without specific, written prior permission. Neither Open Software
44 Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital
45 Equipment Corporation makes any representations about the
46 suitability of this software for any purpose.
25 http://www.ietf.org/rfc/rfc4122.txt
26 which I don't believe conflicts with or restricts the GPL.
27 And this page:
28 http://home.famkruithof.net/guid-uuid-namebased.html
29 tells me I'm not on crack when I try to calculate the uuids
30 myself.
31
32 Copyright (c) 1990- 1993, 1996 Open Software Foundation,
33 Inc. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital
34 Equipment Corporation, Maynard, Mass. Copyright (c) 1998 Microsoft. To anyone
35 who acknowledges that this file is provided "AS IS" without any express or
36 implied warranty: permission to use, copy, modify, and distribute this file
37 for any purpose is hereby granted without fee, provided that the above
38 copyright notices and this notice appears in all source code copies, and that
39 none of the names of Open Software Foundation, Inc., Hewlett-Packard Company,
40 Microsoft, or Digital Equipment Corporation be used in advertising or
41 publicity pertaining to distribution of the software without specific,
42 written prior permission. Neither Open Software Foundation, Inc.,
43 Hewlett-Packard Company, Microsoft, nor Digital Equipment Corporation makes
44 any representations about the suitability of this software for any purpose.
4745 */
4846 //==================================================================//
4947
5351 print_hash
5452 hash - the string array of the sha1 hash
5553
56 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
54 prints out the hex representation of the 16 byte hash - this
55 relates to sha1, but its here to keep the sha1 files as close to original as
56 possible
5757 ----------------------*/
5858 void print_hash(char hash[]) {
59 for (int i=0; i < 20; i++) {
60 fprintf(stdout,"%02x", (uint8_t)hash[i]);
61 }
62 fprintf(stdout, "\n");
63 return;
59 for (int i = 0; i < 20; i++) {
60 fprintf(stdout, "%02x", (uint8_t)hash[i]);
61 }
62 fprintf(stdout, "\n");
63 return;
6464 }
6565
6666 /*----------------------
6767 Swap_Char
6868 in_str - the string to have the swap operation performed on
69 str_len - the amount of bytes to swap in the string
70
71 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
72 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
73 the decrementing end pointer to the temp string byte.
69 str_len - the amount of bytes to swap in the string
70
71 Make a pointer to the start & end of the string, as well as a
72 temporary string to hold the swapped byte. As the start increments up, the end
73 decrements down. Copy the byte at each advancing start position. Copy the byte
74 of the diminishing end string into the start byte, then advance the start byte.
75 Finaly, set each byte of the decrementing end pointer to the temp string byte.
7476 ----------------------*/
7577 void Swap_Char(char *in_str, uint8_t str_len) {
76 char *start_str, *end_str, temp_str;
77
78 start_str= in_str;
79 end_str=start_str+ str_len;
80 while ( start_str<--end_str ) {
81 temp_str=*start_str;
82 *start_str++=*end_str;
83 *end_str=temp_str;
84 }
85 return;
78 char *start_str, *end_str, temp_str;
79
80 start_str = in_str;
81 end_str = start_str + str_len;
82 while (start_str < --end_str) {
83 temp_str = *start_str;
84 *start_str++ = *end_str;
85 *end_str = temp_str;
86 }
87 return;
8688 }
8789
8890 /*----------------------
8991 APar_endian_uuid_bin_str_conversion
9092 raw_uuid - a binary string representation of a uuid
9193
92 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.
93 ----------------------*/
94 void APar_endian_uuid_bin_str_conversion(char* raw_uuid) {
95 #if defined (__ppc__) || defined (__ppc64__)
96 return; //we are *naturally* network byte ordered - simplicity
94 As a string representation of a uuid, there is a 32-bit & 2
95 16-bit numbers in the uuid. These members need to be swapped on big endian
96 systems.
97 ----------------------*/
98 void APar_endian_uuid_bin_str_conversion(char *raw_uuid) {
99 #if defined(__ppc__) || defined(__ppc64__)
100 return; // we are *naturally* network byte ordered - simplicity
97101 #else
98 Swap_Char(raw_uuid, 4);
99 Swap_Char(raw_uuid+4, 2);
100 Swap_Char(raw_uuid+4+2, 2);
101 return;
102 Swap_Char(raw_uuid, 4);
103 Swap_Char(raw_uuid + 4, 2);
104 Swap_Char(raw_uuid + 4 + 2, 2);
105 return;
102106 #endif
103107 }
104108
106110 APar_print_uuid
107111 uuid - a uuid structure containing the uuid
108112
109 Print out a full string representation of a uuid
110 ----------------------*/
111 void APar_print_uuid(ap_uuid_t* uuid, bool new_line) {
112 fprintf(stdout, "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
113 uuid->time_low,
114 uuid->time_mid,
115 uuid->time_hi_and_version,
116 uuid->clock_seq_hi_and_reserved,
117 uuid->clock_seq_low,
118 uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], uuid->node[4], uuid->node[5]);
119 if (new_line) fprintf(stdout, "\n");
120 return;
113 Print out a full string representation of a uuid
114 ----------------------*/
115 void APar_print_uuid(ap_uuid_t *uuid, bool new_line) {
116 fprintf(stdout,
117 "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
118 uuid->time_low,
119 uuid->time_mid,
120 uuid->time_hi_and_version,
121 uuid->clock_seq_hi_and_reserved,
122 uuid->clock_seq_low,
123 uuid->node[0],
124 uuid->node[1],
125 uuid->node[2],
126 uuid->node[3],
127 uuid->node[4],
128 uuid->node[5]);
129 if (new_line)
130 fprintf(stdout, "\n");
131 return;
121132 }
122133
123134 /*----------------------
124135 APar_sprintf_uuid
125136 uuid - a uuid structure containing the uuid
126 destination - the end result uuid will be placed here
127
128 Put a binary representation of a uuid to a human-readable ordered uuid string
129 ----------------------*/
130 void APar_sprintf_uuid(ap_uuid_t* uuid, char* destination) {
131 sprintf(destination, "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
132 uuid->time_low,
133 uuid->time_mid,
134 uuid->time_hi_and_version,
135 uuid->clock_seq_hi_and_reserved,
136 uuid->clock_seq_low,
137 uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], uuid->node[4], uuid->node[5]);
138 return;
137 destination - the end result uuid will be placed here
138
139 Put a binary representation of a uuid to a human-readable
140 ordered uuid string
141 ----------------------*/
142 void APar_sprintf_uuid(ap_uuid_t *uuid, char *destination) {
143 sprintf(destination,
144 "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
145 uuid->time_low,
146 uuid->time_mid,
147 uuid->time_hi_and_version,
148 uuid->clock_seq_hi_and_reserved,
149 uuid->clock_seq_low,
150 uuid->node[0],
151 uuid->node[1],
152 uuid->node[2],
153 uuid->node[3],
154 uuid->node[4],
155 uuid->node[5]);
156 return;
139157 }
140158
141159 /*----------------------
142160 APar_uuid_scanf
143 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
144 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
145
146 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
147 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.
148 ----------------------*/
149 uint8_t APar_uuid_scanf(char* in_formed_uuid, const char* raw_uuid_in) {
150 char *uuid_str, *end_uuid_str, *uuid_byte;
151 uint8_t uuid_pos, uuid_len;
152 uint8_t keeprap = 0;
153 #if defined (_WIN32) && !defined (__CYGWIN__)
154 char *raw_uuid = _strdup(raw_uuid_in);
161 in_formed_uuid - pointer to a string (or a place in a string) where to place a
162 binary (hex representation) string uuid of 16 bytes raw_uuid - the string that
163 contains a string representation of a uuid (from cli input for example). This
164 string isn't 16 bytes - its 36
165
166 Skip past a hyphen, make any upper case characters lower (ahh,
167 that hex 'Q') to do a manual scanf on the string. Add its hex representation as
168 a number for 1/2 of the bits (a single byte is 2 hex characters), shift it over
169 to the upper bits, and repeat adding the lower bits. Repeat until done.
170 ----------------------*/
171 uint8_t APar_uuid_scanf(char *in_formed_uuid, const char *raw_uuid_in) {
172 char *uuid_str, *end_uuid_str, *uuid_byte;
173 uint8_t uuid_pos, uuid_len;
174 uint8_t keeprap = 0;
175 #if defined(_WIN32) && !defined(__CYGWIN__)
176 char *raw_uuid = _strdup(raw_uuid_in);
155177 #else
156 char *raw_uuid = strdup(raw_uuid_in);
178 char *raw_uuid = strdup(raw_uuid_in);
157179 #endif
158180
159 uuid_len = strlen(raw_uuid); //it will be like "55534d54-21d2-4fce-bb88-695cfac9c740"
160 uuid_str = raw_uuid;
161 uuid_pos = 0;
162 end_uuid_str = uuid_str+uuid_len;
163
164 while (uuid_str < end_uuid_str) {
165 uuid_byte = &in_formed_uuid[uuid_pos];
166 if (uuid_str[0] == '-') uuid_str++;
167 if (uuid_str[0] >= 'A' && uuid_str[0] <= 90) uuid_str[0] +=32;
168 if (uuid_str[1] >= 'A' && uuid_str[1] <= 90) uuid_str[0] +=32;
169
170 for (int i = 0; i <= 1; i++) {
171 switch(uuid_str[i]) {
172 case '0' : {
173 keeprap = 0;
174 break;
175 }
176 case '1' : {
177 keeprap = 1;
178 break;
179 }
180 case '2' : {
181 keeprap = 2;
182 break;
183 }
184 case '3' : {
185 keeprap = 3;
186 break;
187 }
188 case '4' : {
189 keeprap = 4;
190 break;
191 }
192 case '5' : {
193 keeprap = 5;
194 break;
195 }
196 case '6' : {
197 keeprap = 6;
198 break;
199 }
200 case '7' : {
201 keeprap = 7;
202 break;
203 }
204 case '8' : {
205 keeprap = 8;
206 break;
207 }
208 case '9' : {
209 keeprap = 9;
210 break;
211 }
212 case 'a' : {
213 keeprap = 10;
214 break;
215 }
216 case 'b' : {
217 keeprap = 11;
218 break;
219 }
220 case 'c' : {
221 keeprap = 12;
222 break;
223 }
224 case 'd' : {
225 keeprap = 13;
226 break;
227 }
228 case 'e' : {
229 keeprap = 14;
230 break;
231 }
232 case 'f' : {
233 keeprap = 15;
234 break;
235 }
236 }
237
238 if (i == 0) {
239 *uuid_byte = keeprap << 4;
240 } else {
241 *uuid_byte |= keeprap; //(keeprap & 0xF0);
242 }
243 }
244 uuid_str+=2;
245 uuid_pos++;
246 }
247 APar_endian_uuid_bin_str_conversion(in_formed_uuid);
248 free(raw_uuid);
249 return uuid_pos;
181 uuid_len = strlen(
182 raw_uuid); // it will be like "55534d54-21d2-4fce-bb88-695cfac9c740"
183 uuid_str = raw_uuid;
184 uuid_pos = 0;
185 end_uuid_str = uuid_str + uuid_len;
186
187 while (uuid_str < end_uuid_str) {
188 uuid_byte = &in_formed_uuid[uuid_pos];
189 if (uuid_str[0] == '-')
190 uuid_str++;
191 if (uuid_str[0] >= 'A' && uuid_str[0] <= 90)
192 uuid_str[0] += 32;
193 if (uuid_str[1] >= 'A' && uuid_str[1] <= 90)
194 uuid_str[0] += 32;
195
196 for (int i = 0; i <= 1; i++) {
197 switch (uuid_str[i]) {
198 case '0': {
199 keeprap = 0;
200 break;
201 }
202 case '1': {
203 keeprap = 1;
204 break;
205 }
206 case '2': {
207 keeprap = 2;
208 break;
209 }
210 case '3': {
211 keeprap = 3;
212 break;
213 }
214 case '4': {
215 keeprap = 4;
216 break;
217 }
218 case '5': {
219 keeprap = 5;
220 break;
221 }
222 case '6': {
223 keeprap = 6;
224 break;
225 }
226 case '7': {
227 keeprap = 7;
228 break;
229 }
230 case '8': {
231 keeprap = 8;
232 break;
233 }
234 case '9': {
235 keeprap = 9;
236 break;
237 }
238 case 'a': {
239 keeprap = 10;
240 break;
241 }
242 case 'b': {
243 keeprap = 11;
244 break;
245 }
246 case 'c': {
247 keeprap = 12;
248 break;
249 }
250 case 'd': {
251 keeprap = 13;
252 break;
253 }
254 case 'e': {
255 keeprap = 14;
256 break;
257 }
258 case 'f': {
259 keeprap = 15;
260 break;
261 }
262 }
263
264 if (i == 0) {
265 *uuid_byte = keeprap << 4;
266 } else {
267 *uuid_byte |= keeprap; //(keeprap & 0xF0);
268 }
269 }
270 uuid_str += 2;
271 uuid_pos++;
272 }
273 APar_endian_uuid_bin_str_conversion(in_formed_uuid);
274 free(raw_uuid);
275 return uuid_pos;
250276 }
251277
252278 /*----------------------
253279 APar_extract_uuid_version
254280 uuid - a uuid structure containing the uuid
255 binary_uuid_str - a binary string rep of a uuid (without dashes, just the hex)
256
257 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
258 ----------------------*/
259 uint8_t APar_extract_uuid_version(ap_uuid_t* uuid, char* binary_uuid_str) {
260 uint8_t uuid_ver = 0;
261 if (binary_uuid_str != NULL) {
262 uuid_ver = (binary_uuid_str[6] >> 4);
263 } else if (uuid != NULL) {
264 uuid_ver = (uuid->time_hi_and_version >> 12);
265 }
266 return uuid_ver;
281 binary_uuid_str - a binary string rep of a uuid (without dashes, just
282 the hex)
283
284 Test the 6th byte in a str and push the bits to get the version
285 or take the 3rd member of a uuid (uint16_t) and shift bits by 12
286 ----------------------*/
287 uint8_t APar_extract_uuid_version(ap_uuid_t *uuid, char *binary_uuid_str) {
288 uint8_t uuid_ver = 0;
289 if (binary_uuid_str != NULL) {
290 uuid_ver = (binary_uuid_str[6] >> 4);
291 } else if (uuid != NULL) {
292 uuid_ver = (uuid->time_hi_and_version >> 12);
293 }
294 return uuid_ver;
267295 }
268296
269297 /*----------------------
270298 AP_Create_UUID_ver5_sha1_name
271299 uuid - pointer to the final version 5 sha1 hash bashed uuid
272 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
273 name - this is the name portion used to make a v5 sha1 hash
274 namelen - length of name (currently a strlen() value)
275
276 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
277 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.
278 The final hash is then copied into the out_uuid, and the endian members of the ap_uuid_t are swapped to endian ordering.
279 ----------------------*/
280 void AP_Create_UUID_ver5_sha1_name(ap_uuid_t* out_uuid, ap_uuid_t desired_namespace, const char *name, int namelen) {
281 sha1_ctx sha_state;
282 char hash[20];
283 ap_uuid_t networkorderd_namespace;
284
285 //swap the endian members of uuid to network byte order for hash uniformity across platforms (the NULL or AP.sf.net uuid)
286 networkorderd_namespace = desired_namespace;
287 networkorderd_namespace.time_low = SWAP32(networkorderd_namespace.time_low);
288 networkorderd_namespace.time_mid = SWAP16(networkorderd_namespace.time_mid);
289 networkorderd_namespace.time_hi_and_version = SWAP16(networkorderd_namespace.time_hi_and_version);
290
291 //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)
292
293 sha1_init_ctx(&sha_state);
294 sha1_process_bytes( (char*)&networkorderd_namespace, sizeof networkorderd_namespace, &sha_state);
295 sha1_process_bytes(name, namelen, &sha_state);
296 sha1_finish_ctx(&sha_state, hash);
297
298 // quasi uuid sha1hash is network byte ordered. swap the endian members of the uuid (uint32_t & uint16_t) to local byte order
299 // this creates additional requirements later that have to be APar_endian_uuid_bin_str_conversion() swapped, but leave this for now for coherence
300 memcpy(out_uuid, hash, sizeof *out_uuid);
301 out_uuid->time_low = SWAP32(out_uuid->time_low);
302 out_uuid->time_mid = SWAP16(out_uuid->time_mid);
303 out_uuid->time_hi_and_version = SWAP16(out_uuid->time_hi_and_version);
304
305 out_uuid->time_hi_and_version &= 0x0FFF; //mask hash octes 6&7
306 out_uuid->time_hi_and_version |= (5 << 12); //set bits 12-15 to the version (5 for sha1 namespace/name hash uuids)
307 out_uuid->clock_seq_hi_and_reserved &= 0x3F; //mask hash octet 8
308 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.
309 return;
300 desired_namespace - the input uuid used as a basis for the hash; a ver5
301 uuid is a namespace/name uuid. This is the namespace portion name - this is the
302 name portion used to make a v5 sha1 hash namelen - length of name (currently a
303 strlen() value)
304
305 This will create a version 5 sha1 based uuid of a name in a
306 namespace. The desired_namespace has its endian members swapped to newtwork byte
307 ordering. The sha1 hash algorithm is fed the reordered netord_namespace uuid to
308 create a hash; the name is then added to the hash to create a hash of the name
309 in the namespace. The final hash is then copied into the out_uuid, and the
310 endian members of the ap_uuid_t are swapped to endian ordering.
311 ----------------------*/
312 void AP_Create_UUID_ver5_sha1_name(ap_uuid_t *out_uuid,
313 ap_uuid_t desired_namespace,
314 const char *name,
315 int namelen) {
316 sha1_ctx sha_state;
317 char hash[20];
318 ap_uuid_t networkorderd_namespace;
319
320 // swap the endian members of uuid to network byte order for hash uniformity
321 // across platforms (the NULL or AP.sf.net uuid)
322 networkorderd_namespace = desired_namespace;
323 networkorderd_namespace.time_low = SWAP32(networkorderd_namespace.time_low);
324 networkorderd_namespace.time_mid = SWAP16(networkorderd_namespace.time_mid);
325 networkorderd_namespace.time_hi_and_version =
326 SWAP16(networkorderd_namespace.time_hi_and_version);
327
328 // make a hash of the input desired_namespace (as netord_ns); add the name
329 // (the AP.sf.net namespace as string or the atom name)
330
331 sha1_init_ctx(&sha_state);
332 sha1_process_bytes((char *)&networkorderd_namespace,
333 sizeof networkorderd_namespace,
334 &sha_state);
335 sha1_process_bytes(name, namelen, &sha_state);
336 sha1_finish_ctx(&sha_state, hash);
337
338 // quasi uuid sha1hash is network byte ordered. swap the endian members of the
339 // uuid (uint32_t & uint16_t) to local byte order this creates additional
340 // requirements later that have to be APar_endian_uuid_bin_str_conversion()
341 // swapped, but leave this for now for coherence
342 memcpy(out_uuid, hash, sizeof *out_uuid);
343 out_uuid->time_low = SWAP32(out_uuid->time_low);
344 out_uuid->time_mid = SWAP16(out_uuid->time_mid);
345 out_uuid->time_hi_and_version = SWAP16(out_uuid->time_hi_and_version);
346
347 out_uuid->time_hi_and_version &= 0x0FFF; // mask hash octes 6&7
348 out_uuid->time_hi_and_version |=
349 (5 << 12); // set bits 12-15 to the version (5 for sha1 namespace/name
350 // hash uuids)
351 out_uuid->clock_seq_hi_and_reserved &= 0x3F; // mask hash octet 8
352 out_uuid->clock_seq_hi_and_reserved |=
353 0x80; // Set the two most significant bits (bits 6 and 7) of the
354 // clock_seq_hi_and_reserved to zero and one, respectively.
355 return;
310356 }
311357
312358 /*----------------------
313359 AP_Create_UUID_ver3_random
314360 out_uuid - pointer to the final version 3 randomly generated uuid
315361
316 Use a high quality random number generator (still requiring a seed) to generate a random uuid.
317 In 2.5 million creations, all have been unique (at least on Mac OS X with sranddev providing the initial seed).
318 ----------------------*/
319 void AP_Create_UUID_ver3_random(ap_uuid_t* out_uuid) {
320 uint32_t rand1 = 0;
321
322 out_uuid->time_low = xor4096i();
323 rand1 = xor4096i();
324 out_uuid->time_mid = (rand1 >> 16) & 0xFFFF;
325 out_uuid->node[0] = (rand1 >> 8) & 0xFF;
326 out_uuid->node[1] = (rand1 >> 0) & 0xFF;
327 rand1 = xor4096i();
328 out_uuid->node[2] = (rand1 >> 24) & 0xFF;
329 out_uuid->node[3] = (rand1 >> 16) & 0xFF;
330 out_uuid->node[4] = (rand1 >> 8) & 0xFF;
331 out_uuid->node[5] = (rand1 >> 0) & 0xFF;
332 rand1 = xor4096i();
333 out_uuid->time_hi_and_version = (rand1 >> 16) & 0xFFFF;
334 out_uuid->clock_seq_low = (rand1 >> 8) & 0xFF;
335 out_uuid->clock_seq_hi_and_reserved = (rand1 >> 0) & 0x3F;
336 out_uuid->clock_seq_hi_and_reserved |= 0x40; //bits 6 & 7 must be 0 & 1 respectively
337
338 out_uuid->time_hi_and_version &= 0x0FFF;
339 out_uuid->time_hi_and_version |= (3 << 12); //set bits 12-15 to the version (3 for peusdo-random/random)
340 return;
362 Use a high quality random number generator (still requiring a
363 seed) to generate a random uuid. In 2.5 million creations, all have been unique
364 (at least on Mac OS X with sranddev providing the initial seed).
365 ----------------------*/
366 void AP_Create_UUID_ver3_random(ap_uuid_t *out_uuid) {
367 uint32_t rand1 = 0;
368
369 out_uuid->time_low = xor4096i();
370 rand1 = xor4096i();
371 out_uuid->time_mid = (rand1 >> 16) & 0xFFFF;
372 out_uuid->node[0] = (rand1 >> 8) & 0xFF;
373 out_uuid->node[1] = (rand1 >> 0) & 0xFF;
374 rand1 = xor4096i();
375 out_uuid->node[2] = (rand1 >> 24) & 0xFF;
376 out_uuid->node[3] = (rand1 >> 16) & 0xFF;
377 out_uuid->node[4] = (rand1 >> 8) & 0xFF;
378 out_uuid->node[5] = (rand1 >> 0) & 0xFF;
379 rand1 = xor4096i();
380 out_uuid->time_hi_and_version = (rand1 >> 16) & 0xFFFF;
381 out_uuid->clock_seq_low = (rand1 >> 8) & 0xFF;
382 out_uuid->clock_seq_hi_and_reserved = (rand1 >> 0) & 0x3F;
383 out_uuid->clock_seq_hi_and_reserved |=
384 0x40; // bits 6 & 7 must be 0 & 1 respectively
385
386 out_uuid->time_hi_and_version &= 0x0FFF;
387 out_uuid->time_hi_and_version |=
388 (3 << 12); // set bits 12-15 to the version (3 for peusdo-random/random)
389 return;
341390 }
342391
343392 /*----------------------
344393 APar_generate_uuid_from_atomname
345394 atom_name - the 4 character atom name to create a uuid for
346 uuid_binary_str - the destination for the created uuid (as a hex string)
347
348 This will create a 16-byte universal unique identifier for any atom name in the AtomicParsley namespace.
349 1. Make a namespace for all uuids to derive from (here its AP.sf.net)
350 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
351 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.
352 4. copy the uuid structure into a binary string representation
353 ----------------------*/
354 void APar_generate_uuid_from_atomname(char* atom_name, char* uuid_binary_str) {
355
356 ap_uuid_t blank_namespace = { 0x00000000, 0x0000, 0x0000,
357 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} };
358 ap_uuid_t APar_namespace_uuid;
359 ap_uuid_t AP_atom_uuid;
360
361 AP_Create_UUID_ver5_sha1_name(&APar_namespace_uuid, blank_namespace, "AtomicParsley.sf.net", 20);
362 AP_Create_UUID_ver5_sha1_name(&AP_atom_uuid, APar_namespace_uuid, atom_name, 4);
363
364 memset(uuid_binary_str, 0, 20);
365 memcpy(uuid_binary_str, &AP_atom_uuid, sizeof(AP_atom_uuid) );
366
367 return;
368 }
369
370 void APar_generate_random_uuid(char* uuid_binary_str) {
371 ap_uuid_t rand_uuid = { 0x00000000, 0x0000, 0x0000,
372 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }};
373 AP_Create_UUID_ver3_random(&rand_uuid);
374 memcpy(uuid_binary_str, &rand_uuid, sizeof(rand_uuid) );
375 return;
395 uuid_binary_str - the destination for the created uuid (as a hex string)
396
397 This will create a 16-byte universal unique identifier for any
398 atom name in the AtomicParsley namespace.
399 1. Make a namespace for all uuids to derive from (here its
400 AP.sf.net)
401 2. Make a sha1 hash of the namespace string; use that hash as
402 the basis for a v5 uuid into a NULL/blank uuid to create an AP namespace uuid
403 3. Make a sha2 hash of the atom_name string; use that hash as
404 the basis for a v5 uuid into an AtomicParsley namespace uuid to create the final
405 uuid.
406 4. copy the uuid structure into a binary string representation
407 ----------------------*/
408 void APar_generate_uuid_from_atomname(char *atom_name, char *uuid_binary_str) {
409
410 ap_uuid_t blank_namespace = {0x00000000,
411 0x0000,
412 0x0000,
413 0x00,
414 0x00,
415 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
416 ap_uuid_t APar_namespace_uuid;
417 ap_uuid_t AP_atom_uuid;
418
419 AP_Create_UUID_ver5_sha1_name(
420 &APar_namespace_uuid, blank_namespace, "AtomicParsley.sf.net", 20);
421 AP_Create_UUID_ver5_sha1_name(
422 &AP_atom_uuid, APar_namespace_uuid, atom_name, 4);
423
424 memset(uuid_binary_str, 0, 20);
425 memcpy(uuid_binary_str, &AP_atom_uuid, sizeof(AP_atom_uuid));
426
427 return;
428 }
429
430 void APar_generate_random_uuid(char *uuid_binary_str) {
431 ap_uuid_t rand_uuid = {0x00000000,
432 0x0000,
433 0x0000,
434 0x00,
435 0x00,
436 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
437 AP_Create_UUID_ver3_random(&rand_uuid);
438 memcpy(uuid_binary_str, &rand_uuid, sizeof(rand_uuid));
439 return;
376440 }
377441
378442 void APar_generate_test_uuid() {
379 ap_uuid_t blank_ns = { 0x00000000, 0x0000, 0x0000,
380 0x00, 0x00,
381 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }};
382 ap_uuid_t APar_ns_uuid;
383 ap_uuid_t APar_test_uuid;
384
385 AP_Create_UUID_ver5_sha1_name(&APar_ns_uuid, blank_ns, "AtomicParsley.sf.net", 20);
386 APar_print_uuid(&APar_ns_uuid); //should be aa80eaf3-1f72-5575-9faa-de9388dc2a90
387 fprintf(stdout, "uuid for 'cprt' in AP namespace: ");
388 AP_Create_UUID_ver5_sha1_name(&APar_test_uuid, APar_ns_uuid, "cprt", 4);
389 APar_print_uuid(&APar_test_uuid); //'cprt' should be 4bd39a57-e2c8-5655-a4fb-7a19620ef151
390 //uuid_t domain_ns_uuid = { 0x6ba7b810, 0x9dad, 0x11d1,
391 // 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; // 6ba7b810-9dad-11d1-80b4-00c04fd430c8 a blank representation of a blank domain
392 return;
393 }
443 ap_uuid_t blank_ns = {0x00000000,
444 0x0000,
445 0x0000,
446 0x00,
447 0x00,
448 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
449 ap_uuid_t APar_ns_uuid;
450 ap_uuid_t APar_test_uuid;
451
452 AP_Create_UUID_ver5_sha1_name(
453 &APar_ns_uuid, blank_ns, "AtomicParsley.sf.net", 20);
454 APar_print_uuid(
455 &APar_ns_uuid); // should be aa80eaf3-1f72-5575-9faa-de9388dc2a90
456 fprintf(stdout, "uuid for 'cprt' in AP namespace: ");
457 AP_Create_UUID_ver5_sha1_name(&APar_test_uuid, APar_ns_uuid, "cprt", 4);
458 APar_print_uuid(
459 &APar_test_uuid); //'cprt' should be 4bd39a57-e2c8-5655-a4fb-7a19620ef151
460 // uuid_t domain_ns_uuid = { 0x6ba7b810, 0x9dad, 0x11d1,
461 // 0x80,
462 // 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; //
463 // 6ba7b810-9dad-11d1-80b4-00c04fd430c8 a blank representation of a blank
464 // domain
465 return;
466 }
Binary diff not shown
0 #!/bin/bash
1 set -xe
2 ./AtomicParsley ./tests/issue-32.mp4 -T 1 -t +
0 #!/bin/bash
1 clang-format -i src/*.cpp src/*.h src/*.mm
0 #!/bin/bash
1 TAGNAME=$(git "show" "-s" "--format=%cd.%h" "--date=format:%Y%m%d.%H%M%S")
2 git tag $TAGNAME