diff --git a/rpcs3/Emu/Cell/Modules/cellPamf.cpp b/rpcs3/Emu/Cell/Modules/cellPamf.cpp index 833fe43e32..d63e7bb3e2 100644 --- a/rpcs3/Emu/Cell/Modules/cellPamf.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPamf.cpp @@ -2,6 +2,7 @@ #include "Emu/System.h" #include "Emu/Cell/PPUModule.h" +#include #include "cellPamf.h" const std::function SQUEUE_ALWAYS_EXIT = []() { return true; }; @@ -35,404 +36,998 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } -error_code pamfStreamTypeToEsFilterId(u8 type, u8 ch, CellCodecEsFilterId& pEsFilterId) +error_code pamfVerifyMagicAndVersion(vm::cptr pAddr, vm::ptr pSelf) { - // convert type and ch to EsFilterId + ensure(!!pAddr); // Not checked on LLE + + if (pAddr->magic != std::bit_cast>("PAMF"_u32)) + { + return CELL_PAMF_ERROR_UNKNOWN_TYPE; + } + + if (pSelf) + { + pSelf->isPsmf = false; + } + + be_t version; + + if (pAddr->version == std::bit_cast>("0040"_u32)) + { + version = 40; + } + else if (pAddr->version == std::bit_cast>("0041"_u32)) + { + version = 41; + } + else + { + return CELL_PAMF_ERROR_UNSUPPORTED_VERSION; + } + + if (pSelf) + { + pSelf->version = version; + } + + return CELL_OK; +} + +error_code pamfGetHeaderAndDataSize(vm::cptr pAddr, u64 fileSize, vm::ptr headerSize, vm::ptr dataSize) +{ + ensure(!!pAddr); // Not checked on LLE + + if (error_code ret = pamfVerifyMagicAndVersion(pAddr, vm::null); ret != CELL_OK) + { + return ret; + } + + const u64 header_size = pAddr->header_size * 0x800ull; + const u64 data_size = pAddr->data_size * 0x800ull; + + if (header_size == 0 || (fileSize != 0 && header_size + data_size != fileSize)) + { + return CELL_PAMF_ERROR_INVALID_PAMF; + } + + if (headerSize) + { + *headerSize = header_size; + } + + if (dataSize) + { + *dataSize = data_size; + } + + return CELL_OK; +} + +error_code pamfTypeChannelToStream(u8 type, u8 ch, u8* stream_coding_type, u8* stream_id, u8* private_stream_id) +{ + // This function breaks if ch is greater than 15, LLE doesn't check for this ensure(ch < 16); - pEsFilterId.supplementalInfo1 = type == CELL_PAMF_STREAM_TYPE_AVC; - pEsFilterId.supplementalInfo2 = 0; + + u8 _stream_coding_type; + u8 _stream_id; + u8 _private_stream_id; switch (type) { case CELL_PAMF_STREAM_TYPE_AVC: { - // code = 0x1b - pEsFilterId.filterIdMajor = 0xe0 | ch; - pEsFilterId.filterIdMinor = 0; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_AVC; + _stream_id = 0xe0 | ch; + _private_stream_id = 0; break; } case CELL_PAMF_STREAM_TYPE_M2V: { - // code = 0x02 - pEsFilterId.filterIdMajor = 0xe0 | ch; - pEsFilterId.filterIdMinor = 0; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_M2V; + _stream_id = 0xe0 | ch; + _private_stream_id = 0; break; } case CELL_PAMF_STREAM_TYPE_ATRAC3PLUS: { - // code = 0xdc - pEsFilterId.filterIdMajor = 0xbd; - pEsFilterId.filterIdMinor = ch; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_ATRAC3PLUS; + _stream_id = 0xbd; + _private_stream_id = ch; break; } case CELL_PAMF_STREAM_TYPE_PAMF_LPCM: { - // code = 0x80 - pEsFilterId.filterIdMajor = 0xbd; - pEsFilterId.filterIdMinor = 0x40 | ch; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_PAMF_LPCM; + _stream_id = 0xbd; + _private_stream_id = 0x40 | ch; break; } case CELL_PAMF_STREAM_TYPE_AC3: { - // code = 0x81 - pEsFilterId.filterIdMajor = 0xbd; - pEsFilterId.filterIdMinor = 0x30 | ch; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_AC3; + _stream_id = 0xbd; + _private_stream_id = 0x30 | ch; break; } case CELL_PAMF_STREAM_TYPE_USER_DATA: { - // code = 0xdd - pEsFilterId.filterIdMajor = 0xbd; - pEsFilterId.filterIdMinor = 0x20 | ch; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_USER_DATA; + _stream_id = 0xbd; + _private_stream_id = 0x20 | ch; break; } - case 6: + case CELL_PAMF_STREAM_TYPE_PSMF_AVC: { - // code = 0xff - pEsFilterId.filterIdMajor = 0xe0 | ch; - pEsFilterId.filterIdMinor = 0; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_PSMF; + _stream_id = 0xe0 | ch; + _private_stream_id = 0; break; } - case 7: + case CELL_PAMF_STREAM_TYPE_PSMF_ATRAC3PLUS: { - // code = 0xff - pEsFilterId.filterIdMajor = 0xbd; - pEsFilterId.filterIdMinor = ch; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_PSMF; + _stream_id = 0xbd; + _private_stream_id = ch; break; } - case 8: + case CELL_PAMF_STREAM_TYPE_PSMF_LPCM: { - // code = 0xff - pEsFilterId.filterIdMajor = 0xbd; - pEsFilterId.filterIdMinor = 0x10 | ch; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_PSMF; + _stream_id = 0xbd; + _private_stream_id = 0x10 | ch; break; } - case 9: + case CELL_PAMF_STREAM_TYPE_PSMF_USER_DATA: { - // code = 0xff - pEsFilterId.filterIdMajor = 0xbd; - pEsFilterId.filterIdMinor = 0x20 | ch; + _stream_coding_type = PAMF_STREAM_CODING_TYPE_PSMF; + _stream_id = 0xbd; + _private_stream_id = 0x20 | ch; break; } default: { - cellPamf.fatal("pamfStreamTypeToEsFilterId(): unknown type (%d, ch=%d)", type, ch); + cellPamf.error("pamfTypeChannelToStream(): unknown type %d", type); return CELL_PAMF_ERROR_INVALID_ARG; } } + if (stream_coding_type) + { + *stream_coding_type = _stream_coding_type; + } + + if (stream_id) + { + *stream_id = _stream_id; + } + + if (private_stream_id) + { + *private_stream_id = _private_stream_id; + } + return CELL_OK; } -u8 pamfGetStreamType(vm::ptr pSelf, u32 stream) +error_code pamfStreamToTypeChannel(u8 stream_coding_type, u8 stream_id, u8 private_stream_id, vm::ptr type, vm::ptr ch) { - // TODO: get stream type correctly - ensure(stream < pSelf->pAddr->stream_count); - auto& header = pSelf->pAddr->stream_headers[stream]; + u8 _type; + u8 _ch; - switch (header.type) + switch (stream_coding_type) { - case 0x1b: return CELL_PAMF_STREAM_TYPE_AVC; - case 0x02: return CELL_PAMF_STREAM_TYPE_M2V; - case 0xdc: return CELL_PAMF_STREAM_TYPE_ATRAC3PLUS; - case 0x80: return CELL_PAMF_STREAM_TYPE_PAMF_LPCM; - case 0x81: return CELL_PAMF_STREAM_TYPE_AC3; - case 0xdd: return CELL_PAMF_STREAM_TYPE_USER_DATA; - default: break; + case PAMF_STREAM_CODING_TYPE_AVC: + _type = CELL_PAMF_STREAM_TYPE_AVC; + _ch = stream_id & 0x0f; + break; + + case PAMF_STREAM_CODING_TYPE_M2V: + _type = CELL_PAMF_STREAM_TYPE_M2V; + _ch = stream_id & 0x0f; + break; + + case PAMF_STREAM_CODING_TYPE_ATRAC3PLUS: + _type = CELL_PAMF_STREAM_TYPE_ATRAC3PLUS; + _ch = private_stream_id & 0x0f; + break; + + case PAMF_STREAM_CODING_TYPE_PAMF_LPCM: + _type = CELL_PAMF_STREAM_TYPE_PAMF_LPCM; + _ch = private_stream_id & 0x0f; + break; + + case PAMF_STREAM_CODING_TYPE_AC3: + _type = CELL_PAMF_STREAM_TYPE_AC3; + _ch = private_stream_id & 0x0f; + break; + + case PAMF_STREAM_CODING_TYPE_USER_DATA: + _type = CELL_PAMF_STREAM_TYPE_USER_DATA; + _ch = private_stream_id & 0x0f; + break; + + case PAMF_STREAM_CODING_TYPE_PSMF: + if ((stream_id & 0xf0) == 0xe0) + { + _type = CELL_PAMF_STREAM_TYPE_PSMF_AVC; + _ch = stream_id & 0x0f; + + if (private_stream_id != 0) + { + return CELL_PAMF_ERROR_STREAM_NOT_FOUND; + } + } + else if (stream_id == 0xbd) + { + _ch = private_stream_id & 0x0f; + + switch (private_stream_id & 0xf0) + { + case 0x00: _type = CELL_PAMF_STREAM_TYPE_PSMF_ATRAC3PLUS; break; + case 0x10: _type = CELL_PAMF_STREAM_TYPE_PSMF_LPCM; break; + case 0x20: _type = CELL_PAMF_STREAM_TYPE_PSMF_LPCM; break; // LLE doesn't use CELL_PAMF_STREAM_TYPE_PSMF_USER_DATA for some reason + default: return CELL_PAMF_ERROR_STREAM_NOT_FOUND; + } + } + else + { + return CELL_PAMF_ERROR_STREAM_NOT_FOUND; + } + break; + + default: + cellPamf.error("pamfStreamToTypeChannel(): unknown stream_coding_type 0x%02x", stream_coding_type); + return CELL_PAMF_ERROR_STREAM_NOT_FOUND; } - cellPamf.fatal("pamfGetStreamType(): unsupported stream type found(0x%x)", header.type); - return 0xff; + if (type) + { + *type = _type; + } + + if (ch) + { + *ch = _ch; + } + + return CELL_OK; } -u8 pamfGetStreamChannel(vm::ptr pSelf, u32 stream) +void pamfEpUnpack(vm::cptr ep_packed, vm::ptr ep) { - // TODO: get stream channel correctly - ensure(stream < pSelf->pAddr->stream_count); - auto& header = pSelf->pAddr->stream_headers[stream]; + ensure(!!ep_packed && !!ep); // Not checked on LLE - switch (header.type) - { - case 0x1b: // AVC - case 0x02: // M2V - { - ensure((header.fid_major & 0xf0) == 0xe0); - ensure(!header.fid_minor); - return header.fid_major % 16; - } - - case 0xdc: // ATRAC3PLUS - { - ensure((header.fid_major == 0xbd)); - ensure((header.fid_minor & 0xf0) == 0); - return header.fid_minor % 16; - } - - case 0x80: // LPCM - { - ensure((header.fid_major == 0xbd)); - ensure((header.fid_minor & 0xf0) == 0x40); - return header.fid_minor % 16; - } - case 0x81: // AC3 - { - ensure((header.fid_major == 0xbd)); - ensure((header.fid_minor & 0xf0) == 0x30); - return header.fid_minor % 16; - } - case 0xdd: - { - ensure((header.fid_major == 0xbd)); - ensure((header.fid_minor & 0xf0) == 0x20); - return header.fid_minor % 16; - } - default: break; - } - - cellPamf.fatal("pamfGetStreamChannel(): unsupported stream type found(0x%x)", header.type); - return 0xff; + ep->indexN = (ep_packed->value0 >> 14) + 1; + ep->nThRefPictureOffset = ((ep_packed->value0 & 0x1fff) * 0x800) + 0x800; + ep->pts.upper = ep_packed->pts_high; + ep->pts.lower = ep_packed->pts_low; + ep->rpnOffset = ep_packed->rpnOffset * 0x800ull; } +void psmfEpUnpack(vm::cptr ep_packed, vm::ptr ep) +{ + ensure(!!ep_packed && !!ep); // Not checked on LLE + + ep->indexN = (ep_packed->value0 >> 14) + 1; + ep->nThRefPictureOffset = ((ep_packed->value0 & 0xffe) * 0x400) + 0x800; + ep->pts.upper = ep_packed->value0 & 1; + ep->pts.lower = ep_packed->pts_low; + ep->rpnOffset = ep_packed->rpnOffset * 0x800ull; +} + +bool pamfIsSameStreamType(u8 type, u8 requested_type) +{ + switch (requested_type) + { + case CELL_PAMF_STREAM_TYPE_VIDEO: return type == CELL_PAMF_STREAM_TYPE_AVC || type == CELL_PAMF_STREAM_TYPE_M2V; + case CELL_PAMF_STREAM_TYPE_AUDIO: return type == CELL_PAMF_STREAM_TYPE_ATRAC3PLUS || type == CELL_PAMF_STREAM_TYPE_AC3 || type == CELL_PAMF_STREAM_TYPE_PAMF_LPCM; + case CELL_PAMF_STREAM_TYPE_UNK: return type == CELL_PAMF_STREAM_TYPE_PAMF_LPCM || type == CELL_PAMF_STREAM_TYPE_PSMF_ATRAC3PLUS; // ??? no idea what this is for + default: return requested_type == type; + } +} + +error_code pamfVerify(vm::cptr pAddr, u64 fileSize, vm::ptr pSelf, u32 attribute) +{ + ensure(!!pAddr); // Not checked on LLE + + if (error_code ret = pamfVerifyMagicAndVersion(pAddr, pSelf); ret != CELL_OK) + { + return ret; + } + + const u64 header_size = pAddr->header_size * 0x800ull; + const u64 data_size = pAddr->data_size * 0x800ull; + + // Header size + if (header_size == 0) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid header_size" }; + } + + if (pSelf) + { + pSelf->headerSize = header_size; + pSelf->dataSize = data_size; + } + + // Data size + if (fileSize != 0 && header_size + data_size != fileSize) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: fileSize isn't equal header_size + data_size" }; + } + + const u32 psmf_marks_offset = pAddr->psmf_marks_offset; + const u32 psmf_marks_size = pAddr->psmf_marks_size; + const u32 unk_offset = pAddr->unk_offset; + const u32 unk_size = pAddr->unk_size; + + // PsmfMarks + if (psmf_marks_offset == 0) + { + if (psmf_marks_size != 0) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: psmf_marks_offset is zero but psmf_marks_size is not zero" }; + } + } + else + { + if (psmf_marks_size == 0) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: psmf_marks_offset is set but psmf_marks_size is zero" }; + } + + if (header_size < static_cast(psmf_marks_offset) + psmf_marks_size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: header_size is less than psmf_marks_offset + psmf_marks_size" }; + } + } + + if (unk_offset == 0) + { + if (unk_size != 0) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: unk_offset is zero but unk_size is not zero" }; + } + } + else + { + if (unk_size == 0) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: unk_offset is set but unk_size is zero" }; + } + + if (header_size < static_cast(unk_offset) + unk_size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: header_size is less than unk_offset + unk_size" }; + } + } + + if (unk_offset < static_cast(psmf_marks_offset) + psmf_marks_size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: unk_offset is less than psmf_marks_offset + psmf_marks_size" }; + } + + + // Sequence Info + + const u32 seq_info_size = pAddr->seq_info.size; + + // Sequence info size + if (offsetof(PamfHeader, seq_info) + sizeof(u32) + seq_info_size > header_size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid seq_info_size" }; + } + + const u64 start_pts = static_cast(pAddr->seq_info.start_pts_high) << 32 | pAddr->seq_info.start_pts_low; + const u64 end_pts = static_cast(pAddr->seq_info.end_pts_high) << 32 | pAddr->seq_info.end_pts_low; + + // Start and end presentation time stamps + if (end_pts > CODEC_TS_INVALID) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid end_pts" }; + } + + if (start_pts >= end_pts) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid start_pts" }; + } + + // Grouping period count + if (pAddr->seq_info.grouping_period_num != 1) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid grouping_period_num" }; + } + + + // Grouping Period + + const u32 grouping_period_size = pAddr->seq_info.grouping_periods.size; + + // Grouping period size + if (offsetof(PamfHeader, seq_info.grouping_periods) + sizeof(u32) + grouping_period_size > header_size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid grouping_period_size" }; + } + + const u64 grp_period_start_pts = static_cast(pAddr->seq_info.grouping_periods.start_pts_high) << 32 | pAddr->seq_info.grouping_periods.start_pts_low; + const u64 grp_period_end_pts = static_cast(pAddr->seq_info.grouping_periods.start_pts_high) << 32 | pAddr->seq_info.grouping_periods.end_pts_low; // LLE uses start_pts_high due to a bug + + // Start and end presentation time stamps + if (grp_period_end_pts > CODEC_TS_INVALID) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid grp_period_end_pts" }; + } + + if (grp_period_start_pts >= grp_period_end_pts) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid grp_period_start_pts" }; + } + + if (grp_period_start_pts != start_pts) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: grp_period_start_pts not equal start_pts" }; + } + + // Group count + if (pAddr->seq_info.grouping_periods.group_num != 1) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid group_num" }; + } + + + // Group + + const u32 group_size = pAddr->seq_info.grouping_periods.groups.size; + + // StreamGroup size + if (offsetof(PamfHeader, seq_info.grouping_periods.groups) + sizeof(u32) + group_size > header_size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid group_size" }; + } + + const u8 stream_num = pAddr->seq_info.grouping_periods.groups.stream_num; + + // Stream count + if (stream_num == 0) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid stream_num" }; + } + + + // Streams + + const auto streams = &pAddr->seq_info.grouping_periods.groups.streams; + + std::bitset<16> channels_used[6]{}; + + u32 end_of_streams_addr = 0; + u32 next_ep_table_addr = 0; + + for (u8 stream_idx = 0; stream_idx < stream_num; stream_idx++) + { + vm::var type; + vm::var ch; + + // Stream coding type and IDs + if (pamfStreamToTypeChannel(streams[stream_idx].stream_coding_type, streams[stream_idx].stream_id, streams[stream_idx].private_stream_id, type, ch) != CELL_OK) + { + return CELL_PAMF_ERROR_UNKNOWN_STREAM; + } + + // Every channel may only be used once per type + if (channels_used[*type].test(*ch)) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid channel" }; + } + + // Mark channel as used + channels_used[*type].set(*ch); + + const u32 ep_offset = streams[stream_idx].ep_offset; + const u32 ep_num = streams[stream_idx].ep_num; + + // Entry point offset and number + if (ep_num == 0) + { + if (ep_offset != 0) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: ep_num is zero but ep_offset is not zero" }; + } + } + else + { + if (ep_offset == 0) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid ep_offset" }; + } + + if (ep_offset + ep_num * sizeof(PamfEpHeader) > header_size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid ep_num" }; + } + } + + + // Entry points + + // Skip if there are no entry points or if the minimum header attribute is set + if (ep_offset == 0 || attribute & CELL_PAMF_ATTRIBUTE_MINIMUM_HEADER) + { + continue; + } + + const auto eps = vm::cptr::make(pAddr.addr() + ep_offset); + + // Entry point tables must be sorted by the stream index to which they belong + // and there mustn't be any gaps between them + if (end_of_streams_addr == 0) + { + end_of_streams_addr = eps.addr(); + next_ep_table_addr = end_of_streams_addr; + } + else if (next_ep_table_addr != eps.addr()) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid ep table address" }; + } + + u64 previous_rpn_offset = 0; + for (u32 ep_idx = 0; ep_idx < ep_num; ep_idx++) + { + const u64 pts = static_cast(eps[ep_idx].pts_high) << 32 | eps[ep_idx].pts_low; + + // Entry point time stamp + if (pts > CODEC_TS_INVALID) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid ep pts" }; + } + + const u64 rpn_offset = eps[ep_idx].rpnOffset * 0x800ull; + + // Entry point rpnOffset + if (rpn_offset > data_size || rpn_offset < previous_rpn_offset) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid rpn_offset" }; + } + + previous_rpn_offset = rpn_offset; + } + + next_ep_table_addr += ep_num * sizeof(PamfEpHeader); + } + + // This can overflow on LLE, the +4 is necessary on both sides and the left operand needs to be u32 + if (group_size + 4 > grouping_period_size - offsetof(PamfGroupingPeriod, groups) + sizeof(u32) + || grouping_period_size + 4 > seq_info_size - offsetof(PamfSequenceInfo, grouping_periods) + sizeof(u32)) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: size mismatch" }; + } + + // Since multiple grouping periods/groups was never implemented, number of streams in SequenceInfo must be equal stream_num in Group + if (pAddr->seq_info.total_stream_num != stream_num) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: number of streams mismatch" }; + } + + + // PsmfMarks + // This is probably useless since the official PAMF tools don't support PsmfMarks + + if (end_of_streams_addr == 0) + { + if (psmf_marks_offset != 0) + { + end_of_streams_addr = pAddr.addr() + psmf_marks_offset; + } + else if (unk_offset != 0) + { + end_of_streams_addr = pAddr.addr() + unk_offset; + } + } + + if (end_of_streams_addr != 0 && pAddr.addr() + offsetof(PamfHeader, seq_info.grouping_periods) + sizeof(u32) + grouping_period_size != end_of_streams_addr) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid offset of ep tables or psmf marks" }; + } + + if (next_ep_table_addr != 0 && psmf_marks_offset == 0) + { + if (unk_offset != 0 && pAddr.addr() + unk_offset != next_ep_table_addr) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid unk_offset" }; + } + } + else if (next_ep_table_addr != 0 && pAddr.addr() + psmf_marks_offset != next_ep_table_addr) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid psmf_marks_offset" }; + } + else if (psmf_marks_offset != 0 && !(attribute & CELL_PAMF_ATTRIBUTE_MINIMUM_HEADER)) + { + const u32 size = vm::read32(pAddr.addr() + psmf_marks_offset); + + if (size + sizeof(u32) != psmf_marks_size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid psmf_marks_size" }; + } + + const u16 marks_num = vm::read16(pAddr.addr() + psmf_marks_offset + 6); // LLE uses the wrong offset (6 instead of 4) + + if (sizeof(u16) + marks_num * 0x28 /*sizeof PsmfMark*/ != size) + { + return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid marks_num" }; + } + + // There are more checks in LLE but due to the bug above these would never be executed + } + + return CELL_OK; +} + +error_code cellPamfGetStreamOffsetAndSize(vm::ptr pAddr, u64 fileSize, vm::ptr pOffset, vm::ptr pSize); + error_code cellPamfGetHeaderSize(vm::ptr pAddr, u64 fileSize, vm::ptr pSize) { - cellPamf.warning("cellPamfGetHeaderSize(pAddr=*0x%x, fileSize=0x%llx, pSize=*0x%x)", pAddr, fileSize, pSize); + cellPamf.notice("cellPamfGetHeaderSize(pAddr=*0x%x, fileSize=0x%llx, pSize=*0x%x)", pAddr, fileSize, pSize); - //if ((u32)pAddr->magic != 0x464d4150) return CELL_PAMF_ERROR_UNKNOWN_TYPE; - - const u64 offset = u64{pAddr->data_offset} << 11; - *pSize = offset; - return CELL_OK; + return cellPamfGetStreamOffsetAndSize(pAddr, fileSize, pSize, vm::null); } error_code cellPamfGetHeaderSize2(vm::ptr pAddr, u64 fileSize, u32 attribute, vm::ptr pSize) { - cellPamf.warning("cellPamfGetHeaderSize2(pAddr=*0x%x, fileSize=0x%llx, attribute=0x%x, pSize=*0x%x)", pAddr, fileSize, attribute, pSize); + cellPamf.notice("cellPamfGetHeaderSize2(pAddr=*0x%x, fileSize=0x%llx, attribute=0x%x, pSize=*0x%x)", pAddr, fileSize, attribute, pSize); - //if ((u32)pAddr->magic != 0x464d4150) return CELL_PAMF_ERROR_UNKNOWN_TYPE; + const vm::var header_size; + + if (error_code ret = cellPamfGetStreamOffsetAndSize(pAddr, fileSize, header_size, vm::null); ret != CELL_OK) + { + return ret; + } + + if (attribute & CELL_PAMF_ATTRIBUTE_MINIMUM_HEADER) + { + if (pAddr->magic != std::bit_cast>("PAMF"_u32)) // Already checked in cellPamfGetStreamOffsetAndSize(), should always evaluate to false at this point + { + return CELL_PAMF_ERROR_UNKNOWN_TYPE; + } + + const u64 min_header_size = offsetof(PamfHeader, seq_info) + sizeof(u32) + pAddr->seq_info.size; // Size without EP tables + + if (min_header_size > *header_size) + { + return CELL_PAMF_ERROR_INVALID_PAMF; + } + + *header_size = min_header_size; + } + + if (pSize) + { + *pSize = *header_size; + } - const u64 offset = u64{pAddr->data_offset} << 11; - *pSize = offset; return CELL_OK; } error_code cellPamfGetStreamOffsetAndSize(vm::ptr pAddr, u64 fileSize, vm::ptr pOffset, vm::ptr pSize) { - cellPamf.warning("cellPamfGetStreamOffsetAndSize(pAddr=*0x%x, fileSize=0x%llx, pOffset=*0x%x, pSize=*0x%x)", pAddr, fileSize, pOffset, pSize); + cellPamf.notice("cellPamfGetStreamOffsetAndSize(pAddr=*0x%x, fileSize=0x%llx, pOffset=*0x%x, pSize=*0x%x)", pAddr, fileSize, pOffset, pSize); - //if ((u32)pAddr->magic != 0x464d4150) return CELL_PAMF_ERROR_UNKNOWN_TYPE; - - const u64 offset = u64{pAddr->data_offset} << 11; - *pOffset = offset; - const u64 size = u64{pAddr->data_size} << 11; - *pSize = size; - return CELL_OK; + return pamfGetHeaderAndDataSize(pAddr, fileSize, pOffset, pSize); } error_code cellPamfVerify(vm::cptr pAddr, u64 fileSize) { - cellPamf.todo("cellPamfVerify(pAddr=*0x%x, fileSize=0x%llx)", pAddr, fileSize); + cellPamf.notice("cellPamfVerify(pAddr=*0x%x, fileSize=0x%llx)", pAddr, fileSize); - // TODO - return CELL_OK; + return pamfVerify(pAddr, fileSize, vm::null, CELL_PAMF_ATTRIBUTE_VERIFY_ON); } +error_code cellPamfReaderSetStreamWithIndex(vm::ptr pSelf, u8 streamIndex); + error_code cellPamfReaderInitialize(vm::ptr pSelf, vm::cptr pAddr, u64 fileSize, u32 attribute) { - cellPamf.warning("cellPamfReaderInitialize(pSelf=*0x%x, pAddr=*0x%x, fileSize=0x%llx, attribute=0x%x)", pSelf, pAddr, fileSize, attribute); + cellPamf.notice("cellPamfReaderInitialize(pSelf=*0x%x, pAddr=*0x%x, fileSize=0x%llx, attribute=0x%x)", pSelf, pAddr, fileSize, attribute); - if (fileSize) - { - pSelf->fileSize = fileSize; - } - else // if fileSize is unknown - { - pSelf->fileSize = (u64{pAddr->data_offset} << 11) + (u64{pAddr->data_size} << 11); - } - pSelf->pAddr = pAddr; + ensure(!!pSelf); // Not checked on LLE + + std::memset(pSelf.get_ptr(), 0, sizeof(CellPamfReader)); + + pSelf->attribute = attribute; if (attribute & CELL_PAMF_ATTRIBUTE_VERIFY_ON) { - // TODO - cellPamf.todo("cellPamfReaderInitialize(): verification"); + if (error_code ret = pamfVerify(pAddr, fileSize, pSelf, attribute); ret != CELL_OK) + { + return ret; + } + } + + pSelf->pamf.header = pAddr; + pSelf->pamf.sequenceInfo = pAddr.ptr(&PamfHeader::seq_info); + + pSelf->currentGroupingPeriodIndex = -1; + pSelf->currentGroupIndex = -1; + pSelf->currentStreamIndex = -1; + + if (pAddr->seq_info.grouping_period_num == 0) + { + return CELL_PAMF_ERROR_INVALID_PAMF; + } + + pSelf->currentGroupingPeriodIndex = 0; + pSelf->pamf.currentGroupingPeriod = pSelf->pamf.sequenceInfo.ptr(&PamfSequenceInfo::grouping_periods); + + if (pAddr->seq_info.grouping_periods.group_num != 0) + { + pSelf->currentGroupIndex = 0; + pSelf->pamf.currentGroup = pSelf->pamf.currentGroupingPeriod.ptr(&PamfGroupingPeriod::groups); + + cellPamfReaderSetStreamWithIndex(pSelf, 0); } - pSelf->stream = 0; // currently set stream return CELL_OK; } error_code cellPamfReaderGetPresentationStartTime(vm::ptr pSelf, vm::ptr pTimeStamp) { - cellPamf.warning("cellPamfReaderGetPresentationStartTime(pSelf=*0x%x, pTimeStamp=*0x%x)", pSelf, pTimeStamp); + cellPamf.notice("cellPamfReaderGetPresentationStartTime(pSelf=*0x%x, pTimeStamp=*0x%x)", pSelf, pTimeStamp); // always returns CELL_OK - pTimeStamp->upper = pSelf->pAddr->start_pts_high; - pTimeStamp->lower = pSelf->pAddr->start_pts_low; + ensure(!!pSelf && !!pTimeStamp); // Not checked on LLE + + if (pSelf->isPsmf) + { + pTimeStamp->upper = pSelf->psmf.sequenceInfo->start_pts_high; + pTimeStamp->lower = pSelf->psmf.sequenceInfo->start_pts_low; + } + else + { + pTimeStamp->upper = pSelf->pamf.sequenceInfo->start_pts_high; + pTimeStamp->lower = pSelf->pamf.sequenceInfo->start_pts_low; + } + return CELL_OK; } error_code cellPamfReaderGetPresentationEndTime(vm::ptr pSelf, vm::ptr pTimeStamp) { - cellPamf.warning("cellPamfReaderGetPresentationEndTime(pSelf=*0x%x, pTimeStamp=*0x%x)", pSelf, pTimeStamp); + cellPamf.notice("cellPamfReaderGetPresentationEndTime(pSelf=*0x%x, pTimeStamp=*0x%x)", pSelf, pTimeStamp); // always returns CELL_OK - pTimeStamp->upper = pSelf->pAddr->end_pts_high; - pTimeStamp->lower = pSelf->pAddr->end_pts_low; + ensure(!!pSelf && !!pTimeStamp); // Not checked on LLE + + if (pSelf->isPsmf) + { + pTimeStamp->upper = pSelf->psmf.sequenceInfo->end_pts_high; + pTimeStamp->lower = pSelf->psmf.sequenceInfo->end_pts_low; + } + else + { + pTimeStamp->upper = pSelf->pamf.sequenceInfo->end_pts_high; + pTimeStamp->lower = pSelf->pamf.sequenceInfo->end_pts_low; + } + return CELL_OK; } u32 cellPamfReaderGetMuxRateBound(vm::ptr pSelf) { - cellPamf.warning("cellPamfReaderGetMuxRateBound(pSelf=*0x%x)", pSelf); + cellPamf.notice("cellPamfReaderGetMuxRateBound(pSelf=*0x%x)", pSelf); - // cannot return error code - return pSelf->pAddr->mux_rate_max; + ensure(!!pSelf); // Not checked on LLE + + if (pSelf->isPsmf) + { + return pSelf->psmf.sequenceInfo->mux_rate_bound & 0x003fffff; + } + + return pSelf->pamf.sequenceInfo->mux_rate_bound & 0x003fffff; } u8 cellPamfReaderGetNumberOfStreams(vm::ptr pSelf) { - cellPamf.warning("cellPamfReaderGetNumberOfStreams(pSelf=*0x%x)", pSelf); + cellPamf.notice("cellPamfReaderGetNumberOfStreams(pSelf=*0x%x)", pSelf); - // cannot return error code - return pSelf->pAddr->stream_count; + ensure(!!pSelf); // Not checked on LLE + + return pSelf->pamf.currentGroup->stream_num; } u8 cellPamfReaderGetNumberOfSpecificStreams(vm::ptr pSelf, u8 streamType) { - cellPamf.warning("cellPamfReaderGetNumberOfSpecificStreams(pSelf=*0x%x, streamType=%d)", pSelf, streamType); + cellPamf.notice("cellPamfReaderGetNumberOfSpecificStreams(pSelf=*0x%x, streamType=%d)", pSelf, streamType); - // cannot return error code + ensure(!!pSelf); // Not checked on LLE - u8 counts[256] = {}; + const vm::var type; + u8 found = 0; - for (u8 i = 0; i < pSelf->pAddr->stream_count; i++) + if (pSelf->isPsmf) { - counts[pamfGetStreamType(pSelf, i)]++; + const auto streams = pSelf->psmf.currentGroup.ptr(&PsmfGroup::streams); + + for (u8 i = 0; i < pSelf->psmf.currentGroup->stream_num; i++) + { + if (pamfStreamToTypeChannel(PAMF_STREAM_CODING_TYPE_PSMF, streams[i].stream_id, streams[i].private_stream_id, type, vm::null) == CELL_OK) + { + found += pamfIsSameStreamType(*type, streamType); + } + } + } + else + { + const auto streams = pSelf->pamf.currentGroup.ptr(&PamfGroup::streams); + + for (u8 i = 0; i < pSelf->pamf.currentGroup->stream_num; i++) + { + if (pamfStreamToTypeChannel(streams[i].stream_coding_type, streams[i].stream_id, streams[i].private_stream_id, type, vm::null) == CELL_OK) + { + found += pamfIsSameStreamType(*type, streamType); + } + } } - switch (streamType) - { - case CELL_PAMF_STREAM_TYPE_AVC: - case CELL_PAMF_STREAM_TYPE_M2V: - case CELL_PAMF_STREAM_TYPE_ATRAC3PLUS: - case CELL_PAMF_STREAM_TYPE_PAMF_LPCM: - case CELL_PAMF_STREAM_TYPE_AC3: - case CELL_PAMF_STREAM_TYPE_USER_DATA: - { - return counts[streamType]; - } - - case CELL_PAMF_STREAM_TYPE_VIDEO: - { - return counts[CELL_PAMF_STREAM_TYPE_AVC] + counts[CELL_PAMF_STREAM_TYPE_M2V]; - } - - case CELL_PAMF_STREAM_TYPE_AUDIO: - { - return counts[CELL_PAMF_STREAM_TYPE_ATRAC3PLUS] + counts[CELL_PAMF_STREAM_TYPE_PAMF_LPCM] + counts[CELL_PAMF_STREAM_TYPE_AC3]; - } - } - - cellPamf.fatal("cellPamfReaderGetNumberOfSpecificStreams(): unsupported stream type (0x%x)", streamType); - return 0; + return found; } error_code cellPamfReaderSetStreamWithIndex(vm::ptr pSelf, u8 streamIndex) { - cellPamf.warning("cellPamfReaderSetStreamWithIndex(pSelf=*0x%x, streamIndex=%d)", pSelf, streamIndex); + cellPamf.notice("cellPamfReaderSetStreamWithIndex(pSelf=*0x%x, streamIndex=%d)", pSelf, streamIndex); - if (streamIndex >= pSelf->pAddr->stream_count) + ensure(!!pSelf); // Not checked on LLE + + if (streamIndex >= pSelf->pamf.currentGroup->stream_num) { return CELL_PAMF_ERROR_INVALID_ARG; } - pSelf->stream = streamIndex; + pSelf->currentStreamIndex = streamIndex; + + if (pSelf->isPsmf) + { + pSelf->psmf.currentStream = pSelf->psmf.currentGroup.ptr(&PsmfGroup::streams) + streamIndex; + } + else + { + pSelf->pamf.currentStream = pSelf->pamf.currentGroup.ptr(&PamfGroup::streams) + streamIndex; + } + return CELL_OK; } error_code cellPamfReaderSetStreamWithTypeAndChannel(vm::ptr pSelf, u8 streamType, u8 ch) { - cellPamf.warning("cellPamfReaderSetStreamWithTypeAndChannel(pSelf=*0x%x, streamType=%d, ch=%d)", pSelf, streamType, ch); + cellPamf.notice("cellPamfReaderSetStreamWithTypeAndChannel(pSelf=*0x%x, streamType=%d, ch=%d)", pSelf, streamType, ch); - // TODO: it probably doesn't support "any audio" or "any video" argument - if (streamType > 5 || ch >= 16) + // This function is broken on LLE + + ensure(!!pSelf); // Not checked on LLE + + u8 stream_coding_type; + u8 stream_id; + u8 private_stream_id; + + if (pamfTypeChannelToStream(streamType, ch, &stream_coding_type, &stream_id, &private_stream_id) != CELL_OK) { return CELL_PAMF_ERROR_INVALID_ARG; } - for (u8 i = 0; i < pSelf->pAddr->stream_count; i++) + const u8 stream_num = pSelf->pamf.currentGroup->stream_num; + u32 i = 0; + + if (pSelf->isPsmf) { - if (pamfGetStreamType(pSelf, i) == streamType) + const auto streams = pSelf->psmf.currentGroup.ptr(&PsmfGroup::streams); + + for (; i < stream_num; i++) { - if (pamfGetStreamChannel(pSelf, i) == ch) + // LLE increments the index by 12 instead of 1 + if (stream_coding_type == PAMF_STREAM_CODING_TYPE_PSMF && streams[i * 12].stream_id == stream_id && streams[i * 12].private_stream_id == private_stream_id) { - pSelf->stream = i; - return i; + break; + } + } + } + else + { + const auto streams = pSelf->pamf.currentGroup.ptr(&PamfGroup::streams); + + for (; i < stream_num; i++) + { + // LLE increments the index by 0x10 instead of 1 + if (streams[i * 0x10].stream_coding_type == stream_coding_type && streams[i * 0x10].stream_id == stream_id && streams[i * 0x10].private_stream_id == private_stream_id) + { + break; } } } - return CELL_PAMF_ERROR_STREAM_NOT_FOUND; + if (i == stream_num) + { + i = CELL_PAMF_ERROR_STREAM_NOT_FOUND; // LLE writes the error code to the index + } + + if (pSelf->currentStreamIndex != i) + { + pSelf->pamf.currentStream = pSelf->pamf.currentGroup.ptr(&PamfGroup::streams); // LLE always sets this to the first stream + pSelf->currentStreamIndex = i; + } + + if (i == CELL_PAMF_ERROR_STREAM_NOT_FOUND) + { + return CELL_PAMF_ERROR_STREAM_NOT_FOUND; + } + + return not_an_error(i); } error_code cellPamfReaderSetStreamWithTypeAndIndex(vm::ptr pSelf, u8 streamType, u8 streamIndex) { - cellPamf.warning("cellPamfReaderSetStreamWithTypeAndIndex(pSelf=*0x%x, streamType=%d, streamIndex=%d)", pSelf, streamType, streamIndex); + cellPamf.notice("cellPamfReaderSetStreamWithTypeAndIndex(pSelf=*0x%x, streamType=%d, streamIndex=%d)", pSelf, streamType, streamIndex); + ensure(!!pSelf); // Not checked on LLE + + const u8 stream_num = pSelf->pamf.currentGroup->stream_num; + + if (streamIndex >= stream_num) + { + return CELL_PAMF_ERROR_INVALID_ARG; + } + + const vm::var type; u32 found = 0; - for (u8 i = 0; i < pSelf->pAddr->stream_count; i++) + if (pSelf->isPsmf) { - const u8 type = pamfGetStreamType(pSelf, i); + const auto streams = pSelf->psmf.currentGroup.ptr(&PsmfGroup::streams); - if (type == streamType) + for (u8 i = 0; i < stream_num; i++) { - found++; - } - else switch(streamType) - { - case CELL_PAMF_STREAM_TYPE_VIDEO: - { - if (type == CELL_PAMF_STREAM_TYPE_AVC || type == CELL_PAMF_STREAM_TYPE_M2V) + if (pamfStreamToTypeChannel(PAMF_STREAM_CODING_TYPE_PSMF, streams[i].stream_id, streams[i].private_stream_id, type, vm::null) != CELL_OK) { - found++; + continue; } - break; - } - case CELL_PAMF_STREAM_TYPE_AUDIO: - { - if (type == CELL_PAMF_STREAM_TYPE_ATRAC3PLUS || type == CELL_PAMF_STREAM_TYPE_AC3 || type == CELL_PAMF_STREAM_TYPE_PAMF_LPCM) - { - found++; - } - break; - } + found += *type == streamType; - default: - { - if (streamType > 5) + if (found > streamIndex) { - return CELL_PAMF_ERROR_INVALID_ARG; + pSelf->currentStreamIndex = streamIndex; // LLE sets this to the index counting only streams of the requested type instead of the overall index + pSelf->psmf.currentStream = streams; // LLE always sets this to the first stream + return not_an_error(i); } } - } + } + else + { + const auto streams = pSelf->pamf.currentGroup.ptr(&PamfGroup::streams); - if (found > streamIndex) + for (u8 i = 0; i < stream_num; i++) { - pSelf->stream = i; - return i; + if (pamfStreamToTypeChannel(streams[i].stream_coding_type, streams[i].stream_id, streams[i].private_stream_id, type, vm::null) != CELL_OK) + { + continue; + } + + found += pamfIsSameStreamType(*type, streamType); + + if (found > streamIndex) + { + pSelf->currentStreamIndex = i; + pSelf->pamf.currentStream = streams + i; + return not_an_error(i); + } } } @@ -441,60 +1036,107 @@ error_code cellPamfReaderSetStreamWithTypeAndIndex(vm::ptr pSelf error_code cellPamfStreamTypeToEsFilterId(u8 type, u8 ch, vm::ptr pEsFilterId) { - cellPamf.warning("cellPamfStreamTypeToEsFilterId(type=%d, ch=%d, pEsFilterId=*0x%x)", type, ch, pEsFilterId); + cellPamf.notice("cellPamfStreamTypeToEsFilterId(type=%d, ch=%d, pEsFilterId=*0x%x)", type, ch, pEsFilterId); if (!pEsFilterId) { return CELL_PAMF_ERROR_INVALID_ARG; } - return pamfStreamTypeToEsFilterId(type, ch, *pEsFilterId); + u8 stream_id = 0; + u8 private_stream_id = 0; + + if (pamfTypeChannelToStream(type, ch, nullptr, &stream_id, &private_stream_id) != CELL_OK) + { + return CELL_PAMF_ERROR_INVALID_ARG; + } + + pEsFilterId->filterIdMajor = stream_id; + pEsFilterId->filterIdMinor = private_stream_id; + pEsFilterId->supplementalInfo1 = type == CELL_PAMF_STREAM_TYPE_AVC; + pEsFilterId->supplementalInfo2 = 0; + + return CELL_OK; } s32 cellPamfReaderGetStreamIndex(vm::ptr pSelf) { - cellPamf.trace("cellPamfReaderGetStreamIndex(pSelf=*0x%x)", pSelf); + cellPamf.notice("cellPamfReaderGetStreamIndex(pSelf=*0x%x)", pSelf); - // seems that CELL_PAMF_ERROR_INVALID_PAMF must be already written in pSelf->stream if it's the case - return pSelf->stream; + ensure(!!pSelf); // Not checked on LLE + + return pSelf->currentStreamIndex; } error_code cellPamfReaderGetStreamTypeAndChannel(vm::ptr pSelf, vm::ptr pType, vm::ptr pCh) { - cellPamf.warning("cellPamfReaderGetStreamTypeAndChannel(pSelf=*0x%x, pType=*0x%x, pCh=*0x%x", pSelf, pType, pCh); + cellPamf.notice("cellPamfReaderGetStreamTypeAndChannel(pSelf=*0x%x, pType=*0x%x, pCh=*0x%x", pSelf, pType, pCh); - // unclear + ensure(!!pSelf); // Not checked on LLE - *pType = pamfGetStreamType(pSelf, pSelf->stream); - *pCh = pamfGetStreamChannel(pSelf, pSelf->stream); - return CELL_OK; + if (pSelf->isPsmf) + { + const auto stream = pSelf->psmf.currentStream; + return pamfStreamToTypeChannel(PAMF_STREAM_CODING_TYPE_PSMF, stream->stream_id, stream->private_stream_id, pType, pCh); + } + else + { + const auto stream = pSelf->pamf.currentStream; + return pamfStreamToTypeChannel(stream->stream_coding_type, stream->stream_id, stream->private_stream_id, pType, pCh); + } } error_code cellPamfReaderGetEsFilterId(vm::ptr pSelf, vm::ptr pEsFilterId) { - cellPamf.warning("cellPamfReaderGetEsFilterId(pSelf=*0x%x, pEsFilterId=*0x%x)", pSelf, pEsFilterId); + cellPamf.notice("cellPamfReaderGetEsFilterId(pSelf=*0x%x, pEsFilterId=*0x%x)", pSelf, pEsFilterId); // always returns CELL_OK - ensure(static_cast(pSelf->stream) < pSelf->pAddr->stream_count); - auto& header = pSelf->pAddr->stream_headers[pSelf->stream]; - pEsFilterId->filterIdMajor = header.fid_major; - pEsFilterId->filterIdMinor = header.fid_minor; - pEsFilterId->supplementalInfo1 = header.type == 0x1b ? 1 : 0; + ensure(!!pSelf && !!pEsFilterId); // Not checked on LLE + + if (pSelf->isPsmf) + { + pEsFilterId->filterIdMajor = pSelf->psmf.currentStream->stream_id; + pEsFilterId->filterIdMinor = pSelf->psmf.currentStream->private_stream_id; + pEsFilterId->supplementalInfo1 = 0; + } + else + { + pEsFilterId->filterIdMajor = pSelf->pamf.currentStream->stream_id; + pEsFilterId->filterIdMinor = pSelf->pamf.currentStream->private_stream_id; + pEsFilterId->supplementalInfo1 = pSelf->pamf.currentStream->stream_coding_type == PAMF_STREAM_CODING_TYPE_AVC; + } + pEsFilterId->supplementalInfo2 = 0; return CELL_OK; } error_code cellPamfReaderGetStreamInfo(vm::ptr pSelf, vm::ptr pInfo, u32 size) { - cellPamf.warning("cellPamfReaderGetStreamInfo(pSelf=*0x%x, pInfo=*0x%x, size=%d)", pSelf, pInfo, size); + cellPamf.notice("cellPamfReaderGetStreamInfo(pSelf=*0x%x, pInfo=*0x%x, size=%d)", pSelf, pInfo, size); - ensure(static_cast(pSelf->stream) < pSelf->pAddr->stream_count); - auto& header = pSelf->pAddr->stream_headers[pSelf->stream]; - const u8 type = pamfGetStreamType(pSelf, pSelf->stream); - const u8 ch = pamfGetStreamChannel(pSelf, pSelf->stream); + ensure(!!pSelf); // Not checked on LLE - switch (type) + const auto& header = *pSelf->pamf.currentStream; + const auto& psmf_header = *pSelf->psmf.currentStream; + const vm::var type; + error_code ret; + + if (pSelf->isPsmf) + { + ret = pamfStreamToTypeChannel(PAMF_STREAM_CODING_TYPE_PSMF, psmf_header.stream_id, psmf_header.private_stream_id, type, vm::null); + } + else + { + ret = pamfStreamToTypeChannel(header.stream_coding_type, header.stream_id, header.private_stream_id, type, vm::null); + } + + if (ret != CELL_OK) + { + return CELL_PAMF_ERROR_INVALID_PAMF; + } + + switch (*type) { case CELL_PAMF_STREAM_TYPE_AVC: { @@ -514,8 +1156,8 @@ error_code cellPamfReaderGetStreamInfo(vm::ptr pSelf, vm::ptrsarWidth = header.AVC.sarInfo.width; - info->sarHeight = header.AVC.sarInfo.height; + info->sarWidth = header.AVC.sarWidth; + info->sarHeight = header.AVC.sarHeight; } else { @@ -665,54 +1307,45 @@ error_code cellPamfReaderGetStreamInfo(vm::ptr pSelf, vm::ptr(pInfo)[0] = psmf_header.video.horizontalSize * 0x10; + vm::static_ptr_cast(pInfo)[1] = psmf_header.video.verticalSize * 0x10; + + cellPamf.notice("cellPamfReaderGetStreamInfo(): CELL_PAMF_STREAM_TYPE_PSMF_AVC"); break; } - case 7: + case CELL_PAMF_STREAM_TYPE_PSMF_ATRAC3PLUS: + case CELL_PAMF_STREAM_TYPE_PSMF_LPCM: { if (size < 2) { return CELL_PAMF_ERROR_INVALID_ARG; } - cellPamf.todo("cellPamfReaderGetStreamInfo(): type 7"); + vm::static_ptr_cast(pInfo)[0] = psmf_header.audio.channelConfiguration; + vm::static_ptr_cast(pInfo)[1] = psmf_header.audio.samplingFrequency & 0x0f; + + cellPamf.notice("cellPamfReaderGetStreamInfo(): PSMF audio"); break; } - case 8: - { - if (size < 2) - { - return CELL_PAMF_ERROR_INVALID_ARG; - } - - cellPamf.todo("cellPamfReaderGetStreamInfo(): type 8"); - break; - } - - case 9: - { - cellPamf.error("cellPamfReaderGetStreamInfo(): invalid type 9"); - return CELL_PAMF_ERROR_INVALID_ARG; - } - default: { // invalid type or getting type/ch failed - cellPamf.error("cellPamfReaderGetStreamInfo(): invalid type %d (ch=%d)", type, ch); + cellPamf.error("cellPamfReaderGetStreamInfo(): invalid type %d", *type); return CELL_PAMF_ERROR_INVALID_PAMF; } } @@ -722,44 +1355,288 @@ error_code cellPamfReaderGetStreamInfo(vm::ptr pSelf, vm::ptr pSelf) { - cellPamf.todo("cellPamfReaderGetNumberOfEp(pSelf=*0x%x)", pSelf); + cellPamf.notice("cellPamfReaderGetNumberOfEp(pSelf=*0x%x)", pSelf); - // cannot return error code - return 0; //pSelf->pAddr->stream_headers[pSelf->stream].ep_num; + ensure(!!pSelf); // Not checked on LLE + + return pSelf->isPsmf ? pSelf->psmf.currentStream->ep_num : pSelf->pamf.currentStream->ep_num; } error_code cellPamfReaderGetEpIteratorWithIndex(vm::ptr pSelf, u32 epIndex, vm::ptr pIt) { - cellPamf.todo("cellPamfReaderGetEpIteratorWithIndex(pSelf=*0x%x, epIndex=%d, pIt=*0x%x)", pSelf, epIndex, pIt); + cellPamf.notice("cellPamfReaderGetEpIteratorWithIndex(pSelf=*0x%x, epIndex=%d, pIt=*0x%x)", pSelf, epIndex, pIt); + + ensure(!!pSelf && !!pIt); // Not checked on LLE + + const u32 ep_num = cellPamfReaderGetNumberOfEp(pSelf); + + if (epIndex >= ep_num) + { + return CELL_PAMF_ERROR_INVALID_ARG; + } + + if (pSelf->attribute & CELL_PAMF_ATTRIBUTE_MINIMUM_HEADER) + { + return CELL_PAMF_ERROR_NOT_AVAILABLE; + } + + pIt->isPamf = !pSelf->isPsmf; + pIt->index = epIndex; + pIt->num = ep_num; + pIt->pCur.set(pSelf->isPsmf ? pSelf->psmf.header.addr() + pSelf->psmf.currentStream->ep_offset + epIndex * sizeof(PsmfEpHeader) + : pSelf->pamf.header.addr() + pSelf->pamf.currentStream->ep_offset + epIndex * sizeof(PamfEpHeader)); - // TODO return CELL_OK; } error_code cellPamfReaderGetEpIteratorWithTimeStamp(vm::ptr pSelf, vm::ptr pTimeStamp, vm::ptr pIt) { - cellPamf.todo("cellPamfReaderGetEpIteratorWithTimeStamp(pSelf=*0x%x, pTimeStamp=*0x%x, pIt=*0x%x)", pSelf, pTimeStamp, pIt); + cellPamf.notice("cellPamfReaderGetEpIteratorWithTimeStamp(pSelf=*0x%x, pTimeStamp=*0x%x, pIt=*0x%x)", pSelf, pTimeStamp, pIt); + + ensure(!!pSelf && !!pTimeStamp && !!pIt); // Not checked on LLE + + if (pSelf->attribute & CELL_PAMF_ATTRIBUTE_MINIMUM_HEADER) + { + return CELL_PAMF_ERROR_NOT_AVAILABLE; + } + + const u32 ep_num = cellPamfReaderGetNumberOfEp(pSelf); + + pIt->num = ep_num; + pIt->isPamf = !pSelf->isPsmf; + + if (ep_num == 0) + { + return CELL_PAMF_ERROR_EP_NOT_FOUND; + } + + u32 i = ep_num - 1; + const u64 requested_time_stamp = std::bit_cast>(*pTimeStamp); + + if (pSelf->isPsmf) + { + const auto eps = vm::cptr::make(pSelf->psmf.header.addr() + pSelf->psmf.currentStream->ep_offset); + + for (; i >= 1; i--) // always output eps[0] if no other suitable ep is found + { + const u64 time_stamp = (static_cast(eps[i].value0 & 1) << 32) | eps[i].pts_low; + + if (time_stamp <= requested_time_stamp) + { + break; + } + } + + pIt->pCur = eps + i; + } + else + { + const auto eps = vm::cptr::make(pSelf->pamf.header.addr() + pSelf->pamf.currentStream->ep_offset); + + for (; i >= 1; i--) // always output eps[0] if no other suitable ep is found + { + const u64 time_stamp = (static_cast(eps[i].pts_high) << 32) | eps[i].pts_low; + + if (time_stamp <= requested_time_stamp) + { + break; + } + } + + pIt->pCur = eps + i; + } + + pIt->index = i; - // TODO return CELL_OK; } error_code cellPamfEpIteratorGetEp(vm::ptr pIt, vm::ptr pEp) { - cellPamf.todo("cellPamfEpIteratorGetEp(pIt=*0x%x, pEp=*0x%x)", pIt, pEp); + cellPamf.notice("cellPamfEpIteratorGetEp(pIt=*0x%x, pEp=*0x%x)", pIt, pEp); // always returns CELL_OK - // TODO + + ensure(!!pIt); // Not checked on LLE + + if (pIt->isPamf) + { + pamfEpUnpack(vm::static_ptr_cast(pIt->pCur), pEp); + } + else + { + psmfEpUnpack(vm::static_ptr_cast(pIt->pCur), pEp); + } + return CELL_OK; } s32 cellPamfEpIteratorMove(vm::ptr pIt, s32 steps, vm::ptr pEp) { - cellPamf.todo("cellPamfEpIteratorMove(pIt=*0x%x, steps=%d, pEp=*0x%x)", pIt, steps, pEp); + cellPamf.notice("cellPamfEpIteratorMove(pIt=*0x%x, steps=%d, pEp=*0x%x)", pIt, steps, pEp); - // cannot return error code - // TODO - return 0; + ensure(!!pIt); // Not checked on LLE + + u32 new_index = pIt->index + steps; + + if (static_cast(new_index) < 0) + { + steps = -static_cast(pIt->index); + new_index = 0; + } + else if (new_index >= pIt->num) + { + steps = (pIt->num - pIt->index) - 1; + new_index = pIt->index + steps; + } + + pIt->index = new_index; + + if (pIt->isPamf) + { + pIt->pCur = vm::static_ptr_cast(pIt->pCur) + steps; + + if (pEp) + { + pamfEpUnpack(vm::static_ptr_cast(pIt->pCur), pEp); + } + } + else + { + pIt->pCur = vm::static_ptr_cast(pIt->pCur) + steps; + + if (pEp) + { + psmfEpUnpack(vm::static_ptr_cast(pIt->pCur), pEp); + } + } + + return steps; +} + +error_code cellPamfReaderGetEpWithTimeStamp(vm::ptr pSelf, vm::ptr pTimeStamp, vm::ptr pEp, u32 unk) +{ + cellPamf.notice("cellPamfReaderGetEpWithTimeStamp(pSelf=*0x%x, pTimeStamp=*0x%x, pEp=*0x%x, unk=0x%x)", pSelf, pTimeStamp, pEp, unk); + + // This function is broken on LLE + + ensure(!!pSelf && !!pTimeStamp && !!pEp); // Not checked on LLE + + if (pSelf->attribute & CELL_PAMF_ATTRIBUTE_MINIMUM_HEADER) + { + return CELL_PAMF_ERROR_NOT_AVAILABLE; + } + + const u32 ep_num = cellPamfReaderGetNumberOfEp(pSelf); + u64 next_rpn_offset = pSelf->dataSize; + + if (ep_num == 0) + { + return CELL_PAMF_ERROR_EP_NOT_FOUND; + } + + u32 i = ep_num - 1; + const u64 requested_time_stamp = std::bit_cast>(*pTimeStamp); + + if (pSelf->isPsmf) + { + const auto eps = vm::cptr::make(pSelf->psmf.header.addr() + pSelf->psmf.currentStream->ep_offset); + + for (; i >= 1; i--) // always output eps[0] if no other suitable ep is found + { + const u64 time_stamp = (static_cast(eps[i].value0 & 1) << 32) | eps[i].pts_low; + + if (time_stamp <= requested_time_stamp) + { + break; + } + } + + // LLE doesn't write the result to pEp + + if (i < ep_num - 1) + { + next_rpn_offset = eps[i + 1].rpnOffset; + } + } + else + { + const auto eps = vm::cptr::make(pSelf->pamf.header.addr() + pSelf->pamf.currentStream->ep_offset); + + for (; i >= 1; i--) // always output eps[0] if no other suitable ep is found + { + const u64 time_stamp = (static_cast(eps[i].pts_high) << 32) | eps[i].pts_low; + + if (time_stamp <= requested_time_stamp) + { + break; + } + } + + // LLE doesn't write the result to pEp + + if (i < ep_num - 1) + { + next_rpn_offset = eps[i + 1].rpnOffset; + } + } + + if (unk == sizeof(CellPamfEpUnk)) + { + pEp->nextRpnOffset = next_rpn_offset; + } + + return CELL_OK; +} + +error_code cellPamfReaderGetEpWithIndex(vm::ptr pSelf, u32 epIndex, vm::ptr pEp, u32 unk) +{ + cellPamf.notice("cellPamfReaderGetEpWithIndex(pSelf=*0x%x, epIndex=%d, pEp=*0x%x, unk=0x%x)", pSelf, epIndex, pEp, unk); + + ensure(!!pSelf && !!pEp); // Not checked on LLE + + const u32 ep_num = cellPamfReaderGetNumberOfEp(pSelf); + u64 next_rpn_offset = pSelf->dataSize; + + if (epIndex >= ep_num) + { + return CELL_PAMF_ERROR_INVALID_ARG; + } + + if (pSelf->attribute & CELL_PAMF_ATTRIBUTE_MINIMUM_HEADER) + { + return CELL_PAMF_ERROR_NOT_AVAILABLE; + } + + if (pSelf->isPsmf) + { + const auto ep = vm::cptr::make(pSelf->psmf.header.addr() + pSelf->psmf.currentStream->ep_offset + epIndex * sizeof(PsmfEpHeader)); + + psmfEpUnpack(ep, pEp.ptr(&CellPamfEpUnk::ep)); + + if (epIndex < ep_num - 1) + { + next_rpn_offset = ep[1].rpnOffset * 0x800ull; + } + } + else + { + const auto ep = vm::cptr::make(pSelf->pamf.header.addr() + pSelf->pamf.currentStream->ep_offset + epIndex * sizeof(PamfEpHeader)); + + pamfEpUnpack(ep, pEp.ptr(&CellPamfEpUnk::ep)); + + if (epIndex < ep_num - 1) + { + next_rpn_offset = ep[1].rpnOffset * 0x800ull; + } + } + + if (unk == sizeof(CellPamfEpUnk)) + { + pEp->nextRpnOffset = next_rpn_offset; + } + + return CELL_OK; } DECLARE(ppu_module_manager::cellPamf)("cellPamf", []() @@ -787,4 +1664,6 @@ DECLARE(ppu_module_manager::cellPamf)("cellPamf", []() REG_FUNC(cellPamf, cellPamfReaderGetEpIteratorWithTimeStamp); REG_FUNC(cellPamf, cellPamfEpIteratorGetEp); REG_FUNC(cellPamf, cellPamfEpIteratorMove); + REG_FUNC(cellPamf, cellPamfReaderGetEpWithTimeStamp); + REG_FUNC(cellPamf, cellPamfReaderGetEpWithIndex); }); diff --git a/rpcs3/Emu/Cell/Modules/cellPamf.h b/rpcs3/Emu/Cell/Modules/cellPamf.h index d1bdcaf0f7..e42acf60f4 100644 --- a/rpcs3/Emu/Cell/Modules/cellPamf.h +++ b/rpcs3/Emu/Cell/Modules/cellPamf.h @@ -24,14 +24,30 @@ enum enum CellPamfStreamType { - CELL_PAMF_STREAM_TYPE_AVC = 0, - CELL_PAMF_STREAM_TYPE_M2V = 1, - CELL_PAMF_STREAM_TYPE_ATRAC3PLUS = 2, - CELL_PAMF_STREAM_TYPE_PAMF_LPCM = 3, - CELL_PAMF_STREAM_TYPE_AC3 = 4, - CELL_PAMF_STREAM_TYPE_USER_DATA = 5, - CELL_PAMF_STREAM_TYPE_VIDEO = 20, - CELL_PAMF_STREAM_TYPE_AUDIO = 21, + CELL_PAMF_STREAM_TYPE_AVC = 0, + CELL_PAMF_STREAM_TYPE_M2V = 1, + CELL_PAMF_STREAM_TYPE_ATRAC3PLUS = 2, + CELL_PAMF_STREAM_TYPE_PAMF_LPCM = 3, + CELL_PAMF_STREAM_TYPE_AC3 = 4, + CELL_PAMF_STREAM_TYPE_USER_DATA = 5, + CELL_PAMF_STREAM_TYPE_PSMF_AVC = 6, + CELL_PAMF_STREAM_TYPE_PSMF_ATRAC3PLUS = 7, + CELL_PAMF_STREAM_TYPE_PSMF_LPCM = 8, + CELL_PAMF_STREAM_TYPE_PSMF_USER_DATA = 9, + CELL_PAMF_STREAM_TYPE_VIDEO = 20, + CELL_PAMF_STREAM_TYPE_AUDIO = 21, + CELL_PAMF_STREAM_TYPE_UNK = 22, +}; + +enum PamfStreamCodingType : u8 +{ + PAMF_STREAM_CODING_TYPE_M2V = 0x02, + PAMF_STREAM_CODING_TYPE_AVC = 0x1b, + PAMF_STREAM_CODING_TYPE_PAMF_LPCM = 0x80, + PAMF_STREAM_CODING_TYPE_AC3 = 0x81, + PAMF_STREAM_CODING_TYPE_ATRAC3PLUS = 0xdc, + PAMF_STREAM_CODING_TYPE_USER_DATA = 0xdd, + PAMF_STREAM_CODING_TYPE_PSMF = 0xff, }; enum @@ -144,7 +160,7 @@ struct CellCodecTimeStamp be_t lower; }; -static const u64 CODEC_TS_INVALID = 0xffffffffffffffffull; +constexpr u32 CODEC_TS_INVALID = umax; // Entry point information struct CellPamfEp @@ -155,13 +171,21 @@ struct CellPamfEp be_t rpnOffset; }; +struct CellPamfEpUnk // Speculative name, only used in two undocumented functions +{ + CellPamfEp ep; + be_t nextRpnOffset; +}; + +CHECK_SIZE(CellPamfEpUnk, 0x20); + // Entry point iterator struct CellPamfEpIterator { b8 isPamf; be_t index; be_t num; - be_t pCur_addr; + vm::bcptr pCur; }; struct CellCodecEsFilterId @@ -252,18 +276,23 @@ struct CellPamfLpcmInfo be_t bitsPerSample; }; +CHECK_SIZE(CellPamfLpcmInfo, 8); +// PAMF file structs, everything here is not aligned (LLE uses exclusively u8 pointers) struct PamfStreamHeader { - u8 type; - u8 unknown[3]; - u8 fid_major; - u8 fid_minor; - u8 unknown1; - u8 unknown2; - be_t ep_offset; // offset of EP section in header - be_t ep_num; // count of EPs + u8 stream_coding_type; + + u8 reserved[3]; + + u8 stream_id; + u8 private_stream_id; // for streams multiplexed as private data streams (stream_id == 0xbd) + + be_t p_std_buffer; // 2 bits: unused ??? "00", 1 bit: P_STD_buffer_scale, 13 bits: P_STD_buffer_size + + be_t ep_offset; // offset of EP section in header + be_t ep_num; // count of EPs union { @@ -276,32 +305,20 @@ struct PamfStreamHeader u8 levelIdc; u8 x2; // contains frameMbsOnlyFlag, videoSignalInfoFlag, frameRateInfo u8 aspectRatioIdc; - u32 x4; // 0 (not used) - be_t horizontalSize; // divided by 16 - be_t verticalSize; // divided by 16 - be_t frameCropLeftOffset; - be_t frameCropRightOffset; - be_t frameCropTopOffset; - be_t frameCropBottomOffset; - - union - { - struct - { - be_t width; - be_t height; - } - sarInfo; - - struct - { - u8 x14; // contains videoFormat and videoFullRangeFlag - u8 colourPrimaries; - u8 transferCharacteristics; - u8 matrixCoefficients; - }; - }; - + be_t sarWidth; + be_t sarHeight; + u8 reserved1; + u8 horizontalSize; // divided by 16 + u8 reserved2; + u8 verticalSize; // divided by 16 + be_t frameCropLeftOffset; + be_t frameCropRightOffset; + be_t frameCropTopOffset; + be_t frameCropBottomOffset; + u8 x14; // contains videoFormat and videoFullRangeFlag + u8 colourPrimaries; + u8 transferCharacteristics; + u8 matrixCoefficients; u8 x18; // contains entropyCodingModeFlag, deblockingFilterFlag, minNumSlicePerPictureIdc, nfwIdc u8 maxMeanBitrate; } @@ -314,13 +331,15 @@ struct PamfStreamHeader u8 x1; // not used u8 x2; // contains progressiveSequence, videoSignalInfoFlag, frameRateInfo u8 aspectRatioIdc; - be_t sarWidth; - be_t sarHeight; - be_t horizontalSize; - be_t verticalSize; - be_t horizontalSizeValue; - be_t verticalSizeValue; - u32 x10; // not used + be_t sarWidth; + be_t sarHeight; + u8 reserved1; + u8 horizontalSize; // in units of 16 pixels + u8 reserved2; + u8 verticalSize; // in units of 16 pixels + be_t horizontalSizeValue; + be_t verticalSizeValue; + be_t x10; // not used u8 x14; // contains videoFormat and videoFullRangeFlag u8 colourPrimaries; u8 transferCharacteristics; @@ -331,7 +350,7 @@ struct PamfStreamHeader // Audio specific information struct { - u16 unknown; // 0 + be_t unknown; // 0 u8 channels; // number of channels (1, 2, 6, 8) u8 freq; // 1 (always 48000) u8 bps; // LPCM only @@ -340,59 +359,236 @@ struct PamfStreamHeader }; }; -CHECK_SIZE_ALIGN(PamfStreamHeader, 48, 4); +CHECK_SIZE_ALIGN(PamfStreamHeader, 48, 1); + +struct PamfGroup +{ + be_t size; // doesn't include this field + + u8 reserved; + + u8 stream_num; // same value as in PamfSequenceInfo + PamfStreamHeader streams; +}; + +CHECK_SIZE_ALIGN(PamfGroup, 6 + sizeof(PamfStreamHeader), 1); + +struct PamfGroupingPeriod +{ + be_t size; // doesn't include this field + + be_t start_pts_high; // always 0, greatest valid pts is UINT32_MAX + be_t start_pts_low; // same value as in PamfSequenceInfo, since there is only one PamfGroupingPeriod + be_t end_pts_high; // unused due to bug + be_t end_pts_low; // same value as in PamfSequenceInfo, since there is only one PamfGroupingPeriod + + u8 reserved; + + u8 group_num; // always 1 + PamfGroup groups; +}; + +CHECK_SIZE_ALIGN(PamfGroupingPeriod, 0x12 + sizeof(PamfGroup), 1); + +struct PamfSequenceInfo +{ + be_t size; // doesn't include this field + + be_t reserved1; + + be_t start_pts_high; // always 0, greatest valid pts is UINT32_MAX + be_t start_pts_low; // Presentation Time Stamp (start) + be_t end_pts_high; // always 0, greatest valid pts is UINT32_MAX + be_t end_pts_low; // Presentation Time Stamp (end) + + be_t mux_rate_bound; // multiplex bitrate in units of 50 bytes per second + be_t std_delay_bound; // buffer delay in units of 1/90000 seconds + + be_t total_stream_num; // across all groups; since there is always only one group, this is equal stream_count in PamfGroup + + u8 reserved2; + + u8 grouping_period_num; // always 1 + PamfGroupingPeriod grouping_periods; +}; + +CHECK_SIZE_ALIGN(PamfSequenceInfo, 0x20 + sizeof(PamfGroupingPeriod), 1); struct PamfHeader { - u32 magic; //"PAMF" - u32 version; //"0041" (is it const?) - be_t data_offset; //== 2048 >> 11, PAMF headers seem to be always 2048 bytes in size - be_t data_size; //== ((fileSize - 2048) >> 11) - u32 reserved[16]; - be_t table_size; //== size of mapping-table - u16 reserved1; - be_t start_pts_high; - be_t start_pts_low; //Presentation Time Stamp (start) - be_t end_pts_high; - be_t end_pts_low; //Presentation Time Stamp (end) - be_t mux_rate_max; //== 0x01D470 (400 bps per unit, == 48000000 bps) - be_t mux_rate_min; //== 0x0107AC (?????) - u16 reserved2; // ????? - u8 reserved3; - u8 stream_count; //total stream count (reduced to 1 byte) - be_t unk1; //== 1 (?????) - be_t table_data_size; //== table_size - 0x20 == 0x14 + (0x30 * total_stream_num) (?????) - //TODO: check relative offset of stream structs (could be from 0x0c to 0x14, currently 0x14) - be_t start_pts_high2; //????? (probably same values) - be_t start_pts_low2; //????? - be_t end_pts_high2; //????? - be_t end_pts_low2; //????? - be_t unk2; //== 0x10000 (?????) - be_t unk3; // ????? - be_t unk4; // == stream_count - //========================== - PamfStreamHeader stream_headers[256]; + be_t magic; // "PAMF" + be_t version; // "0040" or "0041" + be_t header_size; // in units of 2048 bytes + be_t data_size; // in units of 2048 bytes + + be_t psmf_marks_offset; // always 0 + be_t psmf_marks_size; // always 0 + be_t unk_offset; // always 0 + be_t unk_size; // always 0 + + u8 reserved[0x30]; + + PamfSequenceInfo seq_info; }; -CHECK_SIZE_ALIGN(PamfHeader, 136 + sizeof(PamfHeader::stream_headers), 4); +CHECK_SIZE_ALIGN(PamfHeader, 0x50 + sizeof(PamfSequenceInfo), 1); struct PamfEpHeader { - be_t value0; //mixed indexN (probably left 2 bits) and nThRefPictureOffset - be_t pts_high; - be_t pts_low; - be_t rpnOffset; + be_t value0; // 2 bits: indexN, 1 bit: unused, 13 bits: nThRefPictureOffset in units of 2048 bytes + be_t pts_high; // always 0, greatest valid pts is UINT32_MAX + be_t pts_low; + be_t rpnOffset; // in units of 2048 bytes }; -CHECK_SIZE_ALIGN(PamfEpHeader, 12, 4); +CHECK_SIZE_ALIGN(PamfEpHeader, 12, 1); + +// PSMF specific + +struct PsmfStreamHeader +{ + u8 stream_id; + u8 private_stream_id; // for streams multiplexed as private data streams (stream_id == 0xbd) + + be_t p_std_buffer; // 2 bits: unused ??? "00", 1 bit: P_STD_buffer_scale, 13 bits: P_STD_buffer_size + + be_t ep_offset; // offset of EP section in header + be_t ep_num; // count of EPs + + union + { + // Video specific information + struct + { + u8 horizontalSize; // in units of 16 pixels + u8 verticalSize; // in units of 16 pixels + } + video; + + // Audio specific information + struct + { + be_t unknown; // 0 + u8 channelConfiguration; // 1 = mono, 2 = stereo + u8 samplingFrequency; // 2 = 44.1kHz + } + audio; + }; +}; + +CHECK_SIZE_ALIGN(PsmfStreamHeader, 0x10, 1); + +struct PsmfGroup +{ + be_t size; // doesn't include this field + + u8 reserved; + + u8 stream_num; // same value as in PsmfSequenceInfo + PsmfStreamHeader streams; +}; + +CHECK_SIZE_ALIGN(PsmfGroup, 6 + sizeof(PsmfStreamHeader), 1); + +struct PsmfGroupingPeriod +{ + be_t size; // doesn't include this field + + be_t start_pts_high; // always 0, greatest valid pts is UINT32_MAX + be_t start_pts_low; // same value as in PsmfSequenceInfo, since there is only one PsmfGroupingPeriod + be_t end_pts_high; // unused due to bug + be_t end_pts_low; // same value as in PsmfSequenceInfo, since there is only one PsmfGroupingPeriod + + u8 reserved; + + u8 group_num; // always 1 + PsmfGroup groups; +}; + +CHECK_SIZE_ALIGN(PsmfGroupingPeriod, 0x12 + sizeof(PsmfGroup), 1); + +struct PsmfSequenceInfo +{ + be_t size; // doesn't include this field + + be_t start_pts_high; // always 0, greatest valid pts is UINT32_MAX + be_t start_pts_low; // Presentation Time Stamp (start) + be_t end_pts_high; // always 0, greatest valid pts is UINT32_MAX + be_t end_pts_low; // Presentation Time Stamp (end) + + be_t mux_rate_bound; // multiplex bitrate in units of 50 bytes per second + be_t std_delay_bound; // buffer delay in units of 1/90000 seconds + + u8 total_stream_num; // across all groups; since there is always only one group, this is equal stream_count in PsmfGroup + + u8 grouping_period_num; // always 1 + PsmfGroupingPeriod grouping_periods; +}; + +CHECK_SIZE_ALIGN(PsmfSequenceInfo, 0x1a + sizeof(PsmfGroupingPeriod), 1); + +struct PsmfHeader +{ + be_t magic; // "PSMF" + be_t version; // "0012", "0013", "0014" or "0015" + be_t header_size; // not scaled, unlike PAMF + be_t data_size; // not scaled, unlike PAMF + + be_t psmf_marks_offset; + be_t psmf_marks_size; + be_t unk[2]; + + u8 reserved[0x30]; + + PsmfSequenceInfo seq_info; +}; + +CHECK_SIZE_ALIGN(PsmfHeader, 0x50 + sizeof(PsmfSequenceInfo), 1); + +struct PsmfEpHeader +{ + be_t value0; // 2 bits: indexN, 2 bits: unused, 11 bits: nThRefPictureOffset in units of 1024 bytes, 1 bit: pts_high + be_t pts_low; + be_t rpnOffset; // in units of 2048 bytes +}; + +CHECK_SIZE_ALIGN(PsmfEpHeader, 10, 1); -// not directly accessed by virtual CPU, fields are unknown struct CellPamfReader { - vm::cptr pAddr; - s32 stream; - u64 fileSize; - u32 internalData[28]; + be_t headerSize; + be_t dataSize; + be_t attribute; + be_t isPsmf; + be_t version; + be_t currentGroupingPeriodIndex; + be_t currentGroupIndex; + be_t currentStreamIndex; + + union + { + struct + { + vm::bcptr header; + vm::bcptr sequenceInfo; + vm::bcptr currentGroupingPeriod; + vm::bcptr currentGroup; + vm::bcptr currentStream; + } + pamf; + + struct + { + vm::bcptr header; + vm::bcptr sequenceInfo; + vm::bcptr currentGroupingPeriod; + vm::bcptr currentGroup; + vm::bcptr currentStream; + } + psmf; + }; + + u32 reserved[18]; }; CHECK_SIZE(CellPamfReader, 128); diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index d1a5f22d02..071290c595 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -96,7 +96,7 @@ extern const std::map g_prx_list { "libmvcdec.sprx", 0 }, { "libnet.sprx", 0 }, { "libnetctl.sprx", 1 }, - { "libpamf.sprx", 0 }, + { "libpamf.sprx", 1 }, { "libpngdec.sprx", 0 }, { "libpngenc.sprx", 0 }, { "libresc.sprx", 0 },