//====================================================================================================== // Copyright 2025, Rokoko Glove OptiTrack Integration //====================================================================================================== #include "LZ4Wrapper.h" #include #include #include // Unity LZ4 DLL 동적 로딩 static HMODULE lz4Module = nullptr; static decltype(&Unity_LZ4_uncompressSize) pUnity_LZ4_uncompressSize = nullptr; static decltype(&Unity_LZ4_decompress) pUnity_LZ4_decompress = nullptr; static bool LoadUnityLZ4() { if (lz4Module == nullptr) { // 다양한 경로에서 Unity LZ4 DLL 로드 시도 const wchar_t* dllPaths[] = { L"lz4.dll", // 현재 디렉토리 L".\\lz4.dll", // 명시적 현재 디렉토리 L"x64\\Debug\\lz4.dll", // 디버그 폴더 L"C:\\Users\\user\\Documents\\Streamingle_URP\\Assets\\External\\Rokoko\\Scripts\\Plugins\\LZ4\\x86_64\\lz4.dll" // Unity 절대 경로 }; for (const auto& path : dllPaths) { lz4Module = LoadLibrary(path); if (lz4Module != nullptr) { // DLL 로드 성공 - 함수 포인터 획득 pUnity_LZ4_uncompressSize = (decltype(pUnity_LZ4_uncompressSize))GetProcAddress(lz4Module, "Unity_LZ4_uncompressSize"); pUnity_LZ4_decompress = (decltype(pUnity_LZ4_decompress))GetProcAddress(lz4Module, "Unity_LZ4_decompress"); if (pUnity_LZ4_uncompressSize != nullptr && pUnity_LZ4_decompress != nullptr) { // 성공 break; } else { // 함수를 찾지 못함 - DLL 언로드하고 다음 시도 FreeLibrary(lz4Module); lz4Module = nullptr; pUnity_LZ4_uncompressSize = nullptr; pUnity_LZ4_decompress = nullptr; } } } } return (lz4Module != nullptr && pUnity_LZ4_uncompressSize != nullptr && pUnity_LZ4_decompress != nullptr); } namespace RokokoIntegration { std::vector LZ4Wrapper::Decompress(const uint8_t* compressedData, int compressedSize) { try { // 입력 데이터 검증 if (!compressedData || compressedSize <= 0) { return {}; } // Unity LZ4 DLL 로드 if (!LoadUnityLZ4()) { return {}; } // Unity 방식 정확히 구현 // 1. Unity_LZ4_uncompressSize로 압축 해제된 크기 구하기 int uncompressedSize = pUnity_LZ4_uncompressSize( reinterpret_cast(compressedData), compressedSize ); if (uncompressedSize <= 0) { return {}; } // 크기 유효성 검사 if (uncompressedSize > MAX_DECOMPRESSED_SIZE) { return {}; } // 2. Unity_LZ4_decompress로 압축 해제 std::vector decompressed(uncompressedSize); int result = pUnity_LZ4_decompress( reinterpret_cast(compressedData), compressedSize, reinterpret_cast(decompressed.data()), uncompressedSize ); // Unity에서는 result != 0이면 실패 if (result != 0) { return {}; } return decompressed; } catch (...) { // 모든 예외 상황에서 안전하게 빈 벡터 반환 return {}; } } bool LZ4Wrapper::IsValidLZ4Data(const uint8_t* data, int size) { if (!data || size < 4) { return false; } // Rokoko Studio의 LZ4 데이터는 일반적으로 JSON이 아니므로 // 첫 번째 바이트가 '{'가 아닌 경우 LZ4로 간주 if (size > 0 && data[0] == '{') { return false; // JSON 데이터는 LZ4가 아님 } // 기본적인 LZ4 매직 넘버 확인 // LZ4 프레임 헤더의 일부 패턴 확인 if (size >= 4) { // LZ4 프레임 헤더의 매직 넘버 (0x184D2204) if (data[0] == 0x04 && data[1] == 0x22 && data[2] == 0x4D && data[3] == 0x18) { return true; } } // 매직 넘버가 없어도 압축된 데이터로 간주 (Rokoko Studio 특성상) return true; } int LZ4Wrapper::GetDecompressedSize(const uint8_t* compressedData, int compressedSize) { try { // Unity LZ4 DLL 로드 if (!LoadUnityLZ4()) { return -1; } // Unity 방식으로 압축 해제된 크기 구하기 int decompressedSize = pUnity_LZ4_uncompressSize( reinterpret_cast(compressedData), compressedSize ); return decompressedSize; } catch (...) { return -1; } } bool LZ4Wrapper::ValidateDecompressedSize(int compressedSize, int decompressedSize) { // 압축 해제된 크기가 합리적인 범위인지 확인 if (decompressedSize <= 0 || decompressedSize > MAX_DECOMPRESSED_SIZE) { return false; } // 압축률이 합리적인지 확인 (일반적으로 1:1 ~ 1:10) if (decompressedSize < compressedSize || decompressedSize > compressedSize * 10) { return false; } return true; } }