#include <algorithm>
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/Log.h"
#include "Core/MemMapHelpers.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/ErrorCodes.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/System.h"
#include "Core/HLE/AtracCtx2.h"
#include "Core/Util/AtracTrack.h"
#include "Core/HW/Atrac3Standalone.h"
#include "Core/Config.h"
#include "Core/MemMap.h"
#include "Common/File/FileUtil.h"
struct AT3BitrateMeta {
u16 sampleSize;
u8 dataByte;
u8 jointStereo;
};
static const AT3BitrateMeta g_at3BitrateMeta[5] = {
{0x180, 0x04, 0},
{0x130, 0x06, 0},
{0x0C0, 0x0B, 1},
{0x0C0, 0x0E, 0},
{0x098, 0x0F, 0},
};
static int RoundDownToMultiple(int size, int grain) {
return size - (size % grain);
}
static int RoundDownToMultipleWithOffset(int offset, int size, int grain) {
if (size > offset) {
return ((size - offset) / grain) * grain + offset;
} else {
return size;
}
}
static int ComputeSkipFrames(const SceAtracIdInfo &info, int seekPos) {
return (seekPos & info.SamplesFrameMask()) < info.SkipSamples() ? 2 : 1;
}
static int ComputeFileOffset(const SceAtracIdInfo &info, int seekPos) {
int frameOffset = ((seekPos / info.SamplesPerFrame()) - 1) * info.sampleSize;
if ((seekPos & info.SamplesFrameMask()) < info.SkipSamples() && (frameOffset != 0)) {
frameOffset -= info.sampleSize;
}
return frameOffset + info.dataOff;
}
static int ComputeLoopEndFileOffset(const SceAtracIdInfo &info, int seekPos) {
return (seekPos / info.SamplesPerFrame() + 1) * info.sampleSize + info.dataOff;
}
static int ComputeSpaceUsed(const SceAtracIdInfo &info) {
if (info.decodePos > info.loopEnd && info.curBuffer == 1) {
int space = info.secondBufferByte;
if (info.secondStreamOff < space) {
space = RoundDownToMultipleWithOffset(info.secondStreamOff, info.secondBufferByte, info.sampleSize);
}
if ((info.secondStreamOff <= space) && (space - info.secondStreamOff < info.streamDataByte)) {
return info.streamDataByte - (space - info.secondStreamOff);
}
return 0;
}
return info.streamDataByte;
}
static int ComputeRemainFrameStream(const SceAtracIdInfo &info) {
if (info.streamDataByte >= info.fileDataEnd - info.curFileOff) {
return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;
}
return std::max(0, info.streamDataByte / info.sampleSize - (int)info.numSkipFrames);
}
static int ComputeRemainFrameLooped(const SceAtracIdInfo &info) {
const int loopStartFileOffset = ComputeFileOffset(info, info.loopStart);
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
const int writeFileOff = info.curFileOff + info.streamDataByte;
const int leftToRead = writeFileOff - loopEndFileOffset;
int remainFrames;
if (writeFileOff <= loopEndFileOffset) {
remainFrames = info.streamDataByte / info.sampleSize;
} else {
const int skipFramesAtLoopStart = ComputeSkipFrames(info, info.loopStart);
const int firstPartLength = loopEndFileOffset - loopStartFileOffset;
const int secondPartLength = leftToRead % firstPartLength;
remainFrames = (loopEndFileOffset - info.curFileOff) / info.sampleSize +
(leftToRead / firstPartLength) * (firstPartLength / info.sampleSize - skipFramesAtLoopStart);
if (secondPartLength > skipFramesAtLoopStart * info.sampleSize) {
remainFrames += secondPartLength / info.sampleSize - skipFramesAtLoopStart;
}
}
remainFrames = std::max(0, remainFrames - (int)info.numSkipFrames);
if (info.loopNum < 0) {
return remainFrames;
}
const int streamBufferEndFileOffset = info.curFileOff + info.streamDataByte;
if (streamBufferEndFileOffset >= loopEndFileOffset) {
const int numBufferedLoops = (streamBufferEndFileOffset - loopEndFileOffset) / (loopEndFileOffset - loopStartFileOffset);
if (info.loopNum <= numBufferedLoops) {
return PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY;
}
}
return remainFrames;
}
static void InitLengthAndLoop(SceAtracIdInfo *ctx, int endSample, int waveDataSize, int firstSampleOffset, int loopBegin, int loopEnd) {
const int off = ctx->codec == 0x1001 ? 0x45 : 0x170;
const int blockShift = 0x100b - ctx->codec;
const int firstValidSample = firstSampleOffset + off;
int numSamplesInFile;
if (endSample == 0) {
numSamplesInFile = waveDataSize / ctx->sampleSize << (blockShift & 0x1f);
} else {
numSamplesInFile = endSample + firstValidSample;
}
ctx->decodePos = firstValidSample;
ctx->loopNum = 0;
ctx->endSample = numSamplesInFile - 1;
ctx->numSkipFrames = (char)(firstValidSample >> (blockShift & 0x1f));
if (-1 < loopBegin) {
ctx->loopEnd = loopEnd + off;
ctx->loopStart = loopBegin + off;
return;
}
ctx->loopEnd = 0;
ctx->loopStart = 0;
return;
}
static int ComputeAtracStateAndInitSecondBuffer(SceAtracIdInfo *info, u32 readSize, u32 bufferSize) {
AtracStatus state;
int loopEndFileOffset;
int loopEnd;
if (bufferSize < (u32)info->fileDataEnd) {
if (info->streamDataByte < (s32)info->sampleSize * 3) {
return 0x80630011;
}
loopEnd = info->loopEnd;
state = ATRAC_STATUS_STREAMED_WITHOUT_LOOP;
if ((loopEnd != 0) && (state = ATRAC_STATUS_STREAMED_LOOP_FROM_END, loopEnd != info->endSample)) {
loopEndFileOffset = ComputeLoopEndFileOffset(*info, loopEnd);
loopEnd = (loopEndFileOffset - info->dataOff) + 1;
info->state = ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER;
if (loopEnd < info->streamDataByte) {
info->streamDataByte = loopEnd;
}
info->secondStreamOff = 0;
info->secondBuffer = 0;
info->secondBufferByte = 0;
return 0;
}
} else {
state = ATRAC_STATUS_HALFWAY_BUFFER;
if (readSize >= (u32)info->fileDataEnd) {
state = ATRAC_STATUS_ALL_DATA_LOADED;
}
}
info->state = state;
return 0;
}
int InitContextFromTrackInfo(SceAtracContext *ctx, const TrackInfo *wave, u32 bufferAddr, int readSize, int bufferSize) {
(ctx->info).numChan = (char)wave->numChans;
const int extraSamples = (ctx->info).codec == 0x1001 ? 0x45 : 0x170;
(ctx->info).firstValidSample = extraSamples + wave->firstSampleOffset;
int endSample3 = wave->endSample;
(ctx->info).sampleSize = wave->blockAlign;
InitLengthAndLoop(&ctx->info, endSample3, wave->waveDataSize, wave->firstSampleOffset, wave->loopStart, wave->loopEnd);
const int dataOff = wave->dataOff;
const int endSample = (ctx->info).endSample;
(ctx->info).streamDataByte = readSize - dataOff;
(ctx->info).buffer = bufferAddr;
(ctx->info).curFileOff = dataOff;
(ctx->info).dataOff = dataOff;
(ctx->info).fileDataEnd = wave->waveDataSize + dataOff;
(ctx->info).curBuffer = 0;
(ctx->info).bufferByte = bufferSize;
(ctx->info).streamOff = dataOff;
if ((ctx->info).loopEnd > endSample) {
return SCE_ERROR_ATRAC_BAD_CODEC_PARAMS;
}
const int numChunks = (endSample >> (0x100b - (ctx->info).codec & 0x1f));
if ((numChunks * (u32)(ctx->info).sampleSize < (wave->waveDataSize + dataOff) - dataOff)) {
int retval = ComputeAtracStateAndInitSecondBuffer(&ctx->info, readSize, bufferSize);
if (retval < 0) {
return retval;
}
if ((ctx->info).codec != PSP_CODEC_AT3) {
(ctx->codec).unk40 = wave->sampleSizeMaybe;
(ctx->codec).unk48 = 0;
(ctx->codec).unk41 = wave->tailFlag;
return 0;
}
for (int counter = 4; counter >= 0; counter--) {
if ((g_at3BitrateMeta[counter].sampleSize == (ctx->info).sampleSize) &&
((int)g_at3BitrateMeta[counter].dataByte == wave->sampleSizeMaybe)) {
(ctx->codec).unk40 = (char)g_at3BitrateMeta[counter].jointStereo;
(ctx->codec).unk41 = 0;
(ctx->codec).unk42 = 0;
(ctx->codec).unk43 = 0;
return 0;
}
}
return 0;
} else {
return SCE_ERROR_ATRAC_BAD_CODEC_PARAMS;
}
}
int Atrac2::RemainingFrames() const {
const SceAtracIdInfo &info = context_->info;
switch (info.state) {
case ATRAC_STATUS_UNINITIALIZED:
case ATRAC_STATUS_NO_DATA:
return 0;
case ATRAC_STATUS_ALL_DATA_LOADED:
return PSP_ATRAC_ALLDATA_IS_ON_MEMORY;
case ATRAC_STATUS_HALFWAY_BUFFER:
{
const int writeFileOff = info.dataOff + info.streamDataByte;
if (info.curFileOff < writeFileOff) {
return std::max(0, (writeFileOff - info.curFileOff) / info.sampleSize - (int)info.numSkipFrames);
}
return 0;
}
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
return ComputeRemainFrameStream(info);
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
return ComputeRemainFrameLooped(info);
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
if (info.decodePos <= info.loopEnd) {
return ComputeRemainFrameLooped(info);
} else {
return ComputeRemainFrameStream(info);
}
break;
default:
return SCE_ERROR_ATRAC_BAD_ATRACID;
}
}
Atrac2::Atrac2(u32 contextAddr, int codecType) {
if (contextAddr) {
context_ = PSPPointer<SceAtracContext>::Create(contextAddr);
SceAtracIdInfo &info = context_->info;
info.codec = codecType;
info.state = ATRAC_STATUS_NO_DATA;
info.curBuffer = 0;
sas_.streamOffset = 0;
sas_.bufPtr[0] = 0;
sas_.bufPtr[1] = 0;
} else {
}
}
Atrac2::~Atrac2() {
DumpBufferToFile();
}
void Atrac2::DumpBufferToFile() {
if (dumped_) {
return;
}
if (!dumpBuffer_.empty()) {
std::string filename = StringFromFormat("atrac3_%08x_incomplete.at3", context_->info.endSample);
DumpFileIfEnabled(dumpBuffer_.data(), (u32)dumpBuffer_.size(), filename, DumpFileType::Atrac3);
dumpBuffer_.clear();
}
dumped_ = true;
}
void Atrac2::DoState(PointerWrap &p) {
auto s = p.Section("Atrac2", 1, 3);
if (!s)
return;
Do(p, outputChannels_);
Do(p, context_);
if (s >= 2) {
Do(p, sas_.streamOffset);
Do(p, sas_.bufPtr[0]);
}
if (s >= 3) {
Do(p, sas_.bufPtr[1]);
Do(p, sas_.bufSize[0]);
Do(p, sas_.bufSize[1]);
Do(p, sas_.isStreaming);
Do(p, sas_.curBuffer);
Do(p, sas_.fileOffset);
}
const SceAtracIdInfo &info = context_->info;
if (p.mode == p.MODE_READ && info.state != ATRAC_STATUS_NO_DATA) {
CreateDecoder(info.codec, info.sampleSize, info.numChan);
}
}
bool Atrac2::HasSecondBuffer() const {
const SceAtracIdInfo &info = context_->info;
return info.secondBufferByte != 0;
}
int Atrac2::GetSoundSample(int *outEndSample, int *outLoopStartSample, int *outLoopEndSample) const {
const SceAtracIdInfo &info = context_->info;
*outEndSample = info.endSample - info.firstValidSample;
int loopEnd = -1;
if (info.loopEnd == 0) {
*outLoopStartSample = -1;
*outLoopEndSample = -1;
} else {
*outLoopStartSample = info.loopStart - info.firstValidSample;
*outLoopEndSample = info.loopEnd - info.firstValidSample;
}
return 0;
}
int Atrac2::ResetPlayPosition(int seekPos, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) {
*delay = false;
SceAtracIdInfo &info = context_->info;
if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && info.secondBufferByte == 0) {
return SCE_ERROR_ATRAC_SECOND_BUFFER_NEEDED;
}
seekPos += info.firstValidSample;
if ((u32)seekPos > (u32)info.endSample) {
return SCE_ERROR_ATRAC_BAD_SAMPLE;
}
int result = ResetPlayPositionInternal(seekPos, bytesWrittenFirstBuf, bytesWrittenSecondBuf);
if (result >= 0) {
int skipCount = 0;
result = SkipFrames(&skipCount);
if (skipCount) {
*delay = true;
}
}
return result;
}
u32 Atrac2::ResetPlayPositionInternal(int seekPos, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) {
AtracResetBufferInfo bufferInfo;
GetResetBufferInfoInternal(&bufferInfo, seekPos);
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;
}
SceAtracIdInfo &info = context_->info;
info.decodePos = seekPos;
info.numSkipFrames = ComputeSkipFrames(info, seekPos);
info.loopNum = 0;
info.curFileOff = ComputeFileOffset(info, seekPos);
context_->codec.err = 0x20b;
switch (info.state) {
case ATRAC_STATUS_ALL_DATA_LOADED:
return 0;
case ATRAC_STATUS_HALFWAY_BUFFER:
info.streamDataByte += bytesWrittenFirstBuf;
if (info.dataOff + info.streamDataByte >= info.fileDataEnd) {
info.state = ATRAC_STATUS_ALL_DATA_LOADED;
}
return 0;
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
info.streamDataByte = bytesWrittenFirstBuf;
info.curBuffer = 0;
info.streamOff = 0;
return 0;
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
{
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
if (info.curFileOff >= loopEndFileOffset) {
const int secondBufferSizeRounded = RoundDownToMultiple(info.secondBufferByte, info.sampleSize);
if (info.curFileOff < loopEndFileOffset + secondBufferSizeRounded) {
info.streamDataByte = ((loopEndFileOffset + secondBufferSizeRounded) - info.curFileOff) + bytesWrittenFirstBuf;
info.curBuffer = 1;
info.secondStreamOff = info.curFileOff - loopEndFileOffset;
} else {
info.streamDataByte = bytesWrittenFirstBuf;
info.curBuffer = 2;
info.streamOff = 0;
}
} else {
info.streamDataByte = bytesWrittenFirstBuf;
info.curBuffer = 0;
info.streamOff = 0;
}
return 0;
}
default:
_dbg_assert_(false);
return 0;
}
}
int Atrac2::GetBufferInfoForResetting(AtracResetBufferInfo *bufferInfo, int seekPos, bool *delay) {
const SceAtracIdInfo &info = context_->info;
if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && info.secondBufferByte == 0) {
return SCE_ERROR_ATRAC_SECOND_BUFFER_NEEDED;
}
seekPos += info.firstValidSample;
if ((u32)seekPos > (u32)info.endSample) {
return SCE_ERROR_ATRAC_BAD_SAMPLE;
}
GetResetBufferInfoInternal(bufferInfo, seekPos);
int skipCount = 0;
int retval = SkipFrames(&skipCount);
if (skipCount > 0)
*delay = true;
return retval;
}
void Atrac2::GetResetBufferInfoInternal(AtracResetBufferInfo *bufferInfo, int seekPos) const {
const SceAtracIdInfo &info = context_->info;
switch (info.state) {
case ATRAC_STATUS_NO_DATA:
case ATRAC_STATUS_ALL_DATA_LOADED:
bufferInfo->first.writePosPtr = info.buffer;
bufferInfo->first.writableBytes = 0;
bufferInfo->first.minWriteBytes = 0;
bufferInfo->first.filePos = 0;
break;
case ATRAC_STATUS_HALFWAY_BUFFER:
{
const int streamPos = info.dataOff + info.streamDataByte;
const int fileOff = info.dataOff + (seekPos / info.SamplesPerFrame() + 1) * info.sampleSize;
bufferInfo->first.writePosPtr = info.buffer + streamPos;
bufferInfo->first.writableBytes = info.fileDataEnd - streamPos;
bufferInfo->first.filePos = streamPos;
bufferInfo->first.minWriteBytes = std::max(0, fileOff - streamPos);
break;
}
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
{
const int curFileOffset = ComputeFileOffset(info, seekPos);
const int bufferEnd = RoundDownToMultiple(info.bufferByte, info.sampleSize);
bufferInfo->first.writePosPtr = info.buffer;
bufferInfo->first.writableBytes = std::min(info.fileDataEnd - curFileOffset, bufferEnd);
bufferInfo->first.minWriteBytes = (ComputeSkipFrames(info, seekPos) + 1) * info.sampleSize;
bufferInfo->first.filePos = curFileOffset;
break;
}
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
{
const int seekFileOffset = ComputeFileOffset(info, seekPos);
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd) - 1;
const int bufferEnd = RoundDownToMultiple(info.bufferByte, info.sampleSize);
const int skipBytes = (ComputeSkipFrames(info, seekPos) + 1) * info.sampleSize;
const int secondBufferEnd = RoundDownToMultiple(info.secondBufferByte, info.sampleSize);
if (seekFileOffset < loopEndFileOffset) {
const int remainingBeforeLoop = (loopEndFileOffset - seekFileOffset) + 1;
bufferInfo->first.writePosPtr = info.buffer;
bufferInfo->first.writableBytes = std::min(bufferEnd, remainingBeforeLoop);
bufferInfo->first.minWriteBytes = std::min(skipBytes, remainingBeforeLoop);
bufferInfo->first.filePos = seekFileOffset;
} else if (loopEndFileOffset + secondBufferEnd <= seekFileOffset) {
bufferInfo->first.writePosPtr = info.buffer;
bufferInfo->first.writableBytes = std::min(info.fileDataEnd - seekFileOffset, bufferEnd);
bufferInfo->first.minWriteBytes = skipBytes;
bufferInfo->first.filePos = seekFileOffset;
} else if (loopEndFileOffset + (int)info.secondBufferByte + 1 < info.fileDataEnd) {
const int endOffset = loopEndFileOffset + secondBufferEnd + 1;
bufferInfo->first.writePosPtr = info.buffer;
bufferInfo->first.writableBytes = std::min(info.fileDataEnd - endOffset, bufferEnd);
bufferInfo->first.minWriteBytes = std::max(0, seekFileOffset + skipBytes - endOffset);
bufferInfo->first.filePos = endOffset;
} else {
bufferInfo->first.writePosPtr = info.buffer;
bufferInfo->first.writableBytes = 0;
bufferInfo->first.minWriteBytes = 0;
bufferInfo->first.filePos = 0;
}
break;
}
default:
_dbg_assert_(false);
break;
}
bufferInfo->second.writePosPtr = info.buffer;
bufferInfo->second.writableBytes = 0;
bufferInfo->second.minWriteBytes = 0;
bufferInfo->second.filePos = 0;
}
int Atrac2::SetLoopNum(int loopNum) {
SceAtracIdInfo &info = context_->info;
if (info.loopEnd <= 0) {
return SCE_ERROR_ATRAC_NO_LOOP_INFORMATION;
}
info.loopNum = loopNum;
return 0;
}
int Atrac2::LoopNum() const {
return context_->info.loopNum;
}
int Atrac2::LoopStatus() const {
const SceAtracIdInfo &info = context_->info;
if (info.loopEnd == 0) {
return 0;
} else if (info.loopNum != 0) {
return 1;
} else {
return info.decodePos <= info.loopEnd ? 1 : 0;
}
}
u32 Atrac2::GetNextSamples() {
SceAtracIdInfo &info = context_->info;
const int endOfCurrentFrame = info.decodePos | info.SamplesFrameMask();
const int remainder = std::max(0, endOfCurrentFrame - info.endSample);
const int adjusted = (info.decodePos & info.SamplesFrameMask()) + remainder;
return std::max(0, info.SamplesPerFrame() - adjusted);
}
int Atrac2::GetNextDecodePosition(int *pos) const {
const SceAtracIdInfo &info = context_->info;
if (info.decodePos > info.endSample) {
return SCE_ERROR_ATRAC_ALL_DATA_DECODED;
}
if (info.fileDataEnd - info.curFileOff < info.sampleSize) {
return SCE_ERROR_ATRAC_ALL_DATA_DECODED;
}
*pos = info.decodePos - info.firstValidSample;
return 0;
}
int Atrac2::AddStreamData(u32 bytesToAdd) {
SceAtracIdInfo &info = context_->info;
if (info.state == ATRAC_STATUS_HALFWAY_BUFFER) {
const int newFileOffset = info.streamDataByte + info.dataOff + bytesToAdd;
if (newFileOffset == info.fileDataEnd) {
info.state = ATRAC_STATUS_ALL_DATA_LOADED;
} else if (newFileOffset > info.fileDataEnd) {
return SCE_ERROR_ATRAC_ADD_DATA_IS_TOO_BIG;
}
info.streamDataByte += bytesToAdd;
} else {
info.streamDataByte += bytesToAdd;
}
return 0;
}
static int ComputeLoopedStreamWritableBytes(const SceAtracIdInfo &info, const int loopStartFileOffset, const u32 loopEndFileOffset) {
const u32 writeOffset = info.curFileOff + info.streamDataByte;
if (writeOffset >= loopEndFileOffset) {
const int loopLength = loopEndFileOffset - loopStartFileOffset;
return loopLength - ((info.curFileOff + info.streamDataByte) - loopEndFileOffset) % loopLength;
} else {
return loopEndFileOffset - writeOffset;
}
}
static int IncrementAndLoop(int curOffset, int increment, int loopStart, int loopEnd) {
const int sum = curOffset + increment;
if (sum >= loopEnd) {
return loopStart + (sum - loopEnd) % (loopEnd - loopStart);
} else {
return sum;
}
}
static int WrapAroundRoundedBufferSize(int offset, int bufferSize, int addend, int grainSize) {
bufferSize = RoundDownToMultipleWithOffset(offset, bufferSize, grainSize);
const int sum = offset + addend;
if (bufferSize <= sum) {
return sum - bufferSize;
} else {
return sum;
}
}
static void ComputeStreamBufferDataInfo(const SceAtracIdInfo &info, u32 *writePtr, u32 *bytesToWrite, u32 *readFileOffset) {
const u32 streamOff = info.curBuffer != 1 ? info.streamOff : 0;
const int spaceUsed = ComputeSpaceUsed(info);
const int spaceLeftAfterStreamOff = RoundDownToMultipleWithOffset(streamOff, info.bufferByte, info.sampleSize);
const int streamPos = streamOff + spaceUsed;
int spaceLeftInBuffer;
if (streamPos >= spaceLeftAfterStreamOff) {
spaceLeftInBuffer = spaceLeftAfterStreamOff - spaceUsed;
} else {
spaceLeftInBuffer = spaceLeftAfterStreamOff - streamPos;
}
const int loopStartFileOffset = ComputeFileOffset(info, info.loopStart);
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
if (spaceLeftInBuffer < 0) {
WARN_LOG(Log::Atrac, "File corruption detected: spaceLeftInBuffer < 0: %d. Stumbling along.", spaceLeftInBuffer);
spaceLeftInBuffer = 0;
}
switch (info.state) {
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
{
*bytesToWrite = std::clamp(info.fileDataEnd - (info.curFileOff + info.streamDataByte), 0, spaceLeftInBuffer);
const int streamFileOff = info.curFileOff + info.streamDataByte;
if (streamFileOff < info.fileDataEnd) {
*readFileOffset = streamFileOff;
*writePtr = info.buffer + WrapAroundRoundedBufferSize(info.streamOff, info.bufferByte, info.streamDataByte, info.sampleSize);
} else {
*readFileOffset = 0;
*writePtr = info.buffer;
}
break;
}
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
*bytesToWrite = std::min(ComputeLoopedStreamWritableBytes(info, loopStartFileOffset, loopEndFileOffset), spaceLeftInBuffer);
*readFileOffset = IncrementAndLoop(info.curFileOff, info.streamDataByte, loopStartFileOffset, loopEndFileOffset);
*writePtr = info.buffer + WrapAroundRoundedBufferSize(info.streamOff, info.bufferByte, info.streamDataByte, info.sampleSize);
break;
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
{
if (info.decodePos <= info.loopEnd) {
*bytesToWrite = std::min(ComputeLoopedStreamWritableBytes(info, loopStartFileOffset, loopEndFileOffset), spaceLeftInBuffer);
*readFileOffset = IncrementAndLoop(info.curFileOff, info.streamDataByte, loopStartFileOffset, loopEndFileOffset);
} else {
const int streamFileOff = info.curFileOff + info.streamDataByte;
*bytesToWrite = std::clamp(info.fileDataEnd - streamFileOff, 0, spaceLeftInBuffer);
if (streamFileOff < info.fileDataEnd) {
*readFileOffset = streamFileOff;
} else {
*readFileOffset = 0;
}
}
if (info.decodePos <= info.loopEnd || info.curBuffer != 1) {
*writePtr = info.buffer + WrapAroundRoundedBufferSize(info.streamOff, info.bufferByte, info.streamDataByte, info.sampleSize);
} else {
*writePtr = info.buffer + WrapAroundRoundedBufferSize(0, info.bufferByte, spaceUsed, info.sampleSize);
}
break;
}
default:
_dbg_assert_(false);
break;
}
}
void Atrac2::GetStreamDataInfo(u32 *writePtr, u32 *bytesToWrite, u32 *readFileOffset) {
const SceAtracIdInfo &info = context_->info;
switch (info.state) {
case ATRAC_STATUS_ALL_DATA_LOADED:
*writePtr = info.buffer;
*bytesToWrite = 0;
*readFileOffset = 0;
break;
case ATRAC_STATUS_HALFWAY_BUFFER:
{
const int fileOffset = (int)info.dataOff + (int)info.streamDataByte;
const int bytesLeftInFile = (int)info.fileDataEnd - fileOffset;
*writePtr = info.buffer + fileOffset;
*bytesToWrite = bytesLeftInFile;
*readFileOffset = fileOffset;
break;
}
default:
ComputeStreamBufferDataInfo(info, writePtr, bytesToWrite, readFileOffset);
break;
}
}
u32 Atrac2::DecodeData(u8 *outbuf, u32 outbufAddr, int *SamplesNum, int *finish, int *remains) {
SceAtracIdInfo &info = context_->info;
const int tries = info.numSkipFrames + 1;
for (int i = 0; i < tries; i++) {
u32 result = DecodeInternal(outbufAddr, SamplesNum, finish);
if (result != 0) {
*SamplesNum = 0;
return result;
}
}
*remains = RemainingFrames();
return 0;
}
u32 Atrac2::DecodeInternal(u32 outbufAddr, int *SamplesNum, int *finish) {
SceAtracIdInfo &info = context_->info;
const int samplesToDecode = GetNextSamples();
const int nextFileOff = info.curFileOff + info.sampleSize;
if (nextFileOff > info.fileDataEnd || info.decodePos > info.endSample) {
*finish = 1;
return SCE_ERROR_ATRAC_ALL_DATA_DECODED;
}
DEBUG_LOG(Log::Atrac, "Decode(%08x): samplesToDecode: %d nextFileOff: %d", outbufAddr, samplesToDecode, nextFileOff);
if (AtracStatusIsStreaming(info.state) && info.streamDataByte < info.sampleSize) {
*finish = 0;
return SCE_ERROR_ATRAC_BUFFER_IS_EMPTY;
}
if (info.state == ATRAC_STATUS_HALFWAY_BUFFER && info.dataOff + info.streamDataByte < nextFileOff) {
*finish = 0;
return SCE_ERROR_ATRAC_BUFFER_IS_EMPTY;
}
if (info.state == ATRAC_STATUS_FOR_SCESAS) {
_dbg_assert_(false);
}
u32 streamOff;
u32 bufferPtr;
if (!AtracStatusIsStreaming(info.state)) {
bufferPtr = info.buffer;
streamOff = info.curFileOff;
} else {
const int bufferIndex = info.curBuffer & 1;
bufferPtr = bufferIndex == 0 ? info.buffer : info.secondBuffer;
streamOff = bufferIndex == 0 ? info.streamOff : info.secondStreamOff;
}
u32 inAddr = bufferPtr + streamOff;
int16_t *outPtr;
_dbg_assert_(samplesToDecode <= info.SamplesPerFrame());
if (samplesToDecode != info.SamplesPerFrame()) {
if (!decodeTemp_) {
decodeTemp_ = new int16_t[info.SamplesPerFrame() * outputChannels_];
}
outPtr = decodeTemp_;
} else {
outPtr = outbufAddr ? (int16_t *)Memory::GetPointer(outbufAddr) : 0;
}
context_->codec.inBuf = inAddr;
context_->codec.outBuf = outbufAddr;
if (!Memory::IsValidAddress(inAddr)) {
ERROR_LOG(Log::Atrac, "DecodeInternal: Bad inAddr %08x", inAddr);
return SCE_ERROR_ATRAC_API_FAIL;
}
int bytesConsumed = 0;
int outSamples = 0;
if (!decoder_->Decode(Memory::GetPointerUnchecked(inAddr), info.sampleSize, &bytesConsumed, outputChannels_, outPtr, &outSamples)) {
*finish = 0;
context_->codec.err = 0x20b;
return SCE_ERROR_ATRAC_API_FAIL;
} else {
context_->codec.err = 0;
}
_dbg_assert_(bytesConsumed == info.sampleSize);
info.curFileOff += info.sampleSize;
if (info.numSkipFrames == 0) {
*SamplesNum = samplesToDecode;
if (info.endSample < info.decodePos + samplesToDecode) {
*finish = info.loopNum == 0;
} else {
*finish = 0;
}
u8 *outBuf = outbufAddr ? Memory::GetPointerWrite(outbufAddr) : nullptr;
if (samplesToDecode != info.SamplesPerFrame() && samplesToDecode != 0 && outBuf) {
memcpy(outBuf, decodeTemp_, samplesToDecode * outputChannels_ * sizeof(int16_t));
}
info.decodePos += samplesToDecode;
if (info.loopEnd != 0 && info.loopNum != 0 && info.decodePos > info.loopEnd) {
info.curFileOff = ComputeFileOffset(info, info.loopStart);
info.numSkipFrames = ComputeSkipFrames(info, info.loopStart);
info.decodePos = info.loopStart;
if (info.loopNum > 0) {
info.loopNum--;
}
}
} else {
info.numSkipFrames--;
}
if (AtracStatusIsStreaming(info.state)) {
info.streamDataByte -= info.sampleSize;
if (info.curBuffer == 1) {
int nextStreamOff = info.secondStreamOff + info.sampleSize;
if ((int)info.secondBufferByte < nextStreamOff + info.sampleSize) {
info.streamOff = 0;
info.secondStreamOff = 0;
info.curBuffer = 2;
} else {
info.secondStreamOff = nextStreamOff;
}
} else {
const int nextStreamOff = info.streamOff + info.sampleSize;
if (nextStreamOff + info.sampleSize > (int)info.bufferByte) {
info.streamOff = 0;
} else {
info.streamOff = nextStreamOff;
}
if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && info.curBuffer == 0 &&
(info.loopEnd == 0 || (info.loopNum == 0 && info.loopEnd < info.decodePos))) {
if (info.curFileOff >= ComputeLoopEndFileOffset(info, info.loopEnd)) {
info.curBuffer = 1;
info.streamDataByte = info.secondBufferByte;
info.secondStreamOff = 0;
memcpy(Memory::GetPointerWrite(info.buffer),
Memory::GetPointer(info.secondBuffer + (info.secondBufferByte - info.secondBufferByte % info.sampleSize)),
info.secondBufferByte % info.sampleSize);
}
}
}
}
return 0;
}
int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 bufferSize, u32 fileSize, int outputChannels, bool isAA3) {
TrackInfo trackInfo{};
if (bufferAddr) {
if (!isAA3) {
int retval = ParseWaveAT3(Memory::GetPointerRange(bufferAddr, bufferSize), readSize, &trackInfo);
if (retval < 0) {
ERROR_LOG(Log::Atrac, "Atrac2::SetData: ParseWaveAT3 failed with %08x", retval);
return retval;
}
} else {
int retval = ParseAA3(Memory::GetPointerRange(bufferAddr, bufferSize), readSize, fileSize, &trackInfo);
if (retval < 0) {
ERROR_LOG(Log::Atrac, "Atrac2::SetData: ParseAA3 failed with %08x", retval);
return retval;
}
}
}
int retval = InitContextFromTrackInfo(context_, &trackInfo, bufferAddr, readSize, bufferSize);
if (retval < 0) {
ERROR_LOG(Log::Atrac, "Atrac2::SetData: InitContextFromTrackInfo failed with %08x", retval);
return retval;
}
SceAtracIdInfo &info = context_->info;
CreateDecoder(info.codec, info.sampleSize, info.numChan);
outputChannels_ = outputChannels;
INFO_LOG(Log::Atrac,
"Atrac: sampleSize: %d buffer: %08x bufferByte: %d firstValidSample: %d\n"
"endSample: %d loopStart: %d loopEnd: %d\n"
"dataOff: %d curFileOff: %d streamOff: %d streamDataByte: %d\n"
"fileDataEnd: %d decodePos: %d numSkipFrames: %d",
info.sampleSize, info.buffer, info.bufferByte, info.firstValidSample,
info.endSample, info.loopStart, info.loopEnd,
info.dataOff, info.curFileOff, info.streamOff, info.streamDataByte,
info.fileDataEnd, info.decodePos, info.numSkipFrames
);
int skipCount = 0;
retval = SkipFrames(&skipCount);
if (retval == SCE_ERROR_ATRAC_API_FAIL) {
ERROR_LOG(Log::Atrac, "Bad frame during initial skip");
} else if (retval != 0) {
ERROR_LOG(Log::Atrac, "SkipFrames during InitContext returned an error: %08x", retval);
}
WrapLastPacket();
return retval;
}
void Atrac2::WrapLastPacket() {
SceAtracIdInfo &info = context_->info;
if (!AtracStatusIsStreaming(info.state)) {
return;
}
int distanceToEnd = RoundDownToMultiple(info.bufferByte - info.streamOff, info.sampleSize);
if (info.streamDataByte < distanceToEnd) {
Memory::Memset(info.buffer, 0, 128);
} else {
const int copyStart = info.streamOff + distanceToEnd;
const int copyLen = info.bufferByte - copyStart;
DEBUG_LOG(Log::Atrac, "Packets didn't fit evenly. Last packet got split into %d/%d (sum=%d). Copying to start of buffer.",
copyLen, info.sampleSize - copyLen, info.sampleSize);
Memory::Memcpy(info.buffer, info.buffer + copyStart, copyLen);
}
}
u32 Atrac2::SkipFrames(int *skipCount) {
SceAtracIdInfo &info = context_->info;
*skipCount = 0;
int finishIgnored;
while (true) {
if (info.numSkipFrames == 0) {
return 0;
}
u32 retval = DecodeInternal(0, 0, &finishIgnored);
if (retval != 0) {
if (retval == SCE_ERROR_ATRAC_API_FAIL) {
WARN_LOG(Log::Atrac, "Failed during skip-frame, ignoring: %08x", retval);
(*skipCount)++;
}
return retval;
} else {
DEBUG_LOG(Log::Atrac, "Frame correctly decoded during skip. numSkipFrames == %d", info.numSkipFrames);
}
(*skipCount)++;
}
return 0;
}
int Atrac2::GetSecondBufferInfo(u32 *fileOffset, u32 *readSize) const {
const SceAtracIdInfo &info = context_->info;
if (info.state != ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
*fileOffset = 0;
*readSize = 0;
return SCE_ERROR_ATRAC_SECOND_BUFFER_NOT_NEEDED;
}
const int loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
*fileOffset = loopEndFileOffset;
*readSize = info.fileDataEnd - loopEndFileOffset;
return 0;
}
int Atrac2::SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) {
SceAtracIdInfo &info = context_->info;
u32 loopEndFileOffset = ComputeLoopEndFileOffset(info, info.loopEnd);
if ((info.sampleSize * 3 <= (int)secondBufferSize ||
(info.fileDataEnd - loopEndFileOffset) <= (int)secondBufferSize)) {
if (info.state == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
info.secondBuffer = secondBuffer;
info.secondBufferByte = secondBufferSize;
info.secondStreamOff = 0;
return 0;
} else {
return SCE_ERROR_ATRAC_SECOND_BUFFER_NOT_NEEDED;
}
}
return SCE_ERROR_ATRAC_SIZE_TOO_SMALL;
}
u32 Atrac2::GetInternalCodecError() const {
if (context_.IsValid()) {
return context_->codec.err;
} else {
return 0;
}
}
int Atrac2::Bitrate() const {
const SceAtracIdInfo &info = context_->info;
return info.BitRate();
}
void Atrac2::InitLowLevel(const Atrac3LowLevelParams ¶ms, int codecType) {
SceAtracIdInfo &info = context_->info;
info.codec = codecType;
info.numChan = params.encodedChannels;
outputChannels_ = params.outputChannels;
info.sampleSize = params.bytesPerFrame;
info.dataOff = 0;
info.decodePos = 0;
info.state = ATRAC_STATUS_LOW_LEVEL;
CreateDecoder(codecType, info.sampleSize, info.numChan);
}
int Atrac2::DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData, int *bytesWritten) {
SceAtracIdInfo &info = context_->info;
const int channels = outputChannels_;
int outSamples = 0;
bool success = decoder_->Decode(srcData, info.sampleSize, bytesConsumed, channels, dstData, &outSamples);
if (!success) {
ERROR_LOG(Log::Atrac, "Low level decoding failed: sampleSize: %d bytesConsumed: %d", info.sampleSize, *bytesConsumed);
}
*bytesWritten = outSamples * channels * sizeof(int16_t);
return 0;
}
void Atrac2::CheckForSas() {
SceAtracIdInfo &info = context_->info;
if (info.numChan != 1) {
WARN_LOG(Log::Atrac, "Caller forgot to set channels to 1");
}
if (info.state != 0x10) {
WARN_LOG(Log::Atrac, "Caller forgot to set state to 0x10");
}
sas_.isStreaming = info.fileDataEnd > (s32)info.bufferByte;
if (sas_.isStreaming) {
INFO_LOG(Log::Atrac, "SasAtrac stream mode");
} else {
INFO_LOG(Log::Atrac, "SasAtrac non-streaming mode");
}
}
int Atrac2::EnqueueForSas(u32 address, u32 ptr) {
SceAtracIdInfo &info = context_->info;
if (info.secondBuffer != 0xFFFFFFFF) {
return SCE_SAS_ERROR_ATRAC3_ALREADY_QUEUED;
}
if (address == 0 && ptr == 0) {
WARN_LOG(Log::Atrac, "Caller tries to send us a zero buffer. Something went wrong.");
}
DEBUG_LOG(Log::Atrac, "EnqueueForSas: Second buffer updated to %08x, sz: %08x", address, ptr);
info.secondBuffer = address;
info.secondBufferByte = ptr;
return 0;
}
void Atrac2::DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) {
SceAtracIdInfo &info = context_->info;
*bytesWritten = 0;
if (info.buffer) {
sas_.curBuffer = 0;
sas_.bufPtr[0] = info.buffer;
sas_.bufSize[0] = info.bufferByte - info.streamOff;
sas_.streamOffset = 0;
sas_.fileOffset = info.bufferByte;
info.buffer = 0;
}
u8 assembly[1000];
if (sas_.streamOffset + (int)info.sampleSize <= (int)sas_.bufSize[sas_.curBuffer]) {
const u8 *srcData = Memory::GetPointer(sas_.bufPtr[sas_.curBuffer] + sas_.streamOffset);
int bytesConsumed = 0;
bool decodeResult = decoder_->Decode(srcData, info.sampleSize, &bytesConsumed, 1, dstData, bytesWritten);
if (!decodeResult) {
ERROR_LOG(Log::Atrac, "SAS failed to decode regular packet");
}
sas_.streamOffset += bytesConsumed;
} else if (sas_.isStreaming) {
DEBUG_LOG(Log::Atrac, "Streaming atrac through sas, and hit the end of buffer %d", sas_.curBuffer);
int part1Size = sas_.bufSize[sas_.curBuffer] - sas_.streamOffset;
int part2Size = info.sampleSize - part1Size;
_dbg_assert_(part1Size >= 0);
if (part1Size >= 0) {
Memory::Memcpy(assembly, sas_.bufPtr[sas_.curBuffer] + sas_.streamOffset, part1Size);
}
if (sas_.fileOffset >= info.fileDataEnd) {
DEBUG_LOG(Log::Atrac, "Streaming and hit the file end.");
*bytesWritten = 0;
*finish = 1;
return;
}
if (info.secondBuffer == sas_.bufPtr[sas_.curBuffer]) {
ERROR_LOG(Log::Atrac, "Can't enqueue the same buffer twice in a row!");
*bytesWritten = 0;
*finish = 1;
return;
}
if ((int)info.secondBuffer < 0) {
ERROR_LOG(Log::Atrac, "AtracSas streaming ran out of data, no secondbuffer pending");
*bytesWritten = 0;
*finish = 1;
return;
}
sas_.curBuffer ^= 1;
sas_.bufPtr[sas_.curBuffer] = info.secondBuffer;
sas_.bufSize[sas_.curBuffer] = info.secondBufferByte;
sas_.fileOffset += info.secondBufferByte;
sas_.streamOffset = part2Size;
if (sas_.fileOffset >= info.fileDataEnd) {
info.secondBuffer = 0;
DEBUG_LOG(Log::Atrac, "%08x >= %08x: Reached the end.", sas_.fileOffset, info.fileDataEnd);
} else {
info.secondBuffer = 0xFFFFFFFF;
}
DEBUG_LOG(Log::Atrac, "Switching over to buffer %d, updating buffer to %08x, sz: %08x. %s", sas_.curBuffer, info.secondBuffer, info.secondBufferByte, info.secondBuffer == 0xFFFFFFFF ? "Signalling for more data." : "");
Memory::Memcpy(assembly + part1Size, sas_.bufPtr[sas_.curBuffer], part2Size);
const u8 *srcData = assembly;
int bytesConsumed = 0;
bool decodeResult = decoder_->Decode(srcData, info.sampleSize, &bytesConsumed, 1, dstData, bytesWritten);
if (!decodeResult) {
ERROR_LOG(Log::Atrac, "SAS failed to decode assembled packet");
}
}
}