]> git.wh0rd.org - home.git/blame - .bin/backup-dvd
gentoo: att: support multiple files
[home.git] / .bin / backup-dvd
CommitLineData
4108a92c
MF
1#!/bin/bash
2
d6d9f2ac 3err() { printf 'error: %b\n' "$*" 1>&2; exit 1; }
4108a92c
MF
4usage() {
5 cat <<-EOF
d6d9f2ac 6 backup-dvd [options] [commands]
4108a92c
MF
7
8 Options:
9 -i <input dev> Defaults to ${dev}
10 -o <output dir> Defaults to ${out}
2b2f7f17 11 -n <volume name> Defaults to volume on disk
d6d9f2ac
MF
12
13 Commands:
14 --backup Back up the disc
15 --modify Tweak runtime settings (needs -n)
16 --mkiso Create an iso (needs -n)
17
18 If no commands are specified, then all are run.
4108a92c
MF
19 EOF
20 exit ${1:-0}
21}
22
23dev=/dev/cdrom
24out=${PWD}
2b2f7f17 25vol=
4108a92c 26
d6d9f2ac
MF
27unset doit_{backup,modify,mkiso}
28
29eval set -- `getopt -l backup,modify,mkiso -- hi:n:o: "$@"`
4108a92c
MF
30while [[ -n $1 ]] ; do
31 case $1 in
32 -h) usage;;
33 -i) dev=$2; shift 2;;
34 -o) out=$2; shift 2;;
2b2f7f17 35 -n) vol=$2; shift 2;;
d6d9f2ac
MF
36
37 --backup) doit_backup=true; shift;;
38 --modify) doit_modify=true; shift;;
39 --mkiso) doit_mkiso=true; shift;;
40
4108a92c
MF
41 --) shift; break;;
42 -*) usage 1;;
43 *) usage 2; break;;
44 esac
45done
46
d6d9f2ac
MF
47doit_all() {
48 : ${doit_backup:=$1}
49 : ${doit_modify:=$1}
50 : ${doit_mkiso:=$1}
51}
52if [[ -z ${doit_backup}${doit_modify}${doit_mkiso} ]] ; then
53 doit_all true
54else
55 doit_all false
56fi
57
4108a92c
MF
58# iso-info mkisofs
59Application= # -A
60Preparer= # -p
61Publisher= # -publisher
62System= # -sysid
2b2f7f17 63Volume=${vol} # -V
4108a92c
MF
64Volume_Set= # -volset
65
d6d9f2ac
MF
66if ${doit_backup} ; then
67 if [[ -z ${Volume} ]] ; then
68 info=$(iso-info ${dev}) || exit 1
4108a92c 69
d6d9f2ac
MF
70 eval $(echo "${info}" | awk -F: '
71 (NF > 1 && $1 !~ /image/) {
72 sub(/ *$/, "", $1);
73 sub(/ /, "_", $1);
74 sub(/^ */, "", $2);
75 print $1 "=\"" $2 "\"";
76 }')
77 fi
4108a92c 78
d6d9f2ac
MF
79 if [[ -z ${Volume} ]] ; then
80 echo "Unable to parse Volume out of ISO"
81 iso-info ${dev}
82 fi
4108a92c 83
d6d9f2ac
MF
84 for v in Application Preparer Publisher System Volume Volume_Set ; do
85 echo "${v}='${!v}'"
86 done > "${out}/.${Volume}.vars.sh"
87else
88 [[ -z ${Volume} ]] && err "Need to specify -n with --modify/--mkiso"
89 . "${out}/.${Volume}.vars.sh" || exit 1
90fi
4108a92c
MF
91
92e() {
93 for a ; do
94 [[ ${a} == *" "* || ${#a} == 0 ]] && fmt='"%s"' || fmt='%s'
95 printf "${fmt} " "${a}"
96 done
97 echo
98 "$@"
99}
d6d9f2ac
MF
100
101#
102# Backup the disc.
103#
15a28e34
MF
104opts=(
105 -i ${dev}
106 -o "${out}"
107 -n "${Volume}"
108)
d6d9f2ac
MF
109backup_dvd() {
110 echo "Backing up: ${Volume}"
111 if ! e dvdbackup -M "${opts[@]}" ; then
112 e dvdbackup -F "${opts[@]}" || exit 1
113 fi
114}
115
116#
117# Modify some of the runtime settings.
118#
119bytes_get() {
120 local pfx='0x' sep
121 while [[ $# -gt 3 ]]; do
122 case $1 in
123 "-p") pfx=$2;;
124 "-s") sep=$2;;
125 *) break;;
126 esac
127 shift 2
128 done
129 printf '%b' "${pfx}"
130
131 local file=$1 off=$2 num=${3:-1}
132 hexdump -v -n ${num} -s $((${off})) -e "1/1 \"%02x${sep}\"" "${file}"
133}
134bytes_set() {
135 local file=$1 off=$2
136 shift 2
137 (for b; do printf "\x${b#0x}"; done) | \
138 dd of="${file}" \
139 bs=1 seek=$((${off})) \
140 conv=notrunc status=none
141}
142dvdsec() {
143 # DVDs have sectors of 2048 bytes (2^11).
144 local file=$1 off=$2 num=${3:-4}
145 echo $(( $(bytes_get "${file}" "${off}" "${num}") << 11 ))
146}
147
148_check_ifo() {
149 # Back up the .IFO files before we edit them.
150 local ifo=$1 magic=$2
151 [[ -e ${ifo} ]] || return 1
152
153 if [[ $(bytes_get "${ifo}" 0 12) != "0x${magic}" ]] ; then
154 echo "not a VMG IFO file: ${ifo}"
155 return 1
156 fi
157 if [[ ! -e ${ifo}.bak ]] ; then
158 cp -a "${ifo}" "${ifo}.bak"
159 fi
160
161 return 0
162}
163
164check_changes() {
165 [[ -e ifodump ]] || return 0
166
167 local file=$1
168 ./ifodump -f "${file}.bak" > "${file}.bak.dmp"
169 ./ifodump -f "${file}" > "${file}.dmp"
170 diff -u "${file}.bak.dmp" "${file}.dmp" | sed -e 1d -e 2d > "${file}.diff"
171 local out=$(
172 grep '^[+-]' "${file}.diff" | \
173 grep -v \
174 -e '^-VMG Category:.*' \
175 -e '^+VMG Category: 00000000 (Region Code=ff)' \
176 -e '^[-+].*Title playback type:' \
177 -e '^-.*Title or time play:1' \
178 -e '^+.*Title or time play:0' \
179 -e '^-Prohibited user operations:' \
180 -e '^+Prohibited user operations: Angle Change, $' \
181 -e '^+Prohibited user operations: None$' \
182 | egrep -v -e '^[-+]([0-9a-f]{2} )+$'
183 )
184 rm "${file}".{{,bak.}dmp,diff}
185 if [[ -z ${out} ]] ; then
186 return 0
187 else
188 echo "error in ifo modification:"
189 echo "${out}"
190 return 1
191 fi
192}
193
194process_pgci() {
195 # http://dvdnav.mplayerhq.hu/dvdinfo/pgc.html
196 local file=$1 pgci_off=$2 indent=$3
197 local num_pgcs p pgc_off off
198
199 num_pgcs=$(bytes_get "${file}" ${pgci_off} 2)
200 for (( p = 0; p < num_pgcs; ++p )) ; do
201 pgc_off=$((
202 pgci_off +
203 $(bytes_get "${file}" $(( pgci_off + 8 + 8 * p + 4 )) 4)
204 ))
205 off=$(( pgc_off + 8 ))
206 pgc_puo=$(bytes_get "${file}" ${off} 4)
207 printf '%bpgc #%2i @ %#06x: %s: ' "${indent}" ${p} ${vmgm_pgc_off} ${pgc_puo}
208 new_pgc_puo=$(( pgc_puo & (1 << 22) )) # Keep angle field.
209 if [[ ${pgc_puo} -ne ${new_pgc_puo} ]] ; then
210 bytes_set "${file}" ${off} 0 $(printf '%x' $(( new_pgc_puo >> 16 ))) 0 0
211 echo "cleared"
212 else
213 echo "nothing to do"
214 fi
215 done
216}
217
218process_pgci_ut() {
219 # http://dvdnav.mplayerhq.hu/dvdinfo/ifo_vts.html#pgciut
220 local file=$1 pgci_ut_off=$2
221 local num_langs l off
222
223 num_langs=$(bytes_get "${file}" ${pgci_ut_off} 2)
224 for (( l = 0; l < num_langs; ++l )) ; do
225 off=$((
226 pgci_ut_off +
227 $(bytes_get "${file}" $(( pgci_ut_off + 8 + 8 * l + 4 )) 4)
228 ))
229 printf '\tclearing lang #%i @ %#06x\n' ${l} ${off}
230 process_pgci "${file}" ${off} '\t\t'
231 done
232}
233
234process_vmgi() {
235 # Back up the .IFO files before we edit them.
236 local vmgi=$1
237 _check_ifo "${vmgi}" "445644564944454f2d564d47" || return 1
238
239 echo "${vmgi}"
240
241 # Clear the region code.
242 # http://dvdnav.mplayerhq.hu/dvdinfo/ifo.html
243 # 0x23 - region code restrict byte - set to 0x00 for region free.
244 echo "clearing region code"
245 bytes_set "${vmgi}" 0x23 0x00
246
247 # Clear all Prohibited User Operations (PUOs) in TT_SRPT (Uop1 & Uop0).
248 # http://dvdnav.mplayerhq.hu/dvdinfo/ifo_vmg.html
249 echo "clearing PUOs in TT_SRPT"
250 tt_srpt_sec=$(dvdsec "${vmgi}" 0xC4)
251
252 num_titles=$(bytes_get "${vmgi}" ${tt_srpt_sec} 2)
253 for (( t = 0; t < num_titles; ++t )) ; do
254 off=$(( tt_srpt_sec + 8 + (t * 12) ))
255 printf "\ttitle type #%2i @ %#06x: " "${t}" "${off}"
256 title_type=$(bytes_get "${vmgi}" ${off} 1)
257 uop0=$(( title_type & 1 ))
258 uop1=$(( title_type & 2 ))
259 printf "%s (uop1:%i uop0:%i): " ${title_type} ${uop1} ${uop0}
260 new_title_type=$(( title_type & ~3 ))
261 if [[ ${title_type} -ne ${new_title_type} ]] ; then
262 bytes_set "${vmgi}" ${off} $(printf '%x' ${new_title_type})
263 echo "cleared"
264 else
265 echo "nothing to do"
266 fi
267 done
268
269 # Clear all the PUOs in the PGCs. But Preserve the angle bit.
270 # Note: Removal of the angle PUO can cause certain standalone players to
271 # display the "angle" icon during playback. Removal of the angle PUO
272 # whilst permitted is therefore not recommended.
273 # http://dvdnav.mplayerhq.hu/dvdinfo/uops.html
274 #fp_pgc_addr=$(bytes_get "${vmgi}" 0x84 4)
275 #num_pgcs=$(bytes_get "${vmgi}" $(( fp_pgc_sec + 1 )) 1)
276 echo "clearing PUOs in PGCs"
277 vmgm_pgci_ut_sec=$(dvdsec "${vmgi}" 0xC8)
278 process_pgci_ut "${vmgi}" ${vmgm_pgci_ut_sec}
279}
280
281process_vtsi() {
282 # Back up the .IFO files before we edit them.
283 local vtsi=$1
284 _check_ifo "${vtsi}" "445644564944454f2d565453" || return 1
285
286 echo "${vtsi}"
287
288 echo "clearing PUOs in PGCs"
289
290 vts_pgci_sec=$(dvdsec "${vtsi}" 0xCC)
291 process_pgci "${vtsi}" ${vts_pgci_sec} '\t'
292
293 vtsm_pgci_ut_sec=$(dvdsec "${vtsi}" 0xD0)
294 process_pgci_ut "${vtsi}" ${vtsm_pgci_ut_sec}
295}
296
297modify_dvd() {
298 local out_vts_dir="${out}/${Volume}/VIDEO_TS"
299 local vmgi vtsi
300
301 for vmgi in "${out_vts_dir}"/VIDEO_TS.{IFO,BUP} ; do
302 process_vmgi "${vmgi}"
303 check_changes "${vmgi}" || exit 1
304 done
305
306 for vtsi in "${out_vts_dir}"/VTS_??_?.{IFO,BUP} ; do
307 process_vtsi "${vtsi}"
308 check_changes "${vtsi}" || exit 1
309 done
310}
311
312#
313# Finally, create a new iso.
314#
315mkiso_dvd() {
316 set -- mkisofs -quiet -dvd-video -no-bak \
317 -A "${Application}" \
318 -p "${Preparer}" \
319 -publisher "${Publisher}" \
320 -sysid "${System}" \
321 -V "${Volume}" \
322 -volset "${Volume_Set}" \
323 -o "${Volume}.iso" "${Volume}"
324 sh="${out}/.${Volume}.sh"
325 (
326 echo "#!/bin/sh"
327 echo "cd '${out}' || exit 1"
328 echo ". './.${Volume}.vars.sh'"
329 printf 'set -- '
330 printf '%q ' "$@"
331 echo
332 echo 'echo "$@"; exec "$@"'
333 ) > "${sh}"
334 chmod a+x "${sh}"
335 "${sh}" || exit
336
337 du -h "${out}/${Volume}.iso"
338}
339
340${doit_backup} && backup_dvd
341${doit_modify} && modify_dvd
342${doit_mkiso} && mkiso_dvd
343
344${doit_backup} && eject
345exit 0