Compare commits
813
Commits
3c908dc1ed
...
we-ball
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5184064a26 | ||
|
|
6a43363539 | ||
|
|
b9195fbbad | ||
|
|
f715ad2176 | ||
|
|
ca02ee0352 | ||
|
|
fbaa54145e | ||
|
|
28754ffbf2 | ||
|
|
470c0eba7a | ||
|
|
7098dcec43 | ||
|
|
07137f57af | ||
|
|
8b7491a3d3 | ||
|
|
8cfa8ddfeb | ||
|
|
ef284a15a1 | ||
|
|
3723921573 | ||
|
|
195399635e | ||
|
|
46e2a924d3 | ||
|
|
b693ea4102 | ||
|
|
a73f55beb0 | ||
|
|
0bd2491ab7 | ||
|
|
860c797c9c | ||
|
|
38c5080f9f | ||
|
|
fae191d8fe | ||
|
|
d83a953e2d | ||
|
|
2dcf0d0f0d | ||
|
|
6dee37d8c1 | ||
|
|
3bad03afb3 | ||
|
|
6a6d8448f7 | ||
|
|
189babd2cf | ||
|
|
988a0f2294 | ||
|
|
85bf455731 | ||
|
|
589e4224f3 | ||
|
|
bd7fa154b4 | ||
|
|
a292f1992b | ||
|
|
88ddf429b7 | ||
|
|
7a1f6662df | ||
|
|
afc68bccc6 | ||
|
|
b4cdc4a64f | ||
|
|
3ad5afb81c | ||
|
|
bed3f20118 | ||
|
|
de67315178 | ||
|
|
8d5c0c7cad | ||
|
|
a8271e01bd | ||
|
|
900b3f8558 | ||
|
|
fdc4e056f9 | ||
|
|
7b98e40ccf | ||
|
|
01d89cf22c | ||
|
|
503a3c799a | ||
|
|
0b21388844 | ||
|
|
117bdf0c00 | ||
|
|
172dc5d37b | ||
|
|
874c6258ab | ||
|
|
a2d0a12c1a | ||
|
|
38b24e1c3d | ||
|
|
5ecbbe296b | ||
|
|
9d7a769d8f | ||
|
|
9b75e5ed83 | ||
|
|
a9ab8dc1d8 | ||
|
|
a34831aa02 | ||
|
|
84ebaa0751 | ||
|
|
55352805ee | ||
|
|
c0a3f2e16a | ||
|
|
28ff331a28 | ||
|
|
f9a53ec719 | ||
|
|
6a403e6caf | ||
|
|
a6f449bb93 | ||
|
|
0614bfc446 | ||
|
|
bb020c36c1 | ||
|
|
f17b0bfcfb | ||
|
|
2a85c9503f | ||
|
|
182428d6d6 | ||
|
|
8181a28557 | ||
|
|
88aed11d98 | ||
|
|
67010592b8 | ||
|
|
dd22d6424a | ||
|
|
e53775b97f | ||
|
|
d326f6c1ac | ||
|
|
556111c00a | ||
|
|
ebc7110e63 | ||
|
|
af49d1c8bb | ||
|
|
c71dbb1dfc | ||
|
|
432aa3c4fa | ||
|
|
5c93503710 | ||
|
|
0d58050def | ||
|
|
27bb78209a | ||
|
|
3201f5a25b | ||
|
|
d3df8a96d3 | ||
|
|
7ec5266b1f | ||
|
|
e74ff7c805 | ||
|
|
1d5f3c2959 | ||
|
|
469034b223 | ||
|
|
f3a394de21 | ||
|
|
cb23d4c11b | ||
|
|
bbf5b706f0 | ||
|
|
1e890f6d86 | ||
|
|
2fe543a603 | ||
|
|
24631990bd | ||
|
|
0c8c0d24ba | ||
|
|
454b8a91ba | ||
|
|
a74f1568e9 | ||
|
|
3f35d56be4 | ||
|
|
722fe2ccfb | ||
|
|
d85737cc08 | ||
|
|
ce29435831 | ||
|
|
d3715eece8 | ||
|
|
335d79f2c4 | ||
|
|
a9e33660cb | ||
|
|
8faf881399 | ||
|
|
c4969a36cc | ||
|
|
26fcaf6e75 | ||
|
|
a162002af2 | ||
|
|
9810fd51ab | ||
|
|
857c6b3d47 | ||
|
|
e1498f538d | ||
|
|
aa246eff94 | ||
|
|
5be21a21d5 | ||
|
|
8131bcd4d4 | ||
|
|
4ba11e3363 | ||
|
|
acf2be3f66 | ||
|
|
f5df0195e2 | ||
|
|
d26995b48d | ||
|
|
617f8120ae | ||
|
|
06c517c9aa | ||
|
|
593ed6408c | ||
|
|
fb7d3ed122 | ||
|
|
19b88ec858 | ||
|
|
160e65be7f | ||
|
|
079b0d2cf6 | ||
|
|
78f1310f41 | ||
|
|
eb1974c113 | ||
|
|
551409a023 | ||
|
|
a11e14daac | ||
|
|
7441e15e76 | ||
|
|
46506228a6 | ||
|
|
17c49c74cf | ||
|
|
3f8024d4db | ||
|
|
8675e44d28 | ||
|
|
1301d9a718 | ||
|
|
da3db50ca8 | ||
|
|
2ca6780305 | ||
|
|
be68fe5a35 | ||
|
|
dc41c0e302 | ||
|
|
51388c90d5 | ||
|
|
f8c9d33df2 | ||
|
|
ed0420fdce | ||
|
|
47a6f396fa | ||
|
|
9edb2aa0c1 | ||
|
|
003b647d83 | ||
|
|
2849ff8844 | ||
|
|
9f3089742a | ||
|
|
b286a9bbcd | ||
|
|
6204e745ba | ||
|
|
bbe0e48d23 | ||
|
|
79054080c0 | ||
|
|
81024c4c09 | ||
|
|
9068d96130 | ||
|
|
6f47543720 | ||
|
|
5a08384ae1 | ||
|
|
45d8fda0e4 | ||
|
|
a9e664492f | ||
|
|
3c8b6cb2cc | ||
|
|
2b3abbe13b | ||
|
|
241a52b94a | ||
|
|
82c300b077 | ||
|
|
0f8b629e20 | ||
|
|
36f6ac65f2 | ||
|
|
a25871a849 | ||
|
|
57766a9104 | ||
|
|
3770ae1645 | ||
|
|
d73edb403f | ||
|
|
b14196ff0d | ||
|
|
88903fee94 | ||
|
|
1e8311fc04 | ||
|
|
2b78370cb8 | ||
|
|
8f78bba9e9 | ||
|
|
41a4be678e | ||
|
|
8b2b4b7c3d | ||
|
|
1f3a29f89d | ||
|
|
c4c93097cd | ||
|
|
eedb7769e6 | ||
|
|
98db62a4bc | ||
|
|
df48c8e500 | ||
|
|
db9cc0f4c6 | ||
|
|
a79ee429b4 | ||
|
|
6acfca6d48 | ||
|
|
1cd6f4cb72 | ||
|
|
3271e8c7d6 | ||
|
|
0bcde064af | ||
|
|
957980b3c5 | ||
|
|
03eb328d81 | ||
|
|
e1716a741f | ||
|
|
e24707c847 | ||
|
|
7c4b8c307f | ||
|
|
109318aeaf | ||
|
|
1f2657cea0 | ||
|
|
382c435bac | ||
|
|
130fe4ca5d | ||
|
|
31ba3fe127 | ||
|
|
f68b31158f | ||
|
|
653ca9a72d | ||
|
|
ba7857f4df | ||
|
|
23e617ea21 | ||
|
|
cdf5a5229c | ||
|
|
f841a35a53 | ||
|
|
efd31237be | ||
|
|
6502822583 | ||
|
|
a9e6f2b2a5 | ||
|
|
510a94b42c | ||
|
|
d805be47ce | ||
|
|
f9ea8e380a | ||
|
|
5cb05beb30 | ||
|
|
677768e6ab | ||
|
|
ed6c951783 | ||
|
|
54254348b8 | ||
|
|
782fd07a8d | ||
|
|
a8fd55cb38 | ||
|
|
d7f515575a | ||
|
|
bafbf2ec2f | ||
|
|
7415944e0a | ||
|
|
1ff990ff44 | ||
|
|
73e73d8772 | ||
|
|
6d876bb767 | ||
|
|
2be0fe9f06 | ||
|
|
e1fb082927 | ||
|
|
65ca5ae4c4 | ||
|
|
2cea43dc70 | ||
|
|
44a0700800 | ||
|
|
1613a378f1 | ||
|
|
deed98a27d | ||
|
|
9d0cb8fb46 | ||
|
|
d8fe0f6923 | ||
|
|
581dbc2b3c | ||
|
|
7301d2ad76 | ||
|
|
3232a14d1d | ||
|
|
84c1f88d42 | ||
|
|
3695b10e4b | ||
|
|
6da02b25fa | ||
|
|
3bc544fba1 | ||
|
|
368d370f49 | ||
|
|
bb29c0edef | ||
|
|
6edcf75a0c | ||
|
|
31cc186424 | ||
|
|
0e94c1fa6d | ||
|
|
6d9e2dd3e1 | ||
|
|
4a4adeb3c8 | ||
|
|
ff77f8cfa0 | ||
|
|
36db89c36e | ||
|
|
a9948142ad | ||
|
|
8d05510584 | ||
|
|
d373de7a29 | ||
|
|
1efa9a9f7b | ||
|
|
0fb3ba2f91 | ||
|
|
3b4c5b5153 | ||
|
|
9293aeeec8 | ||
|
|
03ae83b119 | ||
|
|
abd63cc6cf | ||
|
|
2e43aa2c44 | ||
|
|
3d984e13c2 | ||
|
|
010900fe21 | ||
|
|
ffed626447 | ||
|
|
61f69af35a | ||
|
|
bd248ee91c | ||
|
|
194255bffe | ||
|
|
52ee627079 | ||
|
|
bd4200e707 | ||
|
|
73e7d6c7f3 | ||
|
|
a41b0e916b | ||
|
|
19f2a2c616 | ||
|
|
998601f722 | ||
|
|
7c3386cf3e | ||
|
|
d161182997 | ||
|
|
1646dc2dbd | ||
|
|
b640295be2 | ||
|
|
b89ae2391b | ||
|
|
a0fad441d0 | ||
|
|
340084dac3 | ||
|
|
c78135aa09 | ||
|
|
d19f8bbd30 | ||
|
|
4205899f5a | ||
|
|
7dd3940770 | ||
|
|
00d94e3015 | ||
|
|
7bacb3ee2b | ||
|
|
8e49be5ac4 | ||
|
|
3b94598d2c | ||
|
|
bddc9af3b6 | ||
|
|
2451d73a7c | ||
|
|
1dd2efa182 | ||
|
|
acea610773 | ||
|
|
8f2f1fd496 | ||
|
|
39c775872a | ||
|
|
bdb3cbd109 | ||
|
|
ff84ce2b04 | ||
|
|
225f405592 | ||
|
|
715ecffa18 | ||
|
|
e51cdc8992 | ||
|
|
133685ea37 | ||
|
|
6aff98d555 | ||
|
|
acdc524284 | ||
|
|
1ee5ec7b43 | ||
|
|
46a5403511 | ||
|
|
87bfb92576 | ||
|
|
c7a3e5601c | ||
|
|
4009130f6e | ||
|
|
c91243f6e9 | ||
|
|
0e3871ac26 | ||
|
|
55baafec8a | ||
|
|
b5a66993ca | ||
|
|
0b570b5fd6 | ||
|
|
378227c377 | ||
|
|
650645eaff | ||
|
|
62c71f3fe6 | ||
|
|
041ec3d710 | ||
|
|
5f2d871bad | ||
|
|
a30b151e4d | ||
|
|
5a651d2d1f | ||
|
|
4b3826edd9 | ||
|
|
bae1ff3759 | ||
|
|
fd82486431 | ||
|
|
c9cd91cbd8 | ||
|
|
c8abd374fe | ||
|
|
2b9ee8f721 | ||
|
|
d02673e04a | ||
|
|
f0117b8e6e | ||
|
|
bb7c41c754 | ||
|
|
efa583c154 | ||
|
|
d16ea13c14 | ||
|
|
673d8e0a18 | ||
|
|
37cfdde1ee | ||
|
|
0778ffb57a | ||
|
|
42099f7241 | ||
|
|
c52e1d22b7 | ||
|
|
0d7b0aadd1 | ||
|
|
4cd3355ef1 | ||
|
|
98d70b96d1 | ||
|
|
64735bdf43 | ||
|
|
7b87347b77 | ||
|
|
b5b29d7061 | ||
|
|
9ec21f85a0 | ||
|
|
da1a5a3f1b | ||
|
|
0885da8d44 | ||
|
|
8af961c6d3 | ||
|
|
ef5febdde3 | ||
|
|
6d7fbd3926 | ||
|
|
2680d373d8 | ||
|
|
2b2ddb3cf2 | ||
|
|
85ff95296b | ||
|
|
314a2de41a | ||
|
|
26fafab47a | ||
|
|
e56ff20e2d | ||
|
|
55d44f229d | ||
|
|
1c5e50cc4d | ||
|
|
ea898da6c2 | ||
|
|
dbb7e9f53c | ||
|
|
cbb68a399d | ||
|
|
0e794f28b1 | ||
|
|
87d2d9123e | ||
|
|
6823a4ddb5 | ||
|
|
20a7c70081 | ||
|
|
9caa33b3bb | ||
|
|
2d7e61460a | ||
|
|
a4b7fb3f44 | ||
|
|
70056cf4ca | ||
|
|
5f4ab71ade | ||
|
|
f3adb3257b | ||
|
|
438edda7fd | ||
|
|
d5b0441e6f | ||
|
|
9ba0ceb000 | ||
|
|
9474a68995 | ||
|
|
09c35f0aa6 | ||
|
|
a2113442cb | ||
|
|
d91808487f | ||
|
|
933949cc19 | ||
|
|
407620387d | ||
|
|
98947dea26 | ||
|
|
ebff7af9b5 | ||
|
|
b23c4b83ae | ||
|
|
c0cff40628 | ||
|
|
97513e354c | ||
|
|
c277ae7aff | ||
|
|
e1835e6282 | ||
|
|
5ac21db997 | ||
|
|
ca0e9fc3b2 | ||
|
|
66ebcb1608 | ||
|
|
ff92a78dda | ||
|
|
7356286fe0 | ||
|
|
54e8e68f86 | ||
|
|
d21cd7f78b | ||
|
|
1d7516982a | ||
|
|
c77a11442c | ||
|
|
5bd43a4643 | ||
|
|
9b87dfa1a9 | ||
|
|
2e3173ea40 | ||
|
|
68eac7cf83 | ||
|
|
6709505630 | ||
|
|
af6e962a5d | ||
|
|
18e6bdabaa | ||
|
|
9743942eae | ||
|
|
23062137a8 | ||
|
|
46f7fb5ccd | ||
|
|
9c90c49a6b | ||
|
|
4517b63557 | ||
|
|
58c239f4b4 | ||
|
|
cc8845ba3e | ||
|
|
6b69ce2901 | ||
|
|
55300ed21c | ||
|
|
7346dd4339 | ||
|
|
2caf3b92ce | ||
|
|
af2cd72a1f | ||
|
|
3d455ec1f8 | ||
|
|
15982d7735 | ||
|
|
5ae3542bd9 | ||
|
|
b1b02ae24b | ||
|
|
f0964e2c92 | ||
|
|
e9661d2998 | ||
|
|
ea6468f2a9 | ||
|
|
a2b38d3b83 | ||
|
|
d67ef02941 | ||
|
|
549ebe25d8 | ||
|
|
9a98348582 | ||
|
|
c5f5b025a6 | ||
|
|
23eaffa3a7 | ||
|
|
c161809248 | ||
|
|
4bf26dc818 | ||
|
|
5dd22fad6c | ||
|
|
2c3fdf7803 | ||
|
|
e984b9f5d7 | ||
|
|
a3c2e37b17 | ||
|
|
edf1b5a0a3 | ||
|
|
8efdf59ebd | ||
|
|
5c4537b2fa | ||
|
|
71e6079054 | ||
|
|
dd048d9b0d | ||
|
|
93074d653e | ||
|
|
9139c4350a | ||
|
|
38ce768168 | ||
|
|
82b3dc576c | ||
|
|
2167889f48 | ||
|
|
e9b02c2acf | ||
|
|
9ee446431b | ||
|
|
df106e3988 | ||
|
|
d0a057e0ee | ||
|
|
8b49902bf6 | ||
|
|
71c1e56564 | ||
|
|
1b12e67de2 | ||
|
|
291bb4bb81 | ||
|
|
342ddb19f8 | ||
|
|
9c9d2d548e | ||
|
|
d7a0bb4509 | ||
|
|
2b1a3323a8 | ||
|
|
99d030003c | ||
|
|
92a753560b | ||
|
|
af9904c892 | ||
|
|
e5e8c49f6c | ||
|
|
b37e5f45ca | ||
|
|
e1f08b07aa | ||
|
|
073ee8dca9 | ||
|
|
a26e51cf46 | ||
|
|
dfed732825 | ||
|
|
87aa70c6d2 | ||
|
|
aa2979ffe7 | ||
|
|
236e16aa6d | ||
|
|
184bb970e6 | ||
|
|
bd54469891 | ||
|
|
2f5dccc3ef | ||
|
|
592edb90a0 | ||
|
|
3db7e6b1b9 | ||
|
|
13c4df0d85 | ||
|
|
ef25fb09da | ||
|
|
03cf4a9efe | ||
|
|
53dd36efdd | ||
|
|
ad9e841a42 | ||
|
|
14f3f464c7 | ||
|
|
cbe51cc8d0 | ||
|
|
efaa3f6eea | ||
|
|
52cce9a3b0 | ||
|
|
b7b390311e | ||
|
|
c1eeddd14b | ||
|
|
1a7a55dfc3 | ||
|
|
fe5927ea6a | ||
|
|
119c794ad7 | ||
|
|
e2076b2c1c | ||
|
|
5208c5148e | ||
|
|
d80660b097 | ||
|
|
b916d0278b | ||
|
|
d51e13e620 | ||
|
|
40ad4326ef | ||
|
|
b8afc1684a | ||
|
|
8348b31ac8 | ||
|
|
411f2dbcce | ||
|
|
ee89c08160 | ||
|
|
357607a89f | ||
|
|
8d6dc2df44 | ||
|
|
5207582ab3 | ||
|
|
71768e6154 | ||
|
|
ecbe235523 | ||
|
|
afef079d1e | ||
|
|
df17696c69 | ||
|
|
065bf0908f | ||
|
|
5b6755e9cf | ||
|
|
b08482acf1 | ||
|
|
80c9c1d389 | ||
|
|
bb7db57bda | ||
|
|
6a83ac767c | ||
|
|
7e47ef9d74 | ||
|
|
e7ec603526 | ||
|
|
2d8ae09bd8 | ||
|
|
b2affbc0a7 | ||
|
|
d50bc61ada | ||
|
|
ec6b032b45 | ||
|
|
bc72f48496 | ||
|
|
dcf06fbd36 | ||
|
|
96311d72c2 | ||
|
|
07938cccc7 | ||
|
|
097c8c00f9 | ||
|
|
aa5b41fe31 | ||
|
|
0d56859d94 | ||
|
|
1af5f238e4 | ||
|
|
dd697d5650 | ||
|
|
5cf299a1c7 | ||
|
|
67bf825cc9 | ||
|
|
56e1696cd4 | ||
|
|
d955fb6430 | ||
|
|
dd910a31aa | ||
|
|
708c4d0ec3 | ||
|
|
ad13d6c6a1 | ||
|
|
1c32158142 | ||
|
|
5cea284906 | ||
|
|
13dba8b604 | ||
|
|
22398ddcef | ||
|
|
94e2cc6210 | ||
|
|
da3513f63d | ||
|
|
2c83e4ba9f | ||
|
|
c862071126 | ||
|
|
fed819e9b2 | ||
|
|
c6f4518684 | ||
|
|
053778a502 | ||
|
|
78e1ae885a | ||
|
|
982d28a3e0 | ||
|
|
c2cad858a5 | ||
|
|
794e0574ad | ||
|
|
c190271565 | ||
|
|
ae8a869f64 | ||
|
|
69d64eb8e4 | ||
|
|
b2f2df650a | ||
|
|
6af570fab2 | ||
|
|
9ed902017c | ||
|
|
32b41c98e1 | ||
|
|
6bdb4ae30d | ||
|
|
25dc97e3cc | ||
|
|
cc85983736 | ||
|
|
6e78ee188d | ||
|
|
9b73f1717f | ||
|
|
c7b9a53535 | ||
|
|
2b9be6675c | ||
|
|
fb93453482 | ||
|
|
81b08b2eba | ||
|
|
d1b03c4cb3 | ||
|
|
9544d15a18 | ||
|
|
0392dd0e7f | ||
|
|
9c25fde548 | ||
|
|
2c9d0c6cff | ||
|
|
9897dbe031 | ||
|
|
e78f117cfd | ||
|
|
07afc3813a | ||
|
|
d788de8637 | ||
|
|
d749ac8a91 | ||
|
|
f71c271c97 | ||
|
|
e1d7b7308f | ||
|
|
26a71a823a | ||
|
|
5e39097faa | ||
|
|
0df7845f2c | ||
|
|
af5bf987c8 | ||
|
|
024ace1078 | ||
|
|
8d00fe9d16 | ||
|
|
ab422b14dd | ||
|
|
95c0690216 | ||
|
|
6cb80e9e23 | ||
|
|
83b799caa8 | ||
|
|
a793ac2ff7 | ||
|
|
aec937b04b | ||
|
|
8ee46fd204 | ||
|
|
726233e55f | ||
|
|
7940f4c487 | ||
|
|
b16dbaceec | ||
|
|
f39b2060a8 | ||
|
|
aed202ebf9 | ||
|
|
a495179e5f | ||
|
|
4e1b404820 | ||
|
|
8c74ee31e0 | ||
|
|
77d3c54ebb | ||
|
|
b5de39926b | ||
|
|
3a8dafbb91 | ||
|
|
6b22f547fe | ||
|
|
de78be3e25 | ||
|
|
9f507be7bc | ||
|
|
9aaf271996 | ||
|
|
b01c0d37b0 | ||
|
|
538079880d | ||
|
|
fe0529d021 | ||
|
|
d068f0f2c3 | ||
|
|
f9a64b8d54 | ||
|
|
01cbfaae95 | ||
|
|
f9006a90d5 | ||
|
|
7daeaee6b5 | ||
|
|
03218ce20f | ||
|
|
6f33522c1c | ||
|
|
3697cc3eef | ||
|
|
51a1077fda | ||
|
|
8740c2b165 | ||
|
|
6ed2bdd4c5 | ||
|
|
c32df89490 | ||
|
|
bd5a67676b | ||
|
|
903dab49e3 | ||
|
|
1668c4b0d2 | ||
|
|
2179a27bf5 | ||
|
|
6e7a0cba76 | ||
|
|
69b37b30bc | ||
|
|
ae941a0fdb | ||
|
|
1b741a81e5 | ||
|
|
edf321515b | ||
|
|
c874e6c197 | ||
|
|
9a59c22288 | ||
|
|
750e8840f0 | ||
|
|
cf59989167 | ||
|
|
7c194ab4b4 | ||
|
|
be422d0a1e | ||
|
|
68b63d3007 | ||
|
|
8525138594 | ||
|
|
7b9f8b190e | ||
|
|
67f62daa9f | ||
|
|
0ec701f30b | ||
|
|
c53439066e | ||
|
|
7278bd0c6f | ||
|
|
b842e5821a | ||
|
|
f7d4cce485 | ||
|
|
4f502b707f | ||
|
|
09f182228f | ||
|
|
69ce48a8b9 | ||
|
|
5c2788efe4 | ||
|
|
768323b5b6 | ||
|
|
e203f225e2 | ||
|
|
ab1e2476a0 | ||
|
|
312f32e786 | ||
|
|
397466f0a8 | ||
|
|
1a773cb8ba | ||
|
|
8441c325fa | ||
|
|
cd4a1afbba | ||
|
|
ae75a932bf | ||
|
|
0fa2beede4 | ||
|
|
8e5d5ca1d7 | ||
|
|
2a68414eec | ||
|
|
3f1c8e28e9 | ||
|
|
348531352e | ||
|
|
6770cc422a | ||
|
|
7c157e22c7 | ||
|
|
0cfc6d0503 | ||
|
|
f7fbd16e57 | ||
|
|
542aeadf0f | ||
|
|
84593867dc | ||
|
|
9f23533069 | ||
|
|
4f8f6a47cb | ||
|
|
7d7a3f30e6 | ||
|
|
d39ed1ea5a | ||
|
|
5c8b314689 | ||
|
|
9953d7d388 | ||
|
|
5adf8a0773 | ||
|
|
26bfb912f1 | ||
|
|
c07d0b32a9 | ||
|
|
562da971e9 | ||
|
|
3eb24da475 | ||
|
|
8977d50992 | ||
|
|
13365dd390 | ||
|
|
aee06f51f0 | ||
|
|
d6c497731f | ||
|
|
f23e26d9da | ||
|
|
ec324e02f2 | ||
|
|
e2ce809762 | ||
|
|
d054cf9e36 | ||
|
|
943e775364 | ||
|
|
b9ec6523d6 | ||
|
|
5206d47b43 | ||
|
|
aaa8622956 | ||
|
|
587d716aae | ||
|
|
5a8710cc76 | ||
|
|
307f3a9dec | ||
|
|
eff5fc3d9a | ||
|
|
5a3004f1d1 | ||
|
|
db589b7d91 | ||
|
|
0a83175b66 | ||
|
|
bc4776f096 | ||
|
|
ab534bb998 | ||
|
|
cf2aacd75b | ||
|
|
9f88374627 | ||
|
|
b7d898b505 | ||
|
|
12c1fb6000 | ||
|
|
1ce1fdff8d | ||
|
|
7c11a7e5bc | ||
|
|
7d46b98310 | ||
|
|
68c4834a62 | ||
|
|
c9608ad7a7 | ||
|
|
6ea4132ff9 | ||
|
|
be79356f42 | ||
|
|
d4a2e059d7 | ||
|
|
f3d985ecbc | ||
|
|
bcba693afb | ||
|
|
b4fb7bf99f | ||
|
|
3ef6205ea3 | ||
|
|
3feb43fdad | ||
|
|
d74226dab1 | ||
|
|
5c3db5d991 | ||
|
|
bcb8bea0fe | ||
|
|
0c0650a2c3 | ||
|
|
2c0fd84c72 | ||
|
|
81cd03e0c3 | ||
|
|
349e6e7c94 | ||
|
|
c4c43b23ad | ||
|
|
7622f81309 | ||
|
|
c31bcf7f6a | ||
|
|
fef31b9102 | ||
|
|
20cf016b06 | ||
|
|
67604eca8d | ||
|
|
46f820690d | ||
|
|
e36256abe3 | ||
|
|
b00ca3d48c | ||
|
|
cf2e6bf382 | ||
|
|
fc52afdb00 | ||
|
|
85434b4edb | ||
|
|
f3a6c8df71 | ||
|
|
6e5c5f61db | ||
|
|
12c31ba9d1 | ||
|
|
ea50d893d4 | ||
|
|
bacd0e6e39 | ||
|
|
c0cd4ead04 | ||
|
|
4b04fc65ad | ||
|
|
83243ba32f | ||
|
|
a734ecaa10 | ||
|
|
22e2f703db | ||
|
|
28174b8dc8 | ||
|
|
061352bcff | ||
|
|
2f40724258 | ||
|
|
a45a2a5bd7 | ||
|
|
08221af3f8 | ||
|
|
f799690d3c | ||
|
|
07ab2b4b02 | ||
|
|
cafeda4368 | ||
|
|
517b39649c | ||
|
|
067b0d2e9f | ||
|
|
9b98181d28 | ||
|
|
46a94ecacd | ||
|
|
964a9f64f2 | ||
|
|
b4d94c2cbe | ||
|
|
268e9ffefd | ||
|
|
c8f8170ec2 | ||
|
|
8b20f0bf31 | ||
|
|
fe9af039fc | ||
|
|
6fad5bef4a | ||
|
|
16a0403fd4 | ||
|
|
e32d1f0900 | ||
|
|
3f37b7cdb5 | ||
|
|
059ccf41b6 | ||
|
|
1af2b8f47b | ||
|
|
87f18d0e13 | ||
|
|
8de12da1ec | ||
|
|
71080682cc | ||
|
|
f915a4208b | ||
|
|
14c41d33a7 | ||
|
|
3e61d6f84d | ||
|
|
4541d5219b | ||
|
|
3ce1566a2e | ||
|
|
368729f0f3 | ||
|
|
127392a1ae | ||
|
|
af1329710d | ||
|
|
30232d1275 | ||
|
|
31fa4948d5 | ||
|
|
6c11096fd2 | ||
|
|
7a90d2d38f | ||
|
|
a543bc7c00 | ||
|
|
8af2f044ed | ||
|
|
947f21cac7 | ||
|
|
479aad2f06 | ||
|
|
329925ea54 | ||
|
|
c8a3ebfcec | ||
|
|
1dfde00317 | ||
|
|
1bf6fe6eaf | ||
|
|
995bbe1acd | ||
|
|
1fb9485ee8 | ||
|
|
f9385ed233 | ||
|
|
94ad64675d | ||
|
|
b1be1deb79 | ||
|
|
1b4c270ccb | ||
|
|
9f5894cdcb | ||
|
|
4688358d6a | ||
|
|
c8390bdb12 | ||
|
|
3a753b1299 | ||
|
|
84f2735246 | ||
|
|
fbfcbe9578 | ||
|
|
1411c2e96b | ||
|
|
6f42a6e195 | ||
|
|
1e57183ca1 | ||
|
|
d79e12ffaa | ||
|
|
bcb1616201 | ||
|
|
38127d9260 | ||
|
|
d48d927202 | ||
|
|
150a410f00 | ||
|
|
fbbd2176fd | ||
|
|
3e19771d8f | ||
|
|
3d4317260f | ||
|
|
6d6a0e4886 | ||
|
|
91b93b5b1e | ||
|
|
c3310a036f | ||
|
|
69acbd017c | ||
|
|
81ed15b171 | ||
|
|
d1ab8b0cc8 | ||
|
|
cbdc271a5e |
@@ -0,0 +1,49 @@
|
||||
name: Setup devkitPro
|
||||
description: Install devkitPro with GameCube/Wii packages on an Ubuntu runner
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install apt dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
cmake \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-polib \
|
||||
python3-pil \
|
||||
python3-dotenv \
|
||||
python3-pyqt5 \
|
||||
python3-opengl \
|
||||
xorriso
|
||||
- name: Install devkitPro pacman
|
||||
shell: bash
|
||||
run: |
|
||||
sudo ln -sf /proc/self/mounts /etc/mtab
|
||||
wget https://apt.devkitpro.org/install-devkitpro-pacman \
|
||||
-O /tmp/install-devkitpro-pacman
|
||||
echo "=== installer script contents ==="
|
||||
cat /tmp/install-devkitpro-pacman
|
||||
echo "================================="
|
||||
chmod +x /tmp/install-devkitpro-pacman
|
||||
sudo /tmp/install-devkitpro-pacman
|
||||
echo "DEVKITPRO=/opt/devkitpro" >> $GITHUB_ENV
|
||||
echo "DEVKITPPC=/opt/devkitpro/devkitPPC" >> $GITHUB_ENV
|
||||
echo "/opt/devkitpro/tools/bin" >> $GITHUB_PATH
|
||||
echo "/opt/devkitpro/devkitPPC/bin" >> $GITHUB_PATH
|
||||
- name: Install devkitPro GameCube/Wii packages
|
||||
shell: bash
|
||||
run: |
|
||||
sudo dkp-pacman -S --needed --noconfirm \
|
||||
gamecube-dev \
|
||||
gamecube-sdl2 \
|
||||
ppc-liblzma \
|
||||
ppc-libzip \
|
||||
libogc2 \
|
||||
gamecube-tools \
|
||||
ppc-libmad \
|
||||
ppc-zlib-ng \
|
||||
ppc-bzip2 \
|
||||
ppc-zstd
|
||||
@@ -0,0 +1,20 @@
|
||||
name: Setup pspdev
|
||||
description: Install the pspdev PSP toolchain on an Ubuntu runner
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y cmake python3 python3-pip python3-dotenv
|
||||
- name: Install pspdev toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
wget -q \
|
||||
https://github.com/pspdev/pspdev/releases/latest/download/pspdev-ubuntu-latest-x86_64.tar.gz \
|
||||
-O /tmp/pspdev.tar.gz
|
||||
sudo tar -xzf /tmp/pspdev.tar.gz -C /usr/local
|
||||
echo "PSPDEV=/usr/local/pspdev" >> $GITHUB_ENV
|
||||
echo "/usr/local/pspdev/bin" >> $GITHUB_PATH
|
||||
@@ -0,0 +1,247 @@
|
||||
name: Build Dusk
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-polib \
|
||||
python3-pil \
|
||||
libsdl2-dev \
|
||||
libgl1-mesa-dev \
|
||||
libzip-dev \
|
||||
python3-dotenv \
|
||||
python3-pyqt5 \
|
||||
python3-opengl \
|
||||
xz-utils \
|
||||
liblzma-dev \
|
||||
libbz2-dev \
|
||||
zlib1g-dev \
|
||||
git \
|
||||
libssl-dev
|
||||
- name: Build Linux
|
||||
run: ./scripts/build-linux.sh
|
||||
- name: Upload Linux binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dusk-linux
|
||||
path: build-linux/Dusk
|
||||
if-no-files-found: error
|
||||
|
||||
build-psp:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup pspdev
|
||||
uses: ./.github/actions/setup-pspdev
|
||||
- name: Build PSP
|
||||
run: ./scripts/build-psp.sh
|
||||
- name: Move EBOOT.PBP to Dusk subfolder
|
||||
run: |
|
||||
mkdir -p ./git-artifcats/Dusk/PSP/GAME/Dusk
|
||||
cp build-psp/EBOOT.PBP ./git-artifcats/Dusk/PSP/GAME/Dusk/EBOOT.PBP
|
||||
- name: Upload PSP binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dusk-psp
|
||||
path: ./git-artifcats/Dusk
|
||||
if-no-files-found: error
|
||||
|
||||
build-knulli:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: debian:trixie
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
run: apt-get update && apt-get install -y nodejs
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dpkg --add-architecture arm64
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
crossbuild-essential-arm64 \
|
||||
ca-certificates \
|
||||
pkg-config \
|
||||
cmake \
|
||||
make \
|
||||
ninja-build \
|
||||
git \
|
||||
file \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-polib \
|
||||
python3-pil \
|
||||
python3-dotenv \
|
||||
python3-pyqt5 \
|
||||
python3-opengl \
|
||||
liblua5.4-dev:arm64 \
|
||||
xz-utils:arm64 \
|
||||
libbz2-dev:arm64 \
|
||||
zlib1g-dev:arm64 \
|
||||
libzip-dev:arm64 \
|
||||
libssl-dev:arm64 \
|
||||
libsdl2-dev:arm64 \
|
||||
liblzma-dev:arm64 \
|
||||
libopengl0:arm64 \
|
||||
libgl1:arm64 \
|
||||
libegl1:arm64 \
|
||||
libgles2:arm64 \
|
||||
libgl1-mesa-dev:arm64
|
||||
- name: Build Knulli
|
||||
run: ./scripts/build-knulli.sh
|
||||
- name: Move output to Dusk subfolder
|
||||
run: |
|
||||
mkdir -p ./git-artifcats/Dusk
|
||||
cp -r build-knulli/dusk ./git-artifcats/Dusk
|
||||
- name: Upload Knulli binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dusk-knulli
|
||||
path: ./git-artifcats/Dusk
|
||||
if-no-files-found: error
|
||||
|
||||
build-gamecube:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/extremscorner/libogc2:latest
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
run: apt-get update && apt-get install -y nodejs
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install additional dependencies
|
||||
run: |
|
||||
apt-get install -y \
|
||||
python3-pip python3-polib python3-pil \
|
||||
python3-dotenv python3-pyqt5 python3-opengl
|
||||
dkp-pacman -Syu --noconfirm
|
||||
dkp-pacman -S --needed --noconfirm \
|
||||
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
||||
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
||||
- name: Build GameCube
|
||||
run: ./scripts/build-gamecube.sh
|
||||
- name: Copy output files
|
||||
run: |
|
||||
mkdir -p ./git-artifcats/Dusk
|
||||
cp build-gamecube/Dusk.dol ./git-artifcats/Dusk/Dusk.dol
|
||||
cp build-gamecube/dusk.dsk ./git-artifcats/Dusk/dusk.dsk
|
||||
- name: Upload GameCube binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dusk-gamecube
|
||||
path: ./git-artifcats/Dusk
|
||||
if-no-files-found: error
|
||||
|
||||
build-gamecube-iso:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/extremscorner/libogc2:latest
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
run: apt-get update && apt-get install -y nodejs
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install additional dependencies
|
||||
run: |
|
||||
apt-get install -y \
|
||||
python3-pip python3-polib python3-pil \
|
||||
python3-dotenv python3-pyqt5 python3-opengl xorriso
|
||||
dkp-pacman -Syu --noconfirm
|
||||
dkp-pacman -S --needed --noconfirm \
|
||||
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
||||
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
||||
- name: Build GameCube ISO
|
||||
run: ./scripts/build-gamecube-iso.sh
|
||||
- name: Copy output files
|
||||
run: |
|
||||
mkdir -p ./git-artifcats/Dusk
|
||||
cp build-gamecube-iso/Dusk-NTSC-J.iso ./git-artifcats/Dusk/Dusk-NTSC-J.iso
|
||||
cp build-gamecube-iso/Dusk-NTSC-U.iso ./git-artifcats/Dusk/Dusk-NTSC-U.iso
|
||||
cp build-gamecube-iso/Dusk-PAL.iso ./git-artifcats/Dusk/Dusk-PAL.iso
|
||||
- name: Upload GameCube ISO
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dusk-gamecube-iso
|
||||
path: ./git-artifcats/Dusk
|
||||
if-no-files-found: error
|
||||
|
||||
build-wii:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/extremscorner/libogc2:latest
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
run: apt-get update && apt-get install -y nodejs
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install additional dependencies
|
||||
run: |
|
||||
apt-get install -y \
|
||||
python3-pip python3-polib python3-pil \
|
||||
python3-dotenv python3-pyqt5 python3-opengl
|
||||
dkp-pacman -Syu --noconfirm
|
||||
dkp-pacman -S --needed --noconfirm \
|
||||
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
||||
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
||||
- name: Build Wii
|
||||
run: ./scripts/build-wii.sh
|
||||
- name: Copy output files
|
||||
run: |
|
||||
mkdir -p ./git-artifcats/Dusk/apps/Dusk
|
||||
cp build-wii/boot.dol ./git-artifcats/Dusk/apps/Dusk/boot.dol
|
||||
cp build-wii/dusk.dsk ./git-artifcats/Dusk/apps/Dusk/dusk.dsk
|
||||
cp build-wii/meta.xml ./git-artifcats/Dusk/apps/Dusk/meta.xml
|
||||
- name: Upload Wii binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dusk-wii
|
||||
path: ./git-artifcats/Dusk
|
||||
if-no-files-found: error
|
||||
|
||||
build-wii-iso:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/extremscorner/libogc2:latest
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
run: apt-get update && apt-get install -y nodejs
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install additional dependencies
|
||||
run: |
|
||||
apt-get install -y \
|
||||
python3-pip python3-polib python3-pil \
|
||||
python3-dotenv python3-pyqt5 python3-opengl xorriso
|
||||
dkp-pacman -Syu --noconfirm
|
||||
dkp-pacman -S --needed --noconfirm \
|
||||
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
||||
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
||||
- name: Build Wii ISO
|
||||
run: ./scripts/build-wii-iso.sh
|
||||
- name: Copy output files
|
||||
run: |
|
||||
mkdir -p ./git-artifcats/Dusk
|
||||
cp build-wii-iso/Dusk-NTSC-J.iso ./git-artifcats/Dusk/Dusk-NTSC-J.iso
|
||||
cp build-wii-iso/Dusk-NTSC-U.iso ./git-artifcats/Dusk/Dusk-NTSC-U.iso
|
||||
cp build-wii-iso/Dusk-PAL.iso ./git-artifcats/Dusk/Dusk-PAL.iso
|
||||
- name: Upload Wii ISO
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dusk-wii-iso
|
||||
path: ./git-artifcats/Dusk
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Test Dusk
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
run-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-polib \
|
||||
python3-pil \
|
||||
libsdl2-dev \
|
||||
libgl1-mesa-dev \
|
||||
libzip-dev \
|
||||
python3-dotenv \
|
||||
python3-pyqt5 \
|
||||
python3-opengl \
|
||||
xz-utils \
|
||||
liblzma-dev \
|
||||
libbz2-dev \
|
||||
zlib1g-dev \
|
||||
git \
|
||||
libssl-dev
|
||||
- name: Run tests
|
||||
run: ./scripts/test-linux.sh
|
||||
+13
-1
@@ -83,7 +83,6 @@ assets/borrowed
|
||||
.VSCode*
|
||||
|
||||
/vita
|
||||
|
||||
._*
|
||||
|
||||
*~
|
||||
@@ -95,3 +94,16 @@ assets/borrowed
|
||||
# /archive
|
||||
|
||||
__pycache__
|
||||
|
||||
package-lock.json
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
|
||||
.editor
|
||||
.venv
|
||||
|
||||
/build2
|
||||
/build*
|
||||
/assets/test
|
||||
/tools_old
|
||||
/assets/test.png
|
||||
+89
-35
@@ -5,14 +5,19 @@
|
||||
|
||||
# Setup
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
|
||||
cmake_policy(SET CMP0079 NEW)
|
||||
# set(FETCHCONTENT_UPDATES_DISCONNECTED ON)
|
||||
|
||||
if(NOT DEFINED DUSK_TARGET_SYSTEM)
|
||||
set(DUSK_TARGET_SYSTEM "linux")
|
||||
# set(DUSK_TARGET_SYSTEM "psp")
|
||||
endif()
|
||||
option(DUSK_BUILD_TESTS "Enable tests" OFF)
|
||||
|
||||
set(DUSK_GAME_NAME "Dusk" CACHE STRING "Game display name")
|
||||
set(DUSK_GAME_AUTHOR "YourWishes" CACHE STRING "Game author / coder")
|
||||
set(DUSK_GAME_SHORT_DESCRIPTION "Dusk game" CACHE STRING "One-line description")
|
||||
set(DUSK_GAME_LONG_DESCRIPTION "No description yet." CACHE STRING "Full description")
|
||||
|
||||
# Prep cache
|
||||
set(DUSK_CACHE_TARGET "dusk-target")
|
||||
@@ -21,56 +26,105 @@ set(DUSK_CACHE_TARGET "dusk-target")
|
||||
set(DUSK_ROOT_DIR "${CMAKE_SOURCE_DIR}")
|
||||
set(DUSK_BUILD_DIR "${CMAKE_BINARY_DIR}")
|
||||
set(DUSK_SOURCES_DIR "${DUSK_ROOT_DIR}/src")
|
||||
set(DUSK_TEST_DIR "${DUSK_ROOT_DIR}/test")
|
||||
set(DUSK_TEMP_DIR "${DUSK_BUILD_DIR}/temp")
|
||||
set(DUSK_TOOLS_DIR "${DUSK_ROOT_DIR}/tools")
|
||||
# set(DUSK_ASSETS_DIR "${DUSK_ROOT_DIR}/assets")
|
||||
# set(DUSK_ASSETS_BUILD_DIR "${DUSK_BUILD_DIR}/assets")
|
||||
set(DUSK_DATA_DIR "${DUSK_ROOT_DIR}/data")
|
||||
set(DUSK_ASSETS_DIR "${DUSK_ROOT_DIR}/assets")
|
||||
set(DUSK_BUILT_ASSETS_DIR "${DUSK_BUILD_DIR}/built_assets" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
set(DUSK_GENERATED_HEADERS_DIR "${DUSK_BUILD_DIR}/generated")
|
||||
set(DUSK_TARGET_NAME "Dusk" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
set(DUSK_BUILD_BINARY ${DUSK_BUILD_DIR}/Dusk CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
set(DUSK_ASSETS "" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
set(DUSK_LIBRARY_TARGET_NAME "DuskCore" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
set(DUSK_BINARY_TARGET_NAME "Dusk" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
set(DUSK_ASSETS_ZIP "${DUSK_BUILD_DIR}/dusk.dsk" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
|
||||
# Toolchain
|
||||
if(NOT DEFINED DUSK_TARGET_SYSTEM)
|
||||
set(DUSK_TARGET_SYSTEM "linux")
|
||||
endif()
|
||||
|
||||
# Create directories
|
||||
file(MAKE_DIRECTORY ${DUSK_GENERATED_HEADERS_DIR})
|
||||
file(MAKE_DIRECTORY ${DUSK_ASSETS_BUILD_DIR})
|
||||
file(MAKE_DIRECTORY ${DUSK_TEMP_DIR})
|
||||
file(MAKE_DIRECTORY ${DUSK_BUILT_ASSETS_DIR})
|
||||
|
||||
# Compilers
|
||||
if(DUSK_TARGET_SYSTEM STREQUAL "psp")
|
||||
find_package(pspsdk REQUIRED)
|
||||
endif()
|
||||
# Required build packages
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
|
||||
# Init Project
|
||||
project(${DUSK_TARGET_NAME}
|
||||
# Init Project.
|
||||
project(${DUSK_LIBRARY_TARGET_NAME}
|
||||
VERSION 1.0.0
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
# Executable
|
||||
add_executable(${DUSK_TARGET_NAME})
|
||||
# Either, create library and binary separately (used for tests), or make them
|
||||
# one in the same so all code is in the binary only.
|
||||
# Binary Executable
|
||||
add_executable(${DUSK_BINARY_TARGET_NAME} ${DUSK_SOURCES_DIR}/dusk/null.c)
|
||||
|
||||
if(DUSK_BUILD_TESTS)
|
||||
# MainLibrary
|
||||
add_library(${DUSK_LIBRARY_TARGET_NAME} STATIC)
|
||||
|
||||
# Link library to binary
|
||||
target_link_libraries(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
else()
|
||||
set(DUSK_LIBRARY_TARGET_NAME "${DUSK_BINARY_TARGET_NAME}" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED DUSK_VERSION)
|
||||
string(TIMESTAMP DUSK_VERSION "debug-%y%m%d%H%M%S")
|
||||
endif()
|
||||
|
||||
# Definitions
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
DUSK_TARGET_SYSTEM="${DUSK_TARGET_SYSTEM}"
|
||||
DUSK_GAME_NAME="${DUSK_GAME_NAME}"
|
||||
DUSK_GAME_AUTHOR="${DUSK_GAME_AUTHOR}"
|
||||
DUSK_GAME_SHORT_DESCRIPTION="${DUSK_GAME_SHORT_DESCRIPTION}"
|
||||
DUSK_GAME_LONG_DESCRIPTION="${DUSK_GAME_LONG_DESCRIPTION}"
|
||||
DUSK_VERSION="${DUSK_VERSION}"
|
||||
)
|
||||
|
||||
# Toolchains
|
||||
include(cmake/targets/${DUSK_TARGET_SYSTEM}.cmake)
|
||||
|
||||
# Add tools
|
||||
add_subdirectory(tools)
|
||||
|
||||
# Add code
|
||||
add_subdirectory(src)
|
||||
|
||||
# Include generated headers
|
||||
target_include_directories(${DUSK_TARGET_NAME}
|
||||
PUBLIC
|
||||
${DUSK_GENERATED_HEADERS_DIR}
|
||||
# Include generated headers from tools.
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
${DUSK_GENERATED_HEADERS_DIR}
|
||||
)
|
||||
|
||||
# Postbuild, create PBP file for PSP.
|
||||
if(DUSK_TARGET_SYSTEM STREQUAL "psp")
|
||||
create_pbp_file(
|
||||
TARGET "${DUSK_TARGET_NAME}"
|
||||
ICON_PATH NULL
|
||||
BACKGROUND_PATH NULL
|
||||
PREVIEW_PATH NULL
|
||||
TITLE "${DUSK_TARGET_NAME}"
|
||||
VERSION 01.00
|
||||
)
|
||||
# Add main code
|
||||
add_subdirectory(${DUSK_SOURCES_DIR})
|
||||
|
||||
# Include generated headers
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
${DUSK_GENERATED_HEADERS_DIR}
|
||||
)
|
||||
|
||||
# Handle tests
|
||||
if(DUSK_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
# Build assets
|
||||
file(GLOB_RECURSE DUSK_ASSET_FILES CONFIGURE_DEPENDS "${DUSK_ASSETS_DIR}/*")
|
||||
add_custom_command(
|
||||
OUTPUT "${DUSK_ASSETS_ZIP}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DUSK_ASSETS_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -f "${DUSK_ASSETS_ZIP}"
|
||||
COMMAND ${CMAKE_COMMAND} -E tar "cf" "${DUSK_ASSETS_ZIP}" --format=zip -- .
|
||||
WORKING_DIRECTORY "${DUSK_ASSETS_DIR}"
|
||||
DEPENDS ${DUSK_ASSET_FILES}
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(DUSK_ASSETS_BUILT DEPENDS "${DUSK_ASSETS_ZIP}")
|
||||
add_dependencies(${DUSK_LIBRARY_TARGET_NAME} DUSK_ASSETS_BUILT)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Documentation
|
||||
- [Scripting](docs/SCRIPTING.md) — writing gameplay logic in JavaScript.
|
||||
- [UI](docs/UI.md) — building buttons/menus/etc from engine/game C code.
|
||||
|
||||
# Building
|
||||
Each build target has different requirements. You can take a look at the git
|
||||
workflow to see how the builds are done for each target. In addition, for
|
||||
accessing the editor and building the game on your host system, install the
|
||||
following packages, depending on your system;
|
||||
|
||||
Fedora;
|
||||
```
|
||||
sudo dnf install git make gcc cmake python python-polib python3-pillow python3-dotenv python3-numpy python-qt5 python3-pyopengl SDL2-devel zlib-devel libzip-devel bzip2-devel openssl-devel lzma-sdk-devel xz xz-devel lua-devel
|
||||
```
|
||||
|
||||
Ubuntu;
|
||||
```
|
||||
sudo apt-get install git build-essential gcc python python-polib python3-pillow python3-dotenv python3-numpy python3-pyqt5 python3-opengl
|
||||
```
|
||||
|
||||
Arch Linux;
|
||||
```
|
||||
sudo pacman -S git base-devel gcc python python-polib python-pillow python-dotenv python-numpy python-pyqt5 python-opengl
|
||||
```
|
||||
-257
@@ -1,257 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "fixed.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
float_t fx248Fromi32(const int32_t b) {
|
||||
return (float_t)b << FIXED248_FRACTION_BITS;
|
||||
}
|
||||
|
||||
float_t fx248Fromu32(const uint32_t b) {
|
||||
return (float_t)((int32_t)b << FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
float_t fx248Fromf32(const float_t b) {
|
||||
return (float_t)(b * (1 << FIXED248_FRACTION_BITS));
|
||||
}
|
||||
|
||||
float_t fx248Fromu16(const uint16_t b) {
|
||||
return (float_t)((int32_t)b << FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
float_t fx248Fromu8(const uint8_t b) {
|
||||
return (float_t)((int32_t)b << FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t fx248Toi32(const float_t a) {
|
||||
return a >> FIXED248_FRACTION_BITS;
|
||||
}
|
||||
|
||||
uint32_t fx248Tou32(const float_t a) {
|
||||
return (uint32_t)(a >> FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
float_t fx248Tof32(const float_t a) {
|
||||
return (float_t)a / (1 << FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
uint16_t fx248Tou16(const float_t a) {
|
||||
return (uint16_t)(a >> FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
uint8_t fx248Tou8(const float_t a) {
|
||||
return (uint8_t)(a >> FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
float_t fx248Addfx248(const float_t a, const float_t b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
float_t fx248Addi32(const float_t a, const int32_t b) {
|
||||
return fx248Addfx248(a, fx248Fromi32(b));
|
||||
}
|
||||
|
||||
float_t fx248Addu32(const float_t a, const uint32_t b) {
|
||||
return fx248Addfx248(a, fx248Fromu32(b));
|
||||
}
|
||||
|
||||
float_t fx248Addf32(const float_t a, const float_t b) {
|
||||
return fx248Addfx248(a, fx248Fromf32(b));
|
||||
}
|
||||
|
||||
|
||||
|
||||
float_t fx248Subfx248(const float_t a, const float_t b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
float_t fx248Subi32(const float_t a, const int32_t b) {
|
||||
return fx248Subfx248(a, fx248Fromi32(b));
|
||||
}
|
||||
|
||||
float_t fx248Subu32(const float_t a, const uint32_t b) {
|
||||
return fx248Subfx248(a, fx248Fromu32(b));
|
||||
}
|
||||
|
||||
float_t fx248Subf32(const float_t a, const float_t b) {
|
||||
return fx248Subfx248(a, fx248Fromf32(b));
|
||||
}
|
||||
|
||||
|
||||
|
||||
float_t fx248Mulfx248(const float_t a, const float_t b) {
|
||||
return (float_t)(((int64_t)a * (int64_t)b) >> FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
float_t fx248Muli32(const float_t a, const int32_t b) {
|
||||
return (float_t)(((int64_t)a * (int64_t)b) >> FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
float_t fx248Mulu32(const float_t a, const uint32_t b) {
|
||||
return (float_t)(
|
||||
((int64_t)a * (int64_t)(int32_t)b
|
||||
) >> FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
float_t fx248Mulf32(const float_t a, const float_t b) {
|
||||
return (float_t)((
|
||||
(int64_t)a * (int64_t)(b * (1 << FIXED248_FRACTION_BITS))
|
||||
) >> FIXED248_FRACTION_BITS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
float_t fx248Divfx248(const float_t a, const float_t b) {
|
||||
assertFalse(b == 0, "Division by zero in fx248Divfx248");
|
||||
return (float_t)(((int64_t)a << FIXED248_FRACTION_BITS) / (int64_t)b);
|
||||
}
|
||||
|
||||
float_t fx248Divi32(const float_t a, const int32_t b) {
|
||||
assertFalse(b == 0, "Division by zero in fx248Divi32");
|
||||
return (float_t)(((int64_t)a << FIXED248_FRACTION_BITS) / (int64_t)b);
|
||||
}
|
||||
|
||||
float_t fx248Divu32(const float_t a, const uint32_t b) {
|
||||
assertFalse(b == 0, "Division by zero in fx248Divu32");
|
||||
return (float_t)(
|
||||
((int64_t)a << FIXED248_FRACTION_BITS
|
||||
) / (int64_t)(int32_t)b);
|
||||
}
|
||||
|
||||
float_t fx248Divf32(const float_t a, const float_t b) {
|
||||
assertFalse(b == 0, "Division by zero in fx248Divf32");
|
||||
return (float_t)((
|
||||
(int64_t)a << FIXED248_FRACTION_BITS
|
||||
) / (int64_t)(b * (1 << FIXED248_FRACTION_BITS)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
float_t fx248Floor(const float_t a) {
|
||||
return a & ~((1 << FIXED248_FRACTION_BITS) - 1);
|
||||
}
|
||||
|
||||
float_t fx248Ceil(const float_t a) {
|
||||
if(a & ((1 << FIXED248_FRACTION_BITS) - 1)) {
|
||||
return (a & ~((1 << FIXED248_FRACTION_BITS) - 1)) + (1 << FIXED248_FRACTION_BITS);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
float_t fx248Round(const float_t a) {
|
||||
if(a & ((1 << (FIXED248_FRACTION_BITS - 1)) - 1)) {
|
||||
return (a & ~((1 << FIXED248_FRACTION_BITS) - 1)) + (1 << FIXED248_FRACTION_BITS);
|
||||
}
|
||||
return a & ~((1 << FIXED248_FRACTION_BITS) - 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t fx248Flooru32(const float_t a) {
|
||||
return (uint32_t)((a >> FIXED248_FRACTION_BITS) & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
uint32_t fx248Ceilu32(const float_t a) {
|
||||
return (uint32_t)(((a + ((1 << FIXED248_FRACTION_BITS) - 1)) >> FIXED248_FRACTION_BITS) & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
uint32_t fx248Roundu32(const float_t a) {
|
||||
return (uint32_t)(((a + (1 << (FIXED248_FRACTION_BITS - 1))) >> FIXED248_FRACTION_BITS) & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
float_t fx248Sqrt(const float_t a) {
|
||||
if(a == 0) return 0;
|
||||
|
||||
float_t y = a > FIXED248(1, 0) ? a : FIXED248(1, 0);
|
||||
float_t last = 0;
|
||||
int max_iter = 16;
|
||||
while(y != last && max_iter-- > 0) {
|
||||
last = y;
|
||||
int32_t div = (int32_t)(((int64_t)a << FIXED248_FRACTION_BITS) / y);
|
||||
y = (y + div) >> 1;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
|
||||
float_t fx248Max(const float_t a, const float_t b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
float_t fx248Min(const float_t a, const float_t b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
float_t fx248Clamp(
|
||||
const float_t a,
|
||||
const float_t min,
|
||||
const float_t max
|
||||
) {
|
||||
return (a < min) ? min : (a > max) ? max : a;
|
||||
}
|
||||
|
||||
float_t fx248Abs(const float_t a) {
|
||||
return (a < 0) ? -a : a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
float_t fx248Atan2(
|
||||
const float_t y,
|
||||
const float_t x
|
||||
) {
|
||||
// Handle special cases
|
||||
if (x == 0) {
|
||||
if (y > 0) return FX248_HALF_PI;
|
||||
if (y < 0) return -FX248_HALF_PI;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Use absolute values for quadrant correction
|
||||
float_t abs_y = y;
|
||||
if (abs_y < 0) abs_y = -abs_y;
|
||||
|
||||
float_t angle;
|
||||
if (abs_y < fx248Abs(x)) {
|
||||
float_t z = fx248Divfx248(y, x);
|
||||
float_t z2 = fx248Mulfx248(z, z);
|
||||
float_t z3 = fx248Mulfx248(z2, z);
|
||||
float_t z5 = fx248Mulfx248(z3, z2);
|
||||
angle = fx248Subfx248(
|
||||
fx248Addfx248(z, fx248Divfx248(z5, fx248Fromi32(5))),
|
||||
fx248Divfx248(z3, fx248Fromi32(3))
|
||||
);
|
||||
if (x < 0) {
|
||||
if (y < 0) {
|
||||
angle -= FX248_PI;
|
||||
} else {
|
||||
angle += FX248_PI;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float_t z = fx248Divfx248(x, y);
|
||||
float_t z2 = fx248Mulfx248(z, z);
|
||||
float_t z3 = fx248Mulfx248(z2, z);
|
||||
float_t z5 = fx248Mulfx248(z3, z2);
|
||||
angle = fx248Subfx248(
|
||||
fx248Addfx248(z, fx248Divfx248(z5, fx248Fromi32(5))),
|
||||
fx248Divfx248(z3, fx248Fromi32(3))
|
||||
);
|
||||
if (y > 0) {
|
||||
angle = FX248_HALF_PI - angle;
|
||||
} else {
|
||||
angle = -FX248_HALF_PI - angle;
|
||||
}
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
-379
@@ -1,379 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
typedef int32_t float_t;
|
||||
|
||||
#define FIXED248_FRACTION_BITS 8
|
||||
#define FIXED248_HIGH_MULTIPLIER (1 << FIXED248_FRACTION_BITS)
|
||||
#define FIXED248_MIN INT32_MIN
|
||||
#define FIXED248_MAX INT32_MAX
|
||||
#define FIXED248(i, f) ((float_t)( \
|
||||
((i) << FIXED248_FRACTION_BITS) + \
|
||||
(((f) * FIXED248_HIGH_MULTIPLIER) / 100) \
|
||||
))
|
||||
#define FIXED248_ONE (FIXED248(1, 0))
|
||||
#define FIXED248_ZERO (FIXED248(0, 0))
|
||||
#define FX248_PI 804
|
||||
#define FX248_HALF_PI 402
|
||||
#define FX248_3PI_4 603
|
||||
#define FX248_NEG_PI -804
|
||||
|
||||
/**
|
||||
* Convert an int32_t value to a float_t value.
|
||||
*
|
||||
* @param b The int32_t value to convert.
|
||||
* @return The converted float_t value.
|
||||
*/
|
||||
float_t fx248Fromi32(const int32_t b);
|
||||
|
||||
/**
|
||||
* Convert a uint32_t value to a float_t value.
|
||||
*
|
||||
* @param b The uint32_t value to convert.
|
||||
* @return The converted float_t value.
|
||||
*/
|
||||
float_t fx248Fromu32(const uint32_t b);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to a float_t value.
|
||||
*
|
||||
* @param b The float_t value to convert.
|
||||
* @return The converted float_t value.
|
||||
*/
|
||||
float_t fx248Fromf32(const float_t b);
|
||||
|
||||
/**
|
||||
* Convert a uint16_t value to a float_t value.
|
||||
*
|
||||
* @param b The uint16_t value to convert.
|
||||
* @return The converted float_t value.
|
||||
*/
|
||||
float_t fx248Fromu16(const uint16_t b);
|
||||
|
||||
/**
|
||||
* Convert a uint8_t value to a float_t value.
|
||||
*
|
||||
* @param b The uint8_t value to convert.
|
||||
* @return The converted float_t value.
|
||||
*/
|
||||
float_t fx248Fromu8(const uint8_t b);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a float_t value to an int32_t value.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted int32_t value.
|
||||
*/
|
||||
int32_t fx248Toi32(const float_t a);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to a uint32_t value.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted uint32_t value.
|
||||
*/
|
||||
uint32_t fx248Tou32(const float_t a);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to a float_t value.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted float_t value.
|
||||
*/
|
||||
float_t fx248Tof32(const float_t a);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to a uint16_t value.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted uint16_t value.
|
||||
*/
|
||||
uint16_t fx248Tou16(const float_t a);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to an uint8_t value.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted uint8_t value.
|
||||
*/
|
||||
uint8_t fx248Tou8(const float_t a);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a float_t value to another float_t value.
|
||||
*
|
||||
* @param a First float_t value.
|
||||
* @param b Second float_t value to add to the first value.
|
||||
* @return The result of the addition as a float_t value.
|
||||
*/
|
||||
float_t fx248Addfx248(const float_t a, const float_t b);
|
||||
|
||||
/**
|
||||
* Add an int32_t value to a float_t value.
|
||||
*
|
||||
* @param a The float_t value to which the int32_t will be added.
|
||||
* @param b The int32_t value to add to the float_t value.
|
||||
* @return The result of the addition as a float_t value.
|
||||
*/
|
||||
float_t fx248Addi32(const float_t a, const int32_t b);
|
||||
|
||||
/**
|
||||
* Add a uint32_t value to a float_t value.
|
||||
*
|
||||
* @param a The float_t value to which the uint32_t will be added.
|
||||
* @param b The uint32_t value to add to the float_t value.
|
||||
* @return The result of the addition as a float_t value.
|
||||
*/
|
||||
float_t fx248Addu32(const float_t a, const uint32_t b);
|
||||
|
||||
/**
|
||||
* Add a float_t value to a float_t value.
|
||||
*
|
||||
* @param a Pointer to the float_t value (will be modified).
|
||||
* @param b The float_t value to add to the float_t value.
|
||||
* @return The result of the addition as a float_t value.
|
||||
*/
|
||||
float_t fx248Addf32(const float_t a, const float_t b);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subtract a float_t value from another float_t value.
|
||||
*
|
||||
* @param a First float_t value.
|
||||
* @param b The float_t value to subtract from the first value.
|
||||
* @return The result of the subtraction as a float_t value.
|
||||
*/
|
||||
float_t fx248Subfx248(const float_t a, const float_t b);
|
||||
|
||||
/**
|
||||
* Subtract an int32_t value from a float_t value.
|
||||
*
|
||||
* @param a The float_t value from which the int32_t will be subtracted.
|
||||
* @param b The int32_t value to subtract from the float_t value.
|
||||
* @return The result of the subtraction as a float_t value.
|
||||
*/
|
||||
float_t fx248Subi32(const float_t a, const int32_t b);
|
||||
|
||||
/**
|
||||
* Subtract a uint32_t value from a float_t value.
|
||||
*
|
||||
* @param a The float_t value from which the uint32_t will be subtracted.
|
||||
* @param b The uint32_t value to subtract from the float_t value.
|
||||
* @return The result of the subtraction as a float_t value.
|
||||
*/
|
||||
float_t fx248Subu32(const float_t a, const uint32_t b);
|
||||
|
||||
/**
|
||||
* Subtract a float_t value from a float_t value.
|
||||
*
|
||||
* @param a The float_t value from which the float_t will be subtracted.
|
||||
* @param b The float_t value to subtract from the float_t value.
|
||||
* @return The result of the subtraction as a float_t value.
|
||||
*/
|
||||
float_t fx248Subf32(const float_t a, const float_t b);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Multiply two float_t values.
|
||||
*
|
||||
* @param a First float_t value.
|
||||
* @param b Second float_t value to multiply with the first value.
|
||||
* @return The result of the multiplication as a float_t value.
|
||||
*/
|
||||
float_t fx248Mulfx248(const float_t a, const float_t b);
|
||||
|
||||
/**
|
||||
* Multiply a float_t value by an int32_t value.
|
||||
*
|
||||
* @param a The float_t value to multiply.
|
||||
* @param b The int32_t value to multiply with the float_t value.
|
||||
* @return The result of the multiplication as a float_t value.
|
||||
*/
|
||||
float_t fx248Muli32(const float_t a, const int32_t b);
|
||||
|
||||
/**
|
||||
* Multiply a float_t value by a uint32_t value.
|
||||
*
|
||||
* @param a The float_t value to multiply.
|
||||
* @param b The uint32_t value to multiply with the float_t value.
|
||||
* @return The result of the multiplication as a float_t value.
|
||||
*/
|
||||
float_t fx248Mulu32(const float_t a, const uint32_t b);
|
||||
|
||||
/**
|
||||
* Multiply a float_t value by a float_t value.
|
||||
*
|
||||
* @param a The float_t value to multiply.
|
||||
* @param b The float_t value to multiply with the float_t value.
|
||||
* @return The result of the multiplication as a float_t value.
|
||||
*/
|
||||
float_t fx248Mulf32(const float_t a, const float_t b);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Divide two float_t values.
|
||||
*
|
||||
* @param a The float_t value to be divided.
|
||||
* @param b The float_t value to divide by.
|
||||
* @return The result of the division as a float_t value.
|
||||
*/
|
||||
float_t fx248Divfx248(const float_t a, const float_t b);
|
||||
|
||||
/**
|
||||
* Divide a float_t value by an int32_t value.
|
||||
*
|
||||
* @param a The float_t value to be divided.
|
||||
* @param b The int32_t value to divide by.
|
||||
* @return The result of the division as a float_t value.
|
||||
*/
|
||||
float_t fx248Divi32(const float_t a, const int32_t b);
|
||||
|
||||
/**
|
||||
* Divide a float_t value by a uint32_t value.
|
||||
*
|
||||
* @param a The float_t value to be divided.
|
||||
* @param b The uint32_t value to divide by.
|
||||
* @return The result of the division as a float_t value.
|
||||
*/
|
||||
float_t fx248Divu32(const float_t a, const uint32_t b);
|
||||
|
||||
/**
|
||||
* Divide a float_t value by a float_t value.
|
||||
*
|
||||
* @param a The float_t value to be divided.
|
||||
* @param b The float_t value to divide by.
|
||||
* @return The result of the division as a float_t value.
|
||||
*/
|
||||
float_t fx248Divf32(const float_t a, const float_t b);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a float_t value to an int32_t value, rounding towards zero.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted int32_t value.
|
||||
*/
|
||||
float_t fx248Floor(const float_t a);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to an int32_t value, rounding towards positive
|
||||
* infinity.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted int32_t value.
|
||||
*/
|
||||
float_t fx248Ceil(const float_t a);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to an int32_t value, rounding to the nearest
|
||||
* integer.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted int32_t value.
|
||||
*/
|
||||
float_t fx248Round(const float_t a);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a float_t value to a uint32_t value, rounding towards zero.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted uint32_t value.
|
||||
*/
|
||||
uint32_t fx248Flooru32(const float_t a);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to a uint32_t value, rounding towards positive
|
||||
* infinity.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted uint32_t value.
|
||||
*/
|
||||
uint32_t fx248Ceilu32(const float_t a);
|
||||
|
||||
/**
|
||||
* Convert a float_t value to a uint32_t value, rounding to the nearest
|
||||
* integer.
|
||||
*
|
||||
* @param a The float_t value to convert.
|
||||
* @return The converted uint32_t value.
|
||||
*/
|
||||
uint32_t fx248Roundu32(const float_t a);
|
||||
|
||||
/**
|
||||
* Returns the square root of a float_t value.
|
||||
*
|
||||
* @param a The float_t value to calculate the square root of.
|
||||
*/
|
||||
float_t fx248Sqrt(const float_t a);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the maximum of two float_t values.
|
||||
*
|
||||
* @param a First float_t value.
|
||||
* @param b Second float_t value.
|
||||
* @return The maximum of the two values.
|
||||
*/
|
||||
float_t fx248Max(const float_t a, const float_t b);
|
||||
|
||||
/**
|
||||
* Returns the minimum of two float_t values.
|
||||
*
|
||||
* @param a First float_t value.
|
||||
* @param b Second float_t value.
|
||||
* @return The minimum of the two values.
|
||||
*/
|
||||
float_t fx248Min(const float_t a, const float_t b);
|
||||
|
||||
/**
|
||||
* Clamp a float_t value between a minimum and maximum value.
|
||||
*
|
||||
* @param a The float_t value to clamp.
|
||||
* @param min The minimum value to clamp to.
|
||||
* @param max The maximum value to clamp to.
|
||||
* @return The clamped float_t value.
|
||||
*/
|
||||
float_t fx248Clamp(
|
||||
const float_t a,
|
||||
const float_t min,
|
||||
const float_t max
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the absolute value of a float_t value.
|
||||
*
|
||||
* @param a The float_t value to calculate the absolute value of.
|
||||
* @return The absolute value as a float_t value.
|
||||
*/
|
||||
float_t fx248Abs(const float_t a);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the arctangent of a float_t value.
|
||||
*
|
||||
* @param y Y coordinate value.
|
||||
* @param x X coordinate value.
|
||||
* @return The arctangent of the value as a float_t value.
|
||||
*/
|
||||
float_t fx248Atan2(
|
||||
const float_t y,
|
||||
const float_t x
|
||||
);
|
||||
@@ -0,0 +1,47 @@
|
||||
var Actions;
|
||||
var camera, cameraPosition;
|
||||
var cube, cubePosition, cubeRenderable, cubeMesh;
|
||||
var ground, groundPosition, groundRenderable;
|
||||
|
||||
// init() is called via scriptManagerCallGlobal(), which pumps the asset
|
||||
// system + job queue until any promise it returns settles - so it's safe
|
||||
// to await include() here even though this runs before the main loop.
|
||||
async function init() {
|
||||
Actions = await include("input.js");
|
||||
|
||||
camera = new Entity();
|
||||
cameraPosition = camera.add(POSITION);
|
||||
camera.add(CAMERA);
|
||||
cameraPosition.position = new Vec3(3, 3, -6);
|
||||
cameraPosition.lookAt(new Vec3(0, 0, 0));
|
||||
|
||||
cube = new Entity();
|
||||
cubePosition = cube.add(POSITION);
|
||||
cubeRenderable = cube.add(RENDERABLE);
|
||||
cubeMesh = Mesh.createCube();
|
||||
cubeRenderable.mesh = cubeMesh;
|
||||
cubeRenderable.color = Color.red();
|
||||
|
||||
ground = new Entity();
|
||||
groundPosition = ground.add(POSITION);
|
||||
groundRenderable = ground.add(RENDERABLE);
|
||||
groundRenderable.mesh = Mesh.createCube();
|
||||
groundRenderable.color = Color.dark_gray();
|
||||
}
|
||||
|
||||
// Runs every frame (including dynamic/interpolation frames) - use for
|
||||
// smooth, purely presentational animation.
|
||||
function update() {
|
||||
cubePosition.rotation.y += TIME.delta * 1.5;
|
||||
cubePosition.rotation.x += TIME.delta * 0.7;
|
||||
}
|
||||
|
||||
// Runs once per fixed timestep only - use for gameplay logic that should
|
||||
// be deterministic and independent of display refresh rate.
|
||||
function fixedUpdate() {
|
||||
}
|
||||
|
||||
function deinit() {
|
||||
cube.dispose();
|
||||
camera.dispose();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Binds physical buttons to abstract actions, then exports the action
|
||||
// constants so other scripts don't need to know raw INPUT_ACTION_* names.
|
||||
Input.bind("w", INPUT_ACTION_UP);
|
||||
Input.bind("s", INPUT_ACTION_DOWN);
|
||||
Input.bind("a", INPUT_ACTION_LEFT);
|
||||
Input.bind("d", INPUT_ACTION_RIGHT);
|
||||
Input.bind("space", INPUT_ACTION_ACCEPT);
|
||||
Input.bind("escape", INPUT_ACTION_RAGEQUIT);
|
||||
|
||||
if(typeof INPUT_GAMEPAD !== "undefined") {
|
||||
Input.bind("gamepad_up", INPUT_ACTION_UP);
|
||||
Input.bind("gamepad_down", INPUT_ACTION_DOWN);
|
||||
Input.bind("gamepad_left", INPUT_ACTION_LEFT);
|
||||
Input.bind("gamepad_right", INPUT_ACTION_RIGHT);
|
||||
Input.bind("gamepad_a", INPUT_ACTION_ACCEPT);
|
||||
Input.bind("gamepad_start", INPUT_ACTION_RAGEQUIT);
|
||||
}
|
||||
|
||||
module = {
|
||||
UP: INPUT_ACTION_UP,
|
||||
DOWN: INPUT_ACTION_DOWN,
|
||||
LEFT: INPUT_ACTION_LEFT,
|
||||
RIGHT: INPUT_ACTION_RIGHT,
|
||||
ACCEPT: INPUT_ACTION_ACCEPT,
|
||||
CANCEL: INPUT_ACTION_CANCEL,
|
||||
RAGEQUIT: INPUT_ACTION_RAGEQUIT
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
msgid "test.string"
|
||||
msgstr "This is a test string"
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
cglm
|
||||
# GIT_REPOSITORY https://git.wish.moe/YourWishes/cglm.git
|
||||
GIT_REPOSITORY https://github.com/recp/cglm.git
|
||||
GIT_TAG v0.9.6
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(cglm)
|
||||
set(cglm_FOUND ON)
|
||||
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
cmocka
|
||||
GIT_REPOSITORY https://gitlab.com/cmocka/cmocka.git
|
||||
GIT_TAG cmocka-2.0.1
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(cmocka)
|
||||
set(cmocka_FOUND ON)
|
||||
@@ -0,0 +1,96 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Turn things off we don't need
|
||||
set(JERRY_CMDLINE OFF CACHE BOOL "" FORCE)
|
||||
set(JERRY_EXT ON CACHE BOOL "" FORCE)
|
||||
set(JERRY_DEBUGGER OFF CACHE BOOL "" FORCE)
|
||||
set(JERRY_BUILTIN_DATE OFF CACHE BOOL "" FORCE)
|
||||
set(ENABLE_LTO OFF CACHE BOOL "" FORCE)
|
||||
|
||||
# Fetch Jerry
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
jerryscript
|
||||
GIT_REPOSITORY https://git.wish.moe/YourWishes/jerryscript
|
||||
GIT_TAG float32-fix
|
||||
)
|
||||
FetchContent_MakeAvailable(jerryscript)
|
||||
|
||||
# Mark found
|
||||
set(jerryscript_FOUND ON)
|
||||
|
||||
# Define targets
|
||||
if(TARGET jerryscript-core)
|
||||
set(JERRY_CORE_TARGET jerryscript-core)
|
||||
elseif(TARGET jerry-core)
|
||||
set(JERRY_CORE_TARGET jerry-core)
|
||||
endif()
|
||||
|
||||
if(TARGET jerryscript-ext)
|
||||
set(JERRY_EXT_TARGET jerryscript-ext)
|
||||
elseif(TARGET jerry-ext)
|
||||
set(JERRY_EXT_TARGET jerry-ext)
|
||||
endif()
|
||||
|
||||
if(TARGET jerryscript-port-default)
|
||||
set(JERRY_PORT_TARGET jerryscript-port-default)
|
||||
elseif(TARGET jerry-port-default)
|
||||
set(JERRY_PORT_TARGET jerry-port-default)
|
||||
elseif(TARGET jerryscript-port)
|
||||
set(JERRY_PORT_TARGET jerryscript-port)
|
||||
elseif(TARGET jerry-port)
|
||||
set(JERRY_PORT_TARGET jerry-port)
|
||||
endif()
|
||||
|
||||
if(NOT JERRY_CORE_TARGET)
|
||||
message(FATAL_ERROR "JerryScript core target not found")
|
||||
endif()
|
||||
|
||||
if(NOT JERRY_EXT_TARGET)
|
||||
message(FATAL_ERROR "JerryScript ext target not found")
|
||||
endif()
|
||||
|
||||
if(NOT JERRY_PORT_TARGET)
|
||||
message(FATAL_ERROR "JerryScript port target not found")
|
||||
endif()
|
||||
|
||||
foreach(tgt IN ITEMS
|
||||
${JERRY_CORE_TARGET}
|
||||
${JERRY_EXT_TARGET}
|
||||
${JERRY_PORT_TARGET}
|
||||
)
|
||||
if(TARGET ${tgt})
|
||||
set_property(TARGET ${tgt} PROPERTY INTERPROCEDURAL_OPTIMIZATION OFF)
|
||||
target_compile_definitions(${JERRY_CORE_TARGET} PRIVATE
|
||||
JERRY_NUMBER_TYPE_FLOAT64=0
|
||||
JERRY_BUILTIN_DATE=0
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Export include dirs through the targets
|
||||
target_include_directories(${JERRY_CORE_TARGET} INTERFACE
|
||||
${jerryscript_SOURCE_DIR}/jerry-core/include
|
||||
)
|
||||
|
||||
target_include_directories(${JERRY_EXT_TARGET} INTERFACE
|
||||
${jerryscript_SOURCE_DIR}/jerry-ext/include
|
||||
)
|
||||
|
||||
target_include_directories(${JERRY_PORT_TARGET} INTERFACE
|
||||
${jerryscript_SOURCE_DIR}/jerry-port/default/include
|
||||
)
|
||||
|
||||
# Suppress JerryScript-only warning
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(${JERRY_CORE_TARGET} PRIVATE
|
||||
-Wno-error
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(jerryscript::core ALIAS ${JERRY_CORE_TARGET})
|
||||
add_library(jerryscript::ext ALIAS ${JERRY_EXT_TARGET})
|
||||
add_library(jerryscript::port ALIAS ${JERRY_PORT_TARGET})
|
||||
@@ -0,0 +1,61 @@
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
find_path(LIBZIP_INCLUDE_DIR NAMES zip.h)
|
||||
mark_as_advanced(LIBZIP_INCLUDE_DIR)
|
||||
|
||||
find_library(LIBZIP_LIBRARY NAMES zip)
|
||||
mark_as_advanced(LIBZIP_LIBRARY)
|
||||
|
||||
get_filename_component(_libzip_libdir ${LIBZIP_LIBRARY} DIRECTORY)
|
||||
find_file(_libzip_pkgcfg libzip.pc
|
||||
HINTS ${_libzip_libdir} ${LIBZIP_INCLUDE_DIR}/..
|
||||
PATH_SUFFIXES pkgconfig lib/pkgconfig libdata/pkgconfig
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
LIBZIP
|
||||
REQUIRED_VARS
|
||||
LIBZIP_LIBRARY
|
||||
LIBZIP_INCLUDE_DIR
|
||||
_libzip_pkgcfg
|
||||
)
|
||||
|
||||
if(LIBZIP_FOUND)
|
||||
if(NOT TARGET libzip::zip)
|
||||
add_library(libzip::zip UNKNOWN IMPORTED)
|
||||
set_target_properties(libzip::zip
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${LIBZIP_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES ZLIB::ZLIB
|
||||
IMPORTED_LOCATION ${LIBZIP_LIBRARY}
|
||||
)
|
||||
# (Ab)use the (always) installed pkgconfig file to check if BZip2 is required
|
||||
file(STRINGS ${_libzip_pkgcfg} _have_extra_libs REGEX Libs)
|
||||
if(_have_extra_libs MATCHES "-lbz2")
|
||||
find_package(BZip2 REQUIRED)
|
||||
set_property(TARGET libzip::zip APPEND PROPERTY INTERFACE_LINK_LIBRARIES BZip2::BZip2)
|
||||
endif()
|
||||
if(_have_extra_libs MATCHES "-lcrypto")
|
||||
find_package(OpenSSL REQUIRED)
|
||||
set_property(TARGET libzip::zip APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenSSL::Crypto)
|
||||
endif()
|
||||
if(_have_extra_libs MATCHES "-lgnutls")
|
||||
find_package(GnuTLS REQUIRED)
|
||||
set_property(TARGET libzip::zip APPEND PROPERTY INTERFACE_LINK_LIBRARIES GnuTLS::GnuTLS)
|
||||
endif()
|
||||
if(_have_extra_libs MATCHES "-lnettle")
|
||||
find_package(Nettle REQUIRED)
|
||||
set_property(TARGET libzip::zip APPEND PROPERTY INTERFACE_LINK_LIBRARIES Nettle::Nettle)
|
||||
endif()
|
||||
if(_have_extra_libs MATCHES "-llzma")
|
||||
find_package(LibLZMA REQUIRED)
|
||||
set_property(TARGET libzip::zip APPEND PROPERTY INTERFACE_LINK_LIBRARIES LibLZMA::LibLZMA)
|
||||
endif()
|
||||
if(_have_extra_libs MATCHES "-lz")
|
||||
find_package(ZLIB REQUIRED)
|
||||
set_property(TARGET libzip::zip APPEND PROPERTY INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -1,103 +0,0 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
if(NOT TARGET pspsdk)
|
||||
message(STATUS "Looking for PSPSDK...")
|
||||
|
||||
set(PSPSDK_FOUND FALSE CACHE INTERNAL "PSPSDK found")
|
||||
set(PSPSDK_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/_pspsdk")
|
||||
set(PSPSDK_SEARCH_ROOTS
|
||||
"${PSPSDK_ROOT}"
|
||||
"$ENV{PSPDEV}"
|
||||
"$ENV{HOME}/pspdev"
|
||||
"/usr/local/pspdev"
|
||||
"/opt/pspdev"
|
||||
"/usr/pspdev"
|
||||
"${PSPSDK_DOWNLOAD_DIR}/pspdev"
|
||||
)
|
||||
|
||||
foreach(root IN LISTS PSPSDK_SEARCH_ROOTS)
|
||||
list(APPEND PSPSDK_BIN_HINTS "${root}/bin")
|
||||
list(APPEND PSPSDK_INCLUDE_HINTS "${root}/include")
|
||||
list(APPEND PSPSDK_LIB_HINTS "${root}/lib")
|
||||
endforeach()
|
||||
|
||||
# Find PSP GCC
|
||||
find_program(PSPSDK_PSP_GCC NAMES psp-gcc HINTS ${PSPSDK_BIN_HINTS})
|
||||
|
||||
# If we did not find it, download it.
|
||||
if(NOT PSPSDK_PSP_GCC)
|
||||
message(STATUS "psp-gcc not found in system paths. Downloading PSPSDK tarball...")
|
||||
file(DOWNLOAD
|
||||
"https://github.com/pspdev/pspdev/releases/latest/download/pspdev-ubuntu-latest-x86_64.tar.gz"
|
||||
"${CMAKE_BINARY_DIR}/pspsdk.tar.gz"
|
||||
EXPECTED_HASH SHA256=afe338e92f6eeec21c56a64eeb65201e6b95ecbae938ae38688bbf0f17b69ead
|
||||
SHOW_PROGRESS
|
||||
)
|
||||
|
||||
# Make output dir
|
||||
file(MAKE_DIRECTORY "${PSPSDK_DOWNLOAD_DIR}")
|
||||
|
||||
# Extract the tarball
|
||||
execute_process(
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E tar xzf "${CMAKE_BINARY_DIR}/pspsdk.tar.gz"
|
||||
WORKING_DIRECTORY
|
||||
"${PSPSDK_DOWNLOAD_DIR}"
|
||||
RESULT_VARIABLE
|
||||
tar_result
|
||||
)
|
||||
if(NOT tar_result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to extract PSPSDK tarball")
|
||||
endif()
|
||||
|
||||
# Retry discovery with extracted fallback
|
||||
find_program(PSPSDK_PSP_GCC NAMES psp-gcc HINTS ${PSPSDK_BIN_HINTS})
|
||||
endif()
|
||||
|
||||
if(PSPSDK_PSP_GCC)
|
||||
get_filename_component(PSPSDK_BIN_ROOT "${PSPSDK_PSP_GCC}" DIRECTORY)
|
||||
get_filename_component(PSPSDK_ROOT "${PSPSDK_BIN_ROOT}" DIRECTORY)
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
set(ENV{PSPDEV} "${PSPSDK_ROOT}")
|
||||
set(CMAKE_TOOLCHAIN_FILE "${PSPSDK_ROOT}/psp/share/pspdev.cmake")
|
||||
set(BUILD_PRX ON CACHE BOOL "Build PRX modules")
|
||||
|
||||
include("${PSPSDK_ROOT}/psp/share/pspdev.cmake")
|
||||
set(CMAKE_C_COMPILER ${PSPSDK_BIN_ROOT}/psp-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${PSPSDK_BIN_ROOT}/psp-g++)
|
||||
|
||||
if(NOT DEFINED PSP)
|
||||
message(FATAL_ERROR "PSP environment variable is not set correctly.")
|
||||
endif()
|
||||
|
||||
add_library(pspsdk INTERFACE IMPORTED)
|
||||
set_target_properties(pspsdk PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${PSPSDK_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_DIRECTORIES "${PSPSDK_LIB_DIR}"
|
||||
)
|
||||
target_include_directories(pspsdk
|
||||
INTERFACE
|
||||
${PSPDEV}/psp/include
|
||||
${PSPDEV}/psp/sdk/include
|
||||
)
|
||||
target_link_directories(pspsdk
|
||||
INTERFACE
|
||||
${PSPDEV}/lib
|
||||
${PSPDEV}/psp/lib
|
||||
${PSPDEV}/psp/sdk/lib
|
||||
)
|
||||
target_link_libraries(pspsdk INTERFACE
|
||||
pspdebug
|
||||
pspdisplay
|
||||
pspge
|
||||
pspctrl
|
||||
pspgu
|
||||
pspaudio
|
||||
pspaudiolib
|
||||
psputility
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,31 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
stb
|
||||
GIT_REPOSITORY https://github.com/nothings/stb.git
|
||||
)
|
||||
|
||||
# Fetch stb if not already done
|
||||
FetchContent_MakeAvailable(stb)
|
||||
|
||||
# Find the stb_image.h header
|
||||
set(STB_INCLUDE_DIR "${stb_SOURCE_DIR}")
|
||||
set(STB_IMAGE_HEADER "${stb_SOURCE_DIR}/stb_image.h")
|
||||
|
||||
if(EXISTS "${STB_IMAGE_HEADER}")
|
||||
add_library(stb_image INTERFACE)
|
||||
target_include_directories(stb_image INTERFACE "${STB_INCLUDE_DIR}")
|
||||
set(STB_IMAGE_FOUND TRUE)
|
||||
else()
|
||||
set(STB_IMAGE_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
# Standard CMake variables
|
||||
set(STB_IMAGE_INCLUDE_DIRS "${STB_INCLUDE_DIR}")
|
||||
set(STB_IMAGE_LIBRARIES stb_image)
|
||||
|
||||
mark_as_advanced(STB_IMAGE_INCLUDE_DIRS STB_IMAGE_LIBRARIES STB_IMAGE_FOUND)
|
||||
@@ -0,0 +1,18 @@
|
||||
include(FetchContent)
|
||||
|
||||
if(NOT TARGET yyjson)
|
||||
FetchContent_Declare(
|
||||
yyjson
|
||||
GIT_REPOSITORY https://github.com/ibireme/yyjson.git
|
||||
GIT_TAG 0.12.0
|
||||
)
|
||||
FetchContent_MakeAvailable(yyjson)
|
||||
endif()
|
||||
|
||||
# Provide an imported target if not already available
|
||||
if(NOT TARGET yyjson::yyjson)
|
||||
add_library(yyjson::yyjson ALIAS yyjson)
|
||||
endif()
|
||||
|
||||
# Mark yyjson as found for find_package compatibility
|
||||
set(yyjson_FOUND TRUE)
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
find_package(cmocka REQUIRED)
|
||||
|
||||
# Function to create a test executable from a single .c file
|
||||
function(dusktest TEST_C_FILE)
|
||||
# Create target name from file name
|
||||
get_filename_component(TEST_NAME ${TEST_C_FILE} NAME_WE)
|
||||
|
||||
# Create the executable target
|
||||
add_executable(${TEST_NAME})
|
||||
|
||||
# Setup some compiler definitions.
|
||||
target_compile_definitions(${TEST_NAME} PRIVATE
|
||||
DUSK_TEST_ASSERT=1
|
||||
)
|
||||
|
||||
# Add the test file
|
||||
target_sources(${TEST_NAME} PRIVATE ${TEST_C_FILE})
|
||||
|
||||
# Add sources, both common and test-specific
|
||||
target_include_directories(${TEST_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${DUSK_TEST_DIR})
|
||||
|
||||
# Link against DuskCore and cmocka
|
||||
target_link_libraries(${TEST_NAME} PUBLIC ${DUSK_LIBRARY_TARGET_NAME} cmocka)
|
||||
|
||||
# Add as a CTest
|
||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||
endfunction()
|
||||
@@ -0,0 +1,79 @@
|
||||
# Build type: DOL (SD/USB via libfat) or ISO (DVD disc via libogc DVD driver)
|
||||
set(DUSK_DOLPHIN_BUILD_TYPE "DOL" CACHE STRING "Dolphin asset source: DOL (SD/USB) or ISO (DVD disc)")
|
||||
set_property(CACHE DUSK_DOLPHIN_BUILD_TYPE PROPERTY STRINGS "DOL" "ISO")
|
||||
|
||||
# Numeric tokens so #if DUSK_DOLPHIN_BUILD_TYPE == DOL works in C.
|
||||
# DUSK_DOLPHIN_BUILD_TYPE is passed without quotes so it expands to the identifier.
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_DOLPHIN
|
||||
DUSK_INPUT_GAMEPAD
|
||||
# DUSK_DISPLAY_WIDTH=640
|
||||
# DUSK_DISPLAY_HEIGHT=480
|
||||
DUSK_DISPLAY_WIDTH=320
|
||||
DUSK_DISPLAY_HEIGHT=240
|
||||
DUSK_DISPLAY_OVERSCAN=16
|
||||
DUSK_THREAD_PTHREAD
|
||||
DOL=1
|
||||
ISO=2
|
||||
DUSK_DOLPHIN_BUILD_TYPE=${DUSK_DOLPHIN_BUILD_TYPE}
|
||||
# GameCube/Wii PowerPC is always big-endian; declare it at compile time
|
||||
# like every other target instead of relying on endian.h's runtime probe.
|
||||
DUSK_PLATFORM_ENDIAN_BIG
|
||||
)
|
||||
|
||||
# Custom compiler flags
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions")
|
||||
|
||||
# Disable all warnings
|
||||
target_compile_options(${DUSK_LIBRARY_TARGET_NAME} PRIVATE -w)
|
||||
|
||||
# cglm: fetched at source level via Findcglm.cmake (FetchContent, headers only)
|
||||
find_package(cglm REQUIRED)
|
||||
|
||||
# Pre-create ZLIB::ZLIB so any downstream cmake module that resolves it
|
||||
# (FindZLIB, pkg-config IMPORTED_TARGET Requires processing) gets plain -lz
|
||||
# rather than an unresolvable IMPORTED target that the PPC linker rejects.
|
||||
if(NOT TARGET ZLIB::ZLIB)
|
||||
add_library(ZLIB::ZLIB INTERFACE IMPORTED GLOBAL)
|
||||
set_target_properties(ZLIB::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES "z")
|
||||
endif()
|
||||
|
||||
# Mark libzip as found so src/dusk/CMakeLists.txt skips Findlibzip.cmake.
|
||||
# Findlibzip.cmake calls find_package(ZLIB) which can recreate a broken
|
||||
# ZLIB::ZLIB IMPORTED target, bypassing the shim above.
|
||||
set(libzip_FOUND TRUE CACHE BOOL "libzip found (devkitpro portlibs)" FORCE)
|
||||
|
||||
# Locate zip.h in the devkitpro sysroot (respects CMAKE_FIND_ROOT_PATH).
|
||||
find_path(_dusk_zip_inc NAMES zip.h)
|
||||
if(_dusk_zip_inc)
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE "${_dusk_zip_inc}")
|
||||
endif()
|
||||
|
||||
# Link libraries.
|
||||
# zip/z/lzma use target_link_options (raw flags) to bypass cmake target
|
||||
# resolution - pkg-config-generated targets for these carry ZLIB::ZLIB in
|
||||
# INTERFACE_LINK_LIBRARIES which breaks the PPC link step.
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
cglm
|
||||
m
|
||||
zip
|
||||
bz2
|
||||
zstd
|
||||
z
|
||||
lzma
|
||||
)
|
||||
|
||||
if(DUSK_DOLPHIN_BUILD_TYPE STREQUAL "ISO")
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC DUSK_DOLPHIN_BUILD_ISO)
|
||||
else()
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE fat)
|
||||
endif()
|
||||
|
||||
# Postbuild: ELF -> DOL
|
||||
set(DUSK_BINARY_TARGET_NAME_DOL "${DUSK_BUILD_DIR}/Dusk.dol")
|
||||
add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
|
||||
COMMAND elf2dol
|
||||
"$<TARGET_FILE:${DUSK_BINARY_TARGET_NAME}>"
|
||||
"${DUSK_BINARY_TARGET_NAME_DOL}"
|
||||
COMMENT "Generating ${DUSK_BINARY_TARGET_NAME_DOL} from ${DUSK_BINARY_TARGET_NAME}"
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
include(cmake/targets/dolphin.cmake)
|
||||
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_GAMECUBE
|
||||
)
|
||||
|
||||
# Link libraries
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
# bba
|
||||
)
|
||||
|
||||
# ISO post-build: produce NTSC-J, NTSC-U and PAL disc images
|
||||
if(DUSK_DOLPHIN_BUILD_TYPE STREQUAL "ISO")
|
||||
add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
"${CMAKE_SOURCE_DIR}/tools/makedolphiniso.py"
|
||||
"GCN"
|
||||
"${DUSK_BINARY_TARGET_NAME_DOL}"
|
||||
"${DUSK_ASSETS_ZIP}"
|
||||
"${DUSK_GAME_NAME}"
|
||||
"${DUSK_BUILD_DIR}"
|
||||
COMMENT "Building GameCube ISO images (NTSC-J, NTSC-U, PAL)"
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,47 @@
|
||||
# Find link platform-specific libraries
|
||||
set(OpenGL_GL_PREFERENCE LEGACY)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_library(EGL_LIB EGL REQUIRED)
|
||||
find_library(GL_LIB GL REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
# Setup endianess at compile time to optimize.
|
||||
include(TestBigEndian)
|
||||
test_big_endian(IS_BIG_ENDIAN)
|
||||
if(IS_BIG_ENDIAN)
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_PLATFORM_ENDIAN_BIG
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_PLATFORM_ENDIAN_LITTLE
|
||||
)
|
||||
endif()
|
||||
|
||||
# Link required libraries.
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
SDL2
|
||||
pthread
|
||||
OpenGL::GLES2
|
||||
${GL_LIB}
|
||||
${EGL_LIB}
|
||||
m
|
||||
)
|
||||
|
||||
# Define platform-specific macros.
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_SDL2
|
||||
DUSK_OPENGL
|
||||
DUSK_OPENGL_ES
|
||||
DUSK_LINUX
|
||||
DUSK_KNULLI
|
||||
DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
DUSK_DISPLAY_WIDTH_DEFAULT=640
|
||||
DUSK_DISPLAY_HEIGHT_DEFAULT=480
|
||||
DUSK_DISPLAY_SCREEN_HEIGHT=240
|
||||
DUSK_INPUT_KEYBOARD
|
||||
DUSK_INPUT_POINTER
|
||||
DUSK_INPUT_GAMEPAD
|
||||
DUSK_TIME_DYNAMIC
|
||||
DUSK_THREAD_PTHREAD
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
# Find link platform-specific libraries
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
# find_package(CURL REQUIRED)
|
||||
|
||||
# Setup endianess at compile time to optimize.
|
||||
include(TestBigEndian)
|
||||
test_big_endian(IS_BIG_ENDIAN)
|
||||
if(IS_BIG_ENDIAN)
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_PLATFORM_ENDIAN_BIG
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_PLATFORM_ENDIAN_LITTLE
|
||||
)
|
||||
endif()
|
||||
|
||||
# Link required libraries.
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
SDL2
|
||||
pthread
|
||||
OpenGL::GL
|
||||
GL
|
||||
m
|
||||
# CURL::libcurl
|
||||
)
|
||||
|
||||
set(DUSK_BACKTRACE ON CACHE BOOL "Enable backtrace support for assert failures.")
|
||||
|
||||
# Define platform-specific macros.
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_SDL2
|
||||
DUSK_OPENGL
|
||||
DUSK_CONSOLE_POSIX
|
||||
# DUSK_OPENGL_LEGACY
|
||||
DUSK_LINUX
|
||||
DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
DUSK_DISPLAY_WIDTH_DEFAULT=854
|
||||
DUSK_DISPLAY_HEIGHT_DEFAULT=480
|
||||
DUSK_DISPLAY_SCREEN_HEIGHT=240
|
||||
DUSK_INPUT_KEYBOARD
|
||||
DUSK_INPUT_POINTER
|
||||
DUSK_INPUT_GAMEPAD
|
||||
DUSK_TIME_DYNAMIC
|
||||
DUSK_NETWORK_IPV6
|
||||
DUSK_THREAD_PTHREAD
|
||||
)
|
||||
@@ -0,0 +1,77 @@
|
||||
set(CMAKE_AR "$ENV{PSPDEV}/bin/psp-ar" CACHE FILEPATH "" FORCE)
|
||||
set(CMAKE_RANLIB "$ENV{PSPDEV}/bin/psp-ranlib" CACHE FILEPATH "" FORCE)
|
||||
set(CMAKE_C_COMPILER_AR "$ENV{PSPDEV}/bin/psp-ar" CACHE FILEPATH "" FORCE)
|
||||
set(CMAKE_C_COMPILER_RANLIB "$ENV{PSPDEV}/bin/psp-ranlib" CACHE FILEPATH "" FORCE)
|
||||
set(CMAKE_C_ARCHIVE_CREATE "$ENV{PSPDEV}/bin/psp-ar qc <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
set(CMAKE_C_ARCHIVE_APPEND "$ENV{PSPDEV}/bin/psp-ar q <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||
set(CMAKE_C_ARCHIVE_FINISH "$ENV{PSPDEV}/bin/psp-ranlib <TARGET>")
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF CACHE BOOL "" FORCE)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_C OFF CACHE BOOL "" FORCE)
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
target_link_libraries(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||
${SDL2_LIBRARIES}
|
||||
SDL2
|
||||
pthread
|
||||
OpenGL::GL
|
||||
zip
|
||||
bz2
|
||||
z
|
||||
mbedtls
|
||||
mbedcrypto
|
||||
lzma
|
||||
m
|
||||
|
||||
pspdebug
|
||||
pspdisplay
|
||||
pspge
|
||||
pspctrl
|
||||
pspgu
|
||||
pspaudio
|
||||
pspaudiolib
|
||||
psputility
|
||||
pspvfpu
|
||||
pspvram
|
||||
psphprm
|
||||
pspnet
|
||||
pspnet_inet
|
||||
pspnet_apctl
|
||||
psphttp
|
||||
pspssl
|
||||
)
|
||||
|
||||
target_include_directories(${DUSK_BINARY_TARGET_NAME} PRIVATE
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||
DUSK_SDL2
|
||||
DUSK_OPENGL
|
||||
DUSK_PSP
|
||||
DUSK_INPUT_GAMEPAD
|
||||
DUSK_PLATFORM_ENDIAN_LITTLE
|
||||
DUSK_OPENGL_LEGACY
|
||||
DUSK_DISPLAY_WIDTH=480
|
||||
DUSK_DISPLAY_HEIGHT=272
|
||||
DUSK_THREAD_PTHREAD
|
||||
DUSK_TIME_DYNAMIC
|
||||
DUSK_DISPLAY_OVERSCAN=6
|
||||
)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||
DUSK_ASSERTIONS_FAKED
|
||||
)
|
||||
endif()
|
||||
|
||||
# Postbuild, create .pbp file for PSP.
|
||||
create_pbp_file(
|
||||
TARGET "${DUSK_BINARY_TARGET_NAME}"
|
||||
ICON_PATH NULL
|
||||
BACKGROUND_PATH NULL
|
||||
PREVIEW_PATH NULL
|
||||
TITLE "${DUSK_BINARY_TARGET_NAME}"
|
||||
PSAR_PATH ${DUSK_ASSETS_ZIP}
|
||||
VERSION 01.00
|
||||
)
|
||||
@@ -0,0 +1,89 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
if(NOT DEFINED ENV{VITASDK})
|
||||
message(FATAL_ERROR "VITASDK environment variable is not set.")
|
||||
endif()
|
||||
|
||||
include("$ENV{VITASDK}/share/vita.cmake" REQUIRED)
|
||||
|
||||
set(VITA_APP_NAME "Dusk")
|
||||
set(VITA_TITLEID "DUSK00001")
|
||||
set(VITA_VERSION "01.00")
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
# Custom flags for cglm
|
||||
set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
|
||||
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
|
||||
find_package(cglm REQUIRED)
|
||||
|
||||
# Link libraries
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
${SDL2_LIBRARIES}
|
||||
cglm
|
||||
SDL2
|
||||
SDL2main
|
||||
zip
|
||||
bz2
|
||||
z
|
||||
zstd
|
||||
crypto
|
||||
lzma
|
||||
m
|
||||
pthread
|
||||
stdc++
|
||||
vitaGL
|
||||
mathneon
|
||||
vitashark
|
||||
kubridge_stub
|
||||
SceAppMgr_stub
|
||||
SceAudio_stub
|
||||
SceCtrl_stub
|
||||
SceCommonDialog_stub
|
||||
SceDisplay_stub
|
||||
SceKernelDmacMgr_stub
|
||||
SceGxm_stub
|
||||
SceShaccCg_stub
|
||||
SceSysmodule_stub
|
||||
ScePower_stub
|
||||
SceTouch_stub
|
||||
SceVshBridge_stub
|
||||
SceIofilemgr_stub
|
||||
SceShaccCgExt
|
||||
libtaihen_stub.a
|
||||
|
||||
|
||||
# SceKernel_stub
|
||||
SceAppUtil_stub
|
||||
SceHid_stub
|
||||
SceRtc_stub
|
||||
)
|
||||
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_SDL2
|
||||
DUSK_OPENGL
|
||||
DUSK_VITA
|
||||
DUSK_INPUT_GAMEPAD
|
||||
DUSK_PLATFORM_ENDIAN_LITTLE
|
||||
DUSK_OPENGL_LEGACY
|
||||
DUSK_DISPLAY_WIDTH=960
|
||||
DUSK_DISPLAY_HEIGHT=544
|
||||
DUSK_THREAD_PTHREAD
|
||||
)
|
||||
|
||||
# Post-build: create SELF from the ELF binary (UNSAFE = homebrew, no signing)
|
||||
vita_create_self(${DUSK_BINARY_TARGET_NAME}.self ${DUSK_BINARY_TARGET_NAME} UNSAFE)
|
||||
|
||||
# Post-build: package SELF + assets into a .vpk installable on the Vita
|
||||
vita_create_vpk(${DUSK_BINARY_TARGET_NAME}.vpk ${VITA_TITLEID} ${DUSK_BINARY_TARGET_NAME}.self
|
||||
VERSION ${VITA_VERSION}
|
||||
NAME ${VITA_APP_NAME}
|
||||
FILE ${DUSK_ASSETS_ZIP} dusk.dsk
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
include(cmake/targets/dolphin.cmake)
|
||||
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_WII
|
||||
)
|
||||
|
||||
# Generate Homebrew Channel meta.xml from project identity variables
|
||||
string(TIMESTAMP DUSK_BUILD_DATE "%Y%m%d000000" UTC)
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/docker/dolphin/meta.xml.in"
|
||||
"${DUSK_BUILD_DIR}/meta.xml"
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# ISO post-build: produce NTSC-J, NTSC-U and PAL disc images
|
||||
if(DUSK_DOLPHIN_BUILD_TYPE STREQUAL "ISO")
|
||||
add_custom_command(TARGET ${DUSK_BINARY_TARGET_NAME} POST_BUILD
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
"${CMAKE_SOURCE_DIR}/tools/makedolphiniso.py"
|
||||
"WII"
|
||||
"${DUSK_BINARY_TARGET_NAME_DOL}"
|
||||
"${DUSK_ASSETS_ZIP}"
|
||||
"${DUSK_GAME_NAME}"
|
||||
"${DUSK_BUILD_DIR}"
|
||||
COMMENT "Building Wii ISO images (NTSC-J, NTSC-U, PAL)"
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,29 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
|
||||
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
|
||||
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
|
||||
set(CMAKE_ASM_COMPILER aarch64-linux-gnu-gcc)
|
||||
|
||||
set(CMAKE_SYSROOT /)
|
||||
set(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu)
|
||||
set(CMAKE_CXX_COMPILER_TARGET aarch64-linux-gnu)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH
|
||||
/usr/aarch64-linux-gnu
|
||||
/usr/lib/aarch64-linux-gnu
|
||||
/usr/include/aarch64-linux-gnu
|
||||
)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
|
||||
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig")
|
||||
set(ENV{PKG_CONFIG_PATH} "/usr/lib/aarch64-linux-gnu/pkgconfig")
|
||||
|
||||
set(CMAKE_PREFIX_PATH "/usr/aarch64-linux-gnu;/usr/lib/aarch64-linux-gnu")
|
||||
|
||||
# Optional: helps some Find modules
|
||||
set(SDL2_DIR "/usr/lib/aarch64-linux-gnu/cmake/SDL2" CACHE PATH "")
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 336 B |
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.10" tiledversion="1.11.1" name="entities" tilewidth="16" tileheight="16" tilecount="64" columns="8">
|
||||
<image source="entities.png" width="128" height="128"/>
|
||||
</tileset>
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"key": "test_event",
|
||||
"items": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "This is a bucket"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Dear God"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"meta": {
|
||||
"language": {
|
||||
"name": "English",
|
||||
"code": "en"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"npc": {
|
||||
"text": "Hello, {{ name }}."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"automappingRulesFile": "",
|
||||
"commands": [
|
||||
],
|
||||
"compatibilityVersion": 1100,
|
||||
"extensionsPath": "extensions",
|
||||
"folders": [
|
||||
"."
|
||||
],
|
||||
"properties": [
|
||||
],
|
||||
"propertyTypes": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "npcInteractType",
|
||||
"storageType": "string",
|
||||
"type": "enum",
|
||||
"values": [
|
||||
"NPC_INTERACT_TYPE_NONE",
|
||||
"NPC_INTERACT_TYPE_TEXT",
|
||||
"NPC_INTERACT_TYPE_CONVO",
|
||||
"NPC_INTERACT_TYPE_EVENT"
|
||||
],
|
||||
"valuesAsFlags": false
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "tileSolidType",
|
||||
"storageType": "string",
|
||||
"type": "enum",
|
||||
"values": [
|
||||
"TILE_SOLID_NONE",
|
||||
"TILE_SOLID_FULL",
|
||||
"TILE_SOLID_TRIANGLE_TOP_RIGHT",
|
||||
"TILE_SOLID_TRIANGLE_TOP_LEFT",
|
||||
"TILE_SOLID_TRIANGLE_BOTTOM_RIGHT",
|
||||
"TILE_SOLID_TRIANGLE_BOTTOM_LEFT"
|
||||
],
|
||||
"valuesAsFlags": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
{
|
||||
"activeFile": "map.tmj",
|
||||
"expandedProjectPaths": [
|
||||
".",
|
||||
"templates"
|
||||
],
|
||||
"fileStates": {
|
||||
":/automap-tiles.tsx": {
|
||||
"scaleInDock": 1
|
||||
},
|
||||
"entities.tsx": {
|
||||
"scaleInDock": 1,
|
||||
"scaleInEditor": 1
|
||||
},
|
||||
"map.tmj": {
|
||||
"scale": 3,
|
||||
"selectedLayer": 2,
|
||||
"viewCenter": {
|
||||
"x": 6603.333333333333,
|
||||
"y": 6846.5
|
||||
}
|
||||
},
|
||||
"minogram.tsx": {
|
||||
"scaleInDock": 1,
|
||||
"scaleInEditor": 16
|
||||
},
|
||||
"overworld.tsx": {
|
||||
"scaleInDock": 1,
|
||||
"scaleInEditor": 4
|
||||
}
|
||||
},
|
||||
"last.externalTilesetPath": "/home/yourwishes/htdocs/dusk/data",
|
||||
"last.imagePath": "/home/yourwishes/htdocs/dusk/data",
|
||||
"last.objectTemplatePath": "/home/yourwishes/htdocs/dusk/data/templates",
|
||||
"openFiles": [
|
||||
"map.tmj",
|
||||
"overworld.tsx"
|
||||
],
|
||||
"project": "map project.tiled-project",
|
||||
"property.type": "string",
|
||||
"recentFiles": [
|
||||
"overworld.tsx",
|
||||
"map.tmj",
|
||||
"minogram.tsx",
|
||||
"entities.tsx"
|
||||
],
|
||||
"tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)",
|
||||
"tileset.lastUsedFormat": "tsx",
|
||||
"tileset.margin": 1,
|
||||
"tileset.spacing": 1,
|
||||
"tileset.tileSize": {
|
||||
"height": 9,
|
||||
"width": 5
|
||||
}
|
||||
}
|
||||
-477
@@ -1,477 +0,0 @@
|
||||
{ "compressionlevel":-1,
|
||||
"height":720,
|
||||
"infinite":true,
|
||||
"layers":[
|
||||
{
|
||||
"chunks":[
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":384,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 18, 19, 20, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 34, 35, 36, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":400,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
19, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":416,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 115, 115, 115, 115, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":432,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":448,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":464,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":384,
|
||||
"y":432
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":400,
|
||||
"y":432
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 49, 49, 49, 49, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 67, 49, 49, 49, 49, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 67, 49, 49, 49, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 67, 49, 49, 49, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 49, 67, 49, 49, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 49, 49, 49, 49, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 49, 49, 49, 67, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 49, 49, 49, 67, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 49, 49, 49, 49, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 49, 49, 49, 49, 49, 49, 49, 49, 49, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":416,
|
||||
"y":432
|
||||
},
|
||||
{
|
||||
"data":[1, 115, 115, 115, 115, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 115, 115, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 115, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
115, 115, 115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
115, 115, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":432,
|
||||
"y":432
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":448,
|
||||
"y":432
|
||||
},
|
||||
{
|
||||
"data":[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":464,
|
||||
"y":432
|
||||
}],
|
||||
"height":32,
|
||||
"id":1,
|
||||
"name":"Base Layer",
|
||||
"opacity":1,
|
||||
"startx":384,
|
||||
"starty":416,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":96,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"chunks":[
|
||||
{
|
||||
"data":[0, 0, 0, 2, 3, 3, 3, 3, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 6, 3, 3, 3, 3, 3, 3, 3,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 22, 35, 35, 35, 35, 35, 35, 35,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 18, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 34, 35, 35, 35, 35, 36, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":400,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 2, 3, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 18, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 18, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 3, 5, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
35, 35, 21, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 18, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 18, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 18, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 18, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 18, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 34, 35, 35, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":416,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
98, 99, 99, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
114, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
114, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
114, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
114, 0, 0, 116, 0, 0, 0, 234, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
114, 0, 0, 0, 99, 99, 99, 100, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
114, 0, 0, 0, 0, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":432,
|
||||
"y":416
|
||||
},
|
||||
{
|
||||
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 50, 51, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 66, 0, 68, 0, 85, 0, 71, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 82, 69, 54, 52, 0, 55, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 39, 0, 66, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 82, 69, 0, 52, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 82, 69, 54, 52, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 71, 0, 0, 0, 0, 82, 69, 0, 52, 0, 0, 0, 0, 130,
|
||||
0, 0, 0, 0, 0, 55, 0, 0, 66, 0, 68, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 84, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":416,
|
||||
"y":432
|
||||
},
|
||||
{
|
||||
"data":[114, 0, 0, 0, 0, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
130, 131, 131, 0, 0, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":16,
|
||||
"width":16,
|
||||
"x":432,
|
||||
"y":432
|
||||
}],
|
||||
"height":32,
|
||||
"id":5,
|
||||
"locked":true,
|
||||
"name":"Overlay Base Layer",
|
||||
"opacity":1,
|
||||
"startx":400,
|
||||
"starty":416,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":48,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"draworder":"topdown",
|
||||
"id":3,
|
||||
"name":"Object Layer 1",
|
||||
"objects":[
|
||||
{
|
||||
"gid":257,
|
||||
"height":16,
|
||||
"id":6,
|
||||
"name":"Player Spawn",
|
||||
"rotation":0,
|
||||
"type":"player_spawn",
|
||||
"visible":true,
|
||||
"width":16,
|
||||
"x":6578.25,
|
||||
"y":6794.25
|
||||
},
|
||||
{
|
||||
"id":13,
|
||||
"properties":[
|
||||
{
|
||||
"name":"interactEvent",
|
||||
"type":"string",
|
||||
"value":"test_event"
|
||||
},
|
||||
{
|
||||
"name":"interactText",
|
||||
"type":"string",
|
||||
"value":"test.npc.text"
|
||||
},
|
||||
{
|
||||
"name":"interactType",
|
||||
"propertytype":"npcInteractType",
|
||||
"type":"string",
|
||||
"value":"NPC_INTERACT_TYPE_EVENT"
|
||||
}],
|
||||
"template":"templates\/NPC.tx",
|
||||
"x":6539.95833333333,
|
||||
"y":6849.66666666666
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":6,
|
||||
"nextobjectid":14,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"1.11.1",
|
||||
"tileheight":16,
|
||||
"tilesets":[
|
||||
{
|
||||
"firstgid":1,
|
||||
"source":"overworld.tsx"
|
||||
},
|
||||
{
|
||||
"firstgid":257,
|
||||
"source":"entities.tsx"
|
||||
},
|
||||
{
|
||||
"firstgid":321,
|
||||
"source":":\/automap-tiles.tsx"
|
||||
}],
|
||||
"tilewidth":16,
|
||||
"type":"map",
|
||||
"version":"1.10",
|
||||
"width":500
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.10" tiledversion="1.11.1" name="minogram" tilewidth="6" tileheight="10" spacing="0" margin="0" tilecount="91" columns="13">
|
||||
<image source="minogram_6x10.png" width="78" height="70"/>
|
||||
</tileset>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.10" tiledversion="1.11.1" name="overworld" tilewidth="16" tileheight="16" tilecount="256" columns="16">
|
||||
<image source="tilemap.png" width="256" height="256"/>
|
||||
<tile id="18">
|
||||
<properties>
|
||||
<property name="solidType" propertytype="tileSolidType" value="TILE_SOLID_NONE"/>
|
||||
</properties>
|
||||
</tile>
|
||||
<tile id="54">
|
||||
<objectgroup draworder="index" id="3">
|
||||
<object id="3" x="5.04743" y="5.92885" width="7.93676" height="6.0751"/>
|
||||
</objectgroup>
|
||||
</tile>
|
||||
<tile id="81">
|
||||
<objectgroup draworder="index" id="2">
|
||||
<object id="1" x="3.18182" y="0.181818">
|
||||
<polygon points="-0.09375,-0.09375 -1.44602,6.42898 6.09091,13 12.9148,13.0057 12.8523,-0.125"/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</tile>
|
||||
</tileset>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
<tileset firstgid="1" source="../entities.tsx"/>
|
||||
<object name="NPC" type="ENTITY_TYPE_NPC" gid="1" width="16" height="16">
|
||||
<properties>
|
||||
<property name="interactText" value=""/>
|
||||
<property name="interactType" propertytype="npcInteractType" value="NPC_INTERACT_TYPE_NONE"/>
|
||||
</properties>
|
||||
</object>
|
||||
</template>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.6 KiB |
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
FROM ghcr.io/extremscorner/libogc2
|
||||
WORKDIR /workdir
|
||||
RUN apt update && \
|
||||
dkp-pacman -Syu --noconfirm && \
|
||||
apt install -y python3 python3-pip python3-polib python3-pil python3-dotenv python3-pyqt5 python3-opengl xorriso && \
|
||||
dkp-pacman -S --needed --noconfirm gamecube-sdl2 ppc-liblzma ppc-libzip libogc2 gamecube-tools ppc-libmad ppc-zlib-ng ppc-liblzma ppc-bzip2 ppc-zstd
|
||||
VOLUME ["/workdir"]
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<app version="1">
|
||||
<name>@DUSK_GAME_NAME@</name>
|
||||
<version>@PROJECT_VERSION@</version>
|
||||
<release_date>@DUSK_BUILD_DATE@</release_date>
|
||||
<coder>@DUSK_GAME_AUTHOR@</coder>
|
||||
<short_description>@DUSK_GAME_SHORT_DESCRIPTION@</short_description>
|
||||
<long_description>@DUSK_GAME_LONG_DESCRIPTION@</long_description>
|
||||
<ahb_access/>
|
||||
</app>
|
||||
@@ -0,0 +1,35 @@
|
||||
FROM debian:trixie
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
WORKDIR /workdir
|
||||
RUN dpkg --add-architecture arm64 && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
crossbuild-essential-arm64 \
|
||||
ca-certificates \
|
||||
pkg-config \
|
||||
cmake \
|
||||
make \
|
||||
ninja-build \
|
||||
git \
|
||||
file \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-polib \
|
||||
python3-pil \
|
||||
python3-dotenv \
|
||||
python3-pyqt5 \
|
||||
python3-opengl \
|
||||
liblua5.4-dev:arm64 \
|
||||
xz-utils:arm64 \
|
||||
libbz2-dev:arm64 \
|
||||
zlib1g-dev:arm64 \
|
||||
libzip-dev:arm64 \
|
||||
libssl-dev:arm64 \
|
||||
libsdl2-dev:arm64 \
|
||||
liblzma-dev:arm64 \
|
||||
libopengl0:arm64 \
|
||||
libgl1:arm64 \
|
||||
libegl1:arm64 \
|
||||
libgles2:arm64 \
|
||||
libgl1-mesa-dev:arm64 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
@@ -0,0 +1,24 @@
|
||||
FROM ubuntu:latest
|
||||
WORKDIR /workdir
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-polib \
|
||||
python3-pil \
|
||||
libsdl2-dev \
|
||||
libgl1-mesa-dev \
|
||||
libzip-dev \
|
||||
python3-dotenv \
|
||||
python3-pyqt5 \
|
||||
python3-opengl \
|
||||
xz-utils \
|
||||
liblzma-dev \
|
||||
libbz2-dev \
|
||||
zlib1g-dev \
|
||||
libzip-dev \
|
||||
libbz2-dev \
|
||||
git \
|
||||
libssl-dev
|
||||
@@ -0,0 +1,7 @@
|
||||
FROM pspdev/pspdev:latest
|
||||
WORKDIR /workdir
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
py3-pip \
|
||||
py3-dotenv
|
||||
VOLUME ["/workdir"]
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM vitasdk/vitasdk:latest
|
||||
WORKDIR /workdir
|
||||
|
||||
# Install vitaGL and its dependencies (vitashark, SceShaccCg) via vdpm
|
||||
RUN which vdpm
|
||||
|
||||
# Install Python (needed for Dusk code generation tools)
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
py3-pip \
|
||||
py3-dotenv
|
||||
|
||||
VOLUME ["/workdir"]
|
||||
@@ -0,0 +1,379 @@
|
||||
# Scripting
|
||||
|
||||
Dusk embeds [JerryScript](https://github.com/jerryscript-project/jerryscript) to
|
||||
drive gameplay logic from JavaScript. The engine itself (rendering, physics,
|
||||
asset loading, entity storage) is all C; scripts sit on top and manipulate that
|
||||
state through a small set of bound objects.
|
||||
|
||||
This document covers the JS-facing scripting API. **UI (buttons, sliders,
|
||||
menus, etc.) is not exposed to scripts** — it's a separate, C-only API. See
|
||||
[UI.md](UI.md) if you're building screens/menus from engine/game C code.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
On startup, `engineInit()` loads and evaluates `assets/engine.js` as the main
|
||||
script, then calls the global `init()` function if one is defined. From then
|
||||
on, every engine tick calls (in order):
|
||||
|
||||
1. `fixedUpdate()` — once per fixed timestep. Use this for gameplay logic that
|
||||
must be deterministic and independent of display refresh rate (movement,
|
||||
physics-adjacent input handling, etc). Skipped on interpolation/dynamic
|
||||
frames when the build has variable-timestep rendering enabled.
|
||||
2. `update()` — once per rendered frame, including interpolation frames. Use
|
||||
this for smooth, purely presentational animation (nothing that needs to be
|
||||
deterministic).
|
||||
|
||||
On shutdown, `deinit()` is called once.
|
||||
|
||||
All four hooks (`init`, `update`, `fixedUpdate`, `deinit`) are **optional** —
|
||||
if a script doesn't define one, the engine simply skips it, no error.
|
||||
|
||||
```js
|
||||
function update() {
|
||||
cubePosition.rotation.y += TIME.delta * 1.5;
|
||||
}
|
||||
```
|
||||
|
||||
### `async init()` and `include()`
|
||||
|
||||
`init` (or any of the other hooks) can be declared `async` and use `await`
|
||||
freely, including awaiting `include()` (see below), even though the engine
|
||||
calls these functions synchronously from C with no external JS event loop.
|
||||
When a hook returns a pending `Promise`, the engine keeps driving the asset
|
||||
system and JerryScript's job queue until that promise settles before
|
||||
continuing — so by the time e.g. `init()` "returns" from the engine's point of
|
||||
view, everything it awaited has actually finished.
|
||||
|
||||
```js
|
||||
async function init() {
|
||||
Actions = await include("input.js");
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
If the awaited work throws/rejects, it surfaces as a C-level error.
|
||||
|
||||
## Loading other scripts: `include(path)`
|
||||
|
||||
```js
|
||||
var Actions = await include("input.js");
|
||||
```
|
||||
|
||||
`include(path)` always returns a `Promise`. The named file is loaded and
|
||||
evaluated once no matter how many times (or from how many different scripts)
|
||||
you `include()` it — later calls for the same path are handed the same
|
||||
in-flight/resolved promise rather than re-running the file.
|
||||
|
||||
The included script communicates its result back by assigning to the bare
|
||||
global `module`:
|
||||
|
||||
```js
|
||||
// input.js
|
||||
Input.bind("w", INPUT_ACTION_UP);
|
||||
// ...
|
||||
|
||||
module = {
|
||||
UP: INPUT_ACTION_UP,
|
||||
DOWN: INPUT_ACTION_DOWN,
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Whatever `input.js` assigns to `module` becomes the resolved value of the
|
||||
promise `include("input.js")` returned — that's what `Actions` ends up being
|
||||
in the example above. If the included script throws, the promise rejects
|
||||
instead.
|
||||
|
||||
## Full example
|
||||
|
||||
This is the actual shipped example content (`assets/engine.js` +
|
||||
`assets/input.js`):
|
||||
|
||||
```js
|
||||
// input.js — binds physical buttons to abstract actions, then exports the
|
||||
// action constants so other scripts don't need to know raw INPUT_ACTION_* names.
|
||||
Input.bind("w", INPUT_ACTION_UP);
|
||||
Input.bind("s", INPUT_ACTION_DOWN);
|
||||
Input.bind("a", INPUT_ACTION_LEFT);
|
||||
Input.bind("d", INPUT_ACTION_RIGHT);
|
||||
Input.bind("space", INPUT_ACTION_ACCEPT);
|
||||
Input.bind("escape", INPUT_ACTION_RAGEQUIT);
|
||||
|
||||
if(typeof INPUT_GAMEPAD !== "undefined") {
|
||||
Input.bind("gamepad_up", INPUT_ACTION_UP);
|
||||
Input.bind("gamepad_down", INPUT_ACTION_DOWN);
|
||||
Input.bind("gamepad_left", INPUT_ACTION_LEFT);
|
||||
Input.bind("gamepad_right", INPUT_ACTION_RIGHT);
|
||||
Input.bind("gamepad_a", INPUT_ACTION_ACCEPT);
|
||||
Input.bind("gamepad_start", INPUT_ACTION_RAGEQUIT);
|
||||
}
|
||||
|
||||
module = {
|
||||
UP: INPUT_ACTION_UP,
|
||||
DOWN: INPUT_ACTION_DOWN,
|
||||
LEFT: INPUT_ACTION_LEFT,
|
||||
RIGHT: INPUT_ACTION_RIGHT,
|
||||
ACCEPT: INPUT_ACTION_ACCEPT,
|
||||
CANCEL: INPUT_ACTION_CANCEL,
|
||||
RAGEQUIT: INPUT_ACTION_RAGEQUIT
|
||||
};
|
||||
```
|
||||
|
||||
```js
|
||||
// engine.js
|
||||
var Actions;
|
||||
var camera, cameraPosition;
|
||||
var cube, cubePosition, cubeRenderable, cubeMesh;
|
||||
|
||||
async function init() {
|
||||
Actions = await include("input.js");
|
||||
|
||||
camera = new Entity();
|
||||
cameraPosition = camera.add(POSITION);
|
||||
camera.add(CAMERA);
|
||||
cameraPosition.position = new Vec3(3, 3, -6);
|
||||
cameraPosition.lookAt(new Vec3(0, 0, 0));
|
||||
|
||||
cube = new Entity();
|
||||
cubePosition = cube.add(POSITION);
|
||||
cubeRenderable = cube.add(RENDERABLE);
|
||||
cubeMesh = Mesh.createCube();
|
||||
cubeRenderable.mesh = cubeMesh;
|
||||
cubeRenderable.color = Color.red();
|
||||
}
|
||||
|
||||
function update() {
|
||||
cubePosition.rotation.y += TIME.delta * 1.5;
|
||||
cubePosition.rotation.x += TIME.delta * 0.7;
|
||||
}
|
||||
|
||||
function fixedUpdate() {
|
||||
var move = 3.0 * TIME.delta;
|
||||
if(Input.isDown(Actions.LEFT)) cubePosition.position.x -= move;
|
||||
if(Input.isDown(Actions.RIGHT)) cubePosition.position.x += move;
|
||||
if(Input.isDown(Actions.UP)) cubePosition.position.z += move;
|
||||
if(Input.isDown(Actions.DOWN)) cubePosition.position.z -= move;
|
||||
if(Input.pressed(Actions.ACCEPT)) cubePosition.position = new Vec3(0, 0, 0);
|
||||
}
|
||||
|
||||
function deinit() {
|
||||
cube.dispose();
|
||||
camera.dispose();
|
||||
}
|
||||
```
|
||||
|
||||
## API reference
|
||||
|
||||
### `TIME`
|
||||
|
||||
Plain global object, live getters (read fresh engine state every access, not
|
||||
snapshotted):
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `TIME.delta` | number | Seconds since the last frame. |
|
||||
| `TIME.time` | number | Total elapsed engine time, in seconds. |
|
||||
|
||||
### `PLATFORM`
|
||||
|
||||
A single global string constant — the compile-time target name, e.g.
|
||||
`"linux"`, `"psp"`, `"vita"`, `"dolphin"`. Individual platform builds may
|
||||
inject additional platform-specific globals via their own
|
||||
`modulePlatformPlatform()` hook; those aren't documented here since they vary
|
||||
per target.
|
||||
|
||||
### `Input`
|
||||
|
||||
Static namespace (not constructible — there's no `new Input()`).
|
||||
|
||||
| Method | Description |
|
||||
|---|---|
|
||||
| `Input.bind(buttonName, action)` | Binds a physical button/key (string, e.g. `"w"`, `"space"`, `"gamepad_up"`) to an abstract `INPUT_ACTION_*` constant. Many buttons can bind to the same action. Throws on an empty/unrecognized button name or invalid action. |
|
||||
| `Input.isDown(action)` → boolean | Is the action currently held. |
|
||||
| `Input.pressed(action)` → boolean | Action transitioned to down this frame. |
|
||||
| `Input.released(action)` → boolean | Action transitioned to up this frame. |
|
||||
| `Input.getValue(action)` → number | Current analog value for the action. |
|
||||
| `Input.axis(negAction, posAction)` → number | Combined axis value from two opposing actions. |
|
||||
| `Input.axis2D(negX, posX, negY, posY)` → `Vec2` | Combined 2D axis from four actions. |
|
||||
|
||||
Global `INPUT_ACTION_*` constants (names are stable API; treat the numeric
|
||||
values as opaque/build-specific): `INPUT_ACTION_UP`, `INPUT_ACTION_DOWN`,
|
||||
`INPUT_ACTION_LEFT`, `INPUT_ACTION_RIGHT`, `INPUT_ACTION_ACCEPT`,
|
||||
`INPUT_ACTION_CANCEL`, `INPUT_ACTION_PAUSE`, `INPUT_ACTION_RAGEQUIT`,
|
||||
`INPUT_ACTION_CONSOLE`, `INPUT_ACTION_POINTERX`, `INPUT_ACTION_POINTERY`.
|
||||
|
||||
Conditionally-defined boolean globals reflecting build capability — only
|
||||
present at all if the corresponding input method is compiled in, so
|
||||
feature-test with `typeof`, don't assume they exist:
|
||||
`INPUT_KEYBOARD`, `INPUT_GAMEPAD`, `INPUT_POINTER`, `INPUT_TOUCH`.
|
||||
|
||||
### `Vec2` / `Vec3` / `Vec4`
|
||||
|
||||
`new Vec2(x?, y?)`, `new Vec3(x?, y?, z?)`, `new Vec4(x?, y?, z?, w?)` — all
|
||||
components optional, default `0`.
|
||||
|
||||
Common instance surface across all three: `.dot(other)`, `.length()`,
|
||||
`.lengthSq()`, `.normalize()`, `.negate()`, `.add(other)`, `.sub(other)`,
|
||||
`.scale(n)`, `.lerp(other, t)` — each of `add`/`sub`/`scale`/`negate`/
|
||||
`normalize`/`lerp` returns a **new** vector (non-mutating). `Vec3` additionally
|
||||
has `.cross(other)` and `.distance(other)`; `Vec2` has `.distance(other)` too;
|
||||
`Vec4` has neither `.cross()` nor `.distance()`.
|
||||
|
||||
`Vec4` also has UV aliases over the same four floats: `.u0` (= `.x`), `.v0`
|
||||
(= `.y`), `.u1` (= `.z`), `.v1` (= `.w`) — handy for texture-rect style code.
|
||||
|
||||
All three have `.x`/`.y`(/`.z`/`.w`) get/set properties and a `.toString()`
|
||||
like `"Vec3(1, 2, 3)"`.
|
||||
|
||||
**"Vec3Ref" — live references.** Several engine properties (entity
|
||||
`position`/`rotation`/`scale`, physics `velocity`, a mesh vertex's `position`)
|
||||
return a vector-*like* object instead of a plain `Vec3`. It has the identical
|
||||
`.x`/`.y`/`.z` surface, but reads/writes go straight into the underlying
|
||||
native buffer — writing `.x` on `entity.position.position` immediately moves
|
||||
the entity, no separate assignment needed. Anywhere the API expects a `Vec3`
|
||||
argument, a Vec3Ref works too. You never construct one directly; you only
|
||||
ever receive them from properties like the ones above.
|
||||
|
||||
### `Mat4`
|
||||
|
||||
`new Mat4()` — always constructs identity; no other constructor form.
|
||||
|
||||
| Member | Description |
|
||||
|---|---|
|
||||
| `.mul(other)` → `Mat4` | `this * other`. |
|
||||
| `.transpose()` → `Mat4` | |
|
||||
| `.inverse()` → `Mat4` | |
|
||||
| `.determinant()` → number | |
|
||||
| `.mulVec3(vec3, w?)` → `Vec3` | `w` defaults to `1.0` (point); pass `0` for a direction. |
|
||||
| `.mulVec4(vec4)` → `Vec4` | |
|
||||
| `.translate(vec3)` → `Mat4` | Non-mutating — returns a translated copy. |
|
||||
| `.scale(vec3)` → `Mat4` | Non-mutating — returns a scaled copy. |
|
||||
| `Mat4.identity()` → `Mat4` | Static. |
|
||||
| `Mat4.perspective(fov, aspect, near, far)` → `Mat4` | Static, all 4 args required. |
|
||||
| `Mat4.lookAt(eye, center, up)` → `Mat4` | Static, all 3 args required `Vec3`s. |
|
||||
|
||||
### `Color`
|
||||
|
||||
`new Color(r?, g?, b?, a?)` — each an int `0..255`, default `255` (so
|
||||
`new Color()` is opaque white). Properties `.r`/`.g`/`.b`/`.a` get/set.
|
||||
|
||||
Named factories, each a zero-arg static returning a new opaque `Color`
|
||||
(alpha `255` unless noted): `Color.black()`, `Color.white()`, `Color.red()`,
|
||||
`Color.green()`, `Color.blue()`, `Color.yellow()`, `Color.cyan()`,
|
||||
`Color.magenta()`, `Color.transparent()` (alpha 0), `Color.transparent_white()`
|
||||
(alpha 0), `Color.transparent_black()` (alpha 0), `Color.gray()`,
|
||||
`Color.light_gray()`, `Color.dark_gray()`, `Color.orange()`, `Color.purple()`,
|
||||
`Color.brown()`, `Color.pink()`, `Color.lime()`, `Color.navy()`,
|
||||
`Color.teal()`, `Color.cornflower_blue()`.
|
||||
|
||||
`Color.rainbow(t?, speed?)` → `Color` — `t` defaults to `TIME.time * 4.0`;
|
||||
produces a shifting rainbow color, useful for debug visuals.
|
||||
|
||||
### `Mesh`
|
||||
|
||||
`new Mesh(vertexCount)` — allocates an uninitialized CPU-side vertex buffer
|
||||
(not yet uploaded to the GPU).
|
||||
|
||||
| Member | Description |
|
||||
|---|---|
|
||||
| `.vertices` | Array of vertex wrappers, each with a `.position` (Vec3Ref, writes straight into that vertex). |
|
||||
| `.vertexCount` | Read-only. |
|
||||
| `.flush()` | Uploads to the GPU. First call initializes the GPU mesh; later calls re-upload the current vertex data — call this after editing `.vertices[i].position`. |
|
||||
| `.dispose()` | Frees GPU + CPU resources. |
|
||||
|
||||
Static engine-owned singletons (read-only, not something you dispose):
|
||||
`Mesh.DEFAULT_CUBE`, `Mesh.DEFAULT_QUAD`, `Mesh.DEFAULT_SPHERE`,
|
||||
`Mesh.DEFAULT_PLANE`, `Mesh.DEFAULT_CAPSULE`, `Mesh.DEFAULT_TRIPRISM`.
|
||||
|
||||
Static factories (each builds and uploads a brand-new `Mesh`):
|
||||
|
||||
| Factory | Notes |
|
||||
|---|---|
|
||||
| `Mesh.createCube(min?, max?)` | Both `Vec3`, default `(-0.5,-0.5,-0.5)`..`(0.5,0.5,0.5)`. |
|
||||
| `Mesh.createQuad(minX?, minY?, maxX?, maxY?)` | Default `-0.5..0.5` both axes; UV fixed `0,0`–`1,1`. |
|
||||
| `Mesh.createSphere(radius?, stacks?, sectors?)` | `radius` default `0.5`. |
|
||||
| `Mesh.createPlane(width?, height?)` | Defaults `1.0`/`1.0`; XZ-aligned, centered at origin. |
|
||||
| `Mesh.createCapsule(radius?, halfHeight?, capRings?, sectors?)` | Defaults `0.5`, `0.5`. |
|
||||
| `Mesh.createTriPrism(x0, y0, x1, y1, x2, y2, minZ, maxZ)` | All 8 args required — a triangular cross-section extruded along Z. |
|
||||
|
||||
### `Entity` and components
|
||||
|
||||
```js
|
||||
var e = new Entity();
|
||||
var pos = e.add(POSITION);
|
||||
```
|
||||
|
||||
`new Entity()` allocates an entity. `.id` is the read-only numeric engine ID.
|
||||
`.add(TYPE)` adds a component and returns its wrapper (`TYPE` is one of the
|
||||
constants below). `.dispose()` removes the entity and all its components.
|
||||
|
||||
Component-type constants: `POSITION`, `CAMERA`, `RENDERABLE`, `PHYSICS`,
|
||||
`TRIGGER`. Each entity also exposes a lowercase getter that returns the
|
||||
existing wrapper if the component is present, or `undefined` if not (it does
|
||||
**not** add the component — use `.add()` for that): `entity.position`,
|
||||
`entity.camera`, `entity.renderable`, `entity.physics`, `entity.trigger`.
|
||||
|
||||
#### `entity.add(POSITION)` → position component
|
||||
|
||||
| Member | Description |
|
||||
|---|---|
|
||||
| `.position` | Vec3Ref. Writing rebuilds the transform automatically. |
|
||||
| `.rotation` | Vec3Ref, Euler angles. Same rebuild-on-write behavior. |
|
||||
| `.scale` | Vec3Ref. Same rebuild-on-write behavior. |
|
||||
| `.parent` | Get/set another position-component wrapper, or `null` to clear parenting. |
|
||||
| `.lookAt(target, up?)` | `target` a `Vec3`; `up` defaults to `(0,1,0)`. |
|
||||
|
||||
#### `entity.add(CAMERA)` → camera component
|
||||
|
||||
| Member | Description |
|
||||
|---|---|
|
||||
| `.zNear` / `.zFar` | Numbers. |
|
||||
| `.fov` | Only meaningful when `projectionType` is `CAMERA_TYPE_PERSPECTIVE`; otherwise get returns `undefined` and set is a no-op. |
|
||||
| `.projectionType` | `CAMERA_TYPE_PERSPECTIVE` or `CAMERA_TYPE_ORTHOGRAPHIC`. |
|
||||
| `.orthoTop` / `.orthoBottom` / `.orthoLeft` / `.orthoRight` | Only meaningful in orthographic mode, same undefined/no-op rule otherwise. |
|
||||
|
||||
#### `entity.add(RENDERABLE)` → renderable component
|
||||
|
||||
| Member | Description |
|
||||
|---|---|
|
||||
| `.type` | `ENTITY_RENDERABLE_TYPE_MATERIAL`, `_SPRITEBATCH`, or `_CALLBACK`. |
|
||||
| `.mesh` | Get/set a `Mesh` instance or a `Mesh.DEFAULT_*` singleton. |
|
||||
| `.color` | Get/set a `Color` instance (throws if given something else). |
|
||||
| `.addSprite({ min?, max?, uvMin?, uvMax? })` | Adds a sprite to this renderable's sprite batch; all fields optional, default zero. |
|
||||
| `.clearSprites()` | Clears the sprite batch. |
|
||||
| `.setCallback(fn?)` | Switches to `ENTITY_RENDERABLE_TYPE_CALLBACK` and calls `fn()` on every render of this entity. Omit/pass non-function to clear. Exceptions inside `fn` surface as a C error. |
|
||||
|
||||
#### `entity.add(PHYSICS)` → physics component
|
||||
|
||||
| Member | Description |
|
||||
|---|---|
|
||||
| `.velocity` | Vec3Ref, plain (no rebuild-on-write). |
|
||||
| `.onGround` | Read-only boolean. |
|
||||
| `.bodyType` | `PHYSICS_BODY_STATIC`, `PHYSICS_BODY_DYNAMIC`, `PHYSICS_BODY_KINEMATIC`. |
|
||||
| `.applyImpulse(vec3)` | Adds to velocity. No-op on static bodies. |
|
||||
| `.setShapeCube(halfExtents)` | `halfExtents` a `Vec3`. |
|
||||
| `.setShapeSphere(radius)` | Number. |
|
||||
| `.setShapeCapsule(radius, halfHeight)` | Two numbers. |
|
||||
| `.setShapePlane(normal, distance)` | `Vec3` + number. |
|
||||
|
||||
Shape-type constants (for reading `.type` on the underlying shape, not for
|
||||
`.bodyType`): `PHYSICS_SHAPE_CUBE`, `PHYSICS_SHAPE_SPHERE`,
|
||||
`PHYSICS_SHAPE_CAPSULE`, `PHYSICS_SHAPE_PLANE`.
|
||||
|
||||
#### `entity.add(TRIGGER)` → trigger component
|
||||
|
||||
| Member | Description |
|
||||
|---|---|
|
||||
| `.min` / `.max` | Plain `Vec3` values (copies, not live refs). |
|
||||
| `.setBounds(min, max)` | Sets both at once. |
|
||||
| `.contains(point)` → boolean | `point` a `Vec3`. |
|
||||
|
||||
## Not yet available to scripts
|
||||
|
||||
The following C modules exist and are fully implemented, but aren't currently
|
||||
wired into script registration (`moduleRegister()` in
|
||||
`src/dusk/script/module/module.h`), so none of these globals exist in a
|
||||
script today: `Screen`, `SpriteBatch`, `Text`, `Scene`, `Easing`, `Console`,
|
||||
`Engine`. If you need one of these from a script, it needs to be registered
|
||||
in `moduleRegister()` first — see the existing entries there and the modules
|
||||
under `src/dusk/script/module/` for the pattern to follow.
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
# UI
|
||||
|
||||
Dusk's UI system (buttons, checkboxes, sliders, dropdowns, tabs, menus, focus
|
||||
navigation) is a **C-only API**. It is not exposed to JerryScript — see
|
||||
[SCRIPTING.md](SCRIPTING.md) for what scripts *can* touch. If you need a
|
||||
script to open/react to a menu, wire it through a C callback or a game-side
|
||||
flag scripts can poll; there's no bridge for this today.
|
||||
|
||||
## Mental model
|
||||
|
||||
There is no retained-mode UI tree, no automatic dispatch, no scissor/clip-rect
|
||||
API. Every widget is a plain struct you own (usually as a global or
|
||||
scene-owned variable). You call its `xxxInit(...)` once, then call its
|
||||
`xxxDraw(widget, x, y)` yourself, every frame you want it visible, at whatever
|
||||
screen position you choose. Nothing draws itself automatically except three
|
||||
fixed system overlays (overscan bars, debug console, FPS counter) — see
|
||||
[System overlays](#system-overlays-automatic) below.
|
||||
|
||||
### Where UI rendering happens in the frame
|
||||
|
||||
- `uiInit()` / `uiDispose()` run once, at engine startup/shutdown.
|
||||
- `uiUpdate()` runs once per tick (drives focus-navigation input handling).
|
||||
- `uiRender()` runs once per frame, called from inside `sceneRender()` — i.e.
|
||||
**after** the active scene's own 3D/game-world rendering, using an
|
||||
orthographic screen-space projection. Your own widget `xxxDraw()` calls
|
||||
should happen around the same point — typically from your scene's render
|
||||
callback, after world content, so UI draws on top.
|
||||
|
||||
## Widgets
|
||||
|
||||
Every widget follows the same shape: `xxxInit(widget, ...)` zeroes the struct
|
||||
and sets its fields; `xxxDraw(const widget*, x, y) -> errorret_t` draws it at
|
||||
that screen position.
|
||||
|
||||
> **Init before Draw.** `uislider_t`, `uidropdown_t`, and `uitab_t` cache
|
||||
> their label's measured width/height at `Init` time (an optimization —
|
||||
> label text doesn't change after that point). Calling `Draw` before `Init`,
|
||||
> or mutating `->label` directly instead of re-initializing, leaves stale
|
||||
> layout. `uibutton_t`/`uicheckbox_t` don't have this restriction.
|
||||
|
||||
### Button
|
||||
|
||||
```c
|
||||
void uiButtonInit(uibutton_t *button, const char_t *label);
|
||||
bool_t uiButtonIsHighlighted(const uibutton_t *button);
|
||||
void uiButtonSetHighlighted(uibutton_t *button, bool_t highlighted);
|
||||
errorret_t uiButtonDraw(const uibutton_t *button, float_t x, float_t y);
|
||||
```
|
||||
|
||||
Draws `label` in red when highlighted, white otherwise.
|
||||
|
||||
### Checkbox
|
||||
|
||||
```c
|
||||
void uiCheckboxInit(uicheckbox_t *checkbox, const char_t *label);
|
||||
bool_t uiCheckboxIsChecked(const uicheckbox_t *checkbox);
|
||||
void uiCheckboxSetChecked(uicheckbox_t *checkbox, bool_t checked);
|
||||
void uiCheckboxToggle(uicheckbox_t *checkbox);
|
||||
bool_t uiCheckboxIsHighlighted(const uicheckbox_t *checkbox);
|
||||
void uiCheckboxSetHighlighted(uicheckbox_t *checkbox, bool_t highlighted);
|
||||
errorret_t uiCheckboxDraw(const uicheckbox_t *checkbox, float_t x, float_t y);
|
||||
```
|
||||
|
||||
Draws `"Y "`/`"N "` then the label.
|
||||
|
||||
### Slider
|
||||
|
||||
```c
|
||||
typedef union { float_t f; int32_t i; } uislidervalue_t;
|
||||
|
||||
void uiSliderInitFloat(uislider_t*, const char_t *label,
|
||||
float_t value, float_t min, float_t max, float_t step);
|
||||
void uiSliderInitInt(uislider_t*, const char_t *label,
|
||||
int32_t value, int32_t min, int32_t max, int32_t step);
|
||||
float_t uiSliderGetFloat(const uislider_t*); // works for either type
|
||||
int32_t uiSliderGetInt(const uislider_t*); // asserts type == INT
|
||||
void uiSliderSetFloat(uislider_t*, float_t value); // asserts type == FLOAT, clamps
|
||||
void uiSliderSetInt(uislider_t*, int32_t value); // asserts type == INT, clamps
|
||||
void uiSliderStepUp(uislider_t*); // wraps to min past max
|
||||
void uiSliderStepDown(uislider_t*); // wraps to max past min
|
||||
float_t uiSliderGetRatio(const uislider_t*); // normalized 0..1
|
||||
int32_t uiSliderGetStepCount(const uislider_t*); // 0 for float sliders
|
||||
bool_t uiSliderIsHighlighted(const uislider_t*);
|
||||
void uiSliderSetHighlighted(uislider_t*, bool_t highlighted);
|
||||
errorret_t uiSliderDraw(const uislider_t*, float_t x, float_t y);
|
||||
```
|
||||
|
||||
Draws label, a track, a fill proportional to the current ratio, discrete step
|
||||
markers if it's an int slider with fewer than 10 steps, then the value as
|
||||
text.
|
||||
|
||||
### Dropdown
|
||||
|
||||
```c
|
||||
void uiDropdownInit(uidropdown_t *dropdown, const char_t *label,
|
||||
const char_t *const *options, uint8_t optionCount,
|
||||
uint8_t selectedIndex);
|
||||
uint8_t uiDropdownGetSelectedIndex(const uidropdown_t *dropdown);
|
||||
const char_t *uiDropdownGetSelectedOption(const uidropdown_t *dropdown);
|
||||
void uiDropdownSetSelectedIndex(uidropdown_t *dropdown, uint8_t index);
|
||||
void uiDropdownStepNext(uidropdown_t *dropdown); // wraps
|
||||
void uiDropdownStepPrev(uidropdown_t *dropdown); // wraps
|
||||
bool_t uiDropdownIsHighlighted(const uidropdown_t *dropdown);
|
||||
void uiDropdownSetHighlighted(uidropdown_t *dropdown, bool_t highlighted);
|
||||
errorret_t uiDropdownDraw(const uidropdown_t *dropdown, float_t x, float_t y);
|
||||
```
|
||||
|
||||
`options` is a caller-owned array of strings that must outlive the dropdown
|
||||
(it isn't copied). Draws `label` then `"< Option >"`.
|
||||
|
||||
### Tab
|
||||
|
||||
```c
|
||||
void uiTabInit(uitab_t *tab, const char_t *label);
|
||||
bool_t uiTabIsActive(const uitab_t *tab);
|
||||
void uiTabSetActive(uitab_t *tab, bool_t active);
|
||||
errorret_t uiTabDraw(const uitab_t *tab, float_t x, float_t y);
|
||||
```
|
||||
|
||||
Draws a background box sized to the label (green if active, red if inactive)
|
||||
with the label on top.
|
||||
|
||||
## Menus: assembling widgets into a navigable list
|
||||
|
||||
`uimenu_t` is the one aggregate widget — it owns an array of items (labels,
|
||||
spacers, and any of the widgets above), lays them out in a grid, and wires
|
||||
keyboard/gamepad navigation via the focus system for you.
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
UI_MENU_WIDGET_TYPE_NONE, UI_MENU_WIDGET_TYPE_LABEL,
|
||||
UI_MENU_WIDGET_TYPE_SPACER, UI_MENU_WIDGET_TYPE_CHECKBOX,
|
||||
UI_MENU_WIDGET_TYPE_BUTTON, UI_MENU_WIDGET_TYPE_TAB,
|
||||
UI_MENU_WIDGET_TYPE_SLIDER, UI_MENU_WIDGET_TYPE_DROPDOWN,
|
||||
} uimenuwidgettype_t;
|
||||
|
||||
void uiMenuInit(uimenu_t *menu, uimenuselectedcallback_t selected,
|
||||
uimenuclosedcallback_t closed, uimenuchangedcallback_t changed);
|
||||
void uiMenuSetItems(uimenu_t *menu, const uimenuitem_t *items,
|
||||
uint8_t itemCount, uint8_t columns);
|
||||
void uiMenuSetPosition(uimenu_t *menu, uint8_t x, uint8_t y); // focus cursor cell, not pixels
|
||||
void uiMenuOpen(uimenu_t *menu); // pushes onto the focus stack
|
||||
void uiMenuClose(uimenu_t *menu); // pops it
|
||||
bool_t uiMenuIsActive(const uimenu_t *menu);
|
||||
errorret_t uiMenuDraw(const uimenu_t *menu, float_t x, float_t y,
|
||||
float_t width, float_t height);
|
||||
```
|
||||
|
||||
- `selected(menu, index, item)` fires when the player presses accept on an item.
|
||||
- `changed(menu, index, item)` fires when the highlighted item changes.
|
||||
- `closed(menu)` fires when the menu is popped off the focus stack.
|
||||
- LEFT/RIGHT on a highlighted slider/checkbox/dropdown adjusts its value in
|
||||
place instead of moving focus off it (handled internally).
|
||||
|
||||
### Building a menu with the `MENU_*` macros
|
||||
|
||||
`uimenu.h` provides macros that cut the boilerplate of filling in a
|
||||
`uimenuitem_t` array. They expand into statements using local variables named
|
||||
`menu`, `menuIndex`, and `menuCapacity`, so use them together, inside one
|
||||
function, starting with `MENU_BEGIN` and ending with `MENU_END`:
|
||||
|
||||
```c
|
||||
static uimenuitem_t optionsItems[8];
|
||||
static uimenu_t optionsMenu;
|
||||
static const char_t *qualityOptions[] = { "Low", "Medium", "High" };
|
||||
|
||||
static void onOptionsSelected(
|
||||
const uimenu_t *menu, const uint8_t index, const uimenuitem_t *item
|
||||
) {
|
||||
if(index == 4) uiMenuClose(&optionsMenu); // "Back" button
|
||||
}
|
||||
|
||||
static void onOptionsClosed(const uimenu_t *menu) {
|
||||
// e.g. return to the previous screen
|
||||
}
|
||||
|
||||
void optionsMenuBuild(void) {
|
||||
MENU_BEGIN(&optionsMenu, optionsItems, onOptionsSelected, onOptionsClosed, NULL);
|
||||
MENU_LABEL("Options");
|
||||
MENU_CHECKBOX("Fullscreen");
|
||||
MENU_SLIDER_FLOAT("Volume", 0.8f, 0.0f, 1.0f, 0.05f);
|
||||
MENU_DROPDOWN("Quality", qualityOptions, 3, 1);
|
||||
MENU_BUTTON("Back");
|
||||
MENU_END(optionsItems, 1);
|
||||
}
|
||||
|
||||
// Once, when the menu screen becomes active:
|
||||
uiMenuOpen(&optionsMenu);
|
||||
|
||||
// Every frame the menu should be visible:
|
||||
uiMenuDraw(&optionsMenu, 20.0f, 20.0f, 200.0f, 100.0f);
|
||||
|
||||
// When leaving the menu screen:
|
||||
uiMenuClose(&optionsMenu);
|
||||
```
|
||||
|
||||
`MENU_LABEL`/`MENU_SPACER` force a row break and aren't focusable/selectable.
|
||||
Every other `MENU_*` macro calls the matching widget's own `Init` for you.
|
||||
|
||||
> This example is constructed directly from the widget/menu API surface (all
|
||||
> function and macro signatures above are verified against the source), but
|
||||
> there's currently no real menu-building call site anywhere else in the
|
||||
> engine to cross-check the *pattern* against — treat it as a starting point,
|
||||
> not a copy of shipped code.
|
||||
|
||||
## Focus system: navigation underneath `uimenu`
|
||||
|
||||
If you're building a custom widget that needs keyboard/gamepad navigation
|
||||
without going through `uimenu`, use `ui/focus/uifocus.h` directly. `uimenu`
|
||||
is implemented entirely in terms of this API, so it's a reasonable reference.
|
||||
|
||||
```c
|
||||
uifocusitem_t * uiFocusPush(
|
||||
uint8_t cols, uint8_t rows,
|
||||
uifocusitemcallback_t selected, // fires on accept
|
||||
uifocusitemcallback_t changed, // fires on cursor move (and once immediately)
|
||||
uifocusitemcallback_t closed, // fires on pop
|
||||
uifocusitemdirectioncallback_t direction, // optional pre-empt of a direction press; NULL for default grid movement
|
||||
void *user
|
||||
);
|
||||
void uiFocusPop(void);
|
||||
void uiFocusPopItem(uifocusitem_t *item);
|
||||
void uiFocusSetPosition(uifocusitem_t *item, uint8_t x, uint8_t y); // wraps
|
||||
void uiFocusMoveDirection(uifocusitem_t *item, uifocusdirection_t dir);
|
||||
```
|
||||
|
||||
`uiFocusUpdate()` runs automatically from `uiUpdate()` every tick — you don't
|
||||
call it yourself. It reads `INPUT_ACTION_ACCEPT` (fires `selected`),
|
||||
`INPUT_ACTION_CANCEL` (pops the stack), and the four directional actions
|
||||
(with hold-to-repeat timing) to move the cursor within the topmost pushed
|
||||
item. Only the topmost stack entry (max depth 8) receives input at a time —
|
||||
opening a submenu means pushing a new focus item on top; closing it pops back
|
||||
to the parent.
|
||||
|
||||
There's no separate "is this widget focused" query — "focused" is expressed
|
||||
as the pushed item's current `(x, y)` cursor cell matching a given slot, which
|
||||
is exactly how `uimenu`'s `changed` callback decides which item to highlight.
|
||||
|
||||
## System overlays (automatic)
|
||||
|
||||
Three small overlays are wired into a fixed internal list and draw themselves
|
||||
every frame with no call needed from game code:
|
||||
|
||||
- **Overscan bars** (`ui/overlay/uicrop.h`) — draws opaque bars over the
|
||||
screen area outside `SCREEN.scanX/scanY/scanWidth/scanHeight` (the
|
||||
overscan-safe viewport). A no-op on platforms/configs where the scan area
|
||||
already equals the full viewport. `UI_CROP.color` (default black) is the
|
||||
only thing you'd normally touch here.
|
||||
- **Debug console** (`ui/debug/uiconsole.h`) — draws console history when
|
||||
visible.
|
||||
- **FPS counter** (`ui/debug/uifps.h`) — draws a live FPS/frame-time readout.
|
||||
|
||||
None of these have a scissor/clip-rect equivalent for your own widgets —
|
||||
there is no clipping API in this UI system; everything draws unclipped at
|
||||
whatever position you give it.
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-dolphin -f docker/dolphin/Dockerfile .
|
||||
docker run --rm -v "$(pwd):/workdir" dusk-dolphin /bin/bash -c "./scripts/build-gamecube.sh"
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-dolphin -f docker/dolphin/Dockerfile .
|
||||
docker run --rm -v "$(pwd):/workdir" dusk-dolphin /bin/bash -c "./scripts/build-gamecube-iso.sh"
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$DEVKITPRO" ]; then
|
||||
echo "DEVKITPRO environment variable is not set. Please set it to the path of your DEVKITPRO installation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-gamecube-iso
|
||||
cmake -S. -Bbuild-gamecube-iso \
|
||||
-DDUSK_TARGET_SYSTEM=gamecube \
|
||||
-DDUSK_DOLPHIN_BUILD_TYPE=ISO \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/GameCube.cmake"
|
||||
cd build-gamecube-iso
|
||||
make -j$(nproc) VERBOSE=1
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$DEVKITPRO" ]; then
|
||||
echo "DEVKITPRO environment variable is not set. Please set it to the path of your DEVKITPRO installation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-gamecube
|
||||
cmake -S. -Bbuild-gamecube \
|
||||
-DDUSK_TARGET_SYSTEM=gamecube \
|
||||
-DDUSK_DOLPHIN_BUILD_TYPE=DOL \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/GameCube.cmake" \
|
||||
-DDKP_OGC_PLATFORM_LIBRARY=libogc2
|
||||
cd build-gamecube
|
||||
make -j$(nproc) VERBOSE=1
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-knulli -f docker/knulli/Dockerfile .
|
||||
docker run --rm -v "$(pwd):/workdir" dusk-knulli /bin/bash -c "./scripts/build-knulli.sh"
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
cmake -S . -B build-knulli -G Ninja \
|
||||
-DDUSK_BUILD_TESTS=ON \
|
||||
-DDUSK_TARGET_SYSTEM=knulli \
|
||||
-DCMAKE_TOOLCHAIN_FILE=./cmake/toolchains/aarch64-linux-gnu.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build-knulli -- -j$(nproc)
|
||||
|
||||
# Copy necessary libs out
|
||||
mkdir -p ./build-knulli/dusk
|
||||
cp ./build-knulli/Dusk ./build-knulli/dusk/Dusk
|
||||
cp ./build-knulli/dusk.dsk ./build-knulli/dusk/dusk.dsk
|
||||
echo '#!/bin/bash' > build-knulli/dusk/Dusk.sh
|
||||
echo 'cd "$(dirname "$(readlink -f "$0")")"' >> build-knulli/dusk/Dusk.sh
|
||||
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(dirname "$(readlink -f "$0")")' >> build-knulli/dusk/Dusk.sh
|
||||
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/gl4es' >> build-knulli/dusk/Dusk.sh
|
||||
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib' >> build-knulli/dusk/Dusk.sh
|
||||
echo '$(dirname "$(readlink -f "$0")")/Dusk' >> build-knulli/dusk/Dusk.sh
|
||||
chmod +x build-knulli/dusk/Dusk.sh
|
||||
cp /usr/lib/aarch64-linux-gnu/liblua5.4.so.0 build-knulli/dusk/
|
||||
# cp /usr/lib/aarch64-linux-gnu/libSDL2-2.0.so.0 build-knulli/dusk/
|
||||
# cp /usr/lib/aarch64-linux-gnu/libGL.so.1 build-knulli/dusk/
|
||||
# cp /usr/lib/aarch64-linux-gnu/libEGL.so.1 build-knulli/dusk/
|
||||
# cp /usr/lib/aarch64-linux-gnu/libGLESv2.so.2 build-knulli/dusk/
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-linux -f docker/linux/Dockerfile .
|
||||
docker run --rm -v "$(pwd):/workdir" dusk-linux /bin/bash -c "./scripts/build-linux.sh"
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
cmake -S . -B build-linux -DDUSK_BUILD_TESTS=ON -DDUSK_TARGET_SYSTEM=linux
|
||||
cmake --build build-linux -- -j$(nproc)
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-psp -f docker/psp/Dockerfile .
|
||||
docker run --rm -v "$(pwd):/workdir" dusk-psp /bin/bash -c "./scripts/build-psp.sh"
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$PSPDEV" ]; then
|
||||
echo "PSPDEV environment variable is not set. Please set it to the path of your PSP development environment."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-psp
|
||||
cd build-psp
|
||||
psp-cmake -DDUSK_TARGET_SYSTEM=psp -DCMAKE_TOOLCHAIN_FILE=$PSPDEV/psp/share/pspdev.cmake -DBUILD_PRX=1 .. CMAKE_BUILD_TYPE=Release
|
||||
make -j$(nproc)
|
||||
# psp-cmake -DDUSK_TARGET_SYSTEM=psp -DCMAKE_TOOLCHAIN_FILE=$PSPDEV/psp/share/pspdev.cmake -DBUILD_PRX=1 -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-vita -f docker/vita/Dockerfile .
|
||||
docker run --rm -v "$(pwd):/workdir" dusk-vita /bin/bash -c "./scripts/build-vita.sh"
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$VITASDK" ]; then
|
||||
echo "VITASDK environment variable is not set. Please set it to the path of your VitaSDK installation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-vita
|
||||
cd build-vita
|
||||
cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$VITASDK/share/vita.toolchain.cmake \
|
||||
-DDUSK_TARGET_SYSTEM=vita \
|
||||
..
|
||||
make -j$(nproc)
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-dolphin -f docker/dolphin/Dockerfile .
|
||||
docker run --rm -v "$(pwd):/workdir" dusk-dolphin /bin/bash -c "./scripts/build-wii.sh"
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-dolphin -f docker/dolphin/Dockerfile .
|
||||
docker run --rm -v "$(pwd):/workdir" dusk-dolphin /bin/bash -c "./scripts/build-wii-iso.sh"
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$DEVKITPRO" ]; then
|
||||
echo "DEVKITPRO environment variable is not set. Please set it to the path of your DEVKITPRO installation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-wii-iso
|
||||
cmake -S. -Bbuild-wii-iso \
|
||||
-DDUSK_TARGET_SYSTEM=wii \
|
||||
-DDUSK_DOLPHIN_BUILD_TYPE=ISO \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/Wii.cmake"
|
||||
cd build-wii-iso
|
||||
make -j$(nproc) VERBOSE=1
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$DEVKITPRO" ]; then
|
||||
echo "DEVKITPRO environment variable is not set. Please set it to the path of your DEVKITPRO installation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-wii
|
||||
cmake -S. -Bbuild-wii \
|
||||
-DDUSK_TARGET_SYSTEM=wii \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$DEVKITPRO/cmake/Wii.cmake" \
|
||||
-DDUSK_DOLPHIN_BUILD_TYPE=DOL
|
||||
cd build-wii
|
||||
make -j$(nproc) VERBOSE=1
|
||||
mv Dusk.dol boot.dol
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-linux -f docker/linux/Dockerfile .
|
||||
docker run \
|
||||
--rm \
|
||||
-v "${GITHUB_WORKSPACE}:/workdir" \
|
||||
dusk-linux \
|
||||
/bin/bash -c "./scripts/test-linux.sh"
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
rm -rf build-tests
|
||||
cmake -S . -B build-tests -DDUSK_BUILD_TESTS=ON -DDUSK_TARGET_SYSTEM=linux
|
||||
cmake --build build-tests -- -j$(nproc)
|
||||
ctest --output-on-failure --test-dir build-tests
|
||||
+15
-4
@@ -1,15 +1,26 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
add_subdirectory(dusk)
|
||||
|
||||
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
|
||||
if(DUSK_TARGET_SYSTEM STREQUAL "linux" OR DUSK_TARGET_SYSTEM STREQUAL "knulli")
|
||||
add_subdirectory(dusklinux)
|
||||
add_subdirectory(dusksdl2)
|
||||
add_subdirectory(duskgl)
|
||||
|
||||
elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
|
||||
add_subdirectory(duskpsp)
|
||||
add_subdirectory(dusksdl2)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported target system: ${DUSK_TARGET_SYSTEM}")
|
||||
add_subdirectory(duskgl)
|
||||
|
||||
elseif(DUSK_TARGET_SYSTEM STREQUAL "vita")
|
||||
add_subdirectory(duskvita)
|
||||
add_subdirectory(dusksdl2)
|
||||
add_subdirectory(duskgl)
|
||||
|
||||
elseif(DUSK_TARGET_SYSTEM STREQUAL "wii" OR DUSK_TARGET_SYSTEM STREQUAL "gamecube")
|
||||
add_subdirectory(duskdolphin)
|
||||
|
||||
endif()
|
||||
+62
-16
@@ -3,35 +3,81 @@
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Libs
|
||||
target_link_libraries(${DUSK_TARGET_NAME}
|
||||
PUBLIC
|
||||
m
|
||||
)
|
||||
# Required Libraries
|
||||
if(NOT cglm_FOUND)
|
||||
find_package(cglm REQUIRED)
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC cglm)
|
||||
endif()
|
||||
|
||||
if(NOT libzip_FOUND)
|
||||
find_package(libzip REQUIRED)
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC zip)
|
||||
endif()
|
||||
|
||||
if(NOT stb_image_FOUND)
|
||||
find_package(stb REQUIRED)
|
||||
if(STB_IMAGE_FOUND)
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC stb_image)
|
||||
else()
|
||||
message(FATAL_ERROR "stb_image not found. Please ensure stb is correctly fetched.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT yyjson_FOUND)
|
||||
find_package(yyjson REQUIRED)
|
||||
if(yyjson_FOUND)
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC yyjson::yyjson)
|
||||
else()
|
||||
message(FATAL_ERROR "yyjson not found. Please ensure yyjson is correctly fetched.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT jerryscript_FOUND)
|
||||
find_package(jerryscript REQUIRED)
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
jerryscript::core
|
||||
jerryscript::ext
|
||||
jerryscript::port
|
||||
)
|
||||
endif()
|
||||
|
||||
if(DUSK_BACKTRACE)
|
||||
target_link_options(${DUSK_LIBRARY_TARGET_NAME} PUBLIC -rdynamic)
|
||||
target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||
DUSK_BACKTRACE
|
||||
)
|
||||
endif()
|
||||
|
||||
# Includes
|
||||
target_include_directories(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
game.c
|
||||
input.c
|
||||
# Main Binary Source
|
||||
target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
main.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(animation)
|
||||
add_subdirectory(assert)
|
||||
add_subdirectory(asset)
|
||||
add_subdirectory(console)
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(error)
|
||||
add_subdirectory(entity)
|
||||
add_subdirectory(event)
|
||||
add_subdirectory(item)
|
||||
add_subdirectory(log)
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(error)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(locale)
|
||||
add_subdirectory(physics)
|
||||
add_subdirectory(scene)
|
||||
add_subdirectory(script)
|
||||
add_subdirectory(system)
|
||||
add_subdirectory(time)
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(network)
|
||||
add_subdirectory(util)
|
||||
add_subdirectory(world)
|
||||
add_subdirectory(thread)
|
||||
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
easing.c
|
||||
animation.c
|
||||
)
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2026 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "animation.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/math.h"
|
||||
|
||||
void animationInit(
|
||||
animation_t *anim,
|
||||
keyframe_t *keyframes,
|
||||
uint16_t keyframeCount
|
||||
) {
|
||||
assertNotNull(anim, "Animation pointer cannot be null.");
|
||||
assertNotNull(keyframes, "Keyframes pointer cannot be null.");
|
||||
assertTrue(keyframeCount > 0, "Keyframe count must be more than 0.");
|
||||
|
||||
anim->keyframes = keyframes;
|
||||
anim->keyframeCount = keyframeCount;
|
||||
}
|
||||
|
||||
float_t animationGetValue(animation_t *anim, const float_t time) {
|
||||
assertNotNull(anim, "Animation pointer cannot be null.");
|
||||
assertNotNull(anim->keyframes, "Keyframes pointer cannot be null.");
|
||||
assertTrue(anim->keyframeCount > 0, "Keyframe count invalid.");
|
||||
assertTrue(time >= 0, "Time must be non-negative.");
|
||||
|
||||
keyframe_t *start;
|
||||
keyframe_t *end;
|
||||
keyframe_t *last = anim->keyframes + anim->keyframeCount - 1;
|
||||
keyframe_t *current = anim->keyframes;
|
||||
start = current;
|
||||
|
||||
do {
|
||||
if(current->time > time) {
|
||||
end = current;
|
||||
break;
|
||||
}
|
||||
start = current;
|
||||
current++;
|
||||
|
||||
if(current > last) {
|
||||
end = start;
|
||||
break;
|
||||
}
|
||||
} while(true);
|
||||
|
||||
float_t t = (time - start->time) / (end->time - start->time);
|
||||
return mathLerp(start->value, end->value, easingApply(start->easing, t));
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2026 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "keyframe.h"
|
||||
|
||||
typedef struct {
|
||||
keyframe_t *keyframes;
|
||||
uint16_t keyframeCount;
|
||||
} animation_t;
|
||||
|
||||
/**
|
||||
* Initializes an animation.
|
||||
*
|
||||
* @param anim The animation to initialize.
|
||||
* @param keyframes The keyframes to use for the animation.
|
||||
* @param keyframeCount The number of keyframes in the animation.
|
||||
*/
|
||||
void animationInit(
|
||||
animation_t *anim,
|
||||
keyframe_t *keyframes,
|
||||
uint16_t keyframeCount
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the value of the animation at a given time.
|
||||
*
|
||||
* @param anim The animation to get the value from.
|
||||
* @param time The time at which to get the value, in seconds.
|
||||
* @return The value of the animation at the given time.
|
||||
*/
|
||||
float_t animationGetValue(animation_t *anim, const float_t time);
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2026 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "easing.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
const easingfn_t EASING_FUNCTIONS[EASING_COUNT] = {
|
||||
easingLinear,
|
||||
easingInSine,
|
||||
easingOutSine,
|
||||
easingInOutSine,
|
||||
easingInQuad,
|
||||
easingOutQuad,
|
||||
easingInOutQuad,
|
||||
easingInCubic,
|
||||
easingOutCubic,
|
||||
easingInOutCubic,
|
||||
easingInQuart,
|
||||
easingOutQuart,
|
||||
easingInOutQuart,
|
||||
easingInBack,
|
||||
easingOutBack,
|
||||
easingInOutBack,
|
||||
};
|
||||
|
||||
float_t easingApply(const easingtype_t type, const float_t t) {
|
||||
assertTrue(type < EASING_COUNT, "Invalid easing type");
|
||||
return EASING_FUNCTIONS[type](t);
|
||||
}
|
||||
|
||||
float_t easingLinear(const float_t t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
float_t easingInSine(const float_t t) {
|
||||
return 1.0f - cosf(t * EASING_PI * 0.5f);
|
||||
}
|
||||
|
||||
float_t easingOutSine(const float_t t) {
|
||||
return sinf(t * EASING_PI * 0.5f);
|
||||
}
|
||||
|
||||
float_t easingInOutSine(const float_t t) {
|
||||
return -(cosf(EASING_PI * t) - 1.0f) * 0.5f;
|
||||
}
|
||||
|
||||
float_t easingInQuad(const float_t t) {
|
||||
return t * t;
|
||||
}
|
||||
|
||||
float_t easingOutQuad(const float_t t) {
|
||||
float_t u = 1.0f - t;
|
||||
return 1.0f - u * u;
|
||||
}
|
||||
|
||||
float_t easingInOutQuad(const float_t t) {
|
||||
if(t < 0.5f) return 2.0f * t * t;
|
||||
float_t u = -2.0f * t + 2.0f;
|
||||
return 1.0f - u * u * 0.5f;
|
||||
}
|
||||
|
||||
float_t easingInCubic(const float_t t) {
|
||||
return t * t * t;
|
||||
}
|
||||
|
||||
float_t easingOutCubic(const float_t t) {
|
||||
float_t u = 1.0f - t;
|
||||
return 1.0f - u * u * u;
|
||||
}
|
||||
|
||||
float_t easingInOutCubic(const float_t t) {
|
||||
if(t < 0.5f) return 4.0f * t * t * t;
|
||||
float_t u = -2.0f * t + 2.0f;
|
||||
return 1.0f - u * u * u * 0.5f;
|
||||
}
|
||||
|
||||
float_t easingInQuart(const float_t t) {
|
||||
return t * t * t * t;
|
||||
}
|
||||
|
||||
float_t easingOutQuart(const float_t t) {
|
||||
float_t u = 1.0f - t;
|
||||
return 1.0f - u * u * u * u;
|
||||
}
|
||||
|
||||
float_t easingInOutQuart(const float_t t) {
|
||||
if(t < 0.5f) return 8.0f * t * t * t * t;
|
||||
float_t u = -2.0f * t + 2.0f;
|
||||
return 1.0f - u * u * u * u * 0.5f;
|
||||
}
|
||||
|
||||
float_t easingInBack(const float_t t) {
|
||||
return EASING_C3 * t * t * t - EASING_C1 * t * t;
|
||||
}
|
||||
|
||||
float_t easingOutBack(const float_t t) {
|
||||
float_t u = t - 1.0f;
|
||||
return 1.0f + EASING_C3 * u * u * u + EASING_C1 * u * u;
|
||||
}
|
||||
|
||||
float_t easingInOutBack(const float_t t) {
|
||||
if(t < 0.5f) {
|
||||
float_t u = 2.0f * t;
|
||||
return u * u * ((EASING_C2 + 1.0f) * u - EASING_C2) * 0.5f;
|
||||
}
|
||||
float_t u = 2.0f * t - 2.0f;
|
||||
return (u * u * ((EASING_C2 + 1.0f) * u + EASING_C2) + 2.0f) * 0.5f;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2026 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
#define EASING_PI 3.14159265358979323846f
|
||||
#define EASING_C1 1.70158f
|
||||
#define EASING_C2 (EASING_C1 * 1.525f)
|
||||
#define EASING_C3 (EASING_C1 + 1.0f)
|
||||
|
||||
typedef enum {
|
||||
EASING_LINEAR,
|
||||
EASING_IN_SINE,
|
||||
EASING_OUT_SINE,
|
||||
EASING_IN_OUT_SINE,
|
||||
EASING_IN_QUAD,
|
||||
EASING_OUT_QUAD,
|
||||
EASING_IN_OUT_QUAD,
|
||||
EASING_IN_CUBIC,
|
||||
EASING_OUT_CUBIC,
|
||||
EASING_IN_OUT_CUBIC,
|
||||
EASING_IN_QUART,
|
||||
EASING_OUT_QUART,
|
||||
EASING_IN_OUT_QUART,
|
||||
EASING_IN_BACK,
|
||||
EASING_OUT_BACK,
|
||||
EASING_IN_OUT_BACK,
|
||||
|
||||
EASING_COUNT
|
||||
} easingtype_t;
|
||||
|
||||
typedef float_t (*easingfn_t)(const float_t t);
|
||||
|
||||
extern const easingfn_t EASING_FUNCTIONS[EASING_COUNT];
|
||||
|
||||
/**
|
||||
* Applies the specified easing function to t.
|
||||
*
|
||||
* @param type The easing type to apply.
|
||||
* @param t The input time, in the range [0, 1].
|
||||
* @return The eased value, in the range [0, 1].
|
||||
*/
|
||||
float_t easingApply(const easingtype_t type, const float_t t);
|
||||
|
||||
float_t easingLinear(const float_t t);
|
||||
float_t easingInSine(const float_t t);
|
||||
float_t easingOutSine(const float_t t);
|
||||
float_t easingInOutSine(const float_t t);
|
||||
float_t easingInQuad(const float_t t);
|
||||
float_t easingOutQuad(const float_t t);
|
||||
float_t easingInOutQuad(const float_t t);
|
||||
float_t easingInCubic(const float_t t);
|
||||
float_t easingOutCubic(const float_t t);
|
||||
float_t easingInOutCubic(const float_t t);
|
||||
float_t easingInQuart(const float_t t);
|
||||
float_t easingOutQuart(const float_t t);
|
||||
float_t easingInOutQuart(const float_t t);
|
||||
float_t easingInBack(const float_t t);
|
||||
float_t easingOutBack(const float_t t);
|
||||
float_t easingInOutBack(const float_t t);
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2026 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "easing.h"
|
||||
|
||||
typedef struct {
|
||||
float_t time;
|
||||
float_t value;
|
||||
easingtype_t easing;
|
||||
} keyframe_t;
|
||||
@@ -4,7 +4,7 @@
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
assert.c
|
||||
)
|
||||
+103
-18
@@ -1,31 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Dominic Masters
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assert.h"
|
||||
#include "log/log.h"
|
||||
#include "util/string.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
#ifndef ASSERTIONS_FAKED
|
||||
void assertTrueImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const bool x,
|
||||
const char *message
|
||||
) {
|
||||
if(x != true) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Assertion Failed in %s:%i\n\n%s\n",
|
||||
file,
|
||||
line,
|
||||
message
|
||||
);
|
||||
abort();
|
||||
}
|
||||
#ifdef DUSK_THREAD_PTHREAD
|
||||
pthread_t ASSERT_MAIN_THREAD_ID = 0;
|
||||
#endif
|
||||
|
||||
#ifndef DUSK_ASSERTIONS_FAKED
|
||||
void assertInit(void) {
|
||||
#ifdef DUSK_THREAD_PTHREAD
|
||||
ASSERT_MAIN_THREAD_ID = pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DUSK_TEST_ASSERT
|
||||
void assertTrueImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const bool x,
|
||||
const char *message
|
||||
) {
|
||||
mock_assert(
|
||||
x == true,
|
||||
message,
|
||||
file,
|
||||
line
|
||||
);
|
||||
}
|
||||
#else
|
||||
#ifdef DUSK_BACKTRACE
|
||||
#include <execinfo.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void assertLogBacktrace(void) {
|
||||
void *frames[64];
|
||||
int count = backtrace(frames, 64);
|
||||
char **symbols = backtrace_symbols(frames, count);
|
||||
memoryTrack(symbols);
|
||||
logError("Stack trace:\n");
|
||||
if(symbols) {
|
||||
for(int i = 0; i < count; i++) {
|
||||
logError(" %s\n", symbols[i]);
|
||||
}
|
||||
memoryFree(symbols);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void assertTrueImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const bool x,
|
||||
const char *message
|
||||
) {
|
||||
if(x != true) {
|
||||
logError(
|
||||
"Assertion Failed in %s:%i\n\n%s\n\n",
|
||||
file,
|
||||
line,
|
||||
message
|
||||
);
|
||||
#ifdef DUSK_BACKTRACE
|
||||
assertLogBacktrace();
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void assertFalseImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
@@ -82,4 +132,39 @@
|
||||
) {
|
||||
assertUnreachableImpl(file, line, message);
|
||||
}
|
||||
|
||||
void assertStringEqualImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const char *a,
|
||||
const char *b,
|
||||
const char *message
|
||||
) {
|
||||
assertTrueImpl(file, line, stringCompare(a, b) == 0, message);
|
||||
}
|
||||
|
||||
void assertIsMainThreadImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const char *message
|
||||
) {
|
||||
#ifdef DUSK_THREAD_PTHREAD
|
||||
assertTrueImpl(
|
||||
file, line, pthread_self() == ASSERT_MAIN_THREAD_ID, message
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
void assertNotMainThreadImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const char *message
|
||||
) {
|
||||
#ifdef DUSK_THREAD_PTHREAD
|
||||
assertTrueImpl(
|
||||
file, line, pthread_self() != ASSERT_MAIN_THREAD_ID, message
|
||||
);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+144
-6
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2023 Dominic Masters
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
@@ -8,7 +8,26 @@
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
#ifndef ASSERTIONS_FAKED
|
||||
#ifdef DUSK_TEST_ASSERT
|
||||
#include <cmocka.h>
|
||||
|
||||
#ifdef DUSK_ASSERTIONS_FAKED
|
||||
#error "Cannot fake assertions when DUSK_TEST_ASSERT is set."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DUSK_THREAD_PTHREAD
|
||||
#include "thread/thread.h"
|
||||
extern pthread_t ASSERT_MAIN_THREAD_ID;
|
||||
#endif
|
||||
|
||||
#ifndef DUSK_ASSERTIONS_FAKED
|
||||
/**
|
||||
* Initializes the assert system. Must be the very first call in engine
|
||||
* startup.
|
||||
*/
|
||||
void assertInit(void);
|
||||
|
||||
/**
|
||||
* Assert a given value to be true.
|
||||
*
|
||||
@@ -96,41 +115,151 @@
|
||||
const char *message
|
||||
);
|
||||
|
||||
void assertMemoryRangeMatchesImpl(
|
||||
/**
|
||||
* Asserts two strings to be equal.
|
||||
*
|
||||
* @param file File that the assertion is being made from.
|
||||
* @param line Line that the assertion is being made from.
|
||||
* @param a First string to compare.
|
||||
* @param b Second string to compare.
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
void assertStringEqualImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const void *start,
|
||||
const void *end,
|
||||
const size_t size,
|
||||
const char *a,
|
||||
const char *b,
|
||||
const char *message
|
||||
);
|
||||
|
||||
/**
|
||||
* Asserts that the current thread is the main thread.
|
||||
*
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
void assertIsMainThreadImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const char *message
|
||||
);
|
||||
|
||||
/**
|
||||
* Asserts that the current thread is NOT the main thread.
|
||||
*
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
void assertNotMainThreadImpl(
|
||||
const char *file,
|
||||
const int32_t line,
|
||||
const char *message
|
||||
);
|
||||
|
||||
/**
|
||||
* Asserts a given value to be true.
|
||||
*
|
||||
* @param x Value to assert as true.
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertTrue(x, message) \
|
||||
assertTrueImpl(__FILE__, __LINE__, x, message)
|
||||
|
||||
/**
|
||||
* Asserts a given statement to be false.
|
||||
*
|
||||
* @param x Value to assert as false.
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertFalse(x, message) \
|
||||
assertFalseImpl(__FILE__, __LINE__, x, message)
|
||||
|
||||
/**
|
||||
* Asserts that a given line of code is unreachable. Essentially a forced
|
||||
* assertion failure, good for "edge cases"
|
||||
*
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertUnreachable(message) \
|
||||
assertUnreachableImpl(__FILE__, __LINE__, message)
|
||||
|
||||
|
||||
/**
|
||||
* Asserts a pointer is not null.
|
||||
*
|
||||
* @param pointer Pointer to check.
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertNotNull(pointer, message) \
|
||||
assertNotNullImpl(__FILE__, __LINE__, pointer, message)
|
||||
|
||||
/**
|
||||
* Asserts a pointer is null.
|
||||
*
|
||||
* @param pointer Pointer to check.
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertNull(pointer, message) \
|
||||
assertNullImpl(__FILE__, __LINE__, pointer, message)
|
||||
|
||||
/**
|
||||
* Asserts a function or code path is deprecated.
|
||||
*
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertDeprecated(message) \
|
||||
assertDeprecatedImpl(__FILE__, __LINE__, message)
|
||||
|
||||
/**
|
||||
* Asserts a string's length is less than a maximum.
|
||||
*
|
||||
* @param str String to check.
|
||||
* @param len Maximum length.
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertStrLenMax(str, len, message) \
|
||||
assertTrue(strlen(str) < len, message)
|
||||
|
||||
/**
|
||||
* Asserts a string's length is at least a minimum.
|
||||
*
|
||||
* @param str String to check.
|
||||
* @param len Minimum length.
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertStrLenMin(str, len, message) \
|
||||
assertTrue(strlen(str) >= len, message)
|
||||
|
||||
/**
|
||||
* Asserts two strings to be equal.
|
||||
*
|
||||
* @param a First string to compare.
|
||||
* @param b Second string to compare.
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertStringEqual(a, b, message) \
|
||||
assertStringEqualImpl(__FILE__, __LINE__, a, b, message)
|
||||
|
||||
/**
|
||||
* Asserts that the current thread is the main thread.
|
||||
*
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertIsMainThread(message) \
|
||||
assertIsMainThreadImpl(__FILE__, __LINE__, message)
|
||||
|
||||
/**
|
||||
* Asserts that the current thread is NOT the main thread.
|
||||
*
|
||||
* @param message Message to throw against assertion failure.
|
||||
*/
|
||||
#define assertNotMainThread(message) \
|
||||
assertNotMainThreadImpl(__FILE__, __LINE__, message)
|
||||
|
||||
#else
|
||||
// If assertions are faked, we define the macros to do nothing.
|
||||
#define assertInit() ((void)0)
|
||||
#define assertMainThreadInit() ((void)0)
|
||||
#define assertIsMainThread(message) ((void)0)
|
||||
#define assertNotMainThread(message) ((void)0)
|
||||
#define assertTrue(x, message) ((void)0)
|
||||
#define assertFalse(x, message) ((void)0)
|
||||
#define assertUnreachable(message) ((void)0)
|
||||
@@ -139,5 +268,14 @@
|
||||
#define assertDeprecated(message) ((void)0)
|
||||
#define assertStrLenMax(str, len, message) ((void)0)
|
||||
#define assertStrLenMin(str, len, message) ((void)0)
|
||||
#define assertStringEqual(a, b, message) ((void)0)
|
||||
#define assertIsMainThread(message) ((void)0)
|
||||
#define assertNotMainThread(message) ((void)0)
|
||||
|
||||
#endif
|
||||
|
||||
// Static Assertions
|
||||
#define assertStructSize(struct, size) \
|
||||
_Static_assert(sizeof(struct) == size, "Size of " #struct " must be " #size)
|
||||
|
||||
// EOF
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
spritebatch.c
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
asset.c
|
||||
assetfile.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
# add_subdirectory(draw)
|
||||
add_subdirectory(loader)
|
||||
@@ -0,0 +1,434 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "asset.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "assert/assert.h"
|
||||
#include "engine/engine.h"
|
||||
#include "util/string.h"
|
||||
#include "console/console.h"
|
||||
#include <unistd.h>
|
||||
|
||||
asset_t ASSET;
|
||||
|
||||
errorret_t assetInit(void) {
|
||||
memoryZero(&ASSET, sizeof(asset_t));
|
||||
|
||||
for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
|
||||
threadMutexInit(&ASSET.loading[i].mutex);
|
||||
}
|
||||
|
||||
// assetInitPlatform must either define ASSET.zip or throw an error.
|
||||
errorChain(assetInitPlatform());
|
||||
assertNotNull(ASSET.zip, "Asset zip null without error.");
|
||||
threadInit(&ASSET.loadThread, assetUpdateAsync);
|
||||
threadStart(&ASSET.loadThread);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
bool_t assetFileExists(const char_t *filename) {
|
||||
assertStrLenMax(filename, ASSET_FILE_NAME_MAX, "Filename too long.");
|
||||
|
||||
zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0);
|
||||
if(idx < 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
assetentry_t * assetGetEntry(
|
||||
const char_t *name,
|
||||
const assetloadertype_t type,
|
||||
assetloaderinput_t *input
|
||||
) {
|
||||
// Is there an existing asset?
|
||||
assetentry_t *entry = ASSET.entries;
|
||||
do {
|
||||
if(entry->type == ASSET_LOADER_TYPE_NULL) {
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
if(stringEquals(entry->name, name)) {
|
||||
assertTrue(entry->type == type, "Asset entry type mismatch.");
|
||||
return entry;
|
||||
}
|
||||
entry++;
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
|
||||
// We did not find one existing. Find first available slot, reaping
|
||||
// zero-ref entries to make room if none are immediately available.
|
||||
bool_t reaped = false;
|
||||
for(;;) {
|
||||
entry = ASSET.entries;
|
||||
do {
|
||||
if(entry->type != ASSET_LOADER_TYPE_NULL) {
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(entry->state == ASSET_ENTRY_STATE_NOT_STARTED) {
|
||||
assetEntryInit(entry, name, type, input);
|
||||
return entry;
|
||||
}
|
||||
entry++;
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
|
||||
if(reaped) break;
|
||||
reaped = true;
|
||||
errorCatch(assetReapUnused());
|
||||
}
|
||||
|
||||
assertUnreachable("No available asset entry slots.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t assetGetEntriesOfType(
|
||||
assetentry_t **outEntries,
|
||||
const assetloadertype_t type
|
||||
) {
|
||||
assertNotNull(outEntries, "Output entries cannot be NULL.");
|
||||
|
||||
uint32_t count = 0;
|
||||
assetentry_t *entry = ASSET.entries;
|
||||
do {
|
||||
if(entry->type == type) {
|
||||
outEntries[count++] = entry;
|
||||
}
|
||||
entry++;
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
errorret_t assetRequireLoaded(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL.");
|
||||
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||
assertIsMainThread("Currently only works on main thread.");
|
||||
|
||||
if(entry->state == ASSET_ENTRY_STATE_LOADED) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Lock to prevent the reaper from collecting the entry mid-spin.
|
||||
assetEntryLock(entry);
|
||||
|
||||
while(entry->state != ASSET_ENTRY_STATE_LOADED) {
|
||||
usleep(1000);
|
||||
errorret_t ret = assetUpdate();
|
||||
if(errorIsNotOk(ret)) {
|
||||
assetEntryUnlock(entry);
|
||||
errorChain(ret);
|
||||
}
|
||||
}
|
||||
|
||||
assetEntryUnlock(entry);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetRequireDisposed(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL.");
|
||||
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||
assertIsMainThread("Currently only works on main thread.");
|
||||
|
||||
if(entry->type == ASSET_LOADER_TYPE_NULL) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
if(
|
||||
entry->state == ASSET_ENTRY_STATE_NOT_STARTED ||
|
||||
entry->state == ASSET_ENTRY_STATE_ERROR
|
||||
) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
assertTrue(
|
||||
entry->refs.count == 0,
|
||||
"Cannot require disposal of an entry with active references."
|
||||
);
|
||||
|
||||
// Lock to prevent the reaper from collecting the entry mid-spin.
|
||||
assetEntryLock(entry);
|
||||
|
||||
while(entry->type != ASSET_LOADER_TYPE_NULL) {
|
||||
usleep(1000);
|
||||
errorret_t ret = assetUpdate();
|
||||
if(errorIsNotOk(ret)) {
|
||||
assetEntryUnlock(entry);
|
||||
errorChain(ret);
|
||||
}
|
||||
}
|
||||
|
||||
assetEntryUnlock(entry);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
assetentry_t * assetLock(
|
||||
const char_t *name,
|
||||
const assetloadertype_t type,
|
||||
assetloaderinput_t *input
|
||||
) {
|
||||
assetentry_t *entry = assetGetEntry(name, type, input);
|
||||
assetEntryLock(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void assetUnlock(const char_t *name) {
|
||||
assertNotNull(name, "Name cannot be NULL.");
|
||||
|
||||
assetentry_t *entry = ASSET.entries;
|
||||
do {
|
||||
if(
|
||||
entry->type != ASSET_LOADER_TYPE_NULL &&
|
||||
stringEquals(entry->name, name)
|
||||
) {
|
||||
assetEntryUnlock(entry);
|
||||
return;
|
||||
}
|
||||
entry++;
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
|
||||
assertUnreachable("Asset entry not found for unlock.");
|
||||
}
|
||||
|
||||
void assetUnlockEntry(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL.");
|
||||
assetEntryUnlock(entry);
|
||||
}
|
||||
|
||||
errorret_t assetReapUnused(void) {
|
||||
assertIsMainThread("assetReapUnused must be called from the main thread.");
|
||||
|
||||
// Repeatedly find and dispose zero-ref LOADED entries until none remain.
|
||||
// This handles dependency chains where an entry (e.g. a model) holds refs
|
||||
// on child entries (mesh, texture): dispose parents first so child ref
|
||||
// counts drop to zero, then pick up the children on the next pass. Without
|
||||
// this, a forward-only scan fails when a shared child entry appears before
|
||||
// a parent that still holds a ref to it.
|
||||
bool_t any;
|
||||
do {
|
||||
any = false;
|
||||
assetentry_t *entry = ASSET.entries;
|
||||
do {
|
||||
if(entry->type == ASSET_LOADER_TYPE_NULL) { entry++; continue; }
|
||||
if(entry->state != ASSET_ENTRY_STATE_LOADED) { entry++; continue; }
|
||||
if(entry->refs.count > 0) { entry++; continue; }
|
||||
errorChain(assetEntryDispose(entry));
|
||||
any = true;
|
||||
entry++;
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
} while(any);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetUpdate(void) {
|
||||
assertIsMainThread("assetUpdate must be called from the main thread.");
|
||||
|
||||
// Determine how many available loading slots we have.
|
||||
assetloading_t *availableLoading[ASSET_LOADING_COUNT_MAX];
|
||||
uint8_t availableLoadingCount = 0;
|
||||
assetloading_t *loading = ASSET.loading;
|
||||
assetentry_t *entry;
|
||||
|
||||
|
||||
do {
|
||||
// We only care about NULL entry references. Nothing async touches this so
|
||||
// it's fine to use raw here.
|
||||
if(loading->entry != NULL) {
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
availableLoading[availableLoadingCount++] = loading;
|
||||
loading++;
|
||||
} while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX);
|
||||
|
||||
|
||||
// Now we can check for pending asset entries, we can't do anything if there
|
||||
// is no available slots though.
|
||||
if(availableLoadingCount > 0) {
|
||||
entry = ASSET.entries;
|
||||
do {
|
||||
// Is this asset "ready to start loading" ?
|
||||
if(entry->type == ASSET_LOADER_TYPE_NULL) {
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We only care about assets not started.
|
||||
if(entry->state != ASSET_ENTRY_STATE_NOT_STARTED) {
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pop a loading slot for this asset entry.
|
||||
loading = availableLoading[--availableLoadingCount];
|
||||
|
||||
// Start loading this asset.
|
||||
assetEntryStartLoading(entry, loading);
|
||||
entry++;
|
||||
|
||||
// Did we run out of loading slots?
|
||||
if(availableLoadingCount == 0) {
|
||||
break;
|
||||
}
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
}
|
||||
|
||||
// Now walk over all the loading slots and see what needs to be done.
|
||||
loading = ASSET.loading;
|
||||
do {
|
||||
// Is the loading slot in use? Entry can only be modified synchronously.
|
||||
if(loading->entry == NULL) {
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lock the loading slot. This will prevent any async modifications.
|
||||
threadMutexLock(&loading->mutex);
|
||||
|
||||
// Check the state of the entry.
|
||||
switch(loading->entry->state) {
|
||||
// This thing is pending synchronous loading.
|
||||
case ASSET_ENTRY_STATE_PENDING_SYNC:
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADING_SYNC;
|
||||
// Unlock before calling loadSync. The sync loader may re-enter
|
||||
// assetUpdate (e.g. a script loading another asset), and the async
|
||||
// thread never touches LOADING_SYNC entries, so this is safe.
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
|
||||
errorret_t ret = (
|
||||
ASSET_LOADER_CALLBACKS[loading->type].loadSync(loading)
|
||||
);
|
||||
|
||||
// After a sync load, these are the only valid states.
|
||||
assertTrue(
|
||||
loading->entry->state == ASSET_ENTRY_STATE_LOADED ||
|
||||
loading->entry->state == ASSET_ENTRY_STATE_ERROR ||
|
||||
loading->entry->state == ASSET_ENTRY_STATE_PENDING_SYNC ||
|
||||
loading->entry->state == ASSET_ENTRY_STATE_PENDING_ASYNC,
|
||||
"Loader did not set entry state to loaded or error on finished load."
|
||||
);
|
||||
|
||||
if(errorIsNotOk(ret)) {
|
||||
errorCatch(errorPrint(ret));
|
||||
assertTrue(
|
||||
loading->entry->state == ASSET_ENTRY_STATE_ERROR,
|
||||
"Loader did not set entry state to error on failed load."
|
||||
);
|
||||
} else if(loading->entry->state == ASSET_ENTRY_STATE_LOADED) {
|
||||
loading->entry = NULL;
|
||||
}
|
||||
|
||||
loading++;
|
||||
break;
|
||||
|
||||
case ASSET_ENTRY_STATE_LOADING_SYNC:
|
||||
// A re-entrant assetUpdate call (e.g. from a script loading another
|
||||
// asset) will see this entry mid-sync-load. Skip it.
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
continue;
|
||||
|
||||
// Done loading, we can just free it up.
|
||||
case ASSET_ENTRY_STATE_LOADED:
|
||||
loading->entry = NULL;
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
break;
|
||||
|
||||
case ASSET_ENTRY_STATE_ERROR: {
|
||||
loading->entry = NULL;
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
errorThrow("Failed to load asset asynchronously.");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
} while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void assetUpdateAsync(thread_t *thread) {
|
||||
assertNotMainThread("assetUpdateAsync must not run on the main thread.");
|
||||
|
||||
while(!threadShouldStop(thread)) {
|
||||
// Walk over each asset
|
||||
assetloading_t *loading;
|
||||
loading = ASSET.loading;
|
||||
|
||||
do {
|
||||
threadMutexLock(&loading->mutex);
|
||||
|
||||
if(loading->entry == NULL) {
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(loading->entry->state) {
|
||||
case ASSET_ENTRY_STATE_PENDING_ASYNC:
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADING_ASYNC;
|
||||
assertNotNull(
|
||||
ASSET_LOADER_CALLBACKS[loading->type].loadAsync,
|
||||
"Loader does not support async loading."
|
||||
);
|
||||
errorret_t ret = (
|
||||
ASSET_LOADER_CALLBACKS[loading->type].loadAsync(loading)
|
||||
);
|
||||
|
||||
if(errorIsNotOk(ret)) {
|
||||
errorCatch(errorPrint(ret));
|
||||
assertTrue(
|
||||
loading->entry->state == ASSET_ENTRY_STATE_ERROR,
|
||||
"Loader did not set entry state to error on failed load."
|
||||
);
|
||||
}
|
||||
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
break;
|
||||
|
||||
case ASSET_ENTRY_STATE_LOADING_ASYNC:
|
||||
assertUnreachable(
|
||||
"Entry is in a pending async state still?"
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
threadMutexUnlock(&loading->mutex);
|
||||
loading++;
|
||||
continue;
|
||||
}
|
||||
} while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX);
|
||||
|
||||
if(threadShouldStop(thread)) break;
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t assetDispose(void) {
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
threadStop(&ASSET.loadThread);
|
||||
|
||||
errorChain(assetReapUnused());
|
||||
|
||||
// Cleanup zip file.
|
||||
if(ASSET.zip != NULL) {
|
||||
if(zip_close(ASSET.zip) != 0) {
|
||||
errorThrow("Failed to close asset zip archive.");
|
||||
}
|
||||
ASSET.zip = NULL;
|
||||
}
|
||||
|
||||
errorChain(assetDisposePlatform());
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "asset/assetplatform.h"
|
||||
#include "assetfile.h"
|
||||
#include "thread/thread.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
#include "asset/loader/assetloading.h"
|
||||
|
||||
#ifndef assetInitPlatform
|
||||
#error "Platform must define assetInitPlatform function."
|
||||
#endif
|
||||
#ifndef assetDisposePlatform
|
||||
#error "Platform must define assetDisposePlatform function."
|
||||
#endif
|
||||
|
||||
#define ASSET_FILE_NAME "dusk.dsk"
|
||||
#define ASSET_HEADER_SIZE 3
|
||||
|
||||
#define ASSET_LOADING_COUNT_MAX 16
|
||||
#define ASSET_ENTRY_COUNT_MAX 64
|
||||
|
||||
typedef struct asset_s {
|
||||
zip_t *zip;
|
||||
assetplatform_t platform;
|
||||
|
||||
// Background loading thread.
|
||||
thread_t loadThread;
|
||||
|
||||
// Assets that are mid loading.
|
||||
assetloading_t loading[ASSET_LOADING_COUNT_MAX];
|
||||
assetentry_t entries[ASSET_ENTRY_COUNT_MAX];
|
||||
} asset_t;
|
||||
|
||||
extern asset_t ASSET;
|
||||
|
||||
/**
|
||||
* Initializes the asset system.
|
||||
*
|
||||
* @return An error code if the asset system could not be initialized properly.
|
||||
*/
|
||||
errorret_t assetInit(void);
|
||||
|
||||
/**
|
||||
* Checks if an asset file exists.
|
||||
*
|
||||
* @param filename The filename of the asset to check.
|
||||
* @return true if the asset file exists, false otherwise.
|
||||
*/
|
||||
bool_t assetFileExists(const char_t *filename);
|
||||
|
||||
/**
|
||||
* Gets, or creates, a new asset entry. Internal - prefer assetLock.
|
||||
*
|
||||
* @param name Filename of the asset.
|
||||
* @param type Type of the asset.
|
||||
* @param input Loader-specific parameters.
|
||||
*/
|
||||
assetentry_t * assetGetEntry(
|
||||
const char_t *name,
|
||||
const assetloadertype_t type,
|
||||
assetloaderinput_t *input
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets all asset entries of a given type.
|
||||
*
|
||||
* @param outEntries Output array to write the entries to.
|
||||
* @param type Type of the asset entries to get.
|
||||
* @return The number of entries written to outEntries.
|
||||
*/
|
||||
uint32_t assetGetEntriesOfType(
|
||||
assetentry_t **outEntries,
|
||||
const assetloadertype_t type
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets, creates, and locks an asset entry. The asset will begin loading on
|
||||
* the next assetUpdate. Call assetUnlock when done to allow the entry to be
|
||||
* reclaimed.
|
||||
*
|
||||
* @param name Filename of the asset.
|
||||
* @param type Type of the asset.
|
||||
* @param input Loader-specific parameters.
|
||||
* @return The locked asset entry.
|
||||
*/
|
||||
assetentry_t * assetLock(
|
||||
const char_t *name,
|
||||
const assetloadertype_t type,
|
||||
assetloaderinput_t *input
|
||||
);
|
||||
|
||||
/**
|
||||
* Releases a lock on an asset entry by name. When all locks are released the
|
||||
* entry will be reclaimed at the start of the next assetUpdate.
|
||||
*
|
||||
* @param name Filename of the asset to unlock.
|
||||
*/
|
||||
void assetUnlock(const char_t *name);
|
||||
|
||||
/**
|
||||
* Releases a lock on an asset entry by pointer. When all locks are released
|
||||
* the entry will be reclaimed at the start of the next assetUpdate.
|
||||
*
|
||||
* @param entry The asset entry to unlock.
|
||||
*/
|
||||
void assetUnlockEntry(assetentry_t *entry);
|
||||
|
||||
/**
|
||||
* Frees every currently unreferenced (zero-ref) loaded asset entry. Repeats
|
||||
* until a full pass frees nothing further, since disposing a parent entry
|
||||
* (e.g. a model) may drop a child entry's (e.g. a mesh) ref count to zero,
|
||||
* making it eligible for reaping too.
|
||||
*
|
||||
* @return An error code if any entry could not be disposed properly.
|
||||
*/
|
||||
errorret_t assetReapUnused(void);
|
||||
|
||||
/**
|
||||
* Requires an asset entry to be loaded. This will block until the asset entry
|
||||
* is fully loaded.
|
||||
*
|
||||
* @param entry The asset entry to require.
|
||||
* @return An error code if the asset entry could not be loaded properly.
|
||||
*/
|
||||
errorret_t assetRequireLoaded(assetentry_t *entry);
|
||||
|
||||
/**
|
||||
* Requires an asset entry to be disposed. This will block until the asset entry
|
||||
* is fully disposed.
|
||||
*
|
||||
* @param entry The asset entry to require disposal of.
|
||||
* @return An error code if the asset entry could not be disposed properly.
|
||||
*/
|
||||
errorret_t assetRequireDisposed(assetentry_t *entry);
|
||||
|
||||
/**
|
||||
* Updates the asset system.
|
||||
*
|
||||
* @return An error code if the asset system could not be updated properly.
|
||||
*/
|
||||
errorret_t assetUpdate(void);
|
||||
|
||||
/**
|
||||
* Starts the background asset loading thread. The thread runs assetUpdate
|
||||
* in a loop with a short sleep until stopped.
|
||||
*
|
||||
* @param thread The thread runner.
|
||||
*/
|
||||
void assetUpdateAsync(thread_t *thread);
|
||||
|
||||
/**
|
||||
* Disposes/cleans up the asset system.
|
||||
*
|
||||
* @return An error code if the asset system could not be disposed properly.
|
||||
*/
|
||||
errorret_t assetDispose(void);
|
||||
@@ -0,0 +1,380 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "asset/asset.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/math.h"
|
||||
|
||||
errorret_t assetFileInit(
|
||||
assetfile_t *file,
|
||||
const char_t *filename,
|
||||
void *params,
|
||||
void *output
|
||||
) {
|
||||
assertNotNull(file, "Asset file cannot be NULL.");
|
||||
assertStrLenMax(filename, ASSET_FILE_NAME_MAX, "Filename too long.");
|
||||
|
||||
memoryZero(file, sizeof(assetfile_t));
|
||||
memoryCopy(file->filename, filename, ASSET_FILE_NAME_MAX);
|
||||
file->params = params;
|
||||
file->output = output;
|
||||
|
||||
// Stat the file
|
||||
zip_stat_init(&file->stat);
|
||||
if(!zip_stat(ASSET.zip, filename, 0, &file->stat) == 0) {
|
||||
errorThrow("Failed to stat asset file: %s", filename);
|
||||
}
|
||||
|
||||
// Minimum file size.
|
||||
file->size = (zip_int64_t)file->stat.size;
|
||||
if(file->size <= 0) {
|
||||
errorThrow("Invalid asset file size: %s", filename);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetFileRewind(assetfile_t *file) {
|
||||
assertNotNull(file, "Asset file cannot be NULL.");
|
||||
assertNotNull(file->zipFile, "Asset file must be opened before rewinding.");
|
||||
|
||||
if(file->position == 0) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorChain(assetFileClose(file));
|
||||
errorChain(assetFileOpen(file));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetFileOpen(assetfile_t *file) {
|
||||
assertNotNull(file, "Asset file cannot be NULL.");
|
||||
assertNotNull(file->filename, "Asset file filename cannot be NULL.");
|
||||
assertNotNull(ASSET.zip, "Asset zip cannot be NULL.");
|
||||
assertNull(file->zipFile, "Asset file already open.");
|
||||
|
||||
file->zipFile = zip_fopen(ASSET.zip, file->filename, 0);
|
||||
if(file->zipFile == NULL) {
|
||||
errorThrow("Failed to open asset file: %s", file->filename);
|
||||
}
|
||||
file->position = 0;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetFileRead(
|
||||
assetfile_t *file,
|
||||
void *buffer,
|
||||
const size_t bufferSize
|
||||
) {
|
||||
assertNotNull(file, "Asset file cannot be NULL.");
|
||||
assertNotNull(file->zipFile, "Asset file must be opened before reading.");
|
||||
|
||||
if(buffer == NULL) {
|
||||
size_t bytesRemaining = bufferSize;
|
||||
uint8_t tempBuffer[256];
|
||||
while(bytesRemaining > 0) {
|
||||
size_t chunkSize = mathMin(bytesRemaining, sizeof(tempBuffer));
|
||||
errorChain(assetFileRead(file, tempBuffer, chunkSize));
|
||||
file->position += chunkSize;
|
||||
bytesRemaining -= chunkSize;
|
||||
}
|
||||
file->lastRead = bufferSize;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// I assume zip_fread takes buffer NULL for skipping?
|
||||
zip_int64_t bytesRead = zip_fread(file->zipFile, buffer, bufferSize);
|
||||
if(bytesRead < 0) {
|
||||
errorThrow("Failed to read from asset file: %s", file->filename);
|
||||
}
|
||||
file->position += bytesRead;
|
||||
file->lastRead = bytesRead;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetFileClose(assetfile_t *file) {
|
||||
assertNotNull(file, "Asset file cannot be NULL.");
|
||||
assertNotNull(file->zipFile, "Asset file must be opened before closing.");
|
||||
|
||||
if(zip_fclose(file->zipFile) != 0) {
|
||||
errorThrow("Failed to close asset file: %s", file->filename);
|
||||
}
|
||||
file->zipFile = NULL;
|
||||
file->position = 0;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetFileDispose(assetfile_t *file) {
|
||||
if(file->zipFile != NULL) {
|
||||
errorChain(assetFileClose(file));
|
||||
}
|
||||
memoryZero(file, sizeof(assetfile_t));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetFileReadEntire(
|
||||
assetfile_t *file,
|
||||
uint8_t **outBuffer,
|
||||
size_t *outSize
|
||||
) {
|
||||
assertNotNull(file, "Asset file cannot be NULL.");
|
||||
assertNotNull(outBuffer, "outBuffer cannot be NULL.");
|
||||
assertNotNull(outSize, "outSize cannot be NULL.");
|
||||
assertTrue(
|
||||
file->size > 0,
|
||||
"Asset file has no size; call assetFileInit first."
|
||||
);
|
||||
|
||||
// File should be closed currently.
|
||||
assertNull(file->zipFile, "Asset file must be closed before reading entire.");
|
||||
|
||||
// Open file
|
||||
errorret_t ret = assetFileOpen(file);
|
||||
if(errorIsNotOk(ret)) {
|
||||
errorChain(ret);
|
||||
}
|
||||
|
||||
// Set output.
|
||||
size_t size = (size_t)file->size;
|
||||
uint8_t *buffer = (uint8_t *)memoryAllocate(size);
|
||||
|
||||
// Read entire file.
|
||||
ret = assetFileRead(file, buffer, size);
|
||||
if(errorIsNotOk(ret)) {
|
||||
memoryFree(buffer);
|
||||
errorChain(ret);
|
||||
}
|
||||
|
||||
// Close the file.
|
||||
ret = assetFileClose(file);
|
||||
if(errorIsNotOk(ret)) {
|
||||
memoryFree(buffer);
|
||||
errorChain(ret);
|
||||
}
|
||||
|
||||
*outBuffer = buffer;
|
||||
*outSize = size;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Line Reader;
|
||||
void assetFileLineReaderInit(
|
||||
assetfilelinereader_t *reader,
|
||||
assetfile_t *file,
|
||||
uint8_t *readBuffer,
|
||||
const size_t readBufferSize,
|
||||
uint8_t *outBuffer,
|
||||
const size_t outBufferSize
|
||||
) {
|
||||
assertNotNull(reader, "Line reader cannot be NULL.");
|
||||
assertNotNull(file, " File cannot be NULL.");
|
||||
assertNotNull(readBuffer, "Read buffer cannot be NULL.");
|
||||
assertNotNull(outBuffer, "Output buffer cannot be NULL.");
|
||||
assertTrue(readBufferSize > 0, "Read buffer size must be greater than 0.");
|
||||
assertTrue(outBufferSize > 0, "Output buffer size must be greater than 0.");
|
||||
|
||||
memoryZero(reader, sizeof(assetfilelinereader_t));
|
||||
|
||||
reader->file = file;
|
||||
reader->readBuffer = readBuffer;
|
||||
reader->readBufferSize = readBufferSize;
|
||||
reader->outBuffer = outBuffer;
|
||||
reader->outBufferSize = outBufferSize;
|
||||
}
|
||||
|
||||
size_t assetFileLineReaderUnreadBytes(const assetfilelinereader_t *reader) {
|
||||
assertNotNull(reader, "Reader cannot be NULL.");
|
||||
assertTrue(reader->bufferEnd >= reader->bufferStart, "Invalid buffer state.");
|
||||
return reader->bufferEnd - reader->bufferStart;
|
||||
}
|
||||
|
||||
const uint8_t *assetFileLineReaderUnreadPtr(
|
||||
const assetfilelinereader_t *reader
|
||||
) {
|
||||
assertNotNull(reader, "Reader cannot be NULL.");
|
||||
assertNotNull(reader->readBuffer, "Read buffer cannot be NULL.");
|
||||
return reader->readBuffer + reader->bufferStart;
|
||||
}
|
||||
|
||||
static errorret_t assetFileLineReaderAppend(
|
||||
assetfilelinereader_t *reader,
|
||||
const uint8_t *src,
|
||||
size_t srcLength
|
||||
) {
|
||||
assertNotNull(reader, "Reader cannot be NULL.");
|
||||
assertNotNull(reader->outBuffer, "Out buffer cannot be NULL.");
|
||||
|
||||
if(srcLength == 0) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
/* reserve room for optional NUL terminator */
|
||||
if(reader->lineLength + srcLength >= reader->outBufferSize) {
|
||||
errorThrow("Line length exceeds output buffer size.");
|
||||
}
|
||||
|
||||
memoryCopy(reader->outBuffer + reader->lineLength, src, srcLength);
|
||||
reader->lineLength += srcLength;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
static void assetFileLineReaderTerminate(assetfilelinereader_t *reader) {
|
||||
assertNotNull(reader, "Reader cannot be NULL.");
|
||||
assertNotNull(reader->outBuffer, "Out buffer cannot be NULL.");
|
||||
assertTrue(
|
||||
reader->lineLength < reader->outBufferSize,
|
||||
"Line length exceeds out buffer."
|
||||
);
|
||||
reader->outBuffer[reader->lineLength] = '\0';
|
||||
}
|
||||
|
||||
static ssize_t assetFileLineReaderFindNewline(
|
||||
const assetfilelinereader_t *reader
|
||||
) {
|
||||
size_t i;
|
||||
|
||||
assertNotNull(reader, "Reader cannot be NULL.");
|
||||
assertNotNull(reader->readBuffer, "Read buffer cannot be NULL.");
|
||||
|
||||
for(i = reader->bufferStart; i < reader->bufferEnd; ++i) {
|
||||
if(reader->readBuffer[i] == '\n') {
|
||||
return (ssize_t)i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
errorret_t assetFileLineReaderFill(assetfilelinereader_t *reader) {
|
||||
|
||||
assertNotNull(reader, "Reader cannot be NULL.");
|
||||
assertNotNull(reader->file, "File cannot be NULL.");
|
||||
assertNotNull(reader->readBuffer, "Read buffer cannot be NULL.");
|
||||
|
||||
if(reader->eof) errorOk();
|
||||
|
||||
errorret_t ret;
|
||||
|
||||
size_t unreadBytes = assetFileLineReaderUnreadBytes(reader);
|
||||
|
||||
/* If buffer is fully consumed, refill from start. */
|
||||
if(unreadBytes == 0) {
|
||||
reader->bufferStart = 0;
|
||||
reader->bufferEnd = 0;
|
||||
|
||||
errorChain(assetFileRead(
|
||||
reader->file,
|
||||
reader->readBuffer,
|
||||
reader->readBufferSize
|
||||
));
|
||||
|
||||
if(reader->file->lastRead == 0) {
|
||||
reader->eof = true;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
reader->bufferStart = 0;
|
||||
reader->bufferEnd = reader->file->lastRead;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
/*
|
||||
* There are unread bytes left but no newline in them.
|
||||
* If bufferStart > 0, slide unread bytes to front so we can read more.
|
||||
* This only happens when necessary to make space.
|
||||
*/
|
||||
if(reader->bufferEnd == reader->readBufferSize) {
|
||||
if(reader->bufferStart == 0) {
|
||||
/*
|
||||
* Entire read buffer is unread and contains no newline.
|
||||
* Caller must have a large enough outBuffer to accumulate across fills,
|
||||
* so we consume these bytes into outBuffer before refilling.
|
||||
*/
|
||||
errorOk();
|
||||
}
|
||||
|
||||
memoryMove(
|
||||
reader->readBuffer,
|
||||
reader->readBuffer + reader->bufferStart,
|
||||
unreadBytes
|
||||
);
|
||||
reader->bufferStart = 0;
|
||||
reader->bufferEnd = unreadBytes;
|
||||
}
|
||||
|
||||
errorChain(assetFileRead(
|
||||
reader->file,
|
||||
reader->readBuffer + reader->bufferEnd,
|
||||
reader->readBufferSize - reader->bufferEnd
|
||||
));
|
||||
|
||||
if(reader->file->lastRead == 0) {
|
||||
reader->eof = true;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
reader->bufferEnd += reader->file->lastRead;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetFileLineReaderNext(assetfilelinereader_t *reader) {
|
||||
assertNotNull(reader, "Reader cannot be NULL.");
|
||||
assertNotNull(reader->file, "File cannot be NULL.");
|
||||
assertNotNull(reader->readBuffer, "Read buffer cannot be NULL.");
|
||||
assertNotNull(reader->outBuffer, "Out buffer cannot be NULL.");
|
||||
|
||||
reader->lineLength = 0;
|
||||
|
||||
for(;;) {
|
||||
ssize_t newlineIndex = assetFileLineReaderFindNewline(reader);
|
||||
|
||||
if(newlineIndex >= 0) {
|
||||
size_t chunkLength = (size_t)newlineIndex - reader->bufferStart;
|
||||
errorret_t ret;
|
||||
|
||||
/* strip CR in CRLF */
|
||||
if(
|
||||
chunkLength > 0 &&
|
||||
reader->readBuffer[(size_t)newlineIndex - 1] == '\r'
|
||||
) {
|
||||
chunkLength--;
|
||||
}
|
||||
|
||||
errorChain(assetFileLineReaderAppend(
|
||||
reader,
|
||||
reader->readBuffer + reader->bufferStart,
|
||||
chunkLength
|
||||
));
|
||||
|
||||
reader->bufferStart = (size_t)newlineIndex + 1;
|
||||
assetFileLineReaderTerminate(reader);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
if(assetFileLineReaderUnreadBytes(reader) > 0) {
|
||||
errorChain(assetFileLineReaderAppend(
|
||||
reader,
|
||||
assetFileLineReaderUnreadPtr(reader),
|
||||
assetFileLineReaderUnreadBytes(reader)
|
||||
));
|
||||
|
||||
reader->bufferStart = reader->bufferEnd;
|
||||
}
|
||||
|
||||
if(reader->eof) {
|
||||
if(reader->lineLength > 0) {
|
||||
assetFileLineReaderTerminate(reader);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorThrow("End of file reached.");
|
||||
}
|
||||
|
||||
errorChain(assetFileLineReaderFill(reader));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include <zip.h>
|
||||
|
||||
#define ASSET_FILE_NAME_MAX 48
|
||||
|
||||
typedef struct assetfile_s assetfile_t;
|
||||
|
||||
typedef errorret_t (*assetfileloader_t)(assetfile_t *file);
|
||||
|
||||
// Describes a file not yet loaded.
|
||||
typedef struct assetfile_s {
|
||||
char_t filename[ASSET_FILE_NAME_MAX];
|
||||
void *params;
|
||||
void *output;
|
||||
|
||||
zip_stat_t stat;
|
||||
zip_int64_t size;
|
||||
zip_int64_t position;
|
||||
zip_int64_t lastRead;
|
||||
zip_file_t *zipFile;
|
||||
} assetfile_t;
|
||||
|
||||
/**
|
||||
* Initializes the asset file structure in preparation for loading. This will
|
||||
* stat the file but not open the handle.
|
||||
*
|
||||
* @param file The asset file structure to initialize.
|
||||
* @param filename The name of the asset file to load.
|
||||
* @param params Optional loader params.
|
||||
* @param output Output pointer for the loader.
|
||||
* @return Error indicating success or failure.
|
||||
*/
|
||||
errorret_t assetFileInit(
|
||||
assetfile_t *file,
|
||||
const char_t *filename,
|
||||
void *params,
|
||||
void *output
|
||||
);
|
||||
|
||||
/**
|
||||
* Opens the asset file for reading. After opening the loader is responsible
|
||||
* for closing the file.
|
||||
*
|
||||
* @param file The asset file to open.
|
||||
* @return An error code if the file could not be opened.
|
||||
*/
|
||||
errorret_t assetFileOpen(assetfile_t *file);
|
||||
|
||||
/**
|
||||
* Rewind the file to the initial position.
|
||||
*
|
||||
* @param file The asset file to rewind.
|
||||
*/
|
||||
errorret_t assetFileRewind(assetfile_t *file);
|
||||
|
||||
/**
|
||||
* Read bytes from the asset file. Assumes the file has already been opened
|
||||
* prior to trying to read anything.
|
||||
*
|
||||
* @param file File to read from.
|
||||
* @param buffer Buffer to read the file data into., or NULL to skip bytes.
|
||||
* @param size Size of the buffer to read into.
|
||||
*/
|
||||
errorret_t assetFileRead(
|
||||
assetfile_t *file,
|
||||
void *buffer,
|
||||
const size_t bufferSize
|
||||
);
|
||||
|
||||
/**
|
||||
* Closes the asset file and releases any resources associated with it.
|
||||
*
|
||||
* @param file The asset file to close.
|
||||
* @return An error code if the file could not be closed properly.
|
||||
*/
|
||||
errorret_t assetFileClose(assetfile_t *file);
|
||||
|
||||
/**
|
||||
* Disposes the asset file structure, closing any open handles and zeroing
|
||||
* out the structure.
|
||||
*
|
||||
* @param file The asset file to dispose.
|
||||
* @return An error code if the file could not be disposed properly.
|
||||
*/
|
||||
errorret_t assetFileDispose(assetfile_t *file);
|
||||
|
||||
/**
|
||||
* Reads the entire contents of the asset file into a newly allocated buffer.
|
||||
* The caller is responsible for freeing the buffer with memoryFree.
|
||||
*
|
||||
* @param file The asset file to read. Must be initialized but not open.
|
||||
* @param outBuffer Receives a pointer to the allocated buffer.
|
||||
* @param outSize Receives the number of bytes written to the buffer.
|
||||
* @return An error code if the file could not be read.
|
||||
*/
|
||||
errorret_t assetFileReadEntire(
|
||||
assetfile_t *file,
|
||||
uint8_t **outBuffer,
|
||||
size_t *outSize
|
||||
);
|
||||
|
||||
typedef struct {
|
||||
assetfile_t *file;
|
||||
uint8_t *readBuffer;
|
||||
size_t readBufferSize;
|
||||
uint8_t *outBuffer;
|
||||
size_t outBufferSize;
|
||||
|
||||
// A
|
||||
size_t bufferStart;
|
||||
size_t bufferEnd;
|
||||
bool_t eof;//?
|
||||
|
||||
// Updated each reach:
|
||||
size_t lineLength;
|
||||
} assetfilelinereader_t;
|
||||
|
||||
/**
|
||||
* Initializes a line reader for the given asset file. The line reader will read
|
||||
* lines from the file into the provided line buffer, using the provided buffer
|
||||
* for reading chunks of the file.
|
||||
*
|
||||
* @param file The asset file to read from. Must already be opened.
|
||||
* @param readBuffer Buffer to use for reading chunks of the file.
|
||||
* @param readBufferSize Size of the read buffer.
|
||||
* @param outBuffer Buffer to read lines into. Lines will be null-terminated.
|
||||
* @param outBufferSize Size of the output buffer.
|
||||
* @return An initialized line reader structure.
|
||||
*/
|
||||
void assetFileLineReaderInit(
|
||||
assetfilelinereader_t *reader,
|
||||
assetfile_t *file,
|
||||
uint8_t *readBuffer,
|
||||
const size_t readBufferSize,
|
||||
uint8_t *outBuffer,
|
||||
const size_t outBufferSize
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads the next line from the asset file into the line buffer. The line
|
||||
* buffer is null-terminated and does not include the newline character.
|
||||
*
|
||||
* @param reader The line reader to read from.
|
||||
* @return An error code if a failure occurs, or errorOk() if a line was read
|
||||
* successfully. If the end of the file is reached, errorEndOfFile() is
|
||||
* returned.
|
||||
*/
|
||||
errorret_t assetFileLineReaderNext(assetfilelinereader_t *reader);
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
assetentry.c
|
||||
assetloader.c
|
||||
)
|
||||
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(locale)
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(dmf)
|
||||
add_subdirectory(script)
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetentry.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
|
||||
void assetEntryInit(
|
||||
assetentry_t *entry,
|
||||
const char_t *name,
|
||||
const assetloadertype_t type,
|
||||
assetloaderinput_t *input
|
||||
) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertStrLenMin(name, 1, "Name cannot be empty");
|
||||
assertStrLenMax(name, ASSET_FILE_NAME_MAX - 1, "Name too long");
|
||||
assertTrue(type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||
assertTrue(type < ASSET_LOADER_TYPE_COUNT, "Invalid loader type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
memoryZero(entry, sizeof(assetentry_t));
|
||||
stringCopy(entry->name, name, ASSET_FILE_NAME_MAX);
|
||||
entry->type = type;
|
||||
entry->state = ASSET_ENTRY_STATE_NOT_STARTED;
|
||||
if(input) {
|
||||
entry->inputData = *input;
|
||||
entry->input = &entry->inputData;
|
||||
} else {
|
||||
memoryZero(&entry->inputData, sizeof(assetloaderinput_t));
|
||||
entry->input = NULL;
|
||||
}
|
||||
refInit(&entry->refs, entry, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void assetEntryLock(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||
|
||||
refLock(&entry->refs);
|
||||
}
|
||||
|
||||
void assetEntryUnlock(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||
|
||||
refUnlock(&entry->refs);
|
||||
}
|
||||
|
||||
void assetEntryStartLoading(
|
||||
assetentry_t *entry,
|
||||
assetloading_t *loading
|
||||
) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||
assertTrue(
|
||||
entry->state == ASSET_ENTRY_STATE_NOT_STARTED,
|
||||
"Can only start loading from NOT_STARTED state."
|
||||
);
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
memoryZero(&loading->loading, sizeof(assetloaderloading_t));
|
||||
loading->type = entry->type;
|
||||
loading->entry = entry;
|
||||
// At this point the asset manager will manage this thing's loading
|
||||
}
|
||||
|
||||
errorret_t assetEntryDispose(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type.");
|
||||
assertTrue(entry->type < ASSET_LOADER_TYPE_COUNT, "Invalid loader type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
assertTrue(
|
||||
entry->refs.count == 0,
|
||||
"Asset entry still refed at dispose time."
|
||||
);
|
||||
|
||||
errorChain(ASSET_LOADER_CALLBACKS[entry->type].dispose(entry));
|
||||
memoryZero(entry, sizeof(assetentry_t));
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "asset/loader/assetloading.h"
|
||||
#include "util/ref.h"
|
||||
|
||||
typedef enum {
|
||||
ASSET_ENTRY_STATE_NOT_STARTED,
|
||||
ASSET_ENTRY_STATE_PENDING_ASYNC,
|
||||
ASSET_ENTRY_STATE_LOADING_ASYNC,
|
||||
ASSET_ENTRY_STATE_PENDING_SYNC,
|
||||
ASSET_ENTRY_STATE_LOADING_SYNC,
|
||||
ASSET_ENTRY_STATE_LOADED,
|
||||
ASSET_ENTRY_STATE_ERROR
|
||||
} assetentrystate_t;
|
||||
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
struct assetentry_s {
|
||||
char_t name[ASSET_FILE_NAME_MAX];
|
||||
assetloadertype_t type;
|
||||
assetloaderoutput_t data;
|
||||
assetentrystate_t state;
|
||||
ref_t refs;
|
||||
assetloaderinput_t *input;
|
||||
assetloaderinput_t inputData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes an asset entry with the given name and type. This does not load
|
||||
* the asset.
|
||||
*
|
||||
* @param entry The asset entry to initialize.
|
||||
* @param name The name of the asset, used as a key for loading and caching.
|
||||
* @param type The type of asset this entry represents.
|
||||
* @param input Data that will be passed to the loader about how it should load.
|
||||
*/
|
||||
void assetEntryInit(
|
||||
assetentry_t *entry,
|
||||
const char_t *name,
|
||||
const assetloadertype_t type,
|
||||
assetloaderinput_t *input
|
||||
);
|
||||
|
||||
/**
|
||||
* Locks an asset entry, preventing it from being freed until it is unlocked.
|
||||
*
|
||||
* @param entry The asset entry to lock.
|
||||
*/
|
||||
void assetEntryLock(assetentry_t *entry);
|
||||
|
||||
/**
|
||||
* Unlocks an asset entry, allowing it to be freed if there are no more locks.
|
||||
*
|
||||
* @param entry The asset entry to unlock.
|
||||
*/
|
||||
void assetEntryUnlock(assetentry_t *entry);
|
||||
|
||||
/**
|
||||
* Starts loading the given asset entry using an assetloading slot. This will
|
||||
* be called by the asset manager when it deems it's a good time to begin the
|
||||
* loading of this asset entry.
|
||||
*
|
||||
* Currently we return the error but in future this will not be returned.
|
||||
*
|
||||
* @param entry The asset entry to start loading.
|
||||
* @param loading The assetloading slot to use for loading this asset entry.
|
||||
* @return Any error that occurs during loading.
|
||||
*/
|
||||
void assetEntryStartLoading(assetentry_t *entry, assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Disposes an asset entry, freeing any resources it holds.
|
||||
*
|
||||
* @param entry The asset entry to dispose.
|
||||
* @return Any error that occurs during disposal.
|
||||
*/
|
||||
errorret_t assetEntryDispose(assetentry_t *entry);
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetloader.h"
|
||||
|
||||
assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
|
||||
[ASSET_LOADER_TYPE_NULL] = { 0 },
|
||||
|
||||
[ASSET_LOADER_TYPE_MESH] = {
|
||||
.loadSync = assetMeshLoaderSync,
|
||||
.loadAsync = assetMeshLoaderAsync,
|
||||
.dispose = assetMeshDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_MODEL] = {
|
||||
.loadSync = assetModelLoaderSync,
|
||||
.loadAsync = assetModelLoaderAsync,
|
||||
.dispose = assetModelDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_TEXTURE] = {
|
||||
.loadSync = assetTextureLoaderSync,
|
||||
.loadAsync = assetTextureLoaderAsync,
|
||||
.dispose = assetTextureDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_TILESET] = {
|
||||
.loadSync = assetTilesetLoaderSync,
|
||||
.loadAsync = assetTilesetLoaderAsync,
|
||||
.dispose = assetTilesetDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_LOCALE] = {
|
||||
.loadSync = assetLocaleLoaderSync,
|
||||
.loadAsync = assetLocaleLoaderAsync,
|
||||
.dispose = assetLocaleDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_JSON] = {
|
||||
.loadSync = assetJsonLoaderSync,
|
||||
.loadAsync = assetJsonLoaderAsync,
|
||||
.dispose = assetJsonDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_SCRIPT] = {
|
||||
.loadSync = assetScriptLoaderSync,
|
||||
.dispose = assetScriptDispose
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "asset/loader/dmf/assetmeshloader.h"
|
||||
#include "asset/loader/dmf/assetmodelloader.h"
|
||||
#include "asset/loader/display/assettextureloader.h"
|
||||
#include "asset/loader/display/assettilesetloader.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
#include "asset/loader/json/assetjsonloader.h"
|
||||
#include "asset/loader/script/assetscriptloader.h"
|
||||
|
||||
typedef enum {
|
||||
ASSET_LOADER_TYPE_NULL,
|
||||
|
||||
ASSET_LOADER_TYPE_MESH,
|
||||
ASSET_LOADER_TYPE_MODEL,
|
||||
ASSET_LOADER_TYPE_TEXTURE,
|
||||
ASSET_LOADER_TYPE_TILESET,
|
||||
ASSET_LOADER_TYPE_LOCALE,
|
||||
ASSET_LOADER_TYPE_JSON,
|
||||
ASSET_LOADER_TYPE_SCRIPT,
|
||||
|
||||
ASSET_LOADER_TYPE_COUNT
|
||||
} assetloadertype_t;
|
||||
|
||||
typedef union {
|
||||
assetmeshloaderloading_t mesh;
|
||||
assetmodelloaderloading_t model;
|
||||
assettextureloaderloading_t texture;
|
||||
assettilesetloaderloading_t tileset;
|
||||
assetlocaleloaderloading_t locale;
|
||||
assetjsonloaderloading_t json;
|
||||
} assetloaderloading_t;
|
||||
|
||||
typedef union {
|
||||
assetmeshoutput_t mesh;
|
||||
assetmodeloutput_t model;
|
||||
assettextureoutput_t texture;
|
||||
assettilesetoutput_t tileset;
|
||||
assetlocaleoutput_t locale;
|
||||
assetjsonoutput_t json;
|
||||
assetscriptoutput_t script;
|
||||
} assetloaderoutput_t;
|
||||
|
||||
typedef union {
|
||||
assettextureloaderinput_t texture;
|
||||
assettilesetloaderinput_t tileset;
|
||||
assetlocaleloaderinput_t locale;
|
||||
assetjsonloaderinput_t json;
|
||||
} assetloaderinput_t;
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef errorret_t (assetloadersynccallback_t)(assetloading_t *loading);
|
||||
typedef errorret_t (assetloaderasynccallback_t)(assetloading_t *loading);
|
||||
typedef errorret_t (assetloaderdisposecallback_t)(assetentry_t *entry);
|
||||
|
||||
typedef struct {
|
||||
assetloadersynccallback_t *loadSync;
|
||||
assetloaderasynccallback_t *loadAsync;
|
||||
assetloaderdisposecallback_t *dispose;
|
||||
} assetloadercallbacks_t;
|
||||
|
||||
extern assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT];
|
||||
|
||||
/**
|
||||
* Shorthand method to both chain an error (against the loader state) and to
|
||||
* set the asset entry state to error.
|
||||
*
|
||||
* @param loading The asset loading slot.
|
||||
* @param ret The error return value to check and chain if it's an error.
|
||||
*/
|
||||
#define assetLoaderErrorChain(loading, _expr) {\
|
||||
errorret_t _alec = (_expr); \
|
||||
if(errorIsNotOk(_alec)) { \
|
||||
(loading)->entry->state = ASSET_ENTRY_STATE_ERROR; \
|
||||
errorChain(_alec); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Like @ref assetLoaderErrorChain, but also frees `_ptr` (via memoryFree)
|
||||
* before chaining the error if `_expr` failed. Use this for any loader step
|
||||
* that runs after a buffer has already been allocated, so a later I/O
|
||||
* failure doesn't leak it.
|
||||
*
|
||||
* @param loading The asset loading slot.
|
||||
* @param _ptr A heap pointer to free if `_expr` fails.
|
||||
* @param _expr The error return value to check and chain if it's an error.
|
||||
*/
|
||||
#define assetLoaderErrorChainFree(loading, _ptr, _expr) {\
|
||||
errorret_t _alecf = (_expr); \
|
||||
if(errorIsNotOk(_alecf)) { \
|
||||
memoryFree(_ptr); \
|
||||
(loading)->entry->state = ASSET_ENTRY_STATE_ERROR; \
|
||||
errorChain(_alecf); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand method to both throw an error (against the loader state) and to
|
||||
* set the asset entry state to error.
|
||||
*
|
||||
* @param loading The asset loading slot.
|
||||
* @param ... Format string and arguments for the error message.
|
||||
*/
|
||||
#define assetLoaderErrorThrow(loading, ...) {\
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR; \
|
||||
errorThrow(__VA_ARGS__); \
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "assetloader.h"
|
||||
#include "asset/assetfile.h"
|
||||
#include "thread/threadmutex.h"
|
||||
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef struct assetloading_s {
|
||||
threadmutex_t mutex;
|
||||
assetloadertype_t type;
|
||||
assetentry_t *entry;
|
||||
assetloaderloading_t loading;
|
||||
} assetloading_t;
|
||||
|
||||
typedef errorret_t (assetloadingcallback_t)(assetloading_t *loading);
|
||||
|
||||
typedef struct {
|
||||
assetloadingcallback_t *loadSync;
|
||||
} assetloadingcallbacks_t;
|
||||
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
assettextureloader.c
|
||||
assettilesetloader.c
|
||||
)
|
||||
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assettextureloader.h"
|
||||
#include "assert/assert.h"
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#include "log/log.h"
|
||||
#include "util/endian.h"
|
||||
#include "asset/loader/assetloading.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
|
||||
stbi_io_callbacks ASSET_TEXTURE_STB_CALLBACKS = {
|
||||
.read = assetTextureReader,
|
||||
.skip = assetTextureSkipper,
|
||||
.eof = assetTextureEOF
|
||||
};
|
||||
|
||||
int assetTextureReader(void *user, char *data, int size) {
|
||||
assertNotNull(data, "Data buffer for stb_image callbacks cannot be NULL.");
|
||||
|
||||
assetfile_t *file = (assetfile_t*)user;
|
||||
assertNotNull(file, "Asset file in stb_image callbacks cannot be NULL.");
|
||||
|
||||
errorret_t ret = assetFileRead(file, data, (size_t)size);
|
||||
if(errorIsNotOk(ret)) {
|
||||
errorCatch(errorPrint(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return file->lastRead;
|
||||
}
|
||||
|
||||
void assetTextureSkipper(void *user, int n) {
|
||||
assetfile_t *file = (assetfile_t*)user;
|
||||
assertNotNull(file, "Asset file in stb_image callbacks cannot be NULL.");
|
||||
|
||||
errorret_t ret = assetFileRead(file, NULL, (size_t)n);
|
||||
if(errorIsNotOk(ret)) {
|
||||
errorCatch(errorPrint(ret));
|
||||
}
|
||||
}
|
||||
|
||||
int assetTextureEOF(void *user) {
|
||||
assetfile_t *file = (assetfile_t*)user;
|
||||
assertNotNull(file, "Asset file in stb_image callbacks cannot be NULL.");
|
||||
|
||||
return file->position >= file->size;
|
||||
}
|
||||
|
||||
errorret_t assetTextureLoaderAsync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertNotMainThread("Should be called from an async thread.");
|
||||
|
||||
// Only care about loading pixels.
|
||||
if(loading->loading.texture.state != ASSET_TEXTURE_LOADING_STATE_LOAD_PIXELS){
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Init the file
|
||||
assertNull(
|
||||
loading->loading.texture.data, "Pixels already defined?"
|
||||
);
|
||||
|
||||
assetfile_t *file = &loading->loading.texture.file;
|
||||
assetLoaderErrorChain(loading, assetFileInit(
|
||||
file,
|
||||
loading->entry->name,
|
||||
NULL,
|
||||
&loading->entry->data.texture
|
||||
));
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
|
||||
// Determine channels
|
||||
int channelsDesired;
|
||||
switch(loading->entry->inputData.texture) {
|
||||
case TEXTURE_FORMAT_RGBA:
|
||||
channelsDesired = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
assetLoaderErrorThrow(loading, "Bad texture format.");
|
||||
}
|
||||
|
||||
// Load image pixels.
|
||||
loading->loading.texture.data = stbi_load_from_callbacks(
|
||||
&ASSET_TEXTURE_STB_CALLBACKS,
|
||||
file,
|
||||
&loading->loading.texture.width,
|
||||
&loading->loading.texture.height,
|
||||
&loading->loading.texture.channels,
|
||||
channelsDesired
|
||||
);
|
||||
|
||||
// Close out the file.
|
||||
assetLoaderErrorChain(loading, assetFileClose(file));
|
||||
assetLoaderErrorChain(loading, assetFileDispose(file));
|
||||
|
||||
// Ensure we loaded correctly.
|
||||
if(loading->loading.texture.data == NULL) {
|
||||
const char_t *errorStr = stbi_failure_reason();
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Failed to load texture from file %s.", errorStr
|
||||
);
|
||||
}
|
||||
|
||||
// Fixes a specific bug probably with Dolphin but for now just assuming endian
|
||||
if(!isHostLittleEndian()) {
|
||||
stbi__vertical_flip(
|
||||
loading->loading.texture.data,
|
||||
loading->loading.texture.width,
|
||||
loading->loading.texture.height,
|
||||
loading->loading.texture.channels
|
||||
);
|
||||
}
|
||||
|
||||
loading->loading.texture.state = ASSET_TEXTURE_LOADING_STATE_CREATE_TEXTURE;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetTextureLoaderSync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
switch(loading->loading.texture.state) {
|
||||
case ASSET_TEXTURE_LOADING_STATE_INITIAL:
|
||||
loading->loading.texture.state = ASSET_TEXTURE_LOADING_STATE_LOAD_PIXELS;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC;
|
||||
errorOk();
|
||||
break;
|
||||
|
||||
case ASSET_TEXTURE_LOADING_STATE_CREATE_TEXTURE:
|
||||
break;
|
||||
|
||||
default:
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Create the texture.
|
||||
assertNotNull(
|
||||
loading->loading.texture.data, "Pixels should have been loaded by now."
|
||||
);
|
||||
|
||||
assetLoaderErrorChain(loading, textureInit(
|
||||
(texture_t*)&loading->entry->data.texture,
|
||||
loading->loading.texture.width,
|
||||
loading->loading.texture.height,
|
||||
loading->entry->inputData.texture,
|
||||
(texturedata_t){
|
||||
.rgbaColors = (color_t*)loading->loading.texture.data
|
||||
}
|
||||
));
|
||||
|
||||
// Free the pixels.
|
||||
stbi_image_free(loading->loading.texture.data);
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetTextureDispose(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Asset entry cannot be NULL");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
return textureDispose(&entry->data.texture);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "asset/assetfile.h"
|
||||
#include "display/texture/texture.h"
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
typedef textureformat_t assettextureloaderinput_t;
|
||||
|
||||
typedef enum {
|
||||
ASSET_TEXTURE_LOADING_STATE_INITIAL,
|
||||
ASSET_TEXTURE_LOADING_STATE_LOAD_PIXELS,
|
||||
ASSET_TEXTURE_LOADING_STATE_CREATE_TEXTURE,
|
||||
ASSET_TEXTURE_LOADING_STATE_DONE
|
||||
} assettextureloadingstate_t;
|
||||
|
||||
typedef struct {
|
||||
assetfile_t file;
|
||||
assettextureloadingstate_t state;
|
||||
|
||||
int channels, width, height;
|
||||
uint8_t *data;
|
||||
} assettextureloaderloading_t;
|
||||
|
||||
typedef texture_t assettextureoutput_t;
|
||||
|
||||
/**
|
||||
* STB image read callback for asset files.
|
||||
*
|
||||
* @param user User data passed to the callback, should be an assetfile_t*.
|
||||
* @param data Buffer to read the file data into.
|
||||
* @param size Size of the buffer to read into.
|
||||
* @return Number of bytes read, or -1 on error.
|
||||
*/
|
||||
int assetTextureReader(void *user, char *data, int size);
|
||||
|
||||
/**
|
||||
* STB image skip callback for asset files.
|
||||
*
|
||||
* @param user User data passed to the callback, should be an assetfile_t*.
|
||||
* @param n Number of bytes to skip in the file.
|
||||
*/
|
||||
void assetTextureSkipper(void *user, int n);
|
||||
|
||||
/**
|
||||
* STB image EOF callback for asset files.
|
||||
*
|
||||
* @param user User data passed to the callback, should be an assetfile_t*.
|
||||
* @return Non-zero if end of file has been reached, zero otherwise.
|
||||
*/
|
||||
int assetTextureEOF(void *user);
|
||||
|
||||
/**
|
||||
* Synchronous loader for texture assets.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure of the load operation.
|
||||
*/
|
||||
errorret_t assetTextureLoaderAsync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Synchronous loader for texture assets.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure of the load operation.
|
||||
*/
|
||||
errorret_t assetTextureLoaderSync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Disposer for texture assets.
|
||||
*
|
||||
* @param entry Asset entry containing the texture to dispose.
|
||||
* @return Error code indicating success or failure of the dispose operation.
|
||||
*/
|
||||
errorret_t assetTextureDispose(assetentry_t *entry);
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assettilesetloader.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/endian.h"
|
||||
#include "asset/loader/assetloading.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
|
||||
errorret_t assetTilesetLoaderAsync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertNotMainThread("Should be called from an async thread.");
|
||||
|
||||
if(loading->loading.tileset.state != ASSET_TILESET_LOADING_STATE_READ_FILE) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
assertNull(loading->loading.tileset.data, "Data already defined?");
|
||||
|
||||
assetfile_t *file = &loading->loading.tileset.file;
|
||||
assetLoaderErrorChain(loading,
|
||||
assetFileInit(file, loading->entry->name, NULL, NULL)
|
||||
);
|
||||
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
|
||||
uint8_t *data = memoryAllocate(file->size);
|
||||
assetLoaderErrorChainFree(loading, data, assetFileRead(file, data, file->size));
|
||||
assetLoaderErrorChainFree(loading, data, assetFileClose(file));
|
||||
assetLoaderErrorChainFree(loading, data, assetFileDispose(file));
|
||||
assertTrue(
|
||||
file->lastRead == file->size,
|
||||
"Failed to read entire tileset file."
|
||||
);
|
||||
|
||||
loading->loading.tileset.data = data;
|
||||
loading->loading.tileset.state = ASSET_TILESET_LOADING_STATE_PARSE;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetTilesetLoaderSync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertTrue(loading->type == ASSET_LOADER_TYPE_TILESET, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
switch(loading->loading.tileset.state) {
|
||||
case ASSET_TILESET_LOADING_STATE_INITIAL:
|
||||
loading->loading.tileset.state = ASSET_TILESET_LOADING_STATE_READ_FILE;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC;
|
||||
errorOk();
|
||||
break;
|
||||
|
||||
case ASSET_TILESET_LOADING_STATE_PARSE:
|
||||
break;
|
||||
|
||||
default:
|
||||
errorOk();
|
||||
}
|
||||
|
||||
uint8_t *data = loading->loading.tileset.data;
|
||||
assertNotNull(data, "Tileset data should have been loaded by now.");
|
||||
|
||||
tileset_t *out = &loading->entry->data.tileset;
|
||||
|
||||
if(data[0] != 'D' || data[1] != 'T' || data[2] != 'F') {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Invalid tileset header");
|
||||
}
|
||||
|
||||
if(data[3] != 0x00) {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Unsupported tileset version");
|
||||
}
|
||||
|
||||
out->tileWidth = endianLittleToHost16(*(uint16_t *)(data + 4));
|
||||
out->tileHeight = endianLittleToHost16(*(uint16_t *)(data + 6));
|
||||
out->columns = endianLittleToHost16(*(uint16_t *)(data + 8));
|
||||
out->rows = endianLittleToHost16(*(uint16_t *)(data + 10));
|
||||
|
||||
if(out->tileWidth == 0) {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Tile width cannot be 0");
|
||||
}
|
||||
if(out->tileHeight == 0) {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Tile height cannot be 0");
|
||||
}
|
||||
if(out->columns == 0) {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Column count cannot be 0");
|
||||
}
|
||||
if(out->rows == 0) {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Row count cannot be 0");
|
||||
}
|
||||
|
||||
out->uv[0] = endianLittleToHostFloat(*(float *)(data + 16));
|
||||
out->uv[1] = endianLittleToHostFloat(*(float *)(data + 20));
|
||||
|
||||
if(out->uv[0] < 0.0f || out->uv[0] > 1.0f) {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Invalid u0 value in tileset");
|
||||
}
|
||||
|
||||
if(out->uv[1] < 0.0f || out->uv[1] > 1.0f) {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Invalid v0 value in tileset");
|
||||
}
|
||||
|
||||
out->tileCount = out->columns * out->rows;
|
||||
memoryFree(data);
|
||||
loading->loading.tileset.data = NULL;
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetTilesetDispose(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertTrue(entry->type == ASSET_LOADER_TYPE_TILESET, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "asset/assetfile.h"
|
||||
#include "display/texture/tileset.h"
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef struct {
|
||||
void *nothing;
|
||||
} assettilesetloaderinput_t;
|
||||
|
||||
typedef enum {
|
||||
ASSET_TILESET_LOADING_STATE_INITIAL,
|
||||
ASSET_TILESET_LOADING_STATE_READ_FILE,
|
||||
ASSET_TILESET_LOADING_STATE_PARSE,
|
||||
ASSET_TILESET_LOADING_STATE_DONE
|
||||
} assettilesetloadingstate_t;
|
||||
|
||||
typedef struct {
|
||||
assetfile_t file;
|
||||
assettilesetloadingstate_t state;
|
||||
|
||||
uint8_t *data;
|
||||
} assettilesetloaderloading_t;
|
||||
|
||||
typedef tileset_t assettilesetoutput_t;
|
||||
|
||||
/**
|
||||
* Asynchronous loader for tileset assets. Reads the raw DTF file bytes into
|
||||
* the loading buffer so the sync phase can parse without blocking the main
|
||||
* thread on I/O.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure of the load operation.
|
||||
*/
|
||||
errorret_t assetTilesetLoaderAsync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Synchronous loader for tileset assets. Parses the DTF binary previously
|
||||
* read by the async phase and populates the output tileset_t.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure of the load operation.
|
||||
*/
|
||||
errorret_t assetTilesetLoaderSync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Disposer for tileset assets.
|
||||
*
|
||||
* @param entry Asset entry containing the tileset to dispose.
|
||||
* @return Error code indicating success or failure of the dispose operation.
|
||||
*/
|
||||
errorret_t assetTilesetDispose(assetentry_t *entry);
|
||||
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
assetmeshloader.c
|
||||
assetmodelloader.c
|
||||
)
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetmeshloader.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/endian.h"
|
||||
#include "asset/loader/assetloading.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
|
||||
errorret_t assetMeshLoaderAsync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertNotMainThread("Should be called from an async thread.");
|
||||
|
||||
if(loading->loading.mesh.state != ASSET_MESH_LOADING_STATE_READ_FILE) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
assertNull(loading->loading.mesh.data, "Data already defined?");
|
||||
|
||||
assetfile_t *file = &loading->loading.mesh.file;
|
||||
assetLoaderErrorChain(loading,
|
||||
assetFileInit(file, loading->entry->name, NULL, NULL)
|
||||
);
|
||||
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
|
||||
uint8_t *raw = memoryAllocate(file->size);
|
||||
assetLoaderErrorChainFree(loading, raw, assetFileRead(file, raw, file->size));
|
||||
assetLoaderErrorChainFree(loading, raw, assetFileClose(file));
|
||||
assetLoaderErrorChainFree(loading, raw, assetFileDispose(file));
|
||||
assertTrue(file->lastRead == file->size, "Failed to read entire DMF file.");
|
||||
|
||||
if(raw[0] != 'D' || raw[1] != 'M' || raw[2] != 'F') {
|
||||
memoryFree(raw);
|
||||
assetLoaderErrorThrow(loading, "Invalid DMF file header");
|
||||
}
|
||||
|
||||
uint32_t version = endianLittleToHost32(*(uint32_t *)(raw + 4));
|
||||
if(version != ASSET_MESH_FILE_VERSION) {
|
||||
memoryFree(raw);
|
||||
assetLoaderErrorThrow(loading, "Unsupported DMF version %u", version);
|
||||
}
|
||||
|
||||
uint32_t vertCount = endianLittleToHost32(*(uint32_t *)(raw + 8));
|
||||
meshvertex_t *vertices = NULL;
|
||||
if(vertCount > 0) {
|
||||
// 32-byte (cache-line) aligned: GX_SetArray + DCFlushRange on Dolphin
|
||||
// require this for the DMA'd vertex data to actually reach the GPU
|
||||
// coherently. Static compiled-in vertex arrays happen to get this from
|
||||
// the linker; a plain memoryAllocate here would not.
|
||||
vertices = memoryAlign(32, vertCount * sizeof(meshvertex_t));
|
||||
memoryCopy(vertices, raw + 12, vertCount * sizeof(meshvertex_t));
|
||||
|
||||
for(uint32_t v = 0; v < vertCount; v++) {
|
||||
vertices[v].uv[0] = endianLittleToHostFloat(vertices[v].uv[0]);
|
||||
vertices[v].uv[1] = endianLittleToHostFloat(vertices[v].uv[1]);
|
||||
vertices[v].pos[0] = endianLittleToHostFloat(vertices[v].pos[0]);
|
||||
vertices[v].pos[1] = endianLittleToHostFloat(vertices[v].pos[1]);
|
||||
vertices[v].pos[2] = endianLittleToHostFloat(vertices[v].pos[2]);
|
||||
}
|
||||
}
|
||||
memoryFree(raw);
|
||||
|
||||
loading->loading.mesh.vertCount = vertCount;
|
||||
loading->loading.mesh.data = (uint8_t *)vertices;
|
||||
loading->loading.mesh.state = ASSET_MESH_LOADING_STATE_CREATE_MESH;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetMeshLoaderSync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertTrue(loading->type == ASSET_LOADER_TYPE_MESH, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
switch(loading->loading.mesh.state) {
|
||||
case ASSET_MESH_LOADING_STATE_INITIAL:
|
||||
loading->loading.mesh.state = ASSET_MESH_LOADING_STATE_READ_FILE;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC;
|
||||
errorOk();
|
||||
break;
|
||||
|
||||
case ASSET_MESH_LOADING_STATE_CREATE_MESH:
|
||||
break;
|
||||
|
||||
default:
|
||||
errorOk();
|
||||
}
|
||||
|
||||
uint32_t vertCount = loading->loading.mesh.vertCount;
|
||||
meshvertex_t *vertices = (meshvertex_t *)loading->loading.mesh.data;
|
||||
loading->loading.mesh.data = NULL;
|
||||
|
||||
assetmeshoutput_t *out = &loading->entry->data.mesh;
|
||||
out->vertices = vertices;
|
||||
|
||||
if(vertCount == 0) {
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t ret = meshInit(
|
||||
&out->mesh,
|
||||
MESH_PRIMITIVE_TYPE_TRIANGLES,
|
||||
(int32_t)vertCount,
|
||||
out->vertices
|
||||
);
|
||||
if(errorIsNotOk(ret)) {
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
memoryFree(out->vertices);
|
||||
out->vertices = NULL;
|
||||
errorChain(ret);
|
||||
}
|
||||
out->meshInitialized = true;
|
||||
|
||||
ret = meshFlush(&out->mesh, 0, (int32_t)vertCount);
|
||||
if(errorIsNotOk(ret)) {
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
meshDispose(&out->mesh);
|
||||
out->meshInitialized = false;
|
||||
memoryFree(out->vertices);
|
||||
out->vertices = NULL;
|
||||
errorChain(ret);
|
||||
}
|
||||
|
||||
#if defined(DUSK_OPENGL) && !defined(DUSK_OPENGL_LEGACY)
|
||||
// VBO owns the data now; CPU copy is no longer needed. The platform
|
||||
// mesh object itself still needs meshDispose later - tracked via
|
||||
// out->meshInitialized, independent of the CPU buffer's lifetime.
|
||||
memoryFree(out->vertices);
|
||||
out->vertices = NULL;
|
||||
#endif
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetMeshDispose(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertTrue(entry->type == ASSET_LOADER_TYPE_MESH, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
assetmeshoutput_t *out = &entry->data.mesh;
|
||||
if(out->meshInitialized) {
|
||||
errorChain(meshDispose(&out->mesh));
|
||||
out->meshInitialized = false;
|
||||
}
|
||||
if(out->vertices != NULL) {
|
||||
memoryFree(out->vertices);
|
||||
out->vertices = NULL;
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "asset/assetfile.h"
|
||||
#include "display/mesh/mesh.h"
|
||||
|
||||
#define ASSET_MESH_FILE_VERSION 1
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef enum {
|
||||
ASSET_MESH_LOADING_STATE_INITIAL,
|
||||
ASSET_MESH_LOADING_STATE_READ_FILE,
|
||||
ASSET_MESH_LOADING_STATE_CREATE_MESH,
|
||||
ASSET_MESH_LOADING_STATE_DONE
|
||||
} assetmeshloadingstate_t;
|
||||
|
||||
typedef struct {
|
||||
assetfile_t file;
|
||||
assetmeshloadingstate_t state;
|
||||
uint32_t vertCount;
|
||||
uint8_t *data;
|
||||
} assetmeshloaderloading_t;
|
||||
|
||||
typedef struct {
|
||||
mesh_t mesh;
|
||||
bool_t meshInitialized;
|
||||
meshvertex_t *vertices;
|
||||
} assetmeshoutput_t;
|
||||
|
||||
/**
|
||||
* Asynchronous loader for DMF mesh assets. Reads the file, validates the
|
||||
* header, and prepares a ready-to-use vertex buffer so the sync phase only
|
||||
* needs to upload to the GPU.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure.
|
||||
*/
|
||||
errorret_t assetMeshLoaderAsync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Synchronous loader for DMF mesh assets. Initializes the mesh from the
|
||||
* vertex buffer prepared by the async phase and flushes it to the GPU.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure.
|
||||
*/
|
||||
errorret_t assetMeshLoaderSync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Disposer for DMF mesh assets. Disposes the mesh and frees the vertex
|
||||
* buffer.
|
||||
*
|
||||
* @param entry Asset entry containing the mesh data to dispose.
|
||||
* @return Error code indicating success or failure.
|
||||
*/
|
||||
errorret_t assetMeshDispose(assetentry_t *entry);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user