#include #include #include #include namespace XCEngine { namespace Resources { namespace { struct WAVHeader { uint32_t sampleRate = 44100; uint32_t channels = 2; uint32_t bitsPerSample = 16; uint32_t dataSize = 0; uint32_t dataOffset = 0; }; uint16_t ReadLE16(const uint8_t* data) { return static_cast(data[0]) | (static_cast(data[1]) << 8); } uint32_t ReadLE32(const uint8_t* data) { return static_cast(data[0]) | (static_cast(data[1]) << 8) | (static_cast(data[2]) << 16) | (static_cast(data[3]) << 24); } bool ParseWAVHeader(const uint8_t* data, size_t size, WAVHeader& header) { if (size < 12) { return false; } if (std::memcmp(data, "RIFF", 4) != 0) { return false; } if (std::memcmp(data + 8, "WAVE", 4) != 0) { return false; } bool foundFormatChunk = false; bool foundDataChunk = false; size_t offset = 12; while (offset + 8 <= size) { const uint8_t* chunk = data + offset; const uint32_t chunkSize = ReadLE32(chunk + 4); const size_t chunkDataOffset = offset + 8; if (chunkDataOffset > size || chunkSize > size - chunkDataOffset) { return false; } if (std::memcmp(chunk, "fmt ", 4) == 0) { if (chunkSize < 16) { return false; } const uint16_t audioFormat = ReadLE16(chunk + 8); if (audioFormat != 1) { return false; } header.channels = ReadLE16(chunk + 10); header.sampleRate = ReadLE32(chunk + 12); header.bitsPerSample = ReadLE16(chunk + 22); foundFormatChunk = true; } else if (std::memcmp(chunk, "data", 4) == 0) { header.dataOffset = static_cast(chunkDataOffset); header.dataSize = chunkSize; foundDataChunk = true; } offset = chunkDataOffset + chunkSize + (chunkSize & 1u); } if (!foundFormatChunk || !foundDataChunk) { return false; } if (header.channels == 0 || header.sampleRate == 0 || header.bitsPerSample == 0) { return false; } if ((header.bitsPerSample % 8u) != 0u) { return false; } const uint32_t bytesPerFrame = static_cast(header.channels) * (header.bitsPerSample / 8u); if (bytesPerFrame == 0 || (header.dataSize % bytesPerFrame) != 0u) { return false; } return header.dataOffset + header.dataSize <= size; } } // namespace AudioLoader::AudioLoader() = default; AudioLoader::~AudioLoader() = default; Containers::Array AudioLoader::GetSupportedExtensions() const { Containers::Array extensions; extensions.PushBack("wav"); return extensions; } bool AudioLoader::CanLoad(const Containers::String& path) const { Containers::String ext = GetExtension(path); return ext == "wav"; } LoadResult AudioLoader::Load(const Containers::String& path, const ImportSettings* settings) { Containers::Array data = ReadFileData(path); if (data.Empty()) { return LoadResult("Failed to read audio file: " + path); } AudioClip* audioClip = new AudioClip(); audioClip->m_path = path; audioClip->m_name = path; audioClip->m_guid = ResourceGUID::Generate(path); AudioFormat format = DetectAudioFormat(path, data); if (format != AudioFormat::WAV) { delete audioClip; return LoadResult("Unsupported audio format: " + path); } if (!ParseWAVData(data, audioClip)) { delete audioClip; return LoadResult("Failed to parse WAV data"); } audioClip->SetAudioFormat(format); audioClip->m_isValid = true; audioClip->m_memorySize = sizeof(AudioClip) + audioClip->m_name.Length() + audioClip->m_path.Length() + audioClip->GetPCMData().Size(); return LoadResult(audioClip); } ImportSettings* AudioLoader::GetDefaultSettings() const { return nullptr; } bool AudioLoader::ParseWAVData(const Containers::Array& data, AudioClip* audioClip) { if (audioClip == nullptr) { return false; } WAVHeader header; if (!ParseWAVHeader(data.Data(), data.Size(), header)) { return false; } audioClip->SetSampleRate(header.sampleRate); audioClip->SetChannels(header.channels); audioClip->SetBitsPerSample(header.bitsPerSample); Containers::Array pcmData; pcmData.ResizeUninitialized(header.dataSize); if (header.dataSize > 0) { std::memcpy( pcmData.Data(), data.Data() + header.dataOffset, header.dataSize); } audioClip->SetPCMData(pcmData); return true; } AudioFormat AudioLoader::DetectAudioFormat(const Containers::String& path, const Containers::Array& data) { Containers::String ext = GetExtension(path); if (ext == "wav") return AudioFormat::WAV; if (ext == "ogg") return AudioFormat::OGG; if (ext == "mp3") return AudioFormat::MP3; if (ext == "flac") return AudioFormat::FLAC; if (data.Size() >= 4) { if (data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F') { return AudioFormat::WAV; } } return AudioFormat::Unknown; } } // namespace Resources } // namespace XCEngine