From 8bf0bde6b988817cd80d7c38034aa0006b75e889 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Thu, 7 Dec 2023 07:06:44 -0500 Subject: [PATCH 1/7] 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 From 260772e374cb6435a1328503dc1b98fed31325e9 Mon Sep 17 00:00:00 2001 From: Mark Langen Date: Thu, 7 Dec 2023 04:10:42 -0800 Subject: [PATCH 2/7] Bootstrap decomp of Tgl rendering library (#293) * Bootstrap decomp of D3DRM rendering code * This PR kicks off work on decompiling the D3D Retained Mode (D3DRM) rendering part of the codebase. * High level overview: * There is a base IMxDirect3DRMObject class which all of the D3DRM rendering objects inherit from. Its only virtual method is one to get the underlying object handle. * A hierarchy of abstract classes inherits from this base class, which I've called "IMxDirect3DRM". These classes only have pure virtual methods on them and don't contain any data. * Each one of the abstract classes has exactly one concrete implementation, which I've called "MxDirect3DRM". These classes have exactly one piece of data, which is a pointer to the underlying D3D Retained Mode object. * If the classes need to store additional data, they store it in a userdata blob which is attached to the D3DRM object rather than the additional data being stored in the class itself. * I've worked out about twice this many classes related to D3DRM rendering so far but the PR was getting large enough as is, so I'm cutting it here for now. * I decomped sufficiently many methods of these classe to convince myself that the above observations are correct. About 60% of the decomped methods here are perfect matches, including at least one non-trivial method per class. * Formatting * Restructure changes using Tgl naming / details * Restructure the changes to use the naming that we know from Tgl. * Fill in some parts of the implementation I couldn't initially figure out using the details from Tgl (got more 100% matches). * Move d3drm link requirement * Fixups FloatMatrix -> FloatMatrix4 * Fix order * Full fix for ordering problems * Put back accidentally removed include. * Fix call which should have been Release * Use new and delete for DeepClone * Missing Tgl:: on CreateRenderer * Revert change to bool return value. * Rename Something -> Unk * Return paramter naming convention to what Tgl used * Add scalar ddtor to verify inline destructor * Fix order * Change malloc/free -> new/delete in Tgl * Remove duplicate destructor. * Check all inline destructors * Fix dtor comments * Third time's the charm * Alphabetical sort * Decomp adjustments * Add d3drm files to clang-format --------- Co-authored-by: Christian Semmler --- .github/workflows/format.yml | 1 + CMakeLists.txt | 11 +- LEGO1/tgl/d3drm/camera.cpp | 33 +++ LEGO1/tgl/d3drm/device.cpp | 85 +++++++ LEGO1/tgl/d3drm/group.cpp | 122 +++++++++ LEGO1/tgl/d3drm/impl.h | 467 ++++++++++++++++++++++++++++++++++ LEGO1/tgl/d3drm/light.cpp | 33 +++ LEGO1/tgl/d3drm/mesh.cpp | 158 ++++++++++++ LEGO1/tgl/d3drm/renderer.cpp | 332 ++++++++++++++++++++++++ LEGO1/tgl/d3drm/texture.cpp | 196 ++++++++++++++ LEGO1/tgl/d3drm/unk.cpp | 57 +++++ LEGO1/tgl/d3drm/view.cpp | 349 +++++++++++++++++++++++++ LEGO1/tgl/tgl.h | 303 +++++++++------------- LEGO1/tgl/tglvector.h | 12 +- LEGO1/viewmanager/viewroi.cpp | 3 +- 15 files changed, 1976 insertions(+), 186 deletions(-) create mode 100644 LEGO1/tgl/d3drm/camera.cpp create mode 100644 LEGO1/tgl/d3drm/device.cpp create mode 100644 LEGO1/tgl/d3drm/group.cpp create mode 100644 LEGO1/tgl/d3drm/impl.h create mode 100644 LEGO1/tgl/d3drm/light.cpp create mode 100644 LEGO1/tgl/d3drm/mesh.cpp create mode 100644 LEGO1/tgl/d3drm/renderer.cpp create mode 100644 LEGO1/tgl/d3drm/texture.cpp create mode 100644 LEGO1/tgl/d3drm/unk.cpp create mode 100644 LEGO1/tgl/d3drm/view.cpp diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 16ad85ae..2eab4970 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -19,6 +19,7 @@ jobs: LEGO1/*.cpp LEGO1/*.h \ LEGO1/realtime/*.cpp LEGO1/realtime/*.h \ LEGO1/tgl/*.h \ + LEGO1/tgl/d3drm/*.cpp LEGO1/tgl/d3drm/*.h \ LEGO1/viewmanager/*.cpp LEGO1/viewmanager/*.h python-format: diff --git a/CMakeLists.txt b/CMakeLists.txt index f6ff00b0..e1fa4db2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,6 +210,15 @@ add_library(lego1 SHARED LEGO1/score.cpp LEGO1/scorestate.cpp LEGO1/skateboard.cpp + LEGO1/tgl/d3drm/camera.cpp + LEGO1/tgl/d3drm/device.cpp + LEGO1/tgl/d3drm/group.cpp + LEGO1/tgl/d3drm/light.cpp + LEGO1/tgl/d3drm/mesh.cpp + LEGO1/tgl/d3drm/renderer.cpp + LEGO1/tgl/d3drm/texture.cpp + LEGO1/tgl/d3drm/unk.cpp + LEGO1/tgl/d3drm/view.cpp LEGO1/towtrack.cpp LEGO1/towtrackmissionstate.cpp LEGO1/viewmanager/viewmanager.cpp @@ -241,7 +250,7 @@ if (ISLE_USE_DX5) endif() # Link libraries -target_link_libraries(lego1 PRIVATE ddraw dsound dxguid dinput winmm) +target_link_libraries(lego1 PRIVATE ddraw dsound dxguid dinput winmm d3drm) # Make sure filenames are ALL CAPS set_property(TARGET lego1 PROPERTY OUTPUT_NAME LEGO1) diff --git a/LEGO1/tgl/d3drm/camera.cpp b/LEGO1/tgl/d3drm/camera.cpp new file mode 100644 index 00000000..08aea939 --- /dev/null +++ b/LEGO1/tgl/d3drm/camera.cpp @@ -0,0 +1,33 @@ +#include "impl.h" + +using namespace TglImpl; + +DECOMP_SIZE_ASSERT(Camera, 0x4); +DECOMP_SIZE_ASSERT(CameraImpl, 0x8); + +// SYNTHETIC: LEGO1 0x100a2560 +// TglImpl::CameraImpl::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100a36f0 +void* CameraImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} + +// FUNCTION: LEGO1 0x100a3700 +Result CameraImpl::SetTransformation(const FloatMatrix4& matrix) +{ + D3DRMMATRIX4D helper; + D3DRMMATRIX4D* pTransformation = Translate(matrix, helper); + + D3DVECTOR position; + Result result; + Result result2; + + result2 = ResultVal(m_data->GetPosition(0, &position)); + result = ResultVal(m_data->AddTransform(D3DRMCOMBINE_REPLACE, *pTransformation)); + // The did this second call just to assert on the return value + result2 = ResultVal(m_data->GetPosition(0, &position)); + + return result; +} diff --git a/LEGO1/tgl/d3drm/device.cpp b/LEGO1/tgl/d3drm/device.cpp new file mode 100644 index 00000000..44307268 --- /dev/null +++ b/LEGO1/tgl/d3drm/device.cpp @@ -0,0 +1,85 @@ +#include "impl.h" + +#include + +using namespace TglImpl; + +// SYNTHETIC: LEGO1 0x100a22c0 +// TglImpl::DeviceImpl::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100a2bf0 +void* DeviceImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} + +// FUNCTION: LEGO1 0x100a2c00 +unsigned long DeviceImpl::GetWidth() +{ + return m_data->GetWidth(); +} + +// FUNCTION: LEGO1 0x100a2c10 +unsigned long DeviceImpl::GetHeight() +{ + return m_data->GetHeight(); +} + +// FUNCTION: LEGO1 0x100a2c20 +Result DeviceImpl::SetColorModel(ColorModel) +{ + return Success; +} + +// FUNCTION: LEGO1 0x100a2c30 +Result DeviceImpl::SetShadingModel(ShadingModel model) +{ + // Doesn't match well even though we know this is exactly + // the original code thanks to the jump table. + D3DRMRENDERQUALITY renderQuality = Translate(model); + return ResultVal(m_data->SetQuality(renderQuality)); +} + +// FUNCTION: LEGO1 0x100a2ca0 +Result DeviceImpl::SetShadeCount(unsigned long shadeCount) +{ + return ResultVal(m_data->SetShades(shadeCount)); +} + +// FUNCTION: LEGO1 0x100a2cc0 +Result DeviceImpl::SetDither(int dither) +{ + return ResultVal(m_data->SetDither(dither)); +} + +// Probably wrong, not sure what's going on in this method. +// FUNCTION: LEGO1 0x100a2ce0 +void DeviceImpl::InitFromD3DDevice(Device*) +{ + // Device argument is intentionally unused. + IDirect3DRMWinDevice* winDevice; + if (ResultVal(m_data->QueryInterface(IID_IDirect3DRMWinDevice, (LPVOID*) &winDevice))) { + m_data->InitFromD3D((LPDIRECT3D) &winDevice, (LPDIRECT3DDEVICE) m_data); + winDevice->Release(); + } +} + +// Really don't know what's going on here. Seems it will call down to Init +// but the decomp suggests it otherwise looks the same as InitFromD3D but Init +// takes widly different parameters. +// FUNCTION: LEGO1 0x100a2d20 +void DeviceImpl::InitFromWindowsDevice(Device*) +{ + // Device argument is intentionally unused. + IDirect3DRMWinDevice* winDevice; + if (SUCCEEDED(m_data->QueryInterface(IID_IDirect3DRMWinDevice, (LPVOID*) &winDevice))) { + // m_data->Init(??); + winDevice->Release(); + } +} + +// FUNCTION: LEGO1 0x100a2d60 +Result DeviceImpl::Update() +{ + return ResultVal(m_data->Update()); +} diff --git a/LEGO1/tgl/d3drm/group.cpp b/LEGO1/tgl/d3drm/group.cpp new file mode 100644 index 00000000..4e6bed96 --- /dev/null +++ b/LEGO1/tgl/d3drm/group.cpp @@ -0,0 +1,122 @@ +#include "impl.h" + +using namespace TglImpl; + +// SYNTHETIC: LEGO1 0x100a2480 +// TglImpl::GroupImpl::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100a31d0 +void* GroupImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} + +// FUNCTION: LEGO1 0x100a31e0 +Result GroupImpl::SetTransformation(const FloatMatrix4& matrix) +{ + D3DRMMATRIX4D helper; + D3DRMMATRIX4D* d3dMatrix = Translate(matrix, helper); + return ResultVal(m_data->AddTransform(D3DRMCOMBINE_REPLACE, *d3dMatrix)); +} + +// FUNCTION: LEGO1 0x100a3240 +Result GroupImpl::SetColor(float r, float g, float b, float a) +{ + // The first instruction makes no sense here: + // cmp dword ptr [esp + 0x10], 0 + // This compares a, which we know is a float because it immediately + // gets passed into D3DRMCreateColorRGBA, but does the comparison + // as though it's an int?? + if (*reinterpret_cast(&a) > 0) { + D3DCOLOR color = D3DRMCreateColorRGBA(r, g, b, a); + return ResultVal(m_data->SetColor(color)); + } + else { + return ResultVal(m_data->SetColorRGB(r, a, b)); + } +} + +// FUNCTION: LEGO1 0x100a32b0 +Result GroupImpl::SetTexture(const Texture* pTexture) +{ + IDirect3DRMTexture* pD3DTexture = pTexture ? static_cast(pTexture)->ImplementationData() : NULL; + return ResultVal(m_data->SetTexture(pD3DTexture)); +} + +// FUNCTION: LEGO1 0x100a32e0 +Result GroupImpl::GetTexture(Texture*& pTexture) +{ + IDirect3DRMTexture* pD3DTexture; + TextureImpl* holder = new TextureImpl(); + Result result = ResultVal(m_data->GetTexture(&pD3DTexture)); + if (result) { + // Seems to actually call the first virtual method of holder here + // but that doesn't make any sense since it passes three arguments + // to the method (self + string constant? + an offset?). + + // This line makes the start of the function match and is what I + // would expect to see there but it clearly isn't what's actually + // there. + holder->SetImplementation(pD3DTexture); + } + pTexture = holder; + return Success; +} + +// FUNCTION: LEGO1 0x100a33c0 +Result GroupImpl::SetMaterialMode(MaterialMode mode) +{ + D3DRMMATERIALMODE d3dMode; + switch (mode) { + case FromParent: + d3dMode = D3DRMMATERIAL_FROMPARENT; + break; + case FromFrame: + d3dMode = D3DRMMATERIAL_FROMFRAME; + break; + case FromMesh: + d3dMode = D3DRMMATERIAL_FROMMESH; + break; + } + return ResultVal(m_data->SetMaterialMode(d3dMode)); +} + +// FUNCTION: LEGO1 0x100a3410 +Result GroupImpl::Add(const Mesh* pMesh) +{ + const MeshImpl* pMeshImpl = static_cast(pMesh); + return ResultVal(m_data->AddVisual(pMeshImpl->ImplementationData()->groupMesh)); +} + +// FUNCTION: LEGO1 0x100a3430 +Result GroupImpl::Add(const Group* pGroup) +{ + const GroupImpl* pGroupImpl = static_cast(pGroup); + return ResultVal(m_data->AddVisual(pGroupImpl->m_data)); +} + +// FUNCTION: LEGO1 0x100a3450 +Result GroupImpl::Remove(const Group* pGroup) +{ + const GroupImpl* pGroupImpl = static_cast(pGroup); + return ResultVal(m_data->DeleteVisual(pGroupImpl->m_data)); +} + +// FUNCTION: LEGO1 0x100a3480 +Result GroupImpl::Remove(const Mesh* pMesh) +{ + const MeshImpl* pMeshImpl = static_cast(pMesh); + return ResultVal(m_data->DeleteVisual(pMeshImpl->ImplementationData()->groupMesh)); +} + +// FUNCTION: LEGO1 0x100a34b0 STUB +Result GroupImpl::RemoveAll() +{ + return Error; +} + +// FUNCTION: LEGO1 0x100a34c0 STUB +Result GroupImpl::Unknown() +{ + return Error; +} diff --git a/LEGO1/tgl/d3drm/impl.h b/LEGO1/tgl/d3drm/impl.h new file mode 100644 index 00000000..bbc7baa0 --- /dev/null +++ b/LEGO1/tgl/d3drm/impl.h @@ -0,0 +1,467 @@ + +#include "../../decomp.h" +#include "../tgl.h" + +#include + +// Forward declare D3D types +struct IDirect3DRM; +struct IDirect3DRMDevice; +struct IDirect3DRMViewport; +struct IDirect3DRMFrame; +struct IDirect3DRMMesh; +struct IDirect3DRMMeshBuilder; +struct IDirect3DRMTexture; + +namespace TglImpl +{ + +using namespace Tgl; + +// Utility function used by implementations +inline Result ResultVal(HRESULT result) +{ + return SUCCEEDED(result) ? Success : Error; +} + +// Forward declare implementations +class RendererImpl; +class DeviceImpl; +class ViewImpl; +class LightImpl; +class CameraImpl; +class GroupImpl; +class MeshImpl; +class TextureImpl; +class UnkImpl; + +// VTABLE 0x100db910 +class RendererImpl : public Renderer { +public: + RendererImpl() : m_data(0) {} + ~RendererImpl() { Destroy(); }; + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual Device* CreateDevice(const DeviceDirect3DCreateData&); + virtual Device* CreateDevice(const DeviceDirectDrawCreateData&); + + // vtable+0x10 + virtual View* CreateView( + const Device*, + const Camera*, + unsigned long x, + unsigned long y, + unsigned long width, + unsigned long height + ); + virtual Camera* CreateCamera(); + virtual Light* CreateLight(LightType, float r, float g, float b); + virtual Group* CreateGroup(const Group* pParent); + + // vtable+0x20 + virtual Unk* CreateUnk(); + virtual Texture* CreateTexture(); + virtual Texture* CreateTexture( + int width, + int height, + int bitsPerTexel, + const void* pTexels, + int pTexelsArePersistent, + int paletteEntryCount, + const PaletteEntry* pEntries + ); + virtual Result SetTextureDefaultShadeCount(unsigned long); + + // vtable+0x30 + virtual Result SetTextureDefaultColorCount(unsigned long); + +public: + inline Result Create(); + inline void Destroy(); + +private: + IDirect3DRM* m_data; +}; + +// VTABLE 0x100db988 +class DeviceImpl : public Device { +public: + DeviceImpl() : m_data(0) {} + ~DeviceImpl() + { + if (m_data) { + m_data->Release(); + m_data = NULL; + } + } + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual unsigned long GetWidth(); + virtual unsigned long GetHeight(); + + // vtable+0x10 + virtual Result SetColorModel(ColorModel); + virtual Result SetShadingModel(ShadingModel); + virtual Result SetShadeCount(unsigned long); + virtual Result SetDither(int); + + // vtable+0x20 + virtual Result Update(); + virtual void InitFromD3DDevice(Device*); + virtual void InitFromWindowsDevice(Device*); + + inline IDirect3DRMDevice* ImplementationData() const { return m_data; } + + friend class RendererImpl; + +private: + IDirect3DRMDevice* m_data; +}; + +// VTABLE 0x100db9e8 +class ViewImpl : public View { +public: + ViewImpl() : m_data(0) {} + ~ViewImpl() + { + if (m_data) { + m_data->Release(); + m_data = NULL; + } + } + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual Result Add(const Light*); + virtual Result Remove(const Light*); + + // vtable+0x10 + virtual Result SetCamera(const Camera*); + virtual Result SetProjection(ProjectionType); + virtual Result SetFrustrum(float frontClippingDistance, float backClippingDistance, float degrees); + virtual Result SetBackgroundColor(float r, float g, float b); + + // vtable+0x20 + virtual Result GetBackgroundColor(float* r, float* g, float* b); + virtual Result Clear(); + virtual Result Render(const Light*); + virtual Result ForceUpdate(unsigned long x, unsigned long y, unsigned long width, unsigned long height); + + // vtable+0x30 + virtual Result TransformWorldToScreen(const float world[3], float screen[4]); + virtual Result TransformScreenToWorld(const float screen[4], float world[3]); + virtual Result Pick( + unsigned long x, + unsigned long y, + const Group** ppGroupsToPickFrom, + int groupsToPickFromCount, + const Group**& rppPickedGroups, + int& rPickedGroupCount + ); + + inline IDirect3DRMViewport* ImplementationData() const { return m_data; } + + static Result ViewportCreateAppData(IDirect3DRM*, IDirect3DRMViewport*, IDirect3DRMFrame*); + + friend class RendererImpl; + +private: + IDirect3DRMViewport* m_data; +}; + +// VTABLE 0x100dbad8 +class CameraImpl : public Camera { +public: + CameraImpl() : m_data(0) {} + ~CameraImpl() + { + if (m_data) { + m_data->Release(); + m_data = NULL; + } + } + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual Result SetTransformation(const FloatMatrix4&); + + inline IDirect3DRMFrame* ImplementationData() const { return m_data; } + + friend class RendererImpl; + +private: + IDirect3DRMFrame* m_data; +}; + +// VTABLE 0x100dbaf8 +class LightImpl : public Light { +public: + LightImpl() : m_data(0) {} + ~LightImpl() + { + if (m_data) { + m_data->Release(); + m_data = NULL; + } + } + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual Result SetTransformation(const FloatMatrix4&); + virtual Result SetColor(float r, float g, float b); + + inline IDirect3DRMFrame* ImplementationData() const { return m_data; } + + friend class RendererImpl; + +private: + IDirect3DRMFrame* m_data; +}; + +// VTABLE 0x100dbb88 +class MeshImpl : public Mesh { +public: + MeshImpl() : m_data(0) {} + ~MeshImpl() + { + if (m_data) { + delete m_data; + m_data = NULL; + } + } + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual Result SetColor(float r, float g, float b, float a); + virtual Result SetTexture(const Texture*); + + // vtable+0x10 + virtual Result GetTexture(Texture*&); + virtual Result SetTextureMappingMode(ProjectionType); + virtual Result SetShadingModel(ShadingModel); + virtual Mesh* DeepClone(Unk*); + + // vtable+0x20 + virtual Mesh* ShallowClone(Unk*); + + struct MeshData { + IDirect3DRMMesh* groupMesh; + int groupIndex; + }; + + inline MeshData* ImplementationData() const { return m_data; } + + friend class RendererImpl; + +private: + MeshData* m_data; +}; + +// VTABLE 0x100dba68 +class GroupImpl : public Group { +public: + GroupImpl() : m_data(0) {} + ~GroupImpl() + { + if (m_data) { + m_data->Release(); + m_data = NULL; + } + } + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual Result SetTransformation(const FloatMatrix4&); + virtual Result SetColor(float r, float g, float b, float a); + + // vtable+0x10 + virtual Result SetTexture(const Texture*); + virtual Result GetTexture(Texture*&); + virtual Result SetMaterialMode(MaterialMode); + virtual Result Add(const Group*); + + // vtable+0x20 + virtual Result Add(const Mesh*); + virtual Result Remove(const Group*); + virtual Result Remove(const Mesh*); + virtual Result RemoveAll(); + + // vtable+0x30 + virtual Result Unknown(); + + friend class RendererImpl; + +private: + IDirect3DRMFrame* m_data; +}; + +// VTABLE 0x100dbb18 +class UnkImpl : public Unk { +public: + UnkImpl() : m_data(0) {} + ~UnkImpl() + { + if (m_data) { + m_data->Release(); + m_data = NULL; + } + } + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual Result SetMeshData( + unsigned long faceCount, + unsigned long vertexCount, + const float (*pPositions)[3], + const float (*pNormals)[3], + const float (*pTextureCoordinates)[2], + unsigned long vertexPerFaceCount, + unsigned long* pFaceData + ); + virtual Result GetBoundingBox(float min[3], float max[3]); + + // vtable+0x10 + virtual Unk* Clone(); + + inline IDirect3DRMMesh* ImplementationData() const { return m_data; } + + friend class RendererImpl; + +private: + IDirect3DRMMesh* m_data; +}; + +// No vtable, this is just a simple wrapper around D3DRMIMAGE +class TglD3DRMIMAGE { +public: + TglD3DRMIMAGE( + int width, + int height, + int depth, + void* pBuffer, + int useBuffer, + int paletteSize, + PaletteEntry* pEntries + ); + ~TglD3DRMIMAGE() { Destroy(); } + + Result CreateBuffer(int width, int height, int depth, void* pBuffer, int useBuffer); + void Destroy(); + void FillRowsOfTexture(int y, int height, char* content); + Result InitializePalette(int paletteSize, PaletteEntry* pEntries); + + D3DRMIMAGE m_image; + int m_texelsAllocatedByClient; +}; + +// VTABLE 0x100dbb48 +class TextureImpl : public Texture { +public: + TextureImpl() : m_data(0) {} + ~TextureImpl() + { + if (m_data) { + m_data->Release(); + m_data = NULL; + } + } + + virtual void* ImplementationDataPtr(); + + // vtable+0x08 + virtual Result SetTexels(int width, int height, int bitsPerTexel, void* pTexels); + virtual void FillRowsOfTexture(int y, int height, void* pBuffer); + + // vtable+0x10 + virtual Result Changed(int texelsChanged, int paletteChanged); + virtual Result GetBufferAndPalette( + int* pWidth, + int* pHeight, + int* pDepth, + void** ppBuffer, + int* ppPaletteSize, + PaletteEntry** ppPalette + ); + virtual Result SetPalette(int entryCount, PaletteEntry* entries); + + inline IDirect3DRMTexture* ImplementationData() const { return m_data; } + inline void SetImplementation(IDirect3DRMTexture* pData) { m_data = pData; } + + friend class RendererImpl; + + static Result SetImage(IDirect3DRMTexture* pSelf, TglD3DRMIMAGE* pImage); + +private: + IDirect3DRMTexture* m_data; +}; + +// Translation helpers +inline D3DRMRENDERQUALITY Translate(ShadingModel tglShadingModel) +{ + D3DRMRENDERQUALITY renderQuality; + + switch (tglShadingModel) { + case Wireframe: + renderQuality = D3DRMRENDER_WIREFRAME; + break; + case UnlitFlat: + renderQuality = D3DRMRENDER_UNLITFLAT; + break; + case Flat: + renderQuality = D3DRMRENDER_FLAT; + break; + case Gouraud: + renderQuality = D3DRMRENDER_GOURAUD; + break; + case Phong: + renderQuality = D3DRMRENDER_PHONG; + break; + default: + renderQuality = D3DRMRENDER_FLAT; + break; + } + + return renderQuality; +} + +inline D3DRMPROJECTIONTYPE Translate(ProjectionType tglProjectionType) +{ + D3DRMPROJECTIONTYPE projectionType; + switch (tglProjectionType) { + case Perspective: + projectionType = D3DRMPROJECT_PERSPECTIVE; + break; + case Orthographic: + projectionType = D3DRMPROJECT_ORTHOGRAPHIC; + break; + default: + projectionType = D3DRMPROJECT_PERSPECTIVE; + break; + } + return projectionType; +} + +// Yes this function serves no purpose, originally they intended it to +// convert from doubles to floats but ended up using floats throughout +// the software stack. +inline D3DRMMATRIX4D* Translate(const FloatMatrix4& tglMatrix4x4, D3DRMMATRIX4D& rD3DRMMatrix4x4) +{ + for (int i = 0; i < (sizeof(rD3DRMMatrix4x4) / sizeof(rD3DRMMatrix4x4[0])); i++) { + for (int j = 0; j < (sizeof(rD3DRMMatrix4x4[0]) / sizeof(rD3DRMMatrix4x4[0][0])); j++) { + rD3DRMMatrix4x4[i][j] = D3DVAL(tglMatrix4x4[i][j]); + } + } + return &rD3DRMMatrix4x4; +} + +} /* namespace TglImpl */ diff --git a/LEGO1/tgl/d3drm/light.cpp b/LEGO1/tgl/d3drm/light.cpp new file mode 100644 index 00000000..2f62c4e6 --- /dev/null +++ b/LEGO1/tgl/d3drm/light.cpp @@ -0,0 +1,33 @@ +#include "impl.h" + +using namespace TglImpl; + +DECOMP_SIZE_ASSERT(Light, 0x4); +DECOMP_SIZE_ASSERT(LightImpl, 0x8); + +// SYNTHETIC: LEGO1 0x100a2640 +// TglImpl::LightImpl::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100a3770 +void* LightImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} + +// FUNCTION: LEGO1 0x100a3780 +Result LightImpl::SetTransformation(const FloatMatrix4& matrix) +{ + D3DRMMATRIX4D helper; + D3DRMMATRIX4D* d3dMatrix = Translate(matrix, helper); + return ResultVal(m_data->AddTransform(D3DRMCOMBINE_REPLACE, *d3dMatrix)); +} + +// FUNCTION: LEGO1 0x100a37e0 +Result LightImpl::SetColor(float r, float g, float b) +{ + IDirect3DRMLightArray* lightArray; + IDirect3DRMLight* light; + m_data->GetLights(&lightArray); + lightArray->GetElement(0, &light); + return ResultVal(light->SetColorRGB(r, g, b)); +} diff --git a/LEGO1/tgl/d3drm/mesh.cpp b/LEGO1/tgl/d3drm/mesh.cpp new file mode 100644 index 00000000..d29ff2b3 --- /dev/null +++ b/LEGO1/tgl/d3drm/mesh.cpp @@ -0,0 +1,158 @@ +#include "impl.h" + +using namespace TglImpl; + +DECOMP_SIZE_ASSERT(D3DRMVERTEX, 0x24); + +DECOMP_SIZE_ASSERT(Mesh, 0x4); +DECOMP_SIZE_ASSERT(MeshImpl, 0x8); + +// SYNTHETIC: LEGO1 0x100a3d80 +// TglImpl::MeshImpl::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100a3ed0 +void* MeshImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} + +// FUNCTION: LEGO1 0x100a3ee0 +Result MeshImpl::SetColor(float r, float g, float b, float a) +{ + // The first instruction makes no sense here: + // cmp dword ptr [esp + 0x10], 0 + // This compares a, which we know is a float because it immediately + // gets passed into D3DRMCreateColorRGBA, but does the comparison + // as though it's an int?? + if (*reinterpret_cast(&a) > 0) { + D3DCOLOR color = D3DRMCreateColorRGBA(r, g, b, a); + return ResultVal(m_data->groupMesh->SetGroupColor(m_data->groupIndex, color)); + } + else { + return ResultVal(m_data->groupMesh->SetGroupColorRGB(m_data->groupIndex, r, g, b)); + } +} + +// FUNCTION: LEGO1 0x100a3f50 +Result MeshImpl::SetTexture(const Texture* pTexture) +{ + IDirect3DRMTexture* texture = pTexture ? static_cast(pTexture)->ImplementationData() : NULL; + return ResultVal(m_data->groupMesh->SetGroupTexture(m_data->groupIndex, texture)); +} + +// FUNCTION: LEGO1 0x100a3f80 +Result MeshImpl::SetTextureMappingMode(ProjectionType projType) +{ + if (projType == Perspective) { + return ResultVal(m_data->groupMesh->SetGroupMapping(m_data->groupIndex, D3DRMMAP_PERSPCORRECT)); + } + else { + return ResultVal(m_data->groupMesh->SetGroupMapping(m_data->groupIndex, 0)); + } +} + +// FUNCTION: LEGO1 0x100a3fc0 +Result MeshImpl::SetShadingModel(ShadingModel model) +{ + D3DRMRENDERQUALITY mode; + switch (model) { + case Wireframe: + mode = D3DRMRENDER_WIREFRAME; + break; + case UnlitFlat: + mode = D3DRMRENDER_UNLITFLAT; + break; + case Flat: + mode = D3DRMRENDER_FLAT; + break; + case Gouraud: + mode = D3DRMRENDER_GOURAUD; + break; + case Phong: + mode = D3DRMRENDER_PHONG; + break; + } + return ResultVal(m_data->groupMesh->SetGroupQuality(m_data->groupIndex, mode)); +} + +// FUNCTION: LEGO1 0x100a4030 +Mesh* MeshImpl::DeepClone(Unk* pUnk) +{ + // Create group + MeshImpl* newMesh = new MeshImpl(); + MeshData* data = new MeshData(); + newMesh->m_data = data; + + // Query information from old group + DWORD dataSize; + unsigned int vcount, fcount, vperface; + m_data->groupMesh->GetGroup(m_data->groupIndex, &vcount, &fcount, &vperface, &dataSize, NULL); + unsigned int* faceBuffer = new unsigned int[dataSize]; + m_data->groupMesh->GetGroup(m_data->groupIndex, &vcount, &fcount, &vperface, &dataSize, faceBuffer); + // We expect vertex to be sized 0x24, checked at start of file. + D3DRMVERTEX* vertexBuffer = new D3DRMVERTEX[vcount]; + m_data->groupMesh->GetVertices(m_data->groupIndex, 0, vcount, vertexBuffer); + LPDIRECT3DRMTEXTURE textureRef; + m_data->groupMesh->GetGroupTexture(m_data->groupIndex, &textureRef); + D3DRMMAPPING mapping = m_data->groupMesh->GetGroupMapping(m_data->groupIndex); + D3DRMRENDERQUALITY quality = m_data->groupMesh->GetGroupQuality(m_data->groupIndex); + D3DCOLOR color = m_data->groupMesh->GetGroupColor(m_data->groupIndex); + + // Push information to new group + UnkImpl* target = static_cast(pUnk); + D3DRMGROUPINDEX index; + target->ImplementationData()->AddGroup(vcount, fcount, vperface, faceBuffer, &index); + newMesh->m_data->groupIndex = index; + target->ImplementationData()->SetVertices(index, 0, vcount, vertexBuffer); + target->ImplementationData()->SetGroupTexture(index, textureRef); + target->ImplementationData()->SetGroupMapping(index, mapping); + target->ImplementationData()->SetGroupQuality(index, quality); + Result result = ResultVal(target->ImplementationData()->SetGroupColor(index, color)); + + // Cleanup + delete[] faceBuffer; + delete[] vertexBuffer; + if (result == Error) { + delete newMesh; + newMesh = NULL; + } + + return newMesh; +} + +// FUNCTION: LEGO1 0x100a4240 +Mesh* MeshImpl::ShallowClone(Unk* pUnk) +{ + MeshImpl* newGroup = new MeshImpl(); + MeshData* newData = new MeshData(); + newGroup->m_data = newData; + if (newData) { + newData->groupIndex = m_data->groupIndex; + newData->groupMesh = static_cast(pUnk)->ImplementationData(); + } + else { + delete newGroup; + newGroup = NULL; + } + return newGroup; +} + +// FUNCTION: LEGO1 0x100a4330 +Result MeshImpl::GetTexture(Texture*& rpTexture) +{ + IDirect3DRMTexture* texture; + TextureImpl* holder = new TextureImpl(); + Result result = ResultVal(m_data->groupMesh->GetGroupTexture(m_data->groupIndex, &texture)); + if (result) { + // Seems to actually call the first virtual method of holder here + // but that doesn't make any sense since it passes three arguments + // to the method (self + string constant? + an offset?). + + // This line makes the start of the function match and is what I + // would expect to see there but it clearly isn't what's actually + // there. + holder->SetImplementation(texture); + } + rpTexture = holder; + return Success; +} diff --git a/LEGO1/tgl/d3drm/renderer.cpp b/LEGO1/tgl/d3drm/renderer.cpp new file mode 100644 index 00000000..92d80d83 --- /dev/null +++ b/LEGO1/tgl/d3drm/renderer.cpp @@ -0,0 +1,332 @@ +#include "impl.h" + +using namespace TglImpl; + +// FUNCTION: LEGO1 0x100a15e0 +Renderer* Tgl::CreateRenderer() +{ + RendererImpl* renderer = new RendererImpl(); + if (!renderer->Create()) { + delete renderer; + renderer = NULL; + } + return renderer; +} + +// SYNTHETIC: LEGO1 0x100a16d0 +// TglImpl::RendererImpl::`scalar deleting destructor' + +// GLOBAL: LEGO1 0x1010103c +IDirect3DRM* g_pD3DRM = NULL; + +// Inlined only +Result RendererImpl::Create() +{ + if (g_pD3DRM) { + g_pD3DRM->AddRef(); + } + else { + LPDIRECT3DRM handle; + Direct3DRMCreate(&handle); + handle->QueryInterface(IID_IDirect3DRM2, (LPVOID*) &g_pD3DRM); + } + m_data = g_pD3DRM; + return (m_data != NULL) ? Success : Error; +} + +inline void RendererDestroy(IDirect3DRM* pRenderer) +{ + int refCount = pRenderer->Release(); + if (refCount <= 0) { + g_pD3DRM = NULL; + } +} + +// Inlined only +void RendererImpl::Destroy() +{ + if (m_data) { + RendererDestroy(m_data); + m_data = NULL; + } +} + +// FUNCTION: LEGO1 0x100a1894 +Device* RendererImpl::CreateDevice(const DeviceDirect3DCreateData& data) +{ + DeviceImpl* device = new DeviceImpl(); + HRESULT result = m_data->CreateDeviceFromD3D(data.m_pDirect3D, data.m_pDirect3DDevice, &device->m_data); + if (!SUCCEEDED(result)) { + delete device; + device = NULL; + } + return device; +} + +// GLOBAL: LEGO1 0x10101040 +static int gSetBufferCount = 1; + +// FUNCTION: LEGO1 0x100a1900 +Device* RendererImpl::CreateDevice(const DeviceDirectDrawCreateData& data) +{ + DeviceImpl* device = new DeviceImpl(); + HRESULT result = m_data->CreateDeviceFromSurface( + const_cast(data.m_driverGUID), + data.m_pDirectDraw, + data.m_pBackBuffer, + &device->m_data + ); + if (SUCCEEDED(result) && data.m_pBackBuffer && gSetBufferCount) { + device->m_data->SetBufferCount(2); + } + if (!SUCCEEDED(result)) { + delete device; + device = NULL; + } + return device; +} + +inline Result RendererCreateView( + IDirect3DRM* pRenderer, + IDirect3DRMDevice* pDevice, + IDirect3DRMFrame* pCamera, + IDirect3DRMViewport*& rpView, + unsigned long x, + unsigned long y, + unsigned long width, + unsigned long height +) +{ + Result result = ResultVal(pRenderer->CreateViewport(pDevice, pCamera, x, y, width, height, &rpView)); + if (Succeeded(result)) { + result = ViewImpl::ViewportCreateAppData(pRenderer, rpView, pCamera); + if (!Succeeded(result)) { + rpView->Release(); + rpView = NULL; + } + } + return result; +} + +// FUNCTION: LEGO1 0x100a1a00 +View* RendererImpl::CreateView( + const Device* pDevice, + const Camera* pCamera, + unsigned long x, + unsigned long y, + unsigned long width, + unsigned long height +) +{ + ViewImpl* view = new ViewImpl(); + Result result = RendererCreateView( + m_data, + static_cast(pDevice)->m_data, + static_cast(pCamera)->m_data, + view->m_data, + x, + y, + width, + height + ); + if (!result) { + delete view; + view = NULL; + } + return view; +} + +inline Result RendererCreateGroup(IDirect3DRM* pRenderer, IDirect3DRMFrame* pParent, IDirect3DRMFrame*& rpGroup) +{ + Result result = ResultVal(pRenderer->CreateFrame(NULL, &rpGroup)); + if (Succeeded(result) && pParent) { + result = ResultVal(pParent->AddVisual(rpGroup)); + if (!Succeeded(result)) { + rpGroup->Release(); + rpGroup = NULL; + } + } + return result; +} + +// FUNCTION: LEGO1 0x100a1b20 +Group* RendererImpl::CreateGroup(const Group* pParent) +{ + GroupImpl* group = new GroupImpl(); + Result result = + RendererCreateGroup(m_data, pParent ? static_cast(pParent)->m_data : NULL, group->m_data); + if (!result) { + delete group; + group = NULL; + } + return group; +} + +// FUNCTION: LEGO1 0x100a1c30 +Camera* RendererImpl::CreateCamera() +{ + CameraImpl* camera = new CameraImpl(); + if (FAILED(m_data->CreateFrame(NULL, &camera->m_data))) { + delete camera; + camera = NULL; + } + return camera; +} + +// FUNCTION: LEGO1 0x100a1cf0 +Light* RendererImpl::CreateLight(LightType type, float r, float g, float b) +{ + LightImpl* newLight = new LightImpl(); + D3DRMLIGHTTYPE translatedType; + switch (type) { + case Ambient: + translatedType = D3DRMLIGHT_AMBIENT; + break; + case Point: + translatedType = D3DRMLIGHT_POINT; + break; + case Spot: + translatedType = D3DRMLIGHT_SPOT; + break; + case Directional: + translatedType = D3DRMLIGHT_DIRECTIONAL; + break; + case ParallelPoint: + translatedType = D3DRMLIGHT_PARALLELPOINT; + break; + default: + translatedType = D3DRMLIGHT_AMBIENT; + } + + LPDIRECT3DRMFRAME frame; + Result result = ResultVal(m_data->CreateFrame(NULL, &frame)); + if (Succeeded(result)) { + LPDIRECT3DRMLIGHT d3dLight; + result = ResultVal(m_data->CreateLightRGB(translatedType, r, g, b, &d3dLight)); + if (!Succeeded(result)) { + frame->Release(); + } + else { + result = ResultVal(frame->AddLight(d3dLight)); + if (!Succeeded(result)) { + d3dLight->Release(); + frame->Release(); + } + else { + d3dLight->Release(); + newLight->m_data = frame; + } + } + } + if (!Succeeded(result)) { + delete newLight; + newLight = NULL; + } + return newLight; +} + +// FUNCTION: LEGO1 0x100a1e90 +Unk* RendererImpl::CreateUnk() +{ + // Note: I'm fairly certain that Unknown is not what Tgl calls a + // "Mesh", because the methods on Mesh in the Tgl leak line up much + // more closely with a different vtable than the one assigned in + // this method (meaning this method is not creating a Mesh). + // Maybe this method is something like CreateMeshBuilder where the + // Mesh data type in the Tgl leak was split into builder/result? + UnkImpl* unknown = new UnkImpl(); + if (FAILED(m_data->CreateMesh(&unknown->m_data))) { + delete unknown; + unknown = NULL; + } + return unknown; +} + +inline Result RendererCreateTexture( + IDirect3DRM* renderer, + IDirect3DRMTexture*& texture, + int width, + int height, + int bytesPerPixel, + void* pBuffer, + int useBuffer, + int paletteSize, + PaletteEntry* pEntries +) +{ + TglD3DRMIMAGE* image; + Result result; + + image = new TglD3DRMIMAGE(width, height, bytesPerPixel, pBuffer, useBuffer, paletteSize, pEntries); + result = ResultVal(renderer->CreateTexture(&image->m_image, &texture)); + if (Succeeded(result)) { + result = TextureImpl::SetImage(texture, image); + if (!Succeeded(result)) { + texture->Release(); + texture = NULL; + delete image; + } + } + else { + delete image; + } + return result; +} + +// FUNCTION: LEGO1 0x100a1f50 +Texture* RendererImpl::CreateTexture( + int width, + int height, + int bitsPerTexel, + const void* pTexels, + int texelsArePersistent, + int paletteEntryCount, + const PaletteEntry* pEntries +) +{ + TextureImpl* texture = new TextureImpl(); + if (!Succeeded(RendererCreateTexture( + m_data, + texture->m_data, + width, + height, + bitsPerTexel, + const_cast(pTexels), + texelsArePersistent, + paletteEntryCount, + const_cast(pEntries) + ))) { + delete texture; + texture = NULL; + } + return texture; +} + +// FUNCTION: LEGO1 0x100a20d0 +Texture* RendererImpl::CreateTexture() +{ + TextureImpl* texture = new TextureImpl(); + if (!Succeeded(RendererCreateTexture(m_data, texture->m_data, 0, 0, 0, NULL, FALSE, 0, NULL))) { + delete texture; + texture = NULL; + } + return texture; +} + +// FUNCTION: LEGO1 0x100a2270 +Result RendererImpl::SetTextureDefaultShadeCount(unsigned long shadeCount) +{ + return ResultVal(m_data->SetDefaultTextureShades(shadeCount)); +} + +// FUNCTION: LEGO1 0x100a2290 +Result RendererImpl::SetTextureDefaultColorCount(unsigned long colorCount) +{ + return ResultVal(m_data->SetDefaultTextureColors(colorCount)); +} + +// FUNCTION: LEGO1 0x100a22b0 +void* RendererImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} diff --git a/LEGO1/tgl/d3drm/texture.cpp b/LEGO1/tgl/d3drm/texture.cpp new file mode 100644 index 00000000..5b733eae --- /dev/null +++ b/LEGO1/tgl/d3drm/texture.cpp @@ -0,0 +1,196 @@ +#include "impl.h" + +using namespace TglImpl; + +DECOMP_SIZE_ASSERT(TglD3DRMIMAGE, 0x40); + +inline TglD3DRMIMAGE* TextureGetImage(IDirect3DRMTexture* pTexture) +{ + return reinterpret_cast(pTexture->GetAppData()); +} + +// Forward declare to satisfy order check +void TextureDestroyCallback(IDirect3DRMObject* pObject, void* pArg); + +// FUNCTION: LEGO1 0x100a12a0 +Result TextureImpl::SetImage(IDirect3DRMTexture* pSelf, TglD3DRMIMAGE* pImage) +{ + unsigned long appData; + Result result; + + appData = reinterpret_cast(pImage); + + // This is here because in the original code they asserted + // on the return value being NULL. + TextureGetImage(pSelf); + + result = ResultVal(pSelf->SetAppData(appData)); + if (Succeeded(result) && pImage) { + result = ResultVal(pSelf->AddDestroyCallback(TextureDestroyCallback, NULL)); + if (!Succeeded(result)) { + pSelf->SetAppData(0); + } + } + return result; +} + +// FUNCTION: LEGO1 0x100a1300 +void TextureDestroyCallback(IDirect3DRMObject* pObject, void* pArg) +{ + TglD3DRMIMAGE* pImage = reinterpret_cast(pObject->GetAppData()); + delete pImage; + pObject->SetAppData(0); +} + +// FUNCTION: LEGO1 0x100a1330 +TglD3DRMIMAGE::TglD3DRMIMAGE( + int width, + int height, + int depth, + void* pBuffer, + int useBuffer, + int paletteSize, + PaletteEntry* pEntries +) +{ + m_image.aspectx = 1; + m_image.aspecty = 1; + m_image.width = 0; + m_image.height = 0; + m_image.depth = 0; + m_image.rgb = 0; + m_image.bytes_per_line = 0; + m_image.buffer1 = NULL; + m_image.buffer2 = NULL; + m_image.red_mask = 0xFF; + m_image.green_mask = 0xFF; + m_image.blue_mask = 0xFF; + m_image.alpha_mask = 0xFF; + m_image.palette_size = 0; + m_image.palette = NULL; + m_texelsAllocatedByClient = 0; + if (pBuffer != NULL) { + CreateBuffer(width, height, depth, pBuffer, useBuffer); + } + if (pEntries != NULL) { + InitializePalette(paletteSize, pEntries); + } +} + +// FUNCTION: LEGO1 0x100a13b0 +void TglD3DRMIMAGE::Destroy() +{ + if (m_texelsAllocatedByClient == 0) { + delete m_image.buffer1; + } + delete m_image.palette; +} + +// FUNCTION: LEGO1 0x100a13e0 STUB +Result TglD3DRMIMAGE::CreateBuffer(int width, int height, int depth, void* pBuffer, int useBuffer) +{ + return Error; +} + +// FUNCTION: LEGO1 0x100a1510 +void TglD3DRMIMAGE::FillRowsOfTexture(int y, int height, char* pContent) +{ + // The purpose is clearly this but I can't get the assembly to line up. + memcpy((char*) m_image.buffer1 + (y * m_image.bytes_per_line), pContent, height * m_image.bytes_per_line); +} + +// FUNCTION: LEGO1 0x100a1550 +Result TglD3DRMIMAGE::InitializePalette(int paletteSize, PaletteEntry* pEntries) +{ + // This function is a 100% match if the PaletteEntry class is copied + // into into the TglD3DRMIMAGE class instead of being a global struct. + if (m_image.palette_size != paletteSize) { + if (m_image.palette != NULL) { + delete m_image.palette; + m_image.palette = NULL; + m_image.palette_size = 0; + } + if (paletteSize > 0) { + m_image.palette = new D3DRMPALETTEENTRY[paletteSize]; + m_image.palette_size = paletteSize; + } + } + if (paletteSize > 0) { + for (int i = 0; i < paletteSize; i++) { + m_image.palette[i].red = pEntries[i].m_red; + m_image.palette[i].green = pEntries[i].m_green; + m_image.palette[i].blue = pEntries[i].m_blue; + m_image.palette[i].flags = D3DRMPALETTE_READONLY; + } + } + return Success; +} + +// SYNTHETIC: LEGO1 0x100a2800 +// TglImpl::TextureImpl::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100a3c10 +Result TextureImpl::SetTexels(int width, int height, int bitsPerTexel, void* pTexels) +{ + TglD3DRMIMAGE* image = TextureGetImage(m_data); + Result result = image->CreateBuffer(width, height, bitsPerTexel, pTexels, TRUE); + if (Succeeded(result)) { + result = ResultVal(m_data->Changed(TRUE, FALSE)); + } + return result; +} + +// FUNCTION: LEGO1 0x100a3c60 +void TextureImpl::FillRowsOfTexture(int y, int height, void* pBuffer) +{ + TglD3DRMIMAGE* image = TextureGetImage(m_data); + image->FillRowsOfTexture(y, height, (char*) pBuffer); +} + +// FUNCTION: LEGO1 0x100a3c90 +Result TextureImpl::Changed(int texelsChanged, int paletteChanged) +{ + return ResultVal(m_data->Changed(texelsChanged, paletteChanged)); +} + +// FUNCTION: LEGO1 0x100a3d00 +Result TextureImpl::GetBufferAndPalette( + int* width, + int* height, + int* depth, + void** pBuffer, + int* paletteSize, + PaletteEntry** pEntries +) +{ + // Something really doesn't match here, not sure what's up. + TglD3DRMIMAGE* image = TextureGetImage(m_data); + *width = image->m_image.width; + *height = image->m_image.height; + *depth = image->m_image.depth; + *pBuffer = image->m_image.buffer1; + *paletteSize = image->m_image.palette_size; + for (int i = 0; i < image->m_image.palette_size; i++) { + pEntries[i]->m_red = image->m_image.palette[i].red; + pEntries[i]->m_green = image->m_image.palette[i].green; + pEntries[i]->m_blue = image->m_image.palette[i].blue; + } + return Success; +} + +// FUNCTION: LEGO1 0x100a3d40 +Result TextureImpl::SetPalette(int entryCount, PaletteEntry* pEntries) +{ + // Not 100% confident this is supposed to directly be forwarding arguments, + // but it probably is given FillRowsOfTexture matches doing that. + TglD3DRMIMAGE* image = TextureGetImage(m_data); + image->InitializePalette(entryCount, pEntries); + m_data->Changed(FALSE, TRUE); + return Success; +} + +// FUNCTION: LEGO1 0x100a3d70 +void* TextureImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} diff --git a/LEGO1/tgl/d3drm/unk.cpp b/LEGO1/tgl/d3drm/unk.cpp new file mode 100644 index 00000000..05785b2e --- /dev/null +++ b/LEGO1/tgl/d3drm/unk.cpp @@ -0,0 +1,57 @@ +#include "impl.h" + +using namespace TglImpl; + +DECOMP_SIZE_ASSERT(Unk, 0x4); +DECOMP_SIZE_ASSERT(UnkImpl, 0x8); + +// SYNTHETIC: LEGO1 0x100a2720 +// TglImpl::UnkImpl::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100a3830 +void* UnkImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} + +// FUNCTION: LEGO1 0x100a3840 STUB +Result UnkImpl::SetMeshData( + unsigned long faceCount, + unsigned long vertexCount, + const float (*pPositions)[3], + const float (*pNormals)[3], + const float (*pTextureCoordinates)[2], + unsigned long vertexPerFaceCount, + unsigned long* pFaceData +) +{ + return Error; +} + +// FUNCTION: LEGO1 0x100a3ae0 +Result UnkImpl::GetBoundingBox(float min[3], float max[3]) +{ + D3DRMBOX box; + Result result = ResultVal(m_data->GetBox(&box)); + if (result == Success) { + min[0] = box.min.x; + min[1] = box.min.y; + min[2] = box.min.z; + max[0] = box.max.x; + max[1] = box.max.y; + max[2] = box.max.z; + } + return result; +} + +// FUNCTION: LEGO1 0x100a3b40 +Unk* UnkImpl::Clone() +{ + UnkImpl* mesh = new UnkImpl(); + int ret = m_data->Clone(0, IID_IDirect3DRMMeshBuilder, (void**) &mesh->m_data); + if (ret < 0) { + delete mesh; + mesh = NULL; + } + return mesh; +} diff --git a/LEGO1/tgl/d3drm/view.cpp b/LEGO1/tgl/d3drm/view.cpp new file mode 100644 index 00000000..116c2b98 --- /dev/null +++ b/LEGO1/tgl/d3drm/view.cpp @@ -0,0 +1,349 @@ +#include "impl.h" + +using namespace TglImpl; + +struct ViewportAppData { + ViewportAppData(IDirect3DRM* pRenderer); + ~ViewportAppData(); + + IDirect3DRMFrame* m_pLightFrame; + IDirect3DRMFrame* m_pCamera; + IDirect3DRMFrame* m_pLastRenderedFrame; + float m_backgroundColorRed; + float m_backgroundColorGreen; + float m_backgroundColorBlue; +}; + +DECOMP_SIZE_ASSERT(ViewportAppData, 0x18); + +// FUNCTION: LEGO1 0x100a10b0 +ViewportAppData::ViewportAppData(IDirect3DRM* pRenderer) +{ + pRenderer->CreateFrame(NULL, &m_pLightFrame); + m_pCamera = NULL; + m_pLastRenderedFrame = NULL; + m_backgroundColorRed = 0.0f; + m_backgroundColorGreen = 0.0f; + m_backgroundColorBlue = 0.0f; +} + +// FUNCTION: LEGO1 0x100a10e0 +ViewportAppData::~ViewportAppData() +{ + IDirect3DRMFrameArray* pChildFrames; + IDirect3DRMFrame* pChildFrame = NULL; + m_pLightFrame->GetChildren(&pChildFrames); + for (int i = 0; i < (int) pChildFrames->GetSize(); i++) { + pChildFrames->GetElement(i, &pChildFrame); + m_pLightFrame->DeleteChild(pChildFrame); + pChildFrame->Release(); // GetElement() does AddRef() + } + pChildFrames->Release(); + m_pLightFrame->Release(); +} + +// Forward declare to satisfy order check +void ViewportDestroyCallback(IDirect3DRMObject* pObject, void* pArg); + +// FUNCTION: LEGO1 0x100a1160 +Result ViewImpl::ViewportCreateAppData(IDirect3DRM* pDevice, IDirect3DRMViewport* pView, IDirect3DRMFrame* pCamera) +{ + ViewportAppData* data = new ViewportAppData(pDevice); + data->m_pCamera = pCamera; + Result result = ResultVal(pView->SetAppData(reinterpret_cast(data))); + if (Succeeded(result)) { + result = ResultVal(pView->AddDestroyCallback(ViewportDestroyCallback, data)); + } + if (!Succeeded(result)) { + delete data; + pView->SetAppData(0); + } + return result; +} + +inline Result ViewRestoreFrameAfterRender( + IDirect3DRMFrame* pFrame, + IDirect3DRMFrame* pCamera, + IDirect3DRMFrame* pLightFrame +) +{ + Result result = Success; + if (pFrame) { + // remove camera and light frame from frame that was rendered + // this doesn't destroy the camera as it is still the camera of the viewport... + result = ResultVal(pFrame->DeleteChild(pCamera)); + result = ResultVal(pFrame->DeleteChild(pLightFrame)); + + // decrease frame's ref count (it was increased in ViewPrepareFrameForRender()) + pFrame->Release(); + } + return result; +} + +// FUNCTION: LEGO1 0x100a1240 +void ViewportDestroyCallback(IDirect3DRMObject* pObject, void* pArg) +{ + ViewportAppData* pViewportAppData = reinterpret_cast(pArg); + + ViewRestoreFrameAfterRender( + pViewportAppData->m_pLastRenderedFrame, + pViewportAppData->m_pCamera, + pViewportAppData->m_pLightFrame + ); + + delete pViewportAppData; +} + +// FUNCTION: LEGO1 0x100a1290 +Result ViewportPickImpl( + IDirect3DRMViewport* pViewport, + int x, + int y, + const Group** ppGroupsToPickFrom, + int groupsToPickFromCount, + const Group**& rppPickedGroups, + int& rPickedGroupCount +) +{ + // Left unimplemented in shipped game. + return Error; +} + +inline ViewportAppData* ViewportGetData(IDirect3DRMViewport* pViewport) +{ + return reinterpret_cast(pViewport->GetAppData()); +} + +inline IDirect3DRMFrame* ViewportGetLightFrame(IDirect3DRMViewport* pViewport) +{ + return ViewportGetData(pViewport)->m_pLightFrame; +} + +// SYNTHETIC: LEGO1 0x100a23a0 +// TglImpl::ViewImpl::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100a2d80 +void* ViewImpl::ImplementationDataPtr() +{ + return reinterpret_cast(&m_data); +} + +// FUNCTION: LEGO1 0x100a2d90 +Result ViewImpl::Add(const Light* pLight) +{ + const LightImpl* light = static_cast(pLight); + IDirect3DRMFrame* frame = light->ImplementationData(); + return ResultVal(ViewportGetLightFrame(m_data)->AddChild(frame)); +} + +// FUNCTION: LEGO1 0x100a2dc0 +Result ViewImpl::Remove(const Light* pLight) +{ + const LightImpl* light = static_cast(pLight); + IDirect3DRMFrame* frame = light->ImplementationData(); + return ResultVal(ViewportGetLightFrame(m_data)->DeleteChild(frame)); +} + +// FUNCTION: LEGO1 0x100a2df0 +Result ViewImpl::SetCamera(const Camera* pCamera) +{ + const CameraImpl* camera = static_cast(pCamera); + IDirect3DRMFrame* frame = camera->ImplementationData(); + + ViewportAppData* pViewportAppData; + Result result; + + pViewportAppData = reinterpret_cast(m_data->GetAppData()); + result = ViewRestoreFrameAfterRender( + pViewportAppData->m_pLastRenderedFrame, + pViewportAppData->m_pCamera, + pViewportAppData->m_pLightFrame + ); + pViewportAppData->m_pCamera = frame; + pViewportAppData->m_pLastRenderedFrame = 0; + + return ResultVal(m_data->SetCamera(frame)); +} + +// FUNCTION: LEGO1 0x100a2e70 +Result ViewImpl::SetProjection(ProjectionType type) +{ + return ResultVal(m_data->SetProjection(Translate(type))); +} + +// FUNCTION: LEGO1 0x100a2eb0 +Result ViewImpl::SetFrustrum(float frontClippingDistance, float backClippingDistance, float degrees) +{ + float field = frontClippingDistance * tan(DegreesToRadians(degrees / 2)); + Result result; + result = ResultVal(m_data->SetFront(frontClippingDistance)); + if (Succeeded(result)) { + result = ResultVal(m_data->SetBack(backClippingDistance)); + } + if (Succeeded(result)) { + result = ResultVal(m_data->SetField(field)); + } + + return result; +} + +// FUNCTION: LEGO1 0x100a2f30 +Result ViewImpl::SetBackgroundColor(float r, float g, float b) +{ + Result ret = Success; + // Note, this method in the shipped game is very diverged from + // the Tgl leak code. + ViewportAppData* data = ViewportGetData(m_data); + data->m_backgroundColorRed = r; + data->m_backgroundColorGreen = g; + data->m_backgroundColorBlue = b; + if (data->m_pLastRenderedFrame) { + ret = ResultVal(data->m_pLastRenderedFrame->SetSceneBackgroundRGB(r, g, b)); + } + return ret; +} + +// FUNCTION: LEGO1 0x100a2f80 +Result ViewImpl::GetBackgroundColor(float* r, float* g, float* b) +{ + ViewportAppData* data = ViewportGetData(m_data); + *r = data->m_backgroundColorRed; + *g = data->m_backgroundColorGreen; + *b = data->m_backgroundColorBlue; + return Success; +} + +// FUNCTION: LEGO1 0x100a2fb0 +Result ViewImpl::Clear() +{ + return ResultVal(m_data->Clear()); +} + +inline Result ViewPrepareFrameForRender( + IDirect3DRMFrame* pFrame, + IDirect3DRMFrame* pCamera, + IDirect3DRMFrame* pLightFrame, + float backgroundRed, + float backgroundGreen, + float backgroundBlue +) +{ + Result result = Success; + + if (pFrame) { + // set background color + result = ResultVal(pFrame->SetSceneBackgroundRGB(backgroundRed, backgroundGreen, backgroundBlue)); + + // add camera to frame to be rendered + result = ResultVal(pFrame->AddChild(pCamera)); + + // add light frame to frame to be rendered + result = ResultVal(pFrame->AddChild(pLightFrame)); + + // increase ref count of frame to ensure it does not get deleted underneath us + pFrame->AddRef(); + } + + return result; +} + +// FUNCTION: LEGO1 0x100a2fd0 +Result ViewImpl::Render(const Light* pCamera) +{ + ViewportAppData* appdata = ViewportGetData(m_data); + + IDirect3DRMFrame* light = static_cast(pCamera)->ImplementationData(); + + IDirect3DRMFrame* lastRendered = appdata->m_pLastRenderedFrame; + if (light != lastRendered) { + if (lastRendered) { + lastRendered->DeleteChild(appdata->m_pCamera); + // Some other call goes here, not sure what. + lastRendered->Release(); + } + appdata->m_pLastRenderedFrame = light; + if (light) { + light->SetSceneBackgroundRGB( + appdata->m_backgroundColorRed, + appdata->m_backgroundColorGreen, + appdata->m_backgroundColorBlue + ); + light->AddChild(appdata->m_pCamera); + // Some other call goes here, not sure what. + light->AddRef(); + } + } + return ResultVal(m_data->Render(light)); +} + +// FUNCTION: LEGO1 0x100a3080 +Result ViewImpl::ForceUpdate(unsigned long x, unsigned long y, unsigned long width, unsigned long height) +{ + return ResultVal(m_data->ForceUpdate(x, y, x + width - 1, y + height - 1)); +} + +// FUNCTION: LEGO1 0x100a30c0 +Result ViewImpl::Pick( + unsigned long x, + unsigned long y, + const Group** ppGroupsToPickFrom, + int groupsToPickFromCount, + const Group**& rppPickedGroups, + int& rPickedGroupCount +) +{ + return ViewportPickImpl( + m_data, + x, + y, + ppGroupsToPickFrom, + groupsToPickFromCount, + rppPickedGroups, + rPickedGroupCount + ); +} + +// FUNCTION: LEGO1 0x100a30f0 +Result ViewImpl::TransformWorldToScreen(const float world[3], float screen[4]) +{ + D3DRMVECTOR4D d3dRMScreen; + D3DVECTOR d3dRMWorld; + d3dRMWorld.x = world[0]; + d3dRMWorld.y = world[1]; + d3dRMWorld.z = world[2]; + Result result; + + result = ResultVal(m_data->Transform(&d3dRMScreen, &d3dRMWorld)); + + if (Succeeded(result)) { + screen[0] = d3dRMScreen.x; + screen[1] = d3dRMScreen.y; + screen[2] = d3dRMScreen.z; + screen[3] = d3dRMScreen.w; + } + + return result; +} + +// FUNCTION: LEGO1 0x100a3160 +Result ViewImpl::TransformScreenToWorld(const float screen[4], float world[3]) +{ + // 100% match minus instruction reordering. + D3DVECTOR d3dRMWorld; + D3DRMVECTOR4D d3dScreen; + d3dScreen.x = screen[0]; + d3dScreen.y = screen[1]; + d3dScreen.z = screen[2]; + d3dScreen.w = screen[3]; + Result result; + + result = ResultVal(m_data->InverseTransform(&d3dRMWorld, &d3dScreen)); + + if (Succeeded(result)) { + world[0] = d3dRMWorld.x; + world[1] = d3dRMWorld.y; + world[2] = d3dRMWorld.z; + } + + return result; +} diff --git a/LEGO1/tgl/tgl.h b/LEGO1/tgl/tgl.h index 5dec4a56..9bf4ce3a 100644 --- a/LEGO1/tgl/tgl.h +++ b/LEGO1/tgl/tgl.h @@ -1,27 +1,22 @@ -#ifndef TGL_H -#define TGL_H -#ifdef _WIN32 +#ifndef _tgl_h +#define _tgl_h + +#include "tglvector.h" -#define NOMINMAX // to avoid conflict with STL #include #include -#include // HWND - -#endif /* _WIN32 */ - -#include "tglVector.h" +#include namespace Tgl { -// ??? enum ColorModel { + // Note: Not used in shipped game, no way to verify contents. Ramp, RGB }; -// ??? enum ShadingModel { Wireframe, UnlitFlat, @@ -30,7 +25,6 @@ enum ShadingModel { Phong }; -// ????? enum LightType { Ambient, Point, @@ -39,7 +33,6 @@ enum LightType { ParallelPoint }; -// ??? enum ProjectionType { Perspective, Orthographic @@ -50,39 +43,39 @@ enum TextureMappingMode { PerspectiveCorrect }; +// Not in the Tgl leak, inferred from the assembly +enum MaterialMode { + FromParent, + FromFrame, + FromMesh, +}; + struct PaletteEntry { unsigned char m_red; unsigned char m_green; unsigned char m_blue; }; -#ifdef _WIN32 - -struct DeviceDirectDrawCreateData { - const GUID* m_driverGUID; - HWND m_hWnd; // ??? derive from m_pDirectDraw - IDirectDraw* m_pDirectDraw; - IDirectDrawSurface* m_pFrontBuffer; // ??? derive from m_pDirectDraw - IDirectDrawSurface* m_pBackBuffer; - IDirectDrawPalette* m_pPalette; // ??? derive from m_pDirectDraw - int m_isFullScreen; // ??? derive from m_pDirectDraw -}; - struct DeviceDirect3DCreateData { IDirect3D* m_pDirect3D; IDirect3DDevice* m_pDirect3DDevice; }; -#else +struct DeviceDirectDrawCreateData { + const GUID* m_driverGUID; + HWND m_hWnd; + IDirectDraw* m_pDirectDraw; + IDirectDrawSurface* m_pFrontBuffer; + IDirectDrawSurface* m_pBackBuffer; -struct DeviceDirectDrawCreateData {}; - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Result (return value type) + // These have possibly been removed in the shipped game + // (Put them back if we can verify when we find a callsite + // which constructs this type) + // IDirectDrawPalette* m_pPalette; + // int m_isFullScreen; +}; +// Result type used for all methods in the Tgl API enum Result { Error = 0, Success = 1 @@ -93,10 +86,7 @@ inline int Succeeded(Result result) return (result == Success); } -////////////////////////////////////////////////////////////////////////////// -// // Forward declarations - class Renderer; class Object; class Device; @@ -106,30 +96,24 @@ class Camera; class Group; class Mesh; class Texture; +class Unk; -////////////////////////////////////////////////////////////////////////////// -// -// Object - +// VTABLE 0x100db980 class Object { public: virtual ~Object() {} - // returns pointer to implementation data virtual void* ImplementationDataPtr() = 0; }; -////////////////////////////////////////////////////////////////////////////// -// -// Renderer - -// ??? for now until we figured out how an app should pass the Renderer around -Renderer* CreateRenderer(); - +// VTABLE 0x100db948 class Renderer : public Object { public: - virtual Device* CreateDevice(const DeviceDirectDrawCreateData&) = 0; + // vtable+0x08 virtual Device* CreateDevice(const DeviceDirect3DCreateData&) = 0; + virtual Device* CreateDevice(const DeviceDirectDrawCreateData&) = 0; + + // vtable+0x10 virtual View* CreateView( const Device*, const Camera*, @@ -139,34 +123,12 @@ class Renderer : public Object { unsigned long height ) = 0; virtual Camera* CreateCamera() = 0; - virtual Light* CreateLight(LightType, double r, double g, double b) = 0; + virtual Light* CreateLight(LightType, float r, float g, float b) = 0; virtual Group* CreateGroup(const Group* pParent = 0) = 0; - // pTextureCoordinates is pointer to array of vertexCount elements - // (each element being two floats), or NULL - // pFaceData is faceCount tuples, each of format - // [vertex1index, ... vertexNindex], where N = vertexPerFaceCount - virtual Mesh* CreateMesh( - unsigned long vertexCount, - const float (*pVertices)[3], - const float (*pTextureCoordinates)[2], - unsigned long faceCount, - unsigned long vertexPerFaceCount, - unsigned long* pFaceData - ) = 0; - // pTextureCoordinates is pointer to array of vertexCount elements - // (each element being two floats), or NULL - // pFaceData is: - // [face1VertexCount face1Vertex1index, ... face1VertexMindex - // face2VertexCount face2Vertex1index, ... face2VertexNindex - // ... - // 0] - virtual Mesh* CreateMesh( - unsigned long vertexCount, - const float (*pVertices)[3], - const float (*pTextureCoordinates)[2], - unsigned long* pFaceData - ) = 0; + // vtable+0x20 + virtual Unk* CreateUnk() = 0; + virtual Texture* CreateTexture() = 0; virtual Texture* CreateTexture( int width, int height, @@ -176,61 +138,54 @@ class Renderer : public Object { int paletteEntryCount, const PaletteEntry* pEntries ) = 0; - virtual Texture* CreateTexture() = 0; - virtual Result SetTextureDefaultShadeCount(unsigned long) = 0; + + // vtable+0x30 virtual Result SetTextureDefaultColorCount(unsigned long) = 0; }; -////////////////////////////////////////////////////////////////////////////// -// -// Device +Renderer* CreateRenderer(); +// VTABLE 0x100db9b8 class Device : public Object { public: + // vtable+0x08 virtual unsigned long GetWidth() = 0; virtual unsigned long GetHeight() = 0; + + // vtable+0x10 virtual Result SetColorModel(ColorModel) = 0; virtual Result SetShadingModel(ShadingModel) = 0; virtual Result SetShadeCount(unsigned long) = 0; virtual Result SetDither(int) = 0; + + // vtable+0x20 virtual Result Update() = 0; - - // ??? should this be handled by app ??? - // ??? this needs to be called when the window on which the device is ... - // is being activated - virtual void HandleActivate(int bActivate) = 0; - - // ??? this needs to be called when the window on which this device is based - // needs to be repainted - virtual void HandlePaint(void*) = 0; - -#ifdef _DEBUG - virtual unsigned long GetDrawnTriangleCount() = 0; -#endif + virtual void InitFromD3DDevice(Device*) = 0; + virtual void InitFromWindowsDevice(Device*) = 0; }; -////////////////////////////////////////////////////////////////////////////// -// -// View - +// VTABLE 0x100dba28 class View : public Object { public: virtual Result Add(const Light*) = 0; virtual Result Remove(const Light*) = 0; + // vtable+0x10 virtual Result SetCamera(const Camera*) = 0; virtual Result SetProjection(ProjectionType) = 0; - virtual Result SetFrustrum(double frontClippingDistance, double backClippingDistance, double degrees) = 0; - virtual Result SetBackgroundColor(double r, double g, double b) = 0; + virtual Result SetFrustrum(float frontClippingDistance, float backClippingDistance, float degrees) = 0; + virtual Result SetBackgroundColor(float r, float g, float b) = 0; + // vtable+0x20 + virtual Result GetBackgroundColor(float* r, float* g, float* b) = 0; virtual Result Clear() = 0; - virtual Result Render(const Group*) = 0; - // ??? needed for fine grain control when using DirectDraw/D3D ??? + virtual Result Render(const Light*) = 0; virtual Result ForceUpdate(unsigned long x, unsigned long y, unsigned long width, unsigned long height) = 0; - // ??? for now: used by Mesh Cost calculation - virtual Result TransformWorldToScreen(const double world[3], double screen[4]) = 0; + // vtable+0x30 + virtual Result TransformWorldToScreen(const float world[3], float screen[4]) = 0; + virtual Result TransformScreenToWorld(const float screen[4], float world[3]) = 0; // Pick(): // x, y: @@ -264,102 +219,94 @@ class View : public Object { ) = 0; }; -////////////////////////////////////////////////////////////////////////////// -// -// Camera - +// VTABLE 0x100dbae8 class Camera : public Object { public: -#if 0 - virtual Result SetPosition(const double[3]) = 0; - virtual Result SetOrientation(const double direction[3], - const double up[3]) = 0; -#endif virtual Result SetTransformation(const FloatMatrix4&) = 0; }; -////////////////////////////////////////////////////////////////////////////// -// -// Light - +// VTABLE 0x100dbb08 class Light : public Object { public: -#if 0 - virtual Result SetPosition(const double[3]) = 0; - virtual Result SetOrientation(const double direction[3], - const double up[3]) = 0; -#endif virtual Result SetTransformation(const FloatMatrix4&) = 0; + virtual Result SetColor(float r, float g, float b) = 0; }; -////////////////////////////////////////////////////////////////////////////// -// -// Group - -class Group : public Object { -public: -#if 0 - virtual Result SetPosition(const double[3]) = 0; - virtual Result SetOrientation(const double direction[3], - const double up[3]) = 0; -#endif - // TODO: The type was changed from `FloatMatrix` to `Matrix` to make code in UpdateWorldData match. - // However, this is unlikely to be correct and will have to be figured out at some point. - virtual Result SetTransformation(const Matrix4&) = 0; - - // ??? not yet fully implemented - virtual Result SetColor(double r, double g, double b) = 0; - virtual Result SetTexture(const Texture*) = 0; - - virtual Result Add(const Group*) = 0; - virtual Result Add(const Mesh*) = 0; - - virtual Result Remove(const Group*) = 0; - virtual Result Remove(const Mesh*) = 0; - - virtual Result RemoveAll() = 0; - - // ??? for now: used by Mesh Cost calculation - virtual Result TransformLocalToWorld(const double local[3], double world[3]) = 0; -}; - -////////////////////////////////////////////////////////////////////////////// -// -// Mesh - +// VTABLE 0x100dbbb0 class Mesh : public Object { public: - // ??? also on Group - virtual Result SetColor(double r, double g, double b) = 0; + virtual Result SetColor(float r, float g, float b, float a) = 0; virtual Result SetTexture(const Texture*) = 0; - virtual Result SetTextureMappingMode(TextureMappingMode) = 0; + virtual Result GetTexture(Texture*&) = 0; + + virtual Result SetTextureMappingMode(ProjectionType) = 0; virtual Result SetShadingModel(ShadingModel) = 0; -#ifdef _DEBUG - virtual Result GetBoundingBox(float min[3], float max[3]) = 0; - virtual unsigned long GetFaceCount() = 0; - virtual unsigned long GetVertexCount() = 0; -#endif + // Clone data in underlying group + virtual Mesh* DeepClone(Unk*) = 0; + + // Just get another Group pointing to the same underlying data + virtual Mesh* ShallowClone(Unk*) = 0; }; -////////////////////////////////////////////////////////////////////////////// -// -// Texture +// VTABLE 0x100dbaa0 +class Group : public Object { +public: + virtual Result SetTransformation(const FloatMatrix4&) = 0; + virtual Result SetColor(float r, float g, float b, float a) = 0; + virtual Result SetTexture(const Texture*) = 0; + virtual Result GetTexture(Texture*&) = 0; + virtual Result SetMaterialMode(MaterialMode) = 0; + virtual Result Add(const Group*) = 0; + virtual Result Add(const Mesh*) = 0; + virtual Result Remove(const Group*) = 0; + virtual Result Remove(const Mesh*) = 0; + virtual Result RemoveAll() = 0; + // This is TransformLocalToWorld in the leak, however it seems + // to have been replaced by something else in the shipped code. + virtual Result Unknown() = 0; +}; + +// Don't know what this is. Seems like another Tgl object which +// was not in the leaked Tgl code. My suspicion is that it's +// some kind of builder class for creating meshes. +// VTABLE 0x100dbb30 +class Unk : public Object { +public: + virtual Result SetMeshData( + unsigned long faceCount, + unsigned long vertexCount, + const float (*pPositions)[3], + const float (*pNormals)[3], + const float (*pTextureCoordinates)[2], + unsigned long vertexPerFaceCount, + unsigned long* pFaceData + ) = 0; + virtual Result GetBoundingBox(float min[3], float max[3]) = 0; + virtual Unk* Clone() = 0; +}; + +// VTABLE 0x100dbb68 class Texture : public Object { public: - virtual Result SetTexels( - int width, - int height, - int bitsPerTexel, - const void* pTexels, - int pTexelsArePersistent - ) = 0; - virtual Result SetPalette(int entryCount, const PaletteEntry* pEntries) = 0; -}; + // vtable+0x08 + virtual Result SetTexels(int width, int height, int bitsPerTexel, void* pTexels) = 0; + virtual void FillRowsOfTexture(int y, int height, void* pBuffer) = 0; -////////////////////////////////////////////////////////////////////////////// + // vtable+0x10 + virtual Result Changed(int texelsChanged, int paletteChanged) = 0; + virtual Result GetBufferAndPalette( + int* pWidth, + int* pHeight, + int* pDepth, + void** ppBuffer, + int* pPaletteSize, + PaletteEntry** ppPalette + ) = 0; + virtual Result SetPalette(int entryCount, PaletteEntry* pEntries) = 0; +}; } // namespace Tgl -#endif // TGL_H +#endif /* _tgl_h */ diff --git a/LEGO1/tgl/tglvector.h b/LEGO1/tgl/tglvector.h index a617928d..78255937 100644 --- a/LEGO1/tgl/tglvector.h +++ b/LEGO1/tgl/tglvector.h @@ -1,7 +1,10 @@ -#ifndef TGLVECTOR_H -#define TGLVECTOR_H +#ifndef _tglVector_h +#define _tglVector_h +// Note: This file is almost an exact copy of the one from +// the leak but using floats instead of doubles, hence the +// strange formatting in some places. -#include "math.h" // ??? sin() in RotateAroundY() +#include "math.h" // sin() in RotateAroundY() #include // offsetof() @@ -142,7 +145,6 @@ class RotationY : public FloatMatrix4 { public: RotationY(float radians); }; - ////////////////////////////////////////////////////////////////////////////// // // Transformation matrices implementation @@ -274,4 +276,4 @@ inline RotationY::RotationY(float radians) } // namespace Tgl -#endif // TLGVECTOR_H +#endif /* _tglVector_h */ diff --git a/LEGO1/viewmanager/viewroi.cpp b/LEGO1/viewmanager/viewroi.cpp index 00af794b..47cb71e8 100644 --- a/LEGO1/viewmanager/viewroi.cpp +++ b/LEGO1/viewmanager/viewroi.cpp @@ -27,8 +27,7 @@ void ViewROI::UpdateWorldData(const Matrix4Data& parent2world) { OrientableROI::UpdateWorldData(parent2world); if (geometry) { - // Tgl::FloatMatrix4 tgl_mat; - Matrix4 mat; + Tgl::FloatMatrix4 mat; SETMAT4(mat, m_local2world.GetMatrix()); Tgl::Result result = geometry->SetTransformation(mat); // assert(Tgl::Succeeded(result)); From 1485e5df4711f284a1768a45ffd226f73b63736b Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Thu, 7 Dec 2023 07:13:31 -0500 Subject: [PATCH 3/7] Implement/match most of MxStillPresenter (#309) * Implement/match most of MxStillPresenter * Fix merge --- LEGO1/mxdsmediaaction.h | 1 + LEGO1/mxflcpresenter.cpp | 10 ++--- LEGO1/mxflcpresenter.h | 2 +- LEGO1/mxrect32.h | 1 + LEGO1/mxsmkpresenter.cpp | 2 +- LEGO1/mxsmkpresenter.h | 2 +- LEGO1/mxstillpresenter.cpp | 88 +++++++++++++++++++++++++++++--------- LEGO1/mxstillpresenter.h | 6 +-- LEGO1/mxvideomanager.h | 16 +++---- LEGO1/mxvideoparam.cpp | 4 ++ LEGO1/mxvideoparam.h | 13 +++--- LEGO1/mxvideopresenter.cpp | 2 +- LEGO1/mxvideopresenter.h | 2 +- 13 files changed, 101 insertions(+), 48 deletions(-) diff --git a/LEGO1/mxdsmediaaction.h b/LEGO1/mxdsmediaaction.h index c361c835..8f7036bb 100644 --- a/LEGO1/mxdsmediaaction.h +++ b/LEGO1/mxdsmediaaction.h @@ -34,6 +34,7 @@ class MxDSMediaAction : public MxDSAction { void CopyMediaSrcPath(const char* p_mediaSrcPath); inline MxS32 GetMediaFormat() const { return this->m_mediaFormat; } + inline MxS32 GetPaletteManagement() const { return this->m_paletteManagement; } inline MxLong GetSustainTime() const { return this->m_sustainTime; } private: diff --git a/LEGO1/mxflcpresenter.cpp b/LEGO1/mxflcpresenter.cpp index 8ec40052..8c49505d 100644 --- a/LEGO1/mxflcpresenter.cpp +++ b/LEGO1/mxflcpresenter.cpp @@ -42,11 +42,9 @@ void MxFlcPresenter::CreateBitmap() } // FUNCTION: LEGO1 0x100b3620 -void MxFlcPresenter::VTable0x70() +void MxFlcPresenter::RealizePalette() { - MxPalette* pal = m_bitmap->CreatePalette(); - MVideoManager()->RealizePalette(pal); - - if (pal) - delete pal; + MxPalette* palette = m_bitmap->CreatePalette(); + MVideoManager()->RealizePalette(palette); + delete palette; } diff --git a/LEGO1/mxflcpresenter.h b/LEGO1/mxflcpresenter.h index 94720718..5790999d 100644 --- a/LEGO1/mxflcpresenter.h +++ b/LEGO1/mxflcpresenter.h @@ -28,7 +28,7 @@ class MxFlcPresenter : public MxVideoPresenter { virtual void LoadHeader(MxStreamChunk* p_chunk) override; // vtable+0x5c virtual void CreateBitmap() override; // vtable+0x60 - virtual void VTable0x70() override; // vtable+0x70 + virtual void RealizePalette() override; // vtable+0x70 protected: FLIC_HEADER* m_flicHeader; diff --git a/LEGO1/mxrect32.h b/LEGO1/mxrect32.h index 070c3d25..f7c38c69 100644 --- a/LEGO1/mxrect32.h +++ b/LEGO1/mxrect32.h @@ -4,6 +4,7 @@ #include "mxpoint32.h" #include "mxsize32.h" +// SIZE 0x10 class MxRect32 { public: MxRect32() {} diff --git a/LEGO1/mxsmkpresenter.cpp b/LEGO1/mxsmkpresenter.cpp index e77e525b..1aea3243 100644 --- a/LEGO1/mxsmkpresenter.cpp +++ b/LEGO1/mxsmkpresenter.cpp @@ -85,7 +85,7 @@ MxU32 MxSmkPresenter::VTable0x88() } // FUNCTION: LEGO1 0x100b42c0 -void MxSmkPresenter::VTable0x70() +void MxSmkPresenter::RealizePalette() { MxPalette* palette = m_bitmap->CreatePalette(); MVideoManager()->RealizePalette(palette); diff --git a/LEGO1/mxsmkpresenter.h b/LEGO1/mxsmkpresenter.h index 06966b9f..6ea4cc10 100644 --- a/LEGO1/mxsmkpresenter.h +++ b/LEGO1/mxsmkpresenter.h @@ -30,7 +30,7 @@ class MxSmkPresenter : public MxVideoPresenter { virtual void LoadHeader(MxStreamChunk* p_chunk) override; // vtable+0x5c virtual void CreateBitmap() override; // vtable+0x60 virtual void LoadFrame(MxStreamChunk* p_chunk) override; // vtable+0x68 - virtual void VTable0x70() override; // vtable+0x70 + virtual void RealizePalette() override; // vtable+0x70 virtual MxU32 VTable0x88(); // vtable+0x88 struct MxSmack { diff --git a/LEGO1/mxstillpresenter.cpp b/LEGO1/mxstillpresenter.cpp index e3adaa90..278b24cf 100644 --- a/LEGO1/mxstillpresenter.cpp +++ b/LEGO1/mxstillpresenter.cpp @@ -3,6 +3,8 @@ #include "decomp.h" #include "define.h" #include "legoomni.h" +#include "mxcompositepresenter.h" +#include "mxdsmediaaction.h" #include "mxomni.h" #include "mxvideomanager.h" @@ -83,8 +85,8 @@ void MxStillPresenter::LoadFrame(MxStreamChunk* p_chunk) MxS32 height = GetHeight() - 1; MxS32 width = GetWidth() - 1; - MxS32 x = GetLocationX(); - MxS32 y = GetLocationY(); + MxS32 x = m_location.m_x; + MxS32 y = m_location.m_y; MxRect32 rect(x, y, width + x, height + y); MVideoManager()->InvalidateRect(rect); @@ -98,12 +100,10 @@ void MxStillPresenter::LoadFrame(MxStreamChunk* p_chunk) m_action->GetFlags() & MxDSAction::Flag_Bit4 ); - if (m_alpha) - delete m_alpha; + delete m_alpha; m_alpha = new AlphaMask(*m_bitmap); - if (m_bitmap) - delete m_bitmap; + delete m_bitmap; m_bitmap = NULL; if (m_unk58 && unk) @@ -113,40 +113,88 @@ void MxStillPresenter::LoadFrame(MxStreamChunk* p_chunk) } } -// STUB: LEGO1 0x100b9f30 -void MxStillPresenter::VTable0x70() +// FUNCTION: LEGO1 0x100b9f30 +void MxStillPresenter::RealizePalette() { - // TODO + MxPalette* palette = m_bitmap->CreatePalette(); + MVideoManager()->RealizePalette(palette); + delete palette; } -// STUB: LEGO1 0x100b9f60 +// FUNCTION: LEGO1 0x100b9f60 void MxStillPresenter::StartingTickle() { - // TODO + MxVideoPresenter::StartingTickle(); + + if (m_currentTickleState == TickleState_Streaming && ((MxDSMediaAction*) m_action)->GetPaletteManagement()) + RealizePalette(); } -// STUB: LEGO1 0x100b9f90 +// FUNCTION: LEGO1 0x100b9f90 void MxStillPresenter::StreamingTickle() { - // TODO + MxStreamChunk* chunk = FUN_100b5650(); + + if (chunk && m_action->GetElapsedTime() >= chunk->GetTime()) { + m_chunkTime = chunk->GetTime(); + NextFrame(); + m_previousTickleStates |= 1 << (unsigned char) m_currentTickleState; + m_currentTickleState = TickleState_Repeating; + + if (m_action->GetDuration() == -1 && m_compositePresenter) + m_compositePresenter->VTable0x60(this); + } } -// STUB: LEGO1 0x100b9ff0 +// FUNCTION: LEGO1 0x100b9ff0 void MxStillPresenter::RepeatingTickle() { - // TODO + if (m_action->GetDuration() != -1) { + if (m_action->GetElapsedTime() >= m_action->GetStartTime() + m_action->GetDuration()) { + m_previousTickleStates |= 1 << (unsigned char) m_currentTickleState; + m_currentTickleState = TickleState_unk5; + } + } } -// STUB: LEGO1 0x100ba040 -void MxStillPresenter::VTable0x88(undefined4, undefined4) +// FUNCTION: LEGO1 0x100ba040 +void MxStillPresenter::VTable0x88(MxS32 p_x, MxS32 p_y) { - // TODO + MxS32 x = m_location.m_x; + MxS32 y = m_location.m_y; + m_location.m_x = p_x; + m_location.m_y = p_y; + + if (IsEnabled()) { + MxS32 height = GetHeight() - 1; + MxS32 width = GetWidth() - 1; + + MxRect32 rect_a(x, y, width + x, height + y); + MxRect32 rect_b(m_location.m_x, m_location.m_y, width + m_location.m_x, height + m_location.m_y); + + MVideoManager()->InvalidateRect(rect_a); + MVideoManager()->vtable0x34(rect_a.GetLeft(), rect_a.GetTop(), rect_a.GetWidth(), rect_a.GetHeight()); + + MVideoManager()->InvalidateRect(rect_b); + MVideoManager()->vtable0x34(rect_b.GetLeft(), rect_b.GetTop(), rect_b.GetWidth(), rect_b.GetHeight()); + } } -// STUB: LEGO1 0x100ba140 +// FUNCTION: LEGO1 0x100ba140 void MxStillPresenter::Enable(MxBool p_enable) { - // TODO + MxVideoPresenter::Enable(p_enable); + + if (MVideoManager() && (m_alpha || m_bitmap)) { + MxS32 height = GetHeight(); + MxS32 width = GetWidth(); + MxS32 x = m_location.m_x; + MxS32 y = m_location.m_y; + + MxRect32 rect(x, y, width + x, height + y); + MVideoManager()->InvalidateRect(rect); + MVideoManager()->vtable0x34(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight()); + } } // FUNCTION: LEGO1 0x100ba1e0 diff --git a/LEGO1/mxstillpresenter.h b/LEGO1/mxstillpresenter.h index d61f2161..6f434faa 100644 --- a/LEGO1/mxstillpresenter.h +++ b/LEGO1/mxstillpresenter.h @@ -32,14 +32,14 @@ class MxStillPresenter : public MxVideoPresenter { virtual void CreateBitmap() override; // vtable+0x60 virtual void NextFrame() override; // vtable+0x64 virtual void LoadFrame(MxStreamChunk* p_chunk) override; // vtable+0x68 - virtual void VTable0x70() override; // vtable+0x70 - virtual void VTable0x88(undefined4, undefined4); // vtable+0x88 + virtual void RealizePalette() override; // vtable+0x70 + virtual void VTable0x88(MxS32 p_x, MxS32 p_y); // vtable+0x88 virtual MxStillPresenter* Clone(); // vtable+0x8c private: void Destroy(MxBool p_fromDestructor); - undefined4 m_unk64; // 0x64 + MxLong m_chunkTime; // 0x64 MxBITMAPINFO* m_bitmapInfo; // 0x68 }; diff --git a/LEGO1/mxvideomanager.h b/LEGO1/mxvideomanager.h index 1fffe403..05cfd99b 100644 --- a/LEGO1/mxvideomanager.h +++ b/LEGO1/mxvideomanager.h @@ -29,8 +29,8 @@ class MxVideoManager : public MxMediaManager { virtual MxResult Create(MxVideoParam& p_videoParam, MxU32 p_frequencyMS, MxBool p_createThread); // vtable+0x2c __declspec(dllexport) void InvalidateRect(MxRect32&); - __declspec(dllexport) virtual MxResult RealizePalette(MxPalette*); // vtable+0x30 - virtual void vtable0x34(MxU32 p_x, MxU32 p_y, MxU32 p_width, MxU32 p_height); + __declspec(dllexport) virtual MxResult RealizePalette(MxPalette*); // vtable+0x30 + virtual void vtable0x34(MxU32 p_x, MxU32 p_y, MxU32 p_width, MxU32 p_height); // vtable+0x34 MxResult Init(); void Destroy(MxBool p_fromDestructor); @@ -42,12 +42,12 @@ class MxVideoManager : public MxMediaManager { inline MxDisplaySurface* GetDisplaySurface() { return this->m_displaySurface; } protected: - MxVideoParam m_videoParam; - LPDIRECTDRAW m_pDirectDraw; - LPDIRECTDRAWSURFACE m_pDDSurface; - MxDisplaySurface* m_displaySurface; - MxRegion* m_region; - MxBool m_unk60; + MxVideoParam m_videoParam; // 0x2c + LPDIRECTDRAW m_pDirectDraw; // 0x50 + LPDIRECTDRAWSURFACE m_pDDSurface; // 0x54 + MxDisplaySurface* m_displaySurface; // 0x58 + MxRegion* m_region; // 0x5c + MxBool m_unk60; // 0x60 }; #endif // MXVIDEOMANAGER_H diff --git a/LEGO1/mxvideoparam.cpp b/LEGO1/mxvideoparam.cpp index cfcd3098..5d037c0c 100644 --- a/LEGO1/mxvideoparam.cpp +++ b/LEGO1/mxvideoparam.cpp @@ -1,8 +1,12 @@ #include "mxvideoparam.h" +#include "decomp.h" + #include #include +DECOMP_SIZE_ASSERT(MxVideoParam, 0x24); + // FUNCTION: LEGO1 0x100bec70 MxVideoParam::MxVideoParam() { diff --git a/LEGO1/mxvideoparam.h b/LEGO1/mxvideoparam.h index 7262fed4..f44bce88 100644 --- a/LEGO1/mxvideoparam.h +++ b/LEGO1/mxvideoparam.h @@ -10,6 +10,7 @@ #include +// SIZE 0x24 class MxVideoParam { public: __declspec(dllexport) MxVideoParam(); @@ -31,12 +32,12 @@ class MxVideoParam { inline MxU32 GetBackBuffers() { return this->m_backBuffers; } private: - MxRect32 m_rect; - MxPalette* m_palette; - MxU32 m_backBuffers; - MxVideoParamFlags m_flags; - int m_unk1c; - char* m_deviceId; + MxRect32 m_rect; // 0x00 + MxPalette* m_palette; // 0x10 + MxU32 m_backBuffers; // 0x14 + MxVideoParamFlags m_flags; // 0x18 + int m_unk1c; // 0x1c + char* m_deviceId; // 0x20 }; #endif // MXVIDEOPARAM_H diff --git a/LEGO1/mxvideopresenter.cpp b/LEGO1/mxvideopresenter.cpp index 09db8738..a8673b48 100644 --- a/LEGO1/mxvideopresenter.cpp +++ b/LEGO1/mxvideopresenter.cpp @@ -26,7 +26,7 @@ void MxVideoPresenter::LoadFrame(MxStreamChunk* p_chunk) } // FUNCTION: LEGO1 0x1000c730 -void MxVideoPresenter::VTable0x70() +void MxVideoPresenter::RealizePalette() { // Empty } diff --git a/LEGO1/mxvideopresenter.h b/LEGO1/mxvideopresenter.h index 6b0eb620..a3e688fa 100644 --- a/LEGO1/mxvideopresenter.h +++ b/LEGO1/mxvideopresenter.h @@ -48,7 +48,7 @@ class MxVideoPresenter : public MxMediaPresenter { virtual void NextFrame(); // vtable+0x64 virtual void LoadFrame(MxStreamChunk* p_chunk); // vtable+0x68 virtual void VTable0x6c(); // vtable+0x6c - virtual void VTable0x70(); // vtable+0x70 + virtual void RealizePalette(); // vtable+0x70 virtual undefined VTable0x74(); // vtable+0x74 virtual LPDIRECTDRAWSURFACE VTable0x78(); // vtable+0x78 virtual MxBool VTable0x7c(); // vtable+0x7c From f87c96f1bb456b1d541ab906e0188a7d75e057e3 Mon Sep 17 00:00:00 2001 From: MS Date: Thu, 7 Dec 2023 10:14:02 -0500 Subject: [PATCH 4/7] Out of order report fix (#317) --- tools/checkorder/checkorder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/checkorder/checkorder.py b/tools/checkorder/checkorder.py index 02636c09..0cdba670 100644 --- a/tools/checkorder/checkorder.py +++ b/tools/checkorder/checkorder.py @@ -49,7 +49,7 @@ def check_file(filename: str, verbose: bool = False) -> bool: f"{fun.end_line - fun.line_number:4} lines", f"{order_lookup[fun.offset]:3}", " ", - sig_truncate(fun.signature), + sig_truncate(fun.name), ] ) print(msg) From ce686705f293f721c323f67b7ad4c748ce412227 Mon Sep 17 00:00:00 2001 From: MS Date: Thu, 7 Dec 2023 14:14:49 -0500 Subject: [PATCH 5/7] Refactor MxList cursors (#313) * LegoWorldList * Refactor list cursors * Add decomp markers for list cursors * Fix decomp markers * MxRegionListCursor edit to prevent accuracy drop * Better fix for MxRegionListCursor --- CMakeLists.txt | 1 + LEGO1/legoomni.cpp | 7 +++--- LEGO1/legoomni.h | 3 ++- LEGO1/legoworld.cpp | 18 ++++++++++++++++ LEGO1/legoworldlist.cpp | 36 +++++++++++++++++++++++++++++++ LEGO1/legoworldlist.h | 27 +++++++++++++++++++++++ LEGO1/mxdsactionlist.h | 9 +++++++- LEGO1/mxdsmultiaction.cpp | 13 +++++++++++ LEGO1/mxdsselectaction.cpp | 15 +++++++++++++ LEGO1/mxlist.h | 12 ++--------- LEGO1/mxmediapresenter.cpp | 13 +++++++++++ LEGO1/mxpresenterlist.h | 12 ++++++++++- LEGO1/mxregion.cpp | 42 ++++++++++++++++++++++++++++++++++++ LEGO1/mxregionlist.h | 32 +++++++++++++++++++++------ LEGO1/mxstreamchunklist.h | 9 +++++++- LEGO1/mxstreamcontroller.cpp | 6 +++--- LEGO1/mxstringlist.h | 8 ++++++- 17 files changed, 236 insertions(+), 27 deletions(-) create mode 100644 LEGO1/legoworldlist.cpp create mode 100644 LEGO1/legoworldlist.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e1fa4db2..9efb9c99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ add_library(lego1 SHARED LEGO1/legovehiclebuildstate.cpp LEGO1/legovideomanager.cpp LEGO1/legoworld.cpp + LEGO1/legoworldlist.cpp LEGO1/legoworldpresenter.cpp LEGO1/motorcycle.cpp LEGO1/mxactionnotificationparam.cpp diff --git a/LEGO1/legoomni.cpp b/LEGO1/legoomni.cpp index 887b8cad..51bb0c0a 100644 --- a/LEGO1/legoomni.cpp +++ b/LEGO1/legoomni.cpp @@ -11,6 +11,7 @@ #include "legoutil.h" #include "legovideomanager.h" #include "legoworld.h" +#include "legoworldlist.h" #include "mxautolocker.h" #include "mxbackgroundaudiomanager.h" #include "mxdsfile.h" @@ -358,7 +359,7 @@ void LegoOmni::Init() m_inputMgr = NULL; m_unk6c = 0; m_gifManager = NULL; - m_unk78 = 0; + m_worldList = NULL; m_currentWorld = NULL; m_unk80 = FALSE; m_currentVehicle = NULL; @@ -427,9 +428,9 @@ MxResult LegoOmni::Create(MxOmniCreateParam& p) m_animationManager = new LegoAnimationManager(); m_buildingManager = new LegoBuildingManager(); m_gameState = new LegoGameState(); - // TODO: initialize list at m_unk78 + m_worldList = new LegoWorldList(); - if (m_unk6c && m_gifManager && m_unk78 && m_plantManager && m_animationManager && m_buildingManager) { + if (m_unk6c && m_gifManager && m_worldList && m_plantManager && m_animationManager && m_buildingManager) { // TODO: initialize a bunch of MxVariables RegisterScripts(); FUN_1001a700(); diff --git a/LEGO1/legoomni.h b/LEGO1/legoomni.h index d5063235..644f2536 100644 --- a/LEGO1/legoomni.h +++ b/LEGO1/legoomni.h @@ -21,6 +21,7 @@ class LegoSoundManager; class LegoUnkSaveDataWriter; class LegoVideoManager; class LegoWorld; +class LegoWorldList; class MxAtomId; class MxBackgroundAudioManager; class MxDSFile; @@ -117,7 +118,7 @@ class LegoOmni : public MxOmni { undefined4 m_unk6c; LegoInputManager* m_inputMgr; // 0x70 GifManager* m_gifManager; - undefined4 m_unk78; + LegoWorldList* m_worldList; // 0x78 LegoWorld* m_currentWorld; MxBool m_unk80; LegoNavController* m_navController; // 0x84 diff --git a/LEGO1/legoworld.cpp b/LEGO1/legoworld.cpp index 938abe3a..5129cca6 100644 --- a/LEGO1/legoworld.cpp +++ b/LEGO1/legoworld.cpp @@ -67,6 +67,24 @@ MxResult LegoWorld::SetAsCurrentWorld(MxDSObject& p_dsObject) return SUCCESS; } +// SYNTHETIC: LEGO1 0x1001eed0 +// MxPresenterListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x1001ef40 +// MxPtrListCursor::~MxPtrListCursor + +// SYNTHETIC: LEGO1 0x1001ef90 +// MxListCursor::`scalar deleting destructor' + +// SYNTHETIC: LEGO1 0x1001f000 +// MxPtrListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x1001f070 +// MxListCursor::~MxListCursor + +// FUNCTION: LEGO1 0x1001f0c0 +// MxPresenterListCursor::~MxPresenterListCursor + // FUNCTION: LEGO1 0x1001f5e0 MxLong LegoWorld::Notify(MxParam& p_param) { diff --git a/LEGO1/legoworldlist.cpp b/LEGO1/legoworldlist.cpp new file mode 100644 index 00000000..a62173b0 --- /dev/null +++ b/LEGO1/legoworldlist.cpp @@ -0,0 +1,36 @@ +#include "legoworldlist.h" + +#include "legoworld.h" + +// FUNCTION: LEGO1 0x100598d0 +MxS8 LegoWorldList::Compare(LegoWorld* p_a, LegoWorld* p_b) +{ + return p_a == p_b ? 0 : p_a < p_b ? -1 : 1; +} + +// TEMPLATE: LEGO1 0x100598f0 +// MxCollection::Compare + +// TEMPLATE: LEGO1 0x10059900 +// MxCollection::~MxCollection + +// TEMPLATE: LEGO1 0x10059950 +// MxCollection::Destroy + +// TEMPLATE: LEGO1 0x10059960 +// MxList::~MxList + +// FUNCTION: LEGO1 0x100599f0 +void LegoWorldList::Destroy(LegoWorld* p_world) +{ + delete p_world; +} + +// SYNTHETIC: LEGO1 0x10059ac0 +// MxCollection::`scalar deleting destructor' + +// SYNTHETIC: LEGO1 0x10059b30 +// MxList::`scalar deleting destructor' + +// SYNTHETIC: LEGO1 0x10059be0 +// MxPtrList::`scalar deleting destructor' diff --git a/LEGO1/legoworldlist.h b/LEGO1/legoworldlist.h new file mode 100644 index 00000000..5e9a7c37 --- /dev/null +++ b/LEGO1/legoworldlist.h @@ -0,0 +1,27 @@ +#ifndef LEGOWORLDLIST_H +#define LEGOWORLDLIST_H + +#include "mxlist.h" +#include "mxtypes.h" + +class LegoWorld; + +// VTABLE: LEGO1 0x100d8700 +// class MxCollection + +// VTABLE: LEGO1 0x100d8718 +// class MxList + +// VTABLE: LEGO1 0x100d8730 +// class MxPtrList + +// VTABLE: LEGO1 0x100d8680 +// SIZE 0x18 +class LegoWorldList : public MxPtrList { +public: + LegoWorldList() : MxPtrList(Destroy) {} + virtual MxS8 Compare(LegoWorld*, LegoWorld*) override; // vtable+0x14 + static void Destroy(LegoWorld*); +}; + +#endif // LEGOWORLDLIST_H diff --git a/LEGO1/mxdsactionlist.h b/LEGO1/mxdsactionlist.h index 19929d20..a467bf52 100644 --- a/LEGO1/mxdsactionlist.h +++ b/LEGO1/mxdsactionlist.h @@ -26,6 +26,13 @@ class MxDSActionList : public MxList { undefined m_unk18; }; -typedef MxListCursorChild MxDSActionListCursor; +// VTABLE: LEGO1 0x100d7e68 +// class MxListCursor + +// VTABLE: LEGO1 0x100d7e50 +class MxDSActionListCursor : public MxListCursor { +public: + MxDSActionListCursor(MxDSActionList* p_list) : MxListCursor(p_list){}; +}; #endif // MXDSACTIONLIST_H diff --git a/LEGO1/mxdsmultiaction.cpp b/LEGO1/mxdsmultiaction.cpp index 27cd806e..b2b31abe 100644 --- a/LEGO1/mxdsmultiaction.cpp +++ b/LEGO1/mxdsmultiaction.cpp @@ -2,6 +2,19 @@ DECOMP_SIZE_ASSERT(MxDSMultiAction, 0x9c) +// TODO: Should be moved later +// SYNTHETIC: LEGO1 0x1004ad10 +// MxDSActionListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x1004ad80 +// MxListCursor::~MxListCursor + +// SYNTHETIC: LEGO1 0x1004add0 +// MxListCursor::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x1004ae40 +// MxDSActionListCursor::~MxDSActionListCursor + // FUNCTION: LEGO1 0x100c9b90 MxDSMultiAction::MxDSMultiAction() { diff --git a/LEGO1/mxdsselectaction.cpp b/LEGO1/mxdsselectaction.cpp index 4bdfbd60..ed9b78ba 100644 --- a/LEGO1/mxdsselectaction.cpp +++ b/LEGO1/mxdsselectaction.cpp @@ -33,6 +33,18 @@ void MxDSSelectAction::CopyFrom(MxDSSelectAction& p_dsSelectAction) this->m_unk0xac->Append(string); } +// SYNTHETIC: LEGO1 0x100cbbd0 +// MxStringListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x100cbc40 +// MxListCursor::~MxListCursor + +// SYNTHETIC: LEGO1 0x100cbc90 +// MxListCursor::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100cbd00 +// MxStringListCursor::~MxStringListCursor + // FUNCTION: LEGO1 0x100cbd50 MxDSSelectAction& MxDSSelectAction::operator=(MxDSSelectAction& p_dsSelectAction) { @@ -130,3 +142,6 @@ void MxDSSelectAction::Deserialize(char** p_source, MxS16 p_unk24) *p_source += extraFlag; } + +// TEMPLATE: LEGO1 0x100cc450 +// MxListEntry::GetValue diff --git a/LEGO1/mxlist.h b/LEGO1/mxlist.h index d44d6c8f..8ca3d199 100644 --- a/LEGO1/mxlist.h +++ b/LEGO1/mxlist.h @@ -100,18 +100,10 @@ class MxListCursor : public MxCore { MxListEntry* m_match; }; -// Unclear purpose template -class MxListCursorChild : public MxListCursor { +class MxPtrListCursor : public MxListCursor { public: - MxListCursorChild(MxList* p_list) : MxListCursor(p_list) {} -}; - -// Unclear purpose -template -class MxListCursorChildChild : public MxListCursorChild { -public: - MxListCursorChildChild(MxList* p_list) : MxListCursorChild(p_list) {} + MxPtrListCursor(MxPtrList* p_list) : MxListCursor(p_list){}; }; template diff --git a/LEGO1/mxmediapresenter.cpp b/LEGO1/mxmediapresenter.cpp index 314f89c7..2d18a839 100644 --- a/LEGO1/mxmediapresenter.cpp +++ b/LEGO1/mxmediapresenter.cpp @@ -21,6 +21,19 @@ void MxMediaPresenter::Destroy() Destroy(FALSE); } +// TODO: These probably belong in another class +// SYNTHETIC: LEGO1 0x100b46e0 +// MxStreamChunkListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x100b4750 +// MxListCursor::~MxListCursor + +// SYNTHETIC: LEGO1 0x100b47a0 +// MxListCursor::`scalar deleting destructor' + +// FUNCTION: LEGO1 0x100b4810 +// MxStreamChunkListCursor::~MxStreamChunkListCursor + // FUNCTION: LEGO1 0x100b54e0 void MxMediaPresenter::Init() { diff --git a/LEGO1/mxpresenterlist.h b/LEGO1/mxpresenterlist.h index d96374b9..23e7f6c2 100644 --- a/LEGO1/mxpresenterlist.h +++ b/LEGO1/mxpresenterlist.h @@ -15,7 +15,17 @@ class MxPresenterList : public MxPtrList { virtual MxS8 Compare(MxPresenter*, MxPresenter*) override; // vtable+0x14 }; -typedef MxListCursorChildChild MxPresenterListCursor; +// VTABLE: LEGO1 0x100d6488 +// class MxListCursor + +// VTABLE: LEGO1 0x100d6530 +// class MxPtrListCursor + +// VTABLE: LEGO1 0x100d6470 +class MxPresenterListCursor : public MxPtrListCursor { +public: + MxPresenterListCursor(MxPresenterList* p_list) : MxPtrListCursor(p_list){}; +}; // VTABLE: LEGO1 0x100d6350 // class MxCollection diff --git a/LEGO1/mxregion.cpp b/LEGO1/mxregion.cpp index afd546d6..0ac28be1 100644 --- a/LEGO1/mxregion.cpp +++ b/LEGO1/mxregion.cpp @@ -86,6 +86,24 @@ void MxRegion::vtable18(MxRect32& p_rect) m_rect.UpdateBounds(p_rect); } +// SYNTHETIC: LEGO1 0x100c3be0 +// MxRegionListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x100c3c50 +// MxPtrListCursor::~MxPtrListCursor + +// SYNTHETIC: LEGO1 0x100c3ca0 +// MxListCursor::`scalar deleting destructor' + +// SYNTHETIC: LEGO1 0x100c3d10 +// MxPtrListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x100c3d80 +// MxListCursor::~MxListCursor + +// FUNCTION: LEGO1 0x100c3dd0 +// MxRegionListCursor::~MxRegionListCursor + // FUNCTION: LEGO1 0x100c3e20 MxBool MxRegion::vtable1c(MxRect32& p_rect) { @@ -105,6 +123,21 @@ MxBool MxRegion::vtable1c(MxRect32& p_rect) return FALSE; } +// SYNTHETIC: LEGO1 0x100c4790 +// MxRegionLeftRightListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x100c4800 +// MxPtrListCursor::~MxPtrListCursor + +// SYNTHETIC: LEGO1 0x100c4850 +// MxListCursor::`scalar deleting destructor' + +// SYNTHETIC: LEGO1 0x100c48c0 +// MxPtrListCursor::`scalar deleting destructor' + +// TEMPLATE: LEGO1 0x100c4930 +// MxListCursor::~MxListCursor + // FUNCTION: LEGO1 0x100c4c90 MxRegionTopBottom::MxRegionTopBottom(MxS32 p_top, MxS32 p_bottom) { @@ -167,6 +200,15 @@ void MxRegionTopBottom::FUN_100c5280(MxS32 p_left, MxS32 p_right) } } +// TEMPLATE: LEGO1 0x100c54f0 +// MxListCursor::MxListCursor + +// FUNCTION: LEGO1 0x100c5560 +// MxRegionLeftRightListCursor::~MxRegionLeftRightListCursor + +// TEMPLATE: LEGO1 0x100c55b0 +// MxListCursor::operator= + // FUNCTION: LEGO1 0x100c55d0 MxRegionTopBottom* MxRegionTopBottom::Clone() { diff --git a/LEGO1/mxregionlist.h b/LEGO1/mxregionlist.h index 3aa0487b..2938e38b 100644 --- a/LEGO1/mxregionlist.h +++ b/LEGO1/mxregionlist.h @@ -23,13 +23,21 @@ class MxRegionList : public MxPtrList { static void Destroy(MxRegionTopBottom*); }; -// VTABLE: LEGO1 0x100dcb88 -// class MxListCursorChildChild -typedef MxListCursorChildChild MxRegionListCursor; +// VTABLE: LEGO1 0x100dcb70 +// class MxPtrListCursor -// VTABLE: LEGO1 0x100dcc10 -// class MxListCursorChildChild -typedef MxListCursorChildChild MxRegionLeftRightListCursor; +// VTABLE: LEGO1 0x100dcba0 +// class MxListCursor + +// TODO: The initialize list param type should be MxRegionList, but doing that +// drastically reduced the match percentage for MxRegion::vtable18. +// It also works with MxPtrList, so we'll do that until we figure this out. + +// VTABLE: LEGO1 0x100dcb88 +class MxRegionListCursor : public MxPtrListCursor { +public: + MxRegionListCursor(MxPtrList* p_list) : MxPtrListCursor(p_list){}; +}; // VTABLE: LEGO1 0x100dcc40 // class MxCollection @@ -48,4 +56,16 @@ class MxRegionLeftRightList : public MxPtrList { static void Destroy(MxRegionLeftRight*); }; +// VTABLE: LEGO1 0x100dcbf8 +// class MxPtrListCursor + +// VTABLE: LEGO1 0x100dcc28 +// class MxListCursor + +// VTABLE: LEGO1 0x100dcc10 +class MxRegionLeftRightListCursor : public MxPtrListCursor { +public: + MxRegionLeftRightListCursor(MxRegionLeftRightList* p_list) : MxPtrListCursor(p_list){}; +}; + #endif // MXREGIONLIST_H diff --git a/LEGO1/mxstreamchunklist.h b/LEGO1/mxstreamchunklist.h index 4924b6ff..9661229c 100644 --- a/LEGO1/mxstreamchunklist.h +++ b/LEGO1/mxstreamchunklist.h @@ -23,6 +23,13 @@ class MxStreamChunkList : public MxList { static void Destroy(MxStreamChunk* p_chunk); }; -typedef MxListCursorChild MxStreamChunkListCursor; +// VTABLE: LEGO1 0x100dc510 +class MxStreamChunkListCursor : public MxListCursor { +public: + MxStreamChunkListCursor(MxStreamChunkList* p_list) : MxListCursor(p_list){}; +}; + +// VTABLE: LEGO1 0x100dc528 +// class MxListCursor #endif // MXSTREAMCHUNKLIST_H diff --git a/LEGO1/mxstreamcontroller.cpp b/LEGO1/mxstreamcontroller.cpp index 2ecf0b43..cdc8eff3 100644 --- a/LEGO1/mxstreamcontroller.cpp +++ b/LEGO1/mxstreamcontroller.cpp @@ -51,13 +51,13 @@ MxStreamController::MxStreamController() // TEMPLATE: LEGO1 0x100c0ee0 // list >::_Buynode -// TEMPLATE: LEGO1 0x100c0fc0 +// FUNCTION: LEGO1 0x100c0fc0 // MxStreamListMxDSSubscriber::~MxStreamListMxDSSubscriber -// TEMPLATE: LEGO1 0x100c1010 +// FUNCTION: LEGO1 0x100c1010 // MxStreamListMxDSAction::~MxStreamListMxDSAction -// TEMPLATE: LEGO1 0x100c1060 +// FUNCTION: LEGO1 0x100c1060 // MxStreamListMxNextActionDataStart::~MxStreamListMxNextActionDataStart // TEMPLATE: LEGO1 0x100c10b0 diff --git a/LEGO1/mxstringlist.h b/LEGO1/mxstringlist.h index 08754405..cacb0be5 100644 --- a/LEGO1/mxstringlist.h +++ b/LEGO1/mxstringlist.h @@ -9,6 +9,12 @@ class MxStringList : public MxList {}; // VTABLE: LEGO1 0x100dd058 -typedef MxListCursorChild MxStringListCursor; +class MxStringListCursor : public MxListCursor { +public: + MxStringListCursor(MxStringList* p_list) : MxListCursor(p_list){}; +}; + +// VTABLE: LEGO1 0x100dd070 +// class MxListCursor #endif // MXSTRINGLIST_H From 2a16a508a500bb92c0832cb6def5a2b1bf991ab7 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Fri, 8 Dec 2023 06:37:44 -0500 Subject: [PATCH 6/7] (Proposal) Use alternative C4786 warning suppression (#312) * Use alternative warning suppression * Remove newline * Fix script * Patch C2.EXE to disable C4786 warning * Delete compile.cmd * py-fixes * Update tools/patch_c2.py * Update tools/patch_c2.py --------- Co-authored-by: Anonymous Maarten Co-authored-by: Anonymous Maarten --- .editorconfig | 2 +- .github/workflows/build.yml | 4 ++ .pylintrc | 4 +- CMakeLists.txt | 5 +- LEGO1/compat.h | 6 -- tools/patch_c2.py | 67 +++++++++++++++++++++ tools/reccmp/reccmp.py | 8 ++- tools/verexp/verexp.py | 116 ++++++++++++++++++++---------------- 8 files changed, 147 insertions(+), 65 deletions(-) create mode 100644 tools/patch_c2.py diff --git a/.editorconfig b/.editorconfig index 604cc306..c6a6539f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*.{py,txt,editorconfig}] indent_style = space -indent_size = 2 +indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8434d906..58cce4dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,6 +20,10 @@ jobs: # Use minimum supported version cmake-version: '3.13.x' + - name: Patch MSVC 4.2 + run: | + python tools/patch_c2.py msvc420/bin/C2.EXE + - name: Build shell: cmd run: | diff --git a/.pylintrc b/.pylintrc index 7afd8185..ab83fceb 100644 --- a/.pylintrc +++ b/.pylintrc @@ -165,7 +165,7 @@ class-naming-style=PascalCase #class-rgx= # Naming style matching correct constant names. -const-naming-style=snake_case +const-naming-style=UPPER_CASE # Regular expression matching correct constant names. Overrides const-naming- # style. If left empty, constant names will be checked with the set naming @@ -511,7 +511,7 @@ ignore-imports=yes ignore-signatures=yes # Minimum lines number of a similarity. -min-similarity-lines=4 +min-similarity-lines=16 [SPELLING] diff --git a/CMakeLists.txt b/CMakeLists.txt index 9efb9c99..4a60e3d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,8 +289,9 @@ if (MSVC) # These flags have been taken from the defaults for a Visual C++ 4.20 project (the compiler the # game was originally built with) and tweaked slightly to produce more debugging info for reccmp. # They ensure a recompilation that can be byte/instruction accurate to the original binaries. - - target_compile_options(isle PRIVATE "/ML$<$:d>") + if (ISLE_BUILD_APP) + target_compile_options(isle PRIVATE "/ML$<$:d>") + endif() target_compile_options(lego1 PRIVATE "/MT$<$:d>") set(CMAKE_CXX_FLAGS "/W3 /GX /D \"WIN32\" /D \"_WINDOWS\"") diff --git a/LEGO1/compat.h b/LEGO1/compat.h index d9cc2170..9616b254 100644 --- a/LEGO1/compat.h +++ b/LEGO1/compat.h @@ -18,12 +18,6 @@ // Impossible to avoid this if using STL map or set. // This removes most (but not all) occurrences of the warning. #pragma warning(disable : 4786) -// To really remove *all* of the warnings, we have to employ the following, -// obscure workaround from https://www.earthli.com/news/view_article.php?id=376 -static class msVC6_4786WorkAround { -public: - msVC6_4786WorkAround() {} -} msVC6_4786WorkAround; #define MSVC420_VERSION 1020 diff --git a/tools/patch_c2.py b/tools/patch_c2.py new file mode 100644 index 00000000..58cc4760 --- /dev/null +++ b/tools/patch_c2.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +import argparse +import hashlib +import pathlib +import shutil + +ORIGINAL_C2_MD5 = "dcd69f1dd28b02dd03dd7ed02984299a" # original C2.EXE + +C2_MD5 = ( + ORIGINAL_C2_MD5, + "e70acde41802ddec06c4263bb357ac30", # patched C2.EXE +) + +C2_SIZE = 549888 + + +def main(): + parser = argparse.ArgumentParser( + allow_abbrev=False, + description="Path to C2.EXE of Microsoft Visual Studio 4.2.0 to disable C4786 warning", + ) + parser.add_argument("path", type=pathlib.Path, help="Path of C2.EXE") + parser.add_argument( + "-f", dest="force", default=False, action="store_true", help="force" + ) + args = parser.parse_args() + + if not args.path.is_file(): + parser.error("Input is not a file") + + binary = bytearray(args.path.open("rb").read()) + md5 = hashlib.md5(binary).hexdigest() + print(md5, C2_MD5) + + msg_cb = parser.error if not args.force else print + if len(binary) != C2_SIZE: + msg_cb("file size is not correct") + if md5 not in C2_MD5: + msg_cb("md5 checksum does not match") + + if md5 == ORIGINAL_C2_MD5: + backup = f"{args.path}.BAK" + print(f'Creating backup "{backup}"') + shutil.copyfile(args.path, backup) + + def nop_patch(start, count, expected=None): + replacement = [0x90] * count + if expected: + current = list(binary[start : start + count]) + assert len(expected) == count + assert current in (expected, replacement) + print(f"Nopping {count} bytes at 0x{start:08x}") + binary[start : start + count] = replacement + + print( + "Disable C4786 warning: '%Fs' : identifier was truncated to '%d' characters in the debug information" + ) + nop_patch(0x52F07, 5, [0xE8, 0x4F, 0xB3, 0xFE, 0xFF]) # 0x00453b07 + nop_patch(0x74832, 5, [0xE8, 0x24, 0x9A, 0xFC, 0xFF]) # 0x00475432 + + args.path.open("wb").write(binary) + print("done") + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/reccmp/reccmp.py b/tools/reccmp/reccmp.py index d2a7f23a..98a8a4d9 100755 --- a/tools/reccmp/reccmp.py +++ b/tools/reccmp/reccmp.py @@ -23,6 +23,7 @@ import colorama from pystache import Renderer + REGISTER_LIST = set( [ "ax", @@ -200,7 +201,8 @@ def gen_svg(svg_file, name_svg, icon, svg_implemented_funcs, total_funcs, raw_ac # Do the actual work -if __name__ == "__main__": +def main(): + # pylint: disable=too-many-locals, too-many-nested-blocks, too-many-branches, too-many-statements parser = argparse.ArgumentParser( allow_abbrev=False, description="Recompilation Compare: compare an original EXE with a recompiled EXE + PDB.", @@ -483,3 +485,7 @@ def gen_svg(svg_file, name_svg, icon, svg_implemented_funcs, total_funcs, raw_ac function_count, total_effective_accuracy, ) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/verexp/verexp.py b/tools/verexp/verexp.py index 37e3e641..455791e1 100755 --- a/tools/verexp/verexp.py +++ b/tools/verexp/verexp.py @@ -8,61 +8,71 @@ from isledecomp.utils import print_diff -parser = argparse.ArgumentParser( - allow_abbrev=False, description="Verify Exports: Compare the exports of two DLLs." -) -parser.add_argument("original", metavar="original-binary", help="The original binary") -parser.add_argument( - "recompiled", metavar="recompiled-binary", help="The recompiled binary" -) -parser.add_argument( - "--no-color", "-n", action="store_true", help="Do not color the output" -) -args = parser.parse_args() +def main(): + parser = argparse.ArgumentParser( + allow_abbrev=False, + description="Verify Exports: Compare the exports of two DLLs.", + ) + parser.add_argument( + "original", metavar="original-binary", help="The original binary" + ) + parser.add_argument( + "recompiled", metavar="recompiled-binary", help="The recompiled binary" + ) + parser.add_argument( + "--no-color", "-n", action="store_true", help="Do not color the output" + ) -if not os.path.isfile(args.original): - parser.error(f"Original binary file {args.original} does not exist") + args = parser.parse_args() -if not os.path.isfile(args.recompiled): - parser.error(f"Recompiled binary {args.recompiled} does not exist") + if not os.path.isfile(args.original): + parser.error(f"Original binary file {args.original} does not exist") + + if not os.path.isfile(args.recompiled): + parser.error(f"Recompiled binary {args.recompiled} does not exist") + + def get_file_in_script_dir(fn): + return os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), fn) + + def get_exports(file): + call = [get_file_in_script_dir("DUMPBIN.EXE"), "/EXPORTS"] + + if os.name != "nt": + call.insert(0, "wine") + file = ( + subprocess.check_output(["winepath", "-w", file]) + .decode("utf-8") + .strip() + ) + + call.append(file) + + raw = subprocess.check_output(call).decode("utf-8").split("\r\n") + exports = [] + + start = False + + for line in raw: + if not start: + if line == " ordinal hint name": + start = True + else: + if line: + exports.append(line[27 : line.rindex(" (")]) + elif exports: + break + + return exports + + og_exp = get_exports(args.original) + re_exp = get_exports(args.recompiled) + + udiff = difflib.unified_diff(og_exp, re_exp) + has_diff = print_diff(udiff, args.no_color) + + return 1 if has_diff else 0 -def get_file_in_script_dir(fn): - return os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), fn) - - -def get_exports(file): - call = [get_file_in_script_dir("DUMPBIN.EXE"), "/EXPORTS"] - - if os.name != "nt": - call.insert(0, "wine") - file = subprocess.check_output(["winepath", "-w", file]).decode("utf-8").strip() - - call.append(file) - - raw = subprocess.check_output(call).decode("utf-8").split("\r\n") - exports = [] - - start = False - - for line in raw: - if not start: - if line == " ordinal hint name": - start = True - else: - if line: - exports.append(line[27 : line.rindex(" (")]) - elif exports: - break - - return exports - - -og_exp = get_exports(args.original) -re_exp = get_exports(args.recompiled) - -udiff = difflib.unified_diff(og_exp, re_exp) -has_diff = print_diff(udiff, args.no_color) - -sys.exit(1 if has_diff else 0) +if __name__ == "__main__": + raise SystemExit(main()) From 7a0558f99d4eeaccfda3f52053aa34c6e65d0ed6 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Fri, 8 Dec 2023 07:28:25 -0500 Subject: [PATCH 7/7] Use better approximation for total function count --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58cce4dc..2c285836 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -83,7 +83,7 @@ jobs: shell: bash run: | python3 tools/reccmp/reccmp.py -S ISLEPROGRESS.SVG --svg-icon tools/reccmp/isle.png -H ISLEPROGRESS.HTML legobin/ISLE.EXE build/ISLE.EXE build/ISLE.PDB . | tee ISLEPROGRESS.TXT - python3 tools/reccmp/reccmp.py -S LEGO1PROGRESS.SVG -T 1929 --svg-icon tools/reccmp/lego1.png -H LEGO1PROGRESS.HTML legobin/LEGO1.DLL build/LEGO1.DLL build/LEGO1.PDB . | tee LEGO1PROGRESS.TXT + python3 tools/reccmp/reccmp.py -S LEGO1PROGRESS.SVG -T 4252 --svg-icon tools/reccmp/lego1.png -H LEGO1PROGRESS.HTML legobin/LEGO1.DLL build/LEGO1.DLL build/LEGO1.PDB . | tee LEGO1PROGRESS.TXT - name: Compare Accuracy With Current Master shell: bash