Allow RTPs to be loaded from archives as well in libretro builds

This commit is contained in:
刘皓 2025-05-13 16:17:04 -04:00
parent 75b953d4c8
commit 714c583b9f
No known key found for this signature in database
GPG key ID: 7901753DB465B711
2 changed files with 107 additions and 63 deletions

View file

@ -20,6 +20,7 @@
*/ */
#include <atomic> #include <atomic>
#include <cctype>
#include <cstdarg> #include <cstdarg>
#include <cstring> #include <cstring>
#include <string> #include <string>
@ -444,14 +445,13 @@ extern const uint8_t dist_zip[];
extern const size_t dist_zip_len; extern const size_t dist_zip_len;
static bool initialized = false; static bool initialized = false;
static ALCdevice *al_device = NULL; static ALCdevice *al_device = nullptr;
static ALCcontext *al_context = NULL; static ALCcontext *al_context = nullptr;
static LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT = NULL; static LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT = nullptr;
static LPALCLOOPBACKOPENDEVICESOFT alcLoopbackOpenDeviceSOFT = NULL; static LPALCLOOPBACKOPENDEVICESOFT alcLoopbackOpenDeviceSOFT = nullptr;
static int16_t *sound_buf = NULL; static int16_t *sound_buf = nullptr;
static bool retro_framebuffer_supported; static bool retro_framebuffer_supported;
static bool dupe_supported; static bool dupe_supported;
static PHYSFS_File *rgssad = NULL;
static retro_system_av_info av_info; static retro_system_av_info av_info;
static struct retro_audio_callback audio_callback; static struct retro_audio_callback audio_callback;
static struct retro_frame_time_callback frame_time_callback = { static struct retro_frame_time_callback frame_time_callback = {
@ -599,25 +599,21 @@ static void deinit_sandbox() {
shared_state_initialized = false; shared_state_initialized = false;
struct lock_guard guard(threaded_audio_mutex); struct lock_guard guard(threaded_audio_mutex);
if (sound_buf != NULL) { if (sound_buf != nullptr) {
mkxp_aligned_free(sound_buf); mkxp_aligned_free(sound_buf);
sound_buf = NULL; sound_buf = nullptr;
} }
mkxp_retro::sandbox.reset(); mkxp_retro::sandbox.reset();
thread_data.reset(); thread_data.reset();
input.reset(); input.reset();
audio.reset(); audio.reset();
if (al_context != NULL) { if (al_context != nullptr) {
alcDestroyContext(al_context); alcDestroyContext(al_context);
al_context = NULL; al_context = nullptr;
} }
if (al_device != NULL) { if (al_device != nullptr) {
alcCloseDevice(al_device); alcCloseDevice(al_device);
al_device = NULL; al_device = nullptr;
}
if (rgssad != NULL) {
PHYSFS_close(rgssad);
rgssad = NULL;
} }
conf.reset(); conf.reset();
fs.reset(); fs.reset();
@ -626,7 +622,7 @@ static void deinit_sandbox() {
static bool init_sandbox() { static bool init_sandbox() {
deinit_sandbox(); deinit_sandbox();
fs.emplace((const char *)NULL, false); fs.emplace(nullptr, false);
{ {
std::string parsed_game_path(game_path); std::string parsed_game_path(game_path);
@ -738,30 +734,38 @@ static bool init_sandbox() {
} }
SharedState::rgssVersion = conf->rgssVersion; SharedState::rgssVersion = conf->rgssVersion;
thread_data.emplace((EventThread *)NULL, (const char *)NULL, (SDL_Window *)NULL, (ALCdevice *)NULL, 60, 1, *conf); thread_data.emplace(nullptr, nullptr, nullptr, nullptr, 60, 1, *conf);
if ((rgssad = PHYSFS_openRead(("/game/" + conf->execName + ".rgssad").c_str())) != NULL) { PHYSFS_File *rgssad;
if ((rgssad = PHYSFS_openRead(("/game/" + conf->execName + ".rgssad").c_str())) != nullptr) {
PHYSFS_mountHandle(rgssad, ('/' + conf->execName + ".rgssad").c_str(), "/game", 1); PHYSFS_mountHandle(rgssad, ('/' + conf->execName + ".rgssad").c_str(), "/game", 1);
} else if ((rgssad = PHYSFS_openRead(("/game/" + conf->execName + ".rgss2a").c_str())) != NULL) { } else if ((rgssad = PHYSFS_openRead(("/game/" + conf->execName + ".rgss2a").c_str())) != nullptr) {
PHYSFS_mountHandle(rgssad, ('/' + conf->execName + ".rgss2a").c_str(), "/game", 1); PHYSFS_mountHandle(rgssad, ('/' + conf->execName + ".rgss2a").c_str(), "/game", 1);
} else if ((rgssad = PHYSFS_openRead(("/game/" + conf->execName + ".rgss3a").c_str())) != NULL) { } else if ((rgssad = PHYSFS_openRead(("/game/" + conf->execName + ".rgss3a").c_str())) != nullptr) {
PHYSFS_mountHandle(rgssad, ('/' + conf->execName + ".rgss3a").c_str(), "/game", 1); PHYSFS_mountHandle(rgssad, ('/' + conf->execName + ".rgss3a").c_str(), "/game", 1);
} }
PHYSFS_mountMemory(dist_zip, dist_zip_len, NULL, "/dist.zip", "/dist", 1); PHYSFS_mountMemory(dist_zip, dist_zip_len, nullptr, "/dist.zip", "/dist", 1);
} }
{ {
const char *system_path; const char *system_path;
if (environment(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_path) && system_path != nullptr) { if (environment(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_path) && system_path != nullptr) {
std::string rtp_root_path(system_path); std::string system_root_path(system_path);
#ifdef _WIN32 #ifdef _WIN32
rtp_root_path.append("\\mkxp-z\\RTP"); system_root_path.append("\\mkxp-z");
#else #else
rtp_root_path.append("/mkxp-z/RTP"); system_root_path.append("/mkxp-z");
#endif // _WIN32 #endif // _WIN32
// Create the subdirectory if needed std::string rtp_root_path(system_root_path);
#ifdef _WIN32
rtp_root_path.append("\\RTP");
#else
rtp_root_path.append("/RTP");
#endif // _WIN32
// Create the RTP root directory if needed
PHYSFS_setWriteDir(system_path); PHYSFS_setWriteDir(system_path);
if (!PHYSFS_mkdir(rtp_root_path.c_str() + std::strlen(system_path) + 1)) { if (!PHYSFS_mkdir(rtp_root_path.c_str() + std::strlen(system_path) + 1)) {
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Failed to create directory at \"%s\": %s\n", rtp_root_path.c_str(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); mkxp_retro::log_printf(RETRO_LOG_ERROR, "Failed to create directory at \"%s\": %s\n", rtp_root_path.c_str(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
@ -769,43 +773,80 @@ static bool init_sandbox() {
return false; return false;
} }
PHYSFS_mount(system_root_path.c_str(), "/system", true);
// Mount each RTP needed by the game to the game directory // Mount each RTP needed by the game to the game directory
PHYSFS_mount(rtp_root_path.c_str(), "/rtp", true);
for (const std::string &rtp : conf->game.rtps) { for (const std::string &rtp : conf->game.rtps) {
struct data { struct data {
std::string rtp_root_path;
std::string rtp; std::string rtp;
std::string desensitized_rtp; std::string rtp_lowercase;
} data; bool found;
data.rtp = rtp; } data = {
for (char &c : data.rtp) { .rtp_root_path = rtp_root_path,
.rtp = rtp,
.rtp_lowercase = rtp,
.found = false,
};
for (char &c : data.rtp_lowercase) {
c = std::tolower(c); c = std::tolower(c);
} }
PHYSFS_enumerate("/rtp", [](void *data, const char *origdir, const char *fname) { PHYSFS_enumerate("/system/RTP", [](void *data_, const char *origdir, const char *fname) {
struct data *data_ = (struct data *)data; struct data &data = *(struct data *)data_;
std::string rtp(fname); std::string rtp(fname);
for (char &c : rtp) { for (char &c : rtp) {
c = std::tolower(c); c = std::tolower(c);
} }
if (data_->rtp == rtp) {
data_->desensitized_rtp = fname; // Make sure this file/directory has a filename that matches the one we're looking for (case-insensitive)
return PHYSFS_ENUM_STOP; if (std::strncmp(rtp.c_str(), data.rtp_lowercase.c_str(), data.rtp_lowercase.length()) || (rtp[data.rtp_lowercase.length()] != '.' && rtp[data.rtp_lowercase.length()] != 0)) {
} else {
return PHYSFS_ENUM_OK; return PHYSFS_ENUM_OK;
} }
}, &data);
if (!data.desensitized_rtp.empty()) { // Check if this is a file or directory
std::string rtp_path(rtp_root_path); std::string fullpath(origdir);
fullpath.push_back('/');
fullpath.append(fname);
PHYSFS_Stat stat;
if (!PHYSFS_stat(fullpath.c_str(), &stat) || (stat.filetype != PHYSFS_FILETYPE_DIRECTORY && stat.filetype != PHYSFS_FILETYPE_REGULAR)) {
return PHYSFS_ENUM_OK;
}
std::string rtp_path(data.rtp_root_path);
#ifdef _WIN32 #ifdef _WIN32
rtp_path.push_back('\\'); rtp_path.push_back('\\');
#else #else
rtp_path.push_back('/'); rtp_path.push_back('/');
#endif // _WIN32 #endif // _WIN32
rtp_path.append(data.desensitized_rtp); rtp_path.append(fname);
log_printf(RETRO_LOG_INFO, "Mounted RTP \"%s\" from \"%s\"\n", rtp.c_str(), rtp_path.c_str());
PHYSFS_mount(rtp_path.c_str(), "/game", true); if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) {
// If it's a directory, just mount the path directly
if (!PHYSFS_mount(rtp_path.c_str(), "/game", true)) {
return PHYSFS_ENUM_OK;
}
} else { } else {
// If it's a file, try to open it as an archive and then mount it
std::string path(origdir);
path.push_back('/');
path.append(fname);
PHYSFS_File *file = PHYSFS_openRead(path.c_str());
if (file == nullptr) {
return PHYSFS_ENUM_OK;
}
if (!PHYSFS_mountHandle(file, path.c_str(), "/game", true)) {
PHYSFS_close(file);
return PHYSFS_ENUM_OK;
}
}
data.found = true;
log_printf(RETRO_LOG_INFO, "Mounted RTP \"%s\" from \"%s\"\n", data.rtp.c_str(), rtp_path.c_str());
return PHYSFS_ENUM_STOP;
}, &data);
if (!data.found) {
log_printf( log_printf(
RETRO_LOG_ERROR, RETRO_LOG_ERROR,
( (
@ -823,7 +864,6 @@ static bool init_sandbox() {
); );
} }
} }
PHYSFS_unmount("/rtp");
} }
} }
@ -882,22 +922,22 @@ static bool init_sandbox() {
} }
} }
alcLoopbackOpenDeviceSOFT = (LPALCLOOPBACKOPENDEVICESOFT)alcGetProcAddress(NULL, "alcLoopbackOpenDeviceSOFT"); alcLoopbackOpenDeviceSOFT = (LPALCLOOPBACKOPENDEVICESOFT)alcGetProcAddress(nullptr, "alcLoopbackOpenDeviceSOFT");
if (alcLoopbackOpenDeviceSOFT == NULL) { if (alcLoopbackOpenDeviceSOFT == nullptr) {
log_printf(RETRO_LOG_ERROR, "OpenAL implementation does not support `alcLoopbackOpenDeviceSOFT`\n"); log_printf(RETRO_LOG_ERROR, "OpenAL implementation does not support `alcLoopbackOpenDeviceSOFT`\n");
deinit_sandbox(); deinit_sandbox();
return false; return false;
} }
alcRenderSamplesSOFT = (LPALCRENDERSAMPLESSOFT)alcGetProcAddress(NULL, "alcRenderSamplesSOFT"); alcRenderSamplesSOFT = (LPALCRENDERSAMPLESSOFT)alcGetProcAddress(nullptr, "alcRenderSamplesSOFT");
if (alcRenderSamplesSOFT == NULL) { if (alcRenderSamplesSOFT == nullptr) {
log_printf(RETRO_LOG_ERROR, "OpenAL implementation does not support `alcRenderSamplesSOFT`\n"); log_printf(RETRO_LOG_ERROR, "OpenAL implementation does not support `alcRenderSamplesSOFT`\n");
deinit_sandbox(); deinit_sandbox();
return false; return false;
} }
al_device = alcLoopbackOpenDeviceSOFT(NULL); al_device = alcLoopbackOpenDeviceSOFT(nullptr);
if (al_device == NULL) { if (al_device == nullptr) {
log_printf(RETRO_LOG_ERROR, "Failed to initialize OpenAL loopback device\n"); log_printf(RETRO_LOG_ERROR, "Failed to initialize OpenAL loopback device\n");
deinit_sandbox(); deinit_sandbox();
return false; return false;
@ -913,17 +953,17 @@ static bool init_sandbox() {
0, 0,
}; };
al_context = alcCreateContext(al_device, al_attrs); al_context = alcCreateContext(al_device, al_attrs);
if (al_context == NULL || alcMakeContextCurrent(al_context) == AL_FALSE) { if (al_context == nullptr || alcMakeContextCurrent(al_context) == AL_FALSE) {
log_printf(RETRO_LOG_ERROR, "Failed to create OpenAL context\n"); log_printf(RETRO_LOG_ERROR, "Failed to create OpenAL context\n");
deinit_sandbox(); deinit_sandbox();
return false; return false;
} }
fluid_set_log_function(FLUID_PANIC, fluid_log, NULL); fluid_set_log_function(FLUID_PANIC, fluid_log, nullptr);
fluid_set_log_function(FLUID_ERR, fluid_log, NULL); fluid_set_log_function(FLUID_ERR, fluid_log, nullptr);
fluid_set_log_function(FLUID_WARN, fluid_log, NULL); fluid_set_log_function(FLUID_WARN, fluid_log, nullptr);
fluid_set_log_function(FLUID_INFO, fluid_log, NULL); fluid_set_log_function(FLUID_INFO, fluid_log, nullptr);
fluid_set_log_function(FLUID_DBG, fluid_log, NULL); fluid_set_log_function(FLUID_DBG, fluid_log, nullptr);
audio.emplace(*thread_data); audio.emplace(*thread_data);
@ -950,7 +990,7 @@ static bool init_sandbox() {
frame_time_callback_enabled = environment(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, &frame_time_callback); frame_time_callback_enabled = environment(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, &frame_time_callback);
sound_buf = (int16_t *)mkxp_aligned_malloc(16, (threaded_audio_enabled ? THREADED_AUDIO_SAMPLES : (size_t)std::ceil((double)SYNTH_SAMPLERATE / av_info.timing.fps)) * 2 * sizeof(int16_t)); sound_buf = (int16_t *)mkxp_aligned_malloc(16, (threaded_audio_enabled ? THREADED_AUDIO_SAMPLES : (size_t)std::ceil((double)SYNTH_SAMPLERATE / av_info.timing.fps)) * 2 * sizeof(int16_t));
if (sound_buf == NULL) { if (sound_buf == nullptr) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
@ -1247,7 +1287,7 @@ extern "C" RETRO_API void retro_run() {
gl.UseProgram(0); gl.UseProgram(0);
gl.ActiveTexture(GL_TEXTURE0); gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(GL_TEXTURE_2D, 0); gl.BindTexture(GL_TEXTURE_2D, 0);
if (gl.BindVertexArray != NULL) { if (gl.BindVertexArray != nullptr) {
gl.BindVertexArray(0); gl.BindVertexArray(0);
} }
gl.BindFramebuffer(GL_FRAMEBUFFER, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
@ -1308,7 +1348,7 @@ extern "C" RETRO_API void retro_cheat_set(unsigned int index, bool enabled, cons
} }
extern "C" RETRO_API bool retro_load_game(const struct retro_game_info *info) { extern "C" RETRO_API bool retro_load_game(const struct retro_game_info *info) {
if (info == NULL || info->path == NULL) { if (info == nullptr || info->path == nullptr) {
log_printf(RETRO_LOG_ERROR, "This core cannot start without a game\n"); log_printf(RETRO_LOG_ERROR, "This core cannot start without a game\n");
return false; return false;
} }
@ -1322,7 +1362,7 @@ extern "C" RETRO_API bool retro_load_game(const struct retro_game_info *info) {
std::memset(&hw_render, 0, sizeof hw_render); std::memset(&hw_render, 0, sizeof hw_render);
hw_render.context_reset = initGLFunctions; hw_render.context_reset = initGLFunctions;
hw_render.context_destroy = NULL; hw_render.context_destroy = nullptr;
hw_render.cache_context = true; hw_render.cache_context = true;
hw_render.bottom_left_origin = true; hw_render.bottom_left_origin = true;
if (hw_render.context_type = RETRO_HW_CONTEXT_OPENGL_CORE, hw_render.version_major = 4, hw_render.version_minor = 6, environment(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) { if (hw_render.context_type = RETRO_HW_CONTEXT_OPENGL_CORE, hw_render.version_major = 4, hw_render.version_minor = 6, environment(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) {
@ -1422,7 +1462,7 @@ extern "C" RETRO_API unsigned int retro_get_region() {
} }
extern "C" RETRO_API void *retro_get_memory_data(unsigned int id) { extern "C" RETRO_API void *retro_get_memory_data(unsigned int id) {
return NULL; return nullptr;
} }
extern "C" RETRO_API size_t retro_get_memory_size(unsigned int id) { extern "C" RETRO_API size_t retro_get_memory_size(unsigned int id) {

View file

@ -417,9 +417,13 @@ struct CacheEnumData {
static PHYSFS_EnumerateCallbackResult cacheEnumCB(void *d, const char *origdir, static PHYSFS_EnumerateCallbackResult cacheEnumCB(void *d, const char *origdir,
const char *fname) { const char *fname) {
#ifndef MKXPZ_RETRO
if (shState && shState->rtData().rqTerm) if (shState && shState->rtData().rqTerm)
throw Exception(Exception::MKXPError, "Game close requested. Aborting path cache enumeration."); throw Exception(Exception::MKXPError, "Game close requested. Aborting path cache enumeration.");
#ifdef MKXPZ_RETRO
// Don't cache the /dist or /system directories because the game doesn't need to access them
if (!*origdir && (!strcmp(fname, "dist") || !strcmp(fname, "system")))
return PHYSFS_ENUM_OK;
#endif // MKXPZ_RETRO #endif // MKXPZ_RETRO
CacheEnumData &data = *static_cast<CacheEnumData *>(d); CacheEnumData &data = *static_cast<CacheEnumData *>(d);