mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Merge pull request #22306 from hrydgard/assorted-fixes
Various fixes to sceMpeg LLE and sceMp3 LLE
This commit is contained in:
8 files changed
+122
-42
No files matched your search
@@ -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());
|
||||
}
|
||||
+44
-11
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+18
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<u8> &luma, std::vector<u8> &cb, std::vector<u8> &cr) {
|
||||
const int width2 = width >> 1;
|
||||
const int height2 = height >> 1;
|
||||
|
||||
@@ -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<u8> 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<u8> &luma, std::vector<u8> &cb, std::vector<u8> &cr);
|
||||
@@ -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<u8> 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<u8> *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[] = {
|
||||
|
||||
Reference in new issue
Block a user