Skip to content

Commit

Permalink
Atrac: match PSP on reset of streaming data.
Browse files Browse the repository at this point in the history
This attempts to match the sizes and positions requested by the PSP
firmware APIs.  The two functions have to match: if they don't, it will
start reading incorrect data.
  • Loading branch information
unknownbrackets committed Nov 6, 2015
1 parent ec5488c commit be1a228
Showing 1 changed file with 101 additions and 29 deletions.
130 changes: 101 additions & 29 deletions Core/HLE/sceAtrac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
#define ATRAC_ERROR_SECOND_BUFFER_NEEDED 0x80630012
#define ATRAC_ERROR_INCORRECT_READ_SIZE 0x80630013
#define ATRAC_ERROR_BAD_SAMPLE 0x80630015
#define ATRAC_ERROR_BAD_FIRST_RESET_SIZE 0x80630016
#define ATRAC_ERROR_BAD_SECOND_RESET_SIZE 0x80630017
#define ATRAC_ERROR_ADD_DATA_IS_TOO_BIG 0x80630018
#define ATRAC_ERROR_NOT_MONO 0x80630019
#define ATRAC_ERROR_NO_LOOP_INFORMATION 0x80630021
Expand Down Expand Up @@ -1086,6 +1088,57 @@ static u32 sceAtracEndEntry() {
return 0;
}

static void AtracGetResetBufferInfo(Atrac *atrac, AtracResetBufferInfo *bufferInfo, int sample) {
if (atrac->bufferState == ATRAC_STATUS_ALL_DATA_LOADED) {
bufferInfo->first.writePosPtr = atrac->first.addr;
// Everything is loaded, so nothing needs to be read.
bufferInfo->first.writableBytes = 0;
bufferInfo->first.minWriteBytes = 0;
bufferInfo->first.filePos = 0;
} else if (atrac->bufferState == 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 = atrac->first.addr + atrac->first.size;
bufferInfo->first.writableBytes = atrac->first.filesize - atrac->first.size;
int minWriteBytes = atrac->getFileOffsetBySample(sample) - atrac->first.size;
if (minWriteBytes > 0) {
bufferInfo->first.minWriteBytes = minWriteBytes;
} else {
bufferInfo->first.minWriteBytes = 0;
}
bufferInfo->first.filePos = atrac->first.size;

atrac->first.writableBytes = bufferInfo->first.writableBytes;
} else {
// This is without the sample offset. The file offset also includes the previous batch of samples?
const int atracSamplesPerFrame = (atrac->codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
int sampleFileOffset = atrac->getFileOffsetBySample(sample - atrac->firstSampleoffset - atracSamplesPerFrame);

// Update the writable bytes. When streaming, this is just the number of bytes until the end.
const u32 bufSizeAligned = (atrac->atracBufSize / atrac->atracBytesPerFrame) * atrac->atracBytesPerFrame;
const int needsMoreFrames = atrac->codecType == PSP_CODEC_AT3PLUS ? 368 : 69;

bufferInfo->first.writePosPtr = atrac->first.addr;
bufferInfo->first.writableBytes = std::min(atrac->first.filesize - sampleFileOffset, bufSizeAligned);
if (((sample + atrac->firstSampleoffset) % atracSamplesPerFrame) > needsMoreFrames) {
// Not clear why, but it seems it wants a bit extra in case the sample is late?
bufferInfo->first.minWriteBytes = atrac->atracBytesPerFrame * 3;
if ((u32)sample < (u32)atrac->firstSampleoffset) {
sampleFileOffset -= atrac->atracBytesPerFrame;
}
} else {
bufferInfo->first.minWriteBytes = atrac->atracBytesPerFrame * 2;
}
bufferInfo->first.filePos = sampleFileOffset;
}

// It seems like this is always the same as the first buffer's pos, weirdly.
bufferInfo->second.writePosPtr = atrac->first.addr;
bufferInfo->second.writableBytes = atrac->second.writableBytes;
bufferInfo->second.minWriteBytes = atrac->second.neededBytes;
bufferInfo->second.filePos = atrac->second.fileoffset;
}

static u32 sceAtracGetBufferInfoForResetting(int atracID, int sample, u32 bufferInfoAddr) {
auto bufferInfo = PSPPointer<AtracResetBufferInfo>::Create(bufferInfoAddr);

Expand All @@ -1099,32 +1152,9 @@ static u32 sceAtracGetBufferInfoForResetting(int atracID, int sample, u32 buffer
} else if ((u32)sample + atrac->firstSampleoffset > (u32)atrac->endSample + atrac->firstSampleoffset) {
return hleLogWarning(ME, ATRAC_ERROR_BAD_SAMPLE, "invalid sample position");
} else {
int Sampleoffset = atrac->getFileOffsetBySample(sample);
int minWritebytes = std::max(Sampleoffset - (int)atrac->first.size, 0);
// Reset temp buf for adding more stream data and set full filled buffer
atrac->first.writableBytes = std::min(atrac->first.filesize - atrac->first.size, atrac->atracBufSize);
atrac->first.offset = 0;
// minWritebytes should not be bigger than writeablebytes
minWritebytes = std::min(minWritebytes, (int)atrac->first.writableBytes);

// If we've already loaded everything, the answer is 0.
if (atrac->first.size >= atrac->first.filesize) {
Sampleoffset = 0;
}
AtracGetResetBufferInfo(atrac, bufferInfo, sample);

bufferInfo->first.writePosPtr = atrac->first.addr;
bufferInfo->first.writableBytes = atrac->first.writableBytes;
bufferInfo->first.minWriteBytes = minWritebytes;
bufferInfo->first.filePos = Sampleoffset;

// TODO: It seems like this is always the same as the first buffer's pos?
bufferInfo->second.writePosPtr = atrac->first.addr;
bufferInfo->second.writableBytes = atrac->second.writableBytes;
bufferInfo->second.minWriteBytes = atrac->second.neededBytes;
bufferInfo->second.filePos = atrac->second.fileoffset;

INFO_LOG(ME, "0=sceAtracGetBufferInfoForResetting(%i, %i, %08x)",atracID, sample, bufferInfoAddr);
return 0;
return hleLogSuccessInfoI(ME, 0);
}
}

Expand Down Expand Up @@ -1416,10 +1446,52 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi
} else if ((u32)sample + atrac->firstSampleoffset > (u32)atrac->endSample + atrac->firstSampleoffset) {
return hleLogWarning(ME, ATRAC_ERROR_BAD_SAMPLE, "invalid sample position");
} else {
INFO_LOG(ME, "sceAtracResetPlayPosition(%i, %i, %i, %i)", atracID, sample, bytesWrittenFirstBuf, bytesWrittenSecondBuf);
if (bytesWrittenFirstBuf > 0) {
atrac->first.fileoffset = atrac->getFileOffsetBySample(sample);
sceAtracAddStreamData(atracID, bytesWrittenFirstBuf);
// Reuse the same calculation as before.
AtracResetBufferInfo bufferInfo;
AtracGetResetBufferInfo(atrac, &bufferInfo, sample);

if ((u32)bytesWrittenFirstBuf < bufferInfo.first.minWriteBytes || (u32)bytesWrittenFirstBuf > bufferInfo.first.writableBytes) {
return hleLogError(ME, ATRAC_ERROR_BAD_FIRST_RESET_SIZE, "first byte count not in valid range");
}
if ((u32)bytesWrittenSecondBuf < bufferInfo.second.minWriteBytes || (u32)bytesWrittenSecondBuf > bufferInfo.second.writableBytes) {
return hleLogError(ME, ATRAC_ERROR_BAD_SECOND_RESET_SIZE, "second byte count not in valid range");
}

if (atrac->bufferState == ATRAC_STATUS_ALL_DATA_LOADED) {
// Always adds zero bytes.
} else if (atrac->bufferState == ATRAC_STATUS_HALFWAY_BUFFER) {
// Okay, it's a valid number of bytes. Let's set them up.
if (bytesWrittenFirstBuf != 0) {
// TODO: We should just use the buffer in PSP RAM.
Memory::Memcpy(atrac->data_buf + atrac->first.size, atrac->first.addr + atrac->first.size, bytesWrittenFirstBuf);
atrac->first.fileoffset += bytesWrittenFirstBuf;
atrac->first.size += bytesWrittenFirstBuf;
atrac->first.writableBytes -= bytesWrittenFirstBuf;
atrac->first.offset += bytesWrittenFirstBuf;
}

// Did we transition to a full buffer?
if (atrac->first.size >= atrac->first.filesize) {
atrac->first.size = atrac->first.filesize;
if (atrac->bufferState == ATRAC_STATUS_HALFWAY_BUFFER)
atrac->bufferState = ATRAC_STATUS_ALL_DATA_LOADED;
}
} else {
if (bufferInfo.first.filePos > atrac->first.filesize) {
return hleDelayResult(hleLogError(ME, ATRAC_ERROR_API_FAIL, "invalid file position"), "reset play pos", 200);
}

// Move the offset to the specified position.
atrac->first.fileoffset = bufferInfo.first.filePos;

if (bytesWrittenFirstBuf != 0) {
// TODO: We should just use the buffer in PSP RAM.
Memory::Memcpy(atrac->data_buf + atrac->first.fileoffset, atrac->first.addr, bytesWrittenFirstBuf);
atrac->first.fileoffset += bytesWrittenFirstBuf;
}
atrac->first.size = atrac->first.fileoffset;
atrac->first.writableBytes = bufferInfo.first.writableBytes - bytesWrittenFirstBuf;
atrac->first.offset = bytesWrittenFirstBuf;
}
#ifdef USE_FFMPEG
if ((atrac->codecType == PSP_MODE_AT_3 || atrac->codecType == PSP_MODE_AT_3_PLUS) && atrac->pCodecCtx) {
Expand Down

0 comments on commit be1a228

Please sign in to comment.