diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index 34541e5..dde376c 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -821,6 +821,9 @@ namespace ps2recomp ss << "#include \"ps2_recompiled_stubs.h\"\n\n"; ss << "#include \"ps2_syscalls.h\"\n"; ss << "#include \"ps2_stubs.h\"\n\n"; + ss << "#ifdef _DEBUG\n"; + ss << "#include \"ps2_log.h\"\n"; + ss << "#endif\n\n"; } AnalysisResult analysisResult = collectInternalBranchTargets(function, instructions); @@ -836,7 +839,11 @@ namespace ps2recomp sanitizedName = nameBuilder.str(); } - ss << "void " << sanitizedName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n\n"; + ss << "void " << sanitizedName << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n"; + ss << "#ifdef _DEBUG\n"; + ss << " PS_LOG_ENTRY(\"" << sanitizedName << "\");\n"; + ss << "#endif\n"; + ss << "\n"; ss << " ctx->pc = 0x" << std::hex << function.start << "u;\n" << std::dec; ss << "\n"; diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index a172236..e52400b 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -984,8 +984,11 @@ namespace ps2recomp std::string generatedName = m_codeGenerator->getFunctionName(function.start); std::stringstream stub; stub << "void " << generatedName - << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n" - << " const uint32_t __entryPc = ctx->pc;\n" + << "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n"; + stub << "#ifdef _DEBUG\n"; + stub << " PS_LOG_ENTRY(\"" << generatedName << "\");\n"; + stub << "#endif\n"; + stub << " const uint32_t __entryPc = ctx->pc;\n" << " "; if (function.isSkipped) @@ -1044,6 +1047,9 @@ namespace ps2recomp combinedOutput << "#include \"ps2_recompiled_stubs.h\"\n"; combinedOutput << "#include \"ps2_syscalls.h\"\n"; combinedOutput << "#include \"ps2_stubs.h\"\n"; + combinedOutput << "#ifdef _DEBUG\n"; + combinedOutput << "#include \"ps2_log.h\"\n"; + combinedOutput << "#endif\n"; combinedOutput << "\n"; for (const auto &function : m_functions) @@ -1100,7 +1106,11 @@ namespace ps2recomp std::stringstream stubFile; stubFile << "#include \"ps2_runtime.h\"\n"; stubFile << "#include \"ps2_syscalls.h\"\n"; - stubFile << "#include \"ps2_stubs.h\"\n\n"; + stubFile << "#include \"ps2_stubs.h\"\n"; + stubFile << "#ifdef _DEBUG\n"; + stubFile << "#include \"ps2_log.h\"\n"; + stubFile << "#endif\n"; + stubFile << "\n"; stubFile << m_generatedStubs.at(function.start) << "\n"; code = stubFile.str(); } diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index 24a682c..7780b3e 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -20,9 +20,14 @@ FetchContent_MakeAvailable(raylib) add_library(ps2_runtime STATIC src/lib/game_overrides.cpp + src/lib/ps2_audio.cpp + src/lib/ps2_audio_vag.cpp src/lib/ps2_gs_gpu.cpp src/lib/ps2_gs_rasterizer.cpp + src/lib/ps2_iop.cpp + src/lib/ps2_iop_audio.cpp src/lib/ps2_memory.cpp + src/lib/ps2_pad.cpp src/lib/ps2_runtime.cpp src/lib/ps2_stubs.cpp src/lib/ps2_syscalls.cpp diff --git a/ps2xRuntime/include/ps2_audio.h b/ps2xRuntime/include/ps2_audio.h new file mode 100644 index 0000000..dc33a40 --- /dev/null +++ b/ps2xRuntime/include/ps2_audio.h @@ -0,0 +1,48 @@ +#ifndef PS2_AUDIO_H +#define PS2_AUDIO_H + +#include +#include +#include +#include +#include + +class PS2AudioBackend +{ +public: + PS2AudioBackend(); + ~PS2AudioBackend(); + + void onVagTransfer(const uint8_t *rdram, uint32_t srcAddr, uint32_t sizeBytes); + void onVagTransferFromBuffer(const uint8_t *data, uint32_t sizeBytes, uint32_t keyAddr); + void onSoundCommand(uint32_t sid, uint32_t rpcNum, + const uint8_t *sendBuf, uint32_t sendSize, + uint8_t *recvBuf, uint32_t recvSize); + + void play(uint32_t sampleAddr, float pitch = 1.0f, float volume = 1.0f, + uint32_t voiceIndex = 0xFFFFFFFFu); + void stop(uint32_t voiceId); + void stopAll(); + void setAudioReady(bool ready) { m_audioReady = ready; } + +private: + struct DecodedSample + { + std::vector pcm; + uint32_t sampleRate = 44100; + }; + + struct Impl; + std::unique_ptr m_impl; + bool m_audioReady = false; + uint32_t m_mostRecentSampleKey = 0; + std::vector m_loadOrderSamples; + std::unordered_map m_sampleBank; + std::mutex m_mutex; + + void playDecodedSample(uint32_t sampleKey, DecodedSample &sample, float pitch, float volume, + bool isBgm = false); + void pruneFinishedSounds(); +}; + +#endif diff --git a/ps2xRuntime/include/ps2_iop.h b/ps2xRuntime/include/ps2_iop.h new file mode 100644 index 0000000..b531eae --- /dev/null +++ b/ps2xRuntime/include/ps2_iop.h @@ -0,0 +1,25 @@ +#ifndef PS2_IOP_H +#define PS2_IOP_H + +#include + +constexpr uint32_t IOP_SID_LIBSD = 0x80000701u; + +class IOP +{ +public: + IOP(); + ~IOP() = default; + + void init(uint8_t *rdram); + void reset(); + + bool handleRPC(uint32_t sid, uint32_t rpcNum, + uint32_t sendBufAddr, uint32_t sendSize, + uint32_t recvBufAddr, uint32_t recvSize); + +private: + uint8_t *m_rdram = nullptr; +}; + +#endif diff --git a/ps2xRuntime/include/ps2_iop_audio.h b/ps2xRuntime/include/ps2_iop_audio.h new file mode 100644 index 0000000..bb54d31 --- /dev/null +++ b/ps2xRuntime/include/ps2_iop_audio.h @@ -0,0 +1,15 @@ +#ifndef PS2_IOP_AUDIO_H +#define PS2_IOP_AUDIO_H + +#include + +class PS2Runtime; + +namespace ps2_iop_audio +{ +void handleLibSdRpc(PS2Runtime *runtime, uint32_t sid, uint32_t rpcNum, + const uint8_t *sendBuf, uint32_t sendSize, + uint8_t *recvBuf, uint32_t recvSize); +} + +#endif diff --git a/ps2xRuntime/include/ps2_log.h b/ps2xRuntime/include/ps2_log.h new file mode 100644 index 0000000..151e46b --- /dev/null +++ b/ps2xRuntime/include/ps2_log.h @@ -0,0 +1,77 @@ +#ifndef PS2_LOG_H +#define PS2_LOG_H + +#include +#include +#include +#if defined(_WIN32) +#include +#endif + +#ifdef _DEBUG + +namespace ps2_log +{ +inline std::string log_path() +{ + static std::string path; + if (path.empty()) + { +#if defined(_WIN32) + char buf[MAX_PATH]; + if (GetModuleFileNameA(nullptr, buf, sizeof(buf))) + path = (std::filesystem::path(buf).parent_path() / "ps2_log.txt").string(); +#endif + if (path.empty()) + path = (std::filesystem::current_path() / "ps2_log.txt").string(); + } + return path; +} +inline std::ostream &log_stream() +{ + static std::ofstream f(log_path(), std::ios::out); + return f.is_open() ? f : std::cerr; +} +inline int &depth() +{ + static thread_local int d = 0; + return d; +} +inline void log_entry(const char *name) +{ + for (int i = 0; i < depth(); ++i) + log_stream() << '\t'; + log_stream() << ">> " << name << " enter\n"; + log_stream().flush(); + depth()++; +} +inline void log_exit(const char *name) +{ + depth()--; + for (int i = 0; i < depth(); ++i) + log_stream() << '\t'; + log_stream() << "<< " << name << " exit\n"; + log_stream().flush(); +} +inline void print_saved_location() +{ + std::cout << "[PS2 LOG] Logs saved at " << log_path() << std::endl; +} +} + +#define PS_LOG_ENTRY(name) \ + ps2_log::log_entry(name); \ + struct _ps2_log_guard_ { const char *_n; _ps2_log_guard_(const char *n) : _n(n) {} \ + ~_ps2_log_guard_() { ps2_log::log_exit(_n); } } _ps2_log_guard_(name) + +#else + +namespace ps2_log +{ +inline void print_saved_location() {} +} +#define PS_LOG_ENTRY(name) ((void)0) + +#endif + +#endif diff --git a/ps2xRuntime/include/ps2_pad.h b/ps2xRuntime/include/ps2_pad.h new file mode 100644 index 0000000..7755fe6 --- /dev/null +++ b/ps2xRuntime/include/ps2_pad.h @@ -0,0 +1,16 @@ +#ifndef PS2_PAD_H +#define PS2_PAD_H + +#include +#include + +class PSPadBackend +{ +public: + PSPadBackend() = default; + ~PSPadBackend() = default; + + bool readState(int port, int slot, uint8_t *data, size_t size); +}; + +#endif diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index d124cf7..264318b 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -22,6 +22,9 @@ #include #include "ps2_memory.h" +#include "ps2_iop.h" +#include "ps2_audio.h" +#include "ps2_pad.h" enum PS2Exception { @@ -457,6 +460,13 @@ public: inline PS2Memory &memory() { return m_memory; } inline const PS2Memory &memory() const { return m_memory; } + inline IOP &iop() { return m_iop; } + inline const IOP &iop() const { return m_iop; } + inline PS2AudioBackend &audioBackend() { return m_audioBackend; } + inline const PS2AudioBackend &audioBackend() const { return m_audioBackend; } + inline PSPadBackend &padBackend() { return m_padBackend; } + inline const PSPadBackend &padBackend() const { return m_padBackend; } + private: struct GuestHeapBlock { @@ -481,6 +491,9 @@ private: private: PS2Memory m_memory; + IOP m_iop; + PS2AudioBackend m_audioBackend; + PSPadBackend m_padBackend; R5900Context m_cpuContext; mutable std::mutex m_guestHeapMutex; std::vector m_guestHeapBlocks; diff --git a/ps2xRuntime/src/lib/ps2_audio.cpp b/ps2xRuntime/src/lib/ps2_audio.cpp new file mode 100644 index 0000000..720d2c9 --- /dev/null +++ b/ps2xRuntime/src/lib/ps2_audio.cpp @@ -0,0 +1,282 @@ +#include "ps2_audio.h" +#include "ps2_memory.h" +#include "raylib.h" +#include +#include + +namespace +{ +std::vector buildWavFromPcm(const int16_t *pcm, size_t sampleCount, uint32_t sampleRate) +{ + const uint32_t dataSize = static_cast(sampleCount * 2); + const uint32_t fileSize = 36 + dataSize; + std::vector wav(8 + fileSize); + + uint8_t *p = wav.data(); + p[0] = 'R'; p[1] = 'I'; p[2] = 'F'; p[3] = 'F'; + p[4] = static_cast(fileSize); + p[5] = static_cast(fileSize >> 8); + p[6] = static_cast(fileSize >> 16); + p[7] = static_cast(fileSize >> 24); + p[8] = 'W'; p[9] = 'A'; p[10] = 'V'; p[11] = 'E'; + p[12] = 'f'; p[13] = 'm'; p[14] = 't'; p[15] = ' '; + p[16] = 16; p[17] = 0; p[18] = 0; p[19] = 0; + p[20] = 1; p[21] = 0; + p[22] = 1; p[23] = 0; + p[24] = static_cast(sampleRate); + p[25] = static_cast(sampleRate >> 8); + p[26] = static_cast(sampleRate >> 16); + p[27] = static_cast(sampleRate >> 24); + const uint32_t byteRate = sampleRate * 2; + p[28] = static_cast(byteRate); + p[29] = static_cast(byteRate >> 8); + p[30] = static_cast(byteRate >> 16); + p[31] = static_cast(byteRate >> 24); + p[32] = 2; p[33] = 0; + p[34] = 16; p[35] = 0; + p[36] = 'd'; p[37] = 'a'; p[38] = 't'; p[39] = 'a'; + p[40] = static_cast(dataSize); + p[41] = static_cast(dataSize >> 8); + p[42] = static_cast(dataSize >> 16); + p[43] = static_cast(dataSize >> 24); + std::memcpy(p + 44, pcm, dataSize); + return wav; +} +} + +namespace ps2_vag +{ +bool decode(const uint8_t *data, uint32_t sizeBytes, + std::vector &outPcm, uint32_t &outSampleRate); +} + +struct PS2AudioBackend::Impl +{ + struct TrackedSound { Sound snd; uint32_t sampleKey; }; + std::vector activeSounds; +}; + +PS2AudioBackend::PS2AudioBackend() : m_impl(std::make_unique()) +{ +} + +PS2AudioBackend::~PS2AudioBackend() +{ + if (m_impl) + stopAll(); +} + +void PS2AudioBackend::onVagTransfer(const uint8_t *rdram, uint32_t srcAddr, uint32_t sizeBytes) +{ + if (!rdram || sizeBytes < 48) + return; + + const uint32_t physAddr = srcAddr & PS2_RAM_MASK; + if (physAddr + sizeBytes > PS2_RAM_SIZE) + return; + + std::vector pcm; + uint32_t sampleRate = 44100; + if (!ps2_vag::decode(rdram + physAddr, sizeBytes, pcm, sampleRate)) + return; + + std::lock_guard lock(m_mutex); + DecodedSample sample; + sample.pcm = std::move(pcm); + sample.sampleRate = sampleRate; + m_sampleBank[physAddr] = std::move(sample); + m_mostRecentSampleKey = physAddr; +} + +void PS2AudioBackend::onVagTransferFromBuffer(const uint8_t *data, uint32_t sizeBytes, uint32_t keyAddr) +{ + if (!data || sizeBytes < 48) + return; + + std::vector pcm; + uint32_t sampleRate = 44100; + if (!ps2_vag::decode(data, sizeBytes, pcm, sampleRate)) + return; + + const uint32_t physAddr = keyAddr & PS2_RAM_MASK; + std::lock_guard lock(m_mutex); + DecodedSample sample; + sample.pcm = std::move(pcm); + sample.sampleRate = sampleRate; + m_sampleBank[physAddr] = sample; + m_mostRecentSampleKey = physAddr; + m_loadOrderSamples.push_back(std::move(sample)); + constexpr size_t kMaxLoadOrderSamples = 32; + if (m_loadOrderSamples.size() > kMaxLoadOrderSamples) + m_loadOrderSamples.erase(m_loadOrderSamples.begin()); +} + +namespace +{ +constexpr uint32_t LIBSD_CMD_SET_VOICE = 0x8010u; +} + +void PS2AudioBackend::onSoundCommand(uint32_t sid, uint32_t rpcNum, + const uint8_t *sendBuf, uint32_t sendSize, + uint8_t *recvBuf, uint32_t recvSize) +{ + if (sid != 0x80000701u) + return; + + if ((rpcNum == LIBSD_CMD_SET_VOICE || (rpcNum & 0xFF00u) == 0x8100u) && + sendBuf && sendSize >= 20) + { + uint32_t sampleAddr = 0; + uint32_t voiceIndex = 0xFFFFFFFFu; + for (int vo = 4; vo >= 0 && voiceIndex == 0xFFFFFFFFu; vo -= 4) + { + if (vo < static_cast(sendSize)) + { + uint32_t v = 0; + std::memcpy(&v, sendBuf + vo, sizeof(v)); + if (v < 24u) + voiceIndex = v; + } + } + + constexpr uint32_t kMinPlausibleAddr = 0x1000u; + for (int off = 12; off <= 24 && sampleAddr == 0; off += 4) + { + if (sendSize >= static_cast(off + 4)) + { + uint32_t cand = 0; + std::memcpy(&cand, sendBuf + off, sizeof(cand)); + if (cand >= kMinPlausibleAddr && (cand <= PS2_RAM_MASK || (cand & ~PS2_RAM_MASK) == 0)) + sampleAddr = cand; + } + } + if (sampleAddr == 0) + sampleAddr = m_mostRecentSampleKey; + + float pitch = 1.0f; + if (sendSize >= 12) + { + uint16_t pitchHalf = 0; + std::memcpy(&pitchHalf, sendBuf + 8, sizeof(pitchHalf)); + if (pitchHalf != 0) + pitch = 4096.0f / static_cast(pitchHalf); + } + play(sampleAddr, pitch, 1.0f, voiceIndex); + } +} + +void PS2AudioBackend::play(uint32_t sampleAddr, float pitch, float volume, uint32_t voiceIndex) +{ + std::lock_guard lock(m_mutex); + DecodedSample *sampleToPlay = nullptr; + uint32_t sampleKey = 0; + + auto it = m_sampleBank.find(sampleAddr & PS2_RAM_MASK); + if (it != m_sampleBank.end()) + { + sampleToPlay = &it->second; + sampleKey = it->first; + } + else if (voiceIndex != 0xFFFFFFFFu && voiceIndex < m_loadOrderSamples.size()) + { + sampleToPlay = &m_loadOrderSamples[voiceIndex]; + sampleKey = 0x1719740u + voiceIndex; + } + else + { + it = m_sampleBank.find(m_mostRecentSampleKey); + if (it == m_sampleBank.end()) + return; + sampleToPlay = &it->second; + sampleKey = it->first; + } + if (!sampleToPlay || sampleToPlay->pcm.empty()) + return; + + const bool isBgm = (sampleToPlay->pcm.size() > static_cast(sampleToPlay->sampleRate * 5)); + playDecodedSample(sampleKey, *sampleToPlay, pitch, volume, isBgm); +} + +void PS2AudioBackend::pruneFinishedSounds() +{ + auto &sounds = m_impl->activeSounds; + auto it = sounds.begin(); + while (it != sounds.end()) + { + if (!IsSoundPlaying(it->snd)) + { + UnloadSound(it->snd); + it = sounds.erase(it); + } + else + { + ++it; + } + } +} + +void PS2AudioBackend::playDecodedSample(uint32_t sampleKey, DecodedSample &sample, float pitch, float volume, + bool isBgm) +{ + if (!m_audioReady || sample.pcm.empty()) + return; + + pruneFinishedSounds(); + + for (const auto &t : m_impl->activeSounds) + { + if (t.sampleKey == sampleKey && IsSoundPlaying(t.snd)) + return; + } + + auto &sounds = m_impl->activeSounds; + if (isBgm) + { + for (auto it = sounds.begin(); it != sounds.end();) + { + if (IsSoundPlaying(it->snd)) + { + StopSound(it->snd); + UnloadSound(it->snd); + it = sounds.erase(it); + } + else + ++it; + } + } + + constexpr int kMaxConcurrentSounds = 4; + while (static_cast(sounds.size()) >= kMaxConcurrentSounds) + { + StopSound(sounds.front().snd); + UnloadSound(sounds.front().snd); + sounds.erase(sounds.begin()); + } + + std::vector wav = buildWavFromPcm(sample.pcm.data(), sample.pcm.size(), sample.sampleRate); + Wave wave = LoadWaveFromMemory(".wav", wav.data(), static_cast(wav.size())); + if (wave.frameCount <= 0) + return; + Sound snd = LoadSoundFromWave(wave); + UnloadWave(wave); + SetSoundPitch(snd, pitch); + SetSoundVolume(snd, volume); + m_impl->activeSounds.push_back({snd, sampleKey}); + PlaySound(snd); +} + +void PS2AudioBackend::stop(uint32_t voiceId) +{ + (void)voiceId; +} + +void PS2AudioBackend::stopAll() +{ + std::lock_guard lock(m_mutex); + for (auto &t : m_impl->activeSounds) + { + StopSound(t.snd); + UnloadSound(t.snd); + } + m_impl->activeSounds.clear(); +} diff --git a/ps2xRuntime/src/lib/ps2_audio_vag.cpp b/ps2xRuntime/src/lib/ps2_audio_vag.cpp new file mode 100644 index 0000000..12ba0ed --- /dev/null +++ b/ps2xRuntime/src/lib/ps2_audio_vag.cpp @@ -0,0 +1,112 @@ +#include "ps2_memory.h" +#include +#include +#include + +namespace +{ +inline int16_t clamp16(int32_t v) +{ + if (v < -32768) return -32768; + if (v > 32767) return 32767; + return static_cast(v); +} + +inline int8_t signExtend4(uint8_t nibble) +{ + uint8_t s = nibble & 0x0F; + return static_cast((s & 8) ? static_cast(s | 0xF0) : static_cast(s)); +} +} + +namespace ps2_vag +{ +bool decode(const uint8_t *data, uint32_t sizeBytes, + std::vector &outPcm, uint32_t &outSampleRate) +{ + if (!data || sizeBytes < 48) + return false; + + const uint32_t magic = (static_cast(data[0]) << 24) | + (static_cast(data[1]) << 16) | + (static_cast(data[2]) << 8) | + static_cast(data[3]); + if (magic != 0x56414770u) + { + const uint32_t magicLE = (static_cast(data[3]) << 24) | + (static_cast(data[2]) << 16) | + (static_cast(data[1]) << 8) | + static_cast(data[0]); + if (magicLE != 0x56414770u) + return false; + } + + uint32_t dataSize = (static_cast(data[0x0c]) << 24) | + (static_cast(data[0x0d]) << 16) | + (static_cast(data[0x0e]) << 8) | + static_cast(data[0x0f]); + outSampleRate = (static_cast(data[0x10]) << 24) | + (static_cast(data[0x11]) << 16) | + (static_cast(data[0x12]) << 8) | + static_cast(data[0x13]); + if (outSampleRate == 0) + outSampleRate = 44100; + + const uint32_t numBlocks = (dataSize + 15) / 16; + outPcm.clear(); + outPcm.reserve(numBlocks * 28); + + int16_t s1 = 0, s2 = 0; + const uint8_t *block = data + 48; + + for (uint32_t b = 0; b < numBlocks && (block + 16) <= data + sizeBytes; ++b, block += 16) + { + uint8_t shift = block[0] & 0x0F; + if (shift > 12) + shift = 9; + uint8_t filter = (block[0] >> 4) & 0x07; + if (filter > 4) + filter = 0; + + for (int sampleIdx = 0; sampleIdx < 28; ++sampleIdx) + { + const uint8_t byte = block[2 + sampleIdx / 2]; + const uint8_t nibble = (sampleIdx & 1) ? (byte >> 4) : (byte & 0x0F); + const int8_t rawSample = signExtend4(nibble); + const int32_t shiftedSample = rawSample << (12 - shift); + + int32_t filteredSample; + const int32_t old = s1; + const int32_t older = s2; + switch (filter) + { + case 0: + filteredSample = shiftedSample; + break; + case 1: + filteredSample = shiftedSample + (60 * old + 32) / 64; + break; + case 2: + filteredSample = shiftedSample + (115 * old - 52 * older + 32) / 64; + break; + case 3: + filteredSample = shiftedSample + (98 * old - 55 * older + 32) / 64; + break; + case 4: + filteredSample = shiftedSample + (122 * old - 60 * older + 32) / 64; + break; + default: + filteredSample = shiftedSample; + break; + } + + const int16_t clamped = clamp16(filteredSample); + s2 = s1; + s1 = clamped; + outPcm.push_back(clamped); + } + } + + return true; +} +} diff --git a/ps2xRuntime/src/lib/ps2_iop.cpp b/ps2xRuntime/src/lib/ps2_iop.cpp new file mode 100644 index 0000000..0a90356 --- /dev/null +++ b/ps2xRuntime/src/lib/ps2_iop.cpp @@ -0,0 +1,22 @@ +#include "ps2_iop.h" + +IOP::IOP() +{ + reset(); +} + +void IOP::init(uint8_t *rdram) +{ + m_rdram = rdram; +} + +void IOP::reset() +{ +} + +bool IOP::handleRPC(uint32_t /*sid*/, uint32_t /*rpcNum*/, + uint32_t /*sendBufAddr*/, uint32_t /*sendSize*/, + uint32_t /*recvBufAddr*/, uint32_t /*recvSize*/) +{ + return false; +} diff --git a/ps2xRuntime/src/lib/ps2_iop_audio.cpp b/ps2xRuntime/src/lib/ps2_iop_audio.cpp new file mode 100644 index 0000000..259209e --- /dev/null +++ b/ps2xRuntime/src/lib/ps2_iop_audio.cpp @@ -0,0 +1,14 @@ +#include "ps2_iop_audio.h" +#include "ps2_runtime.h" + +namespace ps2_iop_audio +{ +void handleLibSdRpc(PS2Runtime *runtime, uint32_t sid, uint32_t rpcNum, + const uint8_t *sendBuf, uint32_t sendSize, + uint8_t *recvBuf, uint32_t recvSize) +{ + if (!runtime) + return; + runtime->audioBackend().onSoundCommand(sid, rpcNum, sendBuf, sendSize, recvBuf, recvSize); +} +} diff --git a/ps2xRuntime/src/lib/ps2_pad.cpp b/ps2xRuntime/src/lib/ps2_pad.cpp new file mode 100644 index 0000000..cc99e1c --- /dev/null +++ b/ps2xRuntime/src/lib/ps2_pad.cpp @@ -0,0 +1,94 @@ +#include "ps2_pad.h" +#include "raylib.h" +#include + +namespace +{ +constexpr uint8_t kPadAnalogMarker = 0x73; +constexpr uint8_t kPadStickCenter = 0x80; + +constexpr uint16_t PAD_LEFT = 0x0080u; +constexpr uint16_t PAD_DOWN = 0x0040u; +constexpr uint16_t PAD_RIGHT = 0x0020u; +constexpr uint16_t PAD_UP = 0x0010u; +constexpr uint16_t PAD_START = 0x0008u; +constexpr uint16_t PAD_R3 = 0x0004u; +constexpr uint16_t PAD_L3 = 0x0002u; +constexpr uint16_t PAD_SELECT = 0x0001u; +constexpr uint16_t PAD_SQUARE = 0x8000u; +constexpr uint16_t PAD_CROSS = 0x4000u; +constexpr uint16_t PAD_CIRCLE = 0x2000u; +constexpr uint16_t PAD_TRIANGLE = 0x1000u; +constexpr uint16_t PAD_R1 = 0x0800u; +constexpr uint16_t PAD_L1 = 0x0400u; +constexpr uint16_t PAD_R2 = 0x0200u; +constexpr uint16_t PAD_L2 = 0x0100u; +} + +bool PSPadBackend::readState(int /*port*/, int /*slot*/, uint8_t *data, size_t size) +{ + if (!data || size < 32) + return false; + + std::memset(data, 0, 32); + data[0] = 0x01; + data[1] = kPadAnalogMarker; + data[2] = 0xFF; + data[3] = 0xFF; + data[4] = data[5] = data[6] = data[7] = kPadStickCenter; + + uint16_t btns = 0xFFFFu; + constexpr int kGamepad = 0; + const bool useGamepad = IsGamepadAvailable(kGamepad); + auto clearBit = [&btns](uint16_t mask) { btns &= ~mask; }; + + if (useGamepad) + { + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)) clearBit(PAD_UP); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) clearBit(PAD_DOWN); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) clearBit(PAD_LEFT); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) clearBit(PAD_RIGHT); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) clearBit(PAD_CROSS); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) clearBit(PAD_CIRCLE); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) clearBit(PAD_SQUARE); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)) clearBit(PAD_TRIANGLE); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) clearBit(PAD_L1); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) clearBit(PAD_R1); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) clearBit(PAD_L2); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_2)) clearBit(PAD_R2); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)) clearBit(PAD_START); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)) clearBit(PAD_SELECT); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_THUMB)) clearBit(PAD_L3); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) clearBit(PAD_R3); + + float lx = GetGamepadAxisMovement(kGamepad, GAMEPAD_AXIS_LEFT_X); + float ly = GetGamepadAxisMovement(kGamepad, GAMEPAD_AXIS_LEFT_Y); + float rx = GetGamepadAxisMovement(kGamepad, GAMEPAD_AXIS_RIGHT_X); + float ry = GetGamepadAxisMovement(kGamepad, GAMEPAD_AXIS_RIGHT_Y); + data[6] = static_cast(128 + lx * 127); + data[7] = static_cast(128 + ly * 127); + data[4] = static_cast(128 + rx * 127); + data[5] = static_cast(128 + ry * 127); + } + else + { + if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) clearBit(PAD_UP); + if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) clearBit(PAD_DOWN); + if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) clearBit(PAD_LEFT); + if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) clearBit(PAD_RIGHT); + if (IsKeyDown(KEY_ENTER) || IsKeyDown(KEY_SPACE)) clearBit(PAD_CROSS); + if (IsKeyDown(KEY_ESCAPE)) clearBit(PAD_CIRCLE); + if (IsKeyDown(KEY_KP_0) || IsKeyDown(KEY_Z)) clearBit(PAD_SQUARE); + if (IsKeyDown(KEY_KP_1) || IsKeyDown(KEY_X)) clearBit(PAD_TRIANGLE); + if (IsKeyDown(KEY_Q)) clearBit(PAD_L1); + if (IsKeyDown(KEY_E)) clearBit(PAD_R1); + if (IsKeyDown(KEY_LEFT_SHIFT)) clearBit(PAD_L2); + if (IsKeyDown(KEY_RIGHT_SHIFT)) clearBit(PAD_R2); + if (IsKeyDown(KEY_ENTER)) clearBit(PAD_START); + if (IsKeyDown(KEY_TAB)) clearBit(PAD_SELECT); + } + + data[2] = static_cast(btns & 0xFF); + data[3] = static_cast(btns >> 8); + return true; +} diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 73f696e..b037778 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -296,8 +296,13 @@ bool PS2Runtime::initialize(const char *title) return false; } + m_iop.init(m_memory.getRDRAM()); + m_iop.reset(); + SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(FB_WIDTH, FB_HEIGHT, title); + InitAudioDevice(); + m_audioBackend.setAudioReady(IsAudioDeviceReady()); SetTargetFPS(60); return true; diff --git a/ps2xRuntime/src/lib/ps2_syscalls.cpp b/ps2xRuntime/src/lib/ps2_syscalls.cpp index 309d7c8..2bdefc3 100644 --- a/ps2xRuntime/src/lib/ps2_syscalls.cpp +++ b/ps2xRuntime/src/lib/ps2_syscalls.cpp @@ -1,5 +1,6 @@ #include "ps2_syscalls.h" #include "ps2_runtime.h" +#include "ps2_iop_audio.h" #include "ps2_runtime_macros.h" #include "ps2_stubs.h" #include diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl index f73eb74..e9ed2e4 100644 --- a/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl +++ b/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl @@ -1500,26 +1500,24 @@ void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - (void)runtime; - - const uint32_t dataAddr = getRegU32(ctx, 6); // a2 + const int port = static_cast(getRegU32(ctx, 4)); + const int slot = static_cast(getRegU32(ctx, 5)); + const uint32_t dataAddr = getRegU32(ctx, 6); uint8_t *data = getMemPtr(rdram, dataAddr); if (!data) { setReturnS32(ctx, 0); return; } - - // struct padButtonStatus (32 bytes): neutral state, no buttons pressed. + if (runtime && runtime->padBackend().readState(port, slot, data, 32)) + { + setReturnS32(ctx, 1); + return; + } std::memset(data, 0, 32); - data[1] = 0x73; // analog/dualshock mode marker - data[2] = 0xFF; // btns low (active-low) - data[3] = 0xFF; // btns high - data[4] = 0x80; // rjoy_h - data[5] = 0x80; // rjoy_v - data[6] = 0x80; // ljoy_h - data[7] = 0x80; // ljoy_v - + data[1] = 0x73; + data[2] = data[3] = 0xFF; + data[4] = data[5] = data[6] = data[7] = 0x80; setReturnS32(ctx, 1); } diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl index d3eb5cb..f9e689a 100644 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl @@ -26,6 +26,15 @@ static void releasePs2Fd(int ps2Fd) g_fileDescriptors.erase(ps2Fd); } +struct VagAccumEntry +{ + std::vector data; + uint32_t firstBufAddr = 0; +}; +static std::unordered_map g_vagAccum; +static std::mutex g_vagAccumMutex; +static constexpr size_t kVagAccumMaxBytes = 16 * 1024 * 1024; + static const char *translateFioMode(int ps2Flags) { bool read = (ps2Flags & PS2_FIO_O_RDONLY) || (ps2Flags & PS2_FIO_O_RDWR); @@ -106,21 +115,47 @@ void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - int ps2Fd = (int)getRegU32(ctx, 4); // $a0 - std::cout << "fioClose: fd=" << ps2Fd << std::endl; + int ps2Fd = (int)getRegU32(ctx, 4); FILE *fp = getHostFile(ps2Fd); if (!fp) { std::cerr << "fioClose warning: Invalid PS2 file descriptor " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // e.g., -EBADF + setReturnS32(ctx, -1); return; } int ret = ::fclose(fp); releasePs2Fd(ps2Fd); - // returns 0 on success, -1 on error + { + std::lock_guard lock(g_vagAccumMutex); + auto it = g_vagAccum.find(ps2Fd); + if (it != g_vagAccum.end()) + { + VagAccumEntry &e = it->second; + if (e.data.size() >= 48) + { + const uint32_t magic = (static_cast(e.data[0]) << 24) | + (static_cast(e.data[1]) << 16) | + (static_cast(e.data[2]) << 8) | + static_cast(e.data[3]); + const uint32_t magicLE = (static_cast(e.data[3]) << 24) | + (static_cast(e.data[2]) << 16) | + (static_cast(e.data[1]) << 8) | + static_cast(e.data[0]); + if (magic == 0x56414770u || magicLE == 0x56414770u) + { + if (runtime) + runtime->audioBackend().onVagTransferFromBuffer( + e.data.data(), static_cast(e.data.size()), + e.firstBufAddr ? e.firstBufAddr : 0u); + } + } + g_vagAccum.erase(it); + } + } + setReturnS32(ctx, ret == 0 ? 0 : -1); } @@ -161,11 +196,39 @@ void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { std::cerr << "fioRead error: fread failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; clearerr(fp); - setReturnS32(ctx, -1); // -EIO or other appropriate error + setReturnS32(ctx, -1); return; } - // returns number of bytes read (can be 0 for EOF) + { + std::lock_guard lock(g_vagAccumMutex); + auto it = g_vagAccum.find(ps2Fd); + if (it != g_vagAccum.end()) + { + VagAccumEntry &e = it->second; + if (e.data.size() + bytesRead <= kVagAccumMaxBytes) + e.data.insert(e.data.end(), hostBuf, hostBuf + bytesRead); + } + else if (bytesRead >= 4) + { + const uint32_t magic = (static_cast(hostBuf[0]) << 24) | + (static_cast(hostBuf[1]) << 16) | + (static_cast(hostBuf[2]) << 8) | + static_cast(hostBuf[3]); + const uint32_t magicLE = (static_cast(hostBuf[3]) << 24) | + (static_cast(hostBuf[2]) << 16) | + (static_cast(hostBuf[1]) << 8) | + static_cast(hostBuf[0]); + if (magic == 0x56414770u || magicLE == 0x56414770u) + { + VagAccumEntry &e = g_vagAccum[ps2Fd]; + e.firstBufAddr = bufAddr; + if (bytesRead <= kVagAccumMaxBytes) + e.data.assign(hostBuf, hostBuf + bytesRead); + } + } + } + setReturnS32(ctx, (int32_t)bytesRead); } diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl index fe085dc..d190b0b 100644 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl @@ -321,6 +321,19 @@ void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) return true; }; + if (!handled && sid != 0 && runtime) + { + if (!runtime->iop().handleRPC(sid, rpcNum, sendBuf, sendSize, recvBuf, recvSize) && + sid == IOP_SID_LIBSD) + { + const uint8_t *sendPtr = sendBuf ? getConstMemPtr(rdram, sendBuf) : nullptr; + uint8_t *recvPtr = recvBuf ? getMemPtr(rdram, recvBuf) : nullptr; + ps2_iop_audio::handleLibSdRpc(runtime, sid, rpcNum, sendPtr, sendSize, recvPtr, recvSize); + handled = true; + resultPtr = recvBuf; + } + } + const bool isDtxUrpc = (sid == kDtxRpcSid) && (rpcNum >= 0x400u) && (rpcNum < 0x500u); uint32_t dtxUrpcCommand = isDtxUrpc ? (rpcNum & 0xFFu) : 0u; uint32_t dtxUrpcFn = 0; diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index f5ad70a..dc3425b 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -1,6 +1,9 @@ #include "ps2_runtime.h" #include "register_functions.h" #include "games_database.h" +#ifdef _DEBUG +#include "ps2_log.h" +#endif #include #include @@ -63,5 +66,8 @@ int main(int argc, char* argv[]) runtime.run(); +#ifdef _DEBUG + ps2_log::print_saved_location(); +#endif return 0; } \ No newline at end of file