Skip to content

Instantly share code, notes, and snippets.

@aerosoul94
Last active November 13, 2023 01:48
Show Gist options
  • Save aerosoul94/fddfeffc82cdeadc27512fee47a5c18b to your computer and use it in GitHub Desktop.
Save aerosoul94/fddfeffc82cdeadc27512fee47a5c18b to your computer and use it in GitHub Desktop.
Script for extracting League of Legends Solid State Network solid files
import xml.etree.ElementTree as ET
import zlib
import struct
import argparse
import os
class File:
def __init__(self, elem):
self._file_name = elem.find('FileName').text
self._file_index = int(elem.find('FileIndex').text)
self._difference_file = elem.find('DifferenceFile').text
self._source_hash = elem.find('SourceHash').text
self._result_hash = elem.find('ResultHash').text
self._offset = int(elem.find('Offset').text)
self._file_size = int(elem.find('FileSize').text)
self._section_count = int(elem.find('SectionCount').text)
def file_name(self):
return self._file_name
def file_index(self):
return self._file_index
def difference_file(self):
return self._difference_file
def source_hash(self):
return self._source_hash
def result_hash(self):
return self._result_hash
def offset(self):
return self._offset
def file_size(self):
return self._file_size
def section_count(self):
return self._section_count
class Section:
def __init__(self, elem):
self._offset = int(elem.find('Offset').text)
self._compressed_size = int(elem.find('CompressedSize').text)
self._uncompressed_size = int(elem.find('UncompressedSize').text)
def offset(self):
return self._offset
def compressed_size(self):
return self._compressed_size
def uncompressed_size(self):
return self._uncompressed_size
class Store:
def __init__(self, elem):
self._section_list = elem.find('SectionList')
self._section_count = int(self._section_list.find('SectionCount').text)
self._sections = []
for section in self._section_list.findall('Section'):
self._sections.append(Section(section))
def sections(self):
return self._sections
class TocStore:
def __init__(self, tocstore, store):
self.store = store
self.tree = ET.parse(tocstore)
self.root = self.tree.getroot()
self.section_size = int(self.root.attrib['SectionSize'])
self.file_list = self.root[0]
self.store_list = self.root[1]
self.files = []
for file in list(self.file_list):
self.files.append(File(file))
self.stores = []
self.store_count = int(self.store_list.find('StoreCount').text)
for store in self.store_list.findall('Store'):
self.stores.append(Store(store))
# match files to their sections
self.patcher_store = {}
for file in self.files:
self.patcher_store[file] = self.get_sections_for_file(file)
def get_sections_for_file(self, file):
file_size = file.file_size()
offset = file.offset()
accumulated_size = 0
start = False
sections = []
# TODO: use file.section_count()
store = self.stores[0]
for section in store.sections():
if section.offset() == offset:
start = True
if start == True:
sections.append(section)
accumulated_size += section.uncompressed_size()
if accumulated_size == file_size:
break
return sections
def extract(self, path):
f = open(self.store, 'rb')
errors = []
for item in self.patcher_store.items():
file = item[0]
sections = item[1]
dirname = path + '/' + os.path.dirname(file.file_name())
if not os.path.exists(dirname) and dirname != '':
os.makedirs(dirname)
file_name = path + '/' + file.file_name()
if file.difference_file() == 'TRUE':
file_name += ".diff"
print(file_name)
o = open(file_name, 'wb')
for section in sections:
#print(section.offset())
f.seek(section.offset() + 0x148)
try:
data = zlib.decompress(f.read(section.compressed_size()))
o.write(data)
except Exception as e:
errors.append("ERROR: {0} [offset: {1}, size: {2}] ({3})".format(file_name, hex(section.offset()), hex(section.compressed_size()), str(e)))
o.close()
print("")
if len(errors) > 0:
print("Extraction completed with {0} errors".format(len(errors)))
for error in errors:
print(error)
print("")
def main():
parser = argparse.ArgumentParser(description="Extract LOL files")
parser.add_argument('tocstore', type=str, help='Input TOC Store file')
parser.add_argument('store', type=str, help='Input Store file')
#parser.add_argument('output', type=str, help='Output directory')
args = parser.parse_args()
tocstore = TocStore(args.tocstore, args.store)
output_path = args.tocstore.split('.tocstore')[0]
tocstore.extract(output_path)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment