Permalink
Browse files

Added library files

  • Loading branch information...
1 parent 7fb4fb2 commit 986725f80133537369c343ee91c3a1886e641ba6 @tec27 committed Nov 1, 2011
View
@@ -0,0 +1 @@
+node_modules
View
@@ -0,0 +1,8 @@
+
+test:
+ NODE_PATH=$(NODE_PATH):$(CURDIR)/lib expresso -c test/*.test.js
+
+cov:
+ node-jscoverage lib lib-cov; NODE_PATH=$(NODE_PATH):$(CURDIR)/lib-cov expresso -c test/*.test.js; rm -rf lib-cov
+
+.PHONY: test cov
View
@@ -0,0 +1,163 @@
+var fs = require('fs')
+ , path = require('path')
+ , async = require('async')
+ , bigint = require('bigint')
+ , mpyq = require('./mpyq')
+ , comsat_util = require('./comsat_util')
+ , headerParser = require('./parsers/header')
+ , detailsParser = require('./parsers/details')
+ , attributesParser = require('./parsers/attributes')
+
+var Replay = module.exports = function Replay(filename) {
+ this.filename = filename;
+ this._loaded = false;
+}
+
+Replay.prototype = {
+ _mergeData: function(data, callback) {
+ if(!data.header && !data.detail) { // TODO: add the rest of the data files to this list
+ callback('No replay data files could be read!');
+ return;
+ }
+
+ this.info = new Info();
+ this.players = [];
+ this.map = new Map();
+ this.messages = [];
+ this.actions = [];
+
+ if(data.header) {
+ var hVersion = data.header.version;
+ this.info.version = { major: hVersion.major, minor: hVersion.minor, patch: hVersion.patch, build: hVersion.build };
+ this.info.gameLength = data.header.game_length;
+ }
+
+ if(data.details) {
+ for(var i = 0; i < data.details.players.length; i++) {
+ var detailPlayer = data.details.players[i];
+ var newPlayer = this.players[i] = this.players[i] || new Player();
+ newPlayer.name = detailPlayer.name;
+ newPlayer.bnetId = detailPlayer.idInfo.realId;
+ newPlayer.race = detailPlayer.race; // TODO: this would be better to come from initialization events, this value is localized
+ newPlayer.color = newPlayer.color || { };
+ newPlayer.color.argb = detailPlayer.color;
+ // newPlayer.team = detailPlayer.team; teams from replay detail are not actually correct
+ newPlayer.handicap = detailPlayer.handicap;
+ newPlayer.outcome = detailPlayer.outcome;
+
+ /* teams from detail are not correct, DO NOT USE
+ if(newPlayer.team) {
+ console.log(newPlayer.name + ' is on team ' + newPlayer.team + " (detail: " + detailPlayer.team);
+ this.info.teams = this.info.teams || [];
+ this.info.teams[newPlayer.team] = this.info.teams[newPlayer.team] || [];
+ this.info.teams[newPlayer.team].push(newPlayer);
+ }*/
+ }
+
+ this.info.region = data.details.mapFiles[0].region.toUpperCase();
+ var unixDate = data.details.datetime.sub(bigint('116444735995904000')).div(bigint(10).pow(7)).toNumber(); // converts from Windows nano time to unix time (so we don't have to deal with bigints outside of this module)
+ var timezoneOffsetSec = data.details.timezoneOffset.div(bigint(10).pow(7)).toNumber();
+ this.info.date = { value: unixDate, timezoneOffset: timezoneOffsetSec };
+ }
+
+ this._loaded = true;
+ callback();
+ },
+
+ load: function(deleteFile, callback) {
+ // vars to access things on 'this' inside of callbacks
+ var replay = this;
+
+ mpyq.extractReplay(replay.filename, deleteFile, function(err,extractDir) {
+ if(err)
+ {
+ callback(err);
+ return;
+ }
+
+ var headerFile = path.join(extractDir, 'replay.header');
+ var detailsFile = path.join(extractDir, 'replay.details');
+
+ async.parallel(
+ {
+ header: function(callback) {
+ fs.readFile(headerFile, function(err, data) { headerParser.parse(err, data, callback); });
+ },
+ details: function(callback) {
+ fs.readFile(detailsFile, function(err, data) { detailsParser.parse(err, data, callback); });
+ },
+ },
+ function(err, results) {
+ if(err) {
+ console.log("Error when parsing replay files: " + err);
+ return;
+ }
+ if(deleteFile)
+ comsat_util.rm_rf(extractDir, function(err) { if(err) console.log("Error deleting extracted replay directory: " + err); });
+
+ replay._mergeData(results, callback);
+ });
+ });
+ }
+}
+
+
+function Info() {
+ this.version = null;
+ this.region = null;
+ this.gameLength = null;
+ this.date = null;
+ this.format = null;
+ this.speed = null;
+ this.teams = null;
+ this.gameType = null;
+ this.recordedBy = null;
+}
+
+Info.prototype = {
+ gameLengthInSeconds: function() {
+ var framesPerSecond = 16;
+ switch(this.speed) { // thanks for the conversions liquipedia! :D
+ case 'Faster': framesPerSecond = 22.06897; break;
+ case 'Fast': framesPerSecond = 19.33534; break;
+ case 'Slow': framesPerSecond = 12.8; break;
+ case 'Slower': framesPerSecond = 9.63855; break;
+ }
+
+ return Math.round(this.gameLength / framesPerSecond);
+ }
+}
+
+function Player(name) {
+ this.name = name;
+ this.bnetId = null;
+ this.race = null;
+ this.color = null;
+ this.team = null;
+ this.handicap = null;
+ this.outcome = null;
+ this.playerType = null;
+ this.difficulty = null;
+}
+
+Player.prototype = {
+ isWinner: function() {
+ return this.outcome === 1;
+ },
+
+ isLoser: function() {
+ return this.outcome === 2;
+ },
+
+ hasKnownOutcome: function() {
+ return this.outcome != 0;
+ }
+}
+
+function Map() {
+ this.name = null;
+ this.minimapFilename = null;
+ this.hashes = null;
+ this.description = null;
+ this.author = null;
+}
View
@@ -0,0 +1,31 @@
+var path = require('path')
+ , async = require('async')
+ , fs = require('fs')
+
+var rm_rf = module.exports.rm_rf = function(dirpath, callback) {
+ callback = callback || function () {};
+
+ fs.readdir(dirpath, function(err, files) {
+ var tasks = [];
+ for(var i = 0; i < files.length; i++)
+ {
+ tasks.push(async.apply(fs.unlink, path.join(dirpath, files[i])));
+ }
+ async.parallel(tasks, function(err, results) {
+ if(err) callback(err);
+ else fs.rmdir(dirpath, callback);
+ });
+ });
+}
+
+
+
+var generateRandomFilename = module.exports.generateRandomFilename = function() {
+ var name = '';
+ for (var i = 0; i < 32; i++) {
+ name += Math.floor(Math.random() * 16).toString(16);
+ }
+
+ return name;
+}
+
View
@@ -0,0 +1,64 @@
+var path = require('path')
+ , child_process = require('child_process')
+ , fs = require('fs')
+ , comsat_util = require('./comsat_util')
+
+var extractReplay = module.exports.extractReplay = function extractReplay(filename, deleteFile, callback) {
+ // TODO: enable passing output directory as parameter
+ var replayExtractPath = path.join('/tmp', comsat_util.generateRandomFilename());
+ var mpyq = child_process.spawn('python', [__dirname + '/mpyq/extract_replay.py', filename, replayExtractPath]);
+ mpyq.stderr.on('data', function(data) {
+ //console.log('mpyq stderr: %s', data);
+ });
+ mpyq.on('exit', function(code) {
+ if(code != 0) {
+ callback('mpyq exited with error code: ' + code);
+ return;
+ }
+ if(deleteFile) {
+ fs.unlink(filename, function(err) {
+ //if(err)
+ // console.log('Error deleting %s: %s', filename, err);
+ //else
+ // console.log('Successfully deleted %s', filename);
+ });
+ }
+ callback(null, replayExtractPath);
+ });
+}
+
+var downloadMap = module.exports.downloadMap = function downloadMap(region, hash, callback) {
+ var url = 'http://' + region.toLowerCase() + '.depot.battle.net:1119';
+ url += '/' + hash + '.s2ma';
+ var localFile = path.join('/tmp', hash);
+ var curlCommand = 'curl -sL -w "%{http_code}\\\\n" -o ' + localFile + ' ' + url;
+ child_process.exec(curlCommand, function(error, stdout, stderr) {
+ if(error || stderr)
+ callback('Error downloading map (error: "' + error + '" stderr: "' + stderr + '"', '');
+ else
+ callback(null, localFile);
+ });
+}
+
+var extractMap = module.exports.extractMap = function extractMap(filename, minimapFile, deleteFile, callback) {
+ // TODO: enable specifying output directories
+ var mpyq = child_process.spawn('python', [__dirname + '/mpyq/extract_map.py', filename, minimapFile]);
+ mpyq.stderr.on('data', function(data) {
+ //console.log('mpyq stderr: %s', data);
+ });
+ mpyq.on('exit', function(code) {
+ if(code != 0) {
+ callback('mpyq exited with error code: ' + code, '');
+ return;
+ }
+ if(deleteFile) {
+ fs.unlink(filename, function(err) {
+ //if(err)
+ // console.log('Error deleting %s: %s', localFile, err);
+ //else
+ // console.log('Successfully deleted %s', localFile);
+ });
+ }
+ callback(null, filename + '-extracted');
+ });
+}
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+import sys
+import os
+import re
+from mpyq import MPQArchive
+
+def extract_map(mpq, thumbnail_name=None, outdir=None):
+ """Extract a map's thumbnail image and language files"""
+ archive_name, extension = os.path.splitext(mpq.file.name)
+ if thumbnail_name is None:
+ thumbnail_name = 'Minimap.tga'
+ if outdir is None:
+ outdir = archive_name + '-extracted'
+ if not os.path.isdir(os.path.join(os.getcwd(), outdir)):
+ os.mkdir(outdir)
+ os.chdir(outdir)
+
+ local_regex = re.compile(r'^[a-z][a-z][A-Z][A-Z]\.SC2Data\\LocalizedData\\GameStrings\.txt$')
+ for filename in mpq.files:
+ if filename == thumbnail_name:
+ data = mpq.read_file(filename)
+ f = open(filename, 'wb')
+ f.write(data)
+ f.close()
+ elif local_regex.match(filename):
+ data = mpq.read_file(filename)
+ f = open(filename, 'wb')
+ f.write(data)
+ f.close()
+
+if len(sys.argv) < 2:
+ print 'syntax: extract_map.py <map> [thumbnail-name] [/output/dir/]'
+ sys.exit(1)
+
+mapdir = os.path.dirname(sys.argv[1])
+if mapdir == "":
+ mapdir = "."
+os.chdir(mapdir)
+archive = MPQArchive(sys.argv[1])
+outdir = None
+thumbnail_name = None
+if len(sys.argv) >= 3:
+ outdir = sys.argv[2]
+if len(sys.argv) >= 4:
+ outdir = sys.argv[3]
+extract_map(archive, outdir)
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+import sys
+import os
+from mpyq import MPQArchive
+
+def extract_to_disk(mpq, outdir=None):
+ """Extract all files and write them to disk."""
+ archive_name, extension = os.path.splitext(mpq.file.name)
+ if outdir is None:
+ outdir = archive_name + '-extracted'
+ if not os.path.isdir(os.path.join(os.getcwd(), outdir)):
+ os.mkdir(outdir)
+ os.chdir(outdir)
+ if mpq.header['user_data_header']:
+ f = open('replay.header', 'wb')
+ f.write(mpq.header['user_data_header']['content'])
+ f.close()
+ for filename, data in mpq.extract().items():
+ f = open(filename, 'wb')
+ f.write(data)
+ f.close()
+
+if len(sys.argv) < 2:
+ print 'syntax: extract_replay.py <replay> [/output/dir/]'
+ sys.exit(1)
+
+repdir = os.path.dirname(sys.argv[1])
+if repdir == "":
+ repdir = "."
+os.chdir(repdir)
+archive = MPQArchive(sys.argv[1])
+outdir = None
+if len(sys.argv) >= 3:
+ outdir = sys.argv[2]
+extract_to_disk(archive, outdir)
@@ -0,0 +1 @@
+
Oops, something went wrong.

0 comments on commit 986725f

Please sign in to comment.