diff --git a/Core/HLE/ErrorCodes.h b/Core/HLE/ErrorCodes.h index 62a629d087..a443ca8b81 100644 --- a/Core/HLE/ErrorCodes.h +++ b/Core/HLE/ErrorCodes.h @@ -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, @@ -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, }; diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index f210c53f35..78e0bd2d66 100644 --- a/Core/HLE/sceAudiocodec.cpp +++ b/Core/HLE/sceAudiocodec.cpp @@ -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" @@ -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); @@ -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::Create(ctxPtr); - ctx->unk_init = 0x5100601; // Firmware version indicator? - ctx->err = 0; - // The rest of the initialization is done by the driver. + if (!IsValidCodec(audioType)) { + return hleLogError(Log::ME, SCE_KERNEL_ERROR_OUT_OF_RANGE, "Invalid codec"); + } - // 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); + // Initialize the codec memory. + auto ctx = PSPPointer::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) { @@ -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 (!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; - } - - if (decoder != NULL) { - // Use SimpleAudioDec to decode audio - auto ctx = PSPPointer::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); - } - DEBUG_LOG(Log::ME, "sceAudiocodecDec(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); - return 0; + if (!IsValidCodec(audioType)) { + return hleLogError(Log::ME, 0, "UNIMPL sceAudiocodecDecode(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); } - ERROR_LOG_REPORT(Log::ME, "UNIMPL sceAudiocodecDecode(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); - return 0; + + // TODO: Should check that codec corresponds to the currently used codec in the context, I guess.. + + auto ctx = PSPPointer::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; + + 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); + } + + ctx->srcBytesRead = inDataConsumed; + ctx->dstSamplesWritten = outSamples; + } + return hleLogInfo(Log::ME, 0, "codec %s", GetCodecName(codec)); } static int sceAudiocodecGetInfo(u32 ctxPtr, int codec) { @@ -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::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::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", 'i', "xi"}, - {0X5B37EB1D, &WrapI_UI, "sceAudiocodecInit", 'i', "xi"}, - {0X8ACA11D5, &WrapI_UI, "sceAudiocodecGetInfo", 'i', "xi"}, - {0X3A20A200, &WrapI_UI, "sceAudiocodecGetEDRAM", 'i', "xi"}, - {0X29681260, &WrapI_UI, "sceAudiocodecReleaseEDRAM", 'i', "xi"}, - {0X9D3F790C, &WrapI_UI, "sceAudiocodecCheckNeedMem", 'i', "xi"}, - {0X59176A0F, nullptr, "sceAudiocodec_59176A0F_GetBlockSizeMaybe", 'i', "xxx" }, // params are context, codec, outptr - {0X3DD7EE1A, &WrapI_UI, "sceAudiocodecInitMono", 'i', "xi"}, // Used by sceAtrac for MOut* functions. + {0X70A703F8, &WrapI_UI, "sceAudiocodecDecode", 'i', "xi"}, + {0X5B37EB1D, &WrapI_UI, "sceAudiocodecInit", 'i', "xi"}, + {0X8ACA11D5, &WrapI_UI, "sceAudiocodecGetInfo", 'i', "xi"}, + {0X3A20A200, &WrapI_UI, "sceAudiocodecGetEDRAM", 'i', "xi"}, + {0X29681260, &WrapI_UI, "sceAudiocodecReleaseEDRAM", 'i', "xi"}, + {0X9D3F790C, &WrapI_UI, "sceAudiocodecCheckNeedMem", 'i', "xi"}, + {0X59176A0F, &WrapI_UI, "sceAudiocodecGetOutputBytes", 'i', "xxx" }, // params are context, codec, outptr + {0X3DD7EE1A, &WrapI_UI, "sceAudiocodecInitMono", 'i', "xi"}, // Used by sceAtrac for MOut* functions. }; void Register_sceAudiocodec() { diff --git a/Core/HLE/sceAudiocodec.h b/Core/HLE/sceAudiocodec.h index fcbac179b4..6162ddad0e 100644 --- a/Core/HLE/sceAudiocodec.h +++ b/Core/HLE/sceAudiocodec.h @@ -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; @@ -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; diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 44c83ecdb5..8b0f8ae2d5 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -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"; @@ -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; } diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 2b34498111..fc8a6b9b3d 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -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); diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index cd17d8829c..1e39569043 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -91,7 +91,6 @@ static const u32 ERROR_MP3_BAD_SAMPLE_RATE = 0x80671302; static const u32 ERROR_MP3_BAD_RESET_FRAME = 0x80671501; static const u32 ERROR_MP3_BAD_ADDR = 0x80671002; static const u32 ERROR_MP3_BAD_SIZE = 0x80671003; -static const u32 ERROR_AVCODEC_INVALID_DATA = 0x807f00fd; static const int AU_BUF_MIN_SIZE = 8192; static const int PCM_BUF_MIN_SIZE = 9216; static const size_t MP3_MAX_HANDLES = 2; @@ -448,7 +447,7 @@ static int sceMp3Init(u32 mp3) { // If we have an ID3 tag, we'll get past it based on frame sync. Don't modify startPos. int header = 0; if (FindMp3Header(ctx, header, 1440) < 0) - return hleDelayResult(hleLogWarning(Log::ME, ERROR_AVCODEC_INVALID_DATA, "no header found"), "mp3 init", PARSE_DELAY_MS); + return hleDelayResult(hleLogWarning(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "no header found"), "mp3 init", PARSE_DELAY_MS); // Parse the Mp3 header int layerBits = (header >> 17) & 0x3; @@ -464,10 +463,10 @@ static int sceMp3Init(u32 mp3) { WARN_LOG_REPORT(Log::ME, "sceMp3Init: invalid data: not layer 3"); } if (bitrate == 0 || bitrate == -1) { - return hleDelayResult(hleReportError(Log::ME, ERROR_AVCODEC_INVALID_DATA, "invalid bitrate v%d l%d rate %04x", versionBits, layerBits, (header >> 12) & 0xF), "mp3 init", PARSE_DELAY_MS); + return hleDelayResult(hleReportError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "invalid bitrate v%d l%d rate %04x", versionBits, layerBits, (header >> 12) & 0xF), "mp3 init", PARSE_DELAY_MS); } if (samplerate == -1) { - return hleDelayResult(hleReportError(Log::ME, ERROR_AVCODEC_INVALID_DATA, "invalid sample rate v%d l%d rate %02x", versionBits, layerBits, (header >> 10) & 0x3), "mp3 init", PARSE_DELAY_MS); + return hleDelayResult(hleReportError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "invalid sample rate v%d l%d rate %02x", versionBits, layerBits, (header >> 10) & 0x3), "mp3 init", PARSE_DELAY_MS); } // Before we allow init, newer SDK versions next require at least 156 bytes. diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index 108e2e6391..00a0e20650 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -83,6 +83,13 @@ static void NotifyLoadStatusAvcodec(int state, u32 loadAddr, u32 totalSize) { static void NotifyLoadStatusAtrac(int state, u32 loadAddr, u32 totalSize) { if (state == 1) { + // If HLE of sceAtrac is disabled, things will break! + // For now we do angry logging and a debug assert. + if ((DisableHLEFlags)g_Config.iDisableHLE & DisableHLEFlags::sceAtrac) { + ERROR_LOG(Log::ME, "sceAtrac HLE is disabled, and the game tries to load sceAtrac from firmware - this won't work!"); + _dbg_assert_(false); + } + // We try to imitate a recent version of the prx. // Let's just give it a piece of the space. constexpr int version = 0x105; // latest. diff --git a/Core/HW/SimpleAudioDec.h b/Core/HW/SimpleAudioDec.h index 9884821361..bc827bd431 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -18,18 +18,11 @@ #pragma once #include "Core/HW/MediaEngine.h" -#include "Core/HLE/sceAudio.h" +#include "Core/HLE/sceAudiocodec.h" // Decodes packet by packet - does NOT demux. -// audioType -enum PSPAudioType { - PSP_CODEC_AT3PLUS = 0x00001000, - PSP_CODEC_AT3 = 0x00001001, - PSP_CODEC_MP3 = 0x00001002, - PSP_CODEC_AAC = 0x00001003, -}; - +// This basically corresponds to the core of sceAudiocodec. class AudioDecoder { public: virtual ~AudioDecoder() {} diff --git a/Core/Util/AtracTrack.cpp b/Core/Util/AtracTrack.cpp index 2c23cfaf4f..16748519ef 100644 --- a/Core/Util/AtracTrack.cpp +++ b/Core/Util/AtracTrack.cpp @@ -136,11 +136,14 @@ int AnalyzeAtracTrack(const u8 *buffer, u32 size, Track *track, std::string *err // TODO: There are some format specific bytes here which seem to have fixed values? // Probably don't need them. - if (at3fmt->fmtTag == AT3_MAGIC) { + if (at3fmt->fmtTag == AT3_MAGIC) { // 0x270 // This is the offset to the jointStereo_ field. track->jointStereo = Read16(buffer, offset + 24); // Then there are more fields here. u16 unknown1_2 = Read16(buffer, offset + 30); + } else if (at3fmt->fmtTag == AT3_PLUS_MAGIC) { + _dbg_assert_(at3fmt->fmtTag == 0xFFFE); + // It's in an "Extensible" wave format. Let's read some more. } if (chunkSize > 16) { diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 4b099a4f15..8b74ffd13b 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -578,7 +578,7 @@ public: && info_->fileType == IdentifiedFileType::PSP_PBP_DIRECTORY) { info_->id = g_paramSFO.GenerateFakeID(gamePath_); info_->id_version = info_->id + "_1.00"; - info_->region = GAMEREGION_MAX + 1; // Homebrew + info_->region = GAMEREGION_COUNT + 1; // Homebrew } info_->MarkReadyNoLock(GameInfoFlags::PARAM_SFO); } @@ -641,7 +641,7 @@ handleELF: if (flags_ & GameInfoFlags::PARAM_SFO) { info_->id = g_paramSFO.GenerateFakeID(gamePath_); info_->id_version = info_->id + "_1.00"; - info_->region = GAMEREGION_MAX + 1; // Homebrew + info_->region = GAMEREGION_COUNT + 1; // Homebrew } if (flags_ & GameInfoFlags::ICON) { diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 8b9c82a184..d0ecbbd4aa 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -47,7 +47,7 @@ enum GameRegion { GAMEREGION_ASIA, GAMEREGION_KOREA, GAMEREGION_OTHER, - GAMEREGION_MAX, + GAMEREGION_COUNT, }; enum class GameInfoFlags { diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 284e2868ae..ab6203f4e3 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -267,6 +267,7 @@ void GameScreen::CreateViews() { }); } + // TODO: This is synchronous, bad! if (g_recentFiles.ContainsFile(gamePath_.ToString())) { Choice *removeButton = rightColumnItems->Add(AddOtherChoice(new Choice(ga->T("Remove From Recent")))); removeButton->OnClick.Handle(this, &GameScreen::OnRemoveFromRecent); @@ -291,7 +292,7 @@ void GameScreen::CreateViews() { btnSetBackground_->OnClick.Handle(this, &GameScreen::OnSetBackground); btnSetBackground_->SetVisibility(V_GONE); - isHomebrew_ = info && info->region > GAMEREGION_MAX; + isHomebrew_ = info && info->region > GAMEREGION_COUNT; if (fileTypeSupportCRC && !isHomebrew_ && !Reporting::HasCRC(gamePath_) ) { btnCalcCRC_ = rightColumnItems->Add(new ChoiceWithValueDisplay(&CRC32string, ga->T("Calculate CRC"), I18NCat::NONE)); btnCalcCRC_->OnClick.Handle(this, &GameScreen::OnDoCRC32); @@ -381,8 +382,8 @@ ScreenRenderFlags GameScreen::render(ScreenRenderMode mode) { } if (tvRegion_) { - if (info->region >= 0 && info->region < GAMEREGION_MAX && info->region != GAMEREGION_OTHER) { - static const char *regionNames[GAMEREGION_MAX] = { + if (info->region >= 0 && info->region < GAMEREGION_COUNT && info->region != GAMEREGION_OTHER) { + static const char *regionNames[GAMEREGION_COUNT] = { "Japan", "USA", "Europe", @@ -391,7 +392,7 @@ ScreenRenderFlags GameScreen::render(ScreenRenderMode mode) { "Korea" }; tvRegion_->SetText(ga->T(regionNames[info->region])); - } else if (info->region > GAMEREGION_MAX) { + } else if (info->region > GAMEREGION_COUNT) { tvRegion_->SetText(ga->T("Homebrew")); } } diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 851ecba3bc..1317ec92bd 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -1066,6 +1066,23 @@ void DrawAudioDecodersView(ImConfig &cfg, ImControl &control) { ImGui::Text("can't access sas state"); } } + + if (ctx->BufferState() == ATRAC_STATUS_ALL_DATA_LOADED) { + if (ImGui::Button("Save to disk...")) { + System_BrowseForFileSave(cfg.requesterToken, "Save AT3 file", "song.at3", BrowseFileType::ATRAC3, [=](const std::string &filename, int) { + const u8 *data = Memory::GetPointerRange(info.buffer, info.bufferByte); + if (!data) { + return; + } + FILE *file = File::OpenCFile(Path(filename), "wb"); + if (!file) { + return; + } + fwrite(data, 1, info.bufferByte, file); + fclose(file); + }); + } + } } else { ImGui::Text("loop: %d", ctx->LoopNum()); } @@ -1591,6 +1608,13 @@ static void DrawSymbols(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro ImGui::End(); } +void ImAtracToolWindow::Load() { + if (File::ReadBinaryFileToString(Path(atracPath_), &data_)) { + track_.reset(new Track()); + AnalyzeAtracTrack((const u8 *)data_.data(), (u32)data_.size(), track_.get(), &error_); + } +} + void ImAtracToolWindow::Draw(ImConfig &cfg) { if (!ImGui::Begin("Atrac Tool", &cfg.atracToolOpen) || !g_symbolMap) { ImGui::End(); @@ -1600,28 +1624,43 @@ void ImAtracToolWindow::Draw(ImConfig &cfg) { ImGui::InputText("File", atracPath_, sizeof(atracPath_)); ImGui::SameLine(); if (ImGui::Button("Choose...")) { - System_BrowseForFile(cfg.requesterToken, "Choose AT3 file", BrowseFileType::ATRAC3, [&](const std::string &filename, int) { + System_BrowseForFile(cfg.requesterToken, "Choose AT3 file", BrowseFileType::ATRAC3, [this](const std::string &filename, int) { truncate_cpy(atracPath_, filename); + Load(); }, nullptr); } if (strlen(atracPath_) > 0) { if (ImGui::Button("Load")) { - track_.reset(new Track()); - std::string data; - if (File::ReadBinaryFileToString(Path(atracPath_), &data)) { - AnalyzeAtracTrack((const u8 *)data.data(), (u32)data.size(), track_.get(), &error_); - } + Load(); } } if (track_.get() != 0) { ImGui::Text("Codec: %s", track_->codecType != PSP_CODEC_AT3 ? "at3+" : "at3"); ImGui::Text("Bitrate: %d kbps Channels: %d", track_->Bitrate(), track_->channels); - ImGui::Text("Frame size in bytes: %d Output frame in samples: %d", track_->BytesPerFrame(), track_->SamplesPerFrame()); + ImGui::Text("Frame size in bytes: %d (%04x) Output frame in samples: %d", track_->BytesPerFrame(), track_->BytesPerFrame(), track_->SamplesPerFrame()); ImGui::Text("First valid sample: %08x", track_->FirstSampleOffsetFull()); } + if (data_.size()) { + if (ImGui::Button("Dump 64 raw frames")) { + std::string firstFrames = data_.substr(track_->dataByteOffset, track_->bytesPerFrame * 64); + System_BrowseForFileSave(cfg.requesterToken, "Save .at3raw", "at3.raw", BrowseFileType::ANY, [firstFrames](const std::string &filename, int) { + FILE *f = File::OpenCFile(Path(filename), "wb"); + if (f) { + fwrite(firstFrames.data(), 1, firstFrames.size(), f); + fclose(f); + } + }); + } + + if (ImGui::Button("Unload")) { + data_.clear(); + track_.reset(nullptr); + } + } + if (!error_.empty()) { ImGui::TextUnformatted(error_.c_str()); } @@ -1674,7 +1713,21 @@ void DrawHLEModules(ImConfig &config) { } w.F("%s 0x%08x %d %s", func.name, func.ID, strlen(func.argmask), amask.c_str()).endl(); } - System_CopyStringToClipboard(std::string_view(buffer, w.size())); + System_CopyStringToClipboard(w.as_view()); + delete[] buffer; + } + if (ImGui::MenuItem("Copy as imports.S")) { + char *buffer = new char[100000]; + StringWriter w(buffer, 100000); + + w.C(".set noreorder\n\n#include \"pspimport.s\"\n\n"); + w.F("IMPORT_START \"%.*s\",0x00090011\n", (int)mod->name.size(), mod->name.data()); + for (int j = 0; j < mod->numFunctions; j++) { + auto &func = mod->funcTable[j]; + w.F("IMPORT_FUNC \"%.*s\",0x%08X,%s\n", (int)mod->name.size(), mod->name.data(), func.ID, func.name); + } + w.endl(); + System_CopyStringToClipboard(w.as_view()); delete[] buffer; } ImGui::EndPopup(); @@ -2444,6 +2497,7 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) { sync.Sync("logConfigOpen", &logConfigOpen, false); sync.Sync("luaConsoleOpen", &luaConsoleOpen, false); sync.Sync("utilityModulesOpen", &utilityModulesOpen, false); + sync.Sync("atracToolOpen", &atracToolOpen, false); for (int i = 0; i < 4; i++) { char name[64]; snprintf(name, sizeof(name), "memory%dOpen", i + 1); diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index dd6f291ab6..5353349685 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -192,10 +192,12 @@ struct Track; class ImAtracToolWindow { public: void Draw(ImConfig &cfg); + void Load(); char atracPath_[1024]{}; std::unique_ptr track_; std::string error_; + std::string data_; }; enum class ImCmd { diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 3c78953c4b..5dd649fcfa 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -394,8 +394,9 @@ void GameButton::Draw(UIContext &dc) { } } } - if (g_Config.bShowRegionOnGameIcon && ginfo->region >= 0 && ginfo->region < GAMEREGION_MAX && ginfo->region != GAMEREGION_OTHER) { - const ImageID regionIcons[GAMEREGION_MAX] = { + const int region = ginfo->region; + if (g_Config.bShowRegionOnGameIcon && region >= 0 && region < GAMEREGION_COUNT && region != GAMEREGION_OTHER) { + const ImageID regionIcons[GAMEREGION_COUNT] = { ImageID("I_FLAG_JP"), ImageID("I_FLAG_US"), ImageID("I_FLAG_EU"), @@ -404,13 +405,13 @@ void GameButton::Draw(UIContext &dc) { ImageID("I_FLAG_KO"), ImageID::invalid(), }; - const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(regionIcons[ginfo->region]); + const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(regionIcons[region]); if (image) { if (gridStyle_) { - dc.Draw()->DrawImage(regionIcons[ginfo->region], x + w - (image->w + 5)*g_Config.fGameGridScale, + dc.Draw()->DrawImage(regionIcons[region], x + w - (image->w + 5)*g_Config.fGameGridScale, y + h - (image->h + 5)*g_Config.fGameGridScale, g_Config.fGameGridScale); } else { - dc.Draw()->DrawImage(regionIcons[ginfo->region], x - 2 - image->w - 3, y + h - image->h - 5, 1.0f); + dc.Draw()->DrawImage(regionIcons[region], x - 2 - image->w - 3, y + h - image->h - 5, 1.0f); } } } diff --git a/ext/at3_standalone/atrac3plusdec.cpp b/ext/at3_standalone/atrac3plusdec.cpp index e5eaae9f1a..bf37fac83a 100644 --- a/ext/at3_standalone/atrac3plusdec.cpp +++ b/ext/at3_standalone/atrac3plusdec.cpp @@ -329,14 +329,9 @@ int atrac3p_decode_frame(ATRAC3PContext *ctx, float *out_data[2], int *nb_sample if (ch_block >= ctx->num_channel_blocks || ctx->channel_blocks[ch_block] != ch_unit_id) { av_log(AV_LOG_WARNING, "Frame data doesn't match channel configuration! ch_block %d >= num_channel_blocks %d", ch_block, ctx->num_channel_blocks); - // This happens in Code Lyoko, see issue #19994. - // The atrac3+ wav header clearly specifies stereo, so we only have one channel block, but here we try to decode a third (and/or fourth) one. - // Maybe the data chunk is just improperly terminated, but it's reported that when ignoring this error previously, - // it didn't sound right. So I'm really not sure what's going on here. Most likely some unknown feature of the Atrac3+ format that we - // failed to parse previously? - // - // return AVERROR_INVALIDDATA; // This soft-locks the game. - return FFMIN(ctx->block_align, indata_size); // We try to stumble along. + // We used to have trouble in Code Lyoko and other games here. It's usually because data is corrupted + // or the wrong channel configuration is used.. + return AVERROR_INVALIDDATA; } ctx->ch_units[ch_block].unit_type = ch_unit_id;