#include #include #include #include #include #include namespace easy2d { namespace { // 简易二进制缓冲区读取器 class BufferReader { public: BufferReader(const uint8_t* data, size_t length) : data_(data), length_(length), pos_(0) {} template T read() { if (pos_ + sizeof(T) > length_) { return T{}; } T value; std::memcpy(&value, data_ + pos_, sizeof(T)); pos_ += sizeof(T); return value; } std::string readAsciiString(int32_t len) { if (len <= 0 || pos_ + static_cast(len) > length_) { return ""; } std::string result(reinterpret_cast(data_ + pos_), len); pos_ += len; return result; } bool hasRemaining() const { return pos_ < length_; } size_t remaining() const { return length_ - pos_; } private: const uint8_t* data_; size_t length_; size_t pos_; }; // 字符串转小写 void toLower(std::string& s) { for (auto& c : s) { c = static_cast(std::tolower(static_cast(c))); } } } // anonymous namespace AniParseResult AniBinaryParser::parse(const uint8_t* data, size_t length) { AniParseResult result; result.clip = AnimationClip::create(); if (!data || length < 4) { result.success = false; result.errorMessage = "Invalid binary ANI data"; return result; } BufferReader reader(data, length); // 读取帧数和资源数 uint16_t frameCount = reader.read(); uint16_t resourceCount = reader.read(); // 读取精灵路径列表 std::vector sprites; sprites.reserve(resourceCount); for (uint16_t i = 0; i < resourceCount; ++i) { int32_t len = reader.read(); std::string path = reader.readAsciiString(len); toLower(path); sprites.push_back(std::move(path)); } // 读取全局参数 uint16_t globalParamCount = reader.read(); for (uint16_t j = 0; j < globalParamCount; ++j) { uint16_t type = reader.read(); switch (type) { case static_cast(AniNodeType::Loop): if (reader.read()) { result.clip->setLooping(true); } break; case static_cast(AniNodeType::Shadow): if (reader.read()) { result.clip->globalProperties().set(FramePropertyKey::Shadow, true); } break; default: break; } } // 逐帧解析 for (uint16_t i = 0; i < frameCount; ++i) { AnimationFrame frame; // 碰撞盒 uint16_t boxCount = reader.read(); for (uint16_t j = 0; j < boxCount; ++j) { uint16_t boxType = reader.read(); std::array box; for (int m = 0; m < 6; ++m) { box[m] = reader.read(); } if (boxType == static_cast(AniNodeType::DamageBox)) { frame.damageBoxes.push_back(box); } else if (boxType == static_cast(AniNodeType::AttackBox)) { frame.attackBoxes.push_back(box); } } // 图片 ID 和参数 uint16_t imgId = reader.read(); uint16_t imgParam = reader.read(); (void)imgParam; if (imgId < sprites.size()) { frame.texturePath = sprites[imgId]; frame.textureIndex = imgId; std::string resolvedPath = resolvePath(frame.texturePath); auto spriteFrame = SpriteFrameCache::getInstance() .getOrCreateFromFile(resolvedPath, frame.textureIndex); frame.spriteFrame = spriteFrame; } // 位置 frame.offset = Vec2( static_cast(reader.read()), static_cast(reader.read()) ); // 帧属性 uint16_t propertyCount = reader.read(); for (uint16_t m = 0; m < propertyCount; ++m) { uint16_t propType = reader.read(); AniNodeType nodeType = static_cast(propType); switch (nodeType) { case AniNodeType::Loop: frame.properties.set(FramePropertyKey::Loop, static_cast(reader.read())); break; case AniNodeType::Shadow: frame.properties.set(FramePropertyKey::Shadow, static_cast(reader.read())); break; case AniNodeType::Interpolation: frame.properties.withInterpolation( static_cast(reader.read())); break; case AniNodeType::Coord: frame.properties.set(FramePropertyKey::Coord, static_cast(reader.read())); break; case AniNodeType::ImageRate: { float rateX = reader.read(); float rateY = reader.read(); frame.properties.withImageRate(Vec2(rateX, rateY)); break; } case AniNodeType::ImageRotate: frame.properties.withImageRotate( static_cast(reader.read())); break; case AniNodeType::RGBA: { uint32_t rgba = reader.read(); uint8_t a = static_cast((rgba >> 24) & 0xFF); uint8_t r = static_cast((rgba >> 16) & 0xFF); uint8_t g = static_cast((rgba >> 8) & 0xFF); uint8_t b = static_cast((rgba) & 0xFF); frame.properties.withColorTint(Color::fromRGBA(r, g, b, a)); break; } case AniNodeType::GraphicEffect: { uint16_t effectType = reader.read(); frame.properties.set(FramePropertyKey::GraphicEffect, static_cast(effectType)); // MONOCHROME 额外读取 rgb if (effectType == 5) { reader.read(); // r reader.read(); // g reader.read(); // b } // SPACEDISTORT 额外读取 pos else if (effectType == 6) { reader.read(); // x reader.read(); // y } break; } case AniNodeType::Delay: frame.delay = static_cast(reader.read()); break; case AniNodeType::DamageType: frame.properties.set(FramePropertyKey::DamageType, static_cast(reader.read())); break; case AniNodeType::PlaySound: { int32_t len = reader.read(); std::string soundPath = reader.readAsciiString(len); frame.properties.withPlaySound(resolvePath(soundPath)); break; } case AniNodeType::SetFlag: frame.properties.withSetFlag(reader.read()); break; case AniNodeType::FlipType: frame.properties.set(FramePropertyKey::FlipType, static_cast(reader.read())); break; case AniNodeType::LoopStart: frame.properties.set(FramePropertyKey::LoopStart, true); break; case AniNodeType::LoopEnd: frame.properties.set(FramePropertyKey::LoopEnd, reader.read()); break; case AniNodeType::Clip: { std::vector clipRegion = { static_cast(reader.read()), static_cast(reader.read()), static_cast(reader.read()), static_cast(reader.read()) }; frame.properties.set(FramePropertyKey::ClipRegion, clipRegion); break; } default: // 未知类型 - 跳过 break; } } result.clip->addFrame(std::move(frame)); } result.success = true; return result; } AniParseResult AniBinaryParser::parseFromFile(const std::string& filePath) { std::ifstream file(filePath, std::ios::binary | std::ios::ate); if (!file.is_open()) { AniParseResult result; result.success = false; result.errorMessage = "Cannot open binary ANI file: " + filePath; return result; } auto size = file.tellg(); file.seekg(0, std::ios::beg); std::vector buffer(static_cast(size)); file.read(reinterpret_cast(buffer.data()), size); file.close(); // 提取基础路径 auto lastSlash = filePath.find_last_of("/\\"); if (lastSlash != std::string::npos && basePath_.empty()) { basePath_ = filePath.substr(0, lastSlash); } auto result = parse(buffer.data(), buffer.size()); if (result.clip) { result.clip->setSourcePath(filePath); std::string name = (lastSlash != std::string::npos) ? filePath.substr(lastSlash + 1) : filePath; result.clip->setName(name); } return result; } std::string AniBinaryParser::resolvePath(const std::string& relativePath) const { std::string resolved = relativePath; if (pathResolver_) { resolved = pathResolver_(relativePath); } if (!basePath_.empty() && !resolved.empty() && resolved[0] != '/') { if (resolved.size() < 2 || resolved[1] != ':') { resolved = basePath_ + "/" + resolved; } } return resolved; } } // namespace easy2d