add AR code translator shell
This commit is contained in:
+7
-1
@@ -40,7 +40,7 @@ find_package(resource_file QUIET)
|
|||||||
|
|
||||||
# Executable definition
|
# Executable definition
|
||||||
|
|
||||||
add_executable(newserv
|
set(SOURCES
|
||||||
src/AFSArchive.cc
|
src/AFSArchive.cc
|
||||||
src/BattleParamsIndex.cc
|
src/BattleParamsIndex.cc
|
||||||
src/BMLArchive.cc
|
src/BMLArchive.cc
|
||||||
@@ -112,6 +112,12 @@ add_executable(newserv
|
|||||||
src/Version.cc
|
src/Version.cc
|
||||||
src/WordSelectTable.cc
|
src/WordSelectTable.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(resource_file_FOUND)
|
||||||
|
set(SOURCES ${SOURCES} src/ARCodeTranslator.cc)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(newserv ${SOURCES})
|
||||||
target_include_directories(newserv PUBLIC ${LIBEVENT_INCLUDE_DIR} ${Iconv_INCLUDE_DIRS})
|
target_include_directories(newserv PUBLIC ${LIBEVENT_INCLUDE_DIR} ${Iconv_INCLUDE_DIRS})
|
||||||
target_link_libraries(newserv phosg ${LIBEVENT_LIBRARIES} ${Iconv_LIBRARIES} pthread)
|
target_link_libraries(newserv phosg ${LIBEVENT_LIBRARIES} ${Iconv_LIBRARIES} pthread)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
inline void run_ar_code_translator(const std::string&) {
|
||||||
|
throw std::runtime_error("resource_file is not available; install it and rebuild newserv");
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#include "ARCodeTranslator.hh"
|
||||||
|
|
||||||
|
#include <phosg/Filesystem.hh>
|
||||||
|
#include <phosg/Strings.hh>
|
||||||
|
#include <resource_file/ExecutableFormats/DOLFile.hh>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
void run_ar_code_translator(const std::string& initial_directory) {
|
||||||
|
string directory = initial_directory;
|
||||||
|
while (ends_with(directory, "/")) {
|
||||||
|
directory.resize(directory.size() - 1);
|
||||||
|
}
|
||||||
|
PrefixedLogger log("[ar-trans] ");
|
||||||
|
|
||||||
|
unordered_map<string, shared_ptr<DOLFile>> files;
|
||||||
|
for (const auto& filename : list_directory(directory)) {
|
||||||
|
if (ends_with(filename, ".dol")) {
|
||||||
|
string name = filename.substr(0, filename.size() - 4);
|
||||||
|
string path = directory + "/" + filename;
|
||||||
|
files.emplace(name, new DOLFile(path.c_str()));
|
||||||
|
log.info("Loaded %s", name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string source_filename;
|
||||||
|
shared_ptr<DOLFile> source_file;
|
||||||
|
auto find_match = [&](std::shared_ptr<DOLFile> target_file, uint32_t source_address) -> uint32_t {
|
||||||
|
const DOLFile::Section* source_section = nullptr;
|
||||||
|
for (const auto& sec : source_file->sections) {
|
||||||
|
if (source_address >= sec.address && source_address < sec.address + sec.data.size()) {
|
||||||
|
source_section = &sec;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!source_section) {
|
||||||
|
throw runtime_error("source address not within any section");
|
||||||
|
}
|
||||||
|
size_t source_offset = source_address - source_section->address;
|
||||||
|
size_t source_bytes_available_after = source_section->data.size() - source_offset;
|
||||||
|
log.info("(find_match) Source offset = %08zX with %08zX bytes available after", source_offset, source_bytes_available_after);
|
||||||
|
|
||||||
|
for (size_t match_length = 4;
|
||||||
|
match_length < min<size_t>(source_bytes_available_after, 0x100);
|
||||||
|
match_length += 4) {
|
||||||
|
size_t num_matches = 0;
|
||||||
|
size_t last_match_address = 0;
|
||||||
|
StringReader source_r(source_section->data.data() + source_offset, match_length);
|
||||||
|
for (const auto& target_section : target_file->sections) {
|
||||||
|
for (size_t target_section_offset = 0;
|
||||||
|
target_section_offset + match_length <= target_section.data.size();
|
||||||
|
target_section_offset += 4) {
|
||||||
|
source_r.go(0);
|
||||||
|
StringReader target_r(target_section.data.data() + target_section_offset, match_length);
|
||||||
|
size_t z;
|
||||||
|
for (z = 0; z < match_length; z += 4) {
|
||||||
|
if (source_section->is_text) {
|
||||||
|
uint32_t source_opcode = source_r.get_u32b();
|
||||||
|
uint32_t target_opcode = target_r.get_u32b();
|
||||||
|
uint32_t source_class = source_opcode & 0xFC000000;
|
||||||
|
if (source_class != (target_opcode & 0xFC000000)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (source_class == 0x48000000) {
|
||||||
|
source_opcode &= 0xFC000003;
|
||||||
|
target_opcode &= 0xFC000003;
|
||||||
|
} else if (source_class == 0x40000000) {
|
||||||
|
source_opcode &= 0xFFFF0003;
|
||||||
|
target_opcode &= 0xFFFF0003;
|
||||||
|
}
|
||||||
|
if (source_opcode != target_opcode) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (source_r.get_u32l() != target_r.get_u32l()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (z == match_length) {
|
||||||
|
num_matches++;
|
||||||
|
last_match_address = target_section.address + target_section_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("(find_match) For match length %zX, %zu matches found", match_length, num_matches);
|
||||||
|
if (num_matches == 1) {
|
||||||
|
return last_match_address;
|
||||||
|
} else if (num_matches == 0) {
|
||||||
|
throw runtime_error("did not find exactly one match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw runtime_error("scan field too long; too many matches");
|
||||||
|
};
|
||||||
|
|
||||||
|
while (!feof(stdin)) {
|
||||||
|
if (!source_filename.empty()) {
|
||||||
|
fprintf(stdout, "ar-trans:%s/%s> ", directory.c_str(), source_filename.c_str());
|
||||||
|
} else {
|
||||||
|
fprintf(stdout, "ar-trans:%s> ", directory.c_str());
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
string command = fgets(stdin);
|
||||||
|
try {
|
||||||
|
strip_trailing_whitespace(command);
|
||||||
|
auto tokens = split(command, ' ');
|
||||||
|
if (tokens.empty()) {
|
||||||
|
throw runtime_error("no command given");
|
||||||
|
|
||||||
|
} else if (tokens[0] == "use") {
|
||||||
|
source_filename = tokens.at(1);
|
||||||
|
source_file = files.at(source_filename);
|
||||||
|
|
||||||
|
} else if (tokens[0] == "match") {
|
||||||
|
if (!source_file) {
|
||||||
|
throw runtime_error("no source file selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t source_addr = stoul(tokens.at(1), nullptr, 16);
|
||||||
|
for (const auto& it : files) {
|
||||||
|
if (it.second == source_file) {
|
||||||
|
log.info("(%s) %08" PRIX32, it.first.c_str(), source_addr);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
uint32_t match_addr = find_match(it.second, source_addr);
|
||||||
|
log.info("(%s) %08" PRIX32, it.first.c_str(), match_addr);
|
||||||
|
} catch (const exception& e) {
|
||||||
|
log.error("(%s) failed: %s", it.first.c_str(), e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!tokens[0].empty()) {
|
||||||
|
throw runtime_error("unknown command");
|
||||||
|
}
|
||||||
|
} catch (const exception& e) {
|
||||||
|
log.error("Failed: %s", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void run_ar_code_translator(const std::string& directory);
|
||||||
+1
-1
@@ -214,7 +214,7 @@ struct Client : public std::enable_shared_from_this<Client> {
|
|||||||
|
|
||||||
// File loading state
|
// File loading state
|
||||||
uint32_t dol_base_addr;
|
uint32_t dol_base_addr;
|
||||||
std::shared_ptr<DOLFileIndex::DOLFile> loading_dol_file;
|
std::shared_ptr<DOLFileIndex::File> loading_dol_file;
|
||||||
std::unordered_map<std::string, std::shared_ptr<const std::string>> sending_files;
|
std::unordered_map<std::string, std::shared_ptr<const std::string>> sending_files;
|
||||||
|
|
||||||
Client(
|
Client(
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
|
|||||||
string name = filename.substr(0, filename.size() - (is_compressed_dol ? 8 : 4));
|
string name = filename.substr(0, filename.size() - (is_compressed_dol ? 8 : 4));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
shared_ptr<DOLFile> dol(new DOLFile());
|
shared_ptr<File> dol(new File());
|
||||||
dol->menu_item_id = next_menu_item_id++;
|
dol->menu_item_id = next_menu_item_id++;
|
||||||
dol->name = name;
|
dol->name = name;
|
||||||
|
|
||||||
|
|||||||
@@ -70,15 +70,15 @@ struct FunctionCodeIndex {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct DOLFileIndex {
|
struct DOLFileIndex {
|
||||||
struct DOLFile {
|
struct File {
|
||||||
uint32_t menu_item_id;
|
uint32_t menu_item_id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string data;
|
std::string data;
|
||||||
bool is_compressed;
|
bool is_compressed;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DOLFile>> item_id_to_file;
|
std::vector<std::shared_ptr<File>> item_id_to_file;
|
||||||
std::unordered_map<std::string, std::shared_ptr<DOLFile>> name_to_file;
|
std::unordered_map<std::string, std::shared_ptr<File>> name_to_file;
|
||||||
std::shared_ptr<const Menu> menu;
|
std::shared_ptr<const Menu> menu;
|
||||||
|
|
||||||
DOLFileIndex() = default;
|
DOLFileIndex() = default;
|
||||||
|
|||||||
+17
-1
@@ -14,6 +14,11 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#ifdef HAVE_RESOURCE_FILE
|
||||||
|
#include "ARCodeTranslator.hh"
|
||||||
|
#else
|
||||||
|
#include "ARCodeTranslator-Stub.hh"
|
||||||
|
#endif
|
||||||
#include "BMLArchive.hh"
|
#include "BMLArchive.hh"
|
||||||
#include "CatSession.hh"
|
#include "CatSession.hh"
|
||||||
#include "Compression.hh"
|
#include "Compression.hh"
|
||||||
@@ -337,6 +342,7 @@ enum class Behavior {
|
|||||||
GENERATE_ALL_DC_SERIAL_NUMBERS,
|
GENERATE_ALL_DC_SERIAL_NUMBERS,
|
||||||
INSPECT_DC_SERIAL_NUMBER,
|
INSPECT_DC_SERIAL_NUMBER,
|
||||||
DC_SERIAL_NUMBER_SPEED_TEST,
|
DC_SERIAL_NUMBER_SPEED_TEST,
|
||||||
|
AR_CODE_TRANSLATOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool behavior_takes_input_filename(Behavior b) {
|
static bool behavior_takes_input_filename(Behavior b) {
|
||||||
@@ -382,7 +388,8 @@ static bool behavior_takes_input_filename(Behavior b) {
|
|||||||
(b == Behavior::PARSE_OBJECT_GRAPH) ||
|
(b == Behavior::PARSE_OBJECT_GRAPH) ||
|
||||||
(b == Behavior::REPLAY_LOG) ||
|
(b == Behavior::REPLAY_LOG) ||
|
||||||
(b == Behavior::CAT_CLIENT) ||
|
(b == Behavior::CAT_CLIENT) ||
|
||||||
(b == Behavior::INSPECT_DC_SERIAL_NUMBER);
|
(b == Behavior::INSPECT_DC_SERIAL_NUMBER) ||
|
||||||
|
(b == Behavior::AR_CODE_TRANSLATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool behavior_takes_output_filename(Behavior b) {
|
static bool behavior_takes_output_filename(Behavior b) {
|
||||||
@@ -655,6 +662,8 @@ int main(int argc, char** argv) {
|
|||||||
behavior = Behavior::INSPECT_DC_SERIAL_NUMBER;
|
behavior = Behavior::INSPECT_DC_SERIAL_NUMBER;
|
||||||
} else if (!strcmp(argv[x], "dc-serial-number-speed-test")) {
|
} else if (!strcmp(argv[x], "dc-serial-number-speed-test")) {
|
||||||
behavior = Behavior::DC_SERIAL_NUMBER_SPEED_TEST;
|
behavior = Behavior::DC_SERIAL_NUMBER_SPEED_TEST;
|
||||||
|
} else if (!strcmp(argv[x], "ar-code-translator")) {
|
||||||
|
behavior = Behavior::AR_CODE_TRANSLATOR;
|
||||||
} else {
|
} else {
|
||||||
throw invalid_argument(string_printf("unknown command: %s (try --help)", argv[x]));
|
throw invalid_argument(string_printf("unknown command: %s (try --help)", argv[x]));
|
||||||
}
|
}
|
||||||
@@ -1946,6 +1955,13 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Behavior::AR_CODE_TRANSLATOR:
|
||||||
|
if (!input_filename || !strcmp(input_filename, "-")) {
|
||||||
|
throw invalid_argument("a directory name is required");
|
||||||
|
}
|
||||||
|
run_ar_code_translator(input_filename);
|
||||||
|
break;
|
||||||
|
|
||||||
case Behavior::REPLAY_LOG:
|
case Behavior::REPLAY_LOG:
|
||||||
case Behavior::RUN_SERVER: {
|
case Behavior::RUN_SERVER: {
|
||||||
bool is_replay = behavior == Behavior::REPLAY_LOG;
|
bool is_replay = behavior == Behavior::REPLAY_LOG;
|
||||||
|
|||||||
Reference in New Issue
Block a user