#include "kmachine.h" #include #include "common/global_profiler/GlobalProfiler.h" #include "common/log/log.h" #include "common/symbols.h" #include "common/util/FileUtil.h" #include "common/util/Timer.h" #include "common/util/font/font_utils.h" #include "common/util/string_util.h" #include "game/external/discord.h" #include "game/graphics/display.h" #include "game/graphics/gfx.h" #include "game/graphics/screenshot.h" #include "game/kernel/common/Ptr.h" #include "game/kernel/common/kernel_types.h" #include "game/kernel/common/kprint.h" #include "game/kernel/common/kscheme.h" #include "game/mips2c/mips2c_table.h" #include "game/runtime.h" #include "game/sce/libcdvd_ee.h" #include "game/sce/libpad.h" #include "game/sce/libscf.h" #include "game/sce/sif_ee.h" /*! * Where does OVERLORD load its data from? */ OverlordDataSource isodrv; // Get IOP modules from DVD or from dsefilesv u32 modsrc; // Reboot IOP with IOP kernel from DVD/CD on boot u32 reboot_iop; const char* init_types[] = {"fakeiso", "deviso", "iso_cd"}; u8 pad_dma_buf[2 * SCE_PAD_DMA_BUFFER_SIZE]; // added u32 vif1_interrupt_handler = 0; u32 vblank_interrupt_handler = 0; Timer ee_clock_timer; void kmachine_init_globals_common() { memset(pad_dma_buf, 0, sizeof(pad_dma_buf)); isodrv = fakeiso; // changed. fakeiso is the only one that works in opengoal. modsrc = 1; reboot_iop = 1; vif1_interrupt_handler = 0; vblank_interrupt_handler = 0; ee_clock_timer = Timer(); } /*! * Initialize the CD Drive * DONE, EXACT */ void InitCD() { lg::info("Initializing CD drive. This may take a while..."); ee::sceCdInit(SCECdINIT); ee::sceCdMmode(SCECdDVD); while (ee::sceCdDiskReady(0) == SCECdNotReady) { lg::debug("Drive not ready... insert a disk!"); } lg::debug("Disk type {}\n", ee::sceCdGetDiskType()); } /*! * Initialize the GS and display the splash screen. */ void InitVideo() { if (!SplashScreen) { lg::info("InitVideo: skipping splash!\n"); return; } std::map lang_to_splash_map{ {SCE_JAPANESE_LANGUAGE, "JAP"}, {SCE_ENGLISH_LANGUAGE, "USA"}, {SCE_FRENCH_LANGUAGE, "FRE"}, {SCE_SPANISH_LANGUAGE, "SPA"}, {SCE_GERMAN_LANGUAGE, "GER"}, {SCE_ITALIAN_LANGUAGE, "ITA"}, {SCE_PORTUGUESE_LANGUAGE, "POR"}, {SCE_KOREAN_LANGUAGE, "KOR"}, }; auto lang = ee::sceScfGetLanguage(); auto filename = "SCREEN1." + lang_to_splash_map.at(lang); auto path = file_util::get_jak_project_dir() / "out" / game_version_names[g_game_version] / "iso" / filename; if (lang != SCE_ENGLISH_LANGUAGE && !fs::exists(path)) { lg::warn("InitVideo: file {} not found, falling back to english...\n", filename); path = file_util::get_jak_project_dir() / "out" / game_version_names[g_game_version] / "iso" / "SCREEN1.USA"; } if (!fs::exists(path)) { lg::warn("InitVideo: splash screen not found!\n"); return; } auto data = file_util::read_binary_file(path); // width is always 512, height is sometimes different (e.g. demo screens), so we infer from file // size constexpr int kWidth = 512; if (data.size() % (kWidth * 4) != 0) { lg::error("InitVideo: splash size {} not divisible by stride {}", data.size(), kWidth * 4); return; } int kHeight = data.size() / (kWidth * 4); if ((int)data.size() != kWidth * kHeight * 4) { lg::error("InitVideo: unexpected size {}, expected {} for splash screen", data.size(), kWidth * kHeight * 4); return; } Gfx::g_splash.data = std::move(data); Gfx::g_splash.width = kWidth; Gfx::g_splash.height = kHeight; Gfx::g_splash.ready.store(true); SplashTimer.start(); while (SplashTimer.getSeconds() < SPLASH_SCREEN_TIME) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } /*! * Flush caches. Does all the memory, regardless of what you specify */ void CacheFlush(void* mem, int size) { (void)mem; (void)size; // FlushCache(0); // FlushCache(2); } /*! * Open a new controller pad. * Set the new_pad flag to 1 and state to 0. * Prints an error if it fails to open. */ u64 CPadOpen(u64 cpad_info, s32 pad_number) { auto cpad = Ptr(cpad_info).c(); if (cpad->cpad_file == 0) { // not open, so we will open it cpad->cpad_file = ee::scePadPortOpen(pad_number, 0, pad_dma_buf + pad_number * SCE_PAD_DMA_BUFFER_SIZE); if (cpad->cpad_file < 1) { MsgErr("dkernel: !open cpad #%d (%d)\n", pad_number, cpad->cpad_file); } cpad->new_pad = 1; cpad->state = 0; } return cpad_info; } /*! * Not checked super carefully for jak 2, but looks the same */ u64 CPadGetData(u64 cpad_info) { using namespace ee; auto cpad = Ptr(cpad_info).c(); auto pad_state = scePadGetState(cpad->number, 0); if (pad_state == scePadStateDiscon) { cpad->state = 0; } cpad->valid = pad_state | 0x80; switch (cpad->state) { // case 99: // functional default: // controller is functioning as normal if (pad_state == scePadStateStable || pad_state == scePadStateFindCTP1) { scePadRead(cpad->number, 0, (u8*)cpad); // ps2 controllers would send an enabled bit if the button was NOT pressed, but we don't do // that here. removed code that flipped the bits. if (cpad->change_time) { scePadSetActDirect(cpad->number, 0, cpad->direct); } cpad->valid = pad_state; } break; case 0: // unavailable if (pad_state == scePadStateStable || pad_state == scePadStateFindCTP1) { auto pad_mode = scePadInfoMode(cpad->number, 0, InfoModeCurID, 0); if (pad_mode != 0) { auto vibration_mode = scePadInfoMode(cpad->number, 0, InfoModeCurExID, 0); if (vibration_mode > 0) { // vibration supported pad_mode = vibration_mode; } if (pad_mode == 4) { // controller mode cpad->state = 40; } else if (pad_mode == 7) { // dualshock mode cpad->state = 70; } else { // who knows mode cpad->state = 90; } } } break; case 40: // controller mode - check for extra modes // cpad->change_time = 0; cpad->change_time = 0; if (scePadInfoMode(cpad->number, 0, InfoModeIdTable, -1) == 0) { // no controller modes cpad->state = 90; return cpad_info; } cpad->state = 41; case 41: // controller mode - change to dualshock mode! // try to enter the 2nd controller mode (dualshock for ds2's) if (scePadSetMainMode(cpad->number, 0, 1, 3) == 1) { cpad->state = 42; } break; case 42: // controller mode change check if (scePadGetReqState(cpad->number, 0) == scePadReqStateFailed) { // failed to change to DS2 cpad->state = 41; } if (scePadGetReqState(cpad->number, 0) == scePadReqStateComplete) { // change successful. go back to the beginning. cpad->state = 0; } break; case 70: // dualshock mode - check vibration // get number of actuators (2 for DS2) if (scePadInfoAct(cpad->number, 0, -1, 0) < 1) { // no actuators means no vibration. skip to end! // cpad->change_time = 0; cpad->change_time = 0; cpad->state = 99; } else { // we have actuators to use. // cpad->change_time = 1; // remember to update pad times. cpad->change_time = 1; cpad->state = 75; } break; case 75: // set actuator vib param info if (scePadSetActAlign(cpad->number, 0, cpad->align) != 0) { if (scePadInfoPressMode(cpad->number, 0) == 1) { // pressure buttons supported cpad->state = 76; } else { // no pressure buttons, done with controller setup cpad->state = 99; } } break; case 76: // enter pressure mode if (scePadEnterPressMode(cpad->number, 0) == 1) { cpad->state = 78; } break; case 78: // pressure mode request check if (scePadGetReqState(cpad->number, 0) == scePadReqStateFailed) { cpad->state = 76; } if (scePadGetReqState(cpad->number, 0) == scePadReqStateComplete) { cpad->state = 99; } break; case 90: break; // unsupported controller. too bad! } return cpad_info; } // should make sure this works the same way in jak 2 void InstallHandler(u32 handler_idx, u32 handler_func) { switch (handler_idx) { case 3: vblank_interrupt_handler = handler_func; break; case 5: vif1_interrupt_handler = handler_func; break; default: lg::error("unknown handler: {}\n", handler_idx); ASSERT(false); } } // nothing used this in jak1, hopefully same for 2 void InstallDebugHandler() { ASSERT(false); } /*! * Get length of a file. */ s32 klength(u64 fs) { auto file_stream = Ptr(fs).c(); if ((file_stream->flags ^ 1) & 1) { // first flag bit not set. This means no errors auto end_seek = ee::sceLseek(file_stream->file, 0, SCE_SEEK_END); auto reset_seek = ee::sceLseek(file_stream->file, 0, SCE_SEEK_SET); if (reset_seek < 0 || end_seek < 0) { // seeking failed, flag it file_stream->flags |= 1; } return end_seek; } else { return 0; } } /*! * Seek a file stream. */ s32 kseek(u64 fs, s32 offset, s32 where) { s32 result = -1; auto file_stream = Ptr(fs).c(); if ((file_stream->flags ^ 1) & 1) { result = ee::sceLseek(file_stream->file, offset, where); if (result < 0) { file_stream->flags |= 1; } } return result; } /*! * Read from a file stream. */ s32 kread(u64 fs, u64 buffer, s32 size) { s32 result = -1; auto file_stream = Ptr(fs).c(); if ((file_stream->flags ^ 1) & 1) { result = ee::sceRead(file_stream->file, Ptr(buffer).c(), size); if (result < 0) { file_stream->flags |= 1; } } return result; } /*! * Write to a file stream. */ s32 kwrite(u64 fs, u64 buffer, s32 size) { s32 result = -1; auto file_stream = Ptr(fs).c(); if ((file_stream->flags ^ 1) & 1) { result = ee::sceWrite(file_stream->file, Ptr(buffer).c(), size); if (result < 0) { file_stream->flags |= 1; } } return result; } s32 kmkdir(u64 name) { char acStack_90[128]; if (Ptr(name)->data()[4] == '/') { // start from the fourth character? sprintf(acStack_90, "%s", Ptr(name)->data() + 5); } else { sprintf(acStack_90, "host:%s", Ptr(name)->data() + 4); } return ee::sceMkDir(acStack_90, 0x1fd); } /*! * Close a file stream. */ u64 kclose(u64 fs) { auto file_stream = Ptr(fs).c(); if ((file_stream->flags ^ 1) & 1) { ee::sceClose(file_stream->file); file_stream->file = -1; } file_stream->flags = 0; return fs; } // TODO dma_to_iop void dma_to_iop() { ASSERT(false); } u64 DecodeLanguage() { return masterConfig.language; } u64 DecodeAspect() { return masterConfig.aspect; } u64 DecodeVolume() { return masterConfig.volume; } // NOTE: this is originally hardcoded, and returns different values depending on the disc region. // it returns 0 for NTSC-U, 1 for PAL and 2 for NTSC-J u64 DecodeTerritory() { return GAME_TERRITORY_SCEA; } u64 DecodeTimeout() { return masterConfig.timeout; } u64 DecodeInactiveTimeout() { return masterConfig.inactive_timeout; } void DecodeTime(u32 ptr) { Ptr clock(ptr); // in jak2, if this fails, they do a sceScfGetLocalTimefromRTC sceCdReadClock(clock.c()); } void vif_interrupt_callback(int bucket_id) { // added for the PC port for faking VIF interrupts from the graphics system. if (vif1_interrupt_handler && MasterExit == RuntimeExitStatus::RUNNING) { call_goal(Ptr(vif1_interrupt_handler), bucket_id, 0, 0, s7.offset, g_ee_main_mem); } } /// PC PORT FUNCTIONS BEGIN u32 offset_of_s7() { return s7.offset; } inline bool symbol_to_bool(const u32 symptr) { return symptr != s7.offset; } inline u64 bool_to_symbol(const bool val) { return val ? static_cast(s7.offset) + true_symbol_offset(g_game_version) : s7.offset; } u64 pc_filter_debug_string(u32 str_ptr, u32 dist_ptr) { auto str = std::string(Ptr(str_ptr).c()->data()); float dist; memcpy(&dist, &dist_ptr, 4); // Check distance first if (Gfx::g_debug_settings.text_check_range) { if (dist / 4096.F > Gfx::g_debug_settings.text_max_range) { return bool_to_symbol(true); } } // Get the current filters const auto& filters = Gfx::g_debug_settings.text_filters; if (filters.empty()) { // there are no filters, exit early return bool_to_symbol(false); } // Currently very dumb contains check for (const auto& filter : filters) { if (filter.type == DebugTextFilter::Type::CONTAINS) { if (!str.empty() && !filter.content.empty() && !str_util::contains(str, filter.content)) { return bool_to_symbol(true); } } else if (filter.type == DebugTextFilter::Type::NOT_CONTAINS) { if (!str.empty() && !filter.content.empty() && str_util::contains(str, filter.content)) { return bool_to_symbol(true); } } else if (filter.type == DebugTextFilter::Type::REGEX) { if (str_util::valid_regex(filter.content) && std::regex_match(str, std::regex(filter.content))) { return bool_to_symbol(true); } } } return bool_to_symbol(false); } CommonPCPortFunctionWrappers g_pc_port_funcs; u64 read_ee_timer() { u64 ns = ee_clock_timer.getNs(); return (ns * 3) / 10; } void pc_memmove(u32 dst, u32 src, u32 size) { memmove(Ptr(dst).c(), Ptr(src).c(), size); } void send_gfx_dma_chain(u32 /*bank*/, u32 chain) { if (Gfx::GetCurrentRenderer()) { Gfx::GetCurrentRenderer()->send_chain(g_ee_main_mem, chain); } } void pc_texture_upload_now(u32 page, u32 mode) { if (Gfx::GetCurrentRenderer()) { Gfx::GetCurrentRenderer()->texture_upload_now(Ptr(page).c(), mode, s7.offset); } } void pc_texture_relocate(u32 dst, u32 src, u32 format) { if (Gfx::GetCurrentRenderer()) { Gfx::GetCurrentRenderer()->texture_relocate(dst, src, format); } } u64 pc_get_mips2c(u32 name) { const char* n = Ptr(name).c()->data(); return Mips2C::gLinkedFunctionTable.get(n); } u64 pc_get_display_id() { if (Display::GetMainDisplay()) { return Display::GetMainDisplay()->get_display_manager()->get_active_display_index(); } return 0; } void pc_set_display_id(u64 display_id) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_display_manager()->enqueue_set_display_id(display_id); } } u64 pc_get_display_name(u32 id, u32 str_dest_ptr) { std::string name = ""; if (Display::GetMainDisplay()) { name = Display::GetMainDisplay()->get_display_manager()->get_connected_display_name(id); } if (name.empty()) { return bool_to_symbol(false); } if (g_game_version == GameVersion::Jak1) { // The Jak 1 font has only caps name = str_util::to_upper(name).c_str(); } // Encode the string to the game font const auto encoded_name = get_font_bank_from_game_version(g_game_version) ->convert_utf8_to_game(str_util::titlize(name).c_str()); strcpy(Ptr(str_dest_ptr).c()->data(), encoded_name.c_str()); strcpy(Ptr(str_dest_ptr).c()->data(), str_util::titlize(encoded_name).c_str()); return bool_to_symbol(true); } u32 pc_get_display_mode() { auto display_mode = game_settings::DisplaySettings::DisplayMode::Windowed; if (Display::GetMainDisplay()) { display_mode = Display::GetMainDisplay()->get_display_manager()->get_display_mode(); } switch (display_mode) { case game_settings::DisplaySettings::DisplayMode::Borderless: return g_pc_port_funcs.intern_from_c("borderless").offset; case game_settings::DisplaySettings::DisplayMode::Fullscreen: return g_pc_port_funcs.intern_from_c("fullscreen").offset; default: return g_pc_port_funcs.intern_from_c("windowed").offset; } } void pc_set_display_mode(u32 symptr, u64 window_width, u64 window_height) { if (!Display::GetMainDisplay()) { return; } if (symptr == g_pc_port_funcs.intern_from_c("windowed").offset || symptr == s7.offset) { Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_display_mode( game_settings::DisplaySettings::DisplayMode::Windowed, window_width, window_height); } else if (symptr == g_pc_port_funcs.intern_from_c("borderless").offset) { Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_display_mode( game_settings::DisplaySettings::DisplayMode::Borderless, window_width, window_height); } else if (symptr == g_pc_port_funcs.intern_from_c("fullscreen").offset) { Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_display_mode( game_settings::DisplaySettings::DisplayMode::Fullscreen, window_width, window_height); } } u64 pc_get_display_count() { if (Display::GetMainDisplay()) { return Display::GetMainDisplay()->get_display_manager()->num_connected_displays(); } return 0; } void pc_get_active_display_size(u32 w_ptr, u32 h_ptr) { if (!Display::GetMainDisplay()) { return; } if (w_ptr) { auto w_out = Ptr(w_ptr).c(); if (w_out) { *w_out = Display::GetMainDisplay()->get_display_manager()->get_screen_width(); } } if (h_ptr) { auto h_out = Ptr(h_ptr).c(); if (h_out) { *h_out = Display::GetMainDisplay()->get_display_manager()->get_screen_height(); } } } s64 pc_get_active_display_refresh_rate() { if (Display::GetMainDisplay()) { return Display::GetMainDisplay()->get_display_manager()->get_active_display_refresh_rate(); } return 0; } void pc_get_window_size(u32 w_ptr, u32 h_ptr) { if (!Display::GetMainDisplay()) { return; } if (w_ptr) { auto w = Ptr(w_ptr).c(); if (w) { *w = Display::GetMainDisplay()->get_display_manager()->get_window_width(); } } if (h_ptr) { auto h = Ptr(h_ptr).c(); if (h) { *h = Display::GetMainDisplay()->get_display_manager()->get_window_height(); } } } void pc_get_window_scale(u32 x_ptr, u32 y_ptr) { if (!Display::GetMainDisplay()) { return; } if (x_ptr) { auto x = Ptr(x_ptr).c(); if (x) { *x = Display::GetMainDisplay()->get_display_manager()->get_window_scale_x(); } } if (y_ptr) { auto y = Ptr(y_ptr).c(); if (y) { *y = Display::GetMainDisplay()->get_display_manager()->get_window_scale_y(); } } } void pc_set_window_size(u64 width, u64 height) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_size(width, height); } } s64 pc_get_num_resolutions(u32 for_windowed) { if (Display::GetMainDisplay()) { return Display::GetMainDisplay()->get_display_manager()->get_num_resolutions( symbol_to_bool(for_windowed)); } return 0; } void pc_get_resolution(u32 id, u32 for_windowed, u32 w_ptr, u32 h_ptr) { if (Display::GetMainDisplay()) { auto res = Display::GetMainDisplay()->get_display_manager()->get_resolution( id, symbol_to_bool(for_windowed)); auto w = Ptr(w_ptr).c(); if (w) { *w = res.width; } auto h = Ptr(h_ptr).c(); if (h) { *h = res.height; } } } u64 pc_is_supported_resolution(u64 width, u64 height) { if (Display::GetMainDisplay()) { return bool_to_symbol( Display::GetMainDisplay()->get_display_manager()->is_supported_resolution(width, height)); } return bool_to_symbol(false); } u64 pc_get_controller_name(u32 id, u32 str_dest_ptr) { std::string name = ""; if (Display::GetMainDisplay()) { name = Display::GetMainDisplay()->get_input_manager()->get_controller_name(id); } if (name.empty()) { return bool_to_symbol(false); } if (g_game_version == GameVersion::Jak1) { // The Jak 1 font has only caps name = str_util::to_upper(name).c_str(); } // Encode the string to the game font const auto encoded_name = get_font_bank_from_game_version(g_game_version) ->convert_utf8_to_game(str_util::titlize(name).c_str()); strcpy(Ptr(str_dest_ptr).c()->data(), encoded_name.c_str()); strcpy(Ptr(str_dest_ptr).c()->data(), str_util::titlize(encoded_name).c_str()); return bool_to_symbol(true); } u64 pc_get_current_bind(s32 bind_assignment_info, u32 str_dest_ptr) { if (!Display::GetMainDisplay()) { // TODO - return something that lets the runtime use a translatable string if unknown strcpy(Ptr(str_dest_ptr).c()->data(), str_util::to_upper("unknown").c_str()); return bool_to_symbol(true); } auto info = bind_assignment_info ? Ptr(bind_assignment_info).c() : NULL; auto port = (int)info->port; auto device_type = (int)info->device_type; auto for_button = info->buttons != s7.offset; auto input_idx = (int)info->input_idx; auto analog_min_range = info->analog_min_range != s7.offset; if (Display::GetMainDisplay()) { auto name = Display::GetMainDisplay()->get_input_manager()->get_current_bind( port, (InputDeviceType)device_type, for_button, input_idx, analog_min_range); if (name.empty()) { return bool_to_symbol(false); } if (g_game_version == GameVersion::Jak1) { // The Jak 1 font has only caps name = str_util::to_upper(name).c_str(); } // Encode the string to the game font const auto encoded_name = get_font_bank_from_game_version(g_game_version) ->convert_utf8_to_game(str_util::titlize(name).c_str()); strcpy(Ptr(str_dest_ptr).c()->data(), encoded_name.c_str()); return bool_to_symbol(true); } // TODO - return something that lets the runtime use a translatable string if unknown strcpy(Ptr(str_dest_ptr).c()->data(), str_util::to_upper("unknown").c_str()); return bool_to_symbol(true); } u64 pc_get_controller_count() { if (Display::GetMainDisplay()) { return Display::GetMainDisplay()->get_input_manager()->get_num_controllers(); } return 0; } u64 pc_get_controller_index(u32 port) { if (Display::GetMainDisplay()) { return Display::GetMainDisplay()->get_input_manager()->get_controller_index(port); } return 0; } void pc_set_controller(u32 controller_id, u32 port) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->set_controller_for_port(controller_id, port); } } u32 pc_get_keyboard_enabled() { if (Display::GetMainDisplay()) { return bool_to_symbol(Display::GetMainDisplay()->get_input_manager()->is_keyboard_enabled()); } return bool_to_symbol(false); } void pc_set_keyboard_enabled(u32 sym_val) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enable_keyboard(symbol_to_bool(sym_val)); } } void pc_set_mouse_options(u32 enabled, u32 control_camera, u32 control_movement) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_update_mouse_options( symbol_to_bool(enabled), symbol_to_bool(control_camera), symbol_to_bool(control_movement)); } } void pc_set_mouse_camera_sens(u32 xsens, u32 ysens) { float xsens_val; memcpy(&xsens_val, &xsens, 4); float ysens_val; memcpy(&ysens_val, &ysens, 4); if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->set_camera_sens(xsens_val, ysens_val); } } void pc_ignore_background_controller_events(u32 sym_val) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_ignore_background_controller_events( symbol_to_bool(sym_val)); } } u64 pc_current_controller_has_led() { if (Display::GetMainDisplay()) { return bool_to_symbol(Display::GetMainDisplay()->get_input_manager()->controller_has_led(0)); } return bool_to_symbol(false); } u64 pc_current_controller_has_rumble() { if (Display::GetMainDisplay()) { return bool_to_symbol(Display::GetMainDisplay()->get_input_manager()->controller_has_rumble(0)); } return bool_to_symbol(false); } void pc_set_controller_led(const int port, const u8 red, const u8 green, const u8 blue) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_set_controller_led(port, red, green, blue); } } u64 pc_waiting_for_bind() { if (Display::GetMainDisplay()) { return bool_to_symbol(Display::GetMainDisplay()->get_input_manager()->get_waiting_for_bind()); } return bool_to_symbol(false); } void pc_set_waiting_for_bind(InputDeviceType device_type, u32 for_analog, u32 for_minimum_analog, s32 input_idx) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->set_wait_for_bind( device_type, symbol_to_bool(for_analog), symbol_to_bool(for_minimum_analog), input_idx); } } void pc_stop_waiting_for_bind() { if (Display::GetMainDisplay()) { return Display::GetMainDisplay()->get_input_manager()->stop_waiting_for_bind(); } } void pc_reset_bindings_to_defaults(const int port, const InputDeviceType device_type) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->reset_input_bindings_to_defaults(port, device_type); } } void pc_set_auto_hide_cursor(u32 val) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_set_auto_hide_mouse( symbol_to_bool(val)); } } u64 pc_get_pressure_sensitivity_enabled() { if (Display::GetMainDisplay()) { return bool_to_symbol( Display::GetMainDisplay()->get_input_manager()->is_pressure_sensitivity_enabled()); } return bool_to_symbol(false); } void pc_set_pressure_sensitivity_enabled(u32 val) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->set_pressure_sensitivity_enabled( symbol_to_bool(val)); } } void pc_set_axis_scale(u32 val) { if (Display::GetMainDisplay()) { // wow dangerous i guess Display::GetMainDisplay()->get_input_manager()->set_axis_scale(*((float*)&val)); } } u32 pc_get_axis_scale() { float out = 1.33f; if (Display::GetMainDisplay()) { out = Display::GetMainDisplay()->get_input_manager()->axis_scale(); } // wow dangerous i guess return *((u32*)&out); } u64 pc_current_controller_has_pressure_sensitivity() { if (Display::GetMainDisplay()) { return bool_to_symbol( Display::GetMainDisplay()->get_input_manager()->controller_has_pressure_sensitivity_support( 0)); } return bool_to_symbol(false); } u64 pc_current_controller_has_trigger_effect_support() { if (Display::GetMainDisplay()) { return bool_to_symbol( Display::GetMainDisplay()->get_input_manager()->controller_has_trigger_effect_support(0)); } return bool_to_symbol(false); } u64 pc_get_trigger_effects_enabled() { if (Display::GetMainDisplay()) { return bool_to_symbol( Display::GetMainDisplay()->get_input_manager()->are_trigger_effects_enabled()); } return bool_to_symbol(false); } void pc_set_trigger_effects_enabled(u32 val) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_set_trigger_effects_enabled( symbol_to_bool(val)); } } void pc_clear_trigger_effect(dualsense_effects::TriggerEffectOption option) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_controller_clear_trigger_effect(0, option); } } void pc_send_trigger_effect_feedback(dualsense_effects::TriggerEffectOption option, u8 position, u8 strength) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_controller_send_trigger_effect_feedback( 0, option, position, strength); } } void pc_send_trigger_effect_vibrate(dualsense_effects::TriggerEffectOption option, u8 position, u8 amplitude, u8 frequency) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_controller_send_trigger_effect_vibrate( 0, option, position, amplitude, frequency); } } void pc_send_trigger_effect_weapon(dualsense_effects::TriggerEffectOption option, u8 start_position, u8 end_position, u8 strength) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_controller_send_trigger_effect_weapon( 0, option, start_position, end_position, strength); } } void pc_send_trigger_rumble(u16 left_rumble, u16 right_rumble, u32 duration_ms) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_input_manager()->enqueue_controller_send_trigger_rumble( 0, left_rumble, right_rumble, duration_ms); } } void pc_set_vsync(u32 sym_val) { Gfx::g_global_settings.vsync = symbol_to_bool(sym_val); } void pc_set_msaa(int samples) { Gfx::g_global_settings.msaa_samples = samples; } void pc_set_frame_rate(int rate) { Gfx::g_global_settings.target_fps = rate; } void pc_set_game_resolution(int w, int h) { Gfx::g_global_settings.game_res_w = w; Gfx::g_global_settings.game_res_h = h; } void pc_set_letterbox(int w, int h) { Gfx::g_global_settings.lbox_w = w; Gfx::g_global_settings.lbox_h = h; } void pc_set_brightness_contrast(s32 color, s32 alpha) { Gfx::g_global_settings.brightness_contrast_color = color; Gfx::g_global_settings.brightness_contrast_alpha = alpha; } void pc_renderer_tree_set_lod(Gfx::RendererTreeType tree, int lod) { switch (tree) { case Gfx::RendererTreeType::TFRAG3: Gfx::g_global_settings.lod_tfrag = lod; break; case Gfx::RendererTreeType::TIE3: Gfx::g_global_settings.lod_tie = lod; break; default: lg::error("Invalid tree {} specified for SetLod ({})", fmt::underlying(tree), lod); break; } } void pc_set_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, s64 mask, u32 symptr) { if (symbol_to_bool(symptr)) { Gfx::CollisionRendererSetMask(mode, mask); } else { Gfx::CollisionRendererClearMask(mode, mask); } } u32 pc_get_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, s64 mask) { return Gfx::CollisionRendererGetMask(mode, mask) ? s7.offset + true_symbol_offset(g_game_version) : s7.offset; } void pc_set_collision_wireframe(u32 symptr) { Gfx::g_global_settings.collision_wireframe = symbol_to_bool(symptr); } void pc_set_collision(u32 symptr) { Gfx::g_global_settings.collision_enable = symbol_to_bool(symptr); } void pc_set_gfx_hack(u64 which, u32 symptr) { switch (which) { case 0: // no tex Gfx::g_global_settings.hack_no_tex = symbol_to_bool(symptr); break; } } u32 pc_get_os() { #ifdef _WIN32 return g_pc_port_funcs.intern_from_c("windows").offset; #elif __linux__ return g_pc_port_funcs.intern_from_c("linux").offset; #elif __APPLE__ return g_pc_port_funcs.intern_from_c("darwin").offset; #else return s7.offset; #endif } time_t pc_get_unix_timestamp() { return std::time(nullptr); } u64 pc_filepath_exists(u32 filepath) { auto filepath_str = std::string(Ptr(filepath).c()->data()); return bool_to_symbol(fs::exists(filepath_str)); } u64 pc_mkdir_filepath(u32 filepath) { auto filepath_str = std::string(Ptr(filepath).c()->data()); return bool_to_symbol(file_util::create_dir_if_needed_for_file(filepath_str)); } void pc_prof(u32 name, ProfNode::Kind kind) { prof().event(Ptr(name).c()->data(), kind); } std::mt19937 extra_random_generator; u32 pc_rand() { return (u32)extra_random_generator(); } void pc_treat_pad0_as_pad1(u32 symptr) { Gfx::g_debug_settings.treat_pad0_as_pad1 = symbol_to_bool(symptr); } u32 pc_is_imgui_visible() { return bool_to_symbol(Gfx::g_debug_settings.show_imgui); } void pc_screen_shot() { g_want_screenshot = true; } void pc_register_screen_shot_settings(u32 ptr) { register_screen_shot_settings(Ptr(ptr).c()); } void pc_encode_utf8_string(u32 src_str_ptr, u32 str_dest_ptr) { auto str = std::string(Ptr(src_str_ptr).c()->data()); std::string version = version_to_game_name(g_game_version); const std::string font_bank_name = version == "jak1" ? "jak1-v2" : version; std::string converted = get_font_bank(get_text_version_from_name(font_bank_name))->convert_utf8_to_game(str); strcpy(Ptr(str_dest_ptr).c()->data(), converted.c_str()); } /// Initializes all functions that are common across all game versions /// These functions have the same implementation and do not use any game specific functions (other /// than the one to create a function in the first place) void init_common_pc_port_functions( std::function(const char*, void*)> make_func_symbol_func, std::function intern_from_c_func, std::function make_string_from_c_func) { g_pc_port_funcs.intern_from_c = intern_from_c_func; g_pc_port_funcs.make_string_from_c = make_string_from_c_func; // Get a 300MHz timer value. Called from EE thread make_func_symbol_func("__read-ee-timer", (void*)read_ee_timer); // Do a fast memory copy. make_func_symbol_func("__mem-move", (void*)pc_memmove); // Called from game thread to submit rendering DMA chain. make_func_symbol_func("__send-gfx-dma-chain", (void*)send_gfx_dma_chain); // Called from game thread to upload a texture outside of the main DMA chain. make_func_symbol_func("__pc-texture-upload-now", (void*)pc_texture_upload_now); make_func_symbol_func("__pc-texture-relocate", (void*)pc_texture_relocate); // Called from the game thread at initialization. The game thread is the only one to touch the // mips2c function table (through the linker and ugh this function), so no locking is needed. make_func_symbol_func("__pc-get-mips2c", (void*)pc_get_mips2c); // -- DISPLAY RELATED -- // Returns the name of the display with the given id or #f if not found / empty make_func_symbol_func("pc-get-display-id", (void*)pc_get_display_id); make_func_symbol_func("pc-set-display-id!", (void*)pc_set_display_id); make_func_symbol_func("pc-get-display-name", (void*)pc_get_display_name); make_func_symbol_func("pc-get-display-mode", (void*)pc_get_display_mode); make_func_symbol_func("pc-set-display-mode!", (void*)pc_set_display_mode); make_func_symbol_func("pc-get-display-count", (void*)pc_get_display_count); // Returns resolution of the monitor's current display mode make_func_symbol_func("pc-get-active-display-size", (void*)pc_get_active_display_size); // Returns the current refresh rate of the currently selected monitor's display mode. make_func_symbol_func("pc-get-active-display-refresh-rate", (void*)pc_get_active_display_refresh_rate); // Returns size of window. Called from game thread make_func_symbol_func("pc-get-window-size", (void*)pc_get_window_size); // Returns scale of window. This is for DPI stuff. make_func_symbol_func("pc-get-window-scale", (void*)pc_get_window_scale); make_func_symbol_func("pc-set-window-size!", (void*)pc_set_window_size); make_func_symbol_func("pc-get-num-resolutions", (void*)pc_get_num_resolutions); make_func_symbol_func("pc-get-resolution", (void*)pc_get_resolution); make_func_symbol_func("pc-is-supported-resolution?", (void*)pc_is_supported_resolution); // -- INPUT RELATED -- // Returns the name of the display with the given id or #f if not found / empty make_func_symbol_func("pc-get-controller-name", (void*)pc_get_controller_name); make_func_symbol_func("pc-get-current-bind", (void*)pc_get_current_bind); make_func_symbol_func("pc-get-controller-count", (void*)pc_get_controller_count); make_func_symbol_func("pc-get-controller-index", (void*)pc_get_controller_index); make_func_symbol_func("pc-set-controller!", (void*)pc_set_controller); make_func_symbol_func("pc-get-keyboard-enabled?", (void*)pc_get_keyboard_enabled); make_func_symbol_func("pc-set-keyboard-enabled!", (void*)pc_set_keyboard_enabled); make_func_symbol_func("pc-set-mouse-options!", (void*)pc_set_mouse_options); make_func_symbol_func("pc-set-mouse-camera-sens!", (void*)pc_set_mouse_camera_sens); make_func_symbol_func("pc-ignore-background-controller-events!", (void*)pc_ignore_background_controller_events); make_func_symbol_func("pc-current-controller-has-led?", (void*)pc_current_controller_has_led); make_func_symbol_func("pc-current-controller-has-rumble?", (void*)pc_current_controller_has_rumble); make_func_symbol_func("pc-set-controller-led!", (void*)pc_set_controller_led); make_func_symbol_func("pc-waiting-for-bind?", (void*)pc_waiting_for_bind); make_func_symbol_func("pc-set-waiting-for-bind!", (void*)pc_set_waiting_for_bind); make_func_symbol_func("pc-stop-waiting-for-bind!", (void*)pc_stop_waiting_for_bind); make_func_symbol_func("pc-reset-bindings-to-defaults!", (void*)pc_reset_bindings_to_defaults); make_func_symbol_func("pc-set-auto-hide-cursor!", (void*)pc_set_auto_hide_cursor); make_func_symbol_func("pc-get-pressure-sensitivity-enabled?", (void*)pc_get_pressure_sensitivity_enabled); make_func_symbol_func("pc-set-pressure-sensitivity-enabled!", (void*)pc_set_pressure_sensitivity_enabled); make_func_symbol_func("pc-set-axis-scale!", (void*)pc_set_axis_scale); make_func_symbol_func("pc-get-axis-scale", (void*)pc_get_axis_scale); make_func_symbol_func("pc-current-controller-has-pressure-sensitivity?", (void*)pc_current_controller_has_pressure_sensitivity); make_func_symbol_func("pc-current-controller-has-trigger-effect-support?", (void*)pc_current_controller_has_trigger_effect_support); make_func_symbol_func("pc-get-trigger-effects-enabled?", (void*)pc_get_trigger_effects_enabled); make_func_symbol_func("pc-set-trigger-effects-enabled!", (void*)pc_set_trigger_effects_enabled); make_func_symbol_func("pc-clear-trigger-effect!", (void*)pc_clear_trigger_effect); make_func_symbol_func("pc-send-trigger-effect-feedback!", (void*)pc_send_trigger_effect_feedback); make_func_symbol_func("pc-send-trigger-effect-vibrate!", (void*)pc_send_trigger_effect_vibrate); make_func_symbol_func("pc-send-trigger-effect-weapon!", (void*)pc_send_trigger_effect_weapon); make_func_symbol_func("pc-send-trigger-rumble!", (void*)pc_send_trigger_rumble); // graphics things make_func_symbol_func("pc-set-vsync", (void*)pc_set_vsync); make_func_symbol_func("pc-set-msaa", (void*)pc_set_msaa); make_func_symbol_func("pc-set-frame-rate", (void*)pc_set_frame_rate); make_func_symbol_func("pc-set-game-resolution", (void*)pc_set_game_resolution); make_func_symbol_func("pc-set-brightness-contrast", (void*)pc_set_brightness_contrast); make_func_symbol_func("pc-set-letterbox", (void*)pc_set_letterbox); make_func_symbol_func("pc-renderer-tree-set-lod", (void*)pc_renderer_tree_set_lod); make_func_symbol_func("pc-set-collision-mode", (void*)Gfx::CollisionRendererSetMode); make_func_symbol_func("pc-set-collision-mask", (void*)pc_set_collision_mask); make_func_symbol_func("pc-get-collision-mask", (void*)pc_get_collision_mask); make_func_symbol_func("pc-set-collision-wireframe", (void*)pc_set_collision_wireframe); make_func_symbol_func("pc-set-collision", (void*)pc_set_collision); make_func_symbol_func("pc-set-gfx-hack", (void*)pc_set_gfx_hack); // -- OTHER -- // Return the current OS as a symbol. Actually returns what it was compiled for! make_func_symbol_func("pc-get-os", (void*)pc_get_os); make_func_symbol_func("pc-get-unix-timestamp", (void*)pc_get_unix_timestamp); make_func_symbol_func("pc-treat-pad0-as-pad1", (void*)pc_treat_pad0_as_pad1); make_func_symbol_func("pc-is-imgui-visible?", (void*)pc_is_imgui_visible); // file related functions make_func_symbol_func("pc-filepath-exists?", (void*)pc_filepath_exists); make_func_symbol_func("pc-mkdir-file-path", (void*)pc_mkdir_filepath); // discord rich presence make_func_symbol_func("pc-discord-rpc-set", (void*)set_discord_rpc); // profiler make_func_symbol_func("pc-prof", (void*)pc_prof); // RNG make_func_symbol_func("pc-rand", (void*)pc_rand); // text make_func_symbol_func("pc-encode-utf8-string", (void*)pc_encode_utf8_string); // debugging tools make_func_symbol_func("pc-filter-debug-string?", (void*)pc_filter_debug_string); make_func_symbol_func("pc-screen-shot", (void*)pc_screen_shot); make_func_symbol_func("pc-register-screen-shot-settings", (void*)pc_register_screen_shot_settings); }