mirror of
https://github.com/open-goal/jak-project
synced 2026-05-23 06:54:31 -04:00
4f537d4a71
This sets up the C Kernel for Jak 3, and makes it possible to build and load code built with `goalc --jak3`. There's not too much interesting here, other than they switched to a system where symbol IDs (unique numbers less than 2^14) are generated at compile time, and those get included in the object file itself. This is kind of annoying, since it means all tools that produce a GOAL object file need to work together to assign unique symbol IDs. And since the symbol IDs can't conflict, and are only a number between 0 and 2^14, you can't just hash and hope for no collisions. We work around this by ignoring the IDs and re-assigning our own. I think this is very similar to what the C Kernel did on early builds of Jak 3 which supported loading old format level files, which didn't have the IDs included. As far as I can tell, this shouldn't cause any problems. It defeats all of their fancy tricks to save memory by not storing the symbol string, but we don't care.
976 lines
31 KiB
C++
976 lines
31 KiB
C++
#include "kmachine.h"
|
|
|
|
#include <random>
|
|
|
|
#include "common/global_profiler/GlobalProfiler.h"
|
|
#include "common/log/log.h"
|
|
#include "common/symbols.h"
|
|
#include "common/util/FileUtil.h"
|
|
#include "common/util/FontUtils.h"
|
|
#include "common/util/Timer.h"
|
|
#include "common/util/string_util.h"
|
|
|
|
#include "game/external/discord.h"
|
|
#include "game/graphics/display.h"
|
|
#include "game/graphics/gfx.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/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.
|
|
* Not yet implemented. TODO
|
|
*/
|
|
void InitVideo() {}
|
|
|
|
/*!
|
|
* 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<CPadInfo>(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<CPadInfo>(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:
|
|
printf("unknown handler: %d\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<FileStream>(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<FileStream>(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<FileStream>(fs).c();
|
|
if ((file_stream->flags ^ 1) & 1) {
|
|
result = ee::sceRead(file_stream->file, Ptr<u8>(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<FileStream>(fs).c();
|
|
if ((file_stream->flags ^ 1) & 1) {
|
|
result = ee::sceWrite(file_stream->file, Ptr<u8>(buffer).c(), size);
|
|
if (result < 0) {
|
|
file_stream->flags |= 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Close a file stream.
|
|
*/
|
|
u64 kclose(u64 fs) {
|
|
auto file_stream = Ptr<FileStream>(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<ee::sceCdCLOCK> 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<Function>(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<u64>(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<String>(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<u8>(dst).c(), Ptr<u8>(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<u8>(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<String>(name).c()->data();
|
|
return Mips2C::gLinkedFunctionTable.get(n);
|
|
}
|
|
|
|
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<String>(str_dest_ptr).c()->data(), encoded_name.c_str());
|
|
strcpy(Ptr<String>(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 = WindowDisplayMode::Windowed;
|
|
if (Display::GetMainDisplay()) {
|
|
display_mode = Display::GetMainDisplay()->get_display_manager()->get_window_display_mode();
|
|
}
|
|
switch (display_mode) {
|
|
case WindowDisplayMode::Borderless:
|
|
return g_pc_port_funcs.intern_from_c("borderless").offset;
|
|
case WindowDisplayMode::Fullscreen:
|
|
return g_pc_port_funcs.intern_from_c("fullscreen").offset;
|
|
default:
|
|
case WindowDisplayMode::Windowed:
|
|
return g_pc_port_funcs.intern_from_c("windowed").offset;
|
|
}
|
|
}
|
|
|
|
void pc_set_display_mode(u32 symptr) {
|
|
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(
|
|
WindowDisplayMode::Windowed);
|
|
} else if (symptr == g_pc_port_funcs.intern_from_c("borderless").offset) {
|
|
Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_display_mode(
|
|
WindowDisplayMode::Borderless);
|
|
} else if (symptr == g_pc_port_funcs.intern_from_c("fullscreen").offset) {
|
|
Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_display_mode(
|
|
WindowDisplayMode::Fullscreen);
|
|
}
|
|
}
|
|
|
|
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<s64>(w_ptr).c();
|
|
if (w_out) {
|
|
*w_out = Display::GetMainDisplay()->get_display_manager()->get_screen_width();
|
|
}
|
|
}
|
|
if (h_ptr) {
|
|
auto h_out = Ptr<s64>(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<s64>(w_ptr).c();
|
|
if (w) {
|
|
*w = Display::GetMainDisplay()->get_display_manager()->get_window_width();
|
|
}
|
|
}
|
|
if (h_ptr) {
|
|
auto h = Ptr<s64>(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<float>(x_ptr).c();
|
|
if (x) {
|
|
*x = Display::GetMainDisplay()->get_display_manager()->get_window_scale_x();
|
|
}
|
|
}
|
|
if (y_ptr) {
|
|
auto y = Ptr<float>(y_ptr).c();
|
|
if (y) {
|
|
*y = Display::GetMainDisplay()->get_display_manager()->get_window_scale_y();
|
|
}
|
|
}
|
|
}
|
|
|
|
void pc_get_fullscreen_display(u64 display_id) {
|
|
if (Display::GetMainDisplay()) {
|
|
Display::GetMainDisplay()->get_display_manager()->enqueue_set_fullscreen_display_id(display_id);
|
|
}
|
|
}
|
|
|
|
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() {
|
|
if (Display::GetMainDisplay()) {
|
|
return Display::GetMainDisplay()->get_display_manager()->get_num_resolutions();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void pc_get_resolution(u32 id, u32 w_ptr, u32 h_ptr) {
|
|
if (Display::GetMainDisplay()) {
|
|
auto res = Display::GetMainDisplay()->get_display_manager()->get_resolution(id);
|
|
auto w = Ptr<s64>(w_ptr).c();
|
|
if (w) {
|
|
*w = res.width;
|
|
}
|
|
auto h = Ptr<s64>(h_ptr).c();
|
|
if (h) {
|
|
*h = res.height;
|
|
}
|
|
}
|
|
}
|
|
|
|
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<String>(str_dest_ptr).c()->data(), encoded_name.c_str());
|
|
strcpy(Ptr<String>(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<String>(str_dest_ptr).c()->data(), str_util::to_upper("unknown").c_str());
|
|
return bool_to_symbol(true);
|
|
}
|
|
|
|
auto info = bind_assignment_info ? Ptr<BindAssignmentInfo>(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<String>(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<String>(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);
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
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_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<String>(filepath).c()->data());
|
|
return bool_to_symbol(fs::exists(filepath_str));
|
|
}
|
|
|
|
u64 pc_mkdir_filepath(u32 filepath) {
|
|
auto filepath_str = std::string(Ptr<String>(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<String>(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);
|
|
}
|
|
|
|
/// 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<Ptr<Function>(const char*, void*)> make_func_symbol_func,
|
|
std::function<InternFromCInfo(const char*)> intern_from_c_func,
|
|
std::function<u64(const char*)> 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-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-fullscreen-display", (void*)pc_get_fullscreen_display);
|
|
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);
|
|
|
|
// -- 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-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);
|
|
|
|
// 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-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);
|
|
|
|
// debugging tools
|
|
make_func_symbol_func("pc-filter-debug-string?", (void*)pc_filter_debug_string);
|
|
}
|