From ec685f4e71424403b322d3971ac68b31821fe7e1 Mon Sep 17 00:00:00 2001 From: dshaligram Date: Sun, 25 Mar 2007 08:58:25 +0000 Subject: Cleaned up hiscores xlog format (suggested by Shawn Moore). Breaks compatbility with older xlogfiles. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1093 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/externs.h | 116 ------------ crawl-ref/source/hiscores.cc | 425 ++++++++++++++++++++++--------------------- crawl-ref/source/hiscores.h | 139 +++++++++++++- crawl-ref/source/libutil.cc | 16 ++ crawl-ref/source/libutil.h | 4 + 5 files changed, 377 insertions(+), 323 deletions(-) (limited to 'crawl-ref') diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index 5266b1ba15..b474245858 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -1477,122 +1477,6 @@ struct tagHeader long offset; }; -struct scorefile_entry -{ -public: - char version; - char release; - long points; - std::string name; - long uid; // for multiuser systems - char race; - char cls; - std::string race_class_name; // overrides race & cls if non-empty. - char lvl; // player level. - char best_skill; // best skill # - char best_skill_lvl; // best skill level - int death_type; - int death_source; // 0 or monster TYPE - int mon_num; // sigh... - std::string death_source_name; // overrides death_source - std::string auxkilldata; // weapon wielded, spell cast, etc - char dlvl; // dungeon level (relative) - char level_type; // what kind of level died on.. - char branch; // dungeon branch - int final_hp; // actual current HPs (probably <= 0) - int final_max_hp; // net HPs after rot - int final_max_max_hp; // gross HPs before rot - int damage; // damage of final attack - int str; // final str (useful for nickname) - int intel; // final int - int dex; // final dex (useful for nickname) - int god; // god - int piety; // piety - int penance; // penance - char wiz_mode; // character used wiz mode - time_t birth_time; // start time of character - time_t death_time; // end time of character - long real_time; // real playing time in seconds - long num_turns; // number of turns taken - int num_diff_runes; // number of rune types in inventory - int num_runes; // total number of runes in inventory - -public: - scorefile_entry(); - scorefile_entry(int damage, int death_source, int death_type, - const char *aux, bool death_cause_only = false); - scorefile_entry(const scorefile_entry &se); - - scorefile_entry &operator = (const scorefile_entry &other); - - void init_death_cause(int damage, int death_source, int death_type, - const char *aux); - void init(); - void reset(); - - enum death_desc_verbosity { - DDV_TERSE, - DDV_ONELINE, - DDV_NORMAL, - DDV_VERBOSE, - DDV_LOGVERBOSE // Semi-verbose for logging purposes - }; - - std::string raw_string() const; - bool parse(const std::string &line); - - std::string hiscore_line(death_desc_verbosity verbosity) const; - - std::string character_description(death_desc_verbosity) const; - // Full description of death: Killed by an xyz wielding foo - std::string death_description(death_desc_verbosity) const; - std::string death_place(death_desc_verbosity) const; - std::string game_time(death_desc_verbosity) const; - -private: - typedef std::vector< std::pair > hs_fields; - typedef std::map hs_map; - - mutable std::auto_ptr fields; - mutable std::auto_ptr fieldmap; - -private: - std::string single_cdesc() const; - std::string strip_article_a(const std::string &s) const; - std::string terse_missile_cause() const; - std::string terse_missile_name() const; - std::string terse_beam_cause() const; - std::string terse_wild_magic() const; - std::string terse_trap() const; - const char *damage_verb() const; - const char *death_source_desc() const; - std::string damage_string(bool terse = false) const; - - bool parse_obsolete_scoreline(const std::string &line); - bool parse_scoreline(const std::string &line); - - void init_with_fields(); - void add_field(const std::string &key, - const char *format, ...) const; - void add_auxkill_field() const; - void set_score_fields() const; - - std::string short_kill_message() const; - std::string long_kill_message() const; - std::string make_oneline(const std::string &s) const; - - std::string str_field(const std::string &key) const; - int int_field(const std::string &key) const; - long long_field(const std::string &key) const; - std::string xlog_escape(const std::string &s) const; - std::string xlog_unescape(const std::string &s) const; - void read_auxkill_field(); - void map_fields(); - void init_from(const scorefile_entry &other); - - int kludge_branch(int branch_01) const; -}; - extern const struct coord_def Compass[8]; extern const char* god_gain_power_messages[MAX_NUM_GODS][MAX_GOD_ABILITIES]; diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc index 72d6dbdac2..3455c3d5e1 100644 --- a/crawl-ref/source/hiscores.cc +++ b/crawl-ref/source/hiscores.cc @@ -624,10 +624,10 @@ bool scorefile_entry::parse(const std::string &line) // 4) 0.2 and onwards, which are xlogfile format - no leading // colon, fields separated by colons, each field specified as // key=value. Colons are not allowed in key names, must be escaped to - // | in values. Literal | must be escaped as || in values. + // :: in values. // // 0.2 only reads entries of type (3) and (4), and only writes entries of - // type (4). + // type (4). 0.3 or 0.4 may discontinue read support for (3). // Leading colon implies 4.0 style line: if (line[0] == ':') @@ -636,148 +636,24 @@ bool scorefile_entry::parse(const std::string &line) return (parse_scoreline(line)); } -// xlogfile escape: s/\\/\\\\/g, s/|/\\|/g, s/:/|/g, -std::string scorefile_entry::xlog_escape(const std::string &s) const -{ - return - replace_all_of( - replace_all_of( - replace_all_of(s, "\\", "\\\\"), - "|", "\\|" ), - ":", "|" ); -} - -// xlogfile unescape: s/\\(.)/$1/g, s/|/:/g -std::string scorefile_entry::xlog_unescape(const std::string &s) const -{ - std::string unesc = s; - bool escaped = false; - for (int i = 0, size = unesc.size(); i < size; ++i) - { - const char c = unesc[i]; - if (escaped) - { - escaped = false; - continue; - } - - if (c == '|') - unesc[i] = ':'; - else if (c == '\\') - { - escaped = true; - unesc.erase(i--, 1); - size--; - } - } - return (unesc); -} - std::string scorefile_entry::raw_string() const { set_score_fields(); if (!fields.get()) return (""); - - std::string line; - for (int i = 0, size = fields->size(); i < size; ++i) - { - const std::pair &f = (*fields)[i]; - // Don't write empty fields. - if (f.second.empty()) - continue; - - if (!line.empty()) - line += ":"; - - line += f.first; - line += "="; - line += xlog_escape(f.second); - } - - return (line); + return fields->xlog_line(); } bool scorefile_entry::parse_scoreline(const std::string &line) { - std::vector rawfields = split_string(":", line); - fields.reset(new hs_fields); - for (int i = 0, size = rawfields.size(); i < size; ++i) - { - const std::string field = rawfields[i]; - std::string::size_type st = field.find('='); - if (st == std::string::npos) - continue; - - fields->push_back( - std::pair( - field.substr(0, st), - xlog_unescape(field.substr(st + 1)) ) ); - } - + fields.reset(new xlog_fields(line)); init_with_fields(); return (true); } -void scorefile_entry::add_field(const std::string &key, - const char *format, - ...) const -{ - char buf[400]; - va_list args; - va_start(args, format); - vsnprintf(buf, sizeof buf, format, args); - va_end(args); - - fields->push_back( - std::pair( key, buf ) ); -} - -void scorefile_entry::add_auxkill_field() const -{ - add_field("kaux", "%s", auxkilldata.c_str()); -} - -void scorefile_entry::read_auxkill_field() -{ - auxkilldata = str_field("kaux"); -} - -std::string scorefile_entry::str_field(const std::string &s) const -{ - hs_map::const_iterator i = fieldmap->find(s); - if (i == fieldmap->end()) - return (""); - - return i->second; -} - -int scorefile_entry::int_field(const std::string &s) const -{ - std::string field = str_field(s); - return atoi(field.c_str()); -} - -long scorefile_entry::long_field(const std::string &s) const -{ - std::string field = str_field(s); - return atol(field.c_str()); -} - -void scorefile_entry::map_fields() -{ - fieldmap.reset(new hs_map); - for (int i = 0, size = fields->size(); i < size; ++i) - { - const std::pair f = (*fields)[i]; - - (*fieldmap)[f.first] = f.second; - } -} - static const char *short_branch_name(int branch) { if (branch >= 0 && branch < NUM_BRANCHES) @@ -830,113 +706,110 @@ static int str_to_god(const std::string &god) void scorefile_entry::init_with_fields() { - map_fields(); - points = long_field("sc"); - name = str_field("name"); - uid = int_field("uid"); - - race = str_to_species(str_field("race")); - cls = get_class_index_by_name(str_field("cls").c_str()); - - lvl = int_field("xl"); - - best_skill = str_to_skill(str_field("sk")); - best_skill_lvl = int_field("sklev"); - death_type = str_to_kill_method(str_field("ktyp")); - death_source_name = str_field("killer"); - - read_auxkill_field(); - - branch = str_to_branch(str_field("br")); - dlvl = int_field("lvl"); - level_type = str_to_level_area_type(str_field("ltyp")); - - final_hp = int_field("hp"); - final_max_hp = int_field("mhp"); - final_max_max_hp = int_field("mmhp"); - damage = int_field("dam"); - str = int_field("str"); - intel = int_field("int"); - dex = int_field("dex"); - - god = str_to_god(str_field("god")); - piety = int_field("piety"); - penance = int_field("pen"); - wiz_mode = int_field("wiz"); - birth_time = parse_time(str_field("start")); - death_time = parse_time(str_field("end")); - real_time = long_field("dur"); - num_turns = long_field("turn"); - num_diff_runes = int_field("urune"); - num_runes = int_field("nrune"); + points = fields->long_field("sc"); + name = fields->str_field("name"); + uid = fields->int_field("uid"); + + race = str_to_species(fields->str_field("race")); + cls = get_class_index_by_name(fields->str_field("cls").c_str()); + + lvl = fields->int_field("xl"); + + best_skill = str_to_skill(fields->str_field("sk")); + best_skill_lvl = fields->int_field("sklev"); + death_type = str_to_kill_method(fields->str_field("ktyp")); + death_source_name = fields->str_field("killer"); + auxkilldata = fields->str_field("kaux"); + branch = str_to_branch(fields->str_field("br")); + dlvl = fields->int_field("lvl"); + level_type = str_to_level_area_type(fields->str_field("ltyp")); + + final_hp = fields->int_field("hp"); + final_max_hp = fields->int_field("mhp"); + final_max_max_hp = fields->int_field("mmhp"); + damage = fields->int_field("dam"); + str = fields->int_field("str"); + intel = fields->int_field("int"); + dex = fields->int_field("dex"); + + god = str_to_god(fields->str_field("god")); + piety = fields->int_field("piety"); + penance = fields->int_field("pen"); + wiz_mode = fields->int_field("wiz"); + birth_time = parse_time(fields->str_field("start")); + death_time = parse_time(fields->str_field("end")); + real_time = fields->long_field("dur"); + num_turns = fields->long_field("turn"); + num_diff_runes = fields->int_field("urune"); + num_runes = fields->int_field("nrune"); } void scorefile_entry::set_score_fields() const { - fields.reset(new hs_fields()); + fields.reset(new xlog_fields); if (!fields.get()) return; - add_field("v", VER_NUM); - add_field("lv", SCORE_VERSION); - add_field("sc", "%ld", points); - add_field("name", "%s", name.c_str()); - add_field("uid", "%d", uid); - add_field("race", "%s", species_name(race, lvl)); - add_field("cls", "%s", get_class_name(cls)); - add_field("xl", "%d", lvl); - add_field("sk", "%s", skill_name(best_skill)); - add_field("sklev", "%d", best_skill_lvl); - add_field("title", "%s", skill_title( best_skill, best_skill_lvl, + fields->add_field("v", VER_NUM); + fields->add_field("lv", SCORE_VERSION); + fields->add_field("sc", "%ld", points); + fields->add_field("name", "%s", name.c_str()); + fields->add_field("uid", "%d", uid); + fields->add_field("race", "%s", species_name(race, lvl)); + fields->add_field("cls", "%s", get_class_name(cls)); + fields->add_field("xl", "%d", lvl); + fields->add_field("sk", "%s", skill_name(best_skill)); + fields->add_field("sklev", "%d", best_skill_lvl); + fields->add_field("title", "%s", skill_title( best_skill, best_skill_lvl, race, str, dex, god ) ); - add_field("ktyp", ::kill_method_name(kill_method_type(death_type))); - add_field("killer", death_source_desc()); + fields->add_field("ktyp", ::kill_method_name(kill_method_type(death_type))); + fields->add_field("killer", death_source_desc()); - add_auxkill_field(); + fields->add_field("kaux", "%s", auxkilldata.c_str()); - add_field("place", "%s", + fields->add_field("place", "%s", place_name(get_packed_place(branch, dlvl, level_type), false, true).c_str()); - add_field("br", "%s", short_branch_name(branch)); - add_field("lvl", "%d", dlvl); - add_field("ltyp", "%s", level_area_type_name(level_type)); - - add_field("hp", "%d", final_hp); - add_field("mhp", "%d", final_max_hp); - add_field("mmhp", "%d", final_max_max_hp); - add_field("dam", "%d", damage); - add_field("str", "%d", str); - add_field("int", "%d", intel); - add_field("dex", "%d", dex); + fields->add_field("br", "%s", short_branch_name(branch)); + fields->add_field("lvl", "%d", dlvl); + fields->add_field("ltyp", "%s", level_area_type_name(level_type)); + + fields->add_field("hp", "%d", final_hp); + fields->add_field("mhp", "%d", final_max_hp); + fields->add_field("mmhp", "%d", final_max_max_hp); + fields->add_field("dam", "%d", damage); + fields->add_field("str", "%d", str); + fields->add_field("int", "%d", intel); + fields->add_field("dex", "%d", dex); // Don't write No God to save some space. if (god != -1) - add_field("god", "%s", god == GOD_NO_GOD? "" : god_name(god)); + fields->add_field("god", "%s", god == GOD_NO_GOD? "" : god_name(god)); if (piety > 0) - add_field("piety", "%d", piety); + fields->add_field("piety", "%d", piety); if (penance > 0) - add_field("pen", "%d", penance); + fields->add_field("pen", "%d", penance); if (wiz_mode) - add_field("wiz", "%d", wiz_mode); + fields->add_field("wiz", "%d", wiz_mode); - add_field("start", "%s", make_date_string(birth_time).c_str()); - add_field("end", "%s", make_date_string(death_time).c_str()); - add_field("dur", "%ld", real_time); - add_field("turn", "%ld", num_turns); + fields->add_field("start", "%s", make_date_string(birth_time).c_str()); + fields->add_field("end", "%s", make_date_string(death_time).c_str()); + fields->add_field("dur", "%ld", real_time); + fields->add_field("turn", "%ld", num_turns); if (num_diff_runes) - add_field("urune", "%d", num_diff_runes); + fields->add_field("urune", "%d", num_diff_runes); if (num_runes) - add_field("nrune", "%d", num_runes); + fields->add_field("nrune", "%d", num_runes); #ifdef DGL_EXTENDED_LOGFILES const std::string short_msg = short_kill_message(); - add_field("tmsg", "%s", short_msg.c_str()); + fields->add_field("tmsg", "%s", short_msg.c_str()); const std::string long_msg = long_kill_message(); if (long_msg != short_msg) - add_field("vmsg", "%s", long_msg.c_str()); + fields->add_field("vmsg", "%s", long_msg.c_str()); #endif } @@ -2058,3 +1931,143 @@ scorefile_entry::death_description(death_desc_verbosity verbosity) const return (desc); } + +////////////////////////////////////////////////////////////////////////////// +// xlog_fields + +xlog_fields::xlog_fields() : fields(), fieldmap() +{ +} + +xlog_fields::xlog_fields(const std::string &line) : fields(), fieldmap() +{ + init(line); +} + +std::string::size_type +xlog_fields::next_separator(const std::string &s, + std::string::size_type start) const +{ + std::string::size_type p = s.find(':', start); + if (p != std::string::npos && p < s.length() - 1 && s[p + 1] == ':') + return next_separator(s, p + 2); + + return (p); +} + +std::vector +xlog_fields::split_fields(const std::string &s) const +{ + std::string::size_type start = 0, end = 0; + std::vector fs; + + for ( ; (end = next_separator(s, start)) != std::string::npos; + start = end + 1 ) + { + fs.push_back( s.substr(start, end - start) ); + } + + if (start < s.length()) + fs.push_back( s.substr(start) ); + + return (fs); +} + +void xlog_fields::init(const std::string &line) +{ + std::vector rawfields = split_fields(line); + for (int i = 0, size = rawfields.size(); i < size; ++i) + { + const std::string field = rawfields[i]; + std::string::size_type st = field.find('='); + if (st == std::string::npos) + continue; + + fields.push_back( + std::pair( + field.substr(0, st), + xlog_unescape(field.substr(st + 1)) ) ); + } + + map_fields(); +} + +// xlogfile escape: s/:/::/g +std::string xlog_fields::xlog_escape(const std::string &s) const +{ + return replace_all(s, ":", "::"); +} + +// xlogfile unescape: s/::/:/g +std::string xlog_fields::xlog_unescape(const std::string &s) const +{ + return replace_all(s, "::", ":"); +} + +void xlog_fields::add_field(const std::string &key, + const char *format, + ...) +{ + char buf[500]; + va_list args; + va_start(args, format); + vsnprintf(buf, sizeof buf, format, args); + va_end(args); + + fields.push_back( + std::pair( key, buf ) ); + fieldmap[key] = buf; +} + +std::string xlog_fields::str_field(const std::string &s) const +{ + xl_map::const_iterator i = fieldmap.find(s); + if (i == fieldmap.end()) + return (""); + + return i->second; +} + +int xlog_fields::int_field(const std::string &s) const +{ + std::string field = str_field(s); + return atoi(field.c_str()); +} + +long xlog_fields::long_field(const std::string &s) const +{ + std::string field = str_field(s); + return atol(field.c_str()); +} + +void xlog_fields::map_fields() const +{ + fieldmap.clear(); + for (int i = 0, size = fields.size(); i < size; ++i) + { + const std::pair &f = fields[i]; + fieldmap[f.first] = f.second; + } +} + +std::string xlog_fields::xlog_line() const +{ + std::string line; + for (int i = 0, size = fields.size(); i < size; ++i) + { + const std::pair &f = fields[i]; + + // Don't write empty fields. + if (f.second.empty()) + continue; + + if (!line.empty()) + line += ":"; + + line += f.first; + line += "="; + line += xlog_escape(f.second); + } + + return (line); +} diff --git a/crawl-ref/source/hiscores.h b/crawl-ref/source/hiscores.h index 5332b42baf..3acf677c89 100644 --- a/crawl-ref/source/hiscores.h +++ b/crawl-ref/source/hiscores.h @@ -12,6 +12,8 @@ #ifndef HISCORES_H #define HISCORES_H +struct scorefile_entry; + // last updated 16feb2001 {gdl} /* *********************************************************************** * called from: ouch @@ -37,6 +39,141 @@ void hiscores_print_all(int display_count = -1, int format = SCORE_TERSE); * *********************************************************************** */ std::string hiscores_format_single( const scorefile_entry &se ); std::string hiscores_format_single_long( const scorefile_entry &se, - bool verbose = false ); + bool verbose = false ); + +class xlog_fields +{ +public: + xlog_fields(); + xlog_fields(const std::string &line); + + void init(const std::string &line); + std::string xlog_line() const; + + void add_field(const std::string &key, + const char *format, ...); + + std::string str_field(const std::string &) const; + int int_field(const std::string &) const; + long long_field(const std::string &) const; + +private: + std::string xlog_unescape(const std::string &) const; + std::string xlog_escape(const std::string &) const; + void map_fields() const; + std::string::size_type next_separator(const std::string &s, + std::string::size_type start) const; + std::vector split_fields(const std::string &s) const; + +private: + typedef std::vector< std::pair > xl_fields; + typedef std::map xl_map; + + xl_fields fields; + mutable xl_map fieldmap; +}; + +struct scorefile_entry +{ +public: + char version; + char release; + long points; + std::string name; + long uid; // for multiuser systems + char race; + char cls; + std::string race_class_name; // overrides race & cls if non-empty. + char lvl; // player level. + char best_skill; // best skill # + char best_skill_lvl; // best skill level + int death_type; + int death_source; // 0 or monster TYPE + int mon_num; // sigh... + std::string death_source_name; // overrides death_source + std::string auxkilldata; // weapon wielded, spell cast, etc + char dlvl; // dungeon level (relative) + char level_type; // what kind of level died on.. + char branch; // dungeon branch + int final_hp; // actual current HPs (probably <= 0) + int final_max_hp; // net HPs after rot + int final_max_max_hp; // gross HPs before rot + int damage; // damage of final attack + int str; // final str (useful for nickname) + int intel; // final int + int dex; // final dex (useful for nickname) + int god; // god + int piety; // piety + int penance; // penance + char wiz_mode; // character used wiz mode + time_t birth_time; // start time of character + time_t death_time; // end time of character + long real_time; // real playing time in seconds + long num_turns; // number of turns taken + int num_diff_runes; // number of rune types in inventory + int num_runes; // total number of runes in inventory + +public: + scorefile_entry(); + scorefile_entry(int damage, int death_source, int death_type, + const char *aux, bool death_cause_only = false); + scorefile_entry(const scorefile_entry &se); + + scorefile_entry &operator = (const scorefile_entry &other); + + void init_death_cause(int damage, int death_source, int death_type, + const char *aux); + void init(); + void reset(); + + enum death_desc_verbosity { + DDV_TERSE, + DDV_ONELINE, + DDV_NORMAL, + DDV_VERBOSE, + DDV_LOGVERBOSE // Semi-verbose for logging purposes + }; + + std::string raw_string() const; + bool parse(const std::string &line); + + std::string hiscore_line(death_desc_verbosity verbosity) const; + + std::string character_description(death_desc_verbosity) const; + // Full description of death: Killed by an xyz wielding foo + std::string death_description(death_desc_verbosity) const; + std::string death_place(death_desc_verbosity) const; + std::string game_time(death_desc_verbosity) const; + +private: + mutable std::auto_ptr fields; + +private: + std::string single_cdesc() const; + std::string strip_article_a(const std::string &s) const; + std::string terse_missile_cause() const; + std::string terse_missile_name() const; + std::string terse_beam_cause() const; + std::string terse_wild_magic() const; + std::string terse_trap() const; + const char *damage_verb() const; + const char *death_source_desc() const; + std::string damage_string(bool terse = false) const; + + bool parse_obsolete_scoreline(const std::string &line); + bool parse_scoreline(const std::string &line); + + void init_with_fields(); + void add_auxkill_field() const; + void set_score_fields() const; + + std::string short_kill_message() const; + std::string long_kill_message() const; + std::string make_oneline(const std::string &s) const; + + void init_from(const scorefile_entry &other); + + int kludge_branch(int branch_01) const; +}; #endif // HISCORES_H diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc index 0b7772b531..c7e057a9e0 100644 --- a/crawl-ref/source/libutil.cc +++ b/crawl-ref/source/libutil.cc @@ -193,6 +193,22 @@ void lowercase(std::string &s) s[i] = tolower(s[i]); } +std::string replace_all(std::string s, + const std::string &find, + const std::string &repl) +{ + std::string::size_type start = 0; + std::string::size_type found; + + while ((found = s.find(find, start)) != std::string::npos) + { + s.replace( found, find.length(), repl ); + start = found + repl.length(); + } + + return (s); +} + // Replaces all occurrences of any of the characters in tofind with the // replacement string. std::string replace_all_of(std::string s, diff --git a/crawl-ref/source/libutil.h b/crawl-ref/source/libutil.h index 4e5db549b3..c790705b7d 100644 --- a/crawl-ref/source/libutil.h +++ b/crawl-ref/source/libutil.h @@ -33,6 +33,10 @@ std::string strip_filename_unsafe_chars(const std::string &s); std::string make_stringf(const char *format, ...); +std::string replace_all(std::string s, + const std::string &tofind, + const std::string &replacement); + std::string replace_all_of(std::string s, const std::string &tofind, const std::string &replacement); -- cgit v1.2.3-54-g00ecf