#!/usr/bin/env python3 #-*- coding:utf-8 -*- # Copyright 2017 Sapphire Becker (logicplace.com) # MIT Licensed import os, sys, argparse, logging import json import zlib import base64 import struct import urllib.request import urllib.error try: import NexonAPI except ImportError: NexonAPI = None else: import getpass #endtry class PatchServerError(Exception): pass class PatchServer: # Ideally one would log in and retrieve this from the Nexon API, but I'm not going to publish that! GAME_ID = "10200" BASE_URL = "https://download2.nexon.net/Game/nxl/games/" + GAME_ID + "/" HASH_URL = "{gameID}.{version}R.manifest.hash" MANIFEST_URL = "{hash}" PART_URL = "{gameID}/{part:.2}/{part}" def __init__(self): # But if you have a library for it already... if NexonAPI: self.BASE_URL = NexonAPI.getBaseURL() #endif self.local_version = None self.target_version = None self.manifest = None self.manifestVersion = None #enddef def _getURL(self, url, fileName=None, serverName=None): try: return urllib.request.urlopen(url) except urllib.error.HTTPError as err: if fileName is None: fileName = url.split("/")[-1] raise PatchServerError("Error retrieving {}: {}".format(fileName, str(err))) except urllib.error.URLError as err: if serverName is None: serverName = url.split("/", maxsplit=3)[2] raise PatchServerError("Could not connect {}: {}".format(serverName, str(err))) #endtry #enddfe def getWebLaunchStatus(self): """ Returns true if the web launcher thinks the game is up. """ conn = self._getURL("http://www.nexon.net/json/game_status.js", "status file") # Have to de-JSONp this. response = json.loads(conn.read()[len("nexon.games.playGame(") : -2].decode("utf8")) # This is Mabi's ID here status = response["SVG012"] logging.info("Web launch status is: {}.".format("UP" if status else "DOWN")) return status #enddef def legacyGetLatestVersion(self): """ Get the latest version as reported by the legacy launcher info. """ conn = self._getURL("http://mabipatchinfo.nexon.net/patch/patch.txt", "patch info file") # Format is a list of var=val, one per line. txt = conn.read().decode("utf8").split("\n") for line in txt: var, val = line.split("=", maxsplit=1) if var.strip() == "main_version": return int(val.strip()) #endfor raise PatchServerError("Version not found in patch info.") #enddef def getLatestVersion(self): """ Get the latest version as reported by some server. """ if NexonAPI is None: ver = self.legacyGetLatestVersion() else: ver = NexonAPI.getLatestVersion() #endif logging.info("Read latest version as {}.".format(ver)) self.target_version = ver return ver #enddef def getLocalVersion(self, path): """ Get the verion of the Mabinogi installed at the given path. """ try: with open(os.path.join(path, "version.dat"), "rb") as f: ver = struct.unpack("