# revival-patch_path.py - one of the three game scripts the Super Mecha Crows Android kit adds. # The installer puts it into the game's update file (patch_npk/p_script.npk on the phone) under the game's own name # patch.patch_path, next to the game's original module, which it copied out of YOUR phone's game under # patch.patch_path_original. This script loads that original unchanged, then runs the Crows part below inside it. import sys import patch.patch_path_original as _revival_original sys.modules['patch.patch_path'] = _revival_original _REVIVAL_PART = "\n# ---- [REVIVAL] Android: NetEase names -> the Crows server inside the game's own Python (docs/ANDROID_LOGIN.md) ----\n# Runs inside the game's patch/patch_path module (imported by the patch stage before its first request), so it needs\n# no root, no hosts file and no system certificate on the phone. Only on Android; anything else returns at once.\n# 1. socket.getaddrinfo / gethostbyname(_ex): the friend kit's hostnames.txt names -> the server's address.\n# 2. SSL_CERT_FILE = Documents/cacert.pem: the patch stage's bare urllib3.PoolManager() finds no CA on Android\n# (the installer appended the Crows certificate to that file).\n# 2b. Some phones (Galaxy Z Fold, Android 17) never let the engine's OpenSSL see SSL_CERT_FILE: the default context\n# holds 0 CAs. SSLContext.load_default_certs / load_verify_locations(cafile) also hand OpenSSL the PEM blocks of\n# that file as cadata, read by Python (fix found by the rv2 tester, 2026-10-03).\n# 3. patch.downloader.OrbitDownloader -> Downloader: NetEase's Java downloader resolves names itself and would\n# fetch NetEase's live patch list; the Python downloader goes through step 1.\n# Every step is guarded and logs to Documents/smc_redirect.log (host + path only). No print(): it crashes this client.\ntry:\n import game3d as _rr_g3d\n if _rr_g3d.get_platform() == _rr_g3d.PLATFORM_ANDROID:\n import os as _rr_os\n import sys as _rr_sys\n import socket as _rr_socket\n _RR_SERVER = 'horsecock.art' # looked up through the real DNS once (a server move needs no new kit)\n _RR_FALLBACK_IP = '213.239.210.107' # the address when this kit was made, if that lookup fails\n _RR_IP = [None]\n _RR_HOSTS = frozenset(['api.cc.163.com', 'api.vop.netease.com', 'appdump.nie.easebar.com', 'appdump.nie.netease.com', 'applog.matrix.easebar.com', 'applog.matrix.netease.com', 'clientdump.x.netease.com', 'data-detect.nie.easebar.com', 'data-detect.nie.netease.com', 'dl.nstool.easebar.com', 'docs.popo.netease.com', 'fp-dev.webapp.163.com', 'g0.gsf.easbar.com', 'g0.gsf.netease.com', 'g93-record.fp.ps.netease.com', 'g93.gph.netease.com', 'g93.update.easebar.com', 'g93.update.netease.com', 'g93na-all-web-login.nie.netease.com', 'g93na.update.easebar.com', 'g93na.update.netease.com', 'h5-change-channel-test.webapp.163.com', 'h5-change-channel.webapp.163.com', 'httpdns.nie.easebar.com', 'httpdns.nie.netease.com', 'hunter.nie.netease.com', 'impression.update.easebar.com', 'impression.update.netease.com', 'interact2.webapp.easebar.com', 'km.netease.com', 'mpay-personal-privacy-protection-dev.g.mkey.163.com', 'mpay-personal-privacy-protection.g.mkey.163.com', 'netlink-sigma.proxima.nie.netease.com', 'nie.v.netease.com', 'nstool.netease.com', 'office-nile-activ.nie.netease.com', 'protocol.unisdk.netease.com', 'proxy-state.nie.easebar.com', 'proxy-state.nie.netease.com', 'qaaccount.netease.com', 'research-game.163.com', 'research.easebar.com', 'share.easebar.com', 'sigma-orbitv3-impression.proxima.nie.easebar.com', 'sigma-orbitv3-impression.proxima.nie.netease.com', 'sm-g93na-1s9l3dqf-prod-ws.nie.easebar.com', 'smc.163.com', 'standby-nile-activ.nie.netease.com', 'test-nile-activ.nie.netease.com', 'test.nie.163.com', 'unisdk.update.netease.com', 'update.my.163.com', 'uu.163.com', 'who.easebar.com', 'who.nie.netease.com'])\n _RR_LOG = '{}/smc_redirect.log'.format(_rr_g3d.get_doc_dir())\n _RR_SEEN = set()\n\n def _rr_log(msg):\n try:\n with open(_RR_LOG, 'a') as _f:\n _f.write(msg + '\\n')\n except Exception:\n pass\n\n def _rr_map(host):\n try:\n h = host.decode('ascii') if isinstance(host, bytes) else host\n key = (h or '').strip().rstrip('.').lower()\n except Exception:\n return host\n if key in _RR_HOSTS:\n if key not in _RR_SEEN:\n _RR_SEEN.add(key)\n _rr_log('redirect ' + key)\n return _rr_server_ip()\n if key and key not in _RR_SEEN and not key[:1].isdigit():\n _RR_SEEN.add(key)\n _rr_log('pass ' + key)\n return host\n\n if not getattr(_rr_socket, '_rr_armed', False):\n _rr_real_ghbn = _rr_socket.gethostbyname\n\n def _rr_server_ip():\n if _RR_IP[0] is None:\n try:\n _RR_IP[0] = _rr_real_ghbn(_RR_SERVER)\n _rr_log('server %s -> %s' % (_RR_SERVER, _RR_IP[0]))\n except Exception as _e:\n _RR_IP[0] = _RR_FALLBACK_IP\n _rr_log('server %s lookup failed (%s), using %s' % (_RR_SERVER, type(_e).__name__, _RR_FALLBACK_IP))\n return _RR_IP[0]\n _rr_orig_gai = _rr_socket.getaddrinfo\n _rr_orig_ghbn = _rr_socket.gethostbyname\n _rr_orig_ghbn_ex = _rr_socket.gethostbyname_ex\n\n def _rr_getaddrinfo(host, *args, **kwargs):\n return _rr_orig_gai(_rr_map(host), *args, **kwargs)\n\n def _rr_gethostbyname(host):\n return _rr_orig_ghbn(_rr_map(host))\n\n def _rr_gethostbyname_ex(host):\n return _rr_orig_ghbn_ex(_rr_map(host))\n _rr_socket.getaddrinfo = _rr_getaddrinfo\n _rr_socket.gethostbyname = _rr_gethostbyname\n _rr_socket.gethostbyname_ex = _rr_gethostbyname_ex\n _rr_socket._rr_armed = True\n _rr_log('armed %d names -> %s' % (len(_RR_HOSTS), _RR_SERVER))\n\n try:\n _rr_ca = '{}/cacert.pem'.format(_rr_g3d.get_doc_dir())\n if _rr_os.path.isfile(_rr_ca):\n _rr_os.environ['SSL_CERT_FILE'] = _rr_ca\n _rr_log('SSL_CERT_FILE -> ' + _rr_ca)\n else:\n _rr_log('no cacert.pem at ' + _rr_ca)\n except Exception as _rr_exc:\n _rr_log('SSL_CERT_FILE failed: %r' % (_rr_exc,))\n\n try:\n import ssl as _rr_ssl\n if not getattr(_rr_ssl, '_rr_ca_fixed', False):\n _rr_pem = '{}/cacert.pem'.format(_rr_g3d.get_doc_dir())\n _rr_orig_ldc = _rr_ssl.SSLContext.load_default_certs\n _rr_orig_lvl = _rr_ssl.SSLContext.load_verify_locations\n\n def _rr_read_pem(path):\n # only the PEM blocks: the game's cacert.pem has non-ASCII CA names in its comment lines,\n # and cadata must be ASCII\n import re as _rr_re\n with open(path, 'rb') as _f:\n text = _f.read().decode('ascii', 'ignore')\n blocks = _rr_re.findall(r'-----BEGIN CERTIFICATE-----[^-]+-----END CERTIFICATE-----', text)\n return '\\n'.join(blocks) + '\\n'\n\n def _rr_ldc(self, *args, **kwargs):\n r = _rr_orig_ldc(self, *args, **kwargs)\n try:\n _rr_orig_lvl(self, cadata=_rr_read_pem(_rr_pem))\n except Exception as _e:\n _rr_log('CA fix: load_default_certs add failed: %s' % (type(_e).__name__,))\n return r\n\n def _rr_lvl(self, cafile=None, capath=None, cadata=None):\n if cafile and capath is None and cadata is None:\n try:\n return _rr_orig_lvl(self, cadata=_rr_read_pem(cafile))\n except Exception as _e:\n _rr_log('CA fix: cadata failed (%s), stock call' % (type(_e).__name__,))\n return _rr_orig_lvl(self, cafile, capath, cadata)\n\n _rr_ssl.SSLContext.load_default_certs = _rr_ldc\n _rr_ssl.SSLContext.load_verify_locations = _rr_lvl\n _rr_ssl._rr_ca_fixed = True\n _rr_log('CA fix armed: default context holds %r' % (\n _rr_ssl.create_default_context().cert_store_stats(),))\n except Exception as _rr_exc:\n _rr_log('CA fix failed: %r' % (_rr_exc,))\n\n try:\n import urllib3 as _rr_u3\n if not getattr(_rr_u3, '_rr_logged', False):\n _rr_orig_urlopen = _rr_u3.PoolManager.urlopen\n\n def _rr_short(url):\n return str(url).split('?', 1)[0][:160]\n\n def _rr_urlopen(self, method, url, *args, **kwargs):\n try:\n r = _rr_orig_urlopen(self, method, url, *args, **kwargs)\n _rr_log('http %s %s -> %s' % (method, _rr_short(url), getattr(r, 'status', '?')))\n return r\n except Exception as _e:\n _rr_log('http %s %s raised %s (%s)' % (method, _rr_short(url), type(_e).__name__,\n type(getattr(_e, 'reason', None)).__name__))\n raise\n _rr_u3.PoolManager.urlopen = _rr_urlopen\n _rr_u3._rr_logged = True\n except Exception as _rr_exc:\n _rr_log('http log hook failed: %r' % (_rr_exc,))\n\n try:\n import social as _rr_social\n if not getattr(_rr_social, '_rr_orbit_off', False):\n _rr_orig_get_channel = _rr_social.get_channel\n _rr_done = [False]\n\n def _rr_get_channel(*args, **kwargs):\n if not _rr_done[0]:\n dl = _rr_sys.modules.get('patch.downloader')\n if dl is not None and hasattr(dl, 'Downloader') and hasattr(dl, 'OrbitDownloader'):\n _rr_done[0] = True\n try:\n dl.OrbitDownloader = dl.Downloader\n _rr_log('orbit off: patch.downloader.OrbitDownloader -> Downloader')\n except Exception as _e:\n _rr_log('orbit off failed: %r' % (_e,))\n return _rr_orig_get_channel(*args, **kwargs)\n _rr_social.get_channel = _rr_get_channel\n _rr_social._rr_orbit_off = True\n _rr_log('orbit switch armed')\n except Exception as _rr_exc:\n _rr_log('orbit switch failed: %r' % (_rr_exc,))\nexcept Exception:\n pass\n" exec(compile(_REVIVAL_PART, 'revival-patch_path', 'exec'), vars(_revival_original)) globals().update({_k: _v for _k, _v in vars(_revival_original).items() if not _k.startswith('__')})