diff --git a/Common/Log/LogManager.cpp b/Common/Log/LogManager.cpp index be728c9bcd..d89bdab8bf 100644 --- a/Common/Log/LogManager.cpp +++ b/Common/Log/LogManager.cpp @@ -515,27 +515,8 @@ void LogManager::StdioLog(const LogMessage &message) { } void PrintfLog(const LogMessage &message) { - const char *category = message.log; - - switch (message.level) { - case LogLevel::LVERBOSE: - fprintf(stderr, "V %s: %s", category, message.msg.c_str()); - break; - case LogLevel::LDEBUG: - fprintf(stderr, "D %s: %s", category, message.msg.c_str()); - break; - case LogLevel::LINFO: - fprintf(stderr, "I %s: %s", category, message.msg.c_str()); - break; - case LogLevel::LERROR: - fprintf(stderr, "E %s: %s", category, message.msg.c_str()); - break; - case LogLevel::LWARNING: - fprintf(stderr, "W %s: %s", category, message.msg.c_str()); - break; - case LogLevel::LNOTICE: - default: - fprintf(stderr, "N %s: %s", category, message.msg.c_str()); - break; - } + // Same shape as the stdio and file outputs the other builds use. It used to be its own + // shorter format, which meant a pattern that matched a log from the app quietly matched + // nothing in one from headless. + fprintf(stderr, "%s %s %s", message.timestamp, message.header, message.msg.c_str()); } diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index cb00a38b5a..c0f02fe3a5 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/sceKernelMemory.h" #include "Core/HLE/ErrorCodes.h" #include "Core/MemMap.h" #include "Core/Reporting.h" @@ -397,7 +398,11 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) { } ctx->srcBytesRead = inDataConsumed + headerBytes; - ctx->dstSamplesWritten = outSamples; + // In bytes, not samples. sceAudiocodecGetOutputBytes describes the same quantity in bytes + // (0x1200 for MPEG1 MP3), and libmp3.prx takes this as the length of the PCM to hand on - + // reporting the sample count instead gave it a quarter of every frame, which played back + // fast and metallic. The decoder always writes stereo 16-bit, whatever the source is. + ctx->dstBytesWritten = outSamples * 2 * (int)sizeof(int16_t); } return hleLogDebug(Log::ME, 0, "codec %s sampleRate: %d bytesPerFrame: %d channels: %d", GetCodecName(codec), sampleRate, bytesPerFrame, channels); } @@ -414,19 +419,47 @@ static int sceAudiocodecGetInfo(u32 ctxPtr, int codec) { // Write some expected values. switch (codec) { case PSP_CODEC_MP3: - // When this is called, the caller has written: - // * inptr - // * outptr - // * fmt.mp3.maxFrameBytes = 0x5A1 - // Our response is written to a bunch of fields, but I really don't know much - // about what the values are - this is handled internally in the ME. + { + // The caller has written inBuf, outBuf and maxFrameBytes, and left version at the 9999 + // sceAudiocodecInit puts there to mean "not known yet". Filling that in is the point of + // this call - libmp3.prx reads it straight back out, and leaving it at 9999 is what made + // Meruru no Atelier Plus go silent: it got this far and then stopped without ever asking + // for a decode. + // + // The hardware reads these off the frame, so read them off the frame. Apart from the + // version index, which has its own numbering, they are the raw MPEG header fields. ctx->fmt.mp3.unk3c = 3; - ctx->fmt.mp3.bitrateIndex = 9; - ctx->fmt.mp3.sampleRateIndex = 0; ctx->fmt.mp3.unk60 = 1; - ctx->fmt.mp3.channelConfig = 1; + + // Header version bits are 0 = MPEG2.5, 2 = MPEG2, 3 = MPEG1 (1 is reserved); the field + // wants 0 = MPEG2, 1 = MPEG1, 2 = MPEG2.5. + static const int versionFromHeader[4] = { 2, -1, 0, 1 }; + const u8 *header = Memory::IsValidRange(ctx->inBuf, 4) ? Memory::GetPointerUnchecked(ctx->inBuf) : nullptr; + const bool haveFrame = header && header[0] == 0xFF && (header[1] & 0xE0) == 0xE0 && + versionFromHeader[(header[1] >> 3) & 3] >= 0; + if (haveFrame) { + ctx->fmt.mp3.version = versionFromHeader[(header[1] >> 3) & 3]; + ctx->fmt.mp3.bitrateIndex = (header[2] >> 4) & 0x0F; + ctx->fmt.mp3.sampleRateIndex = (header[2] >> 2) & 0x03; + ctx->fmt.mp3.channelConfig = (header[3] >> 6) & 0x03; + INFO_LOG(Log::ME, "GetInfo MP3: sdk=%08x version=%d bitrateIdx=%d sampleRateIdx=%d channelConfig=%d (hdr %02x %02x %02x %02x)", + sceKernelGetCompiledSdkVersion(), (int)ctx->fmt.mp3.version, (int)ctx->fmt.mp3.bitrateIndex, (int)ctx->fmt.mp3.sampleRateIndex, + (int)ctx->fmt.mp3.channelConfig, header[0], header[1], header[2], header[3]); + } else { + // Nothing readable to look at. Claim 128kbps 44.1kHz stereo, as this used to + // unconditionally - but do set the version, since 9999 stops the caller dead. + // Worth hearing about: everything the caller does with the stream follows from these, + // so if a game ever lands here its audio will be wrong in a way that starts right here. + WARN_LOG(Log::ME, "sceAudiocodecGetInfo: no MP3 frame at inBuf %08x, guessing 128kbps 44.1kHz stereo", + ctx->inBuf); + ctx->fmt.mp3.version = 1; + ctx->fmt.mp3.bitrateIndex = 9; + ctx->fmt.mp3.sampleRateIndex = 0; + ctx->fmt.mp3.channelConfig = 1; + } break; } + } return hleLogInfo(Log::ME, 0, "codec=%s", GetCodecName(codec)); } @@ -471,7 +504,7 @@ static int sceAudiocodecCheckNeedMem(u32 ctxPtr, int codec) { ctx->err = 0; ctx->magic = 0x5100601; - return hleLogWarning(Log::ME, 0, "%s", GetCodecName(codec)); + return hleLogInfo(Log::ME, 0, "%s: %x", GetCodecName(codec), ctx->neededMem); } static int sceAudiocodecGetEDRAM(u32 ctxPtr, int codec) { diff --git a/Core/HLE/sceAudiocodec.h b/Core/HLE/sceAudiocodec.h index e298cba0b7..ce48f7598b 100644 --- a/Core/HLE/sceAudiocodec.h +++ b/Core/HLE/sceAudiocodec.h @@ -50,7 +50,7 @@ struct SceAudiocodecCodec { u32 inBuf; // 0x18 the raw frame to decode s32 srcBytesRead; // 0x1c written by the decoder u32 outBuf; // 0x20 where decoded PCM goes - s32 dstSamplesWritten; // 0x24 written by the decoder + s32 dstBytesWritten; // 0x24 written by the decoder, in bytes like srcBytesRead // Codec-specific, 0x28..0x67. union { diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 45e4733713..b25069be6b 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -999,7 +999,7 @@ static u32 npdrmRead(FileNode *f, u8 *data, int size) { PGD_DESC *pgd = f->pgdInfo; if (!pgd) { // When pgdInfo is null, fall back to reading the file in non-encrypted mode - WARN_LOG(Log::IO, "npdrmRead: pgdInfo is null for file %s, reading as non-encrypted", f->fullpath.c_str()); + DEBUG_LOG(Log::IO, "npdrmRead: pgdInfo is null for file %s, reading as non-encrypted", f->fullpath.c_str()); return (u32)pspFileSystem.ReadFile(f->handle, data, size); } u32 block, offset, blockPos; diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index d82a0a5529..fff5c2f5d4 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -480,7 +480,24 @@ static int sceMp3Init(u32 mp3) { // TODO: Should return 0x80671301 (unsupported version?) WARN_LOG_REPORT(Log::ME, "sceMp3Init: invalid data: not MPEG v1"); } - if (samplerate != 44100 && sdkver < 3090500) { + // DELIBERATELY MORE LENIENT THAN A PSP. + // + // libmp3.prx only accepts a rate other than 44.1kHz from a game built with SDK 3.09.05 or + // later - it compares the compiled SDK version against 0x030904FF - and audio/mp3/init carries + // the hardware's answers for the rest: 48kHz and 32kHz both come back as 0x80671302. + // + // We only give that answer to something that declares no SDK version at all, which in practice + // means the test. Anything that declares one gets its rate accepted whatever it is. That is + // what PPSSPP has always done here, by accident - the threshold was written as decimal 3090500 + // rather than 0x03090500, so every real version cleared it - but it is worth keeping on + // purpose. Beats and games like it build levels out of MP3s the user supplies, and refusing an + // ordinary 48kHz file looks like a bug to whoever supplied it. + // + // Note this only applies here. Under DisableHLE for sceMp3 the check is inside libmp3.prx and + // it does refuse the file, because the only thing we hand it is the sample rate index - which + // it also uses to look up the rate it plays at, so claiming 44.1kHz to get past the check would + // play the stream at the wrong speed. To be strict again, compare against 0x030904FF. + if (samplerate != 44100 && sdkver == 0) { return hleDelayResult(hleLogError(Log::ME, SCE_MP3_ERROR_BAD_SAMPLE_RATE, "invalid data: not 44.1kHz"), "mp3 init", PARSE_DELAY_MS); } diff --git a/Core/HLE/sceMpegbase.cpp b/Core/HLE/sceMpegbase.cpp index d07d3b0ab1..7167ec9f86 100644 --- a/Core/HLE/sceMpegbase.cpp +++ b/Core/HLE/sceMpegbase.cpp @@ -174,7 +174,7 @@ static const u8 *MpegBaseFramePointer(u32 addr, int size) { // // Untangling it into plain planes costs one pass per frame, which keeps the conversion below // readable and is not where the time goes. -static bool ReadTiledYCbCr(const u32 *buffers, int width, int height, +bool ReadTiledYCbCr(const u32 *buffers, int width, int height, std::vector &luma, std::vector &cb, std::vector &cr) { const int width2 = width >> 1; const int height2 = height >> 1; diff --git a/Core/HLE/sceMpegbase.h b/Core/HLE/sceMpegbase.h index 6bf855d464..cf85d1a4ca 100644 --- a/Core/HLE/sceMpegbase.h +++ b/Core/HLE/sceMpegbase.h @@ -35,3 +35,9 @@ void __MpegBaseDoState(PointerWrap &p); // instead. The payload is moved out and dropped from the table, so each one is decoded once. // Empty if nothing was copied to that address. std::vector MpegBaseTakePESPacket(u32 dest); + +// Un-tiles a decoded frame from the eight buffers the Media Engine lays it out in into three +// planes. The buffers are in sceVideocodec's order: four luma, then four chroma. cb and cr come +// out at half width and half height, as YUV420 does. +bool ReadTiledYCbCr(const u32 *buffers, int width, int height, + std::vector &luma, std::vector &cb, std::vector &cr); diff --git a/Core/HLE/sceVideocodec.cpp b/Core/HLE/sceVideocodec.cpp index 8f1c81618f..f99dffa34f 100644 --- a/Core/HLE/sceVideocodec.cpp +++ b/Core/HLE/sceVideocodec.cpp @@ -653,11 +653,54 @@ static int sceVideocodecSetMode(u32 ctxAddr, int type) { return hleLogWarning(Log::ME, 0, "UNIMPL"); } -// 0xD95C24D5. Copies a decoded YCbCr frame between two sets of buffers through the ME (op -// 0x21521BE5) - the videocodec-level counterpart of sceMpegBaseYCrCbCopy. mpeg.prx calls it on the -// sceMpegAvcCopyYCbCr path. Nothing we run reaches it, so it stays a stub for now. +// 0xD95C24D5. Hands a decoded frame back to the caller as three planes, which is what +// sceMpegAvcCopyYCbCr is built on - the videocodec-level counterpart of sceMpegBaseYCrCbCopy. +// Games that want the raw YCbCr rather than letting sceMpegbase convert to RGB use this and nothing +// else (Monster Hunter Portable 3rd calls it once per decoded frame and never calls a Csc). +// +// mpeg.prx builds the descriptor on its own stack (AvcCopyDeeper in mpeg.prx 2.60) and avcodec.prx +// reads it back at 0x800015c4, which is where the layout below comes from: +// +// 0x00 width in pixels 0x04 height in pixels +// 0x0c the eight frame buffers, but ordered 0,2,4,6 then 1,3,5,7 rather than 0..7 +// 0x2c destination Y, then Cb at +width*height and Cr a further width*height/4 on - so the +// three planes are contiguous, and the caller gets ordinary planar YUV420. static int sceVideocodecCopyYCbCr(u32 ctxAddr, int type) { - return hleLogWarning(Log::ME, 0, "UNIMPL"); + if (!Memory::IsValidRange(ctxAddr, 0x38)) { + return hleLogError(Log::ME, -1, "bad descriptor pointer"); + } + const int width = (int)Memory::ReadUnchecked_U32(ctxAddr + 0x00); + const int height = (int)Memory::ReadUnchecked_U32(ctxAddr + 0x04); + if (width <= 0 || height <= 0 || width > 1024 || height > 1024) { + return hleLogError(Log::ME, -1, "unreasonable frame size %dx%d", width, height); + } + + // Back into the order the rest of our code uses: four luma, then four chroma. + static const int fromDescriptor[8] = { 0, 2, 4, 6, 1, 3, 5, 7 }; + u32 buffers[8]{}; + for (int i = 0; i < 8; i++) { + buffers[fromDescriptor[i]] = Memory::ReadUnchecked_U32(ctxAddr + 0x0c + i * 4); + } + + std::vector luma, cb, cr; + if (!ReadTiledYCbCr(buffers, width, height, luma, cb, cr)) { + return hleLogError(Log::ME, -1, "YCbCr buffers not readable"); + } + + const u32 dst[3] = { + Memory::ReadUnchecked_U32(ctxAddr + 0x2c), + Memory::ReadUnchecked_U32(ctxAddr + 0x30), + Memory::ReadUnchecked_U32(ctxAddr + 0x34), + }; + const std::vector *planes[3] = { &luma, &cb, &cr }; + for (int i = 0; i < 3; i++) { + const u32 size = (u32)planes[i]->size(); + if (!Memory::IsValidRange(dst[i], size)) { + return hleLogError(Log::ME, -1, "plane %d (%08x, %d bytes) not writable", i, dst[i], size); + } + Memory::MemcpyUnchecked(dst[i], planes[i]->data(), size); + } + return hleLogDebug(Log::ME, 0, "%dx%d -> %08x %08x %08x", width, height, dst[0], dst[1], dst[2]); } const HLEFunction sceVideocodec[] = {