mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
[decompiler] bitfield support for sound-name
(#582)
* fix 64-bit fields in 128-bit bitfields * support sound-name * fix merge * support some more sound stuff in overlord
This commit is contained in:
parent
877e3d161c
commit
8faded6400
|
@ -68,7 +68,10 @@ Interpreter::Interpreter() {
|
|||
{"type?", &Interpreter::eval_type},
|
||||
{"current-method-type", &Interpreter::eval_current_method_type},
|
||||
{"fmt", &Interpreter::eval_format},
|
||||
{"error", &Interpreter::eval_error}};
|
||||
{"error", &Interpreter::eval_error},
|
||||
{"string-ref", &Interpreter::eval_string_ref},
|
||||
{"string-length", &Interpreter::eval_string_length},
|
||||
{"ash", &Interpreter::eval_ash}};
|
||||
|
||||
string_to_type = {{"empty-list", ObjectType::EMPTY_LIST},
|
||||
{"integer", ObjectType::INTEGER},
|
||||
|
@ -1042,6 +1045,8 @@ IntType Interpreter::number_to_integer(const Object& obj) {
|
|||
return obj.integer_obj.value;
|
||||
case ObjectType::FLOAT:
|
||||
return (int64_t)obj.float_obj.value;
|
||||
case ObjectType::CHAR:
|
||||
return (int8_t)obj.char_obj.value;
|
||||
default:
|
||||
throw_eval_error(obj, "object cannot be interpreted as a number!");
|
||||
}
|
||||
|
@ -1527,4 +1532,44 @@ Object Interpreter::eval_error(const Object& form,
|
|||
throw_eval_error(form, "Error: " + args.unnamed.at(0).as_string()->data);
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
Object Interpreter::eval_string_ref(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING, ObjectType::INTEGER}, {});
|
||||
auto str = args.unnamed.at(0).as_string();
|
||||
auto idx = args.unnamed.at(1).as_int();
|
||||
if ((size_t)idx >= str->data.size()) {
|
||||
throw_eval_error(form, fmt::format("String index {} out of range for string of size {}", idx,
|
||||
str->data.size()));
|
||||
}
|
||||
return Object::make_char(str->data.at(idx));
|
||||
}
|
||||
|
||||
Object Interpreter::eval_string_length(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
auto str = args.unnamed.at(0).as_string();
|
||||
return Object::make_integer(str->data.length());
|
||||
}
|
||||
|
||||
Object Interpreter::eval_ash(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
auto val = number_to_integer(args.unnamed.at(0));
|
||||
auto sa = number_to_integer(args.unnamed.at(1));
|
||||
if (sa >= 0 && sa < 64) {
|
||||
return Object::make_integer(val << sa);
|
||||
} else if (sa > -64) {
|
||||
return Object::make_integer(val >> sa);
|
||||
} else {
|
||||
throw_eval_error(form, fmt::format("Shift amount {} is out of range", sa));
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
} // namespace goos
|
||||
|
|
|
@ -186,6 +186,15 @@ class Interpreter {
|
|||
Object eval_error(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
Object eval_string_ref(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
Object eval_string_length(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
Object eval_ash(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
|
||||
// specials
|
||||
Object eval_define(const Object& form,
|
||||
|
|
|
@ -25,3 +25,7 @@ constexpr u32 TX_PAGE_VERSION = 7;
|
|||
// GOAL kernel version (OpenGOAL changes this version from the game's version)
|
||||
constexpr int KERNEL_VERSION_MAJOR = 2;
|
||||
constexpr int KERNEL_VERSION_MINOR = 0;
|
||||
|
||||
// OVERLORD version returned by an RPC
|
||||
constexpr int IRX_VERSION_MAJOR = 2;
|
||||
constexpr int IRX_VERSION_MINOR = 0;
|
|
@ -3091,7 +3091,7 @@ void push_asm_pcpyud_to_stack(const AsmOp* op,
|
|||
if (bitfield_info && possible_r0->reg() == Register(Reg::GPR, Reg::R0)) {
|
||||
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
|
||||
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
|
||||
read_elt->push_pcpyud();
|
||||
read_elt->push_pcpyud(env.dts->ts, pool, env);
|
||||
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, read_elt), true,
|
||||
env.get_variable_type(*dst, true));
|
||||
} else {
|
||||
|
|
|
@ -144,6 +144,9 @@ BitfieldAccessElement::BitfieldAccessElement(Form* base_value, const TypeSpec& t
|
|||
}
|
||||
|
||||
goos::Object BitfieldAccessElement::to_form_internal(const Env& env) const {
|
||||
if (m_current_result) {
|
||||
return m_current_result->to_form(env);
|
||||
}
|
||||
return pretty_print::build_list("incomplete-bitfield-access", m_base->to_form(env));
|
||||
}
|
||||
|
||||
|
@ -164,11 +167,11 @@ void BitfieldAccessElement::get_modified_regs(RegSet& regs) const {
|
|||
m_base->get_modified_regs(regs);
|
||||
}
|
||||
|
||||
const BitField& find_field(const TypeSystem& ts,
|
||||
const BitFieldType* type,
|
||||
int start_bit,
|
||||
int size,
|
||||
std::optional<bool> looking_for_unsigned) {
|
||||
std::optional<BitField> try_find_field(const TypeSystem& ts,
|
||||
const BitFieldType* type,
|
||||
int start_bit,
|
||||
int size,
|
||||
std::optional<bool> looking_for_unsigned) {
|
||||
for (auto& field : type->fields()) {
|
||||
if (field.size() == size && field.offset() == start_bit) {
|
||||
// the GOAL compiler sign extended floats.
|
||||
|
@ -195,9 +198,22 @@ const BitField& find_field(const TypeSystem& ts,
|
|||
}
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(
|
||||
fmt::format("Unmatched field: start bit {}, size {}, signed? {}, type {}", start_bit, size,
|
||||
!looking_for_unsigned, type->get_name()));
|
||||
return {};
|
||||
}
|
||||
|
||||
BitField find_field(const TypeSystem& ts,
|
||||
const BitFieldType* type,
|
||||
int start_bit,
|
||||
int size,
|
||||
std::optional<bool> looking_for_unsigned) {
|
||||
auto result = try_find_field(ts, type, start_bit, size, looking_for_unsigned);
|
||||
if (!result) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Unmatched field: start bit {}, size {}, signed? {}, type {}", start_bit, size,
|
||||
!looking_for_unsigned, type->get_name()));
|
||||
} else {
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -238,7 +254,7 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
|||
int right = mr.maps.ints.at(2);
|
||||
int size = 64 - left;
|
||||
int offset = left - right;
|
||||
auto& f = find_field(ts, type, offset, size, {});
|
||||
auto f = find_field(ts, type, offset, size, {});
|
||||
BitFieldDef def;
|
||||
def.value = value;
|
||||
def.field_name = f.name();
|
||||
|
@ -261,7 +277,7 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
|||
int right = *power_of_two;
|
||||
int size = 64 - left;
|
||||
int offset = left - right;
|
||||
auto& f = find_field(ts, type, offset, size, {});
|
||||
auto f = find_field(ts, type, offset, size, {});
|
||||
BitFieldDef def;
|
||||
def.value = value;
|
||||
def.field_name = f.name();
|
||||
|
@ -281,7 +297,7 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
|||
int right = 0;
|
||||
int size = 64 - left;
|
||||
int offset = left - right;
|
||||
auto& f = find_field(ts, type, offset, size, {});
|
||||
auto f = find_field(ts, type, offset, size, {});
|
||||
BitFieldDef def;
|
||||
def.value = value;
|
||||
def.field_name = f.name();
|
||||
|
@ -296,7 +312,7 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
|||
auto value = mr_sllv.maps.forms.at(0);
|
||||
int size = 32;
|
||||
int offset = 0;
|
||||
auto& f = find_field(ts, type, offset, size, {});
|
||||
auto f = find_field(ts, type, offset, size, {});
|
||||
BitFieldDef def;
|
||||
def.value = value;
|
||||
def.field_name = f.name();
|
||||
|
@ -309,11 +325,23 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
|||
|
||||
} // namespace
|
||||
|
||||
void BitfieldAccessElement::push_pcpyud() {
|
||||
void BitfieldAccessElement::push_pcpyud(const TypeSystem& ts, FormPool& pool, const Env& /*env*/) {
|
||||
if (!m_steps.empty()) {
|
||||
throw std::runtime_error("Unexpected pcpyud!");
|
||||
}
|
||||
m_got_pcpyud = true;
|
||||
|
||||
// look for a 64-bit field
|
||||
auto type = ts.lookup_type(m_type);
|
||||
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
|
||||
assert(as_bitfield);
|
||||
auto field = try_find_field(ts, as_bitfield, 64, 64, true);
|
||||
if (field) {
|
||||
auto result =
|
||||
pool.alloc_element<DerefElement>(m_base, false, DerefToken::make_field_name(field->name()));
|
||||
result->inline_nested();
|
||||
m_current_result = pool.alloc_single_form(this, result);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -571,6 +599,45 @@ BitFieldDef BitFieldDef::from_constant(const BitFieldConstantDef& constant, Form
|
|||
return bfd;
|
||||
}
|
||||
|
||||
namespace {
|
||||
Form* cast_sound_name(FormPool& pool, const Env& env, Form* in) {
|
||||
auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::PCPYLD),
|
||||
{Matcher::any(1), Matcher::any(0)});
|
||||
auto mr = match(matcher, in);
|
||||
if (!mr.matched) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto hi = mr.maps.forms.at(1);
|
||||
auto lo = mr.maps.forms.at(0);
|
||||
|
||||
auto hi_int = get_goal_integer_constant(hi, env);
|
||||
auto lo_int = get_goal_integer_constant(lo, env);
|
||||
if (!hi_int || !lo_int) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char name[17];
|
||||
memcpy(name, &lo_int.value(), 8);
|
||||
memcpy(name + 8, &hi_int.value(), 8);
|
||||
name[16] = '\0';
|
||||
|
||||
bool got_zero = false;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (name[i] == 0) {
|
||||
got_zero = true;
|
||||
} else {
|
||||
if (got_zero) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pool.alloc_single_element_form<ConstantTokenElement>(
|
||||
nullptr, fmt::format("(static-sound-name \"{}\")", name));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/*!
|
||||
* Cast the given form to a bitfield.
|
||||
* If the form could have been a (new 'static 'bitfieldtype ...) it will attempt to generate this.
|
||||
|
@ -581,6 +648,15 @@ Form* cast_to_bitfield(const BitFieldType* type_info,
|
|||
const Env& env,
|
||||
Form* in) {
|
||||
in = strip_int_or_uint_cast(in);
|
||||
|
||||
if (type_info->get_name() == "sound-name") {
|
||||
auto as_sound_name = cast_sound_name(pool, env, in);
|
||||
if (as_sound_name) {
|
||||
return as_sound_name;
|
||||
}
|
||||
// just do a normal cast if that failed.
|
||||
return pool.alloc_single_element_form<CastElement>(nullptr, typespec, in);
|
||||
}
|
||||
// check if it's just a constant:
|
||||
auto in_as_atom = form_as_atom(in);
|
||||
if (in_as_atom && in_as_atom->is_int()) {
|
||||
|
|
|
@ -100,11 +100,15 @@ class BitfieldAccessElement : public FormElement {
|
|||
const TypeSystem& ts,
|
||||
FormPool& pool,
|
||||
const Env& env);
|
||||
void push_pcpyud();
|
||||
void push_pcpyud(const TypeSystem& ts, FormPool& pool, const Env& env);
|
||||
|
||||
private:
|
||||
bool m_got_pcpyud = false;
|
||||
Form* m_base = nullptr;
|
||||
|
||||
// if we aren't sure if we are done or not, store the result here.
|
||||
Form* m_current_result = nullptr;
|
||||
|
||||
TypeSpec m_type;
|
||||
std::vector<BitfieldManip> m_steps;
|
||||
};
|
||||
|
@ -190,9 +194,9 @@ Form* cast_to_bitfield_enum(const EnumType* type_info, FormPool& pool, const Env
|
|||
|
||||
std::optional<u64> get_goal_integer_constant(Form* in, const Env&);
|
||||
|
||||
const BitField& find_field(const TypeSystem& ts,
|
||||
const BitFieldType* type,
|
||||
int start_bit,
|
||||
int size,
|
||||
std::optional<bool> looking_for_unsigned);
|
||||
BitField find_field(const TypeSystem& ts,
|
||||
const BitFieldType* type,
|
||||
int start_bit,
|
||||
int size,
|
||||
std::optional<bool> looking_for_unsigned);
|
||||
} // namespace decompiler
|
||||
|
|
|
@ -161,3 +161,5 @@
|
|||
- Fixed a bug where `''a` or similar repeated reader macros would generate a reader error.
|
||||
- Fixed a bug where reader macros without a following form would trigger an assert.
|
||||
- It is now possible to take the address of a lexical variable. The variable will be spilled to the stack automatically.
|
||||
- GOOS supports `string-ref`, `string-length`, `ash`, and characters can now be treated as a signed 8-bit number
|
||||
- Fixed a bug where saved xmm registers might be clobbered when calling a C++ function that wasn't `format`.
|
|
@ -5,9 +5,6 @@
|
|||
* Types used for the DGO Remote Procedure Call between the EE and the IOP
|
||||
*/
|
||||
|
||||
#ifndef JAK1_DGO_RPC_TYPES_H
|
||||
#define JAK1_DGO_RPC_TYPES_H
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
constexpr int DGO_RPC_ID = 0xdeb4;
|
||||
|
@ -29,5 +26,3 @@ struct RPC_Dgo_Cmd {
|
|||
uint32_t buffer_heap_top;
|
||||
char name[16];
|
||||
};
|
||||
|
||||
#endif // JAK1_DGO_RPC_TYPES_H
|
||||
|
|
|
@ -5,10 +5,5 @@
|
|||
* Types used for the Loader Remote Procedure Call between the EE and the IOP
|
||||
*/
|
||||
|
||||
#ifndef JAK1_LOADER_RPC_TYPES_H
|
||||
#define JAK1_LOADER_RPC_TYPES_H
|
||||
|
||||
constexpr int LOADER_RPC_ID = 0xdeb2;
|
||||
constexpr int LOADER_RPC_CHANNEL = 1;
|
||||
|
||||
#endif // JAK1_LOADER_RPC_TYPES_H
|
||||
|
|
|
@ -6,6 +6,44 @@
|
|||
|
||||
SECTION .text
|
||||
|
||||
;; Call C++ code on linux, from GOAL, using Linux calling convention.
|
||||
global _arg_call_linux
|
||||
_arg_call_linux:
|
||||
pop rax
|
||||
push r10
|
||||
push r11
|
||||
sub rsp, 8
|
||||
|
||||
; xmm stuff
|
||||
sub rsp, 128
|
||||
movaps [rsp], xmm8
|
||||
movaps [rsp + 16], xmm9
|
||||
movaps [rsp + 32], xmm10
|
||||
movaps [rsp + 48], xmm11
|
||||
movaps [rsp + 64], xmm12
|
||||
movaps [rsp + 80], xmm13
|
||||
movaps [rsp + 96], xmm14
|
||||
movaps [rsp + 112], xmm15
|
||||
|
||||
call rax
|
||||
|
||||
movaps xmm8, [rsp]
|
||||
movaps xmm9, [rsp + 16]
|
||||
movaps xmm10, [rsp + 32]
|
||||
movaps xmm11, [rsp + 48]
|
||||
movaps xmm12, [rsp + 64]
|
||||
movaps xmm13, [rsp + 80]
|
||||
movaps xmm14, [rsp + 96]
|
||||
movaps xmm15, [rsp + 112]
|
||||
add rsp, 128
|
||||
|
||||
add rsp, 8
|
||||
pop r11
|
||||
pop r10
|
||||
ret
|
||||
|
||||
|
||||
;; Call C++ code on linux, from GOAL. Pug arguments on the stack and put a pointer to this array in the first arg.
|
||||
;; this function pushes all 8 OpenGOAL registers into a stack array.
|
||||
;; then it calls the function pointed to by rax with a pointer to this array.
|
||||
;; it returns the return value of the called function.
|
||||
|
@ -64,7 +102,7 @@ _stack_call_linux:
|
|||
; return!
|
||||
ret
|
||||
|
||||
;; windows implementation of stack_call
|
||||
;; Call C++ code on windows, from GOAL. Pug arguments on the stack and put a pointer to this array in the first arg.
|
||||
global _stack_call_win32
|
||||
_stack_call_win32:
|
||||
pop rax
|
||||
|
@ -124,6 +162,7 @@ _stack_call_win32:
|
|||
ret
|
||||
|
||||
;; The _call_goal_asm function is used to call a GOAL function from C.
|
||||
;; It calls on the parent stack, which is a bad idea if your stack is not already a GOAL stack.
|
||||
;; It supports up to 3 arguments and a return value.
|
||||
;; This should be called with the arguments:
|
||||
;; - first goal arg
|
||||
|
@ -163,6 +202,7 @@ _call_goal_asm_linux:
|
|||
pop r13
|
||||
ret
|
||||
|
||||
;; Call goal, but switch stacks.
|
||||
global _call_goal_on_stack_asm_linux
|
||||
|
||||
_call_goal_on_stack_asm_linux:
|
||||
|
|
|
@ -318,39 +318,44 @@ u64 make_string_from_c(const char* c_str) {
|
|||
return mem;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void _arg_call_linux();
|
||||
}
|
||||
|
||||
/*!
|
||||
* This creates an OpenGOAL function from a C++ function. Only 6 arguments can be accepted.
|
||||
* But calling this function is very fast and doesn't use the stack.
|
||||
* But calling this function is fast. It used to be really fast but wrong.
|
||||
*/
|
||||
Ptr<Function> make_function_from_c_linux(void* func) {
|
||||
// allocate a function object on the global heap
|
||||
auto mem = Ptr<u8>(
|
||||
alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP, *(s7 + FIX_SYM_FUNCTION_TYPE), 0x40));
|
||||
auto f = (uint64_t)func;
|
||||
auto fp = (u8*)&f;
|
||||
auto target_function = (u8*)&f;
|
||||
auto trampoline_function_addr = _arg_call_linux;
|
||||
auto trampoline = (u8*)&trampoline_function_addr;
|
||||
|
||||
int i = 0;
|
||||
// we will put the function address in RAX with a movabs rax, imm8
|
||||
mem.c()[i++] = 0x48;
|
||||
mem.c()[i++] = 0xb8;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
mem.c()[i++] = fp[j];
|
||||
// movabs rax, target_function
|
||||
int offset = 0;
|
||||
mem.c()[offset++] = 0x48;
|
||||
mem.c()[offset++] = 0xb8;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
mem.c()[offset++] = target_function[i];
|
||||
}
|
||||
|
||||
// push r10
|
||||
// push r11
|
||||
// sub rsp, 8
|
||||
// call rax
|
||||
// add rsp, 8
|
||||
// pop r11
|
||||
// pop r10
|
||||
// ret
|
||||
for (auto x : {0x41, 0x52, 0x41, 0x53, 0x48, 0x83, 0xEC, 0x08, 0xFF, 0xD0, 0x48, 0x83, 0xC4, 0x08,
|
||||
0x41, 0x5B, 0x41, 0x5A, 0xC3}) {
|
||||
mem.c()[i++] = x;
|
||||
// push rax
|
||||
mem.c()[offset++] = 0x50;
|
||||
|
||||
// movabs rax, trampoline
|
||||
mem.c()[offset++] = 0x48;
|
||||
mem.c()[offset++] = 0xb8;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
mem.c()[offset++] = trampoline[i];
|
||||
}
|
||||
|
||||
// the C function's ret will return to the caller of this trampoline.
|
||||
// jmp rax
|
||||
mem.c()[offset++] = 0xff;
|
||||
mem.c()[offset++] = 0xe0;
|
||||
// the asm function's ret will return to the caller of this (GOAL code) directlyz.
|
||||
|
||||
// CacheFlush(mem, 0x34);
|
||||
|
||||
|
@ -403,8 +408,6 @@ ret
|
|||
mem.c()[i++] = x;
|
||||
}
|
||||
|
||||
// the C function's ret will return to the caller of this trampoline.
|
||||
|
||||
// CacheFlush(mem, 0x34);
|
||||
|
||||
return mem.cast<Function>();
|
||||
|
|
|
@ -270,6 +270,8 @@ u32 ISOThread() {
|
|||
InitDriver(temp_buffer->get_data()); // unblocks InitISOFS's WaitMbx
|
||||
FreeBuffer(temp_buffer);
|
||||
|
||||
VagCommand* in_progress_vag_command = nullptr;
|
||||
|
||||
// main CD/DVD read loop
|
||||
for (;;) {
|
||||
/////////////////////////////////////
|
||||
|
@ -358,7 +360,27 @@ u32 ISOThread() {
|
|||
load_single_cmd->callback_function = RunDGOStateMachine;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (msg_from_mbx->cmd_id == LOAD_SOUND_BANK) {
|
||||
// if there's an in progress vag command, try again.
|
||||
if (!in_progress_vag_command || !in_progress_vag_command->field_0x3c) {
|
||||
auto buff = TryAllocateBuffer(BUFFER_PAGE_SIZE);
|
||||
if (!buff) {
|
||||
// no buffers, try again.
|
||||
SendMbx(iso_mbx, msg_from_mbx);
|
||||
} else {
|
||||
// todo - actual load here
|
||||
auto* cmd = (SoundBankLoadCommand*)msg_from_mbx;
|
||||
printf("Ignoring request to load sound bank %s\n", cmd->bank_name);
|
||||
FreeBuffer(buff);
|
||||
ReturnMessage(msg_from_mbx);
|
||||
}
|
||||
} else {
|
||||
// just try again...
|
||||
SendMbx(iso_mbx, msg_from_mbx);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
printf("[OVERLORD] Unknown ISOThread message id 0x%x\n", msg_from_mbx->cmd_id);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
* This is a huge mess
|
||||
*/
|
||||
|
||||
#ifndef JAK_V2_ISO_H
|
||||
#define JAK_V2_ISO_H
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "isocommon.h"
|
||||
|
||||
|
@ -16,5 +13,3 @@ void iso_init_globals();
|
|||
FileRecord* FindISOFile(const char* name);
|
||||
u32 GetISOFileLength(FileRecord* f);
|
||||
u32 InitISOFS(const char* fs_mode, const char* loading_screen);
|
||||
|
||||
#endif // JAK_V2_ISO_H
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "iso_api.h"
|
||||
#include "game/sce/iop.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/assert.h"
|
||||
#include "sbank.h"
|
||||
|
||||
using namespace iop;
|
||||
|
||||
|
@ -65,3 +67,20 @@ s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length,
|
|||
}
|
||||
return cmd.length_to_copy;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Send a command to the ISO thread to load a sound bank. This will sleep the calling thread
|
||||
* until the load has completed.
|
||||
*/
|
||||
void LoadSoundBank(const char* bank_name, SoundBank* bank) {
|
||||
(void)bank;
|
||||
assert(strlen(bank_name) < 16);
|
||||
SoundBankLoadCommand cmd;
|
||||
cmd.cmd_id = LOAD_SOUND_BANK;
|
||||
cmd.messagebox_to_reply = 0;
|
||||
cmd.thread_id = GetThreadId();
|
||||
strcpy(cmd.bank_name, bank_name);
|
||||
cmd.bank = bank;
|
||||
SendMbx(iso_mbx, &cmd);
|
||||
SleepThread(); // wait for finish.
|
||||
}
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include "isocommon.h"
|
||||
|
||||
struct SoundBank;
|
||||
|
||||
s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length);
|
||||
s32 LoadISOFileToEE(FileRecord* file, uint32_t ee_addr, uint32_t length);
|
||||
s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length, uint32_t offset);
|
||||
void LoadSoundBank(const char* bank_name, SoundBank* bank);
|
|
@ -35,7 +35,6 @@ VagCommand vag_cmds[N_VAG_CMDS];
|
|||
|
||||
static s32 sSema;
|
||||
|
||||
IsoBufferHeader* TryAllocateBuffer(uint32_t size);
|
||||
void ReleaseMessage(IsoMessage* cmd);
|
||||
void FreeVAGCommand(VagCommand* cmd);
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_V2_ISO_QUEUE_H
|
||||
#define JAK_V2_ISO_QUEUE_H
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "isocommon.h"
|
||||
|
||||
|
@ -15,5 +12,4 @@ void UnqueueMessage(IsoMessage* cmd);
|
|||
IsoMessage* GetMessage();
|
||||
void ProcessMessageData();
|
||||
void ReturnMessage(IsoMessage* cmd);
|
||||
|
||||
#endif // JAK_V2_ISO_QUEUE_H
|
||||
IsoBufferHeader* TryAllocateBuffer(uint32_t size);
|
||||
|
|
|
@ -30,6 +30,7 @@ constexpr int LOAD_TO_EE_CMD_ID = 0x100; // command to load file to ee
|
|||
constexpr int LOAD_TO_IOP_CMD_ID = 0x101; // command to load to iop
|
||||
constexpr int LOAD_TO_EE_OFFSET_CMD_ID = 0x102; // command to load file to ee with offset.
|
||||
constexpr int LOAD_DGO_CMD_ID = 0x200; // command to load DGO
|
||||
constexpr int LOAD_SOUND_BANK = 0x300; // Command to load a sound bank
|
||||
|
||||
constexpr int MAX_ISO_FILES = 350; // maximum files on FS
|
||||
constexpr int MAX_OPEN_FILES = 16; // maximum number of open files at a time.
|
||||
|
@ -117,6 +118,13 @@ struct VagCommand : public IsoMessage {
|
|||
// 0x6c max
|
||||
};
|
||||
|
||||
struct SoundBank;
|
||||
|
||||
struct SoundBankLoadCommand : public IsoMessage {
|
||||
char bank_name[16];
|
||||
SoundBank* bank;
|
||||
};
|
||||
|
||||
/*!
|
||||
* DGO Load State Machine states.
|
||||
*/
|
||||
|
@ -167,16 +175,16 @@ struct PriStackEntry {
|
|||
* API to access files. There are debug modes + reading from an ISO filesystem.
|
||||
*/
|
||||
struct IsoFs {
|
||||
int (*init)(u8*);
|
||||
FileRecord* (*find)(const char*);
|
||||
FileRecord* (*find_in)(const char*);
|
||||
uint32_t (*get_length)(FileRecord*);
|
||||
LoadStackEntry* (*open)(FileRecord*, int32_t);
|
||||
LoadStackEntry* (*open_wad)(FileRecord*, int32_t);
|
||||
void (*close)(LoadStackEntry*);
|
||||
uint32_t (*begin_read)(LoadStackEntry*, void*, int32_t);
|
||||
uint32_t (*sync_read)();
|
||||
uint32_t (*load_sound_bank)(char*, void*);
|
||||
int (*init)(u8*); // 0
|
||||
FileRecord* (*find)(const char*); // 4
|
||||
FileRecord* (*find_in)(const char*); // 8
|
||||
uint32_t (*get_length)(FileRecord*); // c
|
||||
LoadStackEntry* (*open)(FileRecord*, int32_t); // 10
|
||||
LoadStackEntry* (*open_wad)(FileRecord*, int32_t); // 14
|
||||
void (*close)(LoadStackEntry*); // 18
|
||||
uint32_t (*begin_read)(LoadStackEntry*, void*, int32_t); // 1c
|
||||
uint32_t (*sync_read)(); // 20
|
||||
uint32_t (*load_sound_bank)(char*, void*); // 24
|
||||
uint32_t (*load_music)(char*, void*);
|
||||
void (*poll_drive)();
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "iso.h"
|
||||
#include "ssound.h"
|
||||
#include "sbank.h"
|
||||
#include "srpc.h"
|
||||
|
||||
using namespace iop;
|
||||
|
||||
|
@ -44,21 +45,22 @@ int start_overlord(int argc, const char* const* argv) {
|
|||
// return 1;
|
||||
// }
|
||||
//
|
||||
// thread_param.attr = TH_C;
|
||||
// thread_param.initPriority = 99;
|
||||
// thread_param.stackSize = 0x1000;
|
||||
// thread_param.option = 0;
|
||||
// thread_param.entry = Thread_Loader;
|
||||
// auto thread_loader = CreateThread(&thread_param);
|
||||
// if(thread_loader <= 0) {
|
||||
// return 1;
|
||||
// }
|
||||
thread_param.attr = TH_C;
|
||||
thread_param.initPriority = 99;
|
||||
thread_param.stackSize = 0x1000;
|
||||
thread_param.option = 0;
|
||||
thread_param.entry = (void*)Thread_Loader;
|
||||
strcpy(thread_param.name, "Loader"); // added for debug
|
||||
auto thread_loader = CreateThread(&thread_param);
|
||||
if (thread_loader <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
InitISOFS(argv[1], argv[2]);
|
||||
StartThread(thread_server, 0);
|
||||
|
||||
// StartThread(thread_player, 0);
|
||||
// StartThread(thread_loader, 0);
|
||||
StartThread(thread_loader, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,63 @@
|
|||
#include <cstring>
|
||||
#include "sbank.h"
|
||||
|
||||
void InitBanks() {}
|
||||
constexpr int N_BANKS = 3;
|
||||
SoundBank* gBanks[N_BANKS];
|
||||
SoundBank gCommonBank;
|
||||
SoundBank gLevelBank[2];
|
||||
|
||||
void sbank_init_globals() {
|
||||
gBanks[0] = &gCommonBank;
|
||||
gBanks[1] = &gLevelBank[0];
|
||||
gBanks[2] = &gLevelBank[1];
|
||||
memset((void*)&gCommonBank, 0, sizeof(gCommonBank));
|
||||
memset((void*)&gLevelBank, 0, sizeof(gLevelBank));
|
||||
}
|
||||
|
||||
void InitBanks() {
|
||||
for (auto& gBank : gBanks) {
|
||||
gBank->unk1 = 0;
|
||||
gBank->unk2 = 0;
|
||||
strcpy(gBank->name, "<unused>");
|
||||
}
|
||||
}
|
||||
|
||||
SoundBank* AllocateBank() {
|
||||
int idx = 0;
|
||||
// find a bank with unk1 = 0, or return nullptr if none exists
|
||||
while (true) {
|
||||
if (idx >= N_BANKS) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (gBanks[idx]->unk1 == 0) {
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
gBanks[0]->unk2 = 0x3fe; // ??
|
||||
} else {
|
||||
gBanks[idx]->unk2 = 0x65; // ??
|
||||
}
|
||||
|
||||
return gBanks[idx];
|
||||
}
|
||||
|
||||
// name should be a 16-byte "sound name"
|
||||
SoundBank* LookupBank(const char* name) {
|
||||
int idx = N_BANKS - 1;
|
||||
while (true) {
|
||||
if (idx < 0) {
|
||||
return nullptr; // not found.
|
||||
}
|
||||
auto& bank = gBanks[idx];
|
||||
// they had some weird stuff here that took advantage of the fact that this region was
|
||||
// 16-byte aligned, so it probably wasn't a memcmp, but this is easier.
|
||||
if (memcmp(bank->name, name, 16) == 0) {
|
||||
return bank;
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_V2_SBANK_H
|
||||
#define JAK_V2_SBANK_H
|
||||
#include "common/common_types.h"
|
||||
|
||||
struct SoundBank {
|
||||
char name[16];
|
||||
u32 unk1; // maybe status?
|
||||
u32 unk2; // maybe size
|
||||
};
|
||||
|
||||
void sbank_init_globals();
|
||||
|
||||
void InitBanks();
|
||||
|
||||
#endif // JAK_V2_SBANK_H
|
||||
SoundBank* AllocateBank();
|
||||
SoundBank* LookupBank(const char* name);
|
|
@ -1,8 +1,83 @@
|
|||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include "common/util/assert.h"
|
||||
#include "srpc.h"
|
||||
#include "game/sce/iop.h"
|
||||
#include "game/common/loader_rpc_types.h"
|
||||
#include "common/versions.h"
|
||||
#include "sbank.h"
|
||||
#include "iso_api.h"
|
||||
|
||||
using namespace iop;
|
||||
|
||||
MusicTweaks gMusicTweakInfo;
|
||||
constexpr int SRPC_MESSAGE_SIZE = 0x50;
|
||||
uint8_t gLoaderBuf[SRPC_MESSAGE_SIZE];
|
||||
int32_t gSoundEnable = 1;
|
||||
u32 gInfoEE = 0; // EE address where we should send info on each frame.
|
||||
|
||||
void srpc_init_globals() {
|
||||
memset((void*)&gMusicTweakInfo, 0, sizeof(gMusicTweakInfo));
|
||||
memset((void*)gLoaderBuf, 0, sizeof(gLoaderBuf));
|
||||
gSoundEnable = 1;
|
||||
gInfoEE = 0;
|
||||
}
|
||||
|
||||
// todo Thread_Player
|
||||
|
||||
void* RPC_Loader(unsigned int fno, void* data, int size);
|
||||
|
||||
u32 Thread_Loader() {
|
||||
sceSifQueueData dq;
|
||||
sceSifServeData serve;
|
||||
|
||||
// set up RPC
|
||||
CpuDisableIntr();
|
||||
sceSifInitRpc(0);
|
||||
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||
sceSifRegisterRpc(&serve, LOADER_RPC_ID, RPC_Loader, gLoaderBuf, nullptr, nullptr, &dq);
|
||||
CpuEnableIntr();
|
||||
sceSifRpcLoop(&dq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo RPC_Player
|
||||
|
||||
void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
int n_messages = size / SRPC_MESSAGE_SIZE;
|
||||
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
|
||||
if (gSoundEnable) {
|
||||
// I don't think it should be possible to have > 1 message here - the buffer isn't big enough.
|
||||
if (n_messages > 1) {
|
||||
assert(false);
|
||||
}
|
||||
while (n_messages > 0) {
|
||||
switch (cmd->command) {
|
||||
case SoundCommand::LOAD_BANK: {
|
||||
// see if it's already loaded
|
||||
auto bank = LookupBank(cmd->load_bank.bank_name);
|
||||
if (!bank) {
|
||||
// see if we have room to load another
|
||||
auto new_bank = AllocateBank();
|
||||
if (new_bank) {
|
||||
// we do!
|
||||
LoadSoundBank(cmd->load_bank.bank_name, new_bank);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::GET_IRX_VERSION: {
|
||||
cmd->irx_version.major = IRX_VERSION_MAJOR;
|
||||
cmd->irx_version.minor = IRX_VERSION_MINOR;
|
||||
gInfoEE = cmd->irx_version.ee_addr;
|
||||
return cmd;
|
||||
} break;
|
||||
default:
|
||||
printf("Unhandled RPC Loader command %d\n", (int)cmd->command);
|
||||
assert(false);
|
||||
}
|
||||
n_messages--;
|
||||
cmd++;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JAK_V2_SRPC_H
|
||||
#define JAK_V2_SRPC_H
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
void srpc_init_globals();
|
||||
|
@ -18,6 +15,52 @@ struct MusicTweaks {
|
|||
} MusicTweak[MUSIC_TWEAK_COUNT];
|
||||
};
|
||||
|
||||
enum class SoundCommand : u16 {
|
||||
LOAD_BANK = 0,
|
||||
LOAD_MUSIC = 1,
|
||||
UNLOAD_BANK = 2,
|
||||
PLAY = 3,
|
||||
PAUSE_SOUND = 4,
|
||||
STOP_SOUND = 5,
|
||||
CONTINUE_SOUND = 6,
|
||||
SET_PARAM = 7,
|
||||
SET_MASTER_VOLUME = 8,
|
||||
PAUSE_GROUP = 9,
|
||||
STOP_GROUP = 10,
|
||||
CONTINUE_GROUP = 11,
|
||||
GET_IRX_VERSION = 12,
|
||||
SET_FALLOFF_CURVE = 13,
|
||||
SET_SOUND_FALLOFF = 14,
|
||||
RELOAD_INFO = 15,
|
||||
SET_LANGUAGE = 16,
|
||||
SET_FLAVA = 17,
|
||||
SET_REVERB = 18,
|
||||
SET_EAR_TRANS = 19,
|
||||
SHUTDOWN = 20,
|
||||
LIST_SOUNDS = 21,
|
||||
UNLOAD_MUSIC = 22
|
||||
};
|
||||
|
||||
struct SoundRpcGetIrxVersion {
|
||||
u32 major;
|
||||
u32 minor;
|
||||
u32 ee_addr;
|
||||
};
|
||||
|
||||
struct SoundRpcBankCommand {
|
||||
u8 pad[12];
|
||||
char bank_name[16];
|
||||
};
|
||||
|
||||
struct SoundRpcCommand {
|
||||
u16 rsvd1;
|
||||
SoundCommand command;
|
||||
union {
|
||||
SoundRpcGetIrxVersion irx_version;
|
||||
SoundRpcBankCommand load_bank;
|
||||
};
|
||||
};
|
||||
|
||||
extern MusicTweaks gMusicTweakInfo;
|
||||
|
||||
#endif // JAK_V2_SRPC_H
|
||||
u32 Thread_Loader();
|
|
@ -46,6 +46,7 @@
|
|||
#include "game/overlord/overlord.h"
|
||||
#include "game/overlord/srpc.h"
|
||||
#include "game/overlord/stream.h"
|
||||
#include "game/overlord/sbank.h"
|
||||
|
||||
#include "game/graphics/opengl.h"
|
||||
#include "game/graphics/gfx.h"
|
||||
|
@ -191,7 +192,7 @@ void iop_runner(SystemThreadInterface& iface) {
|
|||
// isocommon
|
||||
// overlord
|
||||
ramdisk_init_globals();
|
||||
// sbank
|
||||
sbank_init_globals();
|
||||
// soundcommon
|
||||
srpc_init_globals();
|
||||
// ssound
|
||||
|
|
|
@ -210,6 +210,9 @@ void IOP_Kernel::sif_rpc(s32 rpcChannel,
|
|||
rec = &e;
|
||||
}
|
||||
}
|
||||
if (!rec) {
|
||||
printf("Failed to find handler for sif channel 0x%x\n", rpcChannel);
|
||||
}
|
||||
assert(rec);
|
||||
|
||||
// step 2 - check entry is safe to give command to
|
||||
|
|
|
@ -104,9 +104,9 @@ struct DgoHeader {
|
|||
(define-extern *load-dgo-rpc* rpc-buffer-pair)
|
||||
(when (= 0 (the int *load-dgo-rpc*))
|
||||
;; we need to allocate the rpc buffers
|
||||
(set! *load-dgo-rpc* (new 'global 'rpc-buffer-pair (the uint 32) (the uint 1) 3)) ;; todo, constants
|
||||
(define *load-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 1) 4)) ;; todo, constants
|
||||
(define *play-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 2) 5))
|
||||
(set! *load-dgo-rpc* (new 'global 'rpc-buffer-pair (the uint 32) (the uint 1) RPC-DGO))
|
||||
(define *load-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 1) RPC-LOAD-STR))
|
||||
(define *play-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 2) RPC-PLAY-STR))
|
||||
|
||||
;; we have separate locks for queuing and loading.
|
||||
(define *load-str-lock* '#f)
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
)
|
||||
|
||||
;; allocate the ramdisk RPC buffer
|
||||
(define *ramdisk-rpc* (new 'global 'rpc-buffer-pair (the-as uint 32) (the-as uint 1) 2))
|
||||
(define *ramdisk-rpc* (new 'global 'rpc-buffer-pair (the-as uint 32) (the-as uint 1) RPC-RAMDISK))
|
||||
|
||||
(define *current-ramdisk-id* 0)
|
||||
|
||||
|
|
|
@ -5,6 +5,15 @@
|
|||
;; name in dgo: rpc-h
|
||||
;; dgos: GAME, ENGINE
|
||||
|
||||
;; RPC channels.
|
||||
;; these should match XXX_RPC_CHANNEL in the game/common/xxx_rpc_types.h
|
||||
(defconstant RPC-SOUND-PLAYER 0) ;; called player in IOP code
|
||||
(defconstant RPC-SOUND-LOADER 1) ;; called loader in IOP code
|
||||
(defconstant RPC-RAMDISK 2) ;; called server in IOP code, sometimes
|
||||
(defconstant RPC-DGO 3)
|
||||
(defconstant RPC-LOAD-STR 4) ;; called STR in IOP code
|
||||
(defconstant RPC-PLAY-STR 5) ;; called PLAY in IOP code
|
||||
|
||||
;; an RPC buffer is a container of elements to send to the IOP.
|
||||
;; each element is size elt-size, and there are maximum of elt-count elements
|
||||
;; it is possible to use fewer elements than elt-count.
|
||||
|
@ -136,7 +145,7 @@
|
|||
"Call an RPC. This is an async RPC. Use check-busy or sync to see if it's done."
|
||||
(when (!= 0 (-> obj current elt-used))
|
||||
;; when we have used elements
|
||||
(format 0 "call rpc-buffer-pair with ~D elts~%" (-> obj current elt-used))
|
||||
;; (format 0 "call rpc-buffer-pair with ~D elts~%" (-> obj current elt-used))
|
||||
|
||||
;; make sure the previous buffer is done
|
||||
(let ((active-buffer (if (= (-> obj buffer 0) (-> obj current))
|
||||
|
@ -145,25 +154,21 @@
|
|||
(when (-> active-buffer busy)
|
||||
;; we think the active buffer may be busy.
|
||||
;; first lets just do a simple check
|
||||
(cond
|
||||
((!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
;; busy! print an error and stall!
|
||||
(format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> obj rpc-port))
|
||||
(while (!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
(+ 1 2 3)
|
||||
)
|
||||
)
|
||||
(else
|
||||
;; not busy.
|
||||
(set! (-> active-buffer busy) '#f)
|
||||
(set! (-> active-buffer elt-used) 0)
|
||||
(when (!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
;; busy! print an error and stall!
|
||||
(format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> obj rpc-port))
|
||||
(while (!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
(+ 1 2 3)
|
||||
)
|
||||
)
|
||||
;; not busy.
|
||||
(set! (-> active-buffer busy) '#f)
|
||||
(set! (-> active-buffer elt-used) 0)
|
||||
)
|
||||
;; now we've cleared the last RPC call, we can do another
|
||||
(let ((current-buffer (-> obj current)))
|
||||
;; rpc_channel, fno, async, send_buff, send_size, recv_buff, recv_size
|
||||
(format 0 "recv-size is ~D~%" recv-size)
|
||||
;; (format 0 "recv-size is ~D~%" recv-size)
|
||||
(rpc-call (-> obj rpc-port)
|
||||
fno
|
||||
(the uint 1)
|
||||
|
|
|
@ -31,6 +31,27 @@
|
|||
:flag-assert #x900000010
|
||||
)
|
||||
|
||||
(defmacro static-sound-name (str)
|
||||
"Convert a string constant to a static sound-name."
|
||||
|
||||
;; all this is done at compile-time so we can come up with 2
|
||||
;; 64-bit constants to use
|
||||
(when (> (string-length str) 16)
|
||||
(error "static-sound-name got a string that is too long")
|
||||
)
|
||||
(let ((lo-val 0)
|
||||
(hi-val 0)
|
||||
)
|
||||
(dotimes (i (string-length str))
|
||||
(if (>= i 8)
|
||||
(+! hi-val (ash (string-ref str i) (* 8 (- i 8))))
|
||||
(+! lo-val (ash (string-ref str i) (* 8 i)))
|
||||
)
|
||||
)
|
||||
`(new 'static 'sound-name :lo ,lo-val :hi ,hi-val)
|
||||
)
|
||||
)
|
||||
|
||||
(defenum sound-command
|
||||
:type uint16
|
||||
(load-bank)
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
;; dgos: GAME, ENGINE
|
||||
|
||||
|
||||
(define *sound-player-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 128) 0))
|
||||
(define *sound-loader-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 1) 1))
|
||||
(define *sound-player-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 128) RPC-SOUND-PLAYER))
|
||||
(define *sound-loader-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 1) RPC-SOUND-LOADER))
|
||||
|
||||
(defun sound-name= ((arg0 sound-name) (arg1 sound-name))
|
||||
"Return #t if both are the same name"
|
||||
|
@ -101,8 +101,7 @@
|
|||
0
|
||||
)
|
||||
|
||||
;; TODO rpc 1 unimplemented
|
||||
;(check-irx-version)
|
||||
(check-irx-version)
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -146,10 +145,10 @@
|
|||
0
|
||||
)
|
||||
|
||||
;; TODO rpc 1 unimplemented
|
||||
;(sound-bank-load (new 'static 'sound-name :lo #x6e6f6d6d6f63 :hi 0)) ;; common
|
||||
;(sound-bank-load (new 'static 'sound-name :lo #x317974706d65 :hi 0)) ;; empty1
|
||||
;(sound-bank-load (new 'static 'sound-name :lo #x327974706d65 :hi 0)) ;; empty2
|
||||
(sound-bank-load (static-sound-name "common"))
|
||||
(sound-bank-load (static-sound-name "empty1"))
|
||||
(sound-bank-load (static-sound-name "empty2"))
|
||||
|
||||
(define *sound-bank-1* 'empty1)
|
||||
(define *sound-bank-2* 'empty2)
|
||||
|
||||
|
@ -302,22 +301,20 @@
|
|||
|
||||
(defun string->sound-name ((str string))
|
||||
(let ((snd-name (new 'stack-no-clear 'qword)))
|
||||
(set! (-> snd-name quad) (the-as uint128 0))
|
||||
(let ((out-ptr (the-as (pointer uint8) snd-name))
|
||||
(in-ptr (-> str data))
|
||||
)
|
||||
(while
|
||||
(and
|
||||
(nonzero? (-> in-ptr 0))
|
||||
(< (&- in-ptr (the-as uint (-> str data))) 15)
|
||||
(set! (-> snd-name quad) (the-as uint128 0))
|
||||
(let ((out-ptr (the-as (pointer uint8) snd-name))
|
||||
(in-ptr (-> str data))
|
||||
)
|
||||
(while (and (nonzero? (-> in-ptr 0))
|
||||
(< (&- in-ptr (the-as uint (-> str data))) 15)
|
||||
)
|
||||
(set! (-> out-ptr 0) (-> in-ptr 0))
|
||||
(set! out-ptr (&-> out-ptr 1))
|
||||
(set! in-ptr (&-> in-ptr 1))
|
||||
)
|
||||
)
|
||||
(set! (-> out-ptr 0) (-> in-ptr 0))
|
||||
(set! out-ptr (&-> out-ptr 1))
|
||||
(set! in-ptr (&-> in-ptr 1))
|
||||
)
|
||||
(the-as sound-name (-> snd-name quad))
|
||||
)
|
||||
(the-as sound-name (-> snd-name quad))
|
||||
)
|
||||
)
|
||||
|
||||
(defun sound-set-volume ((group uint) (volume float))
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
|
||||
(defmacro md (file &rest path)
|
||||
"Make Debug: make + print disassembly for a file"
|
||||
`(asm-file ,file :color :write :disassemble ,(first path))
|
||||
(if (null? path)
|
||||
`(asm-file ,file :color :write :disassemble)
|
||||
`(asm-file ,file :color :write :disassemble ,(first path))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro ml (file)
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
`(cond (,clause ,true) (#t ,false))
|
||||
)
|
||||
|
||||
(defsmacro when (clause &rest body)
|
||||
`(if ,clause (begin ,@body) #f)
|
||||
)
|
||||
|
||||
(defsmacro not (x)
|
||||
`(if ,x #f #t)
|
||||
)
|
||||
|
@ -34,6 +38,14 @@
|
|||
)
|
||||
)
|
||||
|
||||
(defsmacro max (a b)
|
||||
`(if (> a b) a b)
|
||||
)
|
||||
|
||||
(defsmacro min (a b)
|
||||
`(if (< a b) a b)
|
||||
)
|
||||
|
||||
(defsmacro caar (x)
|
||||
`(car (car ,x)))
|
||||
|
||||
|
@ -96,6 +108,16 @@
|
|||
)
|
||||
)
|
||||
|
||||
(defsmacro dotimes (var &rest body)
|
||||
`(let (( ,(first var) 0))
|
||||
(while (< ,(first var) ,(second var))
|
||||
,@body
|
||||
(set! ,(first var) (+ ,(first var) 1))
|
||||
)
|
||||
,@(cddr var)
|
||||
)
|
||||
)
|
||||
|
||||
(desfun repeated-list (obj count)
|
||||
(if (= 0 count)
|
||||
'()
|
||||
|
@ -169,5 +191,13 @@
|
|||
`(seval (desfun ,name ,args ,@body))
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
;; shortcut to quit GOOS
|
||||
(defsmacro e ()
|
||||
`(exit)
|
||||
)
|
||||
|
||||
;; this is checked in a test to see if this file is loaded.
|
||||
(define __goos-lib-loaded__ #t)
|
||||
|
|
|
@ -1174,6 +1174,83 @@ TEST_F(FormRegressionTest, Method4ResTag) {
|
|||
test_with_expr(func, type, expected);
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, MakeSqrtTable) {
|
||||
std::string func =
|
||||
"sll r0, r0, 0\n"
|
||||
"L149:\n"
|
||||
" daddiu sp, sp, -32\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sd fp, 8(sp)\n"
|
||||
" or fp, t9, r0\n"
|
||||
" sq gp, 16(sp)\n"
|
||||
" lw t9, format(s7)\n"
|
||||
" daddiu a0, s7, #t\n"
|
||||
" daddiu a1, fp, L190\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
" addiu gp, r0, 0\n"
|
||||
" beq r0, r0, L151\n"
|
||||
" sll r0, r0, 0\n"
|
||||
"L150:\n"
|
||||
//" lwc1 f0, L232(fp)\n"
|
||||
" mtc1 f0, r0\n"
|
||||
" mtc1 f1, gp\n"
|
||||
" cvt.s.w f1, f1\n"
|
||||
" mul.s f0, f0, f1\n"
|
||||
" sqrt.s f0, f0\n"
|
||||
//" lwc1 f1, L233(fp)\n"
|
||||
" mtc1 f1, r0\n"
|
||||
" add.s f0, f1, f0\n"
|
||||
" cvt.w.s f0, f0\n"
|
||||
" mfc1 a2, f0\n"
|
||||
" lw t9, format(s7)\n"
|
||||
" daddiu a0, s7, #t\n"
|
||||
" daddiu a1, fp, L189\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
" or v1, v0, r0\n"
|
||||
" daddiu gp, gp, 1\n"
|
||||
"L151:\n"
|
||||
" slti v1, gp, 256\n"
|
||||
" bne v1, r0, L150\n"
|
||||
" sll r0, r0, 0\n"
|
||||
|
||||
" or v1, s7, r0\n"
|
||||
" or v1, s7, r0\n"
|
||||
" lw t9, format(s7)\n"
|
||||
" daddiu a0, s7, #t\n"
|
||||
" daddiu a1, fp, L188\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
" or v0, r0, r0\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" ld fp, 8(sp)\n"
|
||||
" lq gp, 16(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 32\n"
|
||||
" sll r0, r0, 0\n"
|
||||
" sll r0, r0, 0";
|
||||
std::string type = "(function none)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (format #t \"static int sqrt_table[256] =~%{~%\")\n"
|
||||
" (dotimes (gp-0 256)\n"
|
||||
" (let* ((f0-2 (sqrtf (* 0.0 (the float gp-0))))\n"
|
||||
" (a2-0 (the int (+ 0.0 f0-2)))\n"
|
||||
" )\n"
|
||||
" (format #t \"~D,~%\" a2-0)\n"
|
||||
" )\n"
|
||||
" )\n"
|
||||
" (format #t \"};~%\")\n"
|
||||
" (let ((v0-3 0))\n"
|
||||
" )\n"
|
||||
" (none)\n"
|
||||
" )";
|
||||
test_with_expr(
|
||||
func, type, expected, false, "",
|
||||
{{"L190", "static int sqrt_table[256] =~%{~%"}, {"L189", "~D,~%"}, {"L188", "};~%"}});
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, Method2Vec4s) {
|
||||
std::string func =
|
||||
"sll r0, r0, 0\n"
|
||||
|
@ -1219,3 +1296,24 @@ TEST_F(FormRegressionTest, Method2Vec4s) {
|
|||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {{"L344", "#<vector ~F ~F ~F ~F @ #x~X>"}});
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, SoundNameEqual) {
|
||||
std::string func =
|
||||
"sll r0, r0, 0\n"
|
||||
" dsubu v1, a0, a1\n"
|
||||
" daddiu a2, s7, 8\n"
|
||||
" movn a2, s7, v1\n"
|
||||
" beql s7, a2, L125\n"
|
||||
" or v0, a2, r0\n"
|
||||
" pcpyud v1, a0, r0\n"
|
||||
" pcpyud a0, a1, r0\n"
|
||||
" dsubu v1, v1, a0\n"
|
||||
" daddiu v0, s7, 8\n"
|
||||
" movn v0, s7, v1\n"
|
||||
"L125:\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
std::string type = "(function sound-name sound-name symbol)";
|
||||
std::string expected = "(and (= arg0 arg1) (= (-> arg0 hi) (-> arg1 hi)))";
|
||||
test_with_expr(func, type, expected);
|
||||
}
|
12
test/goalc/source_templates/with_game/test-sound-name.gc
Normal file
12
test/goalc/source_templates/with_game/test-sound-name.gc
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
(let ((sound-name-1 (static-sound-name "asdfghjkzxcvn,."))
|
||||
(sound-name-2 (string->sound-name "asdfghjkzxcvn,."))
|
||||
(sound-name-3 (string->sound-name "asdfghjkzxcvnn,."))
|
||||
(sound-name-4 (string->sound-name "asddghjkzxcvnm,."))
|
||||
)
|
||||
(format #t "~A ~A ~A~%"
|
||||
(sound-name= sound-name-1 sound-name-2)
|
||||
(sound-name= sound-name-1 sound-name-3)
|
||||
(sound-name= sound-name-1 sound-name-4)
|
||||
)
|
||||
)
|
|
@ -753,6 +753,12 @@ TEST_F(WithGameTests, AddrOfVar) {
|
|||
"0\n"});
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, SoundName) {
|
||||
runner.run_static_test(env, testCategory, "test-sound-name.gc",
|
||||
{"#t #f #f\n"
|
||||
"0\n"});
|
||||
}
|
||||
|
||||
TEST(TypeConsistency, TypeConsistency) {
|
||||
Compiler compiler;
|
||||
compiler.enable_throw_on_redefines();
|
||||
|
|
Loading…
Reference in a new issue