mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-12 19:06:09 -10:00
Compare commits
852
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82553567e1 | ||
|
|
37575b2fea | ||
|
|
6ddca15dc5 | ||
|
|
353c28176c | ||
|
|
4815343854 | ||
|
|
4e18f01438 | ||
|
|
abdef9f7b0 | ||
|
|
f67a42d89e | ||
|
|
52b48bd32d | ||
|
|
ad2fcfd22f | ||
|
|
507f50cd90 | ||
|
|
d91739b41d | ||
|
|
b9413a2639 | ||
|
|
bb94d5bcd6 | ||
|
|
8caaf5c00c | ||
|
|
7db1861b59 | ||
|
|
fabdc6ab8a | ||
|
|
07107ce9b2 | ||
|
|
2a4795596f | ||
|
|
caaaf3213d | ||
|
|
e119be513b | ||
|
|
09524fb9fe | ||
|
|
9b5e7ce19d | ||
|
|
e39a8bbed3 | ||
|
|
d224a87828 | ||
|
|
78df244bc7 | ||
|
|
6718a1aba8 | ||
|
|
9d7c5a2b17 | ||
|
|
f4ccab0f9f | ||
|
|
4bfdb9bdf0 | ||
|
|
2b9441eeb6 | ||
|
|
9b1b3e0a74 | ||
|
|
d7a6a94808 | ||
|
|
f49cad0e32 | ||
|
|
d2f853f601 | ||
|
|
d544d42448 | ||
|
|
3d027dc7bc | ||
|
|
163e9175e9 | ||
|
|
91fda5386a | ||
|
|
63e610e07d | ||
|
|
26d6e0f56f | ||
|
|
aafbfb7277 | ||
|
|
7c4e564c92 | ||
|
|
fc5895a910 | ||
|
|
51b4d62007 | ||
|
|
497173d3e2 | ||
|
|
2118cc42f0 | ||
|
|
4a8147dae2 | ||
|
|
c9763b0623 | ||
|
|
63ecfcff6b | ||
|
|
a1670f9e1d | ||
|
|
1287cfacae | ||
|
|
d8fac09281 | ||
|
|
3ac9821907 | ||
|
|
e7a923bef6 | ||
|
|
840c1802e6 | ||
|
|
772a02f56f | ||
|
|
6a803c0550 | ||
|
|
485a5989c8 | ||
|
|
10bbead9ec | ||
|
|
aea4d1d414 | ||
|
|
3a321251f7 | ||
|
|
079a3f96eb | ||
|
|
a94bf2e9e2 | ||
|
|
92e8db7ff4 | ||
|
|
81f5cf4446 | ||
|
|
842858f9b8 | ||
|
|
415b2b4933 | ||
|
|
e243601cf0 | ||
|
|
808231dd7d | ||
|
|
44093bd88c | ||
|
|
4d7324dddf | ||
|
|
83dd78a95f | ||
|
|
d31083cabc | ||
|
|
c154581d6e | ||
|
|
2fd72258f4 | ||
|
|
67051463bd | ||
|
|
930e435ffd | ||
|
|
6b62146388 | ||
|
|
1f648f58e5 | ||
|
|
e7fb0dacc5 | ||
|
|
989c010a64 | ||
|
|
9e3f07305b | ||
|
|
9e93ebde03 | ||
|
|
8774f0480d | ||
|
|
17b33317de | ||
|
|
db4fbe8fd6 | ||
|
|
9e4c6df219 | ||
|
|
9e50680685 | ||
|
|
a02c2deb69 | ||
|
|
df5679c9d1 | ||
|
|
12c9ba07f8 | ||
|
|
7ef78fd17b | ||
|
|
d7274b85f9 | ||
|
|
4f9f0b6868 | ||
|
|
98169f9f2e | ||
|
|
99e1f22d12 | ||
|
|
59769894f3 | ||
|
|
b7e46e49d3 | ||
|
|
d96295e923 | ||
|
|
47aa9a6237 | ||
|
|
e374637df2 | ||
|
|
8e71c392ab | ||
|
|
bad7254692 | ||
|
|
0af2736327 | ||
|
|
df01822c2a | ||
|
|
8e04c6c8e0 | ||
|
|
e71b9629fe | ||
|
|
c3e3337802 | ||
|
|
f70adedfaf | ||
|
|
1edd6e8297 | ||
|
|
77a71f3445 | ||
|
|
0b6a8c11f6 | ||
|
|
08f80fe190 | ||
|
|
fe8ead6609 | ||
|
|
df8f20f208 | ||
|
|
5bf2b956f6 | ||
|
|
e218c3967e | ||
|
|
d68370ade7 | ||
|
|
8c79033819 | ||
|
|
15af2bb1a2 | ||
|
|
82d8f5df74 | ||
|
|
cb12cbef45 | ||
|
|
b8a9c520f3 | ||
|
|
bffe4b787d | ||
|
|
76db58932e | ||
|
|
7f73d35753 | ||
|
|
cd5e276102 | ||
|
|
900218e3e4 | ||
|
|
af285fbdbd | ||
|
|
d78a9fe8e3 | ||
|
|
792cbf20a9 | ||
|
|
0641437803 | ||
|
|
fa5db4fcfa | ||
|
|
bea4c628ec | ||
|
|
11e2491124 | ||
|
|
09021f3141 | ||
|
|
bd4b845290 | ||
|
|
e0e7f141ad | ||
|
|
7bdaac55e5 | ||
|
|
c473601875 | ||
|
|
bd0a1440e7 | ||
|
|
6508f47625 | ||
|
|
05a12213de | ||
|
|
e90563c437 | ||
|
|
7e8005033d | ||
|
|
6be290ed8b | ||
|
|
82735a0cfd | ||
|
|
5f5dbf5139 | ||
|
|
be3f86cd65 | ||
|
|
3df8b53570 | ||
|
|
8eafa260d2 | ||
|
|
ea5342bc70 | ||
|
|
8ffb0fe407 | ||
|
|
f448150156 | ||
|
|
6c1d5263f1 | ||
|
|
58b49486ce | ||
|
|
b2a2c7b0bd | ||
|
|
df6b2d4cdd | ||
|
|
6f84fcbf6d | ||
|
|
028ae98c21 | ||
|
|
f4d11325c6 | ||
|
|
2b83c73f8f | ||
|
|
39e1ae18a4 | ||
|
|
eb1389d738 | ||
|
|
589eb7533c | ||
|
|
fbe9cc156c | ||
|
|
b319143a67 | ||
|
|
d71a6b0ba7 | ||
|
|
5d7b64fcbf | ||
|
|
39912e025b | ||
|
|
4a3f9f1d84 | ||
|
|
8195fd50ab | ||
|
|
a64468200e | ||
|
|
6ff7662c29 | ||
|
|
d3480f70f2 | ||
|
|
2907c7ab94 | ||
|
|
1a130c794a | ||
|
|
c589c72817 | ||
|
|
695bee61c8 | ||
|
|
7c8abe0f16 | ||
|
|
07c86cd9c8 | ||
|
|
cb531f8a88 | ||
|
|
86cb91f9f2 | ||
|
|
f87a72bc15 | ||
|
|
33496419f9 | ||
|
|
4321fd364d | ||
|
|
d40cf7a3cd | ||
|
|
fa90542b00 | ||
|
|
cdc6f7d907 | ||
|
|
084d237437 | ||
|
|
62f6494299 | ||
|
|
19583bc788 | ||
|
|
fff62a85e0 | ||
|
|
e52b144510 | ||
|
|
e8d21a020f | ||
|
|
8b24877f9e | ||
|
|
6efceb2017 | ||
|
|
c28bfcf959 | ||
|
|
0b472ea463 | ||
|
|
ec82cb15ce | ||
|
|
69dba707e0 | ||
|
|
f50be50b18 | ||
|
|
90a5af60c4 | ||
|
|
33c1b788df | ||
|
|
5265627b8f | ||
|
|
94ce0ad2dd | ||
|
|
0eeb895432 | ||
|
|
5d7f10d4f8 | ||
|
|
9013d398db | ||
|
|
7c556854af | ||
|
|
ce519f006d | ||
|
|
4129551341 | ||
|
|
9be888f55a | ||
|
|
b185d1bfea | ||
|
|
19a37eb04d | ||
|
|
ca2f9b788f | ||
|
|
9a1eb4e794 | ||
|
|
592282448b | ||
|
|
e6bff4f864 | ||
|
|
1cf0bacc6e | ||
|
|
1f352967c7 | ||
|
|
cf14728757 | ||
|
|
c5278e2e95 | ||
|
|
fe1e9ca996 | ||
|
|
4ed9231983 | ||
|
|
ca1a844f08 | ||
|
|
833df59d0e | ||
|
|
7cb652b5d2 | ||
|
|
cd06284f50 | ||
|
|
1305f1dbe2 | ||
|
|
b3a8824d68 | ||
|
|
b42fb8272a | ||
|
|
36c134fcb8 | ||
|
|
d648091de4 | ||
|
|
2829c293df | ||
|
|
094d117764 | ||
|
|
0c0f7b9986 | ||
|
|
f30f955058 | ||
|
|
38e803c3c3 | ||
|
|
2c4c337907 | ||
|
|
7a1c587a9c | ||
|
|
8c59d8fb6d | ||
|
|
1623d39eea | ||
|
|
73504f80a5 | ||
|
|
95315d7f8a | ||
|
|
6577d53df8 | ||
|
|
63e44beb9c | ||
|
|
944ed03553 | ||
|
|
a5a927dd71 | ||
|
|
4153aa80a9 | ||
|
|
2a574ef267 | ||
|
|
1767b7a87b | ||
|
|
db3e2ae183 | ||
|
|
340b8d3136 | ||
|
|
39649e9e11 | ||
|
|
6bc1e1c3d9 | ||
|
|
2997633d0d | ||
|
|
e0cf1301fa | ||
|
|
ed15b7e551 | ||
|
|
863604d3a5 | ||
|
|
f750a43b87 | ||
|
|
b13ee73546 | ||
|
|
377751ab28 | ||
|
|
e1d3677f58 | ||
|
|
559b07da63 | ||
|
|
b91e3c6ab1 | ||
|
|
7767fdd933 | ||
|
|
0bd8754064 | ||
|
|
bedfc47273 | ||
|
|
f376142d57 | ||
|
|
0cce8fb34c | ||
|
|
5caa85ffa5 | ||
|
|
c0c6d4b572 | ||
|
|
7516800c62 | ||
|
|
cb465d789f | ||
|
|
25fa3543f2 | ||
|
|
cb81894632 | ||
|
|
18921e720a | ||
|
|
09e18c1164 | ||
|
|
637e44d8d2 | ||
|
|
82ce160093 | ||
|
|
04e5c7e484 | ||
|
|
09586df300 | ||
|
|
534285324d | ||
|
|
af2514755b | ||
|
|
e6dc4d71b6 | ||
|
|
dae0f59911 | ||
|
|
c7f523ae46 | ||
|
|
288e1ba907 | ||
|
|
d4f15a2f96 | ||
|
|
fb3c70126e | ||
|
|
a1cae77142 | ||
|
|
868959499c | ||
|
|
9ff7df0f71 | ||
|
|
692da9d5e2 | ||
|
|
2a2c8a5ac3 | ||
|
|
8bb6861efc | ||
|
|
994e3cf708 | ||
|
|
edbddf0fd5 | ||
|
|
3e0fbbbd90 | ||
|
|
74a5975afc | ||
|
|
9304a7f137 | ||
|
|
ee675a19b5 | ||
|
|
9d92d273e4 | ||
|
|
06aeb403dd | ||
|
|
14baaf9154 | ||
|
|
609cca7fc0 | ||
|
|
4a9f1a2d56 | ||
|
|
5958f9126a | ||
|
|
599e24cf91 | ||
|
|
eea97601b5 | ||
|
|
d88f4d6488 | ||
|
|
59dc59c318 | ||
|
|
7195ec9856 | ||
|
|
f819db3ac5 | ||
|
|
6839d1c3d1 | ||
|
|
91c04a2cfe | ||
|
|
845276bac3 | ||
|
|
1106898454 | ||
|
|
1cb838afe5 | ||
|
|
dd6e706412 | ||
|
|
75df49cd03 | ||
|
|
87a52efaae | ||
|
|
94b6a418c7 | ||
|
|
347dca5e60 | ||
|
|
f7a3d65e0c | ||
|
|
455ffd827a | ||
|
|
20cab0c235 | ||
|
|
5581ecb643 | ||
|
|
9a8bfc0217 | ||
|
|
bcf2ddefba | ||
|
|
ef382c125a | ||
|
|
1a7e2f42d4 | ||
|
|
6aaa40fef3 | ||
|
|
0a5a3a9dd4 | ||
|
|
aa60444ca4 | ||
|
|
c4ede0d7ae | ||
|
|
f2b61fb399 | ||
|
|
9bab9769e4 | ||
|
|
408101f7eb | ||
|
|
0110954518 | ||
|
|
988c61f2e1 | ||
|
|
011d74bb4d | ||
|
|
cf71eda1ad | ||
|
|
a6c8af28f2 | ||
|
|
14704a3415 | ||
|
|
9bbff82c73 | ||
|
|
ce30c433ba | ||
|
|
04da71448f | ||
|
|
c17dcb1835 | ||
|
|
6e5531adcc | ||
|
|
ad6ab9b95b | ||
|
|
ad6cbfb328 | ||
|
|
9b8b7e0dce | ||
|
|
1cb5dbf99b | ||
|
|
f08f72c90a | ||
|
|
46116362c5 | ||
|
|
98e8a0f041 | ||
|
|
8cd3352af7 | ||
|
|
2c85c93136 | ||
|
|
9251ec56ce | ||
|
|
deff6c9ec1 | ||
|
|
3fa4a8dbdc | ||
|
|
bf2d996bd0 | ||
|
|
79709b2372 | ||
|
|
584b9cadd7 | ||
|
|
2f1057f3c3 | ||
|
|
374bcacddd | ||
|
|
94c7fbda6b | ||
|
|
a049152e99 | ||
|
|
7686163957 | ||
|
|
ca62bd67ed | ||
|
|
ac2b864aaa | ||
|
|
1904330ebb | ||
|
|
876922c547 | ||
|
|
4e0a879c4b | ||
|
|
b258a1d159 | ||
|
|
df33974f88 | ||
|
|
8221bc3f04 | ||
|
|
82750b238a | ||
|
|
6b0ad43fb6 | ||
|
|
d55451195e | ||
|
|
9cff51b6bb | ||
|
|
771eb9c875 | ||
|
|
12fec3026b | ||
|
|
df80f64598 | ||
|
|
ada29fd266 | ||
|
|
07ad4cf2e0 | ||
|
|
c07cfc7210 | ||
|
|
c3243cc243 | ||
|
|
2d52ffa2a4 | ||
|
|
f2156292e4 | ||
|
|
4cb0ca42e3 | ||
|
|
3737249459 | ||
|
|
fecca89447 | ||
|
|
080e40d1da | ||
|
|
4c77bc7edf | ||
|
|
334e9d4abf | ||
|
|
b1404d8bbf | ||
|
|
6fd62e6ebd | ||
|
|
f01730e722 | ||
|
|
d4bd640280 | ||
|
|
4e053b234b | ||
|
|
6b9b2a45ab | ||
|
|
698e49d595 | ||
|
|
15886a64b1 | ||
|
|
d4844ab7ed | ||
|
|
94721d06de | ||
|
|
417b12129d | ||
|
|
fc391736ec | ||
|
|
c42fc4fc6f | ||
|
|
4360340bd4 | ||
|
|
72ffa6a052 | ||
|
|
2fc8a12138 | ||
|
|
842076e3b9 | ||
|
|
44cac0de49 | ||
|
|
2dc097ecb6 | ||
|
|
966bc0a0b9 | ||
|
|
03d81a7429 | ||
|
|
d6a556cd31 | ||
|
|
1c36ff40ba | ||
|
|
60fc558a3b | ||
|
|
ce5dfe148d | ||
|
|
9a8e3ba09c | ||
|
|
06066e0c52 | ||
|
|
f10cee9f58 | ||
|
|
6922d3ca2c | ||
|
|
a10b403748 | ||
|
|
6c1a55644e | ||
|
|
64f5554c90 | ||
|
|
c2a368534f | ||
|
|
9274925ddf | ||
|
|
5725562435 | ||
|
|
5f6fe0288c | ||
|
|
d5973346ec | ||
|
|
c23c65596f | ||
|
|
7e1ca2ce51 | ||
|
|
ba1674a9d3 | ||
|
|
fb4e20e364 | ||
|
|
911302c918 | ||
|
|
2d387c4b6a | ||
|
|
d8e7b95e07 | ||
|
|
5e23c5d5e1 | ||
|
|
40d9cc3293 | ||
|
|
d191b6cedc | ||
|
|
b13c816ecb | ||
|
|
3b0866bfe1 | ||
|
|
95e0b6fe6a | ||
|
|
4cf8a32c1e | ||
|
|
e04fc5edb7 | ||
|
|
ef4431e8b1 | ||
|
|
865946b97d | ||
|
|
b024d8f44a | ||
|
|
1d3f2c1da8 | ||
|
|
49bb57478c | ||
|
|
278c78e4f4 | ||
|
|
0ba4e49106 | ||
|
|
789c13f792 | ||
|
|
667e257d64 | ||
|
|
3f7728d328 | ||
|
|
3fb6b7c51c | ||
|
|
16e428c5ae | ||
|
|
04dcb4cf60 | ||
|
|
2de73b50fe | ||
|
|
04541c7e27 | ||
|
|
514a90805d | ||
|
|
0e66e63086 | ||
|
|
da58874390 | ||
|
|
52aa0ac031 | ||
|
|
80511c9337 | ||
|
|
4e922b976e | ||
|
|
bfe496e22a | ||
|
|
1a898ac834 | ||
|
|
b5b4a88775 | ||
|
|
73cd575ea1 | ||
|
|
bff65254a1 | ||
|
|
be27e65df2 | ||
|
|
2e7cfc5cf1 | ||
|
|
4a3287457d | ||
|
|
e07f3a41cb | ||
|
|
b4a628b3e5 | ||
|
|
d2ee8e873b | ||
|
|
eb6295e375 | ||
|
|
cc774b83d4 | ||
|
|
020c7e70fd | ||
|
|
f4b455dd98 | ||
|
|
b0d9c53688 | ||
|
|
b30a801f25 | ||
|
|
260673fcea | ||
|
|
6f0888e30b | ||
|
|
0a81104723 | ||
|
|
4cd9c847f3 | ||
|
|
14c4e553dd | ||
|
|
2cf4f7e4e7 | ||
|
|
09b76c601a | ||
|
|
173d41522e | ||
|
|
d64196eff0 | ||
|
|
45fa09d9cb | ||
|
|
2e1d2116b1 | ||
|
|
a17249eef6 | ||
|
|
1ce3b3486d | ||
|
|
404b205f31 | ||
|
|
a2ea205239 | ||
|
|
b9d98a3aa3 | ||
|
|
5990dbce08 | ||
|
|
de12c0ae0c | ||
|
|
69db392407 | ||
|
|
a43429f658 | ||
|
|
32b2ed5093 | ||
|
|
4ca0f86c74 | ||
|
|
8fffa4b5e9 | ||
|
|
aa1bc354cb | ||
|
|
b42fb9c97c | ||
|
|
c7ae91edef | ||
|
|
9f60839faf | ||
|
|
9d859066f7 | ||
|
|
6b3d5b39ba | ||
|
|
9fd95d8ba6 | ||
|
|
b5258c494a | ||
|
|
b8826835e2 | ||
|
|
c930d1a32e | ||
|
|
18e63d9700 | ||
|
|
dfd04a45fe | ||
|
|
7b79b04bad | ||
|
|
20a22e6a31 | ||
|
|
c409dde698 | ||
|
|
302192f7fe | ||
|
|
8885bb0c35 | ||
|
|
cdf6a21276 | ||
|
|
b2ca49edfe | ||
|
|
eda4cdffa7 | ||
|
|
1d77af86e7 | ||
|
|
ae49a7ca24 | ||
|
|
c321432392 | ||
|
|
d2204fdb11 | ||
|
|
7b324b7153 | ||
|
|
f00ca7c7f9 | ||
|
|
bf21913708 | ||
|
|
92cb2140aa | ||
|
|
599260f2e5 | ||
|
|
4c44934d7a | ||
|
|
b08d6c9837 | ||
|
|
ce5badaf78 | ||
|
|
2c18b8129c | ||
|
|
83e95705ea | ||
|
|
fd7f291b87 | ||
|
|
1d16bb2a04 | ||
|
|
2bf5828c17 | ||
|
|
bd5fb3896b | ||
|
|
a42e9ff90f | ||
|
|
2a4dded277 | ||
|
|
45e374221c | ||
|
|
b4f37f46cc | ||
|
|
ecd7f61956 | ||
|
|
11483057e5 | ||
|
|
4382b577ae | ||
|
|
fe6fafdbfb | ||
|
|
20e36e8190 | ||
|
|
65d0576a91 | ||
|
|
b3a342c796 | ||
|
|
7e35461c08 | ||
|
|
0a22d601fd | ||
|
|
4f4832f67f | ||
|
|
214b2dc053 | ||
|
|
c323917db9 | ||
|
|
5575e65eb6 | ||
|
|
a410654575 | ||
|
|
ea17916de7 | ||
|
|
db8abecc68 | ||
|
|
621e1692c9 | ||
|
|
aa5447c072 | ||
|
|
280c275c4e | ||
|
|
5ed2b6070e | ||
|
|
a614a8ecfc | ||
|
|
b5eca62785 | ||
|
|
a69b0e337e | ||
|
|
5a25de0885 | ||
|
|
4c4d5c2310 | ||
|
|
e99b4716c6 | ||
|
|
e7a79cf1c8 | ||
|
|
378df6c06a | ||
|
|
c005602e8f | ||
|
|
3f65b059f6 | ||
|
|
1c773fb3ca | ||
|
|
ffab766a38 | ||
|
|
8bebea455a | ||
|
|
c2b98654fc | ||
|
|
8d58adc848 | ||
|
|
5ab36f0629 | ||
|
|
4437766a2a | ||
|
|
787630e9de | ||
|
|
a62d13468c | ||
|
|
a42dba8316 | ||
|
|
00948ec261 | ||
|
|
c6c4f54f79 | ||
|
|
3b06c0a18e | ||
|
|
62efff7b97 | ||
|
|
9f9b4e8430 | ||
|
|
ff3de3a2b4 | ||
|
|
98876e99bb | ||
|
|
033402c261 | ||
|
|
43ceefde5f | ||
|
|
322060024c | ||
|
|
bb9d6fa4c5 | ||
|
|
d96a8d031a | ||
|
|
1a407d3fb3 | ||
|
|
5fce4b3408 | ||
|
|
4b4b8657a0 | ||
|
|
31ebefefe1 | ||
|
|
62226e71e7 | ||
|
|
7be17c9656 | ||
|
|
b7dc661fe7 | ||
|
|
33be5b0372 | ||
|
|
73034ed9d2 | ||
|
|
c58195f20d | ||
|
|
1cf11d31fe | ||
|
|
34469c72b4 | ||
|
|
5b6ff638e3 | ||
|
|
df40777e5f | ||
|
|
070edd9ad4 | ||
|
|
438945c691 | ||
|
|
4547b5775f | ||
|
|
5535311087 | ||
|
|
6dbf200cc3 | ||
|
|
778cb6f473 | ||
|
|
1cf0d17449 | ||
|
|
a9261477ba | ||
|
|
a86ba780ec | ||
|
|
484d810896 | ||
|
|
2e59f68e22 | ||
|
|
45ae61d17d | ||
|
|
66b62ee4f0 | ||
|
|
41f619c1ff | ||
|
|
b33ad0d271 | ||
|
|
d3f78a9354 | ||
|
|
85cc7caf18 | ||
|
|
c10927a7ae | ||
|
|
5ee68a653f | ||
|
|
f1f7b32980 | ||
|
|
8aa66b7ed0 | ||
|
|
de8c63e263 | ||
|
|
0779085d30 | ||
|
|
15e9942eee | ||
|
|
ca0e1cb2c1 | ||
|
|
7c623b7314 | ||
|
|
ade14624e8 | ||
|
|
c722aa6e49 | ||
|
|
260072b97f | ||
|
|
23e41970f2 | ||
|
|
581e808733 | ||
|
|
2ce5010f26 | ||
|
|
357449a95f | ||
|
|
5d990772b5 | ||
|
|
e760c58522 | ||
|
|
679094c6f1 | ||
|
|
5be84cace3 | ||
|
|
0ada262dcd | ||
|
|
9203388d6c | ||
|
|
f516809959 | ||
|
|
f49f8d6aea | ||
|
|
4c2ebf53b6 | ||
|
|
e41b44ff1d | ||
|
|
1bcb937fe3 | ||
|
|
d0600a697e | ||
|
|
3dfad4b251 | ||
|
|
486f35d1bf | ||
|
|
27e6507a5e | ||
|
|
0b07eedcfc | ||
|
|
1ab3a289c7 | ||
|
|
7f1d65c765 | ||
|
|
2c9f289188 | ||
|
|
59789a3c25 | ||
|
|
5a984a95ba | ||
|
|
7af854a57c | ||
|
|
130bac0c83 | ||
|
|
5bbb00d589 | ||
|
|
d9c9647919 | ||
|
|
54c73bdbc3 | ||
|
|
c6726f10f3 | ||
|
|
81071bb69a | ||
|
|
aff9bcf08e | ||
|
|
caeecf111e | ||
|
|
f9bc01cca6 | ||
|
|
8333d15f12 | ||
|
|
07ae236642 | ||
|
|
8ece00541f | ||
|
|
acbac94685 | ||
|
|
26f45732da | ||
|
|
547adbfaba | ||
|
|
6ac6a38bf1 | ||
|
|
59eeb39b8c | ||
|
|
ae7bb818cc | ||
|
|
ceda6a9869 | ||
|
|
157de87f15 | ||
|
|
6fb90421f9 | ||
|
|
8bb14d1a1f | ||
|
|
f8c714a4e1 | ||
|
|
6a9922eaa3 | ||
|
|
ea35421a4f | ||
|
|
948d0590b4 | ||
|
|
dd56493543 | ||
|
|
01c50727ac | ||
|
|
032d6a15a9 | ||
|
|
ac0aae23fa | ||
|
|
8dbea5eeb8 | ||
|
|
03ddd51063 | ||
|
|
03591b33ce | ||
|
|
ace67bf6ea | ||
|
|
de6c5582ce | ||
|
|
5bf9db07a8 | ||
|
|
40fba4f304 | ||
|
|
0d8fc66bc8 | ||
|
|
107946d012 | ||
|
|
9eaba0acc2 | ||
|
|
e7add258b0 | ||
|
|
d3f89c1042 | ||
|
|
89bdef89dc | ||
|
|
1e2b6460a3 | ||
|
|
a6831ef217 | ||
|
|
e0395ab51a | ||
|
|
5334c44451 | ||
|
|
27d57ab1b0 | ||
|
|
1d998e4709 | ||
|
|
5492df3a43 | ||
|
|
9dad552a4e | ||
|
|
9fa373b493 | ||
|
|
932c05c64b | ||
|
|
1dad62d7ae | ||
|
|
bce17561ed | ||
|
|
bac77085da | ||
|
|
816e792770 | ||
|
|
634a8a06fc | ||
|
|
2cc153abea | ||
|
|
a1cee67e15 | ||
|
|
60fff3f82f | ||
|
|
1c712a7448 | ||
|
|
facac997f1 | ||
|
|
8d05393ba0 | ||
|
|
03fa181c2d | ||
|
|
6ab508415c | ||
|
|
92931ff541 | ||
|
|
ee0811b097 | ||
|
|
76f2d72d30 | ||
|
|
a5ac021076 | ||
|
|
f693d49f95 | ||
|
|
c2b5fe7f28 | ||
|
|
2f5c690175 | ||
|
|
8ed93bb0e4 | ||
|
|
f266583a47 | ||
|
|
1d08e8543f | ||
|
|
03418796ab | ||
|
|
b93a3902dc | ||
|
|
c14af70287 | ||
|
|
3e4a289cc5 | ||
|
|
5b504bcd4e | ||
|
|
d53074c9ea | ||
|
|
f8f3cd33ce | ||
|
|
4964fccd7e | ||
|
|
5f46aa1444 | ||
|
|
16d5f8830e | ||
|
|
bbafbf1ffd | ||
|
|
2fdcd1e772 | ||
|
|
2b680f861c | ||
|
|
c9f9e1b2f0 | ||
|
|
1d3ff1b6c0 | ||
|
|
8d2674eaf2 | ||
|
|
458c9f30fb | ||
|
|
e1dd3784e2 | ||
|
|
417eb20a3e | ||
|
|
648751445a | ||
|
|
5b4199aca2 | ||
|
|
29a74af3bd | ||
|
|
60554601bd | ||
|
|
c112f093a8 | ||
|
|
c400a8a2ba | ||
|
|
0c240c398e | ||
|
|
d6e47f35d4 | ||
|
|
045276d7bf | ||
|
|
22758e9a4a | ||
|
|
05e70efc4f | ||
|
|
61091ad95a | ||
|
|
5c5a2f6a07 | ||
|
|
c9d8cb47d0 | ||
|
|
c449de9710 | ||
|
|
f8fb23b9f2 | ||
|
|
0d7edb9d0a | ||
|
|
99a59efdbf | ||
|
|
04cf483161 | ||
|
|
213238ce38 | ||
|
|
baf7746986 | ||
|
|
e8bd7363c8 | ||
|
|
ca2a9ca725 | ||
|
|
0169a5477c | ||
|
|
328327bea6 | ||
|
|
8951441311 | ||
|
|
f439a5c5a4 | ||
|
|
fef0e7e1f8 | ||
|
|
49fb506628 | ||
|
|
b754fa56b9 | ||
|
|
c67f7b66aa | ||
|
|
71c296391d | ||
|
|
b75a354505 | ||
|
|
a8cb9aabf1 | ||
|
|
99b427f224 | ||
|
|
17bd4a2fa8 | ||
|
|
81a5d228d4 | ||
|
|
ce8cf62cd8 | ||
|
|
46d5c81b57 | ||
|
|
802f09b947 | ||
|
|
6848eff404 | ||
|
|
82ec8af3aa | ||
|
|
30cbebf527 | ||
|
|
ee689cbb9f | ||
|
|
b065d75beb | ||
|
|
1a212868a0 | ||
|
|
174cff3eac | ||
|
|
67677e06b8 | ||
|
|
16668f3fb9 | ||
|
|
d3f615fe30 | ||
|
|
cea600b265 | ||
|
|
7b47965509 | ||
|
|
14395bc5cd | ||
|
|
87db55b4ac | ||
|
|
3fcb731621 | ||
|
|
82eadd3983 | ||
|
|
d20aa57508 | ||
|
|
4cb9e5cc04 | ||
|
|
bd21cd0c48 | ||
|
|
6e9d3223c8 | ||
|
|
ba826df963 | ||
|
|
6987914a2c | ||
|
|
bb78e74646 | ||
|
|
f693413e26 | ||
|
|
87366d11ca | ||
|
|
d7e3b61210 | ||
|
|
4f6b907e3a | ||
|
|
a2eaccfc48 | ||
|
|
864e218e20 | ||
|
|
319067c537 | ||
|
|
f18bd8af24 | ||
|
|
3942de94f6 | ||
|
|
5d6dc2e1a6 | ||
|
|
478c86d3a6 | ||
|
|
173041e03a | ||
|
|
1fa84b4f7d | ||
|
|
ef97279a67 | ||
|
|
b064a84fd0 | ||
|
|
3040fb8c7a | ||
|
|
a55b94b08a | ||
|
|
b898b04168 | ||
|
|
b12b56d13f |
No files matched your search
@@ -2,3 +2,6 @@ root = true
|
||||
|
||||
[*.{h,cpp}]
|
||||
indent_style = tab
|
||||
|
||||
[*.sdr]
|
||||
indent_style = tab
|
||||
@@ -281,3 +281,8 @@ documentation/doxygen/*
|
||||
|
||||
.idea/*
|
||||
projects/*
|
||||
/.vs
|
||||
CMakeSettings.json
|
||||
|
||||
# Weird files created by Linux
|
||||
.fuse_hidden*
|
||||
+2
-5
@@ -9,9 +9,6 @@ addons:
|
||||
packages:
|
||||
- ninja-build
|
||||
- libopenal-dev
|
||||
- libogg-dev
|
||||
- libvorbis-dev
|
||||
- libtheora-dev
|
||||
- libreadline6-dev
|
||||
- libpng12-dev
|
||||
- libjpeg62-dev
|
||||
@@ -30,13 +27,13 @@ deploy:
|
||||
- provider: releases
|
||||
api_key:
|
||||
secure: "Wdt4Ccefo7a0ZDjEJQJgpghh7n72FtNJuKSAPUvoz4gtEX6Ct9J8yJI1t9V3jE3CTbbLJ21/Q6eZRDfT7UfNJe5DdepoJsP9DeFhKMWqQCq+AChqQWlm0KeZAffJoJreL8vXYhnRYcLT9ceZHEH6AqwN9TPV+xJziGPISAkYNZ8="
|
||||
skip_cleanup: true
|
||||
skip_cleanup: true
|
||||
file_glob: true
|
||||
file: "/tmp/builds/*"
|
||||
on:
|
||||
condition: '"$RELEASE_BUILD" == true'
|
||||
tags: true
|
||||
|
||||
|
||||
- provider: bintray
|
||||
user: sirknightly
|
||||
key:
|
||||
|
||||
+2
-13
@@ -108,27 +108,16 @@ SET(FSO_RUN_ARGUMENTS "" CACHE STRING "Additional arguments passed to a generate
|
||||
|
||||
option(FSO_INSTALL_DEBUG_FILES "Install some debug files (currently only PDB files on windows)" OFF)
|
||||
|
||||
set(DEFAULT_VAL OFF)
|
||||
if(MSVC)
|
||||
# MSVC has been tested
|
||||
set(DEFAULT_VAL ON)
|
||||
endif()
|
||||
|
||||
OPTION(COTIRE_ENABLE "Enable cotire, this speeds up builds but may cause issues while developing (default = OFF)" ${DEFAULT_VAL})
|
||||
|
||||
MARK_AS_ADVANCED(FORCE FSO_CMAKE_DEBUG)
|
||||
MARK_AS_ADVANCED(FORCE FSO_BUILD_INCLUDED_LIBS)
|
||||
MARK_AS_ADVANCED(FORCE FSO_USE_OPENALSOFT)
|
||||
MARK_AS_ADVANCED(FORCE FSO_USE_LUAJIT)
|
||||
MARK_AS_ADVANCED(FORCE FSO_DEVELOPMENT_MODE)
|
||||
MARK_AS_ADVANCED(FORCE FSO_FATAL_WARNINGS)
|
||||
MARK_AS_ADVANCED(FORCE COTIRE_ENABLE)
|
||||
mark_as_advanced(FORCE FSO_INSTALL_DEBUG_FILES)
|
||||
|
||||
IF(COTIRE_ENABLE)
|
||||
# Include cotire file from https://github.com/sakra/cotire/
|
||||
include(cotire)
|
||||
ENDIF(COTIRE_ENABLE)
|
||||
# Include cotire file from https://github.com/sakra/cotire/
|
||||
include(cotire)
|
||||
|
||||
INCLUDE(globals)
|
||||
INCLUDE(toolchain)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
FreeSpace2 *S*ource *C*ode *P*roject
|
||||
==
|
||||
[](https://travis-ci.org/scp-fs2open/fs2open.github.com)
|
||||
[](https://travis-ci.org/scp-fs2open/fs2open.github.com)
|
||||
[](https://ci.appveyor.com/project/SirKnightly/fs2open-github-com/branch/master)
|
||||
[](https://scan.coverity.com/projects/870)
|
||||
|
||||
Building
|
||||
--
|
||||
Before you do anything, make sure you have updated your git submodules, either by running `git submodule update --init --recursive` or by cloning the repository with the `--recursive` flag.<br/>
|
||||
<br/>
|
||||
For building you will need [CMake](http://www.cmake.org/cmake/resources/software.html). Version 3.4 is required. Once you have installed CMake you should create a build directory where the project/make files should be created, **do not create them inside the source tree!**<br>
|
||||
<br>
|
||||
On Windows you can use the `cmake-gui` executable to get a nice GUI, on Unix just use `cmake`. Use the newly created directory as build directory (change to this directory on Unix) and run cmake. On Windows this can be done by clicking the `Configure` button. Choose your compiler version and hit `Generate` when configuring is done. <br>
|
||||
@@ -14,6 +17,7 @@ Depending on the makefile generator used build the `all` or `ALL_BUILD` target t
|
||||
* `FSO_BUILD_WXFRED2`: Build the wxfred2 project, requires wxWidgets
|
||||
* `FSO_FREESPACE_PATH`: Sets the path of your FreeSpace install, can be used to automatically run the generated executables with the correct working directory, this is an optional variable. By default this will use the value of the `FS2PATH` environment variable.
|
||||
* `FSO_BUILD_TOOLS`: Build some tools related to FSO
|
||||
* `CMAKE_BUILD_TYPE`: Sets the binary build type between `Debug` and `Release` (default)
|
||||
|
||||
#### Windows only variables
|
||||
* `FSO_BUILD_FRED2`: Build FRED2, requires a Visual Studio version that ships with MFC
|
||||
@@ -35,4 +39,4 @@ You should only use these variables if you know what you're doing
|
||||
* `MSVC_SIMD_INSTRUCTIONS`: The instruction set the executables will be optimized for.
|
||||
|
||||
### Generating an installation package
|
||||
CMake can automaticall generate installation packages of the project. To do this you will just need to run the `package` target of the build system and the package will be placed inside your build directory. On Windows you will have to install [NSIS](http://nsis.sourceforge.net/Main_Page) to use this.
|
||||
CMake can automatically generate installation packages of the project. To do this you will just need to run the `package` target of the build system and the package will be placed inside your build directory. On Windows you will have to install [NSIS](http://nsis.sourceforge.net/Main_Page) to use this.
|
||||
+6
-7
@@ -5,19 +5,18 @@ configuration:
|
||||
clone_depth: 10
|
||||
environment:
|
||||
matrix:
|
||||
- VS_VERSION: 12
|
||||
- BuildID: 0
|
||||
CMAKE_GENERATOR: Visual Studio 12 2013
|
||||
PlatformToolset: v120_xp
|
||||
- VS_VERSION: 14
|
||||
CMAKE_GENERATOR: Visual Studio 14 2015
|
||||
- BuildID: 1
|
||||
CMAKE_GENERATOR: Visual Studio 14 2015 Win64
|
||||
PlatformToolset: v140_xp
|
||||
before_build:
|
||||
- git submodule update --init --recursive
|
||||
- ps: . $Env:APPVEYOR_BUILD_FOLDER/ci/appveyor/check_release.ps1
|
||||
- ps: . $Env:APPVEYOR_BUILD_FOLDER/ci/appveyor/before_build.ps1
|
||||
build_script:
|
||||
- ps: . $Env:APPVEYOR_BUILD_FOLDER/ci/appveyor/build.ps1
|
||||
|
||||
test_script:
|
||||
- ps: . $Env:APPVEYOR_BUILD_FOLDER/ci/appveyor/test.ps1
|
||||
|
||||
deploy:
|
||||
- provider: GitHub
|
||||
@@ -37,4 +36,4 @@ deploy:
|
||||
publish: true
|
||||
override: true
|
||||
on:
|
||||
NightlyBuild: true # deploy on nightly push only
|
||||
NightlyBuild: true # deploy on nightly push only
|
||||
@@ -1,5 +0,0 @@
|
||||
New-Item build -type directory
|
||||
Set-Location -Path build
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$env:APPVEYOR_BUILD_FOLDER/../install" -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" `
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS=SSE2 -G "$Env:CMAKE_GENERATOR" -T "$Env:PlatformToolset" ..
|
||||
+102
-8
@@ -1,12 +1,103 @@
|
||||
|
||||
write "$env:DeployBuild"
|
||||
write "$env:DeployConfig"
|
||||
class BuildConfig {
|
||||
[string]$Generator
|
||||
[string]$PackageType
|
||||
[string]$Toolset
|
||||
[string]$SimdType
|
||||
}
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:DeployBuild)) {
|
||||
# Release build
|
||||
if (![System.Convert]::ToBoolean($env:DeployConfig)) {
|
||||
exit 0 # End the build
|
||||
$NightlyConfigurations = @(
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015 Win64";
|
||||
PackageType="Win64";
|
||||
Toolset="v140_xp";
|
||||
SimdType="SSE2";
|
||||
},
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015";
|
||||
PackageType="Win32";
|
||||
Toolset="v140_xp";
|
||||
SimdType="SSE2";
|
||||
}
|
||||
)
|
||||
$ReleaseConfigurations = @(
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015";
|
||||
PackageType="Win32";
|
||||
Toolset="v140_xp";
|
||||
SimdType="SSE2";
|
||||
}
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015";
|
||||
PackageType="Win32-AVX";
|
||||
Toolset="v140_xp";
|
||||
SimdType="AVX";
|
||||
}
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015 Win64";
|
||||
PackageType="Win64";
|
||||
Toolset="v140_xp";
|
||||
SimdType="SSE2";
|
||||
}
|
||||
[BuildConfig]@{
|
||||
Generator="Visual Studio 14 2015 Win64";
|
||||
PackageType="Win64-AVX";
|
||||
Toolset="v140_xp";
|
||||
SimdType="AVX";
|
||||
}
|
||||
)
|
||||
|
||||
$BuildConfigurations = $null
|
||||
|
||||
# Default values
|
||||
$ReleaseBuild = $false
|
||||
$NightlyBuild = $false
|
||||
$DeployBuild = $false
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REPO_TAG_NAME" -match "^release_(.*)")) {
|
||||
# Tag matches
|
||||
$ReleaseBuild = $true
|
||||
$PackageName = "fs2_open_$($matches[1])"
|
||||
$BuildConfigurations = $ReleaseConfigurations
|
||||
}
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REPO_TAG_NAME" -match "^nightly_(.*)")) {
|
||||
# Tag matches
|
||||
$NightlyBuild = $true
|
||||
$PackageName = "nightly_$($matches[1])"
|
||||
Set-AppveyorBuildVariable 'VersionName' "$($matches[1])"
|
||||
$BuildConfigurations = $NightlyConfigurations
|
||||
}
|
||||
|
||||
# Multiply by 2 so that we can add 0 or 1 for debug or release Configs
|
||||
$buildID = [convert]::ToInt32($env:BuildID) * 2
|
||||
if ($Env:Configuration -eq "Release") {
|
||||
$buildID = $buildID + 1
|
||||
}
|
||||
Write-Host "$buildID"
|
||||
|
||||
if ($ReleaseBuild -Or $NightlyBuild) {
|
||||
$DeployBuild = $true
|
||||
}
|
||||
|
||||
Set-AppveyorBuildVariable 'ReleaseBuild' "$ReleaseBuild"
|
||||
Set-AppveyorBuildVariable 'NightlyBuild' "$NightlyBuild"
|
||||
|
||||
New-Item build -type directory
|
||||
Set-Location -Path build
|
||||
|
||||
if ($DeployBuild) {
|
||||
# Release build
|
||||
if ($buildID -ge $BuildConfigurations.Length) {
|
||||
# Not needed
|
||||
exit 0
|
||||
}
|
||||
|
||||
$buildConfig = $BuildConfigurations[$buildID]
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$env:APPVEYOR_BUILD_FOLDER/../install" -DFSO_USE_SPEECH="ON" `
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$($buildConfig.SimdType)" `
|
||||
-G "$($buildConfig.Generator)" -T "$($buildConfig.Toolset)" ..
|
||||
|
||||
$Configs = @("Release", "FastDebug")
|
||||
foreach ($config in $Configs) {
|
||||
@@ -17,9 +108,12 @@ if ([System.Convert]::ToBoolean($env:DeployBuild)) {
|
||||
}
|
||||
}
|
||||
|
||||
7z a "$($env:PackageName)-builds-Win32.zip" "$env:APPVEYOR_BUILD_FOLDER/../install/*"
|
||||
Push-AppveyorArtifact "$($env:PackageName)-builds-Win32.zip"
|
||||
7z a "$($PackageName)-builds-$($buildConfig.PackageType).zip" "$env:APPVEYOR_BUILD_FOLDER/../install/*"
|
||||
Push-AppveyorArtifact "$($PackageName)-builds-$($buildConfig.PackageType).zip"
|
||||
} else {
|
||||
cmake -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="ON" -DFSO_BUILD_TESTS="ON" -DMSVC_SIMD_INSTRUCTIONS=SSE2 `
|
||||
-G "$Env:CMAKE_GENERATOR" -T "$Env:PlatformToolset" ..
|
||||
|
||||
cmake --build . --config "$Env:CONFIGURATION" -- /verbosity:minimal
|
||||
|
||||
if (! ($?)) {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
# Default values
|
||||
Set-AppveyorBuildVariable 'ReleaseBuild' 'false'
|
||||
Set-AppveyorBuildVariable 'NightlyBuild' 'false'
|
||||
Set-AppveyorBuildVariable 'DeployBuild' 'false'
|
||||
Set-AppveyorBuildVariable 'PackagePrefix' ''
|
||||
Set-AppveyorBuildVariable 'VersionName' ''
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REPO_TAG_NAME" -match "^release_(.*)")) {
|
||||
# Tag matches
|
||||
Set-AppveyorBuildVariable 'ReleaseBuild' 'true'
|
||||
Set-AppveyorBuildVariable 'PackageName' "fs2_open_$($matches[1])"
|
||||
}
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:APPVEYOR_REPO_TAG) -And ("$env:APPVEYOR_REPO_TAG_NAME" -match "^nightly_(.*)")) {
|
||||
# Tag matches
|
||||
Set-AppveyorBuildVariable 'NightlyBuild' 'true'
|
||||
Set-AppveyorBuildVariable 'PackageName' "nightly_$($matches[1])"
|
||||
Set-AppveyorBuildVariable 'VersionName' "$($matches[1])"
|
||||
}
|
||||
|
||||
if (($Env:Configuration -eq "Release") -And ($Env:VS_VERSION -eq "14")) {
|
||||
Set-AppveyorBuildVariable 'DeployConfig' 'true'
|
||||
} else {
|
||||
Set-AppveyorBuildVariable 'DeployConfig' 'false'
|
||||
}
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:ReleaseBuild) -Or [System.Convert]::ToBoolean($env:NightlyBuild)) {
|
||||
Set-AppveyorBuildVariable 'DeployBuild' 'true'
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
if ([System.Convert]::ToBoolean($env:ReleaseBuild) -Or [System.Convert]::ToBoolean($env:NightlyBuild)) {
|
||||
# Skip tests for deployment
|
||||
exit 0
|
||||
} else {
|
||||
# Appveyor doesn't have OpenAL so copy our DLLs to the output FOLDER
|
||||
$arch=""
|
||||
if ($Env:CMAKE_GENERATOR -Match "Win64") {
|
||||
$arch="win64"
|
||||
} else {
|
||||
$arch="win32"
|
||||
}
|
||||
$dllPath = "$Env:APPVEYOR_BUILD_FOLDER\lib\openal\libs\$arch\OpenAL32.dll"
|
||||
$copyDestination = "$pwd\bin\$Env:CONFIGURATION\"
|
||||
|
||||
write "Copying OpenAL DLL..."
|
||||
Copy-Item "$dllPath" "$copyDestination"
|
||||
|
||||
write "Running unit tests..."
|
||||
# Run unit tests
|
||||
&"$pwd\bin\$Env:CONFIGURATION\unittests.exe" --gtest_shuffle 2>&1
|
||||
|
||||
if (! ($?)) {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
@@ -6,20 +6,28 @@ mkdir -p build
|
||||
cd build
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
export CXXFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
CXXFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
|
||||
if [[ "$CC" =~ ^clang.*$ ]]; then
|
||||
CXXFLAGS="$CXXFLAGS -stdlib=libc++"
|
||||
fi
|
||||
|
||||
export CXXFLAGS
|
||||
export CFLAGS
|
||||
CMAKE="cmake -G Ninja -DFSO_FATAL_WARNINGS=ON"
|
||||
if [ "$BUILD_DEPLOYMENT" = true ]; then
|
||||
for config in $BUILD_CONFIGS
|
||||
do
|
||||
mkdir -p $config
|
||||
cd $config
|
||||
eval $CMAKE -DFSO_INSTALL_DEBUG_FILES=ON -DCMAKE_BUILD_TYPE=$config -DCMAKE_INSTALL_PREFIX="/tmp/release" \
|
||||
eval $CMAKE -DFSO_INSTALL_DEBUG_FILES=ON -DCMAKE_BUILD_TYPE=$config -DCMAKE_INSTALL_PREFIX="/tmp/release/$config" \
|
||||
-DFSO_BUILD_APPIMAGE=ON -DAPPIMAGE_ASSISTANT="$HOME/AppImageAssistant" \
|
||||
-DFSO_BUILD_INCLUDED_LIBS=ON ../..
|
||||
-DFSO_BUILD_INCLUDED_LIBS=ON -DFFMPEG_USE_PRECOMPILED=ON ../..
|
||||
cd ..
|
||||
done
|
||||
else
|
||||
eval $CMAKE -DCMAKE_BUILD_TYPE=$CONFIGURATION -DFSO_BUILD_TESTS=ON ..
|
||||
eval $CMAKE -DCMAKE_BUILD_TYPE=$CONFIGURATION -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON ..
|
||||
fi
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
if [ "$BUILD_DEPLOYMENT" = true ]; then
|
||||
|
||||
@@ -10,15 +10,19 @@ PACKAGE_NAME=""
|
||||
RELEASE_PATTERN="^release_(.*)$"
|
||||
NIGHTLY_PATTERN="^nightly_(.*)$"
|
||||
|
||||
if [[ "$TRAVIS_TAG" =~ $RELEASE_PATTERN ]]; then
|
||||
# These are for testing
|
||||
NIGHTLY_TEST=false
|
||||
RELEASE_TEST=false
|
||||
|
||||
if [ "$RELEASE_TEST" = true ] || [[ "$TRAVIS_TAG" =~ $RELEASE_PATTERN ]]; then
|
||||
echo "This is a release tag!";
|
||||
RELEASE_BUILD=true;
|
||||
PACKAGE_NAME="fs2_open_${BASH_REMATCH[1]}";
|
||||
fi
|
||||
if [[ "$TRAVIS_TAG" =~ $NIGHTLY_PATTERN ]]; then
|
||||
if [ "$NIGHTLY_TEST" = true ] || [[ "$TRAVIS_TAG" =~ $NIGHTLY_PATTERN ]]; then
|
||||
echo "This is a nightly tag!";
|
||||
NIGHTLY_BUILD=true;
|
||||
VERSION_NAME="${BASH_REMATCH[1]}"
|
||||
VERSION_NAME="${BASH_REMATCH[1]}";
|
||||
PACKAGE_NAME="nightly_${BASH_REMATCH[1]}";
|
||||
fi
|
||||
|
||||
@@ -27,7 +31,7 @@ if ([ "$RELEASE_BUILD" = true ] || [ "$NIGHTLY_BUILD" = true ]); then
|
||||
fi
|
||||
|
||||
if [ "$BUILD_DEPLOYMENT" = true ]; then
|
||||
if ([ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CC" = "clang" ]); then
|
||||
if ([ "$TRAVIS_OS_NAME" = "linux" ] && (! [[ "$CC" =~ ^gcc.*$ ]])); then
|
||||
echo "Skipping non-release compiler";
|
||||
exit 0;
|
||||
fi
|
||||
@@ -43,4 +47,4 @@ export NIGHTLY_BUILD
|
||||
export BUILD_DEPLOYMENT
|
||||
export BUILD_CONFIGS
|
||||
export VERSION_NAME
|
||||
export PACKAGE_NAME
|
||||
export PACKAGE_NAME
|
||||
@@ -22,7 +22,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
chmod a+x ./AppImageAssistant
|
||||
fi
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
gem install xcpretty xcpretty-travis-formatter thefox-pastebin
|
||||
gem install xcpretty
|
||||
|
||||
brew update
|
||||
brew outdated cmake || brew upgrade cmake
|
||||
|
||||
@@ -8,11 +8,11 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
for config in $BUILD_CONFIGS
|
||||
do
|
||||
cd "$config"
|
||||
ninja install appimage
|
||||
ninja appimage
|
||||
cd ..
|
||||
done
|
||||
|
||||
ls -al /tmp/release
|
||||
cp `find /tmp/release -name '*.AppImage' -print` /tmp/release
|
||||
(cd /tmp/release && tar -cvzf /tmp/builds/$PACKAGE_NAME-builds-Linux.tar.gz *.AppImage)
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
cd build
|
||||
@@ -20,7 +20,7 @@ elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
for config in $BUILD_CONFIGS
|
||||
do
|
||||
cmake --build . --config "$config" --target install | tee build.log \
|
||||
| xcpretty -f `xcpretty-travis-formatter`
|
||||
| xcpretty
|
||||
XCODE_RET=${PIPESTATUS[0]}
|
||||
if [ "$XCODE_RET" -ne "0" ]; then
|
||||
tar -cvzf build.log.tar.gz build.log
|
||||
|
||||
+2
-2
@@ -8,9 +8,9 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
ninja all
|
||||
|
||||
valgrind --leak-check=full --error-exitcode=1 --gen-suppressions=all \
|
||||
--suppressions="$TRAVIS_BUILD_DIR/ci/travis/valgrind.supp" ./bin/unittests
|
||||
--suppressions="$TRAVIS_BUILD_DIR/ci/travis/valgrind.supp" ./bin/unittests --gtest_shuffle
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
cmake --build . --config "$CONFIGURATION" | tee build.log | xcpretty -f `xcpretty-travis-formatter`
|
||||
cmake --build . --config "$CONFIGURATION" | tee build.log | xcpretty
|
||||
XCODE_RET=${PIPESTATUS[0]}
|
||||
if [ "$XCODE_RET" -ne "0" ]; then
|
||||
tar -cvzf build.log.tar.gz build.log
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
# Sample toolchain file for building for Windows from a Linux system.
|
||||
#
|
||||
# Typical usage:
|
||||
# *) install cross compiler: `sudo apt-get install mingw-w64 g++-mingw-w64`
|
||||
# *) cd build
|
||||
# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-Ubuntu-mingw64.cmake ..
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
|
||||
|
||||
# cross compilers to use for C and C++
|
||||
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
|
||||
set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)
|
||||
|
||||
# target environment on the build host system
|
||||
# set 1st to dir with the cross compiler's C/C++ headers/libs
|
||||
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
|
||||
|
||||
# modify default behavior of FIND_XXX() commands to
|
||||
# search for headers/libs in the target environment and
|
||||
# don't search for programs in the build host environment
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
@@ -1,33 +1,16 @@
|
||||
|
||||
include(CheckIncludeFile)
|
||||
|
||||
add_library(speech INTERFACE)
|
||||
|
||||
if (WIN32)
|
||||
find_package(WindowsSDK REQUIRED)
|
||||
|
||||
foreach(dir ${WINDOWSSDK_DIRS})
|
||||
get_windowssdk_library_dirs("${dir}" LIB_DIRS)
|
||||
CHECK_INCLUDE_FILE_CXX("sapi.h" HAVE_SAPI_H)
|
||||
|
||||
find_library(SPEECH_LIBRARY
|
||||
NAMES sapi
|
||||
PATHS ${LIB_DIRS}
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
if (SPEECH_LIBRARY)
|
||||
set(SDK_DIR "${dir}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (NOT SPEECH_LIBRARY)
|
||||
message(SEND_ERROR "Text to speech library could not be found! Either install it or disable the speech option.")
|
||||
return()
|
||||
if (NOT HAVE_SAPI_H)
|
||||
message(SEND_ERROR "sapi.h could not be found on your platform. Please disable speech support.")
|
||||
endif()
|
||||
|
||||
get_windowssdk_include_dirs("${SDK_DIR}" INCLUDE_DIRS)
|
||||
|
||||
target_include_directories(speech INTERFACE ${INCLUDE_DIRS})
|
||||
else()
|
||||
message(SEND_ERROR "Text to Speech is not supported on this platform!")
|
||||
endif()
|
||||
|
||||
target_compile_definitions(speech INTERFACE -DFS2_SPEECH)
|
||||
target_compile_definitions(speech INTERFACE -DFS2_SPEECH)
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
MESSAGE(STATUS "Doing Mac OSX specific things...")
|
||||
target_compile_definitions(platform INTERFACE APPLE_APP)
|
||||
SET(EXE_GUI_TYPE MACOSX_BUNDLE)
|
||||
|
||||
FIND_LIBRARY(COCOA_LIBRARY Cocoa)
|
||||
@@ -9,3 +10,25 @@ if (NOT COCOA_LIBRARY)
|
||||
endif()
|
||||
|
||||
target_link_libraries(platform INTERFACE ${COCOA_LIBRARY})
|
||||
|
||||
SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.9")
|
||||
|
||||
SET(CMAKE_SKIP_RPATH FALSE)
|
||||
SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
SET(CMAKE_INSTALL_RPATH @loader_path/../Frameworks/)
|
||||
|
||||
# Set the path where library dependencies are copied to
|
||||
SET(LIBRAY_DESTINATION "../Frameworks")
|
||||
|
||||
set(PLATFORM_MAC TRUE CACHE INTERNAL "" FORCE)
|
||||
|
||||
# Generate and don't strip debug symbols
|
||||
# These settings don't seem to generate debug symbols for code and Freespace2
|
||||
# so those settings are set manually in code and Freespace2's CMakeLists.txt files
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS "YES")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_COPY_PHASE_STRIP "NO")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT "NO")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "NO")
|
||||
|
||||
# Generate .dSYM file
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym")
|
||||
@@ -7,4 +7,4 @@ if (NOT EXECINFO_LIB)
|
||||
message(FATAL_ERROR "Couldn't find libexecinfo! Please install it and rerun the configuration.")
|
||||
else()
|
||||
target_link_libraries(platform INTERFACE "${EXECINFO_LIB}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -4,3 +4,10 @@ INCLUDE(util)
|
||||
MESSAGE(STATUS "Configuring UNIX specific things and stuff...")
|
||||
|
||||
target_compile_definitions(platform INTERFACE SCP_UNIX USE_OPENAL)
|
||||
|
||||
# Set RPATH
|
||||
set(CMAKE_SKIP_BUILD_RPATH TRUE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set(CMAKE_INSTALL_RPATH "\$ORIGIN")
|
||||
|
||||
set(PLATFORM_UNIX TRUE CACHE INTERNAL "" FORCE)
|
||||
@@ -1,20 +1,27 @@
|
||||
|
||||
MESSAGE(STATUS "Configuring Windows specific things and stuff...")
|
||||
|
||||
target_link_libraries(platform INTERFACE
|
||||
comctl32.lib
|
||||
msacm32.lib
|
||||
odbc32.lib
|
||||
odbccp32.lib
|
||||
vfw32.lib
|
||||
winmm.lib
|
||||
wsock32.lib
|
||||
Psapi.lib
|
||||
SET(WIN32_LIBS
|
||||
comctl32
|
||||
msacm32
|
||||
odbc32
|
||||
odbccp32
|
||||
vfw32
|
||||
winmm
|
||||
wsock32
|
||||
psapi
|
||||
)
|
||||
|
||||
IF (MINGW)
|
||||
SET(WIN32_LIBS ${WIN32_LIBS} mingw32)
|
||||
ENDIF (MINGW)
|
||||
|
||||
target_link_libraries(platform INTERFACE ${WIN32_LIBS})
|
||||
|
||||
target_compile_definitions(platform INTERFACE WIN32 USE_OPENAL _WINDOWS)
|
||||
|
||||
# Specify minimum Windows version for the headers (currently this is XP)
|
||||
target_compile_definitions(platform INTERFACE NTDDI_VERSION=0x05010000 _WIN32_WINNT=0x0501)
|
||||
|
||||
SET(EXE_GUI_TYPE WIN32)
|
||||
set(PLATFORM_WINDOWS TRUE CACHE INTERNAL "" FORCE)
|
||||
@@ -12,15 +12,21 @@ include(CheckTypeSize)
|
||||
CHECK_INCLUDE_FILE("execinfo.h" HAVE_EXECINFO_H)
|
||||
CHECK_INCLUDE_FILE_CXX("cxxabi.h" HAVE_CXXAPI_H)
|
||||
|
||||
# Check if we have glu.h
|
||||
FIND_PACKAGE(OpenGL REQUIRED)
|
||||
if (APPLE)
|
||||
set(GL_INCLUDE_DIR "${OPENGL_INCLUDE_DIR}/Headers")
|
||||
else()
|
||||
set(GL_INCLUDE_DIR "${OPENGL_INCLUDE_DIR}/GL")
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_INCLUDES "${GL_INCLUDE_DIR}")
|
||||
CHECK_INCLUDE_FILE("glu.h" HAVE_GLU_H)
|
||||
CHECK_TYPE_SIZE("max_align_t" MAX_ALIGN_T)
|
||||
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES "cstddef")
|
||||
CHECK_TYPE_SIZE("std::max_align_t" STD_MAX_ALIGN_T LANGUAGE CXX)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
|
||||
CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP)
|
||||
CHECK_FUNCTION_EXISTS(strncasecmp HAVE_STRNCASECMP)
|
||||
|
||||
CHECK_FUNCTION_EXISTS(_stricmp HAVE__STRICMP)
|
||||
CHECK_FUNCTION_EXISTS(_strnicmp HAVE__STRNICMP)
|
||||
|
||||
CHECK_FUNCTION_EXISTS(strlwr HAVE_STRLWR)
|
||||
|
||||
CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H)
|
||||
|
||||
set(PLATFORM_CHECK_HEADER "${GENERATED_SOURCE_DIR}/platformChecks.h")
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_LIST_DIR}/platformChecks.h.in "${PLATFORM_CHECK_HEADER}")
|
||||
@@ -5,8 +5,18 @@
|
||||
|
||||
#cmakedefine HAVE_EXECINFO_H
|
||||
#cmakedefine HAVE_CXXAPI_H
|
||||
#cmakedefine HAVE_GLU_H
|
||||
#cmakedefine HAVE_MAX_ALIGN_T
|
||||
#cmakedefine HAVE_STD_MAX_ALIGN_T
|
||||
|
||||
#cmakedefine01 IS_64BIT
|
||||
|
||||
#cmakedefine01 HAVE_STRCASECMP
|
||||
#cmakedefine01 HAVE_STRNCASECMP
|
||||
#cmakedefine01 HAVE__STRICMP
|
||||
#cmakedefine01 HAVE__STRNICMP
|
||||
|
||||
#cmakedefine01 HAVE_STRLWR
|
||||
|
||||
#cmakedefine01 HAVE_STRINGS_H
|
||||
|
||||
#endif // __PLATFORM_CHECKS_H__
|
||||
@@ -5,6 +5,11 @@ IF(FSO_CMAKE_DEBUG)
|
||||
INCLUDE(debug-vars)
|
||||
ENDIF(FSO_CMAKE_DEBUG)
|
||||
|
||||
set(PLATFORM_WINDOWS FALSE CACHE INTERNAL "" FORCE)
|
||||
set(PLATFORM_UNIX FALSE CACHE INTERNAL "" FORCE)
|
||||
set(PLATFORM_LINUX FALSE CACHE INTERNAL "" FORCE)
|
||||
set(PLATFORM_MAC FALSE CACHE INTERNAL "" FORCE)
|
||||
|
||||
IF(WIN32)
|
||||
INCLUDE(platform-win32)
|
||||
ELSEIF(UNIX)
|
||||
@@ -16,7 +21,9 @@ ELSEIF(UNIX)
|
||||
INCLUDE(platform-solaris)
|
||||
ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
include(platform-darwin)
|
||||
ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set(PLATFORM_LINUX TRUE CACHE INTERNAL "" FORCE)
|
||||
ENDIF()
|
||||
ELSE(WIN32)
|
||||
MESSAGE(STATUS "This platform is not supported, good luck!")
|
||||
ENDIF(WIN32)
|
||||
+64
-16
@@ -4,16 +4,28 @@ include(CheckCXXCompilerFlag)
|
||||
|
||||
MESSAGE(STATUS "Doing configuration specific to clang...")
|
||||
|
||||
unset(COMPILER_FLAGS)
|
||||
option(CLANG_ENABLE_LEAK_CHECK "Enable -fsanitize=leak" OFF)
|
||||
option(CLANG_ENABLE_ADDRESS_SANITIZER "Enable -fsanitize=address" OFF)
|
||||
|
||||
# These are the default values
|
||||
set(C_BASE_FLAGS "-march=native -pipe")
|
||||
set(CXX_BASE_FLAGS "-march=native -pipe")
|
||||
|
||||
# For C and C++, the values can be overwritten independently
|
||||
if(DEFINED ENV{CXXFLAGS})
|
||||
set(COMPILER_FLAGS $ENV{CXXFLAGS})
|
||||
set(CXX_BASE_FLAGS $ENV{CXXFLAGS})
|
||||
endif()
|
||||
if(DEFINED ENV{CFLAGS})
|
||||
set(C_BASE_FLAGS $ENV{CFLAGS})
|
||||
endif()
|
||||
|
||||
if(NOT COMPILER_FLAGS)
|
||||
set(COMPILER_FLAGS "-march=native -pipe")
|
||||
endif()
|
||||
# Initialize with an empty string to make sure we always get a clean start
|
||||
set(COMPILER_FLAGS "")
|
||||
|
||||
# This is a slight hack since our flag setup is a bit more complicated
|
||||
_enable_extra_compiler_warnings_flags()
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} ${_flags}")
|
||||
|
||||
globally_enable_extra_compiler_warnings()
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -funroll-loops -fsigned-char -Wno-unknown-pragmas")
|
||||
|
||||
# Omit "argument unused during compilation" when clang is used with ccache.
|
||||
@@ -26,12 +38,40 @@ if ("${CMAKE_GENERATOR}" STREQUAL "Ninja")
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -fdiagnostics-color")
|
||||
endif()
|
||||
|
||||
# Omit "deprecated conversion from string constant to 'char*'" warnings.
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-write-strings")
|
||||
# Start with an empty list
|
||||
set(SANITIZE_FLAGS)
|
||||
|
||||
if(CLANG_ENABLE_ADDRESS_SANITIZER)
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
|
||||
CHECK_C_COMPILER_FLAG("-fsanitize=address" SUPPORTS_SANITIZE_ADDRESS)
|
||||
|
||||
if (SUPPORTS_SANITIZE_ADDRESS)
|
||||
set(SANITIZE_FLAGS ${SANITIZE_FLAGS} "address")
|
||||
endif()
|
||||
endif()
|
||||
if (CLANG_ENABLE_LEAK_CHECK)
|
||||
check_linker_flag("-fsanitize=leak" SUPPORTS_FSANITIZE_LEAK)
|
||||
|
||||
if (SUPPORTS_FSANITIZE_LEAK)
|
||||
set(SANITIZE_FLAGS ${SANITIZE_FLAGS} "leak")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(REPLACE ";" "," SANITIZE_FLAGS "${SANITIZE_FLAGS}")
|
||||
if (NOT "${SANITIZE_FLAGS}" STREQUAL "")
|
||||
set(SANITIZE_FLAGS "-fsanitize=${SANITIZE_FLAGS}")
|
||||
endif()
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} ${SANITIZE_FLAGS}")
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wformat-security")
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-unused-function")
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-deprecated -Wno-char-subscripts")
|
||||
# Dear Clang, please tell us if a function does not return a value since that part of the standard is stupid!
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wreturn-type")
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-char-subscripts")
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-unused-parameter")
|
||||
|
||||
@@ -44,13 +84,9 @@ set(COMPILER_FLAGS_RELEASE "-O2 -Wno-unused-variable")
|
||||
|
||||
set(COMPILER_FLAGS_DEBUG "-O0 -g -Wshadow")
|
||||
|
||||
if (FSO_FATAL_WARNINGS)
|
||||
# Make warnings fatal if the right variable is set
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Werror")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS ${COMPILER_FLAGS})
|
||||
set(CMAKE_C_FLAGS ${COMPILER_FLAGS})
|
||||
# Always use the base flags and add our compiler flags at the bacl
|
||||
set(CMAKE_CXX_FLAGS "${CXX_BASE_FLAGS} ${COMPILER_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${C_BASE_FLAGS} ${COMPILER_FLAGS}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE ${COMPILER_FLAGS_RELEASE})
|
||||
set(CMAKE_C_FLAGS_RELEASE ${COMPILER_FLAGS_RELEASE})
|
||||
@@ -61,5 +97,17 @@ set(CMAKE_C_FLAGS_DEBUG ${COMPILER_FLAGS_DEBUG})
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "")
|
||||
|
||||
if (SANITIZE_FLAGS)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZE_FLAGS}")
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-g -rdynamic")
|
||||
|
||||
if (FSO_FATAL_WARNINGS)
|
||||
# Make warnings fatal if the right variable is set
|
||||
target_compile_options(compiler INTERFACE "-Werror")
|
||||
endif()
|
||||
|
||||
# Always define this to make sure that the fixed width format macros are available
|
||||
target_compile_definitions(compiler INTERFACE __STDC_FORMAT_MACROS)
|
||||
+56
-22
@@ -7,19 +7,33 @@ MESSAGE(STATUS "Doing configuration specific to gcc...")
|
||||
|
||||
option(GCC_ENABLE_LEAK_CHECK "Enable -fsanitize=leak" OFF)
|
||||
option(GCC_ENABLE_ADDRESS_SANITIZER "Enable -fsanitize=address" OFF)
|
||||
option(GCC_ENABLE_SANITIZE_UNDEFINED "Enable -fsanitize=undefined" OFF)
|
||||
|
||||
unset(COMPILER_FLAGS)
|
||||
# These are the default values
|
||||
set(C_BASE_FLAGS "-march=native -pipe")
|
||||
set(CXX_BASE_FLAGS "-march=native -pipe")
|
||||
|
||||
# For C and C++, the values can be overwritten independently
|
||||
if(DEFINED ENV{CFLAGS})
|
||||
set(C_BASE_FLAGS $ENV{CFLAGS})
|
||||
endif()
|
||||
if(DEFINED ENV{CXXFLAGS})
|
||||
set(COMPILER_FLAGS $ENV{CXXFLAGS})
|
||||
set(CXX_BASE_FLAGS $ENV{CXXFLAGS})
|
||||
endif()
|
||||
|
||||
if(NOT COMPILER_FLAGS)
|
||||
set(COMPILER_FLAGS "-march=native -pipe")
|
||||
endif()
|
||||
# Initialize with an empty string to make sure we always get a clean start
|
||||
set(COMPILER_FLAGS "")
|
||||
|
||||
# This is a slight hack since our flag setup is a bit more complicated
|
||||
_enable_extra_compiler_warnings_flags()
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} ${_flags}")
|
||||
|
||||
globally_enable_extra_compiler_warnings()
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -funroll-loops -fsigned-char -Wno-unknown-pragmas")
|
||||
|
||||
# Place each function and data in its own section so the linker can
|
||||
# perform dead code elimination
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -fdata-sections -ffunction-sections")
|
||||
|
||||
if ("${CMAKE_GENERATOR}" STREQUAL "Ninja")
|
||||
# Force color diagnostics for Ninja generator
|
||||
CHECK_C_COMPILER_FLAG(-fdiagnostics-color SUPPORTS_DIAGNOSTIC_COLOR)
|
||||
@@ -37,19 +51,22 @@ if(GCC_ENABLE_ADDRESS_SANITIZER)
|
||||
CHECK_C_COMPILER_FLAG("-fsanitize=address" SUPPORTS_SANITIZE_ADDRESS)
|
||||
|
||||
if (SUPPORTS_SANITIZE_ADDRESS)
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -fsanitize=address")
|
||||
set(SANITIZE_FLAGS ${SANITIZE_FLAGS} "address")
|
||||
endif()
|
||||
|
||||
set(SANITIZE_FLAGS ${SANITIZE_FLAGS} "address")
|
||||
endif()
|
||||
if (GCC_ENABLE_LEAK_CHECK)
|
||||
check_linker_flag("-fsanitize=leak" SUPPORTS_FSANITIZE_LEAK)
|
||||
|
||||
if (SUPPORTS_FSANITIZE_LEAK)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=leak")
|
||||
set(SANITIZE_FLAGS ${SANITIZE_FLAGS} "leak")
|
||||
endif()
|
||||
endif()
|
||||
if (GCC_ENABLE_SANITIZE_UNDEFINED)
|
||||
check_linker_flag("-fsanitize=undefined" SUPPORTS_SANITIZE_UNDEFINED)
|
||||
|
||||
if (SUPPORTS_SANITIZE_UNDEFINED)
|
||||
set(SANITIZE_FLAGS ${SANITIZE_FLAGS} "undefined")
|
||||
endif()
|
||||
|
||||
set(SANITIZE_FLAGS ${SANITIZE_FLAGS} "leak")
|
||||
endif()
|
||||
|
||||
string(REPLACE ";" "," SANITIZE_FLAGS "${SANITIZE_FLAGS}")
|
||||
@@ -59,11 +76,13 @@ endif()
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} ${SANITIZE_FLAGS}")
|
||||
|
||||
# Omit "deprecated conversion from string constant to 'char*'" warnings.
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-write-strings")
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wformat-security")
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-unused-function")
|
||||
|
||||
# Dear GCC, please tell us if a function does not return a value since that part of the standard is stupid!
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wreturn-type")
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-deprecated -Wno-char-subscripts")
|
||||
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Wno-unused-parameter")
|
||||
@@ -72,13 +91,9 @@ set(COMPILER_FLAGS_RELEASE "-O2 -Wno-unused-variable -Wno-unused-but-set-variabl
|
||||
|
||||
set(COMPILER_FLAGS_DEBUG "-O0 -g -Wshadow")
|
||||
|
||||
if (FSO_FATAL_WARNINGS)
|
||||
# Make warnings fatal if the right variable is set
|
||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -Werror")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS ${COMPILER_FLAGS})
|
||||
set(CMAKE_C_FLAGS ${COMPILER_FLAGS})
|
||||
# Always use the base flags and add our compiler flags at the bacl
|
||||
set(CMAKE_CXX_FLAGS "${CXX_BASE_FLAGS} ${COMPILER_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${C_BASE_FLAGS} ${COMPILER_FLAGS}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE ${COMPILER_FLAGS_RELEASE})
|
||||
set(CMAKE_C_FLAGS_RELEASE ${COMPILER_FLAGS_RELEASE})
|
||||
@@ -88,13 +103,32 @@ set(CMAKE_C_FLAGS_DEBUG ${COMPILER_FLAGS_DEBUG})
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "")
|
||||
|
||||
IF (MINGW)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++ -Wl,--enable-auto-import")
|
||||
ENDIF(MINGW)
|
||||
|
||||
if (SANITIZE_FLAGS)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZE_FLAGS}")
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-g -rdynamic")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-g")
|
||||
IF(NOT MINGW)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -rdynamic")
|
||||
# Allow the linker to perform dead code elimination; this is considered
|
||||
# experimental for COFF and PE formats and thus disabled for Windows builds
|
||||
# (https://sourceware.org/binutils/docs/ld/Options.html)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
|
||||
ENDIF(NOT MINGW)
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-zignore")
|
||||
ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
|
||||
|
||||
if (FSO_FATAL_WARNINGS)
|
||||
# Make warnings fatal if the right variable is set
|
||||
target_compile_options(compiler INTERFACE "-Werror")
|
||||
endif()
|
||||
|
||||
# Always define this to make sure that the fixed width format macros are available
|
||||
target_compile_definitions(compiler INTERFACE __STDC_FORMAT_MACROS)
|
||||
@@ -39,8 +39,8 @@ set(WARNING_FLAGS
|
||||
target_compile_options(compiler INTERFACE ${WARNING_FLAGS})
|
||||
|
||||
# Base
|
||||
set(CMAKE_C_FLAGS "/MP /GS- /analyze- /Zc:wchar_t /errorReport:prompt /WX- /Zc:forScope /Gd /EHsc /nologo")
|
||||
set(CMAKE_CXX_FLAGS "/MP /GS- /analyze- /Zc:wchar_t /errorReport:prompt /WX- /Zc:forScope /Gd /EHsc /nologo")
|
||||
set(CMAKE_C_FLAGS "/MP /GS- /analyze- /Zc:wchar_t /errorReport:prompt /WX- /Zc:forScope /Gd /EHsc /nologo /Zm200")
|
||||
set(CMAKE_CXX_FLAGS "/MP /GS- /analyze- /Zc:wchar_t /errorReport:prompt /WX- /Zc:forScope /Gd /EHsc /nologo /Zm200")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "/MANIFEST /DYNAMICBASE:NO /SAFESEH:NO /ERRORREPORT:PROMPT /NOLOGO")
|
||||
set(CMAKE_STATIC_LINKER_FLAGS "")
|
||||
@@ -134,7 +134,7 @@ else()
|
||||
endif()
|
||||
|
||||
target_compile_definitions(compiler INTERFACE _CRT_SECURE_NO_DEPRECATE
|
||||
_CRT_SECURE_NO_WARNINGS _SECURE_SCL=0 NOMINMAX)
|
||||
_CRT_SECURE_NO_WARNINGS _SECURE_SCL=0 NOMINMAX "$<$<CONFIG:FastDebug>:_ITERATOR_DEBUG_LEVEL=0>")
|
||||
|
||||
if (FSO_FATAL_WARNINGS)
|
||||
# Make warnings fatal if the right variable is set
|
||||
|
||||
@@ -30,4 +30,4 @@ if (MSVC)
|
||||
|
||||
string(REPLACE "/MD" "/MDd" CMAKE_CXX_FLAGS_FASTDEBUG "${CMAKE_CXX_FLAGS_FASTDEBUG}")
|
||||
string(REPLACE "/MT" "/MTd" CMAKE_CXX_FLAGS_FASTDEBUG "${CMAKE_CXX_FLAGS_FASTDEBUG}")
|
||||
endif()
|
||||
endif()
|
||||
+50
-17
@@ -66,13 +66,33 @@ ENDIF(EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET}/${FILE}\")
|
||||
SET(${OUTVAR} "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}/${NAME} PARENT_SCOPE)
|
||||
ENDFUNCTION(EP_CHECK_FILE_EXISTS)
|
||||
|
||||
MACRO(COPY_FILES_TO_TARGET _target)
|
||||
FOREACH(file IN LISTS TARGET_COPY_FILES)
|
||||
MACRO(COPY_FILE_TO_TARGET _target _file)
|
||||
if(UNIX)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
TARGET ${_target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${file}" "$<TARGET_FILE_DIR:${_target}>"
|
||||
COMMENT "copying '${file}'..."
|
||||
COMMAND cp -a "${_file}" "$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}/"
|
||||
COMMENT "copying '${_file}'..."
|
||||
)
|
||||
else()
|
||||
ADD_CUSTOM_COMMAND(
|
||||
TARGET ${_target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_file}" "$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}/"
|
||||
COMMENT "copying '${_file}'..."
|
||||
)
|
||||
endif()
|
||||
endmacro(COPY_FILE_TO_TARGET)
|
||||
|
||||
MACRO(COPY_FILES_TO_TARGET _target)
|
||||
if(UNIX)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
TARGET ${_target} POST_BUILD
|
||||
COMMAND mkdir -p "$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}/"
|
||||
COMMENT "Creating '$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}/'..."
|
||||
)
|
||||
endif()
|
||||
|
||||
FOREACH(file IN LISTS TARGET_COPY_FILES)
|
||||
COPY_FILE_TO_TARGET("${_target}" "${file}")
|
||||
ENDFOREACH(file)
|
||||
ENDMACRO(COPY_FILES_TO_TARGET)
|
||||
|
||||
@@ -96,7 +116,7 @@ function(CONVERT_OLD_LIBRARIES)
|
||||
else("${lib}" STREQUAL "debug")
|
||||
# Expecting normal library
|
||||
if(is_debug)
|
||||
list(APPEND out_list "$<$<CONFIG:Debug>:${lib}>")
|
||||
list(APPEND out_list "$<$<CONFIG:Debug>:${lib}>" "$<$<CONFIG:FastDebug>:${lib}>")
|
||||
elseif(is_optimized)
|
||||
list(APPEND out_list "$<$<CONFIG:Release>:${lib}>")
|
||||
else(is_debug)
|
||||
@@ -121,23 +141,26 @@ macro(set_if_not_defined VAR VALUE)
|
||||
endmacro(set_if_not_defined)
|
||||
|
||||
macro(configure_cotire target)
|
||||
IF(COTIRE_ENABLE)
|
||||
# Disable unity build as it doesn't work well for us
|
||||
set_target_properties(${target} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
|
||||
# Disable unity build as it doesn't work well for us
|
||||
set_target_properties(${target} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
|
||||
|
||||
# add ignored paths for the precompiled header here
|
||||
set_target_properties(code PROPERTIES COTIRE_PREFIX_HEADER_IGNORE_PATH
|
||||
"${CMAKE_SOURCE_DIR};${CMAKE_BINARY_DIR}")
|
||||
cotire(${target})
|
||||
ENDIF(COTIRE_ENABLE)
|
||||
cotire(${target})
|
||||
endmacro(configure_cotire)
|
||||
|
||||
macro(add_target_copy_files)
|
||||
INSTALL(FILES ${ARGN}
|
||||
DESTINATION ${BINARY_DESTINATION}
|
||||
)
|
||||
foreach(file ${ARGN})
|
||||
if (IS_DIRECTORY "${file}")
|
||||
INSTALL(DIRECTORY ${file}
|
||||
DESTINATION ${LIBRAY_DESTINATION}
|
||||
)
|
||||
else()
|
||||
INSTALL(FILES ${file}
|
||||
DESTINATION ${LIBRAY_DESTINATION}
|
||||
)
|
||||
endif()
|
||||
|
||||
SET(TARGET_COPY_FILES ${TARGET_COPY_FILES} ${ARGN} CACHE INTERNAL "" FORCE)
|
||||
SET(TARGET_COPY_FILES ${TARGET_COPY_FILES} ${file} CACHE INTERNAL "" FORCE)
|
||||
endforeach()
|
||||
endmacro(add_target_copy_files)
|
||||
|
||||
function(detect_simd_instructions _out_var)
|
||||
@@ -162,3 +185,13 @@ function (check_linker_flag _flag _out_var)
|
||||
SET(CMAKE_REQUIRED_FLAGS "${_flag}")
|
||||
CHECK_C_COMPILER_FLAG("" ${_out_var})
|
||||
endfunction(check_linker_flag)
|
||||
|
||||
# Suppresses warnings for the specified target
|
||||
function(suppress_warnings _target)
|
||||
if (MSVC)
|
||||
target_compile_options(${_target} PRIVATE "/W0")
|
||||
else()
|
||||
# Assume everything else uses GCC style options
|
||||
target_compile_options(${_target} PRIVATE "-w")
|
||||
endif()
|
||||
endfunction(suppress_warnings)
|
||||
+13
-3
@@ -19,20 +19,22 @@ TARGET_INCLUDE_DIRECTORIES(code PUBLIC ${FREESPACE_HEADERS})
|
||||
TARGET_INCLUDE_DIRECTORIES(code PUBLIC ${GENERATED_SOURCE_DIR})
|
||||
TARGET_INCLUDE_DIRECTORIES(code PUBLIC ${GENERATED_SOURCE_DIR}/code)
|
||||
|
||||
target_link_libraries(code PUBLIC gl_12)
|
||||
target_link_libraries(code PUBLIC OpenGL)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${OPENAL_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${LUA_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${PNG_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${JPEG_LIBS})
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC oggvorbis)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC sdl2)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${SDL_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ffmpeg)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC platform)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC compiler)
|
||||
|
||||
target_link_libraries(code PUBLIC md5)
|
||||
|
||||
IF (FSO_USE_SPEECH)
|
||||
find_package(Speech REQUIRED)
|
||||
target_link_libraries(code PUBLIC speech)
|
||||
@@ -51,7 +53,15 @@ IF(FSO_BUILD_TOOLS)
|
||||
ADD_SUBDIRECTORY(cfileextractor)
|
||||
ENDIF(FSO_BUILD_TOOLS)
|
||||
|
||||
set_target_properties(code PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "prefix_header.h")
|
||||
|
||||
include(util)
|
||||
configure_cotire(code)
|
||||
|
||||
include(../cmake/Cxx11.cmake)
|
||||
|
||||
# Ensure that Xcode generates debug symbols on macOS for Debug builds
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Debug] "YES")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Debug] "NO")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT[variant=Debug] "NO")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN[variant=Debug] "NO")
|
||||
+5
-28
@@ -66,19 +66,6 @@ extern ai_flag_name Ai_flag_names[];
|
||||
#define AIG_TYPE_PLAYER_WING 4 // from player direct to wing
|
||||
#define AIG_TYPE_DYNAMIC 5 // created on the fly
|
||||
|
||||
// flags for AI_GOALS
|
||||
#define AIGF_DOCKER_INDEX_VALID (1<<0) // when set, index field for docker is valid
|
||||
#define AIGF_DOCKEE_INDEX_VALID (1<<1) // when set, index field for dockee is valid
|
||||
#define AIGF_GOAL_ON_HOLD (1<<2) // when set, this goal cannot currently be satisfied, although it could be in the future
|
||||
#define AIGF_SUBSYS_NEEDS_FIXUP (1<<3) // when set, the subsystem index (for a destroy subsystem goal) is invalid and must be gotten from the subsys name stored in docker.name field!!
|
||||
#define AIGF_GOAL_OVERRIDE (1<<4) // paired with AIG_TYPE_DYNAMIC to mean this goal overrides any other goal
|
||||
#define AIGF_PURGE (1<<5) // purge this goal next time we process
|
||||
#define AIGF_GOALS_PURGED (1<<6) // this goal has already caused other goals to get purged
|
||||
#define AIGF_DEPART_SOUND_PLAYED (1<<7) // Goober5000 - replacement for AL's hack ;)
|
||||
#define AIGF_TARGET_OWN_TEAM (1<<8) // Goober5000 - removes standard precautions against AI ships taking traitorous actions
|
||||
|
||||
#define AIGF_DOCK_INDEXES_VALID (AIGF_DOCKER_INDEX_VALID|AIGF_DOCKEE_INDEX_VALID)
|
||||
|
||||
// Flags to ai_turn_towards_vector().
|
||||
#define AITTV_FAST (1<<0) // Turn fast, not slowed down based on skill level.
|
||||
#define AITTV_VIA_SEXP (1<<1) // Goober5000 - via sexp
|
||||
@@ -86,23 +73,13 @@ extern ai_flag_name Ai_flag_names[];
|
||||
|
||||
#define KAMIKAZE_HULL_ON_DEATH -1000.0f // Hull strength ship gets set to if it crash-dies.
|
||||
|
||||
// flags for possible ai overrides
|
||||
#define AIORF_FULL (1<<0) // Full sexp control
|
||||
#define AIORF_ROLL (1<<1) // Sexp forced roll maneuver
|
||||
#define AIORF_PITCH (1<<2) // Sexp forced pitch change
|
||||
#define AIORF_HEADING (1<<3) // Sexp forced heading change
|
||||
#define AIORF_FULL_LAT (1<<4) // full control over up/side/forward movement
|
||||
#define AIORF_UP (1<<5) // vertical movement
|
||||
#define AIORF_SIDEWAYS (1<<6) // horizontal movement
|
||||
#define AIORF_FORWARD (1<<7) // forward movement
|
||||
|
||||
// structure for AI goals
|
||||
typedef struct ai_goal {
|
||||
int signature; // Unique identifier. All goals ever created (per mission) have a unique signature.
|
||||
int ai_mode; // one of the AIM_* modes for this goal
|
||||
int ai_submode; // maybe need a submode
|
||||
int type; // one of the AIG_TYPE_* values above
|
||||
int flags; // one of the AIGF_* values above
|
||||
flagset<AI::Goal_Flags> flags; // one of the AIGF_* values above
|
||||
fix time; // time at which this goal was issued.
|
||||
int priority; // how important is this goal -- number 0 - 100
|
||||
|
||||
@@ -508,7 +485,7 @@ typedef struct ai_info {
|
||||
vec3d artillery_lock_pos; // base position of the lock point on (in model's frame of reference)
|
||||
float lethality; // measure of how dangerous ship is to enemy BIG|HUGE ships (likelyhood of targeting)
|
||||
|
||||
int ai_override_flags; // flags for marking ai overrides from sexp or lua systems
|
||||
flagset<AI::Maneuver_Override_Flags> ai_override_flags; // flags for marking ai overrides from sexp or lua systems
|
||||
control_info ai_override_ci; // ai override control info
|
||||
int ai_override_timestamp; // mark for when to end the current override
|
||||
} ai_info;
|
||||
@@ -582,7 +559,7 @@ void set_wingnum(int objnum, int wingnum);
|
||||
char *ai_get_goal_target_name(const char *name, int *index);
|
||||
|
||||
extern void init_ai_system(void);
|
||||
extern void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp);
|
||||
extern void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp, int ship_info_index = -1);
|
||||
extern void ai_evade_object(object *evader, object *evaded);
|
||||
extern void ai_ignore_object(object *ignorer, object *ignored, int ignore_new);
|
||||
extern void ai_ignore_wing(object *ignorer, int wingnum);
|
||||
@@ -652,7 +629,7 @@ extern int ai_maybe_fire_afterburner(object *objp, ai_info *aip);
|
||||
extern void set_predicted_enemy_pos(vec3d *predicted_enemy_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, ai_info *aip);
|
||||
|
||||
extern int is_instructor(object *objp);
|
||||
extern int find_enemy(int objnum, float range, int max_attackers);
|
||||
extern int find_enemy(int objnum, float range, int max_attackers, int ship_info_index = -1);
|
||||
|
||||
float ai_get_weapon_speed(ship_weapon *swp);
|
||||
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, vec3d *gun_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range);
|
||||
@@ -674,7 +651,7 @@ extern int find_danger_weapon(object *sobjp, float dtime, float *atime, float do
|
||||
void ai_set_mode_warp_out(object *objp, ai_info *aip);
|
||||
|
||||
// prototyped by Goober5000
|
||||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers);
|
||||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index);
|
||||
|
||||
// moved to header file by Goober5000
|
||||
void ai_announce_ship_dying(object *dying_objp);
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ namespace AI {
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
FLAG_LIST(Override_Flags) {
|
||||
FLAG_LIST(Maneuver_Override_Flags) {
|
||||
Full, // Full sexp control
|
||||
Roll, // Sexp forced roll maneuver
|
||||
Pitch, // Sexp forced pitch change
|
||||
|
||||
@@ -38,7 +38,7 @@ void set_flag(ai_profile_t *profile, const char *name, AI::Profile_Flags flag)
|
||||
}
|
||||
}
|
||||
|
||||
char *AI_path_types[] = {
|
||||
const char *AI_path_types[] = {
|
||||
"normal",
|
||||
"alt1",
|
||||
};
|
||||
@@ -345,7 +345,7 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
}
|
||||
|
||||
if (optional_string("$Detail Distance Multiplier:"))
|
||||
parse_float_list(profile->detail_distance_mult, NUM_SKILL_LEVELS);
|
||||
parse_float_list(profile->detail_distance_mult, MAX_DETAIL_LEVEL + 1);
|
||||
|
||||
set_flag(profile, "$big ships can attack beam turrets on untargeted ships:", AI::Profile_Flags::Big_ships_can_attack_beam_turrets_on_untargeted_ships);
|
||||
|
||||
@@ -616,6 +616,9 @@ void ai_profile_t::reset()
|
||||
delay_bomb_arm_timer[i] = 0;
|
||||
chance_to_use_missiles_on_plr[i] = 0;
|
||||
player_autoaim_fov[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= MAX_DETAIL_LEVEL; ++i) {
|
||||
detail_distance_mult[i] = 0;
|
||||
}
|
||||
}
|
||||
@@ -14,63 +14,6 @@
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "ai/ai_flags.h"
|
||||
|
||||
// flag int defines
|
||||
#define AIP_FLAG 1
|
||||
#define AIP_FLAG2 2
|
||||
|
||||
// flags
|
||||
#define AIPF_SMART_SHIELD_MANAGEMENT (1 << 0)
|
||||
#define AIPF_BIG_SHIPS_CAN_ATTACK_BEAM_TURRETS_ON_UNTARGETED_SHIPS (1 << 1)
|
||||
#define AIPF_SMART_PRIMARY_WEAPON_SELECTION (1 << 2)
|
||||
#define AIPF_SMART_SECONDARY_WEAPON_SELECTION (1 << 3)
|
||||
#define AIPF_ALLOW_RAPID_SECONDARY_DUMBFIRE (1 << 4)
|
||||
#define AIPF_HUGE_TURRET_WEAPONS_IGNORE_BOMBS (1 << 5)
|
||||
#define AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY (1 << 6)
|
||||
#define AIPF_HACK_IMPROVE_NON_HOMING_SWARM_TURRET_FIRE_ACCURACY (1 << 7)
|
||||
#define AIPF_SHOCKWAVES_DAMAGE_SMALL_SHIP_SUBSYSTEMS (1 << 8)
|
||||
#define AIPF_NAVIGATION_SUBSYS_GOVERNS_WARP (1 << 9)
|
||||
#define AIPF_NO_MIN_DOCK_SPEED_CAP (1 << 10)
|
||||
#define AIPF_DISABLE_LINKED_FIRE_PENALTY (1 << 11)
|
||||
#define AIPF_DISABLE_WEAPON_DAMAGE_SCALING (1 << 12)
|
||||
#define AIPF_USE_ADDITIVE_WEAPON_VELOCITY (1 << 13)
|
||||
#define AIPF_USE_NEWTONIAN_DAMPENING (1 << 14)
|
||||
#define AIPF_INCLUDE_BEAMS_IN_STAT_CALCS (1 << 15)
|
||||
#define AIPF_KILL_SCORING_SCALES_WITH_DAMAGE (1 << 16)
|
||||
#define AIPF_ASSIST_SCORING_SCALES_WITH_DAMAGE (1 << 17)
|
||||
#define AIPF_ALLOW_MULTI_EVENT_SCORING (1 << 18)
|
||||
#define AIPF_SMART_AFTERBURNER_MANAGEMENT (1 << 19)
|
||||
#define AIPF_FIX_LINKED_PRIMARY_BUG (1 << 20)
|
||||
#define AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE (1 << 21)
|
||||
#define AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS (1 << 22)
|
||||
#define AIPF_FIX_HEAT_SEEKER_STEALTH_BUG (1 << 23)
|
||||
#define AIPF_MULTI_ALLOW_EMPTY_PRIMARIES (1 << 24)
|
||||
#define AIPF_MULTI_ALLOW_EMPTY_SECONDARIES (1 << 25)
|
||||
#define AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY (1 << 26)
|
||||
#define AIPF_USE_ONLY_SINGLE_FOV_FOR_TURRETS (1 << 27)
|
||||
#define AIPF_ALLOW_VERTICAL_DODGE (1 << 28) //Allows AI ships to evade weapons vertically as well as horizontally
|
||||
#define AIPF_FORCE_BEAM_TURRET_FOV (1 << 29)
|
||||
#define AIPF_FIX_AI_CLASS_BUG (1 << 30)
|
||||
|
||||
// flags2
|
||||
#define AIPF2_TURRETS_IGNORE_TARGET_RADIUS (1 << 0)
|
||||
#define AIPF2_NO_SPECIAL_PLAYER_AVOID (1 << 1)
|
||||
#define AIPF2_PERFORM_FEWER_SCREAM_CHECKS (1 << 2)
|
||||
#define AIPF2_ALL_SHIPS_MANAGE_SHIELDS (1 << 3)
|
||||
#define AIPF2_ADVANCED_TURRET_FOV_EDGE_CHECKS (1 << 4)
|
||||
#define AIPF2_REQUIRE_TURRET_TO_HAVE_TARGET_IN_FOV (1 << 5)
|
||||
#define AIPF2_AI_AIMS_FROM_SHIP_CENTER (1 << 6)
|
||||
#define AIPF2_ALLOW_PRIMARY_LINK_AT_START (1 << 7)
|
||||
#define AIPF2_BEAMS_DAMAGE_WEAPONS (1 << 8)
|
||||
#define AIPF2_PLAYER_WEAPON_SCALE_FIX (1 << 9)
|
||||
#define AIPF2_NO_WARP_CAMERA (1 << 10)
|
||||
#define AIPF2_ASPECT_LOCK_COUNTERMEASURE (1 << 11)
|
||||
#define AIPF2_AI_GUARDS_SPECIFIC_SHIP_IN_WING (1 << 12)
|
||||
#define AIPF2_FIX_AI_PATH_ORDER_BUG (1 << 13)
|
||||
#define AIPF2_STRICT_TURRET_TAGGED_ONLY_TARGETING (1 << 14)
|
||||
#define AIPF2_ASPECT_INVULNERABILITY_FIX (1 << 15)
|
||||
#define AIPF2_GLIDE_DECAY_REQUIRES_THRUST (1 << 16)
|
||||
#define AIPF2_AI_CAN_SLOW_DOWN_ATTACKING_BIG_SHIPS (1 << 17)
|
||||
|
||||
// AI Path types
|
||||
#define AI_PATH_MODE_NORMAL 0
|
||||
#define AI_PATH_MODE_ALT1 1
|
||||
|
||||
+107
-124
@@ -105,7 +105,7 @@
|
||||
|
||||
//XSTR:OFF
|
||||
|
||||
char *Mode_text[MAX_AI_BEHAVIORS] = {
|
||||
const char *Mode_text[MAX_AI_BEHAVIORS] = {
|
||||
"CHASE",
|
||||
"EVADE",
|
||||
"GET_BEHIND",
|
||||
@@ -130,7 +130,7 @@ char *Mode_text[MAX_AI_BEHAVIORS] = {
|
||||
};
|
||||
|
||||
// Submode text is only valid for CHASE mode.
|
||||
char *Submode_text[] = {
|
||||
const char *Submode_text[] = {
|
||||
"undefined",
|
||||
"CONT_TURN",
|
||||
"ATTACK ",
|
||||
@@ -151,7 +151,7 @@ char *Submode_text[] = {
|
||||
"BIG_PARL"
|
||||
};
|
||||
|
||||
char *Strafe_submode_text[5] = {
|
||||
const char *Strafe_submode_text[5] = {
|
||||
"ATTACK",
|
||||
"AVOID",
|
||||
"RETREAT1",
|
||||
@@ -570,7 +570,7 @@ void init_ai_class(ai_class *aicp)
|
||||
aicp->ai_class_autoscale = true; //Retail behavior is to do the stupid autoscaling
|
||||
}
|
||||
|
||||
void set_aic_flag(ai_class *aicp, char *name, AI::Profile_Flags flag)
|
||||
void set_aic_flag(ai_class *aicp, const char *name, AI::Profile_Flags flag)
|
||||
{
|
||||
auto flags = &(aicp->ai_profile_flags);
|
||||
auto set = &(aicp->ai_profile_flags_set);
|
||||
@@ -780,39 +780,46 @@ void reset_ai_class_names()
|
||||
#define AI_CLASS_INCREMENT 10
|
||||
void parse_aitbl()
|
||||
{
|
||||
read_file_text("ai.tbl", CF_TYPE_TABLES);
|
||||
reset_parse();
|
||||
try {
|
||||
read_file_text("ai.tbl", CF_TYPE_TABLES);
|
||||
reset_parse();
|
||||
|
||||
//Just in case parse_aitbl is called twice
|
||||
free_ai_stuff();
|
||||
|
||||
Num_ai_classes = 0;
|
||||
Num_alloced_ai_classes = AI_CLASS_INCREMENT;
|
||||
Ai_classes = (ai_class*) vm_malloc(Num_alloced_ai_classes * sizeof(ai_class));
|
||||
Ai_class_names = (char**) vm_malloc(Num_alloced_ai_classes * sizeof(char*));
|
||||
|
||||
required_string("#AI Classes");
|
||||
//Just in case parse_aitbl is called twice
|
||||
free_ai_stuff();
|
||||
|
||||
while (required_string_either("#End", "$Name:")) {
|
||||
Num_ai_classes = 0;
|
||||
Num_alloced_ai_classes = AI_CLASS_INCREMENT;
|
||||
Ai_classes = (ai_class*) vm_malloc(Num_alloced_ai_classes * sizeof(ai_class));
|
||||
Ai_class_names = (char**) vm_malloc(Num_alloced_ai_classes * sizeof(char*));
|
||||
|
||||
parse_ai_class();
|
||||
required_string("#AI Classes");
|
||||
|
||||
Num_ai_classes++;
|
||||
while (required_string_either("#End", "$Name:")) {
|
||||
|
||||
if(Num_ai_classes >= Num_alloced_ai_classes)
|
||||
{
|
||||
Num_alloced_ai_classes += AI_CLASS_INCREMENT;
|
||||
Ai_classes = (ai_class*) vm_realloc(Ai_classes, Num_alloced_ai_classes * sizeof(ai_class));
|
||||
parse_ai_class();
|
||||
|
||||
// Ai_class_names doesn't realloc all that well so we have to do it the hard way.
|
||||
// Luckily, it's contents can be easily replaced so we don't have to save anything.
|
||||
vm_free(Ai_class_names);
|
||||
Ai_class_names = (char **) vm_malloc(Num_alloced_ai_classes * sizeof(char*));
|
||||
reset_ai_class_names();
|
||||
Num_ai_classes++;
|
||||
|
||||
if(Num_ai_classes >= Num_alloced_ai_classes)
|
||||
{
|
||||
Num_alloced_ai_classes += AI_CLASS_INCREMENT;
|
||||
Ai_classes = (ai_class*) vm_realloc(Ai_classes, Num_alloced_ai_classes * sizeof(ai_class));
|
||||
|
||||
// Ai_class_names doesn't realloc all that well so we have to do it the hard way.
|
||||
// Luckily, it's contents can be easily replaced so we don't have to save anything.
|
||||
vm_free(Ai_class_names);
|
||||
Ai_class_names = (char **) vm_malloc(Num_alloced_ai_classes * sizeof(char*));
|
||||
reset_ai_class_names();
|
||||
}
|
||||
}
|
||||
|
||||
atexit(free_ai_stuff);
|
||||
}
|
||||
catch (const parse::ParseException& e)
|
||||
{
|
||||
mprintf(("TABLES: Unable to parse 'ai.tbl'! Error message = %s.\n", e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
atexit(free_ai_stuff);
|
||||
}
|
||||
|
||||
LOCAL int ai_inited = 0;
|
||||
@@ -1206,10 +1213,9 @@ void ai_turn_towards_vector(vec3d *dest, object *objp, float frametime, float tu
|
||||
}
|
||||
|
||||
// Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
|
||||
// that is causing ships to inexplicably rotate very far. If you hit the Int3(), set the next statement to be
|
||||
// that is causing ships to inexplicably rotate very far. If you see the message below in the log, set the next statement to be
|
||||
// the one marked "HERE". (Do this clicking the cursor there, then right clicking. Choose the right option.)
|
||||
// This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
|
||||
// Note, you'll need to enable the Int3() about ten lines below.
|
||||
#ifndef NDEBUG
|
||||
vec3d tvec = objp->orient.vec.fvec;
|
||||
vec3d vel_in_copy;
|
||||
@@ -1231,13 +1237,13 @@ objp->orient = objp_orient_copy; //-V587
|
||||
} else {
|
||||
vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
|
||||
Assertion(!(delta_time < 0.25f && vm_vec_dot(&objp->orient.vec.fvec, &tvec) < 0.1f), "A ship rotated too far. Offending vessel is %s, please investigate.\n", Ships[objp->instance].ship_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
|
||||
if (delta_time < 0.25f && vm_vec_dot(&objp->orient.vec.fvec, &tvec) < 0.1f)
|
||||
mprintf(("A ship rotated too far. Offending vessel is %s, please investigate.\n", Ships[objp->instance].ship_name));
|
||||
}
|
||||
#endif
|
||||
pip->rotvel = vel_out;
|
||||
}
|
||||
|
||||
@@ -1908,6 +1914,7 @@ typedef struct eval_nearest_objnum {
|
||||
int objnum;
|
||||
object *trial_objp;
|
||||
int enemy_team_mask;
|
||||
int enemy_ship_info_index;
|
||||
int enemy_wing;
|
||||
float range;
|
||||
int max_attackers;
|
||||
@@ -1937,6 +1944,10 @@ void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
|
||||
if ((eno->enemy_wing != -1) && (shipp->wingnum != eno->enemy_wing))
|
||||
return;
|
||||
|
||||
// If only supposed to attack ships of a certain ship class, don't attack other ships.
|
||||
if ((eno->enemy_ship_info_index >= 0) && (shipp->ship_info_index != eno->enemy_ship_info_index))
|
||||
return;
|
||||
|
||||
// Don't keep firing at a ship that is in its death throes.
|
||||
if (shipp->flags[Ship::Ship_Flags::Dying])
|
||||
return;
|
||||
@@ -2024,8 +2035,9 @@ void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
|
||||
* @param enemy_wing Enemy wing chosen
|
||||
* @param range Ship must be within range "range".
|
||||
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it.
|
||||
* @param ship_info_index If >=0, the enemy object must be of the specified ship class
|
||||
*/
|
||||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
|
||||
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers, int ship_info_index)
|
||||
{
|
||||
object *danger_weapon_objp;
|
||||
ai_info *aip;
|
||||
@@ -2034,6 +2046,7 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra
|
||||
// initialize eno struct
|
||||
eval_nearest_objnum eno;
|
||||
eno.enemy_team_mask = enemy_team_mask;
|
||||
eno.enemy_ship_info_index = ship_info_index;
|
||||
eno.enemy_wing = enemy_wing;
|
||||
eno.max_attackers = max_attackers;
|
||||
eno.objnum = objnum;
|
||||
@@ -2076,7 +2089,7 @@ int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float ra
|
||||
// If only looking for target in certain wing and couldn't find anything in
|
||||
// that wing, look for any object.
|
||||
if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
|
||||
return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
|
||||
return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers, ship_info_index);
|
||||
}
|
||||
|
||||
return eno.nearest_objnum;
|
||||
@@ -2182,7 +2195,7 @@ int get_enemy_timestamp()
|
||||
* @param range Range within which to look
|
||||
* @param max_attackers Don't attack a ship that already has at least max_attackers attacking it.
|
||||
*/
|
||||
int find_enemy(int objnum, float range, int max_attackers)
|
||||
int find_enemy(int objnum, float range, int max_attackers, int ship_info_index)
|
||||
{
|
||||
int enemy_team_mask;
|
||||
|
||||
@@ -2202,8 +2215,10 @@ int find_enemy(int objnum, float range, int max_attackers)
|
||||
// This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range. (BAD)
|
||||
if ( Objects[target_objnum].signature == aip->target_signature ) {
|
||||
if (iff_matches_mask(Ships[Objects[target_objnum].instance].team, enemy_team_mask)) {
|
||||
if (!(Objects[target_objnum].flags[Object::Object_Flags::Protected])) {
|
||||
return target_objnum;
|
||||
if (ship_info_index < 0 || ship_info_index == Ships[Objects[target_objnum].instance].ship_info_index) {
|
||||
if (!(Objects[target_objnum].flags[Object::Object_Flags::Protected])) {
|
||||
return target_objnum;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2212,7 +2227,7 @@ int find_enemy(int objnum, float range, int max_attackers)
|
||||
}
|
||||
}
|
||||
|
||||
return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
|
||||
return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers, ship_info_index);
|
||||
|
||||
} else {
|
||||
aip->target_objnum = -1;
|
||||
@@ -2253,7 +2268,7 @@ void force_avoid_player_check(object *objp, ai_info *aip)
|
||||
* If attacked == NULL, then attack any enemy object.
|
||||
* Attack point *rel_pos on object. This is for supporting attacking subsystems.
|
||||
*/
|
||||
void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp)
|
||||
void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp, int ship_info_index)
|
||||
{
|
||||
int temp;
|
||||
ai_info *aip;
|
||||
@@ -2281,9 +2296,9 @@ void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp)
|
||||
if (attacked == NULL) {
|
||||
aip->choose_enemy_timestamp = timestamp(0);
|
||||
// nebula safe
|
||||
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4));
|
||||
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4, ship_info_index));
|
||||
} else {
|
||||
// check if we can see atacked in nebula
|
||||
// check if we can see attacked in nebula
|
||||
if (aip->target_objnum != OBJ_INDEX(attacked)) {
|
||||
aip->aspect_locked_time = 0.0f;
|
||||
}
|
||||
@@ -3799,7 +3814,7 @@ float ai_path_0()
|
||||
int path_num;
|
||||
Assert(aip->active_goal >= 0);
|
||||
ai_goal *aigp = &aip->goals[aip->active_goal];
|
||||
Assert(aigp->flags & AIGF_DOCK_INDEXES_VALID);
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] || aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
|
||||
path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aigp->dockee.index);
|
||||
ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
|
||||
@@ -3942,7 +3957,7 @@ float ai_path_1()
|
||||
int path_num;
|
||||
Assert(aip->active_goal >= 0);
|
||||
ai_goal *aigp = &aip->goals[aip->active_goal];
|
||||
Assert(aigp->flags & AIGF_DOCK_INDEXES_VALID);
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] || aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
|
||||
path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aigp->dockee.index);
|
||||
ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
|
||||
@@ -8545,7 +8560,7 @@ void ai_chase()
|
||||
aip->submode = AIS_CHASE_CIRCLESTRAFE;
|
||||
aip->submode_start_time = Missiontime;
|
||||
aip->last_attack_time = Missiontime;
|
||||
} else if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.8f) && (enemy_sip->is_small_ship()) && (Missiontime - aip->submode_start_time > i2f(5) )) {
|
||||
} else if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.8f) && (enemy_sip != NULL) && (enemy_sip->is_small_ship()) && (Missiontime - aip->submode_start_time > i2f(5) )) {
|
||||
aip->ai_flags.remove(AI::AI_Flags::Attack_slowly); // Just in case, clear here.
|
||||
|
||||
float get_away_chance = (aip->ai_get_away_chance == FLT_MIN)
|
||||
@@ -8978,6 +8993,8 @@ void set_goal_dock_orient(matrix *dom, vec3d *docker_p0, vec3d *docker_p1, vec3d
|
||||
// Return the rotating submodel on which is mounted the specified dockpoint, or -1 for none.
|
||||
int find_parent_rotating_submodel(polymodel *pm, int dock_index)
|
||||
{
|
||||
Assertion(pm != nullptr, "pm cannot be null!");
|
||||
Assertion(dock_index >= 0 && dock_index < pm->n_docks, "for model %s, dock_index %d must be >= 0 and < %d!", pm->filename, dock_index, pm->n_docks);
|
||||
int path_num, submodel;
|
||||
|
||||
// make sure we have a spline path to check against before going any further
|
||||
@@ -9006,9 +9023,33 @@ int find_parent_rotating_submodel(polymodel *pm, int dock_index)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
// pruned version of below function
|
||||
void find_adjusted_dockpoint_normal(vec3d *global_p0_norm, object *objp, polymodel *pm, int submodel, int dock_index)
|
||||
{
|
||||
Assertion(global_p0_norm != nullptr && objp != nullptr && pm != nullptr, "arguments cannot be null!");
|
||||
Assertion(dock_index >= 0 && dock_index < pm->n_docks, "for model %s, dock_index %d must be >= 0 and < %d!", pm->filename, dock_index, pm->n_docks);
|
||||
|
||||
// are we basing this off a rotating submodel?
|
||||
if (submodel >= 0)
|
||||
{
|
||||
// find the normal of the first dockpoint
|
||||
model_instance_find_world_dir(global_p0_norm, &pm->docking_bays[dock_index].norm[0], Ships[objp->instance].model_instance_num, submodel, &objp->orient);
|
||||
}
|
||||
// use the static dockpoints
|
||||
else
|
||||
{
|
||||
vm_vec_unrotate(global_p0_norm, &pm->docking_bays[dock_index].norm[0], &objp->orient);
|
||||
}
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
void find_adjusted_dockpoint_info(vec3d *global_p0, vec3d *global_p1, vec3d *global_p0_norm, object *objp, polymodel *pm, int modelnum, int submodel, int dock_index)
|
||||
{
|
||||
Assertion(global_p0 != nullptr && global_p1 != nullptr && global_p0_norm != nullptr && objp != nullptr && pm != nullptr, "arguments cannot be null!");
|
||||
Assertion(pm->id == modelnum, "inconsistent polymodel and modelnum!");
|
||||
Assertion(dock_index >= 0 && dock_index < pm->n_docks, "for model %s, dock_index %d must be >= 0 and < %d!", pm->filename, dock_index, pm->n_docks);
|
||||
|
||||
// are we basing this off a rotating submodel?
|
||||
if (submodel >= 0)
|
||||
{
|
||||
@@ -10358,7 +10399,7 @@ void ai_get_dock_goal_indexes(object *objp, ai_info *aip, ai_goal *aigp, object
|
||||
{
|
||||
// get them from the active goal
|
||||
Assert(aigp != NULL);
|
||||
Assert(aigp->flags & AIGF_DOCK_INDEXES_VALID);
|
||||
Assert(aigp->flags[AI::Goal_Flags::Dockee_index_valid] || aigp->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
docker_index = aigp->docker.index;
|
||||
dockee_index = aigp->dockee.index;
|
||||
Assert(docker_index >= 0);
|
||||
@@ -10937,10 +10978,10 @@ void ai_dock()
|
||||
break;
|
||||
|
||||
// play the depart sound, but only once, since this mode is called multiple times per frame
|
||||
if ( !(aigp->flags & AIGF_DEPART_SOUND_PLAYED))
|
||||
if ( !(aigp->flags[AI::Goal_Flags::Depart_sound_played]))
|
||||
{
|
||||
snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
|
||||
aigp->flags |= AIGF_DEPART_SOUND_PLAYED;
|
||||
aigp->flags.set(AI::Goal_Flags::Depart_sound_played);
|
||||
}
|
||||
|
||||
dist = dock_orient_and_approach(Pl_objp, docker_index, goal_objp, dockee_index, DOA_UNDOCK_1, &rdinfo);
|
||||
@@ -12229,70 +12270,12 @@ void ai_chase_circle(object *objp)
|
||||
*/
|
||||
void ai_transfer_shield(object *objp, int quadrant_num)
|
||||
{
|
||||
int i;
|
||||
float transfer_amount;
|
||||
float transfer_delta;
|
||||
float max_quadrant_strength;
|
||||
|
||||
max_quadrant_strength = get_max_shield_quad(objp);
|
||||
|
||||
transfer_amount = 0.0f;
|
||||
transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
|
||||
|
||||
if (objp->shield_quadrant[quadrant_num] + (objp->n_quadrants-1)*transfer_delta > max_quadrant_strength)
|
||||
transfer_delta = (max_quadrant_strength - objp->shield_quadrant[quadrant_num])/(objp->n_quadrants-1);
|
||||
|
||||
for (i=0; i<objp->n_quadrants; i++)
|
||||
if (i != quadrant_num) {
|
||||
if (objp->shield_quadrant[i] >= transfer_delta) {
|
||||
objp->shield_quadrant[i] -= transfer_delta;
|
||||
transfer_amount += transfer_delta;
|
||||
} else {
|
||||
transfer_amount += objp->shield_quadrant[i];
|
||||
objp->shield_quadrant[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
objp->shield_quadrant[quadrant_num] += transfer_amount;
|
||||
shield_transfer(objp, quadrant_num, (SHIELD_BALANCE_RATE / 2));
|
||||
}
|
||||
|
||||
void ai_balance_shield(object *objp)
|
||||
{
|
||||
int i;
|
||||
float shield_strength_avg;
|
||||
float delta;
|
||||
|
||||
// if we are already at the max shield strength for all quads then just bail now
|
||||
if ( Ships[objp->instance].ship_max_shield_strength == shield_get_strength(objp) )
|
||||
return;
|
||||
|
||||
|
||||
shield_strength_avg = shield_get_strength(objp)/objp->n_quadrants;
|
||||
|
||||
delta = SHIELD_BALANCE_RATE * shield_strength_avg;
|
||||
|
||||
for (i=0; i<objp->n_quadrants; i++) {
|
||||
if (objp->shield_quadrant[i] < shield_strength_avg) {
|
||||
// only do it the retail way if using smart shields (since that's a bigger thing) - taylor
|
||||
if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags[AI::Profile_Flags::Smart_shield_management])
|
||||
shield_add_strength(objp, delta);
|
||||
else
|
||||
objp->shield_quadrant[i] += delta/objp->n_quadrants;
|
||||
|
||||
if (objp->shield_quadrant[i] > shield_strength_avg)
|
||||
objp->shield_quadrant[i] = shield_strength_avg;
|
||||
|
||||
} else {
|
||||
// only do it the retail way if using smart shields (since that's a bigger thing) - taylor
|
||||
if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags[AI::Profile_Flags::Smart_shield_management])
|
||||
shield_add_strength(objp, -delta);
|
||||
else
|
||||
objp->shield_quadrant[i] -= delta/objp->n_quadrants;
|
||||
|
||||
if (objp->shield_quadrant[i] < shield_strength_avg)
|
||||
objp->shield_quadrant[i] = shield_strength_avg;
|
||||
}
|
||||
}
|
||||
shield_balance(objp, SHIELD_BALANCE_RATE, 0.0f);
|
||||
}
|
||||
|
||||
// Manage the shield for this ship.
|
||||
@@ -13856,7 +13839,7 @@ int ai_need_new_target(object *pl_objp, int target_objnum)
|
||||
// Goober5000 - targeting the same team is allowed if pl_objp is going bonkers
|
||||
ai_info *pl_aip = &Ai_info[Ships[pl_objp->instance].ai_index];
|
||||
if (pl_aip->active_goal != AI_GOAL_NONE && pl_aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) {
|
||||
if (pl_aip->goals[pl_aip->active_goal].flags & AIGF_TARGET_OWN_TEAM) {
|
||||
if (pl_aip->goals[pl_aip->active_goal].flags[AI::Goal_Flags::Target_own_team]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -14169,46 +14152,46 @@ void ai_frame(int objnum)
|
||||
|
||||
void ai_control_info_check( object *obj, ai_info *aip )
|
||||
{
|
||||
if(aip->ai_override_flags == 0)
|
||||
if(aip->ai_override_flags.none_set())
|
||||
return;
|
||||
|
||||
if(timestamp_elapsed(aip->ai_override_timestamp)) {
|
||||
aip->ai_override_flags = 0;
|
||||
aip->ai_override_flags.reset();
|
||||
} else {
|
||||
if(aip->ai_override_flags & AIORF_FULL)
|
||||
if(aip->ai_override_flags[AI::Maneuver_Override_Flags::Full])
|
||||
{
|
||||
AI_ci.pitch = aip->ai_override_ci.pitch;
|
||||
AI_ci.heading = aip->ai_override_ci.heading;
|
||||
AI_ci.bank = aip->ai_override_ci.bank;
|
||||
} else {
|
||||
if(aip->ai_override_flags & AIORF_PITCH)
|
||||
if(aip->ai_override_flags[AI::Maneuver_Override_Flags::Pitch])
|
||||
{
|
||||
AI_ci.pitch = aip->ai_override_ci.pitch;
|
||||
}
|
||||
if(aip->ai_override_flags & AIORF_HEADING)
|
||||
if(aip->ai_override_flags[AI::Maneuver_Override_Flags::Heading])
|
||||
{
|
||||
AI_ci.heading = aip->ai_override_ci.heading;
|
||||
}
|
||||
if(aip->ai_override_flags & AIORF_ROLL)
|
||||
if(aip->ai_override_flags[AI::Maneuver_Override_Flags::Roll])
|
||||
{
|
||||
AI_ci.bank = aip->ai_override_ci.bank;
|
||||
}
|
||||
}
|
||||
if(aip->ai_override_flags & AIORF_FULL_LAT)
|
||||
if(aip->ai_override_flags[AI::Maneuver_Override_Flags::Full_lat])
|
||||
{
|
||||
AI_ci.vertical = aip->ai_override_ci.vertical;
|
||||
AI_ci.sideways = aip->ai_override_ci.sideways;
|
||||
AI_ci.forward = aip->ai_override_ci.forward;
|
||||
} else {
|
||||
if(aip->ai_override_flags & AIORF_UP)
|
||||
if(aip->ai_override_flags[AI::Maneuver_Override_Flags::Up])
|
||||
{
|
||||
AI_ci.vertical = aip->ai_override_ci.vertical;
|
||||
}
|
||||
if(aip->ai_override_flags & AIORF_SIDEWAYS)
|
||||
if(aip->ai_override_flags[AI::Maneuver_Override_Flags::Sideways])
|
||||
{
|
||||
AI_ci.sideways = aip->ai_override_ci.sideways;
|
||||
}
|
||||
if(aip->ai_override_flags & AIORF_FORWARD)
|
||||
if(aip->ai_override_flags[AI::Maneuver_Override_Flags::Forward])
|
||||
{
|
||||
AI_ci.forward = aip->ai_override_ci.forward;
|
||||
}
|
||||
@@ -14440,7 +14423,7 @@ void init_ai_object(int objnum)
|
||||
|
||||
// set lethality to enemy team
|
||||
aip->lethality = 0.0f;
|
||||
aip->ai_override_flags = 0;
|
||||
aip->ai_override_flags.reset();
|
||||
memset(&aip->ai_override_ci,0,sizeof(control_info));
|
||||
}
|
||||
|
||||
|
||||
+124
-75
@@ -59,6 +59,7 @@ int Ai_goal_signature;
|
||||
int Num_ai_dock_names = 0;
|
||||
char Ai_dock_names[MAX_AI_DOCK_NAMES][NAME_LENGTH];
|
||||
|
||||
// Used in objecttypes.tbl to define custom ship types
|
||||
ai_goal_list Ai_goal_names[] =
|
||||
{
|
||||
{ "Attack ship", AI_GOAL_CHASE, 0 },
|
||||
@@ -85,6 +86,7 @@ ai_goal_list Ai_goal_names[] =
|
||||
{ "Play dead", AI_GOAL_PLAY_DEAD, 0 },
|
||||
{ "Attack weapon", AI_GOAL_CHASE_WEAPON, 0 },
|
||||
{ "Fly to ship", AI_GOAL_FLY_TO_SHIP, 0 },
|
||||
{ "Attack ship class", AI_GOAL_CHASE_SHIP_CLASS, 0 },
|
||||
};
|
||||
|
||||
int Num_ai_goals = sizeof(Ai_goal_names) / sizeof(ai_goal_list);
|
||||
@@ -96,34 +98,34 @@ int Num_ai_goals = sizeof(Ai_goal_names) / sizeof(ai_goal_list);
|
||||
const char *Ai_goal_text(int goal)
|
||||
{
|
||||
switch(goal) {
|
||||
case 1:
|
||||
case AI_GOAL_CHASE:
|
||||
case AI_GOAL_CHASE_WING:
|
||||
case AI_GOAL_CHASE_SHIP_CLASS:
|
||||
return XSTR( "attack ", 474);
|
||||
case 2:
|
||||
case AI_GOAL_DOCK:
|
||||
return XSTR( "dock ", 475);
|
||||
case 3:
|
||||
case AI_GOAL_WAYPOINTS:
|
||||
case AI_GOAL_WAYPOINTS_ONCE:
|
||||
return XSTR( "waypoints", 476);
|
||||
case 4:
|
||||
return XSTR( "waypoints", 476);
|
||||
case 6:
|
||||
case AI_GOAL_DESTROY_SUBSYSTEM:
|
||||
return XSTR( "destroy ", 477);
|
||||
case 7:
|
||||
case AI_GOAL_FORM_ON_WING:
|
||||
return XSTR( "form on ", 478);
|
||||
case 8:
|
||||
case AI_GOAL_UNDOCK:
|
||||
return XSTR( "undock ", 479);
|
||||
case 9:
|
||||
return XSTR( "attack ", 474);
|
||||
case 10:
|
||||
case AI_GOAL_GUARD:
|
||||
case AI_GOAL_GUARD_WING:
|
||||
return XSTR( "guard ", 480);
|
||||
case 11:
|
||||
case AI_GOAL_DISABLE_SHIP:
|
||||
return XSTR( "disable ", 481);
|
||||
case 12:
|
||||
case AI_GOAL_DISARM_SHIP:
|
||||
return XSTR( "disarm ", 482);
|
||||
case 15:
|
||||
return XSTR( "guard ", 480);
|
||||
case 16:
|
||||
case AI_GOAL_EVADE_SHIP:
|
||||
return XSTR( "evade ", 483);
|
||||
case 19:
|
||||
case AI_GOAL_REARM_REPAIR:
|
||||
return XSTR( "rearm ", 484);
|
||||
case AI_GOAL_FLY_TO_SHIP:
|
||||
return XSTR( "rendezvous with ", -1);
|
||||
}
|
||||
|
||||
// Avoid compiler warning
|
||||
@@ -238,7 +240,7 @@ void ai_remove_ship_goal( ai_info *aip, int index )
|
||||
aip->goals[index].ai_mode = AI_GOAL_NONE;
|
||||
aip->goals[index].signature = -1;
|
||||
aip->goals[index].priority = -1;
|
||||
aip->goals[index].flags = 0; // must reset the flags since not doing so will screw up goal sorting.
|
||||
aip->goals[index].flags.reset(); // must reset the flags since not doing so will screw up goal sorting.
|
||||
|
||||
if ( index == aip->active_goal )
|
||||
aip->active_goal = AI_GOAL_NONE;
|
||||
@@ -287,7 +289,7 @@ void ai_clear_wing_goals( int wingnum )
|
||||
wingp->ai_goals[i].ai_mode = AI_GOAL_NONE;
|
||||
wingp->ai_goals[i].signature = -1;
|
||||
wingp->ai_goals[i].priority = -1;
|
||||
wingp->ai_goals[i].flags = 0;
|
||||
wingp->ai_goals[i].flags.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -347,7 +349,7 @@ void ai_mission_wing_goal_complete( int wingnum, ai_goal *remove_goalp )
|
||||
wingp->ai_goals[i].ai_mode = AI_GOAL_NONE;
|
||||
wingp->ai_goals[i].signature = -1;
|
||||
wingp->ai_goals[i].priority = -1;
|
||||
wingp->ai_goals[i].flags = 0;
|
||||
wingp->ai_goals[i].flags.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -408,31 +410,41 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai
|
||||
if ( purge_goal->target_name == NULL )
|
||||
continue;
|
||||
|
||||
// determine if the purge goal is acting either on the ship or the ship's wing.
|
||||
purge_wing = wing_name_lookup( purge_goal->target_name, 1 );
|
||||
|
||||
// if the target of the purge goal is a ship (purge_wing will be -1), then if the names
|
||||
// don't match, we can continue; if the wing is valid, don't process if the wing numbers
|
||||
// are different.
|
||||
if ( purge_wing == -1 ) {
|
||||
if ( stricmp(purge_goal->target_name, name ) )
|
||||
// goals operating on ship classes are handled slightly differently
|
||||
if ( purge_ai_mode == AI_GOAL_CHASE_SHIP_CLASS ) {
|
||||
// if the target of the purge goal is the same class of ship we are concerned about, then we have a match;
|
||||
// if it is not, then we can continue (see standard ship check below)
|
||||
if ( stricmp(purge_goal->target_name, Ship_info[Ships[ship_index].ship_info_index].name) )
|
||||
continue;
|
||||
} else if ( purge_wing != wingnum )
|
||||
continue;
|
||||
}
|
||||
// standard goals operating on either wings or ships
|
||||
else {
|
||||
// determine if the purge goal is acting either on the ship or the ship's wing.
|
||||
purge_wing = wing_name_lookup( purge_goal->target_name, 1 );
|
||||
|
||||
// if the target of the purge goal is a ship (purge_wing will be -1), then if the names
|
||||
// don't match, we can continue; if the wing is valid, don't process if the wing numbers
|
||||
// are different.
|
||||
if ( purge_wing == -1 ) {
|
||||
if ( stricmp(purge_goal->target_name, name ) )
|
||||
continue;
|
||||
} else if ( purge_wing != wingnum )
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
// ignore goals should get rid of any kind of attack goal
|
||||
case AI_GOAL_IGNORE:
|
||||
case AI_GOAL_IGNORE_NEW:
|
||||
if ( purge_ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_DESTROY_SUBSYSTEM) )
|
||||
purge_goal->flags |= AIGF_PURGE;
|
||||
if ( purge_ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_CHASE_SHIP_CLASS | AI_GOAL_DESTROY_SUBSYSTEM) )
|
||||
purge_goal->flags.set(AI::Goal_Flags::Purge);
|
||||
break;
|
||||
|
||||
// disarm/disable goals should remove attacks from certain ships types
|
||||
case AI_GOAL_DISARM_SHIP:
|
||||
case AI_GOAL_DISABLE_SHIP:
|
||||
if ( purge_ai_mode & (AI_GOAL_CHASE | AI_GOAL_CHASE_WING) ) {
|
||||
if ( purge_ai_mode & (AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_CHASE_SHIP_CLASS) ) {
|
||||
int ai_ship_type;
|
||||
|
||||
// for wings we grab the ship type of the wing leader
|
||||
@@ -452,7 +464,7 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai
|
||||
// being disarmed/disabled
|
||||
for ( j=0 ; j < (int)crippled_ships_type->ai_cripple_ignores.size(); j++) {
|
||||
if (crippled_ships_type->ai_cripple_ignores[j] == ai_ship_type) {
|
||||
purge_goal->flags |= AIGF_PURGE;
|
||||
purge_goal->flags.set(AI::Goal_Flags::Purge);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -601,7 +613,8 @@ void ai_goal_fixup_dockpoints(ai_info *aip, ai_goal *aigp)
|
||||
|
||||
aigp->docker.index = docker_index;
|
||||
aigp->dockee.index = dockee_index;
|
||||
aigp->flags |= AIGF_DOCK_INDEXES_VALID;
|
||||
aigp->flags.set(AI::Goal_Flags::Dockee_index_valid);
|
||||
aigp->flags.set(AI::Goal_Flags::Docker_index_valid);
|
||||
}
|
||||
|
||||
// these functions deal with adding goals sent from the player. They are slightly different
|
||||
@@ -739,7 +752,8 @@ void ai_add_ship_goal_scripting(int mode, int submode, int priority, char *shipn
|
||||
if ( mode == AI_GOAL_DOCK ) {
|
||||
aigp->docker.name = Ships[aip->shipnum].ship_name;
|
||||
aigp->dockee.name = shipname;
|
||||
aigp->flags &= ~AIGF_DOCK_INDEXES_VALID;
|
||||
aigp->flags.remove(AI::Goal_Flags::Dockee_index_valid);
|
||||
aigp->flags.remove(AI::Goal_Flags::Docker_index_valid);
|
||||
}
|
||||
ai_add_goal_sub_scripting(AIG_TYPE_PLAYER_SHIP, mode, submode, priority, shipname, aigp);
|
||||
|
||||
@@ -818,7 +832,7 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name )
|
||||
aigp->target_name = NULL;
|
||||
aigp->time = Missiontime;
|
||||
aigp->type = type;
|
||||
aigp->flags = 0;
|
||||
aigp->flags.reset();
|
||||
|
||||
op = get_operator_const( text );
|
||||
|
||||
@@ -851,7 +865,7 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name )
|
||||
// store the name of the subsystem in the docker.name field for now -- this field must get
|
||||
// fixed up when the goal is valid since we need to locate the subsystem on the ship's model
|
||||
aigp->docker.name = ai_get_goal_target_name(CTEXT(CDR(CDR(node))), &dummy);
|
||||
aigp->flags |= AIGF_SUBSYS_NEEDS_FIXUP;
|
||||
aigp->flags.set(AI::Goal_Flags::Subsys_needs_fixup);
|
||||
aigp->priority = atoi( CTEXT(CDR(CDR(CDR(node)))) );
|
||||
break;
|
||||
|
||||
@@ -905,7 +919,8 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name )
|
||||
aigp->target_name = ai_get_goal_target_name( CTEXT(CDR(node)), &aigp->target_name_index );
|
||||
aigp->docker.name = ai_add_dock_name(CTEXT(CDR(CDR(node))));
|
||||
aigp->dockee.name = ai_add_dock_name(CTEXT(CDR(CDR(CDR(node)))));
|
||||
aigp->flags &= ~AIGF_DOCK_INDEXES_VALID;
|
||||
aigp->flags.remove(AI::Goal_Flags::Dockee_index_valid);
|
||||
aigp->flags.remove(AI::Goal_Flags::Docker_index_valid);
|
||||
aigp->priority = atoi( CTEXT(CDR(CDR(CDR(CDR(node))))) );
|
||||
|
||||
aigp->ai_mode = AI_GOAL_DOCK;
|
||||
@@ -934,9 +949,10 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name )
|
||||
break;
|
||||
|
||||
case OP_AI_CHASE:
|
||||
case OP_AI_CHASE_WING:
|
||||
case OP_AI_CHASE_SHIP_CLASS:
|
||||
case OP_AI_GUARD:
|
||||
case OP_AI_GUARD_WING:
|
||||
case OP_AI_CHASE_WING:
|
||||
case OP_AI_EVADE_SHIP:
|
||||
case OP_AI_STAY_NEAR_SHIP:
|
||||
case OP_AI_IGNORE:
|
||||
@@ -965,6 +981,8 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name )
|
||||
aigp->ai_mode = AI_GOAL_GUARD_WING;
|
||||
} else if ( op == OP_AI_CHASE_WING ) {
|
||||
aigp->ai_mode = AI_GOAL_CHASE_WING;
|
||||
} else if (op == OP_AI_CHASE_SHIP_CLASS) {
|
||||
aigp->ai_mode = AI_GOAL_CHASE_SHIP_CLASS;
|
||||
} else if ( op == OP_AI_STAY_NEAR_SHIP ) {
|
||||
aigp->ai_mode = AI_GOAL_STAY_NEAR_SHIP;
|
||||
} else if ( op == OP_AI_IGNORE ) {
|
||||
@@ -986,13 +1004,13 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name )
|
||||
}
|
||||
|
||||
// Goober5000 - we now have an extra optional chase argument to allow chasing our own team
|
||||
if ( op == OP_AI_CHASE || op == OP_AI_CHASE_WING || op == OP_AI_DISABLE_SHIP || op == OP_AI_DISARM_SHIP ) {
|
||||
if ( op == OP_AI_CHASE || op == OP_AI_CHASE_WING || op == OP_AI_CHASE_SHIP_CLASS || op == OP_AI_DISABLE_SHIP || op == OP_AI_DISARM_SHIP ) {
|
||||
if ((CDDDR(node) != -1) && is_sexp_true(CDDDR(node)))
|
||||
aigp->flags |= AIGF_TARGET_OWN_TEAM;
|
||||
aigp->flags.set(AI::Goal_Flags::Target_own_team);
|
||||
}
|
||||
if ( op == OP_AI_DESTROY_SUBSYS ) {
|
||||
if ((CDDDDR(node) != -1) && is_sexp_true(CDDDDR(node)))
|
||||
aigp->flags |= AIGF_TARGET_OWN_TEAM;
|
||||
aigp->flags.set(AI::Goal_Flags::Target_own_team);
|
||||
}
|
||||
|
||||
// Goober5000 - since none of the goals act on the actor,
|
||||
@@ -1004,7 +1022,7 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name )
|
||||
aigp->ai_mode = AI_GOAL_NONE;
|
||||
aigp->signature = -1;
|
||||
aigp->priority = -1;
|
||||
aigp->flags = 0;
|
||||
aigp->flags.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1032,7 +1050,7 @@ int ai_find_goal_index( ai_goal* aigp, int mode, int submode, int priority )
|
||||
|
||||
/* Remove a goal from the given goals structure
|
||||
* Returns the index of the goal that it clears out.
|
||||
* This is importnat so that if active_goal == index you can set AI_GOAL_NONE
|
||||
* This is important so that if active_goal == index you can set AI_GOAL_NONE
|
||||
*/
|
||||
int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
|
||||
{
|
||||
@@ -1134,6 +1152,10 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
|
||||
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
|
||||
goalmode = AI_GOAL_CHASE_WING;
|
||||
break;
|
||||
case OP_AI_CHASE_SHIP_CLASS:
|
||||
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
|
||||
goalmode = AI_GOAL_CHASE_SHIP_CLASS;
|
||||
break;
|
||||
case OP_AI_EVADE_SHIP:
|
||||
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
|
||||
goalmode = AI_GOAL_EVADE_SHIP;
|
||||
@@ -1166,7 +1188,7 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
|
||||
aigp[goalindex].ai_mode = AI_GOAL_NONE;
|
||||
aigp[goalindex].signature = -1;
|
||||
aigp[goalindex].priority = -1;
|
||||
aigp[goalindex].flags = 0; // must reset the flags since not doing so will screw up goal sorting.
|
||||
aigp[goalindex].flags.reset(); // must reset the flags since not doing so will screw up goal sorting.
|
||||
|
||||
return goalindex;
|
||||
}
|
||||
@@ -1264,7 +1286,8 @@ void ai_add_goal_ship_internal( ai_info *aip, int goal_type, char *name, int doc
|
||||
|
||||
aigp->time = Missiontime;
|
||||
aigp->type = AIG_TYPE_DYNAMIC;
|
||||
aigp->flags = AIGF_GOAL_OVERRIDE;
|
||||
aigp->flags.reset();
|
||||
aigp->flags.set(AI::Goal_Flags::Goal_override);
|
||||
|
||||
switch ( goal_type ) {
|
||||
|
||||
@@ -1302,7 +1325,7 @@ void ai_add_goal_ship_internal( ai_info *aip, int goal_type, char *name, int doc
|
||||
aigp->ai_submode = 0;
|
||||
aigp->target_name = name;
|
||||
aigp->priority = PLAYER_PRIORITY_MIN-1; // make the priority always less than what the player's is
|
||||
aigp->flags &= ~AIGF_GOAL_OVERRIDE; // don't override this goal. rearm repair requests should happen in order
|
||||
aigp->flags.remove(AI::Goal_Flags::Goal_override); // don't override this goal. rearm repair requests should happen in order
|
||||
ai_goal_fixup_dockpoints( aip, aigp );
|
||||
break;
|
||||
|
||||
@@ -1413,7 +1436,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
if (sindex < 0)
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
|
||||
aigp->flags |= AIGF_GOAL_OVERRIDE;
|
||||
aigp->flags.set(AI::Goal_Flags::Goal_override);
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
}
|
||||
|
||||
@@ -1431,6 +1454,18 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
}
|
||||
|
||||
// chasing all ships of a certain ship class is achievable if there are ships of that class present;
|
||||
// if not, the status is not known because more ships of that class could arrive in the future
|
||||
// (c.f. AI_GOAL_CHASE_WING subsequent to the next switch statement)
|
||||
if ( aigp->ai_mode == AI_GOAL_CHASE_SHIP_CLASS ) {
|
||||
for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
|
||||
if ((objp->type == OBJ_SHIP) && !strcmp(aigp->target_name, Ship_info[Ships[objp->instance].ship_info_index].name)) {
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
}
|
||||
}
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
}
|
||||
|
||||
|
||||
return_val = AI_GOAL_SATISFIED;
|
||||
|
||||
@@ -1456,7 +1491,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
|
||||
// can't determine the status of this goal if ship not valid
|
||||
// or we haven't found a valid subsystem index yet
|
||||
if ( (sindex == -1) || (aigp->flags & AIGF_SUBSYS_NEEDS_FIXUP) ) {
|
||||
if ( (sindex == -1) || (aigp->flags[AI::Goal_Flags::Subsys_needs_fixup]) ) {
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
@@ -1620,15 +1655,15 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
|
||||
// if the goal is an ignore/disable/disarm goal, then
|
||||
// Goober5000 - see note at PURGE_GOALS_ALL_SHIPS... this is bizarre
|
||||
if ((status == SHIP_STATUS_ARRIVED) && !(aigp->flags & AIGF_GOALS_PURGED))
|
||||
if ((status == SHIP_STATUS_ARRIVED) && !(aigp->flags[AI::Goal_Flags::Goals_purged]))
|
||||
{
|
||||
if (aigp->ai_mode & PURGE_GOALS_ALL_SHIPS) {
|
||||
ai_goal_purge_all_invalid_goals(aigp);
|
||||
aigp->flags |= AIGF_GOALS_PURGED;
|
||||
aigp->flags.set(AI::Goal_Flags::Goals_purged);
|
||||
}
|
||||
else if (aigp->ai_mode & PURGE_GOALS_ONE_SHIP) {
|
||||
ai_goal_purge_invalid_goals(aigp, aip->goals, aip, -1);
|
||||
aigp->flags |= AIGF_GOALS_PURGED;
|
||||
aigp->flags.set(AI::Goal_Flags::Goals_purged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1636,21 +1671,21 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
// only enter this calculation if the ship we are docking with has arrived. If the ship is gone, then
|
||||
// this goal will get removed.
|
||||
if ( (aigp->ai_mode == AI_GOAL_DOCK) && (status == SHIP_STATUS_ARRIVED) ) {
|
||||
if (!(aigp->flags & AIGF_DOCKER_INDEX_VALID)) {
|
||||
if (!(aigp->flags[AI::Goal_Flags::Docker_index_valid])) {
|
||||
modelnum = Ship_info[Ships[objp->instance].ship_info_index].model_num;
|
||||
Assert( modelnum >= 0 );
|
||||
index = model_find_dock_name_index(modelnum, aigp->docker.name);
|
||||
aigp->docker.index = index;
|
||||
aigp->flags |= AIGF_DOCKER_INDEX_VALID;
|
||||
aigp->flags.set(AI::Goal_Flags::Docker_index_valid);
|
||||
}
|
||||
|
||||
if (!(aigp->flags & AIGF_DOCKEE_INDEX_VALID)) {
|
||||
if (!(aigp->flags[AI::Goal_Flags::Dockee_index_valid])) {
|
||||
sindex = ship_name_lookup(aigp->target_name);
|
||||
if ( sindex != -1 ) {
|
||||
modelnum = Ship_info[Ships[sindex].ship_info_index].model_num;
|
||||
index = model_find_dock_name_index(modelnum, aigp->dockee.name);
|
||||
aigp->dockee.index = index;
|
||||
aigp->flags |= AIGF_DOCKEE_INDEX_VALID;
|
||||
aigp->flags.set(AI::Goal_Flags::Dockee_index_valid);
|
||||
} else
|
||||
aigp->dockee.index = -1; // this will force code into if statement below making goal not achievable.
|
||||
}
|
||||
@@ -1700,7 +1735,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
else if (obstacle_objp != goal_objp)
|
||||
{
|
||||
// if this goal isn't on hold yet, then issue the undock goal
|
||||
if ( !(aigp->flags & AIGF_GOAL_ON_HOLD) )
|
||||
if ( !(aigp->flags[AI::Goal_Flags::Goal_on_hold]) )
|
||||
ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, Ships[obstacle_objp->instance].ship_name, -1, -1, 0 );
|
||||
|
||||
// return NOT_KNOWN which will place the goal on hold until the undocking is complete.
|
||||
@@ -1725,12 +1760,12 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
// if the ship has arrived, and the goal is destroy subsystem, then check to see that we
|
||||
// have fixed up the subsystem name (of the subsystem to destroy) into an index into
|
||||
// the ship's subsystem list
|
||||
if ( aigp->flags & AIGF_SUBSYS_NEEDS_FIXUP ) {
|
||||
if ( aigp->flags[AI::Goal_Flags::Subsys_needs_fixup] ) {
|
||||
sindex = ship_name_lookup( aigp->target_name );
|
||||
|
||||
if ( sindex != -1 ) {
|
||||
aigp->ai_submode = ship_get_subsys_index( &Ships[sindex], aigp->docker.name );
|
||||
aigp->flags &= ~AIGF_SUBSYS_NEEDS_FIXUP;
|
||||
aigp->flags.remove(AI::Goal_Flags::Subsys_needs_fixup);
|
||||
} else {
|
||||
Int3();
|
||||
return AI_GOAL_NOT_ACHIEVABLE; // force this goal to be invalid
|
||||
@@ -1852,17 +1887,17 @@ int ai_goal_priority_compare(const void *a, const void *b)
|
||||
// If the flag is set, don't push the goal higher in the list even if priority
|
||||
// is higher since goal cannot currently be achieved.
|
||||
|
||||
if ( (ga->flags & AIGF_GOAL_ON_HOLD) && !(gb->flags & AIGF_GOAL_ON_HOLD) )
|
||||
if ( (ga->flags[AI::Goal_Flags::Goal_on_hold]) && !(gb->flags[AI::Goal_Flags::Goal_on_hold]) )
|
||||
return 1;
|
||||
else if ( !(ga->flags & AIGF_GOAL_ON_HOLD) && (gb->flags & AIGF_GOAL_ON_HOLD) )
|
||||
else if ( !(ga->flags[AI::Goal_Flags::Goal_on_hold]) && (gb->flags[AI::Goal_Flags::Goal_on_hold]) )
|
||||
return -1;
|
||||
|
||||
// check whether or not the goal override flag is set. If it is set, then push this goal higher
|
||||
// in the list
|
||||
|
||||
else if ( (ga->flags & AIGF_GOAL_OVERRIDE) && !(gb->flags & AIGF_GOAL_OVERRIDE) )
|
||||
else if ( (ga->flags[AI::Goal_Flags::Goal_override]) && !(gb->flags[AI::Goal_Flags::Goal_override]) )
|
||||
return -1;
|
||||
else if ( !(ga->flags & AIGF_GOAL_OVERRIDE) && (gb->flags & AIGF_GOAL_OVERRIDE) )
|
||||
else if ( !(ga->flags[AI::Goal_Flags::Goal_override]) && (gb->flags[AI::Goal_Flags::Goal_override]) )
|
||||
return 1;
|
||||
|
||||
// now normal priority processing
|
||||
@@ -1917,7 +1952,7 @@ void validate_mission_goals(int objnum, ai_info *aip)
|
||||
continue;
|
||||
|
||||
// purge any goals which should get purged
|
||||
if ( aigp->flags & AIGF_PURGE ) {
|
||||
if ( aigp->flags[AI::Goal_Flags::Purge] ) {
|
||||
ai_remove_ship_goal( aip, i );
|
||||
continue;
|
||||
}
|
||||
@@ -1931,19 +1966,19 @@ void validate_mission_goals(int objnum, ai_info *aip)
|
||||
}
|
||||
|
||||
// if the status is achievable, and the on_hold flag is set, clear the flagb
|
||||
if ( (state == AI_GOAL_ACHIEVABLE) && (aigp->flags & AIGF_GOAL_ON_HOLD) )
|
||||
aigp->flags &= ~AIGF_GOAL_ON_HOLD;
|
||||
if ( (state == AI_GOAL_ACHIEVABLE) && (aigp->flags[AI::Goal_Flags::Goal_on_hold]) )
|
||||
aigp->flags.remove(AI::Goal_Flags::Goal_on_hold);
|
||||
|
||||
// if the goal is not known, then set the ON_HOLD flag so that it doesn't get counted as
|
||||
// a goal to be pursued
|
||||
if ( state == AI_GOAL_NOT_KNOWN )
|
||||
aigp->flags |= AIGF_GOAL_ON_HOLD; // put this goal on hold until it becomes true
|
||||
if (state == AI_GOAL_NOT_KNOWN)
|
||||
aigp->flags.set(AI::Goal_Flags::Goal_on_hold); // put this goal on hold until it becomes true
|
||||
}
|
||||
|
||||
// if we had an active goal, and that goal is now in hold, make the mode AIM_NONE. If a new valid
|
||||
// goal is produced after prioritizing, then the mode will get reset immediately. Otherwise, setting
|
||||
// the mode to none will force ship to do default behavior.
|
||||
if ( (aip->goals[0].ai_mode != AI_GOAL_NONE) && (aip->goals[0].flags & AIGF_GOAL_ON_HOLD) )
|
||||
if ( (aip->goals[0].ai_mode != AI_GOAL_NONE) && (aip->goals[0].flags[AI::Goal_Flags::Goal_on_hold]) )
|
||||
aip->mode = AIM_NONE;
|
||||
|
||||
// if the active goal is a rearm/repair goal, the put all other valid goals (which are not repair goals)
|
||||
@@ -1952,7 +1987,7 @@ void validate_mission_goals(int objnum, ai_info *aip)
|
||||
for ( i = 1; i < MAX_AI_GOALS; i++ ) {
|
||||
if ( (aip->goals[i].ai_mode == AI_GOAL_NONE) || (aip->goals[i].ai_mode == AI_GOAL_REARM_REPAIR) )
|
||||
continue;
|
||||
aip->goals[i].flags |= AIGF_GOAL_ON_HOLD;
|
||||
aip->goals[i].flags.set(AI::Goal_Flags::Goal_on_hold);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2020,7 +2055,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
|
||||
// if the first goal in the list has the ON_HOLD flag, set, there is no current valid goal
|
||||
// to pursue.
|
||||
if ( aip->goals[0].flags & AIGF_GOAL_ON_HOLD )
|
||||
if ( aip->goals[0].flags[AI::Goal_Flags::Goal_on_hold] )
|
||||
return;
|
||||
|
||||
// Kind of a hack for now. active_goal means the goal currently being pursued.
|
||||
@@ -2116,7 +2151,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
|
||||
// be sure that we have indices for docking points here! If we ever had names, they should
|
||||
// get fixed up in goal_achievable so that the points can be checked there for validity
|
||||
Assert (current_goal->flags & AIGF_DOCK_INDEXES_VALID);
|
||||
Assert (current_goal->flags[AI::Goal_Flags::Dockee_index_valid] && current_goal->flags[AI::Goal_Flags::Docker_index_valid]);
|
||||
ai_dock_with_object( objp, current_goal->docker.index, other_obj, current_goal->dockee.index, AIDO_DOCK );
|
||||
break;
|
||||
}
|
||||
@@ -2210,7 +2245,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
|
||||
case AI_GOAL_CHASE_WING:
|
||||
wingnum = wing_name_lookup( current_goal->target_name );
|
||||
Assert( wingnum >= 0 );
|
||||
Assertion( wingnum >= 0, "The target of AI_GOAL_CHASE_WING must refer to a valid wing!" );
|
||||
ai_attack_wing(objp, wingnum);
|
||||
break;
|
||||
|
||||
@@ -2218,6 +2253,13 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
ai_attack_object( objp, NULL, NULL );
|
||||
break;
|
||||
|
||||
// chase-ship-class is chase-any but restricted to a subset of ships
|
||||
case AI_GOAL_CHASE_SHIP_CLASS:
|
||||
shipnum = ship_info_lookup( current_goal->target_name );
|
||||
Assertion( shipnum >= 0, "The target of AI_GOAL_CHASE_SHIP_CLASS must refer to a valid ship class!" );
|
||||
ai_attack_object( objp, NULL, NULL, shipnum );
|
||||
break;
|
||||
|
||||
case AI_GOAL_WARP: {
|
||||
mission_do_departure( objp, true );
|
||||
break;
|
||||
@@ -2347,15 +2389,22 @@ void ai_update_goal_references(ai_goal *goals, int type, const char *old_name, c
|
||||
flag = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Warning(LOCATION, "unhandled FRED reference type %d in ai_update_goal_references", type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (flag) // is this a valid goal to parse for this conversion?
|
||||
if (!stricmp(goals[i].target_name, old_name)) {
|
||||
{
|
||||
if (!stricmp(goals[i].target_name, old_name))
|
||||
{
|
||||
if (*new_name == '<') // target was just deleted..
|
||||
goals[i].ai_mode = AI_GOAL_NONE;
|
||||
else
|
||||
goals[i].target_name = ai_get_goal_target_name(new_name, &dummy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -26,6 +26,7 @@ struct ai_goal;
|
||||
// Fred. If the goal you add doesn't have a target (such as chase_any), then you don't have
|
||||
// to worry about doing this. Also add it to list in Fred\Management.cpp, and let Hoffoss know!
|
||||
// WMC - Oh and add them to Ai_goal_names plz. TY! :)
|
||||
// Goober5000 - As well as Ai_goal_text and Ai_goal_list, if appropriate
|
||||
#define AI_GOAL_CHASE (1<<1) // 0x00000002
|
||||
#define AI_GOAL_DOCK (1<<2) // 0x00000004 // used for undocking as well
|
||||
#define AI_GOAL_WAYPOINTS (1<<3) // 0x00000008
|
||||
@@ -51,10 +52,12 @@ struct ai_goal;
|
||||
// resume regular goals
|
||||
#define AI_GOAL_STAY_STILL (1<<20) // 0x00100000
|
||||
#define AI_GOAL_PLAY_DEAD (1<<21) // 0x00200000
|
||||
#define AI_GOAL_CHASE_WEAPON (1<<22) // 0x00400000
|
||||
|
||||
// added by SCP
|
||||
#define AI_GOAL_CHASE_WEAPON (1<<22) // 0x00400000
|
||||
#define AI_GOAL_FLY_TO_SHIP (1<<23) // 0x00800000
|
||||
#define AI_GOAL_IGNORE_NEW (1<<24) // 0x01000000
|
||||
#define AI_GOAL_CHASE_SHIP_CLASS (1<<25) // 0x02000000
|
||||
|
||||
// now the masks for ship types
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "network/multi.h"
|
||||
#include "network/multimsgs.h"
|
||||
#include "object/objectdock.h"
|
||||
#include "parse/scripting.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "render/3d.h"
|
||||
#include "ship/ship.h"
|
||||
#include "ship/shipfx.h"
|
||||
@@ -47,7 +47,7 @@ float Player_lethality_bump[NUM_SKILL_LEVELS] = {
|
||||
0.0f, 0.0f, 0.0f, 0.0f, 0.0f
|
||||
};
|
||||
|
||||
char *Turret_target_order_names[NUM_TURRET_ORDER_TYPES] = {
|
||||
const char *Turret_target_order_names[NUM_TURRET_ORDER_TYPES] = {
|
||||
"Bombs",
|
||||
"Ships",
|
||||
"Asteroids",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "pcxutils/pcxutils.h"
|
||||
#include "render/3d.h"
|
||||
#include "graphics/material.h"
|
||||
#include "tracing/Monitor.h"
|
||||
|
||||
static color Color_xparent;
|
||||
|
||||
@@ -686,7 +687,7 @@ void anim_read_header(anim *ptr, CFILE *fp)
|
||||
* @details Memory-mapped files will page in the animation from disk as it is needed, but performance is not as good.
|
||||
* @return Pointer to anim that is loaded if sucess, NULL if failure.
|
||||
*/
|
||||
anim *anim_load(char *real_filename, int cf_dir_type, int file_mapped)
|
||||
anim *anim_load(const char *real_filename, int cf_dir_type, int file_mapped)
|
||||
{
|
||||
anim *ptr;
|
||||
CFILE *fp;
|
||||
|
||||
@@ -59,7 +59,7 @@ int anim_stop_playing(anim_instance* anim_instance);
|
||||
int anim_show_next_frame(anim_instance *instance, float frametime);
|
||||
void anim_release_all_instances(int screen_id = 0);
|
||||
void anim_release_render_instance(anim_instance* instance);
|
||||
anim *anim_load(char *name, int cf_dir_type = CF_TYPE_ANY, int file_mapped = PAGE_FROM_MEM);
|
||||
anim *anim_load(const char *name, int cf_dir_type = CF_TYPE_ANY, int file_mapped = PAGE_FROM_MEM);
|
||||
int anim_free(anim *ptr);
|
||||
int anim_playing(anim_instance *ai);
|
||||
int anim_write_frames_out(char *filename);
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "object/objcollide.h"
|
||||
#include "object/object.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "parse/scripting.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "particle/particle.h"
|
||||
#include "render/3d.h"
|
||||
#include "ship/ship.h"
|
||||
@@ -1180,7 +1180,7 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
|
||||
}
|
||||
}
|
||||
|
||||
void asteroid_render(object * obj, draw_list *scene)
|
||||
void asteroid_render(object * obj, model_draw_list *scene)
|
||||
{
|
||||
if (Asteroids_enabled) {
|
||||
int num;
|
||||
@@ -1916,7 +1916,7 @@ void asteroid_parse_tbl()
|
||||
char impact_ani_file[MAX_FILENAME_LEN];
|
||||
|
||||
// How did we get here without having any species defined?
|
||||
Assertion(Species_info.size() > 0,
|
||||
Assertion(!Species_info.empty(),
|
||||
"Cannot parse asteroids/debris if there "
|
||||
"are no species for them to belong to."
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
class object;
|
||||
class polymodel;
|
||||
struct collision_info_struct;
|
||||
class draw_list;
|
||||
class model_draw_list;
|
||||
|
||||
#define MAX_ASTEROIDS 512
|
||||
|
||||
@@ -152,7 +152,7 @@ void asteroid_init();
|
||||
void asteroid_level_init();
|
||||
void asteroid_level_close();
|
||||
void asteroid_create_all();
|
||||
void asteroid_render(object * obj, draw_list *scene);
|
||||
void asteroid_render(object * obj, model_draw_list *scene);
|
||||
void asteroid_delete( object *asteroid_objp );
|
||||
void asteroid_process_pre( object *asteroid_objp );
|
||||
void asteroid_process_post( object *asteroid_objp);
|
||||
|
||||
@@ -950,7 +950,7 @@ void EndAutoPilot()
|
||||
aigp->ai_mode = AI_GOAL_NONE;
|
||||
aigp->signature = -1;
|
||||
aigp->priority = -1;
|
||||
aigp->flags = 0;
|
||||
aigp->flags.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -974,7 +974,7 @@ void EndAutoPilot()
|
||||
aigp->ai_mode = AI_GOAL_NONE;
|
||||
aigp->signature = -1;
|
||||
aigp->priority = -1;
|
||||
aigp->flags = 0;
|
||||
aigp->flags.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1272,7 +1272,7 @@ void NavSystem_Init()
|
||||
|
||||
// ********************************************************************************************
|
||||
|
||||
void parse_autopilot_table(char *filename)
|
||||
void parse_autopilot_table(const char *filename)
|
||||
{
|
||||
SCP_vector<SCP_string> lines;
|
||||
|
||||
@@ -1315,7 +1315,7 @@ void parse_autopilot_table(char *filename)
|
||||
Cmdline_autopilot_interruptable = 0;
|
||||
|
||||
// No Nav selected message
|
||||
char *msg_tags[] = { "$No Nav Selected:", "$Gliding:",
|
||||
const char *msg_tags[] = { "$No Nav Selected:", "$Gliding:",
|
||||
"$Too Close:", "$Hostiles:", "$Linked:", "$Hazard:",
|
||||
"$Support Present:", "$Support Working:" };
|
||||
for (int i = 0; i < NP_NUM_MESSAGES; i++)
|
||||
|
||||
@@ -118,7 +118,7 @@ void NavSystem_Do();
|
||||
void NavSystem_Init();
|
||||
|
||||
// parse autopilot.tbl
|
||||
void parse_autopilot_table(char *filename);
|
||||
void parse_autopilot_table(const char *filename);
|
||||
|
||||
// Finds a Nav point by name
|
||||
int FindNav(char *Nav);
|
||||
|
||||
+17
-7
@@ -29,12 +29,13 @@
|
||||
#include "io/timer.h"
|
||||
#include "jpgutils/jpgutils.h"
|
||||
#include "network/multiutil.h"
|
||||
#include "palman/palman.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "pcxutils/pcxutils.h"
|
||||
#include "pngutils/pngutils.h"
|
||||
#include "ship/ship.h"
|
||||
#include "tgautils/tgautils.h"
|
||||
#include "tracing/Monitor.h"
|
||||
#include "tracing/tracing.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
@@ -55,7 +56,6 @@ MONITOR(SizeBitmapPage)
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// Definition of public variables (declared as extern in bmpman.h).
|
||||
int UNLITMAP = -1;
|
||||
int GLOWMAP = -1;
|
||||
int SPECMAP = -1;
|
||||
int SPECGLOSSMAP = -1;
|
||||
@@ -419,7 +419,7 @@ DCF(bmpman, "Shows/changes bitmap caching parameters and usage") {
|
||||
}
|
||||
|
||||
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
||||
dc_printf("Total RAM usage: %d bytes\n", bm_texture_ram);
|
||||
dc_printf("Total RAM usage: " SIZE_T_ARG " bytes\n", bm_texture_ram);
|
||||
|
||||
if (Bm_max_ram > 1024 * 1024) {
|
||||
dc_printf("\tMax RAM allowed: %.1f MB\n", i2fl(Bm_max_ram) / (1024.0f*1024.0f));
|
||||
@@ -435,14 +435,14 @@ DCF(bmpman, "Shows/changes bitmap caching parameters and usage") {
|
||||
|
||||
|
||||
if (dc_optional_string("flush")) {
|
||||
dc_printf("Total RAM usage before flush: %d bytes\n", bm_texture_ram);
|
||||
dc_printf("Total RAM usage before flush: " SIZE_T_ARG " bytes\n", bm_texture_ram);
|
||||
int i;
|
||||
for (i = 0; i < MAX_BITMAPS; i++) {
|
||||
if (bm_bitmaps[i].type != BM_TYPE_NONE) {
|
||||
bm_free_data(i);
|
||||
}
|
||||
}
|
||||
dc_printf("Total RAM after flush: %d bytes\n", bm_texture_ram);
|
||||
dc_printf("Total RAM after flush: " SIZE_T_ARG " bytes\n", bm_texture_ram);
|
||||
} else if (dc_optional_string("ram")) {
|
||||
dc_stuff_int(&Bm_max_ram);
|
||||
|
||||
@@ -525,6 +525,7 @@ int bm_create(int bpp, int w, int h, void *data, int flags) {
|
||||
bm_bitmaps[n].bm.h = (short)h;
|
||||
bm_bitmaps[n].bm.rowsize = (short)w;
|
||||
bm_bitmaps[n].bm.bpp = (ubyte)bpp;
|
||||
bm_bitmaps[n].bm.true_bpp = (ubyte)bpp;
|
||||
bm_bitmaps[n].bm.flags = (ubyte)flags;
|
||||
bm_bitmaps[n].bm.data = 0;
|
||||
bm_bitmaps[n].bm.palette = NULL;
|
||||
@@ -2158,7 +2159,7 @@ void bm_lock_apng(int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, int
|
||||
cumulative_frame_delay += the_apng->frame.delay;
|
||||
be->info.ani.apng.frame_delay = cumulative_frame_delay;
|
||||
|
||||
nprintf(("apng", "locking apng frame: %s (%i|%i|%i) (%f) %lu\n", be->filename, bpp, bmp->bpp, bm->true_bpp, be->info.ani.apng.frame_delay, be->mem_taken));
|
||||
nprintf(("apng", "locking apng frame: %s (%i|%i|%i) (%f) " SIZE_T_ARG "\n", be->filename, bpp, bmp->bpp, bm->true_bpp, be->info.ani.apng.frame_delay, be->mem_taken));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2279,7 +2280,7 @@ void bm_lock_pcx(int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, int b
|
||||
data = (ubyte *)bm_malloc(bitmapnum, be->mem_taken);
|
||||
bmp->bpp = bpp;
|
||||
bmp->data = (ptr_u)data;
|
||||
bmp->palette = (bpp == 8) ? gr_palette : NULL;
|
||||
bmp->palette = NULL;
|
||||
memset(data, 0, be->mem_taken);
|
||||
|
||||
Assert(&be->bm == bmp);
|
||||
@@ -2577,6 +2578,8 @@ void bm_page_in_start() {
|
||||
}
|
||||
|
||||
void bm_page_in_stop() {
|
||||
TRACE_SCOPE(tracing::PageInStop);
|
||||
|
||||
int i;
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -2593,6 +2596,7 @@ void bm_page_in_stop() {
|
||||
for (i = 0; i < MAX_BITMAPS; i++) {
|
||||
if ((bm_bitmaps[i].type != BM_TYPE_NONE) && (bm_bitmaps[i].type != BM_TYPE_RENDER_TARGET_DYNAMIC) && (bm_bitmaps[i].type != BM_TYPE_RENDER_TARGET_STATIC)) {
|
||||
if (bm_bitmaps[i].preloaded) {
|
||||
TRACE_SCOPE(tracing::PageInSingleBitmap);
|
||||
if (bm_preloading) {
|
||||
if (!gr_preload(bm_bitmaps[i].handle, (bm_bitmaps[i].preloaded == 2))) {
|
||||
mprintf(("Out of VRAM. Done preloading.\n"));
|
||||
@@ -2994,6 +2998,8 @@ void bm_set_low_mem(int mode) {
|
||||
}
|
||||
|
||||
bool bm_set_render_target(int handle, int face) {
|
||||
GR_DEBUG_SCOPE("Set render target");
|
||||
|
||||
int n = handle % MAX_BITMAPS;
|
||||
|
||||
if (n >= 0) {
|
||||
@@ -3077,6 +3083,10 @@ int bm_unload(int handle, int clear_render_targets, bool nodebug) {
|
||||
bitmap_entry *be;
|
||||
bitmap *bmp;
|
||||
|
||||
if (handle == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int n = handle % MAX_BITMAPS;
|
||||
|
||||
Assert((n >= 0) && (n < MAX_BITMAPS));
|
||||
|
||||
@@ -26,7 +26,7 @@ camid Main_camera;
|
||||
|
||||
//*************************CLASS: camera*************************
|
||||
//This is where the camera class beings! :D
|
||||
camera::camera(char *in_name, int in_signature)
|
||||
camera::camera(const char *in_name, int in_signature)
|
||||
{
|
||||
set_name(in_name);
|
||||
sig = in_signature;
|
||||
@@ -78,7 +78,7 @@ void camera::reset()
|
||||
}
|
||||
}
|
||||
|
||||
void camera::set_name(char *in_name)
|
||||
void camera::set_name(const char *in_name)
|
||||
{
|
||||
if(in_name != NULL)
|
||||
strncpy(name, in_name, NAME_LENGTH-1);
|
||||
@@ -885,14 +885,14 @@ int cam_get_next_sig()
|
||||
return next_sig++;
|
||||
}
|
||||
|
||||
camid cam_create(char *n_name, vec3d *n_pos, vec3d *n_norm, object *n_object, int n_object_host_submodel)
|
||||
camid cam_create(const char *n_name, vec3d *n_pos, vec3d *n_norm, object *n_object, int n_object_host_submodel)
|
||||
{
|
||||
matrix ori;
|
||||
vm_vector_2_matrix_norm(&ori, n_norm);
|
||||
return cam_create(n_name, n_pos, &ori, n_object, n_object_host_submodel);
|
||||
}
|
||||
|
||||
camid cam_create(char *n_name, vec3d *n_pos, matrix *n_ori, object *n_object, int n_object_host_submodel)
|
||||
camid cam_create(const char *n_name, vec3d *n_pos, matrix *n_ori, object *n_object, int n_object_host_submodel)
|
||||
{
|
||||
camera *cam = NULL;
|
||||
camid cid;
|
||||
|
||||
@@ -43,13 +43,13 @@ protected:
|
||||
vec3d c_pos;
|
||||
matrix c_ori;
|
||||
public:
|
||||
camera(char *in_name=NULL, int in_signature=-1);
|
||||
camera(const char *in_name=NULL, int in_signature=-1);
|
||||
~camera();
|
||||
void clear();
|
||||
void reset();
|
||||
|
||||
//Set
|
||||
void set_name(char *in_name);
|
||||
void set_name(const char *in_name);
|
||||
|
||||
void set_object_host(object *objp, int n_object_host_submodel = -1);
|
||||
void set_object_target(object *objp, int n_object_target_submodel = -1);
|
||||
@@ -158,8 +158,8 @@ extern float Sexp_fov;
|
||||
void cam_init();
|
||||
void cam_close();
|
||||
void cam_do_frame(float frametime);
|
||||
camid cam_create(char *n_name=NULL, vec3d *n_pos=NULL, matrix *n_ori=NULL, object *n_object=NULL, int n_submodel_parent=-1);
|
||||
camid cam_create(char *n_name, vec3d *n_pos, vec3d *n_norm, object *n_object=NULL, int n_submodel_parent=-1);
|
||||
camid cam_create(const char *n_name=NULL, vec3d *n_pos=NULL, matrix *n_ori=NULL, object *n_object=NULL, int n_submodel_parent=-1);
|
||||
camid cam_create(const char *n_name, vec3d *n_pos, vec3d *n_norm, object *n_object=NULL, int n_submodel_parent=-1);
|
||||
void cam_delete(camid cid);
|
||||
bool cam_set_camera(camid cid);
|
||||
void cam_reset_camera();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
@@ -23,7 +24,6 @@
|
||||
#endif
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
#include <sys/stat.h>
|
||||
#include <glob.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
@@ -69,7 +69,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_VOICE_SPECIAL, "data" DIR_SEPARATOR_STR "voice" DIR_SEPARATOR_STR "special", ".wav .ogg", CF_TYPE_VOICE },
|
||||
{ CF_TYPE_VOICE_TRAINING, "data" DIR_SEPARATOR_STR "voice" DIR_SEPARATOR_STR "training", ".wav .ogg", CF_TYPE_VOICE },
|
||||
{ CF_TYPE_MUSIC, "data" DIR_SEPARATOR_STR "music", ".wav .ogg", CF_TYPE_DATA },
|
||||
{ CF_TYPE_MOVIES, "data" DIR_SEPARATOR_STR "movies", ".mve .msb .ogg", CF_TYPE_DATA },
|
||||
{ CF_TYPE_MOVIES, "data" DIR_SEPARATOR_STR "movies", ".mve .msb .ogg .mp4", CF_TYPE_DATA },
|
||||
{ CF_TYPE_INTERFACE, "data" DIR_SEPARATOR_STR "interface", ".pcx .ani .dds .tga .eff .png .jpg", CF_TYPE_DATA },
|
||||
{ CF_TYPE_FONT, "data" DIR_SEPARATOR_STR "fonts", ".vf .ttf", CF_TYPE_DATA },
|
||||
{ CF_TYPE_EFFECTS, "data" DIR_SEPARATOR_STR "effects", ".ani .eff .pcx .neb .tga .jpg .png .dds .sdr", CF_TYPE_DATA },
|
||||
@@ -82,7 +82,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_CACHE, "data" DIR_SEPARATOR_STR "cache", ".clr .tmp .bx", CF_TYPE_DATA }, //clr=cached color
|
||||
{ CF_TYPE_MULTI_CACHE, "data" DIR_SEPARATOR_STR "multidata", ".pcx .png .dds .fs2 .txt", CF_TYPE_DATA },
|
||||
{ CF_TYPE_MISSIONS, "data" DIR_SEPARATOR_STR "missions", ".fs2 .fc2 .ntl .ssv", CF_TYPE_DATA },
|
||||
{ CF_TYPE_CONFIG, "data" DIR_SEPARATOR_STR "config", ".cfg", CF_TYPE_DATA },
|
||||
{ CF_TYPE_CONFIG, "data" DIR_SEPARATOR_STR "config", ".cfg .tbl .tbm .xml .csv", CF_TYPE_DATA },
|
||||
{ CF_TYPE_DEMOS, "data" DIR_SEPARATOR_STR "demos", ".fsd", CF_TYPE_DATA },
|
||||
{ CF_TYPE_CBANIMS, "data" DIR_SEPARATOR_STR "cbanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_INTEL_ANIMS, "data" DIR_SEPARATOR_STR "intelanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
@@ -608,7 +608,7 @@ static void mkdir_recursive(const char *path) {
|
||||
while ((pos = tmp.find_first_of(DIR_SEPARATOR_CHAR, pre)) != std::string::npos) {
|
||||
dir = tmp.substr(0, pos++);
|
||||
pre = pos;
|
||||
if (dir.size() == 0) continue; // if leading / first time is 0 length
|
||||
if (dir.empty()) continue; // if leading / first time is 0 length
|
||||
|
||||
_mkdir(dir.c_str());
|
||||
}
|
||||
@@ -621,6 +621,7 @@ void cf_create_directory( int dir_type )
|
||||
int num_dirs = 0;
|
||||
int dir_tree[CF_MAX_PATH_TYPES];
|
||||
char longname[MAX_PATH_LEN];
|
||||
struct stat statbuf;
|
||||
|
||||
Assertion( CF_TYPE_SPECIFIED(dir_type), "Invalid dir_type passed to cf_create_directory." );
|
||||
|
||||
@@ -634,13 +635,14 @@ void cf_create_directory( int dir_type )
|
||||
|
||||
} while( current_dir != CF_TYPE_ROOT );
|
||||
|
||||
|
||||
int i;
|
||||
|
||||
for (i=num_dirs-1; i>=0; i-- ) {
|
||||
cf_create_default_path_string( longname, sizeof(longname)-1, dir_tree[i], NULL );
|
||||
mprintf(( "CFILE: Creating new directory '%s'\n", longname ));
|
||||
mkdir_recursive(longname);
|
||||
if (stat(longname, &statbuf) != 0) {
|
||||
mprintf(( "CFILE: Creating new directory '%s'\n", longname ));
|
||||
mkdir_recursive(longname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,10 +161,15 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
default:
|
||||
Int3();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position, cb->lib_offset, cb->lib_offset + cb->size);
|
||||
|
||||
int result = fseek(cb->fp, (long)goal_position, SEEK_SET );
|
||||
Assertion(goal_position >= cb->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cb->lib_offset);
|
||||
cb->raw_position = goal_position - cb->lib_offset;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
@@ -200,6 +205,7 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
}
|
||||
|
||||
if ( (cb->raw_position+size) > cb->size ) {
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
size = cb->size - cb->raw_position;
|
||||
if ( size < 1 ) {
|
||||
return 0;
|
||||
@@ -219,6 +225,7 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
size_t bytes_read = fread( buf, 1, size, cb->fp );
|
||||
if ( bytes_read > 0 ) {
|
||||
cb->raw_position += bytes_read;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
}
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
@@ -249,7 +256,8 @@ int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
|
||||
long orig_pos = ftell(cb->fp);
|
||||
int items_read = fscanf(cb->fp, LUA_NUMBER_SCAN, buf);
|
||||
cb->raw_position += ftell(cb->fp)-orig_pos;
|
||||
cb->raw_position += ftell(cb->fp)-orig_pos;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
|
||||
+154
-58
@@ -30,6 +30,7 @@
|
||||
#include <fnmatch.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
@@ -38,6 +39,7 @@
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "localization/localize.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#define CF_ROOTTYPE_PATH 0
|
||||
#define CF_ROOTTYPE_PACK 1
|
||||
@@ -77,12 +79,13 @@ static int Num_path_roots = 0;
|
||||
|
||||
// Created by searching all roots in order. This means Files is then sorted by precedence.
|
||||
typedef struct cf_file {
|
||||
char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
|
||||
int root_index; // Where in Roots this is located
|
||||
int pathtype_index; // Where in Paths this is located
|
||||
time_t write_time; // When it was last written
|
||||
int size; // How big it is in bytes
|
||||
int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
|
||||
char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
|
||||
int root_index; // Where in Roots this is located
|
||||
int pathtype_index; // Where in Paths this is located
|
||||
time_t write_time; // When it was last written
|
||||
int size; // How big it is in bytes
|
||||
int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
|
||||
char* real_name; // For real files, the full path
|
||||
} cf_file;
|
||||
|
||||
#define CF_NUM_FILES_PER_BLOCK 512
|
||||
@@ -115,6 +118,7 @@ cf_file *cf_create_file()
|
||||
if ( File_blocks[block] == NULL ) {
|
||||
File_blocks[block] = (cf_file_block *)vm_malloc( sizeof(cf_file_block) );
|
||||
Assert( File_blocks[block] != NULL);
|
||||
memset(File_blocks[block], 0, sizeof(cf_file_block));
|
||||
}
|
||||
|
||||
Num_files++;
|
||||
@@ -534,6 +538,10 @@ void cf_search_root_path(int root_index)
|
||||
mprintf(( "Searching root '%s' ... ", root->path ));
|
||||
|
||||
char search_path[CF_MAX_PATHNAME_LENGTH];
|
||||
#ifdef SCP_UNIX
|
||||
// This map stores the mapping between a specific path type and the actual path that we use for it
|
||||
SCP_unordered_map<int, SCP_string> pathTypeToRealPath;
|
||||
#endif
|
||||
|
||||
for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
|
||||
|
||||
@@ -552,6 +560,7 @@ void cf_search_root_path(int root_index)
|
||||
}
|
||||
|
||||
#if defined _WIN32
|
||||
SCP_string search_directory = search_path;
|
||||
strcat_s( search_path, "*.*" );
|
||||
|
||||
intptr_t find_handle;
|
||||
@@ -576,6 +585,11 @@ void cf_search_root_path(int root_index)
|
||||
file->size = find.size;
|
||||
file->pack_offset = 0; // Mark as a non-packed file
|
||||
|
||||
SCP_string file_name;
|
||||
sprintf(file_name, "%s%s%s", search_directory.c_str(), DIR_SEPARATOR_STR, find.name);
|
||||
|
||||
file->real_name = vm_strdup(file_name.c_str());
|
||||
|
||||
num_files++;
|
||||
//mprintf(( "Found file '%s'\n", file->name_ext ));
|
||||
}
|
||||
@@ -588,21 +602,80 @@ void cf_search_root_path(int root_index)
|
||||
_findclose( find_handle );
|
||||
}
|
||||
#elif defined SCP_UNIX
|
||||
DIR *dirp;
|
||||
struct dirent *dir;
|
||||
DIR *dirp = nullptr;
|
||||
SCP_string search_dir;
|
||||
{
|
||||
if (i == CF_TYPE_ROOT) {
|
||||
// Don't search for the same name for the root case since we would be searching in other mod directories in that case
|
||||
dirp = opendir (search_path);
|
||||
search_dir.assign(search_path);
|
||||
} else {
|
||||
// On Unix we can have a different case for the search paths so we also need to account for that
|
||||
// We do that by looking at the parent of search_path and enumerating all directories and the check if any of
|
||||
// them are a case-insensitive match
|
||||
SCP_string directory_name;
|
||||
|
||||
auto parentPathIter = pathTypeToRealPath.find(Pathtypes[i].parent_index);
|
||||
|
||||
if (parentPathIter == pathTypeToRealPath.end()) {
|
||||
// No parent known yet, use the standard dirname
|
||||
char dirname_copy[CF_MAX_PATHNAME_LENGTH];
|
||||
memcpy(dirname_copy, search_path, sizeof(search_path));
|
||||
// According to the documentation of directory_name and basename, the return value does not need to be freed
|
||||
directory_name.assign(dirname(dirname_copy));
|
||||
} else {
|
||||
// we have a valid parent path -> use that
|
||||
directory_name = parentPathIter->second;
|
||||
}
|
||||
|
||||
char basename_copy[CF_MAX_PATHNAME_LENGTH];
|
||||
memcpy(basename_copy, search_path, sizeof(search_path));
|
||||
// According to the documentation of dirname and basename, the return value does not need to be freed
|
||||
auto search_name = basename(basename_copy);
|
||||
|
||||
auto parentDirP = opendir(directory_name.c_str());
|
||||
|
||||
if (parentDirP) {
|
||||
struct dirent *dir = nullptr;
|
||||
while ((dir = readdir (parentDirP)) != nullptr) {
|
||||
|
||||
if (stricmp(search_name, dir->d_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SCP_string fn;
|
||||
sprintf(fn, "%s/%s", directory_name.c_str(), dir->d_name);
|
||||
|
||||
struct stat buf;
|
||||
if (stat(fn.c_str(), &buf) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(buf.st_mode)) {
|
||||
// Found a case insensitive match
|
||||
dirp = opendir(fn.c_str());
|
||||
search_dir = fn;
|
||||
// We also need to store this in our mapping since we may need it in the future
|
||||
pathTypeToRealPath.insert(std::make_pair(i, fn));
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(parentDirP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dirp = opendir (search_path);
|
||||
if ( dirp ) {
|
||||
struct dirent *dir = nullptr;
|
||||
while ((dir = readdir (dirp)) != NULL)
|
||||
{
|
||||
if (!fnmatch ("*.*", dir->d_name, 0))
|
||||
{
|
||||
char fn[MAX_PATH];
|
||||
snprintf(fn, MAX_PATH-1, "%s%s", search_path, dir->d_name);
|
||||
fn[MAX_PATH-1] = 0;
|
||||
SCP_string fn;
|
||||
sprintf(fn, "%s/%s", search_dir.c_str(), dir->d_name);
|
||||
|
||||
struct stat buf;
|
||||
if (stat(fn, &buf) == -1) {
|
||||
if (stat(fn.c_str(), &buf) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -626,6 +699,8 @@ void cf_search_root_path(int root_index)
|
||||
|
||||
file->pack_offset = 0; // Mark as a non-packed file
|
||||
|
||||
file->real_name = vm_strdup(fn.c_str());
|
||||
|
||||
num_files++;
|
||||
//mprintf(( "Found file '%s'\n", file->name_ext ));
|
||||
}
|
||||
@@ -827,6 +902,14 @@ void cf_free_secondary_filelist()
|
||||
// Init the file blocks
|
||||
for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
|
||||
if ( File_blocks[i] ) {
|
||||
// Free file paths
|
||||
for (auto& f : File_blocks[i]->files) {
|
||||
if (f.real_name) {
|
||||
vm_free(f.real_name);
|
||||
f.real_name = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
vm_free( File_blocks[i] );
|
||||
File_blocks[i] = NULL;
|
||||
}
|
||||
@@ -985,17 +1068,14 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
|
||||
*offset = (size_t)f->pack_offset;
|
||||
|
||||
if (pack_filename) {
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
strncpy( pack_filename, r->path, max_out );
|
||||
|
||||
if (f->pack_offset < 1) {
|
||||
strcat_s( pack_filename, max_out, Pathtypes[f->pathtype_index].path );
|
||||
// This is a real file, return the actual file path
|
||||
strncpy( pack_filename, f->real_name, max_out );
|
||||
} else {
|
||||
// File is in a pack file
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR )
|
||||
strcat_s( pack_filename, max_out, DIR_SEPARATOR_STR );
|
||||
|
||||
strcat_s( pack_filename, max_out, f->name_ext );
|
||||
strncpy( pack_filename, r->path, max_out );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1013,19 +1093,14 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
|
||||
*offset = (size_t)f->pack_offset;
|
||||
|
||||
if (pack_filename) {
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
strcpy( pack_filename, r->path );
|
||||
|
||||
if (f->pack_offset < 1) {
|
||||
if ( strlen(Pathtypes[f->pathtype_index].path) ) {
|
||||
strcat_s( pack_filename, max_out, Pathtypes[f->pathtype_index].path );
|
||||
// This is a real file, return the actual file path
|
||||
strncpy( pack_filename, f->real_name, max_out );
|
||||
} else {
|
||||
// File is in a pack file
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR )
|
||||
strcat_s( pack_filename, max_out, DIR_SEPARATOR_STR );
|
||||
}
|
||||
|
||||
strcat_s( pack_filename, max_out, f->name_ext );
|
||||
strncpy( pack_filename, r->path, max_out );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1260,17 +1335,14 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
|
||||
*offset = f->pack_offset;
|
||||
|
||||
if (pack_filename) {
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
strncpy( pack_filename, r->path, max_out );
|
||||
|
||||
if (f->pack_offset < 1) {
|
||||
strcat_s( pack_filename, max_out, Pathtypes[f->pathtype_index].path );
|
||||
// This is a real file, return the actual file path
|
||||
strncpy( pack_filename, f->real_name, max_out );
|
||||
} else {
|
||||
// File is in a pack file
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR )
|
||||
strcat_s( pack_filename, max_out, DIR_SEPARATOR_STR );
|
||||
|
||||
strcat_s( pack_filename, max_out, f->name_ext );
|
||||
strncpy( pack_filename, r->path, max_out );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1291,20 +1363,14 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
|
||||
*offset = f->pack_offset;
|
||||
|
||||
if (pack_filename) {
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
strcpy( pack_filename, r->path );
|
||||
|
||||
if (f->pack_offset < 1) {
|
||||
// This is a real file, return the actual file path
|
||||
strncpy( pack_filename, f->real_name, max_out );
|
||||
} else {
|
||||
// File is in a pack file
|
||||
cf_root *r = cf_get_root(f->root_index);
|
||||
|
||||
if ( strlen(Pathtypes[f->pathtype_index].path) ) {
|
||||
strcat_s( pack_filename, max_out, Pathtypes[f->pathtype_index].path );
|
||||
|
||||
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR )
|
||||
strcat_s( pack_filename, max_out, DIR_SEPARATOR_STR );
|
||||
}
|
||||
|
||||
strcat_s( pack_filename, max_out, f->name_ext );
|
||||
strncpy( pack_filename, r->path, max_out );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1537,8 +1603,7 @@ int cf_get_file_list( SCP_vector<SCP_string> &list, int pathtype, const char *fi
|
||||
}
|
||||
#endif
|
||||
|
||||
bool skip_packfiles = (Skip_packfile_search != 0);
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ( (pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS) ) {
|
||||
skip_packfiles = true;
|
||||
} else if (Get_file_list_child != NULL) {
|
||||
@@ -1563,6 +1628,11 @@ int cf_get_file_list( SCP_vector<SCP_string> &list, int pathtype, const char *fi
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Skip_packfile_search && f->pack_offset != 0) {
|
||||
// If the packfile skip flag is set we skip files in VPs but still search in directories
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
|
||||
//mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
|
||||
|
||||
@@ -1739,8 +1809,16 @@ int cf_get_file_list( int max, char **list, int pathtype, const char *filter, in
|
||||
}
|
||||
#endif
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ((pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
skip_packfiles = true;
|
||||
}
|
||||
else if (Get_file_list_child != NULL) {
|
||||
skip_packfiles = true;
|
||||
}
|
||||
|
||||
// Search all the packfiles and CD.
|
||||
if ( !Skip_packfile_search ) {
|
||||
if ( !skip_packfiles) {
|
||||
for (i=0; i<Num_files; i++ ) {
|
||||
cf_file * f = cf_get_file(i);
|
||||
|
||||
@@ -1760,6 +1838,11 @@ int cf_get_file_list( int max, char **list, int pathtype, const char *filter, in
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Skip_packfile_search && f->pack_offset != 0) {
|
||||
// If the packfile skip flag is set we skip files in VPs but still search in directories
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
|
||||
|
||||
//mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
|
||||
@@ -1943,8 +2026,16 @@ int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char *
|
||||
}
|
||||
#endif
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ((pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
skip_packfiles = true;
|
||||
}
|
||||
else if (Get_file_list_child != NULL) {
|
||||
skip_packfiles = true;
|
||||
}
|
||||
|
||||
// Search all the packfiles and CD.
|
||||
if ( !Skip_packfile_search ) {
|
||||
if (!skip_packfiles) {
|
||||
for (uint i=0; i<Num_files; i++ ) {
|
||||
cf_file * f = cf_get_file(i);
|
||||
|
||||
@@ -1965,6 +2056,11 @@ int cf_get_file_list_preallocated( int max, char arr[][MAX_FILENAME_LEN], char *
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Skip_packfile_search && f->pack_offset != 0) {
|
||||
// If the packfile skip flag is set we skip files in VPs but still search in directories
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) {
|
||||
|
||||
//mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index ));
|
||||
|
||||
@@ -20,10 +20,10 @@ void cf_free_secondary_filelist();
|
||||
|
||||
// Internal stuff
|
||||
typedef struct cf_pathtype {
|
||||
int index; // To verify that the CF_TYPE define is correctly indexed into this array
|
||||
char *path; // Path relative to FreeSpace root, has ending backslash.
|
||||
char *extensions; // Extensions used in this pathtype, separated by spaces
|
||||
int parent_index; // Index of this directory's parent. Used for creating directories when writing.
|
||||
int index; // To verify that the CF_TYPE define is correctly indexed into this array
|
||||
const char* path; // Path relative to FreeSpace root, has ending backslash.
|
||||
const char* extensions; // Extensions used in this pathtype, separated by spaces
|
||||
int parent_index; // Index of this directory's parent. Used for creating directories when writing.
|
||||
} cf_pathtype;
|
||||
|
||||
// During cfile_init, verify that Pathtypes[n].index == n for each item
|
||||
|
||||
@@ -8,7 +8,7 @@ set_target_properties(cfilearchiver
|
||||
PROPERTIES
|
||||
FOLDER "FSOTools"
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(cfilearchiver PUBLIC ${SDL_LIBS})
|
||||
TARGET_LINK_LIBRARIES(cfilearchiver PUBLIC sdl2)
|
||||
TARGET_INCLUDE_DIRECTORIES(cfilearchiver PUBLIC ${GENERATED_SOURCE_DIR})
|
||||
TARGET_INCLUDE_DIRECTORIES(cfilearchiver PUBLIC ${GENERATED_SOURCE_DIR}/code)
|
||||
|
||||
|
||||
@@ -315,28 +315,16 @@ int verify_directory( char *filespec )
|
||||
|
||||
void print_instructions()
|
||||
{
|
||||
printf( "Creates a vp archive out of a FreeSpace data tree.\n\n" );
|
||||
printf( "Usage: cfilearchiver archive_name src_dir\n");
|
||||
printf( "Example: cfilearchiver freespace /tmp/freespace/data\n\n");
|
||||
printf( "Directory structure options:\n" );
|
||||
printf( " Effects (.ani .pcx .neb .tga)\n" );
|
||||
printf( " Fonts (.vf)\n" );
|
||||
printf( " Hud (.ani .pcx .tga\n" );
|
||||
printf( " Interface (.pcx .ani .tga)\n" );
|
||||
printf( " Maps (.pcx .ani .tga)\n" );
|
||||
printf( " Missions (.ntl .ssv), FS1(.fsm .fsc), FS2(.fs2 .fc2)\n" );
|
||||
printf( " Models (.pof)\n" );
|
||||
printf( " Music (.wav)\n" );
|
||||
printf( " Sounds/8b22k (.wav)\n" );
|
||||
printf( " Sounds/16b11k (.wav)\n" );
|
||||
printf( " Tables (.tbl)\n" );
|
||||
printf( " Voice/Briefing (.wav)\n" );
|
||||
printf( " Voice/Command briefings (.wav)\n" );
|
||||
printf( " Voice/Debriefing (.wav)\n" );
|
||||
printf( " Voice/Personas (.wav)\n" );
|
||||
printf( " Voice/Special (.wav)\n" );
|
||||
printf( " Voice/Training (.wav)\n" );
|
||||
|
||||
printf("Creates a vp archive out of a FreeSpace data tree.\n\n");
|
||||
printf("Usage: cfilearchiver archive_name src_dir\n");
|
||||
#ifdef _WIN32
|
||||
printf("Example: cfilearchiver freespace c:\\freespace\\data\n");
|
||||
#else
|
||||
printf("Example: cfilearchiver freespace /tmp/freespace/data\n\n");
|
||||
#endif
|
||||
printf("Creates an archive named freespace out of the freespace data tree\n");
|
||||
printf("For information about the FS2 directory structure, please consult\n");
|
||||
printf("http://www.hard-light.net/wiki/index.php/FS2_Data_Structure\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -352,16 +340,7 @@ int main(int argc, char *argv[] )
|
||||
char *p;
|
||||
|
||||
if ( argc < 3 ) {
|
||||
#ifdef _WIN32
|
||||
printf( "Usage: %s archive_name src_dir\n", argv[0] );
|
||||
printf( "Example: %s freespace c:\\freespace\\data\n", argv[0] );
|
||||
printf( "Creates an archive named freespace out of the\nfreespace data tree\n" );
|
||||
printf( "Press any key to exit...\n" );
|
||||
getch();
|
||||
return 1;
|
||||
#else
|
||||
print_instructions();
|
||||
#endif
|
||||
}
|
||||
|
||||
strcpy_s( archive, argv[1] );
|
||||
|
||||
@@ -8,7 +8,7 @@ set_target_properties(cfileextractor
|
||||
PROPERTIES
|
||||
FOLDER "FSOTools"
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(cfileextractor PUBLIC ${SDL_LIBS})
|
||||
TARGET_LINK_LIBRARIES(cfileextractor PUBLIC sdl2)
|
||||
TARGET_INCLUDE_DIRECTORIES(cfileextractor PUBLIC ${GENERATED_SOURCE_DIR})
|
||||
TARGET_INCLUDE_DIRECTORIES(cfileextractor PUBLIC ${GENERATED_SOURCE_DIR}/code)
|
||||
|
||||
|
||||
+63
-31
@@ -17,7 +17,7 @@
|
||||
#include "globalincs/version.h"
|
||||
#include "hud/hudconfig.h"
|
||||
#include "network/multi.h"
|
||||
#include "parse/scripting.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "parse/sexp.h"
|
||||
#include "globalincs/version.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
@@ -150,6 +150,7 @@ Flag exe_params[] =
|
||||
{ "-noscalevid", "Disable scale-to-window for movies", true, 0, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noscalevid", },
|
||||
{ "-missile_lighting", "Apply lighting to missiles" , true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-missile_lighting", },
|
||||
{ "-nonormal", "Disable normal maps", true, EASY_DEFAULT_MEM, EASY_MEM_ALL_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-normal" },
|
||||
{ "-no_emissive_light", "Disable emissive light from ships", true, 0, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_emissive_light" },
|
||||
{ "-noheight", "Disable height/parallax maps", true, EASY_DEFAULT_MEM, EASY_MEM_ALL_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-height" },
|
||||
{ "-3dshockwave", "Enable 3D shockwaves", true, EASY_MEM_ALL_ON, EASY_DEFAULT_MEM, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-3dshockwave" },
|
||||
{ "-post_process", "Enable post processing", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-post_process" },
|
||||
@@ -208,6 +209,7 @@ Flag exe_params[] =
|
||||
{ "-no_geo_effects", "Disable geometry shader for effects", true, 0, EASY_DEFAULT, "Troubleshoot", "", },
|
||||
{ "-set_cpu_affinity", "Sets processor affinity to config value", true, 0, EASY_DEFAULT, "Troubleshoot", "", },
|
||||
{ "-nograb", "Disables mouse grabbing", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nograb", },
|
||||
{ "-noshadercache", "Disables the shader cache", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noshadercache", },
|
||||
#ifdef WIN32
|
||||
{ "-fix_registry", "Use a different registry path", true, 0, EASY_DEFAULT, "Troubleshoot", "", },
|
||||
#endif
|
||||
@@ -232,12 +234,14 @@ Flag exe_params[] =
|
||||
{ "-save_render_target", "Save render targets to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-save_render_target", },
|
||||
{ "-verify_vps", "Spew VP CRCs to vp_crcs.txt", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-verify_vps", },
|
||||
{ "-reparse_mainhall", "Reparse mainhall.tbl when loading halls", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-reparse_mainhall", },
|
||||
{ "-profile_frame_time","Profile engine subsystems", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_timings", },
|
||||
{ "-profile_write_file", "Write profiling information to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_write_file", },
|
||||
{ "-no_unfocused_pause","Don't pause if the window isn't focused", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_unfocused_pause", },
|
||||
{ "-benchmark_mode", "Puts the game into benchmark mode", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-benchmark_mode", },
|
||||
{ "-noninteractive", "Disables interactive dialogs", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noninteractive", },
|
||||
{ "-json_pilot", "Dump pilot files in JSON format", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-json_pilot", },
|
||||
{ "-json_profiling", "Generate JSON profiling output", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-json_profiling", },
|
||||
{ "-profile_frame_time","Profile engine subsystems", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_timings", },
|
||||
{ "-debug_window", "Enable the debug window", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-debug_window", },
|
||||
};
|
||||
|
||||
// forward declaration
|
||||
@@ -438,6 +442,7 @@ cmdline_parm gl_finish ("-gl_finish", NULL, AT_NONE);
|
||||
cmdline_parm no_geo_sdr_effects("-no_geo_effects", NULL, AT_NONE);
|
||||
cmdline_parm set_cpu_affinity("-set_cpu_affinity", NULL, AT_NONE);
|
||||
cmdline_parm nograb_arg("-nograb", NULL, AT_NONE);
|
||||
cmdline_parm noshadercache_arg("-noshadercache", NULL, AT_NONE);
|
||||
#ifdef WIN32
|
||||
cmdline_parm fix_registry("-fix_registry", NULL, AT_NONE);
|
||||
#endif
|
||||
@@ -455,6 +460,7 @@ bool Cmdline_gl_finish = false;
|
||||
bool Cmdline_no_geo_sdr_effects = false;
|
||||
bool Cmdline_set_cpu_affinity = false;
|
||||
bool Cmdline_nograb = false;
|
||||
bool Cmdline_noshadercache = false;
|
||||
#ifdef WIN32
|
||||
bool Cmdline_alternate_registry_path = false;
|
||||
#endif
|
||||
@@ -466,7 +472,6 @@ cmdline_parm dis_weapons("-dis_weapons", NULL, AT_NONE); // Cmdline_dis_weapons
|
||||
cmdline_parm noparseerrors_arg("-noparseerrors", NULL, AT_NONE); // Cmdline_noparseerrors -- turns off parsing errors -C
|
||||
cmdline_parm extra_warn_arg("-extra_warn", "Enable 'extra' warnings", AT_NONE); // Cmdline_extra_warn
|
||||
cmdline_parm fps_arg("-fps", NULL, AT_NONE); // Cmdline_show_fps
|
||||
cmdline_parm show_mem_usage_arg("-show_mem_usage", NULL, AT_NONE); // Cmdline_show_mem_usage
|
||||
cmdline_parm pos_arg("-pos", NULL, AT_NONE); // Cmdline_show_pos
|
||||
cmdline_parm stats_arg("-stats", NULL, AT_NONE); // Cmdline_show_stats
|
||||
cmdline_parm save_render_targets_arg("-save_render_target", NULL, AT_NONE); // Cmdline_save_render_targets
|
||||
@@ -477,12 +482,15 @@ cmdline_parm center_res_arg("-center_res", "Resolution of center monitor, format
|
||||
cmdline_parm verify_vps_arg("-verify_vps", NULL, AT_NONE); // Cmdline_verify_vps -- spew VP crcs to vp_crcs.txt
|
||||
cmdline_parm parse_cmdline_only(PARSE_COMMAND_LINE_STRING, "Ignore any cmdline_fso.cfg files", AT_NONE);
|
||||
cmdline_parm reparse_mainhall_arg("-reparse_mainhall", NULL, AT_NONE); //Cmdline_reparse_mainhall
|
||||
cmdline_parm frame_profile_arg("-profile_frame_time", NULL, AT_NONE); //Cmdline_frame_profile
|
||||
cmdline_parm frame_profile_write_file("-profile_write_file", NULL, AT_NONE); // Cmdline_profile_write_file
|
||||
cmdline_parm no_unfocused_pause_arg("-no_unfocused_pause", NULL, AT_NONE); //Cmdline_no_unfocus_pause
|
||||
cmdline_parm benchmark_mode_arg("-benchmark_mode", NULL, AT_NONE); //Cmdline_benchmark_mode
|
||||
cmdline_parm noninteractive_arg("-noninteractive", NULL, AT_NONE); //Cmdline_noninteractive
|
||||
cmdline_parm json_pilot("-json_pilot", NULL, AT_NONE); //Cmdline_json_pilot
|
||||
cmdline_parm json_profiling("-json_profiling", NULL, AT_NONE); //Cmdline_json_profiling
|
||||
cmdline_parm show_video_info("-show_video_info", NULL, AT_NONE); //Cmdline_show_video_info
|
||||
cmdline_parm frame_profile_arg("-profile_frame_time", NULL, AT_NONE); //Cmdline_frame_profile
|
||||
cmdline_parm debug_window_arg("-debug_window", NULL, AT_NONE); // Cmdline_debug_window
|
||||
|
||||
|
||||
char *Cmdline_start_mission = NULL;
|
||||
@@ -494,7 +502,6 @@ int Cmdline_noparseerrors = 0;
|
||||
int Cmdline_nowarn = 0; // turn warnings off in FRED
|
||||
#endif
|
||||
int Cmdline_extra_warn = 0;
|
||||
int Cmdline_show_mem_usage = 0;
|
||||
int Cmdline_show_pos = 0;
|
||||
int Cmdline_show_stats = 0;
|
||||
int Cmdline_save_render_targets = 0;
|
||||
@@ -504,12 +511,15 @@ char *Cmdline_res = 0;
|
||||
char *Cmdline_center_res = 0;
|
||||
int Cmdline_verify_vps = 0;
|
||||
int Cmdline_reparse_mainhall = 0;
|
||||
bool Cmdline_frame_profile = false;
|
||||
bool Cmdline_profile_write_file = false;
|
||||
bool Cmdline_no_unfocus_pause = false;
|
||||
bool Cmdline_benchmark_mode = false;
|
||||
bool Cmdline_noninteractive = false;
|
||||
bool Cmdline_json_pilot = false;
|
||||
bool Cmdline_json_profiling = false;
|
||||
bool Cmdline_frame_profile = false;
|
||||
bool Cmdline_show_video_info = false;
|
||||
bool Cmdline_debug_window = false;
|
||||
|
||||
// Other
|
||||
cmdline_parm get_flags_arg("-get_flags", "Output the launcher flags file", AT_NONE);
|
||||
@@ -779,12 +789,7 @@ void os_validate_parms(int argc, char *argv[])
|
||||
if (parm_found == 0) {
|
||||
// if we got a -help, --help, -h, or -? then show the help text, otherwise show unknown option
|
||||
if (!stricmp(token, "-help") || !stricmp(token, "--help") || !stricmp(token, "-h") || !stricmp(token, "-?")) {
|
||||
if (FS_VERSION_HAS_REVIS == 0) {
|
||||
printf("FreeSpace 2 Open, version %i.%i.%i\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD);
|
||||
}
|
||||
else {
|
||||
printf("FreeSpace 2 Open, version %i.%i.%i.%i\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD, FS_VERSION_REVIS);
|
||||
}
|
||||
printf("FreeSpace 2 Open, version %s\n", FS_VERSION_FULL);
|
||||
printf("Website: http://scp.indiegames.us\n");
|
||||
printf("Mantis (bug reporting): http://scp.indiegames.us/mantis/\n\n");
|
||||
printf("Usage: fs2_open [options]\n");
|
||||
@@ -921,32 +926,45 @@ bool has_cmdline_only_flag(int argc, char *argv[])
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove old parms - needed for tests
|
||||
static void reset_cmdline_parms()
|
||||
{
|
||||
for (cmdline_parm *parmp = GET_FIRST(&Parm_list); parmp != END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp)) {
|
||||
if (parmp->args != NULL) {
|
||||
delete[] parmp->args;
|
||||
parmp->args = NULL;
|
||||
}
|
||||
parmp->name_found = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Call once to initialize the command line system
|
||||
//
|
||||
// cmdline - command line string passed to the application
|
||||
void os_init_cmdline(int argc, char *argv[])
|
||||
{
|
||||
// Tests call this multiple times, so reset the params here.
|
||||
// Otherwise e.g. the modlist just grows and grows...
|
||||
reset_cmdline_parms();
|
||||
|
||||
FILE *fp;
|
||||
|
||||
|
||||
if (!has_cmdline_only_flag(argc, argv)) {
|
||||
// Only parse the config file in the current directory if we are in legacy config mode
|
||||
if (os_is_legacy_mode()) {
|
||||
// read the cmdline_fso.cfg file from the data folder, and pass the command line arguments to
|
||||
// the the parse_parms and validate_parms line. Read these first so anything actually on
|
||||
// the command line will take precedence
|
||||
#ifdef _WIN32
|
||||
fp = fopen("data\\cmdline_fso.cfg", "rt");
|
||||
#elif defined(APPLE_APP)
|
||||
#ifdef APPLE_APP
|
||||
char resolved_path[MAX_PATH], data_path[MAX_PATH_LEN];
|
||||
|
||||
GetCurrentDirectory(MAX_PATH_LEN - 1, data_path);
|
||||
getcwd(data_path, sizeof(data_path));
|
||||
snprintf(resolved_path, MAX_PATH, "%s/data/cmdline_fso.cfg", data_path);
|
||||
|
||||
fp = fopen(resolved_path, "rt");
|
||||
#else
|
||||
fp = fopen("data/cmdline_fso.cfg", "rt");
|
||||
fp = fopen("data" DIR_SEPARATOR_STR "cmdline_fso.cfg", "rt");
|
||||
#endif
|
||||
|
||||
// if the file exists, get a single line, and deal with it
|
||||
if (fp) {
|
||||
char *buf, *p;
|
||||
@@ -960,7 +978,6 @@ void os_init_cmdline(int argc, char *argv[])
|
||||
if ((p = strrchr(buf, '\n')) != NULL) {
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
// append a space for the os_parse_parms() check
|
||||
strcat_s(buf, len, " ");
|
||||
@@ -991,14 +1008,14 @@ void os_init_cmdline(int argc, char *argv[])
|
||||
|
||||
// append a space for the os_parse_parms() check
|
||||
strcat_s(buf, len, " ");
|
||||
|
||||
|
||||
os_process_cmdline(buf);
|
||||
}
|
||||
delete [] buf;
|
||||
fclose(fp);
|
||||
}
|
||||
} // If cmdline included PARSE_COMMAND_LINE_STRING
|
||||
|
||||
|
||||
// By parsing cmdline last, anything actually on the command line will take precedence.
|
||||
os_parse_parms(argc, argv);
|
||||
os_validate_parms(argc, argv);
|
||||
@@ -1636,9 +1653,6 @@ bool SetCmdlineParams()
|
||||
if ( weapon_choice_3d_arg.found() )
|
||||
Cmdline_weapon_choice_3d = 1;
|
||||
|
||||
if ( show_mem_usage_arg.found() )
|
||||
Cmdline_show_mem_usage = 1;
|
||||
|
||||
if (ingamejoin_arg.found() )
|
||||
Cmdline_ingamejoin = 1;
|
||||
|
||||
@@ -1691,6 +1705,11 @@ bool SetCmdlineParams()
|
||||
Cmdline_nograb = true;
|
||||
}
|
||||
|
||||
if (noshadercache_arg.found())
|
||||
{
|
||||
Cmdline_noshadercache = true;
|
||||
}
|
||||
|
||||
if (portable_mode.found())
|
||||
{
|
||||
Cmdline_portable_mode = true;
|
||||
@@ -1822,14 +1841,8 @@ bool SetCmdlineParams()
|
||||
Cmdline_no_deferred_lighting = 1;
|
||||
}
|
||||
|
||||
if (frame_profile_arg.found() )
|
||||
{
|
||||
Cmdline_frame_profile = true;
|
||||
}
|
||||
|
||||
if (frame_profile_write_file.found())
|
||||
{
|
||||
Cmdline_frame_profile = true;
|
||||
Cmdline_profile_write_file = true;
|
||||
}
|
||||
|
||||
@@ -1853,6 +1866,25 @@ bool SetCmdlineParams()
|
||||
Cmdline_json_pilot = true;
|
||||
}
|
||||
|
||||
if (json_profiling.found())
|
||||
{
|
||||
Cmdline_json_profiling = true;
|
||||
}
|
||||
|
||||
if (frame_profile_arg.found() )
|
||||
{
|
||||
Cmdline_frame_profile = true;
|
||||
}
|
||||
|
||||
if (debug_window_arg.found()) {
|
||||
Cmdline_debug_window = true;
|
||||
}
|
||||
|
||||
if (show_video_info.found())
|
||||
{
|
||||
Cmdline_show_video_info = true;
|
||||
}
|
||||
|
||||
//Deprecated flags - CommanderDJ
|
||||
if( deprecated_spec_arg.found() )
|
||||
{
|
||||
|
||||
@@ -136,6 +136,7 @@ extern bool Cmdline_gl_finish;
|
||||
extern bool Cmdline_no_geo_sdr_effects;
|
||||
extern bool Cmdline_set_cpu_affinity;
|
||||
extern bool Cmdline_nograb;
|
||||
extern bool Cmdline_noshadercache;
|
||||
#ifdef WIN32
|
||||
extern bool Cmdline_alternate_registry_path;
|
||||
#endif
|
||||
@@ -147,17 +148,19 @@ extern int Cmdline_dis_collisions;
|
||||
extern int Cmdline_dis_weapons;
|
||||
extern int Cmdline_noparseerrors;
|
||||
extern int Cmdline_extra_warn;
|
||||
extern int Cmdline_show_mem_usage;
|
||||
extern int Cmdline_show_pos;
|
||||
extern int Cmdline_show_stats;
|
||||
extern int Cmdline_save_render_targets;
|
||||
extern int Cmdline_verify_vps;
|
||||
extern int Cmdline_reparse_mainhall;
|
||||
extern bool Cmdline_frame_profile;
|
||||
extern bool Cmdline_profile_write_file;
|
||||
extern bool Cmdline_no_unfocus_pause;
|
||||
extern bool Cmdline_benchmark_mode;
|
||||
extern bool Cmdline_noninteractive;
|
||||
extern bool Cmdline_json_pilot;
|
||||
extern bool Cmdline_json_profiling;
|
||||
extern bool Cmdline_frame_profile;
|
||||
extern bool Cmdline_show_video_info;
|
||||
extern bool Cmdline_debug_window;
|
||||
|
||||
#endif
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "missionui/missionscreencommon.h"
|
||||
#include "network/multi_pmsg.h"
|
||||
#include "network/multiutil.h"
|
||||
#include "parse/scripting.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "pilotfile/pilotfile.h"
|
||||
#include "popup/popup.h"
|
||||
#include "ui/ui.h"
|
||||
@@ -48,12 +48,12 @@
|
||||
#define CONTROL_W_COORD 2
|
||||
#define CONTROL_H_COORD 3
|
||||
|
||||
char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
|
||||
const char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
|
||||
"ControlConfig", // GR_640
|
||||
"2_ControlConfig" // GR_1024
|
||||
};
|
||||
|
||||
char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
|
||||
const char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
|
||||
"ControlConfig-m", // GR_640
|
||||
"2_ControlConfig-m" // GR_1024
|
||||
};
|
||||
@@ -919,7 +919,7 @@ int control_config_do_reset()
|
||||
bool cycling_presets = false;
|
||||
|
||||
// If there are presets, then we'll cycle to the next preset and reset to that
|
||||
if (Control_config_presets.size() >= 1) {
|
||||
if (!Control_config_presets.empty()) {
|
||||
cycling_presets = true;
|
||||
|
||||
if (++Defaults_cycle_pos >= Control_config_presets.size())
|
||||
@@ -1974,7 +1974,7 @@ void control_config_do_frame(float frametime)
|
||||
if (t % 2) {
|
||||
gr_set_color_fast(&Color_text_normal);
|
||||
gr_get_string_size(&w, NULL, XSTR( "?", 208));
|
||||
gr_printf_menu(x - w / 2, y - font_height / 2, XSTR( "?", 208));
|
||||
gr_printf_menu(x - w / 2, y - font_height / 2, "%s", XSTR( "?", 208));
|
||||
}
|
||||
|
||||
} else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
|
||||
@@ -1986,7 +1986,7 @@ void control_config_do_frame(float frametime)
|
||||
gr_set_color_fast(&Color_text_normal);
|
||||
str = XSTR( "Control conflicts with:", 209);
|
||||
gr_get_string_size(&w, NULL, str);
|
||||
gr_printf_menu(x - w / 2, y - font_height, str);
|
||||
gr_printf_menu(x - w / 2, y - font_height, "%s", str);
|
||||
|
||||
if (Control_config[i].hasXSTR) {
|
||||
strcpy_s(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
|
||||
@@ -1996,12 +1996,12 @@ void control_config_do_frame(float frametime)
|
||||
|
||||
font::force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
|
||||
gr_get_string_size(&w, NULL, buf);
|
||||
gr_printf_menu(x - w / 2, y, buf);
|
||||
gr_printf_menu(x - w / 2, y, "%s", buf);
|
||||
|
||||
} else if (*bound_string) {
|
||||
gr_set_color_fast(&Color_text_normal);
|
||||
gr_get_string_size(&w, NULL, bound_string);
|
||||
gr_printf_menu(x - w / 2, y - font_height / 2, bound_string);
|
||||
gr_printf_menu(x - w / 2, y - font_height / 2, "%s", bound_string);
|
||||
if (timestamp_elapsed(bound_timestamp)) {
|
||||
*bound_string = 0;
|
||||
}
|
||||
@@ -2009,7 +2009,7 @@ void control_config_do_frame(float frametime)
|
||||
|
||||
if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
|
||||
gr_set_color_fast(&Color_white);
|
||||
gr_printf_menu(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
|
||||
gr_printf_menu(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], "%s", XSTR( "More...", 210));
|
||||
}
|
||||
|
||||
conflict = 0;
|
||||
@@ -2035,7 +2035,7 @@ void control_config_do_frame(float frametime)
|
||||
if (Cc_lines[line].label) {
|
||||
strcpy_s(buf, Cc_lines[line].label);
|
||||
font::force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
|
||||
gr_printf_menu(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
|
||||
gr_printf_menu(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, "%s", buf);
|
||||
}
|
||||
|
||||
if (!(z & JOY_AXIS)) {
|
||||
@@ -2046,7 +2046,7 @@ void control_config_do_frame(float frametime)
|
||||
|
||||
if ((k < 0) && (j < 0)) {
|
||||
gr_set_color_fast(&Color_grey);
|
||||
gr_printf_menu(x, y, XSTR( "None", 211));
|
||||
gr_printf_menu(x, y, "%s", XSTR( "None", 211));
|
||||
|
||||
} else {
|
||||
if (k >= 0) {
|
||||
@@ -2066,7 +2066,7 @@ void control_config_do_frame(float frametime)
|
||||
gr_set_color_fast(c);
|
||||
}
|
||||
|
||||
gr_printf_menu(x, y, buf);
|
||||
gr_printf_menu(x, y, "%s", buf);
|
||||
|
||||
Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
|
||||
gr_get_string_size(&w, NULL, buf);
|
||||
@@ -2075,7 +2075,7 @@ void control_config_do_frame(float frametime)
|
||||
|
||||
if (j >= 0) {
|
||||
gr_set_color_fast(&Color_text_normal);
|
||||
gr_printf_menu(x, y, XSTR( ", ", 212));
|
||||
gr_printf_menu(x, y, "%s", XSTR( ", ", 212));
|
||||
gr_get_string_size(&w, NULL, XSTR( ", ", 212));
|
||||
x += w;
|
||||
}
|
||||
@@ -2099,7 +2099,7 @@ void control_config_do_frame(float frametime)
|
||||
}
|
||||
|
||||
font::force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
|
||||
gr_printf_menu(x, y, buf);
|
||||
gr_printf_menu(x, y, "%s", buf);
|
||||
|
||||
Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
|
||||
gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
|
||||
@@ -2115,7 +2115,7 @@ void control_config_do_frame(float frametime)
|
||||
|
||||
if (j < 0) {
|
||||
gr_set_color_fast(&Color_grey);
|
||||
gr_printf_menu(x, y, XSTR( "None", 211));
|
||||
gr_printf_menu(x, y, "%s", XSTR( "None", 211));
|
||||
|
||||
} else {
|
||||
if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
|
||||
|
||||
@@ -63,7 +63,7 @@ typedef struct config_item {
|
||||
short joy_default; //!< default joystick button bound to action
|
||||
char tab; //!< what tab (category) it belongs in
|
||||
bool hasXSTR; //!< whether we should translate this with an XSTR
|
||||
char *text; //!< describes the action in the config screen
|
||||
const char *text; //!< describes the action in the config screen
|
||||
char type; //!< manner control should be checked in
|
||||
short key_id; //!< actual key bound to action
|
||||
short joy_id; //!< joystick button bound to action
|
||||
@@ -296,8 +296,8 @@ extern int Control_config_overlay_id;
|
||||
extern config_item Control_config[]; //!< Stores the keyboard configuration
|
||||
extern SCP_vector<config_item*> Control_config_presets; // tabled control presets; pointers to config_item arrays
|
||||
extern SCP_vector<SCP_string> Control_config_preset_names; // names for Control_config_presets (identical order of items)
|
||||
extern char **Scan_code_text;
|
||||
extern char **Joy_button_text;
|
||||
extern const char **Scan_code_text;
|
||||
extern const char **Joy_button_text;
|
||||
|
||||
void control_config_common_init(); //!< initialize common control config stuff - call at game startup after localization has been initialized
|
||||
void control_config_common_close(); //!< close common control config stuff - call at game shutdown
|
||||
@@ -311,7 +311,26 @@ void control_config_cancel_exit();
|
||||
void control_config_reset_defaults(int presetnum=-1);
|
||||
int translate_key_to_index(const char *key, bool find_override=true);
|
||||
char *translate_key(char *key);
|
||||
char *textify_scancode(int code);
|
||||
|
||||
/**
|
||||
* @brief Converts the specified key code to a human readable string
|
||||
*
|
||||
* @note The returned value is localized to the current language
|
||||
*
|
||||
* @param code The key code to convert
|
||||
* @return The text representation of the code. The returned value is stored in a temporary location, copy it to your
|
||||
* own buffer if you want to continue using it.
|
||||
*/
|
||||
const char *textify_scancode(int code);
|
||||
|
||||
/**
|
||||
* @note Same as textify_scancode but always returns the same value regardless of current language
|
||||
* @param code The key code to convert
|
||||
* @return The name of the key
|
||||
* @see textify_scancode
|
||||
*/
|
||||
const char *textify_scancode_universal(int code);
|
||||
|
||||
float check_control_timef(int id);
|
||||
int check_control(int id, int key = -1);
|
||||
void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr);
|
||||
|
||||
@@ -199,7 +199,7 @@ config_item Control_config[CCFG_MAX + 1] = {
|
||||
{ -1, -1, -1, false, "", CC_TYPE_TRIGGER, -1, -1, 0, false, false }
|
||||
};
|
||||
|
||||
char *Scan_code_text_german[] = {
|
||||
const char *Scan_code_text_german[] = {
|
||||
"", "Esc", "1", "2", "3", "4", "5", "6",
|
||||
"7", "8", "9", "0", "Akzent '", "Eszett", "R\x81""cktaste", "Tab",
|
||||
"Q", "W", "E", "R", "T", "Z", "U", "I",
|
||||
@@ -241,7 +241,7 @@ char *Scan_code_text_german[] = {
|
||||
"", "", "", "", "", "", "", "",
|
||||
};
|
||||
|
||||
char *Joy_button_text_german[] = {
|
||||
const char *Joy_button_text_german[] = {
|
||||
"Knopf 1", "Knopf 2", "Knopf 3", "Knopf 4", "Knopf 5", "Knopf 6",
|
||||
"Knopf 7", "Knopf 8", "Knopf 9", "Knopf 10", "Knopf 11", "Knopf 12",
|
||||
"Knopf 13", "Knopf 14", "Knopf 15", "Knopf 16", "Knopf 17", "Knopf 18",
|
||||
@@ -250,7 +250,7 @@ char *Joy_button_text_german[] = {
|
||||
"Knopf 31", "Knopf 32", "Hut Hinten", "Hut Vorne", "Hut Links", "Hut Rechts"
|
||||
};
|
||||
|
||||
char *Scan_code_text_french[] = {
|
||||
const char *Scan_code_text_french[] = {
|
||||
"", "\x90""chap", "1", "2", "3", "4", "5", "6",
|
||||
"7", "8", "9", "0", "-", "=", "Fl\x82""che Ret.", "Tab",
|
||||
"Q", "W", "E", "R", "T", "Y", "U", "I",
|
||||
@@ -292,7 +292,7 @@ char *Scan_code_text_french[] = {
|
||||
"", "", "", "", "", "", "", "",
|
||||
};
|
||||
|
||||
char *Joy_button_text_french[] = {
|
||||
const char *Joy_button_text_french[] = {
|
||||
"Bouton 1", "Bouton 2", "Bouton 3", "Bouton 4", "Bouton 5", "Bouton 6",
|
||||
"Bouton 7", "Bouton 8", "Bouton 9", "Bouton 10", "Bouton 11", "Bouton 12",
|
||||
"Bouton 13", "Bouton 14", "Bouton 15", "Bouton 16", "Bouton 17", "Bouton 18",
|
||||
@@ -301,7 +301,7 @@ char *Joy_button_text_french[] = {
|
||||
"Bouton 31", "Bouton 32", "Chapeau Arri\x8Are", "Chapeau Avant", "Chapeau Gauche", "Chapeau Droite"
|
||||
};
|
||||
|
||||
char *Scan_code_text_polish[] = {
|
||||
const char *Scan_code_text_polish[] = {
|
||||
"", "Esc", "1", "2", "3", "4", "5", "6",
|
||||
"7", "8", "9", "0", "-", "=", "Backspace", "Tab",
|
||||
"Q", "W", "E", "R", "T", "Y", "U", "I",
|
||||
@@ -343,7 +343,7 @@ char *Scan_code_text_polish[] = {
|
||||
"", "", "", "", "", "", "", "",
|
||||
};
|
||||
|
||||
char *Joy_button_text_polish[] = {
|
||||
const char *Joy_button_text_polish[] = {
|
||||
"Przyc.1", "Przyc.2", "Przyc.3", "Przyc.4", "Przyc.5", "Przyc.6",
|
||||
"Przyc.7", "Przyc.8", "Przyc.9", "Przyc.10", "Przyc.11", "Przyc.12",
|
||||
"Przyc.13", "Przyc.14", "Przyc.15", "Przyc.16", "Przyc.17", "Przyc.18",
|
||||
@@ -353,7 +353,7 @@ char *Joy_button_text_polish[] = {
|
||||
};
|
||||
|
||||
//! This is the text that is displayed on the screen for the keys a player selects
|
||||
char *Scan_code_text_english[] = {
|
||||
const char *Scan_code_text_english[] = {
|
||||
"", "Esc", "1", "2", "3", "4", "5", "6",
|
||||
"7", "8", "9", "0", "-", "=", "Backspace", "Tab",
|
||||
"Q", "W", "E", "R", "T", "Y", "U", "I",
|
||||
@@ -395,7 +395,7 @@ char *Scan_code_text_english[] = {
|
||||
"", "", "", "", "", "", "", "",
|
||||
};
|
||||
|
||||
char *Joy_button_text_english[] = {
|
||||
const char *Joy_button_text_english[] = {
|
||||
"Button 1", "Button 2", "Button 3", "Button 4", "Button 5", "Button 6",
|
||||
"Button 7", "Button 8", "Button 9", "Button 10", "Button 11", "Button 12",
|
||||
"Button 13", "Button 14", "Button 15", "Button 16", "Button 17", "Button 18",
|
||||
@@ -404,8 +404,8 @@ char *Joy_button_text_english[] = {
|
||||
"Button 31", "Button 32", "Hat Back", "Hat Forward", "Hat Left", "Hat Right"
|
||||
};
|
||||
|
||||
char **Scan_code_text = Scan_code_text_english;
|
||||
char **Joy_button_text = Joy_button_text_english;
|
||||
const char **Scan_code_text = Scan_code_text_english;
|
||||
const char **Joy_button_text = Joy_button_text_english;
|
||||
|
||||
SCP_vector<config_item*> Control_config_presets;
|
||||
SCP_vector<SCP_string> Control_config_preset_names;
|
||||
@@ -510,8 +510,8 @@ int translate_key_to_index(const char *key, bool find_override)
|
||||
char *translate_key(char *key)
|
||||
{
|
||||
int index = -1, key_code = -1, joy_code = -1;
|
||||
char *key_text = NULL;
|
||||
char *joy_text = NULL;
|
||||
const char *key_text = NULL;
|
||||
const char *joy_text = NULL;
|
||||
|
||||
static char text[40] = {"None"};
|
||||
|
||||
@@ -553,7 +553,7 @@ char *translate_key(char *key)
|
||||
return text;
|
||||
}
|
||||
|
||||
char *textify_scancode(int code)
|
||||
const char *textify_scancode(int code)
|
||||
{
|
||||
static char text[40];
|
||||
|
||||
@@ -586,6 +586,28 @@ char *textify_scancode(int code)
|
||||
strcat_s(text, Scan_code_text[keycode]);
|
||||
return text;
|
||||
}
|
||||
|
||||
const char *textify_scancode_universal(int code)
|
||||
{
|
||||
if (code < 0)
|
||||
return "None";
|
||||
|
||||
int keycode = code & KEY_MASK;
|
||||
|
||||
static char text[40];
|
||||
*text = 0;
|
||||
if (code & KEY_ALTED && !(keycode == KEY_LALT || keycode == KEY_RALT)) {
|
||||
strcat_s(text, "Alt-");
|
||||
}
|
||||
|
||||
if (code & KEY_SHIFTED && !(keycode == KEY_LSHIFT || keycode == KEY_RSHIFT)) {
|
||||
strcat_s(text, "Shift-");
|
||||
}
|
||||
|
||||
// Always use the english version here
|
||||
strcat_s(text, Scan_code_text_english[keycode]);
|
||||
return text;
|
||||
}
|
||||
//XSTR:ON
|
||||
|
||||
void control_config_common_load_overrides();
|
||||
@@ -811,118 +833,125 @@ void control_config_common_load_overrides()
|
||||
{
|
||||
LoadEnumsIntoMaps();
|
||||
|
||||
if (cf_exists_full("controlconfigdefaults.tbl", CF_TYPE_TABLES)) {
|
||||
read_file_text("controlconfigdefaults.tbl", CF_TYPE_TABLES);
|
||||
} else {
|
||||
read_file_text_from_default(defaults_get_file("controlconfigdefaults.tbl"));
|
||||
}
|
||||
|
||||
reset_parse();
|
||||
|
||||
// start parsing
|
||||
// TODO: Split this out into more helps. Too many tabs!
|
||||
while(optional_string("#ControlConfigOverride")) {
|
||||
config_item *cfg_preset = new config_item[CCFG_MAX + 1];
|
||||
std::copy(Control_config, Control_config + CCFG_MAX + 1, cfg_preset);
|
||||
Control_config_presets.push_back(cfg_preset);
|
||||
|
||||
SCP_string preset_name;
|
||||
if (optional_string("$Name:")) {
|
||||
stuff_string_line(preset_name);
|
||||
try {
|
||||
if (cf_exists_full("controlconfigdefaults.tbl", CF_TYPE_TABLES)) {
|
||||
read_file_text("controlconfigdefaults.tbl", CF_TYPE_TABLES);
|
||||
} else {
|
||||
preset_name = "<unnamed preset>";
|
||||
read_file_text_from_default(defaults_get_file("controlconfigdefaults.tbl"));
|
||||
}
|
||||
Control_config_preset_names.push_back(preset_name);
|
||||
|
||||
while (required_string_either("#End","$Bind Name:")) {
|
||||
const int iBufferLength = 64;
|
||||
char szTempBuffer[iBufferLength];
|
||||
reset_parse();
|
||||
|
||||
required_string("$Bind Name:");
|
||||
stuff_string(szTempBuffer, F_NAME, iBufferLength);
|
||||
// start parsing
|
||||
// TODO: Split this out into more helps. Too many tabs!
|
||||
while(optional_string("#ControlConfigOverride")) {
|
||||
config_item *cfg_preset = new config_item[CCFG_MAX + 1];
|
||||
std::copy(Control_config, Control_config + CCFG_MAX + 1, cfg_preset);
|
||||
Control_config_presets.push_back(cfg_preset);
|
||||
|
||||
const size_t cCntrlAryLength = sizeof(Control_config) / sizeof(Control_config[0]);
|
||||
for (size_t i = 0; i < cCntrlAryLength; ++i) {
|
||||
config_item& r_ccConfig = cfg_preset[i];
|
||||
SCP_string preset_name;
|
||||
if (optional_string("$Name:")) {
|
||||
stuff_string_line(preset_name);
|
||||
} else {
|
||||
preset_name = "<unnamed preset>";
|
||||
}
|
||||
Control_config_preset_names.push_back(preset_name);
|
||||
|
||||
if (!strcmp(szTempBuffer, r_ccConfig.text)) {
|
||||
/**
|
||||
* short key_default;
|
||||
* short joy_default;
|
||||
* char tab;
|
||||
* bool hasXSTR;
|
||||
* char type;
|
||||
*/
|
||||
while (required_string_either("#End","$Bind Name:")) {
|
||||
const int iBufferLength = 64;
|
||||
char szTempBuffer[iBufferLength];
|
||||
|
||||
int iTemp;
|
||||
required_string("$Bind Name:");
|
||||
stuff_string(szTempBuffer, F_NAME, iBufferLength);
|
||||
|
||||
if (optional_string("$Key Default:")) {
|
||||
if (optional_string("NONE")) {
|
||||
r_ccConfig.key_default = (short)-1;
|
||||
} else {
|
||||
stuff_string(szTempBuffer, F_NAME, iBufferLength);
|
||||
r_ccConfig.key_default = (short)mKeyNameToVal[szTempBuffer];
|
||||
const size_t cCntrlAryLength = sizeof(Control_config) / sizeof(Control_config[0]);
|
||||
for (size_t i = 0; i < cCntrlAryLength; ++i) {
|
||||
config_item& r_ccConfig = cfg_preset[i];
|
||||
|
||||
if (!strcmp(szTempBuffer, r_ccConfig.text)) {
|
||||
/**
|
||||
* short key_default;
|
||||
* short joy_default;
|
||||
* char tab;
|
||||
* bool hasXSTR;
|
||||
* char type;
|
||||
*/
|
||||
|
||||
int iTemp;
|
||||
|
||||
if (optional_string("$Key Default:")) {
|
||||
if (optional_string("NONE")) {
|
||||
r_ccConfig.key_default = (short)-1;
|
||||
} else {
|
||||
stuff_string(szTempBuffer, F_NAME, iBufferLength);
|
||||
r_ccConfig.key_default = (short)mKeyNameToVal[szTempBuffer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (optional_string("$Joy Default:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.joy_default = (short)iTemp;
|
||||
}
|
||||
if (optional_string("$Joy Default:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.joy_default = (short)iTemp;
|
||||
}
|
||||
|
||||
if (optional_string("$Key Mod Shift:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.key_default |= (iTemp == 1) ? KEY_SHIFTED : 0;
|
||||
}
|
||||
if (optional_string("$Key Mod Shift:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.key_default |= (iTemp == 1) ? KEY_SHIFTED : 0;
|
||||
}
|
||||
|
||||
if (optional_string("$Key Mod Alt:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.key_default |= (iTemp == 1) ? KEY_ALTED : 0;
|
||||
}
|
||||
if (optional_string("$Key Mod Alt:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.key_default |= (iTemp == 1) ? KEY_ALTED : 0;
|
||||
}
|
||||
|
||||
if (optional_string("$Key Mod Ctrl:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.key_default |= (iTemp == 1) ? KEY_CTRLED : 0;
|
||||
}
|
||||
if (optional_string("$Key Mod Ctrl:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.key_default |= (iTemp == 1) ? KEY_CTRLED : 0;
|
||||
}
|
||||
|
||||
if (optional_string("$Category:")) {
|
||||
stuff_string(szTempBuffer, F_NAME, iBufferLength);
|
||||
r_ccConfig.tab = (char)mCCTabNameToVal[szTempBuffer];
|
||||
}
|
||||
if (optional_string("$Category:")) {
|
||||
stuff_string(szTempBuffer, F_NAME, iBufferLength);
|
||||
r_ccConfig.tab = (char)mCCTabNameToVal[szTempBuffer];
|
||||
}
|
||||
|
||||
if (optional_string("$Has XStr:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.hasXSTR = (iTemp == 1);
|
||||
}
|
||||
if (optional_string("$Has XStr:")) {
|
||||
stuff_int(&iTemp);
|
||||
r_ccConfig.hasXSTR = (iTemp == 1);
|
||||
}
|
||||
|
||||
if (optional_string("$Type:")) {
|
||||
stuff_string(szTempBuffer, F_NAME, iBufferLength);
|
||||
r_ccConfig.type = (char)mCCTypeNameToVal[szTempBuffer];
|
||||
}
|
||||
if (optional_string("$Type:")) {
|
||||
stuff_string(szTempBuffer, F_NAME, iBufferLength);
|
||||
r_ccConfig.type = (char)mCCTypeNameToVal[szTempBuffer];
|
||||
}
|
||||
|
||||
if (optional_string("+Disable")) {
|
||||
r_ccConfig.disabled = true;
|
||||
if (optional_string("+Disable")) {
|
||||
r_ccConfig.disabled = true;
|
||||
}
|
||||
if (optional_string("$Disable:")) {
|
||||
stuff_boolean(&r_ccConfig.disabled);
|
||||
}
|
||||
|
||||
// Nerf the buffer now.
|
||||
szTempBuffer[0] = '\0';
|
||||
} else if ((i + 1) == cCntrlAryLength) {
|
||||
error_display(1, "Bind Name not found: %s\n", szTempBuffer);
|
||||
advance_to_eoln(NULL);
|
||||
ignore_white_space();
|
||||
return;
|
||||
}
|
||||
if (optional_string("$Disable:")) {
|
||||
stuff_boolean(&r_ccConfig.disabled);
|
||||
}
|
||||
|
||||
// Nerf the buffer now.
|
||||
szTempBuffer[0] = '\0';
|
||||
} else if ((i + 1) == cCntrlAryLength) {
|
||||
error_display(1, "Bind Name not found: %s\n", szTempBuffer);
|
||||
advance_to_eoln(NULL);
|
||||
ignore_white_space();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required_string("#End");
|
||||
required_string("#End");
|
||||
}
|
||||
}
|
||||
|
||||
catch (const parse::ParseException& e)
|
||||
{
|
||||
mprintf(("TABLES: Unable to parse 'controlconfigdefaults.tbl'! Error message = %s.\n", e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Overwrite the control config with the first preset that was found
|
||||
if (Control_config_presets.size() > 0) {
|
||||
if (!Control_config_presets.empty()) {
|
||||
std::copy(Control_config_presets[0], Control_config_presets[0] + CCFG_MAX + 1, Control_config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "cutscene/Decoder.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace cutscene {
|
||||
Decoder::Decoder() : m_decoding(true) {
|
||||
}
|
||||
|
||||
Decoder::~Decoder() {
|
||||
}
|
||||
|
||||
bool Decoder::tryPopAudioData(AudioFramePtr& out) {
|
||||
auto res = m_audioQueue->try_pull_front(out);
|
||||
return res == queue_op_status::success;
|
||||
}
|
||||
|
||||
bool Decoder::tryPopVideoFrame(VideoFramePtr& out) {
|
||||
auto res = m_videoQueue->try_pull_front(out);
|
||||
return res == queue_op_status::success;
|
||||
}
|
||||
|
||||
void Decoder::initializeQueues(size_t queueSize) {
|
||||
m_queueSize = queueSize;
|
||||
|
||||
m_videoQueue.reset(new sync_bounded_queue<VideoFramePtr>(m_queueSize));
|
||||
m_audioQueue.reset(new sync_bounded_queue<AudioFramePtr>(m_queueSize));
|
||||
}
|
||||
|
||||
void Decoder::stopDecoder() {
|
||||
m_decoding = false;
|
||||
|
||||
m_videoQueue->close();
|
||||
m_audioQueue->close();
|
||||
}
|
||||
|
||||
bool Decoder::canPushAudioData() {
|
||||
return !isAudioQueueFull();
|
||||
}
|
||||
|
||||
void Decoder::pushAudioData(AudioFramePtr&& data) {
|
||||
Assertion(data, "Invalid audio data passed!");
|
||||
|
||||
try {
|
||||
m_audioQueue->push_back(std::move(data));
|
||||
}
|
||||
catch (sync_queue_is_closed&) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::canPushVideoData() {
|
||||
return !isVideoQueueFull();
|
||||
}
|
||||
|
||||
void Decoder::pushFrameData(VideoFramePtr&& frame) {
|
||||
Assertion(frame, "Invalid video data passed!");
|
||||
|
||||
try {
|
||||
m_videoQueue->push_back(std::move(frame));
|
||||
}
|
||||
catch (sync_queue_is_closed&) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "utils/boost/syncboundedqueue.h"
|
||||
|
||||
namespace cutscene {
|
||||
struct FrameSize {
|
||||
size_t width = 0;
|
||||
size_t height = 0;
|
||||
size_t stride = 0;
|
||||
};
|
||||
|
||||
class VideoFrame {
|
||||
protected:
|
||||
VideoFrame() {}
|
||||
|
||||
public:
|
||||
virtual ~VideoFrame() {}
|
||||
|
||||
double frameTime = -1.0;
|
||||
int id = -1;
|
||||
|
||||
FrameSize ySize;
|
||||
FrameSize uvSize;
|
||||
|
||||
struct DataPointers {
|
||||
ubyte* y;
|
||||
ubyte* u;
|
||||
ubyte* v;
|
||||
};
|
||||
|
||||
virtual DataPointers getDataPointers() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Pointer type for passing frame data between threads
|
||||
*
|
||||
*/
|
||||
template<typename T>
|
||||
using FramePtr = std::unique_ptr<T>;
|
||||
|
||||
typedef std::unique_ptr<VideoFrame> VideoFramePtr;
|
||||
|
||||
struct MovieProperties {
|
||||
FrameSize size;
|
||||
|
||||
float fps = -1.0f;
|
||||
};
|
||||
|
||||
struct AudioFrame {
|
||||
SCP_vector<short> audioData;
|
||||
|
||||
int channels = -1;
|
||||
|
||||
int rate = -1;
|
||||
};
|
||||
typedef std::unique_ptr<AudioFrame> AudioFramePtr;
|
||||
|
||||
/**
|
||||
* @brief Abstract class for decoding a video or audio stream
|
||||
*
|
||||
* A decoder maintaines two queues of decoded audio and video frames which are filled from a background thread and
|
||||
* retrieved by the main thread.
|
||||
*/
|
||||
class Decoder {
|
||||
private:
|
||||
std::unique_ptr<sync_bounded_queue<VideoFramePtr>> m_videoQueue;
|
||||
std::unique_ptr<sync_bounded_queue<AudioFramePtr>> m_audioQueue;
|
||||
|
||||
bool m_decoding;
|
||||
size_t m_queueSize = 0;
|
||||
|
||||
Decoder(const Decoder&) SCP_DELETED_FUNCTION;
|
||||
|
||||
Decoder& operator=(const Decoder&) SCP_DELETED_FUNCTION;
|
||||
protected:
|
||||
Decoder();
|
||||
|
||||
public:
|
||||
|
||||
virtual ~Decoder();
|
||||
|
||||
/**
|
||||
* @brief Initializes the decoder
|
||||
*
|
||||
* @note A implementation should initialize the datastructures required for decoding here.
|
||||
*
|
||||
* @param fileName The name of the file that should be opened
|
||||
* @return @c true if the initialization was successfull, @c false otherwise
|
||||
*/
|
||||
virtual bool initialize(const SCP_string& fileName) = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the properties of the video
|
||||
* @return The properties
|
||||
*/
|
||||
virtual MovieProperties getProperties() = 0;
|
||||
|
||||
/**
|
||||
* @brief Starts decoding the video
|
||||
*/
|
||||
virtual void startDecoding() = 0;
|
||||
|
||||
/**
|
||||
* @brief Determines if the video has audio
|
||||
* @return @c true if audio is included in the video
|
||||
*/
|
||||
virtual bool hasAudio() = 0;
|
||||
|
||||
/**
|
||||
* @brief Ends decoding of the video file
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
bool isAudioQueueFull() { return m_audioQueue->size() == m_queueSize; }
|
||||
|
||||
bool isAudioFrameAvailable() { return !m_audioQueue->empty(); }
|
||||
|
||||
size_t getAudioQueueSize() { return m_audioQueue->size(); }
|
||||
|
||||
bool tryPopAudioData(AudioFramePtr&);
|
||||
|
||||
bool isVideoQueueFull() { return m_videoQueue->size() == m_queueSize; }
|
||||
|
||||
bool isVideoFrameAvailable() { return !m_videoQueue->empty(); }
|
||||
|
||||
size_t getVideoQueueSize() { return m_videoQueue->size(); }
|
||||
|
||||
bool tryPopVideoFrame(VideoFramePtr&);
|
||||
|
||||
void stopDecoder();
|
||||
|
||||
bool isDecoding() { return m_decoding; }
|
||||
|
||||
protected:
|
||||
void initializeQueues(size_t queueSize);
|
||||
|
||||
bool canPushAudioData();
|
||||
|
||||
void pushAudioData(AudioFramePtr&& data);
|
||||
|
||||
bool canPushVideoData();
|
||||
|
||||
void pushFrameData(VideoFramePtr&& frame);
|
||||
};
|
||||
}
|
||||
+197
-215
@@ -5,7 +5,7 @@
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* source.
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -26,13 +26,13 @@
|
||||
extern int Cmdline_nomovies;
|
||||
|
||||
|
||||
char *Cutscene_bitmap_name[GR_NUM_RESOLUTIONS] = {
|
||||
"ViewFootage",
|
||||
"2_ViewFootage"
|
||||
const char* Cutscene_bitmap_name[GR_NUM_RESOLUTIONS] = {
|
||||
"ViewFootage",
|
||||
"2_ViewFootage"
|
||||
};
|
||||
char *Cutscene_mask_name[GR_NUM_RESOLUTIONS] = {
|
||||
"ViewFootage-m",
|
||||
"2_ViewFootage-m"
|
||||
const char* Cutscene_mask_name[GR_NUM_RESOLUTIONS] = {
|
||||
"ViewFootage-m",
|
||||
"2_ViewFootage-m"
|
||||
};
|
||||
|
||||
int Description_index;
|
||||
@@ -40,8 +40,9 @@ SCP_vector<cutscene_info> Cutscenes;
|
||||
|
||||
void cutscene_close()
|
||||
{
|
||||
for(SCP_vector<cutscene_info>::iterator cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut)
|
||||
if(cut->description != NULL) {
|
||||
for (SCP_vector<cutscene_info>::iterator cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut)
|
||||
if (cut->description != NULL)
|
||||
{
|
||||
vm_free(cut->description);
|
||||
cut->description = NULL;
|
||||
}
|
||||
@@ -81,19 +82,24 @@ void cutscene_init()
|
||||
cutinfo.description = vm_strdup(buf);
|
||||
|
||||
if (optional_string("$cd:"))
|
||||
stuff_int(&cutinfo.cd);
|
||||
else
|
||||
cutinfo.cd = 0;
|
||||
{
|
||||
// Option isn't needed anymore. Consume the token and ignore it
|
||||
|
||||
int junk;
|
||||
stuff_int(&junk);
|
||||
}
|
||||
|
||||
cutinfo.viewable = false;
|
||||
|
||||
if (isFirstCutscene) {
|
||||
if (isFirstCutscene)
|
||||
{
|
||||
isFirstCutscene = false;
|
||||
// The original code assumes the first movie is the intro, so always viewable
|
||||
cutinfo.viewable = true;
|
||||
}
|
||||
|
||||
if (optional_string("$Always Viewable:")) {
|
||||
if (optional_string("$Always Viewable:"))
|
||||
{
|
||||
stuff_boolean(&cutinfo.viewable);
|
||||
}
|
||||
|
||||
@@ -109,44 +115,34 @@ void cutscene_init()
|
||||
}
|
||||
}
|
||||
|
||||
// function to return 0 based index of which CD a particular movie is on
|
||||
// returns -1 on failure.
|
||||
int cutscenes_get_cd_num( char *filename )
|
||||
{
|
||||
for (SCP_vector<cutscene_info>::iterator cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut) {
|
||||
if ( !stricmp(cut->filename, filename) ) {
|
||||
return (cut->cd - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// marks a cutscene as viewable
|
||||
void cutscene_mark_viewable(char *filename)
|
||||
void cutscene_mark_viewable(const char* filename)
|
||||
{
|
||||
char cut_file[MAX_FILENAME_LEN];
|
||||
char file[MAX_FILENAME_LEN];
|
||||
|
||||
Assert(filename!=NULL);
|
||||
Assert(filename != NULL);
|
||||
|
||||
// strip off extension
|
||||
strcpy_s( file, filename );
|
||||
char *p = strchr( file, '.' );
|
||||
if ( p ) {
|
||||
strcpy_s(file, filename);
|
||||
char* p = strchr(file, '.');
|
||||
if (p)
|
||||
{
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
// change to lower case
|
||||
strlwr(file);
|
||||
int i = 0;
|
||||
for (SCP_vector<cutscene_info>::iterator cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut) {
|
||||
for (SCP_vector<cutscene_info>::iterator cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut)
|
||||
{
|
||||
// change the cutscene file name to lower case
|
||||
strcpy_s(cut_file, cut->filename);
|
||||
strlwr(cut_file);
|
||||
|
||||
// see if the stripped filename matches the cutscene filename
|
||||
if ( strstr(cut_file, file) != NULL ) {
|
||||
if (strstr(cut_file, file) != NULL)
|
||||
{
|
||||
cut->viewable = true;
|
||||
return;
|
||||
}
|
||||
@@ -156,17 +152,17 @@ void cutscene_mark_viewable(char *filename)
|
||||
Warning(LOCATION, "Could not find cutscene '%s' in listing; cannot mark it viewable...", filename);
|
||||
}
|
||||
|
||||
#define NUM_BUTTONS 8
|
||||
#define NUM_BUTTONS 8
|
||||
|
||||
#define TECH_DATABASE_BUTTON 0
|
||||
#define SIMULATOR_BUTTON 1
|
||||
#define CUTSCENES_BUTTON 2
|
||||
#define CREDITS_BUTTON 3
|
||||
#define TECH_DATABASE_BUTTON 0
|
||||
#define SIMULATOR_BUTTON 1
|
||||
#define CUTSCENES_BUTTON 2
|
||||
#define CREDITS_BUTTON 3
|
||||
|
||||
#define SCROLL_UP_BUTTON 4
|
||||
#define SCROLL_DOWN_BUTTON 5
|
||||
#define PLAY_BUTTON 6
|
||||
#define EXIT_BUTTON 7
|
||||
#define SCROLL_UP_BUTTON 4
|
||||
#define SCROLL_DOWN_BUTTON 5
|
||||
#define PLAY_BUTTON 6
|
||||
#define EXIT_BUTTON 7
|
||||
|
||||
static SCP_vector<int> Cutscene_list;
|
||||
//static int Stats_scroll_offset; // not used - taylor
|
||||
@@ -177,155 +173,102 @@ static UI_BUTTON List_region;
|
||||
static UI_WINDOW Ui_window;
|
||||
|
||||
static ui_button_info Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
|
||||
{ // GR_640
|
||||
ui_button_info("TDB_00", 7, 3, 37, 7, 0), // tech database 1
|
||||
ui_button_info("TDB_01", 7, 18, 37, 23, 1), // tech database 2
|
||||
ui_button_info("TDB_02", 7, 34, 37, 38, 2), // tech database 3
|
||||
ui_button_info("TDB_03", 7, 49, 37, 54, 3), // tech database 4
|
||||
{ // GR_640
|
||||
ui_button_info("TDB_00", 7, 3, 37, 7, 0), // tech database 1
|
||||
ui_button_info("TDB_01", 7, 18, 37, 23, 1), // tech database 2
|
||||
ui_button_info("TDB_02", 7, 34, 37, 38, 2), // tech database 3
|
||||
ui_button_info("TDB_03", 7, 49, 37, 54, 3), // tech database 4
|
||||
|
||||
ui_button_info("VFB_04", 6, 318, -1, -1, 4), // scroll up
|
||||
ui_button_info("VFB_05", 36, 318, -1, -1, 5), // scroll down
|
||||
ui_button_info("VFB_06", 578, 319, 587, 366, 6), // play
|
||||
ui_button_info("VFB_07", 574, 431, 587, 413, 7), // exit
|
||||
},
|
||||
{ // GR_1024
|
||||
ui_button_info("2_TDB_00", 12, 5, 59, 12, 0), // tech database 1
|
||||
ui_button_info("2_TDB_01", 12, 31, 59, 37, 1), // tech database 2
|
||||
ui_button_info("2_TDB_02", 12, 56, 59, 62, 2), // tech database 3
|
||||
ui_button_info("2_TDB_03", 12, 81, 59, 88, 3), // tech database 4
|
||||
ui_button_info("VFB_04", 6, 318, -1, -1, 4), // scroll up
|
||||
ui_button_info("VFB_05", 36, 318, -1, -1, 5), // scroll down
|
||||
ui_button_info("VFB_06", 578, 319, 587, 366, 6), // play
|
||||
ui_button_info("VFB_07", 574, 431, 587, 413, 7), // exit
|
||||
},
|
||||
{ // GR_1024
|
||||
ui_button_info("2_TDB_00", 12, 5, 59, 12, 0), // tech database 1
|
||||
ui_button_info("2_TDB_01", 12, 31, 59, 37, 1), // tech database 2
|
||||
ui_button_info("2_TDB_02", 12, 56, 59, 62, 2), // tech database 3
|
||||
ui_button_info("2_TDB_03", 12, 81, 59, 88, 3), // tech database 4
|
||||
|
||||
ui_button_info("2_VFB_04", 9, 509, -1, -1, 4), // scroll up
|
||||
ui_button_info("2_VFB_05", 58, 509, -1, -1, 5), // scroll down
|
||||
ui_button_info("2_VFB_06", 925, 511, 940, 586, 6), // play
|
||||
ui_button_info("2_VFB_07", 918, 689, 940, 661, 7), // exit
|
||||
}
|
||||
ui_button_info("2_VFB_04", 9, 509, -1, -1, 4), // scroll up
|
||||
ui_button_info("2_VFB_05", 58, 509, -1, -1, 5), // scroll down
|
||||
ui_button_info("2_VFB_06", 925, 511, 940, 586, 6), // play
|
||||
ui_button_info("2_VFB_07", 918, 689, 940, 661, 7), // exit
|
||||
}
|
||||
};
|
||||
|
||||
// text
|
||||
#define NUM_CUTSCENE_TEXT 6
|
||||
#define NUM_CUTSCENE_TEXT 6
|
||||
UI_XSTR Cutscene_text[GR_NUM_RESOLUTIONS][NUM_CUTSCENE_TEXT] = {
|
||||
{ // GR_640
|
||||
{"Technical Database", 1055, 37, 7, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][TECH_DATABASE_BUTTON].button },
|
||||
{"Mission Simulator", 1056, 37, 23, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][SIMULATOR_BUTTON].button },
|
||||
{"Cutscenes", 1057, 37, 38, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][CUTSCENES_BUTTON].button },
|
||||
{"Credits", 1058, 37, 54, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][CREDITS_BUTTON].button },
|
||||
|
||||
{"Play", 1335, 587, 366, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][PLAY_BUTTON].button },
|
||||
{"Exit", 1419, 587, 413, UI_XSTR_COLOR_PINK, -1, &Buttons[0][EXIT_BUTTON].button },
|
||||
},
|
||||
{ // GR_1024
|
||||
{"Technical Database", 1055, 59, 12, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][TECH_DATABASE_BUTTON].button },
|
||||
{"Mission Simulator", 1056, 59, 37, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][SIMULATOR_BUTTON].button },
|
||||
{"Cutscenes", 1057, 59, 62, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][CUTSCENES_BUTTON].button },
|
||||
{"Credits", 1058, 59, 88, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][CREDITS_BUTTON].button },
|
||||
|
||||
{"Play", 1335, 940, 586, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][PLAY_BUTTON].button },
|
||||
{"Exit", 1419, 940, 661, UI_XSTR_COLOR_PINK, -1, &Buttons[1][EXIT_BUTTON].button },
|
||||
}
|
||||
{ // GR_640
|
||||
{"Technical Database", 1055, 37, 7, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][TECH_DATABASE_BUTTON].button},
|
||||
{"Mission Simulator", 1056, 37, 23, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][SIMULATOR_BUTTON].button},
|
||||
{"Cutscenes", 1057, 37, 38, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][CUTSCENES_BUTTON].button},
|
||||
{"Credits", 1058, 37, 54, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][CREDITS_BUTTON].button},
|
||||
|
||||
{"Play", 1335, 587, 366, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][PLAY_BUTTON].button},
|
||||
{"Exit", 1419, 587, 413, UI_XSTR_COLOR_PINK, -1, &Buttons[0][EXIT_BUTTON].button},
|
||||
},
|
||||
{ // GR_1024
|
||||
{"Technical Database", 1055, 59, 12, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][TECH_DATABASE_BUTTON].button},
|
||||
{"Mission Simulator", 1056, 59, 37, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][SIMULATOR_BUTTON].button},
|
||||
{"Cutscenes", 1057, 59, 62, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][CUTSCENES_BUTTON].button},
|
||||
{"Credits", 1058, 59, 88, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][CREDITS_BUTTON].button},
|
||||
|
||||
{"Play", 1335, 940, 586, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][PLAY_BUTTON].button},
|
||||
{"Exit", 1419, 940, 661, UI_XSTR_COLOR_PINK, -1, &Buttons[1][EXIT_BUTTON].button},
|
||||
}
|
||||
};
|
||||
|
||||
int Cutscene_list_coords[GR_NUM_RESOLUTIONS][4] = {
|
||||
{ // GR_640
|
||||
9, 117, 621, 198
|
||||
},
|
||||
{ // GR_1024
|
||||
14, 188, 994, 316
|
||||
}
|
||||
{ // GR_640
|
||||
9, 117, 621, 198
|
||||
},
|
||||
{ // GR_1024
|
||||
14, 188, 994, 316
|
||||
}
|
||||
};
|
||||
|
||||
int Cutscene_desc_coords[GR_NUM_RESOLUTIONS][4] = {
|
||||
{ // GR_640
|
||||
9, 378, 484, 73
|
||||
},
|
||||
{ // GR_1024
|
||||
14, 605, 775, 117
|
||||
}
|
||||
{ // GR_640
|
||||
9, 378, 484, 73
|
||||
},
|
||||
{ // GR_1024
|
||||
14, 605, 775, 117
|
||||
}
|
||||
};
|
||||
|
||||
#define MAX_TEXT_LINES 20
|
||||
#define MAX_TEXT_LINES 20
|
||||
int Cutscene_max_text_lines[GR_NUM_RESOLUTIONS] = {
|
||||
10,
|
||||
MAX_TEXT_LINES
|
||||
10,
|
||||
MAX_TEXT_LINES
|
||||
};
|
||||
#define MAX_TEXT_LINE_LEN 256
|
||||
#define MAX_TEXT_LINE_LEN 256
|
||||
|
||||
static int Text_size;
|
||||
static int Text_offset = 0;
|
||||
static int Text_line_size[MAX_TEXT_LINES];
|
||||
static const char *Text_lines[MAX_TEXT_LINES];
|
||||
static const char* Text_lines[MAX_TEXT_LINES];
|
||||
|
||||
|
||||
int cutscenes_validate_cd(char *mve_name, int prompt_for_cd)
|
||||
{
|
||||
int cd_present = 0;
|
||||
int cd_drive_num;
|
||||
int cd_mve_is_on;
|
||||
char volume_name[MAX_PATH_LEN];
|
||||
|
||||
int num_attempts = 0;
|
||||
|
||||
while(1) {
|
||||
int path_set_ok;
|
||||
|
||||
cd_mve_is_on = cutscenes_get_cd_num(mve_name);
|
||||
if ((cd_mve_is_on != 0) && (cd_mve_is_on != 1) && (cd_mve_is_on != 2)) {
|
||||
cd_present = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
sprintf(volume_name, NOX("FREESPACE2_%c"), '1' + cd_mve_is_on);
|
||||
|
||||
cd_drive_num = find_freespace_cd(volume_name);
|
||||
path_set_ok = set_cdrom_path(cd_drive_num);
|
||||
|
||||
if ( path_set_ok ) {
|
||||
cd_present = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !prompt_for_cd ) {
|
||||
cd_present = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// no CD found, so prompt user
|
||||
char popup_msg[256];
|
||||
int popup_rval;
|
||||
|
||||
sprintf(popup_msg, XSTR( "Movie not found\n\nInsert FreeSpace CD #%d to continue", 203), cd_mve_is_on+1);
|
||||
|
||||
popup_rval = popup(PF_BODY_BIG, 2, POPUP_CANCEL, POPUP_OK, popup_msg);
|
||||
if ( popup_rval != 1 ) {
|
||||
cd_present = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( num_attempts++ > 5 ) {
|
||||
cd_present = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cd_present;
|
||||
}
|
||||
|
||||
void cutscenes_screen_play()
|
||||
{
|
||||
char name[MAX_FILENAME_LEN]; // *full_name
|
||||
int which_cutscene;
|
||||
|
||||
Assert( (Selected_line >= 0) && (Selected_line < (int)Cutscene_list.size()) );
|
||||
Assert((Selected_line >= 0) && (Selected_line < (int) Cutscene_list.size()));
|
||||
which_cutscene = Cutscene_list[Selected_line];
|
||||
|
||||
strcpy_s(name, Cutscenes[which_cutscene].filename );
|
||||
strcpy_s(name, Cutscenes[which_cutscene].filename);
|
||||
// full_name = cf_add_ext(name, NOX(".mve"));
|
||||
|
||||
main_hall_stop_music(true);
|
||||
main_hall_stop_ambient();
|
||||
int rval = movie_play(name);
|
||||
auto rval = movie::play(name);
|
||||
main_hall_start_music();
|
||||
|
||||
if ( !rval ) {
|
||||
if (!rval)
|
||||
{
|
||||
char str[256];
|
||||
|
||||
if (Cmdline_nomovies)
|
||||
@@ -333,20 +276,21 @@ void cutscenes_screen_play()
|
||||
else
|
||||
sprintf(str, XSTR("Unable to play movie %s.", 204), Cutscenes[which_cutscene].name);
|
||||
|
||||
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, str );
|
||||
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, str);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void cutscenes_screen_scroll_line_up()
|
||||
{
|
||||
if (Selected_line) {
|
||||
if (Selected_line)
|
||||
{
|
||||
Selected_line--;
|
||||
gamesnd_play_iface(SND_SCROLL);
|
||||
|
||||
} else
|
||||
gamesnd_play_iface(SND_GENERAL_FAIL);
|
||||
|
||||
|
||||
if (Selected_line < Scroll_offset)
|
||||
Scroll_offset = Selected_line;
|
||||
}
|
||||
@@ -355,15 +299,17 @@ void cutscenes_screen_scroll_line_down()
|
||||
{
|
||||
int h;
|
||||
|
||||
if (Selected_line < (int)Cutscene_list.size() - 1) {
|
||||
if (Selected_line < (int) Cutscene_list.size() - 1)
|
||||
{
|
||||
Selected_line++;
|
||||
gamesnd_play_iface(SND_SCROLL);
|
||||
|
||||
} else
|
||||
gamesnd_play_iface(SND_GENERAL_FAIL);
|
||||
|
||||
|
||||
h = Cutscene_list_coords[gr_screen.res][3] / gr_get_font_height();
|
||||
if (Selected_line >= Scroll_offset + h){
|
||||
if (Selected_line >= Scroll_offset + h)
|
||||
{
|
||||
Scroll_offset++;
|
||||
}
|
||||
}
|
||||
@@ -372,17 +318,20 @@ void cutscenes_screen_scroll_screen_up()
|
||||
{
|
||||
int h;
|
||||
|
||||
if (Scroll_offset) {
|
||||
if (Scroll_offset)
|
||||
{
|
||||
Scroll_offset--;
|
||||
Assert(Selected_line > Scroll_offset);
|
||||
h = Cutscene_list_coords[gr_screen.res][3] / gr_get_font_height();
|
||||
while (Selected_line >= Scroll_offset + h){
|
||||
while (Selected_line >= Scroll_offset + h)
|
||||
{
|
||||
Selected_line--;
|
||||
}
|
||||
|
||||
gamesnd_play_iface(SND_SCROLL);
|
||||
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
gamesnd_play_iface(SND_GENERAL_FAIL);
|
||||
}
|
||||
}
|
||||
@@ -392,21 +341,25 @@ void cutscenes_screen_scroll_screen_down()
|
||||
int h;
|
||||
|
||||
h = Cutscene_list_coords[gr_screen.res][3] / gr_get_font_height();
|
||||
if (Scroll_offset + h < (int)Cutscene_list.size()) {
|
||||
if (Scroll_offset + h < (int) Cutscene_list.size())
|
||||
{
|
||||
Scroll_offset++;
|
||||
if (Selected_line < Scroll_offset){
|
||||
if (Selected_line < Scroll_offset)
|
||||
{
|
||||
Selected_line = Scroll_offset;
|
||||
}
|
||||
|
||||
gamesnd_play_iface(SND_SCROLL);
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
gamesnd_play_iface(SND_GENERAL_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
int cutscenes_screen_button_pressed(int n)
|
||||
{
|
||||
switch (n) {
|
||||
switch (n)
|
||||
{
|
||||
case TECH_DATABASE_BUTTON:
|
||||
gamesnd_play_iface(SND_SWITCH_SCREENS);
|
||||
gameseq_post_event(GS_EVENT_TECH_MENU);
|
||||
@@ -447,12 +400,13 @@ int cutscenes_screen_button_pressed(int n)
|
||||
void cutscenes_screen_init()
|
||||
{
|
||||
int i;
|
||||
ui_button_info *b;
|
||||
ui_button_info* b;
|
||||
|
||||
Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
|
||||
Ui_window.set_mask_bmap(Cutscene_mask_name[gr_screen.res]);
|
||||
|
||||
for (i=0; i<NUM_BUTTONS; i++) {
|
||||
for (i = 0; i < NUM_BUTTONS; i++)
|
||||
{
|
||||
b = &Buttons[gr_screen.res][i];
|
||||
|
||||
b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
|
||||
@@ -463,15 +417,17 @@ void cutscenes_screen_init()
|
||||
}
|
||||
|
||||
// add xstrs
|
||||
for(i=0; i<NUM_CUTSCENE_TEXT; i++){
|
||||
for (i = 0; i < NUM_CUTSCENE_TEXT; i++)
|
||||
{
|
||||
Ui_window.add_XSTR(&Cutscene_text[gr_screen.res][i]);
|
||||
}
|
||||
|
||||
Buttons[gr_screen.res][EXIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
|
||||
Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
|
||||
Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
|
||||
Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
|
||||
|
||||
List_region.create(&Ui_window, "", Cutscene_list_coords[gr_screen.res][0], Cutscene_list_coords[gr_screen.res][1], Cutscene_list_coords[gr_screen.res][2], Cutscene_list_coords[gr_screen.res][3], 0, 1);
|
||||
List_region.create(&Ui_window, "", Cutscene_list_coords[gr_screen.res][0], Cutscene_list_coords[gr_screen.res][1],
|
||||
Cutscene_list_coords[gr_screen.res][2], Cutscene_list_coords[gr_screen.res][3], 0, 1);
|
||||
List_region.hide();
|
||||
|
||||
// set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
|
||||
@@ -482,11 +438,13 @@ void cutscenes_screen_init()
|
||||
Scroll_offset = Selected_line = 0;
|
||||
Description_index = -1;
|
||||
|
||||
Cutscene_list.clear();
|
||||
|
||||
Cutscene_list.clear();
|
||||
|
||||
int u = 0;
|
||||
for (SCP_vector<cutscene_info>::iterator cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut, u++) {
|
||||
if ( (*cut).viewable ) {
|
||||
for (SCP_vector<cutscene_info>::iterator cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut, u++)
|
||||
{
|
||||
if ((*cut).viewable)
|
||||
{
|
||||
Cutscene_list.push_back(u);
|
||||
}
|
||||
}
|
||||
@@ -507,7 +465,8 @@ void cutscenes_screen_do_frame()
|
||||
int select_tease_line = -1;
|
||||
|
||||
k = Ui_window.process();
|
||||
switch (k) {
|
||||
switch (k)
|
||||
{
|
||||
case KEY_DOWN: // select next line
|
||||
cutscenes_screen_scroll_line_down();
|
||||
break;
|
||||
@@ -542,96 +501,117 @@ void cutscenes_screen_do_frame()
|
||||
gameseq_post_event(GS_EVENT_OPTIONS_MENU);
|
||||
break;
|
||||
|
||||
// the "show-all" hotkey
|
||||
// the "show-all" hotkey
|
||||
case KEY_CTRLED | KEY_SHIFTED | KEY_S:
|
||||
{
|
||||
Cutscene_list.clear();
|
||||
Cutscene_list.clear();
|
||||
size_t size = Cutscenes.size();
|
||||
for (size_t t = 0; t < size; t++) {
|
||||
Cutscene_list.push_back((int)t);
|
||||
for (size_t t = 0; t < size; t++)
|
||||
{
|
||||
Cutscene_list.push_back((int) t);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} // end switch
|
||||
} // end switch
|
||||
|
||||
for (i=0; i<NUM_BUTTONS; i++){
|
||||
if (Buttons[gr_screen.res][i].button.pressed()){
|
||||
if (cutscenes_screen_button_pressed(i)){
|
||||
for (i = 0; i < NUM_BUTTONS; i++)
|
||||
{
|
||||
if (Buttons[gr_screen.res][i].button.pressed())
|
||||
{
|
||||
if (cutscenes_screen_button_pressed(i))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (List_region.button_down()) {
|
||||
if (List_region.button_down())
|
||||
{
|
||||
List_region.get_mouse_pos(NULL, &y);
|
||||
z = Scroll_offset + y / font_height;
|
||||
if ((z >= 0) && (z < (int)Cutscene_list.size()))
|
||||
if ((z >= 0) && (z < (int) Cutscene_list.size()))
|
||||
select_tease_line = z;
|
||||
}
|
||||
|
||||
if (List_region.pressed()) {
|
||||
|
||||
if (List_region.pressed())
|
||||
{
|
||||
List_region.get_mouse_pos(NULL, &y);
|
||||
z = Scroll_offset + y / font_height;
|
||||
if ((z >= 0) && (z < (int)Cutscene_list.size()))
|
||||
if ((z >= 0) && (z < (int) Cutscene_list.size()))
|
||||
Selected_line = z;
|
||||
}
|
||||
|
||||
GR_MAYBE_CLEAR_RES(Background_bitmap);
|
||||
if (Background_bitmap >= 0) {
|
||||
if (Background_bitmap >= 0)
|
||||
{
|
||||
gr_set_bitmap(Background_bitmap);
|
||||
gr_bitmap(0, 0, GR_RESIZE_MENU);
|
||||
}
|
||||
}
|
||||
|
||||
Ui_window.draw();
|
||||
|
||||
for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){
|
||||
if (Buttons[gr_screen.res][i].button.button_down()){
|
||||
for (i = TECH_DATABASE_BUTTON; i <= CREDITS_BUTTON; i++)
|
||||
{
|
||||
if (Buttons[gr_screen.res][i].button.button_down())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > CREDITS_BUTTON){
|
||||
if (i > CREDITS_BUTTON)
|
||||
{
|
||||
Buttons[gr_screen.res][CUTSCENES_BUTTON].button.draw_forced(2);
|
||||
}
|
||||
|
||||
y = 0;
|
||||
z = Scroll_offset;
|
||||
while (y + font_height <= Cutscene_list_coords[gr_screen.res][3]) {
|
||||
if (z >= (int)Cutscene_list.size()){
|
||||
while (y + font_height <= Cutscene_list_coords[gr_screen.res][3])
|
||||
{
|
||||
if (z >= (int) Cutscene_list.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (z == Selected_line){
|
||||
if (z == Selected_line)
|
||||
{
|
||||
gr_set_color_fast(&Color_text_selected);
|
||||
} else if (z == select_tease_line) {
|
||||
} else if (z == select_tease_line)
|
||||
{
|
||||
gr_set_color_fast(&Color_text_subselected);
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
gr_set_color_fast(&Color_text_normal);
|
||||
}
|
||||
|
||||
gr_printf_menu(Cutscene_list_coords[gr_screen.res][0], Cutscene_list_coords[gr_screen.res][1] + y, Cutscenes[Cutscene_list[z]].name);
|
||||
gr_printf_menu(Cutscene_list_coords[gr_screen.res][0], Cutscene_list_coords[gr_screen.res][1] + y,
|
||||
"%s", Cutscenes[Cutscene_list[z]].name);
|
||||
|
||||
y += font_height;
|
||||
z++;
|
||||
}
|
||||
|
||||
if (Description_index != Selected_line) {
|
||||
char *src = NULL;
|
||||
if (Description_index != Selected_line)
|
||||
{
|
||||
char* src = NULL;
|
||||
|
||||
Description_index = Selected_line;
|
||||
Text_size = 0;
|
||||
if ( Description_index < (int)Cutscene_list.size( ) &&
|
||||
(int)Cutscene_list[ Description_index ] < (int)Cutscenes.size( ) ) {
|
||||
if (Description_index < (int) Cutscene_list.size() &&
|
||||
(int) Cutscene_list[Description_index] < (int) Cutscenes.size())
|
||||
{
|
||||
src = Cutscenes[Cutscene_list[Description_index]].description;
|
||||
if (src) {
|
||||
Text_size = split_str(src, Cutscene_desc_coords[gr_screen.res][2], Text_line_size, Text_lines, Cutscene_max_text_lines[gr_screen.res]);
|
||||
if (src)
|
||||
{
|
||||
Text_size = split_str(src, Cutscene_desc_coords[gr_screen.res][2], Text_line_size, Text_lines,
|
||||
Cutscene_max_text_lines[gr_screen.res]);
|
||||
Assert(Text_size >= 0 && Text_size < Cutscene_max_text_lines[gr_screen.res]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Description_index >= 0) {
|
||||
if (Description_index >= 0)
|
||||
{
|
||||
int len;
|
||||
char line[MAX_TEXT_LINE_LEN + 1];
|
||||
|
||||
@@ -639,8 +619,9 @@ void cutscenes_screen_do_frame()
|
||||
|
||||
y = 0;
|
||||
z = Text_offset;
|
||||
while (y + font_height <= Cutscene_desc_coords[gr_screen.res][3]) {
|
||||
if (z >= Text_size || z >= MAX_TEXT_LINES-1)
|
||||
while (y + font_height <= Cutscene_desc_coords[gr_screen.res][3])
|
||||
{
|
||||
if (z >= Text_size || z >= MAX_TEXT_LINES - 1)
|
||||
break;
|
||||
|
||||
len = Text_line_size[z];
|
||||
@@ -649,7 +630,8 @@ void cutscenes_screen_do_frame()
|
||||
|
||||
strncpy(line, Text_lines[z], len);
|
||||
line[len] = 0;
|
||||
gr_string(Cutscene_desc_coords[gr_screen.res][0], Cutscene_desc_coords[gr_screen.res][1] + y, line, GR_RESIZE_MENU);
|
||||
gr_string(Cutscene_desc_coords[gr_screen.res][0], Cutscene_desc_coords[gr_screen.res][1] + y, line,
|
||||
GR_RESIZE_MENU);
|
||||
|
||||
y += font_height;
|
||||
z++;
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
#include "globalincs/globals.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
typedef struct cutscene_info {
|
||||
char filename[MAX_FILENAME_LEN];
|
||||
char name[NAME_LENGTH];
|
||||
char *description;
|
||||
int cd;
|
||||
bool viewable;
|
||||
typedef struct cutscene_info
|
||||
{
|
||||
char filename[MAX_FILENAME_LEN];
|
||||
char name[NAME_LENGTH];
|
||||
char* description;
|
||||
bool viewable;
|
||||
} cutscene_info;
|
||||
|
||||
#if SCP_COMPILER_IS_GNU
|
||||
@@ -33,13 +33,13 @@ extern SCP_vector<cutscene_info> Cutscenes;
|
||||
|
||||
// initializa table data
|
||||
void cutscene_init();
|
||||
int cutscene_get_cd_num(char *filename);
|
||||
|
||||
void cutscenes_screen_init();
|
||||
|
||||
void cutscenes_screen_close();
|
||||
|
||||
void cutscenes_screen_do_frame();
|
||||
|
||||
int cutscenes_validate_cd(char *mve_name, int prompt_for_cd = 1);
|
||||
void cutscene_mark_viewable(char *filename);
|
||||
void cutscene_mark_viewable(const char* filename);
|
||||
|
||||
#endif
|
||||
@@ -1,570 +0,0 @@
|
||||
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
extern int g_width, g_height;
|
||||
extern void *g_vBackBuf1, *g_vBackBuf2;
|
||||
|
||||
/* 16 bit decoding routines */
|
||||
|
||||
static ushort *backBuf1, *backBuf2;
|
||||
static int lookup_initialized;
|
||||
|
||||
static void dispatchDecoder16(ushort **pFrame, ubyte codeType, ubyte **pData, ubyte **pOffData, int *pDataRemain, int *curXb, int *curYb);
|
||||
static void genLoopkupTable();
|
||||
|
||||
void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, unsigned char *pData, int dataRemain)
|
||||
{
|
||||
ubyte *pOrig, *pOffData, op;
|
||||
ushort offset;
|
||||
int length = 0, i, j, xb, yb;
|
||||
|
||||
if (!lookup_initialized) {
|
||||
genLoopkupTable();
|
||||
}
|
||||
|
||||
backBuf1 = (ushort *)g_vBackBuf1;
|
||||
backBuf2 = (ushort *)g_vBackBuf2;
|
||||
|
||||
xb = g_width >> 3;
|
||||
yb = g_height >> 3;
|
||||
|
||||
offset = pData[0]|(pData[1]<<8);
|
||||
|
||||
pOffData = pData + offset;
|
||||
|
||||
pData += 2;
|
||||
|
||||
pOrig = pData;
|
||||
length = offset - 2; /*dataRemain-2;*/
|
||||
|
||||
for (j=0; j<yb; j++) {
|
||||
for (i=0; i<xb/2; i++) {
|
||||
op = (*pMap) & 0xf;
|
||||
dispatchDecoder16((ushort **)&pFrame, op, &pData, &pOffData, &dataRemain, &i, &j);
|
||||
|
||||
op = ((*pMap) >> 4) & 0xf;
|
||||
dispatchDecoder16((ushort **)&pFrame, op, &pData, &pOffData, &dataRemain, &i, &j);
|
||||
|
||||
pMap++;
|
||||
mapRemain--;
|
||||
}
|
||||
|
||||
pFrame += 7*g_width*2;
|
||||
}
|
||||
|
||||
if ((length-(pData-pOrig)) != 0) {
|
||||
nprintf(("MVE", "DEBUG: junk left over: " PTRDIFF_T_ARG ",%d," PTRDIFF_T_ARG "\n",
|
||||
(pData-pOrig), length, (length-(pData-pOrig))));
|
||||
}
|
||||
}
|
||||
|
||||
static ushort GETPIXEL(unsigned char **buf, int off)
|
||||
{
|
||||
ushort val = (*buf)[0+off] | ((*buf)[1+off] << 8);
|
||||
return val;
|
||||
}
|
||||
|
||||
static ushort GETPIXELI(unsigned char **buf, int off)
|
||||
{
|
||||
ushort val = (*buf)[0+off] | ((*buf)[1+off] << 8);
|
||||
(*buf) += 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
static void relClose(int i, int *x, int *y)
|
||||
{
|
||||
int ma, mi;
|
||||
|
||||
ma = i >> 4;
|
||||
mi = i & 0xf;
|
||||
|
||||
*x = mi - 8;
|
||||
*y = ma - 8;
|
||||
}
|
||||
|
||||
static void relFar(int i, int sign, int *x, int *y)
|
||||
{
|
||||
if (i < 56) {
|
||||
*x = sign * (8 + (i % 7));
|
||||
*y = sign * (i / 7);
|
||||
} else {
|
||||
*x = sign * (-14 + (i - 56) % 29);
|
||||
*y = sign * (8 + (i - 56) / 29);
|
||||
}
|
||||
}
|
||||
|
||||
static int close_table[512];
|
||||
static int far_p_table[512];
|
||||
static int far_n_table[512];
|
||||
|
||||
static void genLoopkupTable()
|
||||
{
|
||||
int i;
|
||||
int x, y;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
relClose(i, &x, &y);
|
||||
|
||||
close_table[i*2+0] = x;
|
||||
close_table[i*2+1] = y;
|
||||
|
||||
relFar(i, 1, &x, &y);
|
||||
|
||||
far_p_table[i*2+0] = x;
|
||||
far_p_table[i*2+1] = y;
|
||||
|
||||
relFar(i, -1, &x, &y);
|
||||
|
||||
far_n_table[i*2+0] = x;
|
||||
far_n_table[i*2+1] = y;
|
||||
}
|
||||
|
||||
lookup_initialized = 1;
|
||||
}
|
||||
|
||||
static void copyFrame(ushort *pDest, ushort *pSrc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<8; i++) {
|
||||
memcpy(pDest, pSrc, 16);
|
||||
pDest += g_width;
|
||||
pSrc += g_width;
|
||||
}
|
||||
}
|
||||
|
||||
static void patternRow4Pixels(ushort *pFrame, unsigned char pat0, unsigned char pat1, ushort *p)
|
||||
{
|
||||
ushort mask=0x0003;
|
||||
ushort shift=0;
|
||||
ushort pattern = (pat1 << 8) | pat0;
|
||||
|
||||
while (mask != 0) {
|
||||
*pFrame++ = p[(mask & pattern) >> shift];
|
||||
mask <<= 2;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void patternRow4Pixels2(ushort *pFrame, unsigned char pat0, ushort *p)
|
||||
{
|
||||
unsigned char mask=0x03;
|
||||
unsigned char shift=0;
|
||||
ushort pel;
|
||||
|
||||
while (mask != 0) {
|
||||
pel = p[(mask & pat0) >> shift];
|
||||
pFrame[0] = pel;
|
||||
pFrame[1] = pel;
|
||||
pFrame[g_width + 0] = pel;
|
||||
pFrame[g_width + 1] = pel;
|
||||
pFrame += 2;
|
||||
mask <<= 2;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void patternRow4Pixels2x1(ushort *pFrame, unsigned char pat, ushort *p)
|
||||
{
|
||||
unsigned char mask=0x03;
|
||||
unsigned char shift=0;
|
||||
ushort pel;
|
||||
|
||||
while (mask != 0) {
|
||||
pel = p[(mask & pat) >> shift];
|
||||
pFrame[0] = pel;
|
||||
pFrame[1] = pel;
|
||||
pFrame += 2;
|
||||
mask <<= 2;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void patternQuadrant4Pixels(ushort *pFrame, unsigned char pat0, unsigned char pat1, unsigned char pat2, unsigned char pat3, ushort *p)
|
||||
{
|
||||
unsigned long mask = 0x00000003UL;
|
||||
int shift=0;
|
||||
int i;
|
||||
unsigned long pat = (pat3 << 24) | (pat2 << 16) | (pat1 << 8) | pat0;
|
||||
|
||||
for (i=0; i<16; i++) {
|
||||
pFrame[i&3] = p[(pat & mask) >> shift];
|
||||
if ((i&3) == 3) {
|
||||
pFrame += g_width;
|
||||
}
|
||||
mask <<= 2;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void patternRow2Pixels(ushort *pFrame, unsigned char pat, ushort *p)
|
||||
{
|
||||
unsigned char mask=0x01;
|
||||
|
||||
while (mask != 0) {
|
||||
*pFrame++ = p[(mask & pat) ? 1 : 0];
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void patternRow2Pixels2(ushort *pFrame, unsigned char pat, ushort *p)
|
||||
{
|
||||
ushort pel;
|
||||
unsigned char mask=0x1;
|
||||
|
||||
while (mask != 0x10) {
|
||||
pel = p[(mask & pat) ? 1 : 0];
|
||||
|
||||
pFrame[0] = pel;
|
||||
pFrame[1] = pel;
|
||||
pFrame[g_width + 0] = pel;
|
||||
pFrame[g_width + 1] = pel;
|
||||
pFrame += 2;
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void patternQuadrant2Pixels(ushort *pFrame, unsigned char pat0, unsigned char pat1, ushort *p)
|
||||
{
|
||||
ushort mask = 0x0001;
|
||||
int i;
|
||||
ushort pat = (pat1 << 8) | pat0;
|
||||
|
||||
for (i=0; i<16; i++) {
|
||||
pFrame[i&3] = p[(pat & mask) ? 1 : 0];
|
||||
if ((i&3) == 3) {
|
||||
pFrame += g_width;
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void dispatchDecoder16(ushort **pFrame, unsigned char codeType, unsigned char **pData, unsigned char **pOffData, int *pDataRemain, int *curXb, int *curYb)
|
||||
{
|
||||
ushort p[4];
|
||||
unsigned char pat[16];
|
||||
int i, j, k;
|
||||
int x, y;
|
||||
ushort *pDstBak;
|
||||
|
||||
pDstBak = *pFrame;
|
||||
|
||||
switch(codeType) {
|
||||
case 0x0:
|
||||
copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1));
|
||||
break; // ADDED
|
||||
|
||||
case 0x1:
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
k = *(*pOffData)++;
|
||||
x = far_p_table[k*2+0];
|
||||
y = far_p_table[k*2+1];
|
||||
copyFrame(*pFrame, *pFrame + x + y*g_width);
|
||||
(*pDataRemain)--;
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
k = *(*pOffData)++;
|
||||
x = far_n_table[k*2+0];
|
||||
y = far_n_table[k*2+1];
|
||||
copyFrame(*pFrame, *pFrame + x + y*g_width);
|
||||
(*pDataRemain)--;
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
k = *(*pOffData)++;
|
||||
x = close_table[k*2+0];
|
||||
y = close_table[k*2+1];
|
||||
copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1) + x + y*g_width);
|
||||
(*pDataRemain)--;
|
||||
break;
|
||||
|
||||
case 0x5:
|
||||
x = (char)*(*pData)++;
|
||||
y = (char)*(*pData)++;
|
||||
copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1) + x + y*g_width);
|
||||
*pDataRemain -= 2;
|
||||
break;
|
||||
|
||||
case 0x6:
|
||||
nprintf(("MVE", "STUB: encoding 6 not tested\n"));
|
||||
for (i=0; i<2; i++) {
|
||||
*pFrame += 16;
|
||||
if (++*curXb == (g_width >> 3)) {
|
||||
*pFrame += 7*g_width;
|
||||
*curXb = 0;
|
||||
if (++*curYb == (g_height >> 3)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7:
|
||||
p[0] = GETPIXELI(pData, 0);
|
||||
p[1] = GETPIXELI(pData, 0);
|
||||
if (!((p[0]/*|p[1]*/)&0x8000)) {
|
||||
for (i=0; i<8; i++) {
|
||||
patternRow2Pixels(*pFrame, *(*pData), p);
|
||||
(*pData)++;
|
||||
*pFrame += g_width;
|
||||
}
|
||||
} else {
|
||||
for (i=0; i<2; i++) {
|
||||
patternRow2Pixels2(*pFrame, *(*pData) & 0xf, p);
|
||||
*pFrame += 2*g_width;
|
||||
patternRow2Pixels2(*pFrame, *(*pData) >> 4, p);
|
||||
(*pData)++;
|
||||
*pFrame += 2*g_width;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
p[0] = GETPIXEL(pData, 0);
|
||||
if (!(p[0] & 0x8000)) {
|
||||
for (i=0; i<4; i++) {
|
||||
p[0] = GETPIXELI(pData, 0);
|
||||
p[1] = GETPIXELI(pData, 0);
|
||||
pat[0] = (*pData)[0];
|
||||
pat[1] = (*pData)[1];
|
||||
(*pData) += 2;
|
||||
patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
|
||||
if (i & 1) {
|
||||
*pFrame -= (4*g_width - 4);
|
||||
} else {
|
||||
*pFrame += 4*g_width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p[2] = GETPIXEL(pData, 8);
|
||||
if (!(p[2]&0x8000)) {
|
||||
for (i=0; i<4; i++) {
|
||||
if ((i & 1) == 0) {
|
||||
p[0] = GETPIXELI(pData, 0);
|
||||
p[1] = GETPIXELI(pData, 0);
|
||||
}
|
||||
pat[0] = *(*pData)++;
|
||||
pat[1] = *(*pData)++;
|
||||
patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
|
||||
if (i & 1) {
|
||||
*pFrame -= (4*g_width - 4);
|
||||
} else {
|
||||
*pFrame += 4*g_width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i=0; i<8; i++) {
|
||||
if ((i & 3) == 0) {
|
||||
p[0] = GETPIXELI(pData, 0);
|
||||
p[1] = GETPIXELI(pData, 0);
|
||||
}
|
||||
patternRow2Pixels(*pFrame, *(*pData), p);
|
||||
(*pData)++;
|
||||
*pFrame += g_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9:
|
||||
p[0] = GETPIXELI(pData, 0);
|
||||
p[1] = GETPIXELI(pData, 0);
|
||||
p[2] = GETPIXELI(pData, 0);
|
||||
p[3] = GETPIXELI(pData, 0);
|
||||
*pDataRemain -= 8;
|
||||
if (!(p[0] & 0x8000)) {
|
||||
if (!(p[2] & 0x8000)) {
|
||||
for (i=0; i<8; i++) {
|
||||
pat[0] = (*pData)[0];
|
||||
pat[1] = (*pData)[1];
|
||||
(*pData) += 2;
|
||||
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
*pDataRemain -= 16;
|
||||
} else {
|
||||
patternRow4Pixels2(*pFrame, (*pData)[0], p);
|
||||
*pFrame += 2*g_width;
|
||||
patternRow4Pixels2(*pFrame, (*pData)[1], p);
|
||||
*pFrame += 2*g_width;
|
||||
patternRow4Pixels2(*pFrame, (*pData)[2], p);
|
||||
*pFrame += 2*g_width;
|
||||
patternRow4Pixels2(*pFrame, (*pData)[3], p);
|
||||
(*pData) += 4;
|
||||
*pDataRemain -= 4;
|
||||
}
|
||||
} else {
|
||||
if (!(p[2] & 0x8000)) {
|
||||
for (i=0; i<8; i++) {
|
||||
pat[0] = (*pData)[0];
|
||||
(*pData) += 1;
|
||||
patternRow4Pixels2x1(*pFrame, pat[0], p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
*pDataRemain -= 8;
|
||||
} else {
|
||||
for (i=0; i<4; i++) {
|
||||
pat[0] = (*pData)[0];
|
||||
pat[1] = (*pData)[1];
|
||||
(*pData) += 2;
|
||||
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
|
||||
*pFrame += g_width;
|
||||
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
*pDataRemain -= 8;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
p[0] = GETPIXEL(pData, 0);
|
||||
if (!(p[0] & 0x8000)) {
|
||||
for (i=0; i<4; i++) {
|
||||
p[0] = GETPIXELI(pData, 0);
|
||||
p[1] = GETPIXELI(pData, 0);
|
||||
p[2] = GETPIXELI(pData, 0);
|
||||
p[3] = GETPIXELI(pData, 0);
|
||||
pat[0] = (*pData)[0];
|
||||
pat[1] = (*pData)[1];
|
||||
pat[2] = (*pData)[2];
|
||||
pat[3] = (*pData)[3];
|
||||
(*pData) += 4;
|
||||
patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
|
||||
if (i & 1) {
|
||||
*pFrame -= (4*g_width - 4);
|
||||
} else {
|
||||
*pFrame += 4*g_width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p[0] = GETPIXEL(pData, 16);
|
||||
if (!(p[0] & 0x8000)) {
|
||||
for (i=0; i<4; i++) {
|
||||
if ((i&1) == 0) {
|
||||
p[0] = GETPIXELI(pData, 0);
|
||||
p[1] = GETPIXELI(pData, 0);
|
||||
p[2] = GETPIXELI(pData, 0);
|
||||
p[3] = GETPIXELI(pData, 0);
|
||||
}
|
||||
pat[0] = (*pData)[0];
|
||||
pat[1] = (*pData)[1];
|
||||
pat[2] = (*pData)[2];
|
||||
pat[3] = (*pData)[3];
|
||||
(*pData) += 4;
|
||||
patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
|
||||
if (i & 1) {
|
||||
*pFrame -= (4*g_width - 4);
|
||||
} else {
|
||||
*pFrame += 4*g_width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i=0; i<8; i++) {
|
||||
if ((i&3) == 0) {
|
||||
p[0] = GETPIXELI(pData, 0);
|
||||
p[1] = GETPIXELI(pData, 0);
|
||||
p[2] = GETPIXELI(pData, 0);
|
||||
p[3] = GETPIXELI(pData, 0);
|
||||
}
|
||||
pat[0] = (*pData)[0];
|
||||
pat[1] = (*pData)[1];
|
||||
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
|
||||
*pFrame += g_width;
|
||||
(*pData) += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xb:
|
||||
for (i=0; i<8; i++) {
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
ubyte frame_tmp[16];
|
||||
memcpy(&frame_tmp, *pData, 16);
|
||||
ushort *swap_tmp;
|
||||
for (j = 0; j < 16; j += 2) {
|
||||
swap_tmp = (ushort*)(frame_tmp + j);
|
||||
*swap_tmp = INTEL_SHORT(*swap_tmp);
|
||||
}
|
||||
memcpy(*pFrame, &frame_tmp, 16);
|
||||
#else
|
||||
memcpy(*pFrame, *pData, 16);
|
||||
#endif
|
||||
*pFrame += g_width;
|
||||
*pData += 16;
|
||||
*pDataRemain -= 16;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xc:
|
||||
for (i=0; i<4; i++) {
|
||||
p[0] = GETPIXEL(pData, 0);
|
||||
p[1] = GETPIXEL(pData, 2);
|
||||
p[2] = GETPIXEL(pData, 4);
|
||||
p[3] = GETPIXEL(pData, 6);
|
||||
for (j=0; j<2; j++) {
|
||||
for (k=0; k<4; k++) {
|
||||
(*pFrame)[2*k] = p[k];
|
||||
(*pFrame)[2*k+1] = p[k];
|
||||
}
|
||||
*pFrame += g_width;
|
||||
}
|
||||
*pData += 8;
|
||||
*pDataRemain -= 8;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xd:
|
||||
for (i=0; i<2; i++) {
|
||||
p[0] = GETPIXEL(pData, 0);
|
||||
p[1] = GETPIXEL(pData, 2);
|
||||
for (j=0; j<4; j++) {
|
||||
for (k=0; k<4; k++) {
|
||||
(*pFrame)[k*g_width+j] = p[0];
|
||||
(*pFrame)[k*g_width+j+4] = p[1];
|
||||
}
|
||||
}
|
||||
*pFrame += 4*g_width;
|
||||
*pData += 4;
|
||||
*pDataRemain -= 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
p[0] = GETPIXEL(pData, 0);
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
(*pFrame)[j] = p[0];
|
||||
}
|
||||
*pFrame += g_width;
|
||||
}
|
||||
*pData += 2;
|
||||
*pDataRemain -= 2;
|
||||
break;
|
||||
|
||||
case 0xf:
|
||||
p[0] = GETPIXEL(pData, 0);
|
||||
p[1] = GETPIXEL(pData, 1);
|
||||
for (i=0; i<8; i++) {
|
||||
for (j=0; j<8; j++) {
|
||||
(*pFrame)[j] = p[(i+j)&1];
|
||||
}
|
||||
*pFrame += g_width;
|
||||
}
|
||||
*pData += 4;
|
||||
*pDataRemain -= 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*pFrame = pDstBak+8;
|
||||
}
|
||||
@@ -1,533 +0,0 @@
|
||||
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
|
||||
extern int g_width, g_height;
|
||||
extern void *g_vBackBuf1, *g_vBackBuf2;
|
||||
|
||||
static void dispatchDecoder8(ubyte **pFrame, ubyte codeType, ubyte **pData, int *pDataRemain, int *curXb, int *curYb);
|
||||
|
||||
void decodeFrame8(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain)
|
||||
{
|
||||
int i, j;
|
||||
int xb, yb;
|
||||
|
||||
xb = g_width >> 3;
|
||||
yb = g_height >> 3;
|
||||
|
||||
for (j = 0; j < yb; j++) {
|
||||
for (i = 0; i < (xb / 2); i++) {
|
||||
dispatchDecoder8(&pFrame, (*pMap) & 0xf, &pData, &dataRemain, &i, &j);
|
||||
|
||||
dispatchDecoder8(&pFrame, (*pMap) >> 4, &pData, &dataRemain, &i, &j);
|
||||
|
||||
pMap++;
|
||||
mapRemain--;
|
||||
}
|
||||
|
||||
pFrame += 7*g_width;
|
||||
}
|
||||
}
|
||||
|
||||
static void relClose(int i, int *x, int *y)
|
||||
{
|
||||
int ma, mi;
|
||||
|
||||
ma = i >> 4;
|
||||
mi = i & 0xf;
|
||||
|
||||
*x = mi - 8;
|
||||
*y = ma - 8;
|
||||
}
|
||||
|
||||
static void relFar(int i, int sign, int *x, int *y)
|
||||
{
|
||||
if (i < 56) {
|
||||
*x = sign * (8 + (i % 7));
|
||||
*y = sign * (i / 7);
|
||||
} else {
|
||||
*x = sign * (-14 + (i - 56) % 29);
|
||||
*y = sign * (8 + (i - 56) / 29);
|
||||
}
|
||||
}
|
||||
|
||||
/* copies an 8x8 block from pSrc to pDest.
|
||||
pDest and pSrc are both g_width bytes wide */
|
||||
static void copyFrame(ubyte *pDest, ubyte *pSrc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
memcpy(pDest, pSrc, 8);
|
||||
pDest += g_width;
|
||||
pSrc += g_width;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the next eight bytes with p[0], p[1], p[2], or p[3],
|
||||
// depending on the corresponding two-bit value in pat0 and pat1
|
||||
static void patternRow4Pixels(ubyte *pFrame, ubyte pat0, ubyte pat1, ubyte *p)
|
||||
{
|
||||
ushort mask = 0x0003;
|
||||
ushort shift = 0;
|
||||
ushort pattern = (pat1 << 8) | pat0;
|
||||
|
||||
while (mask != 0) {
|
||||
*pFrame++ = p[(mask & pattern) >> shift];
|
||||
mask <<= 2;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the next four 2x2 pixel blocks with p[0], p[1], p[2], or p[3],
|
||||
// depending on the corresponding two-bit value in pat0.
|
||||
static void patternRow4Pixels2(ubyte *pFrame, ubyte pat0, ubyte *p)
|
||||
{
|
||||
ubyte mask = 0x03;
|
||||
ubyte shift = 0;
|
||||
ubyte pel;
|
||||
|
||||
while (mask != 0) {
|
||||
pel = p[(mask & pat0) >> shift];
|
||||
pFrame[0] = pel;
|
||||
pFrame[1] = pel;
|
||||
pFrame[g_width + 0] = pel;
|
||||
pFrame[g_width + 1] = pel;
|
||||
pFrame += 2;
|
||||
mask <<= 2;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the next four 2x1 pixel blocks with p[0], p[1], p[2], or p[3],
|
||||
// depending on the corresponding two-bit value in pat.
|
||||
static void patternRow4Pixels2x1(ubyte *pFrame, ubyte pat, ubyte *p)
|
||||
{
|
||||
ubyte mask = 0x03;
|
||||
ubyte shift = 0;
|
||||
ubyte pel;
|
||||
|
||||
while (mask != 0) {
|
||||
pel = p[(mask & pat) >> shift];
|
||||
pFrame[0] = pel;
|
||||
pFrame[1] = pel;
|
||||
pFrame += 2;
|
||||
mask <<= 2;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the next 4x4 pixel block with p[0], p[1], p[2], or p[3],
|
||||
// depending on the corresponding two-bit value in pat0, pat1, pat2, and pat3.
|
||||
static void patternQuadrant4Pixels(ubyte *pFrame, ubyte pat0, ubyte pat1, ubyte pat2, ubyte pat3, ubyte *p)
|
||||
{
|
||||
unsigned long mask = 0x00000003UL;
|
||||
int shift=0;
|
||||
int i;
|
||||
unsigned long pat = (pat3 << 24) | (pat2 << 16) | (pat1 << 8) | pat0;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
pFrame[i&3] = p[(pat & mask) >> shift];
|
||||
|
||||
if ( (i & 3) == 3 )
|
||||
pFrame += g_width;
|
||||
|
||||
mask <<= 2;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// fills the next 8 pixels with either p[0] or p[1], depending on pattern
|
||||
static void patternRow2Pixels(ubyte *pFrame, ubyte pat, ubyte *p)
|
||||
{
|
||||
ubyte mask=0x01;
|
||||
|
||||
while (mask != 0) {
|
||||
*pFrame++ = p[(mask & pat) ? 1 : 0];
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// fills the next four 2 x 2 pixel boxes with either p[0] or p[1], depending on pattern
|
||||
static void patternRow2Pixels2(ubyte *pFrame, ubyte pat, ubyte *p)
|
||||
{
|
||||
ubyte pel;
|
||||
ubyte mask=0x1;
|
||||
|
||||
while (mask != 0x10) {
|
||||
pel = p[(mask & pat) ? 1 : 0];
|
||||
|
||||
pFrame[0] = pel; // upper-left
|
||||
pFrame[1] = pel; // upper-right
|
||||
pFrame[g_width + 0] = pel; // lower-left
|
||||
pFrame[g_width + 1] = pel; // lower-right
|
||||
pFrame += 2;
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// fills pixels in the next 4 x 4 pixel boxes with either p[0] or p[1], depending on pat0 and pat1.
|
||||
static void patternQuadrant2Pixels(ubyte *pFrame, ubyte pat0, ubyte pat1, ubyte *p)
|
||||
{
|
||||
ubyte pel;
|
||||
ushort mask = 0x0001;
|
||||
int i, j;
|
||||
ushort pat = (pat1 << 8) | pat0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
pel = p[(pat & mask) ? 1 : 0];
|
||||
|
||||
pFrame[j + i * g_width] = pel;
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dispatchDecoder8(ubyte **pFrame, ubyte codeType, ubyte **pData, int *pDataRemain, int *curXb, int *curYb)
|
||||
{
|
||||
ubyte p[4];
|
||||
ubyte pat[16];
|
||||
int i, j, k;
|
||||
int x, y;
|
||||
|
||||
switch(codeType) {
|
||||
case 0x0:
|
||||
copyFrame(*pFrame, *pFrame + ((ubyte *)g_vBackBuf2 - (ubyte *)g_vBackBuf1));
|
||||
*pFrame += 8;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
*pFrame += 8;
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
relFar(*(*pData)++, 1, &x, &y);
|
||||
copyFrame(*pFrame, *pFrame + x + y*g_width);
|
||||
*pFrame += 8;
|
||||
(*pDataRemain)--;
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
relFar(*(*pData)++, -1, &x, &y);
|
||||
copyFrame(*pFrame, *pFrame + x + y*g_width);
|
||||
*pFrame += 8;
|
||||
(*pDataRemain)--;
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
relClose(*(*pData)++, &x, &y);
|
||||
copyFrame(*pFrame, *pFrame + ((ubyte *)g_vBackBuf2 - (ubyte *)g_vBackBuf1) + x + y*g_width);
|
||||
*pFrame += 8;
|
||||
(*pDataRemain)--;
|
||||
break;
|
||||
|
||||
case 0x5:
|
||||
x = (signed char)*(*pData)++;
|
||||
y = (signed char)*(*pData)++;
|
||||
copyFrame(*pFrame, *pFrame + ((ubyte *)g_vBackBuf2 - (ubyte *)g_vBackBuf1) + x + y*g_width);
|
||||
*pFrame += 8;
|
||||
*pDataRemain -= 2;
|
||||
break;
|
||||
|
||||
case 0x6:
|
||||
for (i = 0; i < 2; i++) {
|
||||
*pFrame += 16;
|
||||
|
||||
if (++*curXb == (g_width >> 3)) {
|
||||
*pFrame += 7*g_width;
|
||||
*curXb = 0;
|
||||
|
||||
if (++*curYb == (g_height >> 3))
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7:
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
|
||||
if (p[0] <= p[1]) {
|
||||
for (i = 0; i < 8; i++) {
|
||||
patternRow2Pixels(*pFrame, *(*pData)++, p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 2; i++) {
|
||||
patternRow2Pixels2(*pFrame, *(*pData) & 0xf, p);
|
||||
*pFrame += 2*g_width;
|
||||
|
||||
patternRow2Pixels2(*pFrame, *(*pData)++ >> 4, p);
|
||||
*pFrame += 2*g_width;
|
||||
}
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
if ( (*pData)[0] <= (*pData)[1] ) {
|
||||
// four quadrant case
|
||||
for (i = 0; i < 4; i++) {
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
pat[0] = *(*pData)++;
|
||||
pat[1] = *(*pData)++;
|
||||
patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
|
||||
|
||||
// alternate between moving down and moving up and right
|
||||
if (i & 1)
|
||||
*pFrame += 4 - 4*g_width; // up and right
|
||||
else
|
||||
*pFrame += 4*g_width; // down
|
||||
}
|
||||
} else if ( (*pData)[6] <= (*pData)[7] ) {
|
||||
// split horizontal
|
||||
for (i = 0; i < 4; i++) {
|
||||
if ((i & 1) == 0) {
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
}
|
||||
|
||||
pat[0] = *(*pData)++;
|
||||
pat[1] = *(*pData)++;
|
||||
patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
|
||||
|
||||
if (i & 1)
|
||||
*pFrame -= (4*g_width - 4);
|
||||
else
|
||||
*pFrame += 4*g_width;
|
||||
}
|
||||
} else {
|
||||
// split vertical
|
||||
for (i = 0; i < 8; i++) {
|
||||
if ((i & 3) == 0) {
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
}
|
||||
|
||||
patternRow2Pixels(*pFrame, *(*pData)++, p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9:
|
||||
if ( (*pData)[0] <= (*pData)[1] ) {
|
||||
if ( (*pData)[2] <= (*pData)[3] ) {
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
p[2] = *(*pData)++;
|
||||
p[3] = *(*pData)++;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
pat[0] = *(*pData)++;
|
||||
pat[1] = *(*pData)++;
|
||||
|
||||
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
} else {
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
p[2] = *(*pData)++;
|
||||
p[3] = *(*pData)++;
|
||||
|
||||
patternRow4Pixels2(*pFrame, *(*pData)++, p);
|
||||
*pFrame += 2*g_width;
|
||||
|
||||
patternRow4Pixels2(*pFrame, *(*pData)++, p);
|
||||
*pFrame += 2*g_width;
|
||||
|
||||
patternRow4Pixels2(*pFrame, *(*pData)++, p);
|
||||
*pFrame += 2*g_width;
|
||||
|
||||
patternRow4Pixels2(*pFrame, *(*pData)++, p);
|
||||
*pFrame -= (6*g_width - 8);
|
||||
}
|
||||
} else {
|
||||
if ( (*pData)[2] <= (*pData)[3] ) {
|
||||
// draw 2x1 strips
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
p[2] = *(*pData)++;
|
||||
p[3] = *(*pData)++;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
pat[0] = *(*pData)++;
|
||||
|
||||
patternRow4Pixels2x1(*pFrame, pat[0], p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
} else {
|
||||
// draw 1x2 strips
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
p[2] = *(*pData)++;
|
||||
p[3] = *(*pData)++;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
pat[0] = *(*pData)++;
|
||||
pat[1] = *(*pData)++;
|
||||
|
||||
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
|
||||
*pFrame += g_width;
|
||||
|
||||
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
if ( (*pData)[0] <= (*pData)[1] ) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
p[2] = *(*pData)++;
|
||||
p[3] = *(*pData)++;
|
||||
pat[0] = *(*pData)++;
|
||||
pat[1] = *(*pData)++;
|
||||
pat[2] = *(*pData)++;
|
||||
pat[3] = *(*pData)++;
|
||||
|
||||
patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
|
||||
|
||||
if (i & 1)
|
||||
*pFrame -= (4*g_width - 4);
|
||||
else
|
||||
*pFrame += 4*g_width;
|
||||
}
|
||||
} else {
|
||||
if ( (*pData)[12] <= (*pData)[13] ) {
|
||||
// split vertical
|
||||
for (i = 0; i < 4; i++) {
|
||||
if ((i&1) == 0) {
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
p[2] = *(*pData)++;
|
||||
p[3] = *(*pData)++;
|
||||
}
|
||||
|
||||
pat[0] = *(*pData)++;
|
||||
pat[1] = *(*pData)++;
|
||||
pat[2] = *(*pData)++;
|
||||
pat[3] = *(*pData)++;
|
||||
|
||||
patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
|
||||
|
||||
if (i & 1)
|
||||
*pFrame -= (4*g_width - 4);
|
||||
else
|
||||
*pFrame += 4*g_width;
|
||||
}
|
||||
} else {
|
||||
// split horizontal
|
||||
for (i = 0; i < 8; i++) {
|
||||
if ((i&3) == 0) {
|
||||
p[0] = *(*pData)++;
|
||||
p[1] = *(*pData)++;
|
||||
p[2] = *(*pData)++;
|
||||
p[3] = *(*pData)++;
|
||||
}
|
||||
|
||||
pat[0] = *(*pData)++;
|
||||
pat[1] = *(*pData)++;
|
||||
|
||||
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xb:
|
||||
for (i = 0; i < 8; i++) {
|
||||
memcpy(*pFrame, *pData, 8);
|
||||
*pFrame += g_width;
|
||||
*pData += 8;
|
||||
*pDataRemain -= 8;
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
break;
|
||||
|
||||
case 0xc:
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
for (k = 0; k < 4; k++) {
|
||||
(*pFrame)[2*k] = (*pData)[k];
|
||||
(*pFrame)[2*k+1] = (*pData)[k];
|
||||
}
|
||||
|
||||
*pFrame += g_width;
|
||||
}
|
||||
|
||||
*pData += 4;
|
||||
*pDataRemain -= 4;
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
break;
|
||||
|
||||
case 0xd:
|
||||
for (i = 0; i < 2; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
for (k = 0; k < 4; k++) {
|
||||
(*pFrame)[k*g_width+j] = (*pData)[0];
|
||||
(*pFrame)[k*g_width+j+4] = (*pData)[1];
|
||||
}
|
||||
}
|
||||
|
||||
*pFrame += 4*g_width;
|
||||
*pData += 2;
|
||||
*pDataRemain -= 2;
|
||||
}
|
||||
|
||||
*pFrame -= (8*g_width - 8);
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
for (i = 0; i < 8; i++) {
|
||||
memset(*pFrame, **pData, 8);
|
||||
*pFrame += g_width;
|
||||
}
|
||||
|
||||
(*pData)++;
|
||||
(*pDataRemain)--;
|
||||
*pFrame -= (8*g_width - 8);
|
||||
break;
|
||||
|
||||
case 0xf:
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
(*pFrame)[j] = (*pData)[(i+j)&1];
|
||||
}
|
||||
|
||||
*pFrame += g_width;
|
||||
}
|
||||
|
||||
*pData += 2;
|
||||
*pDataRemain -= 2;
|
||||
*pFrame -= (8*g_width - 8);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
#include "cutscene/ffmpeg/AudioDecoder.h"
|
||||
|
||||
#include "tracing/tracing.h"
|
||||
|
||||
namespace {
|
||||
const int OUT_CH_LAYOUT = AV_CH_LAYOUT_STEREO;
|
||||
const int OUT_SAMPLE_RATE = 48000;
|
||||
const AVSampleFormat OUT_SAMPLE_FORMAT = AV_SAMPLE_FMT_S16;
|
||||
const int OUT_NUM_CHANNELS = av_get_channel_layout_nb_channels(OUT_CH_LAYOUT);
|
||||
|
||||
const int DEFAULT_SRC_NUM_SAMPLES = 1024;
|
||||
|
||||
SwrContext* getSWRContext(uint64_t layout, int rate, AVSampleFormat inFmt) {
|
||||
auto swr = swr_alloc();
|
||||
|
||||
av_opt_set_int(swr, "in_channel_layout", layout, 0);
|
||||
av_opt_set_int(swr, "in_sample_rate", rate, 0);
|
||||
av_opt_set_int(swr, "in_sample_fmt", inFmt, 0);
|
||||
|
||||
av_opt_set_int(swr, "out_channel_layout", OUT_CH_LAYOUT, 0);
|
||||
av_opt_set_int(swr, "out_sample_rate", OUT_SAMPLE_RATE, 0);
|
||||
av_opt_set_int(swr, "out_sample_fmt", OUT_SAMPLE_FORMAT, 0);
|
||||
|
||||
swr_init(swr);
|
||||
|
||||
return swr;
|
||||
}
|
||||
|
||||
int alloc_array_and_samples(uint8_t*** outData, int* linesize, int channels, int samples, AVSampleFormat format) {
|
||||
auto planes = av_sample_fmt_is_planar(format) ? channels : 1;
|
||||
|
||||
*outData = reinterpret_cast<uint8_t**>(av_mallocz(planes * sizeof(**outData)));
|
||||
|
||||
|
||||
auto ret = av_samples_alloc(*outData, linesize, channels, samples, format, 0);
|
||||
|
||||
if (ret < 0) {
|
||||
av_freep(outData);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t getDelay(SwrContext* ctx, int64_t sampleRate) {
|
||||
#ifdef WITH_LIBAV
|
||||
return avresample_get_delay(ctx);
|
||||
#else
|
||||
return swr_get_delay(ctx, sampleRate);
|
||||
#endif
|
||||
}
|
||||
|
||||
int resample_convert(SwrContext* ctx, uint8_t** output,
|
||||
int out_plane_size, int out_samples, uint8_t** input,
|
||||
int in_plane_size, int in_samples) {
|
||||
#ifdef WITH_LIBAV
|
||||
return avresample_convert(ctx, output, out_plane_size, out_samples, input, in_plane_size, in_samples);
|
||||
#else
|
||||
return swr_convert(ctx, output, out_samples, (const uint8_t**) input, in_samples);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
AudioDecoder::AudioDecoder(DecoderStatus* status)
|
||||
: FFMPEGStreamDecoder(status) {
|
||||
m_audioBuffer.reserve(static_cast<size_t>(OUT_SAMPLE_RATE * OUT_NUM_CHANNELS / 2));
|
||||
|
||||
m_resampleCtx = getSWRContext(m_status->audioCodecPars.channel_layout, m_status->audioCodecPars.sample_rate,
|
||||
m_status->audioCodecPars.audio_format);
|
||||
|
||||
/*
|
||||
* compute the number of converted samples: buffering is avoided
|
||||
* ensuring that the output buffer will contain at least all the
|
||||
* converted input samples
|
||||
*/
|
||||
m_maxOutNumSamples = m_outNumSamples = static_cast<int>(av_rescale_rnd(DEFAULT_SRC_NUM_SAMPLES,
|
||||
OUT_SAMPLE_RATE,
|
||||
m_status->audioCodecPars.sample_rate,
|
||||
AV_ROUND_UP));
|
||||
|
||||
auto ret = alloc_array_and_samples(&m_outData, &m_outLinesize, OUT_NUM_CHANNELS,
|
||||
m_outNumSamples, OUT_SAMPLE_FORMAT);
|
||||
|
||||
if (ret < 0) {
|
||||
mprintf(("FFMPEG: Failed to allocate samples array!\n"));
|
||||
}
|
||||
}
|
||||
|
||||
AudioDecoder::~AudioDecoder() {
|
||||
if (m_outData) {
|
||||
av_freep(&m_outData[0]);
|
||||
}
|
||||
av_freep(&m_outData);
|
||||
|
||||
swr_free(&m_resampleCtx);
|
||||
}
|
||||
|
||||
void AudioDecoder::flushAudioBuffer() {
|
||||
if (m_audioBuffer.empty()) {
|
||||
// Nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
AudioFramePtr audioFrame(new AudioFrame());
|
||||
audioFrame->channels = OUT_NUM_CHANNELS;
|
||||
audioFrame->rate = OUT_SAMPLE_RATE;
|
||||
|
||||
audioFrame->audioData.assign(m_audioBuffer.begin(), m_audioBuffer.end());
|
||||
|
||||
pushFrame(std::move(audioFrame));
|
||||
m_audioBuffer.clear();
|
||||
}
|
||||
|
||||
void AudioDecoder::handleDecodedFrame(AVFrame* frame) {
|
||||
/* compute destination number of samples */
|
||||
m_outNumSamples = static_cast<int>(av_rescale_rnd(getDelay(m_resampleCtx, frame->sample_rate)
|
||||
+ frame->nb_samples, OUT_SAMPLE_RATE, frame->sample_rate,
|
||||
AV_ROUND_UP));
|
||||
|
||||
if (m_outNumSamples > m_maxOutNumSamples) {
|
||||
av_freep(&m_outData[0]);
|
||||
auto ret = av_samples_alloc(m_outData, &m_outLinesize, OUT_NUM_CHANNELS, m_outNumSamples,
|
||||
OUT_SAMPLE_FORMAT, 1);
|
||||
if (ret < 0) {
|
||||
mprintf(("FFMPEG: Failed to allocate samples!!!"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_maxOutNumSamples = m_outNumSamples;
|
||||
}
|
||||
|
||||
/* convert to destination format */
|
||||
auto ret = resample_convert(m_resampleCtx, m_outData, 0, m_outNumSamples,
|
||||
(uint8_t**) frame->data, 0, frame->nb_samples);
|
||||
if (ret < 0) {
|
||||
mprintf(("FFMPEG: Error while converting audio!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto outBufsize = av_samples_get_buffer_size(&m_outLinesize, OUT_NUM_CHANNELS, ret, OUT_SAMPLE_FORMAT, 1);
|
||||
if (outBufsize < 0) {
|
||||
mprintf(("FFMPEG: Could not get sample buffer size!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto begin = reinterpret_cast<short*>(m_outData[0]);
|
||||
auto end = reinterpret_cast<short*>(m_outData[0] + outBufsize);
|
||||
|
||||
auto size = std::distance(begin, end);
|
||||
auto newSize = m_audioBuffer.size() + size;
|
||||
|
||||
if (newSize <= m_audioBuffer.capacity()) {
|
||||
// We haven't filled the buffer yet
|
||||
m_audioBuffer.insert(m_audioBuffer.end(), begin, end);
|
||||
} else {
|
||||
flushAudioBuffer();
|
||||
m_audioBuffer.assign(begin, end);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDecoder::decodePacket(AVPacket* packet) {
|
||||
TRACE_SCOPE(tracing::CutsceneFFmpegAudioDecoder);
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
int send_result;
|
||||
do {
|
||||
send_result = avcodec_send_packet(m_status->audioCodecCtx, packet);
|
||||
|
||||
while (avcodec_receive_frame(m_status->audioCodecCtx, m_decodeFrame) == 0) {
|
||||
handleDecodedFrame(m_decodeFrame);
|
||||
}
|
||||
} while (send_result == AVERROR(EAGAIN));
|
||||
#else
|
||||
int finishedFrame = 0;
|
||||
auto err = avcodec_decode_audio4(m_status->audioCodecCtx, m_decodeFrame, &finishedFrame, packet);
|
||||
|
||||
if (err >= 0 && finishedFrame) {
|
||||
handleDecodedFrame(m_decodeFrame);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioDecoder::finishDecoding() {
|
||||
TRACE_SCOPE(tracing::CutsceneFFmpegAudioDecoder);
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
// Send flush packet
|
||||
avcodec_send_packet(m_status->audioCodecCtx, nullptr);
|
||||
|
||||
// Handle those decoders that have a delay
|
||||
while (true) {
|
||||
auto ret = avcodec_receive_frame(m_status->audioCodecCtx, m_decodeFrame);
|
||||
|
||||
if (ret == 0) {
|
||||
handleDecodedFrame(m_decodeFrame);
|
||||
}
|
||||
else {
|
||||
// Everything consumed or error
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Handle those decoders that have a delay
|
||||
AVPacket nullPacket;
|
||||
memset(&nullPacket, 0, sizeof(nullPacket));
|
||||
nullPacket.data = nullptr;
|
||||
nullPacket.size = 0;
|
||||
|
||||
while (true) {
|
||||
int finishedFrame = 1;
|
||||
auto err = avcodec_decode_audio4(m_status->audioCodecCtx, m_decodeFrame, &finishedFrame, &nullPacket);
|
||||
|
||||
if (err < 0 || !finishedFrame) {
|
||||
break;
|
||||
}
|
||||
|
||||
handleDecodedFrame(m_decodeFrame);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Push the last bits of audio data into the queue
|
||||
flushAudioBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "cutscene/ffmpeg/internal.h"
|
||||
#include "cutscene/ffmpeg/FFMPEGDecoder.h"
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
|
||||
class AudioDecoder: public FFMPEGStreamDecoder<AudioFrame> {
|
||||
private:
|
||||
SwrContext* m_resampleCtx;
|
||||
|
||||
uint8_t** m_outData;
|
||||
int m_outLinesize;
|
||||
|
||||
int m_maxOutNumSamples;
|
||||
int m_outNumSamples;
|
||||
|
||||
SCP_vector<short> m_audioBuffer;
|
||||
|
||||
void handleDecodedFrame(AVFrame* frame);
|
||||
|
||||
void flushAudioBuffer();
|
||||
|
||||
public:
|
||||
explicit AudioDecoder(DecoderStatus* status);
|
||||
|
||||
virtual ~AudioDecoder();
|
||||
|
||||
virtual void decodePacket(AVPacket* packet) SCP_OVERRIDE;
|
||||
|
||||
virtual void finishDecoding() SCP_OVERRIDE;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
#include <limits>
|
||||
|
||||
#include "cutscene/ffmpeg/FFMPEGDecoder.h"
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
#include "libs/ffmpeg/FFmpegContext.h"
|
||||
|
||||
#include "cutscene/ffmpeg/internal.h"
|
||||
|
||||
#include "cutscene/ffmpeg/AudioDecoder.h"
|
||||
#include "cutscene/ffmpeg/VideoDecoder.h"
|
||||
|
||||
using namespace libs::ffmpeg;
|
||||
|
||||
namespace {
|
||||
using namespace cutscene;
|
||||
using namespace cutscene::ffmpeg;
|
||||
|
||||
class AVPacketScope
|
||||
{
|
||||
AVPacket* _packet;
|
||||
public:
|
||||
explicit AVPacketScope(AVPacket* av_packet)
|
||||
: _packet(av_packet) {
|
||||
}
|
||||
|
||||
~AVPacketScope() {
|
||||
av_packet_unref(_packet);
|
||||
}
|
||||
};
|
||||
|
||||
const char* CHECKED_EXTENSIONS[] = {
|
||||
"webm",
|
||||
"mp4",
|
||||
"ogg",
|
||||
"mve"
|
||||
};
|
||||
|
||||
double getFrameRate(AVStream* stream, AVCodecContext* codecCtx) {
|
||||
auto fps = av_q2d(av_stream_get_r_frame_rate(stream));
|
||||
|
||||
if (fps < 0.000001)
|
||||
{
|
||||
fps = av_q2d(stream->avg_frame_rate);
|
||||
}
|
||||
|
||||
if (fps < 0.000001)
|
||||
{
|
||||
fps = 1.0 / av_q2d(codecCtx->time_base);
|
||||
}
|
||||
|
||||
return fps;
|
||||
}
|
||||
|
||||
CodecContextParameters getCodecParameters(AVStream* stream) {
|
||||
CodecContextParameters paras;
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
paras.width = stream->codecpar->width;
|
||||
paras.height = stream->codecpar->height;
|
||||
paras.pixel_format = (AVPixelFormat)stream->codecpar->format;
|
||||
|
||||
paras.channel_layout = stream->codecpar->channel_layout;
|
||||
paras.sample_rate = stream->codecpar->sample_rate;
|
||||
paras.audio_format = (AVSampleFormat)stream->codecpar->format;
|
||||
#else
|
||||
paras.width = stream->codec->width;
|
||||
paras.height = stream->codec->height;
|
||||
paras.pixel_format = stream->codec->pix_fmt;
|
||||
|
||||
paras.channel_layout = stream->codec->channel_layout;
|
||||
paras.sample_rate = stream->codec->sample_rate;
|
||||
paras.audio_format = stream->codec->sample_fmt;
|
||||
#endif
|
||||
return paras;
|
||||
}
|
||||
}
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
struct InputStream {
|
||||
std::unique_ptr<FFmpegContext> m_ctx;
|
||||
};
|
||||
|
||||
FFMPEGDecoder::FFMPEGDecoder() {
|
||||
}
|
||||
|
||||
FFMPEGDecoder::~FFMPEGDecoder() {
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::unique_ptr<InputStream> openStream(const SCP_string& name) {
|
||||
// Only check the root and movies folders
|
||||
int dirType;
|
||||
if (cf_exists_full(name.c_str(), CF_TYPE_ROOT)) {
|
||||
dirType = CF_TYPE_ROOT;
|
||||
} else if (cf_exists_full(name.c_str(), CF_TYPE_MOVIES)) {
|
||||
dirType = CF_TYPE_MOVIES;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<InputStream> input(new InputStream());
|
||||
|
||||
try {
|
||||
// Create the FFmpeg context
|
||||
input->m_ctx = FFmpegContext::createContext(name, dirType);
|
||||
|
||||
// This may fail, in that case return null
|
||||
if (!input->m_ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
catch (const FFmpegException& e) {
|
||||
mprintf(("Error opening %s: %s\n", name.c_str(), e.what()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
std::unique_ptr<DecoderStatus> initializeStatus(std::unique_ptr<InputStream>& stream) {
|
||||
std::unique_ptr<DecoderStatus> status(new DecoderStatus());
|
||||
|
||||
auto ctx = stream->m_ctx->ctx();
|
||||
|
||||
auto videoStream = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &status->videoCodec, 0);
|
||||
if (videoStream < 0) {
|
||||
if (videoStream == AVERROR_STREAM_NOT_FOUND) {
|
||||
mprintf(("FFmpeg: No video stream found in file!\n"));
|
||||
} else if (videoStream == AVERROR_DECODER_NOT_FOUND) {
|
||||
mprintf(("FFmpeg: Codec for video stream could not be found!\n"));
|
||||
} else {
|
||||
mprintf(("FFmpeg: Unknown error while finding video stream!\n"));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto audioStream = av_find_best_stream(ctx, AVMEDIA_TYPE_AUDIO, -1, videoStream, &status->audioCodec, 0);
|
||||
if (audioStream < 0) {
|
||||
if (audioStream == AVERROR_STREAM_NOT_FOUND) {
|
||||
mprintf(("FFmpeg: No audio stream found in file!\n"));
|
||||
} else if (videoStream == AVERROR_DECODER_NOT_FOUND) {
|
||||
mprintf(("FFmpeg: Codec for audio stream could not be found!\n"));
|
||||
} else {
|
||||
mprintf(("FFmpeg: Unknown error while finding audio stream!\n"));
|
||||
}
|
||||
}
|
||||
|
||||
status->videoStreamIndex = videoStream;
|
||||
status->videoStream = ctx->streams[videoStream];
|
||||
|
||||
if (audioStream >= 0) {
|
||||
status->audioStreamIndex = audioStream;
|
||||
status->audioStream = ctx->streams[audioStream];
|
||||
}
|
||||
|
||||
status->videoCodecPars = getCodecParameters(status->videoStream);
|
||||
|
||||
int err;
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
status->videoCodecCtx = avcodec_alloc_context3(status->videoCodec);
|
||||
|
||||
// Copy codec parameters from input stream to output codec context
|
||||
err = avcodec_parameters_to_context(status->videoCodecCtx, status->videoStream->codecpar);
|
||||
if (err < 0) {
|
||||
char errorStr[512];
|
||||
av_strerror(err, errorStr, sizeof(errorStr));
|
||||
mprintf(("FFMPEG: Failed to copy context parameters! Error: %s\n", errorStr));
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
status->videoCodecCtx = status->videoStream->codec;
|
||||
#endif
|
||||
|
||||
err = avcodec_open2(status->videoCodecCtx, status->videoCodec, nullptr);
|
||||
if (err < 0) {
|
||||
char errorStr[512];
|
||||
av_strerror(err, errorStr, sizeof(errorStr));
|
||||
mprintf(("FFMPEG: Failed to open video codec! Error: %s\n", errorStr));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mprintf(("FFmpeg: Using video codec %s (%s).\n", status->videoCodec->long_name ? status->videoCodec->long_name : "<Unknown>",
|
||||
status->videoCodec->name ? status->videoCodec->name : "<Unknown>"));
|
||||
|
||||
// Now initialize audio, if this fails it's not a fatal error
|
||||
if (audioStream >= 0) {
|
||||
status->audioCodecPars = getCodecParameters(status->audioStream);
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
status->audioCodecCtx = avcodec_alloc_context3(status->audioCodec);
|
||||
|
||||
err = avcodec_parameters_to_context(status->audioCodecCtx, status->audioStream->codecpar);
|
||||
if (err < 0) {
|
||||
char errorStr[512];
|
||||
av_strerror(err, errorStr, sizeof(errorStr));
|
||||
mprintf(("FFMPEG: Failed to copy context parameters! Error: %s\n", errorStr));
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
status->audioCodecCtx = status->audioStream->codec;
|
||||
#endif
|
||||
|
||||
err = avcodec_open2(status->audioCodecCtx, status->audioCodec, nullptr);
|
||||
if (err < 0) {
|
||||
char errorStr[512];
|
||||
av_strerror(err, errorStr, sizeof(errorStr));
|
||||
mprintf(("FFMPEG: Failed to open audio codec! Error: %s\n", errorStr));
|
||||
}
|
||||
|
||||
mprintf(("FFmpeg: Using audio codec %s (%s).\n", status->audioCodec->long_name ? status->audioCodec->long_name : "<Unknown>",
|
||||
status->audioCodec->name ? status->audioCodec->name : "<Unknown>"));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
std::unique_ptr<InputStream> openInputStream(const SCP_string& name) {
|
||||
// Check a list of extensions we might use
|
||||
// The actual format of the file may be whatever FFmpeg supports
|
||||
for (auto ext : CHECKED_EXTENSIONS) {
|
||||
auto fileName = name + "." + ext;
|
||||
|
||||
auto input = openStream(fileName);
|
||||
|
||||
if (input) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool FFMPEGDecoder::initialize(const SCP_string& fileName) {
|
||||
SCP_string movieName = fileName;
|
||||
// First make the file name lower case
|
||||
std::transform(movieName.begin(), movieName.end(), movieName.begin(), ::tolower);
|
||||
|
||||
// Then remove the extension
|
||||
size_t dotPos = movieName.find('.');
|
||||
if (dotPos != SCP_string::npos) {
|
||||
movieName.resize(dotPos);
|
||||
}
|
||||
|
||||
// Try to open the input stream
|
||||
auto input = openInputStream(movieName);
|
||||
if (!input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We now have a valid input stream, try to find the correct streams
|
||||
auto status = initializeStatus(input);
|
||||
if (!status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Buffer ~ 2 seconds of video and audio
|
||||
initializeQueues(static_cast<size_t>(ceil(getFrameRate(status->videoStream, status->videoCodecCtx))) * 2);
|
||||
|
||||
// We're done, now just put the pointer into this
|
||||
std::swap(m_input, input);
|
||||
std::swap(m_status, status);
|
||||
return true;
|
||||
}
|
||||
|
||||
MovieProperties FFMPEGDecoder::getProperties() {
|
||||
MovieProperties props;
|
||||
props.size.width = static_cast<size_t>(m_status->videoCodecPars.width);
|
||||
props.size.height = static_cast<size_t>(m_status->videoCodecPars.height);
|
||||
|
||||
props.fps = static_cast<float>(getFrameRate(m_status->videoStream, m_status->videoCodecCtx));
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
void FFMPEGDecoder::startDecoding() {
|
||||
std::unique_ptr<VideoDecoder> videoDecoder(new VideoDecoder(m_status.get()));
|
||||
|
||||
std::unique_ptr<AudioDecoder> audioDecoder;
|
||||
|
||||
if (hasAudio()) {
|
||||
audioDecoder.reset(new AudioDecoder(m_status.get()));
|
||||
}
|
||||
|
||||
auto ctx = m_input->m_ctx->ctx();
|
||||
AVPacket packet;
|
||||
while (isDecoding()) {
|
||||
auto read_err = av_read_frame(ctx, &packet);
|
||||
AVPacketScope scope(&packet);
|
||||
|
||||
if (avio_feof(ctx->pb) != 0) {
|
||||
// EOF!!
|
||||
break;
|
||||
} else if (read_err < 0) {
|
||||
if (read_err == AVERROR_EOF) {
|
||||
// Finished reading -> break out of loop
|
||||
break;
|
||||
} else {
|
||||
// Some kind of other error, try to continue reading
|
||||
char errorStr[512];
|
||||
av_strerror(read_err, errorStr, sizeof(errorStr));
|
||||
mprintf(("FFMPEG: Failed to read frame! Error: %s\n", errorStr));
|
||||
|
||||
// Skip packet
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.stream_index == m_status->videoStreamIndex) {
|
||||
videoDecoder->decodePacket(&packet);
|
||||
|
||||
VideoFramePtr ptr;
|
||||
while((ptr = videoDecoder->getFrame()) != nullptr) {
|
||||
pushFrameData(std::move(ptr));
|
||||
}
|
||||
} else if (audioDecoder && packet.stream_index == m_status->audioStreamIndex) {
|
||||
audioDecoder->decodePacket(&packet);
|
||||
|
||||
AudioFramePtr ptr;
|
||||
while ((ptr = audioDecoder->getFrame()) != nullptr) {
|
||||
pushAudioData(std::move(ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDecoding()) {
|
||||
// If we are still alive then read the last frames from the decoders
|
||||
videoDecoder->finishDecoding();
|
||||
VideoFramePtr video_ptr;
|
||||
while ((video_ptr = videoDecoder->getFrame()) != nullptr) {
|
||||
pushFrameData(std::move(video_ptr));
|
||||
}
|
||||
|
||||
if (audioDecoder) {
|
||||
audioDecoder->finishDecoding();
|
||||
AudioFramePtr audio_frame;
|
||||
while ((audio_frame = audioDecoder->getFrame()) != nullptr) {
|
||||
pushAudioData(std::move(audio_frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopDecoder();
|
||||
}
|
||||
|
||||
bool FFMPEGDecoder::hasAudio() {
|
||||
return m_status->audioStreamIndex >= 0;
|
||||
}
|
||||
|
||||
void FFMPEGDecoder::close() {
|
||||
if (m_status) {
|
||||
m_status = nullptr;
|
||||
}
|
||||
|
||||
if (m_input) {
|
||||
// This will delete the InputStream pointer and free all data
|
||||
m_input = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "cutscene/Decoder.h"
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
struct InputStream;
|
||||
|
||||
struct DecoderStatus;
|
||||
|
||||
class FFMPEGDecoder: public Decoder {
|
||||
private:
|
||||
std::unique_ptr<InputStream> m_input;
|
||||
|
||||
std::unique_ptr<DecoderStatus> m_status;
|
||||
|
||||
public:
|
||||
FFMPEGDecoder();
|
||||
|
||||
virtual ~FFMPEGDecoder();
|
||||
|
||||
bool initialize(const SCP_string& fileName) SCP_OVERRIDE;
|
||||
|
||||
MovieProperties getProperties() SCP_OVERRIDE;
|
||||
|
||||
void startDecoding() SCP_OVERRIDE;
|
||||
|
||||
bool hasAudio() SCP_OVERRIDE;
|
||||
|
||||
void close() SCP_OVERRIDE;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
#include "cutscene/ffmpeg/VideoDecoder.h"
|
||||
|
||||
#include "tracing/tracing.h"
|
||||
|
||||
namespace {
|
||||
const AVPixelFormat DESTINATION_FORMAT = AV_PIX_FMT_YUV420P;
|
||||
|
||||
SwsContext* getSWSContext(int width, int height, AVPixelFormat fmt) {
|
||||
return sws_getContext(width, height, fmt, width, height, DESTINATION_FORMAT,
|
||||
SWS_BILINEAR, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
double getFrameTime(int64_t pts, AVRational time_base) {
|
||||
return pts * av_q2d(time_base);
|
||||
}
|
||||
}
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
class FFMPEGVideoFrame: public VideoFrame {
|
||||
public:
|
||||
FFMPEGVideoFrame() : frame(nullptr) {
|
||||
}
|
||||
|
||||
virtual ~FFMPEGVideoFrame() {
|
||||
if (frame != nullptr) {
|
||||
av_freep(&frame->data[0]);
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
}
|
||||
|
||||
virtual DataPointers getDataPointers() {
|
||||
DataPointers ptrs;
|
||||
ptrs.y = frame->data[0];
|
||||
ptrs.u = frame->data[1];
|
||||
ptrs.v = frame->data[2];
|
||||
|
||||
return ptrs;
|
||||
}
|
||||
|
||||
AVFrame* frame;
|
||||
};
|
||||
|
||||
VideoDecoder::VideoDecoder(DecoderStatus* status)
|
||||
: FFMPEGStreamDecoder(status),
|
||||
m_frameId(0) {
|
||||
m_swsCtx = getSWSContext(m_status->videoCodecPars.width, m_status->videoCodecPars.height,
|
||||
m_status->videoCodecPars.pixel_format);
|
||||
}
|
||||
|
||||
VideoDecoder::~VideoDecoder() {
|
||||
sws_freeContext(m_swsCtx);
|
||||
}
|
||||
|
||||
void VideoDecoder::convertAndPushPicture(const AVFrame* frame) {
|
||||
// Allocate a picture to hold the YUV data
|
||||
AVFrame* yuvFrame = av_frame_alloc();
|
||||
av_frame_copy(yuvFrame, frame);
|
||||
yuvFrame->format = DESTINATION_FORMAT;
|
||||
|
||||
av_image_alloc(yuvFrame->data,
|
||||
yuvFrame->linesize,
|
||||
m_status->videoCodecPars.width,
|
||||
m_status->videoCodecPars.height,
|
||||
DESTINATION_FORMAT,
|
||||
1);
|
||||
|
||||
std::unique_ptr<FFMPEGVideoFrame> videoFramePtr(new FFMPEGVideoFrame());
|
||||
|
||||
if (m_status->videoCodecPars.pixel_format == DESTINATION_FORMAT) {
|
||||
av_image_copy(yuvFrame->data,
|
||||
yuvFrame->linesize,
|
||||
(const uint8_t**) (frame->data),
|
||||
frame->linesize,
|
||||
DESTINATION_FORMAT,
|
||||
m_status->videoCodecPars.width,
|
||||
m_status->videoCodecPars.height);
|
||||
} else {
|
||||
// Convert frame to YUV
|
||||
sws_scale(
|
||||
m_swsCtx,
|
||||
(uint8_t const* const*) frame->data,
|
||||
frame->linesize,
|
||||
0,
|
||||
m_status->videoCodecPars.height,
|
||||
yuvFrame->data,
|
||||
yuvFrame->linesize
|
||||
);
|
||||
}
|
||||
|
||||
videoFramePtr->id = ++m_frameId;
|
||||
videoFramePtr->frameTime = getFrameTime(av_frame_get_best_effort_timestamp(frame), m_status->videoStream->time_base);
|
||||
videoFramePtr->frame = yuvFrame;
|
||||
|
||||
videoFramePtr->ySize.height = static_cast<size_t>(m_status->videoCodecPars.height);
|
||||
videoFramePtr->ySize.width = static_cast<size_t>(m_status->videoCodecPars.width);
|
||||
videoFramePtr->ySize.stride = static_cast<size_t>(yuvFrame->linesize[0]);
|
||||
|
||||
// 420P means that the UV channels have half the width and height
|
||||
videoFramePtr->uvSize.height = static_cast<size_t>(m_status->videoCodecPars.height / 2);
|
||||
videoFramePtr->uvSize.width = static_cast<size_t>(m_status->videoCodecPars.width / 2);
|
||||
videoFramePtr->uvSize.stride = static_cast<size_t>(yuvFrame->linesize[1]);
|
||||
|
||||
pushFrame(VideoFramePtr(videoFramePtr.release()));
|
||||
}
|
||||
|
||||
void VideoDecoder::decodePacket(AVPacket* packet) {
|
||||
TRACE_SCOPE(tracing::CutsceneFFmpegVideoDecoder);
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
int send_result;
|
||||
do {
|
||||
send_result = avcodec_send_packet(m_status->videoCodecCtx, packet);
|
||||
|
||||
while(avcodec_receive_frame(m_status->videoCodecCtx, m_decodeFrame) == 0) {
|
||||
convertAndPushPicture(m_decodeFrame);
|
||||
}
|
||||
} while (send_result == AVERROR(EAGAIN));
|
||||
#else
|
||||
int finishedFrame = 0;
|
||||
auto result = avcodec_decode_video2(m_status->videoCodecCtx, m_decodeFrame, &finishedFrame, packet);
|
||||
|
||||
if (result >= 0 && finishedFrame) {
|
||||
convertAndPushPicture(m_decodeFrame);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VideoDecoder::finishDecoding() {
|
||||
TRACE_SCOPE(tracing::CutsceneFFmpegVideoDecoder);
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
// Send flush packet
|
||||
avcodec_send_packet(m_status->videoCodecCtx, nullptr);
|
||||
|
||||
// Handle those decoders that have a delay
|
||||
while (true) {
|
||||
auto ret = avcodec_receive_frame(m_status->videoCodecCtx, m_decodeFrame);
|
||||
|
||||
if (ret == 0) {
|
||||
convertAndPushPicture(m_decodeFrame);
|
||||
} else {
|
||||
// Everything consumed or error
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Handle those decoders that have a delay
|
||||
AVPacket nullPacket;
|
||||
memset(&nullPacket, 0, sizeof(nullPacket));
|
||||
nullPacket.data = nullptr;
|
||||
nullPacket.size = 0;
|
||||
|
||||
while (true) {
|
||||
int finishedFrame = 1;
|
||||
auto err = avcodec_decode_video2(m_status->videoCodecCtx, m_decodeFrame, &finishedFrame, &nullPacket);
|
||||
|
||||
if (err < 0 || !finishedFrame) {
|
||||
break;
|
||||
}
|
||||
|
||||
convertAndPushPicture(m_decodeFrame);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "cutscene/ffmpeg/internal.h"
|
||||
#include "cutscene/ffmpeg/FFMPEGDecoder.h"
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
class VideoDecoder: public FFMPEGStreamDecoder<VideoFrame> {
|
||||
private:
|
||||
int m_frameId;
|
||||
SwsContext* m_swsCtx;
|
||||
|
||||
void convertAndPushPicture(const AVFrame* frame);
|
||||
|
||||
public:
|
||||
explicit VideoDecoder(DecoderStatus* status);
|
||||
|
||||
virtual ~VideoDecoder();
|
||||
|
||||
virtual void decodePacket(AVPacket* packet) SCP_OVERRIDE;
|
||||
|
||||
virtual void finishDecoding() SCP_OVERRIDE;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
#include "cutscene/ffmpeg/internal.h"
|
||||
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
DecoderStatus::DecoderStatus() {}
|
||||
|
||||
DecoderStatus::~DecoderStatus() {
|
||||
videoStreamIndex = -1;
|
||||
videoStream = nullptr;
|
||||
videoCodec = nullptr;
|
||||
|
||||
if (videoCodecCtx != nullptr) {
|
||||
avcodec_close(videoCodecCtx);
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
avcodec_free_context(&videoCodecCtx);
|
||||
#endif
|
||||
videoCodecCtx = nullptr;
|
||||
}
|
||||
|
||||
audioStreamIndex = -1;
|
||||
audioStream = nullptr;
|
||||
audioCodec = nullptr;
|
||||
|
||||
if (audioCodecCtx != nullptr) {
|
||||
avcodec_close(audioCodecCtx);
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(57, 24, 255)
|
||||
avcodec_free_context(&audioCodecCtx);
|
||||
#endif
|
||||
audioCodecCtx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ffmpeg
|
||||
} // namespace cutscene
|
||||
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "libs/ffmpeg/FFmpegHeaders.h"
|
||||
|
||||
#include "cutscene/Decoder.h"
|
||||
|
||||
namespace cutscene {
|
||||
namespace ffmpeg {
|
||||
struct CodecContextParameters {
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
AVPixelFormat pixel_format = AV_PIX_FMT_NONE;
|
||||
|
||||
uint64_t channel_layout = 0;
|
||||
int sample_rate = -1;
|
||||
AVSampleFormat audio_format = AV_SAMPLE_FMT_NONE;
|
||||
};
|
||||
|
||||
struct DecoderStatus {
|
||||
int videoStreamIndex = -1;
|
||||
AVStream* videoStream = nullptr;
|
||||
CodecContextParameters videoCodecPars;
|
||||
AVCodec* videoCodec = nullptr;
|
||||
AVCodecContext* videoCodecCtx = nullptr;
|
||||
|
||||
int audioStreamIndex = -1;
|
||||
AVStream* audioStream = nullptr;
|
||||
CodecContextParameters audioCodecPars;
|
||||
AVCodec* audioCodec = nullptr;
|
||||
AVCodecContext* audioCodecCtx = nullptr;
|
||||
|
||||
DecoderStatus();
|
||||
|
||||
~DecoderStatus();
|
||||
|
||||
private:
|
||||
DecoderStatus(const DecoderStatus&) SCP_DELETED_FUNCTION;
|
||||
|
||||
DecoderStatus& operator=(const DecoderStatus&) SCP_DELETED_FUNCTION;
|
||||
};
|
||||
|
||||
template<typename Frame>
|
||||
class FFMPEGStreamDecoder {
|
||||
protected:
|
||||
typedef Frame frame_type;
|
||||
|
||||
DecoderStatus* m_status = nullptr;
|
||||
|
||||
AVFrame* m_decodeFrame = nullptr;
|
||||
|
||||
SCP_queue<FramePtr<frame_type>> m_decodedFrames;
|
||||
|
||||
void pushFrame(FramePtr<frame_type>&& frame);
|
||||
public:
|
||||
explicit FFMPEGStreamDecoder(DecoderStatus* status);
|
||||
virtual ~FFMPEGStreamDecoder();
|
||||
|
||||
FFMPEGStreamDecoder(const FFMPEGStreamDecoder&) SCP_DELETED_FUNCTION;
|
||||
FFMPEGStreamDecoder& operator=(const FFMPEGStreamDecoder&) SCP_DELETED_FUNCTION;
|
||||
|
||||
virtual void decodePacket(AVPacket* packet) = 0;
|
||||
|
||||
virtual void finishDecoding() = 0;
|
||||
|
||||
virtual FramePtr<frame_type> getFrame();
|
||||
};
|
||||
|
||||
template <typename Frame>
|
||||
void FFMPEGStreamDecoder<Frame>::pushFrame(FramePtr<frame_type>&& frame)
|
||||
{
|
||||
m_decodedFrames.push(std::move(frame));
|
||||
}
|
||||
|
||||
template <typename Frame>
|
||||
FFMPEGStreamDecoder<Frame>::FFMPEGStreamDecoder(DecoderStatus* status):m_status(status)
|
||||
{
|
||||
m_decodeFrame = av_frame_alloc();
|
||||
}
|
||||
|
||||
template <typename Frame>
|
||||
FFMPEGStreamDecoder<Frame>::~FFMPEGStreamDecoder()
|
||||
{
|
||||
av_frame_free(&m_decodeFrame);
|
||||
}
|
||||
|
||||
template <typename Frame>
|
||||
FramePtr<typename FFMPEGStreamDecoder<Frame>::frame_type> FFMPEGStreamDecoder<Frame>::getFrame()
|
||||
{
|
||||
if (m_decodedFrames.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto frame = std::move(m_decodedFrames.front());
|
||||
m_decodedFrames.pop();
|
||||
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-106
@@ -13,64 +13,21 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "io/cursor.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "cfile/cfile.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "cutscene/cutscenes.h" // cutscene_mark_viewable()
|
||||
#include "cutscene/movie.h"
|
||||
#include "cutscene/mvelib.h"
|
||||
#include "cutscene/oggplayer.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "cutscene/player.h" // cutscene_mark_viewable()
|
||||
|
||||
extern int Game_mode;
|
||||
extern int Is_standalone;
|
||||
|
||||
const char *movie_ext_list[] = { ".ogg", ".mve" };
|
||||
const int NUM_MOVIE_EXT = sizeof(movie_ext_list) / sizeof(char*);
|
||||
|
||||
|
||||
#define MOVIE_NONE -1
|
||||
#define MOVIE_OGG 0
|
||||
#define MOVIE_MVE 1
|
||||
|
||||
// This module links freespace movie calls to the actual API calls the play the movie.
|
||||
// This module handles all the different requires of OS and gfx API and finding the file to play
|
||||
|
||||
|
||||
// filename - file to search for
|
||||
// out_name - output, full path to file
|
||||
// returns non-zero if file is found
|
||||
int movie_find(char *filename, char *out_name)
|
||||
{
|
||||
char full_path[MAX_PATH];
|
||||
char tmp_name[MAX_PATH];
|
||||
size_t size, offset = 0;
|
||||
|
||||
if (out_name == NULL)
|
||||
return MOVIE_NONE;
|
||||
|
||||
|
||||
memset( full_path, 0, sizeof(full_path) );
|
||||
memset( tmp_name, 0, sizeof(tmp_name) );
|
||||
|
||||
// remove extension
|
||||
strcpy_s( tmp_name, filename );
|
||||
char *p = strrchr(tmp_name, '.');
|
||||
if ( p ) *p = 0;
|
||||
|
||||
int rc = cf_find_file_location_ext(tmp_name, NUM_MOVIE_EXT, movie_ext_list, CF_TYPE_ANY, sizeof(full_path) - 1, full_path, &size, &offset, 0);
|
||||
|
||||
if (rc == MOVIE_NONE)
|
||||
return MOVIE_NONE;
|
||||
|
||||
strcpy( out_name, full_path );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
namespace movie {
|
||||
// Play one movie
|
||||
bool movie_play(char *name)
|
||||
{
|
||||
bool play(const char* name) {
|
||||
// mark the movie as viewable to the player when in a campaign
|
||||
// do this before anything else so that we're sure the movie is available
|
||||
// to the player even if it's not going to play right now
|
||||
@@ -78,25 +35,7 @@ bool movie_play(char *name)
|
||||
cutscene_mark_viewable(name);
|
||||
}
|
||||
|
||||
extern int Is_standalone;
|
||||
|
||||
if (Cmdline_nomovies || Is_standalone)
|
||||
return false;
|
||||
|
||||
|
||||
char full_name[MAX_PATH];
|
||||
int rc = 0;
|
||||
|
||||
memset(full_name, 0, sizeof(full_name));
|
||||
|
||||
rc = movie_find(name, full_name);
|
||||
|
||||
if (rc == MOVIE_NONE) {
|
||||
strcpy_s(full_name, name);
|
||||
char *p = strrchr(full_name, '.');
|
||||
if ( p ) *p = 0;
|
||||
|
||||
mprintf(("Movie Error: Unable to open '%s' movie in any supported format.\n", full_name));
|
||||
if (Cmdline_nomovies || Is_standalone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -116,38 +55,12 @@ bool movie_play(char *name)
|
||||
// clear third buffer (may not be one, but that's ok)
|
||||
gr_clear();
|
||||
|
||||
if (rc == MOVIE_OGG) {
|
||||
THEORAFILE *movie_ogg = theora_open(name);
|
||||
|
||||
if (movie_ogg) {
|
||||
// start playing ...
|
||||
theora_play(movie_ogg);
|
||||
|
||||
// ... done playing, close the movie
|
||||
theora_close(movie_ogg);
|
||||
} else {
|
||||
// uh-oh, movie is invalid... Abory, Retry, Fail?
|
||||
mprintf(("MOVIE ERROR: Found invalid movie! (%s)\n", name));
|
||||
io::mouse::CursorManager::get()->popStatus();
|
||||
return false;
|
||||
}
|
||||
} else if (rc == MOVIE_MVE) {
|
||||
MVESTREAM *movie_mve = mve_open(name);
|
||||
|
||||
if (movie_mve) {
|
||||
// start playing ...
|
||||
mve_init(movie_mve);
|
||||
mve_play(movie_mve);
|
||||
|
||||
// ... done playing, close the movie
|
||||
mve_shutdown();
|
||||
mve_close(movie_mve);
|
||||
} else {
|
||||
// uh-oh, movie is invalid... Abory, Retry, Fail?
|
||||
mprintf(("MOVIE ERROR: Found invalid movie! (%s)\n", name));
|
||||
io::mouse::CursorManager::get()->popStatus();
|
||||
return false;
|
||||
}
|
||||
auto player = cutscene::Player::newPlayer(name);
|
||||
if (player) {
|
||||
player->startPlayback();
|
||||
} else {
|
||||
// uh-oh, movie is invalid... Abory, Retry, Fail?
|
||||
mprintf(("MOVIE ERROR: Found invalid movie! (%s)\n", name));
|
||||
}
|
||||
|
||||
// show the mouse cursor again
|
||||
@@ -156,8 +69,9 @@ bool movie_play(char *name)
|
||||
return true;
|
||||
}
|
||||
|
||||
void movie_play_two(char *name1, char *name2)
|
||||
{
|
||||
if ( movie_play(name1) )
|
||||
movie_play(name2);
|
||||
void play_two(const char* name1, const char* name2) {
|
||||
if (play(name1)) {
|
||||
play(name2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,12 @@
|
||||
#ifndef _MOVIE_HEADER_FILE
|
||||
#define _MOVIE_HEADER_FILE
|
||||
|
||||
extern const char *movie_ext_list[];
|
||||
extern const int NUM_MOVIE_EXT;
|
||||
namespace movie {
|
||||
|
||||
bool movie_play(char *name);
|
||||
void movie_play_two(char *name1, char *name2);
|
||||
bool play(const char* name);
|
||||
|
||||
void play_two(const char* name1, const char* name2);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,56 +0,0 @@
|
||||
|
||||
|
||||
static int audio_exp_table[256] =
|
||||
{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 47, 51, 56, 61,
|
||||
66, 72, 79, 86, 94, 102, 112, 122, 133, 145, 158, 173, 189, 206, 225, 245,
|
||||
267, 292, 318, 348, 379, 414, 452, 493, 538, 587, 640, 699, 763, 832, 908, 991,
|
||||
1081, 1180, 1288, 1405, 1534, 1673, 1826, 1993, 2175, 2373, 2590, 2826, 3084, 3365, 3672, 4008,
|
||||
4373, 4772, 5208, 5683, 6202, 6767, 7385, 8059, 8794, 9597, 10472, 11428, 12471, 13609, 14851, 16206,
|
||||
17685, 19298, 21060, 22981, 25078, 27367, 29864, 32589, -29973, -26728, -23186, -19322, -15105, -10503, -5481, -1,
|
||||
1, 1, 5481, 10503, 15105, 19322, 23186, 26728, 29973, -32589, -29864, -27367, -25078, -22981, -21060, -19298,
|
||||
-17685, -16206, -14851, -13609, -12471, -11428, -10472, -9597, -8794, -8059, -7385, -6767, -6202, -5683, -5208, -4772,
|
||||
-4373, -4008, -3672, -3365, -3084, -2826, -2590, -2373, -2175, -1993, -1826, -1673, -1534, -1405, -1288, -1180,
|
||||
-1081, -991, -908, -832, -763, -699, -640, -587, -538, -493, -452, -414, -379, -348, -318, -292,
|
||||
-267, -245, -225, -206, -189, -173, -158, -145, -133, -122, -112, -102, -94, -86, -79, -72,
|
||||
-66, -61, -56, -51, -47, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33,
|
||||
-32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17,
|
||||
-16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
|
||||
};
|
||||
|
||||
static int getWord(unsigned char **fin)
|
||||
{
|
||||
int value = ((*fin)[1] << 8) | (*fin)[0];
|
||||
*fin += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
static void sendWord(short **fout, int nOffset)
|
||||
{
|
||||
*(*fout)++ = (short)nOffset;
|
||||
}
|
||||
|
||||
static void processSwath(short *fout, unsigned char *data, int swath, int *offsets)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<swath; i++) {
|
||||
offsets[i&1] += audio_exp_table[data[i]];
|
||||
sendWord(&fout, offsets[i&1]);
|
||||
}
|
||||
}
|
||||
|
||||
void mveaudio_uncompress(short *buffer, unsigned char *data)
|
||||
{
|
||||
int nCurOffsets[2];
|
||||
int swath;
|
||||
|
||||
data += 4;
|
||||
swath = getWord(&data) / 2;
|
||||
nCurOffsets[0] = getWord(&data);
|
||||
nCurOffsets[1] = getWord(&data);
|
||||
sendWord(&buffer, nCurOffsets[0]);
|
||||
sendWord(&buffer, nCurOffsets[1]);
|
||||
processSwath(buffer, data, swath, nCurOffsets);
|
||||
}
|
||||
@@ -1,371 +0,0 @@
|
||||
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
#include "cutscene/mvelib.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
|
||||
static const char MVE_HEADER[] = "Interplay MVE File\x1A";
|
||||
static const short MVE_HDRCONST1 = 0x001A;
|
||||
static const short MVE_HDRCONST2 = 0x0100;
|
||||
static const short MVE_HDRCONST3 = 0x1133;
|
||||
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// public MVEFILE functions
|
||||
// -----------------------------------------------------------
|
||||
|
||||
// utility functions for mvefile and mveplayer
|
||||
short mve_get_short(ubyte *data)
|
||||
{
|
||||
short value;
|
||||
value = data[0] | (data[1] << 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
ushort mve_get_ushort(ubyte *data)
|
||||
{
|
||||
ushort value;
|
||||
value = data[0] | (data[1] << 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
int mve_get_int(ubyte *data)
|
||||
{
|
||||
int value;
|
||||
value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
||||
return value;
|
||||
}
|
||||
|
||||
// open an MVE file
|
||||
MVEFILE *mvefile_open(char *filename)
|
||||
{
|
||||
int cf_opened = 0;
|
||||
int mve_valid = 1;
|
||||
char lower_name[MAX_FILENAME_LEN];
|
||||
char buffer[20];
|
||||
MVEFILE *file;
|
||||
|
||||
// create the file
|
||||
file = (MVEFILE *)vm_malloc(sizeof(MVEFILE));
|
||||
|
||||
// set defaults
|
||||
file->stream = NULL;
|
||||
file->cur_chunk = NULL;
|
||||
file->buf_size = 0;
|
||||
file->cur_fill = 0;
|
||||
file->next_segment = 0;
|
||||
|
||||
// lower case filename for checking
|
||||
strncpy(lower_name, filename, strlen(filename)+1);
|
||||
strlwr(lower_name);
|
||||
|
||||
char *p = strchr( lower_name, '.' );
|
||||
if ( p ) *p = 0;
|
||||
|
||||
strcat_s( lower_name, ".mve" );
|
||||
|
||||
// NOTE: CF_TYPE *must* be ANY to get movies off of the CDs
|
||||
// assume lower case filename for *nix
|
||||
file->stream = cfopen(lower_name, "rb", CFILE_NORMAL, CF_TYPE_ANY);
|
||||
if ( file->stream ) {
|
||||
cf_opened = 1;
|
||||
}
|
||||
|
||||
if (!cf_opened) {
|
||||
mvefile_close(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// initialize the buffer
|
||||
file->cur_chunk = (ubyte *)vm_malloc(100 + 1024);
|
||||
file->buf_size = 100 + 1024;
|
||||
|
||||
// verify the file's header
|
||||
cfread_string(buffer, 20, file->stream);
|
||||
|
||||
if (strcmp(buffer, MVE_HEADER))
|
||||
mve_valid = 0;
|
||||
|
||||
if (cfread_short(file->stream) != MVE_HDRCONST1)
|
||||
mve_valid = 0;
|
||||
|
||||
if (cfread_short(file->stream) != MVE_HDRCONST2)
|
||||
mve_valid = 0;
|
||||
|
||||
if (cfread_short(file->stream) != MVE_HDRCONST3)
|
||||
mve_valid = 0;
|
||||
|
||||
if (!mve_valid) {
|
||||
mvefile_close(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// now, prefetch the next chunk
|
||||
mvefile_fetch_next_chunk(file);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
// close a MVE file
|
||||
void mvefile_close(MVEFILE *file)
|
||||
{
|
||||
// free the stream
|
||||
if (file->stream)
|
||||
cfclose(file->stream);
|
||||
|
||||
file->stream = NULL;
|
||||
|
||||
// free the buffer
|
||||
if (file->cur_chunk)
|
||||
vm_free(file->cur_chunk);
|
||||
|
||||
file->cur_chunk = NULL;
|
||||
|
||||
// not strictly necessary
|
||||
file->buf_size = 0;
|
||||
file->cur_fill = 0;
|
||||
file->next_segment = 0;
|
||||
|
||||
// free the struct
|
||||
vm_free(file);
|
||||
}
|
||||
|
||||
// get the size of the next segment
|
||||
int mvefile_get_next_segment_size(MVEFILE *file)
|
||||
{
|
||||
// if nothing is cached, fail
|
||||
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
||||
return -1;
|
||||
|
||||
// if we don't have enough data to get a segment, fail
|
||||
if (file->cur_fill - file->next_segment < 4)
|
||||
return -1;
|
||||
|
||||
// otherwise, get the data length
|
||||
return mve_get_short(file->cur_chunk + file->next_segment);
|
||||
}
|
||||
|
||||
// get type of next segment in chunk (0xff if no more segments in chunk)
|
||||
ubyte mvefile_get_next_segment_major(MVEFILE *file)
|
||||
{
|
||||
// if nothing is cached, fail
|
||||
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
||||
return 0xff;
|
||||
|
||||
// if we don't have enough data to get a segment, fail
|
||||
if (file->cur_fill - file->next_segment < 4)
|
||||
return 0xff;
|
||||
|
||||
// otherwise, get the data length
|
||||
return file->cur_chunk[file->next_segment + 2];
|
||||
}
|
||||
|
||||
// get subtype (version) of next segment in chunk (0xff if no more segments in chunk)
|
||||
ubyte mvefile_get_next_segment_minor(MVEFILE *file)
|
||||
{
|
||||
// if nothing is cached, fail
|
||||
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
||||
return 0xff;
|
||||
|
||||
// if we don't have enough data to get a segment, fail
|
||||
if (file->cur_fill - file->next_segment < 4)
|
||||
return 0xff;
|
||||
|
||||
// otherwise, get the data length
|
||||
return file->cur_chunk[file->next_segment + 3];
|
||||
}
|
||||
|
||||
// see next segment (return NULL if no next segment)
|
||||
ubyte *mvefile_get_next_segment(MVEFILE *file)
|
||||
{
|
||||
// if nothing is cached, fail
|
||||
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
||||
return NULL;
|
||||
|
||||
// if we don't have enough data to get a segment, fail
|
||||
if (file->cur_fill - file->next_segment < 4)
|
||||
return NULL;
|
||||
|
||||
// otherwise, get the data length
|
||||
return file->cur_chunk + file->next_segment + 4;
|
||||
}
|
||||
|
||||
// advance to next segment
|
||||
void mvefile_advance_segment(MVEFILE *file)
|
||||
{
|
||||
// if nothing is cached, fail
|
||||
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
||||
return;
|
||||
|
||||
// if we don't have enough data to get a segment, fail
|
||||
if (file->cur_fill - file->next_segment < 4)
|
||||
return;
|
||||
|
||||
// else, advance to next segment
|
||||
file->next_segment += (4 + mve_get_ushort(file->cur_chunk + file->next_segment));
|
||||
}
|
||||
|
||||
// fetch the next chunk (return 0 if at end of stream)
|
||||
int mvefile_fetch_next_chunk(MVEFILE *file)
|
||||
{
|
||||
ubyte buffer[4];
|
||||
ubyte *new_buffer;
|
||||
ushort length;
|
||||
|
||||
// fail if not open
|
||||
if (file->stream == NULL)
|
||||
return 0;
|
||||
|
||||
// fail if we can't read the next segment descriptor
|
||||
if (cfread(buffer, 1, 4, file->stream) < 4)
|
||||
return 0;
|
||||
|
||||
// pull out the next length
|
||||
length = mve_get_short(buffer);
|
||||
|
||||
// setup a new buffer if needed --
|
||||
// only allocate new buffer is old one is too small
|
||||
if (length > file->buf_size) {
|
||||
// allocate new buffer
|
||||
new_buffer = (ubyte *)vm_malloc(100 + length);
|
||||
|
||||
// copy old data
|
||||
if (file->cur_chunk && file->cur_fill)
|
||||
memcpy(new_buffer, file->cur_chunk, file->cur_fill);
|
||||
|
||||
// free old buffer
|
||||
if (file->cur_chunk) {
|
||||
vm_free(file->cur_chunk);
|
||||
file->cur_chunk = NULL;
|
||||
}
|
||||
|
||||
// install new buffer
|
||||
file->cur_chunk = new_buffer;
|
||||
file->buf_size = 100 + length;
|
||||
}
|
||||
|
||||
// read the chunk
|
||||
if (length > 0) {
|
||||
if (cfread(file->cur_chunk, 1, length, file->stream) < length)
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->cur_fill = length;
|
||||
file->next_segment = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// public MVESTREAM functions
|
||||
// -----------------------------------------------------------
|
||||
|
||||
// open an MVE stream
|
||||
MVESTREAM *mve_open(char *filename)
|
||||
{
|
||||
MVESTREAM *stream;
|
||||
|
||||
// allocate
|
||||
stream = (MVESTREAM *)vm_malloc(sizeof(MVESTREAM));
|
||||
|
||||
// defaults
|
||||
stream->movie = NULL;
|
||||
|
||||
// open
|
||||
stream->movie = mvefile_open(filename);
|
||||
|
||||
if (stream->movie == NULL) {
|
||||
mve_close(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
// close an MVE stream
|
||||
void mve_close(MVESTREAM *stream)
|
||||
{
|
||||
// close MVEFILE
|
||||
if (stream->movie)
|
||||
mvefile_close(stream->movie);
|
||||
|
||||
stream->movie = NULL;
|
||||
|
||||
vm_free(stream);
|
||||
}
|
||||
|
||||
// play next chunk
|
||||
int mve_play_next_chunk(MVESTREAM *stream)
|
||||
{
|
||||
ubyte major, minor;
|
||||
ubyte *data;
|
||||
int len;
|
||||
|
||||
// loop over segments
|
||||
major = mvefile_get_next_segment_major(stream->movie);
|
||||
while (major != 0xff) {
|
||||
// check whether to handle the segment
|
||||
if (major < 32) {
|
||||
minor = mvefile_get_next_segment_minor(stream->movie);
|
||||
len = mvefile_get_next_segment_size(stream->movie);
|
||||
data = mvefile_get_next_segment(stream->movie);
|
||||
|
||||
switch (major) {
|
||||
case 0x00:
|
||||
mve_end_movie();
|
||||
break;
|
||||
case 0x01:
|
||||
mve_end_chunk();
|
||||
break;
|
||||
case 0x02:
|
||||
mve_timer_create(data);
|
||||
break;
|
||||
case 0x03:
|
||||
mve_audio_createbuf(minor, data);
|
||||
break;
|
||||
case 0x04:
|
||||
mve_audio_play();
|
||||
break;
|
||||
case 0x05:
|
||||
if (!mve_video_createbuf(minor, data))
|
||||
return 0;
|
||||
break;
|
||||
case 0x07:
|
||||
mve_video_display();
|
||||
break;
|
||||
case 0x08:
|
||||
mve_audio_data(major, data);
|
||||
break;
|
||||
case 0x09:
|
||||
mve_audio_data(major, data);
|
||||
break;
|
||||
case 0x0a:
|
||||
if (!mve_video_init(data))
|
||||
return 0;
|
||||
break;
|
||||
case 0x0c:
|
||||
mve_video_palette(data);
|
||||
break;
|
||||
case 0x0f:
|
||||
mve_video_codemap(data, len);
|
||||
break;
|
||||
case 0x11:
|
||||
mve_video_data(data, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// advance to next segment
|
||||
mvefile_advance_segment(stream->movie);
|
||||
major = mvefile_get_next_segment_major(stream->movie);
|
||||
}
|
||||
|
||||
if (!mvefile_fetch_next_chunk(stream->movie))
|
||||
return 0;
|
||||
|
||||
// return status
|
||||
return 1;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
|
||||
|
||||
#ifndef INCLUDED_MVELIB_H
|
||||
#define INCLUDED_MVELIB_H
|
||||
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
struct CFILE;
|
||||
|
||||
// structure for maintaining info on a MVEFILE stream
|
||||
typedef struct MVEFILE
|
||||
{
|
||||
CFILE *stream;
|
||||
ubyte *cur_chunk;
|
||||
int buf_size;
|
||||
int cur_fill;
|
||||
int next_segment;
|
||||
} MVEFILE;
|
||||
|
||||
// open a .MVE file
|
||||
MVEFILE *mvefile_open(char *filename);
|
||||
|
||||
// close a .MVE file
|
||||
void mvefile_close(MVEFILE *movie);
|
||||
|
||||
// get size of next segment in chunk (-1 if no more segments in chunk)
|
||||
int mvefile_get_next_segment_size(MVEFILE *movie);
|
||||
|
||||
// get type of next segment in chunk (0xff if no more segments in chunk)
|
||||
ubyte mvefile_get_next_segment_major(MVEFILE *movie);
|
||||
|
||||
// get subtype (version) of next segment in chunk (0xff if no more segments in chunk)
|
||||
ubyte mvefile_get_next_segment_minor(MVEFILE *movie);
|
||||
|
||||
// see next segment (return NULL if no next segment)
|
||||
ubyte *mvefile_get_next_segment(MVEFILE *movie);
|
||||
|
||||
// advance to next segment
|
||||
void mvefile_advance_segment(MVEFILE *movie);
|
||||
|
||||
// fetch the next chunk (return 0 if at end of stream)
|
||||
int mvefile_fetch_next_chunk(MVEFILE *movie);
|
||||
|
||||
// structure for maintaining an MVE stream
|
||||
typedef struct MVESTREAM
|
||||
{
|
||||
MVEFILE *movie;
|
||||
} MVESTREAM;
|
||||
|
||||
// open an MVE stream
|
||||
MVESTREAM *mve_open(char *filename);
|
||||
|
||||
// close an MVE stream
|
||||
void mve_close(MVESTREAM *movie);
|
||||
|
||||
// play next chunk
|
||||
int mve_play_next_chunk(MVESTREAM *movie);
|
||||
|
||||
// basic movie playing functions
|
||||
void mve_init(MVESTREAM *mve);
|
||||
void mve_play(MVESTREAM *mve);
|
||||
void mve_shutdown();
|
||||
|
||||
// utility functions
|
||||
short mve_get_short(ubyte *data);
|
||||
ushort mve_get_ushort(ubyte *data);
|
||||
int mve_get_int(ubyte *data);
|
||||
|
||||
// callbacks for data handling
|
||||
// audio
|
||||
void mve_audio_createbuf(ubyte minor, ubyte *data);
|
||||
int mve_audio_data(ubyte major, ubyte *data);
|
||||
void mve_audio_play();
|
||||
// video
|
||||
int mve_video_createbuf(ubyte minor, ubyte *data);
|
||||
int mve_video_init(ubyte *data);
|
||||
void mve_video_palette(ubyte *data);
|
||||
void mve_video_data(ubyte *data, int len);
|
||||
void mve_video_codemap(ubyte *data, int len);
|
||||
void mve_video_display();
|
||||
// misc
|
||||
void mve_end_movie();
|
||||
void mve_end_chunk();
|
||||
int mve_timer_create(ubyte *data);
|
||||
|
||||
typedef short mves;
|
||||
|
||||
#endif /* INCLUDED_MVELIB_H */
|
||||
@@ -1,815 +0,0 @@
|
||||
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "bmpman/bmpman.h"
|
||||
#include "cutscene/movie.h"
|
||||
#include "cutscene/mvelib.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "graphics/gropengl.h"
|
||||
#include "graphics/gropengldraw.h"
|
||||
#include "graphics/gropenglstate.h"
|
||||
#include "graphics/gropenglshader.h"
|
||||
#include "graphics/gropengltexture.h"
|
||||
#include "graphics/gropengltnl.h"
|
||||
#include "io/key.h"
|
||||
#include "io/timer.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "sound/openal.h"
|
||||
#include "sound/sound.h"
|
||||
|
||||
|
||||
extern int Cmdline_noscalevid;
|
||||
|
||||
static int mve_playing;
|
||||
|
||||
// timer variables
|
||||
static int micro_frame_delay = 0;
|
||||
static int timer_started = 0;
|
||||
#ifdef SCP_UNIX
|
||||
static struct timeval timer_expire = { 0, 0 };
|
||||
#else
|
||||
static int timer_expire;
|
||||
#endif
|
||||
|
||||
// audio variables
|
||||
#define MVE_AUDIO_BUFFERS 64 // total buffers to interact with stream
|
||||
static int mve_audio_buffer_tail = 0;
|
||||
static int mve_audio_playing = 0;
|
||||
static int mve_audio_canplay = 0;
|
||||
static int mve_audio_compressed = 0;
|
||||
static int audiobuf_created;
|
||||
|
||||
// struct for the audio stream information
|
||||
typedef struct MVE_AUDIO_T {
|
||||
ALenum format;
|
||||
int sample_rate;
|
||||
int bytes_per_sec;
|
||||
int channels;
|
||||
int bitsize;
|
||||
ALuint source_id;
|
||||
ALuint audio_buffer[MVE_AUDIO_BUFFERS];
|
||||
} mve_audio_t;
|
||||
|
||||
mve_audio_t *mas; // mve_audio_stream
|
||||
|
||||
// audio decompressor
|
||||
extern void mveaudio_uncompress(short *buffer, unsigned char *data);
|
||||
|
||||
// video variables
|
||||
int g_width, g_height;
|
||||
void *g_vBuffers = NULL;
|
||||
void *g_vBackBuf1, *g_vBackBuf2;
|
||||
ushort *pixelbuf = NULL;
|
||||
static GLuint GLtex = 0;
|
||||
static int buffer_handle = -1;
|
||||
static GLfloat gl_screenYH = 0;
|
||||
static GLfloat gl_screenXW = 0;
|
||||
static GLfloat glVertices[4][4] = {{0}};
|
||||
static int g_screenWidth, g_screenHeight;
|
||||
static float g_screenX, g_screenY;
|
||||
static int g_truecolor = 0;
|
||||
static ubyte g_palette[768];
|
||||
static ubyte *g_pCurMap = NULL;
|
||||
static int g_nMapLength = 0;
|
||||
static int videobuf_created, video_inited;
|
||||
static uint mve_video_skiptimer = 0;
|
||||
static int mve_scale_video = 0;
|
||||
|
||||
// video externs from API graphics functions
|
||||
extern GLenum GL_previous_texture_target;
|
||||
|
||||
// the decoders
|
||||
extern void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
|
||||
extern void decodeFrame8(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
|
||||
|
||||
|
||||
/*************************
|
||||
* general handlers
|
||||
*************************/
|
||||
void mve_end_movie()
|
||||
{
|
||||
mve_playing = 0;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* timer handlers
|
||||
*************************/
|
||||
|
||||
int mve_timer_create(ubyte *data)
|
||||
{
|
||||
int rate = mve_get_int(data);
|
||||
int subd = mve_get_short(data+4);
|
||||
|
||||
micro_frame_delay = rate * subd;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void mve_timer_start(void)
|
||||
{
|
||||
#ifdef SCP_UNIX
|
||||
int nsec = 0;
|
||||
|
||||
gettimeofday(&timer_expire, NULL);
|
||||
|
||||
timer_expire.tv_usec += micro_frame_delay;
|
||||
|
||||
if (timer_expire.tv_usec > 1000000) {
|
||||
nsec = timer_expire.tv_usec / 1000000;
|
||||
timer_expire.tv_sec += nsec;
|
||||
timer_expire.tv_usec -= nsec * 1000000;
|
||||
}
|
||||
#else
|
||||
timer_expire = timer_get_microseconds();
|
||||
timer_expire += micro_frame_delay;
|
||||
#endif
|
||||
|
||||
timer_started = 1;
|
||||
}
|
||||
|
||||
static int mve_do_timer_wait(void)
|
||||
{
|
||||
if (!timer_started) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
int nsec = 0;
|
||||
struct timespec ts, tsRem;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
if (tv.tv_sec > timer_expire.tv_sec) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ( (tv.tv_sec == timer_expire.tv_sec) && (tv.tv_usec >= timer_expire.tv_usec) ) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
ts.tv_sec = timer_expire.tv_sec - tv.tv_sec;
|
||||
ts.tv_nsec = 1000 * (timer_expire.tv_usec - tv.tv_usec);
|
||||
|
||||
if (ts.tv_nsec < 0) {
|
||||
ts.tv_nsec += 1000000000UL;
|
||||
--ts.tv_sec;
|
||||
}
|
||||
|
||||
if ( (nanosleep(&ts, &tsRem) == -1) && (errno == EINTR) ) {
|
||||
// so we got an error that was a signal interupt, try to sleep again with remainder of time
|
||||
if ( (nanosleep(&tsRem, NULL) == -1) && (errno == EINTR) ) {
|
||||
mprintf(("MVE: Timer error! Aborting movie playback!\n"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
timer_expire.tv_usec += micro_frame_delay;
|
||||
|
||||
if (timer_expire.tv_usec > 1000000) {
|
||||
nsec = timer_expire.tv_usec / 1000000;
|
||||
timer_expire.tv_sec += nsec;
|
||||
timer_expire.tv_usec -= nsec * 1000000;
|
||||
}
|
||||
#else
|
||||
int tv, ts, ts2;
|
||||
|
||||
tv = timer_get_microseconds();
|
||||
|
||||
if (tv > timer_expire) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
ts = timer_expire - tv;
|
||||
ts2 = ts/1000;
|
||||
Sleep(ts2);
|
||||
|
||||
end:
|
||||
timer_expire += micro_frame_delay;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mve_timer_stop()
|
||||
{
|
||||
#ifdef SCP_UNIX
|
||||
timer_expire.tv_sec = 0;
|
||||
timer_expire.tv_usec = 0;
|
||||
#else
|
||||
timer_expire = 0;
|
||||
#endif
|
||||
timer_started = 0;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* audio handlers
|
||||
*************************/
|
||||
|
||||
// setup the audio information from the data stream
|
||||
void mve_audio_createbuf(ubyte minor, ubyte *data)
|
||||
{
|
||||
if (audiobuf_created) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if game sound disabled don't try and play movie audio
|
||||
if (!Sound_enabled) {
|
||||
mve_audio_canplay = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int flags, sample_rate;
|
||||
|
||||
mas = (mve_audio_t *) vm_malloc ( sizeof(mve_audio_t) );
|
||||
memset(mas, 0, sizeof(mve_audio_t));
|
||||
|
||||
mas->format = AL_INVALID;
|
||||
|
||||
flags = mve_get_ushort(data + 2);
|
||||
sample_rate = mve_get_ushort(data + 4);
|
||||
|
||||
mas->channels = (flags & 0x0001) ? 2 : 1;
|
||||
mas->bitsize = (flags & 0x0002) ? 16 : 8;
|
||||
|
||||
mas->sample_rate = sample_rate;
|
||||
|
||||
if (minor > 0) {
|
||||
mve_audio_compressed = flags & 0x0004 ? 1 : 0;
|
||||
} else {
|
||||
mve_audio_compressed = 0;
|
||||
}
|
||||
|
||||
if (mas->bitsize == 16) {
|
||||
if (mas->channels == 2) {
|
||||
mas->format = AL_FORMAT_STEREO16;
|
||||
} else if (mas->channels == 1) {
|
||||
mas->format = AL_FORMAT_MONO16;
|
||||
}
|
||||
} else if (mas->bitsize == 8) {
|
||||
if (mas->channels == 2) {
|
||||
mas->format = AL_FORMAT_STEREO8;
|
||||
} else if (mas->channels == 1) {
|
||||
mas->format = AL_FORMAT_MONO8;
|
||||
}
|
||||
}
|
||||
|
||||
// somethings wrong, bail now
|
||||
if (mas->format == AL_INVALID) {
|
||||
mve_audio_canplay = 0;
|
||||
audiobuf_created = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
OpenAL_ErrorCheck( alGenSources(1, &mas->source_id), { mve_audio_canplay = 0; return; } );
|
||||
|
||||
mve_audio_canplay = 1;
|
||||
|
||||
OpenAL_ErrorPrint( alSourcef(mas->source_id, AL_GAIN, 1.0f) );
|
||||
|
||||
memset(mas->audio_buffer, 0, MVE_AUDIO_BUFFERS * sizeof(ALuint));
|
||||
|
||||
mve_audio_buffer_tail = 0;
|
||||
|
||||
audiobuf_created = 1;
|
||||
}
|
||||
|
||||
// play and stream the audio
|
||||
void mve_audio_play()
|
||||
{
|
||||
if (mve_audio_canplay) {
|
||||
ALint status, bqueued;
|
||||
|
||||
OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_SOURCE_STATE, &status), return );
|
||||
|
||||
OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued), return );
|
||||
|
||||
mve_audio_playing = 1;
|
||||
|
||||
if (status != AL_PLAYING && bqueued > 0) {
|
||||
OpenAL_ErrorPrint( alSourcePlay(mas->source_id) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call this in shutdown to stop and close audio
|
||||
static void mve_audio_stop()
|
||||
{
|
||||
if (!audiobuf_created || mas == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ALint p = 0;
|
||||
|
||||
mve_audio_playing = 0;
|
||||
|
||||
OpenAL_ErrorPrint( alSourceStop(mas->source_id) );
|
||||
OpenAL_ErrorPrint( alGetSourcei(mas->source_id, AL_BUFFERS_PROCESSED, &p) );
|
||||
OpenAL_ErrorPrint( alSourceUnqueueBuffers(mas->source_id, p, mas->audio_buffer) );
|
||||
OpenAL_ErrorPrint( alDeleteSources(1, &mas->source_id) );
|
||||
|
||||
for (int i = 0; i < MVE_AUDIO_BUFFERS; i++) {
|
||||
// make sure that the buffer is real before trying to delete, it could crash for some otherwise
|
||||
if ( (mas->audio_buffer[i] != 0) && alIsBuffer(mas->audio_buffer[i]) ) {
|
||||
OpenAL_ErrorPrint( alDeleteBuffers(1, &mas->audio_buffer[i]) );
|
||||
}
|
||||
}
|
||||
|
||||
if (mas != NULL) {
|
||||
vm_free(mas);
|
||||
mas = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int mve_audio_data(ubyte major, ubyte *data)
|
||||
{
|
||||
static const int selected_chan=1;
|
||||
int chan;
|
||||
int nsamp;
|
||||
|
||||
if (mve_audio_canplay) {
|
||||
chan = mve_get_ushort(data + 2);
|
||||
nsamp = mve_get_ushort(data + 4);
|
||||
|
||||
if (chan & selected_chan) {
|
||||
ALint bprocessed, bqueued, status;
|
||||
ALuint bid;
|
||||
|
||||
OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_PROCESSED, &bprocessed), return 0 );
|
||||
|
||||
while (bprocessed-- > 2) {
|
||||
OpenAL_ErrorPrint( alSourceUnqueueBuffers(mas->source_id, 1, &bid) );
|
||||
}
|
||||
|
||||
OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued), return 0 );
|
||||
|
||||
if (bqueued == 0) {
|
||||
mprintf(("MVE: Buffer underun (First is normal)\n"));
|
||||
}
|
||||
|
||||
OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_SOURCE_STATE, &status), return 0 );
|
||||
|
||||
if ( (mve_audio_playing) && (status != AL_PLAYING) && (bqueued > 0) ) {
|
||||
OpenAL_ErrorCheck( alSourcePlay(mas->source_id), return 0 );
|
||||
}
|
||||
|
||||
if (bqueued < MVE_AUDIO_BUFFERS) {
|
||||
short *buf = NULL;
|
||||
|
||||
/* HACK: +4 mveaudio_uncompress adds 4 more bytes */
|
||||
if (major == 8) {
|
||||
if (mve_audio_compressed) {
|
||||
nsamp += 4;
|
||||
buf = (short *)vm_malloc(nsamp);
|
||||
mveaudio_uncompress(buf, data); /* XXX */
|
||||
} else {
|
||||
nsamp -= 8;
|
||||
data += 8;
|
||||
buf = (short *)vm_malloc(nsamp);
|
||||
memcpy(buf, data, nsamp);
|
||||
}
|
||||
} else {
|
||||
buf = (short *)vm_malloc(nsamp);
|
||||
memset(buf, 0, nsamp); /* XXX */
|
||||
}
|
||||
|
||||
if (!mas->audio_buffer[mve_audio_buffer_tail]) {
|
||||
OpenAL_ErrorCheck( alGenBuffers(1,&mas->audio_buffer[mve_audio_buffer_tail]), { vm_free(buf); return 0; } );
|
||||
}
|
||||
|
||||
OpenAL_ErrorCheck( alBufferData(mas->audio_buffer[mve_audio_buffer_tail], mas->format, buf, nsamp, mas->sample_rate), { vm_free(buf); return 0; } );
|
||||
|
||||
OpenAL_ErrorCheck( alSourceQueueBuffers(mas->source_id, 1, &mas->audio_buffer[mve_audio_buffer_tail]), { vm_free(buf); return 0;} );
|
||||
|
||||
//fprintf(stderr,"Queued buffer %d(%d)\n", mve_audio_buftail, mas->audio_buffer[mve_audio_buftail]);
|
||||
|
||||
if (++mve_audio_buffer_tail == MVE_AUDIO_BUFFERS) {
|
||||
mve_audio_buffer_tail = 0;
|
||||
}
|
||||
|
||||
bqueued++;
|
||||
vm_free(buf);
|
||||
} else {
|
||||
mprintf(("MVE: Buffer overrun: Queue full\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* video handlers
|
||||
*************************/
|
||||
|
||||
int mve_video_createbuf(ubyte minor, ubyte *data)
|
||||
{
|
||||
if (videobuf_created) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
short w, h;
|
||||
short truecolor;
|
||||
w = mve_get_short(data);
|
||||
h = mve_get_short(data+2);
|
||||
|
||||
if (minor > 1) {
|
||||
truecolor = mve_get_short(data+6);
|
||||
} else {
|
||||
truecolor = 0;
|
||||
}
|
||||
|
||||
g_width = w << 3;
|
||||
g_height = h << 3;
|
||||
|
||||
g_vBackBuf1 = g_vBuffers = vm_malloc(g_width * g_height * 4);
|
||||
|
||||
if (g_vBackBuf1 == NULL) {
|
||||
nprintf(("MOVIE", "ERROR: Can't allocate video buffer"));
|
||||
videobuf_created = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (truecolor) {
|
||||
g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
|
||||
} else {
|
||||
g_vBackBuf2 = (ubyte *)g_vBackBuf1 + (g_width * g_height);
|
||||
}
|
||||
memset(g_vBackBuf1, 0, g_width * g_height * 4);
|
||||
|
||||
g_truecolor = truecolor;
|
||||
videobuf_created = 1;
|
||||
|
||||
if (gr_screen.mode == GR_OPENGL) {
|
||||
GLfloat scale_by = 1.0f;
|
||||
|
||||
float screen_ratio = (float)gr_screen.center_w / (float)gr_screen.center_h;
|
||||
float movie_ratio = (float)g_width / (float)g_height;
|
||||
|
||||
if (screen_ratio > movie_ratio) {
|
||||
scale_by = (float)gr_screen.center_h / (float)g_height;
|
||||
} else {
|
||||
scale_by = (float)gr_screen.center_w / (float)g_width;
|
||||
}
|
||||
|
||||
buffer_handle = gr_create_vertex_buffer(true);
|
||||
|
||||
// don't bother setting anything if we aren't going to need it
|
||||
if (!Cmdline_noscalevid && (scale_by != 1.0f)) {
|
||||
vec3d scale;
|
||||
|
||||
scale.xyz.x = scale_by;
|
||||
scale.xyz.y = scale_by;
|
||||
scale.xyz.z = 1.0f;
|
||||
|
||||
gr_push_scale_matrix(&scale);
|
||||
mve_scale_video = 1;
|
||||
}
|
||||
|
||||
if (mve_scale_video) {
|
||||
g_screenX = ((ceil((gr_screen.center_w / scale_by) - 0.5f) - g_width) / 2) + ceil((gr_screen.center_offset_x / scale_by) - 0.5f);
|
||||
g_screenY = ((ceil((gr_screen.center_h / scale_by) - 0.5f) - g_height) / 2) + ceil((gr_screen.center_offset_y / scale_by) - 0.5f);
|
||||
} else {
|
||||
// centers on 1024x768, fills on 640x480
|
||||
g_screenX = ((float)(gr_screen.center_w - g_width) / 2.0f) + gr_screen.center_offset_x;
|
||||
g_screenY = ((float)(gr_screen.center_h - g_height) / 2.0f) + gr_screen.center_offset_y;
|
||||
}
|
||||
|
||||
// set additional values for screen width/height and UV coords
|
||||
if (gr_screen.mode == GR_OPENGL) {
|
||||
gl_screenYH = g_screenY + g_height;
|
||||
gl_screenXW = g_screenX + g_width;
|
||||
}
|
||||
|
||||
glVertices[0][0] = g_screenX;
|
||||
glVertices[0][1] = g_screenY;
|
||||
glVertices[0][2] = 0;
|
||||
glVertices[0][3] = 0;
|
||||
|
||||
glVertices[1][0] = g_screenX;
|
||||
glVertices[1][1] = gl_screenYH;
|
||||
glVertices[1][2] = 0;
|
||||
glVertices[1][3] = 1.f;
|
||||
|
||||
glVertices[2][0] = gl_screenXW;
|
||||
glVertices[2][1] = g_screenY;
|
||||
glVertices[2][2] = 1.f;
|
||||
glVertices[2][3] = 0;
|
||||
|
||||
glVertices[3][0] = gl_screenXW;
|
||||
glVertices[3][1] = gl_screenYH;
|
||||
glVertices[3][2] = 1.f;
|
||||
glVertices[3][3] = 1.f;
|
||||
|
||||
GL_state.Array.BindArrayBuffer(0);
|
||||
|
||||
vertex_layout vertex_def;
|
||||
|
||||
vertex_def.add_vertex_component(vertex_format_data::POSITION2, sizeof(glVertices[0]), 0);
|
||||
vertex_def.add_vertex_component(vertex_format_data::TEX_COORD, sizeof(glVertices[0]), sizeof(GLfloat) * 2);
|
||||
|
||||
gr_update_buffer_data(buffer_handle, sizeof(glVertices[0]) * 4, glVertices);
|
||||
|
||||
opengl_bind_buffer_object(buffer_handle);
|
||||
opengl_bind_vertex_layout(vertex_def);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void mve_convert_and_draw()
|
||||
{
|
||||
ushort *pDests;
|
||||
ushort *pSrcs = NULL;
|
||||
ushort *pixels = (ushort *)g_vBackBuf1;
|
||||
int x, y;
|
||||
|
||||
ubyte *pSrcs8 = NULL;
|
||||
ubyte *pixels8 = (ubyte *)g_vBackBuf1;
|
||||
ubyte r, g, b;
|
||||
ushort bit_16;
|
||||
|
||||
if (g_truecolor) {
|
||||
pSrcs = pixels;
|
||||
} else {
|
||||
pSrcs8 = pixels8;
|
||||
}
|
||||
pDests = pixelbuf;
|
||||
|
||||
if (gr_screen.mode != GR_OPENGL) {
|
||||
if (g_screenWidth > g_width) {
|
||||
pDests += ((g_screenWidth - g_width) / 2) / 2;
|
||||
}
|
||||
if (g_screenHeight > g_height) {
|
||||
pDests += ((g_screenHeight - g_height) / 2) * g_screenWidth;
|
||||
}
|
||||
}
|
||||
|
||||
for (y=0; y<g_height; y++) {
|
||||
for (x = 0; x < g_width; x++) {
|
||||
if (g_truecolor) {
|
||||
Assert( pSrcs != NULL );
|
||||
pDests[x] = (1<<15)|*pSrcs;
|
||||
pSrcs++;
|
||||
} else {
|
||||
Assert( pSrcs8 != NULL );
|
||||
|
||||
// grab rgb color
|
||||
r = (g_palette[(*pSrcs8)*3] / 2);
|
||||
g = (g_palette[(*pSrcs8)*3 + 1] / 2);
|
||||
b = (g_palette[(*pSrcs8)*3 + 2] / 2);
|
||||
|
||||
// stuff the color
|
||||
bit_16 = (ushort)(r << Gr_t_red.shift);
|
||||
bit_16 |= (ushort)(g << Gr_t_green.shift);
|
||||
bit_16 |= (ushort)(b << Gr_t_blue.shift);
|
||||
bit_16 |= (ushort)(Gr_t_alpha.mask);
|
||||
|
||||
// stuff the pixel
|
||||
pDests[x] = (1<<15)|bit_16;
|
||||
|
||||
pSrcs8++;
|
||||
}
|
||||
}
|
||||
|
||||
pDests += g_screenWidth;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void mve_video_display()
|
||||
{
|
||||
fix t1 = timer_get_fixed_seconds();
|
||||
|
||||
mve_convert_and_draw();
|
||||
|
||||
// this thing is basically stupid but I don't care
|
||||
// micro_frame_delay is divided by 10 to match mve_video_skiptimer overflow catch
|
||||
if ( mve_video_skiptimer > (uint)(micro_frame_delay/10) ) {
|
||||
// we are running slow so subtract desired time from actual and skip this frame
|
||||
mve_video_skiptimer -= (micro_frame_delay/10);
|
||||
return;
|
||||
} else {
|
||||
// zero out so we can get a new count
|
||||
mve_video_skiptimer = 0;
|
||||
}
|
||||
|
||||
if (gr_screen.mode == GR_OPENGL) {
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, g_width, g_height, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
|
||||
|
||||
opengl_shader_set_passthrough(true);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
} else {
|
||||
// DDOI - This is probably really fricking slow
|
||||
int bitmap = bm_create (16, g_screenWidth, g_screenHeight, pixelbuf, 0);
|
||||
gr_set_bitmap (bitmap);
|
||||
gr_bitmap ((int)g_screenX, (int)g_screenY, GR_RESIZE_FULL);
|
||||
bm_release (bitmap);
|
||||
}
|
||||
|
||||
gr_flip();
|
||||
os_poll();
|
||||
|
||||
fix t2 = timer_get_fixed_seconds();
|
||||
|
||||
// only get a new count if we are definitely through with old count
|
||||
if ( mve_video_skiptimer == 0 ) {
|
||||
// for a more accurate count convert the frame rate to a float and multiply
|
||||
// by one-hundred-thousand before converting to an uint.
|
||||
mve_video_skiptimer = (uint)(f2fl(t2-t1) * 100000);
|
||||
}
|
||||
|
||||
int k = key_inkey();
|
||||
switch (k) {
|
||||
case KEY_ESC:
|
||||
case KEY_ENTER:
|
||||
case KEY_SPACEBAR:
|
||||
mve_playing = 0;
|
||||
}
|
||||
|
||||
//mprintf(("mve frame took this long: %.6f\n", f2fl(t2-t1) / 1000.0f));
|
||||
}
|
||||
|
||||
int mve_video_init(ubyte *data)
|
||||
{
|
||||
if (video_inited) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
short width, height;
|
||||
|
||||
width = mve_get_short(data);
|
||||
height = mve_get_short(data+2);
|
||||
|
||||
g_screenWidth = width;
|
||||
g_screenHeight = height;
|
||||
|
||||
// DDOI - Allocate RGB565 pixel buffer
|
||||
pixelbuf = (ushort *)vm_malloc(g_screenWidth * g_screenHeight * 2);
|
||||
|
||||
if (pixelbuf == NULL) {
|
||||
nprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
|
||||
video_inited = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(pixelbuf, 0, g_screenWidth * g_screenHeight * 2);
|
||||
|
||||
if (gr_screen.mode == GR_OPENGL) {
|
||||
opengl_set_texture_target(GL_TEXTURE_2D);
|
||||
|
||||
glGenTextures(1, &GLtex);
|
||||
|
||||
Assert( GLtex != 0 );
|
||||
|
||||
if ( GLtex == 0 ) {
|
||||
nprintf(("MOVIE", "ERROR: Can't create a GL texture"));
|
||||
video_inited = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
gr_set_lighting(false, false);
|
||||
GL_state.Texture.DisableAll();
|
||||
|
||||
GL_state.Texture.SetActiveUnit(0);
|
||||
GL_state.Texture.SetTarget(GL_texture_target);
|
||||
GL_state.Texture.Enable(GLtex);
|
||||
|
||||
GL_state.SetTextureSource(TEXTURE_SOURCE_DECAL);
|
||||
GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE);
|
||||
GL_state.SetZbufferType(ZBUFFER_TYPE_NONE);
|
||||
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
|
||||
glTexImage2D(GL_state.Texture.GetTarget(), 0, GL_RGB5_A1, g_screenWidth, g_screenHeight, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
|
||||
|
||||
// set our color so that we can make sure that it's correct
|
||||
if ( !is_minimum_GLSL_version() ) {
|
||||
GL_state.Color(255, 255, 255, 255);
|
||||
}
|
||||
}
|
||||
|
||||
memset(g_palette, 0, 768);
|
||||
video_inited = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void mve_video_palette(ubyte *data)
|
||||
{
|
||||
short start, count;
|
||||
start = mve_get_short(data);
|
||||
count = mve_get_short(data+2);
|
||||
memcpy(g_palette + 3*start, data+4, 3*count);
|
||||
}
|
||||
|
||||
void mve_video_codemap(ubyte *data, int len)
|
||||
{
|
||||
g_pCurMap = data;
|
||||
g_nMapLength = len;
|
||||
}
|
||||
|
||||
void mve_video_data(ubyte *data, int len)
|
||||
{
|
||||
ushort nFlags;
|
||||
ubyte *temp;
|
||||
|
||||
nFlags = mve_get_ushort(data+12);
|
||||
|
||||
if (nFlags & 1) {
|
||||
temp = (ubyte *)g_vBackBuf1;
|
||||
g_vBackBuf1 = g_vBackBuf2;
|
||||
g_vBackBuf2 = temp;
|
||||
}
|
||||
|
||||
if (g_truecolor) {
|
||||
decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
|
||||
} else {
|
||||
decodeFrame8((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
|
||||
}
|
||||
}
|
||||
|
||||
void mve_end_chunk()
|
||||
{
|
||||
g_pCurMap = NULL;
|
||||
}
|
||||
|
||||
void mve_init(MVESTREAM *mve)
|
||||
{
|
||||
// reset to default values
|
||||
mve_audio_buffer_tail = 0;
|
||||
mve_audio_playing = 0;
|
||||
mve_audio_canplay = 0;
|
||||
mve_audio_compressed = 0;
|
||||
audiobuf_created = 0;
|
||||
|
||||
videobuf_created = 0;
|
||||
video_inited = 0;
|
||||
mve_scale_video = 0;
|
||||
|
||||
mve_playing = 1;
|
||||
}
|
||||
|
||||
void mve_play(MVESTREAM *mve)
|
||||
{
|
||||
int init_timer = 0, timer_error = 0;
|
||||
int cont = 1;
|
||||
|
||||
if (!timer_started) {
|
||||
mve_timer_start();
|
||||
}
|
||||
|
||||
while (cont && mve_playing && !timer_error) {
|
||||
cont = mve_play_next_chunk(mve);
|
||||
|
||||
if (micro_frame_delay && !init_timer) {
|
||||
mve_timer_start();
|
||||
init_timer = 1;
|
||||
}
|
||||
|
||||
timer_error = mve_do_timer_wait();
|
||||
}
|
||||
}
|
||||
|
||||
void mve_shutdown()
|
||||
{
|
||||
if (gr_screen.mode == GR_OPENGL) {
|
||||
if (mve_scale_video) {
|
||||
gr_pop_scale_matrix();
|
||||
}
|
||||
|
||||
gr_delete_buffer(buffer_handle);
|
||||
buffer_handle = -1;
|
||||
|
||||
GL_state.Texture.Disable();
|
||||
GL_state.Texture.Delete(GLtex);
|
||||
glDeleteTextures(1, &GLtex);
|
||||
GLtex = 0;
|
||||
}
|
||||
|
||||
if (pixelbuf != NULL) {
|
||||
vm_free(pixelbuf);
|
||||
pixelbuf = NULL;
|
||||
}
|
||||
|
||||
if (g_vBuffers != NULL) {
|
||||
vm_free(g_vBuffers);
|
||||
g_vBuffers = NULL;
|
||||
}
|
||||
|
||||
mve_audio_stop();
|
||||
mve_timer_stop();
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,40 +0,0 @@
|
||||
|
||||
|
||||
#ifndef _OGGPLAYER_H
|
||||
#define _OGGPLAYER_H
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include "theora/theora.h"
|
||||
#include "vorbis/codec.h"
|
||||
|
||||
struct CFILE;
|
||||
|
||||
// structure for maintaining info on a THEORAFILE stream
|
||||
typedef struct THEORAFILE
|
||||
{
|
||||
CFILE *cfp;
|
||||
ubyte theora_p;
|
||||
ubyte vorbis_p;
|
||||
|
||||
ogg_sync_state osyncstate;
|
||||
ogg_page opage;
|
||||
ogg_packet opacket;
|
||||
ogg_stream_state v_osstate;
|
||||
ogg_stream_state t_osstate;
|
||||
|
||||
theora_info tinfo;
|
||||
theora_comment tcomment;
|
||||
theora_state tstate;
|
||||
|
||||
vorbis_info vinfo;
|
||||
vorbis_dsp_state vstate;
|
||||
vorbis_block vblock;
|
||||
vorbis_comment vcomment;
|
||||
} THEORAFILE;
|
||||
|
||||
void theora_play(THEORAFILE *movie);
|
||||
THEORAFILE *theora_open(char *filename);
|
||||
void theora_close(THEORAFILE *movie);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,469 @@
|
||||
#include "cutscene/player.h"
|
||||
|
||||
#include "cutscene/Decoder.h"
|
||||
#include "cutscene/player/VideoPresenter.h"
|
||||
#include "cutscene/ffmpeg/FFMPEGDecoder.h"
|
||||
|
||||
#include "graphics/2d.h"
|
||||
|
||||
#include "globalincs/alphacolors.h"
|
||||
|
||||
#include "osapi/osapi.h"
|
||||
|
||||
#include "sound/openal.h"
|
||||
|
||||
#include "tracing/tracing.h"
|
||||
|
||||
#include "io/key.h"
|
||||
#include "io/timer.h"
|
||||
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#include "cutscene/player/OpenGLVideoPresenter.h"
|
||||
|
||||
using namespace cutscene::player;
|
||||
|
||||
namespace cutscene {
|
||||
struct PlayerState {
|
||||
MovieProperties props;
|
||||
Decoder* decoder = nullptr;
|
||||
|
||||
bool playing = true;
|
||||
|
||||
bool playbackHasBegun = false;
|
||||
|
||||
// Timing state
|
||||
std::uint64_t playback_time = 0;
|
||||
|
||||
// Audio state
|
||||
bool audioInited = false;
|
||||
bool hasAudio = false;
|
||||
ALuint audioSid = 0;
|
||||
SCP_vector<ALuint> audioBuffers;
|
||||
SCP_queue<ALuint> unqueuedAudioBuffers;
|
||||
|
||||
// Graphics state following
|
||||
bool videoInited = false;
|
||||
|
||||
VideoFramePtr currentFrame;
|
||||
VideoFramePtr nextFrame;
|
||||
bool newFrameAdded = false;
|
||||
|
||||
std::unique_ptr<VideoPresenter> videoPresenter;
|
||||
|
||||
PlayerState() : decoder(nullptr), playing(true), playbackHasBegun(false),
|
||||
playback_time(0),
|
||||
audioInited(0), hasAudio(false), audioSid(0),
|
||||
videoInited(false), newFrameAdded(false) {}
|
||||
|
||||
private:
|
||||
PlayerState(const PlayerState&) SCP_DELETED_FUNCTION;
|
||||
|
||||
PlayerState& operator=(const PlayerState&) SCP_DELETED_FUNCTION;
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
using namespace cutscene;
|
||||
|
||||
const int MAX_AUDIO_BUFFERS = 15;
|
||||
|
||||
Decoder* findDecoder(const SCP_string& name) {
|
||||
{
|
||||
auto ffmpeg = new ffmpeg::FFMPEGDecoder();
|
||||
if (ffmpeg->initialize(name)) {
|
||||
return ffmpeg;
|
||||
}
|
||||
delete ffmpeg;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// get relative time since beginning playback, compensating for A/V drift
|
||||
double playbackGetTime(PlayerState* state) {
|
||||
return state->playback_time * 0.000001;
|
||||
}
|
||||
|
||||
void videoPlaybackInit(PlayerState* state) {
|
||||
if (state->videoInited) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(state != NULL);
|
||||
|
||||
if (gr_screen.mode == GR_OPENGL) {
|
||||
state->videoPresenter.reset(new OpenGLVideoPresenter(state->props));
|
||||
}
|
||||
|
||||
state->videoInited = true;
|
||||
}
|
||||
|
||||
void audioPlaybackInit(PlayerState* state) {
|
||||
if (state->audioInited) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state->decoder->hasAudio() || Cmdline_freespace_no_sound) {
|
||||
state->hasAudio = false;
|
||||
return;
|
||||
}
|
||||
state->hasAudio = true;
|
||||
|
||||
OpenAL_ErrorCheck(alGenSources(1, &state->audioSid), return);
|
||||
OpenAL_ErrorPrint(alSourcef(state->audioSid, AL_GAIN, 1.0f));
|
||||
|
||||
state->audioBuffers.clear();
|
||||
state->audioBuffers.resize(MAX_AUDIO_BUFFERS, 0);
|
||||
|
||||
OpenAL_ErrorCheck(alGenBuffers(static_cast<ALsizei>(state->audioBuffers.size()), state->audioBuffers.data()),
|
||||
return);
|
||||
|
||||
for (auto buffer : state->audioBuffers) {
|
||||
state->unqueuedAudioBuffers.push(buffer);
|
||||
}
|
||||
|
||||
state->audioInited = true;
|
||||
}
|
||||
|
||||
void processVideoData(PlayerState* state) {
|
||||
TRACE_SCOPE(tracing::CutsceneProcessVideoData);
|
||||
|
||||
state->newFrameAdded = false;
|
||||
|
||||
if (!state->decoder->isVideoFrameAvailable()) {
|
||||
// Nothing to do here...
|
||||
return;
|
||||
}
|
||||
|
||||
if (state->currentFrame == nullptr && state->nextFrame == nullptr) {
|
||||
// Load the initial frame
|
||||
VideoFramePtr firstFrame;
|
||||
auto r = state->decoder->tryPopVideoFrame(firstFrame);
|
||||
|
||||
// This shouldn't happen...
|
||||
Assertion(r, "Failed to pop frame!");
|
||||
|
||||
// At this point the new frame is the next frame
|
||||
state->nextFrame = std::move(firstFrame);
|
||||
}
|
||||
|
||||
if (!state->nextFrame) {
|
||||
// No next frame, try getting a new one
|
||||
|
||||
if (!state->decoder->tryPopVideoFrame(state->nextFrame)) {
|
||||
// No new frame available :(
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto currentTime = playbackGetTime(state);
|
||||
if (currentTime < state->nextFrame->frameTime) {
|
||||
// Old frame is still valid, nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
while(currentTime >= state->nextFrame->frameTime) {
|
||||
// Move the next frame to the current frame slot
|
||||
state->currentFrame = std::move(state->nextFrame);
|
||||
|
||||
// Get a new frame from the decoder
|
||||
auto success = state->decoder->tryPopVideoFrame(state->nextFrame);
|
||||
if (!success) {
|
||||
// Make sure the pointer is actually empty
|
||||
state->nextFrame = nullptr;
|
||||
// No more frames available
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now upload the new frame
|
||||
if (state->videoPresenter) {
|
||||
state->videoPresenter->uploadVideoFrame(state->currentFrame);
|
||||
state->newFrameAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool processAudioData(PlayerState* state) {
|
||||
TRACE_SCOPE(tracing::CutsceneProcessAudioData);
|
||||
|
||||
if (!state->hasAudio) {
|
||||
if (state->decoder->hasAudio()) {
|
||||
// Even if we don't play the sound we still need to remove it from the queue
|
||||
AudioFramePtr audioData;
|
||||
while(state->decoder->tryPopAudioData(audioData)) {
|
||||
// Intentionally left empty
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ALint processed = 0;
|
||||
OpenAL_ErrorCheck(alGetSourcei(state->audioSid, AL_BUFFERS_PROCESSED, &processed), return false);
|
||||
|
||||
// First check for free buffers and push them int the audio queue
|
||||
for (int i = 0; i < processed; ++i) {
|
||||
ALuint buffer;
|
||||
OpenAL_ErrorPrint(alSourceUnqueueBuffers(state->audioSid, 1, &buffer));
|
||||
state->unqueuedAudioBuffers.push(buffer);
|
||||
}
|
||||
|
||||
AudioFramePtr audioData;
|
||||
|
||||
while (!state->unqueuedAudioBuffers.empty() && state->decoder->tryPopAudioData(audioData)) {
|
||||
auto buffer = state->unqueuedAudioBuffers.front();
|
||||
state->unqueuedAudioBuffers.pop();
|
||||
|
||||
ALenum format = (audioData->channels == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
|
||||
|
||||
OpenAL_ErrorCheck(alBufferData(buffer, format, audioData->audioData.data(),
|
||||
static_cast<ALsizei>(audioData->audioData.size() * sizeof(short)),
|
||||
static_cast<ALsizei>(audioData->rate)), return false);
|
||||
|
||||
OpenAL_ErrorCheck(alSourceQueueBuffers(state->audioSid, 1, &buffer), return false);
|
||||
}
|
||||
|
||||
ALint status = 0, queued = 0;
|
||||
OpenAL_ErrorCheck(alGetSourcei(state->audioSid, AL_SOURCE_STATE, &status), return false);
|
||||
|
||||
OpenAL_ErrorCheck(alGetSourcei(state->audioSid, AL_BUFFERS_QUEUED, &queued), return false);
|
||||
|
||||
if ((status != AL_PLAYING) && (queued > 0)) {
|
||||
OpenAL_ErrorPrint(alSourcePlay(state->audioSid));
|
||||
}
|
||||
|
||||
// Get status again in cause we just started playback
|
||||
OpenAL_ErrorCheck(alGetSourcei(state->audioSid, AL_SOURCE_STATE, &status), return false);
|
||||
return status == AL_PLAYING;
|
||||
}
|
||||
|
||||
void audioPlaybackClose(PlayerState* state) {
|
||||
if (!state->audioInited) {
|
||||
return;
|
||||
}
|
||||
|
||||
ALint p = 0;
|
||||
|
||||
OpenAL_ErrorPrint(alSourceStop(state->audioSid));
|
||||
OpenAL_ErrorPrint(alGetSourcei(state->audioSid, AL_BUFFERS_PROCESSED, &p));
|
||||
OpenAL_ErrorPrint(alSourceUnqueueBuffers(state->audioSid, p, state->audioBuffers.data()));
|
||||
OpenAL_ErrorPrint(alDeleteSources(1, &state->audioSid));
|
||||
|
||||
for (auto buffer : state->audioBuffers) {
|
||||
// make sure that the buffer is real before trying to delete, it could crash for some otherwise
|
||||
if ((buffer != 0) && alIsBuffer(buffer)) {
|
||||
OpenAL_ErrorPrint(alDeleteBuffers(1, &buffer));
|
||||
}
|
||||
}
|
||||
|
||||
state->audioInited = false;
|
||||
}
|
||||
|
||||
void videoPlaybackClose(PlayerState* state) {
|
||||
if (!state->videoInited) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->videoPresenter.reset();
|
||||
|
||||
state->videoInited = false;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
float print_string(float x, float y, const char* fmt, Args... params) {
|
||||
SCP_string text;
|
||||
sprintf(text, fmt, params...);
|
||||
|
||||
gr_string(x, y, text.c_str(), GR_RESIZE_NONE);
|
||||
|
||||
return y + font::get_current_font()->getHeight();
|
||||
}
|
||||
|
||||
void showVideoInfo(PlayerState* state) {
|
||||
gr_set_color_fast(&Color_white);
|
||||
|
||||
float y = 200.f;
|
||||
float x = 100.f;
|
||||
y = print_string(x, y, "Movie FPS: %f", state->props.fps);
|
||||
y = print_string(x, y, "Size: %dx%d", state->props.size.width, state->props.size.height);
|
||||
|
||||
y = gr_screen.max_h - 200.f;
|
||||
|
||||
size_t audio_queue_size = state->decoder->getAudioQueueSize();
|
||||
if (state->hasAudio) {
|
||||
ALint queued;
|
||||
OpenAL_ErrorPrint(alGetSourcei(state->audioSid, AL_BUFFERS_QUEUED, &queued));
|
||||
audio_queue_size += queued;
|
||||
}
|
||||
|
||||
y = print_string(x, y, "Audio Queue size: " SIZE_T_ARG, audio_queue_size);
|
||||
y = print_string(x, y, "Video Queue size: " SIZE_T_ARG, state->decoder->getVideoQueueSize());
|
||||
y += font::get_current_font()->getHeight();
|
||||
// Estimate the size of the video buffer
|
||||
// We use YUV420p frames so one pixel uses 1.5 bytes of storage
|
||||
size_t single_frame_size = (size_t) (state->props.size.width * state->props.size.height * 1.5);
|
||||
size_t total_size = single_frame_size * state->decoder->getVideoQueueSize();
|
||||
print_string(x, y, "Video buffer size: " SIZE_T_ARG "B", total_size);
|
||||
}
|
||||
|
||||
void displayVideo(PlayerState* state) {
|
||||
if (!state->currentFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE_SCOPE(tracing::CutsceneDrawVideoFrame);
|
||||
|
||||
gr_clear();
|
||||
if (state->videoPresenter) {
|
||||
state->videoPresenter->displayFrame();
|
||||
}
|
||||
|
||||
if (Cmdline_show_video_info) {
|
||||
showVideoInfo(state);
|
||||
}
|
||||
|
||||
gr_flip();
|
||||
}
|
||||
|
||||
void processEvents(PlayerState* state) {
|
||||
io::mouse::CursorManager::get()->showCursor(false);
|
||||
|
||||
os_poll();
|
||||
|
||||
int k = key_inkey();
|
||||
switch (k) {
|
||||
case KEY_ESC:
|
||||
case KEY_ENTER:
|
||||
case KEY_SPACEBAR:
|
||||
state->playing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldBeginPlayback(Decoder* decoder) {
|
||||
auto video = decoder->isVideoQueueFull();
|
||||
auto audio = decoder->isAudioQueueFull();
|
||||
|
||||
// Wait until one of the queues is full, that should make sure
|
||||
// that we have enough frames at the beginning of playback and that
|
||||
// we don't wait indefinitely if the cutscene has a weird amount of audio
|
||||
// or video at the beginning
|
||||
// Also only wait while the decoder is still working. Otherwise we will wait indefinitely if the video
|
||||
// is very short.
|
||||
return (audio || video) || !decoder->isDecoding();
|
||||
}
|
||||
}
|
||||
|
||||
namespace cutscene {
|
||||
Player::Player() {
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
if (m_decoder) {
|
||||
m_decoder->close();
|
||||
}
|
||||
}
|
||||
|
||||
void Player::processDecoderData(PlayerState* state) {
|
||||
if (!state->playbackHasBegun) {
|
||||
// Wait until video and audio are available
|
||||
// If we don't have audio, don't wait for it (obviously...)
|
||||
if (!shouldBeginPlayback(state->decoder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->playbackHasBegun = true;
|
||||
}
|
||||
|
||||
TRACE_SCOPE(tracing::CutsceneProcessDecoder);
|
||||
|
||||
processVideoData(state);
|
||||
|
||||
auto audioPlaying = processAudioData(state);
|
||||
|
||||
// Set the playing flag if the decoder is still active or there is still data available
|
||||
auto decoding = m_decoder->isDecoding();
|
||||
|
||||
// Audio is pending if there is data left in the queue or of OpenAL is still playing audio
|
||||
auto pendingAudio = (m_decoder->isAudioFrameAvailable() && m_decoder->hasAudio()) || audioPlaying;
|
||||
auto pendingVideo = m_decoder->isVideoFrameAvailable();
|
||||
|
||||
state->playing = decoding || pendingAudio || pendingVideo;
|
||||
}
|
||||
|
||||
void Player::startPlayback() {
|
||||
m_decoderThread.reset(new std::thread(std::bind(&Player::decoderThread, this)));
|
||||
|
||||
PlayerState state;
|
||||
state.props = m_decoder->getProperties();
|
||||
state.decoder = m_decoder.get();
|
||||
|
||||
// Compute the maximum time we will sleep to make sure we can still maintain the movie FPS
|
||||
// and not waste too much CPU time
|
||||
// We will sleep at most half the time a frame would be displayed
|
||||
auto sleepTime = static_cast<std::uint64_t>((1. / (4. * state.props.fps)) * 1000.);
|
||||
|
||||
videoPlaybackInit(&state);
|
||||
|
||||
audioPlaybackInit(&state);
|
||||
|
||||
auto lastDisplayTimestamp = timer_get_microseconds();
|
||||
while (state.playing) {
|
||||
TRACE_SCOPE(tracing::CutsceneStep);
|
||||
|
||||
processDecoderData(&state);
|
||||
|
||||
displayVideo(&state);
|
||||
|
||||
processEvents(&state);
|
||||
|
||||
auto timestamp = timer_get_microseconds();
|
||||
|
||||
auto passed = timestamp - lastDisplayTimestamp;
|
||||
lastDisplayTimestamp = timestamp;
|
||||
|
||||
if (state.playbackHasBegun) {
|
||||
state.playback_time += passed;
|
||||
}
|
||||
|
||||
if (passed < sleepTime) {
|
||||
auto sleep = sleepTime - passed;
|
||||
|
||||
os_sleep(static_cast<uint>(sleep));
|
||||
}
|
||||
}
|
||||
m_decoder->stopDecoder();
|
||||
|
||||
audioPlaybackClose(&state);
|
||||
videoPlaybackClose(&state);
|
||||
|
||||
m_decoderThread->join();
|
||||
}
|
||||
|
||||
void Player::decoderThread() {
|
||||
try {
|
||||
m_decoder->startDecoding();
|
||||
} catch (const std::exception& e) {
|
||||
mprintf(("Video: An exception was thrown while decoding the video: %s\n", e.what()));
|
||||
} catch (...) {
|
||||
mprintf(("Video: An exception was thrown while decoding the video!\n"));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Player> Player::newPlayer(const SCP_string& name) {
|
||||
mprintf(("Creating player for movie '%s'.\n", name.c_str()));
|
||||
|
||||
auto decoder = findDecoder(name);
|
||||
|
||||
if (decoder == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Player> player(new Player());
|
||||
player->m_decoder.reset(decoder);
|
||||
|
||||
return player;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
namespace cutscene {
|
||||
class Decoder;
|
||||
|
||||
struct PlayerState;
|
||||
|
||||
/**
|
||||
* @brief A movie player
|
||||
*/
|
||||
class Player {
|
||||
private:
|
||||
std::unique_ptr<Decoder> m_decoder;
|
||||
|
||||
std::unique_ptr<std::thread> m_decoderThread;
|
||||
|
||||
void processDecoderData(PlayerState* state);
|
||||
|
||||
private:
|
||||
Player(const Player&) = delete;
|
||||
|
||||
void decoderThread();
|
||||
|
||||
public:
|
||||
Player();
|
||||
|
||||
~Player();
|
||||
|
||||
/**
|
||||
* @brief Begin playing the previously selected movie
|
||||
*/
|
||||
void startPlayback();
|
||||
|
||||
/**
|
||||
* @brief Creates a player
|
||||
* The player is configured to play the movie with the specified name
|
||||
* @param name The movie to play
|
||||
* @return @c nullptr if the specified movie could not be opened, a valid Player pointer otherwise
|
||||
*/
|
||||
static std::unique_ptr<Player> newPlayer(const SCP_string& name);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
|
||||
#include "OpenGLVideoPresenter.h"
|
||||
|
||||
#include "graphics/opengl/gropenglstate.h"
|
||||
#include "graphics/opengl/gropengltnl.h"
|
||||
#include "graphics/opengl/gropengldraw.h"
|
||||
|
||||
|
||||
using namespace cutscene;
|
||||
using namespace cutscene::player;
|
||||
|
||||
namespace cutscene {
|
||||
namespace player {
|
||||
OpenGLVideoPresenter::OpenGLVideoPresenter(const MovieProperties& props) : _scaleVideo(false),
|
||||
_ytex(0), _utex(0), _vtex(0) {
|
||||
GR_DEBUG_SCOPE("Init video");
|
||||
|
||||
opengl_set_texture_target(GL_TEXTURE_2D);
|
||||
|
||||
_vertexBuffer = gr_create_vertex_buffer(true);
|
||||
|
||||
auto w = static_cast<int>(props.size.width);
|
||||
auto h = static_cast<int>(props.size.height);
|
||||
|
||||
_sdr_handle = gr_maybe_create_shader(SDR_TYPE_VIDEO_PROCESS, 0);
|
||||
|
||||
glGenTextures(1, &_ytex);
|
||||
glGenTextures(1, &_utex);
|
||||
glGenTextures(1, &_vtex);
|
||||
|
||||
if (_ytex + _utex + _vtex == 0) {
|
||||
throw std::runtime_error("Can't create a GL texture");
|
||||
}
|
||||
opengl_shader_set_current(_sdr_handle);
|
||||
|
||||
gr_set_lighting(false, false);
|
||||
|
||||
GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE);
|
||||
GL_state.SetZbufferType(ZBUFFER_TYPE_NONE);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(0);
|
||||
GL_state.Texture.SetTarget(GL_texture_target);
|
||||
GL_state.Texture.Enable(_ytex);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
|
||||
glTexImage2D(GL_state.Texture.GetTarget(), 0, GL_RED, w, h, 0, GL_RED,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(1);
|
||||
GL_state.Texture.SetTarget(GL_texture_target);
|
||||
GL_state.Texture.Enable(_utex);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
|
||||
glTexImage2D(GL_state.Texture.GetTarget(), 0, GL_RED, w / 2, h / 2, 0, GL_RED,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(2);
|
||||
GL_state.Texture.SetTarget(GL_texture_target);
|
||||
GL_state.Texture.Enable(_vtex);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf
|
||||
glTexImage2D(GL_state.Texture.GetTarget(), 0, GL_RED, w / 2, h / 2, 0, GL_RED,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
float screen_ratio = (float) gr_screen.center_w / (float) gr_screen.center_h;
|
||||
float movie_ratio = (float) props.size.width / (float) props.size.height;
|
||||
|
||||
float scale_by;
|
||||
if (screen_ratio > movie_ratio) {
|
||||
scale_by = (float) gr_screen.center_h / (float) props.size.height;
|
||||
} else {
|
||||
scale_by = (float) gr_screen.center_w / (float) props.size.width;
|
||||
}
|
||||
|
||||
// don't bother setting anything if we aren't going to need it
|
||||
if (!Cmdline_noscalevid && (scale_by != 1.0f)) {
|
||||
vec3d scale;
|
||||
|
||||
scale.xyz.x = scale_by;
|
||||
scale.xyz.y = scale_by;
|
||||
scale.xyz.z = -1.0f;
|
||||
|
||||
gr_push_scale_matrix(&scale);
|
||||
_scaleVideo = true;
|
||||
}
|
||||
|
||||
int screenX;
|
||||
int screenY;
|
||||
|
||||
if (_scaleVideo) {
|
||||
screenX = fl2i(((gr_screen.center_w / 2.0f + gr_screen.center_offset_x) / scale_by) - (static_cast<int>(props.size.width) / 2.0f) + 0.5f);
|
||||
screenY = fl2i(((gr_screen.center_h / 2.0f + gr_screen.center_offset_y) / scale_by) - (static_cast<int>(props.size.height) / 2.0f) + 0.5f);
|
||||
} else {
|
||||
// centers on 1024x768, fills on 640x480
|
||||
screenX = ((gr_screen.center_w - static_cast<int>(props.size.width)) / 2) + gr_screen.center_offset_x;
|
||||
screenY = ((gr_screen.center_h - static_cast<int>(props.size.height)) / 2) + gr_screen.center_offset_y;
|
||||
}
|
||||
|
||||
// set additional values for screen width/height and UV coords
|
||||
int screenXW = screenX + static_cast<int>(props.size.width);
|
||||
int screenYH = screenY + static_cast<int>(props.size.height);
|
||||
|
||||
Current_shader->program->Uniforms.setUniformi("ytex", 0);
|
||||
Current_shader->program->Uniforms.setUniformi("utex", 1);
|
||||
Current_shader->program->Uniforms.setUniformi("vtex", 2);
|
||||
|
||||
Current_shader->program->Uniforms.setUniformMatrix4f("projMatrix", GL_projection_matrix);
|
||||
Current_shader->program->Uniforms.setUniformMatrix4f("modelViewMatrix", GL_model_view_matrix);
|
||||
|
||||
GLfloat glVertices[4][4] = {{0}};
|
||||
glVertices[0][0] = (GLfloat)screenX;
|
||||
glVertices[0][1] = (GLfloat)screenY;
|
||||
glVertices[0][2] = 0.0f;
|
||||
glVertices[0][3] = 0.0f;
|
||||
|
||||
glVertices[1][0] = (GLfloat)screenX;
|
||||
glVertices[1][1] = (GLfloat)screenYH;
|
||||
glVertices[1][2] = 0.0f;
|
||||
glVertices[1][3] = 1.0f;
|
||||
|
||||
glVertices[2][0] = (GLfloat)screenXW;
|
||||
glVertices[2][1] = (GLfloat)screenY;
|
||||
glVertices[2][2] = 1.0f;
|
||||
glVertices[2][3] = 0.0f;
|
||||
|
||||
glVertices[3][0] = (GLfloat)screenXW;
|
||||
glVertices[3][1] = (GLfloat)screenYH;
|
||||
glVertices[3][2] = 1.0f;
|
||||
glVertices[3][3] = 1.0f;
|
||||
|
||||
_vertexLayout.add_vertex_component(vertex_format_data::POSITION2, sizeof(glVertices[0]), 0);
|
||||
_vertexLayout.add_vertex_component(vertex_format_data::TEX_COORD, sizeof(glVertices[0]), sizeof(GLfloat) * 2);
|
||||
|
||||
gr_update_buffer_data(_vertexBuffer, sizeof(glVertices[0]) * 4, glVertices);
|
||||
}
|
||||
|
||||
OpenGLVideoPresenter::~OpenGLVideoPresenter() {
|
||||
GR_DEBUG_SCOPE("Deinit video");
|
||||
|
||||
if (_scaleVideo) {
|
||||
gr_pop_scale_matrix();
|
||||
}
|
||||
|
||||
gr_delete_buffer(_vertexBuffer);
|
||||
_vertexBuffer = -1;
|
||||
|
||||
GL_state.Texture.Delete(_ytex);
|
||||
GL_state.Texture.Delete(_utex);
|
||||
GL_state.Texture.Delete(_vtex);
|
||||
glDeleteTextures(1, &_ytex);
|
||||
glDeleteTextures(1, &_utex);
|
||||
glDeleteTextures(1, &_vtex);
|
||||
|
||||
opengl_set_texture_target();
|
||||
|
||||
_ytex = _utex = _vtex = 0;
|
||||
opengl_shader_set_current();
|
||||
}
|
||||
|
||||
void OpenGLVideoPresenter::uploadVideoFrame(const VideoFramePtr& frame) {
|
||||
GR_DEBUG_SCOPE("Update video frame");
|
||||
auto ptrs = frame->getDataPointers();
|
||||
|
||||
auto stride = static_cast<GLint>(frame->ySize.stride);
|
||||
auto width = static_cast<GLint>(frame->ySize.width);
|
||||
auto height = static_cast<GLint>(frame->ySize.height);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(0);
|
||||
GL_state.Texture.Enable(_ytex);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
|
||||
glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE,
|
||||
ptrs.y);
|
||||
|
||||
stride = static_cast<int>(frame->uvSize.stride);
|
||||
width = static_cast<int>(frame->uvSize.width);
|
||||
height = static_cast<int>(frame->uvSize.height);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(1);
|
||||
GL_state.Texture.Enable(_utex);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
|
||||
glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE,
|
||||
ptrs.u);
|
||||
|
||||
GL_state.Texture.SetActiveUnit(2);
|
||||
GL_state.Texture.Enable(_vtex);
|
||||
glTexSubImage2D(GL_state.Texture.GetTarget(), 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE,
|
||||
ptrs.v);
|
||||
|
||||
// Reset this back to default
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
void OpenGLVideoPresenter::displayFrame() {
|
||||
opengl_shader_set_current(_sdr_handle);
|
||||
|
||||
opengl_bind_buffer_object(_vertexBuffer);
|
||||
opengl_bind_vertex_layout(_vertexLayout);
|
||||
|
||||
// Bind textures
|
||||
GL_state.Texture.SetActiveUnit(0);
|
||||
GL_state.Texture.Enable(_ytex);
|
||||
GL_state.Texture.SetActiveUnit(1);
|
||||
GL_state.Texture.Enable(_utex);
|
||||
GL_state.Texture.SetActiveUnit(2);
|
||||
GL_state.Texture.Enable(_vtex);
|
||||
|
||||
GR_DEBUG_SCOPE("Draw video frame");
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
#ifndef FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
#define FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "cutscene/player/VideoPresenter.h"
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <memory>
|
||||
#include <graphics/2d.h>
|
||||
|
||||
namespace cutscene {
|
||||
namespace player {
|
||||
class OpenGLVideoPresenter: public VideoPresenter {
|
||||
int _vertexBuffer = -1;
|
||||
vertex_layout _vertexLayout;
|
||||
int _sdr_handle = -1;
|
||||
|
||||
bool _scaleVideo = false;
|
||||
|
||||
GLuint _ytex = 0;
|
||||
GLuint _utex = 0;
|
||||
GLuint _vtex = 0;
|
||||
public:
|
||||
OpenGLVideoPresenter(const MovieProperties& props);
|
||||
|
||||
virtual ~OpenGLVideoPresenter();
|
||||
|
||||
virtual void uploadVideoFrame(const VideoFramePtr& frame) SCP_OVERRIDE;
|
||||
|
||||
virtual void displayFrame() SCP_OVERRIDE;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //FS2_OPEN_OPENGLVIDEOPRESENTER_H
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
#ifndef FS2_OPEN_VIDEOPRESENTER_H
|
||||
#define FS2_OPEN_VIDEOPRESENTER_H
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "cutscene/Decoder.h"
|
||||
|
||||
namespace cutscene {
|
||||
namespace player {
|
||||
class VideoPresenter {
|
||||
public:
|
||||
virtual ~VideoPresenter() {}
|
||||
|
||||
virtual void uploadVideoFrame(const VideoFramePtr& frame) = 0;
|
||||
|
||||
virtual void displayFrame() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif //FS2_OPEN_VIDEOPRESENTER_H
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "ship/shipfx.h"
|
||||
#include "species_defs/species_defs.h"
|
||||
#include "weapon/weapon.h"
|
||||
#include "tracing/Monitor.h"
|
||||
|
||||
#define MAX_LIFE 10.0f
|
||||
#define MIN_RADIUS_FOR_PERSISTANT_DEBRIS 50 // ship radius at which debris from it becomes persistant
|
||||
@@ -1051,7 +1052,7 @@ void calc_debris_physics_properties( physics_info *pi, vec3d *mins, vec3d *maxs
|
||||
/**
|
||||
* Renders debris
|
||||
*/
|
||||
void debris_render(object * obj, draw_list *scene)
|
||||
void debris_render(object * obj, model_draw_list *scene)
|
||||
{
|
||||
int i, num, swapped;
|
||||
polymodel *pm;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
class object;
|
||||
struct CFILE;
|
||||
class draw_list;
|
||||
class model_draw_list;
|
||||
|
||||
#define MAX_DEBRIS_ARCS 8 // Must be less than MAX_ARC_EFFECTS in model.h
|
||||
|
||||
@@ -63,7 +63,7 @@ extern int Num_debris_pieces;
|
||||
struct collision_info_struct;
|
||||
|
||||
void debris_init();
|
||||
void debris_render(object * obj, draw_list *scene);
|
||||
void debris_render(object * obj, model_draw_list *scene);
|
||||
void debris_delete( object * obj );
|
||||
void debris_process_post( object * obj, float frame_time);
|
||||
object *debris_create( object * source_obj, int model_num, int submodel_num, vec3d *pos, vec3d *exp_center, int hull_flag, float exp_force );
|
||||
|
||||
@@ -306,7 +306,7 @@ void dc_init(void)
|
||||
dc_command_buf.reserve(MAX_CLI_LEN);
|
||||
dc_command_buf.clear();
|
||||
|
||||
sprintf(dc_title, "FreeSpace Open v%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD);
|
||||
sprintf(dc_title, "FreeSpace Open v%s", FS_VERSION_FULL);
|
||||
dc_printf("Debug console started.\n" );
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ bool dc_pause_output(void);
|
||||
* @brief Prints the given char string to the debug console
|
||||
* @details See the doc for std::printf() for formating and more details
|
||||
*/
|
||||
void dc_printf(const char *format, ...);
|
||||
void dc_printf(SCP_FORMAT_STRING const char *format, ...) SCP_FORMAT_STRING_ARGS(1, 2);
|
||||
|
||||
/**
|
||||
* @brief Opens and processes the debug console. (Blocking call)
|
||||
|
||||
@@ -957,7 +957,7 @@ long dc_parse_long(const char *ch, dc_token type) {
|
||||
dc_printf("Error: Could not convert all of the buffer '%s'.\n", buffer_str.c_str());
|
||||
if (Dc_debug_on) {
|
||||
dc_printf("<debug> Buffer value: %s\n", buffer_str.c_str());
|
||||
dc_printf("<debug> Return value: %i", ret);
|
||||
dc_printf("<debug> Return value: %ld", ret);
|
||||
}
|
||||
throw errParse(ch, type);
|
||||
}
|
||||
@@ -1046,7 +1046,7 @@ ulong dc_parse_ulong(const char *ch, dc_token type) {
|
||||
dc_printf("Error: Could not convert all of the buffer '%s'.\n", buffer_str.c_str());
|
||||
if (Dc_debug_on) {
|
||||
dc_printf("<debug> Buffer value: %s\n", buffer_str.c_str());
|
||||
dc_printf("<debug> Return value: %i", ret);
|
||||
dc_printf("<debug> Return value: %ld", ret);
|
||||
}
|
||||
throw errParse(ch, type);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
in vec4 fragTexCoord;
|
||||
out vec4 fragOut0;
|
||||
out vec4 fragOut1;
|
||||
out vec4 fragOut2;
|
||||
out vec4 fragOut3;
|
||||
out vec4 fragOut4;
|
||||
uniform sampler2D bloomed;
|
||||
uniform float bloom_intensity;
|
||||
uniform int levels;
|
||||
void main() {
|
||||
// Global constant
|
||||
vec4 color_out = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
// Bloom
|
||||
if (bloom_intensity > 0.0) {
|
||||
vec3 bloom = vec3(0.0, 0.0, 0.0);
|
||||
float factor = 0.0;
|
||||
for (int mipmap = 0; mipmap < levels; ++mipmap) {
|
||||
float scale = 1.0/exp2(float(mipmap));
|
||||
factor += scale;
|
||||
bloom += textureLod(bloomed, fragTexCoord.xy, float(mipmap)).rgb * scale;
|
||||
}
|
||||
bloom /= factor;
|
||||
color_out.rgb += bloom * bloom_intensity;
|
||||
float factor = 0.0;
|
||||
for (int mipmap = 0; mipmap < levels; ++mipmap) {
|
||||
float scale = 1.0/exp2(float(mipmap));
|
||||
factor += scale;
|
||||
color_out.rgb += textureLod(bloomed, fragTexCoord.xy, float(mipmap)).rgb * scale;
|
||||
}
|
||||
color_out.rgb /= factor;
|
||||
color_out.rgb *= bloom_intensity;
|
||||
fragOut0 = color_out;
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <iterator>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <windows.h>
|
||||
|
||||
struct def_file
|
||||
{
|
||||
|
||||
@@ -29,9 +29,9 @@ vec3 FresnelSchlick(vec3 specColor, vec3 light, vec3 halfVec)
|
||||
{
|
||||
return specColor + (vec3(1.0) - specColor) * pow(1.0 - clamp(dot(light, halfVec), 0.0, 1.0), 5.0);
|
||||
}
|
||||
vec3 SpecularBlinnPhong(vec3 specColor, vec3 light, vec3 normal, vec3 halfVec, float specPower, float dotNL)
|
||||
vec3 SpecularBlinnPhong(vec3 specColor, vec3 light, vec3 normal, vec3 halfVec, float specPower, float fresnel, float dotNL)
|
||||
{
|
||||
return FresnelSchlick(max(specColor, vec3(0.04)), light, halfVec) * ((specPower + 2.0) / 8.0 ) * pow(clamp(dot(normal, halfVec), 0.0, 1.0), specPower) * dotNL;
|
||||
return mix(specColor, FresnelSchlick(specColor, light, halfVec), fresnel) * ((specPower + 2.0) / 8.0 ) * pow(clamp(dot(normal, halfVec), 0.0, 1.0), specPower) * dotNL;
|
||||
}
|
||||
vec4 SpecularLegacy(vec4 specColor, vec3 normal, vec3 halfVec, float specPower)
|
||||
{
|
||||
@@ -46,28 +46,13 @@ void main()
|
||||
discard;
|
||||
vec3 lightDir = lightPosition - position.xyz;
|
||||
float dist = length(lightDir);
|
||||
float attenuation = (1.0 - dist/lightRadius);
|
||||
if(lightType == 2)
|
||||
{
|
||||
float coneDot = dot(normalize(-lightDir), coneDir);
|
||||
if(dualCone) {
|
||||
if(abs(coneDot) < coneAngle)
|
||||
discard;
|
||||
else
|
||||
attenuation *= smoothstep(coneAngle, coneInnerAngle, abs(coneDot));
|
||||
} else {
|
||||
if(coneDot < coneAngle)
|
||||
discard;
|
||||
else
|
||||
attenuation *= smoothstep(coneAngle, coneInnerAngle, coneDot);
|
||||
}
|
||||
}
|
||||
if(dist > lightRadius && lightType != 1)
|
||||
discard;
|
||||
vec3 color = texture(ColorBuffer, screenPos).rgb;
|
||||
vec4 normal = texture(NormalBuffer, screenPos);
|
||||
vec4 specColor = texture(SpecBuffer, screenPos);
|
||||
float gloss = specColor.a;
|
||||
float gloss = normal.a;
|
||||
float fresnel = specColor.a;
|
||||
vec3 eyeDir = normalize(-position);
|
||||
|
||||
if(lightType == 1)
|
||||
@@ -83,11 +68,29 @@ void main()
|
||||
if(dist > lightRadius)
|
||||
discard;
|
||||
}
|
||||
|
||||
float attenuation = 1.0 - clamp(dist / lightRadius, 0.0, 1.0);
|
||||
|
||||
if(lightType == 2)
|
||||
{
|
||||
float coneDot = dot(normalize(-lightDir), coneDir);
|
||||
if(dualCone) {
|
||||
if(abs(coneDot) < coneAngle)
|
||||
discard;
|
||||
else
|
||||
attenuation *= smoothstep(coneAngle, coneInnerAngle, abs(coneDot));
|
||||
} else {
|
||||
if(coneDot < coneAngle)
|
||||
discard;
|
||||
else
|
||||
attenuation *= smoothstep(coneAngle, coneInnerAngle, coneDot);
|
||||
}
|
||||
}
|
||||
|
||||
lightDir /= dist;
|
||||
vec3 halfVec = normalize(lightDir + eyeDir);
|
||||
float NdotHV = clamp(dot(normal.xyz, halfVec), 0.0, 1.0);
|
||||
float NdotL = clamp(dot(normal.xyz, lightDir), 0.0, 1.0);
|
||||
vec4 fragmentColor = vec4(color * (diffuseLightColor * NdotL * attenuation), 1.0);
|
||||
fragmentColor.rgb += SpecularBlinnPhong(specLightColor, lightDir, normal.xyz, halfVec, exp2(10.0 * gloss + 1.0), NdotL).rgb * specColor.rgb * attenuation;
|
||||
fragmentColor.rgb += SpecularBlinnPhong(specColor.rgb, lightDir, normal.xyz, halfVec, exp2(10.0 * gloss + 1.0), fresnel, NdotL).rgb * specLightColor * attenuation;
|
||||
fragOut0 = max(fragmentColor, vec4(0.0));
|
||||
}
|
||||
@@ -5,6 +5,6 @@ uniform sampler2D baseMap;
|
||||
uniform int textured;
|
||||
void main()
|
||||
{
|
||||
vec4 fragmentColor = mix(fragColor, texture2D(baseMap, fragTexCoord.xy)*fragColor.a, (float)textured);
|
||||
vec4 fragmentColor = mix(fragColor, texture(baseMap, fragTexCoord.xy)*fragColor.a, (float)textured);
|
||||
fragOut0 = fragmentColor;
|
||||
}
|
||||
@@ -1,22 +1,7 @@
|
||||
#extension GL_EXT_gpu_shader4 : enable
|
||||
#define FXAA_EARLY_EXIT 1
|
||||
#define FXAA_DISCARD 1
|
||||
#ifndef FXAA_FAST_PIXEL_OFFSET
|
||||
#ifdef GL_EXT_gpu_shader4
|
||||
#define FXAA_FAST_PIXEL_OFFSET 1
|
||||
#endif
|
||||
#ifdef GL_NV_gpu_shader5
|
||||
#extension GL_NV_gpu_shader5 : enable
|
||||
#define FXAA_FAST_PIXEL_OFFSET 1
|
||||
#endif
|
||||
#ifdef GL_ARB_gpu_shader5
|
||||
#extension GL_ARB_gpu_shader5 : enable
|
||||
#define FXAA_FAST_PIXEL_OFFSET 1
|
||||
#endif
|
||||
#ifndef FXAA_FAST_PIXEL_OFFSET
|
||||
#define FXAA_FAST_PIXEL_OFFSET 0
|
||||
#endif
|
||||
#endif
|
||||
#define FXAA_FAST_PIXEL_OFFSET 1 // With OpenGL 3.2 we can always do this
|
||||
|
||||
#ifndef FXAA_GATHER4_ALPHA
|
||||
#ifdef GL_ARB_gpu_shader5
|
||||
#extension GL_ARB_gpu_shader5 : enable
|
||||
@@ -163,11 +148,11 @@
|
||||
#define FxaaSat(x) saturate(x)
|
||||
#endif
|
||||
#if (FXAA_GLSL_120 == 1)
|
||||
#define FxaaTexTop(t, p) texture2DLod(t, p, 0.0)
|
||||
#define FxaaTexTop(t, p) textureLod(t, p, 0.0)
|
||||
#if (FXAA_FAST_PIXEL_OFFSET == 1)
|
||||
#define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o)
|
||||
#define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o)
|
||||
#else
|
||||
#define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0)
|
||||
#define FxaaTexOff(t, p, o, r) textureLod(t, p + (o * r), 0.0)
|
||||
#endif
|
||||
#if (FXAA_GATHER4_ALPHA == 1)
|
||||
#define FxaaTexAlpha4(t, p) textureGather(t, p, 3)
|
||||
@@ -508,4 +493,4 @@ noperspective in vec2 v_pos;
|
||||
out vec4 fragOut0;
|
||||
void main() {
|
||||
fragOut0 = FxaaPixelShader(v_pos, tex0, v_rcpFrame, FXAA_QUALITY_SUBPIX, FXAA_QUALITY_EDGE_THRESHOLD, FXAA_QUALITY_EDGE_THRESHOLD_MIN);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#extension GL_EXT_gpu_shader4 : enable
|
||||
|
||||
in vec4 vertPosition;
|
||||
uniform float rt_w;
|
||||
uniform float rt_h;
|
||||
@@ -8,4 +8,4 @@ void main() {
|
||||
gl_Position = vertPosition;
|
||||
v_rcpFrame = vec2(1.0/rt_w, 1.0/rt_h);
|
||||
v_pos = vertPosition.xy*0.5 + 0.5;
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,14 @@ void main()
|
||||
step *= 1.0 / float(SAMPLE_NUM) * density;
|
||||
float decay = 1.0;
|
||||
vec4 sum = vec4(0.0);
|
||||
vec4 mask = texture2D(cockpit, fragTexCoord.st);
|
||||
vec4 mask = texture(cockpit, fragTexCoord.st);
|
||||
if (mask.r < 1.0) {
|
||||
fragOut0 = vec4(cp_intensity);
|
||||
return;
|
||||
}
|
||||
for(int i=0; i < SAMPLE_NUM ; i++) {
|
||||
pos.st -= step;
|
||||
vec4 tex_sample = texture2D(scene, pos);
|
||||
vec4 tex_sample = texture(scene, pos);
|
||||
if (tex_sample.r == 1.0)
|
||||
sum += decay * weight;
|
||||
decay *= falloff;
|
||||
|
||||
+58
-92
@@ -19,7 +19,6 @@ uniform vec3 emissionFactor;
|
||||
#ifdef FLAG_DIFFUSE_MAP
|
||||
uniform sampler2D sBasemap;
|
||||
uniform int desaturate;
|
||||
uniform vec3 desaturate_clr;
|
||||
uniform int blend_alpha;
|
||||
uniform bool overrideDiffuse;
|
||||
uniform vec3 diffuseClr;
|
||||
@@ -40,7 +39,6 @@ uniform bool gammaSpec;
|
||||
uniform samplerCube sEnvmap;
|
||||
uniform bool envGloss;
|
||||
uniform bool alpha_spec;
|
||||
varying vec3 envReflect;
|
||||
in vec3 fragEnvReflect;
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
@@ -84,10 +82,6 @@ uniform float neardist;
|
||||
uniform float middist;
|
||||
uniform float fardist;
|
||||
#endif
|
||||
#ifdef FLAG_CLIP
|
||||
uniform int use_clip_plane;
|
||||
in float fragClipDistance;
|
||||
#endif
|
||||
#ifdef FLAG_NORMAL_ALPHA
|
||||
uniform vec2 normalAlphaMinMax;
|
||||
#endif
|
||||
@@ -199,39 +193,30 @@ float getShadowValue()
|
||||
return mix(samplePoissonPCF(cascade), samplePoissonPCF(cascade+1), smoothstep(cascade_start_dist[cascade+1] - dist_threshold, cascade_start_dist[cascade+1], depth));
|
||||
}
|
||||
#endif
|
||||
#ifdef FLAG_LIGHT
|
||||
vec3 FresnelSchlick(vec3 specColor, vec3 light, vec3 halfVec)
|
||||
{
|
||||
return specColor + (vec3(1.0) - specColor) * pow(1.0 - clamp(dot(light, halfVec), 0.0, 1.0), 5.0);
|
||||
}
|
||||
vec3 FresnelLazarovEnv(vec3 specColor, vec3 view, vec3 normal, float gloss)
|
||||
{
|
||||
// Fresnel for environment lighting
|
||||
// Equation referenced from Dimitar Lazarov's presentation titled Physically Based Rendering in Call of Duty: Black Ops
|
||||
return specColor + (vec3(1.0) - specColor) * pow(1.0 - clamp(dot(view, normal), 0.0, 1.0), 5.0) / (4.0 - 3.0 * gloss);
|
||||
}
|
||||
vec3 SpecularBlinnPhong(vec3 specColor, vec3 light, vec3 normal, vec3 halfVec, float specPower, float dotNL)
|
||||
vec3 FresnelSchlick(vec3 specColor, vec3 light, vec3 halfVec)
|
||||
{
|
||||
return FresnelSchlick(max(specColor, vec3(0.04)), light, halfVec) * ((specPower + 2.0) / 8.0 ) * pow(clamp(dot(normal, halfVec), 0.0, 1.0), specPower) * dotNL;
|
||||
return specColor + (vec3(1.0) - specColor) * pow(1.0 - clamp(dot(light, halfVec), 0.0, 1.0), 5.0);
|
||||
}
|
||||
vec4 SpecularLegacy(vec4 specColor, vec3 normal, vec3 halfVec, float specPower)
|
||||
vec3 SpecularBlinnPhong(vec3 specColor, vec3 light, vec3 normal, vec3 halfVec, float specPower, float fresnel, float dotNL)
|
||||
{
|
||||
return specColor * pow(clamp(dot(normal, halfVec), 0.0, 1.0), specPower);
|
||||
return mix(specColor, FresnelSchlick(specColor, light, halfVec), fresnel) * ((specPower + 2.0) / 8.0 ) * pow(clamp(dot(normal, halfVec), 0.0, 1.0), specPower) * dotNL;
|
||||
}
|
||||
void GetLightInfo(int i, out vec3 lightDir, out float attenuation, out float specIntensity)
|
||||
#ifdef FLAG_LIGHT
|
||||
void GetLightInfo(int i, out vec3 lightDir, out float attenuation)
|
||||
{
|
||||
lightDir = normalize(lightPosition[i].xyz);
|
||||
attenuation = 1.0;
|
||||
specIntensity = SPEC_INTENSITY_DIRECTIONAL;
|
||||
#if __VERSION__ > 120
|
||||
if (lightType[i] != LT_DIRECTIONAL) {
|
||||
#else
|
||||
if (lightType[i] != LT_DIRECTIONAL && i != 0) {
|
||||
#endif
|
||||
// Positional light source
|
||||
float dist = distance(lightPosition[i].xyz, fragPosition.xyz);
|
||||
lightDir = (lightPosition[i].xyz - fragPosition.xyz);
|
||||
#if __VERSION__ > 120
|
||||
|
||||
if (lightType[i] == LT_TUBE) { // Tube light
|
||||
float beamlength = length(lightDirection[i]);
|
||||
vec3 beamDir = normalize(lightDirection[i]);
|
||||
@@ -242,43 +227,37 @@ void GetLightInfo(int i, out vec3 lightDir, out float attenuation, out float spe
|
||||
lightDir = nearest - fragPosition.xyz;
|
||||
dist = length(lightDir);
|
||||
}
|
||||
#endif
|
||||
|
||||
lightDir = normalize(lightDir);
|
||||
attenuation = 1.0 / (lightAttenuation[i] * dist);
|
||||
specIntensity = SPEC_INTENSITY_POINT;
|
||||
attenuation = 1.0 / (1.0 + lightAttenuation[i] * dist);
|
||||
}
|
||||
}
|
||||
vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial, float gloss, float shadow, float aoFactor)
|
||||
vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial, float gloss, float fresnel, float shadow, float aoFactor)
|
||||
{
|
||||
vec3 eyeDir = vec3(normalize(-fragPosition).xyz);
|
||||
vec3 lightAmbient = (emissionFactor + ambientFactor) * aoFactor;
|
||||
vec3 lightAmbientDiffuse = vec3(0.0, 0.0, 0.0);
|
||||
vec3 lightDiffuse = vec3(0.0, 0.0, 0.0);
|
||||
vec3 lightSpecular = vec3(0.0, 0.0, 0.0);
|
||||
vec3 lightAmbient = (emissionFactor + ambientFactor * ambientFactor) * aoFactor; // ambientFactor^2 due to legacy OpenGL compatibility behavior
|
||||
vec3 lightDiffuse = vec3(0.0, 0.0, 0.0);
|
||||
vec3 lightSpecular = vec3(0.0, 0.0, 0.0);
|
||||
#pragma optionNV unroll all
|
||||
for (int i = 0; i < MAX_LIGHTS; ++i) {
|
||||
#if __VERSION__ > 120
|
||||
if (i > n_lights)
|
||||
break;
|
||||
#endif
|
||||
if(i > 0)
|
||||
shadow = 1.0;
|
||||
|
||||
if(i > 0) {
|
||||
shadow = 1.0;
|
||||
}
|
||||
|
||||
vec3 lightDir;
|
||||
float attenuation;
|
||||
float specIntensity;
|
||||
// gather light params
|
||||
GetLightInfo(i, lightDir, attenuation, specIntensity);
|
||||
GetLightInfo(i, lightDir, attenuation);
|
||||
vec3 halfVec = normalize(lightDir + eyeDir);
|
||||
float NdotL = max(dot(normal, lightDir), 0.0);
|
||||
// Ambient, Diffuse, and Specular
|
||||
lightDiffuse += (lightDiffuseColor[i].rgb * diffuseFactor * NdotL * attenuation) * shadow;
|
||||
lightSpecular += lightSpecColor[i] * SpecularBlinnPhong(specularMaterial, lightDir, normal, halfVec, exp2(10.0 * gloss + 1.0), NdotL) * attenuation * shadow;
|
||||
lightSpecular += lightSpecColor[i] * SpecularBlinnPhong(specularMaterial, lightDir, normal, halfVec, exp2(10.0 * gloss + 1.0), fresnel, NdotL) * attenuation * shadow;
|
||||
}
|
||||
lightAmbientDiffuse = lightAmbient + lightDiffuse;
|
||||
vec3 finalColor = diffuseMaterial;
|
||||
finalColor *= lightAmbientDiffuse.rgb;
|
||||
finalColor += lightSpecular;
|
||||
return finalColor;
|
||||
return diffuseMaterial * (lightAmbient + lightDiffuse) + lightSpecular;
|
||||
}
|
||||
#endif
|
||||
void main()
|
||||
@@ -287,26 +266,15 @@ void main()
|
||||
if(fragNotVisible >= 0.9) { discard; }
|
||||
#endif
|
||||
#ifdef FLAG_SHADOW_MAP
|
||||
#ifdef FLAG_CLIP
|
||||
if(use_clip_plane == 1) { if(fragClipDistance <= 0.0) { discard; } }
|
||||
#endif
|
||||
// need depth and depth squared for variance shadow maps
|
||||
fragOut0 = vec4(fragPosition.z, fragPosition.z * fragPosition.z * VARIANCE_SHADOW_SCALE_INV, 0.0, 1.0);
|
||||
return;
|
||||
#endif
|
||||
vec3 eyeDir = vec3(normalize(-fragPosition).xyz);
|
||||
vec3 lightAmbientDiffuse = vec3(0.0, 0.0, 0.0);
|
||||
vec3 lightDiffuse = vec3(0.0, 0.0, 0.0);
|
||||
vec3 lightAmbient = vec3(0.0, 0.0, 0.0);
|
||||
vec4 lightSpecular = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec2 texCoord = fragTexCoord.xy;
|
||||
vec4 baseColor = color;
|
||||
#ifdef FLAG_HDR
|
||||
baseColor.rgb = pow(baseColor.rgb, vec3(SRGB_GAMMA));
|
||||
#endif
|
||||
vec4 posData = vec4(fragPosition.xyz,1.0);
|
||||
vec4 normData = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec4 specData = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec4 specColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
float fresnelFactor = 0.0;
|
||||
float glossData = 0.6;
|
||||
#ifdef FLAG_LIGHT
|
||||
glossData = defaultGloss;
|
||||
@@ -315,7 +283,7 @@ void main()
|
||||
#ifdef FLAG_AMBIENT_MAP
|
||||
// red channel is ambient occlusion factor which only affects ambient lighting.
|
||||
// green is cavity occlusion factor which only affects diffuse and specular lighting.
|
||||
aoFactors = texture2D(sAmbientmap, texCoord).xy;
|
||||
aoFactors = texture(sAmbientmap, texCoord).xy;
|
||||
#endif
|
||||
vec3 unitNormal = normalize(fragNormal);
|
||||
vec3 normal = unitNormal;
|
||||
@@ -332,7 +300,6 @@ void main()
|
||||
else
|
||||
normal = unitNormal;
|
||||
#endif
|
||||
normData = vec4(normal, 1.0);
|
||||
|
||||
#ifdef FLAG_ANIMATED
|
||||
vec2 distort = vec2(cos(fragPosition.x*fragPosition.w*0.005+anim_timer*20.0)*sin(fragPosition.y*fragPosition.w*0.005),sin(fragPosition.x*fragPosition.w*0.005+anim_timer*20.0)*cos(fragPosition.y*fragPosition.w*0.005))*0.03;
|
||||
@@ -351,45 +318,49 @@ void main()
|
||||
// if blend_alpha is 2, assume blend func is additive and don't modify color
|
||||
if(blend_alpha == 1) baseColor.rgb = baseColor.rgb * baseColor.a;
|
||||
if(overrideDiffuse) baseColor.rgb = diffuseClr;
|
||||
#ifdef FLAG_HDR
|
||||
#endif
|
||||
specColor = vec4(baseColor.rgb * SPEC_FACTOR_NO_SPEC_MAP, glossData);
|
||||
#ifdef FLAG_HDR
|
||||
baseColor.rgb = pow(baseColor.rgb, vec3(SRGB_GAMMA));
|
||||
#endif
|
||||
baseColor.rgb *= aoFactors.y;
|
||||
#endif
|
||||
|
||||
specData = vec4(baseColor.rgb * SPEC_FACTOR_NO_SPEC_MAP, glossData);
|
||||
#ifdef FLAG_SPEC_MAP
|
||||
vec4 specColour = texture2D(sSpecmap, texCoord);
|
||||
if(alphaGloss) glossData = specColour.a;
|
||||
if(overrideSpec) specColour.rgb = specClr;
|
||||
specColor = texture(sSpecmap, texCoord);
|
||||
if(alphaGloss) glossData = specColor.a;
|
||||
if(overrideSpec) {
|
||||
specColor.rgb = specClr;
|
||||
fresnelFactor = 1.0;
|
||||
}
|
||||
if(gammaSpec) {
|
||||
#ifdef FLAG_HDR
|
||||
if(gammaSpec) specColour.rgb = pow(specColour.rgb, vec3(SRGB_GAMMA));
|
||||
specColor.rgb = pow(specColor.rgb, vec3(SRGB_GAMMA));
|
||||
#endif
|
||||
specColour.rgb *= aoFactors.y;
|
||||
specColour.rgb = max(specColour.rgb, vec3(0.04)); // hardcoded minimum specular value. read John Hable's blog post titled 'Everything Is Shiny'.
|
||||
specData = vec4(specColour.rgb, glossData);
|
||||
specColor.rgb = max(specColor.rgb, vec3(0.04)); // hardcoded minimum specular value. read John Hable's blog post titled 'Everything Is Shiny'.
|
||||
fresnelFactor = 1.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
baseColor.rgb *= aoFactors.y;
|
||||
specColor.rgb *= aoFactors.y;
|
||||
#ifdef FLAG_MISC_MAP
|
||||
#ifdef FLAG_TEAMCOLOR
|
||||
vec4 teamMask = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
teamMask = texture(sMiscmap, texCoord);
|
||||
vec3 base = base_color - vec3(0.5);
|
||||
vec3 stripe = stripe_color - vec3(0.5);
|
||||
vec3 base = max(base_color - vec3(0.5), vec3(0.0));
|
||||
vec3 stripe = max(stripe_color - vec3(0.5), vec3(0.0));
|
||||
baseColor.rgb += (base * teamMask.x) + (stripe * teamMask.y);
|
||||
baseColor.rgb = max(baseColor.rgb, vec3(0.0));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
float shadow = 1.0;
|
||||
#ifdef FLAG_SHADOWS
|
||||
shadow = getShadowValue();
|
||||
#endif
|
||||
|
||||
#ifdef FLAG_LIGHT
|
||||
baseColor.rgb = CalculateLighting(normal, baseColor.rgb, specData.rgb, glossData, shadow, aoFactors.x);
|
||||
baseColor.rgb = CalculateLighting(normal, baseColor.rgb, specColor.rgb, glossData, fresnelFactor, shadow, aoFactors.x);
|
||||
#else
|
||||
#ifdef FLAG_SPEC_MAP
|
||||
baseColor.rgb += pow(1.0 - clamp(dot(eyeDir, normal), 0.0, 1.0), 5.0 * clamp(glossData, 0.01, 1.0)) * specColor.rgb;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef FLAG_ENV_MAP
|
||||
vec3 envReflectNM = fragEnvReflect;
|
||||
#ifdef FLAG_NORMAL_MAP
|
||||
@@ -401,12 +372,11 @@ void main()
|
||||
#ifdef FLAG_HDR
|
||||
envColour.rgb = pow(envColour.rgb, vec3(SRGB_GAMMA));
|
||||
#endif
|
||||
envColour.rgb *= (envGloss) ? 1.0 : specColour.a;
|
||||
baseColor.rgb += envColour.rgb * FresnelLazarovEnv(specColour.rgb, eyeDir, normal, glossData);
|
||||
envColour.rgb *= (envGloss) ? 1.0 : specColor.a;
|
||||
baseColor.rgb += envColour.rgb * FresnelLazarovEnv(specColor.rgb, eyeDir, normal, glossData);
|
||||
#endif
|
||||
|
||||
#ifdef FLAG_GLOW_MAP
|
||||
vec3 glowColor = texture2D(sGlowmap, texCoord).rgb;
|
||||
vec3 glowColor = texture(sGlowmap, texCoord).rgb;
|
||||
if(overrideGlow) glowColor = glowClr;
|
||||
#ifdef FLAG_HDR
|
||||
glowColor = pow(glowColor, vec3(SRGB_GAMMA)) * 3.0f;
|
||||
@@ -428,13 +398,13 @@ void main()
|
||||
#ifdef FLAG_DIFFUSE_MAP
|
||||
if(blend_alpha == 1) finalFogColor *= baseColor.a;
|
||||
#endif
|
||||
baseColor.rgb = mix(baseColor.rgb, finalFogColor, fragFogDist);
|
||||
specData.rgb *= fragFogDist;
|
||||
baseColor.rgb = mix(baseColor.rgb, finalFogColor, fragFogDist);
|
||||
specColor.rgb *= fragFogDist;
|
||||
#endif
|
||||
|
||||
#ifdef FLAG_DIFFUSE_MAP
|
||||
if(desaturate == 1) {
|
||||
baseColor.rgb = desaturate_clr * dot(vec3(1.0), baseColor.rgb) * 0.3333333;
|
||||
baseColor.rgb = color.rgb * dot(vec3(1.0), baseColor.rgb) * 0.3333333;
|
||||
}
|
||||
#endif
|
||||
#ifdef FLAG_ANIMATED
|
||||
@@ -468,14 +438,10 @@ void main()
|
||||
float normViewOffset = dot(vec3(0.0, 0.0, 1.0), normal);
|
||||
baseColor.a = smoothstep(min(normalAlphaMinMax.x, normalAlphaMinMax.y), max(normalAlphaMinMax.x, normalAlphaMinMax.y), clamp(normalAlphaMinMax.x > normalAlphaMinMax.y ? normViewOffset : 1.0 - normViewOffset, 0.0, 1.0));
|
||||
#endif
|
||||
#ifdef FLAG_CLIP
|
||||
// for some odd reason if we try to discard the pixel early for plane clipping, it screws up glow maps so let's just do it down here.
|
||||
if(use_clip_plane == 1) { if(fragClipDistance <= 0.0) { discard; } }
|
||||
#endif
|
||||
fragOut0 = baseColor;
|
||||
fragOut0 = baseColor;
|
||||
#ifdef FLAG_DEFERRED
|
||||
fragOut1 = posData;
|
||||
fragOut2 = normData;
|
||||
fragOut3 = specData;
|
||||
fragOut1 = vec4(fragPosition.xyz, 1.0);
|
||||
fragOut2 = vec4(normal, glossData);
|
||||
fragOut3 = vec4(specColor.rgb, fresnelFactor);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,6 @@ out vec4 fragTexCoord;
|
||||
in float geoNotVisible[];
|
||||
out float fragNotVisible;
|
||||
#endif
|
||||
#ifdef FLAG_CLIP
|
||||
in float geoClipDistance[];
|
||||
out float fragClipDistance;
|
||||
#endif
|
||||
void main(void)
|
||||
{
|
||||
int instanceID = int(geoInstance[0]);
|
||||
@@ -31,7 +27,7 @@ void main(void)
|
||||
fragNotVisible = geoNotVisible[0];
|
||||
#endif
|
||||
#ifdef FLAG_CLIP
|
||||
fragClipDistance = geoClipDistance[0];
|
||||
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
|
||||
#endif
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
Loaded 100 of 768 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user