feat: Implement object encoding/decoding for dungeon objects and add unit tests
This commit is contained in:
@@ -93,6 +93,20 @@
|
||||
"YAZE_TEST_ROM_PATH": "${sourceDir}/zelda3.sfc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos-dungeon-dev",
|
||||
"displayName": "Dungeon Editor Dev (ARM64)",
|
||||
"description": "macOS ARM64 build for dungeon editing implementation work",
|
||||
"binaryDir": "${sourceDir}/build_rooms",
|
||||
"inherits": "macos-debug",
|
||||
"cacheVariables": {
|
||||
"YAZE_BUILD_APP": "ON",
|
||||
"YAZE_BUILD_TESTS": "ON",
|
||||
"YAZE_ENABLE_ROM_TESTS": "ON",
|
||||
"YAZE_TEST_ROM_PATH": "${sourceDir}/zelda3.sfc",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci",
|
||||
"displayName": "Continuous Integration",
|
||||
@@ -404,6 +418,11 @@
|
||||
"configurePreset": "macos-agent-test",
|
||||
"displayName": "macOS z3ed Agent Test Build"
|
||||
},
|
||||
{
|
||||
"name": "macos-dungeon-dev",
|
||||
"configurePreset": "macos-dungeon-dev",
|
||||
"displayName": "macOS Dungeon Editor Dev Build"
|
||||
},
|
||||
{
|
||||
"name": "fast",
|
||||
"configurePreset": "debug",
|
||||
|
||||
@@ -248,5 +248,127 @@ int RoomObject::GetTileCount() const {
|
||||
return tile_count_;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Object Encoding/Decoding Implementation (Phase 1, Task 1.1)
|
||||
// ============================================================================
|
||||
|
||||
int RoomObject::DetermineObjectType(uint8_t b1, uint8_t b3) {
|
||||
// Type 3: Objects with ID >= 0xF00
|
||||
// These have b3 >= 0xF8 (top nibble is 0xF)
|
||||
if (b3 >= 0xF8) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Type 2: Objects with ID >= 0x100
|
||||
// These have b1 >= 0xFC (marker for Type2 encoding)
|
||||
if (b1 >= 0xFC) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Type 1: Standard objects (ID 0x00-0xFF)
|
||||
return 1;
|
||||
}
|
||||
|
||||
RoomObject RoomObject::DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3,
|
||||
uint8_t layer) {
|
||||
int type = DetermineObjectType(b1, b3);
|
||||
|
||||
uint8_t x = 0;
|
||||
uint8_t y = 0;
|
||||
uint8_t size = 0;
|
||||
uint16_t id = 0;
|
||||
|
||||
switch (type) {
|
||||
case 1: {
|
||||
// Type1: xxxxxxss yyyyyyss iiiiiiii
|
||||
// X position: bits 2-7 of byte 1
|
||||
x = (b1 & 0xFC) >> 2;
|
||||
|
||||
// Y position: bits 2-7 of byte 2
|
||||
y = (b2 & 0xFC) >> 2;
|
||||
|
||||
// Size: bits 0-1 of byte 1 (high), bits 0-1 of byte 2 (low)
|
||||
size = ((b1 & 0x03) << 2) | (b2 & 0x03);
|
||||
|
||||
// ID: byte 3 (0x00-0xFF)
|
||||
id = b3;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
// Type2: 111111xx xxxxyyyy yyiiiiii
|
||||
// X position: bits 0-1 of byte 1 (high), bits 4-7 of byte 2 (low)
|
||||
x = ((b1 & 0x03) << 4) | ((b2 & 0xF0) >> 4);
|
||||
|
||||
// Y position: bits 0-3 of byte 2 (high), bits 6-7 of byte 3 (low)
|
||||
y = ((b2 & 0x0F) << 2) | ((b3 & 0xC0) >> 6);
|
||||
|
||||
// Size: 0 (Type2 objects don't use size parameter)
|
||||
size = 0;
|
||||
|
||||
// ID: bits 0-5 of byte 3, OR with 0x100 to mark as Type2
|
||||
id = (b3 & 0x3F) | 0x100;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
// Type3: xxxxxxii yyyyyyii 11111iii
|
||||
// X position: bits 2-7 of byte 1
|
||||
x = (b1 & 0xFC) >> 2;
|
||||
|
||||
// Y position: bits 2-7 of byte 2
|
||||
y = (b2 & 0xFC) >> 2;
|
||||
|
||||
// Size: 0 (Type3 objects don't use size parameter)
|
||||
size = 0;
|
||||
|
||||
// ID: Complex reconstruction
|
||||
// Top 8 bits from byte 3 (shifted left by 4)
|
||||
// Bits 2-3 from byte 2
|
||||
// Bits 0-1 from byte 1
|
||||
// Plus 0x80 offset
|
||||
id = ((b3 & 0xFF) << 4) | ((b2 & 0x03) << 2) | (b1 & 0x03) | 0x80;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Should never happen, but default to Type1
|
||||
id = b3;
|
||||
x = (b1 & 0xFC) >> 2;
|
||||
y = (b2 & 0xFC) >> 2;
|
||||
size = ((b1 & 0x03) << 2) | (b2 & 0x03);
|
||||
break;
|
||||
}
|
||||
|
||||
return RoomObject(static_cast<int16_t>(id), x, y, size, layer);
|
||||
}
|
||||
|
||||
RoomObject::ObjectBytes RoomObject::EncodeObjectToBytes() const {
|
||||
ObjectBytes bytes;
|
||||
|
||||
// Determine type based on object ID
|
||||
if (id_ >= 0xF00) {
|
||||
// Type 3: xxxxxxii yyyyyyii 11111iii
|
||||
bytes.b1 = (x_ << 2) | (id_ & 0x03);
|
||||
bytes.b2 = (y_ << 2) | ((id_ >> 2) & 0x03);
|
||||
bytes.b3 = (id_ >> 4) & 0xFF;
|
||||
} else if (id_ >= 0x100) {
|
||||
// Type 2: 111111xx xxxxyyyy yyiiiiii
|
||||
bytes.b1 = 0xFC | ((x_ & 0x30) >> 4);
|
||||
bytes.b2 = ((x_ & 0x0F) << 4) | ((y_ & 0x3C) >> 2);
|
||||
bytes.b3 = ((y_ & 0x03) << 6) | (id_ & 0x3F);
|
||||
} else {
|
||||
// Type 1: xxxxxxss yyyyyyss iiiiiiii
|
||||
// Clamp size to 0-15 range
|
||||
uint8_t clamped_size = size_ > 15 ? 0 : size_;
|
||||
|
||||
bytes.b1 = (x_ << 2) | ((clamped_size >> 2) & 0x03);
|
||||
bytes.b2 = (y_ << 2) | (clamped_size & 0x03);
|
||||
bytes.b3 = static_cast<uint8_t>(id_);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
|
||||
@@ -103,6 +103,35 @@ class RoomObject {
|
||||
// Get tile count without loading all tiles
|
||||
int GetTileCount() const;
|
||||
|
||||
// ============================================================================
|
||||
// Object Encoding/Decoding (Phase 1, Task 1.1)
|
||||
// ============================================================================
|
||||
|
||||
// 3-byte object encoding structure
|
||||
struct ObjectBytes {
|
||||
uint8_t b1;
|
||||
uint8_t b2;
|
||||
uint8_t b3;
|
||||
};
|
||||
|
||||
// Decode object from 3-byte ROM format
|
||||
// Type1: xxxxxxss yyyyyyss iiiiiiii (ID 0x00-0xFF)
|
||||
// Type2: 111111xx xxxxyyyy yyiiiiii (ID 0x100-0x1FF)
|
||||
// Type3: xxxxxxii yyyyyyii 11111iii (ID 0xF00-0xFFF)
|
||||
static RoomObject DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3,
|
||||
uint8_t layer);
|
||||
|
||||
// Encode object to 3-byte ROM format
|
||||
ObjectBytes EncodeObjectToBytes() const;
|
||||
|
||||
// Determine object type from bytes (1, 2, or 3)
|
||||
static int DetermineObjectType(uint8_t b1, uint8_t b3);
|
||||
|
||||
// Get layer from LayerType enum
|
||||
uint8_t GetLayerValue() const { return static_cast<uint8_t>(layer_); }
|
||||
|
||||
// ============================================================================
|
||||
|
||||
void AddTiles(int nbr, int pos) {
|
||||
// Reads nbr Tile16 entries from ROM object data starting at pos (8 bytes per Tile16)
|
||||
for (int i = 0; i < nbr; i++) {
|
||||
|
||||
@@ -42,6 +42,7 @@ if(YAZE_BUILD_TESTS AND NOT YAZE_BUILD_TESTS STREQUAL "OFF")
|
||||
unit/zelda3/sprite_position_test.cc
|
||||
unit/zelda3/test_dungeon_objects.cc
|
||||
unit/zelda3/dungeon_component_unit_test.cc
|
||||
zelda3/dungeon/room_object_encoding_test.cc
|
||||
|
||||
# CLI Services (for catalog serialization tests)
|
||||
../src/cli/service/resources/resource_catalog.cc
|
||||
@@ -96,6 +97,7 @@ if(YAZE_BUILD_TESTS AND NOT YAZE_BUILD_TESTS STREQUAL "OFF")
|
||||
unit/zelda3/sprite_position_test.cc
|
||||
unit/zelda3/test_dungeon_objects.cc
|
||||
unit/zelda3/dungeon_component_unit_test.cc
|
||||
zelda3/dungeon/room_object_encoding_test.cc
|
||||
|
||||
# CLI Services (for catalog serialization tests)
|
||||
../src/cli/service/resources/resource_catalog.cc
|
||||
|
||||
326
test/zelda3/dungeon/room_object_encoding_test.cc
Normal file
326
test/zelda3/dungeon/room_object_encoding_test.cc
Normal file
@@ -0,0 +1,326 @@
|
||||
// test/zelda3/dungeon/room_object_encoding_test.cc
|
||||
// Unit tests for Phase 1, Task 1.1: Object Encoding/Decoding
|
||||
//
|
||||
// These tests verify that the object encoding and decoding functions work
|
||||
// correctly for all three object types (Type1, Type2, Type3) based on
|
||||
// ZScream's proven implementation.
|
||||
|
||||
#include "app/zelda3/dungeon/room_object.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
namespace {
|
||||
|
||||
// ============================================================================
|
||||
// Object Type Detection Tests
|
||||
// ============================================================================
|
||||
|
||||
TEST(RoomObjectEncodingTest, DetermineObjectTypeType1) {
|
||||
// Type1: b1 < 0xFC, b3 < 0xF8
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0x28, 0x10), 1);
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0x50, 0x42), 1);
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0xFB, 0xF7), 1);
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, DetermineObjectTypeType2) {
|
||||
// Type2: b1 >= 0xFC, b3 < 0xF8
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0xFC, 0x42), 2);
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0xFD, 0x25), 2);
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0xFF, 0x00), 2);
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, DetermineObjectTypeType3) {
|
||||
// Type3: b3 >= 0xF8
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0x28, 0xF8), 3);
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0x50, 0xF9), 3);
|
||||
EXPECT_EQ(RoomObject::DetermineObjectType(0xFC, 0xFF), 3);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Type 1 Object Encoding/Decoding Tests
|
||||
// ============================================================================
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type1EncodeDecodeBasic) {
|
||||
// Type1: xxxxxxss yyyyyyss iiiiiiii
|
||||
// Example: Object ID 0x42, position (10, 20), size 3, layer 0
|
||||
|
||||
RoomObject obj(0x42, 10, 20, 3, 0);
|
||||
|
||||
// Encode
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
|
||||
// Decode
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 0);
|
||||
|
||||
// Verify
|
||||
EXPECT_EQ(decoded.id_, obj.id_);
|
||||
EXPECT_EQ(decoded.x(), obj.x());
|
||||
EXPECT_EQ(decoded.y(), obj.y());
|
||||
EXPECT_EQ(decoded.size(), obj.size());
|
||||
EXPECT_EQ(decoded.GetLayerValue(), obj.GetLayerValue());
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type1MaxValues) {
|
||||
// Test maximum valid values for Type1
|
||||
// Max X = 63, Max Y = 63, Max Size = 15
|
||||
RoomObject obj(0xFF, 63, 63, 15, 2);
|
||||
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 2);
|
||||
|
||||
EXPECT_EQ(decoded.id_, obj.id_);
|
||||
EXPECT_EQ(decoded.x(), obj.x());
|
||||
EXPECT_EQ(decoded.y(), obj.y());
|
||||
EXPECT_EQ(decoded.size(), obj.size());
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type1MinValues) {
|
||||
// Test minimum values for Type1
|
||||
RoomObject obj(0x00, 0, 0, 0, 0);
|
||||
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 0);
|
||||
|
||||
EXPECT_EQ(decoded.id_, obj.id_);
|
||||
EXPECT_EQ(decoded.x(), obj.x());
|
||||
EXPECT_EQ(decoded.y(), obj.y());
|
||||
EXPECT_EQ(decoded.size(), obj.size());
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type1DifferentSizes) {
|
||||
// Test all valid size values (0-15)
|
||||
for (int size = 0; size <= 15; size++) {
|
||||
RoomObject obj(0x30, 15, 20, size, 1);
|
||||
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 1);
|
||||
|
||||
EXPECT_EQ(decoded.size(), size) << "Failed for size " << size;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type1RealWorldExample1) {
|
||||
// Example from actual ROM: Wall object
|
||||
// Bytes: 0x28 0x50 0x10
|
||||
// Expected: X=10, Y=20, Size=0, ID=0x10
|
||||
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(0x28, 0x50, 0x10, 0);
|
||||
|
||||
EXPECT_EQ(decoded.x(), 10);
|
||||
EXPECT_EQ(decoded.y(), 20);
|
||||
EXPECT_EQ(decoded.size(), 0);
|
||||
EXPECT_EQ(decoded.id_, 0x10);
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type1RealWorldExample2) {
|
||||
// Example: Ceiling object with size
|
||||
// Bytes: 0x2B 0x53 0x00
|
||||
// Expected: X=10, Y=20, Size=3, ID=0x00
|
||||
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(0x2B, 0x53, 0x00, 0);
|
||||
|
||||
EXPECT_EQ(decoded.x(), 10);
|
||||
EXPECT_EQ(decoded.y(), 20);
|
||||
EXPECT_EQ(decoded.size(), 3);
|
||||
EXPECT_EQ(decoded.id_, 0x00);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Type 2 Object Encoding/Decoding Tests
|
||||
// ============================================================================
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type2EncodeDecodeBasic) {
|
||||
// Type2: 111111xx xxxxyyyy yyiiiiii
|
||||
// Example: Object ID 0x125, position (15, 30), size ignored, layer 1
|
||||
|
||||
RoomObject obj(0x125, 15, 30, 0, 1);
|
||||
|
||||
// Encode
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
|
||||
// Verify b1 starts with 0xFC
|
||||
EXPECT_GE(bytes.b1, 0xFC);
|
||||
|
||||
// Decode
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 1);
|
||||
|
||||
// Verify
|
||||
EXPECT_EQ(decoded.id_, obj.id_);
|
||||
EXPECT_EQ(decoded.x(), obj.x());
|
||||
EXPECT_EQ(decoded.y(), obj.y());
|
||||
EXPECT_EQ(decoded.GetLayerValue(), obj.GetLayerValue());
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type2MaxValues) {
|
||||
// Type2 allows larger position range
|
||||
// Max X = 63, Max Y = 63
|
||||
RoomObject obj(0x13F, 63, 63, 0, 2);
|
||||
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 2);
|
||||
|
||||
EXPECT_EQ(decoded.id_, obj.id_);
|
||||
EXPECT_EQ(decoded.x(), obj.x());
|
||||
EXPECT_EQ(decoded.y(), obj.y());
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type2RealWorldExample) {
|
||||
// Example: Large brazier (object 0x11C)
|
||||
// Position (8, 12)
|
||||
|
||||
RoomObject obj(0x11C, 8, 12, 0, 0);
|
||||
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 0);
|
||||
|
||||
EXPECT_EQ(decoded.id_, 0x11C);
|
||||
EXPECT_EQ(decoded.x(), 8);
|
||||
EXPECT_EQ(decoded.y(), 12);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Type 3 Object Encoding/Decoding Tests
|
||||
// ============================================================================
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type3EncodeDecodeChest) {
|
||||
// Type3: xxxxxxii yyyyyyii 11111iii
|
||||
// Example: Small chest (0xF99), position (5, 10)
|
||||
|
||||
RoomObject obj(0xF99, 5, 10, 0, 0);
|
||||
|
||||
// Encode
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
|
||||
// Verify b3 >= 0xF8
|
||||
EXPECT_GE(bytes.b3, 0xF8);
|
||||
|
||||
// Decode
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 0);
|
||||
|
||||
// Verify
|
||||
EXPECT_EQ(decoded.id_, obj.id_);
|
||||
EXPECT_EQ(decoded.x(), obj.x());
|
||||
EXPECT_EQ(decoded.y(), obj.y());
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type3EncodeDcodeBigChest) {
|
||||
// Example: Big chest (0xFB1), position (15, 20)
|
||||
|
||||
RoomObject obj(0xFB1, 15, 20, 0, 1);
|
||||
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, 1);
|
||||
|
||||
EXPECT_EQ(decoded.id_, 0xFB1);
|
||||
EXPECT_EQ(decoded.x(), 15);
|
||||
EXPECT_EQ(decoded.y(), 20);
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, Type3RealWorldExample) {
|
||||
// Example from ROM: Chest at position (10, 15)
|
||||
// Bytes: 0x29 0x3D 0xF9
|
||||
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(0x29, 0x3D, 0xF9, 0);
|
||||
|
||||
// Expected: X=10, Y=15, ID=0xF99 (small chest)
|
||||
EXPECT_EQ(decoded.x(), 10);
|
||||
EXPECT_EQ(decoded.y(), 15);
|
||||
EXPECT_EQ(decoded.id_, 0xF99);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Edge Cases and Special Values
|
||||
// ============================================================================
|
||||
|
||||
TEST(RoomObjectEncodingTest, LayerPreservation) {
|
||||
// Test that layer information is preserved through encode/decode
|
||||
for (uint8_t layer = 0; layer <= 2; layer++) {
|
||||
RoomObject obj(0x42, 10, 20, 3, layer);
|
||||
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(bytes.b1, bytes.b2, bytes.b3, layer);
|
||||
|
||||
EXPECT_EQ(decoded.GetLayerValue(), layer) << "Failed for layer " << (int)layer;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, BoundaryBetweenTypes) {
|
||||
// Test boundary values between object types
|
||||
|
||||
// Last Type1 object
|
||||
RoomObject type1(0xFF, 10, 20, 3, 0);
|
||||
auto bytes1 = type1.EncodeObjectToBytes();
|
||||
auto decoded1 = RoomObject::DecodeObjectFromBytes(bytes1.b1, bytes1.b2, bytes1.b3, 0);
|
||||
EXPECT_EQ(decoded1.id_, 0xFF);
|
||||
|
||||
// First Type2 object
|
||||
RoomObject type2(0x100, 10, 20, 0, 0);
|
||||
auto bytes2 = type2.EncodeObjectToBytes();
|
||||
auto decoded2 = RoomObject::DecodeObjectFromBytes(bytes2.b1, bytes2.b2, bytes2.b3, 0);
|
||||
EXPECT_EQ(decoded2.id_, 0x100);
|
||||
|
||||
// Last Type2 object
|
||||
RoomObject type2_last(0x13F, 10, 20, 0, 0);
|
||||
auto bytes2_last = type2_last.EncodeObjectToBytes();
|
||||
auto decoded2_last = RoomObject::DecodeObjectFromBytes(bytes2_last.b1, bytes2_last.b2, bytes2_last.b3, 0);
|
||||
EXPECT_EQ(decoded2_last.id_, 0x13F);
|
||||
|
||||
// Type3 objects
|
||||
RoomObject type3(0xF99, 10, 20, 0, 0);
|
||||
auto bytes3 = type3.EncodeObjectToBytes();
|
||||
auto decoded3 = RoomObject::DecodeObjectFromBytes(bytes3.b1, bytes3.b2, bytes3.b3, 0);
|
||||
EXPECT_EQ(decoded3.id_, 0xF99);
|
||||
}
|
||||
|
||||
TEST(RoomObjectEncodingTest, ZeroPosition) {
|
||||
// Test objects at position (0, 0)
|
||||
RoomObject type1(0x10, 0, 0, 0, 0);
|
||||
auto bytes1 = type1.EncodeObjectToBytes();
|
||||
auto decoded1 = RoomObject::DecodeObjectFromBytes(bytes1.b1, bytes1.b2, bytes1.b3, 0);
|
||||
EXPECT_EQ(decoded1.x(), 0);
|
||||
EXPECT_EQ(decoded1.y(), 0);
|
||||
|
||||
RoomObject type2(0x110, 0, 0, 0, 0);
|
||||
auto bytes2 = type2.EncodeObjectToBytes();
|
||||
auto decoded2 = RoomObject::DecodeObjectFromBytes(bytes2.b1, bytes2.b2, bytes2.b3, 0);
|
||||
EXPECT_EQ(decoded2.x(), 0);
|
||||
EXPECT_EQ(decoded2.y(), 0);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Batch Tests with Multiple Objects
|
||||
// ============================================================================
|
||||
|
||||
TEST(RoomObjectEncodingTest, MultipleObjectsRoundTrip) {
|
||||
// Test encoding/decoding a batch of different objects
|
||||
std::vector<RoomObject> objects;
|
||||
|
||||
// Add various objects
|
||||
objects.emplace_back(0x10, 5, 10, 2, 0); // Type1
|
||||
objects.emplace_back(0x42, 15, 20, 5, 1); // Type1
|
||||
objects.emplace_back(0x110, 8, 12, 0, 0); // Type2
|
||||
objects.emplace_back(0x125, 25, 30, 0, 1); // Type2
|
||||
objects.emplace_back(0xF99, 10, 15, 0, 0); // Type3 (chest)
|
||||
objects.emplace_back(0xFB1, 20, 25, 0, 2); // Type3 (big chest)
|
||||
|
||||
for (size_t i = 0; i < objects.size(); i++) {
|
||||
auto& obj = objects[i];
|
||||
auto bytes = obj.EncodeObjectToBytes();
|
||||
auto decoded = RoomObject::DecodeObjectFromBytes(
|
||||
bytes.b1, bytes.b2, bytes.b3, obj.GetLayerValue());
|
||||
|
||||
EXPECT_EQ(decoded.id_, obj.id_) << "Failed at index " << i;
|
||||
EXPECT_EQ(decoded.x(), obj.x()) << "Failed at index " << i;
|
||||
EXPECT_EQ(decoded.y(), obj.y()) << "Failed at index " << i;
|
||||
if (obj.id_ < 0x100) { // Type1 objects have size
|
||||
EXPECT_EQ(decoded.size(), obj.size()) << "Failed at index " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
|
||||
Reference in New Issue
Block a user