mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 21:27:52 -04:00
543 lines
17 KiB
C++
543 lines
17 KiB
C++
#include "kprint.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include "common/listener_common.h"
|
|
#include "common/symbols.h"
|
|
|
|
#include "game/kernel/common/Ptr.h"
|
|
#include "game/kernel/common/fileio.h"
|
|
#include "game/kernel/common/klisten.h"
|
|
#include "game/kernel/common/kprint.h"
|
|
#include "game/kernel/common/kscheme.h"
|
|
#include "game/kernel/jak1/kscheme.h"
|
|
#include "game/sce/sif_ee.h"
|
|
|
|
using namespace jak1_symbols;
|
|
|
|
///////////
|
|
// SDATA
|
|
///////////
|
|
|
|
/*!
|
|
* 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. The Ghidra analyzer completely fails on it, so this is done by hand.
|
|
*
|
|
* To make this work correctly from GOAL with up to 8 arguments, there is an assembly function
|
|
* defined in format_wrapper.nasm that places the GOAL arguments on the stack and calls this
|
|
* format_impl function with a single argument that is a pointer to the argument array.
|
|
*/
|
|
s32 format_impl_jak1(uint64_t* args) {
|
|
using namespace jak1;
|
|
// 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) >> 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 10
|
|
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 '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;
|
|
u64 in = arg_regs[arg_reg_idx++];
|
|
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;
|
|
u64 in = arg_regs[arg_reg_idx++];
|
|
|
|
// if it's a string
|
|
if (((in & 0x7) == 0x4) && *Ptr<u32>(in - 4) == *(s7 + FIX_SYM_STRING_TYPE)) {
|
|
cprintf("%s", Ptr<char>(in).c() + 4);
|
|
} else {
|
|
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) {
|
|
print_object(in);
|
|
} else {
|
|
auto sym = find_symbol_from_c(argument_data[0].data);
|
|
if (sym.offset) {
|
|
Ptr<Type> type = *sym.cast<Ptr<Type>>();
|
|
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(argument_data[0].data);
|
|
if (sym.offset) {
|
|
Ptr<Type> type = *sym.cast<Ptr<Type>>();
|
|
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]);
|
|
ASSERT(false);
|
|
break;
|
|
}
|
|
format_ptr++;
|
|
} else {
|
|
// got normal char, just copy it
|
|
*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) {
|
|
// do nothing, we're done
|
|
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)) {
|
|
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_TYPE)) {
|
|
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;
|
|
}
|