Port over some utility functions, fix bugs.

This commit is contained in:
Henrik Rydgård 2025-03-09 18:47:49 +01:00
parent 9c02eab137
commit 7a4d175e06
6 changed files with 214 additions and 30 deletions

View file

@ -85,7 +85,7 @@ int PSPGamedataInstallDialog::Init(u32 paramAddr) {
if (allFilesSize == 0) {
ERROR_LOG_REPORT(Log::sceUtility, "Game install with no files / data");
// TODO: What happens here?
// Getting a lot of reports of this from patched football games. Can probably ignore. https://report.ppsspp.org/logs/kind/793
return -1;
}

View file

@ -10,7 +10,6 @@
//
// See the big comment in sceAtrac.cpp for an overview of the different modes of operation.
Atrac2::Atrac2(int atracID, u32 contextAddr, int codecType) {
context_ = PSPPointer<SceAtracContext>::Create(contextAddr);
track_.codecType = codecType;
@ -26,6 +25,10 @@ void Atrac2::DoState(PointerWrap &p) {
int Atrac2::RemainingFrames() const {
const SceAtracIdInfo &info = context_->info;
if (info.state == 0) {
return SCE_ERROR_ATRAC_BAD_ATRACID;
}
if (info.state == ATRAC_STATUS_ALL_DATA_LOADED) {
// The buffer contains everything.
return PSP_ATRAC_ALLDATA_IS_ON_MEMORY;
@ -84,11 +87,158 @@ int Atrac2::GetSoundSample(int *endSample, int *loopStartSample, int *loopEndSam
int Atrac2::ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) {
*delay = false;
// This was mostly copied straight from the old impl.
// Reuse the same calculation as before.
AtracResetBufferInfo bufferInfo;
GetResetBufferInfo(&bufferInfo, sample);
if ((u32)bytesWrittenFirstBuf < bufferInfo.first.minWriteBytes || (u32)bytesWrittenFirstBuf > bufferInfo.first.writableBytes) {
return SCE_ERROR_ATRAC_BAD_FIRST_RESET_SIZE;
}
if ((u32)bytesWrittenSecondBuf < bufferInfo.second.minWriteBytes || (u32)bytesWrittenSecondBuf > bufferInfo.second.writableBytes) {
return SCE_ERROR_ATRAC_BAD_SECOND_RESET_SIZE;
}
const SceAtracIdInfo &info = context_->info;
if (info.state == ATRAC_STATUS_ALL_DATA_LOADED) {
// Always adds zero bytes.
} else if (info.state == ATRAC_STATUS_HALFWAY_BUFFER) {
/*
// Okay, it's a valid number of bytes. Let's set them up.
if (bytesWrittenFirstBuf != 0) {
first_.fileoffset += bytesWrittenFirstBuf;
first_.size += bytesWrittenFirstBuf;
first_.offset += bytesWrittenFirstBuf;
}
// Did we transition to a full buffer?
if (first_.size >= track_.fileSize) {
first_.size = track_.fileSize;
bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
}
*/
} else {
if (bufferInfo.first.filePos > track_.fileSize) {
*delay = true;
return SCE_ERROR_ATRAC_API_FAIL;
}
/*
// Move the offset to the specified position.
first_.fileoffset = bufferInfo.first.filePos;
if (bytesWrittenFirstBuf != 0) {
if (!ignoreDataBuf_) {
Memory::Memcpy(dataBuf_ + first_.fileoffset, first_.addr, bytesWrittenFirstBuf, "AtracResetPlayPosition");
}
first_.fileoffset += bytesWrittenFirstBuf;
}
first_.size = first_.fileoffset;
first_.offset = bytesWrittenFirstBuf;
bufferHeaderSize_ = 0;
bufferPos_ = track_.bytesPerFrame;
bufferValidBytes_ = bytesWrittenFirstBuf - bufferPos_;
*/
}
_dbg_assert_(track_.codecType == PSP_MODE_AT_3 || track_.codecType == PSP_MODE_AT_3_PLUS);
SeekToSample(sample);
return 0;
}
void Atrac2::SeekToSample(int sample) {
// This was mostly copied straight from the old impl.
SceAtracIdInfo &info = context_->info;
// It seems like the PSP aligns the sample position to 0x800...?
const u32 offsetSamples = track_.FirstSampleOffsetFull();
const u32 unalignedSamples = (offsetSamples + sample) % track_.SamplesPerFrame();
int seekFrame = sample + offsetSamples - unalignedSamples;
if ((sample != info.decodePos || sample == 0) && decoder_ != nullptr) {
// Prefill the decode buffer with packets before the first sample offset.
decoder_->FlushBuffers();
int adjust = 0;
if (sample == 0) {
int offsetSamples = track_.FirstSampleOffsetFull();
adjust = -(int)(offsetSamples % track_.SamplesPerFrame());
}
const u32 off = track_.FileOffsetBySample(sample + adjust);
const u32 backfill = track_.bytesPerFrame * 2;
const u32 start = off - track_.dataByteOffset < backfill ? track_.dataByteOffset : off - backfill;
for (u32 pos = start; pos < off; pos += track_.bytesPerFrame) {
decoder_->Decode(Memory::GetPointer(info.buffer + pos), track_.bytesPerFrame, nullptr, 2, nullptr, nullptr);
}
}
// Probably more stuff that needs updating!
info.decodePos = sample;
}
int Atrac2::GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) {
_dbg_assert_(false);
// This was mostly copied straight from the old impl.
const SceAtracIdInfo &info = context_->info;
if (info.state == ATRAC_STATUS_ALL_DATA_LOADED) {
bufferInfo->first.writePosPtr = info.buffer;
// Everything is loaded, so nothing needs to be read.
bufferInfo->first.writableBytes = 0;
bufferInfo->first.minWriteBytes = 0;
bufferInfo->first.filePos = 0;
} else if (info.state == ATRAC_STATUS_HALFWAY_BUFFER) {
// Here the message is: you need to read at least this many bytes to get to that position.
// This is because we're filling the buffer start to finish, not streaming.
bufferInfo->first.writePosPtr = info.buffer + info.curOff;
bufferInfo->first.writableBytes = track_.fileSize - info.curOff;
int minWriteBytes = track_.FileOffsetBySample(sample) - info.curOff;
if (minWriteBytes > 0) {
bufferInfo->first.minWriteBytes = minWriteBytes;
} else {
bufferInfo->first.minWriteBytes = 0;
}
bufferInfo->first.filePos = info.curOff;
} else {
// This is without the sample offset. The file offset also includes the previous batch of samples?
int sampleFileOffset = track_.FileOffsetBySample(sample - track_.firstSampleOffset - track_.SamplesPerFrame());
// Update the writable bytes. When streaming, this is just the number of bytes until the end.
const u32 bufSizeAligned = (info.bufferByte / track_.bytesPerFrame) * track_.bytesPerFrame;
const int needsMoreFrames = track_.FirstOffsetExtra(); // ?
bufferInfo->first.writePosPtr = info.buffer;
bufferInfo->first.writableBytes = std::min(track_.fileSize - sampleFileOffset, bufSizeAligned);
if (((sample + track_.firstSampleOffset) % (int)track_.SamplesPerFrame()) >= (int)track_.SamplesPerFrame() - needsMoreFrames) {
// Not clear why, but it seems it wants a bit extra in case the sample is late?
bufferInfo->first.minWriteBytes = track_.bytesPerFrame * 3;
} else {
bufferInfo->first.minWriteBytes = track_.bytesPerFrame * 2;
}
if ((u32)sample < (u32)track_.firstSampleOffset && sampleFileOffset != track_.dataByteOffset) {
sampleFileOffset -= track_.bytesPerFrame;
}
bufferInfo->first.filePos = sampleFileOffset;
if (info.secondBufferByte != 0) {
// TODO: We have a second buffer. Within it, minWriteBytes should be zero.
// The filePos should be after the end of the second buffer (or zero.)
// We actually need to ensure we READ from the second buffer before implementing that.
}
}
// It seems like this is always the same as the first buffer's pos, weirdly.
bufferInfo->second.writePosPtr = info.buffer;
// Reset never needs a second buffer write, since the loop is in a fixed place.
bufferInfo->second.writableBytes = 0;
bufferInfo->second.minWriteBytes = 0;
bufferInfo->second.filePos = 0;
return 0;
}
@ -111,6 +261,17 @@ u32 Atrac2::GetNextSamples() {
return samples;
}
int Atrac2::GetNextDecodePosition(int *pos) const {
const SceAtracIdInfo &info = context_->info;
const int currentSample = info.decodePos - track_.FirstSampleOffsetFull();
if (currentSample >= track_.endSample) {
*pos = 0;
return SCE_ERROR_ATRAC_ALL_DATA_DECODED;
}
*pos = currentSample;
return 0;
}
int Atrac2::AddStreamData(u32 bytesToAdd) {
SceAtracIdInfo &info = context_->info;
// if (bytesToAdd > first_.writableBytes)
@ -168,6 +329,7 @@ u32 Atrac2::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish,
int samplesToWrite = track_.SamplesPerFrame();
if (discardedSamples_) {
_dbg_assert_(samplesToWrite >= discardedSamples_);
samplesToWrite -= discardedSamples_;
discardedSamples_ = 0;
}
@ -194,6 +356,12 @@ u32 Atrac2::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish,
return SCE_ERROR_ATRAC_ALL_DATA_DECODED;
}
// Write the decoded samples to memory.
// TODO: We can detect cases where we can safely just decode directly into output (full samplesToWrite, outbuf != nullptr)
if (outbuf) {
memcpy(outbuf, decodeTemp_, samplesToWrite * outputChannels_ * sizeof(int16_t));
}
info.streamDataByte -= info.sampleSize;
info.streamOff += info.sampleSize;
info.curOff += info.sampleSize;
@ -201,7 +369,7 @@ u32 Atrac2::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish,
// If we reached the end of the buffer, move the cursor back to the start.
// SetData takes care of any split packet.
if (info.streamOff + info.sampleSize > info.bufferByte) {
if (AtracStatusIsStreaming(info.state) && info.streamOff + info.sampleSize > info.bufferByte) {
// Check that we're on the first lap. Should only happen on the first lap around.
_dbg_assert_(info.curOff - info.sampleSize < info.bufferByte);
INFO_LOG(Log::ME, "Hit the buffer wrap.");
@ -221,10 +389,10 @@ u32 Atrac2::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish,
int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 bufferSize, int outputChannels) {
// TODO: Remove track_.
track_ = track;
SceAtracIdInfo &info = context_->info;
if (track_.codecType != PSP_MODE_AT_3 && track_.codecType != PSP_MODE_AT_3_PLUS) {
// Shouldn't have gotten here, Analyze() checks this.
context_->info.state = ATRAC_STATUS_NO_DATA;
ERROR_LOG(Log::ME, "unexpected codec type %d in set data", track_.codecType);
return SCE_ERROR_ATRAC_UNKNOWN_FORMAT;
}
@ -239,7 +407,6 @@ int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 buffer
// Copied from the old implementation, let's see where they are useful.
int firstExtra = track_.FirstOffsetExtra();
SceAtracIdInfo &info = context_->info;
// Copy parameters into struct.
info.buffer = bufferAddr;
info.bufferByte = bufferSize;
@ -291,10 +458,10 @@ int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 buffer
decodeTemp_ = new int16_t[track_.SamplesPerFrame() * track_.channels];
}
// TODO: Decode the first dummy frame to the temp buffer. This initializes the decoder.
// TODO: Decode/discard any first dummy frames to the temp buffer. This initializes the decoder.
// It really does seem to be what's happening here, as evidenced by inBuf in the codec struct - it gets initialized.
// Alternatively, the dummy frame is just there to leave space for wrapping...
if (track_.FirstSampleOffsetFull() >= track_.SamplesPerFrame()) {
while (discardedSamples_ >= track_.SamplesPerFrame()) {
int bytesConsumed;
int outSamples;
if (!decoder_->Decode(Memory::GetPointer(info.buffer + info.streamOff), info.sampleSize, &bytesConsumed, track_.channels, decodeTemp_, &outSamples)) {
@ -307,20 +474,22 @@ int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 buffer
}
// We need to handle wrapping the overshot partial packet at the end. Let's start by computing it.
int cutLen = (info.bufferByte - info.curOff) % info.sampleSize;
int cutRest = info.sampleSize - cutLen;
if (AtracStatusIsStreaming(info.state)) {
int cutLen = (info.bufferByte - info.curOff) % info.sampleSize;
int cutRest = info.sampleSize - cutLen;
// Then, let's copy it.
if (cutLen > 0) {
INFO_LOG(Log::ME, "Packets didn't fit evenly. Last packet got split into %d/%d (sum=%d). Copying to start of buffer.", cutLen, cutRest, cutLen, cutRest, info.sampleSize);
Memory::Memcpy(info.buffer, info.buffer + info.bufferByte - cutLen, cutLen);
// Then, let's copy it.
if (cutLen > 0) {
INFO_LOG(Log::ME, "Streaming: Packets didn't fit evenly. Last packet got split into %d/%d (sum=%d). Copying to start of buffer.", cutLen, cutRest, cutLen, cutRest, info.sampleSize);
Memory::Memcpy(info.buffer, info.buffer + info.bufferByte - cutLen, cutLen);
}
}
return 0;
}
u32 Atrac2::SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) {
return 0;
return 0;
}
int Atrac2::Bitrate() const {

View file

@ -19,7 +19,7 @@ public:
int GetID() const override { return context_->info.atracID; }
int GetNextDecodePosition(int *pos) const override { return context_->info.decodePos; }
int GetNextDecodePosition(int *pos) const override;
int RemainingFrames() const override;
int LoopStatus() const override { return 0; }
@ -52,6 +52,8 @@ public:
void NotifyGetContextAddress() override {}
private:
void SeekToSample(int sample);
// Just the current decoded frame, in order to be able to cut off the first part of it
// to write the initial partial frame.
// Does not need to be saved.

View file

@ -204,7 +204,7 @@ template<int func(u32, u32)> void WrapI_UU() {
template<int func(u32, float, float)> void WrapI_UFF() {
// Not sure about the float arguments.
int retval = func(PARAM(0), PARAMF(0), PARAMF(1));
RETURN(retval);
RETURN(retval);
}
template<int func(u32, u32, u32)> void WrapI_UUU() {

View file

@ -129,6 +129,7 @@ void __AtracNotifyLoadModule(int version, u32 crc, u32 bssAddr, int bssSize) {
g_atracBSS = bssAddr;
g_atracMaxContexts = atracLibVersion <= 0x101 ? 4 : 6; // Need to figure out where the cutoff is.
_dbg_assert_(bssSize >= g_atracMaxContexts * sizeof(SceAtracContext));
Memory::Memset(g_atracBSS, 0, g_atracMaxContexts * sizeof(SceAtracContext));
NotifyMemInfo(MemBlockFlags::ALLOC, g_atracBSS, g_atracMaxContexts * sizeof(SceAtracContext), "AtracContext");
}
@ -282,7 +283,7 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32
u32 numSamples = 0;
u32 finish = 0;
int remains = 0;
int ret = atrac->DecodeData(Memory::GetPointerWrite(outAddr), outAddr, &numSamples, &finish, &remains);
int ret = atrac->DecodeData(outAddr ? Memory::GetPointerWrite(outAddr) : nullptr, outAddr, &numSamples, &finish, &remains);
if (ret != (int)SCE_ERROR_ATRAC_BAD_ATRACID && ret != (int)SCE_ERROR_ATRAC_NO_DATA) {
if (Memory::IsValidAddress(numSamplesAddr))
Memory::WriteUnchecked_U32(numSamples, numSamplesAddr);
@ -412,16 +413,16 @@ static u32 sceAtracGetNextDecodePosition(int atracID, u32 outposAddr) {
return hleLogError(Log::ME, err);
}
if (!Memory::IsValidAddress(outposAddr)) {
return hleLogError(Log::ME, 0, "invalid address");
}
int pos = 0;
int ret = atrac->GetNextDecodePosition(&pos);
if (ret < 0) {
return hleLogError(Log::ME, ret);
}
if (!Memory::IsValidAddress(outposAddr)) {
return hleLogError(Log::ME, 0, "invalid address");
}
Memory::WriteUnchecked_U32(pos, outposAddr);
return hleLogDebug(Log::ME, 0);
}
@ -443,20 +444,19 @@ static u32 sceAtracGetNextSample(int atracID, u32 outNAddr) {
// Obtains the number of frames remaining in the buffer which can be decoded.
// When no more data would be needed, this returns a negative number.
static u32 sceAtracGetRemainFrame(int atracID, u32 remainAddr) {
auto remainingFrames = PSPPointer<u32_le>::Create(remainAddr);
AtracBase *atrac = getAtrac(atracID);
u32 err = AtracValidateManaged(atrac);
if (err != 0) {
return hleLogError(Log::ME, err);
}
if (!remainingFrames.IsValid()) {
if (!Memory::IsValidAddress(remainAddr)) {
// Would crash.
return hleReportError(Log::ME, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid remainingFrames pointer");
}
*remainingFrames = atrac->RemainingFrames();
u32 remaining = atrac->RemainingFrames();
Memory::WriteUnchecked_U32(remaining, remainAddr);
return hleLogDebug(Log::ME, 0);
}
@ -491,10 +491,13 @@ static u32 sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoop
return hleLogError(Log::ME, err);
}
int endSample;
int loopStart;
int loopEnd;
int endSample = -1;
int loopStart = -1;
int loopEnd = -1;
int retval = atrac->GetSoundSample(&endSample, &loopStart, &loopEnd);
if (retval < 0) {
return hleLogError(Log::ME, retval);
}
if (Memory::IsValidAddress(outEndSampleAddr)) {
Memory::WriteUnchecked_U32(endSample, outEndSampleAddr);
}
@ -619,7 +622,7 @@ static u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize) {
}
if (track.codecType != atracContextTypes[atracID]) {
// TODO: Should this not change the buffer size?
return hleReportError(Log::ME, SCE_ERROR_ATRAC_WRONG_CODECTYPE, "atracID uses different codec type than data");
return hleLogError(Log::ME, SCE_ERROR_ATRAC_WRONG_CODECTYPE, "atracID uses different codec type than data");
}
ret = atrac->SetData(track, buffer, bufferSize, bufferSize, 2);

View file

@ -46,6 +46,16 @@ enum AtracStatus : u8 {
};
const char *AtracStatusToString(AtracStatus status);
inline bool AtracStatusIsStreaming(AtracStatus status) {
switch (status) {
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
return true;
default:
return false;
}
}
typedef AtracStatus AtracStatus_le;