mirror of
https://github.com/taglib/taglib.git
synced 2026-06-07 14:59:24 -04:00
Compare commits
567 Commits
v1.10
...
v1.12-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c14571647 | ||
|
|
f4b476a620 | ||
|
|
f8d78a61f7 | ||
|
|
cf6c68bafc | ||
|
|
f7c24930cd | ||
|
|
310c3bc043 | ||
|
|
e6c03c6de8 | ||
|
|
2c29fbeabb | ||
|
|
4828a3b925 | ||
|
|
794a2a0b2b | ||
|
|
f32d27973f | ||
|
|
f74b166435 | ||
|
|
c28dc78060 | ||
|
|
89fb62cfb1 | ||
|
|
59a2994f4e | ||
|
|
f3bdd416da | ||
|
|
39f86d02e7 | ||
|
|
1a2e1d08ac | ||
|
|
3f3b48353c | ||
|
|
54f5c66b65 | ||
|
|
3749dd7b75 | ||
|
|
d602ae483e | ||
|
|
5374cb1ac4 | ||
|
|
8a461ccedc | ||
|
|
17c2220588 | ||
|
|
271c004b30 | ||
|
|
25d9bd1814 | ||
|
|
b0bb7f8c0f | ||
|
|
4d3ab73d2e | ||
|
|
ae867cbd8c | ||
|
|
563fbaf82a | ||
|
|
e5ad041e42 | ||
|
|
9ca7b0c33a | ||
|
|
a00b3499b4 | ||
|
|
d84e86da9c | ||
|
|
741ed4e4bf | ||
|
|
e4813f4996 | ||
|
|
1332d44ff6 | ||
|
|
3d71ea1ad2 | ||
|
|
02c8995273 | ||
|
|
7e92af6e8b | ||
|
|
7ec1127f3e | ||
|
|
30d839538d | ||
|
|
8f6b6ac055 | ||
|
|
a5f11697f7 | ||
|
|
1721354627 | ||
|
|
ac7e5303a6 | ||
|
|
8ba246cdbe | ||
|
|
6656528f18 | ||
|
|
bad2cea122 | ||
|
|
61d5bcfd5b | ||
|
|
2dd6931eee | ||
|
|
91b00b17b2 | ||
|
|
e116824380 | ||
|
|
2db13ad8cf | ||
|
|
cd767738fc | ||
|
|
43bc11541d | ||
|
|
6806dc4cf2 | ||
|
|
ec9c49b964 | ||
|
|
872cafb0b9 | ||
|
|
47342f6974 | ||
|
|
5763d042a6 | ||
|
|
cd9e6b7502 | ||
|
|
54508df30b | ||
|
|
dcf0331eb1 | ||
|
|
96155f35fa | ||
|
|
ebd3373d6d | ||
|
|
f3ecfa11bb | ||
|
|
7082b9f66b | ||
|
|
de25bc6111 | ||
|
|
074f30e3fa | ||
|
|
f1b40de66b | ||
|
|
0b99cd9bac | ||
|
|
4668cf0f93 | ||
|
|
c05fa78406 | ||
|
|
b8dc105ae3 | ||
|
|
fced0f46e9 | ||
|
|
dc0f667a4c | ||
|
|
085180e9a4 | ||
|
|
ef1312d622 | ||
|
|
86c0428475 | ||
|
|
3c78c4cfc9 | ||
|
|
c8bb6271e5 | ||
|
|
84f7462526 | ||
|
|
2f23892182 | ||
|
|
2918602ad0 | ||
|
|
c146cd7e92 | ||
|
|
b124e621fe | ||
|
|
044fba2921 | ||
|
|
e72a98903f | ||
|
|
79bc9ccf8e | ||
|
|
850a3565a4 | ||
|
|
18d424995f | ||
|
|
d04db70bc6 | ||
|
|
ba7adc2bc2 | ||
|
|
79bb1428c0 | ||
|
|
7470f92a67 | ||
|
|
6455671ece | ||
|
|
660748210f | ||
|
|
1cf176af61 | ||
|
|
5cb589a5b8 | ||
|
|
a4d04e0c40 | ||
|
|
4ab0891646 | ||
|
|
d71398c953 | ||
|
|
bfed3797a0 | ||
|
|
e435372146 | ||
|
|
c2f544c9d1 | ||
|
|
8ca75f03b5 | ||
|
|
036a0317b9 | ||
|
|
8f6fe0b16c | ||
|
|
2c4ae870ec | ||
|
|
d8d56d3937 | ||
|
|
2d90d938fe | ||
|
|
a80093167f | ||
|
|
249f892455 | ||
|
|
cb9f07d9dc | ||
|
|
0b583bafd0 | ||
|
|
4648394841 | ||
|
|
e026d797e0 | ||
|
|
eeb2f5de09 | ||
|
|
1792ee9db8 | ||
|
|
1fb310ec1f | ||
|
|
c8bcd153fe | ||
|
|
662f340f93 | ||
|
|
48d8c0a808 | ||
|
|
5ebd3d5276 | ||
|
|
14af861d24 | ||
|
|
3795f277fb | ||
|
|
6045995e65 | ||
|
|
c2fe93c12b | ||
|
|
ea9202d9ee | ||
|
|
3c657d1a44 | ||
|
|
adf0f76360 | ||
|
|
682ea77c2b | ||
|
|
15c3c756ca | ||
|
|
4801fbb927 | ||
|
|
22de22b701 | ||
|
|
48a1a05a88 | ||
|
|
6000a19f70 | ||
|
|
ff28cf276c | ||
|
|
4891ee729d | ||
|
|
9419dab51b | ||
|
|
45ee18e206 | ||
|
|
bf7ee62dc6 | ||
|
|
f76b1e5429 | ||
|
|
f7b15fad20 | ||
|
|
dd4adf94ce | ||
|
|
d4d8410c08 | ||
|
|
931bb042c3 | ||
|
|
a5d9e49c49 | ||
|
|
179c175a6c | ||
|
|
ba98628919 | ||
|
|
87fc4012f4 | ||
|
|
dd5ab2a08f | ||
|
|
b74ffba4b5 | ||
|
|
4552f2c2eb | ||
|
|
6398796f95 | ||
|
|
2c7ac6d6a9 | ||
|
|
6a61f02f85 | ||
|
|
038b52ae01 | ||
|
|
598ab752bc | ||
|
|
922fd611ae | ||
|
|
3d14ff74b1 | ||
|
|
978b822774 | ||
|
|
0c45c63943 | ||
|
|
586c9bd962 | ||
|
|
fc38a0e401 | ||
|
|
5fc5a2e81a | ||
|
|
a358e87cc4 | ||
|
|
5ba8b740f9 | ||
|
|
c4a3c3ab97 | ||
|
|
6bb92c34fa | ||
|
|
d2e0e55223 | ||
|
|
d64c833359 | ||
|
|
c9c757e0ff | ||
|
|
9b548260f5 | ||
|
|
406e872ac3 | ||
|
|
193cbe3b6b | ||
|
|
13be28a52c | ||
|
|
56a7656c2e | ||
|
|
c97be6630e | ||
|
|
6fcc690233 | ||
|
|
83c72518ab | ||
|
|
de87cd7736 | ||
|
|
14c3ce5737 | ||
|
|
ffa32b19a7 | ||
|
|
8905e7095a | ||
|
|
a19a623d4b | ||
|
|
250c59f783 | ||
|
|
8eda5d5053 | ||
|
|
b5115e3497 | ||
|
|
36ccad2bd4 | ||
|
|
b00a5c1aab | ||
|
|
f6a604f54b | ||
|
|
489e2e6cbb | ||
|
|
9336c82da3 | ||
|
|
cfbaf34597 | ||
|
|
046c98230f | ||
|
|
4381bd75f3 | ||
|
|
6df61cf2af | ||
|
|
e9ef40fe7f | ||
|
|
2786aa7463 | ||
|
|
d3062f3af4 | ||
|
|
7871afec37 | ||
|
|
c9a0754e3b | ||
|
|
6cfb11bb12 | ||
|
|
ad075a56f9 | ||
|
|
f80a7c0d83 | ||
|
|
5e1d9fad31 | ||
|
|
eff28c55bf | ||
|
|
d5b9d7b8a7 | ||
|
|
ce77fbb0e7 | ||
|
|
b98a984b66 | ||
|
|
f9a747dceb | ||
|
|
7b8d576bde | ||
|
|
2651372291 | ||
|
|
499f6db977 | ||
|
|
9d58e9f8e8 | ||
|
|
56cd3695f7 | ||
|
|
d81d894d41 | ||
|
|
e390cbac52 | ||
|
|
253c61e37d | ||
|
|
1848b3bc6f | ||
|
|
aae23f3c07 | ||
|
|
da9df1b2a8 | ||
|
|
70334edd19 | ||
|
|
f5ca097379 | ||
|
|
eb6d058ab9 | ||
|
|
e6a69e24bc | ||
|
|
dcab8ed90e | ||
|
|
f5ce498182 | ||
|
|
ee7fa78011 | ||
|
|
873c917081 | ||
|
|
d3bd8fb7ff | ||
|
|
005441faaa | ||
|
|
935534aa53 | ||
|
|
65a24bbc51 | ||
|
|
711b35cc6e | ||
|
|
d53ca6f736 | ||
|
|
aa5f9bb221 | ||
|
|
d2b3547254 | ||
|
|
06ca9a099d | ||
|
|
8d873e4e3e | ||
|
|
b2fa124451 | ||
|
|
ff5b2dc96f | ||
|
|
757f5ebc96 | ||
|
|
e36a9cabb9 | ||
|
|
606f6c0e74 | ||
|
|
1cc047c953 | ||
|
|
cae4f1b804 | ||
|
|
deffe83fd0 | ||
|
|
597dcde72a | ||
|
|
6a96a6426a | ||
|
|
69c65284a5 | ||
|
|
97aaa0f979 | ||
|
|
1b64bb0cb7 | ||
|
|
0dac721ce2 | ||
|
|
bbeeca6fdb | ||
|
|
7e90313690 | ||
|
|
1d3c95f692 | ||
|
|
8c3801f18d | ||
|
|
c9bdd416ef | ||
|
|
9f28e037fe | ||
|
|
92c070ba9e | ||
|
|
75e3ec73aa | ||
|
|
3e47a036fb | ||
|
|
9b995544e4 | ||
|
|
d8e5077961 | ||
|
|
6422054540 | ||
|
|
1b878102f0 | ||
|
|
0a85f9b227 | ||
|
|
31f3109b47 | ||
|
|
76f8ff388f | ||
|
|
7627ae48ed | ||
|
|
b2a6768704 | ||
|
|
7d270a7e20 | ||
|
|
bf53dc6131 | ||
|
|
ff8b6a91e7 | ||
|
|
1a82419872 | ||
|
|
5a3265d031 | ||
|
|
a27199b772 | ||
|
|
6c27a32de8 | ||
|
|
11abffc0f6 | ||
|
|
2aea23aed2 | ||
|
|
e8ef0e0a4b | ||
|
|
520da50bc5 | ||
|
|
c0a909b709 | ||
|
|
8d09f12847 | ||
|
|
710166e32d | ||
|
|
01054009ac | ||
|
|
7d8aa7b8bd | ||
|
|
98a57744c3 | ||
|
|
8c4b484e67 | ||
|
|
2166d703e0 | ||
|
|
887f3b28e5 | ||
|
|
6a9db50c03 | ||
|
|
07d95e0dc0 | ||
|
|
a9acca5d81 | ||
|
|
dadfe79799 | ||
|
|
552185a8b8 | ||
|
|
46eacaeba4 | ||
|
|
455e827e1e | ||
|
|
c08e0b1357 | ||
|
|
f3016c0892 | ||
|
|
f8f7cb0904 | ||
|
|
d037b8c908 | ||
|
|
c349ba3a31 | ||
|
|
bb006e41d7 | ||
|
|
ab2267f9aa | ||
|
|
73662037eb | ||
|
|
aa339eba87 | ||
|
|
45a3bdb695 | ||
|
|
5a155ef4ce | ||
|
|
184a151d41 | ||
|
|
cc2ccbc20c | ||
|
|
fa46185ca4 | ||
|
|
fffff35ca5 | ||
|
|
31894f47f6 | ||
|
|
c1e3d65b68 | ||
|
|
a8ecbbaef4 | ||
|
|
24575aab23 | ||
|
|
c04b24a2f5 | ||
|
|
013fbbf22c | ||
|
|
5350bc8501 | ||
|
|
92a1a00624 | ||
|
|
8afbf6c92a | ||
|
|
9976155aa9 | ||
|
|
c2cb9ab8b0 | ||
|
|
758b7e39ce | ||
|
|
4ba7bb5a8a | ||
|
|
0b62ba1530 | ||
|
|
fe92f3dffe | ||
|
|
b8e82a7775 | ||
|
|
3dc873b7d0 | ||
|
|
1254534ed4 | ||
|
|
7a5fb7d672 | ||
|
|
5b7ee5bc1a | ||
|
|
815d1583e9 | ||
|
|
6c183a7df8 | ||
|
|
8d5f821b16 | ||
|
|
0611744171 | ||
|
|
c25e0c6ead | ||
|
|
45762d20f1 | ||
|
|
67434aa7b4 | ||
|
|
cb23f91c98 | ||
|
|
37c4d0b11d | ||
|
|
626d13467e | ||
|
|
02eac18d00 | ||
|
|
4064b34eff | ||
|
|
22708a0af6 | ||
|
|
12da0ebd6d | ||
|
|
d13a3e3e16 | ||
|
|
e8b28a0466 | ||
|
|
081d6eaf76 | ||
|
|
ac361c7692 | ||
|
|
d7de3983d9 | ||
|
|
1aca1f64fe | ||
|
|
5a1f44d166 | ||
|
|
130654316b | ||
|
|
5cd139a578 | ||
|
|
dd2ed47703 | ||
|
|
d0238ba82f | ||
|
|
cf5d431d77 | ||
|
|
4ec264b6a1 | ||
|
|
ef92ee223f | ||
|
|
8b6bcf411a | ||
|
|
aa66b0651d | ||
|
|
7c5d1e02de | ||
|
|
6d27a1013d | ||
|
|
50711f4758 | ||
|
|
6998be94f6 | ||
|
|
862952bdcc | ||
|
|
f0b0b736e3 | ||
|
|
546870d83a | ||
|
|
581e58585a | ||
|
|
36b905670c | ||
|
|
42459fe457 | ||
|
|
fc571e5150 | ||
|
|
5480458dfc | ||
|
|
9950fca3c2 | ||
|
|
63e838f831 | ||
|
|
94f28b4fa1 | ||
|
|
680784c7b7 | ||
|
|
5d517127ae | ||
|
|
be9b5cc93a | ||
|
|
847eec23cf | ||
|
|
9dfb3fe452 | ||
|
|
a013c7c4c2 | ||
|
|
62ec63b039 | ||
|
|
52d3122e9e | ||
|
|
24f220224d | ||
|
|
a1ac23530e | ||
|
|
e99910dd74 | ||
|
|
88ad2843f4 | ||
|
|
a0b8683656 | ||
|
|
085a0ef298 | ||
|
|
363c471a2d | ||
|
|
7f8efec4d4 | ||
|
|
dcc0fe553c | ||
|
|
8bdddaabce | ||
|
|
2cb89b85c9 | ||
|
|
e6e9ba6094 | ||
|
|
7b854c5434 | ||
|
|
1529af7a12 | ||
|
|
e475e31e0d | ||
|
|
ea55c8b5c1 | ||
|
|
bc8b60ea46 | ||
|
|
83ffc87598 | ||
|
|
d97292c593 | ||
|
|
060a50ab11 | ||
|
|
6966be4292 | ||
|
|
f38749aacf | ||
|
|
b4b16fd7e0 | ||
|
|
b1c67fc5dc | ||
|
|
90f1f03163 | ||
|
|
101a330d55 | ||
|
|
59088096e6 | ||
|
|
d1ceffd1f0 | ||
|
|
eb6542bc8b | ||
|
|
bccd57a470 | ||
|
|
a6701ddcda | ||
|
|
31d1f11969 | ||
|
|
f9e558eef5 | ||
|
|
fcdf7c2ae2 | ||
|
|
8c424badad | ||
|
|
f1b683b582 | ||
|
|
5e37f0101d | ||
|
|
b541ec8b68 | ||
|
|
1fa677e2ef | ||
|
|
463e8f313a | ||
|
|
8130b30397 | ||
|
|
6b836c6ba7 | ||
|
|
f5a3f2b6c1 | ||
|
|
4b8e39e8f0 | ||
|
|
905bf89e66 | ||
|
|
67cbf2c0a8 | ||
|
|
3f84621d58 | ||
|
|
083eadec60 | ||
|
|
f180445be5 | ||
|
|
cbc279b899 | ||
|
|
e1bcb101f0 | ||
|
|
14f3b6c928 | ||
|
|
b6361ddde5 | ||
|
|
66dd0b8a6f | ||
|
|
94b7828990 | ||
|
|
fa4289e044 | ||
|
|
3612c2cc24 | ||
|
|
477aba9465 | ||
|
|
a640074a21 | ||
|
|
25ffbcb4b9 | ||
|
|
e44de9f37f | ||
|
|
2b7d6fef47 | ||
|
|
ae633105d6 | ||
|
|
6978131d22 | ||
|
|
83a0bc3710 | ||
|
|
6477132301 | ||
|
|
559c6b28c9 | ||
|
|
e9f70a59b2 | ||
|
|
f34d73d319 | ||
|
|
ef3ce1e38a | ||
|
|
8724184d56 | ||
|
|
61dabe61a7 | ||
|
|
de0fc83066 | ||
|
|
c3807e59cd | ||
|
|
b592f78238 | ||
|
|
84e3582332 | ||
|
|
ce1c03faa3 | ||
|
|
94c941928a | ||
|
|
8c6fe45453 | ||
|
|
c4fe65787c | ||
|
|
29b0568dec | ||
|
|
7d0a651f3e | ||
|
|
224f133d65 | ||
|
|
f956c89141 | ||
|
|
6dc8d701a8 | ||
|
|
779f904940 | ||
|
|
4161746d89 | ||
|
|
6d5654028a | ||
|
|
93d55dafd6 | ||
|
|
e1e1b6c60c | ||
|
|
1cc3e4cc57 | ||
|
|
f310b1810d | ||
|
|
e10684312e | ||
|
|
a3564d8c68 | ||
|
|
95ef0e7882 | ||
|
|
13a258d9ed | ||
|
|
72a807def1 | ||
|
|
539d951277 | ||
|
|
21788f4a26 | ||
|
|
74cb6f3fc7 | ||
|
|
e0f1151c5c | ||
|
|
10e1fcd686 | ||
|
|
3bce77a359 | ||
|
|
0ffd2e8ab9 | ||
|
|
7e85d9b202 | ||
|
|
c6443dabc6 | ||
|
|
c5db39fbf4 | ||
|
|
b6469bda1a | ||
|
|
978a5f0a27 | ||
|
|
8203ccf04c | ||
|
|
47860c20f4 | ||
|
|
6e6e11f21a | ||
|
|
288e97ad44 | ||
|
|
07b26ab3e4 | ||
|
|
0f00dfc778 | ||
|
|
b01fecd280 | ||
|
|
69921b8090 | ||
|
|
1caaa8a020 | ||
|
|
4f3844114a | ||
|
|
76de4234a1 | ||
|
|
4bac99e3da | ||
|
|
6b7a2c4cf7 | ||
|
|
1a942627bf | ||
|
|
3128f425b8 | ||
|
|
3f968933f4 | ||
|
|
67f44071cd | ||
|
|
11fbf394a3 | ||
|
|
a25e1e9f90 | ||
|
|
091ab9dee0 | ||
|
|
c353a71ce5 | ||
|
|
762581fe3f | ||
|
|
8f147034d6 | ||
|
|
6775cef651 | ||
|
|
86c7e905ba | ||
|
|
2184aa476f | ||
|
|
3e8eff16b8 | ||
|
|
41a44f4ab2 | ||
|
|
70e471f1d1 | ||
|
|
f836cb4dea | ||
|
|
47bd01ecda | ||
|
|
90ad17b4c5 | ||
|
|
f4c1beb161 | ||
|
|
c963d3ba04 | ||
|
|
c4a02a1799 | ||
|
|
59a1b7a491 | ||
|
|
62bb3c95c8 | ||
|
|
d3351a0012 | ||
|
|
0c14bd0ed0 | ||
|
|
7bbf5a2e79 | ||
|
|
ba5137bf2d | ||
|
|
b3cea6cca7 | ||
|
|
a533b9c665 | ||
|
|
ec8e611909 | ||
|
|
c66e6b27d9 | ||
|
|
1d379fdb2f | ||
|
|
47813c5a7f | ||
|
|
94ff9124c7 | ||
|
|
e750cb491d | ||
|
|
1f7715a5da | ||
|
|
8e7504a66f | ||
|
|
9ad5bb1d62 | ||
|
|
b6adb1f108 | ||
|
|
c7231c58a3 | ||
|
|
c5cf9b93bc | ||
|
|
1bb06b1f7a | ||
|
|
35d5ba4eff | ||
|
|
04bee3faec | ||
|
|
e8c1a11730 | ||
|
|
d8e8ec69fe | ||
|
|
6d0712c8df | ||
|
|
00b2f6a386 | ||
|
|
a3dccdc7a3 | ||
|
|
b698c73690 | ||
|
|
00c6e7ea79 | ||
|
|
b5c0ab58ba | ||
|
|
de17843d8c | ||
|
|
944143a91b |
@@ -8,12 +8,12 @@ root = true
|
||||
insert_final_newline = true
|
||||
|
||||
# 2 space indentation
|
||||
[*.{h,cpp,tcc,cmake}]
|
||||
[*.{h,cpp,tcc,cmake,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Trim traling whitespaces
|
||||
[*.{h,cpp,tcc,cmake}]
|
||||
[*.{h,cpp,tcc,cmake,yml}]
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# UTF-8 without BOM
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,8 +19,10 @@ CMakeFiles/
|
||||
/taglib.pc
|
||||
/tests/test_runner
|
||||
/tests/Testing
|
||||
/taglib/libtag.a
|
||||
/taglib_config.h
|
||||
/taglib-config
|
||||
/bindings/c/libtag_c.a
|
||||
/bindings/c/taglib_c.pc
|
||||
/bindings/c/Debug
|
||||
/bindings/c/MinSizeRel
|
||||
@@ -42,5 +44,6 @@ CMakeFiles/
|
||||
/taglib/tag.dir/Release
|
||||
/ALL_BUILD.dir
|
||||
/ZERO_CHECK.dir
|
||||
taglib.h.stamp
|
||||
taglib.xcodeproj
|
||||
CMakeScripts
|
||||
|
||||
24
.travis.yml
24
.travis.yml
@@ -1,12 +1,32 @@
|
||||
language: cpp
|
||||
|
||||
sudo: false
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
dist: trusty
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
arch:
|
||||
- ppc64le
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcppunit-dev
|
||||
- libcppunit-dev
|
||||
- zlib1g-dev
|
||||
script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON . && make && make check
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install cppunit; fi
|
||||
|
||||
script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DCMAKE_CXX_FLAGS="-std=c++11" . && make && make check
|
||||
|
||||
327
3rdparty/utf8-cpp/checked.h
vendored
Normal file
327
3rdparty/utf8-cpp/checked.h
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
// Copyright 2006-2016 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "core.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// Base for the exceptions that may be thrown from the library
|
||||
class exception : public ::std::exception {
|
||||
};
|
||||
|
||||
// Exceptions that may be thrown from the library functions.
|
||||
class invalid_code_point : public exception {
|
||||
uint32_t cp;
|
||||
public:
|
||||
invalid_code_point(uint32_t codepoint) : cp(codepoint) {}
|
||||
virtual const char* what() const throw() { return "Invalid code point"; }
|
||||
uint32_t code_point() const {return cp;}
|
||||
};
|
||||
|
||||
class invalid_utf8 : public exception {
|
||||
uint8_t u8;
|
||||
public:
|
||||
invalid_utf8 (uint8_t u) : u8(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-8"; }
|
||||
uint8_t utf8_octet() const {return u8;}
|
||||
};
|
||||
|
||||
class invalid_utf16 : public exception {
|
||||
uint16_t u16;
|
||||
public:
|
||||
invalid_utf16 (uint16_t u) : u16(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-16"; }
|
||||
uint16_t utf16_word() const {return u16;}
|
||||
};
|
||||
|
||||
class not_enough_room : public exception {
|
||||
public:
|
||||
virtual const char* what() const throw() { return "Not enough space"; }
|
||||
};
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator append(uint32_t cp, octet_iterator result)
|
||||
{
|
||||
if (!utf8::internal::is_code_point_valid(cp))
|
||||
throw invalid_code_point(cp);
|
||||
|
||||
if (cp < 0x80) // one octet
|
||||
*(result++) = static_cast<uint8_t>(cp);
|
||||
else if (cp < 0x800) { // two octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 0x10000) { // three octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else { // four octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
|
||||
{
|
||||
while (start != end) {
|
||||
octet_iterator sequence_start = start;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(start, end);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
for (octet_iterator it = sequence_start; it != start; ++it)
|
||||
*out++ = *it;
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM:
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
break;
|
||||
case internal::INCOMPLETE_SEQUENCE:
|
||||
case internal::OVERLONG_SEQUENCE:
|
||||
case internal::INVALID_CODE_POINT:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
// just one replacement mark for the sequence
|
||||
while (start != end && utf8::internal::is_trail(*start))
|
||||
++start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
|
||||
{
|
||||
static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
|
||||
return utf8::replace_invalid(start, end, out, replacement_marker);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t next(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
uint32_t cp = 0;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM :
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD :
|
||||
case internal::INCOMPLETE_SEQUENCE :
|
||||
case internal::OVERLONG_SEQUENCE :
|
||||
throw invalid_utf8(*it);
|
||||
case internal::INVALID_CODE_POINT :
|
||||
throw invalid_code_point(cp);
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t peek_next(octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t prior(octet_iterator& it, octet_iterator start)
|
||||
{
|
||||
// can't do much if it == start
|
||||
if (it == start)
|
||||
throw not_enough_room();
|
||||
|
||||
octet_iterator end = it;
|
||||
// Go back until we hit either a lead octet or start
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
return utf8::peek_next(it, end);
|
||||
}
|
||||
|
||||
/// Deprecated in versions that include "prior"
|
||||
template <typename octet_iterator>
|
||||
uint32_t previous(octet_iterator& it, octet_iterator pass_start)
|
||||
{
|
||||
octet_iterator end = it;
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == pass_start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename distance_type>
|
||||
void advance (octet_iterator& it, distance_type n, octet_iterator end)
|
||||
{
|
||||
for (distance_type i = 0; i < n; ++i)
|
||||
utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
typename std::iterator_traits<octet_iterator>::difference_type
|
||||
distance (octet_iterator first, octet_iterator last)
|
||||
{
|
||||
typename std::iterator_traits<octet_iterator>::difference_type dist;
|
||||
for (dist = 0; first < last; ++dist)
|
||||
utf8::next(first, last);
|
||||
return dist;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (utf8::internal::is_lead_surrogate(cp)) {
|
||||
if (start != end) {
|
||||
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
|
||||
if (utf8::internal::is_trail_surrogate(trail_surrogate))
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
|
||||
}
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
}
|
||||
// Lone trail surrogate
|
||||
else if (utf8::internal::is_trail_surrogate(cp))
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
result = utf8::append(cp, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start < end) {
|
||||
uint32_t cp = utf8::next(start, end);
|
||||
if (cp > 0xffff) { //make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
result = utf8::append(*(start++), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start < end)
|
||||
(*result++) = utf8::next(start, end);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// The iterator class
|
||||
template <typename octet_iterator>
|
||||
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
|
||||
octet_iterator it;
|
||||
octet_iterator range_start;
|
||||
octet_iterator range_end;
|
||||
public:
|
||||
iterator () {}
|
||||
explicit iterator (const octet_iterator& octet_it,
|
||||
const octet_iterator& rangestart,
|
||||
const octet_iterator& rangeend) :
|
||||
it(octet_it), range_start(rangestart), range_end(rangeend)
|
||||
{
|
||||
if (it < range_start || it > range_end)
|
||||
throw std::out_of_range("Invalid utf-8 iterator position");
|
||||
}
|
||||
// the default "big three" are OK
|
||||
octet_iterator base () const { return it; }
|
||||
uint32_t operator * () const
|
||||
{
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, range_end);
|
||||
}
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
if (range_start != rhs.range_start || range_end != rhs.range_end)
|
||||
throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
|
||||
return (it == rhs.it);
|
||||
}
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(operator == (rhs));
|
||||
}
|
||||
iterator& operator ++ ()
|
||||
{
|
||||
utf8::next(it, range_end);
|
||||
return *this;
|
||||
}
|
||||
iterator operator ++ (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::next(it, range_end);
|
||||
return temp;
|
||||
}
|
||||
iterator& operator -- ()
|
||||
{
|
||||
utf8::prior(it, range_start);
|
||||
return *this;
|
||||
}
|
||||
iterator operator -- (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::prior(it, range_start);
|
||||
return temp;
|
||||
}
|
||||
}; // class iterator
|
||||
|
||||
} // namespace utf8
|
||||
|
||||
#endif //header guard
|
||||
|
||||
|
||||
332
3rdparty/utf8-cpp/core.h
vendored
Normal file
332
3rdparty/utf8-cpp/core.h
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
|
||||
// You may need to change them to match your system.
|
||||
// These typedefs have the same names as ones from cstdint, or boost/cstdint
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
// Helper code - not intended to be directly called by the library users. May be changed at any time
|
||||
namespace internal
|
||||
{
|
||||
// Unicode constants
|
||||
// Leading (high) surrogates: 0xd800 - 0xdbff
|
||||
// Trailing (low) surrogates: 0xdc00 - 0xdfff
|
||||
const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
|
||||
const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
|
||||
const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
|
||||
const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
|
||||
const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
|
||||
const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
|
||||
|
||||
// Maximum valid value for a Unicode code point
|
||||
const uint32_t CODE_POINT_MAX = 0x0010ffffu;
|
||||
|
||||
template<typename octet_type>
|
||||
inline uint8_t mask8(octet_type oc)
|
||||
{
|
||||
return static_cast<uint8_t>(0xff & oc);
|
||||
}
|
||||
template<typename u16_type>
|
||||
inline uint16_t mask16(u16_type oc)
|
||||
{
|
||||
return static_cast<uint16_t>(0xffff & oc);
|
||||
}
|
||||
template<typename octet_type>
|
||||
inline bool is_trail(octet_type oc)
|
||||
{
|
||||
return ((utf8::internal::mask8(oc) >> 6) == 0x2);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_lead_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_trail_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u32>
|
||||
inline bool is_code_point_valid(u32 cp)
|
||||
{
|
||||
return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline typename std::iterator_traits<octet_iterator>::difference_type
|
||||
sequence_length(octet_iterator lead_it)
|
||||
{
|
||||
uint8_t lead = utf8::internal::mask8(*lead_it);
|
||||
if (lead < 0x80)
|
||||
return 1;
|
||||
else if ((lead >> 5) == 0x6)
|
||||
return 2;
|
||||
else if ((lead >> 4) == 0xe)
|
||||
return 3;
|
||||
else if ((lead >> 3) == 0x1e)
|
||||
return 4;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename octet_difference_type>
|
||||
inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
|
||||
{
|
||||
if (cp < 0x80) {
|
||||
if (length != 1)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x800) {
|
||||
if (length != 2)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x10000) {
|
||||
if (length != 3)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
|
||||
|
||||
/// Helper for get_sequence_x
|
||||
template <typename octet_iterator>
|
||||
utf_error increase_safely(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
if (++it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
if (!utf8::internal::is_trail(*it))
|
||||
return INCOMPLETE_SEQUENCE;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
|
||||
|
||||
/// get_sequence_x functions decode utf-8 sequences of the length x
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
// Save the original value of it so we can go back in case of failure
|
||||
// Of course, it does not make much sense with i.e. stream iterators
|
||||
octet_iterator original_it = it;
|
||||
|
||||
uint32_t cp = 0;
|
||||
// Determine the sequence length based on the lead octet
|
||||
typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
|
||||
const octet_difference_type length = utf8::internal::sequence_length(it);
|
||||
|
||||
// Get trail octets and calculate the code point
|
||||
utf_error err = UTF8_OK;
|
||||
switch (length) {
|
||||
case 0:
|
||||
return INVALID_LEAD;
|
||||
case 1:
|
||||
err = utf8::internal::get_sequence_1(it, end, cp);
|
||||
break;
|
||||
case 2:
|
||||
err = utf8::internal::get_sequence_2(it, end, cp);
|
||||
break;
|
||||
case 3:
|
||||
err = utf8::internal::get_sequence_3(it, end, cp);
|
||||
break;
|
||||
case 4:
|
||||
err = utf8::internal::get_sequence_4(it, end, cp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err == UTF8_OK) {
|
||||
// Decoding succeeded. Now, security checks...
|
||||
if (utf8::internal::is_code_point_valid(cp)) {
|
||||
if (!utf8::internal::is_overlong_sequence(cp, length)){
|
||||
// Passed! Return here.
|
||||
code_point = cp;
|
||||
++it;
|
||||
return UTF8_OK;
|
||||
}
|
||||
else
|
||||
err = OVERLONG_SEQUENCE;
|
||||
}
|
||||
else
|
||||
err = INVALID_CODE_POINT;
|
||||
}
|
||||
|
||||
// Failure branch - restore the original value of the iterator
|
||||
it = original_it;
|
||||
return err;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
|
||||
uint32_t ignored;
|
||||
return utf8::internal::validate_next(it, end, ignored);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
// Byte order mark
|
||||
const uint8_t bom[] = {0xef, 0xbb, 0xbf};
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator find_invalid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
octet_iterator result = start;
|
||||
while (result != end) {
|
||||
utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
|
||||
if (err_code != internal::UTF8_OK)
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool is_valid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
return (utf8::find_invalid(start, end) == end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool starts_with_bom (octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return (
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it)) == bom[2])
|
||||
);
|
||||
}
|
||||
|
||||
//Deprecated in release 2.3
|
||||
template <typename octet_iterator>
|
||||
inline bool is_bom (octet_iterator it)
|
||||
{
|
||||
return (
|
||||
(utf8::internal::mask8(*it++)) == bom[0] &&
|
||||
(utf8::internal::mask8(*it++)) == bom[1] &&
|
||||
(utf8::internal::mask8(*it)) == bom[2]
|
||||
);
|
||||
}
|
||||
} // namespace utf8
|
||||
|
||||
#endif // header guard
|
||||
|
||||
|
||||
8
AUTHORS
8
AUTHORS
@@ -2,6 +2,10 @@ Scott Wheeler <wheeler@kde.org>
|
||||
Author, maintainer
|
||||
Lukas Lalinsky <lalinsky@gmail.com>
|
||||
Implementation of multiple new file formats, many bug fixes, maintainer
|
||||
Tsuda Kageyu <tsuda.kageyu@gmail.com>
|
||||
A lot of bug fixes and performance improvements, maintainer.
|
||||
Stephen F. Booth <me@sbooth.org>
|
||||
DSF metadata implementation, bug fixes, maintainer.
|
||||
Ismael Orenstein <orenstein@kde.org>
|
||||
Xing header implementation
|
||||
Allan Sandfeld Jensen <kde@carewolf.org>
|
||||
@@ -10,8 +14,8 @@ Teemu Tervo <teemu.tervo@gmx.net>
|
||||
Numerous bug reports and fixes
|
||||
Mathias Panzenböck <grosser.meister.morti@gmx.net>
|
||||
Mod, S3M, IT and XM metadata implementations
|
||||
Tsuda Kageyu <tsuda.kageyu@gmail.com>
|
||||
A lot of fixes and improvements, i.e. memory copy reduction etc.
|
||||
Damien Plisson <damien78@audirvana.com>
|
||||
DSDIFF metadata implementation
|
||||
|
||||
Please send all patches and questions to taglib-devel@kde.org rather than to
|
||||
individual developers!
|
||||
|
||||
135
CMakeLists.txt
135
CMakeLists.txt
@@ -1,60 +1,84 @@
|
||||
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
|
||||
|
||||
project(taglib)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
cmake_policy(SET CMP0022 OLD)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
option(ENABLE_STATIC "Make static version of libtag" OFF)
|
||||
if(ENABLE_STATIC)
|
||||
if(DEFINED ENABLE_STATIC)
|
||||
message(FATAL_ERROR "This option is no longer available, use BUILD_SHARED_LIBS instead")
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
if(APPLE)
|
||||
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
|
||||
if(BUILD_FRAMEWORK)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
#set(CMAKE_MACOSX_RPATH 1)
|
||||
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
else()
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
endif()
|
||||
OPTION(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
|
||||
option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
|
||||
|
||||
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
add_definitions (-fvisibility=hidden)
|
||||
option(ENABLE_CCACHE "Use ccache when building libtag" OFF)
|
||||
if(ENABLE_CCACHE)
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(BUILD_TESTS "Build the test suite" OFF)
|
||||
option(BUILD_EXAMPLES "Build the examples" OFF)
|
||||
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
|
||||
option(BUILD_TESTS "Build the test suite" OFF)
|
||||
option(BUILD_EXAMPLES "Build the examples" OFF)
|
||||
option(BUILD_BINDINGS "Build the bindings" ON)
|
||||
|
||||
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
|
||||
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
|
||||
|
||||
option(PLATFORM_WINRT "Enable WinRT support" OFF)
|
||||
if(PLATFORM_WINRT)
|
||||
add_definitions(-DPLATFORM_WINRT)
|
||||
endif()
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
|
||||
|
||||
## the following are directories where stuff will be installed to
|
||||
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
|
||||
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries" FORCE)
|
||||
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)" FORCE)
|
||||
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE)
|
||||
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix" FORCE)
|
||||
|
||||
if(APPLE)
|
||||
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
|
||||
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
|
||||
endif()
|
||||
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries")
|
||||
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)")
|
||||
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})")
|
||||
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix")
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
endif()
|
||||
if (MSVC AND ENABLE_STATIC_RUNTIME)
|
||||
|
||||
if(MSVC AND ENABLE_STATIC_RUNTIME)
|
||||
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endforeach(flag_var)
|
||||
endif()
|
||||
|
||||
set(TAGLIB_LIB_MAJOR_VERSION "1")
|
||||
set(TAGLIB_LIB_MINOR_VERSION "10")
|
||||
set(TAGLIB_LIB_PATCH_VERSION "0")
|
||||
# Read version information from file taglib/toolkit/taglib.h into variables
|
||||
# TAGLIB_LIB_MAJOR_VERSION, TAGLIB_LIB_MINOR_VERSION, TAGLIB_LIB_PATCH_VERSION.
|
||||
foreach(version_part MAJOR MINOR PATCH)
|
||||
set(version_var_name "TAGLIB_${version_part}_VERSION")
|
||||
file(STRINGS taglib/toolkit/taglib.h version_line
|
||||
REGEX "^#define +${version_var_name}")
|
||||
if(NOT version_line)
|
||||
message(FATAL_ERROR "${version_var_name} not found in taglib.h")
|
||||
endif()
|
||||
string(REGEX MATCH "${version_var_name} +([^ ]+)" result ${version_line})
|
||||
set(TAGLIB_LIB_${version_part}_VERSION ${CMAKE_MATCH_1})
|
||||
endforeach(version_part)
|
||||
|
||||
# Only used to force cmake rerun when taglib.h changes.
|
||||
configure_file(taglib/toolkit/taglib.h ${CMAKE_CURRENT_BINARY_DIR}/taglib.h.stamp)
|
||||
|
||||
if("${TAGLIB_LIB_PATCH_VERSION}" EQUAL "0")
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}")
|
||||
@@ -65,10 +89,10 @@ endif()
|
||||
# 1. If the library source code has changed at all since the last update, then increment revision.
|
||||
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
|
||||
# 3. If any interfaces have been added since the last public release, then increment age.
|
||||
# 4. If any interfaces have been removed since the last public release, then set age to 0.
|
||||
set(TAGLIB_SOVERSION_CURRENT 16)
|
||||
set(TAGLIB_SOVERSION_REVISION 1)
|
||||
set(TAGLIB_SOVERSION_AGE 15)
|
||||
# 4. If any interfaces have been removed since the last public release, then set age to 0.
|
||||
set(TAGLIB_SOVERSION_CURRENT 19)
|
||||
set(TAGLIB_SOVERSION_REVISION 0)
|
||||
set(TAGLIB_SOVERSION_AGE 18)
|
||||
|
||||
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
|
||||
@@ -76,8 +100,12 @@ math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
|
||||
if(${ZLIB_FOUND})
|
||||
set(ZLIB_LIBRARIES_FLAGS -lz)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY)
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${BIN_INSTALL_DIR}")
|
||||
endif()
|
||||
|
||||
@@ -87,7 +115,7 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_FRAMEWORK)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${LIB_INSTALL_DIR}/pkgconfig")
|
||||
endif()
|
||||
|
||||
@@ -106,32 +134,35 @@ if(WITH_MP4)
|
||||
set(TAGLIB_WITH_MP4 TRUE)
|
||||
endif()
|
||||
|
||||
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
|
||||
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
|
||||
if(TRACE_IN_RELEASE)
|
||||
set(TRACE_IN_RELEASE TRUE)
|
||||
endif()
|
||||
|
||||
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
|
||||
|
||||
add_subdirectory(taglib)
|
||||
add_subdirectory(bindings)
|
||||
if(BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif(BUILD_TESTS)
|
||||
add_subdirectory(examples)
|
||||
add_subdirectory(taglib)
|
||||
|
||||
if(BUILD_BINDINGS)
|
||||
add_subdirectory(bindings)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
|
||||
file(COPY doc/taglib.png DESTINATION doc)
|
||||
add_custom_target(docs doxygen)
|
||||
|
||||
# uninstall target
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
|
||||
|
||||
if (NOT TARGET uninstall)
|
||||
add_custom_target(uninstall
|
||||
COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
if(NOT TARGET uninstall)
|
||||
add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
endif()
|
||||
|
||||
@@ -37,70 +37,46 @@ endif()
|
||||
# Determine which kind of atomic operations your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic<unsigned int> x;
|
||||
x.fetch_add(1);
|
||||
x.fetch_sub(1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_ATOMIC)
|
||||
|
||||
if(NOT HAVE_STD_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <boost/atomic.hpp>
|
||||
int main() {
|
||||
boost::atomic<unsigned int> x(1);
|
||||
x.fetch_add(1);
|
||||
x.fetch_sub(1);
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_BOOST_ATOMIC)
|
||||
" HAVE_GCC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_BOOST_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
" HAVE_MAC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_ATOMIC)
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
endif()
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -190,7 +166,17 @@ if(NOT HAVE_VSNPRINTF)
|
||||
" HAVE_VSPRINTF_S)
|
||||
endif()
|
||||
|
||||
# Check for libz using the cmake supplied FindZLIB.cmake
|
||||
# Determine whether your compiler supports ISO _strdup.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <cstring>
|
||||
int main() {
|
||||
_strdup(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_ISO_STRDUP)
|
||||
|
||||
# Determine whether zlib is installed.
|
||||
|
||||
if(NOT ZLIB_SOURCE)
|
||||
find_package(ZLIB)
|
||||
@@ -201,7 +187,9 @@ if(NOT ZLIB_SOURCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTS)
|
||||
# Determine whether CppUnit is installed.
|
||||
|
||||
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
|
||||
find_package(CppUnit)
|
||||
if(NOT CppUnit_FOUND)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
@@ -209,3 +197,7 @@ if(BUILD_TESTS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Detect WinRT mode
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(PLATFORM WINRT 1)
|
||||
endif()
|
||||
|
||||
159
INSTALL
159
INSTALL
@@ -1,159 +0,0 @@
|
||||
TagLib Installation
|
||||
===================
|
||||
|
||||
TagLib uses the CMake build system. As a user, you will most likely want to
|
||||
build TagLib in release mode and install it into a system-wide location.
|
||||
This can be done using the following commands:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release .
|
||||
make
|
||||
sudo make install
|
||||
|
||||
In order to build the included examples, use the BUILD_EXAMPLES option:
|
||||
|
||||
cmake -DBUILD_EXAMPLES=ON [...]
|
||||
|
||||
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
|
||||
running CMake.
|
||||
|
||||
Mac OS X
|
||||
--------
|
||||
|
||||
On Mac OS X, you might want to build a framework that can be easily integrated
|
||||
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
|
||||
TagLib as a framework. For example, the following command can be used to build
|
||||
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_FRAMEWORK=ON \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
|
||||
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
|
||||
|
||||
For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
|
||||
-DENABLE_STATIC=ON \
|
||||
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
|
||||
|
||||
After 'make', and 'make install', add libtag.a to your XCode project, and add
|
||||
the include folder to the project's User Header Search Paths.
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
It's Windows ... Systems vary!
|
||||
This means you need to adjust things to suit your system, especially paths.
|
||||
|
||||
Tested with:
|
||||
Microsoft Visual Studio 2010
|
||||
Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
|
||||
MinGW32-4.8.0
|
||||
|
||||
Requirements:
|
||||
1. Tool chain, Build Environment, Whatever ya want to call it ...
|
||||
Installed and working.
|
||||
2. CMake program. (Available at: www.cmake.org)
|
||||
Installed and working.
|
||||
|
||||
Optional:
|
||||
1. Zlib library.
|
||||
Available in some Tool Chains, Not all.
|
||||
Search the web, Take your choice.
|
||||
|
||||
Useful configuration options used with CMake (GUI and/or Command line):
|
||||
Any of the ZLIB_ variables may be used at the command line, ZLIB_ROOT is only
|
||||
available on the Command line.
|
||||
ZLIB_ROOT= Where to find ZLib's root directory.
|
||||
Assumes parent of: \include and \lib.
|
||||
ZLIB_INCLUDE_DIR= Where to find ZLib's Include directory.
|
||||
ZLIB_LIBRARY= Where to find ZLib's Library.
|
||||
ZLIB_SOURCE= Where to find ZLib's Source Code.
|
||||
Alternative to ZLIB_INCLUDE_DIR and ZLIB_LIBRARY.
|
||||
CMAKE_INSTALL_PREFIX= Where to install Taglib.
|
||||
CMAKE_BUILD_TYPE= Release, Debug, etc ... (Not available in MSVC)
|
||||
|
||||
The easiest way is at the Command Prompt.
|
||||
MSVS Command Prompt for MSVS Users.
|
||||
(Batch file and/or Shortcuts are your friends)
|
||||
|
||||
1. Build the Makefiles:
|
||||
Replace "GENERATOR" with your needs.
|
||||
For MSVS : "Visual Studio X" where X is the single or two digit version.
|
||||
For MinGW: "MinGW Makefiles"
|
||||
|
||||
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
|
||||
|
||||
Or use the CMake GUI:
|
||||
1. Open CMake GUI.
|
||||
2. Set Paths.
|
||||
"Where is the source code" and "Where to build the binaries"
|
||||
Example, Both would be: C:\GitRoot\taglib
|
||||
3. Tick: Advanced
|
||||
4. Select: Configure
|
||||
5. Select: Generator
|
||||
6. Tick: Use default native compilers
|
||||
7. Select: Finish
|
||||
Wait until done.
|
||||
5. If using ZLib, Scroll down.
|
||||
(to the bottom of the list of options ... should go over them all)
|
||||
1. Edit: ZLIB_INCLUDE_DIR
|
||||
2. Edit: ZLIB_LIBRARY
|
||||
6. Select: Generate
|
||||
|
||||
2. Build the project:
|
||||
MSVS:
|
||||
C:\GitRoot\taglib> msbuild all_build.vcxproj /p:Configuration=Release
|
||||
OR (Depending on MSVS version or personal choice)
|
||||
C:\GitRoot\taglib> devenv all_build.vcxproj /build Release
|
||||
MinGW:
|
||||
C:\GitRoot\taglib> gmake
|
||||
OR (Depending on MinGW install)
|
||||
C:\GitRoot\taglib> mingw32-make
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open MSVS.
|
||||
2. Open taglib solution.
|
||||
3. Set build type to: Release (look in the tool bars)
|
||||
2. Hit F7 to build the solution. (project)
|
||||
|
||||
3. Install the project:
|
||||
(Change 'install' to 'uninstall' to uninstall the project)
|
||||
MSVS:
|
||||
C:\GitRoot\taglib> msbuild install.vcxproj
|
||||
OR (Depending on MSVC version or personal choice)
|
||||
C:\GitRoot\taglib> devenv install.vcxproj
|
||||
MinGW:
|
||||
C:\GitRoot\taglib> gmake install
|
||||
OR (Depending on MinGW install)
|
||||
C:\GitRoot\taglib> mingw32-make install
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open project.
|
||||
2. Open Solution Explorer.
|
||||
3. Right Click: INSTALL
|
||||
4. Select: Project Only
|
||||
5. Select: Build Only INSTALL
|
||||
|
||||
To build a static library enable the following two options with CMake.
|
||||
-DENABLE_STATIC=ON -DENABLE_STATIC_RUNTIME=ON
|
||||
|
||||
Including ENABLE_STATIC_RUNTIME=ON indicates you want TagLib built using the
|
||||
static runtime library, rather than the DLL form of the runtime.
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
|
||||
If you want to run the test suite to make sure TagLib works properly on your
|
||||
system, you need to have cppunit installed. To build the tests, include
|
||||
the option -DBUILD_TESTS=on when running cmake.
|
||||
|
||||
The test suite has a custom target in the build system, so you can run
|
||||
the tests using make:
|
||||
|
||||
make check
|
||||
|
||||
175
INSTALL.md
Normal file
175
INSTALL.md
Normal file
@@ -0,0 +1,175 @@
|
||||
TagLib Installation
|
||||
===================
|
||||
|
||||
TagLib uses the CMake build system. As a user, you will most likely want to
|
||||
build TagLib in release mode and install it into a system-wide location.
|
||||
This can be done using the following commands:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release .
|
||||
make
|
||||
sudo make install
|
||||
|
||||
In order to build the included examples, use the `BUILD_EXAMPLES` option:
|
||||
|
||||
cmake -DBUILD_EXAMPLES=ON [...]
|
||||
|
||||
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
|
||||
running CMake.
|
||||
|
||||
Mac OS X
|
||||
--------
|
||||
|
||||
On Mac OS X, you might want to build a framework that can be easily integrated
|
||||
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
|
||||
TagLib as a framework. For example, the following command can be used to build
|
||||
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_FRAMEWORK=ON \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
|
||||
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
|
||||
|
||||
For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
|
||||
|
||||
After `make`, and `make install`, add `libtag.` to your XCode project, and add
|
||||
the include folder to the project's User Header Search Paths.
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
It's Windows ... Systems vary!
|
||||
This means you need to adjust things to suit your system, especially paths.
|
||||
|
||||
Tested with:
|
||||
* Microsoft Visual Studio 2010, 2015, 2017
|
||||
* Microsoft C++ Build Tools 2015, 2017 (standalone packages not requiring Visual Studio)
|
||||
* Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
|
||||
* MinGW32-4.8.0
|
||||
|
||||
Requirements:
|
||||
* Tool chain, build environment, whatever ya want to call it ...
|
||||
Installed and working.
|
||||
* CMake program. (Available at: www.cmake.org)
|
||||
Installed and working.
|
||||
|
||||
Optional:
|
||||
* Zlib library.
|
||||
Available in some tool chains, not all.
|
||||
Search the web, take your choice.
|
||||
|
||||
Useful configuration options used with CMake (GUI and/or command line):
|
||||
Any of the `ZLIB_` variables may be used at the command line, `ZLIB_ROOT` is only
|
||||
available on the command line.
|
||||
|
||||
| option | description |
|
||||
---------------------| ------------|
|
||||
`ZLIB_ROOT=` | Where to find ZLib's root directory. Assumes parent of: `\include` and `\lib.`|
|
||||
`ZLIB_INCLUDE_DIR=` | Where to find ZLib's Include directory.|
|
||||
`ZLIB_LIBRARY=` | Where to find ZLib's Library.
|
||||
`ZLIB_SOURCE=` | Where to find ZLib's Source Code. Alternative to `ZLIB_INCLUDE_DIR` and `ZLIB_LIBRARY`.
|
||||
`CMAKE_INSTALL_PREFIX=` | Where to install Taglib. |
|
||||
`CMAKE_BUILD_TYPE=` | Release, Debug, etc ... (Not available in MSVC) |
|
||||
|
||||
The easiest way is at the command prompt (Visual C++ command prompt for MSVS users – batch file and/or shortcuts are your friends).
|
||||
|
||||
1. **Build the Makefiles:**
|
||||
|
||||
Replace "GENERATOR" with your needs.
|
||||
* For MSVS: `Visual Studio XX YYYY`, e.g. `Visual Studio 14 2015`.
|
||||
|
||||
**Note**: As Visual Studio 2017 supports CMake, you can skip this step and open the taglib
|
||||
folder in VS instead.
|
||||
* For MinGW: `MinGW Makefiles`
|
||||
|
||||
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
|
||||
|
||||
Or use the CMake GUI:
|
||||
1. Open CMake GUI.
|
||||
2. Set paths: *Where is the source code* and *Where to build the binaries*.
|
||||
|
||||
In the example, both would be: `C:\GitRoot\taglib`
|
||||
3. Tick: Advanced
|
||||
4. Select: Configure
|
||||
5. Select: Generator
|
||||
6. Tick: Use default native compilers
|
||||
7. Select: Finish
|
||||
Wait until done.
|
||||
8. If using ZLib, Scroll down.
|
||||
(to the bottom of the list of options ... should go over them all)
|
||||
1. Edit: `ZLIB_INCLUDE_DIR`
|
||||
2. Edit: `ZLIB_LIBRARY`
|
||||
9. Select: Generate
|
||||
|
||||
2. **Build the project**
|
||||
* MSVS:
|
||||
|
||||
C:\GitRoot\taglib> msbuild all_build.vcxproj /p:Configuration=Release
|
||||
OR (Depending on MSVS version or personal choice)
|
||||
|
||||
C:\GitRoot\taglib> devenv all_build.vcxproj /build Release
|
||||
OR in the MSVS GUI:
|
||||
1. Open MSVS.
|
||||
2. Open taglib solution.
|
||||
3. Set build type to: Release (look in the tool bars)
|
||||
2. Hit F7 to build the solution. (project)
|
||||
* MinGW:
|
||||
|
||||
C:\GitRoot\taglib> gmake
|
||||
|
||||
OR (Depending on MinGW install)
|
||||
|
||||
C:\GitRoot\taglib> mingw32-make
|
||||
|
||||
|
||||
|
||||
3. **Install the project**
|
||||
|
||||
(Change `install` to `uninstall` to uninstall the project)
|
||||
* MSVS:
|
||||
|
||||
C:\GitRoot\taglib> msbuild install.vcxproj
|
||||
OR (Depending on MSVC version or personal choice)
|
||||
|
||||
C:\GitRoot\taglib> devenv install.vcxproj
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open project.
|
||||
2. Open Solution Explorer.
|
||||
3. Right Click: INSTALL
|
||||
4. Select: Project Only
|
||||
5. Select: Build Only INSTALL
|
||||
* MinGW:
|
||||
|
||||
C:\GitRoot\taglib> gmake install
|
||||
OR (Depending on MinGW install)
|
||||
|
||||
C:\GitRoot\taglib> mingw32-make install
|
||||
|
||||
|
||||
To build a static library, set the following two options with CMake:
|
||||
|
||||
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
|
||||
|
||||
Including `ENABLE_STATIC_RUNTIME=ON` indicates you want TagLib built using the
|
||||
static runtime library, rather than the DLL form of the runtime.
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
|
||||
If you want to run the test suite to make sure TagLib works properly on your
|
||||
system, you need to have cppunit installed. To build the tests, include
|
||||
the option `-DBUILD_TESTS=on` when running cmake.
|
||||
|
||||
The test suite has a custom target in the build system, so you can run
|
||||
the tests using make:
|
||||
|
||||
make check
|
||||
100
NEWS
100
NEWS
@@ -1,3 +1,97 @@
|
||||
============================
|
||||
|
||||
* Added support for WinRT.
|
||||
* Added support for Linux on POWER.
|
||||
* Added support for classical music tags of iTunes 12.5.
|
||||
* Added support for file descriptor to FileStream.
|
||||
* Added support for 'cmID', 'purl', 'egid' MP4 atoms.
|
||||
* Added support for 'GRP1' ID3v2 frame.
|
||||
* Added support for extensible WAV subformat.
|
||||
* Enabled FileRef to detect file types based on the stream content.
|
||||
* Dropped support for Windows 9x and NT 4.0 or older.
|
||||
* Check for mandatory header objects in ASF files.
|
||||
* More tolerant handling of RIFF padding, WAV files, broken MPEG streams.
|
||||
* Improved calculation of Ogg, Opus, Speex, WAV, MP4 bitrates.
|
||||
* Improved Windows compatibility by storing FLAC picture after comments.
|
||||
* Fixed numerical genres in ID3v2.3.0 'TCON' frames.
|
||||
* Fixed consistency of API removing MP4 items when empty values are set.
|
||||
* Fixed consistency of API preferring COMM frames with no description.
|
||||
* Fixed OOB read on invalid Ogg FLAC files (CVE-2018-11439).
|
||||
* Fixed handling of empty MPEG files.
|
||||
* Fixed parsing MP4 mdhd timescale.
|
||||
* Fixed reading MP4 atoms with zero length.
|
||||
* Fixed reading FLAC files with zero-sized seektables.
|
||||
* Fixed handling of lowercase field names in Vorbis Comments.
|
||||
* Fixed handling of 'rate' atoms in MP4 files.
|
||||
* Fixed handling of invalid UTF-8 sequences.
|
||||
* Fixed possible file corruptions when saving Ogg files.
|
||||
* Fixed handling of non-audio blocks, sampling rates, DSD audio in WavPack files.
|
||||
* TableOfContentsFrame::toString() improved.
|
||||
* UserTextIdentificationFrame::toString() improved.
|
||||
* Marked FileRef::create() deprecated.
|
||||
* Marked MPEG::File::save() with boolean parameters deprecated,
|
||||
provide overloads with enum parameters.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.11.1 (Oct 24, 2016)
|
||||
============================
|
||||
|
||||
* Fixed binary incompatible change in TagLib::String.
|
||||
* Fixed reading ID3v2 CTOC frames with a lot of entries.
|
||||
* Fixed seeking ByteVectorStream from the end.
|
||||
|
||||
TagLib 1.11 (Apr 29, 2016)
|
||||
==========================
|
||||
|
||||
1.11:
|
||||
|
||||
* Fixed reading APE items with long keys.
|
||||
* Fixed reading ID3v2 SYLT frames when description is empty.
|
||||
|
||||
1.11 BETA 2:
|
||||
|
||||
* Better handling of PCM WAV files with a 'fact' chunk.
|
||||
* Better handling of corrupted APE tags.
|
||||
* Efficient decoding of unsynchronized ID3v2 frames.
|
||||
* Fixed text encoding when saving certain frames in ID3v2.3 tags.
|
||||
* Fixed updating the size of RIFF files when removing chunks.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
1.11 BETA:
|
||||
|
||||
* New API for creating FileRef from IOStream.
|
||||
* Added support for ID3v2 PCST and WFED frames.
|
||||
* Added support for pictures in XiphComment.
|
||||
* Added String::clear().
|
||||
* Added FLAC::File::strip() for removing non-standard tags.
|
||||
* Added alternative functions to XiphComment::removeField().
|
||||
* Added BUILD_BINDINGS build option.
|
||||
* Added ENABLE_CCACHE build option.
|
||||
* Replaced ENABLE_STATIC build option with BUILD_SHARED_LIBS.
|
||||
* Better handling of duplicate ID3v2 tags in all kinds of files.
|
||||
* Better handling of duplicate tag chunks in WAV files.
|
||||
* Better handling of duplicate tag chunks in AIFF files.
|
||||
* Better handling of duplicate Vorbis comment blocks in FLAC files.
|
||||
* Better handling of broken MPEG audio frames.
|
||||
* Fixed crash when calling File::properties() after strip().
|
||||
* Fixed crash when parsing certain MPEG files.
|
||||
* Fixed crash when saving Ogg files.
|
||||
* Fixed possible file corruptions when saving ASF files.
|
||||
* Fixed possible file corruptions when saving FLAC files.
|
||||
* Fixed possible file corruptions when saving MP4 files.
|
||||
* Fixed possible file corruptions when saving MPEG files.
|
||||
* Fixed possible file corruptions when saving APE files.
|
||||
* Fixed possible file corruptions when saving Musepack files.
|
||||
* Fixed possible file corruptions when saving WavPack files.
|
||||
* Fixed updating the comment field of Vorbis comments.
|
||||
* Fixed reading date and time in ID3v2.3 tags.
|
||||
* Marked ByteVector::null and ByteVector::isNull() deprecated.
|
||||
* Marked String::null and String::isNull() deprecated.
|
||||
* Marked XiphComment::removeField() deprecated.
|
||||
* Marked Ogg::Page::getCopyWithNewPageSequenceNumber() deprecated. It returns null.
|
||||
* Marked custom integer types deprecated.
|
||||
* Many smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.10 (Nov 11, 2015)
|
||||
==========================
|
||||
|
||||
@@ -163,7 +257,7 @@ TagLib 1.6.3 (Apr 17, 2010)
|
||||
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
|
||||
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
|
||||
* New method `int String::toInt(bool *ok)` which can return whether the
|
||||
conversion to a number was successfull.
|
||||
conversion to a number was successful.
|
||||
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
|
||||
compressed frames). (BUG:231075)
|
||||
|
||||
@@ -171,7 +265,7 @@ TagLib 1.6.2 (Apr 9, 2010)
|
||||
==========================
|
||||
|
||||
* Read Vorbis Comments from the first FLAC metadata block, if there are
|
||||
multipe ones. (BUG:211089)
|
||||
multiple ones. (BUG:211089)
|
||||
* Fixed a memory leak in FileRef's OGA format detection.
|
||||
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
|
||||
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
|
||||
@@ -206,7 +300,7 @@ TagLib 1.6 (Sep 13, 2009)
|
||||
* Added support for disabling dllimport/dllexport on Windows using the
|
||||
TAGLIB_STATIC macro.
|
||||
* Support for parsing the obsolete 'gnre' MP4 atom.
|
||||
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determin if
|
||||
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if
|
||||
TagLib was built with MP4/ASF support.
|
||||
|
||||
1.6 RC1:
|
||||
|
||||
26
README.md
Normal file
26
README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 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,
|
||||
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
|
||||
|
||||
@@ -21,7 +21,14 @@ set(tag_c_HDRS tag_c.h)
|
||||
add_library(tag_c tag_c.cpp ${tag_c_HDRS})
|
||||
|
||||
target_link_libraries(tag_c tag)
|
||||
set_target_properties(tag_c PROPERTIES PUBLIC_HEADER "${tag_c_HDRS}")
|
||||
set_target_properties(tag_c PROPERTIES
|
||||
PUBLIC_HEADER "${tag_c_HDRS}"
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden
|
||||
)
|
||||
endif()
|
||||
if(BUILD_FRAMEWORK)
|
||||
set_target_properties(tag_c PROPERTIES FRAMEWORK TRUE)
|
||||
endif()
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
* USA *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
@@ -40,9 +44,32 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
static List<char *> strings;
|
||||
static bool unicodeStrings = true;
|
||||
static bool stringManagementEnabled = true;
|
||||
namespace
|
||||
{
|
||||
List<char *> strings;
|
||||
bool unicodeStrings = true;
|
||||
bool stringManagementEnabled = true;
|
||||
|
||||
char *stringToCharArray(const String &s)
|
||||
{
|
||||
const std::string str = s.to8Bit(unicodeStrings);
|
||||
|
||||
#ifdef HAVE_ISO_STRDUP
|
||||
|
||||
return ::_strdup(str.c_str());
|
||||
|
||||
#else
|
||||
|
||||
return ::strdup(str.c_str());
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
String charArrayToString(const char *s)
|
||||
{
|
||||
return String(s, unicodeStrings ? String::UTF8 : String::Latin1);
|
||||
}
|
||||
}
|
||||
|
||||
void taglib_set_strings_unicode(BOOL unicode)
|
||||
{
|
||||
@@ -94,8 +121,6 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void taglib_file_free(TagLib_File *file)
|
||||
@@ -132,7 +157,7 @@ BOOL taglib_file_save(TagLib_File *file)
|
||||
char *taglib_tag_title(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->title().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->title());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -141,7 +166,7 @@ char *taglib_tag_title(const TagLib_Tag *tag)
|
||||
char *taglib_tag_artist(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->artist().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->artist());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -150,7 +175,7 @@ char *taglib_tag_artist(const TagLib_Tag *tag)
|
||||
char *taglib_tag_album(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->album().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->album());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -159,7 +184,7 @@ char *taglib_tag_album(const TagLib_Tag *tag)
|
||||
char *taglib_tag_comment(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->comment().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->comment());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -168,7 +193,7 @@ char *taglib_tag_comment(const TagLib_Tag *tag)
|
||||
char *taglib_tag_genre(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->genre().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->genre());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -189,31 +214,31 @@ unsigned int taglib_tag_track(const TagLib_Tag *tag)
|
||||
void taglib_tag_set_title(TagLib_Tag *tag, const char *title)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setTitle(String(title, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setTitle(charArrayToString(title));
|
||||
}
|
||||
|
||||
void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setArtist(String(artist, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setArtist(charArrayToString(artist));
|
||||
}
|
||||
|
||||
void taglib_tag_set_album(TagLib_Tag *tag, const char *album)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setAlbum(String(album, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setAlbum(charArrayToString(album));
|
||||
}
|
||||
|
||||
void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setComment(String(comment, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setComment(charArrayToString(comment));
|
||||
}
|
||||
|
||||
void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setGenre(String(genre, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setGenre(charArrayToString(genre));
|
||||
}
|
||||
|
||||
void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year)
|
||||
|
||||
@@ -124,7 +124,7 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_F
|
||||
TAGLIB_C_EXPORT void taglib_file_free(TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Returns true if the file is open and readble and valid information for
|
||||
* Returns true if the file is open and readable and valid information for
|
||||
* the Tag and / or AudioProperties was found.
|
||||
*/
|
||||
|
||||
@@ -137,7 +137,7 @@ TAGLIB_C_EXPORT BOOL taglib_file_is_valid(const TagLib_File *file);
|
||||
TAGLIB_C_EXPORT TagLib_Tag *taglib_file_tag(const TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the the audio properties associated with this file. This
|
||||
* Returns a pointer to the audio properties associated with this file. This
|
||||
* will be freed automatically when the file is freed.
|
||||
*/
|
||||
TAGLIB_C_EXPORT const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/* config.h. Generated by cmake from config.h.cmake */
|
||||
|
||||
#ifndef TAGLIB_CONFIG_H
|
||||
#define TAGLIB_CONFIG_H
|
||||
|
||||
/* Defined if your compiler supports some byte swap functions */
|
||||
#cmakedefine HAVE_GCC_BYTESWAP 1
|
||||
#cmakedefine HAVE_GLIBC_BYTESWAP 1
|
||||
@@ -8,8 +11,6 @@
|
||||
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_STD_ATOMIC 1
|
||||
#cmakedefine HAVE_BOOST_ATOMIC 1
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
@@ -19,10 +20,15 @@
|
||||
#cmakedefine HAVE_VSNPRINTF 1
|
||||
#cmakedefine HAVE_VSPRINTF_S 1
|
||||
|
||||
/* Defined if you have libz */
|
||||
/* Defined if your compiler supports ISO _strdup */
|
||||
#cmakedefine HAVE_ISO_STRDUP 1
|
||||
|
||||
/* Defined if zlib is installed */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
|
||||
/* Indicates whether debug messages are shown even in release mode */
|
||||
#cmakedefine TRACE_IN_RELEASE 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,50 +1,40 @@
|
||||
if(BUILD_EXAMPLES)
|
||||
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/ )
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/
|
||||
)
|
||||
|
||||
if(ENABLE_STATIC)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
endif(ENABLE_STATIC)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
endif()
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(tagreader tagreader.cpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(tagreader tag )
|
||||
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader tag)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(tagreader_c tagreader_c.c)
|
||||
|
||||
TARGET_LINK_LIBRARIES(tagreader_c tag_c )
|
||||
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c tag_c)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(tagwriter tagwriter.cpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(tagwriter tag )
|
||||
|
||||
add_executable(tagwriter tagwriter.cpp)
|
||||
target_link_libraries(tagwriter tag)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(framelist framelist.cpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(framelist tag )
|
||||
|
||||
add_executable(framelist framelist.cpp)
|
||||
target_link_libraries(framelist tag)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(strip-id3v1 strip-id3v1.cpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(strip-id3v1 tag )
|
||||
|
||||
|
||||
endif(BUILD_EXAMPLES)
|
||||
add_executable(strip-id3v1 strip-id3v1.cpp)
|
||||
target_link_libraries(strip-id3v1 tag)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <id3v2tag.h>
|
||||
#include <id3v2frame.h>
|
||||
#include <id3v2header.h>
|
||||
#include <commentsframe.h>
|
||||
|
||||
#include <id3v1tag.h>
|
||||
|
||||
@@ -65,8 +66,15 @@ int main(int argc, char *argv[])
|
||||
<< endl;
|
||||
|
||||
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
|
||||
for(; it != id3v2tag->frameList().end(); it++)
|
||||
cout << (*it)->frameID() << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
for(; it != id3v2tag->frameList().end(); it++) {
|
||||
cout << (*it)->frameID();
|
||||
|
||||
if(ID3v2::CommentsFrame *comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
|
||||
if(!comment->description().isEmpty())
|
||||
cout << " [" << comment->description() << "]";
|
||||
|
||||
cout << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid id3v2 tag" << endl;
|
||||
|
||||
@@ -14,10 +14,10 @@ EOH
|
||||
exit 1;
|
||||
}
|
||||
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${CMAKE_INSTALL_PREFIX}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=@LIB_INSTALL_DIR@
|
||||
includedir=@INCLUDE_INSTALL_DIR@
|
||||
|
||||
flags=""
|
||||
|
||||
@@ -29,13 +29,13 @@ while test $# -gt 0
|
||||
do
|
||||
case $1 in
|
||||
--libs)
|
||||
flags="$flags -L$libdir -ltag"
|
||||
flags="$flags -L$libdir -ltag @ZLIB_LIBRARIES_FLAGS@"
|
||||
;;
|
||||
--cflags)
|
||||
flags="$flags -I$includedir/taglib"
|
||||
flags="$flags -I$includedir -I$includedir/taglib"
|
||||
;;
|
||||
--version)
|
||||
echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
echo @TAGLIB_LIB_VERSION_STRING@
|
||||
;;
|
||||
--prefix)
|
||||
echo $prefix
|
||||
|
||||
@@ -28,7 +28,7 @@ goto theend
|
||||
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
|
||||
:doit
|
||||
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
|
||||
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR} -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${CMAKE_INSTALL_PREFIX}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=@LIB_INSTALL_DIR@
|
||||
includedir=@INCLUDE_INSTALL_DIR@
|
||||
|
||||
Name: TagLib
|
||||
Description: Audio meta-data library
|
||||
Requires:
|
||||
Version: ${TAGLIB_LIB_VERSION_STRING}
|
||||
Libs: -L${LIB_INSTALL_DIR} -ltag
|
||||
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
Version: @TAGLIB_LIB_VERSION_STRING@
|
||||
Libs: -L${libdir} -ltag @ZLIB_LIBRARIES_FLAGS@
|
||||
Cflags: -I${includedir} -I${includedir}/taglib
|
||||
|
||||
@@ -24,6 +24,7 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${taglib_SOURCE_DIR}/3rdparty
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
@@ -60,6 +61,7 @@ set(tag_HDRS
|
||||
mpeg/xingheader.h
|
||||
mpeg/id3v1/id3v1tag.h
|
||||
mpeg/id3v1/id3v1genres.h
|
||||
mpeg/id3v2/id3v2.h
|
||||
mpeg/id3v2/id3v2extendedheader.h
|
||||
mpeg/id3v2/id3v2frame.h
|
||||
mpeg/id3v2/id3v2header.h
|
||||
@@ -83,6 +85,7 @@ set(tag_HDRS
|
||||
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
|
||||
@@ -177,6 +180,7 @@ set(frames_SRCS
|
||||
mpeg/id3v2/frames/urllinkframe.cpp
|
||||
mpeg/id3v2/frames/chapterframe.cpp
|
||||
mpeg/id3v2/frames/tableofcontentsframe.cpp
|
||||
mpeg/id3v2/frames/podcastframe.cpp
|
||||
)
|
||||
|
||||
set(ogg_SRCS
|
||||
@@ -303,14 +307,9 @@ set(toolkit_SRCS
|
||||
toolkit/tpropertymap.cpp
|
||||
toolkit/trefcounter.cpp
|
||||
toolkit/tdebuglistener.cpp
|
||||
toolkit/tzlib.cpp
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
set(unicode_SRCS
|
||||
toolkit/unicode.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HAVE_ZLIB_SOURCE)
|
||||
set(zlib_SRCS
|
||||
${ZLIB_SOURCE}/adler32.c
|
||||
@@ -327,16 +326,18 @@ set(tag_LIB_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}
|
||||
${unicode_SRCS} ${zlib_SRCS}
|
||||
${zlib_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
audioproperties.cpp
|
||||
tagutils.cpp
|
||||
)
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
set_property(TARGET tag PROPERTY CXX_STANDARD 98)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
|
||||
target_link_libraries(tag ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
@@ -348,8 +349,18 @@ set_target_properties(tag PROPERTIES
|
||||
LINK_INTERFACE_LIBRARIES ""
|
||||
PUBLIC_HEADER "${tag_HDRS}"
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
endif()
|
||||
|
||||
if(BUILD_FRAMEWORK)
|
||||
set_target_properties(tag PROPERTIES FRAMEWORK TRUE)
|
||||
unset(INSTALL_NAME_DIR)
|
||||
set_target_properties(tag PROPERTIES
|
||||
FRAMEWORK TRUE
|
||||
MACOSX_RPATH 1
|
||||
VERSION "A"
|
||||
SOVERSION "A"
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS tag
|
||||
|
||||
@@ -49,8 +49,8 @@ APE Tag Version 2.000 (with header, recommended):
|
||||
|
||||
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
|
||||
is not required, but is strongly recommended. It would be desirable for the
|
||||
items 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
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
#include <id3v1tag.h>
|
||||
#include <id3v2header.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "apefile.h"
|
||||
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
|
||||
@@ -61,10 +61,7 @@ public:
|
||||
ID3v2Header(0),
|
||||
ID3v2Location(-1),
|
||||
ID3v2Size(0),
|
||||
properties(0),
|
||||
hasAPE(false),
|
||||
hasID3v1(false),
|
||||
hasID3v2(false) {}
|
||||
properties(0) {}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
@@ -73,26 +70,31 @@ public:
|
||||
}
|
||||
|
||||
long APELocation;
|
||||
uint APESize;
|
||||
long APESize;
|
||||
|
||||
long ID3v1Location;
|
||||
|
||||
ID3v2::Header *ID3v2Header;
|
||||
long ID3v2Location;
|
||||
uint ID3v2Size;
|
||||
long ID3v2Size;
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
|
||||
// These indicate whether the file *on disk* has these tags, not if
|
||||
// this data structure does. This is used in computing offsets.
|
||||
|
||||
bool hasAPE;
|
||||
bool hasID3v1;
|
||||
bool hasID3v2;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -125,26 +127,20 @@ TagLib::Tag *APE::File::tag() const
|
||||
|
||||
PropertyMap APE::File::properties() const
|
||||
{
|
||||
if(d->hasAPE)
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, false)->properties();
|
||||
if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->properties();
|
||||
return PropertyMap();
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void APE::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
if(d->hasAPE)
|
||||
d->tag.access<APE::Tag>(ApeAPEIndex, false)->removeUnsupportedProperties(properties);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->removeUnsupportedProperties(properties);
|
||||
d->tag.removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
|
||||
if(ID3v1Tag())
|
||||
ID3v1Tag()->setProperties(properties);
|
||||
|
||||
return APETag(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
APE::Properties *APE::File::audioProperties() const
|
||||
@@ -161,64 +157,67 @@ bool APE::File::save()
|
||||
|
||||
// Update ID3v1 tag
|
||||
|
||||
if(ID3v1Tag()) {
|
||||
if(d->hasID3v1) {
|
||||
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);
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->ID3v1Location = tell();
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
}
|
||||
else {
|
||||
if(d->hasID3v1) {
|
||||
removeBlock(d->ID3v1Location, 128);
|
||||
d->hasID3v1 = false;
|
||||
if(d->hasAPE) {
|
||||
if(d->APELocation > d->ID3v1Location)
|
||||
d->APELocation -= 128;
|
||||
}
|
||||
|
||||
// ID3v1 tag is empty. Remove the old one.
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
truncate(d->ID3v1Location);
|
||||
d->ID3v1Location = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Update APE tag
|
||||
|
||||
if(APETag()) {
|
||||
if(d->hasAPE)
|
||||
insert(APETag()->render(), d->APELocation, d->APESize);
|
||||
else {
|
||||
if(d->hasID3v1) {
|
||||
insert(APETag()->render(), d->ID3v1Location, 0);
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->hasAPE = true;
|
||||
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;
|
||||
d->ID3v1Location += d->APESize;
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->APELocation = tell();
|
||||
writeBlock(APETag()->render());
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->hasAPE = true;
|
||||
}
|
||||
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 {
|
||||
if(d->hasAPE) {
|
||||
|
||||
// APE tag is empty. Remove the old one.
|
||||
|
||||
if(d->APELocation >= 0) {
|
||||
removeBlock(d->APELocation, d->APESize);
|
||||
d->hasAPE = false;
|
||||
if(d->hasID3v1) {
|
||||
if(d->ID3v1Location > d->APELocation) {
|
||||
d->ID3v1Location -= d->APESize;
|
||||
}
|
||||
}
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location -= d->APESize;
|
||||
|
||||
d->APELocation = -1;
|
||||
d->APESize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
ID3v1::Tag *APE::File::ID3v1Tag(bool create)
|
||||
@@ -233,27 +232,24 @@ APE::Tag *APE::File::APETag(bool create)
|
||||
|
||||
void APE::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1) {
|
||||
if(tags & ID3v1)
|
||||
d->tag.set(ApeID3v1Index, 0);
|
||||
APETag(true);
|
||||
}
|
||||
|
||||
if(tags & APE) {
|
||||
if(tags & APE)
|
||||
d->tag.set(ApeAPEIndex, 0);
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
}
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
}
|
||||
|
||||
bool APE::File::hasAPETag() const
|
||||
{
|
||||
return d->hasAPE;
|
||||
return (d->APELocation >= 0);
|
||||
}
|
||||
|
||||
bool APE::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
return (d->ID3v1Location >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -264,36 +260,32 @@ void APE::File::read(bool readProperties)
|
||||
{
|
||||
// Look for an ID3v2 tag
|
||||
|
||||
d->ID3v2Location = findID3v2();
|
||||
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();
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = findID3v1();
|
||||
d->ID3v1Location = Utils::findID3v1(this);
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
// Look for an APE tag
|
||||
|
||||
d->APELocation = findAPE();
|
||||
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 + APETag()->footer()->size() - d->APESize;
|
||||
d->hasAPE = true;
|
||||
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
|
||||
}
|
||||
|
||||
if(!d->hasID3v1)
|
||||
if(d->ID3v1Location < 0)
|
||||
APETag(true);
|
||||
|
||||
// Look for APE audio properties
|
||||
@@ -302,14 +294,14 @@ void APE::File::read(bool readProperties)
|
||||
|
||||
long streamLength;
|
||||
|
||||
if(d->hasAPE)
|
||||
if(d->APELocation >= 0)
|
||||
streamLength = d->APELocation;
|
||||
else if(d->hasID3v1)
|
||||
else if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location;
|
||||
else
|
||||
streamLength = length();
|
||||
|
||||
if(d->hasID3v2) {
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location + d->ID3v2Size);
|
||||
streamLength -= (d->ID3v2Location + d->ID3v2Size);
|
||||
}
|
||||
@@ -320,48 +312,3 @@ void APE::File::read(bool readProperties)
|
||||
d->properties = new Properties(this, streamLength);
|
||||
}
|
||||
}
|
||||
|
||||
long APE::File::findAPE()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
if(d->hasID3v1)
|
||||
seek(-160, End);
|
||||
else
|
||||
seek(-32, End);
|
||||
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(8) == APE::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long APE::File::findID3v1()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(-128, End);
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long APE::File::findID3v2()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(0);
|
||||
|
||||
if(readBlock(3) == ID3v2::Header::fileIdentifier())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -146,9 +146,6 @@ namespace TagLib {
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
* \warning In the current implementation, it's dangerous to call save()
|
||||
* repeatedly. At worst it will corrupt the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
@@ -214,14 +211,20 @@ namespace TagLib {
|
||||
*/
|
||||
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);
|
||||
long findAPE();
|
||||
long findID3v1();
|
||||
long findID3v2();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
|
||||
@@ -38,54 +38,51 @@ using namespace APE;
|
||||
class APE::Footer::FooterPrivate
|
||||
{
|
||||
public:
|
||||
FooterPrivate() : version(0),
|
||||
footerPresent(true),
|
||||
headerPresent(false),
|
||||
isHeader(false),
|
||||
itemCount(0),
|
||||
tagSize(0) {}
|
||||
FooterPrivate() :
|
||||
version(0),
|
||||
footerPresent(true),
|
||||
headerPresent(false),
|
||||
isHeader(false),
|
||||
itemCount(0),
|
||||
tagSize(0) {}
|
||||
|
||||
~FooterPrivate() {}
|
||||
|
||||
uint version;
|
||||
unsigned int version;
|
||||
|
||||
bool footerPresent;
|
||||
bool headerPresent;
|
||||
|
||||
bool isHeader;
|
||||
|
||||
uint itemCount;
|
||||
uint tagSize;
|
||||
|
||||
static const uint size = 32;
|
||||
unsigned int itemCount;
|
||||
unsigned int tagSize;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TagLib::uint APE::Footer::size()
|
||||
unsigned int APE::Footer::size()
|
||||
{
|
||||
return FooterPrivate::size;
|
||||
return 32;
|
||||
}
|
||||
|
||||
ByteVector APE::Footer::fileIdentifier()
|
||||
{
|
||||
return ByteVector::fromCString("APETAGEX");
|
||||
return ByteVector("APETAGEX");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Footer::Footer()
|
||||
APE::Footer::Footer() :
|
||||
d(new FooterPrivate())
|
||||
{
|
||||
d = new FooterPrivate;
|
||||
}
|
||||
|
||||
APE::Footer::Footer(const ByteVector &data)
|
||||
APE::Footer::Footer(const ByteVector &data) :
|
||||
d(new FooterPrivate())
|
||||
{
|
||||
d = new FooterPrivate;
|
||||
parse(data);
|
||||
}
|
||||
|
||||
@@ -94,7 +91,7 @@ APE::Footer::~Footer()
|
||||
delete d;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Footer::version() const
|
||||
unsigned int APE::Footer::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
@@ -119,30 +116,30 @@ void APE::Footer::setHeaderPresent(bool b) const
|
||||
d->headerPresent = b;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Footer::itemCount() const
|
||||
unsigned int APE::Footer::itemCount() const
|
||||
{
|
||||
return d->itemCount;
|
||||
}
|
||||
|
||||
void APE::Footer::setItemCount(uint s)
|
||||
void APE::Footer::setItemCount(unsigned int s)
|
||||
{
|
||||
d->itemCount = s;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Footer::tagSize() const
|
||||
unsigned int APE::Footer::tagSize() const
|
||||
{
|
||||
return d->tagSize;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Footer::completeTagSize() const
|
||||
unsigned int APE::Footer::completeTagSize() const
|
||||
{
|
||||
if(d->headerPresent)
|
||||
return d->tagSize + d->size;
|
||||
return d->tagSize + size();
|
||||
else
|
||||
return d->tagSize;
|
||||
}
|
||||
|
||||
void APE::Footer::setTagSize(uint s)
|
||||
void APE::Footer::setTagSize(unsigned int s)
|
||||
{
|
||||
d->tagSize = s;
|
||||
}
|
||||
@@ -154,13 +151,14 @@ void APE::Footer::setData(const ByteVector &data)
|
||||
|
||||
ByteVector APE::Footer::renderFooter() const
|
||||
{
|
||||
return render(false);
|
||||
return render(false);
|
||||
}
|
||||
|
||||
ByteVector APE::Footer::renderHeader() const
|
||||
{
|
||||
if (!d->headerPresent) return ByteVector();
|
||||
|
||||
if(!d->headerPresent)
|
||||
return ByteVector();
|
||||
else
|
||||
return render(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the version number. (Note: This is the 1000 or 2000.)
|
||||
*/
|
||||
uint version() const;
|
||||
unsigned int version() const;
|
||||
|
||||
/*!
|
||||
* Returns true if a header is present in the tag.
|
||||
@@ -89,13 +89,13 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the number of items in the tag.
|
||||
*/
|
||||
uint itemCount() const;
|
||||
unsigned int itemCount() const;
|
||||
|
||||
/*!
|
||||
* Set the item count to \a s.
|
||||
* \see itemCount()
|
||||
*/
|
||||
void setItemCount(uint s);
|
||||
void setItemCount(unsigned int s);
|
||||
|
||||
/*!
|
||||
* Returns the tag size in bytes. This is the size of the frame content and footer.
|
||||
@@ -103,7 +103,7 @@ namespace TagLib {
|
||||
*
|
||||
* \see completeTagSize()
|
||||
*/
|
||||
uint tagSize() const;
|
||||
unsigned int tagSize() const;
|
||||
|
||||
/*!
|
||||
* Returns the tag size, including if present, the header
|
||||
@@ -111,18 +111,18 @@ namespace TagLib {
|
||||
*
|
||||
* \see tagSize()
|
||||
*/
|
||||
uint completeTagSize() const;
|
||||
unsigned int completeTagSize() const;
|
||||
|
||||
/*!
|
||||
* Set the tag size to \a s.
|
||||
* \see tagSize()
|
||||
*/
|
||||
void setTagSize(uint s);
|
||||
void setTagSize(unsigned int s);
|
||||
|
||||
/*!
|
||||
* Returns the size of the footer. Presently this is always 32 bytes.
|
||||
*/
|
||||
static uint size();
|
||||
static unsigned int size();
|
||||
|
||||
/*!
|
||||
* Returns the string used to identify an APE tag inside of a file.
|
||||
|
||||
@@ -34,7 +34,9 @@ using namespace APE;
|
||||
class APE::Item::ItemPrivate
|
||||
{
|
||||
public:
|
||||
ItemPrivate() : type(Text), readOnly(false) {}
|
||||
ItemPrivate() :
|
||||
type(Text),
|
||||
readOnly(false) {}
|
||||
|
||||
Item::ItemTypes type;
|
||||
String key;
|
||||
@@ -43,40 +45,45 @@ public:
|
||||
bool readOnly;
|
||||
};
|
||||
|
||||
APE::Item::Item()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Item::Item() :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const String &value)
|
||||
APE::Item::Item(const String &key, const String &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->key = key;
|
||||
d->text.append(value);
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const StringList &values)
|
||||
APE::Item::Item(const String &key, const StringList &values) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->key = key;
|
||||
d->text = values;
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const ByteVector &value, bool binary)
|
||||
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->key = key;
|
||||
if(binary) {
|
||||
d->type = Binary;
|
||||
d->value = value;
|
||||
}
|
||||
else
|
||||
else {
|
||||
d->text.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
APE::Item::Item(const Item &item)
|
||||
APE::Item::Item(const Item &item) :
|
||||
d(new ItemPrivate(*item.d))
|
||||
{
|
||||
d = new ItemPrivate(*item.d);
|
||||
}
|
||||
|
||||
APE::Item::~Item()
|
||||
@@ -86,13 +93,17 @@ APE::Item::~Item()
|
||||
|
||||
Item &APE::Item::operator=(const Item &item)
|
||||
{
|
||||
if(&item != this) {
|
||||
delete d;
|
||||
d = new ItemPrivate(*item.d);
|
||||
}
|
||||
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;
|
||||
@@ -173,11 +184,10 @@ void APE::Item::appendValues(const StringList &values)
|
||||
|
||||
int APE::Item::size() const
|
||||
{
|
||||
// SFB: Why is d->key.size() used when size() returns the length in UniChars and not UTF-8?
|
||||
int result = 8 + d->key.size() /* d->key.data(String::UTF8).size() */ + 1;
|
||||
switch (d->type) {
|
||||
int result = 8 + d->key.size() + 1;
|
||||
switch(d->type) {
|
||||
case Text:
|
||||
if(d->text.size()) {
|
||||
if(!d->text.isEmpty()) {
|
||||
StringList::ConstIterator it = d->text.begin();
|
||||
|
||||
result += it->data(String::UTF8).size();
|
||||
@@ -210,7 +220,7 @@ String APE::Item::toString() const
|
||||
if(d->type == Text && !isEmpty())
|
||||
return d->text.front();
|
||||
else
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
bool APE::Item::isEmpty() const
|
||||
@@ -239,17 +249,20 @@ void APE::Item::parse(const ByteVector &data)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint valueLength = data.toUInt(0, false);
|
||||
const uint flags = data.toUInt(4, false);
|
||||
const unsigned int valueLength = data.toUInt(0, false);
|
||||
const unsigned int flags = data.toUInt(4, false);
|
||||
|
||||
d->key = String(data.mid(8), String::UTF8);
|
||||
// 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)
|
||||
if(Text == d->type)
|
||||
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
||||
else
|
||||
d->value = value;
|
||||
@@ -258,7 +271,7 @@ void APE::Item::parse(const ByteVector &data)
|
||||
ByteVector APE::Item::render() const
|
||||
{
|
||||
ByteVector data;
|
||||
TagLib::uint flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
|
||||
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
|
||||
ByteVector value;
|
||||
|
||||
if(isEmpty())
|
||||
@@ -280,7 +293,7 @@ ByteVector APE::Item::render() const
|
||||
|
||||
data.append(ByteVector::fromUInt(value.size(), false));
|
||||
data.append(ByteVector::fromUInt(flags, false));
|
||||
data.append(d->key.data(String::UTF8));
|
||||
data.append(d->key.data(String::Latin1));
|
||||
data.append(ByteVector('\0'));
|
||||
data.append(value);
|
||||
|
||||
|
||||
@@ -90,6 +90,11 @@ namespace TagLib {
|
||||
*/
|
||||
Item &operator=(const Item &item);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of this item by the content of \a item.
|
||||
*/
|
||||
void swap(Item &item);
|
||||
|
||||
/*!
|
||||
* Returns the key.
|
||||
*/
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
int channels;
|
||||
int version;
|
||||
int bitsPerSample;
|
||||
uint sampleFrames;
|
||||
unsigned int sampleFrames;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -122,7 +122,7 @@ int APE::Properties::bitsPerSample() const
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Properties::sampleFrames() const
|
||||
unsigned int APE::Properties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ TagLib::uint APE::Properties::sampleFrames() const
|
||||
|
||||
namespace
|
||||
{
|
||||
inline int headerVersion(const ByteVector &header)
|
||||
int headerVersion(const ByteVector &header)
|
||||
{
|
||||
if(header.size() < 6 || !header.startsWith("MAC "))
|
||||
return -1;
|
||||
@@ -184,7 +184,7 @@ void APE::Properties::analyzeCurrent(File *file)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint descriptorBytes = descriptor.toUInt(0, false);
|
||||
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
|
||||
|
||||
if((descriptorBytes - 52) > 0)
|
||||
file->seek(descriptorBytes - 52, File::Current);
|
||||
@@ -201,12 +201,12 @@ void APE::Properties::analyzeCurrent(File *file)
|
||||
d->sampleRate = header.toUInt(20, false);
|
||||
d->bitsPerSample = header.toShort(16, false);
|
||||
|
||||
const uint totalFrames = header.toUInt(12, false);
|
||||
const unsigned int totalFrames = header.toUInt(12, false);
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
const uint blocksPerFrame = header.toUInt(4, false);
|
||||
const uint finalFrameBlocks = header.toUInt(8, false);
|
||||
const unsigned int blocksPerFrame = header.toUInt(4, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt(8, false);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
}
|
||||
|
||||
@@ -218,14 +218,14 @@ void APE::Properties::analyzeOld(File *file)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint totalFrames = header.toUInt(18, false);
|
||||
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);
|
||||
uint blocksPerFrame;
|
||||
unsigned int blocksPerFrame;
|
||||
if(d->version >= 3950)
|
||||
blocksPerFrame = 73728 * 4;
|
||||
else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
|
||||
@@ -237,7 +237,7 @@ void APE::Properties::analyzeOld(File *file)
|
||||
d->channels = header.toShort(4, false);
|
||||
d->sampleRate = header.toUInt(6, false);
|
||||
|
||||
const uint finalFrameBlocks = header.toUInt(22, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt(22, false);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
|
||||
// Get the bit depth from the RIFF-fmt chunk.
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
TAGLIB_DEPRECATED Properties(File *file, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
@@ -76,7 +76,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -118,7 +118,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the total number of audio samples in file.
|
||||
*/
|
||||
uint sampleFrames() const;
|
||||
unsigned int sampleFrames() const;
|
||||
|
||||
/*!
|
||||
* Returns APE version.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef __SUNPRO_CC
|
||||
#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
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <tstring.h>
|
||||
#include <tmap.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
#include <tutils.h>
|
||||
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
@@ -43,18 +45,44 @@
|
||||
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(-1) {}
|
||||
footerLocation(0) {}
|
||||
|
||||
TagLib::File *file;
|
||||
File *file;
|
||||
long footerLocation;
|
||||
|
||||
Footer footer;
|
||||
|
||||
ItemListMap itemListMap;
|
||||
};
|
||||
|
||||
@@ -91,46 +119,46 @@ ByteVector APE::Tag::fileIdentifier()
|
||||
String APE::Tag::title() const
|
||||
{
|
||||
if(d->itemListMap["TITLE"].isEmpty())
|
||||
return String::null;
|
||||
return String();
|
||||
return d->itemListMap["TITLE"].values().toString();
|
||||
}
|
||||
|
||||
String APE::Tag::artist() const
|
||||
{
|
||||
if(d->itemListMap["ARTIST"].isEmpty())
|
||||
return String::null;
|
||||
return String();
|
||||
return d->itemListMap["ARTIST"].values().toString();
|
||||
}
|
||||
|
||||
String APE::Tag::album() const
|
||||
{
|
||||
if(d->itemListMap["ALBUM"].isEmpty())
|
||||
return String::null;
|
||||
return String();
|
||||
return d->itemListMap["ALBUM"].values().toString();
|
||||
}
|
||||
|
||||
String APE::Tag::comment() const
|
||||
{
|
||||
if(d->itemListMap["COMMENT"].isEmpty())
|
||||
return String::null;
|
||||
return String();
|
||||
return d->itemListMap["COMMENT"].values().toString();
|
||||
}
|
||||
|
||||
String APE::Tag::genre() const
|
||||
{
|
||||
if(d->itemListMap["GENRE"].isEmpty())
|
||||
return String::null;
|
||||
return String();
|
||||
return d->itemListMap["GENRE"].values().toString();
|
||||
}
|
||||
|
||||
TagLib::uint APE::Tag::year() const
|
||||
unsigned int APE::Tag::year() const
|
||||
{
|
||||
if(d->itemListMap["YEAR"].isEmpty())
|
||||
return 0;
|
||||
return d->itemListMap["YEAR"].toString().toInt();
|
||||
}
|
||||
|
||||
TagLib::uint APE::Tag::track() const
|
||||
unsigned int APE::Tag::track() const
|
||||
{
|
||||
if(d->itemListMap["TRACK"].isEmpty())
|
||||
return 0;
|
||||
@@ -162,30 +190,36 @@ void APE::Tag::setGenre(const String &s)
|
||||
addValue("GENRE", s, true);
|
||||
}
|
||||
|
||||
void APE::Tag::setYear(uint i)
|
||||
void APE::Tag::setYear(unsigned int i)
|
||||
{
|
||||
if(i <= 0)
|
||||
if(i == 0)
|
||||
removeItem("YEAR");
|
||||
else
|
||||
addValue("YEAR", String::number(i), true);
|
||||
}
|
||||
|
||||
void APE::Tag::setTrack(uint i)
|
||||
void APE::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
if(i <= 0)
|
||||
if(i == 0)
|
||||
removeItem("TRACK");
|
||||
else
|
||||
addValue("TRACK", String::number(i), true);
|
||||
}
|
||||
|
||||
// conversions of tag keys between what we use in PropertyMap and what's usual
|
||||
// for APE tags
|
||||
static const TagLib::uint keyConversionsSize = 5; //usual, APE
|
||||
static const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
|
||||
{"DATE", "YEAR" },
|
||||
{"ALBUMARTIST", "ALBUM ARTIST"},
|
||||
{"DISCNUMBER", "DISC" },
|
||||
{"REMIXER", "MIXARTIST" }};
|
||||
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" },
|
||||
{"RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS" },
|
||||
{"RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE" }};
|
||||
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
|
||||
}
|
||||
|
||||
PropertyMap APE::Tag::properties() const
|
||||
{
|
||||
@@ -195,14 +229,16 @@ PropertyMap APE::Tag::properties() const
|
||||
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.isNull())
|
||||
if(it->second.type() != Item::Text || tagName.isEmpty()) {
|
||||
properties.unsupportedData().append(it->first);
|
||||
}
|
||||
else {
|
||||
// Some tags need to be handled specially
|
||||
for(uint i = 0; i < keyConversionsSize; ++i)
|
||||
for(size_t i = 0; i < keyConversionsSize; ++i) {
|
||||
if(tagName == keyConversions[i][1])
|
||||
tagName = keyConversions[i][0];
|
||||
properties[tagName].append(it->second.toStringList());
|
||||
}
|
||||
properties[tagName].append(it->second.toStringList());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
@@ -220,7 +256,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
PropertyMap properties(origProps); // make a local copy that can be modified
|
||||
|
||||
// see comment in properties()
|
||||
for(uint i = 0; i < keyConversionsSize; ++i)
|
||||
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]);
|
||||
@@ -232,7 +268,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
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.isNull() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
|
||||
if(!key.isEmpty() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
|
||||
toRemove.append(remIt->first);
|
||||
}
|
||||
|
||||
@@ -247,7 +283,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
if(!checkKey(tagName))
|
||||
invalid.insert(it->first, it->second);
|
||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if(it->second.size() == 0)
|
||||
if(it->second.isEmpty())
|
||||
removeItem(tagName);
|
||||
else {
|
||||
StringList::ConstIterator valueIt = it->second.begin();
|
||||
@@ -263,16 +299,10 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
|
||||
bool APE::Tag::checkKey(const String &key)
|
||||
{
|
||||
if(key.size() < 2 || key.size() > 16)
|
||||
return false;
|
||||
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
|
||||
// only allow printable ASCII including space (32..127)
|
||||
if (*it < 32 || *it >= 128)
|
||||
return false;
|
||||
String upperKey = key.upper();
|
||||
if (upperKey=="ID3" || upperKey=="TAG" || upperKey=="OGGS" || upperKey=="MP+")
|
||||
return false;
|
||||
return true;
|
||||
if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
|
||||
return false;
|
||||
|
||||
return isKeyValid(key.data(String::UTF8));
|
||||
}
|
||||
|
||||
APE::Footer *APE::Tag::footer() const
|
||||
@@ -294,31 +324,39 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace)
|
||||
{
|
||||
if(replace)
|
||||
removeItem(key);
|
||||
if(!key.isEmpty() && !value.isEmpty()) {
|
||||
if(!replace && d->itemListMap.contains(key)) {
|
||||
// Text items may contain more than one value
|
||||
if(APE::Item::Text == d->itemListMap.begin()->second.type())
|
||||
d->itemListMap[key.upper()].appendValue(value);
|
||||
// Binary or locator items may have only one value
|
||||
else
|
||||
setItem(key, Item(key, value));
|
||||
}
|
||||
else
|
||||
setItem(key, Item(key, value));
|
||||
}
|
||||
|
||||
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(!key.isEmpty() && !value.isEmpty())
|
||||
setItem(key, Item(key, value, true));
|
||||
|
||||
if(value.isEmpty())
|
||||
return;
|
||||
|
||||
setItem(key, Item(key, value, true));
|
||||
}
|
||||
|
||||
void APE::Tag::setItem(const String &key, const Item &item)
|
||||
{
|
||||
if(!key.isEmpty())
|
||||
d->itemListMap.insert(key.upper(), 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
|
||||
@@ -338,7 +376,7 @@ void APE::Tag::read()
|
||||
d->footer.setData(d->file->readBlock(Footer::size()));
|
||||
|
||||
if(d->footer.tagSize() <= Footer::size() ||
|
||||
d->footer.tagSize() > uint(d->file->length()))
|
||||
d->footer.tagSize() > static_cast<unsigned long>(d->file->length()))
|
||||
return;
|
||||
|
||||
d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
|
||||
@@ -349,15 +387,11 @@ void APE::Tag::read()
|
||||
ByteVector APE::Tag::render() const
|
||||
{
|
||||
ByteVector data;
|
||||
uint itemCount = 0;
|
||||
unsigned int itemCount = 0;
|
||||
|
||||
{
|
||||
for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
|
||||
it != d->itemListMap.end(); ++it)
|
||||
{
|
||||
data.append(it->second.render());
|
||||
itemCount++;
|
||||
}
|
||||
for(ItemListMap::ConstIterator it = d->itemListMap.begin(); it != d->itemListMap.end(); ++it) {
|
||||
data.append(it->second.render());
|
||||
itemCount++;
|
||||
}
|
||||
|
||||
d->footer.setItemCount(itemCount);
|
||||
@@ -374,14 +408,32 @@ void APE::Tag::parse(const ByteVector &data)
|
||||
if(data.size() < 11)
|
||||
return;
|
||||
|
||||
uint pos = 0;
|
||||
unsigned int pos = 0;
|
||||
|
||||
for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
||||
APE::Item item;
|
||||
item.parse(data.mid(pos));
|
||||
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
||||
|
||||
d->itemListMap.insert(item.key().upper(), item);
|
||||
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;
|
||||
}
|
||||
|
||||
pos += item.size();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,16 +92,16 @@ namespace TagLib {
|
||||
virtual String album() const;
|
||||
virtual String comment() const;
|
||||
virtual String genre() const;
|
||||
virtual uint year() const;
|
||||
virtual uint track() 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(uint i);
|
||||
virtual void setTrack(uint i);
|
||||
virtual void setYear(unsigned int i);
|
||||
virtual void setTrack(unsigned int i);
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- export function.
|
||||
|
||||
@@ -36,20 +36,16 @@ using namespace TagLib;
|
||||
class ASF::Attribute::AttributePrivate : public RefCounter
|
||||
{
|
||||
public:
|
||||
AttributePrivate()
|
||||
: pictureValue(ASF::Picture::fromInvalid()),
|
||||
stream(0),
|
||||
language(0) {}
|
||||
AttributePrivate() :
|
||||
pictureValue(ASF::Picture::fromInvalid()),
|
||||
numericValue(0),
|
||||
stream(0),
|
||||
language(0) {}
|
||||
AttributeTypes type;
|
||||
String stringValue;
|
||||
ByteVector byteVectorValue;
|
||||
ASF::Picture pictureValue;
|
||||
union {
|
||||
unsigned int intValue;
|
||||
unsigned short shortValue;
|
||||
unsigned long long longLongValue;
|
||||
bool boolValue;
|
||||
};
|
||||
unsigned long long numericValue;
|
||||
int stream;
|
||||
int language;
|
||||
};
|
||||
@@ -58,84 +54,86 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Attribute::Attribute()
|
||||
ASF::Attribute::Attribute() :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = UnicodeType;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Attribute &other)
|
||||
: d(other.d)
|
||||
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)
|
||||
{
|
||||
if(&other != this) {
|
||||
if(d->deref())
|
||||
delete d;
|
||||
d = other.d;
|
||||
d->ref();
|
||||
}
|
||||
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::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->intValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned long long value)
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = QWordType;
|
||||
d->longLongValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned short value)
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = WordType;
|
||||
d->shortValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(bool value)
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = BoolType;
|
||||
d->boolValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
|
||||
{
|
||||
return d->type;
|
||||
@@ -155,22 +153,22 @@ ByteVector ASF::Attribute::toByteVector() const
|
||||
|
||||
unsigned short ASF::Attribute::toBool() const
|
||||
{
|
||||
return d->shortValue;
|
||||
return d->numericValue ? 1 : 0;
|
||||
}
|
||||
|
||||
unsigned short ASF::Attribute::toUShort() const
|
||||
{
|
||||
return d->shortValue;
|
||||
return static_cast<unsigned short>(d->numericValue);
|
||||
}
|
||||
|
||||
unsigned int ASF::Attribute::toUInt() const
|
||||
{
|
||||
return d->intValue;
|
||||
return static_cast<unsigned int>(d->numericValue);
|
||||
}
|
||||
|
||||
unsigned long long ASF::Attribute::toULongLong() const
|
||||
{
|
||||
return d->longLongValue;
|
||||
return static_cast<unsigned long long>(d->numericValue);
|
||||
}
|
||||
|
||||
ASF::Picture ASF::Attribute::toPicture() const
|
||||
@@ -180,7 +178,7 @@ ASF::Picture ASF::Attribute::toPicture() const
|
||||
|
||||
String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
{
|
||||
uint size, nameLength;
|
||||
unsigned int size, nameLength;
|
||||
String name;
|
||||
d->pictureValue = Picture::fromInvalid();
|
||||
// extended content descriptor
|
||||
@@ -210,24 +208,24 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
|
||||
switch(d->type) {
|
||||
case WordType:
|
||||
d->shortValue = readWORD(&f);
|
||||
d->numericValue = readWORD(&f);
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
d->boolValue = (readDWORD(&f) == 1);
|
||||
d->numericValue = (readDWORD(&f) != 0);
|
||||
}
|
||||
else {
|
||||
d->boolValue = (readWORD(&f) == 1);
|
||||
d->numericValue = (readWORD(&f) != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
d->intValue = readDWORD(&f);
|
||||
d->numericValue = readDWORD(&f);
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
d->longLongValue = readQWORD(&f);
|
||||
d->numericValue = readQWORD(&f);
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
@@ -278,24 +276,24 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
|
||||
switch (d->type) {
|
||||
case WordType:
|
||||
data.append(ByteVector::fromShort(d->shortValue, false));
|
||||
data.append(ByteVector::fromShort(toUShort(), false));
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false));
|
||||
data.append(ByteVector::fromUInt(toBool(), false));
|
||||
}
|
||||
else {
|
||||
data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false));
|
||||
data.append(ByteVector::fromShort(toBool(), false));
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
data.append(ByteVector::fromUInt(d->intValue, false));
|
||||
data.append(ByteVector::fromUInt(toUInt(), false));
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
data.append(ByteVector::fromLongLong(d->longLongValue, false));
|
||||
data.append(ByteVector::fromLongLong(toULongLong(), false));
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
@@ -351,4 +349,3 @@ void ASF::Attribute::setStream(int value)
|
||||
{
|
||||
d->stream = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,12 @@ namespace TagLib
|
||||
/*!
|
||||
* Copies the contents of \a other into this item.
|
||||
*/
|
||||
ASF::Attribute &operator=(const Attribute &other);
|
||||
Attribute &operator=(const Attribute &other);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the Attribute by the content of \a other.
|
||||
*/
|
||||
void swap(Attribute &other);
|
||||
|
||||
/*!
|
||||
* Destroys the attribute.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asftag.h"
|
||||
@@ -50,7 +51,7 @@ public:
|
||||
class MetadataLibraryObject;
|
||||
|
||||
FilePrivate():
|
||||
size(0),
|
||||
headerSize(0),
|
||||
tag(0),
|
||||
properties(0),
|
||||
contentDescriptionObject(0),
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
delete properties;
|
||||
}
|
||||
|
||||
unsigned long long size;
|
||||
unsigned long long headerSize;
|
||||
|
||||
ASF::Tag *tag;
|
||||
ASF::Properties *properties;
|
||||
@@ -120,21 +121,21 @@ class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePriva
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
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, uint size);
|
||||
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, uint size);
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
@@ -143,7 +144,7 @@ class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::Fil
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
@@ -152,7 +153,7 @@ class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::Ba
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
@@ -161,7 +162,7 @@ class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePriv
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
@@ -171,7 +172,7 @@ public:
|
||||
List<ASF::File::FilePrivate::BaseObject *> objects;
|
||||
HeaderExtensionObject();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
@@ -179,7 +180,7 @@ class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::B
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
|
||||
private:
|
||||
enum CodecType
|
||||
@@ -196,7 +197,7 @@ void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int siz
|
||||
if(size > 24 && size <= (unsigned int)(file->length()))
|
||||
data = file->readBlock(size - 24);
|
||||
else
|
||||
data = ByteVector::null;
|
||||
data = ByteVector();
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
|
||||
@@ -218,7 +219,7 @@ ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
|
||||
return filePropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() < 64) {
|
||||
@@ -236,7 +237,7 @@ ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
|
||||
return streamPropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() < 70) {
|
||||
@@ -256,9 +257,8 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
return contentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->contentDescriptionObject = this;
|
||||
const int titleLength = readWORD(file);
|
||||
const int artistLength = readWORD(file);
|
||||
const int copyrightLength = readWORD(file);
|
||||
@@ -297,9 +297,8 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
|
||||
return extendedContentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->extendedContentDescriptionObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -312,7 +311,7 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF:
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -321,9 +320,8 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
|
||||
return metadataGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->metadataObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -336,7 +334,7 @@ ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -345,9 +343,8 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
|
||||
return metadataLibraryGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->metadataLibraryObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -360,7 +357,7 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -374,9 +371,8 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
return headerExtensionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->headerExtensionObject = this;
|
||||
file->seek(18, File::Current);
|
||||
long long dataSize = readDWORD(file);
|
||||
long long dataPos = 0;
|
||||
@@ -394,10 +390,12 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, uint
|
||||
}
|
||||
BaseObject *obj;
|
||||
if(guid == metadataGuid) {
|
||||
obj = new MetadataObject();
|
||||
file->d->metadataObject = new MetadataObject();
|
||||
obj = file->d->metadataObject;
|
||||
}
|
||||
else if(guid == metadataLibraryGuid) {
|
||||
obj = new MetadataLibraryObject();
|
||||
file->d->metadataLibraryObject = new MetadataLibraryObject();
|
||||
obj = file->d->metadataLibraryObject;
|
||||
}
|
||||
else {
|
||||
obj = new UnknownObject(guid);
|
||||
@@ -423,7 +421,7 @@ ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
|
||||
return codecListGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() <= 20) {
|
||||
@@ -431,7 +429,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
return;
|
||||
}
|
||||
|
||||
uint pos = 16;
|
||||
unsigned int pos = 16;
|
||||
|
||||
const int count = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
@@ -447,13 +445,13 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
int nameLength = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
const uint namePos = pos;
|
||||
const unsigned int namePos = pos;
|
||||
pos += nameLength * 2;
|
||||
|
||||
const int descLength = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
const uint descPos = pos;
|
||||
const unsigned int descPos = pos;
|
||||
pos += descLength * 2;
|
||||
|
||||
const int infoLength = data.toUShort(pos, false);
|
||||
@@ -473,6 +471,18 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -556,6 +566,10 @@ bool ASF::File::save()
|
||||
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) {
|
||||
@@ -591,8 +605,14 @@ bool ASF::File::save()
|
||||
data.append((*it)->render(this));
|
||||
}
|
||||
|
||||
data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
|
||||
insert(data, 0, (TagLib::ulong)d->size);
|
||||
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;
|
||||
}
|
||||
@@ -606,9 +626,8 @@ void ASF::File::read()
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
ByteVector guid = readBlock(16);
|
||||
if(guid != headerGuid) {
|
||||
debug("ASF: Not an ASF file.");
|
||||
if(readBlock(16) != headerGuid) {
|
||||
debug("ASF::File::read(): Not an ASF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
@@ -617,7 +636,7 @@ void ASF::File::read()
|
||||
d->properties = new ASF::Properties();
|
||||
|
||||
bool ok;
|
||||
d->size = readQWORD(this, &ok);
|
||||
d->headerSize = readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
@@ -629,8 +648,10 @@ void ASF::File::read()
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
FilePrivate::FilePropertiesObject *filePropertiesObject = 0;
|
||||
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
guid = readBlock(16);
|
||||
const ByteVector guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
@@ -642,19 +663,24 @@ void ASF::File::read()
|
||||
}
|
||||
FilePrivate::BaseObject *obj;
|
||||
if(guid == filePropertiesGuid) {
|
||||
obj = new FilePrivate::FilePropertiesObject();
|
||||
filePropertiesObject = new FilePrivate::FilePropertiesObject();
|
||||
obj = filePropertiesObject;
|
||||
}
|
||||
else if(guid == streamPropertiesGuid) {
|
||||
obj = new FilePrivate::StreamPropertiesObject();
|
||||
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
|
||||
obj = streamPropertiesObject;
|
||||
}
|
||||
else if(guid == contentDescriptionGuid) {
|
||||
obj = new FilePrivate::ContentDescriptionObject();
|
||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
||||
obj = d->contentDescriptionObject;
|
||||
}
|
||||
else if(guid == extendedContentDescriptionGuid) {
|
||||
obj = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
obj = d->extendedContentDescriptionObject;
|
||||
}
|
||||
else if(guid == headerExtensionGuid) {
|
||||
obj = new FilePrivate::HeaderExtensionObject();
|
||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
||||
obj = d->headerExtensionObject;
|
||||
}
|
||||
else if(guid == codecListGuid) {
|
||||
obj = new FilePrivate::CodecListObject();
|
||||
@@ -670,4 +696,10 @@ void ASF::File::read()
|
||||
obj->parse(this, size);
|
||||
d->objects.append(obj);
|
||||
}
|
||||
|
||||
if(!filePropertiesObject || !streamPropertiesObject) {
|
||||
debug("ASF::File::read(): Missing mandatory header objects.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,12 +112,18 @@ namespace TagLib {
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*
|
||||
* \warning In the current implementation, it's dangerous to call save()
|
||||
* repeatedly. At worst it will corrupt the file.
|
||||
*/
|
||||
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();
|
||||
|
||||
|
||||
@@ -48,14 +48,14 @@ public:
|
||||
// Picture class members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Picture::Picture()
|
||||
ASF::Picture::Picture() :
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
d = new PicturePrivate();
|
||||
d->valid = true;
|
||||
}
|
||||
|
||||
ASF::Picture::Picture(const Picture& other)
|
||||
: d(other.d)
|
||||
ASF::Picture::Picture(const Picture& other) :
|
||||
d(other.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
@@ -120,19 +120,22 @@ int ASF::Picture::dataSize() const
|
||||
|
||||
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
|
||||
{
|
||||
if(other.d != d) {
|
||||
if(d->deref())
|
||||
delete d;
|
||||
d = other.d;
|
||||
d->ref();
|
||||
}
|
||||
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::null;
|
||||
return ByteVector();
|
||||
|
||||
return
|
||||
ByteVector((char)d->type) +
|
||||
ByteVector::fromUInt(d->picture.size(), false) +
|
||||
@@ -148,7 +151,7 @@ void ASF::Picture::parse(const ByteVector& bytes)
|
||||
return;
|
||||
int pos = 0;
|
||||
d->type = (Type)bytes[0]; ++pos;
|
||||
const uint dataLen = bytes.toUInt(pos, false); pos+=4;
|
||||
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
|
||||
|
||||
const ByteVector nullStringTerminator(2, 0);
|
||||
|
||||
@@ -178,4 +181,3 @@ ASF::Picture ASF::Picture::fromInvalid()
|
||||
ret.d->valid = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,11 @@ namespace TagLib
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -39,10 +39,10 @@ public:
|
||||
AttributeListMap attributeListMap;
|
||||
};
|
||||
|
||||
ASF::Tag::Tag()
|
||||
: TagLib::Tag()
|
||||
ASF::Tag::Tag() :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
ASF::Tag::~Tag()
|
||||
@@ -64,7 +64,7 @@ String ASF::Tag::album() const
|
||||
{
|
||||
if(d->attributeListMap.contains("WM/AlbumTitle"))
|
||||
return d->attributeListMap["WM/AlbumTitle"][0].toString();
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String ASF::Tag::copyright() const
|
||||
@@ -107,7 +107,7 @@ String ASF::Tag::genre() const
|
||||
{
|
||||
if(d->attributeListMap.contains("WM/Genre"))
|
||||
return d->attributeListMap["WM/Genre"][0].toString();
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
void ASF::Tag::setTitle(const String &value)
|
||||
@@ -145,12 +145,12 @@ void ASF::Tag::setGenre(const String &value)
|
||||
setAttribute("WM/Genre", value);
|
||||
}
|
||||
|
||||
void ASF::Tag::setYear(uint value)
|
||||
void ASF::Tag::setYear(unsigned int value)
|
||||
{
|
||||
setAttribute("WM/Year", String::number(value));
|
||||
}
|
||||
|
||||
void ASF::Tag::setTrack(uint value)
|
||||
void ASF::Tag::setTrack(unsigned int value)
|
||||
{
|
||||
setAttribute("WM/TrackNumber", String::number(value));
|
||||
}
|
||||
@@ -210,58 +210,70 @@ bool ASF::Tag::isEmpty() const
|
||||
d->attributeListMap.isEmpty();
|
||||
}
|
||||
|
||||
static 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" },
|
||||
};
|
||||
namespace
|
||||
{
|
||||
const char *keyTranslation[][2] = {
|
||||
{ "WM/AlbumTitle", "ALBUM" },
|
||||
{ "WM/AlbumArtist", "ALBUMARTIST" },
|
||||
{ "WM/Composer", "COMPOSER" },
|
||||
{ "WM/Writer", "LYRICIST" },
|
||||
{ "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" },
|
||||
{ "WM/ARTISTS", "ARTISTS" },
|
||||
{ "ASIN", "ASIN" },
|
||||
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "MusicBrainz/Album Release Country", "RELEASECOUNTRY" },
|
||||
{ "MusicBrainz/Album Status", "RELEASESTATUS" },
|
||||
{ "MusicBrainz/Album Type", "RELEASETYPE" },
|
||||
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID" },
|
||||
{ "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
|
||||
{
|
||||
static Map<String, String> keyMap;
|
||||
if(keyMap.isEmpty()) {
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap props;
|
||||
|
||||
if(!d->title.isEmpty()) {
|
||||
@@ -279,8 +291,8 @@ PropertyMap ASF::Tag::properties() const
|
||||
|
||||
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
|
||||
for(; it != d->attributeListMap.end(); ++it) {
|
||||
if(keyMap.contains(it->first)) {
|
||||
String key = keyMap[it->first];
|
||||
const String key = translateKey(it->first);
|
||||
if(!key.isEmpty()) {
|
||||
AttributeList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
if(key == "TRACKNUMBER") {
|
||||
@@ -323,16 +335,16 @@ PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
||||
for(; it != origProps.end(); ++it) {
|
||||
if(!props.contains(it->first) || props[it->first].isEmpty()) {
|
||||
if(it->first == "TITLE") {
|
||||
d->title = String::null;
|
||||
d->title.clear();
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = String::null;
|
||||
d->artist.clear();
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = String::null;
|
||||
d->comment.clear();
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = String::null;
|
||||
d->copyright.clear();
|
||||
}
|
||||
else {
|
||||
d->attributeListMap.erase(reverseKeyMap[it->first]);
|
||||
|
||||
@@ -90,13 +90,13 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the year; if there is no year set, this will return 0.
|
||||
*/
|
||||
virtual uint year() const;
|
||||
virtual unsigned int year() const;
|
||||
|
||||
/*!
|
||||
* Returns the track number; if there is no track number set, this will
|
||||
* return 0.
|
||||
*/
|
||||
virtual uint track() const;
|
||||
virtual unsigned int track() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a s.
|
||||
@@ -137,12 +137,12 @@ namespace TagLib {
|
||||
/*!
|
||||
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setYear(uint i);
|
||||
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(uint i);
|
||||
virtual void setTrack(unsigned int i);
|
||||
|
||||
/*!
|
||||
* Returns true if the tag does not contain any data. This should be
|
||||
@@ -160,6 +160,7 @@ namespace TagLib {
|
||||
* Returns a reference to the item list map. This is an AttributeListMap of
|
||||
* all of the items in the tag.
|
||||
*/
|
||||
// BIC: return by value
|
||||
const AttributeListMap &attributeListMap() const;
|
||||
|
||||
/*!
|
||||
|
||||
@@ -34,65 +34,68 @@ namespace TagLib
|
||||
{
|
||||
namespace ASF
|
||||
{
|
||||
|
||||
inline ushort readWORD(File *file, bool *ok = 0)
|
||||
namespace
|
||||
{
|
||||
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 uint 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;
|
||||
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;
|
||||
}
|
||||
size -= 2;
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
}
|
||||
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;
|
||||
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);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,39 @@
|
||||
|
||||
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 \
|
||||
return (default_value);
|
||||
|
||||
class AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
|
||||
@@ -57,100 +90,14 @@ AudioProperties::~AudioProperties()
|
||||
|
||||
}
|
||||
|
||||
int TagLib::AudioProperties::lengthInSeconds() const
|
||||
int AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
// This is an ugly workaround but we can't add a virtual function.
|
||||
// Should be virtual in taglib2.
|
||||
|
||||
if(dynamic_cast<const APE::Properties*>(this))
|
||||
return dynamic_cast<const APE::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const ASF::Properties*>(this))
|
||||
return dynamic_cast<const ASF::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const FLAC::Properties*>(this))
|
||||
return dynamic_cast<const FLAC::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MP4::Properties*>(this))
|
||||
return dynamic_cast<const MP4::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MPC::Properties*>(this))
|
||||
return dynamic_cast<const MPC::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MPEG::Properties*>(this))
|
||||
return dynamic_cast<const MPEG::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const TrueAudio::Properties*>(this))
|
||||
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if (dynamic_cast<const RIFF::AIFF::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Vorbis::Properties*>(this))
|
||||
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const WavPack::Properties*>(this))
|
||||
return dynamic_cast<const WavPack::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else
|
||||
return 0;
|
||||
VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0)
|
||||
}
|
||||
|
||||
int TagLib::AudioProperties::lengthInMilliseconds() const
|
||||
int AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
// This is an ugly workaround but we can't add a virtual function.
|
||||
// Should be virtual in taglib2.
|
||||
|
||||
if(dynamic_cast<const APE::Properties*>(this))
|
||||
return dynamic_cast<const APE::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const ASF::Properties*>(this))
|
||||
return dynamic_cast<const ASF::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const FLAC::Properties*>(this))
|
||||
return dynamic_cast<const FLAC::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const MP4::Properties*>(this))
|
||||
return dynamic_cast<const MP4::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const MPC::Properties*>(this))
|
||||
return dynamic_cast<const MPC::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const MPEG::Properties*>(this))
|
||||
return dynamic_cast<const MPEG::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const TrueAudio::Properties*>(this))
|
||||
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const RIFF::AIFF::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const Vorbis::Properties*>(this))
|
||||
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const WavPack::Properties*>(this))
|
||||
return dynamic_cast<const WavPack::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else
|
||||
return 0;
|
||||
VIRTUAL_FUNCTION_WORKAROUND(lengthInMilliseconds, 0)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -27,10 +27,13 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tfilestream.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include <trefcounter.h>
|
||||
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
@@ -54,41 +57,260 @@
|
||||
|
||||
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)
|
||||
{
|
||||
if(::strlen(fileName) == 0)
|
||||
return 0;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class FileRef::FileRefPrivate : public RefCounter
|
||||
{
|
||||
public:
|
||||
FileRefPrivate(File *f) : RefCounter(), file(f) {}
|
||||
FileRefPrivate() :
|
||||
RefCounter(),
|
||||
file(0),
|
||||
stream(0) {}
|
||||
|
||||
~FileRefPrivate() {
|
||||
delete file;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
File *file;
|
||||
static List<const FileTypeResolver *> fileTypeResolvers;
|
||||
File *file;
|
||||
IOStream *stream;
|
||||
};
|
||||
|
||||
List<const FileRef::FileTypeResolver *> FileRef::FileRefPrivate::fileTypeResolvers;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FileRef::FileRef()
|
||||
FileRef::FileRef() :
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
d = new FileRefPrivate(0);
|
||||
}
|
||||
|
||||
FileRef::FileRef(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
d = new FileRefPrivate(create(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
parse(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(File *file)
|
||||
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
d = new FileRefPrivate(file);
|
||||
parse(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(const FileRef &ref) : d(ref.d)
|
||||
FileRef::FileRef(File *file) :
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
d->file = file;
|
||||
}
|
||||
|
||||
FileRef::FileRef(const FileRef &ref) :
|
||||
d(ref.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
@@ -133,7 +355,7 @@ bool FileRef::save()
|
||||
|
||||
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
|
||||
{
|
||||
FileRefPrivate::fileTypeResolvers.prepend(resolver);
|
||||
fileTypeResolvers.prepend(resolver);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@@ -144,6 +366,7 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("ogg");
|
||||
l.append("flac");
|
||||
l.append("oga");
|
||||
l.append("opus");
|
||||
l.append("mp3");
|
||||
l.append("mpc");
|
||||
l.append("wv");
|
||||
@@ -155,10 +378,13 @@ StringList FileRef::defaultFileExtensions()
|
||||
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("afc");
|
||||
l.append("aifc");
|
||||
l.append("wav");
|
||||
l.append("ape");
|
||||
l.append("mod");
|
||||
@@ -174,113 +400,86 @@ StringList FileRef::defaultFileExtensions()
|
||||
|
||||
bool FileRef::isNull() const
|
||||
{
|
||||
return !d->file || !d->file->isValid();
|
||||
return (!d->file || !d->file->isValid());
|
||||
}
|
||||
|
||||
FileRef &FileRef::operator=(const FileRef &ref)
|
||||
{
|
||||
if(&ref == this)
|
||||
return *this;
|
||||
|
||||
if(d->deref())
|
||||
delete d;
|
||||
|
||||
d = ref.d;
|
||||
d->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;
|
||||
return (ref.d->file == d->file);
|
||||
}
|
||||
|
||||
bool FileRef::operator!=(const FileRef &ref) const
|
||||
{
|
||||
return ref.d->file != d->file;
|
||||
return (ref.d->file != d->file);
|
||||
}
|
||||
|
||||
File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) // static
|
||||
{
|
||||
|
||||
List<const FileTypeResolver *>::ConstIterator it = FileRefPrivate::fileTypeResolvers.begin();
|
||||
|
||||
for(; it != FileRefPrivate::fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
|
||||
// Ok, this is really dumb for now, but it works for testing.
|
||||
|
||||
String ext;
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
String s = fileName.toString();
|
||||
|
||||
#else
|
||||
|
||||
String s = fileName;
|
||||
|
||||
#endif
|
||||
|
||||
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()) {
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(fileName, 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, 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")
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
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)
|
||||
{
|
||||
// Try user-defined resolvers.
|
||||
|
||||
d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -127,6 +127,23 @@ namespace TagLib {
|
||||
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.
|
||||
@@ -226,6 +243,11 @@ namespace TagLib {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@@ -252,8 +274,10 @@ namespace TagLib {
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <tdebug.h>
|
||||
#include <tagunion.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include <id3v2header.h>
|
||||
#include <id3v2tag.h>
|
||||
@@ -44,41 +45,42 @@ using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
typedef List<FLAC::MetadataBlock *> BlockList;
|
||||
typedef BlockList::Iterator BlockIterator;
|
||||
typedef BlockList::Iterator BlockConstIterator;
|
||||
|
||||
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
|
||||
enum { MinPaddingLength = 4096 };
|
||||
enum { LastBlockFlag = 0x80 };
|
||||
|
||||
const long MinPaddingLength = 4096;
|
||||
const long MaxPaddingLegnth = 1024 * 1024;
|
||||
|
||||
const char LastBlockFlag = '\x80';
|
||||
}
|
||||
|
||||
class FLAC::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
|
||||
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
||||
ID3v2FrameFactory(frameFactory),
|
||||
ID3v2Location(-1),
|
||||
ID3v2OriginalSize(0),
|
||||
ID3v1Location(-1),
|
||||
properties(0),
|
||||
flacStart(0),
|
||||
streamStart(0),
|
||||
scanned(false),
|
||||
hasXiphComment(false),
|
||||
hasID3v2(false),
|
||||
hasID3v1(false)
|
||||
scanned(false)
|
||||
{
|
||||
blocks.setAutoDelete(true);
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
uint size = blocks.size();
|
||||
for(uint i = 0; i < size; i++) {
|
||||
delete blocks[i];
|
||||
}
|
||||
delete properties;
|
||||
}
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
long ID3v2Location;
|
||||
uint ID3v2OriginalSize;
|
||||
long ID3v2OriginalSize;
|
||||
|
||||
long ID3v1Location;
|
||||
|
||||
@@ -86,17 +88,25 @@ public:
|
||||
|
||||
Properties *properties;
|
||||
ByteVector xiphCommentData;
|
||||
List<MetadataBlock *> blocks;
|
||||
BlockList blocks;
|
||||
|
||||
long flacStart;
|
||||
long streamStart;
|
||||
bool scanned;
|
||||
|
||||
bool hasXiphComment;
|
||||
bool hasID3v2;
|
||||
bool hasID3v1;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -112,9 +122,8 @@ FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(new FilePrivate(frameFactory))
|
||||
{
|
||||
d->ID3v2FrameFactory = frameFactory;
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
@@ -122,9 +131,8 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
d(new FilePrivate(frameFactory))
|
||||
{
|
||||
d->ID3v2FrameFactory = frameFactory;
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
@@ -141,30 +149,17 @@ TagLib::Tag *FLAC::File::tag() const
|
||||
|
||||
PropertyMap FLAC::File::properties() const
|
||||
{
|
||||
// once Tag::properties() is virtual, this case distinction could actually be done
|
||||
// within TagUnion.
|
||||
if(d->hasXiphComment)
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->properties();
|
||||
if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->properties();
|
||||
if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->properties();
|
||||
return PropertyMap();
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
if(d->hasXiphComment)
|
||||
d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->removeUnsupportedProperties(unsupported);
|
||||
if(d->hasID3v2)
|
||||
d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->removeUnsupportedProperties(unsupported);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->removeUnsupportedProperties(unsupported);
|
||||
d->tag.removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
|
||||
return xiphComment(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
FLAC::Properties *FLAC::File::audioProperties() const
|
||||
@@ -185,80 +180,119 @@ bool FLAC::File::save()
|
||||
}
|
||||
|
||||
// Create new vorbis comments
|
||||
|
||||
Tag::duplicate(&d->tag, xiphComment(true), false);
|
||||
if(!hasXiphComment())
|
||||
Tag::duplicate(&d->tag, xiphComment(true), false);
|
||||
|
||||
d->xiphCommentData = xiphComment()->render(false);
|
||||
|
||||
// Replace metadata blocks
|
||||
|
||||
bool foundVorbisCommentBlock = false;
|
||||
List<MetadataBlock *> newBlocks;
|
||||
for(uint i = 0; i < d->blocks.size(); i++) {
|
||||
MetadataBlock *block = d->blocks[i];
|
||||
if(block->code() == MetadataBlock::VorbisComment) {
|
||||
// Set the new Vorbis Comment block
|
||||
delete block;
|
||||
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
|
||||
foundVorbisCommentBlock = true;
|
||||
}
|
||||
if(block->code() == MetadataBlock::Padding) {
|
||||
delete block;
|
||||
MetadataBlock *commentBlock =
|
||||
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end();) {
|
||||
if((*it)->code() == MetadataBlock::VorbisComment) {
|
||||
// Remove the old Vorbis Comment block
|
||||
delete *it;
|
||||
it = d->blocks.erase(it);
|
||||
continue;
|
||||
}
|
||||
newBlocks.append(block);
|
||||
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
|
||||
// Set the new Vorbis Comment block before the first picture block
|
||||
d->blocks.insert(it, commentBlock);
|
||||
commentBlock = 0;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if(!foundVorbisCommentBlock) {
|
||||
newBlocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
|
||||
foundVorbisCommentBlock = true;
|
||||
}
|
||||
d->blocks = newBlocks;
|
||||
if(commentBlock)
|
||||
d->blocks.append(commentBlock);
|
||||
|
||||
// Render data for the metadata blocks
|
||||
|
||||
ByteVector data;
|
||||
for(uint i = 0; i < newBlocks.size(); i++) {
|
||||
FLAC::MetadataBlock *block = newBlocks[i];
|
||||
ByteVector blockData = block->render();
|
||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
ByteVector blockData = (*it)->render();
|
||||
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
|
||||
blockHeader[0] = block->code();
|
||||
blockHeader[0] = (*it)->code();
|
||||
data.append(blockHeader);
|
||||
data.append(blockData);
|
||||
}
|
||||
|
||||
// Adjust the padding block(s)
|
||||
// Compute the amount of padding, and append that to data.
|
||||
|
||||
long originalLength = d->streamStart - d->flacStart;
|
||||
int paddingLength = originalLength - data.size() - 4;
|
||||
long paddingLength = originalLength - data.size() - 4;
|
||||
|
||||
if(paddingLength <= 0) {
|
||||
paddingLength = MinPaddingLength;
|
||||
}
|
||||
ByteVector padding = ByteVector::fromUInt(paddingLength);
|
||||
padding.resize(paddingLength + 4);
|
||||
padding[0] = (char)(FLAC::MetadataBlock::Padding | LastBlockFlag);
|
||||
data.append(padding);
|
||||
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->hasXiphComment = true;
|
||||
|
||||
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()) {
|
||||
if(d->hasID3v2) {
|
||||
if(d->ID3v2Location < d->flacStart)
|
||||
debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
|
||||
"start of the FLAC bytestream? Not writing the ID3v2 tag.");
|
||||
else
|
||||
insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
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;
|
||||
}
|
||||
else
|
||||
insert(ID3v2Tag()->render(), 0, 0);
|
||||
}
|
||||
|
||||
if(ID3v1Tag()) {
|
||||
if(d->hasID3v1) {
|
||||
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 {
|
||||
@@ -267,7 +301,15 @@ bool FLAC::File::save()
|
||||
}
|
||||
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
else {
|
||||
|
||||
// ID3v1 tag is empty. Remove the old one.
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
truncate(d->ID3v1Location);
|
||||
d->ID3v1Location = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -275,11 +317,7 @@ bool FLAC::File::save()
|
||||
|
||||
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
|
||||
{
|
||||
if(!create || d->tag[FlacID3v2Index])
|
||||
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
|
||||
|
||||
d->tag.set(FlacID3v2Index, new ID3v2::Tag);
|
||||
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create);
|
||||
}
|
||||
|
||||
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
|
||||
@@ -312,8 +350,8 @@ long FLAC::File::streamLength()
|
||||
List<FLAC::Picture *> FLAC::File::pictureList()
|
||||
{
|
||||
List<Picture *> pictures;
|
||||
for(uint i = 0; i < d->blocks.size(); i++) {
|
||||
Picture *picture = dynamic_cast<Picture *>(d->blocks[i]);
|
||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
Picture *picture = dynamic_cast<Picture *>(*it);
|
||||
if(picture) {
|
||||
pictures.append(picture);
|
||||
}
|
||||
@@ -328,8 +366,7 @@ void FLAC::File::addPicture(Picture *picture)
|
||||
|
||||
void FLAC::File::removePicture(Picture *picture, bool del)
|
||||
{
|
||||
MetadataBlock *block = picture;
|
||||
List<MetadataBlock *>::Iterator it = d->blocks.find(block);
|
||||
BlockIterator it = d->blocks.find(picture);
|
||||
if(it != d->blocks.end())
|
||||
d->blocks.erase(it);
|
||||
|
||||
@@ -339,32 +376,44 @@ void FLAC::File::removePicture(Picture *picture, bool del)
|
||||
|
||||
void FLAC::File::removePictures()
|
||||
{
|
||||
List<MetadataBlock *> newBlocks;
|
||||
for(uint i = 0; i < d->blocks.size(); i++) {
|
||||
Picture *picture = dynamic_cast<Picture *>(d->blocks[i]);
|
||||
if(picture) {
|
||||
delete picture;
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
|
||||
if(dynamic_cast<Picture *>(*it)) {
|
||||
delete *it;
|
||||
it = d->blocks.erase(it);
|
||||
}
|
||||
else {
|
||||
newBlocks.append(d->blocks[i]);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
d->blocks = newBlocks;
|
||||
}
|
||||
|
||||
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->hasXiphComment;
|
||||
return !d->xiphCommentData.isEmpty();
|
||||
}
|
||||
|
||||
bool FLAC::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
return (d->ID3v1Location >= 0);
|
||||
}
|
||||
|
||||
bool FLAC::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
return (d->ID3v2Location >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -375,28 +424,19 @@ void FLAC::File::read(bool readProperties)
|
||||
{
|
||||
// Look for an ID3v2 tag
|
||||
|
||||
d->ID3v2Location = findID3v2();
|
||||
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();
|
||||
|
||||
if(ID3v2Tag()->header()->tagSize() <= 0)
|
||||
d->tag.set(FlacID3v2Index, 0);
|
||||
else
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = findID3v1();
|
||||
d->ID3v1Location = Utils::findID3v1(this);
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
// Look for FLAC metadata, including vorbis comments
|
||||
|
||||
@@ -405,10 +445,10 @@ void FLAC::File::read(bool readProperties)
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
if(d->hasXiphComment)
|
||||
if(!d->xiphCommentData.isEmpty())
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData));
|
||||
else
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment);
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment());
|
||||
|
||||
if(readProperties) {
|
||||
|
||||
@@ -418,10 +458,10 @@ void FLAC::File::read(bool readProperties)
|
||||
|
||||
long streamLength;
|
||||
|
||||
if(d->hasID3v1)
|
||||
if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location - d->streamStart;
|
||||
else
|
||||
streamLength = File::length() - d->streamStart;
|
||||
streamLength = length() - d->streamStart;
|
||||
|
||||
d->properties = new Properties(infoData, streamLength);
|
||||
}
|
||||
@@ -439,7 +479,7 @@ void FLAC::File::scan()
|
||||
|
||||
long nextBlockOffset;
|
||||
|
||||
if(d->hasID3v2)
|
||||
if(d->ID3v2Location >= 0)
|
||||
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
|
||||
else
|
||||
nextBlockOffset = find("fLaC");
|
||||
@@ -453,51 +493,45 @@ void FLAC::File::scan()
|
||||
nextBlockOffset += 4;
|
||||
d->flacStart = nextBlockOffset;
|
||||
|
||||
seek(nextBlockOffset);
|
||||
while(true) {
|
||||
|
||||
ByteVector header = readBlock(4);
|
||||
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
|
||||
// ..
|
||||
// <24> Length of metadata to follow
|
||||
// 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
|
||||
|
||||
char blockType = header[0] & 0x7f;
|
||||
bool isLastBlock = (header[0] & 0x80) != 0;
|
||||
uint length = header.toUInt(1U, 3U);
|
||||
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
|
||||
// First block should be the stream_info metadata
|
||||
|
||||
if(blockType != MetadataBlock::StreamInfo) {
|
||||
debug("FLAC::File::scan() -- invalid FLAC stream");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(d->blocks.isEmpty() && blockType != MetadataBlock::StreamInfo) {
|
||||
debug("FLAC::File::scan() -- First block should be the stream_info metadata");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->blocks.append(new UnknownMetadataBlock(blockType, readBlock(length)));
|
||||
nextBlockOffset += length + 4;
|
||||
|
||||
// Search through the remaining metadata
|
||||
while(!isLastBlock) {
|
||||
|
||||
header = readBlock(4);
|
||||
blockType = header[0] & 0x7f;
|
||||
isLastBlock = (header[0] & 0x80) != 0;
|
||||
length = header.toUInt(1U, 3U);
|
||||
|
||||
if(length == 0 && blockType != MetadataBlock::Padding) {
|
||||
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(length);
|
||||
if(data.size() != length) {
|
||||
const ByteVector data = readBlock(blockLength);
|
||||
if(data.size() != blockLength) {
|
||||
debug("FLAC::File::scan() -- Failed to read a metadata block");
|
||||
setValid(false);
|
||||
return;
|
||||
@@ -507,12 +541,12 @@ void FLAC::File::scan()
|
||||
|
||||
// Found the vorbis-comment
|
||||
if(blockType == MetadataBlock::VorbisComment) {
|
||||
if(!d->hasXiphComment) {
|
||||
if(d->xiphCommentData.isEmpty()) {
|
||||
d->xiphCommentData = data;
|
||||
d->hasXiphComment = true;
|
||||
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one");
|
||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Picture) {
|
||||
@@ -525,25 +559,20 @@ void FLAC::File::scan()
|
||||
delete picture;
|
||||
}
|
||||
}
|
||||
|
||||
if(!block) {
|
||||
block = new UnknownMetadataBlock(blockType, data);
|
||||
}
|
||||
if(block->code() != MetadataBlock::Padding) {
|
||||
d->blocks.append(block);
|
||||
else if(blockType == MetadataBlock::Padding) {
|
||||
// Skip all padding blocks.
|
||||
}
|
||||
else {
|
||||
delete block;
|
||||
block = new UnknownMetadataBlock(blockType, data);
|
||||
}
|
||||
|
||||
nextBlockOffset += length + 4;
|
||||
if(block)
|
||||
d->blocks.append(block);
|
||||
|
||||
if(nextBlockOffset >= File::length()) {
|
||||
debug("FLAC::File::scan() -- FLAC stream corrupted");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
seek(nextBlockOffset);
|
||||
nextBlockOffset += blockLength + 4;
|
||||
|
||||
if(isLastBlock)
|
||||
break;
|
||||
}
|
||||
|
||||
// End of metadata, now comes the datastream
|
||||
@@ -552,30 +581,3 @@ void FLAC::File::scan()
|
||||
|
||||
d->scanned = true;
|
||||
}
|
||||
|
||||
long FLAC::File::findID3v1()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(-128, End);
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long FLAC::File::findID3v2()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(0);
|
||||
|
||||
if(readBlock(3) == ID3v2::Header::fileIdentifier())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,23 @@ namespace TagLib {
|
||||
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.
|
||||
@@ -155,9 +172,6 @@ namespace TagLib {
|
||||
* has no XiphComment, one will be constructed from the ID3-tags.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*
|
||||
* \warning In the current implementation, it's dangerous to call save()
|
||||
* repeatedly. At worst it will corrupt the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
@@ -226,7 +240,7 @@ namespace TagLib {
|
||||
* \see ID3v2FrameFactory
|
||||
* \deprecated This value should be passed in via the constructor
|
||||
*/
|
||||
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
TAGLIB_DEPRECATED void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
|
||||
/*!
|
||||
* Returns the block of data used by FLAC::Properties for parsing the
|
||||
@@ -234,7 +248,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Always returns an empty vector.
|
||||
*/
|
||||
ByteVector streamInfoData(); // BIC: remove
|
||||
TAGLIB_DEPRECATED ByteVector streamInfoData(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns the length of the audio-stream, used by FLAC::Properties for
|
||||
@@ -242,7 +256,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated Always returns zero.
|
||||
*/
|
||||
long streamLength(); // BIC: remove
|
||||
TAGLIB_DEPRECATED long streamLength(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns a list of pictures attached to the FLAC file.
|
||||
@@ -268,6 +282,21 @@ namespace TagLib {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -289,14 +318,21 @@ namespace TagLib {
|
||||
*/
|
||||
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();
|
||||
long findID3v2();
|
||||
long findID3v1();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
|
||||
@@ -50,14 +50,14 @@ public:
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::Picture::Picture()
|
||||
FLAC::Picture::Picture() :
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
d = new PicturePrivate;
|
||||
}
|
||||
|
||||
FLAC::Picture::Picture(const ByteVector &data)
|
||||
FLAC::Picture::Picture(const ByteVector &data) :
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
d = new PicturePrivate;
|
||||
parse(data);
|
||||
}
|
||||
|
||||
@@ -78,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint pos = 0;
|
||||
unsigned int pos = 0;
|
||||
d->type = FLAC::Picture::Type(data.toUInt(pos));
|
||||
pos += 4;
|
||||
uint mimeTypeLength = data.toUInt(pos);
|
||||
unsigned int mimeTypeLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + mimeTypeLength + 24 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -89,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
|
||||
pos += mimeTypeLength;
|
||||
uint descriptionLength = data.toUInt(pos);
|
||||
unsigned int descriptionLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + descriptionLength + 20 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -105,7 +105,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
pos += 4;
|
||||
d->numColors = data.toUInt(pos);
|
||||
pos += 4;
|
||||
uint dataLength = data.toUInt(pos);
|
||||
unsigned int dataLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + dataLength > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
|
||||
@@ -135,7 +135,7 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
return;
|
||||
}
|
||||
|
||||
uint pos = 0;
|
||||
unsigned int pos = 0;
|
||||
|
||||
// Minimum block size (in samples)
|
||||
pos += 2;
|
||||
@@ -149,7 +149,7 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
// Maximum frame size (in bytes)
|
||||
pos += 3;
|
||||
|
||||
const uint flags = data.toUInt(pos, true);
|
||||
const unsigned int flags = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->sampleRate = flags >> 12;
|
||||
@@ -159,8 +159,8 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
// The last 4 bits are the most significant 4 bits for the 36 bit
|
||||
// stream length in samples. (Audio files measured in days)
|
||||
|
||||
const ulonglong hi = flags & 0xf;
|
||||
const ulonglong lo = data.toUInt(pos, true);
|
||||
const unsigned long long hi = flags & 0xf;
|
||||
const unsigned long long lo = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->sampleFrames = (hi << 32) | lo;
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -120,7 +120,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
int sampleWidth() const;
|
||||
TAGLIB_DEPRECATED int sampleWidth() const;
|
||||
|
||||
/*!
|
||||
* Return the number of sample frames.
|
||||
|
||||
@@ -39,11 +39,10 @@ public:
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data)
|
||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
|
||||
d(new UnknownMetadataBlockPrivate())
|
||||
{
|
||||
d = new UnknownMetadataBlockPrivate;
|
||||
d->code = code;
|
||||
//debug(String(data.toHex()));
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,10 +15,15 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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"
|
||||
@@ -96,9 +101,9 @@ bool IT::File::save()
|
||||
|
||||
seek(2, Current);
|
||||
|
||||
ushort length = 0;
|
||||
ushort instrumentCount = 0;
|
||||
ushort sampleCount = 0;
|
||||
unsigned short length = 0;
|
||||
unsigned short instrumentCount = 0;
|
||||
unsigned short sampleCount = 0;
|
||||
|
||||
if(!readU16L(length) || !readU16L(instrumentCount) || !readU16L(sampleCount))
|
||||
return false;
|
||||
@@ -107,9 +112,9 @@ bool IT::File::save()
|
||||
|
||||
// write comment as instrument and sample names:
|
||||
StringList lines = d->tag.comment().split("\n");
|
||||
for(ushort i = 0; i < instrumentCount; ++ i) {
|
||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + ((long)i << 2));
|
||||
ulong instrumentOffset = 0;
|
||||
unsigned long instrumentOffset = 0;
|
||||
if(!readU32L(instrumentOffset))
|
||||
return false;
|
||||
|
||||
@@ -118,28 +123,28 @@ bool IT::File::save()
|
||||
if(i < lines.size())
|
||||
writeString(lines[i], 25);
|
||||
else
|
||||
writeString(String::null, 25);
|
||||
writeString(String(), 25);
|
||||
writeByte(0);
|
||||
}
|
||||
|
||||
for(ushort i = 0; i < sampleCount; ++ i) {
|
||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
||||
ulong sampleOffset = 0;
|
||||
unsigned long sampleOffset = 0;
|
||||
if(!readU32L(sampleOffset))
|
||||
return false;
|
||||
|
||||
seek(sampleOffset + 20);
|
||||
|
||||
if((TagLib::uint)(i + instrumentCount) < lines.size())
|
||||
if((unsigned int)(i + instrumentCount) < lines.size())
|
||||
writeString(lines[i + instrumentCount], 25);
|
||||
else
|
||||
writeString(String::null, 25);
|
||||
writeString(String(), 25);
|
||||
writeByte(0);
|
||||
}
|
||||
|
||||
// write rest as message:
|
||||
StringList messageLines;
|
||||
for(uint i = instrumentCount + sampleCount; i < lines.size(); ++ i)
|
||||
for(unsigned int i = instrumentCount + sampleCount; i < lines.size(); ++ i)
|
||||
messageLines.append(lines[i]);
|
||||
ByteVector message = messageLines.toString("\r").data(String::Latin1);
|
||||
|
||||
@@ -149,15 +154,15 @@ bool IT::File::save()
|
||||
message.resize(7999);
|
||||
message.append((char)0);
|
||||
|
||||
ushort special = 0;
|
||||
ushort messageLength = 0;
|
||||
ulong messageOffset = 0;
|
||||
unsigned short special = 0;
|
||||
unsigned short messageLength = 0;
|
||||
unsigned long messageOffset = 0;
|
||||
|
||||
seek(46);
|
||||
if(!readU16L(special))
|
||||
return false;
|
||||
|
||||
ulong fileSize = File::length();
|
||||
unsigned long fileSize = File::length();
|
||||
if(special & Properties::MessageAttached) {
|
||||
seek(54);
|
||||
if(!readU16L(messageLength) || !readU32L(messageOffset))
|
||||
@@ -259,8 +264,8 @@ void IT::File::read(bool)
|
||||
d->properties.setChannels(channels);
|
||||
|
||||
// real length might be shorter because of skips and terminator
|
||||
ushort realLength = 0;
|
||||
for(ushort i = 0; i < length; ++ i) {
|
||||
unsigned short realLength = 0;
|
||||
for(unsigned short i = 0; i < length; ++ i) {
|
||||
READ_BYTE_AS(order);
|
||||
if(order == 255) break;
|
||||
if(order != 254) ++ realLength;
|
||||
@@ -272,9 +277,9 @@ void IT::File::read(bool)
|
||||
// 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
|
||||
// e.g. VLC seems to interpret a nil as a space. I
|
||||
// don't know what is the proper behaviour.
|
||||
for(ushort i = 0; i < instrumentCount; ++ i) {
|
||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + ((long)i << 2));
|
||||
READ_U32L_AS(instrumentOffset);
|
||||
seek(instrumentOffset);
|
||||
@@ -290,7 +295,7 @@ void IT::File::read(bool)
|
||||
comment.append(instrumentName);
|
||||
}
|
||||
|
||||
for(ushort i = 0; i < sampleCount; ++ i) {
|
||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
||||
READ_U32L_AS(sampleOffset);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,10 +15,15 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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 "itproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -46,26 +51,26 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
int channels;
|
||||
ushort lengthInPatterns;
|
||||
ushort instrumentCount;
|
||||
ushort sampleCount;
|
||||
ushort patternCount;
|
||||
ushort version;
|
||||
ushort compatibleVersion;
|
||||
ushort flags;
|
||||
ushort special;
|
||||
uchar globalVolume;
|
||||
uchar mixVolume;
|
||||
uchar tempo;
|
||||
uchar bpmSpeed;
|
||||
uchar panningSeparation;
|
||||
uchar pitchWheelDepth;
|
||||
int channels;
|
||||
unsigned short lengthInPatterns;
|
||||
unsigned short instrumentCount;
|
||||
unsigned short sampleCount;
|
||||
unsigned short patternCount;
|
||||
unsigned short version;
|
||||
unsigned short compatibleVersion;
|
||||
unsigned short flags;
|
||||
unsigned short special;
|
||||
unsigned char globalVolume;
|
||||
unsigned char mixVolume;
|
||||
unsigned char tempo;
|
||||
unsigned char bpmSpeed;
|
||||
unsigned char panningSeparation;
|
||||
unsigned char pitchWheelDepth;
|
||||
};
|
||||
|
||||
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
|
||||
AudioProperties(propertiesStyle),
|
||||
d(new PropertiesPrivate)
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -104,7 +109,7 @@ int IT::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::lengthInPatterns() const
|
||||
unsigned short IT::Properties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
@@ -114,67 +119,67 @@ bool IT::Properties::stereo() const
|
||||
return d->flags & Stereo;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::instrumentCount() const
|
||||
unsigned short IT::Properties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::sampleCount() const
|
||||
unsigned short IT::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::patternCount() const
|
||||
unsigned short IT::Properties::patternCount() const
|
||||
{
|
||||
return d->patternCount;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::version() const
|
||||
unsigned short IT::Properties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::compatibleVersion() const
|
||||
unsigned short IT::Properties::compatibleVersion() const
|
||||
{
|
||||
return d->compatibleVersion;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::flags() const
|
||||
unsigned short IT::Properties::flags() const
|
||||
{
|
||||
return d->flags;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::special() const
|
||||
unsigned short IT::Properties::special() const
|
||||
{
|
||||
return d->special;
|
||||
}
|
||||
|
||||
uchar IT::Properties::globalVolume() const
|
||||
unsigned char IT::Properties::globalVolume() const
|
||||
{
|
||||
return d->globalVolume;
|
||||
}
|
||||
|
||||
uchar IT::Properties::mixVolume() const
|
||||
unsigned char IT::Properties::mixVolume() const
|
||||
{
|
||||
return d->mixVolume;
|
||||
}
|
||||
|
||||
uchar IT::Properties::tempo() const
|
||||
unsigned char IT::Properties::tempo() const
|
||||
{
|
||||
return d->tempo;
|
||||
}
|
||||
|
||||
uchar IT::Properties::bpmSpeed() const
|
||||
unsigned char IT::Properties::bpmSpeed() const
|
||||
{
|
||||
return d->bpmSpeed;
|
||||
}
|
||||
|
||||
uchar IT::Properties::panningSeparation() const
|
||||
unsigned char IT::Properties::panningSeparation() const
|
||||
{
|
||||
return d->panningSeparation;
|
||||
}
|
||||
|
||||
uchar IT::Properties::pitchWheelDepth() const
|
||||
unsigned char IT::Properties::pitchWheelDepth() const
|
||||
{
|
||||
return d->pitchWheelDepth;
|
||||
}
|
||||
@@ -184,72 +189,72 @@ void IT::Properties::setChannels(int channels)
|
||||
d->channels = channels;
|
||||
}
|
||||
|
||||
void IT::Properties::setLengthInPatterns(ushort lengthInPatterns)
|
||||
void IT::Properties::setLengthInPatterns(unsigned short lengthInPatterns)
|
||||
{
|
||||
d->lengthInPatterns = lengthInPatterns;
|
||||
}
|
||||
|
||||
void IT::Properties::setInstrumentCount(ushort instrumentCount)
|
||||
void IT::Properties::setInstrumentCount(unsigned short instrumentCount)
|
||||
{
|
||||
d->instrumentCount = instrumentCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setSampleCount(ushort sampleCount)
|
||||
void IT::Properties::setSampleCount(unsigned short sampleCount)
|
||||
{
|
||||
d->sampleCount = sampleCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setPatternCount(ushort patternCount)
|
||||
void IT::Properties::setPatternCount(unsigned short patternCount)
|
||||
{
|
||||
d->patternCount = patternCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setFlags(ushort flags)
|
||||
void IT::Properties::setFlags(unsigned short flags)
|
||||
{
|
||||
d->flags = flags;
|
||||
}
|
||||
|
||||
void IT::Properties::setSpecial(ushort special)
|
||||
void IT::Properties::setSpecial(unsigned short special)
|
||||
{
|
||||
d->special = special;
|
||||
}
|
||||
|
||||
void IT::Properties::setCompatibleVersion(ushort compatibleVersion)
|
||||
void IT::Properties::setCompatibleVersion(unsigned short compatibleVersion)
|
||||
{
|
||||
d->compatibleVersion = compatibleVersion;
|
||||
}
|
||||
|
||||
void IT::Properties::setVersion(ushort version)
|
||||
void IT::Properties::setVersion(unsigned short version)
|
||||
{
|
||||
d->version = version;
|
||||
}
|
||||
|
||||
void IT::Properties::setGlobalVolume(uchar globalVolume)
|
||||
void IT::Properties::setGlobalVolume(unsigned char globalVolume)
|
||||
{
|
||||
d->globalVolume = globalVolume;
|
||||
}
|
||||
|
||||
void IT::Properties::setMixVolume(uchar mixVolume)
|
||||
void IT::Properties::setMixVolume(unsigned char mixVolume)
|
||||
{
|
||||
d->mixVolume = mixVolume;
|
||||
}
|
||||
|
||||
void IT::Properties::setTempo(uchar tempo)
|
||||
void IT::Properties::setTempo(unsigned char tempo)
|
||||
{
|
||||
d->tempo = tempo;
|
||||
}
|
||||
|
||||
void IT::Properties::setBpmSpeed(uchar bpmSpeed)
|
||||
void IT::Properties::setBpmSpeed(unsigned char bpmSpeed)
|
||||
{
|
||||
d->bpmSpeed = bpmSpeed;
|
||||
}
|
||||
|
||||
void IT::Properties::setPanningSeparation(uchar panningSeparation)
|
||||
void IT::Properties::setPanningSeparation(unsigned char panningSeparation)
|
||||
{
|
||||
d->panningSeparation = panningSeparation;
|
||||
}
|
||||
|
||||
void IT::Properties::setPitchWheelDepth(uchar pitchWheelDepth)
|
||||
void IT::Properties::setPitchWheelDepth(unsigned char pitchWheelDepth)
|
||||
{
|
||||
d->pitchWheelDepth = pitchWheelDepth;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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_ITPROPERTIES_H
|
||||
@@ -58,37 +62,37 @@ namespace TagLib {
|
||||
int sampleRate() const;
|
||||
int channels() const;
|
||||
|
||||
ushort lengthInPatterns() const;
|
||||
bool stereo() const;
|
||||
ushort instrumentCount() const;
|
||||
ushort sampleCount() const;
|
||||
ushort patternCount() const;
|
||||
ushort version() const;
|
||||
ushort compatibleVersion() const;
|
||||
ushort flags() const;
|
||||
ushort special() const;
|
||||
uchar globalVolume() const;
|
||||
uchar mixVolume() const;
|
||||
uchar tempo() const;
|
||||
uchar bpmSpeed() const;
|
||||
uchar panningSeparation() const;
|
||||
uchar pitchWheelDepth() const;
|
||||
unsigned short lengthInPatterns() const;
|
||||
bool stereo() const;
|
||||
unsigned short instrumentCount() const;
|
||||
unsigned short sampleCount() const;
|
||||
unsigned short patternCount() const;
|
||||
unsigned short version() const;
|
||||
unsigned short compatibleVersion() const;
|
||||
unsigned short flags() const;
|
||||
unsigned short special() const;
|
||||
unsigned char globalVolume() const;
|
||||
unsigned char mixVolume() const;
|
||||
unsigned char tempo() const;
|
||||
unsigned char bpmSpeed() const;
|
||||
unsigned char panningSeparation() const;
|
||||
unsigned char pitchWheelDepth() const;
|
||||
|
||||
void setChannels(int channels);
|
||||
void setLengthInPatterns(ushort lengthInPatterns);
|
||||
void setInstrumentCount(ushort instrumentCount);
|
||||
void setSampleCount (ushort sampleCount);
|
||||
void setPatternCount(ushort patternCount);
|
||||
void setVersion (ushort version);
|
||||
void setCompatibleVersion(ushort compatibleVersion);
|
||||
void setFlags (ushort flags);
|
||||
void setSpecial (ushort special);
|
||||
void setGlobalVolume(uchar globalVolume);
|
||||
void setMixVolume (uchar mixVolume);
|
||||
void setTempo (uchar tempo);
|
||||
void setBpmSpeed (uchar bpmSpeed);
|
||||
void setPanningSeparation(uchar panningSeparation);
|
||||
void setPitchWheelDepth (uchar pitchWheelDepth);
|
||||
void setLengthInPatterns(unsigned short lengthInPatterns);
|
||||
void setInstrumentCount(unsigned short instrumentCount);
|
||||
void setSampleCount (unsigned short sampleCount);
|
||||
void setPatternCount(unsigned short patternCount);
|
||||
void setVersion (unsigned short version);
|
||||
void setCompatibleVersion(unsigned short compatibleVersion);
|
||||
void setFlags (unsigned short flags);
|
||||
void setSpecial (unsigned short special);
|
||||
void setGlobalVolume(unsigned char globalVolume);
|
||||
void setMixVolume (unsigned char mixVolume);
|
||||
void setTempo (unsigned char tempo);
|
||||
void setBpmSpeed (unsigned char bpmSpeed);
|
||||
void setPanningSeparation(unsigned char panningSeparation);
|
||||
void setPitchWheelDepth (unsigned char pitchWheelDepth);
|
||||
|
||||
private:
|
||||
Properties(const Properties&);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,10 +15,15 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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 "modfile.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tdebug.h"
|
||||
@@ -92,14 +97,14 @@ bool Mod::File::save()
|
||||
seek(0);
|
||||
writeString(d->tag.title(), 20);
|
||||
StringList lines = d->tag.comment().split("\n");
|
||||
uint n = std::min(lines.size(), d->properties.instrumentCount());
|
||||
for(uint i = 0; i < n; ++ i) {
|
||||
unsigned int n = std::min(lines.size(), d->properties.instrumentCount());
|
||||
for(unsigned int i = 0; i < n; ++ i) {
|
||||
writeString(lines[i], 22);
|
||||
seek(8, Current);
|
||||
}
|
||||
|
||||
for(uint i = n; i < d->properties.instrumentCount(); ++ i) {
|
||||
writeString(String::null, 22);
|
||||
for(unsigned int i = n; i < d->properties.instrumentCount(); ++ i) {
|
||||
writeString(String(), 22);
|
||||
seek(8, Current);
|
||||
}
|
||||
return true;
|
||||
@@ -114,8 +119,8 @@ void Mod::File::read(bool)
|
||||
ByteVector modId = readBlock(4);
|
||||
READ_ASSERT(modId.size() == 4);
|
||||
|
||||
int channels = 4;
|
||||
uint instruments = 31;
|
||||
int channels = 4;
|
||||
unsigned int instruments = 31;
|
||||
if(modId == "M.K." || modId == "M!K!" || modId == "M&K!" || modId == "N.T.") {
|
||||
d->tag.setTrackerName("ProTracker");
|
||||
channels = 4;
|
||||
@@ -159,7 +164,7 @@ void Mod::File::read(bool)
|
||||
READ_STRING(d->tag.setTitle, 20);
|
||||
|
||||
StringList comment;
|
||||
for(uint i = 0; i < instruments; ++ i) {
|
||||
for(unsigned int i = 0; i < instruments; ++ i) {
|
||||
READ_STRING_AS(instrumentName, 22);
|
||||
// value in words, * 2 (<< 1) for bytes:
|
||||
READ_U16B_AS(sampleLength);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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_MODFILE_H
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,10 +15,15 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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 "modfilebase.h"
|
||||
|
||||
@@ -33,14 +38,14 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeString(const String &s, ulong size, char padding)
|
||||
void Mod::FileBase::writeString(const String &s, unsigned long size, char padding)
|
||||
{
|
||||
ByteVector data(s.data(String::Latin1));
|
||||
data.resize(size, padding);
|
||||
writeBlock(data);
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readString(String &s, ulong size)
|
||||
bool Mod::FileBase::readString(String &s, unsigned long size)
|
||||
{
|
||||
ByteVector data(readBlock(size));
|
||||
if(data.size() < size) return false;
|
||||
@@ -49,39 +54,39 @@ bool Mod::FileBase::readString(String &s, ulong size)
|
||||
{
|
||||
data.resize(index);
|
||||
}
|
||||
data.replace((char) 0xff, ' ');
|
||||
data.replace('\xff', ' ');
|
||||
|
||||
s = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeByte(uchar byte)
|
||||
void Mod::FileBase::writeByte(unsigned char byte)
|
||||
{
|
||||
ByteVector data(1, byte);
|
||||
writeBlock(data);
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU16L(ushort number)
|
||||
void Mod::FileBase::writeU16L(unsigned short number)
|
||||
{
|
||||
writeBlock(ByteVector::fromShort(number, false));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU32L(ulong number)
|
||||
void Mod::FileBase::writeU32L(unsigned long number)
|
||||
{
|
||||
writeBlock(ByteVector::fromUInt(number, false));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU16B(ushort number)
|
||||
void Mod::FileBase::writeU16B(unsigned short number)
|
||||
{
|
||||
writeBlock(ByteVector::fromShort(number, true));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU32B(ulong number)
|
||||
void Mod::FileBase::writeU32B(unsigned long number)
|
||||
{
|
||||
writeBlock(ByteVector::fromUInt(number, true));
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readByte(uchar &byte)
|
||||
bool Mod::FileBase::readByte(unsigned char &byte)
|
||||
{
|
||||
ByteVector data(readBlock(1));
|
||||
if(data.size() < 1) return false;
|
||||
@@ -89,7 +94,7 @@ bool Mod::FileBase::readByte(uchar &byte)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU16L(ushort &number)
|
||||
bool Mod::FileBase::readU16L(unsigned short &number)
|
||||
{
|
||||
ByteVector data(readBlock(2));
|
||||
if(data.size() < 2) return false;
|
||||
@@ -97,14 +102,14 @@ bool Mod::FileBase::readU16L(ushort &number)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU32L(ulong &number) {
|
||||
bool Mod::FileBase::readU32L(unsigned long &number) {
|
||||
ByteVector data(readBlock(4));
|
||||
if(data.size() < 4) return false;
|
||||
number = data.toUInt(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU16B(ushort &number)
|
||||
bool Mod::FileBase::readU16B(unsigned short &number)
|
||||
{
|
||||
ByteVector data(readBlock(2));
|
||||
if(data.size() < 2) return false;
|
||||
@@ -112,7 +117,7 @@ bool Mod::FileBase::readU16B(ushort &number)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU32B(ulong &number) {
|
||||
bool Mod::FileBase::readU32B(unsigned long &number) {
|
||||
ByteVector data(readBlock(4));
|
||||
if(data.size() < 4) return false;
|
||||
number = data.toUInt(true);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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_MODFILEBASE_H
|
||||
@@ -40,19 +44,19 @@ namespace TagLib {
|
||||
FileBase(FileName file);
|
||||
FileBase(IOStream *stream);
|
||||
|
||||
void writeString(const String &s, ulong size, char padding = 0);
|
||||
void writeByte(uchar byte);
|
||||
void writeU16L(ushort number);
|
||||
void writeU32L(ulong number);
|
||||
void writeU16B(ushort number);
|
||||
void writeU32B(ulong number);
|
||||
void writeString(const String &s, unsigned long size, char padding = 0);
|
||||
void writeByte(unsigned char byte);
|
||||
void writeU16L(unsigned short number);
|
||||
void writeU32L(unsigned long number);
|
||||
void writeU16B(unsigned short number);
|
||||
void writeU32B(unsigned long number);
|
||||
|
||||
bool readString(String &s, ulong size);
|
||||
bool readByte(uchar &byte);
|
||||
bool readU16L(ushort &number);
|
||||
bool readU32L(ulong &number);
|
||||
bool readU16B(ushort &number);
|
||||
bool readU32B(ulong &number);
|
||||
bool readString(String &s, unsigned long size);
|
||||
bool readByte(unsigned char &byte);
|
||||
bool readU16L(unsigned short &number);
|
||||
bool readU32L(unsigned long &number);
|
||||
bool readU16B(unsigned short &number);
|
||||
bool readU32B(unsigned long &number);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@
|
||||
setter(number); \
|
||||
}
|
||||
|
||||
#define READ_BYTE(setter) READ(setter,uchar,readByte)
|
||||
#define READ_U16L(setter) READ(setter,ushort,readU16L)
|
||||
#define READ_U32L(setter) READ(setter,ulong,readU32L)
|
||||
#define READ_U16B(setter) READ(setter,ushort,readU16B)
|
||||
#define READ_U32B(setter) READ(setter,ulong,readU32B)
|
||||
#define READ_BYTE(setter) READ(setter,unsigned char,readByte)
|
||||
#define READ_U16L(setter) READ(setter,unsigned short,readU16L)
|
||||
#define READ_U32L(setter) READ(setter,unsigned long,readU32L)
|
||||
#define READ_U16B(setter) READ(setter,unsigned short,readU16B)
|
||||
#define READ_U32B(setter) READ(setter,unsigned long,readU32B)
|
||||
|
||||
#define READ_STRING(setter,size) \
|
||||
{ \
|
||||
@@ -54,11 +54,11 @@
|
||||
type name = 0; \
|
||||
READ_ASSERT(read(name));
|
||||
|
||||
#define READ_BYTE_AS(name) READ_AS(uchar,name,readByte)
|
||||
#define READ_U16L_AS(name) READ_AS(ushort,name,readU16L)
|
||||
#define READ_U32L_AS(name) READ_AS(ulong,name,readU32L)
|
||||
#define READ_U16B_AS(name) READ_AS(ushort,name,readU16B)
|
||||
#define READ_U32B_AS(name) READ_AS(ulong,name,readU32B)
|
||||
#define READ_BYTE_AS(name) READ_AS(unsigned char,name,readByte)
|
||||
#define READ_U16L_AS(name) READ_AS(unsigned short,name,readU16L)
|
||||
#define READ_U32L_AS(name) READ_AS(unsigned long,name,readU32L)
|
||||
#define READ_U16B_AS(name) READ_AS(unsigned short,name,readU16B)
|
||||
#define READ_U32B_AS(name) READ_AS(unsigned long,name,readU32B)
|
||||
|
||||
#define READ_STRING_AS(name,size) \
|
||||
String name; \
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,10 +15,15 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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 "modproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -34,14 +39,14 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
int channels;
|
||||
uint instrumentCount;
|
||||
uchar lengthInPatterns;
|
||||
int channels;
|
||||
unsigned int instrumentCount;
|
||||
unsigned char lengthInPatterns;
|
||||
};
|
||||
|
||||
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
|
||||
AudioProperties(propertiesStyle),
|
||||
d(new PropertiesPrivate)
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -80,12 +85,12 @@ int Mod::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
TagLib::uint Mod::Properties::instrumentCount() const
|
||||
unsigned int Mod::Properties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
uchar Mod::Properties::lengthInPatterns() const
|
||||
unsigned char Mod::Properties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
@@ -95,12 +100,12 @@ void Mod::Properties::setChannels(int channels)
|
||||
d->channels = channels;
|
||||
}
|
||||
|
||||
void Mod::Properties::setInstrumentCount(uint instrumentCount)
|
||||
void Mod::Properties::setInstrumentCount(unsigned int instrumentCount)
|
||||
{
|
||||
d->instrumentCount = instrumentCount;
|
||||
}
|
||||
|
||||
void Mod::Properties::setLengthInPatterns(uchar lengthInPatterns)
|
||||
void Mod::Properties::setLengthInPatterns(unsigned char lengthInPatterns)
|
||||
{
|
||||
d->lengthInPatterns = lengthInPatterns;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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_MODPROPERTIES_H
|
||||
@@ -42,13 +46,13 @@ namespace TagLib {
|
||||
int sampleRate() const;
|
||||
int channels() const;
|
||||
|
||||
uint instrumentCount() const;
|
||||
uchar lengthInPatterns() const;
|
||||
unsigned int instrumentCount() const;
|
||||
unsigned char lengthInPatterns() const;
|
||||
|
||||
void setChannels(int channels);
|
||||
|
||||
void setInstrumentCount(uint sampleCount);
|
||||
void setLengthInPatterns(uchar lengthInPatterns);
|
||||
void setInstrumentCount(unsigned int sampleCount);
|
||||
void setLengthInPatterns(unsigned char lengthInPatterns);
|
||||
|
||||
private:
|
||||
friend class File;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,10 +15,15 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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 "modtag.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
@@ -38,9 +43,10 @@ public:
|
||||
String trackerName;
|
||||
};
|
||||
|
||||
Mod::Tag::Tag() : TagLib::Tag()
|
||||
Mod::Tag::Tag() :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
Mod::Tag::~Tag()
|
||||
@@ -55,12 +61,12 @@ String Mod::Tag::title() const
|
||||
|
||||
String Mod::Tag::artist() const
|
||||
{
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String Mod::Tag::album() const
|
||||
{
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String Mod::Tag::comment() const
|
||||
@@ -70,15 +76,15 @@ String Mod::Tag::comment() const
|
||||
|
||||
String Mod::Tag::genre() const
|
||||
{
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
TagLib::uint Mod::Tag::year() const
|
||||
unsigned int Mod::Tag::year() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
TagLib::uint Mod::Tag::track() const
|
||||
unsigned int Mod::Tag::track() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -110,11 +116,11 @@ void Mod::Tag::setGenre(const String &)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::Tag::setYear(uint)
|
||||
void Mod::Tag::setYear(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::Tag::setTrack(uint)
|
||||
void Mod::Tag::setTrack(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -128,7 +134,7 @@ PropertyMap Mod::Tag::properties() const
|
||||
PropertyMap properties;
|
||||
properties["TITLE"] = d->title;
|
||||
properties["COMMENT"] = d->comment;
|
||||
if(!(d->trackerName.isNull()))
|
||||
if(!(d->trackerName.isEmpty()))
|
||||
properties["TRACKERNAME"] = d->trackerName;
|
||||
return properties;
|
||||
}
|
||||
@@ -142,19 +148,19 @@ PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps)
|
||||
d->title = properties["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title = String::null;
|
||||
d->title.clear();
|
||||
|
||||
if(properties.contains("COMMENT")) {
|
||||
d->comment = properties["COMMENT"].front();
|
||||
oneValueSet.append("COMMENT");
|
||||
} else
|
||||
d->comment = String::null;
|
||||
d->comment.clear();
|
||||
|
||||
if(properties.contains("TRACKERNAME")) {
|
||||
d->trackerName = properties["TRACKERNAME"].front();
|
||||
oneValueSet.append("TRACKERNAME");
|
||||
} else
|
||||
d->trackerName = String::null;
|
||||
d->trackerName.clear();
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 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 *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* 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 *
|
||||
* 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_MODTAG_H
|
||||
@@ -50,39 +54,39 @@ namespace TagLib {
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
String title() const;
|
||||
virtual String title() const;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
String artist() const;
|
||||
virtual String artist() const;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
String album() const;
|
||||
virtual String album() const;
|
||||
|
||||
/*!
|
||||
* Returns the track comment derived from the instrument/sample/pattern
|
||||
* names; if no comment is present in the tag String::null will be
|
||||
* returned.
|
||||
*/
|
||||
String comment() const;
|
||||
virtual String comment() const;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
String genre() const;
|
||||
virtual String genre() const;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns 0.
|
||||
*/
|
||||
uint year() const;
|
||||
virtual unsigned int year() const;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns 0.
|
||||
*/
|
||||
uint track() const;
|
||||
virtual unsigned int track() const;
|
||||
|
||||
/*!
|
||||
* Returns the name of the tracker used to create/edit the module file.
|
||||
@@ -101,17 +105,17 @@ namespace TagLib {
|
||||
* Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20
|
||||
* characters.
|
||||
*/
|
||||
void setTitle(const String &title);
|
||||
virtual void setTitle(const String &title);
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setArtist(const String &artist);
|
||||
virtual void setArtist(const String &artist);
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setAlbum(const String &album);
|
||||
virtual void setAlbum(const String &album);
|
||||
|
||||
/*!
|
||||
* Sets the comment to \a comment. If \a comment is String::null then
|
||||
@@ -130,22 +134,22 @@ namespace TagLib {
|
||||
* Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22
|
||||
* characters.
|
||||
*/
|
||||
void setComment(const String &comment);
|
||||
virtual void setComment(const String &comment);
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setGenre(const String &genre);
|
||||
virtual void setGenre(const String &genre);
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setYear(uint year);
|
||||
virtual void setYear(unsigned int year);
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setTrack(uint track);
|
||||
virtual void setTrack(unsigned int track);
|
||||
|
||||
/*!
|
||||
* Sets the tracker name to \a trackerName. If \a trackerName is
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include <tdebug.h>
|
||||
@@ -43,9 +39,11 @@ const char *MP4::Atom::containers[11] = {
|
||||
|
||||
MP4::Atom::Atom(File *file)
|
||||
{
|
||||
children.setAutoDelete(true);
|
||||
|
||||
offset = file->tell();
|
||||
ByteVector header = file->readBlock(8);
|
||||
if (header.size() != 8) {
|
||||
if(header.size() != 8) {
|
||||
// The atom header must be 8 bytes long, otherwise there is either
|
||||
// trailing garbage or the file is truncated
|
||||
debug("MP4: Couldn't read 8 bytes of data for atom header");
|
||||
@@ -56,24 +54,25 @@ MP4::Atom::Atom(File *file)
|
||||
|
||||
length = header.toUInt();
|
||||
|
||||
if(length == 1) {
|
||||
if(length == 0) {
|
||||
// The last atom which extends to the end of the file.
|
||||
length = file->length() - offset;
|
||||
}
|
||||
else if(length == 1) {
|
||||
// The atom has a 64-bit length.
|
||||
const long long longLength = file->readBlock(8).toLongLong();
|
||||
if(sizeof(long) == sizeof(long long)) {
|
||||
if(longLength <= LONG_MAX) {
|
||||
// The actual length fits in long. That's always the case if long is 64-bit.
|
||||
length = static_cast<long>(longLength);
|
||||
}
|
||||
else {
|
||||
if(longLength <= LONG_MAX) {
|
||||
// The atom has a 64-bit length, but it's actually a 31-bit value
|
||||
length = static_cast<long>(longLength);
|
||||
}
|
||||
else {
|
||||
debug("MP4: 64-bit atoms are not supported");
|
||||
length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
debug("MP4: 64-bit atoms are not supported");
|
||||
length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(length < 8) {
|
||||
debug("MP4: Invalid atom size");
|
||||
length = 0;
|
||||
@@ -94,7 +93,7 @@ MP4::Atom::Atom(File *file)
|
||||
while(file->tell() < offset + length) {
|
||||
MP4::Atom *child = new MP4::Atom(file);
|
||||
children.append(child);
|
||||
if (child->length == 0)
|
||||
if(child->length == 0)
|
||||
return;
|
||||
}
|
||||
return;
|
||||
@@ -106,10 +105,6 @@ MP4::Atom::Atom(File *file)
|
||||
|
||||
MP4::Atom::~Atom()
|
||||
{
|
||||
for(unsigned int i = 0; i < children.size(); i++) {
|
||||
delete children[i];
|
||||
}
|
||||
children.clear();
|
||||
}
|
||||
|
||||
MP4::Atom *
|
||||
@@ -118,9 +113,9 @@ MP4::Atom::find(const char *name1, const char *name2, const char *name3, const c
|
||||
if(name1 == 0) {
|
||||
return this;
|
||||
}
|
||||
for(unsigned int i = 0; i < children.size(); i++) {
|
||||
if(children[i]->name == name1) {
|
||||
return children[i]->find(name2, name3, name4);
|
||||
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
|
||||
if((*it)->name == name1) {
|
||||
return (*it)->find(name2, name3, name4);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -130,12 +125,12 @@ MP4::AtomList
|
||||
MP4::Atom::findall(const char *name, bool recursive)
|
||||
{
|
||||
MP4::AtomList result;
|
||||
for(unsigned int i = 0; i < children.size(); i++) {
|
||||
if(children[i]->name == name) {
|
||||
result.append(children[i]);
|
||||
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
|
||||
if((*it)->name == name) {
|
||||
result.append(*it);
|
||||
}
|
||||
if(recursive) {
|
||||
result.append(children[i]->findall(name, recursive));
|
||||
result.append((*it)->findall(name, recursive));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -148,9 +143,9 @@ MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const
|
||||
if(name1 == 0) {
|
||||
return true;
|
||||
}
|
||||
for(unsigned int i = 0; i < children.size(); i++) {
|
||||
if(children[i]->name == name1) {
|
||||
return children[i]->path(path, name2, name3);
|
||||
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
|
||||
if((*it)->name == name1) {
|
||||
return (*it)->path(path, name2, name3);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -158,6 +153,8 @@ MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const
|
||||
|
||||
MP4::Atoms::Atoms(File *file)
|
||||
{
|
||||
atoms.setAutoDelete(true);
|
||||
|
||||
file->seek(0, File::End);
|
||||
long end = file->tell();
|
||||
file->seek(0);
|
||||
@@ -171,18 +168,14 @@ MP4::Atoms::Atoms(File *file)
|
||||
|
||||
MP4::Atoms::~Atoms()
|
||||
{
|
||||
for(unsigned int i = 0; i < atoms.size(); i++) {
|
||||
delete atoms[i];
|
||||
}
|
||||
atoms.clear();
|
||||
}
|
||||
|
||||
MP4::Atom *
|
||||
MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
for(unsigned int i = 0; i < atoms.size(); i++) {
|
||||
if(atoms[i]->name == name1) {
|
||||
return atoms[i]->find(name2, name3, name4);
|
||||
for(AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
|
||||
if((*it)->name == name1) {
|
||||
return (*it)->find(name2, name3, name4);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -192,9 +185,9 @@ MP4::AtomList
|
||||
MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
MP4::AtomList path;
|
||||
for(unsigned int i = 0; i < atoms.size(); i++) {
|
||||
if(atoms[i]->name == name1) {
|
||||
if(!atoms[i]->path(path, name2, name3, name4)) {
|
||||
for(AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
|
||||
if((*it)->name == name1) {
|
||||
if(!(*it)->path(path, name2, name3, name4)) {
|
||||
path.clear();
|
||||
}
|
||||
return path;
|
||||
@@ -202,4 +195,3 @@ MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,29 +77,29 @@ namespace TagLib {
|
||||
class Atom
|
||||
{
|
||||
public:
|
||||
Atom(File *file);
|
||||
~Atom();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
|
||||
AtomList findall(const char *name, bool recursive = false);
|
||||
long offset;
|
||||
long length;
|
||||
TagLib::ByteVector name;
|
||||
AtomList children;
|
||||
Atom(File *file);
|
||||
~Atom();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
|
||||
AtomList findall(const char *name, bool recursive = false);
|
||||
long offset;
|
||||
long length;
|
||||
TagLib::ByteVector name;
|
||||
AtomList children;
|
||||
private:
|
||||
static const int numContainers = 11;
|
||||
static const char *containers[11];
|
||||
static const int numContainers = 11;
|
||||
static const char *containers[11];
|
||||
};
|
||||
|
||||
//! Root-level atoms
|
||||
class Atoms
|
||||
{
|
||||
public:
|
||||
Atoms(File *file);
|
||||
~Atoms();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList atoms;
|
||||
Atoms(File *file);
|
||||
~Atoms();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList atoms;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -33,20 +33,27 @@ using namespace TagLib;
|
||||
class MP4::CoverArt::CoverArtPrivate : public RefCounter
|
||||
{
|
||||
public:
|
||||
CoverArtPrivate() : RefCounter(), format(MP4::CoverArt::JPEG) {}
|
||||
CoverArtPrivate() :
|
||||
RefCounter(),
|
||||
format(MP4::CoverArt::JPEG) {}
|
||||
|
||||
Format format;
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
MP4::CoverArt::CoverArt(Format format, const ByteVector &data)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MP4::CoverArt::CoverArt(Format format, const ByteVector &data) :
|
||||
d(new CoverArtPrivate())
|
||||
{
|
||||
d = new CoverArtPrivate;
|
||||
d->format = format;
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
MP4::CoverArt::CoverArt(const CoverArt &item) : d(item.d)
|
||||
MP4::CoverArt::CoverArt(const CoverArt &item) :
|
||||
d(item.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
@@ -54,15 +61,18 @@ MP4::CoverArt::CoverArt(const CoverArt &item) : d(item.d)
|
||||
MP4::CoverArt &
|
||||
MP4::CoverArt::operator=(const CoverArt &item)
|
||||
{
|
||||
if(&item != this) {
|
||||
if(d->deref())
|
||||
delete d;
|
||||
d = item.d;
|
||||
d->ref();
|
||||
}
|
||||
CoverArt(item).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
MP4::CoverArt::swap(CoverArt &item)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, item.d);
|
||||
}
|
||||
|
||||
MP4::CoverArt::~CoverArt()
|
||||
{
|
||||
if(d->deref()) {
|
||||
@@ -81,4 +91,3 @@ MP4::CoverArt::data() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,8 +53,17 @@ namespace TagLib {
|
||||
~CoverArt();
|
||||
|
||||
CoverArt(const CoverArt &item);
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a item into this CoverArt.
|
||||
*/
|
||||
CoverArt &operator=(const CoverArt &item);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the CoverArt by the content of \a item.
|
||||
*/
|
||||
void swap(CoverArt &item);
|
||||
|
||||
//! Format of the image
|
||||
Format format() const;
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
#include "mp4file.h"
|
||||
@@ -69,6 +71,22 @@ public:
|
||||
MP4::Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool MP4::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An MP4 file has to have an "ftyp" box first.
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 8, false);
|
||||
return id.containsAt("ftyp", 4);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
@@ -130,8 +148,7 @@ MP4::File::read(bool readProperties)
|
||||
}
|
||||
|
||||
// must have a moov atom, otherwise consider it invalid
|
||||
MP4::Atom *moov = d->atoms->find("moov");
|
||||
if(!moov) {
|
||||
if(!d->atoms->find("moov")) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
@@ -158,3 +175,8 @@ MP4::File::save()
|
||||
return d->tag->save();
|
||||
}
|
||||
|
||||
bool
|
||||
MP4::File::hasMP4Tag() const
|
||||
{
|
||||
return (d->atoms->find("moov", "udta", "meta", "ilst") != 0);
|
||||
}
|
||||
|
||||
@@ -111,12 +111,24 @@ namespace TagLib {
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*
|
||||
* \warning In the current implementation, it's dangerous to call save()
|
||||
* repeatedly. At worst it will corrupt the file.
|
||||
*/
|
||||
bool save();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an MP4 tag, or the
|
||||
* file has a Metadata Item List (ilst) atom.
|
||||
*/
|
||||
bool hasMP4Tag() const;
|
||||
|
||||
/*!
|
||||
* 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(bool readProperties);
|
||||
|
||||
|
||||
@@ -33,7 +33,10 @@ using namespace TagLib;
|
||||
class MP4::Item::ItemPrivate : public RefCounter
|
||||
{
|
||||
public:
|
||||
ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {}
|
||||
ItemPrivate() :
|
||||
RefCounter(),
|
||||
valid(true),
|
||||
atomDataType(TypeUndefined) {}
|
||||
|
||||
bool valid;
|
||||
AtomDataType atomDataType;
|
||||
@@ -41,8 +44,8 @@ public:
|
||||
bool m_bool;
|
||||
int m_int;
|
||||
IntPair m_intPair;
|
||||
uchar m_byte;
|
||||
uint m_uint;
|
||||
unsigned char m_byte;
|
||||
unsigned int m_uint;
|
||||
long long m_longlong;
|
||||
};
|
||||
StringList m_stringList;
|
||||
@@ -50,13 +53,14 @@ public:
|
||||
MP4::CoverArtList m_coverArtList;
|
||||
};
|
||||
|
||||
MP4::Item::Item()
|
||||
MP4::Item::Item() :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->valid = false;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const Item &item) : d(item.d)
|
||||
MP4::Item::Item(const Item &item) :
|
||||
d(item.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
@@ -64,75 +68,76 @@ MP4::Item::Item(const Item &item) : d(item.d)
|
||||
MP4::Item &
|
||||
MP4::Item::operator=(const Item &item)
|
||||
{
|
||||
if(&item != this) {
|
||||
if(d->deref()) {
|
||||
delete d;
|
||||
}
|
||||
d = item.d;
|
||||
d->ref();
|
||||
}
|
||||
Item(item).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Item::swap(Item &item)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, item.d);
|
||||
}
|
||||
|
||||
MP4::Item::~Item()
|
||||
{
|
||||
if(d->deref()) {
|
||||
if(d->deref())
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
MP4::Item::Item(bool value)
|
||||
MP4::Item::Item(bool value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_bool = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(int value)
|
||||
MP4::Item::Item(int value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_int = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(uchar value)
|
||||
MP4::Item::Item(unsigned char value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_byte = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(uint value)
|
||||
MP4::Item::Item(unsigned int value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_uint = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(long long value)
|
||||
MP4::Item::Item(long long value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_longlong = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(int value1, int value2)
|
||||
MP4::Item::Item(int value1, int value2) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_intPair.first = value1;
|
||||
d->m_intPair.second = value2;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const ByteVectorList &value)
|
||||
MP4::Item::Item(const ByteVectorList &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_byteVectorList = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const StringList &value)
|
||||
MP4::Item::Item(const StringList &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_stringList = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const MP4::CoverArtList &value)
|
||||
MP4::Item::Item(const MP4::CoverArtList &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_coverArtList = value;
|
||||
}
|
||||
|
||||
@@ -158,13 +163,13 @@ MP4::Item::toInt() const
|
||||
return d->m_int;
|
||||
}
|
||||
|
||||
uchar
|
||||
unsigned char
|
||||
MP4::Item::toByte() const
|
||||
{
|
||||
return d->m_byte;
|
||||
}
|
||||
|
||||
TagLib::uint
|
||||
unsigned int
|
||||
MP4::Item::toUInt() const
|
||||
{
|
||||
return d->m_uint;
|
||||
@@ -205,4 +210,3 @@ MP4::Item::isValid() const
|
||||
{
|
||||
return d->valid;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,12 +43,22 @@ namespace TagLib {
|
||||
|
||||
Item();
|
||||
Item(const Item &item);
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a item into this Item.
|
||||
*/
|
||||
Item &operator=(const Item &item);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the Item by the content of \a item.
|
||||
*/
|
||||
void swap(Item &item);
|
||||
|
||||
~Item();
|
||||
|
||||
Item(int value);
|
||||
Item(uchar value);
|
||||
Item(uint value);
|
||||
Item(unsigned char value);
|
||||
Item(unsigned int value);
|
||||
Item(long long value);
|
||||
Item(bool value);
|
||||
Item(int first, int second);
|
||||
@@ -60,8 +70,8 @@ namespace TagLib {
|
||||
AtomDataType atomDataType() const;
|
||||
|
||||
int toInt() const;
|
||||
uchar toByte() const;
|
||||
uint toUInt() const;
|
||||
unsigned char toByte() const;
|
||||
unsigned int toUInt() const;
|
||||
long long toLongLong() const;
|
||||
bool toBool() const;
|
||||
IntPair toIntPair() const;
|
||||
|
||||
@@ -31,6 +31,27 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Calculate the total bytes used by audio data, used to calculate the bitrate
|
||||
long long calculateMdatLength(const MP4::AtomList &list)
|
||||
{
|
||||
long long totalLength = 0;
|
||||
for(MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) {
|
||||
long length = (*it)->length;
|
||||
if(length == 0)
|
||||
return 0; // for safety, see checkValid() in mp4file.cpp
|
||||
|
||||
if((*it)->name == "mdat")
|
||||
totalLength += length;
|
||||
|
||||
totalLength += calculateMdatLength((*it)->children);
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
}
|
||||
}
|
||||
|
||||
class MP4::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
@@ -167,7 +188,7 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
file->seek(mdhd->offset);
|
||||
data = file->readBlock(mdhd->length);
|
||||
|
||||
const uint version = data[8];
|
||||
const unsigned int version = data[8];
|
||||
long long unit;
|
||||
long long length;
|
||||
if(version == 1) {
|
||||
@@ -175,11 +196,11 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
unit = data.toLongLong(28U);
|
||||
length = data.toLongLong(36U);
|
||||
unit = data.toUInt(28U);
|
||||
length = data.toLongLong(32U);
|
||||
}
|
||||
else {
|
||||
if(data.size() < 24 + 4) {
|
||||
if(data.size() < 24 + 8) {
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
@@ -187,7 +208,7 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
length = data.toUInt(24U);
|
||||
}
|
||||
if(unit > 0 && length > 0)
|
||||
d->length = static_cast<int>(length * 1000.0 / unit);
|
||||
d->length = static_cast<int>(length * 1000.0 / unit + 0.5);
|
||||
|
||||
MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
|
||||
if(!atom) {
|
||||
@@ -202,7 +223,7 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
d->bitsPerSample = data.toShort(42U);
|
||||
d->sampleRate = data.toUInt(46U);
|
||||
if(data.containsAt("esds", 56) && data[64] == 0x03) {
|
||||
uint pos = 65;
|
||||
unsigned int pos = 65;
|
||||
if(data.containsAt("\x80\x80\x80", pos)) {
|
||||
pos += 3;
|
||||
}
|
||||
@@ -213,7 +234,14 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
pos += 3;
|
||||
}
|
||||
pos += 10;
|
||||
d->bitrate = static_cast<int>((data.toUInt(pos) + 500) / 1000.0 + 0.5);
|
||||
const unsigned int bitrateValue = data.toUInt(pos);
|
||||
if(bitrateValue != 0 || d->length <= 0) {
|
||||
d->bitrate = static_cast<int>((bitrateValue + 500) / 1000.0 + 0.5);
|
||||
}
|
||||
else {
|
||||
d->bitrate = static_cast<int>(
|
||||
(calculateMdatLength(atoms->atoms) * 8) / d->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,6 +252,13 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
d->channels = data.at(73);
|
||||
d->bitrate = static_cast<int>(data.toUInt(80U) / 1000.0 + 0.5);
|
||||
d->sampleRate = data.toUInt(84U);
|
||||
|
||||
if(d->bitrate == 0 && d->length > 0) {
|
||||
// There are files which do not contain a nominal bitrate, e.g. those
|
||||
// generated by refalac64.exe. Calculate the bitrate from the audio
|
||||
// data size (mdat atoms) and the duration.
|
||||
d->bitrate = (calculateMdatLength(atoms->atoms) * 8) / d->length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
|
||||
@@ -35,21 +35,23 @@ using namespace TagLib;
|
||||
class MP4::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate() : file(0), atoms(0) {}
|
||||
~TagPrivate() {}
|
||||
TagPrivate() :
|
||||
file(0),
|
||||
atoms(0) {}
|
||||
|
||||
TagLib::File *file;
|
||||
Atoms *atoms;
|
||||
ItemMap items;
|
||||
};
|
||||
|
||||
MP4::Tag::Tag()
|
||||
MP4::Tag::Tag() :
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
|
||||
MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
d->file = file;
|
||||
d->atoms = atoms;
|
||||
|
||||
@@ -59,8 +61,8 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < ilst->children.size(); i++) {
|
||||
MP4::Atom *atom = ilst->children[i];
|
||||
for(AtomList::ConstIterator it = ilst->children.begin(); it != ilst->children.end(); ++it) {
|
||||
MP4::Atom *atom = *it;
|
||||
file->seek(atom->offset + 8);
|
||||
if(atom->name == "----") {
|
||||
parseFreeForm(atom);
|
||||
@@ -69,14 +71,26 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
|
||||
parseIntPair(atom);
|
||||
}
|
||||
else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" ||
|
||||
atom->name == "hdvd") {
|
||||
atom->name == "hdvd" || atom->name == "shwm") {
|
||||
parseBool(atom);
|
||||
}
|
||||
else if(atom->name == "tmpo") {
|
||||
else if(atom->name == "tmpo" || atom->name == "\251mvi" || atom->name == "\251mvc") {
|
||||
parseInt(atom);
|
||||
}
|
||||
else if(atom->name == "rate") {
|
||||
AtomDataList data = parseData2(atom);
|
||||
if(!data.isEmpty()) {
|
||||
AtomData val = data[0];
|
||||
if (val.type == TypeUTF8) {
|
||||
addItem(atom->name, StringList(String(val.data, String::UTF8)));
|
||||
} else {
|
||||
addItem(atom->name, (int)(val.data.toShort()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" ||
|
||||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID") {
|
||||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID" ||
|
||||
atom->name == "cmID") {
|
||||
parseUInt(atom);
|
||||
}
|
||||
else if(atom->name == "plID") {
|
||||
@@ -91,6 +105,9 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
|
||||
else if(atom->name == "covr") {
|
||||
parseCovr(atom);
|
||||
}
|
||||
else if(atom->name == "purl" || atom->name == "egid") {
|
||||
parseText(atom, -1);
|
||||
}
|
||||
else {
|
||||
parseText(atom);
|
||||
}
|
||||
@@ -149,8 +166,8 @@ MP4::Tag::parseData(const MP4::Atom *atom, int expectedFlags, bool freeForm)
|
||||
{
|
||||
AtomDataList data = parseData2(atom, expectedFlags, freeForm);
|
||||
ByteVectorList result;
|
||||
for(uint i = 0; i < data.size(); i++) {
|
||||
result.append(data[i].data);
|
||||
for(AtomDataList::ConstIterator it = data.begin(); it != data.end(); ++it) {
|
||||
result.append(it->data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -159,7 +176,7 @@ void
|
||||
MP4::Tag::parseInt(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(data.size()) {
|
||||
if(!data.isEmpty()) {
|
||||
addItem(atom->name, (int)data[0].toShort());
|
||||
}
|
||||
}
|
||||
@@ -168,7 +185,7 @@ void
|
||||
MP4::Tag::parseUInt(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(data.size()) {
|
||||
if(!data.isEmpty()) {
|
||||
addItem(atom->name, data[0].toUInt());
|
||||
}
|
||||
}
|
||||
@@ -177,7 +194,7 @@ void
|
||||
MP4::Tag::parseLongLong(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(data.size()) {
|
||||
if(!data.isEmpty()) {
|
||||
addItem(atom->name, data[0].toLongLong());
|
||||
}
|
||||
}
|
||||
@@ -186,8 +203,8 @@ void
|
||||
MP4::Tag::parseByte(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(data.size()) {
|
||||
addItem(atom->name, (uchar)data[0].at(0));
|
||||
if(!data.isEmpty()) {
|
||||
addItem(atom->name, static_cast<unsigned char>(data[0].at(0)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +212,7 @@ void
|
||||
MP4::Tag::parseGnre(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(data.size()) {
|
||||
if(!data.isEmpty()) {
|
||||
int idx = (int)data[0].toShort();
|
||||
if(idx > 0) {
|
||||
addItem("\251gen", StringList(ID3v1::genre(idx - 1)));
|
||||
@@ -207,7 +224,7 @@ void
|
||||
MP4::Tag::parseIntPair(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(data.size()) {
|
||||
if(!data.isEmpty()) {
|
||||
const int a = data[0].toShort(2U);
|
||||
const int b = data[0].toShort(4U);
|
||||
addItem(atom->name, MP4::Item(a, b));
|
||||
@@ -218,7 +235,7 @@ void
|
||||
MP4::Tag::parseBool(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(data.size()) {
|
||||
if(!data.isEmpty()) {
|
||||
bool value = data[0].size() ? data[0][0] != '\0' : false;
|
||||
addItem(atom->name, value);
|
||||
}
|
||||
@@ -228,10 +245,10 @@ void
|
||||
MP4::Tag::parseText(const MP4::Atom *atom, int expectedFlags)
|
||||
{
|
||||
ByteVectorList data = parseData(atom, expectedFlags);
|
||||
if(data.size()) {
|
||||
if(!data.isEmpty()) {
|
||||
StringList value;
|
||||
for(unsigned int i = 0; i < data.size(); i++) {
|
||||
value.append(String(data[i], String::UTF8));
|
||||
for(ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) {
|
||||
value.append(String(*it, String::UTF8));
|
||||
}
|
||||
addItem(atom->name, value);
|
||||
}
|
||||
@@ -242,18 +259,25 @@ MP4::Tag::parseFreeForm(const MP4::Atom *atom)
|
||||
{
|
||||
AtomDataList data = parseData2(atom, -1, true);
|
||||
if(data.size() > 2) {
|
||||
String name = "----:" + String(data[0].data, String::UTF8) + ':' + String(data[1].data, String::UTF8);
|
||||
AtomDataType type = data[2].type;
|
||||
for(uint i = 2; i < data.size(); i++) {
|
||||
if(data[i].type != type) {
|
||||
AtomDataList::ConstIterator itBegin = data.begin();
|
||||
|
||||
String name = "----:";
|
||||
name += String((itBegin++)->data, String::UTF8); // data[0].data
|
||||
name += ':';
|
||||
name += String((itBegin++)->data, String::UTF8); // data[1].data
|
||||
|
||||
AtomDataType type = itBegin->type; // data[2].type
|
||||
|
||||
for(AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) {
|
||||
if(it->type != type) {
|
||||
debug("MP4: We currently don't support values with multiple types");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(type == TypeUTF8) {
|
||||
StringList value;
|
||||
for(uint i = 2; i < data.size(); i++) {
|
||||
value.append(String(data[i].data, String::UTF8));
|
||||
for(AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) {
|
||||
value.append(String(it->data, String::UTF8));
|
||||
}
|
||||
Item item(value);
|
||||
item.setAtomDataType(type);
|
||||
@@ -261,8 +285,8 @@ MP4::Tag::parseFreeForm(const MP4::Atom *atom)
|
||||
}
|
||||
else {
|
||||
ByteVectorList value;
|
||||
for(uint i = 2; i < data.size(); i++) {
|
||||
value.append(data[i].data);
|
||||
for(AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) {
|
||||
value.append(it->data);
|
||||
}
|
||||
Item item(value);
|
||||
item.setAtomDataType(type);
|
||||
@@ -281,7 +305,7 @@ MP4::Tag::parseCovr(const MP4::Atom *atom)
|
||||
const int length = static_cast<int>(data.toUInt(pos));
|
||||
if(length < 12) {
|
||||
debug("MP4: Too short atom");
|
||||
break;;
|
||||
break;
|
||||
}
|
||||
|
||||
const ByteVector name = data.mid(pos + 4, 4);
|
||||
@@ -300,7 +324,7 @@ MP4::Tag::parseCovr(const MP4::Atom *atom)
|
||||
}
|
||||
pos += length;
|
||||
}
|
||||
if(value.size() > 0)
|
||||
if(!value.isEmpty())
|
||||
addItem(atom->name, value);
|
||||
}
|
||||
|
||||
@@ -323,8 +347,8 @@ ByteVector
|
||||
MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data) const
|
||||
{
|
||||
ByteVector result;
|
||||
for(unsigned int i = 0; i < data.size(); i++) {
|
||||
result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + data[i]));
|
||||
for(ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) {
|
||||
result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + *it));
|
||||
}
|
||||
return renderAtom(name, result);
|
||||
}
|
||||
@@ -395,8 +419,8 @@ MP4::Tag::renderText(const ByteVector &name, const MP4::Item &item, int flags) c
|
||||
{
|
||||
ByteVectorList data;
|
||||
StringList value = item.toStringList();
|
||||
for(unsigned int i = 0; i < value.size(); i++) {
|
||||
data.append(value[i].data(String::UTF8));
|
||||
for(StringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
|
||||
data.append(it->data(String::UTF8));
|
||||
}
|
||||
return renderData(name, flags, data);
|
||||
}
|
||||
@@ -406,9 +430,9 @@ MP4::Tag::renderCovr(const ByteVector &name, const MP4::Item &item) const
|
||||
{
|
||||
ByteVector data;
|
||||
MP4::CoverArtList value = item.toCoverArtList();
|
||||
for(unsigned int i = 0; i < value.size(); i++) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(value[i].format()) +
|
||||
ByteVector(4, '\0') + value[i].data()));
|
||||
for(MP4::CoverArtList::ConstIterator it = value.begin(); it != value.end(); ++it) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(it->format()) +
|
||||
ByteVector(4, '\0') + it->data()));
|
||||
}
|
||||
return renderAtom(name, data);
|
||||
}
|
||||
@@ -417,9 +441,9 @@ ByteVector
|
||||
MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const
|
||||
{
|
||||
StringList header = StringList::split(name, ":");
|
||||
if (header.size() != 3) {
|
||||
if(header.size() != 3) {
|
||||
debug("MP4: Invalid free-form item name \"" + name + "\"");
|
||||
return ByteVector::null;
|
||||
return ByteVector();
|
||||
}
|
||||
ByteVector data;
|
||||
data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8)));
|
||||
@@ -435,14 +459,14 @@ MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const
|
||||
}
|
||||
if(type == TypeUTF8) {
|
||||
StringList value = item.toStringList();
|
||||
for(unsigned int i = 0; i < value.size(); i++) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value[i].data(String::UTF8)));
|
||||
for(StringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + it->data(String::UTF8)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
ByteVectorList value = item.toByteVectorList();
|
||||
for(unsigned int i = 0; i < value.size(); i++) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value[i]));
|
||||
for(ByteVectorList::ConstIterator it = value.begin(); it != value.end(); ++it) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + *it));
|
||||
}
|
||||
}
|
||||
return renderAtom("----", data);
|
||||
@@ -463,14 +487,26 @@ MP4::Tag::save()
|
||||
else if(name == "disk") {
|
||||
data.append(renderIntPairNoTrailing(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd") {
|
||||
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd" ||
|
||||
name == "shwm") {
|
||||
data.append(renderBool(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "tmpo") {
|
||||
else if(name == "tmpo" || name == "\251mvi" || name == "\251mvc") {
|
||||
data.append(renderInt(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if (name == "rate") {
|
||||
const MP4::Item& item = it->second;
|
||||
StringList value = item.toStringList();
|
||||
if (value.isEmpty()) {
|
||||
data.append(renderInt(name.data(String::Latin1), item));
|
||||
}
|
||||
else {
|
||||
data.append(renderText(name.data(String::Latin1), item));
|
||||
}
|
||||
}
|
||||
else if(name == "tvsn" || name == "tves" || name == "cnID" ||
|
||||
name == "sfID" || name == "atID" || name == "geID") {
|
||||
name == "sfID" || name == "atID" || name == "geID" ||
|
||||
name == "cmID") {
|
||||
data.append(renderUInt(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "plID") {
|
||||
@@ -482,6 +518,9 @@ MP4::Tag::save()
|
||||
else if(name == "covr") {
|
||||
data.append(renderCovr(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "purl" || name == "egid") {
|
||||
data.append(renderText(name.data(String::Latin1), it->second, TypeImplicit));
|
||||
}
|
||||
else if(name.size() == 4){
|
||||
data.append(renderText(name.data(String::Latin1), it->second));
|
||||
}
|
||||
@@ -505,20 +544,26 @@ MP4::Tag::save()
|
||||
void
|
||||
MP4::Tag::updateParents(const AtomList &path, long delta, int ignore)
|
||||
{
|
||||
for(unsigned int i = 0; i < path.size() - ignore; i++) {
|
||||
d->file->seek(path[i]->offset);
|
||||
if(static_cast<int>(path.size()) <= ignore)
|
||||
return;
|
||||
|
||||
AtomList::ConstIterator itEnd = path.end();
|
||||
std::advance(itEnd, 0 - ignore);
|
||||
|
||||
for(AtomList::ConstIterator it = path.begin(); it != itEnd; ++it) {
|
||||
d->file->seek((*it)->offset);
|
||||
long size = d->file->readBlock(4).toUInt();
|
||||
// 64-bit
|
||||
if (size == 1) {
|
||||
d->file->seek(4, File::Current); // Skip name
|
||||
long long longSize = d->file->readBlock(8).toLongLong();
|
||||
// Seek the offset of the 64-bit size
|
||||
d->file->seek(path[i]->offset + 8);
|
||||
d->file->seek((*it)->offset + 8);
|
||||
d->file->writeBlock(ByteVector::fromLongLong(longSize + delta));
|
||||
}
|
||||
// 32-bit
|
||||
else {
|
||||
d->file->seek(path[i]->offset);
|
||||
d->file->seek((*it)->offset);
|
||||
d->file->writeBlock(ByteVector::fromUInt(size + delta));
|
||||
}
|
||||
}
|
||||
@@ -530,8 +575,8 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
MP4::Atom *moov = d->atoms->find("moov");
|
||||
if(moov) {
|
||||
MP4::AtomList stco = moov->findall("stco", true);
|
||||
for(unsigned int i = 0; i < stco.size(); i++) {
|
||||
MP4::Atom *atom = stco[i];
|
||||
for(MP4::AtomList::ConstIterator it = stco.begin(); it != stco.end(); ++it) {
|
||||
MP4::Atom *atom = *it;
|
||||
if(atom->offset > offset) {
|
||||
atom->offset += delta;
|
||||
}
|
||||
@@ -539,7 +584,7 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
ByteVector data = d->file->readBlock(atom->length - 12);
|
||||
unsigned int count = data.toUInt();
|
||||
d->file->seek(atom->offset + 16);
|
||||
uint pos = 4;
|
||||
unsigned int pos = 4;
|
||||
while(count--) {
|
||||
long o = static_cast<long>(data.toUInt(pos));
|
||||
if(o > offset) {
|
||||
@@ -551,8 +596,8 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
}
|
||||
|
||||
MP4::AtomList co64 = moov->findall("co64", true);
|
||||
for(unsigned int i = 0; i < co64.size(); i++) {
|
||||
MP4::Atom *atom = co64[i];
|
||||
for(MP4::AtomList::ConstIterator it = co64.begin(); it != co64.end(); ++it) {
|
||||
MP4::Atom *atom = *it;
|
||||
if(atom->offset > offset) {
|
||||
atom->offset += delta;
|
||||
}
|
||||
@@ -560,7 +605,7 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
ByteVector data = d->file->readBlock(atom->length - 12);
|
||||
unsigned int count = data.toUInt();
|
||||
d->file->seek(atom->offset + 16);
|
||||
uint pos = 4;
|
||||
unsigned int pos = 4;
|
||||
while(count--) {
|
||||
long long o = data.toLongLong(pos);
|
||||
if(o > offset) {
|
||||
@@ -575,8 +620,8 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
MP4::Atom *moof = d->atoms->find("moof");
|
||||
if(moof) {
|
||||
MP4::AtomList tfhd = moof->findall("tfhd", true);
|
||||
for(unsigned int i = 0; i < tfhd.size(); i++) {
|
||||
MP4::Atom *atom = tfhd[i];
|
||||
for(MP4::AtomList::ConstIterator it = tfhd.begin(); it != tfhd.end(); ++it) {
|
||||
MP4::Atom *atom = *it;
|
||||
if(atom->offset > offset) {
|
||||
atom->offset += delta;
|
||||
}
|
||||
@@ -609,21 +654,28 @@ MP4::Tag::saveNew(ByteVector data)
|
||||
data = renderAtom("udta", data);
|
||||
}
|
||||
|
||||
long offset = path[path.size() - 1]->offset + 8;
|
||||
long offset = path.back()->offset + 8;
|
||||
d->file->insert(data, offset, 0);
|
||||
|
||||
updateParents(path, data.size());
|
||||
updateOffsets(data.size(), offset);
|
||||
|
||||
// Insert the newly created atoms into the tree to keep it up-to-date.
|
||||
|
||||
d->file->seek(offset);
|
||||
path.back()->children.prepend(new Atom(d->file));
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
|
||||
{
|
||||
MP4::Atom *ilst = path[path.size() - 1];
|
||||
AtomList::ConstIterator it = path.end();
|
||||
|
||||
MP4::Atom *ilst = *(--it);
|
||||
long offset = ilst->offset;
|
||||
long length = ilst->length;
|
||||
|
||||
MP4::Atom *meta = path[path.size() - 2];
|
||||
MP4::Atom *meta = *(--it);
|
||||
AtomList::ConstIterator index = meta->children.find(ilst);
|
||||
|
||||
// check if there is an atom before 'ilst', and possibly use it as padding
|
||||
@@ -669,7 +721,7 @@ MP4::Tag::title() const
|
||||
{
|
||||
if(d->items.contains("\251nam"))
|
||||
return d->items["\251nam"].toStringList().toString(", ");
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String
|
||||
@@ -677,7 +729,7 @@ MP4::Tag::artist() const
|
||||
{
|
||||
if(d->items.contains("\251ART"))
|
||||
return d->items["\251ART"].toStringList().toString(", ");
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String
|
||||
@@ -685,7 +737,7 @@ MP4::Tag::album() const
|
||||
{
|
||||
if(d->items.contains("\251alb"))
|
||||
return d->items["\251alb"].toStringList().toString(", ");
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String
|
||||
@@ -693,7 +745,7 @@ MP4::Tag::comment() const
|
||||
{
|
||||
if(d->items.contains("\251cmt"))
|
||||
return d->items["\251cmt"].toStringList().toString(", ");
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String
|
||||
@@ -701,7 +753,7 @@ MP4::Tag::genre() const
|
||||
{
|
||||
if(d->items.contains("\251gen"))
|
||||
return d->items["\251gen"].toStringList().toString(", ");
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
unsigned int
|
||||
@@ -723,43 +775,63 @@ MP4::Tag::track() const
|
||||
void
|
||||
MP4::Tag::setTitle(const String &value)
|
||||
{
|
||||
d->items["\251nam"] = StringList(value);
|
||||
setTextItem("\251nam", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setArtist(const String &value)
|
||||
{
|
||||
d->items["\251ART"] = StringList(value);
|
||||
setTextItem("\251ART", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setAlbum(const String &value)
|
||||
{
|
||||
d->items["\251alb"] = StringList(value);
|
||||
setTextItem("\251alb", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setComment(const String &value)
|
||||
{
|
||||
d->items["\251cmt"] = StringList(value);
|
||||
setTextItem("\251cmt", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setGenre(const String &value)
|
||||
{
|
||||
d->items["\251gen"] = StringList(value);
|
||||
setTextItem("\251gen", value);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setYear(uint value)
|
||||
MP4::Tag::setTextItem(const String &key, const String &value)
|
||||
{
|
||||
d->items["\251day"] = StringList(String::number(value));
|
||||
if (!value.isEmpty()) {
|
||||
d->items[key] = StringList(value);
|
||||
} else {
|
||||
d->items.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setTrack(uint value)
|
||||
MP4::Tag::setYear(unsigned int value)
|
||||
{
|
||||
d->items["trkn"] = MP4::Item(value, 0);
|
||||
if (value == 0) {
|
||||
d->items.erase("\251day");
|
||||
}
|
||||
else {
|
||||
d->items["\251day"] = StringList(String::number(value));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setTrack(unsigned int value)
|
||||
{
|
||||
if (value == 0) {
|
||||
d->items.erase("trkn");
|
||||
}
|
||||
else {
|
||||
d->items["trkn"] = MP4::Item(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool MP4::Tag::isEmpty() const
|
||||
@@ -797,71 +869,98 @@ bool MP4::Tag::contains(const String &key) const
|
||||
return d->items.contains(key);
|
||||
}
|
||||
|
||||
static const char *keyTranslation[][2] = {
|
||||
{ "\251nam", "TITLE" },
|
||||
{ "\251ART", "ARTIST" },
|
||||
{ "\251alb", "ALBUM" },
|
||||
{ "\251cmt", "COMMENT" },
|
||||
{ "\251gen", "GENRE" },
|
||||
{ "\251day", "DATE" },
|
||||
{ "\251wrt", "COMPOSER" },
|
||||
{ "\251grp", "GROUPING" },
|
||||
{ "aART", "ALBUMARTIST" },
|
||||
{ "trkn", "TRACKNUMBER" },
|
||||
{ "disk", "DISCNUMBER" },
|
||||
{ "cpil", "COMPILATION" },
|
||||
{ "tmpo", "BPM" },
|
||||
{ "cprt", "COPYRIGHT" },
|
||||
{ "\251lyr", "LYRICS" },
|
||||
{ "\251too", "ENCODEDBY" },
|
||||
{ "soal", "ALBUMSORT" },
|
||||
{ "soaa", "ALBUMARTISTSORT" },
|
||||
{ "soar", "ARTISTSORT" },
|
||||
{ "sonm", "TITLESORT" },
|
||||
{ "soco", "COMPOSERSORT" },
|
||||
{ "sosn", "SHOWSORT" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "----:com.apple.iTunes:ASIN", "ASIN" },
|
||||
{ "----:com.apple.iTunes:LABEL", "LABEL" },
|
||||
{ "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
|
||||
{ "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" },
|
||||
{ "----:com.apple.iTunes:REMIXER", "REMIXER" },
|
||||
{ "----:com.apple.iTunes:ENGINEER", "ENGINEER" },
|
||||
{ "----:com.apple.iTunes:PRODUCER", "PRODUCER" },
|
||||
{ "----:com.apple.iTunes:DJMIXER", "DJMIXER" },
|
||||
{ "----:com.apple.iTunes:MIXER", "MIXER" },
|
||||
{ "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" },
|
||||
{ "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" },
|
||||
{ "----:com.apple.iTunes:MOOD", "MOOD" },
|
||||
{ "----:com.apple.iTunes:ISRC", "ISRC" },
|
||||
{ "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" },
|
||||
{ "----:com.apple.iTunes:BARCODE", "BARCODE" },
|
||||
{ "----:com.apple.iTunes:SCRIPT", "SCRIPT" },
|
||||
{ "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" },
|
||||
{ "----:com.apple.iTunes:LICENSE", "LICENSE" },
|
||||
{ "----:com.apple.iTunes:MEDIA", "MEDIA" },
|
||||
};
|
||||
namespace
|
||||
{
|
||||
const char *keyTranslation[][2] = {
|
||||
{ "\251nam", "TITLE" },
|
||||
{ "\251ART", "ARTIST" },
|
||||
{ "\251alb", "ALBUM" },
|
||||
{ "\251cmt", "COMMENT" },
|
||||
{ "\251gen", "GENRE" },
|
||||
{ "\251day", "DATE" },
|
||||
{ "\251wrt", "COMPOSER" },
|
||||
{ "\251grp", "GROUPING" },
|
||||
{ "aART", "ALBUMARTIST" },
|
||||
{ "trkn", "TRACKNUMBER" },
|
||||
{ "disk", "DISCNUMBER" },
|
||||
{ "cpil", "COMPILATION" },
|
||||
{ "tmpo", "BPM" },
|
||||
{ "cprt", "COPYRIGHT" },
|
||||
{ "\251lyr", "LYRICS" },
|
||||
{ "\251too", "ENCODEDBY" },
|
||||
{ "soal", "ALBUMSORT" },
|
||||
{ "soaa", "ALBUMARTISTSORT" },
|
||||
{ "soar", "ARTISTSORT" },
|
||||
{ "sonm", "TITLESORT" },
|
||||
{ "soco", "COMPOSERSORT" },
|
||||
{ "sosn", "SHOWSORT" },
|
||||
{ "shwm", "SHOWWORKMOVEMENT" },
|
||||
{ "pgap", "GAPLESSPLAYBACK" },
|
||||
{ "pcst", "PODCAST" },
|
||||
{ "catg", "PODCASTCATEGORY" },
|
||||
{ "desc", "PODCASTDESC" },
|
||||
{ "egid", "PODCASTID" },
|
||||
{ "purl", "PODCASTURL" },
|
||||
{ "tves", "TVEPISODE" },
|
||||
{ "tven", "TVEPISODEID" },
|
||||
{ "tvnn", "TVNETWORK" },
|
||||
{ "tvsn", "TVSEASON" },
|
||||
{ "tvsh", "TVSHOW" },
|
||||
{ "\251wrk", "WORK" },
|
||||
{ "\251mvn", "MOVEMENTNAME" },
|
||||
{ "\251mvi", "MOVEMENTNUMBER" },
|
||||
{ "\251mvc", "MOVEMENTCOUNT" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Release Track Id", "MUSICBRAINZ_RELEASETRACKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Release Country", "RELEASECOUNTRY" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Status", "RELEASESTATUS" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Type", "RELEASETYPE" },
|
||||
{ "----:com.apple.iTunes:ARTISTS", "ARTISTS" },
|
||||
{ "----:com.apple.iTunes:originaldate", "ORIGINALDATE" },
|
||||
{ "----:com.apple.iTunes:ASIN", "ASIN" },
|
||||
{ "----:com.apple.iTunes:LABEL", "LABEL" },
|
||||
{ "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
|
||||
{ "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" },
|
||||
{ "----:com.apple.iTunes:REMIXER", "REMIXER" },
|
||||
{ "----:com.apple.iTunes:ENGINEER", "ENGINEER" },
|
||||
{ "----:com.apple.iTunes:PRODUCER", "PRODUCER" },
|
||||
{ "----:com.apple.iTunes:DJMIXER", "DJMIXER" },
|
||||
{ "----:com.apple.iTunes:MIXER", "MIXER" },
|
||||
{ "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" },
|
||||
{ "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" },
|
||||
{ "----:com.apple.iTunes:MOOD", "MOOD" },
|
||||
{ "----:com.apple.iTunes:ISRC", "ISRC" },
|
||||
{ "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" },
|
||||
{ "----:com.apple.iTunes:BARCODE", "BARCODE" },
|
||||
{ "----:com.apple.iTunes:SCRIPT", "SCRIPT" },
|
||||
{ "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" },
|
||||
{ "----:com.apple.iTunes:LICENSE", "LICENSE" },
|
||||
{ "----:com.apple.iTunes:MEDIA", "MEDIA" },
|
||||
};
|
||||
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 MP4::Tag::properties() const
|
||||
{
|
||||
static Map<String, String> keyMap;
|
||||
if(keyMap.isEmpty()) {
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap props;
|
||||
MP4::ItemMap::ConstIterator it = d->items.begin();
|
||||
for(; it != d->items.end(); ++it) {
|
||||
if(keyMap.contains(it->first)) {
|
||||
String key = keyMap[it->first];
|
||||
for(MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) {
|
||||
const String key = translateKey(it->first);
|
||||
if(!key.isEmpty()) {
|
||||
if(key == "TRACKNUMBER" || key == "DISCNUMBER") {
|
||||
MP4::Item::IntPair ip = it->second.toIntPair();
|
||||
String value = String::number(ip.first);
|
||||
@@ -870,10 +969,12 @@ PropertyMap MP4::Tag::properties() const
|
||||
}
|
||||
props[key] = value;
|
||||
}
|
||||
else if(key == "BPM") {
|
||||
else if(key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT" ||
|
||||
key == "TVEPISODE" || key == "TVSEASON") {
|
||||
props[key] = String::number(it->second.toInt());
|
||||
}
|
||||
else if(key == "COMPILATION") {
|
||||
else if(key == "COMPILATION" || key == "SHOWWORKMOVEMENT" ||
|
||||
key == "GAPLESSPLAYBACK" || key == "PODCAST") {
|
||||
props[key] = String::number(it->second.toBool());
|
||||
}
|
||||
else {
|
||||
@@ -889,8 +990,7 @@ PropertyMap MP4::Tag::properties() const
|
||||
|
||||
void MP4::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
{
|
||||
StringList::ConstIterator it = props.begin();
|
||||
for(; it != props.end(); ++it)
|
||||
for(StringList::ConstIterator it = props.begin(); it != props.end(); ++it)
|
||||
d->items.erase(*it);
|
||||
}
|
||||
|
||||
@@ -905,34 +1005,36 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
|
||||
}
|
||||
|
||||
PropertyMap origProps = properties();
|
||||
PropertyMap::ConstIterator it = origProps.begin();
|
||||
for(; it != origProps.end(); ++it) {
|
||||
for(PropertyMap::ConstIterator it = origProps.begin(); it != origProps.end(); ++it) {
|
||||
if(!props.contains(it->first) || props[it->first].isEmpty()) {
|
||||
d->items.erase(reverseKeyMap[it->first]);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap ignoredProps;
|
||||
it = props.begin();
|
||||
for(; it != props.end(); ++it) {
|
||||
for(PropertyMap::ConstIterator it = props.begin(); it != props.end(); ++it) {
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
if((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) {
|
||||
int first = 0, second = 0;
|
||||
StringList parts = StringList::split(it->second.front(), "/");
|
||||
if(parts.size() > 0) {
|
||||
first = parts[0].toInt();
|
||||
if(!parts.isEmpty()) {
|
||||
int first = parts[0].toInt();
|
||||
int second = 0;
|
||||
if(parts.size() > 1) {
|
||||
second = parts[1].toInt();
|
||||
}
|
||||
d->items[name] = MP4::Item(first, second);
|
||||
}
|
||||
}
|
||||
else if(it->first == "BPM" && !it->second.isEmpty()) {
|
||||
else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" ||
|
||||
it->first == "MOVEMENTCOUNT" || it->first == "TVEPISODE" ||
|
||||
it->first == "TVSEASON") && !it->second.isEmpty()) {
|
||||
int value = it->second.front().toInt();
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
else if(it->first == "COMPILATION" && !it->second.isEmpty()) {
|
||||
else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT" ||
|
||||
it->first == "GAPLESSPLAYBACK" || it->first == "PODCAST") &&
|
||||
!it->second.isEmpty()) {
|
||||
bool value = (it->second.front().toInt() != 0);
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* \deprecated
|
||||
*/
|
||||
typedef TagLib::Map<String, Item> ItemListMap;
|
||||
TAGLIB_DEPRECATED typedef TagLib::Map<String, Item> ItemListMap;
|
||||
typedef TagLib::Map<String, Item> ItemMap;
|
||||
|
||||
class TAGLIB_EXPORT Tag: public TagLib::Tag
|
||||
@@ -50,31 +50,31 @@ namespace TagLib {
|
||||
public:
|
||||
Tag();
|
||||
Tag(TagLib::File *file, Atoms *atoms);
|
||||
~Tag();
|
||||
virtual ~Tag();
|
||||
bool save();
|
||||
|
||||
String title() const;
|
||||
String artist() const;
|
||||
String album() const;
|
||||
String comment() const;
|
||||
String genre() const;
|
||||
uint year() const;
|
||||
uint track() const;
|
||||
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;
|
||||
|
||||
void setTitle(const String &value);
|
||||
void setArtist(const String &value);
|
||||
void setAlbum(const String &value);
|
||||
void setComment(const String &value);
|
||||
void setGenre(const String &value);
|
||||
void setYear(uint value);
|
||||
void setTrack(uint value);
|
||||
virtual void setTitle(const String &value);
|
||||
virtual void setArtist(const String &value);
|
||||
virtual void setAlbum(const String &value);
|
||||
virtual void setComment(const String &value);
|
||||
virtual void setGenre(const String &value);
|
||||
virtual void setYear(unsigned int value);
|
||||
virtual void setTrack(unsigned int value);
|
||||
|
||||
virtual bool isEmpty() const;
|
||||
|
||||
/*!
|
||||
* \deprecated Use the item() and setItem() API instead
|
||||
*/
|
||||
ItemMap &itemListMap();
|
||||
TAGLIB_DEPRECATED ItemMap &itemListMap();
|
||||
|
||||
/*!
|
||||
* Returns a string-keyed map of the MP4::Items for this tag.
|
||||
@@ -106,6 +106,13 @@ namespace TagLib {
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Sets the value of \a key to \a value, overwriting any previous value.
|
||||
* If \a value is empty, the item is removed.
|
||||
*/
|
||||
void setTextItem(const String &key, const String &value);
|
||||
|
||||
private:
|
||||
AtomDataList parseData2(const Atom *atom, int expectedFlags = -1,
|
||||
bool freeForm = false);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <tagunion.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "mpcfile.h"
|
||||
#include "id3v1tag.h"
|
||||
@@ -52,10 +53,7 @@ public:
|
||||
ID3v2Header(0),
|
||||
ID3v2Location(-1),
|
||||
ID3v2Size(0),
|
||||
properties(0),
|
||||
hasAPE(false),
|
||||
hasID3v1(false),
|
||||
hasID3v2(false) {}
|
||||
properties(0) {}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
@@ -64,26 +62,32 @@ public:
|
||||
}
|
||||
|
||||
long APELocation;
|
||||
uint APESize;
|
||||
long APESize;
|
||||
|
||||
long ID3v1Location;
|
||||
|
||||
ID3v2::Header *ID3v2Header;
|
||||
long ID3v2Location;
|
||||
uint ID3v2Size;
|
||||
long ID3v2Size;
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
|
||||
// These indicate whether the file *on disk* has these tags, not if
|
||||
// this data structure does. This is used in computing offsets.
|
||||
|
||||
bool hasAPE;
|
||||
bool hasID3v1;
|
||||
bool hasID3v2;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool MPC::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A newer MPC file has to start with "MPCK" or "MP+", but older files don't
|
||||
// have keys to do a quick check.
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return (id == "MPCK" || id.startsWith("MP+"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -116,26 +120,20 @@ TagLib::Tag *MPC::File::tag() const
|
||||
|
||||
PropertyMap MPC::File::properties() const
|
||||
{
|
||||
if(d->hasAPE)
|
||||
return d->tag.access<APE::Tag>(MPCAPEIndex, false)->properties();
|
||||
if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->properties();
|
||||
return PropertyMap();
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void MPC::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
if(d->hasAPE)
|
||||
d->tag.access<APE::Tag>(MPCAPEIndex, false)->removeUnsupportedProperties(properties);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->removeUnsupportedProperties(properties);
|
||||
d->tag.removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap MPC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<APE::Tag>(MPCID3v1Index, false)->setProperties(properties);
|
||||
return d->tag.access<APE::Tag>(MPCAPEIndex, true)->setProperties(properties);
|
||||
if(ID3v1Tag())
|
||||
ID3v1Tag()->setProperties(properties);
|
||||
|
||||
return APETag(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
MPC::Properties *MPC::File::audioProperties() const
|
||||
@@ -152,69 +150,80 @@ bool MPC::File::save()
|
||||
|
||||
// Possibly strip ID3v2 tag
|
||||
|
||||
if(d->hasID3v2 && !d->ID3v2Header) {
|
||||
if(!d->ID3v2Header && d->ID3v2Location >= 0) {
|
||||
removeBlock(d->ID3v2Location, d->ID3v2Size);
|
||||
d->hasID3v2 = false;
|
||||
if(d->hasID3v1)
|
||||
d->ID3v1Location -= d->ID3v2Size;
|
||||
if(d->hasAPE)
|
||||
|
||||
if(d->APELocation >= 0)
|
||||
d->APELocation -= d->ID3v2Size;
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location -= d->ID3v2Size;
|
||||
|
||||
d->ID3v2Location = -1;
|
||||
d->ID3v2Size = 0;
|
||||
}
|
||||
|
||||
// Update ID3v1 tag
|
||||
|
||||
if(ID3v1Tag()) {
|
||||
if(d->hasID3v1) {
|
||||
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);
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->ID3v1Location = tell();
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
} else
|
||||
if(d->hasID3v1) {
|
||||
removeBlock(d->ID3v1Location, 128);
|
||||
d->hasID3v1 = false;
|
||||
if(d->hasAPE) {
|
||||
if(d->APELocation > d->ID3v1Location)
|
||||
d->APELocation -= 128;
|
||||
}
|
||||
|
||||
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()) {
|
||||
if(d->hasAPE)
|
||||
insert(APETag()->render(), d->APELocation, d->APESize);
|
||||
else {
|
||||
if(d->hasID3v1) {
|
||||
insert(APETag()->render(), d->ID3v1Location, 0);
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->hasAPE = true;
|
||||
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;
|
||||
d->ID3v1Location += d->APESize;
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->APELocation = tell();
|
||||
writeBlock(APETag()->render());
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->hasAPE = true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
if(d->hasAPE) {
|
||||
removeBlock(d->APELocation, d->APESize);
|
||||
d->hasAPE = false;
|
||||
if(d->hasID3v1) {
|
||||
if(d->ID3v1Location > d->APELocation)
|
||||
d->ID3v1Location -= d->APESize;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -231,22 +240,19 @@ APE::Tag *MPC::File::APETag(bool create)
|
||||
|
||||
void MPC::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1) {
|
||||
if(tags & ID3v1)
|
||||
d->tag.set(MPCID3v1Index, 0);
|
||||
|
||||
if(tags & APE)
|
||||
d->tag.set(MPCAPEIndex, 0);
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
}
|
||||
|
||||
if(tags & ID3v2) {
|
||||
delete d->ID3v2Header;
|
||||
d->ID3v2Header = 0;
|
||||
}
|
||||
|
||||
if(tags & APE) {
|
||||
d->tag.set(MPCAPEIndex, 0);
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
}
|
||||
}
|
||||
|
||||
void MPC::File::remove(int tags)
|
||||
@@ -256,12 +262,12 @@ void MPC::File::remove(int tags)
|
||||
|
||||
bool MPC::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
return (d->ID3v1Location >= 0);
|
||||
}
|
||||
|
||||
bool MPC::File::hasAPETag() const
|
||||
{
|
||||
return d->hasAPE;
|
||||
return (d->APELocation >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -270,55 +276,50 @@ bool MPC::File::hasAPETag() const
|
||||
|
||||
void MPC::File::read(bool readProperties)
|
||||
{
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = findID3v1();
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
d->tag.set(MPCID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
// Look for an APE tag
|
||||
|
||||
d->APELocation = findAPE();
|
||||
|
||||
if(d->APELocation >= 0) {
|
||||
d->tag.set(MPCAPEIndex, new APE::Tag(this, d->APELocation));
|
||||
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
|
||||
d->hasAPE = true;
|
||||
}
|
||||
|
||||
if(!d->hasID3v1)
|
||||
APETag(true);
|
||||
|
||||
// Look for an ID3v2 tag
|
||||
|
||||
d->ID3v2Location = findID3v2();
|
||||
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();
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = Utils::findID3v1(this);
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->tag.set(MPCID3v1Index, 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(MPCAPEIndex, 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 MPC metadata
|
||||
|
||||
if(readProperties) {
|
||||
|
||||
long streamLength;
|
||||
|
||||
if(d->hasAPE)
|
||||
if(d->APELocation >= 0)
|
||||
streamLength = d->APELocation;
|
||||
else if(d->hasID3v1)
|
||||
else if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location;
|
||||
else
|
||||
streamLength = length();
|
||||
|
||||
if(d->hasID3v2) {
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location + d->ID3v2Size);
|
||||
streamLength -= (d->ID3v2Location + d->ID3v2Size);
|
||||
}
|
||||
@@ -329,48 +330,3 @@ void MPC::File::read(bool readProperties)
|
||||
d->properties = new Properties(this, streamLength);
|
||||
}
|
||||
}
|
||||
|
||||
long MPC::File::findAPE()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
if(d->hasID3v1)
|
||||
seek(-160, End);
|
||||
else
|
||||
seek(-32, End);
|
||||
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(8) == APE::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long MPC::File::findID3v1()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(-128, End);
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long MPC::File::findID3v2()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(0);
|
||||
|
||||
if(readBlock(3) == ID3v2::Header::fileIdentifier())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -141,9 +141,6 @@ namespace TagLib {
|
||||
* Saves the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*
|
||||
* \warning In the current implementation, it's dangerous to call save()
|
||||
* repeatedly. At worst it will corrupt the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
@@ -201,7 +198,7 @@ namespace TagLib {
|
||||
* \deprecated
|
||||
* \see strip
|
||||
*/
|
||||
void remove(int tags = AllTags);
|
||||
TAGLIB_DEPRECATED void remove(int tags = AllTags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
||||
@@ -217,14 +214,20 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasAPETag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an MPC
|
||||
* 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);
|
||||
long findAPE();
|
||||
long findID3v1();
|
||||
long findID3v2();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
|
||||
@@ -54,13 +54,12 @@ public:
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
uint totalFrames;
|
||||
uint sampleFrames;
|
||||
uint trackGain;
|
||||
uint trackPeak;
|
||||
uint albumGain;
|
||||
uint albumPeak;
|
||||
String flags;
|
||||
unsigned int totalFrames;
|
||||
unsigned int sampleFrames;
|
||||
int trackGain;
|
||||
int trackPeak;
|
||||
int albumGain;
|
||||
int albumPeak;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -129,12 +128,12 @@ int MPC::Properties::mpcVersion() const
|
||||
return d->version;
|
||||
}
|
||||
|
||||
TagLib::uint MPC::Properties::totalFrames() const
|
||||
unsigned int MPC::Properties::totalFrames() const
|
||||
{
|
||||
return d->totalFrames;
|
||||
}
|
||||
|
||||
TagLib::uint MPC::Properties::sampleFrames() const
|
||||
unsigned int MPC::Properties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
@@ -163,42 +162,42 @@ int MPC::Properties::albumPeak() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsigned long readSize(File *file, TagLib::uint &sizeLength, bool &eof)
|
||||
{
|
||||
sizeLength = 0;
|
||||
eof = false;
|
||||
|
||||
unsigned char tmp;
|
||||
unsigned long size = 0;
|
||||
|
||||
do {
|
||||
const ByteVector b = file->readBlock(1);
|
||||
if(b.isEmpty()) {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = b[0];
|
||||
size = (size << 7) | (tmp & 0x7F);
|
||||
sizeLength++;
|
||||
} while((tmp & 0x80));
|
||||
return size;
|
||||
}
|
||||
|
||||
unsigned long readSize(const ByteVector &data, TagLib::uint &pos)
|
||||
{
|
||||
unsigned char tmp;
|
||||
unsigned long size = 0;
|
||||
|
||||
do {
|
||||
tmp = data[pos++];
|
||||
size = (size << 7) | (tmp & 0x7F);
|
||||
} while((tmp & 0x80) && (pos < data.size()));
|
||||
return size;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
unsigned long readSize(File *file, unsigned int &sizeLength, bool &eof)
|
||||
{
|
||||
sizeLength = 0;
|
||||
eof = false;
|
||||
|
||||
unsigned char tmp;
|
||||
unsigned long size = 0;
|
||||
|
||||
do {
|
||||
const ByteVector b = file->readBlock(1);
|
||||
if(b.isEmpty()) {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = b[0];
|
||||
size = (size << 7) | (tmp & 0x7F);
|
||||
sizeLength++;
|
||||
} while((tmp & 0x80));
|
||||
return size;
|
||||
}
|
||||
|
||||
unsigned long readSize(const ByteVector &data, unsigned int &pos)
|
||||
{
|
||||
unsigned char tmp;
|
||||
unsigned long size = 0;
|
||||
|
||||
do {
|
||||
tmp = data[pos++];
|
||||
size = (size << 7) | (tmp & 0x7F);
|
||||
} while((tmp & 0x80) && (pos < data.size()));
|
||||
return size;
|
||||
}
|
||||
|
||||
// This array looks weird, but the same as original MusePack code found at:
|
||||
// https://www.musepack.net/index.php?pg=src
|
||||
const unsigned short sftable [8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 };
|
||||
@@ -211,7 +210,7 @@ void MPC::Properties::readSV8(File *file, long streamLength)
|
||||
while(!readSH && !readRG) {
|
||||
const ByteVector packetType = file->readBlock(2);
|
||||
|
||||
uint packetSizeLength;
|
||||
unsigned int packetSizeLength;
|
||||
bool eof;
|
||||
const unsigned long packetSize = readSize(file, packetSizeLength, eof);
|
||||
if(eof) {
|
||||
@@ -238,7 +237,7 @@ void MPC::Properties::readSV8(File *file, long streamLength)
|
||||
|
||||
readSH = true;
|
||||
|
||||
TagLib::uint pos = 4;
|
||||
unsigned int pos = 4;
|
||||
d->version = data[pos];
|
||||
pos += 1;
|
||||
d->sampleFrames = readSize(data, pos);
|
||||
@@ -247,19 +246,19 @@ void MPC::Properties::readSV8(File *file, long streamLength)
|
||||
break;
|
||||
}
|
||||
|
||||
const ulong begSilence = readSize(data, pos);
|
||||
const unsigned long begSilence = readSize(data, pos);
|
||||
if(pos > dataSize - 2) {
|
||||
debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
|
||||
break;
|
||||
}
|
||||
|
||||
const ushort flags = data.toUShort(pos, true);
|
||||
const unsigned short flags = data.toUShort(pos, true);
|
||||
pos += 2;
|
||||
|
||||
d->sampleRate = sftable[(flags >> 13) & 0x07];
|
||||
d->channels = ((flags >> 4) & 0x0F) + 1;
|
||||
|
||||
const uint frameCount = d->sampleFrames - begSilence;
|
||||
const unsigned int frameCount = d->sampleFrames - begSilence;
|
||||
if(frameCount > 0 && d->sampleRate > 0) {
|
||||
const double length = frameCount * 1000.0 / d->sampleRate;
|
||||
d->length = static_cast<int>(length + 0.5);
|
||||
@@ -305,16 +304,16 @@ void MPC::Properties::readSV7(const ByteVector &data, long streamLength)
|
||||
|
||||
d->totalFrames = data.toUInt(4, false);
|
||||
|
||||
const uint flags = data.toUInt(8, false);
|
||||
const unsigned int flags = data.toUInt(8, false);
|
||||
d->sampleRate = sftable[(flags >> 16) & 0x03];
|
||||
d->channels = 2;
|
||||
|
||||
const uint gapless = data.toUInt(5, false);
|
||||
const unsigned int gapless = data.toUInt(5, false);
|
||||
|
||||
d->trackGain = data.toShort(14, false);
|
||||
d->trackPeak = data.toShort(12, false);
|
||||
d->trackPeak = data.toUShort(12, false);
|
||||
d->albumGain = data.toShort(18, false);
|
||||
d->albumPeak = data.toShort(16, false);
|
||||
d->albumPeak = data.toUShort(16, false);
|
||||
|
||||
// convert gain info
|
||||
if(d->trackGain != 0) {
|
||||
@@ -337,14 +336,14 @@ void MPC::Properties::readSV7(const ByteVector &data, long streamLength)
|
||||
|
||||
bool trueGapless = (gapless >> 31) & 0x0001;
|
||||
if(trueGapless) {
|
||||
uint lastFrameSamples = (gapless >> 20) & 0x07FF;
|
||||
unsigned int lastFrameSamples = (gapless >> 20) & 0x07FF;
|
||||
d->sampleFrames = d->totalFrames * 1152 - lastFrameSamples;
|
||||
}
|
||||
else
|
||||
d->sampleFrames = d->totalFrames * 1152 - 576;
|
||||
}
|
||||
else {
|
||||
const uint headerData = data.toUInt(0, false);
|
||||
const unsigned int headerData = data.toUInt(0, false);
|
||||
|
||||
d->bitrate = (headerData >> 23) & 0x01ff;
|
||||
d->version = (headerData >> 11) & 0x03ff;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace TagLib {
|
||||
|
||||
class File;
|
||||
|
||||
static const uint HeaderSize = 8*7;
|
||||
static const unsigned int HeaderSize = 8 * 7;
|
||||
|
||||
//! An implementation of audio property reading for MPC
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace TagLib {
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
TAGLIB_DEPRECATED virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -113,8 +113,8 @@ namespace TagLib {
|
||||
*/
|
||||
int mpcVersion() const;
|
||||
|
||||
uint totalFrames() const;
|
||||
uint sampleFrames() const;
|
||||
unsigned int totalFrames() const;
|
||||
unsigned int sampleFrames() const;
|
||||
|
||||
/*!
|
||||
* Returns the track gain as an integer value,
|
||||
|
||||
@@ -27,237 +27,260 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace TagLib {
|
||||
namespace ID3v1 {
|
||||
|
||||
static const int genresSize = 192;
|
||||
static const String genres[] = {
|
||||
"Blues",
|
||||
"Classic Rock",
|
||||
"Country",
|
||||
"Dance",
|
||||
"Disco",
|
||||
"Funk",
|
||||
"Grunge",
|
||||
"Hip-Hop",
|
||||
"Jazz",
|
||||
"Metal",
|
||||
"New Age",
|
||||
"Oldies",
|
||||
"Other",
|
||||
"Pop",
|
||||
"R&B",
|
||||
"Rap",
|
||||
"Reggae",
|
||||
"Rock",
|
||||
"Techno",
|
||||
"Industrial",
|
||||
"Alternative",
|
||||
"Ska",
|
||||
"Death Metal",
|
||||
"Pranks",
|
||||
"Soundtrack",
|
||||
"Euro-Techno",
|
||||
"Ambient",
|
||||
"Trip-Hop",
|
||||
"Vocal",
|
||||
"Jazz+Funk",
|
||||
"Fusion",
|
||||
"Trance",
|
||||
"Classical",
|
||||
"Instrumental",
|
||||
"Acid",
|
||||
"House",
|
||||
"Game",
|
||||
"Sound Clip",
|
||||
"Gospel",
|
||||
"Noise",
|
||||
"Alternative Rock",
|
||||
"Bass",
|
||||
"Soul",
|
||||
"Punk",
|
||||
"Space",
|
||||
"Meditative",
|
||||
"Instrumental Pop",
|
||||
"Instrumental Rock",
|
||||
"Ethnic",
|
||||
"Gothic",
|
||||
"Darkwave",
|
||||
"Techno-Industrial",
|
||||
"Electronic",
|
||||
"Pop-Folk",
|
||||
"Eurodance",
|
||||
"Dream",
|
||||
"Southern Rock",
|
||||
"Comedy",
|
||||
"Cult",
|
||||
"Gangsta",
|
||||
"Top 40",
|
||||
"Christian Rap",
|
||||
"Pop/Funk",
|
||||
"Jungle",
|
||||
"Native American",
|
||||
"Cabaret",
|
||||
"New Wave",
|
||||
"Psychedelic",
|
||||
"Rave",
|
||||
"Showtunes",
|
||||
"Trailer",
|
||||
"Lo-Fi",
|
||||
"Tribal",
|
||||
"Acid Punk",
|
||||
"Acid Jazz",
|
||||
"Polka",
|
||||
"Retro",
|
||||
"Musical",
|
||||
"Rock & Roll",
|
||||
"Hard Rock",
|
||||
"Folk",
|
||||
"Folk/Rock",
|
||||
"National Folk",
|
||||
"Swing",
|
||||
"Fusion",
|
||||
"Bebob",
|
||||
"Latin",
|
||||
"Revival",
|
||||
"Celtic",
|
||||
"Bluegrass",
|
||||
"Avantgarde",
|
||||
"Gothic Rock",
|
||||
"Progressive Rock",
|
||||
"Psychedelic Rock",
|
||||
"Symphonic Rock",
|
||||
"Slow Rock",
|
||||
"Big Band",
|
||||
"Chorus",
|
||||
"Easy Listening",
|
||||
"Acoustic",
|
||||
"Humour",
|
||||
"Speech",
|
||||
"Chanson",
|
||||
"Opera",
|
||||
"Chamber Music",
|
||||
"Sonata",
|
||||
"Symphony",
|
||||
"Booty Bass",
|
||||
"Primus",
|
||||
"Porn Groove",
|
||||
"Satire",
|
||||
"Slow Jam",
|
||||
"Club",
|
||||
"Tango",
|
||||
"Samba",
|
||||
"Folklore",
|
||||
"Ballad",
|
||||
"Power Ballad",
|
||||
"Rhythmic Soul",
|
||||
"Freestyle",
|
||||
"Duet",
|
||||
"Punk Rock",
|
||||
"Drum Solo",
|
||||
"A Cappella",
|
||||
"Euro-House",
|
||||
"Dance Hall",
|
||||
"Goa",
|
||||
"Drum & Bass",
|
||||
"Club-House",
|
||||
"Hardcore",
|
||||
"Terror",
|
||||
"Indie",
|
||||
"BritPop",
|
||||
"Negerpunk",
|
||||
"Polsk Punk",
|
||||
"Beat",
|
||||
"Christian Gangsta Rap",
|
||||
"Heavy Metal",
|
||||
"Black Metal",
|
||||
"Crossover",
|
||||
"Contemporary Christian",
|
||||
"Christian Rock",
|
||||
"Merengue",
|
||||
"Salsa",
|
||||
"Thrash Metal",
|
||||
"Anime",
|
||||
"Jpop",
|
||||
"Synthpop",
|
||||
"Abstract",
|
||||
"Art Rock",
|
||||
"Baroque",
|
||||
"Bhangra",
|
||||
"Big Beat",
|
||||
"Breakbeat",
|
||||
"Chillout",
|
||||
"Downtempo",
|
||||
"Dub",
|
||||
"EBM",
|
||||
"Eclectic",
|
||||
"Electro",
|
||||
"Electroclash",
|
||||
"Emo",
|
||||
"Experimental",
|
||||
"Garage",
|
||||
"Global",
|
||||
"IDM",
|
||||
"Illbient",
|
||||
"Industro-Goth",
|
||||
"Jam Band",
|
||||
"Krautrock",
|
||||
"Leftfield",
|
||||
"Lounge",
|
||||
"Math Rock",
|
||||
"New Romantic",
|
||||
"Nu-Breakz",
|
||||
"Post-Punk",
|
||||
"Post-Rock",
|
||||
"Psytrance",
|
||||
"Shoegaze",
|
||||
"Space Rock",
|
||||
"Trop Rock",
|
||||
"World Music",
|
||||
"Neoclassical",
|
||||
"Audiobook",
|
||||
"Audio Theatre",
|
||||
"Neue Deutsche Welle",
|
||||
"Podcast",
|
||||
"Indie Rock",
|
||||
"G-Funk",
|
||||
"Dubstep",
|
||||
"Garage Rock",
|
||||
"Psybient"
|
||||
};
|
||||
}
|
||||
namespace
|
||||
{
|
||||
const wchar_t *genres[] = {
|
||||
L"Blues",
|
||||
L"Classic Rock",
|
||||
L"Country",
|
||||
L"Dance",
|
||||
L"Disco",
|
||||
L"Funk",
|
||||
L"Grunge",
|
||||
L"Hip-Hop",
|
||||
L"Jazz",
|
||||
L"Metal",
|
||||
L"New Age",
|
||||
L"Oldies",
|
||||
L"Other",
|
||||
L"Pop",
|
||||
L"R&B",
|
||||
L"Rap",
|
||||
L"Reggae",
|
||||
L"Rock",
|
||||
L"Techno",
|
||||
L"Industrial",
|
||||
L"Alternative",
|
||||
L"Ska",
|
||||
L"Death Metal",
|
||||
L"Pranks",
|
||||
L"Soundtrack",
|
||||
L"Euro-Techno",
|
||||
L"Ambient",
|
||||
L"Trip-Hop",
|
||||
L"Vocal",
|
||||
L"Jazz-Funk",
|
||||
L"Fusion",
|
||||
L"Trance",
|
||||
L"Classical",
|
||||
L"Instrumental",
|
||||
L"Acid",
|
||||
L"House",
|
||||
L"Game",
|
||||
L"Sound Clip",
|
||||
L"Gospel",
|
||||
L"Noise",
|
||||
L"Alternative Rock",
|
||||
L"Bass",
|
||||
L"Soul",
|
||||
L"Punk",
|
||||
L"Space",
|
||||
L"Meditative",
|
||||
L"Instrumental Pop",
|
||||
L"Instrumental Rock",
|
||||
L"Ethnic",
|
||||
L"Gothic",
|
||||
L"Darkwave",
|
||||
L"Techno-Industrial",
|
||||
L"Electronic",
|
||||
L"Pop-Folk",
|
||||
L"Eurodance",
|
||||
L"Dream",
|
||||
L"Southern Rock",
|
||||
L"Comedy",
|
||||
L"Cult",
|
||||
L"Gangsta",
|
||||
L"Top 40",
|
||||
L"Christian Rap",
|
||||
L"Pop/Funk",
|
||||
L"Jungle",
|
||||
L"Native American",
|
||||
L"Cabaret",
|
||||
L"New Wave",
|
||||
L"Psychedelic",
|
||||
L"Rave",
|
||||
L"Showtunes",
|
||||
L"Trailer",
|
||||
L"Lo-Fi",
|
||||
L"Tribal",
|
||||
L"Acid Punk",
|
||||
L"Acid Jazz",
|
||||
L"Polka",
|
||||
L"Retro",
|
||||
L"Musical",
|
||||
L"Rock & Roll",
|
||||
L"Hard Rock",
|
||||
L"Folk",
|
||||
L"Folk Rock",
|
||||
L"National Folk",
|
||||
L"Swing",
|
||||
L"Fast Fusion",
|
||||
L"Bebop",
|
||||
L"Latin",
|
||||
L"Revival",
|
||||
L"Celtic",
|
||||
L"Bluegrass",
|
||||
L"Avant-garde",
|
||||
L"Gothic Rock",
|
||||
L"Progressive Rock",
|
||||
L"Psychedelic Rock",
|
||||
L"Symphonic Rock",
|
||||
L"Slow Rock",
|
||||
L"Big Band",
|
||||
L"Chorus",
|
||||
L"Easy Listening",
|
||||
L"Acoustic",
|
||||
L"Humour",
|
||||
L"Speech",
|
||||
L"Chanson",
|
||||
L"Opera",
|
||||
L"Chamber Music",
|
||||
L"Sonata",
|
||||
L"Symphony",
|
||||
L"Booty Bass",
|
||||
L"Primus",
|
||||
L"Porn Groove",
|
||||
L"Satire",
|
||||
L"Slow Jam",
|
||||
L"Club",
|
||||
L"Tango",
|
||||
L"Samba",
|
||||
L"Folklore",
|
||||
L"Ballad",
|
||||
L"Power Ballad",
|
||||
L"Rhythmic Soul",
|
||||
L"Freestyle",
|
||||
L"Duet",
|
||||
L"Punk Rock",
|
||||
L"Drum Solo",
|
||||
L"A Cappella",
|
||||
L"Euro-House",
|
||||
L"Dancehall",
|
||||
L"Goa",
|
||||
L"Drum & Bass",
|
||||
L"Club-House",
|
||||
L"Hardcore Techno",
|
||||
L"Terror",
|
||||
L"Indie",
|
||||
L"Britpop",
|
||||
L"Worldbeat",
|
||||
L"Polsk Punk",
|
||||
L"Beat",
|
||||
L"Christian Gangsta Rap",
|
||||
L"Heavy Metal",
|
||||
L"Black Metal",
|
||||
L"Crossover",
|
||||
L"Contemporary Christian",
|
||||
L"Christian Rock",
|
||||
L"Merengue",
|
||||
L"Salsa",
|
||||
L"Thrash Metal",
|
||||
L"Anime",
|
||||
L"Jpop",
|
||||
L"Synthpop",
|
||||
L"Abstract",
|
||||
L"Art Rock",
|
||||
L"Baroque",
|
||||
L"Bhangra",
|
||||
L"Big Beat",
|
||||
L"Breakbeat",
|
||||
L"Chillout",
|
||||
L"Downtempo",
|
||||
L"Dub",
|
||||
L"EBM",
|
||||
L"Eclectic",
|
||||
L"Electro",
|
||||
L"Electroclash",
|
||||
L"Emo",
|
||||
L"Experimental",
|
||||
L"Garage",
|
||||
L"Global",
|
||||
L"IDM",
|
||||
L"Illbient",
|
||||
L"Industro-Goth",
|
||||
L"Jam Band",
|
||||
L"Krautrock",
|
||||
L"Leftfield",
|
||||
L"Lounge",
|
||||
L"Math Rock",
|
||||
L"New Romantic",
|
||||
L"Nu-Breakz",
|
||||
L"Post-Punk",
|
||||
L"Post-Rock",
|
||||
L"Psytrance",
|
||||
L"Shoegaze",
|
||||
L"Space Rock",
|
||||
L"Trop Rock",
|
||||
L"World Music",
|
||||
L"Neoclassical",
|
||||
L"Audiobook",
|
||||
L"Audio Theatre",
|
||||
L"Neue Deutsche Welle",
|
||||
L"Podcast",
|
||||
L"Indie Rock",
|
||||
L"G-Funk",
|
||||
L"Dubstep",
|
||||
L"Garage Rock",
|
||||
L"Psybient"
|
||||
};
|
||||
const int genresSize = sizeof(genres) / sizeof(genres[0]);
|
||||
}
|
||||
|
||||
StringList ID3v1::genreList()
|
||||
{
|
||||
static StringList l;
|
||||
if(l.isEmpty()) {
|
||||
for(int i = 0; i < genresSize; i++)
|
||||
l.append(genres[i]);
|
||||
StringList l;
|
||||
for(int i = 0; i < genresSize; i++) {
|
||||
l.append(genres[i]);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
ID3v1::GenreMap ID3v1::genreMap()
|
||||
{
|
||||
static GenreMap m;
|
||||
if(m.isEmpty()) {
|
||||
for(int i = 0; i < genresSize; i++)
|
||||
m.insert(genres[i], i);
|
||||
GenreMap m;
|
||||
for(int i = 0; i < genresSize; i++) {
|
||||
m.insert(genres[i], i);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
String ID3v1::genre(int i)
|
||||
{
|
||||
if(i >= 0 && i < genresSize)
|
||||
return genres[i] + String::null; // always make a copy
|
||||
return String::null;
|
||||
return String(genres[i]); // always make a copy
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
int ID3v1::genreIndex(const String &name)
|
||||
{
|
||||
if(genreMap().contains(name))
|
||||
return genreMap()[name];
|
||||
for(int i = 0; i < genresSize; ++i) {
|
||||
if(name == genres[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
// If the name was not found, try the names which have been changed
|
||||
static const struct {
|
||||
const wchar_t *genre;
|
||||
int code;
|
||||
} fixUpGenres[] = {
|
||||
{ L"Jazz+Funk", 29 },
|
||||
{ L"Folk/Rock", 81 },
|
||||
{ L"Bebob", 85 },
|
||||
{ L"Avantgarde", 90 },
|
||||
{ L"Dance Hall", 125 },
|
||||
{ L"Hardcore", 129 },
|
||||
{ L"BritPop", 132 },
|
||||
{ L"Negerpunk", 133 }
|
||||
};
|
||||
static const int fixUpGenresSize =
|
||||
sizeof(fixUpGenres) / sizeof(fixUpGenres[0]);
|
||||
for(int i = 0; i < fixUpGenresSize; ++i) {
|
||||
if(name == fixUpGenres[i].genre)
|
||||
return fixUpGenres[i].code;
|
||||
}
|
||||
|
||||
return 255;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,20 @@
|
||||
using namespace TagLib;
|
||||
using namespace ID3v1;
|
||||
|
||||
namespace
|
||||
{
|
||||
const ID3v1::StringHandler defaultStringHandler;
|
||||
const ID3v1::StringHandler *stringHandler = &defaultStringHandler;
|
||||
}
|
||||
|
||||
class ID3v1::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate() : file(0), tagOffset(-1), track(0), genre(255) {}
|
||||
TagPrivate() :
|
||||
file(0),
|
||||
tagOffset(0),
|
||||
track(0),
|
||||
genre(255) {}
|
||||
|
||||
File *file;
|
||||
long tagOffset;
|
||||
@@ -45,15 +55,10 @@ public:
|
||||
String album;
|
||||
String year;
|
||||
String comment;
|
||||
uchar track;
|
||||
uchar genre;
|
||||
|
||||
static const StringHandler *stringHandler;
|
||||
unsigned char track;
|
||||
unsigned char genre;
|
||||
};
|
||||
|
||||
static const StringHandler defaultStringHandler;
|
||||
const ID3v1::StringHandler *ID3v1::Tag::TagPrivate::stringHandler = &defaultStringHandler;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// StringHandler implementation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -69,26 +74,26 @@ String ID3v1::StringHandler::parse(const ByteVector &data) const
|
||||
|
||||
ByteVector ID3v1::StringHandler::render(const String &s) const
|
||||
{
|
||||
if(!s.isLatin1())
|
||||
{
|
||||
if(s.isLatin1())
|
||||
return s.data(String::Latin1);
|
||||
else
|
||||
return ByteVector();
|
||||
}
|
||||
|
||||
return s.data(String::Latin1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ID3v1::Tag::Tag() : TagLib::Tag()
|
||||
ID3v1::Tag::Tag() :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
ID3v1::Tag::Tag(File *file, long tagOffset) : TagLib::Tag()
|
||||
ID3v1::Tag::Tag(File *file, long tagOffset) :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
d->file = file;
|
||||
d->tagOffset = tagOffset;
|
||||
|
||||
@@ -105,11 +110,11 @@ ByteVector ID3v1::Tag::render() const
|
||||
ByteVector data;
|
||||
|
||||
data.append(fileIdentifier());
|
||||
data.append(TagPrivate::stringHandler->render(d->title).resize(30));
|
||||
data.append(TagPrivate::stringHandler->render(d->artist).resize(30));
|
||||
data.append(TagPrivate::stringHandler->render(d->album).resize(30));
|
||||
data.append(TagPrivate::stringHandler->render(d->year).resize(4));
|
||||
data.append(TagPrivate::stringHandler->render(d->comment).resize(28));
|
||||
data.append(stringHandler->render(d->title).resize(30));
|
||||
data.append(stringHandler->render(d->artist).resize(30));
|
||||
data.append(stringHandler->render(d->album).resize(30));
|
||||
data.append(stringHandler->render(d->year).resize(4));
|
||||
data.append(stringHandler->render(d->comment).resize(28));
|
||||
data.append(char(0));
|
||||
data.append(char(d->track));
|
||||
data.append(char(d->genre));
|
||||
@@ -147,12 +152,12 @@ String ID3v1::Tag::genre() const
|
||||
return ID3v1::genre(d->genre);
|
||||
}
|
||||
|
||||
TagLib::uint ID3v1::Tag::year() const
|
||||
unsigned int ID3v1::Tag::year() const
|
||||
{
|
||||
return d->year.toInt();
|
||||
}
|
||||
|
||||
TagLib::uint ID3v1::Tag::track() const
|
||||
unsigned int ID3v1::Tag::track() const
|
||||
{
|
||||
return d->track;
|
||||
}
|
||||
@@ -182,32 +187,32 @@ void ID3v1::Tag::setGenre(const String &s)
|
||||
d->genre = ID3v1::genreIndex(s);
|
||||
}
|
||||
|
||||
void ID3v1::Tag::setYear(TagLib::uint i)
|
||||
void ID3v1::Tag::setYear(unsigned int i)
|
||||
{
|
||||
d->year = i > 0 ? String::number(i) : String::null;
|
||||
d->year = i > 0 ? String::number(i) : String();
|
||||
}
|
||||
|
||||
void ID3v1::Tag::setTrack(TagLib::uint i)
|
||||
void ID3v1::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
d->track = i < 256 ? i : 0;
|
||||
}
|
||||
|
||||
TagLib::uint ID3v1::Tag::genreNumber() const
|
||||
unsigned int ID3v1::Tag::genreNumber() const
|
||||
{
|
||||
return d->genre;
|
||||
}
|
||||
|
||||
void ID3v1::Tag::setGenreNumber(TagLib::uint i)
|
||||
void ID3v1::Tag::setGenreNumber(unsigned int i)
|
||||
{
|
||||
d->genre = i < 256 ? i : 255;
|
||||
}
|
||||
|
||||
void ID3v1::Tag::setStringHandler(const StringHandler *handler)
|
||||
{
|
||||
if (handler)
|
||||
TagPrivate::stringHandler = handler;
|
||||
if(handler)
|
||||
stringHandler = handler;
|
||||
else
|
||||
TagPrivate::stringHandler = &defaultStringHandler;
|
||||
stringHandler = &defaultStringHandler;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -219,7 +224,7 @@ void ID3v1::Tag::read()
|
||||
if(d->file && d->file->isValid()) {
|
||||
d->file->seek(d->tagOffset);
|
||||
// read the tag -- always 128 bytes
|
||||
ByteVector data = d->file->readBlock(128);
|
||||
const ByteVector data = d->file->readBlock(128);
|
||||
|
||||
// some initial sanity checking
|
||||
if(data.size() == 128 && data.startsWith("TAG"))
|
||||
@@ -233,16 +238,16 @@ void ID3v1::Tag::parse(const ByteVector &data)
|
||||
{
|
||||
int offset = 3;
|
||||
|
||||
d->title = TagPrivate::stringHandler->parse(data.mid(offset, 30));
|
||||
d->title = stringHandler->parse(data.mid(offset, 30));
|
||||
offset += 30;
|
||||
|
||||
d->artist = TagPrivate::stringHandler->parse(data.mid(offset, 30));
|
||||
d->artist = stringHandler->parse(data.mid(offset, 30));
|
||||
offset += 30;
|
||||
|
||||
d->album = TagPrivate::stringHandler->parse(data.mid(offset, 30));
|
||||
d->album = stringHandler->parse(data.mid(offset, 30));
|
||||
offset += 30;
|
||||
|
||||
d->year = TagPrivate::stringHandler->parse(data.mid(offset, 4));
|
||||
d->year = stringHandler->parse(data.mid(offset, 4));
|
||||
offset += 4;
|
||||
|
||||
// Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this
|
||||
@@ -253,13 +258,13 @@ void ID3v1::Tag::parse(const ByteVector &data)
|
||||
if(data[offset + 28] == 0 && data[offset + 29] != 0) {
|
||||
// ID3v1.1 detected
|
||||
|
||||
d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 28));
|
||||
d->track = uchar(data[offset + 29]);
|
||||
d->comment = stringHandler->parse(data.mid(offset, 28));
|
||||
d->track = static_cast<unsigned char>(data[offset + 29]);
|
||||
}
|
||||
else
|
||||
d->comment = data.mid(offset, 30);
|
||||
|
||||
offset += 30;
|
||||
|
||||
d->genre = uchar(data[offset]);
|
||||
d->genre = static_cast<unsigned char>(data[offset]);
|
||||
}
|
||||
|
||||
@@ -140,23 +140,23 @@ namespace TagLib {
|
||||
virtual String album() const;
|
||||
virtual String comment() const;
|
||||
virtual String genre() const;
|
||||
virtual TagLib::uint year() const;
|
||||
virtual TagLib::uint track() 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(TagLib::uint i);
|
||||
virtual void setTrack(TagLib::uint i);
|
||||
virtual void setYear(unsigned int i);
|
||||
virtual void setTrack(unsigned int i);
|
||||
|
||||
/*!
|
||||
* Returns the genre in number.
|
||||
*
|
||||
* \note Normally 255 indicates that this tag contains no genre.
|
||||
*/
|
||||
TagLib::uint genreNumber() const;
|
||||
unsigned int genreNumber() const;
|
||||
|
||||
/*!
|
||||
* Sets the genre in number to \a i.
|
||||
@@ -164,7 +164,7 @@ namespace TagLib {
|
||||
* \note Valid value is from 0 up to 255. Normally 255 indicates that
|
||||
* this tag contains no genre.
|
||||
*/
|
||||
void setGenreNumber(TagLib::uint i);
|
||||
void setGenreNumber(unsigned int i);
|
||||
|
||||
/*!
|
||||
* Sets the string handler that decides how the ID3v1 data will be
|
||||
|
||||
@@ -48,14 +48,16 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC")
|
||||
AttachedPictureFrame::AttachedPictureFrame() :
|
||||
Frame("APIC"),
|
||||
d(new AttachedPictureFramePrivate())
|
||||
{
|
||||
d = new AttachedPictureFramePrivate;
|
||||
}
|
||||
|
||||
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data)
|
||||
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) :
|
||||
Frame(data),
|
||||
d(new AttachedPictureFramePrivate())
|
||||
{
|
||||
d = new AttachedPictureFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@@ -137,7 +139,7 @@ void AttachedPictureFrame::parseFields(const ByteVector &data)
|
||||
|
||||
d->mimeType = readStringField(data, String::Latin1, &pos);
|
||||
/* Now we need at least two more bytes available */
|
||||
if (uint(pos) + 1 >= data.size()) {
|
||||
if(static_cast<unsigned int>(pos) + 1 >= data.size()) {
|
||||
debug("Truncated picture frame.");
|
||||
return;
|
||||
}
|
||||
@@ -169,9 +171,10 @@ ByteVector AttachedPictureFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h)
|
||||
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new AttachedPictureFramePrivate())
|
||||
{
|
||||
d = new AttachedPictureFramePrivate;
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
|
||||
@@ -37,17 +37,21 @@ class ChapterFrame::ChapterFramePrivate
|
||||
{
|
||||
public:
|
||||
ChapterFramePrivate() :
|
||||
tagHeader(0)
|
||||
tagHeader(0),
|
||||
startTime(0),
|
||||
endTime(0),
|
||||
startOffset(0),
|
||||
endOffset(0)
|
||||
{
|
||||
embeddedFrameList.setAutoDelete(true);
|
||||
}
|
||||
|
||||
const ID3v2::Header *tagHeader;
|
||||
ByteVector elementID;
|
||||
TagLib::uint startTime;
|
||||
TagLib::uint endTime;
|
||||
TagLib::uint startOffset;
|
||||
TagLib::uint endOffset;
|
||||
unsigned int startTime;
|
||||
unsigned int endTime;
|
||||
unsigned int startOffset;
|
||||
unsigned int endOffset;
|
||||
FrameListMap embeddedFrameListMap;
|
||||
FrameList embeddedFrameList;
|
||||
};
|
||||
@@ -57,21 +61,20 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) :
|
||||
ID3v2::Frame(data)
|
||||
ID3v2::Frame(data),
|
||||
d(new ChapterFramePrivate())
|
||||
{
|
||||
d = new ChapterFramePrivate;
|
||||
d->tagHeader = tagHeader;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
ChapterFrame::ChapterFrame(const ByteVector &elementID,
|
||||
TagLib::uint startTime, TagLib::uint endTime,
|
||||
TagLib::uint startOffset, TagLib::uint endOffset,
|
||||
unsigned int startTime, unsigned int endTime,
|
||||
unsigned int startOffset, unsigned int endOffset,
|
||||
const FrameList &embeddedFrames) :
|
||||
ID3v2::Frame("CHAP")
|
||||
ID3v2::Frame("CHAP"),
|
||||
d(new ChapterFramePrivate())
|
||||
{
|
||||
d = new ChapterFramePrivate;
|
||||
|
||||
// setElementID has a workaround for a previously silly API where you had to
|
||||
// specifically include the null byte.
|
||||
|
||||
@@ -97,22 +100,22 @@ ByteVector ChapterFrame::elementID() const
|
||||
return d->elementID;
|
||||
}
|
||||
|
||||
TagLib::uint ChapterFrame::startTime() const
|
||||
unsigned int ChapterFrame::startTime() const
|
||||
{
|
||||
return d->startTime;
|
||||
}
|
||||
|
||||
TagLib::uint ChapterFrame::endTime() const
|
||||
unsigned int ChapterFrame::endTime() const
|
||||
{
|
||||
return d->endTime;
|
||||
}
|
||||
|
||||
TagLib::uint ChapterFrame::startOffset() const
|
||||
unsigned int ChapterFrame::startOffset() const
|
||||
{
|
||||
return d->startOffset;
|
||||
}
|
||||
|
||||
TagLib::uint ChapterFrame::endOffset() const
|
||||
unsigned int ChapterFrame::endOffset() const
|
||||
{
|
||||
return d->endOffset;
|
||||
}
|
||||
@@ -125,22 +128,22 @@ void ChapterFrame::setElementID(const ByteVector &eID)
|
||||
d->elementID = d->elementID.mid(0, d->elementID.size() - 1);
|
||||
}
|
||||
|
||||
void ChapterFrame::setStartTime(const TagLib::uint &sT)
|
||||
void ChapterFrame::setStartTime(const unsigned int &sT)
|
||||
{
|
||||
d->startTime = sT;
|
||||
}
|
||||
|
||||
void ChapterFrame::setEndTime(const TagLib::uint &eT)
|
||||
void ChapterFrame::setEndTime(const unsigned int &eT)
|
||||
{
|
||||
d->endTime = eT;
|
||||
}
|
||||
|
||||
void ChapterFrame::setStartOffset(const TagLib::uint &sO)
|
||||
void ChapterFrame::setStartOffset(const unsigned int &sO)
|
||||
{
|
||||
d->startOffset = sO;
|
||||
}
|
||||
|
||||
void ChapterFrame::setEndOffset(const TagLib::uint &eO)
|
||||
void ChapterFrame::setEndOffset(const unsigned int &eO)
|
||||
{
|
||||
d->endOffset = eO;
|
||||
}
|
||||
@@ -198,7 +201,7 @@ String ChapterFrame::toString() const
|
||||
s += ", start offset: " + String::number(d->startOffset);
|
||||
|
||||
if(d->endOffset != 0xFFFFFFFF)
|
||||
s += ", start offset: " + String::number(d->endOffset);
|
||||
s += ", end offset: " + String::number(d->endOffset);
|
||||
|
||||
if(!d->embeddedFrameList.isEmpty()) {
|
||||
StringList frameIDs;
|
||||
@@ -238,7 +241,7 @@ ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVec
|
||||
|
||||
void ChapterFrame::parseFields(const ByteVector &data)
|
||||
{
|
||||
TagLib::uint size = data.size();
|
||||
unsigned int size = data.size();
|
||||
if(size < 18) {
|
||||
debug("A CHAP frame must contain at least 18 bytes (1 byte element ID "
|
||||
"terminated by null and 4x4 bytes for start and end time and offset).");
|
||||
@@ -246,7 +249,7 @@ void ChapterFrame::parseFields(const ByteVector &data)
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
TagLib::uint embPos = 0;
|
||||
unsigned int embPos = 0;
|
||||
d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
|
||||
d->startTime = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
@@ -264,7 +267,7 @@ void ChapterFrame::parseFields(const ByteVector &data)
|
||||
return;
|
||||
|
||||
while(embPos < size - header()->size()) {
|
||||
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0));
|
||||
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader);
|
||||
|
||||
if(!frame)
|
||||
return;
|
||||
@@ -298,9 +301,9 @@ ByteVector ChapterFrame::renderFields() const
|
||||
}
|
||||
|
||||
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) :
|
||||
Frame(h)
|
||||
Frame(h),
|
||||
d(new ChapterFramePrivate())
|
||||
{
|
||||
d = new ChapterFramePrivate;
|
||||
d->tagHeader = tagHeader;
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
@@ -57,13 +57,13 @@ namespace TagLib {
|
||||
* \a startTime, end time \a endTime, start offset \a startOffset,
|
||||
* end offset \a endOffset and optionally a list of embedded frames,
|
||||
* whose ownership will then be taken over by this Frame, in
|
||||
* \a embeededFrames;
|
||||
* \a embeddedFrames;
|
||||
*
|
||||
* All times are in milliseconds.
|
||||
*/
|
||||
ChapterFrame(const ByteVector &elementID,
|
||||
uint startTime, uint endTime,
|
||||
uint startOffset, uint endOffset,
|
||||
unsigned int startTime, unsigned int endTime,
|
||||
unsigned int startOffset, unsigned int endOffset,
|
||||
const FrameList &embeddedFrames = FrameList());
|
||||
|
||||
/*!
|
||||
@@ -84,14 +84,14 @@ namespace TagLib {
|
||||
*
|
||||
* \see setStartTime()
|
||||
*/
|
||||
uint startTime() const;
|
||||
unsigned int startTime() const;
|
||||
|
||||
/*!
|
||||
* Returns time of chapter's end (in milliseconds).
|
||||
*
|
||||
* \see setEndTime()
|
||||
*/
|
||||
uint endTime() const;
|
||||
unsigned int endTime() const;
|
||||
|
||||
/*!
|
||||
* Returns zero based byte offset (count of bytes from the beginning
|
||||
@@ -100,7 +100,7 @@ namespace TagLib {
|
||||
* \note If returned value is 0xFFFFFFFF, start time should be used instead.
|
||||
* \see setStartOffset()
|
||||
*/
|
||||
uint startOffset() const;
|
||||
unsigned int startOffset() const;
|
||||
|
||||
/*!
|
||||
* Returns zero based byte offset (count of bytes from the beginning
|
||||
@@ -109,7 +109,7 @@ namespace TagLib {
|
||||
* \note If returned value is 0xFFFFFFFF, end time should be used instead.
|
||||
* \see setEndOffset()
|
||||
*/
|
||||
uint endOffset() const;
|
||||
unsigned int endOffset() const;
|
||||
|
||||
/*!
|
||||
* Sets the element ID of the frame to \a eID. If \a eID isn't
|
||||
@@ -124,14 +124,14 @@ namespace TagLib {
|
||||
*
|
||||
* \see startTime()
|
||||
*/
|
||||
void setStartTime(const uint &sT);
|
||||
void setStartTime(const unsigned int &sT);
|
||||
|
||||
/*!
|
||||
* Sets time of chapter's end (in milliseconds) to \a eT.
|
||||
*
|
||||
* \see endTime()
|
||||
*/
|
||||
void setEndTime(const uint &eT);
|
||||
void setEndTime(const unsigned int &eT);
|
||||
|
||||
/*!
|
||||
* Sets zero based byte offset (count of bytes from the beginning
|
||||
@@ -139,7 +139,7 @@ namespace TagLib {
|
||||
*
|
||||
* \see startOffset()
|
||||
*/
|
||||
void setStartOffset(const uint &sO);
|
||||
void setStartOffset(const unsigned int &sO);
|
||||
|
||||
/*!
|
||||
* Sets zero based byte offset (count of bytes from the beginning
|
||||
@@ -147,7 +147,7 @@ namespace TagLib {
|
||||
*
|
||||
* \see endOffset()
|
||||
*/
|
||||
void setEndOffset(const uint &eO);
|
||||
void setEndOffset(const unsigned int &eO);
|
||||
|
||||
/*!
|
||||
* Returns a reference to the frame list map. This is an FrameListMap of
|
||||
|
||||
@@ -48,15 +48,17 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM")
|
||||
CommentsFrame::CommentsFrame(String::Type encoding) :
|
||||
Frame("COMM"),
|
||||
d(new CommentsFramePrivate())
|
||||
{
|
||||
d = new CommentsFramePrivate;
|
||||
d->textEncoding = encoding;
|
||||
}
|
||||
|
||||
CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data)
|
||||
CommentsFrame::CommentsFrame(const ByteVector &data) :
|
||||
Frame(data),
|
||||
d(new CommentsFramePrivate())
|
||||
{
|
||||
d = new CommentsFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@@ -116,8 +118,6 @@ PropertyMap CommentsFrame::asProperties() const
|
||||
PropertyMap map;
|
||||
if(key.isEmpty() || key == "COMMENT")
|
||||
map.insert("COMMENT", text());
|
||||
else if(key.isNull())
|
||||
map.unsupportedData().append(L"COMM/" + description());
|
||||
else
|
||||
map.insert("COMMENT:" + key, text());
|
||||
return map;
|
||||
@@ -164,7 +164,7 @@ void CommentsFrame::parseFields(const ByteVector &data)
|
||||
} else {
|
||||
d->description = String(l.front(), d->textEncoding);
|
||||
d->text = String(l.back(), d->textEncoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,8 +190,9 @@ ByteVector CommentsFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h)
|
||||
CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new CommentsFramePrivate())
|
||||
{
|
||||
d = new CommentsFramePrivate();
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
@@ -46,15 +46,15 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EventTimingCodesFrame::EventTimingCodesFrame() :
|
||||
Frame("ETCO")
|
||||
Frame("ETCO"),
|
||||
d(new EventTimingCodesFramePrivate())
|
||||
{
|
||||
d = new EventTimingCodesFramePrivate;
|
||||
}
|
||||
|
||||
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) :
|
||||
Frame(data)
|
||||
Frame(data),
|
||||
d(new EventTimingCodesFramePrivate())
|
||||
{
|
||||
d = new EventTimingCodesFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@@ -109,8 +109,8 @@ void EventTimingCodesFrame::parseFields(const ByteVector &data)
|
||||
int pos = 1;
|
||||
d->synchedEvents.clear();
|
||||
while(pos + 4 < end) {
|
||||
EventType type = EventType(uchar(data[pos++]));
|
||||
uint time = data.toUInt(pos, true);
|
||||
EventType type = static_cast<EventType>(static_cast<unsigned char>(data[pos++]));
|
||||
unsigned int time = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
d->synchedEvents.append(SynchedEvent(time, type));
|
||||
}
|
||||
@@ -136,9 +136,9 @@ ByteVector EventTimingCodesFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h)
|
||||
: Frame(h)
|
||||
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new EventTimingCodesFramePrivate())
|
||||
{
|
||||
d = new EventTimingCodesFramePrivate();
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
@@ -108,8 +108,8 @@ namespace TagLib {
|
||||
* Single entry of time stamp and event.
|
||||
*/
|
||||
struct SynchedEvent {
|
||||
SynchedEvent(uint ms, EventType t) : time(ms), type(t) {}
|
||||
uint time;
|
||||
SynchedEvent(unsigned int ms, EventType t) : time(ms), type(t) {}
|
||||
unsigned int time;
|
||||
EventType type;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
|
||||
copyright : (C) 2006 by Aaron VonderHaar
|
||||
email : avh4@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
@@ -26,6 +27,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstringlist.h>
|
||||
|
||||
#include "generalencapsulatedobjectframe.h"
|
||||
|
||||
@@ -48,14 +50,16 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB")
|
||||
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() :
|
||||
Frame("GEOB"),
|
||||
d(new GeneralEncapsulatedObjectFramePrivate())
|
||||
{
|
||||
d = new GeneralEncapsulatedObjectFramePrivate;
|
||||
}
|
||||
|
||||
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data)
|
||||
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) :
|
||||
Frame(data),
|
||||
d(new GeneralEncapsulatedObjectFramePrivate())
|
||||
{
|
||||
d = new GeneralEncapsulatedObjectFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@@ -151,15 +155,21 @@ void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data)
|
||||
|
||||
ByteVector GeneralEncapsulatedObjectFrame::renderFields() const
|
||||
{
|
||||
StringList sl;
|
||||
sl.append(d->fileName);
|
||||
sl.append(d->description);
|
||||
|
||||
const String::Type encoding = checkTextEncoding(sl, d->textEncoding);
|
||||
|
||||
ByteVector data;
|
||||
|
||||
data.append(char(d->textEncoding));
|
||||
data.append(char(encoding));
|
||||
data.append(d->mimeType.data(String::Latin1));
|
||||
data.append(textDelimiter(String::Latin1));
|
||||
data.append(d->fileName.data(d->textEncoding));
|
||||
data.append(textDelimiter(d->textEncoding));
|
||||
data.append(d->description.data(d->textEncoding));
|
||||
data.append(textDelimiter(d->textEncoding));
|
||||
data.append(d->fileName.data(encoding));
|
||||
data.append(textDelimiter(encoding));
|
||||
data.append(d->description.data(encoding));
|
||||
data.append(textDelimiter(encoding));
|
||||
data.append(d->data);
|
||||
|
||||
return data;
|
||||
@@ -169,8 +179,9 @@ ByteVector GeneralEncapsulatedObjectFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h)
|
||||
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new GeneralEncapsulatedObjectFramePrivate())
|
||||
{
|
||||
d = new GeneralEncapsulatedObjectFramePrivate;
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
|
||||
copyright : (C) 2006 by Aaron VonderHaar
|
||||
email : avh4@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstringlist.h>
|
||||
#include <id3v2tag.h>
|
||||
|
||||
#include "ownershipframe.h"
|
||||
#include <id3v2tag.h>
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace ID3v2;
|
||||
@@ -44,15 +45,17 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE")
|
||||
OwnershipFrame::OwnershipFrame(String::Type encoding) :
|
||||
Frame("OWNE"),
|
||||
d(new OwnershipFramePrivate())
|
||||
{
|
||||
d = new OwnershipFramePrivate;
|
||||
d->textEncoding = encoding;
|
||||
}
|
||||
|
||||
OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data)
|
||||
OwnershipFrame::OwnershipFrame(const ByteVector &data) :
|
||||
Frame(data),
|
||||
d(new OwnershipFramePrivate())
|
||||
{
|
||||
d = new OwnershipFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@@ -113,24 +116,24 @@ void OwnershipFrame::setTextEncoding(String::Type encoding)
|
||||
void OwnershipFrame::parseFields(const ByteVector &data)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
|
||||
// Get the text encoding
|
||||
d->textEncoding = String::Type(data[0]);
|
||||
pos += 1;
|
||||
|
||||
|
||||
// Read the price paid this is a null terminate string
|
||||
d->pricePaid = readStringField(data, String::Latin1, &pos);
|
||||
|
||||
|
||||
// If we don't have at least 8 bytes left then don't parse the rest of the
|
||||
// data
|
||||
if(data.size() - pos < 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Read the date purchased YYYYMMDD
|
||||
d->datePurchased = String(data.mid(pos, 8));
|
||||
pos += 8;
|
||||
|
||||
|
||||
// Read the seller
|
||||
if(d->textEncoding == String::Latin1)
|
||||
d->seller = Tag::latin1StringHandler()->parse(data.mid(pos));
|
||||
@@ -140,14 +143,19 @@ void OwnershipFrame::parseFields(const ByteVector &data)
|
||||
|
||||
ByteVector OwnershipFrame::renderFields() const
|
||||
{
|
||||
StringList sl;
|
||||
sl.append(d->seller);
|
||||
|
||||
const String::Type encoding = checkTextEncoding(sl, d->textEncoding);
|
||||
|
||||
ByteVector v;
|
||||
|
||||
v.append(char(d->textEncoding));
|
||||
|
||||
v.append(char(encoding));
|
||||
v.append(d->pricePaid.data(String::Latin1));
|
||||
v.append(textDelimiter(String::Latin1));
|
||||
v.append(d->datePurchased.data(String::Latin1));
|
||||
v.append(d->seller.data(d->textEncoding));
|
||||
|
||||
v.append(d->seller.data(encoding));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -155,8 +163,9 @@ ByteVector OwnershipFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h)
|
||||
OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new OwnershipFramePrivate())
|
||||
{
|
||||
d = new OwnershipFramePrivate;
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
89
taglib/mpeg/id3v2/frames/podcastframe.cpp
Normal file
89
taglib/mpeg/id3v2/frames/podcastframe.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2015 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.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 "podcastframe.h"
|
||||
#include <tpropertymap.h>
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace ID3v2;
|
||||
|
||||
class PodcastFrame::PodcastFramePrivate
|
||||
{
|
||||
public:
|
||||
ByteVector fieldData;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PodcastFrame::PodcastFrame() :
|
||||
Frame("PCST"),
|
||||
d(new PodcastFramePrivate())
|
||||
{
|
||||
d->fieldData = ByteVector(4, '\0');
|
||||
}
|
||||
|
||||
PodcastFrame::~PodcastFrame()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
String PodcastFrame::toString() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
PropertyMap PodcastFrame::asProperties() const
|
||||
{
|
||||
PropertyMap map;
|
||||
map.insert("PODCAST", StringList());
|
||||
return map;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PodcastFrame::parseFields(const ByteVector &data)
|
||||
{
|
||||
d->fieldData = data;
|
||||
}
|
||||
|
||||
ByteVector PodcastFrame::renderFields() const
|
||||
{
|
||||
return d->fieldData;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new PodcastFramePrivate())
|
||||
{
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
82
taglib/mpeg/id3v2/frames/podcastframe.h
Normal file
82
taglib/mpeg/id3v2/frames/podcastframe.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2015 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.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/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_PODCASTFRAME_H
|
||||
#define TAGLIB_PODCASTFRAME_H
|
||||
|
||||
#include "id3v2frame.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace ID3v2 {
|
||||
|
||||
//! ID3v2 podcast frame
|
||||
/*!
|
||||
* An implementation of ID3v2 podcast flag, a frame with four zero bytes.
|
||||
*/
|
||||
class TAGLIB_EXPORT PodcastFrame : public Frame
|
||||
{
|
||||
friend class FrameFactory;
|
||||
|
||||
public:
|
||||
/*!
|
||||
* Construct a podcast frame.
|
||||
*/
|
||||
PodcastFrame();
|
||||
|
||||
/*!
|
||||
* Destroys this PodcastFrame instance.
|
||||
*/
|
||||
virtual ~PodcastFrame();
|
||||
|
||||
/*!
|
||||
* Returns a null string.
|
||||
*/
|
||||
virtual String toString() const;
|
||||
|
||||
PropertyMap asProperties() const;
|
||||
|
||||
protected:
|
||||
// Reimplementations.
|
||||
|
||||
virtual void parseFields(const ByteVector &data);
|
||||
virtual ByteVector renderFields() const;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* The constructor used by the FrameFactory.
|
||||
*/
|
||||
PodcastFrame(const ByteVector &data, Header *h);
|
||||
PodcastFrame(const PodcastFrame &);
|
||||
PodcastFrame &operator=(const PodcastFrame &);
|
||||
|
||||
class PodcastFramePrivate;
|
||||
PodcastFramePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -36,21 +36,23 @@ public:
|
||||
PopularimeterFramePrivate() : rating(0), counter(0) {}
|
||||
String email;
|
||||
int rating;
|
||||
TagLib::uint counter;
|
||||
unsigned int counter;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PopularimeterFrame::PopularimeterFrame() : Frame("POPM")
|
||||
PopularimeterFrame::PopularimeterFrame() :
|
||||
Frame("POPM"),
|
||||
d(new PopularimeterFramePrivate())
|
||||
{
|
||||
d = new PopularimeterFramePrivate;
|
||||
}
|
||||
|
||||
PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data)
|
||||
PopularimeterFrame::PopularimeterFrame(const ByteVector &data) :
|
||||
Frame(data),
|
||||
d(new PopularimeterFramePrivate())
|
||||
{
|
||||
d = new PopularimeterFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@@ -84,12 +86,12 @@ void PopularimeterFrame::setRating(int s)
|
||||
d->rating = s;
|
||||
}
|
||||
|
||||
TagLib::uint PopularimeterFrame::counter() const
|
||||
unsigned int PopularimeterFrame::counter() const
|
||||
{
|
||||
return d->counter;
|
||||
}
|
||||
|
||||
void PopularimeterFrame::setCounter(TagLib::uint s)
|
||||
void PopularimeterFrame::setCounter(unsigned int s)
|
||||
{
|
||||
d->counter = s;
|
||||
}
|
||||
@@ -109,7 +111,7 @@ void PopularimeterFrame::parseFields(const ByteVector &data)
|
||||
if(pos < size) {
|
||||
d->rating = (unsigned char)(data[pos++]);
|
||||
if(pos < size) {
|
||||
d->counter = data.toUInt(static_cast<uint>(pos));
|
||||
d->counter = data.toUInt(static_cast<unsigned int>(pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,8 +132,9 @@ ByteVector PopularimeterFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h)
|
||||
PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new PopularimeterFramePrivate())
|
||||
{
|
||||
d = new PopularimeterFramePrivate;
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
@@ -100,14 +100,14 @@ namespace TagLib {
|
||||
*
|
||||
* \see setCounter()
|
||||
*/
|
||||
uint counter() const;
|
||||
unsigned int counter() const;
|
||||
|
||||
/*!
|
||||
* Set the counter.
|
||||
*
|
||||
* \see counter()
|
||||
*/
|
||||
void setCounter(uint counter);
|
||||
void setCounter(unsigned int counter);
|
||||
|
||||
protected:
|
||||
// Reimplementations.
|
||||
|
||||
@@ -45,15 +45,17 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PrivateFrame::PrivateFrame() : Frame("PRIV")
|
||||
PrivateFrame::PrivateFrame() :
|
||||
Frame("PRIV"),
|
||||
d(new PrivateFramePrivate())
|
||||
{
|
||||
d = new PrivateFramePrivate;
|
||||
}
|
||||
|
||||
PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data)
|
||||
PrivateFrame::PrivateFrame(const ByteVector &data) :
|
||||
Frame(data),
|
||||
d(new PrivateFramePrivate())
|
||||
{
|
||||
d = new PrivateFramePrivate;
|
||||
setData(data);
|
||||
Frame::setData(data);
|
||||
}
|
||||
|
||||
PrivateFrame::~PrivateFrame()
|
||||
@@ -121,8 +123,9 @@ ByteVector PrivateFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : Frame(h)
|
||||
PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new PrivateFramePrivate())
|
||||
{
|
||||
d = new PrivateFramePrivate();
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
@@ -31,11 +31,6 @@
|
||||
using namespace TagLib;
|
||||
using namespace ID3v2;
|
||||
|
||||
static inline int bitsToBytes(int i)
|
||||
{
|
||||
return i % 8 == 0 ? i / 8 : (i - i % 8) / 8 + 1;
|
||||
}
|
||||
|
||||
struct ChannelData
|
||||
{
|
||||
ChannelData() : channelType(RelativeVolumeFrame::Other), volumeAdjustment(0) {}
|
||||
@@ -56,14 +51,16 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2")
|
||||
RelativeVolumeFrame::RelativeVolumeFrame() :
|
||||
Frame("RVA2"),
|
||||
d(new RelativeVolumeFramePrivate())
|
||||
{
|
||||
d = new RelativeVolumeFramePrivate;
|
||||
}
|
||||
|
||||
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data)
|
||||
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) :
|
||||
Frame(data),
|
||||
d(new RelativeVolumeFramePrivate())
|
||||
{
|
||||
d = new RelativeVolumeFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@@ -185,19 +182,18 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data)
|
||||
|
||||
while(pos <= (int)data.size() - 4) {
|
||||
|
||||
|
||||
ChannelType type = ChannelType(data[pos]);
|
||||
pos += 1;
|
||||
|
||||
ChannelData &channel = d->channels[type];
|
||||
|
||||
channel.volumeAdjustment = data.toShort(static_cast<uint>(pos));
|
||||
channel.volumeAdjustment = data.toShort(static_cast<unsigned int>(pos));
|
||||
pos += 2;
|
||||
|
||||
channel.peakVolume.bitsRepresentingPeak = data[pos];
|
||||
pos += 1;
|
||||
|
||||
int bytes = bitsToBytes(channel.peakVolume.bitsRepresentingPeak);
|
||||
const int bytes = (channel.peakVolume.bitsRepresentingPeak + 7) / 8;
|
||||
channel.peakVolume.peakVolume = data.mid(pos, bytes);
|
||||
pos += bytes;
|
||||
}
|
||||
@@ -229,8 +225,9 @@ ByteVector RelativeVolumeFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h)
|
||||
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new RelativeVolumeFramePrivate())
|
||||
{
|
||||
d = new RelativeVolumeFramePrivate;
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
@@ -131,12 +131,12 @@ namespace TagLib {
|
||||
/*!
|
||||
* \deprecated Always returns master volume.
|
||||
*/
|
||||
ChannelType channelType() const;
|
||||
TAGLIB_DEPRECATED ChannelType channelType() const;
|
||||
|
||||
/*!
|
||||
* \deprecated This method no longer has any effect.
|
||||
*/
|
||||
void setChannelType(ChannelType t);
|
||||
TAGLIB_DEPRECATED void setChannelType(ChannelType t);
|
||||
|
||||
/*
|
||||
* There was a terrible API goof here, and while this can't be changed to
|
||||
@@ -155,7 +155,7 @@ namespace TagLib {
|
||||
* available and returns 0 if the specified channel does not exist.
|
||||
*
|
||||
* \see setVolumeAdjustmentIndex()
|
||||
* \see volumeAjustment()
|
||||
* \see volumeAdjustment()
|
||||
*/
|
||||
short volumeAdjustmentIndex(ChannelType type = MasterVolume) const;
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace TagLib {
|
||||
* By default this sets the value for the master volume.
|
||||
*
|
||||
* \see volumeAdjustmentIndex()
|
||||
* \see setVolumeAjustment()
|
||||
* \see setVolumeAdjustment()
|
||||
*/
|
||||
void setVolumeAdjustmentIndex(short index, ChannelType type = MasterVolume);
|
||||
|
||||
|
||||
@@ -52,16 +52,16 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SynchronizedLyricsFrame::SynchronizedLyricsFrame(String::Type encoding) :
|
||||
Frame("SYLT")
|
||||
Frame("SYLT"),
|
||||
d(new SynchronizedLyricsFramePrivate())
|
||||
{
|
||||
d = new SynchronizedLyricsFramePrivate;
|
||||
d->textEncoding = encoding;
|
||||
}
|
||||
|
||||
SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data) :
|
||||
Frame(data)
|
||||
Frame(data),
|
||||
d(new SynchronizedLyricsFramePrivate())
|
||||
{
|
||||
d = new SynchronizedLyricsFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@@ -117,8 +117,7 @@ void SynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding)
|
||||
d->language = languageEncoding.mid(0, 3);
|
||||
}
|
||||
|
||||
void SynchronizedLyricsFrame::setTimestampFormat(
|
||||
SynchronizedLyricsFrame::TimestampFormat f)
|
||||
void SynchronizedLyricsFrame::setTimestampFormat(SynchronizedLyricsFrame::TimestampFormat f)
|
||||
{
|
||||
d->timestampFormat = f;
|
||||
}
|
||||
@@ -159,7 +158,7 @@ void SynchronizedLyricsFrame::parseFields(const ByteVector &data)
|
||||
int pos = 6;
|
||||
|
||||
d->description = readStringField(data, d->textEncoding, &pos);
|
||||
if(d->description.isNull())
|
||||
if(pos == 6)
|
||||
return;
|
||||
|
||||
/*
|
||||
@@ -171,7 +170,7 @@ void SynchronizedLyricsFrame::parseFields(const ByteVector &data)
|
||||
*/
|
||||
String::Type encWithEndianness = d->textEncoding;
|
||||
if(d->textEncoding == String::UTF16) {
|
||||
ushort bom = data.toUShort(6, true);
|
||||
unsigned short bom = data.toUShort(6, true);
|
||||
if(bom == 0xfffe) {
|
||||
encWithEndianness = String::UTF16LE;
|
||||
} else if(bom == 0xfeff) {
|
||||
@@ -184,16 +183,16 @@ void SynchronizedLyricsFrame::parseFields(const ByteVector &data)
|
||||
String::Type enc = d->textEncoding;
|
||||
// If a UTF16 string has no BOM, use the encoding found above.
|
||||
if(enc == String::UTF16 && pos + 1 < end) {
|
||||
ushort bom = data.toUShort(pos, true);
|
||||
unsigned short bom = data.toUShort(pos, true);
|
||||
if(bom != 0xfffe && bom != 0xfeff) {
|
||||
enc = encWithEndianness;
|
||||
}
|
||||
}
|
||||
String text = readStringField(data, enc, &pos);
|
||||
if(text.isNull() || pos + 4 > end)
|
||||
if(pos + 4 > end)
|
||||
return;
|
||||
|
||||
uint time = data.toUInt(pos, true);
|
||||
unsigned int time = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->synchedText.append(SynchedText(time, text));
|
||||
@@ -235,9 +234,9 @@ ByteVector SynchronizedLyricsFrame::renderFields() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h)
|
||||
: Frame(h)
|
||||
SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h),
|
||||
d(new SynchronizedLyricsFramePrivate())
|
||||
{
|
||||
d = new SynchronizedLyricsFramePrivate();
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user