162 lines
5.8 KiB
C++

//======================================================================================================
// Copyright 2025, Rokoko Glove OptiTrack Integration
//======================================================================================================
#include "LZ4Wrapper.h"
#include <cstring>
#include <stdexcept>
#include <windows.h>
// 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<uint8_t> 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<const char*>(compressedData),
compressedSize
);
if (uncompressedSize <= 0) {
return {};
}
// 크기 유효성 검사
if (uncompressedSize > MAX_DECOMPRESSED_SIZE) {
return {};
}
// 2. Unity_LZ4_decompress로 압축 해제
std::vector<uint8_t> decompressed(uncompressedSize);
int result = pUnity_LZ4_decompress(
reinterpret_cast<const char*>(compressedData),
compressedSize,
reinterpret_cast<char*>(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<const char*>(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;
}
}