convert all CRLF line endings to LF only
This commit is contained in:
@@ -1,96 +1,94 @@
|
|||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
NPC: Coren Tsu - The Wanderer
|
NPC: Coren Tsu - The Wanderer
|
||||||
AREAS: Pioneer 2
|
AREAS: Pioneer 2
|
||||||
|
|
||||||
Translations by: apexseals (discord: apexseals)
|
Translations by: apexseals (discord: apexseals)
|
||||||
Proofing & Debugging by: nolrinale (github.com/nolrinale)
|
Proofing & Debugging by: nolrinale (github.com/nolrinale)
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
presentation:
|
presentation:
|
||||||
|
|
||||||
I am Coren Tsu, a wandering merchant,
|
I am Coren Tsu, a wandering merchant,
|
||||||
you could say.
|
you could say.
|
||||||
|
|
||||||
Please take some time to look at
|
Please take some time to look at
|
||||||
the rare and wonderous goods
|
the rare and wonderous goods
|
||||||
I have been collecting.
|
I have been collecting.
|
||||||
|
|
||||||
If you spend a little meseta,
|
If you spend a little meseta,
|
||||||
you could win a wonderful prize.
|
you could win a wonderful prize.
|
||||||
|
|
||||||
Well? Wanna try?
|
Well? Wanna try?
|
||||||
|
|
||||||
|
|
||||||
You may win,
|
You may win,
|
||||||
you may lose.
|
you may lose.
|
||||||
|
|
||||||
But if you don't win,
|
But if you don't win,
|
||||||
don't take it out on me.
|
don't take it out on me.
|
||||||
|
|
||||||
That's just the way
|
That's just the way
|
||||||
gambling is, yes?
|
gambling is, yes?
|
||||||
|
|
||||||
Well then, how much
|
Well then, how much
|
||||||
meseta do you want to pay?
|
meseta do you want to pay?
|
||||||
|
|
||||||
As long as you pay me,
|
As long as you pay me,
|
||||||
I'll give you a great service.
|
I'll give you a great service.
|
||||||
|
|
||||||
|
|
||||||
Huh?
|
Huh?
|
||||||
|
|
||||||
That's too bad...
|
That's too bad...
|
||||||
|
|
||||||
Well, these kind of things usually
|
Well, these kind of things usually
|
||||||
have a chance to lose money.
|
have a chance to lose money.
|
||||||
|
|
||||||
Let's keep this discreet.
|
Let's keep this discreet.
|
||||||
If you feel up to it, talk to me again.
|
If you feel up to it, talk to me again.
|
||||||
|
|
||||||
|
|
||||||
It seems you have
|
It seems you have
|
||||||
too many items.
|
too many items.
|
||||||
|
|
||||||
First, go and
|
First, go and
|
||||||
organize your items,
|
organize your items,
|
||||||
|
|
||||||
Then speak to me again.
|
Then speak to me again.
|
||||||
|
|
||||||
What?
|
What?
|
||||||
|
|
||||||
You said you'd try,
|
You said you'd try,
|
||||||
then you said no.
|
then you said no.
|
||||||
|
|
||||||
People like that
|
People like that
|
||||||
fail at everything.
|
fail at everything.
|
||||||
|
|
||||||
|
|
||||||
What the...?
|
What the...?
|
||||||
|
|
||||||
You don't have the
|
You don't have the
|
||||||
meseta to pay me?
|
meseta to pay me?
|
||||||
|
|
||||||
I won't work with such
|
I won't work with such
|
||||||
cold hearted people.
|
cold hearted people.
|
||||||
|
|
||||||
|
|
||||||
Alright, let's do it.
|
Alright, let's do it.
|
||||||
|
|
||||||
You better pray
|
You better pray
|
||||||
for something good...
|
for something good...
|
||||||
|
|
||||||
|
|
||||||
Look here!
|
Look here!
|
||||||
Take it!
|
Take it!
|
||||||
|
|
||||||
Even if you had bad luck,
|
Even if you had bad luck,
|
||||||
something good will come out of it.
|
something good will come out of it.
|
||||||
|
|
||||||
You'll win someday!
|
You'll win someday!
|
||||||
|
|
||||||
In case you want to try again,
|
In case you want to try again,
|
||||||
come back to me once more.
|
come back to me once more.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+1003
-1003
File diff suppressed because it is too large
Load Diff
+263
-263
@@ -1,263 +1,263 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <phosg/JSON.hh>
|
#include <phosg/JSON.hh>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
class LicenseIndex;
|
class LicenseIndex;
|
||||||
|
|
||||||
struct DCNTELicense {
|
struct DCNTELicense {
|
||||||
std::string serial_number;
|
std::string serial_number;
|
||||||
std::string access_key;
|
std::string access_key;
|
||||||
|
|
||||||
static std::shared_ptr<DCNTELicense> from_json(const JSON& json);
|
static std::shared_ptr<DCNTELicense> from_json(const JSON& json);
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V1V2License {
|
struct V1V2License {
|
||||||
uint32_t serial_number = 0;
|
uint32_t serial_number = 0;
|
||||||
std::string access_key;
|
std::string access_key;
|
||||||
|
|
||||||
static std::shared_ptr<V1V2License> from_json(const JSON& json);
|
static std::shared_ptr<V1V2License> from_json(const JSON& json);
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GCLicense {
|
struct GCLicense {
|
||||||
uint32_t serial_number = 0;
|
uint32_t serial_number = 0;
|
||||||
std::string access_key;
|
std::string access_key;
|
||||||
std::string password;
|
std::string password;
|
||||||
|
|
||||||
static std::shared_ptr<GCLicense> from_json(const JSON& json);
|
static std::shared_ptr<GCLicense> from_json(const JSON& json);
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct XBLicense {
|
struct XBLicense {
|
||||||
std::string gamertag;
|
std::string gamertag;
|
||||||
uint64_t user_id = 0;
|
uint64_t user_id = 0;
|
||||||
uint64_t account_id = 0;
|
uint64_t account_id = 0;
|
||||||
|
|
||||||
static std::shared_ptr<XBLicense> from_json(const JSON& json);
|
static std::shared_ptr<XBLicense> from_json(const JSON& json);
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BBLicense {
|
struct BBLicense {
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string password;
|
std::string password;
|
||||||
|
|
||||||
static std::shared_ptr<BBLicense> from_json(const JSON& json);
|
static std::shared_ptr<BBLicense> from_json(const JSON& json);
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Account {
|
struct Account {
|
||||||
enum class Flag : uint32_t {
|
enum class Flag : uint32_t {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
KICK_USER = 0x00000001,
|
KICK_USER = 0x00000001,
|
||||||
BAN_USER = 0x00000002,
|
BAN_USER = 0x00000002,
|
||||||
SILENCE_USER = 0x00000004,
|
SILENCE_USER = 0x00000004,
|
||||||
CHANGE_EVENT = 0x00000010,
|
CHANGE_EVENT = 0x00000010,
|
||||||
ANNOUNCE = 0x00000020,
|
ANNOUNCE = 0x00000020,
|
||||||
FREE_JOIN_GAMES = 0x00000040,
|
FREE_JOIN_GAMES = 0x00000040,
|
||||||
DEBUG = 0x01000000,
|
DEBUG = 0x01000000,
|
||||||
CHEAT_ANYWHERE = 0x02000000,
|
CHEAT_ANYWHERE = 0x02000000,
|
||||||
DISABLE_QUEST_REQUIREMENTS = 0x04000000,
|
DISABLE_QUEST_REQUIREMENTS = 0x04000000,
|
||||||
ALWAYS_ENABLE_CHAT_COMMANDS = 0x08000000,
|
ALWAYS_ENABLE_CHAT_COMMANDS = 0x08000000,
|
||||||
MODERATOR = 0x00000007,
|
MODERATOR = 0x00000007,
|
||||||
ADMINISTRATOR = 0x000000FF,
|
ADMINISTRATOR = 0x000000FF,
|
||||||
ROOT = 0x7FFFFFFF,
|
ROOT = 0x7FFFFFFF,
|
||||||
IS_SHARED_ACCOUNT = 0x80000000,
|
IS_SHARED_ACCOUNT = 0x80000000,
|
||||||
// NOTE: When adding or changing license flags, don't forget to change the
|
// NOTE: When adding or changing license flags, don't forget to change the
|
||||||
// documentation in the shell's help text.
|
// documentation in the shell's help text.
|
||||||
UNUSED_BITS = 0x70FFFF00,
|
UNUSED_BITS = 0x70FFFF00,
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
// account_id is also the account's guild card number
|
// account_id is also the account's guild card number
|
||||||
uint32_t account_id = 0;
|
uint32_t account_id = 0;
|
||||||
|
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
uint64_t ban_end_time = 0; // 0 = not banned
|
uint64_t ban_end_time = 0; // 0 = not banned
|
||||||
std::string last_player_name;
|
std::string last_player_name;
|
||||||
std::string auto_reply_message;
|
std::string auto_reply_message;
|
||||||
|
|
||||||
uint32_t ep3_current_meseta = 0;
|
uint32_t ep3_current_meseta = 0;
|
||||||
uint32_t ep3_total_meseta_earned = 0;
|
uint32_t ep3_total_meseta_earned = 0;
|
||||||
|
|
||||||
uint32_t bb_team_id = 0;
|
uint32_t bb_team_id = 0;
|
||||||
bool is_temporary = false; // If true, isn't saved to disk
|
bool is_temporary = false; // If true, isn't saved to disk
|
||||||
|
|
||||||
std::unordered_set<std::string> auto_patches_enabled;
|
std::unordered_set<std::string> auto_patches_enabled;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<DCNTELicense>> dc_nte_licenses;
|
std::unordered_map<std::string, std::shared_ptr<DCNTELicense>> dc_nte_licenses;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<V1V2License>> dc_licenses;
|
std::unordered_map<uint32_t, std::shared_ptr<V1V2License>> dc_licenses;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<V1V2License>> pc_licenses;
|
std::unordered_map<uint32_t, std::shared_ptr<V1V2License>> pc_licenses;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<GCLicense>> gc_licenses;
|
std::unordered_map<uint32_t, std::shared_ptr<GCLicense>> gc_licenses;
|
||||||
std::unordered_map<std::string, std::shared_ptr<XBLicense>> xb_licenses;
|
std::unordered_map<std::string, std::shared_ptr<XBLicense>> xb_licenses;
|
||||||
std::unordered_map<std::string, std::shared_ptr<BBLicense>> bb_licenses;
|
std::unordered_map<std::string, std::shared_ptr<BBLicense>> bb_licenses;
|
||||||
|
|
||||||
Account() = default;
|
Account() = default;
|
||||||
explicit Account(const JSON& json);
|
explicit Account(const JSON& json);
|
||||||
virtual ~Account() = default;
|
virtual ~Account() = default;
|
||||||
|
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
virtual void save() const;
|
virtual void save() const;
|
||||||
virtual void delete_file() const;
|
virtual void delete_file() const;
|
||||||
|
|
||||||
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
||||||
return !!(this->flags & static_cast<uint32_t>(flag));
|
return !!(this->flags & static_cast<uint32_t>(flag));
|
||||||
}
|
}
|
||||||
inline void set_flag(Flag flag) {
|
inline void set_flag(Flag flag) {
|
||||||
this->flags |= static_cast<uint32_t>(flag);
|
this->flags |= static_cast<uint32_t>(flag);
|
||||||
}
|
}
|
||||||
inline void clear_flag(Flag flag) {
|
inline void clear_flag(Flag flag) {
|
||||||
this->flags &= (~static_cast<uint32_t>(flag));
|
this->flags &= (~static_cast<uint32_t>(flag));
|
||||||
}
|
}
|
||||||
inline void toggle_flag(Flag flag) {
|
inline void toggle_flag(Flag flag) {
|
||||||
this->flags ^= static_cast<uint32_t>(flag);
|
this->flags ^= static_cast<uint32_t>(flag);
|
||||||
}
|
}
|
||||||
inline void replace_all_flags(Flag mask) {
|
inline void replace_all_flags(Flag mask) {
|
||||||
this->flags = static_cast<uint32_t>(mask);
|
this->flags = static_cast<uint32_t>(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(FILE* stream) const;
|
void print(FILE* stream) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Login {
|
struct Login {
|
||||||
bool account_was_created = false;
|
bool account_was_created = false;
|
||||||
// This field will never be null
|
// This field will never be null
|
||||||
std::shared_ptr<Account> account;
|
std::shared_ptr<Account> account;
|
||||||
// Exactly one of the following will be non-null, representing the license
|
// Exactly one of the following will be non-null, representing the license
|
||||||
// that the client logged in with
|
// that the client logged in with
|
||||||
std::shared_ptr<DCNTELicense> dc_nte_license;
|
std::shared_ptr<DCNTELicense> dc_nte_license;
|
||||||
std::shared_ptr<V1V2License> dc_license;
|
std::shared_ptr<V1V2License> dc_license;
|
||||||
std::shared_ptr<V1V2License> pc_license;
|
std::shared_ptr<V1V2License> pc_license;
|
||||||
std::shared_ptr<GCLicense> gc_license;
|
std::shared_ptr<GCLicense> gc_license;
|
||||||
std::shared_ptr<XBLicense> xb_license;
|
std::shared_ptr<XBLicense> xb_license;
|
||||||
std::shared_ptr<BBLicense> bb_license;
|
std::shared_ptr<BBLicense> bb_license;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AccountIndex {
|
class AccountIndex {
|
||||||
public:
|
public:
|
||||||
class no_username : public std::invalid_argument {
|
class no_username : public std::invalid_argument {
|
||||||
public:
|
public:
|
||||||
no_username() : invalid_argument("serial number is zero or username is missing") {}
|
no_username() : invalid_argument("serial number is zero or username is missing") {}
|
||||||
};
|
};
|
||||||
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_account : public std::invalid_argument {
|
class missing_account : public std::invalid_argument {
|
||||||
public:
|
public:
|
||||||
missing_account() : invalid_argument("missing account") {}
|
missing_account() : invalid_argument("missing account") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit AccountIndex(bool force_all_temporary);
|
explicit AccountIndex(bool force_all_temporary);
|
||||||
virtual ~AccountIndex() = default;
|
virtual ~AccountIndex() = default;
|
||||||
|
|
||||||
std::shared_ptr<Account> create_account(bool is_temporary) const;
|
std::shared_ptr<Account> create_account(bool is_temporary) const;
|
||||||
|
|
||||||
size_t count() const;
|
size_t count() const;
|
||||||
std::vector<std::shared_ptr<Account>> all() const;
|
std::vector<std::shared_ptr<Account>> all() const;
|
||||||
|
|
||||||
void add(std::shared_ptr<Account> a);
|
void add(std::shared_ptr<Account> a);
|
||||||
void remove(uint32_t serial_number);
|
void remove(uint32_t serial_number);
|
||||||
|
|
||||||
void add_dc_nte_license(std::shared_ptr<Account> account, std::shared_ptr<DCNTELicense> license);
|
void add_dc_nte_license(std::shared_ptr<Account> account, std::shared_ptr<DCNTELicense> license);
|
||||||
void add_dc_license(std::shared_ptr<Account> account, std::shared_ptr<V1V2License> license);
|
void add_dc_license(std::shared_ptr<Account> account, std::shared_ptr<V1V2License> license);
|
||||||
void add_pc_license(std::shared_ptr<Account> account, std::shared_ptr<V1V2License> license);
|
void add_pc_license(std::shared_ptr<Account> account, std::shared_ptr<V1V2License> license);
|
||||||
void add_gc_license(std::shared_ptr<Account> account, std::shared_ptr<GCLicense> license);
|
void add_gc_license(std::shared_ptr<Account> account, std::shared_ptr<GCLicense> license);
|
||||||
void add_xb_license(std::shared_ptr<Account> account, std::shared_ptr<XBLicense> license);
|
void add_xb_license(std::shared_ptr<Account> account, std::shared_ptr<XBLicense> license);
|
||||||
void add_bb_license(std::shared_ptr<Account> account, std::shared_ptr<BBLicense> license);
|
void add_bb_license(std::shared_ptr<Account> account, std::shared_ptr<BBLicense> license);
|
||||||
void remove_dc_nte_license(std::shared_ptr<Account> account, const std::string& serial_number);
|
void remove_dc_nte_license(std::shared_ptr<Account> account, const std::string& serial_number);
|
||||||
void remove_dc_license(std::shared_ptr<Account> account, uint32_t serial_number);
|
void remove_dc_license(std::shared_ptr<Account> account, uint32_t serial_number);
|
||||||
void remove_pc_license(std::shared_ptr<Account> account, uint32_t serial_number);
|
void remove_pc_license(std::shared_ptr<Account> account, uint32_t serial_number);
|
||||||
void remove_gc_license(std::shared_ptr<Account> account, uint32_t serial_number);
|
void remove_gc_license(std::shared_ptr<Account> account, uint32_t serial_number);
|
||||||
void remove_xb_license(std::shared_ptr<Account> account, const std::string& gamertag);
|
void remove_xb_license(std::shared_ptr<Account> account, const std::string& gamertag);
|
||||||
void remove_bb_license(std::shared_ptr<Account> account, const std::string& username);
|
void remove_bb_license(std::shared_ptr<Account> account, const std::string& username);
|
||||||
|
|
||||||
std::shared_ptr<Account> from_account_id(uint32_t account_id) const;
|
std::shared_ptr<Account> from_account_id(uint32_t account_id) const;
|
||||||
std::shared_ptr<Login> from_dc_nte_credentials(
|
std::shared_ptr<Login> from_dc_nte_credentials(
|
||||||
const std::string& serial_number,
|
const std::string& serial_number,
|
||||||
const std::string& access_key,
|
const std::string& access_key,
|
||||||
bool allow_create);
|
bool allow_create);
|
||||||
std::shared_ptr<Login> from_dc_credentials(
|
std::shared_ptr<Login> from_dc_credentials(
|
||||||
uint32_t serial_number,
|
uint32_t serial_number,
|
||||||
const std::string& access_key,
|
const std::string& access_key,
|
||||||
const std::string& character_name,
|
const std::string& character_name,
|
||||||
bool allow_create);
|
bool allow_create);
|
||||||
std::shared_ptr<Login> from_pc_nte_credentials(
|
std::shared_ptr<Login> from_pc_nte_credentials(
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
bool allow_create);
|
bool allow_create);
|
||||||
std::shared_ptr<Login> from_pc_credentials(
|
std::shared_ptr<Login> from_pc_credentials(
|
||||||
uint32_t serial_number,
|
uint32_t serial_number,
|
||||||
const std::string& access_key,
|
const std::string& access_key,
|
||||||
const std::string& character_name,
|
const std::string& character_name,
|
||||||
bool allow_create);
|
bool allow_create);
|
||||||
std::shared_ptr<Login> from_gc_credentials(
|
std::shared_ptr<Login> from_gc_credentials(
|
||||||
uint32_t serial_number,
|
uint32_t serial_number,
|
||||||
const std::string& access_key,
|
const std::string& access_key,
|
||||||
const std::string* password,
|
const std::string* password,
|
||||||
const std::string& character_name,
|
const std::string& character_name,
|
||||||
bool allow_create);
|
bool allow_create);
|
||||||
std::shared_ptr<Login> from_xb_credentials(
|
std::shared_ptr<Login> from_xb_credentials(
|
||||||
const std::string& gamertag,
|
const std::string& gamertag,
|
||||||
uint64_t user_id,
|
uint64_t user_id,
|
||||||
uint64_t account_id,
|
uint64_t account_id,
|
||||||
bool allow_create);
|
bool allow_create);
|
||||||
std::shared_ptr<Login> from_bb_credentials(
|
std::shared_ptr<Login> from_bb_credentials(
|
||||||
const std::string& username,
|
const std::string& username,
|
||||||
const std::string* password,
|
const std::string* password,
|
||||||
bool allow_create);
|
bool allow_create);
|
||||||
|
|
||||||
std::shared_ptr<Account> create_temporary_account_for_shared_account(
|
std::shared_ptr<Account> create_temporary_account_for_shared_account(
|
||||||
std::shared_ptr<const Account> src_a, const std::string& variation_data) const;
|
std::shared_ptr<const Account> src_a, const std::string& variation_data) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool force_all_temporary;
|
bool force_all_temporary;
|
||||||
|
|
||||||
// This class must be thread-safe because it's used by both the patch server
|
// This class must be thread-safe because it's used by both the patch server
|
||||||
// and game server threads
|
// and game server threads
|
||||||
mutable std::shared_mutex lock;
|
mutable std::shared_mutex lock;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<Account>> by_account_id;
|
std::unordered_map<uint32_t, std::shared_ptr<Account>> by_account_id;
|
||||||
std::unordered_map<std::string, std::shared_ptr<Account>> by_dc_nte_serial_number;
|
std::unordered_map<std::string, std::shared_ptr<Account>> by_dc_nte_serial_number;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<Account>> by_dc_serial_number;
|
std::unordered_map<uint32_t, std::shared_ptr<Account>> by_dc_serial_number;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<Account>> by_pc_serial_number;
|
std::unordered_map<uint32_t, std::shared_ptr<Account>> by_pc_serial_number;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<Account>> by_gc_serial_number;
|
std::unordered_map<uint32_t, std::shared_ptr<Account>> by_gc_serial_number;
|
||||||
std::unordered_map<std::string, std::shared_ptr<Account>> by_xb_gamertag;
|
std::unordered_map<std::string, std::shared_ptr<Account>> by_xb_gamertag;
|
||||||
std::unordered_map<std::string, std::shared_ptr<Account>> by_bb_username;
|
std::unordered_map<std::string, std::shared_ptr<Account>> by_bb_username;
|
||||||
|
|
||||||
void add_locked(std::shared_ptr<Account> a);
|
void add_locked(std::shared_ptr<Account> a);
|
||||||
|
|
||||||
std::shared_ptr<Login> from_dc_nte_credentials_locked(
|
std::shared_ptr<Login> from_dc_nte_credentials_locked(
|
||||||
const std::string& serial_number,
|
const std::string& serial_number,
|
||||||
const std::string& access_key);
|
const std::string& access_key);
|
||||||
std::shared_ptr<Login> from_dc_credentials_locked(
|
std::shared_ptr<Login> from_dc_credentials_locked(
|
||||||
uint32_t serial_number,
|
uint32_t serial_number,
|
||||||
const std::string& access_key,
|
const std::string& access_key,
|
||||||
const std::string& character_name);
|
const std::string& character_name);
|
||||||
std::shared_ptr<Login> from_pc_credentials_locked(
|
std::shared_ptr<Login> from_pc_credentials_locked(
|
||||||
uint32_t serial_number,
|
uint32_t serial_number,
|
||||||
const std::string& access_key,
|
const std::string& access_key,
|
||||||
const std::string& character_name);
|
const std::string& character_name);
|
||||||
std::shared_ptr<Login> from_gc_credentials_locked(
|
std::shared_ptr<Login> from_gc_credentials_locked(
|
||||||
uint32_t serial_number,
|
uint32_t serial_number,
|
||||||
const std::string& access_key,
|
const std::string& access_key,
|
||||||
const std::string* password,
|
const std::string* password,
|
||||||
const std::string& character_name);
|
const std::string& character_name);
|
||||||
std::shared_ptr<Login> from_xb_credentials_locked(
|
std::shared_ptr<Login> from_xb_credentials_locked(
|
||||||
const std::string& gamertag,
|
const std::string& gamertag,
|
||||||
uint64_t user_id,
|
uint64_t user_id,
|
||||||
uint64_t account_id);
|
uint64_t account_id);
|
||||||
std::shared_ptr<Login> from_bb_credentials_locked(
|
std::shared_ptr<Login> from_bb_credentials_locked(
|
||||||
const std::string& username,
|
const std::string& username,
|
||||||
const std::string* password);
|
const std::string* password);
|
||||||
};
|
};
|
||||||
|
|||||||
+83
-83
@@ -1,83 +1,83 @@
|
|||||||
#include "BattleParamsIndex.hh"
|
#include "BattleParamsIndex.hh"
|
||||||
|
|
||||||
#include <phosg/Filesystem.hh>
|
#include <phosg/Filesystem.hh>
|
||||||
#include <phosg/Strings.hh>
|
#include <phosg/Strings.hh>
|
||||||
|
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void BattleParamsIndex::Table::print(FILE* stream) const {
|
void BattleParamsIndex::Table::print(FILE* stream) const {
|
||||||
auto print_entry = +[](FILE* stream, const PlayerStats& e) {
|
auto print_entry = +[](FILE* stream, const PlayerStats& e) {
|
||||||
fprintf(stream,
|
fprintf(stream,
|
||||||
"%5hu %5hu %5hu %5hu %5hu %5hu %5hu %5hu %5" PRIu32 " %5" PRIu32,
|
"%5hu %5hu %5hu %5hu %5hu %5hu %5hu %5hu %5" PRIu32 " %5" PRIu32,
|
||||||
e.char_stats.atp.load(),
|
e.char_stats.atp.load(),
|
||||||
e.char_stats.mst.load(),
|
e.char_stats.mst.load(),
|
||||||
e.char_stats.evp.load(),
|
e.char_stats.evp.load(),
|
||||||
e.char_stats.hp.load(),
|
e.char_stats.hp.load(),
|
||||||
e.char_stats.dfp.load(),
|
e.char_stats.dfp.load(),
|
||||||
e.char_stats.ata.load(),
|
e.char_stats.ata.load(),
|
||||||
e.char_stats.lck.load(),
|
e.char_stats.lck.load(),
|
||||||
e.esp.load(),
|
e.esp.load(),
|
||||||
e.experience.load(),
|
e.experience.load(),
|
||||||
e.meseta.load());
|
e.meseta.load());
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t diff = 0; diff < 4; diff++) {
|
for (size_t diff = 0; diff < 4; diff++) {
|
||||||
fprintf(stream, "%c ZZ ATP PSV EVP HP DFP ATA LCK ESP EXP DIFF\n",
|
fprintf(stream, "%c ZZ ATP PSV EVP HP DFP ATA LCK ESP EXP DIFF\n",
|
||||||
abbreviation_for_difficulty(diff));
|
abbreviation_for_difficulty(diff));
|
||||||
for (size_t z = 0; z < 0x60; z++) {
|
for (size_t z = 0; z < 0x60; z++) {
|
||||||
fprintf(stream, " %02zX ", z);
|
fprintf(stream, " %02zX ", z);
|
||||||
print_entry(stream, this->stats[diff][z]);
|
print_entry(stream, this->stats[diff][z]);
|
||||||
fputc('\n', stream);
|
fputc('\n', stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
shared_ptr<const string> data_on_ep4,
|
shared_ptr<const string> data_on_ep4,
|
||||||
shared_ptr<const string> data_off_ep1,
|
shared_ptr<const string> data_off_ep1,
|
||||||
shared_ptr<const string> data_off_ep2,
|
shared_ptr<const string> data_off_ep2,
|
||||||
shared_ptr<const string> data_off_ep4) {
|
shared_ptr<const string> data_off_ep4) {
|
||||||
this->files[0][0].data = data_on_ep1;
|
this->files[0][0].data = data_on_ep1;
|
||||||
this->files[0][1].data = data_on_ep2;
|
this->files[0][1].data = data_on_ep2;
|
||||||
this->files[0][2].data = data_on_ep4;
|
this->files[0][2].data = data_on_ep4;
|
||||||
this->files[1][0].data = data_off_ep1;
|
this->files[1][0].data = data_off_ep1;
|
||||||
this->files[1][1].data = data_off_ep2;
|
this->files[1][1].data = data_off_ep2;
|
||||||
this->files[1][2].data = data_off_ep4;
|
this->files[1][2].data = data_off_ep4;
|
||||||
|
|
||||||
for (uint8_t is_solo = 0; is_solo < 2; is_solo++) {
|
for (uint8_t is_solo = 0; is_solo < 2; is_solo++) {
|
||||||
for (uint8_t episode = 0; episode < 3; episode++) {
|
for (uint8_t episode = 0; episode < 3; episode++) {
|
||||||
auto& file = this->files[is_solo][episode];
|
auto& file = this->files[is_solo][episode];
|
||||||
if (file.data->size() < sizeof(Table)) {
|
if (file.data->size() < sizeof(Table)) {
|
||||||
throw runtime_error(string_printf(
|
throw runtime_error(string_printf(
|
||||||
"battle params table size is incorrect (expected %zX bytes, have %zX bytes; is_solo=%hhu, episode=%hhu)",
|
"battle params table size is incorrect (expected %zX bytes, have %zX bytes; is_solo=%hhu, episode=%hhu)",
|
||||||
sizeof(Table), file.data->size(), is_solo, episode));
|
sizeof(Table), file.data->size(), is_solo, episode));
|
||||||
}
|
}
|
||||||
file.table = reinterpret_cast<const Table*>(file.data->data());
|
file.table = reinterpret_cast<const Table*>(file.data->data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const BattleParamsIndex::Table& BattleParamsIndex::get_table(bool solo, Episode episode) const {
|
const BattleParamsIndex::Table& BattleParamsIndex::get_table(bool solo, Episode episode) const {
|
||||||
uint8_t ep_index;
|
uint8_t ep_index;
|
||||||
switch (episode) {
|
switch (episode) {
|
||||||
case Episode::EP1:
|
case Episode::EP1:
|
||||||
ep_index = 0;
|
ep_index = 0;
|
||||||
break;
|
break;
|
||||||
case Episode::EP2:
|
case Episode::EP2:
|
||||||
ep_index = 1;
|
ep_index = 1;
|
||||||
break;
|
break;
|
||||||
case Episode::EP4:
|
case Episode::EP4:
|
||||||
ep_index = 2;
|
ep_index = 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw invalid_argument("invalid episode");
|
throw invalid_argument("invalid episode");
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this->files[!!solo][ep_index].table;
|
return *this->files[!!solo][ep_index].table;
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-100
@@ -1,100 +1,100 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "EnemyType.hh"
|
#include "EnemyType.hh"
|
||||||
#include "LevelTable.hh"
|
#include "LevelTable.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
class BattleParamsIndex {
|
class BattleParamsIndex {
|
||||||
public:
|
public:
|
||||||
// These files are little-endian, even on PSO GC.
|
// These files are little-endian, even on PSO GC.
|
||||||
|
|
||||||
struct AttackData {
|
struct AttackData {
|
||||||
/* 00 */ le_int16_t unknown_a1;
|
/* 00 */ le_int16_t unknown_a1;
|
||||||
/* 02 */ le_int16_t atp;
|
/* 02 */ le_int16_t atp;
|
||||||
/* 04 */ le_int16_t ata_bonus;
|
/* 04 */ le_int16_t ata_bonus;
|
||||||
/* 06 */ le_uint16_t unknown_a4;
|
/* 06 */ le_uint16_t unknown_a4;
|
||||||
/* 08 */ le_float distance_x;
|
/* 08 */ le_float distance_x;
|
||||||
/* 0C */ le_uint32_t angle_x; // Out of 0x10000 (high 16 bits are unused)
|
/* 0C */ le_uint32_t angle_x; // Out of 0x10000 (high 16 bits are unused)
|
||||||
/* 10 */ le_float distance_y;
|
/* 10 */ le_float distance_y;
|
||||||
/* 14 */ le_uint16_t unknown_a8;
|
/* 14 */ le_uint16_t unknown_a8;
|
||||||
/* 16 */ le_uint16_t unknown_a9;
|
/* 16 */ le_uint16_t unknown_a9;
|
||||||
/* 18 */ le_uint16_t unknown_a10;
|
/* 18 */ le_uint16_t unknown_a10;
|
||||||
/* 1A */ le_uint16_t unknown_a11;
|
/* 1A */ le_uint16_t unknown_a11;
|
||||||
/* 1C */ le_uint32_t unknown_a12;
|
/* 1C */ le_uint32_t unknown_a12;
|
||||||
/* 20 */ le_uint32_t unknown_a13;
|
/* 20 */ le_uint32_t unknown_a13;
|
||||||
/* 24 */ le_uint32_t unknown_a14;
|
/* 24 */ le_uint32_t unknown_a14;
|
||||||
/* 28 */ le_uint32_t unknown_a15;
|
/* 28 */ le_uint32_t unknown_a15;
|
||||||
/* 2C */ le_uint32_t unknown_a16;
|
/* 2C */ le_uint32_t unknown_a16;
|
||||||
/* 30 */
|
/* 30 */
|
||||||
} __packed_ws__(AttackData, 0x30);
|
} __packed_ws__(AttackData, 0x30);
|
||||||
|
|
||||||
struct ResistData {
|
struct ResistData {
|
||||||
/* 00 */ le_int16_t evp_bonus;
|
/* 00 */ le_int16_t evp_bonus;
|
||||||
/* 02 */ le_uint16_t efr;
|
/* 02 */ le_uint16_t efr;
|
||||||
/* 04 */ le_uint16_t eic;
|
/* 04 */ le_uint16_t eic;
|
||||||
/* 06 */ le_uint16_t eth;
|
/* 06 */ le_uint16_t eth;
|
||||||
/* 08 */ le_uint16_t elt;
|
/* 08 */ le_uint16_t elt;
|
||||||
/* 0A */ le_uint16_t edk;
|
/* 0A */ le_uint16_t edk;
|
||||||
/* 0C */ le_uint32_t unknown_a6;
|
/* 0C */ le_uint32_t unknown_a6;
|
||||||
/* 10 */ le_uint32_t unknown_a7;
|
/* 10 */ le_uint32_t unknown_a7;
|
||||||
/* 14 */ le_uint32_t unknown_a8;
|
/* 14 */ le_uint32_t unknown_a8;
|
||||||
/* 18 */ le_uint32_t unknown_a9;
|
/* 18 */ le_uint32_t unknown_a9;
|
||||||
/* 1C */ le_int32_t dfp_bonus;
|
/* 1C */ le_int32_t dfp_bonus;
|
||||||
/* 20 */
|
/* 20 */
|
||||||
} __packed_ws__(ResistData, 0x20);
|
} __packed_ws__(ResistData, 0x20);
|
||||||
|
|
||||||
struct MovementData {
|
struct MovementData {
|
||||||
/* 00 */ le_float idle_move_speed;
|
/* 00 */ le_float idle_move_speed;
|
||||||
/* 04 */ le_float idle_animation_speed;
|
/* 04 */ le_float idle_animation_speed;
|
||||||
/* 08 */ le_float move_speed;
|
/* 08 */ le_float move_speed;
|
||||||
/* 0C */ le_float animation_speed;
|
/* 0C */ le_float animation_speed;
|
||||||
/* 10 */ le_float unknown_a1;
|
/* 10 */ le_float unknown_a1;
|
||||||
/* 14 */ le_float unknown_a2;
|
/* 14 */ le_float unknown_a2;
|
||||||
/* 18 */ le_uint32_t unknown_a3;
|
/* 18 */ le_uint32_t unknown_a3;
|
||||||
/* 1C */ le_uint32_t unknown_a4;
|
/* 1C */ le_uint32_t unknown_a4;
|
||||||
/* 20 */ le_uint32_t unknown_a5;
|
/* 20 */ le_uint32_t unknown_a5;
|
||||||
/* 24 */ le_uint32_t unknown_a6;
|
/* 24 */ le_uint32_t unknown_a6;
|
||||||
/* 28 */ le_uint32_t unknown_a7;
|
/* 28 */ le_uint32_t unknown_a7;
|
||||||
/* 2C */ le_uint32_t unknown_a8;
|
/* 2C */ le_uint32_t unknown_a8;
|
||||||
/* 30 */
|
/* 30 */
|
||||||
} __packed_ws__(MovementData, 0x30);
|
} __packed_ws__(MovementData, 0x30);
|
||||||
|
|
||||||
struct Table {
|
struct Table {
|
||||||
/* 0000 */ parray<parray<PlayerStats, 0x60>, 4> stats;
|
/* 0000 */ parray<parray<PlayerStats, 0x60>, 4> stats;
|
||||||
/* 3600 */ parray<parray<AttackData, 0x60>, 4> attack_data;
|
/* 3600 */ parray<parray<AttackData, 0x60>, 4> attack_data;
|
||||||
/* 7E00 */ parray<parray<ResistData, 0x60>, 4> resist_data;
|
/* 7E00 */ parray<parray<ResistData, 0x60>, 4> resist_data;
|
||||||
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data;
|
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data;
|
||||||
/* F600 */
|
/* F600 */
|
||||||
|
|
||||||
void print(FILE* stream) const;
|
void print(FILE* stream) const;
|
||||||
} __packed_ws__(Table, 0xF600);
|
} __packed_ws__(Table, 0xF600);
|
||||||
|
|
||||||
BattleParamsIndex(
|
BattleParamsIndex(
|
||||||
std::shared_ptr<const std::string> data_on_ep1, // BattleParamEntry_on.dat
|
std::shared_ptr<const std::string> data_on_ep1, // BattleParamEntry_on.dat
|
||||||
std::shared_ptr<const std::string> data_on_ep2, // BattleParamEntry_lab_on.dat
|
std::shared_ptr<const std::string> data_on_ep2, // BattleParamEntry_lab_on.dat
|
||||||
std::shared_ptr<const std::string> data_on_ep4, // BattleParamEntry_ep4_on.dat
|
std::shared_ptr<const std::string> data_on_ep4, // BattleParamEntry_ep4_on.dat
|
||||||
std::shared_ptr<const std::string> data_off_ep1, // BattleParamEntry.dat
|
std::shared_ptr<const std::string> data_off_ep1, // BattleParamEntry.dat
|
||||||
std::shared_ptr<const std::string> data_off_ep2, // BattleParamEntry_lab.dat
|
std::shared_ptr<const std::string> data_off_ep2, // BattleParamEntry_lab.dat
|
||||||
std::shared_ptr<const std::string> data_off_ep4); // BattleParamEntry_ep4.dat
|
std::shared_ptr<const std::string> data_off_ep4); // BattleParamEntry_ep4.dat
|
||||||
|
|
||||||
const Table& get_table(bool solo, Episode episode) const;
|
const Table& get_table(bool solo, Episode episode) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct File {
|
struct File {
|
||||||
std::shared_ptr<const std::string> data;
|
std::shared_ptr<const std::string> data;
|
||||||
const Table* table;
|
const Table* table;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Indexed as [online/offline][episode]
|
// Indexed as [online/offline][episode]
|
||||||
std::array<std::array<File, 3>, 2> files;
|
std::array<std::array<File, 3>, 2> files;
|
||||||
};
|
};
|
||||||
|
|||||||
+188
-188
@@ -1,188 +1,188 @@
|
|||||||
#include "CatSession.hh"
|
#include "CatSession.hh"
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#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 <event2/listener.h>
|
#include <event2/listener.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <phosg/Filesystem.hh>
|
#include <phosg/Filesystem.hh>
|
||||||
#include <phosg/Network.hh>
|
#include <phosg/Network.hh>
|
||||||
#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 "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
#include "ProxyCommands.hh"
|
#include "ProxyCommands.hh"
|
||||||
#include "ReceiveCommands.hh"
|
#include "ReceiveCommands.hh"
|
||||||
#include "ReceiveSubcommands.hh"
|
#include "ReceiveSubcommands.hh"
|
||||||
#include "SendCommands.hh"
|
#include "SendCommands.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
CatSession::exit_shell::exit_shell() : runtime_error("shell exited") {}
|
CatSession::exit_shell::exit_shell() : runtime_error("shell exited") {}
|
||||||
|
|
||||||
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,
|
||||||
Version version,
|
Version version,
|
||||||
shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file)
|
shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file)
|
||||||
: log(string_printf("[CatSession:%s] ", name_for_enum(version)), proxy_server_log.min_level),
|
: log(string_printf("[CatSession:%s] ", name_for_enum(version)), proxy_server_log.min_level),
|
||||||
base(base),
|
base(base),
|
||||||
read_event(event_new(this->base.get(), 0, EV_READ | EV_PERSIST, CatSession::dispatch_read_stdin, this), event_free),
|
read_event(event_new(this->base.get(), 0, EV_READ | EV_PERSIST, CatSession::dispatch_read_stdin, this), event_free),
|
||||||
channel(version, 1, CatSession::dispatch_on_channel_input, CatSession::dispatch_on_channel_error, this, "CatSession"),
|
channel(version, 1, CatSession::dispatch_on_channel_input, CatSession::dispatch_on_channel_error, this, "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");
|
||||||
}
|
}
|
||||||
|
|
||||||
string netloc_str = render_sockaddr_storage(remote);
|
string netloc_str = render_sockaddr_storage(remote);
|
||||||
this->log.info("Connecting to %s", netloc_str.c_str());
|
this->log.info("Connecting to %s", netloc_str.c_str());
|
||||||
|
|
||||||
struct bufferevent* bev = bufferevent_socket_new(
|
struct bufferevent* bev = bufferevent_socket_new(
|
||||||
this->base.get(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
this->base.get(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||||
if (!bev) {
|
if (!bev) {
|
||||||
throw runtime_error(string_printf("failed to open socket (%d)", EVUTIL_SOCKET_ERROR()));
|
throw runtime_error(string_printf("failed to open socket (%d)", EVUTIL_SOCKET_ERROR()));
|
||||||
}
|
}
|
||||||
this->channel.set_bufferevent(bev, 0);
|
this->channel.set_bufferevent(bev, 0);
|
||||||
|
|
||||||
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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
event_add(this->read_event.get(), nullptr);
|
event_add(this->read_event.get(), nullptr);
|
||||||
this->poll.add(0, POLLIN);
|
this->poll.add(0, POLLIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
send_command_with_header(this->channel, full_cmd.data(), full_cmd.size());
|
send_command_with_header(this->channel, full_cmd.data(), full_cmd.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatSession::dispatch_on_channel_input(
|
void CatSession::dispatch_on_channel_input(
|
||||||
Channel& ch, uint16_t command, uint32_t flag, std::string& data) {
|
Channel& ch, uint16_t command, uint32_t flag, std::string& data) {
|
||||||
auto* session = reinterpret_cast<CatSession*>(ch.context_obj);
|
auto* session = reinterpret_cast<CatSession*>(ch.context_obj);
|
||||||
session->on_channel_input(command, flag, data);
|
session->on_channel_input(command, flag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatSession::on_channel_input(
|
void CatSession::on_channel_input(
|
||||||
uint16_t command, uint32_t flag, std::string& data) {
|
uint16_t command, uint32_t flag, std::string& data) {
|
||||||
if (!uses_v4_encryption(this->channel.version)) {
|
if (!uses_v4_encryption(this->channel.version)) {
|
||||||
if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) {
|
if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) {
|
||||||
const auto& cmd = check_size_t<S_ServerInitDefault_DC_PC_V3_02_17_91_9B>(data, 0xFFFF);
|
const auto& cmd = check_size_t<S_ServerInitDefault_DC_PC_V3_02_17_91_9B>(data, 0xFFFF);
|
||||||
if (uses_v3_encryption(this->channel.version)) {
|
if (uses_v3_encryption(this->channel.version)) {
|
||||||
this->channel.crypt_in = make_shared<PSOV3Encryption>(cmd.server_key);
|
this->channel.crypt_in = make_shared<PSOV3Encryption>(cmd.server_key);
|
||||||
this->channel.crypt_out = make_shared<PSOV3Encryption>(cmd.client_key);
|
this->channel.crypt_out = make_shared<PSOV3Encryption>(cmd.client_key);
|
||||||
this->log.info("Enabled V3 encryption (server key %08" PRIX32 ", client key %08" PRIX32 ")",
|
this->log.info("Enabled V3 encryption (server key %08" PRIX32 ", client key %08" PRIX32 ")",
|
||||||
cmd.server_key.load(), cmd.client_key.load());
|
cmd.server_key.load(), cmd.client_key.load());
|
||||||
} else { // PC, DC, or patch server
|
} else { // PC, DC, or patch server
|
||||||
this->channel.crypt_in = make_shared<PSOV2Encryption>(cmd.server_key);
|
this->channel.crypt_in = make_shared<PSOV2Encryption>(cmd.server_key);
|
||||||
this->channel.crypt_out = make_shared<PSOV2Encryption>(cmd.client_key);
|
this->channel.crypt_out = make_shared<PSOV2Encryption>(cmd.client_key);
|
||||||
this->log.info("Enabled V2 encryption (server key %08" PRIX32 ", client key %08" PRIX32 ")",
|
this->log.info("Enabled V2 encryption (server key %08" PRIX32 ", client key %08" PRIX32 ")",
|
||||||
cmd.server_key.load(), cmd.client_key.load());
|
cmd.server_key.load(), cmd.client_key.load());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // BB
|
} else { // BB
|
||||||
if (command == 0x03 || command == 0x9B) {
|
if (command == 0x03 || command == 0x9B) {
|
||||||
if (!this->bb_key_file) {
|
if (!this->bb_key_file) {
|
||||||
throw runtime_error("BB encryption requires a key file");
|
throw runtime_error("BB encryption requires a key file");
|
||||||
}
|
}
|
||||||
const auto& cmd = check_size_t<S_ServerInitDefault_BB_03_9B>(data, 0xFFFF);
|
const auto& cmd = check_size_t<S_ServerInitDefault_BB_03_9B>(data, 0xFFFF);
|
||||||
this->channel.crypt_in = make_shared<PSOBBEncryption>(*this->bb_key_file, &cmd.server_key[0], sizeof(cmd.server_key));
|
this->channel.crypt_in = make_shared<PSOBBEncryption>(*this->bb_key_file, &cmd.server_key[0], sizeof(cmd.server_key));
|
||||||
this->channel.crypt_out = make_shared<PSOBBEncryption>(*this->bb_key_file, &cmd.client_key[0], sizeof(cmd.client_key));
|
this->channel.crypt_out = make_shared<PSOBBEncryption>(*this->bb_key_file, &cmd.client_key[0], sizeof(cmd.client_key));
|
||||||
this->log.info("Enabled BB encryption");
|
this->log.info("Enabled BB encryption");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use the iovec form of print_data here instead of
|
// TODO: Use the iovec form of print_data here instead of
|
||||||
// prepend_command_header (which copies the string)
|
// prepend_command_header (which copies the string)
|
||||||
string full_cmd = prepend_command_header(
|
string full_cmd = prepend_command_header(
|
||||||
this->channel.version, this->channel.crypt_in.get(), command, flag, data);
|
this->channel.version, this->channel.crypt_in.get(), command, flag, data);
|
||||||
print_data(stdout, full_cmd, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::OFFSET_16_BITS);
|
print_data(stdout, full_cmd, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::OFFSET_16_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatSession::dispatch_on_channel_error(Channel& ch, short events) {
|
void CatSession::dispatch_on_channel_error(Channel& ch, short events) {
|
||||||
auto* session = reinterpret_cast<CatSession*>(ch.context_obj);
|
auto* session = reinterpret_cast<CatSession*>(ch.context_obj);
|
||||||
session->on_channel_error(events);
|
session->on_channel_error(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatSession::on_channel_error(short events) {
|
void CatSession::on_channel_error(short events) {
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
this->log.info("Channel connected");
|
this->log.info("Channel connected");
|
||||||
}
|
}
|
||||||
if (events & BEV_EVENT_ERROR) {
|
if (events & BEV_EVENT_ERROR) {
|
||||||
int err = EVUTIL_SOCKET_ERROR();
|
int err = EVUTIL_SOCKET_ERROR();
|
||||||
this->log.warning("Error %d (%s) in unlinked client stream", err,
|
this->log.warning("Error %d (%s) in unlinked client stream", err,
|
||||||
evutil_socket_error_to_string(err));
|
evutil_socket_error_to_string(err));
|
||||||
}
|
}
|
||||||
if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
|
if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
|
||||||
this->log.info("Session endpoint has disconnected");
|
this->log.info("Session endpoint has disconnected");
|
||||||
this->channel.disconnect();
|
this->channel.disconnect();
|
||||||
event_base_loopexit(this->base.get(), nullptr);
|
event_base_loopexit(this->base.get(), nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatSession::dispatch_read_stdin(evutil_socket_t, short, void* ctx) {
|
void CatSession::dispatch_read_stdin(evutil_socket_t, short, void* ctx) {
|
||||||
reinterpret_cast<CatSession*>(ctx)->read_stdin();
|
reinterpret_cast<CatSession*>(ctx)->read_stdin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatSession::read_stdin() {
|
void CatSession::read_stdin() {
|
||||||
bool any_command_read = false;
|
bool any_command_read = false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto poll_result = this->poll.poll();
|
auto poll_result = this->poll.poll();
|
||||||
short fd_events = 0;
|
short fd_events = 0;
|
||||||
try {
|
try {
|
||||||
fd_events = poll_result.at(0);
|
fd_events = poll_result.at(0);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(fd_events & POLLIN)) {
|
if (!(fd_events & POLLIN)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
string command(2048, '\0');
|
string command(2048, '\0');
|
||||||
if (!fgets(command.data(), command.size(), stdin)) {
|
if (!fgets(command.data(), command.size(), stdin)) {
|
||||||
if (!any_command_read) {
|
if (!any_command_read) {
|
||||||
// ctrl+d probably; we should exit
|
// ctrl+d probably; we should exit
|
||||||
fputc('\n', stderr);
|
fputc('\n', stderr);
|
||||||
event_base_loopexit(this->base.get(), nullptr);
|
event_base_loopexit(this->base.get(), nullptr);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
break; // probably not EOF; just no more commands for now
|
break; // probably not EOF; just no more commands for now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim the extra data off the string
|
// trim the extra data off the string
|
||||||
size_t len = strlen(command.c_str());
|
size_t len = strlen(command.c_str());
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (command[len - 1] == '\n') {
|
if (command[len - 1] == '\n') {
|
||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
command.resize(len);
|
command.resize(len);
|
||||||
any_command_read = true;
|
any_command_read = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
execute_command(command);
|
execute_command(command);
|
||||||
} catch (const exit_shell&) {
|
} catch (const exit_shell&) {
|
||||||
event_base_loopexit(this->base.get(), nullptr);
|
event_base_loopexit(this->base.get(), nullptr);
|
||||||
return;
|
return;
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
fprintf(stderr, "FAILED: %s\n", e.what());
|
fprintf(stderr, "FAILED: %s\n", e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-54
@@ -1,54 +1,54 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Filesystem.hh>
|
#include <phosg/Filesystem.hh>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
class CatSession {
|
class CatSession {
|
||||||
public:
|
public:
|
||||||
CatSession(
|
CatSession(
|
||||||
std::shared_ptr<struct event_base> base,
|
std::shared_ptr<struct event_base> base,
|
||||||
const struct sockaddr_storage& remote,
|
const struct sockaddr_storage& remote,
|
||||||
Version version,
|
Version version,
|
||||||
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file);
|
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file);
|
||||||
CatSession(const CatSession&) = delete;
|
CatSession(const CatSession&) = delete;
|
||||||
CatSession(CatSession&&) = delete;
|
CatSession(CatSession&&) = delete;
|
||||||
CatSession& operator=(const CatSession&) = delete;
|
CatSession& operator=(const CatSession&) = delete;
|
||||||
CatSession& operator=(CatSession&&) = delete;
|
CatSession& operator=(CatSession&&) = delete;
|
||||||
virtual ~CatSession() = default;
|
virtual ~CatSession() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PrefixedLogger log;
|
PrefixedLogger log;
|
||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
std::unique_ptr<struct event, void (*)(struct event*)> read_event;
|
std::unique_ptr<struct event, void (*)(struct event*)> read_event;
|
||||||
Poll poll;
|
Poll poll;
|
||||||
|
|
||||||
Channel channel;
|
Channel channel;
|
||||||
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file;
|
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file;
|
||||||
|
|
||||||
class exit_shell : public std::runtime_error {
|
class exit_shell : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
exit_shell();
|
exit_shell();
|
||||||
~exit_shell() = default;
|
~exit_shell() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void execute_command(const std::string& command);
|
virtual void execute_command(const std::string& command);
|
||||||
|
|
||||||
static void dispatch_read_stdin(evutil_socket_t fd, short events, void* ctx);
|
static void dispatch_read_stdin(evutil_socket_t fd, short events, void* ctx);
|
||||||
static void dispatch_on_channel_input(Channel& ch, uint16_t command, uint32_t flag, std::string& msg);
|
static void dispatch_on_channel_input(Channel& ch, uint16_t command, uint32_t flag, std::string& msg);
|
||||||
static void dispatch_on_channel_error(Channel& ch, short events);
|
static void dispatch_on_channel_error(Channel& ch, short events);
|
||||||
void on_channel_input(uint16_t command, uint32_t flag, std::string& msg);
|
void on_channel_input(uint16_t command, uint32_t flag, std::string& msg);
|
||||||
void on_channel_error(short events);
|
void on_channel_error(short events);
|
||||||
void read_stdin();
|
void read_stdin();
|
||||||
};
|
};
|
||||||
|
|||||||
+421
-421
@@ -1,421 +1,421 @@
|
|||||||
#include "Channel.hh"
|
#include "Channel.hh"
|
||||||
|
|
||||||
#include <errno.h>
|
#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 <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <phosg/Network.hh>
|
#include <phosg/Network.hh>
|
||||||
#include <phosg/Time.hh>
|
#include <phosg/Time.hh>
|
||||||
|
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
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(
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
on_command_received_t on_command_received,
|
on_command_received_t on_command_received,
|
||||||
on_error_t on_error,
|
on_error_t on_error,
|
||||||
void* context_obj,
|
void* context_obj,
|
||||||
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),
|
||||||
virtual_network_id(0),
|
virtual_network_id(0),
|
||||||
version(version),
|
version(version),
|
||||||
language(language),
|
language(language),
|
||||||
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(
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
uint64_t virtual_network_id,
|
uint64_t virtual_network_id,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
on_command_received_t on_command_received,
|
on_command_received_t on_command_received,
|
||||||
on_error_t on_error,
|
on_error_t on_error,
|
||||||
void* context_obj,
|
void* context_obj,
|
||||||
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),
|
||||||
language(language),
|
language(language),
|
||||||
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, virtual_network_id);
|
this->set_bufferevent(bev, virtual_network_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::replace_with(
|
void Channel::replace_with(
|
||||||
Channel&& other,
|
Channel&& other,
|
||||||
on_command_received_t on_command_received,
|
on_command_received_t on_command_received,
|
||||||
on_error_t on_error,
|
on_error_t on_error,
|
||||||
void* context_obj,
|
void* context_obj,
|
||||||
const std::string& name) {
|
const std::string& name) {
|
||||||
this->set_bufferevent(other.bev.release(), other.virtual_network_id);
|
this->set_bufferevent(other.bev.release(), other.virtual_network_id);
|
||||||
this->local_addr = other.local_addr;
|
this->local_addr = other.local_addr;
|
||||||
this->remote_addr = other.remote_addr;
|
this->remote_addr = other.remote_addr;
|
||||||
this->version = other.version;
|
this->version = other.version;
|
||||||
this->language = other.language;
|
this->language = other.language;
|
||||||
this->crypt_in = other.crypt_in;
|
this->crypt_in = other.crypt_in;
|
||||||
this->crypt_out = other.crypt_out;
|
this->crypt_out = other.crypt_out;
|
||||||
this->name = name;
|
this->name = name;
|
||||||
this->terminal_send_color = other.terminal_send_color;
|
this->terminal_send_color = other.terminal_send_color;
|
||||||
this->terminal_recv_color = other.terminal_recv_color;
|
this->terminal_recv_color = other.terminal_recv_color;
|
||||||
this->on_command_received = on_command_received;
|
this->on_command_received = on_command_received;
|
||||||
this->on_error = on_error;
|
this->on_error = on_error;
|
||||||
this->context_obj = context_obj;
|
this->context_obj = context_obj;
|
||||||
other.disconnect(); // Clears crypts, addrs, etc.
|
other.disconnect(); // Clears crypts, addrs, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::set_bufferevent(struct bufferevent* bev, uint64_t virtual_network_id) {
|
void Channel::set_bufferevent(struct bufferevent* bev, uint64_t virtual_network_id) {
|
||||||
this->bev.reset(bev);
|
this->bev.reset(bev);
|
||||||
this->virtual_network_id = virtual_network_id;
|
this->virtual_network_id = virtual_network_id;
|
||||||
|
|
||||||
if (this->bev.get()) {
|
if (this->bev.get()) {
|
||||||
int fd = bufferevent_getfd(this->bev.get());
|
int fd = bufferevent_getfd(this->bev.get());
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
memset(&this->local_addr, 0, sizeof(this->local_addr));
|
memset(&this->local_addr, 0, sizeof(this->local_addr));
|
||||||
memset(&this->remote_addr, 0, sizeof(this->remote_addr));
|
memset(&this->remote_addr, 0, sizeof(this->remote_addr));
|
||||||
} else {
|
} else {
|
||||||
get_socket_addresses(fd, &this->local_addr, &this->remote_addr);
|
get_socket_addresses(fd, &this->local_addr, &this->remote_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferevent_setcb(this->bev.get(), &Channel::dispatch_on_input, nullptr, &Channel::dispatch_on_error, this);
|
bufferevent_setcb(this->bev.get(), &Channel::dispatch_on_input, nullptr, &Channel::dispatch_on_error, this);
|
||||||
bufferevent_enable(this->bev.get(), EV_READ | EV_WRITE);
|
bufferevent_enable(this->bev.get(), EV_READ | EV_WRITE);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
memset(&this->local_addr, 0, sizeof(this->local_addr));
|
memset(&this->local_addr, 0, sizeof(this->local_addr));
|
||||||
memset(&this->remote_addr, 0, sizeof(this->remote_addr));
|
memset(&this->remote_addr, 0, sizeof(this->remote_addr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
// pool instead of disconnecting it, to make sure all the data gets sent.
|
// pool instead of disconnecting it, to make sure all the data gets sent.
|
||||||
struct evbuffer* out_buffer = bufferevent_get_output(this->bev.get());
|
struct evbuffer* out_buffer = bufferevent_get_output(this->bev.get());
|
||||||
if (evbuffer_get_length(out_buffer) == 0) {
|
if (evbuffer_get_length(out_buffer) == 0) {
|
||||||
this->bev.reset(); // Destructor flushes and frees the bufferevent
|
this->bev.reset(); // Destructor flushes and frees the bufferevent
|
||||||
} else {
|
} else {
|
||||||
// The callbacks will free it when all the data is sent or the client
|
// The callbacks will free it when all the data is sent or the client
|
||||||
// disconnects
|
// disconnects
|
||||||
|
|
||||||
auto on_output = +[](struct bufferevent* bev, void*) -> void {
|
auto on_output = +[](struct bufferevent* bev, void*) -> void {
|
||||||
flush_and_free_bufferevent(bev);
|
flush_and_free_bufferevent(bev);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto on_error = +[](struct bufferevent* bev, short events, void*) -> void {
|
auto on_error = +[](struct bufferevent* bev, short events, void*) -> void {
|
||||||
if (events & BEV_EVENT_ERROR) {
|
if (events & BEV_EVENT_ERROR) {
|
||||||
int err = EVUTIL_SOCKET_ERROR();
|
int err = EVUTIL_SOCKET_ERROR();
|
||||||
channel_exceptions_log.warning(
|
channel_exceptions_log.warning(
|
||||||
"Disconnecting channel caused error %d (%s)", err,
|
"Disconnecting channel caused error %d (%s)", err,
|
||||||
evutil_socket_error_to_string(err));
|
evutil_socket_error_to_string(err));
|
||||||
}
|
}
|
||||||
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
|
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
|
||||||
bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
|
bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
|
||||||
bufferevent_free(bev);
|
bufferevent_free(bev);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bufferevent* bev = this->bev.release();
|
struct bufferevent* bev = this->bev.release();
|
||||||
bufferevent_setcb(bev, nullptr, on_output, on_error, bev);
|
bufferevent_setcb(bev, nullptr, on_output, on_error, bev);
|
||||||
bufferevent_disable(bev, EV_READ);
|
bufferevent_disable(bev, EV_READ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&this->local_addr, 0, sizeof(this->local_addr));
|
memset(&this->local_addr, 0, sizeof(this->local_addr));
|
||||||
memset(&this->remote_addr, 0, sizeof(this->remote_addr));
|
memset(&this->remote_addr, 0, sizeof(this->remote_addr));
|
||||||
this->virtual_network_id = false;
|
this->virtual_network_id = false;
|
||||||
this->crypt_in.reset();
|
this->crypt_in.reset();
|
||||||
this->crypt_out.reset();
|
this->crypt_out.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel::Message Channel::recv() {
|
Channel::Message Channel::recv() {
|
||||||
struct evbuffer* buf = bufferevent_get_input(this->bev.get());
|
struct evbuffer* buf = bufferevent_get_input(this->bev.get());
|
||||||
|
|
||||||
size_t header_size = (this->version == Version::BB_V4) ? 8 : 4;
|
size_t header_size = (this->version == Version::BB_V4) ? 8 : 4;
|
||||||
PSOCommandHeader header;
|
PSOCommandHeader header;
|
||||||
if (evbuffer_copyout(buf, &header, header_size) < static_cast<ssize_t>(header_size)) {
|
if (evbuffer_copyout(buf, &header, header_size) < static_cast<ssize_t>(header_size)) {
|
||||||
throw out_of_range("no command available");
|
throw out_of_range("no command available");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->crypt_in.get()) {
|
if (this->crypt_in.get()) {
|
||||||
this->crypt_in->decrypt(&header, header_size, false);
|
this->crypt_in->decrypt(&header, header_size, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t command_logical_size = header.size(version);
|
size_t command_logical_size = header.size(version);
|
||||||
|
|
||||||
// If encryption is enabled, BB pads commands to 8-byte boundaries, and this
|
// If encryption is enabled, BB pads commands to 8-byte boundaries, and this
|
||||||
// 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 == Version::BB_V4))
|
size_t command_physical_size = (this->crypt_in.get() && (version == Version::BB_V4))
|
||||||
? ((command_logical_size + 7) & ~7)
|
? ((command_logical_size + 7) & ~7)
|
||||||
: command_logical_size;
|
: 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, then there is a full command in the buffer. Some encryption
|
// If we get here, then there is a full command in the buffer. Some encryption
|
||||||
// algorithms' advancement depends on the decrypted data, so we have to
|
// algorithms' advancement depends on the decrypted data, so we have to
|
||||||
// actually decrypt the header again (with advance=true) to keep them in a
|
// actually decrypt the header again (with advance=true) to keep them in a
|
||||||
// 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()) < static_cast<ssize_t>(header_data.size())) {
|
if (evbuffer_remove(buf, header_data.data(), 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()) {
|
||||||
this->crypt_in->decrypt(header_data.data(), header_data.size());
|
this->crypt_in->decrypt(header_data.data(), header_data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) < static_cast<ssize_t>(command_data.size())) {
|
if (evbuffer_remove(buf, command_data.data(), 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->crypt_in.get()) {
|
if (this->crypt_in.get()) {
|
||||||
// Some versions of PSO DC can send commands whose sizes are not a multiple
|
// Some versions of PSO DC can send commands whose sizes are not a multiple
|
||||||
// of 4, but the server is expected to always use a multiple of 4 bytes when
|
// of 4, but the server is expected to always use a multiple of 4 bytes when
|
||||||
// decrypting (the extra cipher bytes are lost). To emulate this behavior,
|
// decrypting (the extra cipher bytes are lost). To emulate this behavior,
|
||||||
// we have to round up the size for DC commands here.
|
// we have to round up the size for DC commands here.
|
||||||
size_t orig_size = command_data.size();
|
size_t orig_size = command_data.size();
|
||||||
command_data.resize((orig_size + 3) & (~3), 0);
|
command_data.resize((orig_size + 3) & (~3), 0);
|
||||||
this->crypt_in->decrypt(command_data.data(), command_data.size());
|
this->crypt_in->decrypt(command_data.data(), command_data.size());
|
||||||
command_data.resize(orig_size);
|
command_data.resize(orig_size);
|
||||||
}
|
}
|
||||||
command_data.resize(command_logical_size - header_size);
|
command_data.resize(command_logical_size - header_size);
|
||||||
|
|
||||||
if (command_data_log.should_log(LogLevel::INFO) && (this->terminal_recv_color != TerminalFormat::END)) {
|
if (command_data_log.should_log(LogLevel::INFO) && (this->terminal_recv_color != TerminalFormat::END)) {
|
||||||
if (use_terminal_colors && this->terminal_recv_color != TerminalFormat::NORMAL) {
|
if (use_terminal_colors && this->terminal_recv_color != TerminalFormat::NORMAL) {
|
||||||
print_color_escape(stderr, this->terminal_recv_color, TerminalFormat::BOLD, TerminalFormat::END);
|
print_color_escape(stderr, this->terminal_recv_color, TerminalFormat::BOLD, TerminalFormat::END);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == Version::BB_V4) {
|
if (version == Version::BB_V4) {
|
||||||
command_data_log.info(
|
command_data_log.info(
|
||||||
"Received from %s (version=BB command=%04hX flag=%08" PRIX32 ")",
|
"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(
|
command_data_log.info(
|
||||||
"Received from %s (version=%s command=%02hX flag=%02" PRIX32 ")",
|
"Received from %s (version=%s command=%02hX flag=%02" PRIX32 ")",
|
||||||
this->name.c_str(),
|
this->name.c_str(),
|
||||||
name_for_enum(this->version),
|
name_for_enum(this->version),
|
||||||
header.command(this->version),
|
header.command(this->version),
|
||||||
header.flag(this->version));
|
header.flag(this->version));
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<struct iovec> iovs;
|
vector<struct iovec> iovs;
|
||||||
iovs.emplace_back(iovec{.iov_base = header_data.data(), .iov_len = header_data.size()});
|
iovs.emplace_back(iovec{.iov_base = header_data.data(), .iov_len = header_data.size()});
|
||||||
iovs.emplace_back(iovec{.iov_base = command_data.data(), .iov_len = command_data.size()});
|
iovs.emplace_back(iovec{.iov_base = command_data.data(), .iov_len = command_data.size()});
|
||||||
print_data(stderr, iovs, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
print_data(stderr, iovs, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
|
|
||||||
if (use_terminal_colors && this->terminal_recv_color != TerminalFormat::NORMAL) {
|
if (use_terminal_colors && this->terminal_recv_color != TerminalFormat::NORMAL) {
|
||||||
print_color_escape(stderr, TerminalFormat::NORMAL, TerminalFormat::END);
|
print_color_escape(stderr, TerminalFormat::NORMAL, TerminalFormat::END);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.command = header.command(this->version),
|
.command = header.command(this->version),
|
||||||
.flag = header.flag(this->version),
|
.flag = header.flag(this->version),
|
||||||
.data = std::move(command_data),
|
.data = std::move(command_data),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::send(uint16_t cmd, uint32_t flag, bool silent) {
|
void Channel::send(uint16_t cmd, uint32_t flag, bool silent) {
|
||||||
this->send(cmd, flag, nullptr, 0, silent);
|
this->send(cmd, flag, nullptr, 0, silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::send(uint16_t cmd, uint32_t flag, const std::vector<std::pair<const void*, size_t>> blocks, bool silent) {
|
void Channel::send(uint16_t cmd, uint32_t flag, const std::vector<std::pair<const void*, size_t>> blocks, bool silent) {
|
||||||
if (!this->connected()) {
|
if (!this->connected()) {
|
||||||
channel_exceptions_log.warning("Attempted to send command on closed channel; dropping data");
|
channel_exceptions_log.warning("Attempted to send command on closed channel; dropping data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
for (const auto& b : blocks) {
|
for (const auto& b : blocks) {
|
||||||
size += b.second;
|
size += b.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
string send_data;
|
string send_data;
|
||||||
size_t logical_size;
|
size_t logical_size;
|
||||||
size_t send_data_size = 0;
|
size_t send_data_size = 0;
|
||||||
switch (this->version) {
|
switch (this->version) {
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
case Version::XB_V3: {
|
case Version::XB_V3: {
|
||||||
PSOCommandHeaderDCV3 header;
|
PSOCommandHeaderDCV3 header;
|
||||||
if (this->crypt_out.get() &&
|
if (this->crypt_out.get() &&
|
||||||
(this->version != Version::DC_NTE) &&
|
(this->version != Version::DC_NTE) &&
|
||||||
(this->version != Version::DC_V1_11_2000_PROTOTYPE) &&
|
(this->version != Version::DC_V1_11_2000_PROTOTYPE) &&
|
||||||
(this->version != Version::DC_V1)) {
|
(this->version != Version::DC_V1)) {
|
||||||
send_data_size = (sizeof(header) + size + 3) & ~3;
|
send_data_size = (sizeof(header) + size + 3) & ~3;
|
||||||
} else {
|
} else {
|
||||||
send_data_size = (sizeof(header) + size);
|
send_data_size = (sizeof(header) + size);
|
||||||
}
|
}
|
||||||
logical_size = send_data_size;
|
logical_size = send_data_size;
|
||||||
header.command = cmd;
|
header.command = cmd;
|
||||||
header.flag = flag;
|
header.flag = flag;
|
||||||
header.size = send_data_size;
|
header.size = send_data_size;
|
||||||
send_data.append(reinterpret_cast<const char*>(&header), sizeof(header));
|
send_data.append(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2: {
|
case Version::PC_V2: {
|
||||||
PSOCommandHeaderPC header;
|
PSOCommandHeaderPC header;
|
||||||
if (this->crypt_out.get()) {
|
if (this->crypt_out.get()) {
|
||||||
send_data_size = (sizeof(header) + size + 3) & ~3;
|
send_data_size = (sizeof(header) + size + 3) & ~3;
|
||||||
} else {
|
} else {
|
||||||
send_data_size = (sizeof(header) + size);
|
send_data_size = (sizeof(header) + size);
|
||||||
}
|
}
|
||||||
logical_size = send_data_size;
|
logical_size = send_data_size;
|
||||||
header.size = send_data_size;
|
header.size = send_data_size;
|
||||||
header.command = cmd;
|
header.command = cmd;
|
||||||
header.flag = flag;
|
header.flag = flag;
|
||||||
send_data.append(reinterpret_cast<const char*>(&header), sizeof(header));
|
send_data.append(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Version::BB_V4: {
|
case Version::BB_V4: {
|
||||||
// BB has an annoying behavior here: command lengths must be multiples of
|
// BB has an annoying behavior here: command lengths must be multiples of
|
||||||
// 4, but the actual data length must be a multiple of 8. If the size
|
// 4, but the actual data length must be a multiple of 8. If the size
|
||||||
// field is not divisible by 8, 4 extra bytes are sent anyway. This
|
// field is not divisible by 8, 4 extra bytes are sent anyway. This
|
||||||
// behavior only applies when encryption is enabled - any commands sent
|
// behavior only applies when encryption is enabled - any commands sent
|
||||||
// before encryption is enabled have no size restrictions (except they
|
// before encryption is enabled have no size restrictions (except they
|
||||||
// must include a full header and must fit in the client's receive
|
// must include a full header and must fit in the client's receive
|
||||||
// buffer), and no implicit extra bytes are sent.
|
// buffer), and no implicit extra bytes are sent.
|
||||||
PSOCommandHeaderBB header;
|
PSOCommandHeaderBB header;
|
||||||
if (this->crypt_out.get()) {
|
if (this->crypt_out.get()) {
|
||||||
send_data_size = (sizeof(header) + size + 7) & ~7;
|
send_data_size = (sizeof(header) + size + 7) & ~7;
|
||||||
} else {
|
} else {
|
||||||
send_data_size = (sizeof(header) + size);
|
send_data_size = (sizeof(header) + size);
|
||||||
}
|
}
|
||||||
logical_size = (sizeof(header) + size + 3) & ~3;
|
logical_size = (sizeof(header) + size + 3) & ~3;
|
||||||
header.size = logical_size;
|
header.size = logical_size;
|
||||||
header.command = cmd;
|
header.command = cmd;
|
||||||
header.flag = flag;
|
header.flag = flag;
|
||||||
send_data.append(reinterpret_cast<const char*>(&header), sizeof(header));
|
send_data.append(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw logic_error("unimplemented game version in send_command");
|
throw logic_error("unimplemented game version in send_command");
|
||||||
}
|
}
|
||||||
|
|
||||||
// All versions of PSO I've seen (so far) have a receive buffer 0x7C00
|
// All versions of PSO I've seen (so far) have a receive buffer 0x7C00
|
||||||
// bytes in size
|
// bytes in size
|
||||||
if (send_data_size > 0x7C00) {
|
if (send_data_size > 0x7C00) {
|
||||||
throw runtime_error("outbound command too large");
|
throw runtime_error("outbound command too large");
|
||||||
}
|
}
|
||||||
|
|
||||||
send_data.reserve(send_data_size);
|
send_data.reserve(send_data_size);
|
||||||
for (const auto& b : blocks) {
|
for (const auto& b : blocks) {
|
||||||
send_data.append(reinterpret_cast<const char*>(b.first), b.second);
|
send_data.append(reinterpret_cast<const char*>(b.first), b.second);
|
||||||
}
|
}
|
||||||
send_data.resize(send_data_size, '\0');
|
send_data.resize(send_data_size, '\0');
|
||||||
|
|
||||||
if (!silent && (command_data_log.should_log(LogLevel::INFO)) && (this->terminal_send_color != TerminalFormat::END)) {
|
if (!silent && (command_data_log.should_log(LogLevel::INFO)) && (this->terminal_send_color != TerminalFormat::END)) {
|
||||||
if (use_terminal_colors && this->terminal_send_color != TerminalFormat::NORMAL) {
|
if (use_terminal_colors && this->terminal_send_color != TerminalFormat::NORMAL) {
|
||||||
print_color_escape(stderr, TerminalFormat::FG_YELLOW, TerminalFormat::BOLD, TerminalFormat::END);
|
print_color_escape(stderr, TerminalFormat::FG_YELLOW, TerminalFormat::BOLD, TerminalFormat::END);
|
||||||
}
|
}
|
||||||
if (version == Version::BB_V4) {
|
if (version == Version::BB_V4) {
|
||||||
command_data_log.info("Sending to %s (version=BB command=%04hX flag=%08" PRIX32 ")",
|
command_data_log.info("Sending to %s (version=BB command=%04hX flag=%08" PRIX32 ")",
|
||||||
this->name.c_str(), cmd, flag);
|
this->name.c_str(), cmd, flag);
|
||||||
} else {
|
} else {
|
||||||
command_data_log.info("Sending to %s (version=%s command=%02hX flag=%02" PRIX32 ")",
|
command_data_log.info("Sending to %s (version=%s command=%02hX flag=%02" PRIX32 ")",
|
||||||
this->name.c_str(), name_for_enum(version), cmd, flag);
|
this->name.c_str(), name_for_enum(version), cmd, flag);
|
||||||
}
|
}
|
||||||
print_data(stderr, send_data.data(), logical_size, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
print_data(stderr, send_data.data(), logical_size, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
|
||||||
if (use_terminal_colors && this->terminal_send_color != TerminalFormat::NORMAL) {
|
if (use_terminal_colors && this->terminal_send_color != TerminalFormat::NORMAL) {
|
||||||
print_color_escape(stderr, TerminalFormat::NORMAL, TerminalFormat::END);
|
print_color_escape(stderr, TerminalFormat::NORMAL, TerminalFormat::END);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->crypt_out.get()) {
|
if (this->crypt_out.get()) {
|
||||||
this->crypt_out->encrypt(send_data.data(), send_data.size());
|
this->crypt_out->encrypt(send_data.data(), send_data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
struct evbuffer* buf = bufferevent_get_output(this->bev.get());
|
struct evbuffer* buf = bufferevent_get_output(this->bev.get());
|
||||||
evbuffer_add(buf, send_data.data(), send_data.size());
|
evbuffer_add(buf, send_data.data(), send_data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::send(uint16_t cmd, uint32_t flag, const void* data, size_t size, bool silent) {
|
void Channel::send(uint16_t cmd, uint32_t flag, const void* data, size_t size, bool silent) {
|
||||||
this->send(cmd, flag, {make_pair(data, size)}, silent);
|
this->send(cmd, flag, {make_pair(data, size)}, silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::send(uint16_t cmd, uint32_t flag, const string& data, bool silent) {
|
void Channel::send(uint16_t cmd, uint32_t flag, const string& data, bool silent) {
|
||||||
this->send(cmd, flag, data.data(), data.size(), silent);
|
this->send(cmd, flag, data.data(), data.size(), silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::send(const void* data, size_t size, bool silent) {
|
void Channel::send(const void* data, size_t size, bool silent) {
|
||||||
size_t header_size = (this->version == Version::BB_V4) ? 8 : 4;
|
size_t header_size = (this->version == Version::BB_V4) ? 8 : 4;
|
||||||
const auto* header = reinterpret_cast<const PSOCommandHeader*>(data);
|
const auto* header = reinterpret_cast<const PSOCommandHeader*>(data);
|
||||||
this->send(
|
this->send(
|
||||||
header->command(this->version),
|
header->command(this->version),
|
||||||
header->flag(this->version),
|
header->flag(this->version),
|
||||||
reinterpret_cast<const uint8_t*>(data) + header_size,
|
reinterpret_cast<const uint8_t*>(data) + header_size,
|
||||||
size - header_size,
|
size - header_size,
|
||||||
silent);
|
silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::send(const string& data, bool silent) {
|
void Channel::send(const string& data, bool silent) {
|
||||||
return this->send(data.data(), data.size(), silent);
|
return this->send(data.data(), data.size(), silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
// make sure ch->bev is valid every time before calling recv()
|
// make sure ch->bev is valid every time before calling recv()
|
||||||
while (ch->bev.get()) {
|
while (ch->bev.get()) {
|
||||||
Message msg;
|
Message msg;
|
||||||
try {
|
try {
|
||||||
msg = ch->recv();
|
msg = ch->recv();
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
break;
|
break;
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
channel_exceptions_log.warning("Error receiving on channel: %s", e.what());
|
channel_exceptions_log.warning("Error receiving on channel: %s", e.what());
|
||||||
ch->on_error(*ch, BEV_EVENT_ERROR);
|
ch->on_error(*ch, BEV_EVENT_ERROR);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch->on_command_received) {
|
if (ch->on_command_received) {
|
||||||
ch->on_command_received(*ch, msg.command, msg.flag, msg.data);
|
ch->on_command_received(*ch, msg.command, msg.flag, msg.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::dispatch_on_error(struct bufferevent*, short events, void* ctx) {
|
void Channel::dispatch_on_error(struct bufferevent*, short events, void* ctx) {
|
||||||
Channel* ch = reinterpret_cast<Channel*>(ctx);
|
Channel* ch = reinterpret_cast<Channel*>(ctx);
|
||||||
if (ch->on_error) {
|
if (ch->on_error) {
|
||||||
ch->on_error(*ch, events);
|
ch->on_error(*ch, events);
|
||||||
} else {
|
} else {
|
||||||
ch->disconnect();
|
ch->disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-103
@@ -1,103 +1,103 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#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;
|
||||||
struct sockaddr_storage remote_addr;
|
struct sockaddr_storage remote_addr;
|
||||||
uint64_t virtual_network_id; // 0 = normal TCP connection
|
uint64_t virtual_network_id; // 0 = normal TCP connection
|
||||||
|
|
||||||
Version version;
|
Version version;
|
||||||
uint8_t language;
|
uint8_t language;
|
||||||
std::shared_ptr<PSOEncryption> crypt_in;
|
std::shared_ptr<PSOEncryption> crypt_in;
|
||||||
std::shared_ptr<PSOEncryption> crypt_out;
|
std::shared_ptr<PSOEncryption> crypt_out;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
TerminalFormat terminal_send_color;
|
TerminalFormat terminal_send_color;
|
||||||
TerminalFormat terminal_recv_color;
|
TerminalFormat terminal_recv_color;
|
||||||
|
|
||||||
struct Message {
|
struct Message {
|
||||||
uint16_t command;
|
uint16_t command;
|
||||||
uint32_t flag;
|
uint32_t flag;
|
||||||
std::string data;
|
std::string data;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*on_command_received_t)(Channel&, uint16_t, uint32_t, std::string&);
|
typedef void (*on_command_received_t)(Channel&, uint16_t, uint32_t, std::string&);
|
||||||
typedef void (*on_error_t)(Channel&, short);
|
typedef void (*on_error_t)(Channel&, short);
|
||||||
|
|
||||||
on_command_received_t on_command_received;
|
on_command_received_t on_command_received;
|
||||||
on_error_t on_error;
|
on_error_t on_error;
|
||||||
void* context_obj;
|
void* context_obj;
|
||||||
|
|
||||||
// Creates an unconnected channel
|
// Creates an unconnected channel
|
||||||
Channel(
|
Channel(
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
on_command_received_t on_command_received,
|
on_command_received_t on_command_received,
|
||||||
on_error_t on_error,
|
on_error_t on_error,
|
||||||
void* context_obj,
|
void* context_obj,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
TerminalFormat terminal_send_color = TerminalFormat::END,
|
TerminalFormat terminal_send_color = TerminalFormat::END,
|
||||||
TerminalFormat terminal_recv_color = TerminalFormat::END);
|
TerminalFormat terminal_recv_color = TerminalFormat::END);
|
||||||
// Creates a connected channel
|
// Creates a connected channel
|
||||||
Channel(
|
Channel(
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
uint64_t virtual_network_id,
|
uint64_t virtual_network_id,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
on_command_received_t on_command_received,
|
on_command_received_t on_command_received,
|
||||||
on_error_t on_error,
|
on_error_t on_error,
|
||||||
void* context_obj,
|
void* context_obj,
|
||||||
const std::string& name = "",
|
const std::string& name = "",
|
||||||
TerminalFormat terminal_send_color = TerminalFormat::END,
|
TerminalFormat terminal_send_color = TerminalFormat::END,
|
||||||
TerminalFormat terminal_recv_color = TerminalFormat::END);
|
TerminalFormat terminal_recv_color = TerminalFormat::END);
|
||||||
Channel(const Channel& other) = delete;
|
Channel(const Channel& other) = delete;
|
||||||
Channel(Channel&& other) = delete;
|
Channel(Channel&& other) = delete;
|
||||||
Channel& operator=(const Channel& other) = delete;
|
Channel& operator=(const Channel& other) = delete;
|
||||||
Channel& operator=(Channel&& other) = delete;
|
Channel& operator=(Channel&& other) = delete;
|
||||||
|
|
||||||
void replace_with(
|
void replace_with(
|
||||||
Channel&& other,
|
Channel&& other,
|
||||||
on_command_received_t on_command_received,
|
on_command_received_t on_command_received,
|
||||||
on_error_t on_error,
|
on_error_t on_error,
|
||||||
void* context_obj,
|
void* context_obj,
|
||||||
const std::string& name = "");
|
const std::string& name = "");
|
||||||
|
|
||||||
void set_bufferevent(struct bufferevent* bev, uint64_t virtual_network_id);
|
void set_bufferevent(struct bufferevent* bev, uint64_t virtual_network_id);
|
||||||
|
|
||||||
inline bool connected() const {
|
inline bool connected() const {
|
||||||
return this->bev.get() != nullptr;
|
return this->bev.get() != nullptr;
|
||||||
}
|
}
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
// Receives a message. Throws std::out_of_range if no messages are available.
|
// Receives a message. Throws std::out_of_range if no messages are available.
|
||||||
Message recv();
|
Message recv();
|
||||||
|
|
||||||
// Sends a message with an automatically-constructed header.
|
// Sends a message with an automatically-constructed header.
|
||||||
void send(uint16_t cmd, uint32_t flag = 0, bool silent = false);
|
void send(uint16_t cmd, uint32_t flag = 0, bool silent = false);
|
||||||
void send(uint16_t cmd, uint32_t flag, const void* data, size_t size, bool silent = false);
|
void send(uint16_t cmd, uint32_t flag, const void* data, size_t size, bool silent = false);
|
||||||
void send(uint16_t cmd, uint32_t flag, const std::vector<std::pair<const void*, size_t>> blocks, bool silent = false);
|
void send(uint16_t cmd, uint32_t flag, const std::vector<std::pair<const void*, size_t>> blocks, bool silent = false);
|
||||||
void send(uint16_t cmd, uint32_t flag, const std::string& data, bool silent = false);
|
void send(uint16_t cmd, uint32_t flag, const std::string& data, bool silent = false);
|
||||||
template <typename CmdT>
|
template <typename CmdT>
|
||||||
requires(!std::is_pointer_v<CmdT>)
|
requires(!std::is_pointer_v<CmdT>)
|
||||||
void send(uint16_t cmd, uint32_t flag, const CmdT& data, bool silent = false) {
|
void send(uint16_t cmd, uint32_t flag, const CmdT& data, bool silent = false) {
|
||||||
this->send(cmd, flag, &data, sizeof(data), silent);
|
this->send(cmd, flag, &data, sizeof(data), silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sends a message with a pre-existing header (as the first few bytes in the
|
// Sends a message with a pre-existing header (as the first few bytes in the
|
||||||
// data)
|
// data)
|
||||||
void send(const void* data, size_t size, bool silent = false);
|
void send(const void* data, size_t size, bool silent = false);
|
||||||
void send(const std::string& data, bool silent = false);
|
void send(const std::string& data, bool silent = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void dispatch_on_input(struct bufferevent*, void* ctx);
|
static void dispatch_on_input(struct bufferevent*, void* ctx);
|
||||||
static void dispatch_on_error(struct bufferevent*, short events, void* ctx);
|
static void dispatch_on_error(struct bufferevent*, short events, void* ctx);
|
||||||
};
|
};
|
||||||
|
|||||||
+2603
-2603
File diff suppressed because it is too large
Load Diff
+14
-14
@@ -1,14 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
#include "Lobby.hh"
|
#include "Lobby.hh"
|
||||||
#include "ProxyServer.hh"
|
#include "ProxyServer.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
void on_chat_command(std::shared_ptr<Client> c, const std::string& text);
|
void on_chat_command(std::shared_ptr<Client> c, const std::string& text);
|
||||||
void on_chat_command(std::shared_ptr<ProxyServer::LinkedSession> ses, const std::string& text);
|
void on_chat_command(std::shared_ptr<ProxyServer::LinkedSession> ses, const std::string& text);
|
||||||
|
|||||||
+150
-150
@@ -1,150 +1,150 @@
|
|||||||
#include "ChoiceSearch.hh"
|
#include "ChoiceSearch.hh"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
|
const vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
|
||||||
ChoiceSearchCategory{
|
ChoiceSearchCategory{
|
||||||
.id = 0x0001,
|
.id = 0x0001,
|
||||||
.name = "Level",
|
.name = "Level",
|
||||||
.choices = {
|
.choices = {
|
||||||
{0x0000, "Any"},
|
{0x0000, "Any"},
|
||||||
{0x0001, "Own level +/- 5"},
|
{0x0001, "Own level +/- 5"},
|
||||||
{0x0002, "Level 1-10"},
|
{0x0002, "Level 1-10"},
|
||||||
{0x0003, "Level 11-20"},
|
{0x0003, "Level 11-20"},
|
||||||
{0x0004, "Level 21-40"},
|
{0x0004, "Level 21-40"},
|
||||||
{0x0005, "Level 41-60"},
|
{0x0005, "Level 41-60"},
|
||||||
{0x0006, "Level 61-80"},
|
{0x0006, "Level 61-80"},
|
||||||
{0x0007, "Level 81-100"},
|
{0x0007, "Level 81-100"},
|
||||||
{0x0008, "Level 101-120"},
|
{0x0008, "Level 101-120"},
|
||||||
{0x0009, "Level 121-160"},
|
{0x0009, "Level 121-160"},
|
||||||
{0x000A, "Level 161-200"},
|
{0x000A, "Level 161-200"},
|
||||||
},
|
},
|
||||||
.client_matches = +[](shared_ptr<Client> searcher_c, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
|
.client_matches = +[](shared_ptr<Client> searcher_c, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
|
||||||
if (choice_id == 0x0000) {
|
if (choice_id == 0x0000) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
uint32_t target_level = target_c->character()->disp.stats.level + 1;
|
uint32_t target_level = target_c->character()->disp.stats.level + 1;
|
||||||
switch (choice_id) {
|
switch (choice_id) {
|
||||||
case 0x0001:
|
case 0x0001:
|
||||||
return (labs(static_cast<int32_t>(target_level - searcher_c->character()->disp.stats.level)) <= 5);
|
return (labs(static_cast<int32_t>(target_level - searcher_c->character()->disp.stats.level)) <= 5);
|
||||||
case 0x0002:
|
case 0x0002:
|
||||||
return (target_level <= 10);
|
return (target_level <= 10);
|
||||||
case 0x0003:
|
case 0x0003:
|
||||||
return (target_level > 10) && (target_level <= 20);
|
return (target_level > 10) && (target_level <= 20);
|
||||||
case 0x0004:
|
case 0x0004:
|
||||||
return (target_level > 20) && (target_level <= 40);
|
return (target_level > 20) && (target_level <= 40);
|
||||||
case 0x0005:
|
case 0x0005:
|
||||||
return (target_level > 40) && (target_level <= 60);
|
return (target_level > 40) && (target_level <= 60);
|
||||||
case 0x0006:
|
case 0x0006:
|
||||||
return (target_level > 60) && (target_level <= 80);
|
return (target_level > 60) && (target_level <= 80);
|
||||||
case 0x0007:
|
case 0x0007:
|
||||||
return (target_level > 80) && (target_level <= 100);
|
return (target_level > 80) && (target_level <= 100);
|
||||||
case 0x0008:
|
case 0x0008:
|
||||||
return (target_level > 100) && (target_level <= 120);
|
return (target_level > 100) && (target_level <= 120);
|
||||||
case 0x0009:
|
case 0x0009:
|
||||||
return (target_level > 120) && (target_level <= 160);
|
return (target_level > 120) && (target_level <= 160);
|
||||||
case 0x000A:
|
case 0x000A:
|
||||||
return (target_level > 160) && (target_level <= 200);
|
return (target_level > 160) && (target_level <= 200);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ChoiceSearchCategory{
|
ChoiceSearchCategory{
|
||||||
.id = 0x0002,
|
.id = 0x0002,
|
||||||
.name = "Class",
|
.name = "Class",
|
||||||
.choices = {
|
.choices = {
|
||||||
{0x0000, "Any"},
|
{0x0000, "Any"},
|
||||||
{0x0010, "Hunter"},
|
{0x0010, "Hunter"},
|
||||||
{0x0001, "HUmar"},
|
{0x0001, "HUmar"},
|
||||||
{0x0002, "HUnewearl"},
|
{0x0002, "HUnewearl"},
|
||||||
{0x0003, "HUcast"},
|
{0x0003, "HUcast"},
|
||||||
{0x000A, "HUcaseal"},
|
{0x000A, "HUcaseal"},
|
||||||
{0x0011, "Ranger"},
|
{0x0011, "Ranger"},
|
||||||
{0x0004, "RAmar"},
|
{0x0004, "RAmar"},
|
||||||
{0x000C, "RAmarl"},
|
{0x000C, "RAmarl"},
|
||||||
{0x0005, "RAcast"},
|
{0x0005, "RAcast"},
|
||||||
{0x0006, "RAcaseal"},
|
{0x0006, "RAcaseal"},
|
||||||
{0x0012, "Force"},
|
{0x0012, "Force"},
|
||||||
{0x000B, "FOmar"},
|
{0x000B, "FOmar"},
|
||||||
{0x0007, "FOmarl"},
|
{0x0007, "FOmarl"},
|
||||||
{0x0008, "FOnewm"},
|
{0x0008, "FOnewm"},
|
||||||
{0x0009, "FOnewearl"},
|
{0x0009, "FOnewearl"},
|
||||||
},
|
},
|
||||||
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
|
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
|
||||||
switch (choice_id) {
|
switch (choice_id) {
|
||||||
case 0x0000:
|
case 0x0000:
|
||||||
return true;
|
return true;
|
||||||
case 0x0010:
|
case 0x0010:
|
||||||
return target_c->character()->disp.visual.class_flags & 0x20;
|
return target_c->character()->disp.visual.class_flags & 0x20;
|
||||||
case 0x0011:
|
case 0x0011:
|
||||||
return target_c->character()->disp.visual.class_flags & 0x40;
|
return target_c->character()->disp.visual.class_flags & 0x40;
|
||||||
case 0x0012:
|
case 0x0012:
|
||||||
return target_c->character()->disp.visual.class_flags & 0x80;
|
return target_c->character()->disp.visual.class_flags & 0x80;
|
||||||
default:
|
default:
|
||||||
return ((choice_id - 1) == target_c->character()->disp.visual.char_class);
|
return ((choice_id - 1) == target_c->character()->disp.visual.char_class);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ChoiceSearchCategory{
|
ChoiceSearchCategory{
|
||||||
.id = 0x0003,
|
.id = 0x0003,
|
||||||
.name = "Platform",
|
.name = "Platform",
|
||||||
.choices = {
|
.choices = {
|
||||||
{0x0000, "Any"},
|
{0x0000, "Any"},
|
||||||
{0x0001, "DC betas"},
|
{0x0001, "DC betas"},
|
||||||
{0x0002, "DC V1"},
|
{0x0002, "DC V1"},
|
||||||
{0x0003, "DC V2 / PC"},
|
{0x0003, "DC V2 / PC"},
|
||||||
{0x0004, "GC / Xbox Episodes 1&2"},
|
{0x0004, "GC / Xbox Episodes 1&2"},
|
||||||
{0x0005, "GC Episode 3"},
|
{0x0005, "GC Episode 3"},
|
||||||
{0x0006, "BB"},
|
{0x0006, "BB"},
|
||||||
},
|
},
|
||||||
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
|
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
|
||||||
if (choice_id == 0x0000) {
|
if (choice_id == 0x0000) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
switch (target_c->version()) {
|
switch (target_c->version()) {
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
return (choice_id == 0x0001);
|
return (choice_id == 0x0001);
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
return (choice_id == 0x0002);
|
return (choice_id == 0x0002);
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return (choice_id == 0x0003);
|
return (choice_id == 0x0003);
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return (choice_id == 0x0004);
|
return (choice_id == 0x0004);
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return (choice_id == 0x0005);
|
return (choice_id == 0x0005);
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return (choice_id == 0x0006);
|
return (choice_id == 0x0006);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ChoiceSearchCategory{
|
ChoiceSearchCategory{
|
||||||
.id = 0x0204,
|
.id = 0x0204,
|
||||||
.name = "Game mode",
|
.name = "Game mode",
|
||||||
.choices = {
|
.choices = {
|
||||||
{0x0000, "Any"},
|
{0x0000, "Any"},
|
||||||
{0x0001, "Normal"},
|
{0x0001, "Normal"},
|
||||||
{0x0002, "Hard"},
|
{0x0002, "Hard"},
|
||||||
{0x0003, "Very Hard"},
|
{0x0003, "Very Hard"},
|
||||||
{0x0004, "Ultimate"},
|
{0x0004, "Ultimate"},
|
||||||
{0x0005, "Battle"},
|
{0x0005, "Battle"},
|
||||||
{0x0006, "Challenge"},
|
{0x0006, "Challenge"},
|
||||||
},
|
},
|
||||||
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
|
.client_matches = +[](shared_ptr<Client>, shared_ptr<Client> target_c, uint16_t choice_id) -> bool {
|
||||||
uint16_t target_choice_id = target_c->character()->choice_search_config.get_setting(0x0204);
|
uint16_t target_choice_id = target_c->character()->choice_search_config.get_setting(0x0204);
|
||||||
return (choice_id == 0) || (target_choice_id == 0) || (choice_id == target_choice_id);
|
return (choice_id == 0) || (target_choice_id == 0) || (choice_id == target_choice_id);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
+64
-64
@@ -1,64 +1,64 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
struct ChoiceSearchConfigT {
|
struct ChoiceSearchConfigT {
|
||||||
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
|
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
|
||||||
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;
|
||||||
|
|
||||||
U32T disabled = 1; // 0 = enabled, 1 = disabled. Unused in command C3
|
U32T disabled = 1; // 0 = enabled, 1 = disabled. Unused in command C3
|
||||||
struct Entry {
|
struct Entry {
|
||||||
U16T parent_choice_id = 0;
|
U16T parent_choice_id = 0;
|
||||||
U16T choice_id = 0;
|
U16T choice_id = 0;
|
||||||
} __packed_ws__(Entry, 4);
|
} __packed_ws__(Entry, 4);
|
||||||
parray<Entry, 5> entries;
|
parray<Entry, 5> entries;
|
||||||
|
|
||||||
int32_t get_setting(uint16_t parent_choice_id) const {
|
int32_t get_setting(uint16_t parent_choice_id) const {
|
||||||
for (size_t z = 0; z < this->entries.size(); z++) {
|
for (size_t z = 0; z < this->entries.size(); z++) {
|
||||||
if (this->entries[z].parent_choice_id == parent_choice_id) {
|
if (this->entries[z].parent_choice_id == parent_choice_id) {
|
||||||
return this->entries[z].choice_id;
|
return this->entries[z].choice_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator ChoiceSearchConfigT<!IsBigEndian>() const {
|
operator ChoiceSearchConfigT<!IsBigEndian>() const {
|
||||||
ChoiceSearchConfigT<!IsBigEndian> ret;
|
ChoiceSearchConfigT<!IsBigEndian> ret;
|
||||||
ret.disabled = this->disabled.load();
|
ret.disabled = this->disabled.load();
|
||||||
for (size_t z = 0; z < this->entries.size(); z++) {
|
for (size_t z = 0; z < this->entries.size(); z++) {
|
||||||
auto& ret_e = ret.entries[z];
|
auto& ret_e = ret.entries[z];
|
||||||
const auto& this_e = this->entries[z];
|
const auto& this_e = this->entries[z];
|
||||||
ret_e.parent_choice_id = this_e.parent_choice_id.load();
|
ret_e.parent_choice_id = this_e.parent_choice_id.load();
|
||||||
ret_e.choice_id = this_e.choice_id.load();
|
ret_e.choice_id = this_e.choice_id.load();
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed__;
|
} __packed__;
|
||||||
|
|
||||||
using ChoiceSearchConfig = ChoiceSearchConfigT<false>;
|
using ChoiceSearchConfig = ChoiceSearchConfigT<false>;
|
||||||
using ChoiceSearchConfigBE = ChoiceSearchConfigT<true>;
|
using ChoiceSearchConfigBE = ChoiceSearchConfigT<true>;
|
||||||
check_struct_size(ChoiceSearchConfig, 0x18);
|
check_struct_size(ChoiceSearchConfig, 0x18);
|
||||||
check_struct_size(ChoiceSearchConfigBE, 0x18);
|
check_struct_size(ChoiceSearchConfigBE, 0x18);
|
||||||
|
|
||||||
struct ChoiceSearchCategory {
|
struct ChoiceSearchCategory {
|
||||||
struct Choice {
|
struct Choice {
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
const char* name;
|
const char* name;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
const char* name;
|
const char* name;
|
||||||
std::vector<Choice> choices;
|
std::vector<Choice> choices;
|
||||||
std::function<bool(std::shared_ptr<Client> searcher_c, std::shared_ptr<Client> target_c, uint16_t choice_id)> client_matches;
|
std::function<bool(std::shared_ptr<Client> searcher_c, std::shared_ptr<Client> target_c, uint16_t choice_id)> client_matches;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const std::vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES;
|
extern const std::vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES;
|
||||||
|
|||||||
+1120
-1120
File diff suppressed because it is too large
Load Diff
+412
-412
@@ -1,412 +1,412 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "Account.hh"
|
#include "Account.hh"
|
||||||
#include "Channel.hh"
|
#include "Channel.hh"
|
||||||
#include "CommandFormats.hh"
|
#include "CommandFormats.hh"
|
||||||
#include "Episode3/BattleRecord.hh"
|
#include "Episode3/BattleRecord.hh"
|
||||||
#include "Episode3/Tournament.hh"
|
#include "Episode3/Tournament.hh"
|
||||||
#include "FileContentsCache.hh"
|
#include "FileContentsCache.hh"
|
||||||
#include "FunctionCompiler.hh"
|
#include "FunctionCompiler.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
#include "PatchFileIndex.hh"
|
#include "PatchFileIndex.hh"
|
||||||
#include "Quest.hh"
|
#include "Quest.hh"
|
||||||
#include "QuestScript.hh"
|
#include "QuestScript.hh"
|
||||||
#include "TeamIndex.hh"
|
#include "TeamIndex.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
extern const uint64_t CLIENT_CONFIG_MAGIC;
|
extern const uint64_t CLIENT_CONFIG_MAGIC;
|
||||||
|
|
||||||
class Server;
|
class Server;
|
||||||
struct Lobby;
|
struct Lobby;
|
||||||
class Parsed6x70Data;
|
class Parsed6x70Data;
|
||||||
|
|
||||||
class Client : public std::enable_shared_from_this<Client> {
|
class Client : public std::enable_shared_from_this<Client> {
|
||||||
public:
|
public:
|
||||||
enum class Flag : uint64_t {
|
enum class Flag : uint64_t {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
||||||
// This mask specifies which flags are sent to the client
|
// This mask specifies which flags are sent to the client
|
||||||
// TODO: It'd be nice to use a pattern here (e.g. all server-side flags are
|
// TODO: It'd be nice to use a pattern here (e.g. all server-side flags are
|
||||||
// in the high bits) but that would require re-recording or manually
|
// in the high bits) but that would require re-recording or manually
|
||||||
// rewriting all the tests
|
// rewriting all the tests
|
||||||
CLIENT_SIDE_MASK = 0xFF3CFFFF7C0FFFFB,
|
CLIENT_SIDE_MASK = 0xFF3CFFFF7C0FFFFB,
|
||||||
|
|
||||||
// Version-related flags
|
// Version-related flags
|
||||||
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
|
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
|
||||||
NO_D6_AFTER_LOBBY = 0x0000000000000100,
|
NO_D6_AFTER_LOBBY = 0x0000000000000100,
|
||||||
NO_D6 = 0x0000000000000200,
|
NO_D6 = 0x0000000000000200,
|
||||||
FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400,
|
FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400,
|
||||||
|
|
||||||
// Flags describing the behavior for send_function_call
|
// Flags describing the behavior for send_function_call
|
||||||
NO_SEND_FUNCTION_CALL = 0x0000000000001000,
|
NO_SEND_FUNCTION_CALL = 0x0000000000001000,
|
||||||
ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000,
|
ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000,
|
||||||
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000,
|
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000,
|
||||||
SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000,
|
SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000,
|
||||||
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000,
|
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000,
|
||||||
|
|
||||||
// State flags
|
// State flags
|
||||||
LOADING = 0x0000000000100000, // Server-side only
|
LOADING = 0x0000000000100000, // Server-side only
|
||||||
LOADING_QUEST = 0x0000000000200000, // Server-side only
|
LOADING_QUEST = 0x0000000000200000, // Server-side only
|
||||||
LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000400000, // Server-side only
|
LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000400000, // Server-side only
|
||||||
LOADING_TOURNAMENT = 0x0000000000800000, // Server-side only
|
LOADING_TOURNAMENT = 0x0000000000800000, // Server-side only
|
||||||
IN_INFORMATION_MENU = 0x0000000001000000, // Server-side only
|
IN_INFORMATION_MENU = 0x0000000001000000, // Server-side only
|
||||||
AT_WELCOME_MESSAGE = 0x0000000002000000, // Server-side only
|
AT_WELCOME_MESSAGE = 0x0000000002000000, // Server-side only
|
||||||
SAVE_ENABLED = 0x0000000004000000,
|
SAVE_ENABLED = 0x0000000004000000,
|
||||||
HAS_EP3_CARD_DEFS = 0x0000000008000000,
|
HAS_EP3_CARD_DEFS = 0x0000000008000000,
|
||||||
HAS_EP3_MEDIA_UPDATES = 0x0000000010000000,
|
HAS_EP3_MEDIA_UPDATES = 0x0000000010000000,
|
||||||
USE_OVERRIDE_RANDOM_SEED = 0x0000000020000000,
|
USE_OVERRIDE_RANDOM_SEED = 0x0000000020000000,
|
||||||
HAS_GUILD_CARD_NUMBER = 0x0000000040000000,
|
HAS_GUILD_CARD_NUMBER = 0x0000000040000000,
|
||||||
HAS_AUTO_PATCHES = 0x0000004000000000,
|
HAS_AUTO_PATCHES = 0x0000004000000000,
|
||||||
AT_BANK_COUNTER = 0x0000000080000000, // Server-side only
|
AT_BANK_COUNTER = 0x0000000080000000, // Server-side only
|
||||||
SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0001000000000000, // Server-side only
|
SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0001000000000000, // Server-side only
|
||||||
SHOULD_SEND_ARTIFICIAL_ENEMY_AND_SET_STATE = 0x0040000000000000, // Server-side only
|
SHOULD_SEND_ARTIFICIAL_ENEMY_AND_SET_STATE = 0x0040000000000000, // Server-side only
|
||||||
SHOULD_SEND_ARTIFICIAL_OBJECT_STATE = 0x0080000000000000, // Server-side only
|
SHOULD_SEND_ARTIFICIAL_OBJECT_STATE = 0x0080000000000000, // Server-side only
|
||||||
SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0002000000000000, // Server-side only
|
SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0002000000000000, // Server-side only
|
||||||
SHOULD_SEND_ARTIFICIAL_PLAYER_STATES = 0x0200000000000000, // Server-side only
|
SHOULD_SEND_ARTIFICIAL_PLAYER_STATES = 0x0200000000000000, // Server-side only
|
||||||
SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000,
|
SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000,
|
||||||
SWITCH_ASSIST_ENABLED = 0x0000000100000000,
|
SWITCH_ASSIST_ENABLED = 0x0000000100000000,
|
||||||
IS_CLIENT_CUSTOMIZATION = 0x0100000000000000,
|
IS_CLIENT_CUSTOMIZATION = 0x0100000000000000,
|
||||||
|
|
||||||
// Cheat mode and option flags
|
// Cheat mode and option flags
|
||||||
INFINITE_HP_ENABLED = 0x0000000200000000,
|
INFINITE_HP_ENABLED = 0x0000000200000000,
|
||||||
INFINITE_TP_ENABLED = 0x0000000400000000,
|
INFINITE_TP_ENABLED = 0x0000000400000000,
|
||||||
DEBUG_ENABLED = 0x0000000800000000,
|
DEBUG_ENABLED = 0x0000000800000000,
|
||||||
ITEM_DROP_NOTIFICATIONS_1 = 0x0010000000000000,
|
ITEM_DROP_NOTIFICATIONS_1 = 0x0010000000000000,
|
||||||
ITEM_DROP_NOTIFICATIONS_2 = 0x0020000000000000,
|
ITEM_DROP_NOTIFICATIONS_2 = 0x0020000000000000,
|
||||||
|
|
||||||
// Proxy option flags
|
// Proxy option flags
|
||||||
PROXY_SAVE_FILES = 0x0000001000000000,
|
PROXY_SAVE_FILES = 0x0000001000000000,
|
||||||
PROXY_CHAT_COMMANDS_ENABLED = 0x0000002000000000,
|
PROXY_CHAT_COMMANDS_ENABLED = 0x0000002000000000,
|
||||||
PROXY_PLAYER_NOTIFICATIONS_ENABLED = 0x0000008000000000,
|
PROXY_PLAYER_NOTIFICATIONS_ENABLED = 0x0000008000000000,
|
||||||
PROXY_SUPPRESS_CLIENT_PINGS = 0x0000010000000000,
|
PROXY_SUPPRESS_CLIENT_PINGS = 0x0000010000000000,
|
||||||
PROXY_SUPPRESS_REMOTE_LOGIN = 0x0000020000000000,
|
PROXY_SUPPRESS_REMOTE_LOGIN = 0x0000020000000000,
|
||||||
PROXY_ZERO_REMOTE_GUILD_CARD = 0x0000040000000000,
|
PROXY_ZERO_REMOTE_GUILD_CARD = 0x0000040000000000,
|
||||||
PROXY_EP3_INFINITE_MESETA_ENABLED = 0x0000080000000000,
|
PROXY_EP3_INFINITE_MESETA_ENABLED = 0x0000080000000000,
|
||||||
PROXY_EP3_INFINITE_TIME_ENABLED = 0x0000100000000000,
|
PROXY_EP3_INFINITE_TIME_ENABLED = 0x0000100000000000,
|
||||||
PROXY_RED_NAME_ENABLED = 0x0000200000000000,
|
PROXY_RED_NAME_ENABLED = 0x0000200000000000,
|
||||||
PROXY_BLANK_NAME_ENABLED = 0x0000400000000000,
|
PROXY_BLANK_NAME_ENABLED = 0x0000400000000000,
|
||||||
PROXY_BLOCK_FUNCTION_CALLS = 0x0000800000000000,
|
PROXY_BLOCK_FUNCTION_CALLS = 0x0000800000000000,
|
||||||
PROXY_EP3_UNMASK_WHISPERS = 0x0008000000000000,
|
PROXY_EP3_UNMASK_WHISPERS = 0x0008000000000000,
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
enum class ItemDropNotificationMode {
|
enum class ItemDropNotificationMode {
|
||||||
NOTHING = 0,
|
NOTHING = 0,
|
||||||
RARES_ONLY = 1,
|
RARES_ONLY = 1,
|
||||||
ALL_ITEMS = 2,
|
ALL_ITEMS = 2,
|
||||||
ALL_ITEMS_INCLUDING_MESETA = 3,
|
ALL_ITEMS_INCLUDING_MESETA = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr uint64_t DEFAULT_FLAGS = static_cast<uint64_t>(Flag::PROXY_CHAT_COMMANDS_ENABLED);
|
static constexpr uint64_t DEFAULT_FLAGS = static_cast<uint64_t>(Flag::PROXY_CHAT_COMMANDS_ENABLED);
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
uint64_t enabled_flags = DEFAULT_FLAGS; // Client::Flag enum
|
uint64_t enabled_flags = DEFAULT_FLAGS; // Client::Flag enum
|
||||||
uint32_t specific_version = 0;
|
uint32_t specific_version = 0;
|
||||||
int32_t override_random_seed = 0;
|
int32_t override_random_seed = 0;
|
||||||
uint8_t override_section_id = 0xFF; // FF = no override
|
uint8_t override_section_id = 0xFF; // FF = no override
|
||||||
uint8_t override_lobby_event = 0xFF; // FF = no override
|
uint8_t override_lobby_event = 0xFF; // FF = no override
|
||||||
uint8_t override_lobby_number = 0x80; // 80 = no override
|
uint8_t override_lobby_number = 0x80; // 80 = no override
|
||||||
uint32_t proxy_destination_address = 0;
|
uint32_t proxy_destination_address = 0;
|
||||||
uint16_t proxy_destination_port = 0;
|
uint16_t proxy_destination_port = 0;
|
||||||
|
|
||||||
Config() = default;
|
Config() = default;
|
||||||
|
|
||||||
bool operator==(const Config& other) const = default;
|
bool operator==(const Config& other) const = default;
|
||||||
bool operator!=(const Config& other) const = default;
|
bool operator!=(const Config& other) const = default;
|
||||||
|
|
||||||
bool should_update_vs(const Config& other) const;
|
bool should_update_vs(const Config& other) const;
|
||||||
|
|
||||||
[[nodiscard]] static inline bool check_flag(uint64_t enabled_flags, Flag flag) {
|
[[nodiscard]] static inline bool check_flag(uint64_t enabled_flags, Flag flag) {
|
||||||
return !!(enabled_flags & static_cast<uint64_t>(flag));
|
return !!(enabled_flags & static_cast<uint64_t>(flag));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
||||||
return this->check_flag(this->enabled_flags, flag);
|
return this->check_flag(this->enabled_flags, flag);
|
||||||
}
|
}
|
||||||
inline void set_flag(Flag flag) {
|
inline void set_flag(Flag flag) {
|
||||||
this->enabled_flags |= static_cast<uint64_t>(flag);
|
this->enabled_flags |= static_cast<uint64_t>(flag);
|
||||||
}
|
}
|
||||||
inline void clear_flag(Flag flag) {
|
inline void clear_flag(Flag flag) {
|
||||||
this->enabled_flags &= (~static_cast<uint64_t>(flag));
|
this->enabled_flags &= (~static_cast<uint64_t>(flag));
|
||||||
}
|
}
|
||||||
inline void toggle_flag(Flag flag) {
|
inline void toggle_flag(Flag flag) {
|
||||||
this->enabled_flags ^= static_cast<uint64_t>(flag);
|
this->enabled_flags ^= static_cast<uint64_t>(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_flags_for_version(Version version, int64_t sub_version);
|
void set_flags_for_version(Version version, int64_t sub_version);
|
||||||
|
|
||||||
ItemDropNotificationMode get_drop_notification_mode() const;
|
ItemDropNotificationMode get_drop_notification_mode() const;
|
||||||
void set_drop_notification_mode(ItemDropNotificationMode new_mode);
|
void set_drop_notification_mode(ItemDropNotificationMode new_mode);
|
||||||
|
|
||||||
template <size_t Bytes>
|
template <size_t Bytes>
|
||||||
void parse_from(const parray<uint8_t, Bytes>& data) {
|
void parse_from(const parray<uint8_t, Bytes>& data) {
|
||||||
StringReader r(data.data(), data.size());
|
StringReader r(data.data(), data.size());
|
||||||
if (r.get_u32l() != CLIENT_CONFIG_MAGIC) {
|
if (r.get_u32l() != CLIENT_CONFIG_MAGIC) {
|
||||||
throw std::invalid_argument("config signature is incorrect");
|
throw std::invalid_argument("config signature is incorrect");
|
||||||
}
|
}
|
||||||
this->specific_version = r.get_u32l();
|
this->specific_version = r.get_u32l();
|
||||||
this->enabled_flags = r.get_u64l();
|
this->enabled_flags = r.get_u64l();
|
||||||
this->override_random_seed = r.get_u32l();
|
this->override_random_seed = r.get_u32l();
|
||||||
this->proxy_destination_address = r.get_u32b();
|
this->proxy_destination_address = r.get_u32b();
|
||||||
this->proxy_destination_port = r.get_u16l();
|
this->proxy_destination_port = r.get_u16l();
|
||||||
this->override_section_id = r.get_u8();
|
this->override_section_id = r.get_u8();
|
||||||
this->override_lobby_event = r.get_u8();
|
this->override_lobby_event = r.get_u8();
|
||||||
this->override_lobby_number = r.get_u8();
|
this->override_lobby_number = r.get_u8();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t Bytes>
|
template <size_t Bytes>
|
||||||
void serialize_into(parray<uint8_t, Bytes>& data) const {
|
void serialize_into(parray<uint8_t, Bytes>& data) const {
|
||||||
StringWriter w;
|
StringWriter w;
|
||||||
w.put_u32l(CLIENT_CONFIG_MAGIC);
|
w.put_u32l(CLIENT_CONFIG_MAGIC);
|
||||||
w.put_u32l(this->specific_version);
|
w.put_u32l(this->specific_version);
|
||||||
w.put_u64l(this->enabled_flags & static_cast<uint64_t>(Flag::CLIENT_SIDE_MASK));
|
w.put_u64l(this->enabled_flags & static_cast<uint64_t>(Flag::CLIENT_SIDE_MASK));
|
||||||
w.put_u32l(this->override_random_seed);
|
w.put_u32l(this->override_random_seed);
|
||||||
w.put_u32b(this->proxy_destination_address);
|
w.put_u32b(this->proxy_destination_address);
|
||||||
w.put_u16l(this->proxy_destination_port);
|
w.put_u16l(this->proxy_destination_port);
|
||||||
w.put_u8(this->override_section_id);
|
w.put_u8(this->override_section_id);
|
||||||
w.put_u8(this->override_lobby_event);
|
w.put_u8(this->override_lobby_event);
|
||||||
w.put_u8(this->override_lobby_number);
|
w.put_u8(this->override_lobby_number);
|
||||||
|
|
||||||
const auto& s = w.str();
|
const auto& s = w.str();
|
||||||
for (size_t z = 0; z < s.size(); z++) {
|
for (size_t z = 0; z < s.size(); z++) {
|
||||||
data[z] = s[z];
|
data[z] = s[z];
|
||||||
}
|
}
|
||||||
data.clear_after(s.size(), 0xFF);
|
data.clear_after(s.size(), 0xFF);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::weak_ptr<Server> server;
|
std::weak_ptr<Server> server;
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
PrefixedLogger log;
|
PrefixedLogger log;
|
||||||
|
|
||||||
std::shared_ptr<Login> login;
|
std::shared_ptr<Login> login;
|
||||||
|
|
||||||
// Network
|
// Network
|
||||||
Channel channel;
|
Channel channel;
|
||||||
struct sockaddr_storage next_connection_addr;
|
struct sockaddr_storage next_connection_addr;
|
||||||
ServerBehavior server_behavior;
|
ServerBehavior server_behavior;
|
||||||
bool should_disconnect;
|
bool should_disconnect;
|
||||||
bool should_send_to_lobby_server;
|
bool should_send_to_lobby_server;
|
||||||
bool should_send_to_proxy_server;
|
bool should_send_to_proxy_server;
|
||||||
std::unordered_map<std::string, std::function<void()>> disconnect_hooks;
|
std::unordered_map<std::string, std::function<void()>> disconnect_hooks;
|
||||||
std::shared_ptr<XBNetworkLocation> xb_netloc;
|
std::shared_ptr<XBNetworkLocation> xb_netloc;
|
||||||
parray<le_uint32_t, 3> xb_9E_unknown_a1a;
|
parray<le_uint32_t, 3> xb_9E_unknown_a1a;
|
||||||
uint8_t bb_connection_phase;
|
uint8_t bb_connection_phase;
|
||||||
uint64_t ping_start_time;
|
uint64_t ping_start_time;
|
||||||
|
|
||||||
// Lobby/positioning
|
// Lobby/positioning
|
||||||
Config config;
|
Config config;
|
||||||
Config synced_config;
|
Config synced_config;
|
||||||
std::unique_ptr<parray<le_uint32_t, 0x20>> override_variations;
|
std::unique_ptr<parray<le_uint32_t, 0x20>> override_variations;
|
||||||
int32_t sub_version;
|
int32_t sub_version;
|
||||||
float x;
|
float x;
|
||||||
float z;
|
float z;
|
||||||
uint32_t floor;
|
uint32_t floor;
|
||||||
std::weak_ptr<Lobby> lobby;
|
std::weak_ptr<Lobby> lobby;
|
||||||
uint8_t lobby_client_id;
|
uint8_t lobby_client_id;
|
||||||
uint8_t lobby_arrow_color;
|
uint8_t lobby_arrow_color;
|
||||||
int64_t preferred_lobby_id; // <0 = no preference
|
int64_t preferred_lobby_id; // <0 = no preference
|
||||||
|
|
||||||
std::unique_ptr<struct event, void (*)(struct event*)> save_game_data_event;
|
std::unique_ptr<struct event, void (*)(struct event*)> save_game_data_event;
|
||||||
std::unique_ptr<struct event, void (*)(struct event*)> send_ping_event;
|
std::unique_ptr<struct event, void (*)(struct event*)> send_ping_event;
|
||||||
std::unique_ptr<struct event, void (*)(struct event*)> idle_timeout_event;
|
std::unique_ptr<struct event, void (*)(struct event*)> idle_timeout_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;
|
||||||
std::weak_ptr<Episode3::Tournament::Team> ep3_tournament_team;
|
std::weak_ptr<Episode3::Tournament::Team> ep3_tournament_team;
|
||||||
std::shared_ptr<const Episode3::BattleRecord> ep3_prev_battle_record;
|
std::shared_ptr<const Episode3::BattleRecord> ep3_prev_battle_record;
|
||||||
std::shared_ptr<const Menu> last_menu_sent;
|
std::shared_ptr<const Menu> last_menu_sent;
|
||||||
uint32_t last_game_info_requested;
|
uint32_t last_game_info_requested;
|
||||||
struct JoinCommand {
|
struct JoinCommand {
|
||||||
uint16_t command;
|
uint16_t command;
|
||||||
uint32_t flag;
|
uint32_t flag;
|
||||||
std::string data;
|
std::string data;
|
||||||
};
|
};
|
||||||
std::unique_ptr<std::deque<JoinCommand>> game_join_command_queue;
|
std::unique_ptr<std::deque<JoinCommand>> game_join_command_queue;
|
||||||
|
|
||||||
// Character / game data
|
// Character / game data
|
||||||
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
|
||||||
std::vector<ItemData> items;
|
std::vector<ItemData> items;
|
||||||
};
|
};
|
||||||
struct PendingCardTrade {
|
struct PendingCardTrade {
|
||||||
uint8_t other_client_id;
|
uint8_t other_client_id;
|
||||||
bool confirmed; // true if client has sent an EE D2 command
|
bool confirmed; // true if client has sent an EE D2 command
|
||||||
std::vector<std::pair<uint32_t, uint32_t>> card_to_count;
|
std::vector<std::pair<uint32_t, uint32_t>> card_to_count;
|
||||||
};
|
};
|
||||||
bool should_update_play_time;
|
bool should_update_play_time;
|
||||||
std::unordered_set<uint32_t> blocked_senders;
|
std::unordered_set<uint32_t> blocked_senders;
|
||||||
std::unique_ptr<PlayerDispDataDCPCV3> v1_v2_last_reported_disp;
|
std::unique_ptr<PlayerDispDataDCPCV3> v1_v2_last_reported_disp;
|
||||||
std::shared_ptr<Parsed6x70Data> last_reported_6x70;
|
std::shared_ptr<Parsed6x70Data> last_reported_6x70;
|
||||||
// These are null unless the client is within the trade sequence (D0-D4 or EE commands)
|
// These are null unless the client is within the trade sequence (D0-D4 or EE commands)
|
||||||
std::unique_ptr<PendingItemTrade> pending_item_trade;
|
std::unique_ptr<PendingItemTrade> pending_item_trade;
|
||||||
std::unique_ptr<PendingCardTrade> pending_card_trade;
|
std::unique_ptr<PendingCardTrade> pending_card_trade;
|
||||||
uint32_t telepipe_lobby_id;
|
uint32_t telepipe_lobby_id;
|
||||||
G_SetTelepipeState_6x68 telepipe_state;
|
G_SetTelepipeState_6x68 telepipe_state;
|
||||||
std::shared_ptr<Episode3::PlayerConfig> ep3_config; // Null for non-Ep3
|
std::shared_ptr<Episode3::PlayerConfig> ep3_config; // Null for non-Ep3
|
||||||
int8_t bb_character_index;
|
int8_t bb_character_index;
|
||||||
ItemData bb_identify_result;
|
ItemData bb_identify_result;
|
||||||
std::array<std::vector<ItemData>, 3> bb_shop_contents;
|
std::array<std::vector<ItemData>, 3> bb_shop_contents;
|
||||||
|
|
||||||
// Miscellaneous (used by chat commands)
|
// Miscellaneous (used by chat commands)
|
||||||
uint32_t next_exp_value; // next EXP value to give
|
uint32_t next_exp_value; // next EXP value to give
|
||||||
RecentSwitchFlags recent_switch_flags; // used for switch assist
|
RecentSwitchFlags recent_switch_flags; // used for switch assist
|
||||||
bool can_chat;
|
bool can_chat;
|
||||||
struct PendingCharacterExport {
|
struct PendingCharacterExport {
|
||||||
std::shared_ptr<const Account> dest_account;
|
std::shared_ptr<const Account> dest_account;
|
||||||
ssize_t character_index = -1;
|
ssize_t character_index = -1;
|
||||||
std::shared_ptr<const BBLicense> dest_bb_license; // Only used for $bbchar; null for $savechar
|
std::shared_ptr<const BBLicense> dest_bb_license; // Only used for $bbchar; null for $savechar
|
||||||
};
|
};
|
||||||
std::unique_ptr<PendingCharacterExport> pending_character_export;
|
std::unique_ptr<PendingCharacterExport> pending_character_export;
|
||||||
std::deque<std::function<void(uint32_t, uint32_t)>> function_call_response_queue;
|
std::deque<std::function<void(uint32_t, uint32_t)>> function_call_response_queue;
|
||||||
|
|
||||||
// File loading state
|
// File loading state
|
||||||
uint32_t dol_base_addr;
|
uint32_t dol_base_addr;
|
||||||
std::shared_ptr<DOLFileIndex::File> loading_dol_file;
|
std::shared_ptr<DOLFileIndex::File> loading_dol_file;
|
||||||
std::unordered_map<std::string, std::shared_ptr<const std::string>> sending_files;
|
std::unordered_map<std::string, std::shared_ptr<const std::string>> sending_files;
|
||||||
|
|
||||||
Client(
|
Client(
|
||||||
std::shared_ptr<Server> server,
|
std::shared_ptr<Server> server,
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
uint64_t virtual_network_id,
|
uint64_t virtual_network_id,
|
||||||
Version version,
|
Version version,
|
||||||
ServerBehavior server_behavior);
|
ServerBehavior server_behavior);
|
||||||
~Client();
|
~Client();
|
||||||
|
|
||||||
void update_channel_name();
|
void update_channel_name();
|
||||||
|
|
||||||
void reschedule_save_game_data_event();
|
void reschedule_save_game_data_event();
|
||||||
void reschedule_ping_and_timeout_events();
|
void reschedule_ping_and_timeout_events();
|
||||||
|
|
||||||
inline Version version() const {
|
inline Version version() const {
|
||||||
return this->channel.version;
|
return this->channel.version;
|
||||||
}
|
}
|
||||||
inline uint8_t language() const {
|
inline uint8_t language() const {
|
||||||
return this->channel.language;
|
return this->channel.language;
|
||||||
}
|
}
|
||||||
|
|
||||||
void convert_account_to_temporary_if_nte();
|
void convert_account_to_temporary_if_nte();
|
||||||
|
|
||||||
void sync_config();
|
void sync_config();
|
||||||
|
|
||||||
std::shared_ptr<ServerState> require_server_state() const;
|
std::shared_ptr<ServerState> require_server_state() const;
|
||||||
std::shared_ptr<Lobby> require_lobby() const;
|
std::shared_ptr<Lobby> require_lobby() const;
|
||||||
|
|
||||||
std::shared_ptr<const TeamIndex::Team> team() const;
|
std::shared_ptr<const TeamIndex::Team> team() const;
|
||||||
|
|
||||||
bool evaluate_quest_availability_expression(
|
bool evaluate_quest_availability_expression(
|
||||||
std::shared_ptr<const IntegralExpression> expr,
|
std::shared_ptr<const IntegralExpression> expr,
|
||||||
std::shared_ptr<const Lobby> game,
|
std::shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const;
|
bool v1_present) const;
|
||||||
bool can_see_quest(
|
bool can_see_quest(
|
||||||
std::shared_ptr<const Quest> q,
|
std::shared_ptr<const Quest> q,
|
||||||
std::shared_ptr<const Lobby> game,
|
std::shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const;
|
bool v1_present) const;
|
||||||
bool can_play_quest(
|
bool can_play_quest(
|
||||||
std::shared_ptr<const Quest> q,
|
std::shared_ptr<const Quest> q,
|
||||||
std::shared_ptr<const Lobby> game,
|
std::shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const;
|
bool v1_present) const;
|
||||||
|
|
||||||
bool can_use_chat_commands() const;
|
bool can_use_chat_commands() const;
|
||||||
|
|
||||||
static void dispatch_save_game_data(evutil_socket_t, short, void* ctx);
|
static void dispatch_save_game_data(evutil_socket_t, short, void* ctx);
|
||||||
void save_game_data();
|
void save_game_data();
|
||||||
static void dispatch_send_ping(evutil_socket_t, short, void* ctx);
|
static void dispatch_send_ping(evutil_socket_t, short, void* ctx);
|
||||||
void send_ping();
|
void send_ping();
|
||||||
static void dispatch_idle_timeout(evutil_socket_t, short, void* ctx);
|
static void dispatch_idle_timeout(evutil_socket_t, short, void* ctx);
|
||||||
void idle_timeout();
|
void idle_timeout();
|
||||||
|
|
||||||
void suspend_timeouts();
|
void suspend_timeouts();
|
||||||
|
|
||||||
const std::string& get_bb_username() const;
|
const std::string& get_bb_username() const;
|
||||||
void set_bb_username(const std::string& bb_username);
|
void set_bb_username(const std::string& bb_username);
|
||||||
|
|
||||||
void create_battle_overlay(std::shared_ptr<const BattleRules> rules, std::shared_ptr<const LevelTable> level_table);
|
void create_battle_overlay(std::shared_ptr<const BattleRules> rules, std::shared_ptr<const LevelTable> level_table);
|
||||||
void create_challenge_overlay(Version version, size_t template_index, std::shared_ptr<const LevelTable> level_table);
|
void create_challenge_overlay(Version version, size_t template_index, std::shared_ptr<const LevelTable> level_table);
|
||||||
inline void delete_overlay() {
|
inline void delete_overlay() {
|
||||||
this->overlay_character_data.reset();
|
this->overlay_character_data.reset();
|
||||||
}
|
}
|
||||||
inline bool has_overlay() const {
|
inline bool has_overlay() const {
|
||||||
return this->overlay_character_data.get() != nullptr;
|
return this->overlay_character_data.get() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void import_blocked_senders(const parray<le_uint32_t, 30>& blocked_senders);
|
void import_blocked_senders(const parray<le_uint32_t, 30>& blocked_senders);
|
||||||
|
|
||||||
std::shared_ptr<PSOBBBaseSystemFile> system_file(bool allow_load = true);
|
std::shared_ptr<PSOBBBaseSystemFile> system_file(bool allow_load = true);
|
||||||
std::shared_ptr<PSOBBCharacterFile> character(bool allow_load = true, bool allow_overlay = true);
|
std::shared_ptr<PSOBBCharacterFile> character(bool allow_load = true, bool allow_overlay = true);
|
||||||
std::shared_ptr<PSOBBGuildCardFile> guild_card_file(bool allow_load = true);
|
std::shared_ptr<PSOBBGuildCardFile> guild_card_file(bool allow_load = true);
|
||||||
std::shared_ptr<const PSOBBBaseSystemFile> system_file(bool allow_load = true) const;
|
std::shared_ptr<const PSOBBBaseSystemFile> system_file(bool allow_load = true) const;
|
||||||
std::shared_ptr<const PSOBBCharacterFile> character(bool allow_load = true, bool allow_overlay = true) const;
|
std::shared_ptr<const PSOBBCharacterFile> character(bool allow_load = true, bool allow_overlay = true) const;
|
||||||
std::shared_ptr<const PSOBBGuildCardFile> guild_card_file(bool allow_load = true) const;
|
std::shared_ptr<const PSOBBGuildCardFile> guild_card_file(bool allow_load = true) const;
|
||||||
|
|
||||||
void create_character_file(
|
void create_character_file(
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
const PlayerDispDataBBPreview& preview,
|
const PlayerDispDataBBPreview& preview,
|
||||||
std::shared_ptr<const LevelTable> level_table);
|
std::shared_ptr<const LevelTable> level_table);
|
||||||
|
|
||||||
std::string system_filename() const;
|
std::string system_filename() const;
|
||||||
static std::string character_filename(const std::string& bb_username, int8_t index);
|
static std::string character_filename(const std::string& bb_username, int8_t index);
|
||||||
static std::string backup_character_filename(uint32_t account_id, size_t index);
|
static std::string backup_character_filename(uint32_t account_id, size_t index);
|
||||||
std::string character_filename(int8_t index = -1) const;
|
std::string character_filename(int8_t index = -1) const;
|
||||||
std::string guild_card_filename() const;
|
std::string guild_card_filename() const;
|
||||||
std::string shared_bank_filename() const;
|
std::string shared_bank_filename() const;
|
||||||
|
|
||||||
std::string legacy_player_filename() const;
|
std::string legacy_player_filename() const;
|
||||||
std::string legacy_account_filename() const;
|
std::string legacy_account_filename() const;
|
||||||
|
|
||||||
void save_all();
|
void save_all();
|
||||||
void save_system_file() const;
|
void save_system_file() const;
|
||||||
static void save_character_file(
|
static void save_character_file(
|
||||||
const std::string& filename,
|
const std::string& filename,
|
||||||
std::shared_ptr<const PSOBBBaseSystemFile> sys,
|
std::shared_ptr<const PSOBBBaseSystemFile> sys,
|
||||||
std::shared_ptr<const PSOBBCharacterFile> character);
|
std::shared_ptr<const PSOBBCharacterFile> character);
|
||||||
// Note: This function is not const because it updates the player's play time.
|
// Note: This function is not const because it updates the player's play time.
|
||||||
void save_character_file();
|
void save_character_file();
|
||||||
void save_guild_card_file() const;
|
void save_guild_card_file() const;
|
||||||
|
|
||||||
void load_backup_character(uint32_t account_id, size_t index);
|
void load_backup_character(uint32_t account_id, size_t index);
|
||||||
void save_and_unload_character();
|
void save_and_unload_character();
|
||||||
|
|
||||||
PlayerBank200& current_bank();
|
PlayerBank200& current_bank();
|
||||||
std::shared_ptr<PSOBBCharacterFile> current_bank_character();
|
std::shared_ptr<PSOBBCharacterFile> current_bank_character();
|
||||||
bool use_shared_bank(); // Returns true if the bank exists; false if it was created
|
bool use_shared_bank(); // Returns true if the bank exists; false if it was created
|
||||||
void use_character_bank(int8_t bb_character_index);
|
void use_character_bank(int8_t bb_character_index);
|
||||||
void use_default_bank();
|
void use_default_bank();
|
||||||
|
|
||||||
void print_inventory(FILE* stream) const;
|
void print_inventory(FILE* stream) const;
|
||||||
void print_bank(FILE* stream) const;
|
void print_bank(FILE* stream) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The overlay character data is used in battle and challenge modes, when
|
// The overlay character data is used in battle and challenge modes, when
|
||||||
// character data is temporarily replaced in-game. In other play modes and in
|
// character data is temporarily replaced in-game. In other play modes and in
|
||||||
// lobbies, overlay_character_data is null.
|
// lobbies, overlay_character_data is null.
|
||||||
std::shared_ptr<PSOBBBaseSystemFile> system_data;
|
std::shared_ptr<PSOBBBaseSystemFile> system_data;
|
||||||
std::shared_ptr<PSOBBCharacterFile> overlay_character_data;
|
std::shared_ptr<PSOBBCharacterFile> overlay_character_data;
|
||||||
std::shared_ptr<PSOBBCharacterFile> character_data;
|
std::shared_ptr<PSOBBCharacterFile> character_data;
|
||||||
std::shared_ptr<PSOBBGuildCardFile> guild_card_data;
|
std::shared_ptr<PSOBBGuildCardFile> guild_card_data;
|
||||||
std::shared_ptr<PlayerBank200> external_bank;
|
std::shared_ptr<PlayerBank200> external_bank;
|
||||||
std::shared_ptr<PSOBBCharacterFile> external_bank_character;
|
std::shared_ptr<PSOBBCharacterFile> external_bank_character;
|
||||||
int8_t external_bank_character_index;
|
int8_t external_bank_character_index;
|
||||||
uint64_t last_play_time_update;
|
uint64_t last_play_time_update;
|
||||||
|
|
||||||
void save_and_clear_external_bank();
|
void save_and_clear_external_bank();
|
||||||
|
|
||||||
void load_all_files();
|
void load_all_files();
|
||||||
void update_character_data_after_load(std::shared_ptr<PSOBBCharacterFile> character_data);
|
void update_character_data_after_load(std::shared_ptr<PSOBBCharacterFile> character_data);
|
||||||
};
|
};
|
||||||
|
|||||||
+7233
-7233
File diff suppressed because it is too large
Load Diff
+1357
-1357
File diff suppressed because it is too large
Load Diff
+226
-226
@@ -1,226 +1,226 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <phosg/Tools.hh>
|
#include <phosg/Tools.hh>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
enum class CompressPhase {
|
enum class CompressPhase {
|
||||||
INDEX = 0,
|
INDEX = 0,
|
||||||
CONSTRUCT_PATHS,
|
CONSTRUCT_PATHS,
|
||||||
BACKTRACE_OPTIMAL_PATH,
|
BACKTRACE_OPTIMAL_PATH,
|
||||||
GENERATE_RESULT,
|
GENERATE_RESULT,
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const char* name_for_enum<CompressPhase>(CompressPhase v);
|
const char* name_for_enum<CompressPhase>(CompressPhase v);
|
||||||
|
|
||||||
typedef std::function<void(CompressPhase phase, size_t input_progress, size_t input_size, size_t output_size)> ProgressCallback;
|
typedef std::function<void(CompressPhase phase, size_t input_progress, size_t input_size, size_t output_size)> ProgressCallback;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// PRS compression
|
// PRS compression
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// 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.)
|
||||||
// To use this class, instantiate it, then call .add() one or more times, then
|
// To use this class, instantiate it, then call .add() one or more times, then
|
||||||
// call .close() and use the returned string as the compressed result.
|
// call .close() and use the returned string as the compressed result.
|
||||||
class PRSCompressor {
|
class PRSCompressor {
|
||||||
public:
|
public:
|
||||||
// compression_level specifies how aggressively to search for alternate paths:
|
// compression_level specifies how aggressively to search for alternate paths:
|
||||||
// -1: Don't perform any compression at all, but produce output that can be
|
// -1: Don't perform any compression at all, but produce output that can be
|
||||||
// understood by prs_decompress. The output will be about 9/8 the size
|
// understood by prs_decompress. The output will be about 9/8 the size
|
||||||
// of the input.
|
// of the input.
|
||||||
// 0: Greedily search for the longest backreference at every point. Don't
|
// 0: Greedily search for the longest backreference at every point. Don't
|
||||||
// consider any alternate paths. Generally offers a good balance between
|
// consider any alternate paths. Generally offers a good balance between
|
||||||
// speed and output size.
|
// speed and output size.
|
||||||
// 1: Consider two paths at each point when a backreference is found: using
|
// 1: Consider two paths at each point when a backreference is found: using
|
||||||
// the backreference or ignoring it.
|
// the backreference or ignoring it.
|
||||||
// 2+: Consider further chains of paths at each point. Using values 2 or
|
// 2+: Consider further chains of paths at each point. Using values 2 or
|
||||||
// greater for compression_level generally yields diminishing returns.
|
// greater for compression_level generally yields diminishing returns.
|
||||||
explicit PRSCompressor(ssize_t compression_level = 0, ProgressCallback progress_fn = nullptr);
|
explicit PRSCompressor(ssize_t compression_level = 0, ProgressCallback progress_fn = nullptr);
|
||||||
~PRSCompressor() = default;
|
~PRSCompressor() = default;
|
||||||
|
|
||||||
// Adds more input data to be compressed, which logically comes after all
|
// Adds more input data to be compressed, which logically comes after all
|
||||||
// previous data provided via add() calls. Cannot be called after close() is
|
// previous data provided via add() calls. Cannot be called after close() is
|
||||||
// called.
|
// called.
|
||||||
void add(const void* data, size_t size);
|
void add(const void* data, size_t size);
|
||||||
void add(const std::string& data);
|
void add(const std::string& data);
|
||||||
|
|
||||||
// Ends compression and returns the complete compressed result. It's OK to
|
// Ends compression and returns the complete compressed result. It's OK to
|
||||||
// std::move() from the returned string reference.
|
// std::move() from the returned string reference.
|
||||||
std::string& close();
|
std::string& close();
|
||||||
|
|
||||||
// Returns the total number of bytes passed to add() calls so far.
|
// Returns the total number of bytes passed to add() calls so far.
|
||||||
inline size_t input_size() const {
|
inline size_t input_size() const {
|
||||||
return this->input_bytes;
|
return this->input_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <size_t Size>
|
template <size_t Size>
|
||||||
struct WrappedLog {
|
struct WrappedLog {
|
||||||
parray<uint8_t, Size> data;
|
parray<uint8_t, Size> data;
|
||||||
|
|
||||||
WrappedLog() : data(0) {}
|
WrappedLog() : data(0) {}
|
||||||
~WrappedLog() = default;
|
~WrappedLog() = default;
|
||||||
|
|
||||||
inline uint8_t at(size_t offset) const {
|
inline uint8_t at(size_t offset) const {
|
||||||
return this->data[offset % this->data.size()];
|
return this->data[offset % this->data.size()];
|
||||||
}
|
}
|
||||||
inline uint8_t& at(size_t offset) {
|
inline uint8_t& at(size_t offset) {
|
||||||
return this->data[offset % this->data.size()];
|
return this->data[offset % this->data.size()];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <size_t Size>
|
template <size_t Size>
|
||||||
struct IndexedLog : WrappedLog<Size> {
|
struct IndexedLog : WrappedLog<Size> {
|
||||||
size_t offset;
|
size_t offset;
|
||||||
size_t size;
|
size_t size;
|
||||||
std::array<std::deque<size_t>, 0x100> index;
|
std::array<std::deque<size_t>, 0x100> index;
|
||||||
|
|
||||||
IndexedLog()
|
IndexedLog()
|
||||||
: WrappedLog<Size>(),
|
: WrappedLog<Size>(),
|
||||||
offset(0),
|
offset(0),
|
||||||
size(0) {}
|
size(0) {}
|
||||||
~IndexedLog() = default;
|
~IndexedLog() = default;
|
||||||
|
|
||||||
inline size_t end_offset() const {
|
inline size_t end_offset() const {
|
||||||
return this->offset + this->size;
|
return this->offset + this->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_back(uint8_t v) {
|
void push_back(uint8_t v) {
|
||||||
if (this->size == Size) {
|
if (this->size == Size) {
|
||||||
this->pop_front();
|
this->pop_front();
|
||||||
}
|
}
|
||||||
size_t write_offset = this->offset + this->size;
|
size_t write_offset = this->offset + this->size;
|
||||||
this->at(write_offset) = v;
|
this->at(write_offset) = v;
|
||||||
this->index[v].push_back(write_offset);
|
this->index[v].push_back(write_offset);
|
||||||
this->size++;
|
this->size++;
|
||||||
}
|
}
|
||||||
uint8_t pop_back() {
|
uint8_t pop_back() {
|
||||||
if (!this->size) {
|
if (!this->size) {
|
||||||
throw std::logic_error("pop_back called on empty IndexedLog");
|
throw std::logic_error("pop_back called on empty IndexedLog");
|
||||||
}
|
}
|
||||||
this->size--;
|
this->size--;
|
||||||
size_t offset = this->offset + this->size;
|
size_t offset = this->offset + this->size;
|
||||||
uint8_t v = this->at(offset);
|
uint8_t v = this->at(offset);
|
||||||
this->index[v].pop_back();
|
this->index[v].pop_back();
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
uint8_t pop_front() {
|
uint8_t pop_front() {
|
||||||
uint8_t v = this->at(this->offset);
|
uint8_t v = this->at(this->offset);
|
||||||
this->index[v].pop_front();
|
this->index[v].pop_front();
|
||||||
this->offset++;
|
this->offset++;
|
||||||
this->size--;
|
this->size--;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
const std::deque<size_t>& find(uint8_t v) {
|
const std::deque<size_t>& find(uint8_t v) {
|
||||||
return this->index[v];
|
return this->index[v];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void add_byte(uint8_t v);
|
void add_byte(uint8_t v);
|
||||||
void advance();
|
void advance();
|
||||||
void move_forward_data_to_reverse_log(size_t size);
|
void move_forward_data_to_reverse_log(size_t size);
|
||||||
void advance_literal();
|
void advance_literal();
|
||||||
void advance_short_copy(ssize_t offset, size_t size);
|
void advance_short_copy(ssize_t offset, size_t size);
|
||||||
void advance_long_copy(ssize_t offset, size_t size);
|
void advance_long_copy(ssize_t offset, size_t size);
|
||||||
void advance_extended_copy(ssize_t offset, size_t size);
|
void advance_extended_copy(ssize_t offset, size_t size);
|
||||||
void write_control(bool z);
|
void write_control(bool z);
|
||||||
void flush_control();
|
void flush_control();
|
||||||
|
|
||||||
ssize_t compression_level;
|
ssize_t compression_level;
|
||||||
ProgressCallback progress_fn;
|
ProgressCallback progress_fn;
|
||||||
bool closed;
|
bool closed;
|
||||||
|
|
||||||
size_t control_byte_offset;
|
size_t control_byte_offset;
|
||||||
uint16_t pending_control_bits;
|
uint16_t pending_control_bits;
|
||||||
|
|
||||||
size_t input_bytes;
|
size_t input_bytes;
|
||||||
WrappedLog<0x101> forward_log;
|
WrappedLog<0x101> forward_log;
|
||||||
IndexedLog<0x2000> reverse_log;
|
IndexedLog<0x2000> reverse_log;
|
||||||
|
|
||||||
StringWriter output;
|
StringWriter output;
|
||||||
};
|
};
|
||||||
|
|
||||||
// These functions use PRSCompressor to compress a buffer of data. This is
|
// These functions use PRSCompressor to compress a buffer of data. This is
|
||||||
// essentially a shortcut for constructing a PRSCompressor, calling .add() on
|
// essentially a shortcut for constructing a PRSCompressor, calling .add() on
|
||||||
// it once, then calling .close().
|
// it once, then calling .close().
|
||||||
std::string prs_compress(
|
std::string prs_compress(
|
||||||
const void* vdata,
|
const void* vdata,
|
||||||
size_t size,
|
size_t size,
|
||||||
ssize_t compression_level = 0,
|
ssize_t compression_level = 0,
|
||||||
ProgressCallback progress_fn = nullptr);
|
ProgressCallback progress_fn = nullptr);
|
||||||
std::string prs_compress(
|
std::string prs_compress(
|
||||||
const std::string& data,
|
const std::string& data,
|
||||||
ssize_t compression_level = 0,
|
ssize_t compression_level = 0,
|
||||||
ProgressCallback progress_fn = nullptr);
|
ProgressCallback progress_fn = nullptr);
|
||||||
|
|
||||||
// A faster form of prs_compress that doesn't have a tunable compression level.
|
// A faster form of prs_compress that doesn't have a tunable compression level.
|
||||||
std::string prs_compress_indexed(
|
std::string prs_compress_indexed(
|
||||||
const void* vdata,
|
const void* vdata,
|
||||||
size_t size,
|
size_t size,
|
||||||
ProgressCallback progress_fn = nullptr);
|
ProgressCallback progress_fn = nullptr);
|
||||||
std::string prs_compress_indexed(
|
std::string prs_compress_indexed(
|
||||||
const std::string& data,
|
const std::string& data,
|
||||||
ProgressCallback progress_fn = nullptr);
|
ProgressCallback progress_fn = nullptr);
|
||||||
|
|
||||||
// Compresses data using PRS to the smallest possible output size. This function
|
// Compresses data using PRS to the smallest possible output size. This function
|
||||||
// is slow, but produces results significantly smaller than even Sega's original
|
// is slow, but produces results significantly smaller than even Sega's original
|
||||||
// compressor.
|
// compressor.
|
||||||
std::string prs_compress_optimal(const void* vdata, size_t size, ProgressCallback progress_fn = nullptr);
|
std::string prs_compress_optimal(const void* vdata, size_t size, ProgressCallback progress_fn = nullptr);
|
||||||
std::string prs_compress_optimal(const std::string& data, ProgressCallback progress_fn = nullptr);
|
std::string prs_compress_optimal(const std::string& data, ProgressCallback progress_fn = nullptr);
|
||||||
|
|
||||||
// Compresses data using PRS to the LARGEST possible output size. There is no
|
// Compresses data using PRS to the LARGEST possible output size. There is no
|
||||||
// practical use for this function except for amusement.
|
// practical use for this function except for amusement.
|
||||||
std::string prs_compress_pessimal(const void* vdata, size_t size);
|
std::string prs_compress_pessimal(const void* vdata, size_t size);
|
||||||
|
|
||||||
// Decompresses PRS-compressed data.
|
// Decompresses PRS-compressed data.
|
||||||
struct PRSDecompressResult {
|
struct PRSDecompressResult {
|
||||||
std::string data;
|
std::string data;
|
||||||
size_t input_bytes_used;
|
size_t input_bytes_used;
|
||||||
};
|
};
|
||||||
PRSDecompressResult prs_decompress_with_meta(const void* data, size_t size, size_t max_output_size = 0, bool allow_unterminated = false);
|
PRSDecompressResult prs_decompress_with_meta(const void* data, size_t size, size_t max_output_size = 0, bool allow_unterminated = false);
|
||||||
PRSDecompressResult prs_decompress_with_meta(const std::string& data, size_t max_output_size = 0, bool allow_unterminated = false);
|
PRSDecompressResult prs_decompress_with_meta(const std::string& data, size_t max_output_size = 0, bool allow_unterminated = false);
|
||||||
std::string prs_decompress(const void* data, size_t size, size_t max_output_size = 0, bool allow_unterminated = false);
|
std::string prs_decompress(const void* data, size_t size, size_t max_output_size = 0, bool allow_unterminated = false);
|
||||||
std::string prs_decompress(const std::string& data, size_t max_output_size = 0, bool allow_unterminated = false);
|
std::string prs_decompress(const std::string& data, size_t max_output_size = 0, bool allow_unterminated = false);
|
||||||
|
|
||||||
// Returns the decompressed size of PRS-compressed data, without actually
|
// Returns the decompressed size of PRS-compressed data, without actually
|
||||||
// decompressing it.
|
// decompressing it.
|
||||||
size_t prs_decompress_size(const void* data, size_t size, size_t max_output_size = 0, bool allow_unterminated = false);
|
size_t prs_decompress_size(const void* data, size_t size, size_t max_output_size = 0, bool allow_unterminated = false);
|
||||||
size_t prs_decompress_size(const std::string& data, size_t max_output_size = 0, bool allow_unterminated = false);
|
size_t prs_decompress_size(const std::string& data, size_t max_output_size = 0, bool allow_unterminated = false);
|
||||||
|
|
||||||
// Prints the command stream from a PRS-compressed buffer.
|
// Prints the command stream from a PRS-compressed buffer.
|
||||||
void prs_disassemble(FILE* stream, const void* data, size_t size);
|
void prs_disassemble(FILE* stream, const void* data, size_t size);
|
||||||
void prs_disassemble(FILE* stream, const std::string& data);
|
void prs_disassemble(FILE* stream, const std::string& data);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// BC0 compression
|
// BC0 compression
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Compresses data using the BC0 algorithm. Like with PRS, the optimal variant
|
// Compresses data using the BC0 algorithm. Like with PRS, the optimal variant
|
||||||
// is slow, but produces the smallest possible output.
|
// is slow, but produces the smallest possible output.
|
||||||
std::string bc0_compress_optimal(
|
std::string bc0_compress_optimal(
|
||||||
const void* in_data_v,
|
const void* in_data_v,
|
||||||
size_t in_size,
|
size_t in_size,
|
||||||
ProgressCallback progress_fn = nullptr);
|
ProgressCallback progress_fn = nullptr);
|
||||||
std::string bc0_compress(const std::string& data, ProgressCallback progress_fn = nullptr);
|
std::string bc0_compress(const std::string& data, ProgressCallback progress_fn = nullptr);
|
||||||
std::string bc0_compress(const void* in_data_v, size_t in_size, ProgressCallback progress_fn = nullptr);
|
std::string bc0_compress(const void* in_data_v, size_t in_size, ProgressCallback progress_fn = nullptr);
|
||||||
|
|
||||||
// Encodes data in a BC0-compatible format without compression (similar to using
|
// Encodes data in a BC0-compatible format without compression (similar to using
|
||||||
// compression_level=-1 with prs_compress).
|
// compression_level=-1 with prs_compress).
|
||||||
std::string bc0_encode(const void* in_data_v, size_t in_size);
|
std::string bc0_encode(const void* in_data_v, size_t in_size);
|
||||||
|
|
||||||
// Decompresses BC0-compressed data.
|
// Decompresses BC0-compressed data.
|
||||||
std::string bc0_decompress(const std::string& data);
|
std::string bc0_decompress(const std::string& data);
|
||||||
std::string bc0_decompress(const void* data, size_t size);
|
std::string bc0_decompress(const void* data, size_t size);
|
||||||
|
|
||||||
// Prints the command stream from a BC0-compressed buffer.
|
// Prints the command stream from a BC0-compressed buffer.
|
||||||
void bc0_disassemble(FILE* stream, const std::string& data);
|
void bc0_disassemble(FILE* stream, const std::string& data);
|
||||||
void bc0_disassemble(FILE* stream, const void* data, size_t size);
|
void bc0_disassemble(FILE* stream, const void* data, size_t size);
|
||||||
|
|||||||
+121
-121
@@ -1,121 +1,121 @@
|
|||||||
#include "DNSServer.hh"
|
#include "DNSServer.hh"
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.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 <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "NetworkAddresses.hh"
|
#include "NetworkAddresses.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
DNSServer::DNSServer(
|
DNSServer::DNSServer(
|
||||||
shared_ptr<struct event_base> base,
|
shared_ptr<struct event_base> base,
|
||||||
uint32_t local_connect_address,
|
uint32_t local_connect_address,
|
||||||
uint32_t external_connect_address,
|
uint32_t external_connect_address,
|
||||||
shared_ptr<const IPV4RangeSet> banned_ipv4_ranges)
|
shared_ptr<const IPV4RangeSet> banned_ipv4_ranges)
|
||||||
: 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),
|
||||||
banned_ipv4_ranges(banned_ipv4_ranges) {}
|
banned_ipv4_ranges(banned_ipv4_ranges) {}
|
||||||
|
|
||||||
DNSServer::~DNSServer() {
|
DNSServer::~DNSServer() {
|
||||||
for (const auto& it : this->fd_to_receive_event) {
|
for (const auto& it : this->fd_to_receive_event) {
|
||||||
close(it.first);
|
close(it.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSServer::listen(const std::string& socket_path) {
|
void DNSServer::listen(const std::string& socket_path) {
|
||||||
this->add_socket(::listen(socket_path, 0, 0));
|
this->add_socket(::listen(socket_path, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSServer::listen(const std::string& addr, int port) {
|
void DNSServer::listen(const std::string& addr, int port) {
|
||||||
this->add_socket(::listen(addr, port, 0));
|
this->add_socket(::listen(addr, port, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSServer::listen(int port) {
|
void DNSServer::listen(int port) {
|
||||||
this->add_socket(::listen("", port, 0));
|
this->add_socket(::listen("", port, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSServer::add_socket(int fd) {
|
void DNSServer::add_socket(int fd) {
|
||||||
unique_ptr<struct event, void (*)(struct event*)> e(
|
unique_ptr<struct event, void (*)(struct event*)> e(
|
||||||
event_new(this->base.get(), fd, EV_READ | EV_PERSIST, &DNSServer::dispatch_on_receive_message, this),
|
event_new(this->base.get(), fd, EV_READ | EV_PERSIST, &DNSServer::dispatch_on_receive_message, this),
|
||||||
event_free);
|
event_free);
|
||||||
event_add(e.get(), nullptr);
|
event_add(e.get(), nullptr);
|
||||||
this->fd_to_receive_event.emplace(fd, std::move(e));
|
this->fd_to_receive_event.emplace(fd, std::move(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSServer::dispatch_on_receive_message(evutil_socket_t fd,
|
void DNSServer::dispatch_on_receive_message(evutil_socket_t fd,
|
||||||
short events, void* ctx) {
|
short events, void* ctx) {
|
||||||
reinterpret_cast<DNSServer*>(ctx)->on_receive_message(fd, events);
|
reinterpret_cast<DNSServer*>(ctx)->on_receive_message(fd, events);
|
||||||
}
|
}
|
||||||
|
|
||||||
string DNSServer::response_for_query(const void* vdata, size_t size, uint32_t resolved_address) {
|
string DNSServer::response_for_query(const void* vdata, size_t size, uint32_t resolved_address) {
|
||||||
if (size < 0x0C) {
|
if (size < 0x0C) {
|
||||||
throw invalid_argument("query too small");
|
throw invalid_argument("query too small");
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* data = reinterpret_cast<const char*>(vdata);
|
const char* data = reinterpret_cast<const char*>(vdata);
|
||||||
size_t name_len = strlen(&data[12]) + 1;
|
size_t name_len = strlen(&data[12]) + 1;
|
||||||
|
|
||||||
be_uint32_t be_resolved_address = resolved_address;
|
be_uint32_t be_resolved_address = resolved_address;
|
||||||
|
|
||||||
string response;
|
string response;
|
||||||
response.append(data, 2);
|
response.append(data, 2);
|
||||||
response.append("\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00", 10);
|
response.append("\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00", 10);
|
||||||
response.append(&data[12], name_len);
|
response.append(&data[12], name_len);
|
||||||
response.append("\x00\x01\x00\x01\xC0\x0C\x00\x01\x00\x01\x00\x00\x00\x3C\x00\x04", 16);
|
response.append("\x00\x01\x00\x01\xC0\x0C\x00\x01\x00\x01\x00\x00\x00\x3C\x00\x04", 16);
|
||||||
response.append(reinterpret_cast<const char*>(&be_resolved_address), 4);
|
response.append(reinterpret_cast<const char*>(&be_resolved_address), 4);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
string DNSServer::response_for_query(
|
string DNSServer::response_for_query(
|
||||||
const string& query, uint32_t resolved_address) {
|
const string& query, uint32_t resolved_address) {
|
||||||
return DNSServer::response_for_query(query.data(), query.size(), resolved_address);
|
return DNSServer::response_for_query(query.data(), query.size(), resolved_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSServer::on_receive_message(int fd, short) {
|
void DNSServer::on_receive_message(int fd, short) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct sockaddr_storage remote;
|
struct sockaddr_storage remote;
|
||||||
socklen_t remote_size = sizeof(sockaddr_in);
|
socklen_t remote_size = sizeof(sockaddr_in);
|
||||||
memset(&remote, 0, remote_size);
|
memset(&remote, 0, remote_size);
|
||||||
|
|
||||||
string input(2048, 0);
|
string input(2048, 0);
|
||||||
ssize_t bytes = recvfrom(fd, const_cast<char*>(input.data()), input.size(),
|
ssize_t bytes = recvfrom(fd, const_cast<char*>(input.data()), input.size(),
|
||||||
0, reinterpret_cast<sockaddr*>(&remote), &remote_size);
|
0, reinterpret_cast<sockaddr*>(&remote), &remote_size);
|
||||||
|
|
||||||
if (bytes < 0) {
|
if (bytes < 0) {
|
||||||
if (errno != EAGAIN) {
|
if (errno != EAGAIN) {
|
||||||
dns_server_log.error("input error %d", errno);
|
dns_server_log.error("input error %d", errno);
|
||||||
throw runtime_error("cannot read from udp socket");
|
throw runtime_error("cannot read from udp socket");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
} else if (bytes == 0) {
|
} else if (bytes == 0) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
} else if (bytes < 0x0C) {
|
} else if (bytes < 0x0C) {
|
||||||
dns_server_log.warning("input query too small");
|
dns_server_log.warning("input query too small");
|
||||||
print_data(stderr, input.data(), bytes);
|
print_data(stderr, input.data(), bytes);
|
||||||
|
|
||||||
} else if (!this->banned_ipv4_ranges->check(remote)) {
|
} else if (!this->banned_ipv4_ranges->check(remote)) {
|
||||||
input.resize(bytes);
|
input.resize(bytes);
|
||||||
const sockaddr_in* remote_sin = reinterpret_cast<const sockaddr_in*>(&remote);
|
const sockaddr_in* remote_sin = reinterpret_cast<const sockaddr_in*>(&remote);
|
||||||
uint32_t remote_address = ntohl(remote_sin->sin_addr.s_addr);
|
uint32_t remote_address = ntohl(remote_sin->sin_addr.s_addr);
|
||||||
uint32_t connect_address = is_local_address(remote_address)
|
uint32_t connect_address = is_local_address(remote_address)
|
||||||
? this->local_connect_address
|
? this->local_connect_address
|
||||||
: this->external_connect_address;
|
: this->external_connect_address;
|
||||||
string response = this->response_for_query(input, connect_address);
|
string response = this->response_for_query(input, connect_address);
|
||||||
sendto(fd, response.data(), response.size(), 0,
|
sendto(fd, response.data(), response.size(), 0,
|
||||||
reinterpret_cast<const sockaddr*>(&remote), remote_size);
|
reinterpret_cast<const sockaddr*>(&remote), remote_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-44
@@ -1,44 +1,44 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "IPV4RangeSet.hh"
|
#include "IPV4RangeSet.hh"
|
||||||
|
|
||||||
class DNSServer {
|
class DNSServer {
|
||||||
public:
|
public:
|
||||||
DNSServer(
|
DNSServer(
|
||||||
std::shared_ptr<struct event_base> base,
|
std::shared_ptr<struct event_base> base,
|
||||||
uint32_t local_connect_address,
|
uint32_t local_connect_address,
|
||||||
uint32_t external_connect_address,
|
uint32_t external_connect_address,
|
||||||
std::shared_ptr<const IPV4RangeSet> banned_ipv4_ranges);
|
std::shared_ptr<const IPV4RangeSet> banned_ipv4_ranges);
|
||||||
DNSServer(const DNSServer&) = delete;
|
DNSServer(const DNSServer&) = delete;
|
||||||
DNSServer(DNSServer&&) = delete;
|
DNSServer(DNSServer&&) = delete;
|
||||||
virtual ~DNSServer();
|
virtual ~DNSServer();
|
||||||
|
|
||||||
inline void set_banned_ipv4_ranges(std::shared_ptr<const IPV4RangeSet> banned_ipv4_ranges) {
|
inline void set_banned_ipv4_ranges(std::shared_ptr<const IPV4RangeSet> banned_ipv4_ranges) {
|
||||||
this->banned_ipv4_ranges = banned_ipv4_ranges;
|
this->banned_ipv4_ranges = banned_ipv4_ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
void listen(const std::string& socket_path);
|
void listen(const std::string& socket_path);
|
||||||
void listen(const std::string& addr, int port);
|
void listen(const std::string& addr, int port);
|
||||||
void listen(int port);
|
void listen(int port);
|
||||||
void add_socket(int fd);
|
void add_socket(int fd);
|
||||||
|
|
||||||
static std::string response_for_query(const void* vdata, size_t size, uint32_t resolved_address);
|
static std::string response_for_query(const void* vdata, size_t size, uint32_t resolved_address);
|
||||||
static std::string response_for_query(const std::string& query, uint32_t resolved_address);
|
static std::string response_for_query(const std::string& query, uint32_t resolved_address);
|
||||||
|
|
||||||
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;
|
||||||
std::shared_ptr<const IPV4RangeSet> banned_ipv4_ranges;
|
std::shared_ptr<const IPV4RangeSet> banned_ipv4_ranges;
|
||||||
|
|
||||||
static void dispatch_on_receive_message(evutil_socket_t fd, short events, void* ctx);
|
static void dispatch_on_receive_message(evutil_socket_t fd, short events, void* ctx);
|
||||||
void on_receive_message(int fd, short event);
|
void on_receive_message(int fd, short event);
|
||||||
};
|
};
|
||||||
|
|||||||
+1120
-1120
File diff suppressed because it is too large
Load Diff
+149
-149
@@ -1,149 +1,149 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <phosg/Tools.hh>
|
#include <phosg/Tools.hh>
|
||||||
|
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
|
|
||||||
enum class EnemyType {
|
enum class EnemyType {
|
||||||
UNKNOWN = -1,
|
UNKNOWN = -1,
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
NON_ENEMY_NPC,
|
NON_ENEMY_NPC,
|
||||||
AL_RAPPY,
|
AL_RAPPY,
|
||||||
ASTARK,
|
ASTARK,
|
||||||
BA_BOOTA,
|
BA_BOOTA,
|
||||||
BARBA_RAY,
|
BARBA_RAY,
|
||||||
BARBAROUS_WOLF,
|
BARBAROUS_WOLF,
|
||||||
BEE_L,
|
BEE_L,
|
||||||
BEE_R,
|
BEE_R,
|
||||||
BOOMA,
|
BOOMA,
|
||||||
BOOTA,
|
BOOTA,
|
||||||
BULCLAW,
|
BULCLAW,
|
||||||
BULK,
|
BULK,
|
||||||
CANADINE,
|
CANADINE,
|
||||||
CANADINE_GROUP,
|
CANADINE_GROUP,
|
||||||
CANANE,
|
CANANE,
|
||||||
CHAOS_BRINGER,
|
CHAOS_BRINGER,
|
||||||
CHAOS_SORCERER,
|
CHAOS_SORCERER,
|
||||||
CLAW,
|
CLAW,
|
||||||
DARK_BELRA,
|
DARK_BELRA,
|
||||||
DARK_FALZ_1,
|
DARK_FALZ_1,
|
||||||
DARK_FALZ_2,
|
DARK_FALZ_2,
|
||||||
DARK_FALZ_3,
|
DARK_FALZ_3,
|
||||||
DARK_GUNNER,
|
DARK_GUNNER,
|
||||||
DARVANT,
|
DARVANT,
|
||||||
DARVANT_ULTIMATE,
|
DARVANT_ULTIMATE,
|
||||||
DE_ROL_LE,
|
DE_ROL_LE,
|
||||||
DE_ROL_LE_BODY,
|
DE_ROL_LE_BODY,
|
||||||
DE_ROL_LE_MINE,
|
DE_ROL_LE_MINE,
|
||||||
DEATH_GUNNER,
|
DEATH_GUNNER,
|
||||||
DEL_LILY,
|
DEL_LILY,
|
||||||
DEL_RAPPY,
|
DEL_RAPPY,
|
||||||
DEL_RAPPY_ALT,
|
DEL_RAPPY_ALT,
|
||||||
DELBITER,
|
DELBITER,
|
||||||
DELDEPTH,
|
DELDEPTH,
|
||||||
DELSABER,
|
DELSABER,
|
||||||
DIMENIAN,
|
DIMENIAN,
|
||||||
DOLMDARL,
|
DOLMDARL,
|
||||||
DOLMOLM,
|
DOLMOLM,
|
||||||
DORPHON,
|
DORPHON,
|
||||||
DORPHON_ECLAIR,
|
DORPHON_ECLAIR,
|
||||||
DRAGON,
|
DRAGON,
|
||||||
DUBCHIC,
|
DUBCHIC,
|
||||||
DUBWITCH, // Has no entry in battle params
|
DUBWITCH, // Has no entry in battle params
|
||||||
EGG_RAPPY,
|
EGG_RAPPY,
|
||||||
EPSIGUARD,
|
EPSIGUARD,
|
||||||
EPSILON,
|
EPSILON,
|
||||||
EVIL_SHARK,
|
EVIL_SHARK,
|
||||||
GAEL,
|
GAEL,
|
||||||
GAL_GRYPHON,
|
GAL_GRYPHON,
|
||||||
GARANZ,
|
GARANZ,
|
||||||
GEE,
|
GEE,
|
||||||
GI_GUE,
|
GI_GUE,
|
||||||
GIBBLES,
|
GIBBLES,
|
||||||
GIGOBOOMA,
|
GIGOBOOMA,
|
||||||
GILLCHIC,
|
GILLCHIC,
|
||||||
GIRTABLULU,
|
GIRTABLULU,
|
||||||
GOBOOMA,
|
GOBOOMA,
|
||||||
GOL_DRAGON,
|
GOL_DRAGON,
|
||||||
GORAN,
|
GORAN,
|
||||||
GORAN_DETONATOR,
|
GORAN_DETONATOR,
|
||||||
GRASS_ASSASSIN,
|
GRASS_ASSASSIN,
|
||||||
GUIL_SHARK,
|
GUIL_SHARK,
|
||||||
HALLO_RAPPY,
|
HALLO_RAPPY,
|
||||||
HIDOOM,
|
HIDOOM,
|
||||||
HILDEBEAR,
|
HILDEBEAR,
|
||||||
HILDEBLUE,
|
HILDEBLUE,
|
||||||
ILL_GILL,
|
ILL_GILL,
|
||||||
KONDRIEU,
|
KONDRIEU,
|
||||||
LA_DIMENIAN,
|
LA_DIMENIAN,
|
||||||
LOVE_RAPPY,
|
LOVE_RAPPY,
|
||||||
MERICAROL,
|
MERICAROL,
|
||||||
MERICUS,
|
MERICUS,
|
||||||
MERIKLE,
|
MERIKLE,
|
||||||
MERILLIA,
|
MERILLIA,
|
||||||
MERILTAS,
|
MERILTAS,
|
||||||
MERISSA_A,
|
MERISSA_A,
|
||||||
MERISSA_AA,
|
MERISSA_AA,
|
||||||
MIGIUM,
|
MIGIUM,
|
||||||
MONEST,
|
MONEST,
|
||||||
MORFOS,
|
MORFOS,
|
||||||
MOTHMANT,
|
MOTHMANT,
|
||||||
NANO_DRAGON,
|
NANO_DRAGON,
|
||||||
NAR_LILY,
|
NAR_LILY,
|
||||||
OLGA_FLOW_1,
|
OLGA_FLOW_1,
|
||||||
OLGA_FLOW_2,
|
OLGA_FLOW_2,
|
||||||
PAL_SHARK,
|
PAL_SHARK,
|
||||||
PAN_ARMS,
|
PAN_ARMS,
|
||||||
PAZUZU,
|
PAZUZU,
|
||||||
PAZUZU_ALT,
|
PAZUZU_ALT,
|
||||||
PIG_RAY,
|
PIG_RAY,
|
||||||
POFUILLY_SLIME,
|
POFUILLY_SLIME,
|
||||||
POUILLY_SLIME,
|
POUILLY_SLIME,
|
||||||
POISON_LILY,
|
POISON_LILY,
|
||||||
PYRO_GORAN,
|
PYRO_GORAN,
|
||||||
RAG_RAPPY,
|
RAG_RAPPY,
|
||||||
RECOBOX,
|
RECOBOX,
|
||||||
RECON,
|
RECON,
|
||||||
SAINT_MILLION,
|
SAINT_MILLION,
|
||||||
SAINT_RAPPY,
|
SAINT_RAPPY,
|
||||||
SAND_RAPPY,
|
SAND_RAPPY,
|
||||||
SAND_RAPPY_ALT,
|
SAND_RAPPY_ALT,
|
||||||
SATELLITE_LIZARD,
|
SATELLITE_LIZARD,
|
||||||
SATELLITE_LIZARD_ALT,
|
SATELLITE_LIZARD_ALT,
|
||||||
SAVAGE_WOLF,
|
SAVAGE_WOLF,
|
||||||
SHAMBERTIN,
|
SHAMBERTIN,
|
||||||
SINOW_BEAT,
|
SINOW_BEAT,
|
||||||
SINOW_BERILL,
|
SINOW_BERILL,
|
||||||
SINOW_GOLD,
|
SINOW_GOLD,
|
||||||
SINOW_SPIGELL,
|
SINOW_SPIGELL,
|
||||||
SINOW_ZELE,
|
SINOW_ZELE,
|
||||||
SINOW_ZOA,
|
SINOW_ZOA,
|
||||||
SO_DIMENIAN,
|
SO_DIMENIAN,
|
||||||
UL_GIBBON,
|
UL_GIBBON,
|
||||||
VOL_OPT_1,
|
VOL_OPT_1,
|
||||||
VOL_OPT_2,
|
VOL_OPT_2,
|
||||||
VOL_OPT_AMP,
|
VOL_OPT_AMP,
|
||||||
VOL_OPT_CORE,
|
VOL_OPT_CORE,
|
||||||
VOL_OPT_MONITOR,
|
VOL_OPT_MONITOR,
|
||||||
VOL_OPT_PILLAR,
|
VOL_OPT_PILLAR,
|
||||||
YOWIE,
|
YOWIE,
|
||||||
YOWIE_ALT,
|
YOWIE_ALT,
|
||||||
ZE_BOOTA,
|
ZE_BOOTA,
|
||||||
ZOL_GIBBON,
|
ZOL_GIBBON,
|
||||||
ZU,
|
ZU,
|
||||||
ZU_ALT,
|
ZU_ALT,
|
||||||
MAX_ENEMY_TYPE,
|
MAX_ENEMY_TYPE,
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const char* name_for_enum<EnemyType>(EnemyType type);
|
const char* name_for_enum<EnemyType>(EnemyType type);
|
||||||
template <>
|
template <>
|
||||||
EnemyType enum_for_name<EnemyType>(const char* name);
|
EnemyType enum_for_name<EnemyType>(const char* name);
|
||||||
|
|
||||||
bool enemy_type_valid_for_episode(Episode episode, EnemyType enemy_type);
|
bool enemy_type_valid_for_episode(Episode episode, EnemyType enemy_type);
|
||||||
uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type);
|
uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type);
|
||||||
uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type);
|
uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type);
|
||||||
const std::vector<EnemyType>& enemy_types_for_rare_table_index(Episode episode, uint8_t rt_index);
|
const std::vector<EnemyType>& enemy_types_for_rare_table_index(Episode episode, uint8_t rt_index);
|
||||||
bool enemy_type_is_rare(EnemyType type);
|
bool enemy_type_is_rare(EnemyType type);
|
||||||
|
|||||||
+528
-528
File diff suppressed because it is too large
Load Diff
+101
-101
@@ -1,101 +1,101 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Menu.hh"
|
#include "Menu.hh"
|
||||||
|
|
||||||
bool function_compiler_available();
|
bool function_compiler_available();
|
||||||
void set_function_compiler_available(bool is_available);
|
void set_function_compiler_available(bool is_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.
|
||||||
|
|
||||||
struct CompiledFunctionCode {
|
struct CompiledFunctionCode {
|
||||||
enum class Architecture {
|
enum class Architecture {
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
POWERPC, // GC
|
POWERPC, // GC
|
||||||
X86, // PC, XB, BB
|
X86, // PC, XB, BB
|
||||||
SH4, // Dreamcast
|
SH4, // Dreamcast
|
||||||
};
|
};
|
||||||
Architecture arch;
|
Architecture arch;
|
||||||
std::string code;
|
std::string code;
|
||||||
std::vector<uint16_t> relocation_deltas;
|
std::vector<uint16_t> relocation_deltas;
|
||||||
std::unordered_map<std::string, uint32_t> label_offsets;
|
std::unordered_map<std::string, uint32_t> label_offsets;
|
||||||
uint32_t entrypoint_offset_offset;
|
uint32_t entrypoint_offset_offset;
|
||||||
std::string source_path; // Path to source file from newserv root
|
std::string source_path; // Path to source file from newserv root
|
||||||
std::string short_name; // Based on filename
|
std::string short_name; // Based on filename
|
||||||
std::string long_name; // From .meta name directive
|
std::string long_name; // From .meta name directive
|
||||||
std::string description; // From .meta description directive
|
std::string description; // From .meta description directive
|
||||||
uint8_t index; // 0 = unused (not registered in index_to_function)
|
uint8_t index; // 0 = unused (not registered in index_to_function)
|
||||||
uint32_t menu_item_id;
|
uint32_t menu_item_id;
|
||||||
bool hide_from_patches_menu;
|
bool hide_from_patches_menu;
|
||||||
uint32_t specific_version;
|
uint32_t specific_version;
|
||||||
|
|
||||||
bool is_big_endian() const;
|
bool is_big_endian() const;
|
||||||
|
|
||||||
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 void* suffix_data = nullptr,
|
const void* suffix_data = nullptr,
|
||||||
size_t suffix_size = 0,
|
size_t suffix_size = 0,
|
||||||
uint32_t override_relocations_offset = 0) const;
|
uint32_t override_relocations_offset = 0) 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 void* suffix_data = nullptr,
|
const void* suffix_data = nullptr,
|
||||||
size_t suffix_size = 0,
|
size_t suffix_size = 0,
|
||||||
uint32_t override_relocations_offset = 0) const;
|
uint32_t override_relocations_offset = 0) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* name_for_architecture(CompiledFunctionCode::Architecture arch);
|
const char* name_for_architecture(CompiledFunctionCode::Architecture arch);
|
||||||
|
|
||||||
std::shared_ptr<CompiledFunctionCode> compile_function_code(
|
std::shared_ptr<CompiledFunctionCode> compile_function_code(
|
||||||
CompiledFunctionCode::Architecture arch,
|
CompiledFunctionCode::Architecture arch,
|
||||||
const std::string& directory,
|
const std::string& directory,
|
||||||
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);
|
||||||
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<CompiledFunctionCode>> name_to_function;
|
std::unordered_map<std::string, std::shared_ptr<CompiledFunctionCode>> name_to_function;
|
||||||
std::unordered_map<uint8_t, std::shared_ptr<CompiledFunctionCode>> index_to_function;
|
std::unordered_map<uint8_t, std::shared_ptr<CompiledFunctionCode>> index_to_function;
|
||||||
std::unordered_map<uint64_t, std::shared_ptr<CompiledFunctionCode>> menu_item_id_and_specific_version_to_patch_function;
|
std::unordered_map<uint64_t, std::shared_ptr<CompiledFunctionCode>> menu_item_id_and_specific_version_to_patch_function;
|
||||||
// Key here is e.g. "PATCHNAME-SPECIFICVERSION", with the latter in hex
|
// Key here is e.g. "PATCHNAME-SPECIFICVERSION", with the latter in hex
|
||||||
std::map<std::string, std::shared_ptr<CompiledFunctionCode>> name_and_specific_version_to_patch_function;
|
std::map<std::string, std::shared_ptr<CompiledFunctionCode>> name_and_specific_version_to_patch_function;
|
||||||
|
|
||||||
std::shared_ptr<const Menu> patch_menu(uint32_t specific_version) const;
|
std::shared_ptr<const Menu> patch_menu(uint32_t specific_version) const;
|
||||||
std::shared_ptr<const Menu> patch_switches_menu(uint32_t specific_version, const std::unordered_set<std::string>& auto_patches_enabled) const;
|
std::shared_ptr<const Menu> patch_switches_menu(uint32_t specific_version, const std::unordered_set<std::string>& auto_patches_enabled) const;
|
||||||
bool patch_menu_empty(uint32_t specific_version) const;
|
bool patch_menu_empty(uint32_t specific_version) const;
|
||||||
|
|
||||||
std::shared_ptr<const CompiledFunctionCode> get_patch(const std::string& name, uint32_t specific_version) const;
|
std::shared_ptr<const CompiledFunctionCode> get_patch(const std::string& name, uint32_t specific_version) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DOLFileIndex {
|
struct DOLFileIndex {
|
||||||
struct File {
|
struct File {
|
||||||
uint32_t menu_item_id;
|
uint32_t menu_item_id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string data;
|
std::string data;
|
||||||
bool is_compressed;
|
bool is_compressed;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::shared_ptr<File>> item_id_to_file;
|
std::vector<std::shared_ptr<File>> item_id_to_file;
|
||||||
std::unordered_map<std::string, std::shared_ptr<File>> name_to_file;
|
std::unordered_map<std::string, std::shared_ptr<File>> name_to_file;
|
||||||
std::shared_ptr<const Menu> menu;
|
std::shared_ptr<const Menu> menu;
|
||||||
|
|
||||||
DOLFileIndex() = default;
|
DOLFileIndex() = default;
|
||||||
explicit DOLFileIndex(const std::string& directory);
|
explicit DOLFileIndex(const std::string& directory);
|
||||||
|
|
||||||
inline bool empty() const {
|
inline bool empty() const {
|
||||||
return this->name_to_file.empty() && this->item_id_to_file.empty();
|
return this->name_to_file.empty() && this->item_id_to_file.empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum);
|
uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum);
|
||||||
|
|||||||
+1031
-1031
File diff suppressed because it is too large
Load Diff
+76
-76
@@ -1,76 +1,76 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/buffer.h>
|
#include <event2/buffer.h>
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
#include <event2/http.h>
|
#include <event2/http.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ProxyServer.hh"
|
#include "ProxyServer.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
class HTTPServer {
|
class HTTPServer {
|
||||||
public:
|
public:
|
||||||
HTTPServer(std::shared_ptr<ServerState> state);
|
HTTPServer(std::shared_ptr<ServerState> state);
|
||||||
HTTPServer(const HTTPServer&) = delete;
|
HTTPServer(const HTTPServer&) = delete;
|
||||||
HTTPServer(HTTPServer&&) = delete;
|
HTTPServer(HTTPServer&&) = delete;
|
||||||
HTTPServer& operator=(const HTTPServer&) = delete;
|
HTTPServer& operator=(const HTTPServer&) = delete;
|
||||||
HTTPServer& operator=(HTTPServer&&) = delete;
|
HTTPServer& operator=(HTTPServer&&) = delete;
|
||||||
virtual ~HTTPServer() = default;
|
virtual ~HTTPServer() = default;
|
||||||
|
|
||||||
void listen(const std::string& socket_path);
|
void listen(const std::string& socket_path);
|
||||||
void listen(const std::string& addr, int port);
|
void listen(const std::string& addr, int port);
|
||||||
void listen(int port);
|
void listen(int port);
|
||||||
void add_socket(int fd);
|
void add_socket(int fd);
|
||||||
|
|
||||||
void schedule_stop();
|
void schedule_stop();
|
||||||
void wait_for_stop();
|
void wait_for_stop();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class http_error : public std::runtime_error {
|
class http_error : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
http_error(int code, const std::string& what);
|
http_error(int code, const std::string& what);
|
||||||
int code;
|
int code;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<ServerState> state;
|
std::shared_ptr<ServerState> state;
|
||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
std::shared_ptr<struct evhttp> http;
|
std::shared_ptr<struct evhttp> http;
|
||||||
std::thread th;
|
std::thread th;
|
||||||
|
|
||||||
void thread_fn();
|
void thread_fn();
|
||||||
|
|
||||||
static void dispatch_handle_request(struct evhttp_request* req, void* ctx);
|
static void dispatch_handle_request(struct evhttp_request* req, void* ctx);
|
||||||
void handle_request(struct evhttp_request* req);
|
void handle_request(struct evhttp_request* req);
|
||||||
|
|
||||||
static const std::unordered_map<int, const char*> explanation_for_response_code;
|
static const std::unordered_map<int, const char*> explanation_for_response_code;
|
||||||
static void send_response(struct evhttp_request* req, int code, const char* content_type, struct evbuffer* b);
|
static void send_response(struct evhttp_request* req, int code, const char* content_type, struct evbuffer* b);
|
||||||
static void send_response(struct evhttp_request* req, int code, const char* content_type, const char* fmt, ...);
|
static void send_response(struct evhttp_request* req, int code, const char* content_type, const char* fmt, ...);
|
||||||
|
|
||||||
static std::unordered_multimap<std::string, std::string> parse_url_params(const std::string& query);
|
static std::unordered_multimap<std::string, std::string> parse_url_params(const std::string& query);
|
||||||
static std::unordered_map<std::string, std::string> parse_url_params_unique(const std::string& query);
|
static std::unordered_map<std::string, std::string> parse_url_params_unique(const std::string& query);
|
||||||
static const std::string& get_url_param(
|
static const std::string& get_url_param(
|
||||||
const std::unordered_multimap<std::string, std::string>& params,
|
const std::unordered_multimap<std::string, std::string>& params,
|
||||||
const std::string& key,
|
const std::string& key,
|
||||||
const std::string* _default = nullptr);
|
const std::string* _default = nullptr);
|
||||||
|
|
||||||
static JSON generate_quest_json_st(std::shared_ptr<const Quest> q);
|
static JSON generate_quest_json_st(std::shared_ptr<const Quest> q);
|
||||||
static JSON generate_client_config_json_st(const Client::Config& config);
|
static JSON generate_client_config_json_st(const Client::Config& config);
|
||||||
static JSON generate_account_json_st(std::shared_ptr<const Account> a);
|
static JSON generate_account_json_st(std::shared_ptr<const Account> a);
|
||||||
static JSON generate_game_client_json_st(std::shared_ptr<const Client> c, std::shared_ptr<const ItemNameIndex> item_name_index);
|
static JSON generate_game_client_json_st(std::shared_ptr<const Client> c, std::shared_ptr<const ItemNameIndex> item_name_index);
|
||||||
static JSON generate_proxy_client_json_st(std::shared_ptr<const ProxyServer::LinkedSession> ses);
|
static JSON generate_proxy_client_json_st(std::shared_ptr<const ProxyServer::LinkedSession> ses);
|
||||||
static JSON generate_lobby_json_st(std::shared_ptr<const Lobby> l, std::shared_ptr<const ItemNameIndex> item_name_index);
|
static JSON generate_lobby_json_st(std::shared_ptr<const Lobby> l, std::shared_ptr<const ItemNameIndex> item_name_index);
|
||||||
JSON generate_game_server_clients_json() const;
|
JSON generate_game_server_clients_json() const;
|
||||||
JSON generate_proxy_server_clients_json() const;
|
JSON generate_proxy_server_clients_json() const;
|
||||||
JSON generate_server_info_json() const;
|
JSON generate_server_info_json() const;
|
||||||
JSON generate_lobbies_json() const;
|
JSON generate_lobbies_json() const;
|
||||||
JSON generate_summary_json() const;
|
JSON generate_summary_json() const;
|
||||||
JSON generate_all_json() const;
|
JSON generate_all_json() const;
|
||||||
|
|
||||||
JSON generate_ep3_cards_json(bool trial) const;
|
JSON generate_ep3_cards_json(bool trial) const;
|
||||||
JSON generate_common_tables_json() const;
|
JSON generate_common_tables_json() const;
|
||||||
JSON generate_rare_tables_json() const;
|
JSON generate_rare_tables_json() const;
|
||||||
JSON generate_rare_table_json(const std::string& table_name) const;
|
JSON generate_rare_table_json(const std::string& table_name) const;
|
||||||
};
|
};
|
||||||
|
|||||||
+73
-73
@@ -1,73 +1,73 @@
|
|||||||
#include "IPV4RangeSet.hh"
|
#include "IPV4RangeSet.hh"
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
IPV4RangeSet::IPV4RangeSet(const JSON& json) {
|
IPV4RangeSet::IPV4RangeSet(const JSON& json) {
|
||||||
for (const auto& it : json.as_list()) {
|
for (const auto& it : json.as_list()) {
|
||||||
// String should be of the form a.b.c.d or a.b.c.d/e
|
// String should be of the form a.b.c.d or a.b.c.d/e
|
||||||
auto tokens = split(it->as_string(), '/');
|
auto tokens = split(it->as_string(), '/');
|
||||||
|
|
||||||
size_t mask_bits;
|
size_t mask_bits;
|
||||||
if (tokens.size() == 1) {
|
if (tokens.size() == 1) {
|
||||||
mask_bits = 32;
|
mask_bits = 32;
|
||||||
} else if (tokens.size() == 2) {
|
} else if (tokens.size() == 2) {
|
||||||
mask_bits = stoul(tokens[1], nullptr, 10);
|
mask_bits = stoul(tokens[1], nullptr, 10);
|
||||||
if (mask_bits > 32) {
|
if (mask_bits > 32) {
|
||||||
throw runtime_error("invalid IPv4 address range");
|
throw runtime_error("invalid IPv4 address range");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw runtime_error("invalid IPv4 address range");
|
throw runtime_error("invalid IPv4 address range");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_tokens = split(tokens[0], '.');
|
auto addr_tokens = split(tokens[0], '.');
|
||||||
if (addr_tokens.size() != 4) {
|
if (addr_tokens.size() != 4) {
|
||||||
throw runtime_error("invalid IPv4 address");
|
throw runtime_error("invalid IPv4 address");
|
||||||
}
|
}
|
||||||
uint32_t addr = 0;
|
uint32_t addr = 0;
|
||||||
for (size_t z = 0; z < 4; z++) {
|
for (size_t z = 0; z < 4; z++) {
|
||||||
size_t end_pos = 0;
|
size_t end_pos = 0;
|
||||||
size_t new_byte = stoul(addr_tokens[z], &end_pos, 10);
|
size_t new_byte = stoul(addr_tokens[z], &end_pos, 10);
|
||||||
if (end_pos != addr_tokens[z].size() || new_byte > 0xFF) {
|
if (end_pos != addr_tokens[z].size() || new_byte > 0xFF) {
|
||||||
throw runtime_error("invalid IPv4 address");
|
throw runtime_error("invalid IPv4 address");
|
||||||
}
|
}
|
||||||
addr = (addr << 8) | new_byte;
|
addr = (addr << 8) | new_byte;
|
||||||
}
|
}
|
||||||
addr &= (0xFFFFFFFF << (32 - mask_bits));
|
addr &= (0xFFFFFFFF << (32 - mask_bits));
|
||||||
|
|
||||||
this->ranges.emplace(addr, mask_bits);
|
this->ranges.emplace(addr, mask_bits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON IPV4RangeSet::json() const {
|
JSON IPV4RangeSet::json() const {
|
||||||
auto ret = JSON::list();
|
auto ret = JSON::list();
|
||||||
for (const auto& it : this->ranges) {
|
for (const auto& it : this->ranges) {
|
||||||
uint32_t addr = it.first;
|
uint32_t addr = it.first;
|
||||||
uint8_t mask_bits = it.second;
|
uint8_t mask_bits = it.second;
|
||||||
ret.emplace_back(string_printf("%hhu.%hhu.%hhu.%hhu/%hhu",
|
ret.emplace_back(string_printf("%hhu.%hhu.%hhu.%hhu/%hhu",
|
||||||
static_cast<uint8_t>((addr >> 24) & 0xFF),
|
static_cast<uint8_t>((addr >> 24) & 0xFF),
|
||||||
static_cast<uint8_t>((addr >> 16) & 0xFF),
|
static_cast<uint8_t>((addr >> 16) & 0xFF),
|
||||||
static_cast<uint8_t>((addr >> 8) & 0xFF),
|
static_cast<uint8_t>((addr >> 8) & 0xFF),
|
||||||
static_cast<uint8_t>(addr & 0xFF),
|
static_cast<uint8_t>(addr & 0xFF),
|
||||||
mask_bits));
|
mask_bits));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IPV4RangeSet::check(uint32_t addr) const {
|
bool IPV4RangeSet::check(uint32_t addr) const {
|
||||||
auto it = this->ranges.upper_bound(addr);
|
auto it = this->ranges.upper_bound(addr);
|
||||||
if (it == this->ranges.begin()) {
|
if (it == this->ranges.begin()) {
|
||||||
return false; // addr is before any range
|
return false; // addr is before any range
|
||||||
}
|
}
|
||||||
const auto& range = *(--it);
|
const auto& range = *(--it);
|
||||||
return (((range.first ^ addr) & (0xFFFFFFFF << (32 - range.second))) == 0);
|
return (((range.first ^ addr) & (0xFFFFFFFF << (32 - range.second))) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IPV4RangeSet::check(const struct sockaddr_storage& ss) const {
|
bool IPV4RangeSet::check(const struct sockaddr_storage& ss) const {
|
||||||
if (ss.ss_family != AF_INET) {
|
if (ss.ss_family != AF_INET) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(&ss);
|
const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(&ss);
|
||||||
return this->check(ntohl(sin->sin_addr.s_addr));
|
return this->check(ntohl(sin->sin_addr.s_addr));
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-18
@@ -1,18 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <phosg/JSON.hh>
|
#include <phosg/JSON.hh>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
class IPV4RangeSet {
|
class IPV4RangeSet {
|
||||||
public:
|
public:
|
||||||
IPV4RangeSet() = default;
|
IPV4RangeSet() = default;
|
||||||
explicit IPV4RangeSet(const JSON& json);
|
explicit IPV4RangeSet(const JSON& json);
|
||||||
|
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
|
|
||||||
bool check(uint32_t addr) const;
|
bool check(uint32_t addr) const;
|
||||||
bool check(const struct sockaddr_storage& ss) const;
|
bool check(const struct sockaddr_storage& ss) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::map<uint32_t, uint8_t> ranges; // {addr: mask_bits}
|
std::map<uint32_t, uint8_t> ranges; // {addr: mask_bits}
|
||||||
};
|
};
|
||||||
|
|||||||
+455
-455
@@ -1,455 +1,455 @@
|
|||||||
#include "IntegralExpression.hh"
|
#include "IntegralExpression.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <phosg/Filesystem.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 <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "CommandFormats.hh"
|
#include "CommandFormats.hh"
|
||||||
#include "Compression.hh"
|
#include "Compression.hh"
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "QuestScript.hh"
|
#include "QuestScript.hh"
|
||||||
#include "SaveFileFormats.hh"
|
#include "SaveFileFormats.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
IntegralExpression::IntegralExpression(const string& text)
|
IntegralExpression::IntegralExpression(const string& text)
|
||||||
: root(this->parse_expr(text)) {}
|
: root(this->parse_expr(text)) {}
|
||||||
|
|
||||||
IntegralExpression::BinaryOperatorNode::BinaryOperatorNode(
|
IntegralExpression::BinaryOperatorNode::BinaryOperatorNode(
|
||||||
Type type, unique_ptr<const Node>&& left, unique_ptr<const Node>&& right)
|
Type type, unique_ptr<const Node>&& left, unique_ptr<const Node>&& right)
|
||||||
: type(type),
|
: type(type),
|
||||||
left(std::move(left)),
|
left(std::move(left)),
|
||||||
right(std::move(right)) {}
|
right(std::move(right)) {}
|
||||||
|
|
||||||
bool IntegralExpression::BinaryOperatorNode::operator==(const Node& other) const {
|
bool IntegralExpression::BinaryOperatorNode::operator==(const Node& other) const {
|
||||||
try {
|
try {
|
||||||
const BinaryOperatorNode& other_bin = dynamic_cast<const BinaryOperatorNode&>(other);
|
const BinaryOperatorNode& other_bin = dynamic_cast<const BinaryOperatorNode&>(other);
|
||||||
return other_bin.type == this->type && *other_bin.left == *this->left && *other_bin.right == *this->right;
|
return other_bin.type == this->type && *other_bin.left == *this->left && *other_bin.right == *this->right;
|
||||||
} catch (const bad_cast&) {
|
} catch (const bad_cast&) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::BinaryOperatorNode::evaluate(const Env& env) const {
|
int64_t IntegralExpression::BinaryOperatorNode::evaluate(const Env& env) const {
|
||||||
switch (this->type) {
|
switch (this->type) {
|
||||||
case Type::LOGICAL_OR:
|
case Type::LOGICAL_OR:
|
||||||
return this->left->evaluate(env) || this->right->evaluate(env);
|
return this->left->evaluate(env) || this->right->evaluate(env);
|
||||||
case Type::LOGICAL_AND:
|
case Type::LOGICAL_AND:
|
||||||
return this->left->evaluate(env) && this->right->evaluate(env);
|
return this->left->evaluate(env) && this->right->evaluate(env);
|
||||||
case Type::BITWISE_OR:
|
case Type::BITWISE_OR:
|
||||||
return this->left->evaluate(env) | this->right->evaluate(env);
|
return this->left->evaluate(env) | this->right->evaluate(env);
|
||||||
case Type::BITWISE_AND:
|
case Type::BITWISE_AND:
|
||||||
return this->left->evaluate(env) & this->right->evaluate(env);
|
return this->left->evaluate(env) & this->right->evaluate(env);
|
||||||
case Type::BITWISE_XOR:
|
case Type::BITWISE_XOR:
|
||||||
return this->left->evaluate(env) ^ this->right->evaluate(env);
|
return this->left->evaluate(env) ^ this->right->evaluate(env);
|
||||||
case Type::LEFT_SHIFT:
|
case Type::LEFT_SHIFT:
|
||||||
return this->left->evaluate(env) << this->right->evaluate(env);
|
return this->left->evaluate(env) << this->right->evaluate(env);
|
||||||
case Type::RIGHT_SHIFT:
|
case Type::RIGHT_SHIFT:
|
||||||
return this->left->evaluate(env) >> this->right->evaluate(env);
|
return this->left->evaluate(env) >> this->right->evaluate(env);
|
||||||
case Type::LESS_THAN:
|
case Type::LESS_THAN:
|
||||||
return this->left->evaluate(env) < this->right->evaluate(env);
|
return this->left->evaluate(env) < this->right->evaluate(env);
|
||||||
case Type::GREATER_THAN:
|
case Type::GREATER_THAN:
|
||||||
return this->left->evaluate(env) > this->right->evaluate(env);
|
return this->left->evaluate(env) > this->right->evaluate(env);
|
||||||
case Type::LESS_OR_EQUAL:
|
case Type::LESS_OR_EQUAL:
|
||||||
return this->left->evaluate(env) <= this->right->evaluate(env);
|
return this->left->evaluate(env) <= this->right->evaluate(env);
|
||||||
case Type::GREATER_OR_EQUAL:
|
case Type::GREATER_OR_EQUAL:
|
||||||
return this->left->evaluate(env) >= this->right->evaluate(env);
|
return this->left->evaluate(env) >= this->right->evaluate(env);
|
||||||
case Type::EQUAL:
|
case Type::EQUAL:
|
||||||
return this->left->evaluate(env) == this->right->evaluate(env);
|
return this->left->evaluate(env) == this->right->evaluate(env);
|
||||||
case Type::NOT_EQUAL:
|
case Type::NOT_EQUAL:
|
||||||
return this->left->evaluate(env) != this->right->evaluate(env);
|
return this->left->evaluate(env) != this->right->evaluate(env);
|
||||||
case Type::ADD:
|
case Type::ADD:
|
||||||
return this->left->evaluate(env) + this->right->evaluate(env);
|
return this->left->evaluate(env) + this->right->evaluate(env);
|
||||||
case Type::SUBTRACT:
|
case Type::SUBTRACT:
|
||||||
return this->left->evaluate(env) - this->right->evaluate(env);
|
return this->left->evaluate(env) - this->right->evaluate(env);
|
||||||
case Type::MULTIPLY:
|
case Type::MULTIPLY:
|
||||||
return this->left->evaluate(env) * this->right->evaluate(env);
|
return this->left->evaluate(env) * this->right->evaluate(env);
|
||||||
case Type::DIVIDE:
|
case Type::DIVIDE:
|
||||||
return this->left->evaluate(env) / this->right->evaluate(env);
|
return this->left->evaluate(env) / this->right->evaluate(env);
|
||||||
case Type::MODULUS:
|
case Type::MODULUS:
|
||||||
return this->left->evaluate(env) % this->right->evaluate(env);
|
return this->left->evaluate(env) % this->right->evaluate(env);
|
||||||
default:
|
default:
|
||||||
throw logic_error("invalid binary operator type");
|
throw logic_error("invalid binary operator type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::BinaryOperatorNode::str() const {
|
string IntegralExpression::BinaryOperatorNode::str() const {
|
||||||
switch (this->type) {
|
switch (this->type) {
|
||||||
case Type::LOGICAL_OR:
|
case Type::LOGICAL_OR:
|
||||||
return "(" + this->left->str() + ") || (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") || (" + this->right->str() + ")";
|
||||||
case Type::LOGICAL_AND:
|
case Type::LOGICAL_AND:
|
||||||
return "(" + this->left->str() + ") && (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") && (" + this->right->str() + ")";
|
||||||
case Type::BITWISE_OR:
|
case Type::BITWISE_OR:
|
||||||
return "(" + this->left->str() + ") | (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") | (" + this->right->str() + ")";
|
||||||
case Type::BITWISE_AND:
|
case Type::BITWISE_AND:
|
||||||
return "(" + this->left->str() + ") & (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") & (" + this->right->str() + ")";
|
||||||
case Type::BITWISE_XOR:
|
case Type::BITWISE_XOR:
|
||||||
return "(" + this->left->str() + ") ^ (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") ^ (" + this->right->str() + ")";
|
||||||
case Type::LEFT_SHIFT:
|
case Type::LEFT_SHIFT:
|
||||||
return "(" + this->left->str() + ") << (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") << (" + this->right->str() + ")";
|
||||||
case Type::RIGHT_SHIFT:
|
case Type::RIGHT_SHIFT:
|
||||||
return "(" + this->left->str() + ") >> (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") >> (" + this->right->str() + ")";
|
||||||
case Type::LESS_THAN:
|
case Type::LESS_THAN:
|
||||||
return "(" + this->left->str() + ") < (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") < (" + this->right->str() + ")";
|
||||||
case Type::GREATER_THAN:
|
case Type::GREATER_THAN:
|
||||||
return "(" + this->left->str() + ") > (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") > (" + this->right->str() + ")";
|
||||||
case Type::LESS_OR_EQUAL:
|
case Type::LESS_OR_EQUAL:
|
||||||
return "(" + this->left->str() + ") <= (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") <= (" + this->right->str() + ")";
|
||||||
case Type::GREATER_OR_EQUAL:
|
case Type::GREATER_OR_EQUAL:
|
||||||
return "(" + this->left->str() + ") >= (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") >= (" + this->right->str() + ")";
|
||||||
case Type::EQUAL:
|
case Type::EQUAL:
|
||||||
return "(" + this->left->str() + ") == (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") == (" + this->right->str() + ")";
|
||||||
case Type::NOT_EQUAL:
|
case Type::NOT_EQUAL:
|
||||||
return "(" + this->left->str() + ") != (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") != (" + this->right->str() + ")";
|
||||||
case Type::ADD:
|
case Type::ADD:
|
||||||
return "(" + this->left->str() + ") + (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") + (" + this->right->str() + ")";
|
||||||
case Type::SUBTRACT:
|
case Type::SUBTRACT:
|
||||||
return "(" + this->left->str() + ") - (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") - (" + this->right->str() + ")";
|
||||||
case Type::MULTIPLY:
|
case Type::MULTIPLY:
|
||||||
return "(" + this->left->str() + ") * (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") * (" + this->right->str() + ")";
|
||||||
case Type::DIVIDE:
|
case Type::DIVIDE:
|
||||||
return "(" + this->left->str() + ") / (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") / (" + this->right->str() + ")";
|
||||||
case Type::MODULUS:
|
case Type::MODULUS:
|
||||||
return "(" + this->left->str() + ") % (" + this->right->str() + ")";
|
return "(" + this->left->str() + ") % (" + this->right->str() + ")";
|
||||||
default:
|
default:
|
||||||
throw logic_error("invalid binary operator type");
|
throw logic_error("invalid binary operator type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegralExpression::UnaryOperatorNode::UnaryOperatorNode(Type type, unique_ptr<const Node>&& sub)
|
IntegralExpression::UnaryOperatorNode::UnaryOperatorNode(Type type, unique_ptr<const Node>&& sub)
|
||||||
: type(type),
|
: type(type),
|
||||||
sub(std::move(sub)) {}
|
sub(std::move(sub)) {}
|
||||||
|
|
||||||
bool IntegralExpression::UnaryOperatorNode::operator==(const Node& other) const {
|
bool IntegralExpression::UnaryOperatorNode::operator==(const Node& other) const {
|
||||||
try {
|
try {
|
||||||
const UnaryOperatorNode& other_un = dynamic_cast<const UnaryOperatorNode&>(other);
|
const UnaryOperatorNode& other_un = dynamic_cast<const UnaryOperatorNode&>(other);
|
||||||
return other_un.type == this->type && *other_un.sub == *this->sub;
|
return other_un.type == this->type && *other_un.sub == *this->sub;
|
||||||
} catch (const bad_cast&) {
|
} catch (const bad_cast&) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::UnaryOperatorNode::evaluate(const Env& env) const {
|
int64_t IntegralExpression::UnaryOperatorNode::evaluate(const Env& env) const {
|
||||||
switch (this->type) {
|
switch (this->type) {
|
||||||
case Type::LOGICAL_NOT:
|
case Type::LOGICAL_NOT:
|
||||||
return !this->sub->evaluate(env);
|
return !this->sub->evaluate(env);
|
||||||
case Type::BITWISE_NOT:
|
case Type::BITWISE_NOT:
|
||||||
return ~this->sub->evaluate(env);
|
return ~this->sub->evaluate(env);
|
||||||
case Type::NEGATIVE:
|
case Type::NEGATIVE:
|
||||||
return -this->sub->evaluate(env);
|
return -this->sub->evaluate(env);
|
||||||
default:
|
default:
|
||||||
throw logic_error("invalid unary operator type");
|
throw logic_error("invalid unary operator type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::UnaryOperatorNode::str() const {
|
string IntegralExpression::UnaryOperatorNode::str() const {
|
||||||
switch (this->type) {
|
switch (this->type) {
|
||||||
case Type::LOGICAL_NOT:
|
case Type::LOGICAL_NOT:
|
||||||
return "!(" + this->sub->str() + ")";
|
return "!(" + this->sub->str() + ")";
|
||||||
case Type::BITWISE_NOT:
|
case Type::BITWISE_NOT:
|
||||||
return "~(" + this->sub->str() + ")";
|
return "~(" + this->sub->str() + ")";
|
||||||
case Type::NEGATIVE:
|
case Type::NEGATIVE:
|
||||||
return "-(" + this->sub->str() + ")";
|
return "-(" + this->sub->str() + ")";
|
||||||
default:
|
default:
|
||||||
throw logic_error("invalid unary operator type");
|
throw logic_error("invalid unary operator type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegralExpression::FlagLookupNode::FlagLookupNode(uint16_t flag_index)
|
IntegralExpression::FlagLookupNode::FlagLookupNode(uint16_t flag_index)
|
||||||
: flag_index(flag_index) {}
|
: flag_index(flag_index) {}
|
||||||
|
|
||||||
bool IntegralExpression::FlagLookupNode::operator==(const Node& other) const {
|
bool IntegralExpression::FlagLookupNode::operator==(const Node& other) const {
|
||||||
try {
|
try {
|
||||||
const FlagLookupNode& other_flag = dynamic_cast<const FlagLookupNode&>(other);
|
const FlagLookupNode& other_flag = dynamic_cast<const FlagLookupNode&>(other);
|
||||||
return other_flag.flag_index == this->flag_index;
|
return other_flag.flag_index == this->flag_index;
|
||||||
} catch (const bad_cast&) {
|
} catch (const bad_cast&) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::FlagLookupNode::evaluate(const Env& env) const {
|
int64_t IntegralExpression::FlagLookupNode::evaluate(const Env& env) const {
|
||||||
if (!env.flags) {
|
if (!env.flags) {
|
||||||
throw runtime_error("quest flags not available");
|
throw runtime_error("quest flags not available");
|
||||||
}
|
}
|
||||||
return env.flags->get(this->flag_index) ? 1 : 0;
|
return env.flags->get(this->flag_index) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::FlagLookupNode::str() const {
|
string IntegralExpression::FlagLookupNode::str() const {
|
||||||
return string_printf("F_%04hX", this->flag_index);
|
return string_printf("F_%04hX", this->flag_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegralExpression::ChallengeCompletionLookupNode::ChallengeCompletionLookupNode(
|
IntegralExpression::ChallengeCompletionLookupNode::ChallengeCompletionLookupNode(
|
||||||
Episode episode, uint8_t stage_index)
|
Episode episode, uint8_t stage_index)
|
||||||
: episode(episode),
|
: episode(episode),
|
||||||
stage_index(stage_index) {}
|
stage_index(stage_index) {}
|
||||||
|
|
||||||
bool IntegralExpression::ChallengeCompletionLookupNode::operator==(const Node& other) const {
|
bool IntegralExpression::ChallengeCompletionLookupNode::operator==(const Node& other) const {
|
||||||
try {
|
try {
|
||||||
const ChallengeCompletionLookupNode& other_cc = dynamic_cast<const ChallengeCompletionLookupNode&>(other);
|
const ChallengeCompletionLookupNode& other_cc = dynamic_cast<const ChallengeCompletionLookupNode&>(other);
|
||||||
return other_cc.episode == this->episode && other_cc.stage_index == this->stage_index;
|
return other_cc.episode == this->episode && other_cc.stage_index == this->stage_index;
|
||||||
} catch (const bad_cast&) {
|
} catch (const bad_cast&) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::ChallengeCompletionLookupNode::evaluate(const Env& env) const {
|
int64_t IntegralExpression::ChallengeCompletionLookupNode::evaluate(const Env& env) const {
|
||||||
if (!env.challenge_records) {
|
if (!env.challenge_records) {
|
||||||
throw runtime_error("challenge records not available");
|
throw runtime_error("challenge records not available");
|
||||||
}
|
}
|
||||||
if (this->episode == Episode::EP1) {
|
if (this->episode == Episode::EP1) {
|
||||||
return env.challenge_records->times_ep1_online.at(this->stage_index).has_value();
|
return env.challenge_records->times_ep1_online.at(this->stage_index).has_value();
|
||||||
} else if (this->episode == Episode::EP2) {
|
} else if (this->episode == Episode::EP2) {
|
||||||
return env.challenge_records->times_ep2_online.at(this->stage_index).has_value();
|
return env.challenge_records->times_ep2_online.at(this->stage_index).has_value();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::ChallengeCompletionLookupNode::str() const {
|
string IntegralExpression::ChallengeCompletionLookupNode::str() const {
|
||||||
return string_printf("CC_%s_%hhu", abbreviation_for_episode(this->episode), static_cast<uint8_t>(this->stage_index + 1));
|
return string_printf("CC_%s_%hhu", abbreviation_for_episode(this->episode), static_cast<uint8_t>(this->stage_index + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegralExpression::TeamRewardLookupNode::TeamRewardLookupNode(const string& reward_name)
|
IntegralExpression::TeamRewardLookupNode::TeamRewardLookupNode(const string& reward_name)
|
||||||
: reward_name(reward_name) {}
|
: reward_name(reward_name) {}
|
||||||
|
|
||||||
bool IntegralExpression::TeamRewardLookupNode::operator==(const Node& other) const {
|
bool IntegralExpression::TeamRewardLookupNode::operator==(const Node& other) const {
|
||||||
try {
|
try {
|
||||||
const TeamRewardLookupNode& other_team_reward = dynamic_cast<const TeamRewardLookupNode&>(other);
|
const TeamRewardLookupNode& other_team_reward = dynamic_cast<const TeamRewardLookupNode&>(other);
|
||||||
return other_team_reward.reward_name == this->reward_name;
|
return other_team_reward.reward_name == this->reward_name;
|
||||||
} catch (const bad_cast&) {
|
} catch (const bad_cast&) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::TeamRewardLookupNode::evaluate(const Env& env) const {
|
int64_t IntegralExpression::TeamRewardLookupNode::evaluate(const Env& env) const {
|
||||||
return (env.team && env.team->has_reward(this->reward_name)) ? 1 : 0;
|
return (env.team && env.team->has_reward(this->reward_name)) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::TeamRewardLookupNode::str() const {
|
string IntegralExpression::TeamRewardLookupNode::str() const {
|
||||||
return "T_" + this->reward_name;
|
return "T_" + this->reward_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegralExpression::NumPlayersLookupNode::NumPlayersLookupNode() {}
|
IntegralExpression::NumPlayersLookupNode::NumPlayersLookupNode() {}
|
||||||
|
|
||||||
bool IntegralExpression::NumPlayersLookupNode::operator==(const Node& other) const {
|
bool IntegralExpression::NumPlayersLookupNode::operator==(const Node& other) const {
|
||||||
return dynamic_cast<const NumPlayersLookupNode*>(&other) != nullptr;
|
return dynamic_cast<const NumPlayersLookupNode*>(&other) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::NumPlayersLookupNode::evaluate(const Env& env) const {
|
int64_t IntegralExpression::NumPlayersLookupNode::evaluate(const Env& env) const {
|
||||||
return env.num_players;
|
return env.num_players;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::NumPlayersLookupNode::str() const {
|
string IntegralExpression::NumPlayersLookupNode::str() const {
|
||||||
return "V_NumPlayers";
|
return "V_NumPlayers";
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegralExpression::EventLookupNode::EventLookupNode() {}
|
IntegralExpression::EventLookupNode::EventLookupNode() {}
|
||||||
|
|
||||||
bool IntegralExpression::EventLookupNode::operator==(const Node& other) const {
|
bool IntegralExpression::EventLookupNode::operator==(const Node& other) const {
|
||||||
return dynamic_cast<const EventLookupNode*>(&other) != nullptr;
|
return dynamic_cast<const EventLookupNode*>(&other) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::EventLookupNode::evaluate(const Env& env) const {
|
int64_t IntegralExpression::EventLookupNode::evaluate(const Env& env) const {
|
||||||
return env.event;
|
return env.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::EventLookupNode::str() const {
|
string IntegralExpression::EventLookupNode::str() const {
|
||||||
return "V_Event";
|
return "V_Event";
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegralExpression::V1PresenceLookupNode::V1PresenceLookupNode() {}
|
IntegralExpression::V1PresenceLookupNode::V1PresenceLookupNode() {}
|
||||||
|
|
||||||
bool IntegralExpression::V1PresenceLookupNode::operator==(const Node& other) const {
|
bool IntegralExpression::V1PresenceLookupNode::operator==(const Node& other) const {
|
||||||
return dynamic_cast<const V1PresenceLookupNode*>(&other) != nullptr;
|
return dynamic_cast<const V1PresenceLookupNode*>(&other) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::V1PresenceLookupNode::evaluate(const Env& env) const {
|
int64_t IntegralExpression::V1PresenceLookupNode::evaluate(const Env& env) const {
|
||||||
return env.v1_present ? 1 : 0;
|
return env.v1_present ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::V1PresenceLookupNode::str() const {
|
string IntegralExpression::V1PresenceLookupNode::str() const {
|
||||||
return "V_V1Present";
|
return "V_V1Present";
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegralExpression::ConstantNode::ConstantNode(int64_t value)
|
IntegralExpression::ConstantNode::ConstantNode(int64_t value)
|
||||||
: value(value) {}
|
: value(value) {}
|
||||||
|
|
||||||
bool IntegralExpression::ConstantNode::operator==(const Node& other) const {
|
bool IntegralExpression::ConstantNode::operator==(const Node& other) const {
|
||||||
try {
|
try {
|
||||||
const ConstantNode& other_const = dynamic_cast<const ConstantNode&>(other);
|
const ConstantNode& other_const = dynamic_cast<const ConstantNode&>(other);
|
||||||
return other_const.value == this->value;
|
return other_const.value == this->value;
|
||||||
} catch (const bad_cast&) {
|
} catch (const bad_cast&) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t IntegralExpression::ConstantNode::evaluate(const Env&) const {
|
int64_t IntegralExpression::ConstantNode::evaluate(const Env&) const {
|
||||||
return this->value;
|
return this->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IntegralExpression::ConstantNode::str() const {
|
string IntegralExpression::ConstantNode::str() const {
|
||||||
return string_printf("%" PRId64, this->value);
|
return string_printf("%" PRId64, this->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string_view text) {
|
unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string_view text) {
|
||||||
// Strip off spaces and fully-enclosing parentheses
|
// Strip off spaces and fully-enclosing parentheses
|
||||||
for (;;) {
|
for (;;) {
|
||||||
size_t starting_size = text.size();
|
size_t starting_size = text.size();
|
||||||
while (text.at(0) == ' ') {
|
while (text.at(0) == ' ') {
|
||||||
text = text.substr(1);
|
text = text.substr(1);
|
||||||
}
|
}
|
||||||
while (text.at(text.size() - 1) == ' ') {
|
while (text.at(text.size() - 1) == ' ') {
|
||||||
text = text.substr(0, text.size() - 1);
|
text = text.substr(0, text.size() - 1);
|
||||||
}
|
}
|
||||||
if (text.at(0) == '(' && text.at(text.size() - 1) == ')') {
|
if (text.at(0) == '(' && text.at(text.size() - 1) == ')') {
|
||||||
// It doesn't suffice to just check the first ant last characters, since
|
// It doesn't suffice to just check the first ant last characters, since
|
||||||
// text could be like "(a) && (b)". Instead, we ignore the first and last
|
// text could be like "(a) && (b)". Instead, we ignore the first and last
|
||||||
// characters, and don't strip anything if the internal parentheses are
|
// characters, and don't strip anything if the internal parentheses are
|
||||||
// unbalanced.
|
// unbalanced.
|
||||||
size_t paren_level = 1;
|
size_t paren_level = 1;
|
||||||
for (size_t z = 1; z < text.size() - 1; z++) {
|
for (size_t z = 1; z < text.size() - 1; z++) {
|
||||||
if (text[z] == '(') {
|
if (text[z] == '(') {
|
||||||
paren_level++;
|
paren_level++;
|
||||||
} else if (text[z] == ')') {
|
} else if (text[z] == ')') {
|
||||||
paren_level--;
|
paren_level--;
|
||||||
if (paren_level == 0) {
|
if (paren_level == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (paren_level > 0) {
|
if (paren_level > 0) {
|
||||||
text = text.substr(1, text.size() - 2);
|
text = text.substr(1, text.size() - 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (text.size() == starting_size) {
|
if (text.size() == starting_size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (text.empty()) {
|
if (text.empty()) {
|
||||||
throw runtime_error("invalid expression");
|
throw runtime_error("invalid expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for binary operators at the root level
|
// Check for binary operators at the root level
|
||||||
using BinType = BinaryOperatorNode::Type;
|
using BinType = BinaryOperatorNode::Type;
|
||||||
static const vector<vector<pair<std::string, BinaryOperatorNode::Type>>> binary_operator_levels = {
|
static const vector<vector<pair<std::string, BinaryOperatorNode::Type>>> binary_operator_levels = {
|
||||||
{{make_pair("*", BinType::MULTIPLY)}, {make_pair("/", BinType::DIVIDE)}, {make_pair("%", BinType::MODULUS)}},
|
{{make_pair("*", BinType::MULTIPLY)}, {make_pair("/", BinType::DIVIDE)}, {make_pair("%", BinType::MODULUS)}},
|
||||||
{{make_pair("+", BinType::ADD)}, {make_pair("-", BinType::SUBTRACT)}},
|
{{make_pair("+", BinType::ADD)}, {make_pair("-", BinType::SUBTRACT)}},
|
||||||
{{make_pair("<<", BinType::LEFT_SHIFT)}, {make_pair(">>", BinType::RIGHT_SHIFT)}},
|
{{make_pair("<<", BinType::LEFT_SHIFT)}, {make_pair(">>", BinType::RIGHT_SHIFT)}},
|
||||||
{{make_pair("<=", BinType::LESS_OR_EQUAL)}, {make_pair(">=", BinType::GREATER_OR_EQUAL)}, {make_pair("<", BinType::LESS_THAN)}, {make_pair(">", BinType::GREATER_THAN)}},
|
{{make_pair("<=", BinType::LESS_OR_EQUAL)}, {make_pair(">=", BinType::GREATER_OR_EQUAL)}, {make_pair("<", BinType::LESS_THAN)}, {make_pair(">", BinType::GREATER_THAN)}},
|
||||||
{{make_pair("==", BinType::EQUAL)}, {make_pair("!=", BinType::NOT_EQUAL)}},
|
{{make_pair("==", BinType::EQUAL)}, {make_pair("!=", BinType::NOT_EQUAL)}},
|
||||||
{{make_pair("&", BinType::BITWISE_AND)}},
|
{{make_pair("&", BinType::BITWISE_AND)}},
|
||||||
{{make_pair("^", BinType::BITWISE_XOR)}},
|
{{make_pair("^", BinType::BITWISE_XOR)}},
|
||||||
{{make_pair("|", BinType::BITWISE_OR)}},
|
{{make_pair("|", BinType::BITWISE_OR)}},
|
||||||
{{make_pair("&&", BinType::LOGICAL_AND)}},
|
{{make_pair("&&", BinType::LOGICAL_AND)}},
|
||||||
{{make_pair("||", BinType::LOGICAL_OR)}},
|
{{make_pair("||", BinType::LOGICAL_OR)}},
|
||||||
};
|
};
|
||||||
for (const auto& operators : binary_operator_levels) {
|
for (const auto& operators : binary_operator_levels) {
|
||||||
size_t paren_level = 0;
|
size_t paren_level = 0;
|
||||||
for (size_t z = 0; z < text.size() - 1; z++) {
|
for (size_t z = 0; z < text.size() - 1; z++) {
|
||||||
if (text[z] == '(') {
|
if (text[z] == '(') {
|
||||||
paren_level++;
|
paren_level++;
|
||||||
continue;
|
continue;
|
||||||
} else if (text[z] == ')') {
|
} else if (text[z] == ')') {
|
||||||
paren_level--;
|
paren_level--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!paren_level) {
|
if (!paren_level) {
|
||||||
for (const auto& oper : operators) {
|
for (const auto& oper : operators) {
|
||||||
// Awful hack (because I'm too lazy to add a tokenization step): if
|
// Awful hack (because I'm too lazy to add a tokenization step): if
|
||||||
// the operator is followed or preceded by another copy of itself,
|
// the operator is followed or preceded by another copy of itself,
|
||||||
// don't match it (this prevents us from matching & when the token is
|
// don't match it (this prevents us from matching & when the token is
|
||||||
// actually &&)
|
// actually &&)
|
||||||
if ((text.size() > z + oper.first.size()) &&
|
if ((text.size() > z + oper.first.size()) &&
|
||||||
((z < oper.first.size()) || (text.compare(z - oper.first.size(), oper.first.size(), oper.first) != 0)) &&
|
((z < oper.first.size()) || (text.compare(z - oper.first.size(), oper.first.size(), oper.first) != 0)) &&
|
||||||
(text.compare(z, oper.first.size(), oper.first) == 0) &&
|
(text.compare(z, oper.first.size(), oper.first) == 0) &&
|
||||||
(text.compare(z + oper.first.size(), oper.first.size(), oper.first) != 0)) {
|
(text.compare(z + oper.first.size(), oper.first.size(), oper.first) != 0)) {
|
||||||
auto left = IntegralExpression::parse_expr(text.substr(0, z));
|
auto left = IntegralExpression::parse_expr(text.substr(0, z));
|
||||||
auto right = IntegralExpression::parse_expr(text.substr(z + oper.first.size()));
|
auto right = IntegralExpression::parse_expr(text.substr(z + oper.first.size()));
|
||||||
return make_unique<BinaryOperatorNode>(oper.second, std::move(left), std::move(right));
|
return make_unique<BinaryOperatorNode>(oper.second, std::move(left), std::move(right));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for unary operators
|
// Check for unary operators
|
||||||
if (text[0] == '!') {
|
if (text[0] == '!') {
|
||||||
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::LOGICAL_NOT,
|
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::LOGICAL_NOT,
|
||||||
IntegralExpression::parse_expr(text.substr(1)));
|
IntegralExpression::parse_expr(text.substr(1)));
|
||||||
} else if (text[0] == '~') {
|
} else if (text[0] == '~') {
|
||||||
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::BITWISE_NOT,
|
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::BITWISE_NOT,
|
||||||
IntegralExpression::parse_expr(text.substr(1)));
|
IntegralExpression::parse_expr(text.substr(1)));
|
||||||
} else if (text[0] == '-') {
|
} else if (text[0] == '-') {
|
||||||
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::NEGATIVE,
|
return make_unique<UnaryOperatorNode>(UnaryOperatorNode::Type::NEGATIVE,
|
||||||
IntegralExpression::parse_expr(text.substr(1)));
|
IntegralExpression::parse_expr(text.substr(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for env lookups
|
// Check for env lookups
|
||||||
if (text.starts_with("F_")) {
|
if (text.starts_with("F_")) {
|
||||||
char* endptr = nullptr;
|
char* endptr = nullptr;
|
||||||
uint64_t flag = strtoul(text.data() + 2, &endptr, 16);
|
uint64_t flag = strtoul(text.data() + 2, &endptr, 16);
|
||||||
if (endptr != text.data() + text.size()) {
|
if (endptr != text.data() + text.size()) {
|
||||||
throw runtime_error("invalid flag lookup token");
|
throw runtime_error("invalid flag lookup token");
|
||||||
}
|
}
|
||||||
if (flag >= 0x400) {
|
if (flag >= 0x400) {
|
||||||
throw runtime_error("invalid flag index");
|
throw runtime_error("invalid flag index");
|
||||||
}
|
}
|
||||||
return make_unique<FlagLookupNode>(flag);
|
return make_unique<FlagLookupNode>(flag);
|
||||||
}
|
}
|
||||||
if (text.starts_with("CC_")) {
|
if (text.starts_with("CC_")) {
|
||||||
Episode episode;
|
Episode episode;
|
||||||
if (text.starts_with("CC_Ep1_")) {
|
if (text.starts_with("CC_Ep1_")) {
|
||||||
episode = Episode::EP1;
|
episode = Episode::EP1;
|
||||||
} else if (text.starts_with("CC_Ep2_")) {
|
} else if (text.starts_with("CC_Ep2_")) {
|
||||||
episode = Episode::EP2;
|
episode = Episode::EP2;
|
||||||
} else {
|
} else {
|
||||||
throw runtime_error("invalid challenge episode");
|
throw runtime_error("invalid challenge episode");
|
||||||
}
|
}
|
||||||
char* endptr = nullptr;
|
char* endptr = nullptr;
|
||||||
uint64_t stage_index = strtoul(text.data() + 7, &endptr, 0) - 1;
|
uint64_t stage_index = strtoul(text.data() + 7, &endptr, 0) - 1;
|
||||||
if (endptr != text.data() + text.size()) {
|
if (endptr != text.data() + text.size()) {
|
||||||
throw runtime_error("invalid challenge completion lookup token");
|
throw runtime_error("invalid challenge completion lookup token");
|
||||||
}
|
}
|
||||||
if ((episode == Episode::EP1 && stage_index > 8) || (episode == Episode::EP2 && stage_index > 4)) {
|
if ((episode == Episode::EP1 && stage_index > 8) || (episode == Episode::EP2 && stage_index > 4)) {
|
||||||
throw runtime_error("invalid challenge stage index");
|
throw runtime_error("invalid challenge stage index");
|
||||||
}
|
}
|
||||||
return make_unique<ChallengeCompletionLookupNode>(episode, stage_index);
|
return make_unique<ChallengeCompletionLookupNode>(episode, stage_index);
|
||||||
}
|
}
|
||||||
if (text.starts_with("T_")) {
|
if (text.starts_with("T_")) {
|
||||||
return make_unique<TeamRewardLookupNode>(string(text.substr(2)));
|
return make_unique<TeamRewardLookupNode>(string(text.substr(2)));
|
||||||
}
|
}
|
||||||
if (text == "V_NumPlayers") {
|
if (text == "V_NumPlayers") {
|
||||||
return make_unique<NumPlayersLookupNode>();
|
return make_unique<NumPlayersLookupNode>();
|
||||||
}
|
}
|
||||||
if (text == "V_Event") {
|
if (text == "V_Event") {
|
||||||
return make_unique<EventLookupNode>();
|
return make_unique<EventLookupNode>();
|
||||||
}
|
}
|
||||||
if (text == "V_V1Present") {
|
if (text == "V_V1Present") {
|
||||||
return make_unique<V1PresenceLookupNode>();
|
return make_unique<V1PresenceLookupNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for constants
|
// Check for constants
|
||||||
if (text == "true") {
|
if (text == "true") {
|
||||||
return make_unique<ConstantNode>(1);
|
return make_unique<ConstantNode>(1);
|
||||||
}
|
}
|
||||||
if (text == "false") {
|
if (text == "false") {
|
||||||
return make_unique<ConstantNode>(0);
|
return make_unique<ConstantNode>(0);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
size_t endpos;
|
size_t endpos;
|
||||||
int64_t v = stoll(string(text), &endpos, 0);
|
int64_t v = stoll(string(text), &endpos, 0);
|
||||||
if (endpos == text.size()) {
|
if (endpos == text.size()) {
|
||||||
return make_unique<ConstantNode>(v);
|
return make_unique<ConstantNode>(v);
|
||||||
}
|
}
|
||||||
} catch (const exception&) {
|
} catch (const exception&) {
|
||||||
}
|
}
|
||||||
throw runtime_error("unparseable expression");
|
throw runtime_error("unparseable expression");
|
||||||
}
|
}
|
||||||
|
|||||||
+188
-188
@@ -1,188 +1,188 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "PlayerSubordinates.hh"
|
#include "PlayerSubordinates.hh"
|
||||||
#include "QuestScript.hh"
|
#include "QuestScript.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "TeamIndex.hh"
|
#include "TeamIndex.hh"
|
||||||
|
|
||||||
class IntegralExpression {
|
class IntegralExpression {
|
||||||
public:
|
public:
|
||||||
struct Env {
|
struct Env {
|
||||||
const QuestFlagsForDifficulty* flags;
|
const QuestFlagsForDifficulty* flags;
|
||||||
const PlayerRecordsChallengeBB* challenge_records;
|
const PlayerRecordsChallengeBB* challenge_records;
|
||||||
std::shared_ptr<const TeamIndex::Team> team;
|
std::shared_ptr<const TeamIndex::Team> team;
|
||||||
size_t num_players;
|
size_t num_players;
|
||||||
uint8_t event;
|
uint8_t event;
|
||||||
bool v1_present;
|
bool v1_present;
|
||||||
};
|
};
|
||||||
|
|
||||||
IntegralExpression(const std::string& text);
|
IntegralExpression(const std::string& text);
|
||||||
~IntegralExpression() = default;
|
~IntegralExpression() = default;
|
||||||
inline bool operator==(const IntegralExpression& other) const {
|
inline bool operator==(const IntegralExpression& other) const {
|
||||||
return this->root->operator==(*other.root);
|
return this->root->operator==(*other.root);
|
||||||
}
|
}
|
||||||
inline bool operator!=(const IntegralExpression& other) const {
|
inline bool operator!=(const IntegralExpression& other) const {
|
||||||
return !this->operator==(other);
|
return !this->operator==(other);
|
||||||
}
|
}
|
||||||
inline int64_t evaluate(const Env& env) const {
|
inline int64_t evaluate(const Env& env) const {
|
||||||
return this->root->evaluate(env);
|
return this->root->evaluate(env);
|
||||||
}
|
}
|
||||||
inline std::string str() const {
|
inline std::string str() const {
|
||||||
return this->root->str();
|
return this->root->str();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class Node {
|
class Node {
|
||||||
public:
|
public:
|
||||||
virtual ~Node() = default;
|
virtual ~Node() = default;
|
||||||
virtual bool operator==(const Node& other) const = 0;
|
virtual bool operator==(const Node& other) const = 0;
|
||||||
inline bool operator!=(const Node& other) const {
|
inline bool operator!=(const Node& other) const {
|
||||||
return !this->operator==(other);
|
return !this->operator==(other);
|
||||||
}
|
}
|
||||||
virtual int64_t evaluate(const Env& env) const = 0;
|
virtual int64_t evaluate(const Env& env) const = 0;
|
||||||
virtual std::string str() const = 0;
|
virtual std::string str() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Node() = default;
|
Node() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BinaryOperatorNode : public Node {
|
class BinaryOperatorNode : public Node {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
LOGICAL_OR = 0,
|
LOGICAL_OR = 0,
|
||||||
LOGICAL_AND,
|
LOGICAL_AND,
|
||||||
BITWISE_OR,
|
BITWISE_OR,
|
||||||
BITWISE_AND,
|
BITWISE_AND,
|
||||||
BITWISE_XOR,
|
BITWISE_XOR,
|
||||||
LEFT_SHIFT,
|
LEFT_SHIFT,
|
||||||
RIGHT_SHIFT,
|
RIGHT_SHIFT,
|
||||||
LESS_THAN,
|
LESS_THAN,
|
||||||
GREATER_THAN,
|
GREATER_THAN,
|
||||||
LESS_OR_EQUAL,
|
LESS_OR_EQUAL,
|
||||||
GREATER_OR_EQUAL,
|
GREATER_OR_EQUAL,
|
||||||
EQUAL,
|
EQUAL,
|
||||||
NOT_EQUAL,
|
NOT_EQUAL,
|
||||||
ADD,
|
ADD,
|
||||||
SUBTRACT,
|
SUBTRACT,
|
||||||
MULTIPLY,
|
MULTIPLY,
|
||||||
DIVIDE,
|
DIVIDE,
|
||||||
MODULUS,
|
MODULUS,
|
||||||
};
|
};
|
||||||
BinaryOperatorNode(Type type, std::unique_ptr<const Node>&& left, std::unique_ptr<const Node>&& right);
|
BinaryOperatorNode(Type type, std::unique_ptr<const Node>&& left, std::unique_ptr<const Node>&& right);
|
||||||
virtual ~BinaryOperatorNode() = default;
|
virtual ~BinaryOperatorNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Type type;
|
Type type;
|
||||||
std::unique_ptr<const Node> left;
|
std::unique_ptr<const Node> left;
|
||||||
std::unique_ptr<const Node> right;
|
std::unique_ptr<const Node> right;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UnaryOperatorNode : public Node {
|
class UnaryOperatorNode : public Node {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
LOGICAL_NOT = 0,
|
LOGICAL_NOT = 0,
|
||||||
BITWISE_NOT,
|
BITWISE_NOT,
|
||||||
NEGATIVE,
|
NEGATIVE,
|
||||||
};
|
};
|
||||||
UnaryOperatorNode(Type type, std::unique_ptr<const Node>&& sub);
|
UnaryOperatorNode(Type type, std::unique_ptr<const Node>&& sub);
|
||||||
virtual ~UnaryOperatorNode() = default;
|
virtual ~UnaryOperatorNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Type type;
|
Type type;
|
||||||
std::unique_ptr<const Node> sub;
|
std::unique_ptr<const Node> sub;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FlagLookupNode : public Node {
|
class FlagLookupNode : public Node {
|
||||||
public:
|
public:
|
||||||
FlagLookupNode(uint16_t flag_index);
|
FlagLookupNode(uint16_t flag_index);
|
||||||
virtual ~FlagLookupNode() = default;
|
virtual ~FlagLookupNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint16_t flag_index;
|
uint16_t flag_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChallengeCompletionLookupNode : public Node {
|
class ChallengeCompletionLookupNode : public Node {
|
||||||
public:
|
public:
|
||||||
ChallengeCompletionLookupNode(Episode episode, uint8_t stage_index);
|
ChallengeCompletionLookupNode(Episode episode, uint8_t stage_index);
|
||||||
virtual ~ChallengeCompletionLookupNode() = default;
|
virtual ~ChallengeCompletionLookupNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Episode episode;
|
Episode episode;
|
||||||
uint8_t stage_index;
|
uint8_t stage_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TeamRewardLookupNode : public Node {
|
class TeamRewardLookupNode : public Node {
|
||||||
public:
|
public:
|
||||||
TeamRewardLookupNode(const std::string& reward_name);
|
TeamRewardLookupNode(const std::string& reward_name);
|
||||||
virtual ~TeamRewardLookupNode() = default;
|
virtual ~TeamRewardLookupNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string reward_name;
|
std::string reward_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NumPlayersLookupNode : public Node {
|
class NumPlayersLookupNode : public Node {
|
||||||
public:
|
public:
|
||||||
NumPlayersLookupNode();
|
NumPlayersLookupNode();
|
||||||
virtual ~NumPlayersLookupNode() = default;
|
virtual ~NumPlayersLookupNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EventLookupNode : public Node {
|
class EventLookupNode : public Node {
|
||||||
public:
|
public:
|
||||||
EventLookupNode();
|
EventLookupNode();
|
||||||
virtual ~EventLookupNode() = default;
|
virtual ~EventLookupNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class V1PresenceLookupNode : public Node {
|
class V1PresenceLookupNode : public Node {
|
||||||
public:
|
public:
|
||||||
V1PresenceLookupNode();
|
V1PresenceLookupNode();
|
||||||
virtual ~V1PresenceLookupNode() = default;
|
virtual ~V1PresenceLookupNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConstantNode : public Node {
|
class ConstantNode : public Node {
|
||||||
public:
|
public:
|
||||||
ConstantNode(int64_t value);
|
ConstantNode(int64_t value);
|
||||||
virtual ~ConstantNode() = default;
|
virtual ~ConstantNode() = default;
|
||||||
virtual bool operator==(const Node& other) const;
|
virtual bool operator==(const Node& other) const;
|
||||||
virtual int64_t evaluate(const Env& env) const;
|
virtual int64_t evaluate(const Env& env) const;
|
||||||
virtual std::string str() const;
|
virtual std::string str() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int64_t value;
|
int64_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<const Node> parse_expr(std::string_view text);
|
std::unique_ptr<const Node> parse_expr(std::string_view text);
|
||||||
|
|
||||||
std::unique_ptr<const Node> root;
|
std::unique_ptr<const Node> root;
|
||||||
};
|
};
|
||||||
|
|||||||
+513
-513
File diff suppressed because it is too large
Load Diff
+25
-25
@@ -1,25 +1,25 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
#include "ItemData.hh"
|
#include "ItemData.hh"
|
||||||
#include "ItemParameterTable.hh"
|
#include "ItemParameterTable.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
|
|
||||||
void player_use_item(std::shared_ptr<Client> c, size_t item_index, std::shared_ptr<PSOLFGEncryption> opt_rand_crypt);
|
void player_use_item(std::shared_ptr<Client> c, size_t item_index, std::shared_ptr<PSOLFGEncryption> opt_rand_crypt);
|
||||||
void player_feed_mag(std::shared_ptr<Client> c, size_t mag_item_index, size_t fed_item_index);
|
void player_feed_mag(std::shared_ptr<Client> c, size_t mag_item_index, size_t fed_item_index);
|
||||||
|
|
||||||
void apply_mag_feed_result(
|
void apply_mag_feed_result(
|
||||||
ItemData& mag_item,
|
ItemData& mag_item,
|
||||||
const ItemData& fed_item,
|
const ItemData& fed_item,
|
||||||
std::shared_ptr<const ItemParameterTable> item_parameter_table,
|
std::shared_ptr<const ItemParameterTable> item_parameter_table,
|
||||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table,
|
std::shared_ptr<const MagEvolutionTable> mag_evolution_table,
|
||||||
uint8_t char_class,
|
uint8_t char_class,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
bool version_has_rare_mags);
|
bool version_has_rare_mags);
|
||||||
|
|||||||
+190
-190
@@ -1,190 +1,190 @@
|
|||||||
#include "LevelTable.hh"
|
#include "LevelTable.hh"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <phosg/Filesystem.hh>
|
#include <phosg/Filesystem.hh>
|
||||||
|
|
||||||
#include "Compression.hh"
|
#include "Compression.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void LevelTable::reset_to_base(PlayerStats& stats, uint8_t char_class) const {
|
void LevelTable::reset_to_base(PlayerStats& stats, uint8_t char_class) const {
|
||||||
stats.level = 0;
|
stats.level = 0;
|
||||||
stats.experience = 0;
|
stats.experience = 0;
|
||||||
stats.char_stats = this->base_stats_for_class(char_class);
|
stats.char_stats = this->base_stats_for_class(char_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelTable::advance_to_level(PlayerStats& stats, uint32_t level, uint8_t char_class) const {
|
void LevelTable::advance_to_level(PlayerStats& stats, uint32_t level, uint8_t char_class) const {
|
||||||
for (; stats.level < level; stats.level++) {
|
for (; stats.level < level; stats.level++) {
|
||||||
const auto& level_stats = this->stats_delta_for_level(char_class, stats.level + 1);
|
const auto& level_stats = this->stats_delta_for_level(char_class, stats.level + 1);
|
||||||
// The original code clamps the resulting stat values to [0, max_stat]; we
|
// The original code clamps the resulting stat values to [0, max_stat]; we
|
||||||
// don't have max_stat handy so we just allow them to be unbounded
|
// don't have max_stat handy so we just allow them to be unbounded
|
||||||
stats.char_stats.atp += level_stats.atp;
|
stats.char_stats.atp += level_stats.atp;
|
||||||
stats.char_stats.mst += level_stats.mst;
|
stats.char_stats.mst += level_stats.mst;
|
||||||
stats.char_stats.evp += level_stats.evp;
|
stats.char_stats.evp += level_stats.evp;
|
||||||
stats.char_stats.hp += level_stats.hp;
|
stats.char_stats.hp += level_stats.hp;
|
||||||
stats.char_stats.dfp += level_stats.dfp;
|
stats.char_stats.dfp += level_stats.dfp;
|
||||||
stats.char_stats.ata += level_stats.ata;
|
stats.char_stats.ata += level_stats.ata;
|
||||||
// Note: It is not a bug that lck is ignored here; the original code
|
// Note: It is not a bug that lck is ignored here; the original code
|
||||||
// ignores it too.
|
// ignores it too.
|
||||||
stats.experience = level_stats.experience;
|
stats.experience = level_stats.experience;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelTableV2::LevelTableV2(const string& data, bool compressed) {
|
LevelTableV2::LevelTableV2(const string& data, bool compressed) {
|
||||||
struct Offsets {
|
struct Offsets {
|
||||||
// TODO: The overall format of this file on V2 has much more data than we
|
// TODO: The overall format of this file on V2 has much more data than we
|
||||||
// actually use. What's known of the structure so far:
|
// actually use. What's known of the structure so far:
|
||||||
le_uint32_t level_deltas; // -> u32[9] -> LevelStatsDelta[200]
|
le_uint32_t level_deltas; // -> u32[9] -> LevelStatsDelta[200]
|
||||||
le_uint32_t unknown_a1; // -> float[6]
|
le_uint32_t unknown_a1; // -> float[6]
|
||||||
le_uint32_t max_stats; // -> PlayerStats[9]
|
le_uint32_t max_stats; // -> PlayerStats[9]
|
||||||
le_uint32_t level_100_stats; // -> Level100Entry[9]
|
le_uint32_t level_100_stats; // -> Level100Entry[9]
|
||||||
le_uint32_t base_stats; // -> u32[9] -> CharacterStats
|
le_uint32_t base_stats; // -> u32[9] -> CharacterStats
|
||||||
le_uint32_t unknown_a2; // -> (0x120 zero bytes)
|
le_uint32_t unknown_a2; // -> (0x120 zero bytes)
|
||||||
le_uint32_t attack_data; // -> AttackData[9]
|
le_uint32_t attack_data; // -> AttackData[9]
|
||||||
le_uint32_t unknown_a4; // -> (0x14-byte struct)[9]
|
le_uint32_t unknown_a4; // -> (0x14-byte struct)[9]
|
||||||
le_uint32_t unknown_a5; // -> float[9]
|
le_uint32_t unknown_a5; // -> float[9]
|
||||||
le_uint32_t unknown_a6; // -> (0x30 bytes)
|
le_uint32_t unknown_a6; // -> (0x30 bytes)
|
||||||
le_uint32_t unknown_a7; // -> (0x2D bytes)
|
le_uint32_t unknown_a7; // -> (0x2D bytes)
|
||||||
le_uint32_t unknown_a8; // -> u32[3] -> float[0x2D]
|
le_uint32_t unknown_a8; // -> u32[3] -> float[0x2D]
|
||||||
le_uint32_t unknown_a9; // -> (0x90 bytes)
|
le_uint32_t unknown_a9; // -> (0x90 bytes)
|
||||||
le_uint32_t unknown_a10; // -> u32[3] -> (0x10-byte struct)[0x0C]
|
le_uint32_t unknown_a10; // -> u32[3] -> (0x10-byte struct)[0x0C]
|
||||||
le_uint32_t unknown_a11; // -> u32[3] -> (0x30-bytes)
|
le_uint32_t unknown_a11; // -> u32[3] -> (0x30-bytes)
|
||||||
le_uint32_t unknown_a12; // -> u32[3] -> (0x14-byte struct)[0x0F]
|
le_uint32_t unknown_a12; // -> u32[3] -> (0x14-byte struct)[0x0F]
|
||||||
} __packed_ws__(Offsets, 0x40);
|
} __packed_ws__(Offsets, 0x40);
|
||||||
|
|
||||||
StringReader r;
|
StringReader r;
|
||||||
string decompressed_data;
|
string decompressed_data;
|
||||||
if (compressed) {
|
if (compressed) {
|
||||||
decompressed_data = prs_decompress(data);
|
decompressed_data = prs_decompress(data);
|
||||||
r = StringReader(decompressed_data);
|
r = StringReader(decompressed_data);
|
||||||
} else {
|
} else {
|
||||||
r = StringReader(data);
|
r = StringReader(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& offsets = r.pget<Offsets>(r.pget_u32l(r.size() - 0x10));
|
const auto& offsets = r.pget<Offsets>(r.pget_u32l(r.size() - 0x10));
|
||||||
const auto& level_deltas_offsets = r.pget<parray<le_uint32_t, 9>>(offsets.level_deltas);
|
const auto& level_deltas_offsets = r.pget<parray<le_uint32_t, 9>>(offsets.level_deltas);
|
||||||
const auto& base_stats_offsets = r.pget<parray<le_uint32_t, 9>>(offsets.base_stats);
|
const auto& base_stats_offsets = r.pget<parray<le_uint32_t, 9>>(offsets.base_stats);
|
||||||
for (size_t char_class = 0; char_class < 9; char_class++) {
|
for (size_t char_class = 0; char_class < 9; char_class++) {
|
||||||
const auto& src_level_deltas = r.pget<parray<LevelStatsDelta, 200>>(level_deltas_offsets[char_class]);
|
const auto& src_level_deltas = r.pget<parray<LevelStatsDelta, 200>>(level_deltas_offsets[char_class]);
|
||||||
for (size_t level = 0; level < 200; level++) {
|
for (size_t level = 0; level < 200; level++) {
|
||||||
this->level_deltas[char_class][level] = src_level_deltas[level];
|
this->level_deltas[char_class][level] = src_level_deltas[level];
|
||||||
}
|
}
|
||||||
this->max_stats[char_class] = r.pget<PlayerStats>(offsets.max_stats + char_class * sizeof(PlayerStats));
|
this->max_stats[char_class] = r.pget<PlayerStats>(offsets.max_stats + char_class * sizeof(PlayerStats));
|
||||||
this->level_100_stats[char_class] = r.pget<Level100Entry>(offsets.level_100_stats + char_class * sizeof(Level100Entry));
|
this->level_100_stats[char_class] = r.pget<Level100Entry>(offsets.level_100_stats + char_class * sizeof(Level100Entry));
|
||||||
this->base_stats[char_class] = r.pget<CharacterStats>(base_stats_offsets[char_class]);
|
this->base_stats[char_class] = r.pget<CharacterStats>(base_stats_offsets[char_class]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CharacterStats& LevelTableV2::base_stats_for_class(uint8_t char_class) const {
|
const CharacterStats& LevelTableV2::base_stats_for_class(uint8_t char_class) const {
|
||||||
return this->base_stats.at(char_class);
|
return this->base_stats.at(char_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LevelTableV2::Level100Entry& LevelTableV2::level_100_stats_for_class(uint8_t char_class) const {
|
const LevelTableV2::Level100Entry& LevelTableV2::level_100_stats_for_class(uint8_t char_class) const {
|
||||||
return this->level_100_stats.at(char_class);
|
return this->level_100_stats.at(char_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlayerStats& LevelTableV2::max_stats_for_class(uint8_t char_class) const {
|
const PlayerStats& LevelTableV2::max_stats_for_class(uint8_t char_class) const {
|
||||||
return this->max_stats.at(char_class);
|
return this->max_stats.at(char_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LevelStatsDelta& LevelTableV2::stats_delta_for_level(uint8_t char_class, uint8_t level) const {
|
const LevelStatsDelta& LevelTableV2::stats_delta_for_level(uint8_t char_class, uint8_t level) const {
|
||||||
return this->level_deltas.at(char_class).at(level);
|
return this->level_deltas.at(char_class).at(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelTableV3BE::LevelTableV3BE(const string& data, bool encrypted) {
|
LevelTableV3BE::LevelTableV3BE(const string& data, bool encrypted) {
|
||||||
StringReader r;
|
StringReader r;
|
||||||
string decompressed_data;
|
string decompressed_data;
|
||||||
if (encrypted) {
|
if (encrypted) {
|
||||||
auto decrypted = decrypt_pr2_data<true>(data);
|
auto decrypted = decrypt_pr2_data<true>(data);
|
||||||
decompressed_data = prs_decompress(decrypted.compressed_data);
|
decompressed_data = prs_decompress(decrypted.compressed_data);
|
||||||
if (decompressed_data.size() != decrypted.decompressed_size) {
|
if (decompressed_data.size() != decrypted.decompressed_size) {
|
||||||
throw runtime_error("decompressed data size does not match expected size");
|
throw runtime_error("decompressed data size does not match expected size");
|
||||||
}
|
}
|
||||||
r = StringReader(decompressed_data);
|
r = StringReader(decompressed_data);
|
||||||
} else {
|
} else {
|
||||||
r = StringReader(data);
|
r = StringReader(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The GC format is very simple (but everything is big-endian):
|
// The GC format is very simple (but everything is big-endian):
|
||||||
// root:
|
// root:
|
||||||
// u32 offset:
|
// u32 offset:
|
||||||
// u32[12] offsets:
|
// u32[12] offsets:
|
||||||
// LevelStatsDeltaBE[200] level_deltas
|
// LevelStatsDeltaBE[200] level_deltas
|
||||||
const auto& offsets = r.pget<parray<be_uint32_t, 12>>(r.pget_u32b(r.pget_u32b(r.size() - 0x10)));
|
const auto& offsets = r.pget<parray<be_uint32_t, 12>>(r.pget_u32b(r.pget_u32b(r.size() - 0x10)));
|
||||||
for (size_t char_class = 0; char_class < 12; char_class++) {
|
for (size_t char_class = 0; char_class < 12; char_class++) {
|
||||||
const auto& src_deltas = r.pget<parray<LevelStatsDeltaBE, 200>>(offsets[char_class]);
|
const auto& src_deltas = r.pget<parray<LevelStatsDeltaBE, 200>>(offsets[char_class]);
|
||||||
for (size_t level = 0; level < 200; level++) {
|
for (size_t level = 0; level < 200; level++) {
|
||||||
const auto& src_delta = src_deltas[level];
|
const auto& src_delta = src_deltas[level];
|
||||||
auto& dest_delta = this->level_deltas[char_class][level];
|
auto& dest_delta = this->level_deltas[char_class][level];
|
||||||
dest_delta.atp = src_delta.atp;
|
dest_delta.atp = src_delta.atp;
|
||||||
dest_delta.mst = src_delta.mst;
|
dest_delta.mst = src_delta.mst;
|
||||||
dest_delta.evp = src_delta.evp;
|
dest_delta.evp = src_delta.evp;
|
||||||
dest_delta.hp = src_delta.hp;
|
dest_delta.hp = src_delta.hp;
|
||||||
dest_delta.dfp = src_delta.dfp;
|
dest_delta.dfp = src_delta.dfp;
|
||||||
dest_delta.ata = src_delta.ata;
|
dest_delta.ata = src_delta.ata;
|
||||||
dest_delta.lck = src_delta.lck;
|
dest_delta.lck = src_delta.lck;
|
||||||
dest_delta.tp = src_delta.tp;
|
dest_delta.tp = src_delta.tp;
|
||||||
dest_delta.experience = src_delta.experience.load();
|
dest_delta.experience = src_delta.experience.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CharacterStats& LevelTableV3BE::base_stats_for_class(uint8_t char_class) const {
|
const CharacterStats& LevelTableV3BE::base_stats_for_class(uint8_t char_class) const {
|
||||||
static const array<CharacterStats, 12> data = {
|
static const array<CharacterStats, 12> data = {
|
||||||
// ATP MST EVP HP DFP ATA LCK
|
// ATP MST EVP HP DFP ATA LCK
|
||||||
CharacterStats{0x0023, 0x001D, 0x002D, 0x0014, 0x0011, 0x001E, 0x000A},
|
CharacterStats{0x0023, 0x001D, 0x002D, 0x0014, 0x0011, 0x001E, 0x000A},
|
||||||
CharacterStats{0x001E, 0x0028, 0x003C, 0x0013, 0x0016, 0x0019, 0x000A},
|
CharacterStats{0x001E, 0x0028, 0x003C, 0x0013, 0x0016, 0x0019, 0x000A},
|
||||||
CharacterStats{0x0023, 0x0000, 0x0023, 0x0016, 0x0012, 0x0023, 0x000A},
|
CharacterStats{0x0023, 0x0000, 0x0023, 0x0016, 0x0012, 0x0023, 0x000A},
|
||||||
CharacterStats{0x0012, 0x0014, 0x0024, 0x0010, 0x000D, 0x0028, 0x000A},
|
CharacterStats{0x0012, 0x0014, 0x0024, 0x0010, 0x000D, 0x0028, 0x000A},
|
||||||
CharacterStats{0x0019, 0x0000, 0x001F, 0x0012, 0x0012, 0x002D, 0x000A},
|
CharacterStats{0x0019, 0x0000, 0x001F, 0x0012, 0x0012, 0x002D, 0x000A},
|
||||||
CharacterStats{0x0014, 0x0000, 0x001F, 0x0011, 0x0017, 0x002D, 0x000A},
|
CharacterStats{0x0014, 0x0000, 0x001F, 0x0011, 0x0017, 0x002D, 0x000A},
|
||||||
CharacterStats{0x000D, 0x0035, 0x0023, 0x0014, 0x000A, 0x000F, 0x000A},
|
CharacterStats{0x000D, 0x0035, 0x0023, 0x0014, 0x000A, 0x000F, 0x000A},
|
||||||
CharacterStats{0x000D, 0x003C, 0x0032, 0x0013, 0x0007, 0x000C, 0x000A},
|
CharacterStats{0x000D, 0x003C, 0x0032, 0x0013, 0x0007, 0x000C, 0x000A},
|
||||||
CharacterStats{0x000A, 0x003A, 0x0035, 0x0013, 0x000D, 0x000A, 0x000A},
|
CharacterStats{0x000A, 0x003A, 0x0035, 0x0013, 0x000D, 0x000A, 0x000A},
|
||||||
CharacterStats{0x0023, 0x0000, 0x0023, 0x0016, 0x0012, 0x0023, 0x000A},
|
CharacterStats{0x0023, 0x0000, 0x0023, 0x0016, 0x0012, 0x0023, 0x000A},
|
||||||
CharacterStats{0x000D, 0x0035, 0x0023, 0x0014, 0x000A, 0x000F, 0x000A},
|
CharacterStats{0x000D, 0x0035, 0x0023, 0x0014, 0x000A, 0x000F, 0x000A},
|
||||||
CharacterStats{0x0012, 0x0014, 0x0024, 0x0010, 0x000D, 0x0028, 0x000A},
|
CharacterStats{0x0012, 0x0014, 0x0024, 0x0010, 0x000D, 0x0028, 0x000A},
|
||||||
};
|
};
|
||||||
return data.at(char_class);
|
return data.at(char_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LevelStatsDelta& LevelTableV3BE::stats_delta_for_level(uint8_t char_class, uint8_t level) const {
|
const LevelStatsDelta& LevelTableV3BE::stats_delta_for_level(uint8_t char_class, uint8_t level) const {
|
||||||
return this->level_deltas.at(char_class).at(level);
|
return this->level_deltas.at(char_class).at(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelTableV4::LevelTableV4(const string& data, bool compressed) {
|
LevelTableV4::LevelTableV4(const string& data, bool compressed) {
|
||||||
struct Offsets {
|
struct Offsets {
|
||||||
le_uint32_t base_stats; // -> u32[12] -> CharacterStats
|
le_uint32_t base_stats; // -> u32[12] -> CharacterStats
|
||||||
le_uint32_t level_deltas; // -> u32[12] -> LevelStatsDelta[200]
|
le_uint32_t level_deltas; // -> u32[12] -> LevelStatsDelta[200]
|
||||||
} __packed_ws__(Offsets, 8);
|
} __packed_ws__(Offsets, 8);
|
||||||
|
|
||||||
StringReader r;
|
StringReader r;
|
||||||
string decompressed_data;
|
string decompressed_data;
|
||||||
if (compressed) {
|
if (compressed) {
|
||||||
decompressed_data = prs_decompress(data);
|
decompressed_data = prs_decompress(data);
|
||||||
r = StringReader(decompressed_data);
|
r = StringReader(decompressed_data);
|
||||||
} else {
|
} else {
|
||||||
r = StringReader(data);
|
r = StringReader(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& offsets = r.pget<Offsets>(r.pget_u32l(r.size() - 0x10));
|
const auto& offsets = r.pget<Offsets>(r.pget_u32l(r.size() - 0x10));
|
||||||
const auto& level_deltas_offsets = r.pget<parray<le_uint32_t, 12>>(offsets.level_deltas);
|
const auto& level_deltas_offsets = r.pget<parray<le_uint32_t, 12>>(offsets.level_deltas);
|
||||||
const auto& base_stats_offsets = r.pget<parray<le_uint32_t, 12>>(offsets.base_stats);
|
const auto& base_stats_offsets = r.pget<parray<le_uint32_t, 12>>(offsets.base_stats);
|
||||||
for (size_t char_class = 0; char_class < 12; char_class++) {
|
for (size_t char_class = 0; char_class < 12; char_class++) {
|
||||||
const auto& src_level_deltas = r.pget<parray<LevelStatsDelta, 200>>(level_deltas_offsets[char_class]);
|
const auto& src_level_deltas = r.pget<parray<LevelStatsDelta, 200>>(level_deltas_offsets[char_class]);
|
||||||
for (size_t level = 0; level < 200; level++) {
|
for (size_t level = 0; level < 200; level++) {
|
||||||
this->level_deltas[char_class][level] = src_level_deltas[level];
|
this->level_deltas[char_class][level] = src_level_deltas[level];
|
||||||
}
|
}
|
||||||
this->base_stats[char_class] = r.pget<CharacterStats>(base_stats_offsets[char_class]);
|
this->base_stats[char_class] = r.pget<CharacterStats>(base_stats_offsets[char_class]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CharacterStats& LevelTableV4::base_stats_for_class(uint8_t char_class) const {
|
const CharacterStats& LevelTableV4::base_stats_for_class(uint8_t char_class) const {
|
||||||
return this->base_stats.at(char_class);
|
return this->base_stats.at(char_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LevelStatsDelta& LevelTableV4::stats_delta_for_level(uint8_t char_class, uint8_t level) const {
|
const LevelStatsDelta& LevelTableV4::stats_delta_for_level(uint8_t char_class, uint8_t level) const {
|
||||||
return this->level_deltas.at(char_class).at(level);
|
return this->level_deltas.at(char_class).at(level);
|
||||||
}
|
}
|
||||||
|
|||||||
+173
-173
@@ -1,173 +1,173 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
class LevelTable;
|
class LevelTable;
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
struct CharacterStatsT {
|
struct CharacterStatsT {
|
||||||
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
|
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
|
||||||
|
|
||||||
/* 00 */ U16T atp = 0;
|
/* 00 */ U16T atp = 0;
|
||||||
/* 02 */ U16T mst = 0;
|
/* 02 */ U16T mst = 0;
|
||||||
/* 04 */ U16T evp = 0;
|
/* 04 */ U16T evp = 0;
|
||||||
/* 06 */ U16T hp = 0;
|
/* 06 */ U16T hp = 0;
|
||||||
/* 08 */ U16T dfp = 0;
|
/* 08 */ U16T dfp = 0;
|
||||||
/* 0A */ U16T ata = 0;
|
/* 0A */ U16T ata = 0;
|
||||||
/* 0C */ U16T lck = 0;
|
/* 0C */ U16T lck = 0;
|
||||||
/* 0E */
|
/* 0E */
|
||||||
|
|
||||||
operator CharacterStatsT<!IsBigEndian>() const {
|
operator CharacterStatsT<!IsBigEndian>() const {
|
||||||
CharacterStatsT<!IsBigEndian> ret;
|
CharacterStatsT<!IsBigEndian> ret;
|
||||||
ret.atp = this->atp.load();
|
ret.atp = this->atp.load();
|
||||||
ret.mst = this->mst.load();
|
ret.mst = this->mst.load();
|
||||||
ret.evp = this->evp.load();
|
ret.evp = this->evp.load();
|
||||||
ret.hp = this->hp.load();
|
ret.hp = this->hp.load();
|
||||||
ret.dfp = this->dfp.load();
|
ret.dfp = this->dfp.load();
|
||||||
ret.ata = this->ata.load();
|
ret.ata = this->ata.load();
|
||||||
ret.lck = this->lck.load();
|
ret.lck = this->lck.load();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed__;
|
} __packed__;
|
||||||
using CharacterStats = CharacterStatsT<false>;
|
using CharacterStats = CharacterStatsT<false>;
|
||||||
using CharacterStatsBE = CharacterStatsT<true>;
|
using CharacterStatsBE = CharacterStatsT<true>;
|
||||||
check_struct_size(CharacterStats, 0x0E);
|
check_struct_size(CharacterStats, 0x0E);
|
||||||
check_struct_size(CharacterStatsBE, 0x0E);
|
check_struct_size(CharacterStatsBE, 0x0E);
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
struct PlayerStatsT {
|
struct PlayerStatsT {
|
||||||
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
|
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
|
||||||
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;
|
||||||
using F32T = typename std::conditional<IsBigEndian, be_float, le_float>::type;
|
using F32T = typename std::conditional<IsBigEndian, be_float, le_float>::type;
|
||||||
|
|
||||||
/* 00 */ CharacterStatsT<IsBigEndian> char_stats;
|
/* 00 */ CharacterStatsT<IsBigEndian> char_stats;
|
||||||
/* 0E */ U16T esp = 0;
|
/* 0E */ U16T esp = 0;
|
||||||
/* 10 */ F32T height = 0.0;
|
/* 10 */ F32T height = 0.0;
|
||||||
/* 14 */ F32T unknown_a3 = 0.0;
|
/* 14 */ F32T unknown_a3 = 0.0;
|
||||||
/* 18 */ U32T level = 0;
|
/* 18 */ U32T level = 0;
|
||||||
/* 1C */ U32T experience = 0;
|
/* 1C */ U32T experience = 0;
|
||||||
/* 20 */ U32T meseta = 0;
|
/* 20 */ U32T meseta = 0;
|
||||||
/* 24 */
|
/* 24 */
|
||||||
|
|
||||||
operator PlayerStatsT<!IsBigEndian>() const {
|
operator PlayerStatsT<!IsBigEndian>() const {
|
||||||
PlayerStatsT<!IsBigEndian> ret;
|
PlayerStatsT<!IsBigEndian> ret;
|
||||||
ret.char_stats = this->char_stats;
|
ret.char_stats = this->char_stats;
|
||||||
ret.esp = this->esp.load();
|
ret.esp = this->esp.load();
|
||||||
ret.height = this->height.load();
|
ret.height = this->height.load();
|
||||||
ret.unknown_a3 = this->unknown_a3.load();
|
ret.unknown_a3 = this->unknown_a3.load();
|
||||||
ret.level = this->level.load();
|
ret.level = this->level.load();
|
||||||
ret.experience = this->experience.load();
|
ret.experience = this->experience.load();
|
||||||
ret.meseta = this->meseta.load();
|
ret.meseta = this->meseta.load();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed__;
|
} __packed__;
|
||||||
using PlayerStats = PlayerStatsT<false>;
|
using PlayerStats = PlayerStatsT<false>;
|
||||||
using PlayerStatsBE = PlayerStatsT<true>;
|
using PlayerStatsBE = PlayerStatsT<true>;
|
||||||
check_struct_size(PlayerStats, 0x24);
|
check_struct_size(PlayerStats, 0x24);
|
||||||
check_struct_size(PlayerStatsBE, 0x24);
|
check_struct_size(PlayerStatsBE, 0x24);
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
struct LevelStatsDeltaT {
|
struct LevelStatsDeltaT {
|
||||||
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;
|
||||||
|
|
||||||
/* 00 */ uint8_t atp;
|
/* 00 */ uint8_t atp;
|
||||||
/* 01 */ uint8_t mst;
|
/* 01 */ uint8_t mst;
|
||||||
/* 02 */ uint8_t evp;
|
/* 02 */ uint8_t evp;
|
||||||
/* 03 */ uint8_t hp;
|
/* 03 */ uint8_t hp;
|
||||||
/* 04 */ uint8_t dfp;
|
/* 04 */ uint8_t dfp;
|
||||||
/* 05 */ uint8_t ata;
|
/* 05 */ uint8_t ata;
|
||||||
/* 06 */ uint8_t lck;
|
/* 06 */ uint8_t lck;
|
||||||
/* 07 */ uint8_t tp;
|
/* 07 */ uint8_t tp;
|
||||||
/* 08 */ U32T experience;
|
/* 08 */ U32T experience;
|
||||||
/* 0C */
|
/* 0C */
|
||||||
|
|
||||||
void apply(CharacterStats& ps) const {
|
void apply(CharacterStats& ps) const {
|
||||||
ps.ata += this->ata;
|
ps.ata += this->ata;
|
||||||
ps.atp += this->atp;
|
ps.atp += this->atp;
|
||||||
ps.dfp += this->dfp;
|
ps.dfp += this->dfp;
|
||||||
ps.evp += this->evp;
|
ps.evp += this->evp;
|
||||||
ps.hp += this->hp;
|
ps.hp += this->hp;
|
||||||
ps.mst += this->mst;
|
ps.mst += this->mst;
|
||||||
ps.lck += this->lck;
|
ps.lck += this->lck;
|
||||||
}
|
}
|
||||||
} __packed__;
|
} __packed__;
|
||||||
using LevelStatsDelta = LevelStatsDeltaT<false>;
|
using LevelStatsDelta = LevelStatsDeltaT<false>;
|
||||||
using LevelStatsDeltaBE = LevelStatsDeltaT<true>;
|
using LevelStatsDeltaBE = LevelStatsDeltaT<true>;
|
||||||
check_struct_size(LevelStatsDelta, 0x0C);
|
check_struct_size(LevelStatsDelta, 0x0C);
|
||||||
check_struct_size(LevelStatsDeltaBE, 0x0C);
|
check_struct_size(LevelStatsDeltaBE, 0x0C);
|
||||||
|
|
||||||
class LevelTable {
|
class LevelTable {
|
||||||
// This is the base class for all the LevelTable implementations. The public
|
// This is the base class for all the LevelTable implementations. The public
|
||||||
// interface here only defines functions that the server needs to handle
|
// interface here only defines functions that the server needs to handle
|
||||||
// requests, but some subclasses implement more functionality. See the
|
// requests, but some subclasses implement more functionality. See the
|
||||||
// comments and Offsets structures inside the subclasses' constructor
|
// comments and Offsets structures inside the subclasses' constructor
|
||||||
// implementations for more details on the file formats.
|
// implementations for more details on the file formats.
|
||||||
public:
|
public:
|
||||||
virtual ~LevelTable() = default;
|
virtual ~LevelTable() = default;
|
||||||
virtual const CharacterStats& base_stats_for_class(uint8_t char_class) const = 0;
|
virtual const CharacterStats& base_stats_for_class(uint8_t char_class) const = 0;
|
||||||
virtual const LevelStatsDelta& stats_delta_for_level(uint8_t char_class, uint8_t level) const = 0;
|
virtual const LevelStatsDelta& stats_delta_for_level(uint8_t char_class, uint8_t level) const = 0;
|
||||||
|
|
||||||
void reset_to_base(PlayerStats& stats, uint8_t char_class) const;
|
void reset_to_base(PlayerStats& stats, uint8_t char_class) const;
|
||||||
void advance_to_level(PlayerStats& stats, uint32_t level, uint8_t char_class) const;
|
void advance_to_level(PlayerStats& stats, uint32_t level, uint8_t char_class) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LevelTable() = default;
|
LevelTable() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LevelTableV2 : public LevelTable { // from PlayerTable.prs (PC)
|
class LevelTableV2 : public LevelTable { // from PlayerTable.prs (PC)
|
||||||
public:
|
public:
|
||||||
struct Level100Entry {
|
struct Level100Entry {
|
||||||
/* 00 */ CharacterStats char_stats;
|
/* 00 */ CharacterStats char_stats;
|
||||||
/* 0E */ le_uint16_t unknown_a1 = 0;
|
/* 0E */ le_uint16_t unknown_a1 = 0;
|
||||||
/* 10 */ le_float height = 0.0;
|
/* 10 */ le_float height = 0.0;
|
||||||
/* 14 */ le_float unknown_a3 = 0.0;
|
/* 14 */ le_float unknown_a3 = 0.0;
|
||||||
/* 18 */ le_uint32_t level = 0;
|
/* 18 */ le_uint32_t level = 0;
|
||||||
/* 1C */
|
/* 1C */
|
||||||
} __packed_ws__(Level100Entry, 0x1C);
|
} __packed_ws__(Level100Entry, 0x1C);
|
||||||
|
|
||||||
LevelTableV2(const std::string& data, bool compressed);
|
LevelTableV2(const std::string& data, bool compressed);
|
||||||
virtual ~LevelTableV2() = default;
|
virtual ~LevelTableV2() = default;
|
||||||
|
|
||||||
virtual const CharacterStats& base_stats_for_class(uint8_t char_class) const;
|
virtual const CharacterStats& base_stats_for_class(uint8_t char_class) const;
|
||||||
const Level100Entry& level_100_stats_for_class(uint8_t char_class) const;
|
const Level100Entry& level_100_stats_for_class(uint8_t char_class) const;
|
||||||
const PlayerStats& max_stats_for_class(uint8_t char_class) const;
|
const PlayerStats& max_stats_for_class(uint8_t char_class) const;
|
||||||
virtual const LevelStatsDelta& stats_delta_for_level(uint8_t char_class, uint8_t level) const;
|
virtual const LevelStatsDelta& stats_delta_for_level(uint8_t char_class, uint8_t level) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<CharacterStats, 9> base_stats;
|
std::array<CharacterStats, 9> base_stats;
|
||||||
std::array<Level100Entry, 9> level_100_stats;
|
std::array<Level100Entry, 9> level_100_stats;
|
||||||
std::array<PlayerStats, 9> max_stats;
|
std::array<PlayerStats, 9> max_stats;
|
||||||
std::array<std::array<LevelStatsDelta, 200>, 9> level_deltas;
|
std::array<std::array<LevelStatsDelta, 200>, 9> level_deltas;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LevelTableV3BE : public LevelTable { // from PlyLevelTbl.cpt (GC)
|
class LevelTableV3BE : public LevelTable { // from PlyLevelTbl.cpt (GC)
|
||||||
public:
|
public:
|
||||||
LevelTableV3BE(const std::string& data, bool encrypted);
|
LevelTableV3BE(const std::string& data, bool encrypted);
|
||||||
virtual ~LevelTableV3BE() = default;
|
virtual ~LevelTableV3BE() = default;
|
||||||
|
|
||||||
virtual const CharacterStats& base_stats_for_class(uint8_t char_class) const;
|
virtual const CharacterStats& base_stats_for_class(uint8_t char_class) const;
|
||||||
virtual const LevelStatsDelta& stats_delta_for_level(uint8_t char_class, uint8_t level) const;
|
virtual const LevelStatsDelta& stats_delta_for_level(uint8_t char_class, uint8_t level) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<std::array<LevelStatsDelta, 200>, 12> level_deltas;
|
std::array<std::array<LevelStatsDelta, 200>, 12> level_deltas;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LevelTableV4 : public LevelTable { // from PlyLevelTbl.prs (BB)
|
class LevelTableV4 : public LevelTable { // from PlyLevelTbl.prs (BB)
|
||||||
public:
|
public:
|
||||||
LevelTableV4(const std::string& data, bool compressed);
|
LevelTableV4(const std::string& data, bool compressed);
|
||||||
virtual ~LevelTableV4() = default;
|
virtual ~LevelTableV4() = default;
|
||||||
|
|
||||||
virtual const CharacterStats& base_stats_for_class(uint8_t char_class) const;
|
virtual const CharacterStats& base_stats_for_class(uint8_t char_class) const;
|
||||||
virtual const LevelStatsDelta& stats_delta_for_level(uint8_t char_class, uint8_t level) const;
|
virtual const LevelStatsDelta& stats_delta_for_level(uint8_t char_class, uint8_t level) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<CharacterStats, 12> base_stats;
|
std::array<CharacterStats, 12> base_stats;
|
||||||
std::array<std::array<LevelStatsDelta, 200>, 12> level_deltas;
|
std::array<std::array<LevelStatsDelta, 200>, 12> level_deltas;
|
||||||
};
|
};
|
||||||
|
|||||||
+1066
-1066
File diff suppressed because it is too large
Load Diff
+322
-322
@@ -1,322 +1,322 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
#include "CommandFormats.hh"
|
#include "CommandFormats.hh"
|
||||||
#include "Episode3/BattleRecord.hh"
|
#include "Episode3/BattleRecord.hh"
|
||||||
#include "Episode3/Server.hh"
|
#include "Episode3/Server.hh"
|
||||||
#include "ItemCreator.hh"
|
#include "ItemCreator.hh"
|
||||||
#include "Map.hh"
|
#include "Map.hh"
|
||||||
#include "Quest.hh"
|
#include "Quest.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
struct ServerState;
|
struct ServerState;
|
||||||
|
|
||||||
struct Lobby : public std::enable_shared_from_this<Lobby> {
|
struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||||
struct FloorItem {
|
struct FloorItem {
|
||||||
ItemData data;
|
ItemData data;
|
||||||
float x;
|
float x;
|
||||||
float z;
|
float z;
|
||||||
uint64_t drop_number;
|
uint64_t drop_number;
|
||||||
// The low 12 bits of flags are visibility flags, specifying which clients
|
// The low 12 bits of flags are visibility flags, specifying which clients
|
||||||
// can see the item. (In practice, only the lowest 4 of these bits are used,
|
// can see the item. (In practice, only the lowest 4 of these bits are used,
|
||||||
// but the game has fields for 12 players so we do too.)
|
// but the game has fields for 12 players so we do too.)
|
||||||
// The 13th bit (0x1000) specifies whether a rare item notification should
|
// The 13th bit (0x1000) specifies whether a rare item notification should
|
||||||
// be sent to all players when the item is picked up. This has no effect for
|
// be sent to all players when the item is picked up. This has no effect for
|
||||||
// non-rare items.
|
// non-rare items.
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
|
|
||||||
bool visible_to_client(uint8_t client_id) const;
|
bool visible_to_client(uint8_t client_id) const;
|
||||||
};
|
};
|
||||||
struct FloorItemManager {
|
struct FloorItemManager {
|
||||||
PrefixedLogger log;
|
PrefixedLogger log;
|
||||||
uint64_t next_drop_number;
|
uint64_t next_drop_number;
|
||||||
// It's important that this is a map and not an unordered_map. See the
|
// It's important that this is a map and not an unordered_map. See the
|
||||||
// comment in send_game_item_state for more details.
|
// comment in send_game_item_state for more details.
|
||||||
std::map<uint32_t, std::shared_ptr<FloorItem>> items; // Keyed on item_id
|
std::map<uint32_t, std::shared_ptr<FloorItem>> items; // Keyed on item_id
|
||||||
std::array<std::map<uint64_t, std::shared_ptr<FloorItem>>, 12> queue_for_client;
|
std::array<std::map<uint64_t, std::shared_ptr<FloorItem>>, 12> queue_for_client;
|
||||||
|
|
||||||
FloorItemManager(uint32_t lobby_id, uint8_t floor);
|
FloorItemManager(uint32_t lobby_id, uint8_t floor);
|
||||||
~FloorItemManager() = default;
|
~FloorItemManager() = default;
|
||||||
|
|
||||||
bool exists(uint32_t item_id) const;
|
bool exists(uint32_t item_id) const;
|
||||||
std::shared_ptr<FloorItem> find(uint32_t item_id) const;
|
std::shared_ptr<FloorItem> find(uint32_t item_id) const;
|
||||||
void add(const ItemData& item, float x, float z, uint16_t flags);
|
void add(const ItemData& item, float x, float z, uint16_t flags);
|
||||||
void add(std::shared_ptr<FloorItem> fi);
|
void add(std::shared_ptr<FloorItem> fi);
|
||||||
std::shared_ptr<FloorItem> remove(uint32_t item_id, uint8_t client_id);
|
std::shared_ptr<FloorItem> remove(uint32_t item_id, uint8_t client_id);
|
||||||
std::unordered_set<std::shared_ptr<FloorItem>> evict();
|
std::unordered_set<std::shared_ptr<FloorItem>> evict();
|
||||||
void clear_inaccessible(uint16_t remaining_clients_mask);
|
void clear_inaccessible(uint16_t remaining_clients_mask);
|
||||||
void clear_private();
|
void clear_private();
|
||||||
void clear();
|
void clear();
|
||||||
uint32_t reassign_all_item_ids(uint32_t next_item_id);
|
uint32_t reassign_all_item_ids(uint32_t next_item_id);
|
||||||
};
|
};
|
||||||
enum class Flag {
|
enum class Flag {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
GAME = 0x00000001,
|
GAME = 0x00000001,
|
||||||
PERSISTENT = 0x00000002,
|
PERSISTENT = 0x00000002,
|
||||||
// Flags used only for games
|
// Flags used only for games
|
||||||
CHEATS_ENABLED = 0x00000100,
|
CHEATS_ENABLED = 0x00000100,
|
||||||
QUEST_SELECTION_IN_PROGRESS = 0x00000200,
|
QUEST_SELECTION_IN_PROGRESS = 0x00000200,
|
||||||
QUEST_IN_PROGRESS = 0x00000400,
|
QUEST_IN_PROGRESS = 0x00000400,
|
||||||
BATTLE_IN_PROGRESS = 0x00000800,
|
BATTLE_IN_PROGRESS = 0x00000800,
|
||||||
JOINABLE_QUEST_IN_PROGRESS = 0x00001000,
|
JOINABLE_QUEST_IN_PROGRESS = 0x00001000,
|
||||||
IS_CLIENT_CUSTOMIZATION = 0x00002000,
|
IS_CLIENT_CUSTOMIZATION = 0x00002000,
|
||||||
IS_SPECTATOR_TEAM = 0x00004000, // .episode must be EP3 also
|
IS_SPECTATOR_TEAM = 0x00004000, // .episode must be EP3 also
|
||||||
SPECTATORS_FORBIDDEN = 0x00008000,
|
SPECTATORS_FORBIDDEN = 0x00008000,
|
||||||
START_BATTLE_PLAYER_IMMEDIATELY = 0x00010000,
|
START_BATTLE_PLAYER_IMMEDIATELY = 0x00010000,
|
||||||
CANNOT_CHANGE_CHEAT_MODE = 0x00020000,
|
CANNOT_CHANGE_CHEAT_MODE = 0x00020000,
|
||||||
USE_CREATOR_SECTION_ID = 0x00040000,
|
USE_CREATOR_SECTION_ID = 0x00040000,
|
||||||
// Flags used only for lobbies
|
// Flags used only for lobbies
|
||||||
PUBLIC = 0x01000000,
|
PUBLIC = 0x01000000,
|
||||||
DEFAULT = 0x02000000,
|
DEFAULT = 0x02000000,
|
||||||
IS_OVERFLOW = 0x08000000,
|
IS_OVERFLOW = 0x08000000,
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
enum class DropMode {
|
enum class DropMode {
|
||||||
DISABLED = 0,
|
DISABLED = 0,
|
||||||
CLIENT = 1, // Not allowed for BB games
|
CLIENT = 1, // Not allowed for BB games
|
||||||
SERVER_SHARED = 2,
|
SERVER_SHARED = 2,
|
||||||
SERVER_PRIVATE = 3,
|
SERVER_PRIVATE = 3,
|
||||||
SERVER_DUPLICATE = 4,
|
SERVER_DUPLICATE = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::weak_ptr<ServerState>
|
std::weak_ptr<ServerState>
|
||||||
server_state;
|
server_state;
|
||||||
PrefixedLogger log;
|
PrefixedLogger log;
|
||||||
|
|
||||||
uint32_t lobby_id;
|
uint32_t lobby_id;
|
||||||
|
|
||||||
uint32_t min_level;
|
uint32_t min_level;
|
||||||
uint32_t max_level;
|
uint32_t max_level;
|
||||||
|
|
||||||
// Game state
|
// Game state
|
||||||
std::array<uint32_t, 12> next_item_id_for_client;
|
std::array<uint32_t, 12> next_item_id_for_client;
|
||||||
uint32_t next_game_item_id;
|
uint32_t next_game_item_id;
|
||||||
std::vector<FloorItemManager> floor_item_managers;
|
std::vector<FloorItemManager> floor_item_managers;
|
||||||
std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates;
|
std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates;
|
||||||
std::shared_ptr<Map> map;
|
std::shared_ptr<Map> map;
|
||||||
parray<le_uint32_t, 0x20> variations;
|
parray<le_uint32_t, 0x20> variations;
|
||||||
std::unique_ptr<QuestFlags> quest_flags_known; // If null, ALL quest flags are known
|
std::unique_ptr<QuestFlags> quest_flags_known; // If null, ALL quest flags are known
|
||||||
std::unique_ptr<QuestFlags> quest_flag_values;
|
std::unique_ptr<QuestFlags> quest_flag_values;
|
||||||
std::unique_ptr<SwitchFlags> switch_flags;
|
std::unique_ptr<SwitchFlags> switch_flags;
|
||||||
|
|
||||||
// Game config
|
// Game config
|
||||||
Version base_version;
|
Version base_version;
|
||||||
// Bits in allowed_versions specify who is allowed to join this game. The
|
// Bits in allowed_versions specify who is allowed to join this game. The
|
||||||
// bits are indexed as (1 << version), where version is a value from the
|
// bits are indexed as (1 << version), where version is a value from the
|
||||||
// Version enum.
|
// Version enum.
|
||||||
uint16_t allowed_versions;
|
uint16_t allowed_versions;
|
||||||
uint8_t creator_section_id;
|
uint8_t creator_section_id;
|
||||||
uint8_t override_section_id;
|
uint8_t override_section_id;
|
||||||
Episode episode;
|
Episode episode;
|
||||||
GameMode mode;
|
GameMode mode;
|
||||||
uint8_t difficulty; // 0-3
|
uint8_t difficulty; // 0-3
|
||||||
uint16_t base_exp_multiplier;
|
uint16_t base_exp_multiplier;
|
||||||
float exp_share_multiplier;
|
float exp_share_multiplier;
|
||||||
float challenge_exp_multiplier;
|
float challenge_exp_multiplier;
|
||||||
std::string password;
|
std::string password;
|
||||||
std::string name;
|
std::string name;
|
||||||
// This seed is also sent to the client for rare enemy generation
|
// This seed is also sent to the client for rare enemy generation
|
||||||
uint32_t random_seed;
|
uint32_t random_seed;
|
||||||
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt;
|
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt;
|
||||||
uint8_t allowed_drop_modes;
|
uint8_t allowed_drop_modes;
|
||||||
DropMode drop_mode;
|
DropMode drop_mode;
|
||||||
std::shared_ptr<ItemCreator> item_creator;
|
std::shared_ptr<ItemCreator> item_creator;
|
||||||
|
|
||||||
struct ChallengeParameters {
|
struct ChallengeParameters {
|
||||||
uint8_t stage_number = 0;
|
uint8_t stage_number = 0;
|
||||||
uint32_t rank_color = 0xFFFFFFFF;
|
uint32_t rank_color = 0xFFFFFFFF;
|
||||||
std::string rank_text;
|
std::string rank_text;
|
||||||
struct RankThreshold {
|
struct RankThreshold {
|
||||||
uint32_t bitmask = 0;
|
uint32_t bitmask = 0;
|
||||||
uint32_t seconds = 0;
|
uint32_t seconds = 0;
|
||||||
};
|
};
|
||||||
std::array<RankThreshold, 3> rank_thresholds;
|
std::array<RankThreshold, 3> rank_thresholds;
|
||||||
};
|
};
|
||||||
std::shared_ptr<ChallengeParameters> challenge_params;
|
std::shared_ptr<ChallengeParameters> challenge_params;
|
||||||
|
|
||||||
// Ep3 stuff
|
// Ep3 stuff
|
||||||
// There are three kinds of Episode 3 games. All of these types have episode
|
// There are three kinds of Episode 3 games. All of these types have episode
|
||||||
// set to EP3; types 2 and 3 additionally have the IS_SPECTATOR_TEAM flag.
|
// set to EP3; types 2 and 3 additionally have the IS_SPECTATOR_TEAM flag.
|
||||||
// 1. Primary games. These are the lobbies where battles may take place.
|
// 1. Primary games. These are the lobbies where battles may take place.
|
||||||
// 2. Watcher games. These lobbies receive all the battle and chat commands
|
// 2. Watcher games. These lobbies receive all the battle and chat commands
|
||||||
// from a primary game. (This the implementation of spectator teams.)
|
// from a primary game. (This the implementation of spectator teams.)
|
||||||
// 3. Replay games. These lobbies replay a sequence of battle commands and
|
// 3. Replay games. These lobbies replay a sequence of battle commands and
|
||||||
// chat commands from a previous primary game.
|
// chat commands from a previous primary game.
|
||||||
// Types 2 and 3 may be distinguished by the presence of the battle_record
|
// Types 2 and 3 may be distinguished by the presence of the battle_record
|
||||||
// field - in replay games, it will be present; in watcher games it will be
|
// field - in replay games, it will be present; in watcher games it will be
|
||||||
// absent.
|
// absent.
|
||||||
std::shared_ptr<Episode3::Server> ep3_server; // Only used in primary games
|
std::shared_ptr<Episode3::Server> ep3_server; // Only used in primary games
|
||||||
std::weak_ptr<Lobby> watched_lobby; // Only used in watcher games
|
std::weak_ptr<Lobby> watched_lobby; // Only used in watcher games
|
||||||
std::unordered_set<std::shared_ptr<Lobby>> watcher_lobbies; // Only used in primary games
|
std::unordered_set<std::shared_ptr<Lobby>> watcher_lobbies; // Only used in primary games
|
||||||
std::shared_ptr<Episode3::BattleRecord> battle_record; // Not used in watcher games
|
std::shared_ptr<Episode3::BattleRecord> battle_record; // Not used in watcher games
|
||||||
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player; // Only used in replay games
|
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player; // Only used in replay games
|
||||||
std::shared_ptr<Episode3::Tournament::Match> tournament_match;
|
std::shared_ptr<Episode3::Tournament::Match> tournament_match;
|
||||||
std::shared_ptr<const G_SetEXResultValues_Ep3_6xB4x4B> ep3_ex_result_values;
|
std::shared_ptr<const G_SetEXResultValues_Ep3_6xB4x4B> ep3_ex_result_values;
|
||||||
|
|
||||||
// Lobby stuff
|
// Lobby stuff
|
||||||
uint8_t event;
|
uint8_t event;
|
||||||
uint8_t block;
|
uint8_t block;
|
||||||
uint8_t leader_id;
|
uint8_t leader_id;
|
||||||
uint8_t max_clients;
|
uint8_t max_clients;
|
||||||
uint32_t enabled_flags;
|
uint32_t enabled_flags;
|
||||||
std::shared_ptr<const Quest> quest;
|
std::shared_ptr<const Quest> quest;
|
||||||
std::array<std::shared_ptr<Client>, 12> clients;
|
std::array<std::shared_ptr<Client>, 12> clients;
|
||||||
// Keys in this map are client_id
|
// Keys in this map are client_id
|
||||||
std::unordered_map<size_t, std::weak_ptr<Client>> clients_to_add;
|
std::unordered_map<size_t, std::weak_ptr<Client>> clients_to_add;
|
||||||
|
|
||||||
// This is only used when the PERSISTENT flag is set and idle_timeout_usecs
|
// This is only used when the PERSISTENT flag is set and idle_timeout_usecs
|
||||||
// is not zero
|
// is not zero
|
||||||
uint64_t idle_timeout_usecs;
|
uint64_t idle_timeout_usecs;
|
||||||
std::unique_ptr<struct event, void (*)(struct event*)> idle_timeout_event;
|
std::unique_ptr<struct event, void (*)(struct event*)> idle_timeout_event;
|
||||||
|
|
||||||
Lobby(std::shared_ptr<ServerState> s, uint32_t id, bool is_game);
|
Lobby(std::shared_ptr<ServerState> s, uint32_t id, bool is_game);
|
||||||
Lobby(const Lobby&) = delete;
|
Lobby(const Lobby&) = delete;
|
||||||
Lobby(Lobby&&) = delete;
|
Lobby(Lobby&&) = delete;
|
||||||
~Lobby();
|
~Lobby();
|
||||||
Lobby& operator=(const Lobby&) = delete;
|
Lobby& operator=(const Lobby&) = delete;
|
||||||
Lobby& operator=(Lobby&&) = delete;
|
Lobby& operator=(Lobby&&) = delete;
|
||||||
|
|
||||||
void reset_next_item_ids();
|
void reset_next_item_ids();
|
||||||
|
|
||||||
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
||||||
return !!(this->enabled_flags & static_cast<uint32_t>(flag));
|
return !!(this->enabled_flags & static_cast<uint32_t>(flag));
|
||||||
}
|
}
|
||||||
inline void set_flag(Flag flag) {
|
inline void set_flag(Flag flag) {
|
||||||
this->enabled_flags |= static_cast<uint32_t>(flag);
|
this->enabled_flags |= static_cast<uint32_t>(flag);
|
||||||
}
|
}
|
||||||
inline void clear_flag(Flag flag) {
|
inline void clear_flag(Flag flag) {
|
||||||
this->enabled_flags &= (~static_cast<uint32_t>(flag));
|
this->enabled_flags &= (~static_cast<uint32_t>(flag));
|
||||||
}
|
}
|
||||||
inline void toggle_flag(Flag flag) {
|
inline void toggle_flag(Flag flag) {
|
||||||
this->enabled_flags ^= static_cast<uint32_t>(flag);
|
this->enabled_flags ^= static_cast<uint32_t>(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ServerState> require_server_state() const;
|
std::shared_ptr<ServerState> require_server_state() const;
|
||||||
std::shared_ptr<ChallengeParameters> require_challenge_params() const;
|
std::shared_ptr<ChallengeParameters> require_challenge_params() const;
|
||||||
void set_drop_mode(DropMode new_mode);
|
void set_drop_mode(DropMode new_mode);
|
||||||
void create_item_creator();
|
void create_item_creator();
|
||||||
void change_section_id();
|
void change_section_id();
|
||||||
uint8_t effective_section_id() const;
|
uint8_t effective_section_id() const;
|
||||||
static std::shared_ptr<Map> load_maps(
|
static std::shared_ptr<Map> load_maps(
|
||||||
Version version,
|
Version version,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint32_t lobby_id,
|
uint32_t lobby_id,
|
||||||
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
|
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
|
||||||
uint32_t random_seed,
|
uint32_t random_seed,
|
||||||
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
|
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
|
||||||
std::shared_ptr<const std::string> quest_dat_contents_decompressed);
|
std::shared_ptr<const std::string> quest_dat_contents_decompressed);
|
||||||
static std::shared_ptr<Map> load_maps(
|
static std::shared_ptr<Map> load_maps(
|
||||||
Version version,
|
Version version,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint32_t lobby_id,
|
uint32_t lobby_id,
|
||||||
std::shared_ptr<const SetDataTableBase> sdt,
|
std::shared_ptr<const SetDataTableBase> sdt,
|
||||||
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_file_data,
|
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_file_data,
|
||||||
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
|
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
|
||||||
uint32_t random_seed,
|
uint32_t random_seed,
|
||||||
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
|
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
|
||||||
const parray<le_uint32_t, 0x20>& variations,
|
const parray<le_uint32_t, 0x20>& variations,
|
||||||
const PrefixedLogger* log = nullptr);
|
const PrefixedLogger* log = nullptr);
|
||||||
static std::shared_ptr<Map> load_maps(
|
static std::shared_ptr<Map> load_maps(
|
||||||
const std::vector<std::string>& enemy_filenames,
|
const std::vector<std::string>& enemy_filenames,
|
||||||
const std::vector<std::string>& object_filenames,
|
const std::vector<std::string>& object_filenames,
|
||||||
const std::vector<std::string>& event_filenames,
|
const std::vector<std::string>& event_filenames,
|
||||||
Version version,
|
Version version,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint32_t lobby_id,
|
uint32_t lobby_id,
|
||||||
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_file_data,
|
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_file_data,
|
||||||
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
|
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
|
||||||
uint32_t random_seed,
|
uint32_t random_seed,
|
||||||
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
|
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
|
||||||
const PrefixedLogger* log = nullptr);
|
const PrefixedLogger* log = nullptr);
|
||||||
void load_maps();
|
void load_maps();
|
||||||
void create_ep3_server();
|
void create_ep3_server();
|
||||||
|
|
||||||
[[nodiscard]] inline bool is_game() const {
|
[[nodiscard]] inline bool is_game() const {
|
||||||
return this->check_flag(Flag::GAME);
|
return this->check_flag(Flag::GAME);
|
||||||
}
|
}
|
||||||
[[nodiscard]] inline bool is_ep3() const {
|
[[nodiscard]] inline bool is_ep3() const {
|
||||||
return this->episode == Episode::EP3;
|
return this->episode == Episode::EP3;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline bool version_is_allowed(Version v) const {
|
[[nodiscard]] inline bool version_is_allowed(Version v) const {
|
||||||
return this->allowed_versions & (1 << static_cast<size_t>(v));
|
return this->allowed_versions & (1 << static_cast<size_t>(v));
|
||||||
}
|
}
|
||||||
inline void allow_version(Version v) {
|
inline void allow_version(Version v) {
|
||||||
this->allowed_versions |= (1 << static_cast<size_t>(v));
|
this->allowed_versions |= (1 << static_cast<size_t>(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
void reassign_leader_on_client_departure(size_t leaving_client_id);
|
void reassign_leader_on_client_departure(size_t leaving_client_id);
|
||||||
size_t count_clients() const;
|
size_t count_clients() const;
|
||||||
bool any_v1_clients_present() const;
|
bool any_v1_clients_present() const;
|
||||||
bool any_client_loading() const;
|
bool any_client_loading() const;
|
||||||
|
|
||||||
void add_client(std::shared_ptr<Client> c, ssize_t required_client_id = -1);
|
void add_client(std::shared_ptr<Client> c, ssize_t required_client_id = -1);
|
||||||
void remove_client(std::shared_ptr<Client> c);
|
void remove_client(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void move_client_to_lobby(
|
void move_client_to_lobby(
|
||||||
std::shared_ptr<Lobby> dest_lobby,
|
std::shared_ptr<Lobby> dest_lobby,
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
ssize_t required_client_id = -1);
|
ssize_t required_client_id = -1);
|
||||||
|
|
||||||
std::shared_ptr<Client> find_client(const std::string* identifier = nullptr, uint64_t account_id = 0);
|
std::shared_ptr<Client> find_client(const std::string* identifier = nullptr, uint64_t account_id = 0);
|
||||||
|
|
||||||
enum class JoinError {
|
enum class JoinError {
|
||||||
ALLOWED = 0,
|
ALLOWED = 0,
|
||||||
FULL,
|
FULL,
|
||||||
VERSION_CONFLICT,
|
VERSION_CONFLICT,
|
||||||
QUEST_SELECTION_IN_PROGRESS,
|
QUEST_SELECTION_IN_PROGRESS,
|
||||||
QUEST_IN_PROGRESS,
|
QUEST_IN_PROGRESS,
|
||||||
BATTLE_IN_PROGRESS,
|
BATTLE_IN_PROGRESS,
|
||||||
LOADING,
|
LOADING,
|
||||||
SOLO,
|
SOLO,
|
||||||
INCORRECT_PASSWORD,
|
INCORRECT_PASSWORD,
|
||||||
LEVEL_TOO_LOW,
|
LEVEL_TOO_LOW,
|
||||||
LEVEL_TOO_HIGH,
|
LEVEL_TOO_HIGH,
|
||||||
NO_ACCESS_TO_QUEST,
|
NO_ACCESS_TO_QUEST,
|
||||||
};
|
};
|
||||||
JoinError join_error_for_client(std::shared_ptr<Client> c, const std::string* password) const;
|
JoinError join_error_for_client(std::shared_ptr<Client> c, const std::string* password) const;
|
||||||
|
|
||||||
bool item_exists(uint8_t floor, uint32_t item_id) const;
|
bool item_exists(uint8_t floor, uint32_t item_id) const;
|
||||||
std::shared_ptr<FloorItem> find_item(uint8_t floor, uint32_t item_id) const;
|
std::shared_ptr<FloorItem> find_item(uint8_t floor, uint32_t item_id) const;
|
||||||
void add_item(uint8_t floor, const ItemData& item, float x, float z, uint16_t flags);
|
void add_item(uint8_t floor, const ItemData& item, float x, float z, uint16_t flags);
|
||||||
void add_item(uint8_t floor, std::shared_ptr<FloorItem>);
|
void add_item(uint8_t floor, std::shared_ptr<FloorItem>);
|
||||||
void evict_items_from_floor(uint8_t floor);
|
void evict_items_from_floor(uint8_t floor);
|
||||||
std::shared_ptr<FloorItem> remove_item(uint8_t floor, uint32_t item_id, uint8_t requesting_client_id);
|
std::shared_ptr<FloorItem> remove_item(uint8_t floor, uint32_t item_id, uint8_t requesting_client_id);
|
||||||
|
|
||||||
uint32_t generate_item_id(uint8_t client_id);
|
uint32_t generate_item_id(uint8_t client_id);
|
||||||
void on_item_id_generated_externally(uint32_t item_id);
|
void on_item_id_generated_externally(uint32_t item_id);
|
||||||
void assign_inventory_and_bank_item_ids(std::shared_ptr<Client> c, bool consume_ids);
|
void assign_inventory_and_bank_item_ids(std::shared_ptr<Client> c, bool consume_ids);
|
||||||
|
|
||||||
QuestIndex::IncludeCondition quest_include_condition() const;
|
QuestIndex::IncludeCondition quest_include_condition() const;
|
||||||
|
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<Client>> clients_by_account_id() const;
|
std::unordered_map<uint32_t, std::shared_ptr<Client>> clients_by_account_id() const;
|
||||||
|
|
||||||
static void dispatch_on_idle_timeout(evutil_socket_t, short, void* ctx);
|
static void dispatch_on_idle_timeout(evutil_socket_t, short, void* ctx);
|
||||||
|
|
||||||
static bool compare_shared(const std::shared_ptr<const Lobby>& a, const std::shared_ptr<const Lobby>& b);
|
static bool compare_shared(const std::shared_ptr<const Lobby>& a, const std::shared_ptr<const Lobby>& b);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
Lobby::DropMode enum_for_name<Lobby::DropMode>(const char* name);
|
Lobby::DropMode enum_for_name<Lobby::DropMode>(const char* name);
|
||||||
template <>
|
template <>
|
||||||
const char* name_for_enum<Lobby::DropMode>(Lobby::DropMode value);
|
const char* name_for_enum<Lobby::DropMode>(Lobby::DropMode value);
|
||||||
|
|||||||
+48
-48
@@ -1,48 +1,48 @@
|
|||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
|
|
||||||
#include <phosg/Strings.hh>
|
#include <phosg/Strings.hh>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
PrefixedLogger ax_messages_log("[$ax message] ", LogLevel::USE_DEFAULT);
|
PrefixedLogger ax_messages_log("[$ax message] ", LogLevel::USE_DEFAULT);
|
||||||
PrefixedLogger channel_exceptions_log("[Channel] ", LogLevel::USE_DEFAULT);
|
PrefixedLogger channel_exceptions_log("[Channel] ", LogLevel::USE_DEFAULT);
|
||||||
PrefixedLogger client_log("", LogLevel::USE_DEFAULT);
|
PrefixedLogger client_log("", LogLevel::USE_DEFAULT);
|
||||||
PrefixedLogger command_data_log("[Commands] ", LogLevel::USE_DEFAULT);
|
PrefixedLogger command_data_log("[Commands] ", LogLevel::USE_DEFAULT);
|
||||||
PrefixedLogger config_log("[Config] ", LogLevel::USE_DEFAULT);
|
PrefixedLogger config_log("[Config] ", LogLevel::USE_DEFAULT);
|
||||||
PrefixedLogger dns_server_log("[DNSServer] ", LogLevel::USE_DEFAULT);
|
PrefixedLogger dns_server_log("[DNSServer] ", LogLevel::USE_DEFAULT);
|
||||||
PrefixedLogger function_compiler_log("[FunctionCompiler] ", 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 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 void set_log_level_from_json(
|
static void set_log_level_from_json(
|
||||||
PrefixedLogger& log, const JSON& d, const char* json_key) {
|
PrefixedLogger& log, const JSON& d, const char* json_key) {
|
||||||
try {
|
try {
|
||||||
string name = toupper(d.at(json_key).as_string());
|
string name = toupper(d.at(json_key).as_string());
|
||||||
log.min_level = enum_for_name<LogLevel>(name.c_str());
|
log.min_level = enum_for_name<LogLevel>(name.c_str());
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_log_levels_from_json(const JSON& json) {
|
void set_log_levels_from_json(const JSON& 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(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");
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-22
@@ -1,22 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <phosg/JSON.hh>
|
#include <phosg/JSON.hh>
|
||||||
#include <phosg/Strings.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;
|
||||||
extern PrefixedLogger client_log;
|
extern PrefixedLogger client_log;
|
||||||
extern PrefixedLogger command_data_log;
|
extern PrefixedLogger command_data_log;
|
||||||
extern PrefixedLogger config_log;
|
extern PrefixedLogger config_log;
|
||||||
extern PrefixedLogger dns_server_log;
|
extern PrefixedLogger dns_server_log;
|
||||||
extern PrefixedLogger function_compiler_log;
|
extern PrefixedLogger function_compiler_log;
|
||||||
extern PrefixedLogger ip_stack_simulator_log;
|
extern PrefixedLogger ip_stack_simulator_log;
|
||||||
extern PrefixedLogger lobby_log;
|
extern PrefixedLogger lobby_log;
|
||||||
extern PrefixedLogger patch_index_log;
|
extern PrefixedLogger patch_index_log;
|
||||||
extern PrefixedLogger player_data_log;
|
extern PrefixedLogger player_data_log;
|
||||||
extern PrefixedLogger proxy_server_log;
|
extern PrefixedLogger proxy_server_log;
|
||||||
extern PrefixedLogger replay_log;
|
extern PrefixedLogger replay_log;
|
||||||
extern PrefixedLogger server_log;
|
extern PrefixedLogger server_log;
|
||||||
extern PrefixedLogger static_game_data_log;
|
extern PrefixedLogger static_game_data_log;
|
||||||
|
|
||||||
void set_log_levels_from_json(const JSON& json);
|
void set_log_levels_from_json(const JSON& json);
|
||||||
|
|||||||
+2820
-2820
File diff suppressed because it is too large
Load Diff
+2567
-2567
File diff suppressed because it is too large
Load Diff
+494
-494
@@ -1,494 +1,494 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <phosg/JSON.hh>
|
#include <phosg/JSON.hh>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "BattleParamsIndex.hh"
|
#include "BattleParamsIndex.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
struct Map {
|
struct Map {
|
||||||
static const char* name_for_object_type(uint16_t type);
|
static const char* name_for_object_type(uint16_t type);
|
||||||
|
|
||||||
struct SectionHeader {
|
struct SectionHeader {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
END = 0,
|
END = 0,
|
||||||
OBJECTS = 1,
|
OBJECTS = 1,
|
||||||
ENEMIES = 2,
|
ENEMIES = 2,
|
||||||
WAVE_EVENTS = 3,
|
WAVE_EVENTS = 3,
|
||||||
RANDOM_ENEMY_LOCATIONS = 4,
|
RANDOM_ENEMY_LOCATIONS = 4,
|
||||||
RANDOM_ENEMY_DEFINITIONS = 5,
|
RANDOM_ENEMY_DEFINITIONS = 5,
|
||||||
};
|
};
|
||||||
le_uint32_t le_type;
|
le_uint32_t le_type;
|
||||||
le_uint32_t section_size; // Includes this header
|
le_uint32_t section_size; // Includes this header
|
||||||
le_uint32_t floor;
|
le_uint32_t floor;
|
||||||
le_uint32_t data_size;
|
le_uint32_t data_size;
|
||||||
|
|
||||||
inline Type type() const {
|
inline Type type() const {
|
||||||
return static_cast<Type>(this->le_type.load());
|
return static_cast<Type>(this->le_type.load());
|
||||||
}
|
}
|
||||||
} __packed_ws__(SectionHeader, 0x10);
|
} __packed_ws__(SectionHeader, 0x10);
|
||||||
|
|
||||||
struct ObjectEntry { // Section type 1 (OBJECTS)
|
struct ObjectEntry { // Section type 1 (OBJECTS)
|
||||||
/* 00 */ le_uint16_t base_type;
|
/* 00 */ le_uint16_t base_type;
|
||||||
/* 02 */ le_uint16_t flags;
|
/* 02 */ le_uint16_t flags;
|
||||||
/* 04 */ le_uint16_t index;
|
/* 04 */ le_uint16_t index;
|
||||||
/* 06 */ le_uint16_t unknown_a2;
|
/* 06 */ le_uint16_t unknown_a2;
|
||||||
/* 08 */ le_uint16_t entity_id; // == index + 0x4000
|
/* 08 */ le_uint16_t entity_id; // == index + 0x4000
|
||||||
/* 0A */ le_uint16_t group;
|
/* 0A */ le_uint16_t group;
|
||||||
/* 0C */ le_uint16_t section;
|
/* 0C */ le_uint16_t section;
|
||||||
/* 0E */ le_uint16_t unknown_a3;
|
/* 0E */ le_uint16_t unknown_a3;
|
||||||
/* 10 */ le_float x;
|
/* 10 */ le_float x;
|
||||||
/* 14 */ le_float y;
|
/* 14 */ le_float y;
|
||||||
/* 18 */ le_float z;
|
/* 18 */ le_float z;
|
||||||
/* 1C */ le_uint32_t x_angle;
|
/* 1C */ le_uint32_t x_angle;
|
||||||
/* 20 */ le_uint32_t y_angle;
|
/* 20 */ le_uint32_t y_angle;
|
||||||
/* 24 */ le_uint32_t z_angle;
|
/* 24 */ le_uint32_t z_angle;
|
||||||
/* 28 */ le_float param1; // If <= 0, this is a specialized box, and the specialization is in param4/5/6
|
/* 28 */ le_float param1; // If <= 0, this is a specialized box, and the specialization is in param4/5/6
|
||||||
/* 2C */ le_float param2;
|
/* 2C */ le_float param2;
|
||||||
/* 30 */ le_float param3;
|
/* 30 */ le_float param3;
|
||||||
/* 34 */ le_uint32_t param4;
|
/* 34 */ le_uint32_t param4;
|
||||||
/* 38 */ le_uint32_t param5;
|
/* 38 */ le_uint32_t param5;
|
||||||
/* 3C */ le_uint32_t param6;
|
/* 3C */ le_uint32_t param6;
|
||||||
/* 40 */ le_uint32_t unused; // Reserved for pointer in client's memory; unused by server
|
/* 40 */ le_uint32_t unused; // Reserved for pointer in client's memory; unused by server
|
||||||
/* 44 */
|
/* 44 */
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
} __packed_ws__(ObjectEntry, 0x44);
|
} __packed_ws__(ObjectEntry, 0x44);
|
||||||
|
|
||||||
struct EnemyEntry { // Section type 2 (ENEMIES)
|
struct EnemyEntry { // Section type 2 (ENEMIES)
|
||||||
/* 00 */ le_uint16_t base_type;
|
/* 00 */ le_uint16_t base_type;
|
||||||
/* 02 */ le_uint16_t flags;
|
/* 02 */ le_uint16_t flags;
|
||||||
/* 04 */ le_uint16_t index;
|
/* 04 */ le_uint16_t index;
|
||||||
/* 06 */ le_uint16_t num_children;
|
/* 06 */ le_uint16_t num_children;
|
||||||
/* 08 */ le_uint16_t floor;
|
/* 08 */ le_uint16_t floor;
|
||||||
/* 0A */ le_uint16_t entity_id; // == index + 0x1000
|
/* 0A */ le_uint16_t entity_id; // == index + 0x1000
|
||||||
/* 0C */ le_uint16_t section;
|
/* 0C */ le_uint16_t section;
|
||||||
/* 0E */ le_uint16_t wave_number;
|
/* 0E */ le_uint16_t wave_number;
|
||||||
/* 10 */ le_uint16_t wave_number2;
|
/* 10 */ le_uint16_t wave_number2;
|
||||||
/* 12 */ le_uint16_t unknown_a1;
|
/* 12 */ le_uint16_t unknown_a1;
|
||||||
/* 14 */ le_float x;
|
/* 14 */ le_float x;
|
||||||
/* 18 */ le_float y;
|
/* 18 */ le_float y;
|
||||||
/* 1C */ le_float z;
|
/* 1C */ le_float z;
|
||||||
/* 20 */ le_uint32_t x_angle;
|
/* 20 */ le_uint32_t x_angle;
|
||||||
/* 24 */ le_uint32_t y_angle;
|
/* 24 */ le_uint32_t y_angle;
|
||||||
/* 28 */ le_uint32_t z_angle;
|
/* 28 */ le_uint32_t z_angle;
|
||||||
/* 2C */ le_float fparam1;
|
/* 2C */ le_float fparam1;
|
||||||
/* 30 */ le_float fparam2;
|
/* 30 */ le_float fparam2;
|
||||||
/* 34 */ le_float fparam3;
|
/* 34 */ le_float fparam3;
|
||||||
/* 38 */ le_float fparam4;
|
/* 38 */ le_float fparam4;
|
||||||
/* 3C */ le_float fparam5;
|
/* 3C */ le_float fparam5;
|
||||||
/* 40 */ le_uint16_t uparam1;
|
/* 40 */ le_uint16_t uparam1;
|
||||||
/* 42 */ le_uint16_t uparam2;
|
/* 42 */ le_uint16_t uparam2;
|
||||||
/* 44 */ le_uint32_t unused; // Reserved for pointer in client's memory; unused by server
|
/* 44 */ le_uint32_t unused; // Reserved for pointer in client's memory; unused by server
|
||||||
/* 48 */
|
/* 48 */
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
} __packed_ws__(EnemyEntry, 0x48);
|
} __packed_ws__(EnemyEntry, 0x48);
|
||||||
|
|
||||||
struct EventsSectionHeader { // Section type 3 (WAVE_EVENTS)
|
struct EventsSectionHeader { // Section type 3 (WAVE_EVENTS)
|
||||||
/* 00 */ le_uint32_t action_stream_offset;
|
/* 00 */ le_uint32_t action_stream_offset;
|
||||||
/* 04 */ le_uint32_t entries_offset;
|
/* 04 */ le_uint32_t entries_offset;
|
||||||
/* 08 */ le_uint32_t entry_count;
|
/* 08 */ le_uint32_t entry_count;
|
||||||
/* 0C */ be_uint32_t format; // 0 or 'evt2'
|
/* 0C */ be_uint32_t format; // 0 or 'evt2'
|
||||||
/* 10 */
|
/* 10 */
|
||||||
} __packed_ws__(EventsSectionHeader, 0x10);
|
} __packed_ws__(EventsSectionHeader, 0x10);
|
||||||
|
|
||||||
struct Event1Entry { // Section type 3 (WAVE_EVENTS) if format == 0
|
struct Event1Entry { // Section type 3 (WAVE_EVENTS) if format == 0
|
||||||
/* 00 */ le_uint32_t event_id;
|
/* 00 */ le_uint32_t event_id;
|
||||||
// Bits in flags:
|
// Bits in flags:
|
||||||
// 0004 = is active
|
// 0004 = is active
|
||||||
// 0008 = post-wave actions have been run
|
// 0008 = post-wave actions have been run
|
||||||
// 0010 = all enemies killed
|
// 0010 = all enemies killed
|
||||||
/* 04 */ le_uint16_t flags;
|
/* 04 */ le_uint16_t flags;
|
||||||
/* 06 */ le_uint16_t event_type;
|
/* 06 */ le_uint16_t event_type;
|
||||||
/* 08 */ le_uint16_t section;
|
/* 08 */ le_uint16_t section;
|
||||||
/* 0A */ le_uint16_t wave_number;
|
/* 0A */ le_uint16_t wave_number;
|
||||||
/* 0C */ le_uint32_t delay;
|
/* 0C */ le_uint32_t delay;
|
||||||
/* 10 */ le_uint32_t action_stream_offset;
|
/* 10 */ le_uint32_t action_stream_offset;
|
||||||
/* 14 */
|
/* 14 */
|
||||||
} __packed_ws__(Event1Entry, 0x14);
|
} __packed_ws__(Event1Entry, 0x14);
|
||||||
|
|
||||||
struct Event2Entry { // Section type 3 (WAVE_EVENTS) if format == 'evt2'
|
struct Event2Entry { // Section type 3 (WAVE_EVENTS) if format == 'evt2'
|
||||||
/* 00 */ le_uint32_t event_id;
|
/* 00 */ le_uint32_t event_id;
|
||||||
/* 04 */ le_uint16_t flags;
|
/* 04 */ le_uint16_t flags;
|
||||||
/* 06 */ le_uint16_t event_type;
|
/* 06 */ le_uint16_t event_type;
|
||||||
/* 08 */ le_uint16_t section;
|
/* 08 */ le_uint16_t section;
|
||||||
/* 0A */ le_uint16_t wave_number;
|
/* 0A */ le_uint16_t wave_number;
|
||||||
/* 0C */ le_uint16_t min_delay;
|
/* 0C */ le_uint16_t min_delay;
|
||||||
/* 0E */ le_uint16_t max_delay;
|
/* 0E */ le_uint16_t max_delay;
|
||||||
/* 10 */ uint8_t min_enemies;
|
/* 10 */ uint8_t min_enemies;
|
||||||
/* 11 */ uint8_t max_enemies;
|
/* 11 */ uint8_t max_enemies;
|
||||||
/* 12 */ le_uint16_t max_waves;
|
/* 12 */ le_uint16_t max_waves;
|
||||||
/* 14 */ le_uint32_t action_stream_offset;
|
/* 14 */ le_uint32_t action_stream_offset;
|
||||||
/* 18 */
|
/* 18 */
|
||||||
} __packed_ws__(Event2Entry, 0x18);
|
} __packed_ws__(Event2Entry, 0x18);
|
||||||
|
|
||||||
struct RandomEnemyLocationsHeader { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
struct RandomEnemyLocationsHeader { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||||
/* 00 */ le_uint32_t section_table_offset; // Offset to RandomEnemyLocationSegment structs, from start of this struct
|
/* 00 */ le_uint32_t section_table_offset; // Offset to RandomEnemyLocationSegment structs, from start of this struct
|
||||||
/* 04 */ le_uint32_t entries_offset; // Offset to RandomEnemyLocationEntry structs, from start of this struct
|
/* 04 */ le_uint32_t entries_offset; // Offset to RandomEnemyLocationEntry structs, from start of this struct
|
||||||
/* 08 */ le_uint32_t num_sections;
|
/* 08 */ le_uint32_t num_sections;
|
||||||
/* 0C */
|
/* 0C */
|
||||||
} __packed_ws__(RandomEnemyLocationsHeader, 0x0C);
|
} __packed_ws__(RandomEnemyLocationsHeader, 0x0C);
|
||||||
|
|
||||||
struct RandomEnemyLocationSection { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
struct RandomEnemyLocationSection { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||||
/* 00 */ le_uint16_t section;
|
/* 00 */ le_uint16_t section;
|
||||||
/* 02 */ le_uint16_t count;
|
/* 02 */ le_uint16_t count;
|
||||||
/* 04 */ le_uint32_t offset;
|
/* 04 */ le_uint32_t offset;
|
||||||
/* 08 */
|
/* 08 */
|
||||||
} __packed_ws__(RandomEnemyLocationSection, 8);
|
} __packed_ws__(RandomEnemyLocationSection, 8);
|
||||||
|
|
||||||
struct RandomEnemyLocationEntry { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
struct RandomEnemyLocationEntry { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||||
/* 00 */ le_float x;
|
/* 00 */ le_float x;
|
||||||
/* 04 */ le_float y;
|
/* 04 */ le_float y;
|
||||||
/* 08 */ le_float z;
|
/* 08 */ le_float z;
|
||||||
/* 0C */ le_uint32_t x_angle;
|
/* 0C */ le_uint32_t x_angle;
|
||||||
/* 10 */ le_uint32_t y_angle;
|
/* 10 */ le_uint32_t y_angle;
|
||||||
/* 14 */ le_uint32_t z_angle;
|
/* 14 */ le_uint32_t z_angle;
|
||||||
/* 18 */ uint16_t unknown_a9;
|
/* 18 */ uint16_t unknown_a9;
|
||||||
/* 1A */ uint16_t unknown_a10;
|
/* 1A */ uint16_t unknown_a10;
|
||||||
/* 1C */
|
/* 1C */
|
||||||
} __packed_ws__(RandomEnemyLocationEntry, 0x1C);
|
} __packed_ws__(RandomEnemyLocationEntry, 0x1C);
|
||||||
|
|
||||||
struct RandomEnemyDefinitionsHeader { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
struct RandomEnemyDefinitionsHeader { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
||||||
/* 00 */ le_uint32_t entries_offset; // Offset to RandomEnemyDefinition structs, from start of this struct
|
/* 00 */ le_uint32_t entries_offset; // Offset to RandomEnemyDefinition structs, from start of this struct
|
||||||
/* 04 */ le_uint32_t weight_entries_offset; // Offset to RandomEnemyDefinitionWeights structs, from start of this struct
|
/* 04 */ le_uint32_t weight_entries_offset; // Offset to RandomEnemyDefinitionWeights structs, from start of this struct
|
||||||
/* 08 */ le_uint32_t entry_count;
|
/* 08 */ le_uint32_t entry_count;
|
||||||
/* 0C */ le_uint32_t weight_entry_count;
|
/* 0C */ le_uint32_t weight_entry_count;
|
||||||
/* 10 */
|
/* 10 */
|
||||||
} __packed_ws__(RandomEnemyDefinitionsHeader, 0x10);
|
} __packed_ws__(RandomEnemyDefinitionsHeader, 0x10);
|
||||||
|
|
||||||
struct RandomEnemyDefinition { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
struct RandomEnemyDefinition { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
||||||
// All fields through entry_num map to the corresponding fields in
|
// All fields through entry_num map to the corresponding fields in
|
||||||
// EnemyEntry. Note that the order of the uparam fields is switched!
|
// EnemyEntry. Note that the order of the uparam fields is switched!
|
||||||
/* 00 */ le_float fparam1;
|
/* 00 */ le_float fparam1;
|
||||||
/* 04 */ le_float fparam2;
|
/* 04 */ le_float fparam2;
|
||||||
/* 08 */ le_float fparam3;
|
/* 08 */ le_float fparam3;
|
||||||
/* 0C */ le_float fparam4;
|
/* 0C */ le_float fparam4;
|
||||||
/* 10 */ le_float fparam5;
|
/* 10 */ le_float fparam5;
|
||||||
/* 14 */ le_uint16_t uparam2;
|
/* 14 */ le_uint16_t uparam2;
|
||||||
/* 16 */ le_uint16_t uparam1;
|
/* 16 */ le_uint16_t uparam1;
|
||||||
/* 18 */ le_uint32_t entry_num;
|
/* 18 */ le_uint32_t entry_num;
|
||||||
/* 1C */ le_uint16_t min_children;
|
/* 1C */ le_uint16_t min_children;
|
||||||
/* 1E */ le_uint16_t max_children;
|
/* 1E */ le_uint16_t max_children;
|
||||||
/* 20 */
|
/* 20 */
|
||||||
} __packed_ws__(RandomEnemyDefinition, 0x20);
|
} __packed_ws__(RandomEnemyDefinition, 0x20);
|
||||||
|
|
||||||
struct RandomEnemyWeight { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
struct RandomEnemyWeight { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
||||||
/* 00 */ uint8_t base_type_index;
|
/* 00 */ uint8_t base_type_index;
|
||||||
/* 01 */ uint8_t definition_entry_num;
|
/* 01 */ uint8_t definition_entry_num;
|
||||||
/* 02 */ uint8_t weight;
|
/* 02 */ uint8_t weight;
|
||||||
/* 03 */ uint8_t unknown_a4;
|
/* 03 */ uint8_t unknown_a4;
|
||||||
/* 04 */
|
/* 04 */
|
||||||
} __packed_ws__(RandomEnemyWeight, 4);
|
} __packed_ws__(RandomEnemyWeight, 4);
|
||||||
|
|
||||||
struct RareEnemyRates {
|
struct RareEnemyRates {
|
||||||
uint32_t hildeblue; // HILDEBEAR -> HILDEBLUE
|
uint32_t hildeblue; // HILDEBEAR -> HILDEBLUE
|
||||||
uint32_t rappy; // RAG_RAPPY -> {AL_RAPPY or seasonal rappies}; SAND_RAPPY -> DEL_RAPPY
|
uint32_t rappy; // RAG_RAPPY -> {AL_RAPPY or seasonal rappies}; SAND_RAPPY -> DEL_RAPPY
|
||||||
uint32_t nar_lily; // POISON_LILY -> NAR_LILY
|
uint32_t nar_lily; // POISON_LILY -> NAR_LILY
|
||||||
uint32_t pouilly_slime; // POFUILLY_SLIME -> POUILLY_SLIME
|
uint32_t pouilly_slime; // POFUILLY_SLIME -> POUILLY_SLIME
|
||||||
uint32_t merissa_aa; // MERISSA_A -> MERISSA_AA
|
uint32_t merissa_aa; // MERISSA_A -> MERISSA_AA
|
||||||
uint32_t pazuzu; // ZU -> PAZUZU (and _ALT variants)
|
uint32_t pazuzu; // ZU -> PAZUZU (and _ALT variants)
|
||||||
uint32_t dorphon_eclair; // DORPHON -> DORPHON_ECLAIR
|
uint32_t dorphon_eclair; // DORPHON -> DORPHON_ECLAIR
|
||||||
uint32_t kondrieu; // {SAINT_MILLION, SHAMBERTIN} -> KONDRIEU
|
uint32_t kondrieu; // {SAINT_MILLION, SHAMBERTIN} -> KONDRIEU
|
||||||
|
|
||||||
RareEnemyRates(uint32_t enemy_rate, uint32_t boss_rate);
|
RareEnemyRates(uint32_t enemy_rate, uint32_t boss_rate);
|
||||||
explicit RareEnemyRates(const JSON& json);
|
explicit RareEnemyRates(const JSON& json);
|
||||||
|
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::shared_ptr<const RareEnemyRates> NO_RARE_ENEMIES;
|
static const std::shared_ptr<const RareEnemyRates> NO_RARE_ENEMIES;
|
||||||
static const std::shared_ptr<const RareEnemyRates> DEFAULT_RARE_ENEMIES;
|
static const std::shared_ptr<const RareEnemyRates> DEFAULT_RARE_ENEMIES;
|
||||||
|
|
||||||
struct Object {
|
struct Object {
|
||||||
// TODO: Add more fields in here if we ever care about them. Currently we
|
// TODO: Add more fields in here if we ever care about them. Currently we
|
||||||
// only care about boxes with fixed item drops.
|
// only care about boxes with fixed item drops.
|
||||||
size_t source_index;
|
size_t source_index;
|
||||||
uint8_t floor;
|
uint8_t floor;
|
||||||
uint16_t object_id;
|
uint16_t object_id;
|
||||||
uint16_t base_type;
|
uint16_t base_type;
|
||||||
uint16_t section;
|
uint16_t section;
|
||||||
uint16_t group;
|
uint16_t group;
|
||||||
float param1; // If <= 0, this is a specialized box, and the specialization is in param4/5/6
|
float param1; // If <= 0, this is a specialized box, and the specialization is in param4/5/6
|
||||||
float param3; // If == 0, the item should be varied by difficulty and area
|
float param3; // If == 0, the item should be varied by difficulty and area
|
||||||
uint32_t param4;
|
uint32_t param4;
|
||||||
uint32_t param5;
|
uint32_t param5;
|
||||||
uint32_t param6;
|
uint32_t param6;
|
||||||
uint16_t game_flags;
|
uint16_t game_flags;
|
||||||
// Technically set_flags shouldn't be part of the Object struct, but all
|
// Technically set_flags shouldn't be part of the Object struct, but all
|
||||||
// object entries always generate exactly one object, so we store it here.
|
// object entries always generate exactly one object, so we store it here.
|
||||||
uint16_t set_flags;
|
uint16_t set_flags;
|
||||||
bool item_drop_checked;
|
bool item_drop_checked;
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Enemy {
|
struct Enemy {
|
||||||
enum Flag {
|
enum Flag {
|
||||||
LAST_HIT_MASK = 0x03,
|
LAST_HIT_MASK = 0x03,
|
||||||
EXP_GIVEN = 0x04,
|
EXP_GIVEN = 0x04,
|
||||||
ITEM_DROPPED = 0x08,
|
ITEM_DROPPED = 0x08,
|
||||||
ALL_HITS_MASK_FIRST = 0x10,
|
ALL_HITS_MASK_FIRST = 0x10,
|
||||||
ALL_HITS_MASK = 0xF0,
|
ALL_HITS_MASK = 0xF0,
|
||||||
};
|
};
|
||||||
size_t source_index;
|
size_t source_index;
|
||||||
size_t set_index;
|
size_t set_index;
|
||||||
uint16_t enemy_id;
|
uint16_t enemy_id;
|
||||||
uint16_t total_damage;
|
uint16_t total_damage;
|
||||||
uint32_t game_flags; // From 6x0A
|
uint32_t game_flags; // From 6x0A
|
||||||
uint16_t section;
|
uint16_t section;
|
||||||
uint16_t wave_number;
|
uint16_t wave_number;
|
||||||
EnemyType type;
|
EnemyType type;
|
||||||
uint8_t floor;
|
uint8_t floor;
|
||||||
uint8_t server_flags;
|
uint8_t server_flags;
|
||||||
|
|
||||||
Enemy(
|
Enemy(
|
||||||
uint16_t enemy_id,
|
uint16_t enemy_id,
|
||||||
size_t source_index,
|
size_t source_index,
|
||||||
size_t set_index,
|
size_t set_index,
|
||||||
uint8_t floor,
|
uint8_t floor,
|
||||||
uint16_t section,
|
uint16_t section,
|
||||||
uint16_t wave_number,
|
uint16_t wave_number,
|
||||||
EnemyType type);
|
EnemyType type);
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
|
|
||||||
inline bool ever_hit_by_client_id(uint8_t client_id) const {
|
inline bool ever_hit_by_client_id(uint8_t client_id) const {
|
||||||
return this->server_flags & (Flag::ALL_HITS_MASK_FIRST << client_id);
|
return this->server_flags & (Flag::ALL_HITS_MASK_FIRST << client_id);
|
||||||
}
|
}
|
||||||
inline bool last_hit_by_client_id(uint8_t client_id) const {
|
inline bool last_hit_by_client_id(uint8_t client_id) const {
|
||||||
return (this->server_flags & Flag::LAST_HIT_MASK) == client_id;
|
return (this->server_flags & Flag::LAST_HIT_MASK) == client_id;
|
||||||
}
|
}
|
||||||
inline void set_last_hit_by_client_id(uint8_t client_id) {
|
inline void set_last_hit_by_client_id(uint8_t client_id) {
|
||||||
this->server_flags = (this->server_flags & (~Flag::LAST_HIT_MASK)) | (Flag::ALL_HITS_MASK_FIRST << client_id) | (client_id & 3);
|
this->server_flags = (this->server_flags & (~Flag::LAST_HIT_MASK)) | (Flag::ALL_HITS_MASK_FIRST << client_id) | (client_id & 3);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Event {
|
struct Event {
|
||||||
uint32_t event_id;
|
uint32_t event_id;
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
uint16_t section;
|
uint16_t section;
|
||||||
uint16_t wave_number;
|
uint16_t wave_number;
|
||||||
uint8_t floor;
|
uint8_t floor;
|
||||||
uint32_t action_stream_offset;
|
uint32_t action_stream_offset;
|
||||||
std::vector<size_t> enemy_indexes;
|
std::vector<size_t> enemy_indexes;
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DATParserRandomState {
|
struct DATParserRandomState {
|
||||||
PSOV2Encryption random;
|
PSOV2Encryption random;
|
||||||
PSOV2Encryption location_table_random;
|
PSOV2Encryption location_table_random;
|
||||||
std::array<uint32_t, 0x20> location_index_table;
|
std::array<uint32_t, 0x20> location_index_table;
|
||||||
uint32_t location_indexes_populated;
|
uint32_t location_indexes_populated;
|
||||||
uint32_t location_indexes_used;
|
uint32_t location_indexes_used;
|
||||||
uint32_t location_entries_base_offset;
|
uint32_t location_entries_base_offset;
|
||||||
|
|
||||||
DATParserRandomState(uint32_t rare_seed);
|
DATParserRandomState(uint32_t rare_seed);
|
||||||
size_t rand_int_biased(size_t min_v, size_t max_v);
|
size_t rand_int_biased(size_t min_v, size_t max_v);
|
||||||
uint32_t next_location_index();
|
uint32_t next_location_index();
|
||||||
void generate_shuffled_location_table(const Map::RandomEnemyLocationsHeader& header, StringReader r, uint16_t section);
|
void generate_shuffled_location_table(const Map::RandomEnemyLocationsHeader& header, StringReader r, uint16_t section);
|
||||||
};
|
};
|
||||||
|
|
||||||
Map(Version version, uint32_t lobby_id, uint32_t rare_seed, std::shared_ptr<PSOLFGEncryption> opt_rand_crypt);
|
Map(Version version, uint32_t lobby_id, uint32_t rare_seed, std::shared_ptr<PSOLFGEncryption> opt_rand_crypt);
|
||||||
~Map() = default;
|
~Map() = default;
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void add_objects_from_map_data(uint8_t floor, const void* data, size_t size);
|
void add_objects_from_map_data(uint8_t floor, const void* data, size_t size);
|
||||||
|
|
||||||
bool check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate);
|
bool check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate);
|
||||||
void add_enemy(
|
void add_enemy(
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t floor,
|
uint8_t floor,
|
||||||
size_t index,
|
size_t index,
|
||||||
const EnemyEntry& e,
|
const EnemyEntry& e,
|
||||||
std::shared_ptr<const RareEnemyRates> rare_rates = DEFAULT_RARE_ENEMIES);
|
std::shared_ptr<const RareEnemyRates> rare_rates = DEFAULT_RARE_ENEMIES);
|
||||||
void add_enemies_from_map_data(
|
void add_enemies_from_map_data(
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t floor,
|
uint8_t floor,
|
||||||
const void* data,
|
const void* data,
|
||||||
size_t size,
|
size_t size,
|
||||||
std::shared_ptr<const RareEnemyRates> rare_rates = DEFAULT_RARE_ENEMIES);
|
std::shared_ptr<const RareEnemyRates> rare_rates = DEFAULT_RARE_ENEMIES);
|
||||||
void add_random_enemies_from_map_data(
|
void add_random_enemies_from_map_data(
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t floor,
|
uint8_t floor,
|
||||||
StringReader wave_events_r,
|
StringReader wave_events_r,
|
||||||
StringReader random_enemy_locations_r,
|
StringReader random_enemy_locations_r,
|
||||||
StringReader random_enemy_definitions_r,
|
StringReader random_enemy_definitions_r,
|
||||||
std::shared_ptr<DATParserRandomState> random_state,
|
std::shared_ptr<DATParserRandomState> random_state,
|
||||||
std::shared_ptr<const RareEnemyRates> rare_rates = DEFAULT_RARE_ENEMIES);
|
std::shared_ptr<const RareEnemyRates> rare_rates = DEFAULT_RARE_ENEMIES);
|
||||||
|
|
||||||
void add_event(
|
void add_event(
|
||||||
uint32_t event_id,
|
uint32_t event_id,
|
||||||
uint16_t flags,
|
uint16_t flags,
|
||||||
uint8_t floor,
|
uint8_t floor,
|
||||||
uint16_t section,
|
uint16_t section,
|
||||||
uint16_t wave_number,
|
uint16_t wave_number,
|
||||||
uint32_t action_stream_offset);
|
uint32_t action_stream_offset);
|
||||||
std::vector<Event*> get_events(uint8_t floor, uint32_t event_id);
|
std::vector<Event*> get_events(uint8_t floor, uint32_t event_id);
|
||||||
std::vector<const Event*> get_events(uint8_t floor, uint32_t event_id) const;
|
std::vector<const Event*> get_events(uint8_t floor, uint32_t event_id) const;
|
||||||
void add_events_from_map_data(uint8_t floor, const void* data, size_t size);
|
void add_events_from_map_data(uint8_t floor, const void* data, size_t size);
|
||||||
|
|
||||||
struct DATSectionsForFloor {
|
struct DATSectionsForFloor {
|
||||||
uint32_t objects = 0xFFFFFFFF;
|
uint32_t objects = 0xFFFFFFFF;
|
||||||
uint32_t enemies = 0xFFFFFFFF;
|
uint32_t enemies = 0xFFFFFFFF;
|
||||||
uint32_t wave_events = 0xFFFFFFFF;
|
uint32_t wave_events = 0xFFFFFFFF;
|
||||||
uint32_t random_enemy_locations = 0xFFFFFFFF;
|
uint32_t random_enemy_locations = 0xFFFFFFFF;
|
||||||
uint32_t random_enemy_definitions = 0xFFFFFFFF;
|
uint32_t random_enemy_definitions = 0xFFFFFFFF;
|
||||||
};
|
};
|
||||||
static std::vector<DATSectionsForFloor> collect_quest_map_data_sections(const void* data, size_t size);
|
static std::vector<DATSectionsForFloor> collect_quest_map_data_sections(const void* data, size_t size);
|
||||||
|
|
||||||
void add_entities_from_quest_data(
|
void add_entities_from_quest_data(
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
const void* data,
|
const void* data,
|
||||||
size_t size,
|
size_t size,
|
||||||
std::shared_ptr<const RareEnemyRates> rare_rates = Map::DEFAULT_RARE_ENEMIES);
|
std::shared_ptr<const RareEnemyRates> rare_rates = Map::DEFAULT_RARE_ENEMIES);
|
||||||
|
|
||||||
const Enemy& find_enemy(uint8_t floor, EnemyType type) const;
|
const Enemy& find_enemy(uint8_t floor, EnemyType type) const;
|
||||||
Enemy& find_enemy(uint8_t floor, EnemyType type);
|
Enemy& find_enemy(uint8_t floor, EnemyType type);
|
||||||
std::vector<Object*> get_objects(uint8_t floor, uint16_t section, uint16_t wave_number);
|
std::vector<Object*> get_objects(uint8_t floor, uint16_t section, uint16_t wave_number);
|
||||||
std::vector<Enemy*> get_enemies(uint8_t floor, uint16_t section, uint16_t wave_number);
|
std::vector<Enemy*> get_enemies(uint8_t floor, uint16_t section, uint16_t wave_number);
|
||||||
std::vector<Event*> get_events(uint8_t floor, uint16_t section, uint16_t wave_number);
|
std::vector<Event*> get_events(uint8_t floor, uint16_t section, uint16_t wave_number);
|
||||||
std::vector<Event*> get_events(uint8_t floor);
|
std::vector<Event*> get_events(uint8_t floor);
|
||||||
|
|
||||||
static std::string disassemble_objects_data(const void* data, size_t size, size_t* object_number = nullptr);
|
static std::string disassemble_objects_data(const void* data, size_t size, size_t* object_number = nullptr);
|
||||||
static std::string disassemble_enemies_data(const void* data, size_t size, size_t* enemy_number = nullptr);
|
static std::string disassemble_enemies_data(const void* data, size_t size, size_t* enemy_number = nullptr);
|
||||||
static std::string disassemble_wave_events_data(const void* data, size_t size, uint8_t floor = 0xFF);
|
static std::string disassemble_wave_events_data(const void* data, size_t size, uint8_t floor = 0xFF);
|
||||||
static std::string disassemble_quest_data(const void* data, size_t size);
|
static std::string disassemble_quest_data(const void* data, size_t size);
|
||||||
|
|
||||||
PrefixedLogger log;
|
PrefixedLogger log;
|
||||||
Version version;
|
Version version;
|
||||||
uint32_t rare_seed;
|
uint32_t rare_seed;
|
||||||
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt;
|
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt;
|
||||||
std::vector<Object> objects;
|
std::vector<Object> objects;
|
||||||
std::vector<Enemy> enemies;
|
std::vector<Enemy> enemies;
|
||||||
std::vector<uint16_t> enemy_set_flags;
|
std::vector<uint16_t> enemy_set_flags;
|
||||||
std::vector<size_t> rare_enemy_indexes;
|
std::vector<size_t> rare_enemy_indexes;
|
||||||
std::vector<Event> events;
|
std::vector<Event> events;
|
||||||
std::string event_action_stream;
|
std::string event_action_stream;
|
||||||
std::multimap<uint64_t, size_t> floor_and_event_id_to_index;
|
std::multimap<uint64_t, size_t> floor_and_event_id_to_index;
|
||||||
std::unordered_multimap<uint64_t, size_t> floor_section_and_group_to_object_index;
|
std::unordered_multimap<uint64_t, size_t> floor_section_and_group_to_object_index;
|
||||||
std::unordered_multimap<uint64_t, size_t> floor_section_and_wave_number_to_enemy_index;
|
std::unordered_multimap<uint64_t, size_t> floor_section_and_wave_number_to_enemy_index;
|
||||||
std::unordered_multimap<uint64_t, size_t> floor_section_and_wave_number_to_event_index;
|
std::unordered_multimap<uint64_t, size_t> floor_section_and_wave_number_to_event_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetDataTableBase {
|
class SetDataTableBase {
|
||||||
public:
|
public:
|
||||||
virtual ~SetDataTableBase() = default;
|
virtual ~SetDataTableBase() = default;
|
||||||
|
|
||||||
parray<le_uint32_t, 0x20> generate_variations(
|
parray<le_uint32_t, 0x20> generate_variations(
|
||||||
Episode episode,
|
Episode episode,
|
||||||
bool is_solo,
|
bool is_solo,
|
||||||
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt = nullptr) const;
|
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt = nullptr) const;
|
||||||
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const = 0;
|
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const = 0;
|
||||||
virtual std::pair<uint32_t, uint32_t> num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const = 0;
|
virtual std::pair<uint32_t, uint32_t> num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const = 0;
|
||||||
|
|
||||||
enum class FilenameType {
|
enum class FilenameType {
|
||||||
OBJECTS = 0,
|
OBJECTS = 0,
|
||||||
ENEMIES,
|
ENEMIES,
|
||||||
EVENTS,
|
EVENTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual std::string map_filename_for_variation(
|
virtual std::string map_filename_for_variation(
|
||||||
uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const = 0;
|
uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const = 0;
|
||||||
std::vector<std::string> map_filenames_for_variations(
|
std::vector<std::string> map_filenames_for_variations(
|
||||||
const parray<le_uint32_t, 0x20>& variations, Episode episode, GameMode mode, FilenameType type) const;
|
const parray<le_uint32_t, 0x20>& variations, Episode episode, GameMode mode, FilenameType type) const;
|
||||||
|
|
||||||
uint8_t default_area_for_floor(Episode episode, uint8_t floor) const;
|
uint8_t default_area_for_floor(Episode episode, uint8_t floor) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit SetDataTableBase(Version version);
|
explicit SetDataTableBase(Version version);
|
||||||
|
|
||||||
Version version;
|
Version version;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetDataTable : public SetDataTableBase {
|
class SetDataTable : public SetDataTableBase {
|
||||||
public:
|
public:
|
||||||
struct SetEntry {
|
struct SetEntry {
|
||||||
std::string object_list_basename;
|
std::string object_list_basename;
|
||||||
std::string enemy_and_event_list_basename;
|
std::string enemy_and_event_list_basename;
|
||||||
std::string area_setup_filename;
|
std::string area_setup_filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
SetDataTable(Version version, const std::string& data);
|
SetDataTable(Version version, const std::string& data);
|
||||||
virtual ~SetDataTable() = default;
|
virtual ~SetDataTable() = default;
|
||||||
|
|
||||||
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
|
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
|
||||||
virtual std::pair<uint32_t, uint32_t> num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const;
|
virtual std::pair<uint32_t, uint32_t> num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const;
|
||||||
virtual std::string map_filename_for_variation(
|
virtual std::string map_filename_for_variation(
|
||||||
uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const;
|
uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const;
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
void load_table_t(const std::string& data);
|
void load_table_t(const std::string& data);
|
||||||
|
|
||||||
// Indexes are [floor][variation1][variation2]
|
// Indexes are [floor][variation1][variation2]
|
||||||
// floor is cumulative per episode, so Ep2 starts at floor=18.
|
// floor is cumulative per episode, so Ep2 starts at floor=18.
|
||||||
std::vector<std::vector<std::vector<SetEntry>>> entries;
|
std::vector<std::vector<std::vector<SetEntry>>> entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetDataTableDCNTE : public SetDataTableBase {
|
class SetDataTableDCNTE : public SetDataTableBase {
|
||||||
public:
|
public:
|
||||||
SetDataTableDCNTE();
|
SetDataTableDCNTE();
|
||||||
virtual ~SetDataTableDCNTE() = default;
|
virtual ~SetDataTableDCNTE() = default;
|
||||||
|
|
||||||
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
|
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
|
||||||
virtual std::pair<uint32_t, uint32_t> num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const;
|
virtual std::pair<uint32_t, uint32_t> num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const;
|
||||||
virtual std::string map_filename_for_variation(
|
virtual std::string map_filename_for_variation(
|
||||||
uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const;
|
uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const std::array<std::vector<std::vector<std::string>>, 0x12> NAMES;
|
static const std::array<std::vector<std::vector<std::string>>, 0x12> NAMES;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetDataTableDC112000 : public SetDataTableBase {
|
class SetDataTableDC112000 : public SetDataTableBase {
|
||||||
public:
|
public:
|
||||||
SetDataTableDC112000();
|
SetDataTableDC112000();
|
||||||
virtual ~SetDataTableDC112000() = default;
|
virtual ~SetDataTableDC112000() = default;
|
||||||
|
|
||||||
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
|
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
|
||||||
virtual std::pair<uint32_t, uint32_t> num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const;
|
virtual std::pair<uint32_t, uint32_t> num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const;
|
||||||
virtual std::string map_filename_for_variation(
|
virtual std::string map_filename_for_variation(
|
||||||
uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const;
|
uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const std::array<std::vector<std::vector<std::string>>, 0x12> NAMES;
|
static const std::array<std::vector<std::vector<std::string>>, 0x12> NAMES;
|
||||||
};
|
};
|
||||||
|
|
||||||
void generate_variations_deprecated(
|
void generate_variations_deprecated(
|
||||||
parray<le_uint32_t, 0x20>& variations,
|
parray<le_uint32_t, 0x20>& variations,
|
||||||
std::shared_ptr<PSOLFGEncryption> random,
|
std::shared_ptr<PSOLFGEncryption> random,
|
||||||
Version version,
|
Version version,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
bool is_solo);
|
bool is_solo);
|
||||||
|
|
||||||
parray<le_uint32_t, 0x20> variation_maxes_deprecated(Version version, Episode episode, bool is_solo);
|
parray<le_uint32_t, 0x20> variation_maxes_deprecated(Version version, Episode episode, bool is_solo);
|
||||||
bool next_variation_deprecated(parray<le_uint32_t, 0x20>& variations, Version version, Episode episode, bool is_solo);
|
bool next_variation_deprecated(parray<le_uint32_t, 0x20>& variations, Version version, Episode episode, bool is_solo);
|
||||||
|
|
||||||
std::vector<std::string> map_filenames_for_variation_deprecated(
|
std::vector<std::string> map_filenames_for_variation_deprecated(
|
||||||
uint8_t floor, uint32_t var1, uint32_t var2, Version version, Episode episode, GameMode mode, bool is_enemies);
|
uint8_t floor, uint32_t var1, uint32_t var2, Version version, Episode episode, GameMode mode, bool is_enemies);
|
||||||
std::vector<std::vector<std::string>> map_filenames_for_variations_deprecated(
|
std::vector<std::vector<std::string>> map_filenames_for_variations_deprecated(
|
||||||
const parray<le_uint32_t, 0x20>& variations,
|
const parray<le_uint32_t, 0x20>& variations,
|
||||||
Version version,
|
Version version,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
bool is_enemies);
|
bool is_enemies);
|
||||||
|
|||||||
+149
-149
@@ -1,149 +1,149 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// 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
|
||||||
// also want to be able to use non-enum values in switch statements without
|
// also want to be able to use non-enum values in switch statements without
|
||||||
// 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 CLEAR_LICENSE_CONFIRMATION = 0x11111111;
|
constexpr uint32_t CLEAR_LICENSE_CONFIRMATION = 0x11111111;
|
||||||
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_EP1 = 0x55010155;
|
constexpr uint32_t QUEST_EP1 = 0x55010155;
|
||||||
constexpr uint32_t QUEST_EP2 = 0x55020255;
|
constexpr uint32_t QUEST_EP2 = 0x55020255;
|
||||||
// See the decsription of the A2 command in CommandFormats.hh for why these
|
// See the decsription of the A2 command in CommandFormats.hh for why these
|
||||||
// menu IDs don't fit the rest of the pattern.
|
// menu IDs don't fit the rest of the pattern.
|
||||||
constexpr uint32_t QUEST_CATEGORIES_EP1 = 0x01000001;
|
constexpr uint32_t QUEST_CATEGORIES_EP1 = 0x01000001;
|
||||||
constexpr uint32_t QUEST_CATEGORIES_EP2 = 0x02000002;
|
constexpr uint32_t QUEST_CATEGORIES_EP2 = 0x02000002;
|
||||||
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 PATCH_SWITCHES = 0x99010199;
|
constexpr uint32_t PATCH_SWITCHES = 0x99010199;
|
||||||
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 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 PATCH_SWITCHES = 0x11676711;
|
constexpr uint32_t PATCH_SWITCHES = 0x11676711;
|
||||||
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 MainMenuItemID
|
||||||
|
|
||||||
namespace ClearLicenseConfirmationMenuItemID {
|
namespace ClearLicenseConfirmationMenuItemID {
|
||||||
constexpr uint32_t CANCEL = 0x01010101;
|
constexpr uint32_t CANCEL = 0x01010101;
|
||||||
constexpr uint32_t CLEAR_LICENSE = 0x02020202;
|
constexpr uint32_t CLEAR_LICENSE = 0x02020202;
|
||||||
} // namespace ClearLicenseConfirmationMenuItemID
|
} // namespace ClearLicenseConfirmationMenuItemID
|
||||||
|
|
||||||
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 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 = 0xAA0101AA;
|
constexpr uint32_t CHAT_COMMANDS = 0xAA0101AA;
|
||||||
constexpr uint32_t PLAYER_NOTIFICATIONS = 0xAA0202AA;
|
constexpr uint32_t PLAYER_NOTIFICATIONS = 0xAA0202AA;
|
||||||
constexpr uint32_t DROP_NOTIFICATIONS = 0xAA0303AA;
|
constexpr uint32_t DROP_NOTIFICATIONS = 0xAA0303AA;
|
||||||
constexpr uint32_t BLOCK_PINGS = 0xAA0404AA;
|
constexpr uint32_t BLOCK_PINGS = 0xAA0404AA;
|
||||||
constexpr uint32_t INFINITE_HP = 0xAA0505AA;
|
constexpr uint32_t INFINITE_HP = 0xAA0505AA;
|
||||||
constexpr uint32_t INFINITE_TP = 0xAA0606AA;
|
constexpr uint32_t INFINITE_TP = 0xAA0606AA;
|
||||||
constexpr uint32_t SWITCH_ASSIST = 0xAA0707AA;
|
constexpr uint32_t SWITCH_ASSIST = 0xAA0707AA;
|
||||||
constexpr uint32_t BLOCK_EVENTS = 0xAA0808AA;
|
constexpr uint32_t BLOCK_EVENTS = 0xAA0808AA;
|
||||||
constexpr uint32_t BLOCK_PATCHES = 0xAA0909AA;
|
constexpr uint32_t BLOCK_PATCHES = 0xAA0909AA;
|
||||||
constexpr uint32_t SAVE_FILES = 0xAA0A0AAA;
|
constexpr uint32_t SAVE_FILES = 0xAA0A0AAA;
|
||||||
constexpr uint32_t RED_NAME = 0xAA0B0BAA;
|
constexpr uint32_t RED_NAME = 0xAA0B0BAA;
|
||||||
constexpr uint32_t BLANK_NAME = 0xAA0C0CAA;
|
constexpr uint32_t BLANK_NAME = 0xAA0C0CAA;
|
||||||
constexpr uint32_t SUPPRESS_LOGIN = 0xAA0D0DAA;
|
constexpr uint32_t SUPPRESS_LOGIN = 0xAA0D0DAA;
|
||||||
constexpr uint32_t SKIP_CARD = 0xAA0E0EAA;
|
constexpr uint32_t SKIP_CARD = 0xAA0E0EAA;
|
||||||
constexpr uint32_t EP3_INFINITE_MESETA = 0xAA0F0FAA;
|
constexpr uint32_t EP3_INFINITE_MESETA = 0xAA0F0FAA;
|
||||||
constexpr uint32_t EP3_INFINITE_TIME = 0xAA1010AA;
|
constexpr uint32_t EP3_INFINITE_TIME = 0xAA1010AA;
|
||||||
constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA1111AA;
|
constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA1111AA;
|
||||||
} // namespace ProxyOptionsMenuItemID
|
} // namespace ProxyOptionsMenuItemID
|
||||||
|
|
||||||
namespace TeamRewardMenuItemID {
|
namespace TeamRewardMenuItemID {
|
||||||
constexpr uint32_t TEAM_FLAG = 0x01010101;
|
constexpr uint32_t TEAM_FLAG = 0x01010101;
|
||||||
constexpr uint32_t DRESSING_ROOM = 0x02020202;
|
constexpr uint32_t DRESSING_ROOM = 0x02020202;
|
||||||
constexpr uint32_t MEMBERS_20_LEADERS_3 = 0x03030303;
|
constexpr uint32_t MEMBERS_20_LEADERS_3 = 0x03030303;
|
||||||
constexpr uint32_t MEMBERS_40_LEADERS_5 = 0x04040404;
|
constexpr uint32_t MEMBERS_40_LEADERS_5 = 0x04040404;
|
||||||
constexpr uint32_t MEMBERS_70_LEADERS_8 = 0x05050505;
|
constexpr uint32_t MEMBERS_70_LEADERS_8 = 0x05050505;
|
||||||
constexpr uint32_t MEMBERS_100_LEADERS_10 = 0x06060606;
|
constexpr uint32_t MEMBERS_100_LEADERS_10 = 0x06060606;
|
||||||
// constexpr uint32_t POINT_OF_DISASTER = ...;
|
// constexpr uint32_t POINT_OF_DISASTER = ...;
|
||||||
// constexpr uint32_t TOYS_TWILIGHT = ...;
|
// constexpr uint32_t TOYS_TWILIGHT = ...;
|
||||||
// constexpr uint32_t COMMANDER_BLADE = ...;
|
// constexpr uint32_t COMMANDER_BLADE = ...;
|
||||||
// constexpr uint32_t UNION_GUARD = ...;
|
// constexpr uint32_t UNION_GUARD = ...;
|
||||||
// constexpr uint32_t TEAM_POINTS_500 = ...;
|
// constexpr uint32_t TEAM_POINTS_500 = ...;
|
||||||
// constexpr uint32_t TEAM_POINTS_1000 = ...;
|
// constexpr uint32_t TEAM_POINTS_1000 = ...;
|
||||||
// constexpr uint32_t TEAM_POINTS_5000 = ...;
|
// constexpr uint32_t TEAM_POINTS_5000 = ...;
|
||||||
// constexpr uint32_t TEAM_POINTS_10000 = ...;
|
// constexpr uint32_t TEAM_POINTS_10000 = ...;
|
||||||
} // namespace TeamRewardMenuItemID
|
} // namespace TeamRewardMenuItemID
|
||||||
|
|
||||||
struct MenuItem {
|
struct MenuItem {
|
||||||
enum Flag {
|
enum Flag {
|
||||||
// For menu items to be visible on DC NTE, they must not have either of the
|
// For menu items to be visible on DC NTE, they must not have either of the
|
||||||
// following two flags. (The INVISIBLE_ON_GC_NTE flag behaves similarly.)
|
// following two flags. (The INVISIBLE_ON_GC_NTE flag behaves similarly.)
|
||||||
INVISIBLE_ON_DC_PROTOS = 0x001,
|
INVISIBLE_ON_DC_PROTOS = 0x001,
|
||||||
INVISIBLE_ON_DC = 0x002,
|
INVISIBLE_ON_DC = 0x002,
|
||||||
INVISIBLE_ON_PC_NTE = 0x004,
|
INVISIBLE_ON_PC_NTE = 0x004,
|
||||||
INVISIBLE_ON_PC = 0x008,
|
INVISIBLE_ON_PC = 0x008,
|
||||||
INVISIBLE_ON_GC_NTE = 0x010,
|
INVISIBLE_ON_GC_NTE = 0x010,
|
||||||
INVISIBLE_ON_GC = 0x020,
|
INVISIBLE_ON_GC = 0x020,
|
||||||
INVISIBLE_ON_XB = 0x040,
|
INVISIBLE_ON_XB = 0x040,
|
||||||
INVISIBLE_ON_BB = 0x080,
|
INVISIBLE_ON_BB = 0x080,
|
||||||
DC_ONLY = INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
DC_ONLY = INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
||||||
PC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_GC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
PC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_GC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
||||||
GC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
GC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
||||||
XB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_BB,
|
XB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_BB,
|
||||||
BB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_XB,
|
BB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_XB,
|
||||||
REQUIRES_MESSAGE_BOXES = 0x100,
|
REQUIRES_MESSAGE_BOXES = 0x100,
|
||||||
REQUIRES_SEND_FUNCTION_CALL = 0x200,
|
REQUIRES_SEND_FUNCTION_CALL = 0x200,
|
||||||
REQUIRES_SAVE_DISABLED = 0x400,
|
REQUIRES_SAVE_DISABLED = 0x400,
|
||||||
INVISIBLE_IN_INFO_MENU = 0x800,
|
INVISIBLE_IN_INFO_MENU = 0x800,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t item_id;
|
uint32_t item_id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string description;
|
std::string description;
|
||||||
std::function<std::string()> get_description;
|
std::function<std::string()> get_description;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
|
||||||
MenuItem(uint32_t item_id, const std::string& name, const std::string& description, uint32_t flags);
|
MenuItem(uint32_t item_id, const std::string& name, const std::string& description, uint32_t flags);
|
||||||
MenuItem(uint32_t item_id, const std::string& name, std::function<std::string()> get_description, uint32_t flags);
|
MenuItem(uint32_t item_id, const std::string& name, std::function<std::string()> get_description, uint32_t flags);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Menu {
|
struct Menu {
|
||||||
uint32_t menu_id;
|
uint32_t menu_id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<MenuItem> items;
|
std::vector<MenuItem> items;
|
||||||
|
|
||||||
Menu() = delete;
|
Menu() = delete;
|
||||||
Menu(uint32_t menu_id, const std::string& name);
|
Menu(uint32_t menu_id, const std::string& name);
|
||||||
};
|
};
|
||||||
|
|||||||
+104
-104
@@ -1,104 +1,104 @@
|
|||||||
#include "NetworkAddresses.hh"
|
#include "NetworkAddresses.hh"
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#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 <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
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(
|
throw runtime_error(string_printf(
|
||||||
"can\'t resolve hostname %s: %s", address, e.c_str()));
|
"can\'t resolve hostname %s: %s", address, e.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<struct addrinfo, void (*)(struct addrinfo*)> res0_unique(res0, freeaddrinfo);
|
std::unique_ptr<struct addrinfo, void (*)(struct addrinfo*)> res0_unique(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!res4) {
|
if (!res4) {
|
||||||
throw runtime_error(string_printf(
|
throw runtime_error(string_printf(
|
||||||
"can\'t resolve hostname %s: no usable data", address));
|
"can\'t resolve hostname %s: no usable data", address));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in* res_sin = (struct sockaddr_in*)res4->ai_addr;
|
struct sockaddr_in* res_sin = (struct sockaddr_in*)res4->ai_addr;
|
||||||
return ntohl(res_sin->sin_addr.s_addr);
|
return ntohl(res_sin->sin_addr.s_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
map<string, uint32_t> get_local_addresses() {
|
map<string, uint32_t> get_local_addresses() {
|
||||||
struct ifaddrs* ifa_raw;
|
struct ifaddrs* ifa_raw;
|
||||||
if (getifaddrs(&ifa_raw)) {
|
if (getifaddrs(&ifa_raw)) {
|
||||||
auto s = string_for_error(errno);
|
auto s = string_for_error(errno);
|
||||||
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) {
|
||||||
if (!i->ifa_addr) {
|
if (!i->ifa_addr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* sin = reinterpret_cast<sockaddr_in*>(i->ifa_addr);
|
auto* sin = reinterpret_cast<sockaddr_in*>(i->ifa_addr);
|
||||||
if (sin->sin_family != AF_INET) {
|
if (sin->sin_family != AF_INET) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.emplace(i->ifa_name, ntohl(sin->sin_addr.s_addr));
|
ret.emplace(i->ifa_name, ntohl(sin->sin_addr.s_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_local_address(uint32_t addr) {
|
bool is_local_address(uint32_t addr) {
|
||||||
uint8_t net = (addr >> 24) & 0xFF;
|
uint8_t net = (addr >> 24) & 0xFF;
|
||||||
return ((net == 127) || (net == 172) || (net == 10) || (net == 192));
|
return ((net == 127) || (net == 172) || (net == 10) || (net == 192));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_local_address(const sockaddr_storage& daddr) {
|
bool is_local_address(const sockaddr_storage& daddr) {
|
||||||
if (daddr.ss_family != AF_INET) {
|
if (daddr.ss_family != AF_INET) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(&daddr);
|
const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(&daddr);
|
||||||
return is_local_address(ntohl(sin->sin_addr.s_addr));
|
return is_local_address(ntohl(sin->sin_addr.s_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
string string_for_address(uint32_t address) {
|
string string_for_address(uint32_t address) {
|
||||||
return string_printf("%hhu.%hhu.%hhu.%hhu",
|
return string_printf("%hhu.%hhu.%hhu.%hhu",
|
||||||
static_cast<uint8_t>(address >> 24), static_cast<uint8_t>(address >> 16),
|
static_cast<uint8_t>(address >> 24), static_cast<uint8_t>(address >> 16),
|
||||||
static_cast<uint8_t>(address >> 8), static_cast<uint8_t>(address));
|
static_cast<uint8_t>(address >> 8), static_cast<uint8_t>(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t address_for_string(const char* address) {
|
uint32_t address_for_string(const char* address) {
|
||||||
return ntohl(inet_addr(address));
|
return ntohl(inet_addr(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t devolution_phone_number_for_netloc(uint32_t addr, uint16_t port) {
|
uint64_t devolution_phone_number_for_netloc(uint32_t addr, uint16_t port) {
|
||||||
// It seems the address part of the number is fixed-width, but the port is
|
// It seems the address part of the number is fixed-width, but the port is
|
||||||
// not. Why did they do it this way?
|
// not. Why did they do it this way?
|
||||||
if (port & 0xF000) {
|
if (port & 0xF000) {
|
||||||
return (static_cast<uint64_t>(addr) << 16) | port;
|
return (static_cast<uint64_t>(addr) << 16) | port;
|
||||||
} else if (port & 0x0F00) {
|
} else if (port & 0x0F00) {
|
||||||
return (static_cast<uint64_t>(addr) << 12) | port;
|
return (static_cast<uint64_t>(addr) << 12) | port;
|
||||||
} else if (port & 0x00F0) {
|
} else if (port & 0x00F0) {
|
||||||
return (static_cast<uint64_t>(addr) << 8) | port;
|
return (static_cast<uint64_t>(addr) << 8) | port;
|
||||||
} else {
|
} else {
|
||||||
return (static_cast<uint64_t>(addr) << 4) | port;
|
return (static_cast<uint64_t>(addr) << 4) | port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-21
@@ -1,21 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#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
|
||||||
|
|
||||||
uint32_t resolve_address(const char* address);
|
uint32_t resolve_address(const char* address);
|
||||||
std::map<std::string, uint32_t> get_local_addresses();
|
std::map<std::string, uint32_t> get_local_addresses();
|
||||||
uint32_t get_connected_address(int fd);
|
uint32_t get_connected_address(int fd);
|
||||||
bool is_local_address(uint32_t daddr);
|
bool is_local_address(uint32_t daddr);
|
||||||
bool is_local_address(const sockaddr_storage& daddr);
|
bool is_local_address(const sockaddr_storage& daddr);
|
||||||
|
|
||||||
std::string string_for_address(uint32_t address);
|
std::string string_for_address(uint32_t address);
|
||||||
uint32_t address_for_string(const char* address);
|
uint32_t address_for_string(const char* address);
|
||||||
|
|
||||||
uint64_t devolution_phone_number_for_netloc(uint32_t addr, uint16_t port);
|
uint64_t devolution_phone_number_for_netloc(uint32_t addr, uint16_t port);
|
||||||
|
|||||||
+914
-914
File diff suppressed because it is too large
Load Diff
+359
-359
@@ -1,359 +1,359 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <phosg/Random.hh>
|
#include <phosg/Random.hh>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Compression.hh"
|
#include "Compression.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
class PSOEncryption {
|
class PSOEncryption {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
V2 = 0,
|
V2 = 0,
|
||||||
V3,
|
V3,
|
||||||
BB,
|
BB,
|
||||||
JSD0,
|
JSD0,
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~PSOEncryption() = default;
|
virtual ~PSOEncryption() = default;
|
||||||
|
|
||||||
virtual void encrypt(void* data, size_t size, bool advance = true) = 0;
|
virtual void encrypt(void* data, size_t size, bool advance = true) = 0;
|
||||||
virtual void decrypt(void* data, size_t size, bool advance = true);
|
virtual void decrypt(void* data, size_t size, bool advance = true);
|
||||||
|
|
||||||
inline void encrypt(std::string& data, bool advance = true) {
|
inline void encrypt(std::string& data, bool advance = true) {
|
||||||
this->encrypt(data.data(), data.size(), advance);
|
this->encrypt(data.data(), data.size(), advance);
|
||||||
}
|
}
|
||||||
inline void decrypt(std::string& data, bool advance = true) {
|
inline void decrypt(std::string& data, bool advance = true) {
|
||||||
this->decrypt(data.data(), data.size(), advance);
|
this->decrypt(data.data(), data.size(), advance);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Type type() const = 0;
|
virtual Type type() const = 0;
|
||||||
|
|
||||||
protected:
|
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);
|
||||||
void encrypt_big_endian(void* data, size_t size, bool advance = true);
|
void encrypt_big_endian(void* data, size_t size, bool advance = true);
|
||||||
void encrypt_minus(void* data, size_t size, bool advance = true);
|
void encrypt_minus(void* data, size_t size, bool advance = true);
|
||||||
void encrypt_big_endian_minus(void* data, size_t size, bool advance = true);
|
void encrypt_big_endian_minus(void* data, size_t size, bool advance = true);
|
||||||
void encrypt_both_endian(void* le_data, void* be_data, size_t size, bool advance = true);
|
void encrypt_both_endian(void* le_data, void* be_data, size_t size, bool advance = true);
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
void encrypt_t(void* data, size_t size, bool advance = true);
|
void encrypt_t(void* data, size_t size, bool advance = true);
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
void encrypt_minus_t(void* data, size_t size, bool advance = true);
|
void encrypt_minus_t(void* data, size_t size, bool advance = true);
|
||||||
|
|
||||||
uint32_t next(bool advance = true);
|
uint32_t next(bool advance = true);
|
||||||
|
|
||||||
inline uint32_t seed() const {
|
inline uint32_t seed() const {
|
||||||
return this->initial_seed;
|
return this->initial_seed;
|
||||||
}
|
}
|
||||||
uint32_t absolute_offset() const {
|
uint32_t absolute_offset() const {
|
||||||
return (this->cycles * this->end_offset) + this->offset;
|
return (this->cycles * this->end_offset) + this->offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PSOLFGEncryption(uint32_t seed, size_t stream_length, size_t end_offset);
|
PSOLFGEncryption(uint32_t seed, size_t stream_length, size_t end_offset);
|
||||||
|
|
||||||
virtual void update_stream() = 0;
|
virtual void update_stream() = 0;
|
||||||
|
|
||||||
std::vector<uint32_t> stream;
|
std::vector<uint32_t> stream;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
size_t end_offset;
|
size_t end_offset;
|
||||||
uint32_t initial_seed;
|
uint32_t initial_seed;
|
||||||
size_t cycles;
|
size_t cycles;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PSOV2Encryption : public PSOLFGEncryption {
|
class PSOV2Encryption : public PSOLFGEncryption {
|
||||||
public:
|
public:
|
||||||
explicit PSOV2Encryption(uint32_t seed);
|
explicit PSOV2Encryption(uint32_t seed);
|
||||||
virtual Type type() const;
|
virtual Type type() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void update_stream();
|
virtual void update_stream();
|
||||||
|
|
||||||
static constexpr size_t STREAM_LENGTH = 0x38;
|
static constexpr size_t STREAM_LENGTH = 0x38;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PSOV3Encryption : public PSOLFGEncryption {
|
class PSOV3Encryption : public PSOLFGEncryption {
|
||||||
public:
|
public:
|
||||||
explicit PSOV3Encryption(uint32_t key);
|
explicit PSOV3Encryption(uint32_t key);
|
||||||
virtual Type type() const;
|
virtual Type type() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void update_stream();
|
virtual void update_stream();
|
||||||
|
|
||||||
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 {
|
||||||
STANDARD = 0x00,
|
STANDARD = 0x00,
|
||||||
MOCB1 = 0x01,
|
MOCB1 = 0x01,
|
||||||
JSD1 = 0x02,
|
JSD1 = 0x02,
|
||||||
TFS1 = 0x03,
|
TFS1 = 0x03,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyFile {
|
struct KeyFile {
|
||||||
// initial_keys are actually a stream of uint32_ts, but we treat them as
|
// initial_keys are actually a stream of uint32_ts, but we treat them as
|
||||||
// bytes for code simplicity
|
// bytes for code simplicity
|
||||||
union InitialKeys {
|
union InitialKeys {
|
||||||
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) {}
|
||||||
} __packed_ws__(InitialKeys, 0x48);
|
} __packed_ws__(InitialKeys, 0x48);
|
||||||
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) {}
|
||||||
} __packed_ws__(PrivateKeys, 0x1000);
|
} __packed_ws__(PrivateKeys, 0x1000);
|
||||||
InitialKeys initial_keys;
|
InitialKeys initial_keys;
|
||||||
PrivateKeys private_keys;
|
PrivateKeys private_keys;
|
||||||
// This field only really needs to be one byte, but annoyingly, some
|
// This field only really needs to be one byte, but annoyingly, some
|
||||||
// compilers pad this structure to a longer alignment, presumably because
|
// compilers pad this structure to a longer alignment, presumably because
|
||||||
// the unions above contain structures with 32-bit alignment. To prevent
|
// the unions above contain structures with 32-bit alignment. To prevent
|
||||||
// this structure's size from not matching the .nsk files' sizes, we use
|
// this structure's size from not matching the .nsk files' sizes, we use
|
||||||
// an unnecessarily large size for this field.
|
// an unnecessarily large size for this field.
|
||||||
le_uint64_t subtype;
|
le_uint64_t subtype;
|
||||||
} __packed_ws__(KeyFile, 0x1050);
|
} __packed_ws__(KeyFile, 0x1050);
|
||||||
|
|
||||||
PSOBBEncryption(const KeyFile& key, const void* seed, size_t seed_size);
|
PSOBBEncryption(const KeyFile& key, const void* seed, size_t seed_size);
|
||||||
|
|
||||||
virtual void encrypt(void* data, size_t size, bool advance = true);
|
virtual void encrypt(void* data, size_t size, bool advance = true);
|
||||||
virtual void decrypt(void* data, size_t size, bool advance = true);
|
virtual void decrypt(void* data, size_t size, bool advance = true);
|
||||||
|
|
||||||
virtual Type type() const;
|
virtual Type type() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
KeyFile state;
|
KeyFile state;
|
||||||
|
|
||||||
void tfs1_scramble(uint32_t* out1, uint32_t* out2) const;
|
void tfs1_scramble(uint32_t* out1, uint32_t* out2) const;
|
||||||
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
|
||||||
|
|
||||||
class PSOV2OrV3DetectorEncryption : public PSOEncryption {
|
class PSOV2OrV3DetectorEncryption : public PSOEncryption {
|
||||||
public:
|
public:
|
||||||
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);
|
||||||
|
|
||||||
virtual void encrypt(void* data, size_t size, bool advance = true);
|
virtual void encrypt(void* data, size_t size, bool advance = true);
|
||||||
|
|
||||||
virtual Type type() const;
|
virtual Type type() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
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;
|
||||||
std::unique_ptr<PSOEncryption> active_crypt;
|
std::unique_ptr<PSOEncryption> active_crypt;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PSOV2OrV3ImitatorEncryption : public PSOEncryption {
|
class PSOV2OrV3ImitatorEncryption : public PSOEncryption {
|
||||||
public:
|
public:
|
||||||
PSOV2OrV3ImitatorEncryption(
|
PSOV2OrV3ImitatorEncryption(
|
||||||
uint32_t key, std::shared_ptr<PSOV2OrV3DetectorEncryption> client_crypt);
|
uint32_t key, std::shared_ptr<PSOV2OrV3DetectorEncryption> client_crypt);
|
||||||
|
|
||||||
virtual void encrypt(void* data, size_t size, bool advance = true);
|
virtual void encrypt(void* data, size_t size, bool advance = true);
|
||||||
|
|
||||||
virtual Type type() const;
|
virtual Type type() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t key;
|
uint32_t key;
|
||||||
std::shared_ptr<const PSOV2OrV3DetectorEncryption> detector_crypt;
|
std::shared_ptr<const PSOV2OrV3DetectorEncryption> detector_crypt;
|
||||||
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
|
||||||
|
|
||||||
class PSOBBMultiKeyDetectorEncryption : public PSOEncryption {
|
class PSOBBMultiKeyDetectorEncryption : public PSOEncryption {
|
||||||
public:
|
public:
|
||||||
PSOBBMultiKeyDetectorEncryption(
|
PSOBBMultiKeyDetectorEncryption(
|
||||||
const std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>>& possible_keys,
|
const std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>>& possible_keys,
|
||||||
const std::unordered_set<std::string>& expected_first_data,
|
const std::unordered_set<std::string>& expected_first_data,
|
||||||
const void* seed,
|
const void* seed,
|
||||||
size_t seed_size);
|
size_t seed_size);
|
||||||
|
|
||||||
virtual void encrypt(void* data, size_t size, bool advance = true);
|
virtual void encrypt(void* data, size_t size, bool advance = true);
|
||||||
virtual void decrypt(void* data, size_t size, bool advance = true);
|
virtual void decrypt(void* data, size_t size, bool advance = true);
|
||||||
|
|
||||||
inline std::shared_ptr<const PSOBBEncryption::KeyFile> get_active_key() const {
|
inline std::shared_ptr<const PSOBBEncryption::KeyFile> get_active_key() const {
|
||||||
return this->active_key;
|
return this->active_key;
|
||||||
}
|
}
|
||||||
inline const std::string& get_seed() const {
|
inline const std::string& get_seed() const {
|
||||||
return this->seed;
|
return this->seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Type type() const;
|
virtual Type type() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> possible_keys;
|
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> possible_keys;
|
||||||
std::shared_ptr<const PSOBBEncryption::KeyFile> active_key;
|
std::shared_ptr<const PSOBBEncryption::KeyFile> active_key;
|
||||||
std::shared_ptr<PSOBBEncryption> active_crypt;
|
std::shared_ptr<PSOBBEncryption> active_crypt;
|
||||||
const std::unordered_set<std::string>& expected_first_data;
|
const std::unordered_set<std::string>& expected_first_data;
|
||||||
std::string seed;
|
std::string seed;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PSOBBMultiKeyImitatorEncryption : public PSOEncryption {
|
class PSOBBMultiKeyImitatorEncryption : public PSOEncryption {
|
||||||
public:
|
public:
|
||||||
PSOBBMultiKeyImitatorEncryption(
|
PSOBBMultiKeyImitatorEncryption(
|
||||||
std::shared_ptr<const PSOBBMultiKeyDetectorEncryption> client_crypt,
|
std::shared_ptr<const PSOBBMultiKeyDetectorEncryption> client_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);
|
||||||
|
|
||||||
virtual void encrypt(void* data, size_t size, bool advance = true);
|
virtual void encrypt(void* data, size_t size, bool advance = true);
|
||||||
virtual void decrypt(void* data, size_t size, bool advance = true);
|
virtual void decrypt(void* data, size_t size, bool advance = true);
|
||||||
|
|
||||||
virtual Type type() const;
|
virtual Type type() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<PSOBBEncryption> ensure_crypt();
|
std::shared_ptr<PSOBBEncryption> ensure_crypt();
|
||||||
|
|
||||||
std::shared_ptr<const PSOBBMultiKeyDetectorEncryption> detector_crypt;
|
std::shared_ptr<const PSOBBMultiKeyDetectorEncryption> detector_crypt;
|
||||||
std::shared_ptr<PSOBBEncryption> active_crypt;
|
std::shared_ptr<PSOBBEncryption> active_crypt;
|
||||||
std::string seed;
|
std::string seed;
|
||||||
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);
|
||||||
|
|
||||||
virtual void encrypt(void* data, size_t size, bool advance = true);
|
virtual void encrypt(void* data, size_t size, bool advance = true);
|
||||||
virtual void decrypt(void* data, size_t size, bool advance = true);
|
virtual void decrypt(void* data, size_t size, bool advance = true);
|
||||||
|
|
||||||
virtual Type type() const = 0;
|
virtual Type type() const = 0;
|
||||||
|
|
||||||
private:
|
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);
|
||||||
|
|
||||||
uint32_t encrypt_challenge_time(uint16_t value);
|
uint32_t encrypt_challenge_time(uint16_t value);
|
||||||
uint16_t decrypt_challenge_time(uint32_t value);
|
uint16_t decrypt_challenge_time(uint32_t value);
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
class ChallengeTimeT {
|
class ChallengeTimeT {
|
||||||
private:
|
private:
|
||||||
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;
|
||||||
U32T value;
|
U32T value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ChallengeTimeT() = default;
|
ChallengeTimeT() = default;
|
||||||
ChallengeTimeT(uint16_t v) {
|
ChallengeTimeT(uint16_t v) {
|
||||||
this->encode(v);
|
this->encode(v);
|
||||||
}
|
}
|
||||||
ChallengeTimeT(const ChallengeTimeT& other) = default;
|
ChallengeTimeT(const ChallengeTimeT& other) = default;
|
||||||
ChallengeTimeT(ChallengeTimeT&& other) = default;
|
ChallengeTimeT(ChallengeTimeT&& other) = default;
|
||||||
ChallengeTimeT& operator=(const ChallengeTimeT& other) = default;
|
ChallengeTimeT& operator=(const ChallengeTimeT& other) = default;
|
||||||
ChallengeTimeT& operator=(ChallengeTimeT&& other) = default;
|
ChallengeTimeT& operator=(ChallengeTimeT&& other) = default;
|
||||||
|
|
||||||
bool has_value() const {
|
bool has_value() const {
|
||||||
return this->value != 0;
|
return this->value != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t load_raw() const {
|
uint32_t load_raw() const {
|
||||||
return this->value;
|
return this->value;
|
||||||
}
|
}
|
||||||
void store_raw(uint32_t value) {
|
void store_raw(uint32_t value) {
|
||||||
this->value = value;
|
this->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t decode() const {
|
uint16_t decode() const {
|
||||||
return decrypt_challenge_time(this->value);
|
return decrypt_challenge_time(this->value);
|
||||||
}
|
}
|
||||||
void encode(uint16_t v) {
|
void encode(uint16_t v) {
|
||||||
this->value = ((v == 0) || (v == 0xFFFF)) ? 0 : encrypt_challenge_time(v);
|
this->value = ((v == 0) || (v == 0xFFFF)) ? 0 : encrypt_challenge_time(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator ChallengeTimeT<!IsBigEndian>() const {
|
operator ChallengeTimeT<!IsBigEndian>() const {
|
||||||
ChallengeTimeT<!IsBigEndian> ret;
|
ChallengeTimeT<!IsBigEndian> ret;
|
||||||
ret.store_raw(this->value);
|
ret.store_raw(this->value);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed__;
|
} __packed__;
|
||||||
using ChallengeTime = ChallengeTimeT<false>;
|
using ChallengeTime = ChallengeTimeT<false>;
|
||||||
using ChallengeTimeBE = ChallengeTimeT<true>;
|
using ChallengeTimeBE = ChallengeTimeT<true>;
|
||||||
check_struct_size(ChallengeTime, 4);
|
check_struct_size(ChallengeTime, 4);
|
||||||
check_struct_size(ChallengeTimeBE, 4);
|
check_struct_size(ChallengeTimeBE, 4);
|
||||||
|
|
||||||
std::string decrypt_v2_registry_value(const void* data, size_t size);
|
std::string decrypt_v2_registry_value(const void* data, size_t size);
|
||||||
|
|
||||||
struct DecryptedPR2 {
|
struct DecryptedPR2 {
|
||||||
std::string compressed_data;
|
std::string compressed_data;
|
||||||
size_t decompressed_size;
|
size_t decompressed_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
DecryptedPR2 decrypt_pr2_data(const std::string& data) {
|
DecryptedPR2 decrypt_pr2_data(const std::string& data) {
|
||||||
using U32T = std::conditional_t<IsBigEndian, be_uint32_t, le_uint32_t>;
|
using U32T = std::conditional_t<IsBigEndian, be_uint32_t, le_uint32_t>;
|
||||||
|
|
||||||
if (data.size() < 8) {
|
if (data.size() < 8) {
|
||||||
throw std::runtime_error("not enough data for PR2 header");
|
throw std::runtime_error("not enough data for PR2 header");
|
||||||
}
|
}
|
||||||
StringReader r(data);
|
StringReader r(data);
|
||||||
DecryptedPR2 ret = {
|
DecryptedPR2 ret = {
|
||||||
.compressed_data = data.substr(8),
|
.compressed_data = data.substr(8),
|
||||||
.decompressed_size = r.get<U32T>()};
|
.decompressed_size = r.get<U32T>()};
|
||||||
PSOV2Encryption crypt(r.get<U32T>());
|
PSOV2Encryption crypt(r.get<U32T>());
|
||||||
if (IsBigEndian) {
|
if (IsBigEndian) {
|
||||||
crypt.encrypt_big_endian(ret.compressed_data.data(), ret.compressed_data.size());
|
crypt.encrypt_big_endian(ret.compressed_data.data(), ret.compressed_data.size());
|
||||||
} else {
|
} else {
|
||||||
crypt.decrypt(ret.compressed_data.data(), ret.compressed_data.size());
|
crypt.decrypt(ret.compressed_data.data(), ret.compressed_data.size());
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
std::string decrypt_and_decompress_pr2_data(const std::string& data) {
|
std::string decrypt_and_decompress_pr2_data(const std::string& data) {
|
||||||
auto decrypted = decrypt_pr2_data<IsBigEndian>(data);
|
auto decrypted = decrypt_pr2_data<IsBigEndian>(data);
|
||||||
std::string decompressed = prs_decompress(decrypted.compressed_data);
|
std::string decompressed = prs_decompress(decrypted.compressed_data);
|
||||||
if (decompressed.size() != decrypted.decompressed_size) {
|
if (decompressed.size() != decrypted.decompressed_size) {
|
||||||
throw std::runtime_error("decompressed size does not match expected size");
|
throw std::runtime_error("decompressed size does not match expected size");
|
||||||
}
|
}
|
||||||
return decompressed;
|
return decompressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
std::string encrypt_pr2_data(const std::string& data, size_t decompressed_size, uint32_t seed) {
|
std::string encrypt_pr2_data(const std::string& data, size_t decompressed_size, uint32_t seed) {
|
||||||
using U32T = std::conditional_t<IsBigEndian, be_uint32_t, le_uint32_t>;
|
using U32T = std::conditional_t<IsBigEndian, be_uint32_t, le_uint32_t>;
|
||||||
|
|
||||||
StringWriter w;
|
StringWriter w;
|
||||||
w.put<U32T>(decompressed_size);
|
w.put<U32T>(decompressed_size);
|
||||||
w.put<U32T>(seed);
|
w.put<U32T>(seed);
|
||||||
w.write(data);
|
w.write(data);
|
||||||
|
|
||||||
std::string ret = std::move(w.str());
|
std::string ret = std::move(w.str());
|
||||||
PSOV2Encryption crypt(seed);
|
PSOV2Encryption crypt(seed);
|
||||||
if (IsBigEndian) {
|
if (IsBigEndian) {
|
||||||
crypt.encrypt_big_endian(ret.data() + 8, ret.size() - 8);
|
crypt.encrypt_big_endian(ret.data() + 8, ret.size() - 8);
|
||||||
} else {
|
} else {
|
||||||
crypt.decrypt(ret.data() + 8, ret.size() - 8);
|
crypt.decrypt(ret.data() + 8, ret.size() - 8);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t random_from_optional_crypt(std::shared_ptr<PSOLFGEncryption> random_crypt) {
|
inline uint32_t random_from_optional_crypt(std::shared_ptr<PSOLFGEncryption> random_crypt) {
|
||||||
return random_crypt ? random_crypt->next() : random_object<uint32_t>();
|
return random_crypt ? random_crypt->next() : random_object<uint32_t>();
|
||||||
}
|
}
|
||||||
|
|||||||
+270
-270
@@ -1,270 +1,270 @@
|
|||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
|
|
||||||
#include <event2/buffer.h>
|
#include <event2/buffer.h>
|
||||||
|
|
||||||
#include <phosg/Strings.hh>
|
#include <phosg/Strings.hh>
|
||||||
#include <stdexcept>
|
#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;
|
||||||
this->bb.flag = 0;
|
this->bb.flag = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t PSOCommandHeader::command(Version version) const {
|
uint16_t PSOCommandHeader::command(Version version) const {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return this->pc.command;
|
return this->pc.command;
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
return this->dc.command;
|
return this->dc.command;
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return this->gc.command;
|
return this->gc.command;
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return this->xb.command;
|
return this->xb.command;
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return this->bb.command;
|
return this->bb.command;
|
||||||
default:
|
default:
|
||||||
throw logic_error("unknown game version");
|
throw logic_error("unknown game version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PSOCommandHeader::set_command(Version version, uint16_t command) {
|
void PSOCommandHeader::set_command(Version version, uint16_t command) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
this->pc.command = command;
|
this->pc.command = command;
|
||||||
break;
|
break;
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
this->dc.command = command;
|
this->dc.command = command;
|
||||||
break;
|
break;
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
this->gc.command = command;
|
this->gc.command = command;
|
||||||
break;
|
break;
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
this->xb.command = command;
|
this->xb.command = command;
|
||||||
break;
|
break;
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
this->bb.command = command;
|
this->bb.command = command;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw logic_error("unknown game version");
|
throw logic_error("unknown game version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t PSOCommandHeader::size(Version version) const {
|
uint16_t PSOCommandHeader::size(Version version) const {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return this->pc.size;
|
return this->pc.size;
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
return this->dc.size;
|
return this->dc.size;
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return this->gc.size;
|
return this->gc.size;
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return this->xb.size;
|
return this->xb.size;
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return this->bb.size;
|
return this->bb.size;
|
||||||
default:
|
default:
|
||||||
throw logic_error("unknown game version");
|
throw logic_error("unknown game version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PSOCommandHeader::set_size(Version version, uint32_t size) {
|
void PSOCommandHeader::set_size(Version version, uint32_t size) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
this->pc.size = size;
|
this->pc.size = size;
|
||||||
break;
|
break;
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
this->dc.size = size;
|
this->dc.size = size;
|
||||||
break;
|
break;
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
this->gc.size = size;
|
this->gc.size = size;
|
||||||
break;
|
break;
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
this->xb.size = size;
|
this->xb.size = size;
|
||||||
break;
|
break;
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
this->bb.size = size;
|
this->bb.size = size;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw logic_error("unknown game version");
|
throw logic_error("unknown game version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t PSOCommandHeader::flag(Version version) const {
|
uint32_t PSOCommandHeader::flag(Version version) const {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return this->pc.flag;
|
return this->pc.flag;
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
return this->dc.flag;
|
return this->dc.flag;
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return this->gc.flag;
|
return this->gc.flag;
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return this->xb.flag;
|
return this->xb.flag;
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return this->bb.flag;
|
return this->bb.flag;
|
||||||
default:
|
default:
|
||||||
throw logic_error("unknown game version");
|
throw logic_error("unknown game version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PSOCommandHeader::set_flag(Version version, uint32_t flag) {
|
void PSOCommandHeader::set_flag(Version version, uint32_t flag) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
this->pc.flag = flag;
|
this->pc.flag = flag;
|
||||||
break;
|
break;
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
this->dc.flag = flag;
|
this->dc.flag = flag;
|
||||||
break;
|
break;
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
this->gc.flag = flag;
|
this->gc.flag = flag;
|
||||||
break;
|
break;
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
this->xb.flag = flag;
|
this->xb.flag = flag;
|
||||||
break;
|
break;
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
this->bb.flag = flag;
|
this->bb.flag = flag;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw logic_error("unknown game version");
|
throw logic_error("unknown game version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
||||||
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
||||||
min_size, size));
|
min_size, size));
|
||||||
}
|
}
|
||||||
if (max_size < min_size) {
|
if (max_size < min_size) {
|
||||||
max_size = min_size;
|
max_size = min_size;
|
||||||
}
|
}
|
||||||
if (size > max_size) {
|
if (size > max_size) {
|
||||||
throw std::runtime_error(string_printf(
|
throw std::runtime_error(string_printf(
|
||||||
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
||||||
max_size, size));
|
max_size, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string prepend_command_header(
|
std::string prepend_command_header(
|
||||||
Version version,
|
Version version,
|
||||||
bool encryption_enabled,
|
bool encryption_enabled,
|
||||||
uint16_t cmd,
|
uint16_t cmd,
|
||||||
uint32_t flag,
|
uint32_t flag,
|
||||||
const std::string& data) {
|
const std::string& data) {
|
||||||
StringWriter ret;
|
StringWriter ret;
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
case Version::XB_V3: {
|
case Version::XB_V3: {
|
||||||
PSOCommandHeaderDCV3 header;
|
PSOCommandHeaderDCV3 header;
|
||||||
if (encryption_enabled) {
|
if (encryption_enabled) {
|
||||||
header.size = (sizeof(header) + data.size() + 3) & ~3;
|
header.size = (sizeof(header) + data.size() + 3) & ~3;
|
||||||
} else {
|
} else {
|
||||||
header.size = (sizeof(header) + data.size());
|
header.size = (sizeof(header) + data.size());
|
||||||
}
|
}
|
||||||
header.command = cmd;
|
header.command = cmd;
|
||||||
header.flag = flag;
|
header.flag = flag;
|
||||||
ret.put(header);
|
ret.put(header);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2: {
|
case Version::PC_V2: {
|
||||||
PSOCommandHeaderPC header;
|
PSOCommandHeaderPC header;
|
||||||
if (encryption_enabled) {
|
if (encryption_enabled) {
|
||||||
header.size = (sizeof(header) + data.size() + 3) & ~3;
|
header.size = (sizeof(header) + data.size() + 3) & ~3;
|
||||||
} else {
|
} else {
|
||||||
header.size = (sizeof(header) + data.size());
|
header.size = (sizeof(header) + data.size());
|
||||||
}
|
}
|
||||||
header.command = cmd;
|
header.command = cmd;
|
||||||
header.flag = flag;
|
header.flag = flag;
|
||||||
ret.put(header);
|
ret.put(header);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Version::BB_V4: {
|
case Version::BB_V4: {
|
||||||
PSOCommandHeaderBB header;
|
PSOCommandHeaderBB header;
|
||||||
if (encryption_enabled) {
|
if (encryption_enabled) {
|
||||||
header.size = (sizeof(header) + data.size() + 3) & ~3;
|
header.size = (sizeof(header) + data.size() + 3) & ~3;
|
||||||
} else {
|
} else {
|
||||||
header.size = (sizeof(header) + data.size());
|
header.size = (sizeof(header) + data.size());
|
||||||
}
|
}
|
||||||
header.command = cmd;
|
header.command = cmd;
|
||||||
header.flag = flag;
|
header.flag = flag;
|
||||||
ret.put(header);
|
ret.put(header);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw logic_error("unimplemented game version in prepend_command_header");
|
throw logic_error("unimplemented game version in prepend_command_header");
|
||||||
}
|
}
|
||||||
ret.write(data);
|
ret.write(data);
|
||||||
return std::move(ret.str());
|
return std::move(ret.str());
|
||||||
}
|
}
|
||||||
|
|||||||
+130
-130
@@ -1,130 +1,130 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/bufferevent.h>
|
#include <event2/bufferevent.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <phosg/Strings.hh>
|
#include <phosg/Strings.hh>
|
||||||
|
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
struct PSOCommandHeaderPC {
|
struct PSOCommandHeaderPC {
|
||||||
le_uint16_t size;
|
le_uint16_t size;
|
||||||
uint8_t command;
|
uint8_t command;
|
||||||
uint8_t flag;
|
uint8_t flag;
|
||||||
} __packed_ws__(PSOCommandHeaderPC, 4);
|
} __packed_ws__(PSOCommandHeaderPC, 4);
|
||||||
|
|
||||||
struct PSOCommandHeaderDCV3 {
|
struct PSOCommandHeaderDCV3 {
|
||||||
uint8_t command;
|
uint8_t command;
|
||||||
uint8_t flag;
|
uint8_t flag;
|
||||||
le_uint16_t size;
|
le_uint16_t size;
|
||||||
} __packed_ws__(PSOCommandHeaderDCV3, 4);
|
} __packed_ws__(PSOCommandHeaderDCV3, 4);
|
||||||
|
|
||||||
struct PSOCommandHeaderBB {
|
struct PSOCommandHeaderBB {
|
||||||
le_uint16_t size;
|
le_uint16_t size;
|
||||||
le_uint16_t command;
|
le_uint16_t command;
|
||||||
le_uint32_t flag;
|
le_uint32_t flag;
|
||||||
} __packed_ws__(PSOCommandHeaderBB, 8);
|
} __packed_ws__(PSOCommandHeaderBB, 8);
|
||||||
|
|
||||||
union PSOCommandHeader {
|
union PSOCommandHeader {
|
||||||
PSOCommandHeaderDCV3 dc;
|
PSOCommandHeaderDCV3 dc;
|
||||||
PSOCommandHeaderPC pc;
|
PSOCommandHeaderPC pc;
|
||||||
PSOCommandHeaderDCV3 gc;
|
PSOCommandHeaderDCV3 gc;
|
||||||
PSOCommandHeaderDCV3 xb;
|
PSOCommandHeaderDCV3 xb;
|
||||||
PSOCommandHeaderBB bb;
|
PSOCommandHeaderBB bb;
|
||||||
|
|
||||||
uint16_t command(Version version) const;
|
uint16_t command(Version version) const;
|
||||||
void set_command(Version version, uint16_t command);
|
void set_command(Version version, uint16_t command);
|
||||||
uint16_t size(Version version) const;
|
uint16_t size(Version version) const;
|
||||||
void set_size(Version version, uint32_t size);
|
void set_size(Version version, uint32_t size);
|
||||||
uint32_t flag(Version version) const;
|
uint32_t flag(Version version) const;
|
||||||
void set_flag(Version version, uint32_t flag);
|
void set_flag(Version version, uint32_t flag);
|
||||||
static inline size_t header_size(Version version) {
|
static inline size_t header_size(Version version) {
|
||||||
return (version == Version::BB_V4) ? 8 : 4;
|
return (version == Version::BB_V4) ? 8 : 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
PSOCommandHeader();
|
PSOCommandHeader();
|
||||||
} __packed_ws__(PSOCommandHeader, 8);
|
} __packed_ws__(PSOCommandHeader, 8);
|
||||||
|
|
||||||
// This function is used in a lot of places to check received command sizes and
|
// This function is used in a lot of places to check received command sizes and
|
||||||
// cast them to the appropriate type
|
// cast them to the appropriate type
|
||||||
template <typename RetT, typename PtrT>
|
template <typename RetT, typename PtrT>
|
||||||
RetT& check_size_generic(
|
RetT& check_size_generic(
|
||||||
PtrT data,
|
PtrT data,
|
||||||
size_t size,
|
size_t size,
|
||||||
size_t min_size,
|
size_t min_size,
|
||||||
size_t max_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(
|
||||||
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
"command too small (expected at least 0x%zX bytes, received 0x%zX bytes)",
|
||||||
min_size, size));
|
min_size, size));
|
||||||
}
|
}
|
||||||
if (size > max_size) {
|
if (size > max_size) {
|
||||||
throw std::runtime_error(string_printf(
|
throw std::runtime_error(string_printf(
|
||||||
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
"command too large (expected at most 0x%zX bytes, received 0x%zX bytes)",
|
||||||
max_size, size));
|
max_size, size));
|
||||||
}
|
}
|
||||||
return *reinterpret_cast<RetT*>(data);
|
return *reinterpret_cast<RetT*>(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T& check_size_t(const std::string& data, size_t min_size, size_t max_size) {
|
const T& check_size_t(const std::string& data, size_t min_size, size_t max_size) {
|
||||||
return check_size_generic<const T, const void*>(data.data(), data.size(), min_size, max_size);
|
return check_size_generic<const T, const void*>(data.data(), data.size(), min_size, max_size);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T& check_size_t(const std::string& data, size_t max_size) {
|
const T& check_size_t(const std::string& data, size_t max_size) {
|
||||||
return check_size_generic<const T, const void*>(data.data(), data.size(), sizeof(T), max_size);
|
return check_size_generic<const T, const void*>(data.data(), data.size(), sizeof(T), max_size);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T& check_size_t(const std::string& data) {
|
const T& check_size_t(const std::string& data) {
|
||||||
return check_size_generic<const T, const void*>(data.data(), data.size(), sizeof(T), sizeof(T));
|
return check_size_generic<const T, const void*>(data.data(), data.size(), sizeof(T), sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& check_size_t(std::string& data, size_t min_size, size_t max_size) {
|
T& check_size_t(std::string& data, size_t min_size, size_t max_size) {
|
||||||
return check_size_generic<T, void*>(data.data(), data.size(), min_size, max_size);
|
return check_size_generic<T, void*>(data.data(), data.size(), min_size, max_size);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& check_size_t(std::string& data, size_t max_size) {
|
T& check_size_t(std::string& data, size_t max_size) {
|
||||||
return check_size_generic<T, void*>(data.data(), data.size(), sizeof(T), max_size);
|
return check_size_generic<T, void*>(data.data(), data.size(), sizeof(T), max_size);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& check_size_t(std::string& data) {
|
T& check_size_t(std::string& data) {
|
||||||
return check_size_generic<T, void*>(data.data(), data.size(), sizeof(T), sizeof(T));
|
return check_size_generic<T, void*>(data.data(), data.size(), sizeof(T), sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T& check_size_t(const void* data, size_t size, size_t min_size, size_t max_size) {
|
const T& check_size_t(const void* data, size_t size, size_t min_size, size_t max_size) {
|
||||||
return check_size_generic<const T, const void*>(data, size, min_size, max_size);
|
return check_size_generic<const T, const void*>(data, size, min_size, max_size);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T& check_size_t(const void* data, size_t size, size_t max_size) {
|
const T& check_size_t(const void* data, size_t size, size_t max_size) {
|
||||||
return check_size_generic<const T, const void*>(data, size, sizeof(T), max_size);
|
return check_size_generic<const T, const void*>(data, size, sizeof(T), max_size);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T& check_size_t(const void* data, size_t size) {
|
const T& check_size_t(const void* data, size_t size) {
|
||||||
return check_size_generic<const T, const void*>(data, size, sizeof(T), sizeof(T));
|
return check_size_generic<const T, const void*>(data, size, sizeof(T), sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& check_size_t(void* data, size_t size, size_t min_size, size_t max_size) {
|
T& check_size_t(void* data, size_t size, size_t min_size, size_t max_size) {
|
||||||
return check_size_generic<T, void*>(data, size, min_size, max_size);
|
return check_size_generic<T, void*>(data, size, min_size, max_size);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& check_size_t(void* data, size_t size, size_t max_size) {
|
T& check_size_t(void* data, size_t size, size_t max_size) {
|
||||||
return check_size_generic<T, void*>(data, size, sizeof(T), max_size);
|
return check_size_generic<T, void*>(data, size, sizeof(T), max_size);
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& check_size_t(void* data, size_t size) {
|
T& check_size_t(void* data, size_t size) {
|
||||||
return check_size_generic<T, void*>(data, size, sizeof(T), sizeof(T));
|
return check_size_generic<T, void*>(data, size, sizeof(T), sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_size_v(size_t size, size_t min_size, size_t max_size = 0);
|
void check_size_v(size_t size, size_t min_size, size_t max_size = 0);
|
||||||
|
|
||||||
std::string prepend_command_header(
|
std::string prepend_command_header(
|
||||||
Version version,
|
Version version,
|
||||||
bool encryption_enabled,
|
bool encryption_enabled,
|
||||||
uint16_t cmd,
|
uint16_t cmd,
|
||||||
uint32_t flag,
|
uint32_t flag,
|
||||||
const std::string& data);
|
const std::string& data);
|
||||||
|
|||||||
+162
-162
@@ -1,162 +1,162 @@
|
|||||||
#include "PatchFileIndex.hh"
|
#include "PatchFileIndex.hh"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#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 <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),
|
: index(index),
|
||||||
crc32(0),
|
crc32(0),
|
||||||
size(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) {
|
||||||
string relative_path = join(this->path_directories, "/") + "/" + this->name;
|
string relative_path = join(this->path_directories, "/") + "/" + this->name;
|
||||||
string full_path = this->index->root_dir + "/" + relative_path;
|
string full_path = this->index->root_dir + "/" + relative_path;
|
||||||
patch_index_log.info("Loading data for %s", relative_path.c_str());
|
patch_index_log.info("Loading data for %s", relative_path.c_str());
|
||||||
this->loaded_data = make_shared<string>(load_file(full_path));
|
this->loaded_data = make_shared<string>(load_file(full_path));
|
||||||
this->size = this->loaded_data->size();
|
this->size = this->loaded_data->size();
|
||||||
}
|
}
|
||||||
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";
|
||||||
JSON metadata_cache_json;
|
JSON metadata_cache_json;
|
||||||
try {
|
try {
|
||||||
string metadata_text = load_file(metadata_cache_filename);
|
string metadata_text = load_file(metadata_cache_filename);
|
||||||
metadata_cache_json = JSON::parse(metadata_text);
|
metadata_cache_json = JSON::parse(metadata_text);
|
||||||
patch_index_log.info("Loaded patch metadata cache from %s", metadata_cache_filename.c_str());
|
patch_index_log.info("Loaded patch metadata cache from %s", metadata_cache_filename.c_str());
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
metadata_cache_json = JSON::dict();
|
metadata_cache_json = JSON::dict();
|
||||||
patch_index_log.warning("Cannot load patch metadata cache from %s: %s", metadata_cache_filename.c_str(), e.what());
|
patch_index_log.warning("Cannot load patch metadata cache from %s: %s", metadata_cache_filename.c_str(), e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assuming it's rare for patch files to change, we skip writing the metadata
|
// Assuming it's rare for patch files to change, we skip writing the metadata
|
||||||
// cache if no files were changed at all (which should usually be the case)
|
// cache if no files were changed at all (which should usually be the case)
|
||||||
bool should_write_metadata_cache = false;
|
bool should_write_metadata_cache = false;
|
||||||
JSON new_metadata_cache_json = JSON::dict();
|
JSON new_metadata_cache_json = JSON::dict();
|
||||||
|
|
||||||
vector<string> path_directories;
|
vector<string> path_directories;
|
||||||
function<void(const string&)> collect_dir = [&](const string& dir) -> void {
|
function<void(const string&)> collect_dir = [&](const string& dir) -> void {
|
||||||
path_directories.emplace_back(dir);
|
path_directories.emplace_back(dir);
|
||||||
|
|
||||||
string relative_dirs = join(path_directories, "/");
|
string relative_dirs = join(path_directories, "/");
|
||||||
string full_dir_path = root_dir + '/' + relative_dirs;
|
string full_dir_path = root_dir + '/' + relative_dirs;
|
||||||
patch_index_log.info("Listing directory %s", full_dir_path.c_str());
|
patch_index_log.info("Listing directory %s", full_dir_path.c_str());
|
||||||
|
|
||||||
for (const auto& item : list_directory(full_dir_path)) {
|
for (const auto& item : list_directory(full_dir_path)) {
|
||||||
// Skip invisible files (e.g. .DS_Store on macOS)
|
// Skip invisible files (e.g. .DS_Store on macOS)
|
||||||
if (starts_with(item, ".")) {
|
if (starts_with(item, ".")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string relative_item_path = relative_dirs + '/' + item;
|
string relative_item_path = relative_dirs + '/' + item;
|
||||||
string full_item_path = root_dir + '/' + relative_item_path;
|
string full_item_path = root_dir + '/' + relative_item_path;
|
||||||
if (isdir(full_item_path)) {
|
if (isdir(full_item_path)) {
|
||||||
collect_dir(item);
|
collect_dir(item);
|
||||||
} else if (isfile(full_item_path)) {
|
} else if (isfile(full_item_path)) {
|
||||||
|
|
||||||
auto st = stat(full_item_path);
|
auto st = stat(full_item_path);
|
||||||
|
|
||||||
auto f = make_shared<File>(this);
|
auto f = make_shared<File>(this);
|
||||||
f->path_directories = path_directories;
|
f->path_directories = path_directories;
|
||||||
f->name = item;
|
f->name = item;
|
||||||
|
|
||||||
string compute_crc32s_message; // If not empty, should compute crc32s
|
string compute_crc32s_message; // If not empty, should compute crc32s
|
||||||
JSON cache_item_json;
|
JSON cache_item_json;
|
||||||
try {
|
try {
|
||||||
cache_item_json = metadata_cache_json.at(relative_item_path);
|
cache_item_json = metadata_cache_json.at(relative_item_path);
|
||||||
uint64_t cached_size = cache_item_json.get_int(0);
|
uint64_t cached_size = cache_item_json.get_int(0);
|
||||||
uint64_t cached_mtime = cache_item_json.get_int(1);
|
uint64_t cached_mtime = cache_item_json.get_int(1);
|
||||||
if (static_cast<uint64_t>(st.st_mtime) != cached_mtime) {
|
if (static_cast<uint64_t>(st.st_mtime) != cached_mtime) {
|
||||||
throw runtime_error("file has been modified");
|
throw runtime_error("file has been modified");
|
||||||
}
|
}
|
||||||
if (static_cast<uint64_t>(st.st_size) != cached_size) {
|
if (static_cast<uint64_t>(st.st_size) != cached_size) {
|
||||||
throw runtime_error("file size has changed");
|
throw runtime_error("file size has changed");
|
||||||
}
|
}
|
||||||
f->size = cached_size;
|
f->size = cached_size;
|
||||||
f->crc32 = cache_item_json.get_int(2);
|
f->crc32 = cache_item_json.get_int(2);
|
||||||
for (const auto& chunk_crc32_json : cache_item_json.get_list(3)) {
|
for (const auto& chunk_crc32_json : cache_item_json.get_list(3)) {
|
||||||
f->chunk_crcs.emplace_back(chunk_crc32_json->as_int());
|
f->chunk_crcs.emplace_back(chunk_crc32_json->as_int());
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
compute_crc32s_message = e.what();
|
compute_crc32s_message = e.what();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!compute_crc32s_message.empty()) {
|
if (!compute_crc32s_message.empty()) {
|
||||||
auto data = f->load_data(); // Sets f->size
|
auto data = f->load_data(); // Sets f->size
|
||||||
f->crc32 = crc32(data->data(), f->size);
|
f->crc32 = crc32(data->data(), f->size);
|
||||||
for (size_t x = 0; x < data->size(); x += 0x4000) {
|
for (size_t x = 0; x < data->size(); x += 0x4000) {
|
||||||
size_t chunk_bytes = min<size_t>(f->size - x, 0x4000);
|
size_t chunk_bytes = min<size_t>(f->size - x, 0x4000);
|
||||||
f->chunk_crcs.emplace_back(::crc32(data->data() + x, chunk_bytes));
|
f->chunk_crcs.emplace_back(::crc32(data->data() + x, chunk_bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
// File was modified or cache item was missing; make a new cache item
|
// File was modified or cache item was missing; make a new cache item
|
||||||
auto chunk_crcs_item = JSON::list();
|
auto chunk_crcs_item = JSON::list();
|
||||||
for (uint32_t chunk_crc : f->chunk_crcs) {
|
for (uint32_t chunk_crc : f->chunk_crcs) {
|
||||||
chunk_crcs_item.emplace_back(chunk_crc);
|
chunk_crcs_item.emplace_back(chunk_crc);
|
||||||
}
|
}
|
||||||
new_metadata_cache_json.emplace(
|
new_metadata_cache_json.emplace(
|
||||||
relative_item_path, JSON::list({f->size, st.st_mtime, f->crc32, std::move(chunk_crcs_item)}));
|
relative_item_path, JSON::list({f->size, st.st_mtime, f->crc32, std::move(chunk_crcs_item)}));
|
||||||
should_write_metadata_cache = true;
|
should_write_metadata_cache = true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// File was not modified and cache item was valid; just use the
|
// File was not modified and cache item was valid; just use the
|
||||||
// existing cache item
|
// existing cache item
|
||||||
new_metadata_cache_json.emplace(
|
new_metadata_cache_json.emplace(
|
||||||
relative_item_path, std::move(cache_item_json));
|
relative_item_path, std::move(cache_item_json));
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
patch_index_log.info(
|
||||||
"Added file %s (%" PRIu32 " bytes; %zu chunks; %08" PRIX32 " from cache)",
|
"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(
|
patch_index_log.info(
|
||||||
"Added file %s (%" PRIu32 " bytes; %zu chunks; %08" PRIX32 " [%s])",
|
"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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path_directories.pop_back();
|
path_directories.pop_back();
|
||||||
};
|
};
|
||||||
|
|
||||||
collect_dir(".");
|
collect_dir(".");
|
||||||
|
|
||||||
if (should_write_metadata_cache) {
|
if (should_write_metadata_cache) {
|
||||||
try {
|
try {
|
||||||
save_file(metadata_cache_filename, new_metadata_cache_json.serialize());
|
save_file(metadata_cache_filename, new_metadata_cache_json.serialize());
|
||||||
patch_index_log.info("Saved patch metadata cache to %s", metadata_cache_filename.c_str());
|
patch_index_log.info("Saved patch metadata cache to %s", metadata_cache_filename.c_str());
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
patch_index_log.warning("Cannot save patch metadata cache to %s: %s", metadata_cache_filename.c_str(), e.what());
|
patch_index_log.warning("Cannot save patch metadata cache to %s: %s", metadata_cache_filename.c_str(), e.what());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
patch_index_log.info("No files were modified; skipping metadata cache update");
|
patch_index_log.info("No files were modified; skipping metadata cache update");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const vector<shared_ptr<PatchFileIndex::File>>&
|
const vector<shared_ptr<PatchFileIndex::File>>&
|
||||||
PatchFileIndex::all_files() const {
|
PatchFileIndex::all_files() const {
|
||||||
return this->files_by_patch_order;
|
return this->files_by_patch_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<PatchFileIndex::File> PatchFileIndex::get(
|
shared_ptr<PatchFileIndex::File> PatchFileIndex::get(
|
||||||
const string& filename) const {
|
const string& filename) const {
|
||||||
return this->files_by_name.at(filename);
|
return this->files_by_name.at(filename);
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-52
@@ -1,52 +1,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct PatchFileIndex {
|
struct PatchFileIndex {
|
||||||
explicit PatchFileIndex(const std::string& root_dir);
|
explicit PatchFileIndex(const std::string& root_dir);
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
PatchFileIndex* index;
|
PatchFileIndex* index;
|
||||||
std::vector<std::string> path_directories;
|
std::vector<std::string> path_directories;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::shared_ptr<const std::string> loaded_data;
|
std::shared_ptr<const std::string> loaded_data;
|
||||||
std::vector<uint32_t> chunk_crcs;
|
std::vector<uint32_t> chunk_crcs;
|
||||||
uint32_t crc32;
|
uint32_t crc32;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
|
||||||
explicit File(PatchFileIndex* index);
|
explicit File(PatchFileIndex* index);
|
||||||
std::shared_ptr<const std::string> load_data();
|
std::shared_ptr<const std::string> load_data();
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<File>>& all_files() const;
|
const std::vector<std::shared_ptr<File>>& all_files() const;
|
||||||
std::shared_ptr<File> get(const std::string& filename) const;
|
std::shared_ptr<File> get(const std::string& filename) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::shared_ptr<File>> files_by_patch_order;
|
std::vector<std::shared_ptr<File>> files_by_patch_order;
|
||||||
std::unordered_map<std::string, std::shared_ptr<File>> files_by_name;
|
std::unordered_map<std::string, std::shared_ptr<File>> files_by_name;
|
||||||
std::string root_dir;
|
std::string root_dir;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PatchFileChecksumRequest {
|
struct PatchFileChecksumRequest {
|
||||||
std::shared_ptr<PatchFileIndex::File> file;
|
std::shared_ptr<PatchFileIndex::File> file;
|
||||||
uint32_t crc32;
|
uint32_t crc32;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
bool response_received;
|
bool response_received;
|
||||||
|
|
||||||
explicit PatchFileChecksumRequest(std::shared_ptr<PatchFileIndex::File> file)
|
explicit PatchFileChecksumRequest(std::shared_ptr<PatchFileIndex::File> file)
|
||||||
: file(file),
|
: file(file),
|
||||||
crc32(0),
|
crc32(0),
|
||||||
size(0),
|
size(0),
|
||||||
response_received(false) {}
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+478
-478
@@ -1,478 +1,478 @@
|
|||||||
#include "PatchServer.hh"
|
#include "PatchServer.hh"
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#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 <event2/listener.h>
|
#include <event2/listener.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#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 <phosg/Time.hh>
|
#include <phosg/Time.hh>
|
||||||
|
|
||||||
#include "EventUtils.hh"
|
#include "EventUtils.hh"
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
#include "ReceiveCommands.hh"
|
#include "ReceiveCommands.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
static atomic<uint64_t> next_id(1);
|
static atomic<uint64_t> next_id(1);
|
||||||
|
|
||||||
PatchServer::Client::Client(
|
PatchServer::Client::Client(
|
||||||
shared_ptr<PatchServer> server,
|
shared_ptr<PatchServer> server,
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
Version version,
|
Version version,
|
||||||
uint64_t idle_timeout_usecs,
|
uint64_t idle_timeout_usecs,
|
||||||
bool hide_data_from_logs)
|
bool hide_data_from_logs)
|
||||||
: server(server),
|
: server(server),
|
||||||
id(next_id++),
|
id(next_id++),
|
||||||
log(string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level),
|
log(string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level),
|
||||||
channel(bev, 0, version, 1, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN),
|
channel(bev, 0, version, 1, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN),
|
||||||
idle_timeout_usecs(idle_timeout_usecs),
|
idle_timeout_usecs(idle_timeout_usecs),
|
||||||
idle_timeout_event(
|
idle_timeout_event(
|
||||||
event_new(bufferevent_get_base(bev), -1, EV_TIMEOUT, &PatchServer::Client::dispatch_idle_timeout, this),
|
event_new(bufferevent_get_base(bev), -1, EV_TIMEOUT, &PatchServer::Client::dispatch_idle_timeout, this),
|
||||||
event_free) {
|
event_free) {
|
||||||
this->reschedule_timeout_event();
|
this->reschedule_timeout_event();
|
||||||
|
|
||||||
// Don't print data sent to patch clients to the logs. The patch server
|
// Don't print data sent to patch clients to the logs. The patch server
|
||||||
// protocol is fully understood and data logs for patch clients are generally
|
// protocol is fully understood and data logs for patch clients are generally
|
||||||
// more annoying than helpful at this point.
|
// more annoying than helpful at this point.
|
||||||
if (hide_data_from_logs) {
|
if (hide_data_from_logs) {
|
||||||
this->channel.terminal_recv_color = TerminalFormat::END;
|
this->channel.terminal_recv_color = TerminalFormat::END;
|
||||||
this->channel.terminal_send_color = TerminalFormat::END;
|
this->channel.terminal_send_color = TerminalFormat::END;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->log.info("Created");
|
this->log.info("Created");
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::Client::reschedule_timeout_event() {
|
void PatchServer::Client::reschedule_timeout_event() {
|
||||||
struct timeval idle_tv = usecs_to_timeval(this->idle_timeout_usecs);
|
struct timeval idle_tv = usecs_to_timeval(this->idle_timeout_usecs);
|
||||||
event_add(this->idle_timeout_event.get(), &idle_tv);
|
event_add(this->idle_timeout_event.get(), &idle_tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::Client::dispatch_idle_timeout(evutil_socket_t, short, void* ctx) {
|
void PatchServer::Client::dispatch_idle_timeout(evutil_socket_t, short, void* ctx) {
|
||||||
reinterpret_cast<Client*>(ctx)->idle_timeout();
|
reinterpret_cast<Client*>(ctx)->idle_timeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::Client::idle_timeout() {
|
void PatchServer::Client::idle_timeout() {
|
||||||
this->log.info("Idle timeout expired");
|
this->log.info("Idle timeout expired");
|
||||||
auto s = this->server.lock();
|
auto s = this->server.lock();
|
||||||
if (s) {
|
if (s) {
|
||||||
auto c = this->shared_from_this();
|
auto c = this->shared_from_this();
|
||||||
s->disconnect_client(c);
|
s->disconnect_client(c);
|
||||||
} else {
|
} else {
|
||||||
this->channel.disconnect();
|
this->channel.disconnect();
|
||||||
this->log.info("Server is deleted; cannot disconnect client");
|
this->log.info("Server is deleted; cannot disconnect client");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::send_server_init(shared_ptr<Client> c) const {
|
void PatchServer::send_server_init(shared_ptr<Client> c) const {
|
||||||
uint32_t server_key = random_object<uint32_t>();
|
uint32_t server_key = random_object<uint32_t>();
|
||||||
uint32_t client_key = random_object<uint32_t>();
|
uint32_t client_key = random_object<uint32_t>();
|
||||||
|
|
||||||
S_ServerInit_Patch_02 cmd;
|
S_ServerInit_Patch_02 cmd;
|
||||||
cmd.copyright.encode("Patch Server. Copyright SonicTeam, LTD. 2001");
|
cmd.copyright.encode("Patch Server. Copyright SonicTeam, LTD. 2001");
|
||||||
cmd.server_key = server_key;
|
cmd.server_key = server_key;
|
||||||
cmd.client_key = client_key;
|
cmd.client_key = client_key;
|
||||||
c->channel.send(0x02, 0x00, cmd);
|
c->channel.send(0x02, 0x00, cmd);
|
||||||
|
|
||||||
c->channel.crypt_out = make_shared<PSOV2Encryption>(server_key);
|
c->channel.crypt_out = make_shared<PSOV2Encryption>(server_key);
|
||||||
c->channel.crypt_in = make_shared<PSOV2Encryption>(client_key);
|
c->channel.crypt_in = make_shared<PSOV2Encryption>(client_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::send_message_box(shared_ptr<Client> c, const string& text) const {
|
void PatchServer::send_message_box(shared_ptr<Client> c, const string& text) const {
|
||||||
StringWriter w;
|
StringWriter w;
|
||||||
try {
|
try {
|
||||||
if (c->version() == Version::PC_PATCH) {
|
if (c->version() == Version::PC_PATCH) {
|
||||||
w.write(tt_encode_marked_optional(text, c->channel.language, true));
|
w.write(tt_encode_marked_optional(text, c->channel.language, true));
|
||||||
} else if (c->version() == Version::BB_PATCH) {
|
} else if (c->version() == Version::BB_PATCH) {
|
||||||
w.write(tt_encode_marked_optional(add_color(text), c->channel.language, true));
|
w.write(tt_encode_marked_optional(add_color(text), c->channel.language, true));
|
||||||
} else {
|
} else {
|
||||||
throw logic_error("non-patch client on patch server");
|
throw logic_error("non-patch client on patch server");
|
||||||
}
|
}
|
||||||
} catch (const runtime_error& e) {
|
} catch (const runtime_error& e) {
|
||||||
log_warning("Failed to encode message for patch message box command: %s", e.what());
|
log_warning("Failed to encode message for patch message box command: %s", e.what());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
w.put_u16(0);
|
w.put_u16(0);
|
||||||
while (w.str().size() & 3) {
|
while (w.str().size() & 3) {
|
||||||
w.put_u8(0);
|
w.put_u8(0);
|
||||||
}
|
}
|
||||||
c->channel.send(0x13, 0x00, w.str());
|
c->channel.send(0x13, 0x00, w.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::send_enter_directory(shared_ptr<Client> c, const string& dir) const {
|
void PatchServer::send_enter_directory(shared_ptr<Client> c, const string& dir) const {
|
||||||
S_EnterDirectory_Patch_09 cmd = {{dir, 1}};
|
S_EnterDirectory_Patch_09 cmd = {{dir, 1}};
|
||||||
c->channel.send(0x09, 0x00, cmd);
|
c->channel.send(0x09, 0x00, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::on_02(shared_ptr<Client> c, string& data) {
|
void PatchServer::on_02(shared_ptr<Client> c, string& data) {
|
||||||
check_size_v(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
c->channel.send(0x04, 0x00); // This requests the user's login information
|
c->channel.send(0x04, 0x00); // This requests the user's login information
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::change_to_directory(
|
void PatchServer::change_to_directory(
|
||||||
shared_ptr<Client> c,
|
shared_ptr<Client> c,
|
||||||
vector<string>& client_path_directories,
|
vector<string>& client_path_directories,
|
||||||
const vector<string>& file_path_directories) const {
|
const vector<string>& file_path_directories) const {
|
||||||
// First, exit all leaf directories that don't match the desired path
|
// First, exit all leaf directories that don't match the desired path
|
||||||
while (!client_path_directories.empty() &&
|
while (!client_path_directories.empty() &&
|
||||||
((client_path_directories.size() > file_path_directories.size()) ||
|
((client_path_directories.size() > file_path_directories.size()) ||
|
||||||
(client_path_directories.back() != file_path_directories[client_path_directories.size() - 1]))) {
|
(client_path_directories.back() != file_path_directories[client_path_directories.size() - 1]))) {
|
||||||
c->channel.send(0x0A, 0x00);
|
c->channel.send(0x0A, 0x00);
|
||||||
client_path_directories.pop_back();
|
client_path_directories.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, client_path_directories should be a prefix of
|
// At this point, client_path_directories should be a prefix of
|
||||||
// file_path_directories (or should match exactly)
|
// file_path_directories (or should match exactly)
|
||||||
if (client_path_directories.size() > file_path_directories.size()) {
|
if (client_path_directories.size() > file_path_directories.size()) {
|
||||||
throw logic_error("did not exit all necessary directories");
|
throw logic_error("did not exit all necessary directories");
|
||||||
}
|
}
|
||||||
for (size_t x = 0; x < client_path_directories.size(); x++) {
|
for (size_t x = 0; x < client_path_directories.size(); x++) {
|
||||||
if (client_path_directories[x] != file_path_directories[x]) {
|
if (client_path_directories[x] != file_path_directories[x]) {
|
||||||
throw logic_error("intermediate path is not a prefix of final path");
|
throw logic_error("intermediate path is not a prefix of final path");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second, enter all necessary leaf directories
|
// Second, enter all necessary leaf directories
|
||||||
while (client_path_directories.size() < file_path_directories.size()) {
|
while (client_path_directories.size() < file_path_directories.size()) {
|
||||||
const string& dir = file_path_directories[client_path_directories.size()];
|
const string& dir = file_path_directories[client_path_directories.size()];
|
||||||
this->send_enter_directory(c, dir);
|
this->send_enter_directory(c, dir);
|
||||||
client_path_directories.emplace_back(dir);
|
client_path_directories.emplace_back(dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::on_04(shared_ptr<Client> c, string& data) {
|
void PatchServer::on_04(shared_ptr<Client> c, string& data) {
|
||||||
const auto& cmd = check_size_t<C_Login_Patch_04>(data);
|
const auto& cmd = check_size_t<C_Login_Patch_04>(data);
|
||||||
|
|
||||||
string username = cmd.username.decode();
|
string username = cmd.username.decode();
|
||||||
string password = cmd.password.decode();
|
string password = cmd.password.decode();
|
||||||
|
|
||||||
// There are 3 cases here:
|
// There are 3 cases here:
|
||||||
// - No login information at all: just proceed without checking credentials
|
// - No login information at all: just proceed without checking credentials
|
||||||
// - Username: check that account exists if allow_unregistered_users is off
|
// - Username: check that account exists if allow_unregistered_users is off
|
||||||
// - Username and password: call verify_bb
|
// - Username and password: call verify_bb
|
||||||
if (!username.empty() && !password.empty()) {
|
if (!username.empty() && !password.empty()) {
|
||||||
try {
|
try {
|
||||||
this->config->account_index->from_bb_credentials(username, &password, false);
|
this->config->account_index->from_bb_credentials(username, &password, false);
|
||||||
|
|
||||||
} catch (const AccountIndex::incorrect_password& e) {
|
} catch (const AccountIndex::incorrect_password& e) {
|
||||||
this->send_message_box(c, string_printf("Login failed: %s", e.what()));
|
this->send_message_box(c, string_printf("Login failed: %s", e.what()));
|
||||||
this->disconnect_client(c);
|
this->disconnect_client(c);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (const AccountIndex::missing_account& e) {
|
} catch (const AccountIndex::missing_account& e) {
|
||||||
if (!this->config->allow_unregistered_users) {
|
if (!this->config->allow_unregistered_users) {
|
||||||
this->send_message_box(c, string_printf("Login failed: %s", e.what()));
|
this->send_message_box(c, string_printf("Login failed: %s", e.what()));
|
||||||
this->disconnect_client(c);
|
this->disconnect_client(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (!username.empty() && !this->config->allow_unregistered_users) {
|
} else if (!username.empty() && !this->config->allow_unregistered_users) {
|
||||||
try {
|
try {
|
||||||
this->config->account_index->from_bb_credentials(username, nullptr, false);
|
this->config->account_index->from_bb_credentials(username, nullptr, false);
|
||||||
} catch (const AccountIndex::missing_account& e) {
|
} catch (const AccountIndex::missing_account& e) {
|
||||||
this->send_message_box(c, string_printf("Login failed: %s", e.what()));
|
this->send_message_box(c, string_printf("Login failed: %s", e.what()));
|
||||||
this->disconnect_client(c);
|
this->disconnect_client(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->config->message.empty()) {
|
if (!this->config->message.empty()) {
|
||||||
this->send_message_box(c, this->config->message.c_str());
|
this->send_message_box(c, this->config->message.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& index = this->config->patch_file_index;
|
const auto& index = this->config->patch_file_index;
|
||||||
if (index.get()) {
|
if (index.get()) {
|
||||||
c->channel.send(0x0B, 0x00); // Start patch session; go to root directory
|
c->channel.send(0x0B, 0x00); // Start patch session; go to root directory
|
||||||
|
|
||||||
vector<string> path_directories;
|
vector<string> path_directories;
|
||||||
for (const auto& file : index->all_files()) {
|
for (const auto& file : index->all_files()) {
|
||||||
this->change_to_directory(c, path_directories, file->path_directories);
|
this->change_to_directory(c, path_directories, file->path_directories);
|
||||||
|
|
||||||
S_FileChecksumRequest_Patch_0C req = {c->patch_file_checksum_requests.size(), {file->name, 1}};
|
S_FileChecksumRequest_Patch_0C req = {c->patch_file_checksum_requests.size(), {file->name, 1}};
|
||||||
c->channel.send(0x0C, 0x00, req);
|
c->channel.send(0x0C, 0x00, req);
|
||||||
c->patch_file_checksum_requests.emplace_back(file);
|
c->patch_file_checksum_requests.emplace_back(file);
|
||||||
}
|
}
|
||||||
this->change_to_directory(c, path_directories, {});
|
this->change_to_directory(c, path_directories, {});
|
||||||
|
|
||||||
c->channel.send(0x0D, 0x00); // End of checksum requests
|
c->channel.send(0x0D, 0x00); // End of checksum requests
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// No patch index present: just do something that will satisfy the client
|
// No patch index present: just do something that will satisfy the client
|
||||||
// without actually checking or downloading any files
|
// without actually checking or downloading any files
|
||||||
this->send_enter_directory(c, ".");
|
this->send_enter_directory(c, ".");
|
||||||
this->send_enter_directory(c, "data");
|
this->send_enter_directory(c, "data");
|
||||||
this->send_enter_directory(c, "scene");
|
this->send_enter_directory(c, "scene");
|
||||||
c->channel.send(0x0A, 0x00);
|
c->channel.send(0x0A, 0x00);
|
||||||
c->channel.send(0x0A, 0x00);
|
c->channel.send(0x0A, 0x00);
|
||||||
c->channel.send(0x0A, 0x00);
|
c->channel.send(0x0A, 0x00);
|
||||||
c->channel.send(0x12, 0x00);
|
c->channel.send(0x12, 0x00);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::on_0F(shared_ptr<Client> c, string& data) {
|
void PatchServer::on_0F(shared_ptr<Client> c, string& data) {
|
||||||
auto& cmd = check_size_t<C_FileInformation_Patch_0F>(data);
|
auto& cmd = check_size_t<C_FileInformation_Patch_0F>(data);
|
||||||
auto& req = c->patch_file_checksum_requests.at(cmd.request_id);
|
auto& req = c->patch_file_checksum_requests.at(cmd.request_id);
|
||||||
req.crc32 = cmd.checksum;
|
req.crc32 = cmd.checksum;
|
||||||
req.size = cmd.size;
|
req.size = cmd.size;
|
||||||
req.response_received = true;
|
req.response_received = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::on_10(shared_ptr<Client> c, string&) {
|
void PatchServer::on_10(shared_ptr<Client> c, string&) {
|
||||||
S_StartFileDownloads_Patch_11 start_cmd = {0, 0};
|
S_StartFileDownloads_Patch_11 start_cmd = {0, 0};
|
||||||
for (const auto& req : c->patch_file_checksum_requests) {
|
for (const auto& req : c->patch_file_checksum_requests) {
|
||||||
if (!req.response_received) {
|
if (!req.response_received) {
|
||||||
throw runtime_error("client did not respond to checksum request");
|
throw runtime_error("client did not respond to checksum request");
|
||||||
}
|
}
|
||||||
if (req.needs_update()) {
|
if (req.needs_update()) {
|
||||||
c->log.info("File %s needs update (CRC: %08" PRIX32 "/%08" PRIX32 ", size: %" PRIu32 "/%" PRIu32 ")",
|
c->log.info("File %s needs update (CRC: %08" PRIX32 "/%08" PRIX32 ", size: %" PRIu32 "/%" PRIu32 ")",
|
||||||
req.file->name.c_str(), req.file->crc32, req.crc32, req.file->size, req.size);
|
req.file->name.c_str(), req.file->crc32, req.crc32, req.file->size, req.size);
|
||||||
start_cmd.total_bytes += req.file->size;
|
start_cmd.total_bytes += req.file->size;
|
||||||
start_cmd.num_files++;
|
start_cmd.num_files++;
|
||||||
} else {
|
} else {
|
||||||
c->log.info("File %s is up to date", req.file->name.c_str());
|
c->log.info("File %s is up to date", req.file->name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start_cmd.num_files) {
|
if (start_cmd.num_files) {
|
||||||
c->channel.send(0x11, 0x00, start_cmd);
|
c->channel.send(0x11, 0x00, start_cmd);
|
||||||
vector<string> path_directories;
|
vector<string> path_directories;
|
||||||
for (const auto& req : c->patch_file_checksum_requests) {
|
for (const auto& req : c->patch_file_checksum_requests) {
|
||||||
if (req.needs_update()) {
|
if (req.needs_update()) {
|
||||||
this->change_to_directory(c, path_directories, req.file->path_directories);
|
this->change_to_directory(c, path_directories, req.file->path_directories);
|
||||||
|
|
||||||
S_OpenFile_Patch_06 open_cmd = {0, req.file->size, {req.file->name, 1}};
|
S_OpenFile_Patch_06 open_cmd = {0, req.file->size, {req.file->name, 1}};
|
||||||
c->channel.send(0x06, 0x00, open_cmd);
|
c->channel.send(0x06, 0x00, open_cmd);
|
||||||
|
|
||||||
for (size_t x = 0; x < req.file->chunk_crcs.size(); x++) {
|
for (size_t x = 0; x < req.file->chunk_crcs.size(); x++) {
|
||||||
auto data = req.file->load_data();
|
auto data = req.file->load_data();
|
||||||
size_t chunk_size = min<uint32_t>(req.file->size - (x * 0x4000), 0x4000);
|
size_t chunk_size = min<uint32_t>(req.file->size - (x * 0x4000), 0x4000);
|
||||||
|
|
||||||
vector<pair<const void*, size_t>> blocks;
|
vector<pair<const void*, size_t>> blocks;
|
||||||
S_WriteFileHeader_Patch_07 cmd_header = {x, req.file->chunk_crcs[x], chunk_size};
|
S_WriteFileHeader_Patch_07 cmd_header = {x, req.file->chunk_crcs[x], chunk_size};
|
||||||
blocks.emplace_back(&cmd_header, sizeof(cmd_header));
|
blocks.emplace_back(&cmd_header, sizeof(cmd_header));
|
||||||
blocks.emplace_back(data->data() + (x * 0x4000), chunk_size);
|
blocks.emplace_back(data->data() + (x * 0x4000), chunk_size);
|
||||||
c->channel.send(0x07, 0x00, blocks);
|
c->channel.send(0x07, 0x00, blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
S_CloseCurrentFile_Patch_08 close_cmd = {0};
|
S_CloseCurrentFile_Patch_08 close_cmd = {0};
|
||||||
c->channel.send(0x08, 0x00, close_cmd);
|
c->channel.send(0x08, 0x00, close_cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->change_to_directory(c, path_directories, {});
|
this->change_to_directory(c, path_directories, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
c->channel.send(0x12, 0x00);
|
c->channel.send(0x12, 0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::disconnect_client(shared_ptr<Client> c) {
|
void PatchServer::disconnect_client(shared_ptr<Client> c) {
|
||||||
if (c->channel.virtual_network_id) {
|
if (c->channel.virtual_network_id) {
|
||||||
server_log.info("Client disconnected: C-%" PRIX64 " on N-%" PRIu64, c->id, c->channel.virtual_network_id);
|
server_log.info("Client disconnected: C-%" PRIX64 " on N-%" PRIu64, c->id, c->channel.virtual_network_id);
|
||||||
} else if (c->channel.bev) {
|
} else if (c->channel.bev) {
|
||||||
server_log.info("Client disconnected: C-%" PRIX64, c->id);
|
server_log.info("Client disconnected: C-%" PRIX64, c->id);
|
||||||
} else {
|
} else {
|
||||||
server_log.info("Client C-%" PRIX64 " removed from patch server", c->id);
|
server_log.info("Client C-%" PRIX64 " removed from patch server", c->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->channel_to_client.erase(&c->channel);
|
this->channel_to_client.erase(&c->channel);
|
||||||
c->channel.disconnect();
|
c->channel.disconnect();
|
||||||
|
|
||||||
// We can't just let c be destroyed here, since disconnect_client can be
|
// We can't just let c be destroyed here, since disconnect_client can be
|
||||||
// called from within the client's channel's receive handler. So, we instead
|
// called from within the client's channel's receive handler. So, we instead
|
||||||
// move it to another set, which we'll clear in an immediately-enqueued
|
// move it to another set, which we'll clear in an immediately-enqueued
|
||||||
// callback after the current event. This will also call the client's
|
// callback after the current event. This will also call the client's
|
||||||
// disconnect hooks (if any).
|
// disconnect hooks (if any).
|
||||||
this->clients_to_destroy.insert(std::move(c));
|
this->clients_to_destroy.insert(std::move(c));
|
||||||
this->enqueue_destroy_clients();
|
this->enqueue_destroy_clients();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::enqueue_destroy_clients() {
|
void PatchServer::enqueue_destroy_clients() {
|
||||||
auto tv = usecs_to_timeval(0);
|
auto tv = usecs_to_timeval(0);
|
||||||
event_add(this->destroy_clients_ev.get(), &tv);
|
event_add(this->destroy_clients_ev.get(), &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::dispatch_destroy_clients(evutil_socket_t, short, void* ctx) {
|
void PatchServer::dispatch_destroy_clients(evutil_socket_t, short, void* ctx) {
|
||||||
reinterpret_cast<PatchServer*>(ctx)->clients_to_destroy.clear();
|
reinterpret_cast<PatchServer*>(ctx)->clients_to_destroy.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::dispatch_on_listen_accept(
|
void PatchServer::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<PatchServer*>(ctx)->on_listen_accept(listener, fd, address, socklen);
|
reinterpret_cast<PatchServer*>(ctx)->on_listen_accept(listener, fd, address, socklen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::dispatch_on_listen_error(
|
void PatchServer::dispatch_on_listen_error(
|
||||||
struct evconnlistener* listener, void* ctx) {
|
struct evconnlistener* listener, void* ctx) {
|
||||||
reinterpret_cast<PatchServer*>(ctx)->on_listen_error(listener);
|
reinterpret_cast<PatchServer*>(ctx)->on_listen_error(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr*, int) {
|
void PatchServer::on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr*, int) {
|
||||||
struct sockaddr_storage remote_addr;
|
struct sockaddr_storage remote_addr;
|
||||||
get_socket_addresses(fd, nullptr, &remote_addr);
|
get_socket_addresses(fd, nullptr, &remote_addr);
|
||||||
if (this->config->banned_ipv4_ranges->check(remote_addr)) {
|
if (this->config->banned_ipv4_ranges->check(remote_addr)) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int listen_fd = evconnlistener_get_fd(listener);
|
int listen_fd = evconnlistener_get_fd(listener);
|
||||||
ListeningSocket* listening_socket;
|
ListeningSocket* listening_socket;
|
||||||
try {
|
try {
|
||||||
listening_socket = &this->listening_sockets.at(listen_fd);
|
listening_socket = &this->listening_sockets.at(listen_fd);
|
||||||
} catch (const out_of_range& e) {
|
} catch (const out_of_range& e) {
|
||||||
server_log.warning("Can\'t determine version for socket %d; disconnecting client", listen_fd);
|
server_log.warning("Can\'t determine version for socket %d; disconnecting client", listen_fd);
|
||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||||
auto c = make_shared<Client>(
|
auto c = make_shared<Client>(
|
||||||
this->shared_from_this(),
|
this->shared_from_this(),
|
||||||
bev,
|
bev,
|
||||||
listening_socket->version,
|
listening_socket->version,
|
||||||
this->config->idle_timeout_usecs,
|
this->config->idle_timeout_usecs,
|
||||||
this->config->hide_data_from_logs);
|
this->config->hide_data_from_logs);
|
||||||
c->channel.on_command_received = PatchServer::on_client_input;
|
c->channel.on_command_received = PatchServer::on_client_input;
|
||||||
c->channel.on_error = PatchServer::on_client_error;
|
c->channel.on_error = PatchServer::on_client_error;
|
||||||
c->channel.context_obj = this;
|
c->channel.context_obj = this;
|
||||||
this->channel_to_client.emplace(&c->channel, c);
|
this->channel_to_client.emplace(&c->channel, c);
|
||||||
|
|
||||||
server_log.info("Patch client connected: U-%" PRIX64 " on fd %d via %d (%s)",
|
server_log.info("Patch client connected: U-%" PRIX64 " on fd %d via %d (%s)",
|
||||||
c->id, fd, listen_fd, listening_socket->addr_str.c_str());
|
c->id, fd, listen_fd, listening_socket->addr_str.c_str());
|
||||||
|
|
||||||
this->send_server_init(c);
|
this->send_server_init(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::on_listen_error(struct evconnlistener* listener) {
|
void PatchServer::on_listen_error(struct evconnlistener* listener) {
|
||||||
int err = EVUTIL_SOCKET_ERROR();
|
int err = EVUTIL_SOCKET_ERROR();
|
||||||
server_log.error("Failure on listening socket %d: %d (%s)",
|
server_log.error("Failure on listening socket %d: %d (%s)",
|
||||||
evconnlistener_get_fd(listener), err, evutil_socket_error_to_string(err));
|
evconnlistener_get_fd(listener), err, evutil_socket_error_to_string(err));
|
||||||
event_base_loopexit(this->base.get(), nullptr);
|
event_base_loopexit(this->base.get(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::on_client_input(Channel& ch, uint16_t command, uint32_t, std::string& data) {
|
void PatchServer::on_client_input(Channel& ch, uint16_t command, uint32_t, std::string& data) {
|
||||||
PatchServer* server = reinterpret_cast<PatchServer*>(ch.context_obj);
|
PatchServer* server = reinterpret_cast<PatchServer*>(ch.context_obj);
|
||||||
shared_ptr<Client> c = server->channel_to_client.at(&ch);
|
shared_ptr<Client> c = server->channel_to_client.at(&ch);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case 0x02:
|
case 0x02:
|
||||||
server->on_02(c, data);
|
server->on_02(c, data);
|
||||||
break;
|
break;
|
||||||
case 0x04:
|
case 0x04:
|
||||||
server->on_04(c, data);
|
server->on_04(c, data);
|
||||||
break;
|
break;
|
||||||
case 0x0F:
|
case 0x0F:
|
||||||
server->on_0F(c, data);
|
server->on_0F(c, data);
|
||||||
break;
|
break;
|
||||||
case 0x10:
|
case 0x10:
|
||||||
server->on_10(c, data);
|
server->on_10(c, data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw runtime_error("invalid command");
|
throw runtime_error("invalid command");
|
||||||
}
|
}
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
server_log.warning("Error processing client command: %s", e.what());
|
server_log.warning("Error processing client command: %s", e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::on_client_error(Channel& ch, short events) {
|
void PatchServer::on_client_error(Channel& ch, short events) {
|
||||||
PatchServer* server = reinterpret_cast<PatchServer*>(ch.context_obj);
|
PatchServer* server = reinterpret_cast<PatchServer*>(ch.context_obj);
|
||||||
shared_ptr<Client> c = server->channel_to_client.at(&ch);
|
shared_ptr<Client> c = server->channel_to_client.at(&ch);
|
||||||
|
|
||||||
if (events & BEV_EVENT_ERROR) {
|
if (events & BEV_EVENT_ERROR) {
|
||||||
int err = EVUTIL_SOCKET_ERROR();
|
int err = EVUTIL_SOCKET_ERROR();
|
||||||
server_log.warning("Client caused error %d (%s)", err, evutil_socket_error_to_string(err));
|
server_log.warning("Client caused error %d (%s)", err, evutil_socket_error_to_string(err));
|
||||||
}
|
}
|
||||||
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
|
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
|
||||||
server->disconnect_client(c);
|
server->disconnect_client(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchServer::PatchServer(shared_ptr<const Config> config)
|
PatchServer::PatchServer(shared_ptr<const Config> config)
|
||||||
: config(config) {
|
: config(config) {
|
||||||
if (config->shared_base) {
|
if (config->shared_base) {
|
||||||
this->base = config->shared_base;
|
this->base = config->shared_base;
|
||||||
this->base_is_shared = true;
|
this->base_is_shared = true;
|
||||||
} else {
|
} else {
|
||||||
this->base.reset(event_base_new(), event_base_free);
|
this->base.reset(event_base_new(), event_base_free);
|
||||||
this->base_is_shared = false;
|
this->base_is_shared = false;
|
||||||
}
|
}
|
||||||
this->destroy_clients_ev.reset(
|
this->destroy_clients_ev.reset(
|
||||||
event_new(this->base.get(), -1, EV_TIMEOUT, &PatchServer::dispatch_destroy_clients, this), event_free);
|
event_new(this->base.get(), -1, EV_TIMEOUT, &PatchServer::dispatch_destroy_clients, this), event_free);
|
||||||
if (!this->base_is_shared) {
|
if (!this->base_is_shared) {
|
||||||
this->th = thread(&PatchServer::thread_fn, this);
|
this->th = thread(&PatchServer::thread_fn, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::schedule_stop() {
|
void PatchServer::schedule_stop() {
|
||||||
if (!this->base_is_shared) {
|
if (!this->base_is_shared) {
|
||||||
event_base_loopexit(this->base.get(), nullptr);
|
event_base_loopexit(this->base.get(), nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::wait_for_stop() {
|
void PatchServer::wait_for_stop() {
|
||||||
if (!this->base_is_shared) {
|
if (!this->base_is_shared) {
|
||||||
this->th.join();
|
this->th.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::listen(const std::string& addr_str, const string& socket_path, Version version) {
|
void PatchServer::listen(const std::string& addr_str, const string& socket_path, Version version) {
|
||||||
int fd = ::listen(socket_path, 0, SOMAXCONN);
|
int fd = ::listen(socket_path, 0, SOMAXCONN);
|
||||||
server_log.info("Listening on Unix socket %s on fd %d as %s", socket_path.c_str(), fd, addr_str.c_str());
|
server_log.info("Listening on Unix socket %s on fd %d as %s", socket_path.c_str(), fd, addr_str.c_str());
|
||||||
this->add_socket(addr_str, fd, version);
|
this->add_socket(addr_str, fd, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::listen(const std::string& addr_str, const string& addr, int port, Version version) {
|
void PatchServer::listen(const std::string& addr_str, const string& addr, int port, Version version) {
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
this->listen(addr_str, addr, version);
|
this->listen(addr_str, addr, version);
|
||||||
} else {
|
} else {
|
||||||
int fd = ::listen(addr, port, SOMAXCONN);
|
int fd = ::listen(addr, port, SOMAXCONN);
|
||||||
string netloc_str = render_netloc(addr, port);
|
string netloc_str = render_netloc(addr, port);
|
||||||
server_log.info("Listening on TCP interface %s on fd %d as %s", netloc_str.c_str(), fd, addr_str.c_str());
|
server_log.info("Listening on TCP interface %s on fd %d as %s", netloc_str.c_str(), fd, addr_str.c_str());
|
||||||
this->add_socket(addr_str, fd, version);
|
this->add_socket(addr_str, fd, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::listen(const std::string& addr_str, int port, Version version) {
|
void PatchServer::listen(const std::string& addr_str, int port, Version version) {
|
||||||
this->listen(addr_str, "", port, version);
|
this->listen(addr_str, "", port, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchServer::ListeningSocket::ListeningSocket(PatchServer* s, const std::string& addr_str, int fd, Version version)
|
PatchServer::ListeningSocket::ListeningSocket(PatchServer* s, const std::string& addr_str, int fd, Version version)
|
||||||
: addr_str(addr_str),
|
: addr_str(addr_str),
|
||||||
fd(fd),
|
fd(fd),
|
||||||
version(version),
|
version(version),
|
||||||
listener(evconnlistener_new(s->base.get(), PatchServer::dispatch_on_listen_accept, s, LEV_OPT_REUSEABLE, 0, this->fd),
|
listener(evconnlistener_new(s->base.get(), PatchServer::dispatch_on_listen_accept, s, LEV_OPT_REUSEABLE, 0, this->fd),
|
||||||
evconnlistener_free) {
|
evconnlistener_free) {
|
||||||
evconnlistener_set_error_cb(this->listener.get(), PatchServer::dispatch_on_listen_error);
|
evconnlistener_set_error_cb(this->listener.get(), PatchServer::dispatch_on_listen_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::add_socket(const std::string& addr_str, int fd, Version version) {
|
void PatchServer::add_socket(const std::string& addr_str, int fd, Version version) {
|
||||||
this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(this, addr_str, fd, version));
|
this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(this, addr_str, fd, version));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::thread_fn() {
|
void PatchServer::thread_fn() {
|
||||||
event_base_loop(this->base.get(), EVLOOP_NO_EXIT_ON_EMPTY);
|
event_base_loop(this->base.get(), EVLOOP_NO_EXIT_ON_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchServer::set_config(std::shared_ptr<const Config> config) {
|
void PatchServer::set_config(std::shared_ptr<const Config> config) {
|
||||||
if (this->base_is_shared) {
|
if (this->base_is_shared) {
|
||||||
this->config = config;
|
this->config = config;
|
||||||
} else {
|
} else {
|
||||||
forward_to_event_thread(this->base, [s = this->shared_from_this(), config = std::move(config)]() {
|
forward_to_event_thread(this->base, [s = this->shared_from_this(), config = std::move(config)]() {
|
||||||
s->config = config;
|
s->config = config;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+131
-131
@@ -1,131 +1,131 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Account.hh"
|
#include "Account.hh"
|
||||||
#include "Channel.hh"
|
#include "Channel.hh"
|
||||||
#include "IPV4RangeSet.hh"
|
#include "IPV4RangeSet.hh"
|
||||||
#include "PatchFileIndex.hh"
|
#include "PatchFileIndex.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
class PatchServer : public std::enable_shared_from_this<PatchServer> {
|
class PatchServer : public std::enable_shared_from_this<PatchServer> {
|
||||||
public:
|
public:
|
||||||
struct Config {
|
struct Config {
|
||||||
bool allow_unregistered_users;
|
bool allow_unregistered_users;
|
||||||
bool hide_data_from_logs;
|
bool hide_data_from_logs;
|
||||||
uint64_t idle_timeout_usecs;
|
uint64_t idle_timeout_usecs;
|
||||||
std::string message;
|
std::string message;
|
||||||
std::shared_ptr<AccountIndex> account_index;
|
std::shared_ptr<AccountIndex> account_index;
|
||||||
std::shared_ptr<const PatchFileIndex> patch_file_index;
|
std::shared_ptr<const PatchFileIndex> patch_file_index;
|
||||||
std::shared_ptr<const IPV4RangeSet> banned_ipv4_ranges;
|
std::shared_ptr<const IPV4RangeSet> banned_ipv4_ranges;
|
||||||
std::shared_ptr<struct event_base> shared_base;
|
std::shared_ptr<struct event_base> shared_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
PatchServer() = delete;
|
PatchServer() = delete;
|
||||||
explicit PatchServer(std::shared_ptr<const Config> config);
|
explicit PatchServer(std::shared_ptr<const Config> config);
|
||||||
PatchServer(const PatchServer&) = delete;
|
PatchServer(const PatchServer&) = delete;
|
||||||
PatchServer(PatchServer&&) = delete;
|
PatchServer(PatchServer&&) = delete;
|
||||||
PatchServer& operator=(const PatchServer&) = delete;
|
PatchServer& operator=(const PatchServer&) = delete;
|
||||||
PatchServer& operator=(PatchServer&&) = delete;
|
PatchServer& operator=(PatchServer&&) = delete;
|
||||||
virtual ~PatchServer() = default;
|
virtual ~PatchServer() = default;
|
||||||
|
|
||||||
void schedule_stop();
|
void schedule_stop();
|
||||||
void wait_for_stop();
|
void wait_for_stop();
|
||||||
|
|
||||||
void listen(const std::string& addr_str, const std::string& socket_path, Version version);
|
void listen(const std::string& addr_str, const std::string& socket_path, Version version);
|
||||||
void listen(const std::string& addr_str, const std::string& addr, int port, Version version);
|
void listen(const std::string& addr_str, const std::string& addr, int port, Version version);
|
||||||
void listen(const std::string& addr_str, int port, Version version);
|
void listen(const std::string& addr_str, int port, Version version);
|
||||||
void add_socket(const std::string& addr_str, int fd, Version version);
|
void add_socket(const std::string& addr_str, int fd, Version version);
|
||||||
|
|
||||||
void set_config(std::shared_ptr<const Config> config);
|
void set_config(std::shared_ptr<const Config> config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Client : public std::enable_shared_from_this<Client> {
|
class Client : public std::enable_shared_from_this<Client> {
|
||||||
public:
|
public:
|
||||||
std::weak_ptr<PatchServer> server;
|
std::weak_ptr<PatchServer> server;
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
PrefixedLogger log;
|
PrefixedLogger log;
|
||||||
|
|
||||||
Channel channel;
|
Channel channel;
|
||||||
std::vector<PatchFileChecksumRequest> patch_file_checksum_requests;
|
std::vector<PatchFileChecksumRequest> patch_file_checksum_requests;
|
||||||
uint64_t idle_timeout_usecs;
|
uint64_t idle_timeout_usecs;
|
||||||
|
|
||||||
std::unique_ptr<struct event, void (*)(struct event*)> idle_timeout_event;
|
std::unique_ptr<struct event, void (*)(struct event*)> idle_timeout_event;
|
||||||
|
|
||||||
Client(
|
Client(
|
||||||
std::shared_ptr<PatchServer> server,
|
std::shared_ptr<PatchServer> server,
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
Version version,
|
Version version,
|
||||||
uint64_t idle_timeout_usecs,
|
uint64_t idle_timeout_usecs,
|
||||||
bool hide_data_from_logs);
|
bool hide_data_from_logs);
|
||||||
~Client() = default;
|
~Client() = default;
|
||||||
|
|
||||||
void reschedule_timeout_event();
|
void reschedule_timeout_event();
|
||||||
|
|
||||||
inline Version version() const {
|
inline Version version() const {
|
||||||
return this->channel.version;
|
return this->channel.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dispatch_idle_timeout(evutil_socket_t, short, void* ctx);
|
static void dispatch_idle_timeout(evutil_socket_t, short, void* ctx);
|
||||||
void idle_timeout();
|
void idle_timeout();
|
||||||
|
|
||||||
const std::string& get_bb_username() const;
|
const std::string& get_bb_username() const;
|
||||||
void set_bb_username(const std::string& bb_username);
|
void set_bb_username(const std::string& bb_username);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ListeningSocket {
|
struct ListeningSocket {
|
||||||
std::string addr_str;
|
std::string addr_str;
|
||||||
int fd;
|
int fd;
|
||||||
Version version;
|
Version version;
|
||||||
std::unique_ptr<struct evconnlistener, void (*)(struct evconnlistener*)> listener;
|
std::unique_ptr<struct evconnlistener, void (*)(struct evconnlistener*)> listener;
|
||||||
|
|
||||||
ListeningSocket(PatchServer* s, const std::string& name, int fd, Version version);
|
ListeningSocket(PatchServer* s, const std::string& name, int fd, Version version);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
bool base_is_shared;
|
bool base_is_shared;
|
||||||
std::shared_ptr<const Config> config;
|
std::shared_ptr<const Config> config;
|
||||||
|
|
||||||
std::unordered_set<std::shared_ptr<Client>> clients_to_destroy;
|
std::unordered_set<std::shared_ptr<Client>> clients_to_destroy;
|
||||||
std::shared_ptr<struct event> destroy_clients_ev;
|
std::shared_ptr<struct event> destroy_clients_ev;
|
||||||
|
|
||||||
std::unordered_map<int, ListeningSocket> listening_sockets;
|
std::unordered_map<int, ListeningSocket> listening_sockets;
|
||||||
std::unordered_map<Channel*, std::shared_ptr<Client>> channel_to_client;
|
std::unordered_map<Channel*, std::shared_ptr<Client>> channel_to_client;
|
||||||
|
|
||||||
std::thread th;
|
std::thread th;
|
||||||
|
|
||||||
void send_server_init(std::shared_ptr<Client> c) const;
|
void send_server_init(std::shared_ptr<Client> c) const;
|
||||||
void send_message_box(std::shared_ptr<Client> c, const std::string& text) const;
|
void send_message_box(std::shared_ptr<Client> c, const std::string& text) const;
|
||||||
void send_enter_directory(std::shared_ptr<Client> c, const std::string& dir) const;
|
void send_enter_directory(std::shared_ptr<Client> c, const std::string& dir) const;
|
||||||
void change_to_directory(
|
void change_to_directory(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::vector<std::string>& client_path_directories,
|
std::vector<std::string>& client_path_directories,
|
||||||
const std::vector<std::string>& file_path_directories) const;
|
const std::vector<std::string>& file_path_directories) const;
|
||||||
void on_02(std::shared_ptr<Client> c, std::string& data);
|
void on_02(std::shared_ptr<Client> c, std::string& data);
|
||||||
void on_04(std::shared_ptr<Client> c, std::string& data);
|
void on_04(std::shared_ptr<Client> c, std::string& data);
|
||||||
void on_0F(std::shared_ptr<Client> c, std::string& data);
|
void on_0F(std::shared_ptr<Client> c, std::string& data);
|
||||||
void on_10(std::shared_ptr<Client> c, std::string& data);
|
void on_10(std::shared_ptr<Client> c, std::string& data);
|
||||||
|
|
||||||
void disconnect_client(std::shared_ptr<Client> c);
|
void disconnect_client(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void enqueue_destroy_clients();
|
void enqueue_destroy_clients();
|
||||||
static void dispatch_destroy_clients(evutil_socket_t, short, void* ctx);
|
static void dispatch_destroy_clients(evutil_socket_t, short, void* ctx);
|
||||||
|
|
||||||
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(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* address, int socklen);
|
void on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* address, int socklen);
|
||||||
void on_listen_error(struct evconnlistener* listener);
|
void on_listen_error(struct evconnlistener* listener);
|
||||||
|
|
||||||
static void on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data);
|
static void on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data);
|
||||||
static void on_client_error(Channel& ch, short events);
|
static void on_client_error(Channel& ch, short events);
|
||||||
|
|
||||||
void thread_fn();
|
void thread_fn();
|
||||||
};
|
};
|
||||||
|
|||||||
+119
-119
@@ -1,119 +1,119 @@
|
|||||||
#include "PlayerFilesManager.hh"
|
#include "PlayerFilesManager.hh"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
#include <phosg/Filesystem.hh>
|
#include <phosg/Filesystem.hh>
|
||||||
#include <phosg/Hash.hh>
|
#include <phosg/Hash.hh>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "FileContentsCache.hh"
|
#include "FileContentsCache.hh"
|
||||||
#include "ItemData.hh"
|
#include "ItemData.hh"
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
PlayerFilesManager::PlayerFilesManager(std::shared_ptr<struct event_base> base)
|
PlayerFilesManager::PlayerFilesManager(std::shared_ptr<struct event_base> base)
|
||||||
: base(base),
|
: base(base),
|
||||||
clear_expired_files_event(
|
clear_expired_files_event(
|
||||||
event_new(this->base.get(), -1, EV_TIMEOUT | EV_PERSIST, &PlayerFilesManager::clear_expired_files, this),
|
event_new(this->base.get(), -1, EV_TIMEOUT | EV_PERSIST, &PlayerFilesManager::clear_expired_files, this),
|
||||||
event_free) {
|
event_free) {
|
||||||
auto tv = usecs_to_timeval(30 * 1000 * 1000);
|
auto tv = usecs_to_timeval(30 * 1000 * 1000);
|
||||||
event_add(this->clear_expired_files_event.get(), &tv);
|
event_add(this->clear_expired_files_event.get(), &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename KeyT, typename ValueT>
|
template <typename KeyT, typename ValueT>
|
||||||
size_t erase_unused(std::unordered_map<KeyT, std::shared_ptr<ValueT>>& m) {
|
size_t erase_unused(std::unordered_map<KeyT, std::shared_ptr<ValueT>>& m) {
|
||||||
size_t ret = 0;
|
size_t ret = 0;
|
||||||
for (auto it = m.begin(); it != m.end();) {
|
for (auto it = m.begin(); it != m.end();) {
|
||||||
if (it->second.use_count() <= 1) {
|
if (it->second.use_count() <= 1) {
|
||||||
it = m.erase(it);
|
it = m.erase(it);
|
||||||
ret++;
|
ret++;
|
||||||
} else {
|
} else {
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<PSOBBBaseSystemFile> PlayerFilesManager::get_system(const std::string& filename) {
|
std::shared_ptr<PSOBBBaseSystemFile> PlayerFilesManager::get_system(const std::string& filename) {
|
||||||
try {
|
try {
|
||||||
return this->loaded_system_files.at(filename);
|
return this->loaded_system_files.at(filename);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<PSOBBCharacterFile> PlayerFilesManager::get_character(const std::string& filename) {
|
std::shared_ptr<PSOBBCharacterFile> PlayerFilesManager::get_character(const std::string& filename) {
|
||||||
try {
|
try {
|
||||||
return this->loaded_character_files.at(filename);
|
return this->loaded_character_files.at(filename);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<PSOBBGuildCardFile> PlayerFilesManager::get_guild_card(const std::string& filename) {
|
std::shared_ptr<PSOBBGuildCardFile> PlayerFilesManager::get_guild_card(const std::string& filename) {
|
||||||
try {
|
try {
|
||||||
return this->loaded_guild_card_files.at(filename);
|
return this->loaded_guild_card_files.at(filename);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<PlayerBank200> PlayerFilesManager::get_bank(const std::string& filename) {
|
std::shared_ptr<PlayerBank200> PlayerFilesManager::get_bank(const std::string& filename) {
|
||||||
try {
|
try {
|
||||||
return this->loaded_bank_files.at(filename);
|
return this->loaded_bank_files.at(filename);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerFilesManager::set_system(const std::string& filename, std::shared_ptr<PSOBBBaseSystemFile> file) {
|
void PlayerFilesManager::set_system(const std::string& filename, std::shared_ptr<PSOBBBaseSystemFile> file) {
|
||||||
if (!this->loaded_system_files.emplace(filename, file).second) {
|
if (!this->loaded_system_files.emplace(filename, file).second) {
|
||||||
throw runtime_error("Guild Card file already loaded: " + filename);
|
throw runtime_error("Guild Card file already loaded: " + filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerFilesManager::set_character(const std::string& filename, std::shared_ptr<PSOBBCharacterFile> file) {
|
void PlayerFilesManager::set_character(const std::string& filename, std::shared_ptr<PSOBBCharacterFile> file) {
|
||||||
if (!this->loaded_character_files.emplace(filename, file).second) {
|
if (!this->loaded_character_files.emplace(filename, file).second) {
|
||||||
throw runtime_error("character file already loaded: " + filename);
|
throw runtime_error("character file already loaded: " + filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerFilesManager::set_guild_card(const std::string& filename, std::shared_ptr<PSOBBGuildCardFile> file) {
|
void PlayerFilesManager::set_guild_card(const std::string& filename, std::shared_ptr<PSOBBGuildCardFile> file) {
|
||||||
if (!this->loaded_guild_card_files.emplace(filename, file).second) {
|
if (!this->loaded_guild_card_files.emplace(filename, file).second) {
|
||||||
throw runtime_error("Guild Card file already loaded: " + filename);
|
throw runtime_error("Guild Card file already loaded: " + filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerFilesManager::set_bank(const std::string& filename, std::shared_ptr<PlayerBank200> file) {
|
void PlayerFilesManager::set_bank(const std::string& filename, std::shared_ptr<PlayerBank200> file) {
|
||||||
if (!this->loaded_bank_files.emplace(filename, file).second) {
|
if (!this->loaded_bank_files.emplace(filename, file).second) {
|
||||||
throw runtime_error("bank file already loaded: " + filename);
|
throw runtime_error("bank file already loaded: " + filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerFilesManager::clear_expired_files(evutil_socket_t, short, void* ctx) {
|
void PlayerFilesManager::clear_expired_files(evutil_socket_t, short, void* ctx) {
|
||||||
auto* self = reinterpret_cast<PlayerFilesManager*>(ctx);
|
auto* self = reinterpret_cast<PlayerFilesManager*>(ctx);
|
||||||
size_t num_deleted = erase_unused(self->loaded_system_files);
|
size_t num_deleted = erase_unused(self->loaded_system_files);
|
||||||
if (num_deleted) {
|
if (num_deleted) {
|
||||||
player_data_log.info("Cleared %zu expired system file(s)", num_deleted);
|
player_data_log.info("Cleared %zu expired system file(s)", num_deleted);
|
||||||
}
|
}
|
||||||
num_deleted = erase_unused(self->loaded_character_files);
|
num_deleted = erase_unused(self->loaded_character_files);
|
||||||
if (num_deleted) {
|
if (num_deleted) {
|
||||||
player_data_log.info("Cleared %zu expired character file(s)", num_deleted);
|
player_data_log.info("Cleared %zu expired character file(s)", num_deleted);
|
||||||
}
|
}
|
||||||
num_deleted = erase_unused(self->loaded_guild_card_files);
|
num_deleted = erase_unused(self->loaded_guild_card_files);
|
||||||
if (num_deleted) {
|
if (num_deleted) {
|
||||||
player_data_log.info("Cleared %zu expired Guild Card file(s)", num_deleted);
|
player_data_log.info("Cleared %zu expired Guild Card file(s)", num_deleted);
|
||||||
}
|
}
|
||||||
num_deleted = erase_unused(self->loaded_bank_files);
|
num_deleted = erase_unused(self->loaded_bank_files);
|
||||||
if (num_deleted) {
|
if (num_deleted) {
|
||||||
player_data_log.info("Cleared %zu expired bank file(s)", num_deleted);
|
player_data_log.info("Cleared %zu expired bank file(s)", num_deleted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-47
@@ -1,47 +1,47 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <phosg/Encoding.hh>
|
#include <phosg/Encoding.hh>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Episode3/DataIndexes.hh"
|
#include "Episode3/DataIndexes.hh"
|
||||||
#include "ItemCreator.hh"
|
#include "ItemCreator.hh"
|
||||||
#include "ItemNameIndex.hh"
|
#include "ItemNameIndex.hh"
|
||||||
#include "LevelTable.hh"
|
#include "LevelTable.hh"
|
||||||
#include "PlayerSubordinates.hh"
|
#include "PlayerSubordinates.hh"
|
||||||
#include "SaveFileFormats.hh"
|
#include "SaveFileFormats.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
class PlayerFilesManager {
|
class PlayerFilesManager {
|
||||||
public:
|
public:
|
||||||
explicit PlayerFilesManager(std::shared_ptr<struct event_base> base);
|
explicit PlayerFilesManager(std::shared_ptr<struct event_base> base);
|
||||||
~PlayerFilesManager() = default;
|
~PlayerFilesManager() = default;
|
||||||
|
|
||||||
std::shared_ptr<PSOBBBaseSystemFile> get_system(const std::string& filename);
|
std::shared_ptr<PSOBBBaseSystemFile> get_system(const std::string& filename);
|
||||||
std::shared_ptr<PSOBBCharacterFile> get_character(const std::string& filename);
|
std::shared_ptr<PSOBBCharacterFile> get_character(const std::string& filename);
|
||||||
std::shared_ptr<PSOBBGuildCardFile> get_guild_card(const std::string& filename);
|
std::shared_ptr<PSOBBGuildCardFile> get_guild_card(const std::string& filename);
|
||||||
std::shared_ptr<PlayerBank200> get_bank(const std::string& filename);
|
std::shared_ptr<PlayerBank200> get_bank(const std::string& filename);
|
||||||
|
|
||||||
void set_system(const std::string& filename, std::shared_ptr<PSOBBBaseSystemFile> file);
|
void set_system(const std::string& filename, std::shared_ptr<PSOBBBaseSystemFile> file);
|
||||||
void set_character(const std::string& filename, std::shared_ptr<PSOBBCharacterFile> file);
|
void set_character(const std::string& filename, std::shared_ptr<PSOBBCharacterFile> file);
|
||||||
void set_guild_card(const std::string& filename, std::shared_ptr<PSOBBGuildCardFile> file);
|
void set_guild_card(const std::string& filename, std::shared_ptr<PSOBBGuildCardFile> file);
|
||||||
void set_bank(const std::string& filename, std::shared_ptr<PlayerBank200> file);
|
void set_bank(const std::string& filename, std::shared_ptr<PlayerBank200> file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
std::unique_ptr<struct event, void (*)(struct event*)> clear_expired_files_event;
|
std::unique_ptr<struct event, void (*)(struct event*)> clear_expired_files_event;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<PSOBBBaseSystemFile>> loaded_system_files;
|
std::unordered_map<std::string, std::shared_ptr<PSOBBBaseSystemFile>> loaded_system_files;
|
||||||
std::unordered_map<std::string, std::shared_ptr<PSOBBCharacterFile>> loaded_character_files;
|
std::unordered_map<std::string, std::shared_ptr<PSOBBCharacterFile>> loaded_character_files;
|
||||||
std::unordered_map<std::string, std::shared_ptr<PSOBBGuildCardFile>> loaded_guild_card_files;
|
std::unordered_map<std::string, std::shared_ptr<PSOBBGuildCardFile>> loaded_guild_card_files;
|
||||||
std::unordered_map<std::string, std::shared_ptr<PlayerBank200>> loaded_bank_files;
|
std::unordered_map<std::string, std::shared_ptr<PlayerBank200>> loaded_bank_files;
|
||||||
|
|
||||||
static void clear_expired_files(evutil_socket_t fd, short events, void* ctx);
|
static void clear_expired_files(evutil_socket_t fd, short events, void* ctx);
|
||||||
};
|
};
|
||||||
|
|||||||
+2376
-2376
File diff suppressed because it is too large
Load Diff
+15
-15
@@ -1,15 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ProxyServer.hh"
|
#include "ProxyServer.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
void on_proxy_command(
|
void on_proxy_command(
|
||||||
std::shared_ptr<ProxyServer::LinkedSession> ses,
|
std::shared_ptr<ProxyServer::LinkedSession> ses,
|
||||||
bool from_server,
|
bool from_server,
|
||||||
uint16_t command,
|
uint16_t command,
|
||||||
uint32_t flag,
|
uint32_t flag,
|
||||||
std::string& data);
|
std::string& data);
|
||||||
|
|||||||
+1029
-1029
File diff suppressed because it is too large
Load Diff
+307
-307
@@ -1,307 +1,307 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Filesystem.hh>
|
#include <phosg/Filesystem.hh>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
class ProxyServer : public std::enable_shared_from_this<ProxyServer> {
|
class ProxyServer : public std::enable_shared_from_this<ProxyServer> {
|
||||||
public:
|
public:
|
||||||
ProxyServer() = delete;
|
ProxyServer() = delete;
|
||||||
ProxyServer(const ProxyServer&) = delete;
|
ProxyServer(const ProxyServer&) = delete;
|
||||||
ProxyServer(ProxyServer&&) = delete;
|
ProxyServer(ProxyServer&&) = delete;
|
||||||
ProxyServer(
|
ProxyServer(
|
||||||
std::shared_ptr<struct event_base> base,
|
std::shared_ptr<struct event_base> base,
|
||||||
std::shared_ptr<ServerState> state);
|
std::shared_ptr<ServerState> state);
|
||||||
virtual ~ProxyServer() = default;
|
virtual ~ProxyServer() = default;
|
||||||
|
|
||||||
void listen(const std::string& addr, uint16_t port, Version version, const struct sockaddr_storage* default_destination = nullptr);
|
void listen(const std::string& addr, uint16_t port, Version version, const struct sockaddr_storage* default_destination = nullptr);
|
||||||
|
|
||||||
void connect_virtual_client(struct bufferevent* bev, uint64_t virtual_network_id, uint16_t server_port);
|
void connect_virtual_client(struct bufferevent* bev, uint64_t virtual_network_id, uint16_t server_port);
|
||||||
|
|
||||||
struct LinkedSession : std::enable_shared_from_this<LinkedSession> {
|
struct LinkedSession : std::enable_shared_from_this<LinkedSession> {
|
||||||
std::weak_ptr<ProxyServer> server;
|
std::weak_ptr<ProxyServer> server;
|
||||||
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<Login> login;
|
std::shared_ptr<Login> login;
|
||||||
|
|
||||||
Channel client_channel;
|
Channel client_channel;
|
||||||
Channel server_channel;
|
Channel server_channel;
|
||||||
uint16_t local_port;
|
uint16_t local_port;
|
||||||
struct sockaddr_storage next_destination;
|
struct sockaddr_storage next_destination;
|
||||||
|
|
||||||
enum class DisconnectAction {
|
enum class DisconnectAction {
|
||||||
LONG_TIMEOUT = 0,
|
LONG_TIMEOUT = 0,
|
||||||
MEDIUM_TIMEOUT,
|
MEDIUM_TIMEOUT,
|
||||||
SHORT_TIMEOUT,
|
SHORT_TIMEOUT,
|
||||||
CLOSE_IMMEDIATELY,
|
CLOSE_IMMEDIATELY,
|
||||||
};
|
};
|
||||||
DisconnectAction disconnect_action;
|
DisconnectAction disconnect_action;
|
||||||
|
|
||||||
uint8_t prev_server_command_bytes[6];
|
uint8_t prev_server_command_bytes[6];
|
||||||
uint32_t remote_ip_crc;
|
uint32_t remote_ip_crc;
|
||||||
bool enable_remote_ip_crc_patch;
|
bool enable_remote_ip_crc_patch;
|
||||||
|
|
||||||
uint32_t sub_version;
|
uint32_t sub_version;
|
||||||
std::string character_name;
|
std::string character_name;
|
||||||
std::string hardware_id; // Only used for DC sessions
|
std::string hardware_id; // Only used for DC sessions
|
||||||
std::string login_command_bb;
|
std::string login_command_bb;
|
||||||
XBNetworkLocation xb_netloc;
|
XBNetworkLocation xb_netloc;
|
||||||
parray<le_uint32_t, 3> xb_9E_unknown_a1a;
|
parray<le_uint32_t, 3> xb_9E_unknown_a1a;
|
||||||
|
|
||||||
uint32_t challenge_rank_color_override;
|
uint32_t challenge_rank_color_override;
|
||||||
std::string challenge_rank_title_override;
|
std::string challenge_rank_title_override;
|
||||||
int64_t remote_guild_card_number;
|
int64_t remote_guild_card_number;
|
||||||
parray<uint8_t, 0x20> remote_client_config_data;
|
parray<uint8_t, 0x20> remote_client_config_data;
|
||||||
Client::Config config;
|
Client::Config config;
|
||||||
// A null handler in here means to forward the response to the remote server
|
// A null handler in here means to forward the response to the remote server
|
||||||
std::deque<std::function<void(uint32_t return_value, uint32_t checksum)>> function_call_return_handler_queue;
|
std::deque<std::function<void(uint32_t return_value, uint32_t checksum)>> function_call_return_handler_queue;
|
||||||
RecentSwitchFlags recent_switch_flags; // used for switch assist
|
RecentSwitchFlags recent_switch_flags; // used for switch assist
|
||||||
ItemData next_drop_item;
|
ItemData next_drop_item;
|
||||||
uint32_t next_item_id;
|
uint32_t next_item_id;
|
||||||
|
|
||||||
enum class DropMode {
|
enum class DropMode {
|
||||||
DISABLED = 0,
|
DISABLED = 0,
|
||||||
PASSTHROUGH,
|
PASSTHROUGH,
|
||||||
INTERCEPT,
|
INTERCEPT,
|
||||||
};
|
};
|
||||||
DropMode drop_mode;
|
DropMode drop_mode;
|
||||||
std::shared_ptr<std::string> quest_dat_data;
|
std::shared_ptr<std::string> quest_dat_data;
|
||||||
std::shared_ptr<ItemCreator> item_creator;
|
std::shared_ptr<ItemCreator> item_creator;
|
||||||
std::shared_ptr<Map> map;
|
std::shared_ptr<Map> map;
|
||||||
|
|
||||||
struct LobbyPlayer {
|
struct LobbyPlayer {
|
||||||
uint32_t guild_card_number = 0;
|
uint32_t guild_card_number = 0;
|
||||||
uint64_t xb_user_id = 0;
|
uint64_t xb_user_id = 0;
|
||||||
std::string name;
|
std::string name;
|
||||||
uint8_t language = 0;
|
uint8_t language = 0;
|
||||||
uint8_t section_id = 0;
|
uint8_t section_id = 0;
|
||||||
uint8_t char_class = 0;
|
uint8_t char_class = 0;
|
||||||
};
|
};
|
||||||
std::vector<LobbyPlayer> lobby_players;
|
std::vector<LobbyPlayer> lobby_players;
|
||||||
size_t lobby_client_id;
|
size_t lobby_client_id;
|
||||||
size_t leader_client_id;
|
size_t leader_client_id;
|
||||||
uint16_t floor;
|
uint16_t floor;
|
||||||
float x;
|
float x;
|
||||||
float z;
|
float z;
|
||||||
bool is_in_game;
|
bool is_in_game;
|
||||||
bool is_in_quest;
|
bool is_in_quest;
|
||||||
uint8_t lobby_event;
|
uint8_t lobby_event;
|
||||||
uint8_t lobby_difficulty;
|
uint8_t lobby_difficulty;
|
||||||
uint8_t lobby_section_id;
|
uint8_t lobby_section_id;
|
||||||
GameMode lobby_mode;
|
GameMode lobby_mode;
|
||||||
Episode lobby_episode;
|
Episode lobby_episode;
|
||||||
uint32_t lobby_random_seed;
|
uint32_t lobby_random_seed;
|
||||||
uint64_t client_ping_start_time = 0;
|
uint64_t client_ping_start_time = 0;
|
||||||
uint64_t server_ping_start_time = 0;
|
uint64_t server_ping_start_time = 0;
|
||||||
|
|
||||||
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt;
|
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt;
|
||||||
|
|
||||||
struct SavingFile {
|
struct SavingFile {
|
||||||
std::string basename;
|
std::string basename;
|
||||||
std::string output_filename;
|
std::string output_filename;
|
||||||
bool is_download;
|
bool is_download;
|
||||||
size_t remaining_bytes;
|
size_t remaining_bytes;
|
||||||
std::deque<std::string> blocks;
|
std::deque<std::string> blocks;
|
||||||
|
|
||||||
SavingFile(
|
SavingFile(
|
||||||
const std::string& basename,
|
const std::string& basename,
|
||||||
const std::string& output_filename,
|
const std::string& output_filename,
|
||||||
size_t remaining_bytes,
|
size_t remaining_bytes,
|
||||||
bool is_download);
|
bool is_download);
|
||||||
};
|
};
|
||||||
std::unordered_map<std::string, SavingFile> saving_files;
|
std::unordered_map<std::string, SavingFile> saving_files;
|
||||||
|
|
||||||
// TODO: This first constructor should be private
|
// TODO: This first constructor should be private
|
||||||
LinkedSession(
|
LinkedSession(
|
||||||
std::shared_ptr<ProxyServer> server,
|
std::shared_ptr<ProxyServer> server,
|
||||||
uint64_t id,
|
uint64_t id,
|
||||||
uint16_t local_port,
|
uint16_t local_port,
|
||||||
Version version);
|
Version version);
|
||||||
LinkedSession(
|
LinkedSession(
|
||||||
std::shared_ptr<ProxyServer> server,
|
std::shared_ptr<ProxyServer> server,
|
||||||
uint16_t local_port,
|
uint16_t local_port,
|
||||||
Version version,
|
Version version,
|
||||||
std::shared_ptr<Login> login,
|
std::shared_ptr<Login> login,
|
||||||
const Client::Config& config);
|
const Client::Config& config);
|
||||||
LinkedSession(
|
LinkedSession(
|
||||||
std::shared_ptr<ProxyServer> server,
|
std::shared_ptr<ProxyServer> server,
|
||||||
uint16_t local_port,
|
uint16_t local_port,
|
||||||
Version version,
|
Version version,
|
||||||
std::shared_ptr<Login> login,
|
std::shared_ptr<Login> login,
|
||||||
const struct sockaddr_storage& next_destination);
|
const struct sockaddr_storage& next_destination);
|
||||||
LinkedSession(
|
LinkedSession(
|
||||||
std::shared_ptr<ProxyServer> server,
|
std::shared_ptr<ProxyServer> server,
|
||||||
uint64_t id,
|
uint64_t id,
|
||||||
uint16_t local_port,
|
uint16_t local_port,
|
||||||
Version version,
|
Version version,
|
||||||
const struct sockaddr_storage& next_destination);
|
const struct sockaddr_storage& next_destination);
|
||||||
|
|
||||||
std::shared_ptr<ProxyServer> require_server() const;
|
std::shared_ptr<ProxyServer> require_server() const;
|
||||||
std::shared_ptr<ServerState> require_server_state() const;
|
std::shared_ptr<ServerState> require_server_state() const;
|
||||||
|
|
||||||
inline Version version() const {
|
inline Version version() const {
|
||||||
return this->client_channel.version;
|
return this->client_channel.version;
|
||||||
}
|
}
|
||||||
inline uint8_t language() const {
|
inline uint8_t language() const {
|
||||||
return this->client_channel.language;
|
return this->client_channel.language;
|
||||||
}
|
}
|
||||||
void set_version(Version v);
|
void set_version(Version v);
|
||||||
|
|
||||||
void resume(
|
void resume(
|
||||||
Channel&& client_channel,
|
Channel&& client_channel,
|
||||||
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt,
|
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt,
|
||||||
uint32_t sub_version,
|
uint32_t sub_version,
|
||||||
const std::string& character_name,
|
const std::string& character_name,
|
||||||
const std::string& hardware_id,
|
const std::string& hardware_id,
|
||||||
const XBNetworkLocation& xb_netloc,
|
const XBNetworkLocation& xb_netloc,
|
||||||
const parray<le_uint32_t, 3>& xb_9E_unknown_a1a);
|
const parray<le_uint32_t, 3>& xb_9E_unknown_a1a);
|
||||||
void resume(
|
void resume(
|
||||||
Channel&& client_channel,
|
Channel&& client_channel,
|
||||||
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt,
|
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt,
|
||||||
std::string&& login_command_bb);
|
std::string&& login_command_bb);
|
||||||
void resume(Channel&& client_channel);
|
void resume(Channel&& client_channel);
|
||||||
void resume_inner(
|
void resume_inner(
|
||||||
Channel&& client_channel,
|
Channel&& client_channel,
|
||||||
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt);
|
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt);
|
||||||
void connect();
|
void connect();
|
||||||
|
|
||||||
static uint64_t timeout_for_disconnect_action(DisconnectAction action);
|
static uint64_t timeout_for_disconnect_action(DisconnectAction action);
|
||||||
static void dispatch_on_timeout(evutil_socket_t fd, short what, void* ctx);
|
static void dispatch_on_timeout(evutil_socket_t fd, short what, void* ctx);
|
||||||
static void on_input(Channel& ch, uint16_t, uint32_t, std::string& msg);
|
static void on_input(Channel& ch, uint16_t, uint32_t, std::string& msg);
|
||||||
static void on_error(Channel& ch, short events);
|
static void on_error(Channel& ch, short events);
|
||||||
void on_timeout();
|
void on_timeout();
|
||||||
|
|
||||||
void update_channel_names();
|
void update_channel_names();
|
||||||
|
|
||||||
void clear_lobby_players(size_t num_slots);
|
void clear_lobby_players(size_t num_slots);
|
||||||
|
|
||||||
void set_drop_mode(DropMode new_mode);
|
void set_drop_mode(DropMode new_mode);
|
||||||
|
|
||||||
void send_to_game_server(const char* error_message = nullptr);
|
void send_to_game_server(const char* error_message = nullptr);
|
||||||
void disconnect();
|
void disconnect();
|
||||||
bool is_connected() const;
|
bool is_connected() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<LinkedSession> get_session() const;
|
std::shared_ptr<LinkedSession> get_session() const;
|
||||||
std::shared_ptr<LinkedSession> get_session_by_name(const std::string& name) const;
|
std::shared_ptr<LinkedSession> get_session_by_name(const std::string& name) const;
|
||||||
const std::unordered_map<uint64_t, std::shared_ptr<LinkedSession>>& all_sessions() const;
|
const std::unordered_map<uint64_t, std::shared_ptr<LinkedSession>>& all_sessions() const;
|
||||||
|
|
||||||
std::shared_ptr<LinkedSession> create_logged_in_session(
|
std::shared_ptr<LinkedSession> create_logged_in_session(
|
||||||
std::shared_ptr<Login> login,
|
std::shared_ptr<Login> login,
|
||||||
uint16_t local_port,
|
uint16_t local_port,
|
||||||
Version version,
|
Version version,
|
||||||
const Client::Config& config);
|
const Client::Config& config);
|
||||||
void delete_session(uint64_t id);
|
void delete_session(uint64_t id);
|
||||||
|
|
||||||
size_t num_sessions() const;
|
size_t num_sessions() const;
|
||||||
|
|
||||||
size_t delete_disconnected_sessions();
|
size_t delete_disconnected_sessions();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ListeningSocket {
|
struct ListeningSocket {
|
||||||
ProxyServer* server;
|
ProxyServer* server;
|
||||||
|
|
||||||
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;
|
||||||
Version version;
|
Version version;
|
||||||
struct sockaddr_storage default_destination;
|
struct sockaddr_storage default_destination;
|
||||||
|
|
||||||
ListeningSocket(
|
ListeningSocket(
|
||||||
ProxyServer* server,
|
ProxyServer* server,
|
||||||
const std::string& addr,
|
const std::string& addr,
|
||||||
uint16_t port,
|
uint16_t port,
|
||||||
Version version,
|
Version version,
|
||||||
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();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UnlinkedSession {
|
struct UnlinkedSession {
|
||||||
std::weak_ptr<ProxyServer> server;
|
std::weak_ptr<ProxyServer> server;
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
|
|
||||||
PrefixedLogger log;
|
PrefixedLogger log;
|
||||||
Channel channel;
|
Channel channel;
|
||||||
uint16_t local_port;
|
uint16_t local_port;
|
||||||
struct sockaddr_storage next_destination;
|
struct sockaddr_storage next_destination;
|
||||||
|
|
||||||
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt;
|
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt;
|
||||||
|
|
||||||
// Temporary state used just before resuming a LinkedSession. These aren't
|
// Temporary state used just before resuming a LinkedSession. These aren't
|
||||||
// just local variables inside on_input because XB requires two commands to
|
// just local variables inside on_input because XB requires two commands to
|
||||||
// get started (9E and 9F), so we need to store this state somewhere between
|
// get started (9E and 9F), so we need to store this state somewhere between
|
||||||
// those commands.
|
// those commands.
|
||||||
std::shared_ptr<Login> login;
|
std::shared_ptr<Login> login;
|
||||||
uint32_t sub_version = 0;
|
uint32_t sub_version = 0;
|
||||||
std::string character_name;
|
std::string character_name;
|
||||||
Client::Config config;
|
Client::Config config;
|
||||||
std::string login_command_bb;
|
std::string login_command_bb;
|
||||||
std::string hardware_id;
|
std::string hardware_id;
|
||||||
XBNetworkLocation xb_netloc;
|
XBNetworkLocation xb_netloc;
|
||||||
parray<le_uint32_t, 3> xb_9E_unknown_a1a;
|
parray<le_uint32_t, 3> xb_9E_unknown_a1a;
|
||||||
|
|
||||||
UnlinkedSession(
|
UnlinkedSession(
|
||||||
std::shared_ptr<ProxyServer> server,
|
std::shared_ptr<ProxyServer> server,
|
||||||
uint64_t id,
|
uint64_t id,
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
uint64_t virtual_network_id,
|
uint64_t virtual_network_id,
|
||||||
uint16_t port,
|
uint16_t port,
|
||||||
Version version);
|
Version version);
|
||||||
|
|
||||||
std::shared_ptr<ProxyServer> require_server() const;
|
std::shared_ptr<ProxyServer> require_server() const;
|
||||||
std::shared_ptr<ServerState> require_server_state() const;
|
std::shared_ptr<ServerState> require_server_state() const;
|
||||||
|
|
||||||
inline Version version() const {
|
inline Version version() const {
|
||||||
return this->channel.version;
|
return this->channel.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_and_process_commands();
|
void receive_and_process_commands();
|
||||||
|
|
||||||
static void on_input(Channel& ch, uint16_t command, uint32_t flag, std::string& msg);
|
static void on_input(Channel& ch, uint16_t command, uint32_t flag, std::string& msg);
|
||||||
static void on_error(Channel& ch, short events);
|
static void on_error(Channel& ch, short events);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
std::shared_ptr<struct event> destroy_sessions_ev;
|
std::shared_ptr<struct event> destroy_sessions_ev;
|
||||||
std::shared_ptr<ServerState> state;
|
std::shared_ptr<ServerState> state;
|
||||||
std::map<int, std::shared_ptr<ListeningSocket>> listeners;
|
std::map<int, std::shared_ptr<ListeningSocket>> listeners;
|
||||||
std::unordered_map<uint64_t, std::shared_ptr<UnlinkedSession>> id_to_unlinked_session;
|
std::unordered_map<uint64_t, std::shared_ptr<UnlinkedSession>> id_to_unlinked_session;
|
||||||
std::unordered_set<std::shared_ptr<UnlinkedSession>> unlinked_sessions_to_destroy;
|
std::unordered_set<std::shared_ptr<UnlinkedSession>> unlinked_sessions_to_destroy;
|
||||||
std::unordered_map<uint64_t, std::shared_ptr<LinkedSession>> id_to_linked_session;
|
std::unordered_map<uint64_t, std::shared_ptr<LinkedSession>> id_to_linked_session;
|
||||||
uint64_t next_unlinked_session_id;
|
uint64_t next_unlinked_session_id;
|
||||||
uint64_t next_logged_out_session_id;
|
uint64_t next_logged_out_session_id;
|
||||||
|
|
||||||
static void dispatch_destroy_sessions(evutil_socket_t, short, void* ctx);
|
static void dispatch_destroy_sessions(evutil_socket_t, short, void* ctx);
|
||||||
void destroy_sessions();
|
void destroy_sessions();
|
||||||
|
|
||||||
void on_client_connect(
|
void on_client_connect(
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
uint64_t virtual_network_id,
|
uint64_t virtual_network_id,
|
||||||
uint16_t listen_port,
|
uint16_t listen_port,
|
||||||
Version version,
|
Version version,
|
||||||
const struct sockaddr_storage* default_destination);
|
const struct sockaddr_storage* default_destination);
|
||||||
|
|
||||||
static constexpr uint64_t MIN_UNLINKED_SESSION_ID = 0xC000000000000000;
|
static constexpr uint64_t MIN_UNLINKED_SESSION_ID = 0xC000000000000000;
|
||||||
static constexpr uint64_t MIN_LINKED_LOGGED_OUT_SESSION_ID = 0x1000000000000000;
|
static constexpr uint64_t MIN_LINKED_LOGGED_OUT_SESSION_ID = 0x1000000000000000;
|
||||||
};
|
};
|
||||||
|
|||||||
+1381
-1381
File diff suppressed because it is too large
Load Diff
+202
-202
@@ -1,202 +1,202 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "IntegralExpression.hh"
|
#include "IntegralExpression.hh"
|
||||||
#include "PlayerSubordinates.hh"
|
#include "PlayerSubordinates.hh"
|
||||||
#include "QuestScript.hh"
|
#include "QuestScript.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "TeamIndex.hh"
|
#include "TeamIndex.hh"
|
||||||
|
|
||||||
enum class QuestFileFormat {
|
enum class QuestFileFormat {
|
||||||
BIN_DAT = 0,
|
BIN_DAT = 0,
|
||||||
BIN_DAT_UNCOMPRESSED,
|
BIN_DAT_UNCOMPRESSED,
|
||||||
BIN_DAT_GCI,
|
BIN_DAT_GCI,
|
||||||
BIN_DAT_VMS,
|
BIN_DAT_VMS,
|
||||||
BIN_DAT_DLQ,
|
BIN_DAT_DLQ,
|
||||||
QST,
|
QST,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class QuestMenuType {
|
enum class QuestMenuType {
|
||||||
NORMAL = 0,
|
NORMAL = 0,
|
||||||
BATTLE = 1,
|
BATTLE = 1,
|
||||||
CHALLENGE = 2,
|
CHALLENGE = 2,
|
||||||
SOLO = 3,
|
SOLO = 3,
|
||||||
GOVERNMENT = 4,
|
GOVERNMENT = 4,
|
||||||
DOWNLOAD = 5,
|
DOWNLOAD = 5,
|
||||||
EP3_DOWNLOAD = 6,
|
EP3_DOWNLOAD = 6,
|
||||||
// 7 can't be used as a menu type (it enables the per-episode filter)
|
// 7 can't be used as a menu type (it enables the per-episode filter)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct QuestCategoryIndex {
|
struct QuestCategoryIndex {
|
||||||
struct Category {
|
struct Category {
|
||||||
uint32_t category_id;
|
uint32_t category_id;
|
||||||
uint16_t enabled_flags;
|
uint16_t enabled_flags;
|
||||||
std::string directory_name;
|
std::string directory_name;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
|
||||||
explicit Category(uint32_t category_id, const JSON& json);
|
explicit Category(uint32_t category_id, const JSON& json);
|
||||||
|
|
||||||
[[nodiscard]] inline bool check_flag(QuestMenuType menu_type) const {
|
[[nodiscard]] inline bool check_flag(QuestMenuType menu_type) const {
|
||||||
return this->enabled_flags & (1 << static_cast<uint8_t>(menu_type));
|
return this->enabled_flags & (1 << static_cast<uint8_t>(menu_type));
|
||||||
}
|
}
|
||||||
[[nodiscard]] inline bool enable_episode_filter() const {
|
[[nodiscard]] inline bool enable_episode_filter() const {
|
||||||
return this->enabled_flags & 0x080;
|
return this->enabled_flags & 0x080;
|
||||||
}
|
}
|
||||||
[[nodiscard]] inline bool use_ep2_icon() const {
|
[[nodiscard]] inline bool use_ep2_icon() const {
|
||||||
return this->enabled_flags & 0x100;
|
return this->enabled_flags & 0x100;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Category>> categories;
|
std::vector<std::shared_ptr<Category>> categories;
|
||||||
|
|
||||||
explicit QuestCategoryIndex(const JSON& json);
|
explicit QuestCategoryIndex(const JSON& json);
|
||||||
|
|
||||||
std::shared_ptr<const Category> at(uint32_t category_id) const;
|
std::shared_ptr<const Category> at(uint32_t category_id) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VersionedQuest {
|
struct VersionedQuest {
|
||||||
uint32_t quest_number;
|
uint32_t quest_number;
|
||||||
uint32_t category_id;
|
uint32_t category_id;
|
||||||
Episode episode;
|
Episode episode;
|
||||||
bool allow_start_from_chat_command;
|
bool allow_start_from_chat_command;
|
||||||
bool joinable;
|
bool joinable;
|
||||||
int16_t lock_status_register;
|
int16_t lock_status_register;
|
||||||
std::string name;
|
std::string name;
|
||||||
Version version;
|
Version version;
|
||||||
uint8_t language;
|
uint8_t language;
|
||||||
bool is_dlq_encoded;
|
bool is_dlq_encoded;
|
||||||
std::string short_description;
|
std::string short_description;
|
||||||
std::string long_description;
|
std::string long_description;
|
||||||
std::shared_ptr<const std::string> bin_contents;
|
std::shared_ptr<const std::string> bin_contents;
|
||||||
std::shared_ptr<const std::string> dat_contents;
|
std::shared_ptr<const std::string> dat_contents;
|
||||||
std::shared_ptr<const std::string> dat_contents_decompressed;
|
std::shared_ptr<const std::string> dat_contents_decompressed;
|
||||||
std::shared_ptr<const std::string> pvr_contents;
|
std::shared_ptr<const std::string> pvr_contents;
|
||||||
std::shared_ptr<const BattleRules> battle_rules;
|
std::shared_ptr<const BattleRules> battle_rules;
|
||||||
ssize_t challenge_template_index;
|
ssize_t challenge_template_index;
|
||||||
uint8_t description_flag;
|
uint8_t description_flag;
|
||||||
std::shared_ptr<const IntegralExpression> available_expression;
|
std::shared_ptr<const IntegralExpression> available_expression;
|
||||||
std::shared_ptr<const IntegralExpression> enabled_expression;
|
std::shared_ptr<const IntegralExpression> enabled_expression;
|
||||||
|
|
||||||
VersionedQuest(
|
VersionedQuest(
|
||||||
uint32_t quest_number,
|
uint32_t quest_number,
|
||||||
uint32_t category_id,
|
uint32_t category_id,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
std::shared_ptr<const std::string> bin_contents,
|
std::shared_ptr<const std::string> bin_contents,
|
||||||
std::shared_ptr<const std::string> dat_contents,
|
std::shared_ptr<const std::string> dat_contents,
|
||||||
std::shared_ptr<const std::string> pvr_contents,
|
std::shared_ptr<const std::string> pvr_contents,
|
||||||
std::shared_ptr<const BattleRules> battle_rules = nullptr,
|
std::shared_ptr<const BattleRules> battle_rules = nullptr,
|
||||||
ssize_t challenge_template_index = -1,
|
ssize_t challenge_template_index = -1,
|
||||||
uint8_t description_flag = 0,
|
uint8_t description_flag = 0,
|
||||||
std::shared_ptr<const IntegralExpression> available_expression = nullptr,
|
std::shared_ptr<const IntegralExpression> available_expression = nullptr,
|
||||||
std::shared_ptr<const IntegralExpression> enabled_expression = nullptr,
|
std::shared_ptr<const IntegralExpression> enabled_expression = nullptr,
|
||||||
bool allow_start_from_chat_command = false,
|
bool allow_start_from_chat_command = false,
|
||||||
bool force_joinable = false,
|
bool force_joinable = false,
|
||||||
int16_t lock_status_register = -1);
|
int16_t lock_status_register = -1);
|
||||||
|
|
||||||
std::string bin_filename() const;
|
std::string bin_filename() const;
|
||||||
std::string dat_filename() const;
|
std::string dat_filename() const;
|
||||||
std::string pvr_filename() const;
|
std::string pvr_filename() const;
|
||||||
std::string xb_filename() const;
|
std::string xb_filename() const;
|
||||||
|
|
||||||
std::shared_ptr<VersionedQuest> create_download_quest(uint8_t override_language = 0xFF) const;
|
std::shared_ptr<VersionedQuest> create_download_quest(uint8_t override_language = 0xFF) const;
|
||||||
std::string encode_qst() const;
|
std::string encode_qst() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Quest {
|
class Quest {
|
||||||
public:
|
public:
|
||||||
Quest() = delete;
|
Quest() = delete;
|
||||||
explicit Quest(std::shared_ptr<const VersionedQuest> initial_version);
|
explicit Quest(std::shared_ptr<const VersionedQuest> initial_version);
|
||||||
Quest(const Quest&) = default;
|
Quest(const Quest&) = default;
|
||||||
Quest(Quest&&) = default;
|
Quest(Quest&&) = default;
|
||||||
Quest& operator=(const Quest&) = default;
|
Quest& operator=(const Quest&) = default;
|
||||||
Quest& operator=(Quest&&) = default;
|
Quest& operator=(Quest&&) = default;
|
||||||
|
|
||||||
void add_version(std::shared_ptr<const VersionedQuest> vq);
|
void add_version(std::shared_ptr<const VersionedQuest> vq);
|
||||||
bool has_version(Version v, uint8_t language) const;
|
bool has_version(Version v, uint8_t language) const;
|
||||||
bool has_version_any_language(Version v) const;
|
bool has_version_any_language(Version v) const;
|
||||||
std::shared_ptr<const VersionedQuest> version(Version v, uint8_t language) const;
|
std::shared_ptr<const VersionedQuest> version(Version v, uint8_t language) const;
|
||||||
|
|
||||||
static uint32_t versions_key(Version v, uint8_t language);
|
static uint32_t versions_key(Version v, uint8_t language);
|
||||||
|
|
||||||
uint32_t quest_number;
|
uint32_t quest_number;
|
||||||
uint32_t category_id;
|
uint32_t category_id;
|
||||||
Episode episode;
|
Episode episode;
|
||||||
bool allow_start_from_chat_command;
|
bool allow_start_from_chat_command;
|
||||||
bool joinable;
|
bool joinable;
|
||||||
int16_t lock_status_register;
|
int16_t lock_status_register;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::shared_ptr<const BattleRules> battle_rules;
|
std::shared_ptr<const BattleRules> battle_rules;
|
||||||
ssize_t challenge_template_index;
|
ssize_t challenge_template_index;
|
||||||
uint8_t description_flag;
|
uint8_t description_flag;
|
||||||
std::shared_ptr<const IntegralExpression> available_expression;
|
std::shared_ptr<const IntegralExpression> available_expression;
|
||||||
std::shared_ptr<const IntegralExpression> enabled_expression;
|
std::shared_ptr<const IntegralExpression> enabled_expression;
|
||||||
std::map<uint32_t, std::shared_ptr<const VersionedQuest>> versions;
|
std::map<uint32_t, std::shared_ptr<const VersionedQuest>> versions;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct QuestIndex {
|
struct QuestIndex {
|
||||||
enum class IncludeState {
|
enum class IncludeState {
|
||||||
HIDDEN = 0,
|
HIDDEN = 0,
|
||||||
AVAILABLE,
|
AVAILABLE,
|
||||||
DISABLED,
|
DISABLED,
|
||||||
};
|
};
|
||||||
using IncludeCondition = std::function<IncludeState(std::shared_ptr<const Quest>)>;
|
using IncludeCondition = std::function<IncludeState(std::shared_ptr<const Quest>)>;
|
||||||
|
|
||||||
std::string directory;
|
std::string directory;
|
||||||
std::shared_ptr<const QuestCategoryIndex> category_index;
|
std::shared_ptr<const QuestCategoryIndex> category_index;
|
||||||
|
|
||||||
std::map<uint32_t, std::shared_ptr<Quest>> quests_by_number;
|
std::map<uint32_t, std::shared_ptr<Quest>> quests_by_number;
|
||||||
std::map<std::string, std::shared_ptr<Quest>> quests_by_name;
|
std::map<std::string, std::shared_ptr<Quest>> quests_by_name;
|
||||||
std::map<uint32_t, std::map<uint32_t, std::shared_ptr<Quest>>> quests_by_category_id_and_number;
|
std::map<uint32_t, std::map<uint32_t, std::shared_ptr<Quest>>> quests_by_category_id_and_number;
|
||||||
|
|
||||||
QuestIndex(const std::string& directory, std::shared_ptr<const QuestCategoryIndex> category_index, bool is_ep3);
|
QuestIndex(const std::string& directory, std::shared_ptr<const QuestCategoryIndex> category_index, bool is_ep3);
|
||||||
|
|
||||||
std::shared_ptr<const Quest> get(uint32_t quest_number) const;
|
std::shared_ptr<const Quest> get(uint32_t quest_number) const;
|
||||||
std::shared_ptr<const Quest> get(const std::string& name) const;
|
std::shared_ptr<const Quest> get(const std::string& name) const;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const QuestCategoryIndex::Category>> categories(
|
std::vector<std::shared_ptr<const QuestCategoryIndex::Category>> categories(
|
||||||
QuestMenuType menu_type,
|
QuestMenuType menu_type,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
Version version,
|
Version version,
|
||||||
IncludeCondition include_condition = nullptr) const;
|
IncludeCondition include_condition = nullptr) const;
|
||||||
std::vector<std::pair<QuestIndex::IncludeState, std::shared_ptr<const Quest>>> filter(
|
std::vector<std::pair<QuestIndex::IncludeState, std::shared_ptr<const Quest>>> filter(
|
||||||
Episode episode,
|
Episode episode,
|
||||||
Version version,
|
Version version,
|
||||||
uint32_t category_id,
|
uint32_t category_id,
|
||||||
IncludeCondition include_condition = nullptr,
|
IncludeCondition include_condition = nullptr,
|
||||||
size_t limit = 0) const;
|
size_t limit = 0) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string encode_download_quest_data(
|
std::string encode_download_quest_data(
|
||||||
const std::string& compressed_data,
|
const std::string& compressed_data,
|
||||||
size_t decompressed_size = 0,
|
size_t decompressed_size = 0,
|
||||||
uint32_t encryption_seed = 0);
|
uint32_t encryption_seed = 0);
|
||||||
|
|
||||||
std::string decode_gci_data(
|
std::string decode_gci_data(
|
||||||
const std::string& data,
|
const std::string& data,
|
||||||
ssize_t find_seed_num_threads = -1,
|
ssize_t find_seed_num_threads = -1,
|
||||||
int64_t known_seed = -1,
|
int64_t known_seed = -1,
|
||||||
bool skip_checksum = false);
|
bool skip_checksum = false);
|
||||||
std::string decode_vms_data(
|
std::string decode_vms_data(
|
||||||
const std::string& data,
|
const std::string& data,
|
||||||
ssize_t find_seed_num_threads = -1,
|
ssize_t find_seed_num_threads = -1,
|
||||||
int64_t known_seed = -1,
|
int64_t known_seed = -1,
|
||||||
bool skip_checksum = false);
|
bool skip_checksum = false);
|
||||||
std::string decode_dlq_data(const std::string& data);
|
std::string decode_dlq_data(const std::string& data);
|
||||||
std::unordered_map<std::string, std::string> decode_qst_data(const std::string& data);
|
std::unordered_map<std::string, std::string> decode_qst_data(const std::string& data);
|
||||||
|
|
||||||
std::string encode_qst_file(
|
std::string encode_qst_file(
|
||||||
const std::unordered_map<std::string, std::shared_ptr<const std::string>>& files,
|
const std::unordered_map<std::string, std::shared_ptr<const std::string>>& files,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
uint32_t quest_number,
|
uint32_t quest_number,
|
||||||
const std::string& xb_filename,
|
const std::string& xb_filename,
|
||||||
Version version,
|
Version version,
|
||||||
bool is_dlq_encoded);
|
bool is_dlq_encoded);
|
||||||
|
|||||||
+653
-653
File diff suppressed because it is too large
Load Diff
+113
-113
@@ -1,113 +1,113 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/JSON.hh>
|
#include <phosg/JSON.hh>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "AFSArchive.hh"
|
#include "AFSArchive.hh"
|
||||||
#include "GSLArchive.hh"
|
#include "GSLArchive.hh"
|
||||||
#include "ItemNameIndex.hh"
|
#include "ItemNameIndex.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
class RareItemSet {
|
class RareItemSet {
|
||||||
public:
|
public:
|
||||||
struct ExpandedDrop {
|
struct ExpandedDrop {
|
||||||
uint32_t probability = 0;
|
uint32_t probability = 0;
|
||||||
ItemData data;
|
ItemData data;
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
std::string str(std::shared_ptr<const ItemNameIndex> name_index) const;
|
std::string str(std::shared_ptr<const ItemNameIndex> name_index) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
RareItemSet();
|
RareItemSet();
|
||||||
RareItemSet(const AFSArchive& afs, bool is_v1);
|
RareItemSet(const AFSArchive& afs, bool is_v1);
|
||||||
RareItemSet(const GSLArchive& gsl, bool is_big_endian);
|
RareItemSet(const GSLArchive& gsl, bool is_big_endian);
|
||||||
RareItemSet(const std::string& rel, bool is_big_endian);
|
RareItemSet(const std::string& rel, bool is_big_endian);
|
||||||
RareItemSet(const JSON& json, std::shared_ptr<const ItemNameIndex> name_index = nullptr);
|
RareItemSet(const JSON& json, std::shared_ptr<const ItemNameIndex> name_index = nullptr);
|
||||||
~RareItemSet() = default;
|
~RareItemSet() = default;
|
||||||
|
|
||||||
std::vector<ExpandedDrop> get_enemy_specs(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t rt_index) const;
|
std::vector<ExpandedDrop> get_enemy_specs(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t rt_index) const;
|
||||||
std::vector<ExpandedDrop> get_box_specs(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t area) const;
|
std::vector<ExpandedDrop> get_box_specs(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t area) const;
|
||||||
|
|
||||||
std::string serialize_afs(bool is_v1) const;
|
std::string serialize_afs(bool is_v1) const;
|
||||||
std::string serialize_gsl(bool big_endian) const;
|
std::string serialize_gsl(bool big_endian) const;
|
||||||
JSON json(std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
JSON json(std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
||||||
|
|
||||||
void multiply_all_rates(double factor);
|
void multiply_all_rates(double factor);
|
||||||
|
|
||||||
void print_collection(
|
void print_collection(
|
||||||
FILE* stream,
|
FILE* stream,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
uint8_t difficulty,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
||||||
void print_all_collections(FILE* stream, std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
void print_all_collections(FILE* stream, std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct SpecCollection {
|
struct SpecCollection {
|
||||||
std::vector<std::vector<ExpandedDrop>> rt_index_to_specs;
|
std::vector<std::vector<ExpandedDrop>> rt_index_to_specs;
|
||||||
std::vector<std::vector<ExpandedDrop>> box_area_to_specs;
|
std::vector<std::vector<ExpandedDrop>> box_area_to_specs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParsedRELData {
|
struct ParsedRELData {
|
||||||
struct PackedDrop {
|
struct PackedDrop {
|
||||||
uint8_t probability = 0;
|
uint8_t probability = 0;
|
||||||
parray<uint8_t, 3> item_code;
|
parray<uint8_t, 3> item_code;
|
||||||
|
|
||||||
PackedDrop() = default;
|
PackedDrop() = default;
|
||||||
explicit PackedDrop(const ExpandedDrop&);
|
explicit PackedDrop(const ExpandedDrop&);
|
||||||
ExpandedDrop expand() const;
|
ExpandedDrop expand() const;
|
||||||
} __packed_ws__(PackedDrop, 4);
|
} __packed_ws__(PackedDrop, 4);
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
struct OffsetsT {
|
struct OffsetsT {
|
||||||
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;
|
||||||
/* 00 */ U32T monster_rares_offset; // -> parray<PackedDrop, 0x65> (or 0x33 on v1)
|
/* 00 */ U32T monster_rares_offset; // -> parray<PackedDrop, 0x65> (or 0x33 on v1)
|
||||||
/* 04 */ U32T box_count; // Usually 30 (0x1E)
|
/* 04 */ U32T box_count; // Usually 30 (0x1E)
|
||||||
/* 08 */ U32T box_areas_offset; // -> parray<uint8_t, 0x1E>
|
/* 08 */ U32T box_areas_offset; // -> parray<uint8_t, 0x1E>
|
||||||
/* 0C */ U32T box_rares_offset; // -> parray<PackedDrop, 0x1E>
|
/* 0C */ U32T box_rares_offset; // -> parray<PackedDrop, 0x1E>
|
||||||
/* 10 */
|
/* 10 */
|
||||||
} __packed__;
|
} __packed__;
|
||||||
using Offsets = OffsetsT<false>;
|
using Offsets = OffsetsT<false>;
|
||||||
using OffsetsBE = OffsetsT<true>;
|
using OffsetsBE = OffsetsT<true>;
|
||||||
check_struct_size(Offsets, 0x10);
|
check_struct_size(Offsets, 0x10);
|
||||||
check_struct_size(OffsetsBE, 0x10);
|
check_struct_size(OffsetsBE, 0x10);
|
||||||
|
|
||||||
struct BoxRare {
|
struct BoxRare {
|
||||||
uint8_t area;
|
uint8_t area;
|
||||||
ExpandedDrop drop;
|
ExpandedDrop drop;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ExpandedDrop> monster_rares;
|
std::vector<ExpandedDrop> monster_rares;
|
||||||
std::vector<BoxRare> box_rares;
|
std::vector<BoxRare> box_rares;
|
||||||
|
|
||||||
ParsedRELData() = default;
|
ParsedRELData() = default;
|
||||||
ParsedRELData(StringReader r, bool big_endian, bool is_v1);
|
ParsedRELData(StringReader r, bool big_endian, bool is_v1);
|
||||||
explicit ParsedRELData(const SpecCollection& collection);
|
explicit ParsedRELData(const SpecCollection& collection);
|
||||||
std::string serialize(bool big_endian, bool is_v1) const;
|
std::string serialize(bool big_endian, bool is_v1) const;
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
void parse_t(StringReader r, bool is_v1);
|
void parse_t(StringReader r, bool is_v1);
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
std::string serialize_t(bool is_v1) const;
|
std::string serialize_t(bool is_v1) const;
|
||||||
|
|
||||||
SpecCollection as_collection() const;
|
SpecCollection as_collection() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_map<uint16_t, SpecCollection> collections;
|
std::unordered_map<uint16_t, SpecCollection> collections;
|
||||||
|
|
||||||
const SpecCollection& get_collection(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid) const;
|
const SpecCollection& get_collection(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid) const;
|
||||||
|
|
||||||
static std::string gsl_entry_name_for_table(GameMode mode, Episode episode, uint8_t difficulty, uint8_t section_id);
|
static std::string gsl_entry_name_for_table(GameMode mode, Episode episode, uint8_t difficulty, uint8_t section_id);
|
||||||
static uint16_t key_for_params(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid);
|
static uint16_t key_for_params(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid);
|
||||||
|
|
||||||
static uint32_t expand_rate(uint8_t pc);
|
static uint32_t expand_rate(uint8_t pc);
|
||||||
static uint8_t compress_rate(uint32_t probability);
|
static uint8_t compress_rate(uint32_t probability);
|
||||||
};
|
};
|
||||||
|
|||||||
+5676
-5676
File diff suppressed because it is too large
Load Diff
+31
-31
@@ -1,31 +1,31 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#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,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::string& password = "",
|
const std::string& password = "",
|
||||||
Episode episode = Episode::EP1,
|
Episode episode = Episode::EP1,
|
||||||
GameMode mode = GameMode::NORMAL,
|
GameMode mode = GameMode::NORMAL,
|
||||||
uint8_t difficulty = 0,
|
uint8_t difficulty = 0,
|
||||||
bool allow_v1 = false,
|
bool allow_v1 = false,
|
||||||
std::shared_ptr<Lobby> watched_lobby = nullptr,
|
std::shared_ptr<Lobby> watched_lobby = nullptr,
|
||||||
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player = nullptr);
|
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player = nullptr);
|
||||||
void set_lobby_quest(std::shared_ptr<Lobby> l, std::shared_ptr<const Quest> q, bool substitute_v3_for_ep3 = false);
|
void set_lobby_quest(std::shared_ptr<Lobby> l, std::shared_ptr<const Quest> q, bool substitute_v3_for_ep3 = false);
|
||||||
|
|
||||||
void on_connect(std::shared_ptr<Client> c);
|
void on_connect(std::shared_ptr<Client> c);
|
||||||
void on_disconnect(std::shared_ptr<Client> c);
|
void on_disconnect(std::shared_ptr<Client> c);
|
||||||
void on_login_complete(std::shared_ptr<Client> c);
|
void on_login_complete(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void on_command(std::shared_ptr<Client> c, uint16_t command, uint32_t flag, std::string& data);
|
void on_command(std::shared_ptr<Client> c, uint16_t command, uint32_t flag, std::string& data);
|
||||||
void on_command_with_header(std::shared_ptr<Client> c, const std::string& data);
|
void on_command_with_header(std::shared_ptr<Client> c, const std::string& data);
|
||||||
|
|
||||||
void send_client_to_login_server(std::shared_ptr<Client> c);
|
void send_client_to_login_server(std::shared_ptr<Client> c);
|
||||||
void send_client_to_lobby_server(std::shared_ptr<Client> c);
|
void send_client_to_lobby_server(std::shared_ptr<Client> c);
|
||||||
void send_client_to_proxy_server(std::shared_ptr<Client> c);
|
void send_client_to_proxy_server(std::shared_ptr<Client> c);
|
||||||
|
|||||||
+4788
-4788
File diff suppressed because it is too large
Load Diff
+124
-124
@@ -1,124 +1,124 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
#include "CommandFormats.hh"
|
#include "CommandFormats.hh"
|
||||||
#include "Lobby.hh"
|
#include "Lobby.hh"
|
||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
void on_subcommand_multi(std::shared_ptr<Client> c, uint8_t command, uint8_t flag, std::string& data);
|
void on_subcommand_multi(std::shared_ptr<Client> c, uint8_t command, uint8_t flag, std::string& data);
|
||||||
bool subcommand_is_implemented(uint8_t which);
|
bool subcommand_is_implemented(uint8_t which);
|
||||||
|
|
||||||
void send_item_notification_if_needed(
|
void send_item_notification_if_needed(
|
||||||
std::shared_ptr<ServerState> s,
|
std::shared_ptr<ServerState> s,
|
||||||
Channel& ch,
|
Channel& ch,
|
||||||
const Client::Config& config,
|
const Client::Config& config,
|
||||||
const ItemData& item,
|
const ItemData& item,
|
||||||
bool is_from_rare_table);
|
bool is_from_rare_table);
|
||||||
|
|
||||||
G_SpecializableItemDropRequest_6xA2 normalize_drop_request(const void* data, size_t size);
|
G_SpecializableItemDropRequest_6xA2 normalize_drop_request(const void* data, size_t size);
|
||||||
|
|
||||||
struct DropReconcileResult {
|
struct DropReconcileResult {
|
||||||
uint8_t effective_rt_index;
|
uint8_t effective_rt_index;
|
||||||
bool is_box;
|
bool is_box;
|
||||||
bool should_drop;
|
bool should_drop;
|
||||||
bool ignore_def;
|
bool ignore_def;
|
||||||
};
|
};
|
||||||
|
|
||||||
DropReconcileResult reconcile_drop_request_with_map(
|
DropReconcileResult reconcile_drop_request_with_map(
|
||||||
PrefixedLogger& log,
|
PrefixedLogger& log,
|
||||||
Channel& client_channel,
|
Channel& client_channel,
|
||||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||||
Version version,
|
Version version,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
const Client::Config& config,
|
const Client::Config& config,
|
||||||
std::shared_ptr<Map> map,
|
std::shared_ptr<Map> map,
|
||||||
bool mark_drop);
|
bool mark_drop);
|
||||||
|
|
||||||
class Parsed6x70Data {
|
class Parsed6x70Data {
|
||||||
public:
|
public:
|
||||||
Version from_version;
|
Version from_version;
|
||||||
bool from_client_customization;
|
bool from_client_customization;
|
||||||
Version item_version;
|
Version item_version;
|
||||||
|
|
||||||
G_SyncPlayerDispAndInventory_BaseDCNTE base;
|
G_SyncPlayerDispAndInventory_BaseDCNTE base;
|
||||||
uint32_t unknown_a5_nte = 0;
|
uint32_t unknown_a5_nte = 0;
|
||||||
uint32_t unknown_a6_nte = 0;
|
uint32_t unknown_a6_nte = 0;
|
||||||
uint16_t bonus_hp_from_materials = 0;
|
uint16_t bonus_hp_from_materials = 0;
|
||||||
uint16_t bonus_tp_from_materials = 0;
|
uint16_t bonus_tp_from_materials = 0;
|
||||||
parray<uint8_t, 0x10> unknown_a5_112000;
|
parray<uint8_t, 0x10> unknown_a5_112000;
|
||||||
parray<G_Unknown_6x70_SubA2, 5> unknown_a4_final;
|
parray<G_Unknown_6x70_SubA2, 5> unknown_a4_final;
|
||||||
uint32_t language = 0;
|
uint32_t language = 0;
|
||||||
uint32_t player_tag = 0;
|
uint32_t player_tag = 0;
|
||||||
uint32_t guild_card_number = 0;
|
uint32_t guild_card_number = 0;
|
||||||
uint32_t unknown_a6 = 0;
|
uint32_t unknown_a6 = 0;
|
||||||
uint32_t battle_team_number = 0;
|
uint32_t battle_team_number = 0;
|
||||||
Telepipe6x70 telepipe;
|
Telepipe6x70 telepipe;
|
||||||
uint32_t unknown_a8 = 0;
|
uint32_t unknown_a8 = 0;
|
||||||
parray<uint8_t, 0x10> unknown_a9_nte_112000;
|
parray<uint8_t, 0x10> unknown_a9_nte_112000;
|
||||||
G_Unknown_6x70_SubA1 unknown_a9_final;
|
G_Unknown_6x70_SubA1 unknown_a9_final;
|
||||||
uint32_t area = 0;
|
uint32_t area = 0;
|
||||||
uint32_t flags2 = 0;
|
uint32_t flags2 = 0;
|
||||||
parray<uint8_t, 0x14> technique_levels_v1 = 0xFF;
|
parray<uint8_t, 0x14> technique_levels_v1 = 0xFF;
|
||||||
PlayerVisualConfig visual;
|
PlayerVisualConfig visual;
|
||||||
std::string name;
|
std::string name;
|
||||||
PlayerStats stats;
|
PlayerStats stats;
|
||||||
uint32_t num_items = 0;
|
uint32_t num_items = 0;
|
||||||
parray<PlayerInventoryItem, 0x1E> items;
|
parray<PlayerInventoryItem, 0x1E> items;
|
||||||
uint32_t floor = 0;
|
uint32_t floor = 0;
|
||||||
uint64_t xb_user_id = 0;
|
uint64_t xb_user_id = 0;
|
||||||
uint32_t xb_unknown_a16 = 0;
|
uint32_t xb_unknown_a16 = 0;
|
||||||
|
|
||||||
Parsed6x70Data(
|
Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_DCNTE_6x70& cmd,
|
const G_SyncPlayerDispAndInventory_DCNTE_6x70& cmd,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
Parsed6x70Data(
|
Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd,
|
const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
Parsed6x70Data(
|
Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_DC_PC_6x70& cmd,
|
const G_SyncPlayerDispAndInventory_DC_PC_6x70& cmd,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
Parsed6x70Data(
|
Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_GC_6x70& cmd,
|
const G_SyncPlayerDispAndInventory_GC_6x70& cmd,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
Parsed6x70Data(
|
Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_XB_6x70& cmd,
|
const G_SyncPlayerDispAndInventory_XB_6x70& cmd,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
Parsed6x70Data(
|
Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_BB_6x70& cmd,
|
const G_SyncPlayerDispAndInventory_BB_6x70& cmd,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
|
|
||||||
G_SyncPlayerDispAndInventory_DCNTE_6x70 as_dc_nte(std::shared_ptr<ServerState> s) const;
|
G_SyncPlayerDispAndInventory_DCNTE_6x70 as_dc_nte(std::shared_ptr<ServerState> s) const;
|
||||||
G_SyncPlayerDispAndInventory_DC112000_6x70 as_dc_112000(std::shared_ptr<ServerState> s) const;
|
G_SyncPlayerDispAndInventory_DC112000_6x70 as_dc_112000(std::shared_ptr<ServerState> s) const;
|
||||||
G_SyncPlayerDispAndInventory_DC_PC_6x70 as_dc_pc(std::shared_ptr<ServerState> s, Version to_version) const;
|
G_SyncPlayerDispAndInventory_DC_PC_6x70 as_dc_pc(std::shared_ptr<ServerState> s, Version to_version) const;
|
||||||
G_SyncPlayerDispAndInventory_GC_6x70 as_gc_gcnte(std::shared_ptr<ServerState> s, Version to_version) const;
|
G_SyncPlayerDispAndInventory_GC_6x70 as_gc_gcnte(std::shared_ptr<ServerState> s, Version to_version) const;
|
||||||
G_SyncPlayerDispAndInventory_XB_6x70 as_xb(std::shared_ptr<ServerState> s) const;
|
G_SyncPlayerDispAndInventory_XB_6x70 as_xb(std::shared_ptr<ServerState> s) const;
|
||||||
G_SyncPlayerDispAndInventory_BB_6x70 as_bb(std::shared_ptr<ServerState> s, uint8_t language) const;
|
G_SyncPlayerDispAndInventory_BB_6x70 as_bb(std::shared_ptr<ServerState> s, uint8_t language) const;
|
||||||
|
|
||||||
uint64_t default_xb_user_id() const;
|
uint64_t default_xb_user_id() const;
|
||||||
void clear_v1_unused_item_fields();
|
void clear_v1_unused_item_fields();
|
||||||
void clear_dc_protos_unused_item_fields();
|
void clear_dc_protos_unused_item_fields();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Parsed6x70Data(
|
Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_BaseV1& base,
|
const G_SyncPlayerDispAndInventory_BaseV1& base,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
G_SyncPlayerDispAndInventory_BaseV1 base_v1() const;
|
G_SyncPlayerDispAndInventory_BaseV1 base_v1() const;
|
||||||
};
|
};
|
||||||
|
|||||||
+810
-810
File diff suppressed because it is too large
Load Diff
+99
-99
@@ -1,99 +1,99 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Channel.hh"
|
#include "Channel.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
class ReplaySession {
|
class ReplaySession {
|
||||||
public:
|
public:
|
||||||
ReplaySession(
|
ReplaySession(
|
||||||
std::shared_ptr<struct event_base> base,
|
std::shared_ptr<struct event_base> base,
|
||||||
FILE* input_log,
|
FILE* input_log,
|
||||||
std::shared_ptr<ServerState> state,
|
std::shared_ptr<ServerState> state,
|
||||||
bool require_basic_credentials);
|
bool require_basic_credentials);
|
||||||
ReplaySession(const ReplaySession&) = delete;
|
ReplaySession(const ReplaySession&) = delete;
|
||||||
ReplaySession(ReplaySession&&) = delete;
|
ReplaySession(ReplaySession&&) = delete;
|
||||||
ReplaySession& operator=(const ReplaySession&) = delete;
|
ReplaySession& operator=(const ReplaySession&) = delete;
|
||||||
ReplaySession& operator=(ReplaySession&&) = delete;
|
ReplaySession& operator=(ReplaySession&&) = delete;
|
||||||
~ReplaySession() = default;
|
~ReplaySession() = default;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Event {
|
struct Event {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
CONNECT = 0,
|
CONNECT = 0,
|
||||||
DISCONNECT,
|
DISCONNECT,
|
||||||
SEND,
|
SEND,
|
||||||
RECEIVE,
|
RECEIVE,
|
||||||
};
|
};
|
||||||
Type type;
|
Type type;
|
||||||
uint64_t client_id;
|
uint64_t client_id;
|
||||||
std::string data; // Only used for SEND and RECEIVE
|
std::string data; // Only used for SEND and RECEIVE
|
||||||
std::string mask; // Only used for RECEIVE
|
std::string mask; // Only used for RECEIVE
|
||||||
bool allow_size_disparity;
|
bool allow_size_disparity;
|
||||||
bool complete;
|
bool complete;
|
||||||
size_t line_num;
|
size_t line_num;
|
||||||
|
|
||||||
std::shared_ptr<Event> next_event;
|
std::shared_ptr<Event> next_event;
|
||||||
|
|
||||||
Event(Type type, uint64_t client_id, size_t line_num);
|
Event(Type type, uint64_t client_id, size_t line_num);
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Client {
|
struct Client {
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
Version version;
|
Version version;
|
||||||
Channel channel;
|
Channel channel;
|
||||||
std::deque<std::shared_ptr<Event>> receive_events;
|
std::deque<std::shared_ptr<Event>> receive_events;
|
||||||
std::shared_ptr<Event> disconnect_event;
|
std::shared_ptr<Event> disconnect_event;
|
||||||
|
|
||||||
Client(ReplaySession* session, uint64_t id, uint16_t port, Version version);
|
Client(ReplaySession* session, uint64_t id, uint16_t port, Version version);
|
||||||
|
|
||||||
std::string str() const;
|
std::string str() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<ServerState> state;
|
std::shared_ptr<ServerState> state;
|
||||||
bool require_basic_credentials;
|
bool require_basic_credentials;
|
||||||
|
|
||||||
std::unordered_map<uint64_t, std::shared_ptr<Client>> clients;
|
std::unordered_map<uint64_t, std::shared_ptr<Client>> clients;
|
||||||
std::unordered_map<Channel*, std::shared_ptr<Client>> channel_to_client;
|
std::unordered_map<Channel*, std::shared_ptr<Client>> channel_to_client;
|
||||||
|
|
||||||
std::shared_ptr<Event> first_event;
|
std::shared_ptr<Event> first_event;
|
||||||
std::shared_ptr<Event> last_event;
|
std::shared_ptr<Event> last_event;
|
||||||
|
|
||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
std::shared_ptr<struct event> timeout_ev;
|
std::shared_ptr<struct event> timeout_ev;
|
||||||
|
|
||||||
size_t commands_sent;
|
size_t commands_sent;
|
||||||
size_t bytes_sent;
|
size_t bytes_sent;
|
||||||
size_t commands_received;
|
size_t commands_received;
|
||||||
size_t bytes_received;
|
size_t bytes_received;
|
||||||
|
|
||||||
std::shared_ptr<ReplaySession::Event> create_event(
|
std::shared_ptr<ReplaySession::Event> create_event(
|
||||||
Event::Type type, std::shared_ptr<Client> c, size_t line_num);
|
Event::Type type, std::shared_ptr<Client> c, size_t line_num);
|
||||||
void update_timeout_event();
|
void update_timeout_event();
|
||||||
|
|
||||||
void apply_default_mask(std::shared_ptr<Event> ev);
|
void apply_default_mask(std::shared_ptr<Event> ev);
|
||||||
void check_for_password(std::shared_ptr<const Event> ev) const;
|
void check_for_password(std::shared_ptr<const Event> ev) const;
|
||||||
|
|
||||||
static void dispatch_on_timeout(evutil_socket_t fd, short events, void* ctx);
|
static void dispatch_on_timeout(evutil_socket_t fd, short events, void* ctx);
|
||||||
static void dispatch_on_command_received(
|
static void dispatch_on_command_received(
|
||||||
Channel& ch, uint16_t command, uint32_t flag, std::string& data);
|
Channel& ch, uint16_t command, uint32_t flag, std::string& data);
|
||||||
static void dispatch_on_error(Channel& ch, short events);
|
static void dispatch_on_error(Channel& ch, short events);
|
||||||
void on_command_received(
|
void on_command_received(
|
||||||
std::shared_ptr<Client> c, uint16_t command, uint32_t flag, std::string& data);
|
std::shared_ptr<Client> c, uint16_t command, uint32_t flag, std::string& data);
|
||||||
void on_error(std::shared_ptr<Client> c, short events);
|
void on_error(std::shared_ptr<Client> c, short events);
|
||||||
|
|
||||||
void execute_pending_events();
|
void execute_pending_events();
|
||||||
};
|
};
|
||||||
|
|||||||
+4251
-4251
File diff suppressed because it is too large
Load Diff
+456
-456
@@ -1,456 +1,456 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/Strings.hh>
|
#include <phosg/Strings.hh>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
#include "CommandFormats.hh"
|
#include "CommandFormats.hh"
|
||||||
#include "FunctionCompiler.hh"
|
#include "FunctionCompiler.hh"
|
||||||
#include "Lobby.hh"
|
#include "Lobby.hh"
|
||||||
#include "Menu.hh"
|
#include "Menu.hh"
|
||||||
#include "Quest.hh"
|
#include "Quest.hh"
|
||||||
#include "Server.hh"
|
#include "Server.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
|
|
||||||
extern const std::unordered_set<uint32_t> v2_crypt_initial_client_commands;
|
extern const std::unordered_set<uint32_t> v2_crypt_initial_client_commands;
|
||||||
extern const std::unordered_set<uint32_t> v3_crypt_initial_client_commands;
|
extern const std::unordered_set<uint32_t> v3_crypt_initial_client_commands;
|
||||||
extern const std::unordered_set<std::string> bb_crypt_initial_client_commands;
|
extern const std::unordered_set<std::string> bb_crypt_initial_client_commands;
|
||||||
|
|
||||||
constexpr size_t GC_QUEST_LOAD_MAX_CHUNKS_IN_FLIGHT = 4;
|
constexpr size_t GC_QUEST_LOAD_MAX_CHUNKS_IN_FLIGHT = 4;
|
||||||
|
|
||||||
// TODO: Many of these functions should take a Channel& instead of a
|
// TODO: Many of these functions should take a Channel& instead of a
|
||||||
// shared_ptr<Client>. Refactor functions appropriately.
|
// shared_ptr<Client>. Refactor functions appropriately.
|
||||||
|
|
||||||
// Note: There are so many versions of this function for a few reasons:
|
// Note: There are so many versions of this function for a few reasons:
|
||||||
// - There are a lot of different target types (sometimes we want to send a
|
// - There are a lot of different target types (sometimes we want to send a
|
||||||
// command to one client, sometimes to everyone in a lobby, etc.)
|
// command to one client, sometimes to everyone in a lobby, etc.)
|
||||||
// - For the const void* versions, the data and size arguments should not be
|
// - For the const void* versions, the data and size arguments should not be
|
||||||
// independently optional - this can lead to bugs where a non-null data
|
// independently optional - this can lead to bugs where a non-null data
|
||||||
// pointer is given but size is accidentally not given (e.g. if the type of
|
// pointer is given but size is accidentally not given (e.g. if the type of
|
||||||
// data in the calling function is changed from string to void*).
|
// data in the calling function is changed from string to void*).
|
||||||
|
|
||||||
template <typename CmdT>
|
template <typename CmdT>
|
||||||
void send_or_enqueue_command(std::shared_ptr<Client> c, uint16_t command, uint32_t flag, const CmdT& cmd) {
|
void send_or_enqueue_command(std::shared_ptr<Client> c, uint16_t command, uint32_t flag, const CmdT& cmd) {
|
||||||
if (c->game_join_command_queue) {
|
if (c->game_join_command_queue) {
|
||||||
c->log.info("Client not ready to receive game commands; adding to queue");
|
c->log.info("Client not ready to receive game commands; adding to queue");
|
||||||
auto& q_cmd = c->game_join_command_queue->emplace_back();
|
auto& q_cmd = c->game_join_command_queue->emplace_back();
|
||||||
q_cmd.command = command;
|
q_cmd.command = command;
|
||||||
q_cmd.flag = flag;
|
q_cmd.flag = flag;
|
||||||
// TODO: It'd be nice to avoid this copy. Maybe take in a pointer to cmd
|
// TODO: It'd be nice to avoid this copy. Maybe take in a pointer to cmd
|
||||||
// and move it into q_cmd somehow, so q_cmd can free it when needed?
|
// and move it into q_cmd somehow, so q_cmd can free it when needed?
|
||||||
q_cmd.data.assign(reinterpret_cast<const char*>(&cmd), sizeof(cmd));
|
q_cmd.data.assign(reinterpret_cast<const char*>(&cmd), sizeof(cmd));
|
||||||
} else {
|
} else {
|
||||||
send_command(c, command, flag, &cmd, sizeof(cmd));
|
send_command(c, command, flag, &cmd, sizeof(cmd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_command(std::shared_ptr<Client> c, uint16_t command,
|
void send_command(std::shared_ptr<Client> c, uint16_t command,
|
||||||
uint32_t flag, const std::vector<std::pair<const void*, size_t>>& blocks);
|
uint32_t flag, const std::vector<std::pair<const void*, size_t>>& blocks);
|
||||||
|
|
||||||
void send_command(std::shared_ptr<Client> c, uint16_t command,
|
void send_command(std::shared_ptr<Client> c, uint16_t command,
|
||||||
uint32_t flag, const void* data, size_t size);
|
uint32_t flag, const void* data, size_t size);
|
||||||
|
|
||||||
inline void send_command(std::shared_ptr<Client> c, uint16_t command,
|
inline void send_command(std::shared_ptr<Client> c, uint16_t command,
|
||||||
uint32_t flag) {
|
uint32_t flag) {
|
||||||
send_command(c, command, flag, nullptr, 0);
|
send_command(c, command, flag, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_command_excluding_client(std::shared_ptr<Lobby> l,
|
void send_command_excluding_client(std::shared_ptr<Lobby> l,
|
||||||
std::shared_ptr<Client> c, uint16_t command, uint32_t flag,
|
std::shared_ptr<Client> c, uint16_t command, uint32_t flag,
|
||||||
const void* data, size_t size);
|
const void* data, size_t size);
|
||||||
|
|
||||||
inline void send_command_excluding_client(std::shared_ptr<Lobby> l,
|
inline void send_command_excluding_client(std::shared_ptr<Lobby> l,
|
||||||
std::shared_ptr<Client> c, uint16_t command, uint32_t flag) {
|
std::shared_ptr<Client> c, uint16_t command, uint32_t flag) {
|
||||||
send_command_excluding_client(l, c, command, flag, nullptr, 0);
|
send_command_excluding_client(l, c, command, flag, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_command_if_not_loading(std::shared_ptr<Lobby> l,
|
void send_command_if_not_loading(std::shared_ptr<Lobby> l,
|
||||||
uint16_t command, uint32_t flag, const void* data, size_t size);
|
uint16_t command, uint32_t flag, const void* data, size_t size);
|
||||||
inline void send_command_if_not_loading(std::shared_ptr<Lobby> l,
|
inline void send_command_if_not_loading(std::shared_ptr<Lobby> l,
|
||||||
uint16_t command, uint32_t flag, const std::string& data) {
|
uint16_t command, uint32_t flag, const std::string& data) {
|
||||||
send_command_if_not_loading(l, command, flag, data.data(), data.size());
|
send_command_if_not_loading(l, command, flag, data.data(), data.size());
|
||||||
}
|
}
|
||||||
template <typename StructT>
|
template <typename StructT>
|
||||||
inline void send_command_if_not_loading(std::shared_ptr<Lobby> l,
|
inline void send_command_if_not_loading(std::shared_ptr<Lobby> l,
|
||||||
uint16_t command, uint32_t flag, const StructT& data) {
|
uint16_t command, uint32_t flag, const StructT& data) {
|
||||||
send_command_if_not_loading(l, command, flag, &data, sizeof(data));
|
send_command_if_not_loading(l, command, flag, &data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_command(std::shared_ptr<Lobby> l, uint16_t command, uint32_t flag,
|
void send_command(std::shared_ptr<Lobby> l, uint16_t command, uint32_t flag,
|
||||||
const void* data, size_t size);
|
const void* data, size_t size);
|
||||||
|
|
||||||
inline void send_command(std::shared_ptr<Lobby> l, uint16_t command, uint32_t flag) {
|
inline void send_command(std::shared_ptr<Lobby> l, uint16_t command, uint32_t flag) {
|
||||||
send_command(l, command, flag, nullptr, 0);
|
send_command(l, command, flag, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_command(std::shared_ptr<ServerState> s, uint16_t command,
|
void send_command(std::shared_ptr<ServerState> s, uint16_t command,
|
||||||
uint32_t flag, const void* data, size_t size);
|
uint32_t flag, const void* data, size_t size);
|
||||||
|
|
||||||
inline void send_command(std::shared_ptr<ServerState> s, uint16_t command,
|
inline void send_command(std::shared_ptr<ServerState> s, uint16_t command,
|
||||||
uint32_t flag) {
|
uint32_t flag) {
|
||||||
send_command(s, command, flag, nullptr, 0);
|
send_command(s, command, flag, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TargetT, typename StructT>
|
template <typename TargetT, typename StructT>
|
||||||
static void send_command_t(std::shared_ptr<TargetT> c, uint16_t command,
|
static void send_command_t(std::shared_ptr<TargetT> c, uint16_t command,
|
||||||
uint32_t flag, const StructT& data) {
|
uint32_t flag, const StructT& data) {
|
||||||
send_command(c, command, flag, &data, sizeof(data));
|
send_command(c, command, flag, &data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TargetT>
|
template <typename TargetT>
|
||||||
static void send_command(std::shared_ptr<TargetT> c, uint16_t command,
|
static void send_command(std::shared_ptr<TargetT> c, uint16_t command,
|
||||||
uint32_t flag, const std::string& data) {
|
uint32_t flag, const std::string& data) {
|
||||||
send_command(c, command, flag, data.data(), data.size());
|
send_command(c, command, flag, data.data(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TargetT, typename StructT>
|
template <typename TargetT, typename StructT>
|
||||||
void send_command_vt(std::shared_ptr<TargetT> c, uint16_t command,
|
void send_command_vt(std::shared_ptr<TargetT> c, uint16_t command,
|
||||||
uint32_t flag, const std::vector<StructT>& data) {
|
uint32_t flag, const std::vector<StructT>& data) {
|
||||||
send_command(c, command, flag, data.data(), data.size() * sizeof(StructT));
|
send_command(c, command, flag, data.data(), data.size() * sizeof(StructT));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StructT>
|
template <typename StructT>
|
||||||
void send_command_vt(Channel& ch, uint16_t command, uint32_t flag,
|
void send_command_vt(Channel& ch, uint16_t command, uint32_t flag,
|
||||||
const std::vector<StructT>& data) {
|
const std::vector<StructT>& data) {
|
||||||
ch.send(command, flag, data.data(), data.size() * sizeof(StructT));
|
ch.send(command, flag, data.data(), data.size() * sizeof(StructT));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TargetT, typename StructT, typename EntryT>
|
template <typename TargetT, typename StructT, typename EntryT>
|
||||||
void send_command_t_vt(std::shared_ptr<TargetT> c, uint16_t command,
|
void send_command_t_vt(std::shared_ptr<TargetT> c, uint16_t command,
|
||||||
uint32_t flag, const StructT& data, const std::vector<EntryT>& array_data) {
|
uint32_t flag, const StructT& data, const std::vector<EntryT>& array_data) {
|
||||||
std::string all_data(reinterpret_cast<const char*>(&data), sizeof(StructT));
|
std::string all_data(reinterpret_cast<const char*>(&data), sizeof(StructT));
|
||||||
all_data.append(reinterpret_cast<const char*>(array_data.data()),
|
all_data.append(reinterpret_cast<const char*>(array_data.data()),
|
||||||
array_data.size() * sizeof(EntryT));
|
array_data.size() * sizeof(EntryT));
|
||||||
send_command(c, command, flag, all_data.data(), all_data.size());
|
send_command(c, command, flag, all_data.data(), all_data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_command_with_header(Channel& c, const void* data, size_t size);
|
void send_command_with_header(Channel& c, const void* data, size_t size);
|
||||||
|
|
||||||
enum SendServerInitFlag {
|
enum SendServerInitFlag {
|
||||||
IS_INITIAL_CONNECTION = 0x01,
|
IS_INITIAL_CONNECTION = 0x01,
|
||||||
USE_SECONDARY_MESSAGE = 0x02,
|
USE_SECONDARY_MESSAGE = 0x02,
|
||||||
};
|
};
|
||||||
|
|
||||||
S_ServerInitWithAfterMessageT_DC_PC_V3_02_17_91_9B<0xB4>
|
S_ServerInitWithAfterMessageT_DC_PC_V3_02_17_91_9B<0xB4>
|
||||||
prepare_server_init_contents_console(
|
prepare_server_init_contents_console(
|
||||||
uint32_t server_key, uint32_t client_key, uint8_t flags);
|
uint32_t server_key, uint32_t client_key, uint8_t flags);
|
||||||
S_ServerInitWithAfterMessageT_BB_03_9B<0xB4>
|
S_ServerInitWithAfterMessageT_BB_03_9B<0xB4>
|
||||||
prepare_server_init_contents_bb(
|
prepare_server_init_contents_bb(
|
||||||
const parray<uint8_t, 0x30>& server_key,
|
const parray<uint8_t, 0x30>& server_key,
|
||||||
const parray<uint8_t, 0x30>& client_key,
|
const parray<uint8_t, 0x30>& client_key,
|
||||||
uint8_t flags);
|
uint8_t flags);
|
||||||
void send_server_init(std::shared_ptr<Client> c, uint8_t flags);
|
void send_server_init(std::shared_ptr<Client> c, uint8_t flags);
|
||||||
void send_update_client_config(std::shared_ptr<Client> c, bool always_send);
|
void send_update_client_config(std::shared_ptr<Client> c, bool always_send);
|
||||||
|
|
||||||
void empty_function_call_response_handler(uint32_t, uint32_t);
|
void empty_function_call_response_handler(uint32_t, uint32_t);
|
||||||
|
|
||||||
void send_quest_buffer_overflow(std::shared_ptr<Client> c);
|
void send_quest_buffer_overflow(std::shared_ptr<Client> c);
|
||||||
void prepare_client_for_patches(std::shared_ptr<Client> c, std::function<void()> on_complete);
|
void prepare_client_for_patches(std::shared_ptr<Client> c, std::function<void()> on_complete);
|
||||||
std::string prepare_send_function_call_data(
|
std::string prepare_send_function_call_data(
|
||||||
std::shared_ptr<const CompiledFunctionCode> code,
|
std::shared_ptr<const CompiledFunctionCode> code,
|
||||||
const std::unordered_map<std::string, uint32_t>& label_writes,
|
const std::unordered_map<std::string, uint32_t>& label_writes,
|
||||||
const void* suffix_data,
|
const void* suffix_data,
|
||||||
size_t suffix_size,
|
size_t suffix_size,
|
||||||
uint32_t checksum_addr,
|
uint32_t checksum_addr,
|
||||||
uint32_t checksum_size,
|
uint32_t checksum_size,
|
||||||
uint32_t override_relocations_offset,
|
uint32_t override_relocations_offset,
|
||||||
bool use_encrypted_format);
|
bool use_encrypted_format);
|
||||||
void send_function_call(
|
void send_function_call(
|
||||||
Channel& ch,
|
Channel& ch,
|
||||||
const Client::Config& client_config,
|
const Client::Config& client_config,
|
||||||
std::shared_ptr<const CompiledFunctionCode> code,
|
std::shared_ptr<const CompiledFunctionCode> code,
|
||||||
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
||||||
const void* suffix_data = nullptr,
|
const void* suffix_data = nullptr,
|
||||||
size_t suffix_size = 0,
|
size_t suffix_size = 0,
|
||||||
uint32_t checksum_addr = 0,
|
uint32_t checksum_addr = 0,
|
||||||
uint32_t checksum_size = 0,
|
uint32_t checksum_size = 0,
|
||||||
uint32_t override_relocations_offset = 0);
|
uint32_t override_relocations_offset = 0);
|
||||||
void send_function_call(
|
void send_function_call(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<const CompiledFunctionCode> code,
|
std::shared_ptr<const CompiledFunctionCode> code,
|
||||||
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
||||||
const void* suffix_data = nullptr,
|
const void* suffix_data = nullptr,
|
||||||
size_t suffix_size = 0,
|
size_t suffix_size = 0,
|
||||||
uint32_t checksum_addr = 0,
|
uint32_t checksum_addr = 0,
|
||||||
uint32_t checksum_size = 0,
|
uint32_t checksum_size = 0,
|
||||||
uint32_t override_relocations_offset = 0);
|
uint32_t override_relocations_offset = 0);
|
||||||
bool send_protected_command(std::shared_ptr<Client> c, const void* data, size_t size, bool echo_to_lobby);
|
bool send_protected_command(std::shared_ptr<Client> c, const void* data, size_t size, bool echo_to_lobby);
|
||||||
|
|
||||||
void send_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port);
|
void send_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port);
|
||||||
void send_pc_console_split_reconnect(
|
void send_pc_console_split_reconnect(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
uint32_t address,
|
uint32_t address,
|
||||||
uint16_t pc_port,
|
uint16_t pc_port,
|
||||||
uint16_t console_port);
|
uint16_t console_port);
|
||||||
|
|
||||||
void send_client_init_bb(std::shared_ptr<Client> c, uint32_t error);
|
void send_client_init_bb(std::shared_ptr<Client> c, uint32_t error);
|
||||||
void send_system_file_bb(std::shared_ptr<Client> c);
|
void send_system_file_bb(std::shared_ptr<Client> c);
|
||||||
void send_player_preview_bb(std::shared_ptr<Client> c, int8_t character_index, const PlayerDispDataBBPreview* preview);
|
void send_player_preview_bb(std::shared_ptr<Client> c, int8_t character_index, const PlayerDispDataBBPreview* preview);
|
||||||
void send_accept_client_checksum_bb(std::shared_ptr<Client> c);
|
void send_accept_client_checksum_bb(std::shared_ptr<Client> c);
|
||||||
void send_guild_card_header_bb(std::shared_ptr<Client> c);
|
void send_guild_card_header_bb(std::shared_ptr<Client> c);
|
||||||
void send_guild_card_chunk_bb(std::shared_ptr<Client> c, size_t chunk_index);
|
void send_guild_card_chunk_bb(std::shared_ptr<Client> c, size_t chunk_index);
|
||||||
void send_stream_file_index_bb(std::shared_ptr<Client> c);
|
void send_stream_file_index_bb(std::shared_ptr<Client> c);
|
||||||
void send_stream_file_chunk_bb(std::shared_ptr<Client> c, uint32_t chunk_index);
|
void send_stream_file_chunk_bb(std::shared_ptr<Client> c, uint32_t chunk_index);
|
||||||
void send_approve_player_choice_bb(std::shared_ptr<Client> c);
|
void send_approve_player_choice_bb(std::shared_ptr<Client> c);
|
||||||
void send_complete_player_bb(std::shared_ptr<Client> c);
|
void send_complete_player_bb(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void send_message_box(std::shared_ptr<Client> c, const std::string& text);
|
void send_message_box(std::shared_ptr<Client> c, const std::string& text);
|
||||||
void send_ep3_timed_message_box(Channel& ch, uint32_t frames, const std::string& text);
|
void send_ep3_timed_message_box(Channel& ch, uint32_t frames, const std::string& text);
|
||||||
void send_lobby_name(std::shared_ptr<Client> c, const std::string& text);
|
void send_lobby_name(std::shared_ptr<Client> c, const std::string& text);
|
||||||
void send_quest_info(std::shared_ptr<Client> c, const std::string& text, uint8_t flag, bool is_download_quest);
|
void send_quest_info(std::shared_ptr<Client> c, const std::string& text, uint8_t flag, bool is_download_quest);
|
||||||
void send_lobby_message_box(std::shared_ptr<Client> c, const std::string& text, bool left_side_on_bb = false);
|
void send_lobby_message_box(std::shared_ptr<Client> c, const std::string& text, bool left_side_on_bb = false);
|
||||||
void send_ship_info(std::shared_ptr<Client> c, const std::string& text);
|
void send_ship_info(std::shared_ptr<Client> c, const std::string& text);
|
||||||
void send_ship_info(Channel& ch, const std::string& text);
|
void send_ship_info(Channel& ch, const std::string& text);
|
||||||
void send_text_message(Channel& ch, const std::string& text);
|
void send_text_message(Channel& ch, const std::string& text);
|
||||||
void send_text_message(std::shared_ptr<Client> c, const std::string& text);
|
void send_text_message(std::shared_ptr<Client> c, const std::string& text);
|
||||||
void send_text_message(std::shared_ptr<Lobby> l, const std::string& text);
|
void send_text_message(std::shared_ptr<Lobby> l, const std::string& text);
|
||||||
void send_text_message(std::shared_ptr<ServerState> s, const std::string& text);
|
void send_text_message(std::shared_ptr<ServerState> s, const std::string& text);
|
||||||
void send_scrolling_message_bb(std::shared_ptr<Client> c, const std::string& text);
|
void send_scrolling_message_bb(std::shared_ptr<Client> c, const std::string& text);
|
||||||
void send_text_or_scrolling_message(std::shared_ptr<Client> c, const std::string& text, const std::string& scrolling);
|
void send_text_or_scrolling_message(std::shared_ptr<Client> c, const std::string& text, const std::string& scrolling);
|
||||||
void send_text_or_scrolling_message(
|
void send_text_or_scrolling_message(
|
||||||
std::shared_ptr<Lobby> l, std::shared_ptr<Client> exclude_c, const std::string& text, const std::string& scrolling);
|
std::shared_ptr<Lobby> l, std::shared_ptr<Client> exclude_c, const std::string& text, const std::string& scrolling);
|
||||||
|
|
||||||
std::string prepare_chat_data(
|
std::string prepare_chat_data(
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
uint8_t from_client_id,
|
uint8_t from_client_id,
|
||||||
const std::string& from_name,
|
const std::string& from_name,
|
||||||
const std::string& text,
|
const std::string& text,
|
||||||
char private_flags);
|
char private_flags);
|
||||||
void send_chat_message_from_client(
|
void send_chat_message_from_client(
|
||||||
Channel& ch,
|
Channel& ch,
|
||||||
const std::string& text,
|
const std::string& text,
|
||||||
char private_flags);
|
char private_flags);
|
||||||
void send_prepared_chat_message(
|
void send_prepared_chat_message(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
uint32_t from_guild_card_number,
|
uint32_t from_guild_card_number,
|
||||||
const std::string& prepared_data);
|
const std::string& prepared_data);
|
||||||
void send_prepared_chat_message(
|
void send_prepared_chat_message(
|
||||||
std::shared_ptr<Lobby> l,
|
std::shared_ptr<Lobby> l,
|
||||||
uint32_t from_guild_card_number,
|
uint32_t from_guild_card_number,
|
||||||
const std::string& prepared_data);
|
const std::string& prepared_data);
|
||||||
void send_chat_message(
|
void send_chat_message(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
uint32_t from_guild_card_number,
|
uint32_t from_guild_card_number,
|
||||||
const std::string& from_name,
|
const std::string& from_name,
|
||||||
const std::string& text,
|
const std::string& text,
|
||||||
char private_flags);
|
char private_flags);
|
||||||
void send_simple_mail(
|
void send_simple_mail(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
uint32_t from_guild_card_number,
|
uint32_t from_guild_card_number,
|
||||||
const std::string& from_name,
|
const std::string& from_name,
|
||||||
const std::string& text);
|
const std::string& text);
|
||||||
void send_simple_mail(
|
void send_simple_mail(
|
||||||
std::shared_ptr<ServerState> s,
|
std::shared_ptr<ServerState> s,
|
||||||
uint32_t from_guild_card_number,
|
uint32_t from_guild_card_number,
|
||||||
const std::string& from_name,
|
const std::string& from_name,
|
||||||
const std::string& text);
|
const std::string& text);
|
||||||
|
|
||||||
template <typename TargetT>
|
template <typename TargetT>
|
||||||
__attribute__((format(printf, 2, 3))) void send_text_message_printf(
|
__attribute__((format(printf, 2, 3))) void send_text_message_printf(
|
||||||
TargetT& t, const char* format, ...) {
|
TargetT& t, const char* format, ...) {
|
||||||
va_list va;
|
va_list va;
|
||||||
va_start(va, format);
|
va_start(va, format);
|
||||||
std::string buf = string_vprintf(format, va);
|
std::string buf = string_vprintf(format, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
return send_text_message(t, buf.c_str());
|
return send_text_message(t, buf.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((format(printf, 2, 3))) void send_ep3_text_message_printf(
|
__attribute__((format(printf, 2, 3))) void send_ep3_text_message_printf(
|
||||||
std::shared_ptr<ServerState> s, const char* format, ...);
|
std::shared_ptr<ServerState> s, const char* format, ...);
|
||||||
|
|
||||||
void send_info_board(std::shared_ptr<Client> c);
|
void send_info_board(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void send_choice_search_choices(std::shared_ptr<Client> c);
|
void send_choice_search_choices(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void send_card_search_result(
|
void send_card_search_result(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<Client> result,
|
std::shared_ptr<Client> result,
|
||||||
std::shared_ptr<Lobby> result_lobby);
|
std::shared_ptr<Lobby> result_lobby);
|
||||||
|
|
||||||
void send_guild_card(
|
void send_guild_card(
|
||||||
Channel& ch,
|
Channel& ch,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint64_t xb_user_id,
|
uint64_t xb_user_id,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::string& team_name,
|
const std::string& team_name,
|
||||||
const std::string& description,
|
const std::string& description,
|
||||||
uint8_t language,
|
uint8_t language,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
uint8_t char_class);
|
uint8_t char_class);
|
||||||
void send_guild_card(std::shared_ptr<Client> c, std::shared_ptr<Client> source);
|
void send_guild_card(std::shared_ptr<Client> c, std::shared_ptr<Client> source);
|
||||||
void send_menu(std::shared_ptr<Client> c, std::shared_ptr<const Menu> menu, bool is_info_menu = false);
|
void send_menu(std::shared_ptr<Client> c, std::shared_ptr<const Menu> menu, bool is_info_menu = false);
|
||||||
void send_game_menu(
|
void send_game_menu(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
bool is_spectator_team_list,
|
bool is_spectator_team_list,
|
||||||
bool is_tournament_game_list);
|
bool is_tournament_game_list);
|
||||||
void send_quest_menu(
|
void send_quest_menu(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
const std::vector<std::pair<QuestIndex::IncludeState, std::shared_ptr<const Quest>>>& quests,
|
const std::vector<std::pair<QuestIndex::IncludeState, std::shared_ptr<const Quest>>>& quests,
|
||||||
bool is_download_menu);
|
bool is_download_menu);
|
||||||
void send_quest_categories_menu(
|
void send_quest_categories_menu(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<const QuestIndex> quest_index,
|
std::shared_ptr<const QuestIndex> quest_index,
|
||||||
QuestMenuType menu_type,
|
QuestMenuType menu_type,
|
||||||
Episode episode);
|
Episode episode);
|
||||||
void send_lobby_list(std::shared_ptr<Client> c);
|
void send_lobby_list(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void send_player_records(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l, std::shared_ptr<Client> joining_client = nullptr);
|
void send_player_records(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l, std::shared_ptr<Client> joining_client = nullptr);
|
||||||
void send_join_lobby(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l);
|
void send_join_lobby(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l);
|
||||||
void send_update_lobby_data_bb(std::shared_ptr<Client> c);
|
void send_update_lobby_data_bb(std::shared_ptr<Client> c);
|
||||||
void send_player_join_notification(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l, std::shared_ptr<Client> joining_client);
|
void send_player_join_notification(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l, std::shared_ptr<Client> joining_client);
|
||||||
void send_player_leave_notification(std::shared_ptr<Lobby> l, uint8_t leaving_client_id);
|
void send_player_leave_notification(std::shared_ptr<Lobby> l, uint8_t leaving_client_id);
|
||||||
void send_self_leave_notification(std::shared_ptr<Client> c);
|
void send_self_leave_notification(std::shared_ptr<Client> c);
|
||||||
void send_get_player_info(std::shared_ptr<Client> c, bool request_extended = false);
|
void send_get_player_info(std::shared_ptr<Client> c, bool request_extended = false);
|
||||||
|
|
||||||
void send_execute_item_trade(std::shared_ptr<Client> c, const std::vector<ItemData>& items);
|
void send_execute_item_trade(std::shared_ptr<Client> c, const std::vector<ItemData>& items);
|
||||||
void send_execute_card_trade(std::shared_ptr<Client> c, const std::vector<std::pair<uint32_t, uint32_t>>& card_to_count);
|
void send_execute_card_trade(std::shared_ptr<Client> c, const std::vector<std::pair<uint32_t, uint32_t>>& card_to_count);
|
||||||
|
|
||||||
void send_arrow_update(std::shared_ptr<Lobby> l);
|
void send_arrow_update(std::shared_ptr<Lobby> l);
|
||||||
void send_unblock_join(std::shared_ptr<Client> c);
|
void send_unblock_join(std::shared_ptr<Client> c);
|
||||||
void send_resume_game(std::shared_ptr<Lobby> l, std::shared_ptr<Client> ready_client);
|
void send_resume_game(std::shared_ptr<Lobby> l, std::shared_ptr<Client> ready_client);
|
||||||
|
|
||||||
enum PlayerStatsChange {
|
enum PlayerStatsChange {
|
||||||
SUBTRACT_HP = 0,
|
SUBTRACT_HP = 0,
|
||||||
SUBTRACT_TP = 1,
|
SUBTRACT_TP = 1,
|
||||||
SUBTRACT_MESETA = 2,
|
SUBTRACT_MESETA = 2,
|
||||||
ADD_HP = 3,
|
ADD_HP = 3,
|
||||||
ADD_TP = 4,
|
ADD_TP = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
void send_player_stats_change(std::shared_ptr<Client> c, PlayerStatsChange stat, uint32_t amount);
|
void send_player_stats_change(std::shared_ptr<Client> c, PlayerStatsChange stat, uint32_t amount);
|
||||||
void send_player_stats_change(Channel& ch, uint16_t client_id, PlayerStatsChange stat, uint32_t amount);
|
void send_player_stats_change(Channel& ch, uint16_t client_id, PlayerStatsChange stat, uint32_t amount);
|
||||||
void send_remove_conditions(std::shared_ptr<Client> c);
|
void send_remove_conditions(std::shared_ptr<Client> c);
|
||||||
void send_remove_conditions(Channel& ch, uint16_t client_id);
|
void send_remove_conditions(Channel& ch, uint16_t client_id);
|
||||||
void send_warp(Channel& ch, uint8_t client_id, uint32_t floor, bool is_private);
|
void send_warp(Channel& ch, uint8_t client_id, uint32_t floor, bool is_private);
|
||||||
void send_warp(std::shared_ptr<Client> c, uint32_t floor, bool is_private);
|
void send_warp(std::shared_ptr<Client> c, uint32_t floor, bool is_private);
|
||||||
void send_warp(std::shared_ptr<Lobby> l, uint32_t floor, bool is_private);
|
void send_warp(std::shared_ptr<Lobby> l, uint32_t floor, bool is_private);
|
||||||
|
|
||||||
void send_ep3_change_music(Channel& ch, uint32_t song);
|
void send_ep3_change_music(Channel& ch, uint32_t song);
|
||||||
void send_revive_player(std::shared_ptr<Client> c);
|
void send_revive_player(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void send_game_join_sync_command(
|
void send_game_join_sync_command(
|
||||||
std::shared_ptr<Client> c, const void* data, size_t size, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc);
|
std::shared_ptr<Client> c, const void* data, size_t size, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc);
|
||||||
void send_game_join_sync_command(
|
void send_game_join_sync_command(
|
||||||
std::shared_ptr<Client> c, const std::string& data, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc);
|
std::shared_ptr<Client> c, const std::string& data, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc);
|
||||||
void send_game_join_sync_command_compressed(
|
void send_game_join_sync_command_compressed(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
const void* data,
|
const void* data,
|
||||||
size_t size,
|
size_t size,
|
||||||
size_t decompressed_size,
|
size_t decompressed_size,
|
||||||
uint8_t dc_nte_sc,
|
uint8_t dc_nte_sc,
|
||||||
uint8_t dc_11_2000_sc,
|
uint8_t dc_11_2000_sc,
|
||||||
uint8_t sc);
|
uint8_t sc);
|
||||||
void send_game_item_state(std::shared_ptr<Client> c);
|
void send_game_item_state(std::shared_ptr<Client> c);
|
||||||
void send_game_enemy_state(std::shared_ptr<Client> c);
|
void send_game_enemy_state(std::shared_ptr<Client> c);
|
||||||
void send_game_object_state(std::shared_ptr<Client> c);
|
void send_game_object_state(std::shared_ptr<Client> c);
|
||||||
void send_game_set_state(std::shared_ptr<Client> c);
|
void send_game_set_state(std::shared_ptr<Client> c);
|
||||||
void send_game_flag_state(std::shared_ptr<Client> c);
|
void send_game_flag_state(std::shared_ptr<Client> c);
|
||||||
void send_game_player_state(std::shared_ptr<Client> to_c, std::shared_ptr<Client> from_c, bool apply_overrides);
|
void send_game_player_state(std::shared_ptr<Client> to_c, std::shared_ptr<Client> from_c, bool apply_overrides);
|
||||||
void send_drop_item_to_channel(std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
void send_drop_item_to_channel(std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
||||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id);
|
bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id);
|
||||||
void send_drop_item_to_lobby(std::shared_ptr<Lobby> l, const ItemData& item,
|
void send_drop_item_to_lobby(std::shared_ptr<Lobby> l, const ItemData& item,
|
||||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id);
|
bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id);
|
||||||
void send_drop_stacked_item_to_channel(
|
void send_drop_stacked_item_to_channel(
|
||||||
std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item, uint8_t floor, float x, float z);
|
std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item, uint8_t floor, float x, float z);
|
||||||
void send_drop_stacked_item_to_lobby(
|
void send_drop_stacked_item_to_lobby(
|
||||||
std::shared_ptr<Lobby> l, const ItemData& item, uint8_t floor, float x, float z);
|
std::shared_ptr<Lobby> l, const ItemData& item, uint8_t floor, float x, float z);
|
||||||
void send_pick_up_item_to_client(std::shared_ptr<Client> c, uint8_t client_id, uint32_t id, uint8_t floor);
|
void send_pick_up_item_to_client(std::shared_ptr<Client> c, uint8_t client_id, uint32_t id, uint8_t floor);
|
||||||
void send_create_inventory_item_to_client(std::shared_ptr<Client> c, uint8_t client_id, const ItemData& item);
|
void send_create_inventory_item_to_client(std::shared_ptr<Client> c, uint8_t client_id, const ItemData& item);
|
||||||
void send_create_inventory_item_to_lobby(std::shared_ptr<Client> c, uint8_t client_id, const ItemData& item, bool exclude_c = false);
|
void send_create_inventory_item_to_lobby(std::shared_ptr<Client> c, uint8_t client_id, const ItemData& item, bool exclude_c = false);
|
||||||
void send_destroy_item_to_lobby(std::shared_ptr<Client> c, uint32_t item_id, uint32_t amount, bool exclude_c = false);
|
void send_destroy_item_to_lobby(std::shared_ptr<Client> c, uint32_t item_id, uint32_t amount, bool exclude_c = false);
|
||||||
void send_destroy_floor_item_to_client(std::shared_ptr<Client> c, uint32_t item_id, uint32_t floor);
|
void send_destroy_floor_item_to_client(std::shared_ptr<Client> c, uint32_t item_id, uint32_t floor);
|
||||||
void send_item_identify_result(std::shared_ptr<Client> c);
|
void send_item_identify_result(std::shared_ptr<Client> c);
|
||||||
void send_bank(std::shared_ptr<Client> c);
|
void send_bank(std::shared_ptr<Client> c);
|
||||||
void send_shop(std::shared_ptr<Client> c, uint8_t shop_type);
|
void send_shop(std::shared_ptr<Client> c, uint8_t shop_type);
|
||||||
void send_level_up(std::shared_ptr<Client> c);
|
void send_level_up(std::shared_ptr<Client> c);
|
||||||
void send_give_experience(std::shared_ptr<Client> c, uint32_t amount);
|
void send_give_experience(std::shared_ptr<Client> c, uint32_t amount);
|
||||||
void send_set_exp_multiplier(std::shared_ptr<Lobby> l);
|
void send_set_exp_multiplier(std::shared_ptr<Lobby> l);
|
||||||
void send_rare_enemy_index_list(std::shared_ptr<Client> c, const std::vector<size_t>& indexes);
|
void send_rare_enemy_index_list(std::shared_ptr<Client> c, const std::vector<size_t>& indexes);
|
||||||
|
|
||||||
void send_quest_function_call(Channel& ch, uint16_t function_id);
|
void send_quest_function_call(Channel& ch, uint16_t function_id);
|
||||||
void send_quest_function_call(std::shared_ptr<Client> c, uint16_t function_id);
|
void send_quest_function_call(std::shared_ptr<Client> c, uint16_t function_id);
|
||||||
|
|
||||||
void send_ep3_card_list_update(std::shared_ptr<Client> c);
|
void send_ep3_card_list_update(std::shared_ptr<Client> c);
|
||||||
void send_ep3_media_update(
|
void send_ep3_media_update(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
uint32_t type,
|
uint32_t type,
|
||||||
uint32_t which,
|
uint32_t which,
|
||||||
const std::string& compressed_data);
|
const std::string& compressed_data);
|
||||||
void send_ep3_rank_update(std::shared_ptr<Client> c);
|
void send_ep3_rank_update(std::shared_ptr<Client> c);
|
||||||
void send_ep3_card_battle_table_state(std::shared_ptr<Lobby> l, uint16_t table_number);
|
void send_ep3_card_battle_table_state(std::shared_ptr<Lobby> l, uint16_t table_number);
|
||||||
void send_ep3_set_context_token(std::shared_ptr<Client> c, uint32_t context_token);
|
void send_ep3_set_context_token(std::shared_ptr<Client> c, uint32_t context_token);
|
||||||
|
|
||||||
void send_ep3_confirm_tournament_entry(
|
void send_ep3_confirm_tournament_entry(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<const Episode3::Tournament> t);
|
std::shared_ptr<const Episode3::Tournament> t);
|
||||||
void send_ep3_tournament_list(
|
void send_ep3_tournament_list(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
bool is_for_spectator_team_create);
|
bool is_for_spectator_team_create);
|
||||||
void send_ep3_tournament_entry_list(
|
void send_ep3_tournament_entry_list(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<const Episode3::Tournament> t,
|
std::shared_ptr<const Episode3::Tournament> t,
|
||||||
bool is_for_spectator_team_create);
|
bool is_for_spectator_team_create);
|
||||||
void send_ep3_tournament_info(
|
void send_ep3_tournament_info(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<const Episode3::Tournament> t);
|
std::shared_ptr<const Episode3::Tournament> t);
|
||||||
void send_ep3_set_tournament_player_decks(std::shared_ptr<Client> c);
|
void send_ep3_set_tournament_player_decks(std::shared_ptr<Client> c);
|
||||||
void send_ep3_tournament_match_result(std::shared_ptr<Lobby> l, uint32_t meseta_reward);
|
void send_ep3_tournament_match_result(std::shared_ptr<Lobby> l, uint32_t meseta_reward);
|
||||||
|
|
||||||
void send_ep3_tournament_details(
|
void send_ep3_tournament_details(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<const Episode3::Tournament> t);
|
std::shared_ptr<const Episode3::Tournament> t);
|
||||||
void send_ep3_game_details(
|
void send_ep3_game_details(
|
||||||
std::shared_ptr<Client> c, std::shared_ptr<Lobby> l);
|
std::shared_ptr<Client> c, std::shared_ptr<Lobby> l);
|
||||||
void send_ep3_update_game_metadata(std::shared_ptr<Lobby> l);
|
void send_ep3_update_game_metadata(std::shared_ptr<Lobby> l);
|
||||||
void send_ep3_card_auction(std::shared_ptr<Lobby> l);
|
void send_ep3_card_auction(std::shared_ptr<Lobby> l);
|
||||||
void send_ep3_disband_watcher_lobbies(std::shared_ptr<Lobby> primary_l);
|
void send_ep3_disband_watcher_lobbies(std::shared_ptr<Lobby> primary_l);
|
||||||
|
|
||||||
// Pass mask_key = 0 to unmask the command
|
// Pass mask_key = 0 to unmask the command
|
||||||
void set_mask_for_ep3_game_command(void* vdata, size_t size, uint8_t mask_key);
|
void set_mask_for_ep3_game_command(void* vdata, size_t size, uint8_t mask_key);
|
||||||
|
|
||||||
enum class QuestFileType {
|
enum class QuestFileType {
|
||||||
ONLINE = 0,
|
ONLINE = 0,
|
||||||
DOWNLOAD_WITHOUT_PVR,
|
DOWNLOAD_WITHOUT_PVR,
|
||||||
DOWNLOAD_WITH_PVR,
|
DOWNLOAD_WITH_PVR,
|
||||||
EPISODE_3,
|
EPISODE_3,
|
||||||
GBA_DEMO,
|
GBA_DEMO,
|
||||||
};
|
};
|
||||||
|
|
||||||
void send_open_quest_file(
|
void send_open_quest_file(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
const std::string& quest_name,
|
const std::string& quest_name,
|
||||||
const std::string& filename,
|
const std::string& filename,
|
||||||
const std::string& xb_filename,
|
const std::string& xb_filename,
|
||||||
uint32_t quest_number,
|
uint32_t quest_number,
|
||||||
QuestFileType type,
|
QuestFileType type,
|
||||||
std::shared_ptr<const std::string> contents);
|
std::shared_ptr<const std::string> contents);
|
||||||
void send_quest_file_chunk(
|
void send_quest_file_chunk(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
const std::string& filename,
|
const std::string& filename,
|
||||||
size_t chunk_index,
|
size_t chunk_index,
|
||||||
const void* data,
|
const void* data,
|
||||||
size_t size,
|
size_t size,
|
||||||
bool is_download_quest);
|
bool is_download_quest);
|
||||||
bool send_quest_barrier_if_all_clients_ready(std::shared_ptr<Lobby> l);
|
bool send_quest_barrier_if_all_clients_ready(std::shared_ptr<Lobby> l);
|
||||||
bool send_ep3_start_tournament_deck_select_if_all_clients_ready(std::shared_ptr<Lobby> l);
|
bool send_ep3_start_tournament_deck_select_if_all_clients_ready(std::shared_ptr<Lobby> l);
|
||||||
|
|
||||||
void send_server_time(std::shared_ptr<Client> c);
|
void send_server_time(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
void send_change_event(std::shared_ptr<Client> c, uint8_t new_event);
|
void send_change_event(std::shared_ptr<Client> c, uint8_t new_event);
|
||||||
void send_change_event(std::shared_ptr<Lobby> l, uint8_t new_event);
|
void send_change_event(std::shared_ptr<Lobby> l, uint8_t new_event);
|
||||||
void send_change_event(std::shared_ptr<ServerState> s, uint8_t new_event);
|
void send_change_event(std::shared_ptr<ServerState> s, uint8_t new_event);
|
||||||
|
|
||||||
void send_team_membership_info(std::shared_ptr<Client> c); // 12EA
|
void send_team_membership_info(std::shared_ptr<Client> c); // 12EA
|
||||||
void send_update_team_metadata_for_client(std::shared_ptr<Client> c); // 15EA (to all clients in lobby, with only c's data)
|
void send_update_team_metadata_for_client(std::shared_ptr<Client> c); // 15EA (to all clients in lobby, with only c's data)
|
||||||
void send_all_nearby_team_metadatas_to_client(std::shared_ptr<Client> c, bool is_13EA); // 13EA/15EA (to only c, with all lobby clients' data)
|
void send_all_nearby_team_metadatas_to_client(std::shared_ptr<Client> c, bool is_13EA); // 13EA/15EA (to only c, with all lobby clients' data)
|
||||||
void send_update_team_reward_flags(std::shared_ptr<Client> c); // 1DEA
|
void send_update_team_reward_flags(std::shared_ptr<Client> c); // 1DEA
|
||||||
void send_team_member_list(std::shared_ptr<Client> c); // 09EA
|
void send_team_member_list(std::shared_ptr<Client> c); // 09EA
|
||||||
void send_intra_team_ranking(std::shared_ptr<Client> c); // 18EA
|
void send_intra_team_ranking(std::shared_ptr<Client> c); // 18EA
|
||||||
void send_team_reward_list(std::shared_ptr<Client> c, bool show_purchased); // 19EA, 1AEA
|
void send_team_reward_list(std::shared_ptr<Client> c, bool show_purchased); // 19EA, 1AEA
|
||||||
void send_cross_team_ranking(std::shared_ptr<Client> c); // 1CEA
|
void send_cross_team_ranking(std::shared_ptr<Client> c); // 1CEA
|
||||||
|
|||||||
+353
-353
@@ -1,353 +1,353 @@
|
|||||||
#include "Server.hh"
|
#include "Server.hh"
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#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 <event2/listener.h>
|
#include <event2/listener.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#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 <phosg/Time.hh>
|
#include <phosg/Time.hh>
|
||||||
|
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "PSOProtocol.hh"
|
#include "PSOProtocol.hh"
|
||||||
#include "ReceiveCommands.hh"
|
#include "ReceiveCommands.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
void Server::disconnect_client(shared_ptr<Client> c) {
|
void Server::disconnect_client(shared_ptr<Client> c) {
|
||||||
if (c->channel.virtual_network_id) {
|
if (c->channel.virtual_network_id) {
|
||||||
server_log.info("Client disconnected: C-%" PRIX64 " on N-%" PRIu64, c->id, c->channel.virtual_network_id);
|
server_log.info("Client disconnected: C-%" PRIX64 " on N-%" PRIu64, c->id, c->channel.virtual_network_id);
|
||||||
} else if (c->channel.bev) {
|
} else if (c->channel.bev) {
|
||||||
server_log.info("Client disconnected: C-%" PRIX64, c->id);
|
server_log.info("Client disconnected: C-%" PRIX64, c->id);
|
||||||
} else {
|
} else {
|
||||||
server_log.info("Client C-%" PRIX64 " removed from game server", c->id);
|
server_log.info("Client C-%" PRIX64 " removed from game server", c->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->state->channel_to_client.erase(&c->channel);
|
this->state->channel_to_client.erase(&c->channel);
|
||||||
c->channel.disconnect();
|
c->channel.disconnect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
on_disconnect(c);
|
on_disconnect(c);
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
server_log.warning("Error during client disconnect cleanup: %s", e.what());
|
server_log.warning("Error during client disconnect cleanup: %s", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't just let c be destroyed here, since disconnect_client can be
|
// We can't just let c be destroyed here, since disconnect_client can be
|
||||||
// called from within the client's channel's receive handler. So, we instead
|
// called from within the client's channel's receive handler. So, we instead
|
||||||
// move it to another set, which we'll clear in an immediately-enqueued
|
// move it to another set, which we'll clear in an immediately-enqueued
|
||||||
// callback after the current event. This will also call the client's
|
// callback after the current event. This will also call the client's
|
||||||
// disconnect hooks (if any).
|
// disconnect hooks (if any).
|
||||||
this->clients_to_destroy.insert(std::move(c));
|
this->clients_to_destroy.insert(std::move(c));
|
||||||
this->enqueue_destroy_clients();
|
this->enqueue_destroy_clients();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::enqueue_destroy_clients() {
|
void Server::enqueue_destroy_clients() {
|
||||||
auto tv = usecs_to_timeval(0);
|
auto tv = usecs_to_timeval(0);
|
||||||
event_add(this->destroy_clients_ev.get(), &tv);
|
event_add(this->destroy_clients_ev.get(), &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::dispatch_destroy_clients(evutil_socket_t, short, void* ctx) {
|
void Server::dispatch_destroy_clients(evutil_socket_t, short, void* ctx) {
|
||||||
reinterpret_cast<Server*>(ctx)->destroy_clients();
|
reinterpret_cast<Server*>(ctx)->destroy_clients();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::destroy_clients() {
|
void Server::destroy_clients() {
|
||||||
for (auto c_it = this->clients_to_destroy.begin();
|
for (auto c_it = this->clients_to_destroy.begin();
|
||||||
c_it != this->clients_to_destroy.end();
|
c_it != this->clients_to_destroy.end();
|
||||||
c_it = this->clients_to_destroy.erase(c_it)) {
|
c_it = this->clients_to_destroy.erase(c_it)) {
|
||||||
auto c = *c_it;
|
auto c = *c_it;
|
||||||
// Note: It's important to move the disconnect hooks out of the client here
|
// Note: It's important to move the disconnect hooks out of the client here
|
||||||
// because the hooks could modify c->disconnect_hooks while it's being
|
// because the hooks could modify c->disconnect_hooks while it's being
|
||||||
// iterated here, which would invalidate these iterators.
|
// iterated here, which would invalidate these iterators.
|
||||||
unordered_map<string, function<void()>> hooks = std::move(c->disconnect_hooks);
|
unordered_map<string, function<void()>> hooks = std::move(c->disconnect_hooks);
|
||||||
for (auto h_it : hooks) {
|
for (auto h_it : hooks) {
|
||||||
try {
|
try {
|
||||||
h_it.second();
|
h_it.second();
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
c->log.warning("Disconnect hook %s failed: %s", h_it.first.c_str(), e.what());
|
c->log.warning("Disconnect hook %s failed: %s", h_it.first.c_str(), e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::dispatch_on_listen_accept(
|
void Server::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<Server*>(ctx)->on_listen_accept(listener, fd, address, socklen);
|
reinterpret_cast<Server*>(ctx)->on_listen_accept(listener, fd, address, socklen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::dispatch_on_listen_error(struct evconnlistener* listener, void* ctx) {
|
void Server::dispatch_on_listen_error(struct evconnlistener* listener, void* ctx) {
|
||||||
reinterpret_cast<Server*>(ctx)->on_listen_error(listener);
|
reinterpret_cast<Server*>(ctx)->on_listen_error(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr*, int) {
|
void Server::on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr*, int) {
|
||||||
struct sockaddr_storage remote_addr;
|
struct sockaddr_storage remote_addr;
|
||||||
get_socket_addresses(fd, nullptr, &remote_addr);
|
get_socket_addresses(fd, nullptr, &remote_addr);
|
||||||
if (this->state->banned_ipv4_ranges->check(remote_addr)) {
|
if (this->state->banned_ipv4_ranges->check(remote_addr)) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int listen_fd = evconnlistener_get_fd(listener);
|
int listen_fd = evconnlistener_get_fd(listener);
|
||||||
ListeningSocket* listening_socket;
|
ListeningSocket* listening_socket;
|
||||||
try {
|
try {
|
||||||
listening_socket = &this->listening_sockets.at(listen_fd);
|
listening_socket = &this->listening_sockets.at(listen_fd);
|
||||||
} catch (const out_of_range& e) {
|
} catch (const out_of_range& e) {
|
||||||
server_log.warning("Can\'t determine version for socket %d; disconnecting client",
|
server_log.warning("Can\'t determine version for socket %d; disconnecting client",
|
||||||
listen_fd);
|
listen_fd);
|
||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||||
auto c = make_shared<Client>(this->shared_from_this(), bev, 0, listening_socket->version, listening_socket->behavior);
|
auto c = make_shared<Client>(this->shared_from_this(), bev, 0, listening_socket->version, listening_socket->behavior);
|
||||||
c->channel.on_command_received = Server::on_client_input;
|
c->channel.on_command_received = Server::on_client_input;
|
||||||
c->channel.on_error = Server::on_client_error;
|
c->channel.on_error = Server::on_client_error;
|
||||||
c->channel.context_obj = this;
|
c->channel.context_obj = this;
|
||||||
this->state->channel_to_client.emplace(&c->channel, c);
|
this->state->channel_to_client.emplace(&c->channel, c);
|
||||||
|
|
||||||
server_log.info("Client connected: C-%" PRIX64 " on fd %d via %d (%s)",
|
server_log.info("Client connected: C-%" PRIX64 " on fd %d via %d (%s)",
|
||||||
c->id, fd, listen_fd, listening_socket->addr_str.c_str());
|
c->id, fd, listen_fd, listening_socket->addr_str.c_str());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
on_connect(c);
|
on_connect(c);
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
server_log.warning("Error during client initialization: %s", e.what());
|
server_log.warning("Error during client initialization: %s", e.what());
|
||||||
this->disconnect_client(c);
|
this->disconnect_client(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::connect_virtual_client(
|
void Server::connect_virtual_client(
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
uint64_t virtual_network_id,
|
uint64_t virtual_network_id,
|
||||||
uint32_t address,
|
uint32_t address,
|
||||||
uint16_t client_port,
|
uint16_t client_port,
|
||||||
uint16_t server_port,
|
uint16_t server_port,
|
||||||
Version version,
|
Version version,
|
||||||
ServerBehavior initial_state) {
|
ServerBehavior initial_state) {
|
||||||
auto c = make_shared<Client>(this->shared_from_this(), bev, virtual_network_id, version, initial_state);
|
auto c = make_shared<Client>(this->shared_from_this(), bev, virtual_network_id, version, initial_state);
|
||||||
c->channel.on_command_received = Server::on_client_input;
|
c->channel.on_command_received = Server::on_client_input;
|
||||||
c->channel.on_error = Server::on_client_error;
|
c->channel.on_error = Server::on_client_error;
|
||||||
c->channel.context_obj = this;
|
c->channel.context_obj = this;
|
||||||
this->state->channel_to_client.emplace(&c->channel, c);
|
this->state->channel_to_client.emplace(&c->channel, c);
|
||||||
|
|
||||||
server_log.info(
|
server_log.info(
|
||||||
"Client connected: C-%" PRIX64 " on virtual network N-%" PRIu64 " via T-%hu-%s-%s-VI",
|
"Client connected: C-%" PRIX64 " on virtual network N-%" PRIu64 " via T-%hu-%s-%s-VI",
|
||||||
c->id,
|
c->id,
|
||||||
virtual_network_id,
|
virtual_network_id,
|
||||||
server_port,
|
server_port,
|
||||||
name_for_enum(version),
|
name_for_enum(version),
|
||||||
name_for_enum(initial_state));
|
name_for_enum(initial_state));
|
||||||
|
|
||||||
// Manually set the remote address, since the bufferevent has no fd and the
|
// Manually set the remote address, since the bufferevent has no fd and the
|
||||||
// Channel constructor can't figure out the virtual remote address
|
// Channel constructor can't figure out the virtual remote address
|
||||||
auto* remote_sin = reinterpret_cast<sockaddr_in*>(&c->channel.remote_addr);
|
auto* remote_sin = reinterpret_cast<sockaddr_in*>(&c->channel.remote_addr);
|
||||||
remote_sin->sin_family = AF_INET;
|
remote_sin->sin_family = AF_INET;
|
||||||
remote_sin->sin_addr.s_addr = htonl(address);
|
remote_sin->sin_addr.s_addr = htonl(address);
|
||||||
remote_sin->sin_port = htons(client_port);
|
remote_sin->sin_port = htons(client_port);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
on_connect(c);
|
on_connect(c);
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
server_log.error("Error during client initialization: %s", e.what());
|
server_log.error("Error during client initialization: %s", e.what());
|
||||||
this->disconnect_client(c);
|
this->disconnect_client(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::connect_virtual_client(shared_ptr<Client> c, Channel&& ch) {
|
void Server::connect_virtual_client(shared_ptr<Client> c, Channel&& ch) {
|
||||||
c->channel.replace_with(std::move(ch), Server::on_client_input, Server::on_client_error, this, string_printf("C-%" PRIX64, c->id));
|
c->channel.replace_with(std::move(ch), Server::on_client_input, Server::on_client_error, this, string_printf("C-%" PRIX64, c->id));
|
||||||
this->state->channel_to_client.emplace(&c->channel, c);
|
this->state->channel_to_client.emplace(&c->channel, c);
|
||||||
server_log.info("Client C-%" PRIX64 " added to game server", c->id);
|
server_log.info("Client C-%" PRIX64 " added to game server", c->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::on_listen_error(struct evconnlistener* listener) {
|
void Server::on_listen_error(struct evconnlistener* listener) {
|
||||||
int err = EVUTIL_SOCKET_ERROR();
|
int err = EVUTIL_SOCKET_ERROR();
|
||||||
server_log.error("Failure on listening socket %d: %d (%s)",
|
server_log.error("Failure on listening socket %d: %d (%s)",
|
||||||
evconnlistener_get_fd(listener), err, evutil_socket_error_to_string(err));
|
evconnlistener_get_fd(listener), err, evutil_socket_error_to_string(err));
|
||||||
event_base_loopexit(this->base.get(), nullptr);
|
event_base_loopexit(this->base.get(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data) {
|
void Server::on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data) {
|
||||||
Server* server = reinterpret_cast<Server*>(ch.context_obj);
|
Server* server = reinterpret_cast<Server*>(ch.context_obj);
|
||||||
shared_ptr<Client> c = server->state->channel_to_client.at(&ch);
|
shared_ptr<Client> c = server->state->channel_to_client.at(&ch);
|
||||||
|
|
||||||
if (c->should_disconnect) {
|
if (c->should_disconnect) {
|
||||||
server->disconnect_client(c);
|
server->disconnect_client(c);
|
||||||
} else {
|
} else {
|
||||||
if (server->state->catch_handler_exceptions) {
|
if (server->state->catch_handler_exceptions) {
|
||||||
try {
|
try {
|
||||||
on_command(c, command, flag, data);
|
on_command(c, command, flag, data);
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
server_log.warning("Error processing client command: %s", e.what());
|
server_log.warning("Error processing client command: %s", e.what());
|
||||||
c->should_disconnect = true;
|
c->should_disconnect = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
on_command(c, command, flag, data);
|
on_command(c, command, flag, data);
|
||||||
}
|
}
|
||||||
if (c->should_disconnect) {
|
if (c->should_disconnect) {
|
||||||
server->disconnect_client(c);
|
server->disconnect_client(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::on_client_error(Channel& ch, short events) {
|
void Server::on_client_error(Channel& ch, short events) {
|
||||||
Server* server = reinterpret_cast<Server*>(ch.context_obj);
|
Server* server = reinterpret_cast<Server*>(ch.context_obj);
|
||||||
shared_ptr<Client> c = server->state->channel_to_client.at(&ch);
|
shared_ptr<Client> c = server->state->channel_to_client.at(&ch);
|
||||||
|
|
||||||
if (events & BEV_EVENT_ERROR) {
|
if (events & BEV_EVENT_ERROR) {
|
||||||
int err = EVUTIL_SOCKET_ERROR();
|
int err = EVUTIL_SOCKET_ERROR();
|
||||||
server_log.warning("Client caused error %d (%s)", err,
|
server_log.warning("Client caused error %d (%s)", err,
|
||||||
evutil_socket_error_to_string(err));
|
evutil_socket_error_to_string(err));
|
||||||
}
|
}
|
||||||
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
|
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
|
||||||
server->disconnect_client(c);
|
server->disconnect_client(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::Server(
|
Server::Server(
|
||||||
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_clients_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &Server::dispatch_destroy_clients, this), event_free),
|
destroy_clients_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &Server::dispatch_destroy_clients, this), event_free),
|
||||||
state(state) {}
|
state(state) {}
|
||||||
|
|
||||||
void Server::listen(const std::string& addr_str, const string& socket_path, Version version, ServerBehavior behavior) {
|
void Server::listen(const std::string& addr_str, const string& socket_path, Version version, ServerBehavior behavior) {
|
||||||
int fd = ::listen(socket_path, 0, SOMAXCONN);
|
int fd = ::listen(socket_path, 0, SOMAXCONN);
|
||||||
server_log.info("Listening on Unix socket %s on fd %d as %s",
|
server_log.info("Listening on Unix socket %s on fd %d as %s",
|
||||||
socket_path.c_str(), fd, addr_str.c_str());
|
socket_path.c_str(), fd, addr_str.c_str());
|
||||||
this->add_socket(addr_str, fd, version, behavior);
|
this->add_socket(addr_str, fd, version, behavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::listen(
|
void Server::listen(
|
||||||
const std::string& addr_str,
|
const std::string& addr_str,
|
||||||
const string& addr,
|
const string& addr,
|
||||||
int port,
|
int port,
|
||||||
Version version,
|
Version version,
|
||||||
ServerBehavior behavior) {
|
ServerBehavior behavior) {
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
this->listen(addr_str, addr, version, behavior);
|
this->listen(addr_str, addr, version, behavior);
|
||||||
} else {
|
} else {
|
||||||
int fd = ::listen(addr, port, SOMAXCONN);
|
int fd = ::listen(addr, port, SOMAXCONN);
|
||||||
string netloc_str = render_netloc(addr, port);
|
string netloc_str = render_netloc(addr, port);
|
||||||
server_log.info("Listening on TCP interface %s on fd %d as %s",
|
server_log.info("Listening on TCP interface %s on fd %d as %s",
|
||||||
netloc_str.c_str(), fd, addr_str.c_str());
|
netloc_str.c_str(), fd, addr_str.c_str());
|
||||||
this->add_socket(addr_str, fd, version, behavior);
|
this->add_socket(addr_str, fd, version, behavior);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::listen(const std::string& addr_str, int port, Version version, ServerBehavior behavior) {
|
void Server::listen(const std::string& addr_str, int port, Version version, ServerBehavior behavior) {
|
||||||
this->listen(addr_str, "", port, version, behavior);
|
this->listen(addr_str, "", port, version, behavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::ListeningSocket::ListeningSocket(
|
Server::ListeningSocket::ListeningSocket(
|
||||||
Server* s, const std::string& addr_str,
|
Server* s, const std::string& addr_str,
|
||||||
int fd, Version version, ServerBehavior behavior)
|
int fd, Version version, ServerBehavior behavior)
|
||||||
: addr_str(addr_str),
|
: addr_str(addr_str),
|
||||||
fd(fd),
|
fd(fd),
|
||||||
version(version),
|
version(version),
|
||||||
behavior(behavior),
|
behavior(behavior),
|
||||||
listener(evconnlistener_new(
|
listener(evconnlistener_new(
|
||||||
s->base.get(), Server::dispatch_on_listen_accept, s,
|
s->base.get(), Server::dispatch_on_listen_accept, s,
|
||||||
LEV_OPT_REUSEABLE, 0, this->fd),
|
LEV_OPT_REUSEABLE, 0, this->fd),
|
||||||
evconnlistener_free) {
|
evconnlistener_free) {
|
||||||
evconnlistener_set_error_cb(
|
evconnlistener_set_error_cb(
|
||||||
this->listener.get(),
|
this->listener.get(),
|
||||||
Server::dispatch_on_listen_error);
|
Server::dispatch_on_listen_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::add_socket(
|
void Server::add_socket(
|
||||||
const std::string& addr_str,
|
const std::string& addr_str,
|
||||||
int fd,
|
int fd,
|
||||||
Version version,
|
Version version,
|
||||||
ServerBehavior behavior) {
|
ServerBehavior behavior) {
|
||||||
this->listening_sockets.emplace(
|
this->listening_sockets.emplace(
|
||||||
piecewise_construct, forward_as_tuple(fd),
|
piecewise_construct, forward_as_tuple(fd),
|
||||||
forward_as_tuple(this, addr_str, fd, version, behavior));
|
forward_as_tuple(this, addr_str, fd, version, behavior));
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<Client> Server::get_client() const {
|
shared_ptr<Client> Server::get_client() const {
|
||||||
if (this->state->channel_to_client.empty()) {
|
if (this->state->channel_to_client.empty()) {
|
||||||
throw runtime_error("no clients on game server");
|
throw runtime_error("no clients on game server");
|
||||||
}
|
}
|
||||||
if (this->state->channel_to_client.size() > 1) {
|
if (this->state->channel_to_client.size() > 1) {
|
||||||
throw runtime_error("multiple clients on game server");
|
throw runtime_error("multiple clients on game server");
|
||||||
}
|
}
|
||||||
return this->state->channel_to_client.begin()->second;
|
return this->state->channel_to_client.begin()->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<shared_ptr<Client>> Server::get_clients_by_identifier(const string& ident) const {
|
vector<shared_ptr<Client>> Server::get_clients_by_identifier(const string& ident) const {
|
||||||
int64_t account_id_hex = -1;
|
int64_t account_id_hex = -1;
|
||||||
int64_t account_id_dec = -1;
|
int64_t account_id_dec = -1;
|
||||||
try {
|
try {
|
||||||
account_id_dec = stoul(ident, nullptr, 10);
|
account_id_dec = stoul(ident, nullptr, 10);
|
||||||
} catch (const invalid_argument&) {
|
} catch (const invalid_argument&) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
account_id_hex = stoul(ident, nullptr, 16);
|
account_id_hex = stoul(ident, nullptr, 16);
|
||||||
} catch (const invalid_argument&) {
|
} catch (const invalid_argument&) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: It's kind of not great that we do a linear search here, but this is
|
// TODO: It's kind of not great that we do a linear search here, but this is
|
||||||
// only used in the shell, so it should be pretty rare.
|
// only used in the shell, so it should be pretty rare.
|
||||||
vector<shared_ptr<Client>> results;
|
vector<shared_ptr<Client>> results;
|
||||||
for (const auto& it : this->state->channel_to_client) {
|
for (const auto& it : this->state->channel_to_client) {
|
||||||
auto c = it.second;
|
auto c = it.second;
|
||||||
if (c->login && c->login->account->account_id == account_id_hex) {
|
if (c->login && c->login->account->account_id == account_id_hex) {
|
||||||
results.emplace_back(c);
|
results.emplace_back(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c->login && c->login->account->account_id == account_id_dec) {
|
if (c->login && c->login->account->account_id == account_id_dec) {
|
||||||
results.emplace_back(c);
|
results.emplace_back(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c->login && c->login->xb_license && c->login->xb_license->gamertag == ident) {
|
if (c->login && c->login->xb_license && c->login->xb_license->gamertag == ident) {
|
||||||
results.emplace_back(c);
|
results.emplace_back(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c->login && c->login->bb_license && c->login->bb_license->username == ident) {
|
if (c->login && c->login->bb_license && c->login->bb_license->username == ident) {
|
||||||
results.emplace_back(c);
|
results.emplace_back(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto p = c->character(false, false);
|
auto p = c->character(false, false);
|
||||||
if (p && p->disp.name.eq(ident, p->inventory.language)) {
|
if (p && p->disp.name.eq(ident, p->inventory.language)) {
|
||||||
results.emplace_back(c);
|
results.emplace_back(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->channel.name == ident) {
|
if (c->channel.name == ident) {
|
||||||
results.emplace_back(c);
|
results.emplace_back(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (starts_with(c->channel.name, ident + " ")) {
|
if (starts_with(c->channel.name, ident + " ")) {
|
||||||
results.emplace_back(c);
|
results.emplace_back(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<shared_ptr<Client>> Server::all_clients() const {
|
vector<shared_ptr<Client>> Server::all_clients() const {
|
||||||
vector<shared_ptr<Client>> ret;
|
vector<shared_ptr<Client>> ret;
|
||||||
for (const auto& it : this->state->channel_to_client) {
|
for (const auto& it : this->state->channel_to_client) {
|
||||||
ret.emplace_back(it.second);
|
ret.emplace_back(it.second);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<struct event_base> Server::get_base() const {
|
shared_ptr<struct event_base> Server::get_base() const {
|
||||||
return this->base;
|
return this->base;
|
||||||
}
|
}
|
||||||
|
|||||||
+82
-82
@@ -1,82 +1,82 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
class Server : public std::enable_shared_from_this<Server> {
|
class Server : public std::enable_shared_from_this<Server> {
|
||||||
public:
|
public:
|
||||||
Server() = delete;
|
Server() = delete;
|
||||||
Server(const Server&) = delete;
|
Server(const Server&) = delete;
|
||||||
Server(Server&&) = delete;
|
Server(Server&&) = delete;
|
||||||
Server(std::shared_ptr<struct event_base> base, std::shared_ptr<ServerState> state);
|
Server(std::shared_ptr<struct event_base> base, std::shared_ptr<ServerState> state);
|
||||||
virtual ~Server() = default;
|
virtual ~Server() = default;
|
||||||
|
|
||||||
void listen(const std::string& addr_str, const std::string& socket_path, Version version, ServerBehavior initial_state);
|
void listen(const std::string& addr_str, const std::string& socket_path, Version version, ServerBehavior initial_state);
|
||||||
void listen(const std::string& addr_str, const std::string& addr, int port, Version version, ServerBehavior initial_state);
|
void listen(const std::string& addr_str, const std::string& addr, int port, Version version, ServerBehavior initial_state);
|
||||||
void listen(const std::string& addr_str, int port, Version version, ServerBehavior initial_state);
|
void listen(const std::string& addr_str, int port, Version version, ServerBehavior initial_state);
|
||||||
void add_socket(const std::string& addr_str, int fd, Version version, ServerBehavior initial_state);
|
void add_socket(const std::string& addr_str, int fd, Version version, ServerBehavior initial_state);
|
||||||
|
|
||||||
void connect_virtual_client(
|
void connect_virtual_client(
|
||||||
struct bufferevent* bev,
|
struct bufferevent* bev,
|
||||||
uint64_t virtual_network_id,
|
uint64_t virtual_network_id,
|
||||||
uint32_t address,
|
uint32_t address,
|
||||||
uint16_t client_port,
|
uint16_t client_port,
|
||||||
uint16_t server_port,
|
uint16_t server_port,
|
||||||
Version version,
|
Version version,
|
||||||
ServerBehavior initial_state);
|
ServerBehavior initial_state);
|
||||||
void connect_virtual_client(std::shared_ptr<Client> c, Channel&& ch);
|
void connect_virtual_client(std::shared_ptr<Client> c, Channel&& ch);
|
||||||
void disconnect_client(std::shared_ptr<Client> c);
|
void disconnect_client(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
std::shared_ptr<Client> get_client() const;
|
std::shared_ptr<Client> get_client() const;
|
||||||
std::vector<std::shared_ptr<Client>> get_clients_by_identifier(const std::string& ident) const;
|
std::vector<std::shared_ptr<Client>> get_clients_by_identifier(const std::string& ident) const;
|
||||||
std::vector<std::shared_ptr<Client>> all_clients() const;
|
std::vector<std::shared_ptr<Client>> all_clients() const;
|
||||||
std::shared_ptr<struct event_base> get_base() const;
|
std::shared_ptr<struct event_base> get_base() const;
|
||||||
|
|
||||||
inline std::shared_ptr<ServerState> get_state() const {
|
inline std::shared_ptr<ServerState> get_state() const {
|
||||||
return this->state;
|
return this->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
std::shared_ptr<struct event> destroy_clients_ev;
|
std::shared_ptr<struct event> destroy_clients_ev;
|
||||||
|
|
||||||
struct ListeningSocket {
|
struct ListeningSocket {
|
||||||
std::string addr_str;
|
std::string addr_str;
|
||||||
int fd;
|
int fd;
|
||||||
Version version;
|
Version version;
|
||||||
ServerBehavior behavior;
|
ServerBehavior behavior;
|
||||||
std::unique_ptr<struct evconnlistener, void (*)(struct evconnlistener*)> listener;
|
std::unique_ptr<struct evconnlistener, void (*)(struct evconnlistener*)> listener;
|
||||||
|
|
||||||
ListeningSocket(
|
ListeningSocket(
|
||||||
Server* s,
|
Server* s,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
int fd,
|
int fd,
|
||||||
Version version,
|
Version version,
|
||||||
ServerBehavior behavior);
|
ServerBehavior behavior);
|
||||||
};
|
};
|
||||||
std::unordered_map<int, ListeningSocket> listening_sockets;
|
std::unordered_map<int, ListeningSocket> listening_sockets;
|
||||||
std::unordered_set<std::shared_ptr<Client>> clients_to_destroy;
|
std::unordered_set<std::shared_ptr<Client>> clients_to_destroy;
|
||||||
|
|
||||||
std::shared_ptr<ServerState> state;
|
std::shared_ptr<ServerState> state;
|
||||||
|
|
||||||
void enqueue_destroy_clients();
|
void enqueue_destroy_clients();
|
||||||
static void dispatch_destroy_clients(evutil_socket_t, short, void* ctx);
|
static void dispatch_destroy_clients(evutil_socket_t, short, void* ctx);
|
||||||
void destroy_clients();
|
void destroy_clients();
|
||||||
|
|
||||||
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(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* address, int socklen);
|
void on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* address, int socklen);
|
||||||
void on_listen_error(struct evconnlistener* listener);
|
void on_listen_error(struct evconnlistener* listener);
|
||||||
|
|
||||||
static void on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data);
|
static void on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data);
|
||||||
static void on_client_error(Channel& ch, short events);
|
static void on_client_error(Channel& ch, short events);
|
||||||
};
|
};
|
||||||
|
|||||||
+1092
-1092
File diff suppressed because it is too large
Load Diff
+33
-33
@@ -1,33 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include "ProxyServer.hh"
|
#include "ProxyServer.hh"
|
||||||
#include "ServerState.hh"
|
#include "ServerState.hh"
|
||||||
|
|
||||||
class ServerShell : public std::enable_shared_from_this<ServerShell> {
|
class ServerShell : public std::enable_shared_from_this<ServerShell> {
|
||||||
public:
|
public:
|
||||||
class exit_shell : public std::runtime_error {
|
class exit_shell : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
exit_shell();
|
exit_shell();
|
||||||
~exit_shell() = default;
|
~exit_shell() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ServerShell(std::shared_ptr<ServerState> state);
|
explicit ServerShell(std::shared_ptr<ServerState> state);
|
||||||
ServerShell(const ServerShell&) = delete;
|
ServerShell(const ServerShell&) = delete;
|
||||||
ServerShell(ServerShell&&) = delete;
|
ServerShell(ServerShell&&) = delete;
|
||||||
ServerShell& operator=(const ServerShell&) = delete;
|
ServerShell& operator=(const ServerShell&) = delete;
|
||||||
ServerShell& operator=(ServerShell&&) = delete;
|
ServerShell& operator=(ServerShell&&) = delete;
|
||||||
~ServerShell();
|
~ServerShell();
|
||||||
|
|
||||||
std::shared_ptr<ProxyServer::LinkedSession> get_proxy_session(const std::string& name);
|
std::shared_ptr<ProxyServer::LinkedSession> get_proxy_session(const std::string& name);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<ServerState> state;
|
std::shared_ptr<ServerState> state;
|
||||||
std::thread th;
|
std::thread th;
|
||||||
|
|
||||||
void thread_fn();
|
void thread_fn();
|
||||||
};
|
};
|
||||||
|
|||||||
+2005
-2005
File diff suppressed because it is too large
Load Diff
+408
-408
@@ -1,408 +1,408 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/JSON.hh>
|
#include <phosg/JSON.hh>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Account.hh"
|
#include "Account.hh"
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
#include "CommonItemSet.hh"
|
#include "CommonItemSet.hh"
|
||||||
#include "DNSServer.hh"
|
#include "DNSServer.hh"
|
||||||
#include "Episode3/DataIndexes.hh"
|
#include "Episode3/DataIndexes.hh"
|
||||||
#include "Episode3/Tournament.hh"
|
#include "Episode3/Tournament.hh"
|
||||||
#include "EventUtils.hh"
|
#include "EventUtils.hh"
|
||||||
#include "FunctionCompiler.hh"
|
#include "FunctionCompiler.hh"
|
||||||
#include "GSLArchive.hh"
|
#include "GSLArchive.hh"
|
||||||
#include "IPV4RangeSet.hh"
|
#include "IPV4RangeSet.hh"
|
||||||
#include "ItemNameIndex.hh"
|
#include "ItemNameIndex.hh"
|
||||||
#include "ItemParameterTable.hh"
|
#include "ItemParameterTable.hh"
|
||||||
#include "LevelTable.hh"
|
#include "LevelTable.hh"
|
||||||
#include "Lobby.hh"
|
#include "Lobby.hh"
|
||||||
#include "Menu.hh"
|
#include "Menu.hh"
|
||||||
#include "PatchServer.hh"
|
#include "PatchServer.hh"
|
||||||
#include "PlayerFilesManager.hh"
|
#include "PlayerFilesManager.hh"
|
||||||
#include "Quest.hh"
|
#include "Quest.hh"
|
||||||
#include "TeamIndex.hh"
|
#include "TeamIndex.hh"
|
||||||
#include "WordSelectTable.hh"
|
#include "WordSelectTable.hh"
|
||||||
|
|
||||||
// Forward declarations due to reference cycles
|
// Forward declarations due to reference cycles
|
||||||
class ProxyServer;
|
class ProxyServer;
|
||||||
class Server;
|
class Server;
|
||||||
class IPStackSimulator;
|
class IPStackSimulator;
|
||||||
|
|
||||||
struct PortConfiguration {
|
struct PortConfiguration {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string addr; // Blank = listen on all interfaces (default)
|
std::string addr; // Blank = listen on all interfaces (default)
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
Version version;
|
Version version;
|
||||||
ServerBehavior behavior;
|
ServerBehavior behavior;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ServerState : public std::enable_shared_from_this<ServerState> {
|
struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||||
enum class RunShellBehavior {
|
enum class RunShellBehavior {
|
||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
ALWAYS,
|
ALWAYS,
|
||||||
NEVER,
|
NEVER,
|
||||||
};
|
};
|
||||||
enum class BehaviorSwitch {
|
enum class BehaviorSwitch {
|
||||||
OFF = 0,
|
OFF = 0,
|
||||||
OFF_BY_DEFAULT,
|
OFF_BY_DEFAULT,
|
||||||
ON_BY_DEFAULT,
|
ON_BY_DEFAULT,
|
||||||
ON,
|
ON,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool behavior_enabled(BehaviorSwitch b) {
|
static inline bool behavior_enabled(BehaviorSwitch b) {
|
||||||
return (b == BehaviorSwitch::ON_BY_DEFAULT) || (b == BehaviorSwitch::ON);
|
return (b == BehaviorSwitch::ON_BY_DEFAULT) || (b == BehaviorSwitch::ON);
|
||||||
}
|
}
|
||||||
static inline bool behavior_can_be_overridden(BehaviorSwitch b) {
|
static inline bool behavior_can_be_overridden(BehaviorSwitch b) {
|
||||||
return (b == BehaviorSwitch::OFF_BY_DEFAULT) || (b == BehaviorSwitch::ON_BY_DEFAULT);
|
return (b == BehaviorSwitch::OFF_BY_DEFAULT) || (b == BehaviorSwitch::ON_BY_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t creation_time;
|
uint64_t creation_time;
|
||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
|
|
||||||
std::string config_filename;
|
std::string config_filename;
|
||||||
std::shared_ptr<const JSON> config_json;
|
std::shared_ptr<const JSON> config_json;
|
||||||
bool is_replay = false;
|
bool is_replay = false;
|
||||||
bool one_time_config_loaded = false;
|
bool one_time_config_loaded = false;
|
||||||
bool default_lobbies_created = false;
|
bool default_lobbies_created = false;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::unordered_map<std::string, std::shared_ptr<PortConfiguration>> name_to_port_config;
|
std::unordered_map<std::string, std::shared_ptr<PortConfiguration>> name_to_port_config;
|
||||||
std::unordered_map<uint16_t, std::shared_ptr<PortConfiguration>> number_to_port_config;
|
std::unordered_map<uint16_t, std::shared_ptr<PortConfiguration>> number_to_port_config;
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string dns_server_addr;
|
std::string dns_server_addr;
|
||||||
uint16_t dns_server_port = 0;
|
uint16_t dns_server_port = 0;
|
||||||
std::vector<std::string> ip_stack_addresses;
|
std::vector<std::string> ip_stack_addresses;
|
||||||
std::vector<std::string> ppp_stack_addresses;
|
std::vector<std::string> ppp_stack_addresses;
|
||||||
std::vector<std::string> ppp_raw_addresses;
|
std::vector<std::string> ppp_raw_addresses;
|
||||||
std::vector<std::string> http_addresses;
|
std::vector<std::string> http_addresses;
|
||||||
uint64_t client_ping_interval_usecs = 30000000;
|
uint64_t client_ping_interval_usecs = 30000000;
|
||||||
uint64_t client_idle_timeout_usecs = 60000000;
|
uint64_t client_idle_timeout_usecs = 60000000;
|
||||||
uint64_t patch_client_idle_timeout_usecs = 300000000;
|
uint64_t patch_client_idle_timeout_usecs = 300000000;
|
||||||
bool ip_stack_debug = false;
|
bool ip_stack_debug = false;
|
||||||
bool allow_unregistered_users = false;
|
bool allow_unregistered_users = false;
|
||||||
bool allow_pc_nte = false;
|
bool allow_pc_nte = false;
|
||||||
bool use_temp_accounts_for_prototypes = true;
|
bool use_temp_accounts_for_prototypes = true;
|
||||||
bool allow_dc_pc_games = true;
|
bool allow_dc_pc_games = true;
|
||||||
bool allow_gc_xb_games = true;
|
bool allow_gc_xb_games = true;
|
||||||
bool enable_chat_commands = true;
|
bool enable_chat_commands = true;
|
||||||
std::unique_ptr<std::array<uint32_t, NUM_NON_PATCH_VERSIONS>> version_name_colors;
|
std::unique_ptr<std::array<uint32_t, NUM_NON_PATCH_VERSIONS>> version_name_colors;
|
||||||
uint32_t client_customization_name_color = 0x00000000;
|
uint32_t client_customization_name_color = 0x00000000;
|
||||||
uint8_t allowed_drop_modes_v1_v2_normal = 0x1F;
|
uint8_t allowed_drop_modes_v1_v2_normal = 0x1F;
|
||||||
uint8_t allowed_drop_modes_v1_v2_battle = 0x07;
|
uint8_t allowed_drop_modes_v1_v2_battle = 0x07;
|
||||||
uint8_t allowed_drop_modes_v1_v2_challenge = 0x07;
|
uint8_t allowed_drop_modes_v1_v2_challenge = 0x07;
|
||||||
uint8_t allowed_drop_modes_v3_normal = 0x1F;
|
uint8_t allowed_drop_modes_v3_normal = 0x1F;
|
||||||
uint8_t allowed_drop_modes_v3_battle = 0x07;
|
uint8_t allowed_drop_modes_v3_battle = 0x07;
|
||||||
uint8_t allowed_drop_modes_v3_challenge = 0x07;
|
uint8_t allowed_drop_modes_v3_challenge = 0x07;
|
||||||
uint8_t allowed_drop_modes_v4_normal = 0x1D; // CLIENT not allowed
|
uint8_t allowed_drop_modes_v4_normal = 0x1D; // CLIENT not allowed
|
||||||
uint8_t allowed_drop_modes_v4_battle = 0x05;
|
uint8_t allowed_drop_modes_v4_battle = 0x05;
|
||||||
uint8_t allowed_drop_modes_v4_challenge = 0x05;
|
uint8_t allowed_drop_modes_v4_challenge = 0x05;
|
||||||
Lobby::DropMode default_drop_mode_v1_v2_normal = Lobby::DropMode::CLIENT;
|
Lobby::DropMode default_drop_mode_v1_v2_normal = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v1_v2_battle = Lobby::DropMode::CLIENT;
|
Lobby::DropMode default_drop_mode_v1_v2_battle = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v1_v2_challenge = Lobby::DropMode::CLIENT;
|
Lobby::DropMode default_drop_mode_v1_v2_challenge = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v3_normal = Lobby::DropMode::CLIENT;
|
Lobby::DropMode default_drop_mode_v3_normal = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v3_battle = Lobby::DropMode::CLIENT;
|
Lobby::DropMode default_drop_mode_v3_battle = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v3_challenge = Lobby::DropMode::CLIENT;
|
Lobby::DropMode default_drop_mode_v3_challenge = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v4_normal = Lobby::DropMode::SERVER_SHARED;
|
Lobby::DropMode default_drop_mode_v4_normal = Lobby::DropMode::SERVER_SHARED;
|
||||||
Lobby::DropMode default_drop_mode_v4_battle = Lobby::DropMode::SERVER_SHARED;
|
Lobby::DropMode default_drop_mode_v4_battle = Lobby::DropMode::SERVER_SHARED;
|
||||||
Lobby::DropMode default_drop_mode_v4_challenge = Lobby::DropMode::SERVER_SHARED;
|
Lobby::DropMode default_drop_mode_v4_challenge = Lobby::DropMode::SERVER_SHARED;
|
||||||
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v1_v2;
|
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v1_v2;
|
||||||
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v3;
|
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v3;
|
||||||
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v4;
|
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v4;
|
||||||
std::unordered_map<std::string, std::pair<uint8_t, uint32_t>> quest_counter_fields; // For $qfread command
|
std::unordered_map<std::string, std::pair<uint8_t, uint32_t>> quest_counter_fields; // For $qfread command
|
||||||
uint64_t persistent_game_idle_timeout_usecs = 0;
|
uint64_t persistent_game_idle_timeout_usecs = 0;
|
||||||
bool ep3_send_function_call_enabled = false;
|
bool ep3_send_function_call_enabled = false;
|
||||||
bool enable_v3_v4_protected_subcommands = false;
|
bool enable_v3_v4_protected_subcommands = false;
|
||||||
bool catch_handler_exceptions = true;
|
bool catch_handler_exceptions = true;
|
||||||
bool ep3_infinite_meseta = false;
|
bool ep3_infinite_meseta = false;
|
||||||
std::vector<uint32_t> ep3_defeat_player_meseta_rewards = {400, 500, 600, 700, 800};
|
std::vector<uint32_t> ep3_defeat_player_meseta_rewards = {400, 500, 600, 700, 800};
|
||||||
std::vector<uint32_t> ep3_defeat_com_meseta_rewards = {100, 200, 300, 400, 500};
|
std::vector<uint32_t> ep3_defeat_com_meseta_rewards = {100, 200, 300, 400, 500};
|
||||||
uint32_t ep3_final_round_meseta_bonus = 300;
|
uint32_t ep3_final_round_meseta_bonus = 300;
|
||||||
bool ep3_jukebox_is_free = false;
|
bool ep3_jukebox_is_free = false;
|
||||||
uint32_t ep3_behavior_flags = 0;
|
uint32_t ep3_behavior_flags = 0;
|
||||||
bool hide_download_commands = true;
|
bool hide_download_commands = true;
|
||||||
RunShellBehavior run_shell_behavior = RunShellBehavior::DEFAULT;
|
RunShellBehavior run_shell_behavior = RunShellBehavior::DEFAULT;
|
||||||
BehaviorSwitch cheat_mode_behavior = BehaviorSwitch::OFF_BY_DEFAULT;
|
BehaviorSwitch cheat_mode_behavior = BehaviorSwitch::OFF_BY_DEFAULT;
|
||||||
bool use_game_creator_section_id = false;
|
bool use_game_creator_section_id = false;
|
||||||
bool default_rare_notifs_enabled_v1_v2 = false;
|
bool default_rare_notifs_enabled_v1_v2 = false;
|
||||||
bool default_rare_notifs_enabled_v3_v4 = false;
|
bool default_rare_notifs_enabled_v3_v4 = false;
|
||||||
std::unordered_set<uint32_t> notify_game_for_item_primary_identifiers_v1_v2;
|
std::unordered_set<uint32_t> notify_game_for_item_primary_identifiers_v1_v2;
|
||||||
std::unordered_set<uint32_t> notify_game_for_item_primary_identifiers_v3;
|
std::unordered_set<uint32_t> notify_game_for_item_primary_identifiers_v3;
|
||||||
std::unordered_set<uint32_t> notify_game_for_item_primary_identifiers_v4;
|
std::unordered_set<uint32_t> notify_game_for_item_primary_identifiers_v4;
|
||||||
std::unordered_set<uint32_t> notify_server_for_item_primary_identifiers_v1_v2;
|
std::unordered_set<uint32_t> notify_server_for_item_primary_identifiers_v1_v2;
|
||||||
std::unordered_set<uint32_t> notify_server_for_item_primary_identifiers_v3;
|
std::unordered_set<uint32_t> notify_server_for_item_primary_identifiers_v3;
|
||||||
std::unordered_set<uint32_t> notify_server_for_item_primary_identifiers_v4;
|
std::unordered_set<uint32_t> notify_server_for_item_primary_identifiers_v4;
|
||||||
bool notify_server_for_max_level_achieved = false;
|
bool notify_server_for_max_level_achieved = false;
|
||||||
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> bb_private_keys;
|
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> bb_private_keys;
|
||||||
std::shared_ptr<const FunctionCodeIndex> function_code_index;
|
std::shared_ptr<const FunctionCodeIndex> function_code_index;
|
||||||
std::shared_ptr<const PatchFileIndex> pc_patch_file_index;
|
std::shared_ptr<const PatchFileIndex> pc_patch_file_index;
|
||||||
std::shared_ptr<const PatchFileIndex> bb_patch_file_index;
|
std::shared_ptr<const PatchFileIndex> bb_patch_file_index;
|
||||||
std::array<std::shared_ptr<ThreadSafeFileCache>, NUM_VERSIONS> map_file_caches;
|
std::array<std::shared_ptr<ThreadSafeFileCache>, NUM_VERSIONS> map_file_caches;
|
||||||
std::shared_ptr<const DOLFileIndex> dol_file_index;
|
std::shared_ptr<const DOLFileIndex> dol_file_index;
|
||||||
std::shared_ptr<const Episode3::CardIndex> ep3_card_index;
|
std::shared_ptr<const Episode3::CardIndex> ep3_card_index;
|
||||||
std::shared_ptr<const Episode3::CardIndex> ep3_card_index_trial;
|
std::shared_ptr<const Episode3::CardIndex> ep3_card_index_trial;
|
||||||
std::shared_ptr<const Episode3::MapIndex> ep3_map_index;
|
std::shared_ptr<const Episode3::MapIndex> ep3_map_index;
|
||||||
std::shared_ptr<const Episode3::COMDeckIndex> ep3_com_deck_index;
|
std::shared_ptr<const Episode3::COMDeckIndex> ep3_com_deck_index;
|
||||||
std::shared_ptr<const G_SetEXResultValues_Ep3_6xB4x4B> ep3_default_ex_values;
|
std::shared_ptr<const G_SetEXResultValues_Ep3_6xB4x4B> ep3_default_ex_values;
|
||||||
std::shared_ptr<const G_SetEXResultValues_Ep3_6xB4x4B> ep3_tournament_ex_values;
|
std::shared_ptr<const G_SetEXResultValues_Ep3_6xB4x4B> ep3_tournament_ex_values;
|
||||||
std::shared_ptr<const G_SetEXResultValues_Ep3_6xB4x4B> ep3_tournament_final_round_ex_values;
|
std::shared_ptr<const G_SetEXResultValues_Ep3_6xB4x4B> ep3_tournament_final_round_ex_values;
|
||||||
std::shared_ptr<const QuestCategoryIndex> quest_category_index;
|
std::shared_ptr<const QuestCategoryIndex> quest_category_index;
|
||||||
std::shared_ptr<const QuestIndex> default_quest_index;
|
std::shared_ptr<const QuestIndex> default_quest_index;
|
||||||
std::shared_ptr<const QuestIndex> ep3_download_quest_index;
|
std::shared_ptr<const QuestIndex> ep3_download_quest_index;
|
||||||
std::shared_ptr<const LevelTable> level_table_v1_v2;
|
std::shared_ptr<const LevelTable> level_table_v1_v2;
|
||||||
std::shared_ptr<const LevelTable> level_table_v3;
|
std::shared_ptr<const LevelTable> level_table_v3;
|
||||||
std::shared_ptr<const LevelTable> level_table_v4;
|
std::shared_ptr<const LevelTable> level_table_v4;
|
||||||
std::shared_ptr<const BattleParamsIndex> battle_params;
|
std::shared_ptr<const BattleParamsIndex> battle_params;
|
||||||
std::shared_ptr<const GSLArchive> bb_data_gsl;
|
std::shared_ptr<const GSLArchive> bb_data_gsl;
|
||||||
std::unordered_map<std::string, std::shared_ptr<const RareItemSet>> rare_item_sets;
|
std::unordered_map<std::string, std::shared_ptr<const RareItemSet>> rare_item_sets;
|
||||||
std::shared_ptr<const CommonItemSet> common_item_set_v2;
|
std::shared_ptr<const CommonItemSet> common_item_set_v2;
|
||||||
std::shared_ptr<const CommonItemSet> common_item_set_v3_v4;
|
std::shared_ptr<const CommonItemSet> common_item_set_v3_v4;
|
||||||
std::shared_ptr<const ArmorRandomSet> armor_random_set;
|
std::shared_ptr<const ArmorRandomSet> armor_random_set;
|
||||||
std::shared_ptr<const ToolRandomSet> tool_random_set;
|
std::shared_ptr<const ToolRandomSet> tool_random_set;
|
||||||
std::array<std::shared_ptr<const WeaponRandomSet>, 4> weapon_random_sets;
|
std::array<std::shared_ptr<const WeaponRandomSet>, 4> weapon_random_sets;
|
||||||
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set;
|
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set;
|
||||||
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
|
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
|
||||||
std::array<std::shared_ptr<const ItemData::StackLimits>, NUM_VERSIONS> item_stack_limits_tables;
|
std::array<std::shared_ptr<const ItemData::StackLimits>, NUM_VERSIONS> item_stack_limits_tables;
|
||||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table;
|
std::shared_ptr<const MagEvolutionTable> mag_evolution_table;
|
||||||
std::shared_ptr<const TextIndex> text_index;
|
std::shared_ptr<const TextIndex> text_index;
|
||||||
std::array<std::shared_ptr<const ItemNameIndex>, NUM_VERSIONS> item_name_indexes;
|
std::array<std::shared_ptr<const ItemNameIndex>, NUM_VERSIONS> item_name_indexes;
|
||||||
std::shared_ptr<const WordSelectTable> word_select_table;
|
std::shared_ptr<const WordSelectTable> word_select_table;
|
||||||
std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> set_data_tables;
|
std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> set_data_tables;
|
||||||
std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> set_data_tables_ep1_ult;
|
std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> set_data_tables_ep1_ult;
|
||||||
std::shared_ptr<const SetDataTableBase> bb_solo_set_data_table;
|
std::shared_ptr<const SetDataTableBase> bb_solo_set_data_table;
|
||||||
std::shared_ptr<const SetDataTableBase> bb_solo_set_data_table_ep1_ult;
|
std::shared_ptr<const SetDataTableBase> bb_solo_set_data_table_ep1_ult;
|
||||||
std::array<std::shared_ptr<const Map::RareEnemyRates>, 4> rare_enemy_rates_by_difficulty;
|
std::array<std::shared_ptr<const Map::RareEnemyRates>, 4> rare_enemy_rates_by_difficulty;
|
||||||
std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates_challenge;
|
std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates_challenge;
|
||||||
std::array<std::array<size_t, 4>, 3> min_levels_v4; // Indexed as [episode][difficulty]
|
std::array<std::array<size_t, 4>, 3> min_levels_v4; // Indexed as [episode][difficulty]
|
||||||
|
|
||||||
struct QuestF960Result {
|
struct QuestF960Result {
|
||||||
uint32_t meseta_cost = 0;
|
uint32_t meseta_cost = 0;
|
||||||
uint32_t base_probability = 0;
|
uint32_t base_probability = 0;
|
||||||
uint32_t probability_upgrade = 0;
|
uint32_t probability_upgrade = 0;
|
||||||
std::array<std::vector<ItemData>, 7> results;
|
std::array<std::vector<ItemData>, 7> results;
|
||||||
|
|
||||||
QuestF960Result() = default;
|
QuestF960Result() = default;
|
||||||
QuestF960Result(const JSON& json, std::shared_ptr<const ItemNameIndex> name_index);
|
QuestF960Result(const JSON& json, std::shared_ptr<const ItemNameIndex> name_index);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Indexed as [type][difficulty][random_choice]
|
// Indexed as [type][difficulty][random_choice]
|
||||||
std::vector<std::vector<std::vector<ItemData>>> quest_F95E_results;
|
std::vector<std::vector<std::vector<ItemData>>> quest_F95E_results;
|
||||||
std::vector<std::pair<size_t, ItemData>> quest_F95F_results; // [(num_photon_tickets, item)]
|
std::vector<std::pair<size_t, ItemData>> quest_F95F_results; // [(num_photon_tickets, item)]
|
||||||
std::vector<QuestF960Result> quest_F960_success_results;
|
std::vector<QuestF960Result> quest_F960_success_results;
|
||||||
QuestF960Result quest_F960_failure_results;
|
QuestF960Result quest_F960_failure_results;
|
||||||
std::vector<ItemData> secret_lottery_results;
|
std::vector<ItemData> secret_lottery_results;
|
||||||
uint16_t bb_global_exp_multiplier = 1;
|
uint16_t bb_global_exp_multiplier = 1;
|
||||||
float exp_share_multiplier = 0.5;
|
float exp_share_multiplier = 0.5;
|
||||||
double server_global_drop_rate_multiplier = 1.0;
|
double server_global_drop_rate_multiplier = 1.0;
|
||||||
|
|
||||||
std::shared_ptr<Episode3::TournamentIndex> ep3_tournament_index;
|
std::shared_ptr<Episode3::TournamentIndex> ep3_tournament_index;
|
||||||
|
|
||||||
uint16_t ep3_card_auction_points = 0;
|
uint16_t ep3_card_auction_points = 0;
|
||||||
uint16_t ep3_card_auction_min_size = 0;
|
uint16_t ep3_card_auction_min_size = 0;
|
||||||
uint16_t ep3_card_auction_max_size = 0;
|
uint16_t ep3_card_auction_max_size = 0;
|
||||||
struct CardAuctionPoolEntry {
|
struct CardAuctionPoolEntry {
|
||||||
uint64_t probability;
|
uint64_t probability;
|
||||||
uint16_t card_id;
|
uint16_t card_id;
|
||||||
uint16_t min_price;
|
uint16_t min_price;
|
||||||
};
|
};
|
||||||
std::vector<CardAuctionPoolEntry> ep3_card_auction_pool;
|
std::vector<CardAuctionPoolEntry> ep3_card_auction_pool;
|
||||||
std::array<std::vector<uint16_t>, 5> ep3_trap_card_ids;
|
std::array<std::vector<uint16_t>, 5> ep3_trap_card_ids;
|
||||||
struct Ep3LobbyBannerEntry {
|
struct Ep3LobbyBannerEntry {
|
||||||
uint32_t type = 1;
|
uint32_t type = 1;
|
||||||
uint32_t which; // See B9 documentation in CommandFormats.hh
|
uint32_t which; // See B9 documentation in CommandFormats.hh
|
||||||
std::string data;
|
std::string data;
|
||||||
};
|
};
|
||||||
std::vector<Ep3LobbyBannerEntry> ep3_lobby_banners;
|
std::vector<Ep3LobbyBannerEntry> ep3_lobby_banners;
|
||||||
|
|
||||||
std::shared_ptr<AccountIndex> account_index;
|
std::shared_ptr<AccountIndex> account_index;
|
||||||
std::shared_ptr<IPV4RangeSet> banned_ipv4_ranges;
|
std::shared_ptr<IPV4RangeSet> banned_ipv4_ranges;
|
||||||
std::shared_ptr<TeamIndex> team_index;
|
std::shared_ptr<TeamIndex> team_index;
|
||||||
JSON team_reward_defs_json;
|
JSON team_reward_defs_json;
|
||||||
|
|
||||||
std::shared_ptr<const Menu> information_menu_v2;
|
std::shared_ptr<const Menu> information_menu_v2;
|
||||||
std::shared_ptr<const Menu> information_menu_v3;
|
std::shared_ptr<const Menu> information_menu_v3;
|
||||||
std::shared_ptr<std::vector<std::string>> information_contents_v2;
|
std::shared_ptr<std::vector<std::string>> information_contents_v2;
|
||||||
std::shared_ptr<std::vector<std::string>> information_contents_v3;
|
std::shared_ptr<std::vector<std::string>> information_contents_v3;
|
||||||
std::shared_ptr<const Menu> proxy_destinations_menu_dc;
|
std::shared_ptr<const Menu> proxy_destinations_menu_dc;
|
||||||
std::shared_ptr<const Menu> proxy_destinations_menu_pc;
|
std::shared_ptr<const Menu> proxy_destinations_menu_pc;
|
||||||
std::shared_ptr<const Menu> proxy_destinations_menu_gc;
|
std::shared_ptr<const Menu> proxy_destinations_menu_gc;
|
||||||
std::shared_ptr<const Menu> proxy_destinations_menu_xb;
|
std::shared_ptr<const Menu> proxy_destinations_menu_xb;
|
||||||
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_dc;
|
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_dc;
|
||||||
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_pc;
|
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_pc;
|
||||||
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_gc;
|
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_gc;
|
||||||
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_xb;
|
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_xb;
|
||||||
std::pair<std::string, uint16_t> proxy_destination_patch;
|
std::pair<std::string, uint16_t> proxy_destination_patch;
|
||||||
std::pair<std::string, uint16_t> proxy_destination_bb;
|
std::pair<std::string, uint16_t> proxy_destination_bb;
|
||||||
std::string welcome_message;
|
std::string welcome_message;
|
||||||
std::string pc_patch_server_message;
|
std::string pc_patch_server_message;
|
||||||
std::string bb_patch_server_message;
|
std::string bb_patch_server_message;
|
||||||
|
|
||||||
std::shared_ptr<PlayerFilesManager> player_files_manager;
|
std::shared_ptr<PlayerFilesManager> player_files_manager;
|
||||||
std::unordered_map<Channel*, std::shared_ptr<Client>> channel_to_client;
|
std::unordered_map<Channel*, std::shared_ptr<Client>> channel_to_client;
|
||||||
std::map<int64_t, std::shared_ptr<Lobby>> id_to_lobby;
|
std::map<int64_t, std::shared_ptr<Lobby>> id_to_lobby;
|
||||||
std::unordered_set<std::shared_ptr<Lobby>> lobbies_to_destroy;
|
std::unordered_set<std::shared_ptr<Lobby>> lobbies_to_destroy;
|
||||||
std::shared_ptr<struct event> destroy_lobbies_event;
|
std::shared_ptr<struct event> destroy_lobbies_event;
|
||||||
std::array<std::vector<uint32_t>, NUM_VERSIONS> public_lobby_search_orders;
|
std::array<std::vector<uint32_t>, NUM_VERSIONS> public_lobby_search_orders;
|
||||||
std::vector<uint32_t> client_customization_public_lobby_search_order;
|
std::vector<uint32_t> client_customization_public_lobby_search_order;
|
||||||
std::atomic<int32_t> next_lobby_id = 1;
|
std::atomic<int32_t> next_lobby_id = 1;
|
||||||
uint8_t pre_lobby_event = 0;
|
uint8_t pre_lobby_event = 0;
|
||||||
int32_t ep3_menu_song = -1;
|
int32_t ep3_menu_song = -1;
|
||||||
|
|
||||||
std::map<std::string, uint32_t> all_addresses;
|
std::map<std::string, uint32_t> all_addresses;
|
||||||
uint32_t local_address = 0;
|
uint32_t local_address = 0;
|
||||||
uint32_t external_address = 0;
|
uint32_t external_address = 0;
|
||||||
|
|
||||||
bool proxy_allow_save_files = true;
|
bool proxy_allow_save_files = true;
|
||||||
bool proxy_enable_login_options = false;
|
bool proxy_enable_login_options = false;
|
||||||
|
|
||||||
std::shared_ptr<IPStackSimulator> ip_stack_simulator;
|
std::shared_ptr<IPStackSimulator> ip_stack_simulator;
|
||||||
std::shared_ptr<DNSServer> dns_server;
|
std::shared_ptr<DNSServer> dns_server;
|
||||||
std::shared_ptr<ProxyServer> proxy_server;
|
std::shared_ptr<ProxyServer> proxy_server;
|
||||||
std::shared_ptr<Server> game_server;
|
std::shared_ptr<Server> game_server;
|
||||||
std::shared_ptr<PatchServer> pc_patch_server;
|
std::shared_ptr<PatchServer> pc_patch_server;
|
||||||
std::shared_ptr<PatchServer> bb_patch_server;
|
std::shared_ptr<PatchServer> bb_patch_server;
|
||||||
|
|
||||||
explicit ServerState(const std::string& config_filename = "");
|
explicit ServerState(const std::string& config_filename = "");
|
||||||
ServerState(std::shared_ptr<struct event_base> base, const std::string& config_filename, bool is_replay);
|
ServerState(std::shared_ptr<struct event_base> base, const std::string& config_filename, bool is_replay);
|
||||||
ServerState(const ServerState&) = delete;
|
ServerState(const ServerState&) = delete;
|
||||||
ServerState(ServerState&&) = delete;
|
ServerState(ServerState&&) = delete;
|
||||||
ServerState& operator=(const ServerState&) = delete;
|
ServerState& operator=(const ServerState&) = delete;
|
||||||
ServerState& operator=(ServerState&&) = delete;
|
ServerState& operator=(ServerState&&) = delete;
|
||||||
|
|
||||||
void add_client_to_available_lobby(std::shared_ptr<Client> c);
|
void add_client_to_available_lobby(std::shared_ptr<Client> c);
|
||||||
void remove_client_from_lobby(std::shared_ptr<Client> c);
|
void remove_client_from_lobby(std::shared_ptr<Client> c);
|
||||||
bool change_client_lobby(
|
bool change_client_lobby(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<Lobby> new_lobby,
|
std::shared_ptr<Lobby> new_lobby,
|
||||||
bool send_join_notification = true,
|
bool send_join_notification = true,
|
||||||
ssize_t required_client_id = -1);
|
ssize_t required_client_id = -1);
|
||||||
|
|
||||||
void send_lobby_join_notifications(std::shared_ptr<Lobby> l,
|
void send_lobby_join_notifications(std::shared_ptr<Lobby> l,
|
||||||
std::shared_ptr<Client> joining_client);
|
std::shared_ptr<Client> joining_client);
|
||||||
|
|
||||||
std::shared_ptr<Lobby> find_lobby(uint32_t lobby_id);
|
std::shared_ptr<Lobby> find_lobby(uint32_t lobby_id);
|
||||||
std::vector<std::shared_ptr<Lobby>> all_lobbies();
|
std::vector<std::shared_ptr<Lobby>> all_lobbies();
|
||||||
|
|
||||||
std::shared_ptr<Lobby> create_lobby(bool is_game);
|
std::shared_ptr<Lobby> create_lobby(bool is_game);
|
||||||
void remove_lobby(std::shared_ptr<Lobby> l);
|
void remove_lobby(std::shared_ptr<Lobby> l);
|
||||||
void on_player_left_lobby(std::shared_ptr<Lobby> l, uint8_t leaving_client_id);
|
void on_player_left_lobby(std::shared_ptr<Lobby> l, uint8_t leaving_client_id);
|
||||||
|
|
||||||
std::shared_ptr<Client> find_client(
|
std::shared_ptr<Client> find_client(
|
||||||
const std::string* identifier = nullptr,
|
const std::string* identifier = nullptr,
|
||||||
uint64_t account_id = 0,
|
uint64_t account_id = 0,
|
||||||
std::shared_ptr<Lobby> l = nullptr);
|
std::shared_ptr<Lobby> l = nullptr);
|
||||||
|
|
||||||
uint32_t connect_address_for_client(std::shared_ptr<Client> c) const;
|
uint32_t connect_address_for_client(std::shared_ptr<Client> c) const;
|
||||||
|
|
||||||
std::shared_ptr<const Menu> information_menu(Version version) const;
|
std::shared_ptr<const Menu> information_menu(Version version) const;
|
||||||
std::shared_ptr<const Menu> proxy_destinations_menu(Version version) const;
|
std::shared_ptr<const Menu> proxy_destinations_menu(Version version) const;
|
||||||
const std::vector<std::pair<std::string, uint16_t>>& proxy_destinations(Version version) const;
|
const std::vector<std::pair<std::string, uint16_t>>& proxy_destinations(Version version) const;
|
||||||
|
|
||||||
std::shared_ptr<const SetDataTableBase> set_data_table(Version version, Episode episode, GameMode mode, uint8_t difficulty) const;
|
std::shared_ptr<const SetDataTableBase> set_data_table(Version version, Episode episode, GameMode mode, uint8_t difficulty) const;
|
||||||
|
|
||||||
std::shared_ptr<const LevelTable> level_table(Version version) const;
|
std::shared_ptr<const LevelTable> level_table(Version version) const;
|
||||||
std::shared_ptr<const ItemParameterTable> item_parameter_table(Version version) const;
|
std::shared_ptr<const ItemParameterTable> item_parameter_table(Version version) const;
|
||||||
std::shared_ptr<const ItemParameterTable> item_parameter_table_for_encode(Version version) const;
|
std::shared_ptr<const ItemParameterTable> item_parameter_table_for_encode(Version version) const;
|
||||||
std::shared_ptr<const ItemData::StackLimits> item_stack_limits(Version version) const;
|
std::shared_ptr<const ItemData::StackLimits> item_stack_limits(Version version) const;
|
||||||
std::shared_ptr<const ItemNameIndex> item_name_index_opt(Version version) const; // Returns null if missing
|
std::shared_ptr<const ItemNameIndex> item_name_index_opt(Version version) const; // Returns null if missing
|
||||||
std::shared_ptr<const ItemNameIndex> item_name_index(Version version) const; // Throws if missing
|
std::shared_ptr<const ItemNameIndex> item_name_index(Version version) const; // Throws if missing
|
||||||
std::string describe_item(Version version, const ItemData& item, bool include_color_codes) const;
|
std::string describe_item(Version version, const ItemData& item, bool include_color_codes) const;
|
||||||
ItemData parse_item_description(Version version, const std::string& description) const;
|
ItemData parse_item_description(Version version, const std::string& description) const;
|
||||||
|
|
||||||
const std::vector<uint32_t>& public_lobby_search_order(Version version, bool is_client_customization) const;
|
const std::vector<uint32_t>& public_lobby_search_order(Version version, bool is_client_customization) const;
|
||||||
inline const std::vector<uint32_t>& public_lobby_search_order(std::shared_ptr<const Client> c) const {
|
inline const std::vector<uint32_t>& public_lobby_search_order(std::shared_ptr<const Client> c) const {
|
||||||
return this->public_lobby_search_order(c->version(), c->config.check_flag(Client::Flag::IS_CLIENT_CUSTOMIZATION));
|
return this->public_lobby_search_order(c->version(), c->config.check_flag(Client::Flag::IS_CLIENT_CUSTOMIZATION));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t name_color_for_client(Version v, bool is_client_customization) const {
|
inline uint32_t name_color_for_client(Version v, bool is_client_customization) const {
|
||||||
if (is_client_customization && this->client_customization_name_color) {
|
if (is_client_customization && this->client_customization_name_color) {
|
||||||
return this->client_customization_name_color;
|
return this->client_customization_name_color;
|
||||||
}
|
}
|
||||||
return this->version_name_colors ? this->version_name_colors->at(static_cast<size_t>(v) - NUM_PATCH_VERSIONS) : 0;
|
return this->version_name_colors ? this->version_name_colors->at(static_cast<size_t>(v) - NUM_PATCH_VERSIONS) : 0;
|
||||||
}
|
}
|
||||||
inline uint32_t name_color_for_client(std::shared_ptr<const Client> c) const {
|
inline uint32_t name_color_for_client(std::shared_ptr<const Client> c) const {
|
||||||
return this->name_color_for_client(c->version(), c->config.check_flag(Client::Flag::IS_CLIENT_CUSTOMIZATION));
|
return this->name_color_for_client(c->version(), c->config.check_flag(Client::Flag::IS_CLIENT_CUSTOMIZATION));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const std::vector<std::string>> information_contents_for_client(std::shared_ptr<const Client> c) const;
|
std::shared_ptr<const std::vector<std::string>> information_contents_for_client(std::shared_ptr<const Client> c) const;
|
||||||
std::shared_ptr<const QuestIndex> quest_index(Version version) const;
|
std::shared_ptr<const QuestIndex> quest_index(Version version) const;
|
||||||
|
|
||||||
size_t default_min_level_for_game(Version version, Episode episode, uint8_t difficulty) const;
|
size_t default_min_level_for_game(Version version, Episode episode, uint8_t difficulty) const;
|
||||||
|
|
||||||
void set_port_configuration(const std::vector<PortConfiguration>& port_configs);
|
void set_port_configuration(const std::vector<PortConfiguration>& port_configs);
|
||||||
|
|
||||||
std::shared_ptr<const std::string> load_bb_file(
|
std::shared_ptr<const std::string> load_bb_file(
|
||||||
const std::string& patch_index_filename,
|
const std::string& patch_index_filename,
|
||||||
const std::string& gsl_filename = "",
|
const std::string& gsl_filename = "",
|
||||||
const std::string& bb_directory_filename = "") const;
|
const std::string& bb_directory_filename = "") const;
|
||||||
std::shared_ptr<const std::string> load_map_file(Version version, const std::string& filename) const;
|
std::shared_ptr<const std::string> load_map_file(Version version, const std::string& filename) const;
|
||||||
std::shared_ptr<const std::string> load_map_file_uncached(Version version, const std::string& filename) const;
|
std::shared_ptr<const std::string> load_map_file_uncached(Version version, const std::string& filename) const;
|
||||||
|
|
||||||
std::pair<std::string, uint16_t> parse_port_spec(const JSON& json) const;
|
std::pair<std::string, uint16_t> parse_port_spec(const JSON& json) const;
|
||||||
std::vector<PortConfiguration> parse_port_configuration(const JSON& json) const;
|
std::vector<PortConfiguration> parse_port_configuration(const JSON& json) const;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void call_on_event_thread(std::function<T()>&& fn) {
|
inline void call_on_event_thread(std::function<T()>&& fn) {
|
||||||
return ::call_on_event_thread<T>(this->base, std::move(fn));
|
return ::call_on_event_thread<T>(this->base, std::move(fn));
|
||||||
}
|
}
|
||||||
inline void forward_to_event_thread(std::function<void()>&& fn) {
|
inline void forward_to_event_thread(std::function<void()>&& fn) {
|
||||||
::forward_to_event_thread(this->base, std::move(fn));
|
::forward_to_event_thread(this->base, std::move(fn));
|
||||||
}
|
}
|
||||||
inline void forward_or_call(bool from_non_event_thread, std::function<void()>&& fn) {
|
inline void forward_or_call(bool from_non_event_thread, std::function<void()>&& fn) {
|
||||||
if (from_non_event_thread) {
|
if (from_non_event_thread) {
|
||||||
::forward_to_event_thread(this->base, std::move(fn));
|
::forward_to_event_thread(this->base, std::move(fn));
|
||||||
} else {
|
} else {
|
||||||
fn();
|
fn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<PatchServer::Config> generate_patch_server_config(bool is_bb) const;
|
std::shared_ptr<PatchServer::Config> generate_patch_server_config(bool is_bb) const;
|
||||||
void update_dependent_server_configs() const;
|
void update_dependent_server_configs() const;
|
||||||
|
|
||||||
// The following functions may only be called from a non-event thread if they
|
// The following functions may only be called from a non-event thread if they
|
||||||
// take a from_non_event_thread argument; any function that does not have this
|
// take a from_non_event_thread argument; any function that does not have this
|
||||||
// argument must be called only from the event thread.
|
// argument must be called only from the event thread.
|
||||||
void create_default_lobbies();
|
void create_default_lobbies();
|
||||||
void collect_network_addresses();
|
void collect_network_addresses();
|
||||||
void load_config_early();
|
void load_config_early();
|
||||||
void load_config_late();
|
void load_config_late();
|
||||||
void load_bb_private_keys(bool from_non_event_thread);
|
void load_bb_private_keys(bool from_non_event_thread);
|
||||||
void load_accounts(bool from_non_event_thread);
|
void load_accounts(bool from_non_event_thread);
|
||||||
void load_teams(bool from_non_event_thread);
|
void load_teams(bool from_non_event_thread);
|
||||||
void load_patch_indexes(bool from_non_event_thread);
|
void load_patch_indexes(bool from_non_event_thread);
|
||||||
void clear_map_file_caches();
|
void clear_map_file_caches();
|
||||||
void load_battle_params(bool from_non_event_thread);
|
void load_battle_params(bool from_non_event_thread);
|
||||||
void load_level_tables(bool from_non_event_thread);
|
void load_level_tables(bool from_non_event_thread);
|
||||||
void load_text_index(bool from_non_event_thread);
|
void load_text_index(bool from_non_event_thread);
|
||||||
std::shared_ptr<ItemNameIndex> create_item_name_index_for_version(
|
std::shared_ptr<ItemNameIndex> create_item_name_index_for_version(
|
||||||
std::shared_ptr<const ItemParameterTable> pmt,
|
std::shared_ptr<const ItemParameterTable> pmt,
|
||||||
std::shared_ptr<const ItemData::StackLimits> limits,
|
std::shared_ptr<const ItemData::StackLimits> limits,
|
||||||
std::shared_ptr<const TextIndex> text_index) const;
|
std::shared_ptr<const TextIndex> text_index) const;
|
||||||
void load_item_name_indexes(bool from_non_event_thread);
|
void load_item_name_indexes(bool from_non_event_thread);
|
||||||
void load_drop_tables(bool from_non_event_thread);
|
void load_drop_tables(bool from_non_event_thread);
|
||||||
void load_item_definitions(bool from_non_event_thread);
|
void load_item_definitions(bool from_non_event_thread);
|
||||||
void load_set_data_tables(bool from_non_event_thread);
|
void load_set_data_tables(bool from_non_event_thread);
|
||||||
void load_word_select_table(bool from_non_event_thread);
|
void load_word_select_table(bool from_non_event_thread);
|
||||||
void load_ep3_cards(bool from_non_event_thread);
|
void load_ep3_cards(bool from_non_event_thread);
|
||||||
void load_ep3_maps(bool from_non_event_thread);
|
void load_ep3_maps(bool from_non_event_thread);
|
||||||
void load_ep3_tournament_state(bool from_non_event_thread);
|
void load_ep3_tournament_state(bool from_non_event_thread);
|
||||||
void load_quest_index(bool from_non_event_thread);
|
void load_quest_index(bool from_non_event_thread);
|
||||||
void compile_functions(bool from_non_event_thread);
|
void compile_functions(bool from_non_event_thread);
|
||||||
void load_dol_files(bool from_non_event_thread);
|
void load_dol_files(bool from_non_event_thread);
|
||||||
void load_all();
|
void load_all();
|
||||||
|
|
||||||
void enqueue_destroy_lobbies();
|
void enqueue_destroy_lobbies();
|
||||||
static void dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx);
|
static void dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx);
|
||||||
|
|
||||||
void disconnect_all_banned_clients();
|
void disconnect_all_banned_clients();
|
||||||
|
|
||||||
std::string format_address_for_channel_name(const struct sockaddr_storage& remote_ss, uint64_t virtual_network);
|
std::string format_address_for_channel_name(const struct sockaddr_storage& remote_ss, uint64_t virtual_network);
|
||||||
};
|
};
|
||||||
|
|||||||
+472
-472
@@ -1,472 +1,472 @@
|
|||||||
#include "TeamIndex.hh"
|
#include "TeamIndex.hh"
|
||||||
|
|
||||||
#include <phosg/Filesystem.hh>
|
#include <phosg/Filesystem.hh>
|
||||||
#include <phosg/Image.hh>
|
#include <phosg/Image.hh>
|
||||||
#include <phosg/Random.hh>
|
#include <phosg/Random.hh>
|
||||||
|
|
||||||
#include "BattleParamsIndex.hh"
|
#include "BattleParamsIndex.hh"
|
||||||
#include "GVMEncoder.hh"
|
#include "GVMEncoder.hh"
|
||||||
#include "ItemData.hh"
|
#include "ItemData.hh"
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
TeamIndex::Team::Member::Member(const JSON& json)
|
TeamIndex::Team::Member::Member(const JSON& json)
|
||||||
: flags(json.get_int("Flags", 0)),
|
: flags(json.get_int("Flags", 0)),
|
||||||
points(json.get_int("Points", 0)),
|
points(json.get_int("Points", 0)),
|
||||||
name(json.get_string("Name", "")) {
|
name(json.get_string("Name", "")) {
|
||||||
try {
|
try {
|
||||||
this->account_id = json.get_int("AccountID");
|
this->account_id = json.get_int("AccountID");
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
this->account_id = json.get_int("SerialNumber");
|
this->account_id = json.get_int("SerialNumber");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON TeamIndex::Team::Member::json() const {
|
JSON TeamIndex::Team::Member::json() const {
|
||||||
return JSON::dict({
|
return JSON::dict({
|
||||||
{"AccountID", this->account_id},
|
{"AccountID", this->account_id},
|
||||||
{"Flags", this->flags},
|
{"Flags", this->flags},
|
||||||
{"Points", this->points},
|
{"Points", this->points},
|
||||||
{"Name", this->name},
|
{"Name", this->name},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TeamIndex::Team::Member::privilege_level() const {
|
uint32_t TeamIndex::Team::Member::privilege_level() const {
|
||||||
if (this->check_flag(Member::Flag::IS_MASTER)) {
|
if (this->check_flag(Member::Flag::IS_MASTER)) {
|
||||||
return 0x40;
|
return 0x40;
|
||||||
} else if (this->check_flag(Member::Flag::IS_LEADER)) {
|
} else if (this->check_flag(Member::Flag::IS_LEADER)) {
|
||||||
return 0x30;
|
return 0x30;
|
||||||
} else {
|
} else {
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamIndex::Team::Team(uint32_t team_id) : Team() {
|
TeamIndex::Team::Team(uint32_t team_id) : Team() {
|
||||||
this->team_id = team_id;
|
this->team_id = team_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
string TeamIndex::Team::json_filename() const {
|
string TeamIndex::Team::json_filename() const {
|
||||||
return string_printf("system/teams/%08" PRIX32 ".json", this->team_id);
|
return string_printf("system/teams/%08" PRIX32 ".json", this->team_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
string TeamIndex::Team::flag_filename() const {
|
string TeamIndex::Team::flag_filename() const {
|
||||||
return string_printf("system/teams/%08" PRIX32 ".bmp", this->team_id);
|
return string_printf("system/teams/%08" PRIX32 ".bmp", this->team_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::Team::load_config() {
|
void TeamIndex::Team::load_config() {
|
||||||
auto json = JSON::parse(load_file(this->json_filename()));
|
auto json = JSON::parse(load_file(this->json_filename()));
|
||||||
this->name = json.get_string("Name");
|
this->name = json.get_string("Name");
|
||||||
this->spent_points = json.get_int("SpentPoints");
|
this->spent_points = json.get_int("SpentPoints");
|
||||||
this->points = 0;
|
this->points = 0;
|
||||||
for (const auto& member_it : json.get_list("Members")) {
|
for (const auto& member_it : json.get_list("Members")) {
|
||||||
Member m(*member_it);
|
Member m(*member_it);
|
||||||
this->points += m.points;
|
this->points += m.points;
|
||||||
uint32_t account_id = m.account_id;
|
uint32_t account_id = m.account_id;
|
||||||
if (m.check_flag(Member::Flag::IS_MASTER)) {
|
if (m.check_flag(Member::Flag::IS_MASTER)) {
|
||||||
this->master_account_id = account_id;
|
this->master_account_id = account_id;
|
||||||
}
|
}
|
||||||
this->members.emplace(account_id, std::move(m));
|
this->members.emplace(account_id, std::move(m));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
for (const auto& it : json.get_list("RewardKeys")) {
|
for (const auto& it : json.get_list("RewardKeys")) {
|
||||||
this->reward_keys.emplace(it->as_string());
|
this->reward_keys.emplace(it->as_string());
|
||||||
}
|
}
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
this->reward_flags = json.get_int("RewardFlags");
|
this->reward_flags = json.get_int("RewardFlags");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::Team::save_config() const {
|
void TeamIndex::Team::save_config() const {
|
||||||
JSON members_json = JSON::list();
|
JSON members_json = JSON::list();
|
||||||
for (const auto& it : this->members) {
|
for (const auto& it : this->members) {
|
||||||
members_json.emplace_back(it.second.json());
|
members_json.emplace_back(it.second.json());
|
||||||
}
|
}
|
||||||
JSON reward_keys_json = JSON::list();
|
JSON reward_keys_json = JSON::list();
|
||||||
for (const auto& it : this->reward_keys) {
|
for (const auto& it : this->reward_keys) {
|
||||||
reward_keys_json.emplace_back(it);
|
reward_keys_json.emplace_back(it);
|
||||||
}
|
}
|
||||||
JSON root = JSON::dict({
|
JSON root = JSON::dict({
|
||||||
{"Name", this->name},
|
{"Name", this->name},
|
||||||
{"SpentPoints", this->spent_points},
|
{"SpentPoints", this->spent_points},
|
||||||
{"Members", std::move(members_json)},
|
{"Members", std::move(members_json)},
|
||||||
{"RewardKeys", std::move(reward_keys_json)},
|
{"RewardKeys", std::move(reward_keys_json)},
|
||||||
{"RewardFlags", this->reward_flags},
|
{"RewardFlags", this->reward_flags},
|
||||||
});
|
});
|
||||||
save_file(this->json_filename(), root.serialize(JSON::SerializeOption::FORMAT | JSON::SerializeOption::HEX_INTEGERS | JSON::SerializeOption::ESCAPE_CONTROLS_ONLY));
|
save_file(this->json_filename(), root.serialize(JSON::SerializeOption::FORMAT | JSON::SerializeOption::HEX_INTEGERS | JSON::SerializeOption::ESCAPE_CONTROLS_ONLY));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::Team::load_flag() {
|
void TeamIndex::Team::load_flag() {
|
||||||
Image img(this->flag_filename());
|
Image img(this->flag_filename());
|
||||||
if (img.get_width() != 32 || img.get_height() != 32) {
|
if (img.get_width() != 32 || img.get_height() != 32) {
|
||||||
throw runtime_error("incorrect flag image dimensions");
|
throw runtime_error("incorrect flag image dimensions");
|
||||||
}
|
}
|
||||||
this->flag_data.reset(new parray<le_uint16_t, 0x20 * 0x20>());
|
this->flag_data.reset(new parray<le_uint16_t, 0x20 * 0x20>());
|
||||||
for (size_t y = 0; y < 32; y++) {
|
for (size_t y = 0; y < 32; y++) {
|
||||||
for (size_t x = 0; x < 32; x++) {
|
for (size_t x = 0; x < 32; x++) {
|
||||||
this->flag_data->at(y * 0x20 + x) = encode_rgba8888_to_argb1555(img.read_pixel(x, y));
|
this->flag_data->at(y * 0x20 + x) = encode_rgba8888_to_argb1555(img.read_pixel(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::Team::save_flag() const {
|
void TeamIndex::Team::save_flag() const {
|
||||||
if (!this->flag_data) {
|
if (!this->flag_data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Image img(32, 32, true);
|
Image img(32, 32, true);
|
||||||
for (size_t y = 0; y < 32; y++) {
|
for (size_t y = 0; y < 32; y++) {
|
||||||
for (size_t x = 0; x < 32; x++) {
|
for (size_t x = 0; x < 32; x++) {
|
||||||
img.write_pixel(x, y, decode_argb1555_to_rgba8888(this->flag_data->at(y * 0x20 + x)));
|
img.write_pixel(x, y, decode_argb1555_to_rgba8888(this->flag_data->at(y * 0x20 + x)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
img.save(this->flag_filename(), Image::Format::WINDOWS_BITMAP);
|
img.save(this->flag_filename(), Image::Format::WINDOWS_BITMAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::Team::delete_files() const {
|
void TeamIndex::Team::delete_files() const {
|
||||||
string json_filename = this->json_filename();
|
string json_filename = this->json_filename();
|
||||||
string flag_filename = this->flag_filename();
|
string flag_filename = this->flag_filename();
|
||||||
remove(json_filename.c_str());
|
remove(json_filename.c_str());
|
||||||
remove(flag_filename.c_str());
|
remove(flag_filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
PSOBBTeamMembership TeamIndex::Team::membership_for_member(uint32_t account_id) const {
|
PSOBBTeamMembership TeamIndex::Team::membership_for_member(uint32_t account_id) const {
|
||||||
const auto& m = this->members.at(account_id);
|
const auto& m = this->members.at(account_id);
|
||||||
|
|
||||||
PSOBBTeamMembership ret;
|
PSOBBTeamMembership ret;
|
||||||
ret.team_master_guild_card_number = this->master_account_id;
|
ret.team_master_guild_card_number = this->master_account_id;
|
||||||
ret.team_id = this->team_id;
|
ret.team_id = this->team_id;
|
||||||
ret.unknown_a5 = 0;
|
ret.unknown_a5 = 0;
|
||||||
ret.unknown_a6 = 0;
|
ret.unknown_a6 = 0;
|
||||||
ret.privilege_level = m.privilege_level();
|
ret.privilege_level = m.privilege_level();
|
||||||
ret.unknown_a7 = 0;
|
ret.unknown_a7 = 0;
|
||||||
ret.unknown_a8 = 0;
|
ret.unknown_a8 = 0;
|
||||||
ret.unknown_a9 = 0;
|
ret.unknown_a9 = 0;
|
||||||
ret.team_name.encode(this->name);
|
ret.team_name.encode(this->name);
|
||||||
if (this->flag_data) {
|
if (this->flag_data) {
|
||||||
ret.flag_data = *this->flag_data;
|
ret.flag_data = *this->flag_data;
|
||||||
} else {
|
} else {
|
||||||
ret.flag_data.clear();
|
ret.flag_data.clear();
|
||||||
}
|
}
|
||||||
ret.reward_flags = this->reward_flags;
|
ret.reward_flags = this->reward_flags;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TeamIndex::Team::has_reward(const string& key) const {
|
bool TeamIndex::Team::has_reward(const string& key) const {
|
||||||
return this->reward_keys.count(key);
|
return this->reward_keys.count(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TeamIndex::Team::num_members() const {
|
size_t TeamIndex::Team::num_members() const {
|
||||||
return this->members.size();
|
return this->members.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TeamIndex::Team::num_leaders() const {
|
size_t TeamIndex::Team::num_leaders() const {
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
for (const auto& it : this->members) {
|
for (const auto& it : this->members) {
|
||||||
if (it.second.check_flag(Member::Flag::IS_LEADER)) {
|
if (it.second.check_flag(Member::Flag::IS_LEADER)) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TeamIndex::Team::max_members() const {
|
size_t TeamIndex::Team::max_members() const {
|
||||||
if (this->check_reward_flag(RewardFlag::MEMBERS_100_LEADERS_10)) {
|
if (this->check_reward_flag(RewardFlag::MEMBERS_100_LEADERS_10)) {
|
||||||
return 100;
|
return 100;
|
||||||
} else if (this->check_reward_flag(RewardFlag::MEMBERS_70_LEADERS_8)) {
|
} else if (this->check_reward_flag(RewardFlag::MEMBERS_70_LEADERS_8)) {
|
||||||
return 70;
|
return 70;
|
||||||
} else if (this->check_reward_flag(RewardFlag::MEMBERS_40_LEADERS_5)) {
|
} else if (this->check_reward_flag(RewardFlag::MEMBERS_40_LEADERS_5)) {
|
||||||
return 40;
|
return 40;
|
||||||
} else if (this->check_reward_flag(RewardFlag::MEMBERS_20_LEADERS_3)) {
|
} else if (this->check_reward_flag(RewardFlag::MEMBERS_20_LEADERS_3)) {
|
||||||
return 20;
|
return 20;
|
||||||
} else {
|
} else {
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TeamIndex::Team::max_leaders() const {
|
size_t TeamIndex::Team::max_leaders() const {
|
||||||
if (this->check_reward_flag(RewardFlag::MEMBERS_100_LEADERS_10)) {
|
if (this->check_reward_flag(RewardFlag::MEMBERS_100_LEADERS_10)) {
|
||||||
return 10;
|
return 10;
|
||||||
} else if (this->check_reward_flag(RewardFlag::MEMBERS_70_LEADERS_8)) {
|
} else if (this->check_reward_flag(RewardFlag::MEMBERS_70_LEADERS_8)) {
|
||||||
return 8;
|
return 8;
|
||||||
} else if (this->check_reward_flag(RewardFlag::MEMBERS_40_LEADERS_5)) {
|
} else if (this->check_reward_flag(RewardFlag::MEMBERS_40_LEADERS_5)) {
|
||||||
return 5;
|
return 5;
|
||||||
} else if (this->check_reward_flag(RewardFlag::MEMBERS_20_LEADERS_3)) {
|
} else if (this->check_reward_flag(RewardFlag::MEMBERS_20_LEADERS_3)) {
|
||||||
return 3;
|
return 3;
|
||||||
} else {
|
} else {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TeamIndex::Team::can_add_member() const {
|
bool TeamIndex::Team::can_add_member() const {
|
||||||
return this->num_members() < this->max_members();
|
return this->num_members() < this->max_members();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TeamIndex::Team::can_promote_leader() const {
|
bool TeamIndex::Team::can_promote_leader() const {
|
||||||
return this->num_leaders() < this->max_leaders();
|
return this->num_leaders() < this->max_leaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamIndex::Reward::Reward(uint32_t menu_item_id, const JSON& def_json)
|
TeamIndex::Reward::Reward(uint32_t menu_item_id, const JSON& def_json)
|
||||||
: menu_item_id(menu_item_id),
|
: menu_item_id(menu_item_id),
|
||||||
key(def_json.get_string("Key")),
|
key(def_json.get_string("Key")),
|
||||||
name(def_json.get_string("Name")),
|
name(def_json.get_string("Name")),
|
||||||
description(def_json.get_string("Description")),
|
description(def_json.get_string("Description")),
|
||||||
is_unique(def_json.get_bool("IsUnique", true)),
|
is_unique(def_json.get_bool("IsUnique", true)),
|
||||||
team_points(def_json.get_int("Points")) {
|
team_points(def_json.get_int("Points")) {
|
||||||
try {
|
try {
|
||||||
for (const auto& it : def_json.get_list("PrerequisiteKeys")) {
|
for (const auto& it : def_json.get_list("PrerequisiteKeys")) {
|
||||||
this->prerequisite_keys.emplace(it->as_string());
|
this->prerequisite_keys.emplace(it->as_string());
|
||||||
}
|
}
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this->reward_flag = static_cast<Team::RewardFlag>(def_json.get_int("RewardFlag"));
|
this->reward_flag = static_cast<Team::RewardFlag>(def_json.get_int("RewardFlag"));
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this->reward_item = ItemData::from_data(parse_data_string(def_json.get_string("RewardItem")));
|
this->reward_item = ItemData::from_data(parse_data_string(def_json.get_string("RewardItem")));
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamIndex::TeamIndex(const string& directory, const JSON& reward_defs_json)
|
TeamIndex::TeamIndex(const string& directory, const JSON& reward_defs_json)
|
||||||
: directory(directory),
|
: directory(directory),
|
||||||
next_team_id(1) {
|
next_team_id(1) {
|
||||||
uint32_t reward_menu_item_id = 0;
|
uint32_t reward_menu_item_id = 0;
|
||||||
for (const auto& it : reward_defs_json.as_list()) {
|
for (const auto& it : reward_defs_json.as_list()) {
|
||||||
this->reward_defs.emplace_back(reward_menu_item_id++, *it);
|
this->reward_defs.emplace_back(reward_menu_item_id++, *it);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isdir(this->directory)) {
|
if (!isdir(this->directory)) {
|
||||||
mkdir(this->directory.c_str(), 0755);
|
mkdir(this->directory.c_str(), 0755);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const auto& filename : list_directory(this->directory)) {
|
for (const auto& filename : list_directory(this->directory)) {
|
||||||
string file_path = this->directory + "/" + filename;
|
string file_path = this->directory + "/" + filename;
|
||||||
if (filename == "base.json") {
|
if (filename == "base.json") {
|
||||||
auto json = JSON::parse(load_file(file_path));
|
auto json = JSON::parse(load_file(file_path));
|
||||||
this->next_team_id = json.get_int("NextTeamID");
|
this->next_team_id = json.get_int("NextTeamID");
|
||||||
} else if (ends_with(filename, ".json")) {
|
} else if (ends_with(filename, ".json")) {
|
||||||
try {
|
try {
|
||||||
uint32_t team_id = stoul(filename.substr(0, filename.size() - 5), nullptr, 16);
|
uint32_t team_id = stoul(filename.substr(0, filename.size() - 5), nullptr, 16);
|
||||||
auto team = make_shared<Team>(team_id);
|
auto team = make_shared<Team>(team_id);
|
||||||
team->load_config();
|
team->load_config();
|
||||||
try {
|
try {
|
||||||
team->load_flag();
|
team->load_flag();
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
static_game_data_log.warning("Failed to load flag for team %08" PRIX32 ": %s", team_id, e.what());
|
static_game_data_log.warning("Failed to load flag for team %08" PRIX32 ": %s", team_id, e.what());
|
||||||
}
|
}
|
||||||
this->add_to_indexes(team);
|
this->add_to_indexes(team);
|
||||||
static_game_data_log.info("Indexed team %08" PRIX32 " (%s) (%zu members)", team_id, team->name.c_str(), team->num_members());
|
static_game_data_log.info("Indexed team %08" PRIX32 " (%s) (%zu members)", team_id, team->name.c_str(), team->num_members());
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
static_game_data_log.warning("Failed to index team from %s: %s", filename.c_str(), e.what());
|
static_game_data_log.warning("Failed to index team from %s: %s", filename.c_str(), e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TeamIndex::count() const {
|
size_t TeamIndex::count() const {
|
||||||
return this->id_to_team.size();
|
return this->id_to_team.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_id(uint32_t team_id) const {
|
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_id(uint32_t team_id) const {
|
||||||
try {
|
try {
|
||||||
return this->id_to_team.at(team_id);
|
return this->id_to_team.at(team_id);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_name(const string& name) const {
|
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_name(const string& name) const {
|
||||||
try {
|
try {
|
||||||
return this->name_to_team.at(name);
|
return this->name_to_team.at(name);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_account_id(uint32_t account_id) const {
|
shared_ptr<const TeamIndex::Team> TeamIndex::get_by_account_id(uint32_t account_id) const {
|
||||||
try {
|
try {
|
||||||
return this->account_id_to_team.at(account_id);
|
return this->account_id_to_team.at(account_id);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<shared_ptr<const TeamIndex::Team>> TeamIndex::all() const {
|
vector<shared_ptr<const TeamIndex::Team>> TeamIndex::all() const {
|
||||||
vector<shared_ptr<const Team>> ret;
|
vector<shared_ptr<const Team>> ret;
|
||||||
for (const auto& it : this->id_to_team) {
|
for (const auto& it : this->id_to_team) {
|
||||||
ret.emplace_back(it.second);
|
ret.emplace_back(it.second);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<const TeamIndex::Team> TeamIndex::create(const string& name, uint32_t master_account_id, const string& master_name) {
|
shared_ptr<const TeamIndex::Team> TeamIndex::create(const string& name, uint32_t master_account_id, const string& master_name) {
|
||||||
auto team = make_shared<Team>(this->next_team_id++);
|
auto team = make_shared<Team>(this->next_team_id++);
|
||||||
save_file(this->directory + "/base.json", JSON::dict({{"NextTeamID", this->next_team_id}}).serialize());
|
save_file(this->directory + "/base.json", JSON::dict({{"NextTeamID", this->next_team_id}}).serialize());
|
||||||
|
|
||||||
Team::Member m;
|
Team::Member m;
|
||||||
m.account_id = master_account_id;
|
m.account_id = master_account_id;
|
||||||
m.flags = 0;
|
m.flags = 0;
|
||||||
m.points = 0;
|
m.points = 0;
|
||||||
m.name = master_name;
|
m.name = master_name;
|
||||||
m.set_flag(Team::Member::Flag::IS_MASTER);
|
m.set_flag(Team::Member::Flag::IS_MASTER);
|
||||||
team->members.emplace(master_account_id, std::move(m));
|
team->members.emplace(master_account_id, std::move(m));
|
||||||
team->name = name;
|
team->name = name;
|
||||||
|
|
||||||
team->save_config();
|
team->save_config();
|
||||||
this->add_to_indexes(team);
|
this->add_to_indexes(team);
|
||||||
return team;
|
return team;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::disband(uint32_t team_id) {
|
void TeamIndex::disband(uint32_t team_id) {
|
||||||
auto team = this->id_to_team.at(team_id);
|
auto team = this->id_to_team.at(team_id);
|
||||||
this->remove_from_indexes(team);
|
this->remove_from_indexes(team);
|
||||||
team->delete_files();
|
team->delete_files();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::rename(uint32_t team_id, const std::string& new_team_name) {
|
void TeamIndex::rename(uint32_t team_id, const std::string& new_team_name) {
|
||||||
auto team = this->id_to_team.at(team_id);
|
auto team = this->id_to_team.at(team_id);
|
||||||
if (!this->name_to_team.emplace(new_team_name, team).second) {
|
if (!this->name_to_team.emplace(new_team_name, team).second) {
|
||||||
throw runtime_error("team name is already in use");
|
throw runtime_error("team name is already in use");
|
||||||
}
|
}
|
||||||
this->name_to_team.erase(team->name);
|
this->name_to_team.erase(team->name);
|
||||||
team->name = new_team_name;
|
team->name = new_team_name;
|
||||||
team->save_config();
|
team->save_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::add_member(uint32_t team_id, uint32_t account_id, const string& name) {
|
void TeamIndex::add_member(uint32_t team_id, uint32_t account_id, const string& name) {
|
||||||
auto team = this->id_to_team.at(team_id);
|
auto team = this->id_to_team.at(team_id);
|
||||||
if (!this->account_id_to_team.emplace(account_id, team).second) {
|
if (!this->account_id_to_team.emplace(account_id, team).second) {
|
||||||
throw runtime_error("user is already in a different team");
|
throw runtime_error("user is already in a different team");
|
||||||
}
|
}
|
||||||
|
|
||||||
Team::Member m;
|
Team::Member m;
|
||||||
m.account_id = account_id;
|
m.account_id = account_id;
|
||||||
m.flags = 0;
|
m.flags = 0;
|
||||||
m.points = 0;
|
m.points = 0;
|
||||||
m.name = name;
|
m.name = name;
|
||||||
team->members.emplace(account_id, std::move(m));
|
team->members.emplace(account_id, std::move(m));
|
||||||
|
|
||||||
team->save_config();
|
team->save_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::remove_member(uint32_t account_id) {
|
void TeamIndex::remove_member(uint32_t account_id) {
|
||||||
auto team_it = this->account_id_to_team.find(account_id);
|
auto team_it = this->account_id_to_team.find(account_id);
|
||||||
if (team_it == this->account_id_to_team.end()) {
|
if (team_it == this->account_id_to_team.end()) {
|
||||||
throw runtime_error("client is not in any team");
|
throw runtime_error("client is not in any team");
|
||||||
}
|
}
|
||||||
auto team = std::move(team_it->second);
|
auto team = std::move(team_it->second);
|
||||||
this->account_id_to_team.erase(team_it);
|
this->account_id_to_team.erase(team_it);
|
||||||
team->members.erase(account_id);
|
team->members.erase(account_id);
|
||||||
if (team->members.empty()) {
|
if (team->members.empty()) {
|
||||||
this->disband(team->team_id);
|
this->disband(team->team_id);
|
||||||
} else {
|
} else {
|
||||||
team->save_config();
|
team->save_config();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::update_member_name(uint32_t account_id, const std::string& name) {
|
void TeamIndex::update_member_name(uint32_t account_id, const std::string& name) {
|
||||||
auto team = this->account_id_to_team.at(account_id);
|
auto team = this->account_id_to_team.at(account_id);
|
||||||
auto& m = team->members.at(account_id);
|
auto& m = team->members.at(account_id);
|
||||||
m.name = name;
|
m.name = name;
|
||||||
team->save_config();
|
team->save_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::add_member_points(uint32_t account_id, uint32_t points) {
|
void TeamIndex::add_member_points(uint32_t account_id, uint32_t points) {
|
||||||
auto team = this->account_id_to_team.at(account_id);
|
auto team = this->account_id_to_team.at(account_id);
|
||||||
auto& m = team->members.at(account_id);
|
auto& m = team->members.at(account_id);
|
||||||
m.points += points;
|
m.points += points;
|
||||||
team->points += points;
|
team->points += points;
|
||||||
team->save_config();
|
team->save_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::set_flag_data(uint32_t team_id, const parray<le_uint16_t, 0x20 * 0x20>& flag_data) {
|
void TeamIndex::set_flag_data(uint32_t team_id, const parray<le_uint16_t, 0x20 * 0x20>& flag_data) {
|
||||||
auto team = this->id_to_team.at(team_id);
|
auto team = this->id_to_team.at(team_id);
|
||||||
team->flag_data.reset(new parray<le_uint16_t, 0x20 * 0x20>(flag_data));
|
team->flag_data.reset(new parray<le_uint16_t, 0x20 * 0x20>(flag_data));
|
||||||
team->save_flag();
|
team->save_flag();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TeamIndex::promote_leader(uint32_t master_account_id, uint32_t leader_account_id) {
|
bool TeamIndex::promote_leader(uint32_t master_account_id, uint32_t leader_account_id) {
|
||||||
auto team = this->account_id_to_team.at(master_account_id);
|
auto team = this->account_id_to_team.at(master_account_id);
|
||||||
auto& master_m = team->members.at(master_account_id);
|
auto& master_m = team->members.at(master_account_id);
|
||||||
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
|
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
|
||||||
throw runtime_error("incorrect master account ID");
|
throw runtime_error("incorrect master account ID");
|
||||||
}
|
}
|
||||||
auto& other_m = team->members.at(leader_account_id);
|
auto& other_m = team->members.at(leader_account_id);
|
||||||
|
|
||||||
if (other_m.check_flag(TeamIndex::Team::Member::Flag::IS_LEADER) || !team->can_promote_leader()) {
|
if (other_m.check_flag(TeamIndex::Team::Member::Flag::IS_LEADER) || !team->can_promote_leader()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
other_m.set_flag(TeamIndex::Team::Member::Flag::IS_LEADER);
|
other_m.set_flag(TeamIndex::Team::Member::Flag::IS_LEADER);
|
||||||
team->save_config();
|
team->save_config();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TeamIndex::demote_leader(uint32_t master_account_id, uint32_t leader_account_id) {
|
bool TeamIndex::demote_leader(uint32_t master_account_id, uint32_t leader_account_id) {
|
||||||
auto team = this->account_id_to_team.at(master_account_id);
|
auto team = this->account_id_to_team.at(master_account_id);
|
||||||
auto& master_m = team->members.at(master_account_id);
|
auto& master_m = team->members.at(master_account_id);
|
||||||
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
|
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
|
||||||
throw runtime_error("incorrect master account ID");
|
throw runtime_error("incorrect master account ID");
|
||||||
}
|
}
|
||||||
auto& other_m = team->members.at(leader_account_id);
|
auto& other_m = team->members.at(leader_account_id);
|
||||||
|
|
||||||
if (!other_m.check_flag(TeamIndex::Team::Member::Flag::IS_LEADER)) {
|
if (!other_m.check_flag(TeamIndex::Team::Member::Flag::IS_LEADER)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
other_m.clear_flag(TeamIndex::Team::Member::Flag::IS_LEADER);
|
other_m.clear_flag(TeamIndex::Team::Member::Flag::IS_LEADER);
|
||||||
team->save_config();
|
team->save_config();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::change_master(uint32_t master_account_id, uint32_t new_master_account_id) {
|
void TeamIndex::change_master(uint32_t master_account_id, uint32_t new_master_account_id) {
|
||||||
auto team = this->account_id_to_team.at(master_account_id);
|
auto team = this->account_id_to_team.at(master_account_id);
|
||||||
auto& master_m = team->members.at(master_account_id);
|
auto& master_m = team->members.at(master_account_id);
|
||||||
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
|
if (!master_m.check_flag(TeamIndex::Team::Member::Flag::IS_MASTER)) {
|
||||||
throw runtime_error("incorrect master account ID");
|
throw runtime_error("incorrect master account ID");
|
||||||
}
|
}
|
||||||
auto& new_master_m = team->members.at(new_master_account_id);
|
auto& new_master_m = team->members.at(new_master_account_id);
|
||||||
|
|
||||||
master_m.clear_flag(TeamIndex::Team::Member::Flag::IS_MASTER);
|
master_m.clear_flag(TeamIndex::Team::Member::Flag::IS_MASTER);
|
||||||
master_m.set_flag(TeamIndex::Team::Member::Flag::IS_LEADER);
|
master_m.set_flag(TeamIndex::Team::Member::Flag::IS_LEADER);
|
||||||
new_master_m.clear_flag(TeamIndex::Team::Member::Flag::IS_LEADER);
|
new_master_m.clear_flag(TeamIndex::Team::Member::Flag::IS_LEADER);
|
||||||
new_master_m.set_flag(TeamIndex::Team::Member::Flag::IS_MASTER);
|
new_master_m.set_flag(TeamIndex::Team::Member::Flag::IS_MASTER);
|
||||||
team->master_account_id = new_master_account_id;
|
team->master_account_id = new_master_account_id;
|
||||||
team->save_config();
|
team->save_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::buy_reward(uint32_t team_id, const string& key, uint32_t points, Team::RewardFlag reward_flag) {
|
void TeamIndex::buy_reward(uint32_t team_id, const string& key, uint32_t points, Team::RewardFlag reward_flag) {
|
||||||
auto team = this->id_to_team.at(team_id);
|
auto team = this->id_to_team.at(team_id);
|
||||||
if (team->spent_points + points > team->points) {
|
if (team->spent_points + points > team->points) {
|
||||||
throw runtime_error("not enough points available");
|
throw runtime_error("not enough points available");
|
||||||
}
|
}
|
||||||
team->reward_keys.emplace(key);
|
team->reward_keys.emplace(key);
|
||||||
team->spent_points += points;
|
team->spent_points += points;
|
||||||
if (reward_flag != Team::RewardFlag::NONE) {
|
if (reward_flag != Team::RewardFlag::NONE) {
|
||||||
team->set_reward_flag(reward_flag);
|
team->set_reward_flag(reward_flag);
|
||||||
}
|
}
|
||||||
team->save_config();
|
team->save_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::add_to_indexes(shared_ptr<Team> team) {
|
void TeamIndex::add_to_indexes(shared_ptr<Team> team) {
|
||||||
if (!this->id_to_team.emplace(team->team_id, team).second) {
|
if (!this->id_to_team.emplace(team->team_id, team).second) {
|
||||||
throw runtime_error("team ID is already in use");
|
throw runtime_error("team ID is already in use");
|
||||||
}
|
}
|
||||||
if (!this->name_to_team.emplace(team->name, team).second) {
|
if (!this->name_to_team.emplace(team->name, team).second) {
|
||||||
this->id_to_team.erase(team->team_id);
|
this->id_to_team.erase(team->team_id);
|
||||||
throw runtime_error("team name is already in use");
|
throw runtime_error("team name is already in use");
|
||||||
}
|
}
|
||||||
for (const auto& it : team->members) {
|
for (const auto& it : team->members) {
|
||||||
if (!this->account_id_to_team.emplace(it.second.account_id, team).second) {
|
if (!this->account_id_to_team.emplace(it.second.account_id, team).second) {
|
||||||
static_game_data_log.warning("Serial number %08" PRIX32 " (%010" PRIu32 ") exists in multiple teams",
|
static_game_data_log.warning("Serial number %08" PRIX32 " (%010" PRIu32 ") exists in multiple teams",
|
||||||
it.second.account_id, it.second.account_id);
|
it.second.account_id, it.second.account_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeamIndex::remove_from_indexes(shared_ptr<Team> team) {
|
void TeamIndex::remove_from_indexes(shared_ptr<Team> team) {
|
||||||
this->id_to_team.erase(team->team_id);
|
this->id_to_team.erase(team->team_id);
|
||||||
this->name_to_team.erase(team->name);
|
this->name_to_team.erase(team->name);
|
||||||
for (const auto& it : team->members) {
|
for (const auto& it : team->members) {
|
||||||
this->account_id_to_team.erase(it.second.account_id);
|
this->account_id_to_team.erase(it.second.account_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+155
-155
@@ -1,155 +1,155 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <phosg/JSON.hh>
|
#include <phosg/JSON.hh>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ItemNameIndex.hh"
|
#include "ItemNameIndex.hh"
|
||||||
#include "SaveFileFormats.hh"
|
#include "SaveFileFormats.hh"
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
class TeamIndex {
|
class TeamIndex {
|
||||||
public:
|
public:
|
||||||
struct Team {
|
struct Team {
|
||||||
struct Member {
|
struct Member {
|
||||||
enum class Flag {
|
enum class Flag {
|
||||||
IS_MASTER = 0x01,
|
IS_MASTER = 0x01,
|
||||||
IS_LEADER = 0x02,
|
IS_LEADER = 0x02,
|
||||||
};
|
};
|
||||||
uint32_t account_id = 0;
|
uint32_t account_id = 0;
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
uint64_t points = 0;
|
uint64_t points = 0;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
Member() = default;
|
Member() = default;
|
||||||
explicit Member(const JSON& json);
|
explicit Member(const JSON& json);
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
|
|
||||||
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
||||||
return !!(static_cast<uint8_t>(flag) & this->flags);
|
return !!(static_cast<uint8_t>(flag) & this->flags);
|
||||||
}
|
}
|
||||||
inline void set_flag(Flag flag) {
|
inline void set_flag(Flag flag) {
|
||||||
this->flags |= static_cast<uint8_t>(flag);
|
this->flags |= static_cast<uint8_t>(flag);
|
||||||
}
|
}
|
||||||
inline void clear_flag(Flag flag) {
|
inline void clear_flag(Flag flag) {
|
||||||
this->flags &= (~static_cast<uint8_t>(flag));
|
this->flags &= (~static_cast<uint8_t>(flag));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t privilege_level() const;
|
uint32_t privilege_level() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class RewardFlag {
|
enum class RewardFlag {
|
||||||
// Only 0x00000001 and 0x00000002 are used by the client; the rest are
|
// Only 0x00000001 and 0x00000002 are used by the client; the rest are
|
||||||
// free to be used however the server chooses.
|
// free to be used however the server chooses.
|
||||||
NONE = 0x00000000,
|
NONE = 0x00000000,
|
||||||
TEAM_FLAG = 0x00000001,
|
TEAM_FLAG = 0x00000001,
|
||||||
DRESSING_ROOM = 0x00000002,
|
DRESSING_ROOM = 0x00000002,
|
||||||
MEMBERS_20_LEADERS_3 = 0x00000004,
|
MEMBERS_20_LEADERS_3 = 0x00000004,
|
||||||
MEMBERS_40_LEADERS_5 = 0x00000008,
|
MEMBERS_40_LEADERS_5 = 0x00000008,
|
||||||
MEMBERS_70_LEADERS_8 = 0x00000010,
|
MEMBERS_70_LEADERS_8 = 0x00000010,
|
||||||
MEMBERS_100_LEADERS_10 = 0x00000020,
|
MEMBERS_100_LEADERS_10 = 0x00000020,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t team_id = 0;
|
uint32_t team_id = 0;
|
||||||
uint32_t points = 0;
|
uint32_t points = 0;
|
||||||
uint32_t spent_points = 0;
|
uint32_t spent_points = 0;
|
||||||
std::string name;
|
std::string name;
|
||||||
uint32_t master_account_id = 0;
|
uint32_t master_account_id = 0;
|
||||||
std::unordered_map<uint32_t, Member> members;
|
std::unordered_map<uint32_t, Member> members;
|
||||||
uint32_t reward_flags = 0;
|
uint32_t reward_flags = 0;
|
||||||
std::unordered_set<std::string> reward_keys;
|
std::unordered_set<std::string> reward_keys;
|
||||||
std::shared_ptr<parray<le_uint16_t, 0x20 * 0x20>> flag_data;
|
std::shared_ptr<parray<le_uint16_t, 0x20 * 0x20>> flag_data;
|
||||||
|
|
||||||
Team() = default;
|
Team() = default;
|
||||||
explicit Team(uint32_t team_id);
|
explicit Team(uint32_t team_id);
|
||||||
JSON json() const;
|
JSON json() const;
|
||||||
|
|
||||||
std::string json_filename() const;
|
std::string json_filename() const;
|
||||||
std::string flag_filename() const;
|
std::string flag_filename() const;
|
||||||
|
|
||||||
void load_config();
|
void load_config();
|
||||||
void save_config() const;
|
void save_config() const;
|
||||||
void load_flag();
|
void load_flag();
|
||||||
void save_flag() const;
|
void save_flag() const;
|
||||||
void delete_files() const;
|
void delete_files() const;
|
||||||
|
|
||||||
PSOBBTeamMembership membership_for_member(uint32_t account_id) const;
|
PSOBBTeamMembership membership_for_member(uint32_t account_id) const;
|
||||||
|
|
||||||
[[nodiscard]] inline bool check_reward_flag(RewardFlag flag) const {
|
[[nodiscard]] inline bool check_reward_flag(RewardFlag flag) const {
|
||||||
return !!(static_cast<uint8_t>(flag) & this->reward_flags);
|
return !!(static_cast<uint8_t>(flag) & this->reward_flags);
|
||||||
}
|
}
|
||||||
inline void set_reward_flag(RewardFlag flag) {
|
inline void set_reward_flag(RewardFlag flag) {
|
||||||
this->reward_flags |= static_cast<uint8_t>(flag);
|
this->reward_flags |= static_cast<uint8_t>(flag);
|
||||||
}
|
}
|
||||||
inline void clear_reward_flag(RewardFlag flag) {
|
inline void clear_reward_flag(RewardFlag flag) {
|
||||||
this->reward_flags &= (~static_cast<uint8_t>(flag));
|
this->reward_flags &= (~static_cast<uint8_t>(flag));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool has_reward(const std::string& key) const;
|
[[nodiscard]] bool has_reward(const std::string& key) const;
|
||||||
|
|
||||||
size_t num_members() const;
|
size_t num_members() const;
|
||||||
size_t num_leaders() const;
|
size_t num_leaders() const;
|
||||||
size_t max_members() const;
|
size_t max_members() const;
|
||||||
size_t max_leaders() const;
|
size_t max_leaders() const;
|
||||||
bool can_add_member() const;
|
bool can_add_member() const;
|
||||||
bool can_promote_leader() const;
|
bool can_promote_leader() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Reward {
|
struct Reward {
|
||||||
uint32_t menu_item_id = 0;
|
uint32_t menu_item_id = 0;
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string description;
|
std::string description;
|
||||||
std::unordered_set<std::string> prerequisite_keys;
|
std::unordered_set<std::string> prerequisite_keys;
|
||||||
bool is_unique = true;
|
bool is_unique = true;
|
||||||
uint32_t team_points = 0;
|
uint32_t team_points = 0;
|
||||||
Team::RewardFlag reward_flag = Team::RewardFlag::NONE;
|
Team::RewardFlag reward_flag = Team::RewardFlag::NONE;
|
||||||
ItemData reward_item;
|
ItemData reward_item;
|
||||||
|
|
||||||
Reward(uint32_t menu_item_id, const JSON& def_json);
|
Reward(uint32_t menu_item_id, const JSON& def_json);
|
||||||
};
|
};
|
||||||
|
|
||||||
TeamIndex(const std::string& directory, const JSON& reward_defs_json);
|
TeamIndex(const std::string& directory, const JSON& reward_defs_json);
|
||||||
~TeamIndex() = default;
|
~TeamIndex() = default;
|
||||||
|
|
||||||
inline const std::vector<Reward>& reward_definitions() const {
|
inline const std::vector<Reward>& reward_definitions() const {
|
||||||
return this->reward_defs;
|
return this->reward_defs;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t count() const;
|
size_t count() const;
|
||||||
std::shared_ptr<const Team> get_by_id(uint32_t team_id) const;
|
std::shared_ptr<const Team> get_by_id(uint32_t team_id) const;
|
||||||
std::shared_ptr<const Team> get_by_name(const std::string& name) const;
|
std::shared_ptr<const Team> get_by_name(const std::string& name) const;
|
||||||
std::shared_ptr<const Team> get_by_account_id(uint32_t account_id) const;
|
std::shared_ptr<const Team> get_by_account_id(uint32_t account_id) const;
|
||||||
std::vector<std::shared_ptr<const Team>> all() const;
|
std::vector<std::shared_ptr<const Team>> all() const;
|
||||||
|
|
||||||
std::shared_ptr<const Team> create(const std::string& name, uint32_t master_account_id, const std::string& master_name);
|
std::shared_ptr<const Team> create(const std::string& name, uint32_t master_account_id, const std::string& master_name);
|
||||||
void disband(uint32_t team_id);
|
void disband(uint32_t team_id);
|
||||||
void rename(uint32_t team_id, const std::string& new_name);
|
void rename(uint32_t team_id, const std::string& new_name);
|
||||||
|
|
||||||
void add_member(uint32_t team_id, uint32_t account_id, const std::string& name);
|
void add_member(uint32_t team_id, uint32_t account_id, const std::string& name);
|
||||||
void remove_member(uint32_t account_id);
|
void remove_member(uint32_t account_id);
|
||||||
void update_member_name(uint32_t account_id, const std::string& name);
|
void update_member_name(uint32_t account_id, const std::string& name);
|
||||||
void add_member_points(uint32_t account_id, uint32_t points);
|
void add_member_points(uint32_t account_id, uint32_t points);
|
||||||
void set_flag_data(uint32_t team_id, const parray<le_uint16_t, 0x20 * 0x20>& flag_data);
|
void set_flag_data(uint32_t team_id, const parray<le_uint16_t, 0x20 * 0x20>& flag_data);
|
||||||
bool promote_leader(uint32_t master_account_id, uint32_t leader_account_id);
|
bool promote_leader(uint32_t master_account_id, uint32_t leader_account_id);
|
||||||
bool demote_leader(uint32_t master_account_id, uint32_t leader_account_id);
|
bool demote_leader(uint32_t master_account_id, uint32_t leader_account_id);
|
||||||
void change_master(uint32_t master_account_id, uint32_t new_master_account_id);
|
void change_master(uint32_t master_account_id, uint32_t new_master_account_id);
|
||||||
void buy_reward(uint32_t team_id, const std::string& key, uint32_t points, Team::RewardFlag reward_flag);
|
void buy_reward(uint32_t team_id, const std::string& key, uint32_t points, Team::RewardFlag reward_flag);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string directory;
|
std::string directory;
|
||||||
uint32_t next_team_id;
|
uint32_t next_team_id;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<Team>> id_to_team;
|
std::unordered_map<uint32_t, std::shared_ptr<Team>> id_to_team;
|
||||||
std::unordered_map<std::string, std::shared_ptr<Team>> name_to_team;
|
std::unordered_map<std::string, std::shared_ptr<Team>> name_to_team;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<Team>> account_id_to_team;
|
std::unordered_map<uint32_t, std::shared_ptr<Team>> account_id_to_team;
|
||||||
std::vector<Reward> reward_defs;
|
std::vector<Reward> reward_defs;
|
||||||
|
|
||||||
void add_to_indexes(std::shared_ptr<Team> team);
|
void add_to_indexes(std::shared_ptr<Team> team);
|
||||||
void remove_from_indexes(std::shared_ptr<Team> team);
|
void remove_from_indexes(std::shared_ptr<Team> team);
|
||||||
};
|
};
|
||||||
|
|||||||
+516
-516
File diff suppressed because it is too large
Load Diff
+724
-724
File diff suppressed because it is too large
Load Diff
+345
-345
@@ -1,345 +1,345 @@
|
|||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "Client.hh"
|
#include "Client.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const char* login_port_name_for_version(Version v) {
|
const char* login_port_name_for_version(Version v) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
return "pc-patch";
|
return "pc-patch";
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
return "bb-patch";
|
return "bb-patch";
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return "console-login";
|
return "console-login";
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return "pc-login";
|
return "pc-login";
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return "xb-login";
|
return "xb-login";
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return "bb-init";
|
return "bb-init";
|
||||||
default:
|
default:
|
||||||
throw runtime_error("unknown version");
|
throw runtime_error("unknown version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* lobby_port_name_for_version(Version v) {
|
const char* lobby_port_name_for_version(Version v) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
return "pc-patch";
|
return "pc-patch";
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
return "bb-patch";
|
return "bb-patch";
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return "console-lobby";
|
return "console-lobby";
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return "pc-lobby";
|
return "pc-lobby";
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return "xb-lobby";
|
return "xb-lobby";
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return "bb-lobby";
|
return "bb-lobby";
|
||||||
default:
|
default:
|
||||||
throw runtime_error("unknown version");
|
throw runtime_error("unknown version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* proxy_port_name_for_version(Version v) {
|
const char* proxy_port_name_for_version(Version v) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
return "pc-patch";
|
return "pc-patch";
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
return "bb-patch";
|
return "bb-patch";
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
return "dc-proxy";
|
return "dc-proxy";
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return "gc-proxy";
|
return "gc-proxy";
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return "pc-proxy";
|
return "pc-proxy";
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return "xb-proxy";
|
return "xb-proxy";
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return "bb-proxy";
|
return "bb-proxy";
|
||||||
default:
|
default:
|
||||||
throw runtime_error("unknown version");
|
throw runtime_error("unknown version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const char* name_for_enum<Version>(Version v) {
|
const char* name_for_enum<Version>(Version v) {
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
return "PC_PATCH";
|
return "PC_PATCH";
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
return "BB_PATCH";
|
return "BB_PATCH";
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
return "DC_NTE";
|
return "DC_NTE";
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
return "DC_V1_11_2000_PROTOTYPE";
|
return "DC_V1_11_2000_PROTOTYPE";
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
return "DC_V1";
|
return "DC_V1";
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
return "DC_V2";
|
return "DC_V2";
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
return "PC_NTE";
|
return "PC_NTE";
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return "PC_V2";
|
return "PC_V2";
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
return "GC_NTE";
|
return "GC_NTE";
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
return "GC_V3";
|
return "GC_V3";
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
return "GC_EP3_NTE";
|
return "GC_EP3_NTE";
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return "GC_EP3";
|
return "GC_EP3";
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return "XB_V3";
|
return "XB_V3";
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return "BB_V4";
|
return "BB_V4";
|
||||||
default:
|
default:
|
||||||
throw runtime_error("unknown version");
|
throw runtime_error("unknown version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
Version enum_for_name<Version>(const char* name) {
|
Version enum_for_name<Version>(const char* name) {
|
||||||
if (!strcmp(name, "PC_PATCH") || !strcasecmp(name, "patch")) {
|
if (!strcmp(name, "PC_PATCH") || !strcasecmp(name, "patch")) {
|
||||||
return Version::PC_PATCH;
|
return Version::PC_PATCH;
|
||||||
} else if (!strcmp(name, "BB_PATCH")) {
|
} else if (!strcmp(name, "BB_PATCH")) {
|
||||||
return Version::BB_PATCH;
|
return Version::BB_PATCH;
|
||||||
} else if (!strcmp(name, "DC_NTE")) {
|
} else if (!strcmp(name, "DC_NTE")) {
|
||||||
return Version::DC_NTE;
|
return Version::DC_NTE;
|
||||||
} else if (!strcmp(name, "DC_V1_11_2000_PROTOTYPE")) {
|
} else if (!strcmp(name, "DC_V1_11_2000_PROTOTYPE")) {
|
||||||
return Version::DC_V1_11_2000_PROTOTYPE;
|
return Version::DC_V1_11_2000_PROTOTYPE;
|
||||||
} else if (!strcmp(name, "DC_V1")) {
|
} else if (!strcmp(name, "DC_V1")) {
|
||||||
return Version::DC_V1;
|
return Version::DC_V1;
|
||||||
} else if (!strcmp(name, "DC_V2") || !strcasecmp(name, "dc")) {
|
} else if (!strcmp(name, "DC_V2") || !strcasecmp(name, "dc")) {
|
||||||
return Version::DC_V2;
|
return Version::DC_V2;
|
||||||
} else if (!strcmp(name, "PC_NTE")) {
|
} else if (!strcmp(name, "PC_NTE")) {
|
||||||
return Version::PC_NTE;
|
return Version::PC_NTE;
|
||||||
} else if (!strcmp(name, "PC_V2") || !strcasecmp(name, "pc")) {
|
} else if (!strcmp(name, "PC_V2") || !strcasecmp(name, "pc")) {
|
||||||
return Version::PC_V2;
|
return Version::PC_V2;
|
||||||
} else if (!strcmp(name, "GC_NTE")) {
|
} else if (!strcmp(name, "GC_NTE")) {
|
||||||
return Version::GC_NTE;
|
return Version::GC_NTE;
|
||||||
} else if (!strcmp(name, "GC_V3") || !strcasecmp(name, "gc")) {
|
} else if (!strcmp(name, "GC_V3") || !strcasecmp(name, "gc")) {
|
||||||
return Version::GC_V3;
|
return Version::GC_V3;
|
||||||
} else if (!strcmp(name, "GC_EP3_NTE")) {
|
} else if (!strcmp(name, "GC_EP3_NTE")) {
|
||||||
return Version::GC_EP3_NTE;
|
return Version::GC_EP3_NTE;
|
||||||
} else if (!strcmp(name, "GC_EP3")) {
|
} else if (!strcmp(name, "GC_EP3")) {
|
||||||
return Version::GC_EP3;
|
return Version::GC_EP3;
|
||||||
} else if (!strcmp(name, "XB_V3") || !strcasecmp(name, "xb")) {
|
} else if (!strcmp(name, "XB_V3") || !strcasecmp(name, "xb")) {
|
||||||
return Version::XB_V3;
|
return Version::XB_V3;
|
||||||
} else if (!strcmp(name, "BB_V4") || !strcasecmp(name, "bb")) {
|
} else if (!strcmp(name, "BB_V4") || !strcasecmp(name, "bb")) {
|
||||||
return Version::BB_V4;
|
return Version::BB_V4;
|
||||||
} else {
|
} else {
|
||||||
throw invalid_argument("incorrect version name");
|
throw invalid_argument("incorrect version name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const char* name_for_enum<ServerBehavior>(ServerBehavior behavior) {
|
const char* name_for_enum<ServerBehavior>(ServerBehavior behavior) {
|
||||||
switch (behavior) {
|
switch (behavior) {
|
||||||
case ServerBehavior::PC_CONSOLE_DETECT:
|
case ServerBehavior::PC_CONSOLE_DETECT:
|
||||||
return "pc_console_detect";
|
return "pc_console_detect";
|
||||||
case ServerBehavior::LOGIN_SERVER:
|
case ServerBehavior::LOGIN_SERVER:
|
||||||
return "login_server";
|
return "login_server";
|
||||||
case ServerBehavior::LOBBY_SERVER:
|
case ServerBehavior::LOBBY_SERVER:
|
||||||
return "lobby_server";
|
return "lobby_server";
|
||||||
case ServerBehavior::PATCH_SERVER_PC:
|
case ServerBehavior::PATCH_SERVER_PC:
|
||||||
return "patch_server_pc";
|
return "patch_server_pc";
|
||||||
case ServerBehavior::PATCH_SERVER_BB:
|
case ServerBehavior::PATCH_SERVER_BB:
|
||||||
return "patch_server_bb";
|
return "patch_server_bb";
|
||||||
case ServerBehavior::PROXY_SERVER:
|
case ServerBehavior::PROXY_SERVER:
|
||||||
return "proxy_server";
|
return "proxy_server";
|
||||||
}
|
}
|
||||||
throw logic_error("invalid server behavior");
|
throw logic_error("invalid server behavior");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
ServerBehavior enum_for_name<ServerBehavior>(const char* name) {
|
ServerBehavior enum_for_name<ServerBehavior>(const char* name) {
|
||||||
if (!strcasecmp(name, "pc_console_detect")) {
|
if (!strcasecmp(name, "pc_console_detect")) {
|
||||||
return ServerBehavior::PC_CONSOLE_DETECT;
|
return ServerBehavior::PC_CONSOLE_DETECT;
|
||||||
} else if (!strcasecmp(name, "login_server") || !strcasecmp(name, "login") || !strcasecmp(name, "data_server_bb")) {
|
} else if (!strcasecmp(name, "login_server") || !strcasecmp(name, "login") || !strcasecmp(name, "data_server_bb")) {
|
||||||
return ServerBehavior::LOGIN_SERVER;
|
return ServerBehavior::LOGIN_SERVER;
|
||||||
} else if (!strcasecmp(name, "lobby_server") || !strcasecmp(name, "lobby")) {
|
} else if (!strcasecmp(name, "lobby_server") || !strcasecmp(name, "lobby")) {
|
||||||
return ServerBehavior::LOBBY_SERVER;
|
return ServerBehavior::LOBBY_SERVER;
|
||||||
} else if (!strcasecmp(name, "patch_server_pc") || !strcasecmp(name, "patch_pc")) {
|
} else if (!strcasecmp(name, "patch_server_pc") || !strcasecmp(name, "patch_pc")) {
|
||||||
return ServerBehavior::PATCH_SERVER_PC;
|
return ServerBehavior::PATCH_SERVER_PC;
|
||||||
} else if (!strcasecmp(name, "patch_server_bb") || !strcasecmp(name, "patch_bb")) {
|
} else if (!strcasecmp(name, "patch_server_bb") || !strcasecmp(name, "patch_bb")) {
|
||||||
return ServerBehavior::PATCH_SERVER_BB;
|
return ServerBehavior::PATCH_SERVER_BB;
|
||||||
} else if (!strcasecmp(name, "proxy_server") || !strcasecmp(name, "proxy")) {
|
} else if (!strcasecmp(name, "proxy_server") || !strcasecmp(name, "proxy")) {
|
||||||
return ServerBehavior::PROXY_SERVER;
|
return ServerBehavior::PROXY_SERVER;
|
||||||
} else {
|
} else {
|
||||||
throw invalid_argument("incorrect server behavior name");
|
throw invalid_argument("incorrect server behavior name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t default_specific_version_for_version(Version version, int64_t sub_version) {
|
uint32_t default_specific_version_for_version(Version version, int64_t sub_version) {
|
||||||
// For versions that don't support send_function_call by default, we need
|
// For versions that don't support send_function_call by default, we need
|
||||||
// to set the specific_version based on sub_version. Fortunately, all
|
// to set the specific_version based on sub_version. Fortunately, all
|
||||||
// versions that share sub_version values also support send_function_call,
|
// versions that share sub_version values also support send_function_call,
|
||||||
// so for those versions we get the specific_version later by sending the
|
// so for those versions we get the specific_version later by sending the
|
||||||
// VersionDetectDC, VersionDetectGC, or VersionDetectXB call.
|
// VersionDetectDC, VersionDetectGC, or VersionDetectXB call.
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
return SPECIFIC_VERSION_DC_NTE; // 1OJ1
|
return SPECIFIC_VERSION_DC_NTE; // 1OJ1
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
return SPECIFIC_VERSION_DC_11_2000_PROTOTYPE; // 1OJ2
|
return SPECIFIC_VERSION_DC_11_2000_PROTOTYPE; // 1OJ2
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
return SPECIFIC_VERSION_DC_V1_INDETERMINATE; // 1___; need to send VersionDetectDC (but can't on V1; rip)
|
return SPECIFIC_VERSION_DC_V1_INDETERMINATE; // 1___; need to send VersionDetectDC (but can't on V1; rip)
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
return SPECIFIC_VERSION_DC_V2_INDETERMINATE; // 2___; need to send VersionDetectDC
|
return SPECIFIC_VERSION_DC_V2_INDETERMINATE; // 2___; need to send VersionDetectDC
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return SPECIFIC_VERSION_PC_V2; // 2OJW
|
return SPECIFIC_VERSION_PC_V2; // 2OJW
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
return SPECIFIC_VERSION_GC_NTE; // 3OJT
|
return SPECIFIC_VERSION_GC_NTE; // 3OJT
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
switch (sub_version) {
|
switch (sub_version) {
|
||||||
case 0x32: // GC Ep1&2 EU 50Hz
|
case 0x32: // GC Ep1&2 EU 50Hz
|
||||||
case 0x33: // GC Ep1&2 EU 60Hz
|
case 0x33: // GC Ep1&2 EU 60Hz
|
||||||
return SPECIFIC_VERSION_GC_V3_EU; // 3OP0
|
return SPECIFIC_VERSION_GC_V3_EU; // 3OP0
|
||||||
case 0x36: // GC Ep1&2 US v1.2 (Plus)
|
case 0x36: // GC Ep1&2 US v1.2 (Plus)
|
||||||
return SPECIFIC_VERSION_GC_V3_US_12; // 3OE2
|
return SPECIFIC_VERSION_GC_V3_US_12; // 3OE2
|
||||||
case 0x34: // GC Ep1&2 JP v1.3
|
case 0x34: // GC Ep1&2 JP v1.3
|
||||||
return SPECIFIC_VERSION_GC_V3_JP_13; // 3OJ3
|
return SPECIFIC_VERSION_GC_V3_JP_13; // 3OJ3
|
||||||
case 0x35: // GC Ep1&2 JP v1.4 (Plus)
|
case 0x35: // GC Ep1&2 JP v1.4 (Plus)
|
||||||
return SPECIFIC_VERSION_GC_V3_JP_14; // 3OJ4
|
return SPECIFIC_VERSION_GC_V3_JP_14; // 3OJ4
|
||||||
case 0x39: // GC Ep1&2 JP v1.5 (Plus)
|
case 0x39: // GC Ep1&2 JP v1.5 (Plus)
|
||||||
return SPECIFIC_VERSION_GC_V3_JP_15; // 3OJ5
|
return SPECIFIC_VERSION_GC_V3_JP_15; // 3OJ5
|
||||||
case -1: // Initial check (before sub_version recognition)
|
case -1: // Initial check (before sub_version recognition)
|
||||||
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 Trial Edition, GC Ep1&2 JP v1.2, at least one version of PSO XB
|
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 Trial Edition, GC Ep1&2 JP v1.2, at least one version of PSO XB
|
||||||
case 0x31: // GC Ep1&2 US v1.0, GC US v1.1, XB US
|
case 0x31: // GC Ep1&2 US v1.0, GC US v1.1, XB US
|
||||||
default:
|
default:
|
||||||
return SPECIFIC_VERSION_GC_V3_INDETERMINATE; // 3O__; need to send VersionDetectGC
|
return SPECIFIC_VERSION_GC_V3_INDETERMINATE; // 3O__; need to send VersionDetectGC
|
||||||
}
|
}
|
||||||
throw logic_error("this should be impossible");
|
throw logic_error("this should be impossible");
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
return SPECIFIC_VERSION_GC_EP3_NTE; // 3SJT
|
return SPECIFIC_VERSION_GC_EP3_NTE; // 3SJT
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
switch (sub_version) {
|
switch (sub_version) {
|
||||||
case 0x41: // GC Ep3 US
|
case 0x41: // GC Ep3 US
|
||||||
return SPECIFIC_VERSION_GC_EP3_US; // 3SE0
|
return SPECIFIC_VERSION_GC_EP3_US; // 3SE0
|
||||||
case 0x42: // GC Ep3 EU 50Hz
|
case 0x42: // GC Ep3 EU 50Hz
|
||||||
case 0x43: // GC Ep3 EU 60Hz
|
case 0x43: // GC Ep3 EU 60Hz
|
||||||
return SPECIFIC_VERSION_GC_EP3_EU; // 3SP0
|
return SPECIFIC_VERSION_GC_EP3_EU; // 3SP0
|
||||||
case -1: // Initial check (before sub_version recognition)
|
case -1: // Initial check (before sub_version recognition)
|
||||||
case 0x40: // GC Ep3 trial and GC Ep3 JP
|
case 0x40: // GC Ep3 trial and GC Ep3 JP
|
||||||
default:
|
default:
|
||||||
return SPECIFIC_VERSION_GC_EP3_INDETERMINATE; // 3SJ_; need to send VersionDetectGC
|
return SPECIFIC_VERSION_GC_EP3_INDETERMINATE; // 3SJ_; need to send VersionDetectGC
|
||||||
}
|
}
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return SPECIFIC_VERSION_XB_V3_INDETERMINATE; // 4O__; need to send VersionDetectXB
|
return SPECIFIC_VERSION_XB_V3_INDETERMINATE; // 4O__; need to send VersionDetectXB
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return SPECIFIC_VERSION_BB_V4_INDETERMINATE; // 5___; we should be able to determine version from initial login
|
return SPECIFIC_VERSION_BB_V4_INDETERMINATE; // 5___; we should be able to determine version from initial login
|
||||||
default:
|
default:
|
||||||
return SPECIFIC_VERSION_INDETERMINATE;
|
return SPECIFIC_VERSION_INDETERMINATE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool specific_version_is_indeterminate(uint32_t specific_version) {
|
bool specific_version_is_indeterminate(uint32_t specific_version) {
|
||||||
return ((specific_version & 0x000000FF) == 0);
|
return ((specific_version & 0x000000FF) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool specific_version_is_dc(uint32_t specific_version) {
|
bool specific_version_is_dc(uint32_t specific_version) {
|
||||||
// All v1 and v2 specific_versions are DC except 324F4A57 (2OJW), which is PC
|
// All v1 and v2 specific_versions are DC except 324F4A57 (2OJW), which is PC
|
||||||
uint8_t major_version = specific_version >> 24;
|
uint8_t major_version = specific_version >> 24;
|
||||||
if (major_version < 0x31 || major_version > 0x32) {
|
if (major_version < 0x31 || major_version > 0x32) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (specific_version != 0x324F4A57);
|
return (specific_version != 0x324F4A57);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool specific_version_is_gc(uint32_t specific_version) {
|
bool specific_version_is_gc(uint32_t specific_version) {
|
||||||
// GC specific_versions are 3GRV, where G is [OS], R is [JEP], V is [0-9T]
|
// GC specific_versions are 3GRV, where G is [OS], R is [JEP], V is [0-9T]
|
||||||
if ((specific_version & 0xFF000000) != 0x33000000) {
|
if ((specific_version & 0xFF000000) != 0x33000000) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char game = specific_version >> 16;
|
char game = specific_version >> 16;
|
||||||
if ((game != 'O') && (game != 'S')) {
|
if ((game != 'O') && (game != 'S')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char region = specific_version >> 8;
|
char region = specific_version >> 8;
|
||||||
if ((region != 'J') && (region != 'E') && (region != 'P')) {
|
if ((region != 'J') && (region != 'E') && (region != 'P')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char revision = specific_version;
|
char revision = specific_version;
|
||||||
return (isdigit(revision) || (revision == 'T'));
|
return (isdigit(revision) || (revision == 'T'));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool specific_version_is_xb(uint32_t specific_version) {
|
bool specific_version_is_xb(uint32_t specific_version) {
|
||||||
// XB specific_versions are 4ORV, where R is [JEP], V is [BDU]
|
// XB specific_versions are 4ORV, where R is [JEP], V is [BDU]
|
||||||
if ((specific_version & 0xFFFF0000) != 0x344F0000) {
|
if ((specific_version & 0xFFFF0000) != 0x344F0000) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char region = specific_version >> 8;
|
char region = specific_version >> 8;
|
||||||
if ((region != 'J') && (region != 'E') && (region != 'P')) {
|
if ((region != 'J') && (region != 'E') && (region != 'P')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char revision = specific_version;
|
char revision = specific_version;
|
||||||
return ((revision == 'B') || (revision == 'D') || (revision == 'U'));
|
return ((revision == 'B') || (revision == 'D') || (revision == 'U'));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool specific_version_is_bb(uint32_t specific_version) {
|
bool specific_version_is_bb(uint32_t specific_version) {
|
||||||
// BB specific_versions are 5XXX, where X is an encoding of the revision number
|
// BB specific_versions are 5XXX, where X is an encoding of the revision number
|
||||||
return (specific_version & 0xFF000000) == 0x35000000;
|
return (specific_version & 0xFF000000) == 0x35000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* file_path_token_for_version(Version version) {
|
const char* file_path_token_for_version(Version version) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::PC_PATCH:
|
case Version::PC_PATCH:
|
||||||
return "pc-patch";
|
return "pc-patch";
|
||||||
case Version::BB_PATCH:
|
case Version::BB_PATCH:
|
||||||
return "bb-patch";
|
return "bb-patch";
|
||||||
case Version::DC_NTE:
|
case Version::DC_NTE:
|
||||||
return "dc-nte";
|
return "dc-nte";
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
return "dc-11-2000";
|
return "dc-11-2000";
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
return "dc-v1";
|
return "dc-v1";
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
return "dc-v2";
|
return "dc-v2";
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
return "pc-nte";
|
return "pc-nte";
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
return "pc-v2";
|
return "pc-v2";
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
return "gc-nte";
|
return "gc-nte";
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
return "gc-v3";
|
return "gc-v3";
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
return "gc-ep3-nte";
|
return "gc-ep3-nte";
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
return "gc-ep3";
|
return "gc-ep3";
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
return "xb-v3";
|
return "xb-v3";
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
return "bb-v4";
|
return "bb-v4";
|
||||||
default:
|
default:
|
||||||
throw runtime_error("invalid game version");
|
throw runtime_error("invalid game version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+189
-189
@@ -1,189 +1,189 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <phosg/Types.hh>
|
#include <phosg/Types.hh>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
enum class Version {
|
enum class Version {
|
||||||
PC_PATCH = 0,
|
PC_PATCH = 0,
|
||||||
BB_PATCH = 1,
|
BB_PATCH = 1,
|
||||||
DC_NTE = 2,
|
DC_NTE = 2,
|
||||||
DC_V1_11_2000_PROTOTYPE = 3,
|
DC_V1_11_2000_PROTOTYPE = 3,
|
||||||
DC_V1 = 4,
|
DC_V1 = 4,
|
||||||
DC_V2 = 5,
|
DC_V2 = 5,
|
||||||
PC_NTE = 6,
|
PC_NTE = 6,
|
||||||
PC_V2 = 7,
|
PC_V2 = 7,
|
||||||
GC_NTE = 8,
|
GC_NTE = 8,
|
||||||
GC_V3 = 9,
|
GC_V3 = 9,
|
||||||
GC_EP3_NTE = 10,
|
GC_EP3_NTE = 10,
|
||||||
GC_EP3 = 11,
|
GC_EP3 = 11,
|
||||||
XB_V3 = 12,
|
XB_V3 = 12,
|
||||||
BB_V4 = 13,
|
BB_V4 = 13,
|
||||||
UNKNOWN = 15,
|
UNKNOWN = 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr size_t NUM_VERSIONS = static_cast<size_t>(Version::BB_V4) + 1;
|
constexpr size_t NUM_VERSIONS = static_cast<size_t>(Version::BB_V4) + 1;
|
||||||
constexpr size_t NUM_PATCH_VERSIONS = static_cast<size_t>(Version::BB_PATCH) + 1;
|
constexpr size_t NUM_PATCH_VERSIONS = static_cast<size_t>(Version::BB_PATCH) + 1;
|
||||||
constexpr size_t NUM_NON_PATCH_VERSIONS = NUM_VERSIONS - NUM_PATCH_VERSIONS;
|
constexpr size_t NUM_NON_PATCH_VERSIONS = NUM_VERSIONS - NUM_PATCH_VERSIONS;
|
||||||
|
|
||||||
static_assert(NUM_NON_PATCH_VERSIONS == 12, "Don't forget to update VersionNameColors in config.json");
|
static_assert(NUM_NON_PATCH_VERSIONS == 12, "Don't forget to update VersionNameColors in config.json");
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const char* name_for_enum<Version>(Version v);
|
const char* name_for_enum<Version>(Version v);
|
||||||
template <>
|
template <>
|
||||||
Version enum_for_name<Version>(const char* name);
|
Version enum_for_name<Version>(const char* name);
|
||||||
|
|
||||||
inline bool is_any_nte(Version version) {
|
inline bool is_any_nte(Version version) {
|
||||||
return (version == Version::DC_NTE) ||
|
return (version == Version::DC_NTE) ||
|
||||||
(version == Version::DC_V1_11_2000_PROTOTYPE) ||
|
(version == Version::DC_V1_11_2000_PROTOTYPE) ||
|
||||||
(version == Version::PC_NTE) ||
|
(version == Version::PC_NTE) ||
|
||||||
(version == Version::GC_NTE) ||
|
(version == Version::GC_NTE) ||
|
||||||
(version == Version::GC_EP3_NTE);
|
(version == Version::GC_EP3_NTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_patch(Version version) {
|
inline bool is_patch(Version version) {
|
||||||
return (version == Version::PC_PATCH) || (version == Version::BB_PATCH);
|
return (version == Version::PC_PATCH) || (version == Version::BB_PATCH);
|
||||||
}
|
}
|
||||||
inline bool is_pre_v1(Version version) {
|
inline bool is_pre_v1(Version version) {
|
||||||
return (version == Version::DC_NTE) || (version == Version::DC_V1_11_2000_PROTOTYPE);
|
return (version == Version::DC_NTE) || (version == Version::DC_V1_11_2000_PROTOTYPE);
|
||||||
}
|
}
|
||||||
inline bool is_v1(Version version) {
|
inline bool is_v1(Version version) {
|
||||||
return (version == Version::DC_NTE) || (version == Version::DC_V1_11_2000_PROTOTYPE) || (version == Version::DC_V1);
|
return (version == Version::DC_NTE) || (version == Version::DC_V1_11_2000_PROTOTYPE) || (version == Version::DC_V1);
|
||||||
}
|
}
|
||||||
inline bool is_v2(Version version) {
|
inline bool is_v2(Version version) {
|
||||||
return (version == Version::DC_V2) || (version == Version::PC_NTE) || (version == Version::PC_V2) || (version == Version::GC_NTE);
|
return (version == Version::DC_V2) || (version == Version::PC_NTE) || (version == Version::PC_V2) || (version == Version::GC_NTE);
|
||||||
}
|
}
|
||||||
inline bool is_v1_or_v2(Version version) {
|
inline bool is_v1_or_v2(Version version) {
|
||||||
return is_v1(version) || is_v2(version);
|
return is_v1(version) || is_v2(version);
|
||||||
}
|
}
|
||||||
inline bool is_v3(Version version) {
|
inline bool is_v3(Version version) {
|
||||||
return (version == Version::GC_V3) ||
|
return (version == Version::GC_V3) ||
|
||||||
(version == Version::GC_EP3_NTE) ||
|
(version == Version::GC_EP3_NTE) ||
|
||||||
(version == Version::GC_EP3) ||
|
(version == Version::GC_EP3) ||
|
||||||
(version == Version::XB_V3);
|
(version == Version::XB_V3);
|
||||||
}
|
}
|
||||||
inline bool is_v4(Version version) {
|
inline bool is_v4(Version version) {
|
||||||
return (version == Version::BB_V4);
|
return (version == Version::BB_V4);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_ep3(Version version) {
|
inline bool is_ep3(Version version) {
|
||||||
return (version == Version::GC_EP3_NTE) || (version == Version::GC_EP3);
|
return (version == Version::GC_EP3_NTE) || (version == Version::GC_EP3);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_dc(Version version) {
|
inline bool is_dc(Version version) {
|
||||||
return (version == Version::DC_NTE) ||
|
return (version == Version::DC_NTE) ||
|
||||||
(version == Version::DC_V1_11_2000_PROTOTYPE) ||
|
(version == Version::DC_V1_11_2000_PROTOTYPE) ||
|
||||||
(version == Version::DC_V1) ||
|
(version == Version::DC_V1) ||
|
||||||
(version == Version::DC_V2);
|
(version == Version::DC_V2);
|
||||||
}
|
}
|
||||||
inline bool is_gc(Version version) {
|
inline bool is_gc(Version version) {
|
||||||
return (version == Version::GC_NTE) ||
|
return (version == Version::GC_NTE) ||
|
||||||
(version == Version::GC_V3) ||
|
(version == Version::GC_V3) ||
|
||||||
(version == Version::GC_EP3_NTE) ||
|
(version == Version::GC_EP3_NTE) ||
|
||||||
(version == Version::GC_EP3);
|
(version == Version::GC_EP3);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_sh4(Version version) {
|
inline bool is_sh4(Version version) {
|
||||||
return (version == Version::DC_NTE) ||
|
return (version == Version::DC_NTE) ||
|
||||||
(version == Version::DC_V1_11_2000_PROTOTYPE) ||
|
(version == Version::DC_V1_11_2000_PROTOTYPE) ||
|
||||||
(version == Version::DC_V1) ||
|
(version == Version::DC_V1) ||
|
||||||
(version == Version::DC_V2);
|
(version == Version::DC_V2);
|
||||||
}
|
}
|
||||||
inline bool is_x86(Version version) {
|
inline bool is_x86(Version version) {
|
||||||
return (version == Version::PC_PATCH) ||
|
return (version == Version::PC_PATCH) ||
|
||||||
(version == Version::BB_PATCH) ||
|
(version == Version::BB_PATCH) ||
|
||||||
(version == Version::PC_NTE) ||
|
(version == Version::PC_NTE) ||
|
||||||
(version == Version::PC_V2) ||
|
(version == Version::PC_V2) ||
|
||||||
(version == Version::XB_V3) ||
|
(version == Version::XB_V3) ||
|
||||||
(version == Version::BB_V4);
|
(version == Version::BB_V4);
|
||||||
}
|
}
|
||||||
inline bool is_ppc(Version version) {
|
inline bool is_ppc(Version version) {
|
||||||
return (version == Version::GC_NTE) ||
|
return (version == Version::GC_NTE) ||
|
||||||
(version == Version::GC_V3) ||
|
(version == Version::GC_V3) ||
|
||||||
(version == Version::GC_EP3_NTE) ||
|
(version == Version::GC_EP3_NTE) ||
|
||||||
(version == Version::GC_EP3);
|
(version == Version::GC_EP3);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_big_endian(Version version) {
|
inline bool is_big_endian(Version version) {
|
||||||
return (version == Version::GC_NTE) ||
|
return (version == Version::GC_NTE) ||
|
||||||
(version == Version::GC_V3) ||
|
(version == Version::GC_V3) ||
|
||||||
(version == Version::GC_EP3_NTE) ||
|
(version == Version::GC_EP3_NTE) ||
|
||||||
(version == Version::GC_EP3);
|
(version == Version::GC_EP3);
|
||||||
}
|
}
|
||||||
inline bool uses_v2_encryption(Version version) {
|
inline bool uses_v2_encryption(Version version) {
|
||||||
return (version == Version::PC_PATCH) ||
|
return (version == Version::PC_PATCH) ||
|
||||||
(version == Version::BB_PATCH) ||
|
(version == Version::BB_PATCH) ||
|
||||||
(version == Version::DC_NTE) ||
|
(version == Version::DC_NTE) ||
|
||||||
(version == Version::DC_V1) ||
|
(version == Version::DC_V1) ||
|
||||||
(version == Version::DC_V2) ||
|
(version == Version::DC_V2) ||
|
||||||
(version == Version::PC_NTE) ||
|
(version == Version::PC_NTE) ||
|
||||||
(version == Version::PC_V2) ||
|
(version == Version::PC_V2) ||
|
||||||
(version == Version::GC_NTE);
|
(version == Version::GC_NTE);
|
||||||
}
|
}
|
||||||
inline bool uses_v3_encryption(Version version) {
|
inline bool uses_v3_encryption(Version version) {
|
||||||
return (version == Version::GC_V3) ||
|
return (version == Version::GC_V3) ||
|
||||||
(version == Version::GC_EP3_NTE) ||
|
(version == Version::GC_EP3_NTE) ||
|
||||||
(version == Version::GC_EP3) ||
|
(version == Version::GC_EP3) ||
|
||||||
(version == Version::XB_V3);
|
(version == Version::XB_V3);
|
||||||
}
|
}
|
||||||
inline bool uses_v4_encryption(Version version) {
|
inline bool uses_v4_encryption(Version version) {
|
||||||
return (version == Version::BB_V4);
|
return (version == Version::BB_V4);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool uses_utf16(Version version) {
|
inline bool uses_utf16(Version version) {
|
||||||
return (version == Version::PC_PATCH) ||
|
return (version == Version::PC_PATCH) ||
|
||||||
(version == Version::BB_PATCH) ||
|
(version == Version::BB_PATCH) ||
|
||||||
(version == Version::PC_NTE) ||
|
(version == Version::PC_NTE) ||
|
||||||
(version == Version::PC_V2) ||
|
(version == Version::PC_V2) ||
|
||||||
(version == Version::BB_V4);
|
(version == Version::BB_V4);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint32_t SPECIFIC_VERSION_DC_NTE = 0x314F4A31; // 1OJ1
|
constexpr uint32_t SPECIFIC_VERSION_DC_NTE = 0x314F4A31; // 1OJ1
|
||||||
constexpr uint32_t SPECIFIC_VERSION_DC_11_2000_PROTOTYPE = 0x314F4A32; // 1OJ2
|
constexpr uint32_t SPECIFIC_VERSION_DC_11_2000_PROTOTYPE = 0x314F4A32; // 1OJ2
|
||||||
constexpr uint32_t SPECIFIC_VERSION_DC_V1_INDETERMINATE = 0x31000000; // 1___
|
constexpr uint32_t SPECIFIC_VERSION_DC_V1_INDETERMINATE = 0x31000000; // 1___
|
||||||
constexpr uint32_t SPECIFIC_VERSION_DC_V2_INDETERMINATE = 0x32000000; // 2___
|
constexpr uint32_t SPECIFIC_VERSION_DC_V2_INDETERMINATE = 0x32000000; // 2___
|
||||||
constexpr uint32_t SPECIFIC_VERSION_PC_V2 = 0x324F4A57; // 2OJW
|
constexpr uint32_t SPECIFIC_VERSION_PC_V2 = 0x324F4A57; // 2OJW
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_NTE = 0x334F4A54; // 3OJT
|
constexpr uint32_t SPECIFIC_VERSION_GC_NTE = 0x334F4A54; // 3OJT
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_V3_EU = 0x334F5030; // 3OP0
|
constexpr uint32_t SPECIFIC_VERSION_GC_V3_EU = 0x334F5030; // 3OP0
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_V3_US_12 = 0x334F4532; // 3OE2
|
constexpr uint32_t SPECIFIC_VERSION_GC_V3_US_12 = 0x334F4532; // 3OE2
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_V3_JP_13 = 0x334F4A33; // 3OJ3
|
constexpr uint32_t SPECIFIC_VERSION_GC_V3_JP_13 = 0x334F4A33; // 3OJ3
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_V3_JP_14 = 0x334F4A34; // 3OJ4
|
constexpr uint32_t SPECIFIC_VERSION_GC_V3_JP_14 = 0x334F4A34; // 3OJ4
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_V3_JP_15 = 0x334F4A35; // 3OJ5
|
constexpr uint32_t SPECIFIC_VERSION_GC_V3_JP_15 = 0x334F4A35; // 3OJ5
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_V3_INDETERMINATE = 0x334F0000; // 3O__
|
constexpr uint32_t SPECIFIC_VERSION_GC_V3_INDETERMINATE = 0x334F0000; // 3O__
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_INDETERMINATE = 0x33534A00; // 3SJ_
|
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_INDETERMINATE = 0x33534A00; // 3SJ_
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_NTE = 0x33534A54; // 3SJT
|
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_NTE = 0x33534A54; // 3SJT
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_JP = 0x33534A30; // 3SJ0
|
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_JP = 0x33534A30; // 3SJ0
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_US = 0x33534530; // 3SE0
|
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_US = 0x33534530; // 3SE0
|
||||||
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_EU = 0x33535030; // 3SP0
|
constexpr uint32_t SPECIFIC_VERSION_GC_EP3_EU = 0x33535030; // 3SP0
|
||||||
constexpr uint32_t SPECIFIC_VERSION_XB_V3_INDETERMINATE = 0x344F0000; // 4O__
|
constexpr uint32_t SPECIFIC_VERSION_XB_V3_INDETERMINATE = 0x344F0000; // 4O__
|
||||||
constexpr uint32_t SPECIFIC_VERSION_BB_V4_INDETERMINATE = 0x35000000; // 5___
|
constexpr uint32_t SPECIFIC_VERSION_BB_V4_INDETERMINATE = 0x35000000; // 5___
|
||||||
constexpr uint32_t SPECIFIC_VERSION_INDETERMINATE = 0x00000000; // ____
|
constexpr uint32_t SPECIFIC_VERSION_INDETERMINATE = 0x00000000; // ____
|
||||||
|
|
||||||
uint32_t default_specific_version_for_version(Version version, int64_t sub_version);
|
uint32_t default_specific_version_for_version(Version version, int64_t sub_version);
|
||||||
bool specific_version_is_indeterminate(uint32_t specific_version);
|
bool specific_version_is_indeterminate(uint32_t specific_version);
|
||||||
bool specific_version_is_dc(uint32_t specific_version);
|
bool specific_version_is_dc(uint32_t specific_version);
|
||||||
bool specific_version_is_gc(uint32_t specific_version);
|
bool specific_version_is_gc(uint32_t specific_version);
|
||||||
bool specific_version_is_xb(uint32_t specific_version);
|
bool specific_version_is_xb(uint32_t specific_version);
|
||||||
bool specific_version_is_bb(uint32_t specific_version);
|
bool specific_version_is_bb(uint32_t specific_version);
|
||||||
|
|
||||||
enum class ServerBehavior {
|
enum class ServerBehavior {
|
||||||
PC_CONSOLE_DETECT = 0,
|
PC_CONSOLE_DETECT = 0,
|
||||||
LOGIN_SERVER,
|
LOGIN_SERVER,
|
||||||
LOBBY_SERVER,
|
LOBBY_SERVER,
|
||||||
PATCH_SERVER_PC,
|
PATCH_SERVER_PC,
|
||||||
PATCH_SERVER_BB,
|
PATCH_SERVER_BB,
|
||||||
PROXY_SERVER,
|
PROXY_SERVER,
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* login_port_name_for_version(Version v);
|
const char* login_port_name_for_version(Version v);
|
||||||
const char* lobby_port_name_for_version(Version v);
|
const char* lobby_port_name_for_version(Version v);
|
||||||
const char* proxy_port_name_for_version(Version v);
|
const char* proxy_port_name_for_version(Version v);
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const char* name_for_enum<ServerBehavior>(ServerBehavior behavior);
|
const char* name_for_enum<ServerBehavior>(ServerBehavior behavior);
|
||||||
template <>
|
template <>
|
||||||
ServerBehavior enum_for_name<ServerBehavior>(const char* name);
|
ServerBehavior enum_for_name<ServerBehavior>(const char* name);
|
||||||
|
|
||||||
const char* file_path_token_for_version(Version version);
|
const char* file_path_token_for_version(Version version);
|
||||||
|
|||||||
+319
-319
@@ -1,319 +1,319 @@
|
|||||||
#include "WordSelectTable.hh"
|
#include "WordSelectTable.hh"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Compression.hh"
|
#include "Compression.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
template <typename RetT, typename ReadT>
|
template <typename RetT, typename ReadT>
|
||||||
static vector<RetT> read_direct_table(const StringReader& base_r, size_t offset, size_t count) {
|
static vector<RetT> read_direct_table(const StringReader& base_r, size_t offset, size_t count) {
|
||||||
vector<RetT> ret;
|
vector<RetT> ret;
|
||||||
auto entries_r = base_r.sub(offset, count * sizeof(ReadT));
|
auto entries_r = base_r.sub(offset, count * sizeof(ReadT));
|
||||||
while (!entries_r.eof()) {
|
while (!entries_r.eof()) {
|
||||||
ret.emplace_back(entries_r.get<ReadT>());
|
ret.emplace_back(entries_r.get<ReadT>());
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename RetT, typename ReadT, typename OffsetT>
|
template <typename RetT, typename ReadT, typename OffsetT>
|
||||||
static vector<vector<RetT>> read_indirect_table(const StringReader& base_r, size_t offset, size_t count) {
|
static vector<vector<RetT>> read_indirect_table(const StringReader& base_r, size_t offset, size_t count) {
|
||||||
vector<vector<RetT>> ret;
|
vector<vector<RetT>> ret;
|
||||||
auto pointers_r = base_r.sub(offset, sizeof(OffsetT) * 2 * count);
|
auto pointers_r = base_r.sub(offset, sizeof(OffsetT) * 2 * count);
|
||||||
while (!pointers_r.eof()) {
|
while (!pointers_r.eof()) {
|
||||||
uint32_t sub_offset = pointers_r.get<OffsetT>();
|
uint32_t sub_offset = pointers_r.get<OffsetT>();
|
||||||
uint32_t sub_count = pointers_r.get<OffsetT>();
|
uint32_t sub_count = pointers_r.get<OffsetT>();
|
||||||
ret.emplace_back(read_direct_table<RetT, ReadT>(base_r, sub_offset, sub_count));
|
ret.emplace_back(read_direct_table<RetT, ReadT>(base_r, sub_offset, sub_count));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IsBigEndian>
|
template <bool IsBigEndian>
|
||||||
struct NonWindowsRootT {
|
struct NonWindowsRootT {
|
||||||
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;
|
||||||
U32T strings_table;
|
U32T strings_table;
|
||||||
U32T table1;
|
U32T table1;
|
||||||
U32T table2;
|
U32T table2;
|
||||||
U32T token_id_to_string_id_table;
|
U32T token_id_to_string_id_table;
|
||||||
U32T table4;
|
U32T table4;
|
||||||
U32T article_types_table;
|
U32T article_types_table;
|
||||||
U32T table6;
|
U32T table6;
|
||||||
} __packed__;
|
} __packed__;
|
||||||
|
|
||||||
using NonWindowsRoot = NonWindowsRootT<false>;
|
using NonWindowsRoot = NonWindowsRootT<false>;
|
||||||
using NonWindowsRootBE = NonWindowsRootT<true>;
|
using NonWindowsRootBE = NonWindowsRootT<true>;
|
||||||
check_struct_size(NonWindowsRoot, 0x1C);
|
check_struct_size(NonWindowsRoot, 0x1C);
|
||||||
check_struct_size(NonWindowsRootBE, 0x1C);
|
check_struct_size(NonWindowsRootBE, 0x1C);
|
||||||
|
|
||||||
struct PCV2Root {
|
struct PCV2Root {
|
||||||
le_uint32_t unknown_a1;
|
le_uint32_t unknown_a1;
|
||||||
le_uint32_t unknown_a2;
|
le_uint32_t unknown_a2;
|
||||||
le_uint32_t table1;
|
le_uint32_t table1;
|
||||||
le_uint32_t table2;
|
le_uint32_t table2;
|
||||||
le_uint32_t token_id_to_string_id_table;
|
le_uint32_t token_id_to_string_id_table;
|
||||||
le_uint32_t table4;
|
le_uint32_t table4;
|
||||||
le_uint32_t article_types_table;
|
le_uint32_t article_types_table;
|
||||||
le_uint32_t table6;
|
le_uint32_t table6;
|
||||||
} __packed_ws__(PCV2Root, 0x20);
|
} __packed_ws__(PCV2Root, 0x20);
|
||||||
|
|
||||||
struct BBRoot {
|
struct BBRoot {
|
||||||
le_uint32_t table1;
|
le_uint32_t table1;
|
||||||
le_uint32_t table2;
|
le_uint32_t table2;
|
||||||
le_uint32_t token_id_to_string_id_table;
|
le_uint32_t token_id_to_string_id_table;
|
||||||
le_uint32_t table4;
|
le_uint32_t table4;
|
||||||
le_uint32_t article_types_table;
|
le_uint32_t article_types_table;
|
||||||
le_uint32_t table6;
|
le_uint32_t table6;
|
||||||
} __packed_ws__(BBRoot, 0x18);
|
} __packed_ws__(BBRoot, 0x18);
|
||||||
|
|
||||||
template <bool IsBigEndian, size_t StringTableCount, size_t TokenCount>
|
template <bool IsBigEndian, size_t StringTableCount, size_t TokenCount>
|
||||||
void WordSelectSet::parse_non_windows_t(const std::string& data, bool use_sjis) {
|
void WordSelectSet::parse_non_windows_t(const std::string& data, bool use_sjis) {
|
||||||
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;
|
||||||
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
|
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
|
||||||
|
|
||||||
StringReader r(data);
|
StringReader r(data);
|
||||||
uint32_t root_offset = r.pget<U32T>(r.size() - 0x10);
|
uint32_t root_offset = r.pget<U32T>(r.size() - 0x10);
|
||||||
const auto& root = r.pget<NonWindowsRootT<IsBigEndian>>(root_offset);
|
const auto& root = r.pget<NonWindowsRootT<IsBigEndian>>(root_offset);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto string_offset_r = r.sub(root.strings_table, sizeof(U32T) * StringTableCount);
|
auto string_offset_r = r.sub(root.strings_table, sizeof(U32T) * StringTableCount);
|
||||||
while (!string_offset_r.eof()) {
|
while (!string_offset_r.eof()) {
|
||||||
string raw_s = r.pget_cstr(string_offset_r.template get<U32T>());
|
string raw_s = r.pget_cstr(string_offset_r.template get<U32T>());
|
||||||
this->strings.emplace_back(use_sjis ? tt_sega_sjis_to_utf8(raw_s) : tt_8859_to_utf8(raw_s));
|
this->strings.emplace_back(use_sjis ? tt_sega_sjis_to_utf8(raw_s) : tt_8859_to_utf8(raw_s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this->table1 = read_indirect_table<uint16_t, U16T, U32T>(r, root.table1, Table1Count);
|
// this->table1 = read_indirect_table<uint16_t, U16T, U32T>(r, root.table1, Table1Count);
|
||||||
// this->table2 = read_indirect_table<uint16_t, U16T, U32T>(r, root.table2, Table2Count);
|
// this->table2 = read_indirect_table<uint16_t, U16T, U32T>(r, root.table2, Table2Count);
|
||||||
this->token_id_to_string_id = read_direct_table<size_t, U16T>(r, root.token_id_to_string_id_table, TokenCount);
|
this->token_id_to_string_id = read_direct_table<size_t, U16T>(r, root.token_id_to_string_id_table, TokenCount);
|
||||||
// this->table4 = read_indirect_table<uint16_t, U16T, U32T>(r, root.table4, Table4Count);
|
// this->table4 = read_indirect_table<uint16_t, U16T, U32T>(r, root.table4, Table4Count);
|
||||||
// this->article_types = read_direct_table<uint8_t, uint8_t>(r, root.article_types_table, ArticleTypesCount);
|
// this->article_types = read_direct_table<uint8_t, uint8_t>(r, root.article_types_table, ArticleTypesCount);
|
||||||
// this->table6 = read_indirect_table<uint16_t, U16T, U32T>(r, root.table6, Table6Count);
|
// this->table6 = read_indirect_table<uint16_t, U16T, U32T>(r, root.table6, Table6Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename RootT, size_t TokenCount>
|
template <typename RootT, size_t TokenCount>
|
||||||
void WordSelectSet::parse_windows_t(const std::string& data, const std::vector<std::string>* unitxt_collection) {
|
void WordSelectSet::parse_windows_t(const std::string& data, const std::vector<std::string>* unitxt_collection) {
|
||||||
if (!unitxt_collection) {
|
if (!unitxt_collection) {
|
||||||
throw runtime_error("a unitxt collection is required");
|
throw runtime_error("a unitxt collection is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
StringReader r(data);
|
StringReader r(data);
|
||||||
uint32_t root_offset = r.pget<le_uint32_t>(r.size() - 0x10);
|
uint32_t root_offset = r.pget<le_uint32_t>(r.size() - 0x10);
|
||||||
const auto& root = r.pget<RootT>(root_offset);
|
const auto& root = r.pget<RootT>(root_offset);
|
||||||
this->strings = *unitxt_collection;
|
this->strings = *unitxt_collection;
|
||||||
// this->table1 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table1, Table1Count);
|
// this->table1 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table1, Table1Count);
|
||||||
// this->table2 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table2, Table2Count);
|
// this->table2 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table2, Table2Count);
|
||||||
this->token_id_to_string_id = read_direct_table<size_t, le_uint16_t>(r, root.token_id_to_string_id_table, TokenCount);
|
this->token_id_to_string_id = read_direct_table<size_t, le_uint16_t>(r, root.token_id_to_string_id_table, TokenCount);
|
||||||
// this->table4 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table4, Table4Count);
|
// this->table4 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table4, Table4Count);
|
||||||
// this->article_types = read_direct_table<uint8_t, uint8_t>(r, root.article_types_table, ArticleTypesCount);
|
// this->article_types = read_direct_table<uint8_t, uint8_t>(r, root.article_types_table, ArticleTypesCount);
|
||||||
// this->table6 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table6, Table6Count);
|
// this->table6 = read_indirect_table<uint16_t, le_uint16_t, le_uint32_t>(r, root.table6, Table6Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
WordSelectSet::WordSelectSet(const string& data, Version version, const vector<string>* unitxt_collection, bool use_sjis) {
|
WordSelectSet::WordSelectSet(const string& data, Version version, const vector<string>* unitxt_collection, bool use_sjis) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::DC_NTE: {
|
case Version::DC_NTE: {
|
||||||
if (data.size() < 4) {
|
if (data.size() < 4) {
|
||||||
throw runtime_error("data is too small");
|
throw runtime_error("data is too small");
|
||||||
}
|
}
|
||||||
string decrypted = data.substr(0, data.size() - 4);
|
string decrypted = data.substr(0, data.size() - 4);
|
||||||
uint32_t seed = *reinterpret_cast<const le_uint32_t*>(data.data() + data.size() - 4);
|
uint32_t seed = *reinterpret_cast<const le_uint32_t*>(data.data() + data.size() - 4);
|
||||||
PSOV2Encryption crypt(seed);
|
PSOV2Encryption crypt(seed);
|
||||||
crypt.decrypt(decrypted);
|
crypt.decrypt(decrypted);
|
||||||
this->parse_non_windows_t<false, 0x469, 0x466>(decrypted, use_sjis);
|
this->parse_non_windows_t<false, 0x469, 0x466>(decrypted, use_sjis);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||||
this->parse_non_windows_t<false, 0x45E, 0x44B>(decrypt_and_decompress_pr2_data<false>(data), use_sjis);
|
this->parse_non_windows_t<false, 0x45E, 0x44B>(decrypt_and_decompress_pr2_data<false>(data), use_sjis);
|
||||||
break;
|
break;
|
||||||
case Version::DC_V1:
|
case Version::DC_V1:
|
||||||
case Version::DC_V2:
|
case Version::DC_V2:
|
||||||
this->parse_non_windows_t<false, 0x467, 0x457>(decrypt_and_decompress_pr2_data<false>(data), use_sjis);
|
this->parse_non_windows_t<false, 0x467, 0x457>(decrypt_and_decompress_pr2_data<false>(data), use_sjis);
|
||||||
break;
|
break;
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
case Version::PC_V2:
|
case Version::PC_V2:
|
||||||
this->parse_windows_t<PCV2Root, 0x645>(decrypt_and_decompress_pr2_data<false>(data), unitxt_collection);
|
this->parse_windows_t<PCV2Root, 0x645>(decrypt_and_decompress_pr2_data<false>(data), unitxt_collection);
|
||||||
break;
|
break;
|
||||||
case Version::GC_NTE:
|
case Version::GC_NTE:
|
||||||
this->parse_non_windows_t<true, 0x63F, 0x693>(decrypt_and_decompress_pr2_data<true>(data), use_sjis);
|
this->parse_non_windows_t<true, 0x63F, 0x693>(decrypt_and_decompress_pr2_data<true>(data), use_sjis);
|
||||||
break;
|
break;
|
||||||
case Version::GC_EP3_NTE:
|
case Version::GC_EP3_NTE:
|
||||||
this->parse_non_windows_t<true, 0x67C, 0x68C>(decrypt_and_decompress_pr2_data<true>(data), use_sjis);
|
this->parse_non_windows_t<true, 0x67C, 0x68C>(decrypt_and_decompress_pr2_data<true>(data), use_sjis);
|
||||||
break;
|
break;
|
||||||
case Version::GC_V3:
|
case Version::GC_V3:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
this->parse_non_windows_t<true, 0x804, 0x68C>(decrypt_and_decompress_pr2_data<true>(data), use_sjis);
|
this->parse_non_windows_t<true, 0x804, 0x68C>(decrypt_and_decompress_pr2_data<true>(data), use_sjis);
|
||||||
break;
|
break;
|
||||||
case Version::XB_V3:
|
case Version::XB_V3:
|
||||||
this->parse_non_windows_t<false, 0x67B, 0x68C>(decrypt_and_decompress_pr2_data<false>(data), use_sjis);
|
this->parse_non_windows_t<false, 0x67B, 0x68C>(decrypt_and_decompress_pr2_data<false>(data), use_sjis);
|
||||||
break;
|
break;
|
||||||
case Version::BB_V4:
|
case Version::BB_V4:
|
||||||
this->parse_windows_t<BBRoot, 0x68C>(decrypt_and_decompress_pr2_data<false>(data), unitxt_collection);
|
this->parse_windows_t<BBRoot, 0x68C>(decrypt_and_decompress_pr2_data<false>(data), unitxt_collection);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw runtime_error("unsupported word select data version");
|
throw runtime_error("unsupported word select data version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const string& WordSelectSet::string_for_token(uint16_t token_id) const {
|
const string& WordSelectSet::string_for_token(uint16_t token_id) const {
|
||||||
return this->strings.at(this->token_id_to_string_id.at(token_id));
|
return this->strings.at(this->token_id_to_string_id.at(token_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WordSelectSet::print(FILE* stream) const {
|
void WordSelectSet::print(FILE* stream) const {
|
||||||
fprintf(stream, "strings:\n");
|
fprintf(stream, "strings:\n");
|
||||||
for (size_t z = 0; z < this->strings.size(); z++) {
|
for (size_t z = 0; z < this->strings.size(); z++) {
|
||||||
fprintf(stream, " [%04zX] \"%s\"\n", z, this->strings[z].c_str());
|
fprintf(stream, " [%04zX] \"%s\"\n", z, this->strings[z].c_str());
|
||||||
}
|
}
|
||||||
fprintf(stream, "token_id_to_string_id:\n");
|
fprintf(stream, "token_id_to_string_id:\n");
|
||||||
for (size_t z = 0; z < this->token_id_to_string_id.size(); z++) {
|
for (size_t z = 0; z < this->token_id_to_string_id.size(); z++) {
|
||||||
fprintf(stream, " [%04zX] %04zX \"%s\"\n", z, this->token_id_to_string_id[z], this->string_for_token(z).c_str());
|
fprintf(stream, " [%04zX] %04zX \"%s\"\n", z, this->token_id_to_string_id[z], this->string_for_token(z).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WordSelectTable::WordSelectTable(
|
WordSelectTable::WordSelectTable(
|
||||||
const WordSelectSet& dc_nte_ws,
|
const WordSelectSet& dc_nte_ws,
|
||||||
const WordSelectSet& dc_112000_ws,
|
const WordSelectSet& dc_112000_ws,
|
||||||
const WordSelectSet& dc_v1_ws,
|
const WordSelectSet& dc_v1_ws,
|
||||||
const WordSelectSet& dc_v2_ws,
|
const WordSelectSet& dc_v2_ws,
|
||||||
const WordSelectSet& pc_nte_ws,
|
const WordSelectSet& pc_nte_ws,
|
||||||
const WordSelectSet& pc_v2_ws,
|
const WordSelectSet& pc_v2_ws,
|
||||||
const WordSelectSet& gc_nte_ws,
|
const WordSelectSet& gc_nte_ws,
|
||||||
const WordSelectSet& gc_v3_ws,
|
const WordSelectSet& gc_v3_ws,
|
||||||
const WordSelectSet& gc_ep3_nte_ws,
|
const WordSelectSet& gc_ep3_nte_ws,
|
||||||
const WordSelectSet& gc_ep3_ws,
|
const WordSelectSet& gc_ep3_ws,
|
||||||
const WordSelectSet& xb_v3_ws,
|
const WordSelectSet& xb_v3_ws,
|
||||||
const WordSelectSet& bb_v4_ws,
|
const WordSelectSet& bb_v4_ws,
|
||||||
const vector<vector<string>>& name_alias_lists) {
|
const vector<vector<string>>& name_alias_lists) {
|
||||||
|
|
||||||
unordered_map<string, string> name_to_canonical_name;
|
unordered_map<string, string> name_to_canonical_name;
|
||||||
for (const auto& alias_list : name_alias_lists) {
|
for (const auto& alias_list : name_alias_lists) {
|
||||||
if (alias_list.size() < 2) {
|
if (alias_list.size() < 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto it = alias_list.begin();
|
auto it = alias_list.begin();
|
||||||
auto canonical_name = *it;
|
auto canonical_name = *it;
|
||||||
for (it++; it != alias_list.end(); it++) {
|
for (it++; it != alias_list.end(); it++) {
|
||||||
name_to_canonical_name.emplace(*it, canonical_name);
|
name_to_canonical_name.emplace(*it, canonical_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<shared_ptr<Token>> dynamic_tokens;
|
vector<shared_ptr<Token>> dynamic_tokens;
|
||||||
{
|
{
|
||||||
for (size_t z = 0; z < 12; z++) {
|
for (size_t z = 0; z < 12; z++) {
|
||||||
auto& token = dynamic_tokens.emplace_back(make_shared<Token>());
|
auto& token = dynamic_tokens.emplace_back(make_shared<Token>());
|
||||||
token->canonical_name = string_printf("__PLAYER_%zu_NAME__", z);
|
token->canonical_name = string_printf("__PLAYER_%zu_NAME__", z);
|
||||||
this->name_to_token.emplace(token->canonical_name, token);
|
this->name_to_token.emplace(token->canonical_name, token);
|
||||||
}
|
}
|
||||||
auto& token = dynamic_tokens.emplace_back(make_shared<Token>());
|
auto& token = dynamic_tokens.emplace_back(make_shared<Token>());
|
||||||
token->canonical_name = "__BLANK__";
|
token->canonical_name = "__BLANK__";
|
||||||
this->name_to_token.emplace(token->canonical_name, token);
|
this->name_to_token.emplace(token->canonical_name, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(NUM_NON_PATCH_VERSIONS == 12, "Don\'t forget to update the WordSelectTable constructor");
|
static_assert(NUM_NON_PATCH_VERSIONS == 12, "Don\'t forget to update the WordSelectTable constructor");
|
||||||
array<const WordSelectSet*, NUM_NON_PATCH_VERSIONS> ws_sets = {
|
array<const WordSelectSet*, NUM_NON_PATCH_VERSIONS> ws_sets = {
|
||||||
&dc_nte_ws, &dc_112000_ws, &dc_v1_ws, &dc_v2_ws,
|
&dc_nte_ws, &dc_112000_ws, &dc_v1_ws, &dc_v2_ws,
|
||||||
&pc_nte_ws, &pc_v2_ws, &gc_nte_ws, &gc_v3_ws,
|
&pc_nte_ws, &pc_v2_ws, &gc_nte_ws, &gc_v3_ws,
|
||||||
&gc_ep3_nte_ws, &gc_ep3_ws, &xb_v3_ws, &bb_v4_ws};
|
&gc_ep3_nte_ws, &gc_ep3_ws, &xb_v3_ws, &bb_v4_ws};
|
||||||
|
|
||||||
for (size_t s_version = 0; s_version < ws_sets.size(); s_version++) {
|
for (size_t s_version = 0; s_version < ws_sets.size(); s_version++) {
|
||||||
Version version = static_cast<Version>(static_cast<size_t>(Version::DC_NTE) + s_version);
|
Version version = static_cast<Version>(static_cast<size_t>(Version::DC_NTE) + s_version);
|
||||||
const auto& ws_set = *ws_sets[s_version];
|
const auto& ws_set = *ws_sets[s_version];
|
||||||
auto& index = this->tokens_by_version.at(s_version);
|
auto& index = this->tokens_by_version.at(s_version);
|
||||||
|
|
||||||
index.reserve(ws_set.num_tokens());
|
index.reserve(ws_set.num_tokens());
|
||||||
for (size_t token_id = 0; token_id < ws_set.num_tokens(); token_id++) {
|
for (size_t token_id = 0; token_id < ws_set.num_tokens(); token_id++) {
|
||||||
const string& str = ws_set.string_for_token(token_id);
|
const string& str = ws_set.string_for_token(token_id);
|
||||||
|
|
||||||
string canonical_name;
|
string canonical_name;
|
||||||
try {
|
try {
|
||||||
canonical_name = name_to_canonical_name.at(str);
|
canonical_name = name_to_canonical_name.at(str);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
canonical_name = str;
|
canonical_name = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto token_it = this->name_to_token.find(canonical_name);
|
auto token_it = this->name_to_token.find(canonical_name);
|
||||||
if (token_it == this->name_to_token.end()) {
|
if (token_it == this->name_to_token.end()) {
|
||||||
token_it = this->name_to_token.emplace(canonical_name, make_shared<Token>()).first;
|
token_it = this->name_to_token.emplace(canonical_name, make_shared<Token>()).first;
|
||||||
token_it->second->canonical_name = std::move(canonical_name);
|
token_it->second->canonical_name = std::move(canonical_name);
|
||||||
}
|
}
|
||||||
token_it->second->slot_for_version(version) = token_id;
|
token_it->second->slot_for_version(version) = token_id;
|
||||||
index.emplace_back(token_it->second);
|
index.emplace_back(token_it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t dynamic_token_base_id = ws_set.num_tokens();
|
size_t dynamic_token_base_id = ws_set.num_tokens();
|
||||||
for (size_t z = 0; z < dynamic_tokens.size(); z++) {
|
for (size_t z = 0; z < dynamic_tokens.size(); z++) {
|
||||||
auto& token = dynamic_tokens[z];
|
auto& token = dynamic_tokens[z];
|
||||||
token->slot_for_version(version) = dynamic_token_base_id + z;
|
token->slot_for_version(version) = dynamic_token_base_id + z;
|
||||||
index.emplace_back(token);
|
index.emplace_back(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WordSelectTable::print(FILE* stream) const {
|
void WordSelectTable::print(FILE* stream) const {
|
||||||
fprintf(stream, "DCN DC11 DCv1 DCv2 PCN PCv2 GCN GCv3 Ep3N Ep3 XBv3 BBv4 CANONICAL-NAME\n");
|
fprintf(stream, "DCN DC11 DCv1 DCv2 PCN PCv2 GCN GCv3 Ep3N Ep3 XBv3 BBv4 CANONICAL-NAME\n");
|
||||||
for (const auto& it : this->name_to_token) {
|
for (const auto& it : this->name_to_token) {
|
||||||
const auto& token = it.second;
|
const auto& token = it.second;
|
||||||
for (size_t z = 0; z < 12; z++) {
|
for (size_t z = 0; z < 12; z++) {
|
||||||
if (token->values_by_version[z] == 0xFFFF) {
|
if (token->values_by_version[z] == 0xFFFF) {
|
||||||
fprintf(stream, " ");
|
fprintf(stream, " ");
|
||||||
} else {
|
} else {
|
||||||
fprintf(stream, "%04hX ", token->values_by_version[z]);
|
fprintf(stream, "%04hX ", token->values_by_version[z]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string serialized = JSON(token->canonical_name).serialize(JSON::SerializeOption::ESCAPE_CONTROLS_ONLY);
|
string serialized = JSON(token->canonical_name).serialize(JSON::SerializeOption::ESCAPE_CONTROLS_ONLY);
|
||||||
fprintf(stream, "%s\n", serialized.c_str());
|
fprintf(stream, "%s\n", serialized.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WordSelectTable::print_index(FILE* stream, Version v) const {
|
void WordSelectTable::print_index(FILE* stream, Version v) const {
|
||||||
fprintf(stream, " DCN DC11 DCv1 DCv2 PCN PCv2 GCN GCv3 Ep3N Ep3 XBv3 BBv4 CANONICAL-NAME\n");
|
fprintf(stream, " DCN DC11 DCv1 DCv2 PCN PCv2 GCN GCv3 Ep3N Ep3 XBv3 BBv4 CANONICAL-NAME\n");
|
||||||
const auto& index = this->tokens_for_version(v);
|
const auto& index = this->tokens_for_version(v);
|
||||||
for (size_t token_id = 0; token_id < index.size(); token_id++) {
|
for (size_t token_id = 0; token_id < index.size(); token_id++) {
|
||||||
const auto& token = index[token_id];
|
const auto& token = index[token_id];
|
||||||
fprintf(stream, "%04zX => ", token_id);
|
fprintf(stream, "%04zX => ", token_id);
|
||||||
for (size_t z = 0; z < 12; z++) {
|
for (size_t z = 0; z < 12; z++) {
|
||||||
if (token->values_by_version[z] == 0xFFFF) {
|
if (token->values_by_version[z] == 0xFFFF) {
|
||||||
fprintf(stream, " ");
|
fprintf(stream, " ");
|
||||||
} else {
|
} else {
|
||||||
fprintf(stream, "%04hX ", token->values_by_version[z]);
|
fprintf(stream, "%04hX ", token->values_by_version[z]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string serialized = JSON(token->canonical_name).serialize(JSON::SerializeOption::ESCAPE_CONTROLS_ONLY);
|
string serialized = JSON(token->canonical_name).serialize(JSON::SerializeOption::ESCAPE_CONTROLS_ONLY);
|
||||||
fprintf(stream, "%s\n", serialized.c_str());
|
fprintf(stream, "%s\n", serialized.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WordSelectMessage WordSelectTable::translate(
|
WordSelectMessage WordSelectTable::translate(
|
||||||
const WordSelectMessage& msg,
|
const WordSelectMessage& msg,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
Version to_version) const {
|
Version to_version) const {
|
||||||
const auto& index = this->tokens_for_version(from_version);
|
const auto& index = this->tokens_for_version(from_version);
|
||||||
|
|
||||||
WordSelectMessage ret;
|
WordSelectMessage ret;
|
||||||
for (size_t z = 0; z < ret.tokens.size(); z++) {
|
for (size_t z = 0; z < ret.tokens.size(); z++) {
|
||||||
if (msg.tokens[z] == 0xFFFF) {
|
if (msg.tokens[z] == 0xFFFF) {
|
||||||
ret.tokens[z] = 0xFFFF;
|
ret.tokens[z] = 0xFFFF;
|
||||||
} else {
|
} else {
|
||||||
const auto& token = index.at(msg.tokens[z]);
|
const auto& token = index.at(msg.tokens[z]);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw runtime_error(string_printf("token %04hX does not exist in the index", msg.tokens[z].load()));
|
throw runtime_error(string_printf("token %04hX does not exist in the index", msg.tokens[z].load()));
|
||||||
}
|
}
|
||||||
ret.tokens[z] = token->slot_for_version(to_version);
|
ret.tokens[z] = token->slot_for_version(to_version);
|
||||||
if (ret.tokens[z] == 0xFFFF) {
|
if (ret.tokens[z] == 0xFFFF) {
|
||||||
throw runtime_error(string_printf("token %04hX has no translation", msg.tokens[z].load()));
|
throw runtime_error(string_printf("token %04hX has no translation", msg.tokens[z].load()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret.num_tokens = msg.num_tokens;
|
ret.num_tokens = msg.num_tokens;
|
||||||
ret.target_type = msg.target_type;
|
ret.target_type = msg.target_type;
|
||||||
ret.numeric_parameter = msg.numeric_parameter;
|
ret.numeric_parameter = msg.numeric_parameter;
|
||||||
ret.unknown_a4 = msg.unknown_a4;
|
ret.unknown_a4 = msg.unknown_a4;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
WordSelectTable::Token::Token() {
|
WordSelectTable::Token::Token() {
|
||||||
for (size_t z = 0; z < this->values_by_version.size(); z++) {
|
for (size_t z = 0; z < this->values_by_version.size(); z++) {
|
||||||
this->values_by_version[z] = 0xFFFF;
|
this->values_by_version[z] = 0xFFFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+110
-110
@@ -1,110 +1,110 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "CommandFormats.hh"
|
#include "CommandFormats.hh"
|
||||||
#include "QuestScript.hh"
|
#include "QuestScript.hh"
|
||||||
|
|
||||||
class WordSelectSet {
|
class WordSelectSet {
|
||||||
public:
|
public:
|
||||||
WordSelectSet(const std::string& data, Version version, const std::vector<std::string>* unitxt_collection, bool use_sjis);
|
WordSelectSet(const std::string& data, Version version, const std::vector<std::string>* unitxt_collection, bool use_sjis);
|
||||||
~WordSelectSet() = default;
|
~WordSelectSet() = default;
|
||||||
|
|
||||||
inline size_t num_strings() const {
|
inline size_t num_strings() const {
|
||||||
return this->strings.size();
|
return this->strings.size();
|
||||||
}
|
}
|
||||||
inline size_t num_tokens() const {
|
inline size_t num_tokens() const {
|
||||||
return this->token_id_to_string_id.size();
|
return this->token_id_to_string_id.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& string_for_token(uint16_t token_id) const;
|
const std::string& string_for_token(uint16_t token_id) const;
|
||||||
|
|
||||||
void print(FILE* stream) const;
|
void print(FILE* stream) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <bool IsBigEndian, size_t StringTableCount, size_t TokenCount>
|
template <bool IsBigEndian, size_t StringTableCount, size_t TokenCount>
|
||||||
void parse_non_windows_t(const std::string& data, bool use_sjis);
|
void parse_non_windows_t(const std::string& data, bool use_sjis);
|
||||||
template <typename RootT, size_t TokenCount>
|
template <typename RootT, size_t TokenCount>
|
||||||
void parse_windows_t(const std::string& data, const std::vector<std::string>* unitxt_collection);
|
void parse_windows_t(const std::string& data, const std::vector<std::string>* unitxt_collection);
|
||||||
|
|
||||||
std::vector<std::string> strings;
|
std::vector<std::string> strings;
|
||||||
std::vector<size_t> token_id_to_string_id;
|
std::vector<size_t> token_id_to_string_id;
|
||||||
// Note: PC NTE and PC have exactly the same parameters
|
// Note: PC NTE and PC have exactly the same parameters
|
||||||
// => DC NTE DC112000 DCv1 DCv2 PCNTE/PC GC NTE GC XB Ep3 NTE Ep3 USA BB
|
// => DC NTE DC112000 DCv1 DCv2 PCNTE/PC GC NTE GC XB Ep3 NTE Ep3 USA BB
|
||||||
// root: => 000074DC 000072A4 0000755C 0000755C 00002B50 0000AB04 0000BCAC 0000B620 0000B648 0000B914 0000B5FC
|
// root: => 000074DC 000072A4 0000755C 0000755C 00002B50 0000AB04 0000BCAC 0000B620 0000B648 0000B914 0000B5FC
|
||||||
// u32 ???: => 00002A9C
|
// u32 ???: => 00002A9C
|
||||||
// TODO
|
// TODO
|
||||||
// u32 ???: => 00002B14
|
// u32 ???: => 00002B14
|
||||||
// TODO
|
// TODO
|
||||||
// u32 strings_table: => 00006338 0000612C 000063C0 000063C0 (unitxt) 00009208 00009C9C 00009C34 00009C5C 00009904 (unitxt)
|
// u32 strings_table: => 00006338 0000612C 000063C0 000063C0 (unitxt) 00009208 00009C9C 00009C34 00009C5C 00009904 (unitxt)
|
||||||
// u32 string_offset[COUNT]: => 469 45E 467 467 (unitxt) 63F 804 67B 67C 804 (unitxt)
|
// u32 string_offset[COUNT]: => 469 45E 467 467 (unitxt) 63F 804 67B 67C 804 (unitxt)
|
||||||
// char string[...\0]
|
// char string[...\0]
|
||||||
// u32 table1: => 00000B90 00000B54 00000D3C 00000D3C 00001018 0000100C 000012F0 000012F0 000012F0 000011D0 000012F0
|
// u32 table1: => 00000B90 00000B54 00000D3C 00000D3C 00001018 0000100C 000012F0 000012F0 000012F0 000011D0 000012F0
|
||||||
// {u32 offset, u32 count}[COUNT]: => 94 122 93 93 F9 F9 126 126 126 17F 126
|
// {u32 offset, u32 count}[COUNT]: => 94 122 93 93 F9 F9 126 126 126 17F 126
|
||||||
// u16[count]
|
// u16[count]
|
||||||
// u32 table2: => 00001178 00001108 00001300 00001300 000019D8 000019CC 00001EE8 00001EE8 00001EE8 00001DC8 00001EE8
|
// u32 table2: => 00001178 00001108 00001300 00001300 000019D8 000019CC 00001EE8 00001EE8 00001EE8 00001DC8 00001EE8
|
||||||
// {u32 offset, u32 count}[COUNT]: => 7 7 7 7 7 7 13 13 13 13 13
|
// {u32 offset, u32 count}[COUNT]: => 7 7 7 7 7 7 13 13 13 13 13
|
||||||
// u16[count]
|
// u16[count]
|
||||||
// u32 token_id_to_string_id_table => 000011B0 00001140 00001338 00001338 00001A10 00001A04 00001F80 00001F80 00001F80 00001E60 00001F80
|
// u32 token_id_to_string_id_table => 000011B0 00001140 00001338 00001338 00001A10 00001A04 00001F80 00001F80 00001F80 00001E60 00001F80
|
||||||
// u16[COUNT] string_id_for_token_id => 466 44B 457 457 645 693 68C 68C 68C 68C 68C
|
// u16[COUNT] string_id_for_token_id => 466 44B 457 457 645 693 68C 68C 68C 68C 68C
|
||||||
// u32 table4: => 00001A5C 00001B08 00001D1C 00001D1C 000027D0 000027C4 00002DCC 00002DCC 00002DCC 00002CAC 00002DCC
|
// u32 table4: => 00001A5C 00001B08 00001D1C 00001D1C 000027D0 000027C4 00002DCC 00002DCC 00002DCC 00002CAC 00002DCC
|
||||||
// (non-NTE) {u32 offset, u32 count}[COUNT]: => 2 2 2 2 2 2 2 2 2 2
|
// (non-NTE) {u32 offset, u32 count}[COUNT]: => 2 2 2 2 2 2 2 2 2 2
|
||||||
// u16[count]
|
// u16[count]
|
||||||
// (NTE) u16[COUNT] => E1
|
// (NTE) u16[COUNT] => E1
|
||||||
// u32 article_types_table: => 00001C1E 00001B18 00001D2C 00001D2C 000027E0 000027D4 00002DDC 00002DDC 00002DDC 00002CBC 00002DDC
|
// u32 article_types_table: => 00001C1E 00001B18 00001D2C 00001D2C 000027E0 000027D4 00002DDC 00002DDC 00002DDC 00002CBC 00002DDC
|
||||||
// u8[COUNT] article_types => 1C8 166 166 166 266 266 28A 28A 28A 28A 266
|
// u8[COUNT] article_types => 1C8 166 166 166 266 266 28A 28A 28A 28A 266
|
||||||
// u32 table6: => 00001E28 00001CBC 00001ED0 00001ED0 00002A84 00002A78 000030A4 000030A4 000030A4 00002F84 00003080
|
// u32 table6: => 00001E28 00001CBC 00001ED0 00001ED0 00002A84 00002A78 000030A4 000030A4 000030A4 00002F84 00003080
|
||||||
// {u32 offset, u32 count}[3]:
|
// {u32 offset, u32 count}[3]:
|
||||||
// u16[count]
|
// u16[count]
|
||||||
};
|
};
|
||||||
|
|
||||||
class WordSelectTable {
|
class WordSelectTable {
|
||||||
public:
|
public:
|
||||||
WordSelectTable(
|
WordSelectTable(
|
||||||
const WordSelectSet& dc_nte_ws,
|
const WordSelectSet& dc_nte_ws,
|
||||||
const WordSelectSet& dc_112000_ws,
|
const WordSelectSet& dc_112000_ws,
|
||||||
const WordSelectSet& dc_v1_ws,
|
const WordSelectSet& dc_v1_ws,
|
||||||
const WordSelectSet& dc_v2_ws,
|
const WordSelectSet& dc_v2_ws,
|
||||||
const WordSelectSet& pc_nte_ws,
|
const WordSelectSet& pc_nte_ws,
|
||||||
const WordSelectSet& pc_v2_ws,
|
const WordSelectSet& pc_v2_ws,
|
||||||
const WordSelectSet& gc_nte_ws,
|
const WordSelectSet& gc_nte_ws,
|
||||||
const WordSelectSet& gc_v3_ws,
|
const WordSelectSet& gc_v3_ws,
|
||||||
const WordSelectSet& gc_ep3_nte_ws,
|
const WordSelectSet& gc_ep3_nte_ws,
|
||||||
const WordSelectSet& gc_ep3_ws,
|
const WordSelectSet& gc_ep3_ws,
|
||||||
const WordSelectSet& xb_v3_ws,
|
const WordSelectSet& xb_v3_ws,
|
||||||
const WordSelectSet& bb_v4_ws,
|
const WordSelectSet& bb_v4_ws,
|
||||||
const std::vector<std::vector<std::string>>& name_alias_lists);
|
const std::vector<std::vector<std::string>>& name_alias_lists);
|
||||||
|
|
||||||
void print(FILE* stream) const;
|
void print(FILE* stream) const;
|
||||||
void print_index(FILE* stream, Version v) const;
|
void print_index(FILE* stream, Version v) const;
|
||||||
|
|
||||||
WordSelectMessage translate(
|
WordSelectMessage translate(
|
||||||
const WordSelectMessage& msg,
|
const WordSelectMessage& msg,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
Version to_version) const;
|
Version to_version) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Token {
|
struct Token {
|
||||||
std::array<uint16_t, 12> values_by_version;
|
std::array<uint16_t, 12> values_by_version;
|
||||||
std::string canonical_name;
|
std::string canonical_name;
|
||||||
|
|
||||||
Token();
|
Token();
|
||||||
|
|
||||||
inline uint16_t& slot_for_version(Version version) {
|
inline uint16_t& slot_for_version(Version version) {
|
||||||
return this->values_by_version.at(static_cast<size_t>(version) - static_cast<size_t>(Version::DC_NTE));
|
return this->values_by_version.at(static_cast<size_t>(version) - static_cast<size_t>(Version::DC_NTE));
|
||||||
}
|
}
|
||||||
inline uint16_t slot_for_version(Version version) const {
|
inline uint16_t slot_for_version(Version version) const {
|
||||||
return this->values_by_version.at(static_cast<size_t>(version) - static_cast<size_t>(Version::DC_NTE));
|
return this->values_by_version.at(static_cast<size_t>(version) - static_cast<size_t>(Version::DC_NTE));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<std::string, std::shared_ptr<Token>> name_to_token;
|
std::map<std::string, std::shared_ptr<Token>> name_to_token;
|
||||||
std::array<std::vector<std::shared_ptr<Token>>, 12> tokens_by_version;
|
std::array<std::vector<std::shared_ptr<Token>>, 12> tokens_by_version;
|
||||||
|
|
||||||
inline const std::vector<std::shared_ptr<Token>>& tokens_for_version(Version version) const {
|
inline const std::vector<std::shared_ptr<Token>>& tokens_for_version(Version version) const {
|
||||||
return this->tokens_by_version.at(static_cast<size_t>(version) - static_cast<size_t>(Version::DC_NTE));
|
return this->tokens_by_version.at(static_cast<size_t>(version) - static_cast<size_t>(Version::DC_NTE));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+1232
-1232
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user