mirror of
https://github.com/Virtual-World-RE/NeoGF.git
synced 2024-11-15 09:45:33 +01:00
Update doltool.py
Added section split when overflowing, better formating for stats, new controls on errors that could occur.
This commit is contained in:
parent
c108d4a822
commit
b8f217d7b6
|
@ -3,15 +3,20 @@ import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
__version__ = "0.0.6"
|
__version__ = "0.0.7"
|
||||||
__author__ = "rigodron, algoflash, GGLinnk"
|
__author__ = "rigodron, algoflash, GGLinnk"
|
||||||
__license__ = "MIT"
|
__license__ = "MIT"
|
||||||
__status__ = "developpement"
|
__status__ = "developpement"
|
||||||
|
|
||||||
|
|
||||||
|
# raised when the action replay ini file contains a bad formated entry
|
||||||
class InvalidIniFileEntryError(Exception): pass
|
class InvalidIniFileEntryError(Exception): pass
|
||||||
|
# raised when trying to resolve an invalid dol file offset
|
||||||
class InvalidImgOffsetError(Exception): pass
|
class InvalidImgOffsetError(Exception): pass
|
||||||
|
# raised when trying to resolve an out of section Virtual address
|
||||||
class InvalidVirtualAddressError(Exception): pass
|
class InvalidVirtualAddressError(Exception): pass
|
||||||
|
# raised when Virtual address + length Overflow out of sections
|
||||||
|
class SectionsOverflowError(Exception): pass
|
||||||
|
|
||||||
|
|
||||||
# Get non-overlapping intervals from interval by removing intervals_to_remove
|
# Get non-overlapping intervals from interval by removing intervals_to_remove
|
||||||
|
@ -48,7 +53,8 @@ def remove_intervals_from_interval(interval:list, intervals_to_remove:list):
|
||||||
def parse_action_replay_ini(path:Path):
|
def parse_action_replay_ini(path:Path):
|
||||||
action_replay_lines = path.read_text().splitlines()
|
action_replay_lines = path.read_text().splitlines()
|
||||||
|
|
||||||
# Address = (first 4 bytes & 0x01FFFFFF) | 0x80000000
|
# address = (first 4 bytes & 0x01FFFFFF) | 0x80000000
|
||||||
|
# opcode = first byte & 0xFE
|
||||||
pattern = re.compile("^(0[2345][0-9a-zA-Z]{6}) ([0-9a-zA-Z]{8})$")
|
pattern = re.compile("^(0[2345][0-9a-zA-Z]{6}) ([0-9a-zA-Z]{8})$")
|
||||||
result_list = []
|
result_list = []
|
||||||
|
|
||||||
|
@ -65,15 +71,15 @@ def parse_action_replay_ini(path:Path):
|
||||||
virtual_address = (int(res[1], base=16) & 0x01FFFFFF) | 0x80000000
|
virtual_address = (int(res[1], base=16) & 0x01FFFFFF) | 0x80000000
|
||||||
opcode = int(res[1][:2], base=16) & 0xFE
|
opcode = int(res[1][:2], base=16) & 0xFE
|
||||||
bytes_value = None
|
bytes_value = None
|
||||||
|
|
||||||
if opcode == 0x04:
|
if opcode == 0x04:
|
||||||
bytes_value = int(res[2], 16).to_bytes(4, "big")
|
bytes_value = int(res[2], 16).to_bytes(4, "big")
|
||||||
elif opcode == 0x02:
|
elif opcode == 0x02:
|
||||||
bytes_value = (int(res[2][:4], 16) + 1) * int(res[2][4:], 16).to_bytes(2, "big")
|
bytes_value = (int(res[2][:4], 16) + 1) * int(res[2][4:], 16).to_bytes(2, "big")
|
||||||
else:
|
else:
|
||||||
raise InvalidIniFileEntryError("Error - Arcode has to be in format: '0AXXXXXX XXXXXXXX' with A in [2,3,4,5] and X in [0-9a-fA-F] line \"{action_replay_line}\".")
|
raise InvalidIniFileEntryError("Error - Arcode has to be in format: '0AXXXXXX XXXXXXXX' with A in [2,3,4,5] and X in [0-9a-fA-F] line \"{action_replay_line}\".")
|
||||||
|
|
||||||
result_list.append( (virtual_address, bytes_value) )
|
result_list.append( (virtual_address, bytes_value) )
|
||||||
"""
|
|
||||||
"""
|
|
||||||
return result_list
|
return result_list
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,19 +111,19 @@ class Dol:
|
||||||
self.__sections_info.append( (offset, address, length, is_used) )
|
self.__sections_info.append( (offset, address, length, is_used) )
|
||||||
# print a table with each sections
|
# print a table with each sections
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
res = f"Entry point: {self.__entry_point:08x}\n\n"
|
res = f"Entry point: {self.__entry_point:08x}\n\n|"
|
||||||
res += "Section | Offset | Address | Length | Used\n" + "-"*48 + "\n"
|
res += "-"*50 + "|\n| Section | Offset | Address | Length | Used |\n|" + "-"*9 + ("|"+"-"*10)*3 + "|" + "-"*7 + "|\n"
|
||||||
i = 0
|
i = 0
|
||||||
for section in self.__sections_info:
|
for section in self.__sections_info:
|
||||||
res+= "text"+str(i) if i < 7 else "data"+str(i)
|
res+= "| text"+str(i) if i < 7 else "| data"+str(i)
|
||||||
if i < 10: res += " "
|
if i < 10: res += " "
|
||||||
res += f" | {section[0]:08x} | {section[1]:08x} | {section[2]:08x} | {str(section[3])}\n"
|
res += f" | {section[0]:08x} | {section[1]:08x} | {section[2]:08x} | {str(section[3]).ljust(5)} |\n"
|
||||||
i += 1
|
i += 1
|
||||||
res += f"\nbss: address:{self.__bss_info[0]:08x} length:{self.__bss_info[1]:08x}"
|
res += "|"+"-"*50+f"|\n\nbss: address:{self.__bss_info[0]:08x} length:{self.__bss_info[1]:08x}"
|
||||||
return res
|
return res
|
||||||
"""
|
"""
|
||||||
# search_raw: bytecode
|
# search_raw: bytecode
|
||||||
# we could also identify text segments to improve search
|
# we could also identify text sections to improve search
|
||||||
def search_raw(self, bytecode:bytes):
|
def search_raw(self, bytecode:bytes):
|
||||||
if len(bytecode) == 0:
|
if len(bytecode) == 0:
|
||||||
raise Exception("Error - No bytecode.")
|
raise Exception("Error - No bytecode.")
|
||||||
|
@ -127,6 +133,27 @@ class Dol:
|
||||||
offsets.append(self.resolve_img2virtual(i + Dol.HEADER_LEN))
|
offsets.append(self.resolve_img2virtual(i + Dol.HEADER_LEN))
|
||||||
return offsets if len(offsets) > 0 else None
|
return offsets if len(offsets) > 0 else None
|
||||||
"""
|
"""
|
||||||
|
# When patching a section we could overflow on the next section
|
||||||
|
# Return list of [ [virtual_address:int, value:bytes], ... ]
|
||||||
|
# raise SectionsOverflowError if part of the bytecode is out of the existing sections
|
||||||
|
# raise InvalidVirtualAddressError if the base virtual address is out of the existing sections
|
||||||
|
def __get_section_mapped_values(self, virtual_address:int, bytes_value:bytes):
|
||||||
|
for section_info in self.__sections_info:
|
||||||
|
if not section_info[3]: continue
|
||||||
|
# first byte out of section:
|
||||||
|
section_end_address = section_info[1] + section_info[2]
|
||||||
|
if virtual_address >= section_info[1] and virtual_address < section_end_address:
|
||||||
|
if virtual_address + len(bytes_value) <= section_end_address:
|
||||||
|
return [ [section_info[0] + virtual_address - section_info[1], bytes_value] ] # dol offset and value
|
||||||
|
# Here we have to split the value to find where in the dol is the second part
|
||||||
|
splited_len = section_end_address - virtual_address
|
||||||
|
splited_result = [[section_info[0] + virtual_address - section_info[1], bytes_value[:splited_len]]]
|
||||||
|
try:
|
||||||
|
splited_result += self.__get_section_mapped_values(virtual_address + splited_len, bytes_value[splited_len:])
|
||||||
|
return splited_result
|
||||||
|
except InvalidVirtualAddressError:
|
||||||
|
raise SectionsOverflowError(f"Error - Value Overflow in an inexistant dol initial section: {virtual_address:08x}:{bytes_value.hex()}")
|
||||||
|
raise InvalidVirtualAddressError(f"Error - Not found in dol initial sections: {virtual_address:08x}")
|
||||||
# Resolve a dol absolute offset to a virtual memory address
|
# Resolve a dol absolute offset to a virtual memory address
|
||||||
def resolve_img2virtual(self, offset:int):
|
def resolve_img2virtual(self, offset:int):
|
||||||
memory_address = None
|
memory_address = None
|
||||||
|
@ -134,14 +161,14 @@ class Dol:
|
||||||
if not section_info[3]: continue
|
if not section_info[3]: continue
|
||||||
if offset >= section_info[0] and offset < section_info[0] + section_info[2]:
|
if offset >= section_info[0] and offset < section_info[0] + section_info[2]:
|
||||||
return section_info[1] + offset - section_info[0]
|
return section_info[1] + offset - section_info[0]
|
||||||
raise InvalidImgOffsetError(f"Not found: {offset:08x}")
|
raise InvalidImgOffsetError(f"Error - Invalid dol image offset: {offset:08x}")
|
||||||
# Resolve a virtual memory address to a dol absolute offset
|
# Resolve a virtual memory address to a dol absolute offset
|
||||||
def resolve_virtual2img(self, address:int):
|
def resolve_virtual2img(self, address:int):
|
||||||
for section_info in self.__sections_info:
|
for section_info in self.__sections_info:
|
||||||
if not section_info[3]: continue
|
if not section_info[3]: continue
|
||||||
if address >= section_info[1] and address < section_info[1] + section_info[2]:
|
if address >= section_info[1] and address < section_info[1] + section_info[2]:
|
||||||
return section_info[0] + address - section_info[1]
|
return section_info[0] + address - section_info[1]
|
||||||
raise InvalidVirtualAddressError(f"Not found in dol initial segments: {address:08x}")
|
raise InvalidVirtualAddressError(f"Error - Not found in dol initial sections: {address:08x}")
|
||||||
def stats(self):
|
def stats(self):
|
||||||
print(self)
|
print(self)
|
||||||
|
|
||||||
|
@ -183,11 +210,11 @@ class Dol:
|
||||||
result_intervals += [[empty_interval[0], empty_interval[1], empty_interval[2], empty_interval[2] - empty_interval[1]]]
|
result_intervals += [[empty_interval[0], empty_interval[1], empty_interval[2], empty_interval[2] - empty_interval[1]]]
|
||||||
|
|
||||||
result_intervals.sort(key=lambda x: x[1])
|
result_intervals.sort(key=lambda x: x[1])
|
||||||
str_buffer = "\nSection | beg_addr | end_addr | length |\n" + "-"*48 + "\n"
|
str_buffer = "\n|"+"-"*46+"|\n| Section | beg_addr | end_addr | length |\n|" + "-"*13 + ("|"+"-"*10)*3 + "|\n"
|
||||||
for interval in result_intervals:
|
for interval in result_intervals:
|
||||||
str_buffer += f"{interval[0].ljust(12)} | {interval[1]:08x} | {interval[2]:08x} | {interval[3]:08x} | \n"
|
str_buffer += f"| {interval[0].ljust(11)} | {interval[1]:08x} | {interval[2]:08x} | {interval[3]:08x} |\n"
|
||||||
|
|
||||||
print(str_buffer)
|
print(str_buffer + "|"+"-"*46+"|")
|
||||||
def extract(self, filename:str, section_index:int):
|
def extract(self, filename:str, section_index:int):
|
||||||
if section_index > 17:
|
if section_index > 17:
|
||||||
raise Exception("Error - Section index has to be in 0 - 17")
|
raise Exception("Error - Section index has to be in 0 - 17")
|
||||||
|
@ -202,8 +229,9 @@ class Dol:
|
||||||
self.__data = bytearray(self.__data)
|
self.__data = bytearray(self.__data)
|
||||||
for virtualaddress_bytes in virtualaddress_bytes_list:
|
for virtualaddress_bytes in virtualaddress_bytes_list:
|
||||||
offset = self.resolve_virtual2img(virtualaddress_bytes[0])
|
offset = self.resolve_virtual2img(virtualaddress_bytes[0])
|
||||||
print(f"Patching {virtualaddress_bytes[0]:08x} at dol offset {offset:08x} with value {virtualaddress_bytes[1].hex()}")
|
for mapped_list in self.__get_section_mapped_values(virtualaddress_bytes[0], virtualaddress_bytes[1]):
|
||||||
self.__data[offset: offset + len(virtualaddress_bytes[1])] = virtualaddress_bytes[1]
|
print(f"Patching {virtualaddress_bytes[0]:08x} at dol offset {offset:08x} with value {virtualaddress_bytes[1].hex()}")
|
||||||
|
self.__data[offset: offset + len(virtualaddress_bytes[1])] = virtualaddress_bytes[1]
|
||||||
|
|
||||||
|
|
||||||
def get_argparser():
|
def get_argparser():
|
||||||
|
|
Loading…
Reference in New Issue
Block a user