[jak3] A bunch of small fixes to get game.cgo to load (#3435)

The `test-play` macro is back, though it doesn't call `play` yet. We can
at least load all of `game.cgo`, which involves loading a lot of the
code we've decompiled, loading/linking objects files compiled by
OpenGOAL (like dir-tpages), and loading/linking Jak's art-groups (for
jak 3 they are stored v5 format that I added to the linker).

There were no major issues - just a few forgotten mips2c entries and
minor bugs/functions that needed stubs. Most of the work was updating
the linker. Hopefully I'll never have to touch that code again - I think
it supports everything we need for jak 3!
This commit is contained in:
water111 2024-03-24 16:30:28 -04:00 committed by GitHub
parent 9b4b54978a
commit ee015e3b22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 2188 additions and 77 deletions

View File

@ -1799,7 +1799,7 @@
some, but not all, functions assume that a matrix is an affine transform.
others assume that the rotation has no scale or shear (and that its inverse is its transpose)."
((data float 16 :offset-assert 0 :score -2) ;; guessed by decompiler
(vector vector 4 :offset 0 :score -1) ;; guessed by decompiler
(vector vector 4 :inline :offset 0 :score -1) ;; guessed by decompiler
(quad uint128 4 :offset 0) ;; guessed by decompiler
(rvec vector :inline :offset 0 :score 1)
(uvec vector :inline :offset 16 :score 1)
@ -30761,7 +30761,6 @@
;; sparticle-launcher ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#|
(deftype sp-queued-launch-particles (structure)
((sp-system sparticle-system :offset-assert 0) ;; guessed by decompiler
(sp-launcher sparticle-launcher :offset-assert 4) ;; guessed by decompiler
@ -30771,34 +30770,28 @@
:size-assert #x20
:flag-assert #x900000020
)
|#
#|
(deftype sp-launch-queue (basic)
((in-use int32 :offset-assert 4)
(queue sp-queued-launch-particles 256 :offset-assert 16) ;; guessed by decompiler
(queue sp-queued-launch-particles 256 :inline :offset-assert 16) ;; guessed by decompiler
)
:method-count-assert 9
:size-assert #x2010
:flag-assert #x900002010
)
|#
#|
(deftype particle-adgif-cache (basic)
((used int32 :offset-assert 4)
(last uint16 :offset-assert 8)
(lastgif adgif-shader :offset-assert 12)
(tidhash uint16 80 :offset-assert 16) ;; guessed by decompiler
(spadgif adgif-shader 80 :offset-assert 176) ;; guessed by decompiler
(spadgif adgif-shader 80 :inline :offset-assert 176) ;; guessed by decompiler
)
:method-count-assert 9
:size-assert #x19b0
:flag-assert #x9000019b0
)
|#
#|
(deftype sp-launch-stack (structure)
((ra basic :offset-assert 0)
(dummy0 basic :offset-assert 4)
@ -30822,23 +30815,22 @@
: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.
)
|#
;; sparticle-launcher is already defined!
(define-extern *part-id-table* (array sparticle-launcher))
(define-extern *part-group-id-table* (array sparticle-launch-group))
;; (define-extern *sp-temp* object) ;; float
(define-extern *sp-temp* float)
;; (define-extern lookup-part-group-by-name function) ;; (function string sparticle-launch-group)
(define-extern lookup-part-group-pointer-by-name (function string (pointer object)))
(define-extern part-group-pointer? (function pointer symbol))
(define-extern unlink-part-group-by-heap (function kheap int))
;; (define-extern sp-init-fields! function) ;; (function (pointer float) (inline-array sp-field-init-spec) sp-field-id sp-field-id symbol (inline-array sp-field-init-spec))
;; (define-extern *sp-launcher-lock* object) ;; symbol
;; (define-extern *sp-launch-queue* object) ;; sp-launch-queue
;; (define-extern *sp-launcher-enable* object) ;; symbol
(define-extern *sp-launcher-lock* symbol)
(define-extern *sp-launch-queue* sp-launch-queue)
(define-extern *sp-launcher-enable* symbol)
;; (define-extern particle-setup-adgif function) ;; (function adgif-shader int none)
;; (define-extern *particle-adgif-cache* object) ;; particle-adgif-cache
(define-extern *particle-adgif-cache* particle-adgif-cache)
(define-extern particle-adgif-cache-flush (function none))
;; (define-extern particle-adgif function) ;; (function adgif-shader texture-id none)
;; (define-extern particle-adgif-callback function) ;; (function adgif-shader texture-id none)
@ -30847,7 +30839,7 @@
;; (define-extern sp-euler-convert function) ;; (function sparticle-launchinfo sparticle-cpuinfo none)
;; (define-extern sp-rotate-system function) ;; (function sparticle-launchinfo sparticle-cpuinfo transformq none)
(define-extern sp-launch-particles-var (function sparticle-system sparticle-launcher matrix sparticle-launch-state sparticle-launch-control float none))
;; (define-extern *death-adgif* object) ;; adgif-shader
(define-extern *death-adgif* adgif-shader)
;; (define-extern sp-launch-particles-death function) ;; (function sparticle-system sparticle-launcher vector none)
;; (define-extern sp-clear-queue function) ;; (function none)
;; (define-extern sp-relaunch-setup-fields function) ;; (function object sparticle-launcher sparticle-cpuinfo sprite-vec-data-3d none)
@ -30865,7 +30857,7 @@
;; (define-extern birth-func-clean function)
;; (define-extern birth-func-process-clock function)
;; (define-extern birth-func-copy-rot-color function) ;; (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d sparticle-launcher sparticle-launch-state none)
;; (define-extern *global-toggle* object) ;; int
(define-extern *global-toggle* int)
;; (define-extern birth-func-copy2-rot-color function) ;; (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d sparticle-launcher sparticle-launch-state none)
;; (define-extern birth-func-copy-omega-to-z function) ;; (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d sparticle-launcher sparticle-launch-state none)
;; (define-extern birth-func-random-next-time function) ;; (function sparticle-system sparticle-cpuinfo sprite-vec-data-3d sparticle-launcher sparticle-launch-state none)

View File

@ -188,10 +188,13 @@ set(RUNTIME_SOURCE
mips2c/jak3_functions/collide_func.cpp
mips2c/jak3_functions/debug.cpp
mips2c/jak3_functions/font.cpp
mips2c/jak3_functions/foreground.cpp
mips2c/jak3_functions/generic_effect.cpp
mips2c/jak3_functions/joint.cpp
mips2c/jak3_functions/lights.cpp
mips2c/jak3_functions/prim.cpp
mips2c/jak3_functions/sky.cpp
mips2c/jak3_functions/texture.cpp
mips2c/mips2c_table.cpp
overlord/common/dma.cpp
overlord/common/fake_iso.cpp

View File

@ -32,6 +32,13 @@ struct ObjectFileHeader {
uint32_t link_block_length;
};
struct SegmentInfoV5 {
uint32_t relocs; // offset of relocation table
uint32_t data; // offset of segment data
uint32_t size; // segment data size (0 if segment doesn't exist)
uint32_t magic; // always 0
};
void klink_init_globals();
/*!
* Stores the state of the linker. Used for multi-threaded linking, so it can be suspended.
@ -65,6 +72,8 @@ struct link_control {
bool m_on_global_heap = false;
LinkHeaderV5Core* m_link_hdr = nullptr;
bool m_moved_link_block = false;
int m_n_segments = 0;
SegmentInfoV5* m_link_segments_table = nullptr;
void jak1_jak2_begin(Ptr<uint8_t> object_file,
const char* name,
@ -89,6 +98,7 @@ struct link_control {
uint32_t jak2_work_v3();
uint32_t jak2_work_v2();
uint32_t jak3_work_v2_v4();
uint32_t jak3_work_v5();
uint32_t jak3_work_opengoal();

View File

@ -7,6 +7,7 @@
#include "game/kernel/common/fileio.h"
#include "game/kernel/common/klink.h"
#include "game/kernel/common/kprint.h"
#include "game/kernel/common/memory_layout.h"
#include "game/kernel/jak3/kmalloc.h"
#include "game/kernel/jak3/kscheme.h"
#include "game/mips2c/mips2c_table.h"
@ -145,19 +146,27 @@ void link_control::jak3_begin(Ptr<uint8_t> object_file,
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.
if (version == 4) {
// it's a v4 produced by opengoal... lets just try using jak2's linker
m_version = 4;
printf("got version 4, falling back to jak1/jak2\n");
jak1_jak2_begin(object_file, name, size, heap, flags);
return;
}
ASSERT(version == 5);
m_heap_top = heap->top;
// this->unk_init1 = 1; TODO
m_busy = true;
m_heap = heap;
// this->m_unk_init0_0 = 0; TODO
m_entry.offset = 0;
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
m_state = 0;
m_segment_process = 0;
m_moved_link_block = 0;
if (version == 4) {
ASSERT_NOT_REACHED();
} else {
@ -183,7 +192,17 @@ void link_control::jak3_begin(Ptr<uint8_t> object_file,
m_heap->current = m_object_data;
}
} else {
// the link block is in the heap. This is problematic because we don't want to hang
// on to this long term, but executing the top-level may allocate on this heap, causing
// stuff to get added after the hole left by the link data.
// So, we make a temporary allocation on the top and move it there.
m_moved_link_block = true;
Ptr<u8> new_link_block_mem;
u8* link_block_move_dst;
u8* old_link_block;
u32 link_block_move_size;
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:
@ -191,9 +210,9 @@ void link_control::jak3_begin(Ptr<uint8_t> object_file,
// 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");
// allocate memory for link data, and header (pvVar5)
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
@ -202,30 +221,38 @@ void link_control::jak3_begin(Ptr<uint8_t> object_file,
// 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);
// dst: pvVar6
link_block_move_dst = new_link_block_mem.c() + sizeof(LinkHeaderV5);
// update our pointer to the link header core.
m_link_hdr = &((LinkHeaderV5*)new_link_block_mem.c())->core;
// move link data! (pcVar8)
old_link_block = object_file.c() + offset_to_link_data;
// 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;
}
link_block_move_size = m_link_hdr->link_length;
} else {
// hm, maybe only possible with version 2 or 3??
ASSERT_NOT_REACHED();
}
memmove(link_block_move_dst, old_link_block, link_block_move_size);
// 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;
}
}
}
if ((m_flags & LINK_FLAG_FORCE_DEBUG) && MasterDebug && !DiskBoot) {
m_keep_debug = true;
}
// hack:
m_version = m_link_hdr->version;
}
}
@ -248,6 +275,11 @@ uint32_t link_control::jak3_work() {
ASSERT(!m_opengoal);
*(u32*)(((u8*)m_link_hdr) - 4) = *((s7 + jak3_symbols::FIX_SYM_LINK_BLOCK - 1).cast<u32>());
rv = jak3_work_v5();
} else if (m_version == 4) {
// Note: this is a bit of a hack. Jak 3 doesn't support v2/v4. But, OpenGOAL generates data
// objects in this format. We will just try reusing the jak 2 v2/v4 linker here and see if it
// works. See corresponding call to jak1_jak2_begin in begin.
rv = jak3_work_v2_v4();
} else {
ASSERT_MSG(false, fmt::format("UNHANDLED OBJECT FILE VERSION {} IN WORK!", m_version));
return 0;
@ -257,9 +289,175 @@ uint32_t link_control::jak3_work() {
return rv;
}
namespace jak3 {
void ultimate_memcpy(void* dst, void* src, uint32_t size);
}
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.
if (m_state == 0) {
// here, we change length_to_get_to_link to an actual pointer to the link table.
// since we need 32-bits, we'll store offset from g_ee_mem.
u8* link_data = ((u8*)m_link_hdr) - 4 + m_link_hdr->length_to_get_to_link;
m_link_hdr->length_to_get_to_link = link_data - g_ee_main_mem;
m_n_segments = m_link_hdr->n_segments;
// the link segments table is just at the start of the link data:
m_link_segments_table = (SegmentInfoV5*)link_data;
/*
for (int i = 0; i < m_n_segments; i++) {
printf(" %d: reloc %d, data %d, size %d, magic %d\n", i, m_link_segments_table[i].relocs,
m_link_segments_table[i].data, m_link_segments_table[i].size,
m_link_segments_table[i].magic);
}
*/
// for now, only supporting 1 segment
ASSERT(m_n_segments == 1);
// fixup the relocs/data offsets into addresses (again, offsets from g_ee_main_mem)
// relocs is relative to this link data
m_link_segments_table[0].relocs += (link_data - g_ee_main_mem);
// data is relative to usual object_data
m_link_segments_table[0].data += m_object_data.offset;
ASSERT(m_link_segments_table[0].magic == 1);
// see if there's even data
if (m_link_segments_table[0].size == 0) {
// no data.
m_link_segments_table[0].data = 0;
} else {
// check if we need to move the main segment.
if (!m_moved_link_block ||
((m_link_hdr->link_length + 0x50) <= m_link_hdr->length_to_get_to_code)) {
// printf(" v5 linker allocating for main segment... (%d)\n", m_moved_link_block);
auto old_data_offset = m_link_segments_table[0].data; // 25
auto new_data = kmalloc(m_heap, m_link_segments_table[0].size, 0, "main-segment");
m_link_segments_table[0].data = new_data.offset;
if (!new_data.offset) {
MsgErr("dkernel: unable to malloc %d bytes for main-segment\n",
m_link_segments_table[0].size);
return 1;
}
jak3::ultimate_memcpy(new_data.c(), old_data_offset + g_ee_main_mem,
m_link_segments_table[0].size);
} else {
m_heap->current = m_object_data + m_code_size;
if (m_heap->top.offset <= m_heap->current.offset) {
MsgErr("dkernel: heap overflow\n");
return 1;
}
}
}
m_segment_process = 0;
m_state = 1;
m_object_data.offset = m_link_segments_table[0].data;
Ptr<u8> base_ptr(m_link_segments_table[0].data);
Ptr<u8> data_ptr = base_ptr - 4;
Ptr<u8> link_ptr(m_link_segments_table[0].relocs);
bool fixing = false;
if (*link_ptr) {
// we have pointers
while (true) {
while (true) {
if (!fixing) {
// seeking
data_ptr.offset += 4 * (*link_ptr);
} else {
// fixing.
for (uint32_t i = 0; i < *link_ptr; i++) {
// uint32_t old_code = *(const uint32_t*)(&data.at(data_ptr));
u32 old_code = *data_ptr.cast<u32>();
if ((old_code >> 24) == 0) {
// printf("modifying pointer at 0x%x (old 0x%x) : now ", data_ptr.offset,
// *data_ptr.cast<u32>());
*data_ptr.cast<u32>() += base_ptr.offset;
// printf("0x%x\n", *data_ptr.cast<u32>());
} else {
ASSERT_NOT_REACHED();
/*
f.stats.v3_split_pointers++;
auto dest_seg = (old_code >> 8) & 0xf;
auto lo_hi_offset = (old_code >> 12) & 0xf;
ASSERT(lo_hi_offset);
ASSERT(dest_seg < 3);
auto offset_upper = old_code & 0xff;
uint32_t low_code = *(const uint32_t*)(&data.at(data_ptr + 4 * lo_hi_offset));
uint32_t offset = low_code & 0xffff;
if (offset_upper) {
offset += (offset_upper << 16);
}
f.pointer_link_split_word(seg_id, data_ptr - base_ptr,
data_ptr + 4 * lo_hi_offset - base_ptr, dest_seg, offset);
*/
}
data_ptr.offset += 4;
}
}
if (*link_ptr != 0xff)
break;
link_ptr.offset++;
if (*link_ptr == 0) {
link_ptr.offset++;
fixing = !fixing;
}
}
link_ptr.offset++;
fixing = !fixing;
if (*link_ptr == 0)
break;
}
}
link_ptr.offset++;
// symbol linking.
if (*link_ptr) {
auto sub_link_ptr = link_ptr;
while (true) {
auto reloc = *sub_link_ptr;
auto next_link_ptr = sub_link_ptr + 1;
link_ptr = next_link_ptr;
if ((reloc & 0x80) == 0) {
link_ptr = sub_link_ptr + 3; //
const char* sname = link_ptr.cast<char>().c();
link_ptr.offset += strlen(sname) + 1;
// printf("linking symbol %s\n", sname);
auto goalObj = jak3::intern_from_c(-1, 0, sname);
link_ptr = c_symlink2(m_object_data, goalObj.cast<u8>(), link_ptr);
} else if ((reloc & 0x3f) == 0x3f) {
ASSERT(false); // todo, does this ever get hit?
} else {
int n_methods_base = reloc & 0x3f;
int n_methods = n_methods_base * 4;
if (n_methods_base) {
n_methods += 3;
}
link_ptr.offset +=
2; // ghidra misses some aliasing here and would have you think this is +1!
const char* sname = link_ptr.cast<char>().c();
// printf("linking type %s\n", sname);
link_ptr.offset += strlen(sname) + 1;
auto goalObj = jak3::intern_type_from_c(-1, 0, sname, n_methods);
link_ptr = c_symlink2(m_object_data, goalObj.cast<u8>(), link_ptr);
}
sub_link_ptr = link_ptr;
if (!*sub_link_ptr)
break;
}
}
m_entry = m_object_data + 4;
return 1;
} else {
ASSERT_NOT_REACHED();
}
}
namespace {
@ -567,7 +765,17 @@ void link_control::jak3_finish(bool jump_from_c_to_goal) {
output_segment_load(m_object_name, m_link_block_ptr, m_flags);
}
} else {
ASSERT_NOT_REACHED();
if (m_flags & LINK_FLAG_EXECUTE) {
auto entry = m_entry;
auto name = basename_goal(m_object_name);
strcpy(Ptr<char>(LINK_CONTROL_NAME_ADDR).c(), name);
// printf(" about to call... (0x%x)\n", entry.offset);
Ptr<jak3::Type> type(*((entry - 4).cast<u32>()));
// printf(" type is %s\n", jak3::sym_to_cstring(type->symbol));
jak3::call_method_of_type_arg2(entry.offset, type, GOAL_RELOC_METHOD, m_heap.offset,
Ptr<char>(LINK_CONTROL_NAME_ADDR).offset);
// printf(" done with call!\n");
}
}
*EnableMethodSet = *EnableMethodSet - this->m_keep_debug;
@ -654,3 +862,214 @@ void ultimate_memcpy(void* dst, void* src, uint32_t size) {
}
} // namespace jak3
#define LINK_V2_STATE_INIT_COPY 0
#define LINK_V2_STATE_OFFSETS 1
#define LINK_V2_STATE_SYMBOL_TABLE 2
#define OBJ_V2_CLOSE_ENOUGH 0x90
#define OBJ_V2_MAX_TRANSFER 0x80000
uint32_t link_control::jak3_work_v2_v4() {
// u32 startCycle = kernel.read_clock(); todo
if (m_state == LINK_V2_STATE_INIT_COPY) { // initialization and copying to heap
// we move the data segment to eliminate gaps
// very small gaps can be tolerated, as it is not worth the time penalty to move large objects
// many bytes. if this requires copying a large amount of data, we will do it in smaller chunks,
// allowing the copy to be spread over multiple game frames
// state initialization
if (m_segment_process == 0) {
m_heap_gap =
m_object_data - m_heap->current; // distance between end of heap and start of object
}
if (m_heap_gap <
OBJ_V2_CLOSE_ENOUGH) { // close enough, don't relocate the object, just expand the heap
if (link_debug_printfs) {
printf("[work_v2] close enough, not moving\n");
}
m_heap->current = m_object_data + m_code_size;
if (m_heap->top.offset <= m_heap->current.offset) {
MsgErr("dkernel: heap overflow\n"); // game has ~% instead of \n :P
return 1;
}
// added in jak 2, move the link block to the top of the heap so we can allocate on
// the level heap during linking without overwriting link data. this is used for level types
u32 link_block_size = *m_link_block_ptr.cast<u32>();
auto new_link_block = kmalloc(m_heap, link_block_size, KMALLOC_TOP, "link-block");
memmove(new_link_block.c(), m_link_block_ptr.c() - 4, link_block_size);
m_link_block_ptr = Ptr<uint8_t>(new_link_block.offset + 4); // basic offset
} else { // not close enough, need to move the object
// on the first run of this state...
if (m_segment_process == 0) {
m_original_object_location = m_object_data;
// allocate on heap, will have no gap
m_object_data = kmalloc(m_heap, m_code_size, 0, "data-segment");
if (link_debug_printfs) {
printf("[work_v2] moving from 0x%x to 0x%x\n", m_original_object_location.offset,
m_object_data.offset);
}
if (!m_object_data.offset) {
MsgErr("dkernel: unable to malloc %d bytes for data-segment\n", m_code_size);
return 1;
}
}
// the actual copy
Ptr<u8> source = m_original_object_location + m_segment_process;
u32 size = m_code_size - m_segment_process;
if (size > OBJ_V2_MAX_TRANSFER) { // around .5 MB
jak3::ultimate_memcpy((m_object_data + m_segment_process).c(), source.c(),
OBJ_V2_MAX_TRANSFER);
m_segment_process += OBJ_V2_MAX_TRANSFER;
return 0; // return, don't want to take too long.
}
// if we have bytes to copy, but they are less than the max transfer, do it in one shot!
if (size) {
jak3::ultimate_memcpy((m_object_data + m_segment_process).c(), source.c(), size);
if (m_segment_process > 0) { // if we did a previous copy, we return now....
m_state = LINK_V2_STATE_OFFSETS;
m_segment_process = 0;
return 0;
}
}
}
// otherwise go straight into the next state.
m_state = LINK_V2_STATE_OFFSETS;
m_segment_process = 0;
}
// init offset phase
if (m_state == LINK_V2_STATE_OFFSETS && m_segment_process == 0) {
m_reloc_ptr = m_link_block_ptr + 8; // seek to link table
if (*m_reloc_ptr == 0) { // do we have pointer links to do?
m_reloc_ptr.offset++; // if not, seek past the \0, and go to next state
m_state = LINK_V2_STATE_SYMBOL_TABLE;
m_segment_process = 0;
} else {
m_base_ptr = m_object_data; // base address for offsetting.
m_loc_ptr = m_object_data; // pointer which seeks thru the code
m_table_toggle = 0; // are we seeking or fixing?
m_segment_process = 1; // we've done first time setup
}
}
if (m_state == LINK_V2_STATE_OFFSETS) { // pointer fixup
// this state reads through a table. Values alternate between "seek amount" and "number of
// consecutive 4-byte
// words to fix up". The counts are encoded using a variable length encoding scheme. They use
// a very stupid
// method of encoding values which requires O(n) bytes to store the value n.
// to avoid dropping a frame, we check every 0x400 relocations to see if 0.5 milliseconds have
// elapsed.
u32 relocCounter = 0x400;
while (true) { // loop over entire table
while (true) { // loop over current mode
// read and seek table
u8 count = *m_reloc_ptr;
m_reloc_ptr.offset++;
if (!m_table_toggle) { // seek mode
m_loc_ptr.offset +=
4 *
count; // perform seek (MIPS instructions are 4 bytes, so we >> 2 the seek amount)
} else { // offset mode
for (u32 i = 0; i < count; i++) {
if (m_loc_ptr.offset % 4) {
ASSERT(false);
}
u32 code = *(m_loc_ptr.cast<u32>());
code += m_base_ptr.offset;
*(m_loc_ptr.cast<u32>()) = code;
m_loc_ptr.offset += 4;
}
}
if (count != 0xff) {
break;
}
if (*m_reloc_ptr == 0) {
m_reloc_ptr.offset++;
m_table_toggle = m_table_toggle ^ 1;
}
}
// reached the end of the tableToggle mode
m_table_toggle = m_table_toggle ^ 1;
if (*m_reloc_ptr == 0) {
break; // end of the state
}
relocCounter--;
if (relocCounter == 0) {
// u32 clock_value = kernel.read_clock();
// if(clock_value - startCycle > 150000) { // 0.5 milliseconds
// return 0;
// }
relocCounter = 0x400;
}
}
m_reloc_ptr.offset++;
m_state = 2;
m_segment_process = 0;
}
if (m_state == 2) { // GOAL object fixup
if (*m_reloc_ptr == 0) {
m_state = 3;
m_segment_process = 0;
} else {
while (true) {
u32 relocation = *m_reloc_ptr;
m_reloc_ptr.offset++;
Ptr<u8> goalObj;
char* name;
if ((relocation & 0x80) == 0) {
// symbol!
if (relocation > 9) {
m_reloc_ptr.offset--; // no idea what this is.
}
name = m_reloc_ptr.cast<char>().c();
if (link_debug_printfs) {
printf("[work_v2] symlink: %s\n", name);
}
goalObj = jak3::intern_from_c(-1, 0, name).cast<u8>();
} else {
// type!
u8 nMethods = relocation & 0x7f;
if (nMethods == 0) {
nMethods = 1;
}
name = m_reloc_ptr.cast<char>().c();
if (link_debug_printfs) {
printf("[work_v2] symlink -type: %s\n", name);
}
goalObj = jak3::intern_type_from_c(-1, 0, name, nMethods).cast<u8>();
}
m_reloc_ptr.offset += strlen(name) + 1;
// DECOMPILER->hookStartSymlinkV3(_state - 1, _objectData, std::string(name));
m_reloc_ptr = c_symlink2(m_object_data, goalObj, m_reloc_ptr);
// DECOMPILER->hookFinishSymlinkV3();
if (*m_reloc_ptr == 0) {
break; // done
}
// u32 currentCycle = kernel.read_clock();
// if(currentCycle - startCycle > 150000) {
// return 0;
// }
}
m_state = 3;
m_segment_process = 0;
}
}
m_entry = m_object_data + 4;
return 1;
}

View File

@ -54,11 +54,11 @@ void kscheme_init_globals() {
#endif
}
namespace {
u32 u32_in_fixed_sym(u32 offset) {
return Ptr<Symbol4<u32>>(s7.offset + offset)->value();
}
namespace {
void fixed_sym_set(u32 offset, u32 value) {
Ptr<Symbol4<u32>>(s7.offset + offset)->value() = value;
}

View File

@ -40,6 +40,7 @@ struct Type {
};
s64 load_and_link(const char* filename, char* decode_name, kheapinfo* heap, u32 flags);
u32 u32_in_fixed_sym(u32 offset);
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*/);
@ -56,7 +57,9 @@ 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);
u64 alloc_heap_object(u32 heap, u32 type, u32 size, u32 pp);
int InitHeapAndSymbol();
u64 call_method_of_type_arg2(u32 arg, Ptr<Type> type, u32 method_id, u32 a1, u32 a2);
template <typename T>
Ptr<Ptr<String>> sym_to_string_ptr(Ptr<Symbol4<T>> in) {
return Ptr<Ptr<String>>(SymbolString.offset + in.offset - s7.offset);

View File

@ -241,7 +241,6 @@ void link() {
} // namespace Mips2C
// add moving_sphere_triangle_intersect::link to the link callback table for the object file.
// FWD DEC:
namespace moving_sphere_triangle_intersect { extern void link(); }
//--------------------------MIPS2C---------------------
// clang-format off
@ -503,4 +502,3 @@ void link() {
} // namespace Mips2C
// add collide_do_primitives::link to the link callback table for the object file.
// FWD DEC:
namespace collide_do_primitives { extern void link(); }

View File

@ -149,8 +149,6 @@ struct Cache {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
u32 call_addr = 0;
c->load_symbol2(v1, cache.math_camera); // lw v1, *math-camera*(s7)
c->load_symbol2(a0, cache.sky_work); // lw a0, *sky-work*(s7)
c->daddiu(a0, a0, 1088); // daddiu a0, a0, 1088
@ -215,7 +213,7 @@ struct Cache {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
u32 call_addr = 0;
// u32 call_addr = 0;
// nop // sll r0, r0, 0
c->daddiu(sp, sp, -8); // daddiu sp, sp, -8
// nop // sll r0, r0, 0
@ -223,7 +221,7 @@ u64 execute(void* ctxt) {
c->load_symbol2(t9, cache.clip_polygon_against_positive_hyperplane);// lw t9, clip-polygon-against-positive-hyperplane(s7)
c->mov64(a2, t4); // or a2, t4, r0
c->mov64(a3, t5); // or a3, t5, r0
call_addr = c->gprs[t9].du32[0]; // function call:
// call_addr = c->gprs[t9].du32[0]; // function call:
c->daddu(t2, a2, r0); // daddu t2, a2, r0
// c->jalr(call_addr); // jalr ra, t9
clip_polygon_against_positive_hyperplane::execute(ctxt);
@ -233,7 +231,7 @@ u64 execute(void* ctxt) {
c->mov64(a2, t5); // or a2, t5, r0
c->mov64(a3, t4); // or a3, t4, r0
call_addr = c->gprs[t9].du32[0]; // function call:
// call_addr = c->gprs[t9].du32[0]; // function call:
c->daddiu(t2, a2, 4); // daddiu t2, a2, 4
// c->jalr(call_addr); // jalr ra, t9
clip_polygon_against_positive_hyperplane::execute(ctxt);
@ -243,7 +241,7 @@ u64 execute(void* ctxt) {
c->mov64(a2, t4); // or a2, t4, r0
c->mov64(a3, t5); // or a3, t5, r0
call_addr = c->gprs[t9].du32[0]; // function call:
// call_addr = c->gprs[t9].du32[0]; // function call:
c->daddu(t2, a2, r0); // daddu t2, a2, r0
// c->jalr(call_addr); // jalr ra, t9
clip_polygon_against_negative_hyperplane::execute(ctxt);
@ -253,7 +251,7 @@ u64 execute(void* ctxt) {
c->mov64(a2, t5); // or a2, t5, r0
c->mov64(a3, t4); // or a3, t4, r0
call_addr = c->gprs[t9].du32[0]; // function call:
// call_addr = c->gprs[t9].du32[0]; // function call:
c->daddiu(t2, a2, 4); // daddiu t2, a2, 4
// c->jalr(call_addr); // jalr ra, t9
clip_polygon_against_negative_hyperplane::execute(ctxt);
@ -355,7 +353,6 @@ u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
c->copy_vfs_from_other(&sky_regs_vfs);
bool bc = false;
u32 call_addr = 0;
c->mov64(v1, a0); // or v1, a0, r0
c->load_symbol2(v1, cache.math_camera); // lw v1, *math-camera*(s7)
c->lqc2(vf14, 780, v1); // lqc2 vf14, 780(v1)
@ -493,7 +490,6 @@ u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
c->copy_vfs_from_other(&sky_regs_vfs);
bool bc = false;
u32 call_addr = 0;
c->mov64(v1, a0); // or v1, a0, r0
get_fake_spad_addr2(t4, cache.fake_scratchpad_data, 0, c);// lui t4, 28672
c->ori(t4, t4, 12288); // ori t4, t4, 12288

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
//--------------------------MIPS2C---------------------
#include "game/mips2c/mips2c_private.h"
namespace Mips2C::jak3 {
// main function for (parent bone, transformq) -> child bone
// this is used to compute world-space bones, used for collision and similar.
// This includes the weird w divisor thing
// This does not take into account the bind pose for mesh drawing.
// (that's handled in bones.gc, which combines this with the bind pose to get the merc/pris matrix)
namespace cspace_parented_transformq_joint {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
// nop // sll r0, r0, 0
c->lw(a3, 0, a0); // lw a3, 0(a0)
c->lui(v1, 16256); // lui v1, 16256
c->lqc2(vf5, 16, a1); // lqc2 vf5, 16(a1)
c->mtc1(f0, v1); // mtc1 f0, v1
c->lw(t0, 16, a3); // lw t0, 16(a3)
// nop // sll r0, r0, 0
c->lw(a2, 16, a0); // lw a2, 16(a0)
c->vadd(DEST::xyzw, vf6, vf5, vf5); // vadd.xyzw vf6, vf5, vf5
c->lwc1(f1, 64, t0); // lwc1 f1, 64(t0)
c->vadd_bc(DEST::x, BC::w, vf2, vf0, vf5); // vaddw.x vf2, vf0, vf5
c->lqc2(vf15, 0, a1); // lqc2 vf15, 0(a1)
c->vadd_bc(DEST::y, BC::z, vf2, vf0, vf5); // vaddz.y vf2, vf0, vf5
c->lqc2(vf1, 32, a1); // lqc2 vf1, 32(a1)
c->divs_accurate(f4, f0, f1); // div.s f4, f0, f1
c->lqc2(vf7, 0, t0); // lqc2 vf7, 0(t0)
c->vsub_bc(DEST::z, BC::y, vf2, vf0, vf5); // vsuby.z vf2, vf0, vf5
c->lqc2(vf8, 16, t0); // lqc2 vf8, 16(t0)
// sets vf2.w to 0
c->vsub_bc(DEST::w, BC::w, vf2, vf0, vf0); // vsubw.w vf2, vf0, vf0
c->lqc2(vf9, 32, t0); // lqc2 vf9, 32(t0)
c->vsub_bc(DEST::x, BC::z, vf3, vf0, vf5); // vsubz.x vf3, vf0, vf5
c->lqc2(vf10, 48, t0); // lqc2 vf10, 48(t0)
c->vadd_bc(DEST::y, BC::w, vf3, vf0, vf5); // vaddw.y vf3, vf0, vf5
c->lwc1(f2, 68, t0); // lwc1 f2, 68(t0)
c->vadd_bc(DEST::z, BC::x, vf3, vf0, vf5); // vaddx.z vf3, vf0, vf5
c->sqc2(vf1, 64, a2); // sqc2 vf1, 64(a2)
c->vsub_bc(DEST::w, BC::w, vf3, vf0, vf0); // vsubw.w vf3, vf0, vf0
c->lwc1(f3, 72, t0); // lwc1 f3, 72(t0)
c->vadd_bc(DEST::x, BC::y, vf4, vf0, vf5); // vaddy.x vf4, vf0, vf5
c->lw(v1, 76, t0); // lw v1, 76(t0)
c->vsub_bc(DEST::y, BC::x, vf4, vf0, vf5); // vsubx.y vf4, vf0, vf5
c->mfc1(t1, f4); // mfc1 t1, f4
c->vadd_bc(DEST::z, BC::w, vf4, vf0, vf5); // vaddw.z vf4, vf0, vf5
c->divs_accurate(f4, f0, f2); // div.s f4, f0, f2
c->vsub_bc(DEST::w, BC::w, vf4, vf0, vf0); // vsubw.w vf4, vf0, vf0
c->vopmula(vf6, vf2); // vopmula.xyz acc, vf6, vf2
c->vopmsub(vf2, vf2, vf6); // vopmsub.xyz vf2, vf2, vf6
c->vopmula(vf6, vf3); // vopmula.xyz acc, vf6, vf3
c->vopmsub(vf3, vf3, vf6); // vopmsub.xyz vf3, vf3, vf6
c->vopmula(vf6, vf4); // vopmula.xyz acc, vf6, vf4
c->vopmsub(vf4, vf4, vf6); // vopmsub.xyz vf4, vf4, vf6
c->vadd_bc(DEST::x, BC::w, vf2, vf2, vf0); // vaddw.x vf2, vf2, vf0
c->vadd_bc(DEST::y, BC::w, vf3, vf3, vf0); // vaddw.y vf3, vf3, vf0
c->vadd_bc(DEST::z, BC::w, vf4, vf4, vf0); // vaddw.z vf4, vf4, vf0
c->mfc1(t2, f4); // mfc1 t2, f4
bc = c->sgpr64(v1) != 0; // bne v1, r0, L50
c->divs_accurate(f4, f0, f3); // div.s f4, f0, f3
if (bc) {
goto block_2;
} // branch non-likely
c->vmul_bc(DEST::xyzw, BC::x, vf2, vf2, vf1); // vmulx.xyzw vf2, vf2, vf1
c->vmul_bc(DEST::xyzw, BC::y, vf3, vf3, vf1); // vmuly.xyzw vf3, vf3, vf1
c->vmul_bc(DEST::xyzw, BC::z, vf4, vf4, vf1); // vmulz.xyzw vf4, vf4, vf1
c->vmula_bc(DEST::xyzw, BC::x, vf7, vf2); // vmulax.xyzw acc, vf7, vf2
c->vmadda_bc(DEST::xyzw, BC::y, vf8, vf2); // vmadday.xyzw acc, vf8, vf2
c->vmadda_bc(DEST::xyzw, BC::z, vf9, vf2); // vmaddaz.xyzw acc, vf9, vf2
c->vmadd_bc(DEST::xyzw, BC::w, vf11, vf10, vf2); // vmaddw.xyzw vf11, vf10, vf2
c->vmula_bc(DEST::xyzw, BC::x, vf7, vf3); // vmulax.xyzw acc, vf7, vf3
c->vmadda_bc(DEST::xyzw, BC::y, vf8, vf3); // vmadday.xyzw acc, vf8, vf3
c->vmadda_bc(DEST::xyzw, BC::z, vf9, vf3); // vmaddaz.xyzw acc, vf9, vf3
c->vmadd_bc(DEST::xyzw, BC::w, vf12, vf10, vf3); // vmaddw.xyzw vf12, vf10, vf3
c->vmula_bc(DEST::xyzw, BC::x, vf7, vf4); // vmulax.xyzw acc, vf7, vf4
c->vmadda_bc(DEST::xyzw, BC::y, vf8, vf4); // vmadday.xyzw acc, vf8, vf4
c->vmadda_bc(DEST::xyzw, BC::z, vf9, vf4); // vmaddaz.xyzw acc, vf9, vf4
c->vmadd_bc(DEST::xyzw, BC::w, vf13, vf10, vf4); // vmaddw.xyzw vf13, vf10, vf4
c->vmula_bc(DEST::xyzw, BC::x, vf7, vf15); // vmulax.xyzw acc, vf7, vf15
c->vmadda_bc(DEST::xyzw, BC::y, vf8, vf15); // vmadday.xyzw acc, vf8, vf15
c->vmadda_bc(DEST::xyzw, BC::z, vf9, vf15); // vmaddaz.xyzw acc, vf9, vf15
c->vmadd_bc(DEST::xyzw, BC::w, vf14, vf10, vf0); // vmaddw.xyzw vf14, vf10, vf0
c->sqc2(vf11, 0, a2); // sqc2 vf11, 0(a2)
c->sqc2(vf12, 16, a2); // sqc2 vf12, 16(a2)
c->sqc2(vf13, 32, a2); // sqc2 vf13, 32(a2)
c->sqc2(vf14, 48, a2); // sqc2 vf14, 48(a2)
// jr ra // jr ra
// nop // sll r0, r0, 0
goto end_of_function; // return
block_2:
c->pextlw(t1, t2, t1); // pextlw t1, t2, t1
c->vmul_bc(DEST::xyzw, BC::x, vf2, vf2, vf1); // vmulx.xyzw vf2, vf2, vf1
c->vmul_bc(DEST::xyzw, BC::y, vf3, vf3, vf1); // vmuly.xyzw vf3, vf3, vf1
c->vmul_bc(DEST::xyzw, BC::z, vf4, vf4, vf1); // vmulz.xyzw vf4, vf4, vf1
// here, f4 is 1/scale. Sometimes the scale out of the joint compression code is slightly negative
// this leads to mfc1 sign extending 1's into the upper 32 bits of t3 (this is weirdly how the ps2
// does it).
c->mfc1(t3, f4); // mfc1 t3, f4
// and this brings those ones into bits 96-128
c->pcpyld(t1, t3, t1); // pcpyld t1, t3, t1
// so here, vf16.w is usually 0, except for when the scale is negative, then it's 0xffff'ffff
// (NaN on x86, -BIG on PS2)
c->mov128_vf_gpr(vf16, t1); // qmtc2.i vf16, t1
// here, vf2/3/4's w's are all 0. On PS2, this always keeps them as 0.
// but on x86, this propagates NaNs: 0 * NaN = NaN.
// so:
c->vfs[vf16].vf.w() = 0; // PATCH to clear invalid float that will be multiplied by 0 below
// (this might seem weird because the multiplication sequence could have 3 instructions removed
// because we know that vf2/3/4.w are all 0. But maybe this is just copy-pasted, or it didn't
// really matter because it would have stalled in place of that 1 cycle instruction because
// multiplication latency is 4).
c->vmul(DEST::xyzw, vf2, vf2, vf16); // vmul.xyzw vf2, vf2, vf16
c->vmul(DEST::xyzw, vf3, vf3, vf16); // vmul.xyzw vf3, vf3, vf16
c->vmul(DEST::xyzw, vf4, vf4, vf16); // vmul.xyzw vf4, vf4, vf16
c->vmula_bc(DEST::xyzw, BC::x, vf7, vf2); // vmulax.xyzw acc, vf7, vf2
c->vmadda_bc(DEST::xyzw, BC::y, vf8, vf2); // vmadday.xyzw acc, vf8, vf2
c->vmadda_bc(DEST::xyzw, BC::z, vf9, vf2); // vmaddaz.xyzw acc, vf9, vf2
c->vmadd_bc(DEST::xyzw, BC::w, vf11, vf10, vf2); // vmaddw.xyzw vf11, vf10, vf2
c->vmula_bc(DEST::xyzw, BC::x, vf7, vf3); // vmulax.xyzw acc, vf7, vf3
c->vmadda_bc(DEST::xyzw, BC::y, vf8, vf3); // vmadday.xyzw acc, vf8, vf3
c->vmadda_bc(DEST::xyzw, BC::z, vf9, vf3); // vmaddaz.xyzw acc, vf9, vf3
c->vmadd_bc(DEST::xyzw, BC::w, vf12, vf10, vf3); // vmaddw.xyzw vf12, vf10, vf3
c->vmula_bc(DEST::xyzw, BC::x, vf7, vf4); // vmulax.xyzw acc, vf7, vf4
c->vmadda_bc(DEST::xyzw, BC::y, vf8, vf4); // vmadday.xyzw acc, vf8, vf4
c->vmadda_bc(DEST::xyzw, BC::z, vf9, vf4); // vmaddaz.xyzw acc, vf9, vf4
c->vmadd_bc(DEST::xyzw, BC::w, vf13, vf10, vf4); // vmaddw.xyzw vf13, vf10, vf4
c->vmula_bc(DEST::xyzw, BC::x, vf7, vf15); // vmulax.xyzw acc, vf7, vf15
c->vmadda_bc(DEST::xyzw, BC::y, vf8, vf15); // vmadday.xyzw acc, vf8, vf15
c->vmadda_bc(DEST::xyzw, BC::z, vf9, vf15); // vmaddaz.xyzw acc, vf9, vf15
c->vmadd_bc(DEST::xyzw, BC::w, vf14, vf10, vf0); // vmaddw.xyzw vf14, vf10, vf0
c->sqc2(vf11, 0, a2); // sqc2 vf11, 0(a2)
c->sqc2(vf12, 16, a2); // sqc2 vf12, 16(a2)
c->sqc2(vf13, 32, a2); // sqc2 vf13, 32(a2)
c->sqc2(vf14, 48, a2); // sqc2 vf14, 48(a2)
// jr ra // jr ra
// nop // sll r0, r0, 0
goto end_of_function; // return
// jr ra // jr ra
c->daddu(sp, sp, r0); // daddu sp, sp, r0
goto end_of_function; // return
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
end_of_function:
return c->gprs[v0].du64[0];
}
void link() {
gLinkedFunctionTable.reg("cspace<-parented-transformq-joint!", execute, 128);
}
} // namespace cspace_parented_transformq_joint
} // namespace Mips2C::jak3
// add cspace<_parented_transformq_joint::link to the link callback table for the object file.
// FWD DEC:

View File

@ -12,7 +12,6 @@ struct Cache {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
u32 call_addr = 0;
c->daddiu(sp, sp, -48); // daddiu sp, sp, -48
c->sd(ra, 0, sp); // sd ra, 0(sp)
c->daddiu(t0, sp, 16); // daddiu t0, sp, 16
@ -158,7 +157,6 @@ struct Cache {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
u32 call_addr = 0;
c->daddiu(sp, sp, -48); // daddiu sp, sp, -48
c->daddiu(v1, sp, 16); // daddiu v1, sp, 16
// nop // sll r0, r0, 0
@ -294,7 +292,6 @@ namespace add_light_sphere_to_light_group {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
u32 call_addr = 0;
bool cop1_bc = false;
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
@ -700,7 +697,6 @@ namespace light_hash_get_bucket_index {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
u32 call_addr = 0;
c->daddiu(sp, sp, -32); // daddiu sp, sp, -32
c->daddiu(v1, sp, 16); // daddiu v1, sp, 16
// nop // sll r0, r0, 0

View File

@ -0,0 +1,188 @@
//--------------------------MIPS2C---------------------
// clang-format off
#include "game/mips2c/mips2c_private.h"
#include "game/kernel/jak3/kscheme.h"
using ::jak3::intern_from_c;
namespace Mips2C::jak3 {
namespace adgif_shader_texture_with_update {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
c->ld(a2, 16, a0); // ld a2, 16(a0)
c->addiu(v1, r0, 256); // addiu v1, r0, 256
c->andi(a2, a2, 513); // andi a2, a2, 513
c->mtc1(f0, v1); // mtc1 f0, v1
c->cvtsw(f0, f0); // cvt.s.w f0, f0
c->lbu(v1, 4, a1); // lbu v1, 4(a1)
c->lwc1(f1, 44, a1); // lwc1 f1, 44(a1)
c->daddiu(v1, v1, -1); // daddiu v1, v1, -1
c->divs(f0, f0, f1); // div.s f0, f0, f1
c->dsll(v1, v1, 2); // dsll v1, v1, 2
c->or_(a2, a2, v1); // or a2, a2, v1
c->lbu(v1, 7, a1); // lbu v1, 7(a1)
c->dsll(v1, v1, 19); // dsll v1, v1, 19
c->lbu(a3, 5, a1); // lbu a3, 5(a1)
c->or_(a2, a2, v1); // or a2, a2, v1
c->dsll(a3, a3, 5); // dsll a3, a3, 5
c->or_(a2, a2, a3); // or a2, a2, a3
c->ld(t1, 0, a0); // ld t1, 0(a0)
c->dsll(t1, t1, 27); // dsll t1, t1, 27
c->lbu(v1, 6, a1); // lbu v1, 6(a1)
c->dsra32(t1, t1, 30); // dsra32 t1, t1, 30
c->dsll(v1, v1, 20); // dsll v1, v1, 20
c->dsll32(t1, t1, 3); // dsll32 t1, t1, 3
c->lhu(a3, 10, a1); // lhu a3, 10(a1)
c->or_(t1, t1, v1); // or t1, t1, v1
c->lbu(v1, 26, a1); // lbu v1, 26(a1)
c->or_(t1, t1, a3); // or t1, t1, a3
c->dsll(v1, v1, 14); // dsll v1, v1, 14
c->or_(t1, t1, v1); // or t1, t1, v1
c->lhu(v1, 0, a1); // lhu v1, 0(a1)
c->plzcw(v1, v1); // plzcw v1, v1
c->addiu(t0, r0, 30); // addiu t0, r0, 30
c->subu(v1, t0, v1); // subu v1, t0, v1
c->lhu(a3, 2, a1); // lhu a3, 2(a1)
c->dsll(v1, v1, 26); // dsll v1, v1, 26
c->plzcw(a3, a3); // plzcw a3, a3
c->or_(t1, t1, v1); // or t1, t1, v1
c->subu(a3, t0, a3); // subu a3, t0, a3
c->dsll(a3, a3, 30); // dsll a3, a3, 30
c->addiu(v1, r0, 1); // addiu v1, r0, 1
c->or_(t1, t1, a3); // or t1, t1, a3
c->dsll32(v1, v1, 2); // dsll32 v1, v1, 2
c->or_(t1, t1, v1); // or t1, t1, v1
c->lhu(v1, 24, a1); // lhu v1, 24(a1)
c->dsll32(v1, v1, 5); // dsll32 v1, v1, 5
c->lhu(a3, 8, a1); // lhu a3, 8(a1)
c->dsll32(a3, a3, 19); // dsll32 a3, a3, 19
c->or_(t1, t1, v1); // or t1, t1, v1
c->or_(t1, t1, a3); // or t1, t1, a3
c->addiu(v1, r0, 1); // addiu v1, r0, 1
c->dsll32(v1, v1, 29); // dsll32 v1, v1, 29
c->cvtws(f0, f0); // cvt.w.s f0, f0
c->or_(t1, t1, v1); // or t1, t1, v1
c->mfc1(v1, f0); // mfc1 v1, f0
c->sd(t1, 0, a0); // sd t1, 0(a0)
c->plzcw(a3, v1); // plzcw a3, v1
c->subu(a3, t0, a3); // subu a3, t0, a3
c->lbu(t0, 7, a1); // lbu t0, 7(a1)
c->daddiu(t0, t0, -1); // daddiu t0, t0, -1
// nop // sll r0, r0, 0
bc = c->sgpr64(t0) == 0; // beq t0, r0, L43
// nop // sll r0, r0, 0
if (bc) {goto block_5;} // branch non-likely
c->daddiu(t0, a3, -4); // daddiu t0, a3, -4
c->dsll(a3, a3, 4); // dsll a3, a3, 4
bc = ((s64)c->sgpr64(t0)) < 0; // bltz t0, L41
c->daddiu(a3, a3, -175); // daddiu a3, a3, -175
if (bc) {goto block_3;} // branch non-likely
//beq r0, r0, L42 // beq r0, r0, L42
c->dsrav(t0, v1, t0); // dsrav t0, v1, t0
goto block_4; // branch always
block_3:
c->dsubu(t0, r0, t0); // dsubu t0, r0, t0
c->dsllv(t0, v1, t0); // dsllv t0, v1, t0
block_4:
c->andi(t0, t0, 15); // andi t0, t0, 15
// nop // sll r0, r0, 0
//beq r0, r0, L46 // beq r0, r0, L46
c->daddu(a3, a3, t0); // daddu a3, a3, t0
goto block_9; // branch always
block_5:
c->daddiu(t0, a3, -5); // daddiu t0, a3, -5
c->dsll(a3, a3, 5); // dsll a3, a3, 5
bc = ((s64)c->sgpr64(t0)) < 0; // bltz t0, L44
c->daddiu(a3, a3, -350); // daddiu a3, a3, -350
if (bc) {goto block_7;} // branch non-likely
//beq r0, r0, L45 // beq r0, r0, L45
c->dsrav(t0, v1, t0); // dsrav t0, v1, t0
goto block_8; // branch always
block_7:
c->dsubu(t0, r0, t0); // dsubu t0, r0, t0
c->dsllv(t0, v1, t0); // dsllv t0, v1, t0
block_8:
c->andi(t0, t0, 31); // andi t0, t0, 31
// nop // sll r0, r0, 0
c->daddu(a3, a3, t0); // daddu a3, a3, t0
// nop // sll r0, r0, 0
block_9:
c->andi(a3, a3, 4095); // andi a3, a3, 4095
c->lhu(t1, 12, a1); // lhu t1, 12(a1)
c->dsll32(a3, a3, 0); // dsll32 a3, a3, 0
c->lbu(v1, 27, a1); // lbu v1, 27(a1)
c->or_(a2, a2, a3); // or a2, a2, a3
c->dsll(v1, v1, 14); // dsll v1, v1, 14
c->sd(a2, 16, a0); // sd a2, 16(a0)
c->or_(a2, t1, v1); // or a2, t1, v1
c->lhu(v1, 14, a1); // lhu v1, 14(a1)
// nop // sll r0, r0, 0
c->lbu(a3, 28, a1); // lbu a3, 28(a1)
c->dsll(v1, v1, 20); // dsll v1, v1, 20
c->or_(a2, a2, v1); // or a2, a2, v1
c->dsll32(a3, a3, 2); // dsll32 a3, a3, 2
c->or_(a2, a2, a3); // or a2, a2, a3
c->lhu(v1, 16, a1); // lhu v1, 16(a1)
c->lbu(a3, 29, a1); // lbu a3, 29(a1)
c->dsll32(v1, v1, 8); // dsll32 v1, v1, 8
c->or_(a2, a2, v1); // or a2, a2, v1
c->dsll32(a3, a3, 22); // dsll32 a3, a3, 22
c->or_(a2, a2, a3); // or a2, a2, a3
c->lbu(t0, 4, a1); // lbu t0, 4(a1)
c->daddiu(t0, t0, -5); // daddiu t0, t0, -5
c->sd(a2, 32, a0); // sd a2, 32(a0)
bc = ((s64)c->sgpr64(t0)) < 0; // bltz t0, L47
c->lbu(a3, 30, a1); // lbu a3, 30(a1)
if (bc) {goto block_11;} // branch non-likely
c->lhu(a2, 18, a1); // lhu a2, 18(a1)
c->dsll(a3, a3, 14); // dsll a3, a3, 14
c->or_(a2, a2, a3); // or a2, a2, a3
c->lhu(v1, 20, a1); // lhu v1, 20(a1)
c->dsll(v1, v1, 20); // dsll v1, v1, 20
c->lbu(a3, 31, a1); // lbu a3, 31(a1)
c->or_(a2, a2, v1); // or a2, a2, v1
c->dsll32(a3, a3, 2); // dsll32 a3, a3, 2
c->or_(a2, a2, a3); // or a2, a2, a3
c->lhu(v1, 22, a1); // lhu v1, 22(a1)
c->dsll32(v1, v1, 8); // dsll32 v1, v1, 8
c->lbu(a3, 32, a1); // lbu a3, 32(a1)
c->or_(a2, a2, v1); // or a2, a2, v1
c->dsll32(a3, a3, 22); // dsll32 a3, a3, 22
c->or_(a2, a2, a3); // or a2, a2, a3
c->addiu(v1, r0, 54); // addiu v1, r0, 54
c->sd(a2, 64, a0); // sd a2, 64(a0)
// nop // sll r0, r0, 0
c->sw(v1, 72, a0); // sw v1, 72(a0)
// nop // sll r0, r0, 0
block_11:
c->mov64(v0, a0); // or v0, a0, r0
//jr ra // jr ra
c->daddu(sp, sp, r0); // daddu sp, sp, r0
goto end_of_function; // return
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
end_of_function:
return c->gprs[v0].du64[0];
}
void link() {
gLinkedFunctionTable.reg("adgif-shader<-texture-with-update!", execute, 128);
}
} // namespace adgif_shader<_texture_with_update
} // namespace Mips2C

View File

@ -7,6 +7,7 @@
#include "game/kernel/common/kscheme.h"
#include "game/kernel/jak1/kscheme.h"
#include "game/kernel/jak2/kscheme.h"
#include "game/kernel/jak3/kscheme.h"
#include "game/runtime.h"
extern "C" {
@ -281,6 +282,14 @@ namespace method_9_font_work { extern void link(); }
namespace draw_string_asm { extern void link(); }
namespace get_string_length { extern void link(); }
namespace method_9_prim_strip { extern void link(); }
namespace adgif_shader_texture_with_update { extern void link(); }
namespace moving_sphere_triangle_intersect { extern void link(); }
namespace collide_do_primitives { extern void link(); }
namespace cspace_parented_transformq_joint { extern void link(); }
namespace foreground_check_longest_edge_asm { extern void link(); }
namespace foreground_merc { extern void link(); }
namespace foreground_generic_merc { extern void link(); }
}
// clang-format on
@ -466,7 +475,14 @@ PerGameVersion<std::unordered_map<std::string, std::vector<void (*)()>>> gMips2C
jak3::generic_no_light_proc::link}},
{"font",
{jak3::method_9_font_work::link, jak3::draw_string_asm::link, jak3::get_string_length::link}},
{"prim", {jak3::method_9_prim_strip::link}}}};
{"texture", {jak3::adgif_shader_texture_with_update::link}},
{"collide-func",
{jak3::moving_sphere_triangle_intersect::link, jak3::collide_do_primitives::link}},
{"prim", {jak3::method_9_prim_strip::link}},
{"joint", {jak3::cspace_parented_transformq_joint::link}},
{"foreground",
{jak3::foreground_check_longest_edge_asm::link, jak3::foreground_generic_merc::link,
jak3::foreground_merc::link}}}};
void LinkedFunctionTable::reg(const std::string& name, u64 (*exec)(void*), u32 stack_size) {
const auto& it = m_executes.insert({name, {exec, Ptr<u8>()}});
@ -488,6 +504,11 @@ void LinkedFunctionTable::reg(const std::string& name, u64 (*exec)(void*), u32 s
s7.offset + jak2_symbols::FIX_SYM_GLOBAL_HEAP,
::jak2::u32_in_fixed_sym(jak2_symbols::FIX_SYM_FUNCTION_TYPE), 0x40, UNKNOWN_PP));
break;
case GameVersion::Jak3:
jump_to_asm = Ptr<u8>(::jak3::alloc_heap_object(
s7.offset + jak3_symbols::FIX_SYM_GLOBAL_HEAP,
::jak3::u32_in_fixed_sym(jak3_symbols::FIX_SYM_FUNCTION_TYPE), 0x40, UNKNOWN_PP));
break;
default:
ASSERT(false);
}

View File

@ -2223,7 +2223,7 @@
;; make-joint-jump-tables: not needed
;; calc-animation-from-spr: not needed
(def-mips2c calc-animation-from-spr (function joint-anim-frame int none))
;; (def-mips2c calc-animation-from-spr (function joint-anim-frame int none))
(defun create-interpolated-joint-animation-frame ((dst joint-anim-frame) (num-joints int) (jc joint-control))
"Compute the entire joint frame by evaluating the blend tree, decompressing animations, and blending them."

View File

@ -45,4 +45,6 @@
(none)
)
(cam-start #f)
(format 0 "Skipping cam-start!!~%")
(format #t "Skipping cam-start!!~%")
;; (cam-start #f)

View File

@ -13,6 +13,13 @@
;; DECOMP BEGINS
;; TODO: remove this - just a stub.
(defun find-free-eye-index ((a int) (b string) (c int))
0)
(defun get-eye-block ((a int) (c int))
0)
(deftype eye (structure)
"Data for a single eye."
((data vector 2 :inline)

View File

@ -8,3 +8,8 @@
;; DECOMP BEGINS
;; TODO: remove these stubs
(defun clear-mood-context ((arg0 mood-context))
(format 0 "no clear-mood-context~%")
#f
)

View File

@ -35,3 +35,9 @@
;; DECOMP BEGINS
;; TODO: stub
(kmemopen global "part-tables")
(define *part-id-table* (new 'global 'boxed-array sparticle-launcher 5500))
(define *part-group-id-table* (new 'global 'boxed-array sparticle-launch-group 1700))
(define *sp-temp* 0.0)
(kmemclose)

View File

@ -1520,6 +1520,8 @@
(defmethod upload-now! ((this texture-page) (mode tex-upload-mode))
"Upload a texture to VRAM immediately, wait for DMA to finish."
(format 0 "TODO: upload-now!~%")
(let ((gp-0 *txt-dma-list*))
(let ((v1-0 gp-0))
(set! (-> v1-0 base) (-> v1-0 data))
@ -1534,7 +1536,10 @@
(set! (-> (the-as (pointer uint64) a0-7) 1) (the-as uint 0))
(set! (-> v1-6 base) (&+ a0-7 16))
)
(dma-buffer-send-chain (the-as dma-bank-source #x1000a000) gp-0)
;; the actual send
(#unless PC_PORT
(dma-buffer-send-chain (the-as dma-bank-source #x1000a000) gp-0)
)
)
(dma-sync (the-as pointer #x1000a000) 0 0)
(none)

View File

@ -20,7 +20,7 @@
(define *kernel-boot-message* 'play)
(start-debug "loading GAME.DGO~%")
(load-package "game" global)
(play-boot)
;; (play-boot)
)
)
@ -3744,6 +3744,8 @@
(none)
)
(format 0 "about to start level stuff...~%")
(when (zero? (-> *level* level0 art-group))
(kmemopen global "level-struct")
(let ((gp-0 *level*))
@ -3789,3 +3791,5 @@
)
(kmemclose)
)
(format 0 "done level stuff...~%")

View File

@ -23,7 +23,7 @@
some, but not all, functions assume that a matrix is an affine transform.
others assume that the rotation has no scale or shear (and that its inverse is its transpose)."
((data float 16)
(vector vector 4 :overlay-at (-> data 0))
(vector vector 4 :inline :overlay-at (-> data 0))
(quad uint128 4 :overlay-at (-> data 0))
(rvec vector :inline :overlay-at (-> data 0))
(uvec vector :inline :overlay-at (-> data 4))

View File

@ -793,8 +793,6 @@ void Debugger::read_symbol_table_jak3() {
continue;
}
printf("got %s\n", str.c_str());
// update maps
if (m_symbol_name_to_offset_map.find(str) != m_symbol_name_to_offset_map.end()) {
if (str == "asize-of-basic-func") {

View File

@ -7,12 +7,12 @@
some, but not all, functions assume that a matrix is an affine transform.
others assume that the rotation has no scale or shear (and that its inverse is its transpose)."
((data float 16)
(vector vector 4 :overlay-at (-> data 0))
(quad uint128 4 :overlay-at (-> data 0))
(rvec vector :inline :overlay-at (-> data 0))
(uvec vector :inline :overlay-at (-> data 4))
(fvec vector :inline :overlay-at (-> data 8))
(trans vector :inline :overlay-at (-> data 12))
(vector vector 4 :inline :overlay-at (-> data 0))
(quad uint128 4 :overlay-at (-> data 0))
(rvec vector :inline :overlay-at (-> data 0))
(uvec vector :inline :overlay-at (-> data 4))
(fvec vector :inline :overlay-at (-> data 8))
(trans vector :inline :overlay-at (-> data 12))
)
(:methods
(transform-vectors! (_type_ (inline-array vector) (inline-array vector) int) none)
@ -145,7 +145,3 @@ and how they were originally packed (for example, in tie/shrub)."
;; failed to figure out what this is:
0