// // RedSpineBakeModel.cpp // empty2dx-desktop // // Created by Liang zhong on 2022/11/5. // #include "RedSpineBakeManage.h" #include "RedBakeSpineIO.h" #include const static std::string REDSPINEBAKE_EXTENSION = ".rb"; static RedSpineBakeManage* single = nullptr; RedSpineBakeManage* RedSpineBakeManage::getInstance() { if (single == nullptr) { single = new RedSpineBakeManage(); } return single; } void RedSpineBakeManage::destoryInstance() { if (single) { single->_release(); delete single; single = nullptr; } } void RedSpineBakeManage::setCurrentAnimationBakeModel(std::string aName,RedAnimationBakeModel *aAni){ if(animateList[fileName][aName]==nullptr){ animateList[fileName][aName] = aAni; } } RedAnimationBakeModel * RedSpineBakeManage::getAnimateByName(std::string aFileName,std::string aName){ if(animateList.find(aFileName) != animateList.end()) { if (animateList[aFileName].count(aName) > 0) { return animateList[aFileName][aName]; } else { const ::google::protobuf::Map& dataIndex_pb = dataIndexPbList[aFileName]; auto iter = dataIndex_pb.find(aName); if (iter == dataIndex_pb.end()) { // log("sc test android getAnimationByName 索引失败"); // CCASSERT(false, "索引失败"); // return nullptr; } const RedSpineBakeProto::DataInfo& dataInfo = iter->second; ::RedSpineBakeProto::RedAnimationBakeModel model_pb; const cocos2d::Data& byteData = byteArrayList[aFileName]; ///如果byteArrayList为空 且为低端机 需要重新进行io 读取对应的动作信息 if(byteData.getBytes() == NULL && !CocosConfig::isCharacterRbCache()){ bool isOk = false; #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID JniMethodInfo methodInfo; jbyteArray byteArray = nullptr; if (JniHelper::getStaticMethodInfo(methodInfo, BinaryFileClassName, "loadData", "(Ljava/lang/String;II)[B")) { jstring jFileName = methodInfo.env->NewStringUTF((aFileName+REDSPINEBAKE_EXTENSION).c_str()); jbyteArray result = (jbyteArray)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, jFileName, (int)dataInfo.offset(), (int)dataInfo.length()); byteArray = (jbyteArray)methodInfo.env->NewGlobalRef(result); // 释放 Java 字符串 methodInfo.env->DeleteLocalRef(result); methodInfo.env->DeleteLocalRef(methodInfo.classID); methodInfo.env->DeleteLocalRef(jFileName); if (byteArray != nullptr) { jbyte* byteData = methodInfo.env->GetByteArrayElements(byteArray, NULL); BYTE* byteArr = (BYTE*)byteData; isOk = model_pb.ParseFromArray(byteArr, dataInfo.length()); methodInfo.env->ReleaseByteArrayElements(byteArray, byteData, JNI_ABORT); } else{ isOk = false; } } #else std::string fileFullPath = FileUtils::getInstance()->fullPathForFilename( aFileName + REDSPINEBAKE_EXTENSION); ///重新进行io std::ifstream file(fileFullPath, std::ios::binary | std::ios::ate); if (!file.is_open()) { CCASSERT(false, "打开文件失败"); } // log("sc_test getAnimationByName,%s, %d, %d", aName.c_str(), dataInfo.offset(), dataInfo.length()); // 获取文件大小 std::streampos fileSize = file.tellg(); // 将文件指针移动到currentOffset的位置 file.seekg(dataInfo.offset(), std::ios::beg); // 读取大小为outIndexSize的内容 std::vector animData(dataInfo.length()); file.read(animData.data(), dataInfo.length()); // 关闭文件 file.close(); isOk = model_pb.ParseFromArray(animData.data(), animData.size()); #endif if(!isOk) { log("sc test android getAnimationByName model_pb失败"); return nullptr; } } else{ if (byteData.getBytes() == NULL){ return nullptr; } if (model_pb.ParseFromArray(byteData.getBytes()+dataInfo.offset(), dataInfo.length()) == false) { return nullptr; } } RedAnimationBakeModel* model = new RedAnimationBakeModel("", aName); spine::RedBakeSpineIO::getInstance()->readAnimationBakeModel(model_pb, model, filePath); animateList[aFileName][aName] = model; return model; } } return nullptr; } void RedSpineBakeManage::fetchAnimations(const std::string& aFileName, std::vector& anims) { auto iter = dataIndexPbList.find(aFileName); if (iter != dataIndexPbList.end()) { for (auto& iter1 : iter->second) { anims.push_back(iter1.first); } } } void RedSpineBakeManage::releaseAimationFile(std::string aFileName){ //把animateList[aFileName]下所有的RedAnimationBakeModel释放 if(animateList.find(aFileName) == animateList.end()) { CCASSERT(false, "没有这个键值"); return; } std::unordered_map bakeModelList = animateList[aFileName]; for(auto iter = bakeModelList.begin(); iter != bakeModelList.end(); iter++) { std::string animName = iter->first; RedAnimationBakeModel* model = iter->second; int slotCount = model->getSlotCount(); for(int i = 0; i < slotCount; ++i) { //释放RedSlotBakeModel int key = model->getAllSlotKey()[i]; RedSlotBakeModel* slot = model->getSlotFromKey(key); delete slot; } //释放RedAnimationBakeModel delete model; } animateList.erase(aFileName); } void RedSpineBakeManage::loadAnimationFileAsync(const std::string& aFileName, const std::string& aPath, bool isCharacter) { ///如果已经加载则直接返回 if (animateList.find(aFileName) != animateList.end()) { return; } if (dataIndexPbList.find(aFileName) != dataIndexPbList.end()) { return; } ///获取文件全路径,保证线程安全 std::string fileFullPath = ""; if (aPath.size() == 0) { fileFullPath = FileUtils::getInstance()->fullPathForFilename(aFileName + REDSPINEBAKE_EXTENSION); } else { fileFullPath = aPath + aFileName + REDSPINEBAKE_EXTENSION; } ///文件不存在则直接返回 if (FileUtils::getInstance()->isFileExist(fileFullPath) == false) { return; } ///创建异步加载请求表单 AsyncLoadAnimationFileRequest* request = new AsyncLoadAnimationFileRequest(); request->key = aFileName; request->fileFullPath = fileFullPath; request->isCharacter = isCharacter; std::unique_lock ul(_requestsMtx); _requests.push(request); ul.unlock(); ///异步线程使用懒汉式创建 if (_loadThread == nullptr) { _loadThread = new std::thread(&RedSpineBakeManage::_readAnimationFileInThread, this); } ///唤醒休眠线程 _sleepCondition.notify_one(); } void RedSpineBakeManage::loadAnimationFile(const std::string& aFileName, const std::string& aPath, bool isCharacter){ if(animateList.find(aFileName) != animateList.end()) { // CCASSERT(false, "重复加载"); return; } if (dataIndexPbList.find(aFileName) != dataIndexPbList.end()) { return; } filePath = aPath; std::unordered_map datas; animateList[aFileName] = datas; std::string fileFullPath = ""; if (aPath.size() == 0) { fileFullPath = FileUtils::getInstance()->fullPathForFilename( aFileName + REDSPINEBAKE_EXTENSION); } else { fileFullPath = aPath + aFileName + REDSPINEBAKE_EXTENSION; } { if(FileUtils::getInstance()->isFileExist( fileFullPath ) == false) { CCASSERT(false, "rb文件没找到"); return; } google::protobuf::Map dataIndexPb; const cocos2d::Data& data = _parseProtoData(fileFullPath, aFileName+REDSPINEBAKE_EXTENSION, dataIndexPb, isCharacter); dataIndexPbList[aFileName] = dataIndexPb; if (data.getSize() > 0) { byteArrayList[aFileName] = data; } } } //********************************* void RedSpineBakeManage::endBakeSaveToFile(std::string aPath){ filePath = aPath; //保存所有animateList到aPath for(auto iter = animateList.begin(); iter != animateList.end(); ++iter) { std::string aFileName = iter->first; if(aFileName.empty()) { continue; } std::unordered_map datas = iter->second; std::string finalPath = filePath + "/" + aFileName + REDSPINEBAKE_EXTENSION; spine::RedBakeSpineIO::getInstance()->write(finalPath, datas); } } void RedSpineBakeManage::startBake(std::string aFileName){ fileName = aFileName; } void RedSpineBakeManage::_release() { if (_loadThread) { _loadThread->join(); delete _loadThread; } } void RedSpineBakeManage::_readAnimationFileInThread() { while (true) { AsyncLoadAnimationFileRequest* request = nullptr; std::unique_lock ul(_requestsMtx); if (_requests.empty() == false) { request = _requests.front(); _requests.pop(); } if (request == nullptr) { _sleepCondition.wait(ul); continue; } ul.unlock(); google::protobuf::Map dataIndexPb; const cocos2d::Data& data = _parseProtoData(request->fileFullPath, request->key+REDSPINEBAKE_EXTENSION, dataIndexPb, request->isCharacter); std::string tFileName = request->key; if (data.getSize() > 0) { cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([this, dataIndexPb, data, tFileName](void) { ///都需要先判断数据是否存在,防止和主线程冲突 if (animateList.find(tFileName) == animateList.end()) { std::unordered_map datas; animateList[tFileName] = datas; } if (byteArrayList.find(tFileName) == byteArrayList.end()) { byteArrayList[tFileName] = data; } if (dataIndexPbList.find(tFileName) == dataIndexPbList.end()) { dataIndexPbList[tFileName] = dataIndexPb; } }); } delete request; } } cocos2d::Data RedSpineBakeManage::_parseProtoData(const std::string& fileFullPath, const std::string& fileName, google::protobuf::Map& dataIndexPb, bool isCharacter) { cocos2d::Data ret; if(!isCharacter){ ret = cocos2d::FileUtils::getInstance()->getDataFromFile(fileFullPath); if (ret.getSize() > 0) { const unsigned char* dataPtr = ret.getBytes(); // 从数据末尾读取两个32位整数 uint32_t currentOffset, outIndexSize; // 获取currentOffset和outIndexSize std::memcpy(¤tOffset, dataPtr + (ret.getSize() - 2 * sizeof(uint32_t)), sizeof(uint32_t)); std::memcpy(&outIndexSize, dataPtr + (ret.getSize() - sizeof(uint32_t)), sizeof(uint32_t)); RedSpineBakeProto::BakeDataIndex dataIndex; bool isOk = dataIndex.ParseFromArray(ret.getBytes()+currentOffset, outIndexSize); if (isOk) { dataIndexPb = dataIndex.dataindex(); } else { CCASSERT(false, "解析索引文件失败"); } } return ret; } RedSpineBakeProto::BakeDataIndex dataIndex; uint32_t currentOffset, outIndexSize; bool isOk = false; #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID JniMethodInfo methodInfo; jbyteArray byteArray = nullptr; if (JniHelper::getStaticMethodInfo(methodInfo, BinaryFileClassName, "loadData", "(Ljava/lang/String;II)[B")) { jstring jFileName = methodInfo.env->NewStringUTF(fileName.c_str()); // 调用获取最后两个32位整数的方法 jintArray jIntegers = (jintArray)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.env->GetStaticMethodID(methodInfo.classID, "getLastTwoIntegers", "(Ljava/lang/String;)[I"), jFileName); // 将 Java 数组拷贝到 C++ 数组 jint* intValues = methodInfo.env->GetIntArrayElements(jIntegers, nullptr); currentOffset = static_cast(intValues[0]); outIndexSize = static_cast(intValues[1]); __android_log_print(ANDROID_LOG_DEBUG, "sc test android info", "%d, %d", currentOffset, outIndexSize); // 释放 Java 数组 methodInfo.env->ReleaseIntArrayElements(jIntegers, intValues, JNI_ABORT); jbyteArray result = (jbyteArray)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, jFileName, (int)currentOffset, (int)outIndexSize); byteArray = (jbyteArray)methodInfo.env->NewGlobalRef(result); if (byteArray != nullptr) { jbyte* byteData = methodInfo.env->GetByteArrayElements(byteArray, NULL); BYTE* byteArr = (BYTE*)byteData; isOk = dataIndex.ParseFromArray(byteArr, outIndexSize); methodInfo.env->ReleaseByteArrayElements(byteArray, byteData, JNI_ABORT); methodInfo.env->DeleteLocalRef(result); methodInfo.env->DeleteLocalRef(methodInfo.classID); } else{ isOk = false; } } #else std::ifstream file(fileFullPath, std::ios::binary | std::ios::ate); if (!file.is_open()) { CCASSERT(false, "打开文件失败"); } // 获取文件大小 std::streampos fileSize = file.tellg(); // 将文件指针移动到文件末尾 file.seekg(-2 * sizeof(uint32_t), std::ios::end); // 读取两个32位整数 file.read(reinterpret_cast(¤tOffset), sizeof(uint32_t)); file.read(reinterpret_cast(&outIndexSize), sizeof(uint32_t)); // log("sc_test _parseProtoData %d, %d, %d", fileSize, currentOffset, outIndexSize); // 将文件指针移动到currentOffset的位置 file.seekg(currentOffset, std::ios::beg); // 读取大小为outIndexSize的内容 std::vector indexData(outIndexSize); file.read(indexData.data(), outIndexSize); // 关闭文件 file.close(); // 初始化RedSpineBakeProto::BakeDataIndex isOk = dataIndex.ParseFromArray(indexData.data(), indexData.size()); // log("sc_test _parseProtoData %s, %d", fileFullPath.c_str(), indexData.size()); #endif if (isOk) { dataIndexPb = dataIndex.dataindex(); } else { CCASSERT(false, "解析索引文件失败"); } ///如果为高端机 缓存rb就进行下面这个 if(isCharacter && CocosConfig::isCharacterRbCache()){ ret = cocos2d::FileUtils::getInstance()->getDataFromFile(fileFullPath); } return ret; }