#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import types
import platform
import ctypes
import binascii
import re

sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from xqimage import *
from gateway import *
from read_info import *
from envbuffer import *


def i2b(value):
  return value.to_bytes(4, byteorder='little')

def build_sign(model, ver):
  model = model.upper()
  payload = None
  if model == "R3G":        # TEST
    poffset = 0x1070
    payload = b'\x66' * 16  # TEST
  if model == "RA69":
    poffset = 0x1070
    payload = i2b(0x4152A8) + i2b(0) + i2b(0x402634) + i2b(0)
  if model == "RA70":
    poffset = 0x1078
    payload = i2b(0x4152D0) + i2b(0) + i2b(0x40265C) + i2b(0)
  if model == "RA72":
    poffset = 0x1078
    payload = i2b(0x4152E0) + i2b(0) + i2b(0x402630) + i2b(0)
  if model == "R3600":
    poffset = 0x1070
    payload = i2b(0x415290) + i2b(0) + i2b(0x402634) + i2b(0)
  if model == "RB03":
    poffset = 0x1078
    payload = i2b(0x4148B0) + i2b(0) + i2b(0x40263C) + i2b(0)
  if not payload:
    die('Payload is not defined for device "{}".'.format(model))
  # add header of sign section (16 bytes)
  sign = (poffset + len(payload)).to_bytes(4, byteorder='little') + b'\x00' * 12
  # add fake sign 
  sign += b'\xEA' * poffset
  # add payload
  sign += payload
  return sign


gw = Gateway(timeout = 4)
if gw.status < 1:
  die("Xiaomi Mi Wi-Fi device not found (IP: {})".format(gw.ip_addr))

print("device_name =", gw.device_name)
print("rom_version = {} {}".format(gw.rom_version, gw.rom_channel))
print("MAC Address = {}".format(gw.mac_address))

signature = build_sign(gw.device_name, gw.rom_version)

dinfo = DevInfo(gw, verbose = 0, infolevel = 0)
syslog = SysLog(gw, timeout = 10, verbose = 1, infolevel = 2)

syslog.verbose = 2
bdata = syslog.parse_bdata()
bdata.var['boot_wait'] = "on"
bdata.var['uart_en'] = "1"
bdata.var['telnet_en'] = "1"
bdata.var['ssh_en'] = "1"
bdata.var['CountryCode'] = "EU"
partname = 'bdata'
bdata.mtd = syslog.get_mtd_by_name(partname)
if not bdata.mtd:
  die('MTD partition "{}" not found!'.format(partname))
#bdata_env_size = 0x4000
bdata_env_size = 0x10000   # fixed size of BData environment buffer
bdata.buf = bdata.pack(bdata_env_size)
bdata.buf += b'\xFF' * (bdata.mtd.size - len(bdata.buf))
bdata.img = XQImage(gw.device_name)
bdata.img.add_version(gw.rom_version)
bdata.img.add_file(bdata.buf, 'bdata.bin', mtd = bdata.mtd.id)

partname = 'crash'
crash_mtd = syslog.get_mtd_by_name(partname)
if not crash_mtd:
  die('MTD partition "{}" not found!'.format(partname))

def create_crash_image(mtd, prefix, outfilename):
  crash = types.SimpleNamespace()
  crash.mtd = mtd
  if prefix is None:
    prefix = b''
  crash.buf = bytearray(prefix + b'\xFF' * (mtd.size - len(prefix)))
  crash.img = XQImage(gw.device_name)
  crash.img.add_version(gw.rom_version)
  crash.img.add_file(crash.buf, 'crash.bin', mtd = mtd.id)
  crash.img.save_image(signature, outfilename)
  print('Created hacked image file: "{}"'.format(outfilename))
  return crash

# image for activate "factory mode" via uboot (insert factory_mode=1 into kernel cmdline)
fn_crash1 = 'outdir/image_{device}_1_crash.bin'.format(device = gw.device_name)
crash1 = create_crash_image(crash_mtd, b'\xA5\x5A\x00\x00', fn_crash1)

# image for change BData environment
bdata.img.save_image(signature, 'outdir/image_{device}_2_bdata.bin'.format(device = gw.device_name))
print('Created hacked image file: "{}"'.format(bdata.img.outfilename))

# image for deactivate "factory mode" via uboot
fn_crash3 = 'outdir/image_{device}_3_crash.bin'.format(device = gw.device_name)
crash3 = create_crash_image(crash_mtd, None, fn_crash3)

print("OK_finish")

