RedCore
Loading...
Searching...
No Matches
SpriteUtil.h
Go to the documentation of this file.
1#pragma once
2
3#include <actor/Actor.h>
4#include <telkin/Print.h>
5
6namespace red {
7
8 class SpriteUtil : private Actor {
9 public:
10 [[nodiscard]]
11 static u8 getNybble1(Actor* target) {
12 return static_cast<SpriteUtil*>(target)->mSwitchFlag1 >> 0x04 & 0xF;
13 }
14
15 [[nodiscard]]
16 static u8 getNybble2(Actor* target) {
17 return static_cast<SpriteUtil*>(target)->mSwitchFlag1 >> 0x00 & 0xF;
18 }
19
20 [[nodiscard]]
21 static u8 getNybble3(Actor* target) {
22 return static_cast<SpriteUtil*>(target)->mSwitchFlag0 >> 0x04 & 0xF;
23 }
24
25 [[nodiscard]]
26 static u8 getNybble4(Actor* target) {
27 return static_cast<SpriteUtil*>(target)->mSwitchFlag0 >> 0x00 & 0xF;
28 }
29
30 [[nodiscard]]
31 static u8 getNybble5(Actor* target) {
32 return static_cast<SpriteUtil*>(target)->mParam0 >> 0x1C & 0xF;
33 }
34
35 [[nodiscard]]
36 static u8 getNybble6(Actor* target) {
37 return static_cast<SpriteUtil*>(target)->mParam0 >> 0x18 & 0xF;
38 }
39
40 [[nodiscard]]
41 static u8 getNybble7(Actor* target) {
42 return static_cast<SpriteUtil*>(target)->mParam0 >> 0x14 & 0xF;
43 }
44
45 [[nodiscard]]
46 static u8 getNybble8(Actor* target) {
47 return static_cast<SpriteUtil*>(target)->mParam0 >> 0x10 & 0xF;
48 }
49
50 [[nodiscard]]
51 static u8 getNybble9(Actor* target) {
52 return static_cast<SpriteUtil*>(target)->mParam0 >> 0x0C & 0xF;
53 }
54
55 [[nodiscard]]
56 static u8 getNybble10(Actor* target) {
57 return static_cast<SpriteUtil*>(target)->mParam0 >> 0x08 & 0xF;
58 }
59
60 [[nodiscard]]
61 static u8 getNybble11(Actor* target) {
62 return static_cast<SpriteUtil*>(target)->mParam0 >> 0x04 & 0xF;
63 }
64
65 [[nodiscard]]
66 static u8 getNybble12(Actor* target) {
67 return static_cast<SpriteUtil*>(target)->mParam0 >> 0x00 & 0xF;
68 }
69
70 [[nodiscard]]
71 static u8 getNybble13(Actor* target) {
72 return static_cast<SpriteUtil*>(target)->mParam1 >> 0x1C & 0xF;
73 }
74
75 [[nodiscard]]
76 static u8 getNybble14(Actor* target) {
77 return static_cast<SpriteUtil*>(target)->mParam1 >> 0x18 & 0xF;
78 }
79
80 [[nodiscard]]
81 static u8 getNybble15(Actor* target) {
82 return static_cast<SpriteUtil*>(target)->mParam1 >> 0x14 & 0xF;
83 }
84
85 [[nodiscard]]
86 static u8 getNybble16(Actor* target) {
87 return static_cast<SpriteUtil*>(target)->mParam1 >> 0x10 & 0xF;
88 }
89
90 [[nodiscard]]
91 static u8 getNybble17(Actor* target) {
92 return static_cast<SpriteUtil*>(target)->mParam1 >> 0x0C & 0xF;
93 }
94
95 [[nodiscard]]
96 static u8 getNybble18(Actor* target) {
97 return static_cast<SpriteUtil*>(target)->mParam1 >> 0x08 & 0xF;
98 }
99
100 [[nodiscard]]
101 static u8 getNybble19(Actor* target) {
102 return static_cast<SpriteUtil*>(target)->mParam1 >> 0x04 & 0xF;
103 }
104
105 [[nodiscard]]
106 static u8 getNybble20(Actor* target) {
107 return static_cast<SpriteUtil*>(target)->mParam1 >> 0x00 & 0xF;
108 }
109
110 [[nodiscard]]
111 static u8 getNybble21(Actor* target) {
112 return static_cast<SpriteUtil*>(target)->mParamEx.course.movement_id >> 0x04 & 0xF;
113 }
114
115 [[nodiscard]]
116 static u8 getNybble22(Actor* target) {
117 return static_cast<SpriteUtil*>(target)->mParamEx.course.movement_id >> 0x00 & 0xF;
118 }
119
120 [[nodiscard]]
121 static u8 getNybble23(Actor* target) {
122 return static_cast<SpriteUtil*>(target)->mParamEx.course.link_id >> 0x04 & 0xF;
123 }
124
125 [[nodiscard]]
126 static u8 getNybble24(Actor* target) {
127 return static_cast<SpriteUtil*>(target)->mParamEx.course.link_id >> 0x00 & 0xF;
128 };
129
130 //* Advanced functions
131
132 /**
133 * @brief Extract the combined value of a range of up to 8 sequential nybbles from the given actor's full 24 nybbles of spritedata.
134 *
135 * @details Max. output is limited to a 32-bit value (8 nybbles) for performance, if the requested range exceeds 8 nybbles in size or is invalid, @c OSFatal is called.
136 * Use multiple @c getNybbleRange calls and manually combine their outputs for values greater than 32 bits.
137 *
138 * @param from 1-indexed left-to-right inclusive nybble index to start the range at. (1-24)
139 * @param end 1-indexed left-to-right inclusive nybble index to end the range at. (1-24)
140 */
141 [[nodiscard]]
142 static u32 getNybbleRange(Actor* target, u8 from, u8 end) {
143 const u8 nybbleCount = end - (from - 1);
144 if (nybbleCount == 0 || nybbleCount > 8 || from > 24 || end > 24) {
145 tk::fatal("red::SpriteUtil::getNybbleRange called with an invalid nybble range. (%i - %i)", from, end);
146 }
147 return getBitRange(target, from * 4 - 4, end * 4);
148 }
149
150 /**
151 * @brief Extract the value of an arbitrary 32-bit range of bits from the given actor's full 96 bits of spritedata which make up all nybbles 1-24.
152 * *Bit layout cheatsheet:*
153 * nybbles 1-4 (bits 0-15), nybbles 5-12 (bits 16-47), nybbles 13-20 (bits 48-79), nybbles 21-24 (bits 80-96)
154 *
155 * @details Max. output is limited to a 32-bit value for performance, if the requested range exceeds 32 bits in size or is invalid, @c OSFatal is called.
156 * Use the other overload if you need to extract more than 32 bits.
157 *
158 * @param from 0-indexed Big Endian (left-to-right) *inclusive* bit index to start the range at. (0-96)
159 * @param end 0-indexed Big Endian (left-to-right) *exclusive* bit index to end the range at. (0-96)
160 */
161 [[nodiscard]]
162 static u32 getBitRange(const Actor* target, u8 from, u8 end) {
163 const u8 bitsCount = end - from;
164 if (bitsCount == 0 || bitsCount > 32 || from > 96 || end > 96) {
165 tk::fatal("red::SpriteUtil::getBitRange called with an invalid bit range. (%i - %i)", from, end);
166 }
167
168 const SpriteUtil* actor = static_cast<const SpriteUtil*>(target);
169
170 // Fast path 1: The entire range fits within the 16-bit mSwitchFlags fields (bits 0 - 15)
171 if (end <= 16) {
172 u32 flags = (static_cast<u32>(actor->mSwitchFlag1) << 8) | actor->mSwitchFlag0;
173 return getBitRangeInt32(flags << 16, from, end);
174 }
175 // Fast path 2: The entire range fits within mParam0 field (bits 16 - 47)
176 if (from >= 16 && end <= 48) {
177 return getBitRangeInt32(actor->mParam0, from - 16, end - 16);
178 }
179 // Fast path 3: The entire range fits within mParam1 field (bits 48 - 79)
180 if (from >= 48 && end <= 80) {
181 return getBitRangeInt32(actor->mParam1, from - 48, end - 48);
182 }
183 // Fast path 4: The entire range fits within the 16-bit mParamEx fields (bits 80 - 95)
184 if (from >= 80) {
185 u32 paramEx = (static_cast<u32>(actor->mParamEx.course.movement_id) << 8) | actor->mParamEx.course.link_id;
186 return getBitRangeInt32(paramEx << 16, from - 80, end - 80);
187 }
188
189 // Slow path: The range crosses a boundary between two fields.
190 // Because bitsNum <= 32, it can only cross exactly 1 of the 3 major boundaries.
191 u32 upperField = 0;
192 u32 lowerField = 0;
193 u8 boundaryBit = 0;
194
195 if (from < 16) { // Crosses mSwitchFlags <-> mParam0 boundary at bit 16
196 boundaryBit = 16;
197 upperField = (static_cast<u32>(actor->mSwitchFlag1) << 8) | actor->mSwitchFlag0;
198 upperField <<= 16; // Align to MSB of a 32-bit window
199 lowerField = actor->mParam0;
200 }
201 else if (from < 48) { // Crosses mParam0 <-> mParam1 boundary at bit 48
202 boundaryBit = 48;
203 upperField = actor->mParam0;
204 lowerField = actor->mParam1;
205 }
206 else { // Crosses mParam1 <-> mParamEx boundary at bit 80
207 boundaryBit = 80;
208 upperField = actor->mParam1;
209 lowerField = (static_cast<u32>(actor->mParamEx.course.movement_id) << 8) | actor->mParamEx.course.link_id;
210 lowerField <<= 16; // Align to MSB of a 32-bit window
211 }
212
213 // Extract the pieces from both fields and stitch them together
214 const u8 upperBitsCount = boundaryBit - from;
215 const u8 lowerBitsCount = bitsCount - upperBitsCount;
216
217 const u32 upperValue = getBitRangeInt32(upperField, 32 - upperBitsCount, 32);
218 const u32 lowerValue = getBitRangeInt32(lowerField, 0, lowerBitsCount);
219
220 return (upperValue << lowerBitsCount) | lowerValue;
221 }
222
223 /**
224 * @brief Extract a compile-time defined range of bits (up to 96) from the given actor's spritedata.
225 * *Bit layout cheatsheet:*
226 * nybbles 1-4 (bits 0-15), nybbles 5-12 (bits 16-47), nybbles 13-20 (bits 48-79), nybbles 21-24 (bits 80-96)
227 *
228 * @details The output is right-aligned in the provided buffer. All memory safety checks and
229 * bounds validations are resolved at compile-time. Fast/Slow paths are optimized statically.
230 *
231 * @param target The actor whose spritedata will be extracted.
232 * @param out Reference to a buffer where the extracted bits will be written (size deduced automatically).
233 * @tparam TFrom 0-indexed Big Endian *inclusive* start bit index.
234 * @tparam TEnd 0-indexed Big Endian *exclusive* end bit index.
235 * @tparam TOutBytes Deduced byte size of the provided output array.
236 */
237 template <u8 TFrom, u8 TEnd, s32 TOutBytes>
238 static void getBitRange(const Actor* target, u8 (&out)[TOutBytes]) {
239 static_assert(TFrom <= 96 && TEnd <= 96, "getBitRange: Bit range out of bounds (max 96).");
240 static_assert(TEnd > TFrom, "getBitRange: End index must be strictly greater than From index.");
241
242 constexpr u8 bitsCount = TEnd - TFrom;
243 constexpr u8 outBytesCount = (bitsCount + 7) / 8;
244
245 static_assert(TOutBytes >= outBytesCount, "getBitRange: Output buffer is too small for the requested bit range.");
246
247 // Fast Path: The range is <= 32 bits, delegate to the 32-bit overload
248 if constexpr (bitsCount <= 32) {
249 u32 val = getBitRange(target, TFrom, TEnd);
250 for (u8 i = 0; i < outBytesCount; i++) {
251 out[i] = static_cast<u8>((val >> ((outBytesCount - 1 - i) * 8)) & 0xFF);
252 }
253 return;
254 } else { // Slow Path
255 const SpriteUtil* actor = static_cast<const SpriteUtil*>(target);
256
257 u8 src[12];
258 src[0] = actor->mSwitchFlag1;
259 src[1] = actor->mSwitchFlag0;
260 src[2] = static_cast<u8>((actor->mParam0 >> 24) & 0xFF);
261 src[3] = static_cast<u8>((actor->mParam0 >> 16) & 0xFF);
262 src[4] = static_cast<u8>((actor->mParam0 >> 8) & 0xFF);
263 src[5] = static_cast<u8>( actor->mParam0 & 0xFF);
264 src[6] = static_cast<u8>((actor->mParam1 >> 24) & 0xFF);
265 src[7] = static_cast<u8>((actor->mParam1 >> 16) & 0xFF);
266 src[8] = static_cast<u8>((actor->mParam1 >> 8) & 0xFF);
267 src[9] = static_cast<u8>( actor->mParam1 & 0xFF);
268 src[10] = actor->mParamEx.course.movement_id;
269 src[11] = actor->mParamEx.course.link_id;
270
271 constexpr u8 paddingBits = (8 - (bitsCount % 8)) % 8;
272
273 for (u8 i = 0; i < outBytesCount; i++) {
274 out[i] = 0;
275 }
276
277 for (u8 i = 0; i < bitsCount; i++) {
278 const u8 srcBitIdx = TFrom + i;
279 const u8 destBitIdx = paddingBits + i;
280
281 const u8 srcBit = (src[srcBitIdx / 8] >> (7 - (srcBitIdx % 8))) & 1;
282 out[destBitIdx / 8] |= (srcBit << (7 - (destBitIdx % 8)));
283 }
284 }
285 }
286
287 private:
288 /** ! Uses MSB (big endian) logic: a u16 will be treated as having bits 0-16 empty if not pre-emptively shifted up. */
289 [[nodiscard]]
290 static u32 getBitRangeInt32(u32 num, u8 from, u8 end) {
291 const u8 bitsCount = end - from;
292 if (bitsCount >= 32) {
293 return num;
294 }
295 const u32 shifted = num >> (32 - end);
296 const u32 mask = (1U << bitsCount) - 1;
297 return shifted & mask;
298 }
299 };
300
301} // namespace red
Definition SpriteUtil.h:8
static u8 getNybble23(Actor *target)
Definition SpriteUtil.h:121
static u8 getNybble20(Actor *target)
Definition SpriteUtil.h:106
static u8 getNybble5(Actor *target)
Definition SpriteUtil.h:31
static u8 getNybble3(Actor *target)
Definition SpriteUtil.h:21
static u8 getNybble8(Actor *target)
Definition SpriteUtil.h:46
static u8 getNybble16(Actor *target)
Definition SpriteUtil.h:86
static u8 getNybble15(Actor *target)
Definition SpriteUtil.h:81
static u8 getNybble6(Actor *target)
Definition SpriteUtil.h:36
static u32 getNybbleRange(Actor *target, u8 from, u8 end)
Extract the combined value of a range of up to 8 sequential nybbles from the given actor's full 24 ny...
Definition SpriteUtil.h:142
static u8 getNybble18(Actor *target)
Definition SpriteUtil.h:96
static u8 getNybble21(Actor *target)
Definition SpriteUtil.h:111
static u8 getNybble2(Actor *target)
Definition SpriteUtil.h:16
static u32 getBitRange(const Actor *target, u8 from, u8 end)
Extract the value of an arbitrary 32-bit range of bits from the given actor's full 96 bits of sprited...
Definition SpriteUtil.h:162
static u8 getNybble4(Actor *target)
Definition SpriteUtil.h:26
static u8 getNybble1(Actor *target)
Definition SpriteUtil.h:11
static u8 getNybble11(Actor *target)
Definition SpriteUtil.h:61
static u8 getNybble10(Actor *target)
Definition SpriteUtil.h:56
static u8 getNybble22(Actor *target)
Definition SpriteUtil.h:116
static u8 getNybble17(Actor *target)
Definition SpriteUtil.h:91
static u8 getNybble9(Actor *target)
Definition SpriteUtil.h:51
static u8 getNybble7(Actor *target)
Definition SpriteUtil.h:41
static u8 getNybble19(Actor *target)
Definition SpriteUtil.h:101
static u8 getNybble13(Actor *target)
Definition SpriteUtil.h:71
static void getBitRange(const Actor *target, u8(&out)[TOutBytes])
Extract a compile-time defined range of bits (up to 96) from the given actor's spritedata....
Definition SpriteUtil.h:238
static u8 getNybble24(Actor *target)
Definition SpriteUtil.h:126
static u8 getNybble14(Actor *target)
Definition SpriteUtil.h:76
static u8 getNybble12(Actor *target)
Definition SpriteUtil.h:66
Definition ActorDonutBlock.h:5