yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_object_encoding_test.cc
Go to the documentation of this file.
1// test/zelda3/dungeon/room_object_encoding_test.cc
2// Unit tests for Phase 1, Task 1.1: Object Encoding/Decoding
3//
4// These tests verify that the object encoding and decoding functions work
5// correctly for all three object types (Type1, Type2, Type3) based on
6// ZScream's proven implementation.
7
9
10#include <gtest/gtest.h>
11
12namespace yaze {
13namespace zelda3 {
14namespace {
15
16// ============================================================================
17// Object Type Detection Tests
18// ============================================================================
19
20TEST(RoomObjectEncodingTest, DetermineObjectTypeType1) {
21 // Type1: b1 < 0xFC, b3 < 0xF8
22 EXPECT_EQ(RoomObject::DetermineObjectType(0x28, 0x10), 1);
23 EXPECT_EQ(RoomObject::DetermineObjectType(0x50, 0x42), 1);
24 EXPECT_EQ(RoomObject::DetermineObjectType(0xFB, 0xF7), 1);
25}
26
27TEST(RoomObjectEncodingTest, DetermineObjectTypeType2) {
28 // Type2: b1 >= 0xFC, b3 < 0xF8
29 EXPECT_EQ(RoomObject::DetermineObjectType(0xFC, 0x42), 2);
30 EXPECT_EQ(RoomObject::DetermineObjectType(0xFD, 0x25), 2);
31 EXPECT_EQ(RoomObject::DetermineObjectType(0xFF, 0x00), 2);
32}
33
34TEST(RoomObjectEncodingTest, DetermineObjectTypeType3) {
35 // Type3: b3 >= 0xF8
36 EXPECT_EQ(RoomObject::DetermineObjectType(0x28, 0xF8), 3);
37 EXPECT_EQ(RoomObject::DetermineObjectType(0x50, 0xF9), 3);
38 EXPECT_EQ(RoomObject::DetermineObjectType(0xFC, 0xFF), 3);
39}
40
41// ============================================================================
42// Type 1 Object Encoding/Decoding Tests
43// ============================================================================
44
45TEST(RoomObjectEncodingTest, Type1EncodeDecodeBasic) {
46 // Type1: xxxxxxss yyyyyyss iiiiiiii
47 // Example: Object ID 0x42, position (10, 20), size 3, layer 0
48
49 RoomObject obj(0x42, 10, 20, 3, 0);
50
51 // Encode
52 auto bytes = obj.EncodeObjectToBytes();
53
54 // Decode
55 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 0);
56
57 // Verify
58 EXPECT_EQ(decoded.id_, obj.id_);
59 EXPECT_EQ(decoded.x(), obj.x());
60 EXPECT_EQ(decoded.y(), obj.y());
61 EXPECT_EQ(decoded.size(), obj.size());
62 EXPECT_EQ(decoded.GetLayerValue(), obj.GetLayerValue());
63}
64
65TEST(RoomObjectEncodingTest, Type1MaxValues) {
66 // Test maximum valid values for Type1
67 // Constraints:
68 // - ID < 0xF8 (b3 >= 0xF8 triggers Type3 detection)
69 // - X < 63 OR Size < 12 (b1 >= 0xFC triggers Type2 detection)
70 // Safe max values: ID=0xF7, X=62, Y=63, Size=15
71 RoomObject obj(0xF7, 62, 63, 15, 2);
72
73 auto bytes = obj.EncodeObjectToBytes();
74 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 2);
75
76 EXPECT_EQ(decoded.id_, obj.id_);
77 EXPECT_EQ(decoded.x(), obj.x());
78 EXPECT_EQ(decoded.y(), obj.y());
79 EXPECT_EQ(decoded.size(), obj.size());
80}
81
82TEST(RoomObjectEncodingTest, Type1MinValues) {
83 // Test minimum values for Type1
84 RoomObject obj(0x00, 0, 0, 0, 0);
85
86 auto bytes = obj.EncodeObjectToBytes();
87 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 0);
88
89 EXPECT_EQ(decoded.id_, obj.id_);
90 EXPECT_EQ(decoded.x(), obj.x());
91 EXPECT_EQ(decoded.y(), obj.y());
92 EXPECT_EQ(decoded.size(), obj.size());
93}
94
95TEST(RoomObjectEncodingTest, Type1DifferentSizes) {
96 // Test all valid size values (0-15)
97 for (int size = 0; size <= 15; size++) {
98 RoomObject obj(0x30, 15, 20, size, 1);
99
100 auto bytes = obj.EncodeObjectToBytes();
101 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 1);
102
103 EXPECT_EQ(decoded.size(), size) << "Failed for size " << size;
104 }
105}
106
107TEST(RoomObjectEncodingTest, Type1RealWorldExample1) {
108 // Example from actual ROM: Wall object
109 // Bytes: 0x28 0x50 0x10
110 // Expected: X=10, Y=20, Size=0, ID=0x10
111
112 auto decoded = RoomObject::DecodeObjectFromBytes(0x28, 0x50, 0x10, 0);
113
114 EXPECT_EQ(decoded.x(), 10);
115 EXPECT_EQ(decoded.y(), 20);
116 EXPECT_EQ(decoded.size(), 0);
117 EXPECT_EQ(decoded.id_, 0x10);
118}
119
120TEST(RoomObjectEncodingTest, Type1RealWorldExample2) {
121 // Example: Ceiling object with size
122 // Correct bytes for X=10, Y=20, Size=3, ID=0x00: 0x28 0x53 0x00
123
124 auto decoded = RoomObject::DecodeObjectFromBytes(0x28, 0x53, 0x00, 0);
125
126 EXPECT_EQ(decoded.x(), 10);
127 EXPECT_EQ(decoded.y(), 20);
128 EXPECT_EQ(decoded.size(), 3);
129 EXPECT_EQ(decoded.id_, 0x00);
130}
131
132// ============================================================================
133// Type 2 Object Encoding/Decoding Tests
134// ============================================================================
135
136TEST(RoomObjectEncodingTest, Type2EncodeDecodeBasic) {
137 // Type2: 111111xx xxxxyyyy yyiiiiii
138 // Example: Object ID 0x125, position (15, 30), size ignored, layer 1
139
140 RoomObject obj(0x125, 15, 30, 0, 1);
141
142 // Encode
143 auto bytes = obj.EncodeObjectToBytes();
144
145 // Verify b1 starts with 0xFC
146 EXPECT_GE(bytes.b1, 0xFC);
147
148 // Decode
149 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 1);
150
151 // Verify
152 EXPECT_EQ(decoded.id_, obj.id_);
153 EXPECT_EQ(decoded.x(), obj.x());
154 EXPECT_EQ(decoded.y(), obj.y());
155 EXPECT_EQ(decoded.GetLayerValue(), obj.GetLayerValue());
156}
157
158TEST(RoomObjectEncodingTest, Type2MaxValues) {
159 // Type2 allows larger position range, but has constraints:
160 // When Y=63 and ID=0x13F, b3 becomes 0xFF >= 0xF8, triggering Type3 detection
161 // Safe max: X=63, Y=59, ID=0x13F (b3 = ((59&0x03)<<6)|(0x3F) = 0xFF still!)
162 // Even safer: X=63, Y=63, ID=0x11F (b3 = (0xC0|0x1F) = 0xDF < 0xF8)
163 RoomObject obj(0x11F, 63, 63, 0, 2);
164
165 auto bytes = obj.EncodeObjectToBytes();
166 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 2);
167
168 EXPECT_EQ(decoded.id_, obj.id_);
169 EXPECT_EQ(decoded.x(), obj.x());
170 EXPECT_EQ(decoded.y(), obj.y());
171}
172
173TEST(RoomObjectEncodingTest, Type2RealWorldExample) {
174 // Example: Large brazier (object 0x11C)
175 // Position (8, 12)
176
177 RoomObject obj(0x11C, 8, 12, 0, 0);
178
179 auto bytes = obj.EncodeObjectToBytes();
180 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 0);
181
182 EXPECT_EQ(decoded.id_, 0x11C);
183 EXPECT_EQ(decoded.x(), 8);
184 EXPECT_EQ(decoded.y(), 12);
185}
186
187// ============================================================================
188// Type 3 Object Encoding/Decoding Tests
189// ============================================================================
190
191TEST(RoomObjectEncodingTest, Type3EncodeDecodeChest) {
192 // Type3: xxxxxxii yyyyyyii 11111iii
193 // Example: Small chest (0xF99), position (5, 10)
194
195 RoomObject obj(0xF99, 5, 10, 0, 0);
196
197 // Encode
198 auto bytes = obj.EncodeObjectToBytes();
199
200 // Verify b3 >= 0xF8
201 EXPECT_GE(bytes.b3, 0xF8);
202
203 // Decode
204 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 0);
205
206 // Verify
207 EXPECT_EQ(decoded.id_, obj.id_);
208 EXPECT_EQ(decoded.x(), obj.x());
209 EXPECT_EQ(decoded.y(), obj.y());
210}
211
212TEST(RoomObjectEncodingTest, Type3EncodeDcodeBigChest) {
213 // Example: Big chest (0xFB1), position (15, 20)
214
215 RoomObject obj(0xFB1, 15, 20, 0, 1);
216
217 auto bytes = obj.EncodeObjectToBytes();
218 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 1);
219
220 EXPECT_EQ(decoded.id_, 0xFB1);
221 EXPECT_EQ(decoded.x(), 15);
222 EXPECT_EQ(decoded.y(), 20);
223}
224
225TEST(RoomObjectEncodingTest, Type3RealWorldExample) {
226 // Example from ROM: Chest at position (10, 15)
227 // Correct bytes for ID 0xF99: 0x29 0x3E 0xF9
228
229 auto decoded = RoomObject::DecodeObjectFromBytes(0x29, 0x3E, 0xF9, 0);
230
231 // Expected: X=10, Y=15, ID=0xF99 (small chest)
232 EXPECT_EQ(decoded.x(), 10);
233 EXPECT_EQ(decoded.y(), 15);
234 EXPECT_EQ(decoded.id_, 0xF99);
235}
236
237// ============================================================================
238// Edge Cases and Special Values
239// ============================================================================
240
241TEST(RoomObjectEncodingTest, LayerPreservation) {
242 // Test that layer information is preserved through encode/decode
243 for (uint8_t layer = 0; layer <= 2; layer++) {
244 RoomObject obj(0x42, 10, 20, 3, layer);
245
246 auto bytes = obj.EncodeObjectToBytes();
247 auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, layer);
248
249 EXPECT_EQ(decoded.GetLayerValue(), layer) << "Failed for layer " << (int)layer;
250 }
251}
252
253TEST(RoomObjectEncodingTest, BoundaryBetweenTypes) {
254 // Test boundary values between object types
255 // NOTE: Type1 can only go up to ID 0xF7 (b3 >= 0xF8 triggers Type3)
256
257 // Last safe Type1 object
258 RoomObject type1(0xF7, 10, 20, 3, 0);
259 auto bytes1 = type1.EncodeObjectToBytes();
260 auto decoded1 = RoomObject::DecodeObjectFromBytes(bytes1.b1, bytes1.b2, bytes1.b3, 0);
261 EXPECT_EQ(decoded1.id_, 0xF7);
262
263 // First Type2 object
264 RoomObject type2(0x100, 10, 20, 0, 0);
265 auto bytes2 = type2.EncodeObjectToBytes();
266 auto decoded2 = RoomObject::DecodeObjectFromBytes(bytes2.b1, bytes2.b2, bytes2.b3, 0);
267 EXPECT_EQ(decoded2.id_, 0x100);
268
269 // Last Type2 object
270 RoomObject type2_last(0x13F, 10, 20, 0, 0);
271 auto bytes2_last = type2_last.EncodeObjectToBytes();
272 auto decoded2_last = RoomObject::DecodeObjectFromBytes(bytes2_last.b1, bytes2_last.b2, bytes2_last.b3, 0);
273 EXPECT_EQ(decoded2_last.id_, 0x13F);
274
275 // Type3 objects (start at 0xF80)
276 RoomObject type3(0xF99, 10, 20, 0, 0);
277 auto bytes3 = type3.EncodeObjectToBytes();
278 auto decoded3 = RoomObject::DecodeObjectFromBytes(bytes3.b1, bytes3.b2, bytes3.b3, 0);
279 EXPECT_EQ(decoded3.id_, 0xF99);
280}
281
282TEST(RoomObjectEncodingTest, ZeroPosition) {
283 // Test objects at position (0, 0)
284 RoomObject type1(0x10, 0, 0, 0, 0);
285 auto bytes1 = type1.EncodeObjectToBytes();
286 auto decoded1 = RoomObject::DecodeObjectFromBytes(bytes1.b1, bytes1.b2, bytes1.b3, 0);
287 EXPECT_EQ(decoded1.x(), 0);
288 EXPECT_EQ(decoded1.y(), 0);
289
290 RoomObject type2(0x110, 0, 0, 0, 0);
291 auto bytes2 = type2.EncodeObjectToBytes();
292 auto decoded2 = RoomObject::DecodeObjectFromBytes(bytes2.b1, bytes2.b2, bytes2.b3, 0);
293 EXPECT_EQ(decoded2.x(), 0);
294 EXPECT_EQ(decoded2.y(), 0);
295}
296
297// ============================================================================
298// Batch Tests with Multiple Objects
299// ============================================================================
300
301TEST(RoomObjectEncodingTest, MultipleObjectsRoundTrip) {
302 // Test encoding/decoding a batch of different objects
303 std::vector<RoomObject> objects;
304
305 // Add various objects
306 objects.emplace_back(0x10, 5, 10, 2, 0); // Type1
307 objects.emplace_back(0x42, 15, 20, 5, 1); // Type1
308 objects.emplace_back(0x110, 8, 12, 0, 0); // Type2
309 objects.emplace_back(0x125, 25, 30, 0, 1); // Type2
310 objects.emplace_back(0xF99, 10, 15, 0, 0); // Type3 (chest)
311 objects.emplace_back(0xFB1, 20, 25, 0, 2); // Type3 (big chest)
312
313 for (size_t i = 0; i < objects.size(); i++) {
314 auto& obj = objects[i];
315 auto bytes = obj.EncodeObjectToBytes();
317 bytes.b1, bytes.b2, bytes.b3, obj.GetLayerValue());
318
319 EXPECT_EQ(decoded.id_, obj.id_) << "Failed at index " << i;
320 EXPECT_EQ(decoded.x(), obj.x()) << "Failed at index " << i;
321 EXPECT_EQ(decoded.y(), obj.y()) << "Failed at index " << i;
322 if (obj.id_ < 0x100) { // Type1 objects have size
323 EXPECT_EQ(decoded.size(), obj.size()) << "Failed at index " << i;
324 }
325 }
326}
327
328} // namespace
329} // namespace zelda3
330} // namespace yaze
static RoomObject DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t layer)
ObjectBytes EncodeObjectToBytes() const
static int DetermineObjectType(uint8_t b1, uint8_t b3)
uint8_t size() const
Definition room_object.h:77
uint8_t GetLayerValue() const
Main namespace for the application.