From a7b81539b1b32bc80561120f2b3de31ce47dd4ad Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Wed, 6 Dec 2023 08:29:24 -0500 Subject: [PATCH 1/3] Match MxBitmap::ImportColorsToPalette --- LEGO1/mxbitmap.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/LEGO1/mxbitmap.cpp b/LEGO1/mxbitmap.cpp index 4e7e07de..38c0e710 100644 --- a/LEGO1/mxbitmap.cpp +++ b/LEGO1/mxbitmap.cpp @@ -375,15 +375,16 @@ MxResult MxBitmap::ImportColorsToPalette(RGBQUAD* p_rgbquad, MxPalette* p_palett if (p_palette) { if (p_palette->GetEntries(entries)) - return ret; + goto done; } else { MxPalette local_pal; if (local_pal.GetEntries(entries)) - return ret; + goto done; } - for (MxS32 i = 0; i < 256; i++) { + MxS32 i; + for (i = 0; i < 256; i++) { p_rgbquad[i].rgbRed = entries[i].peRed; p_rgbquad[i].rgbGreen = entries[i].peGreen; p_rgbquad[i].rgbBlue = entries[i].peBlue; @@ -391,5 +392,7 @@ MxResult MxBitmap::ImportColorsToPalette(RGBQUAD* p_rgbquad, MxPalette* p_palett } ret = SUCCESS; + +done: return ret; } From b46801a774728f25345ef4ede71b5b8b75937c8c Mon Sep 17 00:00:00 2001 From: MS Date: Wed, 6 Dec 2023 14:30:09 -0500 Subject: [PATCH 2/3] Read section info from PE header (#311) * Read section info from PE header * Remove the need for textraw and textvirt members * typo --- tools/isledecomp/isledecomp/bin.py | 201 ++++++++++++++++++++++--- tools/isledecomp/isledecomp/syminfo.py | 5 +- tools/reccmp/reccmp.py | 2 +- 3 files changed, 181 insertions(+), 27 deletions(-) diff --git a/tools/isledecomp/isledecomp/bin.py b/tools/isledecomp/isledecomp/bin.py index 38245ace..bee28444 100644 --- a/tools/isledecomp/isledecomp/bin.py +++ b/tools/isledecomp/isledecomp/bin.py @@ -1,47 +1,200 @@ import struct +from collections import namedtuple + + +class MZHeaderNotFoundError(Exception): + """MZ magic string not found at the start of the binary.""" + + +class PEHeaderNotFoundError(Exception): + """PE magic string not found at the offset given in 0x3c.""" + + +class SectionNotFoundError(KeyError): + """The specified section was not found in the file.""" + + +class InvalidVirtualAddressError(IndexError): + """The given virtual address is too high or low + to point to something in the binary file.""" + + +PEHeader = namedtuple( + "PEHeader", + [ + "Signature", + "Machine", + "NumberOfSections", + "TimeDateStamp", + "PointerToSymbolTable", # deprecated + "NumberOfSymbols", # deprecated + "SizeOfOptionalHeader", + "Characteristics", + ], +) + +ImageSectionHeader = namedtuple( + "ImageSectionHeader", + [ + "Name", + "Misc", + "VirtualAddress", + "SizeOfRawData", + "PointerToRawData", + "PointerToRelocations", + "PointerToLineNumbers", + "NumberOfRelocations", + "NumberOfLineNumbers", + "Characteristics", + ], +) + + +def section_name_match(section, name): + return section.Name == struct.pack("8s", name.encode("ascii")) + + +def section_contains_vaddr(section, imagebase, vaddr) -> bool: + debased = vaddr - imagebase + ofs = debased - section.VirtualAddress + return 0 <= ofs < section.SizeOfRawData -# Declare a class that can automatically convert virtual executable addresses -# to file addresses class Bin: - def __init__(self, filename, logger): + """Parses a PE format EXE and allows reading data from a virtual address. + Reference: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format""" + + def __init__(self, filename, logger=None): self.logger = logger - self.logger.debug('Parsing headers of "%s"... ', filename) + self._debuglog(f'Parsing headers of "{filename}"... ') self.filename = filename self.file = None self.imagebase = None - self.textvirt = None - self.textraw = None + self.sections = [] + self.last_section = None def __enter__(self): - self.logger.debug(f"Bin {self.filename} Enter") + self._debuglog(f"Bin {self.filename} Enter") self.file = open(self.filename, "rb") - # HACK: Strictly, we should be parsing the header, but we know where - # everything is in these two files so we just jump straight there + (mz_str,) = struct.unpack("2s", self.file.read(2)) + if mz_str != b"MZ": + raise MZHeaderNotFoundError - # Read ImageBase - self.file.seek(0xB4) - (self.imagebase,) = struct.unpack(" int: + """The symbols output from cvdump gives addresses in this format: AAAA.BBBBBBBB + where A is the index (1-based) into the section table and B is the local offset. + This will return the virtual address for the start of the section at the given index + so you can get the virtual address for whatever symbol you are looking at. + """ + + section = self.sections[index - 1] + return self.imagebase + section.VirtualAddress + + def get_section_offset_by_name(self, name) -> int: + """Same as above, but use the section name as the lookup""" + + section = self._get_section_by_name(name) + return self.imagebase + section.VirtualAddress + + def get_raw_addr(self, vaddr) -> int: + """Returns the raw offset in the PE binary for the given virtual address.""" + self._set_section_for_vaddr(vaddr) + return ( + vaddr + - self.imagebase + - self.last_section.VirtualAddress + + self.last_section.PointerToRawData + ) + + def is_valid_vaddr(self, vaddr) -> bool: + """Does this virtual address point to anything in the exe?""" + section = next( + filter( + lambda section: section_contains_vaddr(section, self.imagebase, vaddr), + self.sections, + ), + None, + ) + + return section is not None def read(self, offset, size): - self.file.seek(self.get_addr(offset)) - return self.file.read(size) + self._set_section_for_vaddr(offset) + + raw_addr = self.get_raw_addr(offset) + self.file.seek(raw_addr) + + # Clamp the read within the extent of the current section. + # Reading off the end will most likely misrepresent the virtual addressing. + _size = min( + size, + self.last_section.PointerToRawData + + self.last_section.SizeOfRawData + - raw_addr, + ) + return self.file.read(_size) diff --git a/tools/isledecomp/isledecomp/syminfo.py b/tools/isledecomp/isledecomp/syminfo.py index a14bbbe2..6a26e30f 100644 --- a/tools/isledecomp/isledecomp/syminfo.py +++ b/tools/isledecomp/isledecomp/syminfo.py @@ -40,11 +40,12 @@ def __init__(self, pdb, sym_recompfile, sym_logger, sym_wine_path_converter=None current_section = line[4:] if current_section == "SYMBOLS" and "S_GPROC32" in line: + sym_section = int(line[21:25], 16) sym_addr = int(line[26:34], 16) info = RecompiledInfo() - info.addr = ( - sym_addr + sym_recompfile.imagebase + sym_recompfile.textvirt + info.addr = sym_addr + sym_recompfile.get_section_offset_by_index( + sym_section ) use_dbg_offs = False diff --git a/tools/reccmp/reccmp.py b/tools/reccmp/reccmp.py index f7659703..d2a7f23a 100755 --- a/tools/reccmp/reccmp.py +++ b/tools/reccmp/reccmp.py @@ -86,7 +86,7 @@ def filter_out_ptr(ptype, op_str): for i, word in enumerate(words): try: inttest = int(word, 16) - if inttest >= file.imagebase + file.textvirt: + if inttest >= file.get_section_offset_by_index(1): words[i] = placeholder_generator.get(inttest) except ValueError: pass From 8bf0bde6b988817cd80d7c38034aa0006b75e889 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Thu, 7 Dec 2023 07:06:44 -0500 Subject: [PATCH 3/3] Bootstrap MxCompositePresenter (#310) * Bootstrap MxCompositePresenter * Remove MxUnkList * Use TickleState_Idle * Add all annotations * Add vtable * Add Notify * Update mxcompositepresenter.h * Remove extra TEMPLATE * Update mxcompositepresenter.cpp --- LEGO1/mxcompositepresenter.cpp | 74 +++++++++++++++++++++++++++++++++- LEGO1/mxcompositepresenter.h | 23 +++++++---- LEGO1/mxunklist.h | 38 ----------------- 3 files changed, 87 insertions(+), 48 deletions(-) delete mode 100644 LEGO1/mxunklist.h diff --git a/LEGO1/mxcompositepresenter.cpp b/LEGO1/mxcompositepresenter.cpp index acc4002e..d21013d4 100644 --- a/LEGO1/mxcompositepresenter.cpp +++ b/LEGO1/mxcompositepresenter.cpp @@ -19,12 +19,50 @@ MxCompositePresenter::MxCompositePresenter() NotificationManager()->Register(this); } +// TEMPLATE: LEGO1 0x100b61a0 +// list >::~list > + +// FUNCTION: LEGO1 0x100b6210 +// MxCompositePresenter::ClassName + +// FUNCTION: LEGO1 0x100b6220 +// MxCompositePresenter::IsA + +// SYNTHETIC: LEGO1 0x100b62d0 +// MxCompositePresenter::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100b62f0 +// MxCompositePresenterList::~MxCompositePresenterList + +// TEMPLATE: LEGO1 0x100b6340 +// List::~List + // FUNCTION: LEGO1 0x100b6390 MxCompositePresenter::~MxCompositePresenter() { NotificationManager()->Unregister(this); } +// STUB: LEGO1 0x100b6410 +MxResult MxCompositePresenter::StartAction(MxStreamController*, MxDSAction*) +{ + // TODO + return SUCCESS; +} + +// STUB: LEGO1 0x100b65e0 +void MxCompositePresenter::EndAction() +{ + // TODO +} + +// STUB: LEGO1 0x100b6760 +MxLong MxCompositePresenter::Notify(MxParam& p) +{ + // TODO + return 0; +} + // STUB: LEGO1 0x100b67f0 void MxCompositePresenter::VTable0x58() { @@ -37,8 +75,40 @@ void MxCompositePresenter::VTable0x5c() // TODO } -// STUB: LEGO1 0x100b6b40 -void MxCompositePresenter::VTable0x60(undefined4 p_unknown) +// FUNCTION: LEGO1 0x100b6b40 +void MxCompositePresenter::VTable0x60(MxPresenter* p_presenter) +{ + for (MxCompositePresenterList::iterator it = m_list.begin(); it != m_list.end(); it++) { + if (*it == p_presenter) { + if (++it == m_list.end()) { + if (m_compositePresenter) + m_compositePresenter->VTable0x60(this); + } + else if (m_action->IsA("MxDSSerialAction")) { + MxPresenter* presenter = *it; + if (presenter->GetCurrentTickleState() == TickleState_Idle) + presenter->SetTickleState(TickleState_Ready); + } + return; + } + } +} + +// STUB: LEGO1 0x100b6bc0 +void MxCompositePresenter::SetTickleState(TickleState p_tickleState) { // TODO } + +// STUB: LEGO1 0x100b6c30 +void MxCompositePresenter::Enable(MxBool p_enable) +{ + // TODO +} + +// STUB: LEGO1 0x100b6c80 +MxBool MxCompositePresenter::HasTickleStatePassed(TickleState p_tickleState) +{ + // TODO + return TRUE; +} diff --git a/LEGO1/mxcompositepresenter.h b/LEGO1/mxcompositepresenter.h index 2aee103f..20b3c347 100644 --- a/LEGO1/mxcompositepresenter.h +++ b/LEGO1/mxcompositepresenter.h @@ -1,8 +1,10 @@ #ifndef MXCOMPOSITEPRESENTER_H #define MXCOMPOSITEPRESENTER_H +#include "compat.h" // STL #include "mxpresenter.h" -#include "mxunklist.h" + +class MxCompositePresenterList : public list {}; // VTABLE: LEGO1 0x100dc618 // SIZE 0x4c @@ -11,26 +13,31 @@ class MxCompositePresenter : public MxPresenter { MxCompositePresenter(); virtual ~MxCompositePresenter() override; // vtable+0x0 - // FUNCTION: LEGO1 0x100b6210 + virtual MxLong Notify(MxParam& p) override; // vtable+0x04 + inline virtual const char* ClassName() const override // vtable+0x0c { // GLOBAL: LEGO1 0x100f0774 return "MxCompositePresenter"; } - // FUNCTION: LEGO1 0x100b6220 inline virtual MxBool IsA(const char* name) const override // vtable+0x10 { return !strcmp(name, MxCompositePresenter::ClassName()) || MxPresenter::IsA(name); } - virtual void VTable0x58(); - virtual void VTable0x5c(); - virtual void VTable0x60(undefined4 p_unknown); - virtual MxBool VTable0x64(undefined4 p_unknown); + virtual MxResult StartAction(MxStreamController*, MxDSAction*) override; // vtable+0x3c + virtual void EndAction() override; // vtable+0x40 + virtual void SetTickleState(TickleState p_tickleState) override; // vtable+0x44 + virtual MxBool HasTickleStatePassed(TickleState p_tickleState) override; // vtable+0x48 + virtual void Enable(MxBool p_enable) override; // vtable+0x54 + virtual void VTable0x58(); // vtable+0x58 + virtual void VTable0x5c(); // vtable+0x5c + virtual void VTable0x60(MxPresenter* p_presenter); // vtable+0x60 + virtual MxBool VTable0x64(undefined4 p_unknown); // vtable+0x64 private: - MxUnkList m_list; + MxCompositePresenterList m_list; // 0x40 }; #endif // MXCOMPOSITEPRESENTER_H diff --git a/LEGO1/mxunklist.h b/LEGO1/mxunklist.h deleted file mode 100644 index cdbdeb13..00000000 --- a/LEGO1/mxunklist.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef MXUNKLIST_H -#define MXUNKLIST_H - -#include "decomp.h" -#include "mxtypes.h" - -/* - * This is an as-of-yet unknown list-like data structure. - * The class hierarchy/structure isn't quite correct yet. - */ - -struct MxUnkListNode { - MxUnkListNode* m_unk00; - MxUnkListNode* m_unk04; - undefined4 m_unk08; -}; - -class MxUnkList { -public: - inline MxUnkList() - { - undefined unk; - this->m_unk00 = unk; - - MxUnkListNode* node = new MxUnkListNode(); - node->m_unk00 = node; - node->m_unk04 = node; - - this->m_head = node; - this->m_count = 0; - } - - undefined m_unk00; - MxUnkListNode* m_head; - MxU32 m_count; -}; - -#endif // MXUNKLIST_H