clang-format everything

This commit is contained in:
Martin Michelsen
2023-04-16 15:44:12 -07:00
parent b733f4e199
commit 91e484e514
119 changed files with 5101 additions and 5664 deletions
+28
View File
@@ -0,0 +1,28 @@
Standard: c++20
BasedOnStyle: LLVM
IndentWidth: 2
ColumnLimit: 0
AccessModifierOffset: -2
NamespaceIndentation: None
BreakBeforeBraces: Custom
PointerAlignment: Left
IndentCaseLabels: true
PackConstructorInitializers: CurrentLine
BraceWrapping:
AfterEnum: false
AfterStruct: false
AfterClass: false
SplitEmptyFunction: false
AfterControlStatement: false
AfterNamespace: false
AfterFunction: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
AlignTrailingComments: false
AlignAfterOpenBracket: DontAlign
AlignOperands: DontAlign
AlignEscapedNewlines: Left
+2 -5
View File
@@ -8,8 +8,6 @@
using namespace std; using namespace std;
template <bool IsBigEndian> template <bool IsBigEndian>
struct BMLHeader { struct BMLHeader {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type; using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
@@ -54,13 +52,12 @@ void BMLArchive::load_t() {
size_t gvm_offset = offset; size_t gvm_offset = offset;
offset = (offset + entry.compressed_gvm_size + 0x1F) & (~0x1F); offset = (offset + entry.compressed_gvm_size + 0x1F) & (~0x1F);
this->entries.emplace(entry.filename, Entry{ this->entries.emplace(entry.filename, Entry{data_offset, entry.compressed_size, gvm_offset, entry.compressed_gvm_size});
data_offset, entry.compressed_size, gvm_offset, entry.compressed_gvm_size});
} }
} }
BMLArchive::BMLArchive(shared_ptr<const string> data, bool big_endian) BMLArchive::BMLArchive(shared_ptr<const string> data, bool big_endian)
: data(data) { : data(data) {
if (big_endian) { if (big_endian) {
this->load_t<true>(); this->load_t<true>();
} else { } else {
-2
View File
@@ -8,8 +8,6 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
class BMLArchive { class BMLArchive {
public: public:
BMLArchive(std::shared_ptr<const std::string> data, bool big_endian); BMLArchive(std::shared_ptr<const std::string> data, bool big_endian);
+14 -16
View File
@@ -8,8 +8,8 @@
#include <event2/event.h> #include <event2/event.h>
#include <event2/listener.h> #include <event2/listener.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -24,29 +24,27 @@
#include "Loggers.hh" #include "Loggers.hh"
#include "PSOProtocol.hh" #include "PSOProtocol.hh"
#include "SendCommands.hh" #include "ProxyCommands.hh"
#include "ReceiveCommands.hh" #include "ReceiveCommands.hh"
#include "ReceiveSubcommands.hh" #include "ReceiveSubcommands.hh"
#include "ProxyCommands.hh" #include "SendCommands.hh"
using namespace std; using namespace std;
CatSession::CatSession( CatSession::CatSession(
shared_ptr<struct event_base> base, shared_ptr<struct event_base> base,
const struct sockaddr_storage& remote, const struct sockaddr_storage& remote,
GameVersion version, GameVersion version,
shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file) shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file)
: Shell(base), : Shell(base),
log("[CatSession] ", proxy_server_log.min_level), log("[CatSession] ", proxy_server_log.min_level),
channel( channel(
version, version,
CatSession::dispatch_on_channel_input, CatSession::dispatch_on_channel_input,
CatSession::dispatch_on_channel_error, CatSession::dispatch_on_channel_error,
this, this,
"CatSession"), "CatSession"),
bb_key_file(bb_key_file) { bb_key_file(bb_key_file) {
if (remote.ss_family != AF_INET) { if (remote.ss_family != AF_INET) {
throw runtime_error("remote is not AF_INET"); throw runtime_error("remote is not AF_INET");
} }
@@ -62,7 +60,7 @@ CatSession::CatSession(
this->channel.set_bufferevent(bev); this->channel.set_bufferevent(bev);
if (bufferevent_socket_connect(this->channel.bev.get(), if (bufferevent_socket_connect(this->channel.bev.get(),
reinterpret_cast<const sockaddr*>(&remote), sizeof(struct sockaddr_in)) != 0) { reinterpret_cast<const sockaddr*>(&remote), sizeof(struct sockaddr_in)) != 0) {
throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR())); throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR()));
} }
} }
@@ -133,7 +131,7 @@ void CatSession::on_channel_error(short events) {
} }
} }
void CatSession::print_prompt() { } void CatSession::print_prompt() {}
void CatSession::execute_command(const std::string& command) { void CatSession::execute_command(const std::string& command) {
string full_cmd = parse_data_string(command, nullptr, ParseDataFlags::ALLOW_FILES); string full_cmd = parse_data_string(command, nullptr, ParseDataFlags::ALLOW_FILES);
+5 -7
View File
@@ -2,22 +2,20 @@
#include <event2/event.h> #include <event2/event.h>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <functional> #include <functional>
#include <vector> #include <map>
#include <string>
#include <memory> #include <memory>
#include <phosg/Filesystem.hh> #include <phosg/Filesystem.hh>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
#include "PSOProtocol.hh" #include "PSOProtocol.hh"
#include "ServerState.hh" #include "ServerState.hh"
#include "Shell.hh" #include "Shell.hh"
class CatSession : public Shell { class CatSession : public Shell {
public: public:
CatSession( CatSession(
+29 -41
View File
@@ -1,9 +1,9 @@
#include "Channel.hh" #include "Channel.hh"
#include <errno.h>
#include <event2/buffer.h> #include <event2/buffer.h>
#include <event2/bufferevent.h> #include <event2/bufferevent.h>
#include <event2/event.h> #include <event2/event.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -15,19 +15,13 @@
using namespace std; using namespace std;
extern bool use_terminal_colors; extern bool use_terminal_colors;
static void flush_and_free_bufferevent(struct bufferevent* bev) { static void flush_and_free_bufferevent(struct bufferevent* bev) {
bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_FINISHED); bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_FINISHED);
bufferevent_free(bev); bufferevent_free(bev);
} }
Channel::Channel( Channel::Channel(
GameVersion version, GameVersion version,
on_command_received_t on_command_received, on_command_received_t on_command_received,
@@ -36,14 +30,14 @@ Channel::Channel(
const string& name, const string& name,
TerminalFormat terminal_send_color, TerminalFormat terminal_send_color,
TerminalFormat terminal_recv_color) TerminalFormat terminal_recv_color)
: bev(nullptr, flush_and_free_bufferevent), : bev(nullptr, flush_and_free_bufferevent),
version(version), version(version),
name(name), name(name),
terminal_send_color(terminal_send_color), terminal_send_color(terminal_send_color),
terminal_recv_color(terminal_recv_color), terminal_recv_color(terminal_recv_color),
on_command_received(on_command_received), on_command_received(on_command_received),
on_error(on_error), on_error(on_error),
context_obj(context_obj) { context_obj(context_obj) {
} }
Channel::Channel( Channel::Channel(
@@ -55,14 +49,14 @@ Channel::Channel(
const string& name, const string& name,
TerminalFormat terminal_send_color, TerminalFormat terminal_send_color,
TerminalFormat terminal_recv_color) TerminalFormat terminal_recv_color)
: bev(nullptr, flush_and_free_bufferevent), : bev(nullptr, flush_and_free_bufferevent),
version(version), version(version),
name(name), name(name),
terminal_send_color(terminal_send_color), terminal_send_color(terminal_send_color),
terminal_recv_color(terminal_recv_color), terminal_recv_color(terminal_recv_color),
on_command_received(on_command_received), on_command_received(on_command_received),
on_error(on_error), on_error(on_error),
context_obj(context_obj) { context_obj(context_obj) {
this->set_bufferevent(bev); this->set_bufferevent(bev);
} }
@@ -114,8 +108,6 @@ void Channel::set_bufferevent(struct bufferevent* bev) {
} }
} }
void Channel::disconnect() { void Channel::disconnect() {
if (this->bev.get()) { if (this->bev.get()) {
// If the output buffer is not empty, move the bufferevent into the draining // If the output buffer is not empty, move the bufferevent into the draining
@@ -157,15 +149,12 @@ void Channel::disconnect() {
this->crypt_out.reset(); this->crypt_out.reset();
} }
Channel::Message Channel::recv(bool print_contents) { Channel::Message Channel::recv(bool print_contents) {
struct evbuffer* buf = bufferevent_get_input(this->bev.get()); struct evbuffer* buf = bufferevent_get_input(this->bev.get());
size_t header_size = (this->version == GameVersion::BB) ? 8 : 4; size_t header_size = (this->version == GameVersion::BB) ? 8 : 4;
PSOCommandHeader header; PSOCommandHeader header;
if (evbuffer_copyout(buf, &header, header_size) if (evbuffer_copyout(buf, &header, header_size) < static_cast<ssize_t>(header_size)) {
< static_cast<ssize_t>(header_size)) {
throw out_of_range("no command available"); throw out_of_range("no command available");
} }
@@ -179,7 +168,8 @@ Channel::Message Channel::recv(bool print_contents) {
// is not reflected in the size field. This logic does not occur if encryption // is not reflected in the size field. This logic does not occur if encryption
// is not yet enabled. // is not yet enabled.
size_t command_physical_size = (this->crypt_in.get() && (version == GameVersion::BB)) size_t command_physical_size = (this->crypt_in.get() && (version == GameVersion::BB))
? ((command_logical_size + 7) & ~7) : command_logical_size; ? ((command_logical_size + 7) & ~7)
: command_logical_size;
if (evbuffer_get_length(buf) < command_physical_size) { if (evbuffer_get_length(buf) < command_physical_size) {
throw out_of_range("no command available"); throw out_of_range("no command available");
} }
@@ -190,8 +180,7 @@ Channel::Message Channel::recv(bool print_contents) {
// consistent state. // consistent state.
string header_data(header_size, '\0'); string header_data(header_size, '\0');
if (evbuffer_remove(buf, header_data.data(), header_data.size()) if (evbuffer_remove(buf, header_data.data(), header_data.size()) < static_cast<ssize_t>(header_data.size())) {
< static_cast<ssize_t>(header_data.size())) {
throw logic_error("enough bytes available, but could not remove them"); throw logic_error("enough bytes available, but could not remove them");
} }
if (this->crypt_in.get()) { if (this->crypt_in.get()) {
@@ -199,8 +188,7 @@ Channel::Message Channel::recv(bool print_contents) {
} }
string command_data(command_physical_size - header_size, '\0'); string command_data(command_physical_size - header_size, '\0');
if (evbuffer_remove(buf, command_data.data(), command_data.size()) if (evbuffer_remove(buf, command_data.data(), command_data.size()) < static_cast<ssize_t>(command_data.size())) {
< static_cast<ssize_t>(command_data.size())) {
throw logic_error("enough bytes available, but could not remove them"); throw logic_error("enough bytes available, but could not remove them");
} }
@@ -222,12 +210,14 @@ Channel::Message Channel::recv(bool print_contents) {
} }
if (version == GameVersion::BB) { if (version == GameVersion::BB) {
command_data_log.info("Received from %s (version=BB command=%04hX flag=%08" PRIX32 ")", command_data_log.info(
"Received from %s (version=BB command=%04hX flag=%08" PRIX32 ")",
this->name.c_str(), this->name.c_str(),
header.command(this->version), header.command(this->version),
header.flag(this->version)); header.flag(this->version));
} else { } else {
command_data_log.info("Received from %s (version=%s command=%02hX flag=%02" PRIX32 ")", command_data_log.info(
"Received from %s (version=%s command=%02hX flag=%02" PRIX32 ")",
this->name.c_str(), this->name.c_str(),
name_for_version(this->version), name_for_version(this->version),
header.command(this->version), header.command(this->version),
@@ -245,9 +235,9 @@ Channel::Message Channel::recv(bool print_contents) {
} }
return { return {
.command = header.command(this->version), .command = header.command(this->version),
.flag = header.flag(this->version), .flag = header.flag(this->version),
.data = move(command_data), .data = move(command_data),
}; };
} }
@@ -376,8 +366,6 @@ void Channel::send(const string& data, bool print_contents) {
return this->send(data.data(), data.size(), print_contents); return this->send(data.data(), data.size(), print_contents);
} }
void Channel::dispatch_on_input(struct bufferevent*, void* ctx) { void Channel::dispatch_on_input(struct bufferevent*, void* ctx) {
Channel* ch = reinterpret_cast<Channel*>(ctx); Channel* ch = reinterpret_cast<Channel*>(ctx);
// The client can be disconnected during on_command_received, so we have to // The client can be disconnected during on_command_received, so we have to
-2
View File
@@ -9,8 +9,6 @@
#include "PSOProtocol.hh" #include "PSOProtocol.hh"
#include "Version.hh" #include "Version.hh"
struct Channel { struct Channel {
std::unique_ptr<struct bufferevent, void (*)(struct bufferevent*)> bev; std::unique_ptr<struct bufferevent, void (*)(struct bufferevent*)> bev;
struct sockaddr_storage local_addr; struct sockaddr_storage local_addr;
+46 -57
View File
@@ -2,12 +2,12 @@
#include <string.h> #include <string.h>
#include <vector>
#include <string>
#include <unordered_map>
#include <phosg/Random.hh> #include <phosg/Random.hh>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <phosg/Time.hh> #include <phosg/Time.hh>
#include <string>
#include <unordered_map>
#include <vector>
#include "Client.hh" #include "Client.hh"
#include "Lobby.hh" #include "Lobby.hh"
@@ -21,16 +21,12 @@
using namespace std; using namespace std;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Checks // Checks
class precondition_failed { class precondition_failed {
public: public:
precondition_failed(const std::u16string& user_msg) : user_msg(user_msg) { } precondition_failed(const std::u16string& user_msg) : user_msg(user_msg) {}
~precondition_failed() = default; ~precondition_failed() = default;
const std::u16string& what() const { const std::u16string& what() const {
@@ -64,17 +60,13 @@ static void check_not_version(shared_ptr<Client> c, GameVersion version) {
static void check_is_game(shared_ptr<Lobby> l, bool is_game) { static void check_is_game(shared_ptr<Lobby> l, bool is_game) {
if (l->is_game() != is_game) { if (l->is_game() != is_game) {
throw precondition_failed(is_game ? throw precondition_failed(is_game ? u"$C6This command cannot\nbe used in lobbies." : u"$C6This command cannot\nbe used in games.");
u"$C6This command cannot\nbe used in lobbies." :
u"$C6This command cannot\nbe used in games.");
} }
} }
static void check_is_ep3(shared_ptr<Client> c, bool is_ep3) { static void check_is_ep3(shared_ptr<Client> c, bool is_ep3) {
if (!!(c->flags & Client::Flag::IS_EPISODE_3) != is_ep3) { if (!!(c->flags & Client::Flag::IS_EPISODE_3) != is_ep3) {
throw precondition_failed(is_ep3 ? throw precondition_failed(is_ep3 ? u"$C6This command can only\nbe used in Episode 3." : u"$C6This command cannot\nbe used in Episode 3.");
u"$C6This command can only\nbe used in Episode 3." :
u"$C6This command cannot\nbe used in Episode 3.");
} }
} }
@@ -90,8 +82,6 @@ static void check_is_leader(shared_ptr<Lobby> l, shared_ptr<Client> c) {
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Message commands // Message commands
@@ -888,7 +878,8 @@ static void server_command_ban(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
uint64_t usecs = stoull(encode_sjis(args), nullptr, 0) * 1000000; uint64_t usecs = stoull(encode_sjis(args), nullptr, 0) * 1000000;
size_t unit_offset = 0; size_t unit_offset = 0;
for (; isdigit(args[unit_offset]); unit_offset++); for (; isdigit(args[unit_offset]); unit_offset++)
;
if (args[unit_offset] == 'm') { if (args[unit_offset] == 'm') {
usecs *= 60; usecs *= 60;
} else if (args[unit_offset] == 'h') { } else if (args[unit_offset] == 'h') {
@@ -1160,8 +1151,6 @@ static void proxy_command_item(shared_ptr<ServerState>,
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
typedef void (*server_handler_t)(shared_ptr<ServerState> s, shared_ptr<Lobby> l, typedef void (*server_handler_t)(shared_ptr<ServerState> s, shared_ptr<Lobby> l,
@@ -1176,46 +1165,46 @@ struct ChatCommandDefinition {
static const unordered_map<u16string, ChatCommandDefinition> chat_commands({ static const unordered_map<u16string, ChatCommandDefinition> chat_commands({
// TODO: implement command_help and actually use the usage strings here // TODO: implement command_help and actually use the usage strings here
{u"$allevent", {server_command_lobby_event_all, nullptr, u"Usage:\nallevent <name/ID>"}}, {u"$allevent", {server_command_lobby_event_all, nullptr, u"Usage:\nallevent <name/ID>"}},
{u"$ann", {server_command_announce, nullptr, u"Usage:\nann <message>"}}, {u"$ann", {server_command_announce, nullptr, u"Usage:\nann <message>"}},
{u"$arrow", {server_command_arrow, proxy_command_arrow, u"Usage:\narrow <color>"}}, {u"$arrow", {server_command_arrow, proxy_command_arrow, u"Usage:\narrow <color>"}},
{u"$auction", {server_command_auction, proxy_command_auction, u"Usage:\nauction"}}, {u"$auction", {server_command_auction, proxy_command_auction, u"Usage:\nauction"}},
{u"$ax", {server_command_ax, nullptr, u"Usage:\nax <message>"}}, {u"$ax", {server_command_ax, nullptr, u"Usage:\nax <message>"}},
{u"$ban", {server_command_ban, nullptr, u"Usage:\nban <name-or-number>"}}, {u"$ban", {server_command_ban, nullptr, u"Usage:\nban <name-or-number>"}},
// TODO: implement this on proxy server // TODO: implement this on proxy server
{u"$bbchar", {server_command_convert_char_to_bb, nullptr, u"Usage:\nbbchar <user> <pass> <1-4>"}}, {u"$bbchar", {server_command_convert_char_to_bb, nullptr, u"Usage:\nbbchar <user> <pass> <1-4>"}},
{u"$cheat", {server_command_cheat, nullptr, u"Usage:\ncheat"}}, {u"$cheat", {server_command_cheat, nullptr, u"Usage:\ncheat"}},
{u"$debug", {server_command_debug, nullptr, u"Usage:\ndebug"}}, {u"$debug", {server_command_debug, nullptr, u"Usage:\ndebug"}},
{u"$edit", {server_command_edit, nullptr , u"Usage:\nedit <stat> <value>"}}, {u"$edit", {server_command_edit, nullptr, u"Usage:\nedit <stat> <value>"}},
{u"$event", {server_command_lobby_event, proxy_command_lobby_event, u"Usage:\nevent <name>"}}, {u"$event", {server_command_lobby_event, proxy_command_lobby_event, u"Usage:\nevent <name>"}},
{u"$exit", {server_command_exit, proxy_command_exit, u"Usage:\nexit"}}, {u"$exit", {server_command_exit, proxy_command_exit, u"Usage:\nexit"}},
{u"$gc", {server_command_get_self_card, proxy_command_get_player_card, u"Usage:\ngc"}}, {u"$gc", {server_command_get_self_card, proxy_command_get_player_card, u"Usage:\ngc"}},
{u"$infhp", {server_command_infinite_hp, proxy_command_infinite_hp, u"Usage:\ninfhp"}}, {u"$infhp", {server_command_infinite_hp, proxy_command_infinite_hp, u"Usage:\ninfhp"}},
{u"$inftp", {server_command_infinite_tp, proxy_command_infinite_tp, u"Usage:\ninftp"}}, {u"$inftp", {server_command_infinite_tp, proxy_command_infinite_tp, u"Usage:\ninftp"}},
{u"$item", {server_command_item, proxy_command_item, u"Usage:\nitem <item-code>"}}, {u"$item", {server_command_item, proxy_command_item, u"Usage:\nitem <item-code>"}},
{u"$i", {server_command_item, proxy_command_item, u"Usage:\ni <item-code>"}}, {u"$i", {server_command_item, proxy_command_item, u"Usage:\ni <item-code>"}},
{u"$kick", {server_command_kick, nullptr, u"Usage:\nkick <name-or-number>"}}, {u"$kick", {server_command_kick, nullptr, u"Usage:\nkick <name-or-number>"}},
{u"$li", {server_command_lobby_info, proxy_command_lobby_info, u"Usage:\nli"}}, {u"$li", {server_command_lobby_info, proxy_command_lobby_info, u"Usage:\nli"}},
{u"$maxlevel", {server_command_max_level, nullptr, u"Usage:\nmax_level <level>"}}, {u"$maxlevel", {server_command_max_level, nullptr, u"Usage:\nmax_level <level>"}},
{u"$minlevel", {server_command_min_level, nullptr, u"Usage:\nmin_level <level>"}}, {u"$minlevel", {server_command_min_level, nullptr, u"Usage:\nmin_level <level>"}},
{u"$next", {server_command_next, proxy_command_next, u"Usage:\nnext"}}, {u"$next", {server_command_next, proxy_command_next, u"Usage:\nnext"}},
{u"$password", {server_command_password, nullptr, u"Usage:\nlock [password]\nomit password to\nunlock game"}}, {u"$password", {server_command_password, nullptr, u"Usage:\nlock [password]\nomit password to\nunlock game"}},
{u"$patch", {server_command_patch, proxy_command_patch, u"Usage:\npatch <name>"}}, {u"$patch", {server_command_patch, proxy_command_patch, u"Usage:\npatch <name>"}},
{u"$persist", {server_command_persist, nullptr, u"Usage:\npersist"}}, {u"$persist", {server_command_persist, nullptr, u"Usage:\npersist"}},
{u"$playrec", {server_command_playrec, nullptr, u"Usage:\nplayrec <filename>"}}, {u"$playrec", {server_command_playrec, nullptr, u"Usage:\nplayrec <filename>"}},
{u"$rand", {server_command_rand, proxy_command_rand, u"Usage:\nrand [hex seed]\nomit seed to revert\nto default"}}, {u"$rand", {server_command_rand, proxy_command_rand, u"Usage:\nrand [hex seed]\nomit seed to revert\nto default"}},
{u"$saverec", {server_command_saverec, nullptr, u"Usage:\nsaverec <filename>"}}, {u"$saverec", {server_command_saverec, nullptr, u"Usage:\nsaverec <filename>"}},
{u"$sc", {server_command_send_client, proxy_command_send_client, u"Usage:\nsc <data>"}}, {u"$sc", {server_command_send_client, proxy_command_send_client, u"Usage:\nsc <data>"}},
{u"$secid", {server_command_secid, proxy_command_secid, u"Usage:\nsecid [section ID]\nomit section ID to\nrevert to normal"}}, {u"$secid", {server_command_secid, proxy_command_secid, u"Usage:\nsecid [section ID]\nomit section ID to\nrevert to normal"}},
{u"$silence", {server_command_silence, nullptr, u"Usage:\nsilence <name-or-number>"}}, {u"$silence", {server_command_silence, nullptr, u"Usage:\nsilence <name-or-number>"}},
// TODO: implement this on proxy server // TODO: implement this on proxy server
{u"$song", {server_command_song, proxy_command_song, u"Usage:\nsong <song-number>"}}, {u"$song", {server_command_song, proxy_command_song, u"Usage:\nsong <song-number>"}},
{u"$spec", {server_command_spec, nullptr, u"Usage:\nspec"}}, {u"$spec", {server_command_spec, nullptr, u"Usage:\nspec"}},
{u"$ss", {nullptr, proxy_command_send_server, u"Usage:\nss <data>"}}, {u"$ss", {nullptr, proxy_command_send_server, u"Usage:\nss <data>"}},
{u"$swa", {server_command_switch_assist, proxy_command_switch_assist, u"Usage:\nswa"}}, {u"$swa", {server_command_switch_assist, proxy_command_switch_assist, u"Usage:\nswa"}},
{u"$type", {server_command_lobby_type, nullptr, u"Usage:\ntype <name>"}}, {u"$type", {server_command_lobby_type, nullptr, u"Usage:\ntype <name>"}},
{u"$warp", {server_command_warp, proxy_command_warp, u"Usage:\nwarp <area-number>"}}, {u"$warp", {server_command_warp, proxy_command_warp, u"Usage:\nwarp <area-number>"}},
{u"$what", {server_command_what, nullptr, u"Usage:\nwhat"}}, {u"$what", {server_command_what, nullptr, u"Usage:\nwhat"}},
}); });
struct SplitCommand { struct SplitCommand {
+2 -2
View File
@@ -5,10 +5,10 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "ServerState.hh"
#include "Lobby.hh"
#include "Client.hh" #include "Client.hh"
#include "Lobby.hh"
#include "ProxyServer.hh" #include "ProxyServer.hh"
#include "ServerState.hh"
void on_chat_command(std::shared_ptr<ServerState> s, std::shared_ptr<Lobby> l, void on_chat_command(std::shared_ptr<ServerState> s, std::shared_ptr<Lobby> l,
std::shared_ptr<Client> c, const std::u16string& text); std::shared_ptr<Client> c, const std::u16string& text);
+49 -58
View File
@@ -1,9 +1,9 @@
#include "Client.hh" #include "Client.hh"
#include <errno.h>
#include <event2/buffer.h> #include <event2/buffer.h>
#include <event2/bufferevent.h> #include <event2/bufferevent.h>
#include <event2/event.h> #include <event2/event.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -16,71 +16,64 @@
using namespace std; using namespace std;
const uint64_t CLIENT_CONFIG_MAGIC = 0x492A890E82AC9839; const uint64_t CLIENT_CONFIG_MAGIC = 0x492A890E82AC9839;
FileContentsCache client_options_cache(3600000000ULL);
static atomic<uint64_t> next_id(1); static atomic<uint64_t> next_id(1);
ClientOptions::ClientOptions() ClientOptions::ClientOptions()
: switch_assist(false), : switch_assist(false),
infinite_hp(false), infinite_hp(false),
infinite_tp(false), infinite_tp(false),
debug(false), debug(false),
override_section_id(-1), override_section_id(-1),
override_lobby_event(-1), override_lobby_event(-1),
override_lobby_number(-1), override_lobby_number(-1),
override_random_seed(-1), override_random_seed(-1),
save_files(false), save_files(false),
enable_chat_commands(true), enable_chat_commands(true),
enable_chat_filter(true), enable_chat_filter(true),
enable_player_notifications(false), enable_player_notifications(false),
suppress_remote_login(false), suppress_remote_login(false),
zero_remote_guild_card(false), zero_remote_guild_card(false),
ep3_infinite_meseta(false), ep3_infinite_meseta(false),
red_name(false), red_name(false),
blank_name(false), blank_name(false),
function_call_return_value(-1) { } function_call_return_value(-1) {}
Client::Client( Client::Client(
struct bufferevent* bev, struct bufferevent* bev,
GameVersion version, GameVersion version,
ServerBehavior server_behavior) ServerBehavior server_behavior)
: id(next_id++), : id(next_id++),
log("", client_log.min_level), log("", client_log.min_level),
bb_game_state(0), bb_game_state(0),
flags(flags_for_version(version, -1)), flags(flags_for_version(version, -1)),
channel(bev, version, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN), channel(bev, version, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN),
server_behavior(server_behavior), server_behavior(server_behavior),
should_disconnect(false), should_disconnect(false),
should_send_to_lobby_server(false), should_send_to_lobby_server(false),
should_send_to_proxy_server(false), should_send_to_proxy_server(false),
proxy_destination_address(0), proxy_destination_address(0),
proxy_destination_port(0), proxy_destination_port(0),
x(0.0f), x(0.0f),
z(0.0f), z(0.0f),
area(0), area(0),
lobby_id(0), lobby_id(0),
lobby_client_id(0), lobby_client_id(0),
lobby_arrow_color(0), lobby_arrow_color(0),
preferred_lobby_id(-1), preferred_lobby_id(-1),
save_game_data_event( save_game_data_event(
event_new( event_new(
bufferevent_get_base(bev), -1, EV_TIMEOUT | EV_PERSIST, bufferevent_get_base(bev), -1, EV_TIMEOUT | EV_PERSIST,
&Client::dispatch_save_game_data, this), &Client::dispatch_save_game_data, this),
event_free), event_free),
card_battle_table_number(-1), card_battle_table_number(-1),
card_battle_table_seat_number(0), card_battle_table_seat_number(0),
card_battle_table_seat_state(0), card_battle_table_seat_state(0),
next_exp_value(0), next_exp_value(0),
can_chat(true), can_chat(true),
pending_bb_save_player_index(0), pending_bb_save_player_index(0),
dol_base_addr(0) { dol_base_addr(0) {
this->last_switch_enabled_command.header.subcommand = 0; this->last_switch_enabled_command.header.subcommand = 0;
memset(&this->next_connection_addr, 0, sizeof(this->next_connection_addr)); memset(&this->next_connection_addr, 0, sizeof(this->next_connection_addr));
@@ -141,8 +134,6 @@ void Client::import_config(const ClientConfigBB& cc) {
this->game_data.bb_player_index = cc.bb_player_index; this->game_data.bb_player_index = cc.bb_player_index;
} }
void Client::dispatch_save_game_data(evutil_socket_t, short, void* ctx) { void Client::dispatch_save_game_data(evutil_socket_t, short, void* ctx) {
reinterpret_cast<Client*>(ctx)->save_game_data(); reinterpret_cast<Client*>(ctx)->save_game_data();
} }
+21 -26
View File
@@ -6,23 +6,18 @@
#include "Channel.hh" #include "Channel.hh"
#include "CommandFormats.hh" #include "CommandFormats.hh"
#include "Episode3/BattleRecord.hh"
#include "Episode3/Tournament.hh"
#include "FileContentsCache.hh" #include "FileContentsCache.hh"
#include "FunctionCompiler.hh" #include "FunctionCompiler.hh"
#include "License.hh" #include "License.hh"
#include "PatchFileIndex.hh"
#include "Player.hh"
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
#include "PSOProtocol.hh" #include "PSOProtocol.hh"
#include "PatchFileIndex.hh"
#include "Player.hh"
#include "Text.hh" #include "Text.hh"
#include "Episode3/BattleRecord.hh"
#include "Episode3/Tournament.hh"
extern const uint64_t CLIENT_CONFIG_MAGIC; extern const uint64_t CLIENT_CONFIG_MAGIC;
extern FileContentsCache client_options_cache;
struct ClientOptions { struct ClientOptions {
// Options used on both game and proxy server // Options used on both game and proxy server
@@ -61,45 +56,45 @@ struct Client {
// Note that this flag is NOT set for Episode 3 Trial Edition clients, since // Note that this flag is NOT set for Episode 3 Trial Edition clients, since
// that version is similar enough to the release version of Episode 3 that // that version is similar enough to the release version of Episode 3 that
// newserv does not have to change its behavior at all. // newserv does not have to change its behavior at all.
IS_TRIAL_EDITION = 0x00002000, IS_TRIAL_EDITION = 0x00002000,
// Client is DC v1 // Client is DC v1
IS_DC_V1 = 0x00000010, IS_DC_V1 = 0x00000010,
// For patch server clients, client is Blue Burst rather than PC // For patch server clients, client is Blue Burst rather than PC
IS_BB_PATCH = 0x00000001, IS_BB_PATCH = 0x00000001,
// After joining a lobby, client will no longer send D6 commands when they // After joining a lobby, client will no longer send D6 commands when they
// close message boxes // close message boxes
NO_D6_AFTER_LOBBY = 0x00000002, NO_D6_AFTER_LOBBY = 0x00000002,
// Client has the above flag and has already joined a lobby, or is not GC // Client has the above flag and has already joined a lobby, or is not GC
NO_D6 = 0x00000004, NO_D6 = 0x00000004,
// Client is Episode 3, should be able to see CARD lobbies, and should only // Client is Episode 3, should be able to see CARD lobbies, and should only
// be able to see/join games with the EPISODE_3_ONLY flag // be able to see/join games with the EPISODE_3_ONLY flag
IS_EPISODE_3 = 0x00000008, IS_EPISODE_3 = 0x00000008,
// Client disconnects if it receives B2 (send_function_call) // Client disconnects if it receives B2 (send_function_call)
NO_SEND_FUNCTION_CALL = 0x00000200, NO_SEND_FUNCTION_CALL = 0x00000200,
// Client requires doubly-encrypted code section in send_function_call // Client requires doubly-encrypted code section in send_function_call
ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800, ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800,
// Client supports send_function_call but does not actually run the code // Client supports send_function_call but does not actually run the code
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x00001000, SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x00001000,
// Client is vulnerable to a buffer overflow that we can use to enable // Client is vulnerable to a buffer overflow that we can use to enable
// send_function_call // send_function_call
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000, USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000,
// Client is loading into a game // Client is loading into a game
LOADING = 0x00000020, LOADING = 0x00000020,
// Client is loading a quest // Client is loading a quest
LOADING_QUEST = 0x00000040, LOADING_QUEST = 0x00000040,
// Client is waiting to join an Episode 3 card auction // Client is waiting to join an Episode 3 card auction
AWAITING_CARD_AUCTION = 0x00010000, AWAITING_CARD_AUCTION = 0x00010000,
// Client is in the information menu (login server only) // Client is in the information menu (login server only)
IN_INFORMATION_MENU = 0x00000080, IN_INFORMATION_MENU = 0x00000080,
// Client is at the welcome message (login server only) // Client is at the welcome message (login server only)
AT_WELCOME_MESSAGE = 0x00000100, AT_WELCOME_MESSAGE = 0x00000100,
// Client has already received a 97 (enable saves) command, so don't show // Client has already received a 97 (enable saves) command, so don't show
// the programs menu anymore // the programs menu anymore
SAVE_ENABLED = 0x00000400, SAVE_ENABLED = 0x00000400,
// Client has received newserv's Episode 3 card definitions, so don't send // Client has received newserv's Episode 3 card definitions, so don't send
// them again // them again
HAS_EP3_CARD_DEFS = 0x00004000, HAS_EP3_CARD_DEFS = 0x00004000,
}; };
uint64_t id; uint64_t id;
@@ -138,7 +133,7 @@ struct Client {
uint8_t lobby_arrow_color; // lobby arrow color ID uint8_t lobby_arrow_color; // lobby arrow color ID
int64_t preferred_lobby_id; // <0 = no preference int64_t preferred_lobby_id; // <0 = no preference
ClientGameData game_data; ClientGameData game_data;
std::unique_ptr<struct event, void(*)(struct event*)> save_game_data_event; std::unique_ptr<struct event, void (*)(struct event*)> save_game_data_event;
int16_t card_battle_table_number; int16_t card_battle_table_number;
uint16_t card_battle_table_seat_number; uint16_t card_battle_table_seat_number;
uint16_t card_battle_table_seat_state; uint16_t card_battle_table_seat_state;
+96 -69
View File
@@ -1,17 +1,17 @@
#pragma once #pragma once
#include <string>
#include <stdexcept>
#include <phosg/Strings.hh>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <phosg/Strings.hh>
#include <stdexcept>
#include <string>
#include "PSOProtocol.hh"
#include "Text.hh"
#include "Player.hh"
#include "Episode3/DataIndex.hh" #include "Episode3/DataIndex.hh"
#include "Episode3/DeckState.hh" #include "Episode3/DeckState.hh"
#include "Episode3/MapState.hh" #include "Episode3/MapState.hh"
#include "Episode3/PlayerStateSubordinates.hh" #include "Episode3/PlayerStateSubordinates.hh"
#include "PSOProtocol.hh"
#include "Player.hh"
#include "Text.hh"
#define __packed__ __attribute__((packed)) #define __packed__ __attribute__((packed))
@@ -45,8 +45,6 @@
// Structures are sorted by command number. Long BB commands are placed in order // Structures are sorted by command number. Long BB commands are placed in order
// according to their low byte; for example, command 01EB is in position as EB. // according to their low byte; for example, command 01EB is in position as EB.
// Text escape codes // Text escape codes
// Most text fields allow the use of various escape codes to change decoding, // Most text fields allow the use of various escape codes to change decoding,
@@ -85,8 +83,6 @@
// - - $r: Right arrow // - - $r: Right arrow
// - - $u: Up arrow // - - $u: Up arrow
// This is the format of newserv's security data, which we call the client // This is the format of newserv's security data, which we call the client
// config. This data is opaque to the client, so this structure is not // config. This data is opaque to the client, so this structure is not
// technically part of the PSO protocol. Because it is opaque to the client, we // technically part of the PSO protocol. Because it is opaque to the client, we
@@ -124,8 +120,6 @@ struct ClientConfigBB {
parray<uint8_t, 0x06> unused; parray<uint8_t, 0x06> unused;
} __packed__; } __packed__;
// Patch server commands // Patch server commands
// The patch protocol is identical between PSO PC and PSO BB (the only versions // The patch protocol is identical between PSO PC and PSO BB (the only versions
@@ -309,7 +303,8 @@ struct S_Reconnect {
le_uint16_t unused = 0; le_uint16_t unused = 0;
} __packed__; } __packed__;
struct S_Reconnect_Patch_14 : S_Reconnect<be_uint16_t> { } __packed__; struct S_Reconnect_Patch_14 : S_Reconnect<be_uint16_t> {
} __packed__;
// 15 (S->C): Login failure // 15 (S->C): Login failure
// No arguments // No arguments
@@ -318,8 +313,6 @@ struct S_Reconnect_Patch_14 : S_Reconnect<be_uint16_t> { } __packed__;
// No commands beyond 15 are valid on the patch server. // No commands beyond 15 are valid on the patch server.
// Game server commands // Game server commands
// 00: Invalid command // 00: Invalid command
@@ -482,8 +475,10 @@ struct S_UpdateClientConfig {
ClientConfigT cfg; ClientConfigT cfg;
} __packed__; } __packed__;
struct S_UpdateClientConfig_DC_PC_V3_04 : S_UpdateClientConfig<ClientConfig> { } __packed__; struct S_UpdateClientConfig_DC_PC_V3_04 : S_UpdateClientConfig<ClientConfig> {
struct S_UpdateClientConfig_BB_04 : S_UpdateClientConfig<ClientConfigBB> { } __packed__; } __packed__;
struct S_UpdateClientConfig_BB_04 : S_UpdateClientConfig<ClientConfigBB> {
} __packed__;
// 05: Disconnect // 05: Disconnect
// No arguments // No arguments
@@ -534,8 +529,10 @@ struct S_MenuEntry {
le_uint16_t flags = 0x0F04; // Should be this value, apparently le_uint16_t flags = 0x0F04; // Should be this value, apparently
ptext<CharT, EntryLength> text; ptext<CharT, EntryLength> text;
} __packed__; } __packed__;
struct S_MenuEntry_PC_BB_07_1F : S_MenuEntry<char16_t, 0x11> { } __packed__; struct S_MenuEntry_PC_BB_07_1F : S_MenuEntry<char16_t, 0x11> {
struct S_MenuEntry_DC_V3_07_1F : S_MenuEntry<char, 0x12> { } __packed__; } __packed__;
struct S_MenuEntry_DC_V3_07_1F : S_MenuEntry<char, 0x12> {
} __packed__;
// 08 (C->S): Request game list // 08 (C->S): Request game list
// No arguments // No arguments
@@ -569,8 +566,10 @@ struct S_GameMenuEntry {
// 20 = Is challenge mode // 20 = Is challenge mode
uint8_t flags = 0; uint8_t flags = 0;
} __packed__; } __packed__;
struct S_GameMenuEntry_PC_BB_08 : S_GameMenuEntry<char16_t> { } __packed__; struct S_GameMenuEntry_PC_BB_08 : S_GameMenuEntry<char16_t> {
struct S_GameMenuEntry_DC_V3_08_Ep3_E6 : S_GameMenuEntry<char> { } __packed__; } __packed__;
struct S_GameMenuEntry_DC_V3_08_Ep3_E6 : S_GameMenuEntry<char> {
} __packed__;
// 09 (C->S): Menu item info request // 09 (C->S): Menu item info request
// Server will respond with an 11 command, or an A3 or A5 if the specified menu // Server will respond with an 11 command, or an A3 or A5 if the specified menu
@@ -660,16 +659,20 @@ struct C_MenuSelection_10_Flag01 {
C_MenuSelection_10_Flag00 basic_cmd; C_MenuSelection_10_Flag00 basic_cmd;
ptext<CharT, 0x10> unknown_a1; ptext<CharT, 0x10> unknown_a1;
} __packed__; } __packed__;
struct C_MenuSelection_DC_V3_10_Flag01 : C_MenuSelection_10_Flag01<char> { } __packed__; struct C_MenuSelection_DC_V3_10_Flag01 : C_MenuSelection_10_Flag01<char> {
struct C_MenuSelection_PC_BB_10_Flag01 : C_MenuSelection_10_Flag01<char16_t> { } __packed__; } __packed__;
struct C_MenuSelection_PC_BB_10_Flag01 : C_MenuSelection_10_Flag01<char16_t> {
} __packed__;
template <typename CharT> template <typename CharT>
struct C_MenuSelection_10_Flag02 { struct C_MenuSelection_10_Flag02 {
C_MenuSelection_10_Flag00 basic_cmd; C_MenuSelection_10_Flag00 basic_cmd;
ptext<CharT, 0x10> password; ptext<CharT, 0x10> password;
} __packed__; } __packed__;
struct C_MenuSelection_DC_V3_10_Flag02 : C_MenuSelection_10_Flag02<char> { } __packed__; struct C_MenuSelection_DC_V3_10_Flag02 : C_MenuSelection_10_Flag02<char> {
struct C_MenuSelection_PC_BB_10_Flag02 : C_MenuSelection_10_Flag02<char16_t> { } __packed__; } __packed__;
struct C_MenuSelection_PC_BB_10_Flag02 : C_MenuSelection_10_Flag02<char16_t> {
} __packed__;
template <typename CharT> template <typename CharT>
struct C_MenuSelection_10_Flag03 { struct C_MenuSelection_10_Flag03 {
@@ -677,8 +680,10 @@ struct C_MenuSelection_10_Flag03 {
ptext<CharT, 0x10> unknown_a1; ptext<CharT, 0x10> unknown_a1;
ptext<CharT, 0x10> password; ptext<CharT, 0x10> password;
} __packed__; } __packed__;
struct C_MenuSelection_DC_V3_10_Flag03 : C_MenuSelection_10_Flag03<char> { } __packed__; struct C_MenuSelection_DC_V3_10_Flag03 : C_MenuSelection_10_Flag03<char> {
struct C_MenuSelection_PC_BB_10_Flag03 : C_MenuSelection_10_Flag03<char16_t> { } __packed__; } __packed__;
struct C_MenuSelection_PC_BB_10_Flag03 : C_MenuSelection_10_Flag03<char16_t> {
} __packed__;
// 11 (S->C): Ship info // 11 (S->C): Ship info
// Same format as 01 command. The text appears in a small box in the lower-left // Same format as 01 command. The text appears in a small box in the lower-left
@@ -748,7 +753,8 @@ struct C_WriteFileConfirmation_V3_BB_13_A7 {
// Note: PSO XB seems to ignore the address field, which makes sense given its // Note: PSO XB seems to ignore the address field, which makes sense given its
// networking architecture. // networking architecture.
struct S_Reconnect_19 : S_Reconnect<le_uint16_t> { } __packed__; struct S_Reconnect_19 : S_Reconnect<le_uint16_t> {
} __packed__;
// Because PSO PC and some versions of PSO DC/GC use the same port but different // Because PSO PC and some versions of PSO DC/GC use the same port but different
// protocols, we use a specially-crafted 19 command to send them to two // protocols, we use a specially-crafted 19 command to send them to two
@@ -915,11 +921,14 @@ struct S_GuildCardSearchResult {
SC_MeetUserExtension<CharT> extension; SC_MeetUserExtension<CharT> extension;
} __packed__; } __packed__;
struct S_GuildCardSearchResult_PC_41 struct S_GuildCardSearchResult_PC_41
: S_GuildCardSearchResult<PSOCommandHeaderPC, char16_t> { } __packed__; : S_GuildCardSearchResult<PSOCommandHeaderPC, char16_t> {
} __packed__;
struct S_GuildCardSearchResult_DC_V3_41 struct S_GuildCardSearchResult_DC_V3_41
: S_GuildCardSearchResult<PSOCommandHeaderDCV3, char> { } __packed__; : S_GuildCardSearchResult<PSOCommandHeaderDCV3, char> {
} __packed__;
struct S_GuildCardSearchResult_BB_41 struct S_GuildCardSearchResult_BB_41
: S_GuildCardSearchResult<PSOCommandHeaderBB, char16_t> { } __packed__; : S_GuildCardSearchResult<PSOCommandHeaderBB, char16_t> {
} __packed__;
// 42: Invalid command // 42: Invalid command
// 43: Invalid command // 43: Invalid command
@@ -1084,8 +1093,10 @@ struct S_JoinGame {
uint8_t unused3 = 0; uint8_t unused3 = 0;
} __packed__; } __packed__;
struct S_JoinGame_PC_64 : S_JoinGame<PlayerLobbyDataPC, PlayerDispDataDCPCV3> { } __packed__; struct S_JoinGame_PC_64 : S_JoinGame<PlayerLobbyDataPC, PlayerDispDataDCPCV3> {
struct S_JoinGame_DC_GC_64 : S_JoinGame<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3> { } __packed__; } __packed__;
struct S_JoinGame_DC_GC_64 : S_JoinGame<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3> {
} __packed__;
struct S_JoinGame_GC_Ep3_64 : S_JoinGame_DC_GC_64 { struct S_JoinGame_GC_Ep3_64 : S_JoinGame_DC_GC_64 {
// This field is only present if the game (and client) is Episode 3. Similarly // This field is only present if the game (and client) is Episode 3. Similarly
@@ -1101,7 +1112,8 @@ struct S_JoinGame_XB_64 : S_JoinGame<PlayerLobbyDataXB, PlayerDispDataDCPCV3> {
parray<le_uint32_t, 6> unknown_a1; parray<le_uint32_t, 6> unknown_a1;
} __packed__; } __packed__;
struct S_JoinGame_BB_64 : S_JoinGame<PlayerLobbyDataBB, PlayerDispDataBB> { } __packed__; struct S_JoinGame_BB_64 : S_JoinGame<PlayerLobbyDataBB, PlayerDispDataBB> {
} __packed__;
// 65 (S->C): Add player to game // 65 (S->C): Add player to game
// When a player joins an existing game, the joining player receives a 64 // When a player joins an existing game, the joining player receives a 64
@@ -1134,11 +1146,14 @@ struct S_JoinLobby {
} }
} __packed__; } __packed__;
struct S_JoinLobby_PC_65_67_68 struct S_JoinLobby_PC_65_67_68
: S_JoinLobby<PlayerLobbyDataPC, PlayerDispDataDCPCV3> { } __packed__; : S_JoinLobby<PlayerLobbyDataPC, PlayerDispDataDCPCV3> {
} __packed__;
struct S_JoinLobby_DC_GC_65_67_68_Ep3_EB struct S_JoinLobby_DC_GC_65_67_68_Ep3_EB
: S_JoinLobby<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3> { } __packed__; : S_JoinLobby<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3> {
} __packed__;
struct S_JoinLobby_BB_65_67_68 struct S_JoinLobby_BB_65_67_68
: S_JoinLobby<PlayerLobbyDataBB, PlayerDispDataBB> { } __packed__; : S_JoinLobby<PlayerLobbyDataBB, PlayerDispDataBB> {
} __packed__;
struct S_JoinLobby_XB_65_67_68 { struct S_JoinLobby_XB_65_67_68 {
uint8_t client_id = 0; uint8_t client_id = 0;
@@ -1161,8 +1176,7 @@ struct S_JoinLobby_XB_65_67_68 {
parray<Entry, 12> entries; parray<Entry, 12> entries;
static inline size_t size(size_t used_entries) { static inline size_t size(size_t used_entries) {
return offsetof(S_JoinLobby_XB_65_67_68, entries) return offsetof(S_JoinLobby_XB_65_67_68, entries) + used_entries * sizeof(Entry);
+ used_entries * sizeof(Entry);
} }
} __packed__; } __packed__;
@@ -1267,8 +1281,10 @@ struct SC_SimpleMail_81 {
ptext<CharT, 0x200> text; ptext<CharT, 0x200> text;
} __packed__; } __packed__;
struct SC_SimpleMail_PC_81 : SC_SimpleMail_81<char16_t> { } __packed__; struct SC_SimpleMail_PC_81 : SC_SimpleMail_81<char16_t> {
struct SC_SimpleMail_DC_V3_81 : SC_SimpleMail_81<char> { } __packed__; } __packed__;
struct SC_SimpleMail_DC_V3_81 : SC_SimpleMail_81<char> {
} __packed__;
struct SC_SimpleMail_BB_81 { struct SC_SimpleMail_BB_81 {
le_uint32_t player_tag = 0x00010000; le_uint32_t player_tag = 0x00010000;
@@ -1694,7 +1710,7 @@ struct C_Login_GC_9E : C_Login_DC_PC_GC_9D {
union ClientConfigFields { union ClientConfigFields {
ClientConfig cfg; ClientConfig cfg;
parray<uint8_t, 0x20> data; parray<uint8_t, 0x20> data;
ClientConfigFields() : data() { } ClientConfigFields() : data() {}
} __packed__ client_config; } __packed__ client_config;
} __packed__; } __packed__;
struct C_LoginExtended_GC_9E : C_Login_GC_9E { struct C_LoginExtended_GC_9E : C_Login_GC_9E {
@@ -1774,10 +1790,14 @@ struct S_QuestMenuEntry {
ptext<CharT, 0x20> name; ptext<CharT, 0x20> name;
ptext<CharT, ShortDescLength> short_description; ptext<CharT, ShortDescLength> short_description;
} __packed__; } __packed__;
struct S_QuestMenuEntry_PC_A2_A4 : S_QuestMenuEntry<char16_t, 0x70> { } __packed__; struct S_QuestMenuEntry_PC_A2_A4 : S_QuestMenuEntry<char16_t, 0x70> {
struct S_QuestMenuEntry_DC_GC_A2_A4 : S_QuestMenuEntry<char, 0x70> { } __packed__; } __packed__;
struct S_QuestMenuEntry_XB_A2_A4 : S_QuestMenuEntry<char, 0x80> { } __packed__; struct S_QuestMenuEntry_DC_GC_A2_A4 : S_QuestMenuEntry<char, 0x70> {
struct S_QuestMenuEntry_BB_A2_A4 : S_QuestMenuEntry<char16_t, 0x7A> { } __packed__; } __packed__;
struct S_QuestMenuEntry_XB_A2_A4 : S_QuestMenuEntry<char, 0x80> {
} __packed__;
struct S_QuestMenuEntry_BB_A2_A4 : S_QuestMenuEntry<char16_t, 0x7A> {
} __packed__;
// A3 (S->C): Quest information // A3 (S->C): Quest information
// Same format as 1A/D5 command (plain text) // Same format as 1A/D5 command (plain text)
@@ -1948,8 +1968,10 @@ struct S_ExecuteCode_Footer_B2 {
parray<U32T, 3> unused2; parray<U32T, 3> unused2;
} __packed__; } __packed__;
struct S_ExecuteCode_Footer_GC_B2 : S_ExecuteCode_Footer_B2<true> { } __packed__; struct S_ExecuteCode_Footer_GC_B2 : S_ExecuteCode_Footer_B2<true> {
struct S_ExecuteCode_Footer_DC_PC_XB_BB_B2 : S_ExecuteCode_Footer_B2<false> { } __packed__; } __packed__;
struct S_ExecuteCode_Footer_DC_PC_XB_BB_B2 : S_ExecuteCode_Footer_B2<false> {
} __packed__;
// B3 (C->S): Execute code and/or checksum memory result // B3 (C->S): Execute code and/or checksum memory result
// Not used on versions that don't support the B2 command (see above). // Not used on versions that don't support the B2 command (see above).
@@ -2138,9 +2160,12 @@ struct S_ChoiceSearchEntry {
ItemIDT category_id = 0; ItemIDT category_id = 0;
ptext<CharT, 0x1C> text; ptext<CharT, 0x1C> text;
} __packed__; } __packed__;
struct S_ChoiceSearchEntry_DC_C0 : S_ChoiceSearchEntry<le_uint32_t, char> { } __packed__; struct S_ChoiceSearchEntry_DC_C0 : S_ChoiceSearchEntry<le_uint32_t, char> {
struct S_ChoiceSearchEntry_V3_C0 : S_ChoiceSearchEntry<le_uint16_t, char> { } __packed__; } __packed__;
struct S_ChoiceSearchEntry_PC_BB_C0 : S_ChoiceSearchEntry<le_uint16_t, char16_t> { } __packed__; struct S_ChoiceSearchEntry_V3_C0 : S_ChoiceSearchEntry<le_uint16_t, char> {
} __packed__;
struct S_ChoiceSearchEntry_PC_BB_C0 : S_ChoiceSearchEntry<le_uint16_t, char16_t> {
} __packed__;
// Top-level categories are things like "Level", "Class", etc. // Top-level categories are things like "Level", "Class", etc.
// Choices for each top-level category immediately follow the category, so // Choices for each top-level category immediately follow the category, so
@@ -2175,8 +2200,10 @@ struct C_CreateGame {
// players; if set to 1, it's v2-only. // players; if set to 1, it's v2-only.
uint8_t episode = 0; // 1-4 on V3+ (3 on Episode 3); unused on DC/PC uint8_t episode = 0; // 1-4 on V3+ (3 on Episode 3); unused on DC/PC
} __packed__; } __packed__;
struct C_CreateGame_DC_V3_0C_C1_Ep3_EC : C_CreateGame<char> { } __packed__; struct C_CreateGame_DC_V3_0C_C1_Ep3_EC : C_CreateGame<char> {
struct C_CreateGame_PC_C1 : C_CreateGame<char16_t> { } __packed__; } __packed__;
struct C_CreateGame_PC_C1 : C_CreateGame<char16_t> {
} __packed__;
struct C_CreateGame_BB_C1 : C_CreateGame<char16_t> { struct C_CreateGame_BB_C1 : C_CreateGame<char16_t> {
uint8_t solo_mode = 0; uint8_t solo_mode = 0;
@@ -2187,8 +2214,10 @@ struct C_CreateGame_BB_C1 : C_CreateGame<char16_t> {
// Server does not respond. // Server does not respond.
// The ChoiceSearchConfig structure is defined in Player.hh. // The ChoiceSearchConfig structure is defined in Player.hh.
struct C_ChoiceSearchSelections_DC_C2_C3 : ChoiceSearchConfig<le_uint32_t> { } __packed__; struct C_ChoiceSearchSelections_DC_C2_C3 : ChoiceSearchConfig<le_uint32_t> {
struct C_ChoiceSearchSelections_PC_V3_BB_C2_C3 : ChoiceSearchConfig<le_uint16_t> { } __packed__; } __packed__;
struct C_ChoiceSearchSelections_PC_V3_BB_C2_C3 : ChoiceSearchConfig<le_uint16_t> {
} __packed__;
// C3 (C->S): Execute choice search (DCv2 and later versions) // C3 (C->S): Execute choice search (DCv2 and later versions)
// Same format as C2. The disabled field is unused. // Same format as C2. The disabled field is unused.
@@ -2235,8 +2264,10 @@ struct C_SetBlockedSenders_C6 {
parray<le_uint32_t, Count> blocked_senders; parray<le_uint32_t, Count> blocked_senders;
} __packed__; } __packed__;
struct C_SetBlockedSenders_V3_C6 : C_SetBlockedSenders_C6<30> { } __packed__; struct C_SetBlockedSenders_V3_C6 : C_SetBlockedSenders_C6<30> {
struct C_SetBlockedSenders_BB_C6 : C_SetBlockedSenders_C6<28> { } __packed__; } __packed__;
struct C_SetBlockedSenders_BB_C6 : C_SetBlockedSenders_C6<28> {
} __packed__;
// C7 (C->S): Enable simple mail auto-reply (V3/BB) // C7 (C->S): Enable simple mail auto-reply (V3/BB)
// Same format as 1A/D5 command (plain text). // Same format as 1A/D5 command (plain text).
@@ -2402,8 +2433,10 @@ struct S_InfoBoardEntry_D8 {
ptext<CharT, 0x10> name; ptext<CharT, 0x10> name;
ptext<CharT, 0xAC> message; ptext<CharT, 0xAC> message;
} __packed__; } __packed__;
struct S_InfoBoardEntry_BB_D8 : S_InfoBoardEntry_D8<char16_t> { } __packed__; struct S_InfoBoardEntry_BB_D8 : S_InfoBoardEntry_D8<char16_t> {
struct S_InfoBoardEntry_V3_D8 : S_InfoBoardEntry_D8<char> { } __packed__; } __packed__;
struct S_InfoBoardEntry_V3_D8 : S_InfoBoardEntry_D8<char> {
} __packed__;
// D9 (C->S): Write info board (V3/BB) // D9 (C->S): Write info board (V3/BB)
// Contents are plain text, like 1A/D5. // Contents are plain text, like 1A/D5.
@@ -3187,8 +3220,6 @@ struct S_SetShutdownCommand_BB_01EF {
// FE: Invalid command // FE: Invalid command
// FF: Invalid command // FF: Invalid command
// Now, the game subcommands (used in commands 60, 62, 6C, 6D, C9, and CB). // Now, the game subcommands (used in commands 60, 62, 6C, 6D, C9, and CB).
// These are laid out similarly as above. These structs start with G_ to // These are laid out similarly as above. These structs start with G_ to
// indicate that they are (usually) bidirectional, and are (usually) generated // indicate that they are (usually) bidirectional, and are (usually) generated
@@ -3212,8 +3243,6 @@ struct S_SetShutdownCommand_BB_01EF {
// commands must be used. The 60 and 62 commands exhibit undefined behavior if // commands must be used. The 60 and 62 commands exhibit undefined behavior if
// this limit is exceeded. // this limit is exceeded.
// These common structures are used my many subcommands. // These common structures are used my many subcommands.
struct G_ClientIDHeader { struct G_ClientIDHeader {
uint8_t subcommand = 0; uint8_t subcommand = 0;
@@ -3242,8 +3271,6 @@ struct G_ExtendedHeader {
le_uint32_t size = 0; le_uint32_t size = 0;
} __packed__; } __packed__;
// 6x00: Invalid subcommand // 6x00: Invalid subcommand
// 6x01: Invalid subcommand // 6x01: Invalid subcommand
@@ -3295,8 +3322,10 @@ struct G_SendGuildCard_DC_PC_V3 {
struct G_SendGuildCard_DC_6x06 : G_SendGuildCard_DC_PC_V3<char, 0x11> { struct G_SendGuildCard_DC_6x06 : G_SendGuildCard_DC_PC_V3<char, 0x11> {
parray<uint8_t, 3> unused3; parray<uint8_t, 3> unused3;
} __packed__; } __packed__;
struct G_SendGuildCard_PC_6x06 : G_SendGuildCard_DC_PC_V3<char16_t, 0x24> { } __packed__; struct G_SendGuildCard_PC_6x06 : G_SendGuildCard_DC_PC_V3<char16_t, 0x24> {
struct G_SendGuildCard_V3_6x06 : G_SendGuildCard_DC_PC_V3<char, 0x24> { } __packed__; } __packed__;
struct G_SendGuildCard_V3_6x06 : G_SendGuildCard_DC_PC_V3<char, 0x24> {
} __packed__;
struct G_SendGuildCard_BB_6x06 { struct G_SendGuildCard_BB_6x06 {
G_UnusedHeader header; G_UnusedHeader header;
@@ -5052,8 +5081,6 @@ struct G_EnemyKilled_6xC8 {
// 6xFE: Invalid subcommand // 6xFE: Invalid subcommand
// 6xFF: Invalid subcommand // 6xFF: Invalid subcommand
// Now, the Episode 3 CARD battle subsubcommands (used in commands 6xB3, 6xB4, // Now, the Episode 3 CARD battle subsubcommands (used in commands 6xB3, 6xB4,
// and 6xB5). Note that even though there's no overlap in the subsubcommand // and 6xB5). Note that even though there's no overlap in the subsubcommand
// number space, the various subsubcommands must be used with the correct 6xBx // number space, the various subsubcommands must be used with the correct 6xBx
+13 -22
View File
@@ -2,15 +2,14 @@
#include "StaticGameData.hh" #include "StaticGameData.hh"
CommonItemSet::CommonItemSet(shared_ptr<const string> data) CommonItemSet::CommonItemSet(shared_ptr<const string> data)
: gsl(data, true) { } : gsl(data, true) {}
const CommonItemSet::Table<true>& CommonItemSet::get_table( const CommonItemSet::Table<true>& CommonItemSet::get_table(
Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const { Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const {
// TODO: What should we do for Ep4? // TODO: What should we do for Ep4?
string filename = string_printf("ItemPT%s%s%c%1d.rel", string filename = string_printf(
"ItemPT%s%s%c%1d.rel",
((mode == GameMode::CHALLENGE) ? "c" : ""), ((mode == GameMode::CHALLENGE) ? "c" : ""),
((episode == Episode::EP2) ? "l" : ""), ((episode == Episode::EP2) ? "l" : ""),
tolower(abbreviation_for_difficulty(difficulty)), tolower(abbreviation_for_difficulty(difficulty)),
@@ -24,15 +23,12 @@ const CommonItemSet::Table<true>& CommonItemSet::get_table(
return *reinterpret_cast<const Table<true>*>(data.first); return *reinterpret_cast<const Table<true>*>(data.first);
} }
RELFileSet::RELFileSet(std::shared_ptr<const std::string> data) RELFileSet::RELFileSet(std::shared_ptr<const std::string> data)
: data(data), r(*this->data) { } : data(data),
r(*this->data) {}
ArmorRandomSet::ArmorRandomSet(std::shared_ptr<const std::string> data) ArmorRandomSet::ArmorRandomSet(std::shared_ptr<const std::string> data)
: RELFileSet(data) { : RELFileSet(data) {
// For some reason the footer tables are doubly indirect in this file // For some reason the footer tables are doubly indirect in this file
uint32_t specs_offset_offset = r.pget_u32b(data->size() - 0x10); uint32_t specs_offset_offset = r.pget_u32b(data->size() - 0x10);
uint32_t specs_offset = r.pget_u32b(specs_offset_offset); uint32_t specs_offset = r.pget_u32b(specs_offset_offset);
@@ -54,15 +50,14 @@ ArmorRandomSet::get_unit_table(size_t index) const {
return this->get_table<WeightTableEntry8>(this->tables->at(2), index); return this->get_table<WeightTableEntry8>(this->tables->at(2), index);
} }
ToolRandomSet::ToolRandomSet(std::shared_ptr<const std::string> data) ToolRandomSet::ToolRandomSet(std::shared_ptr<const std::string> data)
: RELFileSet(data) { : RELFileSet(data) {
uint32_t specs_offset = r.pget_u32b(data->size() - 0x10); uint32_t specs_offset = r.pget_u32b(data->size() - 0x10);
this->common_recovery_table_spec = &r.pget<TableSpec>(r.pget_u32b( this->common_recovery_table_spec = &r.pget<TableSpec>(r.pget_u32b(
specs_offset)); specs_offset));
this->rare_recovery_table_spec = &r.pget<TableSpec>(r.pget_u32b( this->rare_recovery_table_spec = &r.pget<TableSpec>(
specs_offset + sizeof(uint32_t)), 2 * sizeof(TableSpec)); r.pget_u32b(specs_offset + sizeof(uint32_t)),
2 * sizeof(TableSpec));
this->tech_disk_table_spec = this->rare_recovery_table_spec + 1; this->tech_disk_table_spec = this->rare_recovery_table_spec + 1;
this->tech_disk_level_table_spec = &r.pget<TableSpec>(r.pget_u32b( this->tech_disk_level_table_spec = &r.pget<TableSpec>(r.pget_u32b(
specs_offset + 2 * sizeof(uint32_t))); specs_offset + 2 * sizeof(uint32_t)));
@@ -88,10 +83,8 @@ ToolRandomSet::get_tech_disk_level_table(size_t index) const {
return this->get_table<TechDiskLevelEntry>(*this->tech_disk_level_table_spec, index); return this->get_table<TechDiskLevelEntry>(*this->tech_disk_level_table_spec, index);
} }
WeaponRandomSet::WeaponRandomSet(std::shared_ptr<const std::string> data) WeaponRandomSet::WeaponRandomSet(std::shared_ptr<const std::string> data)
: RELFileSet(data) { : RELFileSet(data) {
uint32_t offsets_offset = this->r.pget_u32b(data->size() - 0x10); uint32_t offsets_offset = this->r.pget_u32b(data->size() - 0x10);
this->offsets = &this->r.pget<Offsets>(offsets_offset); this->offsets = &this->r.pget<Offsets>(offsets_offset);
} }
@@ -107,16 +100,14 @@ WeaponRandomSet::get_weapon_type_table(size_t index) const {
const parray<WeaponRandomSet::WeightTableEntry32, 6>* const parray<WeaponRandomSet::WeightTableEntry32, 6>*
WeaponRandomSet::get_bonus_type_table(size_t which, size_t index) const { WeaponRandomSet::get_bonus_type_table(size_t which, size_t index) const {
uint32_t base_offset = which uint32_t base_offset = which ? this->offsets->bonus_type_table2 : this->offsets->bonus_type_table1;
? this->offsets->bonus_type_table2 : this->offsets->bonus_type_table1;
return &this->r.pget<parray<WeightTableEntry32, 6>>( return &this->r.pget<parray<WeightTableEntry32, 6>>(
base_offset + sizeof(parray<WeightTableEntry32, 6>) * index); base_offset + sizeof(parray<WeightTableEntry32, 6>) * index);
} }
const WeaponRandomSet::RangeTableEntry* const WeaponRandomSet::RangeTableEntry*
WeaponRandomSet::get_bonus_range(size_t which, size_t index) const { WeaponRandomSet::get_bonus_range(size_t which, size_t index) const {
uint32_t base_offset = which uint32_t base_offset = which ? this->offsets->bonus_range_table2 : this->offsets->bonus_range_table1;
? this->offsets->bonus_range_table2 : this->offsets->bonus_range_table1;
return &this->r.pget<RangeTableEntry>( return &this->r.pget<RangeTableEntry>(
base_offset + sizeof(RangeTableEntry) * index); base_offset + sizeof(RangeTableEntry) * index);
} }
-3
View File
@@ -6,7 +6,6 @@
#include "StaticGameData.hh" #include "StaticGameData.hh"
#include "Text.hh" #include "Text.hh"
class CommonItemSet { class CommonItemSet {
public: public:
template <typename IntT> template <typename IntT>
@@ -248,8 +247,6 @@ private:
GSLArchive gsl; GSLArchive gsl;
}; };
class RELFileSet { class RELFileSet {
public: public:
template <typename ValueT, typename WeightT = ValueT> template <typename ValueT, typename WeightT = ValueT>
+15 -24
View File
@@ -1,9 +1,9 @@
#include "Compression.hh" #include "Compression.hh"
#include <errno.h> #include <errno.h>
#include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
@@ -12,16 +12,14 @@
using namespace std; using namespace std;
PRSCompressor::PRSCompressor(function<void(size_t, size_t)> progress_fn) PRSCompressor::PRSCompressor(function<void(size_t, size_t)> progress_fn)
: progress_fn(progress_fn), : progress_fn(progress_fn),
closed(false), closed(false),
control_byte_offset(0), control_byte_offset(0),
pending_control_bits(0), pending_control_bits(0),
input_bytes(0), input_bytes(0),
compression_offset(0), compression_offset(0),
reverse_log_index(0x100) { reverse_log_index(0x100) {
this->output.put_u8(0); this->output.put_u8(0);
} }
@@ -65,8 +63,8 @@ void PRSCompressor::advance() {
size_t match_size = 0; size_t match_size = 0;
size_t match_loop_bytes = this->compression_offset - match_offset; size_t match_loop_bytes = this->compression_offset - match_offset;
while ((match_size < 0x100) && while ((match_size < 0x100) &&
(this->compression_offset + match_size < this->input_bytes) && (this->compression_offset + match_size < this->input_bytes) &&
(this->reverse_log[(match_offset + (match_size % match_loop_bytes)) & 0x1FFF] == this->forward_log[(this->compression_offset + match_size) & 0xFF])) { (this->reverse_log[(match_offset + (match_size % match_loop_bytes)) & 0x1FFF] == this->forward_log[(this->compression_offset + match_size) & 0xFF])) {
match_size++; match_size++;
} }
@@ -204,8 +202,6 @@ void PRSCompressor::flush_control() {
} }
} }
string prs_compress( string prs_compress(
const void* vdata, size_t size, function<void(size_t, size_t)> progress_fn) { const void* vdata, size_t size, function<void(size_t, size_t)> progress_fn) {
PRSCompressor prs(progress_fn); PRSCompressor prs(progress_fn);
@@ -218,11 +214,10 @@ string prs_compress(
return prs_compress(data.data(), data.size(), progress_fn); return prs_compress(data.data(), data.size(), progress_fn);
} }
class ControlStreamReader { class ControlStreamReader {
public: public:
ControlStreamReader(StringReader& r) : r(r), bits(0x0000) { } ControlStreamReader(StringReader& r) : r(r),
bits(0x0000) {}
bool read() { bool read() {
if (!(this->bits & 0x0100)) { if (!(this->bits & 0x0100)) {
@@ -306,7 +301,7 @@ string prs_decompress(const void* data, size_t size, size_t max_output_size) {
// from another byte in the data stream) // from another byte in the data stream)
count = (a & 7) ? ((a & 7) + 2) : (r.get_u8() + 1); count = (a & 7) ? ((a & 7) + 2) : (r.get_u8() + 1);
// Control 00 = short backreference // Control 00 = short backreference
} else { } else {
// Count comes from 2 bits in the control stream instead of from the // Count comes from 2 bits in the control stream instead of from the
// data stream (and 2 is added). Importantly, the control stream bits // data stream (and 2 is added). Importantly, the control stream bits
@@ -388,8 +383,6 @@ size_t prs_decompress_size(const string& data, size_t max_output_size) {
return prs_decompress_size(data.data(), data.size(), max_output_size); return prs_decompress_size(data.data(), data.size(), max_output_size);
} }
void prs_disassemble(FILE* stream, const void* data, size_t size) { void prs_disassemble(FILE* stream, const void* data, size_t size) {
size_t output_bytes = 0; size_t output_bytes = 0;
StringReader r(data, size); StringReader r(data, size);
@@ -439,8 +432,6 @@ void prs_disassemble(FILE* stream, const std::string& data) {
return prs_disassemble(stream, data.data(), data.size()); return prs_disassemble(stream, data.data(), data.size());
} }
// BC0 is a compression algorithm fairly similar to PRS, but with a simpler set // BC0 is a compression algorithm fairly similar to PRS, but with a simpler set
// of commands. Like PRS, there is a control stream, indicating when to copy a // of commands. Like PRS, there is a control stream, indicating when to copy a
// literal byte from the input and when to copy from a backreference; unlike // literal byte from the input and when to copy from a backreference; unlike
@@ -637,8 +628,8 @@ string bc0_decompress(const string& data) {
memo_offset = (memo_offset + 1) & 0x0FFF; memo_offset = (memo_offset + 1) & 0x0FFF;
} }
// Control bit 1 means to write a byte directly from the input to the // Control bit 1 means to write a byte directly from the input to the
// output. As above, the byte is also written to the memo. // output. As above, the byte is also written to the memo.
} else { } else {
uint8_t v = r.get_u8(); uint8_t v = r.get_u8();
w.put_u8(v); w.put_u8(v);
+2 -4
View File
@@ -2,14 +2,12 @@
#include <stddef.h> #include <stddef.h>
#include <string>
#include <functional>
#include <deque> #include <deque>
#include <functional>
#include <string>
#include "Text.hh" #include "Text.hh"
// Use this class if you need to compress from multiple input buffers, or need // Use this class if you need to compress from multiple input buffers, or need
// to compress multiple chunks and don't want to copy their contents // to compress multiple chunks and don't want to copy their contents
// unnecessarily. (For most common use cases, use prs_compress (below) instead.) // unnecessarily. (For most common use cases, use prs_compress (below) instead.)
+12 -12
View File
@@ -1,28 +1,28 @@
#include "DNSServer.hh" #include "DNSServer.hh"
#include <netinet/in.h>
#include <poll.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <poll.h>
#include <netinet/in.h>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <phosg/Network.hh> #include <phosg/Network.hh>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <vector>
#include <string> #include <string>
#include <vector>
#include "Loggers.hh" #include "Loggers.hh"
#include "NetworkAddresses.hh" #include "NetworkAddresses.hh"
using namespace std; using namespace std;
DNSServer::DNSServer(
shared_ptr<struct event_base> base,
DNSServer::DNSServer(shared_ptr<struct event_base> base, uint32_t local_connect_address, uint32_t external_connect_address)
uint32_t local_connect_address, uint32_t external_connect_address) : : base(base),
base(base), local_connect_address(local_connect_address), local_connect_address(local_connect_address),
external_connect_address(external_connect_address) { } external_connect_address(external_connect_address) {}
DNSServer::~DNSServer() { DNSServer::~DNSServer() {
for (const auto& it : this->fd_to_receive_event) { for (const auto& it : this->fd_to_receive_event) {
@@ -43,9 +43,9 @@ void DNSServer::listen(int port) {
} }
void DNSServer::add_socket(int fd) { void DNSServer::add_socket(int fd) {
unique_ptr<struct event, void(*)(struct event*)> e(event_new(this->base.get(), unique_ptr<struct event, void (*)(struct event*)> e(
fd, EV_READ | EV_PERSIST, &DNSServer::dispatch_on_receive_message, event_new(this->base.get(), fd, EV_READ | EV_PERSIST, &DNSServer::dispatch_on_receive_message, this),
this), event_free); event_free);
event_add(e.get(), nullptr); event_add(e.get(), nullptr);
this->fd_to_receive_event.emplace(fd, move(e)); this->fd_to_receive_event.emplace(fd, move(e));
} }
+3 -4
View File
@@ -3,10 +3,9 @@
#include <event2/event.h> #include <event2/event.h>
#include <memory> #include <memory>
#include <unordered_map>
#include <string>
#include <set> #include <set>
#include <string>
#include <unordered_map>
class DNSServer { class DNSServer {
public: public:
@@ -28,7 +27,7 @@ public:
private: private:
std::shared_ptr<struct event_base> base; std::shared_ptr<struct event_base> base;
std::unordered_map<int, std::unique_ptr<struct event, void(*)(struct event*)>> fd_to_receive_event; std::unordered_map<int, std::unique_ptr<struct event, void (*)(struct event*)>> fd_to_receive_event;
uint32_t local_connect_address; uint32_t local_connect_address;
uint32_t external_connect_address; uint32_t external_connect_address;
+81 -87
View File
@@ -6,8 +6,6 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
// Note: This order matches the order that the cards are defined in the original // Note: This order matches the order that the cards are defined in the original
// code. This is relevant for consistency of results when choosing a random card // code. This is relevant for consistency of results when choosing a random card
// (for God Whim). // (for God Whim).
@@ -24,81 +22,81 @@ const vector<uint16_t> ALL_ASSIST_CARD_IDS = {
AssistEffect assist_effect_number_for_card_id(uint16_t card_id) { AssistEffect assist_effect_number_for_card_id(uint16_t card_id) {
static const unordered_map<uint16_t, AssistEffect> card_id_to_effect({ static const unordered_map<uint16_t, AssistEffect> card_id_to_effect({
{0x00F5, /* 0x0001 */ AssistEffect::DICE_HALF}, {0x00F5, /* 0x0001 */ AssistEffect::DICE_HALF},
{0x00F6, /* 0x0002 */ AssistEffect::DICE_PLUS_1}, {0x00F6, /* 0x0002 */ AssistEffect::DICE_PLUS_1},
{0x00F7, /* 0x0003 */ AssistEffect::DICE_FEVER}, {0x00F7, /* 0x0003 */ AssistEffect::DICE_FEVER},
{0x00F8, /* 0x0004 */ AssistEffect::CARD_RETURN}, {0x00F8, /* 0x0004 */ AssistEffect::CARD_RETURN},
{0x00F9, /* 0x0005 */ AssistEffect::LAND_PRICE}, {0x00F9, /* 0x0005 */ AssistEffect::LAND_PRICE},
{0x00FA, /* 0x0006 */ AssistEffect::POWERLESS_RAIN}, {0x00FA, /* 0x0006 */ AssistEffect::POWERLESS_RAIN},
{0x00FB, /* 0x0007 */ AssistEffect::BRAVE_WIND}, {0x00FB, /* 0x0007 */ AssistEffect::BRAVE_WIND},
{0x00FC, /* 0x0008 */ AssistEffect::SILENT_COLOSSEUM}, {0x00FC, /* 0x0008 */ AssistEffect::SILENT_COLOSSEUM},
{0x00FD, /* 0x0009 */ AssistEffect::RESISTANCE}, {0x00FD, /* 0x0009 */ AssistEffect::RESISTANCE},
{0x00FE, /* 0x000A */ AssistEffect::INDEPENDENT}, {0x00FE, /* 0x000A */ AssistEffect::INDEPENDENT},
{0x00FF, /* 0x000B */ AssistEffect::ASSISTLESS}, {0x00FF, /* 0x000B */ AssistEffect::ASSISTLESS},
{0x0100, /* 0x000C */ AssistEffect::ATK_DICE_2}, {0x0100, /* 0x000C */ AssistEffect::ATK_DICE_2},
{0x0101, /* 0x000D */ AssistEffect::DEFLATION}, {0x0101, /* 0x000D */ AssistEffect::DEFLATION},
{0x0102, /* 0x000E */ AssistEffect::INFLATION}, {0x0102, /* 0x000E */ AssistEffect::INFLATION},
{0x0103, /* 0x000F */ AssistEffect::EXCHANGE}, {0x0103, /* 0x000F */ AssistEffect::EXCHANGE},
{0x0104, /* 0x0010 */ AssistEffect::INFLUENCE}, {0x0104, /* 0x0010 */ AssistEffect::INFLUENCE},
{0x0105, /* 0x0011 */ AssistEffect::SKIP_SET}, {0x0105, /* 0x0011 */ AssistEffect::SKIP_SET},
{0x0106, /* 0x0012 */ AssistEffect::SKIP_MOVE}, {0x0106, /* 0x0012 */ AssistEffect::SKIP_MOVE},
{0x0121, /* 0x0013 */ AssistEffect::SKIP_ACT}, {0x0121, /* 0x0013 */ AssistEffect::SKIP_ACT},
{0x0137, /* 0x0014 */ AssistEffect::SKIP_DRAW}, {0x0137, /* 0x0014 */ AssistEffect::SKIP_DRAW},
{0x0107, /* 0x0015 */ AssistEffect::FLY}, {0x0107, /* 0x0015 */ AssistEffect::FLY},
{0x0108, /* 0x0016 */ AssistEffect::NECROMANCER}, {0x0108, /* 0x0016 */ AssistEffect::NECROMANCER},
{0x0109, /* 0x0017 */ AssistEffect::PERMISSION}, {0x0109, /* 0x0017 */ AssistEffect::PERMISSION},
{0x010A, /* 0x0018 */ AssistEffect::SHUFFLE_ALL}, {0x010A, /* 0x0018 */ AssistEffect::SHUFFLE_ALL},
{0x010B, /* 0x0019 */ AssistEffect::LEGACY}, {0x010B, /* 0x0019 */ AssistEffect::LEGACY},
{0x010C, /* 0x001A */ AssistEffect::ASSIST_REVERSE}, {0x010C, /* 0x001A */ AssistEffect::ASSIST_REVERSE},
{0x010D, /* 0x001B */ AssistEffect::STAMINA}, {0x010D, /* 0x001B */ AssistEffect::STAMINA},
{0x010E, /* 0x001C */ AssistEffect::AP_ABSORPTION}, {0x010E, /* 0x001C */ AssistEffect::AP_ABSORPTION},
{0x010F, /* 0x001D */ AssistEffect::HEAVY_FOG}, {0x010F, /* 0x001D */ AssistEffect::HEAVY_FOG},
{0x0125, /* 0x001E */ AssistEffect::TRASH_1}, {0x0125, /* 0x001E */ AssistEffect::TRASH_1},
{0x0126, /* 0x001F */ AssistEffect::EMPTY_HAND}, {0x0126, /* 0x001F */ AssistEffect::EMPTY_HAND},
{0x0127, /* 0x0020 */ AssistEffect::HITMAN}, {0x0127, /* 0x0020 */ AssistEffect::HITMAN},
{0x0128, /* 0x0021 */ AssistEffect::ASSIST_TRASH}, {0x0128, /* 0x0021 */ AssistEffect::ASSIST_TRASH},
{0x0129, /* 0x0022 */ AssistEffect::SHUFFLE_GROUP}, {0x0129, /* 0x0022 */ AssistEffect::SHUFFLE_GROUP},
{0x012A, /* 0x0023 */ AssistEffect::ASSIST_VANISH}, {0x012A, /* 0x0023 */ AssistEffect::ASSIST_VANISH},
{0x012B, /* 0x0024 */ AssistEffect::CHARITY}, {0x012B, /* 0x0024 */ AssistEffect::CHARITY},
{0x012C, /* 0x0025 */ AssistEffect::INHERITANCE}, {0x012C, /* 0x0025 */ AssistEffect::INHERITANCE},
{0x012D, /* 0x0026 */ AssistEffect::FIX}, {0x012D, /* 0x0026 */ AssistEffect::FIX},
{0x012E, /* 0x0027 */ AssistEffect::MUSCULAR}, {0x012E, /* 0x0027 */ AssistEffect::MUSCULAR},
{0x012F, /* 0x0028 */ AssistEffect::CHANGE_BODY}, {0x012F, /* 0x0028 */ AssistEffect::CHANGE_BODY},
{0x0130, /* 0x0029 */ AssistEffect::GOD_WHIM}, {0x0130, /* 0x0029 */ AssistEffect::GOD_WHIM},
{0x0131, /* 0x002A */ AssistEffect::GOLD_RUSH}, {0x0131, /* 0x002A */ AssistEffect::GOLD_RUSH},
{0x0132, /* 0x002B */ AssistEffect::ASSIST_RETURN}, {0x0132, /* 0x002B */ AssistEffect::ASSIST_RETURN},
{0x0133, /* 0x002C */ AssistEffect::REQUIEM}, {0x0133, /* 0x002C */ AssistEffect::REQUIEM},
{0x0134, /* 0x002D */ AssistEffect::RANSOM}, {0x0134, /* 0x002D */ AssistEffect::RANSOM},
{0x0135, /* 0x002E */ AssistEffect::SIMPLE}, {0x0135, /* 0x002E */ AssistEffect::SIMPLE},
{0x0136, /* 0x002F */ AssistEffect::SLOW_TIME}, {0x0136, /* 0x002F */ AssistEffect::SLOW_TIME},
{0x023F, /* 0x0030 */ AssistEffect::QUICK_TIME}, {0x023F, /* 0x0030 */ AssistEffect::QUICK_TIME},
{0x0138, /* 0x0031 */ AssistEffect::TERRITORY}, {0x0138, /* 0x0031 */ AssistEffect::TERRITORY},
{0x0139, /* 0x0032 */ AssistEffect::OLD_TYPE}, {0x0139, /* 0x0032 */ AssistEffect::OLD_TYPE},
{0x013A, /* 0x0033 */ AssistEffect::FLATLAND}, {0x013A, /* 0x0033 */ AssistEffect::FLATLAND},
{0x013B, /* 0x0034 */ AssistEffect::IMMORTALITY}, {0x013B, /* 0x0034 */ AssistEffect::IMMORTALITY},
{0x013C, /* 0x0035 */ AssistEffect::SNAIL_PACE}, {0x013C, /* 0x0035 */ AssistEffect::SNAIL_PACE},
{0x013D, /* 0x0036 */ AssistEffect::TECH_FIELD}, {0x013D, /* 0x0036 */ AssistEffect::TECH_FIELD},
{0x013E, /* 0x0037 */ AssistEffect::FOREST_RAIN}, {0x013E, /* 0x0037 */ AssistEffect::FOREST_RAIN},
{0x013F, /* 0x0038 */ AssistEffect::CAVE_WIND}, {0x013F, /* 0x0038 */ AssistEffect::CAVE_WIND},
{0x0140, /* 0x0039 */ AssistEffect::MINE_BRIGHTNESS}, {0x0140, /* 0x0039 */ AssistEffect::MINE_BRIGHTNESS},
{0x0141, /* 0x003A */ AssistEffect::RUIN_DARKNESS}, {0x0141, /* 0x003A */ AssistEffect::RUIN_DARKNESS},
{0x0142, /* 0x003B */ AssistEffect::SABER_DANCE}, {0x0142, /* 0x003B */ AssistEffect::SABER_DANCE},
{0x0143, /* 0x003C */ AssistEffect::BULLET_STORM}, {0x0143, /* 0x003C */ AssistEffect::BULLET_STORM},
{0x0144, /* 0x003D */ AssistEffect::CANE_PALACE}, {0x0144, /* 0x003D */ AssistEffect::CANE_PALACE},
{0x0145, /* 0x003E */ AssistEffect::GIANT_GARDEN}, {0x0145, /* 0x003E */ AssistEffect::GIANT_GARDEN},
{0x0146, /* 0x003F */ AssistEffect::MARCH_OF_THE_MEEK}, {0x0146, /* 0x003F */ AssistEffect::MARCH_OF_THE_MEEK},
{0x0148, /* 0x0040 */ AssistEffect::SUPPORT}, {0x0148, /* 0x0040 */ AssistEffect::SUPPORT},
{0x014A, /* 0x0041 */ AssistEffect::RICH}, {0x014A, /* 0x0041 */ AssistEffect::RICH},
{0x014B, /* 0x0042 */ AssistEffect::REVERSE_CARD}, {0x014B, /* 0x0042 */ AssistEffect::REVERSE_CARD},
{0x014C, /* 0x0043 */ AssistEffect::VENGEANCE}, {0x014C, /* 0x0043 */ AssistEffect::VENGEANCE},
{0x014D, /* 0x0044 */ AssistEffect::SQUEEZE}, {0x014D, /* 0x0044 */ AssistEffect::SQUEEZE},
{0x014E, /* 0x0045 */ AssistEffect::HOMESICK}, {0x014E, /* 0x0045 */ AssistEffect::HOMESICK},
{0x0240, /* 0x0046 */ AssistEffect::BOMB}, {0x0240, /* 0x0046 */ AssistEffect::BOMB},
{0x0241, /* 0x0047 */ AssistEffect::SKIP_TURN}, {0x0241, /* 0x0047 */ AssistEffect::SKIP_TURN},
{0x0242, /* 0x0048 */ AssistEffect::BATTLE_ROYALE}, {0x0242, /* 0x0048 */ AssistEffect::BATTLE_ROYALE},
{0x0018, /* 0x0049 */ AssistEffect::DICE_FEVER_PLUS}, {0x0018, /* 0x0049 */ AssistEffect::DICE_FEVER_PLUS},
{0x0019, /* 0x004A */ AssistEffect::RICH_PLUS}, {0x0019, /* 0x004A */ AssistEffect::RICH_PLUS},
{0x001A, /* 0x004B */ AssistEffect::CHARITY_PLUS}, {0x001A, /* 0x004B */ AssistEffect::CHARITY_PLUS},
}); });
try { try {
return card_id_to_effect.at(card_id); return card_id_to_effect.at(card_id);
@@ -107,15 +105,13 @@ AssistEffect assist_effect_number_for_card_id(uint16_t card_id) {
} }
} }
AssistServer::AssistServer(shared_ptr<Server> server) AssistServer::AssistServer(shared_ptr<Server> server)
: w_server(server), : w_server(server),
assist_effects(AssistEffect::NONE), assist_effects(AssistEffect::NONE),
num_assist_cards_set(0), num_assist_cards_set(0),
client_ids_with_assists(0xFF), client_ids_with_assists(0xFF),
active_assist_effects(AssistEffect::NONE), active_assist_effects(AssistEffect::NONE),
num_active_assists(0) { } num_active_assists(0) {}
shared_ptr<Server> AssistServer::server() { shared_ptr<Server> AssistServer::server() {
auto s = this->w_server.lock(); auto s = this->w_server.lock();
@@ -281,6 +277,4 @@ void AssistServer::recompute_effects() {
return; return;
} }
} // namespace Episode3 } // namespace Episode3
+1 -5
View File
@@ -6,13 +6,11 @@
#include <vector> #include <vector>
#include "DataIndex.hh" #include "DataIndex.hh"
#include "PlayerState.hh"
#include "DeckState.hh" #include "DeckState.hh"
#include "PlayerState.hh"
namespace Episode3 { namespace Episode3 {
class Server; class Server;
extern const std::vector<uint16_t> ALL_ASSIST_CARD_IDS; extern const std::vector<uint16_t> ALL_ASSIST_CARD_IDS;
@@ -55,6 +53,4 @@ public:
std::shared_ptr<parray<ActionMetadata, 9>> set_card_action_metadatas[4]; std::shared_ptr<parray<ActionMetadata, 9>> set_card_action_metadatas[4];
}; };
} // namespace Episode3 } // namespace Episode3
+13 -22
View File
@@ -9,8 +9,6 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
BattleRecord::Event::Event(StringReader& r) { BattleRecord::Event::Event(StringReader& r) {
this->type = r.get<Event::Type>(); this->type = r.get<Event::Type>();
this->timestamp = r.get_u64l(); this->timestamp = r.get_u64l();
@@ -74,19 +72,17 @@ void BattleRecord::Event::serialize(StringWriter& w) const {
} }
} }
BattleRecord::BattleRecord(uint32_t behavior_flags) BattleRecord::BattleRecord(uint32_t behavior_flags)
: is_writable(true), : is_writable(true),
behavior_flags(behavior_flags), behavior_flags(behavior_flags),
battle_start_timestamp(0), battle_start_timestamp(0),
battle_end_timestamp(0) { } battle_end_timestamp(0) {}
BattleRecord::BattleRecord(const string& data) BattleRecord::BattleRecord(const string& data)
: is_writable(false), : is_writable(false),
behavior_flags(0), behavior_flags(0),
battle_start_timestamp(0), battle_start_timestamp(0),
battle_end_timestamp(0) { battle_end_timestamp(0) {
StringReader r(data); StringReader r(data);
uint64_t signature = r.get_u64l(); uint64_t signature = r.get_u64l();
if (signature != this->SIGNATURE) { if (signature != this->SIGNATURE) {
@@ -128,7 +124,6 @@ const BattleRecord::Event* BattleRecord::get_first_event() const {
return &this->events.front(); return &this->events.front();
} }
void BattleRecord::add_player( void BattleRecord::add_player(
const PlayerLobbyDataDCGC& lobby_data, const PlayerLobbyDataDCGC& lobby_data,
const PlayerInventory& inventory, const PlayerInventory& inventory,
@@ -279,16 +274,14 @@ void BattleRecord::set_battle_end_timestamp() {
this->battle_end_timestamp = now(); this->battle_end_timestamp = now();
} }
BattleRecordPlayer::BattleRecordPlayer( BattleRecordPlayer::BattleRecordPlayer(
shared_ptr<const BattleRecord> rec, shared_ptr<const BattleRecord> rec,
shared_ptr<struct event_base> base) shared_ptr<struct event_base> base)
: record(rec), : record(rec),
event_it(this->record->events.begin()), event_it(this->record->events.begin()),
play_start_timestamp(0), play_start_timestamp(0),
base(base), base(base),
next_command_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &BattleRecordPlayer::dispatch_schedule_events, this), event_free) { } next_command_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &BattleRecordPlayer::dispatch_schedule_events, this), event_free) {}
shared_ptr<const BattleRecord> BattleRecordPlayer::get_record() const { shared_ptr<const BattleRecord> BattleRecordPlayer::get_record() const {
return this->record; return this->record;
@@ -377,6 +370,4 @@ void BattleRecordPlayer::schedule_events() {
} }
} }
} // namespace Episode3 } // namespace Episode3
+2 -7
View File
@@ -1,13 +1,13 @@
#pragma once #pragma once
#include <stdint.h>
#include <event2/event.h> #include <event2/event.h>
#include <stdint.h>
#include <deque> #include <deque>
#include <memory> #include <memory>
#include <phosg/Strings.hh>
#include <string> #include <string>
#include <variant> #include <variant>
#include <phosg/Strings.hh>
#include "../Player.hh" #include "../Player.hh"
@@ -17,8 +17,6 @@ namespace Episode3 {
// The comment in Server.hh does not apply to this file (and BattleRecord.cc). // The comment in Server.hh does not apply to this file (and BattleRecord.cc).
class BattleRecord { class BattleRecord {
public: public:
struct PlayerEntry { struct PlayerEntry {
@@ -118,7 +116,4 @@ private:
std::shared_ptr<struct event> next_command_ev; std::shared_ptr<struct event> next_command_ev;
}; };
} // namespace Episode3 } // namespace Episode3
+26 -32
View File
@@ -1,36 +1,34 @@
#include "Card.hh" #include "Card.hh"
#include "Server.hh"
#include "../CommandFormats.hh" #include "../CommandFormats.hh"
#include "Server.hh"
using namespace std; using namespace std;
namespace Episode3 { namespace Episode3 {
Card::Card( Card::Card(
uint16_t card_id, uint16_t card_id,
uint16_t card_ref, uint16_t card_ref,
uint16_t client_id, uint16_t client_id,
shared_ptr<Server> server) shared_ptr<Server> server)
: w_server(server), : w_server(server),
w_player_state(server->get_player_state(client_id)), w_player_state(server->get_player_state(client_id)),
client_id(client_id), client_id(client_id),
card_id(card_id), card_id(card_id),
card_ref(card_ref), card_ref(card_ref),
card_flags(0), card_flags(0),
loc(0, 0, Direction::RIGHT), loc(0, 0, Direction::RIGHT),
facing_direction(Direction::INVALID_FF), facing_direction(Direction::INVALID_FF),
action_chain(), action_chain(),
action_metadata(), action_metadata(),
num_ally_fcs_destroyed_at_set_time(0), num_ally_fcs_destroyed_at_set_time(0),
num_cards_destroyed_by_team_at_set_time(0), num_cards_destroyed_by_team_at_set_time(0),
unknown_a9(1), unknown_a9(1),
last_attack_preliminary_damage(0), last_attack_preliminary_damage(0),
last_attack_final_damage(0), last_attack_final_damage(0),
num_destroyed_ally_fcs(0), num_destroyed_ally_fcs(0),
current_defense_power(0) { } current_defense_power(0) {}
void Card::init() { void Card::init() {
this->clear_action_chain_and_metadata_and_most_flags(); this->clear_action_chain_and_metadata_and_most_flags();
@@ -117,7 +115,7 @@ ssize_t Card::apply_abnormal_condition(
existing_cond_index = z; existing_cond_index = z;
if (eff.type == ConditionType::MV_BONUS || if (eff.type == ConditionType::MV_BONUS ||
((cond.card_definition_effect_index == def_effect_index) && ((cond.card_definition_effect_index == def_effect_index) &&
(cond.card_ref == target_card_ref))) { (cond.card_ref == target_card_ref))) {
break; break;
} }
} else { } else {
@@ -215,8 +213,8 @@ void Card::apply_ap_adjust_assists_to_attack(
bool Card::card_type_is_sc_or_creature() const { bool Card::card_type_is_sc_or_creature() const {
return (this->def_entry->def.type == CardType::HUNTERS_SC) || return (this->def_entry->def.type == CardType::HUNTERS_SC) ||
(this->def_entry->def.type == CardType::ARKZ_SC) || (this->def_entry->def.type == CardType::ARKZ_SC) ||
(this->def_entry->def.type == CardType::CREATURE); (this->def_entry->def.type == CardType::CREATURE);
} }
bool Card::check_card_flag(uint32_t flags) const { bool Card::check_card_flag(uint32_t flags) const {
@@ -254,7 +252,6 @@ void Card::commit_attack(
effective_damage = 0; effective_damage = 0;
} }
auto attacker_ps = attacker_card->player_state(); auto attacker_ps = attacker_card->player_state();
attacker_ps->stats.damage_given += effective_damage; attacker_ps->stats.damage_given += effective_damage;
this->player_state()->stats.damage_taken += effective_damage; this->player_state()->stats.damage_taken += effective_damage;
@@ -406,13 +403,13 @@ int32_t Card::error_code_for_move_to_location(const Location& loc) const {
return -0x60; return -0x60;
} }
if (!this->server()->ruler_server->card_ref_can_move( if (!this->server()->ruler_server->card_ref_can_move(
this->client_id, this->card_ref, 1)) { this->client_id, this->card_ref, 1)) {
return -0x7B; return -0x7B;
} }
// Note: The original code passes non-null pointers here but ignores the // Note: The original code passes non-null pointers here but ignores the
// values written to them; we use nulls since the behavior should be the same. // values written to them; we use nulls since the behavior should be the same.
if (!this->server()->ruler_server->get_move_path_length_and_cost( if (!this->server()->ruler_server->get_move_path_length_and_cost(
this->client_id, this->card_ref, loc, nullptr, nullptr)) { this->client_id, this->card_ref, loc, nullptr, nullptr)) {
return -0x79; return -0x79;
} }
return 0; return 0;
@@ -556,7 +553,7 @@ int32_t Card::move_to_location(const Location& loc) {
uint32_t path_cost; uint32_t path_cost;
uint32_t path_length; uint32_t path_length;
if (!this->server()->ruler_server->get_move_path_length_and_cost( if (!this->server()->ruler_server->get_move_path_length_and_cost(
this->client_id, this->card_ref, loc, &path_length, &path_cost)) { this->client_id, this->card_ref, loc, &path_length, &path_cost)) {
return -0x79; return -0x79;
} }
@@ -598,7 +595,6 @@ void Card::propagate_shared_hp_if_needed() {
} }
} }
void Card::send_6xB4x4E_4C_4D_if_needed(bool always_send) { void Card::send_6xB4x4E_4C_4D_if_needed(bool always_send) {
ssize_t index = -1; ssize_t index = -1;
if (this->card_ref == this->player_state()->get_sc_card_ref()) { if (this->card_ref == this->player_state()->get_sc_card_ref()) {
@@ -940,11 +936,11 @@ void Card::unknown_80237F98(bool require_condition_20_or_21) {
if (this->action_chain.conditions[z].type != ConditionType::NONE) { if (this->action_chain.conditions[z].type != ConditionType::NONE) {
if (!require_condition_20_or_21 || if (!require_condition_20_or_21 ||
this->server()->card_special->condition_has_when_20_or_21( this->server()->card_special->condition_has_when_20_or_21(
this->action_chain.conditions[z])) { this->action_chain.conditions[z])) {
ActionState as; ActionState as;
auto& cond = this->action_chain.conditions[z]; auto& cond = this->action_chain.conditions[z];
if (!this->server()->card_special->is_card_targeted_by_condition( if (!this->server()->card_special->is_card_targeted_by_condition(
cond, as, this->shared_from_this())) { cond, as, this->shared_from_this())) {
this->server()->card_special->apply_stat_deltas_to_card_from_condition_and_clear_cond( this->server()->card_special->apply_stat_deltas_to_card_from_condition_and_clear_cond(
cond, this->shared_from_this()); cond, this->shared_from_this());
should_send_updates = true; should_send_updates = true;
@@ -1237,6 +1233,4 @@ void Card::unknown_80237734() {
this->send_6xB4x4E_4C_4D_if_needed(); this->send_6xB4x4E_4C_4D_if_needed();
} }
} // namespace Episode3 } // namespace Episode3
+1 -5
View File
@@ -4,14 +4,12 @@
#include <memory> #include <memory>
#include "../Text.hh"
#include "../CommandFormats.hh" #include "../CommandFormats.hh"
#include "../Text.hh"
#include "DataIndex.hh" #include "DataIndex.hh"
namespace Episode3 { namespace Episode3 {
class ServerBase; class ServerBase;
class Server; class Server;
class PlayerState; class PlayerState;
@@ -126,6 +124,4 @@ public:
int16_t current_defense_power; int16_t current_defense_power;
}; };
} // namespace Episode3 } // namespace Episode3
+241 -236
View File
@@ -6,8 +6,6 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
CardSpecial::DiceRoll::DiceRoll() { CardSpecial::DiceRoll::DiceRoll() {
this->clear(); this->clear();
} }
@@ -20,8 +18,6 @@ void CardSpecial::DiceRoll::clear() {
this->unknown_a5 = 0xFFFF; this->unknown_a5 = 0xFFFF;
} }
CardSpecial::AttackEnvStats::AttackEnvStats() { CardSpecial::AttackEnvStats::AttackEnvStats() {
this->clear(); this->clear();
} }
@@ -73,10 +69,9 @@ uint32_t CardSpecial::AttackEnvStats::at(size_t offset) const {
return reinterpret_cast<const parray<uint32_t, count>*>(this)->at(offset); return reinterpret_cast<const parray<uint32_t, count>*>(this)->at(offset);
} }
CardSpecial::CardSpecial(shared_ptr<Server> server) CardSpecial::CardSpecial(shared_ptr<Server> server)
: w_server(server), unknown_a2(0) { } : w_server(server),
unknown_a2(0) {}
shared_ptr<Server> CardSpecial::server() { shared_ptr<Server> CardSpecial::server() {
auto s = this->w_server.lock(); auto s = this->w_server.lock();
@@ -109,11 +104,11 @@ void CardSpecial::adjust_attack_damage_due_to_conditions(
} }
if (!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( if (!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
cond.card_ref, cond.card_ref,
target_card->get_card_ref(), target_card->get_card_ref(),
attacker_card_ref, attacker_card_ref,
cond.card_definition_effect_index, cond.card_definition_effect_index,
attack_medium)) { attack_medium)) {
continue; continue;
} }
@@ -411,7 +406,7 @@ bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond(
if (hp != ap) { if (hp != ap) {
this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0xA0, hp - ap, 0, 0); this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0xA0, hp - ap, 0, 0);
this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0x20, ap - hp, 0, 0); this->send_6xB4x06_for_stat_delta(card, cond_card_ref, 0x20, ap - hp, 0, 0);
card->set_current_hp(ap,1,1); card->set_current_hp(ap, 1, 1);
card->ap = hp; card->ap = hp;
this->destroy_card_if_hp_zero(card, cond_card_ref); this->destroy_card_if_hp_zero(card, cond_card_ref);
} }
@@ -501,7 +496,7 @@ bool CardSpecial::card_has_condition_with_ref(
uint16_t match_card_ref) const { uint16_t match_card_ref) const {
size_t z = 0; size_t z = 0;
while ((z < 9) && while ((z < 9) &&
((card->action_chain.conditions[z].type != cond_type) || ((card->action_chain.conditions[z].type != cond_type) ||
(card->action_chain.conditions[z].card_ref == card_ref))) { (card->action_chain.conditions[z].card_ref == card_ref))) {
z++; z++;
} }
@@ -532,7 +527,7 @@ void CardSpecial::compute_attack_ap(
: AttackMedium::UNKNOWN; : AttackMedium::UNKNOWN;
uint16_t target_card_ref = target_card->get_card_ref(); uint16_t target_card_ref = target_card->get_card_ref();
auto check_card = [&](shared_ptr<Card> card) -> void{ auto check_card = [&](shared_ptr<Card> card) -> void {
if (!card || (card->card_flags & 3)) { if (!card || (card->card_flags & 3)) {
return; return;
} }
@@ -541,19 +536,19 @@ void CardSpecial::compute_attack_ap(
if (cond.type == ConditionType::NONE || if (cond.type == ConditionType::NONE ||
this->card_ref_has_ability_trap(cond) || this->card_ref_has_ability_trap(cond) ||
!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( !this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
card->action_chain.conditions[cond_index].card_ref, card->action_chain.conditions[cond_index].card_ref,
target_card->get_card_ref(), target_card->get_card_ref(),
attacker_card_ref, attacker_card_ref,
card->action_chain.conditions[cond_index].card_definition_effect_index, card->action_chain.conditions[cond_index].card_definition_effect_index,
attacker_sc_attack_medium)) { attacker_sc_attack_medium)) {
continue; continue;
} }
auto cond_type = card->action_chain.conditions[cond_index].type; auto cond_type = card->action_chain.conditions[cond_index].type;
if (((cond_type == ConditionType::UNKNOWN_5F) && if (((cond_type == ConditionType::UNKNOWN_5F) &&
(target_card_ref == card->action_chain.conditions[cond_index].condition_giver_card_ref)) || (target_card_ref == card->action_chain.conditions[cond_index].condition_giver_card_ref)) ||
((cond_type == ConditionType::UNKNOWN_60) && ((cond_type == ConditionType::UNKNOWN_60) &&
(target_card_ref == card->action_chain.conditions[cond_index].card_ref))) { (target_card_ref == card->action_chain.conditions[cond_index].card_ref))) {
*out_value = card->action_chain.conditions[cond_index].value8; *out_value = card->action_chain.conditions[cond_index].value8;
} }
} }
@@ -570,7 +565,7 @@ void CardSpecial::compute_attack_ap(
} }
if (attacker_card && if (attacker_card &&
attacker_card->get_attack_condition_value(ConditionType::UNKNOWN_7D, 0xFFFF,0xFF,0xFFFF, nullptr)) { attacker_card->get_attack_condition_value(ConditionType::UNKNOWN_7D, 0xFFFF, 0xFF, 0xFFFF, nullptr)) {
*out_value = *out_value * 1.5f; *out_value = *out_value * 1.5f;
} }
if (target_card && if (target_card &&
@@ -611,7 +606,8 @@ CardSpecial::AttackEnvStats CardSpecial::compute_attack_env_stats(
ast.total_num_set_cards = ps_num_set_cards; ast.total_num_set_cards = ps_num_set_cards;
uint8_t target_card_team_id = target_card uint8_t target_card_team_id = target_card
? target_card->player_state()->get_team_id() : 0xFF; ? target_card->player_state()->get_team_id()
: 0xFF;
size_t target_team_num_set_cards = 0; size_t target_team_num_set_cards = 0;
size_t condition_giver_team_num_set_cards = 0; size_t condition_giver_team_num_set_cards = 0;
@@ -629,19 +625,26 @@ CardSpecial::AttackEnvStats CardSpecial::compute_attack_env_stats(
ast.condition_giver_team_num_set_cards = condition_giver_team_num_set_cards; ast.condition_giver_team_num_set_cards = condition_giver_team_num_set_cards;
ast.num_native_creatures = this->get_all_set_cards_by_team_and_class( ast.num_native_creatures = this->get_all_set_cards_by_team_and_class(
CardClass::NATIVE_CREATURE, 0xFF, true).size(); CardClass::NATIVE_CREATURE, 0xFF, true)
.size();
ast.num_a_beast_creatures = this->get_all_set_cards_by_team_and_class( ast.num_a_beast_creatures = this->get_all_set_cards_by_team_and_class(
CardClass::A_BEAST_CREATURE, 0xFF, true).size(); CardClass::A_BEAST_CREATURE, 0xFF, true)
.size();
ast.num_machine_creatures = this->get_all_set_cards_by_team_and_class( ast.num_machine_creatures = this->get_all_set_cards_by_team_and_class(
CardClass::MACHINE_CREATURE, 0xFF, true).size(); CardClass::MACHINE_CREATURE, 0xFF, true)
.size();
ast.num_dark_creatures = this->get_all_set_cards_by_team_and_class( ast.num_dark_creatures = this->get_all_set_cards_by_team_and_class(
CardClass::DARK_CREATURE, 0xFF, true).size(); CardClass::DARK_CREATURE, 0xFF, true)
.size();
ast.num_sword_type_items = this->get_all_set_cards_by_team_and_class( ast.num_sword_type_items = this->get_all_set_cards_by_team_and_class(
CardClass::SWORD_ITEM, 0xFF, true).size(); CardClass::SWORD_ITEM, 0xFF, true)
.size();
ast.num_gun_type_items = this->get_all_set_cards_by_team_and_class( ast.num_gun_type_items = this->get_all_set_cards_by_team_and_class(
CardClass::GUN_ITEM, 0xFF, true).size(); CardClass::GUN_ITEM, 0xFF, true)
.size();
ast.num_cane_type_items = this->get_all_set_cards_by_team_and_class( ast.num_cane_type_items = this->get_all_set_cards_by_team_and_class(
CardClass::CANE_ITEM, 0xFF, true).size(); CardClass::CANE_ITEM, 0xFF, true)
.size();
ast.num_sword_type_items_on_team = card ast.num_sword_type_items_on_team = card
? this->get_all_set_cards_by_team_and_class(CardClass::SWORD_ITEM, card->get_team_id(), true).size() ? this->get_all_set_cards_by_team_and_class(CardClass::SWORD_ITEM, card->get_team_id(), true).size()
: 0; : 0;
@@ -672,11 +675,14 @@ CardSpecial::AttackEnvStats CardSpecial::compute_attack_env_stats(
ast.team_dice_boost = card ? this->server()->team_dice_boost[card->get_team_id()] : 0; ast.team_dice_boost = card ? this->server()->team_dice_boost[card->get_team_id()] : 0;
ast.effective_ap_if_not_tech = (!attacker_card || (attacker_card->action_chain.chain.attack_medium == AttackMedium::TECH)) ast.effective_ap_if_not_tech = (!attacker_card || (attacker_card->action_chain.chain.attack_medium == AttackMedium::TECH))
? 0 : attacker_card->action_chain.chain.damage; ? 0
: attacker_card->action_chain.chain.damage;
ast.effective_ap_if_not_tech2 = (!attacker_card || (attacker_card->action_chain.chain.attack_medium == AttackMedium::TECH)) ast.effective_ap_if_not_tech2 = (!attacker_card || (attacker_card->action_chain.chain.attack_medium == AttackMedium::TECH))
? 0 : attacker_card->action_chain.chain.damage; ? 0
: attacker_card->action_chain.chain.damage;
ast.effective_ap_if_not_physical = (!attacker_card || (attacker_card->action_chain.chain.attack_medium == AttackMedium::PHYSICAL)) ast.effective_ap_if_not_physical = (!attacker_card || (attacker_card->action_chain.chain.attack_medium == AttackMedium::PHYSICAL))
? 0 : attacker_card->action_chain.chain.damage; ? 0
: attacker_card->action_chain.chain.damage;
ast.sc_effective_ap = attacker_card ? attacker_card->action_chain.chain.damage : 0; ast.sc_effective_ap = attacker_card ? attacker_card->action_chain.chain.damage : 0;
ast.attack_bonus = card->action_metadata.attack_bonus; ast.attack_bonus = card->action_metadata.attack_bonus;
ast.last_attack_preliminary_damage = card->last_attack_preliminary_damage; ast.last_attack_preliminary_damage = card->last_attack_preliminary_damage;
@@ -707,7 +713,8 @@ CardSpecial::AttackEnvStats CardSpecial::compute_attack_env_stats(
// original code. // original code.
for (z = 0; for (z = 0;
((target_card_ref != pa.attacker_card_ref) && (z < 9) && (pa.action_card_refs[z] != 0xFFFF)); ((target_card_ref != pa.attacker_card_ref) && (z < 9) && (pa.action_card_refs[z] != 0xFFFF));
z++) { } z++) {
}
ast.action_cards_ap = 0; ast.action_cards_ap = 0;
ast.action_cards_tp = 0; ast.action_cards_tp = 0;
for (; (z < 9) && (pa.action_card_refs[z] != 0xFFFF); z++) { for (; (z < 9) && (pa.action_card_refs[z] != 0xFFFF); z++) {
@@ -789,11 +796,11 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
continue; continue;
} }
if (!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( if (!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
target_card->action_chain.conditions[x].card_ref, target_card->action_chain.conditions[x].card_ref,
target_card->get_card_ref(), target_card->get_card_ref(),
attacker_card_ref, attacker_card_ref,
target_card->action_chain.conditions[x].card_definition_effect_index, target_card->action_chain.conditions[x].card_definition_effect_index,
attack_medium)) { attack_medium)) {
continue; continue;
} }
if (target_card->action_chain.conditions[x].type != ConditionType::PARRY) { if (target_card->action_chain.conditions[x].type != ConditionType::PARRY) {
@@ -871,11 +878,11 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
continue; continue;
} }
if (!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( if (!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
other_set_card->action_chain.conditions[z].card_ref, other_set_card->action_chain.conditions[z].card_ref,
other_set_card->get_card_ref(), other_set_card->get_card_ref(),
attacker_card_ref, attacker_card_ref,
other_set_card->action_chain.conditions[z].card_definition_effect_index, other_set_card->action_chain.conditions[z].card_definition_effect_index,
attack_medium)) { attack_medium)) {
continue; continue;
} }
@@ -896,9 +903,9 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
(unknown_p4 != 0) && (unknown_p4 != 0) &&
(target_card_ref == other_set_card->action_chain.conditions[z].condition_giver_card_ref)) { (target_card_ref == other_set_card->action_chain.conditions[z].condition_giver_card_ref)) {
candidate_cards.emplace_back(other_set_card); candidate_cards.emplace_back(other_set_card);
if (unknown_p11 &&(def_effect_index != 0xFF) && (set_card_ref != 0xFFFF) && if (unknown_p11 && (def_effect_index != 0xFF) && (set_card_ref != 0xFFFF) &&
!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( !this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
set_card_ref, sc_card_ref, other_set_card->get_card_ref(), def_effect_index, attack_medium)) { set_card_ref, sc_card_ref, other_set_card->get_card_ref(), def_effect_index, attack_medium)) {
*unknown_p11 = 1; *unknown_p11 = 1;
} }
} }
@@ -950,11 +957,11 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
continue; continue;
} }
if (!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( if (!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
cond.card_ref, cond.card_ref,
other_sc->get_card_ref(), other_sc->get_card_ref(),
attacker_card_ref, attacker_card_ref,
cond.card_definition_effect_index, cond.card_definition_effect_index,
attack_medium)) { attack_medium)) {
continue; continue;
} }
@@ -977,7 +984,7 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
candidate_cards.emplace_back(other_sc); candidate_cards.emplace_back(other_sc);
if (unknown_p11 && (def_effect_index != 0xFF) && (set_card_ref != 0xFFFF) && if (unknown_p11 && (def_effect_index != 0xFF) && (set_card_ref != 0xFFFF) &&
!this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( !this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
set_card_ref, sc_card_ref, other_sc->get_card_ref(), def_effect_index, attack_medium)) { set_card_ref, sc_card_ref, other_sc->get_card_ref(), def_effect_index, attack_medium)) {
*unknown_p11 = 1; *unknown_p11 = 1;
} }
} }
@@ -1262,7 +1269,7 @@ bool CardSpecial::evaluate_effect_arg2_condition(
auto set_card = this->server()->card_for_set_card_ref(set_card_ref); auto set_card = this->server()->card_for_set_card_ref(set_card_ref);
bool set_card_has_ability_trap = (set_card && bool set_card_has_ability_trap = (set_card &&
(this->card_has_condition_with_ref(set_card, ConditionType::ABILITY_TRAP, 0xFFFF, 0xFFFF))); (this->card_has_condition_with_ref(set_card, ConditionType::ABILITY_TRAP, 0xFFFF, 0xFFFF)));
switch (arg2_text[0]) { switch (arg2_text[0]) {
case 'C': case 'C':
@@ -1392,15 +1399,15 @@ bool CardSpecial::evaluate_effect_arg2_condition(
case 13: { case 13: {
auto ce = card->get_definition(); auto ce = card->get_definition();
return ((ce->def.card_class() == CardClass::GUARD_ITEM) || return ((ce->def.card_class() == CardClass::GUARD_ITEM) ||
(ce->def.card_class() == CardClass::MAG_ITEM) || (ce->def.card_class() == CardClass::MAG_ITEM) ||
this->server()->ruler_server->find_condition_on_card_ref( this->server()->ruler_server->find_condition_on_card_ref(
card->get_card_ref(), ConditionType::GUARD_CREATURE, 0, 0, 0)); card->get_card_ref(), ConditionType::GUARD_CREATURE, 0, 0, 0));
} }
case 14: case 14:
return card->get_definition()->def.is_sc(); return card->get_definition()->def.is_sc();
case 15: case 15:
return ((card->action_chain.chain.attack_action_card_ref_count == 0) && return ((card->action_chain.chain.attack_action_card_ref_count == 0) &&
(card->action_metadata.defense_card_ref_count == 0)); (card->action_metadata.defense_card_ref_count == 0));
case 16: case 16:
return this->server()->ruler_server->card_ref_is_aerial(card->get_card_ref()); return this->server()->ruler_server->card_ref_is_aerial(card->get_card_ref());
case 17: { case 17: {
@@ -1462,14 +1469,14 @@ bool CardSpecial::evaluate_effect_arg2_condition(
} }
} }
return (this->find_condition_with_parameters( return (this->find_condition_with_parameters(
card, ConditionType::ANY, set_card_ref, ((v % 10) == 0) ? 0xFF : (v % 10)) != nullptr); card, ConditionType::ANY, set_card_ref, ((v % 10) == 0) ? 0xFF : (v % 10)) != nullptr);
} }
case 'r': case 'r':
return !set_card_has_ability_trap && (random_percent < atoi(arg2_text + 1)); return !set_card_has_ability_trap && (random_percent < atoi(arg2_text + 1));
case 's': { case 's': {
auto ce = card->get_definition(); auto ce = card->get_definition();
return ((ce->def.self_cost >= arg2_text[1] - '0') && return ((ce->def.self_cost >= arg2_text[1] - '0') &&
(ce->def.self_cost <= arg2_text[2] - '0')); (ce->def.self_cost <= arg2_text[2] - '0'));
} }
case 't': { case 't': {
auto set_card = this->server()->card_for_set_card_ref(set_card_ref); auto set_card = this->server()->card_for_set_card_ref(set_card_ref);
@@ -1597,8 +1604,8 @@ bool CardSpecial::execute_effect(
if ((card->card_flags & 3) || if ((card->card_flags & 3) ||
(card->action_metadata.check_flag(0x10) && (card->action_metadata.check_flag(0x10) &&
(cond.card_ref != card->get_card_ref()) && (cond.card_ref != card->get_card_ref()) &&
(cond.condition_giver_card_ref != card->get_card_ref()))) { (cond.condition_giver_card_ref != card->get_card_ref()))) {
unknown_p7 = unknown_p7 & 0xFFFFFFFB; unknown_p7 = unknown_p7 & 0xFFFFFFFB;
} }
if (unknown_p7 == 0) { if (unknown_p7 == 0) {
@@ -1887,8 +1894,7 @@ bool CardSpecial::execute_effect(
case ConditionType::BONUS_FROM_LEADER: case ConditionType::BONUS_FROM_LEADER:
if (unknown_p7 & 1) { if (unknown_p7 & 1) {
clamped_unknown_p5 = this->count_cards_with_card_id_set_by_player_except_card_ref(expr_value, 0xFFFF) clamped_unknown_p5 = this->count_cards_with_card_id_set_by_player_except_card_ref(expr_value, 0xFFFF) + (card->action_chain).chain.ap_effect_bonus;
+ (card->action_chain).chain.ap_effect_bonus;
(card->action_chain).chain.ap_effect_bonus = clamp<int16_t>(clamped_unknown_p5, -99, 99); (card->action_chain).chain.ap_effect_bonus = clamp<int16_t>(clamped_unknown_p5, -99, 99);
} }
return true; return true;
@@ -2069,9 +2075,9 @@ bool CardSpecial::execute_effect(
auto sc_card = this->server()->card_for_set_card_ref(card_ref); auto sc_card = this->server()->card_for_set_card_ref(card_ref);
if (sc_card && (sc_card->get_current_hp() > 0)) { if (sc_card && (sc_card->get_current_hp() > 0)) {
if (this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( if (this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
cond.card_ref, cond.condition_giver_card_ref, cond.card_ref, cond.condition_giver_card_ref,
sc_card->get_card_ref(), cond.card_definition_effect_index, sc_card->get_card_ref(), cond.card_definition_effect_index,
attack_medium)) { attack_medium)) {
this->send_6xB4x06_for_stat_delta(sc_card, attacker_card_ref, 0x20, -sc_card->get_current_hp(), 0, 1); this->send_6xB4x06_for_stat_delta(sc_card, attacker_card_ref, 0x20, -sc_card->get_current_hp(), 0, 1);
sc_card->set_current_hp(0); sc_card->set_current_hp(0);
this->destroy_card_if_hp_zero(sc_card, attacker_card_ref); this->destroy_card_if_hp_zero(sc_card, attacker_card_ref);
@@ -2288,11 +2294,11 @@ bool CardSpecial::execute_effect(
auto set_card = this->server()->card_for_set_card_ref(card_ref); auto set_card = this->server()->card_for_set_card_ref(card_ref);
if (set_card && (set_card->get_current_hp() > 0)) { if (set_card && (set_card->get_current_hp() > 0)) {
if (this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( if (this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
cond.card_ref, cond.card_ref,
cond.condition_giver_card_ref, cond.condition_giver_card_ref,
set_card->get_card_ref(), set_card->get_card_ref(),
cond.card_definition_effect_index, cond.card_definition_effect_index,
attack_medium)) { attack_medium)) {
this->send_6xB4x06_for_stat_delta( this->send_6xB4x06_for_stat_delta(
set_card, attacker_card_ref, 0x20, -set_card->get_current_hp(), 0, 1); set_card, attacker_card_ref, 0x20, -set_card->get_current_hp(), 0, 1);
set_card->set_current_hp(0); set_card->set_current_hp(0);
@@ -2391,7 +2397,7 @@ void CardSpecial::get_effective_ap_tp(
} }
const char* CardSpecial::get_next_expr_token( const char* CardSpecial::get_next_expr_token(
const char *expr, ExpressionTokenType* out_type, int32_t* out_value) const { const char* expr, ExpressionTokenType* out_type, int32_t* out_value) const {
switch (*expr) { switch (*expr) {
case '\0': case '\0':
*out_type = ExpressionTokenType::SPACE; *out_type = ExpressionTokenType::SPACE;
@@ -2427,11 +2433,11 @@ const char* CardSpecial::get_next_expr_token(
*out_type = ExpressionTokenType::SPACE; *out_type = ExpressionTokenType::SPACE;
*out_value = 0x27; *out_value = 0x27;
static const vector<const char*> tokens({ static const vector<const char*> tokens = {
"f", "d", "ap", "tp", "hp", "mhp", "dm", "tdm", "tf", "ac", "php", "f", "d", "ap", "tp", "hp", "mhp", "dm", "tdm", "tf", "ac", "php",
"dc", "cs", "a", "kap", "ktp", "dn", "hf", "df", "ff", "ef", "bi", "dc", "cs", "a", "kap", "ktp", "dn", "hf", "df", "ff", "ef", "bi",
"ab", "mc", "dk", "sa", "gn", "wd", "tt", "lv", "adm", "ddm", "sat", "ab", "mc", "dk", "sa", "gn", "wd", "tt", "lv", "adm", "ddm", "sat",
"edm", "ldm", "rdm", "fdm", "ndm", "ehp"}); "edm", "ldm", "rdm", "fdm", "ndm", "ehp"};
for (size_t z = 0; z < tokens.size(); z++) { for (size_t z = 0; z < tokens.size(); z++) {
if (token_buf == tokens[z]) { if (token_buf == tokens[z]) {
*out_type = ExpressionTokenType::REFERENCE; *out_type = ExpressionTokenType::REFERENCE;
@@ -2467,7 +2473,8 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
} }
auto card2 = this->server()->card_for_set_card_ref((as.attacker_card_ref == 0xFFFF) auto card2 = this->server()->card_for_set_card_ref((as.attacker_card_ref == 0xFFFF)
? as.original_attacker_card_ref : as.attacker_card_ref); ? as.original_attacker_card_ref
: as.attacker_card_ref);
Location card1_loc; Location card1_loc;
if (!card1) { if (!card1) {
@@ -2526,7 +2533,8 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
break; break;
case 4: case 4:
size_t z; size_t z;
for (z = 0; (z < 9) && (as.action_card_refs[z] != 0xFFFF) && (as.action_card_refs[z] != card_ref); z++) { } for (z = 0; (z < 9) && (as.action_card_refs[z] != 0xFFFF) && (as.action_card_refs[z] != card_ref); z++) {
}
for (; (z < 9) && (as.action_card_refs[z] != 0xFFFF); z++) { for (; (z < 9) && (as.action_card_refs[z] != 0xFFFF); z++) {
auto result_card = this->server()->card_for_set_card_ref(as.action_card_refs[z]); auto result_card = this->server()->card_for_set_card_ref(as.action_card_refs[z]);
if (result_card) { if (result_card) {
@@ -2971,7 +2979,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
for (size_t set_index = 0; set_index < 8; set_index++) { for (size_t set_index = 0; set_index < 8; set_index++) {
auto result_card = ps->get_set_card(set_index); auto result_card = ps->get_set_card(set_index);
if (result_card && (card1 != result_card) && if (result_card && (card1 != result_card) &&
(result_card->get_definition()->def.type == CardType::ITEM)) { (result_card->get_definition()->def.type == CardType::ITEM)) {
bool should_add = true; bool should_add = true;
for (auto c : ret) { for (auto c : ret) {
if (c == result_card) { if (c == result_card) {
@@ -2992,7 +3000,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
vector<shared_ptr<const Card>> filtered_ret; vector<shared_ptr<const Card>> filtered_ret;
for (auto c : ret) { for (auto c : ret) {
if (this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs( if (this->server()->ruler_server->check_usability_or_apply_condition_for_card_refs(
card_ref, setter_card_ref, c->get_card_ref(), def_effect_index, attack_medium)) { card_ref, setter_card_ref, c->get_card_ref(), def_effect_index, attack_medium)) {
filtered_ret.emplace_back(c); filtered_ret.emplace_back(c);
} }
} }
@@ -3176,8 +3184,8 @@ bool CardSpecial::should_cancel_condition_due_to_anti_abnormality(
} }
if ((card->card_flags & 3) || if ((card->card_flags & 3) ||
(card->action_metadata.check_flag(0x10) && (card->action_metadata.check_flag(0x10) &&
(card->get_card_ref() != target_card_ref) && (card->get_card_ref() != target_card_ref) &&
(card->get_card_ref() != sc_card_ref))) { (card->get_card_ref() != sc_card_ref))) {
return true; return true;
} }
auto ce = card->get_definition(); auto ce = card->get_definition();
@@ -3519,15 +3527,15 @@ void CardSpecial::unknown_8024C2B0(
bool all_targets_matched = false; bool all_targets_matched = false;
if (!targeted_cards.empty() && if (!targeted_cards.empty() &&
((card_effect.type == ConditionType::UNKNOWN_64) || ((card_effect.type == ConditionType::UNKNOWN_64) ||
(card_effect.type == ConditionType::MISC_DEFENSE_BONUSES) || (card_effect.type == ConditionType::MISC_DEFENSE_BONUSES) ||
(card_effect.type == ConditionType::MOSTLY_HALFGUARDS))) { (card_effect.type == ConditionType::MOSTLY_HALFGUARDS))) {
size_t count = 0; size_t count = 0;
for (size_t z = 0; z < targeted_cards.size(); z++) { for (size_t z = 0; z < targeted_cards.size(); z++) {
dice_roll.value_used_in_expr = false; dice_roll.value_used_in_expr = false;
string arg2_text = card_effect.arg2; string arg2_text = card_effect.arg2;
if (this->evaluate_effect_arg2_condition( if (this->evaluate_effect_arg2_condition(
as, targeted_cards[z], arg2_text.c_str(), dice_roll, as, targeted_cards[z], arg2_text.c_str(), dice_roll,
set_card_ref, sc_card_ref, random_percent, when)) { set_card_ref, sc_card_ref, random_percent, when)) {
count++; count++;
} }
if (dice_roll.value_used_in_expr) { if (dice_roll.value_used_in_expr) {
@@ -3554,7 +3562,7 @@ void CardSpecial::unknown_8024C2B0(
string arg2_str = card_effect.arg2; string arg2_str = card_effect.arg2;
if (all_targets_matched || if (all_targets_matched ||
this->evaluate_effect_arg2_condition( this->evaluate_effect_arg2_condition(
as, targeted_cards[z], arg2_str.c_str(), dice_roll, set_card_ref, sc_card_ref, random_percent, when)) { as, targeted_cards[z], arg2_str.c_str(), dice_roll, set_card_ref, sc_card_ref, random_percent, when)) {
auto env_stats = this->compute_attack_env_stats( auto env_stats = this->compute_attack_env_stats(
as, targeted_cards[z], dice_roll, set_card_ref, sc_card_ref); as, targeted_cards[z], dice_roll, set_card_ref, sc_card_ref);
string expr_str = card_effect.expr; string expr_str = card_effect.expr;
@@ -3577,8 +3585,7 @@ void CardSpecial::unknown_8024C2B0(
} }
ssize_t applied_cond_index = -1; ssize_t applied_cond_index = -1;
if ((unknown_v1 == 0) && !this->should_cancel_condition_due_to_anti_abnormality( if ((unknown_v1 == 0) && !this->should_cancel_condition_due_to_anti_abnormality(card_effect, target_card, dice_cmd.effect.target_card_ref, sc_card_ref)) {
card_effect, target_card, dice_cmd.effect.target_card_ref, sc_card_ref)) {
applied_cond_index = target_card->apply_abnormal_condition( applied_cond_index = target_card->apply_abnormal_condition(
card_effect, def_effect_index, dice_cmd.effect.target_card_ref, sc_card_ref, value, dice_roll.value, random_percent); card_effect, def_effect_index, dice_cmd.effect.target_card_ref, sc_card_ref, value, dice_roll.value, random_percent);
// This debug_print call is in the original code. // This debug_print call is in the original code.
@@ -3713,152 +3720,152 @@ const InterferenceProbabilityEntry* get_interference_probability_entry(
uint16_t column_card_id, uint16_t column_card_id,
bool is_attack) { bool is_attack) {
static const InterferenceProbabilityEntry entries[] = { static const InterferenceProbabilityEntry entries[] = {
{0x0004, 0xFF, 0xFF}, {0x0004, 0xFF, 0xFF},
{0x0002, 0x04, 0x00}, {0x0002, 0x04, 0x00},
{0x0002, 0x00, 0x0F}, {0x0002, 0x00, 0x0F},
{0x0003, 0x03, 0x00}, {0x0003, 0x03, 0x00},
{0x0003, 0x00, 0x0A}, {0x0003, 0x00, 0x0A},
{0x0006, 0x01, 0x00}, {0x0006, 0x01, 0x00},
{0x0006, 0x00, 0x05}, {0x0006, 0x00, 0x05},
{0x0111, 0x01, 0x00}, {0x0111, 0x01, 0x00},
{0x0111, 0x00, 0x05}, {0x0111, 0x00, 0x05},
{0x0001, 0x03, 0x00}, {0x0001, 0x03, 0x00},
{0x0001, 0x00, 0x0A}, {0x0001, 0x00, 0x0A},
{0x0002, 0xFF, 0xFF}, {0x0002, 0xFF, 0xFF},
{0x0004, 0x04, 0x00}, {0x0004, 0x04, 0x00},
{0x0004, 0x00, 0x0F}, {0x0004, 0x00, 0x0F},
{0x0003, 0x06, 0x00}, {0x0003, 0x06, 0x00},
{0x0003, 0x00, 0x14}, {0x0003, 0x00, 0x14},
{0x0006, 0x04, 0x00}, {0x0006, 0x04, 0x00},
{0x0006, 0x00, 0x0F}, {0x0006, 0x00, 0x0F},
{0x0003, 0xFF, 0xFF}, {0x0003, 0xFF, 0xFF},
{0x0004, 0x04, 0x00}, {0x0004, 0x04, 0x00},
{0x0004, 0x00, 0x0F}, {0x0004, 0x00, 0x0F},
{0x0002, 0x04, 0x00}, {0x0002, 0x04, 0x00},
{0x0002, 0x00, 0x0F}, {0x0002, 0x00, 0x0F},
{0x0006, 0xFF, 0xFF}, {0x0006, 0xFF, 0xFF},
{0x0002, 0x06, 0x00}, {0x0002, 0x06, 0x00},
{0x0002, 0x00, 0x14}, {0x0002, 0x00, 0x14},
{0x0111, 0xFF, 0xFF}, {0x0111, 0xFF, 0xFF},
{0x0004, 0x01, 0x00}, {0x0004, 0x01, 0x00},
{0x0004, 0x00, 0x05}, {0x0004, 0x00, 0x05},
{0x0001, 0x06, 0x00}, {0x0001, 0x06, 0x00},
{0x0001, 0x00, 0x14}, {0x0001, 0x00, 0x14},
{0x0001, 0xFF, 0xFF}, {0x0001, 0xFF, 0xFF},
{0x0111, 0x04, 0x00}, {0x0111, 0x04, 0x00},
{0x0111, 0x00, 0x0F}, {0x0111, 0x00, 0x0F},
{0x0112, 0xFF, 0xFF}, {0x0112, 0xFF, 0xFF},
{0x0113, 0x06, 0x00}, {0x0113, 0x06, 0x00},
{0x0113, 0x00, 0x14}, {0x0113, 0x00, 0x14},
{0x0110, 0x06, 0x00}, {0x0110, 0x06, 0x00},
{0x0110, 0x00, 0x14}, {0x0110, 0x00, 0x14},
{0x0114, 0x01, 0x00}, {0x0114, 0x01, 0x00},
{0x0114, 0x00, 0x05}, {0x0114, 0x00, 0x05},
{0x011D, 0x02, 0x00}, {0x011D, 0x02, 0x00},
{0x011D, 0x00, 0x07}, {0x011D, 0x00, 0x07},
{0x0113, 0xFF, 0xFF}, {0x0113, 0xFF, 0xFF},
{0x0003, 0x03, 0x00}, {0x0003, 0x03, 0x00},
{0x0003, 0x00, 0x0A}, {0x0003, 0x00, 0x0A},
{0x0112, 0x03, 0x00}, {0x0112, 0x03, 0x00},
{0x0112, 0x00, 0x0A}, {0x0112, 0x00, 0x0A},
{0x0110, 0xFF, 0xFF}, {0x0110, 0xFF, 0xFF},
{0x0005, 0x03, 0x00}, {0x0005, 0x03, 0x00},
{0x0005, 0x00, 0x0A}, {0x0005, 0x00, 0x0A},
{0x0112, 0x04, 0x00}, {0x0112, 0x04, 0x00},
{0x0112, 0x00, 0x0F}, {0x0112, 0x00, 0x0F},
{0x0005, 0xFF, 0xFF}, {0x0005, 0xFF, 0xFF},
{0x0110, 0x03, 0x00}, {0x0110, 0x03, 0x00},
{0x0110, 0x00, 0x0A}, {0x0110, 0x00, 0x0A},
{0x0114, 0xFF, 0xFF}, {0x0114, 0xFF, 0xFF},
{0x0005, 0x03, 0x00}, {0x0005, 0x03, 0x00},
{0x0005, 0x00, 0x0A}, {0x0005, 0x00, 0x0A},
{0x0110, 0x01, 0x00}, {0x0110, 0x01, 0x00},
{0x0110, 0x00, 0x05}, {0x0110, 0x00, 0x05},
{0x0115, 0x06, 0x00}, {0x0115, 0x06, 0x00},
{0x0115, 0x00, 0x14}, {0x0115, 0x00, 0x14},
{0x0115, 0xFF, 0xFF}, {0x0115, 0xFF, 0xFF},
{0x0004, 0x01, 0x00}, {0x0004, 0x01, 0x00},
{0x0004, 0x00, 0x05}, {0x0004, 0x00, 0x05},
{0x0003, 0x01, 0x00}, {0x0003, 0x01, 0x00},
{0x0003, 0x00, 0x05}, {0x0003, 0x00, 0x05},
{0x0006, 0x01, 0x00}, {0x0006, 0x01, 0x00},
{0x0006, 0x00, 0x05}, {0x0006, 0x00, 0x05},
{0x0112, 0x01, 0x00}, {0x0112, 0x01, 0x00},
{0x0112, 0x00, 0x05}, {0x0112, 0x00, 0x05},
{0x0110, 0x01, 0x00}, {0x0110, 0x01, 0x00},
{0x0110, 0x00, 0x05}, {0x0110, 0x00, 0x05},
{0x0114, 0x04, 0x00}, {0x0114, 0x04, 0x00},
{0x0114, 0x00, 0x0F}, {0x0114, 0x00, 0x0F},
{0x0008, 0xFF, 0xFF}, {0x0008, 0xFF, 0xFF},
{0x0007, 0x06, 0x00}, {0x0007, 0x06, 0x00},
{0x0007, 0x00, 0x14}, {0x0007, 0x00, 0x14},
{0x0116, 0x01, 0x00}, {0x0116, 0x01, 0x00},
{0x0116, 0x00, 0x05}, {0x0116, 0x00, 0x05},
{0x011E, 0x03, 0x00}, {0x011E, 0x03, 0x00},
{0x011E, 0x00, 0x0A}, {0x011E, 0x00, 0x0A},
{0x0118, 0x06, 0x00}, {0x0118, 0x06, 0x00},
{0x0118, 0x00, 0x14}, {0x0118, 0x00, 0x14},
{0x0007, 0xFF, 0xFF}, {0x0007, 0xFF, 0xFF},
{0x0008, 0x06, 0x00}, {0x0008, 0x06, 0x00},
{0x0008, 0x00, 0x14}, {0x0008, 0x00, 0x14},
{0x0118, 0x01, 0x00}, {0x0118, 0x01, 0x00},
{0x0118, 0x00, 0x05}, {0x0118, 0x00, 0x05},
{0x011B, 0x03, 0x00}, {0x011B, 0x03, 0x00},
{0x011B, 0x00, 0x0A}, {0x011B, 0x00, 0x0A},
{0x0116, 0xFF, 0xFF}, {0x0116, 0xFF, 0xFF},
{0x0008, 0x01, 0x00}, {0x0008, 0x01, 0x00},
{0x0008, 0x00, 0x05}, {0x0008, 0x00, 0x05},
{0x011C, 0x03, 0x00}, {0x011C, 0x03, 0x00},
{0x011C, 0x00, 0x0A}, {0x011C, 0x00, 0x0A},
{0x011A, 0xFF, 0xFF}, {0x011A, 0xFF, 0xFF},
{0x0119, 0x04, 0x00}, {0x0119, 0x04, 0x00},
{0x0119, 0x00, 0x0F}, {0x0119, 0x00, 0x0F},
{0x011D, 0x04, 0x00}, {0x011D, 0x04, 0x00},
{0x011D, 0x00, 0x0F}, {0x011D, 0x00, 0x0F},
{0x0119, 0xFF, 0xFF}, {0x0119, 0xFF, 0xFF},
{0x011A, 0x04, 0x00}, {0x011A, 0x04, 0x00},
{0x011A, 0x00, 0x0F}, {0x011A, 0x00, 0x0F},
{0x011D, 0x04, 0x00}, {0x011D, 0x04, 0x00},
{0x011D, 0x00, 0x0F}, {0x011D, 0x00, 0x0F},
{0x011D, 0xFF, 0xFF}, {0x011D, 0xFF, 0xFF},
{0x0119, 0x04, 0x00}, {0x0119, 0x04, 0x00},
{0x0119, 0x00, 0x0F}, {0x0119, 0x00, 0x0F},
{0x011A, 0x04, 0x00}, {0x011A, 0x04, 0x00},
{0x011A, 0x00, 0x0F}, {0x011A, 0x00, 0x0F},
{0x0112, 0x01, 0x00}, {0x0112, 0x01, 0x00},
{0x0112, 0x00, 0x07}, {0x0112, 0x00, 0x07},
{0x011E, 0xFF, 0xFF}, {0x011E, 0xFF, 0xFF},
{0x0008, 0x03, 0x00}, {0x0008, 0x03, 0x00},
{0x0008, 0x00, 0x0A}, {0x0008, 0x00, 0x0A},
{0x0118, 0x06, 0x00}, {0x0118, 0x06, 0x00},
{0x0118, 0x00, 0x14}, {0x0118, 0x00, 0x14},
{0x011C, 0xFF, 0xFF}, {0x011C, 0xFF, 0xFF},
{0x0116, 0x04, 0x00}, {0x0116, 0x04, 0x00},
{0x0116, 0x00, 0x0F}, {0x0116, 0x00, 0x0F},
{0x011E, 0x01, 0x00}, {0x011E, 0x01, 0x00},
{0x011E, 0x00, 0x05}, {0x011E, 0x00, 0x05},
{0x0118, 0xFF, 0xFF}, {0x0118, 0xFF, 0xFF},
{0x011E, 0x06, 0x00}, {0x011E, 0x06, 0x00},
{0x011E, 0x00, 0x14}, {0x011E, 0x00, 0x14},
{0x011B, 0xFF, 0xFF}, {0x011B, 0xFF, 0xFF},
{0x0007, 0x03, 0x00}, {0x0007, 0x03, 0x00},
{0x0007, 0x00, 0x0A}, {0x0007, 0x00, 0x0A},
{0x0117, 0x03, 0x00}, {0x0117, 0x03, 0x00},
{0x0117, 0x00, 0x0A}, {0x0117, 0x00, 0x0A},
{0x011F, 0x06, 0x00}, {0x011F, 0x06, 0x00},
{0x011F, 0x00, 0x14}, {0x011F, 0x00, 0x14},
{0x0117, 0xFF, 0xFF}, {0x0117, 0xFF, 0xFF},
{0x011F, 0x03, 0x00}, {0x011F, 0x03, 0x00},
{0x011F, 0x00, 0x0A}, {0x011F, 0x00, 0x0A},
{0x011B, 0x04, 0x00}, {0x011B, 0x04, 0x00},
{0x011B, 0x00, 0x0F}, {0x011B, 0x00, 0x0F},
{0x011F, 0xFF, 0xFF}, {0x011F, 0xFF, 0xFF},
{0x0007, 0x01, 0x00}, {0x0007, 0x01, 0x00},
{0x0007, 0x00, 0x05}, {0x0007, 0x00, 0x05},
{0x011B, 0x06, 0x00}, {0x011B, 0x06, 0x00},
{0x011B, 0x00, 0x14}, {0x011B, 0x00, 0x14},
{0x0117, 0x04, 0x00}, {0x0117, 0x04, 0x00},
{0x0117, 0x00, 0x0F}, {0x0117, 0x00, 0x0F},
}; };
constexpr size_t num_entries = sizeof(entries) / sizeof(entries[0]); constexpr size_t num_entries = sizeof(entries) / sizeof(entries[0]);
@@ -4526,6 +4533,4 @@ vector<shared_ptr<const Card>> CardSpecial::find_all_sc_cards_of_class(
return ret; return ret;
} }
} // namespace Episode3 } // namespace Episode3
+1 -7
View File
@@ -9,8 +9,6 @@
namespace Episode3 { namespace Episode3 {
struct InterferenceProbabilityEntry { struct InterferenceProbabilityEntry {
uint16_t card_id; uint16_t card_id;
uint8_t attack_probability; uint8_t attack_probability;
@@ -22,8 +20,6 @@ const InterferenceProbabilityEntry* get_interference_probability_entry(
uint16_t column_card_id, uint16_t column_card_id,
bool is_attack); bool is_attack);
class CardSpecial { class CardSpecial {
public: public:
enum class ExpressionTokenType { enum class ExpressionTokenType {
@@ -224,7 +220,7 @@ public:
int16_t ap, int16_t ap,
int16_t tp); int16_t tp);
const char* get_next_expr_token( const char* get_next_expr_token(
const char *expr, ExpressionTokenType* out_type, int32_t* out_value) const; const char* expr, ExpressionTokenType* out_type, int32_t* out_value) const;
std::vector<std::shared_ptr<const Card>> get_targeted_cards_for_condition( std::vector<std::shared_ptr<const Card>> get_targeted_cards_for_condition(
uint16_t card_ref, uint16_t card_ref,
uint8_t def_effect_index, uint8_t def_effect_index,
@@ -341,6 +337,4 @@ private:
uint16_t unknown_a2; uint16_t unknown_a2;
}; };
} // namespace Episode3 } // namespace Episode3
+349 -362
View File
@@ -8,18 +8,16 @@
#include <phosg/Random.hh> #include <phosg/Random.hh>
#include <phosg/Time.hh> #include <phosg/Time.hh>
#include "../Loggers.hh"
#include "../Compression.hh" #include "../Compression.hh"
#include "../Loggers.hh"
#include "../PSOEncryption.hh" #include "../PSOEncryption.hh"
#include "../Text.hh"
#include "../Quest.hh" #include "../Quest.hh"
#include "../Text.hh"
using namespace std; using namespace std;
namespace Episode3 { namespace Episode3 {
const char* name_for_attack_medium(AttackMedium medium) { const char* name_for_attack_medium(AttackMedium medium) {
switch (medium) { switch (medium) {
case AttackMedium::UNKNOWN: case AttackMedium::UNKNOWN:
@@ -37,12 +35,13 @@ const char* name_for_attack_medium(AttackMedium medium) {
} }
} }
Location::Location() : Location(0, 0) {}
Location::Location(uint8_t x, uint8_t y) : Location(x, y, Direction::RIGHT) {}
Location::Location() : Location(0, 0) { }
Location::Location(uint8_t x, uint8_t y) : Location(x, y, Direction::RIGHT) { }
Location::Location(uint8_t x, uint8_t y, Direction direction) Location::Location(uint8_t x, uint8_t y, Direction direction)
: x(x), y(y), direction(direction), unused(0) { } : x(x),
y(y),
direction(direction),
unused(0) {}
bool Location::operator==(const Location& other) const { bool Location::operator==(const Location& other) const {
return (this->x == other.x) && return (this->x == other.x) &&
@@ -56,7 +55,7 @@ bool Location::operator!=(const Location& other) const {
std::string Location::str() const { std::string Location::str() const {
return string_printf("Location[x=%hhu, y=%hhu, dir=%s, u=%hhu]", return string_printf("Location[x=%hhu, y=%hhu, dir=%s, u=%hhu]",
this->x, this->y, name_for_direction(this->direction), this->unused); this->x, this->y, name_for_direction(this->direction), this->unused);
} }
void Location::clear() { void Location::clear() {
@@ -73,8 +72,6 @@ void Location::clear_FF() {
this->unused = 0xFF; this->unused = 0xFF;
} }
Direction turn_left(Direction d) { Direction turn_left(Direction d) {
switch (d) { switch (d) {
case Direction::RIGHT: case Direction::RIGHT:
@@ -137,65 +134,61 @@ const char* name_for_direction(Direction d) {
} }
} }
bool card_class_is_tech_like(CardClass cc) { bool card_class_is_tech_like(CardClass cc) {
return (cc == CardClass::TECH) || return (cc == CardClass::TECH) ||
(cc == CardClass::PHOTON_BLAST) || (cc == CardClass::PHOTON_BLAST) ||
(cc == CardClass::BOSS_TECH); (cc == CardClass::BOSS_TECH);
} }
static const vector<const char*> name_for_card_type({ static const vector<const char*> name_for_card_type({
"HunterSC", "HunterSC",
"ArkzSC", "ArkzSC",
"Item", "Item",
"Creature", "Creature",
"Action", "Action",
"Assist", "Assist",
}); });
static const unordered_map<string, const char*> description_for_expr_token({ static const unordered_map<string, const char*> description_for_expr_token({
{"f", "Number of FCs controlled by current SC"}, {"f", "Number of FCs controlled by current SC"},
{"d", "Die roll"}, {"d", "Die roll"},
{"ap", "Attacker effective AP"}, {"ap", "Attacker effective AP"},
{"tp", "Attacker effective TP"}, {"tp", "Attacker effective TP"},
{"hp", "Current HP"}, {"hp", "Current HP"},
{"mhp", "Maximum HP"}, {"mhp", "Maximum HP"},
{"dm", "Physical damage"}, {"dm", "Physical damage"},
{"tdm", "Technique damage"}, {"tdm", "Technique damage"},
{"tf", "Number of SC\'s destroyed FCs"}, {"tf", "Number of SC\'s destroyed FCs"},
{"ac", "Remaining ATK points"}, {"ac", "Remaining ATK points"},
{"php", "Maximum HP"}, {"php", "Maximum HP"},
{"dc", "Die roll"}, {"dc", "Die roll"},
{"cs", "Card set cost"}, {"cs", "Card set cost"},
{"a", "Number of FCs on all teams"}, {"a", "Number of FCs on all teams"},
{"kap", "Action cards AP"}, {"kap", "Action cards AP"},
{"ktp", "Action cards TP"}, {"ktp", "Action cards TP"},
{"dn", "Unknown: dn"}, {"dn", "Unknown: dn"},
{"hf", "Number of item or creature cards in hand"}, {"hf", "Number of item or creature cards in hand"},
{"df", "Number of destroyed ally FCs (including SC\'s own)"}, {"df", "Number of destroyed ally FCs (including SC\'s own)"},
{"ff", "Number of ally FCs (including SC\'s own)"}, {"ff", "Number of ally FCs (including SC\'s own)"},
{"ef", "Number of enemy FCs"}, {"ef", "Number of enemy FCs"},
{"bi", "Number of Native FCs on either team"}, {"bi", "Number of Native FCs on either team"},
{"ab", "Number of A.Beast FCs on either team"}, {"ab", "Number of A.Beast FCs on either team"},
{"mc", "Number of Machine FCs on either team"}, {"mc", "Number of Machine FCs on either team"},
{"dk", "Number of Dark FCs on either team"}, {"dk", "Number of Dark FCs on either team"},
{"sa", "Number of Sword-type items on either team"}, {"sa", "Number of Sword-type items on either team"},
{"gn", "Number of Gun-type items on either team"}, {"gn", "Number of Gun-type items on either team"},
{"wd", "Number of Cane-type items on either team"}, {"wd", "Number of Cane-type items on either team"},
{"tt", "Physical damage"}, {"tt", "Physical damage"},
{"lv", "Dice boost"}, {"lv", "Dice boost"},
{"adm", "SC attack damage"}, {"adm", "SC attack damage"},
{"ddm", "Defending damage"}, {"ddm", "Defending damage"},
{"sat", "Number of Sword-type items on SC\'s team"}, {"sat", "Number of Sword-type items on SC\'s team"},
{"edm", "Defending damage"}, // TODO: How is this different from ddm? {"edm", "Defending damage"}, // TODO: How is this different from ddm?
{"ldm", "Unknown: ldm"}, // Unused {"ldm", "Unknown: ldm"}, // Unused
{"rdm", "Defending damage"}, // TODO: How is this different from ddm/edm? {"rdm", "Defending damage"}, // TODO: How is this different from ddm/edm?
{"fdm", "Final damage (after defense)"}, {"fdm", "Final damage (after defense)"},
{"ndm", "Unknown: ndm"}, // Unused {"ndm", "Unknown: ndm"}, // Unused
{"ehp", "Attacker HP"}, {"ehp", "Attacker HP"},
}); });
// Arguments are encoded as 3-character null-terminated strings (why?!), and are // Arguments are encoded as 3-character null-terminated strings (why?!), and are
@@ -224,82 +217,82 @@ static const unordered_map<string, const char*> description_for_expr_token({
// tXX = lasts XX turns, or activate after XX turns // tXX = lasts XX turns, or activate after XX turns
static const vector<const char*> description_for_n_condition({ static const vector<const char*> description_for_n_condition({
/* n00 */ "Always true", /* n00 */ "Always true",
/* n01 */ "Card is Hunters-side SC", /* n01 */ "Card is Hunters-side SC",
/* n02 */ "Destroyed with a single attack", /* n02 */ "Destroyed with a single attack",
/* n03 */ "Technique or PB action card was used", /* n03 */ "Technique or PB action card was used",
/* n04 */ "Attack has Pierce", /* n04 */ "Attack has Pierce",
/* n05 */ "Attack has Rampage", /* n05 */ "Attack has Rampage",
/* n06 */ "Native attribute", /* n06 */ "Native attribute",
/* n07 */ "A.Beast attribute", /* n07 */ "A.Beast attribute",
/* n08 */ "Machine attribute", /* n08 */ "Machine attribute",
/* n09 */ "Dark attribute", /* n09 */ "Dark attribute",
/* n10 */ "Sword-type item", /* n10 */ "Sword-type item",
/* n11 */ "Gun-type item", /* n11 */ "Gun-type item",
/* n12 */ "Cane-type item", /* n12 */ "Cane-type item",
/* n13 */ "Guard item or MAG", /* n13 */ "Guard item or MAG",
/* n14 */ "Story Character", /* n14 */ "Story Character",
/* n15 */ "Attacker does not use action cards", /* n15 */ "Attacker does not use action cards",
/* n16 */ "Aerial attribute", /* n16 */ "Aerial attribute",
/* n17 */ "Same AP as opponent", /* n17 */ "Same AP as opponent",
/* n18 */ "Any target is an SC", /* n18 */ "Any target is an SC",
/* n19 */ "Has Paralyzed condition", /* n19 */ "Has Paralyzed condition",
/* n20 */ "Has Frozen condition", /* n20 */ "Has Frozen condition",
/* n21 */ "???", // TODO: This appears related to Pierce/Rampage /* n21 */ "???", // TODO: This appears related to Pierce/Rampage
/* n22 */ "???", // TODO: This appears related to Pierce/Rampage /* n22 */ "???", // TODO: This appears related to Pierce/Rampage
}); });
static const vector<const char*> description_for_p_target({ static const vector<const char*> description_for_p_target({
/* p00 */ "Unknown: p00", // Unused; probably invalid /* p00 */ "Unknown: p00", // Unused; probably invalid
/* p01 */ "SC / FC who set the card", /* p01 */ "SC / FC who set the card",
/* p02 */ "Attacking SC / FC", /* p02 */ "Attacking SC / FC",
/* p03 */ "Unknown: p03", // Unused /* p03 */ "Unknown: p03", // Unused
/* p04 */ "Unknown: p04", // Unused /* p04 */ "Unknown: p04", // Unused
/* p05 */ "SC / FC who set the card", // Identical to p01 /* p05 */ "SC / FC who set the card", // Identical to p01
/* p06 */ "??? (TODO)", /* p06 */ "??? (TODO)",
/* p07 */ "??? (TODO; Weakness)", /* p07 */ "??? (TODO; Weakness)",
/* p08 */ "FC\'s owner SC", /* p08 */ "FC\'s owner SC",
/* p09 */ "Unknown: p09", // Unused /* p09 */ "Unknown: p09", // Unused
/* p10 */ "All ally FCs", /* p10 */ "All ally FCs",
/* p11 */ "All ally FCs", // TODO: how is this different from p10? /* p11 */ "All ally FCs", // TODO: how is this different from p10?
/* p12 */ "All non-aerial FCs on both teams", /* p12 */ "All non-aerial FCs on both teams",
/* p13 */ "All FCs on both teams that are Frozen", /* p13 */ "All FCs on both teams that are Frozen",
/* p14 */ "All FCs on both teams that have <= 3 HP", /* p14 */ "All FCs on both teams that have <= 3 HP",
/* p15 */ "All FCs except SCs", // TODO: used during attacks only? /* p15 */ "All FCs except SCs", // TODO: used during attacks only?
/* p16 */ "All FCs except SCs", // TODO: used during attacks only? how is this different from p15? /* p16 */ "All FCs except SCs", // TODO: used during attacks only? how is this different from p15?
/* p17 */ "This card", /* p17 */ "This card",
/* p18 */ "SC who equipped this card", /* p18 */ "SC who equipped this card",
/* p19 */ "Unknown: p19", // Unused /* p19 */ "Unknown: p19", // Unused
/* p20 */ "Unknown: p20", // Unused /* p20 */ "Unknown: p20", // Unused
/* p21 */ "Unknown: p21", // Unused /* p21 */ "Unknown: p21", // Unused
/* p22 */ "All characters (SCs & FCs) including this card", // TODO: But why does Shifta apply only to allies then? /* p22 */ "All characters (SCs & FCs) including this card", // TODO: But why does Shifta apply only to allies then?
/* p23 */ "All characters (SCs & FCs) except this card", /* p23 */ "All characters (SCs & FCs) except this card",
/* p24 */ "All FCs on both teams that have Paralysis", /* p24 */ "All FCs on both teams that have Paralysis",
/* p25 */ "Unknown: p25", // Unused /* p25 */ "Unknown: p25", // Unused
/* p26 */ "Unknown: p26", // Unused /* p26 */ "Unknown: p26", // Unused
/* p27 */ "Unknown: p27", // Unused /* p27 */ "Unknown: p27", // Unused
/* p28 */ "Unknown: p28", // Unused /* p28 */ "Unknown: p28", // Unused
/* p29 */ "Unknown: p29", // Unused /* p29 */ "Unknown: p29", // Unused
/* p30 */ "Unknown: p30", // Unused /* p30 */ "Unknown: p30", // Unused
/* p31 */ "Unknown: p31", // Unused /* p31 */ "Unknown: p31", // Unused
/* p32 */ "Unknown: p32", // Unused /* p32 */ "Unknown: p32", // Unused
/* p33 */ "Unknown: p33", // Unused /* p33 */ "Unknown: p33", // Unused
/* p34 */ "Unknown: p34", // Unused /* p34 */ "Unknown: p34", // Unused
/* p35 */ "All characters (SCs & FCs) within range", // Used for Explosion effect /* p35 */ "All characters (SCs & FCs) within range", // Used for Explosion effect
/* p36 */ "All ally SCs within range, but not the caster", // Resta /* p36 */ "All ally SCs within range, but not the caster", // Resta
/* p37 */ "All FCs or all opponent FCs (TODO)", // TODO: when to use which selector? is a3 involved here somehow? /* p37 */ "All FCs or all opponent FCs (TODO)", // TODO: when to use which selector? is a3 involved here somehow?
/* p38 */ "All allies except items within range (and not this card)", /* p38 */ "All allies except items within range (and not this card)",
/* p39 */ "All FCs that cost 4 or more points", /* p39 */ "All FCs that cost 4 or more points",
/* p40 */ "All FCs that cost 3 or fewer points", /* p40 */ "All FCs that cost 3 or fewer points",
/* p41 */ "Unknown: p41", // Unused /* p41 */ "Unknown: p41", // Unused
/* p42 */ "Attacker during defense phase", // Only used by TP Defense /* p42 */ "Attacker during defense phase", // Only used by TP Defense
/* p43 */ "Owner SC of defending FC during attack", /* p43 */ "Owner SC of defending FC during attack",
/* p44 */ "SC\'s own creature FCs within range", /* p44 */ "SC\'s own creature FCs within range",
/* p45 */ "Both attacker and defender", // Used for Snatch, which moves EXP from one to the other /* p45 */ "Both attacker and defender", // Used for Snatch, which moves EXP from one to the other
/* p46 */ "All SCs & FCs one space left or right of this card", /* p46 */ "All SCs & FCs one space left or right of this card",
/* p47 */ "FC\'s owner Boss SC", // Only used for Gibbles+ which explicitly mentions Boss SC, so it looks like this is p08 but for bosses /* p47 */ "FC\'s owner Boss SC", // Only used for Gibbles+ which explicitly mentions Boss SC, so it looks like this is p08 but for bosses
/* p48 */ "Everything within range, including this card\'s user", // Madness /* p48 */ "Everything within range, including this card\'s user", // Madness
/* p49 */ "All ally FCs within range except this card", /* p49 */ "All ally FCs within range except this card",
}); });
struct ConditionDescription { struct ConditionDescription {
@@ -309,132 +302,132 @@ struct ConditionDescription {
}; };
static const vector<ConditionDescription> description_for_condition_type({ static const vector<ConditionDescription> description_for_condition_type({
/* 0x00 */ {false, "NONE", nullptr}, /* 0x00 */ {false, "NONE", nullptr},
/* 0x01 */ {true, "AP_BOOST", "Temporarily increase AP by N"}, /* 0x01 */ {true, "AP_BOOST", "Temporarily increase AP by N"},
/* 0x02 */ {false, "RAMPAGE", "Rampage"}, /* 0x02 */ {false, "RAMPAGE", "Rampage"},
/* 0x03 */ {true, "MULTI_STRIKE", "Duplicate attack N times"}, /* 0x03 */ {true, "MULTI_STRIKE", "Duplicate attack N times"},
/* 0x04 */ {true, "DAMAGE_MOD_1", "Set attack damage / AP to N after action cards applied (step 1)"}, /* 0x04 */ {true, "DAMAGE_MOD_1", "Set attack damage / AP to N after action cards applied (step 1)"},
/* 0x05 */ {false, "IMMOBILE", "Give Immobile condition"}, /* 0x05 */ {false, "IMMOBILE", "Give Immobile condition"},
/* 0x06 */ {false, "HOLD", "Give Hold condition"}, /* 0x06 */ {false, "HOLD", "Give Hold condition"},
/* 0x07 */ {false, "UNKNOWN_07", nullptr}, /* 0x07 */ {false, "UNKNOWN_07", nullptr},
/* 0x08 */ {true, "TP_BOOST", "Add N TP temporarily during attack"}, /* 0x08 */ {true, "TP_BOOST", "Add N TP temporarily during attack"},
/* 0x09 */ {true, "GIVE_DAMAGE", "Cause direct N HP loss"}, /* 0x09 */ {true, "GIVE_DAMAGE", "Cause direct N HP loss"},
/* 0x0A */ {false, "GUOM", "Give Guom condition"}, /* 0x0A */ {false, "GUOM", "Give Guom condition"},
/* 0x0B */ {false, "PARALYZE", "Give Paralysis condition"}, /* 0x0B */ {false, "PARALYZE", "Give Paralysis condition"},
/* 0x0C */ {false, "UNKNOWN_0C", nullptr}, /* 0x0C */ {false, "UNKNOWN_0C", nullptr},
/* 0x0D */ {false, "A_H_SWAP", "Swap AP and HP temporarily"}, /* 0x0D */ {false, "A_H_SWAP", "Swap AP and HP temporarily"},
/* 0x0E */ {false, "PIERCE", "Attack SC directly even if they have items equipped"}, /* 0x0E */ {false, "PIERCE", "Attack SC directly even if they have items equipped"},
/* 0x0F */ {false, "UNKNOWN_0F", nullptr}, /* 0x0F */ {false, "UNKNOWN_0F", nullptr},
/* 0x10 */ {true, "HEAL", "Increase HP by N"}, /* 0x10 */ {true, "HEAL", "Increase HP by N"},
/* 0x11 */ {false, "RETURN_TO_HAND", "Return card to hand"}, /* 0x11 */ {false, "RETURN_TO_HAND", "Return card to hand"},
/* 0x12 */ {false, "UNKNOWN_12", nullptr}, /* 0x12 */ {false, "UNKNOWN_12", nullptr},
/* 0x13 */ {false, "UNKNOWN_13", nullptr}, /* 0x13 */ {false, "UNKNOWN_13", nullptr},
/* 0x14 */ {false, "ACID", "Give Acid condition"}, /* 0x14 */ {false, "ACID", "Give Acid condition"},
/* 0x15 */ {false, "UNKNOWN_15", nullptr}, /* 0x15 */ {false, "UNKNOWN_15", nullptr},
/* 0x16 */ {true, "MIGHTY_KNUCKLE", "Temporarily increase AP by N, and set ATK dice to zero"}, /* 0x16 */ {true, "MIGHTY_KNUCKLE", "Temporarily increase AP by N, and set ATK dice to zero"},
/* 0x17 */ {true, "UNIT_BLOW", "Temporarily increase AP by N * number of this card set within phase"}, /* 0x17 */ {true, "UNIT_BLOW", "Temporarily increase AP by N * number of this card set within phase"},
/* 0x18 */ {false, "CURSE", "Give Curse condition"}, /* 0x18 */ {false, "CURSE", "Give Curse condition"},
/* 0x19 */ {false, "COMBO_AP", "Temporarily increase AP by number of this card set within phase"}, /* 0x19 */ {false, "COMBO_AP", "Temporarily increase AP by number of this card set within phase"},
/* 0x1A */ {false, "PIERCE_RAMPAGE_BLOCK", "Block attack if Pierce/Rampage (?)"}, /* 0x1A */ {false, "PIERCE_RAMPAGE_BLOCK", "Block attack if Pierce/Rampage (?)"},
/* 0x1B */ {false, "ABILITY_TRAP", "Temporarily disable opponent abilities"}, /* 0x1B */ {false, "ABILITY_TRAP", "Temporarily disable opponent abilities"},
/* 0x1C */ {false, "FREEZE", "Give Freeze condition"}, /* 0x1C */ {false, "FREEZE", "Give Freeze condition"},
/* 0x1D */ {false, "ANTI_ABNORMALITY_1", "Cure all conditions"}, /* 0x1D */ {false, "ANTI_ABNORMALITY_1", "Cure all conditions"},
/* 0x1E */ {false, "UNKNOWN_1E", nullptr}, /* 0x1E */ {false, "UNKNOWN_1E", nullptr},
/* 0x1F */ {false, "EXPLOSION", "Damage all SCs and FCs by number of this same card set * 2"}, /* 0x1F */ {false, "EXPLOSION", "Damage all SCs and FCs by number of this same card set * 2"},
/* 0x20 */ {false, "UNKNOWN_20", nullptr}, /* 0x20 */ {false, "UNKNOWN_20", nullptr},
/* 0x21 */ {false, "UNKNOWN_21", nullptr}, /* 0x21 */ {false, "UNKNOWN_21", nullptr},
/* 0x22 */ {false, "UNKNOWN_22", nullptr}, /* 0x22 */ {false, "UNKNOWN_22", nullptr},
/* 0x23 */ {false, "RETURN_TO_DECK", "Cancel discard and move to bottom of deck instead"}, /* 0x23 */ {false, "RETURN_TO_DECK", "Cancel discard and move to bottom of deck instead"},
/* 0x24 */ {false, "AERIAL", "Give Aerial status"}, /* 0x24 */ {false, "AERIAL", "Give Aerial status"},
/* 0x25 */ {true, "AP_LOSS", "Make attacker temporarily lose N AP during defense"}, /* 0x25 */ {true, "AP_LOSS", "Make attacker temporarily lose N AP during defense"},
/* 0x26 */ {true, "BONUS_FROM_LEADER", "Gain AP equal to the number of cards of type N on the field"}, /* 0x26 */ {true, "BONUS_FROM_LEADER", "Gain AP equal to the number of cards of type N on the field"},
/* 0x27 */ {false, "FREE_MANEUVER", "Enable movement over occupied tiles"}, /* 0x27 */ {false, "FREE_MANEUVER", "Enable movement over occupied tiles"},
/* 0x28 */ {false, "HASTE", "Make move actions free"}, /* 0x28 */ {false, "HASTE", "Make move actions free"},
/* 0x29 */ {true, "CLONE", "Make setting this card free if at least one card of type N is already on the field"}, /* 0x29 */ {true, "CLONE", "Make setting this card free if at least one card of type N is already on the field"},
/* 0x2A */ {true, "DEF_DISABLE_BY_COST", "Disable use of any defense cards costing between (N / 10) and (N % 10) points, inclusive"}, /* 0x2A */ {true, "DEF_DISABLE_BY_COST", "Disable use of any defense cards costing between (N / 10) and (N % 10) points, inclusive"},
/* 0x2B */ {true, "FILIAL", "Increase controlling SC\'s HP by N when this card is destroyed"}, /* 0x2B */ {true, "FILIAL", "Increase controlling SC\'s HP by N when this card is destroyed"},
/* 0x2C */ {true, "SNATCH", "Steal N EXP during attack"}, /* 0x2C */ {true, "SNATCH", "Steal N EXP during attack"},
/* 0x2D */ {true, "HAND_DISRUPTER", "Discard N cards from hand immediately"}, /* 0x2D */ {true, "HAND_DISRUPTER", "Discard N cards from hand immediately"},
/* 0x2E */ {false, "DROP", "Give Drop condition"}, /* 0x2E */ {false, "DROP", "Give Drop condition"},
/* 0x2F */ {false, "ACTION_DISRUPTER", "Destroy all action cards used by attacker"}, /* 0x2F */ {false, "ACTION_DISRUPTER", "Destroy all action cards used by attacker"},
/* 0x30 */ {true, "SET_HP", "Set HP to N"}, /* 0x30 */ {true, "SET_HP", "Set HP to N"},
/* 0x31 */ {false, "NATIVE_SHIELD", "Block attacks from Native creatures"}, /* 0x31 */ {false, "NATIVE_SHIELD", "Block attacks from Native creatures"},
/* 0x32 */ {false, "A_BEAST_SHIELD", "Block attacks from A.Beast creatures"}, /* 0x32 */ {false, "A_BEAST_SHIELD", "Block attacks from A.Beast creatures"},
/* 0x33 */ {false, "MACHINE_SHIELD", "Block attacks from Machine creatures"}, /* 0x33 */ {false, "MACHINE_SHIELD", "Block attacks from Machine creatures"},
/* 0x34 */ {false, "DARK_SHIELD", "Block attacks from Dark creatures"}, /* 0x34 */ {false, "DARK_SHIELD", "Block attacks from Dark creatures"},
/* 0x35 */ {false, "SWORD_SHIELD", "Block attacks from Sword items"}, /* 0x35 */ {false, "SWORD_SHIELD", "Block attacks from Sword items"},
/* 0x36 */ {false, "GUN_SHIELD", "Block attacks from Gun items"}, /* 0x36 */ {false, "GUN_SHIELD", "Block attacks from Gun items"},
/* 0x37 */ {false, "CANE_SHIELD", "Block attacks from Cane items"}, /* 0x37 */ {false, "CANE_SHIELD", "Block attacks from Cane items"},
/* 0x38 */ {false, "UNKNOWN_38", nullptr}, /* 0x38 */ {false, "UNKNOWN_38", nullptr},
/* 0x39 */ {false, "UNKNOWN_39", nullptr}, /* 0x39 */ {false, "UNKNOWN_39", nullptr},
/* 0x3A */ {false, "DEFENDER", "Make attacks go to setter of this card instead of original target"}, /* 0x3A */ {false, "DEFENDER", "Make attacks go to setter of this card instead of original target"},
/* 0x3B */ {false, "SURVIVAL_DECOYS", "Redirect damage for multi-sided attack"}, /* 0x3B */ {false, "SURVIVAL_DECOYS", "Redirect damage for multi-sided attack"},
/* 0x3C */ {true, "GIVE_OR_TAKE_EXP", "Give N EXP, or take if N is negative"}, /* 0x3C */ {true, "GIVE_OR_TAKE_EXP", "Give N EXP, or take if N is negative"},
/* 0x3D */ {false, "UNKNOWN_3D", nullptr}, /* 0x3D */ {false, "UNKNOWN_3D", nullptr},
/* 0x3E */ {false, "DEATH_COMPANION", "If this card has 1 or 2 HP, set its HP to N"}, /* 0x3E */ {false, "DEATH_COMPANION", "If this card has 1 or 2 HP, set its HP to N"},
/* 0x3F */ {true, "EXP_DECOY", "If defender has EXP, lose EXP instead of getting damage when attacked"}, /* 0x3F */ {true, "EXP_DECOY", "If defender has EXP, lose EXP instead of getting damage when attacked"},
/* 0x40 */ {true, "SET_MV", "Set MV to N"}, /* 0x40 */ {true, "SET_MV", "Set MV to N"},
/* 0x41 */ {true, "GROUP", "Temporarily increase AP by N * number of this card on field, excluding itself"}, /* 0x41 */ {true, "GROUP", "Temporarily increase AP by N * number of this card on field, excluding itself"},
/* 0x42 */ {false, "BERSERK", "User of this card receives the same damage as target, and isn\'t helped by target\'s defense cards"}, /* 0x42 */ {false, "BERSERK", "User of this card receives the same damage as target, and isn\'t helped by target\'s defense cards"},
/* 0x43 */ {false, "GUARD_CREATURE", "Attacks on controlling SC damage this card instead"}, /* 0x43 */ {false, "GUARD_CREATURE", "Attacks on controlling SC damage this card instead"},
/* 0x44 */ {false, "TECH", "Technique cards cost 1 fewer ATK point"}, /* 0x44 */ {false, "TECH", "Technique cards cost 1 fewer ATK point"},
/* 0x45 */ {false, "BIG_SWING", "Increase all attacking ATK costs by 1"}, /* 0x45 */ {false, "BIG_SWING", "Increase all attacking ATK costs by 1"},
/* 0x46 */ {false, "UNKNOWN_46", nullptr}, /* 0x46 */ {false, "UNKNOWN_46", nullptr},
/* 0x47 */ {false, "SHIELD_WEAPON", "Limit attacker\'s choice of target to guard items"}, /* 0x47 */ {false, "SHIELD_WEAPON", "Limit attacker\'s choice of target to guard items"},
/* 0x48 */ {false, "ATK_DICE_BOOST", "Increase ATK dice roll by 1"}, /* 0x48 */ {false, "ATK_DICE_BOOST", "Increase ATK dice roll by 1"},
/* 0x49 */ {false, "UNKNOWN_49", nullptr}, /* 0x49 */ {false, "UNKNOWN_49", nullptr},
/* 0x4A */ {false, "MAJOR_PIERCE", "If SC has over half of max HP, attacks target SC instead of equipped items"}, /* 0x4A */ {false, "MAJOR_PIERCE", "If SC has over half of max HP, attacks target SC instead of equipped items"},
/* 0x4B */ {false, "HEAVY_PIERCE", "If SC has 3 or more items equipped, attacks target SC instead of equipped items"}, /* 0x4B */ {false, "HEAVY_PIERCE", "If SC has 3 or more items equipped, attacks target SC instead of equipped items"},
/* 0x4C */ {false, "MAJOR_RAMPAGE", "If SC has over half of max HP, attacks target SC and all equipped items"}, /* 0x4C */ {false, "MAJOR_RAMPAGE", "If SC has over half of max HP, attacks target SC and all equipped items"},
/* 0x4D */ {false, "HEAVY_RAMPAGE", "If SC has 3 or more items equipped, attacks target SC and all equipped items"}, /* 0x4D */ {false, "HEAVY_RAMPAGE", "If SC has 3 or more items equipped, attacks target SC and all equipped items"},
/* 0x4E */ {true, "AP_GROWTH", "Permanently increase AP by N"}, /* 0x4E */ {true, "AP_GROWTH", "Permanently increase AP by N"},
/* 0x4F */ {true, "TP_GROWTH", "Permanently increase TP by N"}, /* 0x4F */ {true, "TP_GROWTH", "Permanently increase TP by N"},
/* 0x50 */ {true, "REBORN", "If any card of type N is on the field, this card goes to the hand when destroyed instead of being discarded"}, /* 0x50 */ {true, "REBORN", "If any card of type N is on the field, this card goes to the hand when destroyed instead of being discarded"},
/* 0x51 */ {true, "COPY", "Temporarily set AP/TP to N percent (or 100% if N is 0) of opponent\'s values"}, /* 0x51 */ {true, "COPY", "Temporarily set AP/TP to N percent (or 100% if N is 0) of opponent\'s values"},
/* 0x52 */ {false, "UNKNOWN_52", nullptr}, /* 0x52 */ {false, "UNKNOWN_52", nullptr},
/* 0x53 */ {true, "MISC_GUARDS", "Add N to card\'s defense value"}, /* 0x53 */ {true, "MISC_GUARDS", "Add N to card\'s defense value"},
/* 0x54 */ {true, "AP_OVERRIDE", "Set AP to N temporarily"}, /* 0x54 */ {true, "AP_OVERRIDE", "Set AP to N temporarily"},
/* 0x55 */ {true, "TP_OVERRIDE", "Set TP to N temporarily"}, /* 0x55 */ {true, "TP_OVERRIDE", "Set TP to N temporarily"},
/* 0x56 */ {false, "RETURN", "Return card to hand on destruction instead of discarding"}, /* 0x56 */ {false, "RETURN", "Return card to hand on destruction instead of discarding"},
/* 0x57 */ {false, "A_T_SWAP_PERM", "Permanently swap AP and TP"}, /* 0x57 */ {false, "A_T_SWAP_PERM", "Permanently swap AP and TP"},
/* 0x58 */ {false, "A_H_SWAP_PERM", "Permanently swap AP and HP"}, /* 0x58 */ {false, "A_H_SWAP_PERM", "Permanently swap AP and HP"},
/* 0x59 */ {true, "SLAYERS_ASSASSINS", "Temporarily increase AP during attack"}, /* 0x59 */ {true, "SLAYERS_ASSASSINS", "Temporarily increase AP during attack"},
/* 0x5A */ {false, "ANTI_ABNORMALITY_2", "Remove all conditions"}, /* 0x5A */ {false, "ANTI_ABNORMALITY_2", "Remove all conditions"},
/* 0x5B */ {false, "FIXED_RANGE", "Use SC\'s range instead of weapon or attack card ranges"}, /* 0x5B */ {false, "FIXED_RANGE", "Use SC\'s range instead of weapon or attack card ranges"},
/* 0x5C */ {false, "ELUDE", "SC does not lose HP when equipped items are destroyed"}, /* 0x5C */ {false, "ELUDE", "SC does not lose HP when equipped items are destroyed"},
/* 0x5D */ {false, "PARRY", "Forward attack to a random FC within one tile of original target, excluding attacker and original target"}, /* 0x5D */ {false, "PARRY", "Forward attack to a random FC within one tile of original target, excluding attacker and original target"},
/* 0x5E */ {false, "BLOCK_ATTACK", "Completely block attack"}, /* 0x5E */ {false, "BLOCK_ATTACK", "Completely block attack"},
/* 0x5F */ {false, "UNKNOWN_5F", nullptr}, /* 0x5F */ {false, "UNKNOWN_5F", nullptr},
/* 0x60 */ {false, "UNKNOWN_60", nullptr}, /* 0x60 */ {false, "UNKNOWN_60", nullptr},
/* 0x61 */ {true, "COMBO_TP", "Gain TP equal to the number of cards of type N on the field"}, /* 0x61 */ {true, "COMBO_TP", "Gain TP equal to the number of cards of type N on the field"},
/* 0x62 */ {true, "MISC_AP_BONUSES", "Temporarily increase AP by N"}, /* 0x62 */ {true, "MISC_AP_BONUSES", "Temporarily increase AP by N"},
/* 0x63 */ {true, "MISC_TP_BONUSES", "Temporarily increase TP by N"}, /* 0x63 */ {true, "MISC_TP_BONUSES", "Temporarily increase TP by N"},
/* 0x64 */ {false, "UNKNOWN_64", nullptr}, /* 0x64 */ {false, "UNKNOWN_64", nullptr},
/* 0x65 */ {true, "MISC_DEFENSE_BONUSES", "Decrease damage by N"}, /* 0x65 */ {true, "MISC_DEFENSE_BONUSES", "Decrease damage by N"},
/* 0x66 */ {true, "MOSTLY_HALFGUARDS", "Reduce damage from incoming attack by N"}, /* 0x66 */ {true, "MOSTLY_HALFGUARDS", "Reduce damage from incoming attack by N"},
/* 0x67 */ {false, "PERIODIC_FIELD", "Swap immunity to tech or physical attacks"}, /* 0x67 */ {false, "PERIODIC_FIELD", "Swap immunity to tech or physical attacks"},
/* 0x68 */ {false, "FC_LIMIT_BY_COUNT", "Change FC limit from 8 ATK points total to 4 FCs total"}, /* 0x68 */ {false, "FC_LIMIT_BY_COUNT", "Change FC limit from 8 ATK points total to 4 FCs total"},
/* 0x69 */ {false, "UNKNOWN_69", nullptr}, /* 0x69 */ {false, "UNKNOWN_69", nullptr},
/* 0x6A */ {true, "MV_BONUS", "Increase MV by N"}, /* 0x6A */ {true, "MV_BONUS", "Increase MV by N"},
/* 0x6B */ {true, "FORWARD_DAMAGE", "Give N damage back to attacker during defense (?) (TODO)"}, /* 0x6B */ {true, "FORWARD_DAMAGE", "Give N damage back to attacker during defense (?) (TODO)"},
/* 0x6C */ {true, "WEAK_SPOT_INFLUENCE", "Temporarily decrease AP by N"}, /* 0x6C */ {true, "WEAK_SPOT_INFLUENCE", "Temporarily decrease AP by N"},
/* 0x6D */ {true, "DAMAGE_MODIFIER_2", "Set attack damage / AP after action cards applied (step 2)"}, /* 0x6D */ {true, "DAMAGE_MODIFIER_2", "Set attack damage / AP after action cards applied (step 2)"},
/* 0x6E */ {true, "WEAK_HIT_BLOCK", "Block all attacks of N damage or less"}, /* 0x6E */ {true, "WEAK_HIT_BLOCK", "Block all attacks of N damage or less"},
/* 0x6F */ {true, "AP_SILENCE", "Temporarily decrease AP of opponent by N"}, /* 0x6F */ {true, "AP_SILENCE", "Temporarily decrease AP of opponent by N"},
/* 0x70 */ {true, "TP_SILENCE", "Temporarily decrease TP of opponent by N"}, /* 0x70 */ {true, "TP_SILENCE", "Temporarily decrease TP of opponent by N"},
/* 0x71 */ {false, "A_T_SWAP", "Temporarily swap AP and TP"}, /* 0x71 */ {false, "A_T_SWAP", "Temporarily swap AP and TP"},
/* 0x72 */ {true, "HALFGUARD", "Halve damage from attacks that would inflict N or more damage"}, /* 0x72 */ {true, "HALFGUARD", "Halve damage from attacks that would inflict N or more damage"},
/* 0x73 */ {false, "UNKNOWN_73", nullptr}, /* 0x73 */ {false, "UNKNOWN_73", nullptr},
/* 0x74 */ {true, "RAMPAGE_AP_LOSS", "Temporarily reduce AP by N"}, /* 0x74 */ {true, "RAMPAGE_AP_LOSS", "Temporarily reduce AP by N"},
/* 0x75 */ {false, "UNKNOWN_75", nullptr}, /* 0x75 */ {false, "UNKNOWN_75", nullptr},
/* 0x76 */ {false, "REFLECT", "Generate reverse attack"}, /* 0x76 */ {false, "REFLECT", "Generate reverse attack"},
/* 0x77 */ {false, "UNKNOWN_77", nullptr}, /* 0x77 */ {false, "UNKNOWN_77", nullptr},
/* 0x78 */ {false, "ANY", nullptr}, // Treated as "any condition" in find functions /* 0x78 */ {false, "ANY", nullptr}, // Treated as "any condition" in find functions
/* 0x79 */ {false, "UNKNOWN_79", nullptr}, /* 0x79 */ {false, "UNKNOWN_79", nullptr},
/* 0x7A */ {false, "UNKNOWN_7A", nullptr}, /* 0x7A */ {false, "UNKNOWN_7A", nullptr},
/* 0x7B */ {false, "UNKNOWN_7B", nullptr}, /* 0x7B */ {false, "UNKNOWN_7B", nullptr},
/* 0x7C */ {false, "UNKNOWN_7C", nullptr}, /* 0x7C */ {false, "UNKNOWN_7C", nullptr},
/* 0x7D */ {false, "UNKNOWN_7D", nullptr}, /* 0x7D */ {false, "UNKNOWN_7D", nullptr},
}); });
const char* name_for_condition_type(ConditionType cond_type) { const char* name_for_condition_type(ConditionType cond_type) {
@@ -445,8 +438,6 @@ const char* name_for_condition_type(ConditionType cond_type) {
} }
} }
const char* name_for_action_subphase(ActionSubphase subphase) { const char* name_for_action_subphase(ActionSubphase subphase) {
switch (subphase) { switch (subphase) {
case ActionSubphase::ATTACK: case ActionSubphase::ATTACK:
@@ -460,8 +451,6 @@ const char* name_for_action_subphase(ActionSubphase subphase) {
} }
} }
void CardDefinition::Stat::decode_code() { void CardDefinition::Stat::decode_code() {
this->type = static_cast<Type>(this->code / 1000); this->type = static_cast<Type>(this->code / 1000);
int16_t value = this->code - (this->type * 1000); int16_t value = this->code - (this->type * 1000);
@@ -512,8 +501,6 @@ string CardDefinition::Stat::str() const {
} }
} }
bool CardDefinition::Effect::is_empty() const { bool CardDefinition::Effect::is_empty() const {
return (this->effect_num == 0 && return (this->effect_num == 0 &&
this->type == ConditionType::NONE && this->type == ConditionType::NONE &&
@@ -588,7 +575,8 @@ string CardDefinition::Effect::str() const {
cmd_str += ':'; cmd_str += ':';
cmd_str += name; cmd_str += name;
} }
} catch (const out_of_range&) { } } catch (const out_of_range&) {
}
string expr_str = this->expr; string expr_str = this->expr;
if (!expr_str.empty()) { if (!expr_str.empty()) {
@@ -599,12 +587,10 @@ string CardDefinition::Effect::str() const {
string arg2str = this->str_for_arg(this->arg2); string arg2str = this->str_for_arg(this->arg2);
string arg3str = this->str_for_arg(this->arg3); string arg3str = this->str_for_arg(this->arg3);
return string_printf("(cmd=%s%s, when=%02hhX, arg1=%s, arg2=%s, arg3=%s, cond=%02hhX, a2=%02hhX)", return string_printf("(cmd=%s%s, when=%02hhX, arg1=%s, arg2=%s, arg3=%s, cond=%02hhX, a2=%02hhX)",
cmd_str.c_str(), expr_str.c_str(), this->when, arg1str.data(), cmd_str.c_str(), expr_str.c_str(), this->when, arg1str.data(),
arg2str.data(), arg3str.data(), static_cast<uint8_t>(this->apply_criterion), this->unknown_a2); arg2str.data(), arg3str.data(), static_cast<uint8_t>(this->apply_criterion), this->unknown_a2);
} }
bool CardDefinition::is_sc() const { bool CardDefinition::is_sc() const {
return (this->type == CardType::HUNTERS_SC) || (this->type == CardType::ARKZ_SC); return (this->type == CardType::HUNTERS_SC) || (this->type == CardType::ARKZ_SC);
} }
@@ -614,8 +600,7 @@ bool CardDefinition::is_fc() const {
} }
bool CardDefinition::is_named_android_sc() const { bool CardDefinition::is_named_android_sc() const {
static const unordered_set<uint16_t> TARGET_IDS({ static const unordered_set<uint16_t> TARGET_IDS({0x0005, 0x0007, 0x0110, 0x0113, 0x0114, 0x0117, 0x011B, 0x011F});
0x0005, 0x0007, 0x0110, 0x0113, 0x0114, 0x0117, 0x011B, 0x011F});
return TARGET_IDS.count(this->card_id); return TARGET_IDS.count(this->card_id);
} }
@@ -636,8 +621,6 @@ CardClass CardDefinition::card_class() const {
return static_cast<CardClass>(this->be_card_class.load()); return static_cast<CardClass>(this->be_card_class.load());
} }
void CardDefinition::decode_range() { void CardDefinition::decode_range() {
// If the cell representing the FC is nonzero, the card has a range from a // If the cell representing the FC is nonzero, the card has a range from a
// list of constants. Otherwise, its range is already defined in the range // list of constants. Otherwise, its range is already defined in the range
@@ -695,20 +678,20 @@ void CardDefinition::decode_range() {
string name_for_rarity(CardRarity rarity) { string name_for_rarity(CardRarity rarity) {
static const vector<const char*> names({ static const vector<const char*> names({
"N1", "N1",
"R1", "R1",
"S", "S",
"E", "E",
"N2", "N2",
"N3", "N3",
"N4", "N4",
"R2", "R2",
"R3", "R3",
"R4", "R4",
"SS", "SS",
"D1", "D1",
"D2", "D2",
"INVIS", "INVIS",
}); });
try { try {
return names.at(static_cast<uint8_t>(rarity) - 1); return names.at(static_cast<uint8_t>(rarity) - 1);
@@ -719,16 +702,16 @@ string name_for_rarity(CardRarity rarity) {
string name_for_target_mode(TargetMode target_mode) { string name_for_target_mode(TargetMode target_mode) {
static const vector<const char*> names({ static const vector<const char*> names({
"NONE", "NONE",
"SINGLE", "SINGLE",
"MULTI", "MULTI",
"SELF", "SELF",
"TEAM", "TEAM",
"ALL", "ALL",
"MULTI-ALLY", "MULTI-ALLY",
"ALL-ALLY", "ALL-ALLY",
"ALL-ATTACK", "ALL-ATTACK",
"OWN-FCS", "OWN-FCS",
}); });
try { try {
return names.at(static_cast<uint8_t>(target_mode)); return names.at(static_cast<uint8_t>(target_mode));
@@ -837,8 +820,6 @@ string CardDefinition::str() const {
effects_str.c_str()); effects_str.c_str());
} }
Rules::Rules() { Rules::Rules() {
this->clear(); this->clear();
} }
@@ -920,7 +901,7 @@ string Rules::str() const {
break; break;
default: default:
tokens.emplace_back(string_printf("hp_type=(%02hhX)", tokens.emplace_back(string_printf("hp_type=(%02hhX)",
static_cast<uint8_t>(this->hp_type))); static_cast<uint8_t>(this->hp_type)));
break; break;
} }
@@ -938,7 +919,7 @@ string Rules::str() const {
break; break;
default: default:
tokens.emplace_back(string_printf("dice_exchange=(%02hhX)", tokens.emplace_back(string_printf("dice_exchange=(%02hhX)",
static_cast<uint8_t>(this->dice_exchange_mode))); static_cast<uint8_t>(this->dice_exchange_mode)));
break; break;
} }
tokens.emplace_back(string_printf("dice_boost=%s", this->disable_dice_boost ? "DISABLED" : "ENABLED")); tokens.emplace_back(string_printf("dice_boost=%s", this->disable_dice_boost ? "DISABLED" : "ENABLED"));
@@ -961,7 +942,7 @@ string Rules::str() const {
break; break;
default: default:
tokens.emplace_back(string_printf("allowed_cards=(%02hhX)", tokens.emplace_back(string_printf("allowed_cards=(%02hhX)",
static_cast<uint8_t>(this->allowed_cards))); static_cast<uint8_t>(this->allowed_cards)));
break; break;
} }
tokens.emplace_back(string_printf("assist_cards=%s", this->no_assist_cards ? "DISALLOWED" : "ALLOWED")); tokens.emplace_back(string_printf("assist_cards=%s", this->no_assist_cards ? "DISALLOWED" : "ALLOWED"));
@@ -974,8 +955,6 @@ string Rules::str() const {
return "Rules[" + join(tokens, ", ") + "]"; return "Rules[" + join(tokens, ", ") + "]";
} }
StateFlags::StateFlags() { StateFlags::StateFlags() {
this->clear(); this->clear();
} }
@@ -1028,8 +1007,6 @@ void StateFlags::clear_FF() {
this->client_sc_card_types.clear(CardType::INVALID_FF); this->client_sc_card_types.clear(CardType::INVALID_FF);
} }
string MapDefinition::str(const DataIndex* data_index) const { string MapDefinition::str(const DataIndex* data_index) const {
deque<string> lines; deque<string> lines;
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) { auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
@@ -1043,13 +1020,14 @@ string MapDefinition::str(const DataIndex* data_index) const {
}; };
lines.emplace_back(string_printf("Map %08" PRIX32 ": %hhux%hhu", lines.emplace_back(string_printf("Map %08" PRIX32 ": %hhux%hhu",
this->map_number.load(), this->width, this->height)); this->map_number.load(), this->width, this->height));
lines.emplace_back(string_printf(" a1=%08" PRIX32, this->unknown_a1.load())); lines.emplace_back(string_printf(" a1=%08" PRIX32, this->unknown_a1.load()));
lines.emplace_back(string_printf(" environment_number=%02hhX", this->environment_number)); lines.emplace_back(string_printf(" environment_number=%02hhX", this->environment_number));
lines.emplace_back(string_printf(" num_alt_maps=%02hhX", this->num_alt_maps)); lines.emplace_back(string_printf(" num_alt_maps=%02hhX", this->num_alt_maps));
lines.emplace_back(" tiles:"); lines.emplace_back(" tiles:");
add_map(this->map_tiles); add_map(this->map_tiles);
lines.emplace_back(string_printf(" start_tile_definitions=[1p=%02hhX 2p=%02hhX,%02hhX 3p=%02hhX,%02hhX,%02hhX], [1p=%02hhX 2p=%02hhX,%02hhX 3p=%02hhX,%02hhX,%02hhX]", lines.emplace_back(string_printf(
" start_tile_definitions=[1p=%02hhX 2p=%02hhX,%02hhX 3p=%02hhX,%02hhX,%02hhX], [1p=%02hhX 2p=%02hhX,%02hhX 3p=%02hhX,%02hhX,%02hhX]",
this->start_tile_definitions[0][0], this->start_tile_definitions[0][1], this->start_tile_definitions[0][0], this->start_tile_definitions[0][1],
this->start_tile_definitions[0][2], this->start_tile_definitions[0][3], this->start_tile_definitions[0][2], this->start_tile_definitions[0][3],
this->start_tile_definitions[0][4], this->start_tile_definitions[0][5], this->start_tile_definitions[0][4], this->start_tile_definitions[0][5],
@@ -1062,7 +1040,8 @@ string MapDefinition::str(const DataIndex* data_index) const {
add_map(this->alt_maps1[w][z]); add_map(this->alt_maps1[w][z]);
} }
for (size_t w = 0; w < 2; w++) { for (size_t w = 0; w < 2; w++) {
lines.emplace_back(string_printf(" alt tiles a3 %zu/%zu=%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g", z, w, lines.emplace_back(string_printf(
" alt tiles a3 %zu/%zu=%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g", z, w,
this->alt_maps_unknown_a3[w][z][0x00].load(), this->alt_maps_unknown_a3[w][z][0x01].load(), this->alt_maps_unknown_a3[w][z][0x00].load(), this->alt_maps_unknown_a3[w][z][0x01].load(),
this->alt_maps_unknown_a3[w][z][0x02].load(), this->alt_maps_unknown_a3[w][z][0x03].load(), this->alt_maps_unknown_a3[w][z][0x02].load(), this->alt_maps_unknown_a3[w][z][0x03].load(),
this->alt_maps_unknown_a3[w][z][0x04].load(), this->alt_maps_unknown_a3[w][z][0x05].load(), this->alt_maps_unknown_a3[w][z][0x04].load(), this->alt_maps_unknown_a3[w][z][0x05].load(),
@@ -1076,7 +1055,8 @@ string MapDefinition::str(const DataIndex* data_index) const {
} }
for (size_t w = 0; w < 3; w++) { for (size_t w = 0; w < 3; w++) {
for (size_t z = 0; z < 0x24; z += 3) { for (size_t z = 0; z < 0x24; z += 3) {
lines.emplace_back(string_printf(" a4[%zu][0x%02zX:0x%02zX]=%g %g %g", w, z, z + 3, lines.emplace_back(string_printf(
" a4[%zu][0x%02zX:0x%02zX]=%g %g %g", w, z, z + 3,
this->unknown_a4[w][z + 0].load(), this->unknown_a4[w][z + 0].load(),
this->unknown_a4[w][z + 1].load(), this->unknown_a4[w][z + 1].load(),
this->unknown_a4[w][z + 2].load())); this->unknown_a4[w][z + 2].load()));
@@ -1085,16 +1065,19 @@ string MapDefinition::str(const DataIndex* data_index) const {
lines.emplace_back(" modification tiles:"); lines.emplace_back(" modification tiles:");
add_map(this->modification_tiles); add_map(this->modification_tiles);
for (size_t z = 0; z < 0x70; z += 0x10) { for (size_t z = 0; z < 0x70; z += 0x10) {
lines.emplace_back(string_printf(" a5[0x%02zX:0x%02zX]=%02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX", z, z + 0x10, lines.emplace_back(string_printf(
" a5[0x%02zX:0x%02zX]=%02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX", z, z + 0x10,
this->unknown_a5[z + 0x00], this->unknown_a5[z + 0x01], this->unknown_a5[z + 0x02], this->unknown_a5[z + 0x03], this->unknown_a5[z + 0x00], this->unknown_a5[z + 0x01], this->unknown_a5[z + 0x02], this->unknown_a5[z + 0x03],
this->unknown_a5[z + 0x04], this->unknown_a5[z + 0x05], this->unknown_a5[z + 0x06], this->unknown_a5[z + 0x07], this->unknown_a5[z + 0x04], this->unknown_a5[z + 0x05], this->unknown_a5[z + 0x06], this->unknown_a5[z + 0x07],
this->unknown_a5[z + 0x08], this->unknown_a5[z + 0x09], this->unknown_a5[z + 0x0A], this->unknown_a5[z + 0x0B], this->unknown_a5[z + 0x08], this->unknown_a5[z + 0x09], this->unknown_a5[z + 0x0A], this->unknown_a5[z + 0x0B],
this->unknown_a5[z + 0x0C], this->unknown_a5[z + 0x0D], this->unknown_a5[z + 0x0E], this->unknown_a5[z + 0x0F])); this->unknown_a5[z + 0x0C], this->unknown_a5[z + 0x0D], this->unknown_a5[z + 0x0E], this->unknown_a5[z + 0x0F]));
} }
lines.emplace_back(string_printf(" a5[0x70:0x74]=%02hhX %02hhX %02hhX %02hhX", lines.emplace_back(string_printf(
" a5[0x70:0x74]=%02hhX %02hhX %02hhX %02hhX",
this->unknown_a5[0x70], this->unknown_a5[0x71], this->unknown_a5[0x72], this->unknown_a5[0x73])); this->unknown_a5[0x70], this->unknown_a5[0x71], this->unknown_a5[0x72], this->unknown_a5[0x73]));
lines.emplace_back(" default_rules: " + this->default_rules.str()); lines.emplace_back(" default_rules: " + this->default_rules.str());
lines.emplace_back(string_printf(" a6=%02hhX %02hhX %02hhX %02hhX", lines.emplace_back(string_printf(
" a6=%02hhX %02hhX %02hhX %02hhX",
this->unknown_a6[0], this->unknown_a6[1], this->unknown_a6[2], this->unknown_a6[3])); this->unknown_a6[0], this->unknown_a6[1], this->unknown_a6[2], this->unknown_a6[3]));
lines.emplace_back(" name: " + string(this->name)); lines.emplace_back(" name: " + string(this->name));
lines.emplace_back(" location_name: " + string(this->location_name)); lines.emplace_back(" location_name: " + string(this->location_name));
@@ -1103,21 +1086,25 @@ string MapDefinition::str(const DataIndex* data_index) const {
lines.emplace_back(string_printf(" map_xy: %hu %hu", this->map_x.load(), this->map_y.load())); lines.emplace_back(string_printf(" map_xy: %hu %hu", this->map_x.load(), this->map_y.load()));
for (size_t z = 0; z < 3; z++) { for (size_t z = 0; z < 3; z++) {
lines.emplace_back(string_printf(" npc_chars[%zu]:", z)); lines.emplace_back(string_printf(" npc_chars[%zu]:", z));
lines.emplace_back(string_printf(" a1=%04hX %04hX", lines.emplace_back(string_printf(
" a1=%04hX %04hX",
this->npc_chars[z].unknown_a1[0].load(), this->npc_chars[z].unknown_a1[1].load())); this->npc_chars[z].unknown_a1[0].load(), this->npc_chars[z].unknown_a1[1].load()));
lines.emplace_back(string_printf(" a2=%02hX %02hX %02hX %02hX", lines.emplace_back(string_printf(
" a2=%02hX %02hX %02hX %02hX",
this->npc_chars[z].unknown_a2[0], this->npc_chars[z].unknown_a2[1], this->npc_chars[z].unknown_a2[0], this->npc_chars[z].unknown_a2[1],
this->npc_chars[z].unknown_a2[2], this->npc_chars[z].unknown_a2[3])); this->npc_chars[z].unknown_a2[2], this->npc_chars[z].unknown_a2[3]));
lines.emplace_back(" name: " + string(this->npc_chars[z].name)); lines.emplace_back(" name: " + string(this->npc_chars[z].name));
for (size_t w = 0; w < 0x78; w += 0x08) { for (size_t w = 0; w < 0x78; w += 0x08) {
lines.emplace_back(string_printf(" a3[0x%02zX:0x%02zX]=%04hX %04hX %04hX %04hX %04hX %04hX %04hX %04hX", lines.emplace_back(string_printf(
" a3[0x%02zX:0x%02zX]=%04hX %04hX %04hX %04hX %04hX %04hX %04hX %04hX",
w, w + 0x08, w, w + 0x08,
this->npc_chars[z].unknown_a3[w + 0x00].load(), this->npc_chars[z].unknown_a3[w + 0x01].load(), this->npc_chars[z].unknown_a3[w + 0x00].load(), this->npc_chars[z].unknown_a3[w + 0x01].load(),
this->npc_chars[z].unknown_a3[w + 0x02].load(), this->npc_chars[z].unknown_a3[w + 0x03].load(), this->npc_chars[z].unknown_a3[w + 0x02].load(), this->npc_chars[z].unknown_a3[w + 0x03].load(),
this->npc_chars[z].unknown_a3[w + 0x04].load(), this->npc_chars[z].unknown_a3[w + 0x05].load(), this->npc_chars[z].unknown_a3[w + 0x04].load(), this->npc_chars[z].unknown_a3[w + 0x05].load(),
this->npc_chars[z].unknown_a3[w + 0x06].load(), this->npc_chars[z].unknown_a3[w + 0x07].load())); this->npc_chars[z].unknown_a3[w + 0x06].load(), this->npc_chars[z].unknown_a3[w + 0x07].load()));
} }
lines.emplace_back(string_printf(" a3[0x78:0x7E]=%04hX %04hX %04hX %04hX %04hX %04hX", lines.emplace_back(string_printf(
" a3[0x78:0x7E]=%04hX %04hX %04hX %04hX %04hX %04hX",
this->npc_chars[z].unknown_a3[0x78].load(), this->npc_chars[z].unknown_a3[0x79].load(), this->npc_chars[z].unknown_a3[0x78].load(), this->npc_chars[z].unknown_a3[0x79].load(),
this->npc_chars[z].unknown_a3[0x7A].load(), this->npc_chars[z].unknown_a3[0x7B].load(), this->npc_chars[z].unknown_a3[0x7A].load(), this->npc_chars[z].unknown_a3[0x7B].load(),
this->npc_chars[z].unknown_a3[0x7C].load(), this->npc_chars[z].unknown_a3[0x7D].load())); this->npc_chars[z].unknown_a3[0x7C].load(), this->npc_chars[z].unknown_a3[0x7D].load()));
@@ -1129,7 +1116,8 @@ string MapDefinition::str(const DataIndex* data_index) const {
if (data_index) { if (data_index) {
try { try {
entry = data_index->definition_for_card_id(card_id); entry = data_index->definition_for_card_id(card_id);
} catch (const out_of_range&) { } } catch (const out_of_range&) {
}
} }
if (entry) { if (entry) {
string name = entry->def.en_name; string name = entry->def.en_name;
@@ -1152,7 +1140,7 @@ string MapDefinition::str(const DataIndex* data_index) const {
} }
lines.emplace_back(" a7a=" + format_data_string(this->unknown_a7_a.data(), this->unknown_a7_a.bytes())); lines.emplace_back(" a7a=" + format_data_string(this->unknown_a7_a.data(), this->unknown_a7_a.bytes()));
lines.emplace_back(string_printf(" a7b=[%08" PRIX32 " %08" PRIX32 " %08" PRIX32 "]", lines.emplace_back(string_printf(" a7b=[%08" PRIX32 " %08" PRIX32 " %08" PRIX32 "]",
this->unknown_a7_b[0].load(), this->unknown_a7_b[1].load(), this->unknown_a7_b[2].load())); this->unknown_a7_b[0].load(), this->unknown_a7_b[1].load(), this->unknown_a7_b[2].load()));
if (this->before_message[0]) { if (this->before_message[0]) {
lines.emplace_back(" before_message: " + string(this->before_message)); lines.emplace_back(" before_message: " + string(this->before_message));
} }
@@ -1168,7 +1156,8 @@ string MapDefinition::str(const DataIndex* data_index) const {
if (data_index) { if (data_index) {
try { try {
entry = data_index->definition_for_card_id(card_id); entry = data_index->definition_for_card_id(card_id);
} catch (const out_of_range&) { } } catch (const out_of_range&) {
}
} }
if (entry) { if (entry) {
string name = entry->def.en_name; string name = entry->def.en_name;
@@ -1178,35 +1167,35 @@ string MapDefinition::str(const DataIndex* data_index) const {
} }
} }
lines.emplace_back(string_printf(" a9=[%08" PRIX32 " %08" PRIX32 " %04hX %04hX]", lines.emplace_back(string_printf(" a9=[%08" PRIX32 " %08" PRIX32 " %04hX %04hX]",
this->unknown_a9_a.load(), this->unknown_a9_b.load(), this->unknown_a9_c.load(), this->unknown_a9_d.load())); this->unknown_a9_a.load(), this->unknown_a9_b.load(), this->unknown_a9_c.load(), this->unknown_a9_d.load()));
lines.emplace_back(string_printf(" a10=%02hhX", this->unknown_a10)); lines.emplace_back(string_printf(" a10=%02hhX", this->unknown_a10));
lines.emplace_back(string_printf(" cyber_block_type=%02hhX", this->cyber_block_type)); lines.emplace_back(string_printf(" cyber_block_type=%02hhX", this->cyber_block_type));
lines.emplace_back(string_printf(" a11=%02hhX%02hhX", this->unknown_a11[0], this->unknown_a11[1])); lines.emplace_back(string_printf(" a11=%02hhX%02hhX", this->unknown_a11[0], this->unknown_a11[1]));
static const array<const char*, 0x18> sc_card_entry_names = { static const array<const char*, 0x18> sc_card_entry_names = {
"00 (Guykild; 0005)", "00 (Guykild; 0005)",
"01 (Kylria; 0006)", "01 (Kylria; 0006)",
"02 (Saligun; 0110)", "02 (Saligun; 0110)",
"03 (Relmitos; 0111)", "03 (Relmitos; 0111)",
"04 (Kranz; 0002)", "04 (Kranz; 0002)",
"05 (Sil'fer; 0004)", "05 (Sil'fer; 0004)",
"06 (Ino'lis; 0003)", "06 (Ino'lis; 0003)",
"07 (Viviana; 0112)", "07 (Viviana; 0112)",
"08 (Teifu; 0113)", "08 (Teifu; 0113)",
"09 (Orland; 0001)", "09 (Orland; 0001)",
"0A (Stella; 0114)", "0A (Stella; 0114)",
"0B (Glustar; 0115)", "0B (Glustar; 0115)",
"0C (Hyze; 0117)", "0C (Hyze; 0117)",
"0D (Rufina; 0118)", "0D (Rufina; 0118)",
"0E (Peko; 0119)", "0E (Peko; 0119)",
"0F (Creinu; 011A)", "0F (Creinu; 011A)",
"10 (Reiz; 011B)", "10 (Reiz; 011B)",
"11 (Lura; 0007)", "11 (Lura; 0007)",
"12 (Break; 0008)", "12 (Break; 0008)",
"13 (Rio; 011C)", "13 (Rio; 011C)",
"14 (Endu; 0116)", "14 (Endu; 0116)",
"15 (Memoru; 011D)", "15 (Memoru; 011D)",
"16 (K.C.; 011E)", "16 (K.C.; 011E)",
"17 (Ohgun; 011F)", "17 (Ohgun; 011F)",
}; };
string unavailable_sc_cards = " unavailable_sc_cards=["; string unavailable_sc_cards = " unavailable_sc_cards=[";
for (size_t z = 0; z < 0x18; z++) { for (size_t z = 0; z < 0x18; z++) {
@@ -1338,10 +1327,8 @@ bool Rules::check_and_reset_invalid_fields() {
return ret; return ret;
} }
DataIndex::DataIndex(const string& directory, uint32_t behavior_flags) DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
: behavior_flags(behavior_flags) { : behavior_flags(behavior_flags) {
unordered_map<uint32_t, vector<string>> card_tags; unordered_map<uint32_t, vector<string>> card_tags;
unordered_map<uint32_t, string> card_text; unordered_map<uint32_t, string> card_text;
@@ -1487,10 +1474,12 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) { if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
try { try {
entry->text = move(card_text.at(defs[x].card_id)); entry->text = move(card_text.at(defs[x].card_id));
} catch (const out_of_range&) { } } catch (const out_of_range&) {
}
try { try {
entry->debug_tags = move(card_tags.at(defs[x].card_id)); entry->debug_tags = move(card_tags.at(defs[x].card_id));
} catch (const out_of_range&) { } } catch (const out_of_range&) {
}
} }
} }
@@ -1533,13 +1522,13 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
this->maps_by_name.emplace(entry->map.name, entry); this->maps_by_name.emplace(entry->map.name, entry);
string name = entry->map.name; string name = entry->map.name;
static_game_data_log.info("Indexed Episode 3 %s %s (%08" PRIX32 "; %s)", static_game_data_log.info("Indexed Episode 3 %s %s (%08" PRIX32 "; %s)",
is_quest ? "online quest" : "free battle map", is_quest ? "online quest" : "free battle map",
filename.c_str(), entry->map.map_number.load(), name.c_str()); filename.c_str(), entry->map.map_number.load(), name.c_str());
} }
} catch (const exception& e) { } catch (const exception& e) {
static_game_data_log.warning("Failed to index Episode 3 map %s: %s", static_game_data_log.warning("Failed to index Episode 3 map %s: %s",
filename.c_str(), e.what()); filename.c_str(), e.what());
} }
} }
}; };
@@ -1567,10 +1556,12 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
} }
DataIndex::MapEntry::MapEntry(const MapDefinition& map, bool is_quest) DataIndex::MapEntry::MapEntry(const MapDefinition& map, bool is_quest)
: map(map), is_quest(is_quest) { } : map(map),
is_quest(is_quest) {}
DataIndex::MapEntry::MapEntry(const string& compressed, bool is_quest) DataIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
: is_quest(is_quest), compressed_data(compressed) { : is_quest(is_quest),
compressed_data(compressed) {
string decompressed = prs_decompress(this->compressed_data); string decompressed = prs_decompress(this->compressed_data);
if (decompressed.size() != sizeof(MapDefinition)) { if (decompressed.size() != sizeof(MapDefinition)) {
throw runtime_error(string_printf( throw runtime_error(string_printf(
@@ -1673,7 +1664,7 @@ const string& DataIndex::get_compressed_map_list() const {
} }
size_t decompressed_size = sizeof(header) + entries_w.size() + strings_w.size(); size_t decompressed_size = sizeof(header) + entries_w.size() + strings_w.size();
static_game_data_log.info("Generated Episode 3 compressed map list (0x%zX -> 0x%zX bytes)", static_game_data_log.info("Generated Episode 3 compressed map list (0x%zX -> 0x%zX bytes)",
decompressed_size, this->compressed_map_list.size()); decompressed_size, this->compressed_map_list.size());
} }
return this->compressed_map_list; return this->compressed_map_list;
} }
@@ -1711,8 +1702,6 @@ shared_ptr<const COMDeckDefinition> DataIndex::random_com_deck() const {
return this->com_decks[random_object<size_t>() % this->com_decks.size()]; return this->com_decks[random_object<size_t>() % this->com_decks.size()];
} }
void PlayerConfig::decrypt() { void PlayerConfig::decrypt() {
if (!this->is_encrypted) { if (!this->is_encrypted) {
return; return;
@@ -1740,6 +1729,4 @@ void PlayerConfig::encrypt(uint8_t basis) {
this->basis = basis; this->basis = basis;
} }
} // namespace Episode3 } // namespace Episode3
+254 -270
View File
@@ -2,42 +2,36 @@
#include <stdint.h> #include <stdint.h>
#include <string>
#include <map> #include <map>
#include <set>
#include <memory> #include <memory>
#include <unordered_map>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <phosg/JSON.hh> #include <phosg/JSON.hh>
#include <set>
#include <string>
#include <unordered_map>
#include "../Text.hh" #include "../Text.hh"
namespace Episode3 { namespace Episode3 {
// The comment in Server.hh does not apply to this file (and DataIndex.cc). // The comment in Server.hh does not apply to this file (and DataIndex.cc).
// Except for the Location structure, these structures and functions are not // Except for the Location structure, these structures and functions are not
// based on Sega's original implementation. // based on Sega's original implementation.
class DataIndex; class DataIndex;
enum BehaviorFlag { enum BehaviorFlag {
SKIP_DECK_VERIFY = 0x00000001, SKIP_DECK_VERIFY = 0x00000001,
IGNORE_CARD_COUNTS = 0x00000002, IGNORE_CARD_COUNTS = 0x00000002,
SKIP_D1_D2_REPLACE = 0x00000004, SKIP_D1_D2_REPLACE = 0x00000004,
DISABLE_TIME_LIMITS = 0x00000008, DISABLE_TIME_LIMITS = 0x00000008,
ENABLE_STATUS_MESSAGES = 0x00000010, ENABLE_STATUS_MESSAGES = 0x00000010,
LOAD_CARD_TEXT = 0x00000020, LOAD_CARD_TEXT = 0x00000020,
ENABLE_RECORDING = 0x00000040, ENABLE_RECORDING = 0x00000040,
DISABLE_MASKING = 0x00000080, DISABLE_MASKING = 0x00000080,
DISABLE_INTERFERENCE = 0x00000100, DISABLE_INTERFERENCE = 0x00000100,
}; };
enum class StatSwapType : uint8_t { enum class StatSwapType : uint8_t {
NONE = 0, NONE = 0,
A_T_SWAP = 1, A_T_SWAP = 1,
@@ -99,54 +93,54 @@ enum class CriterionCode : uint8_t {
}; };
enum class CardRarity : uint8_t { enum class CardRarity : uint8_t {
N1 = 0x01, N1 = 0x01,
R1 = 0x02, R1 = 0x02,
S = 0x03, S = 0x03,
E = 0x04, E = 0x04,
N2 = 0x05, N2 = 0x05,
N3 = 0x06, N3 = 0x06,
N4 = 0x07, N4 = 0x07,
R2 = 0x08, R2 = 0x08,
R3 = 0x09, R3 = 0x09,
R4 = 0x0A, R4 = 0x0A,
SS = 0x0B, SS = 0x0B,
D1 = 0x0C, D1 = 0x0C,
D2 = 0x0D, D2 = 0x0D,
INVIS = 0x0E, INVIS = 0x0E,
}; };
enum class CardType : uint8_t { enum class CardType : uint8_t {
HUNTERS_SC = 0x00, HUNTERS_SC = 0x00,
ARKZ_SC = 0x01, ARKZ_SC = 0x01,
ITEM = 0x02, ITEM = 0x02,
CREATURE = 0x03, CREATURE = 0x03,
ACTION = 0x04, ACTION = 0x04,
ASSIST = 0x05, ASSIST = 0x05,
INVALID_FF = 0xFF, INVALID_FF = 0xFF,
END_CARD_LIST = 0xFF, END_CARD_LIST = 0xFF,
}; };
enum class CardClass : uint16_t { enum class CardClass : uint16_t {
HU_SC = 0x0000, HU_SC = 0x0000,
RA_SC = 0x0001, RA_SC = 0x0001,
FO_SC = 0x0002, FO_SC = 0x0002,
NATIVE_CREATURE = 0x000A, NATIVE_CREATURE = 0x000A,
A_BEAST_CREATURE = 0x000B, A_BEAST_CREATURE = 0x000B,
MACHINE_CREATURE = 0x000C, MACHINE_CREATURE = 0x000C,
DARK_CREATURE = 0x000D, DARK_CREATURE = 0x000D,
GUARD_ITEM = 0x0015, GUARD_ITEM = 0x0015,
MAG_ITEM = 0x0017, MAG_ITEM = 0x0017,
SWORD_ITEM = 0x0018, SWORD_ITEM = 0x0018,
GUN_ITEM = 0x0019, GUN_ITEM = 0x0019,
CANE_ITEM = 0x001A, CANE_ITEM = 0x001A,
ATTACK_ACTION = 0x001E, ATTACK_ACTION = 0x001E,
DEFENSE_ACTION = 0x001F, DEFENSE_ACTION = 0x001F,
TECH = 0x0020, TECH = 0x0020,
PHOTON_BLAST = 0x0021, PHOTON_BLAST = 0x0021,
CONNECT_ONLY_ATTACK_ACTION = 0x0022, CONNECT_ONLY_ATTACK_ACTION = 0x0022,
BOSS_ATTACK_ACTION = 0x0023, BOSS_ATTACK_ACTION = 0x0023,
BOSS_TECH = 0x0024, BOSS_TECH = 0x0024,
ASSIST = 0x0028, ASSIST = 0x0028,
}; };
bool card_class_is_tech_like(CardClass cc); bool card_class_is_tech_like(CardClass cc);
@@ -165,216 +159,216 @@ enum class TargetMode : uint8_t {
}; };
enum class ConditionType : uint8_t { enum class ConditionType : uint8_t {
NONE = 0x00, NONE = 0x00,
AP_BOOST = 0x01, // Temporarily increase AP by N AP_BOOST = 0x01, // Temporarily increase AP by N
RAMPAGE = 0x02, RAMPAGE = 0x02,
MULTI_STRIKE = 0x03, // Duplicate attack N times MULTI_STRIKE = 0x03, // Duplicate attack N times
DAMAGE_MOD_1 = 0x04, // Set attack damage / AP to N after action cards applied (step 1) DAMAGE_MOD_1 = 0x04, // Set attack damage / AP to N after action cards applied (step 1)
IMMOBILE = 0x05, // Give Immobile condition IMMOBILE = 0x05, // Give Immobile condition
HOLD = 0x06, // Give Hold condition HOLD = 0x06, // Give Hold condition
UNKNOWN_07 = 0x07, UNKNOWN_07 = 0x07,
TP_BOOST = 0x08, // Add N TP temporarily during attack TP_BOOST = 0x08, // Add N TP temporarily during attack
GIVE_DAMAGE = 0x09, // Cause direct N HP loss GIVE_DAMAGE = 0x09, // Cause direct N HP loss
GUOM = 0x0A, // Give Guom condition GUOM = 0x0A, // Give Guom condition
PARALYZE = 0x0B, // Give Paralysis condition PARALYZE = 0x0B, // Give Paralysis condition
UNKNOWN_0C = 0x0C, // Swap AP and TP temporarily (presumably) UNKNOWN_0C = 0x0C, // Swap AP and TP temporarily (presumably)
A_H_SWAP = 0x0D, // Swap AP and HP temporarily A_H_SWAP = 0x0D, // Swap AP and HP temporarily
PIERCE = 0x0E, // Attack SC directly even if they have items equipped PIERCE = 0x0E, // Attack SC directly even if they have items equipped
UNKNOWN_0F = 0x0F, UNKNOWN_0F = 0x0F,
HEAL = 0x10, // Increase HP by N HEAL = 0x10, // Increase HP by N
RETURN_TO_HAND = 0x11, // Return card to hand RETURN_TO_HAND = 0x11, // Return card to hand
UNKNOWN_12 = 0x12, UNKNOWN_12 = 0x12,
UNKNOWN_13 = 0x13, UNKNOWN_13 = 0x13,
ACID = 0x14, // Give Acid condition ACID = 0x14, // Give Acid condition
UNKNOWN_15 = 0x15, UNKNOWN_15 = 0x15,
MIGHTY_KNUCKLE = 0x16, // Temporarily increase AP by N, and set ATK dice to zero MIGHTY_KNUCKLE = 0x16, // Temporarily increase AP by N, and set ATK dice to zero
UNIT_BLOW = 0x17, // Temporarily increase AP by N * number of this card set within phase UNIT_BLOW = 0x17, // Temporarily increase AP by N * number of this card set within phase
CURSE = 0x18, // Give Curse condition CURSE = 0x18, // Give Curse condition
COMBO_AP = 0x19, // Temporarily increase AP by number of this card set within phase COMBO_AP = 0x19, // Temporarily increase AP by number of this card set within phase
PIERCE_RAMPAGE_BLOCK = 0x1A, // Block attack if Pierce/Rampage PIERCE_RAMPAGE_BLOCK = 0x1A, // Block attack if Pierce/Rampage
ABILITY_TRAP = 0x1B, // Temporarily disable opponent abilities ABILITY_TRAP = 0x1B, // Temporarily disable opponent abilities
FREEZE = 0x1C, // Give Freeze condition FREEZE = 0x1C, // Give Freeze condition
ANTI_ABNORMALITY_1 = 0x1D, // Cure all abnormal conditions ANTI_ABNORMALITY_1 = 0x1D, // Cure all abnormal conditions
UNKNOWN_1E = 0x1E, UNKNOWN_1E = 0x1E,
EXPLOSION = 0x1F, // Damage all SCs and FCs by number of this same card set * 2 EXPLOSION = 0x1F, // Damage all SCs and FCs by number of this same card set * 2
UNKNOWN_20 = 0x20, UNKNOWN_20 = 0x20,
UNKNOWN_21 = 0x21, UNKNOWN_21 = 0x21,
UNKNOWN_22 = 0x22, UNKNOWN_22 = 0x22,
RETURN_TO_DECK = 0x23, // Cancel discard and move to bottom of deck instead RETURN_TO_DECK = 0x23, // Cancel discard and move to bottom of deck instead
AERIAL = 0x24, // Give Aerial status AERIAL = 0x24, // Give Aerial status
AP_LOSS = 0x25, // Make attacker temporarily lose N AP during defense AP_LOSS = 0x25, // Make attacker temporarily lose N AP during defense
BONUS_FROM_LEADER = 0x26, // Gain AP equal to the number of cards of type N on the field BONUS_FROM_LEADER = 0x26, // Gain AP equal to the number of cards of type N on the field
FREE_MANEUVER = 0x27, // Enable movement over occupied tiles FREE_MANEUVER = 0x27, // Enable movement over occupied tiles
HASTE = 0x28, // Multiply all move action costs by expr (which may be zero) HASTE = 0x28, // Multiply all move action costs by expr (which may be zero)
CLONE = 0x29, // Make setting this card free if at least one card of type N is already on the field CLONE = 0x29, // Make setting this card free if at least one card of type N is already on the field
DEF_DISABLE_BY_COST = 0x2A, // Disable use of any defense cards costing between (N / 10) and (N % 10) points, inclusive DEF_DISABLE_BY_COST = 0x2A, // Disable use of any defense cards costing between (N / 10) and (N % 10) points, inclusive
FILIAL = 0x2B, // Increase controlling SC's HP by N when this card is destroyed FILIAL = 0x2B, // Increase controlling SC's HP by N when this card is destroyed
SNATCH = 0x2C, // Steal N EXP during attack SNATCH = 0x2C, // Steal N EXP during attack
HAND_DISRUPTER = 0x2D, // Discard N cards from hand immediately HAND_DISRUPTER = 0x2D, // Discard N cards from hand immediately
DROP = 0x2E, // Give Drop condition DROP = 0x2E, // Give Drop condition
ACTION_DISRUPTER = 0x2F, // Destroy all action cards used by attacker ACTION_DISRUPTER = 0x2F, // Destroy all action cards used by attacker
SET_HP = 0x30, // Set HP to N SET_HP = 0x30, // Set HP to N
NATIVE_SHIELD = 0x31, // Block attacks from Native creatures NATIVE_SHIELD = 0x31, // Block attacks from Native creatures
A_BEAST_SHIELD = 0x32, // Block attacks from A.Beast creatures A_BEAST_SHIELD = 0x32, // Block attacks from A.Beast creatures
MACHINE_SHIELD = 0x33, // Block attacks from Machine creatures MACHINE_SHIELD = 0x33, // Block attacks from Machine creatures
DARK_SHIELD = 0x34, // Block attacks from Dark creatures DARK_SHIELD = 0x34, // Block attacks from Dark creatures
SWORD_SHIELD = 0x35, // Block attacks from Sword items SWORD_SHIELD = 0x35, // Block attacks from Sword items
GUN_SHIELD = 0x36, // Block attacks from Gun items GUN_SHIELD = 0x36, // Block attacks from Gun items
CANE_SHIELD = 0x37, // Block attacks from Cane items CANE_SHIELD = 0x37, // Block attacks from Cane items
UNKNOWN_38 = 0x38, UNKNOWN_38 = 0x38,
UNKNOWN_39 = 0x39, UNKNOWN_39 = 0x39,
DEFENDER = 0x3A, // Make attacks go to setter of this card instead of original target DEFENDER = 0x3A, // Make attacks go to setter of this card instead of original target
SURVIVAL_DECOYS = 0x3B, // Redirect damage for multi-sided attack SURVIVAL_DECOYS = 0x3B, // Redirect damage for multi-sided attack
GIVE_OR_TAKE_EXP = 0x3C, // Give N EXP, or take if N is negative GIVE_OR_TAKE_EXP = 0x3C, // Give N EXP, or take if N is negative
UNKNOWN_3D = 0x3D, UNKNOWN_3D = 0x3D,
DEATH_COMPANION = 0x3E, // If this card has 1 or 2 HP, set its HP to N DEATH_COMPANION = 0x3E, // If this card has 1 or 2 HP, set its HP to N
EXP_DECOY = 0x3F, // If defender has EXP, lose EXP instead of getting damage when attacked EXP_DECOY = 0x3F, // If defender has EXP, lose EXP instead of getting damage when attacked
SET_MV = 0x40, // Set MV to N SET_MV = 0x40, // Set MV to N
GROUP = 0x41, // Temporarily increase AP by N * number of this card on field, excluding itself GROUP = 0x41, // Temporarily increase AP by N * number of this card on field, excluding itself
BERSERK = 0x42, // User of this card receives the same damage as target, and isn't helped by target's defense cards BERSERK = 0x42, // User of this card receives the same damage as target, and isn't helped by target's defense cards
GUARD_CREATURE = 0x43, // Attacks on controlling SC damage this card instead GUARD_CREATURE = 0x43, // Attacks on controlling SC damage this card instead
TECH = 0x44, // Technique cards cost 1 fewer ATK point TECH = 0x44, // Technique cards cost 1 fewer ATK point
BIG_SWING = 0x45, // Increase all attacking ATK costs by 1 BIG_SWING = 0x45, // Increase all attacking ATK costs by 1
UNKNOWN_46 = 0x46, UNKNOWN_46 = 0x46,
SHIELD_WEAPON = 0x47, // Limit attacker's choice of target to guard items SHIELD_WEAPON = 0x47, // Limit attacker's choice of target to guard items
ATK_DICE_BOOST = 0x48, // Increase ATK dice roll by 1 ATK_DICE_BOOST = 0x48, // Increase ATK dice roll by 1
UNKNOWN_49 = 0x49, UNKNOWN_49 = 0x49,
MAJOR_PIERCE = 0x4A, // If SC has over half of max HP, attacks target SC instead of equipped items MAJOR_PIERCE = 0x4A, // If SC has over half of max HP, attacks target SC instead of equipped items
HEAVY_PIERCE = 0x4B, // If SC has 3 or more items equipped, attacks target SC instead of equipped items HEAVY_PIERCE = 0x4B, // If SC has 3 or more items equipped, attacks target SC instead of equipped items
MAJOR_RAMPAGE = 0x4C, // If SC has over half of max HP, attacks target SC and all equipped items MAJOR_RAMPAGE = 0x4C, // If SC has over half of max HP, attacks target SC and all equipped items
HEAVY_RAMPAGE = 0x4D, // If SC has 3 or more items equipped, attacks target SC and all equipped items HEAVY_RAMPAGE = 0x4D, // If SC has 3 or more items equipped, attacks target SC and all equipped items
AP_GROWTH = 0x4E, // Permanently increase AP by N AP_GROWTH = 0x4E, // Permanently increase AP by N
TP_GROWTH = 0x4F, // Permanently increase TP by N TP_GROWTH = 0x4F, // Permanently increase TP by N
REBORN = 0x50, // If any card of type N is on the field, this card goes to the hand when destroyed instead of being discarded REBORN = 0x50, // If any card of type N is on the field, this card goes to the hand when destroyed instead of being discarded
COPY = 0x51, // Temporarily set AP/TP to N percent (or 100% if N is 0) of opponent's values COPY = 0x51, // Temporarily set AP/TP to N percent (or 100% if N is 0) of opponent's values
UNKNOWN_52 = 0x52, UNKNOWN_52 = 0x52,
MISC_GUARDS = 0x53, // Add N to card's defense value MISC_GUARDS = 0x53, // Add N to card's defense value
AP_OVERRIDE = 0x54, // Set AP to N temporarily AP_OVERRIDE = 0x54, // Set AP to N temporarily
TP_OVERRIDE = 0x55, // Set TP to N temporarily TP_OVERRIDE = 0x55, // Set TP to N temporarily
RETURN = 0x56, // Return card to hand on destruction instead of discarding RETURN = 0x56, // Return card to hand on destruction instead of discarding
A_T_SWAP_PERM = 0x57, // Permanently swap AP and TP A_T_SWAP_PERM = 0x57, // Permanently swap AP and TP
A_H_SWAP_PERM = 0x58, // Permanently swap AP and HP A_H_SWAP_PERM = 0x58, // Permanently swap AP and HP
SLAYERS_ASSASSINS = 0x59, // Temporarily increase AP during attack SLAYERS_ASSASSINS = 0x59, // Temporarily increase AP during attack
ANTI_ABNORMALITY_2 = 0x5A, // Remove all conditions ANTI_ABNORMALITY_2 = 0x5A, // Remove all conditions
FIXED_RANGE = 0x5B, // Use SC's range instead of weapon or attack card ranges FIXED_RANGE = 0x5B, // Use SC's range instead of weapon or attack card ranges
ELUDE = 0x5C, // SC does not lose HP when equipped items are destroyed ELUDE = 0x5C, // SC does not lose HP when equipped items are destroyed
PARRY = 0x5D, // Forward attack to a random FC within one tile of original target, excluding attacker and original target PARRY = 0x5D, // Forward attack to a random FC within one tile of original target, excluding attacker and original target
BLOCK_ATTACK = 0x5E, // Completely block attack BLOCK_ATTACK = 0x5E, // Completely block attack
UNKNOWN_5F = 0x5F, UNKNOWN_5F = 0x5F,
UNKNOWN_60 = 0x60, UNKNOWN_60 = 0x60,
COMBO_TP = 0x61, // Gain TP equal to the number of cards of type N on the field COMBO_TP = 0x61, // Gain TP equal to the number of cards of type N on the field
MISC_AP_BONUSES = 0x62, // Temporarily increase AP by N MISC_AP_BONUSES = 0x62, // Temporarily increase AP by N
MISC_TP_BONUSES = 0x63, // Temporarily increase TP by N MISC_TP_BONUSES = 0x63, // Temporarily increase TP by N
UNKNOWN_64 = 0x64, UNKNOWN_64 = 0x64,
MISC_DEFENSE_BONUSES = 0x65, // Decrease damage by N MISC_DEFENSE_BONUSES = 0x65, // Decrease damage by N
MOSTLY_HALFGUARDS = 0x66, // Reduce damage from incoming attack by N MOSTLY_HALFGUARDS = 0x66, // Reduce damage from incoming attack by N
PERIODIC_FIELD = 0x67, // Swap immunity to tech or physical attacks PERIODIC_FIELD = 0x67, // Swap immunity to tech or physical attacks
FC_LIMIT_BY_COUNT = 0x68, // Change FC limit from 8 ATK points total to 4 FCs total FC_LIMIT_BY_COUNT = 0x68, // Change FC limit from 8 ATK points total to 4 FCs total
UNKNOWN_69 = 0x69, UNKNOWN_69 = 0x69,
MV_BONUS = 0x6A, // Increase MV by N MV_BONUS = 0x6A, // Increase MV by N
FORWARD_DAMAGE = 0x6B, FORWARD_DAMAGE = 0x6B,
WEAK_SPOT_INFLUENCE = 0x6C, // Temporarily decrease AP by N WEAK_SPOT_INFLUENCE = 0x6C, // Temporarily decrease AP by N
DAMAGE_MODIFIER_2 = 0x6D, // Set attack damage / AP after action cards applied (step 2) DAMAGE_MODIFIER_2 = 0x6D, // Set attack damage / AP after action cards applied (step 2)
WEAK_HIT_BLOCK = 0x6E, // Block all attacks of N damage or less WEAK_HIT_BLOCK = 0x6E, // Block all attacks of N damage or less
AP_SILENCE = 0x6F, // Temporarily decrease AP of opponent by N AP_SILENCE = 0x6F, // Temporarily decrease AP of opponent by N
TP_SILENCE = 0x70, // Temporarily decrease TP of opponent by N TP_SILENCE = 0x70, // Temporarily decrease TP of opponent by N
A_T_SWAP = 0x71, // Temporarily swap AP and TP A_T_SWAP = 0x71, // Temporarily swap AP and TP
HALFGUARD = 0x72, // Halve damage from attacks that would inflict N or more damage HALFGUARD = 0x72, // Halve damage from attacks that would inflict N or more damage
UNKNOWN_73 = 0x73, UNKNOWN_73 = 0x73,
RAMPAGE_AP_LOSS = 0x74, // Temporarily reduce AP by N RAMPAGE_AP_LOSS = 0x74, // Temporarily reduce AP by N
UNKNOWN_75 = 0x75, UNKNOWN_75 = 0x75,
REFLECT = 0x76, // Generate reverse attack REFLECT = 0x76, // Generate reverse attack
UNKNOWN_77 = 0x77, UNKNOWN_77 = 0x77,
ANY = 0x78, // Not a real condition; used as a wildcard in search functions ANY = 0x78, // Not a real condition; used as a wildcard in search functions
UNKNOWN_79 = 0x79, UNKNOWN_79 = 0x79,
UNKNOWN_7A = 0x7A, UNKNOWN_7A = 0x7A,
UNKNOWN_7B = 0x7B, UNKNOWN_7B = 0x7B,
UNKNOWN_7C = 0x7C, UNKNOWN_7C = 0x7C,
UNKNOWN_7D = 0x7D, UNKNOWN_7D = 0x7D,
INVALID_FF = 0xFF, INVALID_FF = 0xFF,
ANY_FF = 0xFF, // Used as a wildcard in some search functions ANY_FF = 0xFF, // Used as a wildcard in some search functions
}; };
const char* name_for_condition_type(ConditionType cond_type); const char* name_for_condition_type(ConditionType cond_type);
enum class AssistEffect : uint16_t { enum class AssistEffect : uint16_t {
NONE = 0x0000, NONE = 0x0000,
DICE_HALF = 0x0001, DICE_HALF = 0x0001,
DICE_PLUS_1 = 0x0002, DICE_PLUS_1 = 0x0002,
DICE_FEVER = 0x0003, DICE_FEVER = 0x0003,
CARD_RETURN = 0x0004, CARD_RETURN = 0x0004,
LAND_PRICE = 0x0005, LAND_PRICE = 0x0005,
POWERLESS_RAIN = 0x0006, POWERLESS_RAIN = 0x0006,
BRAVE_WIND = 0x0007, BRAVE_WIND = 0x0007,
SILENT_COLOSSEUM = 0x0008, SILENT_COLOSSEUM = 0x0008,
RESISTANCE = 0x0009, RESISTANCE = 0x0009,
INDEPENDENT = 0x000A, INDEPENDENT = 0x000A,
ASSISTLESS = 0x000B, ASSISTLESS = 0x000B,
ATK_DICE_2 = 0x000C, ATK_DICE_2 = 0x000C,
DEFLATION = 0x000D, DEFLATION = 0x000D,
INFLATION = 0x000E, INFLATION = 0x000E,
EXCHANGE = 0x000F, EXCHANGE = 0x000F,
INFLUENCE = 0x0010, INFLUENCE = 0x0010,
SKIP_SET = 0x0011, SKIP_SET = 0x0011,
SKIP_MOVE = 0x0012, SKIP_MOVE = 0x0012,
SKIP_ACT = 0x0013, SKIP_ACT = 0x0013,
SKIP_DRAW = 0x0014, SKIP_DRAW = 0x0014,
FLY = 0x0015, FLY = 0x0015,
NECROMANCER = 0x0016, NECROMANCER = 0x0016,
PERMISSION = 0x0017, PERMISSION = 0x0017,
SHUFFLE_ALL = 0x0018, SHUFFLE_ALL = 0x0018,
LEGACY = 0x0019, LEGACY = 0x0019,
ASSIST_REVERSE = 0x001A, ASSIST_REVERSE = 0x001A,
STAMINA = 0x001B, STAMINA = 0x001B,
AP_ABSORPTION = 0x001C, AP_ABSORPTION = 0x001C,
HEAVY_FOG = 0x001D, HEAVY_FOG = 0x001D,
TRASH_1 = 0x001E, TRASH_1 = 0x001E,
EMPTY_HAND = 0x001F, EMPTY_HAND = 0x001F,
HITMAN = 0x0020, HITMAN = 0x0020,
ASSIST_TRASH = 0x0021, ASSIST_TRASH = 0x0021,
SHUFFLE_GROUP = 0x0022, SHUFFLE_GROUP = 0x0022,
ASSIST_VANISH = 0x0023, ASSIST_VANISH = 0x0023,
CHARITY = 0x0024, CHARITY = 0x0024,
INHERITANCE = 0x0025, INHERITANCE = 0x0025,
FIX = 0x0026, FIX = 0x0026,
MUSCULAR = 0x0027, MUSCULAR = 0x0027,
CHANGE_BODY = 0x0028, CHANGE_BODY = 0x0028,
GOD_WHIM = 0x0029, GOD_WHIM = 0x0029,
GOLD_RUSH = 0x002A, GOLD_RUSH = 0x002A,
ASSIST_RETURN = 0x002B, ASSIST_RETURN = 0x002B,
REQUIEM = 0x002C, REQUIEM = 0x002C,
RANSOM = 0x002D, RANSOM = 0x002D,
SIMPLE = 0x002E, SIMPLE = 0x002E,
SLOW_TIME = 0x002F, SLOW_TIME = 0x002F,
QUICK_TIME = 0x0030, QUICK_TIME = 0x0030,
TERRITORY = 0x0031, TERRITORY = 0x0031,
OLD_TYPE = 0x0032, OLD_TYPE = 0x0032,
FLATLAND = 0x0033, FLATLAND = 0x0033,
IMMORTALITY = 0x0034, IMMORTALITY = 0x0034,
SNAIL_PACE = 0x0035, SNAIL_PACE = 0x0035,
TECH_FIELD = 0x0036, TECH_FIELD = 0x0036,
FOREST_RAIN = 0x0037, FOREST_RAIN = 0x0037,
CAVE_WIND = 0x0038, CAVE_WIND = 0x0038,
MINE_BRIGHTNESS = 0x0039, MINE_BRIGHTNESS = 0x0039,
RUIN_DARKNESS = 0x003A, RUIN_DARKNESS = 0x003A,
SABER_DANCE = 0x003B, SABER_DANCE = 0x003B,
BULLET_STORM = 0x003C, BULLET_STORM = 0x003C,
CANE_PALACE = 0x003D, CANE_PALACE = 0x003D,
GIANT_GARDEN = 0x003E, GIANT_GARDEN = 0x003E,
MARCH_OF_THE_MEEK = 0x003F, MARCH_OF_THE_MEEK = 0x003F,
SUPPORT = 0x0040, SUPPORT = 0x0040,
RICH = 0x0041, RICH = 0x0041,
REVERSE_CARD = 0x0042, REVERSE_CARD = 0x0042,
VENGEANCE = 0x0043, VENGEANCE = 0x0043,
SQUEEZE = 0x0044, SQUEEZE = 0x0044,
HOMESICK = 0x0045, HOMESICK = 0x0045,
BOMB = 0x0046, BOMB = 0x0046,
SKIP_TURN = 0x0047, SKIP_TURN = 0x0047,
BATTLE_ROYALE = 0x0048, BATTLE_ROYALE = 0x0048,
DICE_FEVER_PLUS = 0x0049, DICE_FEVER_PLUS = 0x0049,
RICH_PLUS = 0x004A, RICH_PLUS = 0x004A,
CHARITY_PLUS = 0x004B, CHARITY_PLUS = 0x004B,
ANY = 0x004C, // Unused on cards; used in some search functions ANY = 0x004C, // Unused on cards; used in some search functions
}; };
enum class BattlePhase : uint8_t { enum class BattlePhase : uint8_t {
@@ -413,8 +407,6 @@ enum class RegistrationPhase : uint8_t {
INVALID_FF = 0xFF, INVALID_FF = 0xFF,
}; };
enum class Direction : uint8_t { enum class Direction : uint8_t {
RIGHT = 0, RIGHT = 0,
UP = 1, UP = 1,
@@ -711,8 +703,6 @@ struct StateFlags {
void clear_FF(); void clear_FF();
} __attribute__((packed)); } __attribute__((packed));
struct MapList { struct MapList {
be_uint32_t num_maps; be_uint32_t num_maps;
be_uint32_t unknown_a1; // Always 0? be_uint32_t unknown_a1; // Always 0?
@@ -875,10 +865,10 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
/* 59B0 */ parray<be_uint16_t, 0x10> reward_card_ids; /* 59B0 */ parray<be_uint16_t, 0x10> reward_card_ids;
/* 59D0 */be_uint32_t unknown_a9_a; /* 59D0 */ be_uint32_t unknown_a9_a;
/* 59D4 */be_uint32_t unknown_a9_b; /* 59D4 */ be_uint32_t unknown_a9_b;
/* 59D8 */be_uint16_t unknown_a9_c; /* 59D8 */ be_uint16_t unknown_a9_c;
/* 59DA */be_uint16_t unknown_a9_d; /* 59DA */ be_uint16_t unknown_a9_d;
/* 59DC */ uint8_t unknown_a10; /* 59DC */ uint8_t unknown_a10;
@@ -931,8 +921,6 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
std::string str(const DataIndex* data_index = nullptr) const; std::string str(const DataIndex* data_index = nullptr) const;
} __attribute__((packed)); } __attribute__((packed));
struct COMDeckDefinition { struct COMDeckDefinition {
size_t index; size_t index;
std::string player_name; std::string player_name;
@@ -940,8 +928,6 @@ struct COMDeckDefinition {
parray<le_uint16_t, 0x1F> card_ids; parray<le_uint16_t, 0x1F> card_ids;
}; };
class DataIndex { class DataIndex {
public: public:
DataIndex(const std::string& directory, uint32_t behavior_flags); DataIndex(const std::string& directory, uint32_t behavior_flags);
@@ -1004,6 +990,4 @@ private:
std::unordered_map<std::string, std::shared_ptr<COMDeckDefinition>> com_decks_by_name; std::unordered_map<std::string, std::shared_ptr<COMDeckDefinition>> com_decks_by_name;
}; };
} // namespace Episode3 } // namespace Episode3
+2 -12
View File
@@ -4,8 +4,6 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
NameEntry::NameEntry() { NameEntry::NameEntry() {
this->clear(); this->clear();
} }
@@ -17,8 +15,6 @@ void NameEntry::clear() {
this->unused = 0; this->unused = 0;
} }
DeckEntry::DeckEntry() { DeckEntry::DeckEntry() {
this->clear(); this->clear();
} }
@@ -32,8 +28,6 @@ void DeckEntry::clear() {
this->card_ids.clear(0xFFFF); this->card_ids.clear(0xFFFF);
} }
uint8_t index_for_card_ref(uint16_t card_ref) { uint8_t index_for_card_ref(uint16_t card_ref) {
return card_ref & 0xFF; return card_ref & 0xFF;
} }
@@ -42,8 +36,6 @@ uint8_t client_id_for_card_ref(uint16_t card_ref) {
return (card_ref >> 8) & 0xFF; return (card_ref >> 8) & 0xFF;
} }
uint8_t DeckState::num_drawable_cards() const { uint8_t DeckState::num_drawable_cards() const {
return this->card_refs.size() - this->draw_index; return this->card_refs.size() - this->draw_index;
} }
@@ -102,8 +94,8 @@ bool DeckState::draw_card_by_ref(uint16_t card_ref) {
this->entries[index].state = CardState::IN_HAND; this->entries[index].state = CardState::IN_HAND;
return true; return true;
// If the card is still drawable, we need to move it so it's just in front of // If the card is still drawable, we need to move it so it's just in front of
// the draw index, then immediately draw it // the draw index, then immediately draw it
} else if (this->entries[index].state == CardState::DRAWABLE) { } else if (this->entries[index].state == CardState::DRAWABLE) {
ssize_t ref_index; ssize_t ref_index;
for (ref_index = this->card_refs.size(); ref_index >= 0; ref_index--) { for (ref_index = this->card_refs.size(); ref_index >= 0; ref_index--) {
@@ -281,6 +273,4 @@ void DeckState::shuffle() {
} }
} }
} // namespace Episode3 } // namespace Episode3
+7 -11
View File
@@ -4,13 +4,11 @@
#include <memory> #include <memory>
#include "../Text.hh"
#include "../PSOEncryption.hh" #include "../PSOEncryption.hh"
#include "../Text.hh"
namespace Episode3 { namespace Episode3 {
struct NameEntry { struct NameEntry {
parray<char, 0x10> name; parray<char, 0x10> name;
uint8_t client_id; uint8_t client_id;
@@ -57,12 +55,12 @@ public:
uint8_t client_id, uint8_t client_id,
const parray<CardIDT, 0x1F>& card_ids, const parray<CardIDT, 0x1F>& card_ids,
std::shared_ptr<PSOV2Encryption> random_crypt) std::shared_ptr<PSOV2Encryption> random_crypt)
: client_id(client_id), : client_id(client_id),
draw_index(1), draw_index(1),
card_ref_base(this->client_id << 8), card_ref_base(this->client_id << 8),
shuffle_enabled(true), shuffle_enabled(true),
loop_enabled(true), loop_enabled(true),
random_crypt(random_crypt) { random_crypt(random_crypt) {
for (size_t z = 0; z < card_ids.size(); z++) { for (size_t z = 0; z < card_ids.size(); z++) {
auto& e = this->entries[z]; auto& e = this->entries[z];
e.card_id = card_ids[z]; e.card_id = card_ids[z];
@@ -112,6 +110,4 @@ private:
std::shared_ptr<PSOV2Encryption> random_crypt; std::shared_ptr<PSOV2Encryption> random_crypt;
}; };
} // namespace Episode3 } // namespace Episode3
-10
View File
@@ -4,8 +4,6 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
MapState::MapState() { MapState::MapState() {
this->clear(); this->clear();
} }
@@ -32,8 +30,6 @@ void MapState::print(FILE* stream) const {
} }
} }
MapAndRulesState::MapAndRulesState() { MapAndRulesState::MapAndRulesState() {
this->clear(); this->clear();
} }
@@ -54,8 +50,6 @@ void MapAndRulesState::clear() {
this->unused5 = 0; this->unused5 = 0;
} }
bool MapAndRulesState::loc_is_within_bounds(uint8_t x, uint8_t y) const { bool MapAndRulesState::loc_is_within_bounds(uint8_t x, uint8_t y) const {
return (x < this->map.width) && (y < this->map.height); return (x < this->map.width) && (y < this->map.height);
} }
@@ -75,8 +69,6 @@ void MapAndRulesState::clear_occupied_bit_for_tile(uint8_t x, uint8_t y) {
this->map.tiles[y][x] &= 0xEF; this->map.tiles[y][x] &= 0xEF;
} }
OverlayState::OverlayState() { OverlayState::OverlayState() {
this->clear(); this->clear();
} }
@@ -90,6 +82,4 @@ void OverlayState::clear() {
this->unused3.clear(0); this->unused3.clear(0);
} }
} // namespace Episode3 } // namespace Episode3
-4
View File
@@ -9,8 +9,6 @@
namespace Episode3 { namespace Episode3 {
struct MapState { struct MapState {
le_uint16_t width; le_uint16_t width;
le_uint16_t height; le_uint16_t height;
@@ -58,6 +56,4 @@ struct OverlayState {
void clear(); void clear();
} __attribute__((packed)); } __attribute__((packed));
} // namespace Episode3 } // namespace Episode3
+33 -36
View File
@@ -6,40 +6,38 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
PlayerState::PlayerState(uint8_t client_id, shared_ptr<Server> server) PlayerState::PlayerState(uint8_t client_id, shared_ptr<Server> server)
: w_server(server), : w_server(server),
client_id(client_id), client_id(client_id),
num_mulligans_allowed(1), num_mulligans_allowed(1),
sc_card_type(CardType::HUNTERS_SC), sc_card_type(CardType::HUNTERS_SC),
team_id(0xFF), team_id(0xFF),
atk_points(0), atk_points(0),
def_points(0), def_points(0),
atk_points2(0), atk_points2(0),
atk_points2_max(6), atk_points2_max(6),
atk_bonuses(0), atk_bonuses(0),
def_bonuses(0), def_bonuses(0),
dice_results(0), dice_results(0),
unknown_a4(2), unknown_a4(2),
dice_max(6), dice_max(6),
total_set_cards_cost(0), total_set_cards_cost(0),
sc_card_id(0xFFFF), sc_card_id(0xFFFF),
sc_card_ref(0xFFFF), sc_card_ref(0xFFFF),
card_refs(0xFFFF), card_refs(0xFFFF),
discard_log_card_refs(0xFFFF), discard_log_card_refs(0xFFFF),
discard_log_reasons(0), discard_log_reasons(0),
assist_remaining_turns(0), assist_remaining_turns(0),
assist_card_set_number(0), assist_card_set_number(0),
set_assist_card_id(0xFFFF), set_assist_card_id(0xFFFF),
god_whim_can_use_hidden_cards(false), god_whim_can_use_hidden_cards(false),
unknown_a14(0), unknown_a14(0),
assist_flags(0), assist_flags(0),
assist_delay_turns(0), assist_delay_turns(0),
start_facing_direction(Direction::RIGHT), start_facing_direction(Direction::RIGHT),
num_destroyed_fcs(0), num_destroyed_fcs(0),
unknown_a16(0), unknown_a16(0),
unknown_a17(0) { } unknown_a17(0) {}
void PlayerState::init() { void PlayerState::init() {
if (this->server()->player_states[this->client_id].get() != this) { if (this->server()->player_states[this->client_id].get() != this) {
@@ -1411,7 +1409,8 @@ void PlayerState::update_hand_and_equip_state_and_send_6xB4x02_if_needed(
cmd.state.sc_card_ref = this->sc_card_ref; cmd.state.sc_card_ref = this->sc_card_ref;
cmd.state.assist_card_ref2 = this->card_refs[6]; cmd.state.assist_card_ref2 = this->card_refs[6];
cmd.state.assist_card_set_number = (this->card_refs[6] == 0xFFFF) cmd.state.assist_card_set_number = (this->card_refs[6] == 0xFFFF)
? 0 : this->assist_card_set_number; ? 0
: this->assist_card_set_number;
cmd.state.assist_card_id = this->set_assist_card_id; cmd.state.assist_card_id = this->set_assist_card_id;
cmd.state.assist_remaining_turns = this->assist_remaining_turns; cmd.state.assist_remaining_turns = this->assist_remaining_turns;
cmd.state.assist_delay_turns = this->assist_delay_turns; cmd.state.assist_delay_turns = this->assist_delay_turns;
@@ -1830,6 +1829,4 @@ void PlayerState::compute_team_dice_boost_after_draw_phase() {
this->update_hand_and_equip_state_and_send_6xB4x02_if_needed(); this->update_hand_and_equip_state_and_send_6xB4x02_if_needed();
} }
} // namespace Episode3 } // namespace Episode3
+2 -7
View File
@@ -5,15 +5,13 @@
#include <memory> #include <memory>
#include "../Text.hh" #include "../Text.hh"
#include "DataIndex.hh"
#include "Card.hh" #include "Card.hh"
#include "DataIndex.hh"
#include "DeckState.hh" #include "DeckState.hh"
#include "PlayerStateSubordinates.hh" #include "PlayerStateSubordinates.hh"
namespace Episode3 { namespace Episode3 {
class ServerBase; class ServerBase;
class Server; class Server;
@@ -25,8 +23,7 @@ public:
std::shared_ptr<const Server> server() const; std::shared_ptr<const Server> server() const;
bool draw_cards_allowed() const; bool draw_cards_allowed() const;
void apply_assist_card_effect_on_set( void apply_assist_card_effect_on_set(std::shared_ptr<PlayerState> setter_ps);
std::shared_ptr<PlayerState> setter_ps);
void apply_dice_effects(); void apply_dice_effects();
uint16_t card_ref_for_hand_index(size_t hand_index) const; uint16_t card_ref_for_hand_index(size_t hand_index) const;
int16_t compute_attack_or_defense_atk_costs(const ActionState& pa) const; int16_t compute_attack_or_defense_atk_costs(const ActionState& pa) const;
@@ -188,6 +185,4 @@ public:
PlayerStats stats; PlayerStats stats;
}; };
} // namespace Episode3 } // namespace Episode3
+73 -103
View File
@@ -6,8 +6,6 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
template <size_t Count> template <size_t Count>
std::string string_for_refs(const parray<le_uint16_t, Count>& card_refs) { std::string string_for_refs(const parray<le_uint16_t, Count>& card_refs) {
string ret = "["; string ret = "[";
@@ -24,26 +22,24 @@ std::string string_for_refs(const parray<le_uint16_t, Count>& card_refs) {
return ret; return ret;
} }
Condition::Condition() { Condition::Condition() {
this->clear(); this->clear();
} }
bool Condition::operator==(const Condition& other) const { bool Condition::operator==(const Condition& other) const {
return (this->type == other.type) && return (this->type == other.type) &&
(this->remaining_turns == other.remaining_turns) && (this->remaining_turns == other.remaining_turns) &&
(this->a_arg_value == other.a_arg_value) && (this->a_arg_value == other.a_arg_value) &&
(this->dice_roll_value == other.dice_roll_value) && (this->dice_roll_value == other.dice_roll_value) &&
(this->flags == other.flags) && (this->flags == other.flags) &&
(this->card_definition_effect_index == other.card_definition_effect_index) && (this->card_definition_effect_index == other.card_definition_effect_index) &&
(this->card_ref == other.card_ref) && (this->card_ref == other.card_ref) &&
(this->value == other.value) && (this->value == other.value) &&
(this->condition_giver_card_ref == other.condition_giver_card_ref) && (this->condition_giver_card_ref == other.condition_giver_card_ref) &&
(this->random_percent == other.random_percent) && (this->random_percent == other.random_percent) &&
(this->value8 == other.value8) && (this->value8 == other.value8) &&
(this->order == other.order) && (this->order == other.order) &&
(this->unknown_a8 == other.unknown_a8); (this->unknown_a8 == other.unknown_a8);
} }
bool Condition::operator!=(const Condition& other) const { bool Condition::operator!=(const Condition& other) const {
return !this->operator==(other); return !this->operator==(other);
@@ -84,8 +80,8 @@ void Condition::clear_FF() {
std::string Condition::str() const { std::string Condition::str() const {
return string_printf( return string_printf(
"Condition[type=%s, turns=%hhu, a_arg=%hhd, dice=%hhu, flags=%02hhX, " "Condition[type=%s, turns=%hhu, a_arg=%hhd, dice=%hhu, flags=%02hhX, "
"def_eff_index=%hhu, ref=@%04hX, value=%hd, giver_ref=@%04hX " "def_eff_index=%hhu, ref=@%04hX, value=%hd, giver_ref=@%04hX "
"percent=%hhu value8=%hd order=%hu a8=%hu]", "percent=%hhu value8=%hd order=%hu a8=%hu]",
name_for_condition_type(this->type), name_for_condition_type(this->type),
this->remaining_turns, this->remaining_turns,
this->a_arg_value, this->a_arg_value,
@@ -101,8 +97,6 @@ std::string Condition::str() const {
this->unknown_a8); this->unknown_a8);
} }
EffectResult::EffectResult() { EffectResult::EffectResult() {
this->clear(); this->clear();
} }
@@ -123,8 +117,8 @@ void EffectResult::clear() {
std::string EffectResult::str() const { std::string EffectResult::str() const {
return string_printf( return string_printf(
"EffectResult[att_ref=@%04hX, target_ref=@%04hX, value=%hhd, " "EffectResult[att_ref=@%04hX, target_ref=@%04hX, value=%hhd, "
"cur_hp=%hhd, ap=%hhd, tp=%hhd, flags=%02hhX, op=%hhd, " "cur_hp=%hhd, ap=%hhd, tp=%hhd, flags=%02hhX, op=%hhd, "
"cond_index=%hhu, dice=%hhu]", "cond_index=%hhu, dice=%hhu]",
this->attacker_card_ref.load(), this->attacker_card_ref.load(),
this->target_card_ref.load(), this->target_card_ref.load(),
this->value, this->value,
@@ -137,20 +131,18 @@ std::string EffectResult::str() const {
this->dice_roll_value); this->dice_roll_value);
} }
CardShortStatus::CardShortStatus() { CardShortStatus::CardShortStatus() {
this->clear(); this->clear();
} }
bool CardShortStatus::operator==(const CardShortStatus& other) const { bool CardShortStatus::operator==(const CardShortStatus& other) const {
return (this->card_ref == other.card_ref) && return (this->card_ref == other.card_ref) &&
(this->current_hp == other.current_hp) && (this->current_hp == other.current_hp) &&
(this->card_flags == other.card_flags) && (this->card_flags == other.card_flags) &&
(this->loc == other.loc) && (this->loc == other.loc) &&
(this->unused1 == other.unused1) && (this->unused1 == other.unused1) &&
(this->max_hp == other.max_hp) && (this->max_hp == other.max_hp) &&
(this->unused2 == other.unused2); (this->unused2 == other.unused2);
} }
bool CardShortStatus::operator!=(const CardShortStatus& other) const { bool CardShortStatus::operator!=(const CardShortStatus& other) const {
return !this->operator==(other); return !this->operator==(other);
@@ -160,7 +152,7 @@ std::string CardShortStatus::str() const {
string loc_s = this->loc.str(); string loc_s = this->loc.str();
return string_printf( return string_printf(
"CardShortStatus[ref=@%04hX, cur_hp=%hd, flags=%08" PRIX32 ", loc=%s, " "CardShortStatus[ref=@%04hX, cur_hp=%hd, flags=%08" PRIX32 ", loc=%s, "
"u1=%04hX, max_hp=%hhd, u2=%hhu]", "u1=%04hX, max_hp=%hhd, u2=%hhu]",
this->card_ref.load(), this->card_ref.load(),
this->current_hp.load(), this->current_hp.load(),
this->card_flags.load(), this->card_flags.load(),
@@ -190,8 +182,6 @@ void CardShortStatus::clear_FF() {
this->unused2 = 0xFF; this->unused2 = 0xFF;
} }
ActionState::ActionState() { ActionState::ActionState() {
this->clear(); this->clear();
} }
@@ -212,8 +202,8 @@ std::string ActionState::str() const {
string action_refs_s = string_for_refs(this->action_card_refs); string action_refs_s = string_for_refs(this->action_card_refs);
return string_printf( return string_printf(
"ActionState[client=%hu, u=%hhu, facing=%s, attacker_ref=@%04hX, " "ActionState[client=%hu, u=%hhu, facing=%s, attacker_ref=@%04hX, "
"def_ref=@%04hX, target_refs=%s, action_refs=%s, " "def_ref=@%04hX, target_refs=%s, action_refs=%s, "
"orig_attacker_ref=@%04hX]", "orig_attacker_ref=@%04hX]",
this->client_id.load(), this->client_id.load(),
this->unused, this->unused,
name_for_direction(this->facing_direction), name_for_direction(this->facing_direction),
@@ -224,34 +214,32 @@ std::string ActionState::str() const {
this->original_attacker_card_ref.load()); this->original_attacker_card_ref.load());
} }
ActionChain::ActionChain() { ActionChain::ActionChain() {
this->clear(); this->clear();
} }
bool ActionChain::operator==(const ActionChain& other) const { bool ActionChain::operator==(const ActionChain& other) const {
return (this->effective_ap == other.effective_ap) && return (this->effective_ap == other.effective_ap) &&
(this->effective_tp == other.effective_tp) && (this->effective_tp == other.effective_tp) &&
(this->ap_effect_bonus == other.ap_effect_bonus) && (this->ap_effect_bonus == other.ap_effect_bonus) &&
(this->damage == other.damage) && (this->damage == other.damage) &&
(this->acting_card_ref == other.acting_card_ref) && (this->acting_card_ref == other.acting_card_ref) &&
(this->unknown_card_ref_a3 == other.unknown_card_ref_a3) && (this->unknown_card_ref_a3 == other.unknown_card_ref_a3) &&
(this->attack_action_card_refs == other.attack_action_card_refs) && (this->attack_action_card_refs == other.attack_action_card_refs) &&
(this->attack_action_card_ref_count == other.attack_action_card_ref_count) && (this->attack_action_card_ref_count == other.attack_action_card_ref_count) &&
(this->attack_medium == other.attack_medium) && (this->attack_medium == other.attack_medium) &&
(this->target_card_ref_count == other.target_card_ref_count) && (this->target_card_ref_count == other.target_card_ref_count) &&
(this->action_subphase == other.action_subphase) && (this->action_subphase == other.action_subphase) &&
(this->strike_count == other.strike_count) && (this->strike_count == other.strike_count) &&
(this->damage_multiplier == other.damage_multiplier) && (this->damage_multiplier == other.damage_multiplier) &&
(this->attack_number == other.attack_number) && (this->attack_number == other.attack_number) &&
(this->tp_effect_bonus == other.tp_effect_bonus) && (this->tp_effect_bonus == other.tp_effect_bonus) &&
(this->unused1 == other.unused1) && (this->unused1 == other.unused1) &&
(this->unused2 == other.unused2) && (this->unused2 == other.unused2) &&
(this->card_ap == other.card_ap) && (this->card_ap == other.card_ap) &&
(this->card_tp == other.card_tp) && (this->card_tp == other.card_tp) &&
(this->flags == other.flags) && (this->flags == other.flags) &&
(this->target_card_refs == other.target_card_refs); (this->target_card_refs == other.target_card_refs);
} }
bool ActionChain::operator!=(const ActionChain& other) const { bool ActionChain::operator!=(const ActionChain& other) const {
return !this->operator==(other); return !this->operator==(other);
@@ -262,12 +250,12 @@ std::string ActionChain::str() const {
string target_card_refs_s = string_for_refs(this->target_card_refs); string target_card_refs_s = string_for_refs(this->target_card_refs);
return string_printf( return string_printf(
"ActionChain[eff_ap=%hhd, eff_tp=%hhd, ap_bonus=%hhd, damage=%hhd, " "ActionChain[eff_ap=%hhd, eff_tp=%hhd, ap_bonus=%hhd, damage=%hhd, "
"acting_ref=@%04hX, unknown_ref_a3=@%04hX, " "acting_ref=@%04hX, unknown_ref_a3=@%04hX, "
"attack_action_refs=%s, attack_action_ref_count=%hhu, " "attack_action_refs=%s, attack_action_ref_count=%hhu, "
"medium=%s, target_ref_count=%hhu, subphase=%s, " "medium=%s, target_ref_count=%hhu, subphase=%s, "
"strikes=%hhu, damage_mult=%hhd, attack_num=%hhu, " "strikes=%hhu, damage_mult=%hhd, attack_num=%hhu, "
"tp_bonus=%hhd, u1=%hhu, u2=%hhu, card_ap=%hhd, " "tp_bonus=%hhd, u1=%hhu, u2=%hhu, card_ap=%hhd, "
"card_tp=%hhd, flags=%08" PRIX32 ", target_refs=%s]", "card_tp=%hhd, flags=%08" PRIX32 ", target_refs=%s]",
this->effective_ap, this->effective_ap,
this->effective_tp, this->effective_tp,
this->ap_effect_bonus, this->ap_effect_bonus,
@@ -339,8 +327,6 @@ void ActionChain::clear_FF() {
this->target_card_refs.clear(0xFFFF); this->target_card_refs.clear(0xFFFF);
} }
ActionChainWithConds::ActionChainWithConds() { ActionChainWithConds::ActionChainWithConds() {
this->clear(); this->clear();
} }
@@ -495,24 +481,22 @@ bool ActionChainWithConds::unknown_8024DEC4() const {
return this->check_flag(4) ? false : (this->chain.target_card_ref_count != 0); return this->check_flag(4) ? false : (this->chain.target_card_ref_count != 0);
} }
ActionMetadata::ActionMetadata() { ActionMetadata::ActionMetadata() {
this->clear(); this->clear();
} }
bool ActionMetadata::operator==(const ActionMetadata& other) const { bool ActionMetadata::operator==(const ActionMetadata& other) const {
return (this->card_ref == other.card_ref) && return (this->card_ref == other.card_ref) &&
(this->target_card_ref_count == other.target_card_ref_count) && (this->target_card_ref_count == other.target_card_ref_count) &&
(this->defense_card_ref_count == other.defense_card_ref_count) && (this->defense_card_ref_count == other.defense_card_ref_count) &&
(this->action_subphase == other.action_subphase) && (this->action_subphase == other.action_subphase) &&
(this->defense_power == other.defense_power) && (this->defense_power == other.defense_power) &&
(this->defense_bonus == other.defense_bonus) && (this->defense_bonus == other.defense_bonus) &&
(this->attack_bonus == other.attack_bonus) && (this->attack_bonus == other.attack_bonus) &&
(this->flags == other.flags) && (this->flags == other.flags) &&
(this->target_card_refs == other.target_card_refs) && (this->target_card_refs == other.target_card_refs) &&
(this->defense_card_refs == other.defense_card_refs) && (this->defense_card_refs == other.defense_card_refs) &&
(this->original_attacker_card_refs == other.original_attacker_card_refs); (this->original_attacker_card_refs == other.original_attacker_card_refs);
} }
bool ActionMetadata::operator!=(const ActionMetadata& other) const { bool ActionMetadata::operator!=(const ActionMetadata& other) const {
return !this->operator==(other); return !this->operator==(other);
@@ -524,9 +508,9 @@ std::string ActionMetadata::str() const {
string original_attacker_card_refs_s = string_for_refs(this->original_attacker_card_refs); string original_attacker_card_refs_s = string_for_refs(this->original_attacker_card_refs);
return string_printf( return string_printf(
"ActionMetadata[ref=@%04hX, target_ref_count=%hhu, def_ref_count=%hhu, " "ActionMetadata[ref=@%04hX, target_ref_count=%hhu, def_ref_count=%hhu, "
"subphase=%s, def_power=%hhd, def_bonus=%hhd, " "subphase=%s, def_power=%hhd, def_bonus=%hhd, "
"att_bonus=%hhd, flags=%08" PRIX32 ", target_refs=%s, " "att_bonus=%hhd, flags=%08" PRIX32 ", target_refs=%s, "
"defense_refs=%s, original_attacker_refs=%s]", "defense_refs=%s, original_attacker_refs=%s]",
this->card_ref.load(), this->card_ref.load(),
this->target_card_ref_count, this->target_card_ref_count,
this->defense_card_ref_count, this->defense_card_ref_count,
@@ -610,8 +594,6 @@ void ActionMetadata::add_defense_card_ref(
} }
} }
HandAndEquipState::HandAndEquipState() { HandAndEquipState::HandAndEquipState() {
this->clear(); this->clear();
} }
@@ -623,13 +605,13 @@ std::string HandAndEquipState::str() const {
string set_card_refs2_s = string_for_refs(this->set_card_refs2); string set_card_refs2_s = string_for_refs(this->set_card_refs2);
return string_printf( return string_printf(
"HandAndEquipState[dice=[%hhu, %hhu], atk=%hhu, def=%hhu, atk2=%hhu, " "HandAndEquipState[dice=[%hhu, %hhu], atk=%hhu, def=%hhu, atk2=%hhu, "
"a1=%hhu, total_set_cost=%hhu, is_cpu=%hhu, " "a1=%hhu, total_set_cost=%hhu, is_cpu=%hhu, "
"assist_flags=%08" PRIX32 ", hand_refs=%s, " "assist_flags=%08" PRIX32 ", hand_refs=%s, "
"assist_ref=@%04hX, set_refs=%s, sc_ref=@%04hX, " "assist_ref=@%04hX, set_refs=%s, sc_ref=@%04hX, "
"hand_refs2=%s, set_refs2=%s, assist_ref2=@%04hX, " "hand_refs2=%s, set_refs2=%s, assist_ref2=@%04hX, "
"assist_set_num=%hu, assist_card_id=%04hX, " "assist_set_num=%hu, assist_card_id=%04hX, "
"assist_turns=%hhu, assit_dely=%hhu, atk_bonus=%hhu, " "assist_turns=%hhu, assit_dely=%hhu, atk_bonus=%hhu, "
"def_bonus=%hhu, u2=[%hhu, %hhu]]", "def_bonus=%hhu, u2=[%hhu, %hhu]]",
this->dice_results[0], this->dice_results[0],
this->dice_results[1], this->dice_results[1],
this->atk_points, this->atk_points,
@@ -706,8 +688,6 @@ void HandAndEquipState::clear_FF() {
this->unused2.clear(0xFF); this->unused2.clear(0xFF);
} }
PlayerStats::PlayerStats() { PlayerStats::PlayerStats() {
this->clear(); this->clear();
} }
@@ -747,12 +727,7 @@ float PlayerStats::score(size_t num_rounds) const {
// Ep3 PsoV3.dol, so it's presumably correct. Is the PSO-World formula simply // Ep3 PsoV3.dol, so it's presumably correct. Is the PSO-World formula simply
// incorrect, or is it from e.g. the Japanese version, which may have a // incorrect, or is it from e.g. the Japanese version, which may have a
// different rank calculation function? // different rank calculation function?
return 38.0f return 38.0f + 0.8f * this->action_card_negated_damage - 2.3f * num_rounds - 1.8f * this->sc_damage_taken + 3.0f * this->max_attack_combo_size + (this->damage_given - this->damage_taken);
+ 0.8f * this->action_card_negated_damage
- 2.3f * num_rounds
- 1.8f * this->sc_damage_taken
+ 3.0f * this->max_attack_combo_size
+ (this->damage_given - this->damage_taken);
} }
uint8_t PlayerStats::rank(size_t num_rounds) const { uint8_t PlayerStats::rank(size_t num_rounds) const {
@@ -767,7 +742,7 @@ constexpr size_t RANK_THRESHOLD_COUNT = 9;
static const float RANK_THRESHOLDS[RANK_THRESHOLD_COUNT] = { static const float RANK_THRESHOLDS[RANK_THRESHOLD_COUNT] = {
15.0f, 25.0f, 30.0f, 40.0f, 50.0f, 60.0f, 65.0f, 75.0f, 85.0f}; 15.0f, 25.0f, 30.0f, 40.0f, 50.0f, 60.0f, 65.0f, 75.0f, 85.0f};
static const char* RANK_NAMES[RANK_THRESHOLD_COUNT + 1] = { static const char* RANK_NAMES[RANK_THRESHOLD_COUNT + 1] = {
"E", "D", "D+", "C", "C+", "B", "B+", "A", "A+", "S"}; "E", "D", "D+", "C", "C+", "B", "B+", "A", "A+", "S"};
uint8_t PlayerStats::rank_for_score(float score) { uint8_t PlayerStats::rank_for_score(float score) {
size_t rank = 0; size_t rank = 0;
@@ -784,9 +759,6 @@ const char* PlayerStats::name_for_rank(uint8_t rank) {
return RANK_NAMES[rank]; return RANK_NAMES[rank];
} }
bool is_card_within_range( bool is_card_within_range(
const parray<uint8_t, 9 * 9>& range, const parray<uint8_t, 9 * 9>& range,
const Location& anchor_loc, const Location& anchor_loc,
@@ -824,6 +796,4 @@ vector<uint16_t> get_card_refs_within_range(
return ret; return ret;
} }
} // namespace Episode3 } // namespace Episode3
-6
View File
@@ -9,8 +9,6 @@
namespace Episode3 { namespace Episode3 {
class ServerBase; class ServerBase;
class Server; class Server;
class Card; class Card;
@@ -269,13 +267,9 @@ struct PlayerStats {
static const char* name_for_rank(uint8_t rank); static const char* name_for_rank(uint8_t rank);
} __attribute__((packed)); } __attribute__((packed));
std::vector<uint16_t> get_card_refs_within_range( std::vector<uint16_t> get_card_refs_within_range(
const parray<uint8_t, 9 * 9>& range, const parray<uint8_t, 9 * 9>& range,
const Location& loc, const Location& loc,
const parray<CardShortStatus, 0x10>& short_statuses); const parray<CardShortStatus, 0x10>& short_statuses);
} // namespace Episode3 } // namespace Episode3
+223 -228
View File
@@ -6,8 +6,6 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
void compute_effective_range( void compute_effective_range(
parray<uint8_t, 9 * 9>& ret, parray<uint8_t, 9 * 9>& ret,
shared_ptr<const DataIndex> data_index, shared_ptr<const DataIndex> data_index,
@@ -189,14 +187,12 @@ bool card_linkage_is_valid(
return false; return false;
} }
RulerServer::RulerServer(shared_ptr<Server> server) RulerServer::RulerServer(shared_ptr<Server> server)
: w_server(server), : w_server(server),
team_id_for_client_id(0xFF), team_id_for_client_id(0xFF),
error_code1(0), error_code1(0),
error_code2(0), error_code2(0),
error_code3(0) { } error_code3(0) {}
shared_ptr<Server> RulerServer::server() { shared_ptr<Server> RulerServer::server() {
auto s = this->w_server.lock(); auto s = this->w_server.lock();
@@ -318,11 +314,11 @@ bool RulerServer::attack_action_has_rampage_and_not_pierce(
auto attack_medium = this->get_attack_medium(pa); auto attack_medium = this->get_attack_medium(pa);
if (!this->compute_effective_range_and_target_mode_for_attack( if (!this->compute_effective_range_and_target_mode_for_attack(
pa, &effective_range_card_id, &effective_target_mode, &orig_card_ref)) { pa, &effective_range_card_id, &effective_target_mode, &orig_card_ref)) {
return false; return false;
} }
if ((orig_card_ref != 0xFFFF) && (orig_card_ref != pa.attacker_card_ref) && if ((orig_card_ref != 0xFFFF) && (orig_card_ref != pa.attacker_card_ref) &&
!this->check_usability_or_apply_condition_for_card_refs( !this->check_usability_or_apply_condition_for_card_refs(
orig_card_ref, pa.attacker_card_ref, card_ref, 0xFF, AttackMedium::INVALID_FF)) { orig_card_ref, pa.attacker_card_ref, card_ref, 0xFF, AttackMedium::INVALID_FF)) {
return false; return false;
@@ -426,9 +422,9 @@ bool RulerServer::attack_action_has_pierce_and_not_rampage(
for (; last_cond_index >= 0; last_cond_index--) { for (; last_cond_index >= 0; last_cond_index--) {
bool has_rampage = false; bool has_rampage = false;
if (this->card_has_pierce_or_rampage( if (this->card_has_pierce_or_rampage(
client_id, ce->def.effects[last_cond_index].type, &has_rampage, client_id, ce->def.effects[last_cond_index].type, &has_rampage,
pa.attacker_card_ref, pa.action_card_refs[last_action_card_index], pa.attacker_card_ref, pa.action_card_refs[last_action_card_index],
last_cond_index, attack_medium)) { last_cond_index, attack_medium)) {
return true; return true;
} }
if (has_rampage) { if (has_rampage) {
@@ -442,10 +438,10 @@ bool RulerServer::attack_action_has_pierce_and_not_rampage(
for (ssize_t cond_index = 8; cond_index >= 0; cond_index--) { for (ssize_t cond_index = 8; cond_index >= 0; cond_index--) {
bool has_rampage = false; bool has_rampage = false;
if (this->card_has_pierce_or_rampage( if (this->card_has_pierce_or_rampage(
client_id, chain->conditions[cond_index].type, &has_rampage, client_id, chain->conditions[cond_index].type, &has_rampage,
pa.attacker_card_ref, chain->conditions[cond_index].card_ref, pa.attacker_card_ref, chain->conditions[cond_index].card_ref,
chain->conditions[cond_index].card_definition_effect_index, chain->conditions[cond_index].card_definition_effect_index,
attack_medium)) { attack_medium)) {
return true; return true;
} }
if (has_rampage) { if (has_rampage) {
@@ -457,7 +453,6 @@ bool RulerServer::attack_action_has_pierce_and_not_rampage(
return false; return false;
} }
bool RulerServer::card_exists_by_status(const CardShortStatus& stat) const { bool RulerServer::card_exists_by_status(const CardShortStatus& stat) const {
if ((stat.card_flags & 3) || (stat.card_ref == 0xFFFF)) { if ((stat.card_flags & 3) || (stat.card_ref == 0xFFFF)) {
return false; return false;
@@ -494,10 +489,10 @@ bool RulerServer::card_id_is_boss_sc(uint16_t card_id) {
bool RulerServer::card_id_is_support_tech_or_support_pb(uint16_t card_id) { bool RulerServer::card_id_is_support_tech_or_support_pb(uint16_t card_id) {
return (card_id == 0x00E1) || return (card_id == 0x00E1) ||
(card_id == 0x00E2) || (card_id == 0x00E2) ||
(card_id == 0x00E6) || (card_id == 0x00E6) ||
(card_id == 0x00EB) || (card_id == 0x00EB) ||
(card_id == 0x00EC); (card_id == 0x00EC);
} }
bool RulerServer::card_ref_can_attack(uint16_t card_ref) { bool RulerServer::card_ref_can_attack(uint16_t card_ref) {
@@ -537,11 +532,11 @@ bool RulerServer::card_ref_can_attack(uint16_t card_ref) {
// then the item also cannot attack // then the item also cannot attack
if ((ce->def.type == CardType::ITEM) && if ((ce->def.type == CardType::ITEM) &&
(!this->short_statuses[client_id] || (!this->short_statuses[client_id] ||
(this->short_statuses[client_id]->at(0).card_ref == 0xFFFF) || (this->short_statuses[client_id]->at(0).card_ref == 0xFFFF) ||
this->find_condition_on_card_ref(this->short_statuses[client_id]->at(0).card_ref, ConditionType::HOLD) || this->find_condition_on_card_ref(this->short_statuses[client_id]->at(0).card_ref, ConditionType::HOLD) ||
this->find_condition_on_card_ref(this->short_statuses[client_id]->at(0).card_ref, ConditionType::GUOM) || this->find_condition_on_card_ref(this->short_statuses[client_id]->at(0).card_ref, ConditionType::GUOM) ||
this->find_condition_on_card_ref(this->short_statuses[client_id]->at(0).card_ref, ConditionType::PARALYZE) || this->find_condition_on_card_ref(this->short_statuses[client_id]->at(0).card_ref, ConditionType::PARALYZE) ||
this->find_condition_on_card_ref(this->short_statuses[client_id]->at(0).card_ref, ConditionType::FREEZE))) { this->find_condition_on_card_ref(this->short_statuses[client_id]->at(0).card_ref, ConditionType::FREEZE))) {
return false; return false;
} }
@@ -589,7 +584,7 @@ bool RulerServer::card_ref_can_move(
const auto& item_stat = short_statuses->at(z); const auto& item_stat = short_statuses->at(z);
if ((item_stat.card_ref != 0xFFFF) && this->card_exists_by_status(item_stat) && if ((item_stat.card_ref != 0xFFFF) && this->card_exists_by_status(item_stat) &&
(this->find_condition_on_card_ref(item_stat.card_ref, ConditionType::GUOM) || (this->find_condition_on_card_ref(item_stat.card_ref, ConditionType::GUOM) ||
this->find_condition_on_card_ref(item_stat.card_ref, ConditionType::IMMOBILE))) { this->find_condition_on_card_ref(item_stat.card_ref, ConditionType::IMMOBILE))) {
return false; return false;
} }
} }
@@ -951,11 +946,11 @@ bool RulerServer::check_usability_or_condition_apply(
// positioning/team setup // positioning/team setup
if (is_item_usability_check && if (is_item_usability_check &&
((criterion_code == CriterionCode::SAME_TEAM) || ((criterion_code == CriterionCode::SAME_TEAM) ||
(criterion_code == CriterionCode::SAME_PLAYER) || (criterion_code == CriterionCode::SAME_PLAYER) ||
(criterion_code == CriterionCode::SAME_TEAM_NOT_SAME_PLAYER) || (criterion_code == CriterionCode::SAME_TEAM_NOT_SAME_PLAYER) ||
(criterion_code == CriterionCode::UNKNOWN_07) || (criterion_code == CriterionCode::UNKNOWN_07) ||
(criterion_code == CriterionCode::NOT_SC) || (criterion_code == CriterionCode::NOT_SC) ||
(criterion_code == CriterionCode::SC))) { (criterion_code == CriterionCode::SC))) {
criterion_code = CriterionCode::NONE; criterion_code = CriterionCode::NONE;
} }
@@ -988,7 +983,7 @@ bool RulerServer::check_usability_or_condition_apply(
case CriterionCode::SAME_TEAM: case CriterionCode::SAME_TEAM:
if ((client_id1 == client_id2) || if ((client_id1 == client_id2) ||
((client_id1 != 0xFF) && (client_id2 != 0xFF) && ((client_id1 != 0xFF) && (client_id2 != 0xFF) &&
(this->team_id_for_client_id[client_id1] == this->team_id_for_client_id[client_id2]))) { (this->team_id_for_client_id[client_id1] == this->team_id_for_client_id[client_id2]))) {
return true; return true;
} }
break; break;
@@ -1026,255 +1021,255 @@ bool RulerServer::check_usability_or_condition_apply(
break; break;
case CriterionCode::HUNTER_HUMAN_SC: { case CriterionCode::HUNTER_HUMAN_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0001, // Orland 0x0001, // Orland
0x0002, // Kranz 0x0002, // Kranz
0x0003, // Ino'lis 0x0003, // Ino'lis
0x0004, // Sil'fer 0x0004, // Sil'fer
0x0006, // Kylria 0x0006, // Kylria
0x0111, // Relmitos 0x0111, // Relmitos
0x0112, // Viviana 0x0112, // Viviana
0x0115, // Glustar 0x0115, // Glustar
0x02AA, // H-HUmar 0x02AA, // H-HUmar
0x02AB, // H-HUnewearl 0x02AB, // H-HUnewearl
0x02AE, // H-RAmar 0x02AE, // H-RAmar
0x02AF, // H-RAmarl 0x02AF, // H-RAmarl
0x02B2, // H-FOmar 0x02B2, // H-FOmar
0x02B3, // H-FOmarl 0x02B3, // H-FOmarl
0x02B4, // H-FOnewm 0x02B4, // H-FOnewm
0x02B5, // H-FOnewearl 0x02B5, // H-FOnewearl
0x02CC, // H-HUmar 0x02CC, // H-HUmar
0x02CD, // H-RAmarl 0x02CD, // H-RAmarl
0x02CE, // H-FOmarl 0x02CE, // H-FOmarl
0x02CF, // H-HUnewearl 0x02CF, // H-HUnewearl
0x02D1, // H-RAmarl 0x02D1, // H-RAmarl
0x02D5, // H-FOmar 0x02D5, // H-FOmar
0x02D6, // H-FOnewearl 0x02D6, // H-FOnewearl
0x02D9, // H-FOnewm 0x02D9, // H-FOnewm
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_HU_CLASS_MALE_SC: { case CriterionCode::HUNTER_HU_CLASS_MALE_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0001, // Orland 0x0001, // Orland
0x0113, // Teifu 0x0113, // Teifu
0x02AA, // H-HUmar 0x02AA, // H-HUmar
0x02AC, // H-HUcast 0x02AC, // H-HUcast
0x02CC, // H-HUmar 0x02CC, // H-HUmar
0x02D7, // H-HUcast 0x02D7, // H-HUcast
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_FEMALE_SC: { case CriterionCode::HUNTER_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0003, // Ino'lis 0x0003, // Ino'lis
0x0004, // Sil'fer 0x0004, // Sil'fer
0x0006, // Kylria 0x0006, // Kylria
0x0110, // Saligun 0x0110, // Saligun
0x0112, // Viviana 0x0112, // Viviana
0x0114, // Stella 0x0114, // Stella
0x02AB, // H-HUnewearl 0x02AB, // H-HUnewearl
0x02AD, // H-HUcaseal 0x02AD, // H-HUcaseal
0x02AF, // H-RAmarl 0x02AF, // H-RAmarl
0x02B1, // H-RAcaseal 0x02B1, // H-RAcaseal
0x02B3, // H-FOmarl 0x02B3, // H-FOmarl
0x02B5, // H-FOnewearl 0x02B5, // H-FOnewearl
0x02CE, // H-FOmarl 0x02CE, // H-FOmarl
0x02CF, // H-HUnewearl 0x02CF, // H-HUnewearl
0x02D1, // H-RAmarl 0x02D1, // H-RAmarl
0x02D4, // H-HUcaseal 0x02D4, // H-HUcaseal
0x02D6, // H-FOnewearl 0x02D6, // H-FOnewearl
0x02D8, // H-RAcaseal 0x02D8, // H-RAcaseal
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_HU_OR_FO_CLASS_HUMAN_SC: { case CriterionCode::HUNTER_HU_OR_FO_CLASS_HUMAN_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0001, // Orland 0x0001, // Orland
0x0003, // Ino'lis 0x0003, // Ino'lis
0x0004, // Sil'fer 0x0004, // Sil'fer
0x0111, // Relmitos 0x0111, // Relmitos
0x0115, // Glustar 0x0115, // Glustar
0x0112, // Viviana 0x0112, // Viviana
0x02AA, // H-HUmar 0x02AA, // H-HUmar
0x02AB, // H-HUnewearl 0x02AB, // H-HUnewearl
0x02B2, // H-FOmar 0x02B2, // H-FOmar
0x02B3, // H-FOmarl 0x02B3, // H-FOmarl
0x02B4, // H-FOnewm 0x02B4, // H-FOnewm
0x02B5, // H-FOnewearl 0x02B5, // H-FOnewearl
0x02CC, // H-HUmar 0x02CC, // H-HUmar
0x02CE, // H-FOmarl 0x02CE, // H-FOmarl
0x02CF, // H-HUnewearl 0x02CF, // H-HUnewearl
0x02D5, // H-FOmar 0x02D5, // H-FOmar
0x02D6, // H-FOnewearl 0x02D6, // H-FOnewearl
0x02D9, // H-FOnewm 0x02D9, // H-FOnewm
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_HU_CLASS_ANDROID_SC: { case CriterionCode::HUNTER_HU_CLASS_ANDROID_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0110, // Saligun 0x0110, // Saligun
0x0113, // Teifu 0x0113, // Teifu
0x02AC, // H-HUcast 0x02AC, // H-HUcast
0x02AD, // H-HUcaseal 0x02AD, // H-HUcaseal
0x02D4, // H-HUcaseal 0x02D4, // H-HUcaseal
0x02D7, // H-HUcast 0x02D7, // H-HUcast
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::UNKNOWN_10: { case CriterionCode::UNKNOWN_10: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0001, // Orland 0x0001, // Orland
0x0003, // Ino'lis 0x0003, // Ino'lis
0x0110, // Saligun 0x0110, // Saligun
0x0111, // Relmitos 0x0111, // Relmitos
0x0113, // Teifu 0x0113, // Teifu
0x02AA, // H-HUmar 0x02AA, // H-HUmar
0x02AC, // H-HUcast 0x02AC, // H-HUcast
0x02AD, // H-HUcaseal 0x02AD, // H-HUcaseal
0x02B2, // H-FOmar 0x02B2, // H-FOmar
0x02B3, // H-FOmarl 0x02B3, // H-FOmarl
0x02CC, // H-HUmar 0x02CC, // H-HUmar
0x02CE, // H-FOmarl 0x02CE, // H-FOmarl
0x02D4, // H-HUcaseal 0x02D4, // H-HUcaseal
0x02D5, // H-FOmar 0x02D5, // H-FOmar
0x02D7, // H-HUcast 0x02D7, // H-HUcast
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::UNKNOWN_11: { case CriterionCode::UNKNOWN_11: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0001, // Orland 0x0001, // Orland
0x0002, // Kranz 0x0002, // Kranz
0x0005, // Guykild 0x0005, // Guykild
0x0113, // Teifu 0x0113, // Teifu
0x02AA, // H-HUmar 0x02AA, // H-HUmar
0x02AC, // H-HUcast 0x02AC, // H-HUcast
0x02AE, // H-RAmar 0x02AE, // H-RAmar
0x02B0, // H-RAcast 0x02B0, // H-RAcast
0x02CC, // H-HUmar 0x02CC, // H-HUmar
0x02CD, // H-RAmarl 0x02CD, // H-RAmarl
0x02D0, // H-RAcast 0x02D0, // H-RAcast
0x02D7, // H-HUcast 0x02D7, // H-HUcast
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_HUNEWEARL_CLASS_SC: { case CriterionCode::HUNTER_HUNEWEARL_CLASS_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0004, // Sil'fer 0x0004, // Sil'fer
0x02AB, // H-HUnewearl 0x02AB, // H-HUnewearl
0x02CF, // H-HUnewearl 0x02CF, // H-HUnewearl
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_RA_CLASS_MALE_SC: { case CriterionCode::HUNTER_RA_CLASS_MALE_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0002, // Kranz 0x0002, // Kranz
0x0005, // Guykild 0x0005, // Guykild
0x02AE, // H-RAmar 0x02AE, // H-RAmar
0x02B0, // H-RAcast 0x02B0, // H-RAcast
0x02CD, // H-RAmarl 0x02CD, // H-RAmarl
0x02D0, // H-RAcast 0x02D0, // H-RAcast
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_RA_CLASS_FEMALE_SC: { case CriterionCode::HUNTER_RA_CLASS_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0006, // Kylria 0x0006, // Kylria
0x0114, // Stella 0x0114, // Stella
0x02AF, // H-RAmarl 0x02AF, // H-RAmarl
0x02B1, // H-RAcaseal 0x02B1, // H-RAcaseal
0x02D1, // H-RAmarl 0x02D1, // H-RAmarl
0x02D2, // D-RAcaseal 0x02D2, // D-RAcaseal
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_RA_OR_FO_CLASS_FEMALE_SC: { case CriterionCode::HUNTER_RA_OR_FO_CLASS_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0003, // Ino'lis 0x0003, // Ino'lis
0x0006, // Kylria 0x0006, // Kylria
0x0112, // Viviana 0x0112, // Viviana
0x0114, // Stella 0x0114, // Stella
0x02AF, // H-RAmarl 0x02AF, // H-RAmarl
0x02B1, // H-RAcaseal 0x02B1, // H-RAcaseal
0x02B3, // H-FOmarl 0x02B3, // H-FOmarl
0x02B5, // H-FOnewearl 0x02B5, // H-FOnewearl
0x02CE, // H-FOmarl 0x02CE, // H-FOmarl
0x02D1, // H-RAmarl 0x02D1, // H-RAmarl
0x02D6, // H-FOnewearl 0x02D6, // H-FOnewearl
0x02D8, // H-RAcaseal 0x02D8, // H-RAcaseal
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_HU_OR_RA_CLASS_HUMAN_SC: { case CriterionCode::HUNTER_HU_OR_RA_CLASS_HUMAN_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0001, // Orland 0x0001, // Orland
0x0002, // Kranz 0x0002, // Kranz
0x0004, // Sil'fer 0x0004, // Sil'fer
0x0006, // Kylria 0x0006, // Kylria
0x02AA, // H-HUmar 0x02AA, // H-HUmar
0x02AB, // H-HUnewearl 0x02AB, // H-HUnewearl
0x02AE, // H-RAmar 0x02AE, // H-RAmar
0x02AF, // H-RAmarl 0x02AF, // H-RAmarl
0x02CC, // H-HUmar 0x02CC, // H-HUmar
0x02CD, // H-RAmarl 0x02CD, // H-RAmarl
0x02CF, // H-HUnewearl 0x02CF, // H-HUnewearl
0x02D1, // H-RAmarl 0x02D1, // H-RAmarl
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_RA_CLASS_ANDROID_SC: { case CriterionCode::HUNTER_RA_CLASS_ANDROID_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0005, // Guykild 0x0005, // Guykild
0x0114, // Stella 0x0114, // Stella
0x02B0, // H-RAcast 0x02B0, // H-RAcast
0x02B1, // H-RAcaseal 0x02B1, // H-RAcaseal
0x02D0, // H-RAcast 0x02D0, // H-RAcast
0x02D8, // H-RAcaseal 0x02D8, // H-RAcaseal
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_FO_CLASS_FEMALE_SC: { case CriterionCode::HUNTER_FO_CLASS_FEMALE_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0003, // Ino'lis 0x0003, // Ino'lis
0x0112, // Viviana 0x0112, // Viviana
0x02B3, // H-FOmarl 0x02B3, // H-FOmarl
0x02B5, // H-FOnewearl 0x02B5, // H-FOnewearl
0x02CE, // H-FOmarl 0x02CE, // H-FOmarl
0x02D6, // H-FOnewearl 0x02D6, // H-FOnewearl
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_FEMALE_HUMAN_SC: { case CriterionCode::HUNTER_FEMALE_HUMAN_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0003, // Ino'lis 0x0003, // Ino'lis
0x0004, // Sil'fer 0x0004, // Sil'fer
0x0006, // Kylria 0x0006, // Kylria
0x0112, // Viviana 0x0112, // Viviana
0x02AB, // H-HUnewearl 0x02AB, // H-HUnewearl
0x02AF, // H-RAmarl 0x02AF, // H-RAmarl
0x02B3, // H-FOmarl 0x02B3, // H-FOmarl
0x02B5, // H-FOnewearl 0x02B5, // H-FOnewearl
0x02CE, // H-FOmarl 0x02CE, // H-FOmarl
0x02CF, // H-HUnewearl 0x02CF, // H-HUnewearl
0x02D1, // H-RAmarl 0x02D1, // H-RAmarl
0x02D6, // H-FOnewearl 0x02D6, // H-FOnewearl
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
case CriterionCode::HUNTER_ANDROID_SC: { case CriterionCode::HUNTER_ANDROID_SC: {
static const unordered_set<uint16_t> card_ids = { static const unordered_set<uint16_t> card_ids = {
0x0005, // Guykild 0x0005, // Guykild
0x0110, // Saligun 0x0110, // Saligun
0x0113, // Teifu 0x0113, // Teifu
0x0114, // Stella 0x0114, // Stella
0x02AC, // H-HUcast 0x02AC, // H-HUcast
0x02AD, // H-HUcaseal 0x02AD, // H-HUcaseal
0x02B0, // H-RAcast 0x02B0, // H-RAcast
0x02B1, // H-RAcaseal 0x02B1, // H-RAcaseal
0x02D0, // H-RAcast 0x02D0, // H-RAcast
0x02D4, // H-HUcaseal 0x02D4, // H-HUcaseal
0x02D7, // H-HUcast 0x02D7, // H-HUcast
0x02D8, // H-RAcaseal 0x02D8, // H-RAcaseal
}; };
return ret && card_ids.count(card_id2); return ret && card_ids.count(card_id2);
} }
@@ -1368,7 +1363,7 @@ uint16_t RulerServer::compute_attack_or_defense_costs(
if (((action_type == ActionType::ATTACK) || (action_type == ActionType::INVALID_00)) && if (((action_type == ActionType::ATTACK) || (action_type == ActionType::INVALID_00)) &&
(this->find_condition_on_card_ref(pa.attacker_card_ref, ConditionType::BIG_SWING) || (this->find_condition_on_card_ref(pa.attacker_card_ref, ConditionType::BIG_SWING) ||
this->find_condition_on_card_ref(sc_card_ref_if_item, ConditionType::BIG_SWING))) { this->find_condition_on_card_ref(sc_card_ref_if_item, ConditionType::BIG_SWING))) {
cost_bias++; cost_bias++;
} }
@@ -1435,7 +1430,8 @@ bool RulerServer::compute_effective_range_and_target_mode_for_attack(
TargetMode* out_effective_target_mode, TargetMode* out_effective_target_mode,
uint16_t* out_orig_card_ref) const { uint16_t* out_orig_card_ref) const {
size_t z; size_t z;
for (z = 0; (z < 9) && (pa.action_card_refs[z] != 0xFFFF); z++) { } for (z = 0; (z < 9) && (pa.action_card_refs[z] != 0xFFFF); z++) {
}
if (z >= 9) { if (z >= 9) {
return false; return false;
} }
@@ -1705,7 +1701,7 @@ int32_t RulerServer::error_code_for_client_setting_card(
if ((short_statuses->at(0).card_ref == 0xFFFF) || if ((short_statuses->at(0).card_ref == 0xFFFF) ||
!this->card_exists_by_status(short_statuses->at(0)) || !this->card_exists_by_status(short_statuses->at(0)) ||
!this->check_usability_or_apply_condition_for_card_refs( !this->check_usability_or_apply_condition_for_card_refs(
card_ref, short_statuses->at(0).card_ref, 0xFFFF, 0xFF, AttackMedium::INVALID_FF)) { card_ref, short_statuses->at(0).card_ref, 0xFFFF, 0xFF, AttackMedium::INVALID_FF)) {
return -0x75; return -0x75;
} }
@@ -1764,7 +1760,7 @@ int32_t RulerServer::error_code_for_client_setting_card(
Location summon_area_loc; Location summon_area_loc;
uint8_t summon_area_size; uint8_t summon_area_size;
if (!this->get_creature_summon_area( if (!this->get_creature_summon_area(
client_id, &summon_area_loc, &summon_area_size)) { client_id, &summon_area_loc, &summon_area_size)) {
if (team_id != 1) { if (team_id != 1) {
if ((loc->x > 0) && (loc->x < this->map_and_rules->map.width - 1)) { if ((loc->x > 0) && (loc->x < this->map_and_rules->map.width - 1)) {
if ((loc->y < this->map_and_rules->map.height - summon_cost - 1) && if ((loc->y < this->map_and_rules->map.height - summon_cost - 1) &&
@@ -1984,7 +1980,8 @@ shared_ptr<const DataIndex::CardEntry> RulerServer::definition_for_card_id(
uint32_t RulerServer::get_card_id_with_effective_range( uint32_t RulerServer::get_card_id_with_effective_range(
uint16_t card_ref, uint16_t card_id_override, TargetMode* out_target_mode) const { uint16_t card_ref, uint16_t card_id_override, TargetMode* out_target_mode) const {
uint16_t card_id = (card_id_override == 0xFFFF) uint16_t card_id = (card_id_override == 0xFFFF)
? this->card_id_for_card_ref(card_ref) : card_id_override; ? this->card_id_for_card_ref(card_ref)
: card_id_override;
if (card_id != 0xFFFF) { if (card_id != 0xFFFF) {
auto ce = this->definition_for_card_id(card_id); auto ce = this->definition_for_card_id(card_id);
@@ -2102,7 +2099,7 @@ bool RulerServer::get_move_path_length_and_cost(
parray<uint8_t, 0x100> visited_map; parray<uint8_t, 0x100> visited_map;
path.end_loc = loc; path.end_loc = loc;
if (!this->check_move_path_and_get_cost( if (!this->check_move_path_and_get_cost(
client_id, card_ref, &visited_map, &path, out_cost)) { client_id, card_ref, &visited_map, &path, out_cost)) {
return false; return false;
} }
@@ -2189,9 +2186,9 @@ bool RulerServer::is_attack_valid(const ActionState& pa) {
(attacker_chain->chain.acting_card_ref != attacker_card_ref) || (attacker_chain->chain.acting_card_ref != attacker_card_ref) ||
!attacker_ce || !attacker_ce ||
((attacker_ce->def.type != CardType::HUNTERS_SC && ((attacker_ce->def.type != CardType::HUNTERS_SC &&
(attacker_ce->def.type != CardType::ARKZ_SC) && (attacker_ce->def.type != CardType::ARKZ_SC) &&
(attacker_ce->def.type != CardType::CREATURE) && (attacker_ce->def.type != CardType::CREATURE) &&
(attacker_ce->def.type != CardType::ITEM)))) { (attacker_ce->def.type != CardType::ITEM)))) {
this->error_code3 = -0x6F; this->error_code3 = -0x6F;
return false; return false;
} }
@@ -2247,7 +2244,7 @@ bool RulerServer::is_attack_valid(const ActionState& pa) {
} }
if (!this->check_usability_or_apply_condition_for_card_refs( if (!this->check_usability_or_apply_condition_for_card_refs(
right_card_ref, attacker_card_ref, 0xFFFF, 0xFF, AttackMedium::INVALID_FF)) { right_card_ref, attacker_card_ref, 0xFFFF, 0xFF, AttackMedium::INVALID_FF)) {
this->error_code3 = -0x6A; this->error_code3 = -0x6A;
return false; return false;
} }
@@ -2377,7 +2374,7 @@ bool RulerServer::is_defense_valid(const ActionState& pa) {
} }
if (!this->defense_card_can_apply_to_attack( if (!this->defense_card_can_apply_to_attack(
pa.action_card_refs[0], pa.target_card_refs[0], pa.original_attacker_card_ref)) { pa.action_card_refs[0], pa.target_card_refs[0], pa.original_attacker_card_ref)) {
this->error_code3 = -0x61; this->error_code3 = -0x61;
return false; return false;
} }
@@ -2447,10 +2444,10 @@ size_t RulerServer::max_move_distance_for_card_ref(uint32_t card_ref) const {
} }
RulerServer::MovePath::MovePath() RulerServer::MovePath::MovePath()
: length(-1), : length(-1),
remaining_distance(0), remaining_distance(0),
num_occupied_tiles(0), num_occupied_tiles(0),
cost(0) { } cost(0) {}
void RulerServer::MovePath::add_step(const Location& loc) { void RulerServer::MovePath::add_step(const Location& loc) {
this->step_locs[++this->length] = loc; this->step_locs[++this->length] = loc;
@@ -2617,8 +2614,8 @@ const CardShortStatus* RulerServer::short_status_for_card_ref(uint16_t card_ref)
bool RulerServer::should_allow_attacks_on_current_turn() const { bool RulerServer::should_allow_attacks_on_current_turn() const {
return (this->state_flags && return (this->state_flags &&
((this->state_flags->turn_num > 1) || ((this->state_flags->turn_num > 1) ||
(this->state_flags->current_team_turn1 != this->state_flags->first_team_turn))); (this->state_flags->current_team_turn1 != this->state_flags->first_team_turn)));
} }
int32_t RulerServer::verify_deck( int32_t RulerServer::verify_deck(
@@ -2676,6 +2673,4 @@ int32_t RulerServer::verify_deck(
return 0; return 0;
} }
} // namespace Episode3 } // namespace Episode3
+3 -7
View File
@@ -4,15 +4,13 @@
#include <memory> #include <memory>
#include "DataIndex.hh"
#include "PlayerState.hh"
#include "DeckState.hh"
#include "AssistServer.hh" #include "AssistServer.hh"
#include "DataIndex.hh"
#include "DeckState.hh"
#include "PlayerState.hh"
namespace Episode3 { namespace Episode3 {
class Server; class Server;
void compute_effective_range( void compute_effective_range(
@@ -227,6 +225,4 @@ public:
int32_t error_code3; int32_t error_code3;
}; };
} // namespace Episode3 } // namespace Episode3
+77 -91
View File
@@ -1,7 +1,7 @@
#include "Server.hh" #include "Server.hh"
#include <phosg/Time.hh>
#include <phosg/Random.hh> #include <phosg/Random.hh>
#include <phosg/Time.hh>
#include "../SendCommands.hh" #include "../SendCommands.hh"
@@ -9,15 +9,11 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
// This is (obviously) not the original string. The original string is:
// "[V1][FINAL2.0] 03/09/13 15:30 by K.Toya"
// These strings in the original implementation did not contain the semicolons
// (or anything after them).
static const char* VERSION_SIGNATURE = static const char* VERSION_SIGNATURE =
"newserv Ep3 based on [V1][FINAL2.0] 03/09/13 15:30 by K.Toya"; "newserv Ep3 based on [V1][FINAL2.0] 03/09/13 15:30 by K.Toya";
ServerBase::PresenceEntry::PresenceEntry() { ServerBase::PresenceEntry::PresenceEntry() {
this->clear(); this->clear();
} }
@@ -28,19 +24,17 @@ void ServerBase::PresenceEntry::clear() {
this->is_cpu_player = 0; this->is_cpu_player = 0;
} }
ServerBase::ServerBase( ServerBase::ServerBase(
shared_ptr<Lobby> lobby, shared_ptr<Lobby> lobby,
shared_ptr<const DataIndex> data_index, shared_ptr<const DataIndex> data_index,
uint32_t random_seed, uint32_t random_seed,
shared_ptr<const DataIndex::MapEntry> map_if_tournament) shared_ptr<const DataIndex::MapEntry> map_if_tournament)
: lobby(lobby), : lobby(lobby),
data_index(data_index), data_index(data_index),
log(lobby->log.prefix + "[Ep3::Server] "), log(lobby->log.prefix + "[Ep3::Server] "),
random_seed(random_seed), random_seed(random_seed),
is_tournament(!!map_if_tournament), is_tournament(!!map_if_tournament),
last_chosen_map(map_if_tournament) { } last_chosen_map(map_if_tournament) {}
void ServerBase::init() { void ServerBase::init() {
this->reset(); this->reset();
@@ -65,46 +59,44 @@ void ServerBase::recreate_server() {
this->server->init(); this->server->init();
} }
Server::Server(shared_ptr<ServerBase> base) Server::Server(shared_ptr<ServerBase> base)
: w_base(base), : w_base(base),
battle_finished(false), battle_finished(false),
battle_in_progress(false), battle_in_progress(false),
round_num(1), round_num(1),
battle_phase(BattlePhase::INVALID_00), battle_phase(BattlePhase::INVALID_00),
first_team_turn(0xFF), first_team_turn(0xFF),
current_team_turn1(0xFF), current_team_turn1(0xFF),
setup_phase(SetupPhase::REGISTRATION), setup_phase(SetupPhase::REGISTRATION),
registration_phase(RegistrationPhase::AWAITING_NUM_PLAYERS), registration_phase(RegistrationPhase::AWAITING_NUM_PLAYERS),
action_subphase(ActionSubphase::ATTACK), action_subphase(ActionSubphase::ATTACK),
current_team_turn2(0xFF), current_team_turn2(0xFF),
num_pending_attacks(0), num_pending_attacks(0),
client_done_enqueuing_attacks(false), client_done_enqueuing_attacks(false),
player_ready_to_end_phase(false), player_ready_to_end_phase(false),
unknown_a10(0), unknown_a10(0),
overall_time_expired(false), overall_time_expired(false),
battle_start_usecs(0), battle_start_usecs(0),
should_copy_prev_states_to_current_states(0), should_copy_prev_states_to_current_states(0),
card_special(nullptr), card_special(nullptr),
clients_done_in_mulligan_phase(false), clients_done_in_mulligan_phase(false),
num_pending_attacks_with_cards(0), num_pending_attacks_with_cards(0),
unknown_a14(0), unknown_a14(0),
unknown_a15(0), unknown_a15(0),
defense_list_ended_for_client(false), defense_list_ended_for_client(false),
next_assist_card_set_number(1), next_assist_card_set_number(1),
team_exp(0), team_exp(0),
team_dice_boost(0), team_dice_boost(0),
team_client_count(0), team_client_count(0),
team_num_ally_fcs_destroyed(0), team_num_ally_fcs_destroyed(0),
team_num_cards_destroyed(0), team_num_cards_destroyed(0),
hard_reset_flag(false), hard_reset_flag(false),
tournament_flag(base->is_tournament ? 1 : 0), tournament_flag(base->is_tournament ? 1 : 0),
num_trap_tiles_of_type(0), num_trap_tiles_of_type(0),
chosen_trap_tile_index_of_type(0), chosen_trap_tile_index_of_type(0),
has_done_pb(0), has_done_pb(0),
num_6xB4x06_commands_sent(0), num_6xB4x06_commands_sent(0),
prev_num_6xB4x06_commands_sent(0) { } prev_num_6xB4x06_commands_sent(0) {}
void Server::init() { void Server::init() {
this->card_special.reset(new CardSpecial(this->shared_from_this())); this->card_special.reset(new CardSpecial(this->shared_from_this()));
@@ -262,8 +254,7 @@ void Server::send_commands_for_joining_spectator(Channel& c) const {
} }
} }
__attribute__((format(printf, 2, 3))) __attribute__((format(printf, 2, 3))) void Server::log_debug(const char* fmt, ...) const {
void Server::log_debug(const char* fmt, ...) const {
auto l = this->base()->lobby.lock(); auto l = this->base()->lobby.lock();
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) { if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
va_list va; va_list va;
@@ -273,8 +264,7 @@ void Server::log_debug(const char* fmt, ...) const {
} }
} }
__attribute__((format(printf, 2, 3))) __attribute__((format(printf, 2, 3))) void Server::send_debug_message_printf(const char* fmt, ...) const {
void Server::send_debug_message_printf(const char* fmt, ...) const {
auto l = this->base()->lobby.lock(); auto l = this->base()->lobby.lock();
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) { if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
va_list va; va_list va;
@@ -286,8 +276,7 @@ void Server::send_debug_message_printf(const char* fmt, ...) const {
} }
} }
__attribute__((format(printf, 2, 3))) __attribute__((format(printf, 2, 3))) void Server::send_info_message_printf(const char* fmt, ...) const {
void Server::send_info_message_printf(const char* fmt, ...) const {
auto l = this->base()->lobby.lock(); auto l = this->base()->lobby.lock();
if (l) { if (l) {
va_list va; va_list va;
@@ -539,7 +528,8 @@ void Server::check_for_destroyed_cards_and_send_6xB4x05_6xB4x02() {
bool Server::check_presence_entry(uint8_t client_id) const { bool Server::check_presence_entry(uint8_t client_id) const {
return (client_id < 4) return (client_id < 4)
? this->base()->presence_entries[client_id].player_present : false; ? this->base()->presence_entries[client_id].player_present
: false;
} }
void Server::clear_player_flags_after_dice_phase() { void Server::clear_player_flags_after_dice_phase() {
@@ -1220,7 +1210,6 @@ void Server::set_client_id_ready_to_advance_phase(uint8_t client_id) {
} }
} }
void Server::set_phase_after() { void Server::set_phase_after() {
for (size_t client_id = 0; client_id < 4; client_id++) { for (size_t client_id = 0; client_id < 4; client_id++) {
auto ps = this->player_states[client_id]; auto ps = this->player_states[client_id];
@@ -1513,29 +1502,29 @@ bool Server::update_registration_phase() {
} }
const unordered_map<uint8_t, Server::handler_t> Server::subcommand_handlers({ const unordered_map<uint8_t, Server::handler_t> Server::subcommand_handlers({
{0x0B, &Server::handle_6xB3x0B_mulligan_hand}, {0x0B, &Server::handle_6xB3x0B_mulligan_hand},
{0x0C, &Server::handle_6xB3x0C_end_mulligan_phase}, {0x0C, &Server::handle_6xB3x0C_end_mulligan_phase},
{0x0D, &Server::handle_6xB3x0D_end_non_action_phase}, {0x0D, &Server::handle_6xB3x0D_end_non_action_phase},
{0x0E, &Server::handle_6xB3x0E_discard_card_from_hand}, {0x0E, &Server::handle_6xB3x0E_discard_card_from_hand},
{0x0F, &Server::handle_6xB3x0F_set_card_from_hand}, {0x0F, &Server::handle_6xB3x0F_set_card_from_hand},
{0x10, &Server::handle_6xB3x10_move_fc_to_location}, {0x10, &Server::handle_6xB3x10_move_fc_to_location},
{0x11, &Server::handle_6xB3x11_enqueue_attack_or_defense}, {0x11, &Server::handle_6xB3x11_enqueue_attack_or_defense},
{0x12, &Server::handle_6xB3x12_end_attack_list}, {0x12, &Server::handle_6xB3x12_end_attack_list},
{0x13, &Server::handle_6xB3x13_update_map_during_setup}, {0x13, &Server::handle_6xB3x13_update_map_during_setup},
{0x14, &Server::handle_6xB3x14_update_deck_during_setup}, {0x14, &Server::handle_6xB3x14_update_deck_during_setup},
{0x15, &Server::handle_6xB3x15_unused_hard_reset_server_state}, {0x15, &Server::handle_6xB3x15_unused_hard_reset_server_state},
{0x1B, &Server::handle_6xB3x1B_update_player_name}, {0x1B, &Server::handle_6xB3x1B_update_player_name},
{0x1D, &Server::handle_6xB3x1D_start_battle}, {0x1D, &Server::handle_6xB3x1D_start_battle},
{0x21, &Server::handle_6xB3x21_end_battle}, {0x21, &Server::handle_6xB3x21_end_battle},
{0x28, &Server::handle_6xB3x28_end_defense_list}, {0x28, &Server::handle_6xB3x28_end_defense_list},
{0x2B, &Server::handle_6xB3x2B_ignored}, {0x2B, &Server::handle_6xB3x2B_ignored},
{0x34, &Server::handle_6xB3x34_subtract_ally_atk_points}, {0x34, &Server::handle_6xB3x34_subtract_ally_atk_points},
{0x37, &Server::handle_6xB3x37_client_ready_to_advance_from_starter_roll_phase}, {0x37, &Server::handle_6xB3x37_client_ready_to_advance_from_starter_roll_phase},
{0x3A, &Server::handle_6xB3x3A_ignored}, {0x3A, &Server::handle_6xB3x3A_ignored},
{0x40, &Server::handle_6xB3x40_map_list_request}, {0x40, &Server::handle_6xB3x40_map_list_request},
{0x41, &Server::handle_6xB3x41_map_request}, {0x41, &Server::handle_6xB3x41_map_request},
{0x48, &Server::handle_6xB3x48_end_turn}, {0x48, &Server::handle_6xB3x48_end_turn},
{0x49, &Server::handle_6xB3x49_card_counts}, {0x49, &Server::handle_6xB3x49_card_counts},
}); });
void Server::on_server_data_input(const string& data) { void Server::on_server_data_input(const string& data) {
@@ -2025,7 +2014,7 @@ void Server::handle_6xB3x28_end_defense_list(const string& data) {
this->send(out_cmd_fin); this->send(out_cmd_fin);
} }
void Server::handle_6xB3x2B_ignored(const string&) { } void Server::handle_6xB3x2B_ignored(const string&) {}
void Server::handle_6xB3x34_subtract_ally_atk_points(const string& data) { void Server::handle_6xB3x34_subtract_ally_atk_points(const string& data) {
const auto& in_cmd = check_size_t<G_PhotonBlastRequest_GC_Ep3_6xB3x34_CAx34>(data); const auto& in_cmd = check_size_t<G_PhotonBlastRequest_GC_Ep3_6xB3x34_CAx34>(data);
@@ -2131,7 +2120,7 @@ void Server::handle_6xB3x37_client_ready_to_advance_from_starter_roll_phase(cons
} }
} }
void Server::handle_6xB3x3A_ignored(const string&) { } void Server::handle_6xB3x3A_ignored(const string&) {}
void Server::handle_6xB3x40_map_list_request(const string& data) { void Server::handle_6xB3x40_map_list_request(const string& data) {
const auto& in_cmd = check_size_t<G_MapListRequest_GC_Ep3_6xB3x40_CAx40>(data); const auto& in_cmd = check_size_t<G_MapListRequest_GC_Ep3_6xB3x40_CAx40>(data);
@@ -2379,7 +2368,7 @@ void Server::unknown_8023EEF4() {
if (!this->attack_cards[this->unknown_a14]->action_chain.check_flag(0x40)) { if (!this->attack_cards[this->unknown_a14]->action_chain.check_flag(0x40)) {
this->card_special->unknown_8024945C(this->attack_cards[this->unknown_a14], as); this->card_special->unknown_8024945C(this->attack_cards[this->unknown_a14], as);
} }
this->attack_cards[this->unknown_a14]->compute_action_chain_results(1,0); this->attack_cards[this->unknown_a14]->compute_action_chain_results(1, 0);
this->attack_cards[this->unknown_a14]->unknown_80236374(this->attack_cards[this->unknown_a14], &as); this->attack_cards[this->unknown_a14]->unknown_80236374(this->attack_cards[this->unknown_a14], &as);
if (!this->attack_cards[this->unknown_a14]->action_chain.check_flag(0x40)) { if (!this->attack_cards[this->unknown_a14]->action_chain.check_flag(0x40)) {
this->card_special->unknown_8024966C(this->attack_cards[this->unknown_a14], &as); this->card_special->unknown_8024966C(this->attack_cards[this->unknown_a14], &as);
@@ -2653,7 +2642,6 @@ void Server::send_6xB4x05() {
this->send(cmd); this->send(cmd);
} }
void Server::send_6xB4x02_for_all_players_if_needed(bool always_send) { void Server::send_6xB4x02_for_all_players_if_needed(bool always_send) {
for (size_t z = 0; z < 4; z++) { for (size_t z = 0; z < 4; z++) {
auto ps = this->player_states[z]; auto ps = this->player_states[z];
@@ -2680,6 +2668,4 @@ void Server::send_6xB4x50_trap_tile_locations() const {
this->send(this->prepare_6xB4x50_trap_tile_locations()); this->send(this->prepare_6xB4x50_trap_tile_locations());
} }
} // namespace Episode3 } // namespace Episode3
+7 -15
View File
@@ -4,9 +4,9 @@
#include <memory> #include <memory>
#include "../Text.hh"
#include "../CommandFormats.hh"
#include "../Channel.hh" #include "../Channel.hh"
#include "../CommandFormats.hh"
#include "../Text.hh"
#include "AssistServer.hh" #include "AssistServer.hh"
#include "CardSpecial.hh" #include "CardSpecial.hh"
#include "MapState.hh" #include "MapState.hh"
@@ -17,8 +17,6 @@ struct Lobby;
namespace Episode3 { namespace Episode3 {
/** /**
* This implementation of Episode 3 battles (contained in all files in the * This implementation of Episode 3 battles (contained in all files in the
* src/Episode3 directory, except for DataIndex.hh/cc) is derived from Sega's * src/Episode3 directory, except for DataIndex.hh/cc) is derived from Sega's
@@ -27,7 +25,7 @@ namespace Episode3 {
* in these files map very closely to how their server implementation was * in these files map very closely to how their server implementation was
* written; notable differences (due to necessary environment differences or bug * written; notable differences (due to necessary environment differences or bug
* fixes) are described in the comments therein. * fixes) are described in the comments therein.
* *
* Some debugging functions have been added which are not part of the original * Some debugging functions have been added which are not part of the original
* implementation. Notably, this applies to functions like debug message senders * implementation. Notably, this applies to functions like debug message senders
* and loggers and all str() functions. * and loggers and all str() functions.
@@ -55,8 +53,6 @@ namespace Episode3 {
class Server; class Server;
class ServerBase : public std::enable_shared_from_this<ServerBase> { class ServerBase : public std::enable_shared_from_this<ServerBase> {
public: public:
ServerBase( ServerBase(
@@ -122,13 +118,10 @@ public:
void send_commands_for_joining_spectator(Channel& ch) const; void send_commands_for_joining_spectator(Channel& ch) const;
__attribute__((format(printf, 2, 3))) __attribute__((format(printf, 2, 3))) void log_debug(const char* fmt, ...) const;
void log_debug(const char* fmt, ...) const;
__attribute__((format(printf, 2, 3))) __attribute__((format(printf, 2, 3))) void send_debug_message_printf(const char* fmt, ...) const;
void send_debug_message_printf(const char* fmt, ...) const; __attribute__((format(printf, 2, 3))) void send_info_message_printf(const char* fmt, ...) const;
__attribute__((format(printf, 2, 3)))
void send_info_message_printf(const char* fmt, ...) const;
void send_debug_command_received_message( void send_debug_command_received_message(
uint8_t client_id, uint8_t subsubcommand, const char* description) const; uint8_t client_id, uint8_t subsubcommand, const char* description) const;
void send_debug_command_received_message( void send_debug_command_received_message(
@@ -241,6 +234,7 @@ public:
std::vector<std::shared_ptr<Card>> const_cast_set_cards_v( std::vector<std::shared_ptr<Card>> const_cast_set_cards_v(
const std::vector<std::shared_ptr<const Card>>& cards); const std::vector<std::shared_ptr<const Card>>& cards);
private: private:
typedef void (Server::*handler_t)(const std::string&); typedef void (Server::*handler_t)(const std::string&);
static const std::unordered_map<uint8_t, handler_t> subcommand_handlers; static const std::unordered_map<uint8_t, handler_t> subcommand_handlers;
@@ -301,6 +295,4 @@ public:
mutable uint32_t prev_num_6xB4x06_commands_sent; mutable uint32_t prev_num_6xB4x06_commands_sent;
}; };
} // namespace Episode3 } // namespace Episode3
+44 -55
View File
@@ -9,14 +9,14 @@ using namespace std;
namespace Episode3 { namespace Episode3 {
Tournament::PlayerEntry::PlayerEntry(uint32_t serial_number) Tournament::PlayerEntry::PlayerEntry(uint32_t serial_number)
: serial_number(serial_number), com_deck() { } : serial_number(serial_number),
com_deck() {}
Tournament::PlayerEntry::PlayerEntry( Tournament::PlayerEntry::PlayerEntry(
shared_ptr<const COMDeckDefinition> com_deck) shared_ptr<const COMDeckDefinition> com_deck)
: serial_number(0), com_deck(com_deck) { } : serial_number(0),
com_deck(com_deck) {}
bool Tournament::PlayerEntry::is_com() const { bool Tournament::PlayerEntry::is_com() const {
return (this->com_deck != nullptr); return (this->com_deck != nullptr);
@@ -26,17 +26,15 @@ bool Tournament::PlayerEntry::is_human() const {
return (this->serial_number != 0); return (this->serial_number != 0);
} }
Tournament::Team::Team( Tournament::Team::Team(
shared_ptr<Tournament> tournament, size_t index, size_t max_players) shared_ptr<Tournament> tournament, size_t index, size_t max_players)
: tournament(tournament), : tournament(tournament),
index(index), index(index),
max_players(max_players), max_players(max_players),
name(""), name(""),
password(""), password(""),
num_rounds_cleared(0), num_rounds_cleared(0),
is_active(true) { } is_active(true) {}
string Tournament::Team::str() const { string Tournament::Team::str() const {
size_t num_human_players = 0; size_t num_human_players = 0;
@@ -133,8 +131,8 @@ bool Tournament::Team::unregister_player(uint32_t serial_number) {
} }
} }
// If the tournament has not started yet, just remove the player from the // If the tournament has not started yet, just remove the player from the
// team // team
} else { } else {
if (!tournament->all_player_serial_numbers.erase(serial_number)) { if (!tournament->all_player_serial_numbers.erase(serial_number)) {
throw logic_error("player removed from team but not from tournament"); throw logic_error("player removed from team but not from tournament");
@@ -173,17 +171,15 @@ size_t Tournament::Team::num_com_players() const {
return ret; return ret;
} }
Tournament::Match::Match( Tournament::Match::Match(
shared_ptr<Tournament> tournament, shared_ptr<Tournament> tournament,
shared_ptr<Match> preceding_a, shared_ptr<Match> preceding_a,
shared_ptr<Match> preceding_b) shared_ptr<Match> preceding_b)
: tournament(tournament), : tournament(tournament),
preceding_a(preceding_a), preceding_a(preceding_a),
preceding_b(preceding_b), preceding_b(preceding_b),
winner_team(nullptr), winner_team(nullptr),
round_num(0) { round_num(0) {
if (this->preceding_a->round_num != this->preceding_b->round_num) { if (this->preceding_a->round_num != this->preceding_b->round_num) {
throw logic_error("preceding matches have different round numbers"); throw logic_error("preceding matches have different round numbers");
} }
@@ -193,11 +189,11 @@ Tournament::Match::Match(
Tournament::Match::Match( Tournament::Match::Match(
shared_ptr<Tournament> tournament, shared_ptr<Tournament> tournament,
shared_ptr<Team> winner_team) shared_ptr<Team> winner_team)
: tournament(tournament), : tournament(tournament),
preceding_a(nullptr), preceding_a(nullptr),
preceding_b(nullptr), preceding_b(nullptr),
winner_team(winner_team), winner_team(winner_team),
round_num(0) { } round_num(0) {}
string Tournament::Match::str() const { string Tournament::Match::str() const {
string winner_str = this->winner_team ? this->winner_team->str() : "(none)"; string winner_str = this->winner_team ? this->winner_team->str() : "(none)";
@@ -216,7 +212,8 @@ bool Tournament::Match::resolve_if_no_human_players() {
!this->preceding_a->winner_team->has_any_human_players() && !this->preceding_a->winner_team->has_any_human_players() &&
!this->preceding_b->winner_team->has_any_human_players()) { !this->preceding_b->winner_team->has_any_human_players()) {
this->set_winner_team((random_object<uint8_t>() & 1) this->set_winner_team((random_object<uint8_t>() & 1)
? this->preceding_b->winner_team : this->preceding_a->winner_team); ? this->preceding_b->winner_team
: this->preceding_a->winner_team);
return true; return true;
} else { } else {
return false; return false;
@@ -282,8 +279,6 @@ shared_ptr<Tournament::Team> Tournament::Match::opponent_team_for_team(
} }
} }
Tournament::Tournament( Tournament::Tournament(
shared_ptr<const DataIndex> data_index, shared_ptr<const DataIndex> data_index,
uint8_t number, uint8_t number,
@@ -292,15 +287,15 @@ Tournament::Tournament(
const Rules& rules, const Rules& rules,
size_t num_teams, size_t num_teams,
bool is_2v2) bool is_2v2)
: log(string_printf("[Tournament/%02hhX] ", number)), : log(string_printf("[Tournament/%02hhX] ", number)),
data_index(data_index), data_index(data_index),
number(number), number(number),
name(name), name(name),
map(map), map(map),
rules(rules), rules(rules),
num_teams(num_teams), num_teams(num_teams),
is_2v2(is_2v2), is_2v2(is_2v2),
current_state(State::REGISTRATION) { current_state(State::REGISTRATION) {
if (this->num_teams < 4) { if (this->num_teams < 4) {
throw invalid_argument("team count must be 4 or more"); throw invalid_argument("team count must be 4 or more");
} }
@@ -316,11 +311,11 @@ Tournament::Tournament(
std::shared_ptr<const DataIndex> data_index, std::shared_ptr<const DataIndex> data_index,
uint8_t number, uint8_t number,
std::shared_ptr<const JSONObject> json) std::shared_ptr<const JSONObject> json)
: log(string_printf("[Tournament/%02hhX] ", number)), : log(string_printf("[Tournament/%02hhX] ", number)),
data_index(data_index), data_index(data_index),
source_json(json), source_json(json),
number(number), number(number),
current_state(State::REGISTRATION) { } current_state(State::REGISTRATION) {}
void Tournament::init() { void Tournament::init() {
vector<size_t> team_index_to_rounds_cleared; vector<size_t> team_index_to_rounds_cleared;
@@ -414,10 +409,8 @@ void Tournament::init() {
if (!match->preceding_a->winner_team || !match->preceding_b->winner_team) { if (!match->preceding_a->winner_team || !match->preceding_b->winner_team) {
throw logic_error("preceding matches are not resolved"); throw logic_error("preceding matches are not resolved");
} }
size_t& a_rounds_cleared = team_index_to_rounds_cleared[ size_t& a_rounds_cleared = team_index_to_rounds_cleared[match->preceding_a->winner_team->index];
match->preceding_a->winner_team->index]; size_t& b_rounds_cleared = team_index_to_rounds_cleared[match->preceding_b->winner_team->index];
size_t& b_rounds_cleared = team_index_to_rounds_cleared[
match->preceding_b->winner_team->index];
if (a_rounds_cleared && b_rounds_cleared) { if (a_rounds_cleared && b_rounds_cleared) {
throw runtime_error("both teams won the same match"); throw runtime_error("both teams won the same match");
} }
@@ -469,8 +462,7 @@ std::shared_ptr<JSONObject> Tournament::json() const {
dict.emplace("map_number", make_json_int(this->map->map.map_number)); dict.emplace("map_number", make_json_int(this->map->map.map_number));
dict.emplace("rules", this->rules.json()); dict.emplace("rules", this->rules.json());
dict.emplace("is_2v2", make_json_bool(this->is_2v2)); dict.emplace("is_2v2", make_json_bool(this->is_2v2));
dict.emplace("is_registration_complete", make_json_bool( dict.emplace("is_registration_complete", make_json_bool(this->current_state != State::REGISTRATION));
this->current_state != State::REGISTRATION));
vector<shared_ptr<JSONObject>> teams_list; vector<shared_ptr<JSONObject>> teams_list;
for (auto team : this->teams) { for (auto team : this->teams) {
@@ -661,13 +653,12 @@ void Tournament::print_bracket(FILE* stream) const {
} }
} }
TournamentIndex::TournamentIndex( TournamentIndex::TournamentIndex(
shared_ptr<const DataIndex> data_index, shared_ptr<const DataIndex> data_index,
const string& state_filename, const string& state_filename,
bool skip_load_state) bool skip_load_state)
: data_index(data_index), state_filename(state_filename) { : data_index(data_index),
state_filename(state_filename) {
if (this->state_filename.empty() || skip_load_state) { if (this->state_filename.empty() || skip_load_state) {
return; return;
} }
@@ -768,6 +759,4 @@ shared_ptr<Tournament::Team> TournamentIndex::team_for_serial_number(
return nullptr; return nullptr;
} }
} // namespace Episode3 } // namespace Episode3
+9 -13
View File
@@ -1,14 +1,14 @@
#pragma once #pragma once
#include <stdint.h>
#include <event2/event.h> #include <event2/event.h>
#include <stdint.h>
#include <memory> #include <memory>
#include <vector>
#include <unordered_set>
#include <string>
#include <phosg/JSON.hh> #include <phosg/JSON.hh>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <string>
#include <unordered_set>
#include <vector>
#include "../Player.hh" #include "../Player.hh"
@@ -18,8 +18,6 @@ namespace Episode3 {
// The comment in Server.hh does not apply to this file (and Tournament.cc). // The comment in Server.hh does not apply to this file (and Tournament.cc).
class Tournament : public std::enable_shared_from_this<Tournament> { class Tournament : public std::enable_shared_from_this<Tournament> {
public: public:
enum class State { enum class State {
@@ -172,11 +170,11 @@ public:
std::vector<std::shared_ptr<Tournament>> all_tournaments() const; std::vector<std::shared_ptr<Tournament>> all_tournaments() const;
std::shared_ptr<Tournament> create_tournament( std::shared_ptr<Tournament> create_tournament(
const std::string& name, const std::string& name,
std::shared_ptr<const DataIndex::MapEntry> map, std::shared_ptr<const DataIndex::MapEntry> map,
const Rules& rules, const Rules& rules,
size_t num_teams, size_t num_teams,
bool is_2v2); bool is_2v2);
void delete_tournament(uint8_t number); void delete_tournament(uint8_t number);
std::shared_ptr<Tournament> get_tournament(uint8_t number) const; std::shared_ptr<Tournament> get_tournament(uint8_t number) const;
std::shared_ptr<Tournament> get_tournament(const std::string& name) const; std::shared_ptr<Tournament> get_tournament(const std::string& name) const;
@@ -190,6 +188,4 @@ private:
std::shared_ptr<Tournament> tournaments[0x20]; std::shared_ptr<Tournament> tournaments[0x20];
}; };
} // namespace Episode3 } // namespace Episode3
+6 -5
View File
@@ -7,15 +7,15 @@
using namespace std; using namespace std;
FileContentsCache::FileContentsCache(uint64_t ttl_usecs) : ttl_usecs(ttl_usecs) {}
FileContentsCache::FileContentsCache(uint64_t ttl_usecs) : ttl_usecs(ttl_usecs) { }
FileContentsCache::File::File( FileContentsCache::File::File(
const string& name, const string& name,
string&& data, string&& data,
uint64_t load_time) uint64_t load_time)
: name(name), data(new string(move(data))), load_time(load_time) { } : name(name),
data(new string(move(data))),
load_time(load_time) {}
shared_ptr<const FileContentsCache::File> FileContentsCache::replace( shared_ptr<const FileContentsCache::File> FileContentsCache::replace(
const string& name, string&& data, uint64_t t) { const string& name, string&& data, uint64_t t) {
@@ -65,7 +65,8 @@ FileContentsCache::GetResult FileContentsCache::get(const std::string& name,
if (this->ttl_usecs && (t - entry->load_time < this->ttl_usecs)) { if (this->ttl_usecs && (t - entry->load_time < this->ttl_usecs)) {
return {entry, false}; return {entry, false};
} }
} catch (const out_of_range& e) { } } catch (const out_of_range& e) {
}
return {this->replace(name, generate(name)), true}; return {this->replace(name, generate(name)), true};
} }
+3 -3
View File
@@ -1,15 +1,14 @@
#pragma once #pragma once
#include <functional>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <functional>
#include <phosg/Time.hh> #include <phosg/Time.hh>
using namespace std; using namespace std;
class FileContentsCache { class FileContentsCache {
public: public:
struct File { struct File {
@@ -92,7 +91,8 @@ public:
if (this->ttl_usecs && (t - f->load_time < this->ttl_usecs)) { if (this->ttl_usecs && (t - f->load_time < this->ttl_usecs)) {
return {*reinterpret_cast<const T*>(f->data->data()), f, false}; return {*reinterpret_cast<const T*>(f->data->data()), f, false};
} }
} catch (const out_of_range& e) { } } catch (const out_of_range& e) {
}
T value = generate(name); T value = generate(name);
auto ret = this->replace_obj(name, value); auto ret = this->replace_obj(name, value);
ret.generate_called = true; ret.generate_called = true;
+6 -18
View File
@@ -3,20 +3,18 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdexcept>
#include <phosg/Filesystem.hh> #include <phosg/Filesystem.hh>
#include <stdexcept>
#ifdef HAVE_RESOURCE_FILE #ifdef HAVE_RESOURCE_FILE
#include <resource_file/Emulators/PPC32Emulator.hh> #include <resource_file/Emulators/PPC32Emulator.hh>
#endif #endif
#include "Loggers.hh"
#include "CommandFormats.hh" #include "CommandFormats.hh"
#include "Loggers.hh"
using namespace std; using namespace std;
bool function_compiler_available() { bool function_compiler_available() {
#ifndef HAVE_RESOURCE_FILE #ifndef HAVE_RESOURCE_FILE
return false; return false;
@@ -25,8 +23,6 @@ bool function_compiler_available() {
#endif #endif
} }
const char* name_for_architecture(CompiledFunctionCode::Architecture arch) { const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
switch (arch) { switch (arch) {
case CompiledFunctionCode::Architecture::POWERPC: case CompiledFunctionCode::Architecture::POWERPC:
@@ -38,12 +34,10 @@ const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
} }
} }
template <typename FooterT> template <typename FooterT>
string CompiledFunctionCode::generate_client_command_t( string CompiledFunctionCode::generate_client_command_t(
const unordered_map<string, uint32_t>& label_writes, const unordered_map<string, uint32_t>& label_writes,
const string& suffix) const { const string& suffix) const {
FooterT footer; FooterT footer;
footer.num_relocations = this->relocation_deltas.size(); footer.num_relocations = this->relocation_deltas.size();
footer.unused1.clear(0); footer.unused1.clear(0);
@@ -82,8 +76,8 @@ string CompiledFunctionCode::generate_client_command_t(
} }
string CompiledFunctionCode::generate_client_command( string CompiledFunctionCode::generate_client_command(
const unordered_map<string, uint32_t>& label_writes, const unordered_map<string, uint32_t>& label_writes,
const string& suffix) const { const string& suffix) const {
if (this->arch == Architecture::POWERPC) { if (this->arch == Architecture::POWERPC) {
return this->generate_client_command_t<S_ExecuteCode_Footer_GC_B2>( return this->generate_client_command_t<S_ExecuteCode_Footer_GC_B2>(
label_writes, suffix); label_writes, suffix);
@@ -99,8 +93,6 @@ bool CompiledFunctionCode::is_big_endian() const {
return this->arch == Architecture::POWERPC; return this->arch == Architecture::POWERPC;
} }
shared_ptr<CompiledFunctionCode> compile_function_code( shared_ptr<CompiledFunctionCode> compile_function_code(
CompiledFunctionCode::Architecture arch, CompiledFunctionCode::Architecture arch,
const string& directory, const string& directory,
@@ -159,8 +151,6 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
#endif #endif
} }
FunctionCodeIndex::FunctionCodeIndex(const string& directory) { FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
if (!function_compiler_available()) { if (!function_compiler_available()) {
function_compiler_log.info("Function compiler is not available"); function_compiler_log.info("Function compiler is not available");
@@ -217,8 +207,6 @@ vector<MenuItem> FunctionCodeIndex::patch_menu() const {
return ret; return ret;
} }
DOLFileIndex::DOLFileIndex(const string& directory) { DOLFileIndex::DOLFileIndex(const string& directory) {
if (!function_compiler_available()) { if (!function_compiler_available()) {
function_compiler_log.info("Function compiler is not available"); function_compiler_log.info("Function compiler is not available");
+4 -12
View File
@@ -2,20 +2,16 @@
#include <inttypes.h> #include <inttypes.h>
#include <map>
#include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <map>
#include <vector> #include <vector>
#include <memory>
#include "Menu.hh" #include "Menu.hh"
bool function_compiler_available(); bool function_compiler_available();
// TODO: Support x86 and SH4 function calls in the future. Currently we only // TODO: Support x86 and SH4 function calls in the future. Currently we only
// support PPC32 because I haven't written an appropriate x86 assembler yet. // support PPC32 because I haven't written an appropriate x86 assembler yet.
@@ -39,8 +35,8 @@ struct CompiledFunctionCode {
template <typename FooterT> template <typename FooterT>
std::string generate_client_command_t( std::string generate_client_command_t(
const std::unordered_map<std::string, uint32_t>& label_writes, const std::unordered_map<std::string, uint32_t>& label_writes,
const std::string& suffix) const; const std::string& suffix) const;
std::string generate_client_command( std::string generate_client_command(
const std::unordered_map<std::string, uint32_t>& label_writes = {}, const std::unordered_map<std::string, uint32_t>& label_writes = {},
const std::string& suffix = "") const; const std::string& suffix = "") const;
@@ -54,8 +50,6 @@ std::shared_ptr<CompiledFunctionCode> compile_function_code(
const std::string& name, const std::string& name,
const std::string& text); const std::string& text);
struct FunctionCodeIndex { struct FunctionCodeIndex {
FunctionCodeIndex() = default; FunctionCodeIndex() = default;
explicit FunctionCodeIndex(const std::string& directory); explicit FunctionCodeIndex(const std::string& directory);
@@ -72,8 +66,6 @@ struct FunctionCodeIndex {
} }
}; };
struct DOLFileIndex { struct DOLFileIndex {
struct DOLFile { struct DOLFile {
uint32_t menu_item_id; uint32_t menu_item_id;
+1 -3
View File
@@ -8,8 +8,6 @@
using namespace std; using namespace std;
template <bool IsBigEndian> template <bool IsBigEndian>
struct GSLHeaderEntry { struct GSLHeaderEntry {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type; using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
@@ -38,7 +36,7 @@ void GSLArchive::load_t() {
} }
GSLArchive::GSLArchive(shared_ptr<const string> data, bool big_endian) GSLArchive::GSLArchive(shared_ptr<const string> data, bool big_endian)
: data(data) { : data(data) {
if (big_endian) { if (big_endian) {
this->load_t<true>(); this->load_t<true>();
} else { } else {
-2
View File
@@ -8,8 +8,6 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
class GSLArchive { class GSLArchive {
public: public:
GSLArchive(std::shared_ptr<const std::string> data, bool big_endian); GSLArchive(std::shared_ptr<const std::string> data, bool big_endian);
+33 -32
View File
@@ -6,8 +6,6 @@
using namespace std; using namespace std;
static inline uint16_t collapse_checksum(uint32_t sum) { static inline uint16_t collapse_checksum(uint32_t sum) {
// It's impossible for this to be necessary more than twice: the first // It's impossible for this to be necessary more than twice: the first
// addition can carry out at most a single bit. // addition can carry out at most a single bit.
@@ -15,35 +13,33 @@ static inline uint16_t collapse_checksum(uint32_t sum) {
return (sum & 0xFFFF) + (sum >> 16); return (sum & 0xFFFF) + (sum >> 16);
} }
FrameInfo::FrameInfo() FrameInfo::FrameInfo()
: ether(nullptr), : ether(nullptr),
ether_protocol(0), ether_protocol(0),
ipv4(nullptr), ipv4(nullptr),
arp(nullptr), arp(nullptr),
udp(nullptr), udp(nullptr),
tcp(nullptr), tcp(nullptr),
header_start(nullptr), header_start(nullptr),
payload(nullptr), payload(nullptr),
total_size(0), total_size(0),
tcp_options_size(0), tcp_options_size(0),
payload_size(0) { } payload_size(0) {}
FrameInfo::FrameInfo(const string& data) : FrameInfo(data.data(), data.size()) { } FrameInfo::FrameInfo(const string& data) : FrameInfo(data.data(), data.size()) {}
FrameInfo::FrameInfo(const void* header_start, size_t size) FrameInfo::FrameInfo(const void* header_start, size_t size)
: ether(nullptr), : ether(nullptr),
ether_protocol(0), ether_protocol(0),
ipv4(nullptr), ipv4(nullptr),
arp(nullptr), arp(nullptr),
udp(nullptr), udp(nullptr),
tcp(nullptr), tcp(nullptr),
header_start(header_start), header_start(header_start),
payload(nullptr), payload(nullptr),
total_size(size), total_size(size),
tcp_options_size(0), tcp_options_size(0),
payload_size(size) { payload_size(size) {
// Parse ethernet header // Parse ethernet header
if (this->payload_size < sizeof(EthernetHeader)) { if (this->payload_size < sizeof(EthernetHeader)) {
@@ -125,26 +121,31 @@ string FrameInfo::header_str() const {
return "<invalid-frame-info>"; return "<invalid-frame-info>";
} }
string ret = string_printf("%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX->%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX", string ret = string_printf(
"%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX->%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
this->ether->src_mac[0], this->ether->src_mac[1], this->ether->src_mac[2], this->ether->src_mac[0], this->ether->src_mac[1], this->ether->src_mac[2],
this->ether->src_mac[3], this->ether->src_mac[4], this->ether->src_mac[5], this->ether->src_mac[3], this->ether->src_mac[4], this->ether->src_mac[5],
this->ether->dest_mac[0], this->ether->dest_mac[1], this->ether->dest_mac[2], this->ether->dest_mac[0], this->ether->dest_mac[1], this->ether->dest_mac[2],
this->ether->dest_mac[3], this->ether->dest_mac[4], this->ether->dest_mac[5]); this->ether->dest_mac[3], this->ether->dest_mac[4], this->ether->dest_mac[5]);
if (this->arp) { if (this->arp) {
ret += string_printf(",ARP,hw_type=%04hX,proto_type=%04hX,hw_addr_len=%02hhX,proto_addr_len=%02hhX,op=%04hX", ret += string_printf(
",ARP,hw_type=%04hX,proto_type=%04hX,hw_addr_len=%02hhX,proto_addr_len=%02hhX,op=%04hX",
this->arp->hardware_type.load(), this->arp->protocol_type.load(), this->arp->hwaddr_len, this->arp->paddr_len, this->arp->operation.load()); this->arp->hardware_type.load(), this->arp->protocol_type.load(), this->arp->hwaddr_len, this->arp->paddr_len, this->arp->operation.load());
} else if (this->ipv4) { } else if (this->ipv4) {
ret += string_printf(",IPv4,size=%04hX,src=%08" PRIX32 ",dest=%08" PRIX32, ret += string_printf(
",IPv4,size=%04hX,src=%08" PRIX32 ",dest=%08" PRIX32,
this->ipv4->size.load(), this->ipv4->src_addr.load(), this->ipv4->dest_addr.load()); this->ipv4->size.load(), this->ipv4->src_addr.load(), this->ipv4->dest_addr.load());
if (this->udp) { if (this->udp) {
ret += string_printf(",UDP,src_port=%04hX,dest_port=%04hX,size=%04hX", ret += string_printf(
",UDP,src_port=%04hX,dest_port=%04hX,size=%04hX",
this->udp->src_port.load(), this->udp->dest_port.load(), this->udp->size.load()); this->udp->src_port.load(), this->udp->dest_port.load(), this->udp->size.load());
} else if (this->tcp) { } else if (this->tcp) {
ret += string_printf(",TCP,src_port=%04hX,dest_port=%04hX,seq=%08" PRIX32 ",ack=%08" PRIX32 ",flags=%04hX(", ret += string_printf(
",TCP,src_port=%04hX,dest_port=%04hX,seq=%08" PRIX32 ",ack=%08" PRIX32 ",flags=%04hX(",
this->tcp->src_port.load(), this->tcp->dest_port.load(), this->tcp->seq_num.load(), this->tcp->ack_num.load(), this->tcp->flags.load()); this->tcp->src_port.load(), this->tcp->dest_port.load(), this->tcp->seq_num.load(), this->tcp->ack_num.load(), this->tcp->flags.load());
if (this->tcp->flags & TCPHeader::Flag::FIN) { if (this->tcp->flags & TCPHeader::Flag::FIN) {
ret += "FIN,"; ret += "FIN,";
+1 -5
View File
@@ -4,8 +4,6 @@
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
struct EthernetHeader { struct EthernetHeader {
uint8_t dest_mac[6]; uint8_t dest_mac[6];
uint8_t src_mac[6]; uint8_t src_mac[6];
@@ -42,7 +40,7 @@ struct UDPHeader {
struct TCPHeader { struct TCPHeader {
enum Flag { enum Flag {
NS = 0x0100, NS = 0x0100,
CWR = 0x0080, // congestion window reduced CWR = 0x0080, // congestion window reduced
ECE = 0x0040, // ECN capable / congestion experienced ECE = 0x0040, // ECN capable / congestion experienced
URG = 0x0020, // urgent pointer used URG = 0x0020, // urgent pointer used
@@ -63,8 +61,6 @@ struct TCPHeader {
be_uint16_t urgent_ptr; be_uint16_t urgent_ptr;
} __attribute__((packed)); } __attribute__((packed));
struct FrameInfo { struct FrameInfo {
// This is always valid // This is always valid
const EthernetHeader* ether; const EthernetHeader* ether;
+49 -80
View File
@@ -1,30 +1,26 @@
#include "IPStackSimulator.hh" #include "IPStackSimulator.hh"
#include <stdint.h> #include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <event2/buffer.h> #include <event2/buffer.h>
#include <event2/bufferevent.h> #include <event2/bufferevent.h>
#include <event2/listener.h> #include <event2/listener.h>
#include <arpa/inet.h> #include <netinet/in.h>
#include <stdint.h>
#include <string.h>
#include <string>
#include <phosg/Network.hh> #include <phosg/Network.hh>
#include <phosg/Random.hh> #include <phosg/Random.hh>
#include <phosg/Time.hh> #include <phosg/Time.hh>
#include <string>
#include "Loggers.hh"
#include "IPFrameInfo.hh"
#include "DNSServer.hh" #include "DNSServer.hh"
#include "IPFrameInfo.hh"
#include "Loggers.hh"
using namespace std; using namespace std;
static const size_t DEFAULT_RESEND_PUSH_USECS = 200000; // 200ms static const size_t DEFAULT_RESEND_PUSH_USECS = 200000; // 200ms
// Note: these functions exist because seq nums are allowed to wrap around the // Note: these functions exist because seq nums are allowed to wrap around the
// 32-bit integer space by design. We have to do the subtraction before the // 32-bit integer space by design. We have to do the subtraction before the
// comparison to allow integer overflow to occur if needed. // comparison to allow integer overflow to occur if needed.
@@ -45,8 +41,6 @@ static __attribute__((unused)) inline bool seq_num_greater_or_equal(uint32_t a,
return (a == b) || seq_num_greater(a, b); return (a == b) || seq_num_greater(a, b);
} }
string IPStackSimulator::str_for_ipv4_netloc(uint32_t addr, uint16_t port) { string IPStackSimulator::str_for_ipv4_netloc(uint32_t addr, uint16_t port) {
be_uint32_t be_addr = addr; be_uint32_t be_addr = addr;
char addr_str[INET_ADDRSTRLEN]; char addr_str[INET_ADDRSTRLEN];
@@ -67,14 +61,12 @@ string IPStackSimulator::str_for_tcp_connection(shared_ptr<const IPClient> c,
fd, key, client_netloc_str.c_str(), server_netloc_str.c_str()); fd, key, client_netloc_str.c_str(), server_netloc_str.c_str());
} }
IPStackSimulator::IPStackSimulator( IPStackSimulator::IPStackSimulator(
std::shared_ptr<struct event_base> base, std::shared_ptr<struct event_base> base,
std::shared_ptr<ServerState> state) std::shared_ptr<ServerState> state)
: base(base), : base(base),
state(state), state(state),
pcap_text_log_file(state->ip_stack_debug ? fopen("IPStackSimulator-Log.txt", "wt") : nullptr) { pcap_text_log_file(state->ip_stack_debug ? fopen("IPStackSimulator-Log.txt", "wt") : nullptr) {
memset(this->host_mac_address_bytes, 0x90, 6); memset(this->host_mac_address_bytes, 0x90, 6);
memset(this->broadcast_mac_address_bytes, 0xFF, 6); memset(this->broadcast_mac_address_bytes, 0xFF, 6);
} }
@@ -85,8 +77,6 @@ IPStackSimulator::~IPStackSimulator() {
} }
} }
void IPStackSimulator::listen(const std::string& socket_path) { void IPStackSimulator::listen(const std::string& socket_path) {
this->add_socket(::listen(socket_path, 0, SOMAXCONN)); this->add_socket(::listen(socket_path, 0, SOMAXCONN));
} }
@@ -102,17 +92,15 @@ void IPStackSimulator::listen(int port) {
void IPStackSimulator::add_socket(int fd) { void IPStackSimulator::add_socket(int fd) {
this->listeners.emplace( this->listeners.emplace(
evconnlistener_new( evconnlistener_new(
this->base.get(), this->base.get(),
IPStackSimulator::dispatch_on_listen_accept, IPStackSimulator::dispatch_on_listen_accept,
this, this,
LEV_OPT_REUSEABLE, LEV_OPT_REUSEABLE,
0, 0,
fd), fd),
evconnlistener_free); evconnlistener_free);
} }
uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_addr) { uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_addr) {
// Use an address not on the same subnet as the client, so that PSO Plus and // Use an address not on the same subnet as the client, so that PSO Plus and
// Episode III will think they're talking to a remote network and won't reject // Episode III will think they're talking to a remote network and won't reject
@@ -124,41 +112,36 @@ uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_ad
} }
} }
IPStackSimulator::IPClient::IPClient(struct bufferevent* bev) IPStackSimulator::IPClient::IPClient(struct bufferevent* bev)
: bev(bev, bufferevent_free), ipv4_addr(0) { : bev(bev, bufferevent_free),
ipv4_addr(0) {
memset(this->mac_addr, 0, 6); memset(this->mac_addr, 0, 6);
} }
static void flush_and_free_bufferevent(struct bufferevent* bev) { static void flush_and_free_bufferevent(struct bufferevent* bev) {
bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_FINISHED); bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_FINISHED);
bufferevent_free(bev); bufferevent_free(bev);
} }
IPStackSimulator::IPClient::TCPConnection::TCPConnection() IPStackSimulator::IPClient::TCPConnection::TCPConnection()
: server_bev(nullptr, flush_and_free_bufferevent), : server_bev(nullptr, flush_and_free_bufferevent),
pending_data(evbuffer_new(), evbuffer_free), pending_data(evbuffer_new(), evbuffer_free),
resend_push_event(nullptr, event_free), resend_push_event(nullptr, event_free),
awaiting_first_ack(true), awaiting_first_ack(true),
server_addr(0), server_addr(0),
server_port(0), server_port(0),
client_port(0), client_port(0),
next_client_seq(0), next_client_seq(0),
acked_server_seq(0), acked_server_seq(0),
resend_push_usecs(DEFAULT_RESEND_PUSH_USECS), resend_push_usecs(DEFAULT_RESEND_PUSH_USECS),
next_push_max_frame_size(1024), next_push_max_frame_size(1024),
max_frame_size(1024), max_frame_size(1024),
bytes_received(0), bytes_received(0),
bytes_sent(0) { } bytes_sent(0) {}
void IPStackSimulator::dispatch_on_listen_accept( void IPStackSimulator::dispatch_on_listen_accept(
struct evconnlistener* listener, evutil_socket_t fd, struct evconnlistener* listener, evutil_socket_t fd,
struct sockaddr *address, int socklen, void* ctx) { struct sockaddr* address, int socklen, void* ctx) {
reinterpret_cast<IPStackSimulator*>(ctx)->on_listen_accept( reinterpret_cast<IPStackSimulator*>(ctx)->on_listen_accept(
listener, fd, address, socklen); listener, fd, address, socklen);
} }
@@ -168,7 +151,7 @@ void IPStackSimulator::on_listen_accept(struct evconnlistener* listener,
int listen_fd = evconnlistener_get_fd(listener); int listen_fd = evconnlistener_get_fd(listener);
ip_stack_simulator_log.info("Virtual network fd %d connected via fd %d", fd, listen_fd); ip_stack_simulator_log.info("Virtual network fd %d connected via fd %d", fd, listen_fd);
struct bufferevent *bev = bufferevent_socket_new(this->base.get(), fd, struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd,
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
shared_ptr<IPClient> c(new IPClient(bev)); shared_ptr<IPClient> c(new IPClient(bev));
c->sim = this; c->sim = this;
@@ -191,8 +174,6 @@ void IPStackSimulator::on_listen_error(struct evconnlistener* listener) {
event_base_loopexit(this->base.get(), nullptr); event_base_loopexit(this->base.get(), nullptr);
} }
void IPStackSimulator::dispatch_on_client_input( void IPStackSimulator::dispatch_on_client_input(
struct bufferevent* bev, void* ctx) { struct bufferevent* bev, void* ctx) {
reinterpret_cast<IPStackSimulator*>(ctx)->on_client_input(bev); reinterpret_cast<IPStackSimulator*>(ctx)->on_client_input(bev);
@@ -250,8 +231,6 @@ void IPStackSimulator::on_client_error(struct bufferevent* bev,
} }
} }
void IPStackSimulator::on_client_frame( void IPStackSimulator::on_client_frame(
shared_ptr<IPClient> c, const string& frame) { shared_ptr<IPClient> c, const string& frame) {
if (ip_stack_simulator_log.info("Virtual network sent frame")) { if (ip_stack_simulator_log.info("Virtual network sent frame")) {
@@ -310,8 +289,6 @@ void IPStackSimulator::on_client_frame(
} }
} }
void IPStackSimulator::on_client_arp_frame( void IPStackSimulator::on_client_arp_frame(
shared_ptr<IPClient> c, const FrameInfo& fi) { shared_ptr<IPClient> c, const FrameInfo& fi) {
if (fi.arp->hwaddr_len != 6 || if (fi.arp->hwaddr_len != 6 ||
@@ -330,7 +307,7 @@ void IPStackSimulator::on_client_arp_frame(
} }
if (c->ipv4_addr == 0) { if (c->ipv4_addr == 0) {
c->ipv4_addr = *reinterpret_cast<const be_uint32_t*>( c->ipv4_addr = *reinterpret_cast<const be_uint32_t*>(
reinterpret_cast<const uint8_t*>(fi.payload) + 6); reinterpret_cast<const uint8_t*>(fi.payload) + 6);
} }
EthernetHeader r_ether; EthernetHeader r_ether;
@@ -382,8 +359,6 @@ void IPStackSimulator::on_client_arp_frame(
} }
} }
void IPStackSimulator::on_client_udp_frame( void IPStackSimulator::on_client_udp_frame(
shared_ptr<IPClient> c, const FrameInfo& fi) { shared_ptr<IPClient> c, const FrameInfo& fi) {
// We only implement the DNS server here // We only implement the DNS server here
@@ -453,20 +428,18 @@ void IPStackSimulator::on_client_udp_frame(
} }
} }
uint64_t IPStackSimulator::tcp_conn_key_for_connection( uint64_t IPStackSimulator::tcp_conn_key_for_connection(
const IPClient::TCPConnection& conn) { const IPClient::TCPConnection& conn) {
return (static_cast<uint64_t>(conn.server_addr) << 32) | return (static_cast<uint64_t>(conn.server_addr) << 32) |
(static_cast<uint64_t>(conn.server_port) << 16) | (static_cast<uint64_t>(conn.server_port) << 16) |
static_cast<uint64_t>(conn.client_port); static_cast<uint64_t>(conn.client_port);
} }
uint64_t IPStackSimulator::tcp_conn_key_for_client_frame( uint64_t IPStackSimulator::tcp_conn_key_for_client_frame(
const IPv4Header& ipv4, const TCPHeader& tcp) { const IPv4Header& ipv4, const TCPHeader& tcp) {
return (static_cast<uint64_t>(ipv4.dest_addr) << 32) | return (static_cast<uint64_t>(ipv4.dest_addr) << 32) |
(static_cast<uint64_t>(tcp.dest_port) << 16) | (static_cast<uint64_t>(tcp.dest_port) << 16) |
static_cast<uint64_t>(tcp.src_port); static_cast<uint64_t>(tcp.src_port);
} }
uint64_t IPStackSimulator::tcp_conn_key_for_client_frame(const FrameInfo& fi) { uint64_t IPStackSimulator::tcp_conn_key_for_client_frame(const FrameInfo& fi) {
@@ -476,14 +449,12 @@ uint64_t IPStackSimulator::tcp_conn_key_for_client_frame(const FrameInfo& fi) {
return IPStackSimulator::tcp_conn_key_for_client_frame(*fi.ipv4, *fi.tcp); return IPStackSimulator::tcp_conn_key_for_client_frame(*fi.ipv4, *fi.tcp);
} }
void IPStackSimulator::on_client_tcp_frame( void IPStackSimulator::on_client_tcp_frame(
shared_ptr<IPClient> c, const FrameInfo& fi) { shared_ptr<IPClient> c, const FrameInfo& fi) {
ip_stack_simulator_log.info("Virtual network sent TCP frame (seq=%08" PRIX32 ", ack=%08" PRIX32 ")", ip_stack_simulator_log.info("Virtual network sent TCP frame (seq=%08" PRIX32 ", ack=%08" PRIX32 ")",
fi.tcp->seq_num.load(), fi.tcp->ack_num.load()); fi.tcp->seq_num.load(), fi.tcp->ack_num.load());
if (fi.tcp->flags & (TCPHeader::Flag::NS | TCPHeader::Flag::CWR | if (fi.tcp->flags & (TCPHeader::Flag::NS | TCPHeader::Flag::CWR | TCPHeader::Flag::ECE | TCPHeader::Flag::URG)) {
TCPHeader::Flag::ECE | TCPHeader::Flag::URG)) {
throw runtime_error("unsupported flag in TCP packet"); throw runtime_error("unsupported flag in TCP packet");
} }
@@ -536,7 +507,6 @@ void IPStackSimulator::on_client_tcp_frame(
} }
} }
uint64_t key = this->tcp_conn_key_for_client_frame(fi); uint64_t key = this->tcp_conn_key_for_client_frame(fi);
auto emplace_ret = c->tcp_connections.emplace(key, IPClient::TCPConnection()); auto emplace_ret = c->tcp_connections.emplace(key, IPClient::TCPConnection());
auto& conn = emplace_ret.first->second; auto& conn = emplace_ret.first->second;
@@ -645,17 +615,18 @@ void IPStackSimulator::on_client_tcp_frame(
c->tcp_connections.erase(key); c->tcp_connections.erase(key);
conn_valid = false; conn_valid = false;
// Note: The PSH flag isn't required to be set on all packets that contain // Note: The PSH flag isn't required to be set on all packets that contain
// data. The PSH flag just means "tell the application that data is // data. The PSH flag just means "tell the application that data is
// available", so some senders only set the PSH flag on the last frame of a // available", so some senders only set the PSH flag on the last frame of a
// large segment of data, since the application wouldn't be able to process // large segment of data, since the application wouldn't be able to process
// the segment until all of it is available. newserv can handle incomplete // the segment until all of it is available. newserv can handle incomplete
// commands, so we just ignore the PSH flag and forward any data to the // commands, so we just ignore the PSH flag and forward any data to the
// server immediately. // server immediately.
} else if (fi.payload_size != 0) { } else if (fi.payload_size != 0) {
string conn_str = ip_stack_simulator_log.should_log(LogLevel::WARNING) string conn_str = ip_stack_simulator_log.should_log(LogLevel::WARNING)
? this->str_for_tcp_connection(c, *conn) : ""; ? this->str_for_tcp_connection(c, *conn)
: "";
size_t payload_skip_bytes; size_t payload_skip_bytes;
if (fi.tcp->seq_num == conn->next_client_seq) { if (fi.tcp->seq_num == conn->next_client_seq) {
@@ -939,8 +910,6 @@ void IPStackSimulator::on_server_error(
} }
} }
void IPStackSimulator::log_frame(const string& data) const { void IPStackSimulator::log_frame(const string& data) const {
if (this->pcap_text_log_file) { if (this->pcap_text_log_file) {
print_data(this->pcap_text_log_file, data, 0, nullptr, print_data(this->pcap_text_log_file, data, 0, nullptr,
+14 -16
View File
@@ -1,18 +1,16 @@
#include <stdint.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdint.h>
#include <deque> #include <deque>
#include <string>
#include <phosg/Process.hh>
#include <phosg/Filesystem.hh> #include <phosg/Filesystem.hh>
#include <phosg/Process.hh>
#include <string>
#include "IPFrameInfo.hh" #include "IPFrameInfo.hh"
#include "Server.hh"
#include "ProxyServer.hh" #include "ProxyServer.hh"
#include "Server.hh"
#include "ServerState.hh" #include "ServerState.hh"
class IPStackSimulator { class IPStackSimulator {
public: public:
IPStackSimulator( IPStackSimulator(
@@ -31,10 +29,10 @@ private:
std::shared_ptr<struct event_base> base; std::shared_ptr<struct event_base> base;
std::shared_ptr<ServerState> state; std::shared_ptr<ServerState> state;
using unique_listener = std::unique_ptr<struct evconnlistener, void(*)(struct evconnlistener*)>; using unique_listener = std::unique_ptr<struct evconnlistener, void (*)(struct evconnlistener*)>;
using unique_bufferevent = std::unique_ptr<struct bufferevent, void(*)(struct bufferevent*)>; using unique_bufferevent = std::unique_ptr<struct bufferevent, void (*)(struct bufferevent*)>;
using unique_evbuffer = std::unique_ptr<struct evbuffer, void(*)(struct evbuffer*)>; using unique_evbuffer = std::unique_ptr<struct evbuffer, void (*)(struct evbuffer*)>;
using unique_event = std::unique_ptr<struct event, void(*)(struct event*)>; using unique_event = std::unique_ptr<struct event, void (*)(struct event*)>;
struct IPClient { struct IPClient {
IPStackSimulator* sim; IPStackSimulator* sim;
@@ -93,19 +91,19 @@ private:
static std::string str_for_ipv4_netloc(uint32_t addr, uint16_t port); static std::string str_for_ipv4_netloc(uint32_t addr, uint16_t port);
static std::string str_for_tcp_connection(std::shared_ptr<const IPClient> c, static std::string str_for_tcp_connection(std::shared_ptr<const IPClient> c,
const IPClient::TCPConnection& conn); const IPClient::TCPConnection& conn);
static void dispatch_on_listen_accept(struct evconnlistener* listener, static void dispatch_on_listen_accept(struct evconnlistener* listener,
evutil_socket_t fd, struct sockaddr *address, int socklen, void* ctx); evutil_socket_t fd, struct sockaddr* address, int socklen, void* ctx);
void on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, void on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd,
struct sockaddr *address, int socklen); struct sockaddr* address, int socklen);
static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx); static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx);
void on_listen_error(struct evconnlistener* listener); void on_listen_error(struct evconnlistener* listener);
static void dispatch_on_client_input(struct bufferevent* bev, void* ctx); static void dispatch_on_client_input(struct bufferevent* bev, void* ctx);
void on_client_input(struct bufferevent* bev); void on_client_input(struct bufferevent* bev);
static void dispatch_on_client_error(struct bufferevent* bev, short events, static void dispatch_on_client_error(struct bufferevent* bev, short events,
void* ctx); void* ctx);
void on_client_error(struct bufferevent* bev, short events); void on_client_error(struct bufferevent* bev, short events);
void on_client_frame(std::shared_ptr<IPClient> c, const std::string& frame); void on_client_frame(std::shared_ptr<IPClient> c, const std::string& frame);
@@ -114,13 +112,13 @@ private:
void on_client_tcp_frame(std::shared_ptr<IPClient> c, const FrameInfo& fi); void on_client_tcp_frame(std::shared_ptr<IPClient> c, const FrameInfo& fi);
static void dispatch_on_resend_push(evutil_socket_t fd, short events, static void dispatch_on_resend_push(evutil_socket_t fd, short events,
void* ctx); void* ctx);
void on_resend_push(std::shared_ptr<IPClient> c, IPClient::TCPConnection& conn); void on_resend_push(std::shared_ptr<IPClient> c, IPClient::TCPConnection& conn);
static void dispatch_on_server_input(struct bufferevent* bev, void* ctx); static void dispatch_on_server_input(struct bufferevent* bev, void* ctx);
void on_server_input(std::shared_ptr<IPClient> c, IPClient::TCPConnection& conn); void on_server_input(std::shared_ptr<IPClient> c, IPClient::TCPConnection& conn);
static void dispatch_on_server_error(struct bufferevent* bev, short events, static void dispatch_on_server_error(struct bufferevent* bev, short events,
void* ctx); void* ctx);
void on_server_error(std::shared_ptr<IPClient> c, IPClient::TCPConnection& conn, short events); void on_server_error(std::shared_ptr<IPClient> c, IPClient::TCPConnection& conn, short events);
void send_pending_push_frame( void send_pending_push_frame(
+184 -120
View File
@@ -5,8 +5,6 @@
using namespace std; using namespace std;
ItemCreator::ItemCreator( ItemCreator::ItemCreator(
shared_ptr<const CommonItemSet> common_item_set, shared_ptr<const CommonItemSet> common_item_set,
shared_ptr<const RareItemSet> rare_item_set, shared_ptr<const RareItemSet> rare_item_set,
@@ -20,28 +18,26 @@ ItemCreator::ItemCreator(
uint8_t section_id, uint8_t section_id,
uint32_t random_seed, uint32_t random_seed,
shared_ptr<const Restrictions> restrictions) shared_ptr<const Restrictions> restrictions)
: log("[ItemCreator] "), : log("[ItemCreator] "),
episode(episode), episode(episode),
mode(mode), mode(mode),
difficulty(difficulty), difficulty(difficulty),
section_id(section_id), section_id(section_id),
common_item_set(common_item_set), common_item_set(common_item_set),
rare_item_set(rare_item_set), rare_item_set(rare_item_set),
armor_random_set(armor_random_set), armor_random_set(armor_random_set),
tool_random_set(tool_random_set), tool_random_set(tool_random_set),
weapon_random_set(weapon_random_set), weapon_random_set(weapon_random_set),
item_parameter_table(item_parameter_table), item_parameter_table(item_parameter_table),
pt(&this->common_item_set->get_table( pt(&this->common_item_set->get_table(
this->episode, this->mode, this->difficulty, this->section_id)), this->episode, this->mode, this->difficulty, this->section_id)),
rt(&this->rare_item_set->get_table( rt(&this->rare_item_set->get_table(
this->episode, this->mode, this->difficulty, this->section_id)), this->episode, this->mode, this->difficulty, this->section_id)),
restrictions(restrictions), restrictions(restrictions),
random_crypt(random_seed) { random_crypt(random_seed) {
print_data(stderr, this->pt, sizeof(*this->pt)); print_data(stderr, this->pt, sizeof(*this->pt));
} }
bool ItemCreator::are_rare_drops_allowed() const { bool ItemCreator::are_rare_drops_allowed() const {
// Note: The client has an additional check here, which appears to be a subtle // Note: The client has an additional check here, which appears to be a subtle
// anti-cheating measure. There is a flag on the client, initially zero, which // anti-cheating measure. There is a flag on the client, initially zero, which
@@ -74,23 +70,23 @@ uint8_t ItemCreator::normalize_area_number(uint8_t area) const {
throw logic_error("this should be impossible"); throw logic_error("this should be impossible");
case Episode::EP2: { case Episode::EP2: {
static const vector<uint8_t> area_subs = { static const vector<uint8_t> area_subs = {
0x01, // 13 (VR Temple Alpha) 0x01, // 13 (VR Temple Alpha)
0x02, // 14 (VR Temple Beta) 0x02, // 14 (VR Temple Beta)
0x03, // 15 (VR Spaceship Alpha) 0x03, // 15 (VR Spaceship Alpha)
0x04, // 16 (VR Spaceship Beta) 0x04, // 16 (VR Spaceship Beta)
0x08, // 17 (Central Control Area) 0x08, // 17 (Central Control Area)
0x05, // 18 (Jungle North) 0x05, // 18 (Jungle North)
0x06, // 19 (Jungle South) 0x06, // 19 (Jungle South)
0x07, // 1A (Mountain) 0x07, // 1A (Mountain)
0x08, // 1B (Seaside) 0x08, // 1B (Seaside)
0x09, // 1C (Seabed Upper) 0x09, // 1C (Seabed Upper)
0x0A, // 1D (Seabed Lower) 0x0A, // 1D (Seabed Lower)
0x09, // 1E (Gal Gryphon) 0x09, // 1E (Gal Gryphon)
0x0A, // 1F (Olga Flow) 0x0A, // 1F (Olga Flow)
0x03, // 20 (Barba Ray) 0x03, // 20 (Barba Ray)
0x05, // 21 (Gol Dragon) 0x05, // 21 (Gol Dragon)
0x08, // 22 (Seaside Night) 0x08, // 22 (Seaside Night)
0x0A, // 23 (Tower) 0x0A, // 23 (Tower)
}; };
if ((area >= 0x13) && (area < 0x24)) { if ((area >= 0x13) && (area < 0x24)) {
return area_subs.at(area - 0x13); return area_subs.at(area - 0x13);
@@ -109,8 +105,6 @@ uint8_t ItemCreator::normalize_area_number(uint8_t area) const {
} }
} }
ItemData ItemCreator::on_box_item_drop(uint8_t area) { ItemData ItemCreator::on_box_item_drop(uint8_t area) {
return this->on_box_item_drop_with_norm_area(normalize_area_number(area) - 1); return this->on_box_item_drop_with_norm_area(normalize_area_number(area) - 1);
} }
@@ -174,8 +168,10 @@ ItemData ItemCreator::on_monster_item_drop_with_norm_area(
ItemData item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type); ItemData item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type);
if (item.empty()) { if (item.empty()) {
uint32_t item_class_determinant = this->should_allow_meseta_drops() uint32_t item_class_determinant =
? this->rand_int(3) : (this->rand_int(2) + 1); this->should_allow_meseta_drops()
? this->rand_int(3)
: (this->rand_int(2) + 1);
uint32_t item_class; uint32_t item_class;
switch (item_class_determinant) { switch (item_class_determinant) {
@@ -193,7 +189,7 @@ ItemData ItemCreator::on_monster_item_drop_with_norm_area(
} }
this->log.info("Rare drop not chosen; item class determinant is %" PRIu32 "; item class is %" PRIu32, this->log.info("Rare drop not chosen; item class determinant is %" PRIu32 "; item class is %" PRIu32,
item_class_determinant, item_class); item_class_determinant, item_class);
switch (item_class) { switch (item_class) {
case 0: // Weapon case 0: // Weapon
@@ -214,7 +210,8 @@ ItemData ItemCreator::on_monster_item_drop_with_norm_area(
case 5: // Meseta case 5: // Meseta
item.data1[0] = 0x04; item.data1[0] = 0x04;
item.data2d = this->choose_meseta_amount( item.data2d = this->choose_meseta_amount(
this->pt->enemy_meseta_ranges, enemy_type) & 0xFFFF; this->pt->enemy_meseta_ranges, enemy_type) &
0xFFFF;
break; break;
default: default:
return item; return item;
@@ -246,8 +243,6 @@ ItemData ItemCreator::check_rare_specs_and_create_rare_box_item(
return item; return item;
} }
uint32_t ItemCreator::rand_int(uint64_t max) { uint32_t ItemCreator::rand_int(uint64_t max) {
return this->random_crypt.next() % max; return this->random_crypt.next() % max;
} }
@@ -348,7 +343,7 @@ void ItemCreator::generate_rare_weapon_bonuses(
// item.data1[z + 7] by 5 and multiplies it by 5 again if bonus_type is 5 // item.data1[z + 7] by 5 and multiplies it by 5 again if bonus_type is 5
// (Hit). Why this is done is unclear, because item.data1[z + 7] must // (Hit). Why this is done is unclear, because item.data1[z + 7] must
// already be a multiple of 5. // already be a multiple of 5.
} }
this->deduplicate_weapon_bonuses(item); this->deduplicate_weapon_bonuses(item);
} }
@@ -403,8 +398,6 @@ void ItemCreator::set_item_unidentified_flag_if_challenge(ItemData& item) const
} }
} }
void ItemCreator::set_tool_item_amount_to_1(ItemData& item) const { void ItemCreator::set_tool_item_amount_to_1(ItemData& item) const {
if (item.data1[0] == 0x03) { if (item.data1[0] == 0x03) {
item.set_tool_item_amount(1); item.set_tool_item_amount(1);
@@ -418,8 +411,6 @@ void ItemCreator::clear_tool_item_if_invalid(ItemData& item) {
} }
} }
void ItemCreator::clear_item_if_restricted(ItemData& item) const { void ItemCreator::clear_item_if_restricted(ItemData& item) const {
if (this->item_parameter_table->is_item_rare(item) && !this->are_rare_drops_allowed()) { if (this->item_parameter_table->is_item_rare(item) && !this->are_rare_drops_allowed()) {
this->log.info("Restricted: item is rare, but rares not allowed"); this->log.info("Restricted: item is rare, but rares not allowed");
@@ -432,9 +423,8 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
// Note: PSO GC doesn't check for 0x61 or 0x62 here since those items // Note: PSO GC doesn't check for 0x61 or 0x62 here since those items
// (HP/Resurrection and TP/Resurrection) only exist on BB. // (HP/Resurrection and TP/Resurrection) only exist on BB.
if (item.data1[0] == 1) { if (item.data1[0] == 1) {
if ((item.data1[1] == 3) && ( if ((item.data1[1] == 3) && (((item.data1[2] >= 0x33) && (item.data1[2] <= 0x38)) ||
((item.data1[2] >= 0x33) && (item.data1[2] <= 0x38)) || (item.data1[2] == 0x61) || (item.data1[2] == 0x62))) {
(item.data1[2] == 0x61) || (item.data1[2] == 0x62))) {
this->log.info("Restricted: restore items not allowed in Challenge mode"); this->log.info("Restricted: restore items not allowed in Challenge mode");
item.clear(); item.clear();
} }
@@ -486,7 +476,7 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
break; break;
case Restrictions::TechDiskMode::LIMIT_LEVEL: case Restrictions::TechDiskMode::LIMIT_LEVEL:
this->log.info("Restricted: tech disk level limited to %hhu", this->log.info("Restricted: tech disk level limited to %hhu",
static_cast<uint8_t>(this->restrictions->max_tech_disk_level + 1)); static_cast<uint8_t>(this->restrictions->max_tech_disk_level + 1));
if (this->restrictions->max_tech_disk_level == 0) { if (this->restrictions->max_tech_disk_level == 0) {
item.data1[2] = 0; item.data1[2] = 0;
} else { } else {
@@ -513,8 +503,6 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
} }
} }
void ItemCreator::generate_common_item_variances( void ItemCreator::generate_common_item_variances(
uint32_t norm_area, ItemData& item) { uint32_t norm_area, ItemData& item) {
switch (item.data1[0]) { switch (item.data1[0]) {
@@ -543,7 +531,8 @@ void ItemCreator::generate_common_item_variances(
break; break;
case 4: case 4:
item.data2d = this->choose_meseta_amount( item.data2d = this->choose_meseta_amount(
this->pt->box_meseta_ranges, norm_area) & 0xFFFF; this->pt->box_meseta_ranges, norm_area) &
0xFFFF;
break; break;
default: default:
// Note: The original code does the following here: // Note: The original code does the following here:
@@ -590,8 +579,6 @@ void ItemCreator::generate_common_armor_slot_count(ItemData& item) {
this->pt->armor_slot_count_prob_table); this->pt->armor_slot_count_prob_table);
} }
void ItemCreator::generate_common_tool_variances( void ItemCreator::generate_common_tool_variances(
uint32_t area_norm, ItemData& item) { uint32_t area_norm, ItemData& item) {
item.clear(); item.clear();
@@ -633,8 +620,6 @@ void ItemCreator::generate_common_tool_type(
item.data1[2] = data.second; item.data1[2] = data.second;
} }
void ItemCreator::generate_common_mag_variances(ItemData& item) const { void ItemCreator::generate_common_mag_variances(ItemData& item) const {
if (item.data1[0] == 0x02) { if (item.data1[0] == 0x02) {
item.data1[1] = 0x00; item.data1[1] = 0x00;
@@ -642,8 +627,6 @@ void ItemCreator::generate_common_mag_variances(ItemData& item) const {
} }
} }
void ItemCreator::generate_common_weapon_variances( void ItemCreator::generate_common_weapon_variances(
uint8_t area_norm, ItemData& item) { uint8_t area_norm, ItemData& item) {
item.clear(); item.clear();
@@ -730,8 +713,6 @@ uint8_t ItemCreator::choose_weapon_special(uint8_t det) {
return 0; return 0;
} }
void ItemCreator::generate_unit_weights_tables() { void ItemCreator::generate_unit_weights_tables() {
// Note: This part of the function was originally in a different function, // Note: This part of the function was originally in a different function,
// since it had another callsite. Unlike the original code, we generate these // since it had another callsite. Unlike the original code, we generate these
@@ -817,11 +798,9 @@ void ItemCreator::generate_common_unit_variances(uint8_t det, ItemData& item) {
} }
} }
// Returns a weighted random result, indicating the chosen position in the // Returns a weighted random result, indicating the chosen position in the
// weighted table. // weighted table.
// //
// For example, an input table of 40 40 40 40 would be equally likely to return // For example, an input table of 40 40 40 40 would be equally likely to return
// 0, 1, 2, or 3. An input table of 40 40 80 would return 2 50% of the time, and // 0, 1, 2, or 3. An input table of 40 40 80 would return 2 50% of the time, and
// 0 or 1 each 25% of the time. // 0 or 1 each 25% of the time.
@@ -857,11 +836,9 @@ template <typename IntT, size_t X, size_t Y>
IntT ItemCreator::get_rand_from_weighted_tables_2d_vertical( IntT ItemCreator::get_rand_from_weighted_tables_2d_vertical(
const parray<parray<IntT, X>, Y>& tables, size_t offset) { const parray<parray<IntT, X>, Y>& tables, size_t offset) {
return ItemCreator::get_rand_from_weighted_tables<IntT>(tables[0].data(), return ItemCreator::get_rand_from_weighted_tables<IntT>(tables[0].data(),
offset, Y, X); offset, Y, X);
} }
// Note: There are clearly better ways of doing this, but this implementation // Note: There are clearly better ways of doing this, but this implementation
// closely follows what the original code in the client does. // closely follows what the original code in the client does.
template <typename ItemT, size_t MaxCount> template <typename ItemT, size_t MaxCount>
@@ -869,7 +846,7 @@ struct ProbabilityTable {
ItemT items[MaxCount]; ItemT items[MaxCount];
size_t count; size_t count;
ProbabilityTable() : count(0) { } ProbabilityTable() : count(0) {}
void push(ItemT item) { void push(ItemT item) {
if (this->count == MaxCount) { if (this->count == MaxCount) {
@@ -999,7 +976,7 @@ void ItemCreator::generate_armor_shop_armors(
} }
pt.shuffle(this->random_crypt); pt.shuffle(this->random_crypt);
for (size_t items_generated = 0; items_generated < num_items; ) { for (size_t items_generated = 0; items_generated < num_items;) {
ItemData item; ItemData item;
item.data1[0] = 1; item.data1[0] = 1;
item.data1[1] = 1; item.data1[1] = 1;
@@ -1044,7 +1021,7 @@ void ItemCreator::generate_armor_shop_shields(
} }
pt.shuffle(this->random_crypt); pt.shuffle(this->random_crypt);
for (size_t items_generated = 0; items_generated < num_items; ) { for (size_t items_generated = 0; items_generated < num_items;) {
ItemData item; ItemData item;
item.data1[0] = 1; item.data1[0] = 1;
item.data1[1] = 2; item.data1[1] = 2;
@@ -1072,7 +1049,7 @@ void ItemCreator::generate_armor_shop_units(
return; // num_items = 0 return; // num_items = 0
} else if (player_level < 26) { } else if (player_level < 26) {
num_items = 3; num_items = 3;
} else if (player_level < 43) { } else if (player_level < 43) {
num_items = 5; num_items = 5;
} else { } else {
num_items = 6; num_items = 6;
@@ -1088,7 +1065,7 @@ void ItemCreator::generate_armor_shop_units(
} }
pt.shuffle(this->random_crypt); pt.shuffle(this->random_crypt);
for (size_t items_generated = 0; items_generated < num_items; ) { for (size_t items_generated = 0; items_generated < num_items;) {
ItemData item; ItemData item;
item.data1[0] = 1; item.data1[0] = 1;
item.data1[1] = 3; item.data1[1] = 3;
@@ -1100,8 +1077,6 @@ void ItemCreator::generate_armor_shop_units(
} }
} }
vector<ItemData> ItemCreator::generate_tool_shop_contents(size_t player_level) { vector<ItemData> ItemCreator::generate_tool_shop_contents(size_t player_level) {
vector<ItemData> shop; vector<ItemData> shop;
this->generate_common_tool_shop_recovery_items(shop, player_level); this->generate_common_tool_shop_recovery_items(shop, player_level);
@@ -1126,22 +1101,22 @@ size_t ItemCreator::get_table_index_for_tool_shop(size_t player_level) {
} }
static const vector<pair<uint8_t, uint8_t>> tool_item_defs({ static const vector<pair<uint8_t, uint8_t>> tool_item_defs({
{0x00, 0x00}, {0x00, 0x00},
{0x00, 0x01}, {0x00, 0x01},
{0x00, 0x02}, {0x00, 0x02},
{0x01, 0x00}, {0x01, 0x00},
{0x01, 0x01}, {0x01, 0x01},
{0x01, 0x02}, {0x01, 0x02},
{0x06, 0x00}, {0x06, 0x00},
{0x06, 0x01}, {0x06, 0x01},
{0x03, 0x00}, {0x03, 0x00},
{0x04, 0x00}, {0x04, 0x00},
{0x05, 0x00}, {0x05, 0x00},
{0x07, 0x00}, {0x07, 0x00},
{0x08, 0x00}, {0x08, 0x00},
{0x09, 0x00}, {0x09, 0x00},
{0x0A, 0x00}, {0x0A, 0x00},
{0xFF, 0xFF}, {0xFF, 0xFF},
}); });
void ItemCreator::generate_common_tool_shop_recovery_items( void ItemCreator::generate_common_tool_shop_recovery_items(
@@ -1288,8 +1263,6 @@ void ItemCreator::choose_tech_disk_level_for_tool_shop(
} }
} }
vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level) { vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level) {
size_t num_items; size_t num_items;
if (player_level < 11) { if (player_level < 11) {
@@ -1350,8 +1323,16 @@ vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level)
uint8_t which = pt.pop(); uint8_t which = pt.pop();
if (which == 0x39) { if (which == 0x39) {
static const vector<pair<uint8_t, uint8_t>> defs({ static const vector<pair<uint8_t, uint8_t>> defs({
{0x28, 0x00}, {0x2A, 0x00}, {0x2B, 0x00}, {0x35, 0x00}, {0x52, 0x00}, {0x28, 0x00},
{0x48, 0x00}, {0x64, 0x00}, {0x59, 0x00}, {0x8A, 0x00}, {0x99, 0x00}, {0x2A, 0x00},
{0x2B, 0x00},
{0x35, 0x00},
{0x52, 0x00},
{0x48, 0x00},
{0x64, 0x00},
{0x59, 0x00},
{0x8A, 0x00},
{0x99, 0x00},
}); });
const auto& def = defs.at(this->section_id); const auto& def = defs.at(this->section_id);
item.data1[0] = 0; item.data1[0] = 0;
@@ -1360,8 +1341,16 @@ vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level)
} else if (which == 0x3A) { } else if (which == 0x3A) {
static const vector<pair<uint8_t, uint8_t>> defs({ static const vector<pair<uint8_t, uint8_t>> defs({
{0x99, 0x00}, {0x64, 0x00}, {0x8A, 0x00}, {0x28, 0x00}, {0x59, 0x00}, {0x99, 0x00},
{0x2B, 0x00}, {0x52, 0x00}, {0x2A, 0x00}, {0x48, 0x00}, {0x35, 0x00}, {0x64, 0x00},
{0x8A, 0x00},
{0x28, 0x00},
{0x59, 0x00},
{0x2B, 0x00},
{0x52, 0x00},
{0x2A, 0x00},
{0x48, 0x00},
{0x35, 0x00},
}); });
const auto& def = defs.at(this->section_id); const auto& def = defs.at(this->section_id);
item.data1[0] = 0; item.data1[0] = 0;
@@ -1370,21 +1359,78 @@ vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level)
} else { } else {
static const vector<pair<uint8_t, uint8_t>> defs({ static const vector<pair<uint8_t, uint8_t>> defs({
{0x01, 0x00}, {0x01, 0x01}, {0x01, 0x02}, {0x01, 0x03}, {0x01, 0x04}, {0x01, 0x00},
{0x03, 0x00}, {0x03, 0x01}, {0x03, 0x02}, {0x03, 0x03}, {0x03, 0x04}, {0x01, 0x01},
{0x02, 0x00}, {0x02, 0x01}, {0x02, 0x02}, {0x02, 0x03}, {0x02, 0x04}, {0x01, 0x02},
{0x05, 0x00}, {0x05, 0x01}, {0x05, 0x02}, {0x05, 0x03}, {0x05, 0x04}, {0x01, 0x03},
{0x04, 0x00}, {0x04, 0x01}, {0x04, 0x02}, {0x04, 0x03}, {0x04, 0x04}, {0x01, 0x04},
{0x06, 0x00}, {0x06, 0x01}, {0x06, 0x02}, {0x06, 0x03}, {0x06, 0x04}, {0x03, 0x00},
{0x07, 0x00}, {0x07, 0x01}, {0x07, 0x02}, {0x07, 0x03}, {0x07, 0x04}, {0x03, 0x01},
{0x08, 0x00}, {0x08, 0x01}, {0x08, 0x02}, {0x08, 0x03}, {0x08, 0x04}, {0x03, 0x02},
{0x09, 0x00}, {0x09, 0x01}, {0x09, 0x02}, {0x09, 0x03}, {0x09, 0x04}, {0x03, 0x03},
{0x0A, 0x00}, {0x0A, 0x01}, {0x0A, 0x02}, {0x0A, 0x03}, {0x0B, 0x00}, {0x03, 0x04},
{0x0B, 0x01}, {0x0B, 0x02}, {0x0B, 0x03}, {0x0C, 0x00}, {0x0C, 0x01}, {0x02, 0x00},
{0x0C, 0x02}, {0x0C, 0x03}, {0xFF, 0xFF}, {0xFF, 0xFF}, {0x01, 0x05}, {0x02, 0x01},
{0x02, 0x05}, {0x06, 0x05}, {0x08, 0x05}, {0x0A, 0x04}, {0x0C, 0x04}, {0x02, 0x02},
{0x0B, 0x04}, {0x01, 0x06}, {0x03, 0x05}, {0x07, 0x05}, {0x0A, 0x05}, {0x02, 0x03},
{0x0C, 0x05}, {0x0B, 0x05}, {0x02, 0x04},
{0x05, 0x00},
{0x05, 0x01},
{0x05, 0x02},
{0x05, 0x03},
{0x05, 0x04},
{0x04, 0x00},
{0x04, 0x01},
{0x04, 0x02},
{0x04, 0x03},
{0x04, 0x04},
{0x06, 0x00},
{0x06, 0x01},
{0x06, 0x02},
{0x06, 0x03},
{0x06, 0x04},
{0x07, 0x00},
{0x07, 0x01},
{0x07, 0x02},
{0x07, 0x03},
{0x07, 0x04},
{0x08, 0x00},
{0x08, 0x01},
{0x08, 0x02},
{0x08, 0x03},
{0x08, 0x04},
{0x09, 0x00},
{0x09, 0x01},
{0x09, 0x02},
{0x09, 0x03},
{0x09, 0x04},
{0x0A, 0x00},
{0x0A, 0x01},
{0x0A, 0x02},
{0x0A, 0x03},
{0x0B, 0x00},
{0x0B, 0x01},
{0x0B, 0x02},
{0x0B, 0x03},
{0x0C, 0x00},
{0x0C, 0x01},
{0x0C, 0x02},
{0x0C, 0x03},
{0xFF, 0xFF},
{0xFF, 0xFF},
{0x01, 0x05},
{0x02, 0x05},
{0x06, 0x05},
{0x08, 0x05},
{0x0A, 0x04},
{0x0C, 0x04},
{0x0B, 0x04},
{0x01, 0x06},
{0x03, 0x05},
{0x07, 0x05},
{0x0A, 0x05},
{0x0C, 0x05},
{0x0B, 0x05},
}); });
const auto& def = defs.at(which); const auto& def = defs.at(which);
item.data1[0] = 0; item.data1[0] = 0;
@@ -1431,8 +1477,8 @@ void ItemCreator::generate_weapon_shop_item_grind(
uint8_t favored_weapon = favored_weapon_by_section_id.at(this->section_id); uint8_t favored_weapon = favored_weapon_by_section_id.at(this->section_id);
bool is_favored = (favored_weapon != 0xFF) && (item.data1[1] == favored_weapon); bool is_favored = (favored_weapon != 0xFF) && (item.data1[1] == favored_weapon);
const auto* range = is_favored const auto* range = is_favored
? this->weapon_random_set->get_favored_grind_range(table_index) ? this->weapon_random_set->get_favored_grind_range(table_index)
: this->weapon_random_set->get_standard_grind_range(table_index); : this->weapon_random_set->get_standard_grind_range(table_index);
const auto& weapon_def = this->item_parameter_table->get_weapon( const auto& weapon_def = this->item_parameter_table->get_weapon(
item.data1[1], item.data1[2]); item.data1[1], item.data1[2]);
@@ -1491,8 +1537,26 @@ void ItemCreator::generate_weapon_shop_item_special(
} }
static const array<int8_t, 20> bonus_values = { static const array<int8_t, 20> bonus_values = {
-50, -45, -40, -35, -30, -25, -20, -15, -10, -5, -50,
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, -45,
-40,
-35,
-30,
-25,
-20,
-15,
-10,
-5,
5,
10,
15,
20,
25,
30,
35,
40,
45,
50,
}; };
void ItemCreator::generate_weapon_shop_item_bonus1( void ItemCreator::generate_weapon_shop_item_bonus1(
+3 -5
View File
@@ -3,12 +3,10 @@
#include <memory> #include <memory>
#include "CommonItemSet.hh" #include "CommonItemSet.hh"
#include "RareItemSet.hh"
#include "ItemParameterTable.hh" #include "ItemParameterTable.hh"
#include "StaticGameData.hh"
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
#include "RareItemSet.hh"
#include "StaticGameData.hh"
struct ItemDropSub { struct ItemDropSub {
uint8_t override_area; uint8_t override_area;
@@ -137,7 +135,7 @@ private:
void generate_common_mag_variances(ItemData& item) const; void generate_common_mag_variances(ItemData& item) const;
void generate_common_weapon_variances(uint8_t area_norm, ItemData& item); void generate_common_weapon_variances(uint8_t area_norm, ItemData& item);
void generate_common_weapon_grind(ItemData& item, void generate_common_weapon_grind(ItemData& item,
uint8_t offset_within_subtype_range); uint8_t offset_within_subtype_range);
void generate_common_weapon_bonuses(ItemData& item, uint8_t area_norm); void generate_common_weapon_bonuses(ItemData& item, uint8_t area_norm);
void generate_common_weapon_special(ItemData& item, uint8_t area_norm); void generate_common_weapon_special(ItemData& item, uint8_t area_norm);
uint8_t choose_weapon_special(uint8_t det); uint8_t choose_weapon_special(uint8_t det);
+1054 -1065
View File
File diff suppressed because it is too large Load Diff
+10 -12
View File
@@ -1,12 +1,10 @@
#pragma once #pragma once
#include <string>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <string>
#include "Text.hh" #include "Text.hh"
constexpr uint32_t MESETA_IDENTIFIER = 0x00040000; constexpr uint32_t MESETA_IDENTIFIER = 0x00040000;
struct ItemMagStats { struct ItemMagStats {
@@ -21,15 +19,15 @@ struct ItemMagStats {
uint8_t color; uint8_t color;
ItemMagStats() ItemMagStats()
: iq(0), : iq(0),
synchro(40), synchro(40),
def(500), def(500),
pow(0), pow(0),
dex(0), dex(0),
mind(0), mind(0),
flags(0), flags(0),
photon_blasts(0), photon_blasts(0),
color(14) { } color(14) {}
inline uint16_t def_level() const { inline uint16_t def_level() const {
return this->def / 100; return this->def / 100;
+6 -13
View File
@@ -2,10 +2,8 @@
using namespace std; using namespace std;
ItemParameterTable::ItemParameterTable(shared_ptr<const string> data) ItemParameterTable::ItemParameterTable(shared_ptr<const string> data)
: data(data), r(*data) { : data(data), r(*data) {
size_t offset_table_offset = this->r.pget_u32l(this->data->size() - 0x10); size_t offset_table_offset = this->r.pget_u32l(this->data->size() - 0x10);
this->offsets = &r.pget<TableOffsets>(offset_table_offset); this->offsets = &r.pget<TableOffsets>(offset_table_offset);
} }
@@ -139,8 +137,6 @@ uint8_t ItemParameterTable::get_max_tech_level(uint8_t char_class, uint8_t tech_
return r.pget_u8(this->offsets->max_tech_level_table + tech_num * 12 + char_class); return r.pget_u8(this->offsets->max_tech_level_table + tech_num * 12 + char_class);
} }
const ItemParameterTable::ItemBase& ItemParameterTable::get_item_definition( const ItemParameterTable::ItemBase& ItemParameterTable::get_item_definition(
const ItemData& item) const { const ItemData& item) const {
switch (item.data1[0]) { switch (item.data1[0]) {
@@ -176,8 +172,8 @@ uint8_t ItemParameterTable::get_item_base_stars(const ItemData& item) const {
return this->get_item_stars(this->get_item_definition(item).id); return this->get_item_stars(this->get_item_definition(item).id);
} else if (item.data1[0] == 3) { } else if (item.data1[0] == 3) {
const auto& def = (item.data1[1] == 2) const auto& def = (item.data1[1] == 2)
? this->get_tool(2, item.data1[4]) ? this->get_tool(2, item.data1[4])
: this->get_tool(item.data1[1], item.data1[2]); : this->get_tool(item.data1[1], item.data1[2]);
return (def.item_flag & 0x80) ? 12 : 0; return (def.item_flag & 0x80) ? 12 : 0;
} else { } else {
return 0; return 0;
@@ -225,8 +221,6 @@ bool ItemParameterTable::is_unsealable_item(const ItemData& item) const {
return false; return false;
} }
size_t ItemParameterTable::price_for_item(const ItemData& item) const { size_t ItemParameterTable::price_for_item(const ItemData& item) const {
switch (item.data1[0]) { switch (item.data1[0]) {
case 0: { case 0: {
@@ -281,10 +275,9 @@ size_t ItemParameterTable::price_for_item(const ItemData& item) const {
const auto& def = this->get_armor_or_shield(item.data1[1], item.data1[2]); const auto& def = this->get_armor_or_shield(item.data1[1], item.data1[2]);
double power_factor = def.dfp + def.evp + def_bonus + evp_bonus; double power_factor = def.dfp + def.evp + def_bonus + evp_bonus;
double power_factor_floor = static_cast<int32_t>((power_factor * power_factor) / sale_divisor); double power_factor_floor = static_cast<int32_t>((power_factor * power_factor) / sale_divisor);
return power_factor_floor + ( return power_factor_floor + (70.0 *
70.0 * static_cast<double>(item.data1[5] + 1) *
static_cast<double>(item.data1[5] + 1) * static_cast<double>(def.required_level + 1));
static_cast<double>(def.required_level + 1));
} }
case 2: case 2:
+2 -4
View File
@@ -3,13 +3,11 @@
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include <string>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <string>
#include "Text.hh"
#include "ItemData.hh" #include "ItemData.hh"
#include "Text.hh"
class ItemParameterTable { class ItemParameterTable {
public: public:
+2 -4
View File
@@ -6,9 +6,7 @@
using namespace std; using namespace std;
/* These items all need some kind of special handling that hasn't been implemented yet.
/* these items all need some kind of special handling that hasn't been implemented yet.
030B04 = TP Material (?) 030B04 = TP Material (?)
030C00 = Cell Of MAG 502 030C00 = Cell Of MAG 502
@@ -167,7 +165,7 @@ void player_use_item(shared_ptr<Client> c, size_t item_index) {
// used item. On GC and later versions, this does not happen, so we should // used item. On GC and later versions, this does not happen, so we should
// delete the item here. // delete the item here.
bool should_delete_item = (c->version() != GameVersion::DC) && bool should_delete_item = (c->version() != GameVersion::DC) &&
(c->version() != GameVersion::PC); (c->version() != GameVersion::PC);
auto& item = c->game_data.player()->inventory.items[item_index]; auto& item = c->game_data.player()->inventory.items[item_index];
if (item.data.data1w[0] == 0x0203) { // technique disk if (item.data.data1w[0] == 0x0203) { // technique disk
-2
View File
@@ -8,6 +8,4 @@
#include "Client.hh" #include "Client.hh"
#include "StaticGameData.hh" #include "StaticGameData.hh"
void player_use_item(std::shared_ptr<Client> c, size_t item_index); void player_use_item(std::shared_ptr<Client> c, size_t item_index);
+2 -4
View File
@@ -8,8 +8,6 @@
using namespace std; using namespace std;
LevelTable::LevelTable(shared_ptr<const string> data, bool compressed) { LevelTable::LevelTable(shared_ptr<const string> data, bool compressed) {
if (compressed) { if (compressed) {
this->data.reset(new string(prs_decompress(*data))); this->data.reset(new string(prs_decompress(*data)));
@@ -30,8 +28,8 @@ const PlayerStats& LevelTable::base_stats_for_class(uint8_t char_class) const {
return this->table->base_stats[char_class]; return this->table->base_stats[char_class];
} }
const LevelTable::LevelStats& LevelTable::stats_for_level(uint8_t char_class, const LevelTable::LevelStats& LevelTable::stats_for_level(
uint8_t level) const { uint8_t char_class, uint8_t level) const {
if (char_class >= 12) { if (char_class >= 12) {
throw invalid_argument("invalid character class"); throw invalid_argument("invalid character class");
} }
+1 -3
View File
@@ -3,10 +3,8 @@
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include <string>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <string>
struct PlayerStats { struct PlayerStats {
le_uint16_t atp; le_uint16_t atp;
+9 -15
View File
@@ -5,14 +5,12 @@
#include <phosg/Filesystem.hh> #include <phosg/Filesystem.hh>
#include <phosg/Time.hh> #include <phosg/Time.hh>
#include "Loggers.hh"
#include "License.hh" #include "License.hh"
#include "Loggers.hh"
using namespace std; using namespace std;
License::License() : serial_number(0), privileges(0), ban_end_time(0) {}
License::License() : serial_number(0), privileges(0), ban_end_time(0) { }
string License::str() const { string License::str() const {
string ret = string_printf("License(serial_number=%" PRIu32, this->serial_number); string ret = string_printf("License(serial_number=%" PRIu32, this->serial_number);
@@ -39,12 +37,10 @@ string License::str() const {
return ret + ")"; return ret + ")";
} }
LicenseManager::LicenseManager() : filename(""), autosave(false) {}
LicenseManager::LicenseManager() : filename(""), autosave(false) { }
LicenseManager::LicenseManager(const string& filename) LicenseManager::LicenseManager(const string& filename)
: filename(filename), autosave(true) { : filename(filename), autosave(true) {
try { try {
auto licenses = load_vector_file<License>(this->filename); auto licenses = load_vector_file<License>(this->filename);
for (const auto& read_license : licenses) { for (const auto& read_license : licenses) {
@@ -62,7 +58,7 @@ LicenseManager::LicenseManager(const string& filename)
} catch (const cannot_open_file&) { } catch (const cannot_open_file&) {
license_log.warning("File %s does not exist; no licenses are registered", license_log.warning("File %s does not exist; no licenses are registered",
this->filename.c_str()); this->filename.c_str());
} }
} }
@@ -84,7 +80,7 @@ void LicenseManager::set_autosave(bool autosave) {
} }
shared_ptr<const License> LicenseManager::verify_pc(uint32_t serial_number, shared_ptr<const License> LicenseManager::verify_pc(uint32_t serial_number,
const string& access_key) const { const string& access_key) const {
try { try {
auto& license = this->serial_number_to_license.at(serial_number); auto& license = this->serial_number_to_license.at(serial_number);
if (!license->access_key.eq_n(access_key, 8)) { if (!license->access_key.eq_n(access_key, 8)) {
@@ -101,7 +97,7 @@ shared_ptr<const License> LicenseManager::verify_pc(uint32_t serial_number,
} }
shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number, shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
const string& access_key) const { const string& access_key) const {
try { try {
auto& license = this->serial_number_to_license.at(serial_number); auto& license = this->serial_number_to_license.at(serial_number);
if (!license->access_key.eq_n(access_key, 12)) { if (!license->access_key.eq_n(access_key, 12)) {
@@ -117,7 +113,7 @@ shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
} }
shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number, shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
const string& access_key, const string& password) const { const string& access_key, const string& password) const {
try { try {
auto& license = this->serial_number_to_license.at(serial_number); auto& license = this->serial_number_to_license.at(serial_number);
if (!license->access_key.eq_n(access_key, 12)) { if (!license->access_key.eq_n(access_key, 12)) {
@@ -136,7 +132,7 @@ shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
} }
shared_ptr<const License> LicenseManager::verify_bb(const string& username, shared_ptr<const License> LicenseManager::verify_bb(const string& username,
const string& password) const { const string& password) const {
try { try {
auto& license = this->bb_username_to_license.at(username); auto& license = this->bb_username_to_license.at(username);
if (license->bb_password != password) { if (license->bb_password != password) {
@@ -200,8 +196,6 @@ vector<License> LicenseManager::snapshot() const {
return ret; return ret;
} }
shared_ptr<License> LicenseManager::create_license_pc( shared_ptr<License> LicenseManager::create_license_pc(
uint32_t serial_number, const string& access_key, bool temporary) { uint32_t serial_number, const string& access_key, bool temporary) {
shared_ptr<License> l(new License()); shared_ptr<License> l(new License());
+18 -18
View File
@@ -1,29 +1,29 @@
#pragma once #pragma once
#include <unordered_map>
#include <string>
#include <vector>
#include <memory> #include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "Text.hh" #include "Text.hh"
enum Privilege { enum Privilege {
KICK_USER = 0x00000001, KICK_USER = 0x00000001,
BAN_USER = 0x00000002, BAN_USER = 0x00000002,
SILENCE_USER = 0x00000004, SILENCE_USER = 0x00000004,
CHANGE_LOBBY_INFO = 0x00000008, CHANGE_LOBBY_INFO = 0x00000008,
CHANGE_EVENT = 0x00000010, CHANGE_EVENT = 0x00000010,
ANNOUNCE = 0x00000020, ANNOUNCE = 0x00000020,
FREE_JOIN_GAMES = 0x00000040, FREE_JOIN_GAMES = 0x00000040,
UNLOCK_GAMES = 0x00000080, UNLOCK_GAMES = 0x00000080,
DEBUG = 0x01000000, DEBUG = 0x01000000,
MODERATOR = 0x00000007, MODERATOR = 0x00000007,
ADMINISTRATOR = 0x0000003F, ADMINISTRATOR = 0x0000003F,
ROOT = 0x7FFFFFFF, ROOT = 0x7FFFFFFF,
TEMPORARY = 0x80000000, TEMPORARY = 0x80000000,
}; };
enum LicenseVerifyAction { enum LicenseVerifyAction {
@@ -48,17 +48,17 @@ struct License {
class incorrect_password : public std::invalid_argument { class incorrect_password : public std::invalid_argument {
public: public:
incorrect_password() : invalid_argument("incorrect password") { } incorrect_password() : invalid_argument("incorrect password") {}
}; };
class incorrect_access_key : public std::invalid_argument { class incorrect_access_key : public std::invalid_argument {
public: public:
incorrect_access_key() : invalid_argument("incorrect access key") { } incorrect_access_key() : invalid_argument("incorrect access key") {}
}; };
class missing_license : public std::invalid_argument { class missing_license : public std::invalid_argument {
public: public:
missing_license() : invalid_argument("missing license") { } missing_license() : invalid_argument("missing license") {}
}; };
class LicenseManager { class LicenseManager {
+21 -28
View File
@@ -10,27 +10,25 @@
using namespace std; using namespace std;
Lobby::Lobby(uint32_t id) Lobby::Lobby(uint32_t id)
: log(string_printf("[Lobby/%" PRIX32 "] ", id), lobby_log.min_level), : log(string_printf("[Lobby/%" PRIX32 "] ", id), lobby_log.min_level),
lobby_id(id), lobby_id(id),
min_level(0), min_level(0),
max_level(0xFFFFFFFF), max_level(0xFFFFFFFF),
next_game_item_id(0x00810000), next_game_item_id(0x00810000),
version(GameVersion::GC), version(GameVersion::GC),
section_id(0), section_id(0),
episode(Episode::NONE), episode(Episode::NONE),
mode(GameMode::NORMAL), mode(GameMode::NORMAL),
difficulty(0), difficulty(0),
random_seed(random_object<uint32_t>()), random_seed(random_object<uint32_t>()),
random(new mt19937(this->random_seed)), random(new mt19937(this->random_seed)),
event(0), event(0),
block(0), block(0),
type(0), type(0),
leader_id(0), leader_id(0),
max_clients(12), max_clients(12),
flags(0) { flags(0) {
for (size_t x = 0; x < 12; x++) { for (size_t x = 0; x < 12; x++) {
this->next_item_id[x] = 0x00010000 + 0x00200000 * x; this->next_item_id[x] = 0x00010000 + 0x00200000 * x;
} }
@@ -135,7 +133,8 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
lobby_data.player_tag = 0x00010000; lobby_data.player_tag = 0x00010000;
lobby_data.guild_card = c->license->serial_number; lobby_data.guild_card = c->license->serial_number;
lobby_data.name = encode_sjis(c->game_data.player()->disp.name); lobby_data.name = encode_sjis(c->game_data.player()->disp.name);
this->battle_record->add_player(lobby_data, this->battle_record->add_player(
lobby_data,
c->game_data.player()->inventory, c->game_data.player()->inventory,
c->game_data.player()->disp.to_dcpcv3()); c->game_data.player()->disp.to_dcpcv3());
} }
@@ -215,10 +214,8 @@ void Lobby::move_client_to_lobby(
dest_lobby->add_client(c, required_client_id); dest_lobby->add_client(c, required_client_id);
} }
shared_ptr<Client> Lobby::find_client(const u16string* identifier, shared_ptr<Client> Lobby::find_client(const u16string* identifier,
uint64_t serial_number) { uint64_t serial_number) {
for (size_t x = 0; x < this->max_clients; x++) { for (size_t x = 0; x < this->max_clients; x++) {
if (!this->clients[x]) { if (!this->clients[x]) {
continue; continue;
@@ -235,8 +232,6 @@ shared_ptr<Client> Lobby::find_client(const u16string* identifier,
throw out_of_range("client not found"); throw out_of_range("client not found");
} }
uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) { uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) {
if (lobby_event > 7) { if (lobby_event > 7) {
return 0; return 0;
@@ -250,8 +245,6 @@ uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) {
return lobby_event; return lobby_event;
} }
void Lobby::add_item(const PlayerInventoryItem& item, uint8_t area, float x, float z) { void Lobby::add_item(const PlayerInventoryItem& item, uint8_t area, float x, float z) {
auto& fi = this->item_id_to_floor_item[item.data.id]; auto& fi = this->item_id_to_floor_item[item.data.id];
fi.inv_item = item; fi.inv_item = item;
+16 -18
View File
@@ -2,13 +2,13 @@
#include <inttypes.h> #include <inttypes.h>
#include <random>
#include <array> #include <array>
#include <vector>
#include <string>
#include <memory> #include <memory>
#include <unordered_map>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <random>
#include <string>
#include <unordered_map>
#include <vector>
#include "Client.hh" #include "Client.hh"
#include "Episode3/BattleRecord.hh" #include "Episode3/BattleRecord.hh"
@@ -21,27 +21,25 @@
#include "StaticGameData.hh" #include "StaticGameData.hh"
#include "Text.hh" #include "Text.hh"
struct Lobby : public std::enable_shared_from_this<Lobby> { struct Lobby : public std::enable_shared_from_this<Lobby> {
enum Flag { enum Flag {
GAME = 0x00000001, GAME = 0x00000001,
NON_V1_ONLY = 0x00000002, // DC NTE and DCv1 not allowed NON_V1_ONLY = 0x00000002, // DC NTE and DCv1 not allowed
PERSISTENT = 0x00000004, PERSISTENT = 0x00000004,
// Flags used only for games // Flags used only for games
CHEATS_ENABLED = 0x00000100, CHEATS_ENABLED = 0x00000100,
QUEST_IN_PROGRESS = 0x00000200, QUEST_IN_PROGRESS = 0x00000200,
BATTLE_IN_PROGRESS = 0x00000400, BATTLE_IN_PROGRESS = 0x00000400,
JOINABLE_QUEST_IN_PROGRESS = 0x00000800, JOINABLE_QUEST_IN_PROGRESS = 0x00000800,
ITEM_TRACKING_ENABLED = 0x00001000, ITEM_TRACKING_ENABLED = 0x00001000,
IS_SPECTATOR_TEAM = 0x00002000, // episode must be EP3 also IS_SPECTATOR_TEAM = 0x00002000, // episode must be EP3 also
SPECTATORS_FORBIDDEN = 0x00004000, SPECTATORS_FORBIDDEN = 0x00004000,
START_BATTLE_PLAYER_IMMEDIATELY = 0x00008000, START_BATTLE_PLAYER_IMMEDIATELY = 0x00008000,
// Flags used only for lobbies // Flags used only for lobbies
PUBLIC = 0x01000000, PUBLIC = 0x01000000,
DEFAULT = 0x02000000, DEFAULT = 0x02000000,
}; };
PrefixedLogger log; PrefixedLogger log;
+36 -37
View File
@@ -4,32 +4,30 @@
using namespace std; using namespace std;
PrefixedLogger ax_messages_log("[$ax message] ", LogLevel::USE_DEFAULT);
PrefixedLogger channel_exceptions_log("[Channel] ", LogLevel::USE_DEFAULT);
PrefixedLogger ax_messages_log ("[$ax message] " , LogLevel::USE_DEFAULT); PrefixedLogger client_log("", LogLevel::USE_DEFAULT);
PrefixedLogger channel_exceptions_log("[Channel] " , LogLevel::USE_DEFAULT); PrefixedLogger command_data_log("[Commands] ", LogLevel::USE_DEFAULT);
PrefixedLogger client_log ("" , LogLevel::USE_DEFAULT); PrefixedLogger config_log("[Config] ", LogLevel::USE_DEFAULT);
PrefixedLogger command_data_log ("[Commands] " , LogLevel::USE_DEFAULT); PrefixedLogger dns_server_log("[DNSServer] ", LogLevel::USE_DEFAULT);
PrefixedLogger config_log ("[Config] " , LogLevel::USE_DEFAULT); PrefixedLogger function_compiler_log("[FunctionCompiler] ", LogLevel::USE_DEFAULT);
PrefixedLogger dns_server_log ("[DNSServer] " , LogLevel::USE_DEFAULT);
PrefixedLogger function_compiler_log ("[FunctionCompiler] ", LogLevel::USE_DEFAULT);
PrefixedLogger ip_stack_simulator_log("[IPStackSimulator] ", LogLevel::USE_DEFAULT); PrefixedLogger ip_stack_simulator_log("[IPStackSimulator] ", LogLevel::USE_DEFAULT);
PrefixedLogger license_log ("[LicenseManager] " , LogLevel::USE_DEFAULT); PrefixedLogger license_log("[LicenseManager] ", LogLevel::USE_DEFAULT);
PrefixedLogger lobby_log ("" , LogLevel::USE_DEFAULT); PrefixedLogger lobby_log("", LogLevel::USE_DEFAULT);
PrefixedLogger patch_index_log ("[PatchFileIndex] " , LogLevel::USE_DEFAULT); PrefixedLogger patch_index_log("[PatchFileIndex] ", LogLevel::USE_DEFAULT);
PrefixedLogger player_data_log ("" , LogLevel::USE_DEFAULT); PrefixedLogger player_data_log("", LogLevel::USE_DEFAULT);
PrefixedLogger proxy_server_log ("[ProxyServer] " , LogLevel::USE_DEFAULT); PrefixedLogger proxy_server_log("[ProxyServer] ", LogLevel::USE_DEFAULT);
PrefixedLogger replay_log ("[ReplaySession] " , LogLevel::USE_DEFAULT); PrefixedLogger replay_log("[ReplaySession] ", LogLevel::USE_DEFAULT);
PrefixedLogger server_log ("[Server] " , LogLevel::USE_DEFAULT); PrefixedLogger server_log("[Server] ", LogLevel::USE_DEFAULT);
PrefixedLogger static_game_data_log ("[StaticGameData] " , LogLevel::USE_DEFAULT); PrefixedLogger static_game_data_log("[StaticGameData] ", LogLevel::USE_DEFAULT);
static LogLevel log_level_for_name(const string& name) { static LogLevel log_level_for_name(const string& name) {
static const unordered_map<string, LogLevel> levels({ static const unordered_map<string, LogLevel> levels({
{"debug", LogLevel::DEBUG}, {"debug", LogLevel::DEBUG},
{"info", LogLevel::INFO}, {"info", LogLevel::INFO},
{"warning", LogLevel::WARNING}, {"warning", LogLevel::WARNING},
{"error", LogLevel::ERROR}, {"error", LogLevel::ERROR},
{"disabled", LogLevel::DISABLED}, {"disabled", LogLevel::DISABLED},
}); });
return levels.at(tolower(name)); return levels.at(tolower(name));
} }
@@ -38,24 +36,25 @@ static void set_log_level_from_json(
PrefixedLogger& log, shared_ptr<JSONObject> d, const char* json_key) { PrefixedLogger& log, shared_ptr<JSONObject> d, const char* json_key) {
try { try {
log.min_level = log_level_for_name(d->at(json_key)->as_string()); log.min_level = log_level_for_name(d->at(json_key)->as_string());
} catch (const JSONObject::key_error&) { } } catch (const JSONObject::key_error&) {
}
} }
void set_log_levels_from_json(shared_ptr<JSONObject> json) { void set_log_levels_from_json(shared_ptr<JSONObject> json) {
set_log_level_from_json(ax_messages_log , json, "AXMessages"); set_log_level_from_json(ax_messages_log, json, "AXMessages");
set_log_level_from_json(channel_exceptions_log, json, "ChannelExceptions"); set_log_level_from_json(channel_exceptions_log, json, "ChannelExceptions");
set_log_level_from_json(client_log , json, "Clients"); set_log_level_from_json(client_log, json, "Clients");
set_log_level_from_json(command_data_log , json, "CommandData"); set_log_level_from_json(command_data_log, json, "CommandData");
set_log_level_from_json(config_log , json, "Config"); set_log_level_from_json(config_log, json, "Config");
set_log_level_from_json(dns_server_log , json, "DNSServer"); set_log_level_from_json(dns_server_log, json, "DNSServer");
set_log_level_from_json(function_compiler_log , json, "FunctionCompiler"); set_log_level_from_json(function_compiler_log, json, "FunctionCompiler");
set_log_level_from_json(ip_stack_simulator_log, json, "IPStackSimulator"); set_log_level_from_json(ip_stack_simulator_log, json, "IPStackSimulator");
set_log_level_from_json(license_log , json, "LicenseManager"); set_log_level_from_json(license_log, json, "LicenseManager");
set_log_level_from_json(lobby_log , json, "Lobbies"); set_log_level_from_json(lobby_log, json, "Lobbies");
set_log_level_from_json(patch_index_log , json, "PatchFileIndex"); set_log_level_from_json(patch_index_log, json, "PatchFileIndex");
set_log_level_from_json(player_data_log , json, "PlayerData"); set_log_level_from_json(player_data_log, json, "PlayerData");
set_log_level_from_json(proxy_server_log , json, "ProxyServer"); set_log_level_from_json(proxy_server_log, json, "ProxyServer");
set_log_level_from_json(replay_log , json, "Replay"); set_log_level_from_json(replay_log, json, "Replay");
set_log_level_from_json(server_log , json, "GameServer"); set_log_level_from_json(server_log, json, "GameServer");
set_log_level_from_json(static_game_data_log , json, "StaticGameData"); set_log_level_from_json(static_game_data_log, json, "StaticGameData");
} }
+1 -3
View File
@@ -1,9 +1,7 @@
#pragma once #pragma once
#include <phosg/Strings.hh>
#include <phosg/JSON.hh> #include <phosg/JSON.hh>
#include <phosg/Strings.hh>
extern PrefixedLogger ax_messages_log; extern PrefixedLogger ax_messages_log;
extern PrefixedLogger channel_exceptions_log; extern PrefixedLogger channel_exceptions_log;
+87 -66
View File
@@ -4,8 +4,8 @@
#include <string.h> #include <string.h>
#include <phosg/Filesystem.hh> #include <phosg/Filesystem.hh>
#include <phosg/Math.hh>
#include <phosg/JSON.hh> #include <phosg/JSON.hh>
#include <phosg/Math.hh>
#include <phosg/Network.hh> #include <phosg/Network.hh>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <phosg/Tools.hh> #include <phosg/Tools.hh>
@@ -21,9 +21,9 @@
#include "IPStackSimulator.hh" #include "IPStackSimulator.hh"
#include "Loggers.hh" #include "Loggers.hh"
#include "NetworkAddresses.hh" #include "NetworkAddresses.hh"
#include "PSOGCObjectGraph.hh"
#include "Product.hh" #include "Product.hh"
#include "ProxyServer.hh" #include "ProxyServer.hh"
#include "PSOGCObjectGraph.hh"
#include "ReplaySession.hh" #include "ReplaySession.hh"
#include "SaveFileFormats.hh" #include "SaveFileFormats.hh"
#include "SendCommands.hh" #include "SendCommands.hh"
@@ -35,12 +35,8 @@
using namespace std; using namespace std;
bool use_terminal_colors = false; bool use_terminal_colors = false;
template <typename T> template <typename T>
vector<T> parse_int_vector(shared_ptr<const JSONObject> o) { vector<T> parse_int_vector(shared_ptr<const JSONObject> o) {
vector<T> ret; vector<T> ret;
@@ -50,8 +46,6 @@ vector<T> parse_int_vector(shared_ptr<const JSONObject> o) {
return ret; return ret;
} }
void drop_privileges(const string& username) { void drop_privileges(const string& username) {
if ((getuid() != 0) || (getgid() != 0)) { if ((getuid() != 0) || (getgid() != 0)) {
throw runtime_error(string_printf( throw runtime_error(string_printf(
@@ -76,11 +70,9 @@ void drop_privileges(const string& username) {
throw runtime_error(string_printf("can\'t switch to user %d (%s)", throw runtime_error(string_printf("can\'t switch to user %d (%s)",
pw->pw_uid, error.c_str())); pw->pw_uid, error.c_str()));
} }
config_log.info("Switched to user %s (%d:%d)", username.c_str(), pw->pw_uid, pw->pw_gid); config_log.info("Switched to user %s (%d:%d)", username.c_str(), pw->pw_uid, pw->pw_gid);
} }
void print_usage() { void print_usage() {
fputs("\ fputs("\
Usage:\n\ Usage:\n\
@@ -191,7 +183,8 @@ A few options apply to multiple modes described above:\n\
a hex string before encrypting/decoding/etc.\n\ a hex string before encrypting/decoding/etc.\n\
--config=FILENAME\n\ --config=FILENAME\n\
Use this file instead of system/config.json.\n\ Use this file instead of system/config.json.\n\
", stderr); ",
stderr);
} }
enum class Behavior { enum class Behavior {
@@ -219,45 +212,47 @@ enum class Behavior {
REPLAY_LOG, REPLAY_LOG,
CAT_CLIENT, CAT_CLIENT,
GENERATE_PRODUCT, GENERATE_PRODUCT,
INSPECT_PRODUCT,
PRODUCT_SPEED_TEST, PRODUCT_SPEED_TEST,
}; };
static bool behavior_takes_input_filename(Behavior b) { static bool behavior_takes_input_filename(Behavior b) {
return (b == Behavior::COMPRESS_PRS) || return (b == Behavior::COMPRESS_PRS) ||
(b == Behavior::DECOMPRESS_PRS) || (b == Behavior::DECOMPRESS_PRS) ||
(b == Behavior::COMPRESS_BC0) || (b == Behavior::COMPRESS_BC0) ||
(b == Behavior::DECOMPRESS_BC0) || (b == Behavior::DECOMPRESS_BC0) ||
(b == Behavior::PRS_SIZE) || (b == Behavior::PRS_SIZE) ||
(b == Behavior::PRS_DISASSEMBLE) || (b == Behavior::PRS_DISASSEMBLE) ||
(b == Behavior::ENCRYPT_DATA) || (b == Behavior::ENCRYPT_DATA) ||
(b == Behavior::DECRYPT_DATA) || (b == Behavior::DECRYPT_DATA) ||
(b == Behavior::DECRYPT_TRIVIAL_DATA) || (b == Behavior::DECRYPT_TRIVIAL_DATA) ||
(b == Behavior::DECRYPT_GCI_SAVE) || (b == Behavior::DECRYPT_GCI_SAVE) ||
(b == Behavior::ENCRYPT_GCI_SAVE) || (b == Behavior::ENCRYPT_GCI_SAVE) ||
(b == Behavior::DECODE_QUEST_FILE) || (b == Behavior::DECODE_QUEST_FILE) ||
(b == Behavior::DECODE_SJIS) || (b == Behavior::DECODE_SJIS) ||
(b == Behavior::FORMAT_ITEMRT_ENTRY) || (b == Behavior::FORMAT_ITEMRT_ENTRY) ||
(b == Behavior::FORMAT_ITEMRT_REL) || (b == Behavior::FORMAT_ITEMRT_REL) ||
(b == Behavior::EXTRACT_GSL) || (b == Behavior::EXTRACT_GSL) ||
(b == Behavior::EXTRACT_BML) || (b == Behavior::EXTRACT_BML) ||
(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_PRODUCT);
} }
static bool behavior_takes_output_filename(Behavior b) { static bool behavior_takes_output_filename(Behavior b) {
return (b == Behavior::COMPRESS_PRS) || return (b == Behavior::COMPRESS_PRS) ||
(b == Behavior::DECOMPRESS_PRS) || (b == Behavior::DECOMPRESS_PRS) ||
(b == Behavior::COMPRESS_BC0) || (b == Behavior::COMPRESS_BC0) ||
(b == Behavior::DECOMPRESS_BC0) || (b == Behavior::DECOMPRESS_BC0) ||
(b == Behavior::ENCRYPT_DATA) || (b == Behavior::ENCRYPT_DATA) ||
(b == Behavior::DECRYPT_DATA) || (b == Behavior::DECRYPT_DATA) ||
(b == Behavior::DECRYPT_TRIVIAL_DATA) || (b == Behavior::DECRYPT_TRIVIAL_DATA) ||
(b == Behavior::DECRYPT_GCI_SAVE) || (b == Behavior::DECRYPT_GCI_SAVE) ||
(b == Behavior::ENCRYPT_GCI_SAVE) || (b == Behavior::ENCRYPT_GCI_SAVE) ||
(b == Behavior::DECODE_SJIS) || (b == Behavior::DECODE_SJIS) ||
(b == Behavior::EXTRACT_GSL) || (b == Behavior::EXTRACT_GSL) ||
(b == Behavior::EXTRACT_BML); (b == Behavior::EXTRACT_BML);
} }
enum class QuestFileFormat { enum class QuestFileFormat {
@@ -344,7 +339,8 @@ int main(int argc, char** argv) {
if (!strcmp(argv[x], "help")) { if (!strcmp(argv[x], "help")) {
print_usage(); print_usage();
return 0; return 0;
} if (!strcmp(argv[x], "compress-prs")) { }
if (!strcmp(argv[x], "compress-prs")) {
behavior = Behavior::COMPRESS_PRS; behavior = Behavior::COMPRESS_PRS;
} else if (!strcmp(argv[x], "decompress-prs")) { } else if (!strcmp(argv[x], "decompress-prs")) {
behavior = Behavior::DECOMPRESS_PRS; behavior = Behavior::DECOMPRESS_PRS;
@@ -400,6 +396,8 @@ int main(int argc, char** argv) {
behavior = Behavior::EXTRACT_BML; behavior = Behavior::EXTRACT_BML;
} else if (!strcmp(argv[x], "generate-product")) { } else if (!strcmp(argv[x], "generate-product")) {
behavior = Behavior::GENERATE_PRODUCT; behavior = Behavior::GENERATE_PRODUCT;
} else if (!strcmp(argv[x], "inspect-product")) {
behavior = Behavior::INSPECT_PRODUCT;
} else if (!strcmp(argv[x], "product-speed-test")) { } else if (!strcmp(argv[x], "product-speed-test")) {
behavior = Behavior::PRODUCT_SPEED_TEST; behavior = Behavior::PRODUCT_SPEED_TEST;
} else { } else {
@@ -435,9 +433,9 @@ int main(int argc, char** argv) {
// If the output is to a specified file, write it there // If the output is to a specified file, write it there
if (output_filename && strcmp(output_filename, "-")) { if (output_filename && strcmp(output_filename, "-")) {
save_file(output_filename, data, size); save_file(output_filename, data, size);
// If no output filename is given and an input filename is given, write to // If no output filename is given and an input filename is given, write to
// <input-filename>.dec (or an appropriate extension, if it can be // <input-filename>.dec (or an appropriate extension, if it can be
// autodetected) // autodetected)
} else if (!output_filename && input_filename && strcmp(input_filename, "-")) { } else if (!output_filename && input_filename && strcmp(input_filename, "-")) {
string filename = input_filename; string filename = input_filename;
if (behavior == Behavior::COMPRESS_PRS) { if (behavior == Behavior::COMPRESS_PRS) {
@@ -472,11 +470,11 @@ int main(int argc, char** argv) {
filename += ".dec"; filename += ".dec";
} }
save_file(filename, data, size); save_file(filename, data, size);
// If stdout is a terminal, use print_data to write the result // If stdout is a terminal, use print_data to write the result
} else if (isatty(fileno(stdout))) { } else if (isatty(fileno(stdout))) {
print_data(stdout, data, size); print_data(stdout, data, size);
fflush(stdout); fflush(stdout);
// If stdout is not a terminal, write the data as-is // If stdout is not a terminal, write the data as-is
} else { } else {
fwritex(stdout, data, size); fwritex(stdout, data, size);
fflush(stdout); fflush(stdout);
@@ -756,7 +754,8 @@ int main(int argc, char** argv) {
} }
} }
return false; return false;
}, 0, 0x100000000, num_threads); },
0, 0x100000000, num_threads);
if (seed < 0x100000000) { if (seed < 0x100000000) {
log_info("Found seed %08" PRIX64, seed); log_info("Found seed %08" PRIX64, seed);
@@ -774,15 +773,15 @@ int main(int argc, char** argv) {
string output_filename_base = input_filename; string output_filename_base = input_filename;
if (quest_file_type == QuestFileFormat::GCI) { if (quest_file_type == QuestFileFormat::GCI) {
int64_t dec_seed = seed.empty() ? -1 : stoul(seed, nullptr, 16); int64_t dec_seed = seed.empty() ? -1 : stoul(seed, nullptr, 16);
save_file(output_filename_base + ".dec", Quest::decode_gci( auto decoded = Quest::decode_gci(input_filename, num_threads, dec_seed);
input_filename, num_threads, dec_seed)); save_file(output_filename_base + ".dec", decoded);
} else if (quest_file_type == QuestFileFormat::VMS) { } else if (quest_file_type == QuestFileFormat::VMS) {
int64_t dec_seed = seed.empty() ? -1 : stoul(seed, nullptr, 16); int64_t dec_seed = seed.empty() ? -1 : stoul(seed, nullptr, 16);
save_file(output_filename_base + ".dec", Quest::decode_vms( auto decoded = Quest::decode_vms(input_filename, num_threads, dec_seed);
input_filename, num_threads, dec_seed)); save_file(output_filename_base + ".dec", decoded);
} else if (quest_file_type == QuestFileFormat::DLQ) { } else if (quest_file_type == QuestFileFormat::DLQ) {
save_file(output_filename_base + ".dec", Quest::decode_dlq( auto decoded = Quest::decode_dlq(input_filename);
input_filename)); save_file(output_filename_base + ".dec", decoded);
} else if (quest_file_type == QuestFileFormat::QST) { } else if (quest_file_type == QuestFileFormat::QST) {
auto data = Quest::decode_qst(input_filename); auto data = Quest::decode_qst(input_filename);
save_file(output_filename_base + ".bin", data.first); save_file(output_filename_base + ".bin", data.first);
@@ -874,7 +873,8 @@ int main(int argc, char** argv) {
uint32_t expanded_probability = RareItemSet::expand_rate(r.probability); uint32_t expanded_probability = RareItemSet::expand_rate(r.probability);
auto frac = reduce_fraction<uint64_t>(expanded_probability, 0x100000000); auto frac = reduce_fraction<uint64_t>(expanded_probability, 0x100000000);
return string_printf("(%02hhX => %08" PRIX32 " => %" PRIu64 "/%" PRIu64 ") %02hhX%02hhX%02hhX (%s)", return string_printf(
"(%02hhX => %08" PRIX32 " => %" PRIu64 "/%" PRIu64 ") %02hhX%02hhX%02hhX (%s)",
r.probability, expanded_probability, frac.first, frac.second, r.item_code[0], r.item_code[1], r.item_code[2], name.c_str()); r.probability, expanded_probability, frac.first, frac.second, r.item_code[0], r.item_code[1], r.item_code[2], name.c_str());
}; };
@@ -913,7 +913,8 @@ int main(int argc, char** argv) {
uint32_t expanded_probability = RareItemSet::expand_rate(r.probability); uint32_t expanded_probability = RareItemSet::expand_rate(r.probability);
auto frac = reduce_fraction<uint64_t>(expanded_probability, 0x100000000); auto frac = reduce_fraction<uint64_t>(expanded_probability, 0x100000000);
return string_printf("(%02hhX => %08" PRIX32 " => %" PRIu64 "/%" PRIu64 ") %02hhX%02hhX%02hhX (%s)", return string_printf(
"(%02hhX => %08" PRIX32 " => %" PRIu64 "/%" PRIu64 ") %02hhX%02hhX%02hhX (%s)",
r.probability, expanded_probability, frac.first, frac.second, r.item_code[0], r.item_code[1], r.item_code[2], name.c_str()); r.probability, expanded_probability, frac.first, frac.second, r.item_code[0], r.item_code[1], r.item_code[2], name.c_str());
}; };
@@ -949,9 +950,9 @@ int main(int argc, char** argv) {
}; };
static const vector<Episode> episodes = { static const vector<Episode> episodes = {
Episode::EP1, Episode::EP1,
Episode::EP2, Episode::EP2,
Episode::EP4, Episode::EP4,
}; };
for (Episode episode : episodes) { for (Episode episode : episodes) {
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) { for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
@@ -1006,7 +1007,26 @@ int main(int argc, char** argv) {
case Behavior::GENERATE_PRODUCT: { case Behavior::GENERATE_PRODUCT: {
auto product = generate_product(domain, subdomain); auto product = generate_product(domain, subdomain);
fprintf(stderr, "%s\n", product.c_str()); fprintf(stdout, "%s\n", product.c_str());
break;
}
case Behavior::INSPECT_PRODUCT: {
if (!input_filename) {
throw invalid_argument("no product given");
}
size_t num_valid_subdomains = 0;
for (uint8_t domain = 0; domain < 3; domain++) {
for (uint8_t subdomain = 0; subdomain < 3; subdomain++) {
if (product_is_valid_fast(input_filename, domain, subdomain)) {
fprintf(stdout, "%s is valid in domain %hhu subdomain %hhu\n", input_filename, domain, subdomain);
num_valid_subdomains++;
}
}
}
if (num_valid_subdomains == 0) {
fprintf(stdout, "%s is not valid in any domain\n", input_filename);
}
break; break;
} }
@@ -1049,7 +1069,8 @@ int main(int argc, char** argv) {
config_log.info("Starting game server"); config_log.info("Starting game server");
state->game_server.reset(new Server(base, state)); state->game_server.reset(new Server(base, state));
shared_ptr<FILE> log_f(stdin, +[](FILE*) { }); shared_ptr<FILE> log_f(
stdin, +[](FILE*) {});
if (input_filename && strcmp(input_filename, "-")) { if (input_filename && strcmp(input_filename, "-")) {
log_f = fopen_shared(input_filename, "rt"); log_f = fopen_shared(input_filename, "rt");
} }
@@ -1074,14 +1095,14 @@ int main(int argc, char** argv) {
// destination is supported, and we have to manually specify the // destination is supported, and we have to manually specify the
// destination netloc here. // destination netloc here.
if (pc->version == GameVersion::PATCH) { if (pc->version == GameVersion::PATCH) {
struct sockaddr_storage ss = make_sockaddr_storage( auto [ss, size] = make_sockaddr_storage(
state->proxy_destination_patch.first, state->proxy_destination_patch.first,
state->proxy_destination_patch.second).first; state->proxy_destination_patch.second);
state->proxy_server->listen(pc->port, pc->version, &ss); state->proxy_server->listen(pc->port, pc->version, &ss);
} else if (pc->version == GameVersion::BB) { } else if (pc->version == GameVersion::BB) {
struct sockaddr_storage ss = make_sockaddr_storage( auto [ss, size] = make_sockaddr_storage(
state->proxy_destination_bb.first, state->proxy_destination_bb.first,
state->proxy_destination_bb.second).first; state->proxy_destination_bb.second);
state->proxy_server->listen(pc->port, pc->version, &ss); state->proxy_server->listen(pc->port, pc->version, &ss);
} else { } else {
state->proxy_server->listen(pc->port, pc->version); state->proxy_server->listen(pc->port, pc->version);
+148 -157
View File
@@ -8,8 +8,6 @@
using namespace std; using namespace std;
string BattleParamsIndex::Entry::str() const { string BattleParamsIndex::Entry::str() const {
string a1str = format_data_string(this->unknown_a1.data(), this->unknown_a1.bytes()); string a1str = format_data_string(this->unknown_a1.data(), this->unknown_a1.bytes());
return string_printf( return string_printf(
@@ -31,18 +29,18 @@ void BattleParamsIndex::Table::print(FILE* stream) const {
auto print_entry = +[](FILE* stream, const Entry& e) { auto print_entry = +[](FILE* stream, const Entry& e) {
string a1str = format_data_string(e.unknown_a1.data(), e.unknown_a1.bytes()); string a1str = format_data_string(e.unknown_a1.data(), e.unknown_a1.bytes());
fprintf(stream, fprintf(stream,
"%5hu %5hu %5hu %5hu %5hu %5hu %5hu %5hu %s %5" PRIu32 " %5" PRIu32, "%5hu %5hu %5hu %5hu %5hu %5hu %5hu %5hu %s %5" PRIu32 " %5" PRIu32,
e.atp.load(), e.atp.load(),
e.psv.load(), e.psv.load(),
e.evp.load(), e.evp.load(),
e.hp.load(), e.hp.load(),
e.dfp.load(), e.dfp.load(),
e.ata.load(), e.ata.load(),
e.lck.load(), e.lck.load(),
e.esp.load(), e.esp.load(),
a1str.c_str(), a1str.c_str(),
e.experience.load(), e.experience.load(),
e.difficulty.load()); e.difficulty.load());
}; };
for (size_t diff = 0; diff < 4; diff++) { for (size_t diff = 0; diff < 4; diff++) {
@@ -56,8 +54,6 @@ void BattleParamsIndex::Table::print(FILE* stream) const {
} }
} }
BattleParamsIndex::BattleParamsIndex( BattleParamsIndex::BattleParamsIndex(
shared_ptr<const string> data_on_ep1, shared_ptr<const string> data_on_ep1,
shared_ptr<const string> data_on_ep2, shared_ptr<const string> data_on_ep2,
@@ -112,9 +108,7 @@ const BattleParamsIndex::Entry& BattleParamsIndex::get(
return this->files[!!solo][ep_index].table->difficulty[difficulty][monster_type]; return this->files[!!solo][ep_index].table->difficulty[difficulty][monster_type];
} }
PSOEnemy::PSOEnemy(uint64_t id) : PSOEnemy(id, 0, 0, 0, 0, "__missing__") {}
PSOEnemy::PSOEnemy(uint64_t id) : PSOEnemy(id, 0, 0, 0, 0, "__missing__") { }
PSOEnemy::PSOEnemy( PSOEnemy::PSOEnemy(
uint64_t id, uint64_t id,
@@ -123,22 +117,20 @@ PSOEnemy::PSOEnemy(
uint32_t rt_index, uint32_t rt_index,
size_t num_clones, size_t num_clones,
const char* type_name) const char* type_name)
: id(id), : id(id),
source_type(source_type), source_type(source_type),
hit_flags(0), hit_flags(0),
last_hit(0), last_hit(0),
experience(experience), experience(experience),
rt_index(rt_index), rt_index(rt_index),
num_clones(num_clones), num_clones(num_clones),
type_name(type_name) { } type_name(type_name) {}
string PSOEnemy::str() const { string PSOEnemy::str() const {
return string_printf("[Enemy E-%" PRIX64 " \"%s\" source_type=%hX hit=%02hhX/%hu exp=%" PRIu32 " rt_index=%" PRIX32 " clones=%zu]", return string_printf("[Enemy E-%" PRIX64 " \"%s\" source_type=%hX hit=%02hhX/%hu exp=%" PRIu32 " rt_index=%" PRIX32 " clones=%zu]",
this->id, this->type_name, this->source_type, this->hit_flags, this->last_hit, this->experience, this->rt_index, this->num_clones); this->id, this->type_name, this->source_type, this->hit_flags, this->last_hit, this->experience, this->rt_index, this->num_clones);
} }
struct EnemyEntry { struct EnemyEntry {
uint32_t base; uint32_t base;
uint16_t reserved0; uint16_t reserved0;
@@ -174,11 +166,7 @@ vector<PSOEnemy> parse_map(
} }
}; };
auto create_enemy = [&]( auto create_enemy = [&](const EnemyEntry& e, ssize_t bp_index, uint32_t rt_index, const char* type_name) {
const EnemyEntry& e,
ssize_t bp_index,
uint32_t rt_index,
const char* type_name) {
const BattleParamsIndex::Entry& bp_entry = battle_params->get( const BattleParamsIndex::Entry& bp_entry = battle_params->get(
is_solo, episode, difficulty, bp_index); is_solo, episode, difficulty, bp_index);
enemies.emplace_back( enemies.emplace_back(
@@ -423,7 +411,7 @@ vector<PSOEnemy> parse_map(
create_enemy(e, 0x1F, 6, "Girtablulu"); create_enemy(e, 0x1F, 6, "Girtablulu");
break; break;
case 0x0114: // Zu and Pazuzu case 0x0114: // Zu and Pazuzu
create_enemy(e, 0x0B + (e.skin & 0x01) + (alt_enemies ? 0x14: 0x00), create_enemy(e, 0x0B + (e.skin & 0x01) + (alt_enemies ? 0x14 : 0x00),
7 + (e.skin & 0x01), "(Pazu)?zu"); 7 + (e.skin & 0x01), "(Pazu)?zu");
break; break;
case 0x0115: // Boota family case 0x0115: // Boota family
@@ -457,8 +445,6 @@ vector<PSOEnemy> parse_map(
return enemies; return enemies;
} }
SetDataTable::SetDataTable(shared_ptr<const string> data, bool big_endian) { SetDataTable::SetDataTable(shared_ptr<const string> data, bool big_endian) {
if (big_endian) { if (big_endian) {
this->load_table_t<true>(data); this->load_table_t<true>(data);
@@ -523,8 +509,6 @@ void SetDataTable::print(FILE* stream) const {
} }
} }
struct AreaMapFileIndex { struct AreaMapFileIndex {
const char* name_token; const char* name_token;
vector<uint32_t> variation1_values; vector<uint32_t> variation1_values;
@@ -534,127 +518,136 @@ struct AreaMapFileIndex {
const char* name_token, const char* name_token,
vector<uint32_t> variation1_values, vector<uint32_t> variation1_values,
vector<uint32_t> variation2_values) vector<uint32_t> variation2_values)
: name_token(name_token), : name_token(name_token),
variation1_values(variation1_values), variation1_values(variation1_values),
variation2_values(variation2_values) { } variation2_values(variation2_values) {}
}; };
// These are indexed as [episode][is_solo][area], where episode is 0-2 // These are indexed as [episode][is_solo][area], where episode is 0-2
static const vector<vector<vector<AreaMapFileIndex>>> map_file_info = { static const vector<vector<vector<AreaMapFileIndex>>> map_file_info = {
{ // Episode 1 {
{ // Non-solo // Episode 1
{"city00", {}, {0}}, {
{"forest01", {}, {0, 1, 2, 3, 4}}, // Non-solo
{"forest02", {}, {0, 1, 2, 3, 4}}, {"city00", {}, {0}},
{"cave01", {0, 1, 2}, {0, 1}}, {"forest01", {}, {0, 1, 2, 3, 4}},
{"cave02", {0, 1, 2}, {0, 1}}, {"forest02", {}, {0, 1, 2, 3, 4}},
{"cave03", {0, 1, 2}, {0, 1}}, {"cave01", {0, 1, 2}, {0, 1}},
{"machine01", {0, 1, 2}, {0, 1}}, {"cave02", {0, 1, 2}, {0, 1}},
{"machine02", {0, 1, 2}, {0, 1}}, {"cave03", {0, 1, 2}, {0, 1}},
{"ancient01", {0, 1, 2}, {0, 1}}, {"machine01", {0, 1, 2}, {0, 1}},
{"ancient02", {0, 1, 2}, {0, 1}}, {"machine02", {0, 1, 2}, {0, 1}},
{"ancient03", {0, 1, 2}, {0, 1}}, {"ancient01", {0, 1, 2}, {0, 1}},
{"boss01", {}, {}}, {"ancient02", {0, 1, 2}, {0, 1}},
{"boss02", {}, {}}, {"ancient03", {0, 1, 2}, {0, 1}},
{"boss03", {}, {}}, {"boss01", {}, {}},
{"boss04", {}, {}}, {"boss02", {}, {}},
{nullptr, {}, {}}, {"boss03", {}, {}},
{"boss04", {}, {}},
{nullptr, {}, {}},
},
{
// Solo
{"city00", {}, {0}},
{"forest01", {}, {0, 2, 4}},
{"forest02", {}, {0, 3, 4}},
{"cave01", {0, 1, 2}, {0}},
{"cave02", {0, 1, 2}, {0}},
{"cave03", {0, 1, 2}, {0}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
},
}, },
{ // Solo {
{"city00", {}, {0}}, // Episode 2
{"forest01", {}, {0, 2, 4}}, {
{"forest02", {}, {0, 3, 4}}, // Non-solo
{"cave01", {0, 1, 2}, {0}}, {"labo00", {}, {0}},
{"cave02", {0, 1, 2}, {0}}, {"ruins01", {0, 1}, {0}},
{"cave03", {0, 1, 2}, {0}}, {"ruins02", {0, 1}, {0}},
{nullptr, {}, {}}, {"space01", {0, 1}, {0}},
{nullptr, {}, {}}, {"space02", {0, 1}, {0}},
{nullptr, {}, {}}, {"jungle01", {}, {0, 1, 2}},
{nullptr, {}, {}}, {"jungle02", {}, {0, 1, 2}},
{nullptr, {}, {}}, {"jungle03", {}, {0, 1, 2}},
{nullptr, {}, {}}, {"jungle04", {0, 1}, {0, 1}},
{nullptr, {}, {}}, {"jungle05", {}, {0, 1, 2}},
{nullptr, {}, {}}, {"seabed01", {0, 1}, {0, 1}},
{nullptr, {}, {}}, {"seabed02", {0, 1}, {0, 1}},
{nullptr, {}, {}}, {"boss05", {}, {}},
{"boss06", {}, {}},
{"boss07", {}, {}},
{"boss08", {}, {}},
},
{
// Solo
{"labo00", {}, {0}},
{"ruins01", {0, 1}, {0}},
{"ruins02", {0, 1}, {0}},
{"space01", {0, 1}, {0}},
{"space02", {0, 1}, {0}},
{"jungle01", {}, {0, 1, 2}},
{"jungle02", {}, {0, 1, 2}},
{"jungle03", {}, {0, 1, 2}},
{"jungle04", {0, 1}, {0, 1}},
{"jungle05", {}, {0, 1, 2}},
{"seabed01", {0, 1}, {0}},
{"seabed02", {0, 1}, {0}},
{"boss05", {}, {}},
{"boss06", {}, {}},
{"boss07", {}, {}},
{"boss08", {}, {}},
},
}, },
}, {
{ // Episode 2 // Episode 4
{ // Non-solo {
{"labo00", {}, {0}}, // Non-solo
{"ruins01", {0, 1}, {0}}, {"city02", {0}, {0}},
{"ruins02", {0, 1}, {0}}, {"wilds01", {0}, {0, 1, 2}},
{"space01", {0, 1}, {0}}, {"wilds01", {1}, {0, 1, 2}},
{"space02", {0, 1}, {0}}, {"wilds01", {2}, {0, 1, 2}},
{"jungle01", {}, {0, 1, 2}}, {"wilds01", {3}, {0, 1, 2}},
{"jungle02", {}, {0, 1, 2}}, {"crater01", {0}, {0, 1, 2}},
{"jungle03", {}, {0, 1, 2}}, {"desert01", {0, 1, 2}, {0}},
{"jungle04", {0, 1}, {0, 1}}, {"desert02", {0}, {0, 1, 2}},
{"jungle05", {}, {0, 1, 2}}, {"desert03", {0, 1, 2}, {0}},
{"seabed01", {0, 1}, {0, 1}}, {"boss09", {0}, {0}},
{"seabed02", {0, 1}, {0, 1}}, {nullptr, {}, {}},
{"boss05", {}, {}}, {nullptr, {}, {}},
{"boss06", {}, {}}, {nullptr, {}, {}},
{"boss07", {}, {}}, {nullptr, {}, {}},
{"boss08", {}, {}}, {nullptr, {}, {}},
{nullptr, {}, {}},
},
{
// Solo
{"city02", {0}, {0}},
{"wilds01", {0}, {0, 1, 2}},
{"wilds01", {1}, {0, 1, 2}},
{"wilds01", {2}, {0, 1, 2}},
{"wilds01", {3}, {0, 1, 2}},
{"crater01", {0}, {0, 1, 2}},
{"desert01", {0, 1, 2}, {0}},
{"desert02", {0}, {0, 1, 2}},
{"desert03", {0, 1, 2}, {0}},
{"boss09", {0}, {0}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
},
}, },
{ // Solo
{"labo00", {}, {0}},
{"ruins01", {0, 1}, {0}},
{"ruins02", {0, 1}, {0}},
{"space01", {0, 1}, {0}},
{"space02", {0, 1}, {0}},
{"jungle01", {}, {0, 1, 2}},
{"jungle02", {}, {0, 1, 2}},
{"jungle03", {}, {0, 1, 2}},
{"jungle04", {0, 1}, {0, 1}},
{"jungle05", {}, {0, 1, 2}},
{"seabed01", {0, 1}, {0}},
{"seabed02", {0, 1}, {0}},
{"boss05", {}, {}},
{"boss06", {}, {}},
{"boss07", {}, {}},
{"boss08", {}, {}},
},
},
{ // Episode 4
{ // Non-solo
{"city02", {0}, {0}},
{"wilds01", {0}, {0, 1, 2}},
{"wilds01", {1}, {0, 1, 2}},
{"wilds01", {2}, {0, 1, 2}},
{"wilds01", {3}, {0, 1, 2}},
{"crater01", {0}, {0, 1, 2}},
{"desert01", {0, 1, 2}, {0}},
{"desert02", {0}, {0, 1, 2}},
{"desert03", {0, 1, 2}, {0}},
{"boss09", {0}, {0}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
},
{ // Solo
{"city02", {0}, {0}},
{"wilds01", {0}, {0, 1, 2}},
{"wilds01", {1}, {0, 1, 2}},
{"wilds01", {2}, {0, 1, 2}},
{"wilds01", {3}, {0, 1, 2}},
{"crater01", {0}, {0, 1, 2}},
{"desert01", {0, 1, 2}, {0}},
{"desert02", {0}, {0, 1, 2}},
{"desert03", {0, 1, 2}, {0}},
{"boss09", {0}, {0}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
{nullptr, {}, {}},
},
},
}; };
const vector<vector<AreaMapFileIndex>>& map_file_info_for_episode(Episode ep) { const vector<vector<AreaMapFileIndex>>& map_file_info_for_episode(Episode ep) {
@@ -688,10 +681,8 @@ void generate_variations(
variations[z * 2 + 0] = 0; variations[z * 2 + 0] = 0;
variations[z * 2 + 1] = 0; variations[z * 2 + 1] = 0;
} else { } else {
variations[z * 2 + 0] = (a->variation1_values.size() < 2) ? 0 : variations[z * 2 + 0] = (a->variation1_values.size() < 2) ? 0 : ((*random)() % a->variation1_values.size());
((*random)() % a->variation1_values.size()); variations[z * 2 + 1] = (a->variation2_values.size() < 2) ? 0 : ((*random)() % a->variation2_values.size());
variations[z * 2 + 1] = (a->variation2_values.size() < 2) ? 0 :
((*random)() % a->variation2_values.size());
} }
} }
} }
+1 -9
View File
@@ -11,15 +11,13 @@
#include "StaticGameData.hh" #include "StaticGameData.hh"
#include "Text.hh" #include "Text.hh"
class BattleParamsIndex { class BattleParamsIndex {
public: public:
struct Entry { struct Entry {
le_uint16_t atp; // attack power le_uint16_t atp; // attack power
le_uint16_t psv; // perseverance (intelligence?) le_uint16_t psv; // perseverance (intelligence?)
le_uint16_t evp; // evasion le_uint16_t evp; // evasion
le_uint16_t hp; // hit points le_uint16_t hp; // hit points
le_uint16_t dfp; // defense le_uint16_t dfp; // defense
le_uint16_t ata; // accuracy le_uint16_t ata; // accuracy
le_uint16_t lck; // luck le_uint16_t lck; // luck
@@ -58,8 +56,6 @@ private:
LoadedFile files[2][3]; LoadedFile files[2][3];
}; };
struct PSOEnemy { struct PSOEnemy {
uint64_t id; uint64_t id;
uint16_t source_type; uint16_t source_type;
@@ -90,8 +86,6 @@ std::vector<PSOEnemy> parse_map(
std::shared_ptr<const std::string> data, std::shared_ptr<const std::string> data,
bool alt_enemies); bool alt_enemies);
// TODO: This class is currently unused. It would be nice if we could use this // TODO: This class is currently unused. It would be nice if we could use this
// to generate variations and link to the corresponding map filenames, but it // to generate variations and link to the corresponding map filenames, but it
// seems that SetDataTable.rel files link to map filenames that don't actually // seems that SetDataTable.rel files link to map filenames that don't actually
@@ -121,8 +115,6 @@ private:
std::vector<std::vector<std::vector<SetEntry>>> entries; std::vector<std::vector<std::vector<SetEntry>>> entries;
}; };
void generate_variations( void generate_variations(
parray<le_uint32_t, 0x20>& variations, parray<le_uint32_t, 0x20>& variations,
std::shared_ptr<std::mt19937> random, std::shared_ptr<std::mt19937> random,
+7 -5
View File
@@ -2,8 +2,10 @@
using namespace std; using namespace std;
MenuItem::MenuItem(
uint32_t item_id, const u16string& name,
MenuItem::MenuItem(uint32_t item_id, const u16string& name, const u16string& description, uint32_t flags)
const u16string& description, uint32_t flags) : item_id(item_id), name(name), : item_id(item_id),
description(description), flags(flags) { } name(name),
description(description),
flags(flags) {}
+45 -49
View File
@@ -4,8 +4,6 @@
#include <string> #include <string>
// Note: These aren't enums because neither enum nor enum class does what we // Note: These aren't enums because neither enum nor enum class does what we
// want. Specifically, we need GO_BACK to be valid in multiple enums (and enums // want. Specifically, we need GO_BACK to be valid in multiple enums (and enums
// aren't namespaced unless they're enum classes), so we can't use enums. But we // aren't namespaced unless they're enum classes), so we can't use enums. But we
@@ -13,68 +11,66 @@
// casting values all over the place, so we can't use enum classes either. // casting values all over the place, so we can't use enum classes either.
namespace MenuID { namespace MenuID {
constexpr uint32_t MAIN = 0x11000011; constexpr uint32_t MAIN = 0x11000011;
constexpr uint32_t INFORMATION = 0x22000022; constexpr uint32_t INFORMATION = 0x22000022;
constexpr uint32_t LOBBY = 0x33000033; constexpr uint32_t LOBBY = 0x33000033;
constexpr uint32_t GAME = 0x44000044; constexpr uint32_t GAME = 0x44000044;
constexpr uint32_t QUEST = 0x55000055; constexpr uint32_t QUEST = 0x55000055;
constexpr uint32_t QUEST_FILTER = 0x66000066; constexpr uint32_t QUEST_FILTER = 0x66000066;
constexpr uint32_t PROXY_DESTINATIONS = 0x77000077; constexpr uint32_t PROXY_DESTINATIONS = 0x77000077;
constexpr uint32_t PROGRAMS = 0x88000088; constexpr uint32_t PROGRAMS = 0x88000088;
constexpr uint32_t PATCHES = 0x99000099; constexpr uint32_t PATCHES = 0x99000099;
constexpr uint32_t PROXY_OPTIONS = 0xAA0000AA; constexpr uint32_t PROXY_OPTIONS = 0xAA0000AA;
constexpr uint32_t TOURNAMENTS = 0xBB0000BB; constexpr uint32_t TOURNAMENTS = 0xBB0000BB;
constexpr uint32_t TOURNAMENTS_FOR_SPEC = 0xBB1111BB; constexpr uint32_t TOURNAMENTS_FOR_SPEC = 0xBB1111BB;
constexpr uint32_t TOURNAMENT_ENTRIES = 0xCC0000CC; constexpr uint32_t TOURNAMENT_ENTRIES = 0xCC0000CC;
} } // namespace MenuID
namespace MainMenuItemID { namespace MainMenuItemID {
constexpr uint32_t GO_TO_LOBBY = 0x11222211; constexpr uint32_t GO_TO_LOBBY = 0x11222211;
constexpr uint32_t INFORMATION = 0x11333311; constexpr uint32_t INFORMATION = 0x11333311;
constexpr uint32_t DOWNLOAD_QUESTS = 0x11444411; constexpr uint32_t DOWNLOAD_QUESTS = 0x11444411;
constexpr uint32_t PROXY_DESTINATIONS = 0x11555511; constexpr uint32_t PROXY_DESTINATIONS = 0x11555511;
constexpr uint32_t PATCHES = 0x11666611; constexpr uint32_t PATCHES = 0x11666611;
constexpr uint32_t PROGRAMS = 0x11777711; constexpr uint32_t PROGRAMS = 0x11777711;
constexpr uint32_t DISCONNECT = 0x11888811; constexpr uint32_t DISCONNECT = 0x11888811;
constexpr uint32_t CLEAR_LICENSE = 0x11999911; constexpr uint32_t CLEAR_LICENSE = 0x11999911;
} } // namespace MainMenuItemID
namespace InformationMenuItemID { namespace InformationMenuItemID {
constexpr uint32_t GO_BACK = 0x22FFFF22; constexpr uint32_t GO_BACK = 0x22FFFF22;
} }
namespace ProxyDestinationsMenuItemID { namespace ProxyDestinationsMenuItemID {
constexpr uint32_t GO_BACK = 0x77FFFF77; constexpr uint32_t GO_BACK = 0x77FFFF77;
constexpr uint32_t OPTIONS = 0x77EEEE77; constexpr uint32_t OPTIONS = 0x77EEEE77;
} } // namespace ProxyDestinationsMenuItemID
namespace ProgramsMenuItemID { namespace ProgramsMenuItemID {
constexpr uint32_t GO_BACK = 0x88FFFF88; constexpr uint32_t GO_BACK = 0x88FFFF88;
} }
namespace PatchesMenuItemID { namespace PatchesMenuItemID {
constexpr uint32_t GO_BACK = 0x99FFFF99; constexpr uint32_t GO_BACK = 0x99FFFF99;
} }
namespace ProxyOptionsMenuItemID { namespace ProxyOptionsMenuItemID {
constexpr uint32_t GO_BACK = 0xAAFFFFAA; constexpr uint32_t GO_BACK = 0xAAFFFFAA;
constexpr uint32_t CHAT_COMMANDS = 0xAA0000AA; constexpr uint32_t CHAT_COMMANDS = 0xAA0000AA;
constexpr uint32_t CHAT_FILTER = 0xAA1111AA; constexpr uint32_t CHAT_FILTER = 0xAA1111AA;
constexpr uint32_t PLAYER_NOTIFICATIONS = 0xAA2222AA; constexpr uint32_t PLAYER_NOTIFICATIONS = 0xAA2222AA;
constexpr uint32_t INFINITE_HP = 0xAA3333AA; constexpr uint32_t INFINITE_HP = 0xAA3333AA;
constexpr uint32_t INFINITE_TP = 0xAA4444AA; constexpr uint32_t INFINITE_TP = 0xAA4444AA;
constexpr uint32_t SWITCH_ASSIST = 0xAA5555AA; constexpr uint32_t SWITCH_ASSIST = 0xAA5555AA;
constexpr uint32_t BLOCK_EVENTS = 0xAA6666AA; constexpr uint32_t BLOCK_EVENTS = 0xAA6666AA;
constexpr uint32_t BLOCK_PATCHES = 0xAA7777AA; constexpr uint32_t BLOCK_PATCHES = 0xAA7777AA;
constexpr uint32_t SAVE_FILES = 0xAA8888AA; constexpr uint32_t SAVE_FILES = 0xAA8888AA;
constexpr uint32_t RED_NAME = 0xAA9999AA; constexpr uint32_t RED_NAME = 0xAA9999AA;
constexpr uint32_t BLANK_NAME = 0xAAAAAAAA; constexpr uint32_t BLANK_NAME = 0xAAAAAAAA;
constexpr uint32_t SUPPRESS_LOGIN = 0xAABBBBAA; constexpr uint32_t SUPPRESS_LOGIN = 0xAABBBBAA;
constexpr uint32_t SKIP_CARD = 0xAACCCCAA; constexpr uint32_t SKIP_CARD = 0xAACCCCAA;
constexpr uint32_t EP3_INFINITE_MESETA = 0xAADDDDAA; constexpr uint32_t EP3_INFINITE_MESETA = 0xAADDDDAA;
} } // namespace ProxyOptionsMenuItemID
struct MenuItem { struct MenuItem {
enum Flag { enum Flag {
+11 -15
View File
@@ -1,14 +1,12 @@
#include "NetworkAddresses.hh" #include "NetworkAddresses.hh"
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/types.h> #include <errno.h>
#include <sys/socket.h>
#include <ifaddrs.h> #include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <memory> #include <memory>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
@@ -17,19 +15,17 @@
using namespace std; using namespace std;
uint32_t resolve_address(const char* address) { uint32_t resolve_address(const char* address) {
struct addrinfo *res0; struct addrinfo* res0;
if (getaddrinfo(address, nullptr, nullptr, &res0)) { if (getaddrinfo(address, nullptr, nullptr, &res0)) {
auto e = string_for_error(errno); auto e = string_for_error(errno);
throw runtime_error(string_printf("can\'t resolve hostname %s: %s", address, throw runtime_error(string_printf(
e.c_str())); "can\'t resolve hostname %s: %s", address, e.c_str()));
} }
std::unique_ptr<struct addrinfo, void(*)(struct addrinfo*)> res0_unique( std::unique_ptr<struct addrinfo, void (*)(struct addrinfo*)> res0_unique(
res0, freeaddrinfo); res0, freeaddrinfo);
struct addrinfo *res4 = nullptr; struct addrinfo* res4 = nullptr;
for (struct addrinfo* res = res0; res; res = res->ai_next) { for (struct addrinfo* res = res0; res; res = res->ai_next) {
if (res->ai_family == AF_INET) { if (res->ai_family == AF_INET) {
res4 = res; res4 = res;
@@ -51,7 +47,7 @@ map<string, uint32_t> get_local_addresses() {
throw runtime_error(string_printf("failed to get interface addresses: %s", s.c_str())); throw runtime_error(string_printf("failed to get interface addresses: %s", s.c_str()));
} }
unique_ptr<struct ifaddrs, void(*)(struct ifaddrs*)> ifa(ifa_raw, freeifaddrs); unique_ptr<struct ifaddrs, void (*)(struct ifaddrs*)> ifa(ifa_raw, freeifaddrs);
map<string, uint32_t> ret; map<string, uint32_t> ret;
for (struct ifaddrs* i = ifa.get(); i; i = i->ifa_next) { for (struct ifaddrs* i = ifa.get(); i; i = i->ifa_next) {
-2
View File
@@ -6,8 +6,6 @@
#include <map> #include <map>
#include <string> #include <string>
// PSO is IPv4-only, so we just treat addresses as uint32_t everywhere because // PSO is IPv4-only, so we just treat addresses as uint32_t everywhere because
// it's easier // it's easier
+29 -52
View File
@@ -3,29 +3,23 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdexcept>
#include <string>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <stdexcept>
#include <string>
using namespace std; using namespace std;
// TODO: fix style in this file, especially in psobb functions // TODO: fix style in this file, especially in psobb functions
// Most ciphers used by PSO are symmetric; alias decrypt to encrypt by default // Most ciphers used by PSO are symmetric; alias decrypt to encrypt by default
void PSOEncryption::decrypt(void* data, size_t size, bool advance) { void PSOEncryption::decrypt(void* data, size_t size, bool advance) {
this->encrypt(data, size, advance); this->encrypt(data, size, advance);
} }
PSOLFGEncryption::PSOLFGEncryption( PSOLFGEncryption::PSOLFGEncryption(
uint32_t seed, size_t stream_length, size_t end_offset) uint32_t seed, size_t stream_length, size_t end_offset)
: stream(stream_length, 0), offset(0), end_offset(end_offset), seed(seed) { } : stream(stream_length, 0), offset(0), end_offset(end_offset), seed(seed) {}
uint32_t PSOLFGEncryption::next(bool advance) { uint32_t PSOLFGEncryption::next(bool advance) {
if (this->offset == this->end_offset) { if (this->offset == this->end_offset) {
@@ -109,10 +103,8 @@ void PSOLFGEncryption::encrypt_both_endian(
} }
} }
PSOV2Encryption::PSOV2Encryption(uint32_t seed) PSOV2Encryption::PSOV2Encryption(uint32_t seed)
: PSOLFGEncryption(seed, this->STREAM_LENGTH + 1, this->STREAM_LENGTH) { : PSOLFGEncryption(seed, this->STREAM_LENGTH + 1, this->STREAM_LENGTH) {
uint32_t esi, ebx, edi, eax, edx, var1; uint32_t esi, ebx, edi, eax, edx, var1;
esi = 1; esi = 1;
ebx = this->seed; ebx = this->seed;
@@ -163,10 +155,8 @@ PSOEncryption::Type PSOV2Encryption::type() const {
return Type::V2; return Type::V2;
} }
PSOV3Encryption::PSOV3Encryption(uint32_t seed) PSOV3Encryption::PSOV3Encryption(uint32_t seed)
: PSOLFGEncryption(seed, this->STREAM_LENGTH, this->STREAM_LENGTH) { : PSOLFGEncryption(seed, this->STREAM_LENGTH, this->STREAM_LENGTH) {
uint32_t x, y, basekey, source1, source2, source3; uint32_t x, y, basekey, source1, source2, source3;
basekey = 0; basekey = 0;
@@ -219,11 +209,9 @@ PSOEncryption::Type PSOV3Encryption::type() const {
return Type::V3; return Type::V3;
} }
PSOBBEncryption::PSOBBEncryption( PSOBBEncryption::PSOBBEncryption(
const KeyFile& key, const void* original_seed, size_t seed_size) const KeyFile& key, const void* original_seed, size_t seed_size)
: state(key) { : state(key) {
this->apply_seed(original_seed, seed_size); this->apply_seed(original_seed, seed_size);
} }
@@ -238,14 +226,14 @@ void PSOBBEncryption::encrypt(void* vdata, size_t size, bool advance) {
for (size_t y = 0; y < 4; y += 2) { for (size_t y = 0; y < 4; y += 2) {
dwords[x] ^= this->state.initial_keys.as32[y]; dwords[x] ^= this->state.initial_keys.as32[y];
dwords[x + 1] ^= ((this->state.private_keys.as32[dwords[x] >> 24] + dwords[x + 1] ^= ((this->state.private_keys.as32[dwords[x] >> 24] +
this->state.private_keys.as32[((dwords[x] >> 16) & 0xFF) + 0x100]) ^ this->state.private_keys.as32[((dwords[x] >> 16) & 0xFF) + 0x100]) ^
this->state.private_keys.as32[((dwords[x] >> 8) & 0xFF) + 0x200]) + this->state.private_keys.as32[((dwords[x] >> 8) & 0xFF) + 0x200]) +
this->state.private_keys.as32[(dwords[x] & 0xFF) + 0x300]; this->state.private_keys.as32[(dwords[x] & 0xFF) + 0x300];
dwords[x + 1] ^= this->state.initial_keys.as32[y + 1]; dwords[x + 1] ^= this->state.initial_keys.as32[y + 1];
dwords[x] ^= ((this->state.private_keys.as32[dwords[x + 1] >> 24] + dwords[x] ^= ((this->state.private_keys.as32[dwords[x + 1] >> 24] +
this->state.private_keys.as32[(dwords[x + 1] >> 16 & 0xFF) + 0x100]) ^ this->state.private_keys.as32[(dwords[x + 1] >> 16 & 0xFF) + 0x100]) ^
this->state.private_keys.as32[(dwords[x + 1] >> 8 & 0xFF) + 0x200]) + this->state.private_keys.as32[(dwords[x + 1] >> 8 & 0xFF) + 0x200]) +
this->state.private_keys.as32[(dwords[x + 1] & 0xFF) + 0x300]; this->state.private_keys.as32[(dwords[x + 1] & 0xFF) + 0x300];
} }
dwords[x] ^= this->state.initial_keys.as32[4]; dwords[x] ^= this->state.initial_keys.as32[4];
dwords[x + 1] ^= this->state.initial_keys.as32[5]; dwords[x + 1] ^= this->state.initial_keys.as32[5];
@@ -335,14 +323,14 @@ void PSOBBEncryption::decrypt(void* vdata, size_t size, bool advance) {
for (size_t y = 4; y > 0; y -= 2) { for (size_t y = 4; y > 0; y -= 2) {
dwords[x] = dwords[x] ^ this->state.initial_keys.as32[y + 1]; dwords[x] = dwords[x] ^ this->state.initial_keys.as32[y + 1];
dwords[x + 1] ^= ((this->state.private_keys.as32[dwords[x] >> 24] + dwords[x + 1] ^= ((this->state.private_keys.as32[dwords[x] >> 24] +
this->state.private_keys.as32[((dwords[x] >> 16) & 0xFF) + 0x100]) ^ this->state.private_keys.as32[((dwords[x] >> 16) & 0xFF) + 0x100]) ^
this->state.private_keys.as32[((dwords[x] >> 8) & 0xFF) + 0x200]) + this->state.private_keys.as32[((dwords[x] >> 8) & 0xFF) + 0x200]) +
this->state.private_keys.as32[(dwords[x] & 0xFF) + 0x300]; this->state.private_keys.as32[(dwords[x] & 0xFF) + 0x300];
dwords[x + 1] ^= this->state.initial_keys.as32[y]; dwords[x + 1] ^= this->state.initial_keys.as32[y];
dwords[x] ^= ((this->state.private_keys.as32[dwords[x + 1] >> 24] + dwords[x] ^= ((this->state.private_keys.as32[dwords[x + 1] >> 24] +
this->state.private_keys.as32[((dwords[x + 1] >> 16) & 0xFF) + 0x100]) ^ this->state.private_keys.as32[((dwords[x + 1] >> 16) & 0xFF) + 0x100]) ^
this->state.private_keys.as32[((dwords[x + 1] >> 8) & 0xFF) + 0x200]) + this->state.private_keys.as32[((dwords[x + 1] >> 8) & 0xFF) + 0x200]) +
this->state.private_keys.as32[(dwords[x + 1] & 0xFF) + 0x300]; this->state.private_keys.as32[(dwords[x + 1] & 0xFF) + 0x300];
} }
dwords[x] ^= this->state.initial_keys.as32[1]; dwords[x] ^= this->state.initial_keys.as32[1];
dwords[x + 1] ^= this->state.initial_keys.as32[0]; dwords[x + 1] ^= this->state.initial_keys.as32[0];
@@ -432,7 +420,8 @@ void PSOBBEncryption::tfs1_scramble(uint32_t* out1, uint32_t* out2) const {
b ^= (((this->state.private_keys.as32[a >> 24] + b ^= (((this->state.private_keys.as32[a >> 24] +
this->state.private_keys.as32[((a >> 16) & 0xFF) + 0x100]) ^ this->state.private_keys.as32[((a >> 16) & 0xFF) + 0x100]) ^
this->state.private_keys.as32[((a >> 8) & 0xFF) + 0x200]) + this->state.private_keys.as32[((a >> 8) & 0xFF) + 0x200]) +
this->state.private_keys.as32[(a & 0xFF) + 0x300]) ^ this->state.initial_keys.as32[x + 1]; this->state.private_keys.as32[(a & 0xFF) + 0x300]) ^
this->state.initial_keys.as32[x + 1];
a ^= ((this->state.private_keys.as32[b >> 24] + a ^= ((this->state.private_keys.as32[b >> 24] +
this->state.private_keys.as32[((b >> 16) & 0xFF) + 0x100]) ^ this->state.private_keys.as32[((b >> 16) & 0xFF) + 0x100]) ^
this->state.private_keys.as32[((b >> 8) & 0xFF) + 0x200]) + this->state.private_keys.as32[((b >> 8) & 0xFF) + 0x200]) +
@@ -608,7 +597,7 @@ void PSOBBEncryption::apply_seed(const void* original_seed, size_t seed_size) {
esi = this->state.initial_keys.as32[17]; esi = this->state.initial_keys.as32[17];
esi = esi ^ eax; esi = esi ^ eax;
this->state.initial_keys.as32[(edi / 4)] = esi; this->state.initial_keys.as32[(edi / 4)] = esi;
this->state.initial_keys.as32[(edi / 4)+1] = ecx; this->state.initial_keys.as32[(edi / 4) + 1] = ecx;
edi = edi + 8; edi = edi + 8;
} }
@@ -695,13 +684,11 @@ void PSOBBEncryption::apply_seed(const void* original_seed, size_t seed_size) {
} }
} }
PSOV2OrV3DetectorEncryption::PSOV2OrV3DetectorEncryption( PSOV2OrV3DetectorEncryption::PSOV2OrV3DetectorEncryption(
uint32_t key, uint32_t key,
const std::unordered_set<uint32_t>& v2_matches, const std::unordered_set<uint32_t>& v2_matches,
const std::unordered_set<uint32_t>& v3_matches) const std::unordered_set<uint32_t>& v3_matches)
: key(key), v2_matches(v2_matches), v3_matches(v3_matches) { } : key(key), v2_matches(v2_matches), v3_matches(v3_matches) {}
void PSOV2OrV3DetectorEncryption::encrypt(void* data, size_t size, bool advance) { void PSOV2OrV3DetectorEncryption::encrypt(void* data, size_t size, bool advance) {
if (!this->active_crypt) { if (!this->active_crypt) {
@@ -745,11 +732,9 @@ PSOEncryption::Type PSOV2OrV3DetectorEncryption::type() const {
return this->active_crypt->type(); return this->active_crypt->type();
} }
PSOV2OrV3ImitatorEncryption::PSOV2OrV3ImitatorEncryption( PSOV2OrV3ImitatorEncryption::PSOV2OrV3ImitatorEncryption(
uint32_t key, std::shared_ptr<PSOV2OrV3DetectorEncryption> detector_crypt) uint32_t key, std::shared_ptr<PSOV2OrV3DetectorEncryption> detector_crypt)
: key(key), detector_crypt(detector_crypt) { } : key(key), detector_crypt(detector_crypt) {}
void PSOV2OrV3ImitatorEncryption::encrypt(void* data, size_t size, bool advance) { void PSOV2OrV3ImitatorEncryption::encrypt(void* data, size_t size, bool advance) {
if (!this->active_crypt) { if (!this->active_crypt) {
@@ -772,16 +757,14 @@ PSOEncryption::Type PSOV2OrV3ImitatorEncryption::type() const {
return this->active_crypt->type(); return this->active_crypt->type();
} }
PSOBBMultiKeyDetectorEncryption::PSOBBMultiKeyDetectorEncryption( PSOBBMultiKeyDetectorEncryption::PSOBBMultiKeyDetectorEncryption(
const vector<shared_ptr<const PSOBBEncryption::KeyFile>>& possible_keys, const vector<shared_ptr<const PSOBBEncryption::KeyFile>>& possible_keys,
const unordered_set<string>& expected_first_data, const unordered_set<string>& expected_first_data,
const void* seed, const void* seed,
size_t seed_size) size_t seed_size)
: possible_keys(possible_keys), : possible_keys(possible_keys),
expected_first_data(expected_first_data), expected_first_data(expected_first_data),
seed(reinterpret_cast<const char*>(seed), seed_size) { } seed(reinterpret_cast<const char*>(seed), seed_size) {}
void PSOBBMultiKeyDetectorEncryption::encrypt(void* data, size_t size, bool advance) { void PSOBBMultiKeyDetectorEncryption::encrypt(void* data, size_t size, bool advance) {
if (!this->active_crypt.get()) { if (!this->active_crypt.get()) {
@@ -819,16 +802,14 @@ PSOEncryption::Type PSOBBMultiKeyDetectorEncryption::type() const {
return Type::BB; return Type::BB;
} }
PSOBBMultiKeyImitatorEncryption::PSOBBMultiKeyImitatorEncryption( PSOBBMultiKeyImitatorEncryption::PSOBBMultiKeyImitatorEncryption(
shared_ptr<const PSOBBMultiKeyDetectorEncryption> detector_crypt, shared_ptr<const PSOBBMultiKeyDetectorEncryption> detector_crypt,
const void* seed, const void* seed,
size_t seed_size, size_t seed_size,
bool jsd1_use_detector_seed) bool jsd1_use_detector_seed)
: detector_crypt(detector_crypt), : detector_crypt(detector_crypt),
seed(reinterpret_cast<const char*>(seed), seed_size), seed(reinterpret_cast<const char*>(seed), seed_size),
jsd1_use_detector_seed(jsd1_use_detector_seed) { } jsd1_use_detector_seed(jsd1_use_detector_seed) {}
void PSOBBMultiKeyImitatorEncryption::encrypt(void* data, size_t size, bool advance) { void PSOBBMultiKeyImitatorEncryption::encrypt(void* data, size_t size, bool advance) {
this->ensure_crypt()->encrypt(data, size, advance); this->ensure_crypt()->encrypt(data, size, advance);
@@ -863,8 +844,6 @@ shared_ptr<PSOBBEncryption> PSOBBMultiKeyImitatorEncryption::ensure_crypt() {
return this->active_crypt; return this->active_crypt;
} }
JSD0Encryption::JSD0Encryption(const void* seed, size_t seed_size) : key(0) { JSD0Encryption::JSD0Encryption(const void* seed, size_t seed_size) : key(0) {
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(seed); const uint8_t* bytes = reinterpret_cast<const uint8_t*>(seed);
for (size_t z = 0; z < seed_size; z++) { for (size_t z = 0; z < seed_size; z++) {
@@ -892,8 +871,6 @@ PSOEncryption::Type JSD0Encryption::type() const {
return Type::JSD0; return Type::JSD0;
} }
void decrypt_trivial_gci_data(void* data, size_t size, uint8_t basis) { void decrypt_trivial_gci_data(void* data, size_t size, uint8_t basis) {
uint8_t* bytes = reinterpret_cast<uint8_t*>(data); uint8_t* bytes = reinterpret_cast<uint8_t*>(data);
uint8_t key = basis + 0x80; uint8_t key = basis + 0x80;
+5 -20
View File
@@ -4,14 +4,12 @@
#include <stddef.h> #include <stddef.h>
#include <memory> #include <memory>
#include <phosg/Encoding.hh>
#include <string> #include <string>
#include <vector> #include <vector>
#include <phosg/Encoding.hh>
#include "Text.hh" // for parray #include "Text.hh" // for parray
class PSOEncryption { class PSOEncryption {
public: public:
enum class Type { enum class Type {
@@ -39,8 +37,6 @@ protected:
PSOEncryption() = default; PSOEncryption() = default;
}; };
class PSOLFGEncryption : public PSOEncryption { class PSOLFGEncryption : public PSOEncryption {
public: public:
virtual void encrypt(void* data, size_t size, bool advance = true); virtual void encrypt(void* data, size_t size, bool advance = true);
@@ -91,8 +87,6 @@ protected:
static constexpr size_t STREAM_LENGTH = 521; static constexpr size_t STREAM_LENGTH = 521;
}; };
class PSOBBEncryption : public PSOEncryption { class PSOBBEncryption : public PSOEncryption {
public: public:
enum Subtype : uint8_t { enum Subtype : uint8_t {
@@ -109,14 +103,14 @@ public:
uint8_t jsd1_stream_offset; uint8_t jsd1_stream_offset;
parray<uint8_t, 0x48> as8; parray<uint8_t, 0x48> as8;
parray<le_uint32_t, 0x12> as32; parray<le_uint32_t, 0x12> as32;
InitialKeys() : as32() { } InitialKeys() : as32() {}
InitialKeys(const InitialKeys& other) : as32(other.as32) { } InitialKeys(const InitialKeys& other) : as32(other.as32) {}
} __attribute__((packed)); } __attribute__((packed));
union PrivateKeys { union PrivateKeys {
parray<uint8_t, 0x1000> as8; parray<uint8_t, 0x1000> as8;
parray<le_uint32_t, 0x400> as32; parray<le_uint32_t, 0x400> as32;
PrivateKeys() : as32() { } PrivateKeys() : as32() {}
PrivateKeys(const PrivateKeys& other) : as32(other.as32) { } PrivateKeys(const PrivateKeys& other) : as32(other.as32) {}
} __attribute__((packed)); } __attribute__((packed));
InitialKeys initial_keys; InitialKeys initial_keys;
PrivateKeys private_keys; PrivateKeys private_keys;
@@ -137,9 +131,6 @@ protected:
void apply_seed(const void* original_seed, size_t seed_size); void apply_seed(const void* original_seed, size_t seed_size);
}; };
// The following classes provide support for automatically detecting which type // The following classes provide support for automatically detecting which type
// of encryption a client is using based on their initial response to the server // of encryption a client is using based on their initial response to the server
@@ -176,8 +167,6 @@ protected:
std::shared_ptr<PSOEncryption> active_crypt; std::shared_ptr<PSOEncryption> active_crypt;
}; };
// The following classes provide support for multiple PSOBB private keys, and // The following classes provide support for multiple PSOBB private keys, and
// the ability to automatically detect which key the client is using based on // the ability to automatically detect which key the client is using based on
// the first 8 bytes they send // the first 8 bytes they send
@@ -232,8 +221,6 @@ protected:
bool jsd1_use_detector_seed; bool jsd1_use_detector_seed;
}; };
class JSD0Encryption : public PSOEncryption { class JSD0Encryption : public PSOEncryption {
public: public:
JSD0Encryption(const void* seed, size_t seed_size); JSD0Encryption(const void* seed, size_t seed_size);
@@ -247,6 +234,4 @@ private:
uint8_t key; uint8_t key;
}; };
void decrypt_trivial_gci_data(void* data, size_t size, uint8_t basis); void decrypt_trivial_gci_data(void* data, size_t size, uint8_t basis);
+12 -14
View File
@@ -4,8 +4,6 @@
using namespace std; using namespace std;
struct TObjectVTable { struct TObjectVTable {
be_uint32_t unused_a1; be_uint32_t unused_a1;
be_uint32_t unused_a2; be_uint32_t unused_a2;
@@ -36,7 +34,8 @@ shared_ptr<PSOGCObjectGraph::VTable> PSOGCObjectGraph::parse_vtable_memo(
StringReader& r, uint32_t addr) { StringReader& r, uint32_t addr) {
try { try {
return this->all_vtables.at(addr); return this->all_vtables.at(addr);
} catch (const out_of_range&) { } } catch (const out_of_range&) {
}
const auto& vt = r.pget<TObjectVTable>(addr & 0x01FFFFFF); const auto& vt = r.pget<TObjectVTable>(addr & 0x01FFFFFF);
auto ret = this->all_vtables.emplace(addr, new VTable()).first->second; auto ret = this->all_vtables.emplace(addr, new VTable()).first->second;
@@ -52,7 +51,8 @@ shared_ptr<PSOGCObjectGraph::Object> PSOGCObjectGraph::parse_object_memo(
StringReader& r, uint32_t addr) { StringReader& r, uint32_t addr) {
try { try {
return this->all_objects.at(addr); return this->all_objects.at(addr);
} catch (const out_of_range&) { } } catch (const out_of_range&) {
}
const auto& obj = r.pget<TObject>(addr & 0x01FFFFFF); const auto& obj = r.pget<TObject>(addr & 0x01FFFFFF);
string type_name = r.pget_cstr(obj.type_name_addr & 0x01FFFFFF); string type_name = r.pget_cstr(obj.type_name_addr & 0x01FFFFFF);
@@ -79,22 +79,20 @@ void PSOGCObjectGraph::print(FILE* stream) const {
this->root->print(stream); this->root->print(stream);
} }
void PSOGCObjectGraph::Object::print(FILE* stream, size_t indent_level) const { void PSOGCObjectGraph::Object::print(FILE* stream, size_t indent_level) const {
for (size_t z = 0; z < indent_level; z++) { for (size_t z = 0; z < indent_level; z++) {
fputc(' ', stream); fputc(' ', stream);
fputc(' ', stream); fputc(' ', stream);
} }
fprintf(stream, "%s +%04hX @ %08" PRIX32 " (VT %08" PRIX32 ": destroy=%08" PRIX32 " update=%08" PRIX32 " render=%08" PRIX32 " render_shadow=%08" PRIX32 ")\n", fprintf(stream, "%s +%04hX @ %08" PRIX32 " (VT %08" PRIX32 ": destroy=%08" PRIX32 " update=%08" PRIX32 " render=%08" PRIX32 " render_shadow=%08" PRIX32 ")\n",
this->type_name.c_str(), this->type_name.c_str(),
this->flags, this->flags,
this->address, this->address,
this->vtable->address, this->vtable->address,
this->vtable->destroy_addr, this->vtable->destroy_addr,
this->vtable->update_addr, this->vtable->update_addr,
this->vtable->render_addr, this->vtable->render_addr,
this->vtable->render_shadow_addr); this->vtable->render_shadow_addr);
for (const auto& child : this->children) { for (const auto& child : this->children) {
child->print(stream, indent_level + 1); child->print(stream, indent_level + 1);
} }
-2
View File
@@ -9,8 +9,6 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
struct PSOGCObjectGraph { struct PSOGCObjectGraph {
PSOGCObjectGraph(const std::string& memory_data, uint32_t root_address); PSOGCObjectGraph(const std::string& memory_data, uint32_t root_address);
+1 -9
View File
@@ -2,19 +2,15 @@
#include <event2/buffer.h> #include <event2/buffer.h>
#include <stdexcept>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <stdexcept>
#include "Text.hh" #include "Text.hh"
using namespace std; using namespace std;
extern bool use_terminal_colors; extern bool use_terminal_colors;
PSOCommandHeader::PSOCommandHeader() { PSOCommandHeader::PSOCommandHeader() {
this->bb.size = 0; this->bb.size = 0;
this->bb.command = 0; this->bb.command = 0;
@@ -144,8 +140,6 @@ void PSOCommandHeader::set_flag(GameVersion version, uint32_t flag) {
} }
} }
void check_size_v(size_t size, size_t min_size, size_t max_size) { void check_size_v(size_t size, size_t min_size, size_t max_size) {
if (size < min_size) { if (size < min_size) {
throw std::runtime_error(string_printf( throw std::runtime_error(string_printf(
@@ -162,8 +156,6 @@ void check_size_v(size_t size, size_t min_size, size_t max_size) {
} }
} }
std::string prepend_command_header( std::string prepend_command_header(
GameVersion version, GameVersion version,
bool encryption_enabled, bool encryption_enabled,
+2 -2
View File
@@ -1,13 +1,13 @@
#pragma once #pragma once
#include <inttypes.h>
#include <event2/bufferevent.h> #include <event2/bufferevent.h>
#include <inttypes.h>
#include <functional> #include <functional>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include "Version.hh"
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
#include "Version.hh"
struct PSOCommandHeaderPC { struct PSOCommandHeaderPC {
le_uint16_t size; le_uint16_t size;
+13 -13
View File
@@ -4,19 +4,19 @@
#include <string.h> #include <string.h>
#include <functional> #include <functional>
#include <stdexcept>
#include <phosg/Filesystem.hh> #include <phosg/Filesystem.hh>
#include <phosg/Hash.hh> #include <phosg/Hash.hh>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <stdexcept>
#include "Loggers.hh" #include "Loggers.hh"
using namespace std; using namespace std;
PatchFileIndex::File::File(PatchFileIndex* index) PatchFileIndex::File::File(PatchFileIndex* index)
: index(index), crc32(0), size(0) { } : index(index),
crc32(0),
size(0) {}
std::shared_ptr<const std::string> PatchFileIndex::File::load_data() { std::shared_ptr<const std::string> PatchFileIndex::File::load_data() {
if (!this->loaded_data) { if (!this->loaded_data) {
@@ -29,10 +29,8 @@ std::shared_ptr<const std::string> PatchFileIndex::File::load_data() {
return this->loaded_data; return this->loaded_data;
} }
PatchFileIndex::PatchFileIndex(const string& root_dir) PatchFileIndex::PatchFileIndex(const string& root_dir)
: root_dir(root_dir) { : root_dir(root_dir) {
string metadata_cache_filename = root_dir + "/.metadata-cache.json"; string metadata_cache_filename = root_dir + "/.metadata-cache.json";
shared_ptr<JSONObject> metadata_cache_json; shared_ptr<JSONObject> metadata_cache_json;
@@ -113,10 +111,10 @@ PatchFileIndex::PatchFileIndex(const string& root_dir)
chunk_crcs_item.emplace_back(make_json_int(chunk_crc)); chunk_crcs_item.emplace_back(make_json_int(chunk_crc));
} }
vector<shared_ptr<JSONObject>> new_cache_item({ vector<shared_ptr<JSONObject>> new_cache_item({
make_json_int(f->size), make_json_int(f->size),
make_json_int(st.st_mtime), make_json_int(st.st_mtime),
make_json_int(f->crc32), make_json_int(f->crc32),
make_json_list(move(chunk_crcs_item)), make_json_list(move(chunk_crcs_item)),
}); });
new_metadata_cache_json->as_dict().emplace( new_metadata_cache_json->as_dict().emplace(
relative_item_path, make_json_list(move(new_cache_item))); relative_item_path, make_json_list(move(new_cache_item)));
@@ -132,10 +130,12 @@ PatchFileIndex::PatchFileIndex(const string& root_dir)
this->files_by_patch_order.emplace_back(f); this->files_by_patch_order.emplace_back(f);
this->files_by_name.emplace(relative_item_path, f); this->files_by_name.emplace(relative_item_path, f);
if (compute_crc32s_message.empty()) { if (compute_crc32s_message.empty()) {
patch_index_log.info("Added file %s (%" PRIu32 " bytes; %zu chunks; %08" PRIX32 " from cache)", patch_index_log.info(
"Added file %s (%" PRIu32 " bytes; %zu chunks; %08" PRIX32 " from cache)",
full_item_path.c_str(), f->size, f->chunk_crcs.size(), f->crc32); full_item_path.c_str(), f->size, f->chunk_crcs.size(), f->crc32);
} else { } else {
patch_index_log.info("Added file %s (%" PRIu32 " bytes; %zu chunks; %08" PRIX32 " [%s])", patch_index_log.info(
"Added file %s (%" PRIu32 " bytes; %zu chunks; %08" PRIX32 " [%s])",
full_item_path.c_str(), f->size, f->chunk_crcs.size(), f->crc32, compute_crc32s_message.c_str()); full_item_path.c_str(), f->size, f->chunk_crcs.size(), f->crc32, compute_crc32s_message.c_str());
} }
} }
+8 -7
View File
@@ -2,13 +2,11 @@
#include <inttypes.h> #include <inttypes.h>
#include <map>
#include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <map>
#include <vector> #include <vector>
#include <memory>
struct PatchFileIndex { struct PatchFileIndex {
explicit PatchFileIndex(const std::string& root_dir); explicit PatchFileIndex(const std::string& root_dir);
@@ -42,10 +40,13 @@ struct PatchFileChecksumRequest {
bool response_received; bool response_received;
explicit PatchFileChecksumRequest(std::shared_ptr<PatchFileIndex::File> file) explicit PatchFileChecksumRequest(std::shared_ptr<PatchFileIndex::File> file)
: file(file), crc32(0), size(0), response_received(false) { } : file(file),
crc32(0),
size(0),
response_received(false) {}
inline bool needs_update() const { inline bool needs_update() const {
return !this->response_received || return !this->response_received ||
(this->crc32 != this->file->crc32) || (this->crc32 != this->file->crc32) ||
(this->size != this->file->size); (this->size != this->file->size);
} }
}; };
+103 -117
View File
@@ -4,12 +4,12 @@
#include <string.h> #include <string.h>
#include <wchar.h> #include <wchar.h>
#include <stdexcept>
#include <phosg/Filesystem.hh> #include <phosg/Filesystem.hh>
#include <phosg/Hash.hh> #include <phosg/Hash.hh>
#include <stdexcept>
#include "ItemData.hh"
#include "FileContentsCache.hh" #include "FileContentsCache.hh"
#include "ItemData.hh"
#include "Loggers.hh" #include "Loggers.hh"
#include "StaticGameData.hh" #include "StaticGameData.hh"
#include "Text.hh" #include "Text.hh"
@@ -17,8 +17,6 @@
using namespace std; using namespace std;
// Originally there was going to be a language-based header, but then I decided // Originally there was going to be a language-based header, but then I decided
// against it. These strings were already in use for that parser, so I didn't // against it. These strings were already in use for that parser, so I didn't
// bother changing them. // bother changing them.
@@ -27,38 +25,40 @@ static const string PLAYER_FILE_SIGNATURE =
static const string ACCOUNT_FILE_SIGNATURE = static const string ACCOUNT_FILE_SIGNATURE =
"newserv account file format; 7 sections present; sequential;"; "newserv account file format; 7 sections present; sequential;";
static FileContentsCache player_files_cache(300 * 1000 * 1000); static FileContentsCache player_files_cache(300 * 1000 * 1000);
PlayerStats::PlayerStats() noexcept PlayerStats::PlayerStats() noexcept
: atp(0), mst(0), evp(0), hp(0), dfp(0), ata(0), lck(0) { } : atp(0),
mst(0),
evp(0),
hp(0),
dfp(0),
ata(0),
lck(0) {}
PlayerDispDataDCPCV3::PlayerDispDataDCPCV3() noexcept PlayerDispDataDCPCV3::PlayerDispDataDCPCV3() noexcept
: level(0), : level(0),
experience(0), experience(0),
meseta(0), meseta(0),
unknown_a2(0), unknown_a2(0),
name_color(0), name_color(0),
extra_model(0), extra_model(0),
name_color_checksum(0), name_color_checksum(0),
section_id(0), section_id(0),
char_class(0), char_class(0),
v2_flags(0), v2_flags(0),
version(0), version(0),
v1_flags(0), v1_flags(0),
costume(0), costume(0),
skin(0), skin(0),
face(0), face(0),
head(0), head(0),
hair(0), hair(0),
hair_r(0), hair_r(0),
hair_g(0), hair_g(0),
hair_b(0), hair_b(0),
proportion_x(0), proportion_x(0),
proportion_y(0) { } proportion_y(0) {}
void PlayerDispDataDCPCV3::enforce_v2_limits() { void PlayerDispDataDCPCV3::enforce_v2_limits() {
// V1/V2 have fewer classes, so we'll substitute some here // V1/V2 have fewer classes, so we'll substitute some here
@@ -119,31 +119,29 @@ PlayerDispDataBB PlayerDispDataDCPCV3::to_bb() const {
return bb; return bb;
} }
PlayerDispDataBB::PlayerDispDataBB() noexcept PlayerDispDataBB::PlayerDispDataBB() noexcept
: level(0), : level(0),
experience(0), experience(0),
meseta(0), meseta(0),
unknown_a2(0), unknown_a2(0),
name_color(0), name_color(0),
extra_model(0), extra_model(0),
name_color_checksum(0), name_color_checksum(0),
section_id(0), section_id(0),
char_class(0), char_class(0),
v2_flags(0), v2_flags(0),
version(0), version(0),
v1_flags(0), v1_flags(0),
costume(0), costume(0),
skin(0), skin(0),
face(0), face(0),
head(0), head(0),
hair(0), hair(0),
hair_r(0), hair_r(0),
hair_g(0), hair_g(0),
hair_b(0), hair_b(0),
proportion_x(0), proportion_x(0),
proportion_y(0) { } proportion_y(0) {}
PlayerDispDataDCPCV3 PlayerDispDataBB::to_dcpcv3() const { PlayerDispDataDCPCV3 PlayerDispDataBB::to_dcpcv3() const {
PlayerDispDataDCPCV3 ret; PlayerDispDataDCPCV3 ret;
@@ -263,48 +261,44 @@ void PlayerDispDataBB::apply_dressing_room(const PlayerDispDataBBPreview& pre) {
this->name = pre.name; this->name = pre.name;
} }
PlayerDispDataBBPreview::PlayerDispDataBBPreview() noexcept PlayerDispDataBBPreview::PlayerDispDataBBPreview() noexcept
: experience(0), : experience(0),
level(0), level(0),
unknown_a2(0), unknown_a2(0),
name_color(0), name_color(0),
extra_model(0), extra_model(0),
name_color_checksum(0), name_color_checksum(0),
section_id(0), section_id(0),
char_class(0), char_class(0),
v2_flags(0), v2_flags(0),
version(0), version(0),
v1_flags(0), v1_flags(0),
costume(0), costume(0),
skin(0), skin(0),
face(0), face(0),
head(0), head(0),
hair(0), hair(0),
hair_r(0), hair_r(0),
hair_g(0), hair_g(0),
hair_b(0), hair_b(0),
proportion_x(0), proportion_x(0),
proportion_y(0), proportion_y(0),
play_time(0) { } play_time(0) {}
GuildCardV3::GuildCardV3() noexcept GuildCardV3::GuildCardV3() noexcept
: player_tag(0), : player_tag(0),
guild_card_number(0), guild_card_number(0),
present(0), present(0),
language(0), language(0),
section_id(0), section_id(0),
char_class(0) { } char_class(0) {}
GuildCardBB::GuildCardBB() noexcept GuildCardBB::GuildCardBB() noexcept
: guild_card_number(0), : guild_card_number(0),
present(0), present(0),
language(0), language(0),
section_id(0), section_id(0),
char_class(0) { } char_class(0) {}
void GuildCardBB::clear() { void GuildCardBB::clear() {
this->guild_card_number = 0; this->guild_card_number = 0;
@@ -326,8 +320,6 @@ uint32_t GuildCardFileBB::checksum() const {
return crc32(this, sizeof(*this)); return crc32(this, sizeof(*this));
} }
void PlayerBank::load(const string& filename) { void PlayerBank::load(const string& filename) {
*this = player_files_cache.get_obj_or_load<PlayerBank>(filename).obj; *this = player_files_cache.get_obj_or_load<PlayerBank>(filename).obj;
for (uint32_t x = 0; x < this->num_items; x++) { for (uint32_t x = 0; x < this->num_items; x++) {
@@ -342,16 +334,14 @@ void PlayerBank::save(const string& filename, bool save_to_filesystem) const {
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ClientGameData::ClientGameData() ClientGameData::ClientGameData()
: last_play_time_update(0), : last_play_time_update(0),
guild_card_number(0), guild_card_number(0),
should_update_play_time(false), should_update_play_time(false),
bb_player_index(0), bb_player_index(0),
should_save(true) { } should_save(true) {}
ClientGameData::~ClientGameData() { ClientGameData::~ClientGameData() {
if (!this->bb_username.empty()) { if (!this->bb_username.empty()) {
@@ -461,7 +451,8 @@ void ClientGameData::load_account_data() {
player_files_cache.delete_key(filename); player_files_cache.delete_key(filename);
data.reset(new SavedAccountDataBB( data.reset(new SavedAccountDataBB(
player_files_cache.get_obj_or_load<SavedAccountDataBB>( player_files_cache.get_obj_or_load<SavedAccountDataBB>(
"system/players/default.nsa").obj)); "system/players/default.nsa")
.obj));
if (data->signature != ACCOUNT_FILE_SIGNATURE) { if (data->signature != ACCOUNT_FILE_SIGNATURE) {
throw runtime_error("default account data header is incorrect"); throw runtime_error("default account data header is incorrect");
} }
@@ -592,8 +583,6 @@ PlayerBB ClientGameData::export_player_bb() {
return ret; return ret;
} }
void PlayerLobbyDataPC::clear() { void PlayerLobbyDataPC::clear() {
this->player_tag = 0; this->player_tag = 0;
this->guild_card = 0; this->guild_card = 0;
@@ -638,7 +627,6 @@ void PlayerLobbyDataBB::clear() {
this->unknown_a2 = 0; this->unknown_a2 = 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PlayerInventoryItem::PlayerInventoryItem() { PlayerInventoryItem::PlayerInventoryItem() {
@@ -646,7 +634,9 @@ PlayerInventoryItem::PlayerInventoryItem() {
} }
PlayerInventoryItem::PlayerInventoryItem(const PlayerBankItem& src) PlayerInventoryItem::PlayerInventoryItem(const PlayerBankItem& src)
: present(1), flags(0), data(src.data) { } : present(1),
flags(0),
data(src.data) {}
void PlayerInventoryItem::clear() { void PlayerInventoryItem::clear() {
this->present = 0x00000000; this->present = 0x00000000;
@@ -659,9 +649,9 @@ PlayerBankItem::PlayerBankItem() {
} }
PlayerBankItem::PlayerBankItem(const PlayerInventoryItem& src) PlayerBankItem::PlayerBankItem(const PlayerInventoryItem& src)
: data(src.data), : data(src.data),
amount(this->data.stack_size()), amount(this->data.stack_size()),
show_flags(1) { } show_flags(1) {}
void PlayerBankItem::clear() { void PlayerBankItem::clear() {
this->data.clear(); this->data.clear();
@@ -669,15 +659,11 @@ void PlayerBankItem::clear() {
this->show_flags = 0; this->show_flags = 0;
} }
PlayerInventory::PlayerInventory() PlayerInventory::PlayerInventory()
: num_items(0), : num_items(0),
hp_materials_used(0), hp_materials_used(0),
tp_materials_used(0), tp_materials_used(0),
language(0) { } language(0) {}
// TODO: Eliminate duplication between this function and the parallel function // TODO: Eliminate duplication between this function and the parallel function
// in PlayerBank // in PlayerBank
+55 -79
View File
@@ -4,18 +4,16 @@
#include <stddef.h> #include <stddef.h>
#include <array> #include <array>
#include <string>
#include <vector>
#include <utility>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <string>
#include <utility>
#include <vector>
#include "LevelTable.hh"
#include "ItemData.hh"
#include "Version.hh"
#include "Text.hh"
#include "Episode3/DataIndex.hh" #include "Episode3/DataIndex.hh"
#include "ItemData.hh"
#include "LevelTable.hh"
#include "Text.hh"
#include "Version.hh"
struct PlayerBankItem; struct PlayerBankItem;
@@ -67,8 +65,6 @@ struct PlayerBank { // 0x12C8 bytes
size_t find_item(uint32_t item_id); size_t find_item(uint32_t item_id);
} __attribute__((packed)); } __attribute__((packed));
struct PendingItemTrade { struct PendingItemTrade {
uint8_t other_client_id; uint8_t other_client_id;
bool confirmed; // true if client has sent a D2 command bool confirmed; // true if client has sent a D2 command
@@ -81,8 +77,6 @@ struct PendingCardTrade {
std::vector<std::pair<uint32_t, uint32_t>> card_to_count; std::vector<std::pair<uint32_t, uint32_t>> card_to_count;
}; };
struct PlayerDispDataBB; struct PlayerDispDataBB;
struct PlayerDispDataDCPCV3 { // 0xD0 bytes struct PlayerDispDataDCPCV3 { // 0xD0 bytes
@@ -192,15 +186,13 @@ struct PlayerDispDataBB {
PlayerDispDataBB() noexcept; PlayerDispDataBB() noexcept;
inline void enforce_v2_limits() { } inline void enforce_v2_limits() {}
PlayerDispDataDCPCV3 to_dcpcv3() const; PlayerDispDataDCPCV3 to_dcpcv3() const;
PlayerDispDataBBPreview to_preview() const; PlayerDispDataBBPreview to_preview() const;
void apply_preview(const PlayerDispDataBBPreview&); void apply_preview(const PlayerDispDataBBPreview&);
void apply_dressing_room(const PlayerDispDataBBPreview&); void apply_dressing_room(const PlayerDispDataBBPreview&);
} __attribute__((packed)); } __attribute__((packed));
// TODO: Is this the same for XB as it is for GC? (This struct is based on the // TODO: Is this the same for XB as it is for GC? (This struct is based on the
// GC format) // GC format)
struct GuildCardV3 { struct GuildCardV3 {
@@ -212,7 +204,7 @@ struct GuildCardV3 {
/* 8D */ uint8_t language; /* 8D */ uint8_t language;
/* 8E */ uint8_t section_id; /* 8E */ uint8_t section_id;
/* 8F */ uint8_t char_class; /* 8F */ uint8_t char_class;
/* 90 */ /* 90 */
GuildCardV3() noexcept; GuildCardV3() noexcept;
} __attribute__((packed)); } __attribute__((packed));
@@ -252,21 +244,19 @@ struct GuildCardFileBB {
} __attribute__((packed)); } __attribute__((packed));
struct KeyAndTeamConfigBB { struct KeyAndTeamConfigBB {
parray<uint8_t, 0x0114> unknown_a1; // 0000 parray<uint8_t, 0x0114> unknown_a1; // 0000
parray<uint8_t, 0x016C> key_config; // 0114 parray<uint8_t, 0x016C> key_config; // 0114
parray<uint8_t, 0x0038> joystick_config; // 0280 parray<uint8_t, 0x0038> joystick_config; // 0280
le_uint32_t guild_card_number; // 02B8 le_uint32_t guild_card_number; // 02B8
le_uint32_t team_id; // 02BC le_uint32_t team_id; // 02BC
le_uint64_t team_info; // 02C0 le_uint64_t team_info; // 02C0
le_uint16_t team_privilege_level; // 02C8 le_uint16_t team_privilege_level; // 02C8
le_uint16_t reserved; // 02CA le_uint16_t reserved; // 02CA
ptext<char16_t, 0x0010> team_name; // 02CC ptext<char16_t, 0x0010> team_name; // 02CC
parray<uint8_t, 0x0800> team_flag; // 02EC parray<uint8_t, 0x0800> team_flag; // 02EC
le_uint32_t team_rewards; // 0AEC le_uint32_t team_rewards; // 0AEC
} __attribute__((packed)); } __attribute__((packed));
struct PlayerLobbyDataPC { struct PlayerLobbyDataPC {
le_uint32_t player_tag = 0; le_uint32_t player_tag = 0;
le_uint32_t guild_card = 0; le_uint32_t guild_card = 0;
@@ -328,8 +318,6 @@ struct PlayerLobbyDataBB {
void clear(); void clear();
} __attribute__((packed)); } __attribute__((packed));
struct PlayerChallengeDataV3 { struct PlayerChallengeDataV3 {
le_uint32_t client_id; le_uint32_t client_id;
struct { struct {
@@ -365,8 +353,6 @@ struct PlayerChallengeDataBB {
parray<uint8_t, 0x158> unknown_a1; parray<uint8_t, 0x158> unknown_a1;
} __attribute__((packed)); } __attribute__((packed));
template <typename ItemIDT> template <typename ItemIDT>
struct ChoiceSearchConfig { struct ChoiceSearchConfig {
// 0 = enabled, 1 = disabled. Unused for command C3 // 0 = enabled, 1 = disabled. Unused for command C3
@@ -378,8 +364,6 @@ struct ChoiceSearchConfig {
parray<Entry, 5> entries; parray<Entry, 5> entries;
} __attribute__((packed)); } __attribute__((packed));
struct PSOPlayerDataDCPC { // For command 61 struct PSOPlayerDataDCPC { // For command 61
PlayerInventory inventory; PlayerInventory inventory;
PlayerDispDataDCPCV3 disp; PlayerDispDataDCPCV3 disp;
@@ -423,45 +407,43 @@ struct PSOPlayerDataBB { // For command 61
} __attribute__((packed)); } __attribute__((packed));
struct PlayerBB { // Used in 00E7 command struct PlayerBB { // Used in 00E7 command
PlayerInventory inventory; // 0000-034C; player PlayerInventory inventory; // 0000-034C; player
PlayerDispDataBB disp; // 034C-04DC; player PlayerDispDataBB disp; // 034C-04DC; player
parray<uint8_t, 0x0010> unknown; // 04DC-04EC; not saved parray<uint8_t, 0x0010> unknown; // 04DC-04EC; not saved
le_uint32_t option_flags; // 04EC-04F0; account le_uint32_t option_flags; // 04EC-04F0; account
parray<uint8_t, 0x0208> quest_data1; // 04F0-06F8; player parray<uint8_t, 0x0208> quest_data1; // 04F0-06F8; player
PlayerBank bank; // 06F8-19C0; player PlayerBank bank; // 06F8-19C0; player
le_uint32_t guild_card_number; // 19C0-19C4; player le_uint32_t guild_card_number; // 19C0-19C4; player
ptext<char16_t, 0x18> name; // 19C4-19F4; player ptext<char16_t, 0x18> name; // 19C4-19F4; player
ptext<char16_t, 0x10> team_name; // 19F4-1A14; player ptext<char16_t, 0x10> team_name; // 19F4-1A14; player
ptext<char16_t, 0x58> guild_card_description; // 1A14-1AC4; player ptext<char16_t, 0x58> guild_card_description; // 1A14-1AC4; player
uint8_t reserved1; // 1AC4-1AC5; player uint8_t reserved1; // 1AC4-1AC5; player
uint8_t reserved2; // 1AC5-1AC6; player uint8_t reserved2; // 1AC5-1AC6; player
uint8_t section_id; // 1AC6-1AC7; player uint8_t section_id; // 1AC6-1AC7; player
uint8_t char_class; // 1AC7-1AC8; player uint8_t char_class; // 1AC7-1AC8; player
le_uint32_t unknown3; // 1AC8-1ACC; not saved le_uint32_t unknown3; // 1AC8-1ACC; not saved
parray<uint8_t, 0x04E0> symbol_chats; // 1ACC-1FAC; account parray<uint8_t, 0x04E0> symbol_chats; // 1ACC-1FAC; account
parray<uint8_t, 0x0A40> shortcuts; // 1FAC-29EC; account parray<uint8_t, 0x0A40> shortcuts; // 1FAC-29EC; account
ptext<char16_t, 0x00AC> auto_reply; // 29EC-2B44; player ptext<char16_t, 0x00AC> auto_reply; // 29EC-2B44; player
ptext<char16_t, 0x00AC> info_board; // 2B44-2C9C; player ptext<char16_t, 0x00AC> info_board; // 2B44-2C9C; player
parray<uint8_t, 0x001C> unknown5; // 2C9C-2CB8; not saved parray<uint8_t, 0x001C> unknown5; // 2C9C-2CB8; not saved
parray<uint8_t, 0x0140> challenge_data; // 2CB8-2DF8; player parray<uint8_t, 0x0140> challenge_data; // 2CB8-2DF8; player
parray<uint8_t, 0x0028> tech_menu_config; // 2DF8-2E20; player parray<uint8_t, 0x0028> tech_menu_config; // 2DF8-2E20; player
parray<uint8_t, 0x002C> unknown6; // 2E20-2E4C; not saved parray<uint8_t, 0x002C> unknown6; // 2E20-2E4C; not saved
parray<uint8_t, 0x0058> quest_data2; // 2E4C-2EA4; player parray<uint8_t, 0x0058> quest_data2; // 2E4C-2EA4; player
KeyAndTeamConfigBB key_config; // 2EA4-3994; account KeyAndTeamConfigBB key_config; // 2EA4-3994; account
} __attribute__((packed)); } __attribute__((packed));
struct SavedPlayerDataBB { // .nsc file format struct SavedPlayerDataBB { // .nsc file format
ptext<char, 0x40> signature; ptext<char, 0x40> signature;
PlayerDispDataBBPreview preview; PlayerDispDataBBPreview preview;
ptext<char16_t, 0x00AC> auto_reply; ptext<char16_t, 0x00AC> auto_reply;
PlayerBank bank; PlayerBank bank;
parray<uint8_t, 0x0140> challenge_data; parray<uint8_t, 0x0140> challenge_data;
PlayerDispDataBB disp; PlayerDispDataBB disp;
ptext<char16_t, 0x0058> guild_card_description; ptext<char16_t, 0x0058> guild_card_description;
ptext<char16_t, 0x00AC> info_board; ptext<char16_t, 0x00AC> info_board;
PlayerInventory inventory; PlayerInventory inventory;
parray<uint8_t, 0x0208> quest_data1; parray<uint8_t, 0x0208> quest_data1;
parray<uint8_t, 0x0058> quest_data2; parray<uint8_t, 0x0058> quest_data2;
parray<uint8_t, 0x0028> tech_menu_config; parray<uint8_t, 0x0028> tech_menu_config;
@@ -478,19 +460,17 @@ enum AccountFlag {
}; };
struct SavedAccountDataBB { // .nsa file format struct SavedAccountDataBB { // .nsa file format
ptext<char, 0x40> signature; ptext<char, 0x40> signature;
parray<le_uint32_t, 0x001E> blocked_senders; parray<le_uint32_t, 0x001E> blocked_senders;
GuildCardFileBB guild_cards; GuildCardFileBB guild_cards;
KeyAndTeamConfigBB key_config; KeyAndTeamConfigBB key_config;
le_uint32_t newserv_flags; le_uint32_t newserv_flags;
le_uint32_t option_flags; le_uint32_t option_flags;
parray<uint8_t, 0x0A40> shortcuts; parray<uint8_t, 0x0A40> shortcuts;
parray<uint8_t, 0x04E0> symbol_chats; parray<uint8_t, 0x04E0> symbol_chats;
ptext<char16_t, 0x0010> team_name; ptext<char16_t, 0x0010> team_name;
} __attribute__((packed)); } __attribute__((packed));
class ClientGameData { class ClientGameData {
private: private:
std::shared_ptr<SavedAccountDataBB> account_data; std::shared_ptr<SavedAccountDataBB> account_data;
@@ -547,12 +527,8 @@ public:
PlayerBB export_player_bb(); PlayerBB export_player_bb();
}; };
uint32_t compute_guild_card_checksum(const void* data, size_t size); uint32_t compute_guild_card_checksum(const void* data, size_t size);
template <typename DestT, typename SrcT = DestT> template <typename DestT, typename SrcT = DestT>
DestT convert_player_disp_data(const SrcT&) { DestT convert_player_disp_data(const SrcT&) {
static_assert(always_false<DestT, SrcT>::v, static_assert(always_false<DestT, SrcT>::v,
+1 -5
View File
@@ -1,15 +1,13 @@
#include <stdint.h> #include <stdint.h>
#include <phosg/Strings.hh>
#include <phosg/Random.hh> #include <phosg/Random.hh>
#include <phosg/Strings.hh>
#include <phosg/Time.hh> #include <phosg/Time.hh>
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
using namespace std; using namespace std;
static const uint32_t primes1[] = { static const uint32_t primes1[] = {
0x65, 0x67, 0x6B, 0x6D, 0x71, 0x7F, 0x83, 0x89, 0x8B, 0x95, 0x97, 0x9D, 0x65, 0x67, 0x6B, 0x6D, 0x71, 0x7F, 0x83, 0x89, 0x8B, 0x95, 0x97, 0x9D,
0xA3, 0xA7, 0xAD, 0xB3, 0xB5, 0xBF, 0xC1, 0xC5, 0xC7, 0xD3, 0xDF, 0xE3, 0xA3, 0xA7, 0xAD, 0xB3, 0xB5, 0xBF, 0xC1, 0xC5, 0xC7, 0xD3, 0xDF, 0xE3,
@@ -1125,8 +1123,6 @@ static bool check_prime3(uint64_t prime3) {
return primes3_set[offset]; return primes3_set[offset];
} }
static char replace_char_forward(char ch) { static char replace_char_forward(char ch) {
if (ch >= '0' && ch <= '9') { if (ch >= '0' && ch <= '9') {
return "5A76D2C1F0"[ch - '0']; return "5A76D2C1F0"[ch - '0'];
-2
View File
@@ -4,8 +4,6 @@
#include <string> #include <string>
bool product_is_valid( bool product_is_valid(
const std::string& s, uint8_t domain, uint8_t subdomain = 0xFF); const std::string& s, uint8_t domain, uint8_t subdomain = 0xFF);
bool product_is_valid_fast( bool product_is_valid_fast(
+31 -36
View File
@@ -8,8 +8,8 @@
#include <event2/event.h> #include <event2/event.h>
#include <event2/listener.h> #include <event2/listener.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -26,18 +26,16 @@
#include <resource_file/Emulators/PPC32Emulator.hh> #include <resource_file/Emulators/PPC32Emulator.hh>
#endif #endif
#include "Loggers.hh"
#include "ChatCommands.hh" #include "ChatCommands.hh"
#include "Compression.hh" #include "Compression.hh"
#include "Loggers.hh"
#include "PSOProtocol.hh" #include "PSOProtocol.hh"
#include "SendCommands.hh"
#include "ReceiveCommands.hh" #include "ReceiveCommands.hh"
#include "ReceiveSubcommands.hh" #include "ReceiveSubcommands.hh"
#include "SendCommands.hh"
using namespace std; using namespace std;
struct HandlerResult { struct HandlerResult {
enum class Type { enum class Type {
FORWARD = 0, FORWARD = 0,
@@ -50,9 +48,13 @@ struct HandlerResult {
int32_t new_command; int32_t new_command;
int64_t new_flag; int64_t new_flag;
HandlerResult(Type type) : type(type), new_command(-1), new_flag(-1) { } HandlerResult(Type type) : type(type),
new_command(-1),
new_flag(-1) {}
HandlerResult(Type type, uint16_t new_command, uint32_t new_flag) HandlerResult(Type type, uint16_t new_command, uint32_t new_flag)
: type(type), new_command(new_command), new_flag(new_flag) { } : type(type),
new_command(new_command),
new_flag(new_flag) {}
}; };
typedef HandlerResult (*on_command_t)( typedef HandlerResult (*on_command_t)(
@@ -62,8 +64,6 @@ typedef HandlerResult (*on_command_t)(
uint32_t flag, uint32_t flag,
string& data); string& data);
static void forward_command(ProxyServer::LinkedSession& session, bool to_server, static void forward_command(ProxyServer::LinkedSession& session, bool to_server,
uint16_t command, uint32_t flag, string& data, bool print_contents = true) { uint16_t command, uint32_t flag, string& data, bool print_contents = true) {
auto& ch = to_server ? session.server_channel : session.client_channel; auto& ch = to_server ? session.server_channel : session.client_channel;
@@ -86,8 +86,6 @@ static void check_implemented_subcommand(
} }
} }
// Command handlers. These are called to preprocess or react to specific // Command handlers. These are called to preprocess or react to specific
// commands in either direction. The functions have abbreviated names in order // commands in either direction. The functions have abbreviated names in order
// to make the massive table more readable. The functions' names are, in // to make the massive table more readable. The functions' names are, in
@@ -148,8 +146,7 @@ static HandlerResult C_G_9E(shared_ptr<ServerState>,
le_uint64_t checksum = random_object<uint64_t>() & 0x0000FFFFFFFFFFFF; le_uint64_t checksum = random_object<uint64_t>() & 0x0000FFFFFFFFFFFF;
session.server_channel.send(0x96, 0x00, &checksum, sizeof(checksum)); session.server_channel.send(0x96, 0x00, &checksum, sizeof(checksum));
S_UpdateClientConfig_DC_PC_V3_04 cmd = {{ S_UpdateClientConfig_DC_PC_V3_04 cmd = {{0x00010000, session.license->serial_number, ClientConfig()}};
0x00010000, session.license->serial_number, ClientConfig()}};
session.client_channel.send(0x04, 0x00, &cmd, sizeof(cmd)); session.client_channel.send(0x04, 0x00, &cmd, sizeof(cmd));
return HandlerResult::Type::SUPPRESS; return HandlerResult::Type::SUPPRESS;
@@ -159,7 +156,6 @@ static HandlerResult C_G_9E(shared_ptr<ServerState>,
} }
} }
static HandlerResult S_G_9A(shared_ptr<ServerState>, static HandlerResult S_G_9A(shared_ptr<ServerState>,
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string&) { ProxyServer::LinkedSession& session, uint16_t, uint32_t, string&) {
if (!session.license || session.options.suppress_remote_login) { if (!session.license || session.options.suppress_remote_login) {
@@ -265,7 +261,7 @@ static HandlerResult S_V123P_02_17(
return HandlerResult::Type::SUPPRESS; return HandlerResult::Type::SUPPRESS;
} else if ((session.version == GameVersion::DC) || } else if ((session.version == GameVersion::DC) ||
(session.version == GameVersion::PC)) { (session.version == GameVersion::PC)) {
if (session.newserv_client_config.cfg.flags & Client::Flag::IS_DC_V1) { if (session.newserv_client_config.cfg.flags & Client::Flag::IS_DC_V1) {
if (command == 0x17) { if (command == 0x17) {
C_LoginV1_DC_PC_V3_90 cmd; C_LoginV1_DC_PC_V3_90 cmd;
@@ -447,9 +443,9 @@ static HandlerResult S_B_03(shared_ptr<ServerState> s,
return HandlerResult::Type::SUPPRESS; return HandlerResult::Type::SUPPRESS;
// If there's no detector crypt, then the session is new and was linked // If there's no detector crypt, then the session is new and was linked
// immediately at connect time, and an 03 was not yet sent to the client, so // immediately at connect time, and an 03 was not yet sent to the client, so
// we should forward this one. // we should forward this one.
} else { } else {
// Forward the command to the client before setting up the crypts, so the // Forward the command to the client before setting up the crypts, so the
// client receives the unencrypted data // client receives the unencrypted data
@@ -511,12 +507,12 @@ static HandlerResult S_V123_04(shared_ptr<ServerState>,
// init text instead of the port map init text. // init text instead of the port map init text.
memcpy(session.remote_client_config_data.data(), memcpy(session.remote_client_config_data.data(),
had_guild_card_number had_guild_card_number
? "t Lobby Server. Copyright SEGA E" ? "t Lobby Server. Copyright SEGA E"
: "t Port Map. Copyright SEGA Enter", : "t Port Map. Copyright SEGA Enter",
session.remote_client_config_data.bytes()); session.remote_client_config_data.bytes());
memcpy(session.remote_client_config_data.data(), &cmd.cfg, memcpy(session.remote_client_config_data.data(), &cmd.cfg,
min<size_t>(data.size() - offsetof(S_UpdateClientConfig_DC_PC_V3_04, cfg), min<size_t>(data.size() - offsetof(S_UpdateClientConfig_DC_PC_V3_04, cfg),
session.remote_client_config_data.bytes())); session.remote_client_config_data.bytes()));
// If the guild card number was not set, pretend (to the server) that this is // If the guild card number was not set, pretend (to the server) that this is
// the first 04 command the client has received. The client responds with a 96 // the first 04 command the client has received. The client responds with a 96
@@ -912,8 +908,8 @@ static HandlerResult S_6x(shared_ptr<ServerState>,
// Unmask any masked Episode 3 commands from the server // Unmask any masked Episode 3 commands from the server
if ((session.version == GameVersion::GC) && (data.size() > 8) && if ((session.version == GameVersion::GC) && (data.size() > 8) &&
((static_cast<uint8_t>(data[0]) == 0xB3) || ((static_cast<uint8_t>(data[0]) == 0xB3) ||
(static_cast<uint8_t>(data[0]) == 0xB4) || (static_cast<uint8_t>(data[0]) == 0xB4) ||
(static_cast<uint8_t>(data[0]) == 0xB5))) { (static_cast<uint8_t>(data[0]) == 0xB5))) {
const auto& header = check_size_t<G_CardBattleCommandHeader>( const auto& header = check_size_t<G_CardBattleCommandHeader>(
data, sizeof(G_CardBattleCommandHeader), 0xFFFF); data, sizeof(G_CardBattleCommandHeader), 0xFFFF);
if (header.mask_key) { if (header.mask_key) {
@@ -951,8 +947,8 @@ static HandlerResult S_6x(shared_ptr<ServerState>,
} }
} else if ((data[0] == 0x60) && } else if ((data[0] == 0x60) &&
session.next_drop_item.data.data1d[0] && session.next_drop_item.data.data1d[0] &&
(session.version != GameVersion::BB)) { (session.version != GameVersion::BB)) {
const auto& cmd = check_size_t<G_EnemyDropItemRequest_DC_6x60>(data, const auto& cmd = check_size_t<G_EnemyDropItemRequest_DC_6x60>(data,
sizeof(G_EnemyDropItemRequest_DC_6x60), sizeof(G_EnemyDropItemRequest_DC_6x60),
sizeof(G_EnemyDropItemRequest_PC_V3_BB_6x60)); sizeof(G_EnemyDropItemRequest_PC_V3_BB_6x60));
@@ -964,13 +960,13 @@ static HandlerResult S_6x(shared_ptr<ServerState>,
session.next_drop_item.clear(); session.next_drop_item.clear();
return HandlerResult::Type::SUPPRESS; return HandlerResult::Type::SUPPRESS;
// Note: This static_cast is required to make compilers not complain that // Note: This static_cast is required to make compilers not complain that
// the comparison is always false (which even happens in some environments // the comparison is always false (which even happens in some environments
// if we use -0x5E... apparently char is unsigned on some systems, or // if we use -0x5E... apparently char is unsigned on some systems, or
// std::string's char_type isn't char??) // std::string's char_type isn't char??)
} else if ((static_cast<uint8_t>(data[0]) == 0xA2) && } else if ((static_cast<uint8_t>(data[0]) == 0xA2) &&
session.next_drop_item.data.data1d[0] && session.next_drop_item.data.data1d[0] &&
(session.version != GameVersion::BB)) { (session.version != GameVersion::BB)) {
const auto& cmd = check_size_t<G_BoxItemDropRequest_6xA2>(data); const auto& cmd = check_size_t<G_BoxItemDropRequest_6xA2>(data);
session.next_drop_item.data.id = session.next_item_id++; session.next_drop_item.data.id = session.next_item_id++;
send_drop_item(session.server_channel, session.next_drop_item.data, send_drop_item(session.server_channel, session.next_drop_item.data,
@@ -981,8 +977,8 @@ static HandlerResult S_6x(shared_ptr<ServerState>,
return HandlerResult::Type::SUPPRESS; return HandlerResult::Type::SUPPRESS;
} else if ((static_cast<uint8_t>(data[0]) == 0xB5) && } else if ((static_cast<uint8_t>(data[0]) == 0xB5) &&
(session.version == GameVersion::GC) && (session.version == GameVersion::GC) &&
(data.size() > 4)) { (data.size() > 4)) {
if (data[4] == 0x1A) { if (data[4] == 0x1A) {
return HandlerResult::Type::SUPPRESS; return HandlerResult::Type::SUPPRESS;
} else if (data[4] == 0x36) { } else if (data[4] == 0x36) {
@@ -1586,7 +1582,6 @@ constexpr on_command_t C_DGX_81 = &C_81<SC_SimpleMail_DC_V3_81>;
constexpr on_command_t C_P_81 = &C_81<SC_SimpleMail_PC_81>; constexpr on_command_t C_P_81 = &C_81<SC_SimpleMail_PC_81>;
constexpr on_command_t C_B_81 = &C_81<SC_SimpleMail_BB_81>; constexpr on_command_t C_B_81 = &C_81<SC_SimpleMail_BB_81>;
template <typename CmdT> template <typename CmdT>
void C_6x_movement(ProxyServer::LinkedSession& session, const string& data) { void C_6x_movement(ProxyServer::LinkedSession& session, const string& data) {
const auto& cmd = check_size_t<CmdT>(data); const auto& cmd = check_size_t<CmdT>(data);
@@ -1676,10 +1671,9 @@ static HandlerResult C_V123_A0_A1(shared_ptr<ServerState>,
return HandlerResult::Type::SUPPRESS; return HandlerResult::Type::SUPPRESS;
} }
// [command][version][is_client] // [command][version][is_client]
static on_command_t handlers[0x100][6][2] = { static on_command_t handlers[0x100][6][2] = {
// clang-format off
// CMD S-PATCH C-PATCH S-DC C-DC S-PC C-PC S-GC C-GC S-XB C-XB S-BB C-BB // CMD S-PATCH C-PATCH S-DC C-DC S-PC C-PC S-GC C-GC S-XB C-XB S-BB C-BB
/* 00 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* 00 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}},
/* 01 */ {{S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}}, /* 01 */ {{S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}},
@@ -1945,6 +1939,7 @@ static on_command_t handlers[0x100][6][2] = {
/* FE */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* FE */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}},
/* FF */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* FF */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}},
// CMD S-PATCH C-PATCH S-DC C-DC S-PC C-PC S-GC C-GC S-XB C-XB S-BB C-BB // CMD S-PATCH C-PATCH S-DC C-DC S-PC C-PC S-GC C-GC S-XB C-XB S-BB C-BB
// clang-format on
}; };
static on_command_t get_handler(GameVersion version, bool from_server, uint8_t command) { static on_command_t get_handler(GameVersion version, bool from_server, uint8_t command) {
+1 -3
View File
@@ -4,10 +4,8 @@
#include <string> #include <string>
#include "ServerState.hh"
#include "ProxyServer.hh" #include "ProxyServer.hh"
#include "ServerState.hh"
void on_proxy_command( void on_proxy_command(
std::shared_ptr<ServerState> s, std::shared_ptr<ServerState> s,
+83 -111
View File
@@ -8,8 +8,8 @@
#include <event2/event.h> #include <event2/event.h>
#include <event2/listener.h> #include <event2/listener.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -25,23 +25,21 @@
#include "Loggers.hh" #include "Loggers.hh"
#include "PSOProtocol.hh" #include "PSOProtocol.hh"
#include "SendCommands.hh" #include "ProxyCommands.hh"
#include "ReceiveCommands.hh" #include "ReceiveCommands.hh"
#include "ReceiveSubcommands.hh" #include "ReceiveSubcommands.hh"
#include "ProxyCommands.hh" #include "SendCommands.hh"
using namespace std; using namespace std;
using namespace std::placeholders; using namespace std::placeholders;
ProxyServer::ProxyServer( ProxyServer::ProxyServer(
shared_ptr<struct event_base> base, shared_ptr<struct event_base> base,
shared_ptr<ServerState> state) shared_ptr<ServerState> state)
: base(base), : base(base),
destroy_sessions_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &ProxyServer::dispatch_destroy_sessions, this), event_free), destroy_sessions_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &ProxyServer::dispatch_destroy_sessions, this), event_free),
state(state), state(state),
next_unlicensed_session_id(0xFF00000000000001) { } next_unlicensed_session_id(0xFF00000000000001) {}
void ProxyServer::listen(uint16_t port, GameVersion version, void ProxyServer::listen(uint16_t port, GameVersion version,
const struct sockaddr_storage* default_destination) { const struct sockaddr_storage* default_destination) {
@@ -55,12 +53,12 @@ ProxyServer::ListeningSocket::ListeningSocket(
uint16_t port, uint16_t port,
GameVersion version, GameVersion version,
const struct sockaddr_storage* default_destination) const struct sockaddr_storage* default_destination)
: server(server), : server(server),
log(string_printf("[ProxyServer:ListeningSocket:%hu] ", port), proxy_server_log.min_level), log(string_printf("[ProxyServer:ListeningSocket:%hu] ", port), proxy_server_log.min_level),
port(port), port(port),
fd(::listen("", port, SOMAXCONN)), fd(::listen("", port, SOMAXCONN)),
listener(nullptr, evconnlistener_free), listener(nullptr, evconnlistener_free),
version(version) { version(version) {
if (!this->fd.is_open()) { if (!this->fd.is_open()) {
throw runtime_error("cannot listen on port"); throw runtime_error("cannot listen on port");
} }
@@ -114,8 +112,6 @@ void ProxyServer::ListeningSocket::on_listen_error() {
event_base_loopexit(this->server->base.get(), nullptr); event_base_loopexit(this->server->base.get(), nullptr);
} }
void ProxyServer::connect_client(struct bufferevent* bev, uint16_t server_port) { void ProxyServer::connect_client(struct bufferevent* bev, uint16_t server_port) {
// Look up the listening socket for the given port, and use that game version. // Look up the listening socket for the given port, and use that game version.
// We don't support default-destination proxying for virtual connections (yet) // We don't support default-destination proxying for virtual connections (yet)
@@ -135,8 +131,6 @@ void ProxyServer::connect_client(struct bufferevent* bev, uint16_t server_port)
this->on_client_connect(bev, server_port, version, nullptr); this->on_client_connect(bev, server_port, version, nullptr);
} }
void ProxyServer::on_client_connect( void ProxyServer::on_client_connect(
struct bufferevent* bev, struct bufferevent* bev,
uint16_t listen_port, uint16_t listen_port,
@@ -151,8 +145,7 @@ void ProxyServer::on_client_connect(
this->next_unlicensed_session_id = 0xFF00000000000001; this->next_unlicensed_session_id = 0xFF00000000000001;
} }
auto emplace_ret = this->id_to_session.emplace(session_id, new LinkedSession( auto emplace_ret = this->id_to_session.emplace(session_id, new LinkedSession(this, session_id, listen_port, version, *default_destination));
this, session_id, listen_port, version, *default_destination));
if (!emplace_ret.second) { if (!emplace_ret.second) {
throw logic_error("linked session already exists for unlicensed client"); throw logic_error("linked session already exists for unlicensed client");
} }
@@ -162,12 +155,11 @@ void ProxyServer::on_client_connect(
Channel ch(bev, version, nullptr, nullptr, session.get(), "", TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN); Channel ch(bev, version, nullptr, nullptr, session.get(), "", TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN);
session->resume(move(ch)); session->resume(move(ch));
// If no default destination exists, or the client is not a patch client, // If no default destination exists, or the client is not a patch client,
// create an unlinked session - we'll have to get the destination from the // create an unlinked session - we'll have to get the destination from the
// client's config, which we'll get via a 9E command soon. // client's config, which we'll get via a 9E command soon.
} else { } else {
auto emplace_ret = this->bev_to_unlinked_session.emplace(bev, new UnlinkedSession( auto emplace_ret = this->bev_to_unlinked_session.emplace(bev, new UnlinkedSession(this, bev, listen_port, version));
this, bev, listen_port, version));
if (!emplace_ret.second) { if (!emplace_ret.second) {
throw logic_error("stale unlinked session exists"); throw logic_error("stale unlinked session exists");
} }
@@ -227,26 +219,24 @@ void ProxyServer::on_client_connect(
} }
} }
ProxyServer::UnlinkedSession::UnlinkedSession( ProxyServer::UnlinkedSession::UnlinkedSession(
ProxyServer* server, ProxyServer* server,
struct bufferevent* bev, struct bufferevent* bev,
uint16_t local_port, uint16_t local_port,
GameVersion version) GameVersion version)
: server(server), : server(server),
log(string_printf("[ProxyServer:UnlinkedSession:%p] ", bev), proxy_server_log.min_level), log(string_printf("[ProxyServer:UnlinkedSession:%p] ", bev), proxy_server_log.min_level),
channel( channel(
bev, bev,
version, version,
ProxyServer::UnlinkedSession::on_input, ProxyServer::UnlinkedSession::on_input,
ProxyServer::UnlinkedSession::on_error, ProxyServer::UnlinkedSession::on_error,
this, this,
string_printf("UnlinkedSession:%p", bev), string_printf("UnlinkedSession:%p", bev),
TerminalFormat::FG_YELLOW, TerminalFormat::FG_YELLOW,
TerminalFormat::FG_GREEN), TerminalFormat::FG_GREEN),
local_port(local_port), local_port(local_port),
version(version) { version(version) {
memset(&this->next_destination, 0, sizeof(this->next_destination)); memset(&this->next_destination, 0, sizeof(this->next_destination));
} }
@@ -440,51 +430,50 @@ void ProxyServer::UnlinkedSession::on_error(Channel& ch, short events) {
} }
} }
ProxyServer::LinkedSession::LinkedSession( ProxyServer::LinkedSession::LinkedSession(
ProxyServer* server, ProxyServer* server,
uint64_t id, uint64_t id,
uint16_t local_port, uint16_t local_port,
GameVersion version) GameVersion version)
: server(server), : server(server),
id(id), id(id),
log(string_printf("[ProxyServer:LinkedSession:%08" PRIX64 "] ", this->id), proxy_server_log.min_level), log(string_printf("[ProxyServer:LinkedSession:%08" PRIX64 "] ", this->id), proxy_server_log.min_level),
timeout_event(event_new(this->server->base.get(), -1, EV_TIMEOUT, timeout_event(event_new(this->server->base.get(), -1, EV_TIMEOUT,
&LinkedSession::dispatch_on_timeout, this), event_free), &LinkedSession::dispatch_on_timeout, this),
license(nullptr), event_free),
client_channel( license(nullptr),
version, client_channel(
nullptr, version,
nullptr, nullptr,
this, nullptr,
string_printf("LinkedSession:%08" PRIX64 ":client", this->id), this,
TerminalFormat::FG_YELLOW, string_printf("LinkedSession:%08" PRIX64 ":client", this->id),
TerminalFormat::FG_GREEN), TerminalFormat::FG_YELLOW,
server_channel( TerminalFormat::FG_GREEN),
version, server_channel(
nullptr, version,
nullptr, nullptr,
this, nullptr,
string_printf("LinkedSession:%08" PRIX64 ":server", this->id), this,
TerminalFormat::FG_YELLOW, string_printf("LinkedSession:%08" PRIX64 ":server", this->id),
TerminalFormat::FG_RED), TerminalFormat::FG_YELLOW,
local_port(local_port), TerminalFormat::FG_RED),
disconnect_action(DisconnectAction::LONG_TIMEOUT), local_port(local_port),
remote_ip_crc(0), disconnect_action(DisconnectAction::LONG_TIMEOUT),
enable_remote_ip_crc_patch(false), remote_ip_crc(0),
version(version), enable_remote_ip_crc_patch(false),
sub_version(0), // This is set during resume() version(version),
language(1), // Default = English. This is also set during resume() sub_version(0), // This is set during resume()
remote_guild_card_number(-1), language(1), // Default = English. This is also set during resume()
next_item_id(0x0F000000), remote_guild_card_number(-1),
lobby_players(12), next_item_id(0x0F000000),
lobby_client_id(0), lobby_players(12),
leader_client_id(0), lobby_client_id(0),
area(0), leader_client_id(0),
x(0.0), area(0),
z(0.0), x(0.0),
is_in_game(false) { z(0.0),
is_in_game(false) {
this->last_switch_enabled_command.header.subcommand = 0; this->last_switch_enabled_command.header.subcommand = 0;
memset(this->prev_server_command_bytes, 0, sizeof(this->prev_server_command_bytes)); memset(this->prev_server_command_bytes, 0, sizeof(this->prev_server_command_bytes));
} }
@@ -495,7 +484,7 @@ ProxyServer::LinkedSession::LinkedSession(
GameVersion version, GameVersion version,
shared_ptr<const License> license, shared_ptr<const License> license,
const ClientConfigBB& newserv_client_config) const ClientConfigBB& newserv_client_config)
: LinkedSession(server, license->serial_number, local_port, version) { : LinkedSession(server, license->serial_number, local_port, version) {
this->license = license; this->license = license;
this->newserv_client_config = newserv_client_config; this->newserv_client_config = newserv_client_config;
memset(&this->next_destination, 0, sizeof(this->next_destination)); memset(&this->next_destination, 0, sizeof(this->next_destination));
@@ -511,7 +500,7 @@ ProxyServer::LinkedSession::LinkedSession(
GameVersion version, GameVersion version,
std::shared_ptr<const License> license, std::shared_ptr<const License> license,
const struct sockaddr_storage& next_destination) const struct sockaddr_storage& next_destination)
: LinkedSession(server, license->serial_number, local_port, version) { : LinkedSession(server, license->serial_number, local_port, version) {
this->license = license; this->license = license;
this->next_destination = next_destination; this->next_destination = next_destination;
} }
@@ -522,7 +511,7 @@ ProxyServer::LinkedSession::LinkedSession(
uint16_t local_port, uint16_t local_port,
GameVersion version, GameVersion version,
const struct sockaddr_storage& destination) const struct sockaddr_storage& destination)
: LinkedSession(server, id, local_port, version) { : LinkedSession(server, id, local_port, version) {
this->next_destination = destination; this->next_destination = destination;
} }
@@ -594,7 +583,7 @@ void ProxyServer::LinkedSession::connect() {
this->server_channel.set_bufferevent(bufferevent_socket_new( this->server_channel.set_bufferevent(bufferevent_socket_new(
this->server->base.get(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS)); this->server->base.get(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS));
if (bufferevent_socket_connect(this->server_channel.bev.get(), if (bufferevent_socket_connect(this->server_channel.bev.get(),
reinterpret_cast<const sockaddr*>(dest_sin), sizeof(*dest_sin)) != 0) { reinterpret_cast<const sockaddr*>(dest_sin), sizeof(*dest_sin)) != 0) {
throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR())); throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR()));
} }
@@ -606,35 +595,27 @@ void ProxyServer::LinkedSession::connect() {
event_del(this->timeout_event.get()); event_del(this->timeout_event.get());
} }
ProxyServer::LinkedSession::SavingFile::SavingFile( ProxyServer::LinkedSession::SavingFile::SavingFile(
const string& basename, const string& basename,
const string& output_filename, const string& output_filename,
uint32_t remaining_bytes) uint32_t remaining_bytes)
: basename(basename), : basename(basename),
output_filename(output_filename), output_filename(output_filename),
remaining_bytes(remaining_bytes) { remaining_bytes(remaining_bytes) {
if (!this->output_filename.empty()) { if (!this->output_filename.empty()) {
this->f = fopen_unique(this->output_filename, "wb"); this->f = fopen_unique(this->output_filename, "wb");
} }
} }
void ProxyServer::LinkedSession::dispatch_on_timeout( void ProxyServer::LinkedSession::dispatch_on_timeout(
evutil_socket_t, short, void* ctx) { evutil_socket_t, short, void* ctx) {
reinterpret_cast<LinkedSession*>(ctx)->on_timeout(); reinterpret_cast<LinkedSession*>(ctx)->on_timeout();
} }
void ProxyServer::LinkedSession::on_timeout() { void ProxyServer::LinkedSession::on_timeout() {
this->server->delete_session(this->id); this->server->delete_session(this->id);
} }
void ProxyServer::LinkedSession::on_error(Channel& ch, short events) { void ProxyServer::LinkedSession::on_error(Channel& ch, short events) {
auto* session = reinterpret_cast<LinkedSession*>(ch.context_obj); auto* session = reinterpret_cast<LinkedSession*>(ch.context_obj);
bool is_server_stream = (&ch == &session->server_channel); bool is_server_stream = (&ch == &session->server_channel);
@@ -643,11 +624,9 @@ void ProxyServer::LinkedSession::on_error(Channel& ch, short events) {
session->log.info("%s channel connected", is_server_stream ? "Server" : "Client"); session->log.info("%s channel connected", is_server_stream ? "Server" : "Client");
if (is_server_stream && (session->options.override_lobby_event >= 0) && if (is_server_stream && (session->options.override_lobby_event >= 0) &&
( (((session->version == GameVersion::GC) && !(session->newserv_client_config.cfg.flags & Client::Flag::IS_TRIAL_EDITION)) ||
((session->version == GameVersion::GC) && !(session->newserv_client_config.cfg.flags & Client::Flag::IS_TRIAL_EDITION)) || (session->version == GameVersion::XB) ||
(session->version == GameVersion::XB) || (session->version == GameVersion::BB))) {
(session->version == GameVersion::BB)
)) {
session->client_channel.send(0xDA, session->options.override_lobby_event); session->client_channel.send(0xDA, session->options.override_lobby_event);
} }
} }
@@ -702,15 +681,11 @@ void ProxyServer::LinkedSession::send_to_game_server(const char* error_message)
string encoded_name = encode_sjis(this->server->state->name); string encoded_name = encode_sjis(this->server->state->name);
if (this->is_in_game) { if (this->is_in_game) {
send_ship_info(this->client_channel, decode_sjis(string_printf( send_ship_info(this->client_channel, decode_sjis(string_printf("You cannot return\nto $C6%s$C7\nwhile in a game.\n\n%s", encoded_name.c_str(), error_message ? error_message : "")));
"You cannot return\nto $C6%s$C7\nwhile in a game.\n\n%s",
encoded_name.c_str(), error_message ? error_message : "")));
this->disconnect(); this->disconnect();
} else { } else {
send_ship_info(this->client_channel, decode_sjis(string_printf( send_ship_info(this->client_channel, decode_sjis(string_printf("You\'ve returned to\n\tC6%s$C7\n\n%s", encoded_name.c_str(), error_message ? error_message : "")));
"You\'ve returned to\n\tC6%s$C7\n\n%s", encoded_name.c_str(),
error_message ? error_message : "")));
// Restore newserv_client_config, so the login server gets the client flags // Restore newserv_client_config, so the login server gets the client flags
S_UpdateClientConfig_DC_PC_V3_04 update_client_config_cmd; S_UpdateClientConfig_DC_PC_V3_04 update_client_config_cmd;
@@ -722,8 +697,7 @@ void ProxyServer::LinkedSession::send_to_game_server(const char* error_message)
const auto& port_name = version_to_login_port_name.at(static_cast<size_t>( const auto& port_name = version_to_login_port_name.at(static_cast<size_t>(
this->version)); this->version));
S_Reconnect_19 reconnect_cmd = {{ S_Reconnect_19 reconnect_cmd = {{0, this->server->state->name_to_port_config.at(port_name)->port, 0}};
0, this->server->state->name_to_port_config.at(port_name)->port, 0}};
// If the client is on a virtual connection, we can use any address // If the client is on a virtual connection, we can use any address
// here and they should be able to connect back to the game server. If // here and they should be able to connect back to the game server. If
@@ -781,8 +755,6 @@ bool ProxyServer::LinkedSession::is_connected() const {
return (this->server_channel.connected() && this->client_channel.connected()); return (this->server_channel.connected() && this->client_channel.connected());
} }
void ProxyServer::LinkedSession::on_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data) { void ProxyServer::LinkedSession::on_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data) {
auto* session = reinterpret_cast<LinkedSession*>(ch.context_obj); auto* session = reinterpret_cast<LinkedSession*>(ch.context_obj);
bool is_server_stream = (&ch == &session->server_channel); bool is_server_stream = (&ch == &session->server_channel);
+15 -15
View File
@@ -3,21 +3,19 @@
#include <event2/event.h> #include <event2/event.h>
#include <deque> #include <deque>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <functional> #include <functional>
#include <vector> #include <map>
#include <string>
#include <memory> #include <memory>
#include <phosg/Filesystem.hh> #include <phosg/Filesystem.hh>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
#include "PSOProtocol.hh" #include "PSOProtocol.hh"
#include "ServerState.hh" #include "ServerState.hh"
class ProxyServer { class ProxyServer {
public: public:
ProxyServer() = delete; ProxyServer() = delete;
@@ -38,7 +36,7 @@ public:
uint64_t id; uint64_t id;
PrefixedLogger log; PrefixedLogger log;
std::unique_ptr<struct event, void(*)(struct event*)> timeout_event; std::unique_ptr<struct event, void (*)(struct event*)> timeout_event;
std::shared_ptr<const License> license; std::shared_ptr<const License> license;
@@ -80,7 +78,9 @@ public:
std::string name; std::string name;
uint8_t section_id; uint8_t section_id;
uint8_t char_class; uint8_t char_class;
LobbyPlayer() : guild_card_number(0), section_id(0), char_class(0) { } LobbyPlayer() : guild_card_number(0),
section_id(0),
char_class(0) {}
}; };
std::vector<LobbyPlayer> lobby_players; std::vector<LobbyPlayer> lobby_players;
size_t lobby_client_id; size_t lobby_client_id;
@@ -163,10 +163,10 @@ public:
std::shared_ptr<LinkedSession> get_session(); std::shared_ptr<LinkedSession> get_session();
std::shared_ptr<LinkedSession> get_session_by_name(const std::string& name); std::shared_ptr<LinkedSession> get_session_by_name(const std::string& name);
std::shared_ptr<LinkedSession> create_licensed_session( std::shared_ptr<LinkedSession> create_licensed_session(
std::shared_ptr<const License> l, std::shared_ptr<const License> l,
uint16_t local_port, uint16_t local_port,
GameVersion version, GameVersion version,
const ClientConfigBB& newserv_client_config); const ClientConfigBB& newserv_client_config);
void delete_session(uint64_t id); void delete_session(uint64_t id);
void delete_session(struct bufferevent* bev); void delete_session(struct bufferevent* bev);
@@ -179,7 +179,7 @@ private:
PrefixedLogger log; PrefixedLogger log;
uint16_t port; uint16_t port;
scoped_fd fd; scoped_fd fd;
std::unique_ptr<struct evconnlistener, void(*)(struct evconnlistener*)> listener; std::unique_ptr<struct evconnlistener, void (*)(struct evconnlistener*)> listener;
GameVersion version; GameVersion version;
struct sockaddr_storage default_destination; struct sockaddr_storage default_destination;
@@ -190,7 +190,7 @@ private:
const struct sockaddr_storage* default_destination); const struct sockaddr_storage* default_destination);
static void dispatch_on_listen_accept(struct evconnlistener* listener, static void dispatch_on_listen_accept(struct evconnlistener* listener,
evutil_socket_t fd, struct sockaddr *address, int socklen, void* ctx); evutil_socket_t fd, struct sockaddr* address, int socklen, void* ctx);
static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx); static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx);
void on_listen_accept(int fd); void on_listen_accept(int fd);
void on_listen_error(); void on_listen_error();
+45 -58
View File
@@ -2,26 +2,24 @@
#include <algorithm> #include <algorithm>
#include <mutex> #include <mutex>
#include <string>
#include <unordered_map>
#include <phosg/Filesystem.hh>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <phosg/Filesystem.hh>
#include <phosg/Hash.hh> #include <phosg/Hash.hh>
#include <phosg/Random.hh> #include <phosg/Random.hh>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <phosg/Tools.hh> #include <phosg/Tools.hh>
#include <string>
#include <unordered_map>
#include "Loggers.hh"
#include "SaveFileFormats.hh"
#include "CommandFormats.hh" #include "CommandFormats.hh"
#include "Compression.hh" #include "Compression.hh"
#include "Loggers.hh"
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
#include "SaveFileFormats.hh"
#include "Text.hh" #include "Text.hh"
using namespace std; using namespace std;
// GCI decoding logic // GCI decoding logic
template <bool IsBigEndian> template <bool IsBigEndian>
@@ -38,8 +36,10 @@ struct PSOMemCardDLQFileEncryptedHeader {
// Data follows here. // Data follows here.
} __attribute__((packed)); } __attribute__((packed));
struct PSOVMSDLQFileEncryptedHeader : PSOMemCardDLQFileEncryptedHeader<false> { } __attribute__((packed)); struct PSOVMSDLQFileEncryptedHeader : PSOMemCardDLQFileEncryptedHeader<false> {
struct PSOGCIDLQFileEncryptedHeader : PSOMemCardDLQFileEncryptedHeader<true> { } __attribute__((packed)); } __attribute__((packed));
struct PSOGCIDLQFileEncryptedHeader : PSOMemCardDLQFileEncryptedHeader<true> {
} __attribute__((packed));
template <bool IsBigEndian> template <bool IsBigEndian>
string decrypt_gci_or_vms_v2_download_quest_data_section( string decrypt_gci_or_vms_v2_download_quest_data_section(
@@ -78,9 +78,7 @@ string decrypt_gci_or_vms_v2_download_quest_data_section(
// Unlike the above rounds, round 3 is always little-endian (it corresponds to // Unlike the above rounds, round 3 is always little-endian (it corresponds to
// the round of encryption done on the server before sending the file to the // the round of encryption done on the server before sending the file to the
// client in the first place) // client in the first place)
PSOV2Encryption(header->round3_seed).decrypt( PSOV2Encryption(header->round3_seed).decrypt(decrypted.data() + sizeof(HeaderT), decrypted.size() - sizeof(HeaderT));
decrypted.data() + sizeof(HeaderT),
decrypted.size() - sizeof(HeaderT));
decrypted.resize(orig_size); decrypted.resize(orig_size);
// Some download quest GCI files have decompressed_size fields that are 8 // Some download quest GCI files have decompressed_size fields that are 8
@@ -136,7 +134,8 @@ string find_seed_and_decrypt_gci_or_vms_v2_download_quest_data_section(
} catch (const runtime_error&) { } catch (const runtime_error&) {
return false; return false;
} }
}, 0, 0x100000000, num_threads); },
0, 0x100000000, num_threads);
if (!result.empty() && (result_seed < 0x100000000)) { if (!result.empty() && (result_seed < 0x100000000)) {
static_game_data_log.info("Found seed %08" PRIX64, result_seed); static_game_data_log.info("Found seed %08" PRIX64, result_seed);
@@ -146,8 +145,6 @@ string find_seed_and_decrypt_gci_or_vms_v2_download_quest_data_section(
} }
} }
struct PSOVMSFileHeader { struct PSOVMSFileHeader {
ptext<char, 0x10> short_desc; // "PSO/DOWNLOAD " or "PSOV2/DOWNLOAD " ptext<char, 0x10> short_desc; // "PSO/DOWNLOAD " or "PSOV2/DOWNLOAD "
ptext<char, 0x20> long_desc; // Usually quest name ptext<char, 0x20> long_desc; // Usually quest name
@@ -187,19 +184,15 @@ struct PSOVMSFileHeader {
} }
} __attribute__((packed)); } __attribute__((packed));
struct PSODownloadQuestHeader { struct PSODownloadQuestHeader {
le_uint32_t size; le_uint32_t size;
le_uint32_t encryption_seed; le_uint32_t encryption_seed;
} __attribute__((packed)); } __attribute__((packed));
bool category_is_mode(QuestCategory category) { bool category_is_mode(QuestCategory category) {
return (category == QuestCategory::BATTLE) || return (category == QuestCategory::BATTLE) ||
(category == QuestCategory::CHALLENGE) || (category == QuestCategory::CHALLENGE) ||
(category == QuestCategory::EPISODE_3); (category == QuestCategory::EPISODE_3);
} }
const char* name_for_category(QuestCategory category) { const char* name_for_category(QuestCategory category) {
@@ -237,8 +230,6 @@ const char* name_for_category(QuestCategory category) {
} }
} }
struct PSOQuestHeaderDC { // Same format for DC v1 and v2, thankfully struct PSOQuestHeaderDC { // Same format for DC v1 and v2, thankfully
uint32_t start_offset; uint32_t start_offset;
uint32_t unknown_offset1; uint32_t unknown_offset1;
@@ -297,18 +288,16 @@ struct PSOQuestHeaderBB {
ptext<char16_t, 0x120> long_description; ptext<char16_t, 0x120> long_description;
} __attribute__((packed)); } __attribute__((packed));
Quest::Quest(const string& bin_filename) Quest::Quest(const string& bin_filename)
: internal_id(-1), : internal_id(-1),
menu_item_id(0), menu_item_id(0),
category(QuestCategory::UNKNOWN), category(QuestCategory::UNKNOWN),
episode(Episode::NONE), episode(Episode::NONE),
is_dcv1(false), is_dcv1(false),
joinable(false), joinable(false),
file_format(FileFormat::BIN_DAT), file_format(FileFormat::BIN_DAT),
has_mnm_extension(false), has_mnm_extension(false),
is_dlq_encoded(false) { is_dlq_encoded(false) {
if (ends_with(bin_filename, ".bin.gci") || ends_with(bin_filename, ".mnm.gci")) { if (ends_with(bin_filename, ".bin.gci") || ends_with(bin_filename, ".mnm.gci")) {
this->file_format = FileFormat::BIN_DAT_GCI; this->file_format = FileFormat::BIN_DAT_GCI;
@@ -383,30 +372,30 @@ Quest::Quest(const string& bin_filename)
// Get the category from the second token if needed // Get the category from the second token if needed
if (this->category == QuestCategory::UNKNOWN) { if (this->category == QuestCategory::UNKNOWN) {
static const unordered_map<string, QuestCategory> name_to_category({ static const unordered_map<string, QuestCategory> name_to_category({
{"ret", QuestCategory::RETRIEVAL}, {"ret", QuestCategory::RETRIEVAL},
{"ext", QuestCategory::EXTERMINATION}, {"ext", QuestCategory::EXTERMINATION},
{"evt", QuestCategory::EVENT}, {"evt", QuestCategory::EVENT},
{"shp", QuestCategory::SHOP}, {"shp", QuestCategory::SHOP},
{"vr", QuestCategory::VR}, {"vr", QuestCategory::VR},
{"twr", QuestCategory::TOWER}, {"twr", QuestCategory::TOWER},
// Note: This will be overwritten later for Episode 2 & 4 quests - we // Note: This will be overwritten later for Episode 2 & 4 quests - we
// haven't parsed the episode number from the quest script yet // haven't parsed the episode number from the quest script yet
{"gov", QuestCategory::GOVERNMENT_EPISODE_1}, {"gov", QuestCategory::GOVERNMENT_EPISODE_1},
{"dl", QuestCategory::DOWNLOAD}, {"dl", QuestCategory::DOWNLOAD},
{"1p", QuestCategory::SOLO}, {"1p", QuestCategory::SOLO},
}); });
this->category = name_to_category.at(tokens[1]); this->category = name_to_category.at(tokens[1]);
tokens.erase(tokens.begin() + 1); tokens.erase(tokens.begin() + 1);
} }
static const unordered_map<string, GameVersion> name_to_version({ static const unordered_map<string, GameVersion> name_to_version({
{"d1", GameVersion::DC}, {"d1", GameVersion::DC},
{"dc", GameVersion::DC}, {"dc", GameVersion::DC},
{"pc", GameVersion::PC}, {"pc", GameVersion::PC},
{"gc", GameVersion::GC}, {"gc", GameVersion::GC},
{"gc3", GameVersion::GC}, {"gc3", GameVersion::GC},
{"xb", GameVersion::XB}, {"xb", GameVersion::XB},
{"bb", GameVersion::BB}, {"bb", GameVersion::BB},
}); });
this->version = name_to_version.at(tokens[1]); this->version = name_to_version.at(tokens[1]);
@@ -705,7 +694,8 @@ string Quest::decode_vms(
const void* data_section = r.getv(header.data_size); const void* data_section = r.getv(header.data_size);
try { try {
return decrypt_vms_v1_data_section(data_section, header.data_size); return decrypt_vms_v1_data_section(data_section, header.data_size);
} catch (const exception& e) { } } catch (const exception& e) {
}
if (known_seed >= 0) { if (known_seed >= 0) {
return decrypt_gci_or_vms_v2_download_quest_data_section<false>( return decrypt_gci_or_vms_v2_download_quest_data_section<false>(
@@ -973,8 +963,6 @@ string Quest::export_qst(GameVersion version) const {
return move(w.str()); return move(w.str());
} }
QuestIndex::QuestIndex(const string& directory) : directory(directory) { QuestIndex::QuestIndex(const string& directory) : directory(directory) {
auto filename_set = list_directory(this->directory); auto filename_set = list_directory(this->directory);
vector<string> filenames(filename_set.begin(), filename_set.end()); vector<string> filenames(filename_set.begin(), filename_set.end());
@@ -1004,7 +992,8 @@ QuestIndex::QuestIndex(const string& directory) : directory(directory) {
q->menu_item_id = next_menu_item_id++; q->menu_item_id = next_menu_item_id++;
string ascii_name = encode_sjis(q->name); string ascii_name = encode_sjis(q->name);
if (!this->version_menu_item_id_to_quest.emplace( if (!this->version_menu_item_id_to_quest.emplace(
make_pair(q->version, q->menu_item_id), q).second) { make_pair(q->version, q->menu_item_id), q)
.second) {
throw logic_error("duplicate quest menu item id"); throw logic_error("duplicate quest menu item id");
} }
static_game_data_log.info("Indexed quest %s (%s => %s-%" PRId64 " (%" PRIu32 "), %s, %s, joinable=%s, dcv1=%s)", static_game_data_log.info("Indexed quest %s (%s => %s-%" PRId64 " (%" PRIu32 "), %s, %s, joinable=%s, dcv1=%s)",
@@ -1050,8 +1039,6 @@ vector<shared_ptr<const Quest>> QuestIndex::filter(
return ret; return ret;
} }
static string create_download_quest_file(const string& compressed_data, static string create_download_quest_file(const string& compressed_data,
size_t decompressed_size, uint32_t encryption_seed = 0) { size_t decompressed_size, uint32_t encryption_seed = 0) {
// Download quest files are like normal (PRS-compressed) quest files, but they // Download quest files are like normal (PRS-compressed) quest files, but they
+1 -5
View File
@@ -10,8 +10,6 @@
#include "StaticGameData.hh" #include "StaticGameData.hh"
#include "Version.hh" #include "Version.hh"
enum class QuestCategory { enum class QuestCategory {
UNKNOWN = -1, UNKNOWN = -1,
RETRIEVAL = 0, RETRIEVAL = 0,
@@ -33,8 +31,6 @@ enum class QuestCategory {
bool category_is_mode(QuestCategory category); bool category_is_mode(QuestCategory category);
const char* name_for_category(QuestCategory category); const char* name_for_category(QuestCategory category);
class Quest { class Quest {
public: public:
enum class FileFormat { enum class FileFormat {
@@ -107,5 +103,5 @@ struct QuestIndex {
std::shared_ptr<const Quest> get(GameVersion version, uint32_t id) const; std::shared_ptr<const Quest> get(GameVersion version, uint32_t id) const;
std::shared_ptr<const std::string> get_gba(const std::string& name) const; std::shared_ptr<const std::string> get_gba(const std::string& name) const;
std::vector<std::shared_ptr<const Quest>> filter(GameVersion version, std::vector<std::shared_ptr<const Quest>> filter(GameVersion version,
bool is_dcv1, QuestCategory category) const; bool is_dcv1, QuestCategory category) const;
}; };
+5 -11
View File
@@ -7,8 +7,6 @@
using namespace std; using namespace std;
uint32_t RareItemSet::expand_rate(uint8_t pc) { uint32_t RareItemSet::expand_rate(uint8_t pc) {
int8_t shift = ((pc >> 3) & 0x1F) - 4; int8_t shift = ((pc >> 3) & 0x1F) - 4;
if (shift < 0) { if (shift < 0) {
@@ -21,10 +19,8 @@ bool RareItemSet::sample(mt19937& random, uint8_t pc) {
return (random() < RareItemSet::expand_rate(pc)); return (random() < RareItemSet::expand_rate(pc));
} }
GSLRareItemSet::GSLRareItemSet(shared_ptr<const string> data, bool is_big_endian) GSLRareItemSet::GSLRareItemSet(shared_ptr<const string> data, bool is_big_endian)
: gsl(data, is_big_endian) { } : gsl(data, is_big_endian) {}
const GSLRareItemSet::Table& GSLRareItemSet::get_table( const GSLRareItemSet::Table& GSLRareItemSet::get_table(
Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const { Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const {
@@ -40,10 +36,10 @@ const GSLRareItemSet::Table& GSLRareItemSet::get_table(
} }
string filename = string_printf("ItemRT%s%s%c%1d.rel", string filename = string_printf("ItemRT%s%s%c%1d.rel",
((mode == GameMode::CHALLENGE) ? "c" : ""), ((mode == GameMode::CHALLENGE) ? "c" : ""),
((episode == Episode::EP2) ? "l" : ""), ((episode == Episode::EP2) ? "l" : ""),
tolower(abbreviation_for_difficulty(difficulty)), // One of "nhvu" tolower(abbreviation_for_difficulty(difficulty)), // One of "nhvu"
secid); secid);
auto entry = this->gsl.get(filename); auto entry = this->gsl.get(filename);
if (entry.second < sizeof(Table)) { if (entry.second < sizeof(Table)) {
throw runtime_error(string_printf("table %s is too small", filename.c_str())); throw runtime_error(string_printf("table %s is too small", filename.c_str()));
@@ -51,8 +47,6 @@ const GSLRareItemSet::Table& GSLRareItemSet::get_table(
return *reinterpret_cast<const Table*>(entry.first); return *reinterpret_cast<const Table*>(entry.first);
} }
RELRareItemSet::RELRareItemSet(shared_ptr<const string> data) : data(data) { RELRareItemSet::RELRareItemSet(shared_ptr<const string> data) : data(data) {
if (this->data->size() != sizeof(Table) * 10 * 4 * 3) { if (this->data->size() != sizeof(Table) * 10 * 4 * 3) {
throw runtime_error("data file size is incorrect"); throw runtime_error("data file size is incorrect");
+2 -6
View File
@@ -3,13 +3,11 @@
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include <string>
#include <random> #include <random>
#include <string>
#include "StaticGameData.hh"
#include "GSLArchive.hh" #include "GSLArchive.hh"
#include "StaticGameData.hh"
class RareItemSet { class RareItemSet {
public: public:
@@ -52,8 +50,6 @@ protected:
RareItemSet() = default; RareItemSet() = default;
}; };
class GSLRareItemSet : public RareItemSet { class GSLRareItemSet : public RareItemSet {
public: public:
GSLRareItemSet(std::shared_ptr<const std::string> data, bool is_big_endian); GSLRareItemSet(std::shared_ptr<const std::string> data, bool is_big_endian);
+213 -251
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -4,8 +4,6 @@
#include "Client.hh" #include "Client.hh"
#include "ServerState.hh" #include "ServerState.hh"
std::shared_ptr<Lobby> create_game_generic( std::shared_ptr<Lobby> create_game_generic(
std::shared_ptr<ServerState> s, std::shared_ptr<ServerState> s,
std::shared_ptr<Client> c, std::shared_ptr<Client> c,
+415 -430
View File
File diff suppressed because it is too large Load Diff
+5 -7
View File
@@ -1,15 +1,13 @@
#include <stdint.h> #include <stdint.h>
#include "PSOProtocol.hh"
#include "Client.hh" #include "Client.hh"
#include "Lobby.hh" #include "Lobby.hh"
#include "Client.hh" #include "PSOProtocol.hh"
#include "ServerState.hh" #include "ServerState.hh"
void on_subcommand(
std::shared_ptr<ServerState> s, std::shared_ptr<Lobby> l,
void on_subcommand(std::shared_ptr<ServerState> s, std::shared_ptr<Client> c, uint8_t command, uint8_t flag,
std::shared_ptr<Lobby> l, std::shared_ptr<Client> c, uint8_t command, const std::string& data);
uint8_t flag, const std::string& data);
bool subcommand_is_implemented(uint8_t which); bool subcommand_is_implemented(uint8_t which);
+30 -33
View File
@@ -5,19 +5,17 @@
#include <phosg/Time.hh> #include <phosg/Time.hh>
#include "Loggers.hh" #include "Loggers.hh"
#include "Shell.hh"
#include "Server.hh" #include "Server.hh"
#include "Shell.hh"
using namespace std; using namespace std;
ReplaySession::Event::Event(Type type, uint64_t client_id, size_t line_num) ReplaySession::Event::Event(Type type, uint64_t client_id, size_t line_num)
: type(type), : type(type),
client_id(client_id), client_id(client_id),
allow_size_disparity(false), allow_size_disparity(false),
complete(false), complete(false),
line_num(line_num) { } line_num(line_num) {}
string ReplaySession::Event::str() const { string ReplaySession::Event::str() const {
string ret; string ret;
@@ -40,27 +38,23 @@ string ReplaySession::Event::str() const {
return ret; return ret;
} }
ReplaySession::Client::Client( ReplaySession::Client::Client(
ReplaySession* session, uint64_t id, uint16_t port, GameVersion version) ReplaySession* session, uint64_t id, uint16_t port, GameVersion version)
: id(id), : id(id),
port(port), port(port),
version(version), version(version),
channel( channel(
this->version, this->version,
&ReplaySession::dispatch_on_command_received, &ReplaySession::dispatch_on_command_received,
&ReplaySession::dispatch_on_error, &ReplaySession::dispatch_on_error,
session, session,
string_printf("R-%" PRIX64, this->id)) { } string_printf("R-%" PRIX64, this->id)) {}
string ReplaySession::Client::str() const { string ReplaySession::Client::str() const {
return string_printf("Client[%" PRIu64 ", T-%hu, %s]", return string_printf("Client[%" PRIu64 ", T-%hu, %s]",
this->id, this->port, name_for_version(this->version)); this->id, this->port, name_for_version(this->version));
} }
shared_ptr<ReplaySession::Event> ReplaySession::create_event( shared_ptr<ReplaySession::Event> ReplaySession::create_event(
Event::Type type, shared_ptr<Client> c, size_t line_num) { Event::Type type, shared_ptr<Client> c, size_t line_num) {
shared_ptr<Event> event(new Event(type, c->id, line_num)); shared_ptr<Event> event(new Event(type, c->id, line_num));
@@ -242,10 +236,12 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
uint8_t command; uint8_t command;
if (version == GameVersion::PC) { if (version == GameVersion::PC) {
command = check_size_t<PSOCommandHeaderPC>( command = check_size_t<PSOCommandHeaderPC>(
ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF).command; ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF)
.command;
} else { // V3 } else { // V3
command = check_size_t<PSOCommandHeaderDCV3>( command = check_size_t<PSOCommandHeaderDCV3>(
ev->data, sizeof(PSOCommandHeaderDCV3), 0xFFFF).command; ev->data, sizeof(PSOCommandHeaderDCV3), 0xFFFF)
.command;
} }
switch (command) { switch (command) {
case 0x02: case 0x02:
@@ -335,7 +331,8 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
} }
case GameVersion::BB: { case GameVersion::BB: {
uint16_t command = check_size_t<PSOCommandHeaderBB>( uint16_t command = check_size_t<PSOCommandHeaderBB>(
ev->data, sizeof(PSOCommandHeaderBB), 0xFFFF).command; ev->data, sizeof(PSOCommandHeaderBB), 0xFFFF)
.command;
switch (command) { switch (command) {
case 0x0003: { case 0x0003: {
auto& mask = check_size_t<S_ServerInitDefault_BB_03_9B>( auto& mask = check_size_t<S_ServerInitDefault_BB_03_9B>(
@@ -380,14 +377,14 @@ ReplaySession::ReplaySession(
shared_ptr<ServerState> state, shared_ptr<ServerState> state,
const string& required_access_key, const string& required_access_key,
const string& required_password) const string& required_password)
: state(state), : state(state),
required_access_key(required_access_key), required_access_key(required_access_key),
required_password(required_password), required_password(required_password),
base(base), base(base),
commands_sent(0), commands_sent(0),
bytes_sent(0), bytes_sent(0),
commands_received(0), commands_received(0),
bytes_received(0) { bytes_received(0) {
shared_ptr<Event> parsing_command = nullptr; shared_ptr<Event> parsing_command = nullptr;
size_t line_num = 0; size_t line_num = 0;
@@ -419,7 +416,7 @@ ReplaySession::ReplaySession(
if (parsing_command->type == Event::Type::RECEIVE) { if (parsing_command->type == Event::Type::RECEIVE) {
this->apply_default_mask(parsing_command); this->apply_default_mask(parsing_command);
} else if (parsing_command->type == Event::Type::SEND) { } else if (parsing_command->type == Event::Type::SEND) {
this->check_for_password(parsing_command); this->check_for_password(parsing_command);
} }
parsing_command = nullptr; parsing_command = nullptr;
} }

Some files were not shown because too many files have changed in this diff Show More