Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 86 additions & 14 deletions core/libs/prx/libSceNgs2.native/src/Atrac9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,36 @@
#include "Ngs2Internal.hpp"

static constexpr std::uint16_t WAVE_FORMAT_PCM = 0x1;
static constexpr std::uint16_t WAVE_FORMAT_IEEE_FLOAT = 0x3;
static constexpr std::uint16_t WAVE_FORMAT_EXTENSIBLE = 0xfffe;
static constexpr std::uint8_t ATRAC9_GUID[16] = {0xd2, 0x42, 0xe1, 0x47, 0xba, 0x36, 0x8d, 0x4d, 0x88, 0xfc, 0x61, 0x65, 0x4f, 0x8c, 0x83, 0x6c};
static constexpr std::uint8_t PCM_GUID[16] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71};
static constexpr std::uint8_t TYPE_43_GUID[16] = {0x1c, 0xe0, 0x10, 0x9e, 0xcc, 0x8b, 0xd9, 0x47, 0x8f, 0x3b, 0xe3, 0x0b, 0x8b, 0xdc, 0x35, 0x29};
static constexpr std::size_t FMT_EXTENSIBLE_SIZE = 40;
static constexpr std::size_t FMT_ATRAC9_SIZE = 52;
static constexpr std::size_t FMT_GUID_OFFSET = 24;
static constexpr std::size_t FMT_CONFIG_OFFSET = 44;
static constexpr std::size_t FACT_ATRAC9_SIZE = 12;
static constexpr std::uint32_t MAX_SAMPLE_RATE = 192000;
static constexpr std::uint32_t WAVEFORM_TYPE_PCM_FIRST = 0x10;
static constexpr std::uint32_t WAVEFORM_TYPE_PCM_LAST = 0x1b;
static constexpr std::uint32_t WAVEFORM_TYPE_VAG = 0x1c;
static constexpr std::uint32_t PCM_BYTES_PER_SAMPLE[] = {1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 8, 8};

static bool IsPcmType(std::uint32_t type) {
return type >= WAVEFORM_TYPE_PCM_FIRST && type <= WAVEFORM_TYPE_PCM_LAST;
}

static Ngs2WaveformBlock PcmBlock(std::uint32_t type, std::uint32_t channels, std::uint32_t pos, std::uint32_t count) {
const std::uint64_t stride = static_cast<std::uint64_t>(channels) * PCM_BYTES_PER_SAMPLE[type - WAVEFORM_TYPE_PCM_FIRST];
const auto offset = static_cast<std::uint32_t>(pos * stride);
const auto end = (pos + count) * static_cast<std::uint32_t>(stride);
Ngs2WaveformBlock block{};
block.data_offset = offset;
block.data_size = static_cast<std::uint32_t>(end - offset);
block.num_samples = count;
return block;
}

void Ngs2Atrac9DecoderDeleter::operator()(void* handle) const {
Atrac9ReleaseHandle(handle);
Expand Down Expand Up @@ -323,19 +346,52 @@ static int ParseAtrac9(const RiffChunks& chunks, Ngs2WaveformInfo& info) {
return ParseAtrac9Loop(chunks, info);
}

static int ParsePcm16(const RiffChunks& chunks, std::size_t size, Ngs2WaveformInfo& info) {
static int ParsePcmLoop(const RiffChunks& chunks, Ngs2WaveformInfo& info) {
if (chunks.sampler == nullptr) return SCE_NGS2_OK;
if (chunks.samplerSize < 32) throw std::runtime_error("NGS2: a sampler chunk without its loop count is not implemented");
if (ReadLe32(chunks.sampler + 28) == 0) return SCE_NGS2_OK;
if (chunks.samplerSize < 52) throw std::runtime_error("NGS2: a sampler chunk without its first loop is not implemented");
const std::uint32_t begin = ReadLe32(chunks.sampler + 44);
const std::uint32_t end = ReadLe32(chunks.sampler + 48) + 1;
info.loop_begin_position = begin;
info.loop_end_position = end;
if (end <= begin) return SCE_NGS2_OK;
if (end > info.num_samples) throw std::runtime_error("NGS2: a waveform loop that ends after the last sample is not implemented");
info.num_blocks = 0;
const auto add = [&info](std::uint32_t pos, std::uint32_t count, std::uint32_t repeats) {
auto& block = info.block[info.num_blocks++];
block = PcmBlock(info.format.waveform_type, info.format.num_channels, pos, count);
block.data_offset += info.data_offset;
block.num_repeats = repeats;
};
if (begin != 0) add(0, begin, 0);
add(begin, end - begin, UINT32_MAX);
if (end != info.num_samples) add(end, info.num_samples - end, 0);
return SCE_NGS2_OK;
}

static std::uint32_t PcmType(std::uint16_t tag, std::uint32_t bits) {
if (tag == WAVE_FORMAT_IEEE_FLOAT) return bits == 32 ? 0x18 : bits == 64 ? 0x1a : 0;
switch (bits) {
case 8: return 0x11;
case 16: return 0x12;
case 24: return 0x14;
case 32: return 0x16;
default: return 0;
}
}

static int ParsePcm(const RiffChunks& chunks, std::size_t size, std::uint32_t type, Ngs2WaveformInfo& info) {
const auto* format = chunks.format;
if (chunks.formatSize < 16) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
const std::uint32_t channels = ReadLe16(format + 2);
const std::uint32_t sampleRate = ReadLe32(format + 4);
const std::uint32_t bytesPerFrame = channels * sizeof(std::int16_t);
if (ReadLe16(format + 14) != 16 || channels == 0 || channels > NGS2_MAX_CHANNELS || sampleRate == 0 || sampleRate > MAX_SAMPLE_RATE || ReadLe16(format + 12) != bytesPerFrame) {
const std::uint32_t bytesPerFrame = channels * PCM_BYTES_PER_SAMPLE[type - WAVEFORM_TYPE_PCM_FIRST];
if (channels == 0 || channels > NGS2_MAX_CHANNELS || sampleRate == 0 || sampleRate > MAX_SAMPLE_RATE || ReadLe16(format + 12) != bytesPerFrame) {
return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
}
if (chunks.sampler != nullptr && chunks.samplerSize >= 32 && ReadLe32(chunks.sampler + 28) != 0) throw std::runtime_error("NGS2: parsing looped waveforms is not implemented");
if (chunks.dataSize > size - chunks.dataOffset || chunks.dataSize % bytesPerFrame != 0) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
const std::size_t dataSize = chunks.dataSize;
info.format = {SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, channels, sampleRate, 0, 0, 0};
info.format = {type, channels, sampleRate, 0, 0, 0};
info.data_offset = static_cast<std::uint32_t>(chunks.dataOffset);
info.data_size = static_cast<std::uint32_t>(dataSize);
info.num_samples = static_cast<std::uint32_t>(dataSize / bytesPerFrame);
Expand All @@ -348,18 +404,27 @@ static int ParsePcm16(const RiffChunks& chunks, std::size_t size, Ngs2WaveformIn
info.block[0].data_offset = chunks.dataOffset;
info.block[0].data_size = dataSize;
info.block[0].num_samples = info.num_samples;
return SCE_NGS2_OK;
return ParsePcmLoop(chunks, info);
}

static int ParseWaveform(const RiffChunks& chunks, std::size_t size, Ngs2WaveformInfo& info) {
if (chunks.formatSize < 2) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
const std::uint16_t tag = ReadLe16(chunks.format);
if (tag == WAVE_FORMAT_EXTENSIBLE && chunks.formatSize >= FMT_GUID_OFFSET + sizeof(ATRAC9_GUID) &&
std::memcmp(chunks.format + FMT_GUID_OFFSET, ATRAC9_GUID, sizeof(ATRAC9_GUID)) == 0) {
return ParseAtrac9(chunks, info);
if (tag == WAVE_FORMAT_EXTENSIBLE) {
if (chunks.formatSize < FMT_EXTENSIBLE_SIZE) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
const auto* guid = chunks.format + FMT_GUID_OFFSET;
if (std::memcmp(guid, PCM_GUID, sizeof(PCM_GUID)) != 0) {
if (std::memcmp(guid, ATRAC9_GUID, sizeof(ATRAC9_GUID)) == 0 || chunks.formatSize == FMT_ATRAC9_SIZE) return ParseAtrac9(chunks, info);
if (std::memcmp(guid, TYPE_43_GUID, sizeof(TYPE_43_GUID)) == 0) throw std::runtime_error("NGS2: parsing waveforms of type 0x43 is not implemented");
return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
}
} else if (tag != WAVE_FORMAT_PCM && tag != WAVE_FORMAT_IEEE_FLOAT) {
return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
}
if (tag == WAVE_FORMAT_PCM) return ParsePcm16(chunks, size, info);
throw std::runtime_error("NGS2: parsing waveform format tag " + Ngs2Hex(tag) + " is not implemented");
if (chunks.formatSize < 16) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
const auto type = PcmType(tag, ReadLe16(chunks.format + 14));
if (type == 0) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
return ParsePcm(chunks, size, type, info);
}

#pragma GCC visibility push(default)
Expand Down Expand Up @@ -407,10 +472,17 @@ int APS5_VABI sceNgs2ParseWaveformFile(const char* path, uint32_t offset, Ngs2Wa
int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_t sample_pos, uint32_t num_samples, Ngs2WaveformBlock* block) {
if (block == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
*block = {};
if (format == nullptr || format->num_channels == 0 || format->num_channels > NGS2_MAX_CHANNELS || format->sample_rate == 0 || format->sample_rate > MAX_SAMPLE_RATE) {
if (format == nullptr) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
const auto type = format->waveform_type;
if (IsPcmType(type)) {
*block = PcmBlock(type, format->num_channels, sample_pos, num_samples);
return SCE_NGS2_OK;
}
if (type == WAVEFORM_TYPE_VAG) throw std::runtime_error("NGS2: waveform blocks of type " + Ngs2Hex(type) + " are not implemented");
if (type != SCE_NGS2_WAVEFORM_TYPE_ATRAC9) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
if (format->num_channels == 0 || format->num_channels > NGS2_MAX_CHANNELS || format->sample_rate == 0 || format->sample_rate > MAX_SAMPLE_RATE) {
return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
}
if (format->waveform_type != SCE_NGS2_WAVEFORM_TYPE_ATRAC9) throw std::runtime_error("NGS2: waveform blocks of type " + Ngs2Hex(format->waveform_type) + " are not implemented");
Atrac9CodecInfo codec{};
if (!MatchingCodecInfo(*format, codec)) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
const std::uint64_t superframeSamples = static_cast<std::uint64_t>(codec.frameSamples) * codec.framesInSuperframe;
Expand Down
35 changes: 35 additions & 0 deletions core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,40 @@ static void TestCalcBlock() {
Require(sceNgs2CalcWaveformBlock(&silent, 0, 1, &block) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
}

static void RequirePcmBlock(std::uint32_t type, std::uint32_t channels, std::uint32_t pos, std::uint32_t count, std::uint64_t offset, std::uint64_t size) {
const Ngs2WaveformFormat format{type, channels, 48000, 0, 0, 0};
Ngs2WaveformBlock block;
std::memset(&block, 0xab, sizeof(block));
Require(sceNgs2CalcWaveformBlock(&format, pos, count, &block) == SCE_NGS2_OK);
Require(block.data_offset == offset && block.data_size == size && block.num_samples == count);
Require(block.num_repeats == 0 && block.num_skip_samples == 0 && block.reserved == 0 && block.user_data == 0);
}

static void TestCalcPcmBlock() {
const std::uint32_t bytesPerSample[] = {1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 8, 8};
for (std::uint32_t type = 0x10; type <= 0x1b; ++type) {
const auto stride = 100u * bytesPerSample[type - 0x10];
RequirePcmBlock(type, 100, 3, 5, 3u * stride, 5u * stride);
}
RequirePcmBlock(SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 2, 100, 50, 400, 200);
RequirePcmBlock(SCE_NGS2_WAVEFORM_TYPE_PCM_F32L, 1, 10, 0, 40, 0);
RequirePcmBlock(SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 0, 7, 9, 0, 0);
RequirePcmBlock(0x1a, 8, 0x4000000, 1, 0, 64);
RequirePcmBlock(0x1a, 2, 0xffffffffu, 2, 0xfffffff0u, 32);
const Ngs2WaveformFormat noRate{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 2, 0, 0, 0, 0};
Ngs2WaveformBlock block{};
Require(sceNgs2CalcWaveformBlock(&noRate, 1, 1, &block) == SCE_NGS2_OK && block.data_offset == 4 && block.data_size == 4);
for (std::uint32_t type : {0x0u, 0xfu, 0x1du, 0x20u, 0x30u, 0x43u}) {
const Ngs2WaveformFormat unknown{type, 2, 48000, 0, 0, 0};
std::memset(&block, 0xab, sizeof(block));
Require(sceNgs2CalcWaveformBlock(&unknown, 0, 1, &block) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
const auto* bytes = reinterpret_cast<const std::uint8_t*>(&block);
Require(std::all_of(bytes, bytes + sizeof(block), [](std::uint8_t byte) { return byte == 0; }));
}
std::memset(&block, 0xab, sizeof(block));
Require(sceNgs2CalcWaveformBlock(nullptr, 0, 1, &block) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT && block.data_size == 0 && block.num_samples == 0);
}

static void Set32(std::vector<std::uint8_t>& bytes, std::size_t offset, std::uint32_t value) {
for (int i = 0; i < 4; ++i) bytes[offset + i] = static_cast<std::uint8_t>(value >> (i * 8));
}
Expand Down Expand Up @@ -504,6 +538,7 @@ int main() {
TestParseLoops();
TestMalformedLoops();
TestCalcBlock();
TestCalcPcmBlock();
TestCorruptSuperframeAtPageEnd();
TestStreamPartitions();
TestRefill(reference);
Expand Down
Loading
Loading