diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index 2d11e7c3b..7134df80c 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -509,17 +509,6 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { // split_frame_len_ - split_frame_len_partial_); } - if (split_frame_len_partial_ > split_frame_len_) { - XELOGAPU( - "XmaContext {}: Error - Invalid split frame lengths {}! " - "frame_length: {} " - "partial_length: {}", - id(), split_frame_len_, split_frame_len_partial_); - split_frame_len_ = 0; - split_frame_len_partial_ = 0; - SwapInputBuffer(data); - return; - } auto offset = stream.Copy( xma_frame_.data() + 1 + ((split_frame_len_partial_ + split_frame_padding_start_) / 8), @@ -597,9 +586,19 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { std::memset(xma_frame_.data(), 0, xma_frame_.size()); { - auto offset = - stream.Copy(xma_frame_.data() + 1, - std::min(split_frame_len_, split_frame_len_partial_)); + int32_t bits_to_copy = + std::min(split_frame_len_, split_frame_len_partial_); + + if (!stream.IsOffsetValid(bits_to_copy)) { + XELOGAPU( + "XmaContext {}: Error - Invalid amount of bits to copy! " + "split_frame_len: {}, split_partial: {}, offset_bits: {}", + id(), split_frame_len_, split_frame_len_partial_, + stream.offset_bits()); + SwapInputBuffer(data); + return; + } + auto offset = stream.Copy(xma_frame_.data() + 1, bits_to_copy); assert_true(offset < 8); split_frame_padding_start_ = static_cast(offset); } @@ -716,6 +715,8 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { if (!reuse_input_buffer) { if (is_streaming) { SwapInputBuffer(data); + data->input_buffer_read_offset = + GetPacketFirstFrameOffset(data); } else { is_stream_done_ = true; } @@ -727,6 +728,9 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { } } packet = current_input_buffer + packet_idx * kBytesPerPacket; + // TODO(Gliniak): There might be an edge-case when we're in packet 26/27 + // and GetPacketFrameOffset returns that there is no data in this packet + // aka. FrameOffset is set to more than 0x7FFF-0x20 offset = xma::GetPacketFrameOffset(packet) + packet_idx * kBitsPerPacket; } @@ -766,6 +770,23 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { } } +uint32_t XmaContext::GetPacketFirstFrameOffset(const XMA_CONTEXT_DATA* data) { + uint32_t first_frame_offset = kBitsPerHeader; + + uint8_t* in0 = data->input_buffer_0_valid + ? memory()->TranslatePhysical(data->input_buffer_0_ptr) + : nullptr; + uint8_t* in1 = data->input_buffer_1_valid + ? memory()->TranslatePhysical(data->input_buffer_1_ptr) + : nullptr; + uint8_t* current_input_buffer = data->current_buffer ? in1 : in0; + + if (current_input_buffer) { + first_frame_offset = xma::GetPacketFrameOffset(current_input_buffer); + } + return first_frame_offset; +} + size_t XmaContext::GetNextFrame(uint8_t* block, size_t size, size_t bit_offset) { // offset = xma::GetPacketFrameOffset(packet); @@ -884,11 +905,11 @@ std::tuple XmaContext::GetPacketFrameCount(uint8_t* packet) { int frame_count = 0; while (true) { - frame_count++; if (stream.BitsRemaining() < 15) { - return {frame_count, true}; + return {frame_count, false}; } + frame_count++; uint64_t size = stream.Read(15); if ((size - 15) > stream.BitsRemaining()) { return {frame_count, true}; diff --git a/src/xenia/apu/xma_context.h b/src/xenia/apu/xma_context.h index bd09e2ced..baa70643e 100644 --- a/src/xenia/apu/xma_context.h +++ b/src/xenia/apu/xma_context.h @@ -196,6 +196,10 @@ class XmaContext { void Decode(XMA_CONTEXT_DATA* data); int PrepareDecoder(uint8_t* packet, int sample_rate, bool is_two_channel); + // This method should be used ONLY when we're at the last packet of the stream + // and we want to find offset in next buffer + uint32_t GetPacketFirstFrameOffset(const XMA_CONTEXT_DATA* data); + Memory* memory_ = nullptr; uint32_t id_ = 0; diff --git a/src/xenia/base/bit_stream.cc b/src/xenia/base/bit_stream.cc index ced073907..e7f47fb3b 100644 --- a/src/xenia/base/bit_stream.cc +++ b/src/xenia/base/bit_stream.cc @@ -29,6 +29,16 @@ void BitStream::SetOffset(size_t offset_bits) { size_t BitStream::BitsRemaining() { return size_bits_ - offset_bits_; } +bool BitStream::IsOffsetValid(size_t num_bits) { + size_t offset_bytes = offset_bits_ >> 3; + size_t rel_offset_bits = offset_bits_ - (offset_bytes << 3); + + if (rel_offset_bits && int32_t(num_bits - 8 - rel_offset_bits) < 0) { + return false; + } + return true; +} + uint64_t BitStream::Peek(size_t num_bits) { // FYI: The reason we can't copy more than 57 bits is: // 57 = 7 * 8 + 1 - that can only span a maximum of 8 bytes. diff --git a/src/xenia/base/bit_stream.h b/src/xenia/base/bit_stream.h index def889a3e..1d7036188 100644 --- a/src/xenia/base/bit_stream.h +++ b/src/xenia/base/bit_stream.h @@ -28,6 +28,7 @@ class BitStream { void Advance(size_t num_bits); void SetOffset(size_t offset_bits); size_t BitsRemaining(); + bool IsOffsetValid(size_t num_bits); // Note: num_bits MUST be in the range 0-57 (inclusive) uint64_t Peek(size_t num_bits);