Merge pull request #20199 from hrydgard/audiocodec-impl

Partially implement sceAudiocodec
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2025-04-02 18:00:28 +02:00
commit ac69167dcd
16 files changed
+319 -113

No files matched your search

+3
View File
@@ -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,
};
+173 -58
View File
@@ -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<SceAudiocodecCodec>::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<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) {
@@ -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<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);
}
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<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;
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<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() {
+36 -11
View File
@@ -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;
+3
View File
@@ -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;
}
+7 -2
View File
@@ -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);
+3 -4
View File
@@ -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.
+7
View File
@@ -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.
+2 -9
View File
@@ -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() {}
+4 -1
View File
@@ -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) {
+2 -2
View File
@@ -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) {
+1 -1
View File
@@ -47,7 +47,7 @@ enum GameRegion {
GAMEREGION_ASIA,
GAMEREGION_KOREA,
GAMEREGION_OTHER,
GAMEREGION_MAX,
GAMEREGION_COUNT,
};
enum class GameInfoFlags {
+5 -4
View File
@@ -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"));
}
}
+62 -8
View File
@@ -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);
+2
View File
@@ -192,10 +192,12 @@ struct Track;
class ImAtracToolWindow {
public:
void Draw(ImConfig &cfg);
void Load();
char atracPath_[1024]{};
std::unique_ptr<Track> track_;
std::string error_;
std::string data_;
};
enum class ImCmd {
+6 -5
View File
@@ -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);
}
}
}
+3 -8
View File
@@ -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;