Merge pull request #3189 from gookie-dev/perf/ngs2-parse-waveform-file-headers
perf(libSceNgs2.native): parse waveform files without reading their s…
This commit is contained in:
commit
ecd82056fa
|
|
@ -217,14 +217,16 @@ struct RiffChunks {
|
|||
std::size_t dataSize = 0;
|
||||
};
|
||||
|
||||
static int ReadChunks(const std::uint8_t* bytes, std::size_t size, RiffChunks& chunks) {
|
||||
template <class TRead>
|
||||
static int ReadChunks(std::size_t size, TRead&& read, RiffChunks& chunks) {
|
||||
if (size < 12) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
if (std::memcmp(bytes, "RIFF", 4) != 0 || std::memcmp(bytes + 8, "WAVE", 4) != 0) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
|
||||
const std::size_t riffEnd = 8 + static_cast<std::size_t>(ReadLe32(bytes + 4));
|
||||
const auto* header = read(0, 12);
|
||||
if (std::memcmp(header, "RIFF", 4) != 0 || std::memcmp(header + 8, "WAVE", 4) != 0) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
|
||||
const std::size_t riffEnd = 8 + static_cast<std::size_t>(ReadLe32(header + 4));
|
||||
if (riffEnd < 12) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
const std::size_t end = std::min(size, riffEnd);
|
||||
for (std::size_t offset = 12; offset + 8 <= end;) {
|
||||
const auto* chunk = bytes + offset;
|
||||
const auto* chunk = read(offset, 8);
|
||||
const std::size_t payload = offset + 8;
|
||||
const std::size_t chunkSize = ReadLe32(chunk + 4);
|
||||
if (chunkSize > riffEnd - payload) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
|
|
@ -236,13 +238,13 @@ static int ReadChunks(const std::uint8_t* bytes, std::size_t size, RiffChunks& c
|
|||
}
|
||||
if (chunkSize > end - payload) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
if (std::memcmp(chunk, "fmt ", 4) == 0) {
|
||||
chunks.format = bytes + payload;
|
||||
chunks.format = read(payload, chunkSize);
|
||||
chunks.formatSize = chunkSize;
|
||||
} else if (std::memcmp(chunk, "fact", 4) == 0) {
|
||||
chunks.fact = bytes + payload;
|
||||
chunks.fact = read(payload, chunkSize);
|
||||
chunks.factSize = chunkSize;
|
||||
} else if (std::memcmp(chunk, "smpl", 4) == 0) {
|
||||
chunks.sampler = bytes + payload;
|
||||
chunks.sampler = read(payload, chunkSize);
|
||||
chunks.samplerSize = chunkSize;
|
||||
}
|
||||
offset = payload + chunkSize + (chunkSize & 1);
|
||||
|
|
@ -349,6 +351,17 @@ static int ParsePcm16(const RiffChunks& chunks, std::size_t size, Ngs2WaveformIn
|
|||
return SCE_NGS2_OK;
|
||||
}
|
||||
|
||||
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_PCM) return ParsePcm16(chunks, size, info);
|
||||
throw std::runtime_error("NGS2: parsing waveform format tag " + Ngs2Hex(tag) + " is not implemented");
|
||||
}
|
||||
|
||||
#pragma GCC visibility push(default)
|
||||
|
||||
extern "C" {
|
||||
|
|
@ -357,17 +370,11 @@ int APS5_VABI sceNgs2ParseWaveformData(const void* data, size_t data_size, Ngs2W
|
|||
if (info == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
|
||||
*info = {};
|
||||
if (data == nullptr) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
const auto* bytes = static_cast<const std::uint8_t*>(data);
|
||||
RiffChunks chunks;
|
||||
const int result = ReadChunks(static_cast<const std::uint8_t*>(data), data_size, chunks);
|
||||
const int result = ReadChunks(data_size, [bytes](std::size_t at, std::size_t) { return bytes + at; }, chunks);
|
||||
if (result != SCE_NGS2_OK) return result;
|
||||
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_PCM) return ParsePcm16(chunks, data_size, *info);
|
||||
throw std::runtime_error("NGS2: parsing waveform format tag " + Ngs2Hex(tag) + " is not implemented");
|
||||
return ParseWaveform(chunks, data_size, *info);
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2ParseWaveformFile(const char* path, uint32_t offset, Ngs2WaveformInfo* info) {
|
||||
|
|
@ -379,11 +386,18 @@ int APS5_VABI sceNgs2ParseWaveformFile(const char* path, uint32_t offset, Ngs2Wa
|
|||
if (!file) throw std::runtime_error(std::string(__func__) + ": cannot open " + host.string());
|
||||
const auto fileSize = static_cast<std::uint64_t>(file.tellg());
|
||||
if (offset > fileSize) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
const std::uint64_t length = fileSize - offset;
|
||||
std::vector<std::uint8_t> bytes(static_cast<std::size_t>(length));
|
||||
file.seekg(offset);
|
||||
if (!file.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()))) throw std::runtime_error(std::string(__func__) + ": read failed for " + host.string());
|
||||
const int result = sceNgs2ParseWaveformData(bytes.data(), bytes.size(), info);
|
||||
const auto length = static_cast<std::size_t>(fileSize - offset);
|
||||
const std::string function = __func__;
|
||||
std::vector<std::vector<std::uint8_t>> pieces;
|
||||
const auto readBytes = [&](std::size_t at, std::size_t count) {
|
||||
auto& piece = pieces.emplace_back(std::max<std::size_t>(count, 1));
|
||||
file.seekg(static_cast<std::streamoff>(offset + at));
|
||||
if (!file.read(reinterpret_cast<char*>(piece.data()), static_cast<std::streamsize>(count))) throw std::runtime_error(function + ": read failed for " + host.string());
|
||||
return static_cast<const std::uint8_t*>(piece.data());
|
||||
};
|
||||
RiffChunks chunks;
|
||||
int result = ReadChunks(length, readBytes, chunks);
|
||||
if (result == SCE_NGS2_OK) result = ParseWaveform(chunks, length, *info);
|
||||
if (result != SCE_NGS2_OK) return result;
|
||||
info->data_offset += offset;
|
||||
for (std::uint32_t i = 0; i < info->num_blocks; i++) info->block[i].data_offset += offset;
|
||||
|
|
|
|||
|
|
@ -87,8 +87,35 @@ static void TestParseFile() {
|
|||
std::filesystem::remove(path);
|
||||
}
|
||||
|
||||
static void TestParseFileBank() {
|
||||
const auto first = PcmFile(2, 48000, 16, 300);
|
||||
auto second = PcmFile(1, 32000, 16, 50);
|
||||
PutTag(second, "LIST");
|
||||
Put32(second, 4);
|
||||
PutTag(second, "INFO");
|
||||
const std::uint32_t riffSize = static_cast<std::uint32_t>(second.size() - 8);
|
||||
std::memcpy(second.data() + 4, &riffSize, sizeof(riffSize));
|
||||
const char* const guestPath = "/aps5_ngs2_parse_bank.bin";
|
||||
const auto path = ResolvePath_nid_no_patch(guestPath);
|
||||
{
|
||||
std::ofstream out(path, std::ios::binary);
|
||||
out.write(reinterpret_cast<const char*>(first.data()), static_cast<std::streamsize>(first.size()));
|
||||
out.write(reinterpret_cast<const char*>(second.data()), static_cast<std::streamsize>(second.size()));
|
||||
}
|
||||
Ngs2WaveformInfo info{};
|
||||
Require(sceNgs2ParseWaveformFile(guestPath, 0, &info) == SCE_NGS2_OK);
|
||||
Require(info.format.num_channels == 2 && info.format.sample_rate == 48000 && info.num_samples == 300);
|
||||
Require(info.data_offset == 44 && info.data_size == 1200 && info.block[0].data_offset == 44);
|
||||
const auto secondOffset = static_cast<std::uint32_t>(first.size());
|
||||
Require(sceNgs2ParseWaveformFile(guestPath, secondOffset, &info) == SCE_NGS2_OK);
|
||||
Require(info.format.num_channels == 1 && info.format.sample_rate == 32000 && info.num_samples == 50);
|
||||
Require(info.data_offset == secondOffset + 44 && info.data_size == 100 && info.block[0].data_offset == secondOffset + 44);
|
||||
std::filesystem::remove(path);
|
||||
}
|
||||
|
||||
int main() {
|
||||
TestParsePcm();
|
||||
TestParseFile();
|
||||
TestParseFileBank();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue