Skip to content

Commit dd7457b

Browse files
committed
Merge branch 's2gs' of git://github.com/Prillan/sc2reader into s2gs
2 parents 9e355cf + 9d43c14 commit dd7457b

File tree

4 files changed

+197
-49
lines changed

4 files changed

+197
-49
lines changed

sc2reader/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
load_map = __defaultSC2Reader.load_map
2929
load_game_summaries = __defaultSC2Reader.load_game_summaries
3030
load_game_summary = __defaultSC2Reader.load_game_summary
31-
load_match_infos = __defaultSC2Reader.load_match_infos
32-
load_match_info = __defaultSC2Reader.load_match_info
33-
load_match_histories = __defaultSC2Reader.load_match_histories
34-
load_match_history = __defaultSC2Reader.load_match_history
31+
load_map_infos = __defaultSC2Reader.load_map_infos
32+
load_map_info = __defaultSC2Reader.load_map_info
33+
load_map_histories = __defaultSC2Reader.load_map_headers
34+
load_map_history = __defaultSC2Reader.load_map_header
3535

3636

3737
configure = __defaultSC2Reader.configure
38-
reset = __defaultSC2Reader.reset
38+
reset = __defaultSC2Reader.reset

sc2reader/factories.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from sc2reader import exceptions
1515
from sc2reader import utils
1616
from sc2reader import log_utils
17-
from sc2reader.resources import Replay, Map, GameSummary, MatchInfo, MatchHistory
17+
from sc2reader.resources import Replay, Map, GameSummary, MapInfo, MapHeader
1818

1919
class SC2Factory(object):
2020
"""
@@ -214,19 +214,19 @@ def load_game_summary(self, summary_file, options=None, **new_options):
214214

215215
return s2gs
216216

217-
def load_match_infos(self, infos, options=None, **new_options):
217+
def load_map_infos(self, infos, options=None, **new_options):
218218
"""
219-
Loads a collection of MatchInfos. See load_resources for detailed
219+
Loads a collection of MapInfos. See load_resources for detailed
220220
parameter documentation.
221221
222-
:rtype: generator(:class:`MatchInfo`)
222+
:rtype: generator(:class:`MapInfo`)
223223
"""
224-
for s2mi in self.load_resources(infos, self.load_match_info, options=options, extensions=['.s2mi'], **new_options):
224+
for s2mi in self.load_resources(infos, self.load_map_info, options=options, extensions=['.s2mi'], **new_options):
225225
yield s2mi
226226

227-
def load_match_info(self, info_file, options=None, **new_options):
227+
def load_map_info(self, info_file, options=None, **new_options):
228228
"""
229-
Loads the specified match info using the current factory settings with
229+
Loads the specified map info using the current factory settings with
230230
the specified overrides.
231231
232232
:param info_file: An open file object or path/url to a single file
@@ -238,33 +238,33 @@ def load_match_info(self, info_file, options=None, **new_options):
238238
:param new_options: Options values to override current factory settings
239239
while loading this map.
240240
241-
:rtype: :class:`MatchInfo`
241+
:rtype: :class:`MapInfo`
242242
"""
243243
options = options or utils.merged_dict(self.options, new_options)
244244
resource, name = self.load_resource(info_file, options=options)
245-
s2mi = MatchInfo(resource, name, **options)
245+
s2mi = MapInfo(resource, name, **options)
246246

247247
# Load summary procedure here!
248248
#
249249

250250
return s2mi
251251

252-
def load_match_histories(self, histories, options=None, **new_options):
252+
def load_map_headers(self, headers, options=None, **new_options):
253253
"""
254-
Loads a collection of match history files. See load_resources for
254+
Loads a collection of map header files. See load_resources for
255255
detailed parameter documentation.
256256
257-
:rtype: generator(:class:`MatchHistory`)
257+
:rtype: generator(:class:`MapHeader`)
258258
"""
259-
for s2mh in self.load_resources(histories, self.load_match_history, options=options, extensions=['.s2mh'], **new_options):
259+
for s2mh in self.load_resources(headers, self.load_map_header, options=options, extensions=['.s2mh'], **new_options):
260260
yield s2mh
261261

262-
def load_match_history(self, history_file, options=None, **new_options):
262+
def load_map_header(self, header_file, options=None, **new_options):
263263
"""
264264
Loads the specified match info using the current factory settings with
265265
the specified overrides.
266266
267-
:param history_file: An open file object or path/url to a single file
267+
:param header_file: An open file object or path/url to a single file
268268
269269
:param None options: When options are passed directly into the options
270270
parameter the current factory settings are ignored and only the
@@ -273,12 +273,12 @@ def load_match_history(self, history_file, options=None, **new_options):
273273
:param new_options: Options values to override current factory settings
274274
while loading this map.
275275
276-
:rtype: :class:`MatchHistory`
276+
:rtype: :class:`MapHeader`
277277
"""
278278
options = options or utils.merged_dict(self.options, new_options)
279-
resource, name = self.load_resource(history_file, options=options)
279+
resource, name = self.load_resource(header_file, options=options)
280280
print name
281-
s2mh = MatchHistory(resource, name, **options)
281+
s2mh = MapHeader(resource, name, **options)
282282

283283
# Load summary procedure here!
284284
#
@@ -509,4 +509,4 @@ def load_replay(self, replay_file, options=None, **new_options):
509509
else:
510510
replay = super(SC2Cache, self).load_replay(replay_file, options=options)
511511
self.cache.set(replay_file, replay)
512-
return replay
512+
return replay

sc2reader/objects.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,86 @@ def format(self, format_string):
207207

208208
def __repr__(self):
209209
return str(self)
210+
211+
class Graph():
212+
"""
213+
A class to represent a graph on the score screen
214+
"""
215+
#: Times in seconds on the x-axis of the graph
216+
times = list()
217+
218+
#: Values on the y-axis of the graph
219+
values = list()
220+
221+
def __init__(self, x, y, xy_list=None):
222+
if xy_list:
223+
for x, y in xy_list:
224+
self.times.append(x)
225+
self.values.append(y)
226+
else:
227+
self.times = x
228+
self.values = y
229+
230+
def as_points(self):
231+
""" Get the graph as a list of (x, y) tuples """
232+
return zip(self.times, self.values)
233+
234+
def __str__(self):
235+
return "Graph with {0} values".format(len(self.times))
236+
237+
class PlayerSummary():
238+
"""
239+
A class to represent a player in the game summary (.s2gs)
240+
"""
241+
stats_pretty_names = {
242+
'R' : 'Resources',
243+
'U' : 'Units',
244+
'S' : 'Structures',
245+
'O' : 'Overview',
246+
'AUR' : 'Average Unspent Resources',
247+
'RCR' : 'Resource Collection Rate',
248+
'WC' : 'Workers Created',
249+
'UT' : 'Units Trained',
250+
'KUC' : 'Killed Unit Count',
251+
'SB' : 'Structures Built',
252+
'SRC' : 'Structures Razed Count'
253+
}
254+
255+
#: The index of the player in the game
256+
pid = int()
257+
258+
#: The race the player used
259+
race = str()
260+
261+
#: Battle.Net id of the player
262+
bnetid = int()
263+
264+
#: Subregion id of player
265+
subregion = int()
266+
267+
#: unknown1
268+
unknown1 = int()
269+
270+
#: unknown2
271+
unknown2 = dict()
272+
273+
#: :class:`Graph` of player army values over time (seconds)
274+
army_graph = None
275+
276+
#: :class:`Graph` of player income over time (seconds)
277+
income_graph = None
278+
279+
#: Stats from the game in a dictionary
280+
stats = dict()
281+
282+
def __init__(self, pid):
283+
self.pid = pid
284+
285+
def __str__(self):
286+
return '{} - {}/{}/'.format(self.race, self.subregion, self.bnetid)
287+
288+
def get_stats(self):
289+
s = ''
290+
for k in self.stats:
291+
s += '{}: {}\n'.format(self.stats_pretty_names[k], self.stats[k])
292+
return s.strip()

sc2reader/resources.py

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212
from sc2reader import utils
1313
from sc2reader import log_utils
14-
from sc2reader.objects import Player, Observer, Team
15-
from sc2reader.constants import REGIONS, LOCALIZED_RACES, GAME_SPEED_FACTOR
14+
from sc2reader.objects import Player, Observer, Team, PlayerSummary, Graph
15+
from sc2reader.constants import REGIONS, LOCALIZED_RACES, GAME_SPEED_FACTOR, GAME_SPEED_CODES, RACE_CODES
1616

1717

1818
class Resource(object):
@@ -397,40 +397,105 @@ def read_game_strings(self):
397397
elif parts[0] == 'DocInfo/DescLong':
398398
self.description = parts[1]
399399

400-
s2gsmap = [[4, "Average Unspent Resources"],
401-
[5, "Resource Collection Rate"],
402-
[6, "Workers Created"],
403-
[7, "Units Trained"],
404-
[8, "Killed Unit Count"],
405-
[9, "Structure Built"],
406-
]
407400

408401
class GameSummary(Resource):
409402
url_template = 'http://{0}.depot.battle.net:1119/{1}.s2gs'
403+
404+
stats_keys = [
405+
'R',
406+
'U',
407+
'S',
408+
'O',
409+
'AUR',
410+
'RCR',
411+
'WC',
412+
'UT',
413+
'KUC',
414+
'SB',
415+
'SRC',
416+
]
417+
418+
#: Game speed
419+
game_speed = str()
420+
421+
#: Players, a list of :class`PlayerSummary` from the game
422+
players = list()
423+
410424
def __init__(self, summary_file, filename=None, **options):
411425
super(GameSummary, self).__init__(summary_file, filename,**options)
412426
self.data = zlib.decompress(summary_file.read()[16:])
413427
self.parts = list()
414428
buffer = utils.ReplayBuffer(self.data)
415429
while buffer.left:
416430
part = buffer.read_data_struct()
417-
# print str(part)+"\n\n\n"
418431
self.parts.append(part)
419-
# print len(self.parts)
420-
# pprint.PrettyPrinter(indent=2).pprint(self.parts)
421-
for index, name in s2gsmap:
422-
for player in [0, 1]:
423-
print "Player", player, name, self.parts[3][0][index][1][player][0][0]
424432

425-
class MatchInfo(Resource):
426-
url_template = 'http://{0}.depot.battle.net:1119/{1}.s2ma'
427-
def __init__(self, info_file, filename=None, **options):
428-
super(MatchInfo, self).__init__(info_file, filename,**options)
429-
self.data = utils.ReplayBuffer(info_file).read_data_struct()
433+
# Parse basic info
434+
self.game_speed = GAME_SPEED_CODES[''.join(reversed(self.parts[0][0][1]))]
435+
436+
# Parse player structs, 16 is the maximum amount of players
437+
for i in range(16):
438+
player = None
439+
# Check if player, break if not
440+
if self.parts[0][3][i][2] == '\x00\x00\x00\x00':
441+
break
442+
player_struct = self.parts[0][3][i]
443+
444+
player = PlayerSummary(player_struct[0][0])
445+
player.race = RACE_CODES[''.join(reversed(player_struct[2]))]
446+
player.bnetid = player_struct[0][1][0][3]
447+
player.subregion = player_struct[0][1][0][2]
448+
449+
# int
450+
player.unknown1 = player_struct[0][1][0]
451+
# {0:long1, 1:long2}
452+
# Example:
453+
# { 0: 3405691582L, 1: 11402158793782460416L}
454+
player.unknown2 = player_struct[0][1][1]
430455

456+
self.players.append(player)
457+
458+
# Parse graph and stats stucts, for each player
459+
for p in self.players:
460+
461+
# Graph stuff
462+
xy = [(o[2], o[0]) for o in self.parts[4][0][2][1][p.pid]]
463+
p.army_graph = Graph([], [], xy_list=xy)
464+
465+
xy = [(o[2], o[0]) for o in self.parts[4][0][1][1][p.pid]]
466+
p.income_graph = Graph([], [], xy_list=xy)
467+
468+
# Stats stuff
469+
stats_struct = self.parts[3][0]
470+
# The first group of stats is located in parts[3][0]
471+
for i in range(len(stats_struct)):
472+
p.stats[self.stats_keys[i]] = stats_struct[i][1][p.pid][0][0]
473+
# The last piece of stats is in parts[4][0][0][1]
474+
p.stats[self.stats_keys[len(stats_struct)]] = self.parts[4][0][0][1][p.pid][0][0]
475+
476+
477+
class MapInfo(Resource):
478+
url_template = 'http://{0}.depot.battle.net:1119/{1}.s2mi'
479+
480+
#: Name of the Map
481+
map_name = str()
482+
483+
#: Hash of referenced s2mh file
484+
s2mh_hash = str()
485+
486+
#: URL of referenced s2mh file
487+
s2mh_url = str()
431488

432-
class MatchHistory(Resource):
433-
url_template = 'http://{0}.depot.battle.net:1119/{1}.s2ma'
434-
def __init__(self, history_file, filename=None, **options):
435-
super(MatchHistory, self).__init__(history_file, filename,**options)
436-
self.data = utils.ReplayBuffer(history_file).read_data_struct()
489+
def __init__(self, info_file, filename=None, **options):
490+
super(MapInfo, self).__init__(info_file, filename,**options)
491+
self.data = utils.ReplayBuffer(info_file).read_data_struct()
492+
self.map_name = self.data[0][7]
493+
self.language = self.data[0][13]
494+
self.s2mh_hash = ''.join([hex(ord(x))[2:] for x in self.data[0][1][8:]])
495+
self.s2mh_url = MapHeader.url_template.format(self.data[0][1][6:8], self.s2mh_hash)
496+
497+
class MapHeader(Resource):
498+
url_template = 'http://{0}.depot.battle.net:1119/{1}.s2mh'
499+
def __init__(self, header_file, filename=None, **options):
500+
super(MapHeader, self).__init__(header_file, filename,**options)
501+
self.data = utils.ReplayBuffer(header_file).read_data_struct()

0 commit comments

Comments
 (0)