You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			194 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			194 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
#!/usr/bin/env python3
 | 
						|
# -*- coding: utf-8 -*-
 | 
						|
 | 
						|
import os
 | 
						|
import sys
 | 
						|
import types
 | 
						|
import platform
 | 
						|
import ctypes
 | 
						|
import binascii
 | 
						|
 | 
						|
 | 
						|
class XQImgHdr(ctypes.Structure):
 | 
						|
  _fields_ = [("magic",   ctypes.c_uint),      # HDR1
 | 
						|
              ("sign",    ctypes.c_uint),      # offset of sign block
 | 
						|
              ("crc32",   ctypes.c_uint),      # crc32 check sum
 | 
						|
              ("type",    ctypes.c_ushort),    # ROM type (12 = miwifi_ssh.bin)
 | 
						|
              ("model",   ctypes.c_short),     # device number
 | 
						|
              ("files",   ctypes.c_uint * 8)]  # array of section-offset
 | 
						|
 | 
						|
class XQImgFile(ctypes.Structure):
 | 
						|
  _fields_ = [("magic",   ctypes.c_ushort),    # BE BA
 | 
						|
              ("rsvd0",   ctypes.c_ushort), 
 | 
						|
              ("addr",    ctypes.c_uint),      # Flash Address
 | 
						|
              ("size",    ctypes.c_uint),      # size of file
 | 
						|
              ("mtd",     ctypes.c_short),     # mtd number for flashing
 | 
						|
              ("dummy",   ctypes.c_short),
 | 
						|
              ("name",    ctypes.c_char * 32)] # Filename
 | 
						|
 | 
						|
xqModelList = [
 | 
						|
  "<unk0>",
 | 
						|
  "<unk1>",
 | 
						|
  "<unk2>",
 | 
						|
  "R1CM",    # 3
 | 
						|
  "R2D",     # 4
 | 
						|
  "R1CL",
 | 
						|
  "R2CM",
 | 
						|
  "R3",
 | 
						|
  "R3D",     # 8
 | 
						|
  "R3L",
 | 
						|
  "R3P",     # 10
 | 
						|
  "P01",
 | 
						|
  "R3A",
 | 
						|
  "R3G",     # 13
 | 
						|
  "R4",
 | 
						|
  "R4C",
 | 
						|
  "D01",
 | 
						|
  "R4A",
 | 
						|
  "R4CM",
 | 
						|
  "R4AC",
 | 
						|
  "R3GV2",   # 20
 | 
						|
  "R2600",
 | 
						|
  "R2100",   # 22
 | 
						|
  "RM2100",  # 23
 | 
						|
  "R3600",   # 24
 | 
						|
  "R1350",
 | 
						|
  "R2200",
 | 
						|
  "R2350",   # 27
 | 
						|
  "IR1200G",
 | 
						|
  "RM1800",
 | 
						|
  "R2100D",  # 30
 | 
						|
  "RA67",
 | 
						|
  "RA69",
 | 
						|
  "RA71",
 | 
						|
  "CR6006",  # 34
 | 
						|
  "CR6008",
 | 
						|
  "CR6009",
 | 
						|
  "RA70",
 | 
						|
  "RA75",
 | 
						|
  "RA72",
 | 
						|
  "<unk40>", # 40
 | 
						|
  "<unk41>",
 | 
						|
  "<unk42>",
 | 
						|
  "RA80",
 | 
						|
  "RA81",
 | 
						|
  "RA82",
 | 
						|
  "RA83",
 | 
						|
  "RA74",
 | 
						|
  "<unk48>",
 | 
						|
  "YY01",
 | 
						|
  "RB01",    # 50
 | 
						|
  "RB03"     # 51
 | 
						|
]
 | 
						|
 | 
						|
def get_modelid_by_name(name):
 | 
						|
  for i, m in enumerate(xqModelList):
 | 
						|
    if m.lower() == name.lower():
 | 
						|
      return i
 | 
						|
  return -1
 | 
						|
 | 
						|
 | 
						|
class XQImage():
 | 
						|
  model = None
 | 
						|
  type = 0
 | 
						|
  version = None
 | 
						|
  files = []  # list of files
 | 
						|
  
 | 
						|
  def __init__(self, model, type = 0):
 | 
						|
    self.model = model.upper()
 | 
						|
    self.type = type
 | 
						|
    self.header = XQImgHdr()
 | 
						|
    self.version = None
 | 
						|
    self.files = []
 | 
						|
 | 
						|
  def add_version(self, version, channel = 'release'):
 | 
						|
    self.version = None
 | 
						|
    if version is None:
 | 
						|
      return
 | 
						|
    data = "config core 'version'\n"
 | 
						|
    data += "\t" + "option ROM '{}'\n".format(version)
 | 
						|
    if channel:
 | 
						|
      data += "\t" + "option CHANNEL '{}'\n".format(channel.lower())
 | 
						|
    data += "\t" + "option HARDWARE '{}'\n".format(self.model)
 | 
						|
    self.version = data.encode('latin_1')
 | 
						|
    if len(self.version) & 3 != 0:
 | 
						|
      self.version += b'\x00' * (4 - len(self.version) & 3)
 | 
						|
    self.add_file(self.version, 'xiaoqiang_version')
 | 
						|
 | 
						|
  def add_file(self, data, name, mtd = None):
 | 
						|
    file = types.SimpleNamespace()
 | 
						|
    file.data = data
 | 
						|
    file.header = XQImgFile()
 | 
						|
    file.header.magic = int.from_bytes(b'\xBE\xBA', byteorder='little')
 | 
						|
    file.header.rsvd0 = 0 
 | 
						|
    file.header.addr = 0xFFFFFFFF
 | 
						|
    file.header.size = len(data)
 | 
						|
    file.header.mtd = 0xFFFF if mtd is None else mtd
 | 
						|
    file.header.dummy = 0
 | 
						|
    file.header.name = name.encode('latin_1')
 | 
						|
    self.files.append(file)
 | 
						|
  
 | 
						|
  def build_image(self, sign = None):
 | 
						|
    buf = bytearray()
 | 
						|
    self.header = XQImgHdr()
 | 
						|
    self.header.magic = int.from_bytes(b'HDR1', byteorder='little')
 | 
						|
    self.header.sign = 0
 | 
						|
    self.header.crc32 = 0
 | 
						|
    self.header.type = self.type
 | 
						|
    self.header.model = get_modelid_by_name(self.model)
 | 
						|
    buf += bytes(self.header)
 | 
						|
    for i, f in enumerate(self.files):
 | 
						|
      self.header.files[i] = len(buf)
 | 
						|
      buf += bytes(f.header)
 | 
						|
      buf += f.data
 | 
						|
    self.header.sign = len(buf)
 | 
						|
    if sign:    
 | 
						|
      buf += sign
 | 
						|
    else:
 | 
						|
      buf += self.build_sign()
 | 
						|
    self.header.crc32 = 0
 | 
						|
    buf[:ctypes.sizeof(self.header)] = bytes(self.header)
 | 
						|
    self.header.crc32 = 0xFFFFFFFF - binascii.crc32(buf[12:])  # JAMCRC
 | 
						|
    buf[:ctypes.sizeof(self.header)] = bytes(self.header)
 | 
						|
    return buf
 | 
						|
  
 | 
						|
  def save_image(self, filename, sign = None):
 | 
						|
    self.outfilename = filename
 | 
						|
    buf = self.build_image(sign)
 | 
						|
    with open(filename, 'wb') as file:
 | 
						|
      file.write(buf)
 | 
						|
 | 
						|
  def build_sign(self):
 | 
						|
    def i2b(value):
 | 
						|
      return value.to_bytes(4, byteorder='little')
 | 
						|
    payload = None
 | 
						|
    if self.model == "R3G":
 | 
						|
      poffset = 0x1058
 | 
						|
      payload = i2b(0x416078) + i2b(0) + i2b(0) + i2b(0x402810)
 | 
						|
    if self.model == "R3600":  # AX3600
 | 
						|
      poffset = 0x1070
 | 
						|
      payload = i2b(0x415290) + i2b(0) + i2b(0x402634) + i2b(0)
 | 
						|
    if self.model == "RA69":   # AX6
 | 
						|
      poffset = 0x1070
 | 
						|
      payload = i2b(0x4152A8) + i2b(0) + i2b(0x402634) + i2b(0)
 | 
						|
    if self.model == "RA70":   # AX9000
 | 
						|
      poffset = 0x1078
 | 
						|
      payload = i2b(0x4152D0) + i2b(0) + i2b(0x40265C) + i2b(0)
 | 
						|
    if self.model == "RA72":   # AX6000
 | 
						|
      poffset = 0x1078
 | 
						|
      payload = i2b(0x4152E0) + i2b(0) + i2b(0x402630) + i2b(0)
 | 
						|
    if not payload:
 | 
						|
      raise OSError('HDR1 Payload is not defined for device "{}".'.format(self.model))
 | 
						|
    # add header of sign section (16 bytes)
 | 
						|
    sign = i2b(poffset) + (b'\x00' * 12)
 | 
						|
    # add fake sign 
 | 
						|
    size = poffset - len(payload)
 | 
						|
    for i in range(0, size, 4):
 | 
						|
      sign += (0xEAA00000 + i).to_bytes(4, byteorder='little')
 | 
						|
    # add payload
 | 
						|
    sign += payload
 | 
						|
    return sign
 | 
						|
 | 
						|
 | 
						|
 |