add extended patch metadata
This commit is contained in:
+27
-8
@@ -129,7 +129,7 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
|
|||||||
#else
|
#else
|
||||||
auto ret = make_shared<CompiledFunctionCode>();
|
auto ret = make_shared<CompiledFunctionCode>();
|
||||||
ret->arch = arch;
|
ret->arch = arch;
|
||||||
ret->name = name;
|
ret->short_name = name;
|
||||||
ret->index = 0;
|
ret->index = 0;
|
||||||
ret->hide_from_patches_menu = false;
|
ret->hide_from_patches_menu = false;
|
||||||
|
|
||||||
@@ -137,6 +137,22 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
|
|||||||
auto assembled = PPC32Emulator::assemble(text, {directory});
|
auto assembled = PPC32Emulator::assemble(text, {directory});
|
||||||
ret->code = std::move(assembled.code);
|
ret->code = std::move(assembled.code);
|
||||||
ret->label_offsets = std::move(assembled.label_offsets);
|
ret->label_offsets = std::move(assembled.label_offsets);
|
||||||
|
for (const auto& it : assembled.metadata_keys) {
|
||||||
|
if (it.first == "hide_from_patches_menu") {
|
||||||
|
ret->hide_from_patches_menu = true;
|
||||||
|
} else if (it.first == "index") {
|
||||||
|
if (it.second.size() != 1) {
|
||||||
|
throw runtime_error("invalid index value in .meta directive");
|
||||||
|
}
|
||||||
|
ret->index = it.second[0];
|
||||||
|
} else if (it.first == "name") {
|
||||||
|
ret->long_name = it.second;
|
||||||
|
} else if (it.first == "description") {
|
||||||
|
ret->description = it.second;
|
||||||
|
} else {
|
||||||
|
throw runtime_error("unknown metadata key: " + it.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (arch == CompiledFunctionCode::Architecture::X86) {
|
} else if (arch == CompiledFunctionCode::Architecture::X86) {
|
||||||
throw runtime_error("x86 assembler is not implemented");
|
throw runtime_error("x86 assembler is not implemented");
|
||||||
}
|
}
|
||||||
@@ -188,7 +204,7 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
|
|||||||
|
|
||||||
// Check for specific_version token
|
// Check for specific_version token
|
||||||
uint32_t specific_version = 0;
|
uint32_t specific_version = 0;
|
||||||
string patch_name = name;
|
string short_name = name;
|
||||||
if (is_patch &&
|
if (is_patch &&
|
||||||
(filename.size() >= 13) &&
|
(filename.size() >= 13) &&
|
||||||
(filename[filename.size() - 13] == '.') &&
|
(filename[filename.size() - 13] == '.') &&
|
||||||
@@ -197,14 +213,13 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
|
|||||||
(filename[filename.size() - 10] == 'E' || filename[filename.size() - 10] == 'J' || filename[filename.size() - 10] == 'P') &&
|
(filename[filename.size() - 10] == 'E' || filename[filename.size() - 10] == 'J' || filename[filename.size() - 10] == 'P') &&
|
||||||
(isdigit(filename[filename.size() - 9]) || filename[filename.size() - 9] == 'T')) {
|
(isdigit(filename[filename.size() - 9]) || filename[filename.size() - 9] == 'T')) {
|
||||||
specific_version = 0x33000000 | (filename[filename.size() - 11] << 16) | (filename[filename.size() - 10] << 8) | filename[filename.size() - 9];
|
specific_version = 0x33000000 | (filename[filename.size() - 11] << 16) | (filename[filename.size() - 10] << 8) | filename[filename.size() - 9];
|
||||||
patch_name = filename.substr(0, filename.size() - 13);
|
short_name = filename.substr(0, filename.size() - 13);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
string path = directory + "/" + filename;
|
string path = directory + "/" + filename;
|
||||||
string text = load_file(path);
|
string text = load_file(path);
|
||||||
auto code = compile_function_code(
|
auto code = compile_function_code(CompiledFunctionCode::Architecture::POWERPC, directory, name, text);
|
||||||
CompiledFunctionCode::Architecture::POWERPC, directory, name, text);
|
|
||||||
if (code->index != 0) {
|
if (code->index != 0) {
|
||||||
if (!this->index_to_function.emplace(code->index, code).second) {
|
if (!this->index_to_function.emplace(code->index, code).second) {
|
||||||
throw runtime_error(string_printf(
|
throw runtime_error(string_printf(
|
||||||
@@ -212,14 +227,14 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
code->specific_version = specific_version;
|
code->specific_version = specific_version;
|
||||||
code->patch_name = patch_name;
|
code->short_name = short_name;
|
||||||
this->name_to_function.emplace(name, code);
|
this->name_to_function.emplace(name, code);
|
||||||
if (is_patch) {
|
if (is_patch) {
|
||||||
code->menu_item_id = next_menu_item_id++;
|
code->menu_item_id = next_menu_item_id++;
|
||||||
this->menu_item_id_and_specific_version_to_patch_function.emplace(
|
this->menu_item_id_and_specific_version_to_patch_function.emplace(
|
||||||
static_cast<uint64_t>(code->menu_item_id) << 32 | specific_version, code);
|
static_cast<uint64_t>(code->menu_item_id) << 32 | specific_version, code);
|
||||||
this->name_and_specific_version_to_patch_function.emplace(
|
this->name_and_specific_version_to_patch_function.emplace(
|
||||||
string_printf("%s-%08" PRIX32, patch_name.c_str(), specific_version), code);
|
string_printf("%s-%08" PRIX32, short_name.c_str(), specific_version), code);
|
||||||
}
|
}
|
||||||
|
|
||||||
string index_prefix = code->index ? string_printf("%02X => ", code->index) : "";
|
string index_prefix = code->index ? string_printf("%02X => ", code->index) : "";
|
||||||
@@ -241,7 +256,11 @@ shared_ptr<const Menu> FunctionCodeIndex::patch_menu(uint32_t specific_version)
|
|||||||
for (const auto& it : this->name_and_specific_version_to_patch_function) {
|
for (const auto& it : this->name_and_specific_version_to_patch_function) {
|
||||||
const auto& fn = it.second;
|
const auto& fn = it.second;
|
||||||
if (!fn->hide_from_patches_menu && ends_with(it.first, suffix)) {
|
if (!fn->hide_from_patches_menu && ends_with(it.first, suffix)) {
|
||||||
ret->items.emplace_back(fn->menu_item_id, fn->patch_name, "", MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL);
|
ret->items.emplace_back(
|
||||||
|
fn->menu_item_id,
|
||||||
|
fn->long_name.empty() ? fn->short_name : fn->long_name,
|
||||||
|
fn->description,
|
||||||
|
MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ struct CompiledFunctionCode {
|
|||||||
std::vector<uint16_t> relocation_deltas;
|
std::vector<uint16_t> relocation_deltas;
|
||||||
std::unordered_map<std::string, uint32_t> label_offsets;
|
std::unordered_map<std::string, uint32_t> label_offsets;
|
||||||
uint32_t entrypoint_offset_offset;
|
uint32_t entrypoint_offset_offset;
|
||||||
std::string name;
|
std::string short_name; // Based on filename
|
||||||
std::string patch_name; // Blank if not a patch
|
std::string long_name; // From .meta name directive
|
||||||
uint32_t index; // 0 = unused (not registered in index_to_function)
|
std::string description; // From .meta description directive
|
||||||
|
uint8_t index; // 0 = unused (not registered in index_to_function)
|
||||||
uint32_t menu_item_id;
|
uint32_t menu_item_id;
|
||||||
bool hide_from_patches_menu;
|
bool hide_from_patches_menu;
|
||||||
uint32_t specific_version;
|
uint32_t specific_version;
|
||||||
@@ -60,7 +61,7 @@ struct FunctionCodeIndex {
|
|||||||
explicit FunctionCodeIndex(const std::string& directory);
|
explicit FunctionCodeIndex(const std::string& directory);
|
||||||
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<CompiledFunctionCode>> name_to_function;
|
std::unordered_map<std::string, std::shared_ptr<CompiledFunctionCode>> name_to_function;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<CompiledFunctionCode>> index_to_function;
|
std::unordered_map<uint8_t, std::shared_ptr<CompiledFunctionCode>> index_to_function;
|
||||||
std::unordered_map<uint64_t, std::shared_ptr<CompiledFunctionCode>> menu_item_id_and_specific_version_to_patch_function;
|
std::unordered_map<uint64_t, std::shared_ptr<CompiledFunctionCode>> menu_item_id_and_specific_version_to_patch_function;
|
||||||
// Key here is e.g. "PATCHNAME-SPECIFICVERSION", with the latter in hex
|
// Key here is e.g. "PATCHNAME-SPECIFICVERSION", with the latter in hex
|
||||||
std::map<std::string, std::shared_ptr<CompiledFunctionCode>> name_and_specific_version_to_patch_function;
|
std::map<std::string, std::shared_ptr<CompiledFunctionCode>> name_and_specific_version_to_patch_function;
|
||||||
|
|||||||
@@ -2634,14 +2634,13 @@ static void on_B3(shared_ptr<Client> c, uint16_t, uint32_t flag, string& data) {
|
|||||||
c->function_call_response_queue.pop_front();
|
c->function_call_response_queue.pop_front();
|
||||||
} else if (c->loading_dol_file.get()) {
|
} else if (c->loading_dol_file.get()) {
|
||||||
auto called_fn = s->function_code_index->index_to_function.at(flag);
|
auto called_fn = s->function_code_index->index_to_function.at(flag);
|
||||||
if (called_fn->name == "ReadMemoryWord") {
|
if (called_fn->short_name == "ReadMemoryWord") {
|
||||||
c->dol_base_addr = (cmd.return_value - c->loading_dol_file->data.size()) & (~3);
|
c->dol_base_addr = (cmd.return_value - c->loading_dol_file->data.size()) & (~3);
|
||||||
send_dol_file_chunk(c, c->dol_base_addr);
|
send_dol_file_chunk(c, c->dol_base_addr);
|
||||||
} else if (called_fn->name == "WriteMemory") {
|
} else if (called_fn->short_name == "WriteMemory") {
|
||||||
if (cmd.return_value >= c->dol_base_addr + c->loading_dol_file->data.size()) {
|
if (cmd.return_value >= c->dol_base_addr + c->loading_dol_file->data.size()) {
|
||||||
auto fn = s->function_code_index->name_to_function.at("RunDOL");
|
auto fn = s->function_code_index->name_to_function.at("RunDOL");
|
||||||
unordered_map<string, uint32_t> label_writes(
|
unordered_map<string, uint32_t> label_writes({{"dol_base_ptr", c->dol_base_addr}});
|
||||||
{{"dol_base_ptr", c->dol_base_addr}});
|
|
||||||
send_function_call(c, fn, label_writes);
|
send_function_call(c, fn, label_writes);
|
||||||
// The client will stop running PSO after this, so disconnect them
|
// The client will stop running PSO after this, so disconnect them
|
||||||
c->should_disconnect = true;
|
c->should_disconnect = true;
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
# option is disabled, the Patches menu won't appear for the client. If this
|
# option is disabled, the Patches menu won't appear for the client. If this
|
||||||
# patch is run on a different client version, it will do nothing.
|
# patch is run on a different client version, it will do nothing.
|
||||||
|
|
||||||
hide_from_patches_menu:
|
.meta hide_from_patches_menu
|
||||||
|
.meta name="Get all cards"
|
||||||
|
.meta description="This patch gives you\nthe maximum number\nof each card."
|
||||||
|
|
||||||
entry_ptr:
|
entry_ptr:
|
||||||
reloc0:
|
reloc0:
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
# $patch command, since the client will likely crash if the player is not in a
|
# $patch command, since the client will likely crash if the player is not in a
|
||||||
# game or lobby when the patch runs.
|
# game or lobby when the patch runs.
|
||||||
|
|
||||||
hide_from_patches_menu:
|
.meta hide_from_patches_menu
|
||||||
|
.meta name="Editors"
|
||||||
|
.meta description="Enables the various\ndebug menus"
|
||||||
|
|
||||||
entry_ptr:
|
entry_ptr:
|
||||||
reloc0:
|
reloc0:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# This function is required for loading DOLs. If it's not present, newserv can't
|
# This function is required for loading DOLs. If it's not present, newserv can't
|
||||||
# serve DOL files to GameCube clients.
|
# serve DOL files to GameCube clients.
|
||||||
|
|
||||||
newserv_index_E0:
|
.meta index=E0
|
||||||
|
|
||||||
entry_ptr:
|
entry_ptr:
|
||||||
reloc0:
|
reloc0:
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# This function is required for loading DOLs. If it's not present, newserv can't
|
# This function is required for loading DOLs. If it's not present, newserv can't
|
||||||
# serve DOL files to GameCube clients.
|
# serve DOL files to GameCube clients.
|
||||||
|
|
||||||
newserv_index_E2:
|
.meta index=E2
|
||||||
|
|
||||||
entry_ptr:
|
entry_ptr:
|
||||||
reloc0:
|
reloc0:
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
# option is disabled, the Patches menu won't appear for the client. If this
|
# option is disabled, the Patches menu won't appear for the client. If this
|
||||||
# patch is run on a different client version, it will do nothing.
|
# patch is run on a different client version, it will do nothing.
|
||||||
|
|
||||||
|
.meta name="Get VIP card"
|
||||||
|
.meta description="Gives you a VIP card"
|
||||||
|
|
||||||
entry_ptr:
|
entry_ptr:
|
||||||
reloc0:
|
reloc0:
|
||||||
.offsetof start
|
.offsetof start
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
# of the above information. This value is called specific_version in the places
|
# of the above information. This value is called specific_version in the places
|
||||||
# where it's used by the server.
|
# where it's used by the server.
|
||||||
|
|
||||||
newserv_index_E3:
|
.meta index=E3
|
||||||
|
|
||||||
entry_ptr:
|
entry_ptr:
|
||||||
reloc0:
|
reloc0:
|
||||||
|
|||||||
@@ -43,13 +43,19 @@
|
|||||||
# requested by the client, so those features should only be used in general
|
# requested by the client, so those features should only be used in general
|
||||||
# functions.
|
# functions.
|
||||||
|
|
||||||
# A label newserv_index_XX tells newserv what value to use in the flag field
|
# These directives tell newserv what to show to the player in the Patches menu.
|
||||||
|
# Neither of them is required; if the name is omitted, the filename is used
|
||||||
|
# instead.
|
||||||
|
.meta name="Write memory"
|
||||||
|
.meta description="Writes data to any location in memory"
|
||||||
|
|
||||||
|
# The .meta index directive tells newserv what value to use in the flag field
|
||||||
# when sending the B2 command. This is needed if the server needs to do
|
# when sending the B2 command. This is needed if the server needs to do
|
||||||
# something when the B3 response is received. For GameCube functions, if
|
# something when the B3 response is received. If specified, the index must be in
|
||||||
# specified, the index must be in the range 01-FF. The DOL loading
|
# the range 01-FF. The DOL loading functionality, which this function is a part
|
||||||
# functionality, which this function is a part of, uses indexes E0, E1, and E2,
|
# of, uses indexes E0, E1, and E2, but the WriteMemory function can also be used
|
||||||
# but the WriteMemory function can also be used for other purposes.
|
# for other purposes.
|
||||||
newserv_index_E1:
|
.meta index=E1
|
||||||
|
|
||||||
# The entry_ptr label is required for all functions. It should point to a
|
# The entry_ptr label is required for all functions. It should point to a
|
||||||
# .offsetof directive that itself points to the actual entrypoint.
|
# .offsetof directive that itself points to the actual entrypoint.
|
||||||
|
|||||||
Reference in New Issue
Block a user