mirror of
https://github.com/open-goal/jak-project.git
synced 2025-02-02 11:58:16 +00:00
[jak3] Set up ckernel (#3308)
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.
This commit is contained in:
parent
ed6639ee60
commit
4f537d4a71
@ -191,7 +191,7 @@ bool can_node_be_inlined(const FormatterTreeNode& curr_node, int cursor_pos) {
|
||||
}
|
||||
|
||||
std::vector<std::string> apply_formatting(const FormatterTreeNode& curr_node,
|
||||
std::vector<std::string> output = {},
|
||||
std::vector<std::string> /*output*/ = {},
|
||||
int cursor_pos = 0) {
|
||||
using namespace formatter_rules;
|
||||
if (!curr_node.token && curr_node.refs.empty()) {
|
||||
|
@ -9,7 +9,7 @@ namespace config {
|
||||
FormFormattingConfig new_flow_rule(int start_index) {
|
||||
FormFormattingConfig cfg;
|
||||
cfg.hang_forms = false;
|
||||
cfg.inline_until_index = [start_index](const std::vector<std::string>& curr_lines) {
|
||||
cfg.inline_until_index = [start_index](const std::vector<std::string>& /*curr_lines*/) {
|
||||
return start_index;
|
||||
};
|
||||
return cfg;
|
||||
|
@ -62,15 +62,9 @@ constexpr int LEVEL_MAX = 6;
|
||||
constexpr int LEVEL_TOTAL = LEVEL_MAX + 1;
|
||||
} // namespace jak2
|
||||
|
||||
// TODO copypaste from jak 2 for now
|
||||
namespace jak3 {
|
||||
// for now, we don't have the ability to extend the size of the symbol table
|
||||
constexpr s32 GOAL_MAX_SYMBOLS = 0x4000;
|
||||
constexpr s32 SYM_TABLE_MEM_SIZE = 0x30000;
|
||||
// from the "off-by-one" symbol pointer
|
||||
constexpr int SYM_TO_STRING_OFFSET = 0xff37;
|
||||
constexpr int SYM_TO_HASH_OFFSET = 0x1fe6f;
|
||||
|
||||
// amount of levels in level heap
|
||||
constexpr int LEVEL_MAX = 10;
|
||||
// total amount of levels, including ones outside level heap (default-level)
|
||||
@ -84,7 +78,7 @@ constexpr s32 max_symbols(GameVersion version) {
|
||||
case GameVersion::Jak2:
|
||||
return jak2::GOAL_MAX_SYMBOLS;
|
||||
case GameVersion::Jak3:
|
||||
return jak2::GOAL_MAX_SYMBOLS;
|
||||
return jak3::GOAL_MAX_SYMBOLS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,21 @@ struct LinkHeaderV4 {
|
||||
uint32_t code_size; // length of object data before link data starts
|
||||
};
|
||||
|
||||
struct LinkHeaderV5Core {
|
||||
uint32_t length_to_get_to_code; // 4 length.. of link data?
|
||||
uint16_t version; // 8
|
||||
uint16_t unknown; // 10
|
||||
uint32_t length_to_get_to_link; // 12
|
||||
uint32_t link_length; // 16
|
||||
uint8_t n_segments; // 20
|
||||
char name[59]; // 21 (really??)
|
||||
};
|
||||
|
||||
struct LinkHeaderV5 {
|
||||
uint32_t type_tag; // 0 always 0?
|
||||
LinkHeaderV5Core core;
|
||||
};
|
||||
|
||||
// when a u32/s32 symbol link contains this value, (s7 + <val>) should be a 4-byte aligned address,
|
||||
// not including the 1 byte symbol offset. (no effect in jak 1).
|
||||
constexpr u32 LINK_SYM_NO_OFFSET_FLAG = 0xbadbeef;
|
174
common/symbols.h
174
common/symbols.h
@ -150,6 +150,180 @@ constexpr int FIX_FIXED_SYM_END_OFFSET = 0xec;
|
||||
|
||||
} // namespace jak2_symbols
|
||||
|
||||
namespace jak3_symbols {
|
||||
|
||||
constexpr int FIX_SYM_EMPTY_CAR = -0x8;
|
||||
constexpr int S7_OFF_FIX_SYM_EMPTY_PAIR = -0x6 - 1;
|
||||
constexpr int FIX_SYM_EMPTY_CDR = -0x4;
|
||||
constexpr int FIX_SYM_FALSE = 0x0; // GOAL boolean #f (note that this is equal to the $s7 register)
|
||||
constexpr int FIX_SYM_TRUE = 0x4; // GOAL boolean #t
|
||||
constexpr int FIX_SYM_FUNCTION_TYPE = 0x8;
|
||||
constexpr int FIX_SYM_BASIC = 0xc;
|
||||
constexpr int FIX_SYM_STRING_TYPE = 0x10;
|
||||
constexpr int FIX_SYM_SYMBOL_TYPE = 0x14;
|
||||
constexpr int FIX_SYM_TYPE_TYPE = 0x18;
|
||||
constexpr int FIX_SYM_OBJECT_TYPE = 0x1c;
|
||||
constexpr int FIX_SYM_LINK_BLOCK = 0x20;
|
||||
constexpr int FIX_SYM_INTEGER = 0x24;
|
||||
constexpr int FIX_SYM_SINTEGER = 0x28;
|
||||
constexpr int FIX_SYM_UINTEGER = 0x2c;
|
||||
constexpr int FIX_SYM_BINTEGER = 0x30;
|
||||
constexpr int FIX_SYM_INT8 = 0x34;
|
||||
constexpr int FIX_SYM_INT16 = 0x38;
|
||||
constexpr int FIX_SYM_INT32 = 0x3c;
|
||||
constexpr int FIX_SYM_INT64 = 0x40;
|
||||
constexpr int FIX_SYM_INT128 = 0x44;
|
||||
constexpr int FIX_SYM_UINT8 = 0x48;
|
||||
constexpr int FIX_SYM_UINT16 = 0x4c;
|
||||
constexpr int FIX_SYM_UINT32 = 0x50;
|
||||
constexpr int FIX_SYM_UINT64 = 0x54;
|
||||
constexpr int FIX_SYM_UINT128 = 0x58;
|
||||
constexpr int FIX_SYM_FLOAT = 0x5c;
|
||||
constexpr int FIX_SYM_PROCESS_TREE = 0x60;
|
||||
constexpr int FIX_SYM_PROCESS_TYPE = 0x64;
|
||||
constexpr int FIX_SYM_THREAD = 0x68;
|
||||
constexpr int FIX_SYM_STRUCTURE = 0x6c;
|
||||
constexpr int FIX_SYM_PAIR_TYPE = 0x70;
|
||||
constexpr int FIX_SYM_POINTER = 0x74;
|
||||
constexpr int FIX_SYM_NUMBER = 0x78;
|
||||
constexpr int FIX_SYM_ARRAY = 0x7c;
|
||||
constexpr int FIX_SYM_VU_FUNCTION = 0x80;
|
||||
constexpr int FIX_SYM_CONNECTABLE = 0x84;
|
||||
constexpr int FIX_SYM_STACK_FRAME = 0x88;
|
||||
constexpr int FIX_SYM_FILE_STREAM = 0x8c;
|
||||
constexpr int FIX_SYM_HEAP = 0x90;
|
||||
constexpr int FIX_SYM_NOTHING_FUNC = 0x94;
|
||||
constexpr int FIX_SYM_DELETE_BASIC = 0x98;
|
||||
constexpr int FIX_SYM_STATIC = 0x9c;
|
||||
constexpr int FIX_SYM_GLOBAL_HEAP = 0xa0;
|
||||
constexpr int FIX_SYM_DEBUG = 0xa4;
|
||||
constexpr int FIX_SYM_LOADING_LEVEL = 0xa8;
|
||||
constexpr int FIX_SYM_LOADING_PACKAGE = 0xac;
|
||||
constexpr int FIX_SYM_PROCESS_LEVEL_HEAP = 0xb0;
|
||||
constexpr int FIX_SYM_STACK = 0xb4;
|
||||
constexpr int FIX_SYM_SCRATCH = 0xb8;
|
||||
constexpr int FIX_SYM_SCRATCH_TOP = 0xbc;
|
||||
constexpr int FIX_SYM_ZERO_FUNC = 0xc0;
|
||||
constexpr int FIX_SYM_ASIZE_OF_BASIC_FUNC = 0xc4;
|
||||
constexpr int FIX_SYM_COPY_BASIC_FUNC = 0xc8; // bugged name
|
||||
constexpr int FIX_SYM_LEVEL = 0xcc;
|
||||
constexpr int FIX_SYM_ART_GROUP = 0xd0;
|
||||
constexpr int FIX_SYM_TEXTURE_PAGE_DIR = 0xd4;
|
||||
constexpr int FIX_SYM_TEXTURE_PAGE = 0xd8;
|
||||
constexpr int FIX_SYM_SOUND = 0xdc;
|
||||
constexpr int FIX_SYM_DGO = 0xe0;
|
||||
constexpr int FIX_SYM_TOP_LEVEL = 0xe4;
|
||||
constexpr int FIX_SYM_QUOTE = 0xe8;
|
||||
constexpr int FIX_SYM_LISTENER_LINK_BLOCK = 0xec;
|
||||
constexpr int FIX_SYM_LISTENER_FUNCTION = 0xf0;
|
||||
constexpr int FIX_SYM_STACK_TOP = 0xf4;
|
||||
constexpr int FIX_SYM_STACK_BASE = 0xf8;
|
||||
constexpr int FIX_SYM_STACK_SIZE = 0xfc;
|
||||
constexpr int FIX_SYM_KERNEL_FUNCTION = 0x100;
|
||||
constexpr int FIX_SYM_KERNEL_PACKAGES = 0x104;
|
||||
constexpr int FIX_SYM_KERNEL_BOOT_MESSAGE = 0x108;
|
||||
constexpr int FIX_SYM_KERNEL_BOOT_MODE = 0x10c;
|
||||
constexpr int FIX_SYM_KERNEL_BOOT_LEVEL = 0x110;
|
||||
constexpr int FIX_SYM_KERNEL_BOOT_ART_GROUP = 0x114;
|
||||
constexpr int FIX_SYM_KERNEL_DEBUG = 0x118;
|
||||
constexpr int FIX_SYM_KERNEL_VERSION = 0x11c;
|
||||
constexpr int FIX_SYM_KERNEL_DISPATCHER = 0x120;
|
||||
constexpr int FIX_SYM_SYNC_DISPATCHER = 0x124;
|
||||
constexpr int FIX_SYM_PRINT_COLLUMN = 0x128;
|
||||
constexpr int FIX_SYM_DEBUG_SEGMENT = 300;
|
||||
constexpr int FIX_SYM_ENABLE_METHOD_SET = 0x130;
|
||||
constexpr int FIX_SYM_SQL_RESULT = 0x134;
|
||||
constexpr int FIX_SYM_COLLAPSE_QUOTE = 0x138;
|
||||
constexpr int FIX_SYM_LEVEL_TYPE_LIST = 0x13C;
|
||||
constexpr int FIX_SYM_DECI_COUNT = 0x140;
|
||||
constexpr int FIX_SYM_USER = 0x144;
|
||||
constexpr int FIX_SYM_VIDEO_MODE = 0x148;
|
||||
constexpr int FIX_SYM_BOOT_VIDEO_MODE = 0x14C;
|
||||
constexpr int FIX_SYM_BOOT = 0x150;
|
||||
constexpr int FIX_SYM_DEMO = 0x154;
|
||||
constexpr int FIX_SYM_DEMO_SHARED = 0x158;
|
||||
constexpr int FIX_SYM_PREVIEW = 0x15C;
|
||||
constexpr int FIX_SYM_KIOSK = 0x160;
|
||||
constexpr int FIX_SYM_PLAY_BOOT = 0x164;
|
||||
constexpr int FIX_SYM_SIN = 0x168;
|
||||
constexpr int FIX_SYM_COS = 0x16C;
|
||||
constexpr int FIX_SYM_PUT_DISPLAY_ENV = 0x170;
|
||||
constexpr int FIX_SYM_SYNCV = 0x174;
|
||||
constexpr int FIX_SYM_SYNC_PATH = 0x178;
|
||||
constexpr int FIX_SYM_RESET_PATH = 0x17C;
|
||||
constexpr int FIX_SYM_RESET_GRAPH = 0x180;
|
||||
constexpr int FIX_SYM_DMA_SYNC = 0x184;
|
||||
constexpr int FIX_SYM_GS_PUT_IMR = 0x188;
|
||||
constexpr int FIX_SYM_GS_GET_IMR = 0x18C;
|
||||
constexpr int FIX_SYM_GS_STORE_IMAGE = 400;
|
||||
constexpr int FIX_SYM_FLUSH_CACHE = 0x194;
|
||||
constexpr int FIX_SYM_CPAD_OPEN = 0x198;
|
||||
constexpr int FIX_SYM_CPAD_GET_DATA = 0x19C;
|
||||
constexpr int FIX_SYM_MOUSE_GET_DATA = 0x1A0;
|
||||
constexpr int FIX_SYM_KEYBD_GET_DATA = 0x1A4;
|
||||
constexpr int FIX_SYM_INSTALL_HANDLER = 0x1A8;
|
||||
constexpr int FIX_SYM_INSTALL_DEBUG_HANDLER = 0x1AC;
|
||||
constexpr int FIX_SYM_FILE_STREAM_OPEN = 0x1B0;
|
||||
constexpr int FIX_SYM_FILE_STREAM_CLOSE = 0x1B4;
|
||||
constexpr int FIX_SYM_FILE_STREAM_LENGTH = 0x1B8;
|
||||
constexpr int FIX_SYM_FILE_STREAM_SEEK = 0x1BC;
|
||||
constexpr int FIX_SYM_FILE_STREAM_READ = 0x1C0;
|
||||
constexpr int FIX_SYM_FILE_STREAM_WRITE = 0x1C4;
|
||||
constexpr int FIX_SYM_SCF_GET_LANGUAGE = 0x1C8;
|
||||
constexpr int FIX_SYM_SCF_GET_TIME = 0x1CC;
|
||||
constexpr int FIX_SYM_SCF_GET_ASPECT = 0x1D0;
|
||||
constexpr int FIX_SYM_SCF_GET_VOLUME = 0x1D4;
|
||||
constexpr int FIX_SYM_SCF_GET_TERRITORY = 0x1D8;
|
||||
constexpr int FIX_SYM_SCF_GET_TIMEOUT = 0x1DC;
|
||||
constexpr int FIX_SYM_SCF_GET_INACTIVE_TIMEOUT = 0x1E0;
|
||||
constexpr int FIX_SYM_DMA_TO_IOP = 0x1E4;
|
||||
constexpr int FIX_SYM_KERNEL_SHUTDOWN = 0x1E8;
|
||||
constexpr int FIX_SYM_AYBABTU = 0x1EC;
|
||||
constexpr int FIX_SYM_STRING_TO_SYMBOL = 0x1F0;
|
||||
constexpr int FIX_SYM_SYMBOL_TO_STRING = 500;
|
||||
constexpr int FIX_SYM_PRINT = 0x1F8;
|
||||
constexpr int FIX_SYM_INSPECT = 0x1FC;
|
||||
constexpr int FIX_SYM_LOAD = 0x200;
|
||||
constexpr int FIX_SYM_LOADB = 0x204;
|
||||
constexpr int FIX_SYM_LOADO = 0x208;
|
||||
constexpr int FIX_SYM_UNLOAD = 0x20C;
|
||||
constexpr int FIX_SYM_FORMAT = 0x210;
|
||||
constexpr int FIX_SYM_MALLOC = 0x214;
|
||||
constexpr int FIX_SYM_KMALLOC = 0x218;
|
||||
constexpr int FIX_SYM_KMEMOPEN = 0x21C;
|
||||
constexpr int FIX_SYM_KMEMCLOSE = 0x220;
|
||||
constexpr int FIX_SYM_NEW_DYNAMIC_STRUCTURE = 0x224;
|
||||
constexpr int FIX_SYM_METHOD_SET = 0x228;
|
||||
constexpr int FIX_SYM_LINK = 0x22C;
|
||||
constexpr int FIX_SYM_LINK_BUSY = 0x230;
|
||||
constexpr int FIX_SYM_LINK_RESET = 0x234;
|
||||
constexpr int FIX_SYM_LINK_BEGIN = 0x238;
|
||||
constexpr int FIX_SYM_LINK_RESUME = 0x23C;
|
||||
constexpr int FIX_SYM_DGO_LOAD = 0x240;
|
||||
constexpr int FIX_SYM_SQL_QUERY = 0x244;
|
||||
constexpr int FIX_SYM_MC_RUN = 0x248;
|
||||
constexpr int FIX_SYM_MC_FORMAT = 0x24C;
|
||||
constexpr int FIX_SYM_MC_UNFORMAT = 0x250;
|
||||
constexpr int FIX_SYM_MC_CREATE_FILE = 0x254;
|
||||
constexpr int FIX_SYM_MC_SAVE = 600;
|
||||
constexpr int FIX_SYM_MC_LOAD = 0x25C;
|
||||
constexpr int FIX_SYM_MC_CHECK_RESULT = 0x260;
|
||||
constexpr int FIX_SYM_MC_GET_SLOT_INFO = 0x264;
|
||||
constexpr int FIX_SYM_MC_MAKEFILE = 0x268;
|
||||
constexpr int FIX_SYM_KSET_LANGUAGE = 0x26C;
|
||||
constexpr int FIX_SYM_RPC_CALL = 0x270;
|
||||
constexpr int FIX_SYM_RPC_BUSY = 0x274;
|
||||
constexpr int FIX_SYM_TEST_LOAD_DGO_C = 0x278;
|
||||
constexpr int FIX_SYM_SYMLINK2 = 0x27c;
|
||||
constexpr int FIX_SYM_SYMLINK3 = 0x280;
|
||||
constexpr int FIX_SYM_ULTIMATE_MEMCPY = 0x284;
|
||||
constexpr int FIX_SYM_PLAY = 0x288;
|
||||
constexpr int FIX_SYM_SYMBOL_STRING = 0x28c;
|
||||
constexpr int FIX_SYM_KERNEL_SYMBOL_WARNINGS = 0x290;
|
||||
constexpr int FIX_SYM_NETWORK_BOOTSTRAP = 0x294;
|
||||
|
||||
} // namespace jak3_symbols
|
||||
|
||||
constexpr int false_symbol_offset() {
|
||||
return jak1_symbols::FIX_SYM_FALSE;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ constexpr u32 TX_PAGE_VERSION = 8;
|
||||
namespace jak3 {
|
||||
constexpr u32 ART_FILE_VERSION = 8;
|
||||
constexpr u32 LEVEL_FILE_VERSION = 36;
|
||||
constexpr u32 DGO_FILE_VERSION = 1;
|
||||
constexpr u32 TX_PAGE_VERSION = 8;
|
||||
} // namespace jak3
|
||||
|
||||
|
@ -472,7 +472,7 @@ static void link_v5(LinkedObjectFile& f,
|
||||
for (int i = 0; i < n_segs; i++) {
|
||||
segment_data_offsets[i] = header->length_to_get_to_code + seg_info_array[i].data;
|
||||
segment_link_offsets[i] = header->length_to_get_to_link + seg_info_array[i].relocs;
|
||||
ASSERT(seg_info_array[i].magic == 1);
|
||||
ASSERT(seg_info_array[i].magic == 1); // if set, always use symlink2.
|
||||
}
|
||||
|
||||
// check that the data region is filled
|
||||
|
@ -367,7 +367,7 @@ void ObjectFileDB::ir2_analyze_all_types(const fs::path& output_file,
|
||||
for (const auto& [obj_name, type_names] : all_type_names) {
|
||||
for (const auto& type_name : type_names) {
|
||||
if (str_util::starts_with(sym_name, type_name) &&
|
||||
type_name.length() > longest_match_length) {
|
||||
(int)type_name.length() > longest_match_length) {
|
||||
longest_match_length = type_name.length();
|
||||
longest_match = type_name;
|
||||
longest_match_object_name = obj_name;
|
||||
|
@ -1132,38 +1132,40 @@
|
||||
(defenum process-mask
|
||||
:type uint32
|
||||
:bitfield #t
|
||||
(execute 0)
|
||||
(execute 0) ;; 1
|
||||
(freeze 1)
|
||||
(pause 2)
|
||||
(menu 3)
|
||||
(progress 4)
|
||||
(actor-pause 5)
|
||||
(sleep 6)
|
||||
(sleep-code 7)
|
||||
(process-tree 8)
|
||||
(heap-shrunk 9)
|
||||
(going 10)
|
||||
(kernel-run 11)
|
||||
(no-kill 12)
|
||||
(movie 13)
|
||||
(dark-effect 14)
|
||||
(target 15)
|
||||
(sidekick 16)
|
||||
(crate 17)
|
||||
(bit18 18) ;; unused?
|
||||
(enemy 19)
|
||||
(camera 20)
|
||||
(platform 21)
|
||||
(ambient 22)
|
||||
(entity 23)
|
||||
(projectile 24)
|
||||
(bot 25)
|
||||
(collectable 26)
|
||||
(death 27)
|
||||
(no-track 28)
|
||||
(guard 29)
|
||||
(vehicle 30)
|
||||
(civilian 31))
|
||||
(actor-pause 5) ;; 32
|
||||
(sleep 6) ;; 64
|
||||
(sleep-code 7) ;; 128
|
||||
(process-tree 8) ;; 256
|
||||
(heap-shrunk 9) ;; 512
|
||||
(going 10) ;; 1024
|
||||
(kernel-run 11) ;; 2048
|
||||
(no-kill 12) ;; 4096
|
||||
(movie 13) ;; 8192
|
||||
(dark-effect 14) ;; 0x4000
|
||||
(target 15) ;; 0x8000
|
||||
|
||||
(sidekick 16) ;; 0x1'0000
|
||||
(crate 17) ;; 0x2'0000
|
||||
(collectable 18) ;; 0x4'0000
|
||||
(enemy 19) ;; 0x8'0000
|
||||
(camera 20) ;; 0x10'0000
|
||||
(platform 21) ;; 0x20'0000
|
||||
(ambient 22) ;; 0x40'0000
|
||||
(entity 23) ;; 0x80'0000
|
||||
(projectile 24) ;; 0x100'0000
|
||||
(bot 25) ;; 0x200'0000
|
||||
(death 26) ;; 0x400'0000
|
||||
(guard 27) ;; 0x800'0000
|
||||
(vehicle 28) ;; 0x1000'0000
|
||||
(civilian 29) ;; 0x2000'0000
|
||||
(kg-robot 30) ;; 0x4000'0000
|
||||
(metalhead 31) ;; 0x8000'0000
|
||||
)
|
||||
|
||||
(deftype process-tree (basic)
|
||||
((name string :offset-assert 4)
|
||||
@ -1180,11 +1182,11 @@
|
||||
:flag-assert #xe00000024
|
||||
:no-runtime-type
|
||||
(:methods
|
||||
;; (new (symbol type string) _type_ 0)
|
||||
(process-tree-method-9 () none) ;; 9 ;; (activate (_type_ process-tree basic pointer) process-tree 9)
|
||||
(process-tree-method-10 () none) ;; 10 ;; (deactivate (_type_) none 10)
|
||||
(process-tree-method-11 () none) ;; 11 ;; (init-from-entity! (_type_ entity-actor) none 11)
|
||||
(process-tree-method-12 () none) ;; 12 ;; (run-logic? (_type_) symbol 12)
|
||||
(new (symbol type string) _type_)
|
||||
(activate (_type_ process-tree basic pointer) process-tree)
|
||||
(deactivate (_type_) none)
|
||||
(init-from-entity! (_type_ entity-actor) none)
|
||||
(run-logic? (_type_) symbol)
|
||||
(process-tree-method-13 () none) ;; 13 ;; (process-tree-method-13 () none 13)
|
||||
)
|
||||
)
|
||||
@ -1210,7 +1212,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x40
|
||||
:flag-assert #x900000040
|
||||
;; field relocating-level uses ~A with a signed load.
|
||||
;; field relocating-level uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -1239,22 +1241,22 @@
|
||||
:size-assert #x5c
|
||||
:flag-assert #x180000005c
|
||||
(:methods
|
||||
;; (new (symbol type int) _type_ 0)
|
||||
(clock-method-9 () none) ;; 9 ;; (update-rates! (_type_ float) float 9)
|
||||
(new (symbol type int) _type_)
|
||||
(update-rates! (_type_ float) float)
|
||||
(clock-method-10 () none) ;; 10 ;; (advance-by! (_type_ float) clock 10)
|
||||
(clock-method-11 () none) ;; 11 ;; (tick! (_type_) clock 11)
|
||||
(clock-method-12 () none) ;; 12 ;; (save! (_type_ (pointer uint64)) int 12)
|
||||
(clock-method-13 () none) ;; 13 ;; (load! (_type_ (pointer uint64)) int 13)
|
||||
(clock-method-14 () none) ;; 14 ;; (reset! (_type_) none 14)
|
||||
(clock-method-15 () none) ;; 15
|
||||
(clock-method-16 () none) ;; 16
|
||||
(clock-method-17 () none) ;; 17
|
||||
(clock-method-18 () none) ;; 18
|
||||
(clock-method-19 () none) ;; 19
|
||||
(clock-method-20 () none) ;; 20
|
||||
(clock-method-21 () none) ;; 21
|
||||
(clock-method-22 () none) ;; 22
|
||||
(clock-method-23 () none) ;; 23
|
||||
(frame-mask-2 (_type_ int) symbol)
|
||||
(frame-mask-4 (_type_ int) symbol)
|
||||
(frame-mask-8 (_type_ int) symbol)
|
||||
(frame-mask-16 (_type_ int) symbol)
|
||||
(frame-mask-32 (_type_ int) symbol)
|
||||
(frame-mask-64 (_type_ int) symbol)
|
||||
(frame-mask-128 (_type_ int) symbol)
|
||||
(frame-mask-256 (_type_ int) symbol)
|
||||
)
|
||||
)
|
||||
|
||||
@ -4067,7 +4069,7 @@
|
||||
:method-count-assert 209
|
||||
:size-assert #x4e0
|
||||
:flag-assert #xd1046004e0
|
||||
;; field gekko-flag is likely a value type.
|
||||
;; field gekko-flag is likely a value type.
|
||||
(:methods
|
||||
(gekko-method-202 () none) ;; 202
|
||||
(gekko-method-203 () none) ;; 203
|
||||
@ -4793,7 +4795,7 @@
|
||||
:method-count-assert 13
|
||||
:size-assert #x30
|
||||
:flag-assert #xd00000030
|
||||
;; field on-enter uses ~A with a signed load. field on-inside uses ~A with a signed load. field on-exit uses ~A with a signed load.
|
||||
;; field on-enter uses ~A with a signed load. field on-inside uses ~A with a signed load. field on-exit uses ~A with a signed load.
|
||||
(:methods
|
||||
;; (new (symbol type) _type_ 0)
|
||||
(editable-region-method-9 () none) ;; 9 ;; (editable-region-method-9 (_type_ editable-array int int) symbol 9)
|
||||
@ -5256,7 +5258,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x28
|
||||
:flag-assert #x900000028
|
||||
;; field textures uses ~A with a signed load.
|
||||
;; field textures uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -6665,7 +6667,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x20
|
||||
:flag-assert #x900000020
|
||||
;; field speech-type-flag is likely a value type.
|
||||
;; field speech-type-flag is likely a value type.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -6699,7 +6701,7 @@
|
||||
:method-count-assert 15
|
||||
:size-assert #x64
|
||||
:flag-assert #xf00000064
|
||||
;; field speech-channel-flag is likely a value type.
|
||||
;; field speech-channel-flag is likely a value type.
|
||||
(:methods
|
||||
(speech-channel-method-9 () none) ;; 9 ;; (speech-channel-method-9 (_type_ process-drawable speech-type) none 9)
|
||||
(speech-channel-method-10 () none) ;; 10 ;; (speech-channel-method-10 (_type_ handle) none 10)
|
||||
@ -6905,7 +6907,7 @@
|
||||
:method-count-assert 216
|
||||
:size-assert #x404
|
||||
:flag-assert #xd803900404
|
||||
;; field citizen-flag is likely a value type.
|
||||
;; field citizen-flag is likely a value type.
|
||||
(:methods
|
||||
(citizen-method-190 () none) ;; 190 ;; (citizen-method-190 (_type_ vector) none 190)
|
||||
(citizen-method-191 () none) ;; 191 ;; (gen-clear-path (_type_) nav-segment 191)
|
||||
@ -7322,7 +7324,7 @@
|
||||
:method-count-assert 155
|
||||
:size-assert #x228
|
||||
:flag-assert #x9b01b00228
|
||||
;; field enemy-flag is likely a value type. field on-notice uses ~A with a signed load. field on-active uses ~A with a signed load. field on-hostile uses ~A with a signed load. field on-death uses ~A with a signed load.
|
||||
;; field enemy-flag is likely a value type. field on-notice uses ~A with a signed load. field on-active uses ~A with a signed load. field on-hostile uses ~A with a signed load. field on-death uses ~A with a signed load.
|
||||
(:methods
|
||||
(enemy-method-28 () none) ;; 28 ;; (dormant-aware () _type_ :state 28)
|
||||
(enemy-method-29 () none) ;; 29 ;; (hit () _type_ :state 29)
|
||||
@ -8202,7 +8204,7 @@
|
||||
:method-count-assert 12
|
||||
:size-assert #x28
|
||||
:flag-assert #xc00000028
|
||||
;; field out uses ~A with a signed load.
|
||||
;; field out uses ~A with a signed load.
|
||||
(:methods
|
||||
;; (new (symbol type uint) _type_ 0)
|
||||
(history-iterator-method-9 () none) ;; 9 ;; (frame-counter-delta (_type_ history-elt) time-frame 9)
|
||||
@ -10303,7 +10305,7 @@
|
||||
:method-count-assert 12
|
||||
:size-assert #xa8
|
||||
:flag-assert #xc000000a8
|
||||
;; field handle is likely a value type.
|
||||
;; field handle is likely a value type.
|
||||
(:methods
|
||||
(attack-info-method-9 () none) ;; 9 ;; (attack-info-method-9 (_type_ attack-info process-drawable process-drawable) none 9)
|
||||
(attack-info-method-10 () none) ;; 10 ;; (compute-intersect-info (_type_ object process-drawable process touching-shapes-entry) attack-info 10)
|
||||
@ -10647,7 +10649,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x20
|
||||
:flag-assert #x900000020
|
||||
;; field check-too-far uses ~A with a signed load.
|
||||
;; field check-too-far uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -10670,7 +10672,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x30
|
||||
:flag-assert #x900000030
|
||||
;; field default-check-too-far uses ~A with a signed load.
|
||||
;; field default-check-too-far uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -11359,7 +11361,7 @@
|
||||
:method-count-assert 11
|
||||
:size-assert #xa0
|
||||
:flag-assert #xb000000a0
|
||||
;; field extra-sound-bank uses ~A with a signed load.
|
||||
;; field extra-sound-bank uses ~A with a signed load.
|
||||
(:methods
|
||||
(level-load-info-method-9 () none) ;; 9
|
||||
(level-load-info-method-10 () none) ;; 10
|
||||
@ -12400,7 +12402,7 @@
|
||||
:method-count-assert 14
|
||||
:size-assert #x20
|
||||
:flag-assert #xe00000020
|
||||
;; field param1 uses ~A with a signed load. field param2 uses ~A with a signed load. field param3 uses ~A with a signed load.
|
||||
;; field param1 uses ~A with a signed load. field param2 uses ~A with a signed load. field param3 uses ~A with a signed load.
|
||||
(:methods
|
||||
(connection-method-9 () none) ;; 9 ;; (get-engine (connection) engine 9)
|
||||
(connection-method-10 () none) ;; 10 ;; (get-process (connection) process 10)
|
||||
@ -12468,7 +12470,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x20
|
||||
:flag-assert #x900000020
|
||||
;; field key uses ~A with a signed load.
|
||||
;; field key uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -12823,7 +12825,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x50
|
||||
:flag-assert #x900000050
|
||||
;; field key uses ~A with a signed load.
|
||||
;; field key uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -13152,7 +13154,7 @@
|
||||
:method-count-assert 50
|
||||
:size-assert #x70
|
||||
:flag-assert #x3200000070
|
||||
;; field nav-mesh-flag is likely a value type.
|
||||
;; field nav-mesh-flag is likely a value type.
|
||||
(:methods
|
||||
(nav-mesh-method-9 () none) ;; 9 ;; (debug-draw (_type_) none 9)
|
||||
(nav-mesh-method-10 () none) ;; 10 ;; (nav-mesh-method-10 (_type_ vector vector nav-poly) nav-poly 10)
|
||||
@ -13488,7 +13490,7 @@
|
||||
:method-count-assert 11
|
||||
:size-assert #x30c
|
||||
:flag-assert #xb0000030c
|
||||
;; field cam-slave-options is likely a value type. field cam-master-options is likely a value type.
|
||||
;; field cam-slave-options is likely a value type. field cam-master-options is likely a value type.
|
||||
(:methods
|
||||
(cam-setting-data-method-9 () none) ;; 9 ;; (cam-setting-data-method-9 (_type_ engine engine-pers engine) _type_ 9)
|
||||
(cam-setting-data-method-10 () none) ;; 10 ;; (cam-setting-data-method-10 (_type_ object (pointer process) float int) _type_ 10)
|
||||
@ -13643,7 +13645,7 @@
|
||||
:method-count-assert 30
|
||||
:size-assert #xa00
|
||||
:flag-assert #x1e09800a00
|
||||
;; field mode-param2 uses ~A with a 64-bit load. field mode-param3 uses ~A with a 64-bit load.
|
||||
;; field mode-param2 uses ~A with a 64-bit load. field mode-param3 uses ~A with a 64-bit load.
|
||||
(:methods
|
||||
(target-method-28 () none) ;; 28 ;; (init-target (_type_ continue-point symbol) none 28)
|
||||
(target-method-29 () none) ;; 29
|
||||
@ -15664,7 +15666,7 @@
|
||||
:method-count-assert 10
|
||||
:size-assert #x20
|
||||
:flag-assert #xa00000020
|
||||
;; field param1 uses ~A with a signed load. field param2 uses ~A with a signed load.
|
||||
;; field param1 uses ~A with a signed load. field param2 uses ~A with a signed load.
|
||||
(:methods
|
||||
;; (new (symbol type drawable) _type_ 0)
|
||||
(cspace-method-9 () none) ;; 9 ;; (reset-and-assign-geo! (_type_ drawable) _type_ 9)
|
||||
@ -15907,7 +15909,7 @@
|
||||
:method-count-assert 17
|
||||
:size-assert #x20
|
||||
:flag-assert #x1100000020
|
||||
;; field distance is a float printed as hex?
|
||||
;; field distance is a float printed as hex?
|
||||
)
|
||||
|#
|
||||
|
||||
@ -16071,7 +16073,7 @@
|
||||
:method-count-assert 52
|
||||
:size-assert #x1a0
|
||||
:flag-assert #x34012001a0
|
||||
;; field on-activate uses ~A with a signed load. field on-deactivate uses ~A with a signed load. field on-up uses ~A with a signed load. field on-down uses ~A with a signed load. field on-running uses ~A with a signed load. field on-notice uses ~A with a signed load. field on-wait uses ~A with a signed load. field activate-test uses ~A with a signed load.
|
||||
;; field on-activate uses ~A with a signed load. field on-deactivate uses ~A with a signed load. field on-up uses ~A with a signed load. field on-down uses ~A with a signed load. field on-running uses ~A with a signed load. field on-notice uses ~A with a signed load. field on-wait uses ~A with a signed load. field activate-test uses ~A with a signed load.
|
||||
(:methods
|
||||
(elevator-method-39 () none) ;; 39 ;; (calc-dist-between-points! (_type_ int int) none 39)
|
||||
(elevator-method-41 () none) ;; 41 ;; (init-defaults! (_type_) none 41)
|
||||
@ -16232,7 +16234,7 @@
|
||||
:method-count-assert 10
|
||||
:size-assert #x10
|
||||
:flag-assert #xa00000010
|
||||
;; field on-enter uses ~A with a signed load. field on-inside uses ~A with a signed load. field on-exit uses ~A with a signed load.
|
||||
;; field on-enter uses ~A with a signed load. field on-inside uses ~A with a signed load. field on-exit uses ~A with a signed load.
|
||||
(:methods
|
||||
(region-method-9 () none) ;; 9 ;; (region-method-9 (_type_ vector) symbol 9)
|
||||
)
|
||||
@ -17081,7 +17083,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x10
|
||||
:flag-assert #x900000010
|
||||
;; field execute uses ~A with a signed load.
|
||||
;; field execute uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -17098,7 +17100,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x30
|
||||
:flag-assert #x900000030
|
||||
;; field resetter-flag is likely a value type.
|
||||
;; field resetter-flag is likely a value type.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -17125,7 +17127,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x3c
|
||||
:flag-assert #x90000003c
|
||||
;; field handle is likely a value type. field resolution-scene uses ~A with a signed load. field on-complete uses ~A with a signed load. field on-fail uses ~A with a signed load.
|
||||
;; field handle is likely a value type. field resolution-scene uses ~A with a signed load. field on-complete uses ~A with a signed load. field on-fail uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -17170,7 +17172,7 @@
|
||||
:method-count-assert 14
|
||||
:size-assert #x50
|
||||
:flag-assert #xe00000050
|
||||
;; field on-open uses ~A with a signed load. field on-close uses ~A with a signed load.
|
||||
;; field on-open uses ~A with a signed load. field on-close uses ~A with a signed load.
|
||||
(:methods
|
||||
(game-task-node-info-method-9 () none) ;; 9 ;; (close! (_type_ symbol) int 9)
|
||||
(game-task-node-info-method-10 () none) ;; 10 ;; (open! (_type_ symbol) int 10)
|
||||
@ -17195,7 +17197,7 @@
|
||||
:method-count-assert 10
|
||||
:size-assert #x20
|
||||
:flag-assert #xa00000020
|
||||
;; field kiosk-play-continue uses ~A with a signed load.
|
||||
;; field kiosk-play-continue uses ~A with a signed load.
|
||||
(:methods
|
||||
(game-task-info-method-9 () none) ;; 9
|
||||
)
|
||||
@ -18025,7 +18027,7 @@
|
||||
:method-count-assert 14
|
||||
:size-assert #x3c
|
||||
:flag-assert #xe0000003c
|
||||
;; field blend-shape-anim uses ~A with a signed load.
|
||||
;; field blend-shape-anim uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -18201,7 +18203,7 @@
|
||||
:method-count-assert 16
|
||||
:size-assert #xdc
|
||||
:flag-assert #x10000000dc
|
||||
;; field draw-control-status is likely a value type. field draw-control-global-effect is likely a value type.
|
||||
;; field draw-control-status is likely a value type. field draw-control-global-effect is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type process symbol) _type_ 0)
|
||||
(draw-control-method-9 () none) ;; 9 ;; (get-skeleton-origin (_type_) vector 9)
|
||||
@ -18255,7 +18257,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x1c
|
||||
:flag-assert #x90000001c
|
||||
;; field parm0 uses ~A with a signed load. field parm1 uses ~A with a signed load.
|
||||
;; field parm0 uses ~A with a signed load. field parm1 uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -18283,7 +18285,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x1c
|
||||
:flag-assert #x90000001c
|
||||
;; field track-val uses ~A with a signed load. field val-parm0 uses ~A with a signed load. field val-parm1 uses ~A with a signed load.
|
||||
;; field track-val uses ~A with a signed load. field val-parm0 uses ~A with a signed load. field val-parm1 uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -18788,7 +18790,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x80
|
||||
:flag-assert #x900000080
|
||||
;; field xyz-scale is a float printed as hex?
|
||||
;; field xyz-scale is a float printed as hex?
|
||||
)
|
||||
|#
|
||||
|
||||
@ -19826,7 +19828,7 @@
|
||||
:method-count-assert 12
|
||||
:size-assert #x68
|
||||
:flag-assert #xc00000068
|
||||
;; field on-goto uses ~A with a signed load.
|
||||
;; field on-goto uses ~A with a signed load.
|
||||
(:methods
|
||||
(continue-point-method-9 () none) ;; 9 ;; (debug-draw (_type_) int 9)
|
||||
(continue-point-method-10 () none) ;; 10 ;; (continue-point-method-10 (_type_ load-state) continue-point 10)
|
||||
@ -20181,7 +20183,7 @@
|
||||
:method-count-assert 14
|
||||
:size-assert #x30
|
||||
:flag-assert #xe00000030
|
||||
;; field param1 uses ~A with a signed load. field param2 uses ~A with a signed load. field param3 uses ~A with a signed load.
|
||||
;; field param1 uses ~A with a signed load. field param2 uses ~A with a signed load. field param3 uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -20470,7 +20472,7 @@
|
||||
:method-count-assert 14
|
||||
:size-assert #x20
|
||||
:flag-assert #xe00000020
|
||||
;; field on-close uses ~A with a signed load.
|
||||
;; field on-close uses ~A with a signed load.
|
||||
(:methods
|
||||
(talker-speech-class-method-9 () none) ;; 9 ;; (talker-speech-class-method-9 (_type_) symbol 9)
|
||||
(talker-speech-class-method-10 () none) ;; 10 ;; (play-communicator-speech! (_type_) none 10)
|
||||
@ -21138,7 +21140,7 @@
|
||||
:method-count-assert 13
|
||||
:size-assert #x40
|
||||
:flag-assert #xd00000040
|
||||
;; field joint-control-status is likely a value type.
|
||||
;; field joint-control-status is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type int) _type_ 0)
|
||||
(joint-control-method-9 () none) ;; 9 ;; (current-cycle-distance (_type_) float 9)
|
||||
@ -21498,7 +21500,7 @@
|
||||
:method-count-assert 14
|
||||
:size-assert #xb0
|
||||
:flag-assert #xe000000b0
|
||||
;; field param1 uses ~A with a signed load. field param2 uses ~A with a signed load. field param3 uses ~A with a signed load.
|
||||
;; field param1 uses ~A with a signed load. field param2 uses ~A with a signed load. field param3 uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -21568,7 +21570,7 @@
|
||||
:method-count-assert 22
|
||||
:size-assert #x20
|
||||
:flag-assert #x1600000020
|
||||
;; field extra uses ~A with a signed load.
|
||||
;; field extra uses ~A with a signed load.
|
||||
(:methods
|
||||
;; (new (symbol type int int) _type_ 0)
|
||||
(res-lump-method-9 () none) ;; 9 ;; (get-property-data (_type_ symbol symbol float pointer (pointer res-tag) pointer) pointer 9)
|
||||
@ -21680,7 +21682,7 @@
|
||||
:method-count-assert 45
|
||||
:size-assert #x15c
|
||||
:flag-assert #x2d00e0015c
|
||||
;; field on-activate uses ~A with a signed load.
|
||||
;; field on-activate uses ~A with a signed load.
|
||||
(:methods
|
||||
(wstd-arena-plat-method-43 () none) ;; 43
|
||||
(wstd-arena-plat-method-44 () none) ;; 44
|
||||
@ -22226,7 +22228,7 @@
|
||||
:method-count-assert 12
|
||||
:size-assert #x28
|
||||
:flag-assert #xc00000028
|
||||
;; field actor-option is likely a value type.
|
||||
;; field actor-option is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type process pickup-type float) _type_ 0)
|
||||
(fact-info-method-9 () none) ;; 9 ;; (drop-pickup (_type_ symbol process-tree fact-info int) (pointer process) 9)
|
||||
@ -22287,7 +22289,7 @@
|
||||
:method-count-assert 13
|
||||
:size-assert #x138
|
||||
:flag-assert #xd00000138
|
||||
;; field actor-option is likely a value type.
|
||||
;; field actor-option is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type process-drawable pickup-type float) _type_ 0)
|
||||
(fact-info-target-method-12 () none) ;; 12 ;; (get-gun-ammo (_type_) float 12)
|
||||
@ -22318,7 +22320,7 @@
|
||||
:method-count-assert 13
|
||||
:size-assert #x53
|
||||
:flag-assert #xd00000053
|
||||
;; field actor-option is likely a value type.
|
||||
;; field actor-option is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type process (pointer float) pickup-type float) _type_ 0)
|
||||
(fact-info-enemy-method-12 () none) ;; 12 ;; (clear-mask-bits (_type_ int) none 12)
|
||||
@ -22338,7 +22340,7 @@
|
||||
:method-count-assert 12
|
||||
:size-assert #x2c
|
||||
:flag-assert #xc0000002c
|
||||
;; field actor-option is likely a value type.
|
||||
;; field actor-option is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type process pickup-type float) _type_ 0)
|
||||
)
|
||||
@ -22443,7 +22445,7 @@
|
||||
:method-count-assert 12
|
||||
:size-assert #xa0
|
||||
:flag-assert #xc000000a0
|
||||
;; field key uses ~A with a signed load. field expr uses ~A with a signed load.
|
||||
;; field key uses ~A with a signed load. field expr uses ~A with a signed load.
|
||||
(:methods
|
||||
;; (new (symbol type object process vector) _type_ 0)
|
||||
(script-context-method-9 () none) ;; 9 ;; (eval! (_type_ pair) object 9)
|
||||
@ -22486,7 +22488,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x14
|
||||
:flag-assert #x900000014
|
||||
;; field info uses ~A with a signed load.
|
||||
;; field info uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -22735,7 +22737,7 @@
|
||||
:method-count-assert 18
|
||||
:size-assert #x84
|
||||
:flag-assert #x1200000084
|
||||
;; field on-running uses ~A with a signed load. field on-complete uses ~A with a signed load.
|
||||
;; field on-running uses ~A with a signed load. field on-complete uses ~A with a signed load.
|
||||
(:methods
|
||||
(scene-method-16 () none) ;; 16 ;; (scene-method-16 (_type_) _type_ 16)
|
||||
(scene-method-17 () none) ;; 17
|
||||
@ -22778,7 +22780,7 @@
|
||||
:method-count-assert 26
|
||||
:size-assert #x198
|
||||
:flag-assert #x1a01200198
|
||||
;; field user-data uses ~A with a 64-bit load.
|
||||
;; field user-data uses ~A with a 64-bit load.
|
||||
(:methods
|
||||
(scene-player-method-20 () none) ;; 20 ;; (wait (symbol) _type_ :state 20)
|
||||
(scene-player-method-21 () none) ;; 21 ;; (release () _type_ :state 21)
|
||||
@ -23545,7 +23547,7 @@
|
||||
:method-count-assert 23
|
||||
:size-assert #xe0
|
||||
:flag-assert #x17006000e0
|
||||
;; field open-test uses ~A with a signed load.
|
||||
;; field open-test uses ~A with a signed load.
|
||||
(:methods
|
||||
(precur-door-a-method-22 () none) ;; 22
|
||||
)
|
||||
@ -24069,7 +24071,7 @@
|
||||
:method-count-assert 16
|
||||
:size-assert #xac
|
||||
:flag-assert #x10000000ac
|
||||
;; field track-mode is likely a value type.
|
||||
;; field track-mode is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type joint-mod-mode process-drawable int) _type_ 0)
|
||||
(joint-mod-method-9 () none) ;; 9 ;; (mode-set! (_type_ joint-mod-mode) none 9)
|
||||
@ -24875,7 +24877,7 @@
|
||||
:method-count-assert 55
|
||||
:size-assert #xc8
|
||||
:flag-assert #x37000000c8
|
||||
;; field penetrate is likely a value type. field penetrate is likely a value type.
|
||||
;; field penetrate is likely a value type. field penetrate is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type process-drawable collide-list-enum) _type_ 0)
|
||||
(collide-shape-method-28 () none) ;; 28 ;; (move-by-vector! (_type_ vector) none 28)
|
||||
@ -24938,7 +24940,7 @@
|
||||
:method-count-assert 68
|
||||
:size-assert #x1dc
|
||||
:flag-assert #x44000001dc
|
||||
;; field penetrate is likely a value type. field penetrate is likely a value type.
|
||||
;; field penetrate is likely a value type. field penetrate is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type process-drawable collide-list-enum) _type_ 0)
|
||||
(collide-shape-moving-method-55 () none) ;; 55 ;; (find-ground (_type_ collide-query collide-spec float float float) symbol 55)
|
||||
@ -25026,7 +25028,7 @@
|
||||
:method-count-assert 18
|
||||
:size-assert #x15c
|
||||
:flag-assert #x1200e0015c
|
||||
;; field userdata uses ~A with a 64-bit load.
|
||||
;; field userdata uses ~A with a 64-bit load.
|
||||
(:methods
|
||||
(part-tracker-method-14 () none) ;; 14 ;; (active () _type_ :state 14)
|
||||
(part-tracker-method-15 () none) ;; 15 ;; (notify-parent-of-death (_type_) none 15)
|
||||
@ -25048,7 +25050,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x20
|
||||
:flag-assert #x900000020
|
||||
;; field userdata uses ~A with a 64-bit load. field mat-joint uses ~A with a signed load.
|
||||
;; field userdata uses ~A with a 64-bit load. field mat-joint uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -25065,7 +25067,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x24
|
||||
:flag-assert #x900000024
|
||||
;; field userdata uses ~A with a 64-bit load. field mat-joint uses ~A with a signed load.
|
||||
;; field userdata uses ~A with a 64-bit load. field mat-joint uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -25101,7 +25103,7 @@
|
||||
:method-count-assert 17
|
||||
:size-assert #x130
|
||||
:flag-assert #x1100b00130
|
||||
;; field userdata uses ~A with a 64-bit load.
|
||||
;; field userdata uses ~A with a 64-bit load.
|
||||
(:methods
|
||||
(lightning-tracker-method-14 () none) ;; 14 ;; (active () _type_ :state 14)
|
||||
(lightning-tracker-method-15 () none) ;; 15 ;; (notify-parent-of-death (_type_) none 15)
|
||||
@ -29521,7 +29523,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x10
|
||||
:flag-assert #x900000010
|
||||
;; field object uses ~A with a signed load.
|
||||
;; field object uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -29934,7 +29936,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x8c
|
||||
:flag-assert #x90000008c
|
||||
;; field user-object uses ~A with a signed load. field key uses ~A with a signed load.
|
||||
;; field user-object uses ~A with a signed load. field key uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -32403,7 +32405,7 @@
|
||||
:method-count-assert 56
|
||||
:size-assert #x120
|
||||
:flag-assert #x3800a00120
|
||||
;; field rigid-body-object-flag is likely a value type.
|
||||
;; field rigid-body-object-flag is likely a value type.
|
||||
(:methods
|
||||
(rigid-body-object-method-28 () none) ;; 28 ;; (active () _type_ :state 28)
|
||||
(rigid-body-object-method-29 () none) ;; 29 ;; (rigid-body-object-method-29 (_type_ float) none 29)
|
||||
@ -32815,7 +32817,7 @@
|
||||
:method-count-assert 30
|
||||
:size-assert #x1b4
|
||||
:flag-assert #x1e014001b4
|
||||
;; field open-test uses ~A with a signed load.
|
||||
;; field open-test uses ~A with a signed load.
|
||||
(:state-methods
|
||||
open ;; 20
|
||||
)
|
||||
@ -34707,7 +34709,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x14
|
||||
:flag-assert #x900000014
|
||||
;; field param2 uses ~A with a signed load.
|
||||
;; field param2 uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -35025,7 +35027,7 @@
|
||||
:method-count-assert 207
|
||||
:size-assert #x2e0
|
||||
:flag-assert #xcf026002e0
|
||||
;; field mantis-flag is likely a value type.
|
||||
;; field mantis-flag is likely a value type.
|
||||
(:methods
|
||||
(mantis-method-195 () none) ;; 195
|
||||
(mantis-method-199 () none) ;; 199
|
||||
@ -36189,7 +36191,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x130
|
||||
:flag-assert #x900000130
|
||||
;; field ra uses ~A with a signed load. field dummy0 uses ~A with a signed load. field dummy1 uses ~A with a signed load. field b-spfic uses ~A with a signed load. field l-spfic uses ~A with a signed load.
|
||||
;; field ra uses ~A with a signed load. field dummy0 uses ~A with a signed load. field dummy1 uses ~A with a signed load. field b-spfic uses ~A with a signed load. field l-spfic uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -37780,7 +37782,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x44
|
||||
:flag-assert #x900000044
|
||||
;; field squad-target-flag is likely a value type.
|
||||
;; field squad-target-flag is likely a value type.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -37803,7 +37805,7 @@
|
||||
:method-count-assert 10
|
||||
:size-assert #x120
|
||||
:flag-assert #xa00000120
|
||||
;; field squad-alert-flag is likely a value type.
|
||||
;; field squad-alert-flag is likely a value type.
|
||||
(:methods
|
||||
(squad-alert-state-method-9 () none) ;; 9
|
||||
)
|
||||
@ -39263,7 +39265,7 @@
|
||||
:method-count-assert 38
|
||||
:size-assert #xac
|
||||
:flag-assert #x26000000ac
|
||||
;; field cloth-flag is likely a value type.
|
||||
;; field cloth-flag is likely a value type.
|
||||
(:methods
|
||||
(cloth-system-method-16 () none) ;; 16
|
||||
(cloth-system-method-17 () none) ;; 17
|
||||
@ -39301,7 +39303,7 @@
|
||||
:method-count-assert 38
|
||||
:size-assert #x110
|
||||
:flag-assert #x2600000110
|
||||
;; field cloth-flag is likely a value type.
|
||||
;; field cloth-flag is likely a value type.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -40731,7 +40733,7 @@
|
||||
:method-count-assert 17
|
||||
:size-assert #xb0
|
||||
:flag-assert #x11000000b0
|
||||
;; field carry-mode is likely a value type.
|
||||
;; field carry-mode is likely a value type.
|
||||
(:methods
|
||||
;; (new (symbol type process-drawable int vector vector float) _type_ 0)
|
||||
(carry-info-method-9 () none) ;; 9 ;; (carry-info-method-9 (_type_) none 9)
|
||||
@ -41211,7 +41213,7 @@
|
||||
:method-count-assert 23
|
||||
:size-assert #xe4
|
||||
:flag-assert #x17007000e4
|
||||
;; field open-test uses ~A with a signed load.
|
||||
;; field open-test uses ~A with a signed load.
|
||||
(:methods
|
||||
(precura-door-a-method-22 () none) ;; 22
|
||||
)
|
||||
@ -43432,7 +43434,7 @@
|
||||
:method-count-assert 26
|
||||
:size-assert #x100
|
||||
:flag-assert #x1a00800100
|
||||
;; field on-notice uses ~A with a signed load. field on-activate uses ~A with a signed load. field on-close uses ~A with a signed load.
|
||||
;; field on-notice uses ~A with a signed load. field on-activate uses ~A with a signed load. field on-close uses ~A with a signed load.
|
||||
(:methods
|
||||
(warp-gate-method-23 () none) ;; 23 ;; (init-skel-and-collide (_type_) none 23)
|
||||
(warp-gate-method-24 () none) ;; 24 ;; (setup-fields (_type_) none 24)
|
||||
@ -44792,7 +44794,7 @@
|
||||
:method-count-assert 15
|
||||
:size-assert #xa0
|
||||
:flag-assert #xf002000a0
|
||||
;; field activate-script uses ~A with a signed load. field enter-script uses ~A with a signed load. field exit-script uses ~A with a signed load.
|
||||
;; field activate-script uses ~A with a signed load. field enter-script uses ~A with a signed load. field exit-script uses ~A with a signed load.
|
||||
(:state-methods
|
||||
active ;; 14
|
||||
)
|
||||
@ -45605,7 +45607,7 @@
|
||||
:method-count-assert 30
|
||||
:size-assert #x1b0
|
||||
:flag-assert #x1e013001b0
|
||||
;; field level-name uses ~A with a signed load. field open-test uses ~A with a signed load. field on-running uses ~A with a signed load.
|
||||
;; field level-name uses ~A with a signed load. field open-test uses ~A with a signed load. field on-running uses ~A with a signed load.
|
||||
(:methods
|
||||
(com-airlock-method-22 () none) ;; 22 ;; (init-airlock! (_type_) _type_ 22)
|
||||
(com-airlock-method-23 () none) ;; 23 ;; (want-cross-airlock? (_type_) symbol 23)
|
||||
@ -46875,7 +46877,7 @@
|
||||
:method-count-assert 53
|
||||
:size-assert #x110
|
||||
:flag-assert #x3500900110
|
||||
;; field on-notice uses ~A with a signed load. field on-hostile uses ~A with a signed load. field on-beaten uses ~A with a signed load.
|
||||
;; field on-notice uses ~A with a signed load. field on-hostile uses ~A with a signed load. field on-beaten uses ~A with a signed load.
|
||||
(:methods
|
||||
(battle-method-21 () none) ;; 21 ;; (battle-state-21 () _type_ :state 21)
|
||||
(battle-method-25 () none) ;; 25 ;; (spawner-blocked? (_type_ battle-spawner) symbol 25)
|
||||
@ -47086,7 +47088,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x1c
|
||||
:flag-assert #x90000001c
|
||||
;; field net-path-node-status is likely a value type.
|
||||
;; field net-path-node-status is likely a value type.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -49010,7 +49012,7 @@
|
||||
:method-count-assert 59
|
||||
:size-assert #x224
|
||||
:flag-assert #x3b01b00224
|
||||
;; field pause-proc uses ~A with a signed load.
|
||||
;; field pause-proc uses ~A with a signed load.
|
||||
(:methods
|
||||
(target-turret-method-34 () none) ;; 34
|
||||
(target-turret-method-35 () none) ;; 35
|
||||
@ -49372,7 +49374,7 @@
|
||||
:method-count-assert 22
|
||||
:size-assert #x20
|
||||
:flag-assert #x1600000020
|
||||
;; field nav-node-flag is likely a value type.
|
||||
;; field nav-node-flag is likely a value type.
|
||||
(:methods
|
||||
(nav-node-method-9 () none) ;; 9 ;; (debug-draw (_type_) none 9)
|
||||
(nav-node-method-10 () none) ;; 10 ;; (debug-print (_type_ symbol string) none 10)
|
||||
@ -49942,7 +49944,7 @@
|
||||
:method-count-assert 11
|
||||
:size-assert #x20
|
||||
:flag-assert #xb00000020
|
||||
;; field vis-cell-flag is likely a value type. field vis-cell-flag is likely a value type.
|
||||
;; field vis-cell-flag is likely a value type. field vis-cell-flag is likely a value type.
|
||||
(:methods
|
||||
(vis-cell-method-9 () none) ;; 9 ;; (reset-segment-counts (_type_) none 9)
|
||||
(vis-cell-method-10 () none) ;; 10 ;; (debug-draw (_type_) none 10)
|
||||
@ -51284,7 +51286,7 @@
|
||||
:method-count-assert 18
|
||||
:size-assert #x60
|
||||
:flag-assert #x1200000060
|
||||
;; field turret-flag is likely a value type.
|
||||
;; field turret-flag is likely a value type.
|
||||
(:methods
|
||||
(turret-control-method-9 () none) ;; 9 ;; (turret-control-method-9 (_type_ vehicle vector vector) none 9)
|
||||
(turret-control-method-10 () none) ;; 10 ;; (turret-control-method-10 (_type_ vehicle) none 10)
|
||||
@ -51369,7 +51371,7 @@
|
||||
:method-count-assert 68
|
||||
:size-assert #x1dc
|
||||
:flag-assert #x44000001dc
|
||||
;; field penetrate is likely a value type. field penetrate is likely a value type.
|
||||
;; field penetrate is likely a value type. field penetrate is likely a value type.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -55094,7 +55096,7 @@
|
||||
:method-count-assert 152
|
||||
:size-assert #x2b8
|
||||
:flag-assert #x98024002b8
|
||||
;; field vehicle-flag is likely a value type.
|
||||
;; field vehicle-flag is likely a value type.
|
||||
(:methods
|
||||
(vehicle-method-56 () none) ;; 56 ;; (vehicle-state-56 () _type_ :state 56)
|
||||
(vehicle-method-57 () none) ;; 57 ;; (player-control () _type_ :state 57)
|
||||
@ -55855,7 +55857,7 @@
|
||||
:method-count-assert 31
|
||||
:size-assert #x200
|
||||
:flag-assert #x1f01800200
|
||||
;; field on-start uses ~A with a signed load. field on-stop uses ~A with a signed load. field on-shutdown uses ~A with a signed load. field on-trigger uses ~A with a signed load.
|
||||
;; field on-start uses ~A with a signed load. field on-stop uses ~A with a signed load. field on-shutdown uses ~A with a signed load. field on-trigger uses ~A with a signed load.
|
||||
(:methods
|
||||
(elec-gate-method-24 () none) ;; 24 ;; (elec-gate-method-24 (_type_) none 24)
|
||||
(elec-gate-method-25 () none) ;; 25 ;; (set-palette! (_type_) none 25)
|
||||
@ -57117,7 +57119,7 @@
|
||||
:method-count-assert 10
|
||||
:size-assert #xaa
|
||||
:flag-assert #xa000000aa
|
||||
;; field borrow uses ~A with a signed load.
|
||||
;; field borrow uses ~A with a signed load.
|
||||
(:methods
|
||||
(race-info-method-9 () none) ;; 9 ;; (initialize-mesh (_type_) none 9)
|
||||
)
|
||||
@ -58462,7 +58464,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x2c
|
||||
:flag-assert #x90000002c
|
||||
;; field vehicle-wheel-surface-flag is likely a value type.
|
||||
;; field vehicle-wheel-surface-flag is likely a value type.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -59361,7 +59363,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x38
|
||||
:flag-assert #x900000038
|
||||
;; field default-check-too-far uses ~A with a signed load.
|
||||
;; field default-check-too-far uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
@ -66180,7 +66182,7 @@
|
||||
:method-count-assert 9
|
||||
:size-assert #x18
|
||||
:flag-assert #x900000018
|
||||
;; field param uses ~A with a signed load.
|
||||
;; field param uses ~A with a signed load.
|
||||
)
|
||||
|#
|
||||
|
||||
|
@ -126,6 +126,7 @@
|
||||
// will be less picky about types related to pairs.
|
||||
"pair_functions_by_name": [
|
||||
"ref",
|
||||
"ref&",
|
||||
"(method 4 pair)",
|
||||
"last",
|
||||
"member",
|
||||
|
@ -1 +1,37 @@
|
||||
{}
|
||||
{
|
||||
"(method 2 array)": [
|
||||
[23, "gp", "(array int32)"],
|
||||
[43, "gp", "(array uint32)"],
|
||||
[63, "gp", "(array int64)"],
|
||||
[83, "gp", "(array uint64)"],
|
||||
[102, "gp", "(array int8)"],
|
||||
[121, "gp", "(array uint8)"],
|
||||
[141, "gp", "(array int16)"],
|
||||
[161, "gp", "(array uint16)"],
|
||||
[186, "gp", "(array uint128)"],
|
||||
[204, "gp", "(array int32)"],
|
||||
[223, "gp", "(array float)"],
|
||||
[232, "gp", "(array float)"],
|
||||
[249, "gp", "(array basic)"],
|
||||
[258, "gp", "(array basic)"]
|
||||
],
|
||||
"(method 3 array)": [
|
||||
[51, "gp", "(array int32)"],
|
||||
[69, "gp", "(array uint32)"],
|
||||
[87, "gp", "(array int64)"],
|
||||
[105, "gp", "(array uint64)"],
|
||||
[122, "gp", "(array int8)"],
|
||||
[139, "gp", "(array int8)"],
|
||||
[157, "gp", "(array int16)"],
|
||||
[175, "gp", "(array uint16)"],
|
||||
[198, "gp", "(array uint128)"],
|
||||
[214, "gp", "(array int32)"],
|
||||
[233, "gp", "(array float)"],
|
||||
[250, "gp", "(array basic)"]
|
||||
],
|
||||
"(method 0 cpu-thread)": [[[0, 35], "v0", "cpu-thread"]],
|
||||
"(method 0 process)": [
|
||||
// [11, "a0", "int"],
|
||||
// [[12, 45], "v0", "process"]
|
||||
]
|
||||
}
|
||||
|
@ -13,7 +13,9 @@ namespace decompiler {
|
||||
bool allowable_base_type_for_symbol_to_string(const TypeSpec& ts);
|
||||
|
||||
constexpr PerGameVersion<int> SYMBOL_TO_STRING_MEM_OFFSET_DECOMP = {
|
||||
8167 * 8, jak2::SYM_TO_STRING_OFFSET, jak3::SYM_TO_STRING_OFFSET};
|
||||
8167 * 8, jak2::SYM_TO_STRING_OFFSET,
|
||||
-99999, // not supported this way!
|
||||
};
|
||||
|
||||
constexpr PerGameVersion<int> OFFSET_OF_NEXT_STATE_STORE = {72, 64, 68};
|
||||
} // namespace decompiler
|
||||
|
@ -121,6 +121,19 @@ set(RUNTIME_SOURCE
|
||||
kernel/jak2/kprint.cpp
|
||||
kernel/jak2/kscheme.cpp
|
||||
kernel/jak2/ksound.cpp
|
||||
kernel/jak3/fileio.cpp
|
||||
kernel/jak3/kboot.cpp
|
||||
kernel/jak3/kdgo.cpp
|
||||
kernel/jak3/kdsnetm.cpp
|
||||
kernel/jak3/klink.cpp
|
||||
kernel/jak3/klisten.cpp
|
||||
kernel/jak3/kmachine.cpp
|
||||
kernel/jak3/kmalloc.cpp
|
||||
kernel/jak3/kmemcard.cpp
|
||||
kernel/jak3/kprint.cpp
|
||||
kernel/jak3/kscheme.cpp
|
||||
kernel/jak3/ksocket.cpp
|
||||
kernel/jak3/ksound.cpp
|
||||
mips2c/jak1_functions/bones.cpp
|
||||
mips2c/jak1_functions/collide_cache.cpp
|
||||
mips2c/jak1_functions/collide_edge_grab.cpp
|
||||
@ -234,8 +247,6 @@ set(RUNTIME_SOURCE
|
||||
system/IOP_Kernel.cpp
|
||||
system/iop_thread.cpp
|
||||
system/SystemThread.cpp
|
||||
system/vm/dmac.cpp
|
||||
system/vm/vm.cpp
|
||||
tools/filter_menu/filter_menu.cpp
|
||||
tools/subtitle_editor/subtitle_editor_db.cpp
|
||||
tools/subtitle_editor/subtitle_editor_repl_client.cpp
|
||||
|
@ -8,8 +8,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
// TODO: jak 3 stub
|
||||
constexpr PerGameVersion<int> DGO_RPC_ID(0xdeb4, 0xfab3, 0x0);
|
||||
constexpr PerGameVersion<int> DGO_RPC_ID(0xdeb4, 0xfab3, 0xfab3);
|
||||
constexpr int DGO_RPC_CHANNEL = 3;
|
||||
constexpr int DGO_RPC_LOAD_FNO = 0;
|
||||
constexpr int DGO_RPC_LOAD_NEXT_FNO = 1;
|
||||
@ -28,3 +27,17 @@ struct RPC_Dgo_Cmd {
|
||||
uint32_t buffer_heap_top;
|
||||
char name[16];
|
||||
};
|
||||
|
||||
namespace jak3 {
|
||||
struct RPC_Dgo_Cmd {
|
||||
uint16_t rsvd;
|
||||
uint16_t result;
|
||||
uint32_t buffer1;
|
||||
uint32_t buffer2;
|
||||
uint32_t buffer_heap_top;
|
||||
char name[16];
|
||||
uint16_t cgo_id;
|
||||
uint8_t pad[30];
|
||||
};
|
||||
static_assert(sizeof(RPC_Dgo_Cmd) == 0x40);
|
||||
} // namespace jak3
|
@ -13,11 +13,11 @@ enum class Language {
|
||||
Japanese = 5,
|
||||
UK_English = 6,
|
||||
// uk english?
|
||||
Portuguese = 9
|
||||
};
|
||||
|
||||
struct GameLaunchOptions {
|
||||
GameVersion game_version = GameVersion::Jak1;
|
||||
bool disable_display = false;
|
||||
bool disable_debug_vm = true;
|
||||
int server_port = DECI2_PORT;
|
||||
};
|
||||
|
@ -7,6 +7,5 @@
|
||||
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
// TODO: jak 3 stub
|
||||
constexpr PerGameVersion<int> LOADER_RPC_ID(0xdeb2, 0xfab1, 0x0);
|
||||
constexpr PerGameVersion<int> LOADER_RPC_ID(0xdeb2, 0xfab1, 0xfab1);
|
||||
constexpr int LOADER_RPC_CHANNEL = 1;
|
||||
|
@ -7,6 +7,5 @@
|
||||
*/
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
// TODO: jak 3 stub
|
||||
constexpr PerGameVersion<int> PLAY_RPC_ID(0xdeb6, 0xfab5, 0x0);
|
||||
constexpr PerGameVersion<int> PLAY_RPC_ID(0xdeb6, 0xfab5, 0xfab5);
|
||||
constexpr int PLAY_RPC_CHANNEL = 5;
|
||||
|
@ -7,6 +7,5 @@
|
||||
*/
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
// TODO: jak 3 stub
|
||||
constexpr PerGameVersion<int> PLAYER_RPC_ID(0xdeb1, 0xfab0, 0x0);
|
||||
constexpr PerGameVersion<int> PLAYER_RPC_ID(0xdeb1, 0xfab0, 0xfab0);
|
||||
constexpr int PLAYER_RPC_CHANNEL = 0;
|
||||
|
@ -8,8 +8,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
// TODO: jak 3 stub
|
||||
constexpr PerGameVersion<int> RAMDISK_RPC_ID(0xdeb3, 0xfab2, 0x0);
|
||||
constexpr PerGameVersion<int> RAMDISK_RPC_ID(0xdeb3, 0xfab2, 0xfab2);
|
||||
constexpr int RAMDISK_RPC_CHANNEL = 2;
|
||||
constexpr int RAMDISK_GET_DATA_FNO = 0;
|
||||
constexpr int RAMDISK_RESET_AND_LOAD_FNO = 1;
|
||||
|
@ -5,8 +5,7 @@
|
||||
|
||||
#include "game/common/overlord_common.h"
|
||||
|
||||
// TODO: jak 3 stub
|
||||
constexpr PerGameVersion<int> STR_RPC_ID(0xdeb5, 0xfab4, 0x0);
|
||||
constexpr PerGameVersion<int> STR_RPC_ID(0xdeb5, 0xfab4, 0xfab4);
|
||||
constexpr int STR_RPC_CHANNEL = 4;
|
||||
|
||||
/*
|
||||
|
@ -54,6 +54,7 @@ struct Ptr {
|
||||
s32 operator-(Ptr<T> x) { return offset - x.offset; }
|
||||
Ptr operator-(s32 diff) { return Ptr(offset - diff); }
|
||||
bool operator==(const Ptr<T>& x) { return offset == x.offset; }
|
||||
bool operator!=(const Ptr<T>& x) { return offset != x.offset; }
|
||||
|
||||
/*!
|
||||
* Convert to a C pointer.
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
|
||||
/*!
|
||||
* This type is bit strange. The Ptr<Symbol4> should have the same value as the original game's
|
||||
* symbol address. The actual value in here is useless - the address of this thing is what matters.
|
||||
*/
|
||||
template <typename T>
|
||||
struct Symbol4 {
|
||||
u8 foo;
|
||||
T& value() { return *reinterpret_cast<T*>(&foo - 1); }
|
||||
const T& value() const { return *reinterpret_cast<T*>(&foo - 1); }
|
||||
const char* name_cstr() const {
|
||||
return (const char*)(g_ee_main_mem + 4 +
|
||||
*reinterpret_cast<const u32*>(&foo + jak2::SYM_TO_STRING_OFFSET));
|
||||
}
|
||||
};
|
||||
|
@ -333,6 +333,7 @@ s32 FileSave(char* name, u8* data, s32 size) {
|
||||
}
|
||||
|
||||
if (size != 0) {
|
||||
// in jak 3, this became a loop over smaller writes for some reason.
|
||||
s32 written = sceWrite(fd, data, size);
|
||||
if (written != size) {
|
||||
MsgErr("dkernel: can't write full file '%s'\n", name);
|
||||
|
@ -53,6 +53,7 @@ enum GoalFileType {
|
||||
VAG_FILE_TYPE = 0x3e,
|
||||
MISC_FILE_TYPE = 0x3f, // jak2 only
|
||||
MAP_FILE_TYPE = 0x40, // jak2 only
|
||||
CL_FILE_TYPE = 0x41, // jak 3 cloth animation
|
||||
REFPLANT_FILE_TYPE = 0x301,
|
||||
// added this, allows access directly to out/iso from fileio.
|
||||
ISO_FILE_TYPE = 0x302
|
||||
|
@ -18,15 +18,11 @@ ee::sceSifClientData cd[6]; //! client data for each IOP Remove Procedure Call.
|
||||
u32 sShowStallMsg; //! setting to show a "stalled on iop" message
|
||||
u16 x[8]; //! stupid temporary for storing a message
|
||||
u32 sMsgNum; //! Toggle for double buffered message sending.
|
||||
RPC_Dgo_Cmd* sLastMsg; //! Last DGO command sent to IOP
|
||||
RPC_Dgo_Cmd sMsg[2]; //! DGO message buffers
|
||||
|
||||
void kdgo_init_globals() {
|
||||
memset(x, 0, sizeof(x));
|
||||
memset(cd, 0, sizeof(cd));
|
||||
sShowStallMsg = 1;
|
||||
sLastMsg = nullptr;
|
||||
memset(sMsg, 0, sizeof(sMsg));
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -159,79 +155,6 @@ void StopIOP() {
|
||||
printf("DMA shut down\n");
|
||||
}
|
||||
|
||||
/*!
|
||||
* Send message to IOP to start loading a new DGO file
|
||||
* Uses a double-buffered message buffer
|
||||
* @param name: the name of the DGO file
|
||||
* @param buffer1 : one of the two file loading buffers
|
||||
* @param buffer2 : the other of the two file loading buffers
|
||||
* @param currentHeap : the current heap (for loading directly into the heap).
|
||||
*
|
||||
* DONE,
|
||||
* MODIFIED : Added print statement to indicate when DGO load starts.
|
||||
*/
|
||||
void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8> currentHeap) {
|
||||
u8 msgID = sMsgNum;
|
||||
RPC_Dgo_Cmd* mess = sMsg + sMsgNum;
|
||||
sMsgNum = sMsgNum ^ 1; // toggle message buffer.
|
||||
RpcSync(DGO_RPC_CHANNEL); // make sure old RPC is finished
|
||||
|
||||
// put a dummy value here just to make sure the IOP overwrites it.
|
||||
sMsg[msgID].result = DGO_RPC_RESULT_INIT; // !! this is 666
|
||||
|
||||
// inform IOP of buffers
|
||||
sMsg[msgID].buffer1 = buffer1.offset;
|
||||
sMsg[msgID].buffer2 = buffer2.offset;
|
||||
|
||||
// also give a heap pointer so it can load the last object file directly into the heap to save the
|
||||
// precious time.
|
||||
sMsg[msgID].buffer_heap_top = currentHeap.offset;
|
||||
|
||||
// file name
|
||||
strcpy(sMsg[msgID].name, name);
|
||||
lg::debug("[Begin Loading DGO RPC] {}, 0x{:x}, 0x{:x}, 0x{:x}", name, buffer1.offset,
|
||||
buffer2.offset, currentHeap.offset);
|
||||
// this RPC will return once we have loaded the first object file.
|
||||
// but we call async, so we don't block here.
|
||||
RpcCall(DGO_RPC_CHANNEL, DGO_RPC_LOAD_FNO, true, mess, sizeof(RPC_Dgo_Cmd), mess,
|
||||
sizeof(RPC_Dgo_Cmd));
|
||||
sLastMsg = mess;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the next object in the DGO. Will block until something is loaded.
|
||||
* @param lastObjectFlag: will get set to 1 if this is the last object.
|
||||
*
|
||||
* DONE,
|
||||
* MODIFIED : added exception if the sLastMessage isn't set (game just returns null as buffer)
|
||||
*/
|
||||
Ptr<u8> GetNextDGO(u32* lastObjectFlag) {
|
||||
*lastObjectFlag = 1;
|
||||
// Wait for RPC function to respond. This will happen once the first object file is loaded.
|
||||
RpcSync(DGO_RPC_CHANNEL);
|
||||
Ptr<u8> buffer(0);
|
||||
if (sLastMsg) {
|
||||
// if we got a good result, get pointer to object
|
||||
if ((sLastMsg->result == DGO_RPC_RESULT_MORE) || (sLastMsg->result == DGO_RPC_RESULT_DONE)) {
|
||||
buffer.offset =
|
||||
sLastMsg->buffer1; // buffer 1 always contains location of most recently loaded object.
|
||||
}
|
||||
|
||||
// not the last one, so don't set the flag.
|
||||
if (sLastMsg->result == DGO_RPC_RESULT_MORE) {
|
||||
*lastObjectFlag = 0;
|
||||
}
|
||||
|
||||
// no pending message.
|
||||
sLastMsg = nullptr;
|
||||
} else {
|
||||
// I don't see how this case can happen unless there's a bug. The game does check for this and
|
||||
// nothing in this case. (maybe from GOAL this can happen?)
|
||||
printf("last message not set!\n");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Load the TEST.DGO file.
|
||||
* Presumably used for debugging DGO loads.
|
||||
@ -242,6 +165,8 @@ Ptr<u8> GetNextDGO(u32* lastObjectFlag) {
|
||||
* UNUSED
|
||||
*/
|
||||
void LoadDGOTest() {
|
||||
ASSERT_NOT_REACHED(); // no longer supported.
|
||||
/*
|
||||
u32 lastObject = 0;
|
||||
|
||||
// backup show stall message and set it to false
|
||||
@ -273,4 +198,5 @@ void LoadDGOTest() {
|
||||
}
|
||||
|
||||
sShowStallMsg = lastShowStall;
|
||||
*/
|
||||
}
|
@ -13,8 +13,6 @@ s32 RpcCall(s32 rpcChannel,
|
||||
s32 sendSize,
|
||||
void* recvBuff,
|
||||
s32 recvSize);
|
||||
void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8> currentHeap);
|
||||
Ptr<u8> GetNextDGO(u32* lastObjectFlag);
|
||||
u64 RpcCall_wrapper(void* _args);
|
||||
u32 RpcBusy(s32 channel);
|
||||
void RpcSync(s32 channel);
|
||||
@ -24,5 +22,3 @@ u32 InitRPC();
|
||||
void StopIOP();
|
||||
|
||||
extern u32 sShowStallMsg;
|
||||
extern RPC_Dgo_Cmd sMsg[2];
|
||||
extern RPC_Dgo_Cmd* sLastMsg;
|
@ -33,11 +33,11 @@ void klink_init_globals() {
|
||||
* Initialize the link control.
|
||||
* TODO: this hasn't been carefully checked for jak 2 differences.
|
||||
*/
|
||||
void link_control::begin(Ptr<uint8_t> object_file,
|
||||
const char* name,
|
||||
int32_t size,
|
||||
Ptr<kheapinfo> heap,
|
||||
uint32_t flags) {
|
||||
void link_control::jak1_jak2_begin(Ptr<uint8_t> object_file,
|
||||
const char* name,
|
||||
int32_t size,
|
||||
Ptr<kheapinfo> heap,
|
||||
uint32_t flags) {
|
||||
if (is_opengoal_object(object_file.c())) {
|
||||
// save data from call to begin
|
||||
m_object_data = object_file;
|
||||
|
@ -60,22 +60,41 @@ struct link_control {
|
||||
|
||||
bool m_opengoal;
|
||||
bool m_busy; // only in jak2, but doesn't hurt to set it in jak 1.
|
||||
void begin(Ptr<uint8_t> object_file,
|
||||
const char* name,
|
||||
int32_t size,
|
||||
Ptr<kheapinfo> heap,
|
||||
uint32_t flags);
|
||||
|
||||
// jak 3 new stuff
|
||||
bool m_on_global_heap = false;
|
||||
LinkHeaderV5Core* m_link_hdr = nullptr;
|
||||
bool m_moved_link_block = false;
|
||||
|
||||
void jak1_jak2_begin(Ptr<uint8_t> object_file,
|
||||
const char* name,
|
||||
int32_t size,
|
||||
Ptr<kheapinfo> heap,
|
||||
uint32_t flags);
|
||||
|
||||
void jak3_begin(Ptr<uint8_t> object_file,
|
||||
const char* name,
|
||||
int32_t size,
|
||||
Ptr<kheapinfo> heap,
|
||||
uint32_t flags);
|
||||
|
||||
// was originally "work"
|
||||
uint32_t jak1_work();
|
||||
uint32_t jak2_work();
|
||||
uint32_t jak3_work();
|
||||
|
||||
uint32_t jak1_work_v3();
|
||||
uint32_t jak1_work_v2();
|
||||
|
||||
uint32_t jak2_work_v3();
|
||||
uint32_t jak2_work_v2();
|
||||
|
||||
uint32_t jak3_work_v5();
|
||||
uint32_t jak3_work_opengoal();
|
||||
|
||||
void jak1_finish(bool jump_from_c_to_goal);
|
||||
void jak2_finish(bool jump_from_c_to_goal);
|
||||
void jak3_finish(bool jump_from_c_to_goal);
|
||||
|
||||
void reset() {
|
||||
m_object_data.offset = 0;
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include "game/sce/libpad.h"
|
||||
#include "game/sce/libscf.h"
|
||||
#include "game/sce/sif_ee.h"
|
||||
#include "game/system/vm/vm.h"
|
||||
|
||||
/*!
|
||||
* Where does OVERLORD load its data from?
|
||||
@ -973,10 +972,4 @@ void init_common_pc_port_functions(
|
||||
|
||||
// debugging tools
|
||||
make_func_symbol_func("pc-filter-debug-string?", (void*)pc_filter_debug_string);
|
||||
|
||||
// init ps2 VM
|
||||
if (VM::use) {
|
||||
make_func_symbol_func("vm-ptr", (void*)VM::get_vm_ptr);
|
||||
VM::vm_init();
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,25 @@
|
||||
// global and debug kernel heaps
|
||||
Ptr<kheapinfo> kglobalheap;
|
||||
Ptr<kheapinfo> kdebugheap;
|
||||
// if we should count the number of strings and types allocated on the global heap.
|
||||
bool kheaplogging = false;
|
||||
enum MemItemsCategory {
|
||||
STRING = 0,
|
||||
TYPE = 1,
|
||||
NUM_CATEGORIES = 2,
|
||||
};
|
||||
int MemItemsCount[NUM_CATEGORIES] = {0, 0};
|
||||
int MemItemsSize[NUM_CATEGORIES] = {0, 0};
|
||||
|
||||
void kmalloc_init_globals_common() {
|
||||
// _globalheap and _debugheap
|
||||
kglobalheap.offset = GLOBAL_HEAP_INFO_ADDR;
|
||||
kdebugheap.offset = DEBUG_HEAP_INFO_ADDR;
|
||||
kheaplogging = false;
|
||||
for (auto& x : MemItemsCount)
|
||||
x = 0;
|
||||
for (auto& x : MemItemsSize)
|
||||
x = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -75,6 +89,10 @@ Ptr<kheapinfo> kheapstatus(Ptr<kheapinfo> heap) {
|
||||
Msg(6, "\t %d bytes before stack\n", GLOBAL_HEAP_END - heap->current.offset);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_CATEGORIES; i++) {
|
||||
printf(" %d: %d %d\n", i, MemItemsCount[i], MemItemsSize[i]);
|
||||
}
|
||||
|
||||
// might not have returned heap in jak 1
|
||||
return heap;
|
||||
}
|
||||
@ -171,6 +189,17 @@ Ptr<u8> kmalloc(Ptr<kheapinfo> heap, s32 size, u32 flags, char const* name) {
|
||||
|
||||
if (flags & KMALLOC_MEMSET)
|
||||
std::memset(Ptr<u8>(memstart).c(), 0, (size_t)size);
|
||||
|
||||
// this logging was added in Jak 3, but we port it back to all games:
|
||||
if ((heap == kglobalheap) && (kheaplogging != 0)) {
|
||||
if (strcmp(name, "string") == 0) {
|
||||
MemItemsCount[STRING]++;
|
||||
MemItemsSize[STRING] += size;
|
||||
} else if (strcmp(name, "type") == 0) {
|
||||
MemItemsCount[TYPE]++;
|
||||
MemItemsSize[TYPE] += size;
|
||||
}
|
||||
}
|
||||
return Ptr<u8>(memstart);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ struct kheapinfo {
|
||||
// Kernel heaps
|
||||
extern Ptr<kheapinfo> kglobalheap;
|
||||
extern Ptr<kheapinfo> kdebugheap;
|
||||
extern bool kheaplogging;
|
||||
|
||||
// flags for kmalloc/ksmalloc
|
||||
constexpr u32 KMALLOC_TOP = 0x2000; //! Flag to allocate temporary memory from heap top
|
||||
|
@ -68,6 +68,9 @@ void init_output() {
|
||||
case GameVersion::Jak2:
|
||||
use_debug = MasterDebug && DebugSegment;
|
||||
break;
|
||||
case GameVersion::Jak3:
|
||||
use_debug = MasterDebug || DebugSegment;
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
|
@ -28,4 +28,8 @@ constexpr u32 DEBUG_HEAP_START = 0x5000000;
|
||||
|
||||
namespace jak2 {
|
||||
constexpr u32 DEBUG_HEAP_SIZE = 0x2f00000;
|
||||
}
|
||||
|
||||
namespace jak3 {
|
||||
constexpr u32 DEBUG_HEAP_SIZE = 0x2f00000;
|
||||
}
|
@ -12,6 +12,87 @@
|
||||
|
||||
namespace jak1 {
|
||||
|
||||
RPC_Dgo_Cmd* sLastMsg; //! Last DGO command sent to IOP
|
||||
RPC_Dgo_Cmd sMsg[2]; //! DGO message buffers
|
||||
|
||||
void kdgo_init_globals() {
|
||||
sLastMsg = nullptr;
|
||||
memset(sMsg, 0, sizeof(sMsg));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Send message to IOP to start loading a new DGO file
|
||||
* Uses a double-buffered message buffer
|
||||
* @param name: the name of the DGO file
|
||||
* @param buffer1 : one of the two file loading buffers
|
||||
* @param buffer2 : the other of the two file loading buffers
|
||||
* @param currentHeap : the current heap (for loading directly into the heap).
|
||||
*
|
||||
* DONE,
|
||||
* MODIFIED : Added print statement to indicate when DGO load starts.
|
||||
*/
|
||||
void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8> currentHeap) {
|
||||
u8 msgID = sMsgNum;
|
||||
RPC_Dgo_Cmd* mess = sMsg + sMsgNum;
|
||||
sMsgNum = sMsgNum ^ 1; // toggle message buffer.
|
||||
RpcSync(DGO_RPC_CHANNEL); // make sure old RPC is finished
|
||||
|
||||
// put a dummy value here just to make sure the IOP overwrites it.
|
||||
sMsg[msgID].result = DGO_RPC_RESULT_INIT; // !! this is 666
|
||||
|
||||
// inform IOP of buffers
|
||||
sMsg[msgID].buffer1 = buffer1.offset;
|
||||
sMsg[msgID].buffer2 = buffer2.offset;
|
||||
|
||||
// also give a heap pointer so it can load the last object file directly into the heap to save the
|
||||
// precious time.
|
||||
sMsg[msgID].buffer_heap_top = currentHeap.offset;
|
||||
|
||||
// file name
|
||||
strcpy(sMsg[msgID].name, name);
|
||||
lg::debug("[Begin Loading DGO RPC] {}, 0x{:x}, 0x{:x}, 0x{:x}", name, buffer1.offset,
|
||||
buffer2.offset, currentHeap.offset);
|
||||
// this RPC will return once we have loaded the first object file.
|
||||
// but we call async, so we don't block here.
|
||||
RpcCall(DGO_RPC_CHANNEL, DGO_RPC_LOAD_FNO, true, mess, sizeof(RPC_Dgo_Cmd), mess,
|
||||
sizeof(RPC_Dgo_Cmd));
|
||||
sLastMsg = mess;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the next object in the DGO. Will block until something is loaded.
|
||||
* @param lastObjectFlag: will get set to 1 if this is the last object.
|
||||
*
|
||||
* DONE,
|
||||
* MODIFIED : added exception if the sLastMessage isn't set (game just returns null as buffer)
|
||||
*/
|
||||
Ptr<u8> GetNextDGO(u32* lastObjectFlag) {
|
||||
*lastObjectFlag = 1;
|
||||
// Wait for RPC function to respond. This will happen once the first object file is loaded.
|
||||
RpcSync(DGO_RPC_CHANNEL);
|
||||
Ptr<u8> buffer(0);
|
||||
if (sLastMsg) {
|
||||
// if we got a good result, get pointer to object
|
||||
if ((sLastMsg->result == DGO_RPC_RESULT_MORE) || (sLastMsg->result == DGO_RPC_RESULT_DONE)) {
|
||||
buffer.offset =
|
||||
sLastMsg->buffer1; // buffer 1 always contains location of most recently loaded object.
|
||||
}
|
||||
|
||||
// not the last one, so don't set the flag.
|
||||
if (sLastMsg->result == DGO_RPC_RESULT_MORE) {
|
||||
*lastObjectFlag = 0;
|
||||
}
|
||||
|
||||
// no pending message.
|
||||
sLastMsg = nullptr;
|
||||
} else {
|
||||
// I don't see how this case can happen unless there's a bug. The game does check for this and
|
||||
// nothing in this case. (maybe from GOAL this can happen?)
|
||||
printf("last message not set!\n");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Instruct the IOP to continue loading the next object.
|
||||
* Only should be called once it is safe to overwrite the previous.
|
||||
|
@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/common/dgo_rpc_types.h"
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
|
||||
namespace jak1 {
|
||||
void load_and_link_dgo_from_c(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
@ -10,4 +12,7 @@ void load_and_link_dgo_from_c(const char* name,
|
||||
s32 bufferSize,
|
||||
bool jump_from_c_to_goal);
|
||||
void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size);
|
||||
void kdgo_init_globals();
|
||||
extern RPC_Dgo_Cmd sMsg[2];
|
||||
extern RPC_Dgo_Cmd* sLastMsg;
|
||||
} // namespace jak1
|
@ -582,7 +582,7 @@ Ptr<uint8_t> link_and_exec(Ptr<uint8_t> data,
|
||||
uint32_t flags,
|
||||
bool jump_from_c_to_goal) {
|
||||
link_control lc;
|
||||
lc.begin(data, name, size, heap, flags);
|
||||
lc.jak1_jak2_begin(data, name, size, heap, flags);
|
||||
uint32_t done;
|
||||
do {
|
||||
done = lc.jak1_work();
|
||||
@ -608,8 +608,8 @@ u64 link_and_exec_wrapper(u64* args) {
|
||||
*/
|
||||
uint64_t link_begin(u64* args) {
|
||||
// object data, name size, heap flags
|
||||
saved_link_control.begin(Ptr<u8>(args[0]), Ptr<char>(args[1]).c(), args[2],
|
||||
Ptr<kheapinfo>(args[3]), args[4]);
|
||||
saved_link_control.jak1_jak2_begin(Ptr<u8>(args[0]), Ptr<char>(args[1]).c(), args[2],
|
||||
Ptr<kheapinfo>(args[3]), args[4]);
|
||||
auto work_result = saved_link_control.jak1_work();
|
||||
// if we managed to finish in one shot, take care of calling finish
|
||||
if (work_result) {
|
||||
|
@ -43,7 +43,6 @@
|
||||
#include "game/sce/libgraph.h"
|
||||
#include "game/sce/sif_ee.h"
|
||||
#include "game/sce/stubs.h"
|
||||
#include "game/system/vm/vm.h"
|
||||
|
||||
using namespace ee;
|
||||
|
||||
@ -378,11 +377,6 @@ int ShutdownMachine() {
|
||||
ShutdownSound();
|
||||
ShutdownGoalProto();
|
||||
|
||||
// OpenGOAL only - kill ps2 VM
|
||||
if (VM::use) {
|
||||
VM::vm_kill();
|
||||
}
|
||||
|
||||
Msg(6, "kernel: machine shutdown\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ char* MakeFileName(int type, const char* name, int new_string) {
|
||||
// game CGO file (unused, all DGO/CGOs loaded through IOP)
|
||||
sprintf(buf, "game/dgo%d/%s.cgo", DGO_FILE_VERSION, name);
|
||||
} else if (type == CNT_FILE_TYPE) {
|
||||
// game cnt file (continue point?)
|
||||
// game cnt file. leftover from jak 1.
|
||||
sprintf(buf, "%sfinal/res%d/game-cnt.go", prefix, 1);
|
||||
} else if (type == RES_FILE_TYPE) {
|
||||
// RES go file?
|
||||
|
@ -15,6 +15,87 @@
|
||||
|
||||
namespace jak2 {
|
||||
|
||||
RPC_Dgo_Cmd* sLastMsg; //! Last DGO command sent to IOP
|
||||
RPC_Dgo_Cmd sMsg[2]; //! DGO message buffers
|
||||
|
||||
void kdgo_init_globals() {
|
||||
sLastMsg = nullptr;
|
||||
memset(sMsg, 0, sizeof(sMsg));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Send message to IOP to start loading a new DGO file
|
||||
* Uses a double-buffered message buffer
|
||||
* @param name: the name of the DGO file
|
||||
* @param buffer1 : one of the two file loading buffers
|
||||
* @param buffer2 : the other of the two file loading buffers
|
||||
* @param currentHeap : the current heap (for loading directly into the heap).
|
||||
*
|
||||
* DONE,
|
||||
* MODIFIED : Added print statement to indicate when DGO load starts.
|
||||
*/
|
||||
void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8> currentHeap) {
|
||||
u8 msgID = sMsgNum;
|
||||
RPC_Dgo_Cmd* mess = sMsg + sMsgNum;
|
||||
sMsgNum = sMsgNum ^ 1; // toggle message buffer.
|
||||
RpcSync(DGO_RPC_CHANNEL); // make sure old RPC is finished
|
||||
|
||||
// put a dummy value here just to make sure the IOP overwrites it.
|
||||
sMsg[msgID].result = DGO_RPC_RESULT_INIT; // !! this is 666
|
||||
|
||||
// inform IOP of buffers
|
||||
sMsg[msgID].buffer1 = buffer1.offset;
|
||||
sMsg[msgID].buffer2 = buffer2.offset;
|
||||
|
||||
// also give a heap pointer so it can load the last object file directly into the heap to save the
|
||||
// precious time.
|
||||
sMsg[msgID].buffer_heap_top = currentHeap.offset;
|
||||
|
||||
// file name
|
||||
strcpy(sMsg[msgID].name, name);
|
||||
lg::debug("[Begin Loading DGO RPC] {}, 0x{:x}, 0x{:x}, 0x{:x}", name, buffer1.offset,
|
||||
buffer2.offset, currentHeap.offset);
|
||||
// this RPC will return once we have loaded the first object file.
|
||||
// but we call async, so we don't block here.
|
||||
RpcCall(DGO_RPC_CHANNEL, DGO_RPC_LOAD_FNO, true, mess, sizeof(RPC_Dgo_Cmd), mess,
|
||||
sizeof(RPC_Dgo_Cmd));
|
||||
sLastMsg = mess;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the next object in the DGO. Will block until something is loaded.
|
||||
* @param lastObjectFlag: will get set to 1 if this is the last object.
|
||||
*
|
||||
* DONE,
|
||||
* MODIFIED : added exception if the sLastMessage isn't set (game just returns null as buffer)
|
||||
*/
|
||||
Ptr<u8> GetNextDGO(u32* lastObjectFlag) {
|
||||
*lastObjectFlag = 1;
|
||||
// Wait for RPC function to respond. This will happen once the first object file is loaded.
|
||||
RpcSync(DGO_RPC_CHANNEL);
|
||||
Ptr<u8> buffer(0);
|
||||
if (sLastMsg) {
|
||||
// if we got a good result, get pointer to object
|
||||
if ((sLastMsg->result == DGO_RPC_RESULT_MORE) || (sLastMsg->result == DGO_RPC_RESULT_DONE)) {
|
||||
buffer.offset =
|
||||
sLastMsg->buffer1; // buffer 1 always contains location of most recently loaded object.
|
||||
}
|
||||
|
||||
// not the last one, so don't set the flag.
|
||||
if (sLastMsg->result == DGO_RPC_RESULT_MORE) {
|
||||
*lastObjectFlag = 0;
|
||||
}
|
||||
|
||||
// no pending message.
|
||||
sLastMsg = nullptr;
|
||||
} else {
|
||||
// I don't see how this case can happen unless there's a bug. The game does check for this and
|
||||
// nothing in this case. (maybe from GOAL this can happen?)
|
||||
printf("last message not set!\n");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Instruct the IOP to continue loading the next object.
|
||||
* Only should be called once it is safe to overwrite the previous.
|
||||
|
@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/common/dgo_rpc_types.h"
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
|
||||
namespace jak2 {
|
||||
void load_and_link_dgo_from_c(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
@ -14,4 +16,8 @@ void load_and_link_dgo_from_c_fast(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
u32 linkFlag,
|
||||
s32 bufferSize);
|
||||
void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size);
|
||||
void kdgo_init_globals();
|
||||
extern RPC_Dgo_Cmd sMsg[2];
|
||||
extern RPC_Dgo_Cmd* sLastMsg;
|
||||
} // namespace jak2
|
@ -609,7 +609,7 @@ Ptr<uint8_t> link_and_exec(Ptr<uint8_t> data,
|
||||
// probably won't end well...
|
||||
}
|
||||
link_control lc;
|
||||
lc.begin(data, name, size, heap, flags);
|
||||
lc.jak1_jak2_begin(data, name, size, heap, flags);
|
||||
uint32_t done;
|
||||
do {
|
||||
done = lc.jak2_work();
|
||||
@ -635,8 +635,8 @@ u64 link_and_exec_wrapper(u64* args) {
|
||||
*/
|
||||
uint64_t link_begin(u64* args) {
|
||||
// object data, name size, heap flags
|
||||
saved_link_control.begin(Ptr<u8>(args[0]), Ptr<char>(args[1]).c(), args[2],
|
||||
Ptr<kheapinfo>(args[3]), args[4]);
|
||||
saved_link_control.jak1_jak2_begin(Ptr<u8>(args[0]), Ptr<char>(args[1]).c(), args[2],
|
||||
Ptr<kheapinfo>(args[3]), args[4]);
|
||||
auto work_result = saved_link_control.jak2_work();
|
||||
// if we managed to finish in one shot, take care of calling finish
|
||||
if (work_result) {
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include "game/sce/libgraph.h"
|
||||
#include "game/sce/sif_ee.h"
|
||||
#include "game/sce/stubs.h"
|
||||
#include "game/system/vm/vm.h"
|
||||
|
||||
using namespace ee;
|
||||
|
||||
@ -434,10 +433,6 @@ int ShutdownMachine() {
|
||||
|
||||
ShutdownGoalProto();
|
||||
|
||||
// OpenGOAL only - kill ps2 VM
|
||||
if (VM::use) {
|
||||
VM::vm_kill();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -514,10 +509,10 @@ u64 kopen(u64 fs, u64 name, u64 mode) {
|
||||
char buffer[128];
|
||||
// sprintf(buffer, "host:%s", Ptr<String>(name)->data());
|
||||
sprintf(buffer, "%s", Ptr<String>(name)->data());
|
||||
if (!strcmp(Ptr<Symbol4<u8>>(mode)->name_cstr(), "read")) {
|
||||
if (!strcmp(symbol_name_cstr(*Ptr<Symbol4<u8>>(mode)), "read")) {
|
||||
// 0x1
|
||||
file_stream->file = sceOpen(buffer, SCE_RDONLY);
|
||||
} else if (!strcmp(Ptr<Symbol4<u8>>(mode)->name_cstr(), "append")) {
|
||||
} else if (!strcmp(symbol_name_cstr(*Ptr<Symbol4<u8>>(mode)), "append")) {
|
||||
// new in jak 2!
|
||||
// 0x202
|
||||
file_stream->file = sceOpen(buffer, SCE_CREAT | SCE_WRONLY);
|
||||
|
@ -49,10 +49,15 @@ void kscheme_init_globals() {
|
||||
u64 new_illegal(u32 allocation, u32 type) {
|
||||
(void)allocation;
|
||||
MsgErr("dkernel: illegal attempt to call new method of static object type %s\n",
|
||||
Ptr<Type>(type)->symbol->name_cstr());
|
||||
symbol_name_cstr(*Ptr<Type>(type)->symbol));
|
||||
return s7.offset;
|
||||
}
|
||||
|
||||
u32 u32_in_fixed_sym(u32 offset) {
|
||||
return Ptr<Symbol4<u32>>(s7.offset + offset)->value();
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
Ptr<Ptr<String>> sym_to_string_ptr(Ptr<Symbol4<T>> in) {
|
||||
return Ptr<Ptr<String>>(in.offset + SYM_TO_STRING_OFFSET);
|
||||
@ -68,13 +73,10 @@ Ptr<u32> sym_to_hash(Ptr<Symbol4<T>> in) {
|
||||
return Ptr<u32>(in.offset + SYM_TO_HASH_OFFSET);
|
||||
}
|
||||
|
||||
u32 u32_in_fixed_sym(u32 offset) {
|
||||
return Ptr<Symbol4<u32>>(s7.offset + offset)->value();
|
||||
}
|
||||
|
||||
void fixed_sym_set(u32 offset, u32 value) {
|
||||
Ptr<Symbol4<u32>>(s7.offset + offset)->value() = value;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
u64 alloc_from_heap(u32 heap_symbol, u32 type, s32 size, u32 pp) {
|
||||
using namespace jak2_symbols;
|
||||
@ -1409,7 +1411,6 @@ u64 inspect_type(u32 obj) {
|
||||
} else {
|
||||
auto typ = Ptr<Type>(obj);
|
||||
auto sym = typ->symbol;
|
||||
;
|
||||
|
||||
cprintf("[%8x] type\n\tname: %s\n\tparent: ", obj, sym_to_string(sym)->data());
|
||||
print_object(typ->parent.offset);
|
||||
@ -1462,11 +1463,11 @@ u64 inspect_link_block(u32 ob) {
|
||||
return ob;
|
||||
}
|
||||
|
||||
namespace {
|
||||
Ptr<Symbol4<Ptr<Type>>> get_fixed_type_symbol(u32 offset) {
|
||||
return (s7 + offset).cast<Symbol4<Ptr<Type>>>();
|
||||
}
|
||||
|
||||
namespace {
|
||||
u64 pack_type_flag(u64 methods, u64 heap_base, u64 size) {
|
||||
return (methods << 32) + (heap_base << 16) + (size);
|
||||
}
|
||||
@ -1799,7 +1800,6 @@ u64 loadc(const char* /*file_name*/, kheapinfo* /*heap*/, u32 /*flags*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: copied from jak 1
|
||||
u64 loado(u32 file_name_in, u32 heap_in) {
|
||||
char loadName[272];
|
||||
Ptr<String> file_name(file_name_in);
|
||||
@ -1823,7 +1823,6 @@ u64 unload(u32 name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: copied from jak 1
|
||||
s64 load_and_link(const char* filename, char* decode_name, kheapinfo* heap, u32 flags) {
|
||||
(void)filename;
|
||||
s32 sz;
|
||||
|
@ -63,4 +63,10 @@ u64 call_goal_function_by_name(const char* name);
|
||||
u64 alloc_heap_memory(u32 heap, u32 size);
|
||||
u64 alloc_heap_object(u32 heap, u32 type, u32 size, u32 pp);
|
||||
u32 u32_in_fixed_sym(u32 offset);
|
||||
|
||||
template <typename T>
|
||||
const char* symbol_name_cstr(const Symbol4<T>& sym) {
|
||||
return (const char*)(g_ee_main_mem + 4 +
|
||||
*reinterpret_cast<const u32*>(&sym.foo + jak2::SYM_TO_STRING_OFFSET));
|
||||
}
|
||||
} // namespace jak2
|
||||
|
@ -9,7 +9,6 @@ namespace jak2 {
|
||||
* Set up some functions which are somewhat related to sound.
|
||||
*/
|
||||
void InitSoundScheme() {
|
||||
make_function_symbol_from_c("rpc-call", (void*)RpcCall_wrapper);
|
||||
make_function_symbol_from_c("rpc-busy?", (void*)RpcBusy);
|
||||
make_function_symbol_from_c("test-load-dgo-c", (void*)LoadDGOTest);
|
||||
make_stack_arg_function_symbol_from_c("rpc-call", (void*)RpcCall_wrapper);
|
||||
|
254
game/kernel/jak3/fileio.cpp
Normal file
254
game/kernel/jak3/fileio.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
#include "fileio.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "game/kernel/common/fileio.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
// This file naming system was used only in development, as it loads files from the development PC
|
||||
// connected to the PS2 dev-kit.
|
||||
// My theory is that the developers would use this to debug their level/art tools. They could use
|
||||
// these file names to quickly load in new files and see if they worked correctly with the renderer,
|
||||
// without needing to create/load entire new DGO files.
|
||||
// They've been adding to this file over all 3 games, so I believe it is more than just a leftover
|
||||
// from early jak 1.
|
||||
|
||||
/*!
|
||||
* Convert a file-name like $CODE/thing to the appropriate file path on the development computer.
|
||||
*/
|
||||
char* DecodeFileName(const char* name) {
|
||||
char* result;
|
||||
|
||||
if (name[0] == '$') {
|
||||
if (!strncmp(name, "$TEXTURE/", 9)) {
|
||||
result = MakeFileName(TX_PAGE_FILE_TYPE, name + 9, 0);
|
||||
} else if (!strncmp(name, "$ART_GROUP/", 0xb)) {
|
||||
result = MakeFileName(ART_GROUP_FILE_TYPE, name + 0xb, 0);
|
||||
} else if (!strncmp(name, "$LEVEL/", 7)) {
|
||||
int len = (int)strlen(name);
|
||||
if (name[len - 4] == '.') {
|
||||
result = MakeFileName(LEVEL_WITH_EXTENSION_FILE_TYPE, name + 7, 0);
|
||||
} else {
|
||||
// level files can omit a file type if desired
|
||||
result = MakeFileName(LEVEL_FILE_TYPE, name + 7, 0);
|
||||
}
|
||||
} else if (!strncmp(name, "$FINAL/", 6)) { // in jak2, this is FINAL instead of DATA
|
||||
result = MakeFileName(DATA_FILE_TYPE, name + 6, 0);
|
||||
} else if (!strncmp(name, "$CODE/", 6)) {
|
||||
result = MakeFileName(CODE_FILE_TYPE, name + 6, 0);
|
||||
} else if (!strncmp(name, "$RES/", 5)) {
|
||||
result = MakeFileName(RES_FILE_TYPE, name + 5, 0);
|
||||
} else if (!strncmp(name, "$MISC/", 6)) {
|
||||
result = MakeFileName(MISC_FILE_TYPE, name + 6, 0);
|
||||
} else if (!strncmp(name, "$MAP/", 5)) {
|
||||
result = MakeFileName(MAP_FILE_TYPE, name + 5, 0);
|
||||
} else if (!strncmp(name, "$ISO/", 5)) {
|
||||
result = MakeFileName(ISO_FILE_TYPE, name + 5, 0);
|
||||
} else {
|
||||
printf("[ERROR] DecodeFileName: UNKNOWN FILE NAME %s\n", name);
|
||||
result = nullptr;
|
||||
}
|
||||
} else {
|
||||
// no prefix. Treat this as a code file
|
||||
return MakeFileName(CODE_FILE_TYPE, name, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a file name that looks in the appropriate folder in ND's development environment.
|
||||
* This is a bit of dumping ground for all possible files they'd load.
|
||||
*/
|
||||
char* MakeFileName(int type, const char* name, int new_string) {
|
||||
using namespace versions::jak3;
|
||||
// start with network filesystem
|
||||
// kstrcpy(buffer_633, "host:");
|
||||
kstrcpy(buffer_633, "");
|
||||
char* buf = strend(buffer_633);
|
||||
|
||||
// prefix to build directory
|
||||
char prefix[64];
|
||||
kstrcpy(prefix, FOLDER_PREFIX);
|
||||
|
||||
switch (type) {
|
||||
// Unused files that could be used to exchange data between the dev PS2 and the GOAL compiler.
|
||||
case LISTENER_TO_KERNEL_FILE_TYPE:
|
||||
kstrcpy(buf, "kernel/LISTENERTOKERNEL");
|
||||
break;
|
||||
case KERNEL_TO_LISTENER_FILE_TYPE:
|
||||
kstrcpy(buf, "kernel/KERNELTOLISTENER");
|
||||
break;
|
||||
|
||||
// A GOAL object file containing code built from the GOAL compiler.
|
||||
case CODE_FILE_TYPE:
|
||||
sprintf(buf, "game/obj/%s.o", name);
|
||||
break;
|
||||
|
||||
// Unused, opening the gamepad as a file.
|
||||
case GAMEPAD_FILE_TYPE:
|
||||
sprintf(buffer_633, "pad:0");
|
||||
break;
|
||||
|
||||
// Locks for the unused kernel/listener interface. (funny that they added this after...)
|
||||
case LISTENER_TO_KERNEL_LOCK_FILE_TYPE:
|
||||
kstrcpy(buf, "kernel/LISTENERTOKERNEL_LOCK");
|
||||
break;
|
||||
case KERNEL_TO_LISTENER_LOCK_FILE_TYPE:
|
||||
kstrcpy(buf, "kernel/KERNELTOLISTENER_LOCK");
|
||||
break;
|
||||
|
||||
// Host0 IOP modules (stored on the linux SBC inside the dev ps2 itself!)
|
||||
case IOP_MODULE_FILE_TYPE: // 8
|
||||
sprintf(buffer_633, "host0:/usr/local/sce/iop/modules/%s.irx", name);
|
||||
break;
|
||||
|
||||
// plain GOAL data object file
|
||||
case DATA_FILE_TYPE: // 0x20
|
||||
sprintf(buf, "%sfinal/%s.go", prefix, name);
|
||||
break;
|
||||
|
||||
// texture page
|
||||
case TX_PAGE_FILE_TYPE: // 0x21
|
||||
sprintf(buf, "%sdata/texture-page%d/%s.go", prefix, TX_PAGE_VERSION, name);
|
||||
break;
|
||||
|
||||
// joint animation
|
||||
case JA_FILE_TYPE: // 0x22
|
||||
sprintf(buf, "%sdb/artdata%d/%s-ja.go", prefix, ART_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// joint geo (skeleton)
|
||||
case JG_FILE_TYPE: // 0x23
|
||||
sprintf(buf, "%sdb/artdata%d/%s-jg.go", prefix, ART_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// mesh animation (unused)
|
||||
case MA_FILE_TYPE: // 0x24
|
||||
sprintf(buf, "%sdb/artdata%d/%s-ma.go", prefix, ART_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// likely art-mesh-geo, and unused. Maybe was used before MERC?
|
||||
case MG_FILE_TYPE: // 0x25
|
||||
sprintf(buf, "%sdb/artdata%d/%s-mg.go", prefix, ART_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// text group perhaps?
|
||||
case TG_FILE_TYPE:
|
||||
sprintf(buf, "%sdb/%s-tg.go", prefix, name);
|
||||
break;
|
||||
|
||||
// level file
|
||||
case LEVEL_FILE_TYPE: // 0x27
|
||||
sprintf(buf, "%sdb/level%d/%s-bt.go", prefix, LEVEL_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// Everybody's favorite "art group" file. Container of different art.
|
||||
case ART_GROUP_FILE_TYPE: // 0x30
|
||||
sprintf(buf, "%sfinal/art-group%d/%s-ag.go", prefix, ART_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// GOAL data object file containing visibility data. This likely contained the visibility data
|
||||
// that's included in the BSP file.
|
||||
case VS_FILE_TYPE: // 0x31
|
||||
sprintf(buf, "%sfinal/level%d/%s-vs.go", prefix, LEVEL_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// GOAL data object file containing text. Likely the same format as the .TXT in final ISOs.
|
||||
case TX_FILE_TYPE: // 0x32
|
||||
sprintf(buf, "%sfinal/res%d/%s-tx.go", prefix, 1, name);
|
||||
break;
|
||||
|
||||
// Binary format visibility. Likely the format of Jak 1's .VIS files.
|
||||
case VS_BIN_FILE_TYPE: // 0x33
|
||||
sprintf(buf, "%sfinal/level%d/%s-vs.bin", prefix, LEVEL_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// DGO description files. These contain a list of files inside each DGO.
|
||||
case DGO_TXT_FILE_TYPE: // 0x34
|
||||
sprintf(buf, "%sfinal/dgo%d/%s.txt", prefix, DGO_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// Level file! but you have to provide the extension.
|
||||
case LEVEL_WITH_EXTENSION_FILE_TYPE: // 0x35
|
||||
sprintf(buf, "%sfinal/level%d/%s", prefix, LEVEL_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// DGO and CGO files. These can exist in either final/ or game/
|
||||
case DATA_DGO_FILE_TYPE: // 0x36
|
||||
sprintf(buf, "%sfinal/dgo%d/%s.dgo", prefix, DGO_FILE_VERSION, name);
|
||||
break;
|
||||
case GAME_DGO_FILE_TYPE: // 0x37
|
||||
sprintf(buf, "game/dgo%d/%s.dgo", DGO_FILE_VERSION, name);
|
||||
break;
|
||||
case DATA_CGO_FILE_TYPE: // 0x38
|
||||
sprintf(buf, "%sfinal/dgo%d/%s.cgo", prefix, DGO_FILE_VERSION, name);
|
||||
break;
|
||||
case GAME_CGO_FILE_TYPE: // 0x39
|
||||
sprintf(buf, "game/dgo%d/%s.cgo", DGO_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// Jak 1 had a weird game-cnt.gco file containing the total number of orbs/cells.
|
||||
case CNT_FILE_TYPE: // 0x3a
|
||||
sprintf(buf, "%sfinal/res%d/game-cnt.go", prefix, 1);
|
||||
break;
|
||||
|
||||
// Any res file with .go extension.
|
||||
case RES_FILE_TYPE: // 0x3b
|
||||
sprintf(buf, "%sfinal/res%d/%s.go", prefix, 1, name);
|
||||
break;
|
||||
|
||||
// sound bank (sound effects)
|
||||
case SND_BNK_FILE_TYPE: // 0x3c
|
||||
sprintf(buf, "%sfinal/sound%d/%s.bnk", prefix, 1, name); // v1
|
||||
break;
|
||||
|
||||
// music file
|
||||
case MUSIC_BNK_FILE_TYPE: // 0x3d
|
||||
sprintf(buf, "%sfinal/music%d/%s.bnk", prefix, 1, name); // v1
|
||||
break;
|
||||
|
||||
// vag file, but it probably doesn't work due to the file extension.
|
||||
case VAG_FILE_TYPE: // 0x3e
|
||||
// interestingly, jak 2 used vagwad2, but jak 3 doesn't. But the memory bug is still there.
|
||||
sprintf(buf, "%sfinal/vagwad/%s.%s", prefix, name, "<INVALID>"); // v1, memory bug here
|
||||
break;
|
||||
|
||||
// whatever you want.
|
||||
case MISC_FILE_TYPE: // 0x3f
|
||||
sprintf(buf, "%sfinal/misc/%s", prefix, name);
|
||||
break;
|
||||
|
||||
// possible minimap/bigmap data
|
||||
case MAP_FILE_TYPE:
|
||||
sprintf(buf, "%sfinal/map%d/%s", prefix, 1, name); // v1
|
||||
break;
|
||||
|
||||
// jak 3 cloth animation file.
|
||||
case CL_FILE_TYPE: // 0x41
|
||||
sprintf(buf, "%sdb/artdata%d/%s-cl.go", prefix, ART_FILE_VERSION, name);
|
||||
break;
|
||||
|
||||
// no idea
|
||||
case REFPLANT_FILE_TYPE: // 0x301
|
||||
sprintf(buf, "%sdb/refplant/%s", prefix, name);
|
||||
break;
|
||||
default:
|
||||
printf("UNKNOWN FILE TYPE %d\n", type);
|
||||
}
|
||||
|
||||
char* result;
|
||||
if (!new_string) {
|
||||
// return pointer to static filename buffer
|
||||
result = buffer_633;
|
||||
} else {
|
||||
// or create a new string on the global heap.
|
||||
int l = (int)strlen(buffer_633);
|
||||
result = (char*)kmalloc(kglobalheap, l + 1, 0, "filename").c();
|
||||
kstrcpy(result, buffer_633);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace jak3
|
6
game/kernel/jak3/fileio.h
Normal file
6
game/kernel/jak3/fileio.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {
|
||||
char* MakeFileName(int type, const char* name, int new_string);
|
||||
char* DecodeFileName(const char* name);
|
||||
} // namespace jak3
|
163
game/kernel/jak3/kboot.cpp
Normal file
163
game/kernel/jak3/kboot.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include "kboot.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/repl/util.h"
|
||||
#include "common/util/Timer.h"
|
||||
|
||||
#include "game/common/game_common_types.h"
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/Symbol4.h"
|
||||
#include "game/kernel/common/kboot.h"
|
||||
#include "game/kernel/common/klisten.h"
|
||||
#include "game/kernel/common/kprint.h"
|
||||
#include "game/kernel/common/kscheme.h"
|
||||
#include "game/kernel/common/ksocket.h"
|
||||
#include "game/kernel/jak3/klisten.h"
|
||||
#include "game/kernel/jak3/kmachine.h"
|
||||
#include "game/sce/libscf.h"
|
||||
|
||||
// KernelDispatch__3ndiPFv_x
|
||||
// KernelCheckAndDispatch__3ndiv
|
||||
// KernelShutdown__3ndii
|
||||
// main
|
||||
|
||||
namespace jak3 {
|
||||
void KernelCheckAndDispatch();
|
||||
|
||||
char DebugBootUser[64];
|
||||
char DebugBootArtGroup[64];
|
||||
|
||||
void kboot_init_globals() {
|
||||
memset(DebugBootUser, 0, sizeof(DebugBootUser));
|
||||
memset(DebugBootArtGroup, 0, sizeof(DebugBootArtGroup));
|
||||
// strcpy(DebugBootUser, "unknown");
|
||||
// CHANGED : let's just try to find the username automatically by default!
|
||||
// the default is still "unknown"
|
||||
auto username = REPL::find_repl_username();
|
||||
strcpy(DebugBootUser, username.c_str());
|
||||
}
|
||||
|
||||
s32 goal_main(int argc, const char* const* argv) {
|
||||
// only in PC port
|
||||
InitParms(argc, argv);
|
||||
|
||||
masterConfig.aspect = ee::sceScfGetAspect();
|
||||
auto sony_language = ee::sceScfGetLanguage();
|
||||
masterConfig.inactive_timeout = 0;
|
||||
masterConfig.volume = 100;
|
||||
masterConfig.timeout = 0;
|
||||
switch (sony_language) {
|
||||
case SCE_JAPANESE_LANGUAGE:
|
||||
masterConfig.language = 6; // ???
|
||||
break;
|
||||
case SCE_FRENCH_LANGUAGE: // 2 -> 1
|
||||
masterConfig.language = (u16)Language::French;
|
||||
break;
|
||||
case SCE_SPANISH_LANGUAGE: // 3 -> 3
|
||||
masterConfig.language = (u16)Language::Spanish;
|
||||
break;
|
||||
case SCE_GERMAN_LANGUAGE: // 4 -> 2
|
||||
masterConfig.language = (u16)Language::German;
|
||||
break;
|
||||
case SCE_ITALIAN_LANGUAGE: // 5 -> 4
|
||||
masterConfig.language = (u16)Language::Italian;
|
||||
break;
|
||||
case SCE_PORTUGUESE_LANGUAGE:
|
||||
masterConfig.language = (u16)Language::Portuguese;
|
||||
break;
|
||||
default:
|
||||
masterConfig.language = (u16)Language::English;
|
||||
break;
|
||||
}
|
||||
// Set up aspect ratio override in demo
|
||||
if (!strcmp(DebugBootMessage, "demo") || !strcmp(DebugBootMessage, "demo-shared")) {
|
||||
masterConfig.aspect = SCE_ASPECT_FULL;
|
||||
}
|
||||
// removed in PC port
|
||||
// DiskBoot = 1;
|
||||
// MasterDebug = 0;
|
||||
// DebugSegment = 0;
|
||||
|
||||
// Launch GOAL!
|
||||
if (InitMachine() >= 0) { // init kernel
|
||||
KernelCheckAndDispatch(); // run kernel
|
||||
ShutdownMachine(); // kernel died, we should too.
|
||||
// movie playback stuff removed.
|
||||
} else {
|
||||
fprintf(stderr, "InitMachine failed\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KernelDispatch(u32 dispatcher_func) {
|
||||
// place our stack at the end of EE memory
|
||||
u64 goal_stack = u64(g_ee_main_mem) + EE_MAIN_MEM_SIZE - 8;
|
||||
|
||||
// try to get a message from the listener, and process it if needed
|
||||
Ptr<char> new_message = WaitForMessageAndAck();
|
||||
if (new_message.offset) {
|
||||
ProcessListenerMessage(new_message);
|
||||
}
|
||||
|
||||
// remember the old listener
|
||||
auto old_listener_function = ListenerFunction->value();
|
||||
|
||||
// run the kernel!
|
||||
Timer dispatch_timer;
|
||||
if (MasterUseKernel) {
|
||||
call_goal_on_stack(Ptr<Function>(dispatcher_func), goal_stack, s7.offset, g_ee_main_mem);
|
||||
} else {
|
||||
// added, just calls the listener function
|
||||
if (ListenerFunction->value() != s7.offset) {
|
||||
auto result = call_goal_on_stack(Ptr<Function>(ListenerFunction->value()), goal_stack,
|
||||
s7.offset, g_ee_main_mem);
|
||||
#ifdef __linux__
|
||||
cprintf("%ld\n", result);
|
||||
#else
|
||||
cprintf("%lld\n", result);
|
||||
#endif
|
||||
ListenerFunction->value() = s7.offset;
|
||||
}
|
||||
}
|
||||
|
||||
float time_ms = dispatch_timer.getMs();
|
||||
if (time_ms > 50) {
|
||||
printf("Kernel dispatch time: %.3f ms\n", time_ms);
|
||||
}
|
||||
|
||||
// flush stdout
|
||||
ClearPending();
|
||||
|
||||
// now run the extra "kernel function"
|
||||
auto bonus_function = KernelFunction->value();
|
||||
if (bonus_function != s7.offset) {
|
||||
// clear the pending kernel function
|
||||
KernelFunction->value() = s7.offset;
|
||||
// and run
|
||||
call_goal_on_stack(Ptr<Function>(bonus_function), goal_stack, s7.offset, g_ee_main_mem);
|
||||
}
|
||||
|
||||
// send ack to indicate that the listener function has been processed and the result printed
|
||||
if (MasterDebug && ListenerFunction->value() != old_listener_function) {
|
||||
SendAck();
|
||||
}
|
||||
|
||||
// prevent crazy spinning if we're not vsyncing (added)
|
||||
if (time_ms < 4) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||
}
|
||||
}
|
||||
|
||||
void KernelShutdown(u32 reason) {
|
||||
MasterExit = (RuntimeExitStatus)reason;
|
||||
}
|
||||
|
||||
void KernelCheckAndDispatch() {
|
||||
while (MasterExit == RuntimeExitStatus::RUNNING) {
|
||||
KernelDispatch(kernel_dispatcher->value());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
11
game/kernel/jak3/kboot.h
Normal file
11
game/kernel/jak3/kboot.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
extern char DebugBootUser[64];
|
||||
extern char DebugBootArtGroup[64];
|
||||
void kboot_init_globals();
|
||||
void KernelShutdown(u32 reason);
|
||||
s32 goal_main(int argc, const char* const* argv);
|
||||
} // namespace jak3
|
210
game/kernel/jak3/kdgo.cpp
Normal file
210
game/kernel/jak3/kdgo.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
#include "kdgo.h"
|
||||
|
||||
#include "common/global_profiler/GlobalProfiler.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Timer.h"
|
||||
|
||||
#include "game/kernel/common/fileio.h"
|
||||
#include "game/kernel/common/kdgo.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
#include "game/kernel/jak3/klink.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
jak3::RPC_Dgo_Cmd* sLastMsg; //! Last DGO command sent to IOP
|
||||
jak3::RPC_Dgo_Cmd sMsg[2]; //! DGO message buffers
|
||||
uint16_t cgo_id = 10;
|
||||
|
||||
void kdgo_init_globals() {
|
||||
sLastMsg = nullptr;
|
||||
memset(sMsg, 0, sizeof(sMsg));
|
||||
cgo_id = 10;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Send message to IOP to start loading a new DGO file
|
||||
* Uses a double-buffered message buffer
|
||||
* @param name: the name of the DGO file
|
||||
* @param buffer1 : one of the two file loading buffers
|
||||
* @param buffer2 : the other of the two file loading buffers
|
||||
* @param currentHeap : the current heap (for loading directly into the heap).
|
||||
*
|
||||
* DONE,
|
||||
* MODIFIED : Added print statement to indicate when DGO load starts.
|
||||
*/
|
||||
void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8> currentHeap) {
|
||||
u8 msgID = sMsgNum;
|
||||
RPC_Dgo_Cmd* mess = sMsg + sMsgNum;
|
||||
sMsgNum = sMsgNum ^ 1; // toggle message buffer.
|
||||
RpcSync(DGO_RPC_CHANNEL); // make sure old RPC is finished
|
||||
|
||||
// put a dummy value here just to make sure the IOP overwrites it.
|
||||
sMsg[msgID].result = DGO_RPC_RESULT_INIT; // !! this is 666
|
||||
|
||||
// inform IOP of buffers
|
||||
sMsg[msgID].buffer1 = buffer1.offset;
|
||||
sMsg[msgID].buffer2 = buffer2.offset;
|
||||
|
||||
// also give a heap pointer so it can load the last object file directly into the heap to save the
|
||||
// precious time.
|
||||
sMsg[msgID].buffer_heap_top = currentHeap.offset;
|
||||
|
||||
// new for Jak 3: a unique ID.
|
||||
sMsg[msgID].cgo_id = cgo_id;
|
||||
cgo_id++;
|
||||
|
||||
// file name
|
||||
strcpy(sMsg[msgID].name, name);
|
||||
lg::debug("[Begin Loading DGO RPC] {}, 0x{:x}, 0x{:x}, 0x{:x}", name, buffer1.offset,
|
||||
buffer2.offset, currentHeap.offset);
|
||||
// this RPC will return once we have loaded the first object file.
|
||||
// but we call async, so we don't block here.
|
||||
RpcCall(DGO_RPC_CHANNEL, DGO_RPC_LOAD_FNO, true, mess, sizeof(RPC_Dgo_Cmd), mess,
|
||||
sizeof(RPC_Dgo_Cmd));
|
||||
sLastMsg = mess;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the next object in the DGO. Will block until something is loaded.
|
||||
* @param lastObjectFlag: will get set to 1 if this is the last object.
|
||||
*
|
||||
* DONE,
|
||||
* MODIFIED : added exception if the sLastMessage isn't set (game just returns null as buffer)
|
||||
*/
|
||||
Ptr<u8> GetNextDGO(u32* lastObjectFlag) {
|
||||
*lastObjectFlag = 1;
|
||||
// Wait for RPC function to respond. This will happen once the first object file is loaded.
|
||||
RpcSync(DGO_RPC_CHANNEL);
|
||||
Ptr<u8> buffer(0);
|
||||
if (sLastMsg) {
|
||||
// if we got a good result, get pointer to object
|
||||
if ((sLastMsg->result == DGO_RPC_RESULT_MORE) || (sLastMsg->result == DGO_RPC_RESULT_DONE)) {
|
||||
buffer.offset =
|
||||
sLastMsg->buffer1; // buffer 1 always contains location of most recently loaded object.
|
||||
}
|
||||
|
||||
// not the last one, so don't set the flag.
|
||||
if (sLastMsg->result == DGO_RPC_RESULT_MORE) {
|
||||
*lastObjectFlag = 0;
|
||||
}
|
||||
|
||||
// no pending message.
|
||||
sLastMsg = nullptr;
|
||||
} else {
|
||||
// I don't see how this case can happen unless there's a bug. The game does check for this and
|
||||
// nothing in this case. (maybe from GOAL this can happen?)
|
||||
printf("last message not set!\n");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Instruct the IOP to continue loading the next object.
|
||||
* Only should be called once it is safe to overwrite the previous.
|
||||
* @param heapPtr : pointer to heap so the IOP could try to load directly into a heap if it wants.
|
||||
* This should be updated after each object file load to make sure the IOP knows the exact location
|
||||
* of the end of the GOAL heap data.
|
||||
*
|
||||
* Unlike jak 1, we update buffer1 and buffer2 here for borrow heap loads.
|
||||
*/
|
||||
void ContinueLoadingDGO(Ptr<u8> b1, Ptr<u8> b2, Ptr<u8> heapPtr) {
|
||||
u32 msgID = sMsgNum;
|
||||
jak3::RPC_Dgo_Cmd* sendBuff = sMsg + sMsgNum;
|
||||
sMsgNum = sMsgNum ^ 1;
|
||||
sendBuff->result = DGO_RPC_RESULT_INIT;
|
||||
sMsg[msgID].buffer1 = b1.offset;
|
||||
sMsg[msgID].buffer2 = b2.offset;
|
||||
sMsg[msgID].buffer_heap_top = heapPtr.offset;
|
||||
// the IOP will wait for this RpcCall to continue the DGO state machine.
|
||||
RpcCall(DGO_RPC_CHANNEL, DGO_RPC_LOAD_NEXT_FNO, true, sendBuff, sizeof(jak3::RPC_Dgo_Cmd),
|
||||
sendBuff, sizeof(jak3::RPC_Dgo_Cmd));
|
||||
// this async RPC call will complete when the next object is fully loaded.
|
||||
sLastMsg = sendBuff;
|
||||
}
|
||||
/*!
|
||||
* Load and link a DGO file.
|
||||
* This does not use the mutli-threaded linker and will block until the entire file is done.
|
||||
*/
|
||||
void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size) {
|
||||
auto name = Ptr<char>(name_gstr + 4).c();
|
||||
auto heap = Ptr<kheapinfo>(heap_info);
|
||||
load_and_link_dgo_from_c(name, heap, flag, buffer_size, false);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Load and link a DGO file.
|
||||
* This does not use the mutli-threaded linker and will block until the entire file is done.e
|
||||
*/
|
||||
void load_and_link_dgo_from_c(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
u32 linkFlag,
|
||||
s32 bufferSize,
|
||||
bool jump_from_c_to_goal) {
|
||||
Timer timer;
|
||||
lg::debug("[Load and Link DGO From C] {}", name);
|
||||
u32 oldShowStall = sShowStallMsg;
|
||||
|
||||
// remember where the heap top point is so we can clear temporary allocations
|
||||
auto oldHeapTop = heap->top;
|
||||
|
||||
// allocate temporary buffers from top of the given heap
|
||||
// align 64 for IOP DMA
|
||||
// note: both buffers named dgo-buffer-2
|
||||
auto buffer2 = kmalloc(heap, bufferSize, KMALLOC_TOP | KMALLOC_ALIGN_64, "dgo-buffer-2");
|
||||
auto buffer1 = kmalloc(heap, bufferSize, KMALLOC_TOP | KMALLOC_ALIGN_64, "dgo-buffer-2");
|
||||
|
||||
// build filename. If no extension is given, default to CGO.
|
||||
char fileName[16];
|
||||
kstrcpyup(fileName, name);
|
||||
if (fileName[strlen(fileName) - 4] != '.') {
|
||||
strcat(fileName, ".CGO");
|
||||
}
|
||||
|
||||
// no stall messages, as this is a blocking load and when spending 100% CPU time on linking,
|
||||
// the linker can beat the DVD drive.
|
||||
sShowStallMsg = 0;
|
||||
|
||||
// start load on IOP.
|
||||
BeginLoadingDGO(
|
||||
fileName, buffer1, buffer2,
|
||||
Ptr<u8>((heap->current + 0x3f).offset & 0xffffffc0)); // 64-byte aligned for IOP DMA
|
||||
|
||||
u32 lastObjectLoaded = 0;
|
||||
while (!lastObjectLoaded) {
|
||||
// check to see if next object is loaded (I believe it always is?)
|
||||
auto dgoObj = GetNextDGO(&lastObjectLoaded);
|
||||
if (!dgoObj.offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we're on the last object, it is loaded at cheap->current. So we can safely reset the two
|
||||
// dgo-buffer allocations. We do this _before_ we link! This way, the last file loaded has more
|
||||
// heap available, which is important when we need to use the entire memory.
|
||||
if (lastObjectLoaded) {
|
||||
heap->top = oldHeapTop;
|
||||
}
|
||||
|
||||
// determine the size and name of the object we got
|
||||
auto obj = dgoObj + 0x40; // seek past dgo object header
|
||||
u32 objSize = *(dgoObj.cast<u32>()); // size from object's link block
|
||||
|
||||
char objName[64];
|
||||
strcpy(objName, (dgoObj + 4).cast<char>().c()); // name from dgo object header
|
||||
lg::debug("[link and exec] {:18s} {} {:6d} heap-use {:8d} {:8d}: 0x{:x}", objName,
|
||||
lastObjectLoaded, objSize, kheapused(kglobalheap),
|
||||
kdebugheap.offset ? kheapused(kdebugheap) : 0, kglobalheap->current.offset);
|
||||
{
|
||||
auto p = scoped_prof(fmt::format("link-{}", objName).c_str());
|
||||
link_and_exec(obj, objName, objSize, heap, linkFlag, jump_from_c_to_goal); // link now!
|
||||
}
|
||||
|
||||
// inform IOP we are done
|
||||
if (!lastObjectLoaded) {
|
||||
ContinueLoadingDGO(buffer1, buffer2, Ptr<u8>((heap->current + 0x3f).offset & 0xffffffc0));
|
||||
}
|
||||
}
|
||||
lg::info("load_and_link_dgo_from_c took {:.3f} s\n", timer.getSeconds());
|
||||
sShowStallMsg = oldShowStall;
|
||||
}
|
||||
|
||||
} // namespace jak3
|
20
game/kernel/jak3/kdgo.h
Normal file
20
game/kernel/jak3/kdgo.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
|
||||
namespace jak3 {
|
||||
void load_and_link_dgo_from_c(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
u32 linkFlag,
|
||||
s32 bufferSize,
|
||||
bool jump_from_c_to_goal);
|
||||
void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size);
|
||||
void load_and_link_dgo_from_c_fast(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
u32 linkFlag,
|
||||
s32 bufferSize);
|
||||
void kdgo_init_globals();
|
||||
} // namespace jak3
|
3
game/kernel/jak3/kdsnetm.cpp
Normal file
3
game/kernel/jak3/kdsnetm.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
#include "kdsnetm.h"
|
3
game/kernel/jak3/kdsnetm.h
Normal file
3
game/kernel/jak3/kdsnetm.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {}
|
656
game/kernel/jak3/klink.cpp
Normal file
656
game/kernel/jak3/klink.cpp
Normal file
@ -0,0 +1,656 @@
|
||||
#include "klink.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/goal_constants.h"
|
||||
#include "common/symbols.h"
|
||||
|
||||
#include "game/kernel/common/fileio.h"
|
||||
#include "game/kernel/common/klink.h"
|
||||
#include "game/kernel/common/kprint.h"
|
||||
#include "game/kernel/jak3/kmalloc.h"
|
||||
#include "game/kernel/jak3/kscheme.h"
|
||||
#include "game/mips2c/mips2c_table.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace {
|
||||
bool is_opengoal_object(void* data) {
|
||||
u32 first_word;
|
||||
memcpy(&first_word, data, 4);
|
||||
return first_word != 0 && first_word != UINT32_MAX;
|
||||
}
|
||||
constexpr bool link_debug_printfs = false;
|
||||
} // namespace
|
||||
|
||||
void link_control::jak3_begin(Ptr<uint8_t> object_file,
|
||||
const char* name,
|
||||
int32_t size,
|
||||
Ptr<kheapinfo> heap,
|
||||
uint32_t flags) {
|
||||
if (is_opengoal_object(object_file.c())) {
|
||||
m_opengoal = true;
|
||||
// save data from call to begin
|
||||
m_object_data = object_file;
|
||||
kstrcpy(m_object_name, name);
|
||||
m_object_size = size;
|
||||
m_heap = heap;
|
||||
m_flags = flags;
|
||||
|
||||
// initialize link control
|
||||
m_entry.offset = 0;
|
||||
m_heap_top = m_heap->top;
|
||||
m_keep_debug = false;
|
||||
m_opengoal = true;
|
||||
m_busy = true;
|
||||
|
||||
if (link_debug_printfs) {
|
||||
char* goal_name = object_file.cast<char>().c();
|
||||
printf("link %s\n", m_object_name);
|
||||
printf("link_control::begin %c%c%c%c\n", goal_name[0], goal_name[1], goal_name[2],
|
||||
goal_name[3]);
|
||||
}
|
||||
|
||||
// points to the beginning of the linking data
|
||||
m_link_block_ptr = object_file + BASIC_OFFSET;
|
||||
m_code_size = 0;
|
||||
m_code_start = object_file;
|
||||
m_state = 0;
|
||||
m_segment_process = 0;
|
||||
|
||||
ObjectFileHeader* ofh = m_link_block_ptr.cast<ObjectFileHeader>().c();
|
||||
if (ofh->goal_version_major != versions::GOAL_VERSION_MAJOR) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"VERSION ERROR: C Kernel built from GOAL %d.%d, but object file %s is from GOAL %d.%d\n",
|
||||
versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR, name, ofh->goal_version_major,
|
||||
ofh->goal_version_minor);
|
||||
ASSERT(false);
|
||||
}
|
||||
if (link_debug_printfs) {
|
||||
printf("Object file header:\n");
|
||||
printf(" GOAL ver %d.%d obj %d len %d\n", ofh->goal_version_major, ofh->goal_version_minor,
|
||||
ofh->object_file_version, ofh->link_block_length);
|
||||
printf(" segment count %d\n", ofh->segment_count);
|
||||
for (int i = 0; i < N_SEG; i++) {
|
||||
printf(" seg %d link 0x%04x, 0x%04x data 0x%04x, 0x%04x\n", i, ofh->link_infos[i].offset,
|
||||
ofh->link_infos[i].size, ofh->code_infos[i].offset, ofh->code_infos[i].size);
|
||||
}
|
||||
}
|
||||
|
||||
m_version = ofh->object_file_version;
|
||||
if (ofh->object_file_version < 4) {
|
||||
// three segment file
|
||||
|
||||
// seek past the header
|
||||
m_object_data.offset += ofh->link_block_length;
|
||||
// todo, set m_code_size
|
||||
|
||||
if (m_link_block_ptr.offset < m_heap->base.offset ||
|
||||
m_link_block_ptr.offset >= m_heap->top.offset) {
|
||||
// the link block is outside our heap, or in the top of our heap. It's somebody else's
|
||||
// problem.
|
||||
if (link_debug_printfs) {
|
||||
printf("Link block somebody else's problem\n");
|
||||
}
|
||||
|
||||
if (m_heap->base.offset <= m_object_data.offset && // above heap base
|
||||
m_object_data.offset < m_heap->top.offset && // less than heap top (not needed?)
|
||||
m_object_data.offset < m_heap->current.offset) { // less than heap current
|
||||
if (link_debug_printfs) {
|
||||
printf("Code block in the heap, kicking it out for copy into heap\n");
|
||||
}
|
||||
m_heap->current = m_object_data;
|
||||
}
|
||||
} else {
|
||||
// in our heap, we need to move it so we can free up its space later on
|
||||
if (link_debug_printfs) {
|
||||
printf("Link block needs to be moved!\n");
|
||||
}
|
||||
|
||||
// allocate space for a new one
|
||||
auto new_link_block = kmalloc(m_heap, ofh->link_block_length, KMALLOC_TOP, "link-block");
|
||||
auto old_link_block = m_link_block_ptr - BASIC_OFFSET;
|
||||
|
||||
// copy it (was ultimate memcpy, but just use normal one to make it easier)
|
||||
memmove(new_link_block.c(), old_link_block.c(), ofh->link_block_length);
|
||||
m_link_block_ptr = new_link_block + BASIC_OFFSET;
|
||||
|
||||
// if we can save some memory here
|
||||
if (old_link_block.offset < m_heap->current.offset) {
|
||||
if (link_debug_printfs) {
|
||||
printf("Kick out old link block\n");
|
||||
}
|
||||
m_heap->current = old_link_block;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG(false, "UNHANDLED OBJECT FILE VERSION");
|
||||
}
|
||||
|
||||
if ((m_flags & LINK_FLAG_FORCE_DEBUG) && MasterDebug && !DiskBoot) {
|
||||
m_keep_debug = true;
|
||||
}
|
||||
} else {
|
||||
m_opengoal = false;
|
||||
if (heap == kglobalheap) {
|
||||
jak3::kmemopen_from_c(heap, name);
|
||||
m_on_global_heap = true;
|
||||
} else {
|
||||
m_on_global_heap = false;
|
||||
}
|
||||
m_object_data = object_file;
|
||||
kstrcpy(this->m_object_name, name);
|
||||
m_object_size = size;
|
||||
// l_hdr = (LinkHdrWithType*)this->m_object_data;
|
||||
LinkHeaderV5* l_hdr = (LinkHeaderV5*)m_object_data.c();
|
||||
m_flags = flags;
|
||||
u16 version = l_hdr->core.version;
|
||||
ASSERT(version == 5); // I think, since there's only a work v5.
|
||||
m_heap_top = heap->top;
|
||||
// this->unk_init1 = 1; TODO
|
||||
m_busy = true;
|
||||
m_heap = heap;
|
||||
// this->m_unk_init0_0 = 0; TODO
|
||||
m_keep_debug = false;
|
||||
m_link_hdr = &l_hdr->core; // m_hdr_ptr
|
||||
m_code_size = 0;
|
||||
// this->m_ptr_2 = l_hdr; just used for cache flush, so skip it! not really the right thing??
|
||||
// this->m_unk_init0_3 = 0; TODO
|
||||
// this->m_unk_init0_4 = 0; TODO
|
||||
// this->m_unk_init0_5 = 0; TODO
|
||||
if (version == 4) {
|
||||
ASSERT_NOT_REACHED();
|
||||
} else {
|
||||
m_object_data.offset = object_file.offset + l_hdr->core.length_to_get_to_code;
|
||||
if (version == 5) {
|
||||
static_assert(0x50 == sizeof(LinkHeaderV5));
|
||||
size = (size - l_hdr->core.link_length) - sizeof(LinkHeaderV5);
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
m_code_size = size;
|
||||
if ((u8*)m_link_hdr < m_heap->base.c() || (u8*)m_link_hdr >= m_heap->top.c()) {
|
||||
// the link block is outside our heap, or in the allocated top part.
|
||||
// so we ignore it, and leave it as somebody else's problem.
|
||||
|
||||
// let's try to move the code part:
|
||||
if (m_heap->base.offset <= m_object_data.offset && // above heap base
|
||||
m_object_data.offset < m_heap->top.offset && // less than heap top (not needed?)
|
||||
m_object_data.offset < m_heap->current.offset) { // less than heap current
|
||||
if (link_debug_printfs) {
|
||||
printf("Code block in the heap, kicking it out for copy into heap\n");
|
||||
}
|
||||
m_heap->current = m_object_data;
|
||||
}
|
||||
} else {
|
||||
m_moved_link_block = true;
|
||||
if (m_link_hdr->version == 5) {
|
||||
// the link block is inside our heap, but we'd like to avoid this.
|
||||
// we'll copy the link block, and the header to the temporary part of our heap:
|
||||
|
||||
// where we loaded the link data:
|
||||
auto offset_to_link_data = m_link_hdr->length_to_get_to_link;
|
||||
|
||||
// allocate memory for link data, and header
|
||||
auto new_link_block_mem = kmalloc(m_heap, m_link_hdr->link_length + sizeof(LinkHeaderV5),
|
||||
KMALLOC_TOP, "link-block");
|
||||
|
||||
// we'll place the header and link block back to back in the newly alloated block,
|
||||
// so patch up the offset for this new layout before copying
|
||||
m_link_hdr->length_to_get_to_link = sizeof(LinkHeaderV5);
|
||||
|
||||
// move header!
|
||||
memmove(new_link_block_mem.c(), object_file.c(), sizeof(LinkHeaderV5));
|
||||
|
||||
// move link data!
|
||||
auto old_link_block = object_file.c() + offset_to_link_data;
|
||||
memmove(new_link_block_mem.c() + sizeof(LinkHeaderV5), old_link_block,
|
||||
m_link_hdr->link_length);
|
||||
|
||||
// update our pointer to the link header core.
|
||||
m_link_hdr = &((LinkHeaderV5*)new_link_block_mem.c())->core;
|
||||
|
||||
// scary: update the heap to kick out all the link data (and likely the actual data too).
|
||||
// we'll be relying on the linking process to copy the data as needed.l
|
||||
if (old_link_block < m_heap->current.c()) {
|
||||
if (link_debug_printfs) {
|
||||
printf("Kick out old link block\n");
|
||||
}
|
||||
m_heap->current.offset = old_link_block - g_ee_main_mem;
|
||||
}
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((m_flags & LINK_FLAG_FORCE_DEBUG) && MasterDebug && !DiskBoot) {
|
||||
m_keep_debug = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t link_control::jak3_work() {
|
||||
auto old_debug_segment = DebugSegment;
|
||||
if (m_keep_debug) {
|
||||
DebugSegment = s7.offset + true_symbol_offset(g_game_version);
|
||||
}
|
||||
|
||||
// set type tag of link block
|
||||
|
||||
uint32_t rv;
|
||||
|
||||
if (m_version == 3) {
|
||||
ASSERT(m_opengoal);
|
||||
*((m_link_block_ptr - 4).cast<u32>()) =
|
||||
*((s7 + jak3_symbols::FIX_SYM_LINK_BLOCK - 1).cast<u32>());
|
||||
rv = jak3_work_opengoal();
|
||||
} else if (m_version == 5) {
|
||||
ASSERT(!m_opengoal);
|
||||
*(u32*)(((u8*)m_link_hdr) - 4) = *((s7 + jak3_symbols::FIX_SYM_LINK_BLOCK - 1).cast<u32>());
|
||||
rv = jak3_work_v5();
|
||||
} else {
|
||||
ASSERT_MSG(false, fmt::format("UNHANDLED OBJECT FILE VERSION {} IN WORK!", m_version));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DebugSegment = old_debug_segment;
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint32_t link_control::jak3_work_v5() {
|
||||
ASSERT_NOT_REACHED(); // save this for another day...
|
||||
// TODO: there are some missing vars in begin. I just commented them out for now.
|
||||
}
|
||||
|
||||
namespace {
|
||||
/*!
|
||||
* Link a single relative offset (used for RIP)
|
||||
*/
|
||||
uint32_t cross_seg_dist_link_v3(Ptr<uint8_t> link,
|
||||
ObjectFileHeader* ofh,
|
||||
int current_seg,
|
||||
int size) {
|
||||
// target seg, dist into mine, dist into target, patch loc in mine
|
||||
uint8_t target_seg = *link;
|
||||
ASSERT(target_seg < ofh->segment_count);
|
||||
|
||||
uint32_t* link_data = (link + 1).cast<uint32_t>().c();
|
||||
int32_t mine = link_data[0] + ofh->code_infos[current_seg].offset;
|
||||
int32_t tgt = link_data[1] + ofh->code_infos[target_seg].offset;
|
||||
int32_t diff = tgt - mine;
|
||||
uint32_t offset_of_patch = link_data[2] + ofh->code_infos[current_seg].offset;
|
||||
|
||||
// second debug segment case added for jak 2.
|
||||
if (!ofh->code_infos[target_seg].offset || (!DebugSegment && target_seg == DEBUG_SEGMENT)) {
|
||||
// we want to address GOAL 0. In the case where this is a rip-relative load or store, this
|
||||
// will crash, which is what we want. If it's an lea and just getting an address, this will get
|
||||
// us a nullptr. If you do a method-set! with a null pointer it does nothing, so it's safe to
|
||||
// method-set! to things that are in unloaded segments and it'll just keep the old method.
|
||||
diff = -mine;
|
||||
}
|
||||
// printf("link object in seg %d diff %d at %d (%d + %d)\n", target_seg, diff, offset_of_patch,
|
||||
// link_data[2], ofh->code_infos[current_seg].offset);
|
||||
|
||||
// both 32-bit and 64-bit pointer links are supported, though 64-bit ones are no longer in use.
|
||||
// we still support it just in case we want to run ancient code.
|
||||
if (size == 4) {
|
||||
*Ptr<int32_t>(offset_of_patch).c() = diff;
|
||||
} else if (size == 8) {
|
||||
*Ptr<int64_t>(offset_of_patch).c() = diff;
|
||||
} else {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
return 1 + 3 * 4;
|
||||
}
|
||||
|
||||
uint32_t ptr_link_v3(Ptr<u8> link, ObjectFileHeader* ofh, int current_seg) {
|
||||
auto* link_data = link.cast<u32>().c();
|
||||
u32 patch_loc = link_data[0] + ofh->code_infos[current_seg].offset;
|
||||
u32 patch_value = link_data[1] + ofh->code_infos[current_seg].offset;
|
||||
*Ptr<u32>(patch_loc).c() = patch_value;
|
||||
return 8;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Link type pointers for a single type in "v3 equivalent" link data
|
||||
* Returns a pointer to the link table data after the typelinking data.
|
||||
*/
|
||||
uint32_t typelink_v3(Ptr<uint8_t> link, Ptr<uint8_t> data) {
|
||||
// get the name of the type
|
||||
uint32_t seek = 0;
|
||||
char sym_name[256];
|
||||
while (link.c()[seek]) {
|
||||
sym_name[seek] = link.c()[seek];
|
||||
seek++;
|
||||
ASSERT(seek < 256);
|
||||
}
|
||||
sym_name[seek] = 0;
|
||||
seek++;
|
||||
|
||||
// determine the number of methods
|
||||
uint32_t method_count = link.c()[seek++];
|
||||
// jak2 special
|
||||
method_count *= 4;
|
||||
if (method_count) {
|
||||
method_count += 3;
|
||||
}
|
||||
|
||||
// intern the GOAL type, creating the vtable if it doesn't exist.
|
||||
auto type_ptr = jak3::intern_type_from_c(-1, 0, sym_name, method_count);
|
||||
|
||||
// prepare to read the locations of the type pointers
|
||||
Ptr<uint32_t> offsets = link.cast<uint32_t>() + seek;
|
||||
uint32_t offset_count = *offsets;
|
||||
offsets = offsets + 4;
|
||||
seek += 4;
|
||||
|
||||
// write the type pointers into memory
|
||||
for (uint32_t i = 0; i < offset_count; i++) {
|
||||
*(data + offsets.c()[i]).cast<int32_t>() = type_ptr.offset;
|
||||
seek += 4;
|
||||
}
|
||||
|
||||
return seek;
|
||||
}
|
||||
/*!
|
||||
* Link symbols (both offsets and pointers) in "v3 equivalent" link data.
|
||||
* Returns a pointer to the link table data after the linking data for this symbol.
|
||||
*/
|
||||
uint32_t symlink_v3(Ptr<uint8_t> link, Ptr<uint8_t> data) {
|
||||
// get the symbol name
|
||||
uint32_t seek = 0;
|
||||
char sym_name[256];
|
||||
while (link.c()[seek]) {
|
||||
sym_name[seek] = link.c()[seek];
|
||||
seek++;
|
||||
ASSERT(seek < 256);
|
||||
}
|
||||
sym_name[seek] = 0;
|
||||
seek++;
|
||||
|
||||
// intern
|
||||
auto sym = jak3::intern_from_c(-1, 0, sym_name);
|
||||
int32_t sym_offset = sym.cast<u32>() - s7;
|
||||
uint32_t sym_addr = sym.cast<u32>().offset;
|
||||
|
||||
// prepare to read locations of symbol links
|
||||
Ptr<uint32_t> offsets = link.cast<uint32_t>() + seek;
|
||||
uint32_t offset_count = *offsets;
|
||||
offsets = offsets + 4;
|
||||
seek += 4;
|
||||
|
||||
for (uint32_t i = 0; i < offset_count; i++) {
|
||||
uint32_t offset = offsets.c()[i];
|
||||
seek += 4;
|
||||
auto data_ptr = (data + offset).cast<int32_t>();
|
||||
|
||||
if (*data_ptr == -1) {
|
||||
// a "-1" indicates that we should store the address.
|
||||
*(data + offset).cast<int32_t>() = sym_addr;
|
||||
} else if (*(data_ptr.cast<u32>()) == LINK_SYM_NO_OFFSET_FLAG) {
|
||||
*(data + offset).cast<int32_t>() = sym_offset - 1;
|
||||
} else {
|
||||
// otherwise store the offset to st.
|
||||
*(data + offset).cast<int32_t>() = sym_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return seek;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
uint32_t link_control::jak3_work_opengoal() {
|
||||
// note: I'm assuming that the allocation we used in jak2/jak1 will still work here. Once work_v5
|
||||
// is done, we could revisit this.
|
||||
ObjectFileHeader* ofh = m_link_block_ptr.cast<ObjectFileHeader>().c();
|
||||
if (m_state == 0) {
|
||||
// state 0 <- copying data.
|
||||
// the actual game does all copying in one shot. I assume this is ok because v3 files are just
|
||||
// code and always small. Large data which takes too long to copy should use v2.
|
||||
|
||||
// loop over segments
|
||||
for (s32 seg_id = ofh->segment_count - 1; seg_id >= 0; seg_id--) {
|
||||
// link the infos
|
||||
ofh->link_infos[seg_id].offset += m_link_block_ptr.offset;
|
||||
ofh->code_infos[seg_id].offset += m_object_data.offset;
|
||||
|
||||
if (seg_id == DEBUG_SEGMENT) {
|
||||
if (!DebugSegment) {
|
||||
// clear code info if we aren't going to copy the debug segment.
|
||||
ofh->code_infos[seg_id].offset = 0;
|
||||
ofh->code_infos[seg_id].size = 0;
|
||||
} else {
|
||||
if (ofh->code_infos[seg_id].size == 0) {
|
||||
// not actually present
|
||||
ofh->code_infos[seg_id].offset = 0;
|
||||
} else {
|
||||
Ptr<u8> src(ofh->code_infos[seg_id].offset);
|
||||
ofh->code_infos[seg_id].offset =
|
||||
kmalloc(kdebugheap, ofh->code_infos[seg_id].size, 0, "debug-segment").offset;
|
||||
if (ofh->code_infos[seg_id].offset == 0) {
|
||||
MsgErr("dkernel: unable to malloc %d bytes for debug-segment\n",
|
||||
ofh->code_infos[seg_id].size);
|
||||
return 1;
|
||||
}
|
||||
memmove(Ptr<u8>(ofh->code_infos[seg_id].offset).c(), src.c(),
|
||||
ofh->code_infos[seg_id].size);
|
||||
}
|
||||
}
|
||||
} else if (seg_id == MAIN_SEGMENT) {
|
||||
if (ofh->code_infos[seg_id].size == 0) {
|
||||
ofh->code_infos[seg_id].offset = 0;
|
||||
} else {
|
||||
Ptr<u8> src(ofh->code_infos[seg_id].offset);
|
||||
ofh->code_infos[seg_id].offset =
|
||||
kmalloc(m_heap, ofh->code_infos[seg_id].size, 0, "main-segment").offset;
|
||||
if (ofh->code_infos[seg_id].offset == 0) {
|
||||
MsgErr("dkernel: unable to malloc %d bytes for main-segment\n",
|
||||
ofh->code_infos[seg_id].size);
|
||||
return 1;
|
||||
}
|
||||
memmove(Ptr<u8>(ofh->code_infos[seg_id].offset).c(), src.c(),
|
||||
ofh->code_infos[seg_id].size);
|
||||
}
|
||||
} else if (seg_id == TOP_LEVEL_SEGMENT) {
|
||||
if (ofh->code_infos[seg_id].size == 0) {
|
||||
ofh->code_infos[seg_id].offset = 0;
|
||||
} else {
|
||||
Ptr<u8> src(ofh->code_infos[seg_id].offset);
|
||||
ofh->code_infos[seg_id].offset =
|
||||
kmalloc(m_heap, ofh->code_infos[seg_id].size, KMALLOC_TOP, "top-level-segment")
|
||||
.offset;
|
||||
if (ofh->code_infos[seg_id].offset == 0) {
|
||||
MsgErr("dkernel: unable to malloc %d bytes for top-level-segment\n",
|
||||
ofh->code_infos[seg_id].size);
|
||||
return 1;
|
||||
}
|
||||
memmove(Ptr<u8>(ofh->code_infos[seg_id].offset).c(), src.c(),
|
||||
ofh->code_infos[seg_id].size);
|
||||
}
|
||||
} else {
|
||||
printf("UNHANDLED SEG ID IN WORK V3 STATE 1\n");
|
||||
}
|
||||
}
|
||||
|
||||
m_state = 1;
|
||||
m_segment_process = 0;
|
||||
return 0;
|
||||
} else if (m_state == 1) {
|
||||
// state 1: linking. For now all links are done at once. This is probably going to be fine on a
|
||||
// modern computer. But the game broke this into multiple steps.
|
||||
if (m_segment_process < ofh->segment_count) {
|
||||
if (ofh->code_infos[m_segment_process].offset) {
|
||||
Ptr<u8> lp(ofh->link_infos[m_segment_process].offset);
|
||||
|
||||
while (*lp) {
|
||||
switch (*lp) {
|
||||
case LINK_TABLE_END:
|
||||
break;
|
||||
case LINK_SYMBOL_OFFSET:
|
||||
lp = lp + 1;
|
||||
lp = lp + symlink_v3(lp, Ptr<u8>(ofh->code_infos[m_segment_process].offset));
|
||||
break;
|
||||
case LINK_TYPE_PTR:
|
||||
lp = lp + 1; // seek past id
|
||||
lp = lp + typelink_v3(lp, Ptr<u8>(ofh->code_infos[m_segment_process].offset));
|
||||
break;
|
||||
case LINK_DISTANCE_TO_OTHER_SEG_64:
|
||||
lp = lp + 1;
|
||||
lp = lp + cross_seg_dist_link_v3(lp, ofh, m_segment_process, 8);
|
||||
break;
|
||||
case LINK_DISTANCE_TO_OTHER_SEG_32:
|
||||
lp = lp + 1;
|
||||
lp = lp + cross_seg_dist_link_v3(lp, ofh, m_segment_process, 4);
|
||||
break;
|
||||
case LINK_PTR:
|
||||
lp = lp + 1;
|
||||
lp = lp + ptr_link_v3(lp, ofh, m_segment_process);
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, fmt::format("unknown link table thing {}", *lp));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_segment_process++;
|
||||
} else {
|
||||
// all done, can set the entry point to the top-level.
|
||||
m_entry = Ptr<u8>(ofh->code_infos[TOP_LEVEL_SEGMENT].offset) + 4;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
else {
|
||||
printf("WORK v3 INVALID STATE\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void link_control::jak3_finish(bool jump_from_c_to_goal) {
|
||||
// CacheFlush(this->m_ptr_2, this->m_code_size);
|
||||
auto old_debug_segment = DebugSegment;
|
||||
if (m_keep_debug) {
|
||||
// note - this probably doesn't work because DebugSegment isn't *debug-segment*.
|
||||
DebugSegment = s7.offset + jak3_symbols::FIX_SYM_TRUE;
|
||||
}
|
||||
if (m_flags & LINK_FLAG_FORCE_FAST_LINK) {
|
||||
FastLink = 1;
|
||||
}
|
||||
|
||||
*EnableMethodSet = *EnableMethodSet + m_keep_debug;
|
||||
|
||||
if (m_opengoal) {
|
||||
// setup mips2c functions
|
||||
const auto& it = Mips2C::gMips2CLinkCallbacks[GameVersion::Jak3].find(m_object_name);
|
||||
if (it != Mips2C::gMips2CLinkCallbacks[GameVersion::Jak3].end()) {
|
||||
for (auto& x : it->second) {
|
||||
x();
|
||||
}
|
||||
}
|
||||
|
||||
// execute top level!
|
||||
if (m_entry.offset && (m_flags & LINK_FLAG_EXECUTE)) {
|
||||
if (jump_from_c_to_goal) {
|
||||
u64 goal_stack = u64(g_ee_main_mem) + EE_MAIN_MEM_SIZE - 8;
|
||||
call_goal_on_stack(m_entry.cast<Function>(), goal_stack, s7.offset, g_ee_main_mem);
|
||||
} else {
|
||||
call_goal(m_entry.cast<Function>(), 0, 0, 0, s7.offset, g_ee_main_mem);
|
||||
}
|
||||
}
|
||||
|
||||
// inform compiler that we loaded.
|
||||
if (m_flags & LINK_FLAG_OUTPUT_LOAD) {
|
||||
output_segment_load(m_object_name, m_link_block_ptr, m_flags);
|
||||
}
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
*EnableMethodSet = *EnableMethodSet - this->m_keep_debug;
|
||||
FastLink = 0;
|
||||
m_heap->top = m_heap_top;
|
||||
DebugSegment = old_debug_segment;
|
||||
|
||||
m_busy = false;
|
||||
if (m_on_global_heap) {
|
||||
jak3::kmemclose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
Ptr<uint8_t> link_and_exec(Ptr<uint8_t> data,
|
||||
const char* name,
|
||||
int32_t size,
|
||||
Ptr<kheapinfo> heap,
|
||||
uint32_t flags,
|
||||
bool jump_from_c_to_goal) {
|
||||
if (link_busy()) {
|
||||
printf("-------------> saved link is busy\n");
|
||||
// probably won't end well...
|
||||
}
|
||||
link_control lc;
|
||||
lc.jak3_begin(data, name, size, heap, flags);
|
||||
uint32_t done;
|
||||
do {
|
||||
done = lc.jak3_work();
|
||||
} while (!done);
|
||||
lc.jak3_finish(jump_from_c_to_goal);
|
||||
return lc.m_entry;
|
||||
}
|
||||
|
||||
u64 link_and_exec_wrapper(u64* args) {
|
||||
return link_and_exec(Ptr<u8>(args[0]), Ptr<char>(args[1]).c(), args[2], Ptr<kheapinfo>(args[3]),
|
||||
args[4], false)
|
||||
.offset;
|
||||
}
|
||||
|
||||
u32 link_busy() {
|
||||
return saved_link_control.m_busy;
|
||||
}
|
||||
void link_reset() {
|
||||
saved_link_control.m_busy = 0;
|
||||
}
|
||||
uint64_t link_begin(u64* /*args*/) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
uint64_t link_resume() {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Note: update_goal_fns changed to skip the hashtable lookup since symlink2/symlink3 are now fixed
|
||||
// symbols.
|
||||
|
||||
/*!
|
||||
* The ULTIMATE MEMORY COPY
|
||||
* IT IS VERY FAST
|
||||
* but it may use the scratchpad. It is implemented in GOAL, and falls back to normal C memcpy
|
||||
* if GOAL isn't loaded, or if the alignment isn't good enough.
|
||||
*/
|
||||
void ultimate_memcpy(void* dst, void* src, uint32_t size) {
|
||||
// only possible if alignment is good.
|
||||
if (!(u64(dst) & 0xf) && !(u64(src) & 0xf) && !(u64(size) & 0xf) && size > 0xfff) {
|
||||
if (!gfunc_774.offset) {
|
||||
// GOAL function is unknown, lets see if its loaded:
|
||||
auto sym_val = *((s7 + jak3_symbols::FIX_SYM_ULTIMATE_MEMCPY - 1).cast<u32>());
|
||||
if (sym_val == 0) {
|
||||
memmove(dst, src, size);
|
||||
return;
|
||||
}
|
||||
gfunc_774.offset = sym_val;
|
||||
}
|
||||
|
||||
Ptr<u8>(call_goal(gfunc_774, make_u8_ptr(dst).offset, make_u8_ptr(src).offset, size, s7.offset,
|
||||
g_ee_main_mem))
|
||||
.c();
|
||||
} else {
|
||||
memmove(dst, src, size);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
20
game/kernel/jak3/klink.h
Normal file
20
game/kernel/jak3/klink.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
|
||||
namespace jak3 {
|
||||
Ptr<uint8_t> link_and_exec(Ptr<uint8_t> data,
|
||||
const char* name,
|
||||
int32_t size,
|
||||
Ptr<kheapinfo> heap,
|
||||
uint32_t flags,
|
||||
bool jump_from_c_to_goal);
|
||||
u64 link_and_exec_wrapper(u64* args);
|
||||
u32 link_busy();
|
||||
void link_reset();
|
||||
uint64_t link_begin(u64* args);
|
||||
uint64_t link_resume();
|
||||
} // namespace jak3
|
118
game/kernel/jak3/klisten.cpp
Normal file
118
game/kernel/jak3/klisten.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "klisten.h"
|
||||
|
||||
#include "common/symbols.h"
|
||||
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/Symbol4.h"
|
||||
#include "game/kernel/common/kdsnetm.h"
|
||||
#include "game/kernel/common/klink.h"
|
||||
#include "game/kernel/common/klisten.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
#include "game/kernel/common/kprint.h"
|
||||
#include "game/kernel/common/kscheme.h"
|
||||
#include "game/kernel/jak3/klink.h"
|
||||
#include "game/kernel/jak3/kscheme.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
using namespace jak3_symbols;
|
||||
|
||||
Ptr<Symbol4<u32>> ListenerLinkBlock;
|
||||
Ptr<Symbol4<u32>> ListenerFunction;
|
||||
Ptr<Symbol4<u32>> KernelFunction; // new in jak2
|
||||
Ptr<Symbol4<u32>> kernel_dispatcher;
|
||||
Ptr<Symbol4<u32>> kernel_packages;
|
||||
Ptr<Symbol4<u32>> sync_dispatcher;
|
||||
|
||||
void klisten_init_globals() {
|
||||
ListenerLinkBlock.offset = 0;
|
||||
ListenerFunction.offset = 0;
|
||||
KernelFunction.offset = 0;
|
||||
kernel_dispatcher.offset = 0;
|
||||
kernel_packages.offset = 0;
|
||||
sync_dispatcher.offset = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Initialize the Listener by setting up symbols shared between GOAL and C for the listener.
|
||||
* Also adds "kernel" to the kernel_packages list.
|
||||
* There was an "ACK" message sent here, but this is removed because we don't need it.
|
||||
*/
|
||||
void InitListener() {
|
||||
ListenerLinkBlock = intern_from_c(-1, 0, "*listener-link-block*");
|
||||
ListenerFunction = intern_from_c(-1, 0, "*listener-function*");
|
||||
KernelFunction = intern_from_c(-1, 0, "*kernel-function*");
|
||||
kernel_dispatcher = intern_from_c(-1, 0, "kernel-dispatcher");
|
||||
sync_dispatcher = intern_from_c(-1, 0, "sync-dispatcher");
|
||||
kernel_packages = intern_from_c(-1, 0, "*kernel-packages*");
|
||||
print_column = intern_from_c(-1, 0, "*print-column*").cast<u32>(); // this is wrong
|
||||
ListenerLinkBlock->value() = s7.offset;
|
||||
ListenerFunction->value() = s7.offset;
|
||||
KernelFunction->value() = s7.offset;
|
||||
|
||||
kernel_packages->value() =
|
||||
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE - 1).cast<u32>()),
|
||||
make_string_from_c("kernel"), kernel_packages->value());
|
||||
// if(MasterDebug) {
|
||||
// SendFromBufferD(MSG_ACK, 0, AckBufArea + sizeof(ListenerMessageHeader), 0);
|
||||
// }
|
||||
}
|
||||
|
||||
/*!
|
||||
* Handle an incoming listener message
|
||||
*/
|
||||
void ProcessListenerMessage(Ptr<char> msg) {
|
||||
// flag that the listener is connected!
|
||||
ListenerStatus = 1;
|
||||
switch (protoBlock.msg_kind) {
|
||||
case LTT_MSG_POKE:
|
||||
// just flush any pending stuff.
|
||||
ClearPending();
|
||||
break;
|
||||
case LTT_MSG_INSPECT:
|
||||
inspect_object(atoi(msg.c()));
|
||||
ClearPending();
|
||||
break;
|
||||
case LTT_MSG_PRINT:
|
||||
print_object(atoi(msg.c()));
|
||||
ClearPending();
|
||||
break;
|
||||
case LTT_MSG_PRINT_SYMBOLS:
|
||||
printf("[ERROR] unsupported message kind LTT_MSG_PRINT_SYMBOLS (NYI)\n");
|
||||
break;
|
||||
case LTT_MSG_RESET:
|
||||
MasterExit = RuntimeExitStatus::RESTART_RUNTIME;
|
||||
break;
|
||||
case LTT_MSG_SHUTDOWN:
|
||||
MasterExit = RuntimeExitStatus::EXIT;
|
||||
break;
|
||||
case LTT_MSG_CODE: {
|
||||
auto buffer = kmalloc(kdebugheap, MessCount, 0, "listener-link-block");
|
||||
memcpy(buffer.c(), msg.c(), MessCount);
|
||||
ListenerLinkBlock->value() = buffer.offset + 4;
|
||||
// note - this will stash the linked code in the top level and free it.
|
||||
// it will then be used-after-free, but this is OK because nobody else will allocate.
|
||||
// the kernel dispatcher should immediately execute the listener function to avoid this
|
||||
// getting squashed.
|
||||
|
||||
// this setup allows listener function execution to clean up after itself.
|
||||
|
||||
// we have added the LINK_FLAG_OUTPUT_LOAD
|
||||
// jump from c to goal because this is called from the C++ stack.
|
||||
ListenerFunction->value() = link_and_exec(buffer, "*listener*", 0, kdebugheap,
|
||||
LINK_FLAG_FORCE_DEBUG | LINK_FLAG_OUTPUT_LOAD, true)
|
||||
.offset;
|
||||
return; // don't ack yet, this will happen after the function runs.
|
||||
} break;
|
||||
default:
|
||||
MsgErr("dkernel: unknown message error: <%d> of %d bytes\n", protoBlock.msg_kind, MessCount);
|
||||
break;
|
||||
}
|
||||
SendAck();
|
||||
}
|
||||
|
||||
int sql_query_sync(Ptr<String> /*string_in*/) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
} // namespace jak3
|
20
game/kernel/jak3/klisten.h
Normal file
20
game/kernel/jak3/klisten.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/Symbol4.h"
|
||||
#include "game/kernel/common/kscheme.h"
|
||||
|
||||
namespace jak3 {
|
||||
extern Ptr<Symbol4<u32>> ListenerLinkBlock;
|
||||
extern Ptr<Symbol4<u32>> ListenerFunction;
|
||||
extern Ptr<Symbol4<u32>> KernelFunction; // new in jak2
|
||||
extern Ptr<Symbol4<u32>> kernel_dispatcher;
|
||||
extern Ptr<Symbol4<u32>> sync_dispatcher; // new in jak2
|
||||
extern Ptr<Symbol4<u32>> kernel_packages;
|
||||
void InitListener();
|
||||
void klisten_init_globals();
|
||||
void ProcessListenerMessage(Ptr<char> msg);
|
||||
int sql_query_sync(Ptr<String> string_in);
|
||||
} // namespace jak3
|
513
game/kernel/jak3/kmachine.cpp
Normal file
513
game/kernel/jak3/kmachine.cpp
Normal file
@ -0,0 +1,513 @@
|
||||
#include "kmachine.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/symbols.h"
|
||||
|
||||
#include "game/graphics/gfx.h"
|
||||
#include "game/graphics/sceGraphicsInterface.h"
|
||||
#include "game/kernel/common/fileio.h"
|
||||
#include "game/kernel/common/kdgo.h"
|
||||
#include "game/kernel/common/kdsnetm.h"
|
||||
#include "game/kernel/common/kernel_types.h"
|
||||
#include "game/kernel/common/klink.h"
|
||||
#include "game/kernel/common/kmachine.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
#include "game/kernel/common/kprint.h"
|
||||
#include "game/kernel/common/ksocket.h"
|
||||
#include "game/kernel/common/ksound.h"
|
||||
#include "game/kernel/common/memory_layout.h"
|
||||
#include "game/kernel/jak3/kboot.h"
|
||||
#include "game/kernel/jak3/kdgo.h"
|
||||
#include "game/kernel/jak3/klisten.h"
|
||||
#include "game/kernel/jak3/kmalloc.h"
|
||||
#include "game/kernel/jak3/kscheme.h"
|
||||
#include "game/kernel/jak3/ksound.h"
|
||||
#include "game/sce/deci2.h"
|
||||
#include "game/sce/libdma.h"
|
||||
#include "game/sce/libgraph.h"
|
||||
#include "game/sce/sif_ee.h"
|
||||
#include "game/sce/stubs.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
using namespace ee;
|
||||
|
||||
/*!
|
||||
* Initialize global variables based on command line parameters. Not called in retail versions,
|
||||
* but it is present in the ELF.
|
||||
* DONE
|
||||
* Modified to use std::string, and removed call to fflush.
|
||||
*/
|
||||
void InitParms(int argc, const char* const* argv) {
|
||||
// Modified default settings to boot up the game like normal if no arguments are present.
|
||||
if (argc == 1) {
|
||||
DiskBoot = 1;
|
||||
isodrv = fakeiso;
|
||||
modsrc = 0;
|
||||
reboot_iop = 0;
|
||||
DebugSegment = 0;
|
||||
MasterDebug = 0;
|
||||
DebugSymbols = true;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string arg = argv[i];
|
||||
// DVD Settings
|
||||
// ----------------------------
|
||||
|
||||
// the "cd" mode uses the DVD drive for everything. This is how the game runs in retail
|
||||
if (arg == "-cd") {
|
||||
Msg(6, "dkernel: cd mode\n");
|
||||
isodrv = iso_cd; // use the actual DVD drive for data files
|
||||
modsrc = 1; // use the DVD drive data for IOP modules
|
||||
reboot_iop = 1; // Reboot the IOP (load new IOP runtime)
|
||||
}
|
||||
|
||||
// the "cddata" uses the DVD drive for everything but IOP modules.
|
||||
if (arg == "-cddata") {
|
||||
Msg(6, "dkernel: cddata mode\n");
|
||||
isodrv = iso_cd; // tell IOP to use actual DVD drive for data files
|
||||
modsrc = 0; // don't use DVD drive for IOP modules
|
||||
reboot_iop = 0; // no need to reboot the IOP
|
||||
}
|
||||
|
||||
if (arg == "-demo") {
|
||||
Msg(6, "dkernel: demo mode\n");
|
||||
kstrcpy(DebugBootMessage, "demo");
|
||||
}
|
||||
|
||||
// new for jak 2
|
||||
if (arg == "-kiosk") {
|
||||
Msg(6, "dkernel: kiosk mode\n");
|
||||
kstrcpy(DebugBootMessage, "kiosk");
|
||||
}
|
||||
|
||||
// new for jak 2
|
||||
if (arg == "-preview") {
|
||||
Msg(6, "dkernel: preview mode\n");
|
||||
kstrcpy(DebugBootMessage, "preview");
|
||||
}
|
||||
|
||||
// the "deviso" mode is one of two modes for testing without the need for DVDs
|
||||
if (arg == "-deviso") {
|
||||
Msg(6, "dkernel: deviso mode\n");
|
||||
isodrv = deviso; // IOP deviso mode
|
||||
modsrc = 2; // now 2 for Jak 2
|
||||
reboot_iop = 0;
|
||||
}
|
||||
// the "fakeiso" mode is the other of two modes for testing without the need for DVDs
|
||||
if (arg == "-fakeiso") {
|
||||
Msg(6, "dkernel: fakeiso mode\n");
|
||||
isodrv = fakeiso; // IOP fakeeiso mode
|
||||
modsrc = 0; // no IOP module loading (there's no DVD to load from!)
|
||||
reboot_iop = 0;
|
||||
}
|
||||
|
||||
// the "boot" mode is used to set GOAL up for running the game in retail mode
|
||||
if (arg == "-boot") {
|
||||
Msg(6, "dkernel: boot mode\n");
|
||||
MasterDebug = 0;
|
||||
DiskBoot = 1;
|
||||
DebugSegment = 0;
|
||||
}
|
||||
|
||||
// new for jak 2
|
||||
if (arg == "-debug-boot") {
|
||||
Msg(6, "dkernel: debug-boot mode\n");
|
||||
MasterDebug = 0;
|
||||
DebugSegment = 1;
|
||||
DiskBoot = 1;
|
||||
}
|
||||
|
||||
// traditional debug mode
|
||||
if (arg == "-debug") {
|
||||
Msg(6, "dkernel: debug mode\n");
|
||||
MasterDebug = 1;
|
||||
DebugSegment = 1;
|
||||
}
|
||||
|
||||
// the "debug-mem" mode is used to set up GOAL in debug mode, but not to load debug-segments
|
||||
if (arg == "-debug-mem") {
|
||||
Msg(6, "dkernel: debug-mem mode\n");
|
||||
MasterDebug = 1;
|
||||
DebugSegment = 0;
|
||||
}
|
||||
|
||||
// TODO overlord 1 vs. 2 switch
|
||||
|
||||
if (arg == "-debug-symbols") {
|
||||
Msg(6, "dkernel: debug-symbols on\n");
|
||||
DebugSymbols = true;
|
||||
}
|
||||
|
||||
if (arg == "-no-debug-symbols") {
|
||||
Msg(6, "dkernel: debug-symbols off\n");
|
||||
DebugSymbols = true;
|
||||
}
|
||||
|
||||
// the "-level [level-name]" mode is used to inform the game to boot a specific level
|
||||
// the default level is "#f".
|
||||
if (arg == "-level") {
|
||||
i++;
|
||||
std::string levelName = argv[i];
|
||||
Msg(6, "dkernel: level %s\n", levelName.c_str());
|
||||
kstrcpy(DebugBootLevel, levelName.c_str());
|
||||
ASSERT_NOT_REACHED(); // symbol ID junk
|
||||
}
|
||||
|
||||
// new for jak 2
|
||||
if (arg == "-user") {
|
||||
i++;
|
||||
std::string userName = argv[i];
|
||||
Msg(6, "dkernel: user %s\n", userName.c_str());
|
||||
kstrcpy(DebugBootUser, userName.c_str());
|
||||
}
|
||||
|
||||
// new for jak 2
|
||||
if (arg == "-art") {
|
||||
i++;
|
||||
std::string artGroupName = argv[i];
|
||||
Msg(6, "dkernel: art-group %s\n", artGroupName.c_str());
|
||||
kstrcpy(DebugBootArtGroup, artGroupName.c_str());
|
||||
kstrcpy(DebugBootMessage, "art-group");
|
||||
}
|
||||
|
||||
// an added mode to allow booting without a KERNEL.CGO for testing
|
||||
if (arg == "-nokernel") {
|
||||
Msg(6, "dkernel: no kernel mode\n");
|
||||
MasterUseKernel = false;
|
||||
}
|
||||
|
||||
// an added mode to allow booting without sound for testing
|
||||
if (arg == "-nosound") {
|
||||
Msg(6, "dkernel: no sound mode\n");
|
||||
masterConfig.disable_sound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* This is mostly copy-pasted from jak2 and very simplified until we have overlord 2.
|
||||
*/
|
||||
void InitIOP() {
|
||||
Msg(6, "dkernel: boot:%d debug:%d mem:%d syms:%d dev:%d mod:%d\n", DiskBoot, MasterDebug,
|
||||
DebugSegment, DebugSymbols, isodrv, modsrc);
|
||||
sceSifInitRpc(0);
|
||||
|
||||
// init cd if we need it
|
||||
if (((isodrv == iso_cd) || (modsrc == 1)) || (reboot_iop == 1)) {
|
||||
InitCD();
|
||||
}
|
||||
|
||||
if ((isodrv == iso_cd) || (modsrc == 1)) {
|
||||
InitCD();
|
||||
}
|
||||
|
||||
char overlord_boot_command[256];
|
||||
char* cmd = overlord_boot_command;
|
||||
kstrcpy(cmd, init_types[(int)isodrv]);
|
||||
cmd = cmd + strlen(cmd) + 1;
|
||||
if (!strncmp(DebugBootMessage, "demo", 4)) {
|
||||
kstrcpy(cmd, "SCREEN1.DEM");
|
||||
} else {
|
||||
kstrcpy(cmd, "SCREEN1.USA");
|
||||
}
|
||||
cmd = cmd + strlen(cmd) + 1;
|
||||
if (masterConfig.disable_sound) {
|
||||
kstrcpy(cmd, "-nosound");
|
||||
cmd = cmd + strlen(cmd) + 1;
|
||||
}
|
||||
|
||||
int total_len = cmd - overlord_boot_command;
|
||||
|
||||
if (modsrc == 0) {
|
||||
printf("Initializing CD library in FAKEISO mode\n");
|
||||
if (sceSifLoadModule("host0:bin/overlord.irx", total_len, overlord_boot_command) < 0) {
|
||||
MsgErr("loading overlord.irx <3> failed\n");
|
||||
exit(0);
|
||||
}
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
int rv = sceMcInit();
|
||||
if (rv < 0) {
|
||||
MsgErr("MC driver init failed %d\n", rv);
|
||||
exit(0);
|
||||
}
|
||||
printf("InitIOP OK\n");
|
||||
}
|
||||
|
||||
int InitMachine() {
|
||||
// uVar2 = FUN_00116ec8(0x10);
|
||||
// heap_start = malloc(0x10);
|
||||
|
||||
u32 global_heap_size = GLOBAL_HEAP_END - HEAP_START;
|
||||
float size_mb = ((float)global_heap_size) / (float)(1 << 20);
|
||||
lg::info("gkernel: global heap 0x{:08x} to 0x{:08x} (size {:.3f} MB)", HEAP_START,
|
||||
GLOBAL_HEAP_END, size_mb);
|
||||
kinitheap(kglobalheap, Ptr<u8>(HEAP_START), global_heap_size);
|
||||
|
||||
kmemopen_from_c(kglobalheap, "global");
|
||||
kmemopen_from_c(kglobalheap, "scheme-globals");
|
||||
|
||||
if (!MasterDebug && !DebugSegment) {
|
||||
// if no debug, we make the kheapinfo structure NULL so GOAL knows not to use it.
|
||||
// note: either MasterDebug or DebugSegment is enough to give use the debug heap.
|
||||
kdebugheap.offset = 0;
|
||||
} else {
|
||||
kinitheap(kdebugheap, Ptr<u8>(DEBUG_HEAP_START), jak3::DEBUG_HEAP_SIZE);
|
||||
}
|
||||
init_output();
|
||||
InitIOP();
|
||||
// sceGsResetPath();
|
||||
InitVideo();
|
||||
// FlushCache(0);
|
||||
// FlushCache(2);
|
||||
// sceGsSyncV(0);
|
||||
// if (scePadInit(0) != 1) {
|
||||
// MsgErr("dkernel: !init pad\n");
|
||||
// }
|
||||
if (MasterDebug != 0) {
|
||||
InitGoalProto();
|
||||
} else {
|
||||
ee::sceDeci2Disable(); // added
|
||||
}
|
||||
printf("InitSound\n");
|
||||
InitSound();
|
||||
printf("InitRPC\n");
|
||||
InitRPC();
|
||||
reset_output();
|
||||
clear_print();
|
||||
auto status = InitHeapAndSymbol();
|
||||
if (status >= 0) {
|
||||
printf("InitListenerConnect\n");
|
||||
InitListenerConnect();
|
||||
printf("InitCheckListener\n");
|
||||
InitCheckListener();
|
||||
Msg(6, "kernel: machine started\n");
|
||||
return 0;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int ShutdownMachine() {
|
||||
Msg(6, "kernel: machine shutdown (reason %d)\n", MasterExit);
|
||||
|
||||
StopIOP();
|
||||
ShutdownSound();
|
||||
CloseListener();
|
||||
|
||||
ShutdownGoalProto();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 KeybdGetData(u32 /*_mouse*/) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
u32 MouseGetData(u32 /*_mouse*/) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Open a file-stream. Name is a GOAL string. Mode is a GOAL symbol. Use 'read for readonly
|
||||
* and anything else for write only.
|
||||
*/
|
||||
u64 kopen(u64 fs, u64 name, u64 mode) {
|
||||
auto file_stream = Ptr<FileStream>(fs).c();
|
||||
file_stream->mode = mode;
|
||||
file_stream->name = name;
|
||||
file_stream->flags = 0;
|
||||
printf("****** CALL TO kopen() ******\n");
|
||||
char buffer[128];
|
||||
// sprintf(buffer, "host:%s", Ptr<String>(name)->data());
|
||||
sprintf(buffer, "%s", Ptr<String>(name)->data());
|
||||
if (!strcmp(sym_to_cstring(Ptr<Symbol4<u8>>(mode)), "read")) {
|
||||
// 0x1
|
||||
file_stream->file = ee::sceOpen(buffer, SCE_RDONLY);
|
||||
} else if (!strcmp(sym_to_cstring(Ptr<Symbol4<u8>>(mode)), "append")) {
|
||||
// new in jak 2!
|
||||
// 0x202
|
||||
file_stream->file = ee::sceOpen(buffer, SCE_CREAT | SCE_WRONLY);
|
||||
} else {
|
||||
// 0x602
|
||||
file_stream->file = ee::sceOpen(buffer, SCE_TRUNC | SCE_CREAT | SCE_WRONLY);
|
||||
}
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
void PutDisplayEnv(u32 alp) {
|
||||
// we can mostly ignore this, except for one value that sets the 'blackout' amount.
|
||||
auto* renderer = Gfx::GetCurrentRenderer();
|
||||
if (renderer) {
|
||||
renderer->set_pmode_alp(alp / 255.f);
|
||||
}
|
||||
}
|
||||
|
||||
void aybabtu() {}
|
||||
|
||||
void pc_set_levels(u32 lev_list) {
|
||||
if (!Gfx::GetCurrentRenderer()) {
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> levels;
|
||||
for (int i = 0; i < LEVEL_MAX; i++) {
|
||||
u32 lev = *Ptr<u32>(lev_list + i * 4);
|
||||
std::string ls = Ptr<String>(lev).c()->data();
|
||||
if (ls != "none" && ls != "#f" && ls != "") {
|
||||
levels.push_back(ls);
|
||||
}
|
||||
}
|
||||
|
||||
Gfx::GetCurrentRenderer()->set_levels(levels);
|
||||
}
|
||||
|
||||
void pc_set_active_levels(u32 lev_list) {
|
||||
if (!Gfx::GetCurrentRenderer()) {
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> levels;
|
||||
for (int i = 0; i < LEVEL_MAX; i++) {
|
||||
u32 lev = *Ptr<u32>(lev_list + i * 4);
|
||||
std::string ls = Ptr<String>(lev).c()->data();
|
||||
if (ls != "none" && ls != "#f" && ls != "") {
|
||||
levels.push_back(ls);
|
||||
}
|
||||
}
|
||||
|
||||
Gfx::GetCurrentRenderer()->set_active_levels(levels);
|
||||
}
|
||||
|
||||
//// PC Stuff
|
||||
void InitMachine_PCPort() {
|
||||
// PC Port added functions
|
||||
init_common_pc_port_functions(
|
||||
make_function_symbol_from_c,
|
||||
[](const char* name) {
|
||||
const auto result = intern_from_c(-1, 0, name);
|
||||
InternFromCInfo info{};
|
||||
info.offset = result.offset;
|
||||
return info;
|
||||
},
|
||||
make_string_from_c);
|
||||
|
||||
make_function_symbol_from_c("__pc-set-levels", (void*)pc_set_levels);
|
||||
make_function_symbol_from_c("__pc-set-active-levels", (void*)pc_set_active_levels);
|
||||
// make_function_symbol_from_c("__pc-get-tex-remap", (void*)lookup_jak2_texture_dest_offset);
|
||||
// make_function_symbol_from_c("pc-init-autosplitter-struct", (void*)init_autosplit_struct);
|
||||
// make_function_symbol_from_c("pc-encode-utf8-string", (void*)encode_utf8_string);
|
||||
|
||||
// discord rich presence
|
||||
// make_function_symbol_from_c("pc-discord-rpc-update", (void*)update_discord_rpc);
|
||||
|
||||
// debugging tools
|
||||
// make_function_symbol_from_c("alloc-vagdir-names", (void*)alloc_vagdir_names);
|
||||
|
||||
// external RPCs
|
||||
/*
|
||||
make_function_symbol_from_c("pc-fetch-external-speedrun-times",
|
||||
(void*)pc_fetch_external_speedrun_times);
|
||||
make_function_symbol_from_c("pc-fetch-external-race-times", (void*)pc_fetch_external_race_times);
|
||||
make_function_symbol_from_c("pc-fetch-external-highscores", (void*)pc_fetch_external_highscores);
|
||||
make_function_symbol_from_c("pc-get-external-speedrun-time",
|
||||
(void*)pc_get_external_speedrun_time);
|
||||
make_function_symbol_from_c("pc-get-external-race-time", (void*)pc_get_external_race_time);
|
||||
make_function_symbol_from_c("pc-get-external-highscore", (void*)pc_get_external_highscore);
|
||||
make_function_symbol_from_c("pc-get-num-external-speedrun-times",
|
||||
(void*)pc_get_num_external_speedrun_times);
|
||||
make_function_symbol_from_c("pc-get-num-external-race-times",
|
||||
(void*)pc_get_num_external_race_times);
|
||||
make_function_symbol_from_c("pc-get-num-external-highscores",
|
||||
(void*)pc_get_num_external_highscores);
|
||||
*/
|
||||
|
||||
// setup string constants
|
||||
auto user_dir_path = file_util::get_user_config_dir();
|
||||
intern_from_c(-1, 0, "*pc-user-dir-base-path*")->value() =
|
||||
make_string_from_c(user_dir_path.string().c_str());
|
||||
auto settings_path = file_util::get_user_settings_dir(g_game_version);
|
||||
intern_from_c(-1, 0, "*pc-settings-folder*")->value() =
|
||||
make_string_from_c(settings_path.string().c_str());
|
||||
intern_from_c(-1, 0, "*pc-settings-built-sha*")->value() =
|
||||
make_string_from_c(build_revision().c_str());
|
||||
}
|
||||
// End PC Stuff
|
||||
|
||||
void InitMachineScheme() {
|
||||
make_function_symbol_from_c("put-display-env", (void*)PutDisplayEnv);
|
||||
make_function_symbol_from_c("syncv", (void*)sceGsSyncV);
|
||||
make_function_symbol_from_c("sync-path", (void*)sceGsSyncPath);
|
||||
make_function_symbol_from_c("reset-path", (void*)sceGsResetPath);
|
||||
make_function_symbol_from_c("reset-graph", (void*)sceGsResetGraph);
|
||||
make_function_symbol_from_c("dma-sync", (void*)sceDmaSync);
|
||||
make_function_symbol_from_c("gs-put-imr", (void*)sceGsPutIMR);
|
||||
make_function_symbol_from_c("gs-get-imr", (void*)sceGsGetIMR);
|
||||
make_function_symbol_from_c("gs-store-image", (void*)sceGsExecStoreImage);
|
||||
make_function_symbol_from_c("flush-cache", (void*)FlushCache);
|
||||
make_function_symbol_from_c("cpad-open", (void*)CPadOpen);
|
||||
make_function_symbol_from_c("cpad-get-data", (void*)CPadGetData);
|
||||
make_function_symbol_from_c("mouse-get-data", (void*)MouseGetData);
|
||||
make_function_symbol_from_c("keybd-get-data", (void*)KeybdGetData);
|
||||
make_function_symbol_from_c("install-handler", (void*)InstallHandler);
|
||||
make_function_symbol_from_c("install-debug-handler", (void*)InstallDebugHandler);
|
||||
make_function_symbol_from_c("file-stream-open", (void*)kopen);
|
||||
make_function_symbol_from_c("file-stream-close", (void*)kclose);
|
||||
make_function_symbol_from_c("file-stream-length", (void*)klength);
|
||||
make_function_symbol_from_c("file-stream-seek", (void*)kseek);
|
||||
make_function_symbol_from_c("file-stream-read", (void*)kread);
|
||||
make_function_symbol_from_c("file-stream-write", (void*)kwrite);
|
||||
make_function_symbol_from_c("scf-get-language", (void*)DecodeLanguage);
|
||||
make_function_symbol_from_c("scf-get-time", (void*)DecodeTime);
|
||||
make_function_symbol_from_c("scf-get-aspect", (void*)DecodeAspect);
|
||||
make_function_symbol_from_c("scf-get-volume", (void*)DecodeVolume);
|
||||
make_function_symbol_from_c("scf-get-territory", (void*)DecodeTerritory);
|
||||
make_function_symbol_from_c("scf-get-timeout", (void*)DecodeTimeout);
|
||||
make_function_symbol_from_c("scf-get-inactive-timeout", (void*)DecodeInactiveTimeout);
|
||||
make_function_symbol_from_c("dma-to-iop", (void*)dma_to_iop);
|
||||
make_function_symbol_from_c("kernel-shutdown", (void*)KernelShutdown);
|
||||
make_function_symbol_from_c("aybabtu", (void*)aybabtu); // was nothing
|
||||
|
||||
InitMachine_PCPort();
|
||||
|
||||
InitSoundScheme();
|
||||
intern_from_c(-1, 0, "*stack-top*")->value() = 0x7f00000;
|
||||
intern_from_c(-1, 0, "*stack-base*")->value() = 0x7ffffff;
|
||||
intern_from_c(-1, 0, "*stack-size*")->value() = 0x100000;
|
||||
intern_from_c(-1, 0, "*kernel-boot-message*")->value() =
|
||||
intern_from_c(-1, 0, DebugBootMessage).offset;
|
||||
intern_from_c(-1, 0, "*user*")->value() = make_string_from_c(DebugBootUser);
|
||||
if (DiskBoot) {
|
||||
intern_from_c(-1, 0, "*kernel-boot-mode*")->value() = intern_from_c(-1, 0, "boot").offset;
|
||||
}
|
||||
if (strcmp(DebugBootLevel, "#f") == 0) {
|
||||
intern_from_c(-1, 0, "*kernel-boot-level*")->value() = s7.offset;
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
intern_from_c(-1, 0, "*kernel-boot-art-group*")->value() = make_string_from_c(DebugBootArtGroup);
|
||||
|
||||
if (DiskBoot != 0) {
|
||||
*EnableMethodSet = *EnableMethodSet + 1;
|
||||
load_and_link_dgo_from_c("game", kglobalheap,
|
||||
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
|
||||
0x400000, true);
|
||||
*EnableMethodSet = *EnableMethodSet + -1;
|
||||
using namespace jak3_symbols;
|
||||
|
||||
kernel_packages->value() =
|
||||
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE - 1).cast<u32>()),
|
||||
make_string_from_c("engine"), kernel_packages->value());
|
||||
kernel_packages->value() =
|
||||
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE - 1).cast<u32>()),
|
||||
make_string_from_c("art"), kernel_packages->value());
|
||||
kernel_packages->value() =
|
||||
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE - 1).cast<u32>()),
|
||||
make_string_from_c("common"), kernel_packages->value());
|
||||
printf("calling play-boot!\n");
|
||||
call_goal_function_by_name("play-boot"); // new function for jak2!
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
8
game/kernel/jak3/kmachine.h
Normal file
8
game/kernel/jak3/kmachine.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {
|
||||
void InitParms(int argc, const char* const* argv);
|
||||
void InitMachineScheme();
|
||||
int InitMachine();
|
||||
int ShutdownMachine();
|
||||
} // namespace jak3
|
16
game/kernel/jak3/kmalloc.cpp
Normal file
16
game/kernel/jak3/kmalloc.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "kmalloc.h"
|
||||
|
||||
namespace jak3 {
|
||||
// these functions are all stubs in all known copies of the ELF.
|
||||
void kmemopen_from_c(Ptr<kheapinfo> heap, const char* name) {
|
||||
(void)heap;
|
||||
(void)name;
|
||||
}
|
||||
|
||||
void kmemopen(u32 heap, u32 name) {
|
||||
(void)heap;
|
||||
(void)name;
|
||||
}
|
||||
|
||||
void kmemclose() {}
|
||||
} // namespace jak3
|
9
game/kernel/jak3/kmalloc.h
Normal file
9
game/kernel/jak3/kmalloc.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
|
||||
namespace jak3 {
|
||||
void kmemopen_from_c(Ptr<kheapinfo> heap, const char* name);
|
||||
void kmemopen(u32 heap, u32 name);
|
||||
void kmemclose();
|
||||
} // namespace jak3
|
3
game/kernel/jak3/kmemcard.cpp
Normal file
3
game/kernel/jak3/kmemcard.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
#include "kmemcard.h"
|
3
game/kernel/jak3/kmemcard.h
Normal file
3
game/kernel/jak3/kmemcard.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {}
|
564
game/kernel/jak3/kprint.cpp
Normal file
564
game/kernel/jak3/kprint.cpp
Normal file
@ -0,0 +1,564 @@
|
||||
#include "kprint.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/goal_constants.h"
|
||||
#include "common/listener_common.h"
|
||||
#include "common/symbols.h"
|
||||
|
||||
#include "game/kernel/common/fileio.h"
|
||||
#include "game/kernel/common/kboot.h"
|
||||
#include "game/kernel/common/klisten.h"
|
||||
#include "game/kernel/common/kprint.h"
|
||||
#include "game/kernel/jak3/kscheme.h"
|
||||
#include "game/sce/sif_ee.h"
|
||||
|
||||
/*!
|
||||
* The GOAL "format" function. The actual function is named "format". However, GOAL's calling
|
||||
* convention differs from x86-64, so GOAL cannot directly call format. There is an assembly
|
||||
* function in format_wrapper.nasm named format. It takes the GOAL argument registers, stores them
|
||||
* in an array on the stack, and calls this function with a pointer to that array.
|
||||
*
|
||||
* This function is a disaster. For now, it's copied from jak1 and then jak 2, with the obvious
|
||||
* fixes made, but it's probably worth another pass.
|
||||
*/
|
||||
s32 format_impl_jak3(uint64_t* args) {
|
||||
using namespace jak3_symbols;
|
||||
using namespace jak3;
|
||||
|
||||
// first two args are dest, format string
|
||||
uint64_t* arg_regs = args + 2;
|
||||
|
||||
// data for arguments in a format command
|
||||
format_struct argument_data[8];
|
||||
|
||||
u32 arg_reg_idx = 0;
|
||||
|
||||
// the gstring
|
||||
char* format_gstring = Ptr<char>(args[1]).c();
|
||||
|
||||
u32 original_dest = args[0];
|
||||
|
||||
// set up print pending
|
||||
char* print_temp = PrintPending.cast<char>().c();
|
||||
if (!PrintPending.offset) {
|
||||
print_temp = PrintBufArea.cast<char>().c() + sizeof(ListenerMessageHeader);
|
||||
}
|
||||
PrintPending = make_ptr(strend(print_temp)).cast<u8>();
|
||||
|
||||
// what we write to
|
||||
char* output_ptr = PrintPending.cast<char>().c();
|
||||
|
||||
// convert gstring to cstring
|
||||
char* format_cstring = format_gstring + 4;
|
||||
|
||||
// mysteries
|
||||
char* PrintPendingLocal2 = PrintPending.cast<char>().c();
|
||||
char* PrintPendingLocal3 = output_ptr;
|
||||
|
||||
// start by computing indentation
|
||||
u32 indentation = 0;
|
||||
|
||||
// read goal binteger
|
||||
if (print_column.offset) {
|
||||
// added the if check so we can format even if the kernel didn't load right.
|
||||
indentation = (*(print_column - 1)) >> 3;
|
||||
}
|
||||
|
||||
// which arg we're on
|
||||
u32 arg_idx = 0;
|
||||
|
||||
// if last char was newline and we have tabs, do tabs
|
||||
if (indentation && output_ptr[-1] == '\n') {
|
||||
for (u32 i = 0; i < indentation; i++) {
|
||||
*output_ptr = ' ';
|
||||
output_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
// input pointer
|
||||
char* format_ptr = format_cstring;
|
||||
|
||||
// loop over the format string
|
||||
while (*format_ptr) {
|
||||
// got a command?
|
||||
if (*format_ptr == '~') {
|
||||
char* arg_start = format_ptr;
|
||||
// get some arguments
|
||||
arg_idx = 0;
|
||||
u8 justify = 0;
|
||||
for (auto& x : argument_data) {
|
||||
x.reset();
|
||||
}
|
||||
|
||||
// read arguments
|
||||
while ((u8)(format_ptr[1] - '0') < 10 || // number 0 to 9
|
||||
format_ptr[1] == ',' || // comma
|
||||
format_ptr[1] == '\'' || // quote
|
||||
format_ptr[1] == '`' || // backtick
|
||||
(argument_data[arg_idx].data[0] == -1 &&
|
||||
(format_ptr[1] == '-' || format_ptr[1] == '+') // flags1 == -1 && +/-
|
||||
)) {
|
||||
// here format_ptr[1] points to next unread character in argument
|
||||
// format_ptr[0] is originally the ~
|
||||
// should exit loop with format_ptr[1] == the command character
|
||||
char arg_char = format_ptr[1]; // gVar1
|
||||
|
||||
if (arg_char == ',') {
|
||||
// advance to next argument
|
||||
arg_idx++; // increment which argument we're on
|
||||
format_ptr++; // increment past comma, and try again
|
||||
continue;
|
||||
}
|
||||
|
||||
// character argument
|
||||
if (arg_char == '\'') { // 0x27
|
||||
argument_data[arg_idx].data[0] = format_ptr[2];
|
||||
format_ptr += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// string argument
|
||||
if (arg_char == '`') { // 0x60
|
||||
u32 i = 0;
|
||||
format_ptr += 2;
|
||||
// read string
|
||||
while (*format_ptr != '`') {
|
||||
argument_data[arg_idx].data[i] = *format_ptr;
|
||||
i++;
|
||||
format_ptr++;
|
||||
}
|
||||
// null terminate
|
||||
argument_data[arg_idx].data[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg_char == '-') { // 0x2d
|
||||
// negative flag
|
||||
argument_data[arg_idx].data[1] = 1;
|
||||
format_ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg_char == '+') { // 0x2b
|
||||
// positive flag does nothing
|
||||
format_ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise:
|
||||
|
||||
// null terminate if we got no args
|
||||
if (argument_data[arg_idx].data[0] == -1) {
|
||||
argument_data[arg_idx].data[0] = 0;
|
||||
}
|
||||
|
||||
// otherwise it's a number
|
||||
argument_data[arg_idx].data[0] = argument_data[arg_idx].data[0] * 10 + arg_char - '0';
|
||||
format_ptr++;
|
||||
} // end argument while
|
||||
|
||||
// switch on command
|
||||
switch (format_ptr[1]) {
|
||||
// offset of 0x25
|
||||
|
||||
case '%': // newline
|
||||
*output_ptr = '\n';
|
||||
output_ptr++;
|
||||
// indent the next line if there is one
|
||||
if (indentation && format_ptr[2]) {
|
||||
for (u32 i = 0; i < indentation; i++) {
|
||||
*output_ptr = ' ';
|
||||
output_ptr++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '~': // tilde escape
|
||||
*output_ptr = '~';
|
||||
output_ptr++;
|
||||
break;
|
||||
|
||||
// pass through arguments
|
||||
case 'H': // 23 -> 48, H
|
||||
case 'J': // 25 -> 4A, J
|
||||
case 'K': // 26 -> 4B, K
|
||||
case 'L': // 27 -> 4C, L
|
||||
case 'N': // 29 -> 4E, N
|
||||
case 'V': // 31 -> 56, V
|
||||
case 'W': // 32 -> 57, W
|
||||
case 'Y': // 34 -> 59, Y
|
||||
case 'Z': // 35 -> 5A, Z
|
||||
case 'h':
|
||||
case 'j':
|
||||
case 'k':
|
||||
case 'l':
|
||||
case 'n':
|
||||
case 'v':
|
||||
case 'w':
|
||||
case 'y':
|
||||
case 'z':
|
||||
while (arg_start < format_ptr + 1) {
|
||||
*output_ptr = *arg_start;
|
||||
arg_start++;
|
||||
output_ptr++;
|
||||
}
|
||||
*output_ptr = format_ptr[1];
|
||||
output_ptr++;
|
||||
break;
|
||||
|
||||
case 'G': // like %s, prints a C string
|
||||
case 'g': {
|
||||
*output_ptr = 0;
|
||||
u32 in = arg_regs[arg_reg_idx++];
|
||||
kstrcat(output_ptr, Ptr<char>(in).c());
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'O':
|
||||
case 'o': {
|
||||
*output_ptr = '~';
|
||||
output_ptr++;
|
||||
kitoa(output_ptr, arg_regs[arg_reg_idx++], 10, 0, ' ', 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
*output_ptr = 'u';
|
||||
output_ptr++;
|
||||
} break;
|
||||
|
||||
case 'A': // print a boxed object
|
||||
case 'a': // pad,padchar (like ) ~8,'0A
|
||||
{
|
||||
s8 arg0 = argument_data[0].data[0];
|
||||
s32 desired_length = arg0;
|
||||
*output_ptr = 0;
|
||||
u32 in = arg_regs[arg_reg_idx++];
|
||||
jak3::print_object(in);
|
||||
if (desired_length != -1) {
|
||||
s32 print_len = strlen(output_ptr);
|
||||
if (desired_length < print_len) {
|
||||
// too long!
|
||||
if (desired_length > 1) { // mark with tilde that we will truncate
|
||||
output_ptr[desired_length - 1] = '~';
|
||||
}
|
||||
output_ptr[desired_length] = 0; // and truncate
|
||||
} else if (print_len < desired_length) {
|
||||
// too short
|
||||
if (justify == 0) {
|
||||
char pad = ' ';
|
||||
if (argument_data[1].data[0] != -1) {
|
||||
pad = argument_data[1].data[0];
|
||||
}
|
||||
kstrinsert(output_ptr, pad, desired_length - print_len);
|
||||
} else {
|
||||
ASSERT(false);
|
||||
// output_ptr = strend(output_ptr);
|
||||
// while(0 < (desired_length - print_len)) {
|
||||
// char pad = ' ';
|
||||
// if(argument_data[0].data[1] != -1) {
|
||||
// pad = argument_data[0].data[1];
|
||||
// }
|
||||
// output_ptr[0] = pad;
|
||||
// output_ptr++;
|
||||
//
|
||||
// }
|
||||
// *output_ptr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
output_ptr = strend(output_ptr);
|
||||
|
||||
} break;
|
||||
|
||||
case 'S': // like A, but strings are printed without quotes
|
||||
case 's': {
|
||||
s8 arg0 = argument_data[0].data[0];
|
||||
s32 desired_length = arg0;
|
||||
*output_ptr = 0;
|
||||
u32 in = arg_regs[arg_reg_idx++];
|
||||
|
||||
// if it's a string
|
||||
if (((in & 0x7) == 0x4) && *Ptr<u32>(in - 4) == *(s7 + FIX_SYM_STRING_TYPE - 1)) {
|
||||
cprintf("%s", Ptr<char>(in).c() + 4);
|
||||
} else {
|
||||
jak3::print_object(in);
|
||||
}
|
||||
|
||||
if (desired_length != -1) {
|
||||
s32 print_len = strlen(output_ptr);
|
||||
if (desired_length < print_len) {
|
||||
// too long!
|
||||
if (desired_length > 1) { // mark with tilde that we will truncate
|
||||
output_ptr[desired_length - 1] = '~';
|
||||
}
|
||||
output_ptr[desired_length] = 0; // and truncate
|
||||
} else if (print_len < desired_length) {
|
||||
// too short
|
||||
if (justify == 0) {
|
||||
char pad = ' ';
|
||||
if (argument_data[1].data[0] != -1) {
|
||||
pad = argument_data[1].data[0];
|
||||
}
|
||||
kstrinsert(output_ptr, pad, desired_length - print_len);
|
||||
|
||||
} else {
|
||||
ASSERT(false);
|
||||
// output_ptr = strend(output_ptr);
|
||||
// u32 l140 = 0;
|
||||
// while(l140 < (desired_length - print_len)) {
|
||||
// char* l108 = output_ptr;
|
||||
//
|
||||
// char pad = ' ';
|
||||
// if(argument_data[0].data[1] != -1) {
|
||||
// pad = argument_data[0].data[1];
|
||||
// }
|
||||
// output_ptr[0] = pad;
|
||||
// output_ptr++;
|
||||
// }
|
||||
// *output_ptr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'C': // character
|
||||
case 'c':
|
||||
*output_ptr = arg_regs[arg_reg_idx++];
|
||||
output_ptr++;
|
||||
break;
|
||||
|
||||
case 'P': // like ~A, but can specify type explicitly
|
||||
case 'p': {
|
||||
*output_ptr = 0;
|
||||
s8 arg0 = argument_data[0].data[0];
|
||||
u64 in = arg_regs[arg_reg_idx++];
|
||||
if (arg0 == -1) {
|
||||
jak3::print_object(in);
|
||||
} else {
|
||||
auto sym = jak3::find_symbol_from_c(-1, argument_data[0].data);
|
||||
if (sym.offset) {
|
||||
Ptr<Type> type(sym->value());
|
||||
if (type.offset) {
|
||||
call_method_of_type(in, type, GOAL_PRINT_METHOD);
|
||||
}
|
||||
} else {
|
||||
ASSERT(false); // bad type.
|
||||
}
|
||||
}
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'I': // like ~P, but calls inpsect
|
||||
case 'i': {
|
||||
*output_ptr = 0;
|
||||
s8 arg0 = argument_data[0].data[0];
|
||||
u64 in = arg_regs[arg_reg_idx++];
|
||||
if (arg0 == -1) {
|
||||
inspect_object(in);
|
||||
} else {
|
||||
auto sym = find_symbol_from_c(-1, argument_data[0].data);
|
||||
if (sym.offset) {
|
||||
Ptr<Type> type(sym->value());
|
||||
if (type.offset) {
|
||||
call_method_of_type(in, type, GOAL_INSPECT_METHOD);
|
||||
}
|
||||
} else {
|
||||
ASSERT(false); // bad type
|
||||
}
|
||||
}
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'Q': // not yet implemented. hopefully andy gavin finishes this one soon.
|
||||
case 'q':
|
||||
ASSERT(false);
|
||||
break;
|
||||
|
||||
case 'X': // hex, 64 bit, pad padchar
|
||||
case 'x': {
|
||||
char pad = '0';
|
||||
if (argument_data[1].data[0] != -1) {
|
||||
pad = argument_data[1].data[0];
|
||||
}
|
||||
u64 in = arg_regs[arg_reg_idx++];
|
||||
kitoa(output_ptr, in, 16, argument_data[0].data[0], pad, 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'D': // integer 64, pad padchar
|
||||
case 'd': {
|
||||
char pad = ' ';
|
||||
if (argument_data[1].data[0] != -1) {
|
||||
pad = argument_data[1].data[0];
|
||||
}
|
||||
u64 in = arg_regs[arg_reg_idx++];
|
||||
kitoa(output_ptr, in, 10, argument_data[0].data[0], pad, 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'B': // integer 64, pad padchar
|
||||
case 'b': {
|
||||
char pad = '0';
|
||||
if (argument_data[1].data[0] != -1) {
|
||||
pad = argument_data[1].data[0];
|
||||
}
|
||||
u64 in = arg_regs[arg_reg_idx++];
|
||||
kitoa(output_ptr, in, 2, argument_data[0].data[0], pad, 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'F': // float 12 pad, 4 precision
|
||||
{
|
||||
float in = *(float*)&arg_regs[arg_reg_idx++];
|
||||
ftoa(output_ptr, in, 0xc, ' ', 4, 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'f': // float with args
|
||||
{
|
||||
float in = *(float*)&arg_regs[arg_reg_idx++];
|
||||
s8 pad_length = argument_data[0].data[0];
|
||||
s8 pad_char = argument_data[1].data[0];
|
||||
if (pad_char == -1)
|
||||
pad_char = ' ';
|
||||
s8 precision = argument_data[2].data[0];
|
||||
if (precision == -1)
|
||||
precision = 4;
|
||||
ftoa(output_ptr, in, pad_length, pad_char, precision, 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'R': // rotation degrees
|
||||
case 'r': {
|
||||
float in = *(float*)&arg_regs[arg_reg_idx++];
|
||||
s8 pad_length = argument_data[0].data[0];
|
||||
s8 pad_char = argument_data[1].data[0];
|
||||
if (pad_char == -1)
|
||||
pad_char = ' ';
|
||||
s8 precision = argument_data[2].data[0];
|
||||
if (precision == -1)
|
||||
precision = 4;
|
||||
ftoa(output_ptr, in * 360.f / 65536.f, pad_length, pad_char, precision, 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'M': // distance meters
|
||||
case 'm': {
|
||||
float in = *(float*)&arg_regs[arg_reg_idx++];
|
||||
s8 pad_length = argument_data[0].data[0];
|
||||
s8 pad_char = argument_data[1].data[0];
|
||||
if (pad_char == -1)
|
||||
pad_char = ' ';
|
||||
s8 precision = argument_data[2].data[0];
|
||||
if (precision == -1)
|
||||
precision = 4;
|
||||
ftoa(output_ptr, in / 4096.f, pad_length, pad_char, precision, 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'E': // time seconds
|
||||
case 'e': {
|
||||
s64 in = arg_regs[arg_reg_idx++];
|
||||
s8 pad_length = argument_data[0].data[0];
|
||||
s8 pad_char = argument_data[0].data[1];
|
||||
if (pad_char == -1)
|
||||
pad_char = ' ';
|
||||
s8 precision = argument_data[0].data[2];
|
||||
if (precision == -1)
|
||||
precision = 4;
|
||||
float value;
|
||||
if (in < 0) {
|
||||
ASSERT(false); // i don't get this one
|
||||
} else {
|
||||
value = in;
|
||||
}
|
||||
ftoa(output_ptr, value / 300.f, pad_length, pad_char, precision, 0);
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
case 'T':
|
||||
case 't': {
|
||||
sprintf(output_ptr, "\t");
|
||||
output_ptr = strend(output_ptr);
|
||||
} break;
|
||||
|
||||
default:
|
||||
MsgErr("format: unknown code 0x%02x\n", format_ptr[1]);
|
||||
MsgErr("input was %s\n", format_cstring);
|
||||
// ASSERT(false);
|
||||
goto copy_char_hack;
|
||||
break;
|
||||
}
|
||||
format_ptr++;
|
||||
} else {
|
||||
// got normal char, just copy it
|
||||
copy_char_hack: // we goto here if we get a bad code for ~, which sort of backtracks and falls
|
||||
// back to regular character copying
|
||||
*output_ptr = *format_ptr;
|
||||
output_ptr++;
|
||||
}
|
||||
format_ptr++;
|
||||
} // end format string while
|
||||
|
||||
// end
|
||||
*output_ptr = 0;
|
||||
output_ptr++;
|
||||
|
||||
if (original_dest == s7.offset + FIX_SYM_TRUE) {
|
||||
// #t means to put it in the print buffer
|
||||
|
||||
// change for Jak 2: if we are disk-booting and do a (format #t, immediately flush to stdout.
|
||||
// we'd get these eventually in ClearPending, but for some reason they flush these here.
|
||||
// This is nicer because we may crash in between here and flushing the print buffer.
|
||||
if (DiskBoot) {
|
||||
// however, we are going to disable it anyway because it spams the console and is annoying
|
||||
if (false) {
|
||||
printf("%s", PrintPendingLocal3);
|
||||
fflush(stdout);
|
||||
}
|
||||
PrintPending = make_ptr(PrintPendingLocal2).cast<u8>();
|
||||
// if we don't comment this line, our output gets cleared
|
||||
// *PrintPendingLocal3 = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else if (original_dest == s7.offset + FIX_SYM_FALSE) {
|
||||
// #f means print to new string
|
||||
u32 string = make_string_from_c(PrintPendingLocal3);
|
||||
PrintPending = make_ptr(PrintPendingLocal2).cast<u8>();
|
||||
*PrintPendingLocal3 = 0;
|
||||
return string;
|
||||
} else if (original_dest == 0) {
|
||||
printf("%s", PrintPendingLocal3);
|
||||
fflush(stdout);
|
||||
PrintPending = make_ptr(PrintPendingLocal2).cast<u8>();
|
||||
*PrintPendingLocal3 = 0;
|
||||
return 0;
|
||||
} else {
|
||||
if ((original_dest & OFFSET_MASK) == BASIC_OFFSET) {
|
||||
Ptr<Type> type = *Ptr<Ptr<Type>>(original_dest - 4);
|
||||
if (type == *Ptr<Ptr<Type>>(s7.offset + FIX_SYM_STRING_TYPE - 1)) {
|
||||
u32 len = *Ptr<u32>(original_dest);
|
||||
char* str = Ptr<char>(original_dest + 4).c();
|
||||
kstrncat(str, PrintPendingLocal3, len);
|
||||
PrintPending = make_ptr(PrintPendingLocal2).cast<u8>();
|
||||
*PrintPendingLocal3 = 0;
|
||||
return 0;
|
||||
} else if (type == *Ptr<Ptr<Type>>(s7.offset + FIX_SYM_FILE_STREAM - 1)) {
|
||||
size_t len = strlen(PrintPendingLocal3);
|
||||
// sceWrite
|
||||
ee::sceWrite(*Ptr<s32>(original_dest + 12), PrintPendingLocal3, len);
|
||||
|
||||
PrintPending = make_ptr(PrintPendingLocal2).cast<u8>();
|
||||
*PrintPendingLocal3 = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ASSERT(false); // unknown destination
|
||||
return 0;
|
||||
}
|
||||
|
||||
ASSERT(false); // ??????
|
||||
return 7;
|
||||
}
|
12
game/kernel/jak3/kprint.h
Normal file
12
game/kernel/jak3/kprint.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void output_sql_query(char* query_name);
|
||||
}
|
||||
|
||||
// todo, do we actually have to do this, now that we aren't calling it from asm?
|
||||
extern "C" {
|
||||
s32 format_impl_jak3(uint64_t* args);
|
||||
}
|
1986
game/kernel/jak3/kscheme.cpp
Normal file
1986
game/kernel/jak3/kscheme.cpp
Normal file
File diff suppressed because it is too large
Load Diff
74
game/kernel/jak3/kscheme.h
Normal file
74
game/kernel/jak3/kscheme.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/Symbol4.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
#include "game/kernel/common/kscheme.h"
|
||||
|
||||
namespace jak3 {
|
||||
void kscheme_init_globals();
|
||||
constexpr s32 SYMBOL_OFFSET = 1;
|
||||
extern Ptr<u32> SymbolString;
|
||||
extern bool DebugSymbols;
|
||||
|
||||
/*!
|
||||
* GOAL Type
|
||||
*/
|
||||
struct Type {
|
||||
Ptr<Symbol4<Ptr<Type>>> symbol; //! The type's symbol 0x0
|
||||
Ptr<Type> parent; //! The type's parent 0x4
|
||||
u16 allocated_size; //! The type's size in memory 0x8
|
||||
u16 padded_size; //! The type's size, when padded? 0xa
|
||||
|
||||
u16 heap_base; //! relative location of heap 0xc
|
||||
u16 num_methods; //! allocated-length field 0xe - 0xf
|
||||
|
||||
Ptr<Function> new_method; // 16 0
|
||||
Ptr<Function> delete_method; // 20 1
|
||||
Ptr<Function> print_method; // 24 2
|
||||
Ptr<Function> inspect_method; // 28 3
|
||||
Ptr<Function> length_method; // 32 4
|
||||
Ptr<Function> asize_of_method; // 36 5
|
||||
Ptr<Function> copy_method; // 40 6
|
||||
Ptr<Function> relocate_method; // 44 7
|
||||
Ptr<Function> memusage_method; // 48 8
|
||||
|
||||
Ptr<Function>& get_method(u32 i) {
|
||||
Ptr<Function>* f = &new_method;
|
||||
return f[i];
|
||||
}
|
||||
};
|
||||
|
||||
s64 load_and_link(const char* filename, char* decode_name, kheapinfo* heap, u32 flags);
|
||||
Ptr<Symbol4<u32>> intern_from_c(int sym_id, int flags, const char* name);
|
||||
u64 load(u32 /*file_name_in*/, u32 /*heap_in*/);
|
||||
u64 loadb(u32 /*file_name_in*/, u32 /*heap_in*/, u32 /*param3*/);
|
||||
u64 loadc(const char* /*file_name*/, kheapinfo* /*heap*/, u32 /*flags*/);
|
||||
u64 loado(u32 file_name_in, u32 heap_in);
|
||||
u64 unload(u32 name);
|
||||
Ptr<Function> make_function_symbol_from_c(const char* name, void* f);
|
||||
Ptr<Function> make_stack_arg_function_symbol_from_c(const char* name, void* f);
|
||||
u64 print_object(u32 obj);
|
||||
u64 inspect_object(u32 obj);
|
||||
Ptr<Symbol4<u32>> find_symbol_from_c(uint16_t sym_id, const char* name);
|
||||
u64 make_string_from_c(const char* c_str);
|
||||
u64 call_method_of_type(u32 arg, Ptr<Type> type, u32 method_id);
|
||||
u64 new_pair(u32 heap, u32 type, u32 car, u32 cdr);
|
||||
u64 call_goal_function_by_name(const char* name);
|
||||
Ptr<Type> intern_type_from_c(int a, int b, const char* name, u64 methods);
|
||||
int InitHeapAndSymbol();
|
||||
template <typename T>
|
||||
Ptr<Ptr<String>> sym_to_string_ptr(Ptr<Symbol4<T>> in) {
|
||||
return Ptr<Ptr<String>>(SymbolString.offset + in.offset - s7.offset);
|
||||
}
|
||||
template <typename T>
|
||||
Ptr<String> sym_to_string(Ptr<Symbol4<T>> in) {
|
||||
return *sym_to_string_ptr(in);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const char* sym_to_cstring(Ptr<Symbol4<T>> in) {
|
||||
return sym_to_string(in)->data();
|
||||
}
|
||||
|
||||
} // namespace jak3
|
3
game/kernel/jak3/ksocket.cpp
Normal file
3
game/kernel/jak3/ksocket.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
#include "ksocket.h"
|
3
game/kernel/jak3/ksocket.h
Normal file
3
game/kernel/jak3/ksocket.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {}
|
15
game/kernel/jak3/ksound.cpp
Normal file
15
game/kernel/jak3/ksound.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "ksound.h"
|
||||
|
||||
#include "game/kernel/common/kdgo.h"
|
||||
#include "game/kernel/jak3/kscheme.h"
|
||||
|
||||
namespace jak3 {
|
||||
/*!
|
||||
* Set up some functions which are somewhat related to sound.
|
||||
*/
|
||||
void InitSoundScheme() {
|
||||
make_function_symbol_from_c("rpc-busy?", (void*)RpcBusy);
|
||||
make_function_symbol_from_c("test-load-dgo-c", (void*)LoadDGOTest);
|
||||
make_stack_arg_function_symbol_from_c("rpc-call", (void*)RpcCall_wrapper);
|
||||
}
|
||||
} // namespace jak3
|
5
game/kernel/jak3/ksound.h
Normal file
5
game/kernel/jak3/ksound.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {
|
||||
void InitSoundScheme();
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
kboot
|
||||
---------
|
||||
usleep in KernelCheckAndDispatch
|
||||
|
||||
kmachine
|
||||
---------
|
||||
rewrite InitParms to not use std::string
|
||||
InitVideo
|
||||
InitMachine
|
||||
CPadGetData
|
||||
InstallHandler
|
||||
InstallDebugHandler
|
||||
dma_to_iop
|
||||
DecodeTime
|
||||
PutDisplayEnv
|
||||
|
||||
kscheme
|
||||
----------
|
||||
remove the test function
|
||||
add memory card stuff
|
||||
read_clock_code
|
||||
|
||||
klink
|
||||
-------
|
||||
v2 support
|
||||
|
||||
kmemcard
|
||||
---------
|
||||
all of it, basically.
|
@ -95,7 +95,6 @@ int main(int argc, char** argv) {
|
||||
bool verbose_logging = false;
|
||||
bool disable_avx2 = false;
|
||||
bool disable_display = false;
|
||||
bool enable_debug_vm = false;
|
||||
bool enable_profiling = false;
|
||||
std::string gpu_test = "";
|
||||
std::string gpu_test_out_path = "";
|
||||
@ -109,9 +108,8 @@ int main(int argc, char** argv) {
|
||||
app.add_flag(
|
||||
"--port", port_number,
|
||||
"Specify port number for listener connection (default is 8112 for Jak 1 and 8113 for Jak 2)");
|
||||
app.add_flag("--no-avx2", verbose_logging, "Disable AVX2 for testing");
|
||||
app.add_flag("--no-avx2", disable_avx2, "Disable AVX2 for testing");
|
||||
app.add_flag("--no-display", disable_display, "Disable video display");
|
||||
app.add_flag("--vm", enable_debug_vm, "Enable debug PS2 VM (defaulted to off)");
|
||||
app.add_flag("--profile", enable_profiling, "Enables profiling immediately from startup");
|
||||
app.add_option("--gpu-test", gpu_test,
|
||||
"Tests for minimum graphics requirements. Valid Options are: [opengl]");
|
||||
@ -146,7 +144,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Create struct with all non-kmachine handled args to pass to the runtime
|
||||
GameLaunchOptions game_options;
|
||||
game_options.disable_debug_vm = !enable_debug_vm;
|
||||
game_options.disable_display = disable_display;
|
||||
game_options.game_version = game_name_to_version(game_name);
|
||||
game_options.server_port =
|
||||
|
@ -189,7 +189,7 @@ VagCmd* SmartAllocVagCmd(VagCmd* cmd) {
|
||||
return selected;
|
||||
}
|
||||
|
||||
void TerminateVAG(VagCmd* cmd, int param_2) {
|
||||
void TerminateVAG(VagCmd* cmd, int /*param_2*/) {
|
||||
VagStrListNode vag_node;
|
||||
LfoListNode lfo_node;
|
||||
// undefined4 auStack32 [2];
|
||||
@ -386,7 +386,7 @@ void RestartVag(VagCmd* param_1, int param_2, int /*param_3*/) {
|
||||
//}
|
||||
}
|
||||
|
||||
void SetVAGVol(VagCmd* cmd, int param_2) {
|
||||
void SetVAGVol(VagCmd* cmd, int /*param_2*/) {
|
||||
VagCmd* stereo_cmd;
|
||||
u32 lvol, rvol;
|
||||
|
||||
|
@ -43,11 +43,17 @@
|
||||
#include "game/kernel/common/kprint.h"
|
||||
#include "game/kernel/common/kscheme.h"
|
||||
#include "game/kernel/jak1/kboot.h"
|
||||
#include "game/kernel/jak1/kdgo.h"
|
||||
#include "game/kernel/jak1/klisten.h"
|
||||
#include "game/kernel/jak1/kscheme.h"
|
||||
#include "game/kernel/jak2/kboot.h"
|
||||
#include "game/kernel/jak2/kdgo.h"
|
||||
#include "game/kernel/jak2/klisten.h"
|
||||
#include "game/kernel/jak2/kscheme.h"
|
||||
#include "game/kernel/jak3/kboot.h"
|
||||
#include "game/kernel/jak3/kdgo.h"
|
||||
#include "game/kernel/jak3/klisten.h"
|
||||
#include "game/kernel/jak3/kscheme.h"
|
||||
#include "game/overlord/common/fake_iso.h"
|
||||
#include "game/overlord/common/iso.h"
|
||||
#include "game/overlord/common/sbank.h"
|
||||
@ -74,8 +80,6 @@
|
||||
#include "game/overlord/jak2/vag.h"
|
||||
#include "game/system/Deci2Server.h"
|
||||
#include "game/system/iop_thread.h"
|
||||
#include "game/system/vm/dmac.h"
|
||||
#include "game/system/vm/vm.h"
|
||||
#include "sce/deci2.h"
|
||||
#include "sce/iop.h"
|
||||
#include "sce/libcdvd_ee.h"
|
||||
@ -183,21 +187,28 @@ void ee_runner(SystemThreadInterface& iface) {
|
||||
fileio_init_globals();
|
||||
jak1::kboot_init_globals();
|
||||
jak2::kboot_init_globals();
|
||||
jak3::kboot_init_globals();
|
||||
|
||||
kboot_init_globals_common();
|
||||
kdgo_init_globals();
|
||||
jak1::kdgo_init_globals();
|
||||
jak2::kdgo_init_globals();
|
||||
jak3::kdgo_init_globals();
|
||||
|
||||
kdsnetm_init_globals_common();
|
||||
klink_init_globals();
|
||||
|
||||
kmachine_init_globals_common();
|
||||
jak1::kscheme_init_globals();
|
||||
jak2::kscheme_init_globals();
|
||||
jak3::kscheme_init_globals();
|
||||
kscheme_init_globals_common();
|
||||
kmalloc_init_globals_common();
|
||||
|
||||
klisten_init_globals();
|
||||
jak1::klisten_init_globals();
|
||||
jak2::klisten_init_globals();
|
||||
jak3::klisten_init_globals();
|
||||
|
||||
jak2::vag_init_globals();
|
||||
|
||||
@ -216,6 +227,8 @@ void ee_runner(SystemThreadInterface& iface) {
|
||||
case GameVersion::Jak2:
|
||||
jak2::goal_main(g_argc, g_argv);
|
||||
break;
|
||||
case GameVersion::Jak3:
|
||||
jak3::goal_main(g_argc, g_argv);
|
||||
default:
|
||||
ASSERT_MSG(false, "Unsupported game version");
|
||||
}
|
||||
@ -309,6 +322,7 @@ void iop_runner(SystemThreadInterface& iface, GameVersion version) {
|
||||
jak1::start_overlord_wrapper(iop.overlord_argc, iop.overlord_argv, &complete);
|
||||
break;
|
||||
case GameVersion::Jak2:
|
||||
case GameVersion::Jak3: // TODO: jak3 using jak2's overlord.
|
||||
jak2::start_overlord_wrapper(iop.overlord_argc, iop.overlord_argv, &complete);
|
||||
break;
|
||||
default:
|
||||
@ -349,32 +363,6 @@ void null_runner(SystemThreadInterface& iface) {
|
||||
iface.initialization_complete();
|
||||
}
|
||||
|
||||
/*!
|
||||
* SystemThread function for running the PS2 DMA controller.
|
||||
* This does not actually emulate the DMAC, it only fakes its existence enough that we can debug
|
||||
* the DMA packets the original game sends. The port will replace all DMAC code.
|
||||
*/
|
||||
void dmac_runner(SystemThreadInterface& iface) {
|
||||
VM::subscribe_component();
|
||||
|
||||
VM::dmac_init_globals();
|
||||
|
||||
iface.initialization_complete();
|
||||
|
||||
while (!iface.get_want_exit() && !VM::vm_want_exit()) {
|
||||
// for (int i = 0; i < 10; ++i) {
|
||||
// if (VM::dmac_ch[i]->chcr.str) {
|
||||
// // lg::info("DMA detected on channel {}, clearing", i);
|
||||
// VM::dmac_ch[i]->chcr.str = 0;
|
||||
// }
|
||||
// }
|
||||
// avoid running the DMAC on full blast (this does not sync to its clockrate)
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(50000));
|
||||
}
|
||||
|
||||
VM::unsubscribe_component();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Main function to launch the runtime.
|
||||
* GOAL kernel arguments are currently ignored.
|
||||
@ -386,7 +374,6 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
|
||||
g_main_thread_id = std::this_thread::get_id();
|
||||
|
||||
bool enable_display = !game_options.disable_display;
|
||||
VM::use = !game_options.disable_debug_vm;
|
||||
g_game_version = game_options.game_version;
|
||||
g_server_port = game_options.server_port;
|
||||
|
||||
@ -417,7 +404,6 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
|
||||
|
||||
// step 2: system prep
|
||||
prof().begin_event("startup::exec_runtime::system_prep");
|
||||
VM::vm_prepare(); // our fake ps2 VM needs to be prepared
|
||||
SystemThreadManager tm;
|
||||
auto& deci_thread = tm.create_thread("DMP");
|
||||
auto& iop_thread = tm.create_thread("IOP");
|
||||
@ -425,7 +411,6 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
|
||||
// a general worker thread to perform background operations from the EE thread (to not block the
|
||||
// game)
|
||||
auto& ee_worker_thread = tm.create_thread("EE-Worker");
|
||||
auto& vm_dmac_thread = tm.create_thread("VM-DMAC");
|
||||
prof().end_event();
|
||||
|
||||
// step 3: start the EE!
|
||||
@ -445,10 +430,6 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
|
||||
auto p = scoped_prof("startup::exec_runtime::ee-start");
|
||||
ee_thread.start(ee_runner);
|
||||
}
|
||||
if (VM::use) {
|
||||
auto p = scoped_prof("startup::exec_runtime::dmac-start");
|
||||
vm_dmac_thread.start(dmac_runner);
|
||||
}
|
||||
|
||||
// step 4: wait for EE to signal a shutdown. meanwhile, run video loop on main thread.
|
||||
// TODO relegate this to its own function
|
||||
|
@ -27,7 +27,7 @@ void FileAttributes::Read(BinaryReader& data) {
|
||||
num_chunks = data.read<u32>();
|
||||
|
||||
where.resize(num_chunks);
|
||||
for (int i = 0; i < num_chunks; i++) {
|
||||
for (size_t i = 0; i < num_chunks; i++) {
|
||||
where[i].offset = data.read<u32>();
|
||||
where[i].size = data.read<u32>();
|
||||
}
|
||||
@ -215,16 +215,16 @@ SFXBlock* SFXBlock::ReadBlock(nonstd::span<u8> bank_data, nonstd::span<u8> sampl
|
||||
|
||||
s16 NumSounds = data.read<s16>();
|
||||
block->Sounds.resize(NumSounds);
|
||||
s16 NumGrains = data.read<s16>();
|
||||
s16 NumVAGs = data.read<s16>();
|
||||
[[maybe_unused]] s16 NumGrains = data.read<s16>();
|
||||
[[maybe_unused]] s16 NumVAGs = data.read<s16>();
|
||||
|
||||
u32 FirstSound = data.read<u32>();
|
||||
u32 FirstGrain = data.read<u32>();
|
||||
|
||||
u32 VagsInSR = data.read<u32>();
|
||||
u32 VagDataSize = data.read<u32>();
|
||||
u32 SRAMAllocSize = data.read<u32>();
|
||||
u32 NextBlock = data.read<u32>();
|
||||
[[maybe_unused]] u32 VagsInSR = data.read<u32>();
|
||||
[[maybe_unused]] u32 VagDataSize = data.read<u32>();
|
||||
[[maybe_unused]] u32 SRAMAllocSize = data.read<u32>();
|
||||
[[maybe_unused]] u32 NextBlock = data.read<u32>();
|
||||
u32 GrainData = 0;
|
||||
if (block->Version >= 2) {
|
||||
GrainData = data.read<u32>();
|
||||
@ -344,12 +344,12 @@ MusicBank* MusicBank::ReadBank(nonstd::span<u8> bank_data,
|
||||
bank->Sounds.resize(NumSounds);
|
||||
s16 NumProgs = data.read<s16>();
|
||||
bank->Progs.resize(NumProgs);
|
||||
s16 NumTones = data.read<s16>();
|
||||
s16 NumVAGs = data.read<s16>();
|
||||
[[maybe_unused]] s16 NumTones = data.read<s16>();
|
||||
[[maybe_unused]] s16 NumVAGs = data.read<s16>();
|
||||
|
||||
u32 FirstSound = data.read<u32>();
|
||||
u32 FirstProg = data.read<u32>();
|
||||
u32 FirstTone = data.read<u32>();
|
||||
[[maybe_unused]] u32 FirstTone = data.read<u32>();
|
||||
|
||||
// vagsinsr
|
||||
data.read<u32>();
|
||||
|
@ -377,8 +377,8 @@ InputBindingInfo::InputBindingInfo(const InputBinding bind,
|
||||
const bool _analog_button)
|
||||
: sdl_idx(sdl_code),
|
||||
pad_idx(bind.pad_data_index),
|
||||
modifiers(bind.modifiers),
|
||||
analog_button(_analog_button) {
|
||||
analog_button(_analog_button),
|
||||
modifiers(bind.modifiers) {
|
||||
switch (device_type) {
|
||||
case CONTROLLER:
|
||||
if (analog_button) {
|
||||
|
@ -1,32 +0,0 @@
|
||||
/*!
|
||||
* @file dmac.cpp
|
||||
* DMAC implementation for the "PS2 virtual machine".
|
||||
* Not meant to work as a full DMAC emulator, just enough to inspect DMA packets.
|
||||
*/
|
||||
|
||||
#include "dmac.h"
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
#include "game/runtime.h"
|
||||
|
||||
namespace VM {
|
||||
|
||||
Ptr<DmaCommonRegisters> dmac;
|
||||
Ptr<DmaChannelRegisters> dmac_ch[10];
|
||||
|
||||
void dmac_init_globals() {
|
||||
dmac = kmalloc(kdebugheap, sizeof(DmaCommonRegisters), KMALLOC_ALIGN_16 | KMALLOC_MEMSET, "dmac")
|
||||
.cast<DmaCommonRegisters>();
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
dmac_ch[i] =
|
||||
kmalloc(kdebugheap, sizeof(DmaChannelRegisters), KMALLOC_ALIGN_16 | KMALLOC_MEMSET, "dmach")
|
||||
.cast<DmaChannelRegisters>();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VM
|
@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* @file dmac.h
|
||||
* DMAC implementation for the "PS2 virtual machine".
|
||||
* Not meant to work as a full DMAC emulator, just enough to inspect DMA packets.
|
||||
*/
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
|
||||
namespace VM {
|
||||
|
||||
/*!
|
||||
* DMA channel CHCR register. The only one we care about right now.
|
||||
*/
|
||||
struct DmaChcr {
|
||||
u32 dir : 1;
|
||||
u32 _pad1 : 1;
|
||||
u32 mod : 2;
|
||||
u32 asp : 2;
|
||||
u32 tte : 1;
|
||||
u32 tie : 1;
|
||||
u32 str : 1;
|
||||
u32 _pad2 : 7;
|
||||
u16 tag : 16;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Layout of the DMA channel registers in EE memory. For simplicity's sake, all are included,
|
||||
* however, each channel may not actually have some of these registers.
|
||||
* They are 16-byte aligned.
|
||||
*/
|
||||
struct alignas(16) DmaChannelRegisters {
|
||||
alignas(16) DmaChcr chcr;
|
||||
alignas(16) u32 madr;
|
||||
alignas(16) u32 qwc;
|
||||
alignas(16) u32 tadr;
|
||||
alignas(16) u32 asr0;
|
||||
alignas(16) u32 asr1;
|
||||
alignas(16) u128 _pad1;
|
||||
alignas(16) u128 _pad2;
|
||||
alignas(16) u32 sadr;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Layout of the DMAC registers in EE memory.
|
||||
* They are 16-byte aligned.
|
||||
*/
|
||||
struct alignas(16) DmaCommonRegisters {
|
||||
alignas(16) u32 ctrl;
|
||||
alignas(16) u32 stat;
|
||||
alignas(16) u32 pcr;
|
||||
alignas(16) u32 sqwc;
|
||||
alignas(16) u32 rbsr;
|
||||
alignas(16) u32 rbor;
|
||||
alignas(16) u32 stadr;
|
||||
};
|
||||
|
||||
// pointer to DMAC registers
|
||||
extern Ptr<DmaCommonRegisters> dmac;
|
||||
// array of pointers to DMAC channels (they are not stored contiguously)
|
||||
extern Ptr<DmaChannelRegisters> dmac_ch[10];
|
||||
|
||||
// enum DmaChannel { VIF0, VIF1, GIF, fromIPU, toIPU, SIF0, SIF1, SIF2, fromSPR, toSPR };
|
||||
|
||||
static_assert(sizeof(DmaChannelRegisters) == 0x90, "DmaChannelRegisters wrong size");
|
||||
static_assert(alignof(DmaChannelRegisters) == 0x10, "DmaChannelRegisters unaligned");
|
||||
|
||||
void dmac_init_globals();
|
||||
|
||||
} // namespace VM
|
@ -1,140 +0,0 @@
|
||||
/*!
|
||||
* @file vm.cpp
|
||||
* Base "PS2 virtual machine" code.
|
||||
* Simulates the existence of select PS2 components, for inspection & debugging.
|
||||
* Not an emulator!
|
||||
*/
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "dmac.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
namespace VM {
|
||||
|
||||
bool use = true; // enable VM by default, since we're debugging right now
|
||||
|
||||
namespace {
|
||||
Status status;
|
||||
std::condition_variable vm_init_cv;
|
||||
std::condition_variable vm_dead_cv;
|
||||
std::mutex status_mutex;
|
||||
|
||||
int components = 0;
|
||||
} // namespace
|
||||
|
||||
static void vm_change_status(Status new_status) {
|
||||
std::unique_lock<std::mutex> lk(status_mutex);
|
||||
status = new_status;
|
||||
}
|
||||
|
||||
void wait_vm_init() {
|
||||
std::unique_lock<std::mutex> lk(status_mutex);
|
||||
|
||||
vm_init_cv.wait(lk, [&] { return status == Status::Inited; });
|
||||
}
|
||||
|
||||
void wait_vm_dead() {
|
||||
if (status != Status::Kill && status != Status::Dead) {
|
||||
lg::warn("[VM] Dying without being killed! There are {} component(s) running", components);
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lk(status_mutex);
|
||||
|
||||
vm_dead_cv.wait(lk, [&] { return components == 0; });
|
||||
}
|
||||
|
||||
bool vm_want_exit() {
|
||||
return status == Status::Kill || status == Status::Dead;
|
||||
}
|
||||
|
||||
void vm_prepare() {
|
||||
lg::debug("[VM] Preparing...");
|
||||
vm_change_status(Status::Uninited);
|
||||
lg::debug("[VM] Prepared");
|
||||
}
|
||||
|
||||
void vm_init() {
|
||||
if (status != Status::Uninited) {
|
||||
lg::warn("[VM] unexpected status {}", fmt::underlying(status));
|
||||
}
|
||||
|
||||
lg::debug("[VM] Inited");
|
||||
vm_change_status(Status::Inited);
|
||||
vm_init_cv.notify_all();
|
||||
}
|
||||
|
||||
void vm_kill() {
|
||||
lg::debug("[VM] Killing");
|
||||
|
||||
vm_change_status(Status::Kill);
|
||||
|
||||
// stall caller until VM is done dying
|
||||
wait_vm_dead();
|
||||
|
||||
vm_change_status(Status::Dead);
|
||||
}
|
||||
|
||||
void subscribe_component() {
|
||||
if (status == Status::Dead) {
|
||||
throw std::runtime_error("[VM] Cannot add new components when VM is dead!");
|
||||
}
|
||||
|
||||
status_mutex.lock();
|
||||
++components;
|
||||
status_mutex.unlock();
|
||||
|
||||
// stall component until VM is ready
|
||||
if (status == Status::Uninited) {
|
||||
wait_vm_init();
|
||||
}
|
||||
}
|
||||
|
||||
void unsubscribe_component() {
|
||||
status_mutex.lock();
|
||||
--components;
|
||||
status_mutex.unlock();
|
||||
vm_dead_cv.notify_all();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Return the GOAL pointer to a specified PS2 VM component based on the EE address.
|
||||
*/
|
||||
u64 get_vm_ptr(u32 ptr) {
|
||||
// currently, only DMAC and DMA channel banks are implemented. add more as necessary.
|
||||
if (ptr == 0x10008000) {
|
||||
return VM::dmac_ch[0].offset;
|
||||
} else if (ptr == 0x10009000) {
|
||||
return VM::dmac_ch[1].offset;
|
||||
} else if (ptr == 0x1000a000) {
|
||||
return VM::dmac_ch[2].offset;
|
||||
} else if (ptr == 0x1000b000) {
|
||||
return VM::dmac_ch[3].offset;
|
||||
} else if (ptr == 0x1000b400) {
|
||||
return VM::dmac_ch[4].offset;
|
||||
} else if (ptr == 0x1000c000) {
|
||||
return VM::dmac_ch[5].offset;
|
||||
} else if (ptr == 0x1000c400) {
|
||||
return VM::dmac_ch[6].offset;
|
||||
} else if (ptr == 0x1000c800) {
|
||||
return VM::dmac_ch[7].offset;
|
||||
} else if (ptr == 0x1000d000) {
|
||||
return VM::dmac_ch[8].offset;
|
||||
} else if (ptr == 0x1000d400) {
|
||||
return VM::dmac_ch[9].offset;
|
||||
} else if (ptr == 0x1000e000) {
|
||||
return VM::dmac.offset;
|
||||
} else {
|
||||
// return zero, using this result will segfault GOAL!
|
||||
// we could die immediately, but it might be worth it to keep going just on the off chance more
|
||||
// errors are reported, and not just only this one.
|
||||
lg::error("unknown EE register for VM at #x{:08x}", ptr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VM
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* @file vm.h
|
||||
* Base "PS2 virtual machine" code.
|
||||
* Simulates the existence of select PS2 components, for inspection & debugging.
|
||||
* Not an emulator!
|
||||
*/
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VM {
|
||||
|
||||
extern bool use;
|
||||
|
||||
enum class Status { Disabled, Uninited, Inited, Kill, Dead };
|
||||
|
||||
void wait_vm_init();
|
||||
void wait_vm_dead();
|
||||
|
||||
bool vm_want_exit();
|
||||
|
||||
void vm_prepare();
|
||||
void vm_init();
|
||||
void vm_kill();
|
||||
|
||||
void subscribe_component();
|
||||
void unsubscribe_component();
|
||||
|
||||
u64 get_vm_ptr(u32 ptr);
|
||||
|
||||
} // namespace VM
|
@ -34,4 +34,4 @@
|
||||
((basic? ,obj) (-> (the basic ,obj) type))
|
||||
(else symbol)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -127,6 +127,8 @@
|
||||
(define-extern kernel-shutdown (function none))
|
||||
(define-extern *boot-video-mode* "Defined in the kernel" int)
|
||||
(define-extern *kernel-boot-message* symbol)
|
||||
(define-extern *kernel-symbol-warnings* symbol)
|
||||
(define-extern symbol->string (function symbol string))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; PC Port functions
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,3 +7,615 @@
|
||||
|
||||
;; DECOMP BEGINS
|
||||
|
||||
(defconstant *kernel-major-version* 2)
|
||||
(defconstant *kernel-minor-version* 0)
|
||||
|
||||
(defconstant DPROCESS_STACK_SIZE (#if PC_PORT #x8000 #x3800))
|
||||
(defconstant PROCESS_STACK_SIZE (#if PC_PORT #x4000 #x1c00))
|
||||
|
||||
;; the size of the shared heap used by dynamically sized processes
|
||||
(#if PC_BIG_MEMORY
|
||||
(defconstant PROCESS_HEAP_MULT 4) ;; 4x actors
|
||||
(defconstant PROCESS_HEAP_MULT 1)
|
||||
)
|
||||
(defconstant PROCESS_HEAP_SIZE (* PROCESS_HEAP_MULT 1540 1024))
|
||||
(defconstant PROCESS_HEAP_MAX (* PROCESS_HEAP_MULT 768))
|
||||
|
||||
(defconstant *tab-size* (the binteger 8))
|
||||
(defconstant *gtype-basic-offset* 4)
|
||||
|
||||
;; if set, will attempt to detect memory corruption and stack overflow bugs
|
||||
;; to some extent.
|
||||
(defglobalconstant KERNEL_DEBUG #t)
|
||||
|
||||
(defconstant *scratch-memory-top* (the pointer #x70004000))
|
||||
|
||||
(defenum process-mask
|
||||
:type uint32
|
||||
:bitfield #t
|
||||
(execute 0) ;; 1
|
||||
(freeze 1)
|
||||
(pause 2)
|
||||
(menu 3)
|
||||
(progress 4)
|
||||
(actor-pause 5) ;; 32
|
||||
(sleep 6) ;; 64
|
||||
(sleep-code 7) ;; 128
|
||||
(process-tree 8) ;; 256
|
||||
(heap-shrunk 9) ;; 512
|
||||
(going 10) ;; 1024
|
||||
(kernel-run 11) ;; 2048
|
||||
(no-kill 12) ;; 4096
|
||||
(movie 13) ;; 8192
|
||||
(dark-effect 14) ;; 0x4000
|
||||
(target 15) ;; 0x8000
|
||||
|
||||
(sidekick 16) ;; 0x1'0000
|
||||
(crate 17) ;; 0x2'0000
|
||||
(collectable 18) ;; 0x4'0000
|
||||
(enemy 19) ;; 0x8'0000
|
||||
(camera 20) ;; 0x10'0000
|
||||
(platform 21) ;; 0x20'0000
|
||||
(ambient 22) ;; 0x40'0000
|
||||
(entity 23) ;; 0x80'0000
|
||||
(projectile 24) ;; 0x100'0000
|
||||
(bot 25) ;; 0x200'0000
|
||||
(death 26) ;; 0x400'0000
|
||||
(guard 27) ;; 0x800'0000
|
||||
(vehicle 28) ;; 0x1000'0000
|
||||
(civilian 29) ;; 0x2000'0000
|
||||
(kg-robot 30) ;; 0x4000'0000
|
||||
(metalhead 31) ;; 0x8000'0000
|
||||
)
|
||||
|
||||
;; forward declarations
|
||||
(declare-type process-tree basic)
|
||||
(declare-type process process-tree)
|
||||
(declare-type res-lump basic)
|
||||
(declare-type entity res-lump)
|
||||
(declare-type entity-actor entity)
|
||||
(declare-type dead-pool basic)
|
||||
(declare-type level basic)
|
||||
(declare-type state basic)
|
||||
(declare-type event-message-block structure)
|
||||
(declare-type stack-frame basic)
|
||||
(declare-type thread basic)
|
||||
(declare-type cpu-thread thread)
|
||||
|
||||
(deftype kernel-context (basic)
|
||||
((prevent-from-run process-mask)
|
||||
(require-for-run process-mask)
|
||||
(allow-to-run process-mask)
|
||||
(next-pid int32)
|
||||
(fast-stack-top pointer)
|
||||
(current-process process)
|
||||
(relocating-process basic)
|
||||
(relocating-min int32)
|
||||
(relocating-max int32)
|
||||
(relocating-offset int32)
|
||||
(relocating-level level)
|
||||
(low-memory-message symbol)
|
||||
(login-object basic)
|
||||
(login-art-group basic)
|
||||
(login-level-index int32)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype time-frame (int64)
|
||||
()
|
||||
)
|
||||
|
||||
;; times are stored in 300ths of a second.
|
||||
;; this divides evenly into frames at both 50 and 60 fps.
|
||||
;; typically these are stored as integers as more precision is not useful.
|
||||
;; an unsigned 32-bit integer can store about 150 days
|
||||
(defglobalconstant TICKS_PER_SECOND 300) ;; 5 t/frame @ 60fps, 6 t/frame @ 50fps
|
||||
|
||||
;; this was usec in GOAL
|
||||
(defmacro seconds (x)
|
||||
"Convert number to seconds unit.
|
||||
Returns uint."
|
||||
(cond
|
||||
((integer? x)
|
||||
(* TICKS_PER_SECOND x)
|
||||
)
|
||||
((float? x)
|
||||
(* 1 (* 1.0 x TICKS_PER_SECOND))
|
||||
)
|
||||
(#t
|
||||
`(the uint (* TICKS_PER_SECOND ,x))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
(deftype clock (basic)
|
||||
((index int16)
|
||||
(ref-count uint16)
|
||||
(mask process-mask)
|
||||
(clock-ratio float)
|
||||
(accum float)
|
||||
(integral-accum float)
|
||||
(frame-counter uint64)
|
||||
(old-frame-counter uint64)
|
||||
(integral-frame-counter uint64)
|
||||
(old-integral-frame-counter uint64)
|
||||
(sparticle-data vector :inline)
|
||||
(seconds-per-frame float)
|
||||
(frames-per-second float)
|
||||
(time-adjust-ratio float)
|
||||
)
|
||||
(:methods
|
||||
(new (symbol type int) _type_)
|
||||
(update-rates! (_type_ float) float)
|
||||
(clock-method-10 () none)
|
||||
(clock-method-11 () none)
|
||||
(clock-method-12 () none)
|
||||
(clock-method-13 () none)
|
||||
(clock-method-14 () none)
|
||||
(clock-method-15 () none)
|
||||
(frame-mask-2 (_type_ int) symbol)
|
||||
(frame-mask-4 (_type_ int) symbol)
|
||||
(frame-mask-8 (_type_ int) symbol)
|
||||
(frame-mask-16 (_type_ int) symbol)
|
||||
(frame-mask-32 (_type_ int) symbol)
|
||||
(frame-mask-64 (_type_ int) symbol)
|
||||
(frame-mask-128 (_type_ int) symbol)
|
||||
(frame-mask-256 (_type_ int) symbol)
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod frame-mask-2 ((this clock) (arg0 int))
|
||||
(not (logtest? (logxor arg0 (the-as int (-> this integral-frame-counter))) 1))
|
||||
)
|
||||
|
||||
(defmethod frame-mask-4 ((this clock) (arg0 int))
|
||||
(not (logtest? (logxor arg0 (the-as int (-> this integral-frame-counter))) 3))
|
||||
)
|
||||
|
||||
(defmethod frame-mask-8 ((this clock) (arg0 int))
|
||||
(not (logtest? (logxor arg0 (the-as int (-> this integral-frame-counter))) 7))
|
||||
)
|
||||
|
||||
(defmethod frame-mask-16 ((this clock) (arg0 int))
|
||||
(not (logtest? (logxor arg0 (the-as int (-> this integral-frame-counter))) 15))
|
||||
)
|
||||
|
||||
(defmethod frame-mask-32 ((this clock) (arg0 int))
|
||||
(not (logtest? (logxor arg0 (the-as int (-> this integral-frame-counter))) 31))
|
||||
)
|
||||
|
||||
(defmethod frame-mask-64 ((this clock) (arg0 int))
|
||||
(not (logtest? (logxor arg0 (the-as int (-> this integral-frame-counter))) 63))
|
||||
)
|
||||
|
||||
(defmethod frame-mask-128 ((this clock) (arg0 int))
|
||||
(not (logtest? (logxor arg0 (the-as int (-> this integral-frame-counter))) 127))
|
||||
)
|
||||
|
||||
(defmethod frame-mask-256 ((this clock) (arg0 int))
|
||||
(not (logtest? (logxor arg0 (the-as int (-> this integral-frame-counter))) 255))
|
||||
)
|
||||
|
||||
(defmethod new clock ((allocation symbol) (type-to-make type) (arg0 int))
|
||||
(let ((gp-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
|
||||
(set! (-> gp-0 index) arg0)
|
||||
(set! (-> gp-0 frame-counter) (the-as uint #x493e0))
|
||||
(set! (-> gp-0 integral-frame-counter) (the-as uint #x493e0))
|
||||
(set! (-> gp-0 old-frame-counter) (+ (-> gp-0 frame-counter) -1))
|
||||
(set! (-> gp-0 old-integral-frame-counter) (+ (-> gp-0 integral-frame-counter) -1))
|
||||
(update-rates! gp-0 1.0)
|
||||
gp-0
|
||||
)
|
||||
)
|
||||
|
||||
(deftype process-tree (basic)
|
||||
((name string)
|
||||
(mask process-mask)
|
||||
(clock clock)
|
||||
(parent (pointer process-tree))
|
||||
(brother (pointer process-tree))
|
||||
(child (pointer process-tree))
|
||||
(ppointer (pointer process))
|
||||
(self process-tree)
|
||||
)
|
||||
(:methods
|
||||
(new (symbol type string) _type_)
|
||||
(activate (_type_ process-tree basic pointer) process-tree)
|
||||
(deactivate (_type_) none)
|
||||
(init-from-entity! (_type_ entity-actor) none) ;; todo check
|
||||
(run-logic? (_type_) symbol)
|
||||
(process-tree-method-13 () none)
|
||||
)
|
||||
:no-runtime-type
|
||||
)
|
||||
|
||||
(deftype thread (basic)
|
||||
((name symbol)
|
||||
(process process)
|
||||
(previous thread)
|
||||
(suspend-hook (function cpu-thread none))
|
||||
(resume-hook (function cpu-thread none))
|
||||
(pc pointer)
|
||||
(sp pointer)
|
||||
(stack-top pointer)
|
||||
(stack-size int32)
|
||||
)
|
||||
(:methods
|
||||
(thread-method-9 () none)
|
||||
(thread-method-10 () none)
|
||||
(thread-method-11 () none)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype cpu-thread (thread)
|
||||
((rreg uint64 7)
|
||||
(freg float 8)
|
||||
(stack uint8 :dynamic)
|
||||
)
|
||||
(:methods
|
||||
(new (symbol type process symbol int pointer) _type_)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype dead-pool (process-tree)
|
||||
()
|
||||
(:methods
|
||||
(dead-pool-method-14 () none)
|
||||
(dead-pool-method-15 () none)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype dead-pool-heap-rec (structure)
|
||||
((process process)
|
||||
(prev dead-pool-heap-rec)
|
||||
(next dead-pool-heap-rec)
|
||||
)
|
||||
:pack-me
|
||||
)
|
||||
|
||||
(deftype dead-pool-heap (dead-pool)
|
||||
((allocated-length int32)
|
||||
(compact-time uint32)
|
||||
(compact-count-targ uint32)
|
||||
(compact-count uint32)
|
||||
(fill-percent float)
|
||||
(first-gap dead-pool-heap-rec)
|
||||
(first-shrink dead-pool-heap-rec)
|
||||
(heap kheap :inline)
|
||||
(alive-list dead-pool-heap-rec :inline)
|
||||
(last dead-pool-heap-rec :overlay-at (-> alive-list prev))
|
||||
(dead-list dead-pool-heap-rec :inline)
|
||||
(process-list dead-pool-heap-rec :dynamic)
|
||||
)
|
||||
(:methods
|
||||
(dead-pool-heap-method-16 () none)
|
||||
(dead-pool-heap-method-17 () none)
|
||||
(dead-pool-heap-method-18 () none)
|
||||
(dead-pool-heap-method-19 () none)
|
||||
(dead-pool-heap-method-20 () none)
|
||||
(dead-pool-heap-method-21 () none)
|
||||
(dead-pool-heap-method-22 () none)
|
||||
(dead-pool-heap-method-23 () none)
|
||||
(dead-pool-heap-method-24 () none)
|
||||
(dead-pool-heap-method-25 () none)
|
||||
(dead-pool-heap-method-26 () none)
|
||||
(dead-pool-heap-method-27 () none)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype stack-frame (basic)
|
||||
((name symbol)
|
||||
(next stack-frame)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype catch-frame (stack-frame)
|
||||
((sp int32)
|
||||
(ra int32)
|
||||
(freg float 10)
|
||||
(rreg uint128 7)
|
||||
)
|
||||
(:methods
|
||||
(new (symbol type symbol function (pointer uint64)) object)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype protect-frame (stack-frame)
|
||||
((exit (function object))
|
||||
)
|
||||
)
|
||||
|
||||
(deftype handle (uint64)
|
||||
((process (pointer process) :offset 0 :size 32)
|
||||
(pid int32 :offset 32 :size 32)
|
||||
(u64 uint64 :offset 0 :size 64)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro handle->process (handle)
|
||||
"Convert a handle to a process. If the process no longer exists, returns #f."
|
||||
`(let ((the-handle (the-as handle ,handle)))
|
||||
(if (-> the-handle process) ;; if we don't point to a process, kernel sets this to #f
|
||||
(let ((proc (-> (-> the-handle process))))
|
||||
(if (= (-> the-handle pid) (-> proc pid)) ;; make sure it's the same process
|
||||
proc
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod inspect handle ((this handle))
|
||||
(when (not this)
|
||||
(return this)
|
||||
)
|
||||
(format #t "[~8x] ~A~%" this 'handle)
|
||||
(format #t "~1Tprocess: #x~X~%" (-> this process))
|
||||
(format #t "~1Tpid: ~D~%" (-> this pid))
|
||||
this
|
||||
)
|
||||
|
||||
; (defmethod print ((this handle))
|
||||
; (if (nonzero? this)
|
||||
; (format #t "#<handle :process ~A :pid ~D>" (handle->process this) (-> this pid))
|
||||
; (format #t "#<handle :process 0 :pid 0>")
|
||||
; )
|
||||
; this
|
||||
; )
|
||||
|
||||
(defmacro ppointer->process (ppointer)
|
||||
"convert a (pointer process) to a process."
|
||||
;; this uses the self field, which seems to always just get set to the object.
|
||||
;; confirmed in Jak 1 that using self here is useless, not sure...
|
||||
`(let ((the-pp ,ppointer))
|
||||
(if the-pp (-> the-pp 0 self))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro process->ppointer (proc)
|
||||
"safely get a (pointer process) from a process, returning #f if invalid."
|
||||
`(let ((the-proc ,proc))
|
||||
(if the-proc (-> the-proc ppointer))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro ppointer->handle (pproc)
|
||||
"convert a ppointer to a handle. assumes the ppointer is valid."
|
||||
`(let ((the-process (the-as (pointer process) ,pproc)))
|
||||
(new 'static 'handle :process the-process :pid (if the-process (-> the-process 0 pid)))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro process->handle (proc)
|
||||
"convert a process to a handle. if proc is #f, returns a #f handle."
|
||||
`(ppointer->handle (process->ppointer (the-as process ,proc)))
|
||||
)
|
||||
|
||||
|
||||
(deftype state (protect-frame)
|
||||
((parent basic)
|
||||
(code function)
|
||||
(trans (function object))
|
||||
(post function)
|
||||
(enter function)
|
||||
(event (function process int symbol event-message-block object))
|
||||
)
|
||||
)
|
||||
|
||||
(deftype event-message-block (structure)
|
||||
((to-handle uint64)
|
||||
(to (pointer process) :overlay-at to-handle)
|
||||
(from-handle uint64)
|
||||
(from (pointer process) :overlay-at from-handle)
|
||||
(param uint64 6)
|
||||
(message symbol)
|
||||
(num-params int32)
|
||||
)
|
||||
)
|
||||
|
||||
(deftype event-message-block-array (inline-array-class)
|
||||
((data event-message-block :dynamic)
|
||||
)
|
||||
(:methods
|
||||
(event-message-block-array-method-14 () none)
|
||||
)
|
||||
)
|
||||
|
||||
(set! (-> event-message-block-array heap-base) (the-as uint 80))
|
||||
|
||||
(deftype sql-result (array)
|
||||
((result-data object :dynamic :offset 16)
|
||||
)
|
||||
)
|
||||
|
||||
; (defmethod new sql-result ((allocation symbol) (type-to-make type) (arg0 type) (arg1 int))
|
||||
; (let ((v0-0 (object-new allocation type-to-make (the-as int (+ (-> type-to-make size) (* arg0 4))))))
|
||||
; (set! (-> v0-0 allocated-length) (the-as int arg0))
|
||||
; (set! (-> v0-0 content-type) (the-as type 'error))
|
||||
; v0-0
|
||||
; )
|
||||
; )
|
||||
|
||||
(defmethod print ((this sql-result))
|
||||
(format #t "#(~A" (-> this content-type))
|
||||
(dotimes (s5-0 (-> this length))
|
||||
(format #t " ~A" (-> this result-data s5-0))
|
||||
)
|
||||
(format #t ")")
|
||||
this
|
||||
)
|
||||
|
||||
(define *sql-result* (the-as sql-result #f))
|
||||
|
||||
;; failed to figure out what this is:
|
||||
0
|
||||
|
||||
|
||||
|
||||
(defmacro defbehavior (name process-type bindings &rest body)
|
||||
"define a new behavior. This is simply a function where self is bound to the process register,
|
||||
which is assumed to have type process-type."
|
||||
(if (and
|
||||
(> (length body) 1) ;; more than one thing in function
|
||||
(string? (first body)) ;; first thing is a string
|
||||
)
|
||||
;; then it's a docstring and we ignore it.
|
||||
`(define ,name (lambda :name ,name :behavior ,process-type ,bindings ,@(cdr body)))
|
||||
;; otherwise don't ignore it.
|
||||
`(define ,name (lambda :name ,name :behavior ,process-type ,bindings ,@body))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro process-stack-used (proc)
|
||||
"get how much stack the top thread of a process has used."
|
||||
`(- (the int (-> ,proc top-thread stack-top))
|
||||
(the int (-> ,proc top-thread sp))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro process-stack-size (proc)
|
||||
"get how much stack the top thread of a process has"
|
||||
`(-> ,proc top-thread stack-size)
|
||||
)
|
||||
|
||||
(defmacro process-heap-used (proc)
|
||||
"get how much heap a process has used."
|
||||
`(- (-> ,proc allocated-length)
|
||||
(- (the int (-> ,proc heap-top))
|
||||
(the int (-> ,proc heap-cur))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro process-heap-size (proc)
|
||||
"get how much heap a process has"
|
||||
`(the int (-> ,proc allocated-length))
|
||||
)
|
||||
|
||||
(defmacro break ()
|
||||
"crash the game by dividing by 0."
|
||||
`(/ 0 0)
|
||||
)
|
||||
|
||||
(defmacro with-pp (&rest body)
|
||||
"execute the body with pp bound to the current process register."
|
||||
`(rlet ((pp :reg r13 :reset-here #t :type process))
|
||||
,@body)
|
||||
)
|
||||
|
||||
(defconstant PP (with-pp pp))
|
||||
|
||||
(defmacro process-mask? (mask enum-value)
|
||||
"Are any of the given bits set in the process mask?"
|
||||
`(!= 0 (logand ,mask (process-mask ,enum-value)))
|
||||
)
|
||||
|
||||
(defmacro process-mask-set! (mask &rest enum-value)
|
||||
"Set the given bits in the process mask"
|
||||
`(logior! ,mask (process-mask ,@enum-value))
|
||||
)
|
||||
|
||||
(defmacro process-mask-clear! (mask &rest enum-value)
|
||||
"Clear the given bits in the process mask."
|
||||
`(logclear! ,mask (process-mask ,@enum-value))
|
||||
)
|
||||
|
||||
(defmacro suspend ()
|
||||
"suspend the current process, to be resumed on the next frame."
|
||||
`(rlet ((pp :reg r13 :reset-here #t))
|
||||
;; debug check for stack overflow here, where we can easily print the process name.
|
||||
(#when (or KERNEL_DEBUG)
|
||||
(rlet ((sp :reg rsp :reset-here #t :type int)
|
||||
(off :reg r15 :type uint))
|
||||
(let* ((sp-goal (- sp off))
|
||||
(stack-top-goal (-> (the process pp) top-thread stack-top))
|
||||
(stack-used (&- stack-top-goal sp-goal))
|
||||
(stack-size (-> (the process pp) top-thread stack-size))
|
||||
)
|
||||
(when (> stack-used stack-size)
|
||||
(format 0 "ERROR: suspend called without enough stack in proc:~%~A~%Stack: ~D/~D~%" pp stack-used stack-size)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
;; set to the current thread
|
||||
(set! pp (-> (the process pp) top-thread))
|
||||
;; call the suspend hook (put nothing as the argument)
|
||||
((-> (the cpu-thread pp) suspend-hook) (the cpu-thread 0))
|
||||
;; the kernel will set pp (possibly to a new value, if we've been relocated) on resume.
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro process-deactivate ()
|
||||
"deactivate (kill) the current process"
|
||||
`(rlet ((pp :reg r13 :reset-here #t :type process))
|
||||
(deactivate pp)
|
||||
)
|
||||
)
|
||||
|
||||
;; Some assembly functions in GOAL are ported to C++, then accessed from GOAL using these mips2c macros.
|
||||
(defmacro def-mips2c (name type)
|
||||
"Define a mips2c object (typically a function)."
|
||||
`(begin
|
||||
(define-extern ,name ,type)
|
||||
(set! ,name (the-as ,type (__pc-get-mips2c ,(symbol->string name))))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro defmethod-mips2c (name method-id method-type)
|
||||
"Define a mips2c method."
|
||||
`(method-set! ,method-type ,method-id (__pc-get-mips2c ,name))
|
||||
)
|
||||
|
||||
(defmacro kheap-alloc (heap size)
|
||||
"allocate space for a kheap"
|
||||
`(let ((heap ,heap) (size ,size))
|
||||
(set! (-> heap base) (malloc 'global size))
|
||||
(set! (-> heap current) (-> heap base))
|
||||
(set! (-> heap top-base) (&+ (-> heap base) size))
|
||||
(set! (-> heap top) (-> heap top-base))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro kheap-reset (heap)
|
||||
"reset the kheap, so you can use its memory again"
|
||||
`(let ((heap ,heap))
|
||||
(set! (-> heap current) (-> heap base))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro scratchpad-object (type &key (offset 0))
|
||||
"Access an object on the scratchpad."
|
||||
`(the-as ,type (&+ *fake-scratchpad-data* ,offset))
|
||||
)
|
||||
|
||||
(defmacro scratchpad-ptr (type &key (offset 0))
|
||||
"Create a pointer to an object on the scratchpad."
|
||||
`(the-as (pointer ,type) (&+ *fake-scratchpad-data* ,offset))
|
||||
)
|
||||
|
||||
(defmacro current-time ()
|
||||
`(-> PP clock frame-counter)
|
||||
)
|
||||
|
||||
(defmacro seconds-per-frame ()
|
||||
`(-> PP clock seconds-per-frame)
|
||||
)
|
||||
|
||||
(defmacro seconds-per-frame-high-fps ()
|
||||
"Macro for assuming a 16.6 ms frame time at higher frame rates."
|
||||
`(if (= (get-video-mode) 'custom)
|
||||
0.016666668
|
||||
(-> PP clock seconds-per-frame)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro set-time! (time)
|
||||
`(set! ,time (current-time))
|
||||
)
|
||||
|
||||
(defmacro time-elapsed? (time duration)
|
||||
`(>= (- (current-time) ,time) ,duration)
|
||||
)
|
||||
|
||||
|
@ -6,4 +6,25 @@
|
||||
;; dgos: KERNEL
|
||||
|
||||
;; DECOMP BEGINS
|
||||
(define *use-old-listener-print* #f)
|
||||
(define *kernel-version* (the binteger (logior (ash *kernel-major-version* 16) *kernel-minor-version*)))
|
||||
|
||||
(defun kernel-dispatcher ()
|
||||
"Temporary kernel dispatcher for now."
|
||||
|
||||
;; execute the listener function, if we got one.
|
||||
(when *listener-function*
|
||||
(+! *enable-method-set* 1) ;; allow out-of-order method definitions (slower)
|
||||
;; (let ((result (reset-and-call (-> *listener-process* main-thread) *listener-function*))) ;; run function!
|
||||
(let ((result (*listener-function*)))
|
||||
;; print result.
|
||||
(if *use-old-listener-print*
|
||||
(format #t "~D~%" result result result)
|
||||
(format #t "~D #x~X ~F ~A~%" result result result result)
|
||||
)
|
||||
)
|
||||
;; clear pending function
|
||||
(set! *listener-function* #f)
|
||||
(+! *enable-method-set* -1)
|
||||
)
|
||||
)
|
@ -493,10 +493,7 @@ void ObjectGenerator::emit_link_type_pointer(int seg, const TypeSystem* ts) {
|
||||
out.push_back(ts->get_type_method_count(rec.first));
|
||||
break;
|
||||
case GameVersion::Jak2:
|
||||
// the linker/intern_type functions do the +3.
|
||||
out.push_back(ts->get_type_method_count(rec.first) / 4);
|
||||
break;
|
||||
case GameVersion::Jak3:
|
||||
case GameVersion::Jak3: // jak3 opengoal uses same format as jak2 for code.
|
||||
// the linker/intern_type functions do the +3.
|
||||
out.push_back(ts->get_type_method_count(rec.first) / 4);
|
||||
break;
|
||||
|
@ -17,7 +17,7 @@ void add_jak2_expected_type_mismatches(Compiler& c) {
|
||||
c.add_ignored_type_definition("editable-plane");
|
||||
}
|
||||
|
||||
void add_jak3_expected_type_mismatches(Compiler& c) {}
|
||||
void add_jak3_expected_type_mismatches(Compiler& /*c*/) {}
|
||||
|
||||
TEST(Jak1TypeConsistency, MANUAL_TEST_TypeConsistencyWithBuildFirst) {
|
||||
Compiler compiler(GameVersion::Jak1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user