mirror of
https://port.numenaute.org/aleajactaest/clientbot.git
synced 2024-10-05 00:08:50 +00:00
1645 lines
61 KiB
Python
Executable file
1645 lines
61 KiB
Python
Executable file
#!/usr/bin/python3
|
||
# -*- coding: utf-8 -*-
|
||
#
|
||
# script to emulate client khanat
|
||
#
|
||
# Copyright (C) 2019 AleaJactaEst
|
||
#
|
||
# This program is free software: you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation, either version 3 of the License, or
|
||
# (at your option) any later version.
|
||
#
|
||
# This program is distributed in the hope that it will be useful,
|
||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
# GNU General Public License for more details.
|
||
#
|
||
# You should have received a copy of the GNU General Public License
|
||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
# Ex.: ./client.py --khanaturl="172.17.0.3:40916" -d
|
||
|
||
# Modifier les droits pour les nouveaux joueurs (accès à tout)
|
||
# mysql -u root -e "use nel_ams_lib;UPDATE settings SET Value = 7 WHERE settings.Setting = 'Domain_Auto_Add';"
|
||
|
||
import argparse
|
||
import http.client
|
||
import crypt
|
||
import logging
|
||
import os.path
|
||
import sys
|
||
import urllib.request
|
||
import urllib.parse
|
||
import tempfile
|
||
from enum import IntEnum
|
||
from ctypes import *
|
||
import re
|
||
import random
|
||
import lzma
|
||
import socket
|
||
import struct
|
||
|
||
|
||
class BitStream():
|
||
def __init__(self):
|
||
self._pos = 0
|
||
self._read = 0
|
||
self._tampon = []
|
||
|
||
# ------------------------------------
|
||
def internalSerial(self, value, nbits):
|
||
if nbits == 0:
|
||
return
|
||
elif nbits > 32:
|
||
raise "Out of range"
|
||
pos = self._pos % 8
|
||
if pos == 0:
|
||
self._tampon.append(0)
|
||
# print(">", pos, value)
|
||
value = c_uint32(value).value
|
||
if nbits != 32:
|
||
mask = (1 << nbits) - 1;
|
||
v = value & mask;
|
||
else:
|
||
v = value;
|
||
_FreeBits = 8 - (self._pos % 8)
|
||
if nbits > _FreeBits:
|
||
#print(">A")
|
||
self._tampon[-1] |= (v >> ( nbits - _FreeBits))
|
||
self._pos += _FreeBits
|
||
self.internalSerial( v , nbits - _FreeBits)
|
||
else:
|
||
#print(">B")
|
||
self._tampon[-1] |= (v << ( _FreeBits - nbits))
|
||
self._pos += nbits
|
||
|
||
def pushBool(self, valeur):
|
||
if valeur:
|
||
v = 1
|
||
else:
|
||
v = 0
|
||
self.internalSerial(v, 1)
|
||
|
||
def pushUint32(self, valeur):
|
||
self.internalSerial(valeur, 32)
|
||
|
||
def pushSint32(self, valeur):
|
||
self.internalSerial(valeur, 32)
|
||
|
||
def pushUint16(self, valeur):
|
||
self.internalSerial(valeur, 16)
|
||
|
||
def pushSint16(self, valeur):
|
||
self.internalSerial(valeur, 16)
|
||
|
||
def pushUint8(self, valeur):
|
||
self.internalSerial(valeur, 8)
|
||
|
||
def pushSint8(self, valeur):
|
||
self.internalSerial(valeur, 8)
|
||
|
||
def pushUint64(self, valeur):
|
||
self.internalSerial(valeur, 32)
|
||
self.internalSerial(valeur >> 32, 32)
|
||
|
||
def pushSint64(self, valeur):
|
||
self.internalSerial(valeur, 32)
|
||
self.internalSerial(valeur >> 32, 32)
|
||
|
||
def pushFloat(self, valeur):
|
||
v = c_float(valeur).value
|
||
v1 = struct.pack('f', v)
|
||
v2 = struct.unpack('<i',v1)[0]
|
||
self.internalSerial(v2, 32)
|
||
|
||
def pushDouble(self, valeur):
|
||
v = c_double(valeur).value
|
||
v1 = struct.pack('d', v)
|
||
v2 = struct.unpack('<Q',v1)[0]
|
||
#self.internalSerial(v2, 32)
|
||
self.internalSerial(v2, 32)
|
||
self.internalSerial(v2 >> 32, 32)
|
||
|
||
def pushChar(self, valeur):
|
||
v = ord(valeur)
|
||
self.internalSerial(v, 8)
|
||
|
||
def pushString(self, valeur):
|
||
size=len(valeur)
|
||
print(size)
|
||
#self.internalSerial(size, 32)
|
||
self.pushUint32(len(valeur))
|
||
for x in valeur:
|
||
self.pushChar(x)
|
||
#y = ord(x)
|
||
#self.internalSerial(y, 8)
|
||
|
||
# ------------------------------------
|
||
def readSerial(self, nbits):
|
||
if nbits == 0:
|
||
return
|
||
elif nbits > 32:
|
||
raise "Out of range"
|
||
if self._read + nbits > self._pos:
|
||
raise "Stream Overflow"
|
||
value = 0
|
||
pos = self._read // 8
|
||
_FreeBits = 8 - (self._read % 8)
|
||
v = self._tampon[pos] & ((1 << _FreeBits) - 1)
|
||
if nbits > _FreeBits:
|
||
value |= (v << (nbits-_FreeBits))
|
||
self._read += _FreeBits
|
||
value |= self.readSerial(nbits - _FreeBits)
|
||
else:
|
||
value |= (v >> (_FreeBits-nbits))
|
||
self._read += nbits
|
||
return value
|
||
|
||
def readBool(self):
|
||
v = self.readSerial(1)
|
||
if v != 0:
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
def readUint32(self):
|
||
v = self.readSerial(32)
|
||
return v
|
||
|
||
def readSint32(self):
|
||
v = self.readSerial(32)
|
||
return c_int32(v).value
|
||
|
||
def readUint16(self):
|
||
v = self.readSerial(16)
|
||
return v
|
||
|
||
def readSint16(self):
|
||
v = self.readSerial(16)
|
||
return c_int16(v).value
|
||
|
||
def readUint8(self):
|
||
v = self.readSerial(8)
|
||
return v
|
||
|
||
def readSint8(self):
|
||
v = self.readSerial(8)
|
||
return c_int8(v).value
|
||
|
||
def readUint64(self):
|
||
v = self.readSerial(32)
|
||
v1 = self.readSerial(32)
|
||
v2 = v | (v1 << 32)
|
||
return v2
|
||
|
||
def readSint64(self):
|
||
v = self.readSerial(32)
|
||
v1 = self.readSerial(32)
|
||
v2 = v | (v1 << 32)
|
||
return c_int64(v2).value
|
||
|
||
def readFloat(self):
|
||
v = self.readSerial(32)
|
||
v1 = struct.pack('I', v)
|
||
v2 = struct.unpack('<f',v1)[0]
|
||
return v2
|
||
|
||
def readDouble(self):
|
||
v = self.readSerial(32)
|
||
v1 = struct.pack('I', v)
|
||
w = self.readSerial(32)
|
||
w1 = struct.pack('I', w)
|
||
x = v1 + w1
|
||
x1 = struct.unpack('<d', x)[0]
|
||
return x1
|
||
|
||
def readChar(self):
|
||
v = self.readUint8()
|
||
return chr(v)
|
||
|
||
def readString(self):
|
||
tmp = ''
|
||
_size = self.readUint32()
|
||
while _size > 0:
|
||
x = self.readChar()
|
||
tmp += x
|
||
_size -= 1
|
||
return tmp
|
||
|
||
# ------------------------------------
|
||
def __str__(self):
|
||
return ''.join([ chr(x) for x in self._tampon])
|
||
|
||
def message(self):
|
||
# return str(self._pos) + ':' + str(self._tampon)
|
||
return str(self._pos) + ':' + '.'.join([ format(x, "02x") for x in self._tampon])
|
||
|
||
def bytes(self):
|
||
return bytes( self._tampon )
|
||
|
||
def Test():
|
||
a = BitStream()
|
||
a.pushBool(True)
|
||
a.pushBool(False)
|
||
a.pushBool(True)
|
||
a.pushBool(True)
|
||
a.pushUint32(1234567890)
|
||
a.pushSint32(-1234567890)
|
||
a.pushUint16(12345)
|
||
a.pushSint16(-12345)
|
||
a.pushUint8(123)
|
||
a.pushSint8(-123)
|
||
a.pushFloat(-3.3999999521443642e+38) #-3.4E+38) # 1.2339999675750732)
|
||
a.pushDouble(-1.7E+308)
|
||
a.pushUint64(16045690709418696365)
|
||
a.pushSint64(-1)
|
||
a.pushChar('a')
|
||
a.pushString("Test A Faire")
|
||
print("A6:", a)
|
||
print(a.readBool())
|
||
print(a.readBool())
|
||
print(a.readBool())
|
||
print(a.readBool())
|
||
print(a.readUint32())
|
||
print(a.readSint32())
|
||
print(a.readUint16())
|
||
print(a.readSint16())
|
||
print(a.readUint8())
|
||
print(a.readSint8())
|
||
print(a.readFloat())
|
||
print(a.readDouble())
|
||
print(a.readUint64())
|
||
print(a.readSint64())
|
||
print(a.readChar())
|
||
print(a.readString())
|
||
print(a.bytes())
|
||
|
||
|
||
class TConnectionState(IntEnum):
|
||
NotInitialised = 0 # nothing happened yet
|
||
NotConnected = 1 # init() called
|
||
Authenticate = 2 # connect() called, identified by the login server
|
||
Login = 3 # connecting to the frontend, sending identification
|
||
Synchronize = 4 # connection accepted by the frontend, synchronizing
|
||
Connected = 5 # synchronized, connected, ready to work
|
||
Probe = 6 # connection lost by frontend, probing for response
|
||
Stalled = 7 # server is stalled
|
||
Disconnect = 8 # disconnect() called, or timeout, or connection closed by frontend
|
||
Quit = 9 # quit() called
|
||
|
||
|
||
class Card(IntEnum):
|
||
BEGIN_TOKEN = 0
|
||
END_TOKEN = 1
|
||
SINT_TOKEN = 2
|
||
UINT_TOKEN = 3
|
||
FLOAT_TOKEN = 4
|
||
STRING_TOKEN = 5
|
||
FLAG_TOKEN = 6
|
||
EXTEND_TOKEN = 7
|
||
|
||
|
||
|
||
class TType(IntEnum):
|
||
STRUCT_BEGIN = 0
|
||
STRUCT_END = 1
|
||
FLAG = 2
|
||
SINT32 = 3
|
||
UINT32 = 4
|
||
FLOAT32 = 5
|
||
STRING = 6
|
||
SINT64 = 7
|
||
UINT64 = 8
|
||
FLOAT64 = 9
|
||
EXTEND_TYPE = 10
|
||
NB_TYPE = 11
|
||
|
||
|
||
class TExtendType:
|
||
ET_SHEET_ID = 0
|
||
ET_64_BIT_EXTENDED_TYPES = 0x80000000
|
||
ET_ENTITY_ID = 0x80000000 # ET_ENTITY_ID = ET_64_BIT_EXTENDED_TYPES
|
||
|
||
|
||
class CBNPFileVersion:
|
||
def __init__(self):
|
||
self.VersionNumber = None
|
||
self.FileTime = None
|
||
self.FileSize = None
|
||
self.v7ZFileSize = None
|
||
self.PatchSize = None
|
||
self.HashKey = []
|
||
|
||
def __str__(self):
|
||
return "VersionNumber:" + str(self.VersionNumber) + ", FileTime:" + str(self.FileTime) + ", FileSize:" + str(self.FileSize) + ", 7ZFileSize:" + str(self.v7ZFileSize) + ", PatchSize:" + str(self.PatchSize) + ", HashKey:" + str(self.HashKey)
|
||
|
||
|
||
class CBNPFile:
|
||
def __init__(self):
|
||
self.FileName = None
|
||
self.Versions = []
|
||
self.IsIncremental = False
|
||
|
||
def __str__(self):
|
||
return str(self.FileName) +' (' + ', '.join( [str(x) for x in self.Versions]) + ')'
|
||
|
||
def update(self, FileName):
|
||
self.FileName = FileName
|
||
|
||
class CBNPCategorySet:
|
||
def __init__(self):
|
||
self._Name = ""
|
||
self._IsOptional = False
|
||
self._UnpackTo = ""
|
||
self._IsIncremental = False
|
||
self._CatRequired = ""
|
||
self._Hidden = False
|
||
self._Files = ""
|
||
|
||
def __str__(self):
|
||
return self._Name + ' (IsOptional:' + str(self._IsOptional) + ', UnpackTo:' + self._UnpackTo + ', IsIncremental:' + str(self._IsIncremental) + ', CatRequired:' + self._CatRequired + ', Hidden:' + str(self._Hidden) + ', Files:' + self._Files + ')'
|
||
|
||
# #####################################################
|
||
# persistent_data.h:140 # struct CArg
|
||
# #####################################################
|
||
class CArgV1(Structure):
|
||
_fields_ = [("i32_1", c_uint),
|
||
("i32_2", c_uint)]
|
||
|
||
|
||
class CArgV2(Structure):
|
||
_fields_ = [("ex32_1", c_uint),
|
||
("ex32_2", c_uint)]
|
||
|
||
|
||
class CArgV3(Union):
|
||
_fields_ = [("ex32", CArgV2),
|
||
("ExData32", c_uint),
|
||
("ExData64", c_ulong)]
|
||
|
||
|
||
class CArgV4(Structure):
|
||
_fields_ = [("ExType", c_uint),
|
||
("ex", CArgV3)]
|
||
|
||
|
||
class CArgV5(Union):
|
||
_fields_ = [("i", CArgV1),
|
||
("ii32", c_int),
|
||
("ii64", c_long),
|
||
("i32", c_uint),
|
||
("i64", c_ulong),
|
||
("f32", c_float),
|
||
("f64", c_double),
|
||
("ex", CArgV4)]
|
||
|
||
# union
|
||
# {
|
||
# struct
|
||
# {
|
||
# uint32 i32_1;
|
||
# uint32 i32_2;
|
||
# } i;
|
||
#
|
||
# sint32 i32;
|
||
# sint64 i64;
|
||
# float f32;
|
||
# double f64;
|
||
#
|
||
# struct
|
||
# {
|
||
# uint32 ExType;
|
||
# union
|
||
# {
|
||
# struct
|
||
# {
|
||
# uint32 ex32_1;
|
||
# uint32 ex32_2;
|
||
# };
|
||
#
|
||
# uint32 ExData32;
|
||
# uint64 ExData64;
|
||
# } ex;
|
||
# } ex;
|
||
# } _Value;
|
||
|
||
class CArg:
|
||
def __init__(self):
|
||
self._value = CArgV5()
|
||
self._value.ii64 = 0
|
||
self._value.i64 = 0
|
||
self._type = 0
|
||
self._string = 0
|
||
self._type64 = False
|
||
|
||
def read_Type(self):
|
||
return self._type
|
||
|
||
def write_Type(self, value):
|
||
self._type = value
|
||
|
||
def write_Type64(self, value):
|
||
self._type64 = value
|
||
|
||
def read_String(self):
|
||
return self._string
|
||
|
||
def write_String(self, value):
|
||
self._string = value
|
||
|
||
def read_i32_1(self):
|
||
return self._value.i.i32_1
|
||
|
||
def write_i32_1(self, value):
|
||
self._value.i.i32_1 = value
|
||
|
||
def read_i32_2(self):
|
||
return self._value.i.i32_2
|
||
|
||
def write_i32_2(self, value):
|
||
self._value.i.i32_2 = value
|
||
|
||
def read_i32(self):
|
||
return self._value.i32
|
||
|
||
def write_i32(self, value):
|
||
self._value.i32 = value
|
||
|
||
def read_i64(self):
|
||
return self._value.i64
|
||
|
||
def write_i64(self, value):
|
||
self._value.i64 = value
|
||
|
||
def read_f32(self):
|
||
return self._value.f32
|
||
|
||
def write_f32(self, value):
|
||
self._value.f32 = value
|
||
|
||
def read_f64(self):
|
||
return self._value.f64
|
||
|
||
def write_f64(self, value):
|
||
self._value.f64 = value
|
||
|
||
def read_ExType(self):
|
||
return self._value.ex.ExType
|
||
|
||
def write_ExType(self, value):
|
||
self._value.ex.ExType = value
|
||
|
||
def read_ex32_1(self):
|
||
return self._value.ex.ex.ex32.ex32_1
|
||
|
||
def write_ex32_1(self, value):
|
||
self._value.ex.ex.ex32.ex32_1 = value
|
||
|
||
def read_ex32_2(self):
|
||
return self._value.ex.ex.ex32.ex32_2
|
||
|
||
def write_ex32_2(self, value):
|
||
self._value.ex.ex.ex32.ex32_2 = value
|
||
|
||
def read_ExData32(self):
|
||
return self._value.ex.ex.ExData32
|
||
|
||
def write_ExData32(self, value):
|
||
self._value.ex.ex.ExData32 = value
|
||
|
||
def read_ExData64(self):
|
||
return self._value.ex.ex.ExData64
|
||
|
||
def write_ExData64(self, value):
|
||
self._value.ex.ex.ExData64 = value
|
||
|
||
def isExtended(self):
|
||
if self._type == TType.EXTEND_TYPE:
|
||
return True
|
||
elif self._type == TType.STRUCT_BEGIN:
|
||
self.log.error("Can't extract a value from a structure delimiter")
|
||
sys.exit(2)
|
||
elif self._type == TType.STRUCT_END:
|
||
self.log.error("Can't extract a value from a structure delimiter")
|
||
sys.exit(2)
|
||
return False
|
||
|
||
def isFlag(self):
|
||
if self._type == TType.FLAG:
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
def asUint(self):
|
||
if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END:
|
||
self.log.error("Can't extract a value from a structure delimiter")
|
||
sys.exit(2)
|
||
elif self._type == TType.SINT32:
|
||
return self.read_i32()
|
||
elif self._type == TType.UINT32:
|
||
return self.read_i32()
|
||
elif self._type == TType.SINT64:
|
||
return self.read_i64()
|
||
elif self._type == TType.UINT64:
|
||
return self.read_i64()
|
||
elif self._type == TType.FLOAT32:
|
||
return self.read_i32()
|
||
elif self._type == TType.FLOAT64:
|
||
return self.read_i64()
|
||
elif self._type == TType.STRING:
|
||
return int(self._string)
|
||
elif self._type == TType.FLAG:
|
||
return "1"
|
||
elif self._type == TType.EXTEND_TYPE:
|
||
if self.read_ExType() == TExtendType.ET_SHEET_ID:
|
||
return self.read_ExData32()
|
||
elif self.read_ExType() == TExtendType.ET_ENTITY_ID:
|
||
return self.read_ExData64()
|
||
log = logging.getLogger('myLogger')
|
||
log.error("This should never happen!")
|
||
sys.exit(2)
|
||
|
||
def __str__(self):
|
||
log = logging.getLogger('myLogger')
|
||
log.debug(self._type)
|
||
if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END:
|
||
return ''
|
||
elif self._type64:
|
||
# To be confirm for extend
|
||
return str(self.read_ExData64())
|
||
elif self._type == TType.SINT32:
|
||
return str(self.read_i32())
|
||
elif self._type == TType.UINT32:
|
||
return str(self.read_i32())
|
||
elif self._type == TType.SINT64:
|
||
return str(self.read_i64())
|
||
elif self._type == TType.UINT64:
|
||
return str(self.read_i64())
|
||
elif self._type == TType.FLOAT32:
|
||
return str(self.read_i32())
|
||
elif self._type == TType.FLOAT64:
|
||
return str(self.read_i64())
|
||
elif self._type == TType.STRING:
|
||
return self._string
|
||
elif self._type == TType.FLAG:
|
||
return "1"
|
||
return '?'
|
||
|
||
def asSint(self):
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
def asFloat(self):
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
def asDouble(self):
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
def asString(self):
|
||
if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END:
|
||
self.log.error("Can't extract a value from a structure delimiter")
|
||
sys.exit(2)
|
||
elif self._type == TType.SINT32:
|
||
return str(self.read_ii32())
|
||
elif self._type == TType.UINT32:
|
||
return str(self.read_i32())
|
||
elif self._type == TType.SINT64:
|
||
return str(self.read_ii64())
|
||
elif self._type == TType.UINT64:
|
||
return str(self.read_i64())
|
||
elif self._type == TType.FLOAT32:
|
||
return str(self.read_f32())
|
||
elif self._type == TType.FLOAT64:
|
||
return str(self.read_f64())
|
||
elif self._type == TType.STRING:
|
||
return self._string
|
||
elif self._type == TType.FLAG:
|
||
return "1"
|
||
elif self._type == TType.EXTEND_TYPE:
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
# switch(_Value.ExType)
|
||
# {
|
||
# case ET_SHEET_ID:
|
||
# {
|
||
# NLMISC::CSheetId sheetId(_Value.ExData32);
|
||
# return sheetId.toString(true);
|
||
# }
|
||
# case ET_ENTITY_ID:
|
||
# {
|
||
# NLMISC::CEntityId entityId(_Value.ExData64);
|
||
# return entityId.toString();
|
||
# }
|
||
# default:
|
||
# break;
|
||
# }
|
||
self.log.error("This should never happen!")
|
||
sys.exit(2)
|
||
|
||
def asUCString(self):
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
def asEntityId(self):
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
def asSheetId(self):
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
def typeName(self):
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
# #####################################################
|
||
#
|
||
# #####################################################
|
||
|
||
class CPersistentDataRecord:
|
||
def __init__(self, log):
|
||
self.log = log
|
||
self.TokenTable = []
|
||
self.ArgTable = []
|
||
self.StringTable = [ ]
|
||
self.ReadingStructStack = []
|
||
self.offsetToken = 0
|
||
self.ArgOffset = 0
|
||
self.version = 0
|
||
self.totalSize = 0
|
||
self.tokenCount = 0
|
||
self.argCount = 0
|
||
self.stringCount = 0
|
||
self.stringsSize = 0
|
||
self.CBNPFile = []
|
||
self.Categories = []
|
||
|
||
def show(self):
|
||
for x in self.CBNPFile:
|
||
self.log.debug("File:%s" % str(x))
|
||
for x in self.Categories:
|
||
self.log.debug("Categorie:%s" % str(x))
|
||
|
||
# ---------------- Manipulate Token ----------------
|
||
|
||
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|
||
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
||
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|
||
# | Token ID | Token Type |
|
||
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|
||
|
||
def token2Type(self, token, extend): # persistent_data_inline.h:1102 CPersistentDataRecord::CArg::TType CPersistentDataRecord::CArg::token2Type(uint32 token,bool extend)
|
||
self.log.debug("token:%d, extend:%d" % (token, extend))
|
||
if token == Card.BEGIN_TOKEN:
|
||
return TType.STRUCT_BEGIN
|
||
elif token == Card.END_TOKEN:
|
||
return TType.STRUCT_END
|
||
elif token == Card.FLAG_TOKEN:
|
||
return TType.FLAG
|
||
elif token == Card.SINT_TOKEN:
|
||
if extend:
|
||
return TType.SINT64
|
||
else:
|
||
return TType.SINT32
|
||
elif token == Card.UINT_TOKEN:
|
||
if extend:
|
||
return TType.UINT64
|
||
else:
|
||
return TType.UINT32
|
||
elif token == Card.FLOAT_TOKEN:
|
||
if extend:
|
||
return TType.FLOAT64
|
||
else:
|
||
return TType.FLOAT32
|
||
elif token == Card.STRING_TOKEN:
|
||
if extend:
|
||
return TType.EXTEND_TYPE
|
||
else:
|
||
return TType.STRING
|
||
self.log.error('This should never happen!')
|
||
sys.exit(2)
|
||
|
||
def type2Token(self, type): # persistent_data_inline.h:1118 CPersistentDataRecord::TToken CPersistentDataRecord::CArg::type2Token(uint32 type)
|
||
self.log.debug("type: %d" %(type))
|
||
if type == TType.STRUCT_BEGIN:
|
||
return Card.BEGIN_TOKEN
|
||
elif type == TType.STRUCT_END:
|
||
return Card.END_TOKEN
|
||
elif type == TType.FLAG:
|
||
return Card.FLAG_TOKEN
|
||
elif type == TType.SINT32:
|
||
return Card.SINT_TOKEN
|
||
elif type == TType.UINT32:
|
||
return Card.UINT_TOKEN
|
||
elif type == TType.FLOAT32:
|
||
return Card.FLOAT_TOKEN
|
||
elif type == TType.STRING:
|
||
return Card.STRING_TOKEN
|
||
elif type == TType.SINT64:
|
||
return Card.SINT_TOKEN
|
||
elif type == TType.UINT64:
|
||
return Card.UINT_TOKEN
|
||
elif type == TType.FLOAT64:
|
||
return Card.FLOAT_TOKEN
|
||
elif type == TType.EXTEND_TYPE:
|
||
return Card.STRING_TOKEN
|
||
self.log.error('This should never happen!')
|
||
sys.exit(2)
|
||
|
||
def peekNextToken(self):
|
||
token = self.TokenTable[self.offsetToken]
|
||
self.log.debug("[%d] token:%d" %(self.offsetToken, token))
|
||
return token // 8 # persistent_data_inline.h:308 CPersistentDataRecord::TToken CPersistentDataRecord::peekNextToken() # _TokenTable[_TokenOffset]>>3;
|
||
|
||
def peekNextTokenType(self): # persistent_data_limit.h:308 CPersistentDataRecord::TToken CPersistentDataRecord::peekNextToken() const
|
||
self.log.debug("peekNextTokenType - old offset token:%d" % self.offsetToken)
|
||
if self.isEndOfData():
|
||
self.log.error('Attempt to read past end of input data')
|
||
sys.exit(2)
|
||
token = self.TokenTable[self.offsetToken]
|
||
tokenType = token & 7
|
||
if tokenType == Card.EXTEND_TOKEN:
|
||
if self.offsetToken + 1 > self.tokenCount:
|
||
self.log.error('Attempt to read past end of input data')
|
||
sys.exit(2)
|
||
tokenType = self.TokenTable[self.offsetToken+1]
|
||
self.log.debug("peekNextTokenType [%d] token:%d type:%d" %(self.offsetToken, token, tokenType))
|
||
return self.token2Type(tokenType, True)
|
||
self.log.debug("peekNextTokenType [%d] token:%d type:%d" %(self.offsetToken, token, tokenType))
|
||
return self.token2Type(tokenType, False)
|
||
|
||
def isEndOfData(self):
|
||
if self.offsetToken == self.tokenCount:
|
||
return True
|
||
return False
|
||
|
||
def isEndOfStruct(self):
|
||
if self.isEndOfData():
|
||
self.log.debug("isEndOfData")
|
||
return True
|
||
elif len(self.ReadingStructStack) == 0:
|
||
self.log.debug("ReadingStructStack")
|
||
return False
|
||
elif self.peekNextTokenType() != TType.STRUCT_END:
|
||
self.log.debug("peekNextTokenType != TType.STRUCT_END")
|
||
return False
|
||
elif self.ReadingStructStack[-1] != self.peekNextToken():
|
||
self.log.error("Opening and closing structure tokens don't match")
|
||
sys.exit(2)
|
||
self.log.debug("isEndOfStruct")
|
||
return True
|
||
|
||
def isStartOfStruct(self):
|
||
if self.peekNextTokenType() == TType.STRUCT_BEGIN:
|
||
return True
|
||
return False
|
||
|
||
def popStructBegin(self, token):
|
||
if self.peekNextToken() != token:
|
||
self.log.error('Attempting to enter a structure with the wrong delimiting token')
|
||
sys.exit(2)
|
||
if self.peekNextTokenType() != TType.STRUCT_BEGIN:
|
||
self.log.error('Attempting to leave a structure with the wrong delimiting token type')
|
||
sys.exit(2)
|
||
self.ReadingStructStack.append(token)
|
||
self.offsetToken += 1
|
||
|
||
def popStructEnd(self, token):
|
||
if len(self.ReadingStructStack) == 0:
|
||
self.log.error('Attempting to pop end of a structure with nothing left in the open structure stack')
|
||
sys.exit(2)
|
||
nextToken = self.peekNextToken()
|
||
topToken = self.ReadingStructStack[-1]
|
||
if topToken != token:
|
||
self.log.error('Attempting to pop end of a structure with the wrong delimiting token')
|
||
sys.exit(2)
|
||
if nextToken != token:
|
||
self.log.error('Attempting to pop end of a structure with the wrong delimiting token')
|
||
sys.exit(2)
|
||
if self.peekNextTokenType() != TType.STRUCT_END:
|
||
self.log.error('Attempting to leave a structure with the wrong delimiting token type')
|
||
sys.exit(2)
|
||
del self.ReadingStructStack[-1]
|
||
self.offsetToken += 1
|
||
|
||
# ---------------- Manipulate StringTable ----------------
|
||
def lookupString(self, idx):
|
||
if idx >= self.stringCount:
|
||
self.log.error("Attempting to access past end of string table")
|
||
sys.exit(2)
|
||
return self.StringTable[idx]
|
||
|
||
# ---------------- Manipulate Arg ----------------
|
||
def peekNextArg(self): # persistent_data_limit.h:339 CPersistentDataRecord::peekNextArg(CPersistentDataRecord::CArg& result) const
|
||
_type = self.peekNextTokenType()
|
||
result = CArg()
|
||
result.write_Type(_type)
|
||
result.write_Type64(False)
|
||
self.log.debug("peekNextArg - Type:%d ArgOffset:%d" % (_type, self.ArgOffset))
|
||
if result.isExtended():
|
||
self.log.debug("Extended")
|
||
result.write_i32_1(self.ArgTable[self.ArgOffset])
|
||
result.write_i32_2(self.ArgTable[self.ArgOffset+1])
|
||
if result.read_Type() == TType.EXTEND_TYPE and result.read_ExType() == TExtendType.ET_64_BIT_EXTENDED_TYPES:
|
||
result.write_ex32_2(self.ArgTable[self.ArgOffset+2]);
|
||
result.write_Type64(True)
|
||
elif not result.isFlag():
|
||
# result._Value.i32_1 = _ArgTable[_ArgOffset];
|
||
result.write_i32_1(self.ArgTable[self.ArgOffset])
|
||
self.log.debug("peekNextArg - id :%d" % result.read_i32_1())
|
||
if result.read_Type() == TType.STRING:
|
||
result.write_String(self.lookupString(result.read_i32_1()))
|
||
self.log.debug("peekNextArg - String:%s" % result.read_String())
|
||
return result
|
||
|
||
def popNextArg(self, token): # persistent_data_limit.h:414 CPersistentDataRecord::popNextArg(TToken token,CPersistentDataRecord::CArg& result)
|
||
result = self.peekNextArg()
|
||
if result.isFlag():
|
||
self.offsetToken += 1
|
||
elif result.isExtended():
|
||
self.ArgOffset += 2
|
||
self.offsetToken += 2
|
||
if result.read_Type() == TType.EXTEND_TYPE and result.read_ExType() == TExtendType.ET_64_BIT_EXTENDED_TYPES:
|
||
self.ArgOffset += 1
|
||
self.offsetToken += 1
|
||
else:
|
||
self.ArgOffset += 1
|
||
self.offsetToken += 1
|
||
self.log.debug("popNextArg - Arg:%d", result.read_i32_1())
|
||
return result
|
||
|
||
def popString(self, token):
|
||
TempArg = self.popNextArg(token)
|
||
return TempArg.asString()
|
||
|
||
def popUint32(self, token):
|
||
TempArg = self.popNextArg(token)
|
||
return TempArg.asUint()
|
||
|
||
def popBool(self, token):
|
||
TempArg = self.popNextArg(token)
|
||
return TempArg.asUint() != 0
|
||
|
||
# ---------------- Read Data ----------------
|
||
def readFromBinFile(self, filename): # persistent_data.cpp:835 # bool CPersistentDataRecord::fromBuffer(const char *src, uint32 bufferSize)
|
||
self.log.debug('Read Bin File %s' % filename)
|
||
with open(filename, "rb") as fp:
|
||
buffer = fp.read()
|
||
fp.close()
|
||
|
||
self.version = int.from_bytes(buffer[0:4], byteorder='little', signed=False)
|
||
self.totalSize = int.from_bytes(buffer[4:8], byteorder='little', signed=False)
|
||
self.tokenCount = int.from_bytes(buffer[8:12], byteorder='little', signed=False)
|
||
self.argCount = int.from_bytes(buffer[12:16], byteorder='little', signed=False)
|
||
self.stringCount = int.from_bytes(buffer[16:20], byteorder='little', signed=False)
|
||
self.stringsSize = int.from_bytes(buffer[20:24], byteorder='little', signed=False)
|
||
offset = 24
|
||
self.log.debug("version:%d, totalSize:%d, tokenCount:%d, argCount:%d, stringCount:%d, stringsSize:%d" % (self.version, self.totalSize, self.tokenCount, self.argCount, self.stringCount, self.stringsSize))
|
||
if len(buffer) != self.totalSize:
|
||
self.log.error("Failed to parse buffer due to invalid header (file:%s, size:%d, size define:%d)" % (filename, len(buffer), self.totalSize ))
|
||
sys.exit(2)
|
||
if self.version > 0:
|
||
self.log.error("PDR ERROR: Wrong file format version! (file:%s, version:%d)" % (filename, self.version))
|
||
sys.exit(2)
|
||
if (self.stringCount != 0 and self.stringsSize == 0) or (self.stringCount == 0 and self.stringsSize != 0):
|
||
self.log.error("PDR ERROR: Invalid string table parameters! (file:%s, stringCount:%d, stringsSize:%d)" % (filename, self.stringCount, self.stringsSize))
|
||
sys.exit(2)
|
||
# i = offset+tokenCount*sizeof(TToken)+argCount*sizeof(uint32)+stringsSize
|
||
i = offset + self.tokenCount * 2 + self.argCount * 4 + self.stringsSize;
|
||
if self.totalSize != i:
|
||
self.log.error("PDR ERROR: Invalid source data (file:%s, totalSize:%d != datasize:%s)" % (filename, self.totalSize, i))
|
||
sys.exit(2)
|
||
|
||
# READ the tokens
|
||
self.TokenTable = []
|
||
for i in range(0, self.tokenCount):
|
||
tmp = int.from_bytes(buffer[offset:offset+2], byteorder='little', signed=False)
|
||
self.log.debug("token %5d => %3d id:%3d type:%d" %(i, tmp, tmp // 8, tmp & 7))
|
||
self.TokenTable.append(tmp)
|
||
offset += 2
|
||
|
||
# READ the arguments
|
||
self.ArgTable = []
|
||
for i in range(0, self.argCount):
|
||
tmp = int.from_bytes(buffer[offset:offset+4], byteorder='little', signed=False)
|
||
self.ArgTable.append(tmp)
|
||
offset += 4
|
||
print(self.ArgTable)
|
||
|
||
# READ the string table data
|
||
if self.stringsSize != 0:
|
||
chaine = ''
|
||
self.StringTable = [ ]
|
||
while offset < self.totalSize:
|
||
car = buffer[offset:offset+1].decode()
|
||
if car != '\0':
|
||
chaine += car
|
||
else:
|
||
self.StringTable.append(chaine)
|
||
chaine = ''
|
||
offset += 1
|
||
self.log.debug(self.StringTable)
|
||
if chaine != '':
|
||
self.log.error("PDR ERROR: Too few strings found in string table (file:%s)" % (filename))
|
||
sys.exit(2)
|
||
self.log.debug("Red %s" % filename)
|
||
|
||
def decrypt_token(self):
|
||
i = 0
|
||
lvl = 0
|
||
posArg = 0
|
||
extend = False
|
||
extend64 = False
|
||
result = CArg()
|
||
|
||
print("^ Position ^ Token ^")
|
||
for value in self.TokenTable:
|
||
print("| %5d | %3d |" %(i, value))
|
||
i += 1
|
||
|
||
i = 0
|
||
print("^ Position ^ Argument ^")
|
||
for value in self.ArgTable:
|
||
print("| %5d | %3d |" %(i, value))
|
||
i += 1
|
||
|
||
i = 0
|
||
print("^ Position ^ String ^")
|
||
for value in self.StringTable:
|
||
print("| %5d | %s |" %(i, value))
|
||
i += 1
|
||
|
||
i = 0
|
||
print("^ Position ^ Niveau ^ Token ^ Token ID ^^ Token Type (Card) ^^^ Result ^")
|
||
print("^ ^^ (entrée) ^ Valeur ^ Quoi ^ Valeur ^ Card ^ Type ^ ^")
|
||
for token in self.TokenTable:
|
||
tokenId = token // 8
|
||
tokenTypeValue = token & 7
|
||
result.write_String("-")
|
||
if tokenTypeValue == 0:
|
||
tokenCard = 'BEGIN_TOKEN'
|
||
tokenType = 'STRUCT_BEGIN'
|
||
result.write_Type(TType.STRUCT_BEGIN)
|
||
if lvl <= 1:
|
||
print("| |||||||")
|
||
lvl += 1
|
||
elif tokenTypeValue == 1:
|
||
tokenCard = 'END_TOKEN'
|
||
tokenType = 'STRUCT_END'
|
||
result.write_Type(TType.STRUCT_END)
|
||
extend = False
|
||
extend64 = False
|
||
elif tokenTypeValue == 2:
|
||
tokenCard = 'SINT_TOKEN'
|
||
if extend:
|
||
tokenType = 'SINT64'
|
||
result.write_Type(TType.SINT64)
|
||
result.write_i32_1(self.ArgTable[posArg])
|
||
result.write_i32_2(self.ArgTable[posArg+1])
|
||
if extend64:
|
||
result.write_ex32_2(self.ArgTable[posArg+2]);
|
||
posArg += 3
|
||
else:
|
||
posArg += 2
|
||
else:
|
||
tokenType = 'SINT32'
|
||
result.write_Type(TType.SINT32)
|
||
result.write_i32_1(self.ArgTable[posArg])
|
||
posArg += 1
|
||
extend = False
|
||
extend64 = False
|
||
elif tokenTypeValue == 3:
|
||
tokenCard = 'UINT_TOKEN'
|
||
if extend:
|
||
tokenType = 'UINT64'
|
||
result.write_Type(TType.UINT64)
|
||
result.write_i32_1(self.ArgTable[posArg])
|
||
result.write_i32_2(self.ArgTable[posArg+1])
|
||
if extend64:
|
||
result.write_ex32_2(self.ArgTable[posArg+2]);
|
||
posArg += 3
|
||
else:
|
||
posArg += 2
|
||
else:
|
||
tokenType = 'UINT32'
|
||
result.write_Type(TType.UINT32)
|
||
result.write_i32_1(self.ArgTable[posArg])
|
||
posArg += 1
|
||
extend = False
|
||
extend64 = False
|
||
elif tokenTypeValue == 4:
|
||
tokenCard = 'FLOAT_TOKEN'
|
||
if extend:
|
||
tokenType = 'FLOAT64'
|
||
result.write_Type(TType.FLOAT64)
|
||
result.write_i32_1(self.ArgTable[posArg])
|
||
result.write_i32_2(self.ArgTable[posArg+1])
|
||
if extend64:
|
||
result.write_ex32_2(self.ArgTable[posArg+2]);
|
||
posArg += 3
|
||
else:
|
||
posArg += 2
|
||
else:
|
||
tokenType = 'FLOAT32'
|
||
result.write_Type(TType.FLOAT32)
|
||
result.write_i32_1(self.ArgTable[posArg])
|
||
posArg += 1
|
||
extend = False
|
||
extend64 = False
|
||
elif tokenTypeValue == 5:
|
||
tokenCard = 'STRING_TOKEN'
|
||
if extend:
|
||
tokenType = 'EXTEND_TYPE'
|
||
result.write_Type(TType.EXTEND_TYPE)
|
||
result.write_i32_1(self.ArgTable[posArg])
|
||
result.write_i32_2(self.ArgTable[posArg+1])
|
||
if extend64:
|
||
result.write_ex32_2(self.ArgTable[posArg+2]);
|
||
posArg += 3
|
||
else:
|
||
posArg += 2
|
||
else:
|
||
tokenType = 'STRING'
|
||
result.write_Type(TType.STRING)
|
||
result.write_i32_1(self.ArgTable[posArg])
|
||
tmp = result.read_i32_1()
|
||
result.write_String(self.StringTable[tmp])
|
||
posArg += 1
|
||
extend = False
|
||
extend64 = False
|
||
elif tokenType == 6:
|
||
tokenCard = 'FLAG_TOKEN'
|
||
tokenType = 'FLAG'
|
||
result.write_Type(TType.FLAG)
|
||
extend = False
|
||
extend64 = False
|
||
elif tokenTypeValue == 7:
|
||
if extend:
|
||
extend64 = True
|
||
tokenCard = 'EXTEND_TOKEN'
|
||
result.write_Type(TType.EXTEND_TYPE)
|
||
tokenType = ''
|
||
extend = True
|
||
# print("token %5d => %3d id:%3d [%s] type:%d [%s]" %(i, token, tokenId, self.StringTable[tokenId], tokenType, tokenCard))
|
||
print("| %5d | %3d | %3d | %3d | %s | %d | %s | %s | %s |" %(i, lvl, token, tokenId, self.StringTable[tokenId], tokenTypeValue, tokenCard , tokenType, result))
|
||
if tokenTypeValue == 1:
|
||
lvl -= 1
|
||
i += 1
|
||
|
||
def addString(self, name): # persistent_data.cpp:100 uint16 CPersistentDataRecord::addString(const string& name)
|
||
for i in range(0, len(self.StringTable)):
|
||
if self.StringTable[i] == name:
|
||
return i
|
||
self.StringTable.append(name)
|
||
return len(self.StringTable) - 1
|
||
|
||
def CProductDescriptionForClient_apply(self): # persistent_data_template.h:459 # void PERSISTENT_CLASS::apply(CPersistentDataRecord &pdr _PERSISTENT_APPLY_ARGS)
|
||
__Tok__MapKey = self.addString("__Key__")
|
||
__Tok__MapVal = self.addString("__Val__")
|
||
__Tok_Files = self.addString("_Files")
|
||
__Tok_Categories = self.addString("_Categories")
|
||
self.log.debug("MapKey:%d, MapVal:%d, Files:%d, Categories:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Files, __Tok_Categories))
|
||
while not self.isEndOfStruct():
|
||
nextToken = self.peekNextToken()
|
||
self.log.debug("nextToken:%d" % (nextToken))
|
||
if nextToken == __Tok_Files:
|
||
self.popStructBegin(__Tok_Files)
|
||
self.CBNPFileSet_apply()
|
||
self.popStructEnd(__Tok_Files)
|
||
continue
|
||
elif nextToken == __Tok_Categories:
|
||
self.popStructBegin(__Tok_Categories)
|
||
# (_Categories).apply(pdr);
|
||
self.CBNPCategorySet_apply()
|
||
self.popStructEnd(__Tok_Categories)
|
||
continue
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
def CBNPFileSet_apply(self):
|
||
__Tok__MapKey = self.addString("__Key__")
|
||
__Tok__MapVal = self.addString("__Val__")
|
||
__Tok_Files = self.addString("_Files")
|
||
self.log.debug("MapKey:%d, MapVal:%d, Files:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Files))
|
||
while not self.isEndOfStruct():
|
||
nextToken = self.peekNextToken()
|
||
self.log.debug("nextToken:%d" % (nextToken))
|
||
if nextToken == __Tok_Files:
|
||
self.popStructBegin(__Tok_Files)
|
||
self.CBNPFile.append(CBNPFile())
|
||
self.CBNPFile_apply(self.CBNPFile[-1])
|
||
self.popStructEnd(__Tok_Files)
|
||
continue
|
||
self.log.error("TODO")
|
||
sys.exit(2)
|
||
|
||
def CBNPFile_apply(self, _CBNPFile):
|
||
__Tok__MapKey = self.addString("__Key__")
|
||
__Tok__MapVal = self.addString("__Val__")
|
||
__Tok_FileName = self.addString("_FileName")
|
||
__Tok_Versions = self.addString("_Versions")
|
||
_FileName = None
|
||
self.log.debug("MapKey:%d, MapVal:%d, Filename:%d, Versions:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_FileName, __Tok_Versions))
|
||
while not self.isEndOfStruct():
|
||
nextToken = self.peekNextToken()
|
||
self.log.debug("nextToken:%d" % (nextToken))
|
||
if nextToken == __Tok_FileName:
|
||
_FileName = self.popString(nextToken)
|
||
_CBNPFile.FileName = _FileName
|
||
self.log.debug("filename: %s" % _FileName)
|
||
continue
|
||
if nextToken == __Tok_Versions:
|
||
self.popStructBegin(__Tok_Versions)
|
||
# vectAppend(_Versions).apply(pdr);
|
||
_CBNPFile.Versions.append(CBNPFileVersion())
|
||
self.CBNPFileVersion_apply(_CBNPFile.Versions[-1])
|
||
self.popStructEnd(__Tok_Versions)
|
||
continue
|
||
stack = []
|
||
while True:
|
||
if self.isStartOfStruct():
|
||
stack.append(self.peekNextToken())
|
||
self.popStructBegin(stack)
|
||
elif self.isEndOfStruct():
|
||
self.popStructEnd(stack[-1])
|
||
if len(stack) > 0:
|
||
del stack[-1]
|
||
else:
|
||
self.popNextArg(self.peekNextToken())
|
||
if self.isEndOfData() and len(stack) == 0:
|
||
break
|
||
self.log.debug("CBNPFile: %s" % _CBNPFile)
|
||
|
||
def CBNPFileVersion_apply(self, _CBNPFileVersion): # persistent_data_template.h:459 # void CBNPFileVersion::apply(CPersistentDataRecord &pdr )
|
||
__Tok__MapKey = self.addString("__Key__")
|
||
__Tok__MapVal = self.addString("__Val__")
|
||
__Tok_VersionNumber = self.addString("_VersionNumber")
|
||
__Tok_FileSize = self.addString("_FileSize")
|
||
__Tok_7ZFileSize = self.addString("_7ZFileSize")
|
||
__Tok_FileTime = self.addString("_FileTime")
|
||
__Tok_PatchSize = self.addString("_PatchSize")
|
||
__Tok_HashKey = self.addString("_HashKey")
|
||
self.log.debug("MapKey:%d, MapVal:%d, VersionNumber:%d, FileSize:%d, 7ZFileSize:%d, FileTime:%d, PatchSize:%d, HashKey:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_VersionNumber, __Tok_FileSize, __Tok_7ZFileSize, __Tok_FileTime, __Tok_PatchSize, __Tok_HashKey))
|
||
|
||
while not self.isEndOfStruct():
|
||
nextToken = self.peekNextToken()
|
||
self.log.debug("nextToken:%d" % (nextToken))
|
||
if nextToken == __Tok_VersionNumber:
|
||
self.log.debug("__Tok_VersionNumber")
|
||
_CBNPFileVersion.VersionNumber = self.popUint32(__Tok_VersionNumber)
|
||
self.log.debug("VersionNumber: %s" % _CBNPFileVersion.VersionNumber)
|
||
continue
|
||
elif nextToken == __Tok_FileSize:
|
||
self.log.debug("__Tok_FileSize")
|
||
_CBNPFileVersion.FileSize = self.popUint32(__Tok_FileSize)
|
||
self.log.debug("FileSize: %s" % _CBNPFileVersion.FileSize)
|
||
continue
|
||
elif nextToken == __Tok_7ZFileSize:
|
||
self.log.debug("__Tok_7ZFileSize")
|
||
_CBNPFileVersion.v7ZFileSize = self.popUint32(__Tok_7ZFileSize)
|
||
self.log.debug("7ZFileSize: %s" % _CBNPFileVersion.v7ZFileSize)
|
||
continue
|
||
elif nextToken == __Tok_FileTime:
|
||
self.log.debug("__Tok_FileTime")
|
||
_CBNPFileVersion.FileTime = self.popUint32(__Tok_FileTime)
|
||
self.log.debug("FileTime: %s" % _CBNPFileVersion.FileTime)
|
||
continue
|
||
elif nextToken == __Tok_PatchSize:
|
||
self.log.debug("__Tok_PatchSize")
|
||
_CBNPFileVersion.PatchSize = self.popUint32(__Tok_PatchSize)
|
||
self.log.debug("PatchSize: %s" % _CBNPFileVersion.PatchSize)
|
||
continue
|
||
elif nextToken == __Tok_HashKey:
|
||
self.log.debug("__Tok_HashKey")
|
||
_CBNPFileVersion.HashKey.append(self.popUint32(__Tok_HashKey))
|
||
self.log.debug("HashKey: %s" % _CBNPFileVersion.HashKey[-1])
|
||
continue
|
||
# Vidage des autres clefs (inconnues)
|
||
stack = []
|
||
while True:
|
||
if self.isStartOfStruct():
|
||
stack.append(self.peekNextToken())
|
||
self.popStructBegin(stack)
|
||
elif self.isEndOfStruct():
|
||
self.popStructEnd(stack[-1])
|
||
if len(stack) > 0:
|
||
del stack[-1]
|
||
else:
|
||
self.popNextArg(self.peekNextToken())
|
||
if self.isEndOfData() and len(stack) == 0:
|
||
break
|
||
|
||
def CBNPCategorySet_apply(self): # persistent_data_template.h:459 # void CBNPCategorySet::apply(CPersistentDataRecord &pdr )
|
||
#__Tok__MapKey = self.addString("__Key__")
|
||
#__Tok__MapVal = self.addString("__Val__")
|
||
__Tok_Category = self.addString("_Category")
|
||
while not self.isEndOfStruct():
|
||
nextToken = self.peekNextToken()
|
||
self.log.debug("nextToken:%d" % (nextToken))
|
||
if nextToken == __Tok_Category:
|
||
self.log.debug("__Tok_Category")
|
||
self.popStructBegin(__Tok_Category)
|
||
self.Categories.append(CBNPCategorySet())
|
||
self.CBNPCategory_apply(self.Categories[-1])
|
||
self.popStructEnd(__Tok_Category)
|
||
continue
|
||
# Vidage des autres clefs (inconnues)
|
||
stack = []
|
||
while True:
|
||
if self.isStartOfStruct():
|
||
stack.append(self.peekNextToken())
|
||
self.popStructBegin(stack)
|
||
elif self.isEndOfStruct():
|
||
self.popStructEnd(stack[-1])
|
||
if len(stack) > 0:
|
||
del stack[-1]
|
||
else:
|
||
self.popNextArg(self.peekNextToken())
|
||
if self.isEndOfData() and len(stack) == 0:
|
||
break
|
||
|
||
|
||
def CBNPCategory_apply(self, _CBNPCategory): # persistent_data_template.h:459 # void CBNPCategory::apply(CPersistentDataRecord &pdr )
|
||
__Tok__MapKey = self.addString("__Key__")
|
||
__Tok__MapVal = self.addString("__Val__")
|
||
__Tok_Name = self.addString("_Name")
|
||
__Tok_IsOptional = self.addString("_IsOptional")
|
||
__Tok_UnpackTo = self.addString("_UnpackTo")
|
||
__Tok_IsIncremental = self.addString("_IsIncremental")
|
||
__Tok_CatRequired = self.addString("_CatRequired")
|
||
__Tok_Hidden = self.addString("_Hidden")
|
||
__Tok_Files = self.addString("_Files")
|
||
self.log.debug("MapKey:%d, MapVal:%d, Name:%d, IsOptional:%d, UnpackTo:%d, IsIncremental:%d, CatRequired:%d, Hidden:%d, Files:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Name, __Tok_IsOptional, __Tok_UnpackTo, __Tok_IsIncremental, __Tok_CatRequired, __Tok_Hidden, __Tok_Files))
|
||
while not self.isEndOfStruct():
|
||
nextToken = self.peekNextToken()
|
||
self.log.debug("nextToken:%d" % (nextToken))
|
||
if nextToken == __Tok_Name:
|
||
self.log.debug("__Tok_Name")
|
||
_CBNPCategory._Name = self.popString(nextToken)
|
||
self.log.debug("_Name: %s" % _CBNPCategory._Name)
|
||
continue
|
||
elif nextToken == __Tok_IsOptional:
|
||
self.log.debug("__Tok_IsOptional")
|
||
_CBNPCategory._IsOptional = self.popBool(nextToken)
|
||
self.log.debug("_IsOptional: %s" % str(_CBNPCategory._IsOptional))
|
||
continue
|
||
elif nextToken == __Tok_UnpackTo:
|
||
self.log.debug("__Tok_UnpackTo")
|
||
_CBNPCategory._UnpackTo = self.popString(nextToken)
|
||
self.log.debug("_UnpackTo: %s" % str(_CBNPCategory._UnpackTo))
|
||
continue
|
||
elif nextToken == __Tok_IsIncremental:
|
||
self.log.debug("__Tok_IsIncremental")
|
||
_CBNPCategory._IsIncremental = self.popBool(nextToken)
|
||
self.log.debug("_IsIncremental: %s" % str(_CBNPCategory._IsIncremental))
|
||
continue
|
||
elif nextToken == __Tok_CatRequired:
|
||
self.log.debug("__Tok_CatRequired")
|
||
_CBNPCategory._CatRequired = self.popString(nextToken)
|
||
self.log.debug("_CatRequired: %s" % str(_CBNPCategory._CatRequired))
|
||
continue
|
||
elif nextToken == __Tok_Hidden:
|
||
self.log.debug("__Tok_Hidden")
|
||
_CBNPCategory._Hidden = self.popBool(nextToken)
|
||
self.log.debug("_Hidden: %s" % str(_CBNPCategory._Hidden))
|
||
continue
|
||
elif nextToken == __Tok_Files:
|
||
self.log.debug("__Tok_Files")
|
||
_CBNPCategory._Files = self.popString(nextToken)
|
||
self.log.debug("_Files: %s" % str(_CBNPCategory._Files))
|
||
continue
|
||
# Vidage des autres clefs (inconnues)
|
||
stack = []
|
||
while True:
|
||
if self.isStartOfStruct():
|
||
stack.append(self.peekNextToken())
|
||
self.popStructBegin(stack)
|
||
elif self.isEndOfStruct():
|
||
self.popStructEnd(stack[-1])
|
||
if len(stack) > 0:
|
||
del stack[-1]
|
||
else:
|
||
self.popNextArg(self.peekNextToken())
|
||
if self.isEndOfData() and len(stack) == 0:
|
||
break
|
||
|
||
|
||
class ClientNetworkConnection:
|
||
def __init__(self,
|
||
khanaturl,
|
||
LanguageCode="fr"):
|
||
self.log = logging.getLogger('myLogger')
|
||
self._CurrentSendNumber = 0
|
||
self.LanguageCode = LanguageCode
|
||
self._QuitId = 0
|
||
self._ConnectionState = TConnectionState
|
||
self.UserAddr, self.UserKey, self.UserId = None, None, None
|
||
self.khanaturl = khanaturl
|
||
_khanaturl = self.khanaturl.strip('"').strip("'")
|
||
try:
|
||
host, port = _khanaturl.split(':')
|
||
except:
|
||
host = _khanaturl
|
||
port = 47851
|
||
(1024).to_bytes(2, byteorder='big')
|
||
self.frontend = (host, port)
|
||
self._sock = socket.socket(socket.AF_INET, # Internet
|
||
socket.SOCK_DGRAM) # UDP
|
||
|
||
def cookiesInit(self, UserAddr, UserKey, UserId):
|
||
self.UserAddr = UserAddr
|
||
self.UserKey = UserKey
|
||
self.UserId = UserId
|
||
|
||
def reset(self):
|
||
self._CurrentSendNumber += 0
|
||
|
||
def buildSystemHeader(self, msg): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::buildSystemHeader(NLMISC::CBitMemStream &msgout)
|
||
msg.pushSint32(self._CurrentSendNumber)
|
||
msg.pushBool(True) # systemMode
|
||
|
||
def sendSystemLogin(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemLogin()
|
||
if self._sock is None:
|
||
raise ValueError
|
||
msg = BitStream()
|
||
self.buildSystemHeader(msg)
|
||
msg.pushUint8(0) # SYSTEM_LOGIN_CODE
|
||
msg.pushUint32(self.UserAddr)
|
||
msg.pushUint32(self.UserKey)
|
||
msg.pushUint32(self.UserId)
|
||
msg.pushString(self.LanguageCode)
|
||
|
||
self._sock.sendto(msg.bytes(), self.frontend)
|
||
self._CurrentSendNumber += 1
|
||
|
||
self._ConnectionState = TConnectionState.Login
|
||
|
||
def sendSystemQuit(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemQuit()
|
||
# Disconnect
|
||
if self._sock is None:
|
||
raise ValueError
|
||
self._QuitId += 1
|
||
msg = BitStream()
|
||
self.buildSystemHeader(msg)
|
||
msg.pushUint8(8) # SYSTEM_LOGIN_CODE
|
||
msg.pushSint32(_QuitId) # _QuitId
|
||
self._sock.sendto(msg.bytes(), self.frontend)
|
||
self._ConnectionState = TConnectionState.Quit
|
||
|
||
def EmulateFirst(self):
|
||
self.sendSystemLogin()
|
||
|
||
while True:
|
||
data, addr = self._sock.recvfrom(1024) # buffer size is 1024 bytes
|
||
print( "received message:", data)
|
||
self.sendSystemQuit()
|
||
|
||
|
||
class ClientKhanat:
|
||
def __init__(self,
|
||
khanaturl,
|
||
login="tester",
|
||
password="tester",
|
||
clientApp="Lirria",
|
||
LanguageCode="fr",
|
||
url="/login/r2_login.php",
|
||
suffix = None):
|
||
self.log = logging.getLogger('myLogger')
|
||
|
||
if suffix is None:
|
||
suffix = str(random.randrange(1, 9999))
|
||
self.log.debug("suffix : %s" % suffix)
|
||
self.khanaturl = khanaturl
|
||
self.login = login + suffix
|
||
self.password = password
|
||
self.clientApp = clientApp
|
||
self.LanguageCode = LanguageCode
|
||
self.url = url
|
||
self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat, self.r2serverversion, self.r2backuppatchurl, self.r2patchurl = None, None, None, None, None, None, None, None
|
||
self.tempdir = tempfile.TemporaryDirectory(".khanat")
|
||
self.log.debug("Temporary directory:%s" % self.tempdir)
|
||
self.khanat_idx = CPersistentDataRecord(self.log)
|
||
self.UserAddr, self.UserKey, self.UserId = None, None, None
|
||
self.clientNetworkConnection = ClientNetworkConnection(khanaturl)
|
||
|
||
def createAccount(self):
|
||
khanaturl = self.khanaturl.strip('"').strip("'")
|
||
try:
|
||
host, port = khanaturl.split(':')
|
||
except:
|
||
host = khanaturl
|
||
port = 40916
|
||
|
||
conn = http.client.HTTPConnection(host=host, port=port)
|
||
cmd = "/ams/index.php?page=register"
|
||
headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||
'Accept-Language' : 'en-US',
|
||
'Connection': 'keep-alive',
|
||
'DNT': '1',
|
||
'Cookie': 'PHPSESSID=lsoumn9f0ljgm3vo3hgjdead03',
|
||
'Host': khanaturl+':'+ str(port),
|
||
'Referer': 'http://' + khanaturl+':'+ str(port) + '/ams/index.php?page=register',
|
||
'Upgrade-Insecure-Requests': '1',
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20100101 Firefox/6.0',
|
||
'Content-Type': 'application/x-www-form-urlencoded'}
|
||
|
||
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
||
params = urllib.parse.urlencode({'Username': self.login, 'Password': self.password, 'ConfirmPass': self.password, 'Email': self.login+'@khaganat.net', 'TaC': 'on', 'function': 'add_user'})
|
||
conn.request("POST", cmd, params, headers)
|
||
response = conn.getresponse()
|
||
if ( int(response.status) == 302 ):
|
||
conn.close()
|
||
self.log.info("Account : %s" % self.login)
|
||
return
|
||
elif ( int(response.status) != 200 ):
|
||
self.log.error("Impossible to create account (return code:" + str(response.status) + ")")
|
||
sys.exit(2)
|
||
ret = response.read()
|
||
conn.close()
|
||
|
||
ret2 = ret.decode()
|
||
try:
|
||
state, comment = ret2.split(":", 2)
|
||
except:
|
||
state = 1
|
||
comment = ""
|
||
if int(state) != 1:
|
||
self.log.error("Impossible to create account (state:" + state + ", comment:" + comment.strip() + ")")
|
||
sys.exit(2)
|
||
|
||
errordetected = False
|
||
for line in ret2.split('\n'):
|
||
m = re.search("(<strong>(?P<type>.*) Error</strong> )(?P<comment>[^.]+)", line)
|
||
if m:
|
||
if m.group('comment') == 'Username ' + self.login + ' is in use':
|
||
continue
|
||
if m.group('comment') == 'Email is in use':
|
||
continue
|
||
self.log.error('Impossible to create account: field:%s (%s)' % (m.group('type'), m.group('comment')))
|
||
errordetected = True
|
||
if errordetected:
|
||
sys.exit(2)
|
||
self.log.info("Reuse account : %s" % self.login)
|
||
|
||
def connectR2(self):
|
||
khanaturl = self.khanaturl.strip('"').strip("'")
|
||
try:
|
||
host, port = khanaturl.split(':')
|
||
except:
|
||
host = khanaturl
|
||
port = 40916
|
||
|
||
conn = http.client.HTTPConnection(host=host, port=port)
|
||
cmd = self.url + "?cmd=ask&cp=2&login=" + self.login + "&lg=" + self.LanguageCode
|
||
conn.request("GET", cmd)
|
||
response = conn.getresponse()
|
||
if ( int(response.status) != 200 ):
|
||
self.log.error("Impossible to get salt (return code:" + str(response.status) + ")")
|
||
sys.exit(2)
|
||
ret = response.read()
|
||
conn.close()
|
||
|
||
try:
|
||
state, salt = ret.decode().split(":", 1)
|
||
except UnicodeDecodeError:
|
||
try:
|
||
state, salt = ret.decode(encoding='cp1252').split(":", 1)
|
||
except UnicodeDecodeError:
|
||
self.log.error("Impossible to read output login")
|
||
sys.exit(2)
|
||
if int(state) != 1:
|
||
self.log.error("Impossible to get salt (state:" + state + ")")
|
||
|
||
cryptedPassword = crypt.crypt(self.password, salt)
|
||
|
||
conn = http.client.HTTPConnection(host=host, port=port)
|
||
cmd = self.url + "?cmd=login&login=" + self.login + "&password=" + cryptedPassword + "&clientApplication=" + self.clientApp + "&cp=2" + "&lg=" + self.LanguageCode
|
||
conn.request("GET", cmd)
|
||
response = conn.getresponse()
|
||
self.log.debug("%s %s" %(response.status, response.reason))
|
||
ret = response.read()
|
||
self.log.debug(ret)
|
||
try:
|
||
line = ret.decode().split('\n')
|
||
except UnicodeDecodeError:
|
||
try:
|
||
line = ret.decode(encoding='cp1252').split('\n')
|
||
except UnicodeDecodeError:
|
||
self.log.error("Impossible to read output login")
|
||
sys.exit(2)
|
||
|
||
self.log.debug(line[0])
|
||
self.log.debug("line 0 '%s'" % line[0])
|
||
self.log.debug("line 1 '%s'" % line[1])
|
||
try:
|
||
state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat = line[0].split("#", 6)
|
||
except:
|
||
try:
|
||
state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp = line[0].split("#", 5)
|
||
self.stat = 0
|
||
except:
|
||
state, error = line[0].split(":", 1)
|
||
|
||
if int(state) != 1:
|
||
self.log.error(error)
|
||
sys.exit(2)
|
||
|
||
self.r2serverversion, self.r2backuppatchurl, self.r2patchurl = line[1].split("#")
|
||
self.log.debug("%s %s %s %s %s %s %s %s %s" % (state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat, self.r2serverversion, self.r2backuppatchurl, self.r2patchurl))
|
||
self.UserAddr, self.UserKey, self.UserId = [ int(x, 16) for x in self.cookie.split('|') ]
|
||
|
||
conn.close()
|
||
self.log.info("Login Ok")
|
||
self.clientNetworkConnection.cookiesInit(self.UserAddr, self.UserKey, self.UserId)
|
||
|
||
def downloadFileUrl(self, source, dest):
|
||
self.log.info("Download %s (destination:%s)" % (source, dest))
|
||
with urllib.request.urlopen(source) as conn :
|
||
header = conn.getheaders()
|
||
file_size = 0
|
||
for key, value in header:
|
||
if key == 'Content-Length':
|
||
file_size = int(value)
|
||
break
|
||
self.log.debug("size:%d", file_size)
|
||
file_size_dl = 0
|
||
block_size = 1024 # 8192
|
||
|
||
with open(dest, 'wb') as fp:
|
||
while True:
|
||
buffer = conn.read(block_size)
|
||
if not buffer:
|
||
break
|
||
file_size_dl += len(buffer)
|
||
fp.write(buffer)
|
||
self.log.debug("Download %s %10d [%6.2f%%]" % (source, file_size_dl, file_size_dl * 100. / file_size))
|
||
fp.close()
|
||
self.log.debug("Downloaded %s (%d)" % (source, file_size))
|
||
|
||
def getServerFile(self, name, bZipped = False, specifyDestName = None):
|
||
srcName = name
|
||
if specifyDestName:
|
||
dstName = specifyDestName
|
||
else:
|
||
dstName = os.path.basename(name)
|
||
if bZipped:
|
||
srcName += ".ngz"
|
||
dstName += ".ngz"
|
||
self.log.info("Download %s (destination:%s, zip:%d)" % (srcName, dstName, bZipped))
|
||
dstName = os.path.join(self.tempdir.name, dstName)
|
||
self.downloadFileUrl( 'http://' + self.r2patchurl + '/' + srcName, dstName)
|
||
return dstName
|
||
|
||
def downloadAllPatch(self):
|
||
for file in self.khanat_idx.CBNPFile:
|
||
tmp = self.getServerFile("%05d/%s.lzma" % (int(self.r2serverversion), file.FileName), False, "")
|
||
with lzma.open(tmp) as fin:
|
||
dstName = os.path.join(self.tempdir.name, file.FileName)
|
||
with open(dstName, "wb") as fout:
|
||
data = fin.read()
|
||
fout.write(data)
|
||
self.log.info("%s" % dstName)
|
||
|
||
def Emulate(self):
|
||
self.createAccount()
|
||
self.connectR2()
|
||
# download patch
|
||
self.ryzomidx = self.getServerFile("%05d/ryzom_%05d.idx" % (int(self.r2serverversion), int(self.r2serverversion)), False, "")
|
||
self.khanat_idx.readFromBinFile(self.ryzomidx)
|
||
self.khanat_idx.CProductDescriptionForClient_apply()
|
||
# Show detail patch
|
||
self.khanat_idx.decrypt_token()
|
||
self.khanat_idx.show()
|
||
# Todo analyze patch and download if necessary or update if incremental - see category
|
||
# Download all file in patch - login_patch.cpp:2578 # void CPatchThread::processFile (CPatchManager::SFileToPatch &rFTP)
|
||
#self.downloadAllPatch()
|
||
self.clientNetworkConnection.EmulateFirst()
|
||
|
||
|
||
def main():
|
||
FORMAT = '%(asctime)-15s %(levelname)s %(filename)s:%(lineno)d %(message)s'
|
||
logging.basicConfig(format=FORMAT)
|
||
log = logging.getLogger('myLogger')
|
||
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument("--khanaturl", help="khanat URL to auhtenticate", default='localhost')
|
||
parser.add_argument("--suffix", help="define suffix")
|
||
parser.add_argument("-d", "--debug", help="show debug message", action='store_true')
|
||
args = parser.parse_args()
|
||
|
||
if args.debug:
|
||
level = logging.getLevelName('DEBUG')
|
||
else:
|
||
level = logging.getLevelName('INFO')
|
||
log.setLevel(level)
|
||
|
||
client = ClientKhanat(args.khanaturl, suffix=args.suffix)
|
||
client.Emulate()
|
||
log.info("End")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|
||
#Test()
|