diff --git a/CMakeLists.txt b/CMakeLists.txt index 095a2811d..d3ca834fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -361,6 +361,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.hpp include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp + include/services/dsp_firmware_db.hpp ) cmrc_add_resource_library( diff --git a/include/config.hpp b/include/config.hpp index 9364a33dd..5a7c7fff9 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -58,6 +58,7 @@ struct EmulatorConfig { bool enableRenderdoc = false; bool printAppVersion = true; + bool printDSPFirmware = false; bool chargerPlugged = true; // Default to 3% battery to make users suffer diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index bc1adbca8..7eb9b28b0 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -10,6 +10,7 @@ #include "memory.hpp" #include "result/result.hpp" +struct EmulatorConfig; // Circular dependencies! class Kernel; @@ -19,7 +20,9 @@ class DSPService { Handle handle = KernelHandles::DSP; Memory& mem; Kernel& kernel; + const EmulatorConfig& config; Audio::DSPCore* dsp = nullptr; + MAKE_LOG_FUNCTION(log, dspServiceLogger) // Number of DSP pipes @@ -58,7 +61,7 @@ class DSPService { void writeProcessPipe(u32 messagePointer); public: - DSPService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} + DSPService(Memory& mem, Kernel& kernel, const EmulatorConfig& config) : mem(mem), kernel(kernel), config(config) {} void reset(); void handleSyncRequest(u32 messagePointer); void setDSPCore(Audio::DSPCore* pointer) { dsp = pointer; } @@ -84,4 +87,5 @@ class DSPService { void triggerInterrupt1(); ComponentDumpResult dumpComponent(const std::filesystem::path& path); + void printFirmwareInfo(); }; \ No newline at end of file diff --git a/include/services/dsp_firmware_db.hpp b/include/services/dsp_firmware_db.hpp new file mode 100644 index 000000000..bac11d730 --- /dev/null +++ b/include/services/dsp_firmware_db.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include + +#include "helpers.hpp" + +namespace DSP { + struct FirmwareInfo { + using Hash = std::array; + + Hash hash; // Firmware hash (SHA-256) + u32 size; // Firmware size in bytes + + bool supportsAAC; // Does this firmware support AAC decoding? + const char* notes; // Miscellaneous notes about the firmware + + explicit constexpr FirmwareInfo(const Hash& hash, u32 size, bool supportsAAC, const char* notes) + : hash(hash), size(size), supportsAAC(supportsAAC), notes(notes) {} + }; + + static constexpr std::array firmwareDB = { + FirmwareInfo( + {0x47, 0xD6, 0x6C, 0xD2, 0x13, 0x1, 0xFF, 0x62, 0xAD, 0x16, 0x98, 0x2, 0x46, 0x67, 0xF3, 0x9, + 0xDA, 0x7, 0x20, 0x9E, 0xFB, 0xB, 0x6A, 0x81, 0x98, 0xFF, 0x9B, 0xE0, 0x51, 0x67, 0xC9, 0xA6}, + 48480, false, "Spotted in some versions of Activity Log potentially other apps" + ), + + FirmwareInfo( + {0xF5, 0xDA, 0x79, 0xE7, 0x24, 0x6C, 0x51, 0x9A, 0x28, 0x6C, 0x50, 0xC9, 0x9F, 0xA1, 0xE6, 0x4D, + 0xA5, 0x72, 0x96, 0x5F, 0xEA, 0x14, 0x20, 0xA7, 0x70, 0x90, 0x57, 0x42, 0x34, 0x6E, 0x18, 0xD1}, + 49674, false, "One of the most common firmwares. Found in NSMB2 and others" + ), + + FirmwareInfo( + {0x94, 0x4B, 0x40, 0xB5, 0x46, 0x93, 0xF4, 0xB1, 0xD9, 0x52, 0xBE, 0x84, 0x87, 0xE9, 0xE9, 0x1F, + 0x66, 0x7F, 0xC4, 0x89, 0xF8, 0x15, 0x79, 0xF, 0x3D, 0x3E, 0x89, 0x26, 0x5F, 0xE0, 0x89, 0xC4}, + 49800, false, "One of the most common firmwares. Found in Majora's Mask and others" + ), + + FirmwareInfo( + {0x8E, 0x21, 0x3F, 0x3E, 0x71, 0xD2, 0xE3, 0xE4, 0x5D, 0x11, 0x69, 0xBA, 0xC6, 0x46, 0x5A, 0x70, + 0xEA, 0xBE, 0xB2, 0x2B, 0x30, 0x3F, 0x1F, 0xA6, 0xD7, 0x67, 0x93, 0x70, 0xFF, 0xAD, 0xF, 0x54}, + 49756, false, "Fairly common firmware. Found in PSMD and others" + ), + + FirmwareInfo( + {0xA2, 0x6C, 0x74, 0xD1, 0xEF, 0x7F, 0x4F, 0xA5, 0xFF, 0xFF, 0xFB, 0xEC, 0x75, 0x8A, 0x40, 0x8D, + 0x8F, 0x22, 0x87, 0x72, 0x78, 0x1B, 0x81, 0x88, 0x86, 0x5F, 0x83, 0x4D, 0x1D, 0x90, 0x6B, 0xAA}, + 48804, false, "Spotted in MK7" + ), + + FirmwareInfo( + {0x75, 0x12, 0x70, 0xB2, 0x43, 0xB0, 0xCA, 0xFB, 0x51, 0x99, 0xF2, 0x98, 0x2, 0x2, 0xC9, 0xB4, + 0xC7, 0x7A, 0x67, 0x5E, 0xF0, 0x43, 0x8F, 0xD5, 0xA8, 0x9E, 0x83, 0xAA, 0xB9, 0xA8, 0x7, 0x9B}, + 48652, false, "One of the most common firmwares. Found in OoT, Pokemon Rumble Blast, and others" + ), + + FirmwareInfo( + {0xF2, 0x96, 0xE2, 0xE5, 0xEC, 0x34, 0x9F, 0x6A, 0x6C, 0xF3, 0xE1, 0xC7, 0xC, 0xDD, 0x65, 0xC2, + 0x2, 0x72, 0xB6, 0xE7, 0xFF, 0xE5, 0x57, 0x92, 0x69, 0x4E, 0x83, 0xAE, 0x24, 0xF1, 0x68, 0xBF}, + 217976, true, "Most common AAC-enabled firmware. Found in Rhythm Heaven, Fire Emblem Fates/Echoes, Pokemon X/Y, and others" + ), + + FirmwareInfo( + {0xF0, 0x6C, 0x1B, 0x59, 0x23, 0xE1, 0x71, 0x19, 0x5, 0x66, 0x59, 0xCB, 0x3D, 0x9B, 0xF0, 0x26, + 0x62, 0x84, 0xE9, 0xA6, 0xC0, 0x8, 0x23, 0x99, 0xD7, 0x45, 0x8D, 0x7C, 0x52, 0xAE, 0x32, 0x1C}, + 48708, false, "Spotted in Super Mario 3D Land" + ), + + FirmwareInfo( + {0x7E, 0xA3, 0xC4, 0x4A, 0x1C, 0x57, 0x51, 0x4B, 0xEB, 0xBE, 0xBC, 0xE8, 0xA7, 0x99, 0x5F, 0x7F, + 0x3A, 0x29, 0x1, 0x70, 0xEA, 0x3B, 0x6C, 0x14, 0x57, 0x49, 0xAD, 0x93, 0x58, 0x67, 0x2C, 0x97}, + 49716, false, "Spotted in PMD: GTI" + ), + }; +} // namespace DSP \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp index 833a1b4e1..93aed1060 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -99,6 +99,7 @@ void EmulatorConfig::load() { audioEnabled = toml::find_or(audio, "EnableAudio", false); aacEnabled = toml::find_or(audio, "EnableAACAudio", true); + printDSPFirmware = toml::find_or(audio, "PrintDSPFirmware", false); audioDeviceConfig.muteAudio = toml::find_or(audio, "MuteAudio", false); // Our volume ranges from 0.0 (muted) to 2.0 (boosted, using a logarithmic scale). 1.0 is the "default" volume, ie we don't adjust the PCM @@ -177,6 +178,7 @@ void EmulatorConfig::save() { data["Audio"]["EnableAACAudio"] = aacEnabled; data["Audio"]["MuteAudio"] = audioDeviceConfig.muteAudio; data["Audio"]["AudioVolume"] = double(audioDeviceConfig.volumeRaw); + data["Audio"]["PrintDSPFirmware"] = printDSPFirmware; data["Battery"]["ChargerPlugged"] = chargerPlugged; data["Battery"]["BatteryPercentage"] = batteryPercentage; diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index 8c5147611..fa03a24f3 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -1,10 +1,18 @@ #include "services/dsp.hpp" -#include "ipc.hpp" -#include "kernel.hpp" + +#include +#include +#include #include +#include #include +#include "config.hpp" +#include "ipc.hpp" +#include "kernel.hpp" +#include "services/dsp_firmware_db.hpp" + namespace DSPCommands { enum : u32 { RecvData = 0x00010040, @@ -92,6 +100,10 @@ void DSPService::loadComponent(u32 messagePointer) { log("DSP::LoadComponent (size = %08X, program mask = %X, data mask = %X\n", size, programMask, dataMask); dsp->loadComponent(loadedComponent, programMask, dataMask); + if (config.printDSPFirmware) { + printFirmwareInfo(); + } + mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, 1); // Component loaded @@ -303,4 +315,57 @@ void DSPService::triggerInterrupt1() { if (interrupt1.has_value()) { kernel.signalEvent(*interrupt1); } -} \ No newline at end of file +} + +struct FirmwareInfo { + using Hash = std::array; + + Hash hash; // Firmware hash (SHA-256) + u32 size; // Firmware size in bytes + + bool supportsAAC; // Does this firmware support AAC decoding? + const char* notes; // Miscellaneous notes about the firmware + + explicit constexpr FirmwareInfo(const Hash& hash, u32 size, bool supportsAAC, const char* notes) + : hash(hash), size(size), supportsAAC(supportsAAC), notes(notes) {} +}; + +void DSPService::printFirmwareInfo() { + // No component has been loaded, do nothing. + if (!loadedComponent.size()) { + return; + } + + const auto& firmwareDB = DSP::firmwareDB; + const usize firmwareSize = loadedComponent.size(); + std::array hash; + + CryptoPP::SHA256 sha; + sha.CalculateDigest(hash.data(), loadedComponent.data(), firmwareSize); + + fmt::print("\nLoaded DSP firmware\n"); + fmt::print("Firmware SHA-256 hash: {:X}\n", fmt::join(hash, "")); + fmt::print("Size: {} bytes ({} KB)\n", firmwareSize, firmwareSize / 1024); + + bool knownFirmware = false; + + for (int i = 0; i < firmwareDB.size(); i++) { + const auto& entry = firmwareDB[i]; + if (entry.size == firmwareSize && std::memcmp(hash.data(), entry.hash.data(), hash.size()) == 0) { + knownFirmware = true; + fmt::print( + "Firmware found in DSP firmware DB.\nFeatures AAC decoder: {}\nOther notes: {}\n", entry.supportsAAC ? "yes" : "no", entry.notes + ); + + break; + } + } + + if (!knownFirmware) { + fmt::print("Firmware not found in DSP firmware DB.\nHash in case you want to add it to the DB: {{{:#X}}}\n", fmt::join(hash, ", ")); + // DSP firmwares that feature AAC decoding are usually around 210KB as opposed to the average DSP firmware which is around 48KB + fmt::print("Features AAC decoder: {}\n", firmwareSize >= 200_KB ? "probably yes" : "probably not"); + } + + fmt::print("\n"); +} diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 31e3d702a..8fa88a001 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -7,7 +7,7 @@ ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem), - csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), + csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel, config), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {}