Source code for gciso.dolfile

import enum
import struct

from .isofilewrapper import IsoInternalFileWrapper

[docs]class DolFile(object): """ Represents a DOL executable file. You probably don't want to instance this class yourself, but rather call :meth:`IsoFile.getDolFile`. See here for some more information about the attributes of this class: http://hitmen.c02.at/files/yagcd/yagcd/chap14.html#sec14.2 The DOL file is loaded into memory by the Apploader, section by section. This may or may not include permutation or fragmentation (the sections stay contiguous, but there may be gaps between the sections after being loaded.) Parameters ---------- data : bytes or :class:`IsoInternalFileWrapper` The file to be interpreted as a DOL file. See description of this class. Attributes ---------- data : bytes The DOL file as bytes bssMemAddress : int bssSize : int entryPoint : int bodyOffset : int Offset to the the data after the header of the DOL textSections : list of :class:`Section` The text sections of the DOL dataSections : list of :class:`Section` The data sections of the DOL sections : list of :class:`Section` Just a joined list of :attr:`DolFile.textSections` and :attr:`DolFile.dataSections` sectionsDolOrder : list of :class:`Section` :attr:`DolFile.sections` but sorted by DOL offset sectionsMemOrder : list of :class:`Section` :attr:`DolFile.sections` but sorted by memory address """
[docs] class SectionType(enum.Enum): """ The type of section in the DOL file. """ TEXT = "text" DATA = "data"
[docs] class Section(object): """ A section in a DOL file. You probably never want to instantiate this class yourself. Parameters ---------- index : int The index of the section sectionType : :class:`DolFile.SectionType` The type of the section dolOffset : int memAddress : int size : int Attributes ---------- index : int type : :class:`DolFile.SectionType` dolOffset : int endDolOffset : int memAddress : int endMemAddress : int size : int """ def __init__(self, index, sectionType, dolOffset, memAddress, size): self.index = index self.type = sectionType self.dolOffset = dolOffset # both "end" offsets/addresses point right after the section self.endDolOffset = dolOffset + size self.memAddress = memAddress self.endMemAddress = memAddress + size self.size = size
[docs] def isBefore(self, other): return self.memAddress + self.size == other.memAddress \ and self.dolOffset + self.size == other.dolOffset
def __repr__(self): return "<DolFile.Section {} {} - dolOffset: 0x{:x}, memAddress: 0x{:x}, size: 0x{:x}".format( self.type, self.index, self.dolOffset, self.memAddress, self.size)
@staticmethod def _zipSections(sectionType, offsets, memAddresses, sizes): assert len(offsets) == len(memAddresses) == len(sizes) ret = [] for i in range(len(offsets)): offset, memAddress, size = offsets[i], memAddresses[i], sizes[i] if offset == 0 or memAddress == 0 or size == 0: break ret.append(DolFile.Section(i, sectionType, offset, memAddress, size)) return ret def __init__(self, data): if isinstance(data, IsoInternalFileWrapper): data.seek(0) data = data.read() self.data = data # header textSectionFileOffsets = struct.unpack_from(">6I", data, 0) dataSectionFileOffsets = struct.unpack_from(">10I", data, 0x1C) textSectionMemAddresses = struct.unpack_from(">6I", data, 0x48) dataSectionMemAddresses = struct.unpack_from(">10I", data, 0x64) textSectionSizes = struct.unpack_from(">6I", data, 0x90) dataSectionSizes = struct.unpack_from(">10I", data, 0xAC) self.bssMemAddress = struct.unpack_from(">I", data, 0xD8)[0] self.bssSize = struct.unpack_from(">I", data, 0xDC)[0] self.entryPoint = struct.unpack_from(">I", data, 0xE0)[0] self.bodyOffset = 0x100 self.textSections = DolFile._zipSections(DolFile.SectionType.TEXT, textSectionFileOffsets, textSectionMemAddresses, textSectionSizes) self.dataSections = DolFile._zipSections(DolFile.SectionType.DATA, dataSectionFileOffsets, dataSectionMemAddresses, dataSectionSizes) self.sections = self.textSections + self.dataSections self.sectionsDolOrder = list(sorted(self.sections, key=lambda x: x.dolOffset)) self.sectionsMemOrder = list(sorted(self.sections, key=lambda x: x.memAddress))
[docs] def getSectionByMemAddress(self, memAddress): """ Parameters ---------- memAddress : int Memory address Returns ------- :class:`Section` or None The section the memory address points to or `None` if that address does not point to a DOL section. """ for section in self.sections: rel = memAddress - section.memAddress if rel >= 0 and rel < section.size: return section return None
[docs] def getSectionByDolOffset(self, dolOffset): """ Parameters ---------- dolOffset : int A offset inside the DOL file Returns ------- :class:`Section` or None The section `dolOffset` points to or `None` if that offset does not point to a DOL section. """ for section in self.sections: rel = dolOffset - section.dolOffset if rel >= 0 and rel < section.size: return section return None
[docs] def memAddressToDolOffset(self, memAddress): """ Parameters ---------- memAddress : int Memory address Returns ------- int or None The offset inside the DOL of the data that is loaded to `memAddress` if it belongs to a DOL section. `None` otherwise. """ section = self.getSectionByMemAddress(memAddress) if not section: return None return section.dolOffset + (memAddress - section.memAddress)
[docs] def dolOffsetToMemAddress(self, dolOffset): """ Parameters ---------- dolOffset : int An offset inside the DOL file. Returns ------- int or None The memory address the data pointed to by dolOffset is loaded to if it belongs to a DOL section. `None` otherwise. """ section = self.getSectionByDolOffset(dolOffset) if not section: return None return section.memAddress + (dolOffset - section.dolOffset)
[docs] def isMappedContiguousMem(self, memAddressStart, memAddressEnd): """ See :meth:`isMappedContiguous`, but starting with memory. This essentially just maps the memory addresses to DOL offsets and then calls :meth:`isMappedContiguous`. """ return self.isMappedContiguous(self.memAddressToDolOffset(memAddressStart), self.memAddressToDolOffset(memAddressEnd))
# dolOffsetEnd is not included! # if isContiguous(0, 4) is true, then the byte at offset 4 might not be # contiguous with the rest
[docs] def isMappedContiguous(self, dolOffsetStart, dolOffsetEnd): """ This function determines whether a range of (contiguous) memory in the DOL file is loaded contiguously to memory. Parameters ---------- dolOffsetStart : int The start of the data range inside the DOL dolOffsetEnd : int The end of the data range (non-inclusive). Returns ------- bool Notes ----- `dolOffsetEnd` not being inclusive means that if isContiguous(0, 4) is True, then the byte at offset 4 not be 4 bytes after the byte at offset 0 in memory. (Only byte 0, 1, 2 and 3 are). """ section = self.getSectionByDolOffset(dolOffsetStart) if dolOffsetEnd <= section.endDolOffset: return True else: dolIndex = self.sectionsDolOrder.index(section) memIndex = self.sectionsMemOrder.index(section) dolNext = self.sectionsDolOrder[dolIndex+1] memNext = self.sectionsDolOrder[memIndex+1] if dolNext == memNext and section.isBefore(dolNext): return self.isMappedContiguous(dolNext.dolOffset, dolOffsetEnd) else: return False