Better Modloading Support

This commit is contained in:
DepressedTWM 2026-08-21 10:39:24 +04:00
parent 48dda7da8c
commit 95ab9c4434
9 changed files with 139 additions and 50 deletions

View file

@ -326,6 +326,7 @@ set(BINDING_SOURCE
binding-mri/gamepad-binding.cpp binding-mri/gamepad-binding.cpp
binding-mri/profiler-binding.cpp binding-mri/profiler-binding.cpp
binding-mri/audioplayback-binding.cpp binding-mri/audioplayback-binding.cpp
binding-mri/physfs-binding.cpp
) )
source_group("Binding Source" FILES ${BINDING_SOURCE} ${BINDING_HEADERS}) source_group("Binding Source" FILES ${BINDING_SOURCE} ${BINDING_HEADERS})

View file

@ -95,6 +95,7 @@ void shaderBindingInit();
void keybindingsBindingInit(); void keybindingsBindingInit();
void lightmapBindingInit(); void lightmapBindingInit();
void ProfilerInit(); void ProfilerInit();
void PhysFS_binding_init();
RB_METHOD(mriPrint); RB_METHOD(mriPrint);
RB_METHOD(mriP); RB_METHOD(mriP);
@ -150,6 +151,7 @@ static void mriBindingInit(){
keybindingsBindingInit(); keybindingsBindingInit();
lightmapBindingInit(); lightmapBindingInit();
ProfilerInit(); ProfilerInit();
PhysFS_binding_init();
_rb_define_module_function(rb_mKernel, "rgss_main", mriRgssMain); _rb_define_module_function(rb_mKernel, "rgss_main", mriRgssMain);
_rb_define_module_function(rb_mKernel, "rgss_stop", mriRgssStop); _rb_define_module_function(rb_mKernel, "rgss_stop", mriRgssStop);

View file

@ -1,25 +1,14 @@
//https://silverhammermba.github.io/emberb/c/ //https://silverhammermba.github.io/emberb/c/
//https://docs.ruby-lang.org/capi/en/master/d8/d68/include_2ruby_2internal_2intern_2string_8h.html //https://docs.ruby-lang.org/capi/en/master/d8/d68/include_2ruby_2internal_2intern_2string_8h.html
#include "binding-util.h" #include "binding-util.h"
#include "sharedstate.h"
#include "filesystem.h"
#include "util.h" #include "util.h"
#include "debugwriter.h" #include "debugwriter.h"
#include "ruby/encoding.h"
#include "ruby/intern.h"
#include "ruby/thread.h"
#include "modloader.h" #include "modloader.h"
#include "config.h" #include "config.h"
#include <ruby.h> #include <ruby.h>
#include <filesystem> #include <physfs.h>
#include <fstream>
#include <sstream>
#include <system_error>
#include <vector>
#include <string> #include <string>
#include <vector>
namespace fs = std::filesystem;
VALUE meow(const std::vector<std::string> vec){ VALUE meow(const std::vector<std::string> vec){
VALUE ary = rb_ary_new_capa((long)vec.size()); VALUE ary = rb_ary_new_capa((long)vec.size());
for (const std::string &s : vec) { for (const std::string &s : vec) {
@ -29,7 +18,7 @@ VALUE meow(const std::vector<std::string> vec){
return ary; return ary;
} }
static VALUE hooks(int argc, VALUE *argv, VALUE self) { static VALUE hooks(int argc, VALUE *argv, VALUE self){
VALUE v_path = Qnil; VALUE v_path = Qnil;
rb_scan_args(argc, argv, "01", &v_path); rb_scan_args(argc, argv, "01", &v_path);
std::string hook_path = "hooks"; std::string hook_path = "hooks";
@ -38,23 +27,54 @@ static VALUE hooks(int argc, VALUE *argv, VALUE self) {
hook_path = StringValueCStr(v_path); hook_path = StringValueCStr(v_path);
} }
std::vector<std::string> files = {}; std::vector<std::string> files;
if(!std::filesystem::exists(hook_path)){ if (!PHYSFS_exists(hook_path.c_str())) {
return meow(files); return meow(files);
} }
for (const auto &entry : std::filesystem::directory_iterator(hook_path, std::filesystem::directory_options::skip_permission_denied)) {
std::error_code ec; PHYSFS_Stat dir_stat{};
auto p = entry.path();
if (!std::filesystem::is_regular_file(p, ec) || ec) continue; if (
PHYSFS_stat(hook_path.c_str(), &dir_stat) == 0 ||
auto ext = p.extension().string(); dir_stat.filetype != PHYSFS_FILETYPE_DIRECTORY
if (ext != ".rb") continue; ) {
return meow(files);
std::string full = p.string(); }
Debug() << "[MODLOADER] Executing hook: " << full;
files.push_back(full); char **list = PHYSFS_enumerateFiles(hook_path.c_str());
}
return meow(files); if (list == nullptr) {
return meow(files);
}
for (char **entry = list; *entry != nullptr; ++entry) {
std::string name = *entry;
std::string full_path = hook_path;
if (!full_path.empty() && full_path.back() != '/') {
full_path += '/';
}
full_path += name;
PHYSFS_Stat file_stat{};
if (PHYSFS_stat(full_path.c_str(), &file_stat) == 0) {
continue;
}
if (file_stat.filetype != PHYSFS_FILETYPE_REGULAR) {
continue;
}
if (name.size() < 3 ||
name.compare(name.size() - 3, 3, ".rb") != 0) {
continue;
}
Debug() << "[MODLOADER] Executing hook: " << full_path;
files.push_back(full_path);
}
PHYSFS_freeList(list);
return meow(files);
} }
void ModLoaderBindingInit(){ void ModLoaderBindingInit(){

View file

@ -1,6 +1,5 @@
module Kernel module Kernel
alias_method :original_puts, :puts alias_method :original_puts, :puts
def puts(*args) def puts(*args)
string = "" string = ""
args.each do |arg| args.each do |arg|
@ -9,7 +8,6 @@ module Kernel
MKXP.puts string MKXP.puts string
return nil return nil
end end
module_function :puts module_function :puts
end end
@ -17,15 +15,7 @@ module RPG
module Cache module Cache
@cache = {} @cache = {}
def self.load_bitmap(folder_name, filename, hue = 0) def self.load_bitmap(folder_name, filename, hue = 0)
if ModLoader::IS_ENABLED path = Graphics.adapted_file(folder_name + filename)
if File.exist?("/mod-storage/" + folder_name + filename)
path = Graphics.adapted_file("/mod-storage/" + folder_name + filename)
else
path = Graphics.adapted_file(folder_name + filename)
end
else
path = Graphics.adapted_file(folder_name + filename)
end
if not @cache.include?(path) or @cache[path].disposed? if not @cache.include?(path) or @cache[path].disposed?
if filename != "" if filename != ""
@cache[path] = Bitmap.new(path) @cache[path] = Bitmap.new(path)
@ -1323,7 +1313,7 @@ module RPG
return unless ModLoader::IS_ENABLED return unless ModLoader::IS_ENABLED
return unless path.is_a?(String) return unless path.is_a?(String)
ModLoader.hooks(path).each do |item| ModLoader.hooks(path).each do |item|
eval(File.read(item), b) eval(PhysFS.read(item), b)
end end
end end
end end

View file

@ -0,0 +1,70 @@
#include <physfs.h>
#include "binding-util.h"
#include "sharedstate.h"
#include "filesystem.h"
#include "util.h"
#include <ruby.h>
#include <stdexcept>
#include <string>
VALUE module = Qnil;
VALUE rb_ePhysFSError = Qnil;
std::string toStdString(VALUE v) {
Check_Type(v, T_STRING);
return std::string{ RSTRING_PTR(v), static_cast<std::size_t>(RSTRING_LEN(v)) };
}
static VALUE rb_PhysFS_Exist(VALUE, VALUE path) {
if(PHYSFS_exists(StringValuePtr(path)) > 0){
return Qtrue;
}else{
return Qfalse;
}
}
static VALUE rb_PhysFS_Directory(VALUE, VALUE path) {
if(PHYSFS_isDirectory(StringValuePtr(path)) > 0){
return Qtrue;
}else{
return Qfalse;
}
}
std::vector<char> loadFully(const char* path) {
PHYSFS_File* file = PHYSFS_openRead(path);
if (!file){
WarnMsg("Failed to load PhysFS file %s", path);
return {};
}
const auto length = PHYSFS_fileLength(file);
std::vector<char> buffer;
if (length > 0) {
buffer.resize(static_cast<std::size_t>(length));
const auto got = PHYSFS_readBytes(file, buffer.data(), static_cast<PHYSFS_uint64>(length));
if (got != length && !PHYSFS_eof(file)) {
PHYSFS_close(file);
WarnMsg("PhysFS readBytes failed for '%s'", path);
}
buffer.resize(static_cast<std::size_t>(got));
}
PHYSFS_close(file);
return buffer;
}
static VALUE rb_PhysFS_Read(VALUE, VALUE path) {
try {
const auto buf = loadFully(StringValuePtr(path));
return rb_str_new(buf.data(), static_cast<long>(buf.size()));
} catch (const std::exception& e) {
rb_raise(rb_ePhysFSError, "%s", e.what());
}
return Qnil;
}
void PhysFS_binding_init(){
VALUE module = rb_define_module("PhysFS");
rb_ePhysFSError = rb_define_class_under(module, "Error", rb_eStandardError);
rb_define_module_function(module, "exist?", rb_PhysFS_Exist, 1);
rb_define_module_function(module, "directory?", rb_PhysFS_Directory, 1);
rb_define_module_function(module, "read", rb_PhysFS_Read, 1);
}

View file

@ -40,9 +40,13 @@ module Graphics
def self.adapted_file(path, extention = ".png") def self.adapted_file(path, extention = ".png")
file_tag = RESOLUTIONS[Settings[:resolution] || 999]&.[](:file_tag) || "" file_tag = RESOLUTIONS[Settings[:resolution] || 999]&.[](:file_tag) || ""
if File.exist?(path + file_tag + extention) if PhysFS.exist?(path + file_tag + extention)
return path + file_tag return path + file_tag
else
if File.exist?(path + file_tag + extention)
return path + file_tag
end
end end
path path
end end
end end

0
scripts/Graphics.rb Normal file
View file

View file

@ -17,6 +17,7 @@ end
begin begin
RPG::Mod.exec_hooks("hooks/Main/start", binding) RPG::Mod.exec_hooks("hooks/Main/start", binding)
print PhysFS.exist?("gaysex")
$console = Graphics.fullscreen $console = Graphics.fullscreen
Graphics.frame_rate = 60 Graphics.frame_rate = 60
Font.default_size = 20 Font.default_size = 20

View file

@ -15,11 +15,12 @@
#include <SDL3/SDL_video.h> #include <SDL3/SDL_video.h>
#include <SDL3/SDL_timer.h> #include <SDL3/SDL_timer.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <physfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "the_modded_machine.png.xxd" #include "the_modded_machine.png.xxd"
namespace fs = std::filesystem; namespace fs = std::filesystem;
static bool stop_render = false; static bool stop_render = false;
const static std::size_t N = 46; const static std::size_t N = 46;
@ -94,7 +95,7 @@ void ModLoader(Config conf, SDL_Window* win){
std::string full = p.string(); std::string full = p.string();
if (ext == ".zip") { if (ext == ".zip") {
int ok = PHYSFS_mount(full.c_str(), "/mod-storage", 0); int ok = PHYSFS_mount(full.c_str(), "", 0);
mod_list.emplace_back(full); mod_list.emplace_back(full);
modloader_add_to_log("Added mod " + full); modloader_add_to_log("Added mod " + full);
mods_count++; mods_count++;