Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib/__pycache__/
ui/__pycache__/
config.json
7 changes: 4 additions & 3 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"workers_count": 4,
"max_retries": 0,
"output_path": "output",
"major": 7,
"build": 288,
"minor": 38
"major": 8,
"build": 256,
"minor": 37
}
24 changes: 21 additions & 3 deletions lib/worker.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import os
import time
import sys

from urllib.request import urlopen
from PyQt5.QtCore import QThread, pyqtSignal
Expand All @@ -10,10 +12,12 @@
class DownloadWorker(QThread):

file_downloaded = pyqtSignal(bool)
max_retries_reached = pyqtSignal(bool)

def __init__(self, download_queue,
masterhash, assets_url,
decompress_data, output_dir):
decompress_data, output_dir,
max_retries = 0):

self.stopped = False
self.is_running = True
Expand All @@ -23,6 +27,7 @@ def __init__(self, download_queue,
self.output_dir = output_dir
self.download_queue = download_queue
self.decompress_data = decompress_data
self.max_retries = max_retries

QThread.__init__(self)

Expand All @@ -33,8 +38,21 @@ def run(self):
if filename is not None:
file_url = join_path(self.assets_url, self.masterhash, filename)

file_data = urlopen(file_url)

self.retries = 0
while True:
try:
file_data = urlopen(file_url)
break
except Exception as e:
print(f"An error occured while fetching {file_url}\n{e}")
if self.retries < self.max_retries:
self.retries += 1
print("Retrying in 5 seconds...")
time.sleep(5)
else:
self.max_retries_reached.emit(True)
return self.stop()

os.makedirs(os.path.dirname(join_path(self.output_dir, filename)), exist_ok=True)

with open(join_path(self.output_dir, filename), 'wb') as f:
Expand Down
15 changes: 12 additions & 3 deletions lib/worker_launcher.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from PyQt5.QtCore import QThread, pyqtSignal

from lib.utils import join_path
from lib.utils import join_path, build_alert_box
from lib.worker import DownloadWorker


class WorkerLauncher(QThread):

file_downloaded = pyqtSignal(bool)
download_finished = pyqtSignal()
download_finished = pyqtSignal(bool)

def __init__(self, download_widget):
self.download_widget = download_widget
Expand All @@ -18,17 +18,20 @@ def run(self):
self.thread_list = []

self.download_widget.workers_count = self.download_widget.parent.settings_widget.workers_spinbox.value()
self.download_widget.max_retries = self.download_widget.parent.settings_widget.retries_spinbox.value()

for _ in range(self.download_widget.workers_count):
thread = DownloadWorker(self.download_widget.download_queue,
self.download_widget.fingerprint['sha'], self.download_widget.assets_host,
self.download_widget.enable_compression_checkbox.isChecked(),
join_path(self.download_widget.parent.settings_widget.folder_path_input.text(),
self.download_widget.fingerprint['sha'])
self.download_widget.fingerprint['sha']),
self.download_widget.max_retries
)

self.thread_list.append(thread)
thread.file_downloaded.connect(self.emit_file_downloaded)
thread.max_retries_reached.connect(self.max_retries_reached)
thread.start()

self.download_widget.download_queue.join()
Expand All @@ -39,6 +42,12 @@ def run(self):
def emit_file_downloaded(self, update_status):
self.file_downloaded.emit(update_status)

def max_retries_reached(self, boolean):
if boolean == True:
self.stop()
print("Max retries reached")
self.download_finished.emit(False)

def stop(self):
for thread in self.thread_list:
thread.stop()
59 changes: 41 additions & 18 deletions ui/download_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ def on_info_fetched(self, login_failed):

download_method = self.download_method_combo_box.currentText()

if login_failed == False:
self.parent.hide_loading()
self.parent.status_bar_label.setText('Connection error: couldn\'t connect to Supercell servers')
return build_alert_box('Connection error', 'An error occured when connecting to Supercell\'s servers. Please check the console and/or your internet connection!')

login_failed_error_code = login_failed.read_vint()

if login_failed_error_code == 7:
Expand All @@ -241,6 +246,8 @@ def on_info_fetched(self, login_failed):
self.assets_host = login_failed.read_string()

else:
self.parent.hide_loading()
self.parent.status_bar_label.setText('Couldn\'t find any host to download assets !')
return build_alert_box('Download error', 'Couldn\'t find any host to download assets !')

login_failed.read_string()
Expand All @@ -261,9 +268,13 @@ def on_info_fetched(self, login_failed):
return

elif login_failed_error_code == 10:
self.parent.hide_loading()
self.parent.status_bar_label.setText('Server is in maintenance, cannot fetch current info')
return build_alert_box('Error', 'Server is in maintenance, cannot fetch current info')

else:
self.parent.hide_loading()
self.parent.status_bar_label.setText('Wrong login failed code: {}'.format(login_failed_error_code))
return build_alert_box('Error', 'Wrong login failed code: {}'.format(login_failed_error_code))

if download_method == 'Latest Patch':
Expand Down Expand Up @@ -310,20 +321,24 @@ def request_login_failed(self):

client_hello = (10100).to_bytes(2, 'big') + len(client_hello_writer.buffer).to_bytes(3, 'big') + bytes(2) + client_hello_writer.buffer

s = socket.create_connection(('game.clashroyaleapp.com', 9339))
s.send(client_hello)
try:
s = socket.create_connection(('game.clashroyaleapp.com', 9339))
s.send(client_hello)

header = s.recv(7)
message_length = int.from_bytes(header[2:5], 'big')
header = s.recv(7)
message_length = int.from_bytes(header[2:5], 'big')

login_failed = b''
login_failed = b''

while message_length:
data = s.recv(message_length)
message_length -= len(data)
login_failed += data
while message_length:
data = s.recv(message_length)
message_length -= len(data)
login_failed += data

return Reader(login_failed)
return Reader(login_failed)
except Exception as e:
print(e)
return False

def update_client_hello_version(self):
self.start_button.setEnabled(False)
Expand Down Expand Up @@ -354,7 +369,7 @@ def stop_download(self):
self.worker_launcher.stop()
self.worker_launcher.quit()

self.on_donwload_finish()
self.on_download_finish()

def on_masterhash_changed(self, masterhash):
if not is_masterhash_valid(masterhash):
Expand All @@ -369,6 +384,7 @@ def start_download(self, wanted_extensions):
self.downloaded_files = 0
self.download_start_time = datetime.utcnow()
self.download_queue = Queue()
self.downloading = True

overwrite_existing_file = False

Expand Down Expand Up @@ -396,7 +412,7 @@ def start_download(self, wanted_extensions):
self.worker_launcher = WorkerLauncher(self)

self.worker_launcher.file_downloaded.connect(self.update_download_count)
self.worker_launcher.download_finished.connect(self.on_donwload_finish)
self.worker_launcher.download_finished.connect(self.on_download_finish)

self.worker_launcher.start()

Expand All @@ -409,26 +425,33 @@ def update_download_count(self, update_status):
self.downloaded_files,
self.total_files))

def on_donwload_finish(self):
def on_download_finish(self, success = True):
self.parent.hide_loading()
self.download_method_combo_box.setEnabled(True)

if self.downloaded_files:
elapsed_time = (datetime.utcnow() - self.download_start_time).seconds

self.parent.status_bar_label.setText('''Download finished ! {} files downloaded in {}min {}s'''.format(self.downloaded_files,
*divmod(elapsed_time, 60)))


if success == True:
self.parent.status_bar_label.setText('''Download finished ! {} files downloaded in {}min {}s'''.format(self.downloaded_files,
*divmod(elapsed_time, 60)))
else:
self.parent.status_bar_label.setText('''Download failed ! {} files downloaded in {}min {}s'''.format(self.downloaded_files,
*divmod(elapsed_time, 60)))
if self.downloading == True:
build_alert_box("Max retries reached", "Check your internet connection, and/or increase the max retries in the Settings.")

else:
self.parent.status_bar_label.setText('No files were downloaded !')

self.downloading = False
self.progress_bar.reset()
self.stop_button.setEnabled(False)
self.start_button.setEnabled(True)

def display_bruteforce_info(self):
self.parent.status_bar_label.setText('Bruteforce started ! Trying with major {}, build {}, minor {}'.format(self.major, self.build, self.minor))


class InfoFetcherThread(QThread):
info_fetched = pyqtSignal(object)
Expand Down
8 changes: 8 additions & 0 deletions ui/settings_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ def init_ui(self):
self.workers_spinbox.setRange(1, 10)
self.workers_spinbox.setValue(max(min(self.config['workers_count'], 10), 1))

self.retries_spinbox = QSpinBox()

self.retries_spinbox.setRange(1, 10)
self.retries_spinbox.setValue(max(self.config['max_retries'], 0))

self.save_settings_button = QPushButton('Save settings', self)
self.save_settings_button.setIcon(QIcon('ui/assets/save.png'))
self.save_settings_button.setIconSize(QSize(17, 17))
Expand All @@ -55,6 +60,8 @@ def init_ui(self):
self.main_layout.addWidget(self.browse_folder_widget)
self.main_layout.addWidget(QLabel('Workers count (up to 10):'))
self.main_layout.addWidget(self.workers_spinbox)
self.main_layout.addWidget(QLabel('Max retries:'))
self.main_layout.addWidget(self.retries_spinbox)
self.main_layout.addWidget(self.save_settings_button)

self.setLayout(self.main_layout)
Expand All @@ -66,5 +73,6 @@ def browse_folder(self):

def save_settings(self):
self.config['workers_count'] = self.workers_spinbox.value()
self.config['max_retries'] = self.retries_spinbox.value()

self.parent.save_config()