forked from Ports/perfect_dark
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d63e12034 | ||
|
|
23ce248dce | ||
|
|
fa7ef3df64 | ||
|
|
0fd290c8c3 | ||
|
|
b20abed21e | ||
|
|
c4f12ad0a5 | ||
|
|
60989596d5 | ||
|
|
256323a109 | ||
|
|
1e46e68933 | ||
|
|
32a1cb9f26 | ||
|
|
9f30f50b79 | ||
|
|
514bf7affd | ||
|
|
7477a6ce44 | ||
|
|
f7c0cb8b03 | ||
|
|
accb882a36 | ||
|
|
0750c54200 | ||
|
|
b81b7e260b | ||
|
|
3f47f1ec6f | ||
|
|
f9d847df3b | ||
|
|
c110f9cf42 | ||
|
|
cce73d91ce | ||
|
|
3360eb429d | ||
|
|
132e59d3ec | ||
|
|
c166cf0f15 | ||
|
|
20fc4c6cc7 | ||
|
|
309bd45a01 | ||
|
|
45139a9293 | ||
|
|
998eee9c61 | ||
|
|
d8962763b7 | ||
|
|
783791064b | ||
|
|
8106349540 | ||
|
|
bed3bf52d0 | ||
|
|
f25721da8f | ||
|
|
7c949ed9d7 | ||
|
|
8470d50f21 | ||
|
|
27b2dcc27d | ||
|
|
2626434b69 | ||
|
|
11df50b3ad | ||
|
|
ccb3c8668d | ||
|
|
d58a423e5f | ||
|
|
fc70da875e | ||
|
|
d4ac8b155d | ||
|
|
0946cd18c6 | ||
|
|
23aafa4ffa | ||
|
|
426f9c6890 | ||
|
|
cc3886888a | ||
|
|
55ca51d8cc | ||
|
|
f251653317 | ||
|
|
bcb8c2720a | ||
|
|
246d737663 | ||
|
|
425b51e51c | ||
|
|
6e6ffad1a1 | ||
|
|
2028ae7412 | ||
|
|
0c82545ba9 | ||
|
|
2550ce72f4 | ||
|
|
50481b77d0 | ||
|
|
4ee8f3c077 | ||
|
|
0df84ff180 | ||
|
|
a6374b016d | ||
|
|
75d1b8ffd2 | ||
|
|
8165b5005e | ||
|
|
2d0375da2e | ||
|
|
4bcfeddca7 | ||
|
|
2d390ae5ce | ||
|
|
c303c81f88 | ||
|
|
6b0c4a11e2 | ||
|
|
7f68c46959 | ||
|
|
f3f2a593e8 | ||
|
|
79711364bb | ||
|
|
0896860d58 | ||
|
|
e1dd49fc3a | ||
|
|
b53a62189b | ||
|
|
77dc12e41f | ||
|
|
42ef08d662 | ||
|
|
f5cbfe6692 | ||
|
|
550f1d3182 | ||
|
|
7a728c7feb | ||
|
|
857e755b92 | ||
|
|
8ea7e7b410 | ||
|
|
6dd3ec7fda | ||
|
|
bb4fcffeb5 | ||
|
|
498e1667a4 | ||
|
|
19541418b0 | ||
|
|
75952f0395 | ||
|
|
04d280cd42 | ||
|
|
58c2f9d8fa | ||
|
|
6591281d99 | ||
|
|
5fd4a431b1 | ||
|
|
27223802be | ||
|
|
09df313d09 | ||
|
|
01b78105ff | ||
|
|
618a1f3386 | ||
|
|
4bd6dd8617 | ||
|
|
eabd7a438f | ||
|
|
e8a69f39db | ||
|
|
38cb1557d0 | ||
|
|
97444de634 | ||
|
|
d9185982f8 | ||
|
|
790374e95f | ||
|
|
9c98fe7976 | ||
|
|
9bffab7e3e | ||
|
|
8037431686 | ||
|
|
a040b29d40 | ||
|
|
5a3d45de65 | ||
|
|
5a90152991 | ||
|
|
8891bbf7ec | ||
|
|
abf2ae8115 | ||
|
|
20bd9ddcbb | ||
|
|
1d7dd064fe | ||
|
|
629dbfe924 | ||
|
|
f72f93a270 | ||
|
|
57eead9078 | ||
|
|
094d80a5cf | ||
|
|
11bf7aa638 | ||
|
|
79a47809da | ||
|
|
33aa747386 | ||
|
|
a70868fba0 | ||
|
|
c866940873 | ||
|
|
0973f70301 | ||
|
|
54b62c044f | ||
|
|
d5b4e0916c | ||
|
|
45f25c704a | ||
|
|
d8aefc33fc | ||
|
|
cdd116830d | ||
|
|
11d02dd8f2 | ||
|
|
73631e8d5f | ||
|
|
a1136f4d61 | ||
|
|
8bfaaf63ed | ||
|
|
2d59929261 | ||
|
|
86e6e54792 | ||
|
|
f12ae756ef | ||
|
|
062de5bb7b | ||
|
|
400865ec25 | ||
|
|
0d6c557982 | ||
|
|
75e34ace8c | ||
|
|
a2428aac1e | ||
|
|
816d83a0b5 | ||
|
|
039eb8b562 | ||
|
|
872aa3a4a4 | ||
|
|
4b8e1d52b5 | ||
|
|
f96a03e60f | ||
|
|
9d2db1a346 | ||
|
|
fc7141d0f2 | ||
|
|
f414e8a792 | ||
|
|
ab73fdcc30 | ||
|
|
30071e8f64 | ||
|
|
9695856be6 | ||
|
|
053f7dc8a5 | ||
|
|
52992009db | ||
|
|
1555a4be84 | ||
|
|
d4856a193b | ||
|
|
cb6dc1feab | ||
|
|
b93015c51c | ||
|
|
6198fbc479 | ||
|
|
0316317aa3 | ||
|
|
f9cfa0fa43 | ||
|
|
7a98095a0b | ||
|
|
8f477a36b6 | ||
|
|
4d4fa8f681 | ||
|
|
0ac5fdd5c3 | ||
|
|
33c5194a1b | ||
|
|
edd58c8925 | ||
|
|
e32d719c16 | ||
|
|
be4d8330bd | ||
|
|
91c207a51b | ||
|
|
0d76ad102f | ||
|
|
135c293656 | ||
|
|
a1d6916871 | ||
|
|
9a853077a0 | ||
|
|
dace9fd634 | ||
|
|
9e19055348 | ||
|
|
dcef8deed8 | ||
|
|
c44704e2af | ||
|
|
1a5e42e55f | ||
|
|
a26260e932 | ||
|
|
3d75365f2b | ||
|
|
ad9ed9ff3b | ||
|
|
4f55a41d99 | ||
|
|
355f0ed0ee | ||
|
|
f5d851f7c2 | ||
|
|
4019bd59cf | ||
|
|
6c29e7ecc8 | ||
|
|
e65f46d081 | ||
|
|
2ff8819265 | ||
|
|
77d1ded519 | ||
|
|
60b8e3b49e | ||
|
|
8b0b75d4ad | ||
|
|
ea464ca6ac | ||
|
|
a9752198c3 | ||
|
|
fbd9ef002e | ||
|
|
e4972ddbe3 | ||
|
|
8dbe6f2a63 | ||
|
|
045c4c371a | ||
|
|
f4f1470e1e | ||
|
|
26251c0865 | ||
|
|
ac4f317879 | ||
|
|
e3104b7043 | ||
|
|
176b4282fc | ||
|
|
cd8f06d03a | ||
|
|
9cff3591dd | ||
|
|
daade36e19 | ||
|
|
addc890628 | ||
|
|
8a3dd74d5c | ||
|
|
c0aa42759c | ||
|
|
b637c6aac6 | ||
|
|
5cd436e848 | ||
|
|
d5225b7784 | ||
|
|
904faa5485 | ||
|
|
f1084b6d93 | ||
|
|
9a462d3ca8 | ||
|
|
4f7817a0ca | ||
|
|
5084b4be39 | ||
|
|
a05c4290f5 | ||
|
|
49cd478622 | ||
|
|
0443c708dd | ||
|
|
7f105f1dcb | ||
|
|
721b9b004e | ||
|
|
23d5a5771a | ||
|
|
0701f43d6b | ||
|
|
d5ae464f6d | ||
|
|
701c854ced | ||
|
|
afd432444b | ||
|
|
26b3b91433 | ||
|
|
8945809c1f | ||
|
|
da07772b1c | ||
|
|
1e76f6af69 | ||
|
|
a14e9ed97e | ||
|
|
615e9267d8 | ||
|
|
4f2b911802 | ||
|
|
c43f550a7e | ||
|
|
2439eb6e8d | ||
|
|
903dd82ebf | ||
|
|
cb449bf7ce | ||
|
|
e2e5bc352d | ||
|
|
687b966c5e | ||
|
|
0b8a333e2b | ||
|
|
b34bb01240 | ||
|
|
1e2f6dcb91 | ||
|
|
210b299bd4 | ||
|
|
6de98cfcd4 | ||
|
|
d43c4c0cba | ||
|
|
1d14877bec | ||
|
|
5aa1cc6117 | ||
|
|
1be0e3680b | ||
|
|
09b0a12b0a | ||
|
|
05e462b680 | ||
|
|
a2e362caea | ||
|
|
49ee5b7efb | ||
|
|
a1ca69d9ee | ||
|
|
424d4de9ac | ||
|
|
0e1a7e136a | ||
|
|
2386494030 | ||
|
|
67675d8980 | ||
|
|
9c4aded403 | ||
|
|
92e1c4aa0b | ||
|
|
f53fb8e952 | ||
|
|
0ad4492ff6 | ||
|
|
33efd36278 | ||
|
|
37e69cb83d | ||
|
|
5c5c33c116 | ||
|
|
13a3e0a0ea | ||
|
|
ccb50c6021 | ||
|
|
563e8f9f64 | ||
|
|
df3e0359b9 | ||
|
|
460cc25ef5 | ||
|
|
596dd7dd40 | ||
|
|
b2338179d6 | ||
|
|
683d083fab | ||
|
|
f75a569075 | ||
|
|
b97eaa9e12 | ||
|
|
3cadaecf96 | ||
|
|
4bc1a16d36 | ||
|
|
bc1f1c26db | ||
|
|
d2d35263a4 | ||
|
|
ba1b6c3232 | ||
|
|
3c020cad84 | ||
|
|
140f6f24e8 | ||
|
|
c7895d937f | ||
|
|
9f43c906db | ||
|
|
31dbb5e7c6 | ||
|
|
7d8e755fc7 | ||
|
|
103fb0e0e9 | ||
|
|
b220d73f2c | ||
|
|
efda5a5f44 | ||
|
|
41d5630eec | ||
|
|
cea20b6f31 | ||
|
|
06dcbcc1b1 | ||
|
|
d7a612302e | ||
|
|
d747588323 | ||
|
|
363158a93d | ||
|
|
7658a96c1c | ||
|
|
c332738f7a | ||
|
|
2542b21bfc | ||
|
|
ef8413ae58 | ||
|
|
7969d84257 | ||
|
|
3d8f63dbb6 | ||
|
|
08ec8e541b | ||
|
|
08afb2d90a | ||
|
|
d7e5ad5bc7 | ||
|
|
7830ccb88c | ||
|
|
8075307e09 | ||
|
|
fb18da239f | ||
|
|
bc82328353 | ||
|
|
938223db28 | ||
|
|
942c966bf2 | ||
|
|
e0d59345c7 | ||
|
|
f514e25208 | ||
|
|
28fceb67de | ||
|
|
130b286d0d | ||
|
|
b88c3a120b | ||
|
|
a6f5077609 | ||
|
|
e2b708d442 | ||
|
|
2cb29e91c6 | ||
|
|
55cc2d8e72 | ||
|
|
852349bbe0 | ||
|
|
b408d54dfb | ||
|
|
5d9fe034ea | ||
|
|
1d7c381ce2 | ||
|
|
6c8e5ee4dc | ||
|
|
d37d9b7168 | ||
|
|
fab41b6fcb | ||
|
|
ee7ce2d988 | ||
|
|
1a1accceca | ||
|
|
0bdf79cc51 | ||
|
|
91c43385dd | ||
|
|
58e64ebfc9 | ||
|
|
36f9e59304 | ||
|
|
420a1b52c1 | ||
|
|
345da23ebe | ||
|
|
3f887b2ecf | ||
|
|
d5482f3453 | ||
|
|
fdbd1667ad | ||
|
|
421ac0af79 | ||
|
|
163dbf9495 | ||
|
|
2f221b9a1e | ||
|
|
1448cd597a | ||
|
|
68e3a79e47 | ||
|
|
19ef4738b7 | ||
|
|
16f025cbbe | ||
|
|
25cdfc991b | ||
|
|
5c2082f58e | ||
|
|
23539a02a5 | ||
|
|
f37abc941e | ||
|
|
8468e69b9f | ||
|
|
8fe95d7d52 | ||
|
|
19167b8468 | ||
|
|
d5ee41fe5d | ||
|
|
2a5c3a351e | ||
|
|
2550b5a668 | ||
|
|
24d5679f4a | ||
|
|
a3d2b56dd9 | ||
|
|
34d3e082ec | ||
|
|
a277ca7ee4 | ||
|
|
af8e808e39 | ||
|
|
8f445d344f | ||
|
|
e9b946feb4 | ||
|
|
2d51c0a60a | ||
|
|
cf13976db3 | ||
|
|
6344cc8914 | ||
|
|
b2657eb0c2 | ||
|
|
35d263e15b | ||
|
|
c224055ff7 | ||
|
|
096a833838 | ||
|
|
ee8d7a6e89 | ||
|
|
538385a991 | ||
|
|
b8348a45bc | ||
|
|
a6b516be15 | ||
|
|
9ac8af6639 | ||
|
|
28bdccd322 | ||
|
|
8865130c35 | ||
|
|
153ca3341e | ||
|
|
8533189561 | ||
|
|
eabbdc612c | ||
|
|
5841fb9afa | ||
|
|
077cc67b79 | ||
|
|
589bdf1cd9 | ||
|
|
3524ec73b7 | ||
|
|
ab514f39af | ||
|
|
8be982fdb6 | ||
|
|
ae2543de90 | ||
|
|
6a5d249e67 | ||
|
|
4b44b5b4e3 | ||
|
|
2fd1c49ef5 | ||
|
|
22ad5a44db | ||
|
|
e77b3172ed | ||
|
|
a721349cff | ||
|
|
40cc42a941 | ||
|
|
cec92dbcb6 | ||
|
|
2c4d3e7768 | ||
|
|
e09aebebe7 | ||
|
|
a5d9f02184 | ||
|
|
d2dd109a05 | ||
|
|
01cbcd99cb | ||
|
|
a9e82658fd | ||
|
|
5bee1b0cce | ||
|
|
a11abc0511 | ||
|
|
c36b6b286d | ||
|
|
18fe7fd797 | ||
|
|
d6d4bc2c14 | ||
|
|
c519997b33 | ||
|
|
4d60c93beb | ||
|
|
b44dbb16b5 | ||
|
|
8b1e81f32a | ||
|
|
9e34f80a2c | ||
|
|
c1c71ea21d | ||
|
|
bdbf0ab789 | ||
|
|
ca76416e1c | ||
|
|
b534a42685 | ||
|
|
6b71e622c7 | ||
|
|
48c294fc24 | ||
|
|
f0fe7f41f1 | ||
|
|
2844930a21 | ||
|
|
d36c55d048 | ||
|
|
f855dc32f1 | ||
|
|
08e2b49e88 | ||
|
|
9cd978a7a9 | ||
|
|
a60880ef80 | ||
|
|
a4b5b110ec | ||
|
|
897020b3fd | ||
|
|
e85fdb3b9b | ||
|
|
e3e72617a1 | ||
|
|
bc365650ce | ||
|
|
6fa78556f4 | ||
|
|
5fdeff6a82 | ||
|
|
3573906eef | ||
|
|
e36df05a4f | ||
|
|
673ba52cbd | ||
|
|
0ea8872471 | ||
|
|
40273bdba1 | ||
|
|
e8006f3446 | ||
|
|
373f08ca3e | ||
|
|
844d6c7ec8 | ||
|
|
b43c2ec617 | ||
|
|
a8c68aed97 | ||
|
|
4c09de6de0 | ||
|
|
29d6392514 | ||
|
|
b9f6961931 | ||
|
|
949faf09d9 | ||
|
|
5beef0916b | ||
|
|
89397bd566 | ||
|
|
288a007202 | ||
|
|
50b912db98 | ||
|
|
71dc521baa | ||
|
|
06a45738cc | ||
|
|
5795634b9c | ||
|
|
cec197e841 | ||
|
|
58c31075b0 | ||
|
|
167a4a5d81 | ||
|
|
06c01208d3 | ||
|
|
86649345cd | ||
|
|
2e2dbfb578 | ||
|
|
10a3992a62 | ||
|
|
ef4b60d384 | ||
|
|
32faea2b95 | ||
|
|
8a94af6a1c | ||
|
|
315a5642da | ||
|
|
00c990eded | ||
|
|
b6b7133e76 | ||
|
|
b0e6e8f24f | ||
|
|
4dbbcbeb58 | ||
|
|
8c8109e8cd | ||
|
|
b523b1e076 | ||
|
|
07e6fdc1e0 | ||
|
|
3242105ef8 | ||
|
|
7c6508af28 | ||
|
|
d7c4843eea | ||
|
|
55ab679a81 | ||
|
|
de3b8ff548 | ||
|
|
dce96a4a3d | ||
|
|
08b4d780b7 | ||
|
|
bc5911bfba | ||
|
|
ada20c526a | ||
|
|
4c552a42f2 | ||
|
|
e8e513fecb | ||
|
|
4601a9c318 | ||
|
|
5c7d4ecf52 | ||
|
|
9edfd667b7 | ||
|
|
f3757e75e9 | ||
|
|
a6e7d503e6 | ||
|
|
da8b9be09d | ||
|
|
9530924c21 | ||
|
|
2ba192d411 | ||
|
|
bf8192ee84 | ||
|
|
45ec35172d | ||
|
|
926e4dce28 | ||
|
|
548e2c1f0b | ||
|
|
85872d7e71 | ||
|
|
a83e0aa407 | ||
|
|
865ca682dc | ||
|
|
dc9d0f3c9c | ||
|
|
12d06001e7 | ||
|
|
32c5d2c742 | ||
|
|
3199f99fbd | ||
|
|
51d842f516 | ||
|
|
4e7e61c207 | ||
|
|
70b02da163 | ||
|
|
1a3f36f626 | ||
|
|
d8a26fa221 | ||
|
|
28d6c5b827 | ||
|
|
fef8985b6b | ||
|
|
fd8718f7b1 | ||
|
|
197208b951 | ||
|
|
4bd314f60b | ||
|
|
95b43b3f7b | ||
|
|
62a55a3d6e | ||
|
|
12cf9df01b | ||
|
|
c5c1ef1d4e | ||
|
|
f3c1242858 | ||
|
|
ef3d53b9f7 | ||
|
|
1d040b1ccc | ||
|
|
9755f3ab41 | ||
|
|
f86746364f | ||
|
|
536c766793 | ||
|
|
dca1e1752b | ||
|
|
ce6424117e | ||
|
|
15c0327592 | ||
|
|
13003121ea | ||
|
|
ba56947457 | ||
|
|
27faaecda2 | ||
|
|
798fbf7b90 | ||
|
|
a0f8550034 | ||
|
|
73dc52cfd6 | ||
|
|
5b1d03cbbb | ||
|
|
68aee1d2f2 | ||
|
|
7580ebcaf9 | ||
|
|
c9a14f090b | ||
|
|
2483d4fc35 | ||
|
|
501ab95b00 | ||
|
|
0b61ec9641 | ||
|
|
b4f7f467c0 | ||
|
|
894ccfabf3 | ||
|
|
ec8310e4a1 | ||
|
|
80faefbaf6 | ||
|
|
c9f880e427 | ||
|
|
92a9ac43fc | ||
|
|
4baae8de0b | ||
|
|
d11b5705a2 | ||
|
|
839143550f | ||
|
|
fe0d9b3cfd | ||
|
|
935cd1faa9 | ||
|
|
36f8494ebf | ||
|
|
fcb1dbb298 | ||
|
|
815c75c6a8 | ||
|
|
33cc89e8fd | ||
|
|
cc6df47eed | ||
|
|
02d7efb87f | ||
|
|
1f4d9c8cdc | ||
|
|
63c456a233 | ||
|
|
474a6df3d4 | ||
|
|
9cde121b52 | ||
|
|
e53f26f3eb | ||
|
|
81313f0c4e | ||
|
|
cf5588478b | ||
|
|
d9cb850848 | ||
|
|
b9db86f89b | ||
|
|
6bd3597ff6 | ||
|
|
8645d0a82a | ||
|
|
ba52580e0c | ||
|
|
d1d288b96a | ||
|
|
06f2283180 | ||
|
|
b1b17e21c1 | ||
|
|
c487e423b1 | ||
|
|
49f607dd79 | ||
|
|
787758c038 | ||
|
|
3c62a967a2 | ||
|
|
2ef2427419 | ||
|
|
5fcf1083d2 | ||
|
|
f2a2458795 | ||
|
|
a7ab358b43 | ||
|
|
a82ac025da | ||
|
|
461bf8d49b | ||
|
|
db1d00a9b0 | ||
|
|
92c66c93dc | ||
|
|
94df3fc58c | ||
|
|
f2545b4e53 | ||
|
|
0221839699 | ||
|
|
1698f53728 | ||
|
|
7ac734fc81 | ||
|
|
28218b26c2 | ||
|
|
4233e5e066 | ||
|
|
cdd7d6edac | ||
|
|
581c63fc27 | ||
|
|
dee7ab7b54 | ||
|
|
8c509db888 | ||
|
|
5aa799718a | ||
|
|
880a7a0971 | ||
|
|
e84d83e404 | ||
|
|
79fb976bcf | ||
|
|
519bd95a75 | ||
|
|
8421140e3f | ||
|
|
894ea53b29 | ||
|
|
fb05725c34 | ||
|
|
2a6625278a | ||
|
|
342e186f79 | ||
|
|
cabd40f898 | ||
|
|
0a7faf2409 | ||
|
|
03324c0823 | ||
|
|
aeb6851606 | ||
|
|
26da2aa5e6 | ||
|
|
7647062619 | ||
|
|
3a313287bb | ||
|
|
42af4d4604 | ||
|
|
3118d01921 | ||
|
|
f46359b307 | ||
|
|
00297f6d0d | ||
|
|
39d5b57ccb | ||
|
|
d1be171939 | ||
|
|
6f206345a1 | ||
|
|
13b23de6d3 | ||
|
|
d897356029 | ||
|
|
f96e7da00f | ||
|
|
1fb16b3bfd | ||
|
|
2c20a0487e | ||
|
|
aef2463361 | ||
|
|
2b8d5d4f75 | ||
|
|
ef83f23200 | ||
|
|
75133acbbd | ||
|
|
1aea68b89a | ||
|
|
af34015562 | ||
|
|
7cd3dafaa4 | ||
|
|
e0249067b9 | ||
|
|
31ad9351f5 | ||
|
|
116dc7e8b6 | ||
|
|
aced07f356 | ||
|
|
8d99ac92a0 | ||
|
|
4a3c3a29b7 | ||
|
|
523e6ba9d9 | ||
|
|
c6a36633f5 | ||
|
|
626b5937ce | ||
|
|
228851f867 | ||
|
|
1c72b3022e | ||
|
|
5d7b7adb57 | ||
|
|
2d3e3d1aaf | ||
|
|
94e8e1b5e7 | ||
|
|
d3a2ed216a | ||
|
|
600dae58e2 | ||
|
|
3a1d08509b | ||
|
|
45a44cd37e | ||
|
|
5a68daf18c | ||
|
|
e43d29f357 | ||
|
|
3328e147eb | ||
|
|
7e929c2432 | ||
|
|
b88690e8df | ||
|
|
305e64a263 | ||
|
|
b618c99abd | ||
|
|
58d815f1ec | ||
|
|
99da0586ea | ||
|
|
27e36ca24e | ||
|
|
9dfd0ec905 | ||
|
|
6f55843f30 | ||
|
|
4a16f27abd | ||
|
|
11e318b70f | ||
|
|
5bb4f1d193 | ||
|
|
f394f209b2 | ||
|
|
9c7f348652 | ||
|
|
32b2e9c28e | ||
|
|
72e9e2af95 | ||
|
|
8a348ab1b5 | ||
|
|
28b172f5c7 | ||
|
|
ef511d5889 | ||
|
|
83a155fa49 | ||
|
|
eecee9d940 | ||
|
|
cd19c07810 | ||
|
|
f1edc91fd8 | ||
|
|
5df6d2fdfb | ||
|
|
83c5b4980b | ||
|
|
0e043f24b5 | ||
|
|
828776e272 | ||
|
|
407d4724a7 | ||
|
|
d75adeb0c8 | ||
|
|
dc6e6a507c | ||
|
|
fbae610a3b | ||
|
|
479c0335f3 | ||
|
|
d021e95d70 | ||
|
|
3ac194f9ee | ||
|
|
fea0a1e4ca | ||
|
|
a802d2db06 | ||
|
|
4a0296d1a4 | ||
|
|
c040c17b49 | ||
|
|
4eaaae91a6 | ||
|
|
83d22c3b70 | ||
|
|
4914aa335b | ||
|
|
afb91ab9e9 | ||
|
|
ed65f7971a | ||
|
|
3feddd8c64 | ||
|
|
9b65199d3d | ||
|
|
50b74e4280 | ||
|
|
c8db5fa106 | ||
|
|
cea9527936 | ||
|
|
2813ecedeb | ||
|
|
015817f204 | ||
|
|
da7e1493a5 | ||
|
|
368021201b | ||
|
|
30b8400d86 | ||
|
|
77fcac4912 | ||
|
|
790d44afb0 | ||
|
|
0ea3bea096 | ||
|
|
05c108a950 | ||
|
|
6957c1540d | ||
|
|
be689a0495 | ||
|
|
0b9f99a084 | ||
|
|
4c1076080d | ||
|
|
8347900d33 | ||
|
|
f638d49693 | ||
|
|
d1817aaea6 | ||
|
|
e2b1cdce86 | ||
|
|
81a5e971ef | ||
|
|
6f0cb752aa | ||
|
|
abf5703036 | ||
|
|
b8e6e19ef5 | ||
|
|
ba51e9d5d1 | ||
|
|
8bf14c4044 | ||
|
|
b650c820e0 | ||
|
|
80d787caf9 | ||
|
|
1eb1e9686a | ||
|
|
dbebe7e626 | ||
|
|
79f8a35c33 | ||
|
|
b78a5ee064 | ||
|
|
ed3bd97dd7 | ||
|
|
d3d313c9e8 | ||
|
|
8bb847508f | ||
|
|
63b38e48ba | ||
|
|
e8cb7e4136 | ||
|
|
8a71ff651a | ||
|
|
4c201ef3ff | ||
|
|
0278458a28 | ||
|
|
3b4600d0af | ||
|
|
50e3393951 | ||
|
|
72fa10634f | ||
|
|
e6a852ea8b | ||
|
|
603a879bdf | ||
|
|
94221e2c62 | ||
|
|
28837dd034 | ||
|
|
9a7e9c9ee3 | ||
|
|
608b02d363 | ||
|
|
940ba8c4c0 | ||
|
|
b0c6e79329 | ||
|
|
86ed199054 | ||
|
|
466c77362e | ||
|
|
8a342a0698 | ||
|
|
75b0d2dd29 | ||
|
|
704015deac | ||
|
|
d98575d77b | ||
|
|
9b17aaf258 | ||
|
|
4638af52e2 | ||
|
|
8aa1e6b9c9 | ||
|
|
c4e0e4f868 | ||
|
|
85b2ba5f77 | ||
|
|
ef090a2b8f | ||
|
|
d1d11b9362 | ||
|
|
ee4434eb6c | ||
|
|
9d2957c419 | ||
|
|
e606a58515 | ||
|
|
d303971c2d | ||
|
|
bd113a5b44 | ||
|
|
99337e8157 | ||
|
|
dad5b3c074 | ||
|
|
7d5f33d033 | ||
|
|
54842f50db | ||
|
|
5ae29afdeb | ||
|
|
8a62b884cc | ||
|
|
ca8851789a | ||
|
|
205032c626 | ||
|
|
f0842337e1 | ||
|
|
b2cb5c1737 | ||
|
|
eef42a1e3e | ||
|
|
53ff42836c | ||
|
|
e92f556cf9 | ||
|
|
13a6cc1c28 | ||
|
|
89a3377456 | ||
|
|
053d6172e5 | ||
|
|
31bb7c62ad | ||
|
|
c3f572f016 | ||
|
|
309aff78fe | ||
|
|
8553c788ab | ||
|
|
d567f707cb | ||
|
|
6686221029 | ||
|
|
3ee473d909 | ||
|
|
2010d31f6b | ||
|
|
ae2d463d7e | ||
|
|
233f4b7514 | ||
|
|
6a1b0a474c | ||
|
|
78ff5683ce | ||
|
|
53e881b75e | ||
|
|
fff2fcae21 | ||
|
|
a68a23d1cb | ||
|
|
cb1e1e5dca | ||
|
|
3b09cb3fae | ||
|
|
c10050a14c | ||
|
|
f8ef6939b0 | ||
|
|
52854b6502 | ||
|
|
fe5d1dd152 | ||
|
|
180e62d597 | ||
|
|
4870641d38 | ||
|
|
33e2f15f49 | ||
|
|
cf51f09826 | ||
|
|
8a75774506 | ||
|
|
7e8c57ee4d | ||
|
|
84331c4d08 | ||
|
|
ceac1be026 | ||
|
|
892d50ba7c | ||
|
|
c08d8b6bcb | ||
|
|
dde656c4c3 | ||
|
|
2d628e2b5a | ||
|
|
d7652a6db6 | ||
|
|
491d3521f7 | ||
|
|
d6854c412c | ||
|
|
110386a3dd | ||
|
|
7d96cd8fbd | ||
|
|
840a2e73a5 | ||
|
|
d033a7fa02 | ||
|
|
07c3a513ef | ||
|
|
18b467a4d8 | ||
|
|
ff8505c013 | ||
|
|
7207bb6293 | ||
|
|
0d6a62ada0 | ||
|
|
721c27c329 | ||
|
|
488198b7da | ||
|
|
702db43590 | ||
|
|
d8c5960c80 | ||
|
|
0942f61aff | ||
|
|
b83604a677 | ||
|
|
56df5b4054 | ||
|
|
cc079e842b | ||
|
|
37907dccfc | ||
|
|
701193e71a | ||
|
|
eff475c498 | ||
|
|
5235581e33 | ||
|
|
ec6b6b25a4 | ||
|
|
e5e685902c | ||
|
|
b0d97bde89 | ||
|
|
810db26bf4 | ||
|
|
5033c096a2 | ||
|
|
83cfaa5e77 | ||
|
|
29dae044cc | ||
|
|
7e83207444 | ||
|
|
9744b2266f | ||
|
|
9eb3ffcc30 | ||
|
|
917209cb65 | ||
|
|
0bf57ddd48 | ||
|
|
fb009ae52f | ||
|
|
2a5160c519 | ||
|
|
349b502149 | ||
|
|
1c1164d9e8 | ||
|
|
35386bbd2c | ||
|
|
96bd8e05aa | ||
|
|
85c1adb258 | ||
|
|
e540a42c9c | ||
|
|
93ff313237 | ||
|
|
60e2118001 | ||
|
|
49963c2e47 | ||
|
|
e883144a76 | ||
|
|
b6a3aef345 | ||
|
|
b49cc250e0 | ||
|
|
2e49ba10dd | ||
|
|
4042fb3a62 | ||
|
|
24ced381de | ||
|
|
e265482cb4 | ||
|
|
ba8346edb7 | ||
|
|
600f261dcb | ||
|
|
14efaee388 | ||
|
|
687682cab0 | ||
|
|
c3b4d59168 | ||
|
|
970e13773d | ||
|
|
af1422a1de | ||
|
|
4947350230 | ||
|
|
e29e20e779 | ||
|
|
3490f1d7ae | ||
|
|
d39d7fb1f5 | ||
|
|
8b3b684675 | ||
|
|
630b236ac0 | ||
|
|
5bb7cb47aa | ||
|
|
7d152b37ad | ||
|
|
0235a971d6 | ||
|
|
bb9bb83202 | ||
|
|
1a7458a290 | ||
|
|
00decf3347 | ||
|
|
6e1df743d0 | ||
|
|
c54a768b19 | ||
|
|
250e9b1805 | ||
|
|
43a91b2b4b | ||
|
|
e4742bcafb | ||
|
|
21f3dee554 | ||
|
|
889ae950fe | ||
|
|
272b28b6a9 | ||
|
|
9a259acefa | ||
|
|
92392da6fe | ||
|
|
0f6920a3f1 | ||
|
|
d2574c4b49 | ||
|
|
0e5b4bde62 | ||
|
|
916d147619 | ||
|
|
4f56cd4c09 | ||
|
|
ab17a995a4 | ||
|
|
3f562c3009 | ||
|
|
66443dd2c2 | ||
|
|
89849bc805 | ||
|
|
4fef14f9fc | ||
|
|
dce1f5492f | ||
|
|
0b1aabf345 | ||
|
|
57b6247a65 | ||
|
|
9529a991ef | ||
|
|
1f49665c05 | ||
|
|
0f73ab889f | ||
|
|
aa8e08e4d0 | ||
|
|
2a78f6902d | ||
|
|
558875c91c | ||
|
|
8ec0feb49b | ||
|
|
5b0874daef | ||
|
|
df8c1139a1 | ||
|
|
0d7bd746ac | ||
|
|
e95c7f144e | ||
|
|
10844e7a52 | ||
|
|
077a091f7d | ||
|
|
f88493870a | ||
|
|
3f6f0b3f9c | ||
|
|
5a0580005f | ||
|
|
1439f8bbeb | ||
|
|
bf8277fb2c | ||
|
|
d9c974fc00 | ||
|
|
23dfc05511 | ||
|
|
4d9a41bc76 | ||
|
|
db0059e0f0 | ||
|
|
2b27c2dc6a | ||
|
|
85e914c129 | ||
|
|
5710b2a7dd | ||
|
|
844139b339 | ||
|
|
79a94b29ce | ||
|
|
d03617d682 | ||
|
|
8494e29075 | ||
|
|
d229ba2405 | ||
|
|
4ab1e08129 | ||
|
|
4255867f29 | ||
|
|
87529f37c8 | ||
|
|
caaf82385b | ||
|
|
ffdcdeee9f | ||
|
|
0db9e84b58 | ||
|
|
82f647299c | ||
|
|
a85a18774f | ||
|
|
143d2a107a | ||
|
|
47258ebd7f | ||
|
|
095b7540d5 | ||
|
|
e871156301 | ||
|
|
35da8e2e67 | ||
|
|
5d4606d0f6 | ||
|
|
2533a844a7 | ||
|
|
62ea74f332 | ||
|
|
38345c1672 | ||
|
|
3a10d47903 | ||
|
|
12eeb97e71 | ||
|
|
4b1dc93792 | ||
|
|
5aa8474d2b | ||
|
|
58e13db613 | ||
|
|
9e3dd9b6d4 | ||
|
|
81902e1b96 | ||
|
|
2670db7ca1 | ||
|
|
bf43f32946 | ||
|
|
4455cfcde1 | ||
|
|
682c0c83a1 | ||
|
|
b9944e8669 | ||
|
|
b4207b3fd9 | ||
|
|
2b6b205a78 | ||
|
|
8c6d6df5fc | ||
|
|
18ae54f656 | ||
|
|
f6fb534035 | ||
|
|
4b0f2ea439 | ||
|
|
45a81e92f4 | ||
|
|
e0ced73d8a | ||
|
|
f1186a35d8 | ||
|
|
933ba8a708 | ||
|
|
bd5d613a24 | ||
|
|
3da1fd4372 | ||
|
|
d007550adb | ||
|
|
170f2646e1 | ||
|
|
207f97a0eb | ||
|
|
577de9683b | ||
|
|
ec09d4a6ca | ||
|
|
1b04dd8032 | ||
|
|
24bfac89ae | ||
|
|
2a5594d7d5 | ||
|
|
44c8b024b0 | ||
|
|
9acd05f16c | ||
|
|
7e71450a76 | ||
|
|
6fbb7e39ec | ||
|
|
920c0eada5 | ||
|
|
97c1b13370 | ||
|
|
47d4e4f659 | ||
|
|
97258542be | ||
|
|
908aa3556e | ||
|
|
93679e62e8 | ||
|
|
09edfb8545 | ||
|
|
d7926f12ad | ||
|
|
e18167d8ae | ||
|
|
c3dc838acb | ||
|
|
7e26e6eac0 | ||
|
|
0c80abf5b9 | ||
|
|
a7692fe4ef | ||
|
|
05233ef853 | ||
|
|
8232d4a3f2 | ||
|
|
e6a4b9f2fe | ||
|
|
67c4531771 | ||
|
|
6f3f48d1e9 | ||
|
|
b7f77498b3 | ||
|
|
8d26944642 | ||
|
|
2f54ceb96e | ||
|
|
63a5701906 | ||
|
|
41cdef055e | ||
|
|
420053178e | ||
|
|
412dcd7736 | ||
|
|
d7c94b4d04 | ||
|
|
032c5166f4 | ||
|
|
4e42e3ef82 | ||
|
|
a71c7cbba2 | ||
|
|
781fbcddd4 | ||
|
|
1d151f2864 | ||
|
|
ffae4540f9 | ||
|
|
25b5f66e6b | ||
|
|
68c00af44e | ||
|
|
12571746d7 | ||
|
|
d90504675f | ||
|
|
249041764b | ||
|
|
554fde7a9d | ||
|
|
a49bfd00a4 | ||
|
|
bc4c08fe2f | ||
|
|
b906f77b37 | ||
|
|
e1f6e167fb | ||
|
|
034ff984cc | ||
|
|
fedcbb8917 | ||
|
|
fb5b505bab | ||
|
|
f020543599 | ||
|
|
7a4edd6ef7 | ||
|
|
2b5e136cc0 | ||
|
|
ab4d595904 | ||
|
|
b1247b5eac | ||
|
|
bed505fd91 | ||
|
|
ffea7cd65a | ||
|
|
8600aead20 | ||
|
|
f48879653a | ||
|
|
0c01ab76cf | ||
|
|
1eb30b28e1 | ||
|
|
6a749bdae5 | ||
|
|
9aa15c6fbb | ||
|
|
8aa8d61bb8 | ||
|
|
24657743ff | ||
|
|
6f35e00c16 | ||
|
|
20fd3a8f67 | ||
|
|
7666ccf2c7 | ||
|
|
0cc29d2c99 | ||
|
|
1b1222494e | ||
|
|
94c3dfe51e | ||
|
|
58d93cb083 | ||
|
|
f5bcc25e10 | ||
|
|
a31e2209c5 | ||
|
|
4d52a8e476 | ||
|
|
c3fb50c358 | ||
|
|
65115089af | ||
|
|
48bac90a7b | ||
|
|
93a38649f3 | ||
|
|
89c5cfb46b | ||
|
|
51808cd1f8 | ||
|
|
758eb77578 | ||
|
|
358f788b61 | ||
|
|
e0d4bf44b8 | ||
|
|
7ec10febae | ||
|
|
8acd495e91 | ||
|
|
5f11dd48a2 | ||
|
|
38343bfbf1 | ||
|
|
76ed7f180e | ||
|
|
6750bd9422 | ||
|
|
d51a7bc9d6 | ||
|
|
6cb545aaf0 | ||
|
|
4b8c4a5bcd | ||
|
|
0a9a191f27 | ||
|
|
83c1bd3ffd | ||
|
|
23c57ad033 | ||
|
|
cf1594bcbb | ||
|
|
10caa91099 | ||
|
|
6f209d26d8 | ||
|
|
6d777fb18f | ||
|
|
c007bee536 | ||
|
|
0f0e674f71 | ||
|
|
56ab86cddc | ||
|
|
8c9c0abee6 | ||
|
|
ae01850ce1 | ||
|
|
319aaaa81d | ||
|
|
4f4280de58 | ||
|
|
dbd2da4923 | ||
|
|
16fcca7a2f | ||
|
|
a803d5a4ec | ||
|
|
d888e82de6 | ||
|
|
07adda3fa6 | ||
|
|
3416e0632d | ||
|
|
6396b38a6f | ||
|
|
25d9e4a195 | ||
|
|
b109d866dd | ||
|
|
075b7f9bd7 | ||
|
|
fc16dc1330 | ||
|
|
28282f1c35 | ||
|
|
67492e744d | ||
|
|
288b37a1b3 | ||
|
|
60e64e1a1d | ||
|
|
18bab30c59 | ||
|
|
a37441b221 | ||
|
|
245d5095b5 | ||
|
|
62f3007d2a | ||
|
|
f9b27d7d01 | ||
|
|
885bfeaf38 | ||
|
|
2d6e6c887e | ||
|
|
afae1a3aae | ||
|
|
9081421eba | ||
|
|
eb8c4e9ee9 | ||
|
|
81c1c206d4 | ||
|
|
063df3243d | ||
|
|
af3e235348 | ||
|
|
ffb5ca4b5b | ||
|
|
866be01860 | ||
|
|
32c05e6a3d | ||
|
|
f1e1e6b204 | ||
|
|
e1b892e8b4 | ||
|
|
da9046f745 | ||
|
|
51a27cba75 | ||
|
|
0e931b0be0 | ||
|
|
ea8e49c870 | ||
|
|
172f943891 | ||
|
|
e6965580a0 | ||
|
|
a9b772ef99 | ||
|
|
c2d1d9b4bf | ||
|
|
d6ee4f49d2 | ||
|
|
05889b54cb | ||
|
|
952d23de08 | ||
|
|
c8e3641bbc | ||
|
|
a12d756d79 | ||
|
|
406900e253 | ||
|
|
ff9030f5fb | ||
|
|
316516422b | ||
|
|
58120e299e | ||
|
|
46a9888647 | ||
|
|
a542e93fe7 | ||
|
|
03ea57dab8 | ||
|
|
9d1e40c5f7 | ||
|
|
a3c3f8df72 | ||
|
|
4fa7deed5a | ||
|
|
c0e73091f5 | ||
|
|
c449a687cf | ||
|
|
23f5b72943 | ||
|
|
9ec6fa06f8 | ||
|
|
9433dc7769 | ||
|
|
9508b136ff | ||
|
|
157d46b73a | ||
|
|
a37bb4c3be | ||
|
|
5c9a502657 | ||
|
|
2331b0911d | ||
|
|
f91401cb95 | ||
|
|
da94644c38 | ||
|
|
aeca25d984 | ||
|
|
cef885db72 | ||
|
|
a780b5f179 | ||
|
|
3b4451a2d1 | ||
|
|
83e8265d5b |
No files matched your search
@@ -0,0 +1,32 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# Top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Matches all C source files
|
||||
[*.c]
|
||||
indent_style = tab
|
||||
tab_width = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Matches all header files
|
||||
[*.h]
|
||||
indent_style = tab
|
||||
tab_width = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Matches Makefiles
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# Matches Markdown files
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1,323 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "port" ]
|
||||
pull_request:
|
||||
branches: [ "port" ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-i686-windows:
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: Install MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW32
|
||||
update: true
|
||||
install: >-
|
||||
git
|
||||
make
|
||||
mingw-w64-i686-toolchain
|
||||
mingw-w64-i686-SDL2
|
||||
mingw-w64-i686-zlib
|
||||
mingw-w64-i686-cmake
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Build pd.i686.exe
|
||||
run: cmake -G"Unix Makefiles" -Bbuild . && cmake --build build -j
|
||||
- name: Build pd.pal.i686.exe
|
||||
run: cmake -G"Unix Makefiles" -Bbuild_pal -DROMID=pal-final . && cmake --build build_pal -j
|
||||
- name: Build pd.jpn.i686.exe
|
||||
run: cmake -G"Unix Makefiles" -Bbuild_jpn -DROMID=jpn-final . && cmake --build build_jpn -j
|
||||
- name: Prepare artifact for packaging
|
||||
run: |
|
||||
mkdir -p bin/data
|
||||
cp build/pd.i686.exe bin/
|
||||
cp build_pal/pd.pal.i686.exe bin/
|
||||
cp build_jpn/pd.jpn.i686.exe bin/
|
||||
cp /mingw32/bin/{SDL2.dll,zlib1.dll,libgcc_s_dw2-1.dll,libwinpthread-1.dll} bin/
|
||||
touch bin/data/put_your_rom_here.txt
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pd-i686-windows
|
||||
path: bin/
|
||||
retention-days: 0
|
||||
|
||||
build-i686-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install git gcc g++ gcc-multilib g++-multilib make libsdl2-dev zlib1g-dev libsdl2-dev:i386 zlib1g-dev:i386 cmake
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Build pd.i686
|
||||
run: cmake -G"Unix Makefiles" -Bbuild -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 . && cmake --build build -j
|
||||
- name: Build pd.pal.i686
|
||||
run: cmake -G"Unix Makefiles" -Bbuild_pal -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 -DROMID=pal-final . && cmake --build build_pal -j
|
||||
- name: Build pd.jpn.i686
|
||||
run: cmake -G"Unix Makefiles" -Bbuild_jpn -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 -DROMID=jpn-final . && cmake --build build_jpn -j
|
||||
- name: Prepare artifact for packaging
|
||||
run: |
|
||||
mkdir -p bin/data
|
||||
cp build/pd.i686 bin/
|
||||
cp build_pal/pd.pal.i686 bin/
|
||||
cp build_jpn/pd.jpn.i686 bin/
|
||||
touch bin/data/put_your_rom_here.txt
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pd-i686-linux
|
||||
path: bin/
|
||||
retention-days: 0
|
||||
|
||||
build-x86_64-windows:
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: Install MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
update: true
|
||||
install: >-
|
||||
git
|
||||
make
|
||||
mingw-w64-x86_64-toolchain
|
||||
mingw-w64-x86_64-python
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-zlib
|
||||
mingw-w64-x86_64-cmake
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Build pd.x86_64.exe
|
||||
run: cmake -G"Unix Makefiles" -Bbuild . && cmake --build build -j
|
||||
- name: Build pd.pal.x86_64.exe
|
||||
run: cmake -G"Unix Makefiles" -Bbuild_pal -DROMID=pal-final . && cmake --build build_pal -j
|
||||
- name: Build pd.jpn.x86_64.exe
|
||||
run: cmake -G"Unix Makefiles" -Bbuild_jpn -DROMID=jpn-final . && cmake --build build_jpn -j
|
||||
- name: Prepare artifact for packaging
|
||||
run: |
|
||||
mkdir -p bin/data
|
||||
cp build/pd.x86_64.exe bin/
|
||||
cp build_pal/pd.pal.x86_64.exe bin/
|
||||
cp build_jpn/pd.jpn.x86_64.exe bin/
|
||||
cp /mingw64/bin/{SDL2.dll,zlib1.dll,libwinpthread-1.dll,libgcc_s_seh-1.dll} bin/
|
||||
touch bin/data/put_your_rom_here.txt
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pd-x86_64-windows
|
||||
path: bin/
|
||||
retention-days: 0
|
||||
|
||||
build-x86_64-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo dpkg --add-architecture amd64
|
||||
sudo apt-get update
|
||||
sudo apt-get install git gcc g++ gcc-multilib g++-multilib make libsdl2-dev zlib1g-dev libsdl2-2.0-0 cmake
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Build pd.x86_64
|
||||
run: cmake -G"Unix Makefiles" -Bbuild . && cmake --build build -j
|
||||
- name: Build pd.pal.x86_64
|
||||
run: cmake -G"Unix Makefiles" -Bbuild_pal -DROMID=pal-final . && cmake --build build_pal -j
|
||||
- name: Build pd.jpn.x86_64
|
||||
run: cmake -G"Unix Makefiles" -Bbuild_jpn -DROMID=jpn-final . && cmake --build build_jpn -j
|
||||
- name: Prepare artifact for packaging
|
||||
run: |
|
||||
mkdir -p bin/data
|
||||
cp build/pd.x86_64 bin/
|
||||
cp build_pal/pd.pal.x86_64 bin/
|
||||
cp build_jpn/pd.jpn.x86_64 bin/
|
||||
touch bin/data/put_your_rom_here.txt
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pd-x86_64-linux
|
||||
path: bin/
|
||||
retention-days: 0
|
||||
|
||||
build-x86_64-osx:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install zlib
|
||||
wget http://libsdl.org/release/SDL2-2.30.9.dmg -O SDL2.dmg
|
||||
hdiutil mount SDL2.dmg
|
||||
sudo cp -vr /Volumes/SDL2/SDL2.framework /Library/Frameworks
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Build pd.x86_64
|
||||
run: |
|
||||
cmake -G"Unix Makefiles" -Bbuild -DCMAKE_OSX_ARCHITECTURES=x86_64 .
|
||||
cmake --build build --target pd -j4 --clean-first
|
||||
- name: Build pd.pal.x86_64
|
||||
run: |
|
||||
cmake -G"Unix Makefiles" -Bbuild_pal -DCMAKE_OSX_ARCHITECTURES=x86_64 -DROMID=pal-final .
|
||||
cmake --build build_pal --target pd -j4 --clean-first
|
||||
- name: Build pd.jpn.x86_64
|
||||
run: |
|
||||
cmake -G"Unix Makefiles" -Bbuild_jpn -DCMAKE_OSX_ARCHITECTURES=x86_64 -DROMID=jpn-final .
|
||||
cmake --build build_jpn --target pd -j4 --clean-first
|
||||
- name: Prepare artifact for packaging
|
||||
run: |
|
||||
mkdir -p bin/data
|
||||
cp build/pd.x86_64 bin/
|
||||
cp build_pal/pd.pal.x86_64 bin/
|
||||
cp build_jpn/pd.jpn.x86_64 bin/
|
||||
cp -vr /Library/Frameworks/SDL2.framework bin/
|
||||
touch bin/data/put_your_rom_here.txt
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pd-x86_64-osx
|
||||
path: bin/
|
||||
retention-days: 0
|
||||
|
||||
build-arm64-osx:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install zlib
|
||||
wget http://libsdl.org/release/SDL2-2.30.9.dmg -O SDL2.dmg
|
||||
hdiutil mount SDL2.dmg
|
||||
sudo cp -vr /Volumes/SDL2/SDL2.framework /Library/Frameworks
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Build pd.arm64
|
||||
run: |
|
||||
cmake -G"Unix Makefiles" -Bbuild -DCMAKE_OSX_ARCHITECTURES=arm64 .
|
||||
cmake --build build --target pd -j4 --clean-first
|
||||
- name: Build pd.pal.arm64
|
||||
run: |
|
||||
cmake -G"Unix Makefiles" -Bbuild_pal -DCMAKE_OSX_ARCHITECTURES=arm64 -DROMID=pal-final .
|
||||
cmake --build build_pal --target pd -j4 --clean-first
|
||||
- name: Build pd.jpn.arm64
|
||||
run: |
|
||||
cmake -G"Unix Makefiles" -Bbuild_jpn -DCMAKE_OSX_ARCHITECTURES=arm64 -DROMID=jpn-final .
|
||||
cmake --build build_jpn --target pd -j4 --clean-first
|
||||
- name: Prepare artifact for packaging
|
||||
run: |
|
||||
mkdir -p bin/data
|
||||
cp build/pd.arm64 bin/
|
||||
cp build_pal/pd.pal.arm64 bin/
|
||||
cp build_jpn/pd.jpn.arm64 bin/
|
||||
cp -vr /Library/Frameworks/SDL2.framework bin/
|
||||
touch bin/data/put_your_rom_here.txt
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pd-arm64-osx
|
||||
path: bin/
|
||||
retention-days: 0
|
||||
|
||||
build-arm64-nswitch:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/port'
|
||||
steps:
|
||||
- name: Install host dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install git make cmake curl
|
||||
- name: Install devkitA64 docker image
|
||||
run: sudo docker pull devkitpro/devkita64:latest
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Build pd.arm64.nro
|
||||
run: sudo docker run --name pd --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:latest bash ./tools/buildscripts/nswitch_docker.sh build
|
||||
- name: Build pd.pal.arm64.nro
|
||||
run: sudo docker run --name pd --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:latest bash ./tools/buildscripts/nswitch_docker.sh build_pal -DROMID=pal-final
|
||||
- name: Build pd.jpn.arm64.nro
|
||||
run: sudo docker run --name pd --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:latest bash ./tools/buildscripts/nswitch_docker.sh build_jpn -DROMID=jpn-final
|
||||
- name: Prepare artifact for packaging
|
||||
run: |
|
||||
mkdir -p bin/perfectdark/data
|
||||
mkdir -p bin/perfectdark_pal/data
|
||||
mkdir -p bin/perfectdark_jpn/data
|
||||
cp build/pd.arm64.nro bin/perfectdark/
|
||||
cp build_pal/pd.pal.arm64.nro bin/perfectdark_pal/
|
||||
cp build_jpn/pd.jpn.arm64.nro bin/perfectdark_jpn/
|
||||
touch bin/perfectdark/data/put_your_rom_here.txt
|
||||
touch bin/perfectdark_pal/data/put_your_rom_here.txt
|
||||
touch bin/perfectdark_jpn/data/put_your_rom_here.txt
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pd-arm64-nswitch
|
||||
path: bin/
|
||||
retention-days: 0
|
||||
|
||||
build-flatpak-bundle:
|
||||
name: "Build flatpak bundle"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-x86_64-linux]
|
||||
container:
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08
|
||||
options: --privileged
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: pd-x86_64-linux
|
||||
path: dist/linux/bin/
|
||||
|
||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: io.github.perfect_dark_pc_port.perfect_dark.flatpak
|
||||
manifest-path: dist/linux/io.github.perfect_dark_pc_port.perfect_dark.yaml
|
||||
cache-key: flatpak-builder-${{ github.sha }}
|
||||
upload-artifact: false
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: io.github.perfect_dark_pc_port.perfect_dark.flatpak
|
||||
path: io.github.perfect_dark_pc_port.perfect_dark.flatpak
|
||||
retention-days: 0
|
||||
|
||||
|
||||
publish-latest-build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/port'
|
||||
needs: [build-i686-windows, build-i686-linux, build-x86_64-windows, build-x86_64-linux, build-arm64-nswitch, build-x86_64-osx, build-arm64-osx, build-flatpak-bundle]
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ci-artifacts
|
||||
- name: Publish latest dev-build
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
mkdir ci-release
|
||||
pushd ci-artifacts
|
||||
tar czf ../ci-release/pd-i686-linux.tar.gz pd-i686-linux
|
||||
tar czf ../ci-release/pd-x86_64-linux.tar.gz pd-x86_64-linux
|
||||
tar cJf ../ci-release/pd-x86_64-osx.tar.xz pd-x86_64-osx
|
||||
tar cJf ../ci-release/pd-arm64-osx.tar.xz pd-arm64-osx
|
||||
zip -r ../ci-release/pd-i686-windows.zip pd-i686-windows
|
||||
zip -r ../ci-release/pd-x86_64-windows.zip pd-x86_64-windows
|
||||
zip -r ../ci-release/pd-arm64-nswitch.zip pd-arm64-nswitch
|
||||
mv io.github.perfect_dark_pc_port.perfect_dark.flatpak/io.github.perfect_dark_pc_port.perfect_dark.flatpak ../ci-release/
|
||||
popd
|
||||
git tag --force ci-dev-build port
|
||||
git push --force origin ci-dev-build
|
||||
gh release upload --clobber ci-dev-build ci-release/*
|
||||
@@ -1,7 +1,10 @@
|
||||
*.o
|
||||
*.pyc
|
||||
*.dll
|
||||
*.exe
|
||||
pd.*.z64
|
||||
build
|
||||
.flatpak-builder
|
||||
extracted
|
||||
src/assets/*/*.bin
|
||||
src/assets/*/*.ctl
|
||||
@@ -19,3 +22,7 @@ src/assets/*/sequences/*.seq
|
||||
src/assets/*/textures/*.bin
|
||||
src/generated
|
||||
tools/mkrom/mkrom
|
||||
/.vs
|
||||
CppProperties.json
|
||||
.idea
|
||||
cmake-build-debug
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"clang-format.enable": false,
|
||||
"C_Cpp.clang_format_style": "none",
|
||||
"[c]": {
|
||||
"editor.formatOnSave": false,
|
||||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnType": false
|
||||
},
|
||||
"[cpp]": {
|
||||
"editor.formatOnSave": false,
|
||||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnType": false
|
||||
}
|
||||
}
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
if(APPLE)
|
||||
if(CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
|
||||
# ARM Macs start at OS 11.
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version")
|
||||
else()
|
||||
# SDL2 requires at least this version for everything to work properly.
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
project(pd C CXX)
|
||||
|
||||
include(TargetArch)
|
||||
include(FindPkgConfig)
|
||||
include(AssetUtils)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# On first build set the build type to RelWithDebInfo if it's not already set
|
||||
if (NOT EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt)
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Options
|
||||
|
||||
set(ROMID "ntsc-final" CACHE STRING "Which ROM to build an executable for: ntsc-final, pal-final or jpn-final")
|
||||
option(FORCE_CROSSCOMPILE "Force cross compiling" NO)
|
||||
|
||||
# Determine target arch
|
||||
|
||||
target_architecture(TARGET_ARCH)
|
||||
if(TARGET_ARCH STREQUAL "i386")
|
||||
# for historical reasons
|
||||
set(TARGET_ARCH "i686")
|
||||
endif()
|
||||
|
||||
# TODO
|
||||
set(TARGET_IS_BIG_ENDIAN FALSE)
|
||||
|
||||
if((TARGET_ARCH MATCHES "unknown") OR (TARGET_ARCH STREQUAL ""))
|
||||
message(FATAL_ERROR "Unknown target arch")
|
||||
elseif(TARGET_ARCH MATCHES "64")
|
||||
set(TARGET_IS_64BIT TRUE)
|
||||
else()
|
||||
set(TARGET_IS_64BIT FALSE)
|
||||
endif()
|
||||
|
||||
message(STATUS "Target arch: ${TARGET_ARCH}")
|
||||
|
||||
# Determine target OS and platform identifier ($arch-$os)
|
||||
|
||||
if(WIN32)
|
||||
# Windows
|
||||
set(TARGET_OS "windows")
|
||||
elseif(APPLE)
|
||||
# OSX
|
||||
set(TARGET_OS "osx")
|
||||
elseif(NINTENDO_SWITCH)
|
||||
# Switch
|
||||
set(TARGET_OS "nswitch")
|
||||
elseif(UNIX)
|
||||
# Generic *nix; assume Linux
|
||||
set(TARGET_OS "linux")
|
||||
else()
|
||||
# ???
|
||||
message(FATAL_ERROR "Unknown target OS or compiler")
|
||||
endif()
|
||||
|
||||
set(TARGET_PLATFORM "${TARGET_ARCH}-${TARGET_OS}")
|
||||
|
||||
message(STATUS "Target platform: ${TARGET_PLATFORM}")
|
||||
|
||||
# Calculate version info and binary name
|
||||
|
||||
set(VERSION_IS_PAL 0)
|
||||
|
||||
if(ROMID STREQUAL "ntsc-final")
|
||||
set(VERSION 2)
|
||||
set(BIN_REGION "")
|
||||
elseif(ROMID STREQUAL "pal-final")
|
||||
set(VERSION 4)
|
||||
set(BIN_REGION ".pal")
|
||||
set(VERSION_IS_PAL 1)
|
||||
elseif(ROMID STREQUAL "jpn-final")
|
||||
set(VERSION 5)
|
||||
set(BIN_REGION ".jpn")
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown ROM ID: ${ROMID}")
|
||||
endif()
|
||||
|
||||
# git commit hash
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE VERSION_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# git branch name
|
||||
execute_process(
|
||||
COMMAND git rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE VERSION_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
set(VERSION_TARGET "${TARGET_PLATFORM}")
|
||||
set(VERSION_ARCH "${TARGET_ARCH}")
|
||||
set(VERSION_ROMID "${ROMID}")
|
||||
set(VERSION_BUILD "${CMAKE_BUILD_TYPE}")
|
||||
|
||||
if((VERSION_BRANCH STREQUAL "") OR (VERSION_BRANCH STREQUAL "HEAD"))
|
||||
set(VERSION_BRANCH "port-custom")
|
||||
endif()
|
||||
|
||||
message(STATUS "Version: ${VERSION_ROMID}, hash ${VERSION_HASH}, branch ${VERSION_BRANCH}")
|
||||
|
||||
configure_file("${CMAKE_SOURCE_DIR}/port/include/versioninfo.h.in" "${CMAKE_BINARY_DIR}/port/include/versioninfo.h" @ONLY)
|
||||
|
||||
set(ASSET_DIR "src/assets/${ROMID}")
|
||||
set(GENERATED_DIR "src/generated/${ROMID}")
|
||||
|
||||
set(ENV{ROMID} ${ROMID})
|
||||
|
||||
# Build folder postfix
|
||||
|
||||
set(BUILD_ID "${VERSION_ROMID}-${VERSION_TARGET}")
|
||||
|
||||
# Compiler defs
|
||||
|
||||
add_definitions(
|
||||
-DMATCHING=0
|
||||
-DPIRACYCHECKS=0
|
||||
-DROM_SIZE=32
|
||||
-DAVOID_UB=1
|
||||
-D_LANGUAGE_C=1
|
||||
-DPAL=${VERSION_IS_PAL}
|
||||
-DVERSION=${VERSION}
|
||||
)
|
||||
|
||||
if(TARGET_IS_64BIT)
|
||||
add_definitions(-DPLATFORM_64BIT=1)
|
||||
endif()
|
||||
|
||||
# Compiler flags
|
||||
|
||||
# Include directories
|
||||
include_directories("${CMAKE_SOURCE_DIR}/include")
|
||||
include_directories("${CMAKE_SOURCE_DIR}/include/PR")
|
||||
include_directories("${CMAKE_SOURCE_DIR}/src/include")
|
||||
include_directories("${CMAKE_BINARY_DIR}/src/generated/${ROMID}")
|
||||
include_directories("${CMAKE_SOURCE_DIR}/src/lib/ultra/audio")
|
||||
include_directories("${CMAKE_SOURCE_DIR}/port/include")
|
||||
include_directories("${CMAKE_BINARY_DIR}/port/include")
|
||||
|
||||
# Set Release optimization to -Og until I fix the -O2 issues
|
||||
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
|
||||
add_compile_options(-Og)
|
||||
endif()
|
||||
|
||||
# Common CFLAGS
|
||||
add_compile_options(
|
||||
-fno-inline-functions
|
||||
-fno-strict-aliasing
|
||||
-funsigned-char
|
||||
-fwrapv
|
||||
-include "versioninfo.h"
|
||||
)
|
||||
|
||||
# Common warning flags
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wno-address
|
||||
-Wno-int-in-bool-context
|
||||
-Wno-misleading-indentation
|
||||
-Wno-missing-braces
|
||||
-Wno-multichar
|
||||
-Wno-tautological-compare
|
||||
-Wno-unused-but-set-variable
|
||||
-Wno-unused-value
|
||||
-Wno-unused-variable
|
||||
-fdiagnostics-color=always
|
||||
)
|
||||
|
||||
# C specific flags
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-pointer-sign>)
|
||||
|
||||
# C++ specific flags
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-unused-function>)
|
||||
|
||||
# Platform-specific flags
|
||||
if (APPLE)
|
||||
# Make it look for libs in the executable folder manually since CMake's rpath system is wack
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rpath @executable_path")
|
||||
else()
|
||||
# This warning is not recognized by XCode/Clang for some reason.
|
||||
add_compile_options(-Wno-format-truncation)
|
||||
endif()
|
||||
|
||||
# Dependencies
|
||||
|
||||
set(LIBS )
|
||||
|
||||
# SDL2
|
||||
find_package(SDL2 REQUIRED)
|
||||
if(NINTENDO_SWITCH)
|
||||
if(NOT SDL2_LIBRARY OR NOT SDL2_INCLUDE_DIR)
|
||||
# sometimes FindSDL2 just wants us to suffer and does not set these
|
||||
set(SDL2_LIBRARY "SDL2")
|
||||
set(SDL2_INCLUDE_DIR "${DEVKITPRO}/portlibs/switch/include/SDL2")
|
||||
endif()
|
||||
endif()
|
||||
include_directories(SYSTEM "${SDL2_INCLUDE_DIR}")
|
||||
list(APPEND LIBS "${SDL2_LIBRARY}")
|
||||
|
||||
# ZLib
|
||||
find_package(ZLIB REQUIRED)
|
||||
if(ZLIB_INCLUDE_DIR)
|
||||
include_directories(SYSTEM "${ZLIB_INCLUDE_DIR}")
|
||||
endif()
|
||||
list(APPEND LIBS "${ZLIB_LIBRARY}")
|
||||
|
||||
# GL
|
||||
if(NOT GL_LIBRARY)
|
||||
if(WIN32)
|
||||
set(GL_LIBRARY opengl32)
|
||||
elseif(NINTENDO_SWITCH)
|
||||
set(GL_LIBRARY EGL glapi drm_nouveau)
|
||||
elseif(APPLE)
|
||||
set(GL_LIBRARY "-framework OpenGL")
|
||||
else()
|
||||
set(GL_LIBRARY GL)
|
||||
endif()
|
||||
endif()
|
||||
list(APPEND LIBS "${GL_LIBRARY}")
|
||||
|
||||
# Platform-specific extra libs
|
||||
if(NOT EXTRA_LIBRARIES)
|
||||
if(WIN32)
|
||||
set(EXTRA_LIBRARIES dbghelp -static-libstdc++)
|
||||
elseif(NINTENDO_SWITCH)
|
||||
set(EXTRA_LIBRARIES stdc++ pthread)
|
||||
else()
|
||||
set(EXTRA_LIBRARIES stdc++ m dl)
|
||||
endif()
|
||||
endif()
|
||||
list(APPEND LIBS "${EXTRA_LIBRARIES}")
|
||||
|
||||
# Sources
|
||||
|
||||
file(GLOB_RECURSE SRC_PORT "${CMAKE_SOURCE_DIR}/port/*.c")
|
||||
file(GLOB_RECURSE SRC_PORT_CXX "${CMAKE_SOURCE_DIR}/port/*.cpp")
|
||||
|
||||
file(GLOB_RECURSE SRC_GAME "${CMAKE_SOURCE_DIR}/src/game/*.c")
|
||||
|
||||
file(GLOB SRC_LIB_MP3 "${CMAKE_SOURCE_DIR}/src/lib/mp3/*.c")
|
||||
file(GLOB SRC_LIB_NAUDIO "${CMAKE_SOURCE_DIR}/src/lib/naudio/*.c")
|
||||
file(GLOB SRC_LIB_ULTRA_AUDIO "${CMAKE_SOURCE_DIR}/src/lib/ultra/audio/*.c")
|
||||
file(GLOB SRC_LIB_GU "${CMAKE_SOURCE_DIR}/src/lib/ultra/gu/*.c")
|
||||
set(SRC_LIB
|
||||
src/lib/ultra/io/vimodentsclan1.c
|
||||
src/lib/ultra/io/vimodepallan1.c
|
||||
src/lib/ultra/io/vimodempallan1.c
|
||||
src/lib/ultra/io/vitbl.c
|
||||
src/lib/ailist.c
|
||||
src/lib/anim.c
|
||||
src/lib/args.c
|
||||
src/lib/audiodma.c
|
||||
src/lib/audiomgr.c
|
||||
src/lib/base.c
|
||||
src/lib/collision.c
|
||||
src/lib/debughud.c
|
||||
src/lib/dma.c
|
||||
src/lib/fault.c
|
||||
src/lib/joy.c
|
||||
src/lib/lib_2f490_c.c
|
||||
src/lib/lib_2fc60.c
|
||||
src/lib/lib_3e3e0.c
|
||||
src/lib/lib_04f60nb.c
|
||||
src/lib/lib_17ce0.c
|
||||
src/lib/lib_034d0.c
|
||||
src/lib/lib_39c80.c
|
||||
src/lib/lib_15850.c
|
||||
src/lib/mema.c
|
||||
src/lib/memp.c
|
||||
src/lib/model.c
|
||||
src/lib/modelasm_c.c
|
||||
src/lib/mp3.c
|
||||
src/lib/mtx_c.c
|
||||
src/lib/mtx.c
|
||||
src/lib/music.c
|
||||
src/lib/path.c
|
||||
src/lib/profile.c
|
||||
src/lib/rdp.c
|
||||
src/lib/rmon.c
|
||||
src/lib/rng_c.c
|
||||
src/lib/rzip_c.c
|
||||
src/lib/snd.c
|
||||
src/lib/speaker.c
|
||||
src/lib/varsinit.c
|
||||
src/lib/vi.c
|
||||
src/textureconfig.c
|
||||
)
|
||||
list(PREPEND SRC_LIB "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
set(SRC
|
||||
${SRC_GAME}
|
||||
${SRC_PORT}
|
||||
${SRC_PORT_CXX}
|
||||
${SRC_LIB_MP3}
|
||||
${SRC_LIB_NAUDIO}
|
||||
${SRC_LIB_ULTRA_AUDIO}
|
||||
${SRC_LIB_GU}
|
||||
${SRC_LIB}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SRC "${CMAKE_SOURCE_DIR}/dist/windows/icon.rc")
|
||||
endif()
|
||||
|
||||
# Asset files
|
||||
|
||||
generate_asset_headers("lang/" "${CMAKE_SOURCE_DIR}/tools/assetmgr/mklang" "en" JSON_HEADERS)
|
||||
generate_asset_headers("pads/" "${CMAKE_SOURCE_DIR}/tools/assetmgr/mkpads" "" JSON_HEADERS)
|
||||
generate_asset_headers("tiles/" "${CMAKE_SOURCE_DIR}/tools/assetmgr/mktiles" "" JSON_HEADERS)
|
||||
generate_asset_headers("animations.json" "${CMAKE_SOURCE_DIR}/tools/assetmgr/mkanims" "" JSON_HEADERS)
|
||||
generate_asset_headers("sequences.json" "${CMAKE_SOURCE_DIR}/tools/assetmgr/mksequences" "" JSON_HEADERS)
|
||||
generate_asset_headers("textures.json" "${CMAKE_SOURCE_DIR}/tools/assetmgr/mktextures" "" JSON_HEADERS)
|
||||
|
||||
add_custom_target(pd_headers ALL DEPENDS ${JSON_HEADERS})
|
||||
|
||||
# Game target
|
||||
|
||||
set(BIN_NAME "pd${BIN_REGION}.${TARGET_ARCH}")
|
||||
add_executable(pd ${SRC})
|
||||
add_dependencies(pd pd_headers)
|
||||
target_link_libraries(pd ${LIBS})
|
||||
set_target_properties(pd PROPERTIES OUTPUT_NAME "${BIN_NAME}")
|
||||
if(WIN32)
|
||||
set_target_properties(pd PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
endif()
|
||||
|
||||
# Additional commands for specific platforms.
|
||||
|
||||
if(NINTENDO_SWITCH)
|
||||
add_custom_command(TARGET pd POST_BUILD
|
||||
COMMAND nacptool --create "Perfect Dark" "fgsfds and contributors" "${VERSION_ROMID}/${VERSION_BRANCH}/${VERSION_HASH}" "${BIN_NAME}.nacp"
|
||||
COMMAND aarch64-none-elf-strip -o ${CMAKE_BINARY_DIR}/${BIN_NAME}.stripped.elf ${CMAKE_BINARY_DIR}/${BIN_NAME}.elf
|
||||
COMMAND elf2nro ${CMAKE_BINARY_DIR}/${BIN_NAME}.stripped.elf ${CMAKE_BINARY_DIR}/${BIN_NAME}.nro --icon=${CMAKE_SOURCE_DIR}/dist/nswitch/icon.jpg --nacp=${BIN_NAME}.nacp
|
||||
)
|
||||
endif()
|
||||
@@ -1,808 +0,0 @@
|
||||
################################################################################
|
||||
# Options
|
||||
|
||||
# ROMID - The ROM version to build.
|
||||
#
|
||||
# Can be set in your environment using
|
||||
# $ export ROMID=...
|
||||
#
|
||||
# Supported values:
|
||||
# * ntsc-beta
|
||||
# * ntsc-1.0
|
||||
# * ntsc-final
|
||||
# * pal-beta
|
||||
# * pal-final
|
||||
# * jpn-final
|
||||
|
||||
ROMID ?= ntsc-final
|
||||
|
||||
# MATCHING - Whether to build a matching ROM (1) or a custom one (0).
|
||||
#
|
||||
# Build a matching ROM to prove that the decompilation is accurate.
|
||||
#
|
||||
# If enabled, all further options are ignored and are set automatically.
|
||||
|
||||
MATCHING = 1
|
||||
|
||||
# COMPILER - Choose which compiler to use.
|
||||
#
|
||||
# A matching build uses ido. For non-matching builds, use gcc for performance.
|
||||
#
|
||||
# gcc is currently only supported when ROMID is ntsc-1.0 or ntsc-final.
|
||||
#
|
||||
# Supported values: ido, gcc
|
||||
|
||||
COMPILER = gcc
|
||||
|
||||
# GCC_OPT_LVL - the optimisation level if building with gcc.
|
||||
#
|
||||
# Supported values: -O0, -O1, -O2, -Os
|
||||
|
||||
GCC_OPT_LVL = -Os
|
||||
|
||||
# PAL - Whether to use the PAL television standard (1) or NTSC (0).
|
||||
#
|
||||
# PAL runs at 50Hz while NTSC is 60Hz, and PAL uses different screen dimensions.
|
||||
|
||||
PAL = 0
|
||||
|
||||
# ROMALLOCATION
|
||||
#
|
||||
# The lib, data and game segments are compressed in the final ROM.
|
||||
#
|
||||
# These options define how much space is allocated for the compressed segments.
|
||||
#
|
||||
# If any value is not big enough, the build system will tell you which one needs
|
||||
# to be changed and what to change it to.
|
||||
|
||||
ROMALLOCATION_DATA = 0x015000
|
||||
ROMALLOCATION_LIB = 0x038800
|
||||
ROMALLOCATION_GAME = 0x144ee0
|
||||
|
||||
# ROM_SIZE - The desired ROM size in megabytes.
|
||||
#
|
||||
# All versions of the retail ROM are 32MB.
|
||||
#
|
||||
# If this is too low you might get this error from ld:
|
||||
# "final link failed: memory exhausted"
|
||||
|
||||
ROM_SIZE := 32
|
||||
|
||||
# DEBUG - Enable (1) or disable (0) the debug menu and crash screen.
|
||||
#
|
||||
# When enabled, press C-up and C-down simultaneously to toggle the debug menu.
|
||||
# Note that some emulators have problems displaying the crash screen text.
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
################################################################################
|
||||
|
||||
# The VERSION constant is used in the source to handle version-specific code.
|
||||
ifeq ($(ROMID), ntsc-beta)
|
||||
VERSION = 0
|
||||
endif
|
||||
ifeq ($(ROMID), ntsc-1.0)
|
||||
VERSION = 1
|
||||
endif
|
||||
ifeq ($(ROMID), ntsc-final)
|
||||
VERSION = 2
|
||||
endif
|
||||
ifeq ($(ROMID), pal-beta)
|
||||
VERSION = 3
|
||||
endif
|
||||
ifeq ($(ROMID), pal-final)
|
||||
VERSION = 4
|
||||
endif
|
||||
ifeq ($(ROMID), jpn-final)
|
||||
VERSION = 5
|
||||
endif
|
||||
|
||||
# Set runtime piracy checks off. We turn this on for matching builds if needed.
|
||||
PIRACYCHECKS = 0
|
||||
|
||||
# A magic value that's only used to produce a matching ROM.
|
||||
# It's required because old-school zlib was influenced by uninitialised data.
|
||||
# ZIPMAGIC is the value of the uninitialised data. We set it per version for
|
||||
# matching builds, otherwise 0 is fine.
|
||||
ZIPMAGIC = 0x0000
|
||||
|
||||
# This is also a value that's only used to produce a matching ROM.
|
||||
# The final ROM has some duplicate bytes, and there doesn't seem to be any logic
|
||||
# for determining how many bytes are duplicated.
|
||||
COPYLEN = 2
|
||||
|
||||
# If a matching build has been requested, override directives as needed.
|
||||
ifeq ($(MATCHING), 1)
|
||||
COMPILER = ido
|
||||
|
||||
ROMALLOCATION_DATA = 0x015000
|
||||
ROMALLOCATION_LIB = 0x038800
|
||||
|
||||
ROM_SIZE = 32
|
||||
DEBUG = 0
|
||||
|
||||
ifeq ($(ROMID), ntsc-beta)
|
||||
PAL = 0
|
||||
PIRACYCHECKS = 0
|
||||
ROMALLOCATION_DATA = 0x012000
|
||||
ROMALLOCATION_LIB = 0x02f800
|
||||
ROMALLOCATION_GAME = 0x105000
|
||||
DEBUG = 1
|
||||
endif
|
||||
ifeq ($(ROMID), ntsc-1.0)
|
||||
PAL = 0
|
||||
PIRACYCHECKS = 1
|
||||
ROMALLOCATION_GAME = 0x144ee0
|
||||
ZIPMAGIC = 0xffff
|
||||
endif
|
||||
ifeq ($(ROMID), ntsc-final)
|
||||
PAL = 0
|
||||
PIRACYCHECKS = 1
|
||||
ROMALLOCATION_GAME = 0x144ee0
|
||||
ZIPMAGIC = 0xffff
|
||||
endif
|
||||
ifeq ($(ROMID), pal-beta)
|
||||
PAL = 1
|
||||
PIRACYCHECKS = 1
|
||||
ROMALLOCATION_GAME = 0x1306f0
|
||||
ZIPMAGIC = 0x0c00
|
||||
COPYLEN = 6
|
||||
DEBUG = 1
|
||||
endif
|
||||
ifeq ($(ROMID), pal-final)
|
||||
PAL = 1
|
||||
PIRACYCHECKS = 1
|
||||
ROMALLOCATION_GAME = 0x1306f0
|
||||
ZIPMAGIC = 0xaf00
|
||||
endif
|
||||
ifeq ($(ROMID), jpn-final)
|
||||
PAL = 0
|
||||
PIRACYCHECKS = 1
|
||||
ROMALLOCATION_GAME = 0x1296f0
|
||||
ZIPMAGIC = 0x0002
|
||||
COPYLEN = 4
|
||||
endif
|
||||
endif
|
||||
|
||||
DEFINES := \
|
||||
VERSION=$(VERSION) \
|
||||
MATCHING=$(MATCHING) \
|
||||
PAL=$(PAL) \
|
||||
PIRACYCHECKS=$(PIRACYCHECKS) \
|
||||
ROM_SIZE=$(ROM_SIZE)
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
DEFINES := $(DEFINES) DEBUG=1
|
||||
endif
|
||||
|
||||
C_DEFINES := $(foreach d,$(DEFINES),-D$(d))
|
||||
AS_DEFINES := $(foreach d,$(DEFINES),--defsym $(d)) --defsym _LANGUAGE_ASSEMBLY=1
|
||||
|
||||
A_DIR := src/assets/$(ROMID)
|
||||
B_DIR := build/$(ROMID)
|
||||
E_DIR := extracted/$(ROMID)
|
||||
|
||||
ifneq ($(strip $(MIPS_BINUTILS_PREFIX)),)
|
||||
TOOLCHAIN := $(MIPS_BINUTILS_PREFIX)
|
||||
else ifeq ($(shell type mips-elf-ld >/dev/null 2>/dev/null; echo $$?), 0)
|
||||
TOOLCHAIN := mips-elf
|
||||
else ifeq ($(shell type mips-linux-gnu-ld >/dev/null 2>/dev/null; echo $$?), 0)
|
||||
TOOLCHAIN := mips-linux-gnu
|
||||
else ifeq ($(shell type mips64-elf-ld >/dev/null 2>/dev/null; echo $$?), 0)
|
||||
TOOLCHAIN := mips64-elf
|
||||
else ifeq ($(shell type mips64-linux-gnu-ld >/dev/null 2>/dev/null; echo $$?), 0)
|
||||
TOOLCHAIN := mips64-linux-gnu
|
||||
else
|
||||
$(error You need to install a suitable MIPS binutils toolchain (eg. mips-elf-ld))
|
||||
endif
|
||||
|
||||
# Asset files are files which are included in the "files" segment of the ROM.
|
||||
# This excludes animations, fonts, SFX, MIDI and textures.
|
||||
# And because setup files are part of the files segment they are included here,
|
||||
# despite them being written in C.
|
||||
#
|
||||
# Create names such as $(B_DIR)/assets/files/PfooZ.
|
||||
# These names (with .o added) will be dependenices for ld.
|
||||
ASSET_FILES := \
|
||||
$(patsubst $(A_DIR)/files/audio/%.mp3, $(B_DIR)/assets/files/A%M, $(shell find $(A_DIR) -path '*/files/audio/*.mp3')) \
|
||||
$(patsubst $(A_DIR)/files/chrs/%.bin, $(B_DIR)/assets/files/C%Z, $(shell find $(A_DIR) -path '*/files/chrs/*.bin')) \
|
||||
$(patsubst $(A_DIR)/files/guns/%.bin, $(B_DIR)/assets/files/G%Z, $(shell find $(A_DIR) -path '*/files/guns/*.bin')) \
|
||||
$(patsubst $(A_DIR)/files/props/%.bin, $(B_DIR)/assets/files/P%Z, $(shell find $(A_DIR) -path '*/files/props/*.bin')) \
|
||||
$(patsubst src/setups/%.c, $(B_DIR)/assets/files/U%Z, $(shell find src/setups -name '*.c')) \
|
||||
$(patsubst $(A_DIR)/files/bgdata/%.seg, $(B_DIR)/assets/files/bgdata/%.seg, $(shell find $(A_DIR) -path '*/files/bgdata/*.seg')) \
|
||||
$(B_DIR)/assets/files/ob/ob_mid.seg.o
|
||||
|
||||
LANG_JSON_FILES := $(shell find $(A_DIR) -path '*/lang/*.json')
|
||||
PADS_JSON_FILES := $(shell find $(A_DIR) -path '*/pads/*.json')
|
||||
TILES_JSON_FILES := $(shell find $(A_DIR) -path '*/tiles/*.json')
|
||||
|
||||
LANG_O_FILES := \
|
||||
$(patsubst $(A_DIR)/lang/%.json, $(B_DIR)/assets/files/L%E.o, $(LANG_JSON_FILES)) \
|
||||
$(patsubst $(A_DIR)/lang/%.json, $(B_DIR)/assets/files/L%J.o, $(LANG_JSON_FILES)) \
|
||||
$(patsubst $(A_DIR)/lang/%.json, $(B_DIR)/assets/files/L%P.o, $(LANG_JSON_FILES)) \
|
||||
$(patsubst $(A_DIR)/lang/%.json, $(B_DIR)/assets/files/L%_str_f.o, $(LANG_JSON_FILES)) \
|
||||
$(patsubst $(A_DIR)/lang/%.json, $(B_DIR)/assets/files/L%_str_g.o, $(LANG_JSON_FILES)) \
|
||||
$(patsubst $(A_DIR)/lang/%.json, $(B_DIR)/assets/files/L%_str_i.o, $(LANG_JSON_FILES)) \
|
||||
$(patsubst $(A_DIR)/lang/%.json, $(B_DIR)/assets/files/L%_str_s.o, $(LANG_JSON_FILES))
|
||||
|
||||
PADS_O_FILES := $(patsubst $(A_DIR)/pads/%.json, $(B_DIR)/assets/files/bgdata/bg_%_padsZ.o, $(PADS_JSON_FILES))
|
||||
TILES_O_FILES := $(patsubst $(A_DIR)/tiles/%.json, $(B_DIR)/assets/files/bgdata/bg_%_tilesZ.o, $(TILES_JSON_FILES))
|
||||
|
||||
C_FILES := $(shell find src/lib src/game src/inflate -name '*.c')
|
||||
S_FILES := $(shell find src/lib src/game src/preamble -name '*.s')
|
||||
|
||||
C_O_FILES := $(patsubst src/%.c, $(B_DIR)/%.o, $(C_FILES))
|
||||
S_O_FILES := $(patsubst src/%.s, $(B_DIR)/%.o, $(S_FILES))
|
||||
|
||||
O_FILES := \
|
||||
$(C_O_FILES) \
|
||||
$(S_O_FILES) \
|
||||
$(patsubst %, %.o, $(ASSET_FILES)) \
|
||||
$(LANG_O_FILES) \
|
||||
$(PADS_O_FILES) \
|
||||
$(TILES_O_FILES) \
|
||||
$(B_DIR)/assets/animations.o \
|
||||
$(B_DIR)/assets/copyrightZ.o \
|
||||
$(B_DIR)/assets/files/list.o \
|
||||
$(B_DIR)/assets/fonts/bankgothic.o \
|
||||
$(B_DIR)/assets/fonts/handelgothiclg.o \
|
||||
$(B_DIR)/assets/fonts/handelgothicmd.o \
|
||||
$(B_DIR)/assets/fonts/handelgothicsm.o \
|
||||
$(B_DIR)/assets/fonts/handelgothicxs.o \
|
||||
$(B_DIR)/assets/fonts/numeric.o \
|
||||
$(B_DIR)/assets/fonts/ocralg.o \
|
||||
$(B_DIR)/assets/fonts/ocramd.o \
|
||||
$(B_DIR)/assets/fonts/tahoma.o \
|
||||
$(B_DIR)/assets/fonts/zurich.o \
|
||||
$(B_DIR)/assets/mpstrings/mpstringsE.o \
|
||||
$(B_DIR)/assets/mpstrings/mpstringsF.o \
|
||||
$(B_DIR)/assets/mpstrings/mpstringsG.o \
|
||||
$(B_DIR)/assets/mpstrings/mpstringsI.o \
|
||||
$(B_DIR)/assets/mpstrings/mpstringsJ.o \
|
||||
$(B_DIR)/assets/mpstrings/mpstringsP.o \
|
||||
$(B_DIR)/assets/mpstrings/mpstringsS.o \
|
||||
$(B_DIR)/assets/seq.ctl.o \
|
||||
$(B_DIR)/assets/seq.tbl.o \
|
||||
$(B_DIR)/assets/sequences.o \
|
||||
$(B_DIR)/assets/sfx.ctl.o \
|
||||
$(B_DIR)/assets/sfx.tbl.o \
|
||||
$(B_DIR)/assets/textureslist.o \
|
||||
$(B_DIR)/bootloader.o \
|
||||
$(B_DIR)/firingrange.o \
|
||||
$(B_DIR)/getitle.o \
|
||||
$(B_DIR)/mpconfigs.o \
|
||||
$(B_DIR)/romheader.o \
|
||||
$(B_DIR)/rsp/asp.data.o \
|
||||
$(B_DIR)/rsp/asp.text.o \
|
||||
$(B_DIR)/rsp/gsp.data.o \
|
||||
$(B_DIR)/rsp/gsp.text.o \
|
||||
$(B_DIR)/rsp/rspboot.text.o \
|
||||
$(B_DIR)/textureconfig.o
|
||||
|
||||
# ntsc-beta doesn't have this segment
|
||||
ifneq ($(ROMID), ntsc-beta)
|
||||
O_FILES := $(O_FILES) $(B_DIR)/assets/accessingpakZ.o
|
||||
endif
|
||||
|
||||
ifeq ($(ROMID), jpn-final)
|
||||
O_FILES := $(O_FILES) $(B_DIR)/assets/fonts/jpn.o
|
||||
else
|
||||
O_FILES := $(O_FILES) \
|
||||
$(B_DIR)/assets/fonts/jpnsingle.o \
|
||||
$(B_DIR)/assets/fonts/jpnmulti.o
|
||||
endif
|
||||
|
||||
INCLUDES = \
|
||||
-I include \
|
||||
-I include/PR \
|
||||
-I src/include \
|
||||
-I src/generated/$(ROMID) \
|
||||
-I src/lib/ultra/audio \
|
||||
|
||||
AS := $(TOOLCHAIN)-as
|
||||
ASFLAGS = -march=vr4300 -mabi=32 $(INCLUDES) $(AS_DEFINES)
|
||||
|
||||
ifeq ($(COMPILER), ido)
|
||||
RECOMP_FILES := \
|
||||
build/recomp/5.3/as1 \
|
||||
build/recomp/5.3/cc \
|
||||
build/recomp/5.3/cfe \
|
||||
build/recomp/5.3/err.english.cc \
|
||||
build/recomp/5.3/ugen \
|
||||
build/recomp/5.3/ujoin \
|
||||
build/recomp/5.3/uld \
|
||||
build/recomp/5.3/umerge \
|
||||
build/recomp/5.3/uopt \
|
||||
build/recomp/5.3/usplit \
|
||||
build/recomp/7.1/as1 \
|
||||
build/recomp/7.1/cc \
|
||||
build/recomp/7.1/cfe \
|
||||
build/recomp/7.1/err.english.cc \
|
||||
build/recomp/7.1/ugen \
|
||||
build/recomp/7.1/uopt
|
||||
|
||||
LOOPUNROLL_C_FILES := \
|
||||
src/lib/ultra/gu/frustum.c \
|
||||
src/lib/ultra/gu/mtxutil.c \
|
||||
src/lib/ultra/gu/ortho.c \
|
||||
src/lib/ultra/io/conteepread.c \
|
||||
src/lib/ultra/io/conteepwrite.c \
|
||||
src/lib/ultra/io/contpfs.c \
|
||||
src/lib/ultra/io/contramread.c \
|
||||
src/lib/ultra/io/contramwrite.c \
|
||||
src/lib/ultra/io/gbpakgetstatus.c \
|
||||
src/lib/ultra/io/gbpakreadid.c \
|
||||
src/lib/ultra/io/motor.c \
|
||||
src/lib/ultra/io/pfschecker.c \
|
||||
src/lib/ultra/io/pfsfreeblocks.c \
|
||||
src/lib/ultra/io/pfsgetstatus.c \
|
||||
src/lib/ultra/io/pfsinitpak2.c
|
||||
|
||||
MIPS3_C_FILES := \
|
||||
src/lib/rng_c.c \
|
||||
src/lib/ultra/libc/ll.c \
|
||||
src/lib/ultra/libc/llcvt.c
|
||||
|
||||
G_C_FILES := \
|
||||
$(shell find src/lib/ultra/audio -name '*.c') \
|
||||
$(shell find src/lib/naudio -name '*.c') \
|
||||
$(shell find src/lib/mp3 -name '*.c') \
|
||||
src/lib/lib_2fc60.c \
|
||||
src/lib/lib_317f0.c \
|
||||
src/lib/lib_39c80.c \
|
||||
src/lib/lib_3e3e0.c \
|
||||
src/lib/mp3.c \
|
||||
src/lib/speaker.c
|
||||
|
||||
O1_C_FILES := \
|
||||
src/lib/lib_04f60nb.c \
|
||||
src/lib/ultra/io/ai.c \
|
||||
src/lib/ultra/io/aisetnextbuf.c \
|
||||
src/lib/ultra/io/conteeplongread.c \
|
||||
src/lib/ultra/io/contquery.c \
|
||||
src/lib/ultra/io/controller.c \
|
||||
src/lib/ultra/io/dpctr.c \
|
||||
src/lib/ultra/io/pigetcmdq.c \
|
||||
src/lib/ultra/io/pidma.c \
|
||||
src/lib/ultra/io/si.c \
|
||||
src/lib/ultra/io/sp.c \
|
||||
src/lib/ultra/io/spsetpc.c \
|
||||
src/lib/ultra/io/sptaskyielded.c \
|
||||
src/lib/ultra/io/vi.c \
|
||||
src/lib/ultra/io/viblack.c \
|
||||
src/lib/ultra/io/vigetcurrframebuf.c \
|
||||
src/lib/ultra/io/vigetnextframebuf.c \
|
||||
src/lib/ultra/io/visetevent.c \
|
||||
src/lib/ultra/io/visetmode.c \
|
||||
src/lib/ultra/io/visetxscale.c \
|
||||
src/lib/ultra/io/visetyscale.c \
|
||||
src/lib/ultra/io/viswapbuf.c \
|
||||
src/lib/ultra/libc/ll.c \
|
||||
src/lib/ultra/libc/llcvt.c \
|
||||
src/lib/ultra/os/atomic.c \
|
||||
src/lib/ultra/os/createmesgqueue.c \
|
||||
src/lib/ultra/os/destroythread.c \
|
||||
src/lib/ultra/os/getmemsize.c \
|
||||
src/lib/ultra/os/gettime.c \
|
||||
src/lib/ultra/os/initialize2.c \
|
||||
src/lib/ultra/os/jammesg.c \
|
||||
src/lib/ultra/os/recvmesg.c \
|
||||
src/lib/ultra/os/resetglobalintmask.c \
|
||||
src/lib/ultra/os/sendmesg.c \
|
||||
src/lib/ultra/os/seteventmesg.c \
|
||||
src/lib/ultra/os/setglobalintmask.c \
|
||||
src/lib/ultra/os/setthreadpri.c \
|
||||
src/lib/ultra/os/settimer.c \
|
||||
src/lib/ultra/os/startthread.c \
|
||||
src/lib/ultra/os/stopthread.c \
|
||||
src/lib/ultra/os/stoptimer.c \
|
||||
src/lib/ultra/os/thread.c \
|
||||
src/lib/ultra/os/timerintr.c \
|
||||
src/lib/ultra/os/virtualtophysical.c \
|
||||
src/lib/ultra/os/yieldthread.c
|
||||
|
||||
ifeq ($(ROMID), ntsc-beta)
|
||||
O1_C_FILES := $(O1_C_FILES) src/lib/ultra/io/pfsisplug.c
|
||||
endif
|
||||
|
||||
O3_C_FILES := \
|
||||
src/lib/ultra/gu/align.c \
|
||||
src/lib/ultra/gu/frustum.c \
|
||||
src/lib/ultra/gu/ortho.c \
|
||||
src/lib/ultra/gu/scale.c \
|
||||
src/lib/ultra/libc/xprintf.c
|
||||
|
||||
IDO71_C_FILES := \
|
||||
$(shell find src/lib/ultra/audio -name '*.c') \
|
||||
$(shell find src/lib/naudio -name '*.c') \
|
||||
$(shell find src/lib/mp3 -name '*.c') \
|
||||
src/lib/lib_2fc60.c \
|
||||
src/lib/lib_39c80.c
|
||||
|
||||
LOOPUNROLL_O_FILES = $(patsubst src/%.c, $(B_DIR)/%.o, $(LOOPUNROLL_C_FILES))
|
||||
MIPS3_O_FILES = $(patsubst src/%.c, $(B_DIR)/%.o, $(MIPS3_C_FILES))
|
||||
G_O_FILES = $(patsubst src/%.c, $(B_DIR)/%.o, $(G_C_FILES))
|
||||
O1_O_FILES = $(patsubst src/%.c, $(B_DIR)/%.o, $(O1_C_FILES))
|
||||
O3_O_FILES = $(patsubst src/%.c, $(B_DIR)/%.o, $(O3_C_FILES))
|
||||
IDO71_O_FILES = $(patsubst src/%.c, $(B_DIR)/%.o, $(IDO71_C_FILES))
|
||||
|
||||
LOOPUNROLL := -Wo,-loopunroll,0
|
||||
MIPSISET := -mips2
|
||||
OPT_LVL := -O2
|
||||
CC := build/recomp/5.3/cc
|
||||
|
||||
$(LOOPUNROLL_O_FILES): LOOPUNROLL :=
|
||||
$(MIPS3_O_FILES): MIPSISET := -mips3
|
||||
$(G_O_FILES): OPT_LVL := -g
|
||||
$(O1_O_FILES): OPT_LVL := -O1
|
||||
$(O3_O_FILES): OPT_LVL := -O3
|
||||
$(IDO71_O_FILES): CC := build/recomp/7.1/cc
|
||||
|
||||
CFLAGS = $(C_DEFINES) $(INCLUDES) \
|
||||
$(LOOPUNROLL) \
|
||||
-Wab,-r4300_mul \
|
||||
-non_shared \
|
||||
-Olimit 2000 \
|
||||
-G 0 \
|
||||
-Xcpluscomm \
|
||||
-w2 \
|
||||
-Wab,-w \
|
||||
-woff 581,649,819,820,821,838,852 \
|
||||
$(MIPSISET) -32
|
||||
|
||||
# Files containing GLOBAL_ASM must be built with the asm_processor
|
||||
ifeq ($(MATCHING), 1)
|
||||
GLOBALASM_C_FILES != grep -rl 'GLOBAL_ASM(' $(C_FILES)
|
||||
GLOBALASM_O_FILES = $(patsubst src/%.c, $(B_DIR)/%.o, $(GLOBALASM_C_FILES))
|
||||
$(GLOBALASM_O_FILES): CC := /usr/bin/env python3 tools/asm_processor/build.py $(CC) -- $(TOOLCHAIN)-as $(ASFLAGS) --
|
||||
endif
|
||||
else ifeq ($(COMPILER), gcc)
|
||||
CC := $(TOOLCHAIN)-gcc
|
||||
|
||||
$(C_O_FILES): OPT_LVL := $(GCC_OPT_LVL)
|
||||
|
||||
CFLAGS := $(C_DEFINES) -DAVOID_UB=1 $(INCLUDES) \
|
||||
-EB \
|
||||
-march=vr4300 \
|
||||
-mabi=32 \
|
||||
-mfix4300 \
|
||||
-mdivide-breaks \
|
||||
-mno-abicalls \
|
||||
-ffreestanding \
|
||||
-fno-builtin \
|
||||
-fno-common \
|
||||
-fno-inline-functions \
|
||||
-fno-merge-constants \
|
||||
-fno-strict-aliasing \
|
||||
-fno-zero-initialized-in-bss \
|
||||
-funsigned-char \
|
||||
-fwrapv \
|
||||
-Wall \
|
||||
-Werror \
|
||||
-Wno-address \
|
||||
-Wno-aggressive-loop-optimizations \
|
||||
-Wno-array-bounds \
|
||||
-Wno-int-in-bool-context \
|
||||
-Wno-int-to-pointer-cast \
|
||||
-Wno-maybe-uninitialized \
|
||||
-Wno-misleading-indentation \
|
||||
-Wno-missing-braces \
|
||||
-Wno-multichar \
|
||||
-Wno-pointer-sign \
|
||||
-Wno-pointer-to-int-cast \
|
||||
-Wno-tautological-compare \
|
||||
-Wno-uninitialized \
|
||||
-Wno-unused-but-set-variable \
|
||||
-Wno-unused-value \
|
||||
-Wno-unused-variable \
|
||||
-G 0
|
||||
|
||||
$(B_DIR)/lib/ultra/libc/xldtob.o: CFLAGS += -ffast-math -fno-unsafe-math-optimizations
|
||||
else
|
||||
$(error The COMPILER option must be "ido" or "gcc")
|
||||
endif
|
||||
|
||||
# Make ROMID available as an environment variable to all tooling.
|
||||
# (We use this a lot)
|
||||
export ROMID
|
||||
|
||||
default: rom
|
||||
|
||||
################################################################################
|
||||
# Recomp
|
||||
# ------------------------------------------------------------------------------
|
||||
# The ido-static-recomp project is installed as a git submodule at tools/recomp.
|
||||
# The IRIX binaries are taken from that project and they are recompiled to
|
||||
# build/recomp/<version>/
|
||||
|
||||
tools/recomp/recomp.cpp:
|
||||
@echo "Please install ido-static-recomp by running:"
|
||||
@echo
|
||||
@echo " git submodule update --init --recursive"
|
||||
@echo
|
||||
@exit 1
|
||||
|
||||
build/recomp/recomp: tools/recomp/recomp.cpp
|
||||
@mkdir -p build/recomp/5.3
|
||||
@mkdir -p build/recomp/7.1
|
||||
g++ tools/recomp/recomp.cpp -o build/recomp/recomp -g -lcapstone
|
||||
|
||||
build/recomp/5.3/ugen:
|
||||
@mkdir -p build/recomp/5.3
|
||||
g++ tools/recomp/recomp.cpp -o build/recomp/recomp-ugen53 -g -lcapstone -Dugen53
|
||||
./build/recomp/recomp-ugen53 tools/recomp/ido/5.3/usr/lib/ugen > build/recomp/5.3/ugen.c
|
||||
gcc tools/recomp/libc_impl.c build/recomp/5.3/ugen.c -Itools/recomp -o build/recomp/5.3/ugen -O2 -fno-strict-aliasing -lm -no-pie -DIDO53
|
||||
|
||||
build/recomp/5.3/cc: build/recomp/recomp
|
||||
./build/recomp/recomp tools/recomp/ido/5.3/usr/bin/cc > build/recomp/5.3/cc.c
|
||||
gcc tools/recomp/libc_impl.c build/recomp/5.3/cc.c -Itools/recomp -o build/recomp/5.3/cc -O2 -fno-strict-aliasing -lm -no-pie -DIDO53
|
||||
|
||||
build/recomp/5.3/%: build/recomp/recomp
|
||||
./build/recomp/recomp tools/recomp/ido/5.3/usr/lib/$* > build/recomp/5.3/$*.c
|
||||
gcc tools/recomp/libc_impl.c build/recomp/5.3/$*.c -Itools/recomp -o build/recomp/5.3/$* -O2 -fno-strict-aliasing -lm -no-pie -DIDO53
|
||||
|
||||
build/recomp/7.1/cc: build/recomp/recomp
|
||||
./build/recomp/recomp tools/recomp/ido/7.1/usr/bin/cc > build/recomp/7.1/cc.c
|
||||
gcc tools/recomp/libc_impl.c build/recomp/7.1/cc.c -Itools/recomp -o build/recomp/7.1/cc -O2 -fno-strict-aliasing -lm -no-pie -DIDO71
|
||||
|
||||
build/recomp/7.1/%: build/recomp/recomp
|
||||
./build/recomp/recomp tools/recomp/ido/7.1/usr/lib/$* > build/recomp/7.1/$*.c
|
||||
gcc tools/recomp/libc_impl.c build/recomp/7.1/$*.c -Itools/recomp -o build/recomp/7.1/$* -O2 -fno-strict-aliasing -lm -no-pie -DIDO71
|
||||
|
||||
build/recomp/%/err.english.cc:
|
||||
@mkdir -p build/recomp/$?
|
||||
cp tools/err.english.cc build/recomp/$*
|
||||
|
||||
################################################################################
|
||||
# ROM Finalisation Stages
|
||||
|
||||
# Link all objects together with ld to make stage1.elf. In this stage, the game,
|
||||
# lib and data segments are uncompressed and placed past the end of the ROM.
|
||||
$(B_DIR)/stage1.elf: $(O_FILES) ld/pd.ld
|
||||
cpp -DROMID=$(ROMID) -DVERSION=$(VERSION) -DROMALLOCATION_DATA=$(ROMALLOCATION_DATA) -DROMALLOCATION_LIB=$(ROMALLOCATION_LIB) -DROMALLOCATION_GAME=$(ROMALLOCATION_GAME) -DROM_SIZE=$(ROM_SIZE) -P ld/pd.ld -o $(B_DIR)/pd.ld
|
||||
$(TOOLCHAIN)-ld --no-check-sections -z muldefs -T $(B_DIR)/pd.ld --print-map -o $@ > $(B_DIR)/pd.map
|
||||
|
||||
$(B_DIR)/stage1.bin: $(B_DIR)/stage1.elf
|
||||
$(TOOLCHAIN)-objcopy $< $@ -O binary
|
||||
|
||||
# Build the final ROM from stage1.bin using mkrom
|
||||
# mkrom handles calculating the piracy checksums, zipping segments and
|
||||
# calculating the ROM checksum.
|
||||
$(B_DIR)/pd.z64: $(B_DIR)/stage1.bin tools/mkrom/mkrom
|
||||
tools/mkrom/mkrom $(B_DIR)/stage1.bin $(B_DIR)/pd.map $(PIRACYCHECKS) $(ZIPMAGIC) $(COPYLEN) $@
|
||||
@echo -e "\033[0;32mROM written to $@\033[0m"
|
||||
|
||||
tools/mkrom/mkrom:
|
||||
$(MAKE) -C tools/mkrom
|
||||
|
||||
################################################################################
|
||||
# Testing Related
|
||||
# ---------------------
|
||||
# This works a little backwards, in that the ROM has to be built to at least
|
||||
# stage 2, then extract-segment is used to slice out the segments.
|
||||
|
||||
CHECK_FILES := $(shell awk '{print $$2}' checksums.$(ROMID).md5)
|
||||
|
||||
test: $(CHECK_FILES)
|
||||
@md5sum --quiet -c checksums.$(ROMID).md5
|
||||
|
||||
$(B_DIR)/segments/%.bin: $(B_DIR)/pd.z64
|
||||
@B_DIR=$(B_DIR) tools/extract-segment $*
|
||||
|
||||
################################################################################
|
||||
# Raw data segments
|
||||
|
||||
$(B_DIR)/assets/fonts/%.o: $(A_DIR)/fonts/%.bin
|
||||
mkdir -p $(B_DIR)/assets/fonts
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/getitle.o: $(E_DIR)/getitle.bin
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/assets/seq.ctl.o: $(A_DIR)/seq.ctl
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/assets/seq.tbl.o: $(A_DIR)/seq.tbl
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/assets/sfx.ctl.o: $(A_DIR)/sfx.ctl
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/assets/sfx.tbl.o: $(A_DIR)/sfx.tbl
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
################################################################################
|
||||
# Asset Manager
|
||||
|
||||
ASSETMGR_O_FILES := \
|
||||
$(B_DIR)/assets/animations.o \
|
||||
$(LANG_O_FILES) \
|
||||
$(PADS_O_FILES) \
|
||||
$(TILES_O_FILES) \
|
||||
$(B_DIR)/assets/sequences.o \
|
||||
$(B_DIR)/assets/textureslist.o \
|
||||
|
||||
# Anims
|
||||
$(B_DIR)/assets/animations.o: $(A_DIR)/animations.json
|
||||
tools/assetmgr/mkanims
|
||||
|
||||
# Lang
|
||||
$(B_DIR)/assets/files/L%E.o: $(A_DIR)/lang/%.json
|
||||
tools/assetmgr/mklang $< en
|
||||
|
||||
$(B_DIR)/assets/files/L%J.o: $(A_DIR)/lang/%.json
|
||||
tools/assetmgr/mklang $< jp
|
||||
|
||||
$(B_DIR)/assets/files/L%P.o: $(A_DIR)/lang/%.json
|
||||
tools/assetmgr/mklang $< gb
|
||||
|
||||
$(B_DIR)/assets/files/L%_str_f.o: $(A_DIR)/lang/%.json
|
||||
tools/assetmgr/mklang $< fr
|
||||
|
||||
$(B_DIR)/assets/files/L%_str_g.o: $(A_DIR)/lang/%.json
|
||||
tools/assetmgr/mklang $< de
|
||||
|
||||
$(B_DIR)/assets/files/L%_str_i.o: $(A_DIR)/lang/%.json
|
||||
tools/assetmgr/mklang $< it
|
||||
|
||||
$(B_DIR)/assets/files/L%_str_s.o: $(A_DIR)/lang/%.json
|
||||
tools/assetmgr/mklang $< es
|
||||
|
||||
# Pads
|
||||
$(B_DIR)/assets/files/bgdata/bg_%_padsZ.o: $(A_DIR)/pads/%.json
|
||||
tools/assetmgr/mkpads $<
|
||||
|
||||
# Pads - but this is the zipped non-obj, for make test
|
||||
$(B_DIR)/assets/files/bgdata/bg_%_padsZ: $(A_DIR)/pads/%.json
|
||||
tools/assetmgr/mkpads $<
|
||||
|
||||
# Sequences
|
||||
$(B_DIR)/assets/sequences.o: $(A_DIR)/sequences.json
|
||||
tools/assetmgr/mksequences
|
||||
|
||||
# Textures
|
||||
$(B_DIR)/assets/textureslist.o: $(A_DIR)/textures.json
|
||||
tools/assetmgr/mktextures
|
||||
|
||||
# Tiles
|
||||
$(B_DIR)/assets/files/bgdata/bg_%_tilesZ.o: $(A_DIR)/tiles/%.json
|
||||
tools/assetmgr/mktiles $<
|
||||
|
||||
# Tiles - but this is the zipped non-obj, for make test
|
||||
$(B_DIR)/assets/files/bgdata/bg_%_tilesZ: $(A_DIR)/tiles/%.json
|
||||
tools/assetmgr/mktiles $<
|
||||
|
||||
################################################################################
|
||||
# Files
|
||||
|
||||
# Audio
|
||||
$(B_DIR)/assets/files/A%M: $(A_DIR)/files/audio/%.mp3
|
||||
@mkdir -p $(B_DIR)/assets/files
|
||||
cp $< $@
|
||||
|
||||
# BG segs
|
||||
$(B_DIR)/assets/files/bgdata/bg_%.seg: $(A_DIR)/files/bgdata/bg_%.seg
|
||||
@mkdir -p $(B_DIR)/assets/files/bgdata
|
||||
cp $< $@
|
||||
|
||||
# Chrs
|
||||
$(B_DIR)/assets/files/C%Z: $(A_DIR)/files/chrs/%.bin
|
||||
@mkdir -p $(B_DIR)/assets/files
|
||||
tools/rarezip $< > $@
|
||||
|
||||
# Guns
|
||||
$(B_DIR)/assets/files/G%Z: $(A_DIR)/files/guns/%.bin
|
||||
@mkdir -p $(B_DIR)/assets/files
|
||||
tools/rarezip $< > $@
|
||||
|
||||
# OB seg
|
||||
$(B_DIR)/assets/files/ob/ob_mid.seg.o: $(B_DIR)/assets/files/ob/ob_mid.seg
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/assets/files/ob/ob_mid.seg:
|
||||
@mkdir -p $(B_DIR)/assets/files/ob
|
||||
> $@
|
||||
|
||||
# Props
|
||||
$(B_DIR)/assets/files/P%Z: $(A_DIR)/files/props/%.bin
|
||||
tools/rarezip $< > $@
|
||||
|
||||
# Stage setups
|
||||
# $(A_DIR)/files/setup/foo.c
|
||||
# -> B_DIR/assets/files/setup/foo.o (done here)
|
||||
# -> B_DIR/assets/files/setup/foo.elf (done here)
|
||||
# -> B_DIR/assets/files/setup/foo.bin (done here)
|
||||
# -> B_DIR/assets/files/UsetupfooZ (done here)
|
||||
# -> B_DIR/assets/files/UsetupfooZ.o (done elsewhere)
|
||||
$(B_DIR)/assets/files/setup/%.o: src/setups/%.c $(ASSETMGR_O_FILES) $(RECOMP_FILES)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
$(B_DIR)/assets/files/setup/%.elf: $(B_DIR)/assets/files/setup/%.o
|
||||
TOOLCHAIN=$(TOOLCHAIN) tools/mksimpleelf $< $@
|
||||
|
||||
$(B_DIR)/files/setup/%.bin: $(B_DIR)/assets/files/setup/%.elf
|
||||
@mkdir -p $(dir $@)
|
||||
$(TOOLCHAIN)-objcopy $< $@ -O binary
|
||||
|
||||
$(B_DIR)/assets/files/U%Z: $(B_DIR)/files/setup/%.bin
|
||||
tools/rarezip $< > $@
|
||||
|
||||
# General target to convert any finalised file into a raw object for ld
|
||||
$(B_DIR)/assets/files/%.o: $(B_DIR)/assets/files/%
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/assets/files/%.bin: $(B_DIR)/assets/files/%.elf
|
||||
$(TOOLCHAIN)-objcopy $< $@ -O binary
|
||||
|
||||
################################################################################
|
||||
# Miscellaneous
|
||||
|
||||
$(B_DIR)/bootloader.o: $(E_DIR)/bootloader.bin
|
||||
@mkdir -p $(dir $@)
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/romheader.o: src/romheader/romheader.s
|
||||
$(TOOLCHAIN)-as --defsym VERSION=$(VERSION) -march=vr4300 -mabi=32 -I src/include -EB -o $@ $<
|
||||
|
||||
$(B_DIR)/assets/accessingpakZ: $(A_DIR)/accessingpak.bin
|
||||
tools/rarezip $< > $@
|
||||
|
||||
$(B_DIR)/assets/copyrightZ: $(A_DIR)/copyright.bin
|
||||
tools/rarezip $< > $@
|
||||
|
||||
$(B_DIR)/assets/accessingpakZ.o: $(B_DIR)/assets/accessingpakZ
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/assets/copyrightZ.o: $(B_DIR)/assets/copyrightZ
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
$(B_DIR)/rsp/%.text.bin $(B_DIR)/rsp/%.data.bin: src/rsp/%.s
|
||||
@mkdir -p $(dir $@)
|
||||
armips -strequ CODE_FILE $(B_DIR)/rsp/$*.text.bin -strequ DATA_FILE $(B_DIR)/rsp/$*.data.bin $<
|
||||
|
||||
$(B_DIR)/rsp/%.o: $(B_DIR)/rsp/%.bin
|
||||
TOOLCHAIN=$(TOOLCHAIN) ROMID=$(ROMID) tools/mkrawobject $< $@
|
||||
|
||||
ifeq ($(COMPILER), ido)
|
||||
$(B_DIR)/lib/rng_c.o: src/lib/rng_c.c $(ASSETMGR_O_FILES) $(RECOMP_FILES)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) -c $(CFLAGS) $(OPT_LVL) -o $@ $<
|
||||
tools/patchmips3 $@ || rm $@
|
||||
|
||||
$(B_DIR)/lib/ultra/libc/llcvt.o: src/lib/ultra/libc/llcvt.c $(ASSETMGR_O_FILES) $(RECOMP_FILES)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) -c $(CFLAGS) $(OPT_LVL) -o $@ $<
|
||||
tools/patchmips3 $@ || rm $@
|
||||
|
||||
$(B_DIR)/lib/ultra/libc/ll.o: src/lib/ultra/libc/ll.c $(ASSETMGR_O_FILES) $(RECOMP_FILES)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) -c $(CFLAGS) $(OPT_LVL) $< -o $@
|
||||
tools/patchmips3 $@ || rm $@
|
||||
endif
|
||||
|
||||
$(B_DIR)/%.o: src/%.c $(ASSETMGR_O_FILES) $(RECOMP_FILES)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) -c $(CFLAGS) $(OPT_LVL) -o $@ $<
|
||||
|
||||
$(B_DIR)/%.o: src/%.s
|
||||
@mkdir -p $(dir $@)
|
||||
cpp -P -Wno-trigraphs -I include -I include/PR -I src/include $(C_DEFINES) -D_LANGUAGE_ASSEMBLY -D_MIPSEB $< | $(AS) $(ASFLAGS) -o $@
|
||||
|
||||
$(B_DIR)/assets/%.o: $(A_DIR)/%.c $(RECOMP_FILES)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
extract:
|
||||
ROMID=$(ROMID) tools/extract
|
||||
|
||||
rom: $(B_DIR)/pd.z64
|
||||
|
||||
clean:
|
||||
rm -rf build/$(ROMID) src/generated/$(ROMID)
|
||||
|
||||
allclean:
|
||||
rm -rf build/* src/generated
|
||||
|
||||
codeclean:
|
||||
find $(B_DIR)/game $(B_DIR)/inflate $(B_DIR)/lib -name '*.o' -delete
|
||||
@@ -1,62 +1,214 @@
|
||||
# Perfect Dark Decompilation
|
||||
# Perfect Dark port
|
||||
|
||||
This repository contains a complete decompilation of Perfect Dark for the Nintendo 64.
|
||||
This repository contains a work-in-progress port of the [Perfect Dark decompilation](https://github.com/n64decomp/perfect_dark) to modern platforms.
|
||||
|
||||
The project aims to be a matching decompilation. When a matching decompilation is compiled with the same compiler that the original developers used, the output will be exactly the same as the retail game, byte for byte.
|
||||
|
||||
To build the project, you must already have a Perfect Dark ROM. The project can build the exact same ROM from decomp's source code combined with assets that it extracts from your base ROM.
|
||||
To run the port, you must already have a Perfect Dark ROM, specifically one of the following:
|
||||
* `ntsc-final`/`US V1.1`/`US Rev 1` (md5 `e03b088b6ac9e0080440efed07c1e40f`).
|
||||
**This is the recommended version to use**.
|
||||
Called `NTSC version 8.7 final` on the boot screen.
|
||||
* `ntsc-1.0`/`US V1.0` (md5 `7f4171b0c8d17815be37913f535e4e93`).
|
||||
Technically supported, but not recommended.
|
||||
Called `NTSC version 8.7 final` on the boot screen as well.
|
||||
* `jpn-final` (md5 `538d2b75945eae069b29c46193e74790`).
|
||||
Technically supported, but requires a separate custom-built executable.
|
||||
Called `JPN version 8.9 final` on the boot screen.
|
||||
* `pal-final` (md5 `d9b5cd305d228424891ce38e71bc9213`).
|
||||
Technically supported, but requires a separate custom-built executable.
|
||||
Called `PAL 8.7 final` on the boot screen.
|
||||
|
||||
## Status
|
||||
|
||||
See the [Perfect Dark Decompilation Status Page](https://ryandwyer.gitlab.io/pdstatus/).
|
||||
The game is in a mostly functional state, with both singleplayer and split-screen multiplayer modes fully working.
|
||||
There are minor graphics- and gameplay-related issues, and possibly occasional crashes.
|
||||
|
||||
All versions of the game are fully decompiled but a small handful of functions are not yet byte-matching even though they are functionally the same. The status page doesn't show these as 100% because it counts matching functions only.
|
||||
**The following extra features are implemented:**
|
||||
* mouselook;
|
||||
* dual analog controller support;
|
||||
* widescreen resolution support;
|
||||
* configurable field of view;
|
||||
* 60 FPS support, including fixes for some framerate-related issues;
|
||||
* fixes for a couple original bugs and crashes;
|
||||
* basic mod support, currently enough to load a few custom levels;
|
||||
* slightly expanded memory heap size;
|
||||
* experimental high framerate support (up to 240 FPS):
|
||||
* enable `Uncap Tickrate` in `Extended Video Options` to activate;
|
||||
* in practice the game will have issues running faster than ~165 FPS, so use VSync or `Video.FramerateLimit` to cap it.
|
||||
* emulate the Transfer Pak functionality the game has on the Nintendo 64 to unlock some cheats automatically.
|
||||
|
||||
## Installation Requirements
|
||||
**The following platforms are officially supported and tested:**
|
||||
* Windows 7+: i686, x86_64
|
||||
* Linux: i686, x86_64
|
||||
* MacOS: x86_64 (OS 10.9+), arm64 (OS 11.0+)
|
||||
* Nintendo Switch: arm64
|
||||
|
||||
For Arch Linux:
|
||||
## Download
|
||||
|
||||
* Install these packages: `binutils fakeroot gcc make python vim`
|
||||
* Install from AUR: `armips`
|
||||
* Install from AUR: a MIPS binutils package of your choice (eg. `mips-elf-binutils`)
|
||||
* Install from AUR: a MIPS gcc package of your choice (eg. `mips-elf-gcc`)
|
||||
Latest [automatic builds](https://github.com/fgsfdsfgs/perfect_dark/releases/tag/ci-dev-build) for supported platforms:
|
||||
* [x86_64-windows](https://github.com/fgsfdsfgs/perfect_dark/releases/download/ci-dev-build/pd-x86_64-windows.zip)
|
||||
* [i686-windows](https://github.com/fgsfdsfgs/perfect_dark/releases/download/ci-dev-build/pd-i686-windows.zip)
|
||||
* [x86_64-linux](https://github.com/fgsfdsfgs/perfect_dark/releases/download/ci-dev-build/pd-x86_64-linux.tar.gz)
|
||||
* [i686-linux](https://github.com/fgsfdsfgs/perfect_dark/releases/download/ci-dev-build/pd-i686-linux.tar.gz)
|
||||
* [arm64-nswitch](https://github.com/fgsfdsfgs/perfect_dark/releases/download/ci-dev-build/pd-arm64-nswitch.zip)
|
||||
|
||||
For Debian and Ubuntu:
|
||||
If you are looking for netplay builds (the `port-net` branch), see [this link](https://github.com/fgsfdsfgs/perfect_dark/blob/port-net/README.md#download).
|
||||
|
||||
* Install these packages: `binutils-mips-linux-gnu build-essential gcc-mips-linux-gnu libc6-dev-i386 libcapstone-dev make`
|
||||
* Compile and install `armips`
|
||||
## Running
|
||||
|
||||
## ROM Versions
|
||||
You must already have a Perfect Dark ROM to run the game, as specified above.
|
||||
|
||||
Perfect Dark has six known versions:
|
||||
This assumes that you're using an x86_64 build. If you aren't, replace `x86_64` below with your arch (e.g. `i686`).
|
||||
|
||||
| ROM ID | Description | MD5 Checksum |
|
||||
|------------|---------------------------------------------|----------------------------------|
|
||||
| ntsc-beta | NTSC 6.4 beta | aa93f4df16fceada399a749f5ad2f273 |
|
||||
| ntsc-1.0 | NTSC 8.7 final (the initial, buggy release) | 7f4171b0c8d17815be37913f535e4e93 |
|
||||
| ntsc-final | NTSC 8.7 final | e03b088b6ac9e0080440efed07c1e40f |
|
||||
| pal-beta | PAL 28.7 beta | ad2de210a3455ba5ec541f0c78d91444 |
|
||||
| pal-final | PAL 8.7 final | d9b5cd305d228424891ce38e71bc9213 |
|
||||
| jpn-final | JPN 8.9 final | 538d2b75945eae069b29c46193e74790 |
|
||||
1. Create a directory named `data` next to `pd.x86_64` if it's not there.
|
||||
2. Put your Perfect Dark NTSC ROM named `pd.ntsc-final.z64` into it.
|
||||
3. Run the `pd.x86_64` executable.
|
||||
|
||||
The project uses the `$ROMID` environment variable to know which version to work with. If not set, it defaults to `ntsc-final`. You can change it by running something like `export ROMID=ntsc-1.0`.
|
||||
If you want to use a PAL or JPN ROM instead, put them into the `data` directory and run the appropriate executable:
|
||||
* PAL: ROM name `pd.pal-final.z64`, executable name `pd.pal.x86_64`.
|
||||
* JPN: ROM name `pd.jpn-final.z64`, executable name `pd.jpn.x86_64`.
|
||||
|
||||
## Extracting the base ROM
|
||||
Optionally, you can also put your Perfect Dark for GameBoy Color ROM named `pd.gbc` in the `data` directory if you want to emulate having the Nintendo 64's Transfer Pak and unlock some cheats automatically.
|
||||
|
||||
1. Save your existing ROM file into the root of the repository with the name `pd.ntsc-final.z64`. It should not be byteswapped (the first four bytes should be `0x80371240`).
|
||||
2. Run `make extract`.
|
||||
Optionally, you can move the data folder to `~/.local/share/perfectdark` on Linux or `~/Library/Application Support/perfectdark` on MacOS.
|
||||
|
||||
This will extract assets to `src/assets`. If any asset already exists then it will not be overwritten. This means you can modify assets as desired, and your changes will not be overwritten if you run the extract command again.
|
||||
Additional information can be found in the [wiki](https://github.com/fgsfdsfgs/perfect_dark/wiki).
|
||||
|
||||
The extract command will also create an `extracted/ntsc-final` directory. This directory contains some compiled code segments from the ROM and is only used for comparison purposes.
|
||||
A GPU supporting OpenGL 3.0/ES3.0 or above is required to run the port.
|
||||
|
||||
## Compiling
|
||||
### Installing the Nintendo Switch version
|
||||
|
||||
* Run `git submodule update --init --recursive`. You only have to do this once.
|
||||
* Run `make -j` to build the ROM. The ROM will be written to `build/ntsc-final/pd.z64`.
|
||||
The Nintendo Switch build ZIP comes with all 3 regions in different folders: `perfectdark`, `perfectdark_pal` and `perfectdark_jpn`.
|
||||
|
||||
## How do I know the built files are matching?
|
||||
Take the folder for the region you want and put it into the `/switch` folder on your SD card, then put your ROM into the `data` folder inside of the folder you extracted as described above.
|
||||
|
||||
Run `make` followed by `make test`. If `make test` produces no output then the compiled project is matching.
|
||||
## Controls
|
||||
|
||||
You can also md5sum your base ROM with the built ROM and check they have the same hash: `md5sum pd.ntsc-final.z64 build/ntsc-final/pd.z64`.
|
||||
1964GEPD-style and Xbox-style bindings are implemented.
|
||||
|
||||
N64 pad buttons X and Y (or `X_BUTTON`, `Y_BUTTON` in the code) refer to the reserved buttons `0x40` and `0x80`, which are also leveraged by 1964GEPD.
|
||||
|
||||
Support for one controller, two-stick configurations are enabled for 1.2.
|
||||
|
||||
Note that the mouse only controls player 1.
|
||||
|
||||
Controls can be rebound in `pd.ini`. Default control scheme is as follows:
|
||||
|
||||
| Action | Keyboard and mouse | Xbox pad | N64 pad |
|
||||
| - | - | - | - |
|
||||
| Fire / Accept | LMB/Space | RT | Z Trigger |
|
||||
| Aim mode | RMB/Z | LT | R Trigger |
|
||||
| Use / Cancel | E | N/A | B |
|
||||
| Use / Accept | N/A | A | A |
|
||||
| Crouch cycle | N/A | LS Click | `0x80000000` (Extra) |
|
||||
| Half-Crouch | Shift | N/A | `0x40000000` (Extra) |
|
||||
| Full-Crouch | Control | N/A | `0x20000000` (Extra) |
|
||||
| Reload | R | X | X `(0x40)` |
|
||||
| Previous weapon | Mousewheel forward | B | D-Left |
|
||||
| Next weapon | Mousewheel back | Y | Y `(0x80)` |
|
||||
| Radial menu | Q | LB | D-Down |
|
||||
| Alt fire mode | F | RB | L Trigger |
|
||||
| Alt-fire oneshot | `F + LMB` or `E + LMB` | `A + RT` or `RB + RT` | `A + Z` or `L + Z` |
|
||||
| Quick-detonate | `E + Q` or `E + R` | `A + B` or `A + X` | `A + D-Left`or `A + X` |
|
||||
|
||||
## Building
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install [MSYS2](https://www.msys2.org).
|
||||
2. Open the `MINGW64` prompt if building for x86_64, or the `MINGW32` prompt if building for i686. (**NOTE:** _do not_ use the `MSYS` prompt)
|
||||
3. Install dependencies:
|
||||
`pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-SDL2 mingw-w64-x86_64-zlib mingw-w64-x86_64-cmake mingw-w64-x86_64-python3 mingw-w64-i686-toolchain mingw-w64-i686-SDL2 mingw-w64-i686-zlib mingw-w64-i686-cmake mingw-w64-i686-python3 make git`
|
||||
4. Get the source code:
|
||||
`git clone --recursive https://github.com/fgsfdsfgs/perfect_dark.git && cd perfect_dark`
|
||||
5. Run `cmake -G"Unix Makefiles" -Bbuild .`.
|
||||
* Add ` -DROMID=pal-final` or ` -DROMID=jpn-final` at the end of the command if you want to build a PAL or JPN executable respectively.\
|
||||
6. Run `cmake --build build -j4 -- -O`.
|
||||
7. The resulting executable will be at `build/pd.x86_64.exe` (or at `build/pd.i686.exe` if building for i686).
|
||||
8. If you don't know where you downloaded the source to, you can run `explorer .` to open the current directory.
|
||||
|
||||
### Linux
|
||||
|
||||
1. Ensure you have gcc, g++ (version 10.0+), make, cmake, git, python3 and SDL2 (version 2.0.12+), libGL and ZLib installed on your system.
|
||||
* If you wish to crosscompile, you will also need to have libraries and compilers for the target platform installed, e.g. `gcc-multilib` and `g++-multilib` for x86_64 -> i686 crosscompilation.
|
||||
2. Get the source code:
|
||||
`git clone --recursive https://github.com/fgsfdsfgs/perfect_dark.git && cd perfect_dark`
|
||||
3. Run the following command:
|
||||
* ```cmake -G"Unix Makefiles" -Bbuild .```
|
||||
* Add ` -DROMID=pal-final` or ` -DROMID=jpn-final` at the end of the command if you want to build a PAL or JPN executable respectively.
|
||||
* Add ` -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32` at the end of the command if you want to crosscompile from x86_64 to x86.
|
||||
4. Run `cmake --build build -j4`.
|
||||
5. The resulting executable will be at `build/pd.<arch>` (for example `build/pd.x86_64`).
|
||||
|
||||
### MacOS
|
||||
|
||||
1. Set up Homebrew.
|
||||
2. Install dependencies:
|
||||
* Execute command: `brew install cmake gcc python3 zlib git`
|
||||
3. Install SDL2:
|
||||
* Execute commands:
|
||||
```
|
||||
wget http://libsdl.org/release/SDL2-2.30.9.dmg -O SDL2.dmg
|
||||
hdiutil mount SDL2.dmg
|
||||
sudo cp -vr /Volumes/SDL2/SDL2.framework /Library/Frameworks
|
||||
hdiutil detach /Volumes/SDL2
|
||||
```
|
||||
* This installs SDL2 system-wide and this is how the automatic builds are done. The game will also look for it in the executable path, so you could
|
||||
download it locally instead.
|
||||
4. Get the source code:
|
||||
`git clone --recursive https://github.com/fgsfdsfgs/perfect_dark.git && cd perfect_dark`
|
||||
5. Configure:
|
||||
* Execute command: `cmake -G"Unix Makefiles" -Bbuild -DCMAKE_OSX_ARCHITECTURES=x86_64 .`
|
||||
* Replace `x86_64` with `arm64` if building for an ARM64 Mac.
|
||||
* Add ` -DROMID=pal-final` or ` -DROMID=jpn-final` at the end of the command if you want to build a PAL or JPN executable respectively.
|
||||
6. Build:
|
||||
* Execute command: `cmake --build build --target pd -j4 --clean-first`
|
||||
7. The resulting executable will be at `build/pd.<arch>` (for example `build/pd.x86_64`).
|
||||
* You might need to execute `chmod +x build/pd.x86-64` before you can run it.
|
||||
|
||||
### Nintendo Switch
|
||||
|
||||
1. Set up the [devkitA64 environment](https://devkitpro.org/wiki/Getting_Started).
|
||||
* On Windows you can do it under MSYS2 or WSL, usually MSYS2 is recommended.
|
||||
* If using MSYS2, make sure to use the **MSYS2** shell, **not** MINGW32 or MINGW64.
|
||||
2. Install host dependencies:
|
||||
* On MSYS2: execute command `pacman -Syuu && pacman -S git make cmake python3`
|
||||
* On Linux: use your package manager as normal to install the above dependencies.
|
||||
3. Install Switch toolchain and dependencies:
|
||||
* Execute commands:
|
||||
```
|
||||
dkp-pacman -Syuu
|
||||
dkp-pacman -S devkitA64 libnx switch-zlib switch-sdl2 switch-cmake dkp-toolchain-vars
|
||||
```
|
||||
* If in MSYS2 or `dkp-pacman` doesn't work, replace it with just `pacman`.
|
||||
4. Get the source code:
|
||||
`git clone --recursive https://github.com/fgsfdsfgs/perfect_dark.git && cd perfect_dark`
|
||||
5. Ensure devkitA64 environment variables are set:
|
||||
* Execute command: `source /opt/devkitpro/switchvars.sh`
|
||||
* If your `$DEVKITPRO` path is different, substitute that instead or set the variables manually.
|
||||
6. Configure:
|
||||
* Execute command: `aarch64-none-elf-cmake -G"Unix Makefiles" -Bbuild .`
|
||||
* Add ` -DROMID=pal-final` or ` -DROMID=jpn-final` at the end of the command if you want to build a PAL or JPN executable respectively.
|
||||
7. Build:
|
||||
* Execute command: `make -C build -j4`
|
||||
8. The resulting executable will be at `build/pd.arm64.nro`.
|
||||
|
||||
### Notes
|
||||
|
||||
Alternate compilers or toolchains can be specified by passing `-DCMAKE_TOOLCHAIN_FILE=whatever` as normal. The port does not build with Visual Studio.
|
||||
|
||||
You will need to provide a `jpn-final` or `pal-final` ROM to run executables built for those regions, named `pd.jpn-final.z64` or `pd.pal-final.z64`.
|
||||
|
||||
It might be possible to build and run the game on platforms that are not specified in the supported platforms list (e.g. Linux on armv7), but this has not been tested.
|
||||
|
||||
## Credits
|
||||
|
||||
* the original [decompilation project](https://github.com/n64decomp/perfect_dark) authors;
|
||||
* Ryan Dwyer for the above, additional help, and `pd-extract`;
|
||||
* doomhack for the only other publicly available [PD porting effort](https://github.com/doomhack/perfect_dark) I could find;
|
||||
* [sm64-port](https://github.com/sm64-port/sm64-port) authors for the audio mixer and some other changes;
|
||||
* [Ship of Harkinian team](https://github.com/Kenix3/libultraship/tree/main/src/fast), Emill and MaikelChan for the libultraship version of fast3d that this port uses;
|
||||
* lieff for [minimp3](https://github.com/lieff/minimp3);
|
||||
* Mouse Injector and 1964GEPD authors for some of the 60FPS- and mouselook-related fixes;
|
||||
* Raf for the 64-bit port;
|
||||
* NicNamSam for the icon;
|
||||
* everyone who has submitted pull requests and issues to this repository and tested the port;
|
||||
* probably more I'm forgetting.
|
||||
@@ -0,0 +1,27 @@
|
||||
# execute a header generator (execcmd) for every json file in jsonpath, collect headers in headerlist
|
||||
# note that this reads ROMID
|
||||
macro(generate_asset_headers jsonpath execcmd extraarg headerlist)
|
||||
set(TMP_JSON "")
|
||||
|
||||
if (${jsonpath} MATCHES "\.json")
|
||||
# is a json file
|
||||
list(APPEND TMP_JSON "${CMAKE_SOURCE_DIR}/${ASSET_DIR}/${jsonpath}")
|
||||
else()
|
||||
# is a directory
|
||||
file(GLOB TMP_JSON "${CMAKE_SOURCE_DIR}/${ASSET_DIR}/${jsonpath}*.json")
|
||||
endif()
|
||||
|
||||
foreach(JSON ${TMP_JSON})
|
||||
unset(HEADERNAME)
|
||||
string(REPLACE ".json" ".h" HEADERNAME ${JSON})
|
||||
string(REPLACE "${CMAKE_SOURCE_DIR}/${ASSET_DIR}" "${CMAKE_BINARY_DIR}/${GENERATED_DIR}" HEADERNAME ${HEADERNAME})
|
||||
add_custom_command(
|
||||
OUTPUT ${HEADERNAME}
|
||||
DEPENDS ${JSON}
|
||||
COMMAND ${execcmd} ${JSON} ${extraarg} --headers-only --romid=${ROMID}
|
||||
)
|
||||
list(APPEND ${headerlist} "${HEADERNAME}")
|
||||
endforeach()
|
||||
|
||||
unset(TMP_JSON)
|
||||
endmacro()
|
||||
@@ -0,0 +1,172 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
# Locate SDL2 library
|
||||
# This module defines
|
||||
# SDL2_LIBRARY, the name of the library to link against
|
||||
# SDL2_FOUND, if false, do not try to link to SDL2
|
||||
# SDL2_INCLUDE_DIR, where to find SDL.h
|
||||
#
|
||||
# This module responds to the the flag:
|
||||
# SDL2_BUILDING_LIBRARY
|
||||
# If this is defined, then no SDL2_main will be linked in because
|
||||
# only applications need main().
|
||||
# Otherwise, it is assumed you are building an application and this
|
||||
# module will attempt to locate and set the the proper link flags
|
||||
# as part of the returned SDL2_LIBRARY variable.
|
||||
#
|
||||
# Don't forget to include SDL2main.h and SDL2main.m your project for the
|
||||
# OS X framework based version. (Other versions link to -lSDL2main which
|
||||
# this module will try to find on your behalf.) Also for OS X, this
|
||||
# module will automatically add the -framework Cocoa on your behalf.
|
||||
#
|
||||
#
|
||||
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
|
||||
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
|
||||
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
|
||||
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
|
||||
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
|
||||
# as appropriate. These values are used to generate the final SDL2_LIBRARY
|
||||
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
|
||||
#
|
||||
#
|
||||
# $SDL2DIR is an environment variable that would
|
||||
# correspond to the ./configure --prefix=$SDL2DIR
|
||||
# used in building SDL2.
|
||||
# l.e.galup 9-20-02
|
||||
#
|
||||
# Modified by Eric Wing.
|
||||
# Added code to assist with automated building by using environmental variables
|
||||
# and providing a more controlled/consistent search behavior.
|
||||
# Added new modifications to recognize OS X frameworks and
|
||||
# additional Unix paths (FreeBSD, etc).
|
||||
# Also corrected the header search path to follow "proper" SDL2 guidelines.
|
||||
# Added a search for SDL2main which is needed by some platforms.
|
||||
# Added a search for threads which is needed by some platforms.
|
||||
# Added needed compile switches for MinGW.
|
||||
#
|
||||
# On OSX, this will prefer the Framework version (if found) over others.
|
||||
# People will have to manually change the cache values of
|
||||
# SDL2_LIBRARY to override this selection or set the CMake environment
|
||||
# CMAKE_INCLUDE_PATH to modify the search paths.
|
||||
#
|
||||
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
|
||||
# This needed to change because "proper" SDL2 convention
|
||||
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
|
||||
# reasons because not all systems place things in SDL2/ (see FreeBSD).
|
||||
#
|
||||
# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake
|
||||
# module with the minor edit of changing "SDL" to "SDL2" where necessary. This
|
||||
# was not created for redistribution, and exists temporarily pending official
|
||||
# SDL2 CMake modules.
|
||||
|
||||
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES include/SDL2 include
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/include/SDL2
|
||||
/usr/include/SDL2
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
/boot/system/develop/headers/SDL2 #Hiaku OS
|
||||
)
|
||||
#MESSAGE("SDL2_INCLUDE_DIR is ${SDL2_INCLUDE_DIR}")
|
||||
|
||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP
|
||||
NAMES SDL2
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
/system/lib #Hiaku OS
|
||||
)
|
||||
|
||||
#MESSAGE("SDL2_LIBRARY_TEMP is ${SDL2_LIBRARY_TEMP}")
|
||||
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
# Non-OS X framework versions expect you to also dynamically link to
|
||||
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
|
||||
# seem to provide SDL2main for compatibility even though they don't
|
||||
# necessarily need it.
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# SDL2 may require threads on your system.
|
||||
# The Apple build may not need an explicit flag because one of the
|
||||
# frameworks may already provide it.
|
||||
# But for non-OSX systems, I will use the CMake Threads package.
|
||||
IF(NOT APPLE)
|
||||
FIND_PACKAGE(Threads)
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# MinGW needs an additional library, mwindows
|
||||
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
|
||||
# (Actually on second look, I think it only needs one of the m* libraries.)
|
||||
IF(MINGW)
|
||||
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
|
||||
ENDIF(MINGW)
|
||||
|
||||
SET(SDL2_FOUND "NO")
|
||||
IF(SDL2_LIBRARY_TEMP)
|
||||
# For SDL2main
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(SDL2MAIN_LIBRARY)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(SDL2MAIN_LIBRARY)
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
|
||||
# CMake doesn't display the -framework Cocoa string in the UI even
|
||||
# though it actually is there if I modify a pre-used variable.
|
||||
# I think it has something to do with the CACHE STRING.
|
||||
# So I use a temporary variable until the end so I can set the
|
||||
# "real" variable in one-shot.
|
||||
IF(APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
|
||||
ENDIF(APPLE)
|
||||
|
||||
# For threads, as mentioned Apple doesn't need this.
|
||||
# In fact, there seems to be a problem if I used the Threads package
|
||||
# and try using this line, so I'm just skipping it entirely for OS X.
|
||||
IF(NOT APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# For MinGW library
|
||||
IF(MINGW)
|
||||
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(MINGW)
|
||||
|
||||
# Set the final string here so the GUI reflects the final state.
|
||||
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
|
||||
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
|
||||
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
|
||||
|
||||
SET(SDL2_FOUND "YES")
|
||||
ENDIF(SDL2_LIBRARY_TEMP)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2
|
||||
REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
|
||||
@@ -0,0 +1,165 @@
|
||||
|
||||
# Copyright (c) 2012 Petroules Corporation. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation and/or
|
||||
# other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
# OF SUCH DAMAGE.
|
||||
|
||||
# Based on the Qt 5 processor detection code, so should be very accurate
|
||||
# https://qt.gitorious.org/qt/qtbase/blobs/master/src/corelib/global/qprocessordetection.h
|
||||
# Currently handles arm (v5, v6, v7), x86 (32/64), ia64, and ppc (32/64)
|
||||
|
||||
# Regarding POWER/PowerPC, just as is noted in the Qt source,
|
||||
# "There are many more known variants/revisions that we do not handle/detect."
|
||||
|
||||
set(archdetect_c_code "
|
||||
#if defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_M_ARM64) || defined (__aarch64__)
|
||||
#if defined(__ARM_ARCH_7__) \\
|
||||
|| defined(__ARM_ARCH_7A__) \\
|
||||
|| defined(__ARM_ARCH_7R__) \\
|
||||
|| defined(__ARM_ARCH_7M__) \\
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 7)
|
||||
#error cmake_ARCH armv7
|
||||
#elif defined(__ARM_ARCH_6__) \\
|
||||
|| defined(__ARM_ARCH_6J__) \\
|
||||
|| defined(__ARM_ARCH_6T2__) \\
|
||||
|| defined(__ARM_ARCH_6Z__) \\
|
||||
|| defined(__ARM_ARCH_6K__) \\
|
||||
|| defined(__ARM_ARCH_6ZK__) \\
|
||||
|| defined(__ARM_ARCH_6M__) \\
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 6)
|
||||
#error cmake_ARCH armv6
|
||||
#elif defined(__ARM_ARCH_5TEJ__) \\
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 5)
|
||||
#error cmake_ARCH armv5
|
||||
#elif defined(_M_ARM64) || defined (__aarch64__)
|
||||
#error cmake_ARCH arm64
|
||||
#else
|
||||
#error cmake_ARCH arm
|
||||
#endif
|
||||
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
|
||||
#error cmake_ARCH i386
|
||||
#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
|
||||
#error cmake_ARCH x86_64
|
||||
#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
|
||||
#error cmake_ARCH ia64
|
||||
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \\
|
||||
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\
|
||||
|| defined(_M_MPPC) || defined(_M_PPC)
|
||||
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
|
||||
#error cmake_ARCH ppc64
|
||||
#else
|
||||
#error cmake_ARCH ppc
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#error cmake_ARCH unknown
|
||||
")
|
||||
|
||||
# Set ppc_support to TRUE before including this file or ppc and ppc64
|
||||
# will be treated as invalid architectures since they are no longer supported by Apple
|
||||
|
||||
function(target_architecture output_var)
|
||||
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
# On OS X we use CMAKE_OSX_ARCHITECTURES *if* it was set
|
||||
# First let's normalize the order of the values
|
||||
|
||||
# Note that it's not possible to compile PowerPC applications if you are using
|
||||
# the OS X SDK version 10.6 or later - you'll need 10.4/10.5 for that, so we
|
||||
# disable it by default
|
||||
# See this page for more information:
|
||||
# http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4
|
||||
|
||||
# Architecture defaults to i386 or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime.
|
||||
# On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise.
|
||||
|
||||
foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES})
|
||||
if("${osx_arch}" STREQUAL "ppc" AND ppc_support)
|
||||
set(osx_arch_ppc TRUE)
|
||||
elseif("${osx_arch}" STREQUAL "i386")
|
||||
set(osx_arch_i386 TRUE)
|
||||
elseif("${osx_arch}" STREQUAL "x86_64")
|
||||
set(osx_arch_x86_64 TRUE)
|
||||
elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support)
|
||||
set(osx_arch_ppc64 TRUE)
|
||||
elseif("${osx_arch}" STREQUAL "arm64")
|
||||
set(osx_arch_arm64 TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Now add all the architectures in our normalized order
|
||||
if(osx_arch_ppc)
|
||||
list(APPEND ARCH ppc)
|
||||
endif()
|
||||
|
||||
if(osx_arch_i386)
|
||||
list(APPEND ARCH i386)
|
||||
endif()
|
||||
|
||||
if(osx_arch_x86_64)
|
||||
list(APPEND ARCH x86_64)
|
||||
endif()
|
||||
|
||||
if(osx_arch_ppc64)
|
||||
list(APPEND ARCH ppc64)
|
||||
endif()
|
||||
|
||||
if(osx_arch_arm64)
|
||||
list(APPEND ARCH arm64)
|
||||
endif()
|
||||
else()
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/arch.c" "${archdetect_c_code}")
|
||||
|
||||
enable_language(C)
|
||||
|
||||
# Detect the architecture in a rather creative way...
|
||||
# This compiles a small C program which is a series of ifdefs that selects a
|
||||
# particular #error preprocessor directive whose message string contains the
|
||||
# target architecture. The program will always fail to compile (both because
|
||||
# file is not a valid C program, and obviously because of the presence of the
|
||||
# #error preprocessor directives... but by exploiting the preprocessor in this
|
||||
# way, we can detect the correct target architecture even when cross-compiling,
|
||||
# since the program itself never needs to be run (only the compiler/preprocessor)
|
||||
try_run(
|
||||
run_result_unused
|
||||
compile_result_unused
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"${CMAKE_BINARY_DIR}/arch.c"
|
||||
COMPILE_OUTPUT_VARIABLE ARCH
|
||||
CMAKE_FLAGS CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}
|
||||
)
|
||||
|
||||
# Parse the architecture name from the compiler output
|
||||
string(REGEX MATCH "cmake_ARCH ([a-zA-Z0-9_]+)" ARCH "${ARCH}")
|
||||
|
||||
# Get rid of the value marker leaving just the architecture name
|
||||
string(REPLACE "cmake_ARCH " "" ARCH "${ARCH}")
|
||||
|
||||
# If we are compiling with an unknown architecture this variable should
|
||||
# already be set to "unknown" but in the case that it's empty (i.e. due
|
||||
# to a typo in the code), then set it to unknown
|
||||
if (NOT ARCH)
|
||||
set(ARCH unknown)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${output_var} "${ARCH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Name=Perfect Dark
|
||||
Comment=Modern source port of Perfect Dark
|
||||
Exec=io.github.perfect_dark_pc_port.perfect_dark.sh
|
||||
Icon=io.github.perfect_dark_pc_port.perfect_dark
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Game;ActionGame;
|
||||
StartupNotify=false
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>io.github.perfect_dark_pc_port.perfect_dark</id>
|
||||
<name>Perfect Dark</name>
|
||||
<developer id="io.github.perfect_dark_pc_port.perfect_dark">
|
||||
<name>Perfect Dark Contributors</name>
|
||||
<url>https://github.com/fgsfdsfgs/perfect_dark/graphs/contributors</url>
|
||||
</developer>
|
||||
<launchable type="desktop-id">io.github.perfect_dark_pc_port.perfect_dark.desktop</launchable>
|
||||
<summary>Modern cross-platform port of Perfect Dark</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>MIT</project_license>
|
||||
<url type="homepage">https://github.com/fgsfdsfgs/perfect_dark/</url>
|
||||
<description>
|
||||
<p>This is a port of Ryan Dywer's decompilation of classic N64 shooter Perfect Dark to modern systems.</p>
|
||||
</description>
|
||||
<!--
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image type="source"></image>
|
||||
<caption>Screenshot</caption>
|
||||
</screenshot>
|
||||
<screenshots>
|
||||
-->
|
||||
<categories>
|
||||
<category>Game</category>
|
||||
<category>ActionGame</category>
|
||||
<category>Shooter</category>
|
||||
</categories>
|
||||
<keywords>
|
||||
<keyword>n64</keyword>
|
||||
<keyword>scifi</keyword>
|
||||
<keyword>cyberpunk</keyword>
|
||||
<keyword>retro</keyword>
|
||||
<keyword>sourceport</keyword>
|
||||
<keyword>port</keyword>
|
||||
</keywords>
|
||||
<releases>
|
||||
<release version="latest" date="2024-05-14"/>
|
||||
</releases>
|
||||
<content_rating type="oars-1.1">
|
||||
<content_attribute id="violence-realistic">moderate</content_attribute>
|
||||
<content_attribute id="violence-bloodshed">mild</content_attribute>
|
||||
</content_rating>
|
||||
<update_contact>https://github.com/fgsfdsfgs/perfect_dark/</update_contact>
|
||||
</component>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Create user data directories if necessary
|
||||
userdirs=( roms saves mods )
|
||||
for path in "${userdirs[@]}"; do
|
||||
if [ ! -d "${XDG_DATA_HOME}/${path}" ]; then
|
||||
mkdir -p "${XDG_DATA_HOME}/${path}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Default to pd as executable but switch to jpn/pal if the NTSC rom
|
||||
# is missing but JPN/PAL are present.
|
||||
# TODO: Other arch
|
||||
executable="pd.x86_64"
|
||||
if [ -f ${XDG_DATA_HOME}/roms/pd.ntsc-final.z64 ] | \
|
||||
[ -f ${XDG_DATA_HOME}/roms/pd.ntsc-1.0.z64 ]; then
|
||||
executable="pd.x86_64"
|
||||
elif [ -f ${XDG_DATA_HOME}/roms/pd.jpn-final.z64 ]; then
|
||||
executable="pd.jpn.x86_64"
|
||||
elif [ -f ${XDG_DATA_HOME}/roms/pd.pal-final.z64 ]; then
|
||||
executable="pd.pal.x86_64"
|
||||
fi
|
||||
|
||||
# If first parameter passed to this script is one of the pd executables,
|
||||
# run that regardless of ROM auto-detection above and pass the remaining
|
||||
# parameters to it; otherwise pass all arguments to the executable selected
|
||||
# above
|
||||
case $1 in
|
||||
"pd."*)
|
||||
executable=$1
|
||||
params="${@:2}"
|
||||
;;
|
||||
*)
|
||||
params="$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Run game
|
||||
$executable --basedir ${XDG_DATA_HOME}/roms --savedir ${XDG_DATA_HOME}/saves $params
|
||||
@@ -0,0 +1,41 @@
|
||||
app-id: io.github.perfect_dark_pc_port.perfect_dark
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: "25.08"
|
||||
sdk: org.freedesktop.Sdk
|
||||
command: io.github.perfect_dark_pc_port.perfect_dark.sh
|
||||
|
||||
finish-args:
|
||||
- --device=all
|
||||
- --share=ipc
|
||||
- --socket=x11
|
||||
# Audio
|
||||
- --socket=pulseaudio
|
||||
# Enable network for multiplayer
|
||||
- --share=network
|
||||
|
||||
modules:
|
||||
- name: perfect_dark
|
||||
buildsystem: simple
|
||||
sources:
|
||||
- type: file
|
||||
path: bin/pd.x86_64
|
||||
- type: file
|
||||
path: bin/pd.jpn.x86_64
|
||||
- type: file
|
||||
path: bin/pd.pal.x86_64
|
||||
- type: file
|
||||
path: io.github.perfect_dark_pc_port.perfect_dark.sh
|
||||
- type: file
|
||||
path: io.github.perfect_dark_pc_port.perfect_dark.png
|
||||
- type: file
|
||||
path: io.github.perfect_dark_pc_port.perfect_dark.metainfo.xml
|
||||
- type: file
|
||||
path: io.github.perfect_dark_pc_port.perfect_dark.desktop
|
||||
build-commands: []
|
||||
post-install:
|
||||
# Install the binaries
|
||||
- install -Dm 755 -t /app/bin/ pd.*
|
||||
- install -Dm 755 ${FLATPAK_ID}.sh /app/bin/${FLATPAK_ID}.sh
|
||||
- install -Dm 644 ${FLATPAK_ID}.desktop /app/share/applications/${FLATPAK_ID}.desktop
|
||||
- install -Dm 644 ${FLATPAK_ID}.metainfo.xml /app/share/metainfo/${FLATPAK_ID}.metainfo.xml
|
||||
- install -Dm 644 ${FLATPAK_ID}.png /app/share/icons/hicolor/256x256/apps/${FLATPAK_ID}.png
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
# Switch (SDL_GetPlatform is not changed in switch-sdl2)
|
||||
000038f853776974636820436f6e7400,Switch Controller,a:b0,b:b1,back:b11,dpdown:b15,dpleft:b12,dpright:b14,dpup:b13,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Unknown (see SDL_platform.h),
|
||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
Vendored
+1
@@ -0,0 +1 @@
|
||||
id ICON icon.ico
|
||||
@@ -22,6 +22,7 @@
|
||||
#define __R4300_H__
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
#include "platform.h"
|
||||
|
||||
/*
|
||||
* Segment base addresses and sizes
|
||||
@@ -60,6 +61,8 @@
|
||||
|
||||
#else /* _LANGUAGE_C */
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
|
||||
#define K0_TO_K1(x) ((u32)(x)|0xA0000000) /* kseg0 to kseg1 */
|
||||
#define K1_TO_K0(x) ((u32)(x)&0x9FFFFFFF) /* kseg1 to kseg0 */
|
||||
#define K0_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF) /* kseg0 to physical */
|
||||
@@ -68,6 +71,24 @@
|
||||
#define PHYS_TO_K0(x) ((u32)(x)|0x80000000) /* physical to kseg0 */
|
||||
#define PHYS_TO_K1(x) ((u32)(x)|0xA0000000) /* physical to kseg1 */
|
||||
|
||||
#else /* PLATFORM_N64 */
|
||||
|
||||
#ifdef PLATFORM_64BIT
|
||||
typedef u64 k_ptr_t;
|
||||
#else
|
||||
typedef u32 k_ptr_t;
|
||||
#endif
|
||||
|
||||
#define K0_TO_K1(x) ((k_ptr_t)(x)) /* kseg0 to kseg1 */
|
||||
#define K1_TO_K0(x) ((k_ptr_t)(x)) /* kseg1 to kseg0 */
|
||||
#define K0_TO_PHYS(x) ((k_ptr_t)(x)) /* kseg0 to physical */
|
||||
#define K1_TO_PHYS(x) ((k_ptr_t)(x)) /* kseg1 to physical */
|
||||
#define KDM_TO_PHYS(x) ((k_ptr_t)(x)) /* direct mapped to physical */
|
||||
#define PHYS_TO_K0(x) ((k_ptr_t)(x)) /* physical to kseg0 */
|
||||
#define PHYS_TO_K1(x) ((k_ptr_t)(x)) /* physical to kseg1 */
|
||||
|
||||
#endif /* PLATFORM_N64 */
|
||||
|
||||
#endif /* _LANGUAGE_ASSEMBLY */
|
||||
|
||||
/*
|
||||
|
||||
@@ -265,6 +265,8 @@ typedef short RESAMPLE_STATE[16];
|
||||
*/
|
||||
typedef short ENVMIX_STATE[40];
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
|
||||
/*
|
||||
* Macros to assemble the audio command list
|
||||
*/
|
||||
@@ -402,6 +404,12 @@ typedef short ENVMIX_STATE[40];
|
||||
_a->words.w1 = (unsigned int) d; \
|
||||
}
|
||||
|
||||
#else // PLATFORM_N64
|
||||
|
||||
#include "mixer.h"
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _LANGUAGE_C */
|
||||
|
||||
#endif /* !_ABI_H_ */
|
||||
|
||||
+159
-31
@@ -22,6 +22,12 @@
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
#include "gbiex.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
#include "platform.h"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* To use the F3DEX ucodes, define F3DEX_GBI before include this file.
|
||||
@@ -594,10 +600,10 @@
|
||||
#define G_BL_0 3
|
||||
|
||||
#define GBL_c1(m1a, m1b, m2a, m2b) \
|
||||
(m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18
|
||||
(u32)(m1a) << 30 | (u32)(m1b) << 26 | (u32)(m2a) << 22 | (u32)(m2b) << 18
|
||||
|
||||
#define GBL_c2(m1a, m1b, m2a, m2b) \
|
||||
(m1a) << 28 | (m1b) << 24 | (m2a) << 20 | (m2b) << 16
|
||||
(u32)(m1a) << 28 | (u32)(m1b) << 24 | (u32)(m2a) << 20 | (u32)(m2b) << 16
|
||||
|
||||
#define RM_AA_ZB_OPA_SURF(clk) \
|
||||
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \
|
||||
@@ -1029,16 +1035,32 @@ typedef union {
|
||||
* Triangle face
|
||||
*/
|
||||
typedef struct {
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
unsigned char flag;
|
||||
unsigned char v[3];
|
||||
#else
|
||||
unsigned char v[3]; // reverse order
|
||||
unsigned char flag;
|
||||
#endif
|
||||
} Tri;
|
||||
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
#define GFX_TRI_VTX(i) (i)
|
||||
#else
|
||||
#define GFX_TRI_VTX(i) (2 - i)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 4x4 matrix, fixed point s15.16 format.
|
||||
* First 8 words are integer portion of the 4x4 matrix
|
||||
* Last 8 words are the fraction portion of the 4x4 matrix
|
||||
*/
|
||||
typedef long Mtx_t[4][4];
|
||||
#ifdef GBI_FLOATS
|
||||
// 4x4 float matrix
|
||||
typedef float Mtx_t[4][4];
|
||||
#else
|
||||
typedef int32_t Mtx_t[4][4];
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
Mtx_t m;
|
||||
@@ -1290,23 +1312,45 @@ typedef union {
|
||||
/*
|
||||
* Graphics DMA Packet
|
||||
*/
|
||||
#ifdef PLATFORM_64BIT
|
||||
typedef struct {
|
||||
intptr_t par : 24;
|
||||
intptr_t cmd : 8;
|
||||
intptr_t len : 32;
|
||||
uintptr_t addr;
|
||||
} Gdma;
|
||||
#else
|
||||
typedef struct {
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
int cmd:8;
|
||||
unsigned int par:8;
|
||||
unsigned int len:16;
|
||||
unsigned int addr;
|
||||
#else // NOTE: changing signedness mid int will break up the bitfield on normal platforms, even BE
|
||||
int len:16;
|
||||
int par:8;
|
||||
int cmd:8;
|
||||
unsigned int addr;
|
||||
#endif
|
||||
} Gdma;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Graphics Immediate Mode Packet types
|
||||
*/
|
||||
typedef struct {
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
int cmd:8;
|
||||
int pad:24;
|
||||
#else
|
||||
int pad:24;
|
||||
int cmd:8;
|
||||
#endif
|
||||
Tri tri;
|
||||
} Gtri;
|
||||
|
||||
typedef struct {
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
unsigned char cmd:8;
|
||||
unsigned char pad:8;
|
||||
unsigned char z4:4;
|
||||
@@ -1321,6 +1365,28 @@ typedef struct {
|
||||
unsigned char x2:4;
|
||||
unsigned char y1:4;
|
||||
unsigned char x1:4;
|
||||
#else
|
||||
unsigned char z1:4;
|
||||
unsigned char z2:4;
|
||||
unsigned char z3:4;
|
||||
unsigned char z4:4;
|
||||
unsigned char pad:8;
|
||||
unsigned char cmd:8;
|
||||
#ifdef PLATFORM_64BIT
|
||||
unsigned char pad2[4];
|
||||
#endif
|
||||
unsigned char x1:4;
|
||||
unsigned char y1:4;
|
||||
unsigned char x2:4;
|
||||
unsigned char y2:4;
|
||||
unsigned char x3:4;
|
||||
unsigned char y3:4;
|
||||
unsigned char x4:4;
|
||||
unsigned char y4:4;
|
||||
#ifdef PLATFORM_64BIT
|
||||
unsigned char pad3[4];
|
||||
#endif
|
||||
#endif
|
||||
} Gtri4;
|
||||
|
||||
typedef struct {
|
||||
@@ -1356,12 +1422,21 @@ typedef struct {
|
||||
} GsetothermodeH;
|
||||
|
||||
typedef struct {
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
unsigned char cmd;
|
||||
unsigned char lodscale;
|
||||
unsigned char tile;
|
||||
unsigned char on;
|
||||
unsigned short s;
|
||||
unsigned short t;
|
||||
#else
|
||||
unsigned char on;
|
||||
unsigned char tile;
|
||||
unsigned char lodscale;
|
||||
unsigned char cmd;
|
||||
unsigned short t;
|
||||
unsigned short s;
|
||||
#endif
|
||||
} Gtexture;
|
||||
|
||||
typedef struct {
|
||||
@@ -1484,13 +1559,14 @@ typedef struct {
|
||||
* Generic Gfx Packet
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int w0;
|
||||
unsigned int w1;
|
||||
uintptr_t w0;
|
||||
uintptr_t w1;
|
||||
} Gwords;
|
||||
|
||||
// xxxxxxxx 11223344 44555566 66666777
|
||||
// 88888888 99999999 9999aaaa aaaaaaaa
|
||||
typedef struct {
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
unsigned int cmd:8;
|
||||
unsigned int unk08:2;
|
||||
unsigned int unk0a:2;
|
||||
@@ -1502,15 +1578,37 @@ typedef struct {
|
||||
unsigned int unk20:8;
|
||||
unsigned int tile1:12;
|
||||
unsigned int tile2:12;
|
||||
#else
|
||||
unsigned int subcmd:3;
|
||||
unsigned int flags:7;
|
||||
unsigned int unk12:4;
|
||||
unsigned int unk0e:4;
|
||||
unsigned int unk0c:2;
|
||||
unsigned int unk0a:2;
|
||||
unsigned int unk08:2;
|
||||
unsigned int cmd:8;
|
||||
unsigned int tile2:12;
|
||||
unsigned int tile1:12;
|
||||
unsigned int unk20:8;
|
||||
#endif
|
||||
} GunkC0;
|
||||
|
||||
typedef struct {
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
unsigned int cmd:8;
|
||||
unsigned int unk08:4;
|
||||
unsigned int unk0c:4;
|
||||
unsigned int unk10:16;
|
||||
unsigned int seg:8;
|
||||
unsigned int offset:24;
|
||||
#else
|
||||
unsigned int unk10:16;
|
||||
unsigned int unk0c:4;
|
||||
unsigned int unk08:4;
|
||||
unsigned int cmd:8;
|
||||
unsigned int offset:24;
|
||||
unsigned int seg:8;
|
||||
#endif
|
||||
} Gvtx;
|
||||
|
||||
/*
|
||||
@@ -1519,17 +1617,22 @@ typedef struct {
|
||||
*/
|
||||
typedef union {
|
||||
Gwords words;
|
||||
#ifdef PLATFORM_64BIT
|
||||
u8 bytes[16];
|
||||
#else
|
||||
u8 bytes[8];
|
||||
#endif
|
||||
Gdma dma;
|
||||
Gvtx vtx;
|
||||
Gtri tri;
|
||||
Gtri4 tri4;
|
||||
Gline3D line;
|
||||
Gtexture texture;
|
||||
#ifdef PLATFORM_N64 /* the rest is not needed on other platforms and some of em screw up the size */
|
||||
Gpopmtx popmtx;
|
||||
Gsegment segment;
|
||||
GsetothermodeH setothermodeH;
|
||||
GsetothermodeL setothermodeL;
|
||||
Gtexture texture;
|
||||
Gperspnorm perspnorm;
|
||||
Gsetimg setimg;
|
||||
Gsetcombine setcombine;
|
||||
@@ -1539,10 +1642,26 @@ typedef union {
|
||||
Gloadtile loadtile; /* use for loadblock also, th is dxt */
|
||||
Gsettilesize settilesize;
|
||||
Gloadtlut loadtlut;
|
||||
#endif
|
||||
GunkC0 unkc0;
|
||||
#ifdef PLATFORM_64BIT
|
||||
long long int force_structure_alignment[2];
|
||||
#else
|
||||
long long int force_structure_alignment;
|
||||
#endif
|
||||
} Gfx;
|
||||
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
#define GFX_W0_BYTE(i) (i)
|
||||
#define GFX_W1_BYTE(i) (4 + (i))
|
||||
#elif PLATFORM_64BIT
|
||||
#define GFX_W0_BYTE(i) (3 - (i))
|
||||
#define GFX_W1_BYTE(i) (11 - (i))
|
||||
#else
|
||||
#define GFX_W0_BYTE(i) (3 - (i))
|
||||
#define GFX_W1_BYTE(i) (7 - (i))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Macros to assemble the graphics display list
|
||||
*/
|
||||
@@ -1555,12 +1674,12 @@ typedef union {
|
||||
Gfx *_g = (Gfx *)(pkt); \
|
||||
\
|
||||
_g->words.w0 = _SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24); \
|
||||
_g->words.w1 = (unsigned int)(s); \
|
||||
_g->words.w1 = (uintptr_t)(s); \
|
||||
}
|
||||
|
||||
#define gsDma0p(c, s, l) \
|
||||
{ \
|
||||
_SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24), (unsigned int)(s) \
|
||||
_SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24), (uintptr_t)(s) \
|
||||
}
|
||||
|
||||
#define gDma1p(pkt, c, s, l, p) \
|
||||
@@ -1569,14 +1688,14 @@ typedef union {
|
||||
\
|
||||
_g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | \
|
||||
_SHIFTL((l), 0, 16)); \
|
||||
_g->words.w1 = (unsigned int)(s); \
|
||||
_g->words.w1 = (uintptr_t)(s); \
|
||||
}
|
||||
|
||||
#define gsDma1p(c, s, l, p) \
|
||||
{ \
|
||||
(_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | \
|
||||
_SHIFTL((l), 0, 16)), \
|
||||
(unsigned int)(s) \
|
||||
(uintptr_t)(s) \
|
||||
}
|
||||
|
||||
#define gDma2p(pkt, c, adrs, len, idx, ofs) \
|
||||
@@ -1584,14 +1703,14 @@ typedef union {
|
||||
Gfx *_g = (Gfx *)(pkt); \
|
||||
_g->words.w0 = (_SHIFTL((c),24,8)|_SHIFTL(((len)-1)/8,19,5)| \
|
||||
_SHIFTL((ofs)/8,8,8)|_SHIFTL((idx),0,8)); \
|
||||
_g->words.w1 = (unsigned int)(adrs); \
|
||||
_g->words.w1 = (uintptr_t)(adrs); \
|
||||
}
|
||||
|
||||
#define gsDma2p(c, adrs, len, idx, ofs) \
|
||||
{ \
|
||||
(_SHIFTL((c),24,8)|_SHIFTL(((len)-1)/8,19,5)| \
|
||||
_SHIFTL((ofs)/8,8,8)|_SHIFTL((idx),0,8)), \
|
||||
(unsigned int)(adrs) \
|
||||
(uintptr_t)(adrs) \
|
||||
}
|
||||
|
||||
#define gSPNoOp(pkt) gDma0p(pkt, G_SPNOOP, 0, 0)
|
||||
@@ -1640,12 +1759,12 @@ typedef union {
|
||||
Gfx *_g = (Gfx *)(pkt); \
|
||||
\
|
||||
_g->words.w0 = _SHIFTL((c), 24, 8); \
|
||||
_g->words.w1 = (unsigned int)(p0); \
|
||||
_g->words.w1 = (uintptr_t)(p0); \
|
||||
}
|
||||
|
||||
#define gsImmp1(c, p0) \
|
||||
{ \
|
||||
_SHIFTL((c), 24, 8), (unsigned int)(p0) \
|
||||
_SHIFTL((c), 24, 8), (uintptr_t)(p0) \
|
||||
}
|
||||
|
||||
#define gImmp2(pkt, c, p0, p1) \
|
||||
@@ -1682,13 +1801,13 @@ typedef union {
|
||||
\
|
||||
_g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | \
|
||||
_SHIFTL((p1), 0, 8)); \
|
||||
_g->words.w1 = (unsigned int) (dat); \
|
||||
_g->words.w1 = (uintptr_t) (dat); \
|
||||
}
|
||||
|
||||
#define gsImmp21(c, p0, p1, dat) \
|
||||
{ \
|
||||
_SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | _SHIFTL((p1), 0, 8), \
|
||||
(unsigned int) (dat) \
|
||||
(uintptr_t) (dat) \
|
||||
}
|
||||
|
||||
#define gMoveWd(pkt, index, offset, data) \
|
||||
@@ -1831,7 +1950,7 @@ typedef union {
|
||||
\
|
||||
_g->words.w0 = _SHIFTL(G_CULLDL, 24, 8) | \
|
||||
((0x0f & (vstart))*40); \
|
||||
_g->words.w1 = (unsigned int)((0x0f & ((vend)+1))*40); \
|
||||
_g->words.w1 = (uintptr_t)((0x0f & ((vend)+1))*40); \
|
||||
}
|
||||
|
||||
// Removed in PD
|
||||
@@ -1847,6 +1966,15 @@ typedef union {
|
||||
#define gsSPSegment(segment, base) \
|
||||
gsMoveWd(G_MW_SEGMENT, (segment)*4, base)
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
#define SEGADDR(x) x
|
||||
#define UNSEGADDR(x) x
|
||||
#else
|
||||
// we mark all segmented addresses so that it'll be easier to recognize them later
|
||||
#define SEGADDR(x) ((void *)((uintptr_t)(x) | 1))
|
||||
#define UNSEGADDR(x) ((uintptr_t)(x) & ~1)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Clipping Macros
|
||||
*/
|
||||
@@ -2143,12 +2271,12 @@ typedef union {
|
||||
Gfx *_g = (Gfx *)(pkt); \
|
||||
\
|
||||
_g->words.w0 = _SHIFTL(G_SETGEOMETRYMODE, 24, 8); \
|
||||
_g->words.w1 = (unsigned int)(word); \
|
||||
_g->words.w1 = (uintptr_t)(word); \
|
||||
}
|
||||
|
||||
#define gsSPSetGeometryMode(word) \
|
||||
{ \
|
||||
_SHIFTL(G_SETGEOMETRYMODE, 24, 8), (unsigned int)(word) \
|
||||
_SHIFTL(G_SETGEOMETRYMODE, 24, 8), (uintptr_t)(word) \
|
||||
}
|
||||
|
||||
#define gSPClearGeometryMode(pkt, word) \
|
||||
@@ -2156,12 +2284,12 @@ typedef union {
|
||||
Gfx *_g = (Gfx *)(pkt); \
|
||||
\
|
||||
_g->words.w0 = _SHIFTL(G_CLEARGEOMETRYMODE, 24, 8); \
|
||||
_g->words.w1 = (unsigned int)(word); \
|
||||
_g->words.w1 = (uintptr_t)(word); \
|
||||
}
|
||||
|
||||
#define gsSPClearGeometryMode(word) \
|
||||
{ \
|
||||
_SHIFTL(G_CLEARGEOMETRYMODE, 24, 8), (unsigned int)(word) \
|
||||
_SHIFTL(G_CLEARGEOMETRYMODE, 24, 8), (uintptr_t)(word) \
|
||||
}
|
||||
|
||||
#define gSPSetOtherMode(pkt, cmd, sft, len, data) \
|
||||
@@ -2170,13 +2298,13 @@ typedef union {
|
||||
\
|
||||
_g->words.w0 = (_SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | \
|
||||
_SHIFTL(len, 0, 8)); \
|
||||
_g->words.w1 = (unsigned int)(data); \
|
||||
_g->words.w1 = (uintptr_t)(data); \
|
||||
}
|
||||
|
||||
#define gsSPSetOtherMode(cmd, sft, len, data) \
|
||||
{ \
|
||||
_SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | _SHIFTL(len, 0, 8), \
|
||||
(unsigned int)(data) \
|
||||
(uintptr_t)(data) \
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2227,14 +2355,14 @@ typedef union {
|
||||
\
|
||||
_g->words.w0 = _SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | \
|
||||
_SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12); \
|
||||
_g->words.w1 = (unsigned int)(i); \
|
||||
_g->words.w1 = (uintptr_t)(i); \
|
||||
}
|
||||
|
||||
#define gsSetImage(cmd, fmt, siz, width, i) \
|
||||
{ \
|
||||
_SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | \
|
||||
_SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12), \
|
||||
(unsigned int)(i) \
|
||||
(uintptr_t)(i) \
|
||||
}
|
||||
|
||||
#define gDPSetColorImage(pkt, f, s, w, i) gSetImage(pkt, G_SETCIMG, f, s, w, i)
|
||||
@@ -2260,13 +2388,13 @@ typedef union {
|
||||
Gfx *_g = (Gfx *)(pkt); \
|
||||
\
|
||||
_g->words.w0 = _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(muxs0, 0, 24); \
|
||||
_g->words.w1 = (unsigned int)(muxs1); \
|
||||
_g->words.w1 = (uintptr_t)(muxs1); \
|
||||
}
|
||||
|
||||
#define gsDPSetCombine(muxs0, muxs1) \
|
||||
{ \
|
||||
_SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(muxs0, 0, 24), \
|
||||
(unsigned int)(muxs1) \
|
||||
(uintptr_t)(muxs1) \
|
||||
}
|
||||
|
||||
#define GCCc0w0(saRGB0, mRGB0, saA0, mA0) \
|
||||
@@ -2339,12 +2467,12 @@ typedef union {
|
||||
Gfx *_g = (Gfx *)(pkt); \
|
||||
\
|
||||
_g->words.w0 = _SHIFTL(c, 24, 8); \
|
||||
_g->words.w1 = (unsigned int)(d); \
|
||||
_g->words.w1 = (uintptr_t)(d); \
|
||||
}
|
||||
|
||||
#define gsDPSetColor(c, d) \
|
||||
{ \
|
||||
_SHIFTL(c, 24, 8), (unsigned int)(d) \
|
||||
_SHIFTL(c, 24, 8), (uintptr_t)(d) \
|
||||
}
|
||||
|
||||
#define DPRGBColor(pkt, cmd, r, g, b, a) \
|
||||
@@ -2425,13 +2553,13 @@ typedef union {
|
||||
Gfx *_g = (Gfx *)(pkt); \
|
||||
\
|
||||
_g->words.w0 = _SHIFTL(G_RDPSETOTHERMODE,24,8)|_SHIFTL(mode0,0,24); \
|
||||
_g->words.w1 = (unsigned int)(mode1); \
|
||||
_g->words.w1 = (uintptr_t)(mode1); \
|
||||
}
|
||||
|
||||
#define gsDPSetOtherMode(mode0, mode1) \
|
||||
{ \
|
||||
_SHIFTL(G_RDPSETOTHERMODE,24,8)|_SHIFTL(mode0,0,24), \
|
||||
(unsigned int)(mode1) \
|
||||
(uintptr_t)(mode1) \
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
#define OS_M_PI 3.14159265358979323846
|
||||
#define M_DTOR (3.14159265358979323846/180.0)
|
||||
|
||||
#define FTOFIX32(x) (long)((x) * (float)0x00010000)
|
||||
#define FTOFIX32(x) (int)((x) * (float)0x00010000)
|
||||
#define FIX32TOF(x) ((float)(x) * (1.0f / (float)0x00010000))
|
||||
#define FTOFRAC8(x) ((int) MIN(((x) * (128.0f)), 127.0f) & 0xff)
|
||||
|
||||
|
||||
@@ -120,7 +120,11 @@ typedef struct ALLink_s {
|
||||
void alUnlink(ALLink *element);
|
||||
void alLink(ALLink *element, ALLink *after);
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
typedef s32 (*ALDMAproc)(s32 addr, s32 len, void *state);
|
||||
#else
|
||||
typedef uintptr_t (*ALDMAproc)(uintptr_t addr, s32 len, void *state);
|
||||
#endif
|
||||
typedef ALDMAproc (*ALDMANew)(void *state);
|
||||
|
||||
void alCopy(void *src, void *dest, s32 len);
|
||||
|
||||
+14
-3
@@ -28,6 +28,7 @@ extern "C" {
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
#include <PR/mbi.h>
|
||||
#include "platform.h"
|
||||
|
||||
struct var8009c340 {
|
||||
u8 surround;
|
||||
@@ -71,7 +72,7 @@ typedef struct {
|
||||
s32 maxAuxBusses;
|
||||
s32 outputRate;
|
||||
s32 maxOutSamples;
|
||||
s32 sv_dramout;
|
||||
uintptr_t sv_dramout;
|
||||
s32 sv_first;
|
||||
} N_ALSynth;
|
||||
|
||||
@@ -208,7 +209,7 @@ struct fx {
|
||||
s16 unk00;
|
||||
s16 unk02;
|
||||
s32 unk04;
|
||||
s16 unk08[8];
|
||||
s16 unk08[16];
|
||||
};
|
||||
|
||||
typedef struct N_ALVoiceState_s {
|
||||
@@ -256,7 +257,7 @@ typedef struct {
|
||||
ALOscStop stopOsc;
|
||||
ALSeqMarker *loopStart;
|
||||
ALSeqMarker *loopEnd;
|
||||
s32 loopCount; /* -1 = loop forever, 0 = no loop */
|
||||
intptr_t loopCount; /* -1 = loop forever, 0 = no loop */
|
||||
u8 unk88;
|
||||
u8 unk89;
|
||||
} N_ALSeqPlayer;
|
||||
@@ -284,9 +285,19 @@ typedef struct {
|
||||
ALOscInit initOsc;
|
||||
ALOscUpdate updateOsc;
|
||||
ALOscStop stopOsc;
|
||||
|
||||
#ifdef PLATFORM_64BIT
|
||||
s32 _pad1_;
|
||||
f32 unk7c;
|
||||
s32 _pad2_;
|
||||
f32 unk80;
|
||||
#else
|
||||
f32 unk7c;
|
||||
f32 unk80;
|
||||
#endif
|
||||
|
||||
void *queue;
|
||||
|
||||
u8 unk88;
|
||||
u8 unk89;
|
||||
} N_ALCSPlayer;
|
||||
|
||||
+42
-18
@@ -53,14 +53,18 @@ extern "C" {
|
||||
typedef struct {
|
||||
u16 type; /* Controller Type */
|
||||
u8 status; /* Controller status */
|
||||
u8 errno;
|
||||
u8 errnum;
|
||||
}OSContStatus;
|
||||
|
||||
typedef struct {
|
||||
u16 button;
|
||||
u32 button;
|
||||
s8 stick_x; /* -80 <= stick_x <= 80 */
|
||||
s8 stick_y; /* -80 <= stick_y <= 80 */
|
||||
u8 errno;
|
||||
u8 errnum;
|
||||
#ifndef PLATFORM_N64
|
||||
s8 rstick_x;
|
||||
s8 rstick_y;
|
||||
#endif
|
||||
} OSContPad;
|
||||
|
||||
typedef struct {
|
||||
@@ -68,7 +72,7 @@ typedef struct {
|
||||
u8 databuffer[32]; /* address of the data buffer */
|
||||
u8 addressCrc; /* CRC code for address */
|
||||
u8 dataCrc; /* CRC code for data */
|
||||
u8 errno;
|
||||
u8 errnum;
|
||||
} OSContRamIo;
|
||||
|
||||
|
||||
@@ -118,21 +122,39 @@ typedef struct {
|
||||
#define CONT_EEPROM_BUSY 0x80
|
||||
|
||||
/* Buttons */
|
||||
#define CONT_8000 0x80000000
|
||||
#define CONT_4000 0x40000000
|
||||
#define CONT_2000 0x20000000
|
||||
#define CONT_1000 0x10000000
|
||||
#define CONT_0800 0x08000000
|
||||
#define CONT_0400 0x04000000
|
||||
#define CONT_0200 0x02000000
|
||||
#define CONT_0100 0x01000000
|
||||
#define CONT_0080 0x00800000
|
||||
#define CONT_0040 0x00400000
|
||||
#define CONT_0020 0x00200000
|
||||
#define CONT_0010 0x00100000
|
||||
#define CONT_0008 0x00080000
|
||||
#define CONT_0004 0x00040000
|
||||
#define CONT_0002 0x00020000
|
||||
#define CONT_0001 0x00010000
|
||||
|
||||
#define CONT_A 0x8000
|
||||
#define CONT_B 0x4000
|
||||
#define CONT_G 0x2000
|
||||
#define CONT_START 0x1000
|
||||
#define CONT_UP 0x0800
|
||||
#define CONT_DOWN 0x0400
|
||||
#define CONT_LEFT 0x0200
|
||||
#define CONT_RIGHT 0x0100
|
||||
#define CONT_L 0x0020
|
||||
#define CONT_R 0x0010
|
||||
#define CONT_E 0x0008
|
||||
#define CONT_D 0x0004
|
||||
#define CONT_C 0x0002
|
||||
#define CONT_F 0x0001
|
||||
#define CONT_A 0x00008000
|
||||
#define CONT_B 0x00004000
|
||||
#define CONT_G 0x00002000
|
||||
#define CONT_START 0x00001000
|
||||
#define CONT_UP 0x00000800
|
||||
#define CONT_DOWN 0x00000400
|
||||
#define CONT_LEFT 0x00000200
|
||||
#define CONT_RIGHT 0x00000100
|
||||
#define CONT_EXTRA1 0x00000080
|
||||
#define CONT_EXTRA0 0x00000040
|
||||
#define CONT_L 0x00000020
|
||||
#define CONT_R 0x00000010
|
||||
#define CONT_E 0x00000008
|
||||
#define CONT_D 0x00000004
|
||||
#define CONT_C 0x00000002
|
||||
#define CONT_F 0x00000001
|
||||
|
||||
/* Nintendo's official button names */
|
||||
|
||||
@@ -150,6 +172,8 @@ typedef struct {
|
||||
#define L_CBUTTONS CONT_C
|
||||
#define R_CBUTTONS CONT_F
|
||||
#define D_CBUTTONS CONT_D
|
||||
#define X_BUTTON CONT_EXTRA0
|
||||
#define Y_BUTTON CONT_EXTRA1
|
||||
|
||||
/* Controller error number */
|
||||
|
||||
|
||||
+15
-3
@@ -35,6 +35,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
#ifndef PLATFORM_N64
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS)
|
||||
|
||||
@@ -70,12 +73,17 @@ extern "C" {
|
||||
#define OS_CYCLES_TO_NSEC(c) (((u64)(c)*(1000000000LL/15625000LL))/(OS_CPU_COUNTER/15625000LL))
|
||||
#define OS_CYCLES_TO_USEC(c) (((u64)(c)*(1000000LL/15625LL))/(OS_CPU_COUNTER/15625LL))
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
#define OS_K0_TO_PHYSICAL(x) (u32)(((char *)(x)-0x80000000))
|
||||
#define OS_K1_TO_PHYSICAL(x) (u32)(((char *)(x)-0xa0000000))
|
||||
|
||||
#define OS_PHYSICAL_TO_K0(x) (void *)(((u32)(x)+0x80000000))
|
||||
#define OS_PHYSICAL_TO_K1(x) (void *)(((u32)(x)+0xa0000000))
|
||||
|
||||
#else
|
||||
#define OS_K0_TO_PHYSICAL(x) (uintptr_t)(x)
|
||||
#define OS_K1_TO_PHYSICAL(x) (uintptr_t)(x)
|
||||
#define OS_PHYSICAL_TO_K0(x) (void *)(x)
|
||||
#define OS_PHYSICAL_TO_K1(x) (void *)(x)
|
||||
#endif
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
@@ -92,9 +100,13 @@ extern "C" {
|
||||
|
||||
/* Address translation routines and macros */
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
extern u32 osVirtualToPhysical(void *);
|
||||
extern void * osPhysicalToVirtual(u32);
|
||||
|
||||
#else
|
||||
extern uintptr_t osVirtualToPhysical(void *);
|
||||
extern void * osPhysicalToVirtual(uintptr_t);
|
||||
#endif
|
||||
|
||||
#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */
|
||||
|
||||
|
||||
+15
-3
@@ -31,6 +31,14 @@
|
||||
#define _OS_LIBC_H_
|
||||
|
||||
#include "os_pfs.h"
|
||||
#include "platform.h"
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef PLATFORM_OSX
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#ifdef _LANGUAGE_C_PLUS_PLUS
|
||||
extern "C" {
|
||||
@@ -80,13 +88,17 @@ extern "C" {
|
||||
|
||||
/* byte string operations */
|
||||
|
||||
extern void bcopy(const void *, void *, int);
|
||||
extern int bcmp(const void *, const void *, int);
|
||||
extern void bzero(void *, int);
|
||||
#ifndef PLATFORM_OSX
|
||||
extern void bcopy(const void *, void *, size_t);
|
||||
extern int bcmp(const void *, const void *, size_t);
|
||||
extern void bzero(void *, size_t);
|
||||
#endif
|
||||
|
||||
/* Printf */
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
extern int sprintf(char *s, const char *fmt, ...);
|
||||
#endif
|
||||
extern void osSyncPrintf(const char *fmt, ...);
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -37,7 +37,7 @@ extern "C" {
|
||||
#include <PR/ultratypes.h>
|
||||
#include "os_thread.h"
|
||||
#include "os_message.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS)
|
||||
|
||||
@@ -198,7 +198,7 @@ extern u32 osPiGetStatus(void);
|
||||
extern s32 osPiGetDeviceType(void);
|
||||
extern s32 osPiWriteIo(u32, u32);
|
||||
extern s32 osPiReadIo(u32, u32 *);
|
||||
extern s32 osPiStartDma(OSIoMesg *, s32, s32, u32, void *, u32,
|
||||
extern s32 osPiStartDma(OSIoMesg *, s32, s32, uintptr_t, void *, u32,
|
||||
OSMesgQueue *);
|
||||
extern void osCreatePiManager(OSPri, OSMesgQueue *, OSMesg *, s32);
|
||||
|
||||
|
||||
File renamed without changes.
@@ -57,6 +57,8 @@ typedef volatile long long vs64; /* signed 64-bit */
|
||||
typedef float f32; /* single prec floating point */
|
||||
typedef double f64; /* double prec floating point */
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
|
||||
#if !defined(_SIZE_T) && !defined(_SIZE_T_) && !defined(_SIZE_T_DEF)
|
||||
#define _SIZE_T
|
||||
#define _SIZE_T_DEF /* exeGCC size_t define label */
|
||||
@@ -68,6 +70,12 @@ typedef unsigned long size_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _LANGUAGE_C */
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Emill, MaikelChan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef GFX_API_H
|
||||
#define GFX_API_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include "gfx_rendering_api.h"
|
||||
#include "gfx_window_manager_api.h"
|
||||
|
||||
struct XYWidthHeight {
|
||||
int16_t x, y;
|
||||
uint32_t width, height;
|
||||
};
|
||||
|
||||
struct GfxDimensions {
|
||||
float internal_mul;
|
||||
uint32_t width, height;
|
||||
float aspect_ratio;
|
||||
};
|
||||
|
||||
struct GfxInitSettings {
|
||||
struct GfxWindowManagerAPI *wapi;
|
||||
struct GfxRenderingAPI *rapi;
|
||||
struct GfxWindowInitSettings window_settings;
|
||||
};
|
||||
|
||||
extern struct GfxDimensions gfx_current_window_dimensions; // The dimensions of the window
|
||||
extern struct GfxDimensions
|
||||
gfx_current_dimensions; // The dimensions of the draw area the game draws to, before scaling (if applicable)
|
||||
extern struct XYWidthHeight
|
||||
gfx_current_game_window_viewport; // The area of the window the game is drawn to, (0, 0) is top-left corner
|
||||
extern uint32_t gfx_msaa_level;
|
||||
extern struct XYWidthHeight gfx_current_native_viewport; // The internal/native video mode of the game
|
||||
extern float gfx_current_native_aspect; // The aspect ratio of the above mode
|
||||
extern bool gfx_framebuffers_enabled;
|
||||
extern bool gfx_detail_textures_enabled;
|
||||
|
||||
void gfx_init(const struct GfxInitSettings *settings);
|
||||
void gfx_destroy(void);
|
||||
struct GfxRenderingAPI* gfx_get_current_rendering_api(void);
|
||||
void gfx_start_frame(void);
|
||||
void gfx_run(Gfx* commands);
|
||||
void gfx_end_frame(void);
|
||||
void gfx_set_target_fps(int);
|
||||
void gfx_set_texture_filter(enum FilteringMode mode);
|
||||
void gfx_set_mipmap_filter(enum MipmapFilteringMode mode);
|
||||
void gfx_texture_cache_clear(void);
|
||||
void gfx_texture_cache_delete(const uint8_t *orig_addr);
|
||||
void gfx_texture_cache_delete_range(const uint8_t *start, const uint8_t *end);
|
||||
int gfx_create_framebuffer(uint32_t width, uint32_t height, int upscale, int autoresize);
|
||||
void gfx_resize_framebuffer(int fb, uint32_t width, uint32_t height, int upscale, int autoresize);
|
||||
void gfx_set_framebuffer(int fb, float noise_scale) ;
|
||||
void gfx_reset_framebuffer(void);
|
||||
void gfx_copy_framebuffer(int fb_dst, int fb_src, int left, int top, int use_back);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "gfx_cc.h"
|
||||
|
||||
void gfx_cc_get_features(uint64_t shader_id0, uint32_t shader_id1, struct CCFeatures* cc_features) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (int k = 0; k < 4; k++) {
|
||||
cc_features->c[i][j][k] = (shader_id0 >> (i * 32 + j * 16 + k * 4)) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cc_features->opt_alpha = (shader_id1 & SHADER_OPT_ALPHA) != 0;
|
||||
cc_features->opt_fog = (shader_id1 & SHADER_OPT_FOG) != 0;
|
||||
cc_features->opt_texture_edge = (shader_id1 & SHADER_OPT_TEXTURE_EDGE) != 0;
|
||||
cc_features->opt_noise = (shader_id1 & SHADER_OPT_NOISE) != 0;
|
||||
cc_features->opt_2cyc = (shader_id1 & SHADER_OPT_2CYC) != 0;
|
||||
cc_features->opt_alpha_threshold = (shader_id1 & SHADER_OPT_ALPHA_THRESHOLD) != 0;
|
||||
cc_features->opt_invisible = (shader_id1 & SHADER_OPT_INVISIBLE) != 0;
|
||||
cc_features->opt_grayscale = (shader_id1 & SHADER_OPT_GRAYSCALE) != 0;
|
||||
cc_features->opt_blur = (shader_id1 & SHADER_OPT_BLUR) != 0;
|
||||
|
||||
cc_features->clamp[0][0] = (shader_id1 & SHADER_OPT_TEXEL0_CLAMP_S);
|
||||
cc_features->clamp[0][1] = (shader_id1 & SHADER_OPT_TEXEL0_CLAMP_T);
|
||||
cc_features->clamp[1][0] = (shader_id1 & SHADER_OPT_TEXEL1_CLAMP_S);
|
||||
cc_features->clamp[1][1] = (shader_id1 & SHADER_OPT_TEXEL1_CLAMP_T);
|
||||
|
||||
cc_features->used_textures[0] = false;
|
||||
cc_features->used_textures[1] = false;
|
||||
cc_features->num_inputs = 0;
|
||||
|
||||
for (int c = 0; c < 2; c++) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (cc_features->c[c][i][j] >= SHADER_INPUT_1 && cc_features->c[c][i][j] <= SHADER_INPUT_7) {
|
||||
if (cc_features->c[c][i][j] > cc_features->num_inputs) {
|
||||
cc_features->num_inputs = cc_features->c[c][i][j];
|
||||
}
|
||||
}
|
||||
if (cc_features->c[c][i][j] == SHADER_TEXEL0 || cc_features->c[c][i][j] == SHADER_TEXEL0A) {
|
||||
cc_features->used_textures[0] = true;
|
||||
}
|
||||
if (cc_features->c[c][i][j] == SHADER_TEXEL1 || cc_features->c[c][i][j] == SHADER_TEXEL1A) {
|
||||
cc_features->used_textures[1] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int c = 0; c < 2; c++) {
|
||||
cc_features->do_single[c][0] = cc_features->c[c][0][2] == SHADER_0;
|
||||
cc_features->do_single[c][1] = cc_features->c[c][1][2] == SHADER_0;
|
||||
cc_features->do_multiply[c][0] = cc_features->c[c][0][1] == SHADER_0 && cc_features->c[c][0][3] == SHADER_0;
|
||||
cc_features->do_multiply[c][1] = cc_features->c[c][1][1] == SHADER_0 && cc_features->c[c][1][3] == SHADER_0;
|
||||
cc_features->do_mix[c][0] = cc_features->c[c][0][1] == cc_features->c[c][0][3];
|
||||
cc_features->do_mix[c][1] = cc_features->c[c][1][1] == cc_features->c[c][1][3];
|
||||
cc_features->color_alpha_same[c] =
|
||||
((shader_id0 >> c * 32) & 0xffff) == ((shader_id0 >> (c * 32 + 16)) & 0xffff);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef GFX_CC_H
|
||||
#define GFX_CC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <compare>
|
||||
#endif
|
||||
|
||||
enum {
|
||||
SHADER_0,
|
||||
SHADER_INPUT_1,
|
||||
SHADER_INPUT_2,
|
||||
SHADER_INPUT_3,
|
||||
SHADER_INPUT_4,
|
||||
SHADER_INPUT_5,
|
||||
SHADER_INPUT_6,
|
||||
SHADER_INPUT_7,
|
||||
SHADER_TEXEL0,
|
||||
SHADER_TEXEL0A,
|
||||
SHADER_TEXEL1,
|
||||
SHADER_TEXEL1A,
|
||||
SHADER_1,
|
||||
SHADER_COMBINED,
|
||||
SHADER_NOISE,
|
||||
SHADER_LOD_FRAC,
|
||||
};
|
||||
|
||||
#define SHADER_OPT_ALPHA (1 << 0)
|
||||
#define SHADER_OPT_FOG (1 << 1)
|
||||
#define SHADER_OPT_TEXTURE_EDGE (1 << 2)
|
||||
#define SHADER_OPT_NOISE (1 << 3)
|
||||
#define SHADER_OPT_2CYC (1 << 4)
|
||||
#define SHADER_OPT_ALPHA_THRESHOLD (1 << 5)
|
||||
#define SHADER_OPT_INVISIBLE (1 << 6)
|
||||
#define SHADER_OPT_GRAYSCALE (1 << 7)
|
||||
#define SHADER_OPT_TEXEL0_CLAMP_S (1 << 8)
|
||||
#define SHADER_OPT_TEXEL0_CLAMP_T (1 << 9)
|
||||
#define SHADER_OPT_TEXEL1_CLAMP_S (1 << 10)
|
||||
#define SHADER_OPT_TEXEL1_CLAMP_T (1 << 11)
|
||||
#define SHADER_OPT_BLUR (1 << 12)
|
||||
|
||||
struct ColorCombinerKey {
|
||||
uint64_t combine_mode;
|
||||
uint64_t options;
|
||||
auto operator<=>(const ColorCombinerKey&) const = default;
|
||||
};
|
||||
|
||||
#define SHADER_MAX_TEXTURES 2
|
||||
#define SHADER_FIRST_TEXTURE 0
|
||||
|
||||
struct CCFeatures {
|
||||
uint8_t c[2][2][4];
|
||||
bool opt_alpha;
|
||||
bool opt_fog;
|
||||
bool opt_texture_edge;
|
||||
bool opt_noise;
|
||||
bool opt_2cyc;
|
||||
bool opt_alpha_threshold;
|
||||
bool opt_invisible;
|
||||
bool opt_grayscale;
|
||||
bool opt_blur;
|
||||
bool used_textures[2];
|
||||
bool clamp[2][2];
|
||||
int num_inputs;
|
||||
bool do_single[2][2];
|
||||
bool do_multiply[2][2];
|
||||
bool do_mix[2][2];
|
||||
bool color_alpha_same[2];
|
||||
};
|
||||
|
||||
void gfx_cc_get_features(uint64_t shader_id0, uint32_t shader_id1, struct CCFeatures* cc_features);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,8 @@
|
||||
#ifndef GFX_OPENGL_H
|
||||
#define GFX_OPENGL_H
|
||||
|
||||
#include "gfx_rendering_api.h"
|
||||
|
||||
extern struct GfxRenderingAPI gfx_opengl_api;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,60 @@
|
||||
#ifndef GFX_PC_H
|
||||
#define GFX_PC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <cstddef>
|
||||
|
||||
#include <PR/gbi.h>
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#define SCREEN_WIDTH ((int32_t)gfx_current_native_viewport.width)
|
||||
#define SCREEN_HEIGHT ((int32_t)gfx_current_native_viewport.height)
|
||||
|
||||
extern uintptr_t gfxFramebuffer;
|
||||
|
||||
struct GfxRenderingAPI;
|
||||
struct GfxWindowManagerAPI;
|
||||
|
||||
struct TextureCacheKey {
|
||||
const uint8_t* texture_addr;
|
||||
const uint8_t* palette_addrs[2];
|
||||
uint8_t fmt, siz;
|
||||
uint8_t palette_index;
|
||||
|
||||
bool operator==(const TextureCacheKey&) const noexcept = default;
|
||||
|
||||
struct Hasher {
|
||||
size_t operator()(const TextureCacheKey& key) const noexcept {
|
||||
uintptr_t addr = (uintptr_t)key.texture_addr;
|
||||
return (size_t)(addr ^ (addr >> 5));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
typedef std::unordered_map<TextureCacheKey, struct TextureCacheValue, TextureCacheKey::Hasher> TextureCacheMap;
|
||||
typedef std::pair<const TextureCacheKey, struct TextureCacheValue> TextureCacheNode;
|
||||
|
||||
struct TextureCacheValue {
|
||||
uint32_t texture_id;
|
||||
uint8_t cms, cmt;
|
||||
bool linear_filter;
|
||||
|
||||
std::list<struct TextureCacheMapIter>::iterator lru_location;
|
||||
};
|
||||
|
||||
struct TextureCacheMapIter {
|
||||
TextureCacheMap::iterator it;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "gfx_api.h"
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef GFX_RENDERING_API_H
|
||||
#define GFX_RENDERING_API_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct ShaderProgram;
|
||||
|
||||
struct GfxClipParameters {
|
||||
bool z_is_from_0_to_1;
|
||||
bool invert_y;
|
||||
};
|
||||
|
||||
enum FilteringMode { FILTER_NONE, FILTER_LINEAR, FILTER_THREE_POINT };
|
||||
enum MipmapFilteringMode { MIPMAP_DISABLED, MIPMAP_NEAREST, MIPMAP_LINEAR };
|
||||
|
||||
struct GfxRenderingAPI {
|
||||
const char* (*get_name)(void);
|
||||
int (*get_max_texture_size)(void);
|
||||
struct GfxClipParameters (*get_clip_parameters)(void);
|
||||
void (*unload_shader)(struct ShaderProgram* old_prg);
|
||||
void (*load_shader)(struct ShaderProgram* new_prg);
|
||||
struct ShaderProgram* (*create_and_load_new_shader)(uint64_t shader_id0, uint32_t shader_id1);
|
||||
struct ShaderProgram* (*lookup_shader)(uint64_t shader_id0, uint32_t shader_id1);
|
||||
void (*shader_get_info)(struct ShaderProgram* prg, uint8_t* num_inputs, bool used_textures[2]);
|
||||
void (*clear_shaders)(void);
|
||||
uint32_t (*new_texture)(void);
|
||||
void (*select_texture)(int tile, uint32_t texture_id, bool linear_filter);
|
||||
void (*upload_texture)(const uint8_t* rgba32_buf, uint32_t width, uint32_t height, bool gen_mipmaps);
|
||||
void (*set_sampler_parameters)(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt, bool mipmaps);
|
||||
void (*set_depth_mode)(bool depth_test, bool depth_update, bool depth_compare, bool depth_source_prim, uint16_t zmode);
|
||||
void (*set_depth_range)(float znear, float zfar);
|
||||
void (*set_viewport)(int x, int y, int width, int height);
|
||||
void (*set_scissor)(int x, int y, int width, int height);
|
||||
void (*set_use_alpha)(bool use_alpha, bool modulate);
|
||||
void (*draw_triangles)(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris);
|
||||
void (*init)(void);
|
||||
void (*on_resize)(void);
|
||||
void (*start_frame)(void);
|
||||
void (*end_frame)(void);
|
||||
void (*finish_render)(void);
|
||||
int (*create_framebuffer)();
|
||||
void (*update_framebuffer_parameters)(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level,
|
||||
bool opengl_invert_y, bool render_target, bool has_depth_buffer,
|
||||
bool can_extract_depth);
|
||||
bool (*start_draw_to_framebuffer)(int fb_id, float noise_scale);
|
||||
void (*copy_framebuffer)(int fb_dst, int fb_src, int left, int top, bool flip_y, bool use_back);
|
||||
void (*clear_framebuffer)(bool clear_color, bool clear_depth);
|
||||
void (*resolve_msaa_color_buffer)(int fb_id_target, int fb_id_source);
|
||||
void* (*get_framebuffer_texture_id)(int fb_id);
|
||||
void (*select_texture_fb)(int fb_id);
|
||||
void (*delete_texture)(uint32_t texID);
|
||||
void (*set_texture_filter)(enum FilteringMode mode);
|
||||
enum FilteringMode (*get_texture_filter)(void);
|
||||
void (*set_mipmap_filter)(enum MipmapFilteringMode mode);
|
||||
void (*set_anisotropy_level)(int);
|
||||
int (*get_max_anisotropy_level)(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef GFX_SCREEN_CONFIG_H
|
||||
#define GFX_SCREEN_CONFIG_H
|
||||
|
||||
#define DESIRED_SCREEN_WIDTH 640
|
||||
#define DESIRED_SCREEN_HEIGHT 480
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef GFX_SDL_H
|
||||
#define GFX_SDL_H
|
||||
|
||||
#include "gfx_window_manager_api.h"
|
||||
|
||||
extern struct GfxWindowManagerAPI gfx_sdl;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,463 @@
|
||||
#include <stdio.h>
|
||||
#include <SDL.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "gfx_window_manager_api.h"
|
||||
#include "gfx_screen_config.h"
|
||||
|
||||
static SDL_Window* wnd;
|
||||
static SDL_GLContext ctx;
|
||||
static SDL_Renderer* renderer;
|
||||
static int sdl_to_lus_table[512];
|
||||
static bool vsync_enabled = true;
|
||||
// OTRTODO: These are redundant. Info can be queried from SDL.
|
||||
static int window_width = DESIRED_SCREEN_WIDTH;
|
||||
static int window_height = DESIRED_SCREEN_HEIGHT;
|
||||
static uint32_t fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
static bool fullscreen_state;
|
||||
static bool maximized_state;
|
||||
static bool is_running = true;
|
||||
static void (*on_fullscreen_changed_callback)(bool is_now_fullscreen);
|
||||
|
||||
static int target_fps = 120; // above 60 since vsync is enabled by default
|
||||
static uint64_t previous_time;
|
||||
static uint64_t qpc_freq;
|
||||
|
||||
#define FRAME_INTERVAL_US_NUMERATOR 1000000
|
||||
#define FRAME_INTERVAL_US_DENOMINATOR (target_fps)
|
||||
|
||||
static int32_t gfx_sdl_get_maximized_state(void) {
|
||||
return (int32_t)maximized_state;
|
||||
}
|
||||
|
||||
static int32_t gfx_sdl_get_fullscreen_state(void) {
|
||||
return (int32_t)fullscreen_state;
|
||||
}
|
||||
|
||||
static int32_t gfx_sdl_get_fullscreen_flag_mode(void) {
|
||||
return fullscreen_flag == SDL_WINDOW_FULLSCREEN_DESKTOP ? 0 : 1;
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_fullscreen_flag(int32_t mode) {
|
||||
switch (mode) {
|
||||
case 0: {
|
||||
fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
} break;
|
||||
case 1: {
|
||||
fullscreen_flag = SDL_WINDOW_FULLSCREEN;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_fullscreen(bool on, bool call_callback) {
|
||||
fullscreen_state = on;
|
||||
SDL_SetWindowFullscreen(wnd, on ? fullscreen_flag : 0);
|
||||
if (call_callback && on_fullscreen_changed_callback) {
|
||||
on_fullscreen_changed_callback(on);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_maximize_window(bool on) {
|
||||
maximized_state = on;
|
||||
if (on) {
|
||||
SDL_MaximizeWindow(wnd);
|
||||
} else {
|
||||
SDL_RestoreWindow (wnd);
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_sdl_get_active_window_refresh_rate(uint32_t* refresh_rate) {
|
||||
int display_in_use = SDL_GetWindowDisplayIndex(wnd);
|
||||
|
||||
SDL_DisplayMode mode;
|
||||
SDL_GetCurrentDisplayMode(display_in_use, &mode);
|
||||
*refresh_rate = mode.refresh_rate;
|
||||
}
|
||||
|
||||
static void gfx_sdl_init(const struct GfxWindowInitSettings *set) {
|
||||
window_width = set->width;
|
||||
window_height = set->height;
|
||||
|
||||
#ifdef SDL_HINT_VIDEO_HIGHDPI_DISABLED
|
||||
if (!set->allow_hidpi) {
|
||||
// HiDPI control, if available
|
||||
SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1");
|
||||
#if defined(PLATFORM_WIN32) && defined(SDL_HINT_WINDOWS_DPI_AWARENESS)
|
||||
// if HiDPI is disabled, declare ourselves DPI aware to get 1:1 window size on Windows
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitor");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
sysFatalError("Could not init SDL:\n%s", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
if (sysArgCheck("--debug-gl")) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||
}
|
||||
|
||||
int posX = set->x;
|
||||
int posY = set->y;
|
||||
int display_in_use = SDL_GetWindowDisplayIndex(wnd);
|
||||
if (display_in_use < 0) { // Fallback to default if out of bounds
|
||||
posX = SDL_WINDOWPOS_UNDEFINED;
|
||||
posY = SDL_WINDOWPOS_UNDEFINED;
|
||||
}
|
||||
|
||||
if (set->centered) {
|
||||
SDL_DisplayMode mode = {};
|
||||
SDL_GetCurrentDisplayMode(0, &mode);
|
||||
posX = mode.w / 2 - window_width / 2;
|
||||
posY = mode.h / 2 - window_height / 2;
|
||||
}
|
||||
|
||||
if (set->fullscreen_is_exclusive) {
|
||||
fullscreen_flag = SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
|
||||
// we will unhide the window once the GL context is successfully created
|
||||
Uint32 flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL;
|
||||
|
||||
// if fullscreen was requested, start the window in fullscreen right away
|
||||
if (set->fullscreen) {
|
||||
flags |= fullscreen_flag;
|
||||
fullscreen_state = true;
|
||||
}
|
||||
|
||||
if (set->maximized) {
|
||||
flags |= SDL_WINDOW_MAXIMIZED;
|
||||
maximized_state = true;
|
||||
}
|
||||
|
||||
#ifdef SDL_WINDOW_ALLOW_HIGHDPI
|
||||
if (set->allow_hidpi) {
|
||||
flags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ideally we need 3.0 compat
|
||||
// if that doesn't work, try 3.2 core in case we're on mac, 2.1 compat as a last resort
|
||||
static u32 glver[][3] = {
|
||||
{ 0, 0, 0 }, // for command line override
|
||||
{ 3, 0, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY }, // 3.0: default, has all the features required
|
||||
{ 4, 1, SDL_GL_CONTEXT_PROFILE_CORE }, // 4.1core: macs only have core profile and this is the latest
|
||||
{ 3, 2, SDL_GL_CONTEXT_PROFILE_CORE }, // 3.2core: older macs will only have this at best
|
||||
{ 3, 0, SDL_GL_CONTEXT_PROFILE_ES }, // es3: don't really support ES properly, but we can try
|
||||
{ 2, 1, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY }, // 2.1: absolute last resort, will still require GLSL130 as an extension
|
||||
};
|
||||
|
||||
u32 verstart = 1;
|
||||
const u32 verend = sizeof(glver) / sizeof(*glver);
|
||||
const char *verstr = sysArgGetString("--gl-version");
|
||||
if (verstr && *verstr) {
|
||||
// user override
|
||||
glver[0][2] = strstr(verstr, "core") ? SDL_GL_CONTEXT_PROFILE_CORE :
|
||||
(strstr(verstr, "es") ? SDL_GL_CONTEXT_PROFILE_ES :
|
||||
SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
||||
sscanf(verstr, "%d.%d", &glver[0][0], &glver[0][1]);
|
||||
if (glver[0][0] >= 1 && glver[0][0] <= 4 && glver[0][1] < 9) {
|
||||
verstart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ctx = NULL;
|
||||
u32 vmin = 0, vmaj = 0, vprof = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
|
||||
const char *vprofstr = "";
|
||||
for (u32 i = verstart; i < verend && !ctx; ++i) {
|
||||
vmaj = glver[i][0];
|
||||
vmin = glver[i][1];
|
||||
vprof = glver[i][2];
|
||||
vprofstr = (vprof == SDL_GL_CONTEXT_PROFILE_CORE ? "core" :
|
||||
(vprof == SDL_GL_CONTEXT_PROFILE_ES ? "es" : ""));
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, vmaj);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, vmin);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, vprof);
|
||||
|
||||
wnd = SDL_CreateWindow(set->title, posX, posY, window_width, window_height, flags);
|
||||
if (!wnd) {
|
||||
sysLogPrintf(LOG_WARNING, "SDL: could not open SDL window for GL%d.%d%s:\n%s", vmaj, vmin, vprofstr, SDL_GetError());
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx = SDL_GL_CreateContext(wnd);
|
||||
if (!ctx) {
|
||||
sysLogPrintf(LOG_WARNING, "SDL: could not create GL%d.%d%s context: %s", vmaj, vmin, vprofstr, SDL_GetError());
|
||||
SDL_DestroyWindow(wnd);
|
||||
wnd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!wnd || !ctx) {
|
||||
sysFatalError("Could not open SDL window with an OpenGL context of any supported version:\n%s", SDL_GetError());
|
||||
} else {
|
||||
sysLogPrintf(LOG_NOTE, "SDL: created GL%d.%d%s context", vmaj, vmin, vprofstr);
|
||||
}
|
||||
|
||||
SDL_GL_MakeCurrent(wnd, ctx);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
SDL_ShowWindow(wnd);
|
||||
|
||||
qpc_freq = SDL_GetPerformanceFrequency();
|
||||
}
|
||||
|
||||
static void gfx_sdl_close(void) {
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_fullscreen_changed_callback(void (*on_fullscreen_changed)(bool is_now_fullscreen)) {
|
||||
on_fullscreen_changed_callback = on_fullscreen_changed;
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_fullscreen(bool enable) {
|
||||
set_fullscreen(enable, true);
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_fullscreen_exclusive(bool enable) {
|
||||
const uint32_t newflag = enable ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
if (fullscreen_flag != newflag) {
|
||||
fullscreen_flag = newflag;
|
||||
// reset fullscreen to take new value into account if it already is in fullscreen
|
||||
if (fullscreen_state) {
|
||||
fullscreen_state = false;
|
||||
set_fullscreen(enable, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_maximize_window(bool enable) {
|
||||
set_maximize_window(enable);
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_cursor_visibility(bool visible) {
|
||||
if (visible) {
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
} else {
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void get_centered_positions_native(int32_t width, int32_t height, int32_t *posX, int32_t *posY) {
|
||||
const int disp_idx = SDL_GetWindowDisplayIndex(wnd);
|
||||
SDL_DisplayMode mode = {};
|
||||
SDL_GetDesktopDisplayMode(disp_idx, &mode);
|
||||
*posX = mode.w / 2 - width / 2;
|
||||
*posY = mode.h / 2 - height / 2;
|
||||
}
|
||||
|
||||
static void gfx_sdl_get_centered_positions(int32_t width, int32_t height, int32_t *posX, int32_t *posY) {
|
||||
const int disp_idx = SDL_GetWindowDisplayIndex(wnd);
|
||||
SDL_DisplayMode mode = {};
|
||||
SDL_GetCurrentDisplayMode(disp_idx, &mode);
|
||||
*posX = mode.w / 2 - width / 2;
|
||||
*posY = mode.h / 2 - height / 2;
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_closest_resolution(int32_t width, int32_t height, bool should_center) {
|
||||
const SDL_DisplayMode mode = {.w = width, .h = height};
|
||||
const int disp_idx = SDL_GetWindowDisplayIndex(wnd);
|
||||
SDL_DisplayMode closest = {};
|
||||
if (SDL_GetClosestDisplayMode(disp_idx, &mode, &closest)) {
|
||||
SDL_SetWindowDisplayMode(wnd, &closest);
|
||||
SDL_SetWindowSize(wnd, closest.w, closest.h);
|
||||
if (should_center) {
|
||||
int32_t posX = 0;
|
||||
int32_t posY = 0;
|
||||
get_centered_positions_native(closest.w, closest.h, &posX, &posY);
|
||||
SDL_SetWindowPosition(wnd, posX, posY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_dimensions(uint32_t width, uint32_t height, int32_t posX, int32_t posY) {
|
||||
SDL_SetWindowSize(wnd, width, height);
|
||||
SDL_SetWindowPosition(wnd, posX, posY);
|
||||
}
|
||||
|
||||
static void gfx_sdl_get_dimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY) {
|
||||
SDL_GL_GetDrawableSize(wnd, static_cast<int*>((void*)width), static_cast<int*>((void*)height));
|
||||
SDL_GetWindowPosition(wnd, static_cast<int*>(posX), static_cast<int*>(posY));
|
||||
}
|
||||
|
||||
static void gfx_sdl_handle_events(void) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
if (event.key.keysym.sym == SDLK_RETURN && (event.key.keysym.mod & KMOD_ALT)) {
|
||||
// alt-enter received, switch fullscreen state
|
||||
set_fullscreen(!fullscreen_state, true);
|
||||
}
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
||||
SDL_GL_GetDrawableSize(wnd, &window_width, &window_height);
|
||||
if (!fullscreen_state) {
|
||||
maximized_state = SDL_GetWindowFlags(wnd) & SDL_WINDOW_MAXIMIZED ? true : false;
|
||||
}
|
||||
} else if (event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
||||
event.window.windowID == SDL_GetWindowID(wnd)) {
|
||||
// We listen specifically for main window close because closing main window
|
||||
// on macOS does not trigger SDL_Quit.
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool gfx_sdl_start_frame(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t qpc_to_100ns(uint64_t qpc) {
|
||||
return qpc / qpc_freq * 10000000 + qpc % qpc_freq * 10000000 / qpc_freq;
|
||||
}
|
||||
|
||||
static inline void sync_framerate_with_timer(void) {
|
||||
uint64_t t;
|
||||
t = qpc_to_100ns(SDL_GetPerformanceCounter());
|
||||
|
||||
const int64_t next = previous_time + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR;
|
||||
int64_t left = next - t;
|
||||
// We want to exit a bit early, so we can busy-wait the rest to never miss the deadline
|
||||
left -= 15000UL;
|
||||
if (left > 0) {
|
||||
sysSleep(left);
|
||||
}
|
||||
|
||||
do {
|
||||
sysCpuRelax();
|
||||
t = qpc_to_100ns(SDL_GetPerformanceCounter());
|
||||
} while ((int64_t)t < next);
|
||||
|
||||
t = qpc_to_100ns(SDL_GetPerformanceCounter());
|
||||
if (left > 0 && t - next < 10000) {
|
||||
// In case it takes some time for the application to wake up after sleep,
|
||||
// or inaccurate timer,
|
||||
// don't let that slow down the framerate.
|
||||
t = next;
|
||||
}
|
||||
previous_time = t;
|
||||
}
|
||||
|
||||
static void gfx_sdl_swap_buffers_begin(void) {
|
||||
if (target_fps) {
|
||||
sync_framerate_with_timer();
|
||||
}
|
||||
SDL_GL_SwapWindow(wnd);
|
||||
}
|
||||
|
||||
static void gfx_sdl_swap_buffers_end(void) {
|
||||
|
||||
}
|
||||
|
||||
static double gfx_sdl_get_time(void) {
|
||||
return SDL_GetPerformanceCounter() / (double)qpc_freq;
|
||||
}
|
||||
|
||||
static int32_t gfx_sdl_get_target_fps(void) {
|
||||
return target_fps;
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_target_fps(int fps) {
|
||||
target_fps = fps;
|
||||
}
|
||||
|
||||
static bool gfx_sdl_can_disable_vsync(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *gfx_sdl_get_window_handle(void) {
|
||||
return (void *)wnd;
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_window_title(const char *title) {
|
||||
SDL_SetWindowTitle(wnd, title);
|
||||
}
|
||||
|
||||
static int gfx_sdl_get_swap_interval(void) {
|
||||
return SDL_GL_GetSwapInterval();
|
||||
}
|
||||
|
||||
static bool gfx_sdl_set_swap_interval(int interval) {
|
||||
const bool success = SDL_GL_SetSwapInterval(interval) >= 0;
|
||||
vsync_enabled = success && (interval != 0);
|
||||
if (!success) {
|
||||
sysLogPrintf(LOG_WARNING, "SDL: failed to set vsync %d: %s", interval, SDL_GetError());
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
int gfx_sdl_get_display_mode(int modenum, int *out_w, int *out_h) {
|
||||
const int display_in_use = SDL_GetWindowDisplayIndex(wnd);
|
||||
SDL_DisplayMode sdlmode;
|
||||
if (SDL_GetDisplayMode(display_in_use, modenum, &sdlmode) == 0) {
|
||||
*out_w = sdlmode.w;
|
||||
*out_h = sdlmode.h;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gfx_sdl_get_current_display_mode(int *out_w, int *out_h) {
|
||||
const int display_in_use = SDL_GetWindowDisplayIndex(wnd);
|
||||
SDL_DisplayMode sdlmode;
|
||||
if (SDL_GetCurrentDisplayMode(display_in_use, &sdlmode) == 0) {
|
||||
*out_w = sdlmode.w;
|
||||
*out_h = sdlmode.h;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gfx_sdl_get_num_display_modes(void) {
|
||||
const int display_in_use = SDL_GetWindowDisplayIndex(wnd);
|
||||
return SDL_GetNumDisplayModes(display_in_use);
|
||||
}
|
||||
|
||||
struct GfxWindowManagerAPI gfx_sdl = {
|
||||
gfx_sdl_init,
|
||||
gfx_sdl_close,
|
||||
gfx_sdl_get_display_mode,
|
||||
gfx_sdl_get_current_display_mode,
|
||||
gfx_sdl_get_num_display_modes,
|
||||
gfx_sdl_get_fullscreen_state,
|
||||
gfx_sdl_set_fullscreen_changed_callback,
|
||||
gfx_sdl_set_fullscreen,
|
||||
gfx_sdl_set_fullscreen_exclusive,
|
||||
gfx_sdl_set_fullscreen_flag,
|
||||
gfx_sdl_get_fullscreen_flag_mode,
|
||||
gfx_sdl_get_maximized_state,
|
||||
gfx_sdl_set_maximize_window,
|
||||
gfx_sdl_get_active_window_refresh_rate,
|
||||
gfx_sdl_set_cursor_visibility,
|
||||
gfx_sdl_set_closest_resolution,
|
||||
gfx_sdl_set_dimensions,
|
||||
gfx_sdl_get_dimensions,
|
||||
gfx_sdl_get_centered_positions,
|
||||
gfx_sdl_handle_events,
|
||||
gfx_sdl_start_frame,
|
||||
gfx_sdl_swap_buffers_begin,
|
||||
gfx_sdl_swap_buffers_end,
|
||||
gfx_sdl_get_time,
|
||||
gfx_sdl_get_target_fps,
|
||||
gfx_sdl_set_target_fps,
|
||||
gfx_sdl_can_disable_vsync,
|
||||
gfx_sdl_get_window_handle,
|
||||
gfx_sdl_set_window_title,
|
||||
gfx_sdl_get_swap_interval,
|
||||
gfx_sdl_set_swap_interval,
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef GFX_WINDOW_MANAGER_API_H
|
||||
#define GFX_WINDOW_MANAGER_API_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct GfxWindowInitSettings {
|
||||
const char *title;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
bool fullscreen;
|
||||
bool fullscreen_is_exclusive;
|
||||
bool maximized;
|
||||
bool centered;
|
||||
bool allow_hidpi;
|
||||
};
|
||||
|
||||
struct GfxWindowManagerAPI {
|
||||
void (*init)(const struct GfxWindowInitSettings *settings);
|
||||
void (*close)(void);
|
||||
int (*get_display_mode)(int modenum, int *out_w, int *out_h);
|
||||
int (*get_current_display_mode)(int *out_w, int *out_h);
|
||||
int (*get_num_display_modes)(void);
|
||||
int32_t (*get_fullscreen_state)(void);
|
||||
void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen));
|
||||
void (*set_fullscreen)(bool enable);
|
||||
void (*set_fullscreen_exclusive)(bool exc);
|
||||
void (*set_fullscreen_flag)(int32_t mode);
|
||||
int32_t (*get_fullscreen_flag_mode)(void);
|
||||
int32_t (*get_maximized_state)(void);
|
||||
void (*set_maximize)(bool enable);
|
||||
void (*get_active_window_refresh_rate)(uint32_t* refresh_rate);
|
||||
void (*set_cursor_visibility)(bool visible);
|
||||
void (*set_closest_resolution)(int32_t width, int32_t height, bool should_center);
|
||||
void (*set_dimensions)(uint32_t width, uint32_t height, int32_t posX, int32_t posY);
|
||||
void (*get_dimensions)(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY);
|
||||
void (*get_centered_positions)(int32_t width, int32_t height, int32_t *posX, int32_t *posY);
|
||||
void (*handle_events)(void);
|
||||
bool (*start_frame)(void);
|
||||
void (*swap_buffers_begin)(void);
|
||||
void (*swap_buffers_end)(void);
|
||||
double (*get_time)(void); // For debug
|
||||
int32_t (*get_target_fps)(void);
|
||||
void (*set_target_fps)(int fps);
|
||||
bool (*can_disable_vsync)(void);
|
||||
void *(*get_window_handle)(void);
|
||||
void (*set_window_title)(const char *);
|
||||
int (*get_swap_interval)(void);
|
||||
bool (*set_swap_interval)(int);
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,311 @@
|
||||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef _IN_AUDIO_H
|
||||
#define _IN_AUDIO_H
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
s32 audioInit(void);
|
||||
s32 audioGetBytesBuffered(void);
|
||||
s32 audioGetSamplesBuffered(void);
|
||||
void audioSetNextBuffer(const s16 *buf, u32 len);
|
||||
void audioEndFrame(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
#define CONFIG_FNAME "pd.ini"
|
||||
#define CONFIG_PATH "$S/" CONFIG_FNAME
|
||||
|
||||
void configInit(void);
|
||||
|
||||
// loads config from file (path extensions such as ! apply)
|
||||
s32 configLoad(const char *fname);
|
||||
|
||||
// saves config to file (path extensions such as ! apply)
|
||||
s32 configSave(const char *fname);
|
||||
|
||||
// registers a variable in the config file
|
||||
// this should be done before configInit() is called, preferably in a module constructor
|
||||
void configRegisterInt(const char *key, s32 *var, s32 min, s32 max);
|
||||
void configRegisterUInt(const char* key, u32* var, u32 min, u32 max);
|
||||
void configRegisterFloat(const char *key, f32 *var, f32 min, f32 max);
|
||||
void configRegisterString(const char *key, char *var, u32 maxstr);
|
||||
Vendored
+1865
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,27 @@
|
||||
#ifndef _IN_FS_H
|
||||
#define _IN_FS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
#define FS_MAXPATH 1024
|
||||
|
||||
s32 fsInit(void);
|
||||
|
||||
const char *fsFullPath(const char *relPath);
|
||||
|
||||
s32 fsPathIsAbsolute(const char *path);
|
||||
s32 fsPathIsCwdRelative(const char *path);
|
||||
|
||||
void *fsFileLoad(const char *name, u32 *outSize);
|
||||
s32 fsFileLoadTo(const char *name, void *dst, u32 dstSize);
|
||||
s32 fsFileSize(const char *name);
|
||||
|
||||
FILE *fsFileOpenWrite(const char *name);
|
||||
FILE *fsFileOpenRead(const char *name);
|
||||
void fsFileFree(FILE *f);
|
||||
|
||||
const char *fsGetModDir(void);
|
||||
s32 fsCreateDir(const char *path);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,273 @@
|
||||
#ifndef _IN_INPUT_H
|
||||
#define _IN_INPUT_H
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
#include <PR/os_cont.h>
|
||||
|
||||
#define INPUT_MAX_CONTROLLERS MAXCONTROLLERS
|
||||
#define INPUT_MAX_CONNECTED_CONTROLLERS 8
|
||||
#define INPUT_MAX_CONTROLLER_BUTTONS 32
|
||||
#define INPUT_MAX_BINDS 4
|
||||
|
||||
#define CONT_STICK_XNEG 0x10000
|
||||
#define CONT_STICK_XPOS 0x20000
|
||||
#define CONT_STICK_YNEG 0x40000
|
||||
#define CONT_STICK_YPOS 0x80000
|
||||
|
||||
#define CONT_NUM_BUTTONS 32 // not including the stick axes
|
||||
|
||||
enum virtkey {
|
||||
/* same order as SDL scancodes */
|
||||
VK_KEYBOARD_BEGIN = 0,
|
||||
VK_A = 4,
|
||||
VK_Z = 29,
|
||||
VK_1 = 30,
|
||||
VK_9 = 38,
|
||||
VK_0 = 39,
|
||||
VK_RETURN = 40,
|
||||
VK_ESCAPE = 41,
|
||||
VK_BACKSPACE = 42,
|
||||
VK_SPACE = 44,
|
||||
VK_MINUS = 45,
|
||||
VK_LEFTBRACKET = 47,
|
||||
VK_RIGHTBRACKET = 48,
|
||||
VK_SEMICOLON = 51,
|
||||
VK_GRAVE = 53,
|
||||
VK_COMMA = 54,
|
||||
VK_PERIOD = 55,
|
||||
VK_F1 = 58,
|
||||
VK_F9 = 66,
|
||||
VK_DELETE = 76,
|
||||
VK_LCTRL = 224,
|
||||
VK_LSHIFT = 225,
|
||||
VK_LALT = 226,
|
||||
VK_RCTRL = 228,
|
||||
VK_RSHIFT = 229,
|
||||
|
||||
/* same order as SDL mouse buttons */
|
||||
VK_MOUSE_BEGIN = 512,
|
||||
VK_MOUSE_LEFT = VK_MOUSE_BEGIN,
|
||||
VK_MOUSE_MIDDLE,
|
||||
VK_MOUSE_RIGHT,
|
||||
VK_MOUSE_X1,
|
||||
VK_MOUSE_X2,
|
||||
VK_MOUSE_WHEEL_UP,
|
||||
VK_MOUSE_WHEEL_DN,
|
||||
|
||||
/* same order as SDL gamecontroller buttons plus two buttons for triggers */
|
||||
VK_JOY_BEGIN,
|
||||
VK_JOY1_BEGIN = VK_JOY_BEGIN,
|
||||
VK_JOY1_LSHOULDER = VK_JOY1_BEGIN + 9,
|
||||
VK_JOY1_RSHOULDER = VK_JOY1_BEGIN + 10,
|
||||
VK_JOY1_LTRIG = VK_JOY1_BEGIN + 30,
|
||||
VK_JOY1_RTRIG = VK_JOY1_BEGIN + 31,
|
||||
VK_JOY2_BEGIN = VK_JOY1_BEGIN + INPUT_MAX_CONTROLLER_BUTTONS,
|
||||
VK_JOY3_BEGIN = VK_JOY2_BEGIN + INPUT_MAX_CONTROLLER_BUTTONS,
|
||||
VK_JOY4_BEGIN = VK_JOY3_BEGIN + INPUT_MAX_CONTROLLER_BUTTONS,
|
||||
|
||||
VK_TOTAL_COUNT = VK_JOY_BEGIN + INPUT_MAX_CONTROLLERS * INPUT_MAX_CONTROLLER_BUTTONS,
|
||||
};
|
||||
|
||||
enum keymod {
|
||||
/* same order as SDL keymods */
|
||||
KM_LSHIFT = 0x0001,
|
||||
KM_RSHIFT = 0x0002,
|
||||
KM_LCTRL = 0x0040,
|
||||
KM_RCTRL = 0x0080,
|
||||
KM_CAPS = 0x2000,
|
||||
KM_CTRL = KM_LCTRL | KM_RCTRL,
|
||||
KM_SHIFT = KM_LSHIFT | KM_RSHIFT
|
||||
};
|
||||
|
||||
enum contkey {
|
||||
CK_C_R,
|
||||
CK_C_L,
|
||||
CK_C_D,
|
||||
CK_C_U,
|
||||
CK_RTRIG,
|
||||
CK_LTRIG,
|
||||
CK_X, // gap in CONT_
|
||||
CK_Y, // gap in CONT_
|
||||
CK_DPAD_R,
|
||||
CK_DPAD_L,
|
||||
CK_DPAD_D,
|
||||
CK_DPAD_U,
|
||||
CK_START,
|
||||
CK_ZTRIG,
|
||||
CK_B,
|
||||
CK_A,
|
||||
CK_STICK_XNEG,
|
||||
CK_STICK_XPOS,
|
||||
CK_STICK_YNEG,
|
||||
CK_STICK_YPOS,
|
||||
CK_ACCEPT,
|
||||
CK_CANCEL,
|
||||
CK_0040,
|
||||
CK_0080,
|
||||
CK_0100,
|
||||
CK_0200,
|
||||
CK_0400,
|
||||
CK_0800,
|
||||
CK_1000,
|
||||
CK_2000,
|
||||
CK_4000,
|
||||
CK_8000,
|
||||
CK_TOTAL_COUNT
|
||||
};
|
||||
|
||||
enum mouselockmode {
|
||||
MLOCK_OFF = 0,
|
||||
MLOCK_ON = 1,
|
||||
MLOCK_AUTO = 2
|
||||
};
|
||||
|
||||
// returns bitmask of connected controllers or -1 if failed
|
||||
s32 inputInit(void);
|
||||
|
||||
// read the specified player's inputs into the N64 pad struct
|
||||
// returns 0 if read, non-0 if failed
|
||||
s32 inputReadController(s32 idx, OSContPad *npad);
|
||||
|
||||
// returns 1 if rumble is supported for specified player's controller
|
||||
s32 inputRumbleSupported(s32 idx);
|
||||
|
||||
// returns 1 if specified player has a live controller assigned
|
||||
s32 inputControllerConnected(s32 idx);
|
||||
|
||||
// returns bitmask of players with assigned controllers
|
||||
s32 inputControllerMask(void);
|
||||
|
||||
// get/set Input.Player%d.SwapSticks
|
||||
s32 inputControllerGetSticksSwapped(s32 cidx);
|
||||
void inputControllerSetSticksSwapped(s32 cidx, s32 swapped);
|
||||
|
||||
// get/set Input.Player%d.StickCButtons (DualAnalog is 1 if StickCButtons is 0)
|
||||
s32 inputControllerGetDualAnalog(s32 cidx);
|
||||
void inputControllerSetDualAnalog(s32 cidx, s32 enable);
|
||||
|
||||
// get/set Input.Player%d.CancelCButtons
|
||||
s32 inputControllerGetCancelCButtons(s32 cidx);
|
||||
void inputControllerSetCancelCButtons(s32 cidx, s32 cancel);
|
||||
|
||||
// get/set sensitivity for a given player
|
||||
f32 inputControllerGetAxisScale(s32 cidx, s32 stick, s32 axis);
|
||||
void inputControllerSetAxisScale(s32 cidx, s32 stick, s32 axis, f32 value);
|
||||
|
||||
// get/set deadzone for a given player
|
||||
f32 inputControllerGetAxisDeadzone(s32 cidx, s32 stick, s32 axis);
|
||||
void inputControllerSetAxisDeadzone(s32 cidx, s32 stick, s32 axis, f32 value);
|
||||
|
||||
// writes array of up to INPUT_MAX_CONNECTED_CONTROLLERS controller IDs
|
||||
// for all the controllers available on this machine into out if it's not NULL
|
||||
// returns number of IDs that would've been written (or were written if out is not NULL)
|
||||
s32 inputGetConnectedControllers(s32 *out);
|
||||
|
||||
// get name of connected controller id
|
||||
// returns "Invalid" on failure
|
||||
const char *inputGetConnectedControllerName(s32 id);
|
||||
|
||||
// get id of the controller currently assigned to player cidx or -1 if none
|
||||
s32 inputGetAssignedControllerId(s32 cidx);
|
||||
|
||||
// assign connected controller id to player cidx
|
||||
// if id is -1, unassigns controller from player, if any
|
||||
// returns true on success, false on failure
|
||||
s32 inputAssignController(s32 cidx, s32 id);
|
||||
|
||||
// vk is a value from the virtkey enum above
|
||||
s32 inputKeyPressed(u32 vk);
|
||||
s32 inputKeyJustPressed(u32 vk);
|
||||
|
||||
// idx is controller index, contbtn is one of the CONT_ constants
|
||||
s32 inputButtonPressed(s32 idx, u32 contbtn);
|
||||
|
||||
// bind virtkey vk to n64 pad #idx's button/axis ck as represented by its contkey value
|
||||
// if bind is -1, picks a bind slot automatically
|
||||
void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk);
|
||||
|
||||
const u32 *inputKeyGetBinds(s32 idx, u32 ck);
|
||||
|
||||
// get VK_ value from human-readable name
|
||||
s32 inputGetKeyByName(const char *name);
|
||||
|
||||
// get human-readable name from VK_ value
|
||||
const char *inputGetKeyName(s32 vk);
|
||||
|
||||
// get CK_ value from human-readable name
|
||||
s32 inputGetContKeyByName(const char *name);
|
||||
|
||||
// get human-readable name from CK_ value
|
||||
const char *inputGetContKeyName(u32 ck);
|
||||
|
||||
// strength is 0 .. 1; 0 strength turns it off
|
||||
void inputRumble(s32 idx, f32 strength, f32 time);
|
||||
|
||||
f32 inputRumbleGetStrength(s32 cidx);
|
||||
void inputRumbleSetStrength(s32 cidx, f32 val);
|
||||
|
||||
// locks the mouse cursor in the window and makes it invisible if argument is true
|
||||
void inputLockMouse(s32 lock);
|
||||
|
||||
// returns the current state of the above
|
||||
s32 inputMouseIsLocked(void);
|
||||
|
||||
// sets x, y to mouse position in native viewport coordinates (ie, 320x240 most of the time)
|
||||
// returns true if mouse has moved this input frame
|
||||
s32 inputMouseGetPosition(s32 *x, s32 *y);
|
||||
|
||||
// returns changes in mouse position since last frame, in window coordinates
|
||||
void inputMouseGetRawDelta(s32 *dx, s32 *dy);
|
||||
|
||||
// returns changes in mouse position since last frame, scaled by sensitivity
|
||||
// returns 0, 0 when the mouse is not locked into the window
|
||||
void inputMouseGetScaledDelta(f32 *dx, f32 *dy);
|
||||
|
||||
// returns changes in mouse position since last frame, scaled by absolute sensitivity
|
||||
// returns 0, 0 when the mouse is not locked into the window
|
||||
void inputMouseGetAbsScaledDelta(f32 *dx, f32 *dy);
|
||||
|
||||
void inputMouseGetSpeed(f32 *x, f32 *y);
|
||||
void inputMouseSetSpeed(f32 x, f32 y);
|
||||
|
||||
s32 inputMouseIsEnabled(void);
|
||||
void inputMouseEnable(s32 enabled);
|
||||
|
||||
// call this every frame
|
||||
void inputUpdate(void);
|
||||
|
||||
// call this before configSave()
|
||||
void inputSaveBinds(void);
|
||||
|
||||
// reset given player's binds to either PC or N64 defaults
|
||||
void inputSetDefaultKeyBinds(s32 cidx, s32 n64mode);
|
||||
|
||||
// clear or get the last pressed button
|
||||
void inputClearLastKey(void);
|
||||
s32 inputGetLastKey(void);
|
||||
|
||||
// get/set Input.MouseLockMode
|
||||
s32 inputGetMouseLockMode(void);
|
||||
void inputSetMouseLockMode(s32 lockmode);
|
||||
|
||||
// same as inputLockMouse but works only if mouse is enabled and lockmode == MLOCK_AUTO
|
||||
s32 inputAutoLockMouse(s32 wantlock);
|
||||
|
||||
// show/hide mouse cursor; if mouse lock is on the cursor is always hidden
|
||||
void inputMouseShowCursor(s32 show);
|
||||
|
||||
void inputStartTextInput(void);
|
||||
void inputStopTextInput(void);
|
||||
s32 inputIsTextInputActive(void);
|
||||
|
||||
void inputClearLastTextChar(void);
|
||||
char inputGetLastTextChar(void);
|
||||
|
||||
s32 inputTextHandler(char *out, const u32 outSize, s32 *curCol, s32 oskCharsOnly);
|
||||
|
||||
void inputClearClipboard(void);
|
||||
const char *inputGetClipboard(void);
|
||||
|
||||
// returns keymod values
|
||||
u32 inputGetKeyModState(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef MIXER_H
|
||||
#define MIXER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ultra64.h>
|
||||
|
||||
#undef aSegment
|
||||
#undef aClearBuffer
|
||||
#undef aSetBuffer
|
||||
#undef aLoadBuffer
|
||||
#undef aSaveBuffer
|
||||
#undef aDMEMMove
|
||||
#undef aMix
|
||||
#undef aEnvMixer
|
||||
#undef aResample
|
||||
#undef aInterleave
|
||||
#undef aSetVolume
|
||||
#undef aSetVolume32
|
||||
#undef aSetLoop
|
||||
#undef aLoadADPCM
|
||||
#undef aADPCMdec
|
||||
|
||||
void aClearBufferImpl(uint16_t addr, int nbytes);
|
||||
void aLoadADPCMImpl(int num_entries_times_16, const int16_t *book_source_addr);
|
||||
void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes);
|
||||
void aSetLoopImpl(ADPCM_STATE *adpcm_loop_state);
|
||||
void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state, int nbytes, uint16_t inofs, uint16_t outofs);
|
||||
void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state, uint16_t inofs, uint8_t outflag);
|
||||
void aLoadBufferImpl(const void *source_addr, uint16_t dest_addr, uint16_t nbytes);
|
||||
void aSaveBufferImpl(uint16_t source_addr, int16_t *dest_addr, uint16_t nbytes);
|
||||
void aInterleaveImpl(void);
|
||||
void aMixImpl(uint8_t flags, int16_t gain, uint16_t in_addr, uint16_t out_addr);
|
||||
void aEnvMixerImpl(uint8_t flags, ENVMIX_STATE state, int16_t some_vol);
|
||||
void aSetVolumeImpl(uint8_t flags, int16_t v, int16_t t, int16_t r);
|
||||
void aPoleFilterImpl(uint8_t flags, int16_t gain, uint32_t t, uint32_t addr);
|
||||
void aDisableImpl(uint16_t outp, uint32_t b, uint32_t c);
|
||||
void aPlayMP3Impl(const void *mp3file, u32 mp3size, void *out, int reset);
|
||||
|
||||
#define aDisable(pkt, o, b, c) aDisableImpl(o, b, c)
|
||||
#define aClearBuffer(pkt, d, c) aClearBufferImpl(d, c)
|
||||
#define aLoadBuffer(pkt, c, d, s) aLoadBufferImpl((void *)(s), d, c)
|
||||
#define aSaveBuffer(pkt, c, s, d) aSaveBufferImpl(s, (int16_t *)(d), c)
|
||||
#define aLoadADPCM(pkt, c, d) aLoadADPCMImpl(c, (int16_t *)(d))
|
||||
#define aDMEMMove(pkt, i, o, c) aDMEMMoveImpl(i, o, c)
|
||||
#define aSetLoop(pkt, a) aSetLoopImpl((void *)(a))
|
||||
#define aADPCMdec(pkt, s, f, c, i, o) aADPCMdecImpl(f, (void *)(s), c, i, o)
|
||||
#define aResample(pkt, s, f, p, i, o) aResampleImpl(f, p, (void *)(s), i, o)
|
||||
#define aInterleave(pkt) aInterleaveImpl()
|
||||
#define aMix(pkt, f, g, i, o) aMixImpl(f, g, i, o)
|
||||
#define aEnvMixer(pkt, f, t, s) aEnvMixerImpl(f, (void *)(s), t)
|
||||
#define aSetVolume(pkt, f, v, t, r) aSetVolumeImpl(f, v, t, r)
|
||||
#define aPoleFilter(pkt, f, g, t, s) aPoleFilterImpl(f, g, t, s)
|
||||
#define aPlayMP3(pkt, a, b, c, r) aPlayMP3Impl((void *)(a), b, (void *)(c), r)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef _IN_MOD_H
|
||||
#define _IN_MOD_H
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
#define MOD_CONFIG_FNAME "modconfig.txt"
|
||||
|
||||
struct animtableentry;
|
||||
|
||||
s32 modConfigLoad(const char *path);
|
||||
|
||||
s32 modTextureLoad(u16 num, void *dst, u32 dstSize);
|
||||
|
||||
s32 modAnimationLoadDescriptor(u16 num, struct animtableentry *anim);
|
||||
void *modAnimationLoadData(u16 num);
|
||||
|
||||
void *modSequenceLoad(u16 num, u32 *outSize);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef _IN_MPSETUPS_H
|
||||
#define _IN_MPSETUPS_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
s32 mpsetupLoadCurrentFile(void);
|
||||
s32 mpsetupSaveCurrentFile(void);
|
||||
void mpsetupLoadSetup(s32 slotindex);
|
||||
s32 mpsetupSaveSetup(s32 slotindex, u8 savefile);
|
||||
void mpsetupCopyAllFromPak(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef _IN_PREPROCESS_H
|
||||
#define _IN_PREPROCESS_H
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
typedef u8 *(*preprocessfunc)(u8 *data, u32 size, u32 *outSize);
|
||||
|
||||
u8* preprocessAnimations(u8 *data, u32 size, u32 *outSize);
|
||||
u8* preprocessMpConfigs(u8 *data, u32 size, u32 *outSize);
|
||||
u8* preprocessFont(u8 *data, u32 size, u32 *outSize);
|
||||
u8* preprocessJpnFont(u8 *data, u32 size, u32 *outSize);
|
||||
u8* preprocessALBankFile(u8 *data, u32 size, u32 *outSize);
|
||||
u8 *preprocessALCMidiHdr(u8 *data, u32 size, u32 *outSize);
|
||||
u8* preprocessSequences(u8 *data, u32 size, u32 *outSize);
|
||||
u8* preprocessTexturesList(u8 *data, u32 size, u32 *outSize);
|
||||
|
||||
void preprocessBgSection1(u8* data, u32 size, u32 ofs);
|
||||
void preprocessBgSection2(u8 *data, u32 size);
|
||||
void preprocessBgSection3(u8* data, u32 size);
|
||||
void preprocessBgSection1Header(u8 *data, u32 size);
|
||||
void preprocessBgSection2Header(u8 *data, u32 size);
|
||||
void preprocessBgSection3Header(u8 *data, u32 size);
|
||||
u32 preprocessBgRoom(u8* data, u32 size, u32 room_ofs);
|
||||
|
||||
u8 *preprocessLangFile(u8 *data, u32 size, u32 *outSize);
|
||||
u8 *preprocessPadsFile(u8 *data, u32 size, u32 *outSize);
|
||||
u8 *preprocessTilesFile(u8 *data, u32 size, u32 *outSize);
|
||||
u8 *preprocessSetupFile(u8 *data, u32 size, u32 *outSize);
|
||||
u8 *preprocessModelFile(u8 *data, u32 size, u32 *outSize);
|
||||
u8 *preprocessGunFile(u8 *data, u32 size, u32 *outSize);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef _IN_PREPROCESS_COMMON_H
|
||||
#define _IN_PREPROCESS_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
#include "types.h"
|
||||
#include "constants.h"
|
||||
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
#include "romdata.h"
|
||||
#include "preprocess.h"
|
||||
|
||||
#define MAX_PTR_MARKERS (1024 * 8)
|
||||
|
||||
#define PD_ALIGN(val, size) (((val) + ((size) - 1)) & ~((size) - 1))
|
||||
|
||||
#define PD_PTR_BASE(x, b) (void *)((u8 *)b + (uintptr_t)x)
|
||||
#define PD_PTR_BASEOFS(x, b, d) (void *)((u8 *)b - d + (uintptr_t)x)
|
||||
|
||||
static inline f32 swapF32(f32 x) { *(u32*)&x = PD_BE32(*(u32*)&x); return x; }
|
||||
static inline u32 swapU32(u32 x) { return PD_BE32(x); }
|
||||
static inline s32 swapS32(s32 x) { return PD_BE32(x); }
|
||||
static inline u16 swapU16(u16 x) { return PD_BE16(x); }
|
||||
static inline s16 swapS16(s16 x) { return PD_BE16(x); }
|
||||
static inline void* swapPtr(void** x) { return (void*)PD_BEPTR((uintptr_t)x); }
|
||||
static inline struct coord swapCrd(struct coord crd) { crd.x = swapF32(crd.x); crd.y = swapF32(crd.y); crd.z = swapF32(crd.z); return crd; }
|
||||
static inline u32 swapUnk(u32 x) { assert(0 && "unknown type"); return x; }
|
||||
|
||||
#define PD_SWAPPED_VAL(x) _Generic((x), \
|
||||
f32: swapF32, \
|
||||
u32: swapU32, \
|
||||
s32: swapS32, \
|
||||
u16: swapU16, \
|
||||
s16: swapS16, \
|
||||
struct coord: swapCrd, \
|
||||
default: swapUnk \
|
||||
)(x)
|
||||
|
||||
#define PD_SWAP_VAL(x) x = PD_SWAPPED_VAL(x)
|
||||
|
||||
#define PD_SWAP_PTR(x) x = swapPtr((void *)(x))
|
||||
|
||||
// ptr marker functions
|
||||
|
||||
struct ptrmarker {
|
||||
u32 ptr_src;
|
||||
uintptr_t ptr_host;
|
||||
};
|
||||
|
||||
void ptrAdd(u32 ptr_src, uintptr_t ptr_host);
|
||||
struct ptrmarker* ptrFind(uintptr_t ptr_src);
|
||||
void ptrReset(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef _IN_PREPROCESS_GBI_H
|
||||
#define _IN_PREPROCESS_GBI_H
|
||||
|
||||
#include "preprocess/common.h"
|
||||
|
||||
// gbi related functions
|
||||
void gbiReset(void);
|
||||
void gbiSetSegment(int segment, u32 offset);
|
||||
void gbiSetVtx(u32 src_offset, u32 dst_offset);
|
||||
u32 gbiConvertGdl(u8 *dst, u32 dstpos, u8 *src, u32 srcpos, int segment_cmds);
|
||||
void gbiConvertVtx(u8* dst, u32 dstpos, int count);
|
||||
void gbiGdlRewriteAddrs(u8* dst, u32 offset);
|
||||
void gbiAddTexAddr(u32 src_offset, u32 dst_offset);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,640 @@
|
||||
#ifndef _IN_SETUP_DECL
|
||||
#define _IN_SETUP_DECL
|
||||
|
||||
#include "preprocess/common.h"
|
||||
|
||||
struct n64_stagesetup {
|
||||
u32 ptr_waypoints;
|
||||
u32 ptr_waygroups;
|
||||
u32 ptr_cover;
|
||||
u32 ptr_intro;
|
||||
u32 ptr_props;
|
||||
u32 ptr_paths;
|
||||
u32 ptr_ailists;
|
||||
u32 ptr_padfiledata;
|
||||
};
|
||||
|
||||
struct n64_coord {
|
||||
f32 x;
|
||||
f32 y;
|
||||
f32 z;
|
||||
};
|
||||
|
||||
struct n64_defaultobj {
|
||||
u16 extrascale;
|
||||
u8 hidden2;
|
||||
u8 type;
|
||||
s16 modelnum;
|
||||
s16 pad;
|
||||
u32 flags;
|
||||
u32 flags2;
|
||||
u32 flags3;
|
||||
u32 ptr_prop;
|
||||
u32 ptr_model;
|
||||
f32 realrot[3][3];
|
||||
u32 hidden;
|
||||
union {
|
||||
u32 ptr_geotilef;
|
||||
u32 ptr_geoblock;
|
||||
u32 ptr_geocyl;
|
||||
u32 ptr_unkgeo;
|
||||
};
|
||||
union {
|
||||
u32 ptr_projectile;
|
||||
u32 ptr_embedment;
|
||||
};
|
||||
s16 damage;
|
||||
s16 maxdamage;
|
||||
u8 shadecol[4];
|
||||
u8 nextcol[4];
|
||||
u16 floorcol;
|
||||
s8 geocount;
|
||||
s8 _pad_;
|
||||
};
|
||||
|
||||
struct n64_doorobj {
|
||||
struct n64_defaultobj base;
|
||||
f32 maxfrac;
|
||||
f32 perimfrac;
|
||||
f32 accel;
|
||||
f32 decel;
|
||||
f32 maxspeed;
|
||||
u16 doorflags;
|
||||
u16 doortype;
|
||||
u32 keyflags;
|
||||
s32 autoclosetime;
|
||||
f32 frac;
|
||||
f32 fracspeed;
|
||||
s8 mode;
|
||||
s8 glasshits;
|
||||
s16 fadealpha;
|
||||
s16 xludist;
|
||||
s16 opadist;
|
||||
struct n64_coord startpos;
|
||||
f32 mtx98[3][3];
|
||||
u32 ptr_sibling;
|
||||
s32 lastopen60;
|
||||
s16 portalnum;
|
||||
s8 soundtype;
|
||||
s8 fadetime60;
|
||||
s32 lastcalc60;
|
||||
u8 laserfade;
|
||||
u8 unusedmaybe[3];
|
||||
u8 shadeinfo1[4];
|
||||
u8 shadeinfo2[4];
|
||||
u8 actual1;
|
||||
u8 actual2;
|
||||
u8 extra1;
|
||||
u8 extra2;
|
||||
};
|
||||
|
||||
struct n64_doorscaleobj {
|
||||
u32 unk00;
|
||||
s32 scale;
|
||||
};
|
||||
|
||||
struct n64_keyobj {
|
||||
struct n64_defaultobj base;
|
||||
u32 keyflags;
|
||||
};
|
||||
|
||||
struct n64_alarmobj {
|
||||
struct n64_defaultobj base;
|
||||
};
|
||||
|
||||
struct n64_cctvobj {
|
||||
struct n64_defaultobj base;
|
||||
|
||||
s16 lookatpadnum;
|
||||
s16 toleft;
|
||||
//Mtxf camrotm;
|
||||
f32 m[4][4];
|
||||
f32 yzero;
|
||||
f32 yrot;
|
||||
f32 yleft;
|
||||
f32 yright;
|
||||
f32 yspeed;
|
||||
f32 ymaxspeed;
|
||||
s32 seebondtime60;
|
||||
f32 maxdist;
|
||||
f32 xzero;
|
||||
};
|
||||
|
||||
struct n64_ammocrateobj {
|
||||
struct n64_defaultobj base;
|
||||
s32 ammotype;
|
||||
};
|
||||
|
||||
struct n64_gset {
|
||||
u8 weaponnum;
|
||||
u8 unk0639;
|
||||
u8 unk063a;
|
||||
u8 weaponfunc;
|
||||
};
|
||||
|
||||
struct n64_weaponobj {
|
||||
struct n64_defaultobj base;
|
||||
|
||||
union {
|
||||
struct n64_gset gset;
|
||||
struct {
|
||||
u8 weaponnum;
|
||||
s8 unk5d;
|
||||
s8 unk5e;
|
||||
u8 gunfunc;
|
||||
};
|
||||
};
|
||||
|
||||
s8 fadeouttimer60;
|
||||
s8 dualweaponnum;
|
||||
|
||||
union {
|
||||
s16 timer240;
|
||||
s16 team;
|
||||
};
|
||||
|
||||
u32 ptr_dualweapon;
|
||||
};
|
||||
|
||||
struct n64_packedchr {
|
||||
s16 chrindex;
|
||||
s8 unk02;
|
||||
s8 typenum;
|
||||
u32 spawnflags;
|
||||
s16 chrnum;
|
||||
u16 padnum;
|
||||
u8 bodynum;
|
||||
s8 headnum;
|
||||
u16 ailistnum;
|
||||
u16 padpreset;
|
||||
u16 chrpreset;
|
||||
u16 hearscale;
|
||||
u16 viewdist;
|
||||
u32 flags;
|
||||
u32 flags2;
|
||||
u8 team;
|
||||
u8 squadron;
|
||||
s16 chair;
|
||||
u32 convtalk;
|
||||
u8 tude;
|
||||
u8 naturalanim;
|
||||
u8 yvisang;
|
||||
u8 teamscandist;
|
||||
};
|
||||
|
||||
struct n64_tvscreen {
|
||||
u32 ptr_cmdlist;
|
||||
u16 offset;
|
||||
s16 pause60;
|
||||
u32 ptr_tconfig;
|
||||
f32 rot;
|
||||
f32 xscale;
|
||||
f32 xscalefrac;
|
||||
f32 xscaleinc;
|
||||
f32 xscaleold;
|
||||
f32 xscalenew;
|
||||
f32 yscale;
|
||||
f32 yscalefrac;
|
||||
f32 yscaleinc;
|
||||
f32 yscaleold;
|
||||
f32 yscalenew;
|
||||
f32 xmid;
|
||||
f32 xmidfrac;
|
||||
f32 xmidinc;
|
||||
f32 xmidold;
|
||||
f32 xmidnew;
|
||||
f32 ymid;
|
||||
f32 ymidfrac;
|
||||
f32 ymidinc;
|
||||
f32 ymidold;
|
||||
f32 ymidnew;
|
||||
u8 red;
|
||||
u8 redold;
|
||||
u8 rednew;
|
||||
u8 green;
|
||||
u8 greenold;
|
||||
u8 greennew;
|
||||
u8 blue;
|
||||
u8 blueold;
|
||||
u8 bluenew;
|
||||
u8 alpha;
|
||||
u8 alphaold;
|
||||
u8 alphanew;
|
||||
f32 colfrac;
|
||||
f32 colinc;
|
||||
};
|
||||
|
||||
struct n64_singlemonitorobj {
|
||||
struct n64_defaultobj base;
|
||||
struct n64_tvscreen screen;
|
||||
s16 owneroffset;
|
||||
s8 ownerpart;
|
||||
u8 imagenum;
|
||||
};
|
||||
|
||||
struct n64_multimonitorobj {
|
||||
struct n64_defaultobj base;
|
||||
struct n64_tvscreen screens[4];
|
||||
u8 imagenums[4];
|
||||
};
|
||||
|
||||
struct n64_hangingmonitorsobj {
|
||||
struct n64_defaultobj base;
|
||||
};
|
||||
|
||||
struct n64_autogunobj {
|
||||
struct n64_defaultobj base;
|
||||
s16 targetpad;
|
||||
s8 firing;
|
||||
u8 firecount;
|
||||
f32 yzero;
|
||||
f32 ymaxleft;
|
||||
f32 ymaxright;
|
||||
f32 yrot;
|
||||
f32 yspeed;
|
||||
f32 xzero;
|
||||
f32 xrot;
|
||||
f32 xspeed;
|
||||
f32 maxspeed;
|
||||
f32 aimdist;
|
||||
f32 barrelspeed;
|
||||
f32 barrelrot;
|
||||
s32 lastseebond60;
|
||||
s32 lastaimbond60;
|
||||
s32 allowsoundframe;
|
||||
u32 ptr_beam;
|
||||
f32 shotbondsum;
|
||||
u32 ptr_target;
|
||||
u8 targetteam;
|
||||
u8 ammoquantity;
|
||||
s16 nextchrtest;
|
||||
};
|
||||
|
||||
struct n64_linkgunsobj {
|
||||
u32 unk00;
|
||||
s16 offset1;
|
||||
s16 offset2;
|
||||
};
|
||||
|
||||
struct n64_debrisobj {
|
||||
struct n64_defaultobj base;
|
||||
};
|
||||
|
||||
struct n64_hatobj {
|
||||
struct n64_defaultobj base;
|
||||
};
|
||||
|
||||
struct n64_grenadeprobobj { // objtype 0x12
|
||||
u32 unk00;
|
||||
s16 chrnum;
|
||||
u16 probability;
|
||||
};
|
||||
|
||||
struct n64_linkliftdoorobj {
|
||||
u32 unk00;
|
||||
u32 ptr_door;
|
||||
u32 ptr_lift;
|
||||
u32 ptr_next;
|
||||
s32 stopnum;
|
||||
};
|
||||
|
||||
struct n64_multiammocrateslot {
|
||||
u16 modelnum;
|
||||
u16 quantity;
|
||||
};
|
||||
|
||||
struct n64_multiammocrateobj {
|
||||
struct n64_defaultobj base;
|
||||
struct n64_multiammocrateslot slots[19];
|
||||
};
|
||||
|
||||
struct n64_shieldobj {
|
||||
struct n64_defaultobj base;
|
||||
f32 initialamount;
|
||||
f32 amount;
|
||||
u32 unk64;
|
||||
};
|
||||
|
||||
struct n64_tag {
|
||||
u32 identifier;
|
||||
u16 tagnum;
|
||||
s16 cmdoffset;
|
||||
u32 ptr_next;
|
||||
u32 ptr_obj;
|
||||
};
|
||||
|
||||
struct n64_objective {
|
||||
u32 unk00;
|
||||
s32 index;
|
||||
u32 text;
|
||||
u16 unk0c;
|
||||
u8 flags;
|
||||
s8 difficulties;
|
||||
};
|
||||
|
||||
struct n64_stdobjective {
|
||||
u32 cmd;
|
||||
};
|
||||
|
||||
struct n64_objectivecmd {
|
||||
u8 cmd0[4];
|
||||
u32 cmd1;
|
||||
};
|
||||
|
||||
struct n64_briefingobj {
|
||||
u32 unk00;
|
||||
u32 type;
|
||||
u32 text;
|
||||
u32 ptr_next;
|
||||
};
|
||||
|
||||
struct n64_padlockeddoorobj {
|
||||
u32 unk00;
|
||||
u32 ptr_door;
|
||||
u32 ptr_lock;
|
||||
u32 ptr_next;
|
||||
};
|
||||
|
||||
struct n64_truckobj {
|
||||
struct n64_defaultobj base;
|
||||
u32 ptr_ailist;
|
||||
u16 aioffset;
|
||||
s16 aireturnlist;
|
||||
f32 speed;
|
||||
f32 wheelxrot;
|
||||
f32 wheelyrot;
|
||||
f32 speedaim;
|
||||
f32 speedtime60;
|
||||
f32 turnrot60;
|
||||
f32 roty;
|
||||
u32 ptr_path;
|
||||
s32 nextstep;
|
||||
};
|
||||
|
||||
struct n64_tankobj {
|
||||
u8 _pad_[128];
|
||||
};
|
||||
|
||||
struct n64_heliobj {
|
||||
struct n64_defaultobj base;
|
||||
u32 ptr_ailist;
|
||||
u16 aioffset;
|
||||
s16 aireturnlist;
|
||||
f32 rotoryrot;
|
||||
f32 rotoryspeed;
|
||||
f32 rotoryspeedaim;
|
||||
f32 rotoryspeedtime;
|
||||
f32 speed;
|
||||
f32 speedaim;
|
||||
f32 speedtime60;
|
||||
f32 yrot;
|
||||
u32 ptr_path;
|
||||
s32 nextstep;
|
||||
};
|
||||
|
||||
struct n64_glassobj {
|
||||
struct n64_defaultobj base;
|
||||
s16 portalnum;
|
||||
s16 _pad_;
|
||||
};
|
||||
|
||||
struct n64_safeobj {
|
||||
struct n64_defaultobj base;
|
||||
};
|
||||
|
||||
struct n64_safeitemobj {
|
||||
u32 unk00;
|
||||
u32 ptr_item;
|
||||
u32 ptr_safe;
|
||||
u32 ptr_door;
|
||||
u32 ptr_next;
|
||||
};
|
||||
|
||||
struct n64_cameraposobj {
|
||||
s32 type;
|
||||
f32 x;
|
||||
f32 y;
|
||||
f32 z;
|
||||
f32 theta;
|
||||
f32 verta;
|
||||
s32 pad;
|
||||
};
|
||||
|
||||
struct n64_tintedglassobj {
|
||||
struct n64_defaultobj base;
|
||||
s16 xludist;
|
||||
s16 opadist;
|
||||
s16 opacity;
|
||||
s16 portalnum;
|
||||
f32 unk64;
|
||||
};
|
||||
|
||||
struct n64_liftobj {
|
||||
struct n64_defaultobj base;
|
||||
s16 pads[4];
|
||||
u32 ptr_doors[4];
|
||||
f32 dist;
|
||||
f32 speed;
|
||||
f32 accel;
|
||||
f32 maxspeed;
|
||||
s8 soundtype;
|
||||
s8 levelcur;
|
||||
s8 levelaim;
|
||||
s8 _pad_;
|
||||
struct n64_coord prevpos;
|
||||
};
|
||||
|
||||
struct n64_linksceneryobj {
|
||||
u32 unk00;
|
||||
u32 ptr_trigger;
|
||||
u32 ptr_unexp;
|
||||
u32 ptr_exp;
|
||||
u32 ptr_next;
|
||||
};
|
||||
|
||||
struct n64_blockedpathobj {
|
||||
u32 unk00;
|
||||
u32 ptr_blocker;
|
||||
s16 waypoint1;
|
||||
s16 waypoint2;
|
||||
u32 ptr_next;
|
||||
};
|
||||
|
||||
struct n64_hov {
|
||||
u8 type;
|
||||
u8 flags;
|
||||
s16 _pad_;
|
||||
f32 bobycur;
|
||||
f32 bobytarget;
|
||||
f32 bobyspeed;
|
||||
f32 yrot;
|
||||
f32 bobpitchcur;
|
||||
f32 bobpitchtarget;
|
||||
f32 bobpitchspeed;
|
||||
f32 bobrollcur;
|
||||
f32 bobrolltarget;
|
||||
f32 bobrollspeed;
|
||||
f32 groundpitch;
|
||||
f32 y;
|
||||
f32 ground;
|
||||
s32 prevframe60;
|
||||
s32 prevgroundframe60;
|
||||
};
|
||||
|
||||
struct n64_hoverbikeobj {
|
||||
struct n64_defaultobj base;
|
||||
struct n64_hov hov;
|
||||
f32 speed[2];
|
||||
f32 prevpos[2];
|
||||
f32 w;
|
||||
f32 rels[2];
|
||||
f32 exreal;
|
||||
f32 ezreal;
|
||||
f32 ezreal2;
|
||||
f32 leanspeed;
|
||||
f32 leandiff;
|
||||
s32 maxspeedtime240;
|
||||
f32 speedabs[2];
|
||||
f32 speedrel[2];
|
||||
};
|
||||
|
||||
struct n64_hoverpropobj {
|
||||
struct n64_defaultobj base;
|
||||
struct n64_hov hov;
|
||||
};
|
||||
|
||||
struct n64_fanobj {
|
||||
struct n64_defaultobj base;
|
||||
f32 yrot;
|
||||
f32 yrotprev;
|
||||
f32 ymaxspeed;
|
||||
f32 yspeed;
|
||||
f32 yaccel;
|
||||
s8 on;
|
||||
u8 _pad_[3];
|
||||
};
|
||||
|
||||
struct n64_hovercarobj {
|
||||
struct n64_defaultobj base;
|
||||
u32 ptr_ailist;
|
||||
u16 aioffset;
|
||||
s16 aireturnlist;
|
||||
f32 speed;
|
||||
f32 speedaim;
|
||||
f32 speedtime60;
|
||||
f32 turnyspeed60;
|
||||
f32 turnxspeed60;
|
||||
f32 turnrot60;
|
||||
f32 roty;
|
||||
f32 rotx;
|
||||
f32 rotz;
|
||||
u32 ptr_path;
|
||||
s32 nextstep;
|
||||
s16 status;
|
||||
s16 dead;
|
||||
s16 deadtimer60;
|
||||
s16 sparkstimer60;
|
||||
};
|
||||
|
||||
struct n64_chopperobj {
|
||||
struct n64_defaultobj base;
|
||||
u32 ptr_ailist;
|
||||
u16 aioffset;
|
||||
s16 aireturnlist;
|
||||
union {
|
||||
struct {
|
||||
f32 speed;
|
||||
f32 speedaim;
|
||||
f32 speedtime60;
|
||||
};
|
||||
struct n64_coord fall;
|
||||
};
|
||||
f32 turnyspeed60;
|
||||
f32 turnxspeed60;
|
||||
f32 turnrot60;
|
||||
f32 roty;
|
||||
f32 rotx;
|
||||
f32 rotz;
|
||||
u32 ptr_path;
|
||||
s32 nextstep;//
|
||||
s16 weaponsarmed;
|
||||
s16 ontarget;
|
||||
s16 target;
|
||||
u8 attackmode;
|
||||
u8 cw;
|
||||
f32 vx;
|
||||
f32 vy;
|
||||
f32 vz;
|
||||
f32 power;
|
||||
f32 otx;
|
||||
f32 oty;
|
||||
f32 otz;
|
||||
f32 bob;
|
||||
f32 bobstrength;
|
||||
s32 targetvisible;
|
||||
s32 timer60;
|
||||
s32 patroltimer60;
|
||||
f32 gunturnyspeed60;
|
||||
f32 gunturnxspeed60;
|
||||
f32 gunroty;
|
||||
f32 gunrotx;
|
||||
f32 barrelrotspeed;
|
||||
f32 barrelrot;
|
||||
u32 ptr_fireslotthing;
|
||||
s32 dead;
|
||||
};
|
||||
|
||||
struct n64_escalatorobj {
|
||||
struct n64_defaultobj base;
|
||||
s32 frame;
|
||||
struct n64_coord prevpos;
|
||||
};
|
||||
|
||||
struct n64_textoverride {
|
||||
u32 unk00;
|
||||
s32 objoffset;
|
||||
s32 weapon;
|
||||
u32 obtaintext;
|
||||
u32 ownertext;
|
||||
u32 inventorytext;
|
||||
u32 inventory2text;
|
||||
u32 pickuptext;
|
||||
u32 ptr_next;
|
||||
u32 ptr_obj;
|
||||
};
|
||||
|
||||
struct n64_criteria_roomentered {
|
||||
u32 unk00;
|
||||
u32 pad;
|
||||
u32 status;
|
||||
u32 ptr_next;
|
||||
};
|
||||
|
||||
struct n64_criteria_throwinroom {
|
||||
u32 unk00;
|
||||
u32 unk04;
|
||||
u32 pad;
|
||||
u32 status;
|
||||
u32 ptr_next;
|
||||
};
|
||||
|
||||
struct n64_criteria_holograph {
|
||||
u32 unk00;
|
||||
u32 obj;
|
||||
u32 status;
|
||||
u32 ptr_next;
|
||||
};
|
||||
|
||||
struct n64_ailist {
|
||||
u32 ptr_list;
|
||||
s32 id;
|
||||
};
|
||||
|
||||
struct n64_path {
|
||||
u32 ptr_pads;
|
||||
u8 id;
|
||||
u8 flags;
|
||||
u16 len;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef _IN_ROMDATA_H
|
||||
#define _IN_ROMDATA_H
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
extern u8 *g_RomFile;
|
||||
extern u32 g_RomFileSize;
|
||||
|
||||
s32 romdataInit(void);
|
||||
|
||||
u8 *romdataFileLoad(s32 fileNum, u32 *outSize);
|
||||
void romdataFilePreprocess(s32 fileNum, s32 loadType, u8 *data, u32 size, u32 *outSize);
|
||||
void romdataFileFree(s32 fileNum);
|
||||
const char *romdataFileGetName(s32 fileNum);
|
||||
|
||||
u8 *romdataFileGetData(s32 fileNum);
|
||||
s32 romdataFileGetSize(s32 fileNum);
|
||||
|
||||
s32 romdataFileGetNumForName(const char *name);
|
||||
|
||||
u8 *romdataSegGetData(const char *segName);
|
||||
u8 *romdataSegGetDataEnd(const char *segName);
|
||||
u32 romdataSegGetSize(const char *segName);
|
||||
u32 romdataFileGetEstimatedSize(const u32 size, const u32 loadtype);
|
||||
|
||||
s32 romdataCheckGbcRom(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef _IN_SYSTEM_H
|
||||
#define _IN_SYSTEM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
enum LogLevel {
|
||||
LOG_NOTE,
|
||||
LOG_WARNING,
|
||||
LOG_ERROR,
|
||||
};
|
||||
|
||||
void sysInitArgs(s32 argc, const char **argv);
|
||||
void sysInit(void);
|
||||
|
||||
s32 sysArgCheck(const char *arg);
|
||||
const char *sysArgGetString(const char *arg);
|
||||
s32 sysArgGetInt(const char *arg, s32 defval);
|
||||
|
||||
u64 sysGetMicroseconds(void);
|
||||
|
||||
void sysFatalError(const char *fmt, ...) __attribute__((noreturn));
|
||||
|
||||
s32 sysLogIsOpen(void);
|
||||
void sysLogPrintf(s32 level, const char *fmt, ...);
|
||||
|
||||
void sysGetExecutablePath(char *outPath, const u32 outLen);
|
||||
void sysGetHomePath(char *outPath, const u32 outLen);
|
||||
|
||||
void *sysMemAlloc(const u32 size);
|
||||
void *sysMemZeroAlloc(const u32 size);
|
||||
void *sysMemRealloc(void *ptr, const u32 newSize);
|
||||
void sysMemFree(void *ptr);
|
||||
|
||||
// hns is specified in 100ns units
|
||||
void sysSleep(const s64 hns);
|
||||
|
||||
// yield CPU if supported (e.g. during a busy loop)
|
||||
void sysCpuRelax(void);
|
||||
|
||||
void crashInit(void);
|
||||
void crashShutdown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef _IN_UTILS_H
|
||||
#define _IN_UTILS_H
|
||||
|
||||
#define UTIL_MAX_TOKEN 1024
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
char *strRightTrim(char *str);
|
||||
char *strTrim(char *str);
|
||||
char *strUnquote(char *str);
|
||||
char *strParseToken(char *str, char *out, s32 *outCount);
|
||||
char *strFmt(const char *fmt, ...);
|
||||
char *strDuplicate(const char *str);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _IN_VERSIONINFO_H
|
||||
#define _IN_VERSIONINFO_H
|
||||
|
||||
#define VERSION_TARGET "@VERSION_TARGET@"
|
||||
#define VERSION_ARCH "@VERSION_ARCH@"
|
||||
#define VERSION_ROMID "@VERSION_ROMID@"
|
||||
#define VERSION_BUILD "@VERSION_BUILD@"
|
||||
#define VERSION_HASH "@VERSION_HASH@"
|
||||
#define VERSION_BRANCH "@VERSION_BRANCH@"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef _IN_VIDEO_H
|
||||
#define _IN_VIDEO_H
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
#include <PR/gbi.h>
|
||||
|
||||
// maximum framerate; if the game runs faster than this, things will break
|
||||
#if PAL
|
||||
#define VIDEO_MAX_FPS 200
|
||||
#else
|
||||
#define VIDEO_MAX_FPS 240
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
s32 width;
|
||||
s32 height;
|
||||
} displaymode;
|
||||
|
||||
s32 videoInit(void);
|
||||
void videoStartFrame(void);
|
||||
void videoSubmitCommands(Gfx *cmds);
|
||||
void videoClearScreen(void);
|
||||
void videoEndFrame(void);
|
||||
|
||||
void *videoGetWindowHandle(void);
|
||||
|
||||
void videoUpdateNativeResolution(s32 w, s32 h);
|
||||
s32 videoGetNativeWidth(void);
|
||||
s32 videoGetNativeHeight(void);
|
||||
|
||||
s32 videoGetWidth(void);
|
||||
s32 videoGetHeight(void);
|
||||
f32 videoGetAspect(void);
|
||||
s32 videoGetFullscreen(void);
|
||||
s32 videoGetFullscreenMode(void);
|
||||
s32 videoGetMaximizeWindow(void);
|
||||
void videoSetMaximizeWindow(s32 fs);
|
||||
s32 videoGetCenterWindow(void);
|
||||
void videoSetCenterWindow(s32 center);
|
||||
u32 videoGetTextureFilter(void);
|
||||
s32 videoGetTextureFilter2D(void);
|
||||
u32 videoGetAnisotropicFilter(void);
|
||||
u32 videoGetMaxAnisotropyLevel(void);
|
||||
s32 videoGetDetailTextures(void);
|
||||
s32 videoGetDisplayModeIndex(void);
|
||||
s32 videoGetDisplayMode(displaymode *out, const s32 index);
|
||||
s32 videoGetNumDisplayModes(void);
|
||||
s32 videoGetVsync(void);
|
||||
s32 videoGetFramerateLimit(void);
|
||||
s32 videoGetDisplayFPS(void);
|
||||
s32 videoGetMSAA(void);
|
||||
f32 videoGetGlareBrightness(void);
|
||||
f32 videoGetOverexposureScale(void);
|
||||
|
||||
f32 videoGetAverageFPS(void);
|
||||
|
||||
void videoSetWindowOffset(s32 x, s32 y);
|
||||
void videoSetFullscreen(s32 fs);
|
||||
void videoSetFullscreenMode(s32 mode);
|
||||
void videoSetTextureFilter(u32 filter);
|
||||
void videoSetTextureFilter2D(s32 filter);
|
||||
void videoSetAnisotropicFilter(u32 filter);
|
||||
void videoSetDetailTextures(s32 detail);
|
||||
void videoSetDisplayMode(const s32 index);
|
||||
void videoSetVsync(const s32 vsync);
|
||||
void videoSetFramerateLimit(const s32 limit);
|
||||
void videoSetDisplayFPS(const s32 displayfps);
|
||||
void videoSetMSAA(const s32 msaa);
|
||||
void videoSetGlareBrightness(f32 bright);
|
||||
void videoSetOverexposureScale(f32 scale);
|
||||
|
||||
s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize);
|
||||
void videoSetFramebuffer(s32 target);
|
||||
void videoResetFramebuffer(void);
|
||||
void videoCopyFramebuffer(s32 dst, s32 src, s32 left, s32 top);
|
||||
void videoResizeFramebuffer(s32 target, u32 w, u32 h, s32 upscale, s32 autoresize);
|
||||
s32 videoFramebuffersSupported(void);
|
||||
|
||||
void videoResetTextureCache(void);
|
||||
void videoFreeCachedTexture(const void *texptr);
|
||||
void videoFreeCachedTextures(const void *start, const void *end);
|
||||
|
||||
void videoShutdown(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
#include <PR/ultratypes.h>
|
||||
#include <stdio.h>
|
||||
#include <SDL.h>
|
||||
#include "platform.h"
|
||||
#include "config.h"
|
||||
#include "audio.h"
|
||||
#include "system.h"
|
||||
|
||||
static SDL_AudioDeviceID dev;
|
||||
static const s16 *nextBuf;
|
||||
static u32 nextSize = 0;
|
||||
|
||||
static s32 bufferSize = 512;
|
||||
static s32 queueLimit = 8192;
|
||||
|
||||
s32 audioInit(void)
|
||||
{
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
|
||||
sysLogPrintf(LOG_ERROR, "SDL audio init error: %s", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_AudioSpec want, have;
|
||||
SDL_zero(want);
|
||||
want.freq = 22020; // TODO: this might cause trouble for some platforms
|
||||
want.format = AUDIO_S16SYS;
|
||||
want.channels = 2;
|
||||
want.samples = bufferSize;
|
||||
want.callback = NULL;
|
||||
|
||||
nextBuf = NULL;
|
||||
|
||||
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
|
||||
if (dev == 0) {
|
||||
sysLogPrintf(LOG_ERROR, "SDL_OpenAudio error: %s", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 audioGetBytesBuffered(void)
|
||||
{
|
||||
return SDL_GetQueuedAudioSize(dev);
|
||||
}
|
||||
|
||||
s32 audioGetSamplesBuffered(void)
|
||||
{
|
||||
return audioGetBytesBuffered() / 4;
|
||||
}
|
||||
|
||||
void audioSetNextBuffer(const s16 *buf, u32 len)
|
||||
{
|
||||
nextBuf = buf;
|
||||
nextSize = len;
|
||||
}
|
||||
|
||||
void audioEndFrame(void)
|
||||
{
|
||||
if (nextBuf && nextSize) {
|
||||
if (audioGetSamplesBuffered() < queueLimit) {
|
||||
SDL_QueueAudio(dev, nextBuf, nextSize);
|
||||
}
|
||||
nextBuf = NULL;
|
||||
nextSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PD_CONSTRUCTOR static void audioConfigInit(void)
|
||||
{
|
||||
configRegisterInt("Audio.BufferSize", &bufferSize, 0, 1 * 1024 * 1024);
|
||||
configRegisterInt("Audio.QueueLimit", &queueLimit, 0, 1 * 1024 * 1024);
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <ctype.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "fs.h"
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define CONFIG_MAX_SECNAME 128
|
||||
#define CONFIG_MAX_KEYNAME 256
|
||||
#define CONFIG_MAX_SETTINGS 300
|
||||
|
||||
typedef enum {
|
||||
CFG_NONE,
|
||||
CFG_S32,
|
||||
CFG_F32,
|
||||
CFG_U32,
|
||||
CFG_STR
|
||||
} configtype;
|
||||
|
||||
struct configentry {
|
||||
char key[CONFIG_MAX_KEYNAME + 1];
|
||||
s32 seclen;
|
||||
configtype type;
|
||||
void *ptr;
|
||||
union {
|
||||
struct { f32 min_f32, max_f32; };
|
||||
struct { s32 min_s32, max_s32; };
|
||||
struct { u32 min_u32, max_u32; };
|
||||
u32 max_str;
|
||||
};
|
||||
} settings[CONFIG_MAX_SETTINGS];
|
||||
|
||||
static s32 numSettings = 0;
|
||||
static u8 configMaxWarningLogged = 0;
|
||||
|
||||
static inline s32 configClampInt(s32 val, s32 min, s32 max)
|
||||
{
|
||||
return (val < min) ? min : ((val > max) ? max : val);
|
||||
}
|
||||
|
||||
static inline u32 configClampUInt(u32 val, u32 min, u32 max)
|
||||
{
|
||||
return (val < min) ? min : ((val > max) ? max : val);
|
||||
}
|
||||
|
||||
static inline f32 configClampFloat(f32 val, f32 min, f32 max)
|
||||
{
|
||||
return (val < min) ? min : ((val > max) ? max : val);
|
||||
}
|
||||
|
||||
static inline struct configentry *configFindEntry(const char *key)
|
||||
{
|
||||
for (s32 i = 0; i < numSettings; ++i) {
|
||||
if (!strncasecmp(settings[i].key, key, CONFIG_MAX_KEYNAME)) {
|
||||
return &settings[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct configentry *configAddEntry(const char *key)
|
||||
{
|
||||
if (numSettings < CONFIG_MAX_SETTINGS) {
|
||||
struct configentry *cfg = &settings[numSettings++];
|
||||
snprintf(cfg->key, CONFIG_MAX_KEYNAME, "%s", key);
|
||||
const char *delim = strrchr(cfg->key, '.');
|
||||
cfg->seclen = delim ? (delim - cfg->key) : 0;
|
||||
return cfg;
|
||||
}
|
||||
if (!configMaxWarningLogged) {
|
||||
sysLogPrintf(LOG_WARNING, "Maximum number of configuration entries exceeded: %d", CONFIG_MAX_SETTINGS);
|
||||
configMaxWarningLogged = 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct configentry *configFindOrAddEntry(const char *key)
|
||||
{
|
||||
for (s32 i = 0; i < numSettings; ++i) {
|
||||
if (!strncasecmp(settings[i].key, key, CONFIG_MAX_KEYNAME)) {
|
||||
return &settings[i];
|
||||
}
|
||||
}
|
||||
return configAddEntry(key);
|
||||
}
|
||||
|
||||
static inline const char *configGetSection(char *sec, const struct configentry *cfg)
|
||||
{
|
||||
if (!cfg->seclen || cfg->seclen > CONFIG_MAX_SECNAME) {
|
||||
strncpy(sec, cfg->key, CONFIG_MAX_SECNAME);
|
||||
sec[CONFIG_MAX_SECNAME] = '\0';
|
||||
return sec;
|
||||
}
|
||||
|
||||
memcpy(sec, cfg->key, cfg->seclen);
|
||||
sec[cfg->seclen] = '\0';
|
||||
|
||||
return sec;
|
||||
}
|
||||
|
||||
void configRegisterInt(const char *key, s32 *var, s32 min, s32 max)
|
||||
{
|
||||
struct configentry *cfg = configFindOrAddEntry(key);
|
||||
if (cfg) {
|
||||
cfg->type = CFG_S32;
|
||||
cfg->ptr = var;
|
||||
cfg->min_s32 = min;
|
||||
cfg->max_s32 = max;
|
||||
}
|
||||
}
|
||||
|
||||
void configRegisterUInt(const char* key, u32* var, u32 min, u32 max)
|
||||
{
|
||||
struct configentry* cfg = configFindOrAddEntry(key);
|
||||
if (cfg) {
|
||||
cfg->type = CFG_U32;
|
||||
cfg->ptr = var;
|
||||
cfg->min_u32 = min;
|
||||
cfg->max_u32 = max;
|
||||
}
|
||||
}
|
||||
|
||||
void configRegisterFloat(const char *key, f32 *var, f32 min, f32 max)
|
||||
{
|
||||
struct configentry *cfg = configFindOrAddEntry(key);
|
||||
if (cfg) {
|
||||
cfg->type = CFG_F32;
|
||||
cfg->ptr = var;
|
||||
cfg->min_f32 = min;
|
||||
cfg->max_f32 = max;
|
||||
}
|
||||
}
|
||||
|
||||
void configRegisterString(const char *key, char *var, u32 maxstr)
|
||||
{
|
||||
struct configentry *cfg = configFindOrAddEntry(key);
|
||||
if (cfg) {
|
||||
cfg->type = CFG_STR;
|
||||
cfg->ptr = var;
|
||||
cfg->max_str = maxstr;
|
||||
}
|
||||
}
|
||||
|
||||
static void configSetFromString(const char *key, const char *val)
|
||||
{
|
||||
struct configentry *cfg = configFindEntry(key);
|
||||
if (!cfg) return;
|
||||
|
||||
s32 tmp_s32;
|
||||
f32 tmp_f32;
|
||||
u32 tmp_u32;
|
||||
switch (cfg->type) {
|
||||
case CFG_S32:
|
||||
tmp_s32 = strtol(val, NULL, 0);
|
||||
if (cfg->min_s32 < cfg->max_s32) {
|
||||
tmp_s32 = configClampInt(tmp_s32, cfg->min_s32, cfg->max_s32);
|
||||
}
|
||||
*(s32 *)cfg->ptr = tmp_s32;
|
||||
break;
|
||||
case CFG_F32:
|
||||
tmp_f32 = strtof(val, NULL);
|
||||
if (cfg->min_f32 < cfg->max_f32) {
|
||||
tmp_f32 = configClampFloat(tmp_f32, cfg->min_f32, cfg->max_f32);
|
||||
}
|
||||
*(f32 *)cfg->ptr = tmp_f32;
|
||||
break;
|
||||
case CFG_U32:
|
||||
tmp_u32 = strtoul(val, NULL, 0);
|
||||
if (cfg->min_u32 < cfg->max_u32) {
|
||||
tmp_u32 = configClampUInt(tmp_u32, cfg->min_u32, cfg->max_u32);
|
||||
}
|
||||
*(u32*)cfg->ptr = tmp_u32;
|
||||
break;
|
||||
case CFG_STR:
|
||||
strncpy(cfg->ptr, val, cfg->max_str ? cfg->max_str - 1 : 4096);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void configSaveEntry(struct configentry *cfg, FILE *f)
|
||||
{
|
||||
switch (cfg->type) {
|
||||
case CFG_S32:
|
||||
if (cfg->min_s32 < cfg->max_s32) {
|
||||
*(s32 *)cfg->ptr = configClampInt(*(s32 *)cfg->ptr, cfg->min_s32, cfg->max_s32);
|
||||
}
|
||||
fprintf(f, "%s=%d\n", cfg->key + cfg->seclen + 1, *(s32 *)cfg->ptr);
|
||||
break;
|
||||
case CFG_F32:
|
||||
if (cfg->min_f32 < cfg->max_f32) {
|
||||
*(f32 *)cfg->ptr = configClampFloat(*(f32 *)cfg->ptr, cfg->min_f32, cfg->max_f32);
|
||||
}
|
||||
fprintf(f, "%s=%f\n", cfg->key + cfg->seclen + 1, *(f32 *)cfg->ptr);
|
||||
break;
|
||||
case CFG_U32:
|
||||
if (cfg->min_u32 < cfg->max_u32) {
|
||||
*(u32*)cfg->ptr = configClampUInt(*(u32*)cfg->ptr, cfg->min_u32, cfg->max_u32);
|
||||
}
|
||||
fprintf(f, "%s=%u\n", cfg->key + cfg->seclen + 1, *(u32 *)cfg->ptr);
|
||||
break;
|
||||
case CFG_STR:
|
||||
fprintf(f, "%s=%s\n", cfg->key + cfg->seclen + 1, (char *)cfg->ptr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s32 configSave(const char *fname)
|
||||
{
|
||||
FILE *f = fsFileOpenWrite(fname);
|
||||
if (!f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char tmpSec[CONFIG_MAX_SECNAME + 1] = { 0 };
|
||||
char curSec[CONFIG_MAX_SECNAME + 1] = { 0 };
|
||||
configGetSection(curSec, &settings[0]);
|
||||
fprintf(f, "[%s]\n", curSec);
|
||||
|
||||
for (s32 i = 0; i < numSettings; ++i) {
|
||||
struct configentry *cfg = &settings[i];
|
||||
configGetSection(tmpSec, cfg);
|
||||
if (strncmp(curSec, tmpSec, CONFIG_MAX_SECNAME) != 0) {
|
||||
fprintf(f, "\n[%s]\n", tmpSec);
|
||||
strncpy(curSec, tmpSec, CONFIG_MAX_SECNAME);
|
||||
}
|
||||
configSaveEntry(cfg, f);
|
||||
}
|
||||
|
||||
fsFileFree(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 configLoad(const char *fname)
|
||||
{
|
||||
FILE *f = fsFileOpenRead(fname);
|
||||
if (!f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char curSec[CONFIG_MAX_SECNAME + 1] = { 0 };
|
||||
char keyBuf[CONFIG_MAX_SECNAME * 2 + 2] = { 0 }; // SECTION + . + KEY + \0
|
||||
char token[UTIL_MAX_TOKEN + 1] = { 0 };
|
||||
char lineBuf[2048] = { 0 };
|
||||
char *line = lineBuf;
|
||||
s32 lineLen = 0;
|
||||
|
||||
while (fgets(lineBuf, sizeof(lineBuf), f)) {
|
||||
line = lineBuf;
|
||||
|
||||
line = strParseToken(line, token, NULL);
|
||||
|
||||
if (token[0] == '[' && token[1] == '\0') {
|
||||
// section; get name
|
||||
line = strParseToken(line, token, NULL);
|
||||
if (!token[0]) {
|
||||
sysLogPrintf(LOG_ERROR, "configLoad: malformed section line: %s", lineBuf);
|
||||
continue;
|
||||
}
|
||||
strncpy(curSec, token, CONFIG_MAX_SECNAME);
|
||||
// eat ]
|
||||
line = strParseToken(line, token, NULL);
|
||||
if (token[0] != ']' || token[1] != '\0') {
|
||||
sysLogPrintf(LOG_ERROR, "configLoad: malformed section line: %s", lineBuf);
|
||||
}
|
||||
} else if (token[0]) {
|
||||
// probably a key=value pair; append key name to section name
|
||||
snprintf(keyBuf, sizeof(keyBuf) - 1, "%s.%s", curSec, token);
|
||||
// eat =
|
||||
line = strParseToken(line, token, NULL);
|
||||
if (token[0] != '=' || token[1] != '\0') {
|
||||
sysLogPrintf(LOG_ERROR, "configLoad: malformed keyvalue line: %s", lineBuf);
|
||||
continue;
|
||||
}
|
||||
// the rest of the line is the value
|
||||
line = strTrim(line);
|
||||
if (line[0] == '"') {
|
||||
line = strUnquote(line);
|
||||
}
|
||||
configSetFromString(keyBuf, line);
|
||||
}
|
||||
}
|
||||
|
||||
fsFileFree(f);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void configInit(void)
|
||||
{
|
||||
if (fsFileSize(CONFIG_PATH) > 0) {
|
||||
configLoad(CONFIG_PATH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "system.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define CRASH_LOG_FNAME "pd.crash.log"
|
||||
#define CRASH_MAX_MSG 8192
|
||||
#define CRASH_MAX_SYM 256
|
||||
#define CRASH_MAX_FRAMES 32
|
||||
#define CRASH_MSG(...) \
|
||||
if (msglen < CRASH_MAX_MSG) msglen += snprintf(msg + msglen, CRASH_MAX_MSG - msglen, __VA_ARGS__)
|
||||
|
||||
#if defined(PLATFORM_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <inttypes.h>
|
||||
#include <excpt.h>
|
||||
|
||||
// NOTE: game builds with gcc, which means we have no PDBs for the windows version
|
||||
// this means that you generally won't get any symbol names in the main executable
|
||||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER prevExFilter;
|
||||
|
||||
static void *crashGetModuleBase(const void *addr)
|
||||
{
|
||||
HMODULE h = NULL;
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &h);
|
||||
return (void *)h;
|
||||
}
|
||||
|
||||
static void crashStackTrace(char *msg, PEXCEPTION_POINTERS exinfo)
|
||||
{
|
||||
CONTEXT context = *exinfo->ContextRecord;
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
|
||||
SymSetOptions(SymGetOptions() | SYMOPT_DEBUG | SYMOPT_LOAD_LINES);
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
|
||||
STACKFRAME64 stackframe;
|
||||
memset(&stackframe, 0, sizeof(stackframe));
|
||||
|
||||
DWORD image;
|
||||
#ifdef PLATFORM_X86
|
||||
image = IMAGE_FILE_MACHINE_I386;
|
||||
stackframe.AddrPC.Offset = context.Eip;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Ebp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Esp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif defined(PLATFORM_X86_64)
|
||||
image = IMAGE_FILE_MACHINE_AMD64;
|
||||
stackframe.AddrPC.Offset = context.Rip;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Rsp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Rsp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
snprintf(msg, CRASH_MAX_MSG, "no stack trace available on this arch\n");
|
||||
return;
|
||||
#endif
|
||||
|
||||
DWORD disp = 0;
|
||||
DWORD64 disp64 = 0;
|
||||
IMAGEHLP_LINE64 line;
|
||||
DWORD msglen = 0;
|
||||
|
||||
CRASH_MSG("EXCEPTION: 0x%08lx\n", exinfo->ExceptionRecord->ExceptionCode);
|
||||
CRASH_MSG("PC: %p", exinfo->ExceptionRecord->ExceptionAddress);
|
||||
if (SymGetLineFromAddr64(process, (uintptr_t)exinfo->ExceptionRecord->ExceptionAddress, &disp, &line)) {
|
||||
CRASH_MSG(": %s:%lu+%lu", line.FileName, line.LineNumber, disp);
|
||||
}
|
||||
CRASH_MSG("\nMODULE: [%p]\n", crashGetModuleBase(exinfo->ExceptionRecord->ExceptionAddress));
|
||||
CRASH_MSG("MAIN MODULE: [%p]\n", crashGetModuleBase(crashInit));
|
||||
CRASH_MSG("\nBACKTRACE:\n");
|
||||
|
||||
char symbuf[sizeof(SYMBOL_INFO) + CRASH_MAX_SYM * sizeof(TCHAR)];
|
||||
PSYMBOL_INFO sym = (PSYMBOL_INFO)symbuf;
|
||||
sym->SizeOfStruct = sizeof(*sym);
|
||||
sym->MaxNameLen = CRASH_MAX_SYM;
|
||||
|
||||
s32 i;
|
||||
for (i = 0; i < CRASH_MAX_FRAMES; ++i) {
|
||||
const BOOL res = StackWalk64(image, process, thread, &stackframe, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL);
|
||||
if (!res) {
|
||||
break;
|
||||
}
|
||||
|
||||
CRASH_MSG("#%02d: %p", i, (void *)(uintptr_t)stackframe.AddrPC.Offset);
|
||||
|
||||
if (SymFromAddr(process, stackframe.AddrPC.Offset, &disp64, sym)) {
|
||||
CRASH_MSG(": %s+%llu", sym->Name, disp64);
|
||||
} else if (process, stackframe.AddrPC.Offset) {
|
||||
const uintptr_t modbase = (uintptr_t)crashGetModuleBase((void *)(uintptr_t)stackframe.AddrPC.Offset);
|
||||
const uintptr_t modofs = (uintptr_t)stackframe.AddrPC.Offset - modbase;
|
||||
CRASH_MSG(": [%p]+%p", (void *)modbase, (void *)modofs);
|
||||
}
|
||||
|
||||
if(SymGetLineFromAddr64(process, stackframe.AddrPC.Offset, &disp, &line)) {
|
||||
CRASH_MSG(" (%s:%lu+%lu)", line.FileName, line.LineNumber, disp);
|
||||
}
|
||||
|
||||
CRASH_MSG("\n");
|
||||
}
|
||||
|
||||
if (i <= 1) {
|
||||
CRASH_MSG("no information\n");
|
||||
} else if (i == CRASH_MAX_FRAMES) {
|
||||
CRASH_MSG("...\n");
|
||||
}
|
||||
|
||||
SymCleanup(process);
|
||||
}
|
||||
|
||||
static long __stdcall crashHandler(PEXCEPTION_POINTERS exinfo)
|
||||
{
|
||||
char msg[CRASH_MAX_MSG + 1] = { 0 };
|
||||
|
||||
if (IsDebuggerPresent()) {
|
||||
if (prevExFilter) {
|
||||
return prevExFilter(exinfo);
|
||||
}
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
sysLogPrintf(LOG_ERROR, "FATAL: Crashed: PC=%p CODE=0x%08lx", exinfo->ExceptionRecord->ExceptionAddress, exinfo->ExceptionRecord->ExceptionCode);
|
||||
|
||||
fflush(stderr);
|
||||
fflush(stdout);
|
||||
|
||||
crashStackTrace(msg, exinfo);
|
||||
|
||||
// open log file for the crash dump if one hasn't been opened yet
|
||||
if (!sysLogIsOpen()) {
|
||||
FILE *f = fopen(CRASH_LOG_FNAME, "wb");
|
||||
if (f) {
|
||||
fprintf(f, "Crash!\n\n%s", msg);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
sysFatalError("Crash!\n\n%s", msg);
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
|
||||
#include <ucontext.h>
|
||||
#include <signal.h>
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/fcntl.h>
|
||||
|
||||
static struct sigaction prevSigAction;
|
||||
|
||||
static s32 crashIsDebuggerPresent(void)
|
||||
{
|
||||
static s32 result = -1;
|
||||
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
char buf[4096] = { 0 };
|
||||
|
||||
int fd = open("/proc/self/status", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
result = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rx = read(fd, buf, sizeof(buf) - 1);
|
||||
close(fd);
|
||||
if (rx <= 0) {
|
||||
result = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[rx] = 0;
|
||||
|
||||
char *str = strstr(buf, "TracerPid:");
|
||||
if (!str) {
|
||||
result = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
str += 10;
|
||||
|
||||
while (*str && !isdigit(*str)) {
|
||||
if (*str == '\n') {
|
||||
result = 0;
|
||||
return 0;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
|
||||
result = (atoi(str) != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void *crashGetModuleBase(const void *addr)
|
||||
{
|
||||
Dl_info info;
|
||||
if (dladdr(addr, &info)) {
|
||||
return info.dli_fbase;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void crashStackTrace(char *msg, s32 sig, void *pc)
|
||||
{
|
||||
u32 msglen = 0;
|
||||
void *frames[CRASH_MAX_FRAMES] = { NULL };
|
||||
|
||||
const s32 nframes = backtrace(frames, CRASH_MAX_FRAMES);
|
||||
if (nframes <= 0) {
|
||||
CRASH_MSG("no information\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char **strings = backtrace_symbols(frames, nframes);
|
||||
|
||||
CRASH_MSG("SIGNAL: %d\n", sig);
|
||||
CRASH_MSG("PC: ");
|
||||
if (pc) {
|
||||
CRASH_MSG("%p\n", pc);
|
||||
} else if (strings) {
|
||||
CRASH_MSG("%s\n", strings[0]);
|
||||
} else {
|
||||
CRASH_MSG("%p\n", frames[0]);
|
||||
}
|
||||
|
||||
CRASH_MSG("MODULE: %p\n", crashGetModuleBase(frames[0]));
|
||||
CRASH_MSG("MAIN MODULE: %p\n", crashGetModuleBase(crashInit));
|
||||
CRASH_MSG("\nBACKTRACE:\n");
|
||||
|
||||
s32 i;
|
||||
for (i = 0; i < nframes; ++i) {
|
||||
CRASH_MSG("#%02d: ", i);
|
||||
if (strings && strings[i]) {
|
||||
CRASH_MSG("%s\n", strings[i]);
|
||||
} else {
|
||||
CRASH_MSG("%p\n", frames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == CRASH_MAX_FRAMES) {
|
||||
CRASH_MSG("...\n");
|
||||
} else if (i <= 1) {
|
||||
CRASH_MSG("no information\n");
|
||||
}
|
||||
|
||||
free(strings);
|
||||
}
|
||||
|
||||
static void crashHandler(s32 sig, siginfo_t *siginfo, void *ctx)
|
||||
{
|
||||
char msg[CRASH_MAX_MSG + 1] = { 0 };
|
||||
|
||||
if (crashIsDebuggerPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void *pc = NULL;
|
||||
if (ctx) {
|
||||
ucontext_t *ucontext = (ucontext_t *)ctx;
|
||||
#ifdef PLATFORM_X86
|
||||
pc = (void *)ucontext->uc_mcontext.gregs[REG_EIP];
|
||||
#elif defined(PLATFORM_X86_64)
|
||||
pc = (void *)ucontext->uc_mcontext.gregs[REG_RIP];
|
||||
#elif defined(PLATFORM_ARM) && defined(PLATFORM_64BIT)
|
||||
pc = (void *)ucontext->uc_mcontext.pc;
|
||||
#elif defined(PLATFORM_ARM)
|
||||
pc = (void *)ucontext->uc_mcontext.arm_pc;
|
||||
#endif
|
||||
}
|
||||
|
||||
sysLogPrintf(LOG_ERROR, "FATAL: Crashed: PC=%p SIGNAL=%d", pc, sig);
|
||||
|
||||
fflush(stderr);
|
||||
fflush(stdout);
|
||||
|
||||
crashStackTrace(msg, sig, pc);
|
||||
|
||||
sysFatalError("Crash!\n\n%s", msg);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
s32 g_CrashEnabled = 0;
|
||||
|
||||
static char crashMsg[1024];
|
||||
|
||||
void crashInit(void)
|
||||
{
|
||||
#ifdef PLATFORM_WIN32
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
prevExFilter = SetUnhandledExceptionFilter(crashHandler);
|
||||
g_CrashEnabled = 1;
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
struct sigaction sigact = { 0 };
|
||||
sigact.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigact.sa_sigaction = crashHandler;
|
||||
sigaction(SIGSEGV, &sigact, &prevSigAction);
|
||||
sigaction(SIGABRT, &sigact, &prevSigAction);
|
||||
sigaction(SIGBUS, &sigact, &prevSigAction);
|
||||
sigaction(SIGILL, &sigact, &prevSigAction);
|
||||
g_CrashEnabled = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void crashShutdown(void)
|
||||
{
|
||||
if (!g_CrashEnabled) {
|
||||
return;
|
||||
}
|
||||
#ifdef PLATFORM_WIN32
|
||||
if (prevExFilter) {
|
||||
SetUnhandledExceptionFilter(prevExFilter);
|
||||
}
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
sigaction(SIGSEGV, &prevSigAction, NULL);
|
||||
sigaction(SIGABRT, &prevSigAction, NULL);
|
||||
sigaction(SIGBUS, &prevSigAction, NULL);
|
||||
sigaction(SIGILL, &prevSigAction, NULL);
|
||||
#endif
|
||||
g_CrashEnabled = 0;
|
||||
}
|
||||
|
||||
void crashCreateThread(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void crashSetMessage(char *string)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void crashReset(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void crashAppendChar(char c)
|
||||
{
|
||||
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "platform.h"
|
||||
#include "utils.h"
|
||||
#include "fs.h"
|
||||
#ifdef PLATFORM_WIN32
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_BASEDIR_NAME "data"
|
||||
|
||||
static char baseDir[FS_MAXPATH + 1]; // replaces $B
|
||||
static char modDir[FS_MAXPATH + 1]; // replaces $M
|
||||
static char saveDir[FS_MAXPATH + 1]; // replaces $S
|
||||
static char homeDir[FS_MAXPATH + 1]; // replaces $H
|
||||
static char exeDir[FS_MAXPATH + 1]; // replaces $E
|
||||
|
||||
static s32 fsPathIsWritable(const char *path)
|
||||
{
|
||||
#ifdef PLATFORM_WIN32
|
||||
// on windows access() on directories will only check if the directory exists, so
|
||||
char tmp[FS_MAXPATH + 1] = { 0 };
|
||||
snprintf(tmp, sizeof(tmp), "%s/.tmp", path);
|
||||
FILE *f = fopen(tmp, "wb");
|
||||
if (f) {
|
||||
fclose(f);
|
||||
remove(tmp);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return (access(path, W_OK) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
s32 fsPathIsAbsolute(const char *path)
|
||||
{
|
||||
return (path[0] == '/' || (isalpha(path[0]) && path[1] == ':'));
|
||||
}
|
||||
|
||||
s32 fsPathIsCwdRelative(const char *path)
|
||||
{
|
||||
// ., .., ./, ../
|
||||
return (path[0] == '.' && (path[1] == '.' || path[1] == '/' || path[1] == '\\' || path[1] == '\0'));
|
||||
}
|
||||
|
||||
const char *fsFullPath(const char *relPath)
|
||||
{
|
||||
static char pathBuf[FS_MAXPATH + 1];
|
||||
|
||||
if (relPath[0] == '$') {
|
||||
// expandable placeholder $X; will be replaced with the corresponding path, if any
|
||||
const char *expStr = NULL;
|
||||
switch (relPath[1]) {
|
||||
case 'E': expStr = exeDir; break;
|
||||
case 'H': expStr = homeDir; break;
|
||||
case 'M': expStr = modDir; break;
|
||||
case 'B': expStr = baseDir; break;
|
||||
case 'S': expStr = saveDir; break;
|
||||
default: break;
|
||||
}
|
||||
if (expStr) {
|
||||
const u32 len = strlen(expStr);
|
||||
if (len > 0) {
|
||||
memcpy(pathBuf, expStr, len);
|
||||
strncpy(pathBuf + len, relPath + 2, FS_MAXPATH - len);
|
||||
return pathBuf;
|
||||
}
|
||||
}
|
||||
// couldn't expand anything, return as is
|
||||
return relPath;
|
||||
} else if (!baseDir[0] || fsPathIsAbsolute(relPath) || fsPathIsCwdRelative(relPath)) {
|
||||
// user explicitly wants working directory or this is an absolute path or we have no baseDir set up yet
|
||||
return relPath;
|
||||
}
|
||||
|
||||
// path relative to mod or base dir; this will be a read request, so check where the file actually is
|
||||
if (modDir[0]) {
|
||||
snprintf(pathBuf, FS_MAXPATH, "%s/%s", modDir, relPath);
|
||||
if (fsFileSize(pathBuf) >= 0) {
|
||||
return pathBuf;
|
||||
}
|
||||
}
|
||||
// fall back to basedir
|
||||
snprintf(pathBuf, FS_MAXPATH, "%s/%s", baseDir, relPath);
|
||||
return pathBuf;
|
||||
}
|
||||
|
||||
s32 fsInit(void)
|
||||
{
|
||||
sysGetExecutablePath(exeDir, FS_MAXPATH);
|
||||
|
||||
// if this is set, default to exe path for everything
|
||||
const s32 portable = sysArgCheck("--portable");
|
||||
if (portable) {
|
||||
strcpy(homeDir, exeDir);
|
||||
} else {
|
||||
sysGetHomePath(homeDir, FS_MAXPATH);
|
||||
}
|
||||
|
||||
// get path to base dir and expand it if needed
|
||||
const char *path = sysArgGetString("--basedir");
|
||||
if (!path) {
|
||||
// check if there's a `data` directory in working directory or homeDir, otherwise default to exe directory
|
||||
path = "$E/" DEFAULT_BASEDIR_NAME;
|
||||
if (!portable) {
|
||||
if (fsFileSize("./" DEFAULT_BASEDIR_NAME) >= 0) {
|
||||
path = "./" DEFAULT_BASEDIR_NAME;
|
||||
} else if (fsFileSize("$H/" DEFAULT_BASEDIR_NAME) >= 0) {
|
||||
path = "$H/" DEFAULT_BASEDIR_NAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
strncpy(baseDir, fsFullPath(path), FS_MAXPATH);
|
||||
|
||||
// get path to mod dir and expand it if needed
|
||||
// mod directory is overlaid on top of base directory
|
||||
path = sysArgGetString("--moddir");
|
||||
if (path) {
|
||||
if (fsPathIsAbsolute(path) || fsPathIsCwdRelative(path) || path[0] == '$') {
|
||||
// path is explicit; check as-is
|
||||
if (fsFileSize(path) >= 0) {
|
||||
strncpy(modDir, fsFullPath(path), FS_MAXPATH);
|
||||
}
|
||||
} else {
|
||||
// path is relative to workdir; try to find it
|
||||
const char *priority[] = { ".", "$E", "$H" };
|
||||
for (s32 i = 0; i < 2 + (portable != 0); ++i) {
|
||||
char *tmp = strFmt("%s/%s", priority[i], path);
|
||||
if (fsFileSize(tmp) >= 0) {
|
||||
strncpy(modDir, fsFullPath(tmp), FS_MAXPATH);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!modDir[0]) {
|
||||
sysLogPrintf(LOG_WARNING, "could not find specified moddir `%s`", path);
|
||||
}
|
||||
}
|
||||
|
||||
// get path to save dir and expand it if needed
|
||||
path = sysArgGetString("--savedir");
|
||||
if (!path) {
|
||||
if (portable) {
|
||||
path = "$E";
|
||||
} else {
|
||||
#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
|
||||
// check if there's a config in the working directory, otherwise default to homeDir
|
||||
if (fsFileSize("./" CONFIG_FNAME) >= 0) {
|
||||
path = ".";
|
||||
} else {
|
||||
path = "$H";
|
||||
}
|
||||
#else
|
||||
// check if working directory is writable, otherwise default to homeDir
|
||||
if (fsPathIsWritable("./")) {
|
||||
path = ".";
|
||||
} else {
|
||||
sysLogPrintf(LOG_WARNING, "cannot write to working directory, will use %s for saves instead", homeDir);
|
||||
path = "$H";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(saveDir, fsFullPath(path), FS_MAXPATH);
|
||||
|
||||
if (modDir[0]) {
|
||||
sysLogPrintf(LOG_NOTE, " mod dir: %s", modDir);
|
||||
}
|
||||
sysLogPrintf(LOG_NOTE, "base dir: %s", baseDir);
|
||||
sysLogPrintf(LOG_NOTE, "save dir: %s", saveDir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *fsGetModDir(void)
|
||||
{
|
||||
return modDir[0] ? modDir : NULL;
|
||||
}
|
||||
|
||||
s32 fsFileLoadTo(const char *name, void *dst, u32 dstSize)
|
||||
{
|
||||
const char *fullName = fsFullPath(name);
|
||||
|
||||
FILE *f = fopen(fullName, "rb");
|
||||
if (!f) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
const s32 size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (size < 0) {
|
||||
sysLogPrintf(LOG_ERROR, "fsFileLoadTo: empty file or invalid size (%d): %s", size, fullName);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((u32)size > dstSize) {
|
||||
sysLogPrintf(LOG_ERROR, "fsFileLoadTo: file too big for buffer (%u > %u): %s", size, dstSize, fullName);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fread(dst, 1, size, f);
|
||||
fclose(f);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void *fsFileLoad(const char *name, u32 *outSize)
|
||||
{
|
||||
const char *fullName = fsFullPath(name);
|
||||
|
||||
FILE *f = fopen(fullName, "rb");
|
||||
if (!f) {
|
||||
sysLogPrintf(LOG_ERROR, "fsFileLoad: could not find file: %s", fullName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
const s32 size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (size < 0) {
|
||||
sysLogPrintf(LOG_ERROR, "fsFileLoad: empty file or invalid size (%d): %s", size, fullName);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *buf = NULL;
|
||||
if (size) {
|
||||
buf = sysMemZeroAlloc(size + 1); // sick hack for a free null terminator
|
||||
if (!buf) {
|
||||
sysLogPrintf(LOG_ERROR, "fsFileLoad: could not alloc %d bytes for file: %s", size, fullName);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
fread(buf, 1, size, f);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (outSize) {
|
||||
*outSize = size;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
s32 fsFileSize(const char *name)
|
||||
{
|
||||
const char *fullName = fsFullPath(name);
|
||||
struct stat st;
|
||||
if (stat(fullName, &st) < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return st.st_size;
|
||||
}
|
||||
}
|
||||
|
||||
FILE *fsFileOpenWrite(const char *name)
|
||||
{
|
||||
return fopen(fsFullPath(name), "wb");
|
||||
}
|
||||
|
||||
FILE *fsFileOpenRead(const char *name)
|
||||
{
|
||||
return fopen(fsFullPath(name), "rb");
|
||||
}
|
||||
|
||||
void fsFileFree(FILE *f)
|
||||
{
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
s32 fsCreateDir(const char *path)
|
||||
{
|
||||
#ifdef PLATFORM_WIN32
|
||||
return _mkdir(fsFullPath(path));
|
||||
#else
|
||||
return mkdir(fsFullPath(path), 0777);
|
||||
#endif
|
||||
}
|
||||
+1551
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,487 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <PR/os_internal.h>
|
||||
#include <PR/rcp.h>
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
#include "input.h"
|
||||
#include "video.h"
|
||||
#include "audio.h"
|
||||
#include "fs.h"
|
||||
|
||||
#define EEPROM_SIZE (EEP16K_MAXBLOCKS * 8)
|
||||
#define EEPROM_FNAME "eeprom.bin"
|
||||
#define EEPROM_PATH "$S/" EEPROM_FNAME
|
||||
|
||||
#define OS_COUNTER_RATE 46875000ULL
|
||||
#define OS_COUNTER_NUM (OS_COUNTER_RATE / 1000ULL)
|
||||
#define OS_COUNTER_DEN (1000000ULL / 1000ULL)
|
||||
|
||||
u64 osClockRate = OS_CLOCK_RATE;
|
||||
u32 osMemSize = 16 * 1024 * 1024; /* expansion pak installed plus some extra */
|
||||
s32 osTvType = OS_TV_NTSC; /* 0 = PAL, 1 = NTSC, 2 = MPAL */
|
||||
s32 osResetType = 0; /* 0 = cold reset */
|
||||
s32 osViClock = VI_NTSC_CLOCK;
|
||||
|
||||
static u8 eeprom[EEPROM_SIZE];
|
||||
static char eepromPath[FS_MAXPATH + 1];
|
||||
static s32 eepromLoaded = 0;
|
||||
|
||||
/* Time */
|
||||
|
||||
OSTime osGetTime(void)
|
||||
{
|
||||
// u64 should be enough to last a while
|
||||
return (sysGetMicroseconds() * OS_COUNTER_NUM) / OS_COUNTER_DEN;
|
||||
}
|
||||
|
||||
u32 osGetCount(void)
|
||||
{
|
||||
return (u32)osGetTime();
|
||||
}
|
||||
|
||||
/* Thread */
|
||||
|
||||
void osCreateThread(OSThread *thrd, OSId id, void (*entry)(void *), void *arg, void *sp, OSPri p)
|
||||
{
|
||||
thrd->id = id;
|
||||
thrd->state = OS_STATE_STOPPED;
|
||||
}
|
||||
|
||||
void osDestroyThread(OSThread *thrd)
|
||||
{
|
||||
thrd->id = 0;
|
||||
thrd->state = 0;
|
||||
}
|
||||
|
||||
void osYieldThread(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osStartThread(OSThread *thrd)
|
||||
{
|
||||
thrd->state = OS_STATE_RUNNING;
|
||||
}
|
||||
|
||||
void osStopThread(OSThread *thrd)
|
||||
{
|
||||
thrd->state = OS_STATE_STOPPED;
|
||||
}
|
||||
|
||||
OSPri osGetThreadPri(OSThread *thrd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osSetThreadPri(OSThread *thrd, OSPri pri)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* Mesg */
|
||||
|
||||
void osCreateMesgQueue(OSMesgQueue *mq, OSMesg *msgBuf, s32 count)
|
||||
{
|
||||
mq->validCount = 0;
|
||||
mq->first = 0;
|
||||
mq->msgCount = count;
|
||||
mq->msg = msgBuf;
|
||||
}
|
||||
|
||||
void osSetEventMesg(OSEvent e, OSMesgQueue *mq, OSMesg msg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
s32 osSendMesg(OSMesgQueue *mq, OSMesg msg, s32 flag)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 osRecvMesg(OSMesgQueue *mq, OSMesg *msg, s32 flag)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Vi */
|
||||
|
||||
void osCreateViManager(OSPri pri)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osViSetMode(OSViMode *mode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osViSetEvent(OSMesgQueue *mq, OSMesg msg, u32 retraceCount)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osViBlack(u8 active)
|
||||
{
|
||||
if (active) {
|
||||
videoClearScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void osViSetSpecialFeatures(u32 func)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osViSwapBuffer(void *vaddr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osViSetXScale(f32 value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osViSetYScale(f32 value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* Ai */
|
||||
|
||||
s32 osAiSetFrequency(u32 frequency)
|
||||
{
|
||||
// we can allow for any freq
|
||||
return (s32)frequency;
|
||||
}
|
||||
|
||||
u32 osAiGetLength(void)
|
||||
{
|
||||
return audioGetBytesBuffered();
|
||||
}
|
||||
|
||||
s32 osAiSetNextBuffer(void *bufPtr, u32 size)
|
||||
{
|
||||
audioSetNextBuffer(bufPtr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cont */
|
||||
|
||||
s32 osContInit(OSMesgQueue *mesgq, u8 *bitpattern, OSContStatus *data)
|
||||
{
|
||||
if (bitpattern) {
|
||||
*bitpattern = inputControllerMask();
|
||||
}
|
||||
if (data) {
|
||||
osContGetQuery(data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 osContStartReadData(OSMesgQueue *mesgq)
|
||||
{
|
||||
osSendMesg(mesgq, (OSMesg)0, OS_MESG_NOBLOCK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osContGetReadData(OSContPad *pad)
|
||||
{
|
||||
// game always passes in an array of 4 OSContPads
|
||||
for (s32 i = 0; i < MAXCONTROLLERS; ++i, ++pad) {
|
||||
pad->button = 0;
|
||||
pad->stick_x = 0;
|
||||
pad->stick_y = 0;
|
||||
pad->rstick_x = 0;
|
||||
pad->rstick_y = 0;
|
||||
if (inputReadController(i, pad) < 0) {
|
||||
pad->errnum = CONT_NO_RESPONSE_ERROR;
|
||||
} else {
|
||||
pad->errnum = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 osContStartQuery(OSMesgQueue *mq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osContGetQuery(OSContStatus *status)
|
||||
{
|
||||
// also always 4 status structs here
|
||||
for (s32 i = 0; i < MAXCONTROLLERS; ++i, ++status) {
|
||||
if (inputControllerConnected(i)) {
|
||||
status->errnum = 0;
|
||||
status->type = CONT_ABSOLUTE;
|
||||
status->status = CONT_CARD_ON;
|
||||
} else {
|
||||
status->errnum = CONT_NO_RESPONSE_ERROR;
|
||||
status->type = 0;
|
||||
status->status = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Motor */
|
||||
|
||||
s32 osMotorProbe(OSMesgQueue *ctrlrqueue, OSPfs* pfs, s32 channel)
|
||||
{
|
||||
if (pfs && inputRumbleSupported(channel)) {
|
||||
pfs->queue = ctrlrqueue;
|
||||
pfs->channel = channel;
|
||||
pfs->activebank = 0xff;
|
||||
pfs->status = PFS_MOTOR_INITIALIZED;
|
||||
return 0;
|
||||
}
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 __osMotorAccess(OSPfs *pfs, s32 cmd)
|
||||
{
|
||||
if (!pfs || pfs->channel < 0 || pfs->channel >= INPUT_MAX_CONTROLLERS) {
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
const f32 strength = (f32)(cmd == MOTOR_START);
|
||||
inputRumble(pfs->channel, strength, 5.f); // hope someone turns it off in those 5 seconds
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Eeprom */
|
||||
|
||||
static inline void osEepromSetPath(void)
|
||||
{
|
||||
const char *extPath = sysArgGetString("--eeprom-file");
|
||||
if (extPath && extPath[0]) {
|
||||
if (extPath[0] == '$' || fsPathIsAbsolute(extPath) || fsPathIsCwdRelative(extPath)) {
|
||||
strncpy(eepromPath, extPath, FS_MAXPATH);
|
||||
} else {
|
||||
// just a filename, look for it in the save dir
|
||||
snprintf(eepromPath, FS_MAXPATH, "$S/%s", extPath);
|
||||
}
|
||||
} else {
|
||||
strncpy(eepromPath, EEPROM_PATH, FS_MAXPATH);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void osEeepromLoad(const char *fname)
|
||||
{
|
||||
if (!eepromLoaded) {
|
||||
eepromLoaded = 1;
|
||||
FILE *fp = fsFileOpenRead(fname);
|
||||
if (fp) {
|
||||
fread(eeprom, 1, EEPROM_SIZE, fp);
|
||||
fsFileFree(fp);
|
||||
} else {
|
||||
sysLogPrintf(LOG_NOTE, "could not read EEPROM from `%s`: %s", fsFullPath(fname), strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void osEeepromSave(const char *fname)
|
||||
{
|
||||
FILE* fp = fsFileOpenWrite(fname);
|
||||
if (fp) {
|
||||
fwrite(eeprom, 1, EEPROM_SIZE, fp);
|
||||
fsFileFree(fp);
|
||||
} else {
|
||||
sysLogPrintf(LOG_ERROR, "could not save EEPROM to `%s`: %s", fsFullPath(fname), strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
s32 osEepromProbe(OSMesgQueue *mq)
|
||||
{
|
||||
return EEPROM_TYPE_16K;
|
||||
}
|
||||
|
||||
s32 osEepromLongRead(OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes)
|
||||
{
|
||||
if (!eepromPath[0]) {
|
||||
osEepromSetPath();
|
||||
}
|
||||
|
||||
osEeepromLoad(eepromPath);
|
||||
|
||||
memcpy(buffer, eeprom + address * 8, nbytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 osEepromLongWrite(OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes)
|
||||
{
|
||||
if (!eepromPath[0]) {
|
||||
osEepromSetPath();
|
||||
}
|
||||
|
||||
osEeepromLoad(eepromPath);
|
||||
|
||||
memcpy(eeprom + address * 8, buffer, nbytes);
|
||||
|
||||
osEeepromSave(eepromPath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pfs */
|
||||
|
||||
s32 osPfsIsPlug(OSMesgQueue *queue, u8 *pattern)
|
||||
{
|
||||
if (pattern) {
|
||||
*pattern = 0;
|
||||
for (s32 i = 0; i < MAXCONTROLLERS; ++i) {
|
||||
if (inputRumbleSupported(i)) {
|
||||
*pattern |= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 osPfsInitPak(OSMesgQueue *queue, OSPfs *pfs, s32 channel, s32 *arg3)
|
||||
{
|
||||
// if rumble is supported, indicate that we have a rumble pak instead
|
||||
return inputRumbleSupported(channel) ? PFS_ERR_DEVICE : PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsChecker(OSPfs *pfs)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsFreeBlocks(OSPfs *pfs, s32 *remaining)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsNumFiles(OSPfs *pfs, s32 *max_files, s32 *files_used)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsAllocateFile(OSPfs *pfs, u16 company_code, u32 game_code, u8 *game_name, u8 *ext_name, int num_bytes, s32 *file_no)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsFindFile(OSPfs *pfs, u16 company_code, u32 game_code, u8 *game_name, u8 *ext_name, s32 *file_no)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsDeleteFile(OSPfs *pfs, u16 company_code, u32 game_code, u8 *game_name, u8 *ext_name)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsReSizeFile(OSPfs *pfs, u16 company_code, u32 game_code, u8 *game_name, u8 *ext_name, int length)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsFileState(OSPfs *pfs, s32 fileNo, OSPfsState *state)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osPfsReadWriteFile(OSPfs* pfs, s32 fileNo, u8 flag, int offset, int size, u8* data)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
/* Gbpak */
|
||||
|
||||
s32 osGbpakInit(OSMesgQueue *queue, OSPfs *pfs, s32 ch)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osGbpakPower(OSPfs *pfs, s32 flag)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osGbpakReadWrite(OSPfs *pfs, u16 flag, u16 addr, u8 *buf, u16 size)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
s32 osGbpakReadId(OSPfs *pfs, OSGbpakId *id, u8 *status)
|
||||
{
|
||||
return PFS_ERR_NOPACK;
|
||||
}
|
||||
|
||||
/* Misc */
|
||||
|
||||
void osWritebackDCacheAll(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osWritebackDCache(void *a, s32 b)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void osInvalDCache(void *a, s32 b)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
s32 osPiStartDma(OSIoMesg *mb, s32 priority, s32 direction, uintptr_t devAddr, void *vAddr, u32 nbytes, OSMesgQueue *mq)
|
||||
{
|
||||
memcpy(vAddr, (const void *)devAddr, nbytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 osPiReadIo(u32 devaddr, u32 *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t osVirtualToPhysical(void *addr)
|
||||
{
|
||||
return (uintptr_t)addr;
|
||||
}
|
||||
|
||||
u32 osGetMemSize(void)
|
||||
{
|
||||
return osMemSize;
|
||||
}
|
||||
|
||||
OSIntMask osGetIntMask(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
OSIntMask osSetIntMask(OSIntMask mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* libc compatibility wrappers */
|
||||
|
||||
#ifndef PLATFORM_OSX
|
||||
|
||||
void bzero(void *ptr, size_t size)
|
||||
{
|
||||
memset(ptr, 0, size);
|
||||
}
|
||||
|
||||
void bcopy(const void *src, void *dst, size_t n)
|
||||
{
|
||||
memcpy(dst, src, n);
|
||||
}
|
||||
|
||||
s32 bcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
return memcmp(s1, s2, n);
|
||||
}
|
||||
|
||||
#endif
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include <PR/ultrasched.h>
|
||||
#include <PR/os_message.h>
|
||||
|
||||
#include "lib/main.h"
|
||||
#include "bss.h"
|
||||
#include "data.h"
|
||||
|
||||
#include "video.h"
|
||||
#include "audio.h"
|
||||
#include "input.h"
|
||||
#include "fs.h"
|
||||
#include "romdata.h"
|
||||
#include "config.h"
|
||||
#include "mod.h"
|
||||
#include "system.h"
|
||||
#include "utils.h"
|
||||
|
||||
u32 g_OsMemSize = 0;
|
||||
s32 g_OsMemSizeMb = 16;
|
||||
u8 g_Is4Mb = 0;
|
||||
s8 g_Resetting = false;
|
||||
OSSched g_Sched;
|
||||
|
||||
OSMesgQueue g_MainMesgQueue;
|
||||
OSMesg g_MainMesgBuf[32];
|
||||
|
||||
u8 *g_MempHeap = NULL;
|
||||
u32 g_MempHeapSize = 0;
|
||||
|
||||
u32 g_VmNumTlbMisses = 0;
|
||||
u32 g_VmNumPageMisses = 0;
|
||||
u32 g_VmNumPageReplaces = 0;
|
||||
u8 g_VmShowStats = 0;
|
||||
|
||||
s32 g_TickRateDiv = 1;
|
||||
s32 g_TickExtraSleep = true;
|
||||
|
||||
s32 g_SkipIntro = false;
|
||||
|
||||
s32 g_FileAutoSelect = -1;
|
||||
|
||||
extern s32 g_StageNum;
|
||||
|
||||
s32 bootGetMemSize(void)
|
||||
{
|
||||
return (s32)g_OsMemSize;
|
||||
}
|
||||
|
||||
void *bootAllocateStack(s32 threadid, s32 size)
|
||||
{
|
||||
static u8 bruh[0x1000];
|
||||
return bruh;
|
||||
}
|
||||
|
||||
void bootCreateSched(void)
|
||||
{
|
||||
osCreateMesgQueue(&g_MainMesgQueue, g_MainMesgBuf, ARRAYCOUNT(g_MainMesgBuf));
|
||||
if (osTvType == OS_TV_MPAL) {
|
||||
osCreateScheduler(&g_Sched, NULL, OS_VI_MPAL_LAN1, 1);
|
||||
} else {
|
||||
osCreateScheduler(&g_Sched, NULL, OS_VI_NTSC_LAN1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void gameInit(void)
|
||||
{
|
||||
osMemSize = g_OsMemSizeMb * 1024 * 1024;
|
||||
|
||||
for (s32 i = 0; i < MAX_PLAYERS; ++i) {
|
||||
struct extplayerconfig *cfg = g_PlayerExtCfg + i;
|
||||
cfg->fovzoommult = cfg->fovzoom ? cfg->fovy / 60.0f : 1.0f;
|
||||
}
|
||||
|
||||
if (g_HudCenter == HUDCENTER_NORMAL) {
|
||||
g_HudAlignModeL = G_ASPECT_CENTER_EXT;
|
||||
g_HudAlignModeR = G_ASPECT_CENTER_EXT;
|
||||
} else if (g_HudCenter == HUDCENTER_WIDE) {
|
||||
g_HudAlignModeL = G_ASPECT_LEFT_EXT | G_ASPECT_WIDE_EXT;
|
||||
g_HudAlignModeR = G_ASPECT_RIGHT_EXT | G_ASPECT_WIDE_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
sysLogPrintf(LOG_NOTE, "shutdown");
|
||||
inputSaveBinds();
|
||||
configSave(CONFIG_PATH);
|
||||
videoShutdown();
|
||||
crashShutdown();
|
||||
// TODO: actually shut down all subsystems
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
sysInitArgs(argc, argv);
|
||||
|
||||
if (!sysArgCheck("--no-crash-handler")) {
|
||||
crashInit();
|
||||
}
|
||||
|
||||
sysInit();
|
||||
fsInit();
|
||||
configInit();
|
||||
videoInit();
|
||||
inputInit();
|
||||
audioInit();
|
||||
romdataInit();
|
||||
|
||||
g_ValidGbcRomFound = romdataCheckGbcRom();
|
||||
|
||||
gameInit();
|
||||
|
||||
if (fsGetModDir()) {
|
||||
modConfigLoad(MOD_CONFIG_FNAME);
|
||||
}
|
||||
|
||||
atexit(cleanup);
|
||||
|
||||
bootCreateSched();
|
||||
|
||||
g_OsMemSize = osGetMemSize();
|
||||
|
||||
g_MempHeapSize = g_OsMemSize;
|
||||
g_MempHeap = sysMemZeroAlloc(g_MempHeapSize);
|
||||
if (!g_MempHeap) {
|
||||
sysFatalError("Could not alloc %u bytes for memp heap.", g_MempHeapSize);
|
||||
}
|
||||
|
||||
sysLogPrintf(LOG_NOTE, "memp heap at %p - %p", g_MempHeap, g_MempHeap + g_MempHeapSize);
|
||||
sysLogPrintf(LOG_NOTE, "rom file at %p - %p", g_RomFile, g_RomFile + g_RomFileSize);
|
||||
|
||||
g_SndDisabled = sysArgCheck("--no-sound");
|
||||
|
||||
g_StageNum = sysArgGetInt("--boot-stage", STAGE_TITLE);
|
||||
|
||||
if (g_StageNum == STAGE_TITLE && (sysArgCheck("--skip-intro") || g_SkipIntro)) {
|
||||
// shorthand for --boot-stage 0x26
|
||||
g_StageNum = STAGE_CITRAINING;
|
||||
} else if (g_StageNum < 0x01 || g_StageNum > 0x5d) {
|
||||
// stage num out of range
|
||||
g_StageNum = STAGE_TITLE;
|
||||
}
|
||||
|
||||
if (g_StageNum != STAGE_TITLE) {
|
||||
sysLogPrintf(LOG_NOTE, "boot stage set to 0x%02x", g_StageNum);
|
||||
}
|
||||
|
||||
g_FileAutoSelect = sysArgGetInt("--profile", -1);
|
||||
if (g_FileAutoSelect >= 0) {
|
||||
sysLogPrintf(LOG_NOTE, "player profile set to %d", g_FileAutoSelect);
|
||||
}
|
||||
|
||||
mainProc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PD_CONSTRUCTOR static void gameConfigInit(void)
|
||||
{
|
||||
configRegisterInt("Game.MemorySize", &g_OsMemSizeMb, 4, 2048);
|
||||
configRegisterInt("Game.CenterHUD", &g_HudCenter, 0, 2);
|
||||
configRegisterInt("Game.MenuMouseControl", &g_MenuMouseControl, 0, 1);
|
||||
configRegisterFloat("Game.ScreenShakeIntensity", &g_ViShakeIntensityMult, 0.f, 10.f);
|
||||
configRegisterInt("Game.TickRateDivisor", &g_TickRateDiv, 0, 10);
|
||||
configRegisterInt("Game.ExtraSleep", &g_TickExtraSleep, 0, 1);
|
||||
configRegisterInt("Game.SkipIntro", &g_SkipIntro, 0, 1);
|
||||
configRegisterInt("Game.DisableMpDeathMusic", &g_MusicDisableMpDeath, 0, 1);
|
||||
configRegisterInt("Game.GEMuzzleFlashes", &g_BgunGeMuzzleFlashes, 0, 1);
|
||||
configRegisterInt("Game.MaxExplosions", &g_MaxExplosions, 6, 96);
|
||||
configRegisterInt("Game.DecalLimitMultiplier", &g_DecalLimitMultiplier, 1, MAX_DECAL_LIMIT_MULTIPLIER);
|
||||
configRegisterInt("Game.MaxCorpses", &g_MaxCorpses, -1, MAX_CORPSES_LIMIT); // -1 = never fade
|
||||
for (s32 j = 0; j < MAX_PLAYERS; ++j) {
|
||||
const s32 i = j + 1;
|
||||
configRegisterFloat(strFmt("Game.Player%d.FovY", i), &g_PlayerExtCfg[j].fovy, 5.f, 175.f);
|
||||
configRegisterInt(strFmt("Game.Player%d.FovAffectsZoom", i), &g_PlayerExtCfg[j].fovzoom, 0, 1);
|
||||
configRegisterInt(strFmt("Game.Player%d.MouseAimMode", i), &g_PlayerExtCfg[j].mouseaimmode, 0, 1);
|
||||
configRegisterFloat(strFmt("Game.Player%d.MouseAimSpeedX", i), &g_PlayerExtCfg[j].mouseaimspeedx, 0.f, 10.f);
|
||||
configRegisterFloat(strFmt("Game.Player%d.MouseAimSpeedY", i), &g_PlayerExtCfg[j].mouseaimspeedy, 0.f, 10.f);
|
||||
configRegisterFloat(strFmt("Game.Player%d.RadialMenuSpeed", i), &g_PlayerExtCfg[j].radialmenuspeed, 0.f, 10.f);
|
||||
configRegisterFloat(strFmt("Game.Player%d.CrosshairSway", i), &g_PlayerExtCfg[j].crosshairsway, 0.f, 10.f);
|
||||
configRegisterFloat(strFmt("Game.Player%d.CrosshairEdgeBoundary", i), &g_PlayerExtCfg[j].crosshairedgeboundary, 0.0f, 1.0f);
|
||||
configRegisterInt(strFmt("Game.Player%d.CrouchMode", i), &g_PlayerExtCfg[j].crouchmode, 0, CROUCHMODE_TOGGLE_ANALOG);
|
||||
configRegisterInt(strFmt("Game.Player%d.ExtendedControls", i), &g_PlayerExtCfg[j].extcontrols, 0, 1);
|
||||
configRegisterUInt(strFmt("Game.Player%d.CrosshairColour", i), &g_PlayerExtCfg[j].crosshaircolour, 0, 0xFFFFFFFF);
|
||||
configRegisterUInt(strFmt("Game.Player%d.CrosshairSize", i), &g_PlayerExtCfg[j].crosshairsize, 0, 4);
|
||||
configRegisterInt(strFmt("Game.Player%d.CrosshairHealth", i), &g_PlayerExtCfg[j].crosshairhealth, 0, CROSSHAIR_HEALTH_ON_WHITE);
|
||||
configRegisterInt(strFmt("Game.Player%d.UseKeyReloads", i), &g_PlayerExtCfg[j].usereloads, 0, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <ultra64.h>
|
||||
|
||||
#include "mixer.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#include "external/minimp3.h"
|
||||
|
||||
#ifdef __SSE4_1__
|
||||
#include <immintrin.h>
|
||||
#define HAS_SSE41 1
|
||||
#define HAS_NEON 0
|
||||
#elif __ARM_NEON
|
||||
#include <arm_neon.h>
|
||||
#define HAS_SSE41 0
|
||||
#define HAS_NEON 1
|
||||
#else
|
||||
#define HAS_SSE41 0
|
||||
#define HAS_NEON 0
|
||||
#endif
|
||||
|
||||
#pragma GCC optimize ("unroll-loops")
|
||||
|
||||
#if HAS_SSE41
|
||||
#define LOADLH(l, h) _mm_castpd_si128(_mm_loadh_pd(_mm_load_sd((const double *)(l)), (const double *)(h)))
|
||||
#endif
|
||||
|
||||
#define ROUND_UP_64(v) (((v) + 63) & ~63)
|
||||
#define ROUND_UP_32(v) (((v) + 31) & ~31)
|
||||
#define ROUND_UP_16(v) (((v) + 15) & ~15)
|
||||
#define ROUND_UP_8(v) (((v) + 7) & ~7)
|
||||
#define ROUND_DOWN_16(v) ((v) & ~0xf)
|
||||
|
||||
#define BUF_SIZE 3072 // 2720 + slack
|
||||
#define BUF_U8(a) (rspa.buf.as_u8 + (a))
|
||||
#define BUF_S16(a) (rspa.buf.as_s16 + (a) / sizeof(int16_t))
|
||||
|
||||
#define NUM_SAMPLES 0xB8
|
||||
#define NUM_BYTES 0x170
|
||||
|
||||
#define OFS_BASE 0
|
||||
#define OFS_TEMP1 0x170
|
||||
#define OFS_MAIN_L 0x4E0
|
||||
#define OFS_MAIN_R 0x650
|
||||
#define OFS_AUX_L 0x7C0
|
||||
#define OFS_AUX_R 0x930
|
||||
|
||||
static struct {
|
||||
int16_t vol[2];
|
||||
|
||||
int16_t target[2];
|
||||
int32_t rate[2];
|
||||
|
||||
int16_t vol_dry;
|
||||
int16_t vol_wet;
|
||||
|
||||
ADPCM_STATE *adpcm_loop_state;
|
||||
|
||||
int16_t adpcm_table[8][2][8];
|
||||
|
||||
union {
|
||||
int16_t as_s16[BUF_SIZE / sizeof(int16_t)];
|
||||
uint8_t as_u8[BUF_SIZE];
|
||||
} buf;
|
||||
} rspa;
|
||||
|
||||
static int16_t resample_table[64][4] = {
|
||||
{0x0c39, 0x66ad, 0x0d46, 0xffdf}, {0x0b39, 0x6696, 0x0e5f, 0xffd8},
|
||||
{0x0a44, 0x6669, 0x0f83, 0xffd0}, {0x095a, 0x6626, 0x10b4, 0xffc8},
|
||||
{0x087d, 0x65cd, 0x11f0, 0xffbf}, {0x07ab, 0x655e, 0x1338, 0xffb6},
|
||||
{0x06e4, 0x64d9, 0x148c, 0xffac}, {0x0628, 0x643f, 0x15eb, 0xffa1},
|
||||
{0x0577, 0x638f, 0x1756, 0xff96}, {0x04d1, 0x62cb, 0x18cb, 0xff8a},
|
||||
{0x0435, 0x61f3, 0x1a4c, 0xff7e}, {0x03a4, 0x6106, 0x1bd7, 0xff71},
|
||||
{0x031c, 0x6007, 0x1d6c, 0xff64}, {0x029f, 0x5ef5, 0x1f0b, 0xff56},
|
||||
{0x022a, 0x5dd0, 0x20b3, 0xff48}, {0x01be, 0x5c9a, 0x2264, 0xff3a},
|
||||
{0x015b, 0x5b53, 0x241e, 0xff2c}, {0x0101, 0x59fc, 0x25e0, 0xff1e},
|
||||
{0x00ae, 0x5896, 0x27a9, 0xff10}, {0x0063, 0x5720, 0x297a, 0xff02},
|
||||
{0x001f, 0x559d, 0x2b50, 0xfef4}, {0xffe2, 0x540d, 0x2d2c, 0xfee8},
|
||||
{0xffac, 0x5270, 0x2f0d, 0xfedb}, {0xff7c, 0x50c7, 0x30f3, 0xfed0},
|
||||
{0xff53, 0x4f14, 0x32dc, 0xfec6}, {0xff2e, 0x4d57, 0x34c8, 0xfebd},
|
||||
{0xff0f, 0x4b91, 0x36b6, 0xfeb6}, {0xfef5, 0x49c2, 0x38a5, 0xfeb0},
|
||||
{0xfedf, 0x47ed, 0x3a95, 0xfeac}, {0xfece, 0x4611, 0x3c85, 0xfeab},
|
||||
{0xfec0, 0x4430, 0x3e74, 0xfeac}, {0xfeb6, 0x424a, 0x4060, 0xfeaf},
|
||||
{0xfeaf, 0x4060, 0x424a, 0xfeb6}, {0xfeac, 0x3e74, 0x4430, 0xfec0},
|
||||
{0xfeab, 0x3c85, 0x4611, 0xfece}, {0xfeac, 0x3a95, 0x47ed, 0xfedf},
|
||||
{0xfeb0, 0x38a5, 0x49c2, 0xfef5}, {0xfeb6, 0x36b6, 0x4b91, 0xff0f},
|
||||
{0xfebd, 0x34c8, 0x4d57, 0xff2e}, {0xfec6, 0x32dc, 0x4f14, 0xff53},
|
||||
{0xfed0, 0x30f3, 0x50c7, 0xff7c}, {0xfedb, 0x2f0d, 0x5270, 0xffac},
|
||||
{0xfee8, 0x2d2c, 0x540d, 0xffe2}, {0xfef4, 0x2b50, 0x559d, 0x001f},
|
||||
{0xff02, 0x297a, 0x5720, 0x0063}, {0xff10, 0x27a9, 0x5896, 0x00ae},
|
||||
{0xff1e, 0x25e0, 0x59fc, 0x0101}, {0xff2c, 0x241e, 0x5b53, 0x015b},
|
||||
{0xff3a, 0x2264, 0x5c9a, 0x01be}, {0xff48, 0x20b3, 0x5dd0, 0x022a},
|
||||
{0xff56, 0x1f0b, 0x5ef5, 0x029f}, {0xff64, 0x1d6c, 0x6007, 0x031c},
|
||||
{0xff71, 0x1bd7, 0x6106, 0x03a4}, {0xff7e, 0x1a4c, 0x61f3, 0x0435},
|
||||
{0xff8a, 0x18cb, 0x62cb, 0x04d1}, {0xff96, 0x1756, 0x638f, 0x0577},
|
||||
{0xffa1, 0x15eb, 0x643f, 0x0628}, {0xffac, 0x148c, 0x64d9, 0x06e4},
|
||||
{0xffb6, 0x1338, 0x655e, 0x07ab}, {0xffbf, 0x11f0, 0x65cd, 0x087d},
|
||||
{0xffc8, 0x10b4, 0x6626, 0x095a}, {0xffd0, 0x0f83, 0x6669, 0x0a44},
|
||||
{0xffd8, 0x0e5f, 0x6696, 0x0b39}, {0xffdf, 0x0d46, 0x66ad, 0x0c39}
|
||||
};
|
||||
|
||||
static inline int16_t clamp16(int32_t v) {
|
||||
if (v < -0x8000) {
|
||||
return -0x8000;
|
||||
} else if (v > 0x7fff) {
|
||||
return 0x7fff;
|
||||
}
|
||||
return (int16_t)v;
|
||||
}
|
||||
|
||||
static inline int32_t clamp32(int64_t v) {
|
||||
if (v < -0x7fffffff - 1) {
|
||||
return -0x7fffffff - 1;
|
||||
} else if (v > 0x7fffffff) {
|
||||
return 0x7fffffff;
|
||||
}
|
||||
return (int32_t)v;
|
||||
}
|
||||
|
||||
void aClearBufferImpl(uint16_t addr, int nbytes) {
|
||||
nbytes = ROUND_UP_16(nbytes);
|
||||
memset(BUF_U8(addr), 0, nbytes);
|
||||
}
|
||||
|
||||
void aLoadBufferImpl(const void *source_addr, uint16_t dest_addr, uint16_t nbytes) {
|
||||
memcpy(BUF_U8(dest_addr), source_addr, ROUND_UP_8(nbytes));
|
||||
}
|
||||
|
||||
void aSaveBufferImpl(uint16_t source_addr, int16_t *dest_addr, uint16_t nbytes) {
|
||||
memcpy(dest_addr, BUF_S16(source_addr), ROUND_UP_8(nbytes));
|
||||
}
|
||||
|
||||
void aLoadADPCMImpl(int num_entries_times_16, const int16_t *book_source_addr) {
|
||||
memcpy(rspa.adpcm_table, book_source_addr, num_entries_times_16);
|
||||
}
|
||||
|
||||
void aInterleaveImpl(void) {
|
||||
const int16_t *l = BUF_S16(OFS_MAIN_L);
|
||||
const int16_t *r = BUF_S16(OFS_MAIN_R);
|
||||
int count = ROUND_UP_16(NUM_SAMPLES) / 8;
|
||||
int16_t *d = BUF_S16(OFS_BASE);
|
||||
while (count > 0) {
|
||||
int16_t l0 = *l++;
|
||||
int16_t l1 = *l++;
|
||||
int16_t l2 = *l++;
|
||||
int16_t l3 = *l++;
|
||||
int16_t l4 = *l++;
|
||||
int16_t l5 = *l++;
|
||||
int16_t l6 = *l++;
|
||||
int16_t l7 = *l++;
|
||||
int16_t r0 = *r++;
|
||||
int16_t r1 = *r++;
|
||||
int16_t r2 = *r++;
|
||||
int16_t r3 = *r++;
|
||||
int16_t r4 = *r++;
|
||||
int16_t r5 = *r++;
|
||||
int16_t r6 = *r++;
|
||||
int16_t r7 = *r++;
|
||||
*d++ = l0;
|
||||
*d++ = r0;
|
||||
*d++ = l1;
|
||||
*d++ = r1;
|
||||
*d++ = l2;
|
||||
*d++ = r2;
|
||||
*d++ = l3;
|
||||
*d++ = r3;
|
||||
*d++ = l4;
|
||||
*d++ = r4;
|
||||
*d++ = l5;
|
||||
*d++ = r5;
|
||||
*d++ = l6;
|
||||
*d++ = r6;
|
||||
*d++ = l7;
|
||||
*d++ = r7;
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes) {
|
||||
memmove(BUF_U8(out_addr), BUF_U8(in_addr), ROUND_UP_16(nbytes));
|
||||
}
|
||||
|
||||
void aSetLoopImpl(ADPCM_STATE *adpcm_loop_state) {
|
||||
rspa.adpcm_loop_state = adpcm_loop_state;
|
||||
}
|
||||
|
||||
void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state, int nbytes, uint16_t inofs, uint16_t outofs) {
|
||||
#if HAS_SSE41
|
||||
const __m128i tblrev = _mm_setr_epi8(12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, -1, -1);
|
||||
const __m128i pos0 = _mm_set_epi8(3, -1, 3, -1, 2, -1, 2, -1, 1, -1, 1, -1, 0, -1, 0, -1);
|
||||
const __m128i pos1 = _mm_set_epi8(7, -1, 7, -1, 6, -1, 6, -1, 5, -1, 5, -1, 4, -1, 4, -1);
|
||||
const __m128i mult = _mm_set_epi16(0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01);
|
||||
const __m128i mask = _mm_set1_epi16((int16_t)0xf000);
|
||||
#elif HAS_NEON
|
||||
static const int8_t pos0_data[] = {-1, 0, -1, 0, -1, 1, -1, 1, -1, 2, -1, 2, -1, 3, -1, 3};
|
||||
static const int8_t pos1_data[] = {-1, 4, -1, 4, -1, 5, -1, 5, -1, 6, -1, 6, -1, 7, -1, 7};
|
||||
static const int16_t mult_data[] = {0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10};
|
||||
static const int16_t table_prefix_data[] = {0, 0, 0, 0, 0, 0, 0, 1 << 11};
|
||||
const int8x16_t pos0 = vld1q_s8(pos0_data);
|
||||
const int8x16_t pos1 = vld1q_s8(pos1_data);
|
||||
const int16x8_t mult = vld1q_s16(mult_data);
|
||||
const int16x8_t mask = vdupq_n_s16((int16_t)0xf000);
|
||||
const int16x8_t table_prefix = vld1q_s16(table_prefix_data);
|
||||
#endif
|
||||
uint8_t *in = BUF_U8(inofs);
|
||||
int16_t *out = BUF_S16(outofs);
|
||||
nbytes = ROUND_UP_32(nbytes);
|
||||
if (flags & A_INIT) {
|
||||
memset(out, 0, 16 * sizeof(int16_t));
|
||||
} else if (flags & A_LOOP) {
|
||||
memcpy(out, rspa.adpcm_loop_state, 16 * sizeof(int16_t));
|
||||
} else {
|
||||
memcpy(out, state, 16 * sizeof(int16_t));
|
||||
}
|
||||
out += 16;
|
||||
#if HAS_SSE41
|
||||
__m128i prev_interleaved = _mm_set1_epi32((uint16_t)out[-2] | ((uint16_t)out[-1] << 16));
|
||||
//__m128i prev_interleaved = _mm_shuffle_epi32(_mm_loadu_si32(out - 2), 0); // GCC misses this?
|
||||
#elif HAS_NEON
|
||||
int16x8_t result = vld1q_s16(out - 8);
|
||||
#endif
|
||||
while (nbytes > 0) {
|
||||
int shift = *in >> 4; // should be in 0..12
|
||||
int table_index = *in++ & 0xf; // should be in 0..7
|
||||
int16_t (*tbl)[8] = rspa.adpcm_table[table_index];
|
||||
int i;
|
||||
#if HAS_SSE41
|
||||
// The _mm_loadu_si64 instruction was added in GCC 9, and results in the same
|
||||
// asm as the following instructions, so better be compatible with old GCC.
|
||||
//__m128i inv = _mm_loadu_si64(in);
|
||||
uint64_t v; memcpy(&v, in, 8);
|
||||
__m128i inv = _mm_set_epi64x(0, v);
|
||||
__m128i invec[2] = {_mm_shuffle_epi8(inv, pos0), _mm_shuffle_epi8(inv, pos1)};
|
||||
__m128i tblvec0 = _mm_loadu_si128((const __m128i *)tbl[0]);
|
||||
__m128i tblvec1 = _mm_loadu_si128((const __m128i *)(tbl[1]));
|
||||
__m128i tbllo = _mm_unpacklo_epi16(tblvec0, tblvec1);
|
||||
__m128i tblhi = _mm_unpackhi_epi16(tblvec0, tblvec1);
|
||||
__m128i shiftcount = _mm_set_epi64x(0, 12 - shift); // _mm_cvtsi64_si128 does not exist on 32-bit x86
|
||||
__m128i tblvec1_rev[8];
|
||||
|
||||
tblvec1_rev[0] = _mm_insert_epi16(_mm_shuffle_epi8(tblvec1, tblrev), 1 << 11, 7);
|
||||
tblvec1_rev[1] = _mm_bsrli_si128(tblvec1_rev[0], 2);
|
||||
tblvec1_rev[2] = _mm_bsrli_si128(tblvec1_rev[0], 4);
|
||||
tblvec1_rev[3] = _mm_bsrli_si128(tblvec1_rev[0], 6);
|
||||
tblvec1_rev[4] = _mm_bsrli_si128(tblvec1_rev[0], 8);
|
||||
tblvec1_rev[5] = _mm_bsrli_si128(tblvec1_rev[0], 10);
|
||||
tblvec1_rev[6] = _mm_bsrli_si128(tblvec1_rev[0], 12);
|
||||
tblvec1_rev[7] = _mm_bsrli_si128(tblvec1_rev[0], 14);
|
||||
in += 8;
|
||||
for (i = 0; i < 2; i++) {
|
||||
__m128i acc0 = _mm_madd_epi16(prev_interleaved, tbllo);
|
||||
__m128i acc1 = _mm_madd_epi16(prev_interleaved, tblhi);
|
||||
__m128i muls[8];
|
||||
__m128i result;
|
||||
invec[i] = _mm_sra_epi16(_mm_and_si128(_mm_mullo_epi16(invec[i], mult), mask), shiftcount);
|
||||
|
||||
muls[7] = _mm_madd_epi16(tblvec1_rev[0], invec[i]);
|
||||
muls[6] = _mm_madd_epi16(tblvec1_rev[1], invec[i]);
|
||||
muls[5] = _mm_madd_epi16(tblvec1_rev[2], invec[i]);
|
||||
muls[4] = _mm_madd_epi16(tblvec1_rev[3], invec[i]);
|
||||
muls[3] = _mm_madd_epi16(tblvec1_rev[4], invec[i]);
|
||||
muls[2] = _mm_madd_epi16(tblvec1_rev[5], invec[i]);
|
||||
muls[1] = _mm_madd_epi16(tblvec1_rev[6], invec[i]);
|
||||
muls[0] = _mm_madd_epi16(tblvec1_rev[7], invec[i]);
|
||||
|
||||
acc0 = _mm_add_epi32(acc0, _mm_hadd_epi32(_mm_hadd_epi32(muls[0], muls[1]), _mm_hadd_epi32(muls[2], muls[3])));
|
||||
acc1 = _mm_add_epi32(acc1, _mm_hadd_epi32(_mm_hadd_epi32(muls[4], muls[5]), _mm_hadd_epi32(muls[6], muls[7])));
|
||||
|
||||
acc0 = _mm_srai_epi32(acc0, 11);
|
||||
acc1 = _mm_srai_epi32(acc1, 11);
|
||||
|
||||
result = _mm_packs_epi32(acc0, acc1);
|
||||
_mm_storeu_si128((__m128i *)out, result);
|
||||
out += 8;
|
||||
|
||||
prev_interleaved = _mm_shuffle_epi32(result, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
}
|
||||
#elif HAS_NEON
|
||||
int8x8_t inv = vld1_s8((int8_t *)in);
|
||||
int16x8_t tblvec[2] = {vld1q_s16(tbl[0]), vld1q_s16(tbl[1])};
|
||||
int16x8_t invec[2] = {vreinterpretq_s16_s8(vcombine_s8(vtbl1_s8(inv, vget_low_s8(pos0)),
|
||||
vtbl1_s8(inv, vget_high_s8(pos0)))),
|
||||
vreinterpretq_s16_s8(vcombine_s8(vtbl1_s8(inv, vget_low_s8(pos1)),
|
||||
vtbl1_s8(inv, vget_high_s8(pos1))))};
|
||||
int16x8_t shiftcount = vdupq_n_s16(shift - 12); // negative means right shift
|
||||
int16x8_t tblvec1[8];
|
||||
|
||||
in += 8;
|
||||
tblvec1[0] = vextq_s16(table_prefix, tblvec[1], 7);
|
||||
invec[0] = vmulq_s16(invec[0], mult);
|
||||
tblvec1[1] = vextq_s16(table_prefix, tblvec[1], 6);
|
||||
invec[1] = vmulq_s16(invec[1], mult);
|
||||
tblvec1[2] = vextq_s16(table_prefix, tblvec[1], 5);
|
||||
tblvec1[3] = vextq_s16(table_prefix, tblvec[1], 4);
|
||||
invec[0] = vandq_s16(invec[0], mask);
|
||||
tblvec1[4] = vextq_s16(table_prefix, tblvec[1], 3);
|
||||
invec[1] = vandq_s16(invec[1], mask);
|
||||
tblvec1[5] = vextq_s16(table_prefix, tblvec[1], 2);
|
||||
tblvec1[6] = vextq_s16(table_prefix, tblvec[1], 1);
|
||||
invec[0] = vqshlq_s16(invec[0], shiftcount);
|
||||
invec[1] = vqshlq_s16(invec[1], shiftcount);
|
||||
tblvec1[7] = table_prefix;
|
||||
for (i = 0; i < 2; i++) {
|
||||
int32x4_t acc0;
|
||||
int32x4_t acc1;
|
||||
|
||||
acc1 = vmull_lane_s16(vget_high_s16(tblvec[0]), vget_high_s16(result), 2);
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec[1]), vget_high_s16(result), 3);
|
||||
acc0 = vmull_lane_s16(vget_low_s16(tblvec[0]), vget_high_s16(result), 2);
|
||||
acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec[1]), vget_high_s16(result), 3);
|
||||
|
||||
acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec1[0]), vget_low_s16(invec[i]), 0);
|
||||
acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec1[1]), vget_low_s16(invec[i]), 1);
|
||||
acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec1[2]), vget_low_s16(invec[i]), 2);
|
||||
acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec1[3]), vget_low_s16(invec[i]), 3);
|
||||
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[0]), vget_low_s16(invec[i]), 0);
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[1]), vget_low_s16(invec[i]), 1);
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[2]), vget_low_s16(invec[i]), 2);
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[3]), vget_low_s16(invec[i]), 3);
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[4]), vget_high_s16(invec[i]), 0);
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[5]), vget_high_s16(invec[i]), 1);
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[6]), vget_high_s16(invec[i]), 2);
|
||||
acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[7]), vget_high_s16(invec[i]), 3);
|
||||
|
||||
result = vcombine_s16(vqshrn_n_s32(acc0, 11), vqshrn_n_s32(acc1, 11));
|
||||
vst1q_s16(out, result);
|
||||
out += 8;
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < 2; i++) {
|
||||
int16_t ins[8];
|
||||
int16_t prev1 = out[-1];
|
||||
int16_t prev2 = out[-2];
|
||||
int j, k;
|
||||
for (j = 0; j < 4; j++) {
|
||||
ins[j * 2] = (((*in >> 4) << 28) >> 28) << shift;
|
||||
ins[j * 2 + 1] = (((*in++ & 0xf) << 28) >> 28) << shift;
|
||||
}
|
||||
for (j = 0; j < 8; j++) {
|
||||
int32_t acc = tbl[0][j] * prev2 + tbl[1][j] * prev1 + (ins[j] << 11);
|
||||
for (k = 0; k < j; k++) {
|
||||
acc += tbl[1][((j - k) - 1)] * ins[k];
|
||||
}
|
||||
acc >>= 11;
|
||||
*out++ = clamp16(acc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
nbytes -= 16 * sizeof(int16_t);
|
||||
}
|
||||
memcpy(state, out - 16, 16 * sizeof(int16_t));
|
||||
}
|
||||
|
||||
void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state, uint16_t inofs, uint8_t outflag) {
|
||||
int16_t tmp[16];
|
||||
int16_t *in_initial = BUF_S16(inofs);
|
||||
int16_t *in = in_initial;
|
||||
int16_t *out = BUF_S16(OFS_BASE + ((outflag & 3) != 0) * NUM_BYTES);
|
||||
int nbytes = ROUND_UP_16(NUM_BYTES);
|
||||
uint32_t pitch_accumulator;
|
||||
int i;
|
||||
#if !HAS_SSE41 && !HAS_NEON
|
||||
int16_t *tbl;
|
||||
int32_t sample;
|
||||
#endif
|
||||
if (flags & A_INIT) {
|
||||
memset(tmp, 0, 5 * sizeof(int16_t));
|
||||
} else {
|
||||
memcpy(tmp, state, 16 * sizeof(int16_t));
|
||||
}
|
||||
if (flags & 2) {
|
||||
memcpy(in - 8, tmp + 8, 8 * sizeof(int16_t));
|
||||
in -= tmp[5] / sizeof(int16_t);
|
||||
}
|
||||
in -= 4;
|
||||
pitch_accumulator = (uint16_t)tmp[4];
|
||||
memcpy(in, tmp, 4 * sizeof(int16_t));
|
||||
|
||||
#if HAS_SSE41
|
||||
__m128i multiples = _mm_setr_epi16(0, 2, 4, 6, 8, 10, 12, 14);
|
||||
__m128i pitchvec = _mm_set1_epi16((int16_t)pitch);
|
||||
__m128i pitchvec_8_steps = _mm_set1_epi32((pitch << 1) * 8);
|
||||
__m128i pitchacclo_vec = _mm_set1_epi32((uint16_t)pitch_accumulator);
|
||||
__m128i pl = _mm_mullo_epi16(multiples, pitchvec);
|
||||
__m128i ph = _mm_mulhi_epu16(multiples, pitchvec);
|
||||
__m128i acc_a = _mm_add_epi32(_mm_unpacklo_epi16(pl, ph), pitchacclo_vec);
|
||||
__m128i acc_b = _mm_add_epi32(_mm_unpackhi_epi16(pl, ph), pitchacclo_vec);
|
||||
|
||||
do {
|
||||
__m128i tbl_positions = _mm_srli_epi16(_mm_packus_epi32(
|
||||
_mm_and_si128(acc_a, _mm_set1_epi32(0xffff)),
|
||||
_mm_and_si128(acc_b, _mm_set1_epi32(0xffff))), 10);
|
||||
|
||||
__m128i in_positions = _mm_packus_epi32(_mm_srli_epi32(acc_a, 16), _mm_srli_epi32(acc_b, 16));
|
||||
__m128i tbl_entries[4];
|
||||
__m128i samples[4];
|
||||
|
||||
/*for (i = 0; i < 4; i++) {
|
||||
tbl_entries[i] = _mm_castpd_si128(_mm_loadh_pd(_mm_load_sd(
|
||||
(const double *)resample_table[_mm_extract_epi16(tbl_positions, 2 * i)]),
|
||||
(const double *)resample_table[_mm_extract_epi16(tbl_positions, 2 * i + 1)]));
|
||||
samples[i] = _mm_castpd_si128(_mm_loadh_pd(_mm_load_sd(
|
||||
(const double *)&in[_mm_extract_epi16(in_positions, 2 * i)]),
|
||||
(const double *)&in[_mm_extract_epi16(in_positions, 2 * i + 1)]));
|
||||
samples[i] = _mm_mulhrs_epi16(samples[i], tbl_entries[i]);
|
||||
}*/
|
||||
tbl_entries[0] = LOADLH(resample_table[_mm_extract_epi16(tbl_positions, 0)], resample_table[_mm_extract_epi16(tbl_positions, 1)]);
|
||||
tbl_entries[1] = LOADLH(resample_table[_mm_extract_epi16(tbl_positions, 2)], resample_table[_mm_extract_epi16(tbl_positions, 3)]);
|
||||
tbl_entries[2] = LOADLH(resample_table[_mm_extract_epi16(tbl_positions, 4)], resample_table[_mm_extract_epi16(tbl_positions, 5)]);
|
||||
tbl_entries[3] = LOADLH(resample_table[_mm_extract_epi16(tbl_positions, 6)], resample_table[_mm_extract_epi16(tbl_positions, 7)]);
|
||||
samples[0] = LOADLH(&in[_mm_extract_epi16(in_positions, 0)], &in[_mm_extract_epi16(in_positions, 1)]);
|
||||
samples[1] = LOADLH(&in[_mm_extract_epi16(in_positions, 2)], &in[_mm_extract_epi16(in_positions, 3)]);
|
||||
samples[2] = LOADLH(&in[_mm_extract_epi16(in_positions, 4)], &in[_mm_extract_epi16(in_positions, 5)]);
|
||||
samples[3] = LOADLH(&in[_mm_extract_epi16(in_positions, 6)], &in[_mm_extract_epi16(in_positions, 7)]);
|
||||
samples[0] = _mm_mulhrs_epi16(samples[0], tbl_entries[0]);
|
||||
samples[1] = _mm_mulhrs_epi16(samples[1], tbl_entries[1]);
|
||||
samples[2] = _mm_mulhrs_epi16(samples[2], tbl_entries[2]);
|
||||
samples[3] = _mm_mulhrs_epi16(samples[3], tbl_entries[3]);
|
||||
|
||||
_mm_storeu_si128((__m128i *)out, _mm_hadds_epi16(_mm_hadds_epi16(samples[0], samples[1]), _mm_hadds_epi16(samples[2], samples[3])));
|
||||
|
||||
acc_a = _mm_add_epi32(acc_a, pitchvec_8_steps);
|
||||
acc_b = _mm_add_epi32(acc_b, pitchvec_8_steps);
|
||||
out += 8;
|
||||
nbytes -= 8 * sizeof(int16_t);
|
||||
} while (nbytes > 0);
|
||||
in += (uint16_t)_mm_extract_epi16(acc_a, 1);
|
||||
pitch_accumulator = (uint16_t)_mm_extract_epi16(acc_a, 0);
|
||||
#elif HAS_NEON
|
||||
static const uint16_t multiples_data[8] = {0, 2, 4, 6, 8, 10, 12, 14};
|
||||
uint16x8_t multiples = vld1q_u16(multiples_data);
|
||||
uint32x4_t pitchvec_8_steps = vdupq_n_u32((pitch << 1) * 8);
|
||||
uint32x4_t pitchacclo_vec = vdupq_n_u32((uint16_t)pitch_accumulator);
|
||||
uint32x4_t acc_a = vmlal_n_u16(pitchacclo_vec, vget_low_u16(multiples), pitch);
|
||||
uint32x4_t acc_b = vmlal_n_u16(pitchacclo_vec, vget_high_u16(multiples), pitch);
|
||||
|
||||
do {
|
||||
uint16x8x2_t unzipped = vuzpq_u16(vreinterpretq_u16_u32(acc_a), vreinterpretq_u16_u32(acc_b));
|
||||
uint16x8_t tbl_positions = vshrq_n_u16(unzipped.val[0], 10);
|
||||
uint16x8_t in_positions = unzipped.val[1];
|
||||
int16x8_t tbl_entries[4];
|
||||
int16x8_t samples[4];
|
||||
int16x8x2_t unzipped1;
|
||||
int16x8x2_t unzipped2;
|
||||
|
||||
tbl_entries[0] = vcombine_s16(vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 0)]), vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 1)]));
|
||||
tbl_entries[1] = vcombine_s16(vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 2)]), vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 3)]));
|
||||
tbl_entries[2] = vcombine_s16(vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 4)]), vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 5)]));
|
||||
tbl_entries[3] = vcombine_s16(vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 6)]), vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 7)]));
|
||||
samples[0] = vcombine_s16(vld1_s16(&in[vgetq_lane_u16(in_positions, 0)]), vld1_s16(&in[vgetq_lane_u16(in_positions, 1)]));
|
||||
samples[1] = vcombine_s16(vld1_s16(&in[vgetq_lane_u16(in_positions, 2)]), vld1_s16(&in[vgetq_lane_u16(in_positions, 3)]));
|
||||
samples[2] = vcombine_s16(vld1_s16(&in[vgetq_lane_u16(in_positions, 4)]), vld1_s16(&in[vgetq_lane_u16(in_positions, 5)]));
|
||||
samples[3] = vcombine_s16(vld1_s16(&in[vgetq_lane_u16(in_positions, 6)]), vld1_s16(&in[vgetq_lane_u16(in_positions, 7)]));
|
||||
samples[0] = vqrdmulhq_s16(samples[0], tbl_entries[0]);
|
||||
samples[1] = vqrdmulhq_s16(samples[1], tbl_entries[1]);
|
||||
samples[2] = vqrdmulhq_s16(samples[2], tbl_entries[2]);
|
||||
samples[3] = vqrdmulhq_s16(samples[3], tbl_entries[3]);
|
||||
|
||||
unzipped1 = vuzpq_s16(samples[0], samples[1]);
|
||||
unzipped2 = vuzpq_s16(samples[2], samples[3]);
|
||||
samples[0] = vqaddq_s16(unzipped1.val[0], unzipped1.val[1]);
|
||||
samples[1] = vqaddq_s16(unzipped2.val[0], unzipped2.val[1]);
|
||||
unzipped1 = vuzpq_s16(samples[0], samples[1]);
|
||||
samples[0] = vqaddq_s16(unzipped1.val[0], unzipped1.val[1]);
|
||||
|
||||
vst1q_s16(out, samples[0]);
|
||||
|
||||
acc_a = vaddq_u32(acc_a, pitchvec_8_steps);
|
||||
acc_b = vaddq_u32(acc_b, pitchvec_8_steps);
|
||||
out += 8;
|
||||
nbytes -= 8 * sizeof(int16_t);
|
||||
} while (nbytes > 0);
|
||||
in += vgetq_lane_u16(vreinterpretq_u16_u32(acc_a), 1);
|
||||
pitch_accumulator = vgetq_lane_u16(vreinterpretq_u16_u32(acc_a), 0);
|
||||
#else
|
||||
do {
|
||||
for (i = 0; i < 8; i++) {
|
||||
tbl = resample_table[pitch_accumulator * 64 >> 16];
|
||||
sample = ((in[0] * tbl[0] + 0x4000) >> 15) +
|
||||
((in[1] * tbl[1] + 0x4000) >> 15) +
|
||||
((in[2] * tbl[2] + 0x4000) >> 15) +
|
||||
((in[3] * tbl[3] + 0x4000) >> 15);
|
||||
*out++ = clamp16(sample);
|
||||
|
||||
pitch_accumulator += (pitch << 1);
|
||||
in += pitch_accumulator >> 16;
|
||||
pitch_accumulator %= 0x10000;
|
||||
}
|
||||
nbytes -= 8 * sizeof(int16_t);
|
||||
} while (nbytes > 0);
|
||||
#endif
|
||||
|
||||
state[4] = (int16_t)pitch_accumulator;
|
||||
memcpy(state, in, 4 * sizeof(int16_t));
|
||||
i = (in - in_initial + 4) & 7;
|
||||
in -= i;
|
||||
if (i != 0) {
|
||||
i = -8 - i;
|
||||
}
|
||||
state[5] = i;
|
||||
memcpy(state + 8, in, 8 * sizeof(int16_t));
|
||||
}
|
||||
|
||||
void aEnvMixerImpl(uint8_t flags, ENVMIX_STATE state, int16_t rvol) {
|
||||
int16_t *in = BUF_S16(OFS_BASE);
|
||||
int16_t *dry[2] = {BUF_S16(OFS_MAIN_L), BUF_S16(OFS_MAIN_R)};
|
||||
int16_t *wet[2] = {BUF_S16(OFS_AUX_L), BUF_S16(OFS_AUX_R)};
|
||||
int nsamples = NUM_SAMPLES;
|
||||
|
||||
struct {
|
||||
int32_t t[2];
|
||||
int32_t rate[2];
|
||||
int16_t tgt[2];
|
||||
int16_t voldry;
|
||||
int16_t volwet;
|
||||
} *savedstate = (void *)state;
|
||||
|
||||
rspa.vol[1] = rvol; // why the fuck is this here?
|
||||
|
||||
// naudio uses a linear envelope
|
||||
// TODO: sse/neon
|
||||
|
||||
int32_t t[2], tgt[2], rate[2];
|
||||
int16_t voldry, volwet;
|
||||
|
||||
if (flags & A_INIT) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
t[i] = rspa.vol[i] << 16;
|
||||
rate[i] = rspa.rate[i] >> 3;
|
||||
tgt[i] = rspa.target[i] << 16;
|
||||
}
|
||||
voldry = rspa.vol_dry;
|
||||
volwet = rspa.vol_wet;
|
||||
} else {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
t[i] = savedstate->t[i];
|
||||
rate[i] = savedstate->rate[i];
|
||||
tgt[i] = savedstate->tgt[i] << 16;
|
||||
}
|
||||
voldry = savedstate->voldry;
|
||||
volwet = savedstate->volwet;
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_BIG_ENDIAN
|
||||
#define XOR 0
|
||||
#else
|
||||
#define XOR 1
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < nsamples; ++i) {
|
||||
int16_t gain[4];
|
||||
int16_t vol[2];
|
||||
int16_t *outptr[4];
|
||||
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
t[j] += rate[j];
|
||||
if ((rate[j] <= 0 && t[j] <= tgt[j]) || (rate[j] > 0 && t[j] >= tgt[j])) {
|
||||
t[j] = tgt[j];
|
||||
rate[j] = 0;
|
||||
}
|
||||
vol[j] = t[j] >> 16;
|
||||
}
|
||||
|
||||
outptr[0] = dry[0] + (i^XOR);
|
||||
outptr[1] = dry[1] + (i^XOR);
|
||||
outptr[2] = wet[0] + (i^XOR);
|
||||
outptr[3] = wet[1] + (i^XOR);
|
||||
|
||||
gain[0] = clamp16((vol[0] * voldry + 0x4000) >> 15);
|
||||
gain[1] = clamp16((vol[1] * voldry + 0x4000) >> 15);
|
||||
gain[2] = clamp16((vol[0] * volwet + 0x4000) >> 15);
|
||||
gain[3] = clamp16((vol[1] * volwet + 0x4000) >> 15);
|
||||
|
||||
const int16_t insamp = in[i^XOR];
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
*outptr[j] = clamp16(*outptr[j] + ((insamp * gain[j]) >> 15));
|
||||
}
|
||||
}
|
||||
|
||||
#undef XOR
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
savedstate->t[i] = t[i];
|
||||
savedstate->rate[i] = rate[i];
|
||||
savedstate->tgt[i] = tgt[i] >> 16;
|
||||
}
|
||||
savedstate->voldry = voldry;
|
||||
savedstate->volwet = volwet;
|
||||
}
|
||||
|
||||
// flags is always 0 in PD
|
||||
void aMixImpl(uint8_t flags, int16_t gain, uint16_t in_addr, uint16_t out_addr) {
|
||||
int nbytes = ROUND_UP_16(NUM_BYTES); // can't round up to 32 here because 0x170 ain't gonna do that
|
||||
int16_t *in = BUF_S16(in_addr);
|
||||
int16_t *out = BUF_S16(out_addr);
|
||||
#if HAS_SSE41
|
||||
__m128i gain_vec = _mm_set1_epi16(gain);
|
||||
#elif !HAS_NEON
|
||||
int i;
|
||||
int32_t sample;
|
||||
#endif
|
||||
|
||||
#if !HAS_NEON
|
||||
if (gain == -0x8000) {
|
||||
while (nbytes > 0) {
|
||||
#if HAS_SSE41
|
||||
__m128i out1, in1;
|
||||
out1 = _mm_loadu_si128((const __m128i *)out);
|
||||
in1 = _mm_loadu_si128((const __m128i *)in);
|
||||
|
||||
out1 = _mm_subs_epi16(out1, in1);
|
||||
|
||||
_mm_storeu_si128((__m128i *)out, out1);
|
||||
|
||||
out += 8;
|
||||
in += 8;
|
||||
#else
|
||||
for (i = 0; i < 8; i++) {
|
||||
sample = *out - *in++;
|
||||
*out++ = clamp16(sample);
|
||||
}
|
||||
#endif
|
||||
|
||||
nbytes -= 8 * sizeof(int16_t);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
while (nbytes > 0) {
|
||||
#if HAS_SSE41
|
||||
__m128i out1, in1;
|
||||
out1 = _mm_loadu_si128((const __m128i *)out);
|
||||
in1 = _mm_loadu_si128((const __m128i *)in);
|
||||
|
||||
out1 = _mm_adds_epi16(out1, _mm_mulhrs_epi16(in1, gain_vec));
|
||||
|
||||
_mm_storeu_si128((__m128i *)out, out1);
|
||||
|
||||
out += 8;
|
||||
in += 8;
|
||||
#elif HAS_NEON
|
||||
int16x8_t out1, in1;
|
||||
out1 = vld1q_s16(out);
|
||||
in1 = vld1q_s16(in);
|
||||
|
||||
out1 = vqaddq_s16(out1, vqrdmulhq_n_s16(in1, gain));
|
||||
|
||||
vst1q_s16(out, out1);
|
||||
|
||||
out += 8;
|
||||
in += 8;
|
||||
#else
|
||||
for (i = 0; i < 8; i++) {
|
||||
sample = ((*out * 0x7fff + *in++ * gain) + 0x4000) >> 15;
|
||||
*out++ = clamp16(sample);
|
||||
}
|
||||
#endif
|
||||
|
||||
nbytes -= 8 * sizeof(int16_t);
|
||||
}
|
||||
}
|
||||
|
||||
void aSetVolumeImpl(uint8_t flags, int16_t v, int16_t t, int16_t r) {
|
||||
// the flags are not really very intuitive here
|
||||
if (flags & A_VOL) {
|
||||
if (flags & A_LEFT) {
|
||||
rspa.vol[0] = v;
|
||||
rspa.vol_dry = t;
|
||||
rspa.vol_wet = r;
|
||||
} else {
|
||||
rspa.target[1] = v;
|
||||
rspa.rate[1] = (int32_t)((uint16_t)t << 16 | ((uint16_t)r));
|
||||
}
|
||||
} else /* A_RATE */ {
|
||||
rspa.target[0] = v;
|
||||
rspa.rate[0] = (int32_t)((uint16_t)t << 16 | ((uint16_t)r));
|
||||
}
|
||||
}
|
||||
|
||||
void aPlayMP3Impl(const void *mp3file, u32 mp3size, void *out, int reset) {
|
||||
static mp3dec_t mp3d;
|
||||
static const u8 *curdata = NULL; // pointer to the mp3 we're currently processing
|
||||
static s32 dataptr = 0; // byte index into curdata
|
||||
|
||||
if (mp3file != curdata || reset) {
|
||||
// new mp3, reinit decoder
|
||||
mp3dec_init(&mp3d);
|
||||
curdata = mp3file;
|
||||
dataptr = 0;
|
||||
}
|
||||
|
||||
// this command is supposed to write one full frame to out
|
||||
// but which frame? we'll just decode sequentially, it'll probably work
|
||||
if (dataptr < mp3size) {
|
||||
// FIXME: decoding straight to out might bite us in the ass because it's only 1160 bytes
|
||||
mp3dec_frame_info_t info;
|
||||
const s32 samples = mp3dec_decode_frame(&mp3d, curdata + dataptr, mp3size - dataptr, out, &info);
|
||||
// fill in the rest of the buffer if frame is smaller
|
||||
const s32 diff = 580 - samples;
|
||||
if (diff > 0) {
|
||||
memset((s16 *)out + samples, 0, diff * 2);
|
||||
} else {
|
||||
assert(diff == 0);
|
||||
}
|
||||
dataptr += info.frame_bytes;
|
||||
} else {
|
||||
// empty frame
|
||||
memset(out, 0, 580 * 2);
|
||||
}
|
||||
}
|
||||
|
||||
void aPoleFilterImpl(uint8_t flags, int16_t gain, uint32_t t, uint32_t addr) {
|
||||
// this never gets called?
|
||||
}
|
||||
|
||||
void aDisableImpl(uint16_t outp, uint32_t b, uint32_t c) {
|
||||
// this never gets called?
|
||||
}
|
||||
+533
@@ -0,0 +1,533 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
#include "fs.h"
|
||||
#include "utils.h"
|
||||
#include "romdata.h"
|
||||
#include "mod.h"
|
||||
#include "data.h"
|
||||
#include "game/stagetable.h"
|
||||
|
||||
#define MOD_TEXTURES_DIR "textures"
|
||||
#define MOD_ANIMATIONS_DIR "animations"
|
||||
#define MOD_SEQUENCES_DIR "sequences"
|
||||
|
||||
extern struct stagemusic g_StageTracks[];
|
||||
extern struct stageallocation g_StageAllocations8Mb[];
|
||||
|
||||
#define PARSE_STAGE_FLOAT(sec, name, v, min, max) \
|
||||
p = modConfigParseFloatValue(p, token, &v); \
|
||||
if (!p || v < (min) || v > (max)) { \
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: " sec " invalid " name " value: %s", stagenum, token); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define PARSE_STAGE_INT(sec, name, v, min, max) \
|
||||
p = modConfigParseIntValue(p, token, &v); \
|
||||
if (!p || v < (min) || v > (max)) { \
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: " sec " invalid " name " value: %s", stagenum, token); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define PARSE_STAGE_FILENAME(sec, name, v) \
|
||||
p = modConfigParseFileValue(p, token, &v); \
|
||||
if (!p) { \
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: " sec " invalid " name " value: %s", stagenum, token); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define PARSE_STAGE_STRING(sec, name, v) \
|
||||
p = strParseToken(p, token, NULL); \
|
||||
if (!p) { \
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: " sec " invalid " name " value: %s", stagenum, token); \
|
||||
return NULL; \
|
||||
} \
|
||||
v = strUnquote(token);
|
||||
|
||||
#define PARSE_INT(sec, name, v, min, max, ret) \
|
||||
p = modConfigParseIntValue(p, token, &v); \
|
||||
if (!p || v < (min) || v > (max)) { \
|
||||
sysLogPrintf(LOG_ERROR, "mod: %s: invalid " name " value: %s", sec, token); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
static inline char *modConfigParseFileValue(char *p, char *token, s32 *filenum)
|
||||
{
|
||||
p = strParseToken(p, token, NULL);
|
||||
if (!token[0]) {
|
||||
return NULL; // empty
|
||||
}
|
||||
// check if it is a number already
|
||||
s32 num = strtol(token, NULL, 0);
|
||||
if (num > 0 && romdataFileGetName(num)) {
|
||||
*filenum = num;
|
||||
return p;
|
||||
}
|
||||
// it's a filename
|
||||
num = romdataFileGetNumForName(strUnquote(token));
|
||||
if (num >= 0) {
|
||||
*filenum = num;
|
||||
return p;
|
||||
}
|
||||
// the filename was invalid
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline char *modConfigParseIntValue(char *p, char *token, s32 *out)
|
||||
{
|
||||
p = strParseToken(p, token, NULL);
|
||||
if (!token[0]) {
|
||||
return NULL; // empty
|
||||
}
|
||||
char *endp = token;
|
||||
const s32 num = strtol(token, &endp, 0);
|
||||
if (num == 0 && (endp == token || *endp != '\0')) {
|
||||
return NULL;
|
||||
}
|
||||
*out = num;
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline char *modConfigParseFloatValue(char *p, char *token, f32 *out)
|
||||
{
|
||||
p = strParseToken(p, token, NULL);
|
||||
if (!token[0]) {
|
||||
return NULL; // empty
|
||||
}
|
||||
char *endp = token;
|
||||
const f32 num = strtof(token, &endp);
|
||||
if (num == 0.f && (endp == token || *endp != '\0')) {
|
||||
return NULL;
|
||||
}
|
||||
*out = num;
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *modConfigParseStageMusic(char *p, char *token, s32 stagenum)
|
||||
{
|
||||
struct stagemusic *smus = NULL;
|
||||
for (struct stagemusic *p = g_StageTracks; p->stagenum; ++p) {
|
||||
if (p->stagenum == stagenum) {
|
||||
smus = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!smus) {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: music can't be changed for this stage", stagenum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// eat opening bracket
|
||||
p = strParseToken(p, token, NULL);
|
||||
if (token[0] != '{' || token[1] != '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// parse keyvalues until } is reached
|
||||
s32 tmp = 0;
|
||||
p = strParseToken(p, token, NULL);
|
||||
while (p && token[0] && strcmp(token, "}") != 0) {
|
||||
if (!strcmp(token, "primarytrack")) {
|
||||
PARSE_STAGE_INT("music:", "primarytrack", tmp, 0, 128);
|
||||
smus->primarytrack = tmp;
|
||||
} else if (!strcmp(token, "ambienttrack")) {
|
||||
PARSE_STAGE_INT("music:", "ambienttrack", tmp, 0, 128);
|
||||
smus->ambienttrack = tmp;
|
||||
} else if (!strcmp(token, "xtrack")) {
|
||||
PARSE_STAGE_INT("music:", "xtrack", tmp, 0, 128);
|
||||
smus->xtrack = tmp;
|
||||
} else {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: music: invalid key: %s", stagenum, token);
|
||||
return NULL;
|
||||
}
|
||||
p = strParseToken(p, token, NULL);
|
||||
}
|
||||
|
||||
if (token[0] != '}') {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: unterminated music block", stagenum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *modConfigParseStageWeatherRooms(char *p, char *token, s32 stagenum, struct weathercfg *wcfg)
|
||||
{
|
||||
// determine where we can start adding rooms
|
||||
s32 idx;
|
||||
for (idx = 0; idx < WEATHERCFG_MAX_SKIPROOMS && wcfg->skiprooms[idx]; ++idx);
|
||||
|
||||
// eat opening bracket
|
||||
p = strParseToken(p, token, NULL);
|
||||
if (token[0] != '{' || token[1] != '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check if user wants to clear the whole list
|
||||
p = strParseToken(p, token, NULL);
|
||||
if (!strcmp(token, "clear")) {
|
||||
memset(wcfg->skiprooms, 0, sizeof(wcfg->skiprooms));
|
||||
idx = 0;
|
||||
p = strParseToken(p, token, NULL);
|
||||
}
|
||||
|
||||
s32 tmp = 0;
|
||||
while (p && token[0] && strcmp(token, "}") != 0) {
|
||||
if (token[0] == ',' && !token[1]) {
|
||||
p = strParseToken(p, token, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp = strtol(token, NULL, 0);
|
||||
if (tmp <= 0 || tmp > 32767) {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: weather: rooms: invalid room %s", stagenum, token);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (idx < WEATHERCFG_MAX_SKIPROOMS) {
|
||||
wcfg->skiprooms[idx++] = tmp;
|
||||
}
|
||||
|
||||
p = strParseToken(p, token, NULL);
|
||||
}
|
||||
|
||||
if (token[0] != '}') {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: weather: unterminated rooms block", stagenum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *modConfigParseStageWeather(char *p, char *token, s32 stagenum)
|
||||
{
|
||||
s32 wi;
|
||||
struct weathercfg *wcfg = NULL;
|
||||
for (wi = 0; wi < ARRAYCOUNT(g_WeatherConfig) && g_WeatherConfig[wi].stagenum; ++wi) {
|
||||
if (g_WeatherConfig[wi].stagenum == stagenum) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wi >= WEATHERCFG_MAX_STAGES) {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: no more space for weather config", stagenum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wcfg = &g_WeatherConfig[wi];
|
||||
|
||||
if (!wcfg->stagenum) {
|
||||
// new weather config; initialize with defaults
|
||||
*wcfg = g_DefaultWeatherConfig;
|
||||
wcfg->stagenum = stagenum;
|
||||
} else {
|
||||
// flags have to be re-specified
|
||||
wcfg->flags = 0;
|
||||
}
|
||||
|
||||
// eat opening bracket
|
||||
p = strParseToken(p, token, NULL);
|
||||
if (token[0] != '{' || token[1] != '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// parse keyvalues until } is reached
|
||||
s32 tmpi = 0;
|
||||
f32 tmpf = 0.f;
|
||||
p = strParseToken(p, token, NULL);
|
||||
while (p && token[0] && strcmp(token, "}") != 0) {
|
||||
if (!strcmp(token, "include_rooms") || !strcmp(token, "exclude_rooms")) {
|
||||
// include_rooms | exclude_rooms { ROOM_NUMBERS... }
|
||||
const s32 include = (token[0] == 'i');
|
||||
p = modConfigParseStageWeatherRooms(p, token, stagenum, wcfg);
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
if (wcfg->skiprooms[0] && include) {
|
||||
wcfg->flags |= WEATHERFLAG_INCLUDE;
|
||||
}
|
||||
} else if (!strcmp(token, "cutscene_only")) {
|
||||
wcfg->flags |= WEATHERFLAG_CUTSCENE_ONLY;
|
||||
} else if (!strcmp(token, "constant_wind")) {
|
||||
PARSE_STAGE_FLOAT("weather:", "constant_wind (0)", wcfg->windanglerad, -M_TAU, M_TAU);
|
||||
PARSE_STAGE_FLOAT("weather:", "constant_wind (1)", wcfg->windspeedx, -1024.f, 1024.f);
|
||||
PARSE_STAGE_FLOAT("weather:", "constant_wind (2)", wcfg->windspeedz, -1024.f, 1024.f);
|
||||
wcfg->flags |= WEATHERFLAG_FORCE_WINDDIR;
|
||||
} else if (!strcmp(token, "windspeed")) {
|
||||
PARSE_STAGE_FLOAT("weather:", "windspeed", tmpf, -1024.f, 1024.f);
|
||||
wcfg->windspeed = tmpf;
|
||||
} else if (!strcmp(token, "ymin")) {
|
||||
PARSE_STAGE_FLOAT("weather:", "ymin", tmpf, -65536.f, 65536.f);
|
||||
wcfg->ymin = tmpf;
|
||||
} else if (!strcmp(token, "ymax")) {
|
||||
PARSE_STAGE_FLOAT("weather:", "ymax", tmpf, -65536.f, 65536.f);
|
||||
wcfg->ymax = tmpf;
|
||||
} else if (!strcmp(token, "zmax")) {
|
||||
PARSE_STAGE_FLOAT("weather:", "zmax", tmpf, -65536.f, 65536.f);
|
||||
wcfg->zmax = tmpf;
|
||||
} else {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: weather: invalid key: %s", stagenum, token);
|
||||
return NULL;
|
||||
}
|
||||
p = strParseToken(p, token, NULL);
|
||||
}
|
||||
|
||||
if (token[0] != '}') {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: unterminated weather block", stagenum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *modConfigParseStage(char *p, char *token)
|
||||
{
|
||||
// stage number
|
||||
p = strParseToken(p, token, NULL);
|
||||
const s32 stagenum = strtol(token, NULL, 0);
|
||||
if (stagenum <= 0x01 || stagenum > 0x50) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// eat opening bracket
|
||||
p = strParseToken(p, token, NULL);
|
||||
if (token[0] != '{' || token[1] != '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// find the stage table pointers this corresponds to
|
||||
struct stagetableentry *stab = NULL;
|
||||
struct stageallocation *salloc = NULL;
|
||||
const s32 sidx = stageGetIndex(stagenum);
|
||||
if (sidx >= 0) {
|
||||
stab = &g_Stages[sidx];
|
||||
} else {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: unknown stage number", stagenum);
|
||||
return NULL;
|
||||
}
|
||||
for (struct stageallocation *p = g_StageAllocations8Mb; p->stagenum; ++p) {
|
||||
if (p->stagenum == stagenum) {
|
||||
salloc = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// parse keyvalues until } is reached
|
||||
s32 tmp = 0;
|
||||
char *tmps = NULL;
|
||||
p = strParseToken(p, token, NULL);
|
||||
while (p && token[0] && strcmp(token, "}") != 0) {
|
||||
if (!strcmp(token, "bgfile")) {
|
||||
// bg FILE_NAME_OR_NUM
|
||||
PARSE_STAGE_FILENAME("", "bgfile", tmp);
|
||||
stab->bgfileid = tmp;
|
||||
} else if (!strcmp(token, "tilesfile")) {
|
||||
// tilesfile FILE_NAME_OR_NUM
|
||||
PARSE_STAGE_FILENAME("", "tilesfile", tmp);
|
||||
stab->tilefileid = tmp;
|
||||
} else if (!strcmp(token, "padsfile")) {
|
||||
// padsfile FILE_NAME_OR_NUM
|
||||
PARSE_STAGE_FILENAME("", "padsfile", tmp);
|
||||
stab->padsfileid = tmp;
|
||||
} else if (!strcmp(token, "setupfile")) {
|
||||
// setupfile FILE_NAME_OR_NUM
|
||||
PARSE_STAGE_FILENAME("", "setupfile", tmp);
|
||||
stab->setupfileid = tmp;
|
||||
} else if (!strcmp(token, "mpsetupfile")) {
|
||||
// mpsetupfile FILE_NAME_OR_NUM
|
||||
PARSE_STAGE_FILENAME("", "mpsetupfile", tmp);
|
||||
stab->mpsetupfileid = tmp;
|
||||
} else if (!strcmp(token, "alarm")) {
|
||||
PARSE_STAGE_INT("", "alarm", tmp, 1, 0xFFFF);
|
||||
stab->alarm = tmp;
|
||||
} else if (!strcmp(token, "extragunmem")) {
|
||||
PARSE_STAGE_INT("", "extragunmem", tmp, 0, 0xFFFF);
|
||||
stab->extragunmem = tmp;
|
||||
} else if (!strcmp(token, "allocation")) {
|
||||
// allocation "ALLOCSTRING"
|
||||
PARSE_STAGE_STRING("", "allocation", tmps);
|
||||
// FIXME: this leaks
|
||||
tmps = strDuplicate(tmps);
|
||||
if (tmps) {
|
||||
salloc->string = tmps;
|
||||
}
|
||||
} else if (!strcmp(token, "music")) {
|
||||
// music { KEYVALUES... }
|
||||
p = modConfigParseStageMusic(p, token, stagenum);
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
} else if (!strcmp(token, "weather")) {
|
||||
// weather { KEYVALUES... }
|
||||
p = modConfigParseStageWeather(p, token, stagenum);
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: stage 0x%02x: invalid key: %s", stagenum, token);
|
||||
return NULL;
|
||||
}
|
||||
p = strParseToken(p, token, NULL);
|
||||
}
|
||||
|
||||
if (token[0] != '}') {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: unterminated stage 0x%02x block", stagenum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
s32 modConfigLoad(const char *fname)
|
||||
{
|
||||
u32 dataLen = 0;
|
||||
char *data = fsFileLoad(fname, &dataLen);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s32 success = true;
|
||||
char token[UTIL_MAX_TOKEN + 1] = { 0 };
|
||||
char *end = data + dataLen;
|
||||
char *p = strParseToken(data, token, NULL);
|
||||
while (p && token[0]) {
|
||||
if (!strcmp(token, "stage")) {
|
||||
// stage NUMBER { KEYVALUES... }
|
||||
char *prev = p;
|
||||
p = modConfigParseStage(p, token);
|
||||
if (!p) {
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: malformed stage block at offset %d", prev - data);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// garbage
|
||||
sysLogPrintf(LOG_ERROR, "modconfig: unexpected %s at offset %d", token[0] ? token : "end of file", p - data);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
p = strParseToken(p, token, NULL);
|
||||
}
|
||||
|
||||
sysMemFree(data);
|
||||
return success;
|
||||
}
|
||||
|
||||
s32 modTextureLoad(u16 num, void *dst, u32 dstSize)
|
||||
{
|
||||
static s32 dirExists = -1;
|
||||
if (dirExists < 0) {
|
||||
dirExists = (fsFileSize(MOD_TEXTURES_DIR) >= 0);
|
||||
}
|
||||
|
||||
if (!dirExists) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path[FS_MAXPATH + 1];
|
||||
snprintf(path, sizeof(path), MOD_TEXTURES_DIR "/%04x.bin", num);
|
||||
|
||||
const s32 ret = fsFileLoadTo(path, dst, dstSize);
|
||||
if (ret > 0) {
|
||||
sysLogPrintf(LOG_NOTE, "mod: loaded external texture %04x", num);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *modSequenceLoad(u16 num, u32 *outSize)
|
||||
{
|
||||
static s32 dirExists = -1;
|
||||
if (dirExists < 0) {
|
||||
dirExists = (fsFileSize(MOD_SEQUENCES_DIR) >= 0);
|
||||
}
|
||||
|
||||
if (!dirExists) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char path[FS_MAXPATH + 1];
|
||||
snprintf(path, sizeof(path), MOD_SEQUENCES_DIR "/%04x.bin", num);
|
||||
if (fsFileSize(path) > 0) {
|
||||
void *ret = fsFileLoad(path, outSize);
|
||||
if (ret) {
|
||||
sysLogPrintf(LOG_NOTE, "mod: loaded external sequence %04x", num);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *modAnimationLoadData(u16 num)
|
||||
{
|
||||
char path[FS_MAXPATH + 1];
|
||||
// load the animation data
|
||||
snprintf(path, sizeof(path), MOD_ANIMATIONS_DIR "/%04x.bin", num);
|
||||
void *data = fsFileLoad(path, NULL);
|
||||
if (!data) {
|
||||
sysFatalError("External animation %04x has no data file.\nEnsure that it is placed at %s or delete the descriptor.", num, path);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
s32 modAnimationLoadDescriptor(u16 num, struct animtableentry *anim)
|
||||
{
|
||||
static s32 dirExists = -1;
|
||||
if (dirExists < 0) {
|
||||
dirExists = (fsFileSize(MOD_ANIMATIONS_DIR) >= 0);
|
||||
}
|
||||
|
||||
if (!dirExists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char path[FS_MAXPATH + 1];
|
||||
|
||||
// load the descriptor, if any
|
||||
snprintf(path, sizeof(path), MOD_ANIMATIONS_DIR "/%04x.txt", num);
|
||||
if (fsFileSize(path) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *desc = fsFileLoad(path, NULL);
|
||||
if (!desc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse the descriptor
|
||||
char token[UTIL_MAX_TOKEN + 1] = { 0 };
|
||||
char *p = strParseToken(desc, token, NULL);
|
||||
s32 tmp = 0;
|
||||
while (p && token[0]) {
|
||||
if (!strcmp(token, "numframes")) {
|
||||
PARSE_INT(path, "numframes", tmp, 0, 0xFFFF, false);
|
||||
anim->numframes = tmp;
|
||||
} else if (!strcmp(token, "bytesperframe")) {
|
||||
PARSE_INT(path, "bytesperframe", tmp, 0, 0xFFFF, false);
|
||||
anim->bytesperframe = tmp;
|
||||
} else if (!strcmp(token, "headerlen")) {
|
||||
PARSE_INT(path, "headerlen", tmp, 0, 0xFFFF, false);
|
||||
anim->headerlen = tmp;
|
||||
} else if (!strcmp(token, "framelen")) {
|
||||
PARSE_INT(path, "framelen", tmp, 0, 0xFF, false);
|
||||
anim->framelen = tmp;
|
||||
} else if (!strcmp(token, "flags")) {
|
||||
PARSE_INT(path, "flags", tmp, 0, 0xFF, false);
|
||||
anim->flags = tmp;
|
||||
} else {
|
||||
sysLogPrintf(LOG_ERROR, "mod: %s: invalid key: %s", path, token);
|
||||
return false;
|
||||
}
|
||||
p = strParseToken(p, token, NULL);
|
||||
}
|
||||
|
||||
sysMemFree(desc);
|
||||
|
||||
sysLogPrintf(LOG_NOTE, "mod: loaded external animation %04x", num);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,896 @@
|
||||
#include "types.h"
|
||||
#include "constants.h"
|
||||
#include "bss.h"
|
||||
#include "game/pak.h"
|
||||
#include "game/filelist.h"
|
||||
#include "game/menu.h"
|
||||
#include "game/savebuffer.h"
|
||||
#include "game/mplayer/mplayer.h"
|
||||
#include <string.h>
|
||||
#include "fs.h"
|
||||
#include "system.h"
|
||||
#include "mpsetups.h"
|
||||
|
||||
/*
|
||||
MP Setup File Format
|
||||
# header
|
||||
[version{1}]
|
||||
[defaultsetup{1}]
|
||||
[numsetups{1}]
|
||||
# setups
|
||||
[setup_1{80}]
|
||||
...
|
||||
[setup_n{80}]
|
||||
*/
|
||||
|
||||
#define MPSETUP_VERSION 1
|
||||
|
||||
#define MPSETUP_EXPORTDIR "$S/exported/"
|
||||
#define MPSETUP_FILENAME "mpsetups"
|
||||
#define MPSETUP_FILENAME_EXP "mpsetups-ext"
|
||||
|
||||
#define MPSETUP_OP_DEFAULT 0
|
||||
#define MPSETUP_OP_IMPORT 1
|
||||
#define MPSETUP_OP_EXPORT 2
|
||||
|
||||
#define MPSETUP_IMPORT_OVERWRITE 0
|
||||
#define MPSETUP_IMPORT_ADD 1
|
||||
|
||||
#define MPSETUP_IMPORT_CONFLICT 0x1313
|
||||
|
||||
extern struct menudialogdef g_MpSaveSetupNameMenuDialog;
|
||||
|
||||
s16 g_MpCurrentSetup = -1;
|
||||
struct mpsetupfile g_MpSetupFile;
|
||||
|
||||
static struct mpsetupfile g_ImportMpSetupFile;
|
||||
static u64 g_MpImportExportFilter[2];
|
||||
|
||||
static char g_StatusText[128];
|
||||
static char g_TitleImportExportDialog[20];
|
||||
static char g_LabelSetDefault[20] = "Set Default\n";
|
||||
|
||||
/* extended mpsetup menu definitions */
|
||||
|
||||
static MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
static MenuItemHandlerResult menuhandlerDeleteSetup(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
static MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
static MenuItemHandlerResult menuhandlerImportAction(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
static MenuItemHandlerResult menuhandlerOpenImportExportDialog(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
static MenuItemHandlerResult menuhandlerSelectSetupHandler(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
static MenuItemHandlerResult menuhandlerSetupRename(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct menuitem *item, union handlerdata *data);
|
||||
|
||||
static struct menuitem g_StatusOkMenuItems[] = {
|
||||
{
|
||||
MENUITEMTYPE_LABEL,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING,
|
||||
(uintptr_t)g_StatusText,
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
0,
|
||||
MENUITEMFLAG_SELECTABLE_CLOSESDIALOG | MENUITEMFLAG_SELECTABLE_CENTRE,
|
||||
L_OPTIONS_347, // "OK"
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
};
|
||||
|
||||
static struct menudialogdef g_StatusOkDialog = {
|
||||
MENUDIALOGTYPE_SUCCESS,
|
||||
L_OPTIONS_345, // "Cool!"
|
||||
g_StatusOkMenuItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_DISABLEBANNER,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct menuitem g_StatusErrorMenuItems[] = {
|
||||
{
|
||||
MENUITEMTYPE_LABEL,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING,
|
||||
(uintptr_t)g_StatusText,
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
0,
|
||||
MENUITEMFLAG_SELECTABLE_CLOSESDIALOG | MENUITEMFLAG_SELECTABLE_CENTRE,
|
||||
L_OPTIONS_347, // "OK"
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
};
|
||||
|
||||
/* public */
|
||||
struct menudialogdef g_StatusErrorDialog = {
|
||||
MENUDIALOGTYPE_DANGER,
|
||||
L_OPTIONS_277, // "Failed"
|
||||
g_StatusErrorMenuItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_DISABLEBANNER,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct menuitem g_RenameSetupItems[] = {
|
||||
#if VERSION != VERSION_JPN_FINAL
|
||||
{
|
||||
MENUITEMTYPE_LABEL,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING,
|
||||
(uintptr_t)"Enter the setup name:\n",
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
MENUITEMTYPE_KEYBOARD,
|
||||
18,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
menuhandlerRenameSetup,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
};
|
||||
|
||||
static struct menudialogdef g_RenameSetupDialog = {
|
||||
MENUDIALOGTYPE_DEFAULT,
|
||||
(uintptr_t)"Setup Name:\n",
|
||||
g_RenameSetupItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_LITERAL_TEXT,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct menuitem g_DeleteSetupItems[] = {
|
||||
{
|
||||
MENUITEMTYPE_LABEL,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING,
|
||||
(uintptr_t)"Delete Setup?\n",
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
0,
|
||||
MENUITEMFLAG_SELECTABLE_CLOSESDIALOG | MENUITEMFLAG_SELECTABLE_CENTRE,
|
||||
L_OPTIONS_385, // "No"
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
0,
|
||||
MENUITEMFLAG_SELECTABLE_CENTRE,
|
||||
L_OPTIONS_386, // "Yes"
|
||||
0,
|
||||
menuhandlerDeleteSetup,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
};
|
||||
|
||||
static struct menudialogdef g_DeleteSetupDialog = {
|
||||
MENUDIALOGTYPE_DANGER,
|
||||
(uintptr_t)"Delete Setup\n",
|
||||
g_DeleteSetupItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_LITERAL_TEXT,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct menuitem g_ImportExportItems[] = {
|
||||
{
|
||||
MENUITEMTYPE_LIST,
|
||||
0,
|
||||
MENUITEMFLAG_LOCKABLEMINOR | MENUITEMFLAG_LABEL_CUSTOMCOLOUR,
|
||||
140,
|
||||
0x0000004d,
|
||||
menuhandlerImportOrExportSettings,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
};
|
||||
|
||||
static struct menudialogdef g_ImportExportDialog = {
|
||||
MENUDIALOGTYPE_DEFAULT,
|
||||
(uintptr_t) g_TitleImportExportDialog,
|
||||
g_ImportExportItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_LITERAL_TEXT,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct menuitem g_ManageImportExportItems[] = {
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
MPSETUP_OP_IMPORT,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)"Import Settings\n",
|
||||
0,
|
||||
menuhandlerOpenImportExportDialog,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
MPSETUP_OP_EXPORT,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)"Export Settings\n",
|
||||
0,
|
||||
menuhandlerOpenImportExportDialog,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
};
|
||||
|
||||
static struct menudialogdef g_ManageImportExportDialog = {
|
||||
MENUDIALOGTYPE_DEFAULT,
|
||||
(uintptr_t) "Import/Export\n",
|
||||
g_ManageImportExportItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_LITERAL_TEXT,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct menuitem g_MpManageSettingsListItems[] = {
|
||||
{
|
||||
MENUITEMTYPE_LIST,
|
||||
0,
|
||||
MENUITEMFLAG_LABEL_CUSTOMCOLOUR,
|
||||
160,
|
||||
0x00000042,
|
||||
menuhandlerSelectSetupHandler,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
|
||||
};
|
||||
|
||||
/* public */
|
||||
struct menudialogdef g_ManageSettingsDialog = {
|
||||
MENUDIALOGTYPE_DEFAULT,
|
||||
(uintptr_t) "Manage Settings\n",
|
||||
g_MpManageSettingsListItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_LITERAL_TEXT,
|
||||
&g_ManageImportExportDialog,
|
||||
};
|
||||
|
||||
static struct menuitem g_ManageSetupItems[] = {
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)"Rename\n",
|
||||
0,
|
||||
menuhandlerSetupRename,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)"Delete\n",
|
||||
0,
|
||||
menuhandlerSetupDelete,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)g_LabelSetDefault,
|
||||
0,
|
||||
menuhandlerSetupSetDefault,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SEPARATOR,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
0,
|
||||
MENUITEMFLAG_SELECTABLE_CLOSESDIALOG,
|
||||
L_OPTIONS_213, // "Back"
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
};
|
||||
|
||||
static struct menudialogdef g_ManageSetupDialog = {
|
||||
MENUDIALOGTYPE_DEFAULT,
|
||||
(uintptr_t)"Manage Setup",
|
||||
g_ManageSetupItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_LITERAL_TEXT,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct menuitem g_ImportOverrideItems[] = {
|
||||
{
|
||||
MENUITEMTYPE_LABEL,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_LESSLEFTPADDING,
|
||||
(uintptr_t) "How to resolve setups\nwith the same name?\n",
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
MPSETUP_IMPORT_ADD,
|
||||
MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SELECTABLE_CLOSESDIALOG,
|
||||
(uintptr_t) "Add\n",
|
||||
0,
|
||||
menuhandlerImportAction,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_SELECTABLE,
|
||||
MPSETUP_IMPORT_OVERWRITE,
|
||||
MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SELECTABLE_CLOSESDIALOG,
|
||||
(uintptr_t) "Overwrite\n",
|
||||
0,
|
||||
menuhandlerImportAction,
|
||||
},
|
||||
{ MENUITEMTYPE_END },
|
||||
};
|
||||
|
||||
static struct menudialogdef g_ImportOverrideDialog = {
|
||||
MENUDIALOGTYPE_DEFAULT,
|
||||
(uintptr_t)"Name Conflicts\n",
|
||||
g_ImportOverrideItems,
|
||||
NULL,
|
||||
MENUDIALOGFLAG_LITERAL_TEXT,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* common utils */
|
||||
|
||||
static s32 mpsetupDeserialize(FILE *f, struct mpsetupfile *setupfile)
|
||||
{
|
||||
s32 rx = 0;
|
||||
|
||||
rx += fread(&setupfile->version, sizeof(setupfile->version), 1, f);
|
||||
rx += fread(&setupfile->defaultsetup, sizeof(setupfile->defaultsetup), 1, f);
|
||||
rx += fread(&setupfile->numsetups, sizeof(setupfile->numsetups), 1, f);
|
||||
|
||||
for (int i = 0; i < setupfile->numsetups; ++i) {
|
||||
rx += fread(setupfile->setups[i].bytes, sizeof(setupfile->setups[i].bytes), 1, f);
|
||||
}
|
||||
|
||||
return rx;
|
||||
}
|
||||
|
||||
static s32 mpsetupSerialize(FILE *f, struct mpsetupfile *setupfile)
|
||||
{
|
||||
s32 wx = 0;
|
||||
|
||||
wx += fwrite(&setupfile->version, sizeof(setupfile->version), 1, f);
|
||||
wx += fwrite(&setupfile->defaultsetup, sizeof(setupfile->defaultsetup), 1, f);
|
||||
wx += fwrite(&setupfile->numsetups, sizeof(setupfile->numsetups), 1, f);
|
||||
|
||||
for (int i = 0; i < setupfile->numsetups; ++i) {
|
||||
wx += fwrite(setupfile->setups[i].bytes, sizeof(setupfile->setups[i].bytes), 1, f);
|
||||
}
|
||||
|
||||
return wx;
|
||||
}
|
||||
|
||||
static FILE *mpsetupOpenFile(bool write, u8 op) {
|
||||
const char *filename = fsFullPath("$S/" MPSETUP_FILENAME ".bin");
|
||||
|
||||
if (op == MPSETUP_OP_EXPORT) {
|
||||
// create export directory if it doesn't exist
|
||||
if (fsFileSize(MPSETUP_EXPORTDIR) < 0) {
|
||||
if (fsCreateDir(MPSETUP_EXPORTDIR) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
filename = fsFullPath(MPSETUP_EXPORTDIR MPSETUP_FILENAME_EXP ".bin");
|
||||
} else if (op == MPSETUP_OP_IMPORT) {
|
||||
// same name as export but different folder
|
||||
filename = fsFullPath("$S/" MPSETUP_FILENAME_EXP ".bin");
|
||||
}
|
||||
|
||||
FILE *f;
|
||||
|
||||
if (fsFileSize(filename) < 0) {
|
||||
// setup file doesn't exist: create one
|
||||
f = fsFileOpenWrite(filename);
|
||||
fsFileFree(f);
|
||||
}
|
||||
|
||||
if (write) {
|
||||
f = fsFileOpenWrite(filename);
|
||||
} else {
|
||||
f = fsFileOpenRead(filename);
|
||||
}
|
||||
|
||||
if (f == NULL) {
|
||||
sysLogPrintf(LOG_ERROR, "Unable to open mp setup file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static s32 mpsetupSaveFile(u8 op, struct mpsetupfile *setupfile)
|
||||
{
|
||||
FILE *f = mpsetupOpenFile(true, op);
|
||||
if (f == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s32 nwritten = mpsetupSerialize(f, setupfile);
|
||||
if (nwritten < 1) {
|
||||
fsFileFree(f);
|
||||
sysLogPrintf(LOG_ERROR, "Unable to write the MP setup file");
|
||||
snprintf(g_StatusText, sizeof(g_StatusText), "Unable to write the setup file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fsFileFree(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32 mpsetupLoadFile(struct mpsetupfile *setupfile, u8 op)
|
||||
{
|
||||
FILE *f = mpsetupOpenFile(false, op);
|
||||
if (f == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mpsetupDeserialize(f, setupfile);
|
||||
|
||||
if (op == MPSETUP_OP_DEFAULT && setupfile->defaultsetup > 0) {
|
||||
mpsetupLoadSetup(setupfile->defaultsetup - 1);
|
||||
}
|
||||
|
||||
fsFileFree(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32 mpsetupImportFile(u8 op, u8 skipOverlap)
|
||||
{
|
||||
if (!skipOverlap) {
|
||||
// check for names overlap
|
||||
u8 overlap = false;
|
||||
for (int i = 0; i < g_ImportMpSetupFile.numsetups; ++i) {
|
||||
for (int j = 0; j < g_MpSetupFile.numsetups; ++j) {
|
||||
if (strcmp(g_ImportMpSetupFile.setups[i].bytes, g_MpSetupFile.setups[j].bytes) == 0) {
|
||||
overlap = true;
|
||||
return MPSETUP_IMPORT_CONFLICT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < g_ImportMpSetupFile.numsetups; ++i) {
|
||||
s16 overlapIdx = -1;
|
||||
for (int j = g_MpSetupFile.numsetups-1; j >= 0; --j) {
|
||||
if (strcmp(g_ImportMpSetupFile.setups[i].bytes, g_MpSetupFile.setups[j].bytes) == 0) {
|
||||
overlapIdx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s16 importIdx = overlapIdx;
|
||||
if (overlapIdx < 0 || op == MPSETUP_IMPORT_ADD) {
|
||||
if (g_MpSetupFile.numsetups == MPSETUP_MAXSETUPS) {
|
||||
snprintf(g_StatusText, sizeof(g_StatusText), "Number of setups exceeds %d\n", MPSETUP_MAXSETUPS);
|
||||
return -1;
|
||||
}
|
||||
importIdx = g_MpSetupFile.numsetups++;
|
||||
}
|
||||
|
||||
memcpy(g_MpSetupFile.setups[importIdx].bytes, g_ImportMpSetupFile.setups[i].bytes, MPSETUP_BLOCKSIZE);
|
||||
}
|
||||
|
||||
return mpsetupSaveCurrentFile();
|
||||
}
|
||||
|
||||
static s32 mpsetupExportFile(void)
|
||||
{
|
||||
struct mpsetupfile expMpSetupFile;
|
||||
expMpSetupFile.numsetups = 0;
|
||||
expMpSetupFile.defaultsetup = 0;
|
||||
expMpSetupFile.version = MPSETUP_VERSION;
|
||||
|
||||
u8 maxsetups = MPSETUP_MAXSETUPS;
|
||||
maxsetups = MIN(maxsetups, g_MpSetupFile.numsetups);
|
||||
for (int i = 0; i < maxsetups; ++i) {
|
||||
u8 bank = i < 64 ? 0 : 1;
|
||||
u8 bit = i - bank * 64;
|
||||
if (g_MpImportExportFilter[bank] & (1 << bit)) {
|
||||
u8 n = expMpSetupFile.numsetups;
|
||||
expMpSetupFile.numsetups++;
|
||||
char *name = g_MpSetupFile.setups[i].bytes;
|
||||
memcpy(expMpSetupFile.setups[n].bytes, g_MpSetupFile.setups[i].bytes, MPSETUP_BLOCKSIZE);
|
||||
expMpSetupFile.numsetups = n + 1;
|
||||
}
|
||||
}
|
||||
|
||||
s32 err = mpsetupSaveFile(MPSETUP_OP_EXPORT, &expMpSetupFile);
|
||||
if (err) {
|
||||
snprintf(g_StatusText, sizeof(g_StatusText), "Unable to write\nsetup file\n");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static s32 mpsetupDelete(void)
|
||||
{
|
||||
s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex;
|
||||
for (int i = slotindex; i < g_MpSetupFile.numsetups - 1; ++i) {
|
||||
u8* dst = g_MpSetupFile.setups[i].bytes;
|
||||
u8* src = g_MpSetupFile.setups[i+1].bytes;
|
||||
memcpy(dst, src, MPSETUP_BLOCKSIZE);
|
||||
}
|
||||
|
||||
if (g_MpCurrentSetup > slotindex) {
|
||||
g_MpCurrentSetup--;
|
||||
}
|
||||
else if (g_MpCurrentSetup == slotindex) {
|
||||
g_MpCurrentSetup = -1;
|
||||
}
|
||||
|
||||
if (g_MpSetupFile.defaultsetup > slotindex + 1) {
|
||||
g_MpSetupFile.defaultsetup--;
|
||||
}
|
||||
else if (g_MpSetupFile.defaultsetup == slotindex + 1) {
|
||||
g_MpSetupFile.defaultsetup = 0;
|
||||
}
|
||||
|
||||
g_MpSetupFile.numsetups--;
|
||||
return mpsetupSaveCurrentFile();
|
||||
}
|
||||
|
||||
/* menu handlers */
|
||||
|
||||
// Rename setup dialog
|
||||
static MenuItemHandlerResult menuhandlerRenameSetup(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
char *name = data->keyboard.string;
|
||||
s32 slotindex = g_Menus[g_MpPlayerNum].mpsetup.slotindex;
|
||||
struct setupblock *setup = &g_MpSetupFile.setups[slotindex];
|
||||
s32 err;
|
||||
|
||||
switch (operation) {
|
||||
case MENUOP_GETTEXT:
|
||||
strcpy(name, setup->bytes);
|
||||
break;
|
||||
case MENUOP_SETTEXT:
|
||||
strcpy(g_MpSetup.name, name);
|
||||
break;
|
||||
case MENUOP_SET:
|
||||
strcpy(setup->bytes, name);
|
||||
err = mpsetupSaveSetup(slotindex, true);
|
||||
if (!err) {
|
||||
menuPopDialog();
|
||||
} else {
|
||||
// TODO
|
||||
// menuPushDialog(&g_FilemgrFileSavedMenuDialog);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Delete setup dialog
|
||||
static MenuItemHandlerResult menuhandlerDeleteSetup(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
if (operation == MENUOP_SET) {
|
||||
s32 err = mpsetupDelete();
|
||||
if (!err) {
|
||||
menuPopDialog();
|
||||
menuPopDialog();
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Import (or Export) settings dialog
|
||||
static MenuItemHandlerResult menuhandlerImportOrExportSettings(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
static const char *labels[] = {
|
||||
"Select All",
|
||||
"Select None",
|
||||
"Import",
|
||||
"Export",
|
||||
};
|
||||
|
||||
s32 op = g_Menus[g_MpPlayerNum].mpsetup.unke24;
|
||||
|
||||
struct mpsetupfile *setupfile = op == MPSETUP_OP_IMPORT ? &g_ImportMpSetupFile : &g_MpSetupFile;
|
||||
|
||||
u8 numsetups = setupfile->numsetups;
|
||||
u8 numitems = numsetups + 3;
|
||||
u8 bank = data->list.value < 64 ? 0 : 1;
|
||||
u8 bit = data->list.value - bank * 64;
|
||||
u8 hasselection = (g_MpImportExportFilter[0] | g_MpImportExportFilter[1]) != 0;
|
||||
u8 ofs;
|
||||
s32 err;
|
||||
u32 colour;
|
||||
s32 index;
|
||||
|
||||
switch (operation) {
|
||||
case MENUOP_GETCOLOUR:
|
||||
colour = data->list.unk04;
|
||||
if (data->list.value == numitems - 1) {
|
||||
data->list.unk04 = hasselection ? colour : (colour & 0xffffff00) | 0x66;
|
||||
}
|
||||
break;
|
||||
|
||||
case MENUOP_GETOPTIONCOUNT:
|
||||
data->list.value = numitems;
|
||||
break;
|
||||
|
||||
case MENUOP_GETOPTIONTEXT:
|
||||
if (data->list.value < numsetups) {
|
||||
return (uintptr_t) setupfile->setups[data->list.value].bytes;
|
||||
} else {
|
||||
// creates an offset to select the "Export" or "Import" label for the last item
|
||||
ofs = (data->list.value == numitems - 1) ? op - 1 : 0;
|
||||
return (intptr_t)labels[data->list.value - numsetups + ofs];
|
||||
}
|
||||
|
||||
case MENUOP_SET:
|
||||
if (data->list.value < setupfile->numsetups) {
|
||||
g_MpImportExportFilter[bank] ^= (1 << bit);
|
||||
} else {
|
||||
index = data->list.value - setupfile->numsetups;
|
||||
|
||||
switch (index) {
|
||||
// Select All
|
||||
case 0:
|
||||
g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1;
|
||||
break;
|
||||
// Select None
|
||||
case 1:
|
||||
g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = 0;
|
||||
break;
|
||||
// Export (or Import)
|
||||
case 2:
|
||||
if (hasselection) {
|
||||
err = op == MPSETUP_OP_IMPORT ? mpsetupImportFile(0, false) : mpsetupExportFile();
|
||||
if (!err) {
|
||||
menuPopDialog();
|
||||
if (op == MPSETUP_OP_IMPORT) {
|
||||
// back to the 'Manage Settings' screen
|
||||
menuPopDialog();
|
||||
menuPushDialog(&g_ManageSettingsDialog);
|
||||
} else {
|
||||
snprintf(g_StatusText, sizeof(g_StatusText), "File %s.bin\nwritten to the folder 'exported'\n", MPSETUP_FILENAME_EXP);
|
||||
menuPushDialog(&g_StatusOkDialog);
|
||||
}
|
||||
} else {
|
||||
if (err == MPSETUP_IMPORT_CONFLICT) {
|
||||
menuPopDialog();
|
||||
menuPushDialog(&g_ImportOverrideDialog);
|
||||
} else {
|
||||
menuPushDialog(&g_StatusErrorDialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MENUOP_GETSELECTEDINDEX:
|
||||
data->list.value = 0x000fffff;
|
||||
break;
|
||||
|
||||
case MENUOP_GETLISTITEMCHECKBOX:
|
||||
if (data->list.value < numsetups) {
|
||||
data->list.unk04 = (g_MpImportExportFilter[bank] & (1 << bit));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Import: overwrite dialog
|
||||
static MenuItemHandlerResult menuhandlerImportAction(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
if (operation == MENUOP_SET) {
|
||||
s32 err = mpsetupImportFile(item->param, true);
|
||||
if (!err) {
|
||||
menuPopDialog();
|
||||
menuPushDialog(&g_ManageSettingsDialog);
|
||||
} else {
|
||||
menuPushDialog(&g_StatusErrorDialog);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Manage Import/Export settings dialog
|
||||
static MenuItemHandlerResult menuhandlerOpenImportExportDialog(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
switch (operation) {
|
||||
case MENUOP_SET:
|
||||
if (item->param == MPSETUP_OP_IMPORT) {
|
||||
strcpy(g_TitleImportExportDialog, "Import Settings\n");
|
||||
if (fsFileSize("$S/" MPSETUP_FILENAME_EXP ".bin") < 0) {
|
||||
snprintf(
|
||||
g_StatusText, sizeof(g_StatusText),
|
||||
"No import file found.\n"
|
||||
"Place the file %s.bin\n"
|
||||
"Next to your %s.bin file\n",
|
||||
MPSETUP_FILENAME_EXP, MPSETUP_FILENAME
|
||||
);
|
||||
menuPushDialog(&g_StatusErrorDialog);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mpsetupLoadFile(&g_ImportMpSetupFile, MPSETUP_OP_IMPORT);
|
||||
} else {
|
||||
strcpy(g_TitleImportExportDialog, "Export Settings\n");
|
||||
}
|
||||
g_Menus[g_MpPlayerNum].mpsetup.unke24 = item->param;
|
||||
g_MpImportExportFilter[0] = g_MpImportExportFilter[1] = -1;
|
||||
menuPushDialog(&g_ImportExportDialog);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Manage settings dialog
|
||||
static MenuItemHandlerResult menuhandlerSelectSetupHandler(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
u32 colour;
|
||||
switch (operation) {
|
||||
case MENUOP_GETCOLOUR:
|
||||
colour = data->list.unk04;
|
||||
if (data->list.value + 1 == g_MpSetupFile.defaultsetup) {
|
||||
data->list.unk04 = (colour & 0xffff0000) | 0xff;
|
||||
}
|
||||
break;
|
||||
case MENUOP_GETOPTIONCOUNT:
|
||||
data->list.value = g_MpSetupFile.numsetups;
|
||||
break;
|
||||
case MENUOP_GETOPTIONTEXT:
|
||||
return (uintptr_t) g_MpSetupFile.setups[data->list.value].bytes;
|
||||
case MENUOP_SET:
|
||||
g_Menus[g_MpPlayerNum].mpsetup.slotindex = data->list.value;
|
||||
if (data->list.value == g_MpSetupFile.defaultsetup - 1) {
|
||||
strcpy(g_LabelSetDefault, "Clear Default\n");
|
||||
} else {
|
||||
strcpy(g_LabelSetDefault, "Set Default\n");
|
||||
}
|
||||
menuPushDialog(&g_ManageSetupDialog);
|
||||
break;
|
||||
case MENUOP_GETSELECTEDINDEX:
|
||||
data->list.value = 0xfffff;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MenuItemHandlerResult menuhandlerSetupRename(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
if (operation == MENUOP_SET) {
|
||||
menuPushDialog(&g_RenameSetupDialog);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MenuItemHandlerResult menuhandlerSetupDelete(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
if (operation == MENUOP_SET) {
|
||||
menuPushDialog(&g_DeleteSetupDialog);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MenuItemHandlerResult menuhandlerSetupSetDefault(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
if (operation == MENUOP_SET) {
|
||||
s32 selected = g_Menus[g_MpPlayerNum].mpsetup.slotindex;
|
||||
// clicked on "clear default"
|
||||
if (selected == g_MpSetupFile.defaultsetup - 1) {
|
||||
g_MpSetupFile.defaultsetup = 0;
|
||||
strcpy(g_LabelSetDefault, "Set Default\n");
|
||||
} else {
|
||||
g_MpSetupFile.defaultsetup = selected + 1;
|
||||
strcpy(g_LabelSetDefault, "Clear Default\n");
|
||||
}
|
||||
|
||||
mpsetupSaveCurrentFile();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* public functions */
|
||||
|
||||
s32 mpsetupLoadCurrentFile(void)
|
||||
{
|
||||
return mpsetupLoadFile(&g_MpSetupFile, MPSETUP_OP_DEFAULT);
|
||||
}
|
||||
|
||||
s32 mpsetupSaveCurrentFile(void)
|
||||
{
|
||||
g_MpSetupFile.version = MPSETUP_VERSION;
|
||||
return mpsetupSaveFile(MPSETUP_OP_DEFAULT, &g_MpSetupFile);
|
||||
}
|
||||
|
||||
s32 mpsetupSaveSetup(s32 slotindex, u8 savefile)
|
||||
{
|
||||
struct savebuffer setup;
|
||||
|
||||
// request to add a new setup
|
||||
if (slotindex == g_MpSetupFile.numsetups) {
|
||||
g_MpCurrentSetup = g_MpSetupFile.numsetups++;
|
||||
}
|
||||
|
||||
savebufferClear(&setup);
|
||||
mpsetupfileSaveWad(&setup);
|
||||
|
||||
memcpy(g_MpSetupFile.setups[slotindex].bytes, setup.bytes, MPSETUP_BLOCKSIZE);
|
||||
|
||||
return savefile ? mpsetupSaveCurrentFile() : 0;
|
||||
}
|
||||
|
||||
void mpsetupLoadSetup(s32 index)
|
||||
{
|
||||
struct savebuffer buffer;
|
||||
savebufferClear(&buffer);
|
||||
struct setupblock *block = &g_MpSetupFile.setups[index];
|
||||
memcpy(&buffer.bytes, block->bytes, MPSETUP_BLOCKSIZE);
|
||||
mpsetupfileLoadWad(&buffer, g_MpSetupFile.version);
|
||||
g_MpCurrentSetup = index;
|
||||
}
|
||||
|
||||
void mpsetupCopyAllFromPak(void)
|
||||
{
|
||||
if (fsFileSize("$S/" MPSETUP_FILENAME ".bin") > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
filelistCreate(1, FILETYPE_MPSETUP);
|
||||
filelistsTick();
|
||||
|
||||
for (int i = 0; i < g_FileLists[1]->numfiles; ++i) {
|
||||
struct savebuffer buffer;
|
||||
savebufferClear(&buffer);
|
||||
struct filelistfile *file = &g_FileLists[1]->files[i];
|
||||
s32 device = pakFindBySerial(file->deviceserial);
|
||||
s32 err = pakReadBodyAtGuid(device, file->fileid, buffer.bytes, 0);
|
||||
|
||||
if (err != 0) {
|
||||
sysLogPrintf(LOG_ERROR, "Unable to read pak. device %d fileid %d deviceserial %d err %d",
|
||||
device, file->fileid, file->deviceserial, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
mpsetupfileLoadWad(&buffer, 0);
|
||||
|
||||
// save the file when writing the last setup
|
||||
u8 savefile = i == g_FileLists[1]->numfiles - 1;
|
||||
mpsetupSaveSetup(g_MpSetupFile.numsetups, savefile);
|
||||
}
|
||||
|
||||
// to reset the mp setup
|
||||
mpInit(false);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,645 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <ultra64.h>
|
||||
#include <PR/ultrasched.h>
|
||||
#include "lib/sched.h"
|
||||
#include "lib/vars.h"
|
||||
#include "constants.h"
|
||||
#include "game/camdraw.h"
|
||||
#include "game/cheats.h"
|
||||
#include "game/debug.h"
|
||||
#include "game/file.h"
|
||||
#include "game/lang.h"
|
||||
#include "game/race.h"
|
||||
#include "game/body.h"
|
||||
#include "game/stubs/game_000840.h"
|
||||
#include "game/stubs/game_000850.h"
|
||||
#include "game/stubs/game_000860.h"
|
||||
#include "game/stubs/game_000870.h"
|
||||
#include "game/smoke.h"
|
||||
#include "game/stubs/game_0008e0.h"
|
||||
#include "game/stubs/game_0008f0.h"
|
||||
#include "game/stubs/game_000900.h"
|
||||
#include "game/stubs/game_000910.h"
|
||||
#include "game/tex.h"
|
||||
#include "game/stubs/game_00b180.h"
|
||||
#include "game/stubs/game_00b200.h"
|
||||
#include "game/challenge.h"
|
||||
#include "game/title.h"
|
||||
#include "game/pdmode.h"
|
||||
#include "game/objectives.h"
|
||||
#include "game/endscreen.h"
|
||||
#include "game/playermgr.h"
|
||||
#include "game/game_1531a0.h"
|
||||
#include "game/gfxmemory.h"
|
||||
#include "game/lang.h"
|
||||
#include "game/lv.h"
|
||||
#include "game/timing.h"
|
||||
#include "game/music.h"
|
||||
#include "game/stubs/game_175f50.h"
|
||||
#include "game/game_175f90.h"
|
||||
#include "game/zbuf.h"
|
||||
#include "game/game_1a78b0.h"
|
||||
#include "game/mplayer/mplayer.h"
|
||||
#include "game/pak.h"
|
||||
#include "game/splat.h"
|
||||
#include "game/utils.h"
|
||||
#include "bss.h"
|
||||
#include "lib/audiomgr.h"
|
||||
#include "lib/args.h"
|
||||
#include "lib/boot.h"
|
||||
#include "lib/vm.h"
|
||||
#include "lib/rzip.h"
|
||||
#include "lib/vi.h"
|
||||
#include "lib/fault.h"
|
||||
#include "lib/crash.h"
|
||||
#include "lib/dma.h"
|
||||
#include "lib/joy.h"
|
||||
#include "lib/main.h"
|
||||
#include "lib/snd.h"
|
||||
#include "lib/memp.h"
|
||||
#include "lib/mema.h"
|
||||
#include "lib/model.h"
|
||||
#include "lib/profile.h"
|
||||
#include "lib/videbug.h"
|
||||
#include "lib/debughud.h"
|
||||
#include "lib/anim.h"
|
||||
#include "lib/rdp.h"
|
||||
#include "lib/lib_34d0.h"
|
||||
#include "lib/lib_2f490.h"
|
||||
#include "lib/rmon.h"
|
||||
#include "lib/rng.h"
|
||||
#include "lib/str.h"
|
||||
#include "data.h"
|
||||
#include "types.h"
|
||||
#include "system.h"
|
||||
|
||||
extern u8 *g_MempHeap;
|
||||
extern u32 g_MempHeapSize;
|
||||
|
||||
void rngSetSeed(u32 seed);
|
||||
|
||||
bool var8005d9b0 = false;
|
||||
s32 g_StageNum = STAGE_TITLE;
|
||||
u32 g_MainMemaHeapSize = 1024 * 300;
|
||||
bool var8005d9bc = false;
|
||||
s32 var8005d9c0 = 0;
|
||||
s32 var8005d9c4 = 0;
|
||||
bool g_MainGameLogicEnabled = true;
|
||||
u32 g_MainNumGfxTasks = 0;
|
||||
bool g_MainIsEndscreen = false;
|
||||
s32 g_DoBootPakMenu = 0;
|
||||
|
||||
u32 var8005dd3c = 0x00000000;
|
||||
u32 var8005dd40 = 0x00000000;
|
||||
u32 var8005dd44 = 0x00000000;
|
||||
u32 var8005dd48 = 0x00000000;
|
||||
u32 var8005dd4c = 0x00000000;
|
||||
u32 var8005dd50 = 0x00000000;
|
||||
s32 g_MainChangeToStageNum = -1;
|
||||
bool g_MainIsDebugMenuOpen = false;
|
||||
|
||||
struct stageallocation g_StageAllocations8Mb[] = {
|
||||
{ STAGE_CITRAINING, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_DEFECTION, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_INVESTIGATION, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_EXTRACTION, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_CHICAGO, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_G5BUILDING, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_VILLA, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma600" },
|
||||
{ STAGE_INFILTRATION, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma500" },
|
||||
{ STAGE_RESCUE, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma500" },
|
||||
{ STAGE_ESCAPE, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma500" },
|
||||
{ STAGE_AIRBASE, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_AIRFORCEONE, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_CRASHSITE, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_PELAGIC, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_DEEPSEA, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_DEFENSE, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_ATTACKSHIP, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_SKEDARRUINS, "-ml0 -me0 -mgfx110 -mgfxtra80 -mvtx100 -ma700" },
|
||||
{ STAGE_MP_SKEDAR, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_RAVINE, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_PIPES, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_G5BUILDING, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_SEWERS, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_WAREHOUSE, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_BASE, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_COMPLEX, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_TEMPLE, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_FELICITY, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_AREA52, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_GRID, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_CARPARK, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_RUINS, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_FORTRESS, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_MP_VILLA, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_RUN, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP2, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP6, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP7, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP8, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP14, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP16, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP17, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP18, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP19, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_MP20, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_ASH, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_28, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_MBR, "-ml0 -me0 -mgfx120 -mvtx100 -ma700" },
|
||||
{ STAGE_TEST_SILO, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_24, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_MAIANSOS, "-ml0 -me0 -mgfx120 -mvtx100 -ma500" },
|
||||
{ STAGE_RETAKING, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_TEST_DEST, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_2B, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_WAR, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_TEST_UFF, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_TEST_OLD, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_DUEL, "-ml0 -me0 -mgfx120 -mvtx100 -ma700" },
|
||||
{ STAGE_TEST_LAM, "-ml0 -me0 -mgfx120 -mvtx98 -ma400" },
|
||||
{ STAGE_TEST_ARCH, "-ml0 -me0 -mgfx200 -mvtx200 -ma400" },
|
||||
{ STAGE_TEST_LEN, "-ml0 -me0 -mgfx120 -mvtx98 -ma300" },
|
||||
{ STAGE_TITLE, "-ml0 -me0 -mgfx80 -mvtx20 -ma001" },
|
||||
{ 0, "-ml0 -me0 -mgfx120 -mvtx98 -ma300" },
|
||||
};
|
||||
|
||||
struct stageallocation g_StageAllocations4Mb[] = {
|
||||
{ STAGE_MP_SKEDAR, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_PIPES, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_AREA52, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_RAVINE, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_G5BUILDING, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_SEWERS, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_WAREHOUSE, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_BASE, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_COMPLEX, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_TEMPLE, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_FELICITY, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_GRID, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_TEST_RUN, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_CARPARK, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_RUINS, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_MP_FORTRESS, "-ml0 -me0 -mgfx96 -mvtx96 -ma130" },
|
||||
{ STAGE_MP_VILLA, "-ml0 -me0 -mgfx96 -mvtx96 -ma140" },
|
||||
{ STAGE_TEST_MP2, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP6, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP7, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP8, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP14, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP16, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP17, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP18, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP19, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_MP20, "-ml0 -me0 -mgfx96 -mvtx96 -ma115" },
|
||||
{ STAGE_TEST_LEN, "-ml0 -me0 -mgfx100 -mvtx96 -ma120" },
|
||||
{ STAGE_4MBMENU, "-mgfx100 -mvtx50 -ma50" },
|
||||
{ STAGE_TITLE, "-ml0 -me0 -mgfx80 -mvtx20 -ma001" },
|
||||
{ 0, "-ml0 -me0 -mgfx100 -mvtx96 -ma300" },
|
||||
};
|
||||
|
||||
Gfx var8005dcc8[] = {
|
||||
gsSPSegment(0x00, 0x00000000),
|
||||
gsSPDisplayList(&var800613a0),
|
||||
gsSPDisplayList(&var80061380),
|
||||
gsDPFullSync(),
|
||||
gsSPEndDisplayList(),
|
||||
};
|
||||
|
||||
s32 g_MainIsBooting = 1;
|
||||
|
||||
void mainInit(void)
|
||||
{
|
||||
s32 x;
|
||||
s32 i;
|
||||
s32 j;
|
||||
u32 addr;
|
||||
|
||||
faultInit();
|
||||
dmaInit();
|
||||
amgrInit();
|
||||
varsInit();
|
||||
mempInit();
|
||||
memaInit();
|
||||
joyInit();
|
||||
joyReset();
|
||||
|
||||
var8005d9b0 = rmonIsDisabled();
|
||||
|
||||
g_Is4Mb = (osGetMemSize() <= 0x400000);
|
||||
g_VmShowStats = 0;
|
||||
|
||||
// no copyright screen
|
||||
viSetMode(VIMODE_HI);
|
||||
viConfigureForLegal();
|
||||
viBlack(true);
|
||||
viUpdateMode();
|
||||
|
||||
filesInit();
|
||||
|
||||
if (var8005d9b0) {
|
||||
argSetString(" -ml0 -me0 -mgfx100 -mvtx50 -mt700 -ma400");
|
||||
}
|
||||
|
||||
mempSetHeap(g_MempHeap, g_MempHeapSize);
|
||||
|
||||
mempResetPool(MEMPOOL_8);
|
||||
mempResetPool(MEMPOOL_PERMANENT);
|
||||
crashReset();
|
||||
challengesInit();
|
||||
utilsInit();
|
||||
texInit();
|
||||
langInit();
|
||||
lvInit();
|
||||
cheatsInit();
|
||||
textInit();
|
||||
dhudInit();
|
||||
playermgrInit();
|
||||
frametimeInit();
|
||||
profileInit();
|
||||
smokesInit();
|
||||
mpInit(true);
|
||||
pheadInit();
|
||||
paksInit();
|
||||
pheadInit2();
|
||||
animsInit();
|
||||
racesInit();
|
||||
bodiesInit();
|
||||
titleInit();
|
||||
|
||||
modelSetDistanceChecksDisabled(true); // don't use LODs
|
||||
|
||||
g_MainIsBooting = 0;
|
||||
}
|
||||
|
||||
void mainProc(void)
|
||||
{
|
||||
mainInit();
|
||||
rdpInit();
|
||||
sndInit();
|
||||
|
||||
while (true) {
|
||||
mainLoop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It's suspected that this function would have allowed developers to override
|
||||
* the value of variables while the game is running in order to view their
|
||||
* effects immediately rather than having to recompile the game each time.
|
||||
*
|
||||
* The developers would have used rmon to create a table of name/value pairs,
|
||||
* then this function would have looked up the given variable name in the table
|
||||
* and written the new value to the variable's address.
|
||||
*/
|
||||
void mainOverrideVariable(char *name, void *value)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
/**
|
||||
* This function enters an infinite loop which iterates once per stage load.
|
||||
* Within this loop is an inner loop which runs very frequently and decides
|
||||
* whether to run mainTick on each iteration.
|
||||
*
|
||||
* NTSC beta checks two shorts at an offset 64MB into the development board
|
||||
* and refuses to continue if they are not any of the allowed values.
|
||||
* Decomp patches these reads in its build system so it can be played
|
||||
* without the development board.
|
||||
*/
|
||||
void mainLoop(void)
|
||||
{
|
||||
s32 ending = false;
|
||||
s32 index;
|
||||
s32 numplayers;
|
||||
u32 stack;
|
||||
|
||||
func0f175f98();
|
||||
|
||||
var8005d9c4 = 0;
|
||||
argGetLevel(&g_StageNum);
|
||||
|
||||
if (g_DoBootPakMenu) {
|
||||
g_Vars.pakstocheck = 0xfd;
|
||||
g_StageNum = STAGE_BOOTPAKMENU;
|
||||
}
|
||||
|
||||
if (g_StageNum != STAGE_TITLE) {
|
||||
titleSetNextStage(g_StageNum);
|
||||
|
||||
if (g_StageNum < STAGE_TITLE) {
|
||||
func0f01b148(0);
|
||||
|
||||
if (argFindByPrefix(1, "-hard")) {
|
||||
lvSetDifficulty(argFindByPrefix(1, "-hard")[0] - '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_StageNum == STAGE_CITRAINING && IS4MB()) {
|
||||
g_StageNum = STAGE_4MBMENU;
|
||||
}
|
||||
|
||||
rngSetSeed(osGetCount());
|
||||
|
||||
// Outer loop - this is infinite because ending is never changed
|
||||
while (!ending) {
|
||||
g_MainNumGfxTasks = 0;
|
||||
g_MainGameLogicEnabled = true;
|
||||
g_MainIsEndscreen = false;
|
||||
|
||||
if (var8005d9b0 && var8005d9c4 == 0) {
|
||||
index = -1;
|
||||
|
||||
if (IS4MB()) {
|
||||
if (g_StageNum < STAGE_TITLE && getNumPlayers() >= 2) {
|
||||
index = 0; \
|
||||
while (g_StageAllocations4Mb[index].stagenum) { \
|
||||
if (g_StageAllocations4Mb[index].stagenum == g_StageNum + 400) { \
|
||||
break; \
|
||||
} \
|
||||
index++;
|
||||
}
|
||||
|
||||
if (g_StageAllocations4Mb[index].stagenum == 0) {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (index);
|
||||
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
while (g_StageAllocations4Mb[index].stagenum) {
|
||||
if (g_StageNum == g_StageAllocations4Mb[index].stagenum) {
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
argSetString(g_StageAllocations4Mb[index].string);
|
||||
} else {
|
||||
// 8MB
|
||||
if (g_StageNum < STAGE_TITLE && getNumPlayers() >= 2) {
|
||||
index = 0; \
|
||||
while (g_StageAllocations8Mb[index].stagenum) { \
|
||||
if (g_StageNum + 400 == g_StageAllocations8Mb[index].stagenum) { \
|
||||
break; \
|
||||
} \
|
||||
index++;
|
||||
}
|
||||
|
||||
if (g_StageAllocations8Mb[index].stagenum == 0) {
|
||||
index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
|
||||
while (g_StageAllocations8Mb[index].stagenum) {
|
||||
if (g_StageNum == g_StageAllocations8Mb[index].stagenum) {
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
argSetString(g_StageAllocations8Mb[index].string);
|
||||
}
|
||||
}
|
||||
|
||||
var8005d9c4 = 0;
|
||||
|
||||
mempResetPool(MEMPOOL_7);
|
||||
mempResetPool(MEMPOOL_STAGE);
|
||||
filesStop(4);
|
||||
|
||||
if (argFindByPrefix(1, "-ma")) {
|
||||
g_MainMemaHeapSize = strtol(argFindByPrefix(1, "-ma"), NULL, 0) * 1024;
|
||||
}
|
||||
|
||||
memaReset(mempAlloc(g_MainMemaHeapSize, MEMPOOL_STAGE), g_MainMemaHeapSize);
|
||||
langReset(g_StageNum);
|
||||
playermgrReset();
|
||||
|
||||
if (g_StageNum >= STAGE_TITLE) {
|
||||
numplayers = 0;
|
||||
} else {
|
||||
if (argFindByPrefix(1, "-play")) {
|
||||
numplayers = strtol(argFindByPrefix(1, "-play"), NULL, 0);
|
||||
} else {
|
||||
numplayers = 1;
|
||||
}
|
||||
|
||||
if (getNumPlayers() >= 2) {
|
||||
numplayers = getNumPlayers();
|
||||
}
|
||||
}
|
||||
|
||||
if (numplayers < 2) {
|
||||
g_Vars.bondplayernum = 0;
|
||||
g_Vars.coopplayernum = -1;
|
||||
g_Vars.antiplayernum = -1;
|
||||
} else if (argFindByPrefix(1, "-coop")) {
|
||||
g_Vars.bondplayernum = 0;
|
||||
g_Vars.coopplayernum = 1;
|
||||
g_Vars.antiplayernum = -1;
|
||||
} else if (argFindByPrefix(1, "-anti")) {
|
||||
g_Vars.bondplayernum = 0;
|
||||
g_Vars.coopplayernum = -1;
|
||||
g_Vars.antiplayernum = 1;
|
||||
}
|
||||
|
||||
playermgrAllocatePlayers(numplayers);
|
||||
|
||||
if (argFindByPrefix(1, "-mpbots")) {
|
||||
g_Vars.lvmpbotlevel = 1;
|
||||
}
|
||||
|
||||
if (g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) {
|
||||
if (g_MpSetup.chrslots & 0xfff0) {
|
||||
g_MpSetup.storedbotbits = g_MpSetup.chrslots & 0xfff0;
|
||||
}
|
||||
g_MpSetup.chrslots = 0x03;
|
||||
mpReset();
|
||||
} else if (g_Vars.perfectbuddynum) {
|
||||
mpReset();
|
||||
} else if (g_Vars.mplayerisrunning == false
|
||||
&& (numplayers >= 2 || g_Vars.lvmpbotlevel || argFindByPrefix(1, "-play"))) {
|
||||
g_MpSetup.chrslots = 1;
|
||||
|
||||
if (numplayers >= 2) {
|
||||
g_MpSetup.chrslots |= 1 << 1;
|
||||
}
|
||||
|
||||
if (numplayers >= 3) {
|
||||
g_MpSetup.chrslots |= 1 << 2;
|
||||
}
|
||||
|
||||
if (numplayers >= 4) {
|
||||
g_MpSetup.chrslots |= 1 << 3;
|
||||
}
|
||||
|
||||
g_MpSetup.stagenum = g_StageNum;
|
||||
mpReset();
|
||||
}
|
||||
|
||||
gfxReset();
|
||||
joyReset();
|
||||
dhudReset();
|
||||
zbufReset(g_StageNum);
|
||||
lvReset(g_StageNum);
|
||||
viReset(g_StageNum);
|
||||
frametimeCalculate();
|
||||
profileReset();
|
||||
|
||||
while (g_MainChangeToStageNum < 0) {
|
||||
const s32 cycles = osGetCount() - g_Vars.thisframestartt;
|
||||
if (!g_Vars.mininc60 || (cycles >= g_Vars.mininc60 * CYCLES_PER_FRAME - CYCLES_PER_FRAME / 2)) {
|
||||
schedStartFrame(&g_Sched);
|
||||
mainTick();
|
||||
schedEndFrame(&g_Sched);
|
||||
}
|
||||
if (g_TickExtraSleep) {
|
||||
sysSleep(EXTRA_SLEEP_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
lvStop();
|
||||
mempDisablePool(MEMPOOL_STAGE);
|
||||
mempDisablePool(MEMPOOL_7);
|
||||
filesStop(4);
|
||||
viBlack(true);
|
||||
pak0f116994();
|
||||
|
||||
g_StageNum = g_MainChangeToStageNum;
|
||||
g_MainChangeToStageNum = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void mainTick(void)
|
||||
{
|
||||
Gfx *gdl = NULL;
|
||||
Gfx *gdlstart = NULL;
|
||||
OSScMsg msg = {OS_SC_DONE_MSG};
|
||||
s32 i;
|
||||
|
||||
if (g_MainChangeToStageNum < 0) {
|
||||
frametimeCalculate();
|
||||
profileReset();
|
||||
profileSetMarker(PROFILE_MAINTICK_START);
|
||||
joyDebugJoy();
|
||||
schedSetCrashEnable2(false);
|
||||
|
||||
if (g_MainGameLogicEnabled) {
|
||||
gdl = gdlstart = gfxGetMasterDisplayList();
|
||||
|
||||
gDPSetTile(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0x0000, G_TX_LOADTILE, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
|
||||
gDPSetTile(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_4b, 0, 0x0100, 6, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
|
||||
|
||||
lvTick();
|
||||
playermgrShuffle();
|
||||
|
||||
if (g_StageNum < STAGE_TITLE) {
|
||||
for (i = 0; i < PLAYERCOUNT(); i++) {
|
||||
setCurrentPlayerNum(playermgrGetPlayerAtOrder(i));
|
||||
|
||||
if (g_StageNum != STAGE_TEST_OLD || !titleIsKeepingMode()) {
|
||||
viSetViewPosition(g_Vars.currentplayer->viewleft, g_Vars.currentplayer->viewtop);
|
||||
viSetFovAspectAndSize(
|
||||
g_Vars.currentplayer->fovy, g_Vars.currentplayer->aspect,
|
||||
g_Vars.currentplayer->viewwidth, g_Vars.currentplayer->viewheight);
|
||||
}
|
||||
|
||||
lvTickPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
gdl = lvRender(gdl);
|
||||
|
||||
if (debugGetProfileMode() >= 2) {
|
||||
gdl = profileRender(gdl);
|
||||
}
|
||||
|
||||
gDPFullSync(gdl++);
|
||||
gSPEndDisplayList(gdl++);
|
||||
}
|
||||
|
||||
if (g_MainGameLogicEnabled) {
|
||||
gfxSwapBuffers();
|
||||
viUpdateMode();
|
||||
}
|
||||
|
||||
rdpCreateTask(gdlstart, gdl, 0, (uintptr_t) &msg);
|
||||
memaPrint();
|
||||
profileSetMarker(PROFILE_MAINTICK_END);
|
||||
}
|
||||
}
|
||||
|
||||
void mainEndStage(void)
|
||||
{
|
||||
sndStopNosedive();
|
||||
|
||||
if (!g_MainIsEndscreen) {
|
||||
pak0f11c6d0();
|
||||
joyDisableTemporarily();
|
||||
|
||||
if (g_Vars.coopplayernum >= 0) {
|
||||
s32 prevplayernum = g_Vars.currentplayernum;
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < PLAYERCOUNT(); i++) {
|
||||
setCurrentPlayerNum(i);
|
||||
endscreenPushCoop();
|
||||
}
|
||||
|
||||
setCurrentPlayerNum(prevplayernum);
|
||||
musicStartMenu();
|
||||
} else if (g_Vars.antiplayernum >= 0) {
|
||||
s32 prevplayernum = g_Vars.currentplayernum;
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < PLAYERCOUNT(); i++) {
|
||||
setCurrentPlayerNum(i);
|
||||
endscreenPushAnti();
|
||||
}
|
||||
|
||||
setCurrentPlayerNum(prevplayernum);
|
||||
musicStartMenu();
|
||||
} else if (g_Vars.normmplayerisrunning) {
|
||||
mpEndMatch();
|
||||
} else {
|
||||
endscreenPrepare();
|
||||
musicStartMenu();
|
||||
}
|
||||
}
|
||||
|
||||
g_MainIsEndscreen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change to the given stage at the end of the current frame.
|
||||
*/
|
||||
void mainChangeToStage(s32 stagenum)
|
||||
{
|
||||
pak0f11c6d0();
|
||||
|
||||
g_MainChangeToStageNum = stagenum;
|
||||
}
|
||||
|
||||
s32 mainGetStageNum(void)
|
||||
{
|
||||
return g_StageNum;
|
||||
}
|
||||
|
||||
void func0000e990(void)
|
||||
{
|
||||
objectivesCheckAll();
|
||||
objectivesDisableChecking();
|
||||
mainEndStage();
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
#include <PR/ultratypes.h>
|
||||
#include <PR/ultrasched.h>
|
||||
#include "lib/boot.h"
|
||||
#include "lib/sched.h"
|
||||
#include "constants.h"
|
||||
#include "game/menugfx.h"
|
||||
#include "bss.h"
|
||||
#include "lib/args.h"
|
||||
#include "lib/audiomgr.h"
|
||||
#include "lib/reset.h"
|
||||
#include "lib/rzip.h"
|
||||
#include "lib/crash.h"
|
||||
#include "lib/main.h"
|
||||
#include "lib/snd.h"
|
||||
#include "lib/pimgr.h"
|
||||
#include "lib/profile.h"
|
||||
#include "lib/rmon.h"
|
||||
#include "lib/lib_48150.h"
|
||||
#include "lib/vi.h"
|
||||
#include "lib/joy.h"
|
||||
#include "data.h"
|
||||
#include "types.h"
|
||||
|
||||
#include "video.h"
|
||||
#include "audio.h"
|
||||
#include "input.h"
|
||||
#include "mixer.h"
|
||||
|
||||
/*
|
||||
* private typedefs and defines
|
||||
*/
|
||||
#define VIDEO_MSG 666
|
||||
#define RSP_DONE_MSG 667
|
||||
#define RDP_DONE_MSG 668
|
||||
#define PRE_NMI_MSG 669
|
||||
|
||||
/*
|
||||
* OSScTask state
|
||||
*/
|
||||
#define OS_SC_DP 0x0001 /* set if still needs dp */
|
||||
#define OS_SC_SP 0x0002 /* set if still needs sp */
|
||||
#define OS_SC_YIELD 0x0010 /* set if yield requested */
|
||||
#define OS_SC_YIELDED 0x0020 /* set if yield completed */
|
||||
|
||||
/*
|
||||
* OSScTask->flags type identifier
|
||||
*/
|
||||
#define OS_SC_XBUS (OS_SC_SP | OS_SC_DP)
|
||||
#define OS_SC_DRAM (OS_SC_SP | OS_SC_DP | OS_SC_DRAM_DLIST)
|
||||
#define OS_SC_DP_XBUS (OS_SC_SP)
|
||||
#define OS_SC_DP_DRAM (OS_SC_SP | OS_SC_DRAM_DLIST)
|
||||
#define OS_SC_SP_XBUS (OS_SC_DP)
|
||||
#define OS_SC_SP_DRAM (OS_SC_DP | OS_SC_DRAM_DLIST)
|
||||
|
||||
/*
|
||||
* private functions
|
||||
*/
|
||||
void __scMain(void *arg);
|
||||
void __scHandleTasks(OSSched *s);
|
||||
void __scHandleRSP(OSSched *s);
|
||||
void __scHandleRDP(OSSched *s);
|
||||
void __scAppendList(OSSched *s, OSScTask *t);
|
||||
OSScTask *__scTaskReady(OSScTask *t);
|
||||
s32 __scTaskComplete(OSSched *s,OSScTask *t);
|
||||
void __scExec(OSSched *sc, OSScTask *sp, OSScTask *dp);
|
||||
void __scYield(OSSched *s);
|
||||
s32 __scSchedule(OSSched *sc, OSScTask **sp, OSScTask **dp, s32 availRCP);
|
||||
|
||||
OSViMode var8008dcc0[NUM_GFXTASKS];
|
||||
OSViMode *var8008dd60[NUM_GFXTASKS];
|
||||
OSViMode var8008dd68[NUM_GFXTASKS];
|
||||
s32 var8008de08;
|
||||
s32 g_ViCurVStart0;
|
||||
s32 g_ViCurVStart1;
|
||||
u32 var8008de14;
|
||||
OSTimer g_SchedRspTimer;
|
||||
u32 g_SchedDpCounters[4];
|
||||
struct artifact g_ArtifactLists[3][120];
|
||||
u8 g_SchedSpecialArtifactIndexes[3];
|
||||
s32 g_SchedWriteArtifactsIndex;
|
||||
s32 g_SchedFrontArtifactsIndex;
|
||||
s32 g_SchedPendingArtifactsIndex;
|
||||
|
||||
bool g_SchedCrashedUnexpectedly = false;
|
||||
bool g_SchedCrashEnable1 = false;
|
||||
bool g_SchedCrashEnable2 = false;
|
||||
u32 g_SchedCrashRenderInterval = 45000000;
|
||||
u32 g_SchedCrashLastRendered = 0;
|
||||
|
||||
s32 var8005ce74 = 0;
|
||||
f32 g_ViXScalesBySlot[NUM_GFXTASKS] = {1, 1};
|
||||
f32 g_ViYScalesBySlot[NUM_GFXTASKS] = {1, 1};
|
||||
bool g_SchedViModesPending[NUM_GFXTASKS] = {false, false};
|
||||
s32 g_ViUnblackTimer = NUM_FRAMEBUFFERS + 1;
|
||||
s32 g_ViShakeDirection = 1;
|
||||
s32 g_ViShakeIntensity = 0;
|
||||
f32 g_ViShakeIntensityMult = 1.f;
|
||||
s32 g_ViShakeTimer = 0;
|
||||
u32 var8005cea0 = 0;
|
||||
u32 var8005cea4 = 0;
|
||||
OSScMsg g_SchedRspMsg = {OS_SC_RSP_MSG};
|
||||
bool g_SchedIsFirstTask = true;
|
||||
|
||||
s32 g_PrevFrameFb = -1;
|
||||
s32 g_BlurFb = -1;
|
||||
s32 g_BlurFbCapTimer = -1;
|
||||
bool g_BlurFbDirty = true;
|
||||
|
||||
void schedSetCrashEnable1(bool enable)
|
||||
{
|
||||
g_SchedCrashEnable1 = enable;
|
||||
}
|
||||
|
||||
void schedSetCrashedUnexpectedly(bool enable)
|
||||
{
|
||||
g_SchedCrashedUnexpectedly = enable;
|
||||
}
|
||||
|
||||
void schedSetCrashEnable2(bool enable)
|
||||
{
|
||||
g_SchedCrashEnable2 = enable;
|
||||
}
|
||||
|
||||
void schedSetCrashRenderInterval(u32 cycles)
|
||||
{
|
||||
g_SchedCrashRenderInterval = cycles;
|
||||
}
|
||||
|
||||
void schedRenderCrashOnBuffer(void *framebuffer)
|
||||
{
|
||||
if ((g_SchedCrashEnable2 && g_SchedCrashEnable1) || g_SchedCrashedUnexpectedly) {
|
||||
// crashRenderFrame(framebuffer);
|
||||
g_SchedCrashLastRendered = osGetCount();
|
||||
}
|
||||
}
|
||||
|
||||
void schedRenderCrashPeriodically(u32 framecount)
|
||||
{
|
||||
if ((framecount & 0xf) == 0 && ((g_SchedCrashEnable2 && g_SchedCrashEnable1) || g_SchedCrashedUnexpectedly)) {
|
||||
if (osGetCount() - g_SchedCrashLastRendered > g_SchedCrashRenderInterval) {
|
||||
// crashRenderFrame(g_FrameBuffers[0]);
|
||||
// crashRenderFrame(g_FrameBuffers[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void schedInitCrashLastRendered(void)
|
||||
{
|
||||
g_SchedCrashLastRendered = osGetCount();
|
||||
}
|
||||
|
||||
void osCreateScheduler(OSSched *sc, OSThread *thread, u8 mode, u32 numFields)
|
||||
{
|
||||
sc->curRSPTask = 0;
|
||||
sc->curRDPTask = 0;
|
||||
sc->clientList = 0;
|
||||
sc->frameCount = 0;
|
||||
sc->audioListHead = 0;
|
||||
sc->gfxListHead = 0;
|
||||
sc->audioListTail = 0;
|
||||
sc->gfxListTail = 0;
|
||||
sc->retraceMsg.type = OS_SC_RETRACE_MSG;
|
||||
sc->prenmiMsg.type = OS_SC_PRE_NMI_MSG;
|
||||
sc->thread = thread;
|
||||
|
||||
osCreateMesgQueue(&sc->interruptQ, sc->intBuf, OS_SC_MAX_MESGS);
|
||||
osCreateMesgQueue(&sc->cmdQ, sc->cmdMsgBuf, OS_SC_MAX_MESGS);
|
||||
|
||||
osCreateViManager(OS_PRIORITY_VIMGR);
|
||||
|
||||
var8008de08 = osViModeTable[mode].comRegs.hStart;
|
||||
g_ViCurVStart0 = osViModeTable[mode].fldRegs[0].vStart;
|
||||
g_ViCurVStart1 = osViModeTable[mode].fldRegs[1].vStart;
|
||||
|
||||
var8008dd60[0] = &var8008dd68[0];
|
||||
var8008dd60[1] = &var8008dd68[1];
|
||||
|
||||
var8008dd68[0] = osViModeTable[mode];
|
||||
var8008dd68[1] = osViModeTable[mode];
|
||||
|
||||
osViSetEvent(&sc->interruptQ, (OSMesg)VIDEO_MSG, numFields);
|
||||
schedInitCrashLastRendered();
|
||||
|
||||
g_PrevFrameFb = videoCreateFramebuffer(0, 0, false, true);
|
||||
g_BlurFb = videoCreateFramebuffer(0, 0, false, true);
|
||||
}
|
||||
|
||||
void osScAddClient(OSSched *sc, OSScClient *c, OSMesgQueue *msgQ, bool is30fps)
|
||||
{
|
||||
c->msgQ = msgQ;
|
||||
c->is30fps = is30fps;
|
||||
c->next = sc->clientList;
|
||||
sc->clientList = c;
|
||||
}
|
||||
|
||||
OSMesgQueue *osScGetCmdQ(OSSched *sc)
|
||||
{
|
||||
return &sc->cmdQ;
|
||||
}
|
||||
|
||||
void __scUpdateViMode(void)
|
||||
{
|
||||
if (g_SchedIsFirstTask) {
|
||||
osViBlack(false);
|
||||
g_SchedIsFirstTask = false;
|
||||
}
|
||||
|
||||
var8005ce74 = (var8005ce74 + 1) % 2;
|
||||
|
||||
if (g_SchedViModesPending[1 - var8005ce74]) {
|
||||
// TODO: make this a little less awkward
|
||||
extern struct rend_vidat *g_ViBackData;
|
||||
videoUpdateNativeResolution(g_ViBackData->bufx, g_ViBackData->bufy);
|
||||
g_SchedViModesPending[1 - var8005ce74] = false;
|
||||
}
|
||||
|
||||
if (g_ViUnblackTimer != 0 && g_ViUnblackTimer <= NUM_FRAMEBUFFERS) {
|
||||
g_ViUnblackTimer--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nintendo's sheduler accepts tasks on a "command" message queue.
|
||||
* This isn't used here.
|
||||
*
|
||||
* In PD, the main and audio threads submit tasks by calling this function
|
||||
* instead. It temporarily increases the calling thread's priority above the
|
||||
* scheduler, adds the task to the linked list directly and attempts to execute
|
||||
* it. This is faster than the queue method because it avoids switching threads.
|
||||
*/
|
||||
void schedSubmitTask(OSSched *sc, OSScTask *t)
|
||||
{
|
||||
if (sc->doAudio && t->list.t.type == M_AUDTASK) {
|
||||
// send this into the mixer
|
||||
} else if (t->list.t.type == M_GFXTASK) {
|
||||
videoSubmitCommands((Gfx *)t->list.t.data_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void schedStartFrame(OSSched *sc)
|
||||
{
|
||||
videoStartFrame();
|
||||
}
|
||||
|
||||
void schedAudioFrame(OSSched *sc)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
if (!g_SndDisabled) {
|
||||
for (i = 0; i < g_Vars.diffframe60; i++) {
|
||||
amgrFrame();
|
||||
audioEndFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a retrace (vsync) event.
|
||||
*
|
||||
* Audio tasks are scheduled based on retrace + a timer (approximately 6ms).
|
||||
* On NTSC, this is done on every second frame if 8MB, or every second frame
|
||||
* if 4MB. I guess less memory means the audio queue has to be kept smaller
|
||||
* and processed more frequently. On PAL, it's every second frame regardless.
|
||||
*
|
||||
* Controller input is polled here.
|
||||
*
|
||||
* Lastly, if there's crash information available then it will be checked and
|
||||
* rendered periodically (once every 16 retraces). I guess this makes it render
|
||||
* if the RDP has hung.
|
||||
*/
|
||||
void schedEndFrame(OSSched *sc)
|
||||
{
|
||||
sc->frameCount++;
|
||||
|
||||
#if PAL
|
||||
if (!g_Resetting && (sc->frameCount & 1)) {
|
||||
// osStopTimer(&g_SchedRspTimer);
|
||||
// osSetTimer(&g_SchedRspTimer, 280000, 0, amgrGetFrameMesgQueue(), &g_SchedRspMsg);
|
||||
}
|
||||
#else
|
||||
if (!g_Resetting && ((sc->frameCount & 1) || IS4MB())) {
|
||||
// osStopTimer(&g_SchedRspTimer);
|
||||
// osSetTimer(&g_SchedRspTimer, 280000, 0, amgrGetFrameMesgQueue(), &g_SchedRspMsg);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!g_Resetting) {
|
||||
viHandleRetrace();
|
||||
}
|
||||
|
||||
inputUpdate();
|
||||
|
||||
joyStartReadData(&g_PiMesgQueue);
|
||||
joyReadData();
|
||||
joy00014238();
|
||||
|
||||
sndHandleRetrace();
|
||||
schedAudioFrame(sc);
|
||||
schedRenderCrashPeriodically(sc->frameCount);
|
||||
videoEndFrame();
|
||||
|
||||
if (g_MainIsBooting == 0) {
|
||||
schedConsiderScreenshot();
|
||||
}
|
||||
|
||||
// check for vid mode changes
|
||||
__scUpdateViMode();
|
||||
}
|
||||
|
||||
void schedInitArtifacts(void)
|
||||
{
|
||||
s32 i;
|
||||
s32 j;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
for (j = 0; j < MAX_ARTIFACTS; j++) {
|
||||
g_ArtifactLists[i][j].type = ARTIFACTTYPE_FREE;
|
||||
}
|
||||
|
||||
g_SchedSpecialArtifactIndexes[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The write list is an artifact list that is not currently being displayed on
|
||||
* the screen. Update logic can write here to put artifacts on the next frame.
|
||||
*/
|
||||
struct artifact *schedGetWriteArtifacts(void)
|
||||
{
|
||||
return g_ArtifactLists[g_SchedWriteArtifactsIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* The front list is the artifact list that is currently being displayed on the
|
||||
* screen. Rendering logic reads this list. The list may be re-used for multiple
|
||||
* frames in a row during lag.
|
||||
*/
|
||||
struct artifact *schedGetFrontArtifacts(void)
|
||||
{
|
||||
return g_ArtifactLists[g_SchedFrontArtifactsIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* The pending list is possibly misnamed. I'm not sure how this list works.
|
||||
*
|
||||
* @TODO: Investigate.
|
||||
*/
|
||||
struct artifact *schedGetPendingArtifacts(void)
|
||||
{
|
||||
return g_ArtifactLists[g_SchedPendingArtifactsIndex];
|
||||
}
|
||||
|
||||
void schedIncrementWriteArtifacts(void)
|
||||
{
|
||||
g_SchedWriteArtifactsIndex = (g_SchedWriteArtifactsIndex + 1) % 3;
|
||||
}
|
||||
|
||||
void schedIncrementFrontArtifacts(void)
|
||||
{
|
||||
g_SchedFrontArtifactsIndex = (g_SchedFrontArtifactsIndex + 1) % 3;
|
||||
}
|
||||
|
||||
void schedIncrementPendingArtifacts(void)
|
||||
{
|
||||
g_SchedPendingArtifactsIndex = (g_SchedPendingArtifactsIndex + 1) % 3;
|
||||
}
|
||||
|
||||
void schedResetArtifacts(void)
|
||||
{
|
||||
g_SchedWriteArtifactsIndex = 0;
|
||||
g_SchedFrontArtifactsIndex = 1;
|
||||
g_SchedPendingArtifactsIndex = 0;
|
||||
}
|
||||
|
||||
void schedUpdatePendingArtifacts(void)
|
||||
{
|
||||
g_SchedSpecialArtifactIndexes[g_SchedPendingArtifactsIndex] = 0;
|
||||
schedIncrementPendingArtifacts();
|
||||
}
|
||||
|
||||
void schedConsiderScreenshot(void)
|
||||
{
|
||||
if (g_MenuData.screenshottimer == 1) {
|
||||
if (IS8MB()) {
|
||||
menugfxCreateBlur();
|
||||
}
|
||||
|
||||
g_MenuData.screenshottimer = 0;
|
||||
}
|
||||
|
||||
if (g_BlurFbCapTimer == 0) {
|
||||
videoCopyFramebuffer(g_BlurFb, 0, -1, -1);
|
||||
g_BlurFbCapTimer = -1;
|
||||
g_BlurFbDirty = false;
|
||||
} else if (g_BlurFbCapTimer > 0) {
|
||||
--g_BlurFbCapTimer;
|
||||
} else if (g_BlurFbCapTimer < 0) {
|
||||
// no blur requested this frame, mark blur fb dirty
|
||||
g_BlurFbDirty = true;
|
||||
}
|
||||
|
||||
if (g_MenuData.screenshottimer >= 2) {
|
||||
g_MenuData.screenshottimer--;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "preprocess/common.h"
|
||||
|
||||
static struct ptrmarker ptrMarkers[MAX_PTR_MARKERS];
|
||||
static int numPtrMarkers;
|
||||
|
||||
void ptrAdd(u32 ptr_src, uintptr_t ptr_host)
|
||||
{
|
||||
if (numPtrMarkers >= ARRAYCOUNT(ptrMarkers)) {
|
||||
sysFatalError("ptrAdd(%p, %p): ptrmarker limit exceeded", (void *)(uintptr_t)ptr_src, (void *)ptr_host);
|
||||
}
|
||||
|
||||
ptrMarkers[numPtrMarkers].ptr_src = ptr_src;
|
||||
ptrMarkers[numPtrMarkers].ptr_host = ptr_host;
|
||||
numPtrMarkers++;
|
||||
}
|
||||
|
||||
struct ptrmarker *ptrFind(uintptr_t ptr_src)
|
||||
{
|
||||
for (int i = 0; i < numPtrMarkers; i++) {
|
||||
if (ptrMarkers[i].ptr_src == ptr_src) {
|
||||
return &ptrMarkers[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ptrReset(void)
|
||||
{
|
||||
numPtrMarkers = 0;
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "bss.h"
|
||||
|
||||
#include "preprocess/common.h"
|
||||
#include "preprocess/gbi.h"
|
||||
|
||||
/**
|
||||
* 4 bytes decompressed size of primary data
|
||||
* 4 bytes compressed size of section 1 in its entirety
|
||||
* 4 bytes compressed size of primary data
|
||||
* Section 1:
|
||||
* Primary: (zipped)
|
||||
* 4 bytes null
|
||||
* 4 bytes pointer to room table
|
||||
* 4 bytes pointer to portal table
|
||||
* 4 bytes pointer to bgcmds
|
||||
* 4 bytes pointer to lights table
|
||||
* 4 bytes null
|
||||
* (room table)
|
||||
* (portal table)
|
||||
* (bgcmds)
|
||||
* (lights table)
|
||||
* room 1 (zipped)
|
||||
* room 2 (zipped)
|
||||
* ...
|
||||
* Section 2:
|
||||
* 2 bytes decompressed size of section (mask with 0x7fff)
|
||||
* 2 bytes compressed size of section
|
||||
* Texture ID list (zipped)
|
||||
* Section 3:
|
||||
* 2 bytes decompressed size of section (mask with 0x7fff)
|
||||
* 2 bytes compressed size of section
|
||||
* Zipped:
|
||||
* (room bbox table)
|
||||
* (list of roomgfxdata sizes)
|
||||
* (list of light counts per room)
|
||||
*/
|
||||
|
||||
struct fileheader {
|
||||
u32 primary_infsize;
|
||||
u32 section1_cmpsize;
|
||||
u32 primary_cmpsize;
|
||||
};
|
||||
|
||||
struct sectionheader {
|
||||
u16 infsize;
|
||||
u16 cmpsize;
|
||||
};
|
||||
|
||||
struct n64_primaryheader {
|
||||
u32 unused1;
|
||||
u32 ptr_rooms;
|
||||
u32 ptr_portals;
|
||||
u32 ptr_bgcmds;
|
||||
u32 ptr_lights;
|
||||
u32 unused2;
|
||||
};
|
||||
|
||||
struct host_primaryheader {
|
||||
uintptr_t unused1;
|
||||
uintptr_t ptr_rooms;
|
||||
uintptr_t ptr_portals;
|
||||
uintptr_t ptr_bgcmds;
|
||||
uintptr_t ptr_lights;
|
||||
uintptr_t unused2;
|
||||
};
|
||||
|
||||
struct n64_bgroom {
|
||||
u32 ptr_gfxdata;
|
||||
struct coord pos;
|
||||
u8 br_light_min;
|
||||
u8 br_light_max;
|
||||
};
|
||||
|
||||
struct n64_roomblock {
|
||||
u8 type;
|
||||
u32 ptr_next;
|
||||
u32 ptr_gdl;
|
||||
u32 ptr_vertices;
|
||||
u32 ptr_colours;
|
||||
};
|
||||
|
||||
struct n64_roomgfxdata {
|
||||
u32 ptr_vertices;
|
||||
u32 ptr_colours;
|
||||
u32 ptr_opablocks;
|
||||
u32 ptr_xlublocks;
|
||||
s16 lightsindex;
|
||||
s16 numlights;
|
||||
s16 numvertices;
|
||||
s16 numcolours;
|
||||
// roomblocks[]
|
||||
};
|
||||
|
||||
struct vtx {
|
||||
s16 coord[3];
|
||||
u8 flag;
|
||||
u8 color;
|
||||
u16 s;
|
||||
u16 t;
|
||||
};
|
||||
|
||||
static void convertPrimaryRooms(u8 *dst, u32 *dstpos, u8 *src, u32 *srcpos)
|
||||
{
|
||||
struct n64_bgroom *n64_rooms = (struct n64_bgroom *) &src[*srcpos];
|
||||
struct bgroom *host_rooms = (struct bgroom *) &dst[*dstpos];
|
||||
|
||||
s32 numRooms = 0;
|
||||
|
||||
for (int i = 1; n64_rooms[i].ptr_gfxdata != 0; i++) {
|
||||
numRooms++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numRooms + 1; i++) {
|
||||
host_rooms[i].unk00 = PD_BE32(n64_rooms[i].ptr_gfxdata);
|
||||
host_rooms[i].pos = PD_SWAPPED_VAL(n64_rooms[i].pos);
|
||||
host_rooms[i].br_light_min = n64_rooms[i].br_light_min;
|
||||
host_rooms[i].br_light_max = n64_rooms[i].br_light_max;
|
||||
}
|
||||
|
||||
*srcpos += sizeof(*n64_rooms) * (numRooms + 2);
|
||||
*dstpos += sizeof(*host_rooms) * (numRooms +2);
|
||||
}
|
||||
|
||||
static void convertPrimaryPortals(u8 *dst, u32 *dstpos, u8 *src, u32 *srcpos, u32 *src_portalvtxs, u32 *dst_portalvtxs)
|
||||
{
|
||||
struct bgportal *n64_portals = (struct bgportal *) &src[*srcpos];
|
||||
struct bgportal *host_portals = (struct bgportal *) &dst[*dstpos];
|
||||
|
||||
// Count portals
|
||||
int numportals = 0;
|
||||
|
||||
for (int i = 0; n64_portals[i].verticesoffset != 0; i++) {
|
||||
numportals++;
|
||||
}
|
||||
|
||||
// Convert portals
|
||||
for (int i = 0; i < numportals; i++) {
|
||||
host_portals[i].verticesoffset = PD_BE16(n64_portals[i].verticesoffset);
|
||||
host_portals[i].roomnum1 = PD_BE16(n64_portals[i].roomnum1);
|
||||
host_portals[i].roomnum2 = PD_BE16(n64_portals[i].roomnum2);
|
||||
host_portals[i].flags = n64_portals[i].flags;
|
||||
}
|
||||
|
||||
*srcpos += sizeof(*n64_portals) * (numportals + 1);
|
||||
*dstpos += sizeof(*host_portals) * (numportals + 1);
|
||||
|
||||
*src_portalvtxs = *srcpos;
|
||||
*dst_portalvtxs = *dstpos;
|
||||
|
||||
while (1) {
|
||||
struct portalvertices *n64_pvertices = (struct portalvertices *)&src[*srcpos];
|
||||
struct portalvertices *host_pvertices = (struct portalvertices *)&dst[*dstpos];
|
||||
|
||||
host_pvertices->count = n64_pvertices->count;
|
||||
|
||||
for (int j = 0; j < host_pvertices->count; j++) {
|
||||
host_pvertices->vertices[j] = PD_SWAPPED_VAL(n64_pvertices->vertices[j]);
|
||||
}
|
||||
|
||||
*srcpos += 4 + host_pvertices->count * 12;
|
||||
*dstpos += 4 + host_pvertices->count * 12;
|
||||
|
||||
if (host_pvertices->count == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
static void convertPrimaryBgCmds(u8 *dst, u32 *dstpos, u8 *src, u32 dst_portalvtxs, u32 src_portalvtxs)
|
||||
{
|
||||
struct bgcmd *n64_cmds = (struct bgcmd *) src;
|
||||
struct bgcmd *host_cmds = (struct bgcmd *) &dst[*dstpos];
|
||||
int num_cmds = 0;
|
||||
|
||||
for (int i = 0; n64_cmds[i].type != 0; i++) {
|
||||
num_cmds++;
|
||||
}
|
||||
|
||||
num_cmds++;
|
||||
|
||||
for (int i = 0; i < num_cmds; i++) {
|
||||
host_cmds[i].type = n64_cmds[i].type;
|
||||
host_cmds[i].len = n64_cmds[i].len;
|
||||
host_cmds[i].param = PD_BE32(n64_cmds[i].param);
|
||||
|
||||
if (host_cmds[i].type == 0x64) {
|
||||
u32 offset = host_cmds[i].param - src_portalvtxs;
|
||||
host_cmds[i].param = dst_portalvtxs + offset;
|
||||
}
|
||||
}
|
||||
|
||||
*dstpos += sizeof(*host_cmds) * num_cmds;
|
||||
}
|
||||
|
||||
static void convertPrimaryLights(u8 *dst, u32 *dstpos, u8 *src, u32 *srcpos, u32 end)
|
||||
{
|
||||
struct light *n64_lights = (struct light *) &src[*srcpos];
|
||||
struct light *host_lights = (struct light *) &dst[*dstpos];
|
||||
int num_lights = (end - *srcpos) / sizeof(struct light);
|
||||
|
||||
for (int i = 0; i < num_lights; i++) {
|
||||
host_lights[i].roomnum = PD_BE16(n64_lights[i].roomnum);
|
||||
host_lights[i].colour = PD_BE16(n64_lights[i].colour);
|
||||
host_lights[i].brightness = n64_lights[i].brightness;
|
||||
host_lights[i].sparkable = n64_lights[i].sparkable;
|
||||
host_lights[i].healthy = n64_lights[i].healthy;
|
||||
host_lights[i].on = n64_lights[i].on;
|
||||
host_lights[i].sparking = n64_lights[i].sparking;
|
||||
host_lights[i].vulnerable = n64_lights[i].vulnerable;
|
||||
host_lights[i].brightnessmult = n64_lights[i].brightnessmult;
|
||||
host_lights[i].dirx = n64_lights[i].dirx;
|
||||
host_lights[i].diry = n64_lights[i].diry;
|
||||
host_lights[i].dirz = n64_lights[i].dirz;
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
host_lights[i].bbox[j].x = PD_BE16(n64_lights[i].bbox[j].x);
|
||||
host_lights[i].bbox[j].y = PD_BE16(n64_lights[i].bbox[j].y);
|
||||
host_lights[i].bbox[j].z = PD_BE16(n64_lights[i].bbox[j].z);
|
||||
}
|
||||
}
|
||||
|
||||
*srcpos += sizeof(*n64_lights) * num_lights;
|
||||
*dstpos += ALIGN(sizeof(*host_lights) * num_lights, 4);
|
||||
}
|
||||
|
||||
#define BLOCK_LEAF 0
|
||||
#define BLOCK_PARENT 1
|
||||
|
||||
void relinkPtr(uintptr_t* ptr)
|
||||
{
|
||||
if (!*ptr) return;
|
||||
|
||||
struct ptrmarker* marker = ptrFind(*ptr);
|
||||
if (!marker) {
|
||||
sysFatalError("[BG] WARNING: Unable to relink pointer: %p\n", ptr);
|
||||
}
|
||||
|
||||
*ptr = marker->ptr_host;
|
||||
}
|
||||
|
||||
static u32 convertRoomGfxData(u8 *dst, u8 *src, u32 infsize, u32 src_ofs)
|
||||
{
|
||||
ptrReset();
|
||||
gbiReset();
|
||||
|
||||
u32 dst_roomoffset = src_ofs;
|
||||
|
||||
uintptr_t gdls_addr[64];
|
||||
int numgdls = 0;
|
||||
|
||||
struct n64_roomgfxdata *src_header = (struct n64_roomgfxdata*)src;
|
||||
struct roomgfxdata *dst_header = (struct roomgfxdata*)dst;
|
||||
|
||||
dst_header->vertices = (void *)(uintptr_t)PD_BE32(src_header->ptr_vertices);
|
||||
dst_header->colours = (void *)(uintptr_t)PD_BE32(src_header->ptr_colours);
|
||||
dst_header->opablocks = (void *)(uintptr_t)PD_BE32(src_header->ptr_opablocks);
|
||||
dst_header->xlublocks = (void *)(uintptr_t)PD_BE32(src_header->ptr_xlublocks);
|
||||
dst_header->lightsindex = PD_BE16(src_header->lightsindex);
|
||||
dst_header->numlights = PD_BE16(src_header->numlights);
|
||||
dst_header->numvertices = PD_BE16(src_header->numvertices);
|
||||
dst_header->numcolours = PD_BE16(src_header->numcolours);
|
||||
|
||||
intptr_t endpos = (uintptr_t)dst_header->vertices - src_ofs;
|
||||
uintptr_t curpos_src = sizeof(struct n64_roomgfxdata);
|
||||
uintptr_t curpos_dst = sizeof(struct roomgfxdata) - sizeof(struct roomblock); // compensate for the [1]
|
||||
|
||||
// roomblocks
|
||||
int ncoords = 0;
|
||||
|
||||
// save the ptr to roomblocks here to iterate later to fix the pointers
|
||||
struct roomblock *ptr_dst_roomblocks = (struct roomblock*)&dst[curpos_dst];
|
||||
|
||||
int numblocks = 0;
|
||||
while ((intptr_t)(curpos_src + sizeof(struct n64_roomblock)) <= endpos) {
|
||||
struct n64_roomblock *src_roomblock = (struct n64_roomblock*)&src[curpos_src];
|
||||
struct roomblock *dest_roomblock = (struct roomblock*)&dst[curpos_dst];
|
||||
|
||||
dest_roomblock->type = src_roomblock->type;
|
||||
dest_roomblock->next = (void *)(uintptr_t)PD_BE32(src_roomblock->ptr_next);
|
||||
dest_roomblock->gdl = (void *)(uintptr_t)PD_BE32(src_roomblock->ptr_gdl);
|
||||
dest_roomblock->vertices = (void *)(uintptr_t)PD_BE32(src_roomblock->ptr_vertices);
|
||||
dest_roomblock->colours = (void *)(uintptr_t)PD_BE32(src_roomblock->ptr_colours);
|
||||
|
||||
ptrAdd(curpos_src + src_ofs, curpos_dst + dst_roomoffset);
|
||||
|
||||
if (src_roomblock->type == BLOCK_PARENT) {
|
||||
ncoords++;
|
||||
uintptr_t vtx_ptr = (uintptr_t)dest_roomblock->vertices - src_ofs;
|
||||
if (vtx_ptr < endpos) endpos = vtx_ptr;
|
||||
}
|
||||
else if (src_roomblock->ptr_gdl) {
|
||||
gdls_addr[numgdls++] = PD_BE32(src_roomblock->ptr_gdl) - src_ofs;
|
||||
}
|
||||
|
||||
curpos_src += sizeof(struct n64_roomblock);
|
||||
curpos_dst += sizeof(struct roomblock);
|
||||
numblocks++;
|
||||
}
|
||||
|
||||
// block coords
|
||||
for (size_t i = 0; i < ncoords; i++) {
|
||||
u32 *src_coord = (u32*)(src + curpos_src);
|
||||
u32 *dst_coord = (u32*)(dst + curpos_dst);
|
||||
|
||||
dst_coord[0] = PD_BE32(src_coord[0]);
|
||||
dst_coord[1] = PD_BE32(src_coord[1]);
|
||||
dst_coord[2] = PD_BE32(src_coord[2]);
|
||||
|
||||
dst_coord[3] = PD_BE32(src_coord[3]);
|
||||
dst_coord[4] = PD_BE32(src_coord[4]);
|
||||
dst_coord[5] = PD_BE32(src_coord[5]);
|
||||
|
||||
ptrAdd(curpos_src + src_ofs, curpos_dst + dst_roomoffset);
|
||||
|
||||
curpos_src += 6 * sizeof(u32);
|
||||
curpos_dst += 6 * sizeof(u32);
|
||||
}
|
||||
|
||||
curpos_dst = ALIGN8(curpos_dst);
|
||||
|
||||
// vertices
|
||||
uintptr_t ptr_src_vertices = curpos_src = ALIGN8((uintptr_t)dst_header->vertices - src_ofs);
|
||||
uintptr_t ptr_dst_vertices = curpos_dst + dst_roomoffset;
|
||||
|
||||
ptrAdd(curpos_src + src_ofs, curpos_dst + dst_roomoffset);
|
||||
|
||||
uintptr_t vtx_end = (uintptr_t)dst_header->colours - src_ofs;
|
||||
|
||||
while (dst_header->colours && curpos_src < vtx_end) {
|
||||
struct vtx *src_vtx = (struct vtx*)(src + curpos_src);
|
||||
struct vtx *dst_vtx = (struct vtx*)(dst + curpos_dst);
|
||||
|
||||
dst_vtx->coord[0] = PD_BE16(src_vtx->coord[0]);
|
||||
dst_vtx->coord[1] = PD_BE16(src_vtx->coord[1]);
|
||||
dst_vtx->coord[2] = PD_BE16(src_vtx->coord[2]);
|
||||
|
||||
dst_vtx->flag = src_vtx->flag;
|
||||
dst_vtx->color = src_vtx->color;
|
||||
|
||||
dst_vtx->s = PD_BE16(src_vtx->s);
|
||||
dst_vtx->t = PD_BE16(src_vtx->t);
|
||||
|
||||
curpos_src += sizeof(struct vtx);
|
||||
curpos_dst += sizeof(struct vtx);
|
||||
}
|
||||
|
||||
curpos_dst = ALIGN8(curpos_dst);
|
||||
|
||||
// colors
|
||||
uintptr_t ptr_src_colors = 0;
|
||||
uintptr_t ptr_dst_colors = 0;
|
||||
|
||||
if (dst_header->colours) {
|
||||
ptr_src_colors = curpos_src = ALIGN8((uintptr_t)dst_header->colours - src_ofs);
|
||||
ptr_dst_colors = curpos_dst + dst_roomoffset;
|
||||
|
||||
ptrAdd(curpos_src + src_ofs, curpos_dst + dst_roomoffset);
|
||||
|
||||
uintptr_t col_end = (numgdls > 0) ? gdls_addr[0] : infsize;
|
||||
size_t col_len = col_end - curpos_src;
|
||||
|
||||
memcpy(dst + curpos_dst, src + curpos_src, col_len);
|
||||
curpos_src += col_len;
|
||||
curpos_dst += col_len;
|
||||
}
|
||||
|
||||
// gdls
|
||||
gbiSetVtx(src_header->ptr_vertices, ptr_src_vertices);
|
||||
gbiSetSegment(0x0e, (uintptr_t)dst_header->vertices);
|
||||
|
||||
uintptr_t gdlstart = curpos_dst;
|
||||
for (size_t i = 0; i < numgdls; i++) {
|
||||
ptrAdd(gdls_addr[i] + src_ofs, curpos_dst + dst_roomoffset);
|
||||
curpos_dst = gbiConvertGdl(dst, curpos_dst, src, gdls_addr[i], 1);
|
||||
}
|
||||
|
||||
// relink ptrs: roomblocks
|
||||
for (size_t i = 0; i < numblocks; i++) {
|
||||
struct roomblock *block = (struct roomblock*)&ptr_dst_roomblocks[i];
|
||||
|
||||
relinkPtr((uintptr_t*)&block->next);
|
||||
|
||||
if (block->type == BLOCK_LEAF) {
|
||||
relinkPtr((uintptr_t*)&block->gdl);
|
||||
u32 offset_vtx = (uintptr_t)block->vertices - ptr_src_vertices - src_ofs;
|
||||
u32 offset_col = (uintptr_t)block->colours - ptr_src_colors - src_ofs;
|
||||
|
||||
block->vertices = (void *)(ptr_dst_vertices + offset_vtx);
|
||||
block->colours = (void *)(ptr_dst_colors + offset_col);
|
||||
}
|
||||
else {
|
||||
relinkPtr((uintptr_t*)&block->gdl); // child
|
||||
relinkPtr((uintptr_t*)&block->vertices); // coord
|
||||
}
|
||||
}
|
||||
|
||||
// relink ptrs: header
|
||||
relinkPtr((uintptr_t*)&dst_header->vertices);
|
||||
relinkPtr((uintptr_t*)&dst_header->colours);
|
||||
relinkPtr((uintptr_t*)&dst_header->opablocks);
|
||||
relinkPtr((uintptr_t*)&dst_header->xlublocks);
|
||||
|
||||
return curpos_dst;
|
||||
}
|
||||
|
||||
static u32 convertSection1(u8 *dst, u8 *src, u32 ofs)
|
||||
{
|
||||
// Convert primary
|
||||
u32 srcpos = 0;
|
||||
u32 dstpos = 0;
|
||||
struct host_primaryheader *host_primary_header = (struct host_primaryheader *) dst;
|
||||
struct n64_primaryheader *n64_primary_header = (struct n64_primaryheader *) src;
|
||||
srcpos += sizeof(struct n64_primaryheader);
|
||||
dstpos += sizeof(*host_primary_header);
|
||||
|
||||
u32 src_bgcmds = PD_BE32(n64_primary_header->ptr_bgcmds) - ofs;
|
||||
|
||||
host_primary_header->unused1 = 0;
|
||||
host_primary_header->unused2 = 0;
|
||||
|
||||
host_primary_header->ptr_rooms = dstpos + ofs;
|
||||
convertPrimaryRooms(dst, &dstpos, src, &srcpos);
|
||||
|
||||
if (n64_primary_header->ptr_lights) {
|
||||
host_primary_header->ptr_lights = dstpos + ofs;
|
||||
srcpos = PD_BE32(n64_primary_header->ptr_lights) - ofs;
|
||||
convertPrimaryLights(dst, &dstpos, src, &srcpos, src_bgcmds);
|
||||
}
|
||||
|
||||
host_primary_header->ptr_portals = dstpos + ofs;
|
||||
srcpos = PD_BE32(n64_primary_header->ptr_portals) - ofs;
|
||||
u32 src_portalvtxs, dst_portalvtxs;
|
||||
convertPrimaryPortals(dst, &dstpos, src, &srcpos, &src_portalvtxs, &dst_portalvtxs);
|
||||
|
||||
host_primary_header->ptr_bgcmds = dstpos + ofs;
|
||||
convertPrimaryBgCmds(dst, &dstpos, &src[src_bgcmds], dst_portalvtxs, src_portalvtxs);
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
void preprocessBgSection1(u8 *data, u32 size, u32 ofs)
|
||||
{
|
||||
u8 *dst = sysMemZeroAlloc(size);
|
||||
|
||||
u32 newSize = convertSection1(dst, data, ofs);
|
||||
|
||||
if (newSize > size) {
|
||||
sysFatalError("overflow when trying to preprocess a bg file, size %d newsize %d", size, newSize);
|
||||
}
|
||||
|
||||
memcpy(data, dst, newSize);
|
||||
sysMemFree(dst);
|
||||
}
|
||||
|
||||
u32 preprocessBgRoom(u8 *data, u32 size, u32 room_ofs)
|
||||
{
|
||||
size *= 2;
|
||||
u8 *dst = sysMemZeroAlloc(size);
|
||||
u32 newSize = convertRoomGfxData(dst, data, size, room_ofs);
|
||||
|
||||
if (newSize > size) {
|
||||
sysFatalError("overflow when trying to preprocess a bg room, size %d newsize %d", size, newSize);
|
||||
}
|
||||
|
||||
memcpy(data, dst, newSize);
|
||||
sysMemFree(dst);
|
||||
|
||||
return newSize;
|
||||
}
|
||||
|
||||
void preprocessBgSection1Header(u8* data, u32 size)
|
||||
{
|
||||
u32* header = (u32*)data;
|
||||
PD_SWAP_VAL(header[0]); // inflatedsize
|
||||
PD_SWAP_VAL(header[1]); // section1size
|
||||
PD_SWAP_VAL(header[2]); // primcompsize
|
||||
}
|
||||
|
||||
void preprocessBgSection2Header(u8* data, u32 size)
|
||||
{
|
||||
u16* header = (u16*)data;
|
||||
PD_SWAP_VAL(header[0]); // inflatedsize
|
||||
PD_SWAP_VAL(header[1]); // section2compsize
|
||||
}
|
||||
|
||||
void preprocessBgSection2(u8* data, u32 size)
|
||||
{
|
||||
// section2 is a texture id list
|
||||
u16* section2 = (u16*)data;
|
||||
for (s32 i = 0; i < size; ++i) {
|
||||
PD_SWAP_VAL(section2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void preprocessBgSection3Header(u8* data, u32 size)
|
||||
{
|
||||
u16* header = (u16*)data;
|
||||
PD_SWAP_VAL(header[0]);
|
||||
PD_SWAP_VAL(header[1]);
|
||||
}
|
||||
|
||||
void preprocessBgSection3(u8* data, u32 size)
|
||||
{
|
||||
// room bounding boxes, 6x s16 per room
|
||||
s16* bbox = (s16*)data;
|
||||
for (s32 i = 1; i < g_Vars.roomcount; ++i) {
|
||||
PD_SWAP_VAL(*bbox); ++bbox;
|
||||
PD_SWAP_VAL(*bbox); ++bbox;
|
||||
PD_SWAP_VAL(*bbox); ++bbox;
|
||||
PD_SWAP_VAL(*bbox); ++bbox;
|
||||
PD_SWAP_VAL(*bbox); ++bbox;
|
||||
PD_SWAP_VAL(*bbox); ++bbox;
|
||||
}
|
||||
|
||||
// gfxdatalen list
|
||||
u16* gfxdatalen = (u16*)bbox;
|
||||
for (s32 i = 1; i < g_Vars.roomcount; ++i) {
|
||||
PD_SWAP_VAL(*gfxdatalen); ++gfxdatalen;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "preprocess/common.h"
|
||||
|
||||
static int getNextNonZero(u8 *src, u32 offset, size_t len)
|
||||
{
|
||||
u32 *ptr = (u32 *) &src[offset];
|
||||
u32 *end = (u32 *) &src[len];
|
||||
|
||||
while (ptr < end) {
|
||||
if (*ptr != 0) {
|
||||
return PD_BE32(*ptr);
|
||||
}
|
||||
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getNumStrings(u8 *src, size_t len)
|
||||
{
|
||||
u32 ptr_table_len = getNextNonZero(src, 0, len);
|
||||
|
||||
if (ptr_table_len) {
|
||||
return ptr_table_len / sizeof(u32);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u32 convertLangFile(u8 *dst, u8 *src, size_t srclen)
|
||||
{
|
||||
// Convert it
|
||||
u32 *src_offsets = (u32 *) src;
|
||||
size_t src_ptr_table_len = PD_BE32(src_offsets[0]);
|
||||
size_t src_str_table_len = srclen - src_ptr_table_len;
|
||||
int num_strings = getNumStrings(src, srclen);
|
||||
size_t dst_ptr_table_len = num_strings * sizeof(uintptr_t);
|
||||
size_t dst_str_table_len = src_str_table_len;
|
||||
|
||||
int cur_dst_offset = dst_ptr_table_len;
|
||||
|
||||
uintptr_t *dst_offsets = (uintptr_t *) dst;
|
||||
|
||||
for (int i = 0; i < num_strings; i++) {
|
||||
u32 src_offset = PD_BE32(src_offsets[i]);
|
||||
|
||||
if (src_offset) {
|
||||
// Write pointer
|
||||
dst_offsets[i] = (cur_dst_offset);
|
||||
|
||||
// Write string
|
||||
u32 end = getNextNonZero(src, (i + 1) * 4, num_strings * 4);
|
||||
|
||||
if (!end) {
|
||||
end = srclen;
|
||||
}
|
||||
|
||||
size_t len = end - src_offset;
|
||||
len = ALIGN4(len);
|
||||
memcpy(&dst[cur_dst_offset], &src[src_offset], len);
|
||||
|
||||
cur_dst_offset += len;
|
||||
} else {
|
||||
// Write pointer
|
||||
dst_offsets[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ALIGN16(cur_dst_offset);
|
||||
}
|
||||
|
||||
u8 *preprocessLangFile(u8 *data, u32 size, u32 *outSize) {
|
||||
u32 newSizeEstimated = romdataFileGetEstimatedSize(size, LOADTYPE_LANG);
|
||||
u8 *dst = sysMemZeroAlloc(newSizeEstimated);
|
||||
|
||||
u32 newSize = convertLangFile(dst, data, size);
|
||||
|
||||
if (newSize > newSizeEstimated) {
|
||||
sysFatalError("overflow when trying to preprocess a lang file, size %d newsize %d", size, newSize);
|
||||
}
|
||||
|
||||
memcpy(data, dst, newSize);
|
||||
sysMemFree(dst);
|
||||
|
||||
*outSize = newSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,292 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "preprocess/common.h"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
struct n64_header {
|
||||
s32 num_pads;
|
||||
s32 num_covers;
|
||||
u32 ptr_waypoints;
|
||||
u32 ptr_waygroups;
|
||||
u32 ptr_cover;
|
||||
};
|
||||
|
||||
struct host_header {
|
||||
s32 num_pads;
|
||||
s32 num_covers;
|
||||
uintptr_t ptr_waypoints;
|
||||
uintptr_t ptr_waygroups;
|
||||
uintptr_t ptr_cover;
|
||||
};
|
||||
|
||||
struct padheader {
|
||||
u32 flags : 18;
|
||||
u32 room : 10;
|
||||
u32 liftnum : 4;
|
||||
};
|
||||
|
||||
struct n64_waypoint {
|
||||
s32 padnum;
|
||||
u32 ptr_neighbours;
|
||||
s32 groupnum;
|
||||
s32 step;
|
||||
};
|
||||
|
||||
struct n64_waygroup {
|
||||
u32 ptr_neighbours;
|
||||
u32 ptr_waypoints;
|
||||
s32 step;
|
||||
};
|
||||
|
||||
static u32 convertPads(u8 *dst, u32 dstpos, u8 *src, u32 srcpos, int num_pads)
|
||||
{
|
||||
u16 *src_offsets = (u16 *) &src[srcpos];
|
||||
u16 *dst_offsets = (u16 *) &dst[dstpos];
|
||||
|
||||
dstpos += num_pads * sizeof(u16);
|
||||
|
||||
for (int i = 0; i < num_pads; i++) {
|
||||
srcpos = PD_BE16(src_offsets[i]);
|
||||
|
||||
dst_offsets[i] = (dstpos);
|
||||
|
||||
// Header
|
||||
u32 n64_padheader = PD_BE32(*(u32 *) &src[srcpos]);
|
||||
struct padheader *host_padheader = (struct padheader *) &dst[dstpos];
|
||||
u32 flags = (n64_padheader >> 14) & 0x3ffff;
|
||||
|
||||
*(u32*)host_padheader = (n64_padheader);
|
||||
|
||||
srcpos += sizeof(struct padheader);
|
||||
dstpos += sizeof(struct padheader);
|
||||
|
||||
// Position
|
||||
if (flags & PADFLAG_INTPOS) {
|
||||
s16 *srcptr = (s16 *) &src[srcpos];
|
||||
s16 *dstptr = (s16 *) &dst[dstpos];
|
||||
|
||||
dstptr[0] = PD_BE16(srcptr[0]);
|
||||
dstptr[1] = PD_BE16(srcptr[1]);
|
||||
dstptr[2] = PD_BE16(srcptr[2]);
|
||||
|
||||
srcpos += 8;
|
||||
dstpos += 8;
|
||||
} else {
|
||||
u32 *srcptr = (u32 *) &src[srcpos];
|
||||
u32 *dstptr = (u32 *) &dst[dstpos];
|
||||
|
||||
dstptr[0] = PD_BE32(srcptr[0]);
|
||||
dstptr[1] = PD_BE32(srcptr[1]);
|
||||
dstptr[2] = PD_BE32(srcptr[2]);
|
||||
|
||||
srcpos += 12;
|
||||
dstpos += 12;
|
||||
}
|
||||
|
||||
// Up
|
||||
if ((flags & (PADFLAG_UPALIGNTOX | PADFLAG_UPALIGNTOY | PADFLAG_UPALIGNTOZ)) == 0) {
|
||||
u32 *srcptr = (u32 *) &src[srcpos];
|
||||
u32 *dstptr = (u32 *) &dst[dstpos];
|
||||
|
||||
dstptr[0] = PD_BE32(srcptr[0]);
|
||||
dstptr[1] = PD_BE32(srcptr[1]);
|
||||
dstptr[2] = PD_BE32(srcptr[2]);
|
||||
|
||||
srcpos += 12;
|
||||
dstpos += 12;
|
||||
}
|
||||
|
||||
// Look
|
||||
if ((flags & (PADFLAG_LOOKALIGNTOX | PADFLAG_LOOKALIGNTOY | PADFLAG_LOOKALIGNTOZ)) == 0) {
|
||||
u32 *srcptr = (u32 *) &src[srcpos];
|
||||
u32 *dstptr = (u32 *) &dst[dstpos];
|
||||
|
||||
dstptr[0] = PD_BE32(srcptr[0]);
|
||||
dstptr[1] = PD_BE32(srcptr[1]);
|
||||
dstptr[2] = PD_BE32(srcptr[2]);
|
||||
|
||||
srcpos += 12;
|
||||
dstpos += 12;
|
||||
}
|
||||
|
||||
// Bbox
|
||||
if (flags & PADFLAG_HASBBOXDATA) {
|
||||
u32 *srcptr = (u32 *) &src[srcpos];
|
||||
u32 *dstptr = (u32 *) &dst[dstpos];
|
||||
|
||||
dstptr[0] = PD_BE32(srcptr[0]);
|
||||
dstptr[1] = PD_BE32(srcptr[1]);
|
||||
dstptr[2] = PD_BE32(srcptr[2]);
|
||||
dstptr[3] = PD_BE32(srcptr[3]);
|
||||
dstptr[4] = PD_BE32(srcptr[4]);
|
||||
dstptr[5] = PD_BE32(srcptr[5]);
|
||||
|
||||
srcpos += 4 * 6;
|
||||
dstpos += 4 * 6;
|
||||
}
|
||||
}
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertWayPoints(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
struct n64_waypoint *n64_waypoints = (struct n64_waypoint *) &src[srcpos];
|
||||
struct waypoint *host_waypoints = (struct waypoint *) &dst[dstpos];
|
||||
int num_waypoints;
|
||||
|
||||
for (num_waypoints = 0; n64_waypoints[num_waypoints].padnum != -1; num_waypoints++);
|
||||
|
||||
dstpos += (num_waypoints + 1) * sizeof(struct waypoint);
|
||||
|
||||
u32 *host_neighbours = (u32 *) &dst[dstpos];
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < num_waypoints; i++) {
|
||||
host_waypoints[i].padnum = PD_BE32(n64_waypoints[i].padnum);
|
||||
host_waypoints[i].neighbours = (void *)(uintptr_t)dstpos;
|
||||
host_waypoints[i].groupnum = PD_BE32(n64_waypoints[i].groupnum);
|
||||
host_waypoints[i].step = 0;
|
||||
|
||||
u32 *n64_neighbours = (u32 *) &src[PD_BE32(n64_waypoints[i].ptr_neighbours)];
|
||||
|
||||
for (int j = 0; n64_neighbours[j] != 0xffffffff; j++) {
|
||||
host_neighbours[n++] = PD_BE32(n64_neighbours[j]);
|
||||
dstpos += 4;
|
||||
}
|
||||
|
||||
host_neighbours[n++] = 0xffffffff;
|
||||
dstpos += 4;
|
||||
}
|
||||
|
||||
// Terminator
|
||||
host_waypoints[num_waypoints].padnum = 0xffffffff;
|
||||
host_waypoints[num_waypoints].neighbours = NULL;
|
||||
host_waypoints[num_waypoints].groupnum = 0;
|
||||
host_waypoints[num_waypoints].step = 0;
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertWayGroups(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
struct n64_waygroup *n64_waygroups = (struct n64_waygroup *) &src[srcpos];
|
||||
struct waygroup *host_waygroups = (struct waygroup *) &dst[dstpos];
|
||||
int num_waygroups;
|
||||
|
||||
for (num_waygroups = 0; n64_waygroups[num_waygroups].ptr_neighbours != 0; num_waygroups++);
|
||||
|
||||
dstpos += (num_waygroups + 1) * sizeof(struct waygroup);
|
||||
|
||||
// Waygroups and child waypoints
|
||||
u32 *host_waypoints = (u32 *) &dst[dstpos];
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < num_waygroups; i++) {
|
||||
host_waygroups[i].waypoints = (void *)(uintptr_t)dstpos;
|
||||
host_waygroups[i].step = 0;
|
||||
|
||||
u32 *n64_waypoints = (u32 *) &src[PD_BE32(n64_waygroups[i].ptr_waypoints)];
|
||||
|
||||
for (int j = 0; n64_waypoints[j] != 0xffffffff; j++) {
|
||||
host_waypoints[n++] = PD_BE32(n64_waypoints[j]);
|
||||
dstpos += 4;
|
||||
}
|
||||
|
||||
host_waypoints[n++] = 0xffffffff;
|
||||
dstpos += 4;
|
||||
}
|
||||
|
||||
// Terminator
|
||||
host_waygroups[num_waygroups].neighbours = NULL;
|
||||
host_waygroups[num_waygroups].waypoints = NULL;
|
||||
host_waygroups[num_waygroups].step = 0;
|
||||
|
||||
// Waygroup neighbours
|
||||
u32 *host_neighbours = (u32 *) &dst[dstpos];
|
||||
n = 0;
|
||||
|
||||
for (int i = 0; i < num_waygroups; i++) {
|
||||
host_waygroups[i].neighbours = (void *)(uintptr_t)dstpos;
|
||||
|
||||
u32 *n64_neighbours = (u32 *) &src[PD_BE32(n64_waygroups[i].ptr_neighbours)];
|
||||
|
||||
for (int j = 0; n64_neighbours[j] != 0xffffffff; j++) {
|
||||
host_neighbours[n++] = PD_BE32(n64_neighbours[j]);
|
||||
dstpos += 4;
|
||||
}
|
||||
|
||||
host_neighbours[n++] = 0xffffffff;
|
||||
dstpos += 4;
|
||||
}
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertCover(u8 *dst, u32 dstpos, u8 *src, u32 srcpos, int num_covers)
|
||||
{
|
||||
struct coverdefinition *n64_covers = (struct coverdefinition *) &src[srcpos];
|
||||
struct coverdefinition *host_covers = (struct coverdefinition *) &dst[dstpos];
|
||||
|
||||
for (int i = 0; i < num_covers; i++) {
|
||||
host_covers[i].pos = PD_SWAPPED_VAL(n64_covers[i].pos);
|
||||
host_covers[i].look = PD_SWAPPED_VAL(n64_covers[i].look);
|
||||
host_covers[i].flags = PD_BE16(n64_covers[i].flags);
|
||||
}
|
||||
|
||||
dstpos += num_covers * sizeof(struct coverdefinition);
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertPadsFile(u8 *dst, u8 *src)
|
||||
{
|
||||
u32 dstpos = 0;
|
||||
struct n64_header *n64_header = (struct n64_header *) src;
|
||||
struct host_header *host_header = (struct host_header *) dst;
|
||||
|
||||
int num_pads = PD_BE32(n64_header->num_pads);
|
||||
int num_covers = PD_BE32(n64_header->num_covers);
|
||||
host_header->num_pads = (num_pads);
|
||||
host_header->num_covers = (num_covers);
|
||||
|
||||
dstpos += sizeof(struct host_header);
|
||||
|
||||
// Pads
|
||||
dstpos = convertPads(dst, dstpos, src, sizeof(struct n64_header), num_pads);
|
||||
|
||||
// Waypoints
|
||||
host_header->ptr_waypoints = (dstpos);
|
||||
dstpos = convertWayPoints(dst, dstpos, src, PD_BE32(n64_header->ptr_waypoints));
|
||||
|
||||
// Waygroups
|
||||
host_header->ptr_waygroups = (dstpos);
|
||||
dstpos = convertWayGroups(dst, dstpos, src, PD_BE32(n64_header->ptr_waygroups));
|
||||
|
||||
// Cover
|
||||
host_header->ptr_cover = (dstpos);
|
||||
dstpos = convertCover(dst, dstpos, src, PD_BE32(n64_header->ptr_cover), num_covers);
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
u8* preprocessPadsFile(u8 *data, u32 size, u32 *outSize) {
|
||||
u32 newSizeEstimated = romdataFileGetEstimatedSize(size, LOADTYPE_PADS);
|
||||
u8* dst = sysMemZeroAlloc(newSizeEstimated);
|
||||
|
||||
u32 newSize = convertPadsFile(dst, data);
|
||||
|
||||
if (newSize > newSizeEstimated) {
|
||||
sysFatalError("overflow when trying to preprocess a pads file, size %d newsize %d", size, newSize);
|
||||
}
|
||||
|
||||
memcpy(data, dst, newSize);
|
||||
sysMemFree(dst);
|
||||
|
||||
*outSize = newSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,103 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "preprocess/common.h"
|
||||
|
||||
struct tile {
|
||||
u8 type;
|
||||
u8 numvertices;
|
||||
u16 flags;
|
||||
u16 floortype;
|
||||
u8 xmin;
|
||||
u8 ymin;
|
||||
u8 zmin;
|
||||
u8 xmax;
|
||||
u8 ymax;
|
||||
u8 zmax;
|
||||
u16 floorcol;
|
||||
};
|
||||
|
||||
static u32 convertTiles(u8 *dst, u8 *src, size_t srclen)
|
||||
{
|
||||
int num_rooms = PD_BE32(*(u32 *) src);
|
||||
|
||||
size_t src_ptr_table_len = (num_rooms + 1) * 4;
|
||||
size_t dst_ptr_table_len = (num_rooms + 1) * sizeof(u32);
|
||||
|
||||
u32 *src_offsets = (u32 *) &src[4];
|
||||
|
||||
size_t data_len = srclen - src_ptr_table_len - 4;
|
||||
|
||||
*(u32 *) dst = (num_rooms);
|
||||
|
||||
u32 *dst_offsets = (u32 *) (dst + sizeof(u32));
|
||||
u32 cur_dst_offset = dst_ptr_table_len + sizeof(u32);
|
||||
|
||||
u32 cur_src_offset = PD_BE32(src_offsets[0]);
|
||||
u32 end_src_offset = PD_BE32(src_offsets[num_rooms]);
|
||||
int room = 0;
|
||||
|
||||
while (1) {
|
||||
// Check if this is a new room and write the pointer offset if so
|
||||
while (room < (num_rooms + 1) && PD_BE32(src_offsets[room]) == cur_src_offset) {
|
||||
dst_offsets[room] = (cur_dst_offset);
|
||||
room++;
|
||||
}
|
||||
|
||||
if (cur_src_offset >= end_src_offset) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Write tile data
|
||||
struct tile *n64tile = (struct tile *) &src[cur_src_offset];
|
||||
struct tile hosttile;
|
||||
|
||||
hosttile.type = n64tile->type;
|
||||
hosttile.numvertices = n64tile->numvertices;
|
||||
hosttile.flags = PD_BE16(n64tile->flags);
|
||||
hosttile.floortype = PD_BE16(n64tile->floortype);
|
||||
hosttile.xmin = n64tile->xmin;
|
||||
hosttile.ymin = n64tile->ymin;
|
||||
hosttile.zmin = n64tile->zmin;
|
||||
hosttile.xmax = n64tile->xmax;
|
||||
hosttile.ymax = n64tile->ymax;
|
||||
hosttile.zmax = n64tile->zmax;
|
||||
hosttile.floorcol = PD_BE16(n64tile->floorcol);
|
||||
|
||||
memcpy(&dst[cur_dst_offset], &hosttile, sizeof(struct tile));
|
||||
|
||||
cur_dst_offset += sizeof(struct tile);
|
||||
cur_src_offset += sizeof(struct tile);
|
||||
|
||||
// Write tile vertices
|
||||
for (int i = 0; i < hosttile.numvertices; i++) {
|
||||
*(s16 *) &dst[cur_dst_offset + 0] = PD_BE16(*(s16 *) &src[cur_src_offset + 0]);
|
||||
*(s16 *) &dst[cur_dst_offset + 2] = PD_BE16(*(s16 *) &src[cur_src_offset + 2]);
|
||||
*(s16 *) &dst[cur_dst_offset + 4] = PD_BE16(*(s16 *) &src[cur_src_offset + 4]);
|
||||
cur_dst_offset += 6;
|
||||
cur_src_offset += 6;
|
||||
}
|
||||
}
|
||||
|
||||
return ALIGN16(cur_dst_offset);
|
||||
}
|
||||
|
||||
u8 *preprocessTilesFile(u8 *data, u32 size, u32 *outSize)
|
||||
{
|
||||
u32 newSizeEstimated = romdataFileGetEstimatedSize(size, LOADTYPE_TILES);
|
||||
u8 *dst = sysMemZeroAlloc(newSizeEstimated);
|
||||
|
||||
u32 newSize = convertTiles(dst, data, size);
|
||||
|
||||
if (newSize > newSizeEstimated) {
|
||||
sysFatalError("overflow when trying to preprocess a tiles file, size %d newsize %d", size, newSize);
|
||||
}
|
||||
|
||||
memcpy(data, dst, newSize);
|
||||
sysMemFree(dst);
|
||||
|
||||
*outSize = newSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "preprocess/common.h"
|
||||
#include "preprocess/gbi.h"
|
||||
|
||||
#if UINTPTR_MAX > 0xFFFFFFFFu
|
||||
#define HOST_DWORDS_PER_CMD 2
|
||||
#else
|
||||
#define HOST_DWORDS_PER_CMD 1
|
||||
#endif
|
||||
|
||||
struct vtx {
|
||||
s16 x;
|
||||
s16 y;
|
||||
s16 z;
|
||||
u8 flags;
|
||||
u8 colour;
|
||||
s16 s;
|
||||
s16 t;
|
||||
};
|
||||
|
||||
struct texaddr {
|
||||
u32 src;
|
||||
u32 dst;
|
||||
};
|
||||
|
||||
static u32 gbiSegments[16];
|
||||
static u32 srcVtxOffset;
|
||||
static u32 dstVtxOffset;
|
||||
|
||||
static struct texaddr texAddrs[64];
|
||||
static int numTexAddrs;
|
||||
|
||||
void gbiReset(void)
|
||||
{
|
||||
for (int i = 0; i < ARRAYCOUNT(gbiSegments); i++) {
|
||||
gbiSegments[i] = 0;
|
||||
}
|
||||
|
||||
srcVtxOffset = 0;
|
||||
dstVtxOffset = 0;
|
||||
numTexAddrs = 0;
|
||||
}
|
||||
|
||||
void gbiSetSegment(int segment, u32 offset)
|
||||
{
|
||||
gbiSegments[segment] = offset & 0x00ffffff;
|
||||
}
|
||||
|
||||
void gbiSetVtx(u32 src_offset, u32 dst_offset)
|
||||
{
|
||||
srcVtxOffset = src_offset & 0x00ffffff;
|
||||
dstVtxOffset = dst_offset & 0x00ffffff;
|
||||
}
|
||||
|
||||
void gbiAddTexAddr(u32 src_offset, u32 dst_offset)
|
||||
{
|
||||
texAddrs[numTexAddrs].src = src_offset & 0x00ffffff;
|
||||
texAddrs[numTexAddrs++].dst = dst_offset & 0x00ffffff;
|
||||
}
|
||||
|
||||
u32 gbiFindTexAddr(u32 src_offset)
|
||||
{
|
||||
for (int i = 0; i < numTexAddrs; i++) {
|
||||
if (texAddrs[i].src == src_offset)
|
||||
return texAddrs[i].dst;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gbiConvertVtx(u8 *dst, u32 offset, int count)
|
||||
{
|
||||
if (!offset) return;
|
||||
struct vtx* vtxs = (struct vtx*)(dst + offset);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
struct vtx* vtx = &vtxs[i];
|
||||
|
||||
vtx->x = PD_BE16(vtx->x);
|
||||
vtx->y = PD_BE16(vtx->y);
|
||||
vtx->z = PD_BE16(vtx->z);
|
||||
vtx->s = PD_BE16(vtx->s);
|
||||
vtx->t = PD_BE16(vtx->t);
|
||||
}
|
||||
}
|
||||
|
||||
#define CMD_IS_VTX(cmd) ((cmd & 0xff00000000000000) == 0x0400000000000000)
|
||||
#define CMD_IS_DL(cmd) ((cmd & 0xff00000000000000) == 0x0600000000000000)
|
||||
#define CMD_IS_COL(cmd) ((cmd & 0xff00000000000000) == 0x0700000000000000)
|
||||
#define CMD_IS_ENDDL(cmd) ((cmd & 0xff00000000000000) == 0xb800000000000000)
|
||||
#define CMD_IS_MOVEWORD(cmd) ((cmd & 0xff00000000000000) == 0xbc00000000000000)
|
||||
#define CMD_IS_MOVEMEM(cmd) ((cmd & 0xff00000000000000) == 0x0300000000000000)
|
||||
#define CMD_IS_MTX(cmd) ((cmd & 0xff00000000000000) == 0x0100000000000000)
|
||||
#define CMD_IS_SETTIMG(cmd) ((cmd & 0xff00000000000000) == 0xfd00000000000000)
|
||||
#define CMD_IS_SETZIMG(cmd) ((cmd & 0xff00000000000000) == 0xfe00000000000000)
|
||||
#define CMD_IS_SETCIMG(cmd) ((cmd & 0xff00000000000000) == 0xff00000000000000)
|
||||
|
||||
#define CMD_HAS_ADDR(cmd) CMD_IS_VTX(cmd) \
|
||||
|| CMD_IS_COL(cmd) \
|
||||
|| CMD_IS_MOVEWORD(cmd) \
|
||||
|| CMD_IS_SETTIMG(cmd)
|
||||
|
||||
|
||||
#define CMD_IS_SEGMENTED(cmd) ( CMD_IS_MOVEMEM(cmd) \
|
||||
|| CMD_IS_MTX(cmd) \
|
||||
|| CMD_IS_VTX(cmd) \
|
||||
|| CMD_IS_COL(cmd) \
|
||||
|| CMD_IS_DL(cmd) \
|
||||
|| CMD_IS_SETTIMG(cmd) \
|
||||
|| CMD_IS_SETCIMG(cmd) \
|
||||
|| CMD_IS_SETZIMG(cmd))
|
||||
|
||||
/**
|
||||
* Segment 5 is the start of the model file. The data in these files shifts
|
||||
* depending on the pointer size, so this function adjusts the offset accordingly.
|
||||
*/
|
||||
static u64 gbiRewriteAddr(u64 cmd, u8 opcode)
|
||||
{
|
||||
u8 segment = (cmd & 0x0f000000) >> 24;
|
||||
u32 offset = cmd & 0x00ffffff;
|
||||
|
||||
if (segment == 5) {
|
||||
if (opcode == 0xfd) {
|
||||
offset = gbiFindTexAddr(offset);
|
||||
}
|
||||
else {
|
||||
if (offset < srcVtxOffset) {
|
||||
sysFatalError("Tried to load data from offset 0x%x but segment starts at 0x%x\n", offset, srcVtxOffset);
|
||||
}
|
||||
|
||||
offset = offset - srcVtxOffset + dstVtxOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return (cmd & 0xffffffffff000000) | offset;
|
||||
}
|
||||
|
||||
void gbiGdlRewriteAddrs(u8 *dst, u32 offset)
|
||||
{
|
||||
if (!offset) return;
|
||||
|
||||
u64 *cmds = (u64 *)(dst + (offset & 0x00ffffff));
|
||||
if (!cmds) return;
|
||||
|
||||
u64 cmd;
|
||||
|
||||
do {
|
||||
cmd = *cmds;
|
||||
Gfx* gfxcmd = (Gfx *)cmds;
|
||||
int idx = 0;
|
||||
u8 opcode = (cmd >> 24) & 0xff;
|
||||
|
||||
#if HOST_DWORDS_PER_CMD == 2
|
||||
idx = 1;
|
||||
opcode = (cmd >> 24) & 0xff;
|
||||
#endif
|
||||
cmd = (u64)opcode << 56;
|
||||
|
||||
if (CMD_HAS_ADDR(cmd)) {
|
||||
cmds[idx] = gbiRewriteAddr(cmds[idx], opcode);
|
||||
}
|
||||
|
||||
if (CMD_IS_SEGMENTED(cmd)) {
|
||||
gfxcmd->words.w1 |= 1;
|
||||
}
|
||||
|
||||
cmds += HOST_DWORDS_PER_CMD;
|
||||
} while (!CMD_IS_ENDDL(cmd));
|
||||
}
|
||||
|
||||
u32 gbiConvertGdl(u8 *dst, u32 dstpos, u8 *src, u32 srcpos, int segment_cmds)
|
||||
{
|
||||
dstpos = ALIGN8(dstpos);
|
||||
|
||||
u64 *n64_cmd = (u64*)&src[srcpos];
|
||||
u64 *host_cmd = (u64*)&dst[dstpos];
|
||||
u64 cmd;
|
||||
|
||||
do {
|
||||
cmd = PD_BE64(*n64_cmd);
|
||||
|
||||
#if HOST_DWORDS_PER_CMD == 2
|
||||
host_cmd[0] = ((cmd & 0xffffffff00000000) >> 32);
|
||||
host_cmd[1] = (cmd & 0xffffffff);
|
||||
|
||||
if (segment_cmds && CMD_IS_SEGMENTED(cmd)) {
|
||||
host_cmd[1] |= 1;
|
||||
}
|
||||
#else
|
||||
host_cmd[0] = (cmd >> 32) | ((cmd & 0xffffffff) << 32);
|
||||
|
||||
if (segment_cmds && CMD_IS_SEGMENTED(cmd)) {
|
||||
Gfx* gfxcmd = (Gfx*)host_cmd;
|
||||
gfxcmd->words.w1 |= 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
dstpos += sizeof(*host_cmd) * HOST_DWORDS_PER_CMD;
|
||||
host_cmd += HOST_DWORDS_PER_CMD;
|
||||
n64_cmd++;
|
||||
} while (!CMD_IS_ENDDL(cmd));
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include <PR/gbi.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "bss.h"
|
||||
#include "game/setuputils.h"
|
||||
#include "game/texdecompress.h"
|
||||
#include "mod.h"
|
||||
|
||||
#include "preprocess/common.h"
|
||||
|
||||
u8 *preprocessAnimations(u8* data, u32 size, u32* outSize)
|
||||
{
|
||||
// set the anim table pointers as well
|
||||
extern u8 *_animationsTableRomStart;
|
||||
extern u8 *_animationsTableRomEnd;
|
||||
|
||||
// the animation table is at the end of the segment
|
||||
u32 *animtbl = (void *)(data + size - 0x38a0);
|
||||
_animationsTableRomStart = (u8 *)animtbl;
|
||||
_animationsTableRomEnd = data + size;
|
||||
|
||||
PD_SWAP_VAL(*animtbl);
|
||||
const u32 count = *animtbl++;
|
||||
|
||||
struct animtableentry *anim = (struct animtableentry *)animtbl;
|
||||
for (u32 i = 0; i < count; ++i, ++anim) {
|
||||
PD_SWAP_VAL(anim->numframes);
|
||||
PD_SWAP_VAL(anim->bytesperframe);
|
||||
PD_SWAP_VAL(anim->headerlen);
|
||||
PD_SWAP_VAL(anim->data);
|
||||
// if an external replacement exists, replace the table entry and mark the offset
|
||||
if (modAnimationLoadDescriptor(i, anim) > 0) {
|
||||
anim->data = 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8 *preprocessMpConfigs(u8* data, u32 size, u32* outSize)
|
||||
{
|
||||
const u32 count = size / sizeof(struct mpconfig);
|
||||
struct mpconfig *cfg = (struct mpconfig *)data;
|
||||
for (u32 i = 0; i < count; ++i, ++cfg) {
|
||||
PD_SWAP_VAL(cfg->setup.options);
|
||||
PD_SWAP_VAL(cfg->setup.teamscorelimit);
|
||||
PD_SWAP_VAL(cfg->setup.chrslots);
|
||||
// TODO: are these required or are they always 0?
|
||||
PD_SWAP_VAL(cfg->setup.fileguid.deviceserial);
|
||||
PD_SWAP_VAL(cfg->setup.fileguid.fileid);
|
||||
// convert MPWEAPON_ to take classic weapons and JPN weapons into account
|
||||
for (s32 j = 0; j < ARRAYCOUNT(cfg->setup.weapons); ++j) {
|
||||
#if VERSION == VERSION_JPN_FINAL /* TODO: replace with runtime check */
|
||||
if (cfg->setup.weapons[j] >= 0x24) {
|
||||
// weapons after and including the shield need to be shifted (for classic weapons)
|
||||
cfg->setup.weapons[j] += (MPWEAPON_SHIELD - MPWEAPON_PP9I);
|
||||
}
|
||||
if (cfg->setup.weapons[j] >= 0x22) {
|
||||
// weapons after and including the cloaking device need to be shifted (for IR Scanner and Night Vision)
|
||||
cfg->setup.weapons[j] += (MPWEAPON_CLOAKINGDEVICE - MPWEAPON_NIGHTVISION);
|
||||
}
|
||||
if (cfg->setup.weapons[j] >= 0x19) {
|
||||
// weapons after the combat knife also need to be shifted up in JPN
|
||||
cfg->setup.weapons[j]++;
|
||||
}
|
||||
#else
|
||||
if (cfg->setup.weapons[j] >= 0x25) {
|
||||
// weapons after and including the shield need to be shifted (for classic weapons)
|
||||
cfg->setup.weapons[j] += (MPWEAPON_SHIELD - MPWEAPON_PP9I);
|
||||
}
|
||||
if (cfg->setup.weapons[j] >= 0x23) {
|
||||
// weapons after and including the cloaking device need to be shifted (for IR Scanner and Night Vision)
|
||||
cfg->setup.weapons[j] += (MPWEAPON_CLOAKINGDEVICE - MPWEAPON_NIGHTVISION);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8 *preprocessTexturesList(u8* data, u32 size, u32* outSize)
|
||||
{
|
||||
struct texture *tex = (struct texture *)data;
|
||||
const u32 count = size / sizeof(*tex);
|
||||
for (u32 i = 0; i < count; ++i, ++tex) {
|
||||
// TODO: it sure looks like none of the fields except soundsurfacetype, surfacetype and dataoffset are set
|
||||
// just swap the last 3 bytes of the first word...
|
||||
const u32 dofs = (u32)tex->dataoffset << 8;
|
||||
tex->dataoffset = PD_BE32(dofs);
|
||||
// ...and the surface types in the first byte
|
||||
const u8 tmp = tex->soundsurfacetype;
|
||||
tex->soundsurfacetype = tex->surfacetype;
|
||||
tex->surfacetype = tmp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libaudio.h"
|
||||
|
||||
#include "preprocess/common.h"
|
||||
|
||||
struct n64_adpcm_waveinfo {
|
||||
u32 loop; // ptr to ALADPCMloop
|
||||
u32 book; // ptr to ALADPCMBook
|
||||
};
|
||||
|
||||
struct n64_raw_waveinfo {
|
||||
u32 loop; // ptr to ALRawLoop
|
||||
};
|
||||
|
||||
struct n64_wavetable {
|
||||
u32 base;
|
||||
s32 len;
|
||||
u8 type;
|
||||
u8 flags;
|
||||
union {
|
||||
struct n64_adpcm_waveinfo adpcmWave;
|
||||
struct n64_raw_waveinfo rawWave;
|
||||
} waveInfo;
|
||||
};
|
||||
|
||||
struct n64_sound {
|
||||
u32 envelope; // ptr to ALEnvelope
|
||||
u32 keyMap; // ptr to ALKeyMap
|
||||
u32 wavetable; // ptr to struct n64_wavetable
|
||||
u8 samplePan;
|
||||
u8 sampleVolume;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
struct n64_instrument {
|
||||
u8 volume;
|
||||
u8 pan;
|
||||
u8 priority;
|
||||
u8 flags;
|
||||
u8 tremType;
|
||||
u8 tremRate;
|
||||
u8 tremDepth;
|
||||
u8 tremDelay;
|
||||
u8 vibType;
|
||||
u8 vibRate;
|
||||
u8 vibDepth;
|
||||
u8 vibDelay;
|
||||
s16 bendRange;
|
||||
s16 soundCount;
|
||||
u32 soundArray[1]; // ptr to struct n64_sound
|
||||
};
|
||||
|
||||
struct n64_bank {
|
||||
s16 instCount;
|
||||
u8 flags;
|
||||
u8 pad;
|
||||
s32 sampleRate;
|
||||
u32 percussion; // ptr to struct n64_instrument
|
||||
u32 instArray[1]; // ptr to struct n64_instrument
|
||||
};
|
||||
|
||||
struct n64_bankfile {
|
||||
s16 revision;
|
||||
s16 bankCount;
|
||||
u32 bankArray[1]; // ptr to struct n64_bank
|
||||
};
|
||||
|
||||
// only proceeds to convert the next item if it's not already converted
|
||||
#define AL_NEXT_ITEM(field, func) { \
|
||||
struct ptrmarker *marker = ptrFind(srcpos); \
|
||||
if (marker == NULL) { \
|
||||
field = (void *)(uintptr_t)(dstpos); \
|
||||
ptrAdd(srcpos, (uintptr_t)(field)); \
|
||||
dstpos = func(dst, dstpos, src, srcpos); \
|
||||
} else { \
|
||||
field = (void *)marker->ptr_host; } \
|
||||
}
|
||||
|
||||
static u32 convertAudioEnvelope(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
ALEnvelope *n64_envelope = (ALEnvelope *) &src[srcpos];
|
||||
ALEnvelope *host_envelope = (ALEnvelope *) &dst[dstpos];
|
||||
|
||||
dstpos += sizeof(ALEnvelope);
|
||||
|
||||
host_envelope->attackTime = PD_BE32(n64_envelope->attackTime);
|
||||
host_envelope->decayTime = PD_BE32(n64_envelope->decayTime);
|
||||
host_envelope->releaseTime = PD_BE32(n64_envelope->releaseTime);
|
||||
host_envelope->attackVolume = n64_envelope->attackVolume;
|
||||
host_envelope->decayVolume = n64_envelope->decayVolume;
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioKeyMap(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
ALKeyMap *n64_keymap = (ALKeyMap *) &src[srcpos];
|
||||
ALKeyMap *host_keymap = (ALKeyMap *) &dst[dstpos];
|
||||
|
||||
dstpos += sizeof(ALKeyMap);
|
||||
|
||||
host_keymap->velocityMin = n64_keymap->velocityMin;
|
||||
host_keymap->velocityMax = n64_keymap->velocityMax;
|
||||
host_keymap->keyMin = n64_keymap->keyMin;
|
||||
host_keymap->keyMax = n64_keymap->keyMax;
|
||||
host_keymap->keyBase = n64_keymap->keyBase;
|
||||
host_keymap->detune = n64_keymap->detune;
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioAdpcmLoop(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
ALADPCMloop *n64_loop = (ALADPCMloop *) &src[srcpos];
|
||||
ALADPCMloop *host_loop = (ALADPCMloop *) &dst[dstpos];
|
||||
|
||||
dstpos += sizeof(ALADPCMloop);
|
||||
|
||||
host_loop->start = PD_BE32(n64_loop->start);
|
||||
host_loop->end = PD_BE32(n64_loop->end);
|
||||
host_loop->count = PD_BE32(n64_loop->count);
|
||||
|
||||
for (int i = 0; i < ARRAYCOUNT(host_loop->state); i++) {
|
||||
host_loop->state[i] = PD_BE16(n64_loop->state[i]);
|
||||
}
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioAdpcmBook(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
ALADPCMBook *n64_book = (ALADPCMBook *) &src[srcpos];
|
||||
ALADPCMBook *host_book = (ALADPCMBook *) &dst[dstpos];
|
||||
|
||||
dstpos += sizeof(ALADPCMBook);
|
||||
|
||||
host_book->order = PD_BE32(n64_book->order);
|
||||
host_book->npredictors = PD_BE32(n64_book->npredictors);
|
||||
|
||||
for (int i = 0; i < ARRAYCOUNT(host_book->book); i++) {
|
||||
host_book->book[i] = PD_BE16(n64_book->book[i]);
|
||||
}
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioRawLoop(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
ALRawLoop *n64_loop = (ALRawLoop *) &src[srcpos];
|
||||
ALRawLoop *host_loop = (ALRawLoop *) &dst[dstpos];
|
||||
|
||||
dstpos += sizeof(ALRawLoop);
|
||||
|
||||
host_loop->start = PD_BE32(n64_loop->start);
|
||||
host_loop->end = PD_BE32(n64_loop->end);
|
||||
host_loop->count = PD_BE32(n64_loop->count);
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioWaveTable(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
struct n64_wavetable *n64_wavetable = (struct n64_wavetable *) &src[srcpos];
|
||||
ALWaveTable *host_wavetable = (ALWaveTable *) &dst[dstpos];
|
||||
|
||||
dstpos += sizeof(ALWaveTable);
|
||||
|
||||
host_wavetable->base = (void *)(uintptr_t)PD_BE32(n64_wavetable->base);
|
||||
host_wavetable->len = PD_BE32(n64_wavetable->len);
|
||||
host_wavetable->type = n64_wavetable->type;
|
||||
host_wavetable->flags = n64_wavetable->flags;
|
||||
|
||||
if (host_wavetable->type == AL_ADPCM_WAVE) {
|
||||
if (n64_wavetable->waveInfo.adpcmWave.loop) {
|
||||
srcpos = PD_BE32(n64_wavetable->waveInfo.adpcmWave.loop);
|
||||
AL_NEXT_ITEM(host_wavetable->waveInfo.adpcmWave.loop, convertAudioAdpcmLoop);
|
||||
} else {
|
||||
host_wavetable->waveInfo.adpcmWave.loop = NULL;
|
||||
}
|
||||
|
||||
if (n64_wavetable->waveInfo.adpcmWave.book) {
|
||||
srcpos = PD_BE32(n64_wavetable->waveInfo.adpcmWave.book);
|
||||
AL_NEXT_ITEM(host_wavetable->waveInfo.adpcmWave.book, convertAudioAdpcmBook);
|
||||
} else {
|
||||
host_wavetable->waveInfo.adpcmWave.book = NULL;
|
||||
}
|
||||
} else if (host_wavetable->type == AL_RAW16_WAVE) {
|
||||
if (n64_wavetable->waveInfo.rawWave.loop) {
|
||||
srcpos = PD_BE32(n64_wavetable->waveInfo.rawWave.loop);
|
||||
AL_NEXT_ITEM(host_wavetable->waveInfo.rawWave.loop, convertAudioRawLoop);
|
||||
} else {
|
||||
host_wavetable->waveInfo.rawWave.loop = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioSound(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
struct n64_sound *n64_sound = (struct n64_sound *) &src[srcpos];
|
||||
ALSound *host_sound = (ALSound *) &dst[dstpos];
|
||||
|
||||
dstpos += sizeof(ALSound);
|
||||
|
||||
if (n64_sound->envelope) {
|
||||
srcpos = PD_BE32(n64_sound->envelope);
|
||||
AL_NEXT_ITEM(host_sound->envelope, convertAudioEnvelope);
|
||||
} else {
|
||||
host_sound->envelope = NULL;
|
||||
}
|
||||
|
||||
if (n64_sound->keyMap) {
|
||||
srcpos = PD_BE32(n64_sound->keyMap);
|
||||
AL_NEXT_ITEM(host_sound->keyMap, convertAudioKeyMap);
|
||||
} else {
|
||||
host_sound->keyMap = NULL;
|
||||
}
|
||||
|
||||
if (n64_sound->wavetable) {
|
||||
srcpos = PD_BE32(n64_sound->wavetable);
|
||||
AL_NEXT_ITEM(host_sound->wavetable, convertAudioWaveTable);
|
||||
} else {
|
||||
host_sound->wavetable = NULL;
|
||||
}
|
||||
|
||||
host_sound->samplePan = n64_sound->samplePan;
|
||||
host_sound->sampleVolume = n64_sound->sampleVolume;
|
||||
host_sound->flags = n64_sound->flags;
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioInstrument(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
struct n64_instrument *n64_instrument = (struct n64_instrument *) &src[srcpos];
|
||||
ALInstrument *host_instrument = (ALInstrument *) &dst[dstpos];
|
||||
const s16 soundCount = PD_BE16(n64_instrument->soundCount);
|
||||
|
||||
host_instrument->volume = n64_instrument->volume;
|
||||
host_instrument->pan = n64_instrument->pan;
|
||||
host_instrument->priority = n64_instrument->priority;
|
||||
host_instrument->flags = n64_instrument->flags;
|
||||
host_instrument->tremType = n64_instrument->tremType;
|
||||
host_instrument->tremRate = n64_instrument->tremRate;
|
||||
host_instrument->tremDepth = n64_instrument->tremDepth;
|
||||
host_instrument->tremDelay = n64_instrument->tremDelay;
|
||||
host_instrument->vibType = n64_instrument->vibType;
|
||||
host_instrument->vibRate = n64_instrument->vibRate;
|
||||
host_instrument->vibDepth = n64_instrument->vibDepth;
|
||||
host_instrument->vibDelay = n64_instrument->vibDelay;
|
||||
host_instrument->bendRange = PD_BE16(n64_instrument->bendRange);
|
||||
host_instrument->soundCount = (soundCount);
|
||||
|
||||
dstpos = dstpos + sizeof(ALInstrument) + sizeof(uintptr_t) * (soundCount - 1);
|
||||
|
||||
for (int i = 0; i < soundCount; i++) {
|
||||
srcpos = PD_BE32(n64_instrument->soundArray[i]);
|
||||
AL_NEXT_ITEM(host_instrument->soundArray[i], convertAudioSound);
|
||||
}
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioBank(u8 *dst, u32 dstpos, u8 *src, u32 srcpos)
|
||||
{
|
||||
struct n64_bank *n64_bank = (struct n64_bank *) &src[srcpos];
|
||||
ALBank *host_bank = (ALBank *) &dst[dstpos];
|
||||
const s16 instCount = PD_BE16(n64_bank->instCount);
|
||||
|
||||
host_bank->instCount = (instCount);
|
||||
host_bank->flags = n64_bank->flags;
|
||||
host_bank->pad = n64_bank->pad;
|
||||
host_bank->sampleRate = PD_BE32(n64_bank->sampleRate);
|
||||
|
||||
dstpos = dstpos + sizeof(ALBank) + sizeof(uintptr_t) * (instCount - 1);
|
||||
|
||||
if (n64_bank->percussion) {
|
||||
srcpos = PD_BE32(n64_bank->percussion);
|
||||
AL_NEXT_ITEM(host_bank->percussion, convertAudioInstrument);
|
||||
} else {
|
||||
host_bank->percussion = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < instCount; i++) {
|
||||
srcpos = PD_BE32(n64_bank->instArray[i]);
|
||||
AL_NEXT_ITEM(host_bank->instArray[i], convertAudioInstrument);
|
||||
}
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
static u32 convertAudioBankFile(u8 *dst, u8 *src)
|
||||
{
|
||||
struct n64_bankfile *n64_bankfile = (struct n64_bankfile *)src;
|
||||
ALBankFile *host_bankfile = (ALBankFile *)dst;
|
||||
const s16 bankCount = PD_BE16(n64_bankfile->bankCount);
|
||||
|
||||
host_bankfile->revision = PD_BE16(n64_bankfile->revision);
|
||||
host_bankfile->bankCount = (bankCount);
|
||||
|
||||
u32 dstpos = sizeof(ALBankFile) + sizeof(uintptr_t) * (bankCount - 1);
|
||||
|
||||
for (int i = 0; i < bankCount; i++) {
|
||||
host_bankfile->bankArray[i] = (void *)(uintptr_t)(dstpos);
|
||||
u32 srcpos = PD_BE32(n64_bankfile->bankArray[i]);
|
||||
dstpos = convertAudioBank(dst, dstpos, src, srcpos);
|
||||
}
|
||||
|
||||
return dstpos;
|
||||
}
|
||||
|
||||
u8 *preprocessALBankFile(u8 *src, u32 size, u32 *outSize)
|
||||
{
|
||||
ptrReset();
|
||||
|
||||
const u32 dstlen = size * 3; // this should overshoot any possible bank size, but * 2 also works for vanilla banks
|
||||
u8 *dst = sysMemZeroAlloc(dstlen);
|
||||
|
||||
u32 reallen = convertAudioBankFile(dst, src);
|
||||
if (reallen > dstlen || ALIGN16(reallen) > dstlen) {
|
||||
sysFatalError("overflow when trying to preprocess an ALBankFile, size %u dstlen %u reallen %u", size, dstlen, reallen);
|
||||
}
|
||||
|
||||
reallen = ALIGN16(reallen);
|
||||
|
||||
if (reallen < dstlen) {
|
||||
dst = sysMemRealloc(dst, reallen);
|
||||
}
|
||||
|
||||
*outSize = reallen;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
u8 *preprocessALCMidiHdr(u8 *data, u32 size, u32 *outSize)
|
||||
{
|
||||
ALCMidiHdr *hdr = (ALCMidiHdr *)data;
|
||||
PD_SWAP_VAL(hdr->division);
|
||||
for (s32 i = 0; i < ARRAYCOUNT(hdr->trackOffset); ++i) {
|
||||
PD_SWAP_VAL(hdr->trackOffset[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8 *preprocessSequences(u8* data, u32 size, u32 *outSize)
|
||||
{
|
||||
struct seqtable *seq = (struct seqtable *)data;
|
||||
PD_SWAP_VAL(seq->count);
|
||||
|
||||
for (s16 i = 0; i < seq->count; ++i) {
|
||||
PD_SWAP_VAL(seq->entries[i].binlen);
|
||||
PD_SWAP_VAL(seq->entries[i].ziplen);
|
||||
PD_SWAP_VAL(seq->entries[i].romaddr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "preprocess/common.h"
|
||||
|
||||
struct n64_fontchar {
|
||||
u8 index;
|
||||
s8 baseline;
|
||||
u8 height;
|
||||
u8 width;
|
||||
s32 kerningindex;
|
||||
u32 pixeldata;
|
||||
};
|
||||
|
||||
struct n64_fontcharjpn {
|
||||
u16 index;
|
||||
s8 baseline;
|
||||
u8 height;
|
||||
u8 width;
|
||||
s16 kerningindex;
|
||||
u32 pixeldata;
|
||||
};
|
||||
|
||||
u8 *preprocessFont(u8 *src, u32 srclen, u32 *outSize)
|
||||
{
|
||||
int num_chars = 94;
|
||||
#if VERSION == VERSION_PAL_FINAL
|
||||
// PAL has more characters in these fonts
|
||||
extern u8* _fonthandelgothicsmSegmentRomStart;
|
||||
extern u8* _fonthandelgothicxsSegmentRomStart;
|
||||
extern u8* _fonthandelgothicmdSegmentRomStart;
|
||||
if (src == _fonthandelgothicsmSegmentRomStart
|
||||
|| src == _fonthandelgothicxsSegmentRomStart
|
||||
|| src == _fonthandelgothicmdSegmentRomStart) {
|
||||
num_chars = 135;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t dstlen = srclen - (num_chars * sizeof(struct n64_fontchar)) + (num_chars * sizeof(struct fontchar)) + 0x20;
|
||||
u8* dst = sysMemZeroAlloc(dstlen);
|
||||
|
||||
// Kerning table
|
||||
s32 *src_kerning_table = (s32 *)src;
|
||||
s32 *dst_kerning_table = (s32 *)dst;
|
||||
|
||||
for (int i = 0; i < 13 * 13; i++) {
|
||||
dst_kerning_table[i] = PD_BE32(src_kerning_table[i]);
|
||||
}
|
||||
|
||||
u32 src_offset = PD_ALIGN(13 * 13 * 4, sizeof(u32));
|
||||
u32 dst_offset = PD_ALIGN(13 * 13 * 4, sizeof(uintptr_t));
|
||||
|
||||
// Character table
|
||||
#if VERSION == VERSION_JPN_FINAL
|
||||
struct n64_fontcharjpn *src_char_table = (struct n64_fontcharjpn *) &src[src_offset];
|
||||
struct fontchar *dst_char_table = (struct fontchar *) &dst[dst_offset];
|
||||
u32 diff = (dst_offset + num_chars * sizeof(struct fontchar)) - (src_offset + num_chars * sizeof(struct n64_fontcharjpn));
|
||||
|
||||
for (int i = 0; i < num_chars; i++) {
|
||||
dst_char_table[i].index = PD_BE16(src_char_table[i].index);
|
||||
dst_char_table[i].baseline = src_char_table[i].baseline;
|
||||
dst_char_table[i].height = src_char_table[i].height;
|
||||
dst_char_table[i].width = src_char_table[i].width;
|
||||
dst_char_table[i].kerningindex = PD_BE16(src_char_table[i].kerningindex);
|
||||
dst_char_table[i].pixeldata = (void *)(uintptr_t)(src_char_table[i].pixeldata ? PD_BE32(src_char_table[i].pixeldata) + diff : 0);
|
||||
}
|
||||
|
||||
src_offset += num_chars * sizeof(struct n64_fontcharjpn);
|
||||
dst_offset += num_chars * sizeof(struct fontchar);
|
||||
#else
|
||||
struct n64_fontchar *src_char_table = (struct n64_fontchar *) &src[src_offset];
|
||||
struct fontchar *dst_char_table = (struct fontchar *) &dst[dst_offset];
|
||||
u32 diff = (dst_offset + num_chars * sizeof(struct fontchar)) - (src_offset + num_chars * sizeof(struct n64_fontchar));
|
||||
|
||||
for (int i = 0; i < num_chars; i++) {
|
||||
dst_char_table[i].index = src_char_table[i].index;
|
||||
dst_char_table[i].baseline = src_char_table[i].baseline;
|
||||
dst_char_table[i].height = src_char_table[i].height;
|
||||
dst_char_table[i].width = src_char_table[i].width;
|
||||
dst_char_table[i].kerningindex = PD_BE32(src_char_table[i].kerningindex);
|
||||
dst_char_table[i].pixeldata = (void *)(uintptr_t)(src_char_table[i].pixeldata ? PD_BE32(src_char_table[i].pixeldata) + diff : 0);
|
||||
}
|
||||
|
||||
src_offset += num_chars * sizeof(struct n64_fontchar);
|
||||
dst_offset += num_chars * sizeof(struct fontchar);
|
||||
#endif
|
||||
|
||||
// Pixel data
|
||||
u32 len = srclen - src_offset;
|
||||
memcpy(&dst[dst_offset], &src[src_offset], len);
|
||||
|
||||
if (outSize) *outSize = ALIGN16(dst_offset + len);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
u8 *preprocessJpnFont(u8* data, u32 size, u32* outSize)
|
||||
{
|
||||
// ???
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "lib/rzip.h"
|
||||
#include "romdata.h"
|
||||
#include "fs.h"
|
||||
#include "system.h"
|
||||
#include "preprocess.h"
|
||||
#include "platform.h"
|
||||
|
||||
/**
|
||||
* asset files and ROM segments can be replaced by optional external files,
|
||||
* but asset filenames still have to be either pulled from the ROM or from an
|
||||
* external file, so stuff can't be completely custom
|
||||
*
|
||||
* all data is assumed to be big endian, so it has to be byteswapped
|
||||
* at load time, which is fucking terrible
|
||||
*/
|
||||
|
||||
#define ROMDATA_FILEDIR "files"
|
||||
#define ROMDATA_SEGDIR "segs"
|
||||
|
||||
#define ROMDATA_ROM_NAME "pd." VERSION_ROMID ".z64"
|
||||
#define ROMDATA_ROM_SIZE 33554432
|
||||
|
||||
#if VERSION == VERSION_NTSC_FINAL
|
||||
#define ROMDATA_ROM_TITLE "Perfect Dark"
|
||||
#define ROMDATA_ROM_ID "NPDE"
|
||||
#define ROMDATA_ROM_DESC "NTSC v1.1"
|
||||
#define ROMDATA_FILES_OFS 0x28080
|
||||
#define ROMDATA_DATA_OFS 0x39850
|
||||
#elif VERSION == VERSION_PAL_FINAL
|
||||
#define ROMDATA_ROM_TITLE "Perfect Dark"
|
||||
#define ROMDATA_ROM_ID "NPDP"
|
||||
#define ROMDATA_ROM_DESC "PAL"
|
||||
#define ROMDATA_FILES_OFS 0x28910
|
||||
#define ROMDATA_DATA_OFS 0x39850
|
||||
#elif VERSION == VERSION_JPN_FINAL
|
||||
#define ROMDATA_ROM_TITLE "PERFECT DARK"
|
||||
#define ROMDATA_ROM_ID "NPDJ"
|
||||
#define ROMDATA_ROM_DESC "JPN"
|
||||
#define ROMDATA_FILES_OFS 0x28800
|
||||
#define ROMDATA_DATA_OFS 0x39850
|
||||
#else
|
||||
#error "This ROM version is unsupported."
|
||||
#endif
|
||||
|
||||
#define ROMDATA_MAX_FILES 2048
|
||||
|
||||
#define GBC_ROM_NAME "pd.gbc"
|
||||
#define GBC_ROM_SIZE 4194304
|
||||
|
||||
u8 *g_RomFile;
|
||||
u32 g_RomFileSize;
|
||||
|
||||
static u8 *romDataSeg;
|
||||
static u32 romDataSegSize;
|
||||
static const char *romName = ROMDATA_ROM_NAME;
|
||||
|
||||
enum loadsource {
|
||||
SRC_UNLOADED = 0,
|
||||
SRC_ROM,
|
||||
SRC_EXTERNAL
|
||||
};
|
||||
|
||||
struct romfilepatch {
|
||||
u32 ofs;
|
||||
u32 len;
|
||||
const char *src;
|
||||
const char *dst;
|
||||
};
|
||||
|
||||
struct romfile {
|
||||
u8 **segstart;
|
||||
u8 **segend;
|
||||
const char *name;
|
||||
u8 *data;
|
||||
u32 size;
|
||||
preprocessfunc preprocess;
|
||||
s32 source; // enum loadsource
|
||||
s32 preprocessed;
|
||||
const struct romfilepatch *patches;
|
||||
u32 numpatches;
|
||||
};
|
||||
|
||||
/* patches for individual files; applied on file load, before preprocFuncs, but */
|
||||
/* after unzip; only applied when loading from a ROM file */
|
||||
static const struct romfilepatch filePatches[] = {
|
||||
/* FILE_USETUPLUE: fixes Jon's double "if what" in Infiltration outro */
|
||||
{ 0x92a2, 1, "\x6c", "\x99" },
|
||||
{ 0x92b0, 1, "\x6c", "\x99" },
|
||||
};
|
||||
|
||||
static struct romfile fileSlots[ROMDATA_MAX_FILES] = {
|
||||
[FILE_USETUPLUE] = { .patches = &filePatches[0], .numpatches = 2 },
|
||||
};
|
||||
|
||||
#define ROMSEG_START(n) _ ## n ## SegmentRomStart
|
||||
#define ROMSEG_END(n) _ ## n ## SegmentRomEnd
|
||||
|
||||
/* segment table for ntsc-final */
|
||||
/* size will get calculated automatically if it is 0 */
|
||||
/* if there are replacement files in the data dir, they will be loaded instead */
|
||||
/* offsets are specified for ntsc-final, pal-final and jpn-final in that order */
|
||||
#define ROMSEG_LIST() \
|
||||
ROMSEG_DECL_SEG(fontjpnsingle, 0x194b20, 0x180330, 0x0, 0x0, preprocessJpnFont ) \
|
||||
ROMSEG_DECL_SEG(fontjpnmulti, 0x19fb40, 0x18b340, 0x0, 0x0, preprocessJpnFont ) \
|
||||
ROMSEG_DECL_SEG(animations, 0x1a15c0, 0x18cdc0, 0x190c50, 0x0, preprocessAnimations ) \
|
||||
ROMSEG_DECL_SEG(mpconfigs, 0x7d0a40, 0x7bc240, 0x7c00d0, 0x11e0, preprocessMpConfigs ) \
|
||||
ROMSEG_DECL_SEG(mpstringsE, 0x7d1c20, 0x7bd420, 0x7c12b0, 0x3700, NULL ) \
|
||||
ROMSEG_DECL_SEG(mpstringsJ, 0x7d5320, 0x7c0b20, 0x7c49b0, 0x3700, NULL ) \
|
||||
ROMSEG_DECL_SEG(mpstringsP, 0x7d8a20, 0x7c4220, 0x7c80b0, 0x3700, NULL ) \
|
||||
ROMSEG_DECL_SEG(mpstringsG, 0x7dc120, 0x7c7920, 0x7cb7b0, 0x3700, NULL ) \
|
||||
ROMSEG_DECL_SEG(mpstringsF, 0x7df820, 0x7cb020, 0x7ceeb0, 0x3700, NULL ) \
|
||||
ROMSEG_DECL_SEG(mpstringsS, 0x7e2f20, 0x7ce720, 0x7d25b0, 0x3700, NULL ) \
|
||||
ROMSEG_DECL_SEG(mpstringsI, 0x7e6620, 0x7d1e20, 0x7d5cb0, 0x3700, NULL ) \
|
||||
ROMSEG_DECL_SEG(firingrange, 0x7e9d20, 0x7d5520, 0x7d93b0, 0x1550, NULL ) \
|
||||
ROMSEG_DECL_SEG(fonttahoma, 0x7f7860, 0x7e3060, 0x7e6ef0, 0x0, preprocessFont ) \
|
||||
ROMSEG_DECL_SEG(fontnumeric, 0x7f8b20, 0x7e4320, 0x7e81b0, 0x0, preprocessFont ) \
|
||||
ROMSEG_DECL_SEG(fonthandelgothicsm, 0x7f9d30, 0x7e5530, 0x7e93c0, 0x0, preprocessFont ) \
|
||||
ROMSEG_DECL_SEG(fonthandelgothicxs, 0x7fbfb0, 0x7e87b0, 0x7ec640, 0x0, preprocessFont ) \
|
||||
ROMSEG_DECL_SEG(fonthandelgothicmd, 0x7fdd80, 0x7eae20, 0x7eecb0, 0x0, preprocessFont ) \
|
||||
ROMSEG_DECL_SEG(fonthandelgothiclg, 0x8008e0, 0x7eee70, 0x7f2d00, 0x0, preprocessFont ) \
|
||||
ROMSEG_DECL_SEG(sfxctl, 0x80a250, 0x7f87e0, 0x7fc670, 0x2fb80, preprocessALBankFile ) \
|
||||
ROMSEG_DECL_SEG(sfxtbl, 0x839dd0, 0x828360, 0x82c1f0, 0x4c2160, NULL ) \
|
||||
ROMSEG_DECL_SEG(seqctl, 0xcfbf30, 0xcea4c0, 0xcee350, 0xa060, preprocessALBankFile ) \
|
||||
ROMSEG_DECL_SEG(seqtbl, 0xd05f90, 0xcf4520, 0xcf83b0, 0x17c070, NULL ) \
|
||||
ROMSEG_DECL_SEG(sequences, 0xe82000, 0xe70590, 0xe74420, 0x563a0, preprocessSequences ) \
|
||||
ROMSEG_DECL_SEG(texturesdata, 0x1d65f40, 0x1d5ca20, 0x1d61f90, 0x0, NULL ) \
|
||||
ROMSEG_DECL_SEG(textureslist, 0x1ff7ca0, 0x1fee780, 0x1ff68f0, 0x0, preprocessTexturesList ) \
|
||||
ROMSEG_DECL_SEG(copyright, 0x1ffea20, 0x1ff5500, 0x1ffd6b0, 0xb30, NULL ) \
|
||||
ROMSEG_DECL_SEG(fontjpn, 0x0, 0x0, 0x178c40, 0x17920, preprocessJpnFont )
|
||||
|
||||
// declare the vars first
|
||||
|
||||
#undef ROMSEG_DECL_SEG
|
||||
#define ROMSEG_DECL_SEG(name, ofs_ntsc, ofs_pal, ofs_jpn, size, preproc) u8 *ROMSEG_START(name), *ROMSEG_END(name);
|
||||
ROMSEG_LIST()
|
||||
|
||||
// this is part of the animations seg and as such does not follow the naming convention
|
||||
// these are set in preprocessAnimations
|
||||
u8 *_animationsTableRomStart;
|
||||
u8 *_animationsTableRomEnd;
|
||||
|
||||
// then build the table
|
||||
|
||||
#undef ROMSEG_DECL_SEG
|
||||
|
||||
#if VERSION == VERSION_NTSC_FINAL
|
||||
#define ROMSEG_DECL_SEG(name, ofs_ntsc, ofs_pal, ofs_jpn, size, preproc) { &ROMSEG_START(name), &ROMSEG_END(name), #name, (u8 *)ofs_ntsc, size, preproc },
|
||||
#elif VERSION == VERSION_PAL_FINAL
|
||||
#define ROMSEG_DECL_SEG(name, ofs_ntsc, ofs_pal, ofs_jpn, size, preproc) { &ROMSEG_START(name), &ROMSEG_END(name), #name, (u8 *)ofs_pal, size, preproc },
|
||||
#elif VERSION == VERSION_JPN_FINAL
|
||||
#define ROMSEG_DECL_SEG(name, ofs_ntsc, ofs_pal, ofs_jpn, size, preproc) { &ROMSEG_START(name), &ROMSEG_END(name), #name, (u8 *)ofs_jpn, size, preproc },
|
||||
#endif
|
||||
|
||||
static struct romfile romSegs[] = {
|
||||
ROMSEG_LIST()
|
||||
{ NULL, NULL, NULL, NULL, 0, NULL },
|
||||
};
|
||||
|
||||
/* the game sets g_LoadType to the type of file it expects, */
|
||||
/* so we can hijack that in fileLoad and automatically byteswap the file */
|
||||
static preprocessfunc filePreprocFuncs[] = {
|
||||
/* LOADTYPE_NONE */ NULL,
|
||||
/* LOADTYPE_BG */ NULL, // loaded in parts
|
||||
/* LOADTYPE_TILES */ preprocessTilesFile,
|
||||
/* LOADTYPE_LANG */ preprocessLangFile,
|
||||
/* LOADTYPE_SETUP */ preprocessSetupFile,
|
||||
/* LOADTYPE_PADS */ preprocessPadsFile,
|
||||
/* LOADTYPE_MODEL */ preprocessModelFile,
|
||||
/* LOADTYPE_GUN */ preprocessGunFile,
|
||||
};
|
||||
|
||||
static inline void romdataWrongRomError(const char *fmt, ...)
|
||||
{
|
||||
char reason[1024];
|
||||
reason[0] = '\0';
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(reason, sizeof(reason), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
sysFatalError("Wrong ROM file.\n%s\nEnsure that you have the correct " ROMDATA_ROM_DESC " ROM in z64 format.", reason);
|
||||
}
|
||||
|
||||
static inline void romdataLoadRom(void)
|
||||
{
|
||||
sysLogPrintf(LOG_NOTE, "ROM file: %s", romName);
|
||||
|
||||
g_RomFile = fsFileLoad(romName, &g_RomFileSize);
|
||||
|
||||
if (!g_RomFile) {
|
||||
sysFatalError("Could not open ROM file %s.\nEnsure that it is in the %s directory.", romName, fsFullPath(""));
|
||||
}
|
||||
|
||||
// zips are not guaranteed to start with PK, but might as well at least try
|
||||
if (g_RomFileSize > 2 && (!memcmp(g_RomFile, "PK", 2) || !memcmp(g_RomFile, "Rar", 3) || !memcmp(g_RomFile, "7z", 2))) {
|
||||
romdataWrongRomError("Your ROM is in an archive file. Please extract it.");
|
||||
}
|
||||
|
||||
if (g_RomFileSize != ROMDATA_ROM_SIZE) {
|
||||
romdataWrongRomError("ROM size does not match: expected: %u, got: %u.", ROMDATA_ROM_SIZE, g_RomFileSize);
|
||||
}
|
||||
|
||||
if (memcmp(g_RomFile + 0x3b, ROMDATA_ROM_ID, 4) || memcmp(g_RomFile + 0x20, ROMDATA_ROM_TITLE, sizeof(ROMDATA_ROM_TITLE) - 1)) {
|
||||
romdataWrongRomError("ROM header does not match.");
|
||||
}
|
||||
|
||||
// inflate the compressed data segment since that's where some useful stuff is
|
||||
|
||||
u8 *zipped = g_RomFile + ROMDATA_DATA_OFS;
|
||||
if (!rzipIs1173(zipped)) {
|
||||
romdataWrongRomError("Data segment is not 1173-compressed.");
|
||||
}
|
||||
|
||||
const u32 dataSegLen = ((u32)zipped[2] << 16) | ((u32)zipped[3] << 8) | (u32)zipped[4];
|
||||
if (dataSegLen < ROMDATA_FILES_OFS) {
|
||||
romdataWrongRomError("Data segment too small (%u), need at least %u.", dataSegLen, ROMDATA_FILES_OFS);
|
||||
}
|
||||
|
||||
u8 *dataSeg = sysMemAlloc(dataSegLen);
|
||||
if (!dataSeg) {
|
||||
sysFatalError("Could not allocate %u bytes for data segment.", dataSegLen);
|
||||
}
|
||||
|
||||
u8 scratch[5 * 1024];
|
||||
if (rzipInflate(zipped, dataSeg, scratch) < 0) {
|
||||
free(dataSeg);
|
||||
sysFatalError("Could not inflate data segment.");
|
||||
}
|
||||
|
||||
romDataSeg = dataSeg;
|
||||
romDataSegSize = dataSegLen;
|
||||
}
|
||||
|
||||
static inline void romdataUpdateSegStartEnd(struct romfile* seg)
|
||||
{
|
||||
if (seg->segstart) {
|
||||
*seg->segstart = seg->data;
|
||||
}
|
||||
|
||||
if (seg->segend) {
|
||||
*seg->segend = seg->data + seg->size;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void romdataInitSegment(struct romfile *seg)
|
||||
{
|
||||
if (!seg->data) {
|
||||
// unused in this ROM, skip it
|
||||
sysLogPrintf(LOG_NOTE, "skipping segment %s", seg->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!seg->size) {
|
||||
// size unknown
|
||||
if (seg[1].name) {
|
||||
// use next segment's base to calculate
|
||||
seg->size = seg[1].data - seg->data;
|
||||
} else {
|
||||
// this is the last segment, calculate based on rom size
|
||||
seg->size = (uintptr_t)g_RomFileSize - (uintptr_t)seg->data;
|
||||
}
|
||||
}
|
||||
|
||||
// check if we have an external replacement and load it if so
|
||||
char tmp[FS_MAXPATH];
|
||||
snprintf(tmp, sizeof(tmp), ROMDATA_SEGDIR "/%s", seg->name);
|
||||
u8 *newData = NULL;
|
||||
const s32 extFileSize = fsFileSize(tmp);
|
||||
if (extFileSize > 0) {
|
||||
newData = fsFileLoad(tmp, &seg->size);
|
||||
}
|
||||
|
||||
if (!newData) {
|
||||
// no external data, just make it point to the rom
|
||||
if (g_RomFile) {
|
||||
newData = g_RomFile + (uintptr_t)seg->data;
|
||||
seg->source = SRC_ROM;
|
||||
sysLogPrintf(LOG_NOTE, "loading segment %s from ROM (offset %08x pointer %p)", seg->name, (uintptr_t)seg->data, newData);
|
||||
} else {
|
||||
sysFatalError("No ROM or external file for segment:\n%s", seg->name);
|
||||
}
|
||||
} else {
|
||||
// loaded external data
|
||||
seg->source = SRC_EXTERNAL;
|
||||
sysLogPrintf(LOG_NOTE, "loading segment %s from file (pointer %p)", seg->name, newData);
|
||||
}
|
||||
|
||||
seg->data = newData;
|
||||
|
||||
romdataUpdateSegStartEnd(seg);
|
||||
|
||||
// call the post load function if any
|
||||
if (seg->preprocess && !seg->preprocessed) {
|
||||
newData = seg->preprocess(seg->data, seg->size, &seg->size);
|
||||
|
||||
if (newData) {
|
||||
if (seg->source == SRC_EXTERNAL)
|
||||
sysMemFree(seg->data);
|
||||
seg->data = newData;
|
||||
romdataUpdateSegStartEnd(seg);
|
||||
}
|
||||
|
||||
seg->preprocessed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline s32 romdataLoadExternalFileList(void)
|
||||
{
|
||||
romDataSeg = fsFileLoad("filenames.lst", &romDataSegSize); // this null terminates the file by itself
|
||||
if (!romDataSeg || !romDataSegSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 n = 1;
|
||||
char *p = (char *)romDataSeg;
|
||||
while (*p && n < ROMDATA_MAX_FILES) {
|
||||
// skip whitespace
|
||||
while (*p && isspace(*p)) ++p;
|
||||
if (*p) {
|
||||
const char *start = p;
|
||||
// skip to next whitespace or end of file
|
||||
while (*p && !isspace(*p)) ++p;
|
||||
// null terminate the name if needed
|
||||
if (*p) {
|
||||
*p++ = '\0';
|
||||
}
|
||||
fileSlots[n++].name = start;
|
||||
}
|
||||
}
|
||||
|
||||
return n - 1;
|
||||
}
|
||||
|
||||
static inline void romdataInitFiles(void)
|
||||
{
|
||||
if (!g_RomFile) {
|
||||
// no ROM; try to load the file name list from disk
|
||||
if (!romdataLoadExternalFileList()) {
|
||||
sysFatalError("No ROM file or external filename table found.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// the file offset table is in the data seg
|
||||
const u32 *offsets = (u32 *)(romDataSeg + ROMDATA_FILES_OFS);
|
||||
u32 i;
|
||||
for (i = 1; offsets[i]; ++i) {
|
||||
if (offsets + i + 1 < (u32 *)(romDataSeg + romDataSegSize)) {
|
||||
const u32 nextofs = PD_BE32(offsets[i + 1]);
|
||||
const u32 ofs = PD_BE32(offsets[i]);
|
||||
fileSlots[i].data = g_RomFile + ofs;
|
||||
fileSlots[i].size = nextofs - ofs;
|
||||
fileSlots[i].source = SRC_UNLOADED;
|
||||
fileSlots[i].preprocessed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// last offset is to the name table
|
||||
const u32 *nameOffsets = (u32 *)(g_RomFile + PD_BE32(offsets[i - 1]));
|
||||
for (i = 1; nameOffsets[i]; ++i) {
|
||||
const u32 ofs = PD_BE32(nameOffsets[i]);
|
||||
fileSlots[i].name = (const char *)nameOffsets + ofs; // ofs is relative to the start of the name table
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct romfile *romdataGetSeg(const char *name)
|
||||
{
|
||||
struct romfile *seg = romSegs;
|
||||
while (seg->name && strcmp(name, seg->name)) {
|
||||
++seg;
|
||||
}
|
||||
return seg;
|
||||
}
|
||||
|
||||
s32 romdataInit(void)
|
||||
{
|
||||
const char *altRomName = sysArgGetString("--rom-file");
|
||||
if (altRomName) {
|
||||
romName = altRomName;
|
||||
}
|
||||
|
||||
romdataLoadRom();
|
||||
|
||||
// set segments to point to the rom or load them externally
|
||||
for (struct romfile *seg = romSegs; seg->name; ++seg) {
|
||||
romdataInitSegment(seg);
|
||||
}
|
||||
|
||||
// load file table from the files segment
|
||||
romdataInitFiles();
|
||||
|
||||
sysLogPrintf(LOG_NOTE, "romdataInit: loaded rom, size = %u", g_RomFileSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool romdataCheckGbcRomContents(const u8 *gbcRomFile, const u32 gbcRomSize)
|
||||
{
|
||||
if (gbcRomSize != GBC_ROM_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ROM title
|
||||
if (memcmp(gbcRomFile + 0x134, "PerfDark VPDE", 15) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Licensee code
|
||||
if (memcmp(gbcRomFile + 0x144, "4Y", 2) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Header and global checksums
|
||||
if (gbcRomFile[0x14D] != 0xA1 || gbcRomFile[0x14E] != 0xAD || gbcRomFile[0x14F] != 0x0F) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 romdataCheckGbcRom(void)
|
||||
{
|
||||
if (fsFileSize(GBC_ROM_NAME) < 0) {
|
||||
// bail early if it doesn't exist to avoid generating error messages
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 gbcRomSize = 0;
|
||||
u8 *gbcRomFile = fsFileLoad(GBC_ROM_NAME, &gbcRomSize);
|
||||
if (!gbcRomFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool ret = romdataCheckGbcRomContents(gbcRomFile, gbcRomSize);
|
||||
sysMemFree(gbcRomFile);
|
||||
|
||||
if (ret) {
|
||||
sysLogPrintf(LOG_NOTE, "romdataCheckGbcRom: valid GBC rom found");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 romdataFileGetSize(s32 fileNum)
|
||||
{
|
||||
if (fileNum < 1 || fileNum >= ROMDATA_MAX_FILES) {
|
||||
sysLogPrintf(LOG_ERROR, "romdataFileGetSize: invalid file num %d", fileNum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ensure any external files are loaded and we use their size
|
||||
if (romdataFileLoad(fileNum, NULL)) {
|
||||
return fileSlots[fileNum].size;
|
||||
}
|
||||
|
||||
sysLogPrintf(LOG_ERROR, "romdataFileGetSize: could not load file num %d", fileNum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u8 *romdataFileGetData(s32 fileNum)
|
||||
{
|
||||
return romdataFileLoad(fileNum, NULL);
|
||||
}
|
||||
|
||||
u8 *romdataFileLoad(s32 fileNum, u32 *outSize)
|
||||
{
|
||||
if (fileNum < 1 || fileNum >= ROMDATA_MAX_FILES) {
|
||||
sysLogPrintf(LOG_ERROR, "romdataFileLoad: invalid file num %d", fileNum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8 *out = NULL;
|
||||
|
||||
// try to load external file
|
||||
if (fileSlots[fileNum].source == SRC_UNLOADED) {
|
||||
char tmp[FS_MAXPATH] = { 0 };
|
||||
snprintf(tmp, sizeof(tmp), ROMDATA_FILEDIR "/%s", fileSlots[fileNum].name);
|
||||
if (fsFileSize(tmp) > 0) {
|
||||
u32 size = 0;
|
||||
out = fsFileLoad(tmp, &size);
|
||||
if (out && size) {
|
||||
sysLogPrintf(LOG_NOTE, "file %d (%s) loaded externally", fileNum, fileSlots[fileNum].name);
|
||||
fileSlots[fileNum].data = out;
|
||||
fileSlots[fileNum].size = size;
|
||||
fileSlots[fileNum].source = SRC_EXTERNAL;
|
||||
// external file; do not apply patches to this
|
||||
fileSlots[fileNum].numpatches = 0;
|
||||
}
|
||||
}
|
||||
// tried and failed, fall back to ROM
|
||||
fileSlots[fileNum].source = SRC_ROM;
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
out = fileSlots[fileNum].data;
|
||||
}
|
||||
|
||||
if (out && outSize) {
|
||||
*outSize = fileSlots[fileNum].size;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void romdataFilePreprocess(s32 fileNum, s32 loadType, u8 *data, u32 size, u32 *outSize)
|
||||
{
|
||||
if (fileNum < 1 || fileNum >= ROMDATA_MAX_FILES) {
|
||||
sysLogPrintf(LOG_ERROR, "romdataFilePreprocess: invalid file num %d", fileNum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data && size /* && !fileSlots[fileNum].preprocessed*/) {
|
||||
if (loadType && loadType < (u32)ARRAYCOUNT(filePreprocFuncs) && filePreprocFuncs[loadType]) {
|
||||
// apply patches
|
||||
for (u32 i = 0; i < fileSlots[fileNum].numpatches; ++i) {
|
||||
const struct romfilepatch *p = &fileSlots[fileNum].patches[i];
|
||||
if (!memcmp(data + p->ofs, p->src, p->len)) {
|
||||
memcpy(data + p->ofs, p->dst, p->len);
|
||||
sysLogPrintf(LOG_NOTE, "file %d (%s) patched at offset 0x%x", fileNum, fileSlots[fileNum].name, p->ofs);
|
||||
}
|
||||
}
|
||||
// then preprocess
|
||||
filePreprocFuncs[loadType](data, size, outSize);
|
||||
// fileSlots[fileNum].preprocessed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void romdataFileFree(s32 fileNum)
|
||||
{
|
||||
if (fileNum < 1 || fileNum >= ROMDATA_MAX_FILES) {
|
||||
sysLogPrintf(LOG_ERROR, "fsFileFree: invalid file num %d", fileNum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileSlots[fileNum].source == SRC_EXTERNAL) {
|
||||
sysMemFree(fileSlots[fileNum].data);
|
||||
fileSlots[fileNum].data = NULL;
|
||||
}
|
||||
|
||||
fileSlots[fileNum].source = SRC_UNLOADED;
|
||||
}
|
||||
|
||||
const char *romdataFileGetName(s32 fileNum)
|
||||
{
|
||||
if (fileNum < 1 || fileNum >= ROMDATA_MAX_FILES) {
|
||||
return NULL;
|
||||
}
|
||||
return fileSlots[fileNum].name;
|
||||
}
|
||||
|
||||
s32 romdataFileGetNumForName(const char *name)
|
||||
{
|
||||
if (!name || !name[0]) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < ROMDATA_MAX_FILES; ++i) {
|
||||
if (fileSlots[i].name && !strcmp(fileSlots[i].name, name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
u8 *romdataSegGetData(const char *segName)
|
||||
{
|
||||
return romdataGetSeg(segName)->data;
|
||||
}
|
||||
|
||||
u8 *romdataSegGetDataEnd(const char *segName)
|
||||
{
|
||||
struct romfile *seg = romdataGetSeg(segName);
|
||||
return seg->data + seg->size;
|
||||
}
|
||||
|
||||
u32 romdataSegGetSize(const char *segName)
|
||||
{
|
||||
return romdataGetSeg(segName)->size;
|
||||
}
|
||||
|
||||
u32 romdataFileGetEstimatedSize(const u32 size, const u32 loadtype)
|
||||
{
|
||||
#ifdef PLATFORM_64BIT
|
||||
switch (loadtype) {
|
||||
case LOADTYPE_BG: return (u32)(size * 1.1f);
|
||||
case LOADTYPE_TILES: return (u32)(size * 1.1f);
|
||||
case LOADTYPE_LANG: return (u32)(size * 1.3f);
|
||||
case LOADTYPE_SETUP: return (u32)(size * 1.5f);
|
||||
case LOADTYPE_PADS: return (u32)(size * 1.7f);
|
||||
case LOADTYPE_MODEL: return (u32)(size * 1.7f);
|
||||
case LOADTYPE_GUN: return (u32)(size * 1.7f);
|
||||
default:
|
||||
sysLogPrintf(LOG_WARNING, "romdataFileGetEstimatedSize: wrong loadtype %d", loadtype);
|
||||
}
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <SDL.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// on win32 we use waitable timers instead of nanosleep
|
||||
typedef HANDLE WINAPI (*CREATEWAITABLETIMEREXAFN)(LPSECURITY_ATTRIBUTES, LPCSTR, DWORD, DWORD);
|
||||
static HANDLE timer;
|
||||
static CREATEWAITABLETIMEREXAFN pfnCreateWaitableTimerExA;
|
||||
|
||||
// winapi also provides a yield macro
|
||||
#define DO_YIELD() YieldProcessor()
|
||||
|
||||
// ask system for high performance GPU, if any
|
||||
__attribute__((dllexport)) u32 NvOptimusEnablement = 1;
|
||||
__attribute__((dllexport)) u32 AmdPowerXpressRequestHighPerformance = 1;
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
// figure out how to yield
|
||||
#if defined(PLATFORM_X86) || defined(PLATFORM_X86_64)
|
||||
// this should work even if the code is not built with SSE enabled, at least on gcc and clang,
|
||||
// but if it doesn't we'll have to use __builtin_ia32_pause() or something
|
||||
#include <immintrin.h>
|
||||
#define DO_YIELD() _mm_pause()
|
||||
#elif defined(PLATFORM_ARM) && (defined(PLATFORM_64BIT) || PLATFORM_ARM == 7 || PLATFORM_ARM == 8)
|
||||
// same as YieldProcessor() on ARM Windows
|
||||
#define DO_YIELD() __asm__ volatile("dmb ishst\n\tyield":::"memory")
|
||||
#else
|
||||
// fuck it
|
||||
#define DO_YIELD() do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define LOG_FNAME "pd.log"
|
||||
#define CRASHLOG_FNAME "pd.crash.log"
|
||||
#define USEC_IN_SEC 1000000ULL
|
||||
|
||||
static u64 startTick = 0;
|
||||
static char logPath[2048];
|
||||
|
||||
static s32 sysArgc;
|
||||
static const char **sysArgv;
|
||||
|
||||
static inline void sysLogSetPath(const char *fname)
|
||||
{
|
||||
// figure out where the log is and clear it
|
||||
// try working dir first
|
||||
snprintf(logPath, sizeof(logPath), "./%s", fname);
|
||||
FILE *f = fopen(logPath, "wb");
|
||||
if (!f) {
|
||||
// try home dir
|
||||
sysGetHomePath(logPath, sizeof(logPath) - 1);
|
||||
strncat(logPath, "/", sizeof(logPath) - 1);
|
||||
strncat(logPath, fname, sizeof(logPath) - 1);
|
||||
f = fopen(logPath, "wb");
|
||||
}
|
||||
if (f) {
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void sysInitArgs(s32 argc, const char **argv)
|
||||
{
|
||||
sysArgc = argc;
|
||||
sysArgv = argv;
|
||||
}
|
||||
|
||||
void sysInit(void)
|
||||
{
|
||||
startTick = sysGetMicroseconds();
|
||||
|
||||
if (sysArgCheck("--log")) {
|
||||
sysLogSetPath(LOG_FNAME);
|
||||
}
|
||||
|
||||
#ifdef VERSION_HASH
|
||||
sysLogPrintf(LOG_NOTE, "version: " VERSION_BRANCH " " VERSION_HASH " (" VERSION_TARGET ")");
|
||||
#endif
|
||||
|
||||
char timestr[256];
|
||||
const time_t curtime = time(NULL);
|
||||
strftime(timestr, sizeof(timestr), "%d %b %Y %H:%M:%S", localtime(&curtime));
|
||||
sysLogPrintf(LOG_NOTE, "startup date: %s", timestr);
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
// this function is only present on Vista+, so try to import it from kernel32 by hand
|
||||
pfnCreateWaitableTimerExA = (CREATEWAITABLETIMEREXAFN)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateWaitableTimerExA");
|
||||
if (pfnCreateWaitableTimerExA) {
|
||||
// function exists, try to create a hires timer
|
||||
timer = pfnCreateWaitableTimerExA(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
||||
}
|
||||
if (!timer) {
|
||||
// no function or hires timers not supported, fallback to lower resolution timer
|
||||
sysLogPrintf(LOG_WARNING, "SYS: hires waitable timers not available");
|
||||
timer = CreateWaitableTimerA(NULL, FALSE, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
s32 sysArgCheck(const char *arg)
|
||||
{
|
||||
for (s32 i = 1; i < sysArgc; ++i) {
|
||||
if (!strcasecmp(sysArgv[i], arg)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *sysArgGetString(const char *arg)
|
||||
{
|
||||
for (s32 i = 1; i < sysArgc; ++i) {
|
||||
if (!strcasecmp(sysArgv[i], arg)) {
|
||||
if (i < sysArgc - 1) {
|
||||
return sysArgv[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s32 sysArgGetInt(const char *arg, s32 defval)
|
||||
{
|
||||
for (s32 i = 1; i < sysArgc; ++i) {
|
||||
if (!strcasecmp(sysArgv[i], arg)) {
|
||||
if (i < sysArgc - 1) {
|
||||
return strtol(sysArgv[i + 1], NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defval;
|
||||
}
|
||||
|
||||
u64 sysGetMicroseconds(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return ((u64)tv.tv_sec * USEC_IN_SEC + (u64)tv.tv_usec) - startTick;
|
||||
}
|
||||
|
||||
s32 sysLogIsOpen(void)
|
||||
{
|
||||
return (logPath[0] != '\0');
|
||||
}
|
||||
|
||||
void sysLogPrintf(s32 level, const char *fmt, ...)
|
||||
{
|
||||
static const char *prefix[3] = {
|
||||
"", "WARNING: ", "ERROR: "
|
||||
};
|
||||
|
||||
char logmsg[2048];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (logPath[0]) {
|
||||
FILE *f = fopen(logPath, "ab");
|
||||
if (f) {
|
||||
fprintf(f, "%s%s\n", prefix[level], logmsg);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
FILE *fout = (level == LOG_NOTE) ? stdout : stderr;
|
||||
fprintf(fout, "%s%s\n", prefix[level], logmsg);
|
||||
}
|
||||
|
||||
void sysFatalError(const char *fmt, ...)
|
||||
{
|
||||
static s32 alreadyCrashed = 0;
|
||||
|
||||
if (alreadyCrashed) {
|
||||
abort();
|
||||
}
|
||||
|
||||
char errmsg[2048] = { 0 };
|
||||
|
||||
alreadyCrashed = 1;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
sysLogPrintf(LOG_ERROR, "FATAL: %s", errmsg);
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", errmsg, NULL);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void sysGetExecutablePath(char *outPath, const u32 outLen)
|
||||
{
|
||||
// try asking SDL
|
||||
char *sdlPath = SDL_GetBasePath();
|
||||
|
||||
if (sdlPath && *sdlPath) {
|
||||
// -1 to trim trailing slash
|
||||
const u32 len = strlen(sdlPath) - 1;
|
||||
if (len < outLen) {
|
||||
memcpy(outPath, sdlPath, len);
|
||||
outPath[len] = '\0';
|
||||
}
|
||||
} else if (sysArgc && sysArgv[0] && sysArgv[0][0]) {
|
||||
// get exe path from argv[0]
|
||||
strncpy(outPath, sysArgv[0], outLen - 1);
|
||||
outPath[outLen - 1] = '\0';
|
||||
} else if (outLen > 1) {
|
||||
// give up, use working directory instead
|
||||
outPath[0] = '.';
|
||||
outPath[1] = '\0';
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
// replace all backslashes with forward slashes, windows supports both
|
||||
for (u32 i = 0; i < outLen && outPath[i]; ++i) {
|
||||
if (outPath[i] == '\\') {
|
||||
outPath[i] = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_free(sdlPath);
|
||||
}
|
||||
|
||||
void sysGetHomePath(char *outPath, const u32 outLen)
|
||||
{
|
||||
// try asking SDL
|
||||
char *sdlPath = SDL_GetPrefPath("", "perfectdark");
|
||||
|
||||
if (sdlPath && *sdlPath) {
|
||||
// -1 to trim trailing slash
|
||||
const u32 len = strlen(sdlPath) - 1;
|
||||
if (len < outLen) {
|
||||
memcpy(outPath, sdlPath, len);
|
||||
outPath[len] = '\0';
|
||||
}
|
||||
} else if (outLen > 1) {
|
||||
// give up, use working directory instead
|
||||
outPath[0] = '.';
|
||||
outPath[1] = '\0';
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
// replace all backslashes with forward slashes, windows supports both
|
||||
for (u32 i = 0; i < outLen && outPath[i]; ++i) {
|
||||
if (outPath[i] == '\\') {
|
||||
outPath[i] = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_free(sdlPath);
|
||||
}
|
||||
|
||||
void *sysMemAlloc(const u32 size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void *sysMemZeroAlloc(const u32 size)
|
||||
{
|
||||
return calloc(1, size);
|
||||
}
|
||||
|
||||
void *sysMemRealloc(void *ptr, const u32 newSize)
|
||||
{
|
||||
return realloc(ptr, newSize);
|
||||
}
|
||||
|
||||
void sysMemFree(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void sysSleep(const s64 hns)
|
||||
{
|
||||
#ifdef PLATFORM_WIN32
|
||||
static LARGE_INTEGER li;
|
||||
li.QuadPart = -hns;
|
||||
SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE);
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
#else
|
||||
const struct timespec spec = { 0, hns * 100 };
|
||||
nanosleep(&spec, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sysCpuRelax(void)
|
||||
{
|
||||
DO_YIELD();
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
#include "utils.h"
|
||||
|
||||
static inline bool isSingleCharToken(const s32 ch)
|
||||
{
|
||||
switch (ch) {
|
||||
case '{':
|
||||
case '}':
|
||||
case '[':
|
||||
case ']':
|
||||
case '(':
|
||||
case ')':
|
||||
case ',':
|
||||
case '\'':
|
||||
case '=':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
char *strFmt(const char *fmt, ...)
|
||||
{
|
||||
static char buf[4096];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *strRightTrim(char *str)
|
||||
{
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const s32 len = strlen(str);
|
||||
for (s32 i = len - 1; i >= 0 && isspace(str[i]); --i) {
|
||||
str[i] = '\0';
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *strTrim(char *str)
|
||||
{
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// left trim
|
||||
while (*str && (u8)*str < ' ') {
|
||||
++str;
|
||||
}
|
||||
|
||||
// right trim
|
||||
const s32 len = strlen(str);
|
||||
for (s32 i = len - 1; i > 0 && isspace(str[i]); --i) {
|
||||
str[i] = '\0';
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *strUnquote(char *str)
|
||||
{
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*str == '"') {
|
||||
++str;
|
||||
}
|
||||
|
||||
char *end = strrchr(str, '"');
|
||||
if (end && end[1] == '\0') {
|
||||
*end = '\0';
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *strParseToken(char *str, char *out, s32 *outCount)
|
||||
{
|
||||
if (outCount) {
|
||||
*outCount = 0;
|
||||
}
|
||||
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out[0] = '\0';
|
||||
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s32 cnt = 0;
|
||||
while (*str) {
|
||||
// skip whitespace and other garbage
|
||||
while (*str && (u8)*str <= ' ') {
|
||||
++str;
|
||||
}
|
||||
if (!*str) {
|
||||
// just whitespace
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// skip #, ; and // comments
|
||||
if (*str == ';' || *str == '#' || (str[0] == '/' && str[1] == '/')) {
|
||||
while (*str && *str != '\n') {
|
||||
++str;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*str == '"') {
|
||||
// quoted string; treat it as an entire token, including the quotes
|
||||
out[cnt++] = *str++;
|
||||
while (true) {
|
||||
if (*str == '\0') {
|
||||
// unterminated quoted string
|
||||
break;
|
||||
}
|
||||
|
||||
if (str[0] == '\\' && str[1] == '"') {
|
||||
// escaped quote; add to token
|
||||
if (cnt + 1 < UTIL_MAX_TOKEN) {
|
||||
out[cnt++] = str[1];
|
||||
}
|
||||
str += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// add char to token
|
||||
const s32 ch = (u8)*str++;
|
||||
if (cnt + 1 < UTIL_MAX_TOKEN) {
|
||||
out[cnt++] = ch;
|
||||
}
|
||||
|
||||
if (ch == '"') {
|
||||
// ch was the closing quote
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSingleCharToken(*str)) {
|
||||
// single char token
|
||||
out[cnt++] = *str++;
|
||||
break;
|
||||
}
|
||||
|
||||
// regular single word
|
||||
do {
|
||||
if (cnt + 1 < UTIL_MAX_TOKEN) {
|
||||
out[cnt++] = *str++;
|
||||
}
|
||||
if (isSingleCharToken(*str)) {
|
||||
break;
|
||||
}
|
||||
} while ((u8)*str > ' ');
|
||||
break;
|
||||
}
|
||||
|
||||
out[cnt] = '\0';
|
||||
if (outCount) {
|
||||
*outCount = cnt;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *strDuplicate(const char *str)
|
||||
{
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
const u32 len = strlen(str);
|
||||
char *out = sysMemAlloc(len + 1);
|
||||
if (out) {
|
||||
memcpy(out, str, len + 1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include <PR/gbi.h>
|
||||
#include "platform.h"
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "video.h"
|
||||
|
||||
#include "../fast3d/gfx_api.h"
|
||||
#include "../fast3d/gfx_sdl.h"
|
||||
#include "../fast3d/gfx_opengl.h"
|
||||
|
||||
#ifdef PLATFORM_NSWITCH
|
||||
#define DEFAULT_VID_WIDTH 1280
|
||||
#define DEFAULT_VID_HEIGHT 720
|
||||
#define DEFAULT_VID_FULLSCREEN true
|
||||
#define DEFAULT_VID_FULLSCREEN_EXCLUSIVE true
|
||||
#else
|
||||
#define DEFAULT_VID_WIDTH 640
|
||||
#define DEFAULT_VID_HEIGHT 480
|
||||
#define DEFAULT_VID_FULLSCREEN false
|
||||
#define DEFAULT_VID_FULLSCREEN_EXCLUSIVE false
|
||||
#endif
|
||||
|
||||
static struct GfxWindowManagerAPI *wmAPI;
|
||||
static struct GfxRenderingAPI *renderingAPI;
|
||||
|
||||
static bool initDone = false;
|
||||
|
||||
static s32 vidWidth = DEFAULT_VID_WIDTH;
|
||||
static s32 vidHeight = DEFAULT_VID_HEIGHT;
|
||||
static s32 vidFramebuffers = true;
|
||||
static s32 vidFullscreen = DEFAULT_VID_FULLSCREEN;
|
||||
static s32 vidFullscreenExclusive = DEFAULT_VID_FULLSCREEN_EXCLUSIVE;
|
||||
static s32 vidMaximize = false;
|
||||
static s32 vidCenter = false;
|
||||
static s32 vidAllowHiDpi = false;
|
||||
static s32 vidVsync = 1;
|
||||
static s32 vidMSAA = 1;
|
||||
static s32 vidFramerateLimit = 0;
|
||||
|
||||
static s32 vidDisplayFPS = 0;
|
||||
static f32 vidDisplayFPSInterval = 1.f;
|
||||
static f32 vidAvgFPS = 0;
|
||||
|
||||
static s32 vidNumModes = 1;
|
||||
static displaymode vidModeDefault;
|
||||
static displaymode *vidModes = &vidModeDefault;
|
||||
|
||||
static f32 vidGlareBrightness = 1.f;
|
||||
static f32 vidOverexposureScale = 1.f;
|
||||
|
||||
static s32 texFilter = FILTER_LINEAR;
|
||||
static s32 texFilter2D = true;
|
||||
static s32 texDetail = false;
|
||||
static s32 texMipmapFilter = MIPMAP_LINEAR;
|
||||
static u32 texAnisotropicFilter = 4;
|
||||
|
||||
static u32 dlcount = 0;
|
||||
static u32 frames = 0;
|
||||
static f64 startTime, endTime;
|
||||
static f64 accumDelta = 0.0;
|
||||
static f64 fpsTime = 0.0;
|
||||
static s32 fpsNumFrames = 0;
|
||||
|
||||
static s32 videoInitDisplayModes(void);
|
||||
void optionsMenuInit();
|
||||
|
||||
s32 videoInit(void)
|
||||
{
|
||||
wmAPI = &gfx_sdl;
|
||||
renderingAPI = &gfx_opengl_api;
|
||||
|
||||
gfx_current_native_viewport.width = 320;
|
||||
gfx_current_native_viewport.height = 220;
|
||||
gfx_current_native_aspect = 320.f / 220.f;
|
||||
gfx_framebuffers_enabled = (bool)vidFramebuffers;
|
||||
gfx_detail_textures_enabled = (bool)texDetail;
|
||||
gfx_msaa_level = vidMSAA;
|
||||
|
||||
struct GfxInitSettings set = {
|
||||
.wapi = wmAPI,
|
||||
.rapi = renderingAPI,
|
||||
.window_settings = {
|
||||
.title = "Perfect Dark",
|
||||
.width = vidWidth,
|
||||
.height = vidHeight,
|
||||
.x = 100,
|
||||
.y = 100,
|
||||
.fullscreen = vidFullscreen,
|
||||
.fullscreen_is_exclusive = vidFullscreenExclusive,
|
||||
.maximized = vidMaximize,
|
||||
.centered = vidCenter,
|
||||
.allow_hidpi = vidAllowHiDpi
|
||||
}
|
||||
};
|
||||
|
||||
gfx_init(&set);
|
||||
|
||||
videoInitDisplayModes();
|
||||
videoSetVsync(vidVsync);
|
||||
videoSetFramerateLimit(vidFramerateLimit);
|
||||
|
||||
gfx_set_texture_filter((enum FilteringMode)texFilter);
|
||||
gfx_set_mipmap_filter((enum MipmapFilteringMode)texMipmapFilter);
|
||||
videoSetAnisotropicFilter(texAnisotropicFilter);
|
||||
optionsMenuInit();
|
||||
|
||||
initDone = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void videoStartFrame(void)
|
||||
{
|
||||
if (initDone) {
|
||||
startTime = wmAPI->get_time();
|
||||
gfx_start_frame();
|
||||
}
|
||||
|
||||
// Synchronize with their backend counterparts.
|
||||
vidFullscreen = videoGetFullscreen();
|
||||
vidMaximize = videoGetMaximizeWindow();
|
||||
}
|
||||
|
||||
void videoSubmitCommands(Gfx *cmds)
|
||||
{
|
||||
if (initDone) {
|
||||
gfx_run(cmds);
|
||||
++dlcount;
|
||||
}
|
||||
}
|
||||
|
||||
void videoEndFrame(void)
|
||||
{
|
||||
if (!initDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_end_frame();
|
||||
|
||||
++frames;
|
||||
++fpsNumFrames;
|
||||
|
||||
const f64 flipTime = wmAPI->get_time();
|
||||
accumDelta += flipTime - endTime;
|
||||
endTime = flipTime;
|
||||
|
||||
if (endTime >= fpsTime) {
|
||||
char tmp[128];
|
||||
vidAvgFPS = fpsNumFrames ? ((f64)fpsNumFrames / accumDelta) : 0.f;
|
||||
fpsNumFrames = 0;
|
||||
accumDelta = 0.0;
|
||||
fpsTime = endTime + vidDisplayFPSInterval;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
f32 videoGetAverageFPS(void)
|
||||
{
|
||||
return vidAvgFPS;
|
||||
}
|
||||
|
||||
void videoClearScreen(void)
|
||||
{
|
||||
videoStartFrame();
|
||||
// TODO: clear
|
||||
videoEndFrame();
|
||||
}
|
||||
|
||||
void *videoGetWindowHandle(void)
|
||||
{
|
||||
if (initDone) {
|
||||
return wmAPI->get_window_handle();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void videoUpdateNativeResolution(s32 w, s32 h)
|
||||
{
|
||||
gfx_current_native_viewport.width = w;
|
||||
gfx_current_native_viewport.height = h;
|
||||
gfx_current_native_aspect = (float)w / (float)h;
|
||||
}
|
||||
|
||||
s32 videoGetNativeWidth(void)
|
||||
{
|
||||
return gfx_current_native_viewport.width;
|
||||
}
|
||||
|
||||
s32 videoGetNativeHeight(void)
|
||||
{
|
||||
return gfx_current_native_viewport.height;
|
||||
}
|
||||
|
||||
s32 videoGetWidth(void)
|
||||
{
|
||||
return gfx_current_dimensions.width;
|
||||
}
|
||||
|
||||
s32 videoGetHeight(void)
|
||||
{
|
||||
return gfx_current_dimensions.height;
|
||||
}
|
||||
|
||||
s32 videoGetFullscreen(void)
|
||||
{
|
||||
vidFullscreen = wmAPI->get_fullscreen_state();
|
||||
return vidFullscreen;
|
||||
}
|
||||
|
||||
s32 videoGetFullscreenMode(void)
|
||||
{
|
||||
vidFullscreenExclusive = wmAPI->get_fullscreen_flag_mode();
|
||||
return vidFullscreenExclusive;
|
||||
}
|
||||
|
||||
s32 videoGetMaximizeWindow(void)
|
||||
{
|
||||
vidMaximize = wmAPI->get_maximized_state();
|
||||
return vidMaximize;
|
||||
}
|
||||
|
||||
s32 videoGetCenterWindow(void)
|
||||
{
|
||||
return vidCenter;
|
||||
}
|
||||
|
||||
f32 videoGetAspect(void)
|
||||
{
|
||||
return gfx_current_dimensions.aspect_ratio;
|
||||
}
|
||||
|
||||
s32 videoGetDisplayModeIndex(void)
|
||||
{
|
||||
for (s32 i = 1; i < vidNumModes; ++i) {
|
||||
if (vidModes[i].width == gfx_current_dimensions.width &&
|
||||
vidModes[i].height == gfx_current_dimensions.height) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Current dimensions don't match any known mode, so return index 0, "Custom".
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 videoGetMSAA(void)
|
||||
{
|
||||
vidMSAA = (s32)gfx_msaa_level;
|
||||
return vidMSAA;
|
||||
}
|
||||
|
||||
s32 videoGetVsync(void)
|
||||
{
|
||||
vidVsync = wmAPI->get_swap_interval();
|
||||
return vidVsync;
|
||||
}
|
||||
|
||||
s32 videoGetFramerateLimit(void)
|
||||
{
|
||||
vidFramerateLimit = wmAPI->get_target_fps();
|
||||
return vidFramerateLimit;
|
||||
}
|
||||
|
||||
s32 videoGetDisplayFPS(void)
|
||||
{
|
||||
return vidDisplayFPS;
|
||||
}
|
||||
|
||||
static s32 videoInitDisplayModes(void)
|
||||
{
|
||||
if (!wmAPI->get_current_display_mode(&vidModeDefault.width, &vidModeDefault.height)) {
|
||||
vidModeDefault.width = 640;
|
||||
vidModeDefault.height = 480;
|
||||
return false;
|
||||
}
|
||||
|
||||
const s32 numBaseModes = wmAPI->get_num_display_modes();
|
||||
if (!numBaseModes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const s32 numCustomModes = 1;
|
||||
displaymode *modeList = sysMemZeroAlloc((numBaseModes + numCustomModes) * sizeof(displaymode));
|
||||
if (!modeList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
modeList[0].width = 0;
|
||||
modeList[0].height = 0;
|
||||
|
||||
s32 numModes = 1;
|
||||
s32 w = -1, h = w, neww = w, newh = w;
|
||||
|
||||
// SDL modes are guaranteed to be sorted high to low
|
||||
for (s32 i = 0; i < numBaseModes; ++i) {
|
||||
wmAPI->get_display_mode(i, &neww, &newh);
|
||||
|
||||
if (neww != w || newh != h) {
|
||||
w = neww;
|
||||
h = newh;
|
||||
modeList[numModes].width = w;
|
||||
modeList[numModes].height = h;
|
||||
++numModes;
|
||||
}
|
||||
}
|
||||
|
||||
modeList = sysMemRealloc(modeList, numModes * sizeof(displaymode));
|
||||
if (!modeList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vidModes = modeList;
|
||||
vidNumModes = numModes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 videoGetDisplayMode(displaymode *out, const s32 index)
|
||||
{
|
||||
if (index >= 0 && index < vidNumModes) {
|
||||
*out = vidModes[index];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
s32 videoGetNumDisplayModes(void)
|
||||
{
|
||||
return vidNumModes;
|
||||
}
|
||||
|
||||
void videoSetDisplayMode(const s32 index)
|
||||
{
|
||||
const displaymode dm = vidModes[index];
|
||||
|
||||
if (index == 0) {
|
||||
// "Custom" video mode.
|
||||
return;
|
||||
}
|
||||
|
||||
vidWidth = dm.width;
|
||||
vidHeight = dm.height;
|
||||
|
||||
s32 posX = 100;
|
||||
s32 posY = 100;
|
||||
if (vidCenter) {
|
||||
wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY);
|
||||
}
|
||||
|
||||
if (vidFullscreen) {
|
||||
wmAPI->set_closest_resolution(vidWidth, vidHeight, vidCenter);
|
||||
} else {
|
||||
if (vidMaximize) {
|
||||
videoSetMaximizeWindow(false);
|
||||
} else {
|
||||
wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 videoGetTextureFilter2D(void)
|
||||
{
|
||||
return texFilter2D;
|
||||
}
|
||||
|
||||
u32 videoGetTextureFilter(void)
|
||||
{
|
||||
return texFilter;
|
||||
}
|
||||
|
||||
u32 videoGetAnisotropicFilter()
|
||||
{
|
||||
return texAnisotropicFilter;
|
||||
}
|
||||
|
||||
u32 videoGetMaxAnisotropyLevel()
|
||||
{
|
||||
return renderingAPI->get_max_anisotropy_level();
|
||||
}
|
||||
|
||||
s32 videoGetDetailTextures(void)
|
||||
{
|
||||
return texDetail;
|
||||
}
|
||||
|
||||
f32 videoGetGlareBrightness(void)
|
||||
{
|
||||
return vidGlareBrightness;
|
||||
}
|
||||
|
||||
f32 videoGetOverexposureScale(void)
|
||||
{
|
||||
return vidOverexposureScale;
|
||||
}
|
||||
|
||||
void videoSetWindowOffset(s32 x, s32 y)
|
||||
{
|
||||
gfx_current_game_window_viewport.x = x;
|
||||
gfx_current_game_window_viewport.y = y;
|
||||
}
|
||||
|
||||
void videoSetFullscreen(s32 fs)
|
||||
{
|
||||
if (fs != vidFullscreen) {
|
||||
vidFullscreen = !!fs;
|
||||
wmAPI->set_closest_resolution(vidWidth, vidHeight, vidCenter);
|
||||
wmAPI->set_fullscreen(vidFullscreen);
|
||||
if (!vidFullscreen && vidMaximize) {
|
||||
wmAPI->set_maximize(false);
|
||||
wmAPI->set_maximize(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void videoSetFullscreenMode(s32 mode)
|
||||
{
|
||||
vidFullscreenExclusive = mode;
|
||||
wmAPI->set_fullscreen_flag(mode);
|
||||
if (vidFullscreen) {
|
||||
wmAPI->set_fullscreen(false);
|
||||
wmAPI->set_fullscreen(true);
|
||||
}
|
||||
}
|
||||
|
||||
void videoSetMaximizeWindow(s32 fs)
|
||||
{
|
||||
if (fs != vidMaximize) {
|
||||
vidMaximize = !!fs;
|
||||
wmAPI->set_maximize(vidMaximize);
|
||||
if (vidCenter && !vidMaximize) {
|
||||
s32 posX = 0;
|
||||
s32 posY = 0;
|
||||
wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY);
|
||||
wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void videoSetCenterWindow(s32 center)
|
||||
{
|
||||
vidCenter = center;
|
||||
if (vidCenter && !vidMaximize) {
|
||||
s32 posX = 0;
|
||||
s32 posY = 0;
|
||||
wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY);
|
||||
wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY);
|
||||
}
|
||||
}
|
||||
|
||||
void videoSetTextureFilter(u32 filter)
|
||||
{
|
||||
if (filter > FILTER_THREE_POINT) filter = FILTER_THREE_POINT;
|
||||
if (texFilter == filter) return;
|
||||
texFilter = filter;
|
||||
gfx_set_texture_filter((enum FilteringMode)filter);
|
||||
}
|
||||
|
||||
void videoSetTextureFilter2D(s32 filter)
|
||||
{
|
||||
texFilter2D = !!filter;
|
||||
}
|
||||
|
||||
void videoSetAnisotropicFilter(u32 level)
|
||||
{
|
||||
texAnisotropicFilter = level;
|
||||
renderingAPI->set_anisotropy_level(level);
|
||||
}
|
||||
|
||||
void videoSetDetailTextures(s32 detail)
|
||||
{
|
||||
texDetail = !!detail;
|
||||
gfx_detail_textures_enabled = (bool)texDetail;
|
||||
}
|
||||
|
||||
void videoSetGlareBrightness(f32 bright)
|
||||
{
|
||||
vidGlareBrightness = (bright < 0.f ? 0.f : (bright > 1.f ? 1.f : bright));
|
||||
}
|
||||
|
||||
void videoSetOverexposureScale(f32 scale)
|
||||
{
|
||||
vidOverexposureScale = (scale < 0.f ? 0.f : (scale > 1.f ? 1.f : scale));
|
||||
}
|
||||
|
||||
s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize)
|
||||
{
|
||||
return gfx_create_framebuffer(w, h, upscale, autoresize);
|
||||
}
|
||||
|
||||
void videoSetMSAA(const s32 msaa)
|
||||
{
|
||||
vidMSAA = msaa;
|
||||
gfx_msaa_level = (u32)vidMSAA;
|
||||
}
|
||||
|
||||
void videoSetVsync(const s32 vsync)
|
||||
{
|
||||
vidVsync = wmAPI->set_swap_interval(vsync) ? vsync : 0;
|
||||
|
||||
if (vidVsync == 0 && vidFramerateLimit == 0) {
|
||||
// cap FPS if there's no vsync to prevent the game from exploding
|
||||
videoSetFramerateLimit(VIDEO_MAX_FPS);
|
||||
}
|
||||
}
|
||||
|
||||
void videoSetFramerateLimit(const s32 limit)
|
||||
{
|
||||
vidFramerateLimit = (vidVsync == 0 && limit == 0) ? VIDEO_MAX_FPS : limit;
|
||||
wmAPI->set_target_fps(vidFramerateLimit);
|
||||
}
|
||||
|
||||
void videoSetDisplayFPS(const s32 displayfps)
|
||||
{
|
||||
vidDisplayFPS = displayfps;
|
||||
}
|
||||
|
||||
void videoSetFramebuffer(s32 target)
|
||||
{
|
||||
return gfx_set_framebuffer(target, 1.f);
|
||||
}
|
||||
|
||||
void videoResetFramebuffer(void)
|
||||
{
|
||||
return gfx_reset_framebuffer();
|
||||
}
|
||||
|
||||
s32 videoFramebuffersSupported(void)
|
||||
{
|
||||
return gfx_framebuffers_enabled;
|
||||
}
|
||||
|
||||
void videoResizeFramebuffer(s32 target, u32 w, u32 h, s32 upscale, s32 autoresize)
|
||||
{
|
||||
gfx_resize_framebuffer(target, w, h, upscale, autoresize);
|
||||
}
|
||||
|
||||
void videoCopyFramebuffer(s32 dst, s32 src, s32 left, s32 top)
|
||||
{
|
||||
// assume immediate copies always read the front buffer
|
||||
gfx_copy_framebuffer(dst, src, left, top, false);
|
||||
}
|
||||
|
||||
void videoResetTextureCache(void)
|
||||
{
|
||||
gfx_texture_cache_clear();
|
||||
}
|
||||
|
||||
void videoFreeCachedTexture(const void *texptr)
|
||||
{
|
||||
gfx_texture_cache_delete(texptr);
|
||||
}
|
||||
|
||||
void videoFreeCachedTextures(const void *start, const void *end)
|
||||
{
|
||||
gfx_texture_cache_delete_range(start, end);
|
||||
}
|
||||
|
||||
void videoShutdown(void)
|
||||
{
|
||||
free(vidModes);
|
||||
}
|
||||
|
||||
PD_CONSTRUCTOR static void videoConfigInit(void)
|
||||
{
|
||||
configRegisterInt("Video.DefaultFullscreen", &vidFullscreen, 0, 1);
|
||||
configRegisterInt("Video.DefaultMaximize", &vidMaximize, 0, 1);
|
||||
configRegisterInt("Video.DefaultWidth", &vidWidth, 0, 32767);
|
||||
configRegisterInt("Video.DefaultHeight", &vidHeight, 0, 32767);
|
||||
configRegisterInt("Video.ExclusiveFullscreen", &vidFullscreenExclusive, 0, 1);
|
||||
configRegisterInt("Video.CenterWindow", &vidCenter, 0, 1);
|
||||
configRegisterInt("Video.AllowHiDpi", &vidAllowHiDpi, 0, 1);
|
||||
configRegisterInt("Video.VSync", &vidVsync, -1, 10);
|
||||
configRegisterInt("Video.FramebufferEffects", &vidFramebuffers, 0, 1);
|
||||
configRegisterInt("Video.FramerateLimit", &vidFramerateLimit, 0, VIDEO_MAX_FPS);
|
||||
configRegisterInt("Video.DisplayFPS", &vidDisplayFPS, 0, 1);
|
||||
configRegisterFloat("Video.DisplayFPSInterval", &vidDisplayFPSInterval, 0.01f, 32.f);
|
||||
configRegisterInt("Video.MSAA", &vidMSAA, 1, 16);
|
||||
configRegisterInt("Video.TextureFilter", &texFilter, 0, 2);
|
||||
configRegisterInt("Video.TextureFilter2D", &texFilter2D, 0, 1);
|
||||
configRegisterInt("Video.DetailTextures", &texDetail, 0, 1);
|
||||
configRegisterInt("Video.MipmapFilter", &texMipmapFilter, 0, 2);
|
||||
configRegisterInt("Video.AnisotropicFilter", &texAnisotropicFilter, 0, 16);
|
||||
configRegisterFloat("Video.GlareBrightness", &vidGlareBrightness, 0.f, 1.f);
|
||||
configRegisterFloat("Video.OverexposureScale", &vidOverexposureScale, 0.f, 1.f);
|
||||
}
|
||||
+2
-2
@@ -57,7 +57,7 @@ s32 func0f096890(s32 arg0)
|
||||
return value - (((value - nextvalue) * (arg0 & mask)) >> shiftamount);
|
||||
}
|
||||
|
||||
u16 acos(s16 arg0)
|
||||
u16 acosx(s16 arg0)
|
||||
{
|
||||
s32 value = arg0 >= 0 ? arg0 : -arg0;
|
||||
|
||||
@@ -70,7 +70,7 @@ u16 acos(s16 arg0)
|
||||
return value;
|
||||
}
|
||||
|
||||
s16 asin(s16 arg0)
|
||||
s16 asinx(s16 arg0)
|
||||
{
|
||||
s32 value = arg0 >= 0 ? arg0 : -arg0;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ f32 acosf(f32 value)
|
||||
intval = value * 32767.0f;
|
||||
}
|
||||
|
||||
return acos(intval) * M_PI / 65535.0f;
|
||||
return acosx(intval) * M_PI / 65535.0f;
|
||||
}
|
||||
|
||||
f32 asinf(f32 value)
|
||||
@@ -32,5 +32,5 @@ f32 asinf(f32 value)
|
||||
intval = value * 32767.0f;
|
||||
}
|
||||
|
||||
return asin(intval) * M_PI / 65535.0f;
|
||||
return asinx(intval) * M_PI / 65535.0f;
|
||||
}
|
||||
+26
-24
@@ -90,19 +90,8 @@ MenuItemHandlerResult amPickTargetMenuList(s32 operation, struct menuitem *item,
|
||||
switch (operation) {
|
||||
case MENUOP_GETOPTIONCOUNT:
|
||||
if (g_AmMenus[g_AmIndex].prevallbots) {
|
||||
// All bots: num bot opponents + all human players
|
||||
s32 count = 0;
|
||||
s32 i;
|
||||
struct chrdata *playerchr = g_Vars.currentplayer->prop->chr;
|
||||
|
||||
for (i = PLAYERCOUNT(); i < g_MpNumChrs; i++) {
|
||||
if (g_MpAllChrPtrs[i]->team != playerchr->team) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
count += PLAYERCOUNT();
|
||||
data->list.value = count;
|
||||
// All bots
|
||||
data->list.value = g_MpNumChrs;
|
||||
} else {
|
||||
// Single bot: All except the bot itself
|
||||
data->list.value = g_MpNumChrs - 1;
|
||||
@@ -125,9 +114,7 @@ MenuItemHandlerResult amPickTargetMenuList(s32 operation, struct menuitem *item,
|
||||
chrindex++;
|
||||
|
||||
if (g_AmMenus[g_AmIndex].prevallbots) {
|
||||
if (playerchr == g_MpAllChrPtrs[chrindex] || playerchr->team != g_MpAllChrPtrs[chrindex]->team) {
|
||||
numremaining--;
|
||||
}
|
||||
numremaining--;
|
||||
} else {
|
||||
if (botchr != g_MpAllChrPtrs[chrindex]) {
|
||||
numremaining--;
|
||||
@@ -139,7 +126,9 @@ MenuItemHandlerResult amPickTargetMenuList(s32 operation, struct menuitem *item,
|
||||
g_AmMenus[g_AmIndex].prevallbots = false;
|
||||
|
||||
for (i = 0; i < g_Vars.currentplayer->numaibuddies; i++) {
|
||||
botApplyAttack(g_MpAllChrPtrs[g_Vars.currentplayer->aibuddynums[i]], g_MpAllChrPtrs[chrindex]->prop);
|
||||
if (g_Vars.currentplayer->aibuddynums[i] != chrindex) {
|
||||
botApplyAttack(g_MpAllChrPtrs[g_Vars.currentplayer->aibuddynums[i]], g_MpAllChrPtrs[chrindex]->prop);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
botApplyAttack(botchr, g_MpAllChrPtrs[chrindex]->prop);
|
||||
@@ -167,9 +156,7 @@ MenuItemHandlerResult amPickTargetMenuList(s32 operation, struct menuitem *item,
|
||||
chrindex++;
|
||||
|
||||
if (g_AmMenus[g_AmIndex].prevallbots) {
|
||||
if (playerchr == g_MpAllChrPtrs[chrindex] || playerchr->team != g_MpAllChrPtrs[chrindex]->team) {
|
||||
numremaining--;
|
||||
}
|
||||
numremaining--;
|
||||
} else {
|
||||
if (botchr != g_MpAllChrPtrs[chrindex]) {
|
||||
numremaining--;
|
||||
@@ -190,7 +177,7 @@ MenuItemHandlerResult amPickTargetMenuList(s32 operation, struct menuitem *item,
|
||||
gdl = text0f153628(gdl);
|
||||
gdl = textRenderProjected(gdl, &x, &y, g_MpAllChrConfigPtrs[chrindex]->name, g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, viGetWidth(), viGetHeight(), 0, 0);
|
||||
gdl = text0f153780(gdl);
|
||||
return (s32)gdl;
|
||||
return (uintptr_t)gdl;
|
||||
}
|
||||
case MENUOP_GETOPTIONHEIGHT:
|
||||
data->list.value = LINEHEIGHT;
|
||||
@@ -364,7 +351,11 @@ void amApply(s32 slot)
|
||||
bgunEquipWeapon2(HAND_RIGHT, weaponnum);
|
||||
}
|
||||
|
||||
if (bgunGetWeaponNum(HAND_LEFT) != WEAPON_NONE) {
|
||||
// don't unequip detonator
|
||||
// if we already have it equipped
|
||||
if (weaponnum == WEAPON_REMOTEMINE) {
|
||||
bgunEquipWeapon2(HAND_LEFT, weaponnum);
|
||||
} else if (bgunGetWeaponNum(HAND_LEFT) != WEAPON_NONE) {
|
||||
bgunEquipWeapon2(HAND_LEFT, WEAPON_NONE);
|
||||
}
|
||||
}
|
||||
@@ -1270,6 +1261,13 @@ Gfx *amRender(Gfx *gdl)
|
||||
g_ScaleX = g_ViRes == VIRES_HI ? 2 : 1;
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
const s32 playercount = PLAYERCOUNT();
|
||||
if (playercount < 2 || (playercount == 2 && optionsGetScreenSplit() == SCREENSPLIT_HORIZONTAL)) {
|
||||
gSPSetExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT);
|
||||
}
|
||||
#endif
|
||||
|
||||
g_AmIndex = g_Vars.currentplayernum;
|
||||
g_Vars.currentplayer->commandingaibot = NULL;
|
||||
|
||||
@@ -1368,8 +1366,8 @@ Gfx *amRender(Gfx *gdl)
|
||||
vertices[6].colour = 4;
|
||||
vertices[7].colour = 4;
|
||||
|
||||
colours[0].word = 0x22222200;
|
||||
colours[1].word = 0x0000004f;
|
||||
colours[0].word = PD_BE32(0x22222200);
|
||||
colours[1].word = PD_BE32(0x0000004f);
|
||||
|
||||
gSPColor(gdl++, osVirtualToPhysical(colours), 2);
|
||||
gSPVertex(gdl++, osVirtualToPhysical(vertices), 8, 0);
|
||||
@@ -1698,6 +1696,10 @@ Gfx *amRender(Gfx *gdl)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
gSPClearExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT);
|
||||
#endif
|
||||
|
||||
g_ScaleX = 1;
|
||||
|
||||
return gdl;
|
||||
|
||||
+75
-54
@@ -12,6 +12,10 @@
|
||||
#include "lib/str.h"
|
||||
#include "data.h"
|
||||
#include "types.h"
|
||||
#ifndef PLATFORM_N64
|
||||
#include "game/player.h"
|
||||
#include "input.h"
|
||||
#endif
|
||||
|
||||
void amTick(void)
|
||||
{
|
||||
@@ -23,7 +27,7 @@ void amTick(void)
|
||||
g_AmIndex = g_Vars.currentplayernum;
|
||||
|
||||
if (g_AmMenus[g_AmIndex].togglefunc) {
|
||||
if (bgunConsiderToggleGunFunction(60, false, true) > 0) {
|
||||
if (bgunConsiderToggleGunFunction(60, false, true, 0) > 0) {
|
||||
g_AmMenus[g_AmIndex].togglefunc = false;
|
||||
}
|
||||
} else {
|
||||
@@ -40,15 +44,38 @@ void amTick(void)
|
||||
s8 contpadnum = optionsGetContpadNum1(g_Vars.currentplayerstats->mpindex);
|
||||
s32 numsamples = joyGetNumSamples();
|
||||
s32 j;
|
||||
u32 amask, lrtmask, umask, dmask, lmask, rmask;
|
||||
|
||||
if (controlmode == CONTROLMODE_PC) {
|
||||
amask = D_JPAD;
|
||||
lrtmask = R_TRIG;
|
||||
umask = U_CBUTTONS;
|
||||
dmask = D_CBUTTONS;
|
||||
lmask = L_CBUTTONS;
|
||||
rmask = R_CBUTTONS;
|
||||
} else {
|
||||
amask = A_BUTTON;
|
||||
lrtmask = L_TRIG | R_TRIG;
|
||||
umask = U_JPAD | U_CBUTTONS;
|
||||
dmask = D_JPAD | D_CBUTTONS;
|
||||
lmask = L_JPAD | L_CBUTTONS;
|
||||
rmask = R_JPAD | R_CBUTTONS;
|
||||
}
|
||||
|
||||
for (j = 0; j < numsamples; j++) {
|
||||
s8 gotonextscreen = false;
|
||||
s8 cstickx = joyGetStickXOnSample(j, contpadnum);
|
||||
s8 csticky = joyGetStickYOnSample(j, contpadnum);
|
||||
#ifdef AVOID_UB
|
||||
// if cstickx is -128, it will get negated and stored into absstickx, negating it again if it's 8 bit
|
||||
s32 absstickx;
|
||||
s32 abssticky;
|
||||
#else
|
||||
s8 absstickx;
|
||||
s8 abssticky;
|
||||
u16 buttonsstate = joyGetButtonsOnSample(j, contpadnum, 0xffff);
|
||||
u16 buttonspressed = joyGetButtonsPressedOnSample(j, contpadnum, 0xffff);
|
||||
#endif
|
||||
u32 buttonsstate = joyGetButtonsOnSample(j, contpadnum, 0xffffffff);
|
||||
u32 buttonspressed = joyGetButtonsPressedOnSample(j, contpadnum, 0xffffffff);
|
||||
bool stickpushed = false;
|
||||
s32 slotnum;
|
||||
bool stayopen;
|
||||
@@ -63,8 +90,28 @@ void amTick(void)
|
||||
|
||||
g_AmMenus[g_AmIndex].allbots = false;
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
s32 newstickx = (s32)cstickx;
|
||||
s32 newsticky = (s32)csticky;
|
||||
if (j == 0 && g_Vars.currentplayernum == 0 && inputMouseIsLocked()) {
|
||||
f32 mdx, mdy;
|
||||
struct activemenu *am = &g_AmMenus[g_AmIndex];
|
||||
inputMouseGetAbsScaledDelta(&mdx, &mdy);
|
||||
if (mdx || mdy) {
|
||||
am->mousex += mdx * PLAYER_EXTCFG().radialmenuspeed;
|
||||
am->mousey += mdy * PLAYER_EXTCFG().radialmenuspeed;
|
||||
am->mousex = (am->mousex > 127.f) ? 127.f : (am->mousex < -128.f) ? -128.f : am->mousex;
|
||||
am->mousey = (am->mousey > 127.f) ? 127.f : (am->mousey < -128.f) ? -128.f : am->mousey;
|
||||
}
|
||||
newstickx += (s32)am->mousex;
|
||||
newsticky -= (s32)am->mousey;
|
||||
}
|
||||
cstickx = (newstickx < -128) ? -128 : (newstickx > 127) ? 127 : newstickx;
|
||||
csticky = (newsticky < -128) ? -128 : (newsticky > 127) ? 127 : newsticky;
|
||||
#endif
|
||||
|
||||
if (g_Vars.currentplayer->activemenumode == AMMODE_EDIT) {
|
||||
buttonsstate = buttonsstate & A_BUTTON;
|
||||
buttonsstate = buttonsstate & amask;
|
||||
cstickx = 0;
|
||||
csticky = 0;
|
||||
buttonspressed = 0;
|
||||
@@ -72,12 +119,12 @@ void amTick(void)
|
||||
|
||||
// JPN fixes the bug that's documented in amChangeScreen
|
||||
if (controlmode == CONTROLMODE_13 || controlmode == CONTROLMODE_14) {
|
||||
if ((buttonsstate & R_TRIG) || (buttonsstate & L_TRIG)) {
|
||||
if (buttonsstate & (L_TRIG | R_TRIG)) {
|
||||
stayopen = true;
|
||||
}
|
||||
|
||||
if (buttonsstate & A_BUTTON) {
|
||||
#if VERSION >= VERSION_JPN_FINAL
|
||||
#if VERSION >= VERSION_JPN_FINAL || !defined(PLATFORM_N64)
|
||||
if (g_Vars.currentplayer->numaibuddies > 0) {
|
||||
g_AmMenus[g_AmIndex].allbots = true;
|
||||
}
|
||||
@@ -86,12 +133,12 @@ void amTick(void)
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if (buttonsstate & A_BUTTON) {
|
||||
if (buttonsstate & amask) {
|
||||
stayopen = true;
|
||||
}
|
||||
|
||||
if ((buttonsstate & R_TRIG) || (buttonsstate & L_TRIG)) {
|
||||
#if VERSION >= VERSION_JPN_FINAL
|
||||
if (buttonsstate & lrtmask) {
|
||||
#if VERSION >= VERSION_JPN_FINAL || !defined(PLATFORM_N64)
|
||||
if (g_Vars.currentplayer->numaibuddies > 0) {
|
||||
g_AmMenus[g_AmIndex].allbots = true;
|
||||
}
|
||||
@@ -118,35 +165,19 @@ void amTick(void)
|
||||
amChangeScreen(0);
|
||||
}
|
||||
|
||||
if (buttonsstate & U_CBUTTONS) {
|
||||
if (buttonsstate & umask) {
|
||||
row = 0;
|
||||
}
|
||||
|
||||
if (buttonsstate & D_CBUTTONS) {
|
||||
if (buttonsstate & dmask) {
|
||||
row = 2;
|
||||
}
|
||||
|
||||
if (buttonsstate & L_CBUTTONS) {
|
||||
if (buttonsstate & lmask) {
|
||||
column = 0;
|
||||
}
|
||||
|
||||
if (buttonsstate & R_CBUTTONS) {
|
||||
column = 2;
|
||||
}
|
||||
|
||||
if (buttonsstate & U_JPAD) {
|
||||
row = 0;
|
||||
}
|
||||
|
||||
if (buttonsstate & D_JPAD) {
|
||||
row = 2;
|
||||
}
|
||||
|
||||
if (buttonsstate & L_JPAD) {
|
||||
column = 0;
|
||||
}
|
||||
|
||||
if (buttonsstate & R_JPAD) {
|
||||
if (buttonsstate & rmask) {
|
||||
column = 2;
|
||||
}
|
||||
|
||||
@@ -157,8 +188,8 @@ void amTick(void)
|
||||
s8 contpadnum2 = optionsGetContpadNum2(g_Vars.currentplayerstats->mpindex);
|
||||
s8 cstickx2 = joyGetStickXOnSample(j, contpadnum2);
|
||||
s8 csticky2 = joyGetStickYOnSample(j, contpadnum2);
|
||||
u16 buttonsstate2 = joyGetButtonsOnSample(j, contpadnum2, 0xffff);
|
||||
u16 buttonspressed2 = joyGetButtonsPressedOnSample(j, contpadnum2, 0xffff);
|
||||
u32 buttonsstate2 = joyGetButtonsOnSample(j, contpadnum2, 0xffffffff);
|
||||
u32 buttonspressed2 = joyGetButtonsPressedOnSample(j, contpadnum2, 0xffffffff);
|
||||
|
||||
if (g_Vars.currentplayer->activemenumode == AMMODE_EDIT) {
|
||||
buttonsstate2 = buttonsstate2 & A_BUTTON;
|
||||
@@ -175,35 +206,19 @@ void amTick(void)
|
||||
toggle = true;
|
||||
}
|
||||
|
||||
if (buttonsstate2 & U_CBUTTONS) {
|
||||
if (buttonsstate2 & umask) {
|
||||
row = 0;
|
||||
}
|
||||
|
||||
if (buttonsstate2 & D_CBUTTONS) {
|
||||
if (buttonsstate2 & dmask) {
|
||||
row = 2;
|
||||
}
|
||||
|
||||
if (buttonsstate2 & L_CBUTTONS) {
|
||||
if (buttonsstate2 & lmask) {
|
||||
column = 0;
|
||||
}
|
||||
|
||||
if (buttonsstate2 & R_CBUTTONS) {
|
||||
column = 2;
|
||||
}
|
||||
|
||||
if (buttonsstate2 & U_JPAD) {
|
||||
row = 0;
|
||||
}
|
||||
|
||||
if (buttonsstate2 & D_JPAD) {
|
||||
row = 2;
|
||||
}
|
||||
|
||||
if (buttonsstate2 & L_JPAD) {
|
||||
column = 0;
|
||||
}
|
||||
|
||||
if (buttonsstate2 & R_JPAD) {
|
||||
if (buttonsstate2 & rmask) {
|
||||
column = 2;
|
||||
}
|
||||
|
||||
@@ -248,7 +263,7 @@ void amTick(void)
|
||||
stayopen = false;
|
||||
}
|
||||
|
||||
#if VERSION >= VERSION_NTSC_1_0
|
||||
#if (VERSION >= VERSION_NTSC_1_0) && defined(PLATFORM_N64)
|
||||
if (g_Vars.lvupdate240 == 0) {
|
||||
stayopen = false;
|
||||
}
|
||||
@@ -369,6 +384,12 @@ void amTick(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef PLATFORM_N64
|
||||
else {
|
||||
g_AmMenus[g_AmIndex].mousex = 0.f;
|
||||
g_AmMenus[g_AmIndex].mousey = 0.f;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_Vars.currentplayer->activemenumode != AMMODE_EDIT) {
|
||||
s16 dist;
|
||||
@@ -407,7 +428,7 @@ void amTick(void)
|
||||
|
||||
// Update alpha of slots so they fade in
|
||||
if (g_AmMenus[g_AmIndex].alphafrac < 1) {
|
||||
g_AmMenus[g_AmIndex].alphafrac += LVUPDATE60FREAL() / 30.0f;
|
||||
g_AmMenus[g_AmIndex].alphafrac += (f32)g_Vars.lvupdate240 / (4.f * 30.0f);
|
||||
}
|
||||
|
||||
if (g_AmMenus[g_AmIndex].alphafrac > 1) {
|
||||
@@ -415,7 +436,7 @@ void amTick(void)
|
||||
}
|
||||
|
||||
// Make selection border pulsate
|
||||
g_AmMenus[g_AmIndex].selpulse += LVUPDATE60FREAL() / 5.0f;
|
||||
g_AmMenus[g_AmIndex].selpulse += (f32)g_Vars.lvupdate240 / (4.f * 5.0f);
|
||||
|
||||
if (g_AmMenus[g_AmIndex].selpulse > 18.849555969238f) {
|
||||
g_AmMenus[g_AmIndex].selpulse -= 18.849555969238f;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "game/game_0b2150.h"
|
||||
#include "game/tex.h"
|
||||
#include "game/sky.h"
|
||||
#include "game/game_13c510.h"
|
||||
#include "game/artifact.h"
|
||||
#include "game/bg.h"
|
||||
#include "game/stagetable.h"
|
||||
#include "game/room.h"
|
||||
@@ -16,6 +16,77 @@
|
||||
#include "lib/mtx.h"
|
||||
#include "data.h"
|
||||
#include "types.h"
|
||||
#ifndef PLATFORM_N64
|
||||
#include "lib/collision.h"
|
||||
#include "lib/lib_17ce0.h"
|
||||
#include "game/player.h"
|
||||
#include "game/prop.h"
|
||||
#include "video.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Artifacts are points of interest in the z-buffer.
|
||||
*
|
||||
* They correspond to:
|
||||
* - Individual circles in the lens flare effect from the sun.
|
||||
* - Individual corners of each light fixture's box.
|
||||
*
|
||||
* The game needs to do line of sight checks to these points. The collision
|
||||
* system would normally be used for line of sight checks, but it only works
|
||||
* with basic geometric volumes and also doesn't take into account the player's
|
||||
* gun. Instead, the checks are done by reading from the previous frame's
|
||||
* z-buffer.
|
||||
*
|
||||
* Typically, the z-buffer would be read by the scheduler after a graphics frame
|
||||
* is rendered, but this only works if the z-buffer is a complete image.
|
||||
* PD draws the scene, then clears the z-buffer before drawing the player's gun,
|
||||
* so the z-buffer at the end only contains the player's gun. To work around
|
||||
* this, the GPU is given commands to read the z-buffer as a texture and copy
|
||||
* depth values to a safe place before clearing it. Then once the frame is
|
||||
* rendered, the scheduler compares the saved depths with the current z-buffer
|
||||
* and updates the artifact, applying the minimum of the two depths.
|
||||
*
|
||||
* The scheduler maintains 3 arrays of artifacts, each referenced by indexes
|
||||
* which rotate between them.
|
||||
* - Artifacts are written by the main thread using the write index.
|
||||
* The write and front indexes are then incremented.
|
||||
* - The same artifacts are updated by the scheduler using the pending index.
|
||||
* The pending index is then updated.
|
||||
* - The artifacts are then rendered on a later frame using the front index.
|
||||
*
|
||||
* In each artifacts array, the first several slots are reserved for the sun
|
||||
* flare artifacts. The quantity depends on the number of suns in the stage
|
||||
* (Skedar Ruins has 3) and there are 8 artifacts per sun. The remaining slots
|
||||
* are used for light fixture corners. The array is big enough to handle at
|
||||
* least 90 lights on screen at a time.
|
||||
*
|
||||
* The initial state of the indexes are write=0, front=1, pending=0.
|
||||
* These are set in sched_reset_artifacts.
|
||||
*
|
||||
* The detailed workflow is:
|
||||
* - CPU: Light artifacts are determined and added to write artifacts (0) array
|
||||
* - CPU: Constructs the gdl in this order:
|
||||
* - Render scene
|
||||
* - Copy relevant parts of z-buffer to write depths (0) array
|
||||
* - Clear z-buffer and render gun
|
||||
* - Render artifacts by reading from front artifacts (1) array
|
||||
* - CPU: increments write (0 -> 1) and front (1 -> 2) indexes
|
||||
* - GPU: Executes above task. g_ArtifactDepths0 now contains depths, and is
|
||||
* pointed to by pending.
|
||||
* - Scheduler: Reads the z-buffer, which at this point only contains the gun
|
||||
* depth information, and updates the artifact with the minimum of the two
|
||||
* - Scheduler: Increments pending (0 -> 1)
|
||||
*
|
||||
* It appears that the front index should be initialised to 2 instead, so that
|
||||
* it's one frame behind the others rather than two frames behind.
|
||||
*
|
||||
* There's no doubt this went through several iterations before landing on this
|
||||
* implementation. It's likely that this was implemented using a single and full
|
||||
* z-buffer and it worked well until they decided to clear the z-buffer before
|
||||
* rendering the gun. There is evidence in zbuf.c that they allocated a second
|
||||
* z-buffer and swapped it. But when memory got too tight they had to change it
|
||||
* to this texture read method.
|
||||
*/
|
||||
|
||||
u8 *var800a41a0;
|
||||
u32 var800a41a4;
|
||||
@@ -38,11 +109,25 @@ void artifactsTick(void)
|
||||
schedIncrementFrontArtifacts();
|
||||
}
|
||||
|
||||
u16 func0f13c574(f32 arg0)
|
||||
u16 floatToN64Depth(f32 arg0)
|
||||
{
|
||||
/**
|
||||
* Method to convert a 32 bit floating point depth value to the
|
||||
* unsigned 16 bit integer format used by the zbuffer on the N64.
|
||||
* The argument arg0 represents z normalized to [0, 1] * 32704.0f.
|
||||
*
|
||||
* It works by converting the depth value to a large, unsigned 32 bit
|
||||
* integer before scaling back down to an unsigned 16 bit integer.
|
||||
* The scaling is done using bit shift operations on the most & least
|
||||
* significant bytes of the resulting 16 bit integer, likely because
|
||||
* bit shift operations are faster than division. The segmentation of
|
||||
* this calculation at values 0x3f800, 0x3f000, etc. is probably
|
||||
* intended to reduce z-fighting by adding more differentiation for
|
||||
* distant z values.
|
||||
*/
|
||||
u32 value = arg0 * 8.0f;
|
||||
u32 left;
|
||||
u32 right = value;
|
||||
u32 left; // forms the most significant byte of the final u16
|
||||
u32 right = value; // forms the least significant byte of the final u16
|
||||
|
||||
if (value > 0x3f800) {
|
||||
right = value & 0x7ff;
|
||||
@@ -81,7 +166,7 @@ u16 func0f13c574(f32 arg0)
|
||||
return left << 13 | (right << 2);
|
||||
}
|
||||
|
||||
s32 func0f13c710(f32 arg0)
|
||||
s32 artifactsFloatToInt(f32 arg0)
|
||||
{
|
||||
if (arg0 > 0.0f) {
|
||||
if (arg0 > 2147483520.0f) {
|
||||
@@ -96,6 +181,34 @@ s32 func0f13c710(f32 arg0)
|
||||
return arg0;
|
||||
}
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
|
||||
bool artifactTestLos(struct coord *spec, struct coord *roompos, s32 xi, s32 yi)
|
||||
{
|
||||
s32 i = 0;
|
||||
|
||||
if (!g_Vars.currentplayer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct coord endpos;
|
||||
endpos.x = roompos->x + spec->x;
|
||||
endpos.y = roompos->y + spec->y;
|
||||
endpos.z = roompos->z + spec->z;
|
||||
|
||||
struct coord gundir2d;
|
||||
struct coord gunpos2d = {{ 0.f, 0.f, 0.f }};
|
||||
struct coord gundir3d;
|
||||
struct coord gunpos3d = g_Vars.currentplayer->cam_pos;
|
||||
f32 crosspos[2] = { (f32)xi, (f32)yi };
|
||||
cam0f0b4c3c(crosspos, &gundir2d, 1.f);
|
||||
mtx4RotateVec(camGetProjectionMtxF(), &gundir2d, &gundir3d);
|
||||
|
||||
return shotTestLos(&gunpos2d, &gundir2d, &gunpos3d, &gundir3d, &endpos);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void artifactsCalculateGlaresForRoom(s32 roomnum)
|
||||
{
|
||||
s32 i;
|
||||
@@ -134,6 +247,12 @@ void artifactsCalculateGlaresForRoom(s32 roomnum)
|
||||
struct coord *campos = &g_Vars.currentplayer->cam_pos;
|
||||
struct artifact *artifact;
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
if (videoGetGlareBrightness() <= 0.f) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_Rooms[roomnum].gfxdata != NULL && g_Rooms[roomnum].loaded240) {
|
||||
numlights = g_Rooms[roomnum].gfxdata->numlights;
|
||||
|
||||
@@ -198,8 +317,8 @@ void artifactsCalculateGlaresForRoom(s32 roomnum)
|
||||
|
||||
if (spdc[3] > 0.0001f) {
|
||||
f20 = 1.0f / spdc[3];
|
||||
x = func0f13c710(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f));
|
||||
y = func0f13c710(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f));
|
||||
x = artifactsFloatToInt(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f));
|
||||
y = artifactsFloatToInt(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f));
|
||||
f0 = (spdc[2] * f20 * 511.0f + 511.0f) * 32.0f;
|
||||
|
||||
if (f0 < 32576.0f) {
|
||||
@@ -301,8 +420,8 @@ void artifactsCalculateGlaresForRoom(s32 roomnum)
|
||||
f20 = -9999.0f;
|
||||
}
|
||||
|
||||
xi = func0f13c710(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f));
|
||||
yi = func0f13c710(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f));
|
||||
xi = artifactsFloatToInt(viewleft + (1.0f + spdc[0] * f20) * (viewwidth * 0.5f));
|
||||
yi = artifactsFloatToInt(viewtop + (1.0f - spdc[1] * f20) * (viewheight * 0.5f));
|
||||
f0 = (spdc[2] * f20 * 511.0f + 511.0f) * 32.0f;
|
||||
|
||||
if (g_ZbufPtr1
|
||||
@@ -322,12 +441,21 @@ void artifactsCalculateGlaresForRoom(s32 roomnum)
|
||||
}
|
||||
|
||||
if (index < MAX_ARTIFACTS) {
|
||||
artifact->unk04 = func0f13c574(f0) >> 2;
|
||||
artifact->unk08 = &g_ZbufPtr1[viGetWidth() * yi + xi];
|
||||
#ifndef PLATFORM_N64
|
||||
artifact->visiblelos = artifactTestLos(&spec, &g_BgRooms[roomnum].pos, xi, yi);
|
||||
#endif
|
||||
/**
|
||||
* the original game performs artifact depth comparison
|
||||
* using the N64 depth values divided by 4. This is
|
||||
* accomplished by bit shifting the N64 depth value
|
||||
* to the right using >> 2
|
||||
*/
|
||||
artifact->expecteddepth = floatToN64Depth(f0) >> 2;
|
||||
artifact->zbufptr = &g_ZbufPtr1[viGetWidth() * yi + xi];
|
||||
artifact->light = &roomlights[i];
|
||||
artifact->type = ARTIFACTTYPE_GLARE;
|
||||
artifact->unk0c.u16_2 = xi;
|
||||
artifact->unk0c.u16_1 = yi;
|
||||
artifact->screenx = xi;
|
||||
artifact->screeny = yi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +466,7 @@ void artifactsCalculateGlaresForRoom(s32 roomnum)
|
||||
}
|
||||
}
|
||||
|
||||
u8 func0f13d3c4(u8 arg0, u8 arg1)
|
||||
u8 artifactsClamp(u8 arg0, u8 arg1)
|
||||
{
|
||||
if (arg1 >= arg0 + 7) {
|
||||
return arg0 + 7;
|
||||
@@ -386,18 +514,18 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum)
|
||||
u16 min;
|
||||
u16 max;
|
||||
f32 lightop_cur_frac;
|
||||
s32 t2;
|
||||
s32 numgood;
|
||||
struct light *light;
|
||||
u8 *s3;
|
||||
s32 k;
|
||||
s32 count;
|
||||
u16 t4;
|
||||
u16 actualdepth;
|
||||
f32 add;
|
||||
s32 l;
|
||||
f32 brightness;
|
||||
s32 avg;
|
||||
s32 tolerance;
|
||||
f32 f0;
|
||||
s32 v1;
|
||||
s32 difference;
|
||||
s32 r;
|
||||
s32 g;
|
||||
s32 b;
|
||||
@@ -411,6 +539,13 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum)
|
||||
f32 f24;
|
||||
bool extra;
|
||||
f32 f26;
|
||||
#ifndef PLATFORM_N64
|
||||
const f32 pfov = g_Vars.currentplayerstats ? PLAYER_DEFAULT_FOV : 60.f;
|
||||
const f32 brightscale = videoGetGlareBrightness();
|
||||
if (brightscale <= 0.f) {
|
||||
return gdl;
|
||||
}
|
||||
#endif
|
||||
|
||||
artifacts = schedGetFrontArtifacts();
|
||||
lightop_cur_frac = roomGetLightOpCurFrac(roomnum);
|
||||
@@ -423,6 +558,12 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum)
|
||||
struct light *light2 = artifacts[i].light;
|
||||
count = 0;
|
||||
|
||||
/**
|
||||
* light arifacts are created from several, closely spaced
|
||||
* textures that give the appearance of a dynamic light glare
|
||||
* as the character moves. Loop to count all the sub-artifacts
|
||||
* in this light.
|
||||
*/
|
||||
for (j = i; j < MAX_ARTIFACTS && artifacts[j].type == ARTIFACTTYPE_GLARE && artifacts[j].light == light2; j++) {
|
||||
count++;
|
||||
}
|
||||
@@ -433,48 +574,70 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum)
|
||||
if (roomnum == light->roomnum) {
|
||||
lightindex = ((uintptr_t)light - (uintptr_t)g_BgLightsFileData) / sizeof(struct light);
|
||||
s3 = &var800a41a0[lightindex * 3];
|
||||
t2 = 0;
|
||||
numgood = 0;
|
||||
min = 0xffff;
|
||||
max = 0;
|
||||
|
||||
/**
|
||||
* loop to determine the min & max depth of
|
||||
* the sub-artifacts composing this room light.
|
||||
*/
|
||||
for (k = i; k < i + count; k++) {
|
||||
if (artifacts[k].unk04 > max) {
|
||||
max = artifacts[k].unk04;
|
||||
if (artifacts[k].expecteddepth > max) {
|
||||
max = artifacts[k].expecteddepth;
|
||||
}
|
||||
|
||||
if (artifacts[k].unk04 < min) {
|
||||
min = artifacts[k].unk04;
|
||||
if (artifacts[k].expecteddepth < min) {
|
||||
min = artifacts[k].expecteddepth;
|
||||
}
|
||||
}
|
||||
|
||||
avg = (max - min) >> 1;
|
||||
/**
|
||||
* Define a depth tolerance from the min & max
|
||||
* depths of sub-artifacts for a given light.
|
||||
* This will be used to determine which light
|
||||
* artifacts are visible when comparing depth
|
||||
* values of lights to other items rendered
|
||||
* on screen. Without this, the lights would
|
||||
* constantly flicker due to z-fighting caused
|
||||
* by the low precision of the N64 depth.
|
||||
*/
|
||||
tolerance = (max - min) >> 1;
|
||||
|
||||
if (avg < 25) {
|
||||
avg = 25;
|
||||
if (tolerance < 25) {
|
||||
tolerance = 25;
|
||||
}
|
||||
|
||||
for (k = i; k < i + count; k++) {
|
||||
u16 tmp;
|
||||
t4 = (artifacts[k].unk02 & 0xfffc) >> 2;
|
||||
tmp = artifacts[k].unk04;
|
||||
#ifdef PLATFORM_N64
|
||||
u16 expecteddepth;
|
||||
actualdepth = (artifacts[k].actualdepth & 0xfffc) >> 2;
|
||||
expecteddepth = artifacts[k].expecteddepth;
|
||||
|
||||
if (tmp < t4) {
|
||||
v1 = t4 - tmp;
|
||||
if (expecteddepth < actualdepth) {
|
||||
difference = actualdepth - expecteddepth;
|
||||
} else {
|
||||
v1 = tmp - t4;
|
||||
difference = expecteddepth - actualdepth;
|
||||
}
|
||||
|
||||
if (avg >= v1) {
|
||||
t2++;
|
||||
if (difference <= tolerance) {
|
||||
numgood++;
|
||||
}
|
||||
#else
|
||||
numgood += artifacts[k].visiblelos;
|
||||
#endif
|
||||
|
||||
artifacts[k].type = ARTIFACTTYPE_FREE;
|
||||
}
|
||||
|
||||
s3[0] = func0f13d3c4(s3[0], t2 * 2);
|
||||
s3[0] = artifactsClamp(s3[0], numgood * 2);
|
||||
|
||||
if (t2 > 0) {
|
||||
if (numgood > 0) {
|
||||
#ifndef PLATFORM_N64
|
||||
brightness = viGetFovY() / pfov;
|
||||
#else
|
||||
brightness = viGetFovY() * 0.017453292f;
|
||||
#endif
|
||||
add = cosf(brightness) / sinf(brightness) * 14.6f;
|
||||
|
||||
if (lightIsHealthy(roomnum, lightindex - g_Rooms[roomnum].gfxdata->lightsindex)) {
|
||||
@@ -504,6 +667,9 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum)
|
||||
}
|
||||
|
||||
f0 = s3[2] * (1.0f / 255.0f);
|
||||
#ifndef PLATFORM_N64
|
||||
f0 *= (60.f / pfov);
|
||||
#endif
|
||||
|
||||
skySetOverexposure((s32) ((f32)f0 * r), (s32) ((f32)f0 * g), (s32) ((f32)f0 * b));
|
||||
|
||||
@@ -553,6 +719,10 @@ Gfx *artifactsRenderGlaresForRoom(Gfx *gdl, s32 roomnum)
|
||||
alpha *= (s3[1] / 255.0f);
|
||||
alpha *= (s3[0] / 8.0f);
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
alpha *= brightscale;
|
||||
#endif
|
||||
|
||||
if (USINGDEVICE(DEVICE_NIGHTVISION)) {
|
||||
alpha *= lightop_cur_frac * 7.0f;
|
||||
}
|
||||
+254
-97
@@ -15,7 +15,7 @@
|
||||
#include "game/sky.h"
|
||||
#include "game/stars.h"
|
||||
#include "game/dyntex.h"
|
||||
#include "game/game_13c510.h"
|
||||
#include "game/artifact.h"
|
||||
#include "game/game_1531a0.h"
|
||||
#include "game/gfxmemory.h"
|
||||
#include "game/gfxreplace.h"
|
||||
@@ -43,6 +43,12 @@
|
||||
#include "data.h"
|
||||
#include "gbiex.h"
|
||||
#include "types.h"
|
||||
#ifndef PLATFORM_N64
|
||||
#include "preprocess.h"
|
||||
#include "system.h"
|
||||
#include "video.h"
|
||||
#include "platform.h"
|
||||
#endif
|
||||
|
||||
#define BGCMD_END 0x00
|
||||
#define BGCMD_PUSH 0x01
|
||||
@@ -86,13 +92,13 @@ u32 var800a4920;
|
||||
u32 g_BgSection3;
|
||||
struct room *g_Rooms;
|
||||
u8 *g_MpRoomVisibility;
|
||||
s16 g_BgForceOnscreenRooms[350];
|
||||
RoomNum g_BgForceOnscreenRooms[350];
|
||||
s32 g_BgNumForceOnscreenRooms;
|
||||
u16 g_BgUnloadDelay240;
|
||||
u16 g_BgUnloadDelay240_2;
|
||||
u32 var800a4bf4;
|
||||
s16 g_GlareRooms[100];
|
||||
u32 *g_BgPrimaryData2;
|
||||
RoomNum g_GlareRooms[100];
|
||||
uintptr_t *g_BgPrimaryData2;
|
||||
struct bgroom *g_BgRooms;
|
||||
struct bgportal *g_BgPortals;
|
||||
struct portalmetric *g_PortalMetrics;
|
||||
@@ -108,7 +114,7 @@ struct portalcamcacheitem *g_PortalCameraCache;
|
||||
struct bgsnake g_BgSnake;
|
||||
|
||||
s32 g_StageIndex = 1;
|
||||
u32 var8007fc04 = 0;
|
||||
uintptr_t var8007fc04 = 0;
|
||||
u8 *var8007fc08 = NULL;
|
||||
|
||||
#if VERSION < VERSION_NTSC_1_0
|
||||
@@ -129,13 +135,16 @@ s32 g_BgMostAttemptedDrawSlots = 0;
|
||||
s32 g_BgNumRoomLoadCandidates = 0;
|
||||
u16 g_BgFrameCount = 0xfffe;
|
||||
s32 g_BgNumPortalCameraCacheItems = 0;
|
||||
#ifndef PLATFORM_N64
|
||||
bool g_BgHitXluDisabled = false;
|
||||
#endif
|
||||
|
||||
void bgUnpausePropsInRoom(u32 roomnum, bool tintedglassonly)
|
||||
{
|
||||
struct prop *prop;
|
||||
struct defaultobj *obj;
|
||||
s16 *propnumptr;
|
||||
s16 rooms[2];
|
||||
RoomNum rooms[2];
|
||||
s16 propnums[256];
|
||||
|
||||
rooms[0] = roomnum;
|
||||
@@ -283,7 +292,7 @@ Gfx *bgRenderXrayData(Gfx *gdl, struct xraydata *xraydata)
|
||||
vertices[i].y = xraydata->vertices[i][1];
|
||||
vertices[i].z = xraydata->vertices[i][2];
|
||||
vertices[i].colour = i << 2;
|
||||
colours[i].word = xraydata->colours[i];
|
||||
colours[i].word = PD_BE32(xraydata->colours[i]);
|
||||
}
|
||||
|
||||
count = xraydata->numvertices;
|
||||
@@ -697,17 +706,17 @@ Gfx *bgRenderGdlInXray(Gfx *gdl, s8 *readgdl, Vtx *vertices, s16 arg3[3])
|
||||
xraydata.numvertices = 0;
|
||||
|
||||
while (true) {
|
||||
if (readgdl[0] == G_ENDDL) {
|
||||
if (readgdl[GFX_W0_BYTE(0)] == G_ENDDL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (readgdl[0] == G_MTX) {
|
||||
if (readgdl[GFX_W0_BYTE(0)] == G_MTX) {
|
||||
// empty
|
||||
} else if (readgdl[0] == G_VTX) {
|
||||
} else if (readgdl[GFX_W0_BYTE(0)] == G_VTX) {
|
||||
Gfx *cmd = (Gfx *) readgdl;
|
||||
s32 dmemindex = cmd->bytes[1] & 0xf;
|
||||
s32 numvertices = ((u32) cmd->bytes[1] >> 4) + 1;
|
||||
u32 offset = cmd->words.w1 & 0xffffff;
|
||||
s32 dmemindex = cmd->bytes[GFX_W0_BYTE(1)] & 0xf;
|
||||
s32 numvertices = ((u32) cmd->bytes[GFX_W0_BYTE(1)] >> 4) + 1;
|
||||
u32 offset = UNSEGADDR(cmd->words.w1) & 0xffffff;
|
||||
|
||||
for (i = 0; i < numvertices; i++) {
|
||||
Vtx *vtx = (Vtx *) (verticesu8 + offset);
|
||||
@@ -720,14 +729,14 @@ Gfx *bgRenderGdlInXray(Gfx *gdl, s8 *readgdl, Vtx *vertices, s16 arg3[3])
|
||||
|
||||
offset += sizeof(Vtx);
|
||||
}
|
||||
} else if (readgdl[0] == G_TRI1) {
|
||||
} else if (readgdl[GFX_W0_BYTE(0)] == G_TRI1) {
|
||||
Gfx *cmd = (Gfx *) readgdl;
|
||||
s16 x = cmd->tri.tri.v[0] / 10;
|
||||
s16 y = cmd->tri.tri.v[1] / 10;
|
||||
s16 z = cmd->tri.tri.v[2] / 10;
|
||||
s16 x = cmd->tri.tri.v[GFX_TRI_VTX(0)] / 10;
|
||||
s16 y = cmd->tri.tri.v[GFX_TRI_VTX(1)] / 10;
|
||||
s16 z = cmd->tri.tri.v[GFX_TRI_VTX(2)] / 10;
|
||||
|
||||
gdl = bgProcessXrayTri(gdl, &xraydata, dmemvertices[x], dmemvertices[y], dmemvertices[z], dmemcolours[x], dmemcolours[y], dmemcolours[z], inrange[x], inrange[y], inrange[z]);
|
||||
} else if (readgdl[0] == G_TRI4) {
|
||||
} else if (readgdl[GFX_W0_BYTE(0)] == G_TRI4) {
|
||||
Gfx *cmd = (Gfx *) readgdl;
|
||||
s16 x;
|
||||
s16 y;
|
||||
@@ -758,7 +767,7 @@ Gfx *bgRenderGdlInXray(Gfx *gdl, s8 *readgdl, Vtx *vertices, s16 arg3[3])
|
||||
gdl = bgProcessXrayTri(gdl, &xraydata, dmemvertices[x], dmemvertices[y], dmemvertices[z], dmemcolours[x], dmemcolours[y], dmemcolours[z], inrange[x], inrange[y], inrange[z]);
|
||||
}
|
||||
|
||||
readgdl += 8;
|
||||
readgdl += sizeof(Gfx);
|
||||
}
|
||||
|
||||
gdl = bgRenderXrayData(gdl, &xraydata);
|
||||
@@ -869,11 +878,11 @@ Gfx *bgRenderRoomInXray(Gfx *gdl, s32 roomnum)
|
||||
|
||||
Gfx *bgRenderSceneInXray(Gfx *gdl)
|
||||
{
|
||||
s16 *roomnumptr;
|
||||
s16 *room;
|
||||
RoomNum *roomnumptr;
|
||||
RoomNum *room;
|
||||
s16 i;
|
||||
s32 j;
|
||||
s16 roomnumsbyprop[200];
|
||||
RoomNum roomnumsbyprop[200];
|
||||
struct prop *prop;
|
||||
struct prop **ptr;
|
||||
s32 k;
|
||||
@@ -969,17 +978,19 @@ Gfx *bgRenderScene(Gfx *gdl)
|
||||
s32 firstroomnum = -1;
|
||||
s32 i;
|
||||
s32 roomnum;
|
||||
s16 roomnumsbyprop[200];
|
||||
RoomNum roomnumsbyprop[200];
|
||||
struct prop **ptr;
|
||||
struct drawslot *thing;
|
||||
s16 *roomnumptr;
|
||||
RoomNum *roomnumptr;
|
||||
struct prop *prop;
|
||||
s16 tmp;
|
||||
s16 *room;
|
||||
RoomNum *room;
|
||||
s16 roomorder[60];
|
||||
s16 roomnums[60];
|
||||
RoomNum roomnums[60];
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
g_NumRoomsWithGlares = 0;
|
||||
#endif
|
||||
|
||||
if (g_Vars.currentplayer->visionmode == VISIONMODE_XRAY) {
|
||||
gdl = bgRenderSceneInXray(gdl);
|
||||
@@ -1201,6 +1212,7 @@ Gfx *bgRenderScene(Gfx *gdl)
|
||||
gdl = propsRender(gdl, thing->roomnum, RENDERPASS_XLU, roomnumsbyprop);
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_N64 // on PC we do this earlier, before prop matrices turn into garbage
|
||||
if (!g_Vars.mplayerisrunning) {
|
||||
artifactsCalculateGlaresForRoom(thing->roomnum);
|
||||
|
||||
@@ -1208,6 +1220,7 @@ Gfx *bgRenderScene(Gfx *gdl)
|
||||
g_GlareRooms[g_NumRoomsWithGlares++] = thing->roomnum;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VERSION < VERSION_NTSC_1_0
|
||||
@@ -1459,7 +1472,7 @@ void bgReset(s32 stagenum)
|
||||
u32 section2compsize;
|
||||
u32 section2start;
|
||||
u32 section1compsize;
|
||||
u32 scratch;
|
||||
uintptr_t scratch;
|
||||
|
||||
var8007fc0c = 8;
|
||||
|
||||
@@ -1492,11 +1505,19 @@ void bgReset(s32 stagenum)
|
||||
// Copy section 1 header to stack and parse into variables
|
||||
header = (u8 *)ALIGN16((uintptr_t)headerbuffer);
|
||||
bgLoadFile(header, 0, 0x40);
|
||||
#ifndef PLATFORM_N64
|
||||
preprocessBgSection1Header(header, 0x40);
|
||||
#endif
|
||||
inflatedsize = *(u32 *)&header[0];
|
||||
section1compsize = *(u32 *)&header[4];
|
||||
primcompsize = *(u32 *)&header[8];
|
||||
var8007fc54 = inflatedsize - primcompsize;
|
||||
var8007fc54 -= 0xc;
|
||||
|
||||
#ifdef PLATFORM_64BIT
|
||||
inflatedsize = romdataFileGetEstimatedSize(inflatedsize, LOADTYPE_BG);
|
||||
#endif
|
||||
|
||||
inflatedsize = ALIGN16(inflatedsize);
|
||||
|
||||
// Allocate space for the primary bg data
|
||||
@@ -1516,6 +1537,10 @@ void bgReset(s32 stagenum)
|
||||
scratch += 0xc;
|
||||
bgInflate((u8 *) scratch, g_BgPrimaryData, primcompsize);
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
preprocessBgSection1(g_BgPrimaryData, inflatedsize, 0x0f000000);
|
||||
#endif
|
||||
|
||||
// Shrink the allocation (ie. free the scratch space)
|
||||
mempRealloc(g_BgPrimaryData, inflatedsize, MEMPOOL_STAGE);
|
||||
|
||||
@@ -1523,6 +1548,9 @@ void bgReset(s32 stagenum)
|
||||
section2start = section1compsize + 0xc;
|
||||
|
||||
bgLoadFile(header, section2start, 0x40);
|
||||
#ifndef PLATFORM_N64
|
||||
preprocessBgSection2Header(header, 0x40);
|
||||
#endif
|
||||
|
||||
inflatedsize = (*(u16 *) &header[0] & 0x7fff) - 1;
|
||||
section2compsize = *(u16 *) &header[2];
|
||||
@@ -1552,6 +1580,10 @@ void bgReset(s32 stagenum)
|
||||
// Iterate texture IDs and ensure they're loaded
|
||||
inflatedsize = (*(u16 *) &header[0] & 0x7fff) >> 1;
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
preprocessBgSection2((u8 *)section2, inflatedsize);
|
||||
#endif
|
||||
|
||||
for (i = 0; i ^ inflatedsize; i++) {
|
||||
texLoadFromTextureNum(section2[i] & 0xffff & 0xffff & 0xffff & 0xffff & 0xffff & 0xffff & 0xffff & 0xffff, NULL);
|
||||
}
|
||||
@@ -1575,7 +1607,7 @@ void bgReset(s32 stagenum)
|
||||
// Copy 0x40 bytes from a random location in ROM to a random
|
||||
// location in RAM. The write address can be anywhere in the
|
||||
// boot segment or in the lib segment up to modelRenderNodeDl.
|
||||
dmaExec((void *)(PHYS_TO_K0(0x1000) + (random() & 0x1fff8)), random() & 0x1fffe, 0x40);
|
||||
dmaExec((void *)(PHYS_TO_K0(0x1000) + (rngRandom() & 0x1fff8)), rngRandom() & 0x1fffe, 0x40);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1583,7 +1615,7 @@ void bgReset(s32 stagenum)
|
||||
var800a4920 = *(u32 *)g_BgPrimaryData;
|
||||
|
||||
if (var800a4920 == 0) {
|
||||
g_BgPrimaryData2 = (u32 *)g_BgPrimaryData;
|
||||
g_BgPrimaryData2 = (uintptr_t*)g_BgPrimaryData;
|
||||
g_BgRooms = (struct bgroom *)(g_BgPrimaryData2[1] + g_BgPrimaryData - 0x0f000000);
|
||||
goto foo; foo:;
|
||||
g_Vars.roomcount = 0;
|
||||
@@ -1667,6 +1699,9 @@ void bgBuildTables(s32 stagenum)
|
||||
g_Rooms[i].unk4d = 0;
|
||||
g_Rooms[i].lightop = 0;
|
||||
g_Rooms[i].unk4e_04 = 0;
|
||||
#ifndef PLATFORM_N64
|
||||
g_Rooms[i].extra_flags = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bgSetStageTranslationThing(g_Stages[g_StageIndex].unk14);
|
||||
@@ -1891,6 +1926,9 @@ void bgBuildTables(s32 stagenum)
|
||||
// Load and read the header
|
||||
header = (u8 *)ALIGN16((uintptr_t)headerbuffer);
|
||||
bgLoadFile(header, g_BgSection3, 0x40);
|
||||
#ifndef PLATFORM_N64
|
||||
preprocessBgSection3Header(header, 0x40);
|
||||
#endif
|
||||
inflatedsize = (*(u16 *)&header[0] & 0x7fff) - 1;
|
||||
section3compsize = *(u16 *)&header[2];
|
||||
inflatedsize = (inflatedsize | 0xf) + 1;
|
||||
@@ -1909,6 +1947,9 @@ void bgBuildTables(s32 stagenum)
|
||||
|
||||
bgLoadFile(scratch, g_BgSection3 + 4, ((section3compsize - 1) | 0xf) + 1);
|
||||
bgInflate(scratch, section3, section3compsize);
|
||||
#ifndef PLATFORM_N64
|
||||
preprocessBgSection3(section3, section3compsize);
|
||||
#endif
|
||||
|
||||
// Section 3 starts with a table of room bounding boxes
|
||||
bboxptr = (s16 *) section3;
|
||||
@@ -2186,13 +2227,26 @@ Gfx *bgScissorToViewport(Gfx *gdl)
|
||||
|
||||
Gfx *bgScissorWithinViewportF(Gfx *gdl, f32 viewleft, f32 viewtop, f32 viewright, f32 viewbottom)
|
||||
{
|
||||
gdl = bgScissorWithinViewport(gdl, viewleft, viewtop, ceil(viewright), ceil(viewbottom));
|
||||
gdl = bgScissorWithinViewport(gdl, viewleft, viewtop, ceiltoint(viewright), ceiltoint(viewbottom));
|
||||
|
||||
return gdl;
|
||||
}
|
||||
|
||||
Gfx *bgScissorWithinViewport(Gfx *gdl, s32 viewleft, s32 viewtop, s32 viewright, s32 viewbottom)
|
||||
{
|
||||
#ifndef PLATFORM_N64
|
||||
const s32 xmargin = videoGetWidth() / SCREEN_320 - 1;
|
||||
const s32 ymargin = videoGetHeight() / SCREEN_240 - 1;
|
||||
if (xmargin > 0) {
|
||||
viewleft -= xmargin;
|
||||
viewright += xmargin;
|
||||
}
|
||||
if (ymargin > 0) {
|
||||
viewtop -= ymargin;
|
||||
viewbottom += ymargin;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (viewleft < g_Vars.currentplayer->viewleft) {
|
||||
viewleft = g_Vars.currentplayer->viewleft;
|
||||
}
|
||||
@@ -2762,12 +2816,28 @@ void bgLoadRoom(s32 roomnum)
|
||||
alloclen += 1024;
|
||||
}
|
||||
} else {
|
||||
#ifdef PLATFORM_N64
|
||||
alloclen = memaGetLongestFree();
|
||||
#else
|
||||
// probably never reaches here in practice as all rooms have gfxdatalen
|
||||
// alloc 10k and hope for the best
|
||||
alloclen = 10240;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_64BIT
|
||||
alloclen = alloclen * 4; // just to be safe for now, adjust properly later #TODO
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
bgGarbageCollectRooms(alloclen, false);
|
||||
|
||||
allocation = memaAlloc(alloclen);
|
||||
#else
|
||||
// allocate room data from heap to not take up mema space
|
||||
allocation = sysMemAlloc(alloclen);
|
||||
#endif
|
||||
|
||||
if (allocation != NULL) {
|
||||
dyntexSetCurrentRoom(roomnum);
|
||||
@@ -2795,6 +2865,9 @@ void bgLoadRoom(s32 roomnum)
|
||||
|
||||
// Inflate the data to the left side of the allocation
|
||||
inflatedlen = bgInflate(memaddr, allocation, g_BgRooms[roomnum + 1].unk00 - g_BgRooms[roomnum].unk00);
|
||||
#ifndef PLATFORM_N64
|
||||
inflatedlen = preprocessBgRoom(allocation, inflatedlen, g_BgRooms[roomnum].unk00);
|
||||
#endif
|
||||
|
||||
g_Rooms[roomnum].gfxdata = (struct roomgfxdata *)allocation;
|
||||
|
||||
@@ -2874,7 +2947,7 @@ void bgLoadRoom(s32 roomnum)
|
||||
|
||||
// Copy gdls to the right-side of the allocation
|
||||
// and build a pointer array to them
|
||||
texCopyGdls((void *) gfxblocks[0], (void *) (allocation + alloclen - (gfxblocks[numgdls] - gfxblocks[0])), (u32) (gfxblocks[numgdls] - gfxblocks[0]));
|
||||
texCopyGdls((void *) gfxblocks[0], (void *) (allocation + alloclen - (gfxblocks[numgdls] - gfxblocks[0])), (uintptr_t) (gfxblocks[numgdls] - gfxblocks[0]));
|
||||
|
||||
for (i = 0; i < numgdls + 1; i++) {
|
||||
gdlpointers[i] = gfxblocks[i] + (allocation + alloclen - gfxblocks[numgdls]);
|
||||
@@ -2913,9 +2986,11 @@ void bgLoadRoom(s32 roomnum)
|
||||
|
||||
g_Rooms[roomnum].loaded240 = 1;
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
if (g_Rooms[roomnum].gfxdatalen != alloclen) {
|
||||
memaRealloc((intptr_t) allocation, alloclen, g_Rooms[roomnum].gfxdatalen);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update gdl pointers in the gfxdata so they point to the ones
|
||||
// that have been processed by textLoadFromGdl.
|
||||
@@ -3000,14 +3075,22 @@ void bgUnloadRoom(s32 roomnum)
|
||||
u32 size;
|
||||
|
||||
if (g_Rooms[roomnum].vtxbatches) {
|
||||
#ifdef PLATFORM_N64
|
||||
size = ((g_Rooms[roomnum].numvtxbatches) * sizeof(struct vtxbatch) + 0xf) & ~0xf;
|
||||
memaFree(g_Rooms[roomnum].vtxbatches, size);
|
||||
#else
|
||||
sysMemFree(g_Rooms[roomnum].vtxbatches);
|
||||
#endif
|
||||
g_Rooms[roomnum].vtxbatches = NULL;
|
||||
}
|
||||
|
||||
if (g_Rooms[roomnum].gfxdatalen > 0) {
|
||||
#ifdef PLATFORM_N64
|
||||
size = g_Rooms[roomnum].gfxdatalen;
|
||||
memaFree(g_Rooms[roomnum].gfxdata, size);
|
||||
#else
|
||||
sysMemFree(g_Rooms[roomnum].gfxdata);
|
||||
#endif
|
||||
g_Rooms[roomnum].gfxdata = NULL;
|
||||
}
|
||||
|
||||
@@ -3037,6 +3120,7 @@ void bgUnloadAllRooms(void)
|
||||
*/
|
||||
void bgGarbageCollectRooms(s32 bytesneeded, bool desparate)
|
||||
{
|
||||
#ifdef PLATFORM_N64 // don't need this on PC as rooms are allocated from heap
|
||||
s32 bytesfree = memaGetLongestFree();
|
||||
s32 oldestroom;
|
||||
s32 oldesttimer;
|
||||
@@ -3084,6 +3168,7 @@ void bgGarbageCollectRooms(s32 bytesneeded, bool desparate)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3144,8 +3229,13 @@ Gfx *bgRenderRoomPass(Gfx *gdl, s32 roomnum, struct roomblock *block, bool arg3)
|
||||
v0 = (uintptr_t)g_Rooms[roomnum].colours;
|
||||
|
||||
if (v0 != NULL) {
|
||||
#ifdef PLATFORM_N64
|
||||
s32 addr = ALIGN8((uintptr_t)&g_Rooms[roomnum].gfxdata->vertices[g_Rooms[roomnum].gfxdata->numvertices]);
|
||||
v0 += (((intptr_t)block->colours - addr) >> 2) * 4;
|
||||
#else
|
||||
uintptr_t addr = ALIGN8((uintptr_t)&g_Rooms[roomnum].gfxdata->vertices[g_Rooms[roomnum].gfxdata->numvertices]);
|
||||
v0 += (((uintptr_t)block->colours - addr) >> 2) * 4;
|
||||
#endif
|
||||
} else {
|
||||
v0 = (uintptr_t)block->colours;
|
||||
}
|
||||
@@ -3269,8 +3359,8 @@ s32 bgPopulateVtxBatchType(s32 roomnum, struct vtxbatch *batches, Gfx *gdl, s32
|
||||
batches[batchindex].bbmax.f[j] = -32768.0f;
|
||||
}
|
||||
|
||||
numvertices = (((u32)gdl[i].bytes[1] >> 4) & 0xf) + 1;
|
||||
batchvertices = (Vtx *)((uintptr_t)vertices + (gdl[i].words.w1 & 0xffffff));
|
||||
numvertices = (((u32)gdl[i].bytes[GFX_W0_BYTE(1)] >> 4) & 0xf) + 1;
|
||||
batchvertices = (Vtx *)((uintptr_t)vertices + (UNSEGADDR(gdl[i].words.w1) & 0xffffff));
|
||||
|
||||
for (j = 0; j < numvertices; j++) {
|
||||
f32 x = batchvertices[j].x;
|
||||
@@ -3369,7 +3459,11 @@ void bgFindRoomVtxBatches(s32 roomnum)
|
||||
|
||||
batchindex += xlucount;
|
||||
|
||||
#ifdef PLATFORM_N64
|
||||
batches = memaAlloc((batchindex * sizeof(struct vtxbatch) + 0xf) & ~0xf);
|
||||
#else
|
||||
batches = sysMemAlloc((batchindex * sizeof(struct vtxbatch) + 0xf) & ~0xf);
|
||||
#endif
|
||||
|
||||
if (batches != NULL) {
|
||||
gdl = bgGetNextGdlInLayer(roomnum, NULL, VTXBATCHTYPE_OPA);
|
||||
@@ -3556,7 +3650,7 @@ bool bgTestHitOnObj(struct coord *arg0, struct coord *arg1, struct coord *arg2,
|
||||
Gfx *imggdl = NULL;
|
||||
s32 texturenum;
|
||||
f32 lowestsqdist = MAXFLOAT;
|
||||
s32 offset;
|
||||
uintptr_t offset;
|
||||
s32 numvertices;
|
||||
Gfx *tri4gdl;
|
||||
s32 count;
|
||||
@@ -3578,9 +3672,19 @@ bool bgTestHitOnObj(struct coord *arg0, struct coord *arg1, struct coord *arg2,
|
||||
break;
|
||||
} else if (gdl->dma.cmd == G_VTX) {
|
||||
ptr = var800a6470;
|
||||
count = gdl->bytes[1] & 0xf;
|
||||
offset = (gdl->words.w1 & 0xffffff);
|
||||
numvertices = (((u32)gdl->bytes[1] >> 4) & 0xf) + 1;
|
||||
count = gdl->bytes[GFX_W0_BYTE(1)] & 0xf;
|
||||
#ifdef PLATFORM_N64
|
||||
offset = (UNSEGADDR(gdl->words.w1) & 0xffffff);
|
||||
#else
|
||||
if (gdl->words.w1 & 1) {
|
||||
// segmented address
|
||||
offset = (UNSEGADDR(gdl->words.w1) & 0xffffff);
|
||||
} else {
|
||||
// linear address
|
||||
offset = gdl->words.w1 - (uintptr_t)vertices;
|
||||
}
|
||||
#endif
|
||||
numvertices = (((u32) gdl->bytes[GFX_W0_BYTE(1)] >> 4) & 0xf) + 1;
|
||||
vtx = (Vtx *)((uintptr_t)vertices + offset);
|
||||
vtx -= count;
|
||||
|
||||
@@ -3684,9 +3788,9 @@ bool bgTestHitOnObj(struct coord *arg0, struct coord *arg1, struct coord *arg2,
|
||||
if (gdl->dma.cmd == G_TRI1) {
|
||||
trisremaining = 0;
|
||||
triref = 0;
|
||||
points[0] = gdl->tri.tri.v[0] / 10;
|
||||
points[1] = gdl->tri.tri.v[1] / 10;
|
||||
points[2] = gdl->tri.tri.v[2] / 10;
|
||||
points[0] = gdl->tri.tri.v[GFX_TRI_VTX(0)] / 10;
|
||||
points[1] = gdl->tri.tri.v[GFX_TRI_VTX(1)] / 10;
|
||||
points[2] = gdl->tri.tri.v[GFX_TRI_VTX(2)] / 10;
|
||||
} else if (gdl->dma.cmd == G_TRI4) {
|
||||
tri4gdl = gdl;
|
||||
trisremaining = 3;
|
||||
@@ -3780,19 +3884,27 @@ bool bgTestHitOnObj(struct coord *arg0, struct coord *arg1, struct coord *arg2,
|
||||
hit = true;
|
||||
|
||||
if (imggdl == NULL
|
||||
|| (imggdl->words.w1 & 0x0f000000) == 0x0f000000
|
||||
|| (imggdl->words.w1 & 0x05000000) == 0x05000000) {
|
||||
#ifdef PLATFORM_N64
|
||||
|| (imggdl->words.w1 & 0x0f000000) == 0x0f000000
|
||||
|| (imggdl->words.w1 & 0x05000000) == 0x05000000) {
|
||||
#else // not sure if the above check even works right on N64, but we can test easily for seg addresses
|
||||
|| (imggdl->words.w1 & 1)) {
|
||||
#endif
|
||||
texturenum = -1;
|
||||
} else {
|
||||
s32 tmp = PHYS_TO_K0(imggdl->words.w1 - 8);
|
||||
#ifdef PLATFORM_N64
|
||||
s32 tmp = PHYS_TO_K0(UNSEGADDR(imggdl->words.w1) - 8);
|
||||
#else
|
||||
uintptr_t tmp = PHYS_TO_K0(UNSEGADDR(imggdl->words.w1) - 8);
|
||||
#endif
|
||||
texturenum = *(s16 *) tmp;
|
||||
}
|
||||
|
||||
lowestsqdist = sqdist;
|
||||
|
||||
hitthing->unk00.x = sp8c.x;
|
||||
hitthing->unk00.y = sp8c.y;
|
||||
hitthing->unk00.z = sp8c.z;
|
||||
hitthing->pos.x = sp8c.x;
|
||||
hitthing->pos.y = sp8c.y;
|
||||
hitthing->pos.z = sp8c.z;
|
||||
hitthing->unk0c.x = sp80.x;
|
||||
hitthing->unk0c.y = sp80.y;
|
||||
hitthing->unk0c.z = sp80.z;
|
||||
@@ -3875,13 +3987,13 @@ bool bgTestHitOnChr(struct model *model, struct coord *arg1, struct coord *arg2,
|
||||
}
|
||||
break;
|
||||
} else if (gdl->dma.cmd == G_MTX) {
|
||||
word = gdl->words.w1 & 0xffffff;
|
||||
word = UNSEGADDR(gdl->words.w1) & 0xffffff;
|
||||
i = word / sizeof(Mtxf);
|
||||
mtx = &model->matrices[i];
|
||||
} else if (gdl->dma.cmd == G_VTX) {
|
||||
count = (gdl->bytes[1] & 0xf);
|
||||
word = gdl->words.w1 & 0xffffff;
|
||||
numvertices = ((u32)gdl->bytes[1] >> 4) + 1;
|
||||
count = (gdl->bytes[GFX_W0_BYTE(1)] & 0xf);
|
||||
word = UNSEGADDR(gdl->words.w1) & 0xffffff;
|
||||
numvertices = ((u32) gdl->bytes[GFX_W0_BYTE(1)] >> 4) + 1;
|
||||
vtx = (Vtx *)((uintptr_t)vertices + word);
|
||||
|
||||
if (count < spdc) {
|
||||
@@ -3971,9 +4083,9 @@ bool bgTestHitOnChr(struct model *model, struct coord *arg1, struct coord *arg2,
|
||||
if (gdl->dma.cmd == G_TRI1) {
|
||||
i = 0;
|
||||
triref = 0;
|
||||
points[0] = gdl->tri.tri.v[0] / 10;
|
||||
points[1] = gdl->tri.tri.v[1] / 10;
|
||||
points[2] = gdl->tri.tri.v[2] / 10;
|
||||
points[0] = gdl->tri.tri.v[GFX_TRI_VTX(0)] / 10;
|
||||
points[1] = gdl->tri.tri.v[GFX_TRI_VTX(1)] / 10;
|
||||
points[2] = gdl->tri.tri.v[GFX_TRI_VTX(2)] / 10;
|
||||
} else if (gdl->dma.cmd == G_TRI4) {
|
||||
tri4gdl = gdl;
|
||||
i = 3;
|
||||
@@ -4068,9 +4180,9 @@ bool bgTestHitOnChr(struct model *model, struct coord *arg1, struct coord *arg2,
|
||||
|
||||
*sqdistptr = sqdist;
|
||||
|
||||
hitthing->unk00.x = sp84.x;
|
||||
hitthing->unk00.y = sp84.y;
|
||||
hitthing->unk00.z = sp84.z;
|
||||
hitthing->pos.x = sp84.x;
|
||||
hitthing->pos.y = sp84.y;
|
||||
hitthing->pos.z = sp84.z;
|
||||
hitthing->unk0c.x = sp78.x;
|
||||
hitthing->unk0c.y = sp78.y;
|
||||
hitthing->unk0c.z = sp78.z;
|
||||
@@ -4141,8 +4253,8 @@ bool bgTestHitInVtxBatch(struct coord *arg0, struct coord *arg1, struct coord *a
|
||||
|
||||
vtx = bgFindVerticesForGdl(roomnum, gdl);
|
||||
iter = &gdl[batch->gbicmdindex];
|
||||
vtx = (Vtx *)((iter->words.w1 & 0xffffff) + (s32)vtx);
|
||||
numvertices = (((u32)iter->bytes[1] >> 4) & 0xf) + 1;
|
||||
vtx = (Vtx *)((UNSEGADDR(iter->words.w1) & 0xffffff) + (uintptr_t)vtx);
|
||||
numvertices = (((u32) iter->bytes[GFX_W0_BYTE(1)] >> 4) & 0xf) + 1;
|
||||
ptr = var800a6470;
|
||||
|
||||
while (numvertices > 0) {
|
||||
@@ -4169,9 +4281,9 @@ bool bgTestHitInVtxBatch(struct coord *arg0, struct coord *arg1, struct coord *a
|
||||
if (iter->dma.cmd == G_TRI1) {
|
||||
trisremaining = 0;
|
||||
triref = 0;
|
||||
points[0] = iter->tri.tri.v[0] / 10;
|
||||
points[1] = iter->tri.tri.v[1] / 10;
|
||||
points[2] = iter->tri.tri.v[2] / 10;
|
||||
points[0] = iter->tri.tri.v[GFX_TRI_VTX(0)] / 10;
|
||||
points[1] = iter->tri.tri.v[GFX_TRI_VTX(1)] / 10;
|
||||
points[2] = iter->tri.tri.v[GFX_TRI_VTX(2)] / 10;
|
||||
} else if (iter->dma.cmd == G_TRI4) {
|
||||
tri4gdl = iter;
|
||||
trisremaining = 3;
|
||||
@@ -4272,29 +4384,41 @@ bool bgTestHitInVtxBatch(struct coord *arg0, struct coord *arg1, struct coord *a
|
||||
|
||||
tmpgdl = iter;
|
||||
|
||||
while (tmpgdl->bytes[0] != G_SETTIMG && tmpgdl > gdl) {
|
||||
while (tmpgdl->bytes[GFX_W0_BYTE(0)] != G_SETTIMG && tmpgdl > gdl) {
|
||||
tmpgdl--;
|
||||
}
|
||||
|
||||
if (tmpgdl == gdl
|
||||
#ifdef PLATFORM_N64
|
||||
|| (tmpgdl->words.w1 & 0x0f000000) == 0x0f000000
|
||||
|| (tmpgdl->words.w1 & 0x05000000) == 0x05000000) {
|
||||
#else // not sure if the above check even works right on N64, but we can test easily for seg addresses
|
||||
|| (tmpgdl->words.w1 & 1)) {
|
||||
#endif
|
||||
texturenum = -1;
|
||||
} else {
|
||||
s32 tmp = tmpgdl->words.w1 - 8;
|
||||
#ifdef PLATFORM_N64
|
||||
s32 tmp = UNSEGADDR(tmpgdl->words.w1) - 8;
|
||||
#else
|
||||
uintptr_t tmp = UNSEGADDR(tmpgdl->words.w1) - 8;
|
||||
#endif
|
||||
texturenum = *(s16 *) PHYS_TO_K0(tmp);
|
||||
}
|
||||
|
||||
#ifdef AVOID_UB
|
||||
if (batch->type == VTXBATCHTYPE_XLU && texturenum >= 0 && g_Textures[texturenum].surfacetype == SURFACETYPE_DEFAULT) {
|
||||
#else
|
||||
if (batch->type == VTXBATCHTYPE_XLU && g_Textures[texturenum].surfacetype == SURFACETYPE_DEFAULT) {
|
||||
#endif
|
||||
hit = false;
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
lowestsqdist = sqdist;
|
||||
|
||||
hitthing->unk00.x = spb0.x;
|
||||
hitthing->unk00.y = spb0.y;
|
||||
hitthing->unk00.z = spb0.z;
|
||||
hitthing->pos.x = spb0.x;
|
||||
hitthing->pos.y = spb0.y;
|
||||
hitthing->pos.z = spb0.z;
|
||||
hitthing->unk0c.x = spa4.x;
|
||||
hitthing->unk0c.y = spa4.y;
|
||||
hitthing->unk0c.z = spa4.z;
|
||||
@@ -4469,20 +4593,24 @@ bool bgTestHitInRoom(struct coord *frompos, struct coord *topos, s32 roomnum, st
|
||||
numbatches = g_Rooms[roomnum].numvtxbatches;
|
||||
|
||||
for (i = 0; i < numbatches; batch++, i++) {
|
||||
j = bg0f1612e4(&batch->bbmin, &batch->bbmax, &from, &dist, &sp94, &hitthing->unk00);
|
||||
#ifndef PLATFORM_N64
|
||||
if (g_BgHitXluDisabled && batch->type == VTXBATCHTYPE_XLU)
|
||||
continue;
|
||||
#endif
|
||||
j = bg0f1612e4(&batch->bbmin, &batch->bbmax, &from, &dist, &sp94, &hitthing->pos);
|
||||
|
||||
if (j == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (j == 1) {
|
||||
f0 = from.x - hitthing->unk00.x;
|
||||
f0 = from.x - hitthing->pos.x;
|
||||
f20 = f0 * f0;
|
||||
|
||||
f0 = from.y - hitthing->unk00.y;
|
||||
f0 = from.y - hitthing->pos.y;
|
||||
f20 += f0 * f0;
|
||||
|
||||
f0 = from.z - hitthing->unk00.z;
|
||||
f0 = from.z - hitthing->pos.z;
|
||||
f20 += f0 * f0;
|
||||
} else {
|
||||
f20 = -1.0f;
|
||||
@@ -4512,13 +4640,13 @@ bool bgTestHitInRoom(struct coord *frompos, struct coord *topos, s32 roomnum, st
|
||||
|
||||
for (j = 0; j < ARRAYCOUNT(var800a6538); j++) {
|
||||
if (bgTestHitInVtxBatch(&from, &to, &dist, &g_Rooms[roomnum].vtxbatches[var800a6538[j].vtxbatchindex], roomnum, hitthing)) {
|
||||
f0 = from.x - hitthing->unk00.x;
|
||||
f0 = from.x - hitthing->pos.x;
|
||||
f2 = f0 * f0;
|
||||
|
||||
f0 = from.y - hitthing->unk00.y;
|
||||
f0 = from.y - hitthing->pos.y;
|
||||
f2 += f0 * f0;
|
||||
|
||||
f0 = from.z - hitthing->unk00.z;
|
||||
f0 = from.z - hitthing->pos.z;
|
||||
f2 += f0 * f0;
|
||||
|
||||
if (count == 0) {
|
||||
@@ -4564,31 +4692,31 @@ bool bgTestHitInRoom(struct coord *frompos, struct coord *topos, s32 roomnum, st
|
||||
i++;
|
||||
|
||||
if (i < count) {
|
||||
f0 = from.x - hitthing->unk00.x;
|
||||
f0 = from.x - hitthing->pos.x;
|
||||
spc8 = f0 * f0;
|
||||
|
||||
f0 = from.y - hitthing->unk00.y;
|
||||
f0 = from.y - hitthing->pos.y;
|
||||
spc8 += f0 * f0;
|
||||
|
||||
f0 = from.z - hitthing->unk00.z;
|
||||
f0 = from.z - hitthing->pos.z;
|
||||
spc8 += f0 * f0;
|
||||
|
||||
for (; i < count; i++) {
|
||||
if (var800a6538[i].unk04 <= spc8) {
|
||||
if (bgTestHitInVtxBatch(&from, &to, &dist, &batch[var800a6538[i].vtxbatchindex], roomnum, &sp60)) {
|
||||
f0 = from.f[0] - sp60.unk00.f[0];
|
||||
f0 = from.f[0] - sp60.pos.f[0];
|
||||
f20 = f0 * f0;
|
||||
|
||||
f0 = from.f[1] - sp60.unk00.f[1];
|
||||
f0 = from.f[1] - sp60.pos.f[1];
|
||||
f20 += f0 * f0;
|
||||
|
||||
f0 = from.f[2] - sp60.unk00.f[2];
|
||||
f0 = from.f[2] - sp60.pos.f[2];
|
||||
f20 += f0 * f0;
|
||||
|
||||
if (f20 < spc8) {
|
||||
hitthing->unk00.x = sp60.unk00.x;
|
||||
hitthing->unk00.y = sp60.unk00.y;
|
||||
hitthing->unk00.z = sp60.unk00.z;
|
||||
hitthing->pos.x = sp60.pos.x;
|
||||
hitthing->pos.y = sp60.pos.y;
|
||||
hitthing->pos.z = sp60.pos.z;
|
||||
hitthing->unk0c.x = sp60.unk0c.x;
|
||||
hitthing->unk0c.y = sp60.unk0c.y;
|
||||
hitthing->unk0c.z = sp60.unk0c.z;
|
||||
@@ -4619,7 +4747,7 @@ bool bgRoomIsLoaded(s32 room)
|
||||
return g_Rooms[room].loaded240;
|
||||
}
|
||||
|
||||
bool bgRoomContainsCoord(struct coord *pos, s16 roomnum)
|
||||
bool bgRoomContainsCoord(struct coord *pos, RoomNum roomnum)
|
||||
{
|
||||
struct coord copy;
|
||||
copy.x = pos->x;
|
||||
@@ -4646,7 +4774,7 @@ bool bgRoomContainsCoord(struct coord *pos, s16 roomnum)
|
||||
* - The normal points towards the front of the portal.
|
||||
* - The room on the front side is roomnum2.
|
||||
*/
|
||||
bool bgTestPosInRoomCheap(struct coord *pos, s16 roomnum)
|
||||
bool bgTestPosInRoomCheap(struct coord *pos, RoomNum roomnum)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
@@ -4672,7 +4800,7 @@ bool bgTestPosInRoomCheap(struct coord *pos, s16 roomnum)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bgTestPosInRoomExpensive(struct coord *pos, s16 roomnum)
|
||||
bool bgTestPosInRoomExpensive(struct coord *pos, RoomNum roomnum)
|
||||
{
|
||||
s32 t5;
|
||||
struct coord *next;
|
||||
@@ -4780,7 +4908,7 @@ bool bgTestPosInRoomExpensive(struct coord *pos, s16 roomnum)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bgTestPosInRoom(struct coord *pos, s16 roomnum)
|
||||
bool bgTestPosInRoom(struct coord *pos, RoomNum roomnum)
|
||||
{
|
||||
if (g_Rooms[roomnum].flags & ROOMFLAG_COMPLICATEDPORTALS) {
|
||||
return bgTestPosInRoomExpensive(pos, roomnum);
|
||||
@@ -4809,7 +4937,7 @@ bool bgTestPosInRoom(struct coord *pos, s16 roomnum)
|
||||
* to pos and writes the room number to the bestroom pointer. The bestroom
|
||||
* pointer is a pointer to a single s16 rather than an array.
|
||||
*/
|
||||
void bgFindRoomsByPos(struct coord *posarg, s16 *inrooms, s16 *aboverooms, s32 max, s16 *bestroom)
|
||||
void bgFindRoomsByPos(struct coord *posarg, RoomNum *inrooms, RoomNum *aboverooms, s32 max, RoomNum *bestroom)
|
||||
{
|
||||
s32 inlen = 0;
|
||||
s32 abovelen = 0;
|
||||
@@ -5332,7 +5460,7 @@ void bgTickPortalsXray(void)
|
||||
bgChooseRoomsToLoad();
|
||||
}
|
||||
|
||||
void bgAddToSnake(s16 fromroomnum, s16 roomnum, s16 depth, struct screenbox *box)
|
||||
void bgAddToSnake(RoomNum fromroomnum, RoomNum roomnum, s16 depth, struct screenbox *box)
|
||||
{
|
||||
struct bgsnakeitem *item;
|
||||
s32 i;
|
||||
@@ -5419,10 +5547,10 @@ void bgConsumeSnakeItem(struct bgsnakeitem *item)
|
||||
s32 i;
|
||||
s16 portalnum;
|
||||
s16 prevvalidcount;
|
||||
s16 prevfoundroom;
|
||||
s16 newfoundroom;
|
||||
RoomNum prevfoundroom;
|
||||
RoomNum newfoundroom;
|
||||
s16 side;
|
||||
s16 tmp;
|
||||
RoomNum tmp;
|
||||
bool pass;
|
||||
struct portalmetric *metric;
|
||||
struct screenbox prevbox;
|
||||
@@ -5832,7 +5960,7 @@ Gfx *bgRenderSceneAndLoadCandidate(Gfx *gdl)
|
||||
return gdl;
|
||||
}
|
||||
|
||||
s32 bgGetForceOnscreenRooms(s16 *rooms, s32 len)
|
||||
s32 bgGetForceOnscreenRooms(RoomNum *rooms, s32 len)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
@@ -5845,7 +5973,7 @@ s32 bgGetForceOnscreenRooms(s16 *rooms, s32 len)
|
||||
return i;
|
||||
}
|
||||
|
||||
s32 bgRoomGetNeighbours(s32 roomnum, s16 *dstrooms, s32 len)
|
||||
s32 bgRoomGetNeighbours(s32 roomnum, RoomNum *dstrooms, s32 len)
|
||||
{
|
||||
s32 count = 0;
|
||||
s32 i;
|
||||
@@ -5991,7 +6119,7 @@ bool bgPortalExists(s32 portalnum)
|
||||
|
||||
void bgPortalSwapRooms(s32 portal)
|
||||
{
|
||||
s16 tmp = g_BgPortals[portal].roomnum1;
|
||||
RoomNum tmp = g_BgPortals[portal].roomnum1;
|
||||
g_BgPortals[portal].roomnum1 = g_BgPortals[portal].roomnum2;
|
||||
g_BgPortals[portal].roomnum2 = tmp;
|
||||
}
|
||||
@@ -6210,10 +6338,10 @@ void bgCalculatePortalBbox(s32 portalnum, struct coord *bbmin, struct coord *bbm
|
||||
}
|
||||
}
|
||||
|
||||
void bgFindEnteredRooms(struct coord *bbmin, struct coord *bbmax, s16 *rooms, s32 maxlen, bool arg4)
|
||||
void bgFindEnteredRooms(struct coord *bbmin, struct coord *bbmax, RoomNum *rooms, s32 maxlen, bool arg4)
|
||||
{
|
||||
s16 room;
|
||||
s16 otherroom;
|
||||
RoomNum room;
|
||||
RoomNum otherroom;
|
||||
s32 portalnum;
|
||||
struct coord propbbmin;
|
||||
struct coord propbbmax;
|
||||
@@ -6287,3 +6415,32 @@ void bgFindEnteredRooms(struct coord *bbmin, struct coord *bbmax, s16 *rooms, s3
|
||||
end:
|
||||
rooms[len] = -1;
|
||||
}
|
||||
|
||||
#ifndef PLATFORM_N64
|
||||
|
||||
void bgCalculateGlaresForVisibleRooms(void)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
g_NumRoomsWithGlares = 0;
|
||||
|
||||
// disable line-of-sight hits for transparent background
|
||||
// surfaces before testing for light obstructions
|
||||
g_BgHitXluDisabled = true;
|
||||
|
||||
if (!g_Vars.mplayerisrunning) {
|
||||
for (i = 1; i < g_Vars.roomcount; i++) {
|
||||
if (g_Rooms[i].flags & ROOMFLAG_ONSCREEN) {
|
||||
artifactsCalculateGlaresForRoom(i);
|
||||
if (g_NumRoomsWithGlares < 100) {
|
||||
g_GlareRooms[g_NumRoomsWithGlares++] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// re-enable background hits for transparent surfaces
|
||||
g_BgHitXluDisabled = false;
|
||||
}
|
||||
|
||||
#endif
|
||||
+10
-10
@@ -299,7 +299,7 @@ struct model *bodyAllocateModel(s32 bodynum, s32 headnum, u32 spawnflags)
|
||||
if (spawnflags & SPAWNFLAG_FORCESUNGLASSES) {
|
||||
sunglasses = true;
|
||||
} else if (spawnflags & SPAWNFLAG_MAYBESUNGLASSES) {
|
||||
sunglasses = random() % 2 == 0;
|
||||
sunglasses = rngRandom() % 2 == 0;
|
||||
}
|
||||
|
||||
if (spawnflags & SPAWNFLAG_FIXEDHEIGHT) {
|
||||
@@ -325,7 +325,7 @@ s32 bodyChooseHead(s32 bodynum)
|
||||
g_ActiveMaleHeadsIndex = 0;
|
||||
}
|
||||
} else if (bodynum == BODY_FEM_GUARD) {
|
||||
head = g_FemGuardHeads[random() % 3];
|
||||
head = g_FemGuardHeads[rngRandom() % 3];
|
||||
} else {
|
||||
head = g_ActiveFemaleHeads[g_ActiveFemaleHeadsIndex++];
|
||||
|
||||
@@ -346,7 +346,7 @@ s32 bodyChooseHead(s32 bodynum)
|
||||
void bodyAllocateChr(s32 stagenum, struct packedchr *packed, s32 cmdindex)
|
||||
{
|
||||
struct pad pad;
|
||||
s16 rooms[2];
|
||||
RoomNum rooms[2];
|
||||
struct chrdata *chr;
|
||||
struct modeldef *headmodeldef;
|
||||
struct model *model;
|
||||
@@ -493,10 +493,10 @@ void bodyAllocateChr(s32 stagenum, struct packedchr *packed, s32 cmdindex)
|
||||
if (packed->tude != 4) {
|
||||
chr->tude = packed->tude;
|
||||
} else {
|
||||
chr->tude = random() % 4;
|
||||
chr->tude = rngRandom() % 4;
|
||||
}
|
||||
|
||||
chr->voicebox = random() % 3;
|
||||
chr->voicebox = rngRandom() % 3;
|
||||
|
||||
if (!g_HeadsAndBodies[chr->bodynum].ismale) {
|
||||
chr->voicebox = VOICEBOX_FEMALE;
|
||||
@@ -519,7 +519,7 @@ void bodyAllocateChr(s32 stagenum, struct packedchr *packed, s32 cmdindex)
|
||||
chr->flags |= CHRFLAG0_AIVSAI;
|
||||
}
|
||||
|
||||
if (random() % 5 == 0) {
|
||||
if (rngRandom() % 5 == 0) {
|
||||
// Make chr punch slower
|
||||
chr->flags2 |= CHRFLAG1_ADJUSTPUNCHSPEED;
|
||||
}
|
||||
@@ -531,9 +531,9 @@ void bodyAllocateChr(s32 stagenum, struct packedchr *packed, s32 cmdindex)
|
||||
}
|
||||
}
|
||||
|
||||
struct prop *bodyAllocateEyespy(struct pad *pad, s16 room)
|
||||
struct prop *bodyAllocateEyespy(struct pad *pad, RoomNum room)
|
||||
{
|
||||
s16 rooms[2];
|
||||
RoomNum rooms[2];
|
||||
struct prop *prop;
|
||||
struct chrdata *chr;
|
||||
struct model *model;
|
||||
@@ -596,8 +596,8 @@ struct prop *bodyAllocateEyespy(struct pad *pad, s16 room)
|
||||
chr->team = 0;
|
||||
chr->squadron = 0;
|
||||
chr->maxdamage = 2;
|
||||
chr->tude = random() & 3;
|
||||
chr->voicebox = random() % 3;
|
||||
chr->tude = rngRandom() & 3;
|
||||
chr->voicebox = rngRandom() % 3;
|
||||
chr->naturalanim = 0;
|
||||
chr->myspecial = 0;
|
||||
chr->yvisang = 0;
|
||||
|
||||
@@ -26,7 +26,7 @@ void bodiesReset(s32 stagenum)
|
||||
g_HeadsAndBodies[i].modeldef = NULL;
|
||||
}
|
||||
|
||||
var80062c80 = random() % g_NumBondBodies;
|
||||
var80062c80 = rngRandom() % g_NumBondBodies;
|
||||
var80062b14 = 0;
|
||||
var80062b18 = 0;
|
||||
|
||||
@@ -67,7 +67,7 @@ void bodiesReset(s32 stagenum)
|
||||
for (i = 0; i < g_NumActiveHeadsPerGender; i++) {
|
||||
do {
|
||||
done = true;
|
||||
g_ActiveMaleHeads[i] = headsavailablelist[random() % headsavailablelen];
|
||||
g_ActiveMaleHeads[i] = headsavailablelist[rngRandom() % headsavailablelen];
|
||||
|
||||
if (headsavailablelen > g_NumActiveHeadsPerGender) {
|
||||
for (j = 0; j < i; j++) {
|
||||
@@ -97,7 +97,7 @@ void bodiesReset(s32 stagenum)
|
||||
for (i = 0; i < g_NumActiveHeadsPerGender; i++) {
|
||||
do {
|
||||
done = true;
|
||||
g_ActiveFemaleHeads[i] = headsavailablelist[random() % headsavailablelen];
|
||||
g_ActiveFemaleHeads[i] = headsavailablelist[rngRandom() % headsavailablelen];
|
||||
|
||||
if (headsavailablelen > g_NumActiveHeadsPerGender) {
|
||||
for (j = 0; j < i; j++) {
|
||||
|
||||
Loaded 100 of 364 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user