The Battle for Wesnoth  1.19.7+dev
game_version.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
3  by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "game_version.hpp"
17 
18 #include "utils/math.hpp"
19 #include "lexical_cast.hpp"
21 #include "wesconfig.h"
22 
23 #ifdef LOAD_REVISION
24 #include "revision.h"
25 #endif
26 
27 #include <cassert>
28 #include <functional>
29 #include <locale>
30 
31 #include <boost/algorithm/string.hpp>
32 
33 namespace game_config
34 {
37 const version_info test_version("test");
38 
39 #ifdef REVISION
40 const std::string revision = VERSION " (" REVISION ")";
41 #elif defined(VCS_SHORT_HASH) && defined(VCS_WC_MODIFIED)
42 const std::string revision = std::string(VERSION) + " (" + VCS_SHORT_HASH + (VCS_WC_MODIFIED ? "-Modified" : "-Clean") + ")";
43 #else
44 const std::string revision = VERSION;
45 #endif
46 
47 } // namespace game_config
48 
50  : nums_(3,0), special_(""), special_separator_('\0')
51 {
52 }
53 
55  : version_info(std::string(str))
56 {
57 }
58 
59 version_info::version_info(unsigned int major, unsigned int minor, unsigned int revision_level,
60  char special_separator, const std::string& special)
61  : nums_(3,0), special_(special), special_separator_(special_separator)
62 {
63  nums_[0] = major;
64  nums_[1] = minor;
65  nums_[2] = revision_level;
66 }
67 
68 version_info::version_info(const std::string& str)
69  : nums_(3,0)
70  , special_("")
71  , special_separator_('\0')
72 {
73  std::string v = str;
74  boost::trim(v);
75 
76  if(v.empty())
77  return;
78 
79  //
80  // The breakpoint is where the "special" version component begins.
81  // For 1.1.2a it would at the index of the char 'a'. For 1.1.4+dev it is at '+'.
82  //
83  // For 1.5.2 it is at npos.
84  //
85  const std::string::size_type breakpoint_pos = v.find_first_not_of(".0123456789");
86  std::string left_side;
87  if(breakpoint_pos != std::string::npos) {
88  const std::string right_side = v.substr(breakpoint_pos);
89  assert(right_side.empty() == false);
90 
91  if(std::isalpha(right_side[0], std::locale::classic())) {
92  special_separator_ = '\0';
93  special_ = right_side;
94  }
95  else {
96  special_separator_ = right_side[0];
97  if(right_side.size() > 1) {
98  special_ = right_side.substr(1);
99  }
100  }
101 
102  left_side = v.substr(0, breakpoint_pos);
103  }
104  else {
105  left_side = v;
106  }
107 
108  const std::vector<std::string> components = utils::split(left_side, '.');
109  const std::size_t s = components.size();
110  if(s == 0) {
111  return;
112  }
113  else if(s > 3) {
114  nums_.resize(s, 0);
115  }
116 
117  for(std::size_t i = 0; (i < s); ++i) {
118  nums_[i] = lexical_cast_default<unsigned int>(components[i]);
119  }
120 }
121 
122 std::string version_info::str() const
123 {
124  const std::size_t s = nums_.size();
125 
126  std::ostringstream o;
127  for(std::size_t k = 0; k < s; ++k) {
128  o << nums_[k];
129 
130  if(s != 1+k) {
131  o << '.';
132  }
133  }
134 
135  if(! special_.empty()) {
136  if(special_separator_ != '\0') {
137  o << special_separator_;
138  }
139 
140  o << special_;
141  }
142 
143  return o.str();
144 }
145 
146 void version_info::set_major_version(unsigned int v) {
147  nums_[0] = v;
148 }
149 
150 void version_info::set_minor_version(unsigned int v) {
151  nums_[1] = v;
152 }
153 
154 void version_info::set_revision_level(unsigned int v) {
155  nums_[2] = v;
156 }
157 
158 unsigned int version_info::major_version() const {
159  return nums_[0];
160 }
161 
162 unsigned int version_info::minor_version() const {
163  return nums_[1];
164 }
165 
166 unsigned int version_info::revision_level() const {
167  return nums_[2];
168 }
169 
171  return nums_.size() <= 3;
172 }
173 
175  return is_odd(minor_version());
176 }
177 
178 namespace {
179  template<template<typename> class Fcn>
180  bool version_comparison_internal(const version_info& l, const version_info& r)
181  {
182  std::vector<unsigned int> lc = l.components();
183  std::vector<unsigned int> rc = r.components();
184 
185  const std::size_t lsize = lc.size();
186  const std::size_t rsize = rc.size();
187  const std::size_t csize = std::max(lsize, rsize);
188 
189  // make compatible, missing items default to zero
190  if(lsize < csize) lc.resize(csize, 0);
191  if(rsize < csize) rc.resize(csize, 0);
192 
193  using comp_list = std::vector<unsigned int>;
194  using comp_pair = std::tuple<const comp_list&, const std::string&>;
195  Fcn<comp_pair> comp;
196 
197  const comp_pair& lp = std::tie(lc, l.special_version());
198  const comp_pair& rp = std::tie(rc, r.special_version());
199  return comp(lp, rp);
200  }
201 } // end unnamed namespace
202 
203 bool operator==(const version_info& l, const version_info& r)
204 {
205  return version_comparison_internal<std::equal_to>(l, r);
206 }
207 
208 bool operator!=(const version_info& l, const version_info& r)
209 {
210  return version_comparison_internal<std::not_equal_to>(l, r);
211 }
212 
213 bool operator<(const version_info& l, const version_info& r)
214 {
215  return version_comparison_internal<std::less>(l, r);
216 }
217 
218 bool operator>(const version_info& l, const version_info& r)
219 {
220  return version_comparison_internal<std::greater>(l, r);
221 }
222 
223 bool operator<=(const version_info& l, const version_info& r)
224 {
225  return version_comparison_internal<std::less_equal>(l, r);
226 }
227 
228 bool operator>=(const version_info& l, const version_info& r)
229 {
230  return version_comparison_internal<std::greater_equal>(l, r);
231 }
232 
233 VERSION_COMP_OP parse_version_op(const std::string& op_str)
234 {
235  if(op_str == "==") {
236  return OP_EQUAL;
237  } else if(op_str == "!=") {
238  return OP_NOT_EQUAL;
239  } else if(op_str == "<") {
240  return OP_LESS;
241  } else if(op_str == "<=") {
242  return OP_LESS_OR_EQUAL;
243  } else if(op_str == ">") {
244  return OP_GREATER;
245  } else if(op_str == ">=") {
246  return OP_GREATER_OR_EQUAL;
247  }
248 
249  return OP_INVALID;
250 }
251 
253 {
254  switch(op) {
255  case OP_EQUAL:
256  return a == b;
257  case OP_NOT_EQUAL:
258  return a != b;
259  case OP_LESS:
260  return a < b;
261  case OP_LESS_OR_EQUAL:
262  return a <= b;
263  case OP_GREATER:
264  return a > b;
265  case OP_GREATER_OR_EQUAL:
266  return a >= b;
267  default:
268  ;
269  }
270 
271  return false;
272 }
Represents version numbers.
std::string special_
std::string str() const
Serializes the version number into string form.
unsigned int revision_level() const
Retrieves the revision level (x3 in "x1.x2.x3").
char special_separator_
void set_minor_version(unsigned int)
Sets the minor version number.
const std::string & special_version() const
Retrieves the special version suffix (e.g.
void set_major_version(unsigned int)
Sets the major version number.
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
std::vector< unsigned int > nums_
void set_revision_level(unsigned int)
Sets the revision level.
const std::vector< unsigned int > & components() const
Read-only access to all numeric components.
bool is_canonical() const
Whether the version number is considered canonical for mainline Wesnoth.
version_info()
Default constructor.
bool is_dev_version() const
Whether this version represents a development version of Wesnoth aka whether the minor version odd.
std::size_t i
Definition: function.cpp:1029
bool operator>(const version_info &l, const version_info &r)
Greater-than operator for version_info.
bool do_version_check(const version_info &a, VERSION_COMP_OP op, const version_info &b)
bool operator!=(const version_info &l, const version_info &r)
Inequality operator for version_info.
bool operator>=(const version_info &l, const version_info &r)
Greater-than-or-equal operator for version_info.
bool operator<=(const version_info &l, const version_info &r)
Less-than-or-equal operator for version_info.
bool operator==(const version_info &l, const version_info &r)
Equality operator for version_info.
VERSION_COMP_OP parse_version_op(const std::string &op_str)
bool operator<(const version_info &l, const version_info &r)
Less-than operator for version_info.
Interfaces for manipulating version numbers of engine, add-ons, etc.
VERSION_COMP_OP
@ OP_INVALID
@ OP_EQUAL
@ OP_GREATER_OR_EQUAL
@ OP_LESS_OR_EQUAL
@ OP_GREATER
@ OP_NOT_EQUAL
@ OP_LESS
New lexcical_cast header.
General math utility functions.
constexpr bool is_odd(T num)
Definition: math.hpp:36
Game configuration data as global variables.
Definition: build_info.cpp:61
const version_info min_savegame_version(MIN_SAVEGAME_VERSION)
const version_info test_version("test")
const version_info wesnoth_version(VERSION)
const std::string revision
void trim(std::string_view &s)
std::vector< std::string > split(const config_attribute_value &val)
static map_location::direction s
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.
#define VERSION
Definition: wesconfig.h:43
#define MIN_SAVEGAME_VERSION
Some older savegames of Wesnoth cannot be loaded anymore, this variable defines the minimum required ...
Definition: wesconfig.h:33
#define b