Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Core/HLE/ErrorCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

enum PSPErrorCode : u32 {
SCE_KERNEL_ERROR_OK = 0,
SCE_KERNEL_ERROR_BAD_ARGUMENT = 0x80000004, // Maybe
SCE_KERNEL_ERROR_ALREADY = 0x80000020,
SCE_KERNEL_ERROR_BUSY = 0x80000021,
SCE_KERNEL_ERROR_OUT_OF_MEMORY = 0x80000022,
Expand Down Expand Up @@ -492,4 +493,6 @@ enum PSPErrorCode : u32 {
SCE_SAS_ERROR_ATRAC3_NOT_SET = 0x80420041,
SCE_SAS_ERROR_ATRAC3_ALREADY_QUEUED = 0x80420042,
SCE_SAS_ERROR_NOT_INIT = 0x80420100,

SCE_AVCODEC_ERROR_INVALID_DATA = 0x807f00fd,
};
227 changes: 171 additions & 56 deletions Core/HLE/sceAudiocodec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceAudiocodec.h"
#include "Core/HLE/ErrorCodes.h"
#include "Core/MemMap.h"
#include "Core/Reporting.h"
#include "Core/HW/SimpleAudioDec.h"
Expand All @@ -35,6 +36,50 @@ static bool oldStateLoaded = false;

static_assert(sizeof(SceAudiocodecCodec) == 128);

// Atrac3+ (0x1000) frame sizes, and control bytes
//
// Bitrate Frame Size Byte 1 Byte 2 Channels
// -----------------------------------------------------
// 48kbps 0x118 0x24 0x22 1? // This hits "Frame data doesn't match channel configuration".
// 64kbps 0x178
// 96kbps? 0x230 0x28 0x45 2
// 128kbps 0x2E8 0x28 0x5c 2
//
// Seems like maybe the frame size is equal to "Byte 2" * 8 + 8
//
// Known byte values.

void CalculateInputBytesAndChannels(const SceAudiocodecCodec *ctx, int codec, int *inputBytes, int *channels) {
*inputBytes = 0;
*channels = 2;
switch (codec) {
case PSP_CODEC_AT3PLUS:
{
int size = ctx->unk41 * 8 + 8;
// No idea if this is accurate, this is just a guess...
if (ctx->unk40 & 8) {
*channels = 2;
} else {
*channels = 1;
}
switch (size) {
case 0x118:
case 0x178:
case 0x230:
case 0x2E8:
// These have been seen before, let's return it.
*inputBytes = size;
return;
default:
break;
}
}
default:
// Unsupported codec, ignore.
break;
}
}

// find the audio decoder for corresponding ctxPtr in audioList
static AudioDecoder *findDecoder(u32 ctxPtr) {
auto it = g_audioDecoderContexts.find(ctxPtr);
Expand Down Expand Up @@ -74,26 +119,38 @@ void __AudioCodecShutdown() {
// TODO: Actually support mono output.
static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
PSPAudioType audioType = (PSPAudioType)codec;
if (IsValidCodec(audioType)) {
// Initialize the codec memory.
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr);
ctx->unk_init = 0x5100601; // Firmware version indicator?
ctx->err = 0;
// The rest of the initialization is done by the driver.

// Create audio decoder for given audio codec and push it into AudioList
if (removeDecoder(ctxPtr)) {
WARN_LOG_REPORT(Log::HLE, "sceAudiocodecInit(%08x, %d): replacing existing context", ctxPtr, codec);
}
AudioDecoder *decoder = CreateAudioDecoder(audioType);
if (!IsValidCodec(audioType)) {
return hleLogError(Log::ME, SCE_KERNEL_ERROR_OUT_OF_RANGE, "Invalid codec");
}

// Initialize the codec memory.
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr);
ctx->unk_init = 0x5100601; // Firmware version indicator?
ctx->err = 0;

if (codec == 0x1002) {
ctx->mp3_9999 = 9999;
}

// Create audio decoder for given audio codec and push it into AudioList
if (removeDecoder(ctxPtr)) {
WARN_LOG_REPORT(Log::HLE, "sceAudiocodecInit(%08x, %d): replacing existing context", ctxPtr, codec);
}

int inFrameBytes;
int channels;
CalculateInputBytesAndChannels(ctx, codec, &inFrameBytes, &channels);

if (inFrameBytes) {
INFO_LOG(Log::ME, "sceAudioDecoder: Creating codec with %04x frame size and %d channels, codec %04x", inFrameBytes, channels, codec);
AudioDecoder *decoder = CreateAudioDecoder(audioType, 44100, channels, inFrameBytes);
decoder->SetCtxPtr(ctxPtr);
g_audioDecoderContexts[ctxPtr] = decoder;
INFO_LOG(Log::ME, "sceAudiocodecInit(%08x, %i (%s))", ctxPtr, codec, GetCodecName(audioType));
DEBUG_LOG(Log::ME, "Number of playing sceAudioCodec audios : %d", (int)g_audioDecoderContexts.size());
return 0;
} else {
ERROR_LOG(Log::ME, "sceAudioDecoder: Unsupported codec %08x", codec);
g_audioDecoderContexts[ctxPtr] = nullptr;
}
ERROR_LOG_REPORT(Log::ME, "sceAudiocodecInit(%08x, %i (%s)): Unknown audio codec %i", ctxPtr, codec, GetCodecName(audioType), codec);
return 0;
return hleLogDebug(Log::ME, 0);
}

static int sceAudiocodecInit(u32 ctxPtr, int codec) {
Expand All @@ -106,36 +163,53 @@ static int sceAudiocodecInitMono(u32 ctxPtr, int codec) {

static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
PSPAudioType audioType = (PSPAudioType)codec;
if (!ctxPtr){
ERROR_LOG_REPORT(Log::ME, "sceAudiocodecDecode(%08x, %i (%s)) got NULL pointer", ctxPtr, codec, GetCodecName(audioType));
if (!ctxPtr) {
ERROR_LOG(Log::ME, "sceAudiocodecDecode(%08x, %i (%s)) got NULL pointer", ctxPtr, codec, GetCodecName(audioType));
return -1;
}

if (IsValidCodec(audioType)){
// find a decoder in audioList
auto decoder = findDecoder(ctxPtr);
if (!IsValidCodec(audioType)) {
return hleLogError(Log::ME, 0, "UNIMPL sceAudiocodecDecode(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec));
}

if (!decoder && oldStateLoaded) {
// We must have loaded an old state that did not have sceAudiocodec information.
// Fake it by creating the desired context.
decoder = CreateAudioDecoder(audioType);
decoder->SetCtxPtr(ctxPtr);
g_audioDecoderContexts[ctxPtr] = decoder;
}
// TODO: Should check that codec corresponds to the currently used codec in the context, I guess..

auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.
int inFrameBytes;
int channels;
CalculateInputBytesAndChannels(ctx, codec, &inFrameBytes, &channels);

// find a decoder in audioList
auto decoder = findDecoder(ctxPtr);

if (!decoder && oldStateLoaded) {
// We must have loaded an old state that did not have sceAudiocodec information.
// Fake it by creating the desired context.
decoder = CreateAudioDecoder(audioType, 44100, channels, inFrameBytes);
decoder->SetCtxPtr(ctxPtr);
g_audioDecoderContexts[ctxPtr] = decoder;
}

if (decoder) {
// Use SimpleAudioDec to decode audio
// Decode audio
int inDataConsumed = 0;
int outSamples = 0;

if (decoder != NULL) {
// Use SimpleAudioDec to decode audio
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.
// Decode audio
int inDataConsumed = 0;
int outSamples = 0;
decoder->Decode(Memory::GetPointer(ctx->inBuf), ctx->srcFrameSize, &inDataConsumed, 2, (int16_t *)Memory::GetPointerWrite(ctx->outBuf), &outSamples);
INFO_LOG(Log::ME, "decoder. in: %08x out: %08x unk40: %d unk41: %d", ctx->inBuf, ctx->outBuf, ctx->unk40, ctx->unk41);

int16_t *outBuf = (int16_t *)Memory::GetPointerWrite(ctx->outBuf);

bool result = decoder->Decode(Memory::GetPointer(ctx->inBuf), inFrameBytes, &inDataConsumed, 2, outBuf, &outSamples);
if (!result) {
ctx->err = 0x20b;
ERROR_LOG(Log::ME, "AudioCodec decode failed. Setting error to %08x", ctx->err);
}
DEBUG_LOG(Log::ME, "sceAudiocodecDec(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec));
return 0;

ctx->srcBytesRead = inDataConsumed;
ctx->dstSamplesWritten = outSamples;
}
ERROR_LOG_REPORT(Log::ME, "UNIMPL sceAudiocodecDecode(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec));
return 0;
return hleLogInfo(Log::ME, 0, "codec %s", GetCodecName(codec));
}

static int sceAudiocodecGetInfo(u32 ctxPtr, int codec) {
Expand All @@ -144,33 +218,74 @@ static int sceAudiocodecGetInfo(u32 ctxPtr, int codec) {
}

static int sceAudiocodecCheckNeedMem(u32 ctxPtr, int codec) {
WARN_LOG(Log::ME, "UNIMPL sceAudiocodecCheckNeedMem(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec));
return 0;
if (codec < 0x1000 || codec >= 0x1006) {
return hleLogError(Log::ME, 0x80000004, "invalid codec");
}

if (!Memory::IsValidRange(ctxPtr, sizeof(SceAudiocodecCodec))) {
return hleLogError(Log::ME, 0, "Bad address");
}

// Check for expected values.
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.

switch (codec) {
case 0x1000:
ctx->neededMem = 0x7bc0;
if (ctx->unk40 != 0x28 || ctx->unk41 != 0x5c) {
ctx->err = 0x20f;
return hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "Bad format values: %02x %02x", ctx->unk40, ctx->unk41);
}
break;
case 0x1001:
ctx->neededMem = 0x3de0;
break;
case 0x1003:
// Kosmodrones uses sceAudiocodec directly (no intermediate library).
INFO_LOG(Log::ME, "CheckNeedMem for codec %04x: format %02x %02x", ctx->unk40, ctx->unk41);
break;
}

ctx->err = 0;
ctx->unk_init = 0x5100601;

return hleLogWarning(Log::ME, 0, "%s", GetCodecName(codec));
}

static int sceAudiocodecGetEDRAM(u32 ctxPtr, int codec) {
WARN_LOG(Log::ME, "UNIMPL sceAudiocodecGetEDRAM(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec));
return 0;
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.
// TODO: Set this a bit more dynamically. No idea what the allocation algorithm is...
ctx->allocMem = 0x0018ea90;
ctx->edramAddr = (ctx->allocMem + 0x3f) & ~0x3f; // round up to 64 bytes.
return hleLogInfo(Log::ME, 0, "edram address set to %08x", ctx->edramAddr);
}

static int sceAudiocodecReleaseEDRAM(u32 ctxPtr, int id) {
if (removeDecoder(ctxPtr)){
INFO_LOG(Log::ME, "sceAudiocodecReleaseEDRAM(%08x, %i)", ctxPtr, id);
return 0;
return hleLogInfo(Log::ME, 0);
}
WARN_LOG(Log::ME, "UNIMPL sceAudiocodecReleaseEDRAM(%08x, %i)", ctxPtr, id);
return 0;
return hleLogWarning(Log::ME, 0, "failed to remove decoder");
}

static int sceAudiocodecGetOutputBytes(u32 ctxPtr, int codec) {
switch (codec) {
case 0x1000: return hleLogInfo(Log::ME, 0x2000); // Atrac3+
case 0x1001: return hleLogInfo(Log::ME, 0x1000); // Atrac3
default:
return hleLogWarning(Log::ME, 0, "Block size query not implemented for codec %04x", codec);
}
return hleLogInfo(Log::ME, 0);
}

const HLEFunction sceAudiocodec[] = {
{0X70A703F8, &WrapI_UI<sceAudiocodecDecode>, "sceAudiocodecDecode", 'i', "xi"},
{0X5B37EB1D, &WrapI_UI<sceAudiocodecInit>, "sceAudiocodecInit", 'i', "xi"},
{0X8ACA11D5, &WrapI_UI<sceAudiocodecGetInfo>, "sceAudiocodecGetInfo", 'i', "xi"},
{0X3A20A200, &WrapI_UI<sceAudiocodecGetEDRAM>, "sceAudiocodecGetEDRAM", 'i', "xi"},
{0X29681260, &WrapI_UI<sceAudiocodecReleaseEDRAM>, "sceAudiocodecReleaseEDRAM", 'i', "xi"},
{0X9D3F790C, &WrapI_UI<sceAudiocodecCheckNeedMem>, "sceAudiocodecCheckNeedMem", 'i', "xi"},
{0X59176A0F, nullptr, "sceAudiocodec_59176A0F_GetBlockSizeMaybe", 'i', "xxx" }, // params are context, codec, outptr
{0X3DD7EE1A, &WrapI_UI<sceAudiocodecInitMono>, "sceAudiocodecInitMono", 'i', "xi"}, // Used by sceAtrac for MOut* functions.
{0X70A703F8, &WrapI_UI<sceAudiocodecDecode>, "sceAudiocodecDecode", 'i', "xi"},
{0X5B37EB1D, &WrapI_UI<sceAudiocodecInit>, "sceAudiocodecInit", 'i', "xi"},
{0X8ACA11D5, &WrapI_UI<sceAudiocodecGetInfo>, "sceAudiocodecGetInfo", 'i', "xi"},
{0X3A20A200, &WrapI_UI<sceAudiocodecGetEDRAM>, "sceAudiocodecGetEDRAM", 'i', "xi"},
{0X29681260, &WrapI_UI<sceAudiocodecReleaseEDRAM>, "sceAudiocodecReleaseEDRAM", 'i', "xi"},
{0X9D3F790C, &WrapI_UI<sceAudiocodecCheckNeedMem>, "sceAudiocodecCheckNeedMem", 'i', "xi"},
{0X59176A0F, &WrapI_UI<sceAudiocodecGetOutputBytes>, "sceAudiocodecGetOutputBytes", 'i', "xxx" }, // params are context, codec, outptr
{0X3DD7EE1A, &WrapI_UI<sceAudiocodecInitMono>, "sceAudiocodecInitMono", 'i', "xi"}, // Used by sceAtrac for MOut* functions.
};

void Register_sceAudiocodec() {
Expand Down
47 changes: 36 additions & 11 deletions Core/HLE/sceAudiocodec.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@

class PointerWrap;

// audioType
enum PSPAudioType {
PSP_CODEC_AT3PLUS = 0x00001000,
PSP_CODEC_AT3 = 0x00001001,
PSP_CODEC_MP3 = 0x00001002,
PSP_CODEC_AAC = 0x00001003,
PSP_CODEC_WMA = 0x00001005,
};

struct SceAudiocodecCodec {
s32 unk_init;
s32 unk4;
Expand All @@ -29,21 +38,37 @@ struct SceAudiocodecCodec {
s32 neededMem; // 10 // 0x102400 for Atrac3+
s32 inited; // 14
u32 inBuf; // 18 // Before decoding, set this to the start of the raw frame.
s32 srcFrameSize; // 1c
s32 srcBytesRead; // 1c
u32 outBuf; // 20 // This is where the decoded data is written.
s32 dstBytesWritten; // 24
s8 unk40; // 28 format or looping related // Probably, from here on out is a union with different fields for different codecs.
s8 unk41; // 29 format or looping related
s16 unk42; // 2a
s8 unk44;
s8 unk45;
s8 unk46;
s8 unk47;
s32 dstSamplesWritten; // 24
// Probably, from here on out is a union with different fields for different codecs.
union { // offset 40 / 0x28
struct {
s8 unk40; // 28 format or looping related
s8 unk41; // 29 format or looping related
s8 unk42;
s8 unk43;
};
u32 formatOutSamples;
};
union { // offset 44 / 0x2C
struct {
u8 unk44;
s8 unk45;
s8 unk46;
s8 unk47;
};
struct {
s16 unk44_16;
s16 unk46_16;
};
u32 unk44_32;
};
s32 unk48; // 30 Atrac3 (non-+) related. Zero with Atrac3+.
s32 unk52; // 34
s32 unk56;
s32 mp3_9999; // 38 // unk56
s32 unk60;
s32 unk64;
s32 unk64; // Atrac3+ size related
s32 unk68;
s32 unk72;
s32 unk76;
Expand Down
3 changes: 3 additions & 0 deletions Core/HLE/sceKernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,7 @@ void Register_ThreadManForKernel()
const char *KernelErrorToString(u32 err) {
switch (err) {
case 0x00000000: return "ERROR_OK";
case SCE_KERNEL_ERROR_BAD_ARGUMENT: "BAD_ARGUMENT";
case 0x80000020: return "ALREADY";
case 0x80000021: return "BUSY";
case 0x80000022: return "OUT_OF_MEMORY";
Expand Down Expand Up @@ -1489,6 +1490,8 @@ const char *KernelErrorToString(u32 err) {
case SCE_SAS_ERROR_ATRAC3_NOT_SET: return "SCE_SAS_ERROR_ATRAC3_NOT_SET";
case SCE_SAS_ERROR_NOT_INIT: return "SCE_SAS_ERROR_NOT_INIT";

case SCE_AVCODEC_ERROR_INVALID_DATA: return "SCE_AVCODEC_ERROR_INVALID_DATA";

default:
return nullptr;
}
Expand Down
9 changes: 7 additions & 2 deletions Core/HLE/sceKernelMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,13 @@ void __KernelMemoryInit()
kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), false);
userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), false);
volatileMemory.Init(PSP_GetVolatileMemoryStart(), PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart(), false);
Memory::Memset(PSP_GetKernelMemoryBase(), 0, PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase());
NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetKernelMemoryBase(), "MemInit");

Memory::Memset(PSP_GetKernelMemoryBase(), 0, PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase());
NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), "MemInitK");

Memory::Memset(PSP_GetUserMemoryBase(), 0, PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase());
NotifyMemInfo(MemBlockFlags::WRITE, PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), "MemInitU");

INFO_LOG(Log::sceKernel, "Kernel and user memory pools initialized");

vplWaitTimer = CoreTiming::RegisterEvent("VplTimeout", __KernelVplTimeout);
Expand Down
Loading
Loading