Package: faf-replay-parser
FAF Replay ParserA fast library for parsing Supreme Commander Forged Alliance replay files.InstallationPre-built packages are available for Linux, MacOS, and Windows. You can install
them with:pip install faf-replay-parserDocumentationHere are some examples of using the parser. Checkhelp(fafreplay)for more
details on available functions.Gathering basic infoFor the most basic uses there are a few special functions that should be
preferred over constructing aParserobject. These do only one job, but they
do it very quickly.Current functions:body_offset- Used for splitting replay data into header and body.body_ticks- Used for extracting the game length.Examplefromdatetimeimporttimedeltafromfafreplayimportbody_offset,body_ticks# Split replay data into header and bodyoffset=body_offset(data)header_data,body_data=data[:offset],data[offset:]# Get replay length in ticksticks=body_ticks(body_data)print("Game length:",timedelta(milliseconds=ticks*100))Using the Parser objectTheParserobject can be used to get fine grained control over how replay
commands are parsed. Generally it's a good idea to parse only the minimum
commands needed, as conversion back to python dictionaries is quite expensive.fromdatetimeimporttimedeltafromfafreplayimportParser,commandsparser=Parser(# Skip all commands except the ones defined herecommands=[commands.Advance,# For the tick countercommands.VerifyChecksum,# For desync detection],# Throw away commands right after we parse them. Setting this to `True` will# significantly increase the parse time.save_commands=False,limit=None,stop_on_desync=False)# Or create a parser with default arguments (turn off save_commands though)# parser = Parser(save_commands=False)# Read replay to a `bytes` objectwithopen("12345.scfareplay","rb")asf:data=f.read()# Parse to a python dictionary. Data must of type `bytes` or `bytearray`replay=parser.parse(data)print("Game time:",timedelta(milliseconds=replay["body"]["sim"]["tick"]*100))ifreplay["body"]["sim"]["desync_ticks"]:print("Replay desynced!")Benchmark comparisonTo see how much faster the basic functions can be, consider this simple example
done on replay8653680(an almost 50 minute long Seton's game).>>>len(body_data)5586339>>>body_ticks(body_data)28917>>>parser=Parser(...commands=[commands.Advance],...save_commands=False...)>>>timeit.timeit("parser.parse_body(body_data)['sim']['tick']",globals=globals(),number=100)1.4510237049980788>>>timeit.timeit("body_ticks(body_data)",globals=globals(),number=100)0.20173147800232982In this casebody_ticksturned out to be more than 7x faster than using aParser.Reading .fafreplay filesReplays downloaded fromfaforever.comuse a compressed
data format to reduce the size of the files. These can be decompressed to the
original.scfareplaydata using theextract_scfafunction.fromfafreplayimportextract_scfawithopen("12345.scfareplay","rb")asf:scfa_data=f.read()withopen("12345.fafreplay","rb")asf:faf_data=extract_scfa(f)# The extracted data is in the .scfareplay formatassertfaf_data==scfa_dataNote that there are several versions of the.fafreplayformat. Version 1 uses
base64 and zlib compression which are both part of the python standard library.
However, version 2 useszstdwhich must be installed through a third party
package. To ensure that this dependency is installed you can use thefafextra
when installing the parser:pip install "faf-replay-parser[faf]"