15 if (out_index ==
nullptr) {
42 "track_corner_TL.bin",
43 "track_corner_TR.bin",
44 "track_corner_BL.bin",
45 "track_corner_BR.bin",
48 "track_floor_corner_TL.bin",
49 "track_floor_corner_TR.bin",
50 "track_floor_corner_BL.bin",
51 "track_floor_corner_BR.bin",
52 "track_floor_any.bin",
53 "wall_sword_house.bin",
73 LOG_INFO(
"CustomObjectManager",
"Initialize: base_path='%s'",
78 "Object 0x31 file list has %zu entries (corners need indices 2-5)",
82 "Object 0x31 not mapped - corner overrides 0x100-0x103 will fail");
88 const std::unordered_map<
int, std::vector<std::string>>& map) {
99 int object_id)
const {
102 return &custom_it->second;
104 if (object_id == 0x31) {
107 if (object_id == 0x32) {
114 int resolved_index)
const {
124 const auto& list = custom_it->second;
125 if (resolved_index < 0 || resolved_index >=
static_cast<int>(list.size())) {
129 const std::string& filename = list[resolved_index];
130 if (filename.empty()) {
134 std::filesystem::path candidate = filename;
135 if (!candidate.is_absolute() && !
base_path_.empty()) {
136 candidate = std::filesystem::path(
base_path_) / candidate;
138 return std::filesystem::exists(candidate);
142 const std::string& filename) {
143 if (
cache_.contains(filename)) {
148 std::filesystem::path full_path =
151 std::ifstream file(full_path, std::ios::binary);
153 LOG_ERROR(
"CustomObjectManager",
"Failed to open file: %s",
155 return absl::NotFoundError(
"Could not open file: " + full_path.string());
159 std::vector<uint8_t> buffer((std::istreambuf_iterator<char>(file)),
160 std::istreambuf_iterator<char>());
163 if (!object_or_error.ok()) {
164 return object_or_error.status();
168 std::make_shared<CustomObject>(std::move(object_or_error.value()));
169 cache_[filename] = object_ptr;
175 const std::vector<uint8_t>& data) {
178 int current_buffer_pos = 0;
188 constexpr int kBufferStride = 128;
190 while (cursor + 1 < data.size()) {
192 uint16_t header = data[cursor] | (data[cursor + 1] << 8);
198 int count = header & 0x001F;
199 int jump_offset = (header >> 8) & 0xFF;
204 int row_start_pos = current_buffer_pos;
207 for (
int i = 0; i < count; ++i) {
208 if (cursor + 1 >= data.size()) {
210 "Unexpected end of file parsing object");
214 uint16_t tile_data = data[cursor] | (data[cursor + 1] << 8);
219 int rel_y = current_buffer_pos / kBufferStride;
220 int rel_x = (current_buffer_pos % kBufferStride) / 2;
222 obj.
tiles.push_back({rel_x, rel_y, tile_data});
224 current_buffer_pos += 2;
229 current_buffer_pos = row_start_pos + jump_offset;
235absl::StatusOr<std::shared_ptr<CustomObject>>
240 if (!list && ResolveCornerOverrideIndex(object_id, &index)) {
242 return absl::NotFoundError(
243 "Corner alias override not configured for current runtime context");
250 return absl::NotFoundError(
"Object ID not mapped to custom object");
253 if (index < 0 || index >=
static_cast<int>(list->size())) {
254 return absl::OutOfRangeError(
"Subtype index out of range");
262 return static_cast<int>(list->size());
264 if (object_id >= 0x100 && object_id <= 0x103) {
266 return static_cast<int>(list->size());
273 const std::string& filename) {
277 if (!defaults.empty()) {
288 int object_id)
const {
295const std::vector<std::string>&
297 static const std::vector<std::string> kEmpty;
298 if (object_id == 0x31) {
301 if (object_id == 0x32) {
315 if (!list && ResolveCornerOverrideIndex(object_id, &index)) {
318 if (list && index >= 0 && index <
static_cast<int>(list->size())) {
319 return (*list)[index];
Manages loading and caching of custom object binary files.
int GetSubtypeCount(int object_id) const
void RestoreState(const State &state)
bool IsCornerAliasOverrideEnabled(int resolved_index) const
absl::StatusOr< CustomObject > ParseBinaryData(const std::vector< uint8_t > &data)
static const std::vector< std::string > & DefaultSubtypeFilenamesForObject(int object_id)
void SetObjectFileMap(const std::unordered_map< int, std::vector< std::string > > &map)
static CustomObjectManager & Get()
absl::StatusOr< std::shared_ptr< CustomObject > > LoadObject(const std::string &filename)
void AddObjectFile(int object_id, const std::string &filename)
absl::StatusOr< std::shared_ptr< CustomObject > > GetObjectInternal(int object_id, int subtype)
static const std::vector< std::string > kSubtype2Filenames
void Initialize(const std::string &custom_objects_folder)
std::unordered_map< std::string, std::shared_ptr< CustomObject > > cache_
const std::vector< std::string > * ResolveFileList(int object_id) const
void ClearObjectFileMap()
std::unordered_map< int, std::vector< std::string > > custom_file_map_
std::string ResolveFilename(int object_id, int subtype) const
static const std::vector< std::string > kSubtype1Filenames
std::vector< std::string > GetEffectiveFileList(int object_id) const
State SnapshotState() const
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
bool ResolveCornerOverrideIndex(int object_id, int *out_index)
std::unordered_map< int, std::vector< std::string > > custom_file_map
Represents a decoded custom object (from binary format)
std::vector< TileMapEntry > tiles