mirror of
https://forgejo.xatacraft.ru/Sunshine/mkxp-sunshine.git
synced 2026-08-19 13:29:54 +00:00
ModLoader and hooks
This commit is contained in:
parent
d978fae6cf
commit
3ed5988375
61 changed files with 3304 additions and 2899 deletions
|
|
@ -6,14 +6,15 @@ include(FindPackageHandleStandardArgs)
|
|||
option(STEAM "Build for Steam" OFF)
|
||||
option(DEBUG "Debug mode" OFF)
|
||||
option(FORCE32 "Force 32bit compile on 64bit OS" OFF) # from MKXP
|
||||
option(NATIVE "Use native instructions(for local use only)" ON)
|
||||
option(PROFILE "Extra optimization" ON)
|
||||
option(NATIVE "Use native instructions(for local use only)" OFF)
|
||||
option(PROFILE "Extra optimization" OFF)
|
||||
option(EXTRA_SECURITY "Extra security" OFF)
|
||||
|
||||
set(CMAKE_DISABLE_IN_SOURCE_BUILD TRUE)
|
||||
set(VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION "5.1.2600.0")
|
||||
set(STEAMWORKS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/steamworks" CACHE PATH "Path to Steamworks folder")
|
||||
set(ZLIB_USE_STATIC_LIBS ON)
|
||||
set(OPENSSL_USE_STATIC_LIBS ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
# from MKXP
|
||||
|
|
@ -53,12 +54,6 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(PROFILE)
|
||||
if (!MSVC)
|
||||
add_compile_options(-fprofile-generate -fprofile-use)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(EXTRA_SECURITY)
|
||||
if (MSVC)
|
||||
add_compile_options(/guard:cf /GS /sdl /DYNAMICBASE /HIGHENTROPYVA /SAFESEH /guard:ehcont)
|
||||
|
|
@ -128,6 +123,7 @@ set(MAIN_HEADERS
|
|||
src/pipe.h
|
||||
src/i18n.h
|
||||
src/meow.h
|
||||
src/modloader.h
|
||||
)
|
||||
|
||||
set(MAIN_SOURCE
|
||||
|
|
@ -172,6 +168,7 @@ set(MAIN_SOURCE
|
|||
src/screen.cpp
|
||||
src/i18n.cpp
|
||||
src/meow.cpp
|
||||
src/modloader.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
|
@ -333,6 +330,7 @@ set(BINDING_SOURCE
|
|||
binding-mri/niko-binding.cpp
|
||||
binding-mri/time-binding.cpp
|
||||
binding-mri/shader-binging.cpp
|
||||
binding-mri/modloader-binding.cpp
|
||||
)
|
||||
|
||||
source_group("Binding Source" FILES ${BINDING_SOURCE} ${BINDING_HEADERS})
|
||||
|
|
@ -355,6 +353,7 @@ if(LINUX)
|
|||
target_link_libraries(${PROJECT_NAME} PRIVATE ${SECCOMP_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(SDL3 CONFIG)
|
||||
find_package(SDL3_image CONFIG)
|
||||
|
|
@ -363,6 +362,7 @@ find_package(SDL3_ttf CONFIG)
|
|||
find_package(OpenAL CONFIG)
|
||||
find_package(Boost REQUIRED COMPONENTS program_options chrono)
|
||||
find_package(PhysFS CONFIG)
|
||||
find_package(OpenSSL CONFIG REQUIRED COMPONENTS Crypto)
|
||||
find_path(PIXMAN_INCLUDE_DIR NAMES pixman.h PATH_SUFFIXES pixman-1)
|
||||
find_library(PIXMAN_LIBRARY NAMES pixman-1 pixman-1_static pixman-1_staticd)
|
||||
find_package(Ruby 3.0 REQUIRED COMPONENTS Development)
|
||||
|
|
@ -370,6 +370,7 @@ pkg_check_modules(SIGC2 REQUIRED sigc++-2.0)
|
|||
pkg_check_modules(VORBISFILE REQUIRED vorbisfile)
|
||||
find_package_handle_standard_args(pixman-1 DEFAULT_MSG PIXMAN_LIBRARY PIXMAN_INCLUDE_DIR)
|
||||
mark_as_advanced(PIXMAN_INCLUDE_DIR PIXMAN_LIBRARY)
|
||||
pkg_check_modules(LIBZIP REQUIRED libzip)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
|
|
@ -379,7 +380,8 @@ target_include_directories(${PROJECT_NAME}
|
|||
PRIVATE
|
||||
src
|
||||
include
|
||||
|
||||
${LIBZIP_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
${PIXMAN_INCLUDE_DIR}
|
||||
${SIGC2_INCLUDE_DIRS}
|
||||
${Ruby_INCLUDE_DIRS}
|
||||
|
|
@ -390,8 +392,10 @@ target_include_directories(${PROJECT_NAME}
|
|||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
SDL3::SDL3 SDL3_image::SDL3_image SDL3_sound::SDL3_sound SDL3_ttf::SDL3_ttf
|
||||
OpenSSL::Crypto
|
||||
OpenAL::OpenAL
|
||||
physfs
|
||||
${LIBZIP_LIBRARIES}
|
||||
${PIXMAN_LIBRARY}
|
||||
${SIGC2_LIBRARIES}
|
||||
${PLATFORM_LIBRARIES}
|
||||
|
|
|
|||
24
MODLOADER.md
Normal file
24
MODLOADER.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# wtf is hook?
|
||||
Hook - Ruby script executed in other script, with context(binding) of parent script.
|
||||
|
||||
# Hooks dirs
|
||||
For more information you can read scripts/
|
||||
* hooks/*/init/ - executed in script after initialization (you can change some variables and other)
|
||||
'*' - class like Scene_Title
|
||||
* hooks/Window_NameInput/init2 - Window_NameInput init function
|
||||
* hooks/Scene_Map/main - main function of Map scene
|
||||
* hooks/Main/at_exit - on game exit
|
||||
* hooks/Main/start - at game start
|
||||
|
||||
# How add custom hooks?
|
||||
use RPG::Mod.exec_hooks(path, binding)
|
||||
path - path in game directory like "hooks/Scene_Title/init", you can't load something from other place, only from game direcory.
|
||||
binding - its a Ruby binding.
|
||||
|
||||
# API
|
||||
Custom API for mods:
|
||||
|
||||
work in progress
|
||||
|
||||
# how create mod?
|
||||
First what you need it
|
||||
|
|
@ -11,7 +11,7 @@ Target of sunshine mod - improve original game.
|
|||
* Cmake
|
||||
* C/C++ compiler
|
||||
* xxd
|
||||
* Ruby (3 or higher)
|
||||
* Ruby 3+
|
||||
* Boost
|
||||
* SDL3
|
||||
* pixman
|
||||
|
|
@ -21,6 +21,8 @@ Target of sunshine mod - improve original game.
|
|||
* OpenAL
|
||||
* PhysFS
|
||||
* sigc++-2.0
|
||||
* OpenSSL
|
||||
* libzip
|
||||
* GTK3(Linux only!)
|
||||
* libxfconf(Linux only!)
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ void oneshotBindingInit();
|
|||
void SunshineBindingInit();
|
||||
void steamBindingInit();
|
||||
void shaderBindingInit();
|
||||
void ModLoaderBindingInit();
|
||||
|
||||
RB_METHOD(mriPrint);
|
||||
RB_METHOD(mriP);
|
||||
|
|
@ -119,6 +120,7 @@ static void mriBindingInit(){
|
|||
SunshineBindingInit();
|
||||
steamBindingInit();
|
||||
shaderBindingInit();
|
||||
ModLoaderBindingInit();
|
||||
|
||||
_rb_define_module_function(rb_mKernel, "rgss_main", mriRgssMain);
|
||||
_rb_define_module_function(rb_mKernel, "rgss_stop", mriRgssStop);
|
||||
|
|
|
|||
63
binding-mri/modloader-binding.cpp
Normal file
63
binding-mri/modloader-binding.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//https://silverhammermba.github.io/emberb/c/
|
||||
//https://docs.ruby-lang.org/capi/en/master/d8/d68/include_2ruby_2internal_2intern_2string_8h.html
|
||||
#include "binding-util.h"
|
||||
#include "sharedstate.h"
|
||||
#include "filesystem.h"
|
||||
#include "util.h"
|
||||
#include "debugwriter.h"
|
||||
#include "ruby/encoding.h"
|
||||
#include "ruby/intern.h"
|
||||
#include "ruby/thread.h"
|
||||
#include <ruby.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
VALUE meow(std::vector<std::string> vec){
|
||||
VALUE ary = rb_ary_new_capa((long)vec.size());
|
||||
for (const std::string &s : vec) {
|
||||
VALUE str = rb_str_new_cstr(s.c_str());
|
||||
rb_ary_push(ary, str);
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
|
||||
static VALUE hooks(int argc, VALUE *argv, VALUE self) {
|
||||
VALUE v_path = Qnil;
|
||||
rb_scan_args(argc, argv, "01", &v_path);
|
||||
std::string hook_path = "hooks";
|
||||
if (v_path != Qnil) {
|
||||
v_path = StringValue(v_path);
|
||||
hook_path = StringValueCStr(v_path);
|
||||
}
|
||||
|
||||
std::vector<std::string> files = {};
|
||||
if(!std::filesystem::exists(hook_path)){
|
||||
return meow(files);
|
||||
}
|
||||
for (const auto &entry : std::filesystem::directory_iterator(hook_path, std::filesystem::directory_options::skip_permission_denied)) {
|
||||
std::error_code ec;
|
||||
auto p = entry.path();
|
||||
if (!std::filesystem::is_regular_file(p, ec) || ec) continue;
|
||||
|
||||
auto ext = p.extension().string();
|
||||
if (ext != ".rb") continue;
|
||||
|
||||
std::string full = p.string();
|
||||
Debug() << "[MODLOADER] Executing hook: " << full;
|
||||
files.push_back(full);
|
||||
}
|
||||
return meow(files);
|
||||
}
|
||||
|
||||
void ModLoaderBindingInit(){
|
||||
Debug() << "[MODLOADER] initalizing binding...";
|
||||
VALUE klass = rb_define_module("ModLoader");
|
||||
rb_define_module_function(klass, "hooks", RUBY_METHOD_FUNC(hooks), -1);
|
||||
}
|
||||
|
|
@ -64,9 +64,6 @@ module RPG
|
|||
def self.windowskin(filename)
|
||||
self.load_bitmap("Graphics/Windowskins/", filename)
|
||||
end
|
||||
#def self.minimap(zone, location)
|
||||
# self.load_bitmap("Graphics/Menus/minimap/", zone + "_" + location)
|
||||
#end
|
||||
def self.tile(filename, tile_id, hue)
|
||||
key = [filename, tile_id, hue]
|
||||
if not @cache.include?(key) or @cache[key].disposed?
|
||||
|
|
@ -1276,6 +1273,14 @@ module RPG
|
|||
attr_accessor :timings
|
||||
end
|
||||
|
||||
class Mod
|
||||
def self.exec_hooks(path, b)
|
||||
ModLoader.hooks(path).each do |item|
|
||||
eval(File.read(item), b)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Tileset
|
||||
def initialize
|
||||
@id = 0
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -26,7 +26,6 @@
|
|||
# (default: disabled)
|
||||
# screenMode=false
|
||||
|
||||
|
||||
# Display current FPS in Window title
|
||||
# (default: disabled)
|
||||
# printFPS=false
|
||||
|
|
@ -185,5 +184,12 @@
|
|||
|
||||
# [Windows only] Alloc console
|
||||
# Launch a console window with debug information along with the game.
|
||||
# (default: false)
|
||||
#
|
||||
# Windows_AllocConsole=false
|
||||
|
||||
# Mods directory path
|
||||
# Path to mods directory
|
||||
# (default: "mods")
|
||||
#
|
||||
# ModsDirPath="mods"
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class Credits_Message
|
|||
# Animation flags
|
||||
@fade_in = false
|
||||
@fade_out = false
|
||||
RPG::Mod.exec_hooks("hooks/Credits_Message/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Dispose
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class FastTravel
|
|||
@name = name
|
||||
@maps = maps
|
||||
@locations = locations;
|
||||
RPG::Mod.exec_hooks("hooks/FastTravel/init", binding)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ class FastTravel
|
|||
@next_right = next_right
|
||||
@next_bottom = next_bottom
|
||||
@next_left = next_left
|
||||
RPG::Mod.exec_hooks("hooks/ZoneLocations/init", binding)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class Desktop_Message
|
|||
# Animation flags
|
||||
@fade_in = false
|
||||
@fade_out = false
|
||||
RPG::Mod.exec_hooks("hooks/Desktop_Message/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Dispose
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class Doc_Message
|
|||
# Animation flags
|
||||
@fade_in = false
|
||||
@fade_out = false
|
||||
RPG::Mod.exec_hooks("hooks/Doc_Message/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Dispose
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class Ed_Message
|
|||
@fade_out = false
|
||||
@fade_in_text = false
|
||||
@fade_out_text = false
|
||||
RPG::Mod.exec_hooks("hooks/Ed_Message/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Dispose
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ class FastTravel
|
|||
@fade_out = false
|
||||
|
||||
@transfer_player = nil
|
||||
RPG::Mod.exec_hooks("hooks/FastTravel/init", binding)
|
||||
end
|
||||
|
||||
#WME
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class Game_Actor < Game_Battler
|
|||
#--------------------------------------------------------------------------
|
||||
def initialize(actor_id)
|
||||
super()
|
||||
RPG::Mod.exec_hooks("hooks/Game_Actor/init", binding)
|
||||
setup(actor_id)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class Game_Actors
|
|||
#--------------------------------------------------------------------------
|
||||
def initialize
|
||||
@data = []
|
||||
RPG::Mod.exec_hooks("hooks/Game_Actors/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Get Actor
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class Game_CommonEvent
|
|||
def initialize(common_event_id)
|
||||
@common_event_id = common_event_id
|
||||
@interpreter = nil
|
||||
RPG::Mod.exec_hooks("hooks/Game_CommonEvent/init", binding)
|
||||
refresh
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ class Game_Event < Game_Character
|
|||
@custom_flags.concat(special.flags)
|
||||
@collision = special.collision
|
||||
end
|
||||
|
||||
RPG::Mod.exec_hooks("hooks/Game_Event/init", binding)
|
||||
|
||||
# Move to starting position
|
||||
moveto(@event.x, @event.y)
|
||||
refresh
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class Game_FastTravel
|
|||
@unlocked = {}
|
||||
@zone = nil
|
||||
@enabled = false
|
||||
RPG::Mod.exec_hooks("hooks/Game_FastTravel/init", binding)
|
||||
end
|
||||
|
||||
def unlock(map, id, x, y, dir)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ class Game_Follower < Game_Character
|
|||
@leader = leader
|
||||
self.actor = actor
|
||||
moveto(leader.x, leader.y)
|
||||
RPG::Mod.exec_hooks("hooks/Game_Follower/init", binding)
|
||||
end
|
||||
|
||||
# Overrides
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ class Game_Map
|
|||
@map_id = 0
|
||||
@display_x = 0
|
||||
@display_y = 0
|
||||
RPG::Mod.exec_hooks("hooks/Game_Map/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Setup
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ class Game_Oneshot
|
|||
@plight_timer = nil
|
||||
@wallpaper = nil
|
||||
@bruteforce_start = nil
|
||||
RPG::Mod.exec_hooks("hooks/Game_Oneshot/init", binding)
|
||||
end
|
||||
|
||||
def self.get_user_name
|
||||
user_name = (Oneshot::USER_NAME).split(/\s+/)
|
||||
#user_name = (Steam.enabled? ? Steam::USER_NAME : Oneshot::USER_NAME).split(/\s+/)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Game_Party
|
|||
@items = {}
|
||||
@weapons = {}
|
||||
@armors = {}
|
||||
RPG::Mod.exec_hooks("hooks/Game_Party/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Initial Party Setup
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ class Game_Picture
|
|||
@tone_duration = 0
|
||||
@angle = 0
|
||||
@rotate_speed = 0
|
||||
RPG::Mod.exec_hooks("hooks/Game_Picture/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Show Picture
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class Game_Screen
|
|||
@weather_type_target = 0
|
||||
@weather_max_target = 0.0
|
||||
@weather_duration = 0
|
||||
RPG::Mod.exec_hooks("hooks/Game_Screen/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Start Changing Color Tone
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class Game_SelfSwitches
|
|||
#--------------------------------------------------------------------------
|
||||
def initialize
|
||||
@data = {}
|
||||
RPG::Mod.exec_hooks("hooks/Game_SelfSwitches/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Get Self Switch
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class Game_Switches
|
|||
#--------------------------------------------------------------------------
|
||||
def initialize
|
||||
@data = []
|
||||
RPG::Mod.exec_hooks("hooks/Game_Switches/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Get Switch
|
||||
|
|
@ -47,4 +48,4 @@ end
|
|||
# some switches that we know of
|
||||
# 101 - Use tower footsplashes i think
|
||||
# 112 - Niko in minecart
|
||||
# 160 - re-playing game (when Niko is TWM)
|
||||
# 160 - re-playing game (when Niko is TWM)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class Game_System
|
|||
@message_frame = 0
|
||||
@save_count = 0
|
||||
@magic_number = 0
|
||||
RPG::Mod.exec_hooks("hooks/Game_System/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Play Background Music
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ class Game_Temp
|
|||
@menus_visible = false
|
||||
@countdown_password = ""
|
||||
@igt_timer_visible = false
|
||||
RPG::Mod.exec_hooks("hooks/Game_Temp/init", binding)
|
||||
end
|
||||
|
||||
def bgm_fadein(game_system)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class Game_Variables
|
|||
#--------------------------------------------------------------------------
|
||||
def initialize
|
||||
@data = []
|
||||
RPG::Mod.exec_hooks("hooks/Game_Variables/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Get Variable
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@
|
|||
#==============================================================================
|
||||
|
||||
at_exit do
|
||||
RPG::Mod.exec_hooks("hooks/Main/at_exit", binding)
|
||||
Wallpaper.reset
|
||||
save unless $game_switches[99] || ($game_system.map_interpreter.running? || !$scene.is_a?(Scene_Map))
|
||||
end
|
||||
|
||||
begin
|
||||
RPG::Mod.exec_hooks("hooks/Main/start", binding)
|
||||
$console = Graphics.fullscreen
|
||||
Graphics.frame_rate = 60
|
||||
Font.default_size = 20
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class Particle
|
|||
@sprite.oy = bitmap.height / 2
|
||||
self.x = rand(Graphics.width)
|
||||
self.y = rand(Graphics.height)
|
||||
RPG::Mod.exec_hooks("hooks/Particle/main", binding)
|
||||
end
|
||||
|
||||
# Link various things to the sprite
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ class Scene_Map
|
|||
@blackfade.bitmap.fill_rect(0, 0, Graphics.width, Graphics.height, Color.new(0, 0, 0))
|
||||
@blackfade.visible = false
|
||||
@blackfade.z = 9999
|
||||
|
||||
RPG::Mod.exec_hooks("hooks/Scene_Map/main", binding)
|
||||
|
||||
# Transition run
|
||||
Graphics.transition
|
||||
# Main loop
|
||||
|
|
|
|||
|
|
@ -59,13 +59,15 @@ class Scene_Title
|
|||
end
|
||||
end
|
||||
|
||||
# check for debug file to add debug items
|
||||
if Settings[:debug]
|
||||
$game_party.gain_item(54, 1) # debug save
|
||||
$game_party.gain_item(82, 1) # plight skip
|
||||
$game_party.gain_item(81, 1) #George reroler
|
||||
end
|
||||
RPG::Mod.exec_hooks("hooks/Scene_Title/init", binding)
|
||||
|
||||
# check for debug file to add debug items
|
||||
if Settings[:debug]
|
||||
$game_party.gain_item(54, 1) # debug save
|
||||
$game_party.gain_item(82, 1) # plight skip
|
||||
$game_party.gain_item(81, 1) # George reroler
|
||||
end
|
||||
|
||||
@sprite.zoom_x = 2.0
|
||||
@sprite.zoom_y = 2.0
|
||||
# Create/render menu options
|
||||
|
|
@ -76,7 +78,7 @@ class Scene_Title
|
|||
@menu.bitmap.draw_text(MENU_X, MENU_Y + 25, 150, 24, tr("Settings"))
|
||||
@menu.bitmap.draw_text(MENU_X, MENU_Y + 50, 150, 24, tr("Exit"))
|
||||
|
||||
#Debug info like in minecraft Forge :P
|
||||
#Debug info like in minecraft Forge :P
|
||||
@debug = Sprite.new
|
||||
@debug.z += @menu.z
|
||||
@debug.bitmap = Bitmap.new(Graphics.width, Graphics.height)
|
||||
|
|
@ -88,7 +90,7 @@ class Scene_Title
|
|||
@menu.bitmap.draw_text(MENU_X, MENU_Y + 75, 150, 24, tr("..."))
|
||||
end
|
||||
|
||||
Language.register_text_sprite(self.class.name + "_contents", @menu.bitmap)
|
||||
Language.register_text_sprite(self.class.name + "_contents", @menu.bitmap)
|
||||
|
||||
# Make cursor graphic
|
||||
@cursor = Sprite.new
|
||||
|
|
@ -98,9 +100,10 @@ class Scene_Title
|
|||
@cursor.bitmap = RPG::Cache.menu('cursor')
|
||||
@cursor.x = MENU_X - 12
|
||||
@cursor.y = MENU_Y + (20 - @cursor.bitmap.height) / 2
|
||||
|
||||
|
||||
# Initialize cursor position
|
||||
@cursor_pos = 0.0
|
||||
|
||||
|
||||
# Play title BGM
|
||||
if File.exist?("badend.lock")
|
||||
|
|
@ -138,7 +141,7 @@ class Scene_Title
|
|||
@menu.dispose
|
||||
@cursor.bitmap.dispose
|
||||
@cursor.dispose
|
||||
@window_settings_title.dispose
|
||||
@window_settings_title.dispose
|
||||
Audio.bgm_fade(60)
|
||||
Graphics.transition(60)
|
||||
# Run automatic change for BGM and BGS set with map
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ class Sprite_Character
|
|||
@text_sprite.bitmap = Bitmap.new(Graphics.width, 24)
|
||||
@text_sprite.bitmap.font.size = 12
|
||||
@character = character
|
||||
|
||||
|
||||
RPG::Mod.exec_hooks("hooks/Sprite_Character/init", binding)
|
||||
update
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class Sprite_Footsplash < Sprite
|
|||
self.oy = 80
|
||||
self.bitmap = RPG::Cache.misc('foot_splash')
|
||||
self.src_rect.set(0, 0, 80, 80)
|
||||
RPG::Mod.exec_hooks("hooks/Sprite_Footsplash/init", binding)
|
||||
update
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ class Light < RPG::Sprite
|
|||
@map_y = y
|
||||
self.z = 9999
|
||||
self.visible = true
|
||||
RPG::Mod.exec_hooks("hooks/Light/init", binding)
|
||||
update
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class Sprite_MapText < Sprite
|
|||
self.oy = 24
|
||||
self.ox = width/2
|
||||
|
||||
|
||||
RPG::Mod.exec_hooks("hooks/Sprite_MapText/init", binding)
|
||||
update
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class Sprite_Picture < Sprite
|
|||
def initialize(viewport, picture)
|
||||
super(viewport)
|
||||
@picture = picture
|
||||
RPG::Mod.exec_hooks("hooks/Sprite_Picture/init", binding)
|
||||
update
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class Sprite_Timer < Sprite
|
|||
self.x = 640 - self.bitmap.width
|
||||
self.y = 0
|
||||
self.z = 500
|
||||
RPG::Mod.exec_hooks("hooks/Sprite_Timer/init", binding)
|
||||
update
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ class Spriteset_Map
|
|||
@bulb.opacity = has_lightbulb? ? 255 : 0
|
||||
# Panorama animation timer
|
||||
@pan_animate_timer = 0
|
||||
RPG::Mod.exec_hooks("hooks/Spriteset_Map/init", binding)
|
||||
# Frame update
|
||||
update
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class Window_Base < Window
|
|||
self.width = width
|
||||
self.height = height
|
||||
self.z = 100
|
||||
RPG::Mod.exec_hooks("hooks/Window_Base/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Dispose
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class Window_DebugLeft < Window_Selectable
|
|||
def initialize
|
||||
super(0, 0, 192, Graphics.height)
|
||||
self.index = 0
|
||||
RPG::Mod.exec_hooks("hooks/Window_DebugLeft/init", binding)
|
||||
refresh
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class Window_DebugRight < Window_Selectable
|
|||
@item_max = 10
|
||||
@mode = 0
|
||||
@top_id = 1
|
||||
RPG::Mod.exec_hooks("hooks/Window_DebugRight/init", binding)
|
||||
refresh
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class Window_Help < Window_Base
|
|||
self.visible = false
|
||||
self.back_opacity = 230
|
||||
self.z = 9998
|
||||
RPG::Mod.exec_hooks("hooks/Window_Help/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Set Text
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Window_InputNumber < Window_Base
|
|||
@index = 0
|
||||
refresh
|
||||
update_cursor_rect
|
||||
RPG::Mod.exec_hooks("hooks/Window_InputNumber/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Get Number
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Window_Item < Window_Selectable
|
|||
@fade_out = false
|
||||
|
||||
@help_window = Window_Help.new
|
||||
RPG::Mod.exec_hooks("hooks/Window_Item/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Get Item
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class Window_MainMenu < Window_Selectable
|
|||
draw_item(i, normal_color)
|
||||
end
|
||||
self.z = 9998
|
||||
RPG::Mod.exec_hooks("hooks/Window_MainMenu/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Dispose
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class Window_Message < Window_Selectable
|
|||
|
||||
# Text blip sound
|
||||
@blipsound = nil
|
||||
RPG::Mod.exec_hooks("hooks/Window_Message/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Dispose
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class Window_NameEdit < Window_Base
|
|||
@index = name_array.size
|
||||
refresh
|
||||
update_cursor_rect
|
||||
RPG::Mod.exec_hooks("hooks/Window_NameEdit/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Return to Default Name
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class NameInputMode
|
|||
@size = names.map { |n| bitmap.text_size(n).width }.max
|
||||
@index = 0
|
||||
@count = 0
|
||||
RPG::Mod.exec_hooks("hooks/NameInputMode/init", binding)
|
||||
end
|
||||
|
||||
def cycle
|
||||
|
|
@ -47,6 +48,7 @@ class Window_NameInput < Window_Base
|
|||
@ok_text = "OK"
|
||||
@char_w = 28
|
||||
@char_h = 32
|
||||
RPG::Mod.exec_hooks("hooks/Window_NameInput/init", binding)
|
||||
end
|
||||
def init
|
||||
# Create dimension information
|
||||
|
|
@ -63,6 +65,8 @@ class Window_NameInput < Window_Base
|
|||
end
|
||||
end
|
||||
|
||||
RPG::Mod.exec_hooks("hooks/Window_NameInput/init2", binding)
|
||||
|
||||
refresh
|
||||
update_cursor_rect
|
||||
self
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class Window_Selectable < Window_Base
|
|||
@item_max = 1
|
||||
@column_max = 1
|
||||
@index = -1
|
||||
RPG::Mod.exec_hooks("hooks/Window_Selectable/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Set Cursor Position
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ class Window_Settings
|
|||
|
||||
@transfer_player = nil
|
||||
@visible = false
|
||||
RPG::Mod.exec_hooks("hooks/Window_Settings/init", binding)
|
||||
end
|
||||
|
||||
def dispose
|
||||
|
|
@ -449,4 +450,4 @@ class Window_Settings
|
|||
def opacity
|
||||
@bg.opacity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class Window_TPtL < Window_Selectable
|
|||
draw_item(i, normal_color)
|
||||
end
|
||||
self.z = 9998
|
||||
RPG::Mod.exec_hooks("hooks/Window_TPtL/init", binding)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Dispose
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ void Config::read(int argc, char *argv[]){
|
|||
PO_DESC(defScreenW, int, 0) \
|
||||
PO_DESC(defScreenH, int, 0) \
|
||||
PO_DESC(windowTitle, std::string, "") \
|
||||
PO_DESC(ModsDirPath, std::string,"mods") \
|
||||
PO_DESC(fixedFramerate, int, 0) \
|
||||
PO_DESC(frameSkip, bool, true) \
|
||||
PO_DESC(syncToRefreshrate, bool, false) \
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ struct Config{
|
|||
int defScreenW;
|
||||
int defScreenH;
|
||||
std::string windowTitle;
|
||||
std::string ModsDirPath;
|
||||
|
||||
int fixedFramerate;
|
||||
bool frameSkip;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ struct Exception{
|
|||
SDLError,
|
||||
MKXPError,
|
||||
|
||||
//modloader
|
||||
ModLoaderError,
|
||||
|
||||
// For crash()
|
||||
MEOW
|
||||
};
|
||||
|
|
|
|||
11
src/main.cpp
11
src/main.cpp
|
|
@ -49,6 +49,7 @@
|
|||
#include "gl-fun.h"
|
||||
#include "i18n.h"
|
||||
#include "security.h"
|
||||
#include "modloader.h"
|
||||
|
||||
#include "meow.h"
|
||||
|
||||
|
|
@ -271,6 +272,7 @@ int main(int argc, char *argv[]){
|
|||
freopen("CONOUT$", "w", stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (!conf.gameFolder.empty()){
|
||||
if (chdir(conf.gameFolder.c_str()) != 0){
|
||||
|
|
@ -279,6 +281,15 @@ int main(int argc, char *argv[]){
|
|||
}
|
||||
}
|
||||
|
||||
std::string new_path = ModLoader(conf);
|
||||
|
||||
if(new_path != ""){
|
||||
if (chdir(new_path.c_str()) != 0){
|
||||
crash(Exception::MEOW, "Unable to switch into new gameFolder %s", new_path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern int screenMain(Config &conf);
|
||||
if (conf.screenMode)
|
||||
return screenMain(conf);
|
||||
|
|
|
|||
211
src/modloader.cpp
Normal file
211
src/modloader.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
//https://terminalroot.com/how-to-generate-sha256-hash-with-cpp-and-openssl/
|
||||
//https://stackoverflow.com/questions/15347123/how-to-construct-a-stdstring-from-a-stdvectorstring
|
||||
//https://stackoverflow.com/questions/54260184/how-to-sha256-hash-a-text-file-in-chunks-with-openssl-sha-h
|
||||
//https://stackoverflow.com/questions/1673445/how-to-convert-unsigned-char-to-stdstring-in-c
|
||||
//https://www.geeksforgeeks.org/cpp/cpp-program-to-read-and-print-all-files-from-a-zip-file/
|
||||
#include "modloader.h"
|
||||
#include "debugwriter.h"
|
||||
#include "meow.h"
|
||||
#include "config.h"
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <openssl/sha.h>
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
#include <zip.h>
|
||||
#include <locale>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <system_error>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Get directory for storing cached builds
|
||||
//TODO: support for other OS and platforms
|
||||
std::string getCacheDir(){
|
||||
#ifdef _WIN32
|
||||
return SDL_getenv("Temp");
|
||||
#elif defined(__linux__)
|
||||
return std::string(SDL_getenv("HOME")) + "/.cache";
|
||||
#else
|
||||
return "idk";
|
||||
#endif
|
||||
}
|
||||
|
||||
//helper
|
||||
bool ensure_parent_dir(const std::filesystem::path& p){
|
||||
if (p.has_parent_path()){
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(p.parent_path(), ec);
|
||||
return !ec;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string sha512(const std::string str){
|
||||
unsigned char hash[SHA512_DIGEST_LENGTH];
|
||||
|
||||
SHA512_CTX sha512;
|
||||
SHA512_Init(&sha512);
|
||||
SHA512_Update(&sha512, str.c_str(), str.size());
|
||||
SHA512_Final(hash, &sha512);
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
for(int i = 0; i < SHA512_DIGEST_LENGTH; i++){
|
||||
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>( hash[i] );
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string sha256_file(const std::string &fn) {
|
||||
FILE *file = fopen(fn.c_str(), "rb");
|
||||
if (!file) {
|
||||
crash(Exception::ModLoaderError, "Failed to load mod, filesystem error.");
|
||||
}
|
||||
|
||||
unsigned char buf[1024];
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
size_t len;
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
while ((len = fread(buf, 1, sizeof buf, file)) != 0){
|
||||
SHA256_Update(&ctx, buf, len);
|
||||
}
|
||||
fclose(file);
|
||||
SHA256_Final(hash, &ctx);
|
||||
|
||||
std::string out;
|
||||
out.reserve(SHA256_DIGEST_LENGTH * 2);
|
||||
char hex[3] = {0};
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
|
||||
snprintf(hex, sizeof hex, "%02x", hash[i]);
|
||||
out += hex;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string ModLoader(Config conf){
|
||||
//"mods" by defailt
|
||||
std::string path = conf.ModsDirPath;
|
||||
if (!std::filesystem::exists(path) || !std::filesystem::is_directory(path)) {
|
||||
Debug() << "[MODLOADER] Mods directory not found, skip.";
|
||||
return "";
|
||||
}
|
||||
|
||||
//buildID - unique ID of a certain combination of mods
|
||||
std::string buildID = "";
|
||||
std::string buildID_tmp = "";
|
||||
std::vector<std::string> mod_list = {};
|
||||
try{
|
||||
//1.check if any zip(mod) file, 2. calculate sha256 hash of zip(mod) files
|
||||
for (const auto &entry : std::filesystem::directory_iterator(path, std::filesystem::directory_options::skip_permission_denied)) {
|
||||
std::error_code ec;
|
||||
auto p = entry.path();
|
||||
if (!std::filesystem::is_regular_file(p, ec) || ec) continue;
|
||||
|
||||
auto ext = p.extension().string();
|
||||
if (ext != ".zip") continue;
|
||||
|
||||
std::string full = p.string();
|
||||
Debug() << "[MODLOADER] " << full;
|
||||
mod_list.push_back(full);
|
||||
|
||||
try {
|
||||
auto h = sha256_file(full);
|
||||
buildID_tmp.append(h);
|
||||
}catch (const std::exception &e) {
|
||||
Debug() << "[MODLOADER] sha256 failed for " << full << ", ex: " << e.what();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
buildID = sha512(buildID_tmp);
|
||||
Debug() << "[MODLOADER] BuildID: " << buildID;
|
||||
std::string path3 = getCacheDir() + "/sunshine-" + buildID;
|
||||
int err = 0;
|
||||
if(!std::filesystem::exists(path3)){
|
||||
std::error_code ec;
|
||||
fs::create_directory(path3, ec);
|
||||
if (ec) {
|
||||
crash(Exception::ModLoaderError, "Failed to create destination: %s", ec.message().c_str());
|
||||
}
|
||||
|
||||
fs::copy(conf.gameFolder, path3, fs::copy_options::recursive | fs::copy_options::overwrite_existing, ec);
|
||||
if (ec) {
|
||||
crash(Exception::ModLoaderError, "Copy error: %s", ec.message().c_str());
|
||||
}
|
||||
|
||||
char state2[1024];
|
||||
//Extracting zipsodpsofspo idk
|
||||
for (size_t i = 0; i < mod_list.size(); ++i){
|
||||
zip_t* za = zip_open(mod_list[i].c_str(), ZIP_RDONLY, &err);
|
||||
if (!za){
|
||||
crash(Exception::ModLoaderError, "Failed to open zip");
|
||||
}
|
||||
|
||||
zip_int64_t n = zip_get_num_entries(za, 0);
|
||||
for (zip_uint64_t i2 = 0; i2 < static_cast<zip_uint64_t>(n); ++i2) {
|
||||
zip_stat_t st;
|
||||
if (zip_stat_index(za, i2, 0, &st) != 0) {
|
||||
crash(Exception::ModLoaderError, "zip_stat_index failed");
|
||||
}
|
||||
|
||||
std::string name = st.name;
|
||||
std::filesystem::path target = path3 / std::filesystem::path(name);
|
||||
|
||||
// If entry name ends with '/', treat as directory
|
||||
if (!name.empty() && name.back() == '/') {
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(target, ec);
|
||||
if (ec) crash(Exception::MEOW, "Failed to create dir");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ensure_parent_dir(target)){
|
||||
crash(Exception::ModLoaderError, "Failed to create parent dirs");
|
||||
}
|
||||
|
||||
zip_file_t* zf = zip_fopen_index(za, i2, 0);
|
||||
if (!zf) {
|
||||
crash(Exception::ModLoaderError, "zip_fopen_index failed");
|
||||
}
|
||||
std::ofstream out(target, std::ios::binary);
|
||||
if (!out) {
|
||||
crash(Exception::ModLoaderError, "Failed to open output file %s", target.c_str());
|
||||
zip_fclose(zf);
|
||||
}
|
||||
|
||||
const zip_uint64_t bufsize = 4096;
|
||||
std::vector<char> buf(bufsize);
|
||||
zip_int64_t bytes_read;
|
||||
zip_uint64_t remaining = st.size;
|
||||
while (remaining > 0) {
|
||||
zip_uint64_t to_read = std::min<zip_uint64_t>(bufsize, remaining);
|
||||
bytes_read = zip_fread(zf, buf.data(), to_read);
|
||||
if (bytes_read < 0) {
|
||||
crash(Exception::ModLoaderError, "zip_fread error for %s", name.c_str());
|
||||
break;
|
||||
}
|
||||
out.write(buf.data(), bytes_read);
|
||||
remaining -= bytes_read;
|
||||
}
|
||||
zip_fclose(zf);
|
||||
}
|
||||
zip_close(za);
|
||||
}
|
||||
return path3;
|
||||
}else{
|
||||
return path3;
|
||||
}
|
||||
}catch(const std::exception& e){
|
||||
crash(Exception::ModLoaderError, "Something is wrong, Exception: %s ", e.what());
|
||||
}
|
||||
}
|
||||
4
src/modloader.h
Normal file
4
src/modloader.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
//modloader wow
|
||||
#include <string>
|
||||
#include "config.h"
|
||||
std::string ModLoader(Config conf);
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
SCMP_SYS(move_mount), SCMP_SYS(mount_setattr), SCMP_SYS(mount), SCMP_SYS(lsm_set_self_attr), SCMP_SYS(lsm_list_modules), SCMP_SYS(lsm_get_self_attr),
|
||||
SCMP_SYS(process_vm_readv), SCMP_SYS(process_vm_writev), SCMP_SYS(ptrace), SCMP_SYS(swapon), SCMP_SYS(swapoff), SCMP_SYS(shutdown), SCMP_SYS(settimeofday),
|
||||
SCMP_SYS(sethostname), SCMP_SYS(umount), SCMP_SYS(umount2), SCMP_SYS(vm86old), SCMP_SYS(vm86), SCMP_SYS(setgroups), SCMP_SYS(setgid), SCMP_SYS(setfsuid),
|
||||
SCMP_SYS(setfsgid), SCMP_SYS(setdomainname), SCMP_SYS(setns), SCMP_SYS(setpgid), SCMP_SYS(pciconfig_write), SCMP_SYS(shutdown), SCMP_SYS(shutdown)};
|
||||
SCMP_SYS(setfsgid), SCMP_SYS(setdomainname), SCMP_SYS(setns), SCMP_SYS(setpgid), SCMP_SYS(pciconfig_write)};
|
||||
#endif
|
||||
|
||||
void SecurityManagerInit(){
|
||||
|
|
|
|||
Loading…
Reference in a new issue