Compare commits
2036 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
655c872ac9 | ||
|
|
cb0f2b48fd | ||
|
|
20a84bdefc | ||
|
|
83ad7d4935 | ||
|
|
ae2ca175d3 | ||
|
|
c855108b94 | ||
|
|
5e80b694c2 | ||
|
|
99640aa2ad | ||
|
|
4c0f7c3dd4 | ||
|
|
2a8490ef31 | ||
|
|
43dc694963 | ||
|
|
e03ee00554 | ||
|
|
01a07ec0e8 | ||
|
|
74b7738a9d | ||
|
|
10aaaebc38 | ||
|
|
a8d072150d | ||
|
|
7877bb7956 | ||
|
|
bd31e3df89 | ||
|
|
531e97b499 | ||
|
|
43876e967a | ||
|
|
efaf917939 | ||
|
|
7d3ceb7d8c | ||
|
|
201ac47943 | ||
|
|
5a70285c10 | ||
|
|
e13c27d32c | ||
|
|
9c9b673eeb | ||
|
|
7323fe0000 | ||
|
|
460d045cbe | ||
|
|
4983c1cfc9 | ||
|
|
c7bc9d471a | ||
|
|
a36b91ffe8 | ||
|
|
88da0db0f7 | ||
|
|
816c2b0ca8 | ||
|
|
d6ff68339b | ||
|
|
fdf483c98b | ||
|
|
eb020c1cb8 | ||
|
|
6fa331ad06 | ||
|
|
5a58ac2845 | ||
|
|
0c4edc4d17 | ||
|
|
b5cea4c27e | ||
|
|
b7de5d6df3 | ||
|
|
d7310ba307 | ||
|
|
cabb94b856 | ||
|
|
82b649f67d | ||
|
|
8753782a0c | ||
|
|
566a139edc | ||
|
|
39dda0f16b | ||
|
|
bddb371e31 | ||
|
|
128223a28a | ||
|
|
45b9d0553f | ||
|
|
a87f0e0475 | ||
|
|
beacea0482 | ||
|
|
85b59e79d3 | ||
|
|
9a947c5544 | ||
|
|
e4cebf4cbe | ||
|
|
f63e05b7d4 | ||
|
|
84b7fa02bb | ||
|
|
36e597a045 | ||
|
|
f760b3f271 | ||
|
|
d4c8fa6a24 | ||
|
|
c6604734c9 | ||
|
|
91ab8e22b7 | ||
|
|
14fb647647 | ||
|
|
8a39a43ad5 | ||
|
|
a3d23a6f57 | ||
|
|
ae5da12afb | ||
|
|
a3042b8f79 | ||
|
|
29cbfe7c1a | ||
|
|
1ddc3292cc | ||
|
|
658e1d122e | ||
|
|
d936a080db | ||
|
|
2a92af1e09 | ||
|
|
9cde537066 | ||
|
|
ffc5446914 | ||
|
|
5f70b32795 | ||
|
|
6b6117653a | ||
|
|
59bffed47f | ||
|
|
f91a679cdf | ||
|
|
66b8d27d66 | ||
|
|
1219f504ea | ||
|
|
ee5a191e39 | ||
|
|
f577aa8d4f | ||
|
|
2436c87372 | ||
|
|
e90b5e63e5 | ||
|
|
78588d8cdf | ||
|
|
20c1c1d4be | ||
|
|
329dfb21b9 | ||
|
|
02c30211a7 | ||
|
|
645da2713d | ||
|
|
20c5a79efa | ||
|
|
b224aeb0d8 | ||
|
|
73eebd6162 | ||
|
|
99a1851f4d | ||
|
|
d9d89d0927 | ||
|
|
54f2aa5f77 | ||
|
|
d022e9dd00 | ||
|
|
17cf8ec1c6 | ||
|
|
c4a6d81cda | ||
|
|
66ed485803 | ||
|
|
6de585d1c8 | ||
|
|
9498638988 | ||
|
|
87dad82210 | ||
|
|
e37aec16ac | ||
|
|
6fb48af598 | ||
|
|
8193be36e5 | ||
|
|
a7df2bc4fb | ||
|
|
5eda028af3 | ||
|
|
f77475dbfe | ||
|
|
96c1a35c8e | ||
|
|
0ef26be03f | ||
|
|
f5bb15f72e | ||
|
|
d84aebd8f4 | ||
|
|
7d8d9f4c4d | ||
|
|
61b201810d | ||
|
|
c75db8dc60 | ||
|
|
e0bf4091dd | ||
|
|
1d89c46638 | ||
|
|
88797424f1 | ||
|
|
e0c57e77a9 | ||
|
|
e15a99f39f | ||
|
|
9723c3606a | ||
|
|
d2385d9287 | ||
|
|
2695169514 | ||
|
|
efcdfdf612 | ||
|
|
f77e7334c5 | ||
|
|
9079a7988c | ||
|
|
5256d795b1 | ||
|
|
24a765814a | ||
|
|
ef18a00362 | ||
|
|
1823786344 | ||
|
|
548a1f30ca | ||
|
|
e63859442a | ||
|
|
69032db3c3 | ||
|
|
d3e5b1b4e0 | ||
|
|
e2cd68e3e8 | ||
|
|
3e6029d33c | ||
|
|
89f1f8e6dc | ||
|
|
cff25374b5 | ||
|
|
04bbff338d | ||
|
|
bbcd6a3261 | ||
|
|
e587e8cfc8 | ||
|
|
869f8cf380 | ||
|
|
1ff6ba9480 | ||
|
|
5807bee23b | ||
|
|
b5bafbf23a | ||
|
|
3954ccab8a | ||
|
|
24eb9d9e14 | ||
|
|
57207a72ea | ||
|
|
8771911931 | ||
|
|
7b2411eec5 | ||
|
|
49a0b0bff2 | ||
|
|
3cea7ee1b9 | ||
|
|
d366c1ac64 | ||
|
|
315858e410 | ||
|
|
a53965b824 | ||
|
|
cf973c3f25 | ||
|
|
db09144624 | ||
|
|
32b94555c3 | ||
|
|
f507fef40d | ||
|
|
d5caff58c7 | ||
|
|
ab4c725217 | ||
|
|
901ba9cf13 | ||
|
|
19b1bc6b3e | ||
|
|
c623ea7c2a | ||
|
|
72e61106a9 | ||
|
|
e15e7f0942 | ||
|
|
133f094d72 | ||
|
|
e4c89c1aed | ||
|
|
193cf1c750 | ||
|
|
75c2a630f6 | ||
|
|
3a7cecac31 | ||
|
|
0ab214fd5d | ||
|
|
eb603e942f | ||
|
|
b1e1c704b1 | ||
|
|
f908336ec6 | ||
|
|
3ff97e43e1 | ||
|
|
622434b457 | ||
|
|
877319a8e5 | ||
|
|
295db43bca | ||
|
|
4285f43316 | ||
|
|
29e8316527 | ||
|
|
2b988b8ab1 | ||
|
|
0abf991b05 | ||
|
|
7fc5b70553 | ||
|
|
b3681002a7 | ||
|
|
1f9af7a1e4 | ||
|
|
9ed184edbf | ||
|
|
89572b07fb | ||
|
|
443578e0ee | ||
|
|
0f96daec9a | ||
|
|
59412c54de | ||
|
|
16ca55e4ec | ||
|
|
03959a68d5 | ||
|
|
59b48ceb4a | ||
|
|
d499bfab8a | ||
|
|
b7b2b5089f | ||
|
|
fcb417af17 | ||
|
|
28c130a910 | ||
|
|
57a5eabd8f | ||
|
|
88874f0dcd | ||
|
|
98d5e27a8c | ||
|
|
576956fa1d | ||
|
|
be051a4bb1 | ||
|
|
cc6c7430d4 | ||
|
|
477ee14b6d | ||
|
|
2539b2333c | ||
|
|
ebf6ba9ca1 | ||
|
|
60528525e0 | ||
|
|
cb5a7f8c9d | ||
|
|
fd9c6d460a | ||
|
|
fa5b135439 | ||
|
|
9f5249e5b8 | ||
|
|
819800d3a4 | ||
|
|
8ac5b55b3a | ||
|
|
abe1079328 | ||
|
|
775c62fbb4 | ||
|
|
daff2c1f84 | ||
|
|
1ea8b064cc | ||
|
|
a0e030a6ee | ||
|
|
690eb343ea | ||
|
|
ad5e476920 | ||
|
|
2a92ce867a | ||
|
|
bf7c8df353 | ||
|
|
d57f6303f4 | ||
|
|
9c6c26f424 | ||
|
|
00a98f6e30 | ||
|
|
50d83dc070 | ||
|
|
010a0cc2a7 | ||
|
|
71728bb3fe | ||
|
|
fc1537fde8 | ||
|
|
6e40f639b7 | ||
|
|
21c5a97d15 | ||
|
|
8a4d78ae4d | ||
|
|
001964f2f4 | ||
|
|
a6a29d5c5a | ||
|
|
706308cefd | ||
|
|
72b194cd7e | ||
|
|
b85819ed9d | ||
|
|
c5cd648b5d | ||
|
|
3743408543 | ||
|
|
fd251c6e61 | ||
|
|
b7ab6c5c3e | ||
|
|
5659fd5839 | ||
|
|
4dcd3e95df | ||
|
|
57709413e1 | ||
|
|
fb634bc2d4 | ||
|
|
18a67e1f20 | ||
|
|
b4c2ae251b | ||
|
|
bfaabafbfa | ||
|
|
c213d5b8d9 | ||
|
|
50beda6621 | ||
|
|
8ea359ec8a | ||
|
|
c983c8e447 | ||
|
|
af96ce3e82 | ||
|
|
6a5928863c | ||
|
|
514becc53a | ||
|
|
1b140535a8 | ||
|
|
cb5222e4d6 | ||
|
|
b6a9e1a996 | ||
|
|
b58a7c32c2 | ||
|
|
fa0fd6b7d0 | ||
|
|
057ec49a3e | ||
|
|
3ae9092226 | ||
|
|
351999a01b | ||
|
|
e407080057 | ||
|
|
fa0bd8d31a | ||
|
|
9b5fdb1345 | ||
|
|
47fd30c23f | ||
|
|
ad469e4f61 | ||
|
|
eaed953614 | ||
|
|
977ac16a1e | ||
|
|
c7986e4277 | ||
|
|
aff8fa1968 | ||
|
|
0af767fff0 | ||
|
|
a928aa61ca | ||
|
|
30cb0b1cae | ||
|
|
32ae3cd567 | ||
|
|
91b9602e12 | ||
|
|
124eba6800 | ||
|
|
a5ab8ddf8b | ||
|
|
1444a1578a | ||
|
|
5b8894b0ff | ||
|
|
a4329dd9a0 | ||
|
|
1cab25aa7d | ||
|
|
d3faf18251 | ||
|
|
ddcc296af4 | ||
|
|
ee765bc210 | ||
|
|
630c67f7d0 | ||
|
|
e78bb1b29c | ||
|
|
db312a7380 | ||
|
|
48b4cbe1b9 | ||
|
|
edb1d4c747 | ||
|
|
128935b55e | ||
|
|
d5a0d67c7b | ||
|
|
a86640fdee | ||
|
|
03291b27d1 | ||
|
|
acf81f116f | ||
|
|
6564c405c1 | ||
|
|
1bd586268c | ||
|
|
497952611d | ||
|
|
9149d1baa3 | ||
|
|
66c60ac5c7 | ||
|
|
971fad4560 | ||
|
|
8ed1ce4103 | ||
|
|
3112c34d11 | ||
|
|
7ebf3cecc6 | ||
|
|
660fa99f9c | ||
|
|
a997e53d8b | ||
|
|
1d1dd583e2 | ||
|
|
d490b29265 | ||
|
|
e8ca64f16b | ||
|
|
380aa7d884 | ||
|
|
0a31a94ee8 | ||
|
|
e1d50ce28f | ||
|
|
38cc39f23d | ||
|
|
8110035d71 | ||
|
|
9af409d6d6 | ||
|
|
8646829853 | ||
|
|
b9e87813b1 | ||
|
|
138e032746 | ||
|
|
d94c6b5aba | ||
|
|
7c03a39316 | ||
|
|
c482e264f4 | ||
|
|
3a9a1f0a94 | ||
|
|
5b5f728f49 | ||
|
|
179aac6b35 | ||
|
|
25de9126ca | ||
|
|
87be5662ad | ||
|
|
053a59e3f4 | ||
|
|
250857b3d4 | ||
|
|
dc288c584c | ||
|
|
fc02543f15 | ||
|
|
0808db706f | ||
|
|
1e649fc565 | ||
|
|
c523e959d7 | ||
|
|
1422f988c1 | ||
|
|
e86252b934 | ||
|
|
e9c59b5c31 | ||
|
|
f7bddee0ce | ||
|
|
d56b76c9e3 | ||
|
|
a41353ed03 | ||
|
|
7ea12cf2f9 | ||
|
|
5bc5170112 | ||
|
|
946ed0c0b5 | ||
|
|
fcd4e5aca2 | ||
|
|
6ab6e6d3a8 | ||
|
|
0893d01b4a | ||
|
|
bf04c45ebf | ||
|
|
2786a1a97e | ||
|
|
cd9a4f1aa9 | ||
|
|
91e5cafe76 | ||
|
|
47754951f0 | ||
|
|
fbef8ef32e | ||
|
|
abd5bf3158 | ||
|
|
fd024368a3 | ||
|
|
cf4856a0c9 | ||
|
|
1b791c82fc | ||
|
|
3a27a3e868 | ||
|
|
7e6c16b287 | ||
|
|
f877639ed7 | ||
|
|
846309d4dd | ||
|
|
f2239ddfdf | ||
|
|
742d455b0e | ||
|
|
20b08a78e7 | ||
|
|
042da74955 | ||
|
|
4dcae4ce21 | ||
|
|
fa0f07f553 | ||
|
|
39792d76cf | ||
|
|
d181ee9101 | ||
|
|
335ba1a5b0 | ||
|
|
4934a360f1 | ||
|
|
0559a4c6f2 | ||
|
|
fc07919a75 | ||
|
|
d7661edf67 | ||
|
|
1c91693294 | ||
|
|
3fc3cbc6d5 | ||
|
|
11b5895e69 | ||
|
|
b4c614edbf | ||
|
|
deddaed04a | ||
|
|
a155e503f4 | ||
|
|
c0663bc19f | ||
|
|
7ffa51b83d | ||
|
|
6d397b9988 | ||
|
|
571a7fa26b | ||
|
|
b3b5a38c3a | ||
|
|
a4b115f89b | ||
|
|
3d672bb145 | ||
|
|
15b656b753 | ||
|
|
f5785db163 | ||
|
|
1ff1bf3292 | ||
|
|
b062febea0 | ||
|
|
35301dc79e | ||
|
|
30c336726b | ||
|
|
3821680817 | ||
|
|
74242ea24f | ||
|
|
73c7024e11 | ||
|
|
bbdec92dc6 | ||
|
|
b4c289101c | ||
|
|
722d0797f6 | ||
|
|
deb27d5b55 | ||
|
|
9cc4ffdf6e | ||
|
|
c76d63d1d9 | ||
|
|
21bb4f33ad | ||
|
|
2897b881d6 | ||
|
|
e9b89d0929 | ||
|
|
e801254b2e | ||
|
|
6f49918ee9 | ||
|
|
0ae7c18f1f | ||
|
|
c9c3fb396a | ||
|
|
91db4f1934 | ||
|
|
9bbed6e95c | ||
|
|
748bc27b25 | ||
|
|
f42708e8bc | ||
|
|
160e4570a2 | ||
|
|
6272965143 | ||
|
|
1e9613bf7f | ||
|
|
a061dac298 | ||
|
|
914dee8571 | ||
|
|
47e2905edf | ||
|
|
ee6675aee0 | ||
|
|
62e0d9fe64 | ||
|
|
a174c142c1 | ||
|
|
95afc5fdec | ||
|
|
04d69f66c0 | ||
|
|
0347141edd | ||
|
|
1d6baae6e0 | ||
|
|
fb0f48f08a | ||
|
|
76e5e03d31 | ||
|
|
4cab743634 | ||
|
|
7c10ec97b7 | ||
|
|
8718a16889 | ||
|
|
75a0b924c3 | ||
|
|
83a90e0c05 | ||
|
|
e5eadd1315 | ||
|
|
f0142d90d4 | ||
|
|
cabd6e6e9d | ||
|
|
4804a05736 | ||
|
|
c258e5a3af | ||
|
|
e8492940a5 | ||
|
|
4bccb1ab47 | ||
|
|
b6d219e232 | ||
|
|
23ee17594d | ||
|
|
d9d39d8e25 | ||
|
|
224d5d46c1 | ||
|
|
09e0059930 | ||
|
|
0ddff2b087 | ||
|
|
2e6a29eacc | ||
|
|
ad2fb82aa9 | ||
|
|
a3f91c11e8 | ||
|
|
a50c978ce3 | ||
|
|
27d6f881cd | ||
|
|
944cd020af | ||
|
|
bbe5d64b99 | ||
|
|
f7b36ac4c7 | ||
|
|
1d555ca17e | ||
|
|
f91b6c3468 | ||
|
|
abe6eeb350 | ||
|
|
64f90a7912 | ||
|
|
5733966843 | ||
|
|
eb1344fcec | ||
|
|
c5fb29f00e | ||
|
|
63135b9c54 | ||
|
|
f7c666584e | ||
|
|
6834324de2 | ||
|
|
3a0d59e66f | ||
|
|
ffd2e2188a | ||
|
|
0e8d5bdc5d | ||
|
|
14806f6614 | ||
|
|
00ece83b9d | ||
|
|
8197ae2a2d | ||
|
|
5fe658bb16 | ||
|
|
617179f0c6 | ||
|
|
95ac85f642 | ||
|
|
ca8877ad47 | ||
|
|
6d8f31048c | ||
|
|
ac859eb576 | ||
|
|
2dfa171b6a | ||
|
|
6f72e3e2ea | ||
|
|
c2b73ae963 | ||
|
|
6c50077409 | ||
|
|
06746449a1 | ||
|
|
da7b8edf51 | ||
|
|
6e29b41f23 | ||
|
|
912bb069af | ||
|
|
6b2d7a67d8 | ||
|
|
dbb8ec0290 | ||
|
|
60b32760f2 | ||
|
|
73a40bcb49 | ||
|
|
0e258a5a32 | ||
|
|
7ca65c81d8 | ||
|
|
2ad1a60e59 | ||
|
|
cf17ff4478 | ||
|
|
fffc3aac68 | ||
|
|
295ac3c458 | ||
|
|
b6693a71f9 | ||
|
|
5b21118a8c | ||
|
|
0235b19801 | ||
|
|
7426399aa2 | ||
|
|
6861b0d668 | ||
|
|
c30fb0d38c | ||
|
|
e45521c6c0 | ||
|
|
d11fe8d4fc | ||
|
|
8e83e63e3d | ||
|
|
c8fd0ac4b3 | ||
|
|
d78419eb33 | ||
|
|
e44a3d013d | ||
|
|
aeb0d05017 | ||
|
|
62702e4b3d | ||
|
|
5146cdfa2f | ||
|
|
246e7018c3 | ||
|
|
24286dbe9d | ||
|
|
2f442dfbe1 | ||
|
|
b2fb01ee9c | ||
|
|
45e0a9a4ef | ||
|
|
675b7b4bf4 | ||
|
|
be7a35443e | ||
|
|
9918615fcd | ||
|
|
4b72ef77c1 | ||
|
|
e427c61fbb | ||
|
|
c78e8937d5 | ||
|
|
5d63f7a93d | ||
|
|
46551ada7f | ||
|
|
83f17a37b1 | ||
|
|
9e23e0a623 | ||
|
|
4a53d4f043 | ||
|
|
0fd61945c7 | ||
|
|
e3624eed30 | ||
|
|
1923c8be0c | ||
|
|
a3e37fbfe2 | ||
|
|
318c3bb422 | ||
|
|
db96e24028 | ||
|
|
cc2cce66df | ||
|
|
7afeabd288 | ||
|
|
eb43a812d6 | ||
|
|
12cbcdb6f4 | ||
|
|
a2e35e30dc | ||
|
|
b6ff7e6b47 | ||
|
|
6dba40c6bb | ||
|
|
896da46422 | ||
|
|
7c07f5eb2a | ||
|
|
298cff37de | ||
|
|
8add802fe9 | ||
|
|
6d080a0d59 | ||
|
|
f0ae1051ee | ||
|
|
1ad0ffeaa6 | ||
|
|
45b44d012d | ||
|
|
cb6cbb9ee5 | ||
|
|
013a0d9cb0 | ||
|
|
8abb3ea225 | ||
|
|
74a5233b5d | ||
|
|
cd695a4522 | ||
|
|
eec4b2cc0e | ||
|
|
616875d0d2 | ||
|
|
eb749bd76f | ||
|
|
7d1e404efd | ||
|
|
4a01a578d1 | ||
|
|
393a2e0ea0 | ||
|
|
8e21decb8d | ||
|
|
22bfbab832 | ||
|
|
28d0cc8795 | ||
|
|
51f2383a07 | ||
|
|
a6426c6eba | ||
|
|
0631da6c8e | ||
|
|
3960239cfe | ||
|
|
b16e48af6f | ||
|
|
4d3950565a | ||
|
|
8d24bc50c9 | ||
|
|
458efe9168 | ||
|
|
ae940a8681 | ||
|
|
d76b223b7c | ||
|
|
7243d5f7cb | ||
|
|
387f74f66a | ||
|
|
61ffb7d97a | ||
|
|
a553693f34 | ||
|
|
cdcfd64ec4 | ||
|
|
7f4302ba20 | ||
|
|
0d94f2d376 | ||
|
|
c1eb20a20b | ||
|
|
634db67685 | ||
|
|
86648258a3 | ||
|
|
287f0a3739 | ||
|
|
8f42df209a | ||
|
|
390fd64a74 | ||
|
|
48ee471def | ||
|
|
e862afcb78 | ||
|
|
872da05ff6 | ||
|
|
d09e2daf00 | ||
|
|
e2d5b44b0a | ||
|
|
52d42ef2a8 | ||
|
|
b6abc34461 | ||
|
|
ae6a50626d | ||
|
|
8f9dbfee2c | ||
|
|
c71ccda967 | ||
|
|
0f284f2e1d | ||
|
|
45ac80dd8c | ||
|
|
09cdba4b3d | ||
|
|
d94ee8863c | ||
|
|
cc8ced6430 | ||
|
|
cdb144e6a6 | ||
|
|
58fb8643c6 | ||
|
|
b81cfa8acb | ||
|
|
938ee20f1f | ||
|
|
d02dc54c1b | ||
|
|
8680a54ae4 | ||
|
|
30001be0ee | ||
|
|
4614cb5ec1 | ||
|
|
e390f3a399 | ||
|
|
e22d463d11 | ||
|
|
5877aa822c | ||
|
|
a2915913bb | ||
|
|
a8b40747b2 | ||
|
|
b63030d302 | ||
|
|
4457e416db | ||
|
|
c7c1a8ede1 | ||
|
|
fd1e9d7fb0 | ||
|
|
59a6d2317b | ||
|
|
531c171542 | ||
|
|
0c743452b0 | ||
|
|
4893d3da1f | ||
|
|
5cdc24bfb0 | ||
|
|
523cdca449 | ||
|
|
09537d73da | ||
|
|
99c0c0b3b1 | ||
|
|
45bc353341 | ||
|
|
b2fc41a911 | ||
|
|
ebefe8b6d2 | ||
|
|
9e3508134b | ||
|
|
3166ca2127 | ||
|
|
204508478f | ||
|
|
51ce674c97 | ||
|
|
6fcde9fe5f | ||
|
|
c688e3431d | ||
|
|
230376c7f3 | ||
|
|
91d0a2cd0c | ||
|
|
1ffd010e4a | ||
|
|
ae366e141f | ||
|
|
eb869c6e97 | ||
|
|
b559f7cd16 | ||
|
|
375d32f08e | ||
|
|
89d6b7cec0 | ||
|
|
fdf96e8342 | ||
|
|
c5d73d7b09 | ||
|
|
34b4cc2d9e | ||
|
|
40ed17e609 | ||
|
|
673d76af45 | ||
|
|
c13fb6f9d5 | ||
|
|
424b1e7d1f | ||
|
|
e80c9c4101 | ||
|
|
f75c3633e9 | ||
|
|
d535cd8276 | ||
|
|
c13f6ab2af | ||
|
|
d2a30bfb78 | ||
|
|
e5b17092b4 | ||
|
|
466cb4c78b | ||
|
|
9722e1ddc4 | ||
|
|
e3a9b0b943 | ||
|
|
d668a8aee2 | ||
|
|
75dc0aafcf | ||
|
|
41b94233c6 | ||
|
|
52cff01b9c | ||
|
|
372fc6603d | ||
|
|
9221797c9d | ||
|
|
7501131558 | ||
|
|
bba7be99a3 | ||
|
|
abb95534d0 | ||
|
|
0b7b4c12e4 | ||
|
|
4056f00169 | ||
|
|
10303cb9c0 | ||
|
|
e3587d369e | ||
|
|
2a048502cc | ||
|
|
cac01fbde9 | ||
|
|
2d2ce191ec | ||
|
|
6380cb8458 | ||
|
|
48e0e6af71 | ||
|
|
b756bccc7a | ||
|
|
ae8eed7a67 | ||
|
|
ae4882bec3 | ||
|
|
1379741859 | ||
|
|
b45c7ace78 | ||
|
|
3843d9f55b | ||
|
|
f3422cb2fe | ||
|
|
73692797dc | ||
|
|
31dd910289 | ||
|
|
da51580299 | ||
|
|
9db148b1ec | ||
|
|
091b1b8209 | ||
|
|
900a4071ed | ||
|
|
df4b2f7938 | ||
|
|
803f44d9bc | ||
|
|
4b67aee020 | ||
|
|
71dc47d6c9 | ||
|
|
9cb305fb0d | ||
|
|
1672130486 | ||
|
|
8e135e3e79 | ||
|
|
aa1a4f6ea5 | ||
|
|
ba34cf5258 | ||
|
|
647089d2a8 | ||
|
|
5211508eb4 | ||
|
|
e6f05ae465 | ||
|
|
a9193f9b76 | ||
|
|
e373a17cd3 | ||
|
|
ebab9b7e4a | ||
|
|
6de0399807 | ||
|
|
5cc7bb80f6 | ||
|
|
6e0bd9b3f8 | ||
|
|
d1943f72d3 | ||
|
|
81709873bd | ||
|
|
f6bb7cd8ed | ||
|
|
d1c19e431c | ||
|
|
9ab2dde8ab | ||
|
|
71559bb125 | ||
|
|
ae4c95262c | ||
|
|
dbbf07c9c1 | ||
|
|
ab8cd619d5 | ||
|
|
c30ad2819d | ||
|
|
311e91797a | ||
|
|
52be4df355 | ||
|
|
0364e81050 | ||
|
|
2d49b71bc9 | ||
|
|
a18a4bdf31 | ||
|
|
d3acbe8288 | ||
|
|
22afcbcbb6 | ||
|
|
495c6bc21c | ||
|
|
cfd1fe59f3 | ||
|
|
c46cf5bc84 | ||
|
|
a8742557bd | ||
|
|
3bea58cf56 | ||
|
|
5aaa5231b8 | ||
|
|
82d10dd7cb | ||
|
|
841065fb91 | ||
|
|
c7146ef138 | ||
|
|
08f32d1de6 | ||
|
|
4c3f86aa4d | ||
|
|
445cf22333 | ||
|
|
4919de647a | ||
|
|
77fae99528 | ||
|
|
10bd4b40b9 | ||
|
|
9d8e6bb253 | ||
|
|
49c71ecfad | ||
|
|
d18834b415 | ||
|
|
e52cda193e | ||
|
|
0d5edd35ea | ||
|
|
f3f51c3d9d | ||
|
|
1431916183 | ||
|
|
ae48008803 | ||
|
|
5664813931 | ||
|
|
3948af80b8 | ||
|
|
343d6f9aff | ||
|
|
51b379502f | ||
|
|
82142751de | ||
|
|
4e5755f218 | ||
|
|
2f4f29517e | ||
|
|
e8a073651f | ||
|
|
d23da7a612 | ||
|
|
1ae4938da3 | ||
|
|
b5e27d4d69 | ||
|
|
d74fc8d1ce | ||
|
|
c5d9a41967 | ||
|
|
7e3042c4f4 | ||
|
|
1291dadbd6 | ||
|
|
f645099a39 | ||
|
|
8f32038891 | ||
|
|
a6fb1dcdf9 | ||
|
|
f01b469f3f | ||
|
|
47884919c8 | ||
|
|
653a35496d | ||
|
|
9b14df6b27 | ||
|
|
09813f3c5a | ||
|
|
7d76b7e4c2 | ||
|
|
47b5dea95c | ||
|
|
b0df63f1e8 | ||
|
|
3c4209b676 | ||
|
|
d51b9a8e0e | ||
|
|
3b56125bd2 | ||
|
|
6e69e39007 | ||
|
|
97208cb329 | ||
|
|
414a4a97fb | ||
|
|
2a809f96c4 | ||
|
|
17799b03f3 | ||
|
|
efc55fc648 | ||
|
|
22bd41211a | ||
|
|
4cb0171bd0 | ||
|
|
ce6c5af72c | ||
|
|
171575256c | ||
|
|
d3664dcf78 | ||
|
|
0788981783 | ||
|
|
aeee7c02d5 | ||
|
|
6e49a50461 | ||
|
|
fbc99827ab | ||
|
|
3b134320c4 | ||
|
|
c315e5016d | ||
|
|
7aebd6ed57 | ||
|
|
1a8ca06495 | ||
|
|
a27ae7e4a6 | ||
|
|
dd0ab897aa | ||
|
|
00ad92fb6d | ||
|
|
f84128ecbd | ||
|
|
ba89e0f4e3 | ||
|
|
832d36a4d4 | ||
|
|
0b437b3bfb | ||
|
|
7b50ec4630 | ||
|
|
4ddb13abac | ||
|
|
d2ac081177 | ||
|
|
9692fbf15b | ||
|
|
be966488e8 | ||
|
|
0ce613264f | ||
|
|
34634d776e | ||
|
|
673ded3819 | ||
|
|
b9a94ad3ae | ||
|
|
3f80b330cc | ||
|
|
01632d538c | ||
|
|
b0a9b1cd09 | ||
|
|
1f772081fd | ||
|
|
4ae54dbaad | ||
|
|
e47f4ff731 | ||
|
|
465369d79e | ||
|
|
15ddf6ff20 | ||
|
|
16a753bd95 | ||
|
|
c15103636c | ||
|
|
8a5f82ee7d | ||
|
|
5ec33ec821 | ||
|
|
ab7d383cf1 | ||
|
|
184e9a5c93 | ||
|
|
c4f5363cde | ||
|
|
002882cebf | ||
|
|
1cb3ec0c7b | ||
|
|
6bf325c6f6 | ||
|
|
09c7ff9e8b | ||
|
|
c2a94b61bf | ||
|
|
1db16232de | ||
|
|
3da681a6b1 | ||
|
|
a79b3e7852 | ||
|
|
b1099e6974 | ||
|
|
4f6e06131c | ||
|
|
19f69e9e6c | ||
|
|
01481da773 | ||
|
|
3e8f7e1cf1 | ||
|
|
5da69646f2 | ||
|
|
3cac01583b | ||
|
|
d16a26605e | ||
|
|
a4f692c788 | ||
|
|
9f01206c57 | ||
|
|
d34fc551ed | ||
|
|
7aa5f0d258 | ||
|
|
276a34bb66 | ||
|
|
0d820eda12 | ||
|
|
1991c1b677 | ||
|
|
459404e3f0 | ||
|
|
badc623a3c | ||
|
|
8e39f92cb7 | ||
|
|
3ff4885973 | ||
|
|
f9d45f7657 | ||
|
|
789ff9df5c | ||
|
|
a2064ed16b | ||
|
|
b3d06c0868 | ||
|
|
db1a6b3e38 | ||
|
|
8390237cc4 | ||
|
|
9967eae7bb | ||
|
|
b3be7d1c6f | ||
|
|
33ccb5dbb2 | ||
|
|
5b90c0d695 | ||
|
|
472a660239 | ||
|
|
ab67536d9a | ||
|
|
ee85fb3aec | ||
|
|
214b6f4358 | ||
|
|
af0d092054 | ||
|
|
b07903c3e9 | ||
|
|
ffa4c6bf09 | ||
|
|
b4125fa56c | ||
|
|
2c72302087 | ||
|
|
0fa52bc64f | ||
|
|
f55a80b15a | ||
|
|
0735483321 | ||
|
|
53fc2c7c21 | ||
|
|
f22133c3c5 | ||
|
|
2d5a6d6583 | ||
|
|
dc4adf2836 | ||
|
|
dd6e254e4f | ||
|
|
4c028c1659 | ||
|
|
d332a6777a | ||
|
|
378251f229 | ||
|
|
b6b9b903ed | ||
|
|
143d68cfd5 | ||
|
|
a31eac1426 | ||
|
|
797196f7fc | ||
|
|
c9d0bc81dd | ||
|
|
b5448ff607 | ||
|
|
5ebd363d5d | ||
|
|
1d439e673e | ||
|
|
0b7b7656b2 | ||
|
|
eb270df835 | ||
|
|
ff73dd2183 | ||
|
|
e043a03eb6 | ||
|
|
3cb4e8e373 | ||
|
|
7e6de528b4 | ||
|
|
df901c30ef | ||
|
|
13856b33ec | ||
|
|
a3a1c6f4c8 | ||
|
|
638998a861 | ||
|
|
6e2ec89a05 | ||
|
|
af67de8aa6 | ||
|
|
425dac478e | ||
|
|
b15c4ecd10 | ||
|
|
d7f88cf3a4 | ||
|
|
5b7fbcd9b8 | ||
|
|
7af64b0782 | ||
|
|
b84c70e811 | ||
|
|
f5b245c72d | ||
|
|
4307183817 | ||
|
|
aeb32783d6 | ||
|
|
3927b3bf27 | ||
|
|
da9d2f9417 | ||
|
|
1cec48e8f8 | ||
|
|
f1105393da | ||
|
|
dc7047e3c2 | ||
|
|
08b2945623 | ||
|
|
cbcc223150 | ||
|
|
9830f21e4a | ||
|
|
5f49567bf7 | ||
|
|
6154ae7342 | ||
|
|
978f3a3682 | ||
|
|
1f0961d574 | ||
|
|
a101252701 | ||
|
|
c96c29b1e3 | ||
|
|
3b0fc180ff | ||
|
|
9b8bfdf33c | ||
|
|
4328831fcd | ||
|
|
e5b3df41e9 | ||
|
|
cf5259e218 | ||
|
|
f24b6a520c | ||
|
|
4140163ab2 | ||
|
|
7afde0e93f | ||
|
|
d27a571882 | ||
|
|
1c70e3be25 | ||
|
|
1819f64467 | ||
|
|
9852e588c1 | ||
|
|
71a1ea481b | ||
|
|
9e32f0d778 | ||
|
|
4478174dc2 | ||
|
|
07553476d4 | ||
|
|
1773283456 | ||
|
|
221ab51d90 | ||
|
|
43e0dd922b | ||
|
|
b3262652c3 | ||
|
|
0cfa2b8c20 | ||
|
|
c4600b2187 | ||
|
|
89b15e3b57 | ||
|
|
07d8c3e770 | ||
|
|
c5ae64070e | ||
|
|
2cb7c52147 | ||
|
|
c045b16213 | ||
|
|
b29387d409 | ||
|
|
614a09db1d | ||
|
|
bcc5450aef | ||
|
|
b0420a0566 | ||
|
|
08d627bc9f | ||
|
|
5b5f048faf | ||
|
|
212ebae043 | ||
|
|
a0c99df6b2 | ||
|
|
b0fabd7897 | ||
|
|
938811f24f | ||
|
|
374d6e4d0f | ||
|
|
5f54cdee0f | ||
|
|
c2bd452391 | ||
|
|
d2bfc73b91 | ||
|
|
42c62206c8 | ||
|
|
4208b512f8 | ||
|
|
3a3d5cd487 | ||
|
|
e143a6d126 | ||
|
|
a99a19aa60 | ||
|
|
aba7d2b6a4 | ||
|
|
2fc6f835a8 | ||
|
|
27ce0e6d60 | ||
|
|
05c70f2adb | ||
|
|
904097b7b1 | ||
|
|
4e003c12a6 | ||
|
|
24f2cfb29f | ||
|
|
3f2b683fca | ||
|
|
83cee26d7d | ||
|
|
c7df0c9b28 | ||
|
|
26a8bfaac4 | ||
|
|
fc8a3be7d9 | ||
|
|
b195c4033d | ||
|
|
ab2732c55d | ||
|
|
7fd5c058be | ||
|
|
b22e8b4702 | ||
|
|
e2c2af3447 | ||
|
|
4cd738ecb5 | ||
|
|
e838470c26 | ||
|
|
68e0bc40e1 | ||
|
|
9fc8bcdf62 | ||
|
|
227b14a0b6 | ||
|
|
abcd184d5d | ||
|
|
3fd9f4b0df | ||
|
|
12ff3e963b | ||
|
|
558e392234 | ||
|
|
e747c2e263 | ||
|
|
4d78b30e8c | ||
|
|
9a1520d5e3 | ||
|
|
04f3543424 | ||
|
|
eae287be37 | ||
|
|
a0e698b058 | ||
|
|
cd2932adea | ||
|
|
286b270592 | ||
|
|
d1ebef2cc5 | ||
|
|
deb567fde7 | ||
|
|
26586fb9ef | ||
|
|
5f71a558b9 | ||
|
|
08882639e0 | ||
|
|
837ae2932f | ||
|
|
b51cc21140 | ||
|
|
f1115ba706 | ||
|
|
dc36aee7ff | ||
|
|
740f9581e6 | ||
|
|
de0e3a7bc9 | ||
|
|
95e894fb4d | ||
|
|
faacb0d69f | ||
|
|
28b139e1b8 | ||
|
|
39b1e0676b | ||
|
|
c05fc5bd36 | ||
|
|
bbae9157fb | ||
|
|
fc5c63a869 | ||
|
|
e2e068f8e5 | ||
|
|
43a068e30e | ||
|
|
2beb4b8c6f | ||
|
|
ad4230f7fa | ||
|
|
00369a2cfe | ||
|
|
248e487dd5 | ||
|
|
3a3dc02a66 | ||
|
|
f49c47c20d | ||
|
|
417ef1a7a4 | ||
|
|
8e8889483f | ||
|
|
b5818a9b62 | ||
|
|
e709f676dc | ||
|
|
bb611bf655 | ||
|
|
24e8ca67c5 | ||
|
|
0695941a77 | ||
|
|
7c71efd1b3 | ||
|
|
5099aff5c3 | ||
|
|
d3463250a9 | ||
|
|
3b58c02db0 | ||
|
|
64f0313d3c | ||
|
|
d3958e757b | ||
|
|
651020388d | ||
|
|
72ede666d4 | ||
|
|
a68c249d4e | ||
|
|
56caab4461 | ||
|
|
13b60351a6 | ||
|
|
ad49d38e46 | ||
|
|
082c9097e4 | ||
|
|
2fbdb29ebc | ||
|
|
ef34dce4dc | ||
|
|
577b7d8ec8 | ||
|
|
4ce099294c | ||
|
|
72bff7fa35 | ||
|
|
8c36b8803f | ||
|
|
c69993bd57 | ||
|
|
ea3e4982d2 | ||
|
|
9a5f6c7b9b | ||
|
|
033d56a4b3 | ||
|
|
dfa684cccc | ||
|
|
b4e0f43dfc | ||
|
|
783cf7f1b0 | ||
|
|
92d6fc3fad | ||
|
|
436cdea4fd | ||
|
|
6b144b3b1f | ||
|
|
7820082eb8 | ||
|
|
e732f921a3 | ||
|
|
8b7c5d8585 | ||
|
|
9e959b189c | ||
|
|
5c7a4cdc22 | ||
|
|
756e619e07 | ||
|
|
eea6194b90 | ||
|
|
a09c1fa154 | ||
|
|
38c742328c | ||
|
|
1d5db1446d | ||
|
|
3f5f3d143f | ||
|
|
d297a7198a | ||
|
|
e5bd99dee4 | ||
|
|
2720e13e88 | ||
|
|
0e9c1789ff | ||
|
|
281cb10f84 | ||
|
|
69a92ffe72 | ||
|
|
6447f159e5 | ||
|
|
94430883ad | ||
|
|
046512eb3d | ||
|
|
4479d97e90 | ||
|
|
bf5fea8951 | ||
|
|
07282e3de6 | ||
|
|
c2f90a20df | ||
|
|
481d2d699e | ||
|
|
cf9a7e6ed3 | ||
|
|
c35235371a | ||
|
|
5d5723ad58 | ||
|
|
6c77294a86 | ||
|
|
823f65f1ca | ||
|
|
0c378c1642 | ||
|
|
bbb4162867 | ||
|
|
5dbdde3f2b | ||
|
|
2521954bd9 | ||
|
|
5f1002894e | ||
|
|
0489b312a3 | ||
|
|
732be5a34f | ||
|
|
1f45c78ebb | ||
|
|
8b86c79bf9 | ||
|
|
710ed81067 | ||
|
|
24ac0e7b9b | ||
|
|
15b2bfbb29 | ||
|
|
b7494eb381 | ||
|
|
27ac590250 | ||
|
|
972076edab | ||
|
|
bfa9a1eb8a | ||
|
|
b0966f14e6 | ||
|
|
37cf0c2fb6 | ||
|
|
4eb11c32b0 | ||
|
|
25457bc09a | ||
|
|
d5cfb5f733 | ||
|
|
79ba6e628e | ||
|
|
ef73add05a | ||
|
|
ec3d11fb27 | ||
|
|
3fbc7031b5 | ||
|
|
40beb5e428 | ||
|
|
0f608c8ef0 | ||
|
|
8509cb4743 | ||
|
|
f4429e8c4a | ||
|
|
e9e0829cdc | ||
|
|
93f0230423 | ||
|
|
f26a0df4a4 | ||
|
|
c7d4624282 | ||
|
|
b03eee2a22 | ||
|
|
7d4d72e706 | ||
|
|
e3c367984b | ||
|
|
0ebfa10d32 | ||
|
|
16d9a077f0 | ||
|
|
a9d8bbad42 | ||
|
|
d78bb94af3 | ||
|
|
b139c0a824 | ||
|
|
5b0b924d34 | ||
|
|
f75acf820c | ||
|
|
43a47f33ac | ||
|
|
fcea3a0877 | ||
|
|
a950ec3bd5 | ||
|
|
e35501ff0a | ||
|
|
4bfad9dad8 | ||
|
|
c5c7a07c12 | ||
|
|
7e22e0e552 | ||
|
|
84ec4bdc79 | ||
|
|
2bcad9b637 | ||
|
|
c8d5f03070 | ||
|
|
168e101a5a | ||
|
|
b4bc7333d9 | ||
|
|
e8b58c940e | ||
|
|
ec7202e3f6 | ||
|
|
9a740f7962 | ||
|
|
9210fdee0d | ||
|
|
d7661f0964 | ||
|
|
139e148912 | ||
|
|
1b8dedb4ed | ||
|
|
5d6b0fa329 | ||
|
|
f35bbd89c9 | ||
|
|
538a9e42f4 | ||
|
|
623147dea7 | ||
|
|
dfecd0cd12 | ||
|
|
fe3af3a676 | ||
|
|
25f60331ed | ||
|
|
d4860a3426 | ||
|
|
e7e77ed86b | ||
|
|
dc80459c59 | ||
|
|
2f2de59234 | ||
|
|
7bccc21878 | ||
|
|
40f9dafa44 | ||
|
|
355d436d29 | ||
|
|
079b684388 | ||
|
|
fd11f46d30 | ||
|
|
cb7099199a | ||
|
|
8566d91e89 | ||
|
|
f44ce49ea7 | ||
|
|
6ef69f6b32 | ||
|
|
f5983d5f10 | ||
|
|
54cce5e089 | ||
|
|
4e4e596a1e | ||
|
|
727a1f5ad1 | ||
|
|
85fa86625b | ||
|
|
2c91877f83 | ||
|
|
7d1fac44e9 | ||
|
|
2e34abfc0d | ||
|
|
81ba63e247 | ||
|
|
8b11a65522 | ||
|
|
7190ad1d15 | ||
|
|
1c9bae5df5 | ||
|
|
cc7fd73916 | ||
|
|
6d8725f268 | ||
|
|
373c7cdbc4 | ||
|
|
a4855bb33b | ||
|
|
57c1358ded | ||
|
|
eb4ce1feab | ||
|
|
838c17e144 | ||
|
|
4499cbca3c | ||
|
|
a835a4a2f7 | ||
|
|
9cc6a94353 | ||
|
|
5ed9d9c4a0 | ||
|
|
9c5ac7080d | ||
|
|
6346370e86 | ||
|
|
80697f8f30 | ||
|
|
18b8b56367 | ||
|
|
760aacca26 | ||
|
|
1a4f0dcf5a | ||
|
|
947484a71c | ||
|
|
a74439d038 | ||
|
|
c338618593 | ||
|
|
011897da53 | ||
|
|
3fcaa58947 | ||
|
|
ef8bd4362a | ||
|
|
627a2ef6dd | ||
|
|
2732536d6e | ||
|
|
5a1b4b3ff8 | ||
|
|
d93ec82e4f | ||
|
|
15080972f3 | ||
|
|
171b58f737 | ||
|
|
c008ab6141 | ||
|
|
f14c3654dc | ||
|
|
ae05a61551 | ||
|
|
8e1def225b | ||
|
|
6e061764ee | ||
|
|
ac55b22839 | ||
|
|
49f77d3b75 | ||
|
|
4abc650edf | ||
|
|
5ba00b61be | ||
|
|
bc16a6c4cb | ||
|
|
ea4dc6f040 | ||
|
|
749ae8d5eb | ||
|
|
7a56ffb7c3 | ||
|
|
e62ab23de2 | ||
|
|
c6f6118506 | ||
|
|
8a5d5ad952 | ||
|
|
49e2615d14 | ||
|
|
9289394261 | ||
|
|
8da4c88fd3 | ||
|
|
a303850341 | ||
|
|
fb33610672 | ||
|
|
d024dd6563 | ||
|
|
0be48f9f59 | ||
|
|
df9292bafe | ||
|
|
c1dcef3477 | ||
|
|
48bc1f8361 | ||
|
|
2b2b4dbcf4 | ||
|
|
a1eadecdef | ||
|
|
f0b529952d | ||
|
|
c1ac2debb8 | ||
|
|
ac40094d37 | ||
|
|
cb2bb4cb67 | ||
|
|
f2965940cc | ||
|
|
c9ca147898 | ||
|
|
c379d7f846 | ||
|
|
45ae1ed265 | ||
|
|
9bf00eff40 | ||
|
|
1677b3d5b9 | ||
|
|
2a6806004a | ||
|
|
39347d69df | ||
|
|
a2c0e4d4b1 | ||
|
|
ab2ffd9ac1 | ||
|
|
c69fff52cc | ||
|
|
1cfe61dc72 | ||
|
|
a23f39d81e | ||
|
|
b7724ff583 | ||
|
|
e5dba60fab | ||
|
|
2ccf489a83 | ||
|
|
068939ca0b | ||
|
|
94ba8614ec | ||
|
|
f12a0c2379 | ||
|
|
8ab257645b | ||
|
|
6331a0615f | ||
|
|
a21855fa20 | ||
|
|
12150c2180 | ||
|
|
d90aecb164 | ||
|
|
e738f2bc9f | ||
|
|
2f72c41cda | ||
|
|
aa43d42cdb | ||
|
|
be8228e33c | ||
|
|
5591472dbd | ||
|
|
30e6ced4e9 | ||
|
|
5f4c2bae89 | ||
|
|
0f036d9a43 | ||
|
|
1695ac3a32 | ||
|
|
07a19ba619 | ||
|
|
4dd78d89a0 | ||
|
|
8f4056faa6 | ||
|
|
7b40c33892 | ||
|
|
b06bb5142f | ||
|
|
d576156ee1 | ||
|
|
77079968a5 | ||
|
|
e12aca6214 | ||
|
|
2a72891f28 | ||
|
|
6fe47e78f1 | ||
|
|
0094894b52 | ||
|
|
0951bfb7ac | ||
|
|
60e8519b65 | ||
|
|
78df0ed707 | ||
|
|
e3da0d6c9f | ||
|
|
5df30580a6 | ||
|
|
4816b7dcd8 | ||
|
|
75bced198b | ||
|
|
b500813617 | ||
|
|
a80037f7fc | ||
|
|
1a6345342f | ||
|
|
21b2193cd0 | ||
|
|
3efc496c41 | ||
|
|
16340fbb78 | ||
|
|
a858b28bc4 | ||
|
|
307961cc7e | ||
|
|
3074377b55 | ||
|
|
5a3edc00ac | ||
|
|
5a5f50e1e4 | ||
|
|
7f39a38d6c | ||
|
|
8321a48af7 | ||
|
|
7e613f032e | ||
|
|
1c38c39db2 | ||
|
|
4a0235c2ed | ||
|
|
b92961fda0 | ||
|
|
a2a06be62f | ||
|
|
c0956ed3b0 | ||
|
|
3705e4e4d4 | ||
|
|
3fdbe84573 | ||
|
|
fb8e7d803f | ||
|
|
b01821aa03 | ||
|
|
69f974c9d0 | ||
|
|
5db8b743fe | ||
|
|
c424b0c888 | ||
|
|
d035c7d3b8 | ||
|
|
144ffbc428 | ||
|
|
afa8486d40 | ||
|
|
a4cd42c448 | ||
|
|
853f4b75fa | ||
|
|
b95612287f | ||
|
|
64e779172c | ||
|
|
10a136b4a3 | ||
|
|
ddc3af28ab | ||
|
|
f602948dcf | ||
|
|
973229cf4e | ||
|
|
d1f5cbea4a | ||
|
|
49fe670ddb | ||
|
|
d6d87675c9 | ||
|
|
1ca1927904 | ||
|
|
454c3d1a3c | ||
|
|
11f5004112 | ||
|
|
625343f698 | ||
|
|
89d96a3ec1 | ||
|
|
c4c47fa196 | ||
|
|
ca35894b82 | ||
|
|
0213b6556b | ||
|
|
4edfd9be30 | ||
|
|
e55d9cafb6 | ||
|
|
8deb0ed556 | ||
|
|
82f995f243 | ||
|
|
b41c2ca724 | ||
|
|
06ddbaf2cc | ||
|
|
2adbfe1fbd | ||
|
|
7ae049b559 | ||
|
|
72913ceb1a | ||
|
|
6a2be22fa1 | ||
|
|
eaf20ce8d2 | ||
|
|
97f9e142b4 | ||
|
|
2e0f7b367f | ||
|
|
7312e3f452 | ||
|
|
8faa9f075b | ||
|
|
584dcae075 | ||
|
|
910e869b8d | ||
|
|
469e00b396 | ||
|
|
ec46c758ba | ||
|
|
8970e46bce | ||
|
|
3a4107d903 | ||
|
|
52ee21d550 | ||
|
|
cdda4826cb | ||
|
|
6745df5041 | ||
|
|
3d3efe1117 | ||
|
|
82e5ae05ce | ||
|
|
0dbdd55a6a | ||
|
|
424b0e61cb | ||
|
|
533da8f89c | ||
|
|
31506662db | ||
|
|
7ff1a88ca8 | ||
|
|
1851f26e3f | ||
|
|
84cd65dd6c | ||
|
|
8e0d792bf0 | ||
|
|
e74548b991 | ||
|
|
2356ff5ebb | ||
|
|
691f5d99ca | ||
|
|
ab7b65a30b | ||
|
|
814bb3006c | ||
|
|
4bd4d3ae03 | ||
|
|
af1b0ace19 | ||
|
|
fb54febe03 | ||
|
|
28faae5a4a | ||
|
|
624a920aec | ||
|
|
2bf8187bff | ||
|
|
cf06bf91e3 | ||
|
|
e96f0504c0 | ||
|
|
5fd61725bd | ||
|
|
3fddfa075b | ||
|
|
d712e5c409 | ||
|
|
6287afae6c | ||
|
|
925540055a | ||
|
|
d4373aae93 | ||
|
|
c4be310081 | ||
|
|
d1aa276465 | ||
|
|
2af6fc7452 | ||
|
|
ed15394a96 | ||
|
|
3791d11162 | ||
|
|
032ddabc06 | ||
|
|
bfb93ac6ed | ||
|
|
f6d889d752 | ||
|
|
a7053f4269 | ||
|
|
233a6d06be | ||
|
|
853224148f | ||
|
|
c5c8c27b2e | ||
|
|
d352d5fcff | ||
|
|
0003de9320 | ||
|
|
04f43e77af | ||
|
|
be7cc55488 | ||
|
|
c8f3379a48 | ||
|
|
b5a7945e49 | ||
|
|
0bdac2e97d | ||
|
|
1468a821fb | ||
|
|
3cdc8dc4b6 | ||
|
|
aa255aa7e6 | ||
|
|
66e5ccb9cc | ||
|
|
2215f300bf | ||
|
|
eec767406b | ||
|
|
31aa42c2fa | ||
|
|
e912c59402 | ||
|
|
c9988976f3 | ||
|
|
443be1c2c8 | ||
|
|
7f442cff3b | ||
|
|
3696ae44ad | ||
|
|
fc2d601424 | ||
|
|
8818f24114 | ||
|
|
0e12c8249e | ||
|
|
06d62a70a9 | ||
|
|
76d8018ca2 | ||
|
|
4123b41a5e | ||
|
|
5930257fed | ||
|
|
d3d60327ab | ||
|
|
5080ffb9fc | ||
|
|
9b688a5179 | ||
|
|
a603dc5227 | ||
|
|
c25f682caf | ||
|
|
27a2fd298d | ||
|
|
bb38053cb3 | ||
|
|
5e82ee8695 | ||
|
|
6c691ff9a8 | ||
|
|
ac5a14fe4a | ||
|
|
00402d13ef | ||
|
|
079a559247 | ||
|
|
a19ea8fdba | ||
|
|
c6e172f942 | ||
|
|
bdc9f3e8bf | ||
|
|
8ec5a587fc | ||
|
|
9caf46f2fb | ||
|
|
13fdbfc5e8 | ||
|
|
882c94110e | ||
|
|
97bc980611 | ||
|
|
be9bf5c173 | ||
|
|
6df38c389c | ||
|
|
6c6bceb8cc | ||
|
|
b5a897bb4d | ||
|
|
ab85b716bb | ||
|
|
8e256e6d5c | ||
|
|
288036a63b | ||
|
|
f2005c3343 | ||
|
|
79d516b899 | ||
|
|
28b2f6eae3 | ||
|
|
09ed2b945c | ||
|
|
74aec89ef0 | ||
|
|
4faa23d099 | ||
|
|
5c4997ab20 | ||
|
|
cd490ca946 | ||
|
|
793f6b00e6 | ||
|
|
c1ec568fda | ||
|
|
4e2b52975e | ||
|
|
c141df6b86 | ||
|
|
63b781765a | ||
|
|
c121e141e7 | ||
|
|
7544c9a9f5 | ||
|
|
722a088515 | ||
|
|
0ce75bff58 | ||
|
|
3744b6d3de | ||
|
|
a4ebd91e8d | ||
|
|
3bb0ee916d | ||
|
|
fd6afdf5f3 | ||
|
|
47c13a840e | ||
|
|
5b61992b3c | ||
|
|
36331dc253 | ||
|
|
4265cf31b2 | ||
|
|
337e47269f | ||
|
|
7039234471 | ||
|
|
f7f9333d91 | ||
|
|
bf35665932 | ||
|
|
089a2271c2 | ||
|
|
f8e83e3631 | ||
|
|
46fd329913 | ||
|
|
6b9ba96e77 | ||
|
|
b7d360d850 | ||
|
|
8e226302ab | ||
|
|
7795b9edaf | ||
|
|
9375d9699a | ||
|
|
cf0442d5b8 | ||
|
|
b386ca14df | ||
|
|
ea47fae31e | ||
|
|
e0fed07b10 | ||
|
|
779d5ff7b6 | ||
|
|
eb6fbd03ec | ||
|
|
95409d1b0e | ||
|
|
49599c8731 | ||
|
|
572f94e000 | ||
|
|
c0a2ad5f50 | ||
|
|
71fa5acc74 | ||
|
|
bac5b7679d | ||
|
|
93ade821a5 | ||
|
|
a718e19979 | ||
|
|
8ac83a46f5 | ||
|
|
1b65dcd7df | ||
|
|
bbad45f1e7 | ||
|
|
331b9cca18 | ||
|
|
a9accb7d85 | ||
|
|
1862e70628 | ||
|
|
c4f7054ca6 | ||
|
|
175e568a28 | ||
|
|
c8d580e7de | ||
|
|
bc0c50ee65 | ||
|
|
45e9dd96d1 | ||
|
|
1cc73562a3 | ||
|
|
d870ef0bd5 | ||
|
|
53d308dac5 | ||
|
|
89b06ae7c7 | ||
|
|
d38485a8ad | ||
|
|
834877c503 | ||
|
|
d033b79af4 | ||
|
|
daec2cc203 | ||
|
|
4e593cebab | ||
|
|
73d7701e94 | ||
|
|
24f1d7a72f | ||
|
|
6387a01d7b | ||
|
|
e838840548 | ||
|
|
6a430b441e | ||
|
|
7b977ea839 | ||
|
|
62b8521cbe | ||
|
|
308244d901 | ||
|
|
6f521183f9 | ||
|
|
76c6f7e733 | ||
|
|
80ebfbeb6b | ||
|
|
793901b319 | ||
|
|
3950df8ec9 | ||
|
|
e800b236aa | ||
|
|
4ab7871106 | ||
|
|
3de85549b6 | ||
|
|
73164f7182 | ||
|
|
004b000890 | ||
|
|
d9c703d944 | ||
|
|
364b650033 | ||
|
|
156eb874db | ||
|
|
1a28dd0311 | ||
|
|
e29b4b8609 | ||
|
|
c02997e6d9 | ||
|
|
7c9fc91af9 | ||
|
|
cf5198ac64 | ||
|
|
e76ddd6dd2 | ||
|
|
e3a4cf1cf5 | ||
|
|
5844616ea8 | ||
|
|
abeb580228 | ||
|
|
4d888dfce8 | ||
|
|
08ff6f0ede | ||
|
|
c458c27231 | ||
|
|
9821b70c38 | ||
|
|
8ab8401110 | ||
|
|
4f798c85cf | ||
|
|
1949bdb43f | ||
|
|
8cf2e6b31b | ||
|
|
b1069f9f18 | ||
|
|
fb8dfa9ae9 | ||
|
|
4402a56e94 | ||
|
|
f449808ba3 | ||
|
|
5599739433 | ||
|
|
197cf85f56 | ||
|
|
8c039c9c8b | ||
|
|
c1d9bc046d | ||
|
|
cd99fac7ed | ||
|
|
f4489e6807 | ||
|
|
f2078271b6 | ||
|
|
a3ae9acebb | ||
|
|
b0580265ca | ||
|
|
b4f012392a | ||
|
|
1598809f55 | ||
|
|
6d4f7aa61f | ||
|
|
382080ae4e | ||
|
|
66ac529bca | ||
|
|
ab72207027 | ||
|
|
e3aebf1ca2 | ||
|
|
0e5820c038 | ||
|
|
780e4d34df | ||
|
|
a8700572b7 | ||
|
|
055516312a | ||
|
|
76b4a6585e | ||
|
|
b57535c5ad | ||
|
|
eb10a15eee | ||
|
|
30ed362a8c | ||
|
|
34f071ed1d | ||
|
|
3d3d641e1c | ||
|
|
10c11f2c3d | ||
|
|
cec2745dc0 | ||
|
|
8d15edb063 | ||
|
|
38cf3dc141 | ||
|
|
c146290e07 | ||
|
|
a1745289be | ||
|
|
5a7dbfc87a | ||
|
|
f645950a8f | ||
|
|
b273da0b5e | ||
|
|
689f1eb0c5 | ||
|
|
b0a2edf5fa | ||
|
|
e7b5e02657 | ||
|
|
2d4efd6cf0 | ||
|
|
defc0ada78 | ||
|
|
2f3f4f609c | ||
|
|
4a9c9f8cd4 | ||
|
|
bdc089290d | ||
|
|
cf9f48d8da | ||
|
|
2d67279180 | ||
|
|
83e10aac27 | ||
|
|
9972124aa1 | ||
|
|
d0eb1ba96e | ||
|
|
f5d2910638 | ||
|
|
1cafaf3a79 | ||
|
|
fccc133cb2 | ||
|
|
9fb50b8a73 | ||
|
|
c66c1e17d3 | ||
|
|
4496760340 | ||
|
|
506200d2ee | ||
|
|
04898a3b01 | ||
|
|
f67fb53308 | ||
|
|
c5b78dde04 | ||
|
|
57d9c87de6 | ||
|
|
795f95d855 | ||
|
|
c0ebbc8e2f | ||
|
|
fb377c32ea | ||
|
|
e13faff2d7 | ||
|
|
b0c5348116 | ||
|
|
b462ec022a | ||
|
|
e45a0bf24b | ||
|
|
8962644ba8 | ||
|
|
ad5e04bbcc | ||
|
|
6fc7baec39 | ||
|
|
acd49a9c40 | ||
|
|
bf04358a47 | ||
|
|
b7e13c7b86 | ||
|
|
9b45f0661e | ||
|
|
2a61b1202b | ||
|
|
114f862ea4 | ||
|
|
5a490a8b5a | ||
|
|
8fdc1c1b21 | ||
|
|
32cac90720 | ||
|
|
b2160255d3 | ||
|
|
7fe1f4de93 | ||
|
|
1fbb1b1524 | ||
|
|
0105a53765 | ||
|
|
bd5ab80276 | ||
|
|
387d790228 | ||
|
|
d199a2be0d | ||
|
|
f81ecffda6 | ||
|
|
882f80de1e | ||
|
|
4359f2a0ce | ||
|
|
9c485c4d94 | ||
|
|
1330197036 | ||
|
|
0b0d5fa227 | ||
|
|
45424b8a52 | ||
|
|
0956acbdfb | ||
|
|
654f553d8f | ||
|
|
e4b6e20db6 | ||
|
|
b4f6c6f869 | ||
|
|
16edc52bae | ||
|
|
a33e6c03e4 | ||
|
|
4dccb40bf9 | ||
|
|
9ce1981f93 | ||
|
|
6374c77aa8 | ||
|
|
871bb391d6 | ||
|
|
c3903a7b35 | ||
|
|
6aabf82c11 | ||
|
|
89b624aadf | ||
|
|
67317292ee | ||
|
|
42e7f64856 | ||
|
|
79d963fd65 | ||
|
|
44649fd950 | ||
|
|
0e97f99f93 | ||
|
|
4f52ceb3e0 | ||
|
|
61253b5551 | ||
|
|
12bd6f0d9b | ||
|
|
a32010e03b | ||
|
|
4a934c9dab | ||
|
|
20766c1feb | ||
|
|
6cd4de548f | ||
|
|
8ac4dea8f1 | ||
|
|
e8af3e8d3c | ||
|
|
cd05b10168 | ||
|
|
140935bd8c | ||
|
|
ecb122d93c | ||
|
|
c6e08e0039 | ||
|
|
be1e14df81 | ||
|
|
28bca261fb | ||
|
|
2ab5bc1ad2 | ||
|
|
10c57cf6da | ||
|
|
e65c5eeab1 | ||
|
|
85fad35dc2 | ||
|
|
c992768efe | ||
|
|
7b13c0d059 | ||
|
|
2d3f41da6f | ||
|
|
43b9941dc8 | ||
|
|
dcf27f54aa | ||
|
|
fbf6b69e75 | ||
|
|
0bfebd90da | ||
|
|
e7c3dafa36 | ||
|
|
e90a36da79 | ||
|
|
93f33615ad | ||
|
|
99569081c9 | ||
|
|
588a0b3c41 | ||
|
|
4a1118ceb3 | ||
|
|
a65415bc10 | ||
|
|
8a0e66bf11 | ||
|
|
02cda47c28 | ||
|
|
d34a323a81 | ||
|
|
4166ae8db0 | ||
|
|
955b906b52 | ||
|
|
0d424aa81e | ||
|
|
80acbfa56a | ||
|
|
a7cac24004 | ||
|
|
2927bcf09d | ||
|
|
08dee6bb4f | ||
|
|
08c92f906a | ||
|
|
8e9c9802d1 | ||
|
|
138df66d5e | ||
|
|
0a71347e9a | ||
|
|
2a541a7b9c | ||
|
|
705c12e8da | ||
|
|
cf33df1339 | ||
|
|
87e543b5ef | ||
|
|
81caec99b7 | ||
|
|
41484f8673 | ||
|
|
da0d61f36a | ||
|
|
6f3bc74db0 | ||
|
|
af3bd6ec2f | ||
|
|
b5eb13449b | ||
|
|
bd78e8c275 | ||
|
|
2df21081a1 | ||
|
|
ffebff4ea9 | ||
|
|
2885bc99ca | ||
|
|
2657b80adb | ||
|
|
ebfc106701 | ||
|
|
4d60ca951d | ||
|
|
acf5c57599 | ||
|
|
927df5ff39 | ||
|
|
31fc031267 | ||
|
|
02794e0ebd | ||
|
|
ea6cce7068 | ||
|
|
e4cefeaa8f | ||
|
|
d12d5fd8ae | ||
|
|
0617c3fdc0 | ||
|
|
f00eedf57e | ||
|
|
6d36ae6197 | ||
|
|
45125abb8f | ||
|
|
045b0cd075 | ||
|
|
ff3333e1bf | ||
|
|
7663c5e149 | ||
|
|
4d4748a0a8 | ||
|
|
f8f84ed09e | ||
|
|
e7de7ebbfa | ||
|
|
c9f01f4bc4 | ||
|
|
f2675adc05 | ||
|
|
75beaa3684 | ||
|
|
079495cc32 | ||
|
|
c8bd89e56f | ||
|
|
b089ba2e04 | ||
|
|
adbf8495c6 | ||
|
|
ad062862d8 | ||
|
|
3a86a93154 | ||
|
|
dcf0d6f72d | ||
|
|
83d3725240 | ||
|
|
ce1dd69557 | ||
|
|
4081bdd752 | ||
|
|
c3f1e312b3 | ||
|
|
d820878b89 | ||
|
|
7fa1461d5e | ||
|
|
f4b1ef4d04 | ||
|
|
656130a739 | ||
|
|
aa8679dff5 | ||
|
|
f94a3095fd | ||
|
|
e11958dd58 | ||
|
|
a5a251a964 | ||
|
|
8cb1015a35 | ||
|
|
b5dd90b2d5 | ||
|
|
5f7efee00e | ||
|
|
4150e3efde | ||
|
|
8ebcb71e6e | ||
|
|
f3e852c042 | ||
|
|
5e2a07d144 | ||
|
|
25f6231e9d | ||
|
|
beeba88ea5 | ||
|
|
cc3d454d60 | ||
|
|
afb583cff4 | ||
|
|
561fa66393 | ||
|
|
870dc0d36f | ||
|
|
51462dee1e | ||
|
|
c752d28c6a | ||
|
|
d5ca0ca283 | ||
|
|
f371b3a338 | ||
|
|
93fc4a2c86 | ||
|
|
f10d3f86cc | ||
|
|
af17b0015b | ||
|
|
61af1d1c72 | ||
|
|
65780e1672 | ||
|
|
c92a7967ea | ||
|
|
60aed593b3 | ||
|
|
044f347729 | ||
|
|
aec9df1882 | ||
|
|
4f0a2515f8 | ||
|
|
5cde33711e | ||
|
|
64750025f6 | ||
|
|
cd1cbfdffe | ||
|
|
d413f2c3a7 | ||
|
|
ba314dd734 | ||
|
|
9105b7615c | ||
|
|
a9c162476c | ||
|
|
732b37aca0 | ||
|
|
291b5fad7f | ||
|
|
c2a6def8b9 | ||
|
|
9083c578cc | ||
|
|
bcfd1d39bb | ||
|
|
1185b910c4 | ||
|
|
540b6eba04 | ||
|
|
47f4287e64 | ||
|
|
264c6e259b | ||
|
|
0bbe9838c4 | ||
|
|
2fe337eac3 | ||
|
|
5d7caafdf7 | ||
|
|
f416ef925b | ||
|
|
60bd90848b | ||
|
|
b9f4407815 | ||
|
|
f9cd2639ff | ||
|
|
aeb36e8665 | ||
|
|
057482a3e5 | ||
|
|
15721da46e | ||
|
|
f12b82b5ce | ||
|
|
10dc725942 | ||
|
|
5bd5cdf435 | ||
|
|
84b3603c08 | ||
|
|
8f59b96731 | ||
|
|
e1de110dd7 | ||
|
|
8b6fd3d594 | ||
|
|
d7761e8d79 | ||
|
|
751d652a2f | ||
|
|
647e7e708a | ||
|
|
505c0eeae2 | ||
|
|
bf4001968e | ||
|
|
9d222e2c57 | ||
|
|
033300d659 | ||
|
|
4f2b04bd8f | ||
|
|
f8b9bb4b0f | ||
|
|
8ce8e320c3 | ||
|
|
7c0ab4212b | ||
|
|
26633e0982 | ||
|
|
d27a3f1f33 | ||
|
|
89252d0dba | ||
|
|
dbd2edf442 | ||
|
|
dabd6f8284 | ||
|
|
beb3ab463f | ||
|
|
1d67b623e0 | ||
|
|
2c8cde4d91 | ||
|
|
8cc1d48115 | ||
|
|
ec2468fb8d | ||
|
|
7b54cef23b | ||
|
|
a9da8811fc | ||
|
|
60e86b9881 | ||
|
|
a6766f3c99 | ||
|
|
e59c3c6f70 | ||
|
|
49aa344d8b | ||
|
|
4fed6ab298 | ||
|
|
58eab4d3bc | ||
|
|
5ef5da687d | ||
|
|
4875d319dc | ||
|
|
fb5a53435e | ||
|
|
4f805d65b3 | ||
|
|
56ffb0deb1 | ||
|
|
c0c1457073 | ||
|
|
85a0748ad9 | ||
|
|
c1939a36dd | ||
|
|
b7c394b7a5 | ||
|
|
a070681f89 | ||
|
|
d5e424eec8 | ||
|
|
e0366d38f1 | ||
|
|
cbf1d96b16 | ||
|
|
7cc0d6bb5a | ||
|
|
e19b840ee6 | ||
|
|
5c2ca1e3d9 | ||
|
|
059c4beb30 | ||
|
|
427808226f | ||
|
|
5448245942 | ||
|
|
34ab907007 | ||
|
|
e5fde27859 | ||
|
|
699e8e3ddb | ||
|
|
28ee967371 | ||
|
|
1b0b5f2554 | ||
|
|
1bcc86f989 | ||
|
|
f26f932fd7 | ||
|
|
2b7d48ce77 | ||
|
|
111712fd6d | ||
|
|
7609bc181e | ||
|
|
8b05af7ca3 | ||
|
|
20f9108ebf | ||
|
|
cecae7cdc3 | ||
|
|
e5f3123567 | ||
|
|
27b0e27cfd | ||
|
|
890fba0f61 | ||
|
|
c4b732ff93 | ||
|
|
b8fa2985d5 | ||
|
|
ba741fbce8 | ||
|
|
67d070c334 | ||
|
|
8576afe6f5 | ||
|
|
36807fd376 | ||
|
|
2ad1d27a59 | ||
|
|
7cc9c75d15 | ||
|
|
f33609bbf8 | ||
|
|
69eeb4b0f8 | ||
|
|
aa583ec1aa | ||
|
|
2b9f153257 | ||
|
|
cb9b7fc917 | ||
|
|
74b36c8884 | ||
|
|
89ff7d6dae | ||
|
|
91df420cb4 | ||
|
|
fc25c9f0bc | ||
|
|
12a27e3a8d | ||
|
|
125e32ff00 | ||
|
|
4a110633e8 | ||
|
|
65648f8abd | ||
|
|
50174861dc | ||
|
|
486eb1722e | ||
|
|
740ead4059 | ||
|
|
96424be0da | ||
|
|
cd9d659672 | ||
|
|
ca140388d9 | ||
|
|
8fe0229a2c | ||
|
|
1f43de9458 | ||
|
|
2793f38c2d | ||
|
|
42de7de21d | ||
|
|
1072dc0a41 | ||
|
|
9d8f310e30 | ||
|
|
170adfd00c | ||
|
|
d0135a5ff7 | ||
|
|
1c926cca45 | ||
|
|
ace8ecbb4e | ||
|
|
193fa176dc | ||
|
|
054db62cfa | ||
|
|
fe549cf4c5 | ||
|
|
d347e49b6a | ||
|
|
d6a3f7b329 | ||
|
|
4171bc4c70 | ||
|
|
deca5e5021 | ||
|
|
e0923a0494 | ||
|
|
0a1dfeb860 | ||
|
|
1c911575fa | ||
|
|
d2ef0a996f | ||
|
|
1e886cb12c | ||
|
|
984abc89a8 | ||
|
|
3c6c9741ff | ||
|
|
247a11146c | ||
|
|
d1108c533f | ||
|
|
881339848f | ||
|
|
c44638dcbe | ||
|
|
4887b85b8a | ||
|
|
239f58e290 | ||
|
|
560000f69d | ||
|
|
2d3509ae56 | ||
|
|
4cc926a627 | ||
|
|
a499a70633 | ||
|
|
60a9154326 | ||
|
|
f761b8d4e1 | ||
|
|
7a0f6684e5 | ||
|
|
3ed6817ac9 | ||
|
|
ba76385a2f | ||
|
|
7bcd5ba14c | ||
|
|
40db9f7020 | ||
|
|
dffc46551e | ||
|
|
cf92852bb3 | ||
|
|
844c4a28f4 | ||
|
|
d2fc5a6228 | ||
|
|
006da837aa | ||
|
|
d920e27ab3 | ||
|
|
27bafa8ab2 | ||
|
|
907d18a83a | ||
|
|
37b923bea3 | ||
|
|
91e597bbdd | ||
|
|
a0d697bf6f | ||
|
|
63d5018ad6 | ||
|
|
ca928bdacb | ||
|
|
f7e7791b8b | ||
|
|
80c2e5b141 | ||
|
|
63c171e3b4 | ||
|
|
b0bfb0fdd4 | ||
|
|
31b24d9a09 | ||
|
|
8b04a3b16a | ||
|
|
b88d35d7cb | ||
|
|
e00dcf1af0 | ||
|
|
7f23b9b424 | ||
|
|
e9bf04031b | ||
|
|
c8f2334003 | ||
|
|
380b84195f | ||
|
|
fd26137ad2 | ||
|
|
1ad163aac3 | ||
|
|
36dccc8157 | ||
|
|
6dcdf5bf92 | ||
|
|
0b460017b8 | ||
|
|
061b06cd08 | ||
|
|
86272e1788 | ||
|
|
86f3e011d8 | ||
|
|
0367c5bd1c | ||
|
|
eed008de4b | ||
|
|
74f713ee35 | ||
|
|
6dbb8751b2 | ||
|
|
368bb54870 | ||
|
|
77903a5ecd | ||
|
|
9be161d165 | ||
|
|
0ce5b50950 | ||
|
|
9dded5203e | ||
|
|
d1c2188e5c | ||
|
|
c4285a14b2 | ||
|
|
cf0a47e836 | ||
|
|
c38639afcb | ||
|
|
5863593c65 | ||
|
|
c2fa7f9a57 | ||
|
|
41f5cf6bee | ||
|
|
ea508e9c35 |
417
.circleci/config.yml
Normal file
417
.circleci/config.yml
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
version: 2.1
|
||||||
|
commands:
|
||||||
|
|
||||||
|
cmake:
|
||||||
|
description: Configure build
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Configure build
|
||||||
|
command: cmake ..
|
||||||
|
working_directory: build
|
||||||
|
|
||||||
|
|
||||||
|
build_source:
|
||||||
|
description: Create source tarball
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Create source tarball
|
||||||
|
command: ../dist/scripts/maketarball.sh
|
||||||
|
working_directory: build
|
||||||
|
|
||||||
|
|
||||||
|
build_rpm:
|
||||||
|
description: Build RPM
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Create RPM build sources directories
|
||||||
|
command: mkdir -p ~/rpmbuild/SOURCES /usr/src/packages/SOURCES
|
||||||
|
- run:
|
||||||
|
name: Copy source tarball 1
|
||||||
|
command: cp strawberry-*.tar.xz ~/rpmbuild/SOURCES/
|
||||||
|
working_directory: build
|
||||||
|
- run:
|
||||||
|
name: Copy source tarball 2
|
||||||
|
command: cp strawberry-*.tar.xz /usr/src/packages/SOURCES/
|
||||||
|
working_directory: build
|
||||||
|
- run:
|
||||||
|
name: Build RPM
|
||||||
|
command: rpmbuild -ba ../dist/unix/strawberry.spec
|
||||||
|
working_directory: build
|
||||||
|
|
||||||
|
|
||||||
|
build_deb:
|
||||||
|
description: Build Deb
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: make deb
|
||||||
|
command: dpkg-buildpackage -b -d -uc -us -nc -j2
|
||||||
|
|
||||||
|
|
||||||
|
install_opensuse_dependencies:
|
||||||
|
description: Install openSUSE dependencies
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Update packages
|
||||||
|
command: zypper --non-interactive --gpg-auto-import-keys ref
|
||||||
|
- run:
|
||||||
|
name: Install openSUSE dependencies
|
||||||
|
command: >
|
||||||
|
zypper --non-interactive --gpg-auto-import-keys install
|
||||||
|
lsb-release
|
||||||
|
rpm-build
|
||||||
|
git
|
||||||
|
tar
|
||||||
|
make
|
||||||
|
cmake
|
||||||
|
gcc
|
||||||
|
gcc-c++
|
||||||
|
gettext-tools
|
||||||
|
glibc-devel
|
||||||
|
libboost_headers-devel
|
||||||
|
boost-devel
|
||||||
|
glib2-devel
|
||||||
|
glib2-tools
|
||||||
|
dbus-1-devel
|
||||||
|
alsa-devel
|
||||||
|
libnotify-devel
|
||||||
|
libgnutls-devel
|
||||||
|
protobuf-devel
|
||||||
|
sqlite3-devel
|
||||||
|
libpulse-devel
|
||||||
|
gstreamer-devel
|
||||||
|
gstreamer-plugins-base-devel
|
||||||
|
taglib-devel
|
||||||
|
vlc-devel
|
||||||
|
libQt5Core-devel
|
||||||
|
libQt5Gui-devel
|
||||||
|
libQt5Widgets-devel
|
||||||
|
libQt5Concurrent-devel
|
||||||
|
libQt5Network-devel
|
||||||
|
libQt5Sql-devel
|
||||||
|
libQt5DBus-devel
|
||||||
|
libQt5Test-devel
|
||||||
|
libqt5-qtx11extras-devel
|
||||||
|
libqt5-qtbase-common-devel
|
||||||
|
libQt5Sql5-sqlite
|
||||||
|
libqt5-linguist-devel
|
||||||
|
libcdio-devel
|
||||||
|
libgpod-devel
|
||||||
|
libmtp-devel
|
||||||
|
libchromaprint-devel
|
||||||
|
desktop-file-utils
|
||||||
|
update-desktop-files
|
||||||
|
appstream-glib
|
||||||
|
hicolor-icon-theme
|
||||||
|
|
||||||
|
|
||||||
|
install_fedora_dependencies:
|
||||||
|
description: Install Fedora dependencies
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Update packages
|
||||||
|
command: yum update --assumeyes
|
||||||
|
- run:
|
||||||
|
name: Upgrade packages
|
||||||
|
command: yum upgrade --assumeyes
|
||||||
|
- run:
|
||||||
|
name: Install Fedora dependencies
|
||||||
|
command: >
|
||||||
|
dnf install --assumeyes
|
||||||
|
@development-tools
|
||||||
|
redhat-lsb-core
|
||||||
|
git
|
||||||
|
glibc
|
||||||
|
gcc-c++
|
||||||
|
rpmdevtools
|
||||||
|
make
|
||||||
|
cmake
|
||||||
|
pkgconfig
|
||||||
|
glib
|
||||||
|
man
|
||||||
|
tar
|
||||||
|
gettext
|
||||||
|
openssh
|
||||||
|
boost-devel
|
||||||
|
dbus-devel
|
||||||
|
protobuf-devel
|
||||||
|
protobuf-compiler
|
||||||
|
sqlite-devel
|
||||||
|
alsa-lib-devel
|
||||||
|
pulseaudio-libs-devel
|
||||||
|
libnotify-devel
|
||||||
|
gnutls-devel
|
||||||
|
qt5-qtbase-devel
|
||||||
|
qt5-qtx11extras-devel
|
||||||
|
qt5-qttools-devel
|
||||||
|
gstreamer1-devel
|
||||||
|
gstreamer1-plugins-base-devel
|
||||||
|
taglib-devel
|
||||||
|
libcdio-devel
|
||||||
|
libgpod-devel
|
||||||
|
libmtp-devel
|
||||||
|
libchromaprint-devel
|
||||||
|
fftw-devel
|
||||||
|
desktop-file-utils
|
||||||
|
libappstream-glib
|
||||||
|
hicolor-icon-theme
|
||||||
|
|
||||||
|
|
||||||
|
install_debian_dependencies:
|
||||||
|
description: Install Debian dependencies
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Install Debian dependencies
|
||||||
|
command: >
|
||||||
|
apt-get update && apt-get install -y
|
||||||
|
build-essential
|
||||||
|
dh-make
|
||||||
|
ssh
|
||||||
|
git
|
||||||
|
make
|
||||||
|
cmake
|
||||||
|
gcc
|
||||||
|
pkg-config
|
||||||
|
fakeroot
|
||||||
|
gettext
|
||||||
|
lsb-release
|
||||||
|
libglib2.0-dev
|
||||||
|
dpkg-dev
|
||||||
|
libdbus-1-dev
|
||||||
|
libboost-dev
|
||||||
|
libprotobuf-dev
|
||||||
|
protobuf-compiler
|
||||||
|
libsqlite3-dev
|
||||||
|
libgnutls28-dev
|
||||||
|
libasound2-dev
|
||||||
|
libpulse-dev
|
||||||
|
libtag1-dev
|
||||||
|
qtbase5-dev
|
||||||
|
qtbase5-dev-tools
|
||||||
|
qtbase5-private-dev
|
||||||
|
libqt5x11extras5-dev
|
||||||
|
qttools5-dev
|
||||||
|
libgstreamer1.0-dev
|
||||||
|
libgstreamer-plugins-base1.0-dev
|
||||||
|
gstreamer1.0-alsa
|
||||||
|
gstreamer1.0-pulseaudio
|
||||||
|
libchromaprint-dev
|
||||||
|
libfftw3-dev
|
||||||
|
libcdio-dev
|
||||||
|
libmtp-dev
|
||||||
|
libgpod-dev
|
||||||
|
|
||||||
|
|
||||||
|
install_ubuntu_dependencies:
|
||||||
|
description: Install Ubuntu dependencies
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Setup environment
|
||||||
|
command: |
|
||||||
|
echo 'export DEBIAN_FRONTEND=noninteractive' >> $BASH_ENV
|
||||||
|
source $BASH_ENV
|
||||||
|
- run:
|
||||||
|
name: Install Ubuntu dependencies
|
||||||
|
command: >
|
||||||
|
apt-get update && apt-get install -y
|
||||||
|
build-essential
|
||||||
|
dh-make
|
||||||
|
ssh
|
||||||
|
git
|
||||||
|
make
|
||||||
|
cmake
|
||||||
|
pkg-config
|
||||||
|
gcc
|
||||||
|
fakeroot
|
||||||
|
wget
|
||||||
|
curl
|
||||||
|
gettext
|
||||||
|
lsb-release
|
||||||
|
dpkg-dev
|
||||||
|
libglib2.0-dev
|
||||||
|
libboost-dev
|
||||||
|
libdbus-1-dev
|
||||||
|
libprotobuf-dev
|
||||||
|
protobuf-compiler
|
||||||
|
libsqlite3-dev
|
||||||
|
libgnutls28-dev
|
||||||
|
libasound2-dev
|
||||||
|
libpulse-dev
|
||||||
|
libtag1-dev
|
||||||
|
qtbase5-dev
|
||||||
|
qtbase5-dev-tools
|
||||||
|
qtbase5-private-dev
|
||||||
|
libqt5x11extras5-dev
|
||||||
|
qttools5-dev
|
||||||
|
libgstreamer1.0-dev
|
||||||
|
libgstreamer-plugins-base1.0-dev
|
||||||
|
libgstreamer-plugins-good1.0-dev
|
||||||
|
gstreamer1.0-alsa
|
||||||
|
gstreamer1.0-pulseaudio
|
||||||
|
libchromaprint-dev
|
||||||
|
libfftw3-dev
|
||||||
|
libcdio-dev
|
||||||
|
libmtp-dev
|
||||||
|
libgpod-dev
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build_source:
|
||||||
|
docker:
|
||||||
|
- image: opensuse/leap:15.2
|
||||||
|
steps:
|
||||||
|
- install_opensuse_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_source
|
||||||
|
|
||||||
|
|
||||||
|
build_opensuse_lp151:
|
||||||
|
docker:
|
||||||
|
- image: opensuse/leap:15.1
|
||||||
|
environment:
|
||||||
|
RPM_BUILD_NCPUS: "2"
|
||||||
|
steps:
|
||||||
|
- install_opensuse_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_source
|
||||||
|
- build_rpm
|
||||||
|
|
||||||
|
build_opensuse_lp152:
|
||||||
|
docker:
|
||||||
|
- image: opensuse/leap:15.2
|
||||||
|
environment:
|
||||||
|
RPM_BUILD_NCPUS: "2"
|
||||||
|
steps:
|
||||||
|
- install_opensuse_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_source
|
||||||
|
- build_rpm
|
||||||
|
|
||||||
|
|
||||||
|
build_fedora_32:
|
||||||
|
docker:
|
||||||
|
- image: fedora:32
|
||||||
|
environment:
|
||||||
|
RPM_BUILD_NCPUS: "2"
|
||||||
|
steps:
|
||||||
|
- install_fedora_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_source
|
||||||
|
- build_rpm
|
||||||
|
|
||||||
|
build_fedora_33:
|
||||||
|
docker:
|
||||||
|
- image: fedora:33
|
||||||
|
environment:
|
||||||
|
RPM_BUILD_NCPUS: "2"
|
||||||
|
steps:
|
||||||
|
- install_fedora_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_source
|
||||||
|
- build_rpm
|
||||||
|
|
||||||
|
|
||||||
|
build_debian_buster:
|
||||||
|
docker:
|
||||||
|
- image: debian:buster
|
||||||
|
steps:
|
||||||
|
- install_debian_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_deb
|
||||||
|
|
||||||
|
build_debian_bullseye:
|
||||||
|
docker:
|
||||||
|
- image: debian:bullseye
|
||||||
|
steps:
|
||||||
|
- install_debian_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_deb
|
||||||
|
|
||||||
|
|
||||||
|
build_ubuntu_bionic:
|
||||||
|
docker:
|
||||||
|
- image: ubuntu:bionic
|
||||||
|
steps:
|
||||||
|
- install_ubuntu_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_deb
|
||||||
|
|
||||||
|
build_ubuntu_focal:
|
||||||
|
docker:
|
||||||
|
- image: ubuntu:focal
|
||||||
|
steps:
|
||||||
|
- install_ubuntu_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_deb
|
||||||
|
|
||||||
|
build_ubuntu_groovy:
|
||||||
|
docker:
|
||||||
|
- image: ubuntu:groovy
|
||||||
|
steps:
|
||||||
|
- install_ubuntu_dependencies
|
||||||
|
- checkout
|
||||||
|
- cmake
|
||||||
|
- build_deb
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
build_all:
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
- build_source:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
|
||||||
|
|
||||||
|
- build_opensuse_lp151:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
- build_opensuse_lp152:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
|
||||||
|
|
||||||
|
- build_fedora_32:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
- build_fedora_33:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
|
||||||
|
|
||||||
|
- build_debian_buster:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
- build_debian_bullseye:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
|
||||||
|
|
||||||
|
- build_ubuntu_bionic:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
- build_ubuntu_focal:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
|
- build_ubuntu_groovy:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
105
.clang-format
Normal file
105
.clang-format
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
BasedOnStyle: Chromium
|
||||||
|
Language: Cpp
|
||||||
|
Standard: Cpp11
|
||||||
|
AccessModifierOffset: -1
|
||||||
|
AlignAfterOpenBracket: false
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignConsecutiveMacros: true
|
||||||
|
AlignEscapedNewlines: true
|
||||||
|
AlignOperands: false
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: true
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
AllowShortLambdasOnASingleLine: true
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: No
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BreakBeforeBraces: false
|
||||||
|
BreakBeforeBinaryOperators: false
|
||||||
|
BreakBeforeBraces: Stroustrup
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakStringLiterals: false
|
||||||
|
ColumnLimit: 0
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 2
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: true
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: true
|
||||||
|
FixNamespaceComments: true
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: AfterHash
|
||||||
|
IndentWidth: 2
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MaxEmptyLinesToKeep: 100
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: false
|
||||||
|
PenaltyBreakAssignment: 0
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 0
|
||||||
|
PenaltyBreakComment: 0
|
||||||
|
PenaltyBreakFirstLessLess: 0
|
||||||
|
PenaltyBreakString: 0
|
||||||
|
PenaltyBreakTemplateDeclaration: 0
|
||||||
|
PenaltyExcessCharacter: 0
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 0
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: Never
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterFunction: false
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: true
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
github: jonaski
|
||||||
|
patreon: jonaskvinge
|
||||||
|
custom: https://paypal.me/jonaskvinge
|
||||||
34
.github/ISSUE_TEMPLATE.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For technical issues, questions and feature suggestions/requests please use the forum on https://forum.strawberrymusicplayer.org/
|
||||||
|
|
||||||
|
Check the Changelog to see if the issue is already fixed:
|
||||||
|
https://github.com/strawberrymusicplayer/strawberry/blob/master/Changelog
|
||||||
|
|
||||||
|
If it's fixed, try the latest development build from: https://builds.strawberrymusicplayer.org/
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior.
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots:**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**System Information:**
|
||||||
|
- Operating system:
|
||||||
|
- Strawberry Version:
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
1663
.github/workflows/ccpp.yml
vendored
Normal file
1663
.github/workflows/ccpp.yml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
.gitignore
vendored
6
.gitignore
vendored
@@ -73,6 +73,11 @@ CMakeLists.txt.user*
|
|||||||
target_wrapper.*
|
target_wrapper.*
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
|
||||||
|
*.kdev4
|
||||||
|
*.vscode
|
||||||
|
*.code-workspace
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
# Temporary files
|
# Temporary files
|
||||||
*~
|
*~
|
||||||
*.autosave
|
*.autosave
|
||||||
@@ -99,7 +104,6 @@ Thumbs.db
|
|||||||
|
|
||||||
# Stuff in dist
|
# Stuff in dist
|
||||||
maketarball.sh
|
maketarball.sh
|
||||||
create-dmg.sh
|
|
||||||
changelog
|
changelog
|
||||||
PKGBUILD
|
PKGBUILD
|
||||||
|
|
||||||
|
|||||||
74
.travis.yml
74
.travis.yml
@@ -1,53 +1,43 @@
|
|||||||
sudo: required
|
sudo: required
|
||||||
language: C++
|
language: C++
|
||||||
os:
|
os: osx
|
||||||
- linux
|
osx_image: xcode11.3
|
||||||
#- osx
|
compiler: clang
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
compiler:
|
|
||||||
- gcc
|
|
||||||
- clang
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- echo $DEPLOY_KEY_ENC | base64 --decode | openssl aes-256-cbc -K $encrypted_83a41ac424a6_key -iv $encrypted_83a41ac424a6_iv -out ~/.ssh/id_rsa -d
|
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
||||||
- chmod 600 ~/.ssh/id_rsa
|
echo $DEPLOY_KEY_ENC | base64 --decode | openssl aes-256-cbc -K $encrypted_83a41ac424a6_key -iv $encrypted_83a41ac424a6_iv -out ~/.ssh/id_rsa -d ;
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
chmod 600 ~/.ssh/id_rsa ;
|
||||||
docker build -f Dockerfile -t strawberry-build .;
|
|
||||||
docker run --name build -itd strawberry-build /bin/bash;
|
|
||||||
docker exec build git clone https://github.com/jonaski/strawberry;
|
|
||||||
fi
|
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
|
||||||
git fetch --unshallow;
|
|
||||||
git pull;
|
|
||||||
brew update;
|
|
||||||
brew unlink python;
|
|
||||||
brew install glib pkgconfig libffi protobuf protobuf-c qt gettext;
|
|
||||||
brew install sqlite --with-fts;
|
|
||||||
brew install gstreamer gst-plugins-base;
|
|
||||||
brew install gst-plugins-good --with-flac;
|
|
||||||
brew install gst-plugins-bad gst-plugins-ugly gst-libav;
|
|
||||||
brew install chromaprint;
|
|
||||||
brew install libcdio libmtp libimobiledevice libplist;
|
|
||||||
export Qt5_DIR=/usr/local/opt/qt5/lib/cmake;
|
|
||||||
export Qt5LinguistTools_DIR=/usr/local/Cellar/qt/$(ls /usr/local/Cellar/qt | head -n1)/lib/cmake/Qt5LinguistTools;
|
|
||||||
export PATH="/usr/local/opt/gettext/bin:$PATH";
|
|
||||||
ls /usr/local/lib/gstreamer-1.0;
|
|
||||||
fi
|
fi
|
||||||
|
- git fetch --unshallow
|
||||||
|
- git pull
|
||||||
|
- brew update
|
||||||
|
- travis_wait 400 brew upgrade || echo "Failed"
|
||||||
|
- travis_wait 400 brew upgrade || echo "Failed"
|
||||||
|
- brew install glib pkgconfig libffi protobuf protobuf-c qt gettext gnutls fftw sqlite chromaprint zlib taglib
|
||||||
|
- brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav
|
||||||
|
- brew install libcdio libmtp
|
||||||
|
- brew install create-dmg
|
||||||
|
- brew install --cask sparkle
|
||||||
|
- sudo ln -s /usr/local/Caskroom/sparkle/$(ls /usr/local/Caskroom/sparkle | head -n1) /usr/local/opt/sparkle
|
||||||
|
- export Qt6_DIR=/usr/local/opt/qt6/lib/cmake
|
||||||
|
- export Qt6LinguistTools_DIR=/usr/local/opt/qt6/lib/cmake/Qt6LinguistTools
|
||||||
|
- ls /usr/local/lib/gstreamer-1.0
|
||||||
before_script:
|
before_script:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild -DENABLE_TRANSLATIONS=ON ; fi
|
- mkdir build
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DUSE_BUNDLE=ON -DENABLE_TRANSLATIONS=ON ; fi
|
- cd build
|
||||||
|
- cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_WITH_QT6=ON -DBUILD_WERROR=ON -DUSE_BUNDLE=ON
|
||||||
script:
|
script:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build make -C build -j8 ; fi
|
- make -j8
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
- make install
|
||||||
make -j8;
|
- make dmg2
|
||||||
sudo make install;
|
|
||||||
sudo ../dist/macos/macdeploy.py strawberry.app;
|
|
||||||
../dist/macos/create-dmg.sh strawberry.app $CC_FOR_BUILD;
|
|
||||||
fi
|
|
||||||
after_success:
|
after_success:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ls -lh strawberry.dmg; fi
|
- ls -lh strawberry*.dmg
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$TRAVIS_BRANCH" == "master" ]]; then rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos; fi
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [ -f ~/.ssh/id_rsa ]; then
|
||||||
|
if [[ "$TRAVIS_BRANCH" == "master" ]] || [[ "$TRAVIS_BRANCH" == "travis" ]]; then
|
||||||
|
rsync -e "ssh -o StrictHostKeyChecking=no" -va strawberry*.dmg travis@echoes.jkvinge.net:/home/travis/builds/macos/mojave/;
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
except:
|
except:
|
||||||
|
|||||||
48
3rdparty/README.md
vendored
48
3rdparty/README.md
vendored
@@ -12,46 +12,14 @@ It is included here because it is not packed by distros and is also used on macO
|
|||||||
URL: https://github.com/itay-grudev/SingleApplication
|
URL: https://github.com/itay-grudev/SingleApplication
|
||||||
|
|
||||||
|
|
||||||
taglib
|
|
||||||
------
|
|
||||||
|
|
||||||
TagLib is a library for reading and editing the meta-data of several popular audio formats. It is also used
|
|
||||||
by Strawberry to identify audio files. It is important that it is kept up-to-date for Strawberry to function
|
|
||||||
correctly.
|
|
||||||
|
|
||||||
It is kept in 3rdparty because there currently is no offical release of TagLib with the features and bugfixes
|
|
||||||
that are in the official repository. And also because some distros use older, or unpatched versions.
|
|
||||||
|
|
||||||
There is a bug in the latest version (1.11.1) corrupting Ogg files,
|
|
||||||
see: https://github.com/taglib/taglib/issues/864
|
|
||||||
If you decide to use the systems taglib, make sure it has been patched with the following commit:
|
|
||||||
https://github.com/taglib/taglib/commit/9336c82da3a04552168f208cd7a5fa4646701ea4
|
|
||||||
|
|
||||||
The current taglib in 3rdparty also has the following features:
|
|
||||||
- Audio file detection by content.
|
|
||||||
- DSF and DSDIFF support
|
|
||||||
|
|
||||||
URL: https://github.com/taglib/taglib
|
|
||||||
|
|
||||||
|
|
||||||
utf8-cpp
|
|
||||||
--------
|
|
||||||
|
|
||||||
This is 2 header files used by taglib, but kept in a seperate directory because it is maintained by others.
|
|
||||||
|
|
||||||
URL: http://utfcpp.sourceforge.net/
|
|
||||||
|
|
||||||
|
|
||||||
qocoa
|
|
||||||
-----
|
|
||||||
This is a small static library currently used for the search fields above the collection, playlist and in
|
|
||||||
the cover manager. It is slightly modified from original version.
|
|
||||||
|
|
||||||
URL: https://github.com/mikemcquaid/Qocoa
|
|
||||||
|
|
||||||
|
|
||||||
SPMediaKeyTap
|
SPMediaKeyTap
|
||||||
-------------
|
-------------
|
||||||
|
Used on macOS to exclusively enable strawberry to grab global media shortcuts.
|
||||||
|
Can safely be deleted on other platforms.
|
||||||
|
|
||||||
This is used for macOS only to enable strawberry to grab global shortcuts and can safely be deleted on other
|
|
||||||
platforms.
|
macdeployqt
|
||||||
|
-----------
|
||||||
|
A modified version of Qt's official macdeployqt utility that fixes some issues,
|
||||||
|
this version also deploys gstreamer plugins.
|
||||||
|
Can safely be deleted on other platforms.
|
||||||
|
|||||||
2
3rdparty/SPMediaKeyTap/CMakeLists.txt
vendored
2
3rdparty/SPMediaKeyTap/CMakeLists.txt
vendored
@@ -1,11 +1,9 @@
|
|||||||
set(SPMEDIAKEY-SOURCES
|
set(SPMEDIAKEY-SOURCES
|
||||||
SPMediaKeyTap.m
|
SPMediaKeyTap.m
|
||||||
SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SPMEDIAKEY-HEADERS
|
set(SPMEDIAKEY-HEADERS
|
||||||
SPMediaKeyTap.h
|
SPMediaKeyTap.h
|
||||||
SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ADD_LIBRARY(SPMediaKeyTap STATIC
|
ADD_LIBRARY(SPMediaKeyTap STATIC
|
||||||
|
|||||||
4
3rdparty/SPMediaKeyTap/README.md
vendored
4
3rdparty/SPMediaKeyTap/README.md
vendored
@@ -1,7 +1,7 @@
|
|||||||
SPMediaKeyTap
|
SPMediaKeyTap
|
||||||
=============
|
=============
|
||||||
|
|
||||||
`SPMediaKeyTap` abstracts a `CGEventHook` and other nastiness in order to give you a relatively simple API to receive media key events (prev/next/playpause, on F7 to F9 on modern MacBook Pros) exclusively, without them reaching other applications like iTunes. `SPMediaKeyTap` is clever enough to resign its exclusive lock on media keys by looking for which application was active most recently: if that application is in `SPMediaKeyTap`'s whitelist, it will resign the keys. This is similar to the behavior of Apple's applications collaborating on media key handling exclusivity, but unfortunately, Apple are not exposing any APIs allowing third-parties to join in on this collaboration.
|
`SPMediaKeyTap` abstracts a `CGEventHook` and other nastiness in order to give you a relatively simple API to receive media key events (prev/next/playpause, on F7 to F9 on modern MacBook Pros) exclusively, without them reaching other applications like iTunes. `SPMediaKeyTap` is clever enough to resign its exclusive lock on media keys by looking for which application was active most recently: if that application is in `SPMediaKeyTap`'s whitelist, it will resign the keys. This is similar to the behavior of Apple's applications collaborating on media key handling exclusivity, but unfortunately, Apple is not exposing any APIs allowing third-parties to join in on this collaboration.
|
||||||
|
|
||||||
For now, the whitelist is just a hardcoded array in `+[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers]`. If your app starts using `SPMediaKeyTap`, please [mail me](mailto:nevyn@spotify.com) your bundle ID, and I'll include it in the canonical repository. This is a bad solution; a better solution would be to use distributed notifications to collaborate in creating this whitelist at runtime. Hopefully someone'll have the time and energy to write this soon.
|
For now, the whitelist is just a hardcoded array in `+[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers]`. If your app starts using `SPMediaKeyTap`, please [mail me](mailto:nevyn@spotify.com) your bundle ID, and I'll include it in the canonical repository. This is a bad solution; a better solution would be to use distributed notifications to collaborate in creating this whitelist at runtime. Hopefully someone'll have the time and energy to write this soon.
|
||||||
|
|
||||||
@@ -9,4 +9,4 @@ In `Example/SPMediaKeyTapExampleAppDelegate.m` is an example of both how you use
|
|||||||
|
|
||||||
`SPMediaKeyTap` and other `CGEventHook`s on the event type `NSSystemDefined` is known to interfere with each other and applications doing weird stuff with mouse input, because mouse clicks are also part of the `NSSystemDefined` category. The single issue we have had reported here at Spotify is Adobe Fireworks, in which item selection stops working with `SPMediaKeyTap` is active.
|
`SPMediaKeyTap` and other `CGEventHook`s on the event type `NSSystemDefined` is known to interfere with each other and applications doing weird stuff with mouse input, because mouse clicks are also part of the `NSSystemDefined` category. The single issue we have had reported here at Spotify is Adobe Fireworks, in which item selection stops working with `SPMediaKeyTap` is active.
|
||||||
|
|
||||||
`SPMediaKeyTap` requires 10.5 to work, and disables itself on 10.4.
|
`SPMediaKeyTap` requires 10.5 to work, and disables itself on 10.4.
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@interface SPInvocationGrabber : NSObject {
|
|
||||||
id _object;
|
|
||||||
NSInvocation *_invocation;
|
|
||||||
int frameCount;
|
|
||||||
char **frameStrings;
|
|
||||||
BOOL backgroundAfterForward;
|
|
||||||
BOOL onMainAfterForward;
|
|
||||||
BOOL waitUntilDone;
|
|
||||||
}
|
|
||||||
-(id)initWithObject:(id)obj;
|
|
||||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
|
||||||
@property (readonly, retain, nonatomic) id object;
|
|
||||||
@property (readonly, retain, nonatomic) NSInvocation *invocation;
|
|
||||||
@property BOOL backgroundAfterForward;
|
|
||||||
@property BOOL onMainAfterForward;
|
|
||||||
@property BOOL waitUntilDone;
|
|
||||||
-(void)invoke; // will release object and invocation
|
|
||||||
-(void)printBacktrace;
|
|
||||||
-(void)saveBacktrace;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSObject (SPInvocationGrabbing)
|
|
||||||
-(id)grab;
|
|
||||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
|
||||||
-(id)nextRunloop;
|
|
||||||
-(id)inBackground;
|
|
||||||
-(id)onMainAsync:(BOOL)async;
|
|
||||||
@end
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
#import "NSObject+SPInvocationGrabbing.h"
|
|
||||||
#import <execinfo.h>
|
|
||||||
|
|
||||||
#pragma mark Invocation grabbing
|
|
||||||
@interface SPInvocationGrabber ()
|
|
||||||
@property (readwrite, retain, nonatomic) id object;
|
|
||||||
@property (readwrite, retain, nonatomic) NSInvocation *invocation;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation SPInvocationGrabber
|
|
||||||
- (id)initWithObject:(id)obj;
|
|
||||||
{
|
|
||||||
return [self initWithObject:obj stacktraceSaving:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
|
||||||
{
|
|
||||||
self.object = obj;
|
|
||||||
|
|
||||||
if(saveStack)
|
|
||||||
[self saveBacktrace];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
-(void)dealloc;
|
|
||||||
{
|
|
||||||
free(frameStrings);
|
|
||||||
self.object = nil;
|
|
||||||
self.invocation = nil;
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
@synthesize invocation = _invocation, object = _object;
|
|
||||||
|
|
||||||
@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone;
|
|
||||||
- (void)runInBackground;
|
|
||||||
{
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
@try {
|
|
||||||
[self invoke];
|
|
||||||
}
|
|
||||||
@finally {
|
|
||||||
[pool drain];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)forwardInvocation:(NSInvocation *)anInvocation {
|
|
||||||
[anInvocation retainArguments];
|
|
||||||
anInvocation.target = _object;
|
|
||||||
self.invocation = anInvocation;
|
|
||||||
|
|
||||||
if(backgroundAfterForward)
|
|
||||||
[NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil];
|
|
||||||
else if(onMainAfterForward)
|
|
||||||
[self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone];
|
|
||||||
}
|
|
||||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector {
|
|
||||||
NSMethodSignature *signature = [super methodSignatureForSelector:inSelector];
|
|
||||||
if (signature == NULL)
|
|
||||||
signature = [_object methodSignatureForSelector:inSelector];
|
|
||||||
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invoke;
|
|
||||||
{
|
|
||||||
|
|
||||||
@try {
|
|
||||||
[_invocation invoke];
|
|
||||||
}
|
|
||||||
@catch (NSException * e) {
|
|
||||||
NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e);
|
|
||||||
[self printBacktrace];
|
|
||||||
printf("\n");
|
|
||||||
[e raise];
|
|
||||||
}
|
|
||||||
|
|
||||||
self.invocation = nil;
|
|
||||||
self.object = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)saveBacktrace;
|
|
||||||
{
|
|
||||||
void *backtraceFrames[128];
|
|
||||||
frameCount = backtrace(&backtraceFrames[0], 128);
|
|
||||||
frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
|
|
||||||
}
|
|
||||||
-(void)printBacktrace;
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
for(x = 3; x < frameCount; x++) {
|
|
||||||
if(frameStrings[x] == NULL) { break; }
|
|
||||||
printf("%s\n", frameStrings[x]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation NSObject (SPInvocationGrabbing)
|
|
||||||
-(id)grab;
|
|
||||||
{
|
|
||||||
return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease];
|
|
||||||
}
|
|
||||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
|
||||||
{
|
|
||||||
id grabber = [self grab];
|
|
||||||
[NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
|
|
||||||
return grabber;
|
|
||||||
}
|
|
||||||
- (id)nextRunloop;
|
|
||||||
{
|
|
||||||
return [self invokeAfter:0];
|
|
||||||
}
|
|
||||||
-(id)inBackground;
|
|
||||||
{
|
|
||||||
SPInvocationGrabber *grabber = [self grab];
|
|
||||||
grabber.backgroundAfterForward = YES;
|
|
||||||
return grabber;
|
|
||||||
}
|
|
||||||
-(id)onMainAsync:(BOOL)async;
|
|
||||||
{
|
|
||||||
SPInvocationGrabber *grabber = [self grab];
|
|
||||||
grabber.onMainAfterForward = YES;
|
|
||||||
grabber.waitUntilDone = !async;
|
|
||||||
return grabber;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
// A
|
|
||||||
+(UIView*)flashAt:(CGRect)r in:(UIView*)parent color:(UIColor*)color;
|
|
||||||
{
|
|
||||||
float duration = 0.5;
|
|
||||||
UIView *flash = [[[UIView alloc] initWithFrame:r] autorelease];
|
|
||||||
flash.backgroundColor = color;
|
|
||||||
[parent addSubview:flash];
|
|
||||||
[[flash invokeAfter:duration+0.1] removeFromSuperview];
|
|
||||||
|
|
||||||
[UIView beginAnimations:@"SPFlash" context:NULL];
|
|
||||||
[UIView setAnimationDuration:duration];
|
|
||||||
flash.alpha = 0.0;
|
|
||||||
[UIView commitAnimations];
|
|
||||||
return flash;
|
|
||||||
}
|
|
||||||
|
|
||||||
// B
|
|
||||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
||||||
|
|
||||||
// Force the animation to happen by calling this method again after a small
|
|
||||||
// delay - see http://blog.instapaper.com/post/53568356
|
|
||||||
[[self nextRunloop] delayedTableViewDidSelectRowAtIndexPath: indexPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
// C
|
|
||||||
[[tableView invokeAfter:0.15] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
|
|
||||||
[[tableView invokeAfter:0.30] deselectRowAtIndexPath:indexPath animated:YES];
|
|
||||||
[[tableView invokeAfter:0.45] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
@interface MyClass : NSObject
|
|
||||||
-(BOOL)areTheNewViewersGoneYet:(Duck*)duck;
|
|
||||||
@end
|
|
||||||
...
|
|
||||||
MyClass *myInstance = [[MyClass alloc] init];
|
|
||||||
id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease];
|
|
||||||
|
|
||||||
|
|
||||||
[invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]]; // line 9
|
|
||||||
|
|
||||||
|
|
||||||
NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation];
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "NSObject+SPInvocationGrabbing.h"
|
|
||||||
|
|
||||||
@interface Foo : NSObject {
|
|
||||||
int a;
|
|
||||||
}
|
|
||||||
-(void)startIt;
|
|
||||||
-(void)theBackgroundStuff;
|
|
||||||
-(void)theForegroundStuff;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation Foo
|
|
||||||
-(void)startIt;
|
|
||||||
{
|
|
||||||
NSLog(@"Starting out on the main thread...");
|
|
||||||
a = 3;
|
|
||||||
[[self inBackground] theBackgroundStuff];
|
|
||||||
}
|
|
||||||
-(void)theBackgroundStuff;
|
|
||||||
{
|
|
||||||
NSLog(@"Woah, this is a background thread!");
|
|
||||||
a += 6;
|
|
||||||
[[self onMainAsync:YES] theForegroundStuff];
|
|
||||||
}
|
|
||||||
-(void)theForegroundStuff;
|
|
||||||
{
|
|
||||||
NSLog(@"Hey presto: %d", a);
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
||||||
Foo *foo = [Foo new];
|
|
||||||
[foo startIt];
|
|
||||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
|
|
||||||
[pool release];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
61
3rdparty/SPMediaKeyTap/SPMediaKeyTap.h
vendored
61
3rdparty/SPMediaKeyTap/SPMediaKeyTap.h
vendored
@@ -1,34 +1,53 @@
|
|||||||
#include <Cocoa/Cocoa.h>
|
/*
|
||||||
|
Copyright (c) 2011, Joachim Bengtsson
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Neither the name of the organization nor the names of its contributors may
|
||||||
|
be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <IOKit/hidsystem/ev_keymap.h>
|
#import <IOKit/hidsystem/ev_keymap.h>
|
||||||
#import <Carbon/Carbon.h>
|
|
||||||
|
|
||||||
// http://overooped.com/post/2593597587/mediakeys
|
// http://overooped.com/post/2593597587/mediakeys
|
||||||
|
|
||||||
#define SPSystemDefinedEventMediaKeys 8
|
#define SPSystemDefinedEventMediaKeys 8
|
||||||
|
|
||||||
@interface SPMediaKeyTap : NSObject {
|
@interface SPMediaKeyTap : NSObject
|
||||||
EventHandlerRef _app_switching_ref;
|
|
||||||
EventHandlerRef _app_terminating_ref;
|
|
||||||
CFMachPortRef _eventPort;
|
|
||||||
CFRunLoopSourceRef _eventPortSource;
|
|
||||||
CFRunLoopRef _tapThreadRL;
|
|
||||||
BOOL _shouldInterceptMediaKeyEvents;
|
|
||||||
id _delegate;
|
|
||||||
// The app that is frontmost in this list owns media keys
|
|
||||||
NSMutableArray *_mediaKeyAppList;
|
|
||||||
}
|
|
||||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
|
||||||
|
|
||||||
-(id)initWithDelegate:(id)delegate;
|
- (id)initWithDelegate:(id)delegate;
|
||||||
|
|
||||||
+(BOOL)usesGlobalMediaKeyTap;
|
+ (BOOL)usesGlobalMediaKeyTap;
|
||||||
-(void)startWatchingMediaKeys;
|
- (BOOL)startWatchingMediaKeys;
|
||||||
-(void)stopWatchingMediaKeys;
|
- (void)stopWatchingMediaKeys;
|
||||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event;
|
- (void)handleAndReleaseMediaKeyEvent:(NSEvent *)event;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface NSObject (SPMediaKeyTapDelegate)
|
@interface NSObject (SPMediaKeyTapDelegate)
|
||||||
-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
- (void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey;
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern NSString *kIgnoreMediaKeysDefaultsKey;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
495
3rdparty/SPMediaKeyTap/SPMediaKeyTap.m
vendored
495
3rdparty/SPMediaKeyTap/SPMediaKeyTap.m
vendored
@@ -1,18 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011, Joachim Bengtsson
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Neither the name of the organization nor the names of its contributors may
|
||||||
|
be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
// Copyright (c) 2010 Spotify AB
|
// Copyright (c) 2010 Spotify AB
|
||||||
#import "SPMediaKeyTap.h"
|
#import "SPMediaKeyTap.h"
|
||||||
#import "SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule
|
|
||||||
|
|
||||||
@interface SPMediaKeyTap ()
|
// Define to enable app list debug output
|
||||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
// #define DEBUG_SPMEDIAKEY_APPLIST 1
|
||||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
|
||||||
-(void)startWatchingAppSwitching;
|
NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys";
|
||||||
-(void)stopWatchingAppSwitching;
|
|
||||||
-(void)eventTapThread;
|
@interface SPMediaKeyTap () {
|
||||||
|
CFMachPortRef _eventPort;
|
||||||
|
CFRunLoopSourceRef _eventPortSource;
|
||||||
|
CFRunLoopRef _tapThreadRL;
|
||||||
|
NSThread *_tapThread;
|
||||||
|
BOOL _shouldInterceptMediaKeyEvents;
|
||||||
|
id _delegate;
|
||||||
|
// The app that is frontmost in this list owns media keys
|
||||||
|
NSMutableArray<NSRunningApplication *> *_mediaKeyAppList;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)shouldInterceptMediaKeyEvents;
|
||||||
|
- (void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
||||||
|
- (void)startWatchingAppSwitching;
|
||||||
|
- (void)stopWatchingAppSwitching;
|
||||||
|
- (void)eventTapThread;
|
||||||
@end
|
@end
|
||||||
static SPMediaKeyTap *singleton = nil;
|
|
||||||
|
|
||||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
|
||||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
|
||||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
|
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
|
||||||
|
|
||||||
|
|
||||||
@@ -22,127 +56,191 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark Setup and teardown
|
#pragma mark Setup and teardown
|
||||||
-(id)initWithDelegate:(id)delegate;
|
|
||||||
|
- (id)initWithDelegate:(id)delegate
|
||||||
{
|
{
|
||||||
_delegate = delegate;
|
self = [super init];
|
||||||
[self startWatchingAppSwitching];
|
if (self) {
|
||||||
singleton = self;
|
_delegate = delegate;
|
||||||
_mediaKeyAppList = [NSMutableArray new];
|
[self startWatchingAppSwitching];
|
||||||
return self;
|
_mediaKeyAppList = [NSMutableArray new];
|
||||||
}
|
}
|
||||||
-(void)dealloc;
|
return self;
|
||||||
{
|
|
||||||
[self stopWatchingMediaKeys];
|
|
||||||
[self stopWatchingAppSwitching];
|
|
||||||
[_mediaKeyAppList release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)startWatchingAppSwitching;
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
// Listen to "app switched" event, so that we don't intercept media keys if we
|
[self stopWatchingMediaKeys];
|
||||||
// weren't the last "media key listening" app to be active
|
[self stopWatchingAppSwitching];
|
||||||
EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched };
|
[super dealloc];
|
||||||
OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, self, &_app_switching_ref);
|
|
||||||
assert(err == noErr);
|
|
||||||
|
|
||||||
eventType.eventKind = kEventAppTerminated;
|
|
||||||
err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, self, &_app_terminating_ref);
|
|
||||||
assert(err == noErr);
|
|
||||||
}
|
|
||||||
-(void)stopWatchingAppSwitching;
|
|
||||||
{
|
|
||||||
if(!_app_switching_ref) return;
|
|
||||||
RemoveEventHandler(_app_switching_ref);
|
|
||||||
_app_switching_ref = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)startWatchingMediaKeys;{
|
- (void)startWatchingAppSwitching
|
||||||
[self setShouldInterceptMediaKeyEvents:YES];
|
{
|
||||||
|
// Listen to "app switched" event, so that we don't intercept media keys if we
|
||||||
// Add an event tap to intercept the system defined media key events
|
// weren't the last "media key listening" app to be active
|
||||||
_eventPort = CGEventTapCreate(kCGSessionEventTap,
|
|
||||||
kCGHeadInsertEventTap,
|
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
|
||||||
kCGEventTapOptionDefault,
|
selector:@selector(frontmostAppChanged:)
|
||||||
CGEventMaskBit(NX_SYSDEFINED),
|
name:NSWorkspaceDidActivateApplicationNotification
|
||||||
tapEventCallback,
|
object:nil];
|
||||||
self);
|
|
||||||
assert(_eventPort != NULL);
|
|
||||||
|
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
|
||||||
|
selector:@selector(appTerminated:)
|
||||||
|
name:NSWorkspaceDidTerminateApplicationNotification
|
||||||
|
object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)stopWatchingAppSwitching
|
||||||
|
{
|
||||||
|
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)startWatchingMediaKeys
|
||||||
|
{
|
||||||
|
// Prevent having multiple mediaKeys threads
|
||||||
|
[self stopWatchingMediaKeys];
|
||||||
|
|
||||||
|
[self setShouldInterceptMediaKeyEvents:YES];
|
||||||
|
|
||||||
|
// Add an event tap to intercept the system defined media key events
|
||||||
|
_eventPort = CGEventTapCreate(kCGSessionEventTap,
|
||||||
|
kCGHeadInsertEventTap,
|
||||||
|
kCGEventTapOptionDefault,
|
||||||
|
CGEventMaskBit(NX_SYSDEFINED),
|
||||||
|
tapEventCallback,
|
||||||
|
(__bridge void * __nullable)(self));
|
||||||
|
|
||||||
|
// Can be NULL if the app has no accessibility access permission
|
||||||
|
if (_eventPort == NULL)
|
||||||
|
return NO;
|
||||||
|
|
||||||
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
|
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
|
||||||
assert(_eventPortSource != NULL);
|
assert(_eventPortSource != NULL);
|
||||||
|
|
||||||
// Let's do this in a separate thread so that a slow app doesn't lag the event tap
|
if (_eventPortSource == NULL)
|
||||||
[NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil];
|
return NO;
|
||||||
|
|
||||||
|
// Let's do this in a separate thread so that a slow app doesn't lag the event tap
|
||||||
|
_tapThread = [[NSThread alloc] initWithTarget:self
|
||||||
|
selector:@selector(eventTapThread)
|
||||||
|
object:nil];
|
||||||
|
[_tapThread start];
|
||||||
|
|
||||||
|
return YES;
|
||||||
}
|
}
|
||||||
-(void)stopWatchingMediaKeys;
|
|
||||||
|
- (void)stopWatchingMediaKeys
|
||||||
{
|
{
|
||||||
// TODO<nevyn>: Shut down thread, remove event tap port and source
|
// Shut down tap thread
|
||||||
|
if(_tapThreadRL){
|
||||||
|
CFRunLoopStop(_tapThreadRL);
|
||||||
|
_tapThreadRL = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove tap port
|
||||||
|
if(_eventPort){
|
||||||
|
CFMachPortInvalidate(_eventPort);
|
||||||
|
CFRelease(_eventPort);
|
||||||
|
_eventPort = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove tap source
|
||||||
|
if(_eventPortSource){
|
||||||
|
CFRelease(_eventPortSource);
|
||||||
|
_eventPortSource = nil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark Accessors
|
#pragma mark Accessors
|
||||||
|
|
||||||
+(BOOL)usesGlobalMediaKeyTap
|
+ (BOOL)usesGlobalMediaKeyTap
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot
|
// breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot
|
||||||
return NO;
|
return NO;
|
||||||
#else
|
#else
|
||||||
// XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy.
|
// XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy.
|
||||||
return floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/;
|
return
|
||||||
|
![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey]
|
||||||
|
&& floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
+ (NSArray*)mediaKeyUserBundleIdentifiers
|
||||||
{
|
{
|
||||||
return [NSArray arrayWithObjects:
|
return [NSArray arrayWithObjects:
|
||||||
[[NSBundle mainBundle] bundleIdentifier], // your app
|
[[NSBundle mainBundle] bundleIdentifier], // your app
|
||||||
@"com.spotify.client",
|
@"com.spotify.client",
|
||||||
@"com.apple.iTunes",
|
@"com.apple.iTunes",
|
||||||
@"com.apple.QuickTimePlayerX",
|
@"com.apple.QuickTimePlayerX",
|
||||||
@"com.apple.quicktimeplayer",
|
@"com.apple.quicktimeplayer",
|
||||||
@"com.apple.iWork.Keynote",
|
@"com.apple.iWork.Keynote",
|
||||||
@"com.apple.iPhoto",
|
@"com.apple.iPhoto",
|
||||||
@"org.videolan.vlc",
|
@"org.videolan.vlc",
|
||||||
@"com.apple.Aperture",
|
@"com.apple.Aperture",
|
||||||
@"com.plexsquared.Plex",
|
@"com.plexsquared.Plex",
|
||||||
@"com.soundcloud.desktop",
|
@"com.soundcloud.desktop",
|
||||||
@"com.macromedia.fireworks", // the tap messes up their mouse input
|
@"org.niltsh.MPlayerX",
|
||||||
nil
|
@"com.ilabs.PandorasHelper",
|
||||||
];
|
@"com.mahasoftware.pandabar",
|
||||||
|
@"com.bitcartel.pandorajam",
|
||||||
|
@"org.clementine-player.clementine",
|
||||||
|
@"fm.last.Last.fm",
|
||||||
|
@"fm.last.Scrobbler",
|
||||||
|
@"com.beatport.BeatportPro",
|
||||||
|
@"com.Timenut.SongKey",
|
||||||
|
@"com.macromedia.fireworks", // the tap messes up their mouse input
|
||||||
|
@"at.justp.Theremin",
|
||||||
|
@"ru.ya.themblsha.YandexMusic",
|
||||||
|
@"com.jriver.MediaCenter18",
|
||||||
|
@"com.jriver.MediaCenter19",
|
||||||
|
@"com.jriver.MediaCenter20",
|
||||||
|
@"co.rackit.mate",
|
||||||
|
@"com.ttitt.b-music",
|
||||||
|
@"com.beardedspice.BeardedSpice",
|
||||||
|
@"com.plug.Plug",
|
||||||
|
@"com.plug.Plug2",
|
||||||
|
@"com.netease.163music",
|
||||||
|
@"org.quodlibet.quodlibet",
|
||||||
|
nil
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
- (BOOL)shouldInterceptMediaKeyEvents
|
||||||
{
|
{
|
||||||
BOOL shouldIntercept = NO;
|
BOOL shouldIntercept = NO;
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
shouldIntercept = _shouldInterceptMediaKeyEvents;
|
shouldIntercept = _shouldInterceptMediaKeyEvents;
|
||||||
}
|
}
|
||||||
return shouldIntercept;
|
return shouldIntercept;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)pauseTapOnTapThread:(BOOL)yeahno;
|
- (void)pauseTapOnTapThread:(NSNumber *)yeahno
|
||||||
{
|
{
|
||||||
CGEventTapEnable(self->_eventPort, yeahno);
|
CGEventTapEnable(self->_eventPort, [yeahno boolValue]);
|
||||||
}
|
|
||||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
|
||||||
{
|
|
||||||
BOOL oldSetting;
|
|
||||||
@synchronized(self) {
|
|
||||||
oldSetting = _shouldInterceptMediaKeyEvents;
|
|
||||||
_shouldInterceptMediaKeyEvents = newSetting;
|
|
||||||
}
|
|
||||||
if(_tapThreadRL && oldSetting != newSetting) {
|
|
||||||
id grab = [self grab];
|
|
||||||
[grab pauseTapOnTapThread:newSetting];
|
|
||||||
NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO];
|
|
||||||
CFRunLoopAddTimer(_tapThreadRL, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark
|
- (void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting
|
||||||
|
{
|
||||||
|
BOOL oldSetting;
|
||||||
|
@synchronized(self) {
|
||||||
|
oldSetting = _shouldInterceptMediaKeyEvents;
|
||||||
|
_shouldInterceptMediaKeyEvents = newSetting;
|
||||||
|
}
|
||||||
|
if(_tapThreadRL && oldSetting != newSetting) {
|
||||||
|
[self performSelector:@selector(pauseTapOnTapThread:)
|
||||||
|
onThread:_tapThread
|
||||||
|
withObject:@(newSetting)
|
||||||
|
waitUntilDone:NO];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
#pragma mark Event tap callbacks
|
#pragma mark Event tap callbacks
|
||||||
|
|
||||||
@@ -150,151 +248,114 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||||||
|
|
||||||
static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
||||||
{
|
{
|
||||||
SPMediaKeyTap *self = refcon;
|
#pragma unused(proxy)
|
||||||
|
SPMediaKeyTap *self = (__bridge SPMediaKeyTap *)refcon;
|
||||||
|
|
||||||
if(type == kCGEventTapDisabledByTimeout) {
|
if(type == kCGEventTapDisabledByTimeout) {
|
||||||
NSLog(@"Media key event tap was disabled by timeout");
|
NSLog(@"Media key event tap was disabled by timeout");
|
||||||
CGEventTapEnable(self->_eventPort, TRUE);
|
CGEventTapEnable(self->_eventPort, TRUE);
|
||||||
return event;
|
return event;
|
||||||
} else if(type == kCGEventTapDisabledByUserInput) {
|
} else if(type == kCGEventTapDisabledByUserInput) {
|
||||||
// Was disabled manually by -[pauseTapOnTapThread]
|
// Was disabled manually by -[pauseTapOnTapThread]
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
NSEvent *nsEvent = nil;
|
NSEvent *nsEvent = nil;
|
||||||
@try {
|
@try {
|
||||||
nsEvent = [NSEvent eventWithCGEvent:event];
|
nsEvent = [NSEvent eventWithCGEvent:event];
|
||||||
}
|
}
|
||||||
@catch (NSException * e) {
|
@catch (NSException * e) {
|
||||||
NSLog(@"Strange CGEventType: %d: %@", type, e);
|
NSLog(@"Strange CGEventType: %d: %@", type, e);
|
||||||
assert(0);
|
assert(0);
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys)
|
if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys)
|
||||||
return event;
|
return event;
|
||||||
|
|
||||||
int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16);
|
int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16);
|
||||||
if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND)
|
if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND && keyCode != NX_KEYTYPE_PREVIOUS && keyCode != NX_KEYTYPE_NEXT)
|
||||||
return event;
|
return event;
|
||||||
|
|
||||||
if (![self shouldInterceptMediaKeyEvents])
|
if (![self shouldInterceptMediaKeyEvents])
|
||||||
return event;
|
return event;
|
||||||
|
|
||||||
[nsEvent retain]; // matched in handleAndReleaseMediaKeyEvent:
|
[self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO];
|
||||||
[self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO];
|
|
||||||
|
return NULL;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
|
||||||
{
|
{
|
||||||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
@autoreleasepool {
|
||||||
CGEventRef ret = tapEventCallback2(proxy, type, event, refcon);
|
CGEventRef ret = tapEventCallback2(proxy, type, event, refcon);
|
||||||
[pool drain];
|
return ret;
|
||||||
return ret;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)handleAndReleaseMediaKeyEvent:(NSEvent *)event
|
||||||
// event will have been retained in the other thread
|
|
||||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event {
|
|
||||||
[event autorelease];
|
|
||||||
|
|
||||||
[_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-(void)eventTapThread;
|
|
||||||
{
|
{
|
||||||
_tapThreadRL = CFRunLoopGetCurrent();
|
[_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
|
||||||
CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes);
|
|
||||||
CFRunLoopRun();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)eventTapThread
|
||||||
|
{
|
||||||
|
_tapThreadRL = CFRunLoopGetCurrent();
|
||||||
|
CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes);
|
||||||
|
CFRunLoopRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
#pragma mark Task switching callbacks
|
#pragma mark Task switching callbacks
|
||||||
|
|
||||||
NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys";
|
- (void)mediaKeyAppListChanged
|
||||||
|
|
||||||
|
|
||||||
-(void)mediaKeyAppListChanged;
|
|
||||||
{
|
{
|
||||||
if([_mediaKeyAppList count] == 0) return;
|
#ifdef DEBUG_SPMEDIAKEY_APPLIST
|
||||||
|
[self debugPrintAppList];
|
||||||
/*NSLog(@"--");
|
#endif
|
||||||
int i = 0;
|
|
||||||
for (NSValue *psnv in _mediaKeyAppList) {
|
|
||||||
ProcessSerialNumber psn; [psnv getValue:&psn];
|
|
||||||
NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary(
|
|
||||||
&psn,
|
|
||||||
kProcessDictionaryIncludeAllInformationMask
|
|
||||||
) autorelease];
|
|
||||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
|
||||||
NSLog(@"%d: %@", i++, bundleIdentifier);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
ProcessSerialNumber mySerial, topSerial;
|
|
||||||
GetCurrentProcess(&mySerial);
|
|
||||||
[[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial];
|
|
||||||
|
|
||||||
Boolean same;
|
if([_mediaKeyAppList count] == 0)
|
||||||
OSErr err = SameProcess(&mySerial, &topSerial, &same);
|
return;
|
||||||
[self setShouldInterceptMediaKeyEvents:(err == noErr && same)];
|
|
||||||
|
|
||||||
}
|
NSRunningApplication *thisApp = [NSRunningApplication currentApplication];
|
||||||
-(void)appIsNowFrontmost:(ProcessSerialNumber)psn;
|
NSRunningApplication *otherApp = [_mediaKeyAppList firstObject];
|
||||||
{
|
|
||||||
NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
|
||||||
|
|
||||||
NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary(
|
|
||||||
&psn,
|
|
||||||
kProcessDictionaryIncludeAllInformationMask
|
|
||||||
) autorelease];
|
|
||||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
|
||||||
|
|
||||||
NSArray *whitelistIdentifiers = [[NSUserDefaults standardUserDefaults] arrayForKey:kMediaKeyUsingBundleIdentifiersDefaultsKey];
|
BOOL isCurrent = [thisApp isEqual:otherApp];
|
||||||
if(![whitelistIdentifiers containsObject:bundleIdentifier]) return;
|
|
||||||
|
|
||||||
[_mediaKeyAppList removeObject:psnv];
|
[self setShouldInterceptMediaKeyEvents:isCurrent];
|
||||||
[_mediaKeyAppList insertObject:psnv atIndex:0];
|
|
||||||
[self mediaKeyAppListChanged];
|
|
||||||
}
|
|
||||||
-(void)appTerminated:(ProcessSerialNumber)psn;
|
|
||||||
{
|
|
||||||
NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
|
||||||
[_mediaKeyAppList removeObject:psnv];
|
|
||||||
[self mediaKeyAppListChanged];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData)
|
- (void)frontmostAppChanged:(NSNotification *)notification
|
||||||
{
|
{
|
||||||
SPMediaKeyTap *self = (id)userData;
|
NSRunningApplication *app = [notification.userInfo objectForKey:NSWorkspaceApplicationKey];
|
||||||
|
if (app.bundleIdentifier == nil)
|
||||||
|
return;
|
||||||
|
|
||||||
ProcessSerialNumber newSerial;
|
if (![[SPMediaKeyTap mediaKeyUserBundleIdentifiers] containsObject:app.bundleIdentifier])
|
||||||
GetFrontProcess(&newSerial);
|
return;
|
||||||
|
|
||||||
[self appIsNowFrontmost:newSerial];
|
[_mediaKeyAppList removeObject:app];
|
||||||
|
[_mediaKeyAppList insertObject:app atIndex:0];
|
||||||
return CallNextEventHandler(nextHandler, evt);
|
[self mediaKeyAppListChanged];
|
||||||
}
|
}
|
||||||
|
|
||||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData)
|
- (void)appTerminated:(NSNotification *)notification
|
||||||
{
|
{
|
||||||
SPMediaKeyTap *self = (id)userData;
|
NSRunningApplication *app = [notification.userInfo objectForKey:NSWorkspaceApplicationKey];
|
||||||
|
[_mediaKeyAppList removeObject:app];
|
||||||
ProcessSerialNumber deadPSN;
|
|
||||||
|
|
||||||
GetEventParameter(
|
[self mediaKeyAppListChanged];
|
||||||
evt,
|
|
||||||
kEventParamProcessID,
|
|
||||||
typeProcessSerialNumber,
|
|
||||||
NULL,
|
|
||||||
sizeof(deadPSN),
|
|
||||||
NULL,
|
|
||||||
&deadPSN
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
[self appTerminated:deadPSN];
|
|
||||||
return CallNextEventHandler(nextHandler, evt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPMEDIAKEY_APPLIST
|
||||||
|
- (void)debugPrintAppList
|
||||||
|
{
|
||||||
|
NSMutableString *list = [NSMutableString stringWithCapacity:255];
|
||||||
|
for (NSRunningApplication *app in _mediaKeyAppList) {
|
||||||
|
[list appendFormat:@" - %@\n", app.bundleIdentifier];
|
||||||
|
}
|
||||||
|
NSLog(@"List: \n%@", list);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
25
3rdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m
vendored
25
3rdparty/SPMediaKeyTap/SPMediaKeyTapDelegate.m
vendored
@@ -1,25 +0,0 @@
|
|||||||
-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
|
||||||
{
|
|
||||||
assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
|
|
||||||
|
|
||||||
int keyCode = (([event data1] & 0xFFFF0000) >> 16);
|
|
||||||
int keyFlags = ([event data1] & 0x0000FFFF);
|
|
||||||
int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
|
|
||||||
int keyRepeat = (keyFlags & 0x1);
|
|
||||||
|
|
||||||
if (keyState == 1 && windowController != NULL) {
|
|
||||||
|
|
||||||
|
|
||||||
switch (keyCode) {
|
|
||||||
|
|
||||||
case NX_KEYTYPE_PLAY:
|
|
||||||
... return;
|
|
||||||
|
|
||||||
case NX_KEYTYPE_FAST:
|
|
||||||
... return;
|
|
||||||
|
|
||||||
case NX_KEYTYPE_REWIND:
|
|
||||||
... return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
3rdparty/macdeployqt/CMakeLists.txt
vendored
Normal file
7
3rdparty/macdeployqt/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
add_executable(macdeployqt main.cpp shared.cpp)
|
||||||
|
target_link_libraries(macdeployqt PRIVATE
|
||||||
|
"-framework AppKit"
|
||||||
|
${QtCore_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
#execute_process(COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/macdeployqt ${CMAKE_BINARY_DIR})
|
||||||
298
3rdparty/macdeployqt/main.cpp
vendored
Normal file
298
3rdparty/macdeployqt/main.cpp
vendored
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the tools applications of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#undef QT_NO_DEBUG_OUTPUT
|
||||||
|
#undef QT_NO_WARNING_OUTPUT
|
||||||
|
#undef QT_NO_INFO_OUTPUT
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
|
||||||
|
#include "shared.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
|
QString appBundlePath;
|
||||||
|
if (argc > 1)
|
||||||
|
appBundlePath = QString::fromLocal8Bit(argv[1]);
|
||||||
|
|
||||||
|
if (argc < 2 || appBundlePath.startsWith("-")) {
|
||||||
|
qDebug() << "Usage: macdeployqt app-bundle [options]";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "Options:";
|
||||||
|
qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug";
|
||||||
|
qDebug() << " -no-plugins : Skip plugin deployment";
|
||||||
|
qDebug() << " -dmg : Create a .dmg disk image";
|
||||||
|
qDebug() << " -no-strip : Don't run 'strip' on the binaries";
|
||||||
|
qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)";
|
||||||
|
qDebug() << " -executable=<path> : Let the given executable use the deployed frameworks too";
|
||||||
|
qDebug() << " -qmldir=<path> : Scan for QML imports in the given path";
|
||||||
|
qDebug() << " -qmlimport=<path> : Add the given path to the QML module search locations";
|
||||||
|
qDebug() << " -always-overwrite : Copy files even if the target file exists";
|
||||||
|
qDebug() << " -codesign=<ident> : Run codesign with the given identity on all executables";
|
||||||
|
qDebug() << " -hardened-runtime : Enable Hardened Runtime when code signing";
|
||||||
|
qDebug() << " -timestamp : Include a secure timestamp when code signing (requires internet connection)";
|
||||||
|
qDebug() << " -sign-for-notarization=<ident>: Activate the necessary options for notarization (requires internet connection)";
|
||||||
|
qDebug() << " -appstore-compliant : Skip deployment of components that use private API";
|
||||||
|
qDebug() << " -libpath=<path> : Add the given path to the library search path";
|
||||||
|
qDebug() << " -fs=<filesystem> : Set the filesystem used for the .dmg disk image (defaults to HFS+)";
|
||||||
|
qDebug() << " -plugins-dir=<path> : Set plugins directory";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "macdeployqt takes an application bundle as input and makes it";
|
||||||
|
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
|
||||||
|
qDebug() << "the application uses.";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "Plugins related to a framework are copied in with the";
|
||||||
|
qDebug() << "framework. The accessibility, image formats, and text codec";
|
||||||
|
qDebug() << "plugins are always copied, unless \"-no-plugins\" is specified.";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "Qt plugins may use private API and will cause the app to be";
|
||||||
|
qDebug() << "rejected from the Mac App store. MacDeployQt will print a warning";
|
||||||
|
qDebug() << "when known incompatible plugins are deployed. Use -appstore-compliant ";
|
||||||
|
qDebug() << "to skip these plugins. Currently two SQL plugins are known to";
|
||||||
|
qDebug() << "be incompatible: qsqlodbc and qsqlpsql.";
|
||||||
|
qDebug() << "";
|
||||||
|
qDebug() << "See the \"Deploying Applications on OS X\" topic in the";
|
||||||
|
qDebug() << "documentation for more information about deployment on OS X.";
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
appBundlePath = QDir::cleanPath(appBundlePath);
|
||||||
|
|
||||||
|
if (QDir().exists(appBundlePath) == false) {
|
||||||
|
qDebug() << "Error: Could not find app bundle" << appBundlePath;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plugins = true;
|
||||||
|
bool dmg = false;
|
||||||
|
QByteArray filesystem("HFS+");
|
||||||
|
bool useDebugLibs = false;
|
||||||
|
extern bool runStripEnabled;
|
||||||
|
extern bool alwaysOwerwriteEnabled;
|
||||||
|
extern QStringList librarySearchPath;
|
||||||
|
QStringList additionalExecutables;
|
||||||
|
bool qmldirArgumentUsed = false;
|
||||||
|
QStringList qmlDirs;
|
||||||
|
QStringList qmlImportPaths;
|
||||||
|
extern bool runCodesign;
|
||||||
|
extern QString codesignIdentiy;
|
||||||
|
extern bool hardenedRuntime;
|
||||||
|
extern bool appstoreCompliant;
|
||||||
|
extern bool deployFramework;
|
||||||
|
extern bool secureTimestamp;
|
||||||
|
QString plugin_dir;
|
||||||
|
|
||||||
|
for (int i = 2; i < argc; ++i) {
|
||||||
|
QByteArray argument = QByteArray(argv[i]);
|
||||||
|
if (argument == QByteArray("-no-plugins")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
plugins = false;
|
||||||
|
} else if (argument == QByteArray("-dmg")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
dmg = true;
|
||||||
|
} else if (argument == QByteArray("-no-strip")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
runStripEnabled = false;
|
||||||
|
} else if (argument == QByteArray("-use-debug-libs")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
useDebugLibs = true;
|
||||||
|
runStripEnabled = false;
|
||||||
|
} else if (argument.startsWith(QByteArray("-verbose"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf("=");
|
||||||
|
bool ok = false;
|
||||||
|
int number = argument.mid(index+1).toInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
LogError() << "Could not parse verbose level";
|
||||||
|
else
|
||||||
|
logLevel = number;
|
||||||
|
} else if (argument.startsWith(QByteArray("-executable"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing executable path";
|
||||||
|
else
|
||||||
|
additionalExecutables << argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith(QByteArray("-qmldir"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
qmldirArgumentUsed = true;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing qml directory path";
|
||||||
|
else
|
||||||
|
qmlDirs << argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith(QByteArray("-qmlimport"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing qml import path";
|
||||||
|
else
|
||||||
|
qmlImportPaths << argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith(QByteArray("-libpath"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing library search path";
|
||||||
|
else
|
||||||
|
librarySearchPath << argument.mid(index+1);
|
||||||
|
} else if (argument == QByteArray("-always-overwrite")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
alwaysOwerwriteEnabled = true;
|
||||||
|
} else if (argument.startsWith(QByteArray("-codesign"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf("=");
|
||||||
|
if (index < 0 || index >= argument.size()) {
|
||||||
|
LogError() << "Missing code signing identity";
|
||||||
|
} else {
|
||||||
|
runCodesign = true;
|
||||||
|
codesignIdentiy = argument.mid(index+1);
|
||||||
|
}
|
||||||
|
} else if (argument.startsWith(QByteArray("-sign-for-notarization"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf("=");
|
||||||
|
if (index < 0 || index >= argument.size()) {
|
||||||
|
LogError() << "Missing code signing identity";
|
||||||
|
} else {
|
||||||
|
runCodesign = true;
|
||||||
|
hardenedRuntime = true;
|
||||||
|
secureTimestamp = true;
|
||||||
|
codesignIdentiy = argument.mid(index+1);
|
||||||
|
}
|
||||||
|
} else if (argument.startsWith(QByteArray("-hardened-runtime"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
hardenedRuntime = true;
|
||||||
|
} else if (argument.startsWith(QByteArray("-timestamp"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
secureTimestamp = true;
|
||||||
|
} else if (argument == QByteArray("-appstore-compliant")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
appstoreCompliant = true;
|
||||||
|
|
||||||
|
// Undocumented option, may not work as intented
|
||||||
|
} else if (argument == QByteArray("-deploy-framework")) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
deployFramework = true;
|
||||||
|
|
||||||
|
} else if (argument.startsWith(QByteArray("-fs"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing filesystem type";
|
||||||
|
else
|
||||||
|
filesystem = argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith(QByteArray("-plugins-dir"))) {
|
||||||
|
LogDebug() << "Argument found:" << argument;
|
||||||
|
int index = argument.indexOf('=');
|
||||||
|
if (index == -1)
|
||||||
|
LogError() << "Missing filesystem type";
|
||||||
|
else
|
||||||
|
plugin_dir = argument.mid(index+1);
|
||||||
|
} else if (argument.startsWith("-")) {
|
||||||
|
LogError() << "Unknown argument" << argument << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, additionalExecutables, useDebugLibs);
|
||||||
|
|
||||||
|
if (deploymentInfo.isDebug)
|
||||||
|
useDebugLibs = true;
|
||||||
|
|
||||||
|
if (deployFramework && deploymentInfo.isFramework)
|
||||||
|
fixupFramework(appBundlePath);
|
||||||
|
|
||||||
|
// Convenience: Look for .qml files in the current directoty if no -qmldir specified.
|
||||||
|
if (qmlDirs.isEmpty()) {
|
||||||
|
QDir dir;
|
||||||
|
if (!dir.entryList(QStringList() << QStringLiteral("*.qml")).isEmpty()) {
|
||||||
|
qmlDirs += QStringLiteral(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qmlDirs.isEmpty()) {
|
||||||
|
bool ok = deployQmlImports(appBundlePath, deploymentInfo, qmlDirs, qmlImportPaths);
|
||||||
|
if (!ok && qmldirArgumentUsed)
|
||||||
|
return 1; // exit if the user explicitly asked for qml import deployment
|
||||||
|
|
||||||
|
// Update deploymentInfo.deployedFrameworks - the QML imports
|
||||||
|
// may have brought in extra frameworks as dependencies.
|
||||||
|
deploymentInfo.deployedFrameworks += findAppFrameworkNames(appBundlePath);
|
||||||
|
deploymentInfo.deployedFrameworks =
|
||||||
|
QSet<QString>(deploymentInfo.deployedFrameworks.begin(),
|
||||||
|
deploymentInfo.deployedFrameworks.end()).values();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plugins) {
|
||||||
|
if (plugin_dir.isEmpty()) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
deploymentInfo.pluginPath = QLibraryInfo::path(QLibraryInfo::PluginsPath);
|
||||||
|
#else
|
||||||
|
deploymentInfo.pluginPath = QLibraryInfo::location(QLibraryInfo::PluginsPath);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deploymentInfo.pluginPath = plugin_dir;
|
||||||
|
}
|
||||||
|
if (deploymentInfo.pluginPath.isEmpty()) {
|
||||||
|
LogError() << "Missing Qt plugins path\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!QDir(deploymentInfo.pluginPath).exists()) {
|
||||||
|
LogError() << "Plugins path does not exist\n" << deploymentInfo.pluginPath;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Q_ASSERT(!deploymentInfo.pluginPath.isEmpty());
|
||||||
|
if (!deploymentInfo.pluginPath.isEmpty()) {
|
||||||
|
LogNormal();
|
||||||
|
deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
|
||||||
|
createQtConf(appBundlePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runStripEnabled)
|
||||||
|
stripAppBinary(appBundlePath);
|
||||||
|
|
||||||
|
if (!FinalCheck(appBundlePath)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runCodesign)
|
||||||
|
codesign(codesignIdentiy, appBundlePath);
|
||||||
|
|
||||||
|
if (dmg) {
|
||||||
|
LogNormal();
|
||||||
|
createDiskImage(appBundlePath, filesystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1832
3rdparty/macdeployqt/shared.cpp
vendored
Normal file
1832
3rdparty/macdeployqt/shared.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
141
3rdparty/macdeployqt/shared.h
vendored
Normal file
141
3rdparty/macdeployqt/shared.h
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the tools applications of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#ifndef MAC_DEPLOMYMENT_SHARED_H
|
||||||
|
#define MAC_DEPLOMYMENT_SHARED_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QVersionNumber>
|
||||||
|
|
||||||
|
extern int logLevel;
|
||||||
|
#define LogError() if (logLevel < 0) {} else qDebug() << "ERROR:"
|
||||||
|
#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:"
|
||||||
|
#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:"
|
||||||
|
#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:"
|
||||||
|
|
||||||
|
extern bool runStripEnabled;
|
||||||
|
|
||||||
|
class FrameworkInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool isDylib;
|
||||||
|
QString frameworkDirectory;
|
||||||
|
QString frameworkName;
|
||||||
|
QString frameworkPath;
|
||||||
|
QString binaryDirectory;
|
||||||
|
QString binaryName;
|
||||||
|
QString binaryPath;
|
||||||
|
QString rpathUsed;
|
||||||
|
QString version;
|
||||||
|
QString installName;
|
||||||
|
QString deployedInstallName;
|
||||||
|
QString sourceFilePath;
|
||||||
|
QString frameworkDestinationDirectory;
|
||||||
|
QString binaryDestinationDirectory;
|
||||||
|
|
||||||
|
bool isDebugLibrary() const
|
||||||
|
{
|
||||||
|
return binaryName.contains(QLatin1String("_debug"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DylibInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString binaryPath;
|
||||||
|
QVersionNumber currentVersion;
|
||||||
|
QVersionNumber compatibilityVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OtoolInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString installName;
|
||||||
|
QString binaryPath;
|
||||||
|
QVersionNumber currentVersion;
|
||||||
|
QVersionNumber compatibilityVersion;
|
||||||
|
QList<DylibInfo> dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const FrameworkInfo &a, const FrameworkInfo &b);
|
||||||
|
QDebug operator<<(QDebug debug, const FrameworkInfo &info);
|
||||||
|
|
||||||
|
class ApplicationBundleInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString path;
|
||||||
|
QString binaryPath;
|
||||||
|
QStringList libraryPaths;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeploymentInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString qtPath;
|
||||||
|
QString pluginPath;
|
||||||
|
QStringList deployedFrameworks;
|
||||||
|
QSet<QString> rpathsUsed;
|
||||||
|
bool useLoaderPath;
|
||||||
|
bool isFramework;
|
||||||
|
bool isDebug;
|
||||||
|
|
||||||
|
bool containsModule(const QString &module, const QString &libInFix) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
|
||||||
|
|
||||||
|
OtoolInfo findDependencyInfo(const QString &binaryPath);
|
||||||
|
FrameworkInfo parseOtoolLibraryLine(const QString &line, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||||
|
QString findAppBinary(const QString &appBundlePath);
|
||||||
|
QList<FrameworkInfo> getQtFrameworks(const QString &path, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||||
|
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, const QString &appBundlePath, const QSet<QString> &rpaths, bool useDebugLibs);
|
||||||
|
QString copyFramework(const FrameworkInfo &framework, const QString path);
|
||||||
|
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringList &additionalExecutables, bool useDebugLibs);
|
||||||
|
DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
|
||||||
|
void createQtConf(const QString &appBundlePath);
|
||||||
|
void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs);
|
||||||
|
bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs, QStringList &qmlImportPaths);
|
||||||
|
void changeIdentification(const QString &id, const QString &binaryPath);
|
||||||
|
void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
|
||||||
|
void runStrip(const QString &binaryPath);
|
||||||
|
void stripAppBinary(const QString &bundlePath);
|
||||||
|
QString findAppBinary(const QString &appBundlePath);
|
||||||
|
QStringList findAppFrameworkNames(const QString &appBundlePath);
|
||||||
|
QStringList findAppFrameworkPaths(const QString &appBundlePath);
|
||||||
|
void codesignFile(const QString &identity, const QString &filePath);
|
||||||
|
QSet<QString> codesignBundle(const QString &identity,
|
||||||
|
const QString &appBundlePath,
|
||||||
|
QList<QString> additionalBinariesContainingRpaths);
|
||||||
|
void codesign(const QString &identity, const QString &appBundlePath);
|
||||||
|
void createDiskImage(const QString &appBundlePath, const QString &filesystemType);
|
||||||
|
void fixupFramework(const QString &appBundlePath);
|
||||||
|
bool FinalCheck(const QString &appBundlePath);
|
||||||
|
|
||||||
|
#endif
|
||||||
35
3rdparty/qocoa/CMakeLists.txt
vendored
35
3rdparty/qocoa/CMakeLists.txt
vendored
@@ -1,35 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.11)
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
|
|
||||||
set(SOURCES)
|
|
||||||
|
|
||||||
set(HEADERS
|
|
||||||
qsearchfield.h
|
|
||||||
qbutton.h
|
|
||||||
qprogressindicatorspinning.h
|
|
||||||
)
|
|
||||||
|
|
||||||
qt5_wrap_cpp(MOC_SOURCES ${HEADERS})
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
list(APPEND SOURCES
|
|
||||||
qsearchfield_mac.mm
|
|
||||||
qbutton_mac.mm
|
|
||||||
qprogressindicatorspinning_mac.mm
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
list(APPEND SOURCES
|
|
||||||
qsearchfield_nonmac.cpp
|
|
||||||
qbutton_nonmac.cpp
|
|
||||||
qprogressindicatorspinning_nonmac.cpp
|
|
||||||
)
|
|
||||||
set(RESOURCES
|
|
||||||
qsearchfield_nonmac.qrc
|
|
||||||
qprogressindicatorspinning_nonmac.qrc
|
|
||||||
)
|
|
||||||
qt5_add_resources(RESOURCES_SOURCES ${RESOURCES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(Qocoa STATIC ${SOURCES} ${MOC_SOURCES} ${RESOURCES_SOURCES})
|
|
||||||
target_link_libraries(Qocoa ${QT_LIBRARIES})
|
|
||||||
|
|
||||||
38
3rdparty/qocoa/README.md
vendored
38
3rdparty/qocoa/README.md
vendored
@@ -1,38 +0,0 @@
|
|||||||
# Qocoa
|
|
||||||
Qocoa is a collection of Qt wrappers for OSX's Cocoa widgets.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
- basic fallback to sensible Qt types on non-OSX platforms
|
|
||||||
- shared class headers which expose no implementation details
|
|
||||||
- typical Qt signal/slot-based API
|
|
||||||
- trivial to import into projects (class header/implementation, [single shared global header](https://github.com/mikemcquaid/Qocoa/blob/master/qocoa_mac.h))
|
|
||||||
|
|
||||||
## Building
|
|
||||||
```
|
|
||||||
git clone git://github.com/mikemcquaid/Qocoa.git
|
|
||||||
cd Qocoa
|
|
||||||
qmake # or cmake .
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
## Status
|
|
||||||
I'm not personally working on this any more but will accept pull-requests.
|
|
||||||
|
|
||||||
[](https://travis-ci.org/MikeMcQuaid/Qocoa)
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
For each class you want to use copy the [`qocoa_mac.h`](https://github.com/mikemcquaid/Qocoa/blob/master/qocoa_mac.h), `$CLASS.h`, `$CLASS_mac.*` and `$CLASS_nonmac.*` files into your source tree and add them to your buildsystem. Examples are provided for [CMake](https://github.com/mikemcquaid/Qocoa/blob/master/CMakeLists.txt) and [QMake](https://github.com/mikemcquaid/Qocoa/blob/master/Qocoa.pro).
|
|
||||||
|
|
||||||
## Contact
|
|
||||||
[Mike McQuaid](mailto:mike@mikemcquaid.com)
|
|
||||||
|
|
||||||
## License
|
|
||||||
Qocoa is licensed under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).
|
|
||||||
The full license text is available in [LICENSE.txt](https://github.com/mikemcquaid/Qocoa/blob/master/LICENSE.txt).
|
|
||||||
|
|
||||||
Magnifier and EditClear icons taken from [QtCreator](http://qt-project.org/) and are licensed under the [LGPL](http://www.gnu.org/copyleft/lesser.html).
|
|
||||||
|
|
||||||
Other icons are taken from the [Oxygen Project](http://www.oxygen-icons.org/) and are licensed under the [Creative Commons Attribution-ShareAlike 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/).
|
|
||||||
|
|
||||||
## Gallery
|
|
||||||

|
|
||||||
13
3rdparty/qocoa/TODO.md
vendored
13
3rdparty/qocoa/TODO.md
vendored
@@ -1,13 +0,0 @@
|
|||||||
Widgets I hope to implement (or at least investigate):
|
|
||||||
|
|
||||||
- NSTokenField
|
|
||||||
- NSSegmentedControl
|
|
||||||
- NSLevelIndicator
|
|
||||||
- NSPathControl
|
|
||||||
- NSSlider (Circular)
|
|
||||||
- NSSplitView
|
|
||||||
- NSTextFinder
|
|
||||||
- NSOutlineView in an NSScrollView (Source List)
|
|
||||||
- NSDrawer
|
|
||||||
- PDFView
|
|
||||||
- WebView
|
|
||||||
49
3rdparty/qocoa/qbutton.h
vendored
49
3rdparty/qocoa/qbutton.h
vendored
@@ -1,49 +0,0 @@
|
|||||||
#ifndef QBUTTON_H
|
|
||||||
#define QBUTTON_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QPointer>
|
|
||||||
|
|
||||||
class QButtonPrivate;
|
|
||||||
class QButton : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
// Matches NSBezelStyle
|
|
||||||
enum BezelStyle {
|
|
||||||
Rounded = 1,
|
|
||||||
RegularSquare = 2,
|
|
||||||
Disclosure = 5,
|
|
||||||
ShadowlessSquare = 6,
|
|
||||||
Circular = 7,
|
|
||||||
TexturedSquare = 8,
|
|
||||||
HelpButton = 9,
|
|
||||||
SmallSquare = 10,
|
|
||||||
TexturedRounded = 11,
|
|
||||||
RoundRect = 12,
|
|
||||||
Recessed = 13,
|
|
||||||
RoundedDisclosure = 14,
|
|
||||||
#ifdef __MAC_10_7
|
|
||||||
Inline = 15
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit QButton(QWidget *parent, BezelStyle bezelStyle = Rounded);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setText(const QString &text);
|
|
||||||
void setImage(const QPixmap &image);
|
|
||||||
void setChecked(bool checked);
|
|
||||||
|
|
||||||
public:
|
|
||||||
void setCheckable(bool checkable);
|
|
||||||
bool isChecked();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void clicked(bool checked = false);
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class QButtonPrivate;
|
|
||||||
QPointer<QButtonPrivate> pimpl;
|
|
||||||
};
|
|
||||||
#endif // QBUTTON_H
|
|
||||||
229
3rdparty/qocoa/qbutton_mac.mm
vendored
229
3rdparty/qocoa/qbutton_mac.mm
vendored
@@ -1,229 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2011 by Mike McQuaid
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qbutton.h"
|
|
||||||
|
|
||||||
#include "qocoa_mac.h"
|
|
||||||
|
|
||||||
#import "Foundation/NSAutoreleasePool.h"
|
|
||||||
#import "AppKit/NSButton.h"
|
|
||||||
#import "AppKit/NSFont.h"
|
|
||||||
|
|
||||||
class QButtonPrivate : public QObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QButtonPrivate(QButton *qButton, NSButton *nsButton, QButton::BezelStyle bezelStyle)
|
|
||||||
: QObject(qButton), qButton(qButton), nsButton(nsButton)
|
|
||||||
{
|
|
||||||
switch(bezelStyle) {
|
|
||||||
case QButton::Disclosure:
|
|
||||||
case QButton::Circular:
|
|
||||||
#ifdef __MAC_10_7
|
|
||||||
case QButton::Inline:
|
|
||||||
#endif
|
|
||||||
case QButton::RoundedDisclosure:
|
|
||||||
case QButton::HelpButton:
|
|
||||||
[nsButton setTitle:@""];
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSFont* font = 0;
|
|
||||||
switch(bezelStyle) {
|
|
||||||
case QButton::RoundRect:
|
|
||||||
font = [NSFont fontWithName:@"Lucida Grande" size:12];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QButton::Recessed:
|
|
||||||
font = [NSFont fontWithName:@"Lucida Grande Bold" size:12];
|
|
||||||
break;
|
|
||||||
|
|
||||||
#ifdef __MAC_10_7
|
|
||||||
case QButton::Inline:
|
|
||||||
font = [NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
default:
|
|
||||||
font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[nsButton setFont:font];
|
|
||||||
|
|
||||||
switch(bezelStyle) {
|
|
||||||
case QButton::Rounded:
|
|
||||||
qButton->setMinimumWidth(40);
|
|
||||||
qButton->setFixedHeight(24);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
||||||
break;
|
|
||||||
case QButton::RegularSquare:
|
|
||||||
case QButton::TexturedSquare:
|
|
||||||
qButton->setMinimumSize(14, 23);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
break;
|
|
||||||
case QButton::ShadowlessSquare:
|
|
||||||
qButton->setMinimumSize(5, 25);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
break;
|
|
||||||
case QButton::SmallSquare:
|
|
||||||
qButton->setMinimumSize(4, 21);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
break;
|
|
||||||
case QButton::TexturedRounded:
|
|
||||||
qButton->setMinimumSize(10, 22);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
break;
|
|
||||||
case QButton::RoundRect:
|
|
||||||
case QButton::Recessed:
|
|
||||||
qButton->setMinimumWidth(16);
|
|
||||||
qButton->setFixedHeight(18);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
||||||
break;
|
|
||||||
case QButton::Disclosure:
|
|
||||||
qButton->setMinimumWidth(13);
|
|
||||||
qButton->setFixedHeight(13);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
||||||
break;
|
|
||||||
case QButton::Circular:
|
|
||||||
qButton->setMinimumSize(16, 16);
|
|
||||||
qButton->setMaximumHeight(40);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
break;
|
|
||||||
case QButton::HelpButton:
|
|
||||||
case QButton::RoundedDisclosure:
|
|
||||||
qButton->setMinimumWidth(22);
|
|
||||||
qButton->setFixedHeight(22);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
||||||
break;
|
|
||||||
#ifdef __MAC_10_7
|
|
||||||
case QButton::Inline:
|
|
||||||
qButton->setMinimumWidth(10);
|
|
||||||
qButton->setFixedHeight(16);
|
|
||||||
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(bezelStyle) {
|
|
||||||
case QButton::Recessed:
|
|
||||||
[nsButton setButtonType:NSPushOnPushOffButton];
|
|
||||||
case QButton::Disclosure:
|
|
||||||
[nsButton setButtonType:NSOnOffButton];
|
|
||||||
default:
|
|
||||||
[nsButton setButtonType:NSMomentaryPushInButton];
|
|
||||||
}
|
|
||||||
|
|
||||||
[nsButton setBezelStyle:(__bridge NSBezelStyle)bezelStyle];
|
|
||||||
}
|
|
||||||
|
|
||||||
void clicked()
|
|
||||||
{
|
|
||||||
emit qButton->clicked(qButton->isChecked());
|
|
||||||
}
|
|
||||||
|
|
||||||
~QButtonPrivate() {
|
|
||||||
[[nsButton target] release];
|
|
||||||
[nsButton setTarget:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
QButton *qButton;
|
|
||||||
NSButton *nsButton;
|
|
||||||
};
|
|
||||||
|
|
||||||
@interface QButtonTarget : NSObject
|
|
||||||
{
|
|
||||||
@public
|
|
||||||
QPointer<QButtonPrivate> pimpl;
|
|
||||||
}
|
|
||||||
-(void)clicked;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation QButtonTarget
|
|
||||||
-(void)clicked {
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (pimpl)
|
|
||||||
pimpl->clicked();
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
QButton::QButton(QWidget *parent, BezelStyle bezelStyle) : QWidget(parent)
|
|
||||||
{
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
|
|
||||||
NSButton *button = [[NSButton alloc] init];
|
|
||||||
pimpl = new QButtonPrivate(this, button, bezelStyle);
|
|
||||||
|
|
||||||
QButtonTarget *target = [[QButtonTarget alloc] init];
|
|
||||||
target->pimpl = pimpl;
|
|
||||||
[button setTarget:target];
|
|
||||||
|
|
||||||
[button setAction:@selector(clicked)];
|
|
||||||
|
|
||||||
setupLayout(button, this);
|
|
||||||
|
|
||||||
[button release];
|
|
||||||
|
|
||||||
[pool drain];
|
|
||||||
}
|
|
||||||
|
|
||||||
void QButton::setText(const QString &text)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (!pimpl)
|
|
||||||
return;
|
|
||||||
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
[pimpl->nsButton setTitle:fromQString(text)];
|
|
||||||
[pool drain];
|
|
||||||
}
|
|
||||||
|
|
||||||
void QButton::setImage(const QPixmap &image)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (pimpl)
|
|
||||||
[pimpl->nsButton setImage:fromQPixmap(image)];
|
|
||||||
}
|
|
||||||
|
|
||||||
void QButton::setChecked(bool checked)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (pimpl)
|
|
||||||
[pimpl->nsButton setState:checked];
|
|
||||||
}
|
|
||||||
|
|
||||||
void QButton::setCheckable(bool checkable)
|
|
||||||
{
|
|
||||||
const NSInteger cellMask = checkable ? NSChangeBackgroundCellMask : NSNoCellMask;
|
|
||||||
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (pimpl)
|
|
||||||
[[pimpl->nsButton cell] setShowsStateBy:cellMask];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QButton::isChecked()
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (!pimpl)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return [pimpl->nsButton state];
|
|
||||||
}
|
|
||||||
89
3rdparty/qocoa/qbutton_nonmac.cpp
vendored
89
3rdparty/qocoa/qbutton_nonmac.cpp
vendored
@@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2011 by Mike McQuaid
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qbutton.h"
|
|
||||||
|
|
||||||
#include <QToolBar>
|
|
||||||
#include <QToolButton>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
class QButtonPrivate : public QObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QButtonPrivate(QButton *button, QAbstractButton *abstractButton)
|
|
||||||
: QObject(button), abstractButton(abstractButton) {}
|
|
||||||
QPointer<QAbstractButton> abstractButton;
|
|
||||||
};
|
|
||||||
|
|
||||||
QButton::QButton(QWidget *parent, BezelStyle) : QWidget(parent)
|
|
||||||
{
|
|
||||||
QAbstractButton *button = 0;
|
|
||||||
if (qobject_cast<QToolBar*>(parent))
|
|
||||||
button = new QToolButton(this);
|
|
||||||
else
|
|
||||||
button = new QPushButton(this);
|
|
||||||
connect(button, SIGNAL(clicked()),
|
|
||||||
this, SIGNAL(clicked()));
|
|
||||||
pimpl = new QButtonPrivate(this, button);
|
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
||||||
layout->setMargin(0);
|
|
||||||
layout->addWidget(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QButton::setText(const QString &text)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (pimpl)
|
|
||||||
pimpl->abstractButton->setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QButton::setImage(const QPixmap &image)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (pimpl)
|
|
||||||
pimpl->abstractButton->setIcon(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QButton::setChecked(bool checked)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (pimpl)
|
|
||||||
pimpl->abstractButton->setChecked(checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QButton::setCheckable(bool checkable)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (pimpl)
|
|
||||||
pimpl->abstractButton->setCheckable(checkable);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QButton::isChecked()
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (!pimpl)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return pimpl->abstractButton->isChecked();
|
|
||||||
}
|
|
||||||
56
3rdparty/qocoa/qocoa_mac.h
vendored
56
3rdparty/qocoa/qocoa_mac.h
vendored
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2011 by Mike McQuaid
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <AppKit/NSImage.h>
|
|
||||||
#include <Foundation/NSString.h>
|
|
||||||
#include <QString>
|
|
||||||
#include <QBoxLayout>
|
|
||||||
#include <QtMacExtras>
|
|
||||||
#include <QMacCocoaViewContainer>
|
|
||||||
|
|
||||||
static inline NSString* fromQString(const QString &string)
|
|
||||||
{
|
|
||||||
const QByteArray utf8 = string.toUtf8();
|
|
||||||
const char* cString = utf8.constData();
|
|
||||||
return [[NSString alloc] initWithUTF8String:cString];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString toQString(NSString *string)
|
|
||||||
{
|
|
||||||
if (!string)
|
|
||||||
return QString();
|
|
||||||
return QString::fromUtf8([string UTF8String]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline NSImage* fromQPixmap(const QPixmap &pixmap)
|
|
||||||
{
|
|
||||||
CGImageRef cgImage = QtMac::toCGImageRef(pixmap);
|
|
||||||
return [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void setupLayout(NSView *cocoaView, QWidget *parent)
|
|
||||||
{
|
|
||||||
parent->setAttribute(Qt::WA_NativeWindow);
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(parent);
|
|
||||||
layout->setMargin(0);
|
|
||||||
layout->addWidget(new QMacCocoaViewContainer(cocoaView, parent));
|
|
||||||
}
|
|
||||||
29
3rdparty/qocoa/qprogressindicatorspinning.h
vendored
29
3rdparty/qocoa/qprogressindicatorspinning.h
vendored
@@ -1,29 +0,0 @@
|
|||||||
#ifndef QPROGRESSINDICATORSPINNING_H
|
|
||||||
#define QPROGRESSINDICATORSPINNING_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QPointer>
|
|
||||||
|
|
||||||
class QProgressIndicatorSpinningPrivate;
|
|
||||||
class QProgressIndicatorSpinning : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
// Matches NSProgressIndicatorThickness
|
|
||||||
enum Thickness {
|
|
||||||
Default = 14,
|
|
||||||
Small = 10,
|
|
||||||
Large = 18,
|
|
||||||
Aqua = 12
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit QProgressIndicatorSpinning(QWidget *parent,
|
|
||||||
Thickness thickness = Default);
|
|
||||||
public slots:
|
|
||||||
void animate(bool animate = true);
|
|
||||||
private:
|
|
||||||
friend class QProgressIndicatorSpinningPrivate;
|
|
||||||
QPointer<QProgressIndicatorSpinningPrivate> pimpl;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // QPROGRESSINDICATORSPINNING_H
|
|
||||||
70
3rdparty/qocoa/qprogressindicatorspinning_mac.mm
vendored
70
3rdparty/qocoa/qprogressindicatorspinning_mac.mm
vendored
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2011 by Mike McQuaid
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qprogressindicatorspinning.h"
|
|
||||||
|
|
||||||
#include "qocoa_mac.h"
|
|
||||||
|
|
||||||
#import "Foundation/NSAutoreleasePool.h"
|
|
||||||
#import "AppKit/NSProgressIndicator.h"
|
|
||||||
|
|
||||||
class QProgressIndicatorSpinningPrivate : public QObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QProgressIndicatorSpinningPrivate(QProgressIndicatorSpinning *qProgressIndicatorSpinning,
|
|
||||||
NSProgressIndicator *nsProgressIndicator)
|
|
||||||
: QObject(qProgressIndicatorSpinning), nsProgressIndicator(nsProgressIndicator) {}
|
|
||||||
|
|
||||||
NSProgressIndicator *nsProgressIndicator;
|
|
||||||
};
|
|
||||||
|
|
||||||
QProgressIndicatorSpinning::QProgressIndicatorSpinning(QWidget *parent,
|
|
||||||
Thickness thickness)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
|
|
||||||
NSProgressIndicator *progress = [[NSProgressIndicator alloc] init];
|
|
||||||
[progress setStyle:NSProgressIndicatorSpinningStyle];
|
|
||||||
|
|
||||||
pimpl = new QProgressIndicatorSpinningPrivate(this, progress);
|
|
||||||
|
|
||||||
setupLayout(progress, this);
|
|
||||||
|
|
||||||
setFixedSize(thickness, thickness);
|
|
||||||
|
|
||||||
[progress release];
|
|
||||||
|
|
||||||
[pool drain];
|
|
||||||
}
|
|
||||||
|
|
||||||
void QProgressIndicatorSpinning::animate(bool animate)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl);
|
|
||||||
if (!pimpl)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (animate)
|
|
||||||
[pimpl->nsProgressIndicator startAnimation:nil];
|
|
||||||
else
|
|
||||||
[pimpl->nsProgressIndicator stopAnimation:nil];
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2011 by Mike McQuaid
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qprogressindicatorspinning.h"
|
|
||||||
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QMovie>
|
|
||||||
#include <QLabel>
|
|
||||||
|
|
||||||
class QProgressIndicatorSpinningPrivate : public QObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QProgressIndicatorSpinningPrivate(QProgressIndicatorSpinning *qProgressIndicatorSpinning,
|
|
||||||
QMovie *movie)
|
|
||||||
: QObject(qProgressIndicatorSpinning), movie(movie) {}
|
|
||||||
|
|
||||||
QPointer<QMovie> movie;
|
|
||||||
};
|
|
||||||
|
|
||||||
QProgressIndicatorSpinning::QProgressIndicatorSpinning(QWidget *parent,
|
|
||||||
Thickness thickness)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
||||||
layout->setMargin(0);
|
|
||||||
|
|
||||||
QSize size(thickness, thickness);
|
|
||||||
QMovie *movie = new QMovie(this);
|
|
||||||
movie->setFileName(":/Qocoa/qprogressindicatorspinning_nonmac.gif");
|
|
||||||
movie->setScaledSize(size);
|
|
||||||
// Roughly match OSX speed.
|
|
||||||
movie->setSpeed(200);
|
|
||||||
pimpl = new QProgressIndicatorSpinningPrivate(this, movie);
|
|
||||||
|
|
||||||
QLabel *label = new QLabel(this);
|
|
||||||
label->setMovie(movie);
|
|
||||||
|
|
||||||
layout->addWidget(label);
|
|
||||||
setFixedSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void QProgressIndicatorSpinning::animate(bool animate)
|
|
||||||
{
|
|
||||||
Q_ASSERT(pimpl && pimpl->movie);
|
|
||||||
if (!(pimpl && pimpl->movie))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (animate)
|
|
||||||
pimpl->movie->start();
|
|
||||||
else
|
|
||||||
pimpl->movie->stop();
|
|
||||||
}
|
|
||||||
BIN
3rdparty/qocoa/qprogressindicatorspinning_nonmac.gif
vendored
BIN
3rdparty/qocoa/qprogressindicatorspinning_nonmac.gif
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,5 +0,0 @@
|
|||||||
<RCC>
|
|
||||||
<qresource prefix="/Qocoa">
|
|
||||||
<file>qprogressindicatorspinning_nonmac.gif</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
||||||
161
3rdparty/qocoa/qsearchfield_nonmac.cpp
vendored
161
3rdparty/qocoa/qsearchfield_nonmac.cpp
vendored
@@ -1,161 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2011 by Mike McQuaid
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qsearchfield.h"
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QDesktopWidget>
|
|
||||||
#include <QLineEdit>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QToolButton>
|
|
||||||
#include <QStyle>
|
|
||||||
#include <QEvent>
|
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "../../src/core/iconloader.h"
|
|
||||||
|
|
||||||
class QSearchFieldPrivate : public QObject {
|
|
||||||
public:
|
|
||||||
QSearchFieldPrivate(QSearchField *searchField, QLineEdit *lineEdit, QToolButton *clearButton)
|
|
||||||
: QObject(searchField), lineEdit(lineEdit), clearButton(clearButton) {}
|
|
||||||
|
|
||||||
int lineEditFrameWidth() const {
|
|
||||||
return lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
int clearButtonPaddedWidth() const {
|
|
||||||
return clearButton->width() + lineEditFrameWidth() * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int clearButtonPaddedHeight() const {
|
|
||||||
return clearButton->height() + lineEditFrameWidth() * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointer<QLineEdit> lineEdit;
|
|
||||||
QPointer<QToolButton> clearButton;
|
|
||||||
};
|
|
||||||
|
|
||||||
QSearchField::QSearchField(QWidget *parent) : QWidget(parent) {
|
|
||||||
|
|
||||||
QLineEdit *lineEdit = new QLineEdit(this);
|
|
||||||
connect(lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged(QString)));
|
|
||||||
connect(lineEdit, SIGNAL(editingFinished()), this, SIGNAL(editingFinished()));
|
|
||||||
connect(lineEdit, SIGNAL(returnPressed()), this, SIGNAL(returnPressed()));
|
|
||||||
connect(lineEdit, SIGNAL(textChanged(QString)), this, SLOT(setText(QString)));
|
|
||||||
|
|
||||||
QToolButton *clearButton = new QToolButton(this);
|
|
||||||
QIcon clearIcon(IconLoader::Load("edit-clear-locationbar-ltr"));
|
|
||||||
|
|
||||||
clearButton->setIcon(clearIcon);
|
|
||||||
clearButton->setIconSize(QSize(16, 16));
|
|
||||||
clearButton->setStyleSheet("border: none; padding: 0px;");
|
|
||||||
clearButton->resize(clearButton->sizeHint());
|
|
||||||
|
|
||||||
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
|
|
||||||
|
|
||||||
pimpl = new QSearchFieldPrivate(this, lineEdit, clearButton);
|
|
||||||
|
|
||||||
const int frame_width = lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
|
||||||
|
|
||||||
lineEdit->setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(clearButton->width()));
|
|
||||||
const int width = frame_width + qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth());
|
|
||||||
const int height = frame_width + qMax(lineEdit->minimumSizeHint().height(), pimpl->clearButtonPaddedHeight());
|
|
||||||
lineEdit->setMinimumSize(width, height);
|
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
||||||
layout->setMargin(0);
|
|
||||||
layout->addWidget(lineEdit);
|
|
||||||
|
|
||||||
lineEdit->installEventFilter(this);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void QSearchField::setText(const QString &text) {
|
|
||||||
|
|
||||||
Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit);
|
|
||||||
if (!(pimpl && pimpl->clearButton && pimpl->lineEdit)) return;
|
|
||||||
if (text != this->text()) pimpl->lineEdit->setText(text);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void QSearchField::setPlaceholderText(const QString &text) {
|
|
||||||
Q_ASSERT(pimpl && pimpl->lineEdit);
|
|
||||||
if (!(pimpl && pimpl->lineEdit)) return;
|
|
||||||
pimpl->lineEdit->setPlaceholderText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QSearchField::placeholderText() const {
|
|
||||||
return pimpl->lineEdit->placeholderText();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QSearchField::setFocus(Qt::FocusReason reason) {
|
|
||||||
Q_ASSERT(pimpl && pimpl->lineEdit);
|
|
||||||
if (pimpl && pimpl->lineEdit) pimpl->lineEdit->setFocus(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QSearchField::setFocus() {
|
|
||||||
setFocus(Qt::OtherFocusReason);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QSearchField::clear() {
|
|
||||||
Q_ASSERT(pimpl && pimpl->lineEdit);
|
|
||||||
if (!(pimpl && pimpl->lineEdit)) return;
|
|
||||||
pimpl->lineEdit->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QSearchField::selectAll() {
|
|
||||||
Q_ASSERT(pimpl && pimpl->lineEdit);
|
|
||||||
if (!(pimpl && pimpl->lineEdit)) return;
|
|
||||||
pimpl->lineEdit->selectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QSearchField::text() const {
|
|
||||||
Q_ASSERT(pimpl && pimpl->lineEdit);
|
|
||||||
if (!(pimpl && pimpl->lineEdit)) return QString();
|
|
||||||
return pimpl->lineEdit->text();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QSearchField::resizeEvent(QResizeEvent *resizeEvent) {
|
|
||||||
Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit);
|
|
||||||
if (!(pimpl && pimpl->clearButton && pimpl->lineEdit)) return;
|
|
||||||
|
|
||||||
QWidget::resizeEvent(resizeEvent);
|
|
||||||
const int x = pimpl->lineEditFrameWidth();
|
|
||||||
const int y = (height() - pimpl->clearButton->height())/2;
|
|
||||||
pimpl->clearButton->move(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QSearchField::eventFilter(QObject *o, QEvent *e) {
|
|
||||||
if (pimpl && pimpl->lineEdit && o == pimpl->lineEdit) {
|
|
||||||
// Forward some lineEdit events to QSearchField (only those we need for
|
|
||||||
// now, but some might be added later if needed)
|
|
||||||
switch (e->type()) {
|
|
||||||
case QEvent::FocusIn:
|
|
||||||
case QEvent::FocusOut:
|
|
||||||
QApplication::sendEvent(this, e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QWidget::eventFilter(o, e);
|
|
||||||
}
|
|
||||||
7
3rdparty/qocoa/qsearchfield_nonmac.qrc
vendored
7
3rdparty/qocoa/qsearchfield_nonmac.qrc
vendored
@@ -1,7 +0,0 @@
|
|||||||
<RCC>
|
|
||||||
<qresource prefix="/Qocoa">
|
|
||||||
<file>qsearchfield_nonmac_clear.png</file>
|
|
||||||
<file>qsearchfield_nonmac_magnifier_menu.png</file>
|
|
||||||
<file>qsearchfield_nonmac_magnifier.png</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
||||||
BIN
3rdparty/qocoa/qsearchfield_nonmac_clear.png
vendored
BIN
3rdparty/qocoa/qsearchfield_nonmac_clear.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 736 B |
BIN
3rdparty/qocoa/qsearchfield_nonmac_magnifier.png
vendored
BIN
3rdparty/qocoa/qsearchfield_nonmac_magnifier.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 300 B |
Binary file not shown.
|
Before Width: | Height: | Size: 439 B |
48
3rdparty/singleapplication/CMakeLists.txt
vendored
48
3rdparty/singleapplication/CMakeLists.txt
vendored
@@ -1,14 +1,46 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.11)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
|
include(CheckIncludeFiles)
|
||||||
|
include(CheckFunctionExists)
|
||||||
|
|
||||||
|
if(CMAKE_VERSION VERSION_GREATER 3.0)
|
||||||
|
check_function_exists(geteuid HAVE_GETEUID)
|
||||||
|
check_function_exists(getpwuid HAVE_GETPWUID)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
|
set(SINGLEAPP-SOURCES singleapplication.cpp singleapplication_p.cpp)
|
||||||
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
|
set(SINGLEAPP-MOC-HEADERS singleapplication.h singleapplication_p.h)
|
||||||
QT5_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
if(BUILD_WITH_QT6)
|
||||||
ADD_LIBRARY(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
|
qt6_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||||
target_link_libraries(singleapplication Qt5::Core Qt5::Widgets Qt5::Network)
|
else()
|
||||||
|
qt5_wrap_cpp(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})
|
||||||
|
endif()
|
||||||
|
add_library(singleapplication STATIC ${SINGLEAPP-SOURCES} ${SINGLEAPP-SOURCES-MOC})
|
||||||
|
target_include_directories(singleapplication PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
target_link_libraries(singleapplication PRIVATE
|
||||||
|
${QtCore_LIBRARIES}
|
||||||
|
${QtWidgets_LIBRARIES}
|
||||||
|
${QtNetwork_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
|
set(SINGLECOREAPP-SOURCES singlecoreapplication.cpp singlecoreapplication_p.cpp)
|
||||||
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
|
set(SINGLECOREAPP-MOC-HEADERS singlecoreapplication.h singlecoreapplication_p.h)
|
||||||
QT5_WRAP_CPP(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
if(BUILD_WITH_QT6)
|
||||||
ADD_LIBRARY(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
qt6_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||||
target_link_libraries(singlecoreapplication Qt5::Core Qt5::Widgets Qt5::Network)
|
else()
|
||||||
|
qt5_wrap_cpp(SINGLECOREAPP-SOURCES-MOC ${SINGLECOREAPP-MOC-HEADERS})
|
||||||
|
endif()
|
||||||
|
add_library(singlecoreapplication STATIC ${SINGLECOREAPP-SOURCES} ${SINGLECOREAPP-SOURCES-MOC})
|
||||||
|
target_include_directories(singlecoreapplication PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
target_link_libraries(singlecoreapplication PRIVATE
|
||||||
|
${QtCore_LIBRARIES}
|
||||||
|
${QtNetwork_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(config.h.in "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||||
|
|||||||
2
3rdparty/singleapplication/config.h.in
vendored
Normal file
2
3rdparty/singleapplication/config.h.in
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#cmakedefine HAVE_GETEUID
|
||||||
|
#cmakedefine HAVE_GETPWUID
|
||||||
322
3rdparty/singleapplication/singleapplication.cpp
vendored
322
3rdparty/singleapplication/singleapplication.cpp
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -20,156 +20,250 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G !!!
|
||||||
|
// -----------------
|
||||||
|
//
|
||||||
|
// This is a modified version of SingleApplication,
|
||||||
|
// The original version is at:
|
||||||
|
//
|
||||||
|
// https://github.com/itay-grudev/SingleApplication
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QtCore/QTime>
|
#include <QThread>
|
||||||
#include <QtCore/QThread>
|
#include <QSharedMemory>
|
||||||
#include <QtCore/QDateTime>
|
#include <QLocalSocket>
|
||||||
#include <QtCore/QByteArray>
|
#include <QByteArray>
|
||||||
#include <QtCore/QSharedMemory>
|
#include <QElapsedTimer>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
#include "singleapplication.h"
|
#include "singleapplication.h"
|
||||||
#include "singleapplication_p.h"
|
#include "singleapplication_p.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor. Checks and fires up LocalServer or closes the program
|
* @brief Constructor. Checks and fires up LocalServer or closes the program if another instance already exists
|
||||||
* if another instance already exists
|
|
||||||
* @param argc
|
* @param argc
|
||||||
* @param argv
|
* @param argv
|
||||||
* @param {bool} allowSecondaryInstances
|
* @param allowSecondary Whether to enable secondary instance support
|
||||||
|
* @param options Optional flags to toggle specific behaviour
|
||||||
|
* @param timeout Maximum time blocking functions are allowed during app load
|
||||||
*/
|
*/
|
||||||
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
|
SingleApplication::SingleApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
||||||
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
|
: app_t(argc, argv),
|
||||||
{
|
d_ptr(new SingleApplicationPrivate(this)) {
|
||||||
Q_D(SingleApplication);
|
|
||||||
|
|
||||||
// Store the current mode of the program
|
Q_D(SingleApplication);
|
||||||
d->options = options;
|
|
||||||
|
|
||||||
// Generating an application ID used for identifying the shared memory
|
// Store the current mode of the program
|
||||||
// block and QLocalServer
|
d->options_ = options;
|
||||||
d->genBlockServerName();
|
|
||||||
|
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
||||||
|
d->genBlockServerName();
|
||||||
|
|
||||||
|
// To mitigate QSharedMemory issues with large amount of processes attempting to attach at the same time
|
||||||
|
d->randomSleep();
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
// By explicitly attaching it and then deleting it we make sure that the
|
// By explicitly attaching it and then deleting it we make sure that the memory is deleted even after the process has crashed on Unix.
|
||||||
// memory is deleted even after the process has crashed on Unix.
|
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||||
d->memory = new QSharedMemory( d->blockServerName );
|
d->memory_->attach();
|
||||||
d->memory->attach();
|
delete d->memory_;
|
||||||
delete d->memory;
|
|
||||||
#endif
|
#endif
|
||||||
// Guarantee thread safe behaviour with a shared memory block.
|
|
||||||
d->memory = new QSharedMemory( d->blockServerName );
|
|
||||||
|
|
||||||
// Create a shared memory block
|
// Guarantee thread safe behaviour with a shared memory block.
|
||||||
if( d->memory->create( sizeof( InstancesInfo ) ) ) {
|
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||||
// Initialize the shared memory block
|
|
||||||
d->memory->lock();
|
// Create a shared memory block
|
||||||
d->initializeMemoryBlock();
|
if (d->memory_->create(sizeof(InstancesInfo))) {
|
||||||
d->memory->unlock();
|
// Initialize the shared memory block
|
||||||
} else {
|
if (!d->memory_->lock()) {
|
||||||
// Attempt to attach to the memory segment
|
qCritical() << "SingleApplication: Unable to lock memory block after create.";
|
||||||
if( ! d->memory->attach() ) {
|
abortSafely();
|
||||||
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
}
|
||||||
qCritical() << d->memory->errorString();
|
d->initializeMemoryBlock();
|
||||||
delete d;
|
}
|
||||||
::exit( EXIT_FAILURE );
|
else {
|
||||||
}
|
if (d->memory_->error() == QSharedMemory::AlreadyExists) {
|
||||||
|
// Attempt to attach to the memory segment
|
||||||
|
if (!d->memory_->attach()) {
|
||||||
|
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
|
if (!d->memory_->lock()) {
|
||||||
|
qCritical() << "SingleApplication: Unable to lock memory block after attach.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qCritical() << "SingleApplication: Unable to create block.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(d->memory_->data());
|
||||||
|
QElapsedTimer time;
|
||||||
|
time.start();
|
||||||
|
|
||||||
|
// Make sure the shared memory block is initialised and in consistent state
|
||||||
|
forever {
|
||||||
|
// If the shared memory block's checksum is valid continue
|
||||||
|
if (d->blockChecksum() == inst->checksum) break;
|
||||||
|
|
||||||
|
// If more than 5s have elapsed, assume the primary instance crashed and assume it's position
|
||||||
|
if (time.elapsed() > 5000) {
|
||||||
|
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||||
|
d->initializeMemoryBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>( d->memory->data() );
|
// Otherwise wait for a random period and try again.
|
||||||
QTime time;
|
// The random sleep here limits the probability of a collision between two racing apps and allows the app to initialise faster
|
||||||
time.start();
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleApplication: Unable to unlock memory for random wait.";
|
||||||
// Make sure the shared memory block is initialised and in consistent state
|
qDebug() << d->memory_->errorString();
|
||||||
while( true ) {
|
|
||||||
d->memory->lock();
|
|
||||||
|
|
||||||
if( d->blockChecksum() == inst->checksum ) break;
|
|
||||||
|
|
||||||
if( time.elapsed() > 5000 ) {
|
|
||||||
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
|
||||||
d->initializeMemoryBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
d->memory->unlock();
|
|
||||||
|
|
||||||
// Random sleep here limits the probability of a collision between two racing apps
|
|
||||||
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
|
|
||||||
QThread::sleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ) );
|
|
||||||
}
|
}
|
||||||
|
d->randomSleep();
|
||||||
if( inst->primary == false) {
|
if (!d->memory_->lock()) {
|
||||||
d->startPrimary();
|
qCritical() << "SingleApplication: Unable to lock memory after random wait.";
|
||||||
d->memory->unlock();
|
abortSafely();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if another instance can be started
|
if (inst->primary == false) {
|
||||||
if( allowSecondary ) {
|
d->startPrimary();
|
||||||
inst->secondary += 1;
|
if (!d->memory_->unlock()) {
|
||||||
inst->checksum = d->blockChecksum();
|
qDebug() << "SingleApplication: Unable to unlock memory after primary start.";
|
||||||
d->instanceNumber = inst->secondary;
|
qDebug() << d->memory_->errorString();
|
||||||
d->startSecondary();
|
|
||||||
if( d->options & Mode::SecondaryNotification ) {
|
|
||||||
d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
|
|
||||||
}
|
|
||||||
d->memory->unlock();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
d->memory->unlock();
|
// Check if another instance can be started
|
||||||
|
if (allowSecondary) {
|
||||||
|
d->startSecondary();
|
||||||
|
if (d->options_ & Mode::SecondaryNotification) {
|
||||||
|
d->connectToPrimary(timeout, SingleApplicationPrivate::SecondaryInstance);
|
||||||
|
}
|
||||||
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleApplication: Unable to unlock memory after secondary start.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleApplication: Unable to unlock memory at end of execution.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
|
|
||||||
delete d;
|
d->connectToPrimary(timeout, SingleApplicationPrivate::NewInstance);
|
||||||
|
|
||||||
::exit( EXIT_SUCCESS );
|
delete d;
|
||||||
|
|
||||||
|
::exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleApplication::~SingleApplication() {
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destructor
|
* Checks if the current application instance is primary.
|
||||||
|
* @return Returns true if the instance is primary, false otherwise.
|
||||||
*/
|
*/
|
||||||
SingleApplication::~SingleApplication()
|
bool SingleApplication::isPrimary() {
|
||||||
{
|
Q_D(SingleApplication);
|
||||||
Q_D(SingleApplication);
|
return d->server_ != nullptr;
|
||||||
delete d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SingleApplication::isPrimary()
|
/**
|
||||||
{
|
* Checks if the current application instance is secondary.
|
||||||
Q_D(SingleApplication);
|
* @return Returns true if the instance is secondary, false otherwise.
|
||||||
return d->server != nullptr;
|
*/
|
||||||
|
bool SingleApplication::isSecondary() {
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
return d->server_ == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SingleApplication::isSecondary()
|
/**
|
||||||
{
|
* Allows you to identify an instance by returning unique consecutive instance ids.
|
||||||
Q_D(SingleApplication);
|
* It is reset when the first (primary) instance of your app starts and only incremented afterwards.
|
||||||
return d->server == nullptr;
|
* @return Returns a unique instance id.
|
||||||
|
*/
|
||||||
|
quint32 SingleApplication::instanceId() {
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
return d->instanceNumber_;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint32 SingleApplication::instanceId()
|
/**
|
||||||
{
|
* Returns the OS PID (Process Identifier) of the process running the primary instance.
|
||||||
Q_D(SingleApplication);
|
* Especially useful when SingleApplication is coupled with OS. specific APIs.
|
||||||
return d->instanceNumber;
|
* @return Returns the primary instance PID.
|
||||||
|
*/
|
||||||
|
qint64 SingleApplication::primaryPid() {
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
return d->primaryPid();
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 SingleApplication::primaryPid()
|
/**
|
||||||
{
|
* Returns the username the primary instance is running as.
|
||||||
Q_D(SingleApplication);
|
* @return Returns the username the primary instance is running as.
|
||||||
return d->primaryPid();
|
*/
|
||||||
|
QString SingleApplication::primaryUser() {
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
return d->primaryUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SingleApplication::sendMessage( QByteArray message, int timeout )
|
/**
|
||||||
{
|
* Returns the username the current instance is running as.
|
||||||
Q_D(SingleApplication);
|
* @return Returns the username the current instance is running as.
|
||||||
|
*/
|
||||||
// Nobody to connect to
|
QString SingleApplication::currentUser() {
|
||||||
if( isPrimary() ) return false;
|
Q_D(SingleApplication);
|
||||||
|
return d->getUsername();
|
||||||
// Make sure the socket is connected
|
}
|
||||||
d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect );
|
|
||||||
|
/**
|
||||||
d->socket->write( message );
|
* Sends message to the Primary Instance.
|
||||||
bool dataWritten = d->socket->flush();
|
* @param message The message to send.
|
||||||
d->socket->waitForBytesWritten( timeout );
|
* @param timeout the maximum timeout in milliseconds for blocking functions.
|
||||||
return dataWritten;
|
* @return true if the message was sent successfuly, false otherwise.
|
||||||
|
*/
|
||||||
|
bool SingleApplication::sendMessage(QByteArray message, int timeout) {
|
||||||
|
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
|
||||||
|
// Nobody to connect to
|
||||||
|
if (isPrimary()) return false;
|
||||||
|
|
||||||
|
// Make sure the socket is connected
|
||||||
|
if (!d->connectToPrimary(timeout, SingleApplicationPrivate::Reconnect))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
d->socket_->write(message);
|
||||||
|
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);
|
||||||
|
d->socket_->flush();
|
||||||
|
return dataWritten;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up the shared memory block and exits with a failure.
|
||||||
|
* This function halts program execution.
|
||||||
|
*/
|
||||||
|
void SingleApplication::abortSafely() {
|
||||||
|
|
||||||
|
Q_D(SingleApplication);
|
||||||
|
|
||||||
|
qCritical() << "SingleApplication: " << d->memory_->error() << d->memory_->errorString();
|
||||||
|
delete d;
|
||||||
|
::exit(EXIT_FAILURE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
195
3rdparty/singleapplication/singleapplication.h
vendored
195
3rdparty/singleapplication/singleapplication.h
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -20,111 +20,134 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G !!!
|
||||||
|
// -----------------
|
||||||
|
//
|
||||||
|
// This is a modified version of SingleApplication,
|
||||||
|
// The original version is at:
|
||||||
|
//
|
||||||
|
// https://github.com/itay-grudev/SingleApplication
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
#ifndef SINGLEAPPLICATION_H
|
#ifndef SINGLEAPPLICATION_H
|
||||||
#define SINGLEAPPLICATION_H
|
#define SINGLEAPPLICATION_H
|
||||||
|
|
||||||
#include <QtCore/QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QtNetwork/QLocalSocket>
|
#include <QFlags>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
class SingleApplicationPrivate;
|
class SingleApplicationPrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The SingleApplication class handles multipe instances of the same
|
* @brief The SingleApplication class handles multipe instances of the same Application
|
||||||
* Application
|
* @see QApplication
|
||||||
* @see QCoreApplication
|
|
||||||
*/
|
*/
|
||||||
class SingleApplication : public QApplication
|
class SingleApplication : public QApplication {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
typedef QApplication app_t;
|
typedef QApplication app_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Mode of operation of SingleApplication.
|
* @brief Mode of operation of SingleApplication.
|
||||||
* Whether the block should be user-wide or system-wide and whether the
|
* Whether the block should be user-wide or system-wide and whether the
|
||||||
* primary instance should be notified when a secondary instance had been
|
* primary instance should be notified when a secondary instance had been
|
||||||
* started.
|
* started.
|
||||||
* @note Operating system can restrict the shared memory blocks to the same
|
* @note Operating system can restrict the shared memory blocks to the same
|
||||||
* user, in which case the User/System modes will have no effect and the
|
* user, in which case the User/System modes will have no effect and the
|
||||||
* block will be user wide.
|
* block will be user wide.
|
||||||
* @enum
|
* @enum
|
||||||
*/
|
*/
|
||||||
enum Mode {
|
enum Mode {
|
||||||
User = 1 << 0,
|
User = 1 << 0,
|
||||||
System = 1 << 1,
|
System = 1 << 1,
|
||||||
SecondaryNotification = 1 << 2,
|
SecondaryNotification = 1 << 2,
|
||||||
ExcludeAppVersion = 1 << 3,
|
ExcludeAppVersion = 1 << 3,
|
||||||
ExcludeAppPath = 1 << 4
|
ExcludeAppPath = 1 << 4
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Options, Mode)
|
Q_DECLARE_FLAGS(Options, Mode)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Intitializes a SingleApplication instance with argc command line
|
* @brief Intitializes a SingleApplication instance with argc command line
|
||||||
* arguments in argv
|
* arguments in argv
|
||||||
* @arg {int &} argc - Number of arguments in argv
|
* @arg {int &} argc - Number of arguments in argv
|
||||||
* @arg {const char *[]} argv - Supplied command line arguments
|
* @arg {const char *[]} argv - Supplied command line arguments
|
||||||
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
||||||
* if there is already a primary instance.
|
* if there is already a primary instance.
|
||||||
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
|
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
|
||||||
* User wide or System wide.
|
* User wide or System wide.
|
||||||
* @arg {int} timeout - Timeout to wait in miliseconds.
|
* @arg {int} timeout - Timeout to wait in milliseconds.
|
||||||
* @note argc and argv may be changed as Qt removes arguments that it
|
* @note argc and argv may be changed as Qt removes arguments that it
|
||||||
* recognizes
|
* recognizes
|
||||||
* @note Mode::SecondaryNotification only works if set on both the primary
|
* @note Mode::SecondaryNotification only works if set on both the primary
|
||||||
* instance and the secondary instance.
|
* instance and the secondary instance.
|
||||||
* @note The timeout is just a hint for the maximum time of blocking
|
* @note The timeout is just a hint for the maximum time of blocking
|
||||||
* operations. It does not guarantee that the SingleApplication
|
* operations. It does not guarantee that the SingleApplication
|
||||||
* initialisation will be completed in given time, though is a good hint.
|
* initialisation will be completed in given time, though is a good hint.
|
||||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||||
*/
|
*/
|
||||||
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
explicit SingleApplication(int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000);
|
||||||
~SingleApplication();
|
~SingleApplication() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns if the instance is the primary instance
|
* @brief Returns if the instance is the primary instance
|
||||||
* @returns {bool}
|
* @returns {bool}
|
||||||
*/
|
*/
|
||||||
bool isPrimary();
|
bool isPrimary();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns if the instance is a secondary instance
|
* @brief Returns if the instance is a secondary instance
|
||||||
* @returns {bool}
|
* @returns {bool}
|
||||||
*/
|
*/
|
||||||
bool isSecondary();
|
bool isSecondary();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a unique identifier for the current instance
|
* @brief Returns a unique identifier for the current instance
|
||||||
* @returns {qint32}
|
* @returns {qint32}
|
||||||
*/
|
*/
|
||||||
quint32 instanceId();
|
quint32 instanceId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the process ID (PID) of the primary instance
|
* @brief Returns the process ID (PID) of the primary instance
|
||||||
* @returns {qint64}
|
* @returns {qint64}
|
||||||
*/
|
*/
|
||||||
qint64 primaryPid();
|
qint64 primaryPid();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sends a message to the primary instance. Returns true on success.
|
* @brief Returns the username of the user running the primary instance
|
||||||
* @param {int} timeout - Timeout for connecting
|
* @returns {QString}
|
||||||
* @returns {bool}
|
*/
|
||||||
* @note sendMessage() will return false if invoked from the primary
|
QString primaryUser();
|
||||||
* instance.
|
|
||||||
*/
|
|
||||||
bool sendMessage( QByteArray message, int timeout = 100 );
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
/**
|
||||||
void instanceStarted();
|
* @brief Returns the username of the current user
|
||||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
* @returns {QString}
|
||||||
|
*/
|
||||||
|
QString currentUser();
|
||||||
|
|
||||||
private:
|
/**
|
||||||
SingleApplicationPrivate *d_ptr;
|
* @brief Sends a message to the primary instance. Returns true on success.
|
||||||
Q_DECLARE_PRIVATE(SingleApplication)
|
* @param {int} timeout - Timeout for connecting
|
||||||
|
* @returns {bool}
|
||||||
|
* @note sendMessage() will return false if invoked from the primary
|
||||||
|
* instance.
|
||||||
|
*/
|
||||||
|
bool sendMessage(QByteArray message, int timeout = 1000);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void instanceStarted();
|
||||||
|
void receivedMessage(quint32 instanceId, QByteArray message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SingleApplicationPrivate *d_ptr;
|
||||||
|
Q_DECLARE_PRIVATE(SingleApplication)
|
||||||
|
void abortSafely();
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
|
||||||
|
|
||||||
#endif // SINGLEAPPLICATION_H
|
#endif // SINGLEAPPLICATION_H
|
||||||
|
|||||||
718
3rdparty/singleapplication/singleapplication_p.cpp
vendored
718
3rdparty/singleapplication/singleapplication_p.cpp
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,381 +24,447 @@
|
|||||||
// W A R N I N G !!!
|
// W A R N I N G !!!
|
||||||
// -----------------
|
// -----------------
|
||||||
//
|
//
|
||||||
// This file is not part of the SingleApplication API. It is used purely as an
|
// This is a modified version of SingleApplication,
|
||||||
// implementation detail. This header file may change from version to
|
// The original version is at:
|
||||||
// version without notice, or may even be removed.
|
|
||||||
//
|
//
|
||||||
|
// https://github.com/itay-grudev/SingleApplication
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include <QtCore/QDir>
|
#ifdef Q_OS_UNIX
|
||||||
#include <QtCore/QProcess>
|
# include <unistd.h>
|
||||||
#include <QtCore/QByteArray>
|
# include <sys/types.h>
|
||||||
#include <QtCore/QSemaphore>
|
# include <pwd.h>
|
||||||
#include <QtCore/QDataStream>
|
#endif
|
||||||
#include <QtCore/QStandardPaths>
|
|
||||||
#include <QtCore/QCryptographicHash>
|
#include <QObject>
|
||||||
#include <QtNetwork/QLocalServer>
|
#include <QThread>
|
||||||
#include <QtNetwork/QLocalSocket>
|
#include <QIODevice>
|
||||||
|
#include <QSharedMemory>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QLocalServer>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
# include <QRandomGenerator>
|
||||||
|
#else
|
||||||
|
# include <QDateTime>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "singleapplication.h"
|
#include "singleapplication.h"
|
||||||
#include "singleapplication_p.h"
|
#include "singleapplication_p.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <windows.h>
|
# include <windows.h>
|
||||||
#include <lmcons.h>
|
# include <lmcons.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
|
SingleApplicationPrivate::SingleApplicationPrivate(SingleApplication *ptr)
|
||||||
: q_ptr( q_ptr )
|
: q_ptr(ptr),
|
||||||
{
|
memory_(nullptr),
|
||||||
server = nullptr;
|
socket_(nullptr),
|
||||||
socket = nullptr;
|
server_(nullptr),
|
||||||
memory = nullptr;
|
instanceNumber_(-1) {}
|
||||||
instanceNumber = -1;
|
|
||||||
|
SingleApplicationPrivate::~SingleApplicationPrivate() {
|
||||||
|
|
||||||
|
if (socket_ != nullptr) {
|
||||||
|
socket_->close();
|
||||||
|
delete socket_;
|
||||||
|
socket_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memory_ != nullptr) {
|
||||||
|
memory_->lock();
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
if (server_ != nullptr) {
|
||||||
|
server_->close();
|
||||||
|
delete server_;
|
||||||
|
inst->primary = false;
|
||||||
|
inst->primaryPid = -1;
|
||||||
|
inst->primaryUser[0] = '\0';
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
}
|
||||||
|
memory_->unlock();
|
||||||
|
|
||||||
|
delete memory_;
|
||||||
|
memory_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleApplicationPrivate::~SingleApplicationPrivate()
|
QString SingleApplicationPrivate::getUsername() {
|
||||||
{
|
|
||||||
if( socket != nullptr ) {
|
|
||||||
socket->close();
|
|
||||||
delete socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
memory->lock();
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
|
||||||
if( server != nullptr ) {
|
|
||||||
server->close();
|
|
||||||
delete server;
|
|
||||||
inst->primary = false;
|
|
||||||
inst->primaryPid = -1;
|
|
||||||
inst->checksum = blockChecksum();
|
|
||||||
}
|
|
||||||
memory->unlock();
|
|
||||||
|
|
||||||
delete memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleApplicationPrivate::genBlockServerName()
|
|
||||||
{
|
|
||||||
QCryptographicHash appData( QCryptographicHash::Sha256 );
|
|
||||||
appData.addData( "SingleApplication", 17 );
|
|
||||||
appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
|
|
||||||
appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
|
|
||||||
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
|
|
||||||
|
|
||||||
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) {
|
|
||||||
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) {
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
|
|
||||||
#else
|
|
||||||
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// User level block requires a user specific data in the hash
|
|
||||||
if( options & SingleApplication::Mode::User ) {
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
wchar_t username [ UNLEN + 1 ];
|
|
||||||
// Specifies size of the buffer on input
|
|
||||||
DWORD usernameLength = UNLEN + 1;
|
|
||||||
if( GetUserNameW( username, &usernameLength ) ) {
|
|
||||||
appData.addData( QString::fromWCharArray(username).toUtf8() );
|
|
||||||
} else {
|
|
||||||
appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
QProcess process;
|
QString username;
|
||||||
process.start( "whoami" );
|
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||||
if( process.waitForFinished( 100 ) &&
|
struct passwd *pw = getpwuid(geteuid());
|
||||||
process.exitCode() == QProcess::NormalExit) {
|
if (pw) {
|
||||||
appData.addData( process.readLine() );
|
username = QString::fromLocal8Bit(pw->pw_name);
|
||||||
} else {
|
}
|
||||||
appData.addData(
|
|
||||||
QDir(
|
|
||||||
QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).first()
|
|
||||||
).absolutePath().toUtf8()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
if (username.isEmpty()) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
|
username = qEnvironmentVariable("USER");
|
||||||
// server naming requirements.
|
#else
|
||||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
username = QString::fromLocal8Bit(qgetenv("USER"));
|
||||||
}
|
#endif
|
||||||
|
}
|
||||||
void SingleApplicationPrivate::initializeMemoryBlock()
|
return username;
|
||||||
{
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
|
||||||
inst->primary = false;
|
|
||||||
inst->secondary = 0;
|
|
||||||
inst->primaryPid = -1;
|
|
||||||
inst->checksum = blockChecksum();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleApplicationPrivate::startPrimary()
|
|
||||||
{
|
|
||||||
Q_Q(SingleApplication);
|
|
||||||
|
|
||||||
// Successful creation means that no main process exists
|
|
||||||
// So we start a QLocalServer to listen for connections
|
|
||||||
QLocalServer::removeServer( blockServerName );
|
|
||||||
server = new QLocalServer();
|
|
||||||
|
|
||||||
// Restrict access to the socket according to the
|
|
||||||
// SingleApplication::Mode::User flag on User level or no restrictions
|
|
||||||
if( options & SingleApplication::Mode::User ) {
|
|
||||||
server->setSocketOptions( QLocalServer::UserAccessOption );
|
|
||||||
} else {
|
|
||||||
server->setSocketOptions( QLocalServer::WorldAccessOption );
|
|
||||||
}
|
|
||||||
|
|
||||||
server->listen( blockServerName );
|
|
||||||
QObject::connect(
|
|
||||||
server,
|
|
||||||
&QLocalServer::newConnection,
|
|
||||||
this,
|
|
||||||
&SingleApplicationPrivate::slotConnectionEstablished
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reset the number of connections
|
|
||||||
InstancesInfo* inst = static_cast <InstancesInfo*>( memory->data() );
|
|
||||||
|
|
||||||
inst->primary = true;
|
|
||||||
inst->primaryPid = q->applicationPid();
|
|
||||||
inst->checksum = blockChecksum();
|
|
||||||
|
|
||||||
instanceNumber = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleApplicationPrivate::startSecondary()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
|
|
||||||
{
|
|
||||||
// Connect to the Local Server of the Primary Instance if not already
|
|
||||||
// connected.
|
|
||||||
if( socket == nullptr ) {
|
|
||||||
socket = new QLocalSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If already connected - we are done;
|
|
||||||
if( socket->state() == QLocalSocket::ConnectedState )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If not connect
|
|
||||||
if( socket->state() == QLocalSocket::UnconnectedState ||
|
|
||||||
socket->state() == QLocalSocket::ClosingState ) {
|
|
||||||
socket->connectToServer( blockServerName );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for being connected
|
|
||||||
if( socket->state() == QLocalSocket::ConnectingState ) {
|
|
||||||
socket->waitForConnected( msecs );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialisation message according to the SingleApplication protocol
|
|
||||||
if( socket->state() == QLocalSocket::ConnectedState ) {
|
|
||||||
// Notify the parent that a new instance had been started;
|
|
||||||
QByteArray initMsg;
|
|
||||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
|
||||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
writeStream << blockServerName.toLatin1();
|
#ifdef Q_OS_WIN
|
||||||
writeStream << static_cast<quint8>(connectionType);
|
wchar_t username[UNLEN + 1];
|
||||||
writeStream << instanceNumber;
|
// Specifies size of the buffer on input
|
||||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
DWORD usernameLength = UNLEN + 1;
|
||||||
writeStream << checksum;
|
if (GetUserNameW(username, &usernameLength)) {
|
||||||
|
return QString::fromWCharArray(username);
|
||||||
// The header indicates the message length that follows
|
}
|
||||||
QByteArray header;
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
return qEnvironmentVariable("USERNAME");
|
||||||
|
#else
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
return QString::fromLocal8Bit(qgetenv("USERNAME"));
|
||||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
#endif
|
||||||
#endif
|
#endif
|
||||||
headerStream << static_cast <quint64>( initMsg.length() );
|
|
||||||
|
|
||||||
socket->write( header );
|
}
|
||||||
socket->write( initMsg );
|
|
||||||
socket->flush();
|
void SingleApplicationPrivate::genBlockServerName() {
|
||||||
socket->waitForBytesWritten( msecs );
|
|
||||||
|
QCryptographicHash appData(QCryptographicHash::Sha256);
|
||||||
|
appData.addData("SingleApplication", 17);
|
||||||
|
appData.addData(SingleApplication::app_t::applicationName().toUtf8());
|
||||||
|
appData.addData(SingleApplication::app_t::organizationName().toUtf8());
|
||||||
|
appData.addData(SingleApplication::app_t::organizationDomain().toUtf8());
|
||||||
|
|
||||||
|
if (!(options_ & SingleApplication::Mode::ExcludeAppVersion)) {
|
||||||
|
appData.addData(SingleApplication::app_t::applicationVersion().toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(options_ & SingleApplication::Mode::ExcludeAppPath)) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
appData.addData(SingleApplication::app_t::applicationFilePath().toLower().toUtf8());
|
||||||
|
#else
|
||||||
|
appData.addData(SingleApplication::app_t::applicationFilePath().toUtf8());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// User level block requires a user specific data in the hash
|
||||||
|
if (options_ & SingleApplication::Mode::User) {
|
||||||
|
appData.addData(getUsername().toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
||||||
|
blockServerName_ = appData.result().toBase64().replace("/", "_");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleApplicationPrivate::initializeMemoryBlock() {
|
||||||
|
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
inst->primary = false;
|
||||||
|
inst->secondary = 0;
|
||||||
|
inst->primaryPid = -1;
|
||||||
|
inst->primaryUser[0] = '\0';
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleApplicationPrivate::startPrimary() {
|
||||||
|
|
||||||
|
Q_Q(SingleApplication);
|
||||||
|
|
||||||
|
// Reset the number of connections
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
|
||||||
|
inst->primary = true;
|
||||||
|
inst->primaryPid = q->applicationPid();
|
||||||
|
qstrncpy(inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser));
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
instanceNumber_ = 0;
|
||||||
|
// Successful creation means that no main process exists
|
||||||
|
// So we start a QLocalServer to listen for connections
|
||||||
|
QLocalServer::removeServer(blockServerName_);
|
||||||
|
server_ = new QLocalServer();
|
||||||
|
|
||||||
|
// Restrict access to the socket according to the SingleApplication::Mode::User flag on User level or no restrictions
|
||||||
|
if (options_ & SingleApplication::Mode::User) {
|
||||||
|
server_->setSocketOptions(QLocalServer::UserAccessOption);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
server_->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
server_->listen(blockServerName_);
|
||||||
|
QObject::connect(server_, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleApplicationPrivate::startSecondary() {
|
||||||
|
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
|
||||||
|
inst->secondary += 1;
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
instanceNumber_ = inst->secondary;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleApplicationPrivate::connectToPrimary(const int timeout, const ConnectionType connectionType) {
|
||||||
|
|
||||||
|
QElapsedTimer time;
|
||||||
|
time.start();
|
||||||
|
|
||||||
|
// Connect to the Local Server of the Primary Instance if not already connected.
|
||||||
|
if (socket_ == nullptr) {
|
||||||
|
socket_ = new QLocalSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectedState) return true;
|
||||||
|
|
||||||
|
if (socket_->state() != QLocalSocket::ConnectedState) {
|
||||||
|
|
||||||
|
forever {
|
||||||
|
randomSleep();
|
||||||
|
|
||||||
|
if (socket_->state() != QLocalSocket::ConnectingState)
|
||||||
|
socket_->connectToServer(blockServerName_);
|
||||||
|
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectingState) {
|
||||||
|
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If connected break out of the loop
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectedState) break;
|
||||||
|
|
||||||
|
// If elapsed time since start is longer than the method timeout return
|
||||||
|
if (time.elapsed() >= timeout) return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisation message according to the SingleApplication protocol
|
||||||
|
QByteArray initMsg;
|
||||||
|
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||||
|
writeStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
|
||||||
|
writeStream << blockServerName_.toLatin1();
|
||||||
|
writeStream << static_cast<quint8>(connectionType);
|
||||||
|
writeStream << instanceNumber_;
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
|
||||||
|
#else
|
||||||
|
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
writeStream << checksum;
|
||||||
|
|
||||||
|
// The header indicates the message length that follows
|
||||||
|
QByteArray header;
|
||||||
|
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||||
|
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
headerStream << static_cast<quint64>(initMsg.length());
|
||||||
|
|
||||||
|
socket_->write(header);
|
||||||
|
socket_->write(initMsg);
|
||||||
|
bool result = socket_->waitForBytesWritten(static_cast<int>(timeout - time.elapsed()));
|
||||||
|
socket_->flush();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quint16 SingleApplicationPrivate::blockChecksum()
|
quint16 SingleApplicationPrivate::blockChecksum() {
|
||||||
{
|
|
||||||
return qChecksum(
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
static_cast <const char *>( memory->data() ),
|
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
|
||||||
offsetof( InstancesInfo, checksum )
|
#else
|
||||||
);
|
quint16 checksum = qChecksum(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return checksum;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 SingleApplicationPrivate::primaryPid()
|
qint64 SingleApplicationPrivate::primaryPid() {
|
||||||
{
|
|
||||||
qint64 pid;
|
|
||||||
|
|
||||||
memory->lock();
|
memory_->lock();
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
pid = inst->primaryPid;
|
qint64 pid = inst->primaryPid;
|
||||||
memory->unlock();
|
memory_->unlock();
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SingleApplicationPrivate::primaryUser() {
|
||||||
|
|
||||||
|
memory_->lock();
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
QByteArray username = inst->primaryUser;
|
||||||
|
memory_->unlock();
|
||||||
|
|
||||||
|
return QString::fromUtf8(username);
|
||||||
|
|
||||||
return pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Executed when a connection has been made to the LocalServer
|
* @brief Executed when a connection has been made to the LocalServer
|
||||||
*/
|
*/
|
||||||
void SingleApplicationPrivate::slotConnectionEstablished()
|
void SingleApplicationPrivate::slotConnectionEstablished() {
|
||||||
{
|
|
||||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
|
||||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
|
||||||
[nextConnSocket, this]() {
|
connectionMap_.insert(nextConnSocket, ConnectionInfo());
|
||||||
auto &info = connectionMap[nextConnSocket];
|
|
||||||
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
|
||||||
[nextConnSocket, this](){
|
auto &info = connectionMap_[nextConnSocket];
|
||||||
connectionMap.remove(nextConnSocket);
|
slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||||
nextConnSocket->deleteLater();
|
});
|
||||||
}
|
|
||||||
);
|
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, this, [nextConnSocket, this]() {
|
||||||
|
connectionMap_.remove(nextConnSocket);
|
||||||
|
nextConnSocket->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
|
||||||
|
auto &info = connectionMap_[nextConnSocket];
|
||||||
|
switch (info.stage) {
|
||||||
|
case StageHeader:
|
||||||
|
readInitMessageHeader(nextConnSocket);
|
||||||
|
break;
|
||||||
|
case StageBody:
|
||||||
|
readInitMessageBody(nextConnSocket);
|
||||||
|
break;
|
||||||
|
case StageConnected:
|
||||||
|
slotDataAvailable(nextConnSocket, info.instanceId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
|
||||||
[nextConnSocket, this]() {
|
|
||||||
auto &info = connectionMap[nextConnSocket];
|
|
||||||
switch(info.stage) {
|
|
||||||
case StageHeader:
|
|
||||||
readInitMessageHeader(nextConnSocket);
|
|
||||||
break;
|
|
||||||
case StageBody:
|
|
||||||
readInitMessageBody(nextConnSocket);
|
|
||||||
break;
|
|
||||||
case StageConnected:
|
|
||||||
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
|
void SingleApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||||
{
|
|
||||||
if (!connectionMap.contains( sock )) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) {
|
if (!connectionMap_.contains(sock)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDataStream headerStream( sock );
|
if (sock->bytesAvailable() < static_cast<qint64>(sizeof(quint64))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
QDataStream headerStream(sock);
|
||||||
headerStream.setVersion( QDataStream::Qt_5_6 );
|
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
|
||||||
|
// Read the header to know the message length
|
||||||
|
quint64 msgLen = 0;
|
||||||
|
headerStream >> msgLen;
|
||||||
|
ConnectionInfo &info = connectionMap_[sock];
|
||||||
|
info.stage = StageBody;
|
||||||
|
info.msgLen = msgLen;
|
||||||
|
|
||||||
|
if (sock->bytesAvailable() >= static_cast<qint64>(msgLen)) {
|
||||||
|
readInitMessageBody(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||||
|
|
||||||
|
Q_Q(SingleApplication);
|
||||||
|
|
||||||
|
if (!connectionMap_.contains(sock)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionInfo &info = connectionMap_[sock];
|
||||||
|
if (sock->bytesAvailable() < static_cast<qint64>(info.msgLen)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the message body
|
||||||
|
QByteArray msgBytes = sock->read(info.msgLen);
|
||||||
|
QDataStream readStream(msgBytes);
|
||||||
|
readStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
|
||||||
|
// server name
|
||||||
|
QByteArray latin1Name;
|
||||||
|
readStream >> latin1Name;
|
||||||
|
|
||||||
|
// connection type
|
||||||
|
ConnectionType connectionType = InvalidConnection;
|
||||||
|
quint8 connTypeVal = InvalidConnection;
|
||||||
|
readStream >> connTypeVal;
|
||||||
|
connectionType = static_cast<ConnectionType>(connTypeVal);
|
||||||
|
|
||||||
|
// instance id
|
||||||
|
quint32 instanceId = 0;
|
||||||
|
readStream >> instanceId;
|
||||||
|
|
||||||
|
// checksum
|
||||||
|
quint16 msgChecksum = 0;
|
||||||
|
readStream >> msgChecksum;
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
|
||||||
|
#else
|
||||||
|
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Read the header to know the message length
|
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName_ && msgChecksum == actualChecksum;
|
||||||
quint64 msgLen = 0;
|
|
||||||
headerStream >> msgLen;
|
if (!isValid) {
|
||||||
ConnectionInfo &info = connectionMap[sock];
|
sock->close();
|
||||||
info.stage = StageBody;
|
return;
|
||||||
info.msgLen = msgLen;
|
}
|
||||||
|
|
||||||
|
info.instanceId = instanceId;
|
||||||
|
info.stage = StageConnected;
|
||||||
|
|
||||||
|
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options_ & SingleApplication::Mode::SecondaryNotification)) {
|
||||||
|
Q_EMIT q->instanceStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sock->bytesAvailable() > 0) {
|
||||||
|
slotDataAvailable(sock, instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
if ( sock->bytesAvailable() >= (qint64) msgLen ) {
|
|
||||||
readInitMessageBody( sock );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
|
void SingleApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, const quint32 instanceId) {
|
||||||
{
|
|
||||||
Q_Q(SingleApplication);
|
|
||||||
|
|
||||||
if (!connectionMap.contains( sock )) {
|
Q_Q(SingleApplication);
|
||||||
return;
|
Q_EMIT q->receivedMessage(instanceId, dataSocket->readAll());
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionInfo &info = connectionMap[sock];
|
}
|
||||||
if( sock->bytesAvailable() < ( qint64 )info.msgLen ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the message body
|
void SingleApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
|
||||||
QByteArray msgBytes = sock->read(info.msgLen);
|
|
||||||
QDataStream readStream(msgBytes);
|
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
if (closedSocket->bytesAvailable() > 0) {
|
||||||
readStream.setVersion( QDataStream::Qt_5_6 );
|
slotDataAvailable(closedSocket, instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleApplicationPrivate::randomSleep() {
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
|
||||||
|
#else
|
||||||
|
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||||
|
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// server name
|
|
||||||
QByteArray latin1Name;
|
|
||||||
readStream >> latin1Name;
|
|
||||||
|
|
||||||
// connection type
|
|
||||||
ConnectionType connectionType = InvalidConnection;
|
|
||||||
quint8 connTypeVal = InvalidConnection;
|
|
||||||
readStream >> connTypeVal;
|
|
||||||
connectionType = static_cast <ConnectionType>( connTypeVal );
|
|
||||||
|
|
||||||
// instance id
|
|
||||||
quint32 instanceId = 0;
|
|
||||||
readStream >> instanceId;
|
|
||||||
|
|
||||||
// checksum
|
|
||||||
quint16 msgChecksum = 0;
|
|
||||||
readStream >> msgChecksum;
|
|
||||||
|
|
||||||
const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast<quint32>( msgBytes.length() - sizeof( quint16 ) ) );
|
|
||||||
|
|
||||||
bool isValid = readStream.status() == QDataStream::Ok &&
|
|
||||||
QLatin1String(latin1Name) == blockServerName &&
|
|
||||||
msgChecksum == actualChecksum;
|
|
||||||
|
|
||||||
if( !isValid ) {
|
|
||||||
sock->close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.instanceId = instanceId;
|
|
||||||
info.stage = StageConnected;
|
|
||||||
|
|
||||||
if( connectionType == NewInstance ||
|
|
||||||
( connectionType == SecondaryInstance &&
|
|
||||||
options & SingleApplication::Mode::SecondaryNotification ) )
|
|
||||||
{
|
|
||||||
Q_EMIT q->instanceStarted();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sock->bytesAvailable() > 0) {
|
|
||||||
Q_EMIT this->slotDataAvailable( sock, instanceId );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
|
|
||||||
{
|
|
||||||
Q_Q(SingleApplication);
|
|
||||||
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
|
|
||||||
{
|
|
||||||
if( closedSocket->bytesAvailable() > 0 )
|
|
||||||
Q_EMIT slotDataAvailable( closedSocket, instanceId );
|
|
||||||
}
|
}
|
||||||
|
|||||||
120
3rdparty/singleapplication/singleapplication_p.h
vendored
120
3rdparty/singleapplication/singleapplication_p.h
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2016
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,76 +24,88 @@
|
|||||||
// W A R N I N G !!!
|
// W A R N I N G !!!
|
||||||
// -----------------
|
// -----------------
|
||||||
//
|
//
|
||||||
// This file is not part of the SingleApplication API. It is used purely as an
|
// This is a modified version of SingleApplication,
|
||||||
// implementation detail. This header file may change from version to
|
// The original version is at:
|
||||||
// version without notice, or may even be removed.
|
//
|
||||||
|
// https://github.com/itay-grudev/SingleApplication
|
||||||
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef SINGLEAPPLICATION_P_H
|
#ifndef SINGLEAPPLICATION_P_H
|
||||||
#define SINGLEAPPLICATION_P_H
|
#define SINGLEAPPLICATION_P_H
|
||||||
|
|
||||||
#include <QtCore/QSharedMemory>
|
#include <QtGlobal>
|
||||||
#include <QtNetwork/QLocalServer>
|
#include <QObject>
|
||||||
#include <QtNetwork/QLocalSocket>
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
#include "singleapplication.h"
|
#include "singleapplication.h"
|
||||||
|
|
||||||
|
class QLocalServer;
|
||||||
|
class QLocalSocket;
|
||||||
|
class QSharedMemory;
|
||||||
|
|
||||||
struct InstancesInfo {
|
struct InstancesInfo {
|
||||||
bool primary;
|
bool primary;
|
||||||
quint32 secondary;
|
quint32 secondary;
|
||||||
qint64 primaryPid;
|
qint64 primaryPid;
|
||||||
quint16 checksum;
|
char primaryUser[128];
|
||||||
|
quint16 checksum;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConnectionInfo {
|
struct ConnectionInfo {
|
||||||
explicit ConnectionInfo() :
|
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
|
||||||
msgLen(0), instanceId(0), stage(0) {}
|
qint64 msgLen;
|
||||||
qint64 msgLen;
|
quint32 instanceId;
|
||||||
quint32 instanceId;
|
quint8 stage;
|
||||||
quint8 stage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SingleApplicationPrivate : public QObject {
|
class SingleApplicationPrivate : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
|
||||||
enum ConnectionType : quint8 {
|
|
||||||
InvalidConnection = 0,
|
|
||||||
NewInstance = 1,
|
|
||||||
SecondaryInstance = 2,
|
|
||||||
Reconnect = 3
|
|
||||||
};
|
|
||||||
enum ConnectionStage : quint8 {
|
|
||||||
StageHeader = 0,
|
|
||||||
StageBody = 1,
|
|
||||||
StageConnected = 2,
|
|
||||||
};
|
|
||||||
Q_DECLARE_PUBLIC(SingleApplication)
|
|
||||||
|
|
||||||
SingleApplicationPrivate( SingleApplication *q_ptr );
|
public:
|
||||||
~SingleApplicationPrivate();
|
enum ConnectionType : quint8 {
|
||||||
|
InvalidConnection = 0,
|
||||||
|
NewInstance = 1,
|
||||||
|
SecondaryInstance = 2,
|
||||||
|
Reconnect = 3
|
||||||
|
};
|
||||||
|
enum ConnectionStage : quint8 {
|
||||||
|
StageHeader = 0,
|
||||||
|
StageBody = 1,
|
||||||
|
StageConnected = 2,
|
||||||
|
};
|
||||||
|
Q_DECLARE_PUBLIC(SingleApplication)
|
||||||
|
|
||||||
void genBlockServerName();
|
explicit SingleApplicationPrivate(SingleApplication *ptr);
|
||||||
void initializeMemoryBlock();
|
~SingleApplicationPrivate() override;
|
||||||
void startPrimary();
|
|
||||||
void startSecondary();
|
|
||||||
void connectToPrimary(int msecs, ConnectionType connectionType );
|
|
||||||
quint16 blockChecksum();
|
|
||||||
qint64 primaryPid();
|
|
||||||
void readInitMessageHeader(QLocalSocket *socket);
|
|
||||||
void readInitMessageBody(QLocalSocket *socket);
|
|
||||||
|
|
||||||
SingleApplication *q_ptr;
|
QString getUsername();
|
||||||
QSharedMemory *memory;
|
void genBlockServerName();
|
||||||
QLocalSocket *socket;
|
void initializeMemoryBlock();
|
||||||
QLocalServer *server;
|
void startPrimary();
|
||||||
quint32 instanceNumber;
|
void startSecondary();
|
||||||
QString blockServerName;
|
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
|
||||||
SingleApplication::Options options;
|
quint16 blockChecksum();
|
||||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
qint64 primaryPid();
|
||||||
|
QString primaryUser();
|
||||||
|
void readInitMessageHeader(QLocalSocket *socket);
|
||||||
|
void readInitMessageBody(QLocalSocket *socket);
|
||||||
|
void randomSleep();
|
||||||
|
|
||||||
public Q_SLOTS:
|
SingleApplication *q_ptr;
|
||||||
void slotConnectionEstablished();
|
QSharedMemory *memory_;
|
||||||
void slotDataAvailable( QLocalSocket*, quint32 );
|
QLocalSocket *socket_;
|
||||||
void slotClientConnectionClosed( QLocalSocket*, quint32 );
|
QLocalServer *server_;
|
||||||
|
quint32 instanceNumber_;
|
||||||
|
QString blockServerName_;
|
||||||
|
SingleApplication::Options options_;
|
||||||
|
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotConnectionEstablished();
|
||||||
|
void slotDataAvailable(QLocalSocket*, const quint32);
|
||||||
|
void slotClientConnectionClosed(QLocalSocket*, const quint32);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SINGLEAPPLICATION_P_H
|
#endif // SINGLEAPPLICATION_P_H
|
||||||
|
|||||||
322
3rdparty/singleapplication/singlecoreapplication.cpp
vendored
322
3rdparty/singleapplication/singlecoreapplication.cpp
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -20,156 +20,250 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G !!!
|
||||||
|
// -----------------
|
||||||
|
//
|
||||||
|
// This is a modified version of SingleApplication,
|
||||||
|
// The original version is at:
|
||||||
|
//
|
||||||
|
// https://github.com/itay-grudev/SingleApplication
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QtCore/QTime>
|
#include <QThread>
|
||||||
#include <QtCore/QThread>
|
#include <QSharedMemory>
|
||||||
#include <QtCore/QDateTime>
|
#include <QLocalSocket>
|
||||||
#include <QtCore/QByteArray>
|
#include <QByteArray>
|
||||||
#include <QtCore/QSharedMemory>
|
#include <QElapsedTimer>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
#include "singlecoreapplication.h"
|
#include "singlecoreapplication.h"
|
||||||
#include "singlecoreapplication_p.h"
|
#include "singlecoreapplication_p.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor. Checks and fires up LocalServer or closes the program
|
* @brief Constructor. Checks and fires up LocalServer or closes the program if another instance already exists
|
||||||
* if another instance already exists
|
|
||||||
* @param argc
|
* @param argc
|
||||||
* @param argv
|
* @param argv
|
||||||
* @param {bool} allowSecondaryInstances
|
* @param allowSecondary Whether to enable secondary instance support
|
||||||
|
* @param options Optional flags to toggle specific behaviour
|
||||||
|
* @param timeout Maximum time blocking functions are allowed during app load
|
||||||
*/
|
*/
|
||||||
SingleCoreApplication::SingleCoreApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
|
SingleCoreApplication::SingleCoreApplication(int &argc, char *argv[], bool allowSecondary, Options options, int timeout)
|
||||||
: app_t( argc, argv ), d_ptr( new SingleCoreApplicationPrivate( this ) )
|
: app_t(argc, argv),
|
||||||
{
|
d_ptr(new SingleCoreApplicationPrivate(this)) {
|
||||||
Q_D(SingleCoreApplication);
|
|
||||||
|
|
||||||
// Store the current mode of the program
|
Q_D(SingleCoreApplication);
|
||||||
d->options = options;
|
|
||||||
|
|
||||||
// Generating an application ID used for identifying the shared memory
|
// Store the current mode of the program
|
||||||
// block and QLocalServer
|
d->options_ = options;
|
||||||
d->genBlockServerName();
|
|
||||||
|
// Generating an application ID used for identifying the shared memory block and QLocalServer
|
||||||
|
d->genBlockServerName();
|
||||||
|
|
||||||
|
// To mitigate QSharedMemory issues with large amount of processes attempting to attach at the same time
|
||||||
|
d->randomSleep();
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
// By explicitly attaching it and then deleting it we make sure that the
|
// By explicitly attaching it and then deleting it we make sure that the memory is deleted even after the process has crashed on Unix.
|
||||||
// memory is deleted even after the process has crashed on Unix.
|
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||||
d->memory = new QSharedMemory( d->blockServerName );
|
d->memory_->attach();
|
||||||
d->memory->attach();
|
delete d->memory_;
|
||||||
delete d->memory;
|
|
||||||
#endif
|
#endif
|
||||||
// Guarantee thread safe behaviour with a shared memory block.
|
|
||||||
d->memory = new QSharedMemory( d->blockServerName );
|
|
||||||
|
|
||||||
// Create a shared memory block
|
// Guarantee thread safe behaviour with a shared memory block.
|
||||||
if( d->memory->create( sizeof( InstancesInfo ) ) ) {
|
d->memory_ = new QSharedMemory(d->blockServerName_);
|
||||||
// Initialize the shared memory block
|
|
||||||
d->memory->lock();
|
// Create a shared memory block
|
||||||
d->initializeMemoryBlock();
|
if (d->memory_->create(sizeof(InstancesInfo))) {
|
||||||
d->memory->unlock();
|
// Initialize the shared memory block
|
||||||
} else {
|
if (!d->memory_->lock()) {
|
||||||
// Attempt to attach to the memory segment
|
qCritical() << "SingleCoreApplication: Unable to lock memory block after create.";
|
||||||
if( ! d->memory->attach() ) {
|
abortSafely();
|
||||||
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
|
}
|
||||||
qCritical() << d->memory->errorString();
|
d->initializeMemoryBlock();
|
||||||
delete d;
|
}
|
||||||
::exit( EXIT_FAILURE );
|
else {
|
||||||
}
|
if (d->memory_->error() == QSharedMemory::AlreadyExists) {
|
||||||
|
// Attempt to attach to the memory segment
|
||||||
|
if (!d->memory_->attach()) {
|
||||||
|
qCritical() << "SingleCoreApplication: Unable to attach to shared memory block.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
|
if (!d->memory_->lock()) {
|
||||||
|
qCritical() << "SingleCoreApplication: Unable to lock memory block after attach.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qCritical() << "SingleCoreApplication: Unable to create block.";
|
||||||
|
abortSafely();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(d->memory_->data());
|
||||||
|
QElapsedTimer time;
|
||||||
|
time.start();
|
||||||
|
|
||||||
|
// Make sure the shared memory block is initialised and in consistent state
|
||||||
|
forever {
|
||||||
|
// If the shared memory block's checksum is valid continue
|
||||||
|
if (d->blockChecksum() == inst->checksum) break;
|
||||||
|
|
||||||
|
// If more than 5s have elapsed, assume the primary instance crashed and assume it's position
|
||||||
|
if (time.elapsed() > 5000) {
|
||||||
|
qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||||
|
d->initializeMemoryBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>( d->memory->data() );
|
// Otherwise wait for a random period and try again.
|
||||||
QTime time;
|
// The random sleep here limits the probability of a collision between two racing apps and allows the app to initialise faster
|
||||||
time.start();
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleCoreApplication: Unable to unlock memory for random wait.";
|
||||||
// Make sure the shared memory block is initialised and in consistent state
|
qDebug() << d->memory_->errorString();
|
||||||
while( true ) {
|
|
||||||
d->memory->lock();
|
|
||||||
|
|
||||||
if( d->blockChecksum() == inst->checksum ) break;
|
|
||||||
|
|
||||||
if( time.elapsed() > 5000 ) {
|
|
||||||
qWarning() << "SingleCoreApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
|
||||||
d->initializeMemoryBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
d->memory->unlock();
|
|
||||||
|
|
||||||
// Random sleep here limits the probability of a collision between two racing apps
|
|
||||||
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
|
|
||||||
QThread::sleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ) );
|
|
||||||
}
|
}
|
||||||
|
d->randomSleep();
|
||||||
if( inst->primary == false) {
|
if (!d->memory_->lock()) {
|
||||||
d->startPrimary();
|
qCritical() << "SingleCoreApplication: Unable to lock memory after random wait.";
|
||||||
d->memory->unlock();
|
abortSafely();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if another instance can be started
|
if (inst->primary == false) {
|
||||||
if( allowSecondary ) {
|
d->startPrimary();
|
||||||
inst->secondary += 1;
|
if (!d->memory_->unlock()) {
|
||||||
inst->checksum = d->blockChecksum();
|
qDebug() << "SingleCoreApplication: Unable to unlock memory after primary start.";
|
||||||
d->instanceNumber = inst->secondary;
|
qDebug() << d->memory_->errorString();
|
||||||
d->startSecondary();
|
|
||||||
if( d->options & Mode::SecondaryNotification ) {
|
|
||||||
d->connectToPrimary( timeout, SingleCoreApplicationPrivate::SecondaryInstance );
|
|
||||||
}
|
|
||||||
d->memory->unlock();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
d->memory->unlock();
|
// Check if another instance can be started
|
||||||
|
if (allowSecondary) {
|
||||||
|
d->startSecondary();
|
||||||
|
if (d->options_ & Mode::SecondaryNotification) {
|
||||||
|
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::SecondaryInstance);
|
||||||
|
}
|
||||||
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleCoreApplication: Unable to unlock memory after secondary start.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
d->connectToPrimary( timeout, SingleCoreApplicationPrivate::NewInstance );
|
if (!d->memory_->unlock()) {
|
||||||
|
qDebug() << "SingleCoreApplication: Unable to unlock memory at end of execution.";
|
||||||
|
qDebug() << d->memory_->errorString();
|
||||||
|
}
|
||||||
|
|
||||||
delete d;
|
d->connectToPrimary(timeout, SingleCoreApplicationPrivate::NewInstance);
|
||||||
|
|
||||||
::exit( EXIT_SUCCESS );
|
delete d;
|
||||||
|
|
||||||
|
::exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleCoreApplication::~SingleCoreApplication() {
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destructor
|
* Checks if the current application instance is primary.
|
||||||
|
* @return Returns true if the instance is primary, false otherwise.
|
||||||
*/
|
*/
|
||||||
SingleCoreApplication::~SingleCoreApplication()
|
bool SingleCoreApplication::isPrimary() {
|
||||||
{
|
Q_D(SingleCoreApplication);
|
||||||
Q_D(SingleCoreApplication);
|
return d->server_ != nullptr;
|
||||||
delete d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SingleCoreApplication::isPrimary()
|
/**
|
||||||
{
|
* Checks if the current application instance is secondary.
|
||||||
Q_D(SingleCoreApplication);
|
* @return Returns true if the instance is secondary, false otherwise.
|
||||||
return d->server != nullptr;
|
*/
|
||||||
|
bool SingleCoreApplication::isSecondary() {
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
return d->server_ == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SingleCoreApplication::isSecondary()
|
/**
|
||||||
{
|
* Allows you to identify an instance by returning unique consecutive instance ids.
|
||||||
Q_D(SingleCoreApplication);
|
* It is reset when the first (primary) instance of your app starts and only incremented afterwards.
|
||||||
return d->server == nullptr;
|
* @return Returns a unique instance id.
|
||||||
|
*/
|
||||||
|
quint32 SingleCoreApplication::instanceId() {
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
return d->instanceNumber_;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint32 SingleCoreApplication::instanceId()
|
/**
|
||||||
{
|
* Returns the OS PID (Process Identifier) of the process running the primary instance.
|
||||||
Q_D(SingleCoreApplication);
|
* Especially useful when SingleCoreApplication is coupled with OS. specific APIs.
|
||||||
return d->instanceNumber;
|
* @return Returns the primary instance PID.
|
||||||
|
*/
|
||||||
|
qint64 SingleCoreApplication::primaryPid() {
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
return d->primaryPid();
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 SingleCoreApplication::primaryPid()
|
/**
|
||||||
{
|
* Returns the username the primary instance is running as.
|
||||||
Q_D(SingleCoreApplication);
|
* @return Returns the username the primary instance is running as.
|
||||||
return d->primaryPid();
|
*/
|
||||||
|
QString SingleCoreApplication::primaryUser() {
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
return d->primaryUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SingleCoreApplication::sendMessage( QByteArray message, int timeout )
|
/**
|
||||||
{
|
* Returns the username the current instance is running as.
|
||||||
Q_D(SingleCoreApplication);
|
* @return Returns the username the current instance is running as.
|
||||||
|
*/
|
||||||
// Nobody to connect to
|
QString SingleCoreApplication::currentUser() {
|
||||||
if( isPrimary() ) return false;
|
Q_D(SingleCoreApplication);
|
||||||
|
return d->getUsername();
|
||||||
// Make sure the socket is connected
|
}
|
||||||
d->connectToPrimary( timeout, SingleCoreApplicationPrivate::Reconnect );
|
|
||||||
|
/**
|
||||||
d->socket->write( message );
|
* Sends message to the Primary Instance.
|
||||||
bool dataWritten = d->socket->flush();
|
* @param message The message to send.
|
||||||
d->socket->waitForBytesWritten( timeout );
|
* @param timeout the maximum timeout in milliseconds for blocking functions.
|
||||||
return dataWritten;
|
* @return true if the message was sent successfuly, false otherwise.
|
||||||
|
*/
|
||||||
|
bool SingleCoreApplication::sendMessage(QByteArray message, int timeout) {
|
||||||
|
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
|
||||||
|
// Nobody to connect to
|
||||||
|
if (isPrimary()) return false;
|
||||||
|
|
||||||
|
// Make sure the socket is connected
|
||||||
|
if (!d->connectToPrimary(timeout, SingleCoreApplicationPrivate::Reconnect))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
d->socket_->write(message);
|
||||||
|
const bool dataWritten = d->socket_->waitForBytesWritten(timeout);
|
||||||
|
d->socket_->flush();
|
||||||
|
return dataWritten;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up the shared memory block and exits with a failure.
|
||||||
|
* This function halts program execution.
|
||||||
|
*/
|
||||||
|
void SingleCoreApplication::abortSafely() {
|
||||||
|
|
||||||
|
Q_D(SingleCoreApplication);
|
||||||
|
|
||||||
|
qCritical() << "SingleCoreApplication: " << d->memory_->error() << d->memory_->errorString();
|
||||||
|
delete d;
|
||||||
|
::exit(EXIT_FAILURE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
192
3rdparty/singleapplication/singlecoreapplication.h
vendored
192
3rdparty/singleapplication/singlecoreapplication.h
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -20,111 +20,133 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G !!!
|
||||||
|
// -----------------
|
||||||
|
//
|
||||||
|
// This is a modified version of SingleApplication,
|
||||||
|
// The original version is at:
|
||||||
|
//
|
||||||
|
// https://github.com/itay-grudev/SingleApplication
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
#ifndef SINGLECOREAPPLICATION_H
|
#ifndef SINGLECOREAPPLICATION_H
|
||||||
#define SINGLECOREAPPLICATION_H
|
#define SINGLECOREAPPLICATION_H
|
||||||
|
|
||||||
#include <QtCore/QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QtNetwork/QLocalSocket>
|
#include <QFlags>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
class SingleCoreApplicationPrivate;
|
class SingleCoreApplicationPrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The SingleCoreApplication class handles multipe instances of the same
|
* @brief The SingleCoreApplication class handles multipe instances of the same Application
|
||||||
* Application
|
|
||||||
* @see QCoreApplication
|
* @see QCoreApplication
|
||||||
*/
|
*/
|
||||||
class SingleCoreApplication : public QCoreApplication
|
class SingleCoreApplication : public QCoreApplication {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
typedef QCoreApplication app_t;
|
typedef QCoreApplication app_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Mode of operation of SingleCoreApplication.
|
* @brief Mode of operation of SingleCoreApplication.
|
||||||
* Whether the block should be user-wide or system-wide and whether the
|
* Whether the block should be user-wide or system-wide and whether the
|
||||||
* primary instance should be notified when a secondary instance had been
|
* primary instance should be notified when a secondary instance had been
|
||||||
* started.
|
* started.
|
||||||
* @note Operating system can restrict the shared memory blocks to the same
|
* @note Operating system can restrict the shared memory blocks to the same
|
||||||
* user, in which case the User/System modes will have no effect and the
|
* user, in which case the User/System modes will have no effect and the
|
||||||
* block will be user wide.
|
* block will be user wide.
|
||||||
* @enum
|
* @enum
|
||||||
*/
|
*/
|
||||||
enum Mode {
|
enum Mode {
|
||||||
User = 1 << 0,
|
User = 1 << 0,
|
||||||
System = 1 << 1,
|
System = 1 << 1,
|
||||||
SecondaryNotification = 1 << 2,
|
SecondaryNotification = 1 << 2,
|
||||||
ExcludeAppVersion = 1 << 3,
|
ExcludeAppVersion = 1 << 3,
|
||||||
ExcludeAppPath = 1 << 4
|
ExcludeAppPath = 1 << 4
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Options, Mode)
|
Q_DECLARE_FLAGS(Options, Mode)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Intitializes a SingleCoreApplication instance with argc command line
|
* @brief Intitializes a SingleCoreApplication instance with argc command line
|
||||||
* arguments in argv
|
* arguments in argv
|
||||||
* @arg {int &} argc - Number of arguments in argv
|
* @arg {int &} argc - Number of arguments in argv
|
||||||
* @arg {const char *[]} argv - Supplied command line arguments
|
* @arg {const char *[]} argv - Supplied command line arguments
|
||||||
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
||||||
* if there is already a primary instance.
|
* if there is already a primary instance.
|
||||||
* @arg {Mode} mode - Whether for the SingleCoreApplication block to be applied
|
* @arg {Mode} mode - Whether for the SingleCoreApplication block to be applied
|
||||||
* User wide or System wide.
|
* User wide or System wide.
|
||||||
* @arg {int} timeout - Timeout to wait in miliseconds.
|
* @arg {int} timeout - Timeout to wait in milliseconds.
|
||||||
* @note argc and argv may be changed as Qt removes arguments that it
|
* @note argc and argv may be changed as Qt removes arguments that it
|
||||||
* recognizes
|
* recognizes
|
||||||
* @note Mode::SecondaryNotification only works if set on both the primary
|
* @note Mode::SecondaryNotification only works if set on both the primary
|
||||||
* instance and the secondary instance.
|
* instance and the secondary instance.
|
||||||
* @note The timeout is just a hint for the maximum time of blocking
|
* @note The timeout is just a hint for the maximum time of blocking
|
||||||
* operations. It does not guarantee that the SingleCoreApplication
|
* operations. It does not guarantee that the SingleCoreApplication
|
||||||
* initialisation will be completed in given time, though is a good hint.
|
* initialisation will be completed in given time, though is a good hint.
|
||||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
*/
|
||||||
*/
|
explicit SingleCoreApplication(int &argc, char *argv[], const bool allowSecondary = false, const Options options = Mode::User, const int timeout = 1000);
|
||||||
explicit SingleCoreApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
~SingleCoreApplication() override;
|
||||||
~SingleCoreApplication();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns if the instance is the primary instance
|
* @brief Returns if the instance is the primary instance
|
||||||
* @returns {bool}
|
* @returns {bool}
|
||||||
*/
|
*/
|
||||||
bool isPrimary();
|
bool isPrimary();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns if the instance is a secondary instance
|
* @brief Returns if the instance is a secondary instance
|
||||||
* @returns {bool}
|
* @returns {bool}
|
||||||
*/
|
*/
|
||||||
bool isSecondary();
|
bool isSecondary();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a unique identifier for the current instance
|
* @brief Returns a unique identifier for the current instance
|
||||||
* @returns {qint32}
|
* @returns {qint32}
|
||||||
*/
|
*/
|
||||||
quint32 instanceId();
|
quint32 instanceId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the process ID (PID) of the primary instance
|
* @brief Returns the process ID (PID) of the primary instance
|
||||||
* @returns {qint64}
|
* @returns {qint64}
|
||||||
*/
|
*/
|
||||||
qint64 primaryPid();
|
qint64 primaryPid();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sends a message to the primary instance. Returns true on success.
|
* @brief Returns the username of the user running the primary instance
|
||||||
* @param {int} timeout - Timeout for connecting
|
* @returns {QString}
|
||||||
* @returns {bool}
|
*/
|
||||||
* @note sendMessage() will return false if invoked from the primary
|
QString primaryUser();
|
||||||
* instance.
|
|
||||||
*/
|
|
||||||
bool sendMessage( QByteArray message, int timeout = 100 );
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
/**
|
||||||
void instanceStarted();
|
* @brief Returns the username of the current user
|
||||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
* @returns {QString}
|
||||||
|
*/
|
||||||
|
QString currentUser();
|
||||||
|
|
||||||
private:
|
/**
|
||||||
SingleCoreApplicationPrivate *d_ptr;
|
* @brief Sends a message to the primary instance. Returns true on success.
|
||||||
Q_DECLARE_PRIVATE(SingleCoreApplication)
|
* @param {int} timeout - Timeout for connecting
|
||||||
|
* @returns {bool}
|
||||||
|
* @note sendMessage() will return false if invoked from the primary
|
||||||
|
* instance.
|
||||||
|
*/
|
||||||
|
bool sendMessage(QByteArray message, int timeout = 1000);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void instanceStarted();
|
||||||
|
void receivedMessage(quint32 instanceId, QByteArray message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SingleCoreApplicationPrivate *d_ptr;
|
||||||
|
Q_DECLARE_PRIVATE(SingleCoreApplication)
|
||||||
|
void abortSafely();
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleCoreApplication::Options)
|
||||||
|
|
||||||
#endif // SINGLECOREAPPLICATION_H
|
#endif // SINGLECOREAPPLICATION_H
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2018
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,381 +24,447 @@
|
|||||||
// W A R N I N G !!!
|
// W A R N I N G !!!
|
||||||
// -----------------
|
// -----------------
|
||||||
//
|
//
|
||||||
// This file is not part of the SingleCoreApplication API. It is used purely as an
|
// This is a modified version of SingleApplication,
|
||||||
// implementation detail. This header file may change from version to
|
// The original version is at:
|
||||||
// version without notice, or may even be removed.
|
|
||||||
//
|
//
|
||||||
|
// https://github.com/itay-grudev/SingleApplication
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include <QtCore/QDir>
|
#ifdef Q_OS_UNIX
|
||||||
#include <QtCore/QProcess>
|
# include <unistd.h>
|
||||||
#include <QtCore/QByteArray>
|
# include <sys/types.h>
|
||||||
#include <QtCore/QSemaphore>
|
# include <pwd.h>
|
||||||
#include <QtCore/QDataStream>
|
#endif
|
||||||
#include <QtCore/QStandardPaths>
|
|
||||||
#include <QtCore/QCryptographicHash>
|
#include <QObject>
|
||||||
#include <QtNetwork/QLocalServer>
|
#include <QThread>
|
||||||
#include <QtNetwork/QLocalSocket>
|
#include <QIODevice>
|
||||||
|
#include <QSharedMemory>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QLocalServer>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
# include <QRandomGenerator>
|
||||||
|
#else
|
||||||
|
# include <QDateTime>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "singlecoreapplication.h"
|
#include "singlecoreapplication.h"
|
||||||
#include "singlecoreapplication_p.h"
|
#include "singlecoreapplication_p.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <windows.h>
|
# include <windows.h>
|
||||||
#include <lmcons.h>
|
# include <lmcons.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate( SingleCoreApplication *q_ptr )
|
SingleCoreApplicationPrivate::SingleCoreApplicationPrivate(SingleCoreApplication *ptr)
|
||||||
: q_ptr( q_ptr )
|
: q_ptr(ptr),
|
||||||
{
|
memory_(nullptr),
|
||||||
server = nullptr;
|
socket_(nullptr),
|
||||||
socket = nullptr;
|
server_(nullptr),
|
||||||
memory = nullptr;
|
instanceNumber_(-1) {}
|
||||||
instanceNumber = -1;
|
|
||||||
|
SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate() {
|
||||||
|
|
||||||
|
if (socket_ != nullptr) {
|
||||||
|
socket_->close();
|
||||||
|
delete socket_;
|
||||||
|
socket_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memory_ != nullptr) {
|
||||||
|
memory_->lock();
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
if (server_ != nullptr) {
|
||||||
|
server_->close();
|
||||||
|
delete server_;
|
||||||
|
inst->primary = false;
|
||||||
|
inst->primaryPid = -1;
|
||||||
|
inst->primaryUser[0] = '\0';
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
}
|
||||||
|
memory_->unlock();
|
||||||
|
|
||||||
|
delete memory_;
|
||||||
|
memory_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleCoreApplicationPrivate::~SingleCoreApplicationPrivate()
|
QString SingleCoreApplicationPrivate::getUsername() {
|
||||||
{
|
|
||||||
if( socket != nullptr ) {
|
|
||||||
socket->close();
|
|
||||||
delete socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
memory->lock();
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
|
||||||
if( server != nullptr ) {
|
|
||||||
server->close();
|
|
||||||
delete server;
|
|
||||||
inst->primary = false;
|
|
||||||
inst->primaryPid = -1;
|
|
||||||
inst->checksum = blockChecksum();
|
|
||||||
}
|
|
||||||
memory->unlock();
|
|
||||||
|
|
||||||
delete memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::genBlockServerName()
|
|
||||||
{
|
|
||||||
QCryptographicHash appData( QCryptographicHash::Sha256 );
|
|
||||||
appData.addData( "SingleApplication", 17 );
|
|
||||||
appData.addData( SingleCoreApplication::app_t::applicationName().toUtf8() );
|
|
||||||
appData.addData( SingleCoreApplication::app_t::organizationName().toUtf8() );
|
|
||||||
appData.addData( SingleCoreApplication::app_t::organizationDomain().toUtf8() );
|
|
||||||
|
|
||||||
if( ! (options & SingleCoreApplication::Mode::ExcludeAppVersion) ) {
|
|
||||||
appData.addData( SingleCoreApplication::app_t::applicationVersion().toUtf8() );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ! (options & SingleCoreApplication::Mode::ExcludeAppPath) ) {
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
appData.addData( SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8() );
|
|
||||||
#else
|
|
||||||
appData.addData( SingleCoreApplication::app_t::applicationFilePath().toUtf8() );
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// User level block requires a user specific data in the hash
|
|
||||||
if( options & SingleCoreApplication::Mode::User ) {
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
wchar_t username [ UNLEN + 1 ];
|
|
||||||
// Specifies size of the buffer on input
|
|
||||||
DWORD usernameLength = UNLEN + 1;
|
|
||||||
if( GetUserNameW( username, &usernameLength ) ) {
|
|
||||||
appData.addData( QString::fromWCharArray(username).toUtf8() );
|
|
||||||
} else {
|
|
||||||
appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
QProcess process;
|
QString username;
|
||||||
process.start( "whoami" );
|
#if defined(HAVE_GETEUID) && defined(HAVE_GETPWUID)
|
||||||
if( process.waitForFinished( 100 ) &&
|
struct passwd *pw = getpwuid(geteuid());
|
||||||
process.exitCode() == QProcess::NormalExit) {
|
if (pw) {
|
||||||
appData.addData( process.readLine() );
|
username = QString::fromLocal8Bit(pw->pw_name);
|
||||||
} else {
|
}
|
||||||
appData.addData(
|
|
||||||
QDir(
|
|
||||||
QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).first()
|
|
||||||
).absolutePath().toUtf8()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
if (username.isEmpty()) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
|
username = qEnvironmentVariable("USER");
|
||||||
// server naming requirements.
|
#else
|
||||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
username = QString::fromLocal8Bit(qgetenv("USER"));
|
||||||
}
|
#endif
|
||||||
|
}
|
||||||
void SingleCoreApplicationPrivate::initializeMemoryBlock()
|
return username;
|
||||||
{
|
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
|
||||||
inst->primary = false;
|
|
||||||
inst->secondary = 0;
|
|
||||||
inst->primaryPid = -1;
|
|
||||||
inst->checksum = blockChecksum();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::startPrimary()
|
|
||||||
{
|
|
||||||
Q_Q(SingleCoreApplication);
|
|
||||||
|
|
||||||
// Successful creation means that no main process exists
|
|
||||||
// So we start a QLocalServer to listen for connections
|
|
||||||
QLocalServer::removeServer( blockServerName );
|
|
||||||
server = new QLocalServer();
|
|
||||||
|
|
||||||
// Restrict access to the socket according to the
|
|
||||||
// SingleCoreApplication::Mode::User flag on User level or no restrictions
|
|
||||||
if( options & SingleCoreApplication::Mode::User ) {
|
|
||||||
server->setSocketOptions( QLocalServer::UserAccessOption );
|
|
||||||
} else {
|
|
||||||
server->setSocketOptions( QLocalServer::WorldAccessOption );
|
|
||||||
}
|
|
||||||
|
|
||||||
server->listen( blockServerName );
|
|
||||||
QObject::connect(
|
|
||||||
server,
|
|
||||||
&QLocalServer::newConnection,
|
|
||||||
this,
|
|
||||||
&SingleCoreApplicationPrivate::slotConnectionEstablished
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reset the number of connections
|
|
||||||
InstancesInfo* inst = static_cast <InstancesInfo*>( memory->data() );
|
|
||||||
|
|
||||||
inst->primary = true;
|
|
||||||
inst->primaryPid = q->applicationPid();
|
|
||||||
inst->checksum = blockChecksum();
|
|
||||||
|
|
||||||
instanceNumber = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::startSecondary()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
|
|
||||||
{
|
|
||||||
// Connect to the Local Server of the Primary Instance if not already
|
|
||||||
// connected.
|
|
||||||
if( socket == nullptr ) {
|
|
||||||
socket = new QLocalSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If already connected - we are done;
|
|
||||||
if( socket->state() == QLocalSocket::ConnectedState )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If not connect
|
|
||||||
if( socket->state() == QLocalSocket::UnconnectedState ||
|
|
||||||
socket->state() == QLocalSocket::ClosingState ) {
|
|
||||||
socket->connectToServer( blockServerName );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for being connected
|
|
||||||
if( socket->state() == QLocalSocket::ConnectingState ) {
|
|
||||||
socket->waitForConnected( msecs );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialisation message according to the SingleCoreApplication protocol
|
|
||||||
if( socket->state() == QLocalSocket::ConnectedState ) {
|
|
||||||
// Notify the parent that a new instance had been started;
|
|
||||||
QByteArray initMsg;
|
|
||||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
|
||||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
writeStream << blockServerName.toLatin1();
|
#ifdef Q_OS_WIN
|
||||||
writeStream << static_cast<quint8>(connectionType);
|
wchar_t username[UNLEN + 1];
|
||||||
writeStream << instanceNumber;
|
// Specifies size of the buffer on input
|
||||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
DWORD usernameLength = UNLEN + 1;
|
||||||
writeStream << checksum;
|
if (GetUserNameW(username, &usernameLength)) {
|
||||||
|
return QString::fromWCharArray(username);
|
||||||
// The header indicates the message length that follows
|
}
|
||||||
QByteArray header;
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
return qEnvironmentVariable("USERNAME");
|
||||||
|
#else
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
return QString::fromLocal8Bit(qgetenv("USERNAME"));
|
||||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
#endif
|
||||||
#endif
|
#endif
|
||||||
headerStream << static_cast <quint64>( initMsg.length() );
|
|
||||||
|
|
||||||
socket->write( header );
|
}
|
||||||
socket->write( initMsg );
|
|
||||||
socket->flush();
|
void SingleCoreApplicationPrivate::genBlockServerName() {
|
||||||
socket->waitForBytesWritten( msecs );
|
|
||||||
|
QCryptographicHash appData(QCryptographicHash::Sha256);
|
||||||
|
appData.addData("SingleApplication", 17);
|
||||||
|
appData.addData(SingleCoreApplication::app_t::applicationName().toUtf8());
|
||||||
|
appData.addData(SingleCoreApplication::app_t::organizationName().toUtf8());
|
||||||
|
appData.addData(SingleCoreApplication::app_t::organizationDomain().toUtf8());
|
||||||
|
|
||||||
|
if (!(options_ & SingleCoreApplication::Mode::ExcludeAppVersion)) {
|
||||||
|
appData.addData(SingleCoreApplication::app_t::applicationVersion().toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(options_ & SingleCoreApplication::Mode::ExcludeAppPath)) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toLower().toUtf8());
|
||||||
|
#else
|
||||||
|
appData.addData(SingleCoreApplication::app_t::applicationFilePath().toUtf8());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// User level block requires a user specific data in the hash
|
||||||
|
if (options_ & SingleCoreApplication::Mode::User) {
|
||||||
|
appData.addData(getUsername().toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with server naming requirements.
|
||||||
|
blockServerName_ = appData.result().toBase64().replace("/", "_");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleCoreApplicationPrivate::initializeMemoryBlock() {
|
||||||
|
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
inst->primary = false;
|
||||||
|
inst->secondary = 0;
|
||||||
|
inst->primaryPid = -1;
|
||||||
|
inst->primaryUser[0] = '\0';
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleCoreApplicationPrivate::startPrimary() {
|
||||||
|
|
||||||
|
Q_Q(SingleCoreApplication);
|
||||||
|
|
||||||
|
// Reset the number of connections
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
|
||||||
|
inst->primary = true;
|
||||||
|
inst->primaryPid = q->applicationPid();
|
||||||
|
qstrncpy(inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser));
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
instanceNumber_ = 0;
|
||||||
|
// Successful creation means that no main process exists
|
||||||
|
// So we start a QLocalServer to listen for connections
|
||||||
|
QLocalServer::removeServer(blockServerName_);
|
||||||
|
server_ = new QLocalServer();
|
||||||
|
|
||||||
|
// Restrict access to the socket according to the SingleCoreApplication::Mode::User flag on User level or no restrictions
|
||||||
|
if (options_ & SingleCoreApplication::Mode::User) {
|
||||||
|
server_->setSocketOptions(QLocalServer::UserAccessOption);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
server_->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
server_->listen(blockServerName_);
|
||||||
|
QObject::connect(server_, &QLocalServer::newConnection, this, &SingleCoreApplicationPrivate::slotConnectionEstablished);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleCoreApplicationPrivate::startSecondary() {
|
||||||
|
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
|
||||||
|
inst->secondary += 1;
|
||||||
|
inst->checksum = blockChecksum();
|
||||||
|
instanceNumber_ = inst->secondary;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleCoreApplicationPrivate::connectToPrimary(const int timeout, const ConnectionType connectionType) {
|
||||||
|
|
||||||
|
QElapsedTimer time;
|
||||||
|
time.start();
|
||||||
|
|
||||||
|
// Connect to the Local Server of the Primary Instance if not already connected.
|
||||||
|
if (socket_ == nullptr) {
|
||||||
|
socket_ = new QLocalSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectedState) return true;
|
||||||
|
|
||||||
|
if (socket_->state() != QLocalSocket::ConnectedState) {
|
||||||
|
|
||||||
|
forever {
|
||||||
|
randomSleep();
|
||||||
|
|
||||||
|
if (socket_->state() != QLocalSocket::ConnectingState)
|
||||||
|
socket_->connectToServer(blockServerName_);
|
||||||
|
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectingState) {
|
||||||
|
socket_->waitForConnected(static_cast<int>(timeout - time.elapsed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If connected break out of the loop
|
||||||
|
if (socket_->state() == QLocalSocket::ConnectedState) break;
|
||||||
|
|
||||||
|
// If elapsed time since start is longer than the method timeout return
|
||||||
|
if (time.elapsed() >= timeout) return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisation message according to the SingleCoreApplication protocol
|
||||||
|
QByteArray initMsg;
|
||||||
|
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||||
|
writeStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
|
||||||
|
writeStream << blockServerName_.toLatin1();
|
||||||
|
writeStream << static_cast<quint8>(connectionType);
|
||||||
|
writeStream << instanceNumber_;
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
|
||||||
|
#else
|
||||||
|
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
writeStream << checksum;
|
||||||
|
|
||||||
|
// The header indicates the message length that follows
|
||||||
|
QByteArray header;
|
||||||
|
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||||
|
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
headerStream << static_cast<quint64>(initMsg.length());
|
||||||
|
|
||||||
|
socket_->write(header);
|
||||||
|
socket_->write(initMsg);
|
||||||
|
bool result = socket_->waitForBytesWritten(timeout - static_cast<int>(time.elapsed()));
|
||||||
|
socket_->flush();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quint16 SingleCoreApplicationPrivate::blockChecksum()
|
quint16 SingleCoreApplicationPrivate::blockChecksum() {
|
||||||
{
|
|
||||||
return qChecksum(
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
static_cast <const char *>( memory->data() ),
|
quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum)));
|
||||||
offsetof( InstancesInfo, checksum )
|
#else
|
||||||
);
|
quint16 checksum = qChecksum(static_cast<const char*>(memory_->constData()), offsetof(InstancesInfo, checksum));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return checksum;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 SingleCoreApplicationPrivate::primaryPid()
|
qint64 SingleCoreApplicationPrivate::primaryPid() {
|
||||||
{
|
|
||||||
qint64 pid;
|
|
||||||
|
|
||||||
memory->lock();
|
memory_->lock();
|
||||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
pid = inst->primaryPid;
|
qint64 pid = inst->primaryPid;
|
||||||
memory->unlock();
|
memory_->unlock();
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SingleCoreApplicationPrivate::primaryUser() {
|
||||||
|
|
||||||
|
memory_->lock();
|
||||||
|
InstancesInfo *inst = static_cast<InstancesInfo*>(memory_->data());
|
||||||
|
QByteArray username = inst->primaryUser;
|
||||||
|
memory_->unlock();
|
||||||
|
|
||||||
|
return QString::fromUtf8(username);
|
||||||
|
|
||||||
return pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Executed when a connection has been made to the LocalServer
|
* @brief Executed when a connection has been made to the LocalServer
|
||||||
*/
|
*/
|
||||||
void SingleCoreApplicationPrivate::slotConnectionEstablished()
|
void SingleCoreApplicationPrivate::slotConnectionEstablished() {
|
||||||
{
|
|
||||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
|
||||||
connectionMap.insert(nextConnSocket, ConnectionInfo());
|
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
|
QLocalSocket *nextConnSocket = server_->nextPendingConnection();
|
||||||
[nextConnSocket, this]() {
|
connectionMap_.insert(nextConnSocket, ConnectionInfo());
|
||||||
auto &info = connectionMap[nextConnSocket];
|
|
||||||
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
|
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, this]() {
|
||||||
[nextConnSocket, this](){
|
auto &info = connectionMap_[nextConnSocket];
|
||||||
connectionMap.remove(nextConnSocket);
|
slotClientConnectionClosed(nextConnSocket, info.instanceId);
|
||||||
nextConnSocket->deleteLater();
|
});
|
||||||
}
|
|
||||||
);
|
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, this, [nextConnSocket, this]() {
|
||||||
|
connectionMap_.remove(nextConnSocket);
|
||||||
|
nextConnSocket->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, this]() {
|
||||||
|
auto &info = connectionMap_[nextConnSocket];
|
||||||
|
switch (info.stage) {
|
||||||
|
case StageHeader:
|
||||||
|
readInitMessageHeader(nextConnSocket);
|
||||||
|
break;
|
||||||
|
case StageBody:
|
||||||
|
readInitMessageBody(nextConnSocket);
|
||||||
|
break;
|
||||||
|
case StageConnected:
|
||||||
|
slotDataAvailable(nextConnSocket, info.instanceId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
|
|
||||||
[nextConnSocket, this]() {
|
|
||||||
auto &info = connectionMap[nextConnSocket];
|
|
||||||
switch(info.stage) {
|
|
||||||
case StageHeader:
|
|
||||||
readInitMessageHeader(nextConnSocket);
|
|
||||||
break;
|
|
||||||
case StageBody:
|
|
||||||
readInitMessageBody(nextConnSocket);
|
|
||||||
break;
|
|
||||||
case StageConnected:
|
|
||||||
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
|
void SingleCoreApplicationPrivate::readInitMessageHeader(QLocalSocket *sock) {
|
||||||
{
|
|
||||||
if (!connectionMap.contains( sock )) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) {
|
if (!connectionMap_.contains(sock)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDataStream headerStream( sock );
|
if (sock->bytesAvailable() < static_cast<qint64>(sizeof(quint64))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
QDataStream headerStream(sock);
|
||||||
headerStream.setVersion( QDataStream::Qt_5_6 );
|
headerStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
|
||||||
|
// Read the header to know the message length
|
||||||
|
quint64 msgLen = 0;
|
||||||
|
headerStream >> msgLen;
|
||||||
|
ConnectionInfo &info = connectionMap_[sock];
|
||||||
|
info.stage = StageBody;
|
||||||
|
info.msgLen = msgLen;
|
||||||
|
|
||||||
|
if (sock->bytesAvailable() >= static_cast<qint64>(msgLen)) {
|
||||||
|
readInitMessageBody(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleCoreApplicationPrivate::readInitMessageBody(QLocalSocket *sock) {
|
||||||
|
|
||||||
|
Q_Q(SingleCoreApplication);
|
||||||
|
|
||||||
|
if (!connectionMap_.contains(sock)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionInfo &info = connectionMap_[sock];
|
||||||
|
if (sock->bytesAvailable() < static_cast<qint64>(info.msgLen)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the message body
|
||||||
|
QByteArray msgBytes = sock->read(info.msgLen);
|
||||||
|
QDataStream readStream(msgBytes);
|
||||||
|
readStream.setVersion(QDataStream::Qt_5_8);
|
||||||
|
|
||||||
|
// server name
|
||||||
|
QByteArray latin1Name;
|
||||||
|
readStream >> latin1Name;
|
||||||
|
|
||||||
|
// connection type
|
||||||
|
ConnectionType connectionType = InvalidConnection;
|
||||||
|
quint8 connTypeVal = InvalidConnection;
|
||||||
|
readStream >> connTypeVal;
|
||||||
|
connectionType = static_cast<ConnectionType>(connTypeVal);
|
||||||
|
|
||||||
|
// instance id
|
||||||
|
quint32 instanceId = 0;
|
||||||
|
readStream >> instanceId;
|
||||||
|
|
||||||
|
// checksum
|
||||||
|
quint16 msgChecksum = 0;
|
||||||
|
readStream >> msgChecksum;
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
|
||||||
|
#else
|
||||||
|
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Read the header to know the message length
|
bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName_ && msgChecksum == actualChecksum;
|
||||||
quint64 msgLen = 0;
|
|
||||||
headerStream >> msgLen;
|
if (!isValid) {
|
||||||
ConnectionInfo &info = connectionMap[sock];
|
sock->close();
|
||||||
info.stage = StageBody;
|
return;
|
||||||
info.msgLen = msgLen;
|
}
|
||||||
|
|
||||||
|
info.instanceId = instanceId;
|
||||||
|
info.stage = StageConnected;
|
||||||
|
|
||||||
|
if (connectionType == NewInstance || (connectionType == SecondaryInstance && options_ & SingleCoreApplication::Mode::SecondaryNotification)) {
|
||||||
|
Q_EMIT q->instanceStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sock->bytesAvailable() > 0) {
|
||||||
|
slotDataAvailable(sock, instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
if ( sock->bytesAvailable() >= (qint64) msgLen ) {
|
|
||||||
readInitMessageBody( sock );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
|
void SingleCoreApplicationPrivate::slotDataAvailable(QLocalSocket *dataSocket, const quint32 instanceId) {
|
||||||
{
|
|
||||||
Q_Q(SingleCoreApplication);
|
|
||||||
|
|
||||||
if (!connectionMap.contains( sock )) {
|
Q_Q(SingleCoreApplication);
|
||||||
return;
|
Q_EMIT q->receivedMessage(instanceId, dataSocket->readAll());
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionInfo &info = connectionMap[sock];
|
}
|
||||||
if( sock->bytesAvailable() < ( qint64 )info.msgLen ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the message body
|
void SingleCoreApplicationPrivate::slotClientConnectionClosed(QLocalSocket *closedSocket, const quint32 instanceId) {
|
||||||
QByteArray msgBytes = sock->read(info.msgLen);
|
|
||||||
QDataStream readStream(msgBytes);
|
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
if (closedSocket->bytesAvailable() > 0) {
|
||||||
readStream.setVersion( QDataStream::Qt_5_6 );
|
slotDataAvailable(closedSocket, instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleCoreApplicationPrivate::randomSleep() {
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
QThread::msleep(QRandomGenerator::global()->bounded(8u, 18u));
|
||||||
|
#else
|
||||||
|
qsrand(QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max());
|
||||||
|
QThread::msleep(8 + static_cast<unsigned long>(static_cast<float>(qrand()) / RAND_MAX * 10));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// server name
|
|
||||||
QByteArray latin1Name;
|
|
||||||
readStream >> latin1Name;
|
|
||||||
|
|
||||||
// connection type
|
|
||||||
ConnectionType connectionType = InvalidConnection;
|
|
||||||
quint8 connTypeVal = InvalidConnection;
|
|
||||||
readStream >> connTypeVal;
|
|
||||||
connectionType = static_cast <ConnectionType>( connTypeVal );
|
|
||||||
|
|
||||||
// instance id
|
|
||||||
quint32 instanceId = 0;
|
|
||||||
readStream >> instanceId;
|
|
||||||
|
|
||||||
// checksum
|
|
||||||
quint16 msgChecksum = 0;
|
|
||||||
readStream >> msgChecksum;
|
|
||||||
|
|
||||||
const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast<quint32>( msgBytes.length() - sizeof( quint16 ) ) );
|
|
||||||
|
|
||||||
bool isValid = readStream.status() == QDataStream::Ok &&
|
|
||||||
QLatin1String(latin1Name) == blockServerName &&
|
|
||||||
msgChecksum == actualChecksum;
|
|
||||||
|
|
||||||
if( !isValid ) {
|
|
||||||
sock->close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.instanceId = instanceId;
|
|
||||||
info.stage = StageConnected;
|
|
||||||
|
|
||||||
if( connectionType == NewInstance ||
|
|
||||||
( connectionType == SecondaryInstance &&
|
|
||||||
options & SingleCoreApplication::Mode::SecondaryNotification ) )
|
|
||||||
{
|
|
||||||
Q_EMIT q->instanceStarted();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sock->bytesAvailable() > 0) {
|
|
||||||
Q_EMIT this->slotDataAvailable( sock, instanceId );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
|
|
||||||
{
|
|
||||||
Q_Q(SingleCoreApplication);
|
|
||||||
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleCoreApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
|
|
||||||
{
|
|
||||||
if( closedSocket->bytesAvailable() > 0 )
|
|
||||||
Q_EMIT slotDataAvailable( closedSocket, instanceId );
|
|
||||||
}
|
}
|
||||||
|
|||||||
120
3rdparty/singleapplication/singlecoreapplication_p.h
vendored
120
3rdparty/singleapplication/singlecoreapplication_p.h
vendored
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) Itay Grudev 2015 - 2016
|
// Copyright (c) Itay Grudev 2015 - 2020
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,76 +24,88 @@
|
|||||||
// W A R N I N G !!!
|
// W A R N I N G !!!
|
||||||
// -----------------
|
// -----------------
|
||||||
//
|
//
|
||||||
// This file is not part of the SingleCoreApplication API. It is used purely as an
|
// This is a modified version of SingleApplication,
|
||||||
// implementation detail. This header file may change from version to
|
// The original version is at:
|
||||||
// version without notice, or may even be removed.
|
//
|
||||||
|
// https://github.com/itay-grudev/SingleApplication
|
||||||
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef SINGLECOREAPPLICATION_P_H
|
#ifndef SINGLECOREAPPLICATION_P_H
|
||||||
#define SINGLECOREAPPLICATION_P_H
|
#define SINGLECOREAPPLICATION_P_H
|
||||||
|
|
||||||
#include <QtCore/QSharedMemory>
|
#include <QtGlobal>
|
||||||
#include <QtNetwork/QLocalServer>
|
#include <QObject>
|
||||||
#include <QtNetwork/QLocalSocket>
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
#include "singlecoreapplication.h"
|
#include "singlecoreapplication.h"
|
||||||
|
|
||||||
|
class QLocalServer;
|
||||||
|
class QLocalSocket;
|
||||||
|
class QSharedMemory;
|
||||||
|
|
||||||
struct InstancesInfo {
|
struct InstancesInfo {
|
||||||
bool primary;
|
bool primary;
|
||||||
quint32 secondary;
|
quint32 secondary;
|
||||||
qint64 primaryPid;
|
qint64 primaryPid;
|
||||||
quint16 checksum;
|
char primaryUser[128];
|
||||||
|
quint16 checksum;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConnectionInfo {
|
struct ConnectionInfo {
|
||||||
explicit ConnectionInfo() :
|
explicit ConnectionInfo() : msgLen(0), instanceId(0), stage(0) {}
|
||||||
msgLen(0), instanceId(0), stage(0) {}
|
qint64 msgLen;
|
||||||
qint64 msgLen;
|
quint32 instanceId;
|
||||||
quint32 instanceId;
|
quint8 stage;
|
||||||
quint8 stage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SingleCoreApplicationPrivate : public QObject {
|
class SingleCoreApplicationPrivate : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
|
||||||
enum ConnectionType : quint8 {
|
|
||||||
InvalidConnection = 0,
|
|
||||||
NewInstance = 1,
|
|
||||||
SecondaryInstance = 2,
|
|
||||||
Reconnect = 3
|
|
||||||
};
|
|
||||||
enum ConnectionStage : quint8 {
|
|
||||||
StageHeader = 0,
|
|
||||||
StageBody = 1,
|
|
||||||
StageConnected = 2,
|
|
||||||
};
|
|
||||||
Q_DECLARE_PUBLIC(SingleCoreApplication)
|
|
||||||
|
|
||||||
SingleCoreApplicationPrivate( SingleCoreApplication *q_ptr );
|
public:
|
||||||
~SingleCoreApplicationPrivate();
|
enum ConnectionType : quint8 {
|
||||||
|
InvalidConnection = 0,
|
||||||
|
NewInstance = 1,
|
||||||
|
SecondaryInstance = 2,
|
||||||
|
Reconnect = 3
|
||||||
|
};
|
||||||
|
enum ConnectionStage : quint8 {
|
||||||
|
StageHeader = 0,
|
||||||
|
StageBody = 1,
|
||||||
|
StageConnected = 2,
|
||||||
|
};
|
||||||
|
Q_DECLARE_PUBLIC(SingleCoreApplication)
|
||||||
|
|
||||||
void genBlockServerName();
|
explicit SingleCoreApplicationPrivate(SingleCoreApplication *ptr);
|
||||||
void initializeMemoryBlock();
|
~SingleCoreApplicationPrivate() override;
|
||||||
void startPrimary();
|
|
||||||
void startSecondary();
|
|
||||||
void connectToPrimary(int msecs, ConnectionType connectionType );
|
|
||||||
quint16 blockChecksum();
|
|
||||||
qint64 primaryPid();
|
|
||||||
void readInitMessageHeader(QLocalSocket *socket);
|
|
||||||
void readInitMessageBody(QLocalSocket *socket);
|
|
||||||
|
|
||||||
SingleCoreApplication *q_ptr;
|
QString getUsername();
|
||||||
QSharedMemory *memory;
|
void genBlockServerName();
|
||||||
QLocalSocket *socket;
|
void initializeMemoryBlock();
|
||||||
QLocalServer *server;
|
void startPrimary();
|
||||||
quint32 instanceNumber;
|
void startSecondary();
|
||||||
QString blockServerName;
|
bool connectToPrimary(const int timeout, const ConnectionType connectionType);
|
||||||
SingleCoreApplication::Options options;
|
quint16 blockChecksum();
|
||||||
QMap<QLocalSocket*, ConnectionInfo> connectionMap;
|
qint64 primaryPid();
|
||||||
|
QString primaryUser();
|
||||||
|
void readInitMessageHeader(QLocalSocket *socket);
|
||||||
|
void readInitMessageBody(QLocalSocket *socket);
|
||||||
|
void randomSleep();
|
||||||
|
|
||||||
public Q_SLOTS:
|
SingleCoreApplication *q_ptr;
|
||||||
void slotConnectionEstablished();
|
QSharedMemory *memory_;
|
||||||
void slotDataAvailable( QLocalSocket*, quint32 );
|
QLocalSocket *socket_;
|
||||||
void slotClientConnectionClosed( QLocalSocket*, quint32 );
|
QLocalServer *server_;
|
||||||
|
quint32 instanceNumber_;
|
||||||
|
QString blockServerName_;
|
||||||
|
SingleCoreApplication::Options options_;
|
||||||
|
QMap<QLocalSocket*, ConnectionInfo> connectionMap_;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotConnectionEstablished();
|
||||||
|
void slotDataAvailable(QLocalSocket*, const quint32);
|
||||||
|
void slotClientConnectionClosed(QLocalSocket*, const quint32);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SINGLECOREAPPLICATION_P_H
|
#endif // SINGLECOREAPPLICATION_P_H
|
||||||
|
|||||||
398
3rdparty/taglib/CMakeLists.txt
vendored
398
3rdparty/taglib/CMakeLists.txt
vendored
@@ -1,398 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.11)
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-delete-non-virtual-dtor")
|
|
||||||
|
|
||||||
set(TAGLIB_SOVERSION_CURRENT 17)
|
|
||||||
set(TAGLIB_SOVERSION_REVISION 0)
|
|
||||||
set(TAGLIB_SOVERSION_AGE 16)
|
|
||||||
|
|
||||||
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
|
|
||||||
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
|
|
||||||
math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
|
|
||||||
|
|
||||||
include(TestBigEndian)
|
|
||||||
test_big_endian(IS_BIG_ENDIAN)
|
|
||||||
|
|
||||||
if(NOT IS_BIG_ENDIAN)
|
|
||||||
add_definitions(-DSYSTEM_BYTEORDER=1)
|
|
||||||
else()
|
|
||||||
add_definitions(-DSYSTEM_BYTEORDER=2)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
configure_file(taglib_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h)
|
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
|
||||||
include_directories(
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/toolkit
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/asf
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/flac
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mpc
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mp4
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ape
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/riff
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/mod
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
|
||||||
${CMAKE_SOURCE_DIR}/3rdparty
|
|
||||||
)
|
|
||||||
|
|
||||||
if(ZLIB_FOUND)
|
|
||||||
include_directories(${ZLIB_INCLUDE_DIR})
|
|
||||||
elseif(HAVE_ZLIB_SOURCE)
|
|
||||||
include_directories(${ZLIB_SOURCE})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(tag_HDRS
|
|
||||||
tag.h
|
|
||||||
fileref.h
|
|
||||||
audioproperties.h
|
|
||||||
taglib_export.h
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h
|
|
||||||
toolkit/taglib.h
|
|
||||||
toolkit/tstring.h
|
|
||||||
toolkit/tlist.h
|
|
||||||
toolkit/tlist.tcc
|
|
||||||
toolkit/tstringlist.h
|
|
||||||
toolkit/tbytevector.h
|
|
||||||
toolkit/tbytevectorlist.h
|
|
||||||
toolkit/tbytevectorstream.h
|
|
||||||
toolkit/tiostream.h
|
|
||||||
toolkit/tfile.h
|
|
||||||
toolkit/tfilestream.h
|
|
||||||
toolkit/tmap.h
|
|
||||||
toolkit/tmap.tcc
|
|
||||||
toolkit/tpropertymap.h
|
|
||||||
toolkit/trefcounter.h
|
|
||||||
toolkit/tdebuglistener.h
|
|
||||||
mpeg/mpegfile.h
|
|
||||||
mpeg/mpegproperties.h
|
|
||||||
mpeg/mpegheader.h
|
|
||||||
mpeg/xingheader.h
|
|
||||||
mpeg/id3v1/id3v1tag.h
|
|
||||||
mpeg/id3v1/id3v1genres.h
|
|
||||||
mpeg/id3v2/id3v2extendedheader.h
|
|
||||||
mpeg/id3v2/id3v2frame.h
|
|
||||||
mpeg/id3v2/id3v2header.h
|
|
||||||
mpeg/id3v2/id3v2synchdata.h
|
|
||||||
mpeg/id3v2/id3v2footer.h
|
|
||||||
mpeg/id3v2/id3v2framefactory.h
|
|
||||||
mpeg/id3v2/id3v2tag.h
|
|
||||||
mpeg/id3v2/frames/attachedpictureframe.h
|
|
||||||
mpeg/id3v2/frames/commentsframe.h
|
|
||||||
mpeg/id3v2/frames/eventtimingcodesframe.h
|
|
||||||
mpeg/id3v2/frames/generalencapsulatedobjectframe.h
|
|
||||||
mpeg/id3v2/frames/ownershipframe.h
|
|
||||||
mpeg/id3v2/frames/popularimeterframe.h
|
|
||||||
mpeg/id3v2/frames/privateframe.h
|
|
||||||
mpeg/id3v2/frames/relativevolumeframe.h
|
|
||||||
mpeg/id3v2/frames/synchronizedlyricsframe.h
|
|
||||||
mpeg/id3v2/frames/textidentificationframe.h
|
|
||||||
mpeg/id3v2/frames/uniquefileidentifierframe.h
|
|
||||||
mpeg/id3v2/frames/unknownframe.h
|
|
||||||
mpeg/id3v2/frames/unsynchronizedlyricsframe.h
|
|
||||||
mpeg/id3v2/frames/urllinkframe.h
|
|
||||||
mpeg/id3v2/frames/chapterframe.h
|
|
||||||
mpeg/id3v2/frames/tableofcontentsframe.h
|
|
||||||
mpeg/id3v2/frames/podcastframe.h
|
|
||||||
ogg/oggfile.h
|
|
||||||
ogg/oggpage.h
|
|
||||||
ogg/oggpageheader.h
|
|
||||||
ogg/xiphcomment.h
|
|
||||||
ogg/vorbis/vorbisfile.h
|
|
||||||
ogg/vorbis/vorbisproperties.h
|
|
||||||
ogg/flac/oggflacfile.h
|
|
||||||
ogg/speex/speexfile.h
|
|
||||||
ogg/speex/speexproperties.h
|
|
||||||
ogg/opus/opusfile.h
|
|
||||||
ogg/opus/opusproperties.h
|
|
||||||
flac/flacfile.h
|
|
||||||
flac/flacpicture.h
|
|
||||||
flac/flacproperties.h
|
|
||||||
flac/flacmetadatablock.h
|
|
||||||
ape/apefile.h
|
|
||||||
ape/apeproperties.h
|
|
||||||
ape/apetag.h
|
|
||||||
ape/apefooter.h
|
|
||||||
ape/apeitem.h
|
|
||||||
mpc/mpcfile.h
|
|
||||||
mpc/mpcproperties.h
|
|
||||||
wavpack/wavpackfile.h
|
|
||||||
wavpack/wavpackproperties.h
|
|
||||||
trueaudio/trueaudiofile.h
|
|
||||||
trueaudio/trueaudioproperties.h
|
|
||||||
riff/rifffile.h
|
|
||||||
riff/aiff/aifffile.h
|
|
||||||
riff/aiff/aiffproperties.h
|
|
||||||
riff/wav/wavfile.h
|
|
||||||
riff/wav/wavproperties.h
|
|
||||||
riff/wav/infotag.h
|
|
||||||
asf/asffile.h
|
|
||||||
asf/asfproperties.h
|
|
||||||
asf/asftag.h
|
|
||||||
asf/asfattribute.h
|
|
||||||
asf/asfpicture.h
|
|
||||||
mp4/mp4file.h
|
|
||||||
mp4/mp4atom.h
|
|
||||||
mp4/mp4tag.h
|
|
||||||
mp4/mp4item.h
|
|
||||||
mp4/mp4properties.h
|
|
||||||
mp4/mp4coverart.h
|
|
||||||
mod/modfilebase.h
|
|
||||||
mod/modfile.h
|
|
||||||
mod/modtag.h
|
|
||||||
mod/modproperties.h
|
|
||||||
it/itfile.h
|
|
||||||
it/itproperties.h
|
|
||||||
s3m/s3mfile.h
|
|
||||||
s3m/s3mproperties.h
|
|
||||||
xm/xmfile.h
|
|
||||||
xm/xmproperties.h
|
|
||||||
dsf/dsffile.h
|
|
||||||
dsf/dsfproperties.h
|
|
||||||
dsdiff/dsdifffile.h
|
|
||||||
dsdiff/dsdiffproperties.h
|
|
||||||
dsdiff/dsdiffdiintag.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(mpeg_SRCS
|
|
||||||
mpeg/mpegfile.cpp
|
|
||||||
mpeg/mpegproperties.cpp
|
|
||||||
mpeg/mpegheader.cpp
|
|
||||||
mpeg/xingheader.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(id3v1_SRCS
|
|
||||||
mpeg/id3v1/id3v1tag.cpp
|
|
||||||
mpeg/id3v1/id3v1genres.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(id3v2_SRCS
|
|
||||||
mpeg/id3v2/id3v2framefactory.cpp
|
|
||||||
mpeg/id3v2/id3v2synchdata.cpp
|
|
||||||
mpeg/id3v2/id3v2tag.cpp
|
|
||||||
mpeg/id3v2/id3v2header.cpp
|
|
||||||
mpeg/id3v2/id3v2frame.cpp
|
|
||||||
mpeg/id3v2/id3v2footer.cpp
|
|
||||||
mpeg/id3v2/id3v2extendedheader.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(frames_SRCS
|
|
||||||
mpeg/id3v2/frames/attachedpictureframe.cpp
|
|
||||||
mpeg/id3v2/frames/commentsframe.cpp
|
|
||||||
mpeg/id3v2/frames/eventtimingcodesframe.cpp
|
|
||||||
mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp
|
|
||||||
mpeg/id3v2/frames/ownershipframe.cpp
|
|
||||||
mpeg/id3v2/frames/popularimeterframe.cpp
|
|
||||||
mpeg/id3v2/frames/privateframe.cpp
|
|
||||||
mpeg/id3v2/frames/relativevolumeframe.cpp
|
|
||||||
mpeg/id3v2/frames/synchronizedlyricsframe.cpp
|
|
||||||
mpeg/id3v2/frames/textidentificationframe.cpp
|
|
||||||
mpeg/id3v2/frames/uniquefileidentifierframe.cpp
|
|
||||||
mpeg/id3v2/frames/unknownframe.cpp
|
|
||||||
mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
|
|
||||||
mpeg/id3v2/frames/urllinkframe.cpp
|
|
||||||
mpeg/id3v2/frames/chapterframe.cpp
|
|
||||||
mpeg/id3v2/frames/tableofcontentsframe.cpp
|
|
||||||
mpeg/id3v2/frames/podcastframe.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(ogg_SRCS
|
|
||||||
ogg/oggfile.cpp
|
|
||||||
ogg/oggpage.cpp
|
|
||||||
ogg/oggpageheader.cpp
|
|
||||||
ogg/xiphcomment.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(vorbis_SRCS
|
|
||||||
ogg/vorbis/vorbisfile.cpp
|
|
||||||
ogg/vorbis/vorbisproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(flacs_SRCS
|
|
||||||
flac/flacfile.cpp
|
|
||||||
flac/flacpicture.cpp
|
|
||||||
flac/flacproperties.cpp
|
|
||||||
flac/flacmetadatablock.cpp
|
|
||||||
flac/flacunknownmetadatablock.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(oggflacs_SRCS
|
|
||||||
ogg/flac/oggflacfile.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(mpc_SRCS
|
|
||||||
mpc/mpcfile.cpp
|
|
||||||
mpc/mpcproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(mp4_SRCS
|
|
||||||
mp4/mp4file.cpp
|
|
||||||
mp4/mp4atom.cpp
|
|
||||||
mp4/mp4tag.cpp
|
|
||||||
mp4/mp4item.cpp
|
|
||||||
mp4/mp4properties.cpp
|
|
||||||
mp4/mp4coverart.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(ape_SRCS
|
|
||||||
ape/apetag.cpp
|
|
||||||
ape/apefooter.cpp
|
|
||||||
ape/apeitem.cpp
|
|
||||||
ape/apefile.cpp
|
|
||||||
ape/apeproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(wavpack_SRCS
|
|
||||||
wavpack/wavpackfile.cpp
|
|
||||||
wavpack/wavpackproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(speex_SRCS
|
|
||||||
ogg/speex/speexfile.cpp
|
|
||||||
ogg/speex/speexproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(opus_SRCS
|
|
||||||
ogg/opus/opusfile.cpp
|
|
||||||
ogg/opus/opusproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(trueaudio_SRCS
|
|
||||||
trueaudio/trueaudiofile.cpp
|
|
||||||
trueaudio/trueaudioproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(asf_SRCS
|
|
||||||
asf/asftag.cpp
|
|
||||||
asf/asffile.cpp
|
|
||||||
asf/asfproperties.cpp
|
|
||||||
asf/asfattribute.cpp
|
|
||||||
asf/asfpicture.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(riff_SRCS
|
|
||||||
riff/rifffile.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(aiff_SRCS
|
|
||||||
riff/aiff/aifffile.cpp
|
|
||||||
riff/aiff/aiffproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(wav_SRCS
|
|
||||||
riff/wav/wavfile.cpp
|
|
||||||
riff/wav/wavproperties.cpp
|
|
||||||
riff/wav/infotag.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(mod_SRCS
|
|
||||||
mod/modfilebase.cpp
|
|
||||||
mod/modfile.cpp
|
|
||||||
mod/modtag.cpp
|
|
||||||
mod/modproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(s3m_SRCS
|
|
||||||
s3m/s3mfile.cpp
|
|
||||||
s3m/s3mproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(it_SRCS
|
|
||||||
it/itfile.cpp
|
|
||||||
it/itproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(xm_SRCS
|
|
||||||
xm/xmfile.cpp
|
|
||||||
xm/xmproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(dsf_SRCS
|
|
||||||
dsf/dsffile.cpp
|
|
||||||
dsf/dsfproperties.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(dsdiff_SRCS
|
|
||||||
dsdiff/dsdifffile.cpp
|
|
||||||
dsdiff/dsdiffproperties.cpp
|
|
||||||
dsdiff/dsdiffdiintag.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(toolkit_SRCS
|
|
||||||
toolkit/tstring.cpp
|
|
||||||
toolkit/tstringlist.cpp
|
|
||||||
toolkit/tbytevector.cpp
|
|
||||||
toolkit/tbytevectorlist.cpp
|
|
||||||
toolkit/tbytevectorstream.cpp
|
|
||||||
toolkit/tiostream.cpp
|
|
||||||
toolkit/tfile.cpp
|
|
||||||
toolkit/tfilestream.cpp
|
|
||||||
toolkit/tdebug.cpp
|
|
||||||
toolkit/tpropertymap.cpp
|
|
||||||
toolkit/trefcounter.cpp
|
|
||||||
toolkit/tdebuglistener.cpp
|
|
||||||
toolkit/tzlib.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
if(HAVE_ZLIB_SOURCE)
|
|
||||||
set(zlib_SRCS
|
|
||||||
${ZLIB_SOURCE}/adler32.c
|
|
||||||
${ZLIB_SOURCE}/crc32.c
|
|
||||||
${ZLIB_SOURCE}/inffast.c
|
|
||||||
${ZLIB_SOURCE}/inflate.c
|
|
||||||
${ZLIB_SOURCE}/inftrees.c
|
|
||||||
${ZLIB_SOURCE}/zutil.c
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(tag_LIB_SRCS
|
|
||||||
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
|
|
||||||
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
|
|
||||||
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
|
|
||||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} ${dsf_SRCS} ${dsdiff_SRCS}
|
|
||||||
${zlib_SRCS}
|
|
||||||
tag.cpp
|
|
||||||
tagunion.cpp
|
|
||||||
fileref.cpp
|
|
||||||
audioproperties.cpp
|
|
||||||
tagutils.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(tag STATIC ${tag_LIB_SRCS} ${tag_HDRS})
|
|
||||||
|
|
||||||
if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
|
|
||||||
target_link_libraries(tag ${ZLIB_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set_target_properties(tag PROPERTIES
|
|
||||||
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
|
|
||||||
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
|
|
||||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
|
||||||
LINK_INTERFACE_LIBRARIES ""
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach(header ${tag_HDRS})
|
|
||||||
get_filename_component(header_name ${header} NAME)
|
|
||||||
configure_file(
|
|
||||||
"${header}"
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/headers/taglib/${header_name}"
|
|
||||||
COPYONLY
|
|
||||||
)
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
502
3rdparty/taglib/COPYING
vendored
502
3rdparty/taglib/COPYING
vendored
@@ -1,502 +0,0 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 2.1, February 1999
|
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
[This is the first released version of the Lesser GPL. It also counts
|
|
||||||
as the successor of the GNU Library Public License, version 2, hence
|
|
||||||
the version number 2.1.]
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
Licenses are intended to guarantee your freedom to share and change
|
|
||||||
free software--to make sure the software is free for all its users.
|
|
||||||
|
|
||||||
This license, the Lesser General Public License, applies to some
|
|
||||||
specially designated software packages--typically libraries--of the
|
|
||||||
Free Software Foundation and other authors who decide to use it. You
|
|
||||||
can use it too, but we suggest you first think carefully about whether
|
|
||||||
this license or the ordinary General Public License is the better
|
|
||||||
strategy to use in any particular case, based on the explanations below.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom of use,
|
|
||||||
not price. Our General Public Licenses are designed to make sure that
|
|
||||||
you have the freedom to distribute copies of free software (and charge
|
|
||||||
for this service if you wish); that you receive source code or can get
|
|
||||||
it if you want it; that you can change the software and use pieces of
|
|
||||||
it in new free programs; and that you are informed that you can do
|
|
||||||
these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
distributors to deny you these rights or to ask you to surrender these
|
|
||||||
rights. These restrictions translate to certain responsibilities for
|
|
||||||
you if you distribute copies of the library or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of the library, whether gratis
|
|
||||||
or for a fee, you must give the recipients all the rights that we gave
|
|
||||||
you. You must make sure that they, too, receive or can get the source
|
|
||||||
code. If you link other code with the library, you must provide
|
|
||||||
complete object files to the recipients, so that they can relink them
|
|
||||||
with the library after making changes to the library and recompiling
|
|
||||||
it. And you must show them these terms so they know their rights.
|
|
||||||
|
|
||||||
We protect your rights with a two-step method: (1) we copyright the
|
|
||||||
library, and (2) we offer you this license, which gives you legal
|
|
||||||
permission to copy, distribute and/or modify the library.
|
|
||||||
|
|
||||||
To protect each distributor, we want to make it very clear that
|
|
||||||
there is no warranty for the free library. Also, if the library is
|
|
||||||
modified by someone else and passed on, the recipients should know
|
|
||||||
that what they have is not the original version, so that the original
|
|
||||||
author's reputation will not be affected by problems that might be
|
|
||||||
introduced by others.
|
|
||||||
|
|
||||||
Finally, software patents pose a constant threat to the existence of
|
|
||||||
any free program. We wish to make sure that a company cannot
|
|
||||||
effectively restrict the users of a free program by obtaining a
|
|
||||||
restrictive license from a patent holder. Therefore, we insist that
|
|
||||||
any patent license obtained for a version of the library must be
|
|
||||||
consistent with the full freedom of use specified in this license.
|
|
||||||
|
|
||||||
Most GNU software, including some libraries, is covered by the
|
|
||||||
ordinary GNU General Public License. This license, the GNU Lesser
|
|
||||||
General Public License, applies to certain designated libraries, and
|
|
||||||
is quite different from the ordinary General Public License. We use
|
|
||||||
this license for certain libraries in order to permit linking those
|
|
||||||
libraries into non-free programs.
|
|
||||||
|
|
||||||
When a program is linked with a library, whether statically or using
|
|
||||||
a shared library, the combination of the two is legally speaking a
|
|
||||||
combined work, a derivative of the original library. The ordinary
|
|
||||||
General Public License therefore permits such linking only if the
|
|
||||||
entire combination fits its criteria of freedom. The Lesser General
|
|
||||||
Public License permits more lax criteria for linking other code with
|
|
||||||
the library.
|
|
||||||
|
|
||||||
We call this license the "Lesser" General Public License because it
|
|
||||||
does Less to protect the user's freedom than the ordinary General
|
|
||||||
Public License. It also provides other free software developers Less
|
|
||||||
of an advantage over competing non-free programs. These disadvantages
|
|
||||||
are the reason we use the ordinary General Public License for many
|
|
||||||
libraries. However, the Lesser license provides advantages in certain
|
|
||||||
special circumstances.
|
|
||||||
|
|
||||||
For example, on rare occasions, there may be a special need to
|
|
||||||
encourage the widest possible use of a certain library, so that it becomes
|
|
||||||
a de-facto standard. To achieve this, non-free programs must be
|
|
||||||
allowed to use the library. A more frequent case is that a free
|
|
||||||
library does the same job as widely used non-free libraries. In this
|
|
||||||
case, there is little to gain by limiting the free library to free
|
|
||||||
software only, so we use the Lesser General Public License.
|
|
||||||
|
|
||||||
In other cases, permission to use a particular library in non-free
|
|
||||||
programs enables a greater number of people to use a large body of
|
|
||||||
free software. For example, permission to use the GNU C Library in
|
|
||||||
non-free programs enables many more people to use the whole GNU
|
|
||||||
operating system, as well as its variant, the GNU/Linux operating
|
|
||||||
system.
|
|
||||||
|
|
||||||
Although the Lesser General Public License is Less protective of the
|
|
||||||
users' freedom, it does ensure that the user of a program that is
|
|
||||||
linked with the Library has the freedom and the wherewithal to run
|
|
||||||
that program using a modified version of the Library.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow. Pay close attention to the difference between a
|
|
||||||
"work based on the library" and a "work that uses the library". The
|
|
||||||
former contains code derived from the library, whereas the latter must
|
|
||||||
be combined with the library in order to run.
|
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
|
||||||
program which contains a notice placed by the copyright holder or
|
|
||||||
other authorized party saying it may be distributed under the terms of
|
|
||||||
this Lesser General Public License (also called "this License").
|
|
||||||
Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
A "library" means a collection of software functions and/or data
|
|
||||||
prepared so as to be conveniently linked with application programs
|
|
||||||
(which use some of those functions and data) to form executables.
|
|
||||||
|
|
||||||
The "Library", below, refers to any such software library or work
|
|
||||||
which has been distributed under these terms. A "work based on the
|
|
||||||
Library" means either the Library or any derivative work under
|
|
||||||
copyright law: that is to say, a work containing the Library or a
|
|
||||||
portion of it, either verbatim or with modifications and/or translated
|
|
||||||
straightforwardly into another language. (Hereinafter, translation is
|
|
||||||
included without limitation in the term "modification".)
|
|
||||||
|
|
||||||
"Source code" for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For a library, complete source code means
|
|
||||||
all the source code for all modules it contains, plus any associated
|
|
||||||
interface definition files, plus the scripts used to control compilation
|
|
||||||
and installation of the library.
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running a program using the Library is not restricted, and output from
|
|
||||||
such a program is covered only if its contents constitute a work based
|
|
||||||
on the Library (independent of the use of the Library in a tool for
|
|
||||||
writing it). Whether that is true depends on what the Library does
|
|
||||||
and what the program that uses the Library does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Library's
|
|
||||||
complete source code as you receive it, in any medium, provided that
|
|
||||||
you conspicuously and appropriately publish on each copy an
|
|
||||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
|
||||||
all the notices that refer to this License and to the absence of any
|
|
||||||
warranty; and distribute a copy of this License along with the
|
|
||||||
Library.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy,
|
|
||||||
and you may at your option offer warranty protection in exchange for a
|
|
||||||
fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Library or any portion
|
|
||||||
of it, thus forming a work based on the Library, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The modified work must itself be a software library.
|
|
||||||
|
|
||||||
b) You must cause the files modified to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
c) You must cause the whole of the work to be licensed at no
|
|
||||||
charge to all third parties under the terms of this License.
|
|
||||||
|
|
||||||
d) If a facility in the modified Library refers to a function or a
|
|
||||||
table of data to be supplied by an application program that uses
|
|
||||||
the facility, other than as an argument passed when the facility
|
|
||||||
is invoked, then you must make a good faith effort to ensure that,
|
|
||||||
in the event an application does not supply such function or
|
|
||||||
table, the facility still operates, and performs whatever part of
|
|
||||||
its purpose remains meaningful.
|
|
||||||
|
|
||||||
(For example, a function in a library to compute square roots has
|
|
||||||
a purpose that is entirely well-defined independent of the
|
|
||||||
application. Therefore, Subsection 2d requires that any
|
|
||||||
application-supplied function or table used by this function must
|
|
||||||
be optional: if the application does not supply it, the square
|
|
||||||
root function must still compute square roots.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Library,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Library, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote
|
|
||||||
it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Library.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Library
|
|
||||||
with the Library (or with a work based on the Library) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
|
||||||
License instead of this License to a given copy of the Library. To do
|
|
||||||
this, you must alter all the notices that refer to this License, so
|
|
||||||
that they refer to the ordinary GNU General Public License, version 2,
|
|
||||||
instead of to this License. (If a newer version than version 2 of the
|
|
||||||
ordinary GNU General Public License has appeared, then you can specify
|
|
||||||
that version instead if you wish.) Do not make any other change in
|
|
||||||
these notices.
|
|
||||||
|
|
||||||
Once this change is made in a given copy, it is irreversible for
|
|
||||||
that copy, so the ordinary GNU General Public License applies to all
|
|
||||||
subsequent copies and derivative works made from that copy.
|
|
||||||
|
|
||||||
This option is useful when you wish to copy part of the code of
|
|
||||||
the Library into a program that is not a library.
|
|
||||||
|
|
||||||
4. You may copy and distribute the Library (or a portion or
|
|
||||||
derivative of it, under Section 2) in object code or executable form
|
|
||||||
under the terms of Sections 1 and 2 above provided that you accompany
|
|
||||||
it with the complete corresponding machine-readable source code, which
|
|
||||||
must be distributed under the terms of Sections 1 and 2 above on a
|
|
||||||
medium customarily used for software interchange.
|
|
||||||
|
|
||||||
If distribution of object code is made by offering access to copy
|
|
||||||
from a designated place, then offering equivalent access to copy the
|
|
||||||
source code from the same place satisfies the requirement to
|
|
||||||
distribute the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
5. A program that contains no derivative of any portion of the
|
|
||||||
Library, but is designed to work with the Library by being compiled or
|
|
||||||
linked with it, is called a "work that uses the Library". Such a
|
|
||||||
work, in isolation, is not a derivative work of the Library, and
|
|
||||||
therefore falls outside the scope of this License.
|
|
||||||
|
|
||||||
However, linking a "work that uses the Library" with the Library
|
|
||||||
creates an executable that is a derivative of the Library (because it
|
|
||||||
contains portions of the Library), rather than a "work that uses the
|
|
||||||
library". The executable is therefore covered by this License.
|
|
||||||
Section 6 states terms for distribution of such executables.
|
|
||||||
|
|
||||||
When a "work that uses the Library" uses material from a header file
|
|
||||||
that is part of the Library, the object code for the work may be a
|
|
||||||
derivative work of the Library even though the source code is not.
|
|
||||||
Whether this is true is especially significant if the work can be
|
|
||||||
linked without the Library, or if the work is itself a library. The
|
|
||||||
threshold for this to be true is not precisely defined by law.
|
|
||||||
|
|
||||||
If such an object file uses only numerical parameters, data
|
|
||||||
structure layouts and accessors, and small macros and small inline
|
|
||||||
functions (ten lines or less in length), then the use of the object
|
|
||||||
file is unrestricted, regardless of whether it is legally a derivative
|
|
||||||
work. (Executables containing this object code plus portions of the
|
|
||||||
Library will still fall under Section 6.)
|
|
||||||
|
|
||||||
Otherwise, if the work is a derivative of the Library, you may
|
|
||||||
distribute the object code for the work under the terms of Section 6.
|
|
||||||
Any executables containing that work also fall under Section 6,
|
|
||||||
whether or not they are linked directly with the Library itself.
|
|
||||||
|
|
||||||
6. As an exception to the Sections above, you may also combine or
|
|
||||||
link a "work that uses the Library" with the Library to produce a
|
|
||||||
work containing portions of the Library, and distribute that work
|
|
||||||
under terms of your choice, provided that the terms permit
|
|
||||||
modification of the work for the customer's own use and reverse
|
|
||||||
engineering for debugging such modifications.
|
|
||||||
|
|
||||||
You must give prominent notice with each copy of the work that the
|
|
||||||
Library is used in it and that the Library and its use are covered by
|
|
||||||
this License. You must supply a copy of this License. If the work
|
|
||||||
during execution displays copyright notices, you must include the
|
|
||||||
copyright notice for the Library among them, as well as a reference
|
|
||||||
directing the user to the copy of this License. Also, you must do one
|
|
||||||
of these things:
|
|
||||||
|
|
||||||
a) Accompany the work with the complete corresponding
|
|
||||||
machine-readable source code for the Library including whatever
|
|
||||||
changes were used in the work (which must be distributed under
|
|
||||||
Sections 1 and 2 above); and, if the work is an executable linked
|
|
||||||
with the Library, with the complete machine-readable "work that
|
|
||||||
uses the Library", as object code and/or source code, so that the
|
|
||||||
user can modify the Library and then relink to produce a modified
|
|
||||||
executable containing the modified Library. (It is understood
|
|
||||||
that the user who changes the contents of definitions files in the
|
|
||||||
Library will not necessarily be able to recompile the application
|
|
||||||
to use the modified definitions.)
|
|
||||||
|
|
||||||
b) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (1) uses at run time a
|
|
||||||
copy of the library already present on the user's computer system,
|
|
||||||
rather than copying library functions into the executable, and (2)
|
|
||||||
will operate properly with a modified version of the library, if
|
|
||||||
the user installs one, as long as the modified version is
|
|
||||||
interface-compatible with the version that the work was made with.
|
|
||||||
|
|
||||||
c) Accompany the work with a written offer, valid for at
|
|
||||||
least three years, to give the same user the materials
|
|
||||||
specified in Subsection 6a, above, for a charge no more
|
|
||||||
than the cost of performing this distribution.
|
|
||||||
|
|
||||||
d) If distribution of the work is made by offering access to copy
|
|
||||||
from a designated place, offer equivalent access to copy the above
|
|
||||||
specified materials from the same place.
|
|
||||||
|
|
||||||
e) Verify that the user has already received a copy of these
|
|
||||||
materials or that you have already sent this user a copy.
|
|
||||||
|
|
||||||
For an executable, the required form of the "work that uses the
|
|
||||||
Library" must include any data and utility programs needed for
|
|
||||||
reproducing the executable from it. However, as a special exception,
|
|
||||||
the materials to be distributed need not include anything that is
|
|
||||||
normally distributed (in either source or binary form) with the major
|
|
||||||
components (compiler, kernel, and so on) of the operating system on
|
|
||||||
which the executable runs, unless that component itself accompanies
|
|
||||||
the executable.
|
|
||||||
|
|
||||||
It may happen that this requirement contradicts the license
|
|
||||||
restrictions of other proprietary libraries that do not normally
|
|
||||||
accompany the operating system. Such a contradiction means you cannot
|
|
||||||
use both them and the Library together in an executable that you
|
|
||||||
distribute.
|
|
||||||
|
|
||||||
7. You may place library facilities that are a work based on the
|
|
||||||
Library side-by-side in a single library together with other library
|
|
||||||
facilities not covered by this License, and distribute such a combined
|
|
||||||
library, provided that the separate distribution of the work based on
|
|
||||||
the Library and of the other library facilities is otherwise
|
|
||||||
permitted, and provided that you do these two things:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work
|
|
||||||
based on the Library, uncombined with any other library
|
|
||||||
facilities. This must be distributed under the terms of the
|
|
||||||
Sections above.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library of the fact
|
|
||||||
that part of it is a work based on the Library, and explaining
|
|
||||||
where to find the accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
8. You may not copy, modify, sublicense, link with, or distribute
|
|
||||||
the Library except as expressly provided under this License. Any
|
|
||||||
attempt otherwise to copy, modify, sublicense, link with, or
|
|
||||||
distribute the Library is void, and will automatically terminate your
|
|
||||||
rights under this License. However, parties who have received copies,
|
|
||||||
or rights, from you under this License will not have their licenses
|
|
||||||
terminated so long as such parties remain in full compliance.
|
|
||||||
|
|
||||||
9. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Library or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Library (or any work based on the
|
|
||||||
Library), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Library or works based on it.
|
|
||||||
|
|
||||||
10. Each time you redistribute the Library (or any work based on the
|
|
||||||
Library), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute, link with or modify the Library
|
|
||||||
subject to these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties with
|
|
||||||
this License.
|
|
||||||
|
|
||||||
11. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Library at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Library by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Library.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under any
|
|
||||||
particular circumstance, the balance of the section is intended to apply,
|
|
||||||
and the section as a whole is intended to apply in other circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
12. If the distribution and/or use of the Library is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Library under this License may add
|
|
||||||
an explicit geographical distribution limitation excluding those countries,
|
|
||||||
so that distribution is permitted only in or among countries not thus
|
|
||||||
excluded. In such case, this License incorporates the limitation as if
|
|
||||||
written in the body of this License.
|
|
||||||
|
|
||||||
13. The Free Software Foundation may publish revised and/or new
|
|
||||||
versions of the Lesser General Public License from time to time.
|
|
||||||
Such new versions will be similar in spirit to the present version,
|
|
||||||
but may differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Library
|
|
||||||
specifies a version number of this License which applies to it and
|
|
||||||
"any later version", you have the option of following the terms and
|
|
||||||
conditions either of that version or of any later version published by
|
|
||||||
the Free Software Foundation. If the Library does not specify a
|
|
||||||
license version number, you may choose any version ever published by
|
|
||||||
the Free Software Foundation.
|
|
||||||
|
|
||||||
14. If you wish to incorporate parts of the Library into other free
|
|
||||||
programs whose distribution conditions are incompatible with these,
|
|
||||||
write to the author to ask for permission. For software which is
|
|
||||||
copyrighted by the Free Software Foundation, write to the Free
|
|
||||||
Software Foundation; we sometimes make exceptions for this. Our
|
|
||||||
decision will be guided by the two goals of preserving the free status
|
|
||||||
of all derivatives of our free software and of promoting the sharing
|
|
||||||
and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
|
||||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
||||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
|
||||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
|
||||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
|
||||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
|
||||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
|
||||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
|
||||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
|
||||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
|
||||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
|
||||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
|
||||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
|
||||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
|
||||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
||||||
DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Libraries
|
|
||||||
|
|
||||||
If you develop a new library, and you want it to be of the greatest
|
|
||||||
possible use to the public, we recommend making it free software that
|
|
||||||
everyone can redistribute and change. You can do so by permitting
|
|
||||||
redistribution under these terms (or, alternatively, under the terms of the
|
|
||||||
ordinary General Public License).
|
|
||||||
|
|
||||||
To apply these terms, attach the following notices to the library. It is
|
|
||||||
safest to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least the
|
|
||||||
"copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the library's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
|
||||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1990
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
That's all there is to it!
|
|
||||||
26
3rdparty/taglib/README.md
vendored
26
3rdparty/taglib/README.md
vendored
@@ -1,26 +0,0 @@
|
|||||||
# TagLib
|
|
||||||
|
|
||||||
[](https://travis-ci.org/taglib/taglib)
|
|
||||||
|
|
||||||
### TagLib Audio Metadata Library
|
|
||||||
|
|
||||||
http://taglib.org/
|
|
||||||
|
|
||||||
TagLib is a library for reading and editing the metadata of several
|
|
||||||
popular audio formats. Currently it supports both ID3v1 and [ID3v2][]
|
|
||||||
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
|
|
||||||
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE,
|
|
||||||
DSF, DFF, and ASF files.
|
|
||||||
|
|
||||||
TagLib is distributed under the [GNU Lesser General Public License][]
|
|
||||||
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
|
|
||||||
it may be used in proprietary applications, but if changes are made to
|
|
||||||
TagLib they must be contributed back to the project. Please review the
|
|
||||||
licenses if you are considering using TagLib in your project.
|
|
||||||
|
|
||||||
[ID3v2]: http://www.id3.org
|
|
||||||
[Ogg Vorbis]: http://vorbis.com/
|
|
||||||
[FLAC]: https://xiph.org/flac/
|
|
||||||
[GNU Lesser General Public License]: http://www.gnu.org/licenses/lgpl.html
|
|
||||||
[Mozilla Public License]: http://www.mozilla.org/MPL/MPL-1.1.html
|
|
||||||
|
|
||||||
170
3rdparty/taglib/ape/ape-tag-format.txt
vendored
170
3rdparty/taglib/ape/ape-tag-format.txt
vendored
@@ -1,170 +0,0 @@
|
|||||||
================================================================================
|
|
||||||
= APE Tag Specification, Version 2.000
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
Original Content (C) 2004, Frank Klemm <frank.klemm@elster.offl.uni-jena.de>
|
|
||||||
Formatting / Editing (C) 2004, Scott Wheeler <wheeler@kde.org>
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
= Contents
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
1 - APE Tag General Structure
|
|
||||||
2 - APE Tag Header / Footer Format
|
|
||||||
3 - APE Tag Flags
|
|
||||||
4 - APE Tag Item Format
|
|
||||||
5 - APE Tag Item Supported Keys
|
|
||||||
6 - APE Tag Item Content
|
|
||||||
7 - Data Types
|
|
||||||
7.1 - Data Types / UTF-8
|
|
||||||
7.2 - Data Types / Dates
|
|
||||||
7.3 - Data Types / Timestamps
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
= 1 - APE Tag General Structure
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
Member of Basic Components of SV8 Stream Note:
|
|
||||||
|
|
||||||
It is strongly recommended that the data size be stored in the tags. The size
|
|
||||||
should normally be in the roughly one kilobyte, never more than 8 kilobytes.
|
|
||||||
|
|
||||||
Larger data should be stored externally using link entries. Linked data is much
|
|
||||||
easier to process by normal programs, so for instance JPEG data should not be
|
|
||||||
included inside the audio file.
|
|
||||||
|
|
||||||
APE Tag Version 2.000 (with header, recommended):
|
|
||||||
|
|
||||||
/================================\
|
|
||||||
| APE Tag Header | 32 bytes |
|
|
||||||
|-------------------|------------|
|
|
||||||
| APE Tag Item 1 | > 10 bytes |
|
|
||||||
| APE Tag Item 2 | > 10 bytes |
|
|
||||||
| APE Tag Item n-1 | > 10 bytes |
|
|
||||||
| APE Tag Item n | > 10 bytes |
|
|
||||||
|-------------------|------------|
|
|
||||||
| APE Tag Footer | 32 bytes |
|
|
||||||
\================================/
|
|
||||||
|
|
||||||
|
|
||||||
APE tag items should be sorted ascending by size. When streaming, parts of the
|
|
||||||
APE tag may be dropped to reduce the danger of drop outs between tracks. This
|
|
||||||
is not required, but is strongly recommended. It would be desirable for the i
|
|
||||||
tems to be sorted by importance / size, but this is not feasible. This
|
|
||||||
convention should only be broken when adding less important small items and it
|
|
||||||
is not desirable to rewrite the entire tag. An APE tag at the end of a file
|
|
||||||
(the recommended location) must have at least a footer; an APE tag at the
|
|
||||||
beginning of a file (strongly discouraged) must have at least a header.
|
|
||||||
|
|
||||||
APE Tag Version 1.000 (without header, deprecated)
|
|
||||||
|
|
||||||
/================================\
|
|
||||||
| APE Tag Item 1 | > 10 bytes |
|
|
||||||
| APE Tag Item 2 | > 10 bytes |
|
|
||||||
| APE Tag Item n-1 | > 10 bytes |
|
|
||||||
| APE Tag Item n | > 10 bytes |
|
|
||||||
|-------------------|------------|
|
|
||||||
| APE Tag Footer | 32 bytes |
|
|
||||||
\================================/
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
= 2 - APE Tag Header / Footer Format
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
Contains number, length and attributes of all tag items
|
|
||||||
|
|
||||||
Header and Footer are different in 1 bit in the Tags Flags to distinguish
|
|
||||||
between them.
|
|
||||||
|
|
||||||
Member of APE Tag 2.0
|
|
||||||
|
|
||||||
/===========================================================================\
|
|
||||||
| Preamble | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' } |
|
|
||||||
|----------------|---------|------------------------------------------------|
|
|
||||||
| Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000 |
|
|
||||||
|----------------|---------|------------------------------------------------|
|
|
||||||
| Tag Size | 4 bytes | Tag size in bytes including footer and all tag |
|
|
||||||
| | | items excluding the header (for 1.000 |
|
|
||||||
| | | compatibility) |
|
|
||||||
|----------------|---------|------------------------------------------------|
|
|
||||||
| Item Count | 4 bytes | Number of items in the tag |
|
|
||||||
|----------------|---------|------------------------------------------------|
|
|
||||||
| Tag Flags | 4 bytes | Global flags |
|
|
||||||
|----------------|---------|------------------------------------------------|
|
|
||||||
| Reserved | 8 bytes | Must be zeroed |
|
|
||||||
\===========================================================================/
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
= 3 - APE Tag Flags
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
The general flag structure for either items or headers / footers is the same.
|
|
||||||
Bits 31, 30 and 29 are specific to headers / footers, whereas 2 through 0 are
|
|
||||||
item specific.
|
|
||||||
|
|
||||||
Note: APE Tags from Version 1.0 do not use any of the following. All flags in
|
|
||||||
that version are zeroed and ignored when reading.
|
|
||||||
|
|
||||||
/=================================================================\
|
|
||||||
| Contains Header | Bit 31 | 1 - has header | 0 - no header |
|
|
||||||
|-----------------|-------------|---------------------------------|
|
|
||||||
| Contains Footer | Bit 30 | 1 - has footer | 0 - no footer |
|
|
||||||
|-----------------|-------------|---------------------------------|
|
|
||||||
| Is Header | Bit 29 | 1 - is header | 0 - is footer |
|
|
||||||
|-----------------|-------------|---------------------------------|
|
|
||||||
| Undefined | Bits 28 - 3 | Undefined, must be zeroed |
|
|
||||||
|-----------------|-------------|---------------------------------|
|
|
||||||
| Encoding | Bits 2 - 1 | 00 - UTF-8 |
|
|
||||||
| | | 01 - Binary Data * |
|
|
||||||
| | | 10 - External Reference ** |
|
|
||||||
| | | 11 - Reserved |
|
|
||||||
|-----------------|-------------|---------------------------------|
|
|
||||||
| Read Only | Bit 0 | 1 - read only | 0 - read/write |
|
|
||||||
\=================================================================/
|
|
||||||
|
|
||||||
(*) Should be ignored by tools for editing text values
|
|
||||||
(**) Allowed external reference formats:
|
|
||||||
- http://host/directory/filename.ext
|
|
||||||
- ftp://host/directory/filename.ext
|
|
||||||
- filename.ext
|
|
||||||
- /directory/filename.ext
|
|
||||||
- DRIVE:/directory/filename.ext
|
|
||||||
|
|
||||||
Note: External references are also UTF-8 encoded.
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
= 4 - APE Tag Item Format
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
APE Tag Items are stored as key-value pairs. APE Tags Item Key are case
|
|
||||||
sensitive, however it is illegal to use keys which only differ in case and
|
|
||||||
it is recommended that tag reading not be case sensitive.
|
|
||||||
|
|
||||||
Every key can only occur (at most) once. It is not possible to repeat a key
|
|
||||||
to signify updated contents.
|
|
||||||
|
|
||||||
Tags can be partially or completely repeated in the streaming format. This
|
|
||||||
makes it possible to display an artist and / or title if it was missed at the
|
|
||||||
beginning of the stream. It is recommended that the important information like
|
|
||||||
artist, album and title should occur approximately every 2 minutes in the
|
|
||||||
stream and again 5 to 10 seconds before the end. However, care should be tak
|
|
||||||
en not to replicate this information too often or during passages with high
|
|
||||||
bitrate demands to avoid unnecessary drop-outs.
|
|
||||||
|
|
||||||
/==============================================================================\
|
|
||||||
| Content Size | 4 bytes | Length of the value in bytes |
|
|
||||||
|----------------|---------------|---------------------------------------------|
|
|
||||||
| Flags | 4 bytes | Item flags |
|
|
||||||
|----------------|---------------|---------------------------------------------|
|
|
||||||
| Key | 2 - 255 bytes | Item key |
|
|
||||||
|----------------|---------------|---------------------------------------------|
|
|
||||||
| Key Terminator | 1 byte | Null byte that indicates the end of the key |
|
|
||||||
|----------------|---------------|---------------------------------------------|
|
|
||||||
| Value | variable | Content (formatted according to the flags) |
|
|
||||||
\==============================================================================/
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
Sections 5 - 7 haven't yet been converted from:
|
|
||||||
|
|
||||||
http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html
|
|
||||||
314
3rdparty/taglib/ape/apefile.cpp
vendored
314
3rdparty/taglib/ape/apefile.cpp
vendored
@@ -1,314 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2010 by Alex Novichkov
|
|
||||||
email : novichko@atnet.ru
|
|
||||||
|
|
||||||
copyright : (C) 2006 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
(original WavPack implementation)
|
|
||||||
|
|
||||||
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
(original MPC implementation)
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tbytevector.h>
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <tagunion.h>
|
|
||||||
#include <id3v1tag.h>
|
|
||||||
#include <id3v2header.h>
|
|
||||||
#include <tpropertymap.h>
|
|
||||||
#include <tagutils.h>
|
|
||||||
|
|
||||||
#include "apefile.h"
|
|
||||||
#include "apetag.h"
|
|
||||||
#include "apefooter.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
|
|
||||||
}
|
|
||||||
|
|
||||||
class APE::File::FilePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FilePrivate() :
|
|
||||||
APELocation(-1),
|
|
||||||
APESize(0),
|
|
||||||
ID3v1Location(-1),
|
|
||||||
ID3v2Header(0),
|
|
||||||
ID3v2Location(-1),
|
|
||||||
ID3v2Size(0),
|
|
||||||
properties(0) {}
|
|
||||||
|
|
||||||
~FilePrivate()
|
|
||||||
{
|
|
||||||
delete ID3v2Header;
|
|
||||||
delete properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
long APELocation;
|
|
||||||
long APESize;
|
|
||||||
|
|
||||||
long ID3v1Location;
|
|
||||||
|
|
||||||
ID3v2::Header *ID3v2Header;
|
|
||||||
long ID3v2Location;
|
|
||||||
long ID3v2Size;
|
|
||||||
|
|
||||||
TagUnion tag;
|
|
||||||
|
|
||||||
Properties *properties;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// static members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool APE::File::isSupported(IOStream *stream)
|
|
||||||
{
|
|
||||||
// An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
|
|
||||||
|
|
||||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
|
|
||||||
return (buffer.find("MAC ") >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
|
||||||
TagLib::File(file),
|
|
||||||
d(new FilePrivate())
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
|
|
||||||
TagLib::File(stream),
|
|
||||||
d(new FilePrivate())
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::File::~File()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::Tag *APE::File::tag() const
|
|
||||||
{
|
|
||||||
return &d->tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap APE::File::properties() const
|
|
||||||
{
|
|
||||||
return d->tag.properties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::File::removeUnsupportedProperties(const StringList &properties)
|
|
||||||
{
|
|
||||||
d->tag.removeUnsupportedProperties(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
|
||||||
{
|
|
||||||
if(ID3v1Tag())
|
|
||||||
ID3v1Tag()->setProperties(properties);
|
|
||||||
|
|
||||||
return APETag(true)->setProperties(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Properties *APE::File::audioProperties() const
|
|
||||||
{
|
|
||||||
return d->properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::File::save()
|
|
||||||
{
|
|
||||||
if(readOnly()) {
|
|
||||||
debug("APE::File::save() -- File is read only.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update ID3v1 tag
|
|
||||||
|
|
||||||
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
|
|
||||||
|
|
||||||
// ID3v1 tag is not empty. Update the old one or create a new one.
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0) {
|
|
||||||
seek(d->ID3v1Location);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
seek(0, End);
|
|
||||||
d->ID3v1Location = tell();
|
|
||||||
}
|
|
||||||
|
|
||||||
writeBlock(ID3v1Tag()->render());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
// ID3v1 tag is empty. Remove the old one.
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0) {
|
|
||||||
truncate(d->ID3v1Location);
|
|
||||||
d->ID3v1Location = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update APE tag
|
|
||||||
|
|
||||||
if(APETag() && !APETag()->isEmpty()) {
|
|
||||||
|
|
||||||
// APE tag is not empty. Update the old one or create a new one.
|
|
||||||
|
|
||||||
if(d->APELocation < 0) {
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
d->APELocation = d->ID3v1Location;
|
|
||||||
else
|
|
||||||
d->APELocation = length();
|
|
||||||
}
|
|
||||||
|
|
||||||
const ByteVector data = APETag()->render();
|
|
||||||
insert(data, d->APELocation, d->APESize);
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
|
|
||||||
|
|
||||||
d->APESize = data.size();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
// APE tag is empty. Remove the old one.
|
|
||||||
|
|
||||||
if(d->APELocation >= 0) {
|
|
||||||
removeBlock(d->APELocation, d->APESize);
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
d->ID3v1Location -= d->APESize;
|
|
||||||
|
|
||||||
d->APELocation = -1;
|
|
||||||
d->APESize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID3v1::Tag *APE::File::ID3v1Tag(bool create)
|
|
||||||
{
|
|
||||||
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, create);
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Tag *APE::File::APETag(bool create)
|
|
||||||
{
|
|
||||||
return d->tag.access<APE::Tag>(ApeAPEIndex, create);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::File::strip(int tags)
|
|
||||||
{
|
|
||||||
if(tags & ID3v1)
|
|
||||||
d->tag.set(ApeID3v1Index, 0);
|
|
||||||
|
|
||||||
if(tags & APE)
|
|
||||||
d->tag.set(ApeAPEIndex, 0);
|
|
||||||
|
|
||||||
if(!ID3v1Tag())
|
|
||||||
APETag(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::File::hasAPETag() const
|
|
||||||
{
|
|
||||||
return (d->APELocation >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::File::hasID3v1Tag() const
|
|
||||||
{
|
|
||||||
return (d->ID3v1Location >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void APE::File::read(bool readProperties)
|
|
||||||
{
|
|
||||||
// Look for an ID3v2 tag
|
|
||||||
|
|
||||||
d->ID3v2Location = Utils::findID3v2(this);
|
|
||||||
|
|
||||||
if(d->ID3v2Location >= 0) {
|
|
||||||
seek(d->ID3v2Location);
|
|
||||||
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
|
|
||||||
d->ID3v2Size = d->ID3v2Header->completeTagSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for an ID3v1 tag
|
|
||||||
|
|
||||||
d->ID3v1Location = Utils::findID3v1(this);
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
|
||||||
|
|
||||||
// Look for an APE tag
|
|
||||||
|
|
||||||
d->APELocation = Utils::findAPE(this, d->ID3v1Location);
|
|
||||||
|
|
||||||
if(d->APELocation >= 0) {
|
|
||||||
d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation));
|
|
||||||
d->APESize = APETag()->footer()->completeTagSize();
|
|
||||||
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(d->ID3v1Location < 0)
|
|
||||||
APETag(true);
|
|
||||||
|
|
||||||
// Look for APE audio properties
|
|
||||||
|
|
||||||
if(readProperties) {
|
|
||||||
|
|
||||||
long streamLength;
|
|
||||||
|
|
||||||
if(d->APELocation >= 0)
|
|
||||||
streamLength = d->APELocation;
|
|
||||||
else if(d->ID3v1Location >= 0)
|
|
||||||
streamLength = d->ID3v1Location;
|
|
||||||
else
|
|
||||||
streamLength = length();
|
|
||||||
|
|
||||||
if(d->ID3v2Location >= 0) {
|
|
||||||
seek(d->ID3v2Location + d->ID3v2Size);
|
|
||||||
streamLength -= (d->ID3v2Location + d->ID3v2Size);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
seek(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
d->properties = new Properties(this, streamLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
235
3rdparty/taglib/ape/apefile.h
vendored
235
3rdparty/taglib/ape/apefile.h
vendored
@@ -1,235 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2010 by Alex Novichkov
|
|
||||||
email : novichko@atnet.ru
|
|
||||||
|
|
||||||
copyright : (C) 2006 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
(original WavPack implementation)
|
|
||||||
|
|
||||||
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
(original MPC implementation)
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_APEFILE_H
|
|
||||||
#define TAGLIB_APEFILE_H
|
|
||||||
|
|
||||||
#include "tfile.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "apeproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
class Tag;
|
|
||||||
|
|
||||||
namespace ID3v1 { class Tag; }
|
|
||||||
namespace APE { class Tag; }
|
|
||||||
|
|
||||||
//! An implementation of APE metadata
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This is implementation of APE metadata.
|
|
||||||
*
|
|
||||||
* This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
|
|
||||||
* properties from the file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace APE {
|
|
||||||
|
|
||||||
//! An implementation of TagLib::File with APE specific methods
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This implements and provides an interface for APE files to the
|
|
||||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
|
||||||
* the abstract TagLib::File API as well as providing some additional
|
|
||||||
* information specific to APE files.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT File : public TagLib::File
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* This set of flags is used for various operations and is suitable for
|
|
||||||
* being OR-ed together.
|
|
||||||
*/
|
|
||||||
enum TagTypes {
|
|
||||||
//! Empty set. Matches no tag types.
|
|
||||||
NoTags = 0x0000,
|
|
||||||
//! Matches ID3v1 tags.
|
|
||||||
ID3v1 = 0x0001,
|
|
||||||
//! Matches APE tags.
|
|
||||||
APE = 0x0002,
|
|
||||||
//! Matches all tag types.
|
|
||||||
AllTags = 0xffff
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an APE file from \a file. If \a readProperties is true the
|
|
||||||
* file's audio properties will also be read.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
|
||||||
*/
|
|
||||||
File(FileName file, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an APE file from \a stream. If \a readProperties is true the
|
|
||||||
* file's audio properties will also be read.
|
|
||||||
*
|
|
||||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
|
||||||
* responsible for deleting it after the File object.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
|
||||||
*/
|
|
||||||
File(IOStream *stream, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this instance of the File.
|
|
||||||
*/
|
|
||||||
virtual ~File();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
|
|
||||||
* or a combination of the two.
|
|
||||||
*/
|
|
||||||
virtual TagLib::Tag *tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- export function.
|
|
||||||
* If the file contains both an APE and an ID3v1 tag, only APE
|
|
||||||
* will be converted to the PropertyMap.
|
|
||||||
*/
|
|
||||||
PropertyMap properties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Removes unsupported properties. Forwards to the actual Tag's
|
|
||||||
* removeUnsupportedProperties() function.
|
|
||||||
*/
|
|
||||||
void removeUnsupportedProperties(const StringList &properties);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- import function.
|
|
||||||
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
|
|
||||||
* tag will be updated as well.
|
|
||||||
*/
|
|
||||||
PropertyMap setProperties(const PropertyMap &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the APE::Properties for this file. If no audio properties
|
|
||||||
* were read then this will return a null pointer.
|
|
||||||
*/
|
|
||||||
virtual Properties *audioProperties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Saves the file.
|
|
||||||
*
|
|
||||||
* \note According to the official Monkey's Audio SDK, an APE file
|
|
||||||
* can only have either ID3V1 or APE tags, so a parameter is used here.
|
|
||||||
*/
|
|
||||||
virtual bool save();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to the ID3v1 tag of the file.
|
|
||||||
*
|
|
||||||
* If \a create is false (the default) this may return a null pointer
|
|
||||||
* if there is no valid ID3v1 tag. If \a create is true it will create
|
|
||||||
* an ID3v1 tag if one does not exist and returns a valid pointer.
|
|
||||||
*
|
|
||||||
* \note This may return a valid pointer regardless of whether or not the
|
|
||||||
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
|
|
||||||
* on disk actually has an ID3v1 tag.
|
|
||||||
*
|
|
||||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
|
||||||
* deleted by the user. It will be deleted when the file (object) is
|
|
||||||
* destroyed.
|
|
||||||
*
|
|
||||||
* \see hasID3v1Tag()
|
|
||||||
*/
|
|
||||||
ID3v1::Tag *ID3v1Tag(bool create = false);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to the APE tag of the file.
|
|
||||||
*
|
|
||||||
* If \a create is false (the default) this may return a null pointer
|
|
||||||
* if there is no valid APE tag. If \a create is true it will create
|
|
||||||
* an APE tag if one does not exist and returns a valid pointer.
|
|
||||||
*
|
|
||||||
* \note This may return a valid pointer regardless of whether or not the
|
|
||||||
* file on disk has an APE tag. Use hasAPETag() to check if the file
|
|
||||||
* on disk actually has an APE tag.
|
|
||||||
*
|
|
||||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
|
||||||
* deleted by the user. It will be deleted when the file (object) is
|
|
||||||
* destroyed.
|
|
||||||
*
|
|
||||||
* \see hasAPETag()
|
|
||||||
*/
|
|
||||||
APE::Tag *APETag(bool create = false);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This will remove the tags that match the OR-ed together TagTypes from the
|
|
||||||
* file. By default it removes all tags.
|
|
||||||
*
|
|
||||||
* \note This will also invalidate pointers to the tags
|
|
||||||
* as their memory will be freed.
|
|
||||||
* \note In order to make the removal permanent save() still needs to be called
|
|
||||||
*/
|
|
||||||
void strip(int tags = AllTags);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the file on disk actually has an APE tag.
|
|
||||||
*
|
|
||||||
* \see APETag()
|
|
||||||
*/
|
|
||||||
bool hasAPETag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
|
||||||
*
|
|
||||||
* \see ID3v1Tag()
|
|
||||||
*/
|
|
||||||
bool hasID3v1Tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the given \a stream can be opened as an APE
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* \note This method is designed to do a quick check. The result may
|
|
||||||
* not necessarily be correct.
|
|
||||||
*/
|
|
||||||
static bool isSupported(IOStream *stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
File(const File &);
|
|
||||||
File &operator=(const File &);
|
|
||||||
|
|
||||||
void read(bool readProperties);
|
|
||||||
|
|
||||||
class FilePrivate;
|
|
||||||
FilePrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
234
3rdparty/taglib/ape/apefooter.cpp
vendored
234
3rdparty/taglib/ape/apefooter.cpp
vendored
@@ -1,234 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
||||||
(C) 2002 - 2008 by Scott Wheeler (id3v2header.cpp)
|
|
||||||
email : kde@carewolf.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <bitset>
|
|
||||||
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
|
|
||||||
#include "apefooter.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
using namespace APE;
|
|
||||||
|
|
||||||
class APE::Footer::FooterPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FooterPrivate() :
|
|
||||||
version(0),
|
|
||||||
footerPresent(true),
|
|
||||||
headerPresent(false),
|
|
||||||
isHeader(false),
|
|
||||||
itemCount(0),
|
|
||||||
tagSize(0) {}
|
|
||||||
|
|
||||||
unsigned int version;
|
|
||||||
|
|
||||||
bool footerPresent;
|
|
||||||
bool headerPresent;
|
|
||||||
|
|
||||||
bool isHeader;
|
|
||||||
|
|
||||||
unsigned int itemCount;
|
|
||||||
unsigned int tagSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// static members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
unsigned int APE::Footer::size()
|
|
||||||
{
|
|
||||||
return 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Footer::fileIdentifier()
|
|
||||||
{
|
|
||||||
return ByteVector("APETAGEX");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
APE::Footer::Footer() :
|
|
||||||
d(new FooterPrivate())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Footer::Footer(const ByteVector &data) :
|
|
||||||
d(new FooterPrivate())
|
|
||||||
{
|
|
||||||
parse(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Footer::~Footer()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int APE::Footer::version() const
|
|
||||||
{
|
|
||||||
return d->version;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::Footer::headerPresent() const
|
|
||||||
{
|
|
||||||
return d->headerPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::Footer::footerPresent() const
|
|
||||||
{
|
|
||||||
return d->footerPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::Footer::isHeader() const
|
|
||||||
{
|
|
||||||
return d->isHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Footer::setHeaderPresent(bool b) const
|
|
||||||
{
|
|
||||||
d->headerPresent = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int APE::Footer::itemCount() const
|
|
||||||
{
|
|
||||||
return d->itemCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Footer::setItemCount(unsigned int s)
|
|
||||||
{
|
|
||||||
d->itemCount = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int APE::Footer::tagSize() const
|
|
||||||
{
|
|
||||||
return d->tagSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int APE::Footer::completeTagSize() const
|
|
||||||
{
|
|
||||||
if(d->headerPresent)
|
|
||||||
return d->tagSize + size();
|
|
||||||
else
|
|
||||||
return d->tagSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Footer::setTagSize(unsigned int s)
|
|
||||||
{
|
|
||||||
d->tagSize = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Footer::setData(const ByteVector &data)
|
|
||||||
{
|
|
||||||
parse(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Footer::renderFooter() const
|
|
||||||
{
|
|
||||||
return render(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Footer::renderHeader() const
|
|
||||||
{
|
|
||||||
if(!d->headerPresent)
|
|
||||||
return ByteVector();
|
|
||||||
else
|
|
||||||
return render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// protected members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void APE::Footer::parse(const ByteVector &data)
|
|
||||||
{
|
|
||||||
if(data.size() < size())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// The first eight bytes, data[0..7], are the File Identifier, "APETAGEX".
|
|
||||||
|
|
||||||
// Read the version number
|
|
||||||
|
|
||||||
d->version = data.toUInt(8, false);
|
|
||||||
|
|
||||||
// Read the tag size
|
|
||||||
|
|
||||||
d->tagSize = data.toUInt(12, false);
|
|
||||||
|
|
||||||
// Read the item count
|
|
||||||
|
|
||||||
d->itemCount = data.toUInt(16, false);
|
|
||||||
|
|
||||||
// Read the flags
|
|
||||||
|
|
||||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
|
|
||||||
|
|
||||||
d->headerPresent = flags[31];
|
|
||||||
d->footerPresent = !flags[30];
|
|
||||||
d->isHeader = flags[29];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Footer::render(bool isHeader) const
|
|
||||||
{
|
|
||||||
ByteVector v;
|
|
||||||
|
|
||||||
// add the file identifier -- "APETAGEX"
|
|
||||||
|
|
||||||
v.append(fileIdentifier());
|
|
||||||
|
|
||||||
// add the version number -- we always render a 2.000 tag regardless of what
|
|
||||||
// the tag originally was.
|
|
||||||
|
|
||||||
v.append(ByteVector::fromUInt(2000, false));
|
|
||||||
|
|
||||||
// add the tag size
|
|
||||||
|
|
||||||
v.append(ByteVector::fromUInt(d->tagSize, false));
|
|
||||||
|
|
||||||
// add the item count
|
|
||||||
|
|
||||||
v.append(ByteVector::fromUInt(d->itemCount, false));
|
|
||||||
|
|
||||||
// render and add the flags
|
|
||||||
|
|
||||||
std::bitset<32> flags;
|
|
||||||
|
|
||||||
flags[31] = d->headerPresent;
|
|
||||||
flags[30] = false; // footer is always present
|
|
||||||
flags[29] = isHeader;
|
|
||||||
|
|
||||||
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
|
|
||||||
|
|
||||||
// add the reserved 64bit
|
|
||||||
|
|
||||||
v.append(ByteVector::fromLongLong(0));
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
173
3rdparty/taglib/ape/apefooter.h
vendored
173
3rdparty/taglib/ape/apefooter.h
vendored
@@ -1,173 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_APEFOOTER_H
|
|
||||||
#define TAGLIB_APEFOOTER_H
|
|
||||||
|
|
||||||
#include "tbytevector.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace APE {
|
|
||||||
|
|
||||||
//! An implementation of APE footers
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This class implements APE footers (and headers). It attempts to follow, both
|
|
||||||
* semantically and programmatically, the structure specified in
|
|
||||||
* the APE v2.0 standard. The API is based on the properties of APE footer and
|
|
||||||
* headers specified there.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Footer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Constructs an empty APE footer.
|
|
||||||
*/
|
|
||||||
Footer();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an APE footer based on \a data. parse() is called
|
|
||||||
* immediately.
|
|
||||||
*/
|
|
||||||
Footer(const ByteVector &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys the footer.
|
|
||||||
*/
|
|
||||||
virtual ~Footer();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the version number. (Note: This is the 1000 or 2000.)
|
|
||||||
*/
|
|
||||||
unsigned int version() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if a header is present in the tag.
|
|
||||||
*/
|
|
||||||
bool headerPresent() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if a footer is present in the tag.
|
|
||||||
*/
|
|
||||||
bool footerPresent() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true this is actually the header.
|
|
||||||
*/
|
|
||||||
bool isHeader() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets whether the header should be rendered or not
|
|
||||||
*/
|
|
||||||
void setHeaderPresent(bool b) const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of items in the tag.
|
|
||||||
*/
|
|
||||||
unsigned int itemCount() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the item count to \a s.
|
|
||||||
* \see itemCount()
|
|
||||||
*/
|
|
||||||
void setItemCount(unsigned int s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the tag size in bytes. This is the size of the frame content and footer.
|
|
||||||
* The size of the \e entire tag will be this plus the header size, if present.
|
|
||||||
*
|
|
||||||
* \see completeTagSize()
|
|
||||||
*/
|
|
||||||
unsigned int tagSize() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the tag size, including if present, the header
|
|
||||||
* size.
|
|
||||||
*
|
|
||||||
* \see tagSize()
|
|
||||||
*/
|
|
||||||
unsigned int completeTagSize() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the tag size to \a s.
|
|
||||||
* \see tagSize()
|
|
||||||
*/
|
|
||||||
void setTagSize(unsigned int s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the size of the footer. Presently this is always 32 bytes.
|
|
||||||
*/
|
|
||||||
static unsigned int size();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the string used to identify an APE tag inside of a file.
|
|
||||||
* Presently this is always "APETAGEX".
|
|
||||||
*/
|
|
||||||
static ByteVector fileIdentifier();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the data that will be used as the footer. 32 bytes,
|
|
||||||
* starting from \a data will be used.
|
|
||||||
*/
|
|
||||||
void setData(const ByteVector &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Renders the footer back to binary format.
|
|
||||||
*/
|
|
||||||
ByteVector renderFooter() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Renders the header corresponding to the footer. If headerPresent is
|
|
||||||
* set to false, it returns an empty ByteVector.
|
|
||||||
*/
|
|
||||||
ByteVector renderHeader() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/*!
|
|
||||||
* Called by setData() to parse the footer data. It makes this information
|
|
||||||
* available through the public API.
|
|
||||||
*/
|
|
||||||
void parse(const ByteVector &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Called by renderFooter and renderHeader
|
|
||||||
*/
|
|
||||||
ByteVector render(bool isHeader) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Footer(const Footer &);
|
|
||||||
Footer &operator=(const Footer &);
|
|
||||||
|
|
||||||
class FooterPrivate;
|
|
||||||
FooterPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
301
3rdparty/taglib/ape/apeitem.cpp
vendored
301
3rdparty/taglib/ape/apeitem.cpp
vendored
@@ -1,301 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tbytevectorlist.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
|
|
||||||
#include "apeitem.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
using namespace APE;
|
|
||||||
|
|
||||||
class APE::Item::ItemPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ItemPrivate() :
|
|
||||||
type(Text),
|
|
||||||
readOnly(false) {}
|
|
||||||
|
|
||||||
Item::ItemTypes type;
|
|
||||||
String key;
|
|
||||||
ByteVector value;
|
|
||||||
StringList text;
|
|
||||||
bool readOnly;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
APE::Item::Item() :
|
|
||||||
d(new ItemPrivate())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Item::Item(const String &key, const String &value) :
|
|
||||||
d(new ItemPrivate())
|
|
||||||
{
|
|
||||||
d->key = key;
|
|
||||||
d->text.append(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Item::Item(const String &key, const StringList &values) :
|
|
||||||
d(new ItemPrivate())
|
|
||||||
{
|
|
||||||
d->key = key;
|
|
||||||
d->text = values;
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
|
|
||||||
d(new ItemPrivate())
|
|
||||||
{
|
|
||||||
d->key = key;
|
|
||||||
if(binary) {
|
|
||||||
d->type = Binary;
|
|
||||||
d->value = value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
d->text.append(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Item::Item(const Item &item) :
|
|
||||||
d(new ItemPrivate(*item.d))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Item::~Item()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
Item &APE::Item::operator=(const Item &item)
|
|
||||||
{
|
|
||||||
Item(item).swap(*this);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::swap(Item &item)
|
|
||||||
{
|
|
||||||
using std::swap;
|
|
||||||
|
|
||||||
swap(d, item.d);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::setReadOnly(bool readOnly)
|
|
||||||
{
|
|
||||||
d->readOnly = readOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::Item::isReadOnly() const
|
|
||||||
{
|
|
||||||
return d->readOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::setType(APE::Item::ItemTypes val)
|
|
||||||
{
|
|
||||||
d->type = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Item::ItemTypes APE::Item::type() const
|
|
||||||
{
|
|
||||||
return d->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
String APE::Item::key() const
|
|
||||||
{
|
|
||||||
return d->key;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Item::binaryData() const
|
|
||||||
{
|
|
||||||
return d->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::setBinaryData(const ByteVector &value)
|
|
||||||
{
|
|
||||||
d->type = Binary;
|
|
||||||
d->value = value;
|
|
||||||
d->text.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Item::value() const
|
|
||||||
{
|
|
||||||
// This seems incorrect as it won't be actually rendering the value to keep it
|
|
||||||
// up to date.
|
|
||||||
|
|
||||||
return d->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::setKey(const String &key)
|
|
||||||
{
|
|
||||||
d->key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::setValue(const String &value)
|
|
||||||
{
|
|
||||||
d->type = Text;
|
|
||||||
d->text = value;
|
|
||||||
d->value.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::setValues(const StringList &value)
|
|
||||||
{
|
|
||||||
d->type = Text;
|
|
||||||
d->text = value;
|
|
||||||
d->value.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::appendValue(const String &value)
|
|
||||||
{
|
|
||||||
d->type = Text;
|
|
||||||
d->text.append(value);
|
|
||||||
d->value.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::appendValues(const StringList &values)
|
|
||||||
{
|
|
||||||
d->type = Text;
|
|
||||||
d->text.append(values);
|
|
||||||
d->value.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Item::size() const
|
|
||||||
{
|
|
||||||
int result = 8 + d->key.size() + 1;
|
|
||||||
switch(d->type) {
|
|
||||||
case Text:
|
|
||||||
if(!d->text.isEmpty()) {
|
|
||||||
StringList::ConstIterator it = d->text.begin();
|
|
||||||
|
|
||||||
result += it->data(String::UTF8).size();
|
|
||||||
it++;
|
|
||||||
for(; it != d->text.end(); ++it)
|
|
||||||
result += 1 + it->data(String::UTF8).size();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Binary:
|
|
||||||
case Locator:
|
|
||||||
result += d->value.size();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringList APE::Item::toStringList() const
|
|
||||||
{
|
|
||||||
return d->text;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringList APE::Item::values() const
|
|
||||||
{
|
|
||||||
return d->text;
|
|
||||||
}
|
|
||||||
|
|
||||||
String APE::Item::toString() const
|
|
||||||
{
|
|
||||||
if(d->type == Text && !isEmpty())
|
|
||||||
return d->text.front();
|
|
||||||
else
|
|
||||||
return String();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::Item::isEmpty() const
|
|
||||||
{
|
|
||||||
switch(d->type) {
|
|
||||||
case Text:
|
|
||||||
if(d->text.isEmpty())
|
|
||||||
return true;
|
|
||||||
if(d->text.size() == 1 && d->text.front().isEmpty())
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
case Binary:
|
|
||||||
case Locator:
|
|
||||||
return d->value.isEmpty();
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Item::parse(const ByteVector &data)
|
|
||||||
{
|
|
||||||
// 11 bytes is the minimum size for an APE item
|
|
||||||
|
|
||||||
if(data.size() < 11) {
|
|
||||||
debug("APE::Item::parse() -- no data in item");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned int valueLength = data.toUInt(0, false);
|
|
||||||
const unsigned int flags = data.toUInt(4, false);
|
|
||||||
|
|
||||||
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
|
|
||||||
// We assume that the validity of the given key has been checked.
|
|
||||||
|
|
||||||
d->key = String(&data[8], String::Latin1);
|
|
||||||
|
|
||||||
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
|
|
||||||
|
|
||||||
setReadOnly(flags & 1);
|
|
||||||
setType(ItemTypes((flags >> 1) & 3));
|
|
||||||
|
|
||||||
if(Text == d->type)
|
|
||||||
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
|
||||||
else
|
|
||||||
d->value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Item::render() const
|
|
||||||
{
|
|
||||||
ByteVector data;
|
|
||||||
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
|
|
||||||
ByteVector value;
|
|
||||||
|
|
||||||
if(isEmpty())
|
|
||||||
return data;
|
|
||||||
|
|
||||||
if(d->type == Text) {
|
|
||||||
StringList::ConstIterator it = d->text.begin();
|
|
||||||
|
|
||||||
value.append(it->data(String::UTF8));
|
|
||||||
it++;
|
|
||||||
for(; it != d->text.end(); ++it) {
|
|
||||||
value.append('\0');
|
|
||||||
value.append(it->data(String::UTF8));
|
|
||||||
}
|
|
||||||
d->value = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
value.append(d->value);
|
|
||||||
|
|
||||||
data.append(ByteVector::fromUInt(value.size(), false));
|
|
||||||
data.append(ByteVector::fromUInt(flags, false));
|
|
||||||
data.append(d->key.data(String::Latin1));
|
|
||||||
data.append(ByteVector('\0'));
|
|
||||||
data.append(value);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
224
3rdparty/taglib/ape/apeitem.h
vendored
224
3rdparty/taglib/ape/apeitem.h
vendored
@@ -1,224 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_APEITEM_H
|
|
||||||
#define TAGLIB_APEITEM_H
|
|
||||||
|
|
||||||
#include "tbytevector.h"
|
|
||||||
#include "tstring.h"
|
|
||||||
#include "tstringlist.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace APE {
|
|
||||||
|
|
||||||
//! An implementation of APE-items
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This class provides the features of items in the APEv2 standard.
|
|
||||||
*/
|
|
||||||
class TAGLIB_EXPORT Item
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Enum of types an Item can have. The value of 3 is reserved.
|
|
||||||
*/
|
|
||||||
enum ItemTypes {
|
|
||||||
//! Item contains text information coded in UTF-8
|
|
||||||
Text = 0,
|
|
||||||
//! Item contains binary information
|
|
||||||
Binary = 1,
|
|
||||||
//! Item is a locator of external stored information
|
|
||||||
Locator = 2
|
|
||||||
};
|
|
||||||
/*!
|
|
||||||
* Constructs an empty item.
|
|
||||||
*/
|
|
||||||
Item();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs a text item with \a key and \a value.
|
|
||||||
*/
|
|
||||||
// BIC: Remove this, StringList has a constructor from a single string
|
|
||||||
Item(const String &key, const String &value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs a text item with \a key and \a values.
|
|
||||||
*/
|
|
||||||
Item(const String &key, const StringList &values);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an item with \a key and \a value.
|
|
||||||
* If \a binary is true a Binary item will be created, otherwise \a value will be interpreted as text
|
|
||||||
*/
|
|
||||||
Item(const String &key, const ByteVector &value, bool binary);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Construct an item as a copy of \a item.
|
|
||||||
*/
|
|
||||||
Item(const Item &item);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys the item.
|
|
||||||
*/
|
|
||||||
virtual ~Item();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Copies the contents of \a item into this item.
|
|
||||||
*/
|
|
||||||
Item &operator=(const Item &item);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Exchanges the content of this item by the content of \a item.
|
|
||||||
*/
|
|
||||||
void swap(Item &item);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the key.
|
|
||||||
*/
|
|
||||||
String key() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the binary value.
|
|
||||||
* If the item type is not \a Binary, always returns an empty ByteVector.
|
|
||||||
*/
|
|
||||||
ByteVector binaryData() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the binary value to \a value
|
|
||||||
* The item's type will also be set to \a Binary
|
|
||||||
*/
|
|
||||||
void setBinaryData(const ByteVector &value);
|
|
||||||
|
|
||||||
#ifndef DO_NOT_DOCUMENT
|
|
||||||
/* Remove in next binary incompatible release */
|
|
||||||
ByteVector value() const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the key for the item to \a key.
|
|
||||||
*/
|
|
||||||
void setKey(const String &key);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the text value of the item to \a value and clears any previous contents.
|
|
||||||
*
|
|
||||||
* \see toString()
|
|
||||||
*/
|
|
||||||
void setValue(const String &value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the text value of the item to the list of values in \a value and clears
|
|
||||||
* any previous contents.
|
|
||||||
*
|
|
||||||
* \see toStringList()
|
|
||||||
*/
|
|
||||||
void setValues(const StringList &values);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Appends \a value to create (or extend) the current list of text values.
|
|
||||||
*
|
|
||||||
* \see toString()
|
|
||||||
*/
|
|
||||||
void appendValue(const String &value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Appends \a values to extend the current list of text values.
|
|
||||||
*
|
|
||||||
* \see toStringList()
|
|
||||||
*/
|
|
||||||
void appendValues(const StringList &values);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the size of the full item.
|
|
||||||
*/
|
|
||||||
int size() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the value as a single string. In case of multiple strings,
|
|
||||||
* the first is returned. If the data type is not \a Text, always returns
|
|
||||||
* an empty String.
|
|
||||||
*/
|
|
||||||
String toString() const;
|
|
||||||
|
|
||||||
#ifndef DO_NOT_DOCUMENT
|
|
||||||
/* Remove in next binary incompatible release */
|
|
||||||
StringList toStringList() const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the list of text values. If the data type is not \a Text, always
|
|
||||||
* returns an empty StringList.
|
|
||||||
*/
|
|
||||||
StringList values() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Render the item to a ByteVector.
|
|
||||||
*/
|
|
||||||
ByteVector render() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Parse the item from the ByteVector \a data.
|
|
||||||
*/
|
|
||||||
void parse(const ByteVector& data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the item to read-only.
|
|
||||||
*/
|
|
||||||
void setReadOnly(bool readOnly);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Return true if the item is read-only.
|
|
||||||
*/
|
|
||||||
bool isReadOnly() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the type of the item to \a type.
|
|
||||||
*
|
|
||||||
* \see ItemTypes
|
|
||||||
*/
|
|
||||||
void setType(ItemTypes type);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the type of the item.
|
|
||||||
*/
|
|
||||||
ItemTypes type() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns if the item has any real content.
|
|
||||||
*/
|
|
||||||
bool isEmpty() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class ItemPrivate;
|
|
||||||
ItemPrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
252
3rdparty/taglib/ape/apeproperties.cpp
vendored
252
3rdparty/taglib/ape/apeproperties.cpp
vendored
@@ -1,252 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2010 by Alex Novichkov
|
|
||||||
email : novichko@atnet.ru
|
|
||||||
|
|
||||||
copyright : (C) 2006 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
(original WavPack implementation)
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <bitset>
|
|
||||||
#include "id3v2tag.h"
|
|
||||||
#include "apeproperties.h"
|
|
||||||
#include "apefile.h"
|
|
||||||
#include "apetag.h"
|
|
||||||
#include "apefooter.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class APE::Properties::PropertiesPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PropertiesPrivate() :
|
|
||||||
length(0),
|
|
||||||
bitrate(0),
|
|
||||||
sampleRate(0),
|
|
||||||
channels(0),
|
|
||||||
version(0),
|
|
||||||
bitsPerSample(0),
|
|
||||||
sampleFrames(0) {}
|
|
||||||
|
|
||||||
int length;
|
|
||||||
int bitrate;
|
|
||||||
int sampleRate;
|
|
||||||
int channels;
|
|
||||||
int version;
|
|
||||||
int bitsPerSample;
|
|
||||||
unsigned int sampleFrames;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
APE::Properties::Properties(File *, ReadStyle style) :
|
|
||||||
AudioProperties(style),
|
|
||||||
d(new PropertiesPrivate())
|
|
||||||
{
|
|
||||||
debug("APE::Properties::Properties() -- This constructor is no longer used.");
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Properties::Properties(File *file, long streamLength, ReadStyle style) :
|
|
||||||
AudioProperties(style),
|
|
||||||
d(new PropertiesPrivate())
|
|
||||||
{
|
|
||||||
read(file, streamLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Properties::~Properties()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Properties::length() const
|
|
||||||
{
|
|
||||||
return lengthInSeconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Properties::lengthInSeconds() const
|
|
||||||
{
|
|
||||||
return d->length / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Properties::lengthInMilliseconds() const
|
|
||||||
{
|
|
||||||
return d->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Properties::bitrate() const
|
|
||||||
{
|
|
||||||
return d->bitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Properties::sampleRate() const
|
|
||||||
{
|
|
||||||
return d->sampleRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Properties::channels() const
|
|
||||||
{
|
|
||||||
return d->channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Properties::version() const
|
|
||||||
{
|
|
||||||
return d->version;
|
|
||||||
}
|
|
||||||
|
|
||||||
int APE::Properties::bitsPerSample() const
|
|
||||||
{
|
|
||||||
return d->bitsPerSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int APE::Properties::sampleFrames() const
|
|
||||||
{
|
|
||||||
return d->sampleFrames;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
int headerVersion(const ByteVector &header)
|
|
||||||
{
|
|
||||||
if(header.size() < 6 || !header.startsWith("MAC "))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return header.toUShort(4, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Properties::read(File *file, long streamLength)
|
|
||||||
{
|
|
||||||
// First, we assume that the file pointer is set at the first descriptor.
|
|
||||||
long offset = file->tell();
|
|
||||||
int version = headerVersion(file->readBlock(6));
|
|
||||||
|
|
||||||
// Next, we look for the descriptor.
|
|
||||||
if(version < 0) {
|
|
||||||
offset = file->find("MAC ", offset);
|
|
||||||
file->seek(offset);
|
|
||||||
version = headerVersion(file->readBlock(6));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(version < 0) {
|
|
||||||
debug("APE::Properties::read() -- APE descriptor not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->version = version;
|
|
||||||
|
|
||||||
if(d->version >= 3980)
|
|
||||||
analyzeCurrent(file);
|
|
||||||
else
|
|
||||||
analyzeOld(file);
|
|
||||||
|
|
||||||
if(d->sampleFrames > 0 && d->sampleRate > 0) {
|
|
||||||
const double length = d->sampleFrames * 1000.0 / d->sampleRate;
|
|
||||||
d->length = static_cast<int>(length + 0.5);
|
|
||||||
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Properties::analyzeCurrent(File *file)
|
|
||||||
{
|
|
||||||
// Read the descriptor
|
|
||||||
file->seek(2, File::Current);
|
|
||||||
const ByteVector descriptor = file->readBlock(44);
|
|
||||||
if(descriptor.size() < 44) {
|
|
||||||
debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
|
|
||||||
|
|
||||||
if((descriptorBytes - 52) > 0)
|
|
||||||
file->seek(descriptorBytes - 52, File::Current);
|
|
||||||
|
|
||||||
// Read the header
|
|
||||||
const ByteVector header = file->readBlock(24);
|
|
||||||
if(header.size() < 24) {
|
|
||||||
debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the APE info
|
|
||||||
d->channels = header.toShort(18, false);
|
|
||||||
d->sampleRate = header.toUInt(20, false);
|
|
||||||
d->bitsPerSample = header.toShort(16, false);
|
|
||||||
|
|
||||||
const unsigned int totalFrames = header.toUInt(12, false);
|
|
||||||
if(totalFrames == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const unsigned int blocksPerFrame = header.toUInt(4, false);
|
|
||||||
const unsigned int finalFrameBlocks = header.toUInt(8, false);
|
|
||||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Properties::analyzeOld(File *file)
|
|
||||||
{
|
|
||||||
const ByteVector header = file->readBlock(26);
|
|
||||||
if(header.size() < 26) {
|
|
||||||
debug("APE::Properties::analyzeOld() -- MAC header is too short.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned int totalFrames = header.toUInt(18, false);
|
|
||||||
|
|
||||||
// Fail on 0 length APE files (catches non-finalized APE files)
|
|
||||||
if(totalFrames == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const short compressionLevel = header.toShort(0, false);
|
|
||||||
unsigned int blocksPerFrame;
|
|
||||||
if(d->version >= 3950)
|
|
||||||
blocksPerFrame = 73728 * 4;
|
|
||||||
else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
|
|
||||||
blocksPerFrame = 73728;
|
|
||||||
else
|
|
||||||
blocksPerFrame = 9216;
|
|
||||||
|
|
||||||
// Get the APE info
|
|
||||||
d->channels = header.toShort(4, false);
|
|
||||||
d->sampleRate = header.toUInt(6, false);
|
|
||||||
|
|
||||||
const unsigned int finalFrameBlocks = header.toUInt(22, false);
|
|
||||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
|
||||||
|
|
||||||
// Get the bit depth from the RIFF-fmt chunk.
|
|
||||||
file->seek(16, File::Current);
|
|
||||||
const ByteVector fmt = file->readBlock(28);
|
|
||||||
if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
|
|
||||||
debug("APE::Properties::analyzeOld() -- fmt header is too short.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->bitsPerSample = fmt.toShort(26, false);
|
|
||||||
}
|
|
||||||
143
3rdparty/taglib/ape/apeproperties.h
vendored
143
3rdparty/taglib/ape/apeproperties.h
vendored
@@ -1,143 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2010 by Alex Novichkov
|
|
||||||
email : novichko@atnet.ru
|
|
||||||
|
|
||||||
copyright : (C) 2006 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
(original WavPack implementation)
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_APEPROPERTIES_H
|
|
||||||
#define TAGLIB_APEPROPERTIES_H
|
|
||||||
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "audioproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace APE {
|
|
||||||
|
|
||||||
class File;
|
|
||||||
|
|
||||||
//! An implementation of audio property reading for APE
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This reads the data from an APE stream found in the AudioProperties
|
|
||||||
* API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Create an instance of APE::Properties with the data read from the
|
|
||||||
* APE::File \a file.
|
|
||||||
*
|
|
||||||
* \deprecated
|
|
||||||
*/
|
|
||||||
Properties(File *file, ReadStyle style = Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Create an instance of APE::Properties with the data read from the
|
|
||||||
* APE::File \a file.
|
|
||||||
*/
|
|
||||||
Properties(File *file, long streamLength, ReadStyle style = Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this APE::Properties instance.
|
|
||||||
*/
|
|
||||||
virtual ~Properties();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in seconds. The length is rounded down to
|
|
||||||
* the nearest whole second.
|
|
||||||
*
|
|
||||||
* \note This method is just an alias of lengthInSeconds().
|
|
||||||
*
|
|
||||||
* \deprecated
|
|
||||||
*/
|
|
||||||
virtual int length() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in seconds. The length is rounded down to
|
|
||||||
* the nearest whole second.
|
|
||||||
*
|
|
||||||
* \see lengthInMilliseconds()
|
|
||||||
*/
|
|
||||||
// BIC: make virtual
|
|
||||||
int lengthInSeconds() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in milliseconds.
|
|
||||||
*
|
|
||||||
* \see lengthInSeconds()
|
|
||||||
*/
|
|
||||||
// BIC: make virtual
|
|
||||||
int lengthInMilliseconds() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the average bit rate of the file in kb/s.
|
|
||||||
*/
|
|
||||||
virtual int bitrate() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the sample rate in Hz.
|
|
||||||
*/
|
|
||||||
virtual int sampleRate() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of audio channels.
|
|
||||||
*/
|
|
||||||
virtual int channels() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of bits per audio sample.
|
|
||||||
*/
|
|
||||||
int bitsPerSample() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the total number of audio samples in file.
|
|
||||||
*/
|
|
||||||
unsigned int sampleFrames() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns APE version.
|
|
||||||
*/
|
|
||||||
int version() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Properties(const Properties &);
|
|
||||||
Properties &operator=(const Properties &);
|
|
||||||
|
|
||||||
void read(File *file, long streamLength);
|
|
||||||
|
|
||||||
void analyzeCurrent(File *file);
|
|
||||||
void analyzeOld(File *file);
|
|
||||||
|
|
||||||
class PropertiesPrivate;
|
|
||||||
PropertiesPrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
437
3rdparty/taglib/ape/apetag.cpp
vendored
437
3rdparty/taglib/ape/apetag.cpp
vendored
@@ -1,437 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5130)
|
|
||||||
// Sun Studio finds multiple specializations of Map because
|
|
||||||
// it considers specializations with and without class types
|
|
||||||
// to be different; this define forces Map to use only the
|
|
||||||
// specialization with the class keyword.
|
|
||||||
#define WANT_CLASS_INSTANTIATION_OF_MAP (1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <tfile.h>
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tmap.h>
|
|
||||||
#include <tpropertymap.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <tutils.h>
|
|
||||||
|
|
||||||
#include "apetag.h"
|
|
||||||
#include "apefooter.h"
|
|
||||||
#include "apeitem.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
using namespace APE;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const unsigned int MinKeyLength = 2;
|
|
||||||
const unsigned int MaxKeyLength = 255;
|
|
||||||
|
|
||||||
bool isKeyValid(const ByteVector &key)
|
|
||||||
{
|
|
||||||
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 };
|
|
||||||
|
|
||||||
// only allow printable ASCII including space (32..126)
|
|
||||||
|
|
||||||
for(ByteVector::ConstIterator it = key.begin(); it != key.end(); ++it) {
|
|
||||||
const int c = static_cast<unsigned char>(*it);
|
|
||||||
if(c < 32 || c > 126)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const String upperKey = String(key).upper();
|
|
||||||
for(size_t i = 0; invalidKeys[i] != 0; ++i) {
|
|
||||||
if(upperKey == invalidKeys[i])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class APE::Tag::TagPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TagPrivate() :
|
|
||||||
file(0),
|
|
||||||
footerLocation(0) {}
|
|
||||||
|
|
||||||
File *file;
|
|
||||||
long footerLocation;
|
|
||||||
|
|
||||||
Footer footer;
|
|
||||||
ItemListMap itemListMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public methods
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
APE::Tag::Tag() :
|
|
||||||
TagLib::Tag(),
|
|
||||||
d(new TagPrivate())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
|
|
||||||
TagLib::Tag(),
|
|
||||||
d(new TagPrivate())
|
|
||||||
{
|
|
||||||
d->file = file;
|
|
||||||
d->footerLocation = footerLocation;
|
|
||||||
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Tag::~Tag()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Tag::fileIdentifier()
|
|
||||||
{
|
|
||||||
return ByteVector::fromCString("APETAGEX");
|
|
||||||
}
|
|
||||||
|
|
||||||
String APE::Tag::title() const
|
|
||||||
{
|
|
||||||
if(d->itemListMap["TITLE"].isEmpty())
|
|
||||||
return String();
|
|
||||||
return d->itemListMap["TITLE"].values().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String APE::Tag::artist() const
|
|
||||||
{
|
|
||||||
if(d->itemListMap["ARTIST"].isEmpty())
|
|
||||||
return String();
|
|
||||||
return d->itemListMap["ARTIST"].values().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String APE::Tag::album() const
|
|
||||||
{
|
|
||||||
if(d->itemListMap["ALBUM"].isEmpty())
|
|
||||||
return String();
|
|
||||||
return d->itemListMap["ALBUM"].values().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String APE::Tag::comment() const
|
|
||||||
{
|
|
||||||
if(d->itemListMap["COMMENT"].isEmpty())
|
|
||||||
return String();
|
|
||||||
return d->itemListMap["COMMENT"].values().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String APE::Tag::genre() const
|
|
||||||
{
|
|
||||||
if(d->itemListMap["GENRE"].isEmpty())
|
|
||||||
return String();
|
|
||||||
return d->itemListMap["GENRE"].values().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int APE::Tag::year() const
|
|
||||||
{
|
|
||||||
if(d->itemListMap["YEAR"].isEmpty())
|
|
||||||
return 0;
|
|
||||||
return d->itemListMap["YEAR"].toString().toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int APE::Tag::track() const
|
|
||||||
{
|
|
||||||
if(d->itemListMap["TRACK"].isEmpty())
|
|
||||||
return 0;
|
|
||||||
return d->itemListMap["TRACK"].toString().toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setTitle(const String &s)
|
|
||||||
{
|
|
||||||
addValue("TITLE", s, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setArtist(const String &s)
|
|
||||||
{
|
|
||||||
addValue("ARTIST", s, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setAlbum(const String &s)
|
|
||||||
{
|
|
||||||
addValue("ALBUM", s, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setComment(const String &s)
|
|
||||||
{
|
|
||||||
addValue("COMMENT", s, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setGenre(const String &s)
|
|
||||||
{
|
|
||||||
addValue("GENRE", s, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setYear(unsigned int i)
|
|
||||||
{
|
|
||||||
if(i == 0)
|
|
||||||
removeItem("YEAR");
|
|
||||||
else
|
|
||||||
addValue("YEAR", String::number(i), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setTrack(unsigned int i)
|
|
||||||
{
|
|
||||||
if(i == 0)
|
|
||||||
removeItem("TRACK");
|
|
||||||
else
|
|
||||||
addValue("TRACK", String::number(i), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// conversions of tag keys between what we use in PropertyMap and what's usual
|
|
||||||
// for APE tags
|
|
||||||
// usual, APE
|
|
||||||
const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
|
|
||||||
{"DATE", "YEAR" },
|
|
||||||
{"ALBUMARTIST", "ALBUM ARTIST"},
|
|
||||||
{"DISCNUMBER", "DISC" },
|
|
||||||
{"REMIXER", "MIXARTIST" }};
|
|
||||||
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap APE::Tag::properties() const
|
|
||||||
{
|
|
||||||
PropertyMap properties;
|
|
||||||
ItemListMap::ConstIterator it = itemListMap().begin();
|
|
||||||
for(; it != itemListMap().end(); ++it) {
|
|
||||||
String tagName = it->first.upper();
|
|
||||||
// if the item is Binary or Locator, or if the key is an invalid string,
|
|
||||||
// add to unsupportedData
|
|
||||||
if(it->second.type() != Item::Text || tagName.isEmpty()) {
|
|
||||||
properties.unsupportedData().append(it->first);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Some tags need to be handled specially
|
|
||||||
for(size_t i = 0; i < keyConversionsSize; ++i) {
|
|
||||||
if(tagName == keyConversions[i][1])
|
|
||||||
tagName = keyConversions[i][0];
|
|
||||||
}
|
|
||||||
properties[tagName].append(it->second.toStringList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
|
|
||||||
{
|
|
||||||
StringList::ConstIterator it = properties.begin();
|
|
||||||
for(; it != properties.end(); ++it)
|
|
||||||
removeItem(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
|
||||||
{
|
|
||||||
PropertyMap properties(origProps); // make a local copy that can be modified
|
|
||||||
|
|
||||||
// see comment in properties()
|
|
||||||
for(size_t i = 0; i < keyConversionsSize; ++i)
|
|
||||||
if(properties.contains(keyConversions[i][0])) {
|
|
||||||
properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]);
|
|
||||||
properties.erase(keyConversions[i][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// first check if tags need to be removed completely
|
|
||||||
StringList toRemove;
|
|
||||||
ItemListMap::ConstIterator remIt = itemListMap().begin();
|
|
||||||
for(; remIt != itemListMap().end(); ++remIt) {
|
|
||||||
String key = remIt->first.upper();
|
|
||||||
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
|
|
||||||
if(!key.isEmpty() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
|
|
||||||
toRemove.append(remIt->first);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(StringList::ConstIterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
|
|
||||||
removeItem(*removeIt);
|
|
||||||
|
|
||||||
// now sync in the "forward direction"
|
|
||||||
PropertyMap::ConstIterator it = properties.begin();
|
|
||||||
PropertyMap invalid;
|
|
||||||
for(; it != properties.end(); ++it) {
|
|
||||||
const String &tagName = it->first;
|
|
||||||
if(!checkKey(tagName))
|
|
||||||
invalid.insert(it->first, it->second);
|
|
||||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
|
||||||
if(it->second.isEmpty())
|
|
||||||
removeItem(tagName);
|
|
||||||
else {
|
|
||||||
StringList::ConstIterator valueIt = it->second.begin();
|
|
||||||
addValue(tagName, *valueIt, true);
|
|
||||||
++valueIt;
|
|
||||||
for(; valueIt != it->second.end(); ++valueIt)
|
|
||||||
addValue(tagName, *valueIt, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::Tag::checkKey(const String &key)
|
|
||||||
{
|
|
||||||
if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return isKeyValid(key.data(String::UTF8));
|
|
||||||
}
|
|
||||||
|
|
||||||
APE::Footer *APE::Tag::footer() const
|
|
||||||
{
|
|
||||||
return &d->footer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const APE::ItemListMap& APE::Tag::itemListMap() const
|
|
||||||
{
|
|
||||||
return d->itemListMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::removeItem(const String &key)
|
|
||||||
{
|
|
||||||
d->itemListMap.erase(key.upper());
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::addValue(const String &key, const String &value, bool replace)
|
|
||||||
{
|
|
||||||
if(replace)
|
|
||||||
removeItem(key);
|
|
||||||
|
|
||||||
if(value.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Text items may contain more than one value.
|
|
||||||
// Binary or locator items may have only one value, hence always replaced.
|
|
||||||
|
|
||||||
ItemListMap::Iterator it = d->itemListMap.find(key.upper());
|
|
||||||
|
|
||||||
if(it != d->itemListMap.end() && it->second.type() == Item::Text)
|
|
||||||
it->second.appendValue(value);
|
|
||||||
else
|
|
||||||
setItem(key, Item(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setData(const String &key, const ByteVector &value)
|
|
||||||
{
|
|
||||||
removeItem(key);
|
|
||||||
|
|
||||||
if(value.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
setItem(key, Item(key, value, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::setItem(const String &key, const Item &item)
|
|
||||||
{
|
|
||||||
if(!checkKey(key)) {
|
|
||||||
debug("APE::Tag::setItem() - Couldn't set an item due to an invalid key.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->itemListMap[key.upper()] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool APE::Tag::isEmpty() const
|
|
||||||
{
|
|
||||||
return d->itemListMap.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// protected methods
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void APE::Tag::read()
|
|
||||||
{
|
|
||||||
if(d->file && d->file->isValid()) {
|
|
||||||
|
|
||||||
d->file->seek(d->footerLocation);
|
|
||||||
d->footer.setData(d->file->readBlock(Footer::size()));
|
|
||||||
|
|
||||||
if(d->footer.tagSize() <= Footer::size() ||
|
|
||||||
d->footer.tagSize() > static_cast<unsigned long>(d->file->length()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
|
|
||||||
parse(d->file->readBlock(d->footer.tagSize() - Footer::size()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector APE::Tag::render() const
|
|
||||||
{
|
|
||||||
ByteVector data;
|
|
||||||
unsigned int itemCount = 0;
|
|
||||||
|
|
||||||
for(ItemListMap::ConstIterator it = d->itemListMap.begin(); it != d->itemListMap.end(); ++it) {
|
|
||||||
data.append(it->second.render());
|
|
||||||
itemCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->footer.setItemCount(itemCount);
|
|
||||||
d->footer.setTagSize(data.size() + Footer::size());
|
|
||||||
d->footer.setHeaderPresent(true);
|
|
||||||
|
|
||||||
return d->footer.renderHeader() + data + d->footer.renderFooter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void APE::Tag::parse(const ByteVector &data)
|
|
||||||
{
|
|
||||||
// 11 bytes is the minimum size for an APE item
|
|
||||||
|
|
||||||
if(data.size() < 11)
|
|
||||||
return;
|
|
||||||
|
|
||||||
unsigned int pos = 0;
|
|
||||||
|
|
||||||
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
|
||||||
|
|
||||||
const int nullPos = data.find('\0', pos + 8);
|
|
||||||
if(nullPos < 0) {
|
|
||||||
debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned int keyLength = nullPos - pos - 8;
|
|
||||||
const unsigned int valLegnth = data.toUInt(pos, false);
|
|
||||||
|
|
||||||
if(keyLength >= MinKeyLength
|
|
||||||
&& keyLength <= MaxKeyLength
|
|
||||||
&& isKeyValid(data.mid(pos + 8, keyLength)))
|
|
||||||
{
|
|
||||||
APE::Item item;
|
|
||||||
item.parse(data.mid(pos));
|
|
||||||
|
|
||||||
d->itemListMap.insert(item.key().upper(), item);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug("APE::Tag::parse() - Skipped an item due to an invalid key.");
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += keyLength + valLegnth + 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
208
3rdparty/taglib/ape/apetag.h
vendored
208
3rdparty/taglib/ape/apetag.h
vendored
@@ -1,208 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2004 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_APETAG_H
|
|
||||||
#define TAGLIB_APETAG_H
|
|
||||||
|
|
||||||
#include "tag.h"
|
|
||||||
#include "tbytevector.h"
|
|
||||||
#include "tmap.h"
|
|
||||||
#include "tstring.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
|
|
||||||
#include "apeitem.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
class File;
|
|
||||||
|
|
||||||
//! An implementation of the APE tagging format
|
|
||||||
|
|
||||||
namespace APE {
|
|
||||||
|
|
||||||
class Footer;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* A mapping between a list of item names, or keys, and the associated item.
|
|
||||||
*
|
|
||||||
* \see APE::Tag::itemListMap()
|
|
||||||
*/
|
|
||||||
typedef Map<const String, Item> ItemListMap;
|
|
||||||
|
|
||||||
|
|
||||||
//! An APE tag implementation
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Create an APE tag with default values.
|
|
||||||
*/
|
|
||||||
Tag();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Create an APE tag and parse the data in \a file with APE footer at
|
|
||||||
* \a tagOffset.
|
|
||||||
*/
|
|
||||||
Tag(TagLib::File *file, long footerLocation);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this Tag instance.
|
|
||||||
*/
|
|
||||||
virtual ~Tag();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Renders the in memory values to a ByteVector suitable for writing to
|
|
||||||
* the file.
|
|
||||||
*/
|
|
||||||
ByteVector render() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the string "APETAGEX" suitable for usage in locating the tag in a
|
|
||||||
* file.
|
|
||||||
*/
|
|
||||||
static ByteVector fileIdentifier();
|
|
||||||
|
|
||||||
// Reimplementations.
|
|
||||||
|
|
||||||
virtual String title() const;
|
|
||||||
virtual String artist() const;
|
|
||||||
virtual String album() const;
|
|
||||||
virtual String comment() const;
|
|
||||||
virtual String genre() const;
|
|
||||||
virtual unsigned int year() const;
|
|
||||||
virtual unsigned int track() const;
|
|
||||||
|
|
||||||
virtual void setTitle(const String &s);
|
|
||||||
virtual void setArtist(const String &s);
|
|
||||||
virtual void setAlbum(const String &s);
|
|
||||||
virtual void setComment(const String &s);
|
|
||||||
virtual void setGenre(const String &s);
|
|
||||||
virtual void setYear(unsigned int i);
|
|
||||||
virtual void setTrack(unsigned int i);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified tag dictionary interface -- export function.
|
|
||||||
* APE tags are perfectly compatible with the dictionary interface because they
|
|
||||||
* support both arbitrary tag names and multiple values. Currently only
|
|
||||||
* APE items of type *Text* are handled by the dictionary interface; all *Binary*
|
|
||||||
* and *Locator* items will be put into the unsupportedData list and can be
|
|
||||||
* deleted on request using removeUnsupportedProperties(). The same happens
|
|
||||||
* to Text items if their key is invalid for PropertyMap (which should actually
|
|
||||||
* never happen).
|
|
||||||
*
|
|
||||||
* The only conversion done by this export function is to rename the APE tags
|
|
||||||
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively,
|
|
||||||
* in order to be compliant with the names used in other formats.
|
|
||||||
*/
|
|
||||||
PropertyMap properties() const;
|
|
||||||
|
|
||||||
void removeUnsupportedProperties(const StringList &properties);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified tag dictionary interface -- import function. The same
|
|
||||||
* comments as for the export function apply; additionally note that the APE tag
|
|
||||||
* specification requires keys to have between 2 and 16 printable ASCII characters
|
|
||||||
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
|
|
||||||
*/
|
|
||||||
PropertyMap setProperties(const PropertyMap &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Check if the given String is a valid APE tag key.
|
|
||||||
*/
|
|
||||||
static bool checkKey(const String&);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to the tag's footer.
|
|
||||||
*/
|
|
||||||
Footer *footer() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a reference to the item list map. This is an ItemListMap of
|
|
||||||
* all of the items in the tag.
|
|
||||||
*
|
|
||||||
* This is the most powerful structure for accessing the items of the tag.
|
|
||||||
*
|
|
||||||
* APE tags are case-insensitive, all keys in this map have been converted
|
|
||||||
* to upper case.
|
|
||||||
*
|
|
||||||
* \warning You should not modify this data structure directly, instead
|
|
||||||
* use setItem() and removeItem().
|
|
||||||
*/
|
|
||||||
const ItemListMap &itemListMap() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Removes the \a key item from the tag
|
|
||||||
*/
|
|
||||||
void removeItem(const String &key);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Adds to the text item specified by \a key the data \a value. If \a replace
|
|
||||||
* is true, then all of the other values on the same key will be removed
|
|
||||||
* first. If a binary item exists for \a key it will be removed first.
|
|
||||||
*/
|
|
||||||
void addValue(const String &key, const String &value, bool replace = true);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the binary data for the key specified by \a item to \a value
|
|
||||||
* This will convert the item to type \a Binary if it isn't already and
|
|
||||||
* all of the other values on the same key will be removed.
|
|
||||||
*/
|
|
||||||
void setData(const String &key, const ByteVector &value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the \a key item to the value of \a item. If an item with the \a key is already
|
|
||||||
* present, it will be replaced.
|
|
||||||
*/
|
|
||||||
void setItem(const String &key, const Item &item);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if the tag does not contain any data.
|
|
||||||
*/
|
|
||||||
bool isEmpty() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Reads from the file specified in the constructor.
|
|
||||||
*/
|
|
||||||
void read();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Parses the body of the tag in \a data.
|
|
||||||
*/
|
|
||||||
void parse(const ByteVector &data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Tag(const Tag &);
|
|
||||||
Tag &operator=(const Tag &);
|
|
||||||
|
|
||||||
class TagPrivate;
|
|
||||||
TagPrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
351
3rdparty/taglib/asf/asfattribute.cpp
vendored
351
3rdparty/taglib/asf/asfattribute.cpp
vendored
@@ -1,351 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <taglib.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <trefcounter.h>
|
|
||||||
|
|
||||||
#include "asfattribute.h"
|
|
||||||
#include "asffile.h"
|
|
||||||
#include "asfutils.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class ASF::Attribute::AttributePrivate : public RefCounter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AttributePrivate() :
|
|
||||||
pictureValue(ASF::Picture::fromInvalid()),
|
|
||||||
numericValue(0),
|
|
||||||
stream(0),
|
|
||||||
language(0) {}
|
|
||||||
AttributeTypes type;
|
|
||||||
String stringValue;
|
|
||||||
ByteVector byteVectorValue;
|
|
||||||
ASF::Picture pictureValue;
|
|
||||||
unsigned long long numericValue;
|
|
||||||
int stream;
|
|
||||||
int language;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute() :
|
|
||||||
d(new AttributePrivate())
|
|
||||||
{
|
|
||||||
d->type = UnicodeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute(const ASF::Attribute &other) :
|
|
||||||
d(other.d)
|
|
||||||
{
|
|
||||||
d->ref();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute(const String &value) :
|
|
||||||
d(new AttributePrivate())
|
|
||||||
{
|
|
||||||
d->type = UnicodeType;
|
|
||||||
d->stringValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute(const ByteVector &value) :
|
|
||||||
d(new AttributePrivate())
|
|
||||||
{
|
|
||||||
d->type = BytesType;
|
|
||||||
d->byteVectorValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute(const ASF::Picture &value) :
|
|
||||||
d(new AttributePrivate())
|
|
||||||
{
|
|
||||||
d->type = BytesType;
|
|
||||||
d->pictureValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute(unsigned int value) :
|
|
||||||
d(new AttributePrivate())
|
|
||||||
{
|
|
||||||
d->type = DWordType;
|
|
||||||
d->numericValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute(unsigned long long value) :
|
|
||||||
d(new AttributePrivate())
|
|
||||||
{
|
|
||||||
d->type = QWordType;
|
|
||||||
d->numericValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute(unsigned short value) :
|
|
||||||
d(new AttributePrivate())
|
|
||||||
{
|
|
||||||
d->type = WordType;
|
|
||||||
d->numericValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::Attribute(bool value) :
|
|
||||||
d(new AttributePrivate())
|
|
||||||
{
|
|
||||||
d->type = BoolType;
|
|
||||||
d->numericValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
|
|
||||||
{
|
|
||||||
Attribute(other).swap(*this);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Attribute::swap(Attribute &other)
|
|
||||||
{
|
|
||||||
using std::swap;
|
|
||||||
|
|
||||||
swap(d, other.d);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::~Attribute()
|
|
||||||
{
|
|
||||||
if(d->deref())
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
|
|
||||||
{
|
|
||||||
return d->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Attribute::toString() const
|
|
||||||
{
|
|
||||||
return d->stringValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::Attribute::toByteVector() const
|
|
||||||
{
|
|
||||||
if(d->pictureValue.isValid())
|
|
||||||
return d->pictureValue.render();
|
|
||||||
return d->byteVectorValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short ASF::Attribute::toBool() const
|
|
||||||
{
|
|
||||||
return d->numericValue ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short ASF::Attribute::toUShort() const
|
|
||||||
{
|
|
||||||
return static_cast<unsigned short>(d->numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int ASF::Attribute::toUInt() const
|
|
||||||
{
|
|
||||||
return static_cast<unsigned int>(d->numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long ASF::Attribute::toULongLong() const
|
|
||||||
{
|
|
||||||
return static_cast<unsigned long long>(d->numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Picture ASF::Attribute::toPicture() const
|
|
||||||
{
|
|
||||||
return d->pictureValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Attribute::parse(ASF::File &f, int kind)
|
|
||||||
{
|
|
||||||
unsigned int size, nameLength;
|
|
||||||
String name;
|
|
||||||
d->pictureValue = Picture::fromInvalid();
|
|
||||||
// extended content descriptor
|
|
||||||
if(kind == 0) {
|
|
||||||
nameLength = readWORD(&f);
|
|
||||||
name = readString(&f, nameLength);
|
|
||||||
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
|
||||||
size = readWORD(&f);
|
|
||||||
}
|
|
||||||
// metadata & metadata library
|
|
||||||
else {
|
|
||||||
int temp = readWORD(&f);
|
|
||||||
// metadata library
|
|
||||||
if(kind == 2) {
|
|
||||||
d->language = temp;
|
|
||||||
}
|
|
||||||
d->stream = readWORD(&f);
|
|
||||||
nameLength = readWORD(&f);
|
|
||||||
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
|
||||||
size = readDWORD(&f);
|
|
||||||
name = readString(&f, nameLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(kind != 2 && size > 65535) {
|
|
||||||
debug("ASF::Attribute::parse() -- Value larger than 64kB");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(d->type) {
|
|
||||||
case WordType:
|
|
||||||
d->numericValue = readWORD(&f);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BoolType:
|
|
||||||
if(kind == 0) {
|
|
||||||
d->numericValue = (readDWORD(&f) != 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
d->numericValue = (readWORD(&f) != 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DWordType:
|
|
||||||
d->numericValue = readDWORD(&f);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QWordType:
|
|
||||||
d->numericValue = readQWORD(&f);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UnicodeType:
|
|
||||||
d->stringValue = readString(&f, size);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BytesType:
|
|
||||||
case GuidType:
|
|
||||||
d->byteVectorValue = f.readBlock(size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(d->type == BytesType && name == "WM/Picture") {
|
|
||||||
d->pictureValue.parse(d->byteVectorValue);
|
|
||||||
if(d->pictureValue.isValid()) {
|
|
||||||
d->byteVectorValue.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Attribute::dataSize() const
|
|
||||||
{
|
|
||||||
switch (d->type) {
|
|
||||||
case WordType:
|
|
||||||
return 2;
|
|
||||||
case BoolType:
|
|
||||||
return 4;
|
|
||||||
case DWordType:
|
|
||||||
return 4;
|
|
||||||
case QWordType:
|
|
||||||
return 5;
|
|
||||||
case UnicodeType:
|
|
||||||
return d->stringValue.size() * 2 + 2;
|
|
||||||
case BytesType:
|
|
||||||
if(d->pictureValue.isValid())
|
|
||||||
return d->pictureValue.dataSize();
|
|
||||||
case GuidType:
|
|
||||||
return d->byteVectorValue.size();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::Attribute::render(const String &name, int kind) const
|
|
||||||
{
|
|
||||||
ByteVector data;
|
|
||||||
|
|
||||||
switch (d->type) {
|
|
||||||
case WordType:
|
|
||||||
data.append(ByteVector::fromShort(toUShort(), false));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BoolType:
|
|
||||||
if(kind == 0) {
|
|
||||||
data.append(ByteVector::fromUInt(toBool(), false));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data.append(ByteVector::fromShort(toBool(), false));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DWordType:
|
|
||||||
data.append(ByteVector::fromUInt(toUInt(), false));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QWordType:
|
|
||||||
data.append(ByteVector::fromLongLong(toULongLong(), false));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UnicodeType:
|
|
||||||
data.append(renderString(d->stringValue));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BytesType:
|
|
||||||
if(d->pictureValue.isValid()) {
|
|
||||||
data.append(d->pictureValue.render());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GuidType:
|
|
||||||
data.append(d->byteVectorValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(kind == 0) {
|
|
||||||
data = renderString(name, true) +
|
|
||||||
ByteVector::fromShort((int)d->type, false) +
|
|
||||||
ByteVector::fromShort(data.size(), false) +
|
|
||||||
data;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ByteVector nameData = renderString(name);
|
|
||||||
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
|
|
||||||
ByteVector::fromShort(d->stream, false) +
|
|
||||||
ByteVector::fromShort(nameData.size(), false) +
|
|
||||||
ByteVector::fromShort((int)d->type, false) +
|
|
||||||
ByteVector::fromUInt(data.size(), false) +
|
|
||||||
nameData +
|
|
||||||
data;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Attribute::language() const
|
|
||||||
{
|
|
||||||
return d->language;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Attribute::setLanguage(int value)
|
|
||||||
{
|
|
||||||
d->language = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Attribute::stream() const
|
|
||||||
{
|
|
||||||
return d->stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Attribute::setStream(int value)
|
|
||||||
{
|
|
||||||
d->stream = value;
|
|
||||||
}
|
|
||||||
208
3rdparty/taglib/asf/asfattribute.h
vendored
208
3rdparty/taglib/asf/asfattribute.h
vendored
@@ -1,208 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_ASFATTRIBUTE_H
|
|
||||||
#define TAGLIB_ASFATTRIBUTE_H
|
|
||||||
|
|
||||||
#include "tstring.h"
|
|
||||||
#include "tbytevector.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "asfpicture.h"
|
|
||||||
|
|
||||||
namespace TagLib
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace ASF
|
|
||||||
{
|
|
||||||
|
|
||||||
class File;
|
|
||||||
class Picture;
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Attribute
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Enum of types an Attribute can have.
|
|
||||||
*/
|
|
||||||
enum AttributeTypes {
|
|
||||||
UnicodeType = 0,
|
|
||||||
BytesType = 1,
|
|
||||||
BoolType = 2,
|
|
||||||
DWordType = 3,
|
|
||||||
QWordType = 4,
|
|
||||||
WordType = 5,
|
|
||||||
GuidType = 6
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an empty attribute.
|
|
||||||
*/
|
|
||||||
Attribute();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an attribute with \a key and a UnicodeType \a value.
|
|
||||||
*/
|
|
||||||
Attribute(const String &value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an attribute with \a key and a BytesType \a value.
|
|
||||||
*/
|
|
||||||
Attribute(const ByteVector &value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an attribute with \a key and a Picture \a value.
|
|
||||||
*
|
|
||||||
* This attribute is compatible with the ID3 frame, APIC. The ID3 specification for the APIC frame stipulates that,
|
|
||||||
* while there may be any number of APIC frames associated with a file,
|
|
||||||
* only one may be of type 1 and only one may be of type 2.
|
|
||||||
*
|
|
||||||
* The specification also states that the description of the picture can be no longer than 64 characters, but can be empty.
|
|
||||||
* WM/Picture attributes added with TagLib::ASF are not automatically validated to conform to ID3 specifications.
|
|
||||||
* You must add code in your application to perform validations if you want to maintain complete compatibility with ID3.
|
|
||||||
*/
|
|
||||||
Attribute(const Picture &value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an attribute with \a key and a DWordType \a value.
|
|
||||||
*/
|
|
||||||
Attribute(unsigned int value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an attribute with \a key and a QWordType \a value.
|
|
||||||
*/
|
|
||||||
Attribute(unsigned long long value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an attribute with \a key and a WordType \a value.
|
|
||||||
*/
|
|
||||||
Attribute(unsigned short value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an attribute with \a key and a BoolType \a value.
|
|
||||||
*/
|
|
||||||
Attribute(bool value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Construct an attribute as a copy of \a other.
|
|
||||||
*/
|
|
||||||
Attribute(const Attribute &item);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Copies the contents of \a other into this item.
|
|
||||||
*/
|
|
||||||
Attribute &operator=(const Attribute &other);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Exchanges the content of the Attribute by the content of \a other.
|
|
||||||
*/
|
|
||||||
void swap(Attribute &other);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys the attribute.
|
|
||||||
*/
|
|
||||||
virtual ~Attribute();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns type of the value.
|
|
||||||
*/
|
|
||||||
AttributeTypes type() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the BoolType \a value.
|
|
||||||
*/
|
|
||||||
unsigned short toBool() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the WordType \a value.
|
|
||||||
*/
|
|
||||||
unsigned short toUShort() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the DWordType \a value.
|
|
||||||
*/
|
|
||||||
unsigned int toUInt() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the QWordType \a value.
|
|
||||||
*/
|
|
||||||
unsigned long long toULongLong() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the UnicodeType \a value.
|
|
||||||
*/
|
|
||||||
String toString() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the BytesType \a value.
|
|
||||||
*/
|
|
||||||
ByteVector toByteVector() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the Picture \a value.
|
|
||||||
*/
|
|
||||||
Picture toPicture() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the language number, or 0 is no stream number was set.
|
|
||||||
*/
|
|
||||||
int language() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the language number.
|
|
||||||
*/
|
|
||||||
void setLanguage(int value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the stream number, or 0 is no stream number was set.
|
|
||||||
*/
|
|
||||||
int stream() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the stream number.
|
|
||||||
*/
|
|
||||||
void setStream(int value);
|
|
||||||
|
|
||||||
#ifndef DO_NOT_DOCUMENT
|
|
||||||
/* THIS IS PRIVATE, DON'T TOUCH IT! */
|
|
||||||
String parse(ASF::File &file, int kind = 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//! Returns the size of the stored data
|
|
||||||
int dataSize() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class File;
|
|
||||||
|
|
||||||
ByteVector render(const String &name, int kind = 0) const;
|
|
||||||
|
|
||||||
class AttributePrivate;
|
|
||||||
AttributePrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
705
3rdparty/taglib/asf/asffile.cpp
vendored
705
3rdparty/taglib/asf/asffile.cpp
vendored
@@ -1,705 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <tbytevectorlist.h>
|
|
||||||
#include <tpropertymap.h>
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tagutils.h>
|
|
||||||
|
|
||||||
#include "asffile.h"
|
|
||||||
#include "asftag.h"
|
|
||||||
#include "asfproperties.h"
|
|
||||||
#include "asfutils.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
class BaseObject;
|
|
||||||
class UnknownObject;
|
|
||||||
class FilePropertiesObject;
|
|
||||||
class StreamPropertiesObject;
|
|
||||||
class ContentDescriptionObject;
|
|
||||||
class ExtendedContentDescriptionObject;
|
|
||||||
class HeaderExtensionObject;
|
|
||||||
class CodecListObject;
|
|
||||||
class MetadataObject;
|
|
||||||
class MetadataLibraryObject;
|
|
||||||
|
|
||||||
FilePrivate():
|
|
||||||
headerSize(0),
|
|
||||||
tag(0),
|
|
||||||
properties(0),
|
|
||||||
contentDescriptionObject(0),
|
|
||||||
extendedContentDescriptionObject(0),
|
|
||||||
headerExtensionObject(0),
|
|
||||||
metadataObject(0),
|
|
||||||
metadataLibraryObject(0)
|
|
||||||
{
|
|
||||||
objects.setAutoDelete(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
~FilePrivate()
|
|
||||||
{
|
|
||||||
delete tag;
|
|
||||||
delete properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long headerSize;
|
|
||||||
|
|
||||||
ASF::Tag *tag;
|
|
||||||
ASF::Properties *properties;
|
|
||||||
|
|
||||||
List<BaseObject *> objects;
|
|
||||||
|
|
||||||
ContentDescriptionObject *contentDescriptionObject;
|
|
||||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
|
|
||||||
HeaderExtensionObject *headerExtensionObject;
|
|
||||||
MetadataObject *metadataObject;
|
|
||||||
MetadataLibraryObject *metadataLibraryObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
|
||||||
const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
|
|
||||||
const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
|
|
||||||
const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
|
||||||
const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
|
|
||||||
const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
|
|
||||||
const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
|
|
||||||
const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
|
|
||||||
const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16);
|
|
||||||
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
|
|
||||||
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
|
|
||||||
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ByteVector data;
|
|
||||||
virtual ~BaseObject() {}
|
|
||||||
virtual ByteVector guid() const = 0;
|
|
||||||
virtual void parse(ASF::File *file, unsigned int size);
|
|
||||||
virtual ByteVector render(ASF::File *file);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
ByteVector myGuid;
|
|
||||||
public:
|
|
||||||
UnknownObject(const ByteVector &guid);
|
|
||||||
ByteVector guid() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ByteVector guid() const;
|
|
||||||
void parse(ASF::File *file, unsigned int size);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ByteVector guid() const;
|
|
||||||
void parse(ASF::File *file, unsigned int size);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ByteVector guid() const;
|
|
||||||
void parse(ASF::File *file, unsigned int size);
|
|
||||||
ByteVector render(ASF::File *file);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ByteVectorList attributeData;
|
|
||||||
ByteVector guid() const;
|
|
||||||
void parse(ASF::File *file, unsigned int size);
|
|
||||||
ByteVector render(ASF::File *file);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ByteVectorList attributeData;
|
|
||||||
ByteVector guid() const;
|
|
||||||
void parse(ASF::File *file, unsigned int size);
|
|
||||||
ByteVector render(ASF::File *file);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ByteVectorList attributeData;
|
|
||||||
ByteVector guid() const;
|
|
||||||
void parse(ASF::File *file, unsigned int size);
|
|
||||||
ByteVector render(ASF::File *file);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
List<ASF::File::FilePrivate::BaseObject *> objects;
|
|
||||||
HeaderExtensionObject();
|
|
||||||
ByteVector guid() const;
|
|
||||||
void parse(ASF::File *file, unsigned int size);
|
|
||||||
ByteVector render(ASF::File *file);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ByteVector guid() const;
|
|
||||||
void parse(ASF::File *file, unsigned int size);
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum CodecType
|
|
||||||
{
|
|
||||||
Video = 0x0001,
|
|
||||||
Audio = 0x0002,
|
|
||||||
Unknown = 0xFFFF
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
|
|
||||||
{
|
|
||||||
data.clear();
|
|
||||||
if(size > 24 && size <= (unsigned int)(file->length()))
|
|
||||||
data = file->readBlock(size - 24);
|
|
||||||
else
|
|
||||||
data = ByteVector();
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
|
|
||||||
{
|
|
||||||
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::UnknownObject::guid() const
|
|
||||||
{
|
|
||||||
return myGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
|
|
||||||
{
|
|
||||||
return filePropertiesGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size)
|
|
||||||
{
|
|
||||||
BaseObject::parse(file, size);
|
|
||||||
if(data.size() < 64) {
|
|
||||||
debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const long long duration = data.toLongLong(40, false);
|
|
||||||
const long long preroll = data.toLongLong(56, false);
|
|
||||||
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
|
|
||||||
{
|
|
||||||
return streamPropertiesGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size)
|
|
||||||
{
|
|
||||||
BaseObject::parse(file, size);
|
|
||||||
if(data.size() < 70) {
|
|
||||||
debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
file->d->properties->setCodec(data.toUShort(54, false));
|
|
||||||
file->d->properties->setChannels(data.toUShort(56, false));
|
|
||||||
file->d->properties->setSampleRate(data.toUInt(58, false));
|
|
||||||
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
|
|
||||||
file->d->properties->setBitsPerSample(data.toUShort(68, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
|
||||||
{
|
|
||||||
return contentDescriptionGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
|
||||||
{
|
|
||||||
const int titleLength = readWORD(file);
|
|
||||||
const int artistLength = readWORD(file);
|
|
||||||
const int copyrightLength = readWORD(file);
|
|
||||||
const int commentLength = readWORD(file);
|
|
||||||
const int ratingLength = readWORD(file);
|
|
||||||
file->d->tag->setTitle(readString(file,titleLength));
|
|
||||||
file->d->tag->setArtist(readString(file,artistLength));
|
|
||||||
file->d->tag->setCopyright(readString(file,copyrightLength));
|
|
||||||
file->d->tag->setComment(readString(file,commentLength));
|
|
||||||
file->d->tag->setRating(readString(file,ratingLength));
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file)
|
|
||||||
{
|
|
||||||
const ByteVector v1 = renderString(file->d->tag->title());
|
|
||||||
const ByteVector v2 = renderString(file->d->tag->artist());
|
|
||||||
const ByteVector v3 = renderString(file->d->tag->copyright());
|
|
||||||
const ByteVector v4 = renderString(file->d->tag->comment());
|
|
||||||
const ByteVector v5 = renderString(file->d->tag->rating());
|
|
||||||
data.clear();
|
|
||||||
data.append(ByteVector::fromShort(v1.size(), false));
|
|
||||||
data.append(ByteVector::fromShort(v2.size(), false));
|
|
||||||
data.append(ByteVector::fromShort(v3.size(), false));
|
|
||||||
data.append(ByteVector::fromShort(v4.size(), false));
|
|
||||||
data.append(ByteVector::fromShort(v5.size(), false));
|
|
||||||
data.append(v1);
|
|
||||||
data.append(v2);
|
|
||||||
data.append(v3);
|
|
||||||
data.append(v4);
|
|
||||||
data.append(v5);
|
|
||||||
return BaseObject::render(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const
|
|
||||||
{
|
|
||||||
return extendedContentDescriptionGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
|
||||||
{
|
|
||||||
int count = readWORD(file);
|
|
||||||
while(count--) {
|
|
||||||
ASF::Attribute attribute;
|
|
||||||
String name = attribute.parse(*file);
|
|
||||||
file->d->tag->addAttribute(name, attribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
|
|
||||||
{
|
|
||||||
data.clear();
|
|
||||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
|
||||||
data.append(attributeData.toByteVector(""));
|
|
||||||
return BaseObject::render(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
|
|
||||||
{
|
|
||||||
return metadataGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
|
|
||||||
{
|
|
||||||
int count = readWORD(file);
|
|
||||||
while(count--) {
|
|
||||||
ASF::Attribute attribute;
|
|
||||||
String name = attribute.parse(*file, 1);
|
|
||||||
file->d->tag->addAttribute(name, attribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
|
|
||||||
{
|
|
||||||
data.clear();
|
|
||||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
|
||||||
data.append(attributeData.toByteVector(""));
|
|
||||||
return BaseObject::render(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
|
|
||||||
{
|
|
||||||
return metadataLibraryGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
|
|
||||||
{
|
|
||||||
int count = readWORD(file);
|
|
||||||
while(count--) {
|
|
||||||
ASF::Attribute attribute;
|
|
||||||
String name = attribute.parse(*file, 2);
|
|
||||||
file->d->tag->addAttribute(name, attribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
|
|
||||||
{
|
|
||||||
data.clear();
|
|
||||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
|
||||||
data.append(attributeData.toByteVector(""));
|
|
||||||
return BaseObject::render(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
|
|
||||||
{
|
|
||||||
objects.setAutoDelete(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
|
||||||
{
|
|
||||||
return headerExtensionGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
|
|
||||||
{
|
|
||||||
file->seek(18, File::Current);
|
|
||||||
long long dataSize = readDWORD(file);
|
|
||||||
long long dataPos = 0;
|
|
||||||
while(dataPos < dataSize) {
|
|
||||||
ByteVector guid = file->readBlock(16);
|
|
||||||
if(guid.size() != 16) {
|
|
||||||
file->setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bool ok;
|
|
||||||
long long size = readQWORD(file, &ok);
|
|
||||||
if(!ok) {
|
|
||||||
file->setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
BaseObject *obj;
|
|
||||||
if(guid == metadataGuid) {
|
|
||||||
file->d->metadataObject = new MetadataObject();
|
|
||||||
obj = file->d->metadataObject;
|
|
||||||
}
|
|
||||||
else if(guid == metadataLibraryGuid) {
|
|
||||||
file->d->metadataLibraryObject = new MetadataLibraryObject();
|
|
||||||
obj = file->d->metadataLibraryObject;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
obj = new UnknownObject(guid);
|
|
||||||
}
|
|
||||||
obj->parse(file, (unsigned int)size);
|
|
||||||
objects.append(obj);
|
|
||||||
dataPos += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
|
|
||||||
{
|
|
||||||
data.clear();
|
|
||||||
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
|
|
||||||
data.append((*it)->render(file));
|
|
||||||
}
|
|
||||||
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
|
|
||||||
return BaseObject::render(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
|
|
||||||
{
|
|
||||||
return codecListGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size)
|
|
||||||
{
|
|
||||||
BaseObject::parse(file, size);
|
|
||||||
if(data.size() <= 20) {
|
|
||||||
debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int pos = 16;
|
|
||||||
|
|
||||||
const int count = data.toUInt(pos, false);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
for(int i = 0; i < count; ++i) {
|
|
||||||
|
|
||||||
if(pos >= data.size())
|
|
||||||
break;
|
|
||||||
|
|
||||||
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
int nameLength = data.toUShort(pos, false);
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
const unsigned int namePos = pos;
|
|
||||||
pos += nameLength * 2;
|
|
||||||
|
|
||||||
const int descLength = data.toUShort(pos, false);
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
const unsigned int descPos = pos;
|
|
||||||
pos += descLength * 2;
|
|
||||||
|
|
||||||
const int infoLength = data.toUShort(pos, false);
|
|
||||||
pos += 2 + infoLength * 2;
|
|
||||||
|
|
||||||
if(type == CodecListObject::Audio) {
|
|
||||||
// First audio codec found.
|
|
||||||
|
|
||||||
const String name(data.mid(namePos, nameLength * 2), String::UTF16LE);
|
|
||||||
file->d->properties->setCodecName(name.stripWhiteSpace());
|
|
||||||
|
|
||||||
const String desc(data.mid(descPos, descLength * 2), String::UTF16LE);
|
|
||||||
file->d->properties->setCodecDescription(desc.stripWhiteSpace());
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// static members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool ASF::File::isSupported(IOStream *stream)
|
|
||||||
{
|
|
||||||
// An ASF file has to start with the designated GUID.
|
|
||||||
|
|
||||||
const ByteVector id = Utils::readHeader(stream, 16, false);
|
|
||||||
return (id == headerGuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
|
||||||
TagLib::File(file),
|
|
||||||
d(new FilePrivate())
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
|
|
||||||
TagLib::File(stream),
|
|
||||||
d(new FilePrivate())
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::File::~File()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Tag *ASF::File::tag() const
|
|
||||||
{
|
|
||||||
return d->tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap ASF::File::properties() const
|
|
||||||
{
|
|
||||||
return d->tag->properties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::File::removeUnsupportedProperties(const StringList &properties)
|
|
||||||
{
|
|
||||||
d->tag->removeUnsupportedProperties(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
|
|
||||||
{
|
|
||||||
return d->tag->setProperties(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Properties *ASF::File::audioProperties() const
|
|
||||||
{
|
|
||||||
return d->properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASF::File::save()
|
|
||||||
{
|
|
||||||
if(readOnly()) {
|
|
||||||
debug("ASF::File::save() -- File is read only.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isValid()) {
|
|
||||||
debug("ASF::File::save() -- Trying to save invalid file.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!d->contentDescriptionObject) {
|
|
||||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
|
||||||
d->objects.append(d->contentDescriptionObject);
|
|
||||||
}
|
|
||||||
if(!d->extendedContentDescriptionObject) {
|
|
||||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
|
||||||
d->objects.append(d->extendedContentDescriptionObject);
|
|
||||||
}
|
|
||||||
if(!d->headerExtensionObject) {
|
|
||||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
|
||||||
d->objects.append(d->headerExtensionObject);
|
|
||||||
}
|
|
||||||
if(!d->metadataObject) {
|
|
||||||
d->metadataObject = new FilePrivate::MetadataObject();
|
|
||||||
d->headerExtensionObject->objects.append(d->metadataObject);
|
|
||||||
}
|
|
||||||
if(!d->metadataLibraryObject) {
|
|
||||||
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
|
|
||||||
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
d->extendedContentDescriptionObject->attributeData.clear();
|
|
||||||
d->metadataObject->attributeData.clear();
|
|
||||||
d->metadataLibraryObject->attributeData.clear();
|
|
||||||
|
|
||||||
const AttributeListMap allAttributes = d->tag->attributeListMap();
|
|
||||||
|
|
||||||
for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) {
|
|
||||||
|
|
||||||
const String &name = it->first;
|
|
||||||
const AttributeList &attributes = it->second;
|
|
||||||
|
|
||||||
bool inExtendedContentDescriptionObject = false;
|
|
||||||
bool inMetadataObject = false;
|
|
||||||
|
|
||||||
for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
|
|
||||||
|
|
||||||
const Attribute &attribute = *jt;
|
|
||||||
const bool largeValue = (attribute.dataSize() > 65535);
|
|
||||||
const bool guid = (attribute.type() == Attribute::GuidType);
|
|
||||||
|
|
||||||
if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
|
|
||||||
d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
|
|
||||||
inExtendedContentDescriptionObject = true;
|
|
||||||
}
|
|
||||||
else if(!inMetadataObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
|
|
||||||
d->metadataObject->attributeData.append(attribute.render(name, 1));
|
|
||||||
inMetadataObject = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
d->metadataLibraryObject->attributeData.append(attribute.render(name, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector data;
|
|
||||||
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
|
|
||||||
data.append((*it)->render(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
seek(16);
|
|
||||||
writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
|
|
||||||
writeBlock(ByteVector::fromUInt(d->objects.size(), false));
|
|
||||||
writeBlock(ByteVector("\x01\x02", 2));
|
|
||||||
|
|
||||||
insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
|
|
||||||
|
|
||||||
d->headerSize = data.size() + 30;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void ASF::File::read()
|
|
||||||
{
|
|
||||||
if(!isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(readBlock(16) != headerGuid) {
|
|
||||||
debug("ASF::File::read(): Not an ASF file.");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->tag = new ASF::Tag();
|
|
||||||
d->properties = new ASF::Properties();
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
d->headerSize = readQWORD(this, &ok);
|
|
||||||
if(!ok) {
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int numObjects = readDWORD(this, &ok);
|
|
||||||
if(!ok) {
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
seek(2, Current);
|
|
||||||
|
|
||||||
FilePrivate::FilePropertiesObject *filePropertiesObject = 0;
|
|
||||||
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
|
|
||||||
for(int i = 0; i < numObjects; i++) {
|
|
||||||
const ByteVector guid = readBlock(16);
|
|
||||||
if(guid.size() != 16) {
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
long size = (long)readQWORD(this, &ok);
|
|
||||||
if(!ok) {
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
FilePrivate::BaseObject *obj;
|
|
||||||
if(guid == filePropertiesGuid) {
|
|
||||||
filePropertiesObject = new FilePrivate::FilePropertiesObject();
|
|
||||||
obj = filePropertiesObject;
|
|
||||||
}
|
|
||||||
else if(guid == streamPropertiesGuid) {
|
|
||||||
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
|
|
||||||
obj = streamPropertiesObject;
|
|
||||||
}
|
|
||||||
else if(guid == contentDescriptionGuid) {
|
|
||||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
|
||||||
obj = d->contentDescriptionObject;
|
|
||||||
}
|
|
||||||
else if(guid == extendedContentDescriptionGuid) {
|
|
||||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
|
||||||
obj = d->extendedContentDescriptionObject;
|
|
||||||
}
|
|
||||||
else if(guid == headerExtensionGuid) {
|
|
||||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
|
||||||
obj = d->headerExtensionObject;
|
|
||||||
}
|
|
||||||
else if(guid == codecListGuid) {
|
|
||||||
obj = new FilePrivate::CodecListObject();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(guid == contentEncryptionGuid ||
|
|
||||||
guid == extendedContentEncryptionGuid ||
|
|
||||||
guid == advancedContentEncryptionGuid) {
|
|
||||||
d->properties->setEncrypted(true);
|
|
||||||
}
|
|
||||||
obj = new FilePrivate::UnknownObject(guid);
|
|
||||||
}
|
|
||||||
obj->parse(this, size);
|
|
||||||
d->objects.append(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!filePropertiesObject || !streamPropertiesObject) {
|
|
||||||
debug("ASF::File::read(): Missing mandatory header objects.");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
138
3rdparty/taglib/asf/asffile.h
vendored
138
3rdparty/taglib/asf/asffile.h
vendored
@@ -1,138 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_ASFFILE_H
|
|
||||||
#define TAGLIB_ASFFILE_H
|
|
||||||
|
|
||||||
#include "tag.h"
|
|
||||||
#include "tfile.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "asfproperties.h"
|
|
||||||
#include "asftag.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
//! An implementation of ASF (WMA) metadata
|
|
||||||
namespace ASF {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This implements and provides an interface for ASF files to the
|
|
||||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
|
||||||
* the abstract TagLib::File API as well as providing some additional
|
|
||||||
* information specific to ASF files.
|
|
||||||
*/
|
|
||||||
class TAGLIB_EXPORT File : public TagLib::File
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an ASF file from \a file.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, both \a readProperties and
|
|
||||||
* \a propertiesStyle are ignored. The audio properties are always
|
|
||||||
* read.
|
|
||||||
*/
|
|
||||||
File(FileName file, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an ASF file from \a stream.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, both \a readProperties and
|
|
||||||
* \a propertiesStyle are ignored. The audio properties are always
|
|
||||||
* read.
|
|
||||||
*
|
|
||||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
|
||||||
* responsible for deleting it after the File object.
|
|
||||||
*/
|
|
||||||
File(IOStream *stream, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this instance of the File.
|
|
||||||
*/
|
|
||||||
virtual ~File();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to the ASF tag of the file.
|
|
||||||
*
|
|
||||||
* ASF::Tag implements the tag interface, so this serves as the
|
|
||||||
* reimplementation of TagLib::File::tag().
|
|
||||||
*
|
|
||||||
* \note The Tag <b>is still</b> owned by the ASF::File and should not be
|
|
||||||
* deleted by the user. It will be deleted when the file (object) is
|
|
||||||
* destroyed.
|
|
||||||
*/
|
|
||||||
virtual Tag *tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- export function.
|
|
||||||
*/
|
|
||||||
PropertyMap properties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Removes unsupported properties. Forwards to the actual Tag's
|
|
||||||
* removeUnsupportedProperties() function.
|
|
||||||
*/
|
|
||||||
void removeUnsupportedProperties(const StringList &properties);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- import function.
|
|
||||||
*/
|
|
||||||
PropertyMap setProperties(const PropertyMap &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the ASF audio properties for this file.
|
|
||||||
*/
|
|
||||||
virtual Properties *audioProperties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Save the file.
|
|
||||||
*
|
|
||||||
* This returns true if the save was successful.
|
|
||||||
*/
|
|
||||||
virtual bool save();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the given \a stream can be opened as an ASF
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* \note This method is designed to do a quick check. The result may
|
|
||||||
* not necessarily be correct.
|
|
||||||
*/
|
|
||||||
static bool isSupported(IOStream *stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void read();
|
|
||||||
|
|
||||||
class FilePrivate;
|
|
||||||
FilePrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
183
3rdparty/taglib/asf/asfpicture.cpp
vendored
183
3rdparty/taglib/asf/asfpicture.cpp
vendored
@@ -1,183 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2010 by Anton Sergunov
|
|
||||||
email : setosha@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <taglib.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <trefcounter.h>
|
|
||||||
|
|
||||||
#include "asfattribute.h"
|
|
||||||
#include "asffile.h"
|
|
||||||
#include "asfpicture.h"
|
|
||||||
#include "asfutils.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class ASF::Picture::PicturePrivate : public RefCounter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool valid;
|
|
||||||
Type type;
|
|
||||||
String mimeType;
|
|
||||||
String description;
|
|
||||||
ByteVector picture;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Picture class members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ASF::Picture::Picture() :
|
|
||||||
d(new PicturePrivate())
|
|
||||||
{
|
|
||||||
d->valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Picture::Picture(const Picture& other) :
|
|
||||||
d(other.d)
|
|
||||||
{
|
|
||||||
d->ref();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Picture::~Picture()
|
|
||||||
{
|
|
||||||
if(d->deref())
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASF::Picture::isValid() const
|
|
||||||
{
|
|
||||||
return d->valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Picture::mimeType() const
|
|
||||||
{
|
|
||||||
return d->mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Picture::setMimeType(const String &value)
|
|
||||||
{
|
|
||||||
d->mimeType = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Picture::Type ASF::Picture::type() const
|
|
||||||
{
|
|
||||||
return d->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Picture::setType(const ASF::Picture::Type& t)
|
|
||||||
{
|
|
||||||
d->type = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Picture::description() const
|
|
||||||
{
|
|
||||||
return d->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Picture::setDescription(const String &desc)
|
|
||||||
{
|
|
||||||
d->description = desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::Picture::picture() const
|
|
||||||
{
|
|
||||||
return d->picture;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Picture::setPicture(const ByteVector &p)
|
|
||||||
{
|
|
||||||
d->picture = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Picture::dataSize() const
|
|
||||||
{
|
|
||||||
return
|
|
||||||
9 + (d->mimeType.length() + d->description.length()) * 2 +
|
|
||||||
d->picture.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
|
|
||||||
{
|
|
||||||
Picture(other).swap(*this);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Picture::swap(Picture &other)
|
|
||||||
{
|
|
||||||
using std::swap;
|
|
||||||
|
|
||||||
swap(d, other.d);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector ASF::Picture::render() const
|
|
||||||
{
|
|
||||||
if(!isValid())
|
|
||||||
return ByteVector();
|
|
||||||
|
|
||||||
return
|
|
||||||
ByteVector((char)d->type) +
|
|
||||||
ByteVector::fromUInt(d->picture.size(), false) +
|
|
||||||
renderString(d->mimeType) +
|
|
||||||
renderString(d->description) +
|
|
||||||
d->picture;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Picture::parse(const ByteVector& bytes)
|
|
||||||
{
|
|
||||||
d->valid = false;
|
|
||||||
if(bytes.size() < 9)
|
|
||||||
return;
|
|
||||||
int pos = 0;
|
|
||||||
d->type = (Type)bytes[0]; ++pos;
|
|
||||||
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
|
|
||||||
|
|
||||||
const ByteVector nullStringTerminator(2, 0);
|
|
||||||
|
|
||||||
int endPos = bytes.find(nullStringTerminator, pos, 2);
|
|
||||||
if(endPos < 0)
|
|
||||||
return;
|
|
||||||
d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
|
||||||
pos = endPos+2;
|
|
||||||
|
|
||||||
endPos = bytes.find(nullStringTerminator, pos, 2);
|
|
||||||
if(endPos < 0)
|
|
||||||
return;
|
|
||||||
d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
|
||||||
pos = endPos+2;
|
|
||||||
|
|
||||||
if(dataLen + pos != bytes.size())
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->picture = bytes.mid(pos, dataLen);
|
|
||||||
d->valid = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Picture ASF::Picture::fromInvalid()
|
|
||||||
{
|
|
||||||
Picture ret;
|
|
||||||
ret.d->valid = false;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
222
3rdparty/taglib/asf/asfpicture.h
vendored
222
3rdparty/taglib/asf/asfpicture.h
vendored
@@ -1,222 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2010 by Anton Sergunov
|
|
||||||
email : setosha@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef ASFPICTURE_H
|
|
||||||
#define ASFPICTURE_H
|
|
||||||
|
|
||||||
#include "tstring.h"
|
|
||||||
#include "tbytevector.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "attachedpictureframe.h"
|
|
||||||
|
|
||||||
namespace TagLib
|
|
||||||
{
|
|
||||||
namespace ASF
|
|
||||||
{
|
|
||||||
|
|
||||||
//! An ASF attached picture interface implementation
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This is an implementation of ASF attached pictures interface. Pictures may be
|
|
||||||
* included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture
|
|
||||||
* attribute in a single tag). These pictures are usually in either JPEG or
|
|
||||||
* PNG format.
|
|
||||||
* \see Attribute::toPicture()
|
|
||||||
* \see Attribute::Attribute(const Picture& picture)
|
|
||||||
*/
|
|
||||||
class TAGLIB_EXPORT Picture {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This describes the function or content of the picture.
|
|
||||||
*/
|
|
||||||
enum Type {
|
|
||||||
//! A type not enumerated below
|
|
||||||
Other = 0x00,
|
|
||||||
//! 32x32 PNG image that should be used as the file icon
|
|
||||||
FileIcon = 0x01,
|
|
||||||
//! File icon of a different size or format
|
|
||||||
OtherFileIcon = 0x02,
|
|
||||||
//! Front cover image of the album
|
|
||||||
FrontCover = 0x03,
|
|
||||||
//! Back cover image of the album
|
|
||||||
BackCover = 0x04,
|
|
||||||
//! Inside leaflet page of the album
|
|
||||||
LeafletPage = 0x05,
|
|
||||||
//! Image from the album itself
|
|
||||||
Media = 0x06,
|
|
||||||
//! Picture of the lead artist or soloist
|
|
||||||
LeadArtist = 0x07,
|
|
||||||
//! Picture of the artist or performer
|
|
||||||
Artist = 0x08,
|
|
||||||
//! Picture of the conductor
|
|
||||||
Conductor = 0x09,
|
|
||||||
//! Picture of the band or orchestra
|
|
||||||
Band = 0x0A,
|
|
||||||
//! Picture of the composer
|
|
||||||
Composer = 0x0B,
|
|
||||||
//! Picture of the lyricist or text writer
|
|
||||||
Lyricist = 0x0C,
|
|
||||||
//! Picture of the recording location or studio
|
|
||||||
RecordingLocation = 0x0D,
|
|
||||||
//! Picture of the artists during recording
|
|
||||||
DuringRecording = 0x0E,
|
|
||||||
//! Picture of the artists during performance
|
|
||||||
DuringPerformance = 0x0F,
|
|
||||||
//! Picture from a movie or video related to the track
|
|
||||||
MovieScreenCapture = 0x10,
|
|
||||||
//! Picture of a large, coloured fish
|
|
||||||
ColouredFish = 0x11,
|
|
||||||
//! Illustration related to the track
|
|
||||||
Illustration = 0x12,
|
|
||||||
//! Logo of the band or performer
|
|
||||||
BandLogo = 0x13,
|
|
||||||
//! Logo of the publisher (record company)
|
|
||||||
PublisherLogo = 0x14
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an empty picture.
|
|
||||||
*/
|
|
||||||
Picture();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Construct an picture as a copy of \a other.
|
|
||||||
*/
|
|
||||||
Picture(const Picture& other);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys the picture.
|
|
||||||
*/
|
|
||||||
virtual ~Picture();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Copies the contents of \a other into this picture.
|
|
||||||
*/
|
|
||||||
Picture& operator=(const Picture& other);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Exchanges the content of the Picture by the content of \a other.
|
|
||||||
*/
|
|
||||||
void swap(Picture &other);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if Picture stores valid picture
|
|
||||||
*/
|
|
||||||
bool isValid() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the mime type of the image. This should in most cases be
|
|
||||||
* "image/png" or "image/jpeg".
|
|
||||||
* \see setMimeType(const String &)
|
|
||||||
* \see picture()
|
|
||||||
* \see setPicture(const ByteArray&)
|
|
||||||
*/
|
|
||||||
String mimeType() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the mime type of the image. This should in most cases be
|
|
||||||
* "image/png" or "image/jpeg".
|
|
||||||
* \see setMimeType(const String &)
|
|
||||||
* \see picture()
|
|
||||||
* \see setPicture(const ByteArray&)
|
|
||||||
*/
|
|
||||||
void setMimeType(const String &value);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the type of the image.
|
|
||||||
*
|
|
||||||
* \see Type
|
|
||||||
* \see setType()
|
|
||||||
*/
|
|
||||||
Type type() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the type for the image.
|
|
||||||
*
|
|
||||||
* \see Type
|
|
||||||
* \see type()
|
|
||||||
*/
|
|
||||||
void setType(const ASF::Picture::Type& t);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a text description of the image.
|
|
||||||
*
|
|
||||||
* \see setDescription()
|
|
||||||
*/
|
|
||||||
String description() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets a textual description of the image to \a desc.
|
|
||||||
*
|
|
||||||
* \see description()
|
|
||||||
*/
|
|
||||||
void setDescription(const String &desc);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the image data as a ByteVector.
|
|
||||||
*
|
|
||||||
* \note ByteVector has a data() method that returns a const char * which
|
|
||||||
* should make it easy to export this data to external programs.
|
|
||||||
*
|
|
||||||
* \see setPicture()
|
|
||||||
* \see mimeType()
|
|
||||||
*/
|
|
||||||
ByteVector picture() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the image data to \a p. \a p should be of the type specified in
|
|
||||||
* this frame's mime-type specification.
|
|
||||||
*
|
|
||||||
* \see picture()
|
|
||||||
* \see mimeType()
|
|
||||||
* \see setMimeType()
|
|
||||||
*/
|
|
||||||
void setPicture(const ByteVector &p);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns picture as binary raw data \a value
|
|
||||||
*/
|
|
||||||
ByteVector render() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns picture as binary raw data \a value
|
|
||||||
*/
|
|
||||||
int dataSize() const;
|
|
||||||
|
|
||||||
#ifndef DO_NOT_DOCUMENT
|
|
||||||
/* THIS IS PRIVATE, DON'T TOUCH IT! */
|
|
||||||
void parse(const ByteVector& );
|
|
||||||
static Picture fromInvalid();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
class PicturePrivate;
|
|
||||||
PicturePrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ASFPICTURE_H
|
|
||||||
194
3rdparty/taglib/asf/asfproperties.cpp
vendored
194
3rdparty/taglib/asf/asfproperties.cpp
vendored
@@ -1,194 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <tstring.h>
|
|
||||||
#include "asfproperties.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class ASF::Properties::PropertiesPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PropertiesPrivate() :
|
|
||||||
length(0),
|
|
||||||
bitrate(0),
|
|
||||||
sampleRate(0),
|
|
||||||
channels(0),
|
|
||||||
bitsPerSample(0),
|
|
||||||
codec(ASF::Properties::Unknown),
|
|
||||||
encrypted(false) {}
|
|
||||||
|
|
||||||
int length;
|
|
||||||
int bitrate;
|
|
||||||
int sampleRate;
|
|
||||||
int channels;
|
|
||||||
int bitsPerSample;
|
|
||||||
ASF::Properties::Codec codec;
|
|
||||||
String codecName;
|
|
||||||
String codecDescription;
|
|
||||||
bool encrypted;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ASF::Properties::Properties() :
|
|
||||||
AudioProperties(AudioProperties::Average),
|
|
||||||
d(new PropertiesPrivate())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Properties::~Properties()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Properties::length() const
|
|
||||||
{
|
|
||||||
return lengthInSeconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Properties::lengthInSeconds() const
|
|
||||||
{
|
|
||||||
return d->length / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Properties::lengthInMilliseconds() const
|
|
||||||
{
|
|
||||||
return d->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Properties::bitrate() const
|
|
||||||
{
|
|
||||||
return d->bitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Properties::sampleRate() const
|
|
||||||
{
|
|
||||||
return d->sampleRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Properties::channels() const
|
|
||||||
{
|
|
||||||
return d->channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ASF::Properties::bitsPerSample() const
|
|
||||||
{
|
|
||||||
return d->bitsPerSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Properties::Codec ASF::Properties::codec() const
|
|
||||||
{
|
|
||||||
return d->codec;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Properties::codecName() const
|
|
||||||
{
|
|
||||||
return d->codecName;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Properties::codecDescription() const
|
|
||||||
{
|
|
||||||
return d->codecDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASF::Properties::isEncrypted() const
|
|
||||||
{
|
|
||||||
return d->encrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void ASF::Properties::setLength(int /*length*/)
|
|
||||||
{
|
|
||||||
debug("ASF::Properties::setLength() -- This method is deprecated. Do not use.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setLengthInMilliseconds(int value)
|
|
||||||
{
|
|
||||||
d->length = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setBitrate(int value)
|
|
||||||
{
|
|
||||||
d->bitrate = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setSampleRate(int value)
|
|
||||||
{
|
|
||||||
d->sampleRate = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setChannels(int value)
|
|
||||||
{
|
|
||||||
d->channels = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setBitsPerSample(int value)
|
|
||||||
{
|
|
||||||
d->bitsPerSample = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setCodec(int value)
|
|
||||||
{
|
|
||||||
switch(value)
|
|
||||||
{
|
|
||||||
case 0x0160:
|
|
||||||
d->codec = WMA1;
|
|
||||||
break;
|
|
||||||
case 0x0161:
|
|
||||||
d->codec = WMA2;
|
|
||||||
break;
|
|
||||||
case 0x0162:
|
|
||||||
d->codec = WMA9Pro;
|
|
||||||
break;
|
|
||||||
case 0x0163:
|
|
||||||
d->codec = WMA9Lossless;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
d->codec = Unknown;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setCodecName(const String &value)
|
|
||||||
{
|
|
||||||
d->codecName = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setCodecDescription(const String &value)
|
|
||||||
{
|
|
||||||
d->codecDescription = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Properties::setEncrypted(bool value)
|
|
||||||
{
|
|
||||||
d->encrypted = value;
|
|
||||||
}
|
|
||||||
186
3rdparty/taglib/asf/asfproperties.h
vendored
186
3rdparty/taglib/asf/asfproperties.h
vendored
@@ -1,186 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_ASFPROPERTIES_H
|
|
||||||
#define TAGLIB_ASFPROPERTIES_H
|
|
||||||
|
|
||||||
#include "audioproperties.h"
|
|
||||||
#include "tstring.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace ASF {
|
|
||||||
|
|
||||||
//! An implementation of ASF audio properties
|
|
||||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Audio codec types can be used in ASF file.
|
|
||||||
*/
|
|
||||||
enum Codec
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
* Couldn't detect the codec.
|
|
||||||
*/
|
|
||||||
Unknown = 0,
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Windows Media Audio 1
|
|
||||||
*/
|
|
||||||
WMA1,
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Windows Media Audio 2 or above
|
|
||||||
*/
|
|
||||||
WMA2,
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Windows Media Audio 9 Professional
|
|
||||||
*/
|
|
||||||
WMA9Pro,
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Windows Media Audio 9 Lossless
|
|
||||||
*/
|
|
||||||
WMA9Lossless,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Creates an instance of ASF::Properties.
|
|
||||||
*/
|
|
||||||
Properties();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this ASF::Properties instance.
|
|
||||||
*/
|
|
||||||
virtual ~Properties();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in seconds. The length is rounded down to
|
|
||||||
* the nearest whole second.
|
|
||||||
*
|
|
||||||
* \note This method is just an alias of lengthInSeconds().
|
|
||||||
*
|
|
||||||
* \deprecated
|
|
||||||
*/
|
|
||||||
virtual int length() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in seconds. The length is rounded down to
|
|
||||||
* the nearest whole second.
|
|
||||||
*
|
|
||||||
* \see lengthInMilliseconds()
|
|
||||||
*/
|
|
||||||
// BIC: make virtual
|
|
||||||
int lengthInSeconds() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in milliseconds.
|
|
||||||
*
|
|
||||||
* \see lengthInSeconds()
|
|
||||||
*/
|
|
||||||
// BIC: make virtual
|
|
||||||
int lengthInMilliseconds() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the average bit rate of the file in kb/s.
|
|
||||||
*/
|
|
||||||
virtual int bitrate() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the sample rate in Hz.
|
|
||||||
*/
|
|
||||||
virtual int sampleRate() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of audio channels.
|
|
||||||
*/
|
|
||||||
virtual int channels() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of bits per audio sample.
|
|
||||||
*/
|
|
||||||
int bitsPerSample() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the codec used in the file.
|
|
||||||
*
|
|
||||||
* \see codecName()
|
|
||||||
* \see codecDescription()
|
|
||||||
*/
|
|
||||||
Codec codec() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the concrete codec name, for example "Windows Media Audio 9.1"
|
|
||||||
* used in the file if available, otherwise an empty string.
|
|
||||||
*
|
|
||||||
* \see codec()
|
|
||||||
* \see codecDescription()
|
|
||||||
*/
|
|
||||||
String codecName() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the codec description, typically contains the encoder settings,
|
|
||||||
* for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available,
|
|
||||||
* otherwise an empty string.
|
|
||||||
*
|
|
||||||
* \see codec()
|
|
||||||
* \see codecName()
|
|
||||||
*/
|
|
||||||
String codecDescription() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the file is encrypted.
|
|
||||||
*/
|
|
||||||
bool isEncrypted() const;
|
|
||||||
|
|
||||||
#ifndef DO_NOT_DOCUMENT
|
|
||||||
// deprecated
|
|
||||||
void setLength(int value);
|
|
||||||
|
|
||||||
void setLengthInMilliseconds(int value);
|
|
||||||
void setBitrate(int value);
|
|
||||||
void setSampleRate(int value);
|
|
||||||
void setChannels(int value);
|
|
||||||
void setBitsPerSample(int value);
|
|
||||||
void setCodec(int value);
|
|
||||||
void setCodecName(const String &value);
|
|
||||||
void setCodecDescription(const String &value);
|
|
||||||
void setEncrypted(bool value);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
class PropertiesPrivate;
|
|
||||||
PropertiesPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
378
3rdparty/taglib/asf/asftag.cpp
vendored
378
3rdparty/taglib/asf/asftag.cpp
vendored
@@ -1,378 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tpropertymap.h>
|
|
||||||
#include "asftag.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class ASF::Tag::TagPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
String title;
|
|
||||||
String artist;
|
|
||||||
String copyright;
|
|
||||||
String comment;
|
|
||||||
String rating;
|
|
||||||
AttributeListMap attributeListMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
ASF::Tag::Tag() :
|
|
||||||
TagLib::Tag(),
|
|
||||||
d(new TagPrivate())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::Tag::~Tag()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Tag::title() const
|
|
||||||
{
|
|
||||||
return d->title;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Tag::artist() const
|
|
||||||
{
|
|
||||||
return d->artist;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Tag::album() const
|
|
||||||
{
|
|
||||||
if(d->attributeListMap.contains("WM/AlbumTitle"))
|
|
||||||
return d->attributeListMap["WM/AlbumTitle"][0].toString();
|
|
||||||
return String();
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Tag::copyright() const
|
|
||||||
{
|
|
||||||
return d->copyright;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Tag::comment() const
|
|
||||||
{
|
|
||||||
return d->comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Tag::rating() const
|
|
||||||
{
|
|
||||||
return d->rating;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int ASF::Tag::year() const
|
|
||||||
{
|
|
||||||
if(d->attributeListMap.contains("WM/Year"))
|
|
||||||
return d->attributeListMap["WM/Year"][0].toString().toInt();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int ASF::Tag::track() const
|
|
||||||
{
|
|
||||||
if(d->attributeListMap.contains("WM/TrackNumber")) {
|
|
||||||
const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0];
|
|
||||||
if(attr.type() == ASF::Attribute::DWordType)
|
|
||||||
return attr.toUInt();
|
|
||||||
else
|
|
||||||
return attr.toString().toInt();
|
|
||||||
}
|
|
||||||
if(d->attributeListMap.contains("WM/Track"))
|
|
||||||
return d->attributeListMap["WM/Track"][0].toUInt();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ASF::Tag::genre() const
|
|
||||||
{
|
|
||||||
if(d->attributeListMap.contains("WM/Genre"))
|
|
||||||
return d->attributeListMap["WM/Genre"][0].toString();
|
|
||||||
return String();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setTitle(const String &value)
|
|
||||||
{
|
|
||||||
d->title = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setArtist(const String &value)
|
|
||||||
{
|
|
||||||
d->artist = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setCopyright(const String &value)
|
|
||||||
{
|
|
||||||
d->copyright = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setComment(const String &value)
|
|
||||||
{
|
|
||||||
d->comment = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setRating(const String &value)
|
|
||||||
{
|
|
||||||
d->rating = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setAlbum(const String &value)
|
|
||||||
{
|
|
||||||
setAttribute("WM/AlbumTitle", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setGenre(const String &value)
|
|
||||||
{
|
|
||||||
setAttribute("WM/Genre", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setYear(unsigned int value)
|
|
||||||
{
|
|
||||||
setAttribute("WM/Year", String::number(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setTrack(unsigned int value)
|
|
||||||
{
|
|
||||||
setAttribute("WM/TrackNumber", String::number(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::AttributeListMap& ASF::Tag::attributeListMap()
|
|
||||||
{
|
|
||||||
return d->attributeListMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ASF::AttributeListMap &ASF::Tag::attributeListMap() const
|
|
||||||
{
|
|
||||||
return d->attributeListMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASF::Tag::contains(const String &key) const
|
|
||||||
{
|
|
||||||
return d->attributeListMap.contains(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::removeItem(const String &key)
|
|
||||||
{
|
|
||||||
d->attributeListMap.erase(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::AttributeList ASF::Tag::attribute(const String &name) const
|
|
||||||
{
|
|
||||||
return d->attributeListMap[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
|
|
||||||
{
|
|
||||||
AttributeList value;
|
|
||||||
value.append(attribute);
|
|
||||||
d->attributeListMap.insert(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::setAttribute(const String &name, const AttributeList &values)
|
|
||||||
{
|
|
||||||
d->attributeListMap.insert(name, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::addAttribute(const String &name, const Attribute &attribute)
|
|
||||||
{
|
|
||||||
if(d->attributeListMap.contains(name)) {
|
|
||||||
d->attributeListMap[name].append(attribute);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setAttribute(name, attribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ASF::Tag::isEmpty() const
|
|
||||||
{
|
|
||||||
return TagLib::Tag::isEmpty() &&
|
|
||||||
copyright().isEmpty() &&
|
|
||||||
rating().isEmpty() &&
|
|
||||||
d->attributeListMap.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const char *keyTranslation[][2] = {
|
|
||||||
{ "WM/AlbumTitle", "ALBUM" },
|
|
||||||
{ "WM/AlbumArtist", "ALBUMARTIST" },
|
|
||||||
{ "WM/Composer", "COMPOSER" },
|
|
||||||
{ "WM/Writer", "WRITER" },
|
|
||||||
{ "WM/Conductor", "CONDUCTOR" },
|
|
||||||
{ "WM/ModifiedBy", "REMIXER" },
|
|
||||||
{ "WM/Year", "DATE" },
|
|
||||||
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
|
|
||||||
{ "WM/Producer", "PRODUCER" },
|
|
||||||
{ "WM/ContentGroupDescription", "GROUPING" },
|
|
||||||
{ "WM/SubTitle", "SUBTITLE" },
|
|
||||||
{ "WM/SetSubTitle", "DISCSUBTITLE" },
|
|
||||||
{ "WM/TrackNumber", "TRACKNUMBER" },
|
|
||||||
{ "WM/PartOfSet", "DISCNUMBER" },
|
|
||||||
{ "WM/Genre", "GENRE" },
|
|
||||||
{ "WM/BeatsPerMinute", "BPM" },
|
|
||||||
{ "WM/Mood", "MOOD" },
|
|
||||||
{ "WM/ISRC", "ISRC" },
|
|
||||||
{ "WM/Lyrics", "LYRICS" },
|
|
||||||
{ "WM/Media", "MEDIA" },
|
|
||||||
{ "WM/Publisher", "LABEL" },
|
|
||||||
{ "WM/CatalogNo", "CATALOGNUMBER" },
|
|
||||||
{ "WM/Barcode", "BARCODE" },
|
|
||||||
{ "WM/EncodedBy", "ENCODEDBY" },
|
|
||||||
{ "WM/AlbumSortOrder", "ALBUMSORT" },
|
|
||||||
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
|
|
||||||
{ "WM/ArtistSortOrder", "ARTISTSORT" },
|
|
||||||
{ "WM/TitleSortOrder", "TITLESORT" },
|
|
||||||
{ "WM/Script", "SCRIPT" },
|
|
||||||
{ "WM/Language", "LANGUAGE" },
|
|
||||||
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
|
|
||||||
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
|
|
||||||
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
|
|
||||||
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
|
||||||
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
|
||||||
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
|
|
||||||
{ "MusicIP/PUID", "MUSICIP_PUID" },
|
|
||||||
{ "Acoustid/Id", "ACOUSTID_ID" },
|
|
||||||
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
|
|
||||||
};
|
|
||||||
const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
|
||||||
|
|
||||||
String translateKey(const String &key)
|
|
||||||
{
|
|
||||||
for(size_t i = 0; i < keyTranslationSize; ++i) {
|
|
||||||
if(key == keyTranslation[i][0])
|
|
||||||
return keyTranslation[i][1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return String();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap ASF::Tag::properties() const
|
|
||||||
{
|
|
||||||
PropertyMap props;
|
|
||||||
|
|
||||||
if(!d->title.isEmpty()) {
|
|
||||||
props["TITLE"] = d->title;
|
|
||||||
}
|
|
||||||
if(!d->artist.isEmpty()) {
|
|
||||||
props["ARTIST"] = d->artist;
|
|
||||||
}
|
|
||||||
if(!d->copyright.isEmpty()) {
|
|
||||||
props["COPYRIGHT"] = d->copyright;
|
|
||||||
}
|
|
||||||
if(!d->comment.isEmpty()) {
|
|
||||||
props["COMMENT"] = d->comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
|
|
||||||
for(; it != d->attributeListMap.end(); ++it) {
|
|
||||||
const String key = translateKey(it->first);
|
|
||||||
if(!key.isEmpty()) {
|
|
||||||
AttributeList::ConstIterator it2 = it->second.begin();
|
|
||||||
for(; it2 != it->second.end(); ++it2) {
|
|
||||||
if(key == "TRACKNUMBER") {
|
|
||||||
if(it2->type() == ASF::Attribute::DWordType)
|
|
||||||
props.insert(key, String::number(it2->toUInt()));
|
|
||||||
else
|
|
||||||
props.insert(key, it2->toString());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
props.insert(key, it2->toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
props.unsupportedData().append(it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
|
|
||||||
{
|
|
||||||
StringList::ConstIterator it = props.begin();
|
|
||||||
for(; it != props.end(); ++it)
|
|
||||||
d->attributeListMap.erase(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
|
||||||
{
|
|
||||||
static Map<String, String> reverseKeyMap;
|
|
||||||
if(reverseKeyMap.isEmpty()) {
|
|
||||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
|
||||||
for(int i = 0; i < numKeys; i++) {
|
|
||||||
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap origProps = properties();
|
|
||||||
PropertyMap::ConstIterator it = origProps.begin();
|
|
||||||
for(; it != origProps.end(); ++it) {
|
|
||||||
if(!props.contains(it->first) || props[it->first].isEmpty()) {
|
|
||||||
if(it->first == "TITLE") {
|
|
||||||
d->title.clear();
|
|
||||||
}
|
|
||||||
else if(it->first == "ARTIST") {
|
|
||||||
d->artist.clear();
|
|
||||||
}
|
|
||||||
else if(it->first == "COMMENT") {
|
|
||||||
d->comment.clear();
|
|
||||||
}
|
|
||||||
else if(it->first == "COPYRIGHT") {
|
|
||||||
d->copyright.clear();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
d->attributeListMap.erase(reverseKeyMap[it->first]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap ignoredProps;
|
|
||||||
it = props.begin();
|
|
||||||
for(; it != props.end(); ++it) {
|
|
||||||
if(reverseKeyMap.contains(it->first)) {
|
|
||||||
String name = reverseKeyMap[it->first];
|
|
||||||
removeItem(name);
|
|
||||||
StringList::ConstIterator it2 = it->second.begin();
|
|
||||||
for(; it2 != it->second.end(); ++it2) {
|
|
||||||
addAttribute(name, *it2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(it->first == "TITLE") {
|
|
||||||
d->title = it->second.toString();
|
|
||||||
}
|
|
||||||
else if(it->first == "ARTIST") {
|
|
||||||
d->artist = it->second.toString();
|
|
||||||
}
|
|
||||||
else if(it->first == "COMMENT") {
|
|
||||||
d->comment = it->second.toString();
|
|
||||||
}
|
|
||||||
else if(it->first == "COPYRIGHT") {
|
|
||||||
d->copyright = it->second.toString();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ignoredProps.insert(it->first, it->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ignoredProps;
|
|
||||||
}
|
|
||||||
209
3rdparty/taglib/asf/asftag.h
vendored
209
3rdparty/taglib/asf/asftag.h
vendored
@@ -1,209 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_ASFTAG_H
|
|
||||||
#define TAGLIB_ASFTAG_H
|
|
||||||
|
|
||||||
#include "tag.h"
|
|
||||||
#include "tlist.h"
|
|
||||||
#include "tmap.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "asfattribute.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace ASF {
|
|
||||||
|
|
||||||
typedef List<Attribute> AttributeList;
|
|
||||||
typedef Map<String, AttributeList> AttributeListMap;
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Tag : public TagLib::Tag {
|
|
||||||
|
|
||||||
friend class File;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Tag();
|
|
||||||
|
|
||||||
virtual ~Tag();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the track name.
|
|
||||||
*/
|
|
||||||
virtual String title() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the artist name.
|
|
||||||
*/
|
|
||||||
virtual String artist() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the album name; if no album name is present in the tag
|
|
||||||
* String::null will be returned.
|
|
||||||
*/
|
|
||||||
virtual String album() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the track comment.
|
|
||||||
*/
|
|
||||||
virtual String comment() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the genre name; if no genre is present in the tag String::null
|
|
||||||
* will be returned.
|
|
||||||
*/
|
|
||||||
virtual String genre() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the rating.
|
|
||||||
*/
|
|
||||||
virtual String rating() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the genre name; if no genre is present in the tag String::null
|
|
||||||
* will be returned.
|
|
||||||
*/
|
|
||||||
virtual String copyright() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the year; if there is no year set, this will return 0.
|
|
||||||
*/
|
|
||||||
virtual unsigned int year() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the track number; if there is no track number set, this will
|
|
||||||
* return 0.
|
|
||||||
*/
|
|
||||||
virtual unsigned int track() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the title to \a s.
|
|
||||||
*/
|
|
||||||
virtual void setTitle(const String &s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the artist to \a s.
|
|
||||||
*/
|
|
||||||
virtual void setArtist(const String &s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the album to \a s. If \a s is String::null then this value will be
|
|
||||||
* cleared.
|
|
||||||
*/
|
|
||||||
virtual void setAlbum(const String &s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the comment to \a s.
|
|
||||||
*/
|
|
||||||
virtual void setComment(const String &s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the rating to \a s.
|
|
||||||
*/
|
|
||||||
virtual void setRating(const String &s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the copyright to \a s.
|
|
||||||
*/
|
|
||||||
virtual void setCopyright(const String &s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the genre to \a s.
|
|
||||||
*/
|
|
||||||
virtual void setGenre(const String &s);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
|
|
||||||
*/
|
|
||||||
virtual void setYear(unsigned int i);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
|
|
||||||
*/
|
|
||||||
virtual void setTrack(unsigned int i);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if the tag does not contain any data. This should be
|
|
||||||
* reimplemented in subclasses that provide more than the basic tagging
|
|
||||||
* abilities in this class.
|
|
||||||
*/
|
|
||||||
virtual bool isEmpty() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \deprecated
|
|
||||||
*/
|
|
||||||
AttributeListMap &attributeListMap();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a reference to the item list map. This is an AttributeListMap of
|
|
||||||
* all of the items in the tag.
|
|
||||||
*/
|
|
||||||
const AttributeListMap &attributeListMap() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \return True if a value for \a attribute is currently set.
|
|
||||||
*/
|
|
||||||
bool contains(const String &name) const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Removes the \a key attribute from the tag
|
|
||||||
*/
|
|
||||||
void removeItem(const String &name);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \return The list of values for the key \a name, or an empty list if no
|
|
||||||
* values have been set.
|
|
||||||
*/
|
|
||||||
AttributeList attribute(const String &name) const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the \a key attribute to the value of \a attribute. If an attribute
|
|
||||||
* with the \a key is already present, it will be replaced.
|
|
||||||
*/
|
|
||||||
void setAttribute(const String &name, const Attribute &attribute);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets multiple \a values to the key \a name.
|
|
||||||
*/
|
|
||||||
void setAttribute(const String &name, const AttributeList &values);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the \a key attribute to the value of \a attribute. If an attribute
|
|
||||||
* with the \a key is already present, it will be added to the list.
|
|
||||||
*/
|
|
||||||
void addAttribute(const String &name, const Attribute &attribute);
|
|
||||||
|
|
||||||
PropertyMap properties() const;
|
|
||||||
void removeUnsupportedProperties(const StringList& properties);
|
|
||||||
PropertyMap setProperties(const PropertyMap &properties);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
class TagPrivate;
|
|
||||||
TagPrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
104
3rdparty/taglib/asf/asfutils.h
vendored
104
3rdparty/taglib/asf/asfutils.h
vendored
@@ -1,104 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2015 by Tsuda Kageyu
|
|
||||||
email : tsuda.kageyu@gmail.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_ASFUTILS_H
|
|
||||||
#define TAGLIB_ASFUTILS_H
|
|
||||||
|
|
||||||
// THIS FILE IS NOT A PART OF THE TAGLIB API
|
|
||||||
|
|
||||||
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
|
|
||||||
|
|
||||||
namespace TagLib
|
|
||||||
{
|
|
||||||
namespace ASF
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
inline unsigned short readWORD(File *file, bool *ok = 0)
|
|
||||||
{
|
|
||||||
const ByteVector v = file->readBlock(2);
|
|
||||||
if(v.size() != 2) {
|
|
||||||
if(ok) *ok = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(ok) *ok = true;
|
|
||||||
return v.toUShort(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline unsigned int readDWORD(File *file, bool *ok = 0)
|
|
||||||
{
|
|
||||||
const ByteVector v = file->readBlock(4);
|
|
||||||
if(v.size() != 4) {
|
|
||||||
if(ok) *ok = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(ok) *ok = true;
|
|
||||||
return v.toUInt(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline long long readQWORD(File *file, bool *ok = 0)
|
|
||||||
{
|
|
||||||
const ByteVector v = file->readBlock(8);
|
|
||||||
if(v.size() != 8) {
|
|
||||||
if(ok) *ok = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(ok) *ok = true;
|
|
||||||
return v.toLongLong(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline String readString(File *file, int length)
|
|
||||||
{
|
|
||||||
ByteVector data = file->readBlock(length);
|
|
||||||
unsigned int size = data.size();
|
|
||||||
while (size >= 2) {
|
|
||||||
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
size -= 2;
|
|
||||||
}
|
|
||||||
if(size != data.size()) {
|
|
||||||
data.resize(size);
|
|
||||||
}
|
|
||||||
return String(data, String::UTF16LE);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ByteVector renderString(const String &str, bool includeLength = false)
|
|
||||||
{
|
|
||||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
|
||||||
if(includeLength) {
|
|
||||||
data = ByteVector::fromShort(data.size(), false) + data;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
117
3rdparty/taglib/audioproperties.cpp
vendored
117
3rdparty/taglib/audioproperties.cpp
vendored
@@ -1,117 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
|
||||||
email : wheeler@kde.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tbytevector.h>
|
|
||||||
|
|
||||||
#include "aiffproperties.h"
|
|
||||||
#include "apeproperties.h"
|
|
||||||
#include "asfproperties.h"
|
|
||||||
#include "flacproperties.h"
|
|
||||||
#include "mp4properties.h"
|
|
||||||
#include "mpcproperties.h"
|
|
||||||
#include "mpegproperties.h"
|
|
||||||
#include "opusproperties.h"
|
|
||||||
#include "speexproperties.h"
|
|
||||||
#include "trueaudioproperties.h"
|
|
||||||
#include "vorbisproperties.h"
|
|
||||||
#include "wavproperties.h"
|
|
||||||
#include "wavpackproperties.h"
|
|
||||||
#include "dsfproperties.h"
|
|
||||||
#include "dsdiffproperties.h"
|
|
||||||
|
|
||||||
#include "audioproperties.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
// This macro is a workaround for the fact that we can't add virtual functions.
|
|
||||||
// Should be true virtual functions in taglib2.
|
|
||||||
|
|
||||||
#define VIRTUAL_FUNCTION_WORKAROUND(function_name, default_value) \
|
|
||||||
if(dynamic_cast<const APE::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const APE::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const ASF::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const ASF::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const FLAC::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const FLAC::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const MP4::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const MP4::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const MPC::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const MPC::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const MPEG::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const MPEG::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const Ogg::Opus::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const Ogg::Opus::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const Ogg::Speex::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const Ogg::Speex::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const TrueAudio::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const TrueAudio::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const RIFF::AIFF::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const RIFF::WAV::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const RIFF::WAV::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const Vorbis::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const Vorbis::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const WavPack::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const WavPack::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const DSF::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const DSF::Properties*>(this)->function_name(); \
|
|
||||||
else if(dynamic_cast<const DSDIFF::Properties*>(this)) \
|
|
||||||
return dynamic_cast<const DSDIFF::Properties*>(this)->function_name(); \
|
|
||||||
else \
|
|
||||||
return (default_value);
|
|
||||||
|
|
||||||
class AudioProperties::AudioPropertiesPrivate
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public methods
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
AudioProperties::~AudioProperties()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int AudioProperties::lengthInSeconds() const
|
|
||||||
{
|
|
||||||
VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
int AudioProperties::lengthInMilliseconds() const
|
|
||||||
{
|
|
||||||
VIRTUAL_FUNCTION_WORKAROUND(lengthInMilliseconds, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// protected methods
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
AudioProperties::AudioProperties(ReadStyle) :
|
|
||||||
d(0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
127
3rdparty/taglib/audioproperties.h
vendored
127
3rdparty/taglib/audioproperties.h
vendored
@@ -1,127 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
|
||||||
email : wheeler@kde.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_AUDIOPROPERTIES_H
|
|
||||||
#define TAGLIB_AUDIOPROPERTIES_H
|
|
||||||
|
|
||||||
#include "taglib_export.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
//! A simple, abstract interface to common audio properties
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The values here are common to most audio formats. For more specific, codec
|
|
||||||
* dependent values, please see see the subclasses APIs. This is meant to
|
|
||||||
* compliment the TagLib::File and TagLib::Tag APIs in providing a simple
|
|
||||||
* interface that is sufficient for most applications.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT AudioProperties
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Reading audio properties from a file can sometimes be very time consuming
|
|
||||||
* and for the most accurate results can often involve reading the entire
|
|
||||||
* file. Because in many situations speed is critical or the accuracy of the
|
|
||||||
* values is not particularly important this allows the level of desired
|
|
||||||
* accuracy to be set.
|
|
||||||
*/
|
|
||||||
enum ReadStyle {
|
|
||||||
//! Read as little of the file as possible
|
|
||||||
Fast,
|
|
||||||
//! Read more of the file and make better values guesses
|
|
||||||
Average,
|
|
||||||
//! Read as much of the file as needed to report accurate values
|
|
||||||
Accurate
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this AudioProperties instance.
|
|
||||||
*/
|
|
||||||
virtual ~AudioProperties();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in seconds.
|
|
||||||
*/
|
|
||||||
virtual int length() const = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in seconds. The length is rounded down to
|
|
||||||
* the nearest whole second.
|
|
||||||
*
|
|
||||||
* \see lengthInMilliseconds()
|
|
||||||
*/
|
|
||||||
// BIC: make virtual
|
|
||||||
int lengthInSeconds() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in milliseconds.
|
|
||||||
*
|
|
||||||
* \see lengthInSeconds()
|
|
||||||
*/
|
|
||||||
// BIC: make virtual
|
|
||||||
int lengthInMilliseconds() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the most appropriate bit rate for the file in kb/s. For constant
|
|
||||||
* bitrate formats this is simply the bitrate of the file. For variable
|
|
||||||
* bitrate formats this is either the average or nominal bitrate.
|
|
||||||
*/
|
|
||||||
virtual int bitrate() const = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the sample rate in Hz.
|
|
||||||
*/
|
|
||||||
virtual int sampleRate() const = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of audio channels.
|
|
||||||
*/
|
|
||||||
virtual int channels() const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Construct an audio properties instance. This is protected as this class
|
|
||||||
* should not be instantiated directly, but should be instantiated via its
|
|
||||||
* subclasses and can be fetched from the FileRef or File APIs.
|
|
||||||
*
|
|
||||||
* \see ReadStyle
|
|
||||||
*/
|
|
||||||
AudioProperties(ReadStyle style);
|
|
||||||
|
|
||||||
private:
|
|
||||||
AudioProperties(const AudioProperties &);
|
|
||||||
AudioProperties &operator=(const AudioProperties &);
|
|
||||||
|
|
||||||
class AudioPropertiesPrivate;
|
|
||||||
AudioPropertiesPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
162
3rdparty/taglib/dsdiff/dsdiffdiintag.cpp
vendored
162
3rdparty/taglib/dsdiff/dsdiffdiintag.cpp
vendored
@@ -1,162 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
|
||||||
email : damien78@audirvana.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include "dsdiffdiintag.h"
|
|
||||||
#include "tstringlist.h"
|
|
||||||
#include "tpropertymap.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
using namespace DSDIFF::DIIN;
|
|
||||||
|
|
||||||
class DSDIFF::DIIN::Tag::TagPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TagPrivate()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
String title;
|
|
||||||
String artist;
|
|
||||||
};
|
|
||||||
|
|
||||||
DSDIFF::DIIN::Tag::Tag() : TagLib::Tag()
|
|
||||||
{
|
|
||||||
d = new TagPrivate;
|
|
||||||
}
|
|
||||||
|
|
||||||
DSDIFF::DIIN::Tag::~Tag()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
String DSDIFF::DIIN::Tag::title() const
|
|
||||||
{
|
|
||||||
return d->title;
|
|
||||||
}
|
|
||||||
|
|
||||||
String DSDIFF::DIIN::Tag::artist() const
|
|
||||||
{
|
|
||||||
return d->artist;
|
|
||||||
}
|
|
||||||
|
|
||||||
String DSDIFF::DIIN::Tag::album() const
|
|
||||||
{
|
|
||||||
return String();
|
|
||||||
}
|
|
||||||
|
|
||||||
String DSDIFF::DIIN::Tag::comment() const
|
|
||||||
{
|
|
||||||
return String();
|
|
||||||
}
|
|
||||||
|
|
||||||
String DSDIFF::DIIN::Tag::genre() const
|
|
||||||
{
|
|
||||||
return String();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int DSDIFF::DIIN::Tag::year() const
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int DSDIFF::DIIN::Tag::track() const
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::DIIN::Tag::setTitle(const String &title)
|
|
||||||
{
|
|
||||||
if(title.isNull() || title.isEmpty())
|
|
||||||
d->title = String();
|
|
||||||
else
|
|
||||||
d->title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
|
|
||||||
{
|
|
||||||
if(artist.isNull() || artist.isEmpty())
|
|
||||||
d->artist = String();
|
|
||||||
else
|
|
||||||
d->artist = artist;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::DIIN::Tag::setAlbum(const String &)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::DIIN::Tag::setComment(const String &)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::DIIN::Tag::setGenre(const String &)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::DIIN::Tag::setYear(unsigned int)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap DSDIFF::DIIN::Tag::properties() const
|
|
||||||
{
|
|
||||||
PropertyMap properties;
|
|
||||||
properties["TITLE"] = d->title;
|
|
||||||
properties["ARTIST"] = d->artist;
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
|
|
||||||
{
|
|
||||||
PropertyMap properties(origProps);
|
|
||||||
properties.removeEmpty();
|
|
||||||
StringList oneValueSet;
|
|
||||||
|
|
||||||
if(properties.contains("TITLE")) {
|
|
||||||
d->title = properties["TITLE"].front();
|
|
||||||
oneValueSet.append("TITLE");
|
|
||||||
} else
|
|
||||||
d->title = String();
|
|
||||||
|
|
||||||
if(properties.contains("ARTIST")) {
|
|
||||||
d->artist = properties["ARTIST"].front();
|
|
||||||
oneValueSet.append("ARTIST");
|
|
||||||
} else
|
|
||||||
d->artist = String();
|
|
||||||
|
|
||||||
// for each tag that has been set above, remove the first entry in the corresponding
|
|
||||||
// value list. The others will be returned as unsupported by this format.
|
|
||||||
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
|
|
||||||
if(properties[*it].size() == 1)
|
|
||||||
properties.erase(*it);
|
|
||||||
else
|
|
||||||
properties[*it].erase(properties[*it].begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
150
3rdparty/taglib/dsdiff/dsdiffdiintag.h
vendored
150
3rdparty/taglib/dsdiff/dsdiffdiintag.h
vendored
@@ -1,150 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
|
||||||
email : damien78@audirvana.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_DSDIFFDIINTAG_H
|
|
||||||
#define TAGLIB_DSDIFFDIINTAG_H
|
|
||||||
|
|
||||||
#include "tag.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace DSDIFF {
|
|
||||||
|
|
||||||
namespace DIIN {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Tags from the Edited Master Chunk Info
|
|
||||||
*
|
|
||||||
* Only Title and Artist tags are supported
|
|
||||||
*/
|
|
||||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Tag();
|
|
||||||
virtual ~Tag();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the track name; if no track name is present in the tag
|
|
||||||
* String() will be returned.
|
|
||||||
*/
|
|
||||||
String title() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the artist name; if no artist name is present in the tag
|
|
||||||
* String() will be returned.
|
|
||||||
*/
|
|
||||||
String artist() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported. Therefore always returns String().
|
|
||||||
*/
|
|
||||||
String album() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported. Therefore always returns String().
|
|
||||||
*/
|
|
||||||
String comment() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported. Therefore always returns String().
|
|
||||||
*/
|
|
||||||
String genre() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported. Therefore always returns 0.
|
|
||||||
*/
|
|
||||||
unsigned int year() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported. Therefore always returns 0.
|
|
||||||
*/
|
|
||||||
unsigned int track() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the title to \a title. If \a title is String() then this
|
|
||||||
* value will be cleared.
|
|
||||||
*/
|
|
||||||
void setTitle(const String &title);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the artist to \a artist. If \a artist is String() then this
|
|
||||||
* value will be cleared.
|
|
||||||
*/
|
|
||||||
void setArtist(const String &artist);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported and therefore ignored.
|
|
||||||
*/
|
|
||||||
void setAlbum(const String &album);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported and therefore ignored.
|
|
||||||
*/
|
|
||||||
void setComment(const String &comment);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported and therefore ignored.
|
|
||||||
*/
|
|
||||||
void setGenre(const String &genre);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported and therefore ignored.
|
|
||||||
*/
|
|
||||||
void setYear(unsigned int year);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Not supported and therefore ignored.
|
|
||||||
*/
|
|
||||||
void setTrack(unsigned int track);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- export function.
|
|
||||||
* Since the DIIN tag is very limited, the exported map is as well.
|
|
||||||
*/
|
|
||||||
PropertyMap properties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- import function.
|
|
||||||
* Because of the limitations of the DIIN file tag, any tags besides
|
|
||||||
* TITLE and ARTIST, will be
|
|
||||||
* returned. Additionally, if the map contains tags with multiple values,
|
|
||||||
* all but the first will be contained in the returned map of unsupported
|
|
||||||
* properties.
|
|
||||||
*/
|
|
||||||
PropertyMap setProperties(const PropertyMap &);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Tag(const Tag &);
|
|
||||||
Tag &operator=(const Tag &);
|
|
||||||
|
|
||||||
class TagPrivate;
|
|
||||||
TagPrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
812
3rdparty/taglib/dsdiff/dsdifffile.cpp
vendored
812
3rdparty/taglib/dsdiff/dsdifffile.cpp
vendored
@@ -1,812 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
|
||||||
email : damien78@audirvana.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tbytevector.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <id3v2tag.h>
|
|
||||||
#include <tstringlist.h>
|
|
||||||
#include <tpropertymap.h>
|
|
||||||
#include <tagutils.h>
|
|
||||||
|
|
||||||
#include "tagunion.h"
|
|
||||||
#include "dsdifffile.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
struct Chunk64
|
|
||||||
{
|
|
||||||
ByteVector name;
|
|
||||||
unsigned long long offset;
|
|
||||||
unsigned long long size;
|
|
||||||
char padding;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
enum {
|
|
||||||
ID3v2Index = 0,
|
|
||||||
DIINIndex = 1
|
|
||||||
};
|
|
||||||
enum {
|
|
||||||
PROPChunk = 0,
|
|
||||||
DIINChunk = 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class DSDIFF::File::FilePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FilePrivate() :
|
|
||||||
endianness(BigEndian),
|
|
||||||
size(0),
|
|
||||||
isID3InPropChunk(false),
|
|
||||||
duplicateID3V2chunkIndex(-1),
|
|
||||||
properties(0),
|
|
||||||
id3v2TagChunkID("ID3 "),
|
|
||||||
hasID3v2(false),
|
|
||||||
hasDiin(false)
|
|
||||||
{
|
|
||||||
childChunkIndex[ID3v2Index] = -1;
|
|
||||||
childChunkIndex[DIINIndex] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
~FilePrivate()
|
|
||||||
{
|
|
||||||
delete properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
Endianness endianness;
|
|
||||||
ByteVector type;
|
|
||||||
unsigned long long size;
|
|
||||||
ByteVector format;
|
|
||||||
std::vector<Chunk64> chunks;
|
|
||||||
std::vector<Chunk64> childChunks[2];
|
|
||||||
int childChunkIndex[2];
|
|
||||||
bool isID3InPropChunk; // Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
|
|
||||||
int duplicateID3V2chunkIndex; // 2 ID3 chunks are present. This is then the index of the one in
|
|
||||||
// PROP chunk that will be removed upon next save to remove duplicates.
|
|
||||||
|
|
||||||
Properties *properties;
|
|
||||||
|
|
||||||
TagUnion tag;
|
|
||||||
|
|
||||||
ByteVector id3v2TagChunkID;
|
|
||||||
|
|
||||||
bool hasID3v2;
|
|
||||||
bool hasDiin;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// static members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool DSDIFF::File::isSupported(IOStream *stream)
|
|
||||||
{
|
|
||||||
// A DSDIFF file has to start with "FRM8????????DSD ".
|
|
||||||
|
|
||||||
const ByteVector id = Utils::readHeader(stream, 16, false);
|
|
||||||
return (id.startsWith("FRM8") && id.containsAt("DSD ", 12));
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
DSDIFF::File::File(FileName file, bool readProperties,
|
|
||||||
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
|
|
||||||
{
|
|
||||||
d = new FilePrivate;
|
|
||||||
d->endianness = BigEndian;
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties, propertiesStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
DSDIFF::File::File(IOStream *stream, bool readProperties,
|
|
||||||
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
|
|
||||||
{
|
|
||||||
d = new FilePrivate;
|
|
||||||
d->endianness = BigEndian;
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties, propertiesStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
DSDIFF::File::~File()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::Tag *DSDIFF::File::tag() const
|
|
||||||
{
|
|
||||||
return &d->tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID3v2::Tag *DSDIFF::File::ID3v2Tag() const
|
|
||||||
{
|
|
||||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DSDIFF::File::hasID3v2Tag() const
|
|
||||||
{
|
|
||||||
return d->hasID3v2;
|
|
||||||
}
|
|
||||||
|
|
||||||
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const
|
|
||||||
{
|
|
||||||
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DSDIFF::File::hasDIINTag() const
|
|
||||||
{
|
|
||||||
return d->hasDiin;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap DSDIFF::File::properties() const
|
|
||||||
{
|
|
||||||
if(d->hasID3v2)
|
|
||||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
|
|
||||||
|
|
||||||
return PropertyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
|
|
||||||
{
|
|
||||||
if(d->hasID3v2)
|
|
||||||
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported);
|
|
||||||
|
|
||||||
if(d->hasDiin)
|
|
||||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->removeUnsupportedProperties(unsupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
|
|
||||||
{
|
|
||||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
DSDIFF::Properties *DSDIFF::File::audioProperties() const
|
|
||||||
{
|
|
||||||
return d->properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DSDIFF::File::save()
|
|
||||||
{
|
|
||||||
if(readOnly()) {
|
|
||||||
debug("DSDIFF::File::save() -- File is read only.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isValid()) {
|
|
||||||
debug("DSDIFF::File::save() -- Trying to save invalid file.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First: save ID3V2 chunk
|
|
||||||
ID3v2::Tag *id3v2Tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
|
||||||
if(d->isID3InPropChunk) {
|
|
||||||
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
|
|
||||||
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(), PROPChunk);
|
|
||||||
d->hasID3v2 = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Empty tag: remove it
|
|
||||||
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
|
|
||||||
d->hasID3v2 = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
|
|
||||||
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render());
|
|
||||||
d->hasID3v2 = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Empty tag: remove it
|
|
||||||
setRootChunkData(d->id3v2TagChunkID, ByteVector());
|
|
||||||
d->hasID3v2 = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second: save the DIIN chunk
|
|
||||||
if(d->hasDiin) {
|
|
||||||
DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
|
||||||
|
|
||||||
if(!diinTag->title().isNull() && !diinTag->title().isEmpty()) {
|
|
||||||
ByteVector diinTitle;
|
|
||||||
diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian));
|
|
||||||
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
|
|
||||||
setChildChunkData("DITI", diinTitle, DIINChunk);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
setChildChunkData("DITI", ByteVector(), DIINChunk);
|
|
||||||
|
|
||||||
if(!diinTag->artist().isNull() && !diinTag->artist().isEmpty()) {
|
|
||||||
ByteVector diinArtist;
|
|
||||||
diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian));
|
|
||||||
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
|
|
||||||
setChildChunkData("DIAR", diinArtist, DIINChunk);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
setChildChunkData("DIAR", ByteVector(), DIINChunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any
|
|
||||||
if(d->duplicateID3V2chunkIndex>=0) {
|
|
||||||
setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk);
|
|
||||||
d->duplicateID3V2chunkIndex = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
|
|
||||||
{
|
|
||||||
if(data.isNull() || data.isEmpty()) {
|
|
||||||
// Null data: remove chunk
|
|
||||||
// Update global size
|
|
||||||
unsigned long long removedChunkTotalSize = d->chunks[i].size + d->chunks[i].padding + 12;
|
|
||||||
d->size -= removedChunkTotalSize;
|
|
||||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
|
||||||
|
|
||||||
removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize);
|
|
||||||
|
|
||||||
// Update the internal offsets
|
|
||||||
for(unsigned long r = i + 1; r < d->chunks.size(); r++)
|
|
||||||
d->chunks[r].offset = d->chunks[r - 1].offset + 12
|
|
||||||
+ d->chunks[r - 1].size + d->chunks[r - 1].padding;
|
|
||||||
|
|
||||||
d->chunks.erase(d->chunks.begin() + i);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Non null data: update chunk
|
|
||||||
// First we update the global size
|
|
||||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
|
||||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
|
||||||
|
|
||||||
// Now update the specific chunk
|
|
||||||
writeChunk(d->chunks[i].name,
|
|
||||||
data,
|
|
||||||
d->chunks[i].offset - 12,
|
|
||||||
d->chunks[i].size + d->chunks[i].padding + 12);
|
|
||||||
|
|
||||||
d->chunks[i].size = data.size();
|
|
||||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
|
||||||
|
|
||||||
// Finally update the internal offsets
|
|
||||||
updateRootChunksStructure(i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
|
|
||||||
{
|
|
||||||
if(d->chunks.size() == 0) {
|
|
||||||
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
|
||||||
if(d->chunks[i].name == name) {
|
|
||||||
setRootChunkData(i, data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Couldn't find an existing chunk, so let's create a new one.
|
|
||||||
unsigned int i = d->chunks.size() - 1;
|
|
||||||
unsigned long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
|
|
||||||
|
|
||||||
// First we update the global size
|
|
||||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
|
||||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
|
||||||
|
|
||||||
// Now add the chunk to the file
|
|
||||||
writeChunk(name,
|
|
||||||
data,
|
|
||||||
offset,
|
|
||||||
std::max<unsigned long long>(0, length() - offset),
|
|
||||||
(offset & 1) ? 1 : 0);
|
|
||||||
|
|
||||||
Chunk64 chunk;
|
|
||||||
chunk.name = name;
|
|
||||||
chunk.size = data.size();
|
|
||||||
chunk.offset = offset + 12;
|
|
||||||
chunk.padding = (data.size() & 0x01) ? 1 : 0;
|
|
||||||
|
|
||||||
d->chunks.push_back(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::File::setChildChunkData(unsigned int i,
|
|
||||||
const ByteVector &data,
|
|
||||||
unsigned int childChunkNum)
|
|
||||||
{
|
|
||||||
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
|
|
||||||
|
|
||||||
if(data.isNull() || data.isEmpty()) {
|
|
||||||
// Null data: remove chunk
|
|
||||||
// Update global size
|
|
||||||
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
|
|
||||||
d->size -= removedChunkTotalSize;
|
|
||||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
|
||||||
// Update child chunk size
|
|
||||||
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
|
|
||||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
|
||||||
d->endianness == BigEndian),
|
|
||||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
|
||||||
// Remove the chunk
|
|
||||||
removeBlock(childChunks[i].offset - 12, removedChunkTotalSize);
|
|
||||||
|
|
||||||
// Update the internal offsets
|
|
||||||
// For child chunks
|
|
||||||
if((i + 1) < childChunks.size()) {
|
|
||||||
childChunks[i + 1].offset = childChunks[i].offset;
|
|
||||||
i++;
|
|
||||||
for(i++; i < childChunks.size(); i++)
|
|
||||||
childChunks[i].offset = childChunks[i - 1].offset + 12
|
|
||||||
+ childChunks[i - 1].size + childChunks[i - 1].padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
// And for root chunks
|
|
||||||
for(i = d->childChunkIndex[childChunkNum] + 1; i < d->chunks.size(); i++)
|
|
||||||
d->chunks[i].offset = d->chunks[i - 1].offset + 12
|
|
||||||
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
|
|
||||||
|
|
||||||
childChunks.erase(childChunks.begin() + i);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Non null data: update chunk
|
|
||||||
// First we update the global size
|
|
||||||
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
|
||||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
|
||||||
// And the PROP chunk size
|
|
||||||
d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1)
|
|
||||||
- (childChunks[i].size + childChunks[i].padding);
|
|
||||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
|
||||||
d->endianness == BigEndian),
|
|
||||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
|
||||||
|
|
||||||
// Now update the specific chunk
|
|
||||||
writeChunk(childChunks[i].name,
|
|
||||||
data,
|
|
||||||
childChunks[i].offset - 12,
|
|
||||||
childChunks[i].size + childChunks[i].padding + 12);
|
|
||||||
|
|
||||||
childChunks[i].size = data.size();
|
|
||||||
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
|
||||||
|
|
||||||
// Now update the internal offsets
|
|
||||||
// For child Chunks
|
|
||||||
for(i++; i < childChunks.size(); i++)
|
|
||||||
childChunks[i].offset = childChunks[i - 1].offset + 12
|
|
||||||
+ childChunks[i - 1].size + childChunks[i - 1].padding;
|
|
||||||
|
|
||||||
// And for root chunks
|
|
||||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
|
||||||
const ByteVector &data,
|
|
||||||
unsigned int childChunkNum)
|
|
||||||
{
|
|
||||||
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
|
|
||||||
|
|
||||||
if(childChunks.size() == 0) {
|
|
||||||
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned int i = 0; i < childChunks.size(); i++) {
|
|
||||||
if(childChunks[i].name == name) {
|
|
||||||
setChildChunkData(i, data, childChunkNum);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not attempt to remove a non existing chunk
|
|
||||||
if(data.isNull() || data.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Couldn't find an existing chunk, so let's create a new one.
|
|
||||||
unsigned int i = childChunks.size() - 1;
|
|
||||||
unsigned long offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
|
|
||||||
|
|
||||||
// First we update the global size
|
|
||||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
|
||||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
|
||||||
// And the child chunk size
|
|
||||||
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
|
|
||||||
+ ((data.size() + 1) & ~1) + 12;
|
|
||||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
|
||||||
d->endianness == BigEndian),
|
|
||||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
|
||||||
|
|
||||||
// Now add the chunk to the file
|
|
||||||
unsigned long long nextRootChunkIdx = length();
|
|
||||||
if((d->childChunkIndex[childChunkNum] + 1) < static_cast<int>(d->chunks.size()))
|
|
||||||
nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12;
|
|
||||||
|
|
||||||
writeChunk(name, data, offset,
|
|
||||||
std::max<unsigned long long>(0, nextRootChunkIdx - offset),
|
|
||||||
(offset & 1) ? 1 : 0);
|
|
||||||
|
|
||||||
// For root chunks
|
|
||||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
|
||||||
|
|
||||||
Chunk64 chunk;
|
|
||||||
chunk.name = name;
|
|
||||||
chunk.size = data.size();
|
|
||||||
chunk.offset = offset + 12;
|
|
||||||
chunk.padding = (data.size() & 0x01) ? 1 : 0;
|
|
||||||
|
|
||||||
childChunks.push_back(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isValidChunkID(const ByteVector &name)
|
|
||||||
{
|
|
||||||
if(name.size() != 4)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for(int i = 0; i < 4; i++) {
|
|
||||||
if(name[i] < 32 || name[i] > 127)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
|
|
||||||
{
|
|
||||||
for(unsigned int i = startingChunk; i < d->chunks.size(); i++)
|
|
||||||
d->chunks[i].offset = d->chunks[i - 1].offset + 12
|
|
||||||
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
|
|
||||||
|
|
||||||
// Update childchunks structure as well
|
|
||||||
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
|
|
||||||
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[PROPChunk];
|
|
||||||
if(childChunksToUpdate.size() > 0) {
|
|
||||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
|
|
||||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
|
||||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
|
||||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
|
|
||||||
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[DIINChunk];
|
|
||||||
if(childChunksToUpdate.size() > 0) {
|
|
||||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
|
|
||||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
|
||||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
|
||||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
|
|
||||||
{
|
|
||||||
bool bigEndian = (d->endianness == BigEndian);
|
|
||||||
|
|
||||||
d->type = readBlock(4);
|
|
||||||
d->size = readBlock(8).toLongLong(bigEndian);
|
|
||||||
d->format = readBlock(4);
|
|
||||||
|
|
||||||
// + 12: chunk header at least, fix for additional junk bytes
|
|
||||||
while(tell() + 12 <= length()) {
|
|
||||||
ByteVector chunkName = readBlock(4);
|
|
||||||
unsigned long long chunkSize = readBlock(8).toLongLong(bigEndian);
|
|
||||||
|
|
||||||
if(!isValidChunkID(chunkName)) {
|
|
||||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(static_cast<unsigned long long>(tell()) + chunkSize > static_cast<unsigned long long>(length())) {
|
|
||||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName
|
|
||||||
+ "' has invalid size (larger than the file size)");
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunk64 chunk;
|
|
||||||
chunk.name = chunkName;
|
|
||||||
chunk.size = chunkSize;
|
|
||||||
chunk.offset = tell();
|
|
||||||
|
|
||||||
seek(chunk.size, Current);
|
|
||||||
|
|
||||||
// Check padding
|
|
||||||
chunk.padding = 0;
|
|
||||||
long uPosNotPadded = tell();
|
|
||||||
if((uPosNotPadded & 0x01) != 0) {
|
|
||||||
ByteVector iByte = readBlock(1);
|
|
||||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
|
||||||
// Not well formed, re-seek
|
|
||||||
seek(uPosNotPadded, Beginning);
|
|
||||||
else
|
|
||||||
chunk.padding = 1;
|
|
||||||
}
|
|
||||||
d->chunks.push_back(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long lengthDSDSamplesTimeChannels = 0; // For DSD uncompressed
|
|
||||||
unsigned long long audioDataSizeinBytes = 0; // For computing bitrate
|
|
||||||
unsigned long dstNumFrames = 0; // For DST compressed frames
|
|
||||||
unsigned short dstFrameRate = 0; // For DST compressed frames
|
|
||||||
|
|
||||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
|
||||||
if(d->chunks[i].name == "DSD ") {
|
|
||||||
lengthDSDSamplesTimeChannels = d->chunks[i].size * 8;
|
|
||||||
audioDataSizeinBytes = d->chunks[i].size;
|
|
||||||
}
|
|
||||||
else if(d->chunks[i].name == "DST ") {
|
|
||||||
// Now decode the chunks inside the DST chunk to read the DST Frame Information one
|
|
||||||
long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
|
||||||
seek(d->chunks[i].offset);
|
|
||||||
|
|
||||||
audioDataSizeinBytes = d->chunks[i].size;
|
|
||||||
|
|
||||||
while(tell() + 12 <= dstChunkEnd) {
|
|
||||||
ByteVector dstChunkName = readBlock(4);
|
|
||||||
long long dstChunkSize = readBlock(8).toLongLong(bigEndian);
|
|
||||||
|
|
||||||
if(!isValidChunkID(dstChunkName)) {
|
|
||||||
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID");
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(static_cast<long long>(tell()) + dstChunkSize > dstChunkEnd) {
|
|
||||||
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName
|
|
||||||
+ "' has invalid size (larger than the DST chunk)");
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dstChunkName == "FRTE") {
|
|
||||||
// Found the DST frame information chunk
|
|
||||||
dstNumFrames = readBlock(4).toUInt(bigEndian);
|
|
||||||
dstFrameRate = readBlock(2).toUShort(bigEndian);
|
|
||||||
break; // Found the wanted one, no need to look at the others
|
|
||||||
}
|
|
||||||
|
|
||||||
seek(dstChunkSize, Current);
|
|
||||||
|
|
||||||
// Check padding
|
|
||||||
long uPosNotPadded = tell();
|
|
||||||
if((uPosNotPadded & 0x01) != 0) {
|
|
||||||
ByteVector iByte = readBlock(1);
|
|
||||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
|
||||||
// Not well formed, re-seek
|
|
||||||
seek(uPosNotPadded, Beginning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(d->chunks[i].name == "PROP") {
|
|
||||||
d->childChunkIndex[PROPChunk] = i;
|
|
||||||
// Now decodes the chunks inside the PROP chunk
|
|
||||||
long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
|
||||||
seek(d->chunks[i].offset + 4); // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
|
|
||||||
while(tell() + 12 <= propChunkEnd) {
|
|
||||||
ByteVector propChunkName = readBlock(4);
|
|
||||||
long long propChunkSize = readBlock(8).toLongLong(bigEndian);
|
|
||||||
|
|
||||||
if(!isValidChunkID(propChunkName)) {
|
|
||||||
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID");
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) {
|
|
||||||
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName
|
|
||||||
+ "' has invalid size (larger than the PROP chunk)");
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunk64 chunk;
|
|
||||||
chunk.name = propChunkName;
|
|
||||||
chunk.size = propChunkSize;
|
|
||||||
chunk.offset = tell();
|
|
||||||
|
|
||||||
seek(chunk.size, Current);
|
|
||||||
|
|
||||||
// Check padding
|
|
||||||
chunk.padding = 0;
|
|
||||||
long uPosNotPadded = tell();
|
|
||||||
if((uPosNotPadded & 0x01) != 0) {
|
|
||||||
ByteVector iByte = readBlock(1);
|
|
||||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
|
||||||
// Not well formed, re-seek
|
|
||||||
seek(uPosNotPadded, Beginning);
|
|
||||||
else
|
|
||||||
chunk.padding = 1;
|
|
||||||
}
|
|
||||||
d->childChunks[PROPChunk].push_back(chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(d->chunks[i].name == "DIIN") {
|
|
||||||
d->childChunkIndex[DIINChunk] = i;
|
|
||||||
d->hasDiin = true;
|
|
||||||
// Now decode the chunks inside the DIIN chunk
|
|
||||||
long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
|
||||||
seek(d->chunks[i].offset);
|
|
||||||
|
|
||||||
while(tell() + 12 <= diinChunkEnd) {
|
|
||||||
ByteVector diinChunkName = readBlock(4);
|
|
||||||
long long diinChunkSize = readBlock(8).toLongLong(bigEndian);
|
|
||||||
|
|
||||||
if(!isValidChunkID(diinChunkName)) {
|
|
||||||
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID");
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) {
|
|
||||||
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName
|
|
||||||
+ "' has invalid size (larger than the DIIN chunk)");
|
|
||||||
setValid(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunk64 chunk;
|
|
||||||
chunk.name = diinChunkName;
|
|
||||||
chunk.size = diinChunkSize;
|
|
||||||
chunk.offset = tell();
|
|
||||||
|
|
||||||
seek(chunk.size, Current);
|
|
||||||
|
|
||||||
// Check padding
|
|
||||||
chunk.padding = 0;
|
|
||||||
long uPosNotPadded = tell();
|
|
||||||
if((uPosNotPadded & 0x01) != 0) {
|
|
||||||
ByteVector iByte = readBlock(1);
|
|
||||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
|
||||||
// Not well formed, re-seek
|
|
||||||
seek(uPosNotPadded, Beginning);
|
|
||||||
else
|
|
||||||
chunk.padding = 1;
|
|
||||||
}
|
|
||||||
d->childChunks[DIINChunk].push_back(chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") {
|
|
||||||
d->id3v2TagChunkID = d->chunks[i].name;
|
|
||||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset));
|
|
||||||
d->isID3InPropChunk = false;
|
|
||||||
d->hasID3v2 = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(d->childChunkIndex[PROPChunk] < 0) {
|
|
||||||
debug("DSDIFF::File::read() -- no PROP chunk found");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read properties
|
|
||||||
|
|
||||||
unsigned int sampleRate=0;
|
|
||||||
unsigned short channels=0;
|
|
||||||
|
|
||||||
for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) {
|
|
||||||
if(d->childChunks[PROPChunk][i].name == "ID3 " || d->childChunks[PROPChunk][i].name == "id3 ") {
|
|
||||||
if(d->hasID3v2) {
|
|
||||||
d->duplicateID3V2chunkIndex = i;
|
|
||||||
continue; // ID3V2 tag has already been found at root level
|
|
||||||
}
|
|
||||||
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
|
|
||||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset));
|
|
||||||
d->isID3InPropChunk = true;
|
|
||||||
d->hasID3v2 = true;
|
|
||||||
}
|
|
||||||
else if(d->childChunks[PROPChunk][i].name == "FS ") {
|
|
||||||
// Sample rate
|
|
||||||
seek(d->childChunks[PROPChunk][i].offset);
|
|
||||||
sampleRate = readBlock(4).toUInt(0, 4, bigEndian);
|
|
||||||
}
|
|
||||||
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
|
|
||||||
// Channels
|
|
||||||
seek(d->childChunks[PROPChunk][i].offset);
|
|
||||||
channels = readBlock(2).toShort(0, bigEndian);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read title & artist from DIIN chunk
|
|
||||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true);
|
|
||||||
|
|
||||||
if(d->hasDiin) {
|
|
||||||
for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) {
|
|
||||||
if(d->childChunks[DIINChunk][i].name == "DITI") {
|
|
||||||
seek(d->childChunks[DIINChunk][i].offset);
|
|
||||||
unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
|
||||||
if(titleStrLength <= d->childChunks[DIINChunk][i].size) {
|
|
||||||
ByteVector titleStr = readBlock(titleStrLength);
|
|
||||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setTitle(titleStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(d->childChunks[DIINChunk][i].name == "DIAR") {
|
|
||||||
seek(d->childChunks[DIINChunk][i].offset);
|
|
||||||
unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
|
||||||
if(artistStrLength <= d->childChunks[DIINChunk][i].size) {
|
|
||||||
ByteVector artistStr = readBlock(artistStrLength);
|
|
||||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setArtist(artistStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(readProperties) {
|
|
||||||
if(lengthDSDSamplesTimeChannels == 0) {
|
|
||||||
// DST compressed signal : need to compute length of DSD uncompressed frames
|
|
||||||
if(dstFrameRate > 0)
|
|
||||||
lengthDSDSamplesTimeChannels = (unsigned long long)dstNumFrames
|
|
||||||
* (unsigned long long)sampleRate / (unsigned long long)dstFrameRate;
|
|
||||||
else
|
|
||||||
lengthDSDSamplesTimeChannels = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// In DSD uncompressed files, the read number of samples is the total for each channel
|
|
||||||
if(channels > 0)
|
|
||||||
lengthDSDSamplesTimeChannels /= channels;
|
|
||||||
}
|
|
||||||
int bitrate = 0;
|
|
||||||
if(lengthDSDSamplesTimeChannels > 0)
|
|
||||||
bitrate = (audioDataSizeinBytes*8*sampleRate) / lengthDSDSamplesTimeChannels / 1000;
|
|
||||||
|
|
||||||
d->properties = new Properties(sampleRate,
|
|
||||||
channels,
|
|
||||||
lengthDSDSamplesTimeChannels,
|
|
||||||
bitrate,
|
|
||||||
propertiesStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!ID3v2Tag()) {
|
|
||||||
d->tag.access<ID3v2::Tag>(ID3v2Index, true);
|
|
||||||
d->isID3InPropChunk = false; // By default, ID3 chunk is at root level
|
|
||||||
d->hasID3v2 = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
|
|
||||||
unsigned long long offset, unsigned long replace,
|
|
||||||
unsigned int leadingPadding)
|
|
||||||
{
|
|
||||||
ByteVector combined;
|
|
||||||
if(leadingPadding)
|
|
||||||
combined.append(ByteVector(leadingPadding, '\x00'));
|
|
||||||
|
|
||||||
combined.append(name);
|
|
||||||
combined.append(ByteVector::fromLongLong(data.size(), d->endianness == BigEndian));
|
|
||||||
combined.append(data);
|
|
||||||
if((data.size() & 0x01) != 0)
|
|
||||||
combined.append('\x00');
|
|
||||||
|
|
||||||
insert(combined, offset, replace);
|
|
||||||
}
|
|
||||||
|
|
||||||
260
3rdparty/taglib/dsdiff/dsdifffile.h
vendored
260
3rdparty/taglib/dsdiff/dsdifffile.h
vendored
@@ -1,260 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
|
||||||
email : damien78@audirvana.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_DSDIFFFILE_H
|
|
||||||
#define TAGLIB_DSDIFFFILE_H
|
|
||||||
|
|
||||||
#include "rifffile.h"
|
|
||||||
#include "id3v2tag.h"
|
|
||||||
#include "dsdiffproperties.h"
|
|
||||||
#include "dsdiffdiintag.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
//! An implementation of DSDIFF metadata
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This is implementation of DSDIFF metadata.
|
|
||||||
*
|
|
||||||
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
|
|
||||||
* chunk as well as properties from the file.
|
|
||||||
* Description of the DSDIFF format is available
|
|
||||||
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
|
|
||||||
* DSDIFF standard does not explictly specify the ID3V2 chunk
|
|
||||||
* It can be found at the root level, but also sometimes inside the PROP chunk
|
|
||||||
* In addition, title and artist info are stored as part of the standard
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace DSDIFF {
|
|
||||||
|
|
||||||
//! An implementation of TagLib::File with DSDIFF specific methods
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This implements and provides an interface for DSDIFF files to the
|
|
||||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
|
||||||
* the abstract TagLib::File API as well as providing some additional
|
|
||||||
* information specific to DSDIFF files.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT File : public TagLib::File
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Constructs an DSDIFF file from \a file. If \a readProperties is true
|
|
||||||
* the file's audio properties will also be read.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
|
||||||
*/
|
|
||||||
File(FileName file, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an DSDIFF file from \a stream. If \a readProperties is true
|
|
||||||
* the file's audio properties will also be read.
|
|
||||||
*
|
|
||||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
|
||||||
* responsible for deleting it after the File object.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
|
||||||
*/
|
|
||||||
File(IOStream *stream, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this instance of the File.
|
|
||||||
*/
|
|
||||||
virtual ~File();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
|
|
||||||
* tags. The ID3v2 tag is given priority in reading the information -- if
|
|
||||||
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
|
|
||||||
* the information from the ID3v2 tag will be returned.
|
|
||||||
*
|
|
||||||
* If you would like more granular control over the content of the tags,
|
|
||||||
* with the concession of generality, use the tag-type specific calls.
|
|
||||||
*
|
|
||||||
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
|
|
||||||
* but a union of the two this pointer may not be cast to the specific
|
|
||||||
* tag types.
|
|
||||||
*
|
|
||||||
* \see ID3v2Tag()
|
|
||||||
* \see DIINTag()
|
|
||||||
*/
|
|
||||||
virtual Tag *tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the ID3V2 Tag for this file.
|
|
||||||
*
|
|
||||||
* \note This always returns a valid pointer regardless of whether or not
|
|
||||||
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the
|
|
||||||
* file on disk actually has an ID3v2 tag.
|
|
||||||
*
|
|
||||||
* \see hasID3v2Tag()
|
|
||||||
*/
|
|
||||||
virtual ID3v2::Tag *ID3v2Tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the DSDIFF DIIN Tag for this file
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
DSDIFF::DIIN::Tag *DIINTag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- export function.
|
|
||||||
* This method forwards to ID3v2::Tag::properties().
|
|
||||||
*/
|
|
||||||
PropertyMap properties() const;
|
|
||||||
|
|
||||||
void removeUnsupportedProperties(const StringList &properties);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- import function.
|
|
||||||
* This method forwards to ID3v2::Tag::setProperties().
|
|
||||||
*/
|
|
||||||
PropertyMap setProperties(const PropertyMap &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the AIFF::Properties for this file. If no audio properties
|
|
||||||
* were read then this will return a null pointer.
|
|
||||||
*/
|
|
||||||
virtual Properties *audioProperties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
|
|
||||||
* will duplicate its content into the other tag. This returns true
|
|
||||||
* if saving was successful.
|
|
||||||
*
|
|
||||||
* If neither exists or if both tags are empty, this will strip the tags
|
|
||||||
* from the file.
|
|
||||||
*
|
|
||||||
* This is the same as calling save(AllTags);
|
|
||||||
*
|
|
||||||
* If you would like more granular control over the content of the tags,
|
|
||||||
* with the concession of generality, use paramaterized save call below.
|
|
||||||
*
|
|
||||||
* \see save(int tags)
|
|
||||||
*/
|
|
||||||
virtual bool save();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Save the file. This will attempt to save all of the tag types that are
|
|
||||||
* specified by OR-ing together TagTypes values. The save() method above
|
|
||||||
* uses AllTags. This returns true if saving was successful.
|
|
||||||
*
|
|
||||||
* This strips all tags not included in the mask, but does not modify them
|
|
||||||
* in memory, so later calls to save() which make use of these tags will
|
|
||||||
* remain valid. This also strips empty tags.
|
|
||||||
*/
|
|
||||||
bool save(int tags);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
|
||||||
*
|
|
||||||
* \see ID3v2Tag()
|
|
||||||
*/
|
|
||||||
bool hasID3v2Tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the file on disk actually has the DSDIFF
|
|
||||||
* Title & Artist tag.
|
|
||||||
*
|
|
||||||
* \see DIINTag()
|
|
||||||
*/
|
|
||||||
bool hasDIINTag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the given \a stream can be opened as a DSDIFF
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* \note This method is designed to do a quick check. The result may
|
|
||||||
* not necessarily be correct.
|
|
||||||
*/
|
|
||||||
static bool isSupported(IOStream *stream);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
enum Endianness { BigEndian, LittleEndian };
|
|
||||||
|
|
||||||
File(FileName file, Endianness endianness);
|
|
||||||
File(IOStream *stream, Endianness endianness);
|
|
||||||
|
|
||||||
private:
|
|
||||||
File(const File &);
|
|
||||||
File &operator=(const File &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the data for the the specified chunk at root level to \a data.
|
|
||||||
*
|
|
||||||
* \warning This will update the file immediately.
|
|
||||||
*/
|
|
||||||
void setRootChunkData(unsigned int i, const ByteVector &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the data for the root-level chunk \a name to \a data.
|
|
||||||
* If a root-level chunk with the given name already exists
|
|
||||||
* it will be overwritten, otherwise it will be
|
|
||||||
* created after the existing chunks.
|
|
||||||
*
|
|
||||||
* \warning This will update the file immediately.
|
|
||||||
*/
|
|
||||||
void setRootChunkData(const ByteVector &name, const ByteVector &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the data for the the specified child chunk to \a data.
|
|
||||||
*
|
|
||||||
* If data is null, then remove the chunk
|
|
||||||
*
|
|
||||||
* \warning This will update the file immediately.
|
|
||||||
*/
|
|
||||||
void setChildChunkData(unsigned int i, const ByteVector &data,
|
|
||||||
unsigned int childChunkNum);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the data for the child chunk \a name to \a data. If a chunk with
|
|
||||||
* the given name already exists it will be overwritten, otherwise it will
|
|
||||||
* be created after the existing chunks inside child chunk.
|
|
||||||
*
|
|
||||||
* If data is null, then remove the chunks with \a name name
|
|
||||||
*
|
|
||||||
* \warning This will update the file immediately.
|
|
||||||
*/
|
|
||||||
void setChildChunkData(const ByteVector &name, const ByteVector &data,
|
|
||||||
unsigned int childChunkNum);
|
|
||||||
|
|
||||||
void updateRootChunksStructure(unsigned int startingChunk);
|
|
||||||
|
|
||||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
|
||||||
void writeChunk(const ByteVector &name, const ByteVector &data,
|
|
||||||
unsigned long long offset, unsigned long replace = 0,
|
|
||||||
unsigned int leadingPadding = 0);
|
|
||||||
|
|
||||||
class FilePrivate;
|
|
||||||
FilePrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
120
3rdparty/taglib/dsdiff/dsdiffproperties.cpp
vendored
120
3rdparty/taglib/dsdiff/dsdiffproperties.cpp
vendored
@@ -1,120 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
|
||||||
email : damien78@audirvana.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
|
|
||||||
#include "dsdiffproperties.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class DSDIFF::Properties::PropertiesPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PropertiesPrivate() :
|
|
||||||
length(0),
|
|
||||||
bitrate(0),
|
|
||||||
sampleRate(0),
|
|
||||||
channels(0),
|
|
||||||
sampleWidth(0),
|
|
||||||
sampleCount(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int length;
|
|
||||||
int bitrate;
|
|
||||||
int sampleRate;
|
|
||||||
int channels;
|
|
||||||
int sampleWidth;
|
|
||||||
unsigned long long sampleCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
DSDIFF::Properties::Properties(const unsigned int sampleRate,
|
|
||||||
const unsigned short channels,
|
|
||||||
const unsigned long long samplesCount,
|
|
||||||
const int bitrate,
|
|
||||||
ReadStyle style) : AudioProperties(style)
|
|
||||||
{
|
|
||||||
d = new PropertiesPrivate;
|
|
||||||
|
|
||||||
d->channels = channels;
|
|
||||||
d->sampleCount = samplesCount;
|
|
||||||
d->sampleWidth = 1;
|
|
||||||
d->sampleRate = sampleRate;
|
|
||||||
d->bitrate = bitrate;
|
|
||||||
d->length = d->sampleRate > 0
|
|
||||||
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
|
|
||||||
: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DSDIFF::Properties::~Properties()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSDIFF::Properties::length() const
|
|
||||||
{
|
|
||||||
return lengthInSeconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSDIFF::Properties::lengthInSeconds() const
|
|
||||||
{
|
|
||||||
return d->length / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSDIFF::Properties::lengthInMilliseconds() const
|
|
||||||
{
|
|
||||||
return d->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSDIFF::Properties::bitrate() const
|
|
||||||
{
|
|
||||||
return d->bitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSDIFF::Properties::sampleRate() const
|
|
||||||
{
|
|
||||||
return d->sampleRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSDIFF::Properties::channels() const
|
|
||||||
{
|
|
||||||
return d->channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSDIFF::Properties::bitsPerSample() const
|
|
||||||
{
|
|
||||||
return d->sampleWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
long long DSDIFF::Properties::sampleCount() const
|
|
||||||
{
|
|
||||||
return d->sampleCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
83
3rdparty/taglib/dsdiff/dsdiffproperties.h
vendored
83
3rdparty/taglib/dsdiff/dsdiffproperties.h
vendored
@@ -1,83 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
|
||||||
email : damien78@audirvana.com
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_DSDIFFPROPERTIES_H
|
|
||||||
#define TAGLIB_DSDIFFPROPERTIES_H
|
|
||||||
|
|
||||||
#include "audioproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace DSDIFF {
|
|
||||||
|
|
||||||
class File;
|
|
||||||
|
|
||||||
//! An implementation of audio property reading for DSDIFF
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This reads the data from an DSDIFF stream found in the AudioProperties
|
|
||||||
* API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Create an instance of DSDIFF::Properties with the data read from the
|
|
||||||
* ByteVector \a data.
|
|
||||||
*/
|
|
||||||
Properties(const unsigned int sampleRate, const unsigned short channels,
|
|
||||||
const unsigned long long samplesCount, const int bitrate,
|
|
||||||
ReadStyle style);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this DSDIFF::Properties instance.
|
|
||||||
*/
|
|
||||||
virtual ~Properties();
|
|
||||||
|
|
||||||
// Reimplementations.
|
|
||||||
|
|
||||||
virtual int length() const;
|
|
||||||
virtual int lengthInSeconds() const;
|
|
||||||
virtual int lengthInMilliseconds() const;
|
|
||||||
virtual int bitrate() const;
|
|
||||||
virtual int sampleRate() const;
|
|
||||||
virtual int channels() const;
|
|
||||||
|
|
||||||
int bitsPerSample() const;
|
|
||||||
long long sampleCount() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Properties(const Properties &);
|
|
||||||
Properties &operator=(const Properties &);
|
|
||||||
|
|
||||||
class PropertiesPrivate;
|
|
||||||
PropertiesPrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
241
3rdparty/taglib/dsf/dsffile.cpp
vendored
241
3rdparty/taglib/dsf/dsffile.cpp
vendored
@@ -1,241 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2013 - 2018 by Stephen F. Booth
|
|
||||||
email : me@sbooth.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tbytevector.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <id3v2tag.h>
|
|
||||||
#include <tstringlist.h>
|
|
||||||
#include <tpropertymap.h>
|
|
||||||
#include <tagutils.h>
|
|
||||||
|
|
||||||
#include "dsffile.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
|
||||||
|
|
||||||
class DSF::File::FilePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FilePrivate() :
|
|
||||||
properties(0),
|
|
||||||
tag(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~FilePrivate()
|
|
||||||
{
|
|
||||||
delete properties;
|
|
||||||
delete tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
long long fileSize;
|
|
||||||
long long metadataOffset;
|
|
||||||
Properties *properties;
|
|
||||||
ID3v2::Tag *tag;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// static members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool DSF::File::isSupported(IOStream *stream)
|
|
||||||
{
|
|
||||||
// A DSF file has to start with "DSD "
|
|
||||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
|
||||||
return id.startsWith("DSD ");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
DSF::File::File(FileName file, bool readProperties,
|
|
||||||
Properties::ReadStyle propertiesStyle) :
|
|
||||||
TagLib::File(file),
|
|
||||||
d(new FilePrivate())
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties, propertiesStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
DSF::File::File(IOStream *stream, bool readProperties,
|
|
||||||
Properties::ReadStyle propertiesStyle) :
|
|
||||||
TagLib::File(stream),
|
|
||||||
d(new FilePrivate())
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties, propertiesStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
DSF::File::~File()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID3v2::Tag *DSF::File::tag() const
|
|
||||||
{
|
|
||||||
return d->tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap DSF::File::properties() const
|
|
||||||
{
|
|
||||||
return d->tag->properties();
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
|
|
||||||
{
|
|
||||||
return d->tag->setProperties(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
DSF::Properties *DSF::File::audioProperties() const
|
|
||||||
{
|
|
||||||
return d->properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DSF::File::save()
|
|
||||||
{
|
|
||||||
if(readOnly()) {
|
|
||||||
debug("DSF::File::save() -- File is read only.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isValid()) {
|
|
||||||
debug("DSF::File::save() -- Trying to save invalid file.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Three things must be updated: the file size, the tag data, and the metadata offset
|
|
||||||
|
|
||||||
if(d->tag->isEmpty()) {
|
|
||||||
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
|
||||||
|
|
||||||
// Update the file size
|
|
||||||
if(d->fileSize != newFileSize) {
|
|
||||||
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
|
|
||||||
d->fileSize = newFileSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the metadata offset to 0 since there is no longer a tag
|
|
||||||
if(d->metadataOffset) {
|
|
||||||
insert(ByteVector::fromLongLong(0ULL, false), 20, 8);
|
|
||||||
d->metadataOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the old tag
|
|
||||||
truncate(newFileSize);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ByteVector tagData = d->tag->render();
|
|
||||||
|
|
||||||
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
|
||||||
long long newFileSize = newMetadataOffset + tagData.size();
|
|
||||||
long long oldTagSize = d->fileSize - newMetadataOffset;
|
|
||||||
|
|
||||||
// Update the file size
|
|
||||||
if(d->fileSize != newFileSize) {
|
|
||||||
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
|
|
||||||
d->fileSize = newFileSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the metadata offset
|
|
||||||
if(d->metadataOffset != newMetadataOffset) {
|
|
||||||
insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8);
|
|
||||||
d->metadataOffset = newMetadataOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the old tag and write the new one
|
|
||||||
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
void DSF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
|
|
||||||
{
|
|
||||||
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
|
|
||||||
// The file format is not chunked in the sense of a RIFF File, though
|
|
||||||
|
|
||||||
// DSD chunk
|
|
||||||
ByteVector chunkName = readBlock(4);
|
|
||||||
if(chunkName != "DSD ") {
|
|
||||||
debug("DSF::File::read() -- Not a DSF file.");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long long chunkSize = readBlock(8).toLongLong(false);
|
|
||||||
|
|
||||||
// Integrity check
|
|
||||||
if(28 != chunkSize) {
|
|
||||||
debug("DSF::File::read() -- File is corrupted, wrong chunk size");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->fileSize = readBlock(8).toLongLong(false);
|
|
||||||
|
|
||||||
// File is malformed or corrupted
|
|
||||||
if(d->fileSize != length()) {
|
|
||||||
debug("DSF::File::read() -- File is corrupted wrong length");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->metadataOffset = readBlock(8).toLongLong(false);
|
|
||||||
|
|
||||||
// File is malformed or corrupted
|
|
||||||
if(d->metadataOffset > d->fileSize) {
|
|
||||||
debug("DSF::File::read() -- Invalid metadata offset.");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format chunk
|
|
||||||
chunkName = readBlock(4);
|
|
||||||
if(chunkName != "fmt ") {
|
|
||||||
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunkSize = readBlock(8).toLongLong(false);
|
|
||||||
|
|
||||||
d->properties = new Properties(readBlock(chunkSize), propertiesStyle);
|
|
||||||
|
|
||||||
// Skip the data chunk
|
|
||||||
|
|
||||||
// A metadata offset of 0 indicates the absence of an ID3v2 tag
|
|
||||||
if(0 == d->metadataOffset)
|
|
||||||
d->tag = new ID3v2::Tag();
|
|
||||||
else
|
|
||||||
d->tag = new ID3v2::Tag(this, d->metadataOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
128
3rdparty/taglib/dsf/dsffile.h
vendored
128
3rdparty/taglib/dsf/dsffile.h
vendored
@@ -1,128 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2013 - 2018 by Stephen F. Booth
|
|
||||||
email : me@sbooth.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_DSFFILE_H
|
|
||||||
#define TAGLIB_DSFFILE_H
|
|
||||||
|
|
||||||
#include "tfile.h"
|
|
||||||
#include "id3v2tag.h"
|
|
||||||
#include "dsfproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
//! An implementation of DSF metadata
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This is implementation of DSF metadata.
|
|
||||||
*
|
|
||||||
* This supports an ID3v2 tag as well as properties from the file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace DSF {
|
|
||||||
|
|
||||||
//! An implementation of TagLib::File with DSF specific methods
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This implements and provides an interface for DSF files to the
|
|
||||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
|
||||||
* the abstract TagLib::File API as well as providing some additional
|
|
||||||
* information specific to DSF files.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT File : public TagLib::File
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Contructs an DSF file from \a file. If \a readProperties is true the
|
|
||||||
* file's audio properties will also be read using \a propertiesStyle. If
|
|
||||||
* false, \a propertiesStyle is ignored.
|
|
||||||
*/
|
|
||||||
File(FileName file, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Contructs an DSF file from \a file. If \a readProperties is true the
|
|
||||||
* file's audio properties will also be read using \a propertiesStyle. If
|
|
||||||
* false, \a propertiesStyle is ignored.
|
|
||||||
*/
|
|
||||||
File(IOStream *stream, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this instance of the File.
|
|
||||||
*/
|
|
||||||
virtual ~File();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the Tag for this file.
|
|
||||||
*/
|
|
||||||
ID3v2::Tag *tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- export function.
|
|
||||||
* This method forwards to ID3v2::Tag::properties().
|
|
||||||
*/
|
|
||||||
PropertyMap properties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- import function.
|
|
||||||
* This method forwards to ID3v2::Tag::setProperties().
|
|
||||||
*/
|
|
||||||
PropertyMap setProperties(const PropertyMap &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the DSF::AudioProperties for this file. If no audio properties
|
|
||||||
* were read then this will return a null pointer.
|
|
||||||
*/
|
|
||||||
virtual Properties *audioProperties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Saves the file.
|
|
||||||
*/
|
|
||||||
virtual bool save();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the given \a stream can be opened as a DSF
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* \note This method is designed to do a quick check. The result may
|
|
||||||
* not necessarily be correct.
|
|
||||||
*/
|
|
||||||
static bool isSupported(IOStream *stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
File(const File &);
|
|
||||||
File &operator=(const File &);
|
|
||||||
|
|
||||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
|
||||||
|
|
||||||
class FilePrivate;
|
|
||||||
FilePrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
161
3rdparty/taglib/dsf/dsfproperties.cpp
vendored
161
3rdparty/taglib/dsf/dsfproperties.cpp
vendored
@@ -1,161 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2013 by Stephen F. Booth
|
|
||||||
email : me@sbooth.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
|
|
||||||
#include "dsfproperties.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class DSF::Properties::PropertiesPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PropertiesPrivate() :
|
|
||||||
formatVersion(0),
|
|
||||||
formatID(0),
|
|
||||||
channelType(0),
|
|
||||||
channelNum(0),
|
|
||||||
samplingFrequency(0),
|
|
||||||
bitsPerSample(0),
|
|
||||||
sampleCount(0),
|
|
||||||
blockSizePerChannel(0),
|
|
||||||
bitrate(0),
|
|
||||||
length(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nomenclature is from DSF file format specification
|
|
||||||
unsigned int formatVersion;
|
|
||||||
unsigned int formatID;
|
|
||||||
unsigned int channelType;
|
|
||||||
unsigned int channelNum;
|
|
||||||
unsigned int samplingFrequency;
|
|
||||||
unsigned int bitsPerSample;
|
|
||||||
long long sampleCount;
|
|
||||||
unsigned int blockSizePerChannel;
|
|
||||||
|
|
||||||
// Computed
|
|
||||||
unsigned int bitrate;
|
|
||||||
unsigned int length;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) : TagLib::AudioProperties(style)
|
|
||||||
{
|
|
||||||
d = new PropertiesPrivate;
|
|
||||||
read(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
DSF::Properties::~Properties()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::length() const
|
|
||||||
{
|
|
||||||
return lengthInSeconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::lengthInSeconds() const
|
|
||||||
{
|
|
||||||
return d->length / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::lengthInMilliseconds() const
|
|
||||||
{
|
|
||||||
return d->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::bitrate() const
|
|
||||||
{
|
|
||||||
return d->bitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::sampleRate() const
|
|
||||||
{
|
|
||||||
return d->samplingFrequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::channels() const
|
|
||||||
{
|
|
||||||
return d->channelNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DSF specific
|
|
||||||
int DSF::Properties::formatVersion() const
|
|
||||||
{
|
|
||||||
return d->formatVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::formatID() const
|
|
||||||
{
|
|
||||||
return d->formatID;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::channelType() const
|
|
||||||
{
|
|
||||||
return d->channelType;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::bitsPerSample() const
|
|
||||||
{
|
|
||||||
return d->bitsPerSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
long long DSF::Properties::sampleCount() const
|
|
||||||
{
|
|
||||||
return d->sampleCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSF::Properties::blockSizePerChannel() const
|
|
||||||
{
|
|
||||||
return d->blockSizePerChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void DSF::Properties::read(const ByteVector &data)
|
|
||||||
{
|
|
||||||
d->formatVersion = data.toUInt(0U,false);
|
|
||||||
d->formatID = data.toUInt(4U,false);
|
|
||||||
d->channelType = data.toUInt(8U,false);
|
|
||||||
d->channelNum = data.toUInt(12U,false);
|
|
||||||
d->samplingFrequency = data.toUInt(16U,false);
|
|
||||||
d->bitsPerSample = data.toUInt(20U,false);
|
|
||||||
d->sampleCount = data.toLongLong(24U,false);
|
|
||||||
d->blockSizePerChannel = data.toUInt(32U,false);
|
|
||||||
|
|
||||||
d->bitrate
|
|
||||||
= static_cast<unsigned int>((d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0 + 0.5);
|
|
||||||
d->length
|
|
||||||
= d->samplingFrequency > 0 ? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
92
3rdparty/taglib/dsf/dsfproperties.h
vendored
92
3rdparty/taglib/dsf/dsfproperties.h
vendored
@@ -1,92 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2013 by Stephen F. Booth
|
|
||||||
email : me@sbooth.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_DSFPROPERTIES_H
|
|
||||||
#define TAGLIB_DSFPROPERTIES_H
|
|
||||||
|
|
||||||
#include "audioproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace DSF {
|
|
||||||
|
|
||||||
class File;
|
|
||||||
|
|
||||||
//! An implementation of audio property reading for DSF
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This reads the data from a DSF stream found in the AudioProperties
|
|
||||||
* API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Properties : public TagLib::AudioProperties
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Create an instance of DSF::AudioProperties with the data read from the
|
|
||||||
* ByteVector \a data.
|
|
||||||
*/
|
|
||||||
Properties(const ByteVector &data, ReadStyle style);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this DSF::AudioProperties instance.
|
|
||||||
*/
|
|
||||||
virtual ~Properties();
|
|
||||||
|
|
||||||
// Reimplementations.
|
|
||||||
|
|
||||||
virtual int length() const;
|
|
||||||
virtual int lengthInSeconds() const;
|
|
||||||
virtual int lengthInMilliseconds() const;
|
|
||||||
virtual int bitrate() const;
|
|
||||||
virtual int sampleRate() const;
|
|
||||||
virtual int channels() const;
|
|
||||||
|
|
||||||
int formatVersion() const;
|
|
||||||
int formatID() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
|
|
||||||
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
|
|
||||||
*/
|
|
||||||
int channelType() const;
|
|
||||||
int bitsPerSample() const;
|
|
||||||
long long sampleCount() const;
|
|
||||||
int blockSizePerChannel() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Properties(const Properties &);
|
|
||||||
Properties &operator=(const Properties &);
|
|
||||||
|
|
||||||
void read(const ByteVector &data);
|
|
||||||
|
|
||||||
class PropertiesPrivate;
|
|
||||||
PropertiesPrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
491
3rdparty/taglib/fileref.cpp
vendored
491
3rdparty/taglib/fileref.cpp
vendored
@@ -1,491 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
|
||||||
email : wheeler@kde.org
|
|
||||||
|
|
||||||
copyright : (C) 2010 by Alex Novichkov
|
|
||||||
email : novichko@atnet.ru
|
|
||||||
(added APE file support)
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tfile.h>
|
|
||||||
#include <tfilestream.h>
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <trefcounter.h>
|
|
||||||
|
|
||||||
#include "fileref.h"
|
|
||||||
#include "asffile.h"
|
|
||||||
#include "mpegfile.h"
|
|
||||||
#include "vorbisfile.h"
|
|
||||||
#include "flacfile.h"
|
|
||||||
#include "oggflacfile.h"
|
|
||||||
#include "mpcfile.h"
|
|
||||||
#include "mp4file.h"
|
|
||||||
#include "wavpackfile.h"
|
|
||||||
#include "speexfile.h"
|
|
||||||
#include "opusfile.h"
|
|
||||||
#include "trueaudiofile.h"
|
|
||||||
#include "aifffile.h"
|
|
||||||
#include "wavfile.h"
|
|
||||||
#include "apefile.h"
|
|
||||||
#include "modfile.h"
|
|
||||||
#include "s3mfile.h"
|
|
||||||
#include "itfile.h"
|
|
||||||
#include "xmfile.h"
|
|
||||||
#include "dsffile.h"
|
|
||||||
#include "dsdifffile.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
typedef List<const FileRef::FileTypeResolver *> ResolverList;
|
|
||||||
ResolverList fileTypeResolvers;
|
|
||||||
|
|
||||||
// Detect the file type by user-defined resolvers.
|
|
||||||
|
|
||||||
File *detectByResolvers(FileName fileName, bool readAudioProperties,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
|
||||||
{
|
|
||||||
ResolverList::ConstIterator it = fileTypeResolvers.begin();
|
|
||||||
for(; it != fileTypeResolvers.end(); ++it) {
|
|
||||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(file)
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect the file type based on the file extension.
|
|
||||||
|
|
||||||
File* detectByExtension(IOStream *stream, bool readAudioProperties,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
const String s = stream->name().toString();
|
|
||||||
#else
|
|
||||||
const String s(stream->name());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
String ext;
|
|
||||||
const int pos = s.rfind(".");
|
|
||||||
if(pos != -1)
|
|
||||||
ext = s.substr(pos + 1).upper();
|
|
||||||
|
|
||||||
// If this list is updated, the method defaultFileExtensions() should also be
|
|
||||||
// updated. However at some point that list should be created at the same time
|
|
||||||
// that a default file type resolver is created.
|
|
||||||
|
|
||||||
if(ext.isEmpty())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
|
|
||||||
|
|
||||||
if(ext == "MP3")
|
|
||||||
return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "OGG")
|
|
||||||
return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "FLAC")
|
|
||||||
return new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "MPC")
|
|
||||||
return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "WV")
|
|
||||||
return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "SPX")
|
|
||||||
return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "OPUS")
|
|
||||||
return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "TTA")
|
|
||||||
return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
|
||||||
return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "WMA" || ext == "ASF")
|
|
||||||
return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
|
||||||
return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "WAV")
|
|
||||||
return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "APE")
|
|
||||||
return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
// module, nst and wow are possible but uncommon extensions
|
|
||||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
|
||||||
return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "S3M")
|
|
||||||
return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "IT")
|
|
||||||
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "XM")
|
|
||||||
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "DFF" || ext == "DSDIFF")
|
|
||||||
return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "DSF")
|
|
||||||
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect the file type based on the actual content of the stream.
|
|
||||||
|
|
||||||
File *detectByContent(IOStream *stream, bool readAudioProperties,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
|
||||||
{
|
|
||||||
File *file = 0;
|
|
||||||
|
|
||||||
if(MPEG::File::isSupported(stream))
|
|
||||||
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(Ogg::Vorbis::File::isSupported(stream))
|
|
||||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(Ogg::FLAC::File::isSupported(stream))
|
|
||||||
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(FLAC::File::isSupported(stream))
|
|
||||||
file = new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(MPC::File::isSupported(stream))
|
|
||||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(WavPack::File::isSupported(stream))
|
|
||||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(Ogg::Speex::File::isSupported(stream))
|
|
||||||
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(Ogg::Opus::File::isSupported(stream))
|
|
||||||
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(TrueAudio::File::isSupported(stream))
|
|
||||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(MP4::File::isSupported(stream))
|
|
||||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(ASF::File::isSupported(stream))
|
|
||||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(RIFF::AIFF::File::isSupported(stream))
|
|
||||||
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(RIFF::WAV::File::isSupported(stream))
|
|
||||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(APE::File::isSupported(stream))
|
|
||||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(DSDIFF::File::isSupported(stream))
|
|
||||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
else if(DSF::File::isSupported(stream))
|
|
||||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
|
|
||||||
// isSupported() only does a quick check, so double check the file here.
|
|
||||||
|
|
||||||
if(file) {
|
|
||||||
if(file->isValid())
|
|
||||||
return file;
|
|
||||||
else
|
|
||||||
delete file;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal function that supports FileRef::create().
|
|
||||||
// This looks redundant, but necessary in order not to change the previous
|
|
||||||
// behavior of FileRef::create().
|
|
||||||
|
|
||||||
File* createInternal(FileName fileName, bool readAudioProperties,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
|
||||||
{
|
|
||||||
File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(file)
|
|
||||||
return file;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
const String s = fileName.toString();
|
|
||||||
#else
|
|
||||||
const String s(fileName);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
String ext;
|
|
||||||
const int pos = s.rfind(".");
|
|
||||||
if(pos != -1)
|
|
||||||
ext = s.substr(pos + 1).upper();
|
|
||||||
|
|
||||||
if(ext.isEmpty())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if(ext == "MP3")
|
|
||||||
return new MPEG::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "OGG")
|
|
||||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "OGA") {
|
|
||||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
|
||||||
File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(file->isValid())
|
|
||||||
return file;
|
|
||||||
delete file;
|
|
||||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
}
|
|
||||||
if(ext == "FLAC")
|
|
||||||
return new FLAC::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "MPC")
|
|
||||||
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "WV")
|
|
||||||
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "SPX")
|
|
||||||
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "OPUS")
|
|
||||||
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "TTA")
|
|
||||||
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
|
||||||
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "WMA" || ext == "ASF")
|
|
||||||
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
|
||||||
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "WAV")
|
|
||||||
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "APE")
|
|
||||||
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
// module, nst and wow are possible but uncommon extensions
|
|
||||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
|
||||||
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "S3M")
|
|
||||||
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "IT")
|
|
||||||
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "XM")
|
|
||||||
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "DFF" || ext == "DSDIFF")
|
|
||||||
return new DSDIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(ext == "DSF")
|
|
||||||
return new DSF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FileRef::FileRefPrivate : public RefCounter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileRefPrivate() :
|
|
||||||
RefCounter(),
|
|
||||||
file(0),
|
|
||||||
stream(0) {}
|
|
||||||
|
|
||||||
~FileRefPrivate() {
|
|
||||||
delete file;
|
|
||||||
delete stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
File *file;
|
|
||||||
IOStream *stream;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
FileRef::FileRef() :
|
|
||||||
d(new FileRefPrivate())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FileRef::FileRef(FileName fileName, bool readAudioProperties,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle) :
|
|
||||||
d(new FileRefPrivate())
|
|
||||||
{
|
|
||||||
parse(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
|
||||||
d(new FileRefPrivate())
|
|
||||||
{
|
|
||||||
parse(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileRef::FileRef(File *file) :
|
|
||||||
d(new FileRefPrivate())
|
|
||||||
{
|
|
||||||
d->file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileRef::FileRef(const FileRef &ref) :
|
|
||||||
d(ref.d)
|
|
||||||
{
|
|
||||||
d->ref();
|
|
||||||
}
|
|
||||||
|
|
||||||
FileRef::~FileRef()
|
|
||||||
{
|
|
||||||
if(d->deref())
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tag *FileRef::tag() const
|
|
||||||
{
|
|
||||||
if(isNull()) {
|
|
||||||
debug("FileRef::tag() - Called without a valid file.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return d->file->tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioProperties *FileRef::audioProperties() const
|
|
||||||
{
|
|
||||||
if(isNull()) {
|
|
||||||
debug("FileRef::audioProperties() - Called without a valid file.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return d->file->audioProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
File *FileRef::file() const
|
|
||||||
{
|
|
||||||
return d->file;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileRef::save()
|
|
||||||
{
|
|
||||||
if(isNull()) {
|
|
||||||
debug("FileRef::save() - Called without a valid file.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return d->file->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
|
|
||||||
{
|
|
||||||
fileTypeResolvers.prepend(resolver);
|
|
||||||
return resolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringList FileRef::defaultFileExtensions()
|
|
||||||
{
|
|
||||||
StringList l;
|
|
||||||
|
|
||||||
l.append("ogg");
|
|
||||||
l.append("flac");
|
|
||||||
l.append("oga");
|
|
||||||
l.append("mp3");
|
|
||||||
l.append("mpc");
|
|
||||||
l.append("wv");
|
|
||||||
l.append("spx");
|
|
||||||
l.append("tta");
|
|
||||||
l.append("m4a");
|
|
||||||
l.append("m4r");
|
|
||||||
l.append("m4b");
|
|
||||||
l.append("m4p");
|
|
||||||
l.append("3g2");
|
|
||||||
l.append("mp4");
|
|
||||||
l.append("m4v");
|
|
||||||
l.append("wma");
|
|
||||||
l.append("asf");
|
|
||||||
l.append("aif");
|
|
||||||
l.append("aiff");
|
|
||||||
l.append("wav");
|
|
||||||
l.append("ape");
|
|
||||||
l.append("mod");
|
|
||||||
l.append("module"); // alias for "mod"
|
|
||||||
l.append("nst"); // alias for "mod"
|
|
||||||
l.append("wow"); // alias for "mod"
|
|
||||||
l.append("s3m");
|
|
||||||
l.append("it");
|
|
||||||
l.append("xm");
|
|
||||||
l.append("dsf");
|
|
||||||
l.append("dff");
|
|
||||||
l.append("dsdiff"); // alias for "dff"
|
|
||||||
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileRef::isNull() const
|
|
||||||
{
|
|
||||||
return (!d->file || !d->file->isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
FileRef &FileRef::operator=(const FileRef &ref)
|
|
||||||
{
|
|
||||||
FileRef(ref).swap(*this);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileRef::swap(FileRef &ref)
|
|
||||||
{
|
|
||||||
using std::swap;
|
|
||||||
|
|
||||||
swap(d, ref.d);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileRef::operator==(const FileRef &ref) const
|
|
||||||
{
|
|
||||||
return (ref.d->file == d->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileRef::operator!=(const FileRef &ref) const
|
|
||||||
{
|
|
||||||
return (ref.d->file != d->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
File *FileRef::create(FileName fileName, bool readAudioProperties,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle) // static
|
|
||||||
{
|
|
||||||
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void FileRef::parse(FileName fileName, bool readAudioProperties,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
|
||||||
{
|
|
||||||
// Try user-defined resolvers.
|
|
||||||
|
|
||||||
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(d->file)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Try to resolve file types based on the file extension.
|
|
||||||
|
|
||||||
d->stream = new FileStream(fileName);
|
|
||||||
d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(d->file)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// At last, try to resolve file types based on the actual content.
|
|
||||||
|
|
||||||
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(d->file)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Stream have to be closed here if failed to resolve file types.
|
|
||||||
|
|
||||||
delete d->stream;
|
|
||||||
d->stream = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileRef::parse(IOStream *stream, bool readAudioProperties,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
|
||||||
{
|
|
||||||
// User-defined resolvers won't work with a stream.
|
|
||||||
|
|
||||||
// Try to resolve file types based on the file extension.
|
|
||||||
|
|
||||||
d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
if(d->file)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// At last, try to resolve file types based on the actual content of the file.
|
|
||||||
|
|
||||||
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
|
|
||||||
}
|
|
||||||
287
3rdparty/taglib/fileref.h
vendored
287
3rdparty/taglib/fileref.h
vendored
@@ -1,287 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
|
||||||
email : wheeler@kde.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_FILEREF_H
|
|
||||||
#define TAGLIB_FILEREF_H
|
|
||||||
|
|
||||||
#include "tfile.h"
|
|
||||||
#include "tstringlist.h"
|
|
||||||
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "audioproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
class Tag;
|
|
||||||
|
|
||||||
//! This class provides a simple abstraction for creating and handling files
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* FileRef exists to provide a minimal, generic and value-based wrapper around
|
|
||||||
* a File. It is lightweight and implicitly shared, and as such suitable for
|
|
||||||
* pass-by-value use. This hides some of the uglier details of TagLib::File
|
|
||||||
* and the non-generic portions of the concrete file implementations.
|
|
||||||
*
|
|
||||||
* This class is useful in a "simple usage" situation where it is desirable
|
|
||||||
* to be able to get and set some of the tag information that is similar
|
|
||||||
* across file types.
|
|
||||||
*
|
|
||||||
* Also note that it is probably a good idea to plug this into your mime
|
|
||||||
* type system rather than using the constructor that accepts a file name using
|
|
||||||
* the FileTypeResolver.
|
|
||||||
*
|
|
||||||
* \see FileTypeResolver
|
|
||||||
* \see addFileTypeResolver()
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT FileRef
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
//! A class for pluggable file type resolution.
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This class is used to add extend TagLib's very basic file name based file
|
|
||||||
* type resolution.
|
|
||||||
*
|
|
||||||
* This can be accomplished with:
|
|
||||||
*
|
|
||||||
* \code
|
|
||||||
*
|
|
||||||
* class MyFileTypeResolver : FileTypeResolver
|
|
||||||
* {
|
|
||||||
* TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle) const
|
|
||||||
* {
|
|
||||||
* if(someCheckForAnMP3File(fileName))
|
|
||||||
* return new TagLib::MPEG::File(fileName);
|
|
||||||
* return 0;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* FileRef::addFileTypeResolver(new MyFileTypeResolver);
|
|
||||||
*
|
|
||||||
* \endcode
|
|
||||||
*
|
|
||||||
* Naturally a less contrived example would be slightly more complex. This
|
|
||||||
* can be used to plug in mime-type detection systems or to add new file types
|
|
||||||
* to TagLib.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT FileTypeResolver
|
|
||||||
{
|
|
||||||
TAGLIB_IGNORE_MISSING_DESTRUCTOR
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* This method must be overridden to provide an additional file type
|
|
||||||
* resolver. If the resolver is able to determine the file type it should
|
|
||||||
* return a valid File object; if not it should return 0.
|
|
||||||
*
|
|
||||||
* \note The created file is then owned by the FileRef and should not be
|
|
||||||
* deleted. Deletion will happen automatically when the FileRef passes
|
|
||||||
* out of scope.
|
|
||||||
*/
|
|
||||||
virtual File *createFile(FileName fileName,
|
|
||||||
bool readAudioProperties = true,
|
|
||||||
AudioProperties::ReadStyle
|
|
||||||
audioPropertiesStyle = AudioProperties::Average) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Creates a null FileRef.
|
|
||||||
*/
|
|
||||||
FileRef();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Create a FileRef from \a fileName. If \a readAudioProperties is true then
|
|
||||||
* the audio properties will be read using \a audioPropertiesStyle. If
|
|
||||||
* \a readAudioProperties is false then \a audioPropertiesStyle will be
|
|
||||||
* ignored.
|
|
||||||
*
|
|
||||||
* Also see the note in the class documentation about why you may not want to
|
|
||||||
* use this method in your application.
|
|
||||||
*/
|
|
||||||
explicit FileRef(FileName fileName,
|
|
||||||
bool readAudioProperties = true,
|
|
||||||
AudioProperties::ReadStyle
|
|
||||||
audioPropertiesStyle = AudioProperties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Construct a FileRef from an opened \a IOStream. If \a readAudioProperties
|
|
||||||
* is true then the audio properties will be read using \a audioPropertiesStyle.
|
|
||||||
* If \a readAudioProperties is false then \a audioPropertiesStyle will be
|
|
||||||
* ignored.
|
|
||||||
*
|
|
||||||
* Also see the note in the class documentation about why you may not want to
|
|
||||||
* use this method in your application.
|
|
||||||
*
|
|
||||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
|
||||||
* responsible for deleting it after the File object.
|
|
||||||
*/
|
|
||||||
explicit FileRef(IOStream* stream,
|
|
||||||
bool readAudioProperties = true,
|
|
||||||
AudioProperties::ReadStyle
|
|
||||||
audioPropertiesStyle = AudioProperties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Construct a FileRef using \a file. The FileRef now takes ownership of the
|
|
||||||
* pointer and will delete the File when it passes out of scope.
|
|
||||||
*/
|
|
||||||
explicit FileRef(File *file);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Make a copy of \a ref.
|
|
||||||
*/
|
|
||||||
FileRef(const FileRef &ref);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this FileRef instance.
|
|
||||||
*/
|
|
||||||
virtual ~FileRef();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to represented file's tag.
|
|
||||||
*
|
|
||||||
* \warning This pointer will become invalid when this FileRef and all
|
|
||||||
* copies pass out of scope.
|
|
||||||
*
|
|
||||||
* \warning Do not cast it to any subclasses of \class Tag.
|
|
||||||
* Use tag returning methods of appropriate subclasses of \class File instead.
|
|
||||||
*
|
|
||||||
* \see File::tag()
|
|
||||||
*/
|
|
||||||
Tag *tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the audio properties for this FileRef. If no audio properties
|
|
||||||
* were read then this will returns a null pointer.
|
|
||||||
*/
|
|
||||||
AudioProperties *audioProperties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to the file represented by this handler class.
|
|
||||||
*
|
|
||||||
* As a general rule this call should be avoided since if you need to work
|
|
||||||
* with file objects directly, you are probably better served instantiating
|
|
||||||
* the File subclasses (i.e. MPEG::File) manually and working with their APIs.
|
|
||||||
*
|
|
||||||
* This <i>handle</i> exists to provide a minimal, generic and value-based
|
|
||||||
* wrapper around a File. Accessing the file directly generally indicates
|
|
||||||
* a moving away from this simplicity (and into things beyond the scope of
|
|
||||||
* FileRef).
|
|
||||||
*
|
|
||||||
* \warning This pointer will become invalid when this FileRef and all
|
|
||||||
* copies pass out of scope.
|
|
||||||
*/
|
|
||||||
File *file() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Saves the file. Returns true on success.
|
|
||||||
*/
|
|
||||||
bool save();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Adds a FileTypeResolver to the list of those used by TagLib. Each
|
|
||||||
* additional FileTypeResolver is added to the front of a list of resolvers
|
|
||||||
* that are tried. If the FileTypeResolver returns zero the next resolver
|
|
||||||
* is tried.
|
|
||||||
*
|
|
||||||
* Returns a pointer to the added resolver (the same one that's passed in --
|
|
||||||
* this is mostly so that static initializers have something to use for
|
|
||||||
* assignment).
|
|
||||||
*
|
|
||||||
* \see FileTypeResolver
|
|
||||||
*/
|
|
||||||
static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* As is mentioned elsewhere in this class's documentation, the default file
|
|
||||||
* type resolution code provided by TagLib only works by comparing file
|
|
||||||
* extensions.
|
|
||||||
*
|
|
||||||
* This method returns the list of file extensions that are used by default.
|
|
||||||
*
|
|
||||||
* The extensions are all returned in lowercase, though the comparison used
|
|
||||||
* by TagLib for resolution is case-insensitive.
|
|
||||||
*
|
|
||||||
* \note This does not account for any additional file type resolvers that
|
|
||||||
* are plugged in. Also note that this is not intended to replace a proper
|
|
||||||
* mime-type resolution system, but is just here for reference.
|
|
||||||
*
|
|
||||||
* \see FileTypeResolver
|
|
||||||
*/
|
|
||||||
static StringList defaultFileExtensions();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if the file (and as such other pointers) are null.
|
|
||||||
*/
|
|
||||||
bool isNull() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Assign the file pointed to by \a ref to this FileRef.
|
|
||||||
*/
|
|
||||||
FileRef &operator=(const FileRef &ref);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Exchanges the content of the FileRef by the content of \a ref.
|
|
||||||
*/
|
|
||||||
void swap(FileRef &ref);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if this FileRef and \a ref point to the same File object.
|
|
||||||
*/
|
|
||||||
bool operator==(const FileRef &ref) const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns true if this FileRef and \a ref do not point to the same File
|
|
||||||
* object.
|
|
||||||
*/
|
|
||||||
bool operator!=(const FileRef &ref) const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* A simple implementation of file type guessing. If \a readAudioProperties
|
|
||||||
* is true then the audio properties will be read using
|
|
||||||
* \a audioPropertiesStyle. If \a readAudioProperties is false then
|
|
||||||
* \a audioPropertiesStyle will be ignored.
|
|
||||||
*
|
|
||||||
* \note You generally shouldn't use this method, but instead the constructor
|
|
||||||
* directly.
|
|
||||||
*
|
|
||||||
* \deprecated
|
|
||||||
*/
|
|
||||||
static File *create(FileName fileName,
|
|
||||||
bool readAudioProperties = true,
|
|
||||||
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
|
||||||
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
|
||||||
|
|
||||||
class FileRefPrivate;
|
|
||||||
FileRefPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace TagLib
|
|
||||||
|
|
||||||
#endif
|
|
||||||
575
3rdparty/taglib/flac/flacfile.cpp
vendored
575
3rdparty/taglib/flac/flacfile.cpp
vendored
@@ -1,575 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2003-2004 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tbytevector.h>
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tlist.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <tagunion.h>
|
|
||||||
#include <tpropertymap.h>
|
|
||||||
#include <tagutils.h>
|
|
||||||
|
|
||||||
#include <id3v2header.h>
|
|
||||||
#include <id3v2tag.h>
|
|
||||||
#include <id3v1tag.h>
|
|
||||||
#include <xiphcomment.h>
|
|
||||||
|
|
||||||
#include "flacpicture.h"
|
|
||||||
#include "flacfile.h"
|
|
||||||
#include "flacmetadatablock.h"
|
|
||||||
#include "flacunknownmetadatablock.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
typedef List<FLAC::MetadataBlock *> BlockList;
|
|
||||||
typedef BlockList::Iterator BlockIterator;
|
|
||||||
typedef BlockList::Iterator BlockConstIterator;
|
|
||||||
|
|
||||||
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
|
|
||||||
|
|
||||||
const long MinPaddingLength = 4096;
|
|
||||||
const long MaxPaddingLegnth = 1024 * 1024;
|
|
||||||
|
|
||||||
const char LastBlockFlag = '\x80';
|
|
||||||
}
|
|
||||||
|
|
||||||
class FLAC::File::FilePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
|
||||||
ID3v2FrameFactory(frameFactory),
|
|
||||||
ID3v2Location(-1),
|
|
||||||
ID3v2OriginalSize(0),
|
|
||||||
ID3v1Location(-1),
|
|
||||||
properties(0),
|
|
||||||
flacStart(0),
|
|
||||||
streamStart(0),
|
|
||||||
scanned(false)
|
|
||||||
{
|
|
||||||
blocks.setAutoDelete(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
~FilePrivate()
|
|
||||||
{
|
|
||||||
delete properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
|
||||||
long ID3v2Location;
|
|
||||||
long ID3v2OriginalSize;
|
|
||||||
|
|
||||||
long ID3v1Location;
|
|
||||||
|
|
||||||
TagUnion tag;
|
|
||||||
|
|
||||||
Properties *properties;
|
|
||||||
ByteVector xiphCommentData;
|
|
||||||
BlockList blocks;
|
|
||||||
|
|
||||||
long flacStart;
|
|
||||||
long streamStart;
|
|
||||||
bool scanned;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// static members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool FLAC::File::isSupported(IOStream *stream)
|
|
||||||
{
|
|
||||||
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
|
|
||||||
|
|
||||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
|
|
||||||
return (buffer.find("fLaC") >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
|
||||||
TagLib::File(file),
|
|
||||||
d(new FilePrivate())
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
|
||||||
bool readProperties, Properties::ReadStyle) :
|
|
||||||
TagLib::File(file),
|
|
||||||
d(new FilePrivate(frameFactory))
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
|
||||||
bool readProperties, Properties::ReadStyle) :
|
|
||||||
TagLib::File(stream),
|
|
||||||
d(new FilePrivate(frameFactory))
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::File::~File()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::Tag *FLAC::File::tag() const
|
|
||||||
{
|
|
||||||
return &d->tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap FLAC::File::properties() const
|
|
||||||
{
|
|
||||||
return d->tag.properties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
|
|
||||||
{
|
|
||||||
d->tag.removeUnsupportedProperties(unsupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
|
|
||||||
{
|
|
||||||
return xiphComment(true)->setProperties(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::Properties *FLAC::File::audioProperties() const
|
|
||||||
{
|
|
||||||
return d->properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FLAC::File::save()
|
|
||||||
{
|
|
||||||
if(readOnly()) {
|
|
||||||
debug("FLAC::File::save() - Cannot save to a read only file.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isValid()) {
|
|
||||||
debug("FLAC::File::save() -- Trying to save invalid file.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new vorbis comments
|
|
||||||
if(!hasXiphComment())
|
|
||||||
Tag::duplicate(&d->tag, xiphComment(true), false);
|
|
||||||
|
|
||||||
d->xiphCommentData = xiphComment()->render(false);
|
|
||||||
|
|
||||||
// Replace metadata blocks
|
|
||||||
|
|
||||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
|
||||||
if((*it)->code() == MetadataBlock::VorbisComment) {
|
|
||||||
// Set the new Vorbis Comment block
|
|
||||||
delete *it;
|
|
||||||
d->blocks.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d->blocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
|
|
||||||
|
|
||||||
// Render data for the metadata blocks
|
|
||||||
|
|
||||||
ByteVector data;
|
|
||||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
|
||||||
ByteVector blockData = (*it)->render();
|
|
||||||
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
|
|
||||||
blockHeader[0] = (*it)->code();
|
|
||||||
data.append(blockHeader);
|
|
||||||
data.append(blockData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the amount of padding, and append that to data.
|
|
||||||
|
|
||||||
long originalLength = d->streamStart - d->flacStart;
|
|
||||||
long paddingLength = originalLength - data.size() - 4;
|
|
||||||
|
|
||||||
if(paddingLength <= 0) {
|
|
||||||
paddingLength = MinPaddingLength;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Padding won't increase beyond 1% of the file size or 1MB.
|
|
||||||
|
|
||||||
long threshold = length() / 100;
|
|
||||||
threshold = std::max(threshold, MinPaddingLength);
|
|
||||||
threshold = std::min(threshold, MaxPaddingLegnth);
|
|
||||||
|
|
||||||
if(paddingLength > threshold)
|
|
||||||
paddingLength = MinPaddingLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector paddingHeader = ByteVector::fromUInt(paddingLength);
|
|
||||||
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
|
|
||||||
data.append(paddingHeader);
|
|
||||||
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
|
|
||||||
|
|
||||||
// Write the data to the file
|
|
||||||
|
|
||||||
insert(data, d->flacStart, originalLength);
|
|
||||||
|
|
||||||
d->streamStart += (static_cast<long>(data.size()) - originalLength);
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
d->ID3v1Location += (static_cast<long>(data.size()) - originalLength);
|
|
||||||
|
|
||||||
// Update ID3 tags
|
|
||||||
|
|
||||||
if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
|
|
||||||
|
|
||||||
// ID3v2 tag is not empty. Update the old one or create a new one.
|
|
||||||
|
|
||||||
if(d->ID3v2Location < 0)
|
|
||||||
d->ID3v2Location = 0;
|
|
||||||
|
|
||||||
data = ID3v2Tag()->render();
|
|
||||||
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
|
|
||||||
|
|
||||||
d->flacStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
|
||||||
d->streamStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
d->ID3v1Location += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
|
||||||
|
|
||||||
d->ID3v2OriginalSize = data.size();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
// ID3v2 tag is empty. Remove the old one.
|
|
||||||
|
|
||||||
if(d->ID3v2Location >= 0) {
|
|
||||||
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
|
|
||||||
|
|
||||||
d->flacStart -= d->ID3v2OriginalSize;
|
|
||||||
d->streamStart -= d->ID3v2OriginalSize;
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
d->ID3v1Location -= d->ID3v2OriginalSize;
|
|
||||||
|
|
||||||
d->ID3v2Location = -1;
|
|
||||||
d->ID3v2OriginalSize = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
|
|
||||||
|
|
||||||
// ID3v1 tag is not empty. Update the old one or create a new one.
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0) {
|
|
||||||
seek(d->ID3v1Location);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
seek(0, End);
|
|
||||||
d->ID3v1Location = tell();
|
|
||||||
}
|
|
||||||
|
|
||||||
writeBlock(ID3v1Tag()->render());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
// ID3v1 tag is empty. Remove the old one.
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0) {
|
|
||||||
truncate(d->ID3v1Location);
|
|
||||||
d->ID3v1Location = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
|
|
||||||
{
|
|
||||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create);
|
|
||||||
}
|
|
||||||
|
|
||||||
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
|
|
||||||
{
|
|
||||||
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, create);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ogg::XiphComment *FLAC::File::xiphComment(bool create)
|
|
||||||
{
|
|
||||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
|
|
||||||
{
|
|
||||||
d->ID3v2FrameFactory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector FLAC::File::streamInfoData()
|
|
||||||
{
|
|
||||||
debug("FLAC::File::streamInfoData() -- This function is obsolete. Returning an empty ByteVector.");
|
|
||||||
return ByteVector();
|
|
||||||
}
|
|
||||||
|
|
||||||
long FLAC::File::streamLength()
|
|
||||||
{
|
|
||||||
debug("FLAC::File::streamLength() -- This function is obsolete. Returning zero.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<FLAC::Picture *> FLAC::File::pictureList()
|
|
||||||
{
|
|
||||||
List<Picture *> pictures;
|
|
||||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
|
||||||
Picture *picture = dynamic_cast<Picture *>(*it);
|
|
||||||
if(picture) {
|
|
||||||
pictures.append(picture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pictures;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::File::addPicture(Picture *picture)
|
|
||||||
{
|
|
||||||
d->blocks.append(picture);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::File::removePicture(Picture *picture, bool del)
|
|
||||||
{
|
|
||||||
BlockIterator it = d->blocks.find(picture);
|
|
||||||
if(it != d->blocks.end())
|
|
||||||
d->blocks.erase(it);
|
|
||||||
|
|
||||||
if(del)
|
|
||||||
delete picture;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::File::removePictures()
|
|
||||||
{
|
|
||||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
|
|
||||||
if(dynamic_cast<Picture *>(*it)) {
|
|
||||||
delete *it;
|
|
||||||
it = d->blocks.erase(it);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::File::strip(int tags)
|
|
||||||
{
|
|
||||||
if(tags & ID3v1)
|
|
||||||
d->tag.set(FlacID3v1Index, 0);
|
|
||||||
|
|
||||||
if(tags & ID3v2)
|
|
||||||
d->tag.set(FlacID3v2Index, 0);
|
|
||||||
|
|
||||||
if(tags & XiphComment) {
|
|
||||||
xiphComment()->removeAllFields();
|
|
||||||
xiphComment()->removeAllPictures();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FLAC::File::hasXiphComment() const
|
|
||||||
{
|
|
||||||
return !d->xiphCommentData.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FLAC::File::hasID3v1Tag() const
|
|
||||||
{
|
|
||||||
return (d->ID3v1Location >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FLAC::File::hasID3v2Tag() const
|
|
||||||
{
|
|
||||||
return (d->ID3v2Location >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void FLAC::File::read(bool readProperties)
|
|
||||||
{
|
|
||||||
// Look for an ID3v2 tag
|
|
||||||
|
|
||||||
d->ID3v2Location = Utils::findID3v2(this);
|
|
||||||
|
|
||||||
if(d->ID3v2Location >= 0) {
|
|
||||||
d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
|
|
||||||
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for an ID3v1 tag
|
|
||||||
|
|
||||||
d->ID3v1Location = Utils::findID3v1(this);
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
|
||||||
|
|
||||||
// Look for FLAC metadata, including vorbis comments
|
|
||||||
|
|
||||||
scan();
|
|
||||||
|
|
||||||
if(!isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!d->xiphCommentData.isEmpty())
|
|
||||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData));
|
|
||||||
else
|
|
||||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment());
|
|
||||||
|
|
||||||
if(readProperties) {
|
|
||||||
|
|
||||||
// First block should be the stream_info metadata
|
|
||||||
|
|
||||||
const ByteVector infoData = d->blocks.front()->render();
|
|
||||||
|
|
||||||
long streamLength;
|
|
||||||
|
|
||||||
if(d->ID3v1Location >= 0)
|
|
||||||
streamLength = d->ID3v1Location - d->streamStart;
|
|
||||||
else
|
|
||||||
streamLength = length() - d->streamStart;
|
|
||||||
|
|
||||||
d->properties = new Properties(infoData, streamLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::File::scan()
|
|
||||||
{
|
|
||||||
// Scan the metadata pages
|
|
||||||
|
|
||||||
if(d->scanned)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
long nextBlockOffset;
|
|
||||||
|
|
||||||
if(d->ID3v2Location >= 0)
|
|
||||||
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
|
|
||||||
else
|
|
||||||
nextBlockOffset = find("fLaC");
|
|
||||||
|
|
||||||
if(nextBlockOffset < 0) {
|
|
||||||
debug("FLAC::File::scan() -- FLAC stream not found");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextBlockOffset += 4;
|
|
||||||
d->flacStart = nextBlockOffset;
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
|
|
||||||
seek(nextBlockOffset);
|
|
||||||
const ByteVector header = readBlock(4);
|
|
||||||
|
|
||||||
// Header format (from spec):
|
|
||||||
// <1> Last-metadata-block flag
|
|
||||||
// <7> BLOCK_TYPE
|
|
||||||
// 0 : STREAMINFO
|
|
||||||
// 1 : PADDING
|
|
||||||
// ..
|
|
||||||
// 4 : VORBIS_COMMENT
|
|
||||||
// ..
|
|
||||||
// 6 : PICTURE
|
|
||||||
// ..
|
|
||||||
// <24> Length of metadata to follow
|
|
||||||
|
|
||||||
const char blockType = header[0] & ~LastBlockFlag;
|
|
||||||
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
|
|
||||||
const unsigned int blockLength = header.toUInt(1U, 3U);
|
|
||||||
|
|
||||||
// First block should be the stream_info metadata
|
|
||||||
|
|
||||||
if(d->blocks.isEmpty() && blockType != MetadataBlock::StreamInfo) {
|
|
||||||
debug("FLAC::File::scan() -- First block should be the stream_info metadata");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(blockLength == 0
|
|
||||||
&& blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable)
|
|
||||||
{
|
|
||||||
debug("FLAC::File::scan() -- Zero-sized metadata block found");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ByteVector data = readBlock(blockLength);
|
|
||||||
if(data.size() != blockLength) {
|
|
||||||
debug("FLAC::File::scan() -- Failed to read a metadata block");
|
|
||||||
setValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetadataBlock *block = 0;
|
|
||||||
|
|
||||||
// Found the vorbis-comment
|
|
||||||
if(blockType == MetadataBlock::VorbisComment) {
|
|
||||||
if(d->xiphCommentData.isEmpty()) {
|
|
||||||
d->xiphCommentData = data;
|
|
||||||
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(blockType == MetadataBlock::Picture) {
|
|
||||||
FLAC::Picture *picture = new FLAC::Picture();
|
|
||||||
if(picture->parse(data)) {
|
|
||||||
block = picture;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug("FLAC::File::scan() -- invalid picture found, discarding");
|
|
||||||
delete picture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(blockType == MetadataBlock::Padding) {
|
|
||||||
// Skip all padding blocks.
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
block = new UnknownMetadataBlock(blockType, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(block)
|
|
||||||
d->blocks.append(block);
|
|
||||||
|
|
||||||
nextBlockOffset += blockLength + 4;
|
|
||||||
|
|
||||||
if(isLastBlock)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of metadata, now comes the datastream
|
|
||||||
|
|
||||||
d->streamStart = nextBlockOffset;
|
|
||||||
|
|
||||||
d->scanned = true;
|
|
||||||
}
|
|
||||||
343
3rdparty/taglib/flac/flacfile.h
vendored
343
3rdparty/taglib/flac/flacfile.h
vendored
@@ -1,343 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2003 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_FLACFILE_H
|
|
||||||
#define TAGLIB_FLACFILE_H
|
|
||||||
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "tfile.h"
|
|
||||||
#include "tlist.h"
|
|
||||||
#include "tag.h"
|
|
||||||
|
|
||||||
#include "flacpicture.h"
|
|
||||||
#include "flacproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
class Tag;
|
|
||||||
namespace ID3v2 { class FrameFactory; class Tag; }
|
|
||||||
namespace ID3v1 { class Tag; }
|
|
||||||
namespace Ogg { class XiphComment; }
|
|
||||||
|
|
||||||
//! An implementation of FLAC metadata
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This is implementation of FLAC metadata for non-Ogg FLAC files. At some
|
|
||||||
* point when Ogg / FLAC is more common there will be a similar implementation
|
|
||||||
* under the Ogg hierarchy.
|
|
||||||
*
|
|
||||||
* This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream
|
|
||||||
* properties from the file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FLAC {
|
|
||||||
|
|
||||||
//! An implementation of TagLib::File with FLAC specific methods
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This implements and provides an interface for FLAC files to the
|
|
||||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
|
||||||
* the abstract TagLib::File API as well as providing some additional
|
|
||||||
* information specific to FLAC files.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT File : public TagLib::File
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* This set of flags is used for various operations and is suitable for
|
|
||||||
* being OR-ed together.
|
|
||||||
*/
|
|
||||||
enum TagTypes {
|
|
||||||
//! Empty set. Matches no tag types.
|
|
||||||
NoTags = 0x0000,
|
|
||||||
//! Matches Vorbis comments.
|
|
||||||
XiphComment = 0x0001,
|
|
||||||
//! Matches ID3v1 tags.
|
|
||||||
ID3v1 = 0x0002,
|
|
||||||
//! Matches ID3v2 tags.
|
|
||||||
ID3v2 = 0x0004,
|
|
||||||
//! Matches all tag types.
|
|
||||||
AllTags = 0xffff
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs a FLAC file from \a file. If \a readProperties is true the
|
|
||||||
* file's audio properties will also be read.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
|
||||||
*
|
|
||||||
* \deprecated This constructor will be dropped in favor of the one below
|
|
||||||
* in a future version.
|
|
||||||
*/
|
|
||||||
File(FileName file, bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs an FLAC file from \a file. If \a readProperties is true the
|
|
||||||
* file's audio properties will also be read.
|
|
||||||
*
|
|
||||||
* If this file contains and ID3v2 tag the frames will be created using
|
|
||||||
* \a frameFactory.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
|
||||||
*/
|
|
||||||
// BIC: merge with the above constructor
|
|
||||||
File(FileName file, ID3v2::FrameFactory *frameFactory,
|
|
||||||
bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs a FLAC file from \a stream. If \a readProperties is true the
|
|
||||||
* file's audio properties will also be read.
|
|
||||||
*
|
|
||||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
|
||||||
* responsible for deleting it after the File object.
|
|
||||||
*
|
|
||||||
* If this file contains and ID3v2 tag the frames will be created using
|
|
||||||
* \a frameFactory.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
|
||||||
*/
|
|
||||||
// BIC: merge with the above constructor
|
|
||||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
|
||||||
bool readProperties = true,
|
|
||||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this instance of the File.
|
|
||||||
*/
|
|
||||||
virtual ~File();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the Tag for this file. This will be a union of XiphComment,
|
|
||||||
* ID3v1 and ID3v2 tags.
|
|
||||||
*
|
|
||||||
* \see ID3v2Tag()
|
|
||||||
* \see ID3v1Tag()
|
|
||||||
* \see XiphComment()
|
|
||||||
*/
|
|
||||||
virtual TagLib::Tag *tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- export function.
|
|
||||||
* If the file contains more than one tag (e.g. XiphComment and ID3v1),
|
|
||||||
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
|
|
||||||
* converted to the PropertyMap.
|
|
||||||
*/
|
|
||||||
PropertyMap properties() const;
|
|
||||||
|
|
||||||
void removeUnsupportedProperties(const StringList &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Implements the unified property interface -- import function.
|
|
||||||
* This always creates a Xiph comment, if none exists. The return value
|
|
||||||
* relates to the Xiph comment only.
|
|
||||||
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
|
|
||||||
* in the FLAC specification.
|
|
||||||
*/
|
|
||||||
PropertyMap setProperties(const PropertyMap &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the FLAC::Properties for this file. If no audio properties
|
|
||||||
* were read then this will return a null pointer.
|
|
||||||
*/
|
|
||||||
virtual Properties *audioProperties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Save the file. This will primarily save the XiphComment, but
|
|
||||||
* will also keep any old ID3-tags up to date. If the file
|
|
||||||
* has no XiphComment, one will be constructed from the ID3-tags.
|
|
||||||
*
|
|
||||||
* This returns true if the save was successful.
|
|
||||||
*/
|
|
||||||
virtual bool save();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to the ID3v2 tag of the file.
|
|
||||||
*
|
|
||||||
* If \a create is false (the default) this returns a null pointer
|
|
||||||
* if there is no valid ID3v2 tag. If \a create is true it will create
|
|
||||||
* an ID3v2 tag if one does not exist and returns a valid pointer.
|
|
||||||
*
|
|
||||||
* \note This may return a valid pointer regardless of whether or not the
|
|
||||||
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
|
|
||||||
* on disk actually has an ID3v2 tag.
|
|
||||||
*
|
|
||||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
|
||||||
* deleted by the user. It will be deleted when the file (object) is
|
|
||||||
* destroyed.
|
|
||||||
*
|
|
||||||
* \see hasID3v2Tag()
|
|
||||||
*/
|
|
||||||
ID3v2::Tag *ID3v2Tag(bool create = false);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to the ID3v1 tag of the file.
|
|
||||||
*
|
|
||||||
* If \a create is false (the default) this returns a null pointer
|
|
||||||
* if there is no valid APE tag. If \a create is true it will create
|
|
||||||
* an APE tag if one does not exist and returns a valid pointer.
|
|
||||||
*
|
|
||||||
* \note This may return a valid pointer regardless of whether or not the
|
|
||||||
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
|
|
||||||
* on disk actually has an ID3v1 tag.
|
|
||||||
*
|
|
||||||
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
|
|
||||||
* deleted by the user. It will be deleted when the file (object) is
|
|
||||||
* destroyed.
|
|
||||||
*
|
|
||||||
* \see hasID3v1Tag()
|
|
||||||
*/
|
|
||||||
ID3v1::Tag *ID3v1Tag(bool create = false);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a pointer to the XiphComment for the file.
|
|
||||||
*
|
|
||||||
* If \a create is false (the default) this returns a null pointer
|
|
||||||
* if there is no valid XiphComment. If \a create is true it will create
|
|
||||||
* a XiphComment if one does not exist and returns a valid pointer.
|
|
||||||
*
|
|
||||||
* \note This may return a valid pointer regardless of whether or not the
|
|
||||||
* file on disk has a XiphComment. Use hasXiphComment() to check if the
|
|
||||||
* file on disk actually has a XiphComment.
|
|
||||||
*
|
|
||||||
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
|
|
||||||
* deleted by the user. It will be deleted when the file (object) is
|
|
||||||
* destroyed.
|
|
||||||
*
|
|
||||||
* \see hasXiphComment()
|
|
||||||
*/
|
|
||||||
Ogg::XiphComment *xiphComment(bool create = false);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set the ID3v2::FrameFactory to something other than the default. This
|
|
||||||
* can be used to specify the way that ID3v2 frames will be interpreted
|
|
||||||
* when
|
|
||||||
*
|
|
||||||
* \see ID3v2FrameFactory
|
|
||||||
* \deprecated This value should be passed in via the constructor
|
|
||||||
*/
|
|
||||||
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the block of data used by FLAC::Properties for parsing the
|
|
||||||
* stream properties.
|
|
||||||
*
|
|
||||||
* \deprecated Always returns an empty vector.
|
|
||||||
*/
|
|
||||||
ByteVector streamInfoData(); // BIC: remove
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the audio-stream, used by FLAC::Properties for
|
|
||||||
* calculating the bitrate.
|
|
||||||
*
|
|
||||||
* \deprecated Always returns zero.
|
|
||||||
*/
|
|
||||||
long streamLength(); // BIC: remove
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a list of pictures attached to the FLAC file.
|
|
||||||
*/
|
|
||||||
List<Picture *> pictureList();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Removes an attached picture. If \a del is true the picture's memory
|
|
||||||
* will be freed; if it is false, it must be deleted by the user.
|
|
||||||
*/
|
|
||||||
void removePicture(Picture *picture, bool del = true);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Remove all attached images.
|
|
||||||
*/
|
|
||||||
void removePictures();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Add a new picture to the file. The file takes ownership of the
|
|
||||||
* picture and will handle freeing its memory.
|
|
||||||
*
|
|
||||||
* \note The file will be saved only after calling save().
|
|
||||||
*/
|
|
||||||
void addPicture(Picture *picture);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This will remove the tags that match the OR-ed together TagTypes from
|
|
||||||
* the file. By default it removes all tags.
|
|
||||||
*
|
|
||||||
* \warning This will also invalidate pointers to the tags as their memory
|
|
||||||
* will be freed.
|
|
||||||
*
|
|
||||||
* \note In order to make the removal permanent save() still needs to be
|
|
||||||
* called.
|
|
||||||
*
|
|
||||||
* \note This won't remove the Vorbis comment block completely. The
|
|
||||||
* vendor ID will be preserved.
|
|
||||||
*/
|
|
||||||
void strip(int tags = AllTags);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the file on disk actually has a XiphComment.
|
|
||||||
*
|
|
||||||
* \see xiphComment()
|
|
||||||
*/
|
|
||||||
bool hasXiphComment() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
|
||||||
*
|
|
||||||
* \see ID3v1Tag()
|
|
||||||
*/
|
|
||||||
bool hasID3v1Tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
|
||||||
*
|
|
||||||
* \see ID3v2Tag()
|
|
||||||
*/
|
|
||||||
bool hasID3v2Tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns whether or not the given \a stream can be opened as a FLAC
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* \note This method is designed to do a quick check. The result may
|
|
||||||
* not necessarily be correct.
|
|
||||||
*/
|
|
||||||
static bool isSupported(IOStream *stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
File(const File &);
|
|
||||||
File &operator=(const File &);
|
|
||||||
|
|
||||||
void read(bool readProperties);
|
|
||||||
void scan();
|
|
||||||
|
|
||||||
class FilePrivate;
|
|
||||||
FilePrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
47
3rdparty/taglib/flac/flacmetadatablock.cpp
vendored
47
3rdparty/taglib/flac/flacmetadatablock.cpp
vendored
@@ -1,47 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2010 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <taglib.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include "flacmetadatablock.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class FLAC::MetadataBlock::MetadataBlockPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MetadataBlockPrivate() {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
FLAC::MetadataBlock::MetadataBlock()
|
|
||||||
{
|
|
||||||
d = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::MetadataBlock::~MetadataBlock()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
75
3rdparty/taglib/flac/flacmetadatablock.h
vendored
75
3rdparty/taglib/flac/flacmetadatablock.h
vendored
@@ -1,75 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2010 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_FLACMETADATABLOCK_H
|
|
||||||
#define TAGLIB_FLACMETADATABLOCK_H
|
|
||||||
|
|
||||||
#include "tlist.h"
|
|
||||||
#include "tbytevector.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace FLAC {
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT MetadataBlock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MetadataBlock();
|
|
||||||
virtual ~MetadataBlock();
|
|
||||||
|
|
||||||
enum BlockType {
|
|
||||||
StreamInfo = 0,
|
|
||||||
Padding,
|
|
||||||
Application,
|
|
||||||
SeekTable,
|
|
||||||
VorbisComment,
|
|
||||||
CueSheet,
|
|
||||||
Picture
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the FLAC metadata block type.
|
|
||||||
*/
|
|
||||||
virtual int code() const = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Render the content of the block.
|
|
||||||
*/
|
|
||||||
virtual ByteVector render() const = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
MetadataBlock(const MetadataBlock &item);
|
|
||||||
MetadataBlock &operator=(const MetadataBlock &item);
|
|
||||||
|
|
||||||
class MetadataBlockPrivate;
|
|
||||||
MetadataBlockPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
217
3rdparty/taglib/flac/flacpicture.cpp
vendored
217
3rdparty/taglib/flac/flacpicture.cpp
vendored
@@ -1,217 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2010 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <taglib.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include "flacpicture.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class FLAC::Picture::PicturePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PicturePrivate() :
|
|
||||||
type(FLAC::Picture::Other),
|
|
||||||
width(0),
|
|
||||||
height(0),
|
|
||||||
colorDepth(0),
|
|
||||||
numColors(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Type type;
|
|
||||||
String mimeType;
|
|
||||||
String description;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
int colorDepth;
|
|
||||||
int numColors;
|
|
||||||
ByteVector data;
|
|
||||||
};
|
|
||||||
|
|
||||||
FLAC::Picture::Picture() :
|
|
||||||
d(new PicturePrivate())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::Picture::Picture(const ByteVector &data) :
|
|
||||||
d(new PicturePrivate())
|
|
||||||
{
|
|
||||||
parse(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::Picture::~Picture()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Picture::code() const
|
|
||||||
{
|
|
||||||
return FLAC::MetadataBlock::Picture;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FLAC::Picture::parse(const ByteVector &data)
|
|
||||||
{
|
|
||||||
if(data.size() < 32) {
|
|
||||||
debug("A picture block must contain at least 5 bytes.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int pos = 0;
|
|
||||||
d->type = FLAC::Picture::Type(data.toUInt(pos));
|
|
||||||
pos += 4;
|
|
||||||
unsigned int mimeTypeLength = data.toUInt(pos);
|
|
||||||
pos += 4;
|
|
||||||
if(pos + mimeTypeLength + 24 > data.size()) {
|
|
||||||
debug("Invalid picture block.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
|
|
||||||
pos += mimeTypeLength;
|
|
||||||
unsigned int descriptionLength = data.toUInt(pos);
|
|
||||||
pos += 4;
|
|
||||||
if(pos + descriptionLength + 20 > data.size()) {
|
|
||||||
debug("Invalid picture block.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
|
|
||||||
pos += descriptionLength;
|
|
||||||
d->width = data.toUInt(pos);
|
|
||||||
pos += 4;
|
|
||||||
d->height = data.toUInt(pos);
|
|
||||||
pos += 4;
|
|
||||||
d->colorDepth = data.toUInt(pos);
|
|
||||||
pos += 4;
|
|
||||||
d->numColors = data.toUInt(pos);
|
|
||||||
pos += 4;
|
|
||||||
unsigned int dataLength = data.toUInt(pos);
|
|
||||||
pos += 4;
|
|
||||||
if(pos + dataLength > data.size()) {
|
|
||||||
debug("Invalid picture block.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
d->data = data.mid(pos, dataLength);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector FLAC::Picture::render() const
|
|
||||||
{
|
|
||||||
ByteVector result;
|
|
||||||
result.append(ByteVector::fromUInt(d->type));
|
|
||||||
ByteVector mimeTypeData = d->mimeType.data(String::UTF8);
|
|
||||||
result.append(ByteVector::fromUInt(mimeTypeData.size()));
|
|
||||||
result.append(mimeTypeData);
|
|
||||||
ByteVector descriptionData = d->description.data(String::UTF8);
|
|
||||||
result.append(ByteVector::fromUInt(descriptionData.size()));
|
|
||||||
result.append(descriptionData);
|
|
||||||
result.append(ByteVector::fromUInt(d->width));
|
|
||||||
result.append(ByteVector::fromUInt(d->height));
|
|
||||||
result.append(ByteVector::fromUInt(d->colorDepth));
|
|
||||||
result.append(ByteVector::fromUInt(d->numColors));
|
|
||||||
result.append(ByteVector::fromUInt(d->data.size()));
|
|
||||||
result.append(d->data);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::Picture::Type FLAC::Picture::type() const
|
|
||||||
{
|
|
||||||
return d->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::Picture::setType(FLAC::Picture::Type type)
|
|
||||||
{
|
|
||||||
d->type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
String FLAC::Picture::mimeType() const
|
|
||||||
{
|
|
||||||
return d->mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::Picture::setMimeType(const String &mimeType)
|
|
||||||
{
|
|
||||||
d->mimeType = mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
String FLAC::Picture::description() const
|
|
||||||
{
|
|
||||||
return d->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::Picture::setDescription(const String &description)
|
|
||||||
{
|
|
||||||
d->description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Picture::width() const
|
|
||||||
{
|
|
||||||
return d->width;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::Picture::setWidth(int width)
|
|
||||||
{
|
|
||||||
d->width = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Picture::height() const
|
|
||||||
{
|
|
||||||
return d->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::Picture::setHeight(int height)
|
|
||||||
{
|
|
||||||
d->height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Picture::colorDepth() const
|
|
||||||
{
|
|
||||||
return d->colorDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::Picture::setColorDepth(int colorDepth)
|
|
||||||
{
|
|
||||||
d->colorDepth = colorDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Picture::numColors() const
|
|
||||||
{
|
|
||||||
return d->numColors;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::Picture::setNumColors(int numColors)
|
|
||||||
{
|
|
||||||
d->numColors = numColors;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector FLAC::Picture::data() const
|
|
||||||
{
|
|
||||||
return d->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::Picture::setData(const ByteVector &data)
|
|
||||||
{
|
|
||||||
d->data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
208
3rdparty/taglib/flac/flacpicture.h
vendored
208
3rdparty/taglib/flac/flacpicture.h
vendored
@@ -1,208 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2010 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_FLACPICTURE_H
|
|
||||||
#define TAGLIB_FLACPICTURE_H
|
|
||||||
|
|
||||||
#include "tlist.h"
|
|
||||||
#include "tstring.h"
|
|
||||||
#include "tbytevector.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "flacmetadatablock.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace FLAC {
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Picture : public MetadataBlock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This describes the function or content of the picture.
|
|
||||||
*/
|
|
||||||
enum Type {
|
|
||||||
//! A type not enumerated below
|
|
||||||
Other = 0x00,
|
|
||||||
//! 32x32 PNG image that should be used as the file icon
|
|
||||||
FileIcon = 0x01,
|
|
||||||
//! File icon of a different size or format
|
|
||||||
OtherFileIcon = 0x02,
|
|
||||||
//! Front cover image of the album
|
|
||||||
FrontCover = 0x03,
|
|
||||||
//! Back cover image of the album
|
|
||||||
BackCover = 0x04,
|
|
||||||
//! Inside leaflet page of the album
|
|
||||||
LeafletPage = 0x05,
|
|
||||||
//! Image from the album itself
|
|
||||||
Media = 0x06,
|
|
||||||
//! Picture of the lead artist or soloist
|
|
||||||
LeadArtist = 0x07,
|
|
||||||
//! Picture of the artist or performer
|
|
||||||
Artist = 0x08,
|
|
||||||
//! Picture of the conductor
|
|
||||||
Conductor = 0x09,
|
|
||||||
//! Picture of the band or orchestra
|
|
||||||
Band = 0x0A,
|
|
||||||
//! Picture of the composer
|
|
||||||
Composer = 0x0B,
|
|
||||||
//! Picture of the lyricist or text writer
|
|
||||||
Lyricist = 0x0C,
|
|
||||||
//! Picture of the recording location or studio
|
|
||||||
RecordingLocation = 0x0D,
|
|
||||||
//! Picture of the artists during recording
|
|
||||||
DuringRecording = 0x0E,
|
|
||||||
//! Picture of the artists during performance
|
|
||||||
DuringPerformance = 0x0F,
|
|
||||||
//! Picture from a movie or video related to the track
|
|
||||||
MovieScreenCapture = 0x10,
|
|
||||||
//! Picture of a large, coloured fish
|
|
||||||
ColouredFish = 0x11,
|
|
||||||
//! Illustration related to the track
|
|
||||||
Illustration = 0x12,
|
|
||||||
//! Logo of the band or performer
|
|
||||||
BandLogo = 0x13,
|
|
||||||
//! Logo of the publisher (record company)
|
|
||||||
PublisherLogo = 0x14
|
|
||||||
};
|
|
||||||
|
|
||||||
Picture();
|
|
||||||
Picture(const ByteVector &data);
|
|
||||||
~Picture();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the type of the image.
|
|
||||||
*/
|
|
||||||
Type type() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the type of the image.
|
|
||||||
*/
|
|
||||||
void setType(Type type);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the mime type of the image. This should in most cases be
|
|
||||||
* "image/png" or "image/jpeg".
|
|
||||||
*/
|
|
||||||
String mimeType() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the mime type of the image. This should in most cases be
|
|
||||||
* "image/png" or "image/jpeg".
|
|
||||||
*/
|
|
||||||
void setMimeType(const String &m);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns a text description of the image.
|
|
||||||
*/
|
|
||||||
|
|
||||||
String description() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets a textual description of the image to \a desc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void setDescription(const String &desc);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the width of the image.
|
|
||||||
*/
|
|
||||||
int width() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the width of the image.
|
|
||||||
*/
|
|
||||||
void setWidth(int w);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the height of the image.
|
|
||||||
*/
|
|
||||||
int height() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the height of the image.
|
|
||||||
*/
|
|
||||||
void setHeight(int h);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the color depth (in bits-per-pixel) of the image.
|
|
||||||
*/
|
|
||||||
int colorDepth() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the color depth (in bits-per-pixel) of the image.
|
|
||||||
*/
|
|
||||||
void setColorDepth(int depth);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of colors used on the image..
|
|
||||||
*/
|
|
||||||
int numColors() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the number of colors used on the image (for indexed images).
|
|
||||||
*/
|
|
||||||
void setNumColors(int numColors);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the image data.
|
|
||||||
*/
|
|
||||||
ByteVector data() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the image data.
|
|
||||||
*/
|
|
||||||
void setData(const ByteVector &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the FLAC metadata block type.
|
|
||||||
*/
|
|
||||||
int code() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Render the content to the FLAC picture block format.
|
|
||||||
*/
|
|
||||||
ByteVector render() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Parse the picture data in the FLAC picture block format.
|
|
||||||
*/
|
|
||||||
bool parse(const ByteVector &rawData);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Picture(const Picture &item);
|
|
||||||
Picture &operator=(const Picture &item);
|
|
||||||
|
|
||||||
class PicturePrivate;
|
|
||||||
PicturePrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef List<Picture> PictureList;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
176
3rdparty/taglib/flac/flacproperties.cpp
vendored
176
3rdparty/taglib/flac/flacproperties.cpp
vendored
@@ -1,176 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2003 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <tstring.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
|
|
||||||
#include "flacproperties.h"
|
|
||||||
#include "flacfile.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class FLAC::Properties::PropertiesPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PropertiesPrivate() :
|
|
||||||
length(0),
|
|
||||||
bitrate(0),
|
|
||||||
sampleRate(0),
|
|
||||||
bitsPerSample(0),
|
|
||||||
channels(0),
|
|
||||||
sampleFrames(0) {}
|
|
||||||
|
|
||||||
int length;
|
|
||||||
int bitrate;
|
|
||||||
int sampleRate;
|
|
||||||
int bitsPerSample;
|
|
||||||
int channels;
|
|
||||||
unsigned long long sampleFrames;
|
|
||||||
ByteVector signature;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// public members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
|
|
||||||
AudioProperties(style),
|
|
||||||
d(new PropertiesPrivate())
|
|
||||||
{
|
|
||||||
read(data, streamLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::Properties::Properties(File *, ReadStyle style) :
|
|
||||||
AudioProperties(style),
|
|
||||||
d(new PropertiesPrivate())
|
|
||||||
{
|
|
||||||
debug("FLAC::Properties::Properties() - This constructor is no longer used.");
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::Properties::~Properties()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Properties::length() const
|
|
||||||
{
|
|
||||||
return lengthInSeconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Properties::lengthInSeconds() const
|
|
||||||
{
|
|
||||||
return d->length / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Properties::lengthInMilliseconds() const
|
|
||||||
{
|
|
||||||
return d->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Properties::bitrate() const
|
|
||||||
{
|
|
||||||
return d->bitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Properties::sampleRate() const
|
|
||||||
{
|
|
||||||
return d->sampleRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Properties::bitsPerSample() const
|
|
||||||
{
|
|
||||||
return d->bitsPerSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Properties::sampleWidth() const
|
|
||||||
{
|
|
||||||
return bitsPerSample();
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::Properties::channels() const
|
|
||||||
{
|
|
||||||
return d->channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long FLAC::Properties::sampleFrames() const
|
|
||||||
{
|
|
||||||
return d->sampleFrames;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector FLAC::Properties::signature() const
|
|
||||||
{
|
|
||||||
return d->signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// private members
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
|
||||||
{
|
|
||||||
if(data.size() < 18) {
|
|
||||||
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int pos = 0;
|
|
||||||
|
|
||||||
// Minimum block size (in samples)
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
// Maximum block size (in samples)
|
|
||||||
pos += 2;
|
|
||||||
|
|
||||||
// Minimum frame size (in bytes)
|
|
||||||
pos += 3;
|
|
||||||
|
|
||||||
// Maximum frame size (in bytes)
|
|
||||||
pos += 3;
|
|
||||||
|
|
||||||
const unsigned int flags = data.toUInt(pos, true);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
d->sampleRate = flags >> 12;
|
|
||||||
d->channels = ((flags >> 9) & 7) + 1;
|
|
||||||
d->bitsPerSample = ((flags >> 4) & 31) + 1;
|
|
||||||
|
|
||||||
// The last 4 bits are the most significant 4 bits for the 36 bit
|
|
||||||
// stream length in samples. (Audio files measured in days)
|
|
||||||
|
|
||||||
const unsigned long long hi = flags & 0xf;
|
|
||||||
const unsigned long long lo = data.toUInt(pos, true);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
d->sampleFrames = (hi << 32) | lo;
|
|
||||||
|
|
||||||
if(d->sampleFrames > 0 && d->sampleRate > 0) {
|
|
||||||
const double length = d->sampleFrames * 1000.0 / d->sampleRate;
|
|
||||||
d->length = static_cast<int>(length + 0.5);
|
|
||||||
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(data.size() >= pos + 16)
|
|
||||||
d->signature = data.mid(pos, 16);
|
|
||||||
}
|
|
||||||
148
3rdparty/taglib/flac/flacproperties.h
vendored
148
3rdparty/taglib/flac/flacproperties.h
vendored
@@ -1,148 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2003 by Allan Sandfeld Jensen
|
|
||||||
email : kde@carewolf.org
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_FLACPROPERTIES_H
|
|
||||||
#define TAGLIB_FLACPROPERTIES_H
|
|
||||||
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "audioproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace FLAC {
|
|
||||||
|
|
||||||
class File;
|
|
||||||
|
|
||||||
//! An implementation of audio property reading for FLAC
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* This reads the data from an FLAC stream found in the AudioProperties
|
|
||||||
* API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Create an instance of FLAC::Properties with the data read from the
|
|
||||||
* ByteVector \a data.
|
|
||||||
*/
|
|
||||||
// BIC: switch to const reference
|
|
||||||
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Create an instance of FLAC::Properties with the data read from the
|
|
||||||
* FLAC::File \a file.
|
|
||||||
*/
|
|
||||||
// BIC: remove
|
|
||||||
Properties(File *file, ReadStyle style = Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this FLAC::Properties instance.
|
|
||||||
*/
|
|
||||||
virtual ~Properties();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in seconds. The length is rounded down to
|
|
||||||
* the nearest whole second.
|
|
||||||
*
|
|
||||||
* \note This method is just an alias of lengthInSeconds().
|
|
||||||
*
|
|
||||||
* \deprecated
|
|
||||||
*/
|
|
||||||
virtual int length() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in seconds. The length is rounded down to
|
|
||||||
* the nearest whole second.
|
|
||||||
*
|
|
||||||
* \see lengthInMilliseconds()
|
|
||||||
*/
|
|
||||||
// BIC: make virtual
|
|
||||||
int lengthInSeconds() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the file in milliseconds.
|
|
||||||
*
|
|
||||||
* \see lengthInSeconds()
|
|
||||||
*/
|
|
||||||
// BIC: make virtual
|
|
||||||
int lengthInMilliseconds() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the average bit rate of the file in kb/s.
|
|
||||||
*/
|
|
||||||
virtual int bitrate() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the sample rate in Hz.
|
|
||||||
*/
|
|
||||||
virtual int sampleRate() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of audio channels.
|
|
||||||
*/
|
|
||||||
virtual int channels() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the number of bits per audio sample as read from the FLAC
|
|
||||||
* identification header.
|
|
||||||
*/
|
|
||||||
int bitsPerSample() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the sample width as read from the FLAC identification
|
|
||||||
* header.
|
|
||||||
*
|
|
||||||
* \note This method is just an alias of bitsPerSample().
|
|
||||||
*
|
|
||||||
* \deprecated
|
|
||||||
*/
|
|
||||||
int sampleWidth() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Return the number of sample frames.
|
|
||||||
*/
|
|
||||||
unsigned long long sampleFrames() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the MD5 signature of the uncompressed audio stream as read
|
|
||||||
* from the stream info header.
|
|
||||||
*/
|
|
||||||
ByteVector signature() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Properties(const Properties &);
|
|
||||||
Properties &operator=(const Properties &);
|
|
||||||
|
|
||||||
void read(const ByteVector &data, long streamLength);
|
|
||||||
|
|
||||||
class PropertiesPrivate;
|
|
||||||
PropertiesPrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2010 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#include <taglib.h>
|
|
||||||
#include <tdebug.h>
|
|
||||||
#include <tstring.h>
|
|
||||||
#include "flacunknownmetadatablock.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
|
|
||||||
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
UnknownMetadataBlockPrivate() : code(0) {}
|
|
||||||
|
|
||||||
int code;
|
|
||||||
ByteVector data;
|
|
||||||
};
|
|
||||||
|
|
||||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
|
|
||||||
d(new UnknownMetadataBlockPrivate())
|
|
||||||
{
|
|
||||||
d->code = code;
|
|
||||||
d->data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FLAC::UnknownMetadataBlock::code() const
|
|
||||||
{
|
|
||||||
return d->code;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::UnknownMetadataBlock::setCode(int code)
|
|
||||||
{
|
|
||||||
d->code = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector FLAC::UnknownMetadataBlock::data() const
|
|
||||||
{
|
|
||||||
return d->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FLAC::UnknownMetadataBlock::setData(const ByteVector &data)
|
|
||||||
{
|
|
||||||
d->data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteVector FLAC::UnknownMetadataBlock::render() const
|
|
||||||
{
|
|
||||||
return d->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
81
3rdparty/taglib/flac/flacunknownmetadatablock.h
vendored
81
3rdparty/taglib/flac/flacunknownmetadatablock.h
vendored
@@ -1,81 +0,0 @@
|
|||||||
/**************************************************************************
|
|
||||||
copyright : (C) 2010 by Lukáš Lalinský
|
|
||||||
email : lalinsky@gmail.com
|
|
||||||
**************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_FLACUNKNOWNMETADATABLOCK_H
|
|
||||||
#define TAGLIB_FLACUNKNOWNMETADATABLOCK_H
|
|
||||||
|
|
||||||
#include "tlist.h"
|
|
||||||
#include "tbytevector.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "flacmetadatablock.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace FLAC {
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
UnknownMetadataBlock(int blockType, const ByteVector &data);
|
|
||||||
~UnknownMetadataBlock();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the FLAC metadata block type.
|
|
||||||
*/
|
|
||||||
int code() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the FLAC metadata block type.
|
|
||||||
*/
|
|
||||||
void setCode(int code);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the FLAC metadata block type.
|
|
||||||
*/
|
|
||||||
ByteVector data() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Sets the FLAC metadata block type.
|
|
||||||
*/
|
|
||||||
void setData(const ByteVector &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Render the content of the block.
|
|
||||||
*/
|
|
||||||
ByteVector render() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
UnknownMetadataBlock(const MetadataBlock &item);
|
|
||||||
UnknownMetadataBlock &operator=(const MetadataBlock &item);
|
|
||||||
|
|
||||||
class UnknownMetadataBlockPrivate;
|
|
||||||
UnknownMetadataBlockPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
335
3rdparty/taglib/it/itfile.cpp
vendored
335
3rdparty/taglib/it/itfile.cpp
vendored
@@ -1,335 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2011 by Mathias Panzenböck
|
|
||||||
email : grosser.meister.morti@gmx.net
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
|
||||||
* 02110-1301 USA *
|
|
||||||
* *
|
|
||||||
* Alternatively, this file is available under the Mozilla Public *
|
|
||||||
* License Version 1.1. You may obtain a copy of the License at *
|
|
||||||
* http://www.mozilla.org/MPL/ *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#include "tstringlist.h"
|
|
||||||
#include "itfile.h"
|
|
||||||
#include "tdebug.h"
|
|
||||||
#include "modfileprivate.h"
|
|
||||||
#include "tpropertymap.h"
|
|
||||||
|
|
||||||
using namespace TagLib;
|
|
||||||
using namespace IT;
|
|
||||||
|
|
||||||
class IT::File::FilePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
|
||||||
: tag(), properties(propertiesStyle)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Mod::Tag tag;
|
|
||||||
IT::Properties properties;
|
|
||||||
};
|
|
||||||
|
|
||||||
IT::File::File(FileName file, bool readProperties,
|
|
||||||
AudioProperties::ReadStyle propertiesStyle) :
|
|
||||||
Mod::FileBase(file),
|
|
||||||
d(new FilePrivate(propertiesStyle))
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
IT::File::File(IOStream *stream, bool readProperties,
|
|
||||||
AudioProperties::ReadStyle propertiesStyle) :
|
|
||||||
Mod::FileBase(stream),
|
|
||||||
d(new FilePrivate(propertiesStyle))
|
|
||||||
{
|
|
||||||
if(isOpen())
|
|
||||||
read(readProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
IT::File::~File()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mod::Tag *IT::File::tag() const
|
|
||||||
{
|
|
||||||
return &d->tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap IT::File::properties() const
|
|
||||||
{
|
|
||||||
return d->tag.properties();
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyMap IT::File::setProperties(const PropertyMap &properties)
|
|
||||||
{
|
|
||||||
return d->tag.setProperties(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
IT::Properties *IT::File::audioProperties() const
|
|
||||||
{
|
|
||||||
return &d->properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IT::File::save()
|
|
||||||
{
|
|
||||||
if(readOnly())
|
|
||||||
{
|
|
||||||
debug("IT::File::save() - Cannot save to a read only file.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seek(4);
|
|
||||||
writeString(d->tag.title(), 25);
|
|
||||||
writeByte(0);
|
|
||||||
|
|
||||||
seek(2, Current);
|
|
||||||
|
|
||||||
unsigned short length = 0;
|
|
||||||
unsigned short instrumentCount = 0;
|
|
||||||
unsigned short sampleCount = 0;
|
|
||||||
|
|
||||||
if(!readU16L(length) || !readU16L(instrumentCount) || !readU16L(sampleCount))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
seek(15, Current);
|
|
||||||
|
|
||||||
// write comment as instrument and sample names:
|
|
||||||
StringList lines = d->tag.comment().split("\n");
|
|
||||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
|
||||||
seek(192L + length + ((long)i << 2));
|
|
||||||
unsigned long instrumentOffset = 0;
|
|
||||||
if(!readU32L(instrumentOffset))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
seek(instrumentOffset + 32);
|
|
||||||
|
|
||||||
if(i < lines.size())
|
|
||||||
writeString(lines[i], 25);
|
|
||||||
else
|
|
||||||
writeString(String(), 25);
|
|
||||||
writeByte(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
|
||||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
|
||||||
unsigned long sampleOffset = 0;
|
|
||||||
if(!readU32L(sampleOffset))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
seek(sampleOffset + 20);
|
|
||||||
|
|
||||||
if((unsigned int)(i + instrumentCount) < lines.size())
|
|
||||||
writeString(lines[i + instrumentCount], 25);
|
|
||||||
else
|
|
||||||
writeString(String(), 25);
|
|
||||||
writeByte(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// write rest as message:
|
|
||||||
StringList messageLines;
|
|
||||||
for(unsigned int i = instrumentCount + sampleCount; i < lines.size(); ++ i)
|
|
||||||
messageLines.append(lines[i]);
|
|
||||||
ByteVector message = messageLines.toString("\r").data(String::Latin1);
|
|
||||||
|
|
||||||
// it's actually not really stated if the message needs a
|
|
||||||
// terminating NUL but it does not hurt to add one:
|
|
||||||
if(message.size() > 7999)
|
|
||||||
message.resize(7999);
|
|
||||||
message.append((char)0);
|
|
||||||
|
|
||||||
unsigned short special = 0;
|
|
||||||
unsigned short messageLength = 0;
|
|
||||||
unsigned long messageOffset = 0;
|
|
||||||
|
|
||||||
seek(46);
|
|
||||||
if(!readU16L(special))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
unsigned long fileSize = File::length();
|
|
||||||
if(special & Properties::MessageAttached) {
|
|
||||||
seek(54);
|
|
||||||
if(!readU16L(messageLength) || !readU32L(messageOffset))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(messageLength == 0)
|
|
||||||
messageOffset = fileSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
messageOffset = fileSize;
|
|
||||||
seek(46);
|
|
||||||
writeU16L(special | 0x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(messageOffset + messageLength >= fileSize) {
|
|
||||||
// append new message
|
|
||||||
seek(54);
|
|
||||||
writeU16L(message.size());
|
|
||||||
writeU32L(messageOffset);
|
|
||||||
seek(messageOffset);
|
|
||||||
writeBlock(message);
|
|
||||||
truncate(messageOffset + message.size());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Only overwrite existing message.
|
|
||||||
// I'd need to parse (understand!) the whole file for more.
|
|
||||||
// Although I could just move the message to the end of file
|
|
||||||
// and let the existing one be, but that would waste space.
|
|
||||||
message.resize(messageLength, 0);
|
|
||||||
seek(messageOffset);
|
|
||||||
writeBlock(message);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IT::File::read(bool)
|
|
||||||
{
|
|
||||||
if(!isOpen())
|
|
||||||
return;
|
|
||||||
|
|
||||||
seek(0);
|
|
||||||
READ_ASSERT(readBlock(4) == "IMPM");
|
|
||||||
READ_STRING(d->tag.setTitle, 26);
|
|
||||||
|
|
||||||
seek(2, Current);
|
|
||||||
|
|
||||||
READ_U16L_AS(length);
|
|
||||||
READ_U16L_AS(instrumentCount);
|
|
||||||
READ_U16L_AS(sampleCount);
|
|
||||||
|
|
||||||
d->properties.setInstrumentCount(instrumentCount);
|
|
||||||
d->properties.setSampleCount(sampleCount);
|
|
||||||
READ_U16L(d->properties.setPatternCount);
|
|
||||||
READ_U16L(d->properties.setVersion);
|
|
||||||
READ_U16L(d->properties.setCompatibleVersion);
|
|
||||||
READ_U16L(d->properties.setFlags);
|
|
||||||
READ_U16L_AS(special);
|
|
||||||
d->properties.setSpecial(special);
|
|
||||||
READ_BYTE(d->properties.setGlobalVolume);
|
|
||||||
READ_BYTE(d->properties.setMixVolume);
|
|
||||||
READ_BYTE(d->properties.setBpmSpeed);
|
|
||||||
READ_BYTE(d->properties.setTempo);
|
|
||||||
READ_BYTE(d->properties.setPanningSeparation);
|
|
||||||
READ_BYTE(d->properties.setPitchWheelDepth);
|
|
||||||
|
|
||||||
// IT supports some kind of comment tag. Still, the
|
|
||||||
// sample/instrument names are abused as comments so
|
|
||||||
// I just add all together.
|
|
||||||
String message;
|
|
||||||
if(special & Properties::MessageAttached) {
|
|
||||||
READ_U16L_AS(messageLength);
|
|
||||||
READ_U32L_AS(messageOffset);
|
|
||||||
seek(messageOffset);
|
|
||||||
ByteVector messageBytes = readBlock(messageLength);
|
|
||||||
READ_ASSERT(messageBytes.size() == messageLength);
|
|
||||||
int index = messageBytes.find((char) 0);
|
|
||||||
if(index > -1)
|
|
||||||
messageBytes.resize(index, 0);
|
|
||||||
messageBytes.replace('\r', '\n');
|
|
||||||
message = messageBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
seek(64);
|
|
||||||
|
|
||||||
ByteVector pannings = readBlock(64);
|
|
||||||
ByteVector volumes = readBlock(64);
|
|
||||||
READ_ASSERT(pannings.size() == 64 && volumes.size() == 64);
|
|
||||||
int channels = 0;
|
|
||||||
for(int i = 0; i < 64; ++ i) {
|
|
||||||
// Strictly speaking an IT file has always 64 channels, but
|
|
||||||
// I don't count disabled and muted channels.
|
|
||||||
// But this always gives 64 channels for all my files anyway.
|
|
||||||
// Strangely VLC does report other values. I wonder how VLC
|
|
||||||
// gets it's values.
|
|
||||||
if((unsigned char) pannings[i] < 128 && volumes[i] > 0)
|
|
||||||
++channels;
|
|
||||||
}
|
|
||||||
d->properties.setChannels(channels);
|
|
||||||
|
|
||||||
// real length might be shorter because of skips and terminator
|
|
||||||
unsigned short realLength = 0;
|
|
||||||
for(unsigned short i = 0; i < length; ++ i) {
|
|
||||||
READ_BYTE_AS(order);
|
|
||||||
if(order == 255) break;
|
|
||||||
if(order != 254) ++ realLength;
|
|
||||||
}
|
|
||||||
d->properties.setLengthInPatterns(realLength);
|
|
||||||
|
|
||||||
StringList comment;
|
|
||||||
// Note: I found files that have nil characters somewhere
|
|
||||||
// in the instrument/sample names and more characters
|
|
||||||
// afterwards. The spec does not mention such a case.
|
|
||||||
// Currently I just discard anything after a nil, but
|
|
||||||
// e.g. VLC seems to interprete a nil as a space. I
|
|
||||||
// don't know what is the proper behaviour.
|
|
||||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
|
||||||
seek(192L + length + ((long)i << 2));
|
|
||||||
READ_U32L_AS(instrumentOffset);
|
|
||||||
seek(instrumentOffset);
|
|
||||||
|
|
||||||
ByteVector instrumentMagic = readBlock(4);
|
|
||||||
READ_ASSERT(instrumentMagic == "IMPI");
|
|
||||||
|
|
||||||
READ_STRING_AS(dosFileName, 13);
|
|
||||||
|
|
||||||
seek(15, Current);
|
|
||||||
|
|
||||||
READ_STRING_AS(instrumentName, 26);
|
|
||||||
comment.append(instrumentName);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
|
||||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
|
||||||
READ_U32L_AS(sampleOffset);
|
|
||||||
|
|
||||||
seek(sampleOffset);
|
|
||||||
|
|
||||||
ByteVector sampleMagic = readBlock(4);
|
|
||||||
READ_ASSERT(sampleMagic == "IMPS");
|
|
||||||
|
|
||||||
READ_STRING_AS(dosFileName, 13);
|
|
||||||
READ_BYTE_AS(globalVolume);
|
|
||||||
READ_BYTE_AS(sampleFlags);
|
|
||||||
READ_BYTE_AS(sampleVolume);
|
|
||||||
READ_STRING_AS(sampleName, 26);
|
|
||||||
/*
|
|
||||||
READ_BYTE_AS(sampleCvt);
|
|
||||||
READ_BYTE_AS(samplePanning);
|
|
||||||
READ_U32L_AS(sampleLength);
|
|
||||||
READ_U32L_AS(loopStart);
|
|
||||||
READ_U32L_AS(loopStop);
|
|
||||||
READ_U32L_AS(c5speed);
|
|
||||||
READ_U32L_AS(sustainLoopStart);
|
|
||||||
READ_U32L_AS(sustainLoopEnd);
|
|
||||||
READ_U32L_AS(sampleDataOffset);
|
|
||||||
READ_BYTE_AS(vibratoSpeed);
|
|
||||||
READ_BYTE_AS(vibratoDepth);
|
|
||||||
READ_BYTE_AS(vibratoRate);
|
|
||||||
READ_BYTE_AS(vibratoType);
|
|
||||||
*/
|
|
||||||
|
|
||||||
comment.append(sampleName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(message.size() > 0)
|
|
||||||
comment.append(message);
|
|
||||||
d->tag.setComment(comment.toString("\n"));
|
|
||||||
d->tag.setTrackerName("Impulse Tracker");
|
|
||||||
}
|
|
||||||
109
3rdparty/taglib/it/itfile.h
vendored
109
3rdparty/taglib/it/itfile.h
vendored
@@ -1,109 +0,0 @@
|
|||||||
/***************************************************************************
|
|
||||||
copyright : (C) 2011 by Mathias Panzenböck
|
|
||||||
email : grosser.meister.morti@gmx.net
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* This library is free software; you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU Lesser General Public License version *
|
|
||||||
* 2.1 as published by the Free Software Foundation. *
|
|
||||||
* *
|
|
||||||
* This library is distributed in the hope that it will be useful, but *
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
||||||
* Lesser General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU Lesser General Public *
|
|
||||||
* License along with this library; if not, write to the Free Software *
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
|
||||||
* MA 02110-1301 USA *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef TAGLIB_ITFILE_H
|
|
||||||
#define TAGLIB_ITFILE_H
|
|
||||||
|
|
||||||
#include "tfile.h"
|
|
||||||
#include "audioproperties.h"
|
|
||||||
#include "taglib_export.h"
|
|
||||||
#include "modfilebase.h"
|
|
||||||
#include "modtag.h"
|
|
||||||
#include "itproperties.h"
|
|
||||||
|
|
||||||
namespace TagLib {
|
|
||||||
|
|
||||||
namespace IT {
|
|
||||||
|
|
||||||
class TAGLIB_EXPORT File : public Mod::FileBase {
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Constructs a Impulse Tracker file from \a file.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, both \a readProperties and
|
|
||||||
* \a propertiesStyle are ignored. The audio properties are always
|
|
||||||
* read.
|
|
||||||
*/
|
|
||||||
File(FileName file, bool readProperties = true,
|
|
||||||
AudioProperties::ReadStyle propertiesStyle =
|
|
||||||
AudioProperties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Constructs a Impulse Tracker file from \a stream.
|
|
||||||
*
|
|
||||||
* \note In the current implementation, both \a readProperties and
|
|
||||||
* \a propertiesStyle are ignored. The audio properties are always
|
|
||||||
* read.
|
|
||||||
*
|
|
||||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
|
||||||
* responsible for deleting it after the File object.
|
|
||||||
*/
|
|
||||||
File(IOStream *stream, bool readProperties = true,
|
|
||||||
AudioProperties::ReadStyle propertiesStyle =
|
|
||||||
AudioProperties::Average);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Destroys this instance of the File.
|
|
||||||
*/
|
|
||||||
virtual ~File();
|
|
||||||
|
|
||||||
Mod::Tag *tag() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Forwards to Mod::Tag::properties().
|
|
||||||
* BIC: will be removed once File::toDict() is made virtual
|
|
||||||
*/
|
|
||||||
PropertyMap properties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Forwards to Mod::Tag::setProperties().
|
|
||||||
* BIC: will be removed once File::setProperties() is made virtual
|
|
||||||
*/
|
|
||||||
PropertyMap setProperties(const PropertyMap &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the IT::Properties for this file. If no audio properties
|
|
||||||
* were read then this will return a null pointer.
|
|
||||||
*/
|
|
||||||
IT::Properties *audioProperties() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Save the file.
|
|
||||||
* This is the same as calling save(AllTags);
|
|
||||||
*
|
|
||||||
* \note Saving Impulse Tracker tags is not supported.
|
|
||||||
*/
|
|
||||||
bool save();
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
File(const File &);
|
|
||||||
File &operator=(const File &);
|
|
||||||
|
|
||||||
void read(bool readProperties);
|
|
||||||
|
|
||||||
class FilePrivate;
|
|
||||||
FilePrivate *d;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user