Skip to content

Commit

Permalink
update some formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfcomp committed Apr 15, 2024
1 parent f6b608b commit ccb8277
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 61 deletions.
122 changes: 88 additions & 34 deletions luminapie/excel.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from luminapie.enums import ExcelColumnDataType


class ExcelListFile:
def __init__(self, data: list[bytes]):
self.data = b''.join(data).split('\r\n'.encode('utf-8'))
Expand All @@ -18,6 +19,7 @@ def parse(self):
continue
self.dict[int(linearr[1])] = linearr[0]


class ExcelHeader:
def __init__(self, data: bytes):
self.data = data
Expand All @@ -40,6 +42,7 @@ def parse(self):
def __repr__(self):
return f'''Header: {self.magic}, version: {self.version}, data_offset: {self.data_offset}, column_count: {self.column_count}, page_count: {self.page_count}, language_count: {self.language_count}, unknown1: {self.unknown1}, unknown2: {self.unknown2}, variant: {self.variant}, unknown3: {self.unknown3}, row_count: {self.row_count}, unknown4: {self.unknown4}'''


class ExcelColumnDefinition:
def __init__(self, data: bytes):
self.data = data
Expand All @@ -51,7 +54,8 @@ def parse(self):

def __repr__(self):
return f'''[{self.type.name}, {self.offset:x}]'''



class ExcelDataPagination:
def __init__(self, data: bytes):
self.data = data
Expand All @@ -60,10 +64,11 @@ def __init__(self, data: bytes):
def parse(self):
self.start_id = int.from_bytes(self.data[0:2], 'big')
self.row_count = int.from_bytes(self.data[2:4], 'big')

def __repr__(self):
return f'''[{self.start_id:x}, {self.row_count}]'''


class ExcelHeaderFile:
def __init__(self, data: list[bytes]):
self.data = data[0]
Expand All @@ -72,21 +77,31 @@ def __init__(self, data: list[bytes]):
self.languages: list[int] = []
self.header: ExcelHeader = None
self.parse()

def parse(self):
self.header = ExcelHeader(self.data[0:32])
if(self.header.magic != b'EXHF'):
if self.header.magic != b'EXHF':
raise Exception('Invalid EXHF header')
self.column_definitions: list[ExcelColumnDefinition] = []
for i in range(self.header.column_count):
self.column_definitions.append(ExcelColumnDefinition(self.data[32 + (i * 4):32 + ((i + 1) * 4)]))
self.column_definitions.append(ExcelColumnDefinition(self.data[32 + (i * 4) : 32 + ((i + 1) * 4)]))
self.pagination: list[ExcelDataPagination] = []
for i in range(self.header.page_count):
self.pagination.append(ExcelDataPagination(self.data[32 + (self.header.column_count * 4) + (i * 4):32 + (self.header.column_count * 4) + ((i + 1) * 4)]))
self.pagination.append(
ExcelDataPagination(
self.data[
32
+ (self.header.column_count * 4)
+ (i * 4) : 32
+ (self.header.column_count * 4)
+ ((i + 1) * 4)
]
)
)
self.languages: list[int] = []
for i in range(self.header.language_count):
self.languages.append(self.data[32 + (self.header.column_count * 4) + (self.header.page_count * 4) + i])

def map_names(self, names: dict[int, str] = {}) -> tuple[dict[int, tuple[str, str]], int]:
"""Maps the header column definitions to names and c types."""
mapped: dict[int, tuple[str, str]] = {}
Expand All @@ -95,10 +110,12 @@ def map_names(self, names: dict[int, str] = {}) -> tuple[dict[int, tuple[str, st
if self.column_definitions[i].offset > self.column_definitions[largest_offset_index].offset:
largest_offset_index = i

size = self.column_definitions[largest_offset_index].offset + column_data_type_to_size(self.column_definitions[largest_offset_index].type)
size = self.column_definitions[largest_offset_index].offset + column_data_type_to_size(
self.column_definitions[largest_offset_index].type
)

for i in range(self.header.column_count):
if (self.column_definitions[i].offset in mapped and mapped[self.column_definitions[i].offset] is not None):
if self.column_definitions[i].offset in mapped and mapped[self.column_definitions[i].offset] is not None:
[_, name] = mapped[self.column_definitions[i].offset]
if name.split('_')[0] == 'Unknown':
continue
Expand All @@ -107,50 +124,87 @@ def map_names(self, names: dict[int, str] = {}) -> tuple[dict[int, tuple[str, st
if column_data_type_to_c_type(self.column_definitions[i].type) != 'unsigned __int8':
continue
else:
mapped[self.column_definitions[i].offset] = (column_data_type_to_c_type(self.column_definitions[i].type), f'{name}_{names[i]}')
mapped[self.column_definitions[i].offset] = (
column_data_type_to_c_type(self.column_definitions[i].type),
f'{name}_{names[i]}',
)
else:
if i not in names:
mapped[self.column_definitions[i].offset] = (column_data_type_to_c_type(self.column_definitions[i].type), f'Unknown_{self.column_definitions[i].offset:X}')
mapped[self.column_definitions[i].offset] = (
column_data_type_to_c_type(self.column_definitions[i].type),
f'Unknown_{self.column_definitions[i].offset:X}',
)
else:
mapped[self.column_definitions[i].offset] = (column_data_type_to_c_type(self.column_definitions[i].type), names[i])
mapped[self.column_definitions[i].offset] = (
column_data_type_to_c_type(self.column_definitions[i].type),
names[i],
)
mapped = dict(sorted(mapped.items()))
return [mapped, size]

def __repr__(self):
return f'''ExcelHeaderFile: {self.header} , {self.column_definitions} , {self.pagination} , {self.languages}'''



def column_data_type_to_c_type(column_data_type: ExcelColumnDataType) -> str:
if(column_data_type == ExcelColumnDataType.Bool):
if column_data_type == ExcelColumnDataType.Bool:
return 'bool'
elif(column_data_type == ExcelColumnDataType.Int8):
elif column_data_type == ExcelColumnDataType.Int8:
return '__int8'
elif(column_data_type == ExcelColumnDataType.UInt8):
elif column_data_type == ExcelColumnDataType.UInt8:
return 'unsigned __int8'
elif(column_data_type == ExcelColumnDataType.Int16):
elif column_data_type == ExcelColumnDataType.Int16:
return '__int16'
elif(column_data_type == ExcelColumnDataType.UInt16):
elif column_data_type == ExcelColumnDataType.UInt16:
return 'unsigned __int16'
elif(column_data_type == ExcelColumnDataType.Int32):
elif column_data_type == ExcelColumnDataType.Int32:
return '__int32'
elif(column_data_type == ExcelColumnDataType.UInt32):
elif column_data_type == ExcelColumnDataType.UInt32:
return 'unsigned __int32'
elif(column_data_type == ExcelColumnDataType.Float32):
elif column_data_type == ExcelColumnDataType.Float32:
return 'float'
elif(column_data_type == ExcelColumnDataType.Int64):
elif column_data_type == ExcelColumnDataType.Int64:
return '__int64'
elif(column_data_type == ExcelColumnDataType.UInt64):
elif column_data_type == ExcelColumnDataType.UInt64:
return 'unsigned __int64'
elif(column_data_type == ExcelColumnDataType.PackedBool0 or column_data_type == ExcelColumnDataType.PackedBool1 or column_data_type == ExcelColumnDataType.PackedBool2 or column_data_type == ExcelColumnDataType.PackedBool3 or column_data_type == ExcelColumnDataType.PackedBool4 or column_data_type == ExcelColumnDataType.PackedBool5 or column_data_type == ExcelColumnDataType.PackedBool6 or column_data_type == ExcelColumnDataType.PackedBool7):
return 'unsigned __int8' # IDA doesn't support bitfields in decompilation, so we'll just use a byte. A different method would be to create an enum for each bitfield, but that's a lot of work that i cant be bothered doing.
elif(column_data_type == ExcelColumnDataType.String):
return '__unsigned __int32' # strings are stored as a 4 byte offset to a string table, so we'll just use a 4 byte integer since another function handles reasign of strings.

elif (
column_data_type == ExcelColumnDataType.PackedBool0
or column_data_type == ExcelColumnDataType.PackedBool1
or column_data_type == ExcelColumnDataType.PackedBool2
or column_data_type == ExcelColumnDataType.PackedBool3
or column_data_type == ExcelColumnDataType.PackedBool4
or column_data_type == ExcelColumnDataType.PackedBool5
or column_data_type == ExcelColumnDataType.PackedBool6
or column_data_type == ExcelColumnDataType.PackedBool7
):
return 'unsigned __int8' # IDA doesn't support bitfields in decompilation, so we'll just use a byte. A different method would be to create an enum for each bitfield, but that's a lot of work that i cant be bothered doing.
elif column_data_type == ExcelColumnDataType.String:
return '__unsigned __int32' # strings are stored as a 4 byte offset to a string table, so we'll just use a 4 byte integer since another function handles reasign of strings.


def column_data_type_to_size(column_data_type: ExcelColumnDataType) -> int:
if(column_data_type == ExcelColumnDataType.Bool or column_data_type == ExcelColumnDataType.Int8 or column_data_type == ExcelColumnDataType.UInt8 or column_data_type == ExcelColumnDataType.PackedBool0 or column_data_type == ExcelColumnDataType.PackedBool1 or column_data_type == ExcelColumnDataType.PackedBool2 or column_data_type == ExcelColumnDataType.PackedBool3 or column_data_type == ExcelColumnDataType.PackedBool4 or column_data_type == ExcelColumnDataType.PackedBool5 or column_data_type == ExcelColumnDataType.PackedBool6 or column_data_type == ExcelColumnDataType.PackedBool7):
if (
column_data_type == ExcelColumnDataType.Bool
or column_data_type == ExcelColumnDataType.Int8
or column_data_type == ExcelColumnDataType.UInt8
or column_data_type == ExcelColumnDataType.PackedBool0
or column_data_type == ExcelColumnDataType.PackedBool1
or column_data_type == ExcelColumnDataType.PackedBool2
or column_data_type == ExcelColumnDataType.PackedBool3
or column_data_type == ExcelColumnDataType.PackedBool4
or column_data_type == ExcelColumnDataType.PackedBool5
or column_data_type == ExcelColumnDataType.PackedBool6
or column_data_type == ExcelColumnDataType.PackedBool7
):
return 1
elif(column_data_type == ExcelColumnDataType.Int16 or column_data_type == ExcelColumnDataType.UInt16):
elif column_data_type == ExcelColumnDataType.Int16 or column_data_type == ExcelColumnDataType.UInt16:
return 2
elif(column_data_type == ExcelColumnDataType.Int32 or column_data_type == ExcelColumnDataType.UInt32 or column_data_type == ExcelColumnDataType.Float32 or column_data_type == ExcelColumnDataType.String):
elif (
column_data_type == ExcelColumnDataType.Int32
or column_data_type == ExcelColumnDataType.UInt32
or column_data_type == ExcelColumnDataType.Float32
or column_data_type == ExcelColumnDataType.String
):
return 4
elif(column_data_type == ExcelColumnDataType.Int64 or column_data_type == ExcelColumnDataType.UInt64):
return 8
elif column_data_type == ExcelColumnDataType.Int64 or column_data_type == ExcelColumnDataType.UInt64:
return 8
13 changes: 7 additions & 6 deletions luminapie/file_handlers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import os


def get_game_data_folders(root: str):
for folder in os.listdir(os.path.join(root, 'sqpack')):
if (os.path.isdir(os.path.join(root, 'sqpack', folder))):
if os.path.isdir(os.path.join(root, 'sqpack', folder)):
yield folder


def get_files(path):
files: list[bytes] = []
for (dir_path, dir_names, file_names) in os.walk(path):
for dir_path, dir_names, file_names in os.walk(path):
files.extend(os.path.join(dir_path, file) for file in file_names)

return files
Expand All @@ -17,17 +18,17 @@ def get_files(path):
def get_sqpack_files(root: str, path: str):
for file in get_files(os.path.join(root, 'sqpack', path)):
ext = file.split('.')[-1]
if (ext.startswith('dat')):
if ext.startswith('dat'):
yield file


def get_sqpack_index(root: str, path: str):
for file in get_files(os.path.join(root, 'sqpack', path)):
if (file.endswith('.index')):
if file.endswith('.index'):
yield file


def get_sqpack_index2(root: str, path: str):
for file in get_files(os.path.join(root, 'sqpack', path)):
if (file.endswith('.index2')):
yield file
if file.endswith('.index2'):
yield file
13 changes: 7 additions & 6 deletions luminapie/game_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

crc = Crc32()


class Repository:
def __init__(self, name: str, root: str):
self.root = root
Expand All @@ -15,12 +16,12 @@ def __init__(self, name: str, root: str):
self.get_expansion_id()

def get_expansion_id(self):
if (self.name.startswith('ex')):
if self.name.startswith('ex'):
self.expansion_id = int(self.name.removeprefix('ex'))

def parse_version(self):
versionPath = ""
if (self.name == 'ffxiv'):
if self.name == 'ffxiv':
versionPath = os.path.join(self.root, 'ffxivgame.ver')
else:
versionPath = os.path.join(self.root, 'sqpack', self.name, self.name + '.ver')
Expand All @@ -47,7 +48,7 @@ def get_file(self, hash: int):
id = index.data_file_id()
offset = index.data_file_offset()
return SqPack(self.root, sqpack.data_files[id]).read_file(offset)

def __repr__(self):
return f'''Repository: {self.name} ({self.version}) - {self.expansion_id}'''

Expand All @@ -59,7 +60,7 @@ def __init__(self, root: str):
self.setup()

def get_repo_index(self, folder: str):
if (folder == 'ffxiv'):
if folder == 'ffxiv':
return 0
else:
return int(folder.removeprefix('ex'))
Expand Down Expand Up @@ -90,6 +91,6 @@ def __init__(self, path: str):
self.repo = parts[1]
if self.repo[0] != 'e' or self.repo[1] != 'x' or not self.repo[2].isdigit():
self.repo = 'ffxiv'

def __repr__(self):
return f'''ParsedFileName: {self.path}, category: {self.category}, index: {self.index:X}, index2: {self.index2:X}, repo: {self.repo}'''
return f'''ParsedFileName: {self.path}, category: {self.category}, index: {self.index:X}, index2: {self.index2:X}, repo: {self.repo}'''
36 changes: 28 additions & 8 deletions luminapie/se_crc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,30 @@ def calc(self, value: bytes):
size = len(value)
crc_local = 4294967295 ^ 0
while size >= 16:
a = self.table[(3*256) + value[start + 12]] ^ self.table[(2*256) + value[start + 13]] ^ self.table[(1*256) + value[start + 14]] ^ self.table[(0*256) + value[start + 15]]
b = self.table[(7*256) + value[start + 8]] ^ self.table[(6*256) + value[start + 9]] ^ self.table[(5*256) + value[start + 10]] ^ self.table[(4*256) + value[start + 11]]
c = self.table[(11*256) + value[start + 4]] ^ self.table[(10*256) + value[start + 5]] ^ self.table[(9*256) + value[start + 6]] ^ self.table[(8*256) + value[start + 7]]
d = self.table[(15*256) + (self.byte(crc_local) ^ value[start])] ^ self.table[(14*256) + (self.byte(crc_local, 1) ^ value[start+1])] ^ self.table[(13*256) + (self.byte(crc_local, 2) ^ value[start+2])] ^ self.table[(12*256) + (self.byte(crc_local, 3) ^ value[start+3])]
a = (
self.table[(3 * 256) + value[start + 12]]
^ self.table[(2 * 256) + value[start + 13]]
^ self.table[(1 * 256) + value[start + 14]]
^ self.table[(0 * 256) + value[start + 15]]
)
b = (
self.table[(7 * 256) + value[start + 8]]
^ self.table[(6 * 256) + value[start + 9]]
^ self.table[(5 * 256) + value[start + 10]]
^ self.table[(4 * 256) + value[start + 11]]
)
c = (
self.table[(11 * 256) + value[start + 4]]
^ self.table[(10 * 256) + value[start + 5]]
^ self.table[(9 * 256) + value[start + 6]]
^ self.table[(8 * 256) + value[start + 7]]
)
d = (
self.table[(15 * 256) + (self.byte(crc_local) ^ value[start])]
^ self.table[(14 * 256) + (self.byte(crc_local, 1) ^ value[start + 1])]
^ self.table[(13 * 256) + (self.byte(crc_local, 2) ^ value[start + 2])]
^ self.table[(12 * 256) + (self.byte(crc_local, 3) ^ value[start + 3])]
)
crc_local = d ^ c ^ b ^ a
start += 16
size -= 16
Expand All @@ -31,9 +51,9 @@ def calc(self, value: bytes):
size -= 1

return ~(crc_local ^ 4294967295) % (1 << 32)
def byte(self, number: int, i = 0):
return (number & (0xff << (i * 8))) >> (i * 8)

def byte(self, number: int, i=0):
return (number & (0xFF << (i * 8))) >> (i * 8)

def calc_index(self, path: str):
path_parts = path.split('/')
Expand All @@ -46,4 +66,4 @@ def calc_index(self, path: str):
return folder_crc << 32 | file_crc

def calc_index2(self, path: str):
return self.calc(path.encode('utf-8'))
return self.calc(path.encode('utf-8'))
Loading

0 comments on commit ccb8277

Please sign in to comment.