mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Port over some utility functions, fix bugs.
This commit is contained in:
parent
9c02eab137
commit
7a4d175e06
6 changed files with 214 additions and 30 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue