diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af1dac3b..c846a9f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: uses: jwlawson/actions-setup-cmake@v1.13 with: # Use minimum supported version - cmake-version: '3.0.x' + cmake-version: '3.13.x' - name: Build shell: cmd diff --git a/.gitignore b/.gitignore index 14ad2717..402fb660 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ Release/ ISLE.EXE LEGO1.DLL build/ +*.swp diff --git a/3rdparty/smartheap/SHLW32MT.LIB b/3rdparty/smartheap/SHLW32MT.LIB new file mode 100644 index 00000000..732aedf8 Binary files /dev/null and b/3rdparty/smartheap/SHLW32MT.LIB differ diff --git a/3rdparty/smartheap/SHMALLOC.H b/3rdparty/smartheap/SHMALLOC.H new file mode 100644 index 00000000..709c19b8 --- /dev/null +++ b/3rdparty/smartheap/SHMALLOC.H @@ -0,0 +1,63 @@ +/* shmalloc.h -- SmartHeap ANSI Standard C memory API + * Professional Memory Management Library + * + * Copyright (C) 1991-1996 by Arthur D. Applegate. All Rights Reserved. + * All Rights Reserved. + * + * No part of this source code may be copied, modified or reproduced + * in any form without retaining the above copyright notice. + * This source code, or source code derived from it, may not be redistributed + * without express written permission of the author. + */ + +#if !(defined(_SHMALLOC_H)) +#define _SHMALLOC_H + +#include "smrtheap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ANSI Standard Memory Management API */ + +#if (!defined(MEM_DEBUG) && !defined(NO_MALLOC_MACRO)) || defined(MALLOC_MACRO) +#ifdef malloc +#undef malloc +#endif +#define malloc(s) MEM_malloc(s) +#ifdef calloc +#undef calloc +#endif +#define calloc(s,c) MEM_calloc(s,c) +#ifdef realloc +#undef realloc +#endif +#define realloc(p,s) MEM_realloc(p,s) +#ifdef free +#undef free +#endif +#define free(p) MEM_free(p) + +#endif /* NO_MALLOC_MACRO */ + +#ifndef MEM_malloc +void MEM_FAR * MEM_ENTRY_ANSI MEM_malloc(size_t size); +void MEM_FAR * MEM_ENTRY_ANSI MEM_calloc(size_t nobj, size_t size); +void MEM_FAR * MEM_ENTRY_ANSI MEM_realloc(void MEM_FAR *p, size_t size); +void MEM_ENTRY_ANSI MEM_free(void MEM_FAR *p); +#endif /* MEM_malloc */ + +#if defined(__WATCOMC__) && defined(__SW_3S) +/* Watcom stack calling convention */ + #pragma aux (syscall) MEM_malloc + #pragma aux (syscall) MEM_realloc + #pragma aux (syscall) MEM_calloc + #pragma aux (syscall) MEM_free +#endif /* __WATCOMC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(_SHMALLOC_H) */ diff --git a/3rdparty/smartheap/SHMFC4M.LIB b/3rdparty/smartheap/SHMFC4M.LIB new file mode 100644 index 00000000..a8f04163 Binary files /dev/null and b/3rdparty/smartheap/SHMFC4M.LIB differ diff --git a/3rdparty/smartheap/SMRTHEAP.H b/3rdparty/smartheap/SMRTHEAP.H new file mode 100644 index 00000000..b4f17a74 --- /dev/null +++ b/3rdparty/smartheap/SMRTHEAP.H @@ -0,0 +1,656 @@ +/* smrtheap.h -- SmartHeap (tm) public C header file + * Professional Memory Management Library + * + * Copyright (C) 1991-1997 by Arthur D. Applegate. All Rights Reserved. + * All Rights Reserved. + * + * No part of this source code may be copied, modified or reproduced + * in any form without retaining the above copyright notice. + * This source code, or source code derived from it, may not be redistributed + * without express written permission of the author. + * + */ + +#if !defined(_SMARTHEAP_H) +#define _SMARTHEAP_H + +#include +#include + +#if !defined(macintosh) && !defined(THINK_C) && !defined(__MWERKS__) \ + && !defined(SHANSI) && UINT_MAX == 0xFFFFu \ + && (defined(_Windows) || defined(_WINDOWS) || defined(__WINDOWS__)) + #define MEM_WIN16 +#endif + +#if (UINT_MAX == 0xFFFFu) && (defined(MEM_WIN16) \ + || defined(MSDOS) || defined(__MSDOS__) || defined(__DOS__)) + /* 16-bit X86 */ + #if defined(SYS_DLL) + #if defined(_MSC_VER) && _MSC_VER <= 600 + #define MEM_ENTRY _export _loadds far pascal + #else + #define MEM_ENTRY _export far pascal + #endif + #else + #define MEM_ENTRY far pascal + #endif + #ifdef __WATCOMC__ + #define MEM_ENTRY_ANSI __far + #else + #define MEM_ENTRY_ANSI far cdecl + #endif + #define MEM_FAR far + #if defined(MEM_WIN16) + #define MEM_ENTRY2 _export far pascal + #elif defined(DOS16M) || defined(DOSX286) + #define MEM_ENTRY2 _export _loadds far pascal + #endif + +#else /* not 16-bit X86 */ + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) \ + || defined(__WIN32__) || defined(__NT__) + #define MEM_WIN32 + #if defined(_MSC_VER) + #if defined(_SHI_Pool) && defined(SYS_DLL) + #define MEM_ENTRY1 __declspec(dllexport) + #define MEM_ENTRY4 __declspec(dllexport) extern + #elif !defined(_SHI_Pool) && (defined(MEM_DEBUG) || defined(MEM_DLL)) + #define MEM_ENTRY1 __declspec(dllimport) + #if defined(_M_IX86) || defined(_X86_) + #define MemDefaultPool shi_MemDefaultPool + #define MEM_ENTRY4 __declspec(dllimport) + #endif + #endif + #endif + #if !defined(_MSC_VER) || defined(_M_IX86) || defined(_X86_) + #define MEM_ENTRY __stdcall + #else + #define MEM_ENTRY __cdecl /* for NT/RISC */ + #endif + #ifndef __WATCOMC__ + #define MEM_ENTRY_ANSI __cdecl + #endif + +#elif defined(__OS2__) + #if defined(__BORLANDC__) || defined(__WATCOMC__) + #if defined(SYS_DLL) + #define MEM_ENTRY __export __syscall + #else + #define MEM_ENTRY __syscall + #endif /* SYS_DLL */ + #ifdef __BORLANDC__ + #define MEM_ENTRY_ANSI __stdcall + #endif + #elif defined(__IBMC__) || defined(__IBMCPP__) + #if defined(SYS_DLL) && 0 + #define MEM_ENTRY _Export _System + #else + #define MEM_ENTRY _System + #endif + #define MEM_ENTRY_ANSI _Optlink + #define MEM_ENTRY3 MEM_ENTRY + #define MEM_CALLBACK MEM_ENTRY3 + #define MEM_ENTRY2 + #endif +#endif /* __OS2__ */ + +#if defined(__WATCOMC__) && defined(__SW_3S) + /* Watcom stack calling convention */ +#ifndef __OS2__ +#ifdef __WINDOWS_386__ + #pragma aux syscall "*_" parm routine [eax ebx ecx edx fs gs] modify [eax]; +#else + #pragma aux syscall "*_" parm routine [eax ebx ecx edx] modify [eax]; +#endif +#ifndef MEM_ENTRY + #define MEM_ENTRY __syscall +#endif /* MEM_ENTRY */ +#endif +#endif /* Watcom stack calling convention */ + +#endif /* end of system-specific declarations */ + +#ifndef MEM_ENTRY + #define MEM_ENTRY +#endif +#ifndef MEM_ENTRY1 + #define MEM_ENTRY1 +#endif +#ifndef MEM_ENTRY2 + #define MEM_ENTRY2 MEM_ENTRY +#endif +#ifndef MEM_ENTRY3 + #define MEM_ENTRY3 +#endif +#ifndef MEM_ENTRY4 + #define MEM_ENTRY4 extern +#endif +#ifndef MEM_CALLBACK +#define MEM_CALLBACK MEM_ENTRY2 +#endif +#ifndef MEM_ENTRY_ANSI + #define MEM_ENTRY_ANSI +#endif +#ifndef MEM_FAR + #define MEM_FAR +#endif + +#ifdef applec +/* Macintosh: Apple MPW C/C++ passes char/short parms as longs (4 bytes), + * whereas Symantec C/C++ for MPW passes these as words (2 bytes); + * therefore, canonicalize all integer parms as 'int' for this platform. + */ + #define MEM_USHORT unsigned + #define MEM_UCHAR unsigned +#else + #define MEM_USHORT unsigned short + #define MEM_UCHAR unsigned char +#endif /* applec */ + +#ifdef MEM_DEBUG +#include "heapagnt.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*** Types ***/ + +#ifndef MEM_BOOL_DEFINED +#define MEM_BOOL_DEFINED +typedef int MEM_BOOL; +#endif + +/* Version Masks */ +typedef unsigned MEM_VERSION; +#define MEM_MAJOR_VERSION(v) (((v) & 0xF000u) >> 12) +#define MEM_MINOR_VERSION(v) (((v) & 0x0F00u) >> 8) +#define MEM_UPDATE_VERSION(v) ((v) & 0x00FFu) + +/* Note: these types are struct's rather than integral types to facilitate + * compile-time type-checking. MEM_POOL and MEM_HANDLE should be regarded + * as black boxes, and treated just like handles. + * You should not have any type casts to or from MEM_POOL or MEM_HANDLE; + * nor should you dereference variables of type MEM_POOL or MEM_HANDLE + * (unless you are using SmartHeap to replace NewHandle on the Mac, and + * you have existing code that dereferences handles). + */ +#ifndef MEM_POOL_DEFINED +#define MEM_POOL_DEFINED +#ifdef _SHI_Pool + typedef struct _SHI_Pool MEM_FAR *MEM_POOL; + typedef struct _SHI_MovHandle MEM_FAR *MEM_HANDLE; +#else + #ifdef THINK_C + typedef void *MEM_POOL; + typedef void **MEM_HANDLE; + #else + typedef struct _SHI_Pool { int reserved; } MEM_FAR *MEM_POOL; + typedef struct _SHI_MovHandle { int reserved; } MEM_FAR *MEM_HANDLE; + #endif +#endif +#endif /* MEM_POOL_DEFINED */ + + +#if !defined(MEM_DEBUG) || !(defined(MEM_WIN16) || defined(MEM_WIN32)) +#define SHI_MAJOR_VERSION 3 +#define SHI_MINOR_VERSION 3 +#define SHI_UPDATE_LEVEL 0 +#endif /* !WINDOWS */ + +#ifndef MEM_DEBUG + +/* Error codes: errorCode field of MEM_ERROR_INFO */ +#ifndef MEM_ERROR_CODE_DEFINED +#define MEM_ERROR_CODE_DEFINED +typedef enum +{ + MEM_NO_ERROR=0, + MEM_INTERNAL_ERROR, + MEM_OUT_OF_MEMORY, + MEM_BLOCK_TOO_BIG, + MEM_ALLOC_ZERO, + MEM_RESIZE_FAILED, + MEM_LOCK_ERROR, + MEM_EXCEEDED_CEILING, + MEM_TOO_MANY_PAGES, + MEM_TOO_MANY_TASKS, + MEM_BAD_MEM_POOL, + MEM_BAD_BLOCK, + MEM_BAD_FREE_BLOCK, + MEM_BAD_HANDLE, + MEM_BAD_POINTER, + MEM_WRONG_TASK, + MEM_NOT_FIXED_SIZE, + MEM_BAD_FLAGS, + MEM_ERROR_CODE_INT_MAX = INT_MAX /* to ensure enum is full int in size */ +} MEM_ERROR_CODE; +#endif /* MEM_ERROR_CODE_DEFINED */ + + +/* ### we should have packing pragma around these structure decls in case + * ### user is compiling with non-default packing directive (only needed + * ### if user is packing more strictly than native int size *and* if + * ### native int size is != native long and ptr sizes) + */ + +/* Error info, passed to error-handling callback routine */ +#ifndef MEM_ERROR_INFO_DEFINED +#define MEM_ERROR_INFO_DEFINED +typedef struct _MEM_ERROR_INFO +{ + MEM_ERROR_CODE errorCode; /* error code identifying type of error */ + MEM_POOL pool; /* pool in which error occurred, if known */ +} MEM_ERROR_INFO; + +/* Error handling callback function */ +typedef MEM_BOOL (MEM_ENTRY2 * MEM_ENTRY3 MEM_ERROR_FN) + (MEM_ERROR_INFO MEM_FAR *); + +#endif /* MEM_ERROR_INFO_DEFINED */ + +#endif /* MEM_DEBUG */ + + +#ifndef MEM_BLOCK_TYPE_DEFINED +#define MEM_BLOCK_TYPE_DEFINED +/* Block Type: field of MEM_POOL_ENTRY, field of MEM_POOL_INFO, + * parameter to MemPoolPreAllocate + */ +typedef enum +{ + MEM_FS_BLOCK = 0x0001u, + MEM_VAR_MOVEABLE_BLOCK = 0x0002u, + MEM_VAR_FIXED_BLOCK = 0x0004u, + MEM_EXTERNAL_BLOCK = 0x0008u, + MEM_BLOCK_TYPE_INT_MAX = INT_MAX /* to ensure enum is full int in size */ +} MEM_BLOCK_TYPE; +#endif /* MEM_BLOCK_TYPE_DEFINED */ + +#ifndef MEM_POOL_ENTRY_DEFINED +#define MEM_POOL_ENTRY_DEFINED +/* Pool Entry: parameter to MemPoolWalk */ +typedef struct +{ + void MEM_FAR *entry; + MEM_POOL pool; + MEM_BLOCK_TYPE type; + MEM_BOOL isInUse; + unsigned long size; + MEM_HANDLE handle; + unsigned lockCount; + void MEM_FAR *reserved_ptr; +} MEM_POOL_ENTRY; +#endif /* MEM_POOL_ENTRY_DEFINED */ + +#ifndef MEM_POOL_STATUS_DEFINED +#define MEM_POOL_STATUS_DEFINED +/* Pool Status: returned by MemPoolWalk, MemPoolFirst, MemPoolNext */ +typedef enum +{ + MEM_POOL_OK = 1, + MEM_POOL_CORRUPT = -1, + MEM_POOL_CORRUPT_FATAL = -2, + MEM_POOL_END = 0, + MEM_POOL_STATUS_INT_MAX = INT_MAX /* to ensure enum is full int in size */ +} MEM_POOL_STATUS; +#endif /* MEM_POOL_STATUS_DEFINED */ + +#ifndef MEM_POINTER_STATUS_DEFINED +#define MEM_POINTER_STATUS_DEFINED +/* Pointer Status: returned by MemCheckPtr */ +typedef enum +{ + MEM_POINTER_OK = 1, + MEM_POINTER_WILD = 0, + MEM_POINTER_FREE = -1, + MEM_POINTER_STATUS_INT_MAX = INT_MAX /* to ensure enum is full int in size */ +} MEM_POINTER_STATUS; +#endif /* MEM_POINTER_STATUS_DEFINED */ + +/* Pool Info: parameter to MemPoolInfo, MemPoolFirst, MemPoolNext */ +typedef struct +{ + MEM_POOL pool; + MEM_BLOCK_TYPE type; /* disjunctive combination of block type flags */ + unsigned short blockSizeFS; + unsigned short smallBlockSize; + unsigned pageSize; + unsigned long floor; + unsigned long ceiling; + unsigned flags; + MEM_ERROR_FN errorFn; +} MEM_POOL_INFO; + +/* Flags passed to MemAlloc, MemAllocPtr, MemReAlloc, MemReAllocPtr */ +#define MEM_FIXED 0x0000u /* fixed handle-based block */ +#define MEM_ZEROINIT 0x0001u /* == TRUE for SH 1.5 compatibility */ +#define MEM_MOVEABLE 0x0002u /* moveable handle-based block */ +#define MEM_RESIZEABLE 0x0004u /* reserve space above block */ +#define MEM_RESIZE_IN_PLACE 0x0008u /* do not move block (realloc) */ +#define MEM_NOGROW 0x0010u /* do not grow heap to satisfy request */ +#define MEM_NOEXTERNAL 0x0020u /* reserved for internal use */ +#define MEM_NOCOMPACT 0x0040u /* do not compact to satisfy request */ +#define MEM_NO_SERIALIZE 0x0080u /* do not serialize this request */ +#define MEM_HANDLEBASED 0x4000u /* for internal use */ +#define MEM_RESERVED 0x8000u /* for internal use */ + +#define MEM_UNLOCK_FAILED USHRT_MAX + +/* Flags passed to MemPoolInit, MemPoolInitFS */ +#ifndef MEM_POOL_SHARED +#define MEM_POOL_SHARED 0x0001u /* == TRUE for SH 1.5 compatibility */ +#define MEM_POOL_SERIALIZE 0x0002u /* pool used in more than one thread */ +#define MEM_POOL_VIRTUAL_LOCK 0x0004u /* pool is locked in physical memory */ +#define MEM_POOL_ZEROINIT 0x0008u /* malloc/new from pool zero-inits */ +#define MEM_POOL_REGION 0x0010u /* store pool in user-supplied region*/ +#define MEM_POOL_DEFAULT 0x8000u /* pool with default characteristics */ +#endif /* MEM_POOL_SHARED */ + +MEM_ENTRY4 MEM_POOL MemDefaultPool; + +/* Default memory pool for C malloc, C++ new (for backwards compatibility) */ +#define MEM_DEFAULT_POOL MemDefaultPool + +/* define and initialize these variables at file scope to change defaults */ +extern unsigned short MemDefaultPoolBlockSizeFS; +extern unsigned MemDefaultPoolPageSize; +extern unsigned MemDefaultPoolFlags; + +/* define SmartHeap_malloc at file scope if you + * are intentionally _NOT_ linking in the SmartHeap malloc definition + * ditto for SmartHeap operator new, and fmalloc et al. + */ +extern int SmartHeap_malloc; +extern int SmartHeap_far_malloc; +extern int SmartHeap_new; + +#define MEM_ERROR_RET ULONG_MAX + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* !defined(_SMARTHEAP_H) */ + + +/*** Function Prototypes ***/ + +#ifndef _SMARTHEAP_PROT +#define _SMARTHEAP_PROT + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _shAPI + #if defined(MEM_DEBUG) && !defined(SHI_NO_MEM_DEBUG) + #define _shAPI(ret, name) MEM_ENTRY1 ret MEM_ENTRY _dbg ## name + #else + #define _shAPI(ret, name) MEM_ENTRY1 ret MEM_ENTRY name + #endif +#endif + +#ifndef _dbgARGS + #if defined(MEM_DEBUG) && !defined(SHI_NO_MEM_DEBUG) + #define _dbgARGS1 const char MEM_FAR *, int + #define _dbgARGS , _dbgARGS1 + #else + #define _dbgARGS1 void + #define _dbgARGS + #endif +#endif + + +/**** HOW TO READ SmartHeap PROTOTYPES **** + * prototypes below have the follow syntax in order to support both debug + * and non-debug APIs with single-source: + * + * _shiAPI(, )([] _dbgARGS); + * + * the above translates to a C prototype as follows: + * + * ([]); + */ + +/* Library Version */ +MEM_ENTRY1 MEM_VERSION MEM_ENTRY MemVersion(void); + +/* Library Registration */ +_shAPI(MEM_BOOL, MemRegisterTask)(_dbgARGS1); +_shAPI(MEM_BOOL, MemUnregisterTask)(_dbgARGS1); + +/* Memory Pool Functions */ +_shAPI(MEM_POOL, MemPoolInit)(unsigned _dbgARGS); +_shAPI(MEM_POOL, MemPoolInitFS)(MEM_USHORT, unsigned long, + unsigned _dbgARGS); +_shAPI(MEM_POOL, MemPoolInitRegion)(void MEM_FAR *, + unsigned long size, unsigned _dbgARGS); +_shAPI(MEM_POOL, MemPoolInitNamedShared)(const char MEM_FAR *, + unsigned long size,unsigned _dbgARGS); +_shAPI(MEM_POOL, MemPoolInitNamedSharedEx)(void MEM_FAR *addr, + unsigned pidCount, unsigned long MEM_FAR *pids, void MEM_FAR *security, + const char MEM_FAR *name, unsigned long size, unsigned flags _dbgARGS); +_shAPI(MEM_POOL, MemPoolAttachShared)(MEM_POOL, const char MEM_FAR * _dbgARGS); +_shAPI(MEM_BOOL, MemPoolFree)(MEM_POOL _dbgARGS); +MEM_POOL MEM_ENTRY MemInitDefaultPool(void); +MEM_BOOL MEM_ENTRY MemFreeDefaultPool(void); +_shAPI(unsigned, MemPoolSetPageSize)(MEM_POOL, unsigned _dbgARGS); +_shAPI(MEM_BOOL, MemPoolSetBlockSizeFS)(MEM_POOL, MEM_USHORT _dbgARGS); +_shAPI(MEM_BOOL, MemPoolSetSmallBlockSize)(MEM_POOL, MEM_USHORT _dbgARGS); +_shAPI(unsigned long, MemPoolSetFloor)(MEM_POOL, unsigned long _dbgARGS); +_shAPI(unsigned long, MemPoolSetCeiling)(MEM_POOL, unsigned long _dbgARGS); +_shAPI(unsigned long, MemPoolPreAllocate)(MEM_POOL, unsigned long, + MEM_BLOCK_TYPE _dbgARGS); +_shAPI(unsigned long, MemPoolPreAllocateHandles)(MEM_POOL, + unsigned long _dbgARGS); +_shAPI(unsigned long, MemPoolShrink)(MEM_POOL _dbgARGS); +_shAPI(unsigned long, MemPoolSize)(MEM_POOL _dbgARGS); +_shAPI(unsigned long, MemPoolCount)(MEM_POOL _dbgARGS); +_shAPI(MEM_BOOL, MemPoolInfo)(MEM_POOL, void MEM_FAR *, + MEM_POOL_INFO MEM_FAR* _dbgARGS); +_shAPI(MEM_POOL_STATUS, MemPoolFirst)(MEM_POOL_INFO MEM_FAR *, + MEM_BOOL _dbgARGS); +_shAPI(MEM_POOL_STATUS,MemPoolNext)(MEM_POOL_INFO MEM_FAR*,MEM_BOOL _dbgARGS); +_shAPI(MEM_POOL_STATUS,MemPoolWalk)(MEM_POOL,MEM_POOL_ENTRY MEM_FAR*_dbgARGS); +_shAPI(MEM_BOOL, MemPoolCheck)(MEM_POOL _dbgARGS); +_shAPI(MEM_BOOL, MemPoolLock)(MEM_POOL _dbgARGS); +_shAPI(MEM_BOOL, MemPoolUnlock)(MEM_POOL _dbgARGS); + +/* Handle-based API for moveable memory within heap. */ +_shAPI(MEM_HANDLE, MemAlloc)(MEM_POOL, unsigned, unsigned long _dbgARGS); +_shAPI(MEM_HANDLE, MemReAlloc)(MEM_HANDLE,unsigned long,unsigned _dbgARGS); +_shAPI(MEM_BOOL, MemFree)(MEM_HANDLE _dbgARGS); +_shAPI(void MEM_FAR *, MemLock)(MEM_HANDLE _dbgARGS); +_shAPI(unsigned, MemUnlock)(MEM_HANDLE _dbgARGS); +_shAPI(void MEM_FAR *, MemFix)(MEM_HANDLE _dbgARGS); +_shAPI(unsigned, MemUnfix)(MEM_HANDLE _dbgARGS); +_shAPI(unsigned, MemLockCount)(MEM_HANDLE _dbgARGS); +#ifndef MemFlags +#define MemFlags(mem) MemLockCount(mem) +#endif +_shAPI(MEM_BOOL, MemIsMoveable)(MEM_HANDLE _dbgARGS); +_shAPI(unsigned long, MemSize)(MEM_HANDLE _dbgARGS); +_shAPI(unsigned long, MemSizeRequested)(MEM_HANDLE _dbgARGS); +_shAPI(MEM_HANDLE, MemHandle)(void MEM_FAR * _dbgARGS); +#ifndef MEM_REFERENCE + #ifdef MEM_DEBUG + MEM_ENTRY1 void MEM_FAR * MEM_ENTRY _dbgMemReference(MEM_HANDLE, + const char MEM_FAR *, int); + #define MEM_REFERENCE(handle) \ + _dbgMemReference(handle, __FILE__, __LINE__) + #else + #define MEM_REFERENCE(handle) (*(void MEM_FAR * MEM_FAR *)handle) + #endif +#endif + +/* General Heap Allocator (returns direct pointer to memory) */ +_shAPI(void MEM_FAR*,MemAllocPtr)(MEM_POOL,unsigned long,unsigned _dbgARGS); +_shAPI(void MEM_FAR *, MemReAllocPtr)(void MEM_FAR *, unsigned long, + unsigned _dbgARGS); +_shAPI(MEM_BOOL, MemFreePtr)(void MEM_FAR * _dbgARGS); +_shAPI(unsigned long, MemSizePtr)(void MEM_FAR * _dbgARGS); +_shAPI(MEM_POINTER_STATUS, MemCheckPtr)(MEM_POOL, void MEM_FAR * _dbgARGS); + +/* Fixed-Size Allocator */ +_shAPI(void MEM_FAR *, MemAllocFS)(MEM_POOL _dbgARGS); +_shAPI(MEM_BOOL, MemFreeFS)(void MEM_FAR * _dbgARGS); + +/* Error Handling Functions */ +MEM_ENTRY1 MEM_ERROR_FN MEM_ENTRY MemSetErrorHandler(MEM_ERROR_FN); +MEM_ENTRY1 MEM_BOOL MEM_ENTRY MemDefaultErrorHandler(MEM_ERROR_INFO MEM_FAR*); +MEM_ENTRY1 void MEM_ENTRY MemErrorUnwind(void); + +#ifdef MEM_WIN32 +/* patching control */ + +#ifndef MEM_PATCHING_DEFINED +#define MEM_PATCHING_DEFINED +typedef enum +{ + MEM_PATCH_ALL = 0, + MEM_SKIP_PATCHING_THIS_DLL = 1, + MEM_DISABLE_SYSTEM_HEAP_PATCHING = 2, + MEM_DISABLE_ALL_PATCHING = 4|2|1, + MEM_PATCHING_INT_MAX = INT_MAX /* to ensure enum is full int in size */ +} MEM_PATCHING; +#endif /* MEM_PATCHING_DEFINED */ + +#ifdef _MSC_VER +__declspec(dllexport) +#endif +MEM_PATCHING MEM_ENTRY MemSetPatching(const char ***skipDLLs); + +#endif /* MEM_WIN32 */ + +/* internal routines */ +MEM_ENTRY1 MEM_BOOL MEM_ENTRY _shi_enterCriticalSection(void); +MEM_ENTRY1 void MEM_ENTRY _shi_leaveCriticalSection(void); +MEM_BOOL shi_call_new_handler_msc(size_t, MEM_BOOL); + + +/* Wrapper macros for debugging API */ +#ifndef _SHI_dbgMacros +#ifdef MEM_DEBUG +#define MemRegisterTask() _dbgMemRegisterTask(__FILE__, __LINE__) +#define MemUnregisterTask() _dbgMemUnregisterTask(__FILE__, __LINE__) +#define MemPoolInit(flags) _dbgMemPoolInit(flags, __FILE__, __LINE__) +#define MemPoolInitFS(bs, bc, f) _dbgMemPoolInitFS(bs,bc,f,__FILE__,__LINE__) +#define MemPoolInitRegion(addr, sz, f) \ + _dbgMemPoolInitRegion(addr, sz, f, __FILE__, __LINE__) +#define MemPoolInitNamedShared(nm, sz, f) \ + _dbgMemPoolInitNamedShared(nm, sz, f, __FILE__, __LINE__) +#define MemPoolInitNamedSharedEx(a, c, p, sec, nm, sz, f) \ + _dbgMemPoolInitNamedSharedEx(a, c, p, sec, nm, sz, f, __FILE__, __LINE__) +#define MemPoolAttachShared(p, n) \ + _dbgMemPoolAttachShared(p, n, __FILE__, __LINE__) +#define MemPoolFree(pool) _dbgMemPoolFree(pool, __FILE__, __LINE__) +#define MemPoolSetPageSize(p, s) _dbgMemPoolSetPageSize(p,s,__FILE__,__LINE__) +#define MemPoolSetBlockSizeFS(p, s) \ + _dbgMemPoolSetBlockSizeFS(p, s, __FILE__, __LINE__) +#define MemPoolSetSmallBlockSize(p, s) \ + _dbgMemPoolSetSmallBlockSize(p, s, __FILE__, __LINE__) +#define MemPoolSetFloor(p, f) _dbgMemPoolSetFloor(p, f, __FILE__, __LINE__) +#define MemPoolSetCeiling(p, c) _dbgMemPoolSetCeiling(p,c,__FILE__, __LINE__) +#define MemPoolPreAllocate(p,s,t) \ + _dbgMemPoolPreAllocate(p,s,t,__FILE__, __LINE__) +#define MemPoolPreAllocateHandles(p,h) \ + _dbgMemPoolPreAllocateHandles(p,h,__FILE__, __LINE__) +#define MemPoolShrink(p) _dbgMemPoolShrink(p, __FILE__, __LINE__) +#define MemPoolCheck(p) _dbgMemPoolCheck(p, __FILE__, __LINE__) +#define MemPoolWalk(p, e) _dbgMemPoolWalk(p, e, __FILE__, __LINE__) +#define MemPoolSize(p) _dbgMemPoolSize(p, __FILE__, __LINE__) +#define MemPoolCount(p) _dbgMemPoolCount(p, __FILE__, __LINE__) +#define MemPoolInfo(p,x,i) _dbgMemPoolInfo(p,x,i, __FILE__, __LINE__) +#define MemPoolFirst(i, b) _dbgMemPoolFirst(i, b, __FILE__, __LINE__) +#define MemPoolNext(i, b) _dbgMemPoolNext(i, b, __FILE__, __LINE__) +#define MemPoolLock(p) _dbgMemPoolLock(p, __FILE__, __LINE__) +#define MemPoolUnlock(p) _dbgMemPoolUnlock(p, __FILE__, __LINE__) +#define MemAlloc(p, f, s) _dbgMemAlloc(p, f, s, __FILE__, __LINE__) +#define MemReAlloc(h, s, f) _dbgMemReAlloc(h, s, f, __FILE__, __LINE__) +#define MemFree(h) _dbgMemFree(h, __FILE__, __LINE__) +#define MemLock(h) _dbgMemLock(h, __FILE__, __LINE__) +#define MemUnlock(h) _dbgMemUnlock(h, __FILE__, __LINE__) +#define MemFix(h) _dbgMemFix(h, __FILE__, __LINE__) +#define MemUnfix(h) _dbgMemUnfix(h, __FILE__, __LINE__) +#define MemSize(h) _dbgMemSize(h, __FILE__, __LINE__) +#define MemSizeRequested(h) _dbgMemSizeRequested(h, __FILE__, __LINE__) +#define MemLockCount(h) _dbgMemLockCount(h, __FILE__, __LINE__) +#define MemIsMoveable(h) _dbgMemIsMoveable(h, __FILE__, __LINE__) +#define MemHandle(p) _dbgMemHandle(p, __FILE__, __LINE__) +#define MemAllocPtr(p, s, f) _dbgMemAllocPtr(p, s, f, __FILE__, __LINE__) +#define MemReAllocPtr(p, s, f) _dbgMemReAllocPtr(p, s, f, __FILE__,__LINE__) +#define MemFreePtr(p) _dbgMemFreePtr(p, __FILE__, __LINE__) +#define MemSizePtr(p) _dbgMemSizePtr(p, __FILE__, __LINE__) +#define MemCheckPtr(p, x) _dbgMemCheckPtr(p, x, __FILE__, __LINE__) +#define MemAllocFS(p) _dbgMemAllocFS(p, __FILE__, __LINE__) +#define MemFreeFS(p) _dbgMemFreeFS(p, __FILE__, __LINE__) + +#else /* MEM_DEBUG */ + +/* MEM_DEBUG not defined: define dbgMemXXX as no-op macros + * each macro returns "success" value when MEM_DEBUG not defined + */ +#ifndef dbgMemBreakpoint +#define dbgMemBreakpoint() ((void)0) +#define dbgMemCheckAll() 1 +#define dbgMemCheckPtr(p, f, s) 1 +#define dbgMemDeferFreeing(b) 1 +#define dbgMemFormatCall(i, b, s) 0 +#define dbgMemFormatErrorInfo(i, b, s) 0 +#define dbgMemPoolDeferFreeing(p, b) 1 +#define dbgMemFreeDeferred() 1 +#define dbgMemPoolFreeDeferred(p) 1 +#define dbgMemPoolInfo(p, b) 1 +#define dbgMemPoolSetCheckFrequency(p, f) 1 +#define dbgMemPoolSetDeferQueueLen(p, b) 1 +#define dbgMemPoolSetName(p, n) 1 +#define dbgMemProtectPtr(p, f) 1 +#define dbgMemPtrInfo(p, b) 1 +#define dbgMemReallocMoves(b) 1 +#define dbgMemReportLeakage(p, c1, c2) 1 +#define dbgMemReportWrongTaskRef(b) 1 +#define dbgMemScheduleChecking(b, p, i) 1 +#define dbgMemSetCheckFrequency(f) 1 +#define dbgMemSetCheckpoint(c) 1 +#define dbgMemSetDefaultErrorOutput(x, f) 1 +#define dbgMemSetDeferQueueLen(l) 1 +#define dbgMemSetDeferSizeThreshold(s) 1 +#define dbgMemSetEntryHandler(f) 0 +#define dbgMemSetExitHandler(f) 0 +#define dbgMemSetFreeFill(c) 1 +#define dbgMemSetGuardFill(c) 1 +#define dbgMemSetGuardSize(s) 1 +#define dbgMemSetInUseFill(c) 1 +#define dbgMemSetCallstackChains(s) 1 +#define dbgMemSetStackChecking(s) 1 +#define dbgMemSetSafetyLevel(s) 1 +#define dbgMemSettingsInfo(b) 1 +#define dbgMemSuppressFreeFill(b) 1 +#define dbgMemTotalCount() 1 +#define dbgMemTotalSize() 1 +#define dbgMemWalkHeap(b) MEM_POOL_OK +#endif /* dbgMemBreakpoint */ + +#endif /* MEM_DEBUG */ +#endif /* _SHI_dbgMacros */ + +#if defined(__WATCOMC__) && defined(__SW_3S) +/* Watcom stack calling convention */ + #pragma aux MemDefaultPool "_*"; + #pragma aux MemDefaultPoolBlockSizeFS "_*"; + #pragma aux MemDefaultPoolPageSize "_*"; + #pragma aux MemDefaultPoolFlags "_*"; + #pragma aux SmartHeap_malloc "_*"; + #pragma aux SmartHeap_far_malloc "_*"; + #pragma aux SmartHeap_new "_*"; +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* !defined(_SMARTHEAP_PROT) */ diff --git a/3rdparty/smartheap/SMRTHEAP.HPP b/3rdparty/smartheap/SMRTHEAP.HPP new file mode 100644 index 00000000..663b6c90 --- /dev/null +++ b/3rdparty/smartheap/SMRTHEAP.HPP @@ -0,0 +1,159 @@ +// smrtheap.hpp -- SmartHeap public C++ header file +// Professional Memory Management Library +// +// Copyright (C) 1991-1996 by Arthur D. Applegate +// All Rights Reserved. +// +// No part of this source code may be copied, modified or reproduced +// in any form without retaining the above copyright notice. +// This source code, or source code derived from it, may not be redistributed +// without express written permission of the author. +// +// COMMENTS: +// - Include this header file to call the SmartHeap-specific versions of +// operators new (i.e. with placement syntax), to: +// o allocate from a specific memory pool; +// o specify allocation flags, such as zero-initialization; +// o resize an allocation. +// +// - If you include this header file, you must compile and link shnew.cpp, or +// link with one of the SmartHeap static operator new libraries: +// sh[l|d]XXXX.lib +// +// - Can be used in both EXEs and DLLs. +// +// - For 16-bit x86 platforms, use only in large or compact memory model. +// +// - If you do not want to use SmartHeap's global operator new but you do +// want to use SmartHeap's other facilities in a C++ application, then +// include the smrtheap.h header file but do not include this header file, +// and do not link with shnew.cpp. The two ".Xpp" files are present +// ONLY for the purpose of defining operator new and operator delete. +// +// - Use the MemDefaultPool global variable to refer to a memory pool to pass +// to SmartHeap functions that accept a pool as a parameter, +// e.g. MemPoolCount, MemPoolSize, MemPoolWalk, etc. +// + +#if !defined(_SMARTHEAP_HPP) +#define _SMARTHEAP_HPP + +#if defined(_MSC_VER) \ + && (defined(M_I86LM) || defined(M_I86CM) || defined(M_I86HM)) \ + && !defined(MEM_HUGE) +#define MEM_HUGE 0x8000u +#endif + +#ifndef __BORLANDC__ +/* Borland C++ does not treat extern "C++" correctly */ +extern "C++" +{ +#endif /* __BORLANDC__ */ + +#if defined(_MSC_VER) && _MSC_VER >= 900 +#pragma warning(disable : 4507) +#endif + +#if defined(new) +#if defined(MEM_DEBUG) +#undef new +#endif +#endif + +#include + +#include "smrtheap.h" + +#if defined(new) +#if defined(MEM_DEBUG) +#undef new +#endif +#endif + +#if ((defined(__BORLANDC__) && (__BORLANDC__ >= 0x450)) \ + || (defined(__WATCOMC__) && __WATCOMC__ >= 1000) \ + || (defined(__IBMCPP__) && __IBMCPP__ >= 250)) +#define SHI_ARRAY_NEW 1 +#endif + +void MEM_FAR * MEM_ENTRY_ANSI shi_New(unsigned long sz, unsigned flags=0, MEM_POOL pool=0); + +// operator new variants: + + +// version of new that passes memory allocation flags +// (e.g. MEM_ZEROINIT to zero-initialize memory) +// call with syntax 'ptr = new (flags) ' +inline void MEM_FAR *operator new(size_t sz, unsigned flags) + { return shi_New(sz, flags); } + +// version of new that allocates from a specified memory pool with alloc flags +// call with the syntax 'ptr = new (pool, [flags=0]) ' +inline void MEM_FAR *operator new(size_t sz, MEM_POOL pool, unsigned flags=0) + { return shi_New(sz, flags, pool); } +#ifdef SHI_ARRAY_NEW +inline void MEM_FAR *operator new[](size_t sz, MEM_POOL pool, unsigned flags=0) + { return shi_New(sz, flags, pool); } +#endif + +// version of new that changes the size of a memory block previously allocated +// from an SmartHeap memory pool +// call with the syntax 'ptr = new (ptr, flags) ' +#if !defined(MEM_DEBUG) || !defined(__HIGHC__) +/* bug in MetaWare High C++ parser confuses this with new(file,line) */ +inline void MEM_FAR *operator new(size_t new_sz, void MEM_FAR *lpMem, + unsigned flags) + { return MemReAllocPtr(lpMem, new_sz, flags); } +#ifdef SHI_ARRAY_NEW +inline void MEM_FAR *operator new[](size_t new_sz, void MEM_FAR *lpMem, + unsigned flags) + { return MemReAllocPtr(lpMem, new_sz, flags); } +#endif // SHI_ARRAY_NEW +#endif + + +// new_handler prototypes: note that MSC/C++ prototype differs from the +// protosed ANSI standard prototype for set_new_handler +#ifdef __MWERKS__ +#define MEM_CPP_THROW throw() +#else +#define MEM_CPP_THROW +#endif // __MWERKS__ +#ifndef _CRTIMP +#define _CRTIMP +#endif // _CRTIMP +#ifdef _MSC_VER +_CRTIMP _PNH MEM_ENTRY_ANSI _set_new_handler(_PNH); +#if UINT_MAX == 0xFFFFu +_PNH MEM_ENTRY_ANSI _set_fnew_handler(_PNH); +_PNHH MEM_ENTRY_ANSI _set_hnew_handler(_PNHH); +#endif // UINT_MAX +#endif // _MSC_VER +typedef void (MEM_ENTRY_ANSI * pnh)(); +_CRTIMP pnh MEM_ENTRY_ANSI set_new_handler(pnh) MEM_CPP_THROW; + +#ifndef DBG_FORMAL +#define DBG_FORMAL +#define DBG_ACTUAL +#ifndef DEBUG_NEW +#define DEBUG_NEW new +#endif // DEBUG_NEW +#define DEBUG_NEW1(x_) new(x_) +#define DEBUG_NEW2(x_, y_) new(x_, y_) +#define DEBUG_NEW3(x_, y_, z_) new(x_, y_, z_) +#define DEBUG_DELETE delete +#endif + +#ifdef DEFINE_NEW_MACRO +#define new DEBUG_NEW +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 900 +#pragma warning(default : 4507) +#endif + +#ifndef __BORLANDC__ +} +#endif /* __BORLANDC__ */ + +#endif /* !defined(_SMARTHEAP_HPP) */ diff --git a/CMakeLists.txt b/CMakeLists.txt index 23af12f2..e3b28ec6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,9 @@ -cmake_minimum_required(VERSION 3.0...3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) project(isle CXX) option(ISLE_BUILD_APP "Build ISLE.EXE application" ON) +option(ISLE_USE_SMARTHEAP "Build with SmartHeap" ${MSVC}) add_library(lego1 SHARED LEGO1/act1state.cpp @@ -134,6 +135,7 @@ add_library(lego1 SHARED LEGO1/mxomnicreateparam.cpp LEGO1/mxomnicreateparambase.cpp LEGO1/mxpalette.cpp + LEGO1/mxparam.cpp LEGO1/mxpresenter.cpp LEGO1/mxscheduler.cpp LEGO1/mxsemaphore.cpp @@ -178,6 +180,16 @@ add_library(lego1 SHARED ) target_include_directories(lego1 PRIVATE "dx5sdk/inc") +if (ISLE_USE_SMARTHEAP) + add_library(SmartHeap::SmartHeap STATIC IMPORTED) + set_target_properties(SmartHeap::SmartHeap PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/3rdparty/smartheap/SHLW32MT.LIB" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/smartheap" + INTERFACE_COMPILE_OPTIONS "/FI${CMAKE_SOURCE_DIR}/3rdparty/smartheap/SMRTHEAP.HPP") + + target_link_libraries(lego1 PRIVATE SmartHeap::SmartHeap) +endif() + # Link libraries target_link_libraries(lego1 PRIVATE ddraw dsound winmm "${CMAKE_SOURCE_DIR}/dx5sdk/lib/dinput.lib" "${CMAKE_SOURCE_DIR}/dx5sdk/lib/dxguid.lib") @@ -195,6 +207,10 @@ if (ISLE_BUILD_APP) # Include LEGO1 headers in ISLE target_include_directories(isle PRIVATE "${CMAKE_SOURCE_DIR}/LEGO1" "dx5sdk/inc") + if (ISLE_USE_SMARTHEAP) + target_link_libraries(isle PRIVATE SmartHeap::SmartHeap) + endif() + # Link DSOUND, WINMM, and LEGO1 target_link_libraries(isle PRIVATE dsound winmm lego1 "${CMAKE_SOURCE_DIR}/dx5sdk/lib/dinput.lib" "${CMAKE_SOURCE_DIR}/dx5sdk/lib/dxguid.lib") diff --git a/LEGO1/compat.h b/LEGO1/compat.h index 496fa4db..6248259b 100644 --- a/LEGO1/compat.h +++ b/LEGO1/compat.h @@ -11,4 +11,24 @@ #define COMPAT_CONST #endif -#endif // ISLECOMPAT_H \ No newline at end of file +#define MSVC420_VERSION 1020 + +// STL compatibility. +#if defined(_MSC_VER) && _MSC_VER <= MSVC420_VERSION +#include +#else +#include +#include +using namespace std; +template +using List = list; +#endif + +// We use `override` so newer compilers can tell us our vtables are valid, +// however this keyword was added in C++11, so we define it as empty for +// compatibility with older compilers. +#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 corresponds to VC6.0 but "override" was probably added even later +#define override +#endif + +#endif // ISLECOMPAT_H diff --git a/LEGO1/legostream.h b/LEGO1/legostream.h index a505cd54..ca110f01 100644 --- a/LEGO1/legostream.h +++ b/LEGO1/legostream.h @@ -1,6 +1,7 @@ #ifndef LEGOSTREAM_H #define LEGOSTREAM_H +#include "compat.h" #include "decomp.h" #include "mxtypes.h" @@ -70,4 +71,4 @@ class LegoMemoryStream : public LegoStream MxU32 m_offset; }; -#endif // LEGOSTREAM_H \ No newline at end of file +#endif // LEGOSTREAM_H diff --git a/LEGO1/mxcore.h b/LEGO1/mxcore.h index 5e6586ba..8ca7c421 100644 --- a/LEGO1/mxcore.h +++ b/LEGO1/mxcore.h @@ -3,6 +3,7 @@ #include +#include "compat.h" #include "mxtypes.h" class MxParam; @@ -14,8 +15,8 @@ class MxCore public: __declspec(dllexport) MxCore(); __declspec(dllexport) virtual ~MxCore(); // vtable+00 - __declspec(dllexport) virtual MxLong Notify(MxParam &p); // vtable+04 - virtual MxLong Tickle(); // vtable+08 + __declspec(dllexport) virtual MxResult Notify(MxParam &p); // vtable+04 + virtual MxResult Tickle(); // vtable+08 // OFFSET: LEGO1 0x100144c0 inline virtual const char *ClassName() const // vtable+0c @@ -30,8 +31,13 @@ class MxCore return !strcmp(name, MxCore::ClassName()); } + inline MxU32 GetId() + { + return m_id; + } + private: - unsigned int m_id; + MxU32 m_id; }; diff --git a/LEGO1/mxdsaction.cpp b/LEGO1/mxdsaction.cpp index e5162f89..b5cf6e75 100644 --- a/LEGO1/mxdsaction.cpp +++ b/LEGO1/mxdsaction.cpp @@ -5,10 +5,12 @@ MxDSAction::MxDSAction() { // TODO this->SetType(MxDSType_Action); + this->m_omni = NULL; + this->m_someTimingField = -0x80000000; } -// OFFSET: LEGO1 0x100ada80 STUB +// OFFSET: LEGO1 0x100ada80 MxDSAction::~MxDSAction() { - // TODO -} + delete this->m_unk7c; +} \ No newline at end of file diff --git a/LEGO1/mxdsaction.h b/LEGO1/mxdsaction.h index e26d28a6..155d7cc2 100644 --- a/LEGO1/mxdsaction.h +++ b/LEGO1/mxdsaction.h @@ -2,6 +2,7 @@ #define MXDSACTION_H #include "mxdsobject.h" +#include "mxomni.h" // VTABLE 0x100dc098 // SIZE 0x94 @@ -23,33 +24,33 @@ class MxDSAction : public MxDSObject { return !strcmp(name, MxDSAction::ClassName()) || MxDSObject::IsA(name); } - - int m_unk2c; - int m_unk30; - int m_unk34; - int m_unk38; - int m_unk3c; - int m_unk40; - int m_unk44; - int m_unk48; - int m_unk4c; - int m_unk50; - int m_unk54; - int m_unk58; - int m_unk5c; - int m_unk60; - int m_unk64; - int m_unk68; - int m_unk6c; - int m_unk70; - int m_unk74; - int m_unk78; - int m_unk7c; - int m_unk80; - int m_unk84; - int m_unk88; - int m_unk8c; - int m_unk90; +private: + MxLong m_unk2c; + MxLong m_unk30; + MxLong m_unk34; + MxLong* m_unk38; + MxLong m_unk3c; + MxLong m_unk40; + MxLong m_unk44; + MxLong m_unk48; + MxLong m_unk4c; + MxLong m_unk50; + MxLong m_unk54; + MxLong m_unk58; + MxLong m_unk5c; + MxLong m_unk60; + MxLong m_unk64; + MxLong m_unk68; + MxLong m_unk6c; + MxLong m_unk70; + MxLong m_unk74; + MxLong m_unk78; + MxLong* m_unk7c; + MxLong m_unk80; + MxLong m_unk84; + MxLong m_unk88; + MxOmni* m_omni; // 0x8c + MxS32 m_someTimingField; // 0x90 }; #endif // MXDSACTION_H diff --git a/LEGO1/mxdschunk.cpp b/LEGO1/mxdschunk.cpp index 7ec8531c..63c43ba2 100644 --- a/LEGO1/mxdschunk.cpp +++ b/LEGO1/mxdschunk.cpp @@ -1,13 +1,19 @@ #include "mxdschunk.h" -// OFFSET: LEGO1 0x100be050 STUB +// OFFSET: LEGO1 0x100be050 MxDSChunk::MxDSChunk() { - // TODO + this->m_length = 0; + this->m_pStuff = NULL; + this->m_buffer = -1; + this->m_long1FromHeader = 0; + this->m_long2FromHeader = 0; } -// OFFSET: LEGO1 0x100be170 STUB +// OFFSET: LEGO1 0x100be170 MxDSChunk::~MxDSChunk() { - // TODO + if ((this->m_length & 1) != 0) { + delete this->m_pStuff; + } } diff --git a/LEGO1/mxdschunk.h b/LEGO1/mxdschunk.h index 8758ffa3..f9b1d9d0 100644 --- a/LEGO1/mxdschunk.h +++ b/LEGO1/mxdschunk.h @@ -1,8 +1,11 @@ #ifndef MXDSCHUNK_H #define MXDSCHUNK_H -#include "mxcore.h" +#include "mxcore.h" +#include "mxtypes.h" + +// VTABLE 0x100dc7f8 class MxDSChunk : public MxCore { public: @@ -21,6 +24,13 @@ class MxDSChunk : public MxCore { return !strcmp(name, MxDSChunk::ClassName()) || MxCore::IsA(name); } +private: + MxS16 m_length; // 0x8 + MxLong m_buffer; // 0xc + MxLong m_long1FromHeader; // 0x10 + MxLong m_long2FromHeader; // 0x14 + void* m_pStuff; // 0x18 + void* m_pSomething; // 0x1c }; #endif // MXDSCHUNK_H diff --git a/LEGO1/mxdsmediaaction.cpp b/LEGO1/mxdsmediaaction.cpp index 5f9c1132..a8acde30 100644 --- a/LEGO1/mxdsmediaaction.cpp +++ b/LEGO1/mxdsmediaaction.cpp @@ -3,12 +3,19 @@ // OFFSET: LEGO1 0x100c8b40 MxDSMediaAction::MxDSMediaAction() { - // TODO + this->m_unk98 = 0; + this->m_unk9c = 0; + this->m_unka0 = 0; + this->m_unka4 = 0; + this->m_unka8 = 0; + this->m_unkac = 1; + this->m_unkb4 = 0xffffffff; + this->m_unkb0 = 0; this->SetType(MxDSType_MediaAction); } -// OFFSET: LEGO1 0x100c8cf0 STUB +// OFFSET: LEGO1 0x100c8cf0 MxDSMediaAction::~MxDSMediaAction() { - // TODO + delete this->m_unk98; } diff --git a/LEGO1/mxdsmediaaction.h b/LEGO1/mxdsmediaaction.h index 5beb12f5..5e6a6141 100644 --- a/LEGO1/mxdsmediaaction.h +++ b/LEGO1/mxdsmediaaction.h @@ -1,6 +1,7 @@ #ifndef MXDSMEDIAACTION_H #define MXDSMEDIAACTION_H +#include "decomp.h" #include "mxdsaction.h" // VTABLE 0x100dcd40 @@ -23,7 +24,16 @@ class MxDSMediaAction : public MxDSAction { return !strcmp(name, MxDSMediaAction::ClassName()) || MxDSAction::IsA(name); } - +private: + MxS32* m_unk94; + MxS32* m_unk98; + MxS32* m_unk9c; + MxS32* m_unka0; + MxS32* m_unka4; + MxS32* m_unka8; + MxS32 m_unkac; + MxS32* m_unkb0; + MxS32 m_unkb4; }; #endif // MXDSMEDIAACTION_H diff --git a/LEGO1/mxdssound.cpp b/LEGO1/mxdssound.cpp index 30894abc..d05f785d 100644 --- a/LEGO1/mxdssound.cpp +++ b/LEGO1/mxdssound.cpp @@ -3,7 +3,7 @@ // OFFSET: LEGO1 0x100c92c0 MxDSSound::MxDSSound() { - // TODO + this->m_lastField = 0x4f; this->SetType(MxDSType_Sound); } diff --git a/LEGO1/mxdssound.h b/LEGO1/mxdssound.h index 45a61d63..eca6c055 100644 --- a/LEGO1/mxdssound.h +++ b/LEGO1/mxdssound.h @@ -2,6 +2,7 @@ #define MXDSSOUND_H #include "mxdsmediaaction.h" +#include "mxtypes.h" // VTABLE 0x100dcdd0 // SIZE 0xc0 @@ -23,6 +24,9 @@ class MxDSSound : public MxDSMediaAction { return !strcmp(name, MxDSSound::ClassName()) || MxDSMediaAction::IsA(name); } +private: + MxS32 m_unkb8; + MxLong m_lastField; // 0xbc }; diff --git a/LEGO1/mxentity.cpp b/LEGO1/mxentity.cpp index 7459dd24..1b5ea84f 100644 --- a/LEGO1/mxentity.cpp +++ b/LEGO1/mxentity.cpp @@ -1,19 +1,12 @@ #include "mxentity.h" -// OFFSET: LEGO1 0x1001d190 STUB +// OFFSET: LEGO1 0x1001d190 MxEntity::MxEntity() { - // TODO + this->m_mxEntityId = -1; } -// OFFSET: LEGO1 0x1000c110 STUB +// OFFSET: LEGO1 0x1000c110 MxEntity::~MxEntity() { - // TODO -} - -// OFFSET: LEGO1 0x10001070 STUB -void MxEntity::Destroy() -{ - // TODO -} +} \ No newline at end of file diff --git a/LEGO1/mxentity.h b/LEGO1/mxentity.h index 7123abbf..a7e26e37 100644 --- a/LEGO1/mxentity.h +++ b/LEGO1/mxentity.h @@ -1,7 +1,9 @@ #ifndef MXENTITY_H #define MXENTITY_H +#include "mxatomid.h" #include "mxcore.h" +#include "mxtypes.h" // VTABLE 0x100d5390 class MxEntity : public MxCore @@ -22,11 +24,9 @@ class MxEntity : public MxCore { return !strcmp(name, MxEntity::ClassName()) || MxCore::IsA(name); } - - virtual void Destroy(); // vtable+0x1c - - // 0x8: MxResult - // 0xc MxAtomId +private: + MxS32 m_mxEntityId; // 0x8 + MxAtomId m_atom; // 0xc }; #endif // MXENTITY_H diff --git a/LEGO1/mxioinfo.cpp b/LEGO1/mxioinfo.cpp index 2d55ef36..c834f33d 100644 --- a/LEGO1/mxioinfo.cpp +++ b/LEGO1/mxioinfo.cpp @@ -1,9 +1,15 @@ #include "mxioinfo.h" +#include "decomp.h" + +// This class should be 72 bytes in size, same as the MMIOINFO struct. +// The current implementation has MMIOINFO as the only member of the class, +// but this assert will enforce the size if we decide to change that. +DECOMP_SIZE_ASSERT(MXIOINFO, sizeof(MMIOINFO)); // OFFSET: LEGO1 0x100cc800 MXIOINFO::MXIOINFO() { - memset(&m_info, 0, sizeof(MMIOINFO)); + memset(&m_info, 0, sizeof(m_info)); } // OFFSET: LEGO1 0x100cc820 @@ -13,37 +19,427 @@ MXIOINFO::~MXIOINFO() } // OFFSET: LEGO1 0x100cc830 -MxU16 MXIOINFO::Open(const char *filename, DWORD fdwOpen) +MxU16 MXIOINFO::Open(const char *p_filename, MxULong p_flags) { - return 0; + OFSTRUCT _unused; + MxU16 result = 0; + + m_info.lBufOffset = 0; + m_info.lDiskOffset = 0; + + // Cast of p_flags to u16 forces the `movzx` instruction + m_info.hmmio = (HMMIO)OpenFile(p_filename, &_unused, (MxU16)p_flags); + + if ((HFILE)m_info.hmmio != HFILE_ERROR) { + m_info.dwFlags = p_flags; + if (p_flags & MMIO_ALLOCBUF) { + + // Default buffer length of 8k if none specified + int len = m_info.cchBuffer ? m_info.cchBuffer : 8192; + HPSTR buf = new char[len]; + + if (!buf) { + result = MMIOERR_OUTOFMEMORY; + m_info.cchBuffer = 0; + m_info.dwFlags &= ~MMIO_ALLOCBUF; + m_info.pchBuffer = 0; + } else { + m_info.pchBuffer = buf; + m_info.cchBuffer = len; + } + + m_info.pchEndRead = m_info.pchBuffer; + m_info.pchNext = m_info.pchBuffer; + m_info.pchEndWrite = m_info.pchBuffer + m_info.cchBuffer; + } + } else { + result = MMIOERR_CANNOTOPEN; + } + + return result; } // OFFSET: LEGO1 0x100cc8e0 -void MXIOINFO::Close(MxLong arg) +MxU16 MXIOINFO::Close(MxLong p_unused) { - + MxU16 result = 0; + + if (m_info.hmmio) { + result = Flush(0); + _lclose((HFILE)m_info.hmmio); + m_info.hmmio = 0; + + if (m_info.dwFlags & MMIO_ALLOCBUF) + delete[] m_info.pchBuffer; + + m_info.pchEndWrite = 0; + m_info.pchEndRead = 0; + m_info.pchBuffer = 0; + m_info.dwFlags = 0; + } + + return result; } // OFFSET: LEGO1 0x100cc930 -MxULong MXIOINFO::Read(HPSTR pch, LONG cch) +MxLong MXIOINFO::Read(void *p_buf, MxLong p_len) { - return 0; + MxLong bytes_read = 0; + + if (m_info.pchBuffer) { + + int bytes_left = m_info.pchEndRead - m_info.pchNext; + while (p_len > 0) { + + if (bytes_left > 0) { + if (p_len < bytes_left) + bytes_left = p_len; + + memcpy(p_buf, m_info.pchNext, bytes_left); + p_len -= bytes_left; + + m_info.pchNext += bytes_left; + bytes_read += bytes_left; + } + + if (p_len <= 0 || Advance(0)) + break; + + bytes_left = m_info.pchEndRead - m_info.pchNext; + if (bytes_left <= 0) + break; + } + } else if (m_info.hmmio && p_len > 0) { + bytes_read = _hread((HFILE)m_info.hmmio, p_buf, p_len); + + if (bytes_read == -1) { + bytes_read = 0; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + m_info.lDiskOffset += bytes_read; + } + } + + return bytes_read; } // OFFSET: LEGO1 0x100cca00 -LONG MXIOINFO::Seek(LONG lOffset, int iOrigin) +MxLong MXIOINFO::Seek(MxLong p_offset, int p_origin) { - return 0; + MxLong result = -1; + + // If buffered I/O + if (m_info.pchBuffer) { + if (p_origin == SEEK_CUR) { + if (!p_offset) { + // don't seek at all and just return where we are. + return m_info.lBufOffset + (m_info.pchNext - m_info.pchBuffer); + } else { + // With SEEK_CUR, p_offset is a relative offset. + // Get the absolute position instead and use SEEK_SET. + p_offset += m_info.lBufOffset + (m_info.pchNext - m_info.pchBuffer); + p_origin = SEEK_SET; + } + } else if (p_origin == SEEK_END) { + // not possible with buffered I/O + return -1; + } + + // else p_origin == SEEK_SET. + + // is p_offset between the start and end of the buffer? + // i.e. can we do the seek without reading more from disk? + if (p_offset >= m_info.lBufOffset && p_offset < m_info.lBufOffset + m_info.cchBuffer) { + m_info.pchNext = m_info.pchBuffer + (p_offset - m_info.lBufOffset); + result = p_offset; + } else { + // we have to read another chunk from disk. + if (m_info.hmmio && !Flush(0)) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, p_offset, p_origin); + + if (m_info.lDiskOffset == -1) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + + // align offset to buffer size + int new_offset = p_offset - (p_offset % m_info.cchBuffer); + m_info.lBufOffset = new_offset; + + // do we need to seek again? + // (i.e. are we already aligned to buffer size?) + if (p_offset != new_offset) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, new_offset, SEEK_SET); + + if (m_info.lDiskOffset == -1) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } + } + + if (m_info.lBufOffset == m_info.lDiskOffset) { + // is the file open for writing only? + if ((m_info.dwFlags & MMIO_RWMODE) && + ((m_info.dwFlags & MMIO_RWMODE) != MMIO_READWRITE)) { + + m_info.pchNext = m_info.pchBuffer - m_info.lBufOffset + p_offset; + + result = p_offset; + } else { + // We can read from the file. Fill the buffer. + int bytes_read = _hread((HFILE)m_info.hmmio, m_info.pchBuffer, m_info.cchBuffer); + + if (bytes_read == -1) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + m_info.lDiskOffset += bytes_read; + m_info.pchNext = p_offset - m_info.lBufOffset + m_info.pchBuffer; + m_info.pchEndRead = m_info.pchBuffer + bytes_read; + + if (m_info.pchNext < m_info.pchEndRead) { + result = p_offset; + } + } + } + } + } + } + } + } else { + // No buffer so just seek the file directly (if we have a valid handle) + if (m_info.hmmio) { + // i.e. if we just want to get the current file position + if (p_origin == SEEK_CUR && p_offset == 0) { + return m_info.lDiskOffset; + } else { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, p_offset, p_origin); + + result = m_info.lDiskOffset; + + if (result == -1) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } + } + } + } + + return result; } // OFFSET: LEGO1 0x100ccbc0 -void MXIOINFO::SetBuffer(LPSTR pchBuffer, LONG cchBuffer, LONG unk) +MxU16 MXIOINFO::SetBuffer(char *p_buf, MxLong p_len, MxLong p_unused) { - + MxU16 result = Flush(0); + + if (m_info.dwFlags & MMIO_ALLOCBUF) { + m_info.dwFlags &= ~MMIO_ALLOCBUF; + delete[] m_info.pchBuffer; + } + + m_info.pchBuffer = p_buf; + m_info.cchBuffer = p_len; + m_info.pchEndWrite = m_info.pchBuffer + m_info.cchBuffer; + m_info.pchEndRead = m_info.pchBuffer; + + return result; +} + +// OFFSET: LEGO1 0x100ccc10 +MxU16 MXIOINFO::Flush(MxU16 p_unused) +{ + MxU16 result = 0; + + // if buffer is dirty + if (m_info.dwFlags & MMIO_DIRTY) { + // if we have allocated an IO buffer + if (m_info.pchBuffer) { + // if we have a file open for writing + if (m_info.hmmio && (m_info.dwFlags & MMIO_RWMODE)) { + // (pulling this value out into a variable forces it into EBX) + MxLong cchBuffer = m_info.cchBuffer; + if (cchBuffer > 0) { + if (m_info.lBufOffset != m_info.lDiskOffset) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, m_info.lBufOffset, SEEK_SET); + } + + // Was the previous seek (if required) successful? + if (m_info.lBufOffset != m_info.lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + MxLong bytes_written = _hwrite((HFILE)m_info.hmmio, m_info.pchBuffer, cchBuffer); + + if (bytes_written != -1 && bytes_written == cchBuffer) { + m_info.lDiskOffset += bytes_written; + m_info.pchNext = m_info.pchBuffer; + m_info.dwFlags &= ~MMIO_DIRTY; + } else { + result = MMIOERR_CANNOTWRITE; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } + } + } + } else { + result = MMIOERR_CANNOTWRITE; + } + } else { + result = MMIOERR_UNBUFFERED; + } + } + + return result; +} + +// OFFSET: LEGO1 0x100ccd00 +MxU16 MXIOINFO::Advance(MxU16 p_option) +{ + MxU16 result = 0; + MxULong rwmode = m_info.dwFlags & MMIO_RWMODE; + + if (m_info.pchBuffer) { + MxLong cch = m_info.cchBuffer; + + // If we can and should write to the file, + // if we are being asked to write to the file, + // and if there is a buffer *to* write: + if ((rwmode == MMIO_WRITE || rwmode == MMIO_READWRITE) && + (m_info.dwFlags & MMIO_DIRTY) && + ((p_option & MMIO_WRITE) || (rwmode == MMIO_READWRITE)) && + cch > 0) { + + if (m_info.lBufOffset != m_info.lDiskOffset) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, m_info.lBufOffset, SEEK_SET); + } + + if (m_info.lBufOffset != m_info.lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + } else { + MxLong bytes_written = _hwrite((HFILE)m_info.hmmio, m_info.pchBuffer, cch); + + if (bytes_written != -1 && bytes_written == cch) { + m_info.lDiskOffset += bytes_written; + m_info.pchNext = m_info.pchBuffer; + m_info.pchEndRead = m_info.pchBuffer; + m_info.dwFlags &= ~MMIO_DIRTY; + } else { + result = MMIOERR_CANNOTWRITE; + } + } + + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + + } + + m_info.lBufOffset += cch; + if ((!rwmode || rwmode == MMIO_READWRITE) && cch > 0) { + if (m_info.lBufOffset != m_info.lDiskOffset) { + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, m_info.lBufOffset, SEEK_SET); + } + + // if previous seek failed + if (m_info.lBufOffset != m_info.lDiskOffset) { + result = MMIOERR_CANNOTSEEK; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + int bytes_read = _hread((HFILE)m_info.hmmio, m_info.pchBuffer, cch); + + if (bytes_read == -1) { + result = MMIOERR_CANNOTREAD; + m_info.lDiskOffset = _llseek((HFILE)m_info.hmmio, 0, SEEK_CUR); + } else { + m_info.lDiskOffset += bytes_read; + m_info.pchNext = m_info.pchBuffer; + m_info.pchEndRead = m_info.pchBuffer + bytes_read; + } + } + } + } else { + result = MMIOERR_UNBUFFERED; + } + + return result; } // OFFSET: LEGO1 0x100cce60 -MxU16 MXIOINFO::Descend(LPMMCKINFO pmmcki, const MMCKINFO *pmmckiParent, UINT fuDescend) +MxU16 MXIOINFO::Descend(MMCKINFO *p_chunkInfo, const MMCKINFO *p_parentInfo, MxU16 p_descend) { - return 0; + MxU16 result = 0; + + if (!p_chunkInfo) + return MMIOERR_BASE; // ? + + if (!p_descend) { + p_chunkInfo->dwFlags = 0; + if (Read(p_chunkInfo, 8) != 8) { + result = MMIOERR_CANNOTREAD; + } else { + if (m_info.pchBuffer) { + p_chunkInfo->dwDataOffset = m_info.pchNext - m_info.pchBuffer + m_info.lBufOffset; + } else { + p_chunkInfo->dwDataOffset = m_info.lDiskOffset; + } + + if (p_chunkInfo->ckid == FOURCC_RIFF || p_chunkInfo->ckid == FOURCC_LIST) { + if (Read(&p_chunkInfo->fccType, 4) != 4) { + result = MMIOERR_CANNOTREAD; + } + } + } + } else { + MxULong ofs = MAXLONG; + + if (p_parentInfo) + ofs = p_parentInfo->cksize + p_parentInfo->dwDataOffset; + + BOOL running = TRUE; + BOOL read_ok = FALSE; + MMCKINFO tmp; + tmp.dwFlags = 0; + + // This loop is... something + do { + if (Read(&tmp, 8) != 8) { + // If the first read fails, report read error. Else EOF. + result = read_ok ? MMIOERR_CHUNKNOTFOUND : MMIOERR_CANNOTREAD; + running = FALSE; + } else { + read_ok = TRUE; + if (m_info.pchBuffer) { + tmp.dwDataOffset = m_info.pchNext - m_info.pchBuffer + m_info.lBufOffset; + } else { + tmp.dwDataOffset = m_info.lDiskOffset; + } + + if (ofs < tmp.dwDataOffset) { + result = MMIOERR_CHUNKNOTFOUND; + running = FALSE; + } else { + if ((p_descend == MMIO_FINDLIST && tmp.ckid == FOURCC_LIST) || + (p_descend == MMIO_FINDRIFF && tmp.ckid == FOURCC_RIFF)) { + if (Read(&tmp.fccType, 4) != 4) { + result = MMIOERR_CANNOTREAD; + } else { + if (p_chunkInfo->fccType != tmp.fccType) + continue; + } + running = FALSE; + } else { + if (p_chunkInfo->ckid != tmp.ckid) { + if (Seek((tmp.cksize&1)+tmp.cksize, SEEK_CUR) != -1) { + continue; + } else { + result = MMIOERR_CANNOTSEEK; + } + } + running = FALSE; + } + } + } + + } while (running); + + if (!result) + memcpy(p_chunkInfo, &tmp, sizeof(MMCKINFO)); + + } + + return result; } diff --git a/LEGO1/mxioinfo.h b/LEGO1/mxioinfo.h index 5ce0947a..83da930a 100644 --- a/LEGO1/mxioinfo.h +++ b/LEGO1/mxioinfo.h @@ -12,13 +12,17 @@ class MXIOINFO MXIOINFO(); __declspec(dllexport) ~MXIOINFO(); - MxU16 Open(const char *filename, DWORD fdwOpen); - void Close(MxLong arg); - LONG Seek(LONG lOffset, int iOrigin); - MxULong Read(HPSTR pch, LONG cch); - void SetBuffer(LPSTR pchBuffer, LONG cchBuffer, LONG unk); - MxU16 Descend(LPMMCKINFO pmmcki, const MMCKINFO *pmmckiParent, UINT fuDescend); + MxU16 Open(const char *, MxULong); + MxU16 Close(MxLong); + MxLong Read(void *, MxLong); + MxLong Seek(MxLong, int); + MxU16 SetBuffer(char *, MxLong, MxLong); + MxU16 Flush(MxU16); + MxU16 Advance(MxU16); + MxU16 Descend(MMCKINFO *, const MMCKINFO *, MxU16); + // NOTE: In MXIOINFO, the `hmmio` member of MMIOINFO is used like + // an HFILE (int) instead of an HMMIO (WORD). MMIOINFO m_info; }; diff --git a/LEGO1/mxnotificationmanager.cpp b/LEGO1/mxnotificationmanager.cpp index 472c87b5..b7d001aa 100644 --- a/LEGO1/mxnotificationmanager.cpp +++ b/LEGO1/mxnotificationmanager.cpp @@ -1,15 +1,185 @@ +#include "legoomni.h" +#include "mxautolocker.h" +#include "mxcore.h" #include "mxnotificationmanager.h" +#include "mxparam.h" +#include "mxtypes.h" -// OFFSET: LEGO1 0x100ac450 STUB -MxNotificationManager::~MxNotificationManager() +#include "compat.h" +#include "decomp.h" + +DECOMP_SIZE_ASSERT(MxNotification, 0x8); +DECOMP_SIZE_ASSERT(MxNotificationManager, 0x40); + +// OFFSET: LEGO1 0x100ac220 +MxNotification::MxNotification(MxCore *p_target, MxParam *p_param) { - // TODO + m_target = p_target; + m_param = p_param->Clone(); } -// OFFSET: LEGO1 0x100ac800 STUB -MxLong MxNotificationManager::Tickle() +// OFFSET: LEGO1 0x100ac240 +MxNotification::~MxNotification() { - // TODO + delete m_param; +} - return 0; -} \ No newline at end of file +// OFFSET: LEGO1 0x100ac250 +MxNotificationManager::MxNotificationManager() : MxCore(), m_lock(), m_listenerIds() +{ + m_unk2c = 0; + m_queue = NULL; + m_active = TRUE; + m_sendList = NULL; +} + +// OFFSET: LEGO1 0x100ac450 +MxNotificationManager::~MxNotificationManager() +{ + MxAutoLocker lock(&m_lock); + Tickle(); + delete m_queue; + m_queue = NULL; + + TickleManager()->Unregister(this); +} + +// OFFSET: LEGO1 0x100ac800 +MxResult MxNotificationManager::Tickle() +{ + m_sendList = new MxNotificationPtrList(); + if (m_sendList == NULL) { + return FAILURE; + } + else { + { + MxAutoLocker lock(&m_lock); + swap(m_queue, m_sendList); + } + + while (m_sendList->size() != 0) { + MxNotification *notif = m_sendList->front(); + m_sendList->pop_front(); + notif->GetTarget()->Notify(*notif->GetParam()); + delete notif; + } + + delete m_sendList; + m_sendList = NULL; + return SUCCESS; + } +} + +// OFFSET: LEGO1 0x100ac600 +MxResult MxNotificationManager::Create(MxS32 p_unk1, MxS32 p_unk2) +{ + MxResult result = SUCCESS; + m_queue = new MxNotificationPtrList(); + + if (m_queue == NULL) { + result = FAILURE; + } + else { + TickleManager()->Register(this, 10); + } + + return result; +} + +// OFFSET: LEGO1 0x100acd20 +void MxNotificationManager::Register(MxCore *p_listener) +{ + MxAutoLocker lock(&m_lock); + + MxIdList::iterator it = find(m_listenerIds.begin(), m_listenerIds.end(), p_listener->GetId()); + if (it != m_listenerIds.end()) + return; + + m_listenerIds.push_back(p_listener->GetId()); +} + +// OFFSET: LEGO1 0x100acdf0 +void MxNotificationManager::Unregister(MxCore *p_listener) +{ + MxAutoLocker lock(&m_lock); + + MxIdList::iterator it = find(m_listenerIds.begin(), m_listenerIds.end(), p_listener->GetId()); + + if (it != m_listenerIds.end()) { + m_listenerIds.erase(it); + FlushPending(p_listener); + } +} + +// OFFSET: LEGO1 0x100ac990 +void MxNotificationManager::FlushPending(MxCore *p_listener) +{ + MxNotificationPtrList pending; + MxNotification *notif; + + { + MxAutoLocker lock(&m_lock); + + // Find all notifications from, and addressed to, p_listener. + if (m_sendList != NULL) { + MxNotificationPtrList::iterator it = m_sendList->begin(); + while (it != m_sendList->end()) { + notif = *it; + if ((notif->GetTarget()->GetId() == p_listener->GetId()) || + (notif->GetParam()->GetSender()) && (notif->GetParam()->GetSender()->GetId() == p_listener->GetId())) { + m_sendList->erase(it++); + pending.push_back(notif); + } + else { + it++; + } + } + } + + MxNotificationPtrList::iterator it = m_queue->begin(); + while (it != m_queue->end()) { + notif = *it; + if ((notif->GetTarget()->GetId() == p_listener->GetId()) || + (notif->GetParam()->GetSender()) && (notif->GetParam()->GetSender()->GetId() == p_listener->GetId())) { + m_queue->erase(it++); + pending.push_back(notif); + } + else { + it++; + } + } + } + + // Deliver those notifications. + while (pending.size() != 0) { + notif = pending.front(); + pending.pop_front(); + notif->GetTarget()->Notify(*notif->GetParam()); + delete notif; + } +} + +// OFFSET: LEGO1 0x100ac6c0 +MxResult MxNotificationManager::Send(MxCore *p_listener, MxParam *p_param) +{ + MxAutoLocker lock(&m_lock); + + if (m_active == FALSE) { + return FAILURE; + } + else { + MxIdList::iterator it = find(m_listenerIds.begin(), m_listenerIds.end(), p_listener->GetId()); + if (it == m_listenerIds.end()) { + return FAILURE; + } + else { + MxNotification *notif = new MxNotification(p_listener, p_param); + if (notif != NULL) { + m_queue->push_back(notif); + return SUCCESS; + } + } + } + + return FAILURE; +} diff --git a/LEGO1/mxnotificationmanager.h b/LEGO1/mxnotificationmanager.h index 86cb4f00..e313510b 100644 --- a/LEGO1/mxnotificationmanager.h +++ b/LEGO1/mxnotificationmanager.h @@ -2,16 +2,62 @@ #define MXNOTIFICATIONMANAGER_H #include "mxcore.h" +#include "mxcriticalsection.h" +#include "mxtypes.h" + +#include "compat.h" + +class MxNotification +{ +public: + MxNotification(MxCore *p_target, MxParam *p_param); + ~MxNotification(); + + inline MxCore *GetTarget() + { + return m_target; + } + + inline MxParam *GetParam() + { + return m_param; + } + +private: + MxCore *m_target; // 0x0 + MxParam *m_param; // 0x4 +}; + +class MxIdList : public List +{}; + +class MxNotificationPtrList : public List +{}; // VTABLE 0x100dc078 class MxNotificationManager : public MxCore { +private: + MxNotificationPtrList *m_queue; // 0x8 + MxNotificationPtrList *m_sendList; // 0xc + MxCriticalSection m_lock; // 0x10 + MxS32 m_unk2c; // 0x2c + MxIdList m_listenerIds; // 0x30 + MxBool m_active; // 0x3c + public: - virtual ~MxNotificationManager(); // vtable+0x0 + MxNotificationManager(); + virtual ~MxNotificationManager(); // vtable+0x0 (scalar deleting destructor) - virtual MxLong Tickle(); // vtable+0x8 + virtual MxResult Tickle(); // vtable+0x8 + // TODO: Where does this method come from? + virtual MxResult Create(MxS32 p_unk1, MxS32 p_unk2); // vtable+0x14 + void Register(MxCore *p_listener); + void Unregister(MxCore *p_listener); + MxResult Send(MxCore *p_listener, MxParam *p_param); - // 0x10: MxCriticalSection +private: + void FlushPending(MxCore *p_listener); }; #endif // MXNOTIFICATIONMANAGER_H diff --git a/LEGO1/mxomni.h b/LEGO1/mxomni.h index ea77259c..3c7cd7e3 100644 --- a/LEGO1/mxomni.h +++ b/LEGO1/mxomni.h @@ -80,5 +80,6 @@ __declspec(dllexport) MxVariableTable * VariableTable(); __declspec(dllexport) MxMusicManager * MusicManager(); __declspec(dllexport) MxEventManager * EventManager(); __declspec(dllexport) MxNotificationManager * NotificationManager(); +MxVideoManager * MVideoManager(); #endif // MXOMNI_H diff --git a/LEGO1/mxpalette.cpp b/LEGO1/mxpalette.cpp index 546dddf9..7fb02f4f 100644 --- a/LEGO1/mxpalette.cpp +++ b/LEGO1/mxpalette.cpp @@ -1,4 +1,349 @@ #include "mxpalette.h" +#include "mxomni.h" + +// GLOBAL: LEGO1 0x10102188 0x400 +PALETTEENTRY g_defaultPaletteEntries[256] = +{ + { 0u, 0u, 0u, 0u }, + { 128u, 0u, 0u, 0u }, + { 0u, 128u, 0u, 0u }, + { 128u, 128u, 0u, 0u }, + { 0u, 0u, 128u, 0u }, + { 128u, 0u, 128u, 0u }, + { 0u, 128u, 128u, 0u }, + { 128u, 128u, 128u, 0u }, + { 192u, 220u, 192u, 0u }, + { 166u, 202u, 240u, 0u }, + { 255u, 255u, 255u, 0u }, + { 250u, 250u, 250u, 0u }, + { 239u, 239u, 239u, 0u }, + { 228u, 228u, 228u, 0u }, + { 217u, 217u, 217u, 0u }, + { 206u, 206u, 206u, 0u }, + { 195u, 195u, 195u, 0u }, + { 185u, 185u, 185u, 0u }, + { 174u, 174u, 174u, 0u }, + { 163u, 163u, 163u, 0u }, + { 152u, 152u, 152u, 0u }, + { 141u, 141u, 141u, 0u }, + { 130u, 130u, 130u, 0u }, + { 123u, 123u, 123u, 0u }, + { 115u, 115u, 115u, 0u }, + { 108u, 108u, 108u, 0u }, + { 101u, 101u, 101u, 0u }, + { 93u, 93u, 93u, 0u }, + { 86u, 86u, 86u, 0u }, + { 79u, 79u, 79u, 0u }, + { 71u, 71u, 71u, 0u }, + { 64u, 64u, 64u, 0u }, + { 54u, 54u, 54u, 0u }, + { 43u, 43u, 43u, 0u }, + { 33u, 33u, 33u, 0u }, + { 22u, 22u, 22u, 0u }, + { 12u, 12u, 12u, 0u }, + { 8u, 8u, 8u, 0u }, + { 4u, 4u, 4u, 0u }, + { 0u, 0u, 0u, 0u }, + { 225u, 218u, 217u, 0u }, + { 195u, 182u, 179u, 0u }, + { 165u, 145u, 141u, 0u }, + { 134u, 108u, 102u, 0u }, + { 104u, 72u, 64u, 0u }, + { 74u, 35u, 26u, 0u }, + { 59u, 28u, 21u, 0u }, + { 44u, 21u, 16u, 0u }, + { 30u, 14u, 10u, 0u }, + { 15u, 7u, 5u, 0u }, + { 250u, 231u, 232u, 0u }, + { 240u, 185u, 189u, 0u }, + { 233u, 154u, 160u, 0u }, + { 226u, 124u, 131u, 0u }, + { 219u, 93u, 102u, 0u }, + { 213u, 62u, 73u, 0u }, + { 203u, 18u, 32u, 0u }, + { 172u, 15u, 27u, 0u }, + { 159u, 14u, 25u, 0u }, + { 146u, 13u, 23u, 0u }, + { 133u, 12u, 21u, 0u }, + { 120u, 11u, 19u, 0u }, + { 107u, 10u, 17u, 0u }, + { 94u, 8u, 15u, 0u }, + { 81u, 7u, 13u, 0u }, + { 68u, 6u, 11u, 0u }, + { 55u, 5u, 9u, 0u }, + { 42u, 4u, 7u, 0u }, + { 29u, 3u, 5u, 0u }, + { 10u, 1u, 2u, 0u }, + { 227u, 236u, 242u, 0u }, + { 178u, 203u, 220u, 0u }, + { 145u, 181u, 205u, 0u }, + { 112u, 159u, 191u, 0u }, + { 79u, 137u, 176u, 0u }, + { 30u, 104u, 154u, 0u }, + { 0u, 84u, 140u, 0u }, + { 0u, 79u, 132u, 0u }, + { 0u, 72u, 119u, 0u }, + { 0u, 66u, 110u, 0u }, + { 0u, 61u, 101u, 0u }, + { 0u, 55u, 92u, 0u }, + { 0u, 47u, 78u, 0u }, + { 0u, 39u, 65u, 0u }, + { 0u, 34u, 56u, 0u }, + { 0u, 28u, 47u, 0u }, + { 0u, 23u, 38u, 0u }, + { 0u, 18u, 29u, 0u }, + { 0u, 12u, 20u, 0u }, + { 0u, 4u, 7u, 0u }, + { 230u, 242u, 234u, 0u }, + { 180u, 215u, 193u, 0u }, + { 147u, 198u, 166u, 0u }, + { 113u, 180u, 138u, 0u }, + { 80u, 162u, 111u, 0u }, + { 30u, 136u, 70u, 0u }, + { 0u, 120u, 45u, 0u }, + { 0u, 114u, 43u, 0u }, + { 0u, 102u, 38u, 0u }, + { 0u, 95u, 35u, 0u }, + { 0u, 83u, 31u, 0u }, + { 0u, 72u, 27u, 0u }, + { 0u, 63u, 24u, 0u }, + { 0u, 56u, 21u, 0u }, + { 0u, 48u, 18u, 0u }, + { 0u, 36u, 14u, 0u }, + { 0u, 25u, 9u, 0u }, + { 0u, 17u, 6u, 0u }, + { 0u, 9u, 3u, 0u }, + { 0u, 1u, 1u, 0u }, + { 254u, 244u, 220u, 0u }, + { 255u, 239u, 181u, 0u }, + { 255u, 231u, 156u, 0u }, + { 255u, 222u, 132u, 0u }, + { 255u, 222u, 115u, 0u }, + { 255u, 214u, 99u, 0u }, + { 255u, 206u, 66u, 0u }, + { 255u, 198u, 41u, 0u }, + { 255u, 185u, 0u, 0u }, + { 255u, 189u, 8u, 0u }, + { 247u, 181u, 0u, 0u }, + { 222u, 156u, 0u, 0u }, + { 189u, 140u, 0u, 0u }, + { 173u, 123u, 0u, 0u }, + { 148u, 107u, 0u, 0u }, + { 132u, 90u, 0u, 0u }, + { 107u, 74u, 0u, 0u }, + { 74u, 49u, 0u, 0u }, + { 57u, 41u, 0u, 0u }, + { 33u, 24u, 0u, 0u }, + { 117u, 52u, 87u, 0u }, + { 176u, 158u, 50u, 0u }, + { 122u, 165u, 29u, 0u }, + { 242u, 142u, 8u, 0u }, + { 164u, 43u, 36u, 0u }, + { 113u, 67u, 20u, 0u }, + { 255u, 0u, 255u, 0u }, + { 255u, 0u, 255u, 0u }, + { 255u, 0u, 255u, 0u }, + { 255u, 0u, 255u, 0u }, + { 255u, 0u, 255u, 0u }, + { 57u, 163u, 217u, 0u }, + { 255u, 255u, 255u, 0u }, + { 254u, 255u, 247u, 0u }, + { 253u, 253u, 239u, 0u }, + { 248u, 247u, 247u, 0u }, + { 248u, 247u, 231u, 0u }, + { 240u, 240u, 240u, 0u }, + { 239u, 239u, 218u, 0u }, + { 227u, 232u, 236u, 0u }, + { 224u, 221u, 209u, 0u }, + { 215u, 222u, 215u, 0u }, + { 213u, 214u, 215u, 0u }, + { 214u, 214u, 203u, 0u }, + { 255u, 219u, 57u, 0u }, + { 206u, 206u, 206u, 0u }, + { 206u, 206u, 198u, 0u }, + { 255u, 214u, 18u, 0u }, + { 207u, 203u, 186u, 0u }, + { 197u, 199u, 199u, 0u }, + { 255u, 206u, 0u, 0u }, + { 207u, 198u, 159u, 0u }, + { 247u, 204u, 0u, 0u }, + { 189u, 198u, 189u, 0u }, + { 189u, 189u, 189u, 0u }, + { 238u, 199u, 0u, 0u }, + { 189u, 189u, 181u, 0u }, + { 238u, 190u, 24u, 0u }, + { 181u, 189u, 184u, 0u }, + { 161u, 186u, 224u, 0u }, + { 181u, 181u, 181u, 0u }, + { 231u, 189u, 0u, 0u }, + { 173u, 182u, 173u, 0u }, + { 222u, 181u, 0u, 0u }, + { 173u, 173u, 173u, 0u }, + { 213u, 182u, 0u, 0u }, + { 172u, 173u, 160u, 0u }, + { 214u, 173u, 0u, 0u }, + { 165u, 165u, 165u, 0u }, + { 206u, 173u, 0u, 0u }, + { 160u, 168u, 151u, 0u }, + { 206u, 164u, 0u, 0u }, + { 198u, 165u, 0u, 0u }, + { 157u, 156u, 156u, 0u }, + { 134u, 156u, 200u, 0u }, + { 153u, 156u, 144u, 0u }, + { 142u, 156u, 161u, 0u }, + { 189u, 156u, 0u, 0u }, + { 148u, 148u, 148u, 0u }, + { 146u, 148u, 138u, 0u }, + { 133u, 143u, 161u, 0u }, + { 189u, 143u, 0u, 0u }, + { 140u, 140u, 140u, 0u }, + { 177u, 147u, 0u, 0u }, + { 131u, 140u, 136u, 0u }, + { 146u, 130u, 126u, 0u }, + { 170u, 137u, 0u, 0u }, + { 132u, 132u, 130u, 0u }, + { 123u, 125u, 125u, 0u }, + { 123u, 123u, 133u, 0u }, + { 153u, 126u, 0u, 0u }, + { 114u, 116u, 118u, 0u }, + { 110u, 112u, 108u, 0u }, + { 97u, 109u, 136u, 0u }, + { 127u, 108u, 6u, 0u }, + { 0u, 173u, 0u, 0u }, + { 100u, 99u, 101u, 0u }, + { 176u, 71u, 41u, 0u }, + { 36u, 142u, 33u, 0u }, + { 98u, 91u, 75u, 0u }, + { 80u, 88u, 104u, 0u }, + { 252u, 0u, 0u, 0u }, + { 78u, 71u, 73u, 0u }, + { 73u, 71u, 78u, 0u }, + { 62u, 63u, 61u, 0u }, + { 0u, 66u, 211u, 0u }, + { 99u, 51u, 14u, 0u }, + { 198u, 0u, 0u, 0u }, + { 189u, 0u, 0u, 0u }, + { 0u, 57u, 206u, 0u }, + { 181u, 0u, 0u, 0u }, + { 0u, 56u, 185u, 0u }, + { 173u, 0u, 0u, 0u }, + { 165u, 0u, 0u, 0u }, + { 49u, 49u, 49u, 0u }, + { 0u, 49u, 165u, 0u }, + { 156u, 0u, 0u, 0u }, + { 42u, 45u, 60u, 0u }, + { 148u, 0u, 0u, 0u }, + { 140u, 0u, 0u, 0u }, + { 41u, 41u, 41u, 0u }, + { 0u, 41u, 144u, 0u }, + { 132u, 0u, 0u, 0u }, + { 123u, 0u, 0u, 0u }, + { 7u, 35u, 114u, 0u }, + { 34u, 36u, 32u, 0u }, + { 115u, 0u, 0u, 0u }, + { 107u, 0u, 0u, 0u }, + { 90u, 0u, 0u, 0u }, + { 23u, 24u, 27u, 0u }, + { 74u, 0u, 0u, 0u }, + { 15u, 15u, 16u, 0u }, + { 49u, 0u, 0u, 0u }, + { 16u, 12u, 4u, 0u }, + { 7u, 8u, 8u, 0u }, + { 0u, 0u, 8u, 0u }, + { 255u, 251u, 240u, 0u }, + { 160u, 160u, 164u, 0u }, + { 128u, 128u, 128u, 0u }, + { 255u, 0u, 0u, 0u }, + { 0u, 255u, 0u, 0u }, + { 255u, 255u, 0u, 0u }, + { 0u, 0u, 255u, 0u }, + { 255u, 0u, 255u, 0u }, + { 0u, 255u, 255u, 0u }, + { 255u, 255u, 255u, 0u } +}; + + +// OFFSET: LEGO1 0x100bee30 +MxPalette::MxPalette() +{ + this->m_overrideSkyColor = FALSE; + this->m_palette = NULL; + GetDefaultPalette(this->m_entries); + this->m_skyColor = this->m_entries[141]; +} + +// OFFSET: LEGO1 0x100BEED0 +MxPalette::MxPalette(const RGBQUAD *p_colors) +{ + this->m_overrideSkyColor = FALSE; + this->m_palette = NULL; + ApplySystemEntriesToPalette(this->m_entries); + + for ( MxS32 i = 10; i < 246; i++ ) + { + this->m_entries[i].peRed = p_colors[i].rgbRed; + this->m_entries[i].peGreen = p_colors[i].rgbGreen; + this->m_entries[i].peBlue = p_colors[i].rgbBlue; + this->m_entries[i].peFlags = 0; + } + + this->m_skyColor = this->m_entries[141]; +} + +// OFFSET: LEGO1 100bef90 +MxPalette::~MxPalette() +{ + if (m_palette) { + m_palette->Release(); + } +} + +// OFFSET: LEGO1 0x100bf390 +void MxPalette::ApplySystemEntriesToPalette(LPPALETTEENTRY p_entries) +{ + HDC hdc; + + hdc = GetDC(0); + if ( (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) != 0 && GetDeviceCaps(hdc, SIZEPALETTE) == 256 ) + { + GetSystemPaletteEntries(hdc, 0, 10, p_entries); + GetSystemPaletteEntries(hdc, 246, 10, &p_entries[246]); + } + else + { + memcpy(p_entries, g_defaultPaletteEntries, sizeof(PALETTEENTRY) * 10); + memcpy(&p_entries[246], &g_defaultPaletteEntries[246], sizeof(PALETTEENTRY) * 10); + } + ReleaseDC(0, hdc); +} + +// OFFSET: LEGO1 0x100bf0b0 +MxPalette* MxPalette::Clone() +{ + MxPalette *result = new MxPalette; + this->GetEntries(result->m_entries); + result->m_overrideSkyColor = this->m_overrideSkyColor; + return result; +} + +// OFFSET: LEGO1 0x100bf420 +void MxPalette::GetDefaultPalette(LPPALETTEENTRY p_entries) +{ + HDC hdc; + + hdc = GetDC(0); + if ( (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) != 0 && GetDeviceCaps(hdc, SIZEPALETTE) == 256 ) + { + GetSystemPaletteEntries(hdc, 0, 256, p_entries); + memcpy(&p_entries[10], &g_defaultPaletteEntries[10], sizeof(PALETTEENTRY) * 236); + } + else + { + memcpy(p_entries, g_defaultPaletteEntries, sizeof(PALETTEENTRY) * 256); + } + + ReleaseDC(0, hdc); +} // OFFSET: LEGO1 0x100bf150 MxResult MxPalette::GetEntries(LPPALETTEENTRY p_entries) @@ -8,14 +353,137 @@ MxResult MxPalette::GetEntries(LPPALETTEENTRY p_entries) } // OFFSET: LEGO1 0x100bf340 -MxBool MxPalette::operator==(MxPalette &) +MxBool MxPalette::operator==(MxPalette &other) { - // TODO - return FALSE; + for (MxS32 i = 0; i < 256; i++) + { + if (this->m_entries[i].peRed != other.m_entries[i].peRed) + return FALSE; + if (this->m_entries[i].peGreen != other.m_entries[i].peGreen) + return FALSE; + if (this->m_entries[i].peBlue != other.m_entries[i].peBlue) + return FALSE; + } + return TRUE; } // OFFSET: LEGO1 0x100bf330 void MxPalette::Detach() { - // TODO + this->m_palette = NULL; +} + +// OFFSET: LEGO1 0x100bf170 +MxResult MxPalette::SetEntries(LPPALETTEENTRY p_entries) +{ + MxS32 i; + MxResult status = 0; + + if ( this->m_palette ) + { + for ( i = 0; i < 10; i++ ) + this->m_entries[i].peFlags = 0x80; + for ( i = 10; i < 136; i++ ) + { + this->m_entries[i].peFlags = 68; + this->m_entries[i].peRed = p_entries[i].peRed; + this->m_entries[i].peGreen = p_entries[i].peGreen; + this->m_entries[i].peBlue = p_entries[i].peBlue; + } + for ( i = 136; i < 140; i++ ) + { + this->m_entries[i].peFlags = 132; + this->m_entries[i].peRed = p_entries[i].peRed; + this->m_entries[i].peGreen = p_entries[i].peGreen; + this->m_entries[i].peBlue = p_entries[i].peBlue; + } + if ( !this->m_overrideSkyColor ) + { + this->m_entries[140].peFlags = 0x44; + this->m_entries[140].peRed = p_entries[140].peRed; + this->m_entries[140].peGreen = p_entries[140].peGreen; + this->m_entries[140].peBlue = p_entries[140].peBlue; + this->m_entries[141].peFlags = 0x84; + this->m_entries[141].peRed = p_entries[141].peRed; + this->m_entries[141].peGreen = p_entries[141].peGreen; + this->m_entries[141].peBlue = p_entries[141].peBlue; + } + + for ( i = 142; i < 246; i++ ) + { + this->m_entries[i].peFlags = 132; + this->m_entries[i].peRed = p_entries[i].peRed; + this->m_entries[i].peGreen = p_entries[i].peGreen; + this->m_entries[i].peBlue = p_entries[i].peBlue; + } + + for ( i = 246; i < 256; i++ ) + this->m_entries[i].peFlags = 0x80; + + if ( this->m_palette->SetEntries(0, 0, 256, this->m_entries) ) + status = -1; + } + + return status; +} + +// OFFSET: LEGO1 0x100bf2d0 +MxResult MxPalette::SetSkyColor(LPPALETTEENTRY p_sky_color) +{ + MxResult status = 0; + if ( this->m_palette != NULL ) + { + this->m_entries[141].peRed = p_sky_color->peRed; + this->m_entries[141].peGreen = p_sky_color->peGreen; + this->m_entries[141].peBlue = p_sky_color->peBlue; + this->m_skyColor = this->m_entries[141]; + if ( this->m_palette->SetEntries(0, 141, 1, &this->m_skyColor) ) + { + status = -1; + } + } + return status; +} + +// OFFSET: LEGO1 0x100BF490 +void MxPalette::Reset(MxBool p_ignoreSkyColor) +{ + if ( this->m_palette != NULL ) + { + GetDefaultPalette(this->m_entries); + if ( !p_ignoreSkyColor ) + { + this->m_entries[140] = this->m_entries[141] = this->m_skyColor; + } + SetEntries(this->m_entries); + this->m_palette->SetEntries(0, 0, 256, this->m_entries); + } +} + +// OFFSET: LEGO1 0x100BF000 +LPDIRECTDRAWPALETTE MxPalette::CreateNativePalette() +{ + MxS32 i; + if ( this->m_palette == NULL ) + { + for (i = 0; i < 10; i++) + this->m_entries[i].peFlags = 0x80; + for (i = 10; i < 136; i++) + this->m_entries[i].peFlags = 0x44; + for (i = 136; i < 140; i++) + this->m_entries[i].peFlags = 0x84; + this->m_entries[140].peFlags = 0x84; + this->m_entries[141].peFlags = 0x44; + for (i = 142; i < 246; i++) + this->m_entries[i].peFlags = 0x84; + for (i = 246; i < 256; i++) + this->m_entries[i].peFlags = 0x80; + + if (MVideoManager() && MVideoManager()->GetDirectDraw()) + { + MVideoManager()->GetDirectDraw()->CreatePalette(4, this->m_entries, &this->m_palette, NULL); + } + } + + return this->m_palette; } diff --git a/LEGO1/mxpalette.h b/LEGO1/mxpalette.h index 7f4a4299..b024330a 100644 --- a/LEGO1/mxpalette.h +++ b/LEGO1/mxpalette.h @@ -14,12 +14,23 @@ class MxPalette : public MxCore __declspec(dllexport) MxBool operator==(MxPalette &); __declspec(dllexport) void Detach(); - MxResult GetEntries(LPPALETTEENTRY p_entries); + MxPalette(); + MxPalette(const RGBQUAD *); + virtual ~MxPalette(); + void ApplySystemEntriesToPalette(LPPALETTEENTRY p_entries); + MxPalette* Clone(); + void GetDefaultPalette(LPPALETTEENTRY p_entries); + MxResult GetEntries(LPPALETTEENTRY p_entries); + MxResult SetEntries(LPPALETTEENTRY p_palette); + MxResult SetSkyColor(LPPALETTEENTRY p_sky_color); + void Reset(MxBool p_ignoreSkyColor); + LPDIRECTDRAWPALETTE CreateNativePalette(); private: - LPDIRECTDRAWPALETTE m_pDirectDrawPalette; - PALETTEENTRY m_entries[256]; - // there's a bit more here + LPDIRECTDRAWPALETTE m_palette; + PALETTEENTRY m_entries[256]; // 0xc + MxBool m_overrideSkyColor; // 0x40c + PALETTEENTRY m_skyColor; // 0x40d }; #endif // MXPALETTE_H diff --git a/LEGO1/mxparam.cpp b/LEGO1/mxparam.cpp new file mode 100644 index 00000000..d7bb36b2 --- /dev/null +++ b/LEGO1/mxparam.cpp @@ -0,0 +1,11 @@ +#include "mxparam.h" + +#include "decomp.h" + +DECOMP_SIZE_ASSERT(MxParam, 0xc); + +// OFFSET: LEGO1 0x10010390 +MxParam* MxParam::Clone() +{ + return new MxParam(m_type, m_sender); +} diff --git a/LEGO1/mxparam.h b/LEGO1/mxparam.h new file mode 100644 index 00000000..c4150ff7 --- /dev/null +++ b/LEGO1/mxparam.h @@ -0,0 +1,33 @@ +#ifndef MXPARAM_H +#define MXPARAM_H + +#include "mxomnicreateparambase.h" +#include "mxtypes.h" + +class MxCore; + +// VTABLE 0x100d56e0 +class MxParam : public MxOmniCreateParamBase +{ +public: + inline MxParam(MxS32 p_type, MxCore *p_sender) : MxOmniCreateParamBase(), m_type(p_type), m_sender(p_sender){} + + virtual ~MxParam(){}; // vtable+0x0 (scalar deleting destructor) + virtual MxParam *Clone(); // vtable+0x4 + + inline MxS32 GetType() const + { + return m_type; + } + + inline MxCore *GetSender() const + { + return m_sender; + } + +private: + MxS32 m_type; // 0x4 + MxCore *m_sender; // 0x8 +}; + +#endif // MXPARAM_H diff --git a/LEGO1/mxthread.h b/LEGO1/mxthread.h index 6ac96b59..4537dc14 100644 --- a/LEGO1/mxthread.h +++ b/LEGO1/mxthread.h @@ -1,6 +1,7 @@ #ifndef MXTHREAD_H #define MXTHREAD_H +#include "compat.h" #include "mxtypes.h" #include "mxsemaphore.h" @@ -54,4 +55,4 @@ class MxTickleThread : public MxThread MxS32 m_frequencyMS; }; -#endif // MXTHREAD_H \ No newline at end of file +#endif // MXTHREAD_H diff --git a/LEGO1/mxticklemanager.h b/LEGO1/mxticklemanager.h index 60f2f14d..3d976d5a 100644 --- a/LEGO1/mxticklemanager.h +++ b/LEGO1/mxticklemanager.h @@ -12,8 +12,8 @@ class MxTickleManager : public MxCore virtual MxLong Tickle(); virtual const char *ClassName() const; virtual MxBool IsA(const char *name) const; - virtual void vtable14(); - virtual void vtable18(); + virtual void Register(MxCore *p_listener, int p_milliseconds); + virtual void Unregister(MxCore *p_listener); virtual void vtable1c(void *v, int p); virtual void vtable20(); }; diff --git a/LEGO1/mxtypes.h b/LEGO1/mxtypes.h index 8a7d5026..d17ac09a 100644 --- a/LEGO1/mxtypes.h +++ b/LEGO1/mxtypes.h @@ -39,8 +39,4 @@ typedef MxU8 MxBool; #define FALSE 0 #endif -#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 corresponds to VC6.0 but "override" was probably added even later -#define override -#endif - #endif // MXTYPE_H diff --git a/LEGO1/mxvideomanager.cpp b/LEGO1/mxvideomanager.cpp index 543f4136..af4995ae 100644 --- a/LEGO1/mxvideomanager.cpp +++ b/LEGO1/mxvideomanager.cpp @@ -23,7 +23,7 @@ MxVideoManager::MxVideoManager() // OFFSET: LEGO1 0x100be320 int MxVideoManager::Init() { - this->m_unk50 = 0; + this->m_pDirectDraw = NULL; this->m_unk54 = NULL; this->m_unk58 = NULL; this->m_unk5c = 0; diff --git a/LEGO1/mxvideomanager.h b/LEGO1/mxvideomanager.h index f70bf06e..2061ec6b 100644 --- a/LEGO1/mxvideomanager.h +++ b/LEGO1/mxvideomanager.h @@ -21,10 +21,10 @@ class MxVideoManager : public MxUnknown100dc6b0 int Init(); inline MxVideoParam& GetVideoParam() { return this->m_videoParam; } - + inline LPDIRECTDRAW GetDirectDraw() { return this->m_pDirectDraw; } private: MxVideoParam m_videoParam; - int m_unk50; + LPDIRECTDRAW m_pDirectDraw; LPDIRECTDRAWSURFACE m_unk54; void* m_unk58; int m_unk5c; diff --git a/tools/reccmp/reccmp.py b/tools/reccmp/reccmp.py index 226be15d..69779872 100755 --- a/tools/reccmp/reccmp.py +++ b/tools/reccmp/reccmp.py @@ -10,6 +10,7 @@ import os import sys import colorama +import re parser = argparse.ArgumentParser(allow_abbrev=False, description='Recompilation Compare: compare an original EXE with a recompiled EXE + PDB.') @@ -248,9 +249,21 @@ def get_recompiled_address(self, filename, line): md = Cs(CS_ARCH_X86, CS_MODE_32) -def sanitize(file, mnemonic, op_str): - offsetplaceholder = '' +class OffsetPlaceholderGenerator: + def __init__(self): + self.counter = 0 + self.replacements = {} + def get(self, addr): + if addr in self.replacements: + return self.replacements[addr] + else: + self.counter += 1 + replacement = '' % self.counter + self.replacements[addr] = replacement + return replacement + +def sanitize(file, placeholderGenerator, mnemonic, op_str): op_str_is_number = False try: int(op_str, 16) @@ -262,7 +275,7 @@ def sanitize(file, mnemonic, op_str): # Filter out "calls" because the offsets we're not currently trying to # match offsets. As long as there's a call in the right place, it's # probably accurate. - op_str = offsetplaceholder + op_str = placeholderGenerator.get(int(op_str, 16)) else: def filter_out_ptr(ptype, op_str): try: @@ -273,7 +286,7 @@ def filter_out_ptr(ptype, op_str): # This will throw ValueError if not hex inttest = int(op_str[start:end], 16) - return op_str[0:start] + offsetplaceholder + op_str[end:] + return op_str[0:start] + placeholderGenerator.get(inttest) + op_str[end:] except ValueError: return op_str @@ -288,7 +301,7 @@ def filter_out_ptr(ptype, op_str): try: inttest = int(word, 16) if inttest >= file.imagebase + file.textvirt: - words[i] = offsetplaceholder + words[i] = placeholderGenerator.get(inttest) except ValueError: pass op_str = ' '.join(words) @@ -298,18 +311,76 @@ def filter_out_ptr(ptype, op_str): def parse_asm(file, addr, size): asm = [] data = file.read(addr, size) + placeholderGenerator = OffsetPlaceholderGenerator() for i in md.disasm(data, 0): # Use heuristics to disregard some differences that aren't representative # of the accuracy of a function (e.g. global offsets) - mnemonic, op_str = sanitize(file, i.mnemonic, i.op_str) + mnemonic, op_str = sanitize(file, placeholderGenerator, i.mnemonic, i.op_str) if op_str is None: asm.append(mnemonic) else: asm.append("%s %s" % (mnemonic, op_str)) return asm +REGISTER_LIST = set([ + 'eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', + 'ax', 'bx', 'cx', 'dx', 'di', 'si', 'bp', 'sp', +]) +WORDS = re.compile(r'\w+') + +def get_registers(line: str): + to_replace = [] + # use words regex to find all matching positions: + for match in WORDS.finditer(line): + reg = match.group(0) + if reg in REGISTER_LIST: + to_replace.append((reg, match.start())) + return to_replace + +def replace_register(lines: list[str], start_line: int, reg: str, replacement: str): + for i in range(start_line, len(lines)): + lines[i] = lines[i].replace(reg, replacement) + +# Is it possible to make new_asm the same as original_asm by swapping registers? +def can_resolve_register_differences(original_asm, new_asm): + # Swapping ain't gonna help if the lengths are different + if len(original_asm) != len(new_asm): + return False + + # Make copies so we don't modify the original + original_asm = original_asm.copy() + new_asm = new_asm.copy() + + # Look for the mismatching lines + for i in range(len(original_asm)): + new_line = new_asm[i] + original_line = original_asm[i] + if new_line != original_line: + # Find all the registers to replace + to_replace = get_registers(original_line) + + for j in range(len(to_replace)): + (reg, reg_index) = to_replace[j] + replacing_reg = new_line[reg_index:reg_index + len(reg)] + if replacing_reg in REGISTER_LIST: + if replacing_reg != reg: + # Do a three-way swap replacing in all the subsequent lines + temp_reg = "&" * len(reg) + replace_register(new_asm, i, replacing_reg, temp_reg) + replace_register(new_asm, i, reg, replacing_reg) + replace_register(new_asm, i, temp_reg, reg) + else: + # No replacement to do, different code, bail out + return False + # Check if the lines are now the same + for i in range(len(original_asm)): + if new_asm[i] != original_asm[i]: + return False + return True + function_count = 0 total_accuracy = 0 +total_effective_accuracy = 0 htmlinsert = [] # Generate basename of original file, used in locating OFFSET lines @@ -356,24 +427,41 @@ def parse_asm(file, addr, size): if not recinfo: continue + # The effective_ratio is the ratio when ignoring differing register + # allocation vs the ratio is the true ratio. + ratio = 0.0 + effective_ratio = 0.0 if recinfo.size: origasm = parse_asm(origfile, addr + recinfo.start, recinfo.size) recompasm = parse_asm(recompfile, recinfo.addr + recinfo.start, recinfo.size) diff = difflib.SequenceMatcher(None, origasm, recompasm) ratio = diff.ratio() + effective_ratio = ratio + + if ratio != 1.0: + # Check whether we can resolve register swaps which are actually + # perfect matches modulo compiler entropy. + if can_resolve_register_differences(origasm, recompasm): + effective_ratio = 1.0 else: ratio = 0 - percenttext = "%.2f%%" % (ratio * 100) + percenttext = "%.2f%%" % (effective_ratio * 100) if not plain: - if ratio == 1.0: + if effective_ratio == 1.0: percenttext = colorama.Fore.GREEN + percenttext + colorama.Style.RESET_ALL - elif ratio > 0.8: + elif effective_ratio > 0.8: percenttext = colorama.Fore.YELLOW + percenttext + colorama.Style.RESET_ALL else: percenttext = colorama.Fore.RED + percenttext + colorama.Style.RESET_ALL + if effective_ratio == 1.0 and ratio != 1.0: + if plain: + percenttext += "*" + else: + percenttext += colorama.Fore.RED + "*" + colorama.Style.RESET_ALL + if not verbose: if args.print_rec_addr: addrs = '%s / %s' % (hex(addr), hex(recinfo.addr)) @@ -383,14 +471,21 @@ def parse_asm(file, addr, size): function_count += 1 total_accuracy += ratio + total_effective_accuracy += effective_ratio if recinfo.size: udiff = difflib.unified_diff(origasm, recompasm, n=10) # If verbose, print the diff for that funciton to the output if verbose: - if ratio == 1.0: - print("%s: %s 100%% match.\n\nOK!" % (hex(addr), recinfo.name)) + if effective_ratio == 1.0: + ok_text = "OK!" if plain else (colorama.Fore.GREEN + "✨ OK! ✨" + colorama.Style.RESET_ALL) + if ratio == 1.0: + print("%s: %s 100%% match.\n\n%s\n\n" % + (hex(addr), recinfo.name, ok_text)) + else: + print("%s: %s Effective 100%% match. (Differs in register allocation only)\n\n%s (still differs in register allocation)\n\n" % + (hex(addr), recinfo.name, ok_text)) else: for line in udiff: if line.startswith("++") or line.startswith("@@") or line.startswith("--"): @@ -416,7 +511,7 @@ def parse_asm(file, addr, size): # If html, record the diffs to an HTML file if html: escaped = '\\n'.join(udiff).replace('"', '\\"').replace('\n', '\\n').replace('<', '<').replace('>', '>') - htmlinsert.append('{address: "%s", name: "%s", matching: %s, diff: "%s"}' % (hex(addr), recinfo.name, str(ratio), escaped)) + htmlinsert.append('{address: "%s", name: "%s", matching: %s, diff: "%s"}' % (hex(addr), recinfo.name, str(effective_ratio), escaped)) except UnicodeDecodeError: break @@ -496,7 +591,8 @@ def gen_svg(svg, name, icon, implemented_funcs, total_funcs, raw_accuracy): function_count = int(args.total) if function_count > 0: - print('\nTotal accuracy %.2f%% across %i functions' % (total_accuracy / function_count * 100, function_count)) + print('\nTotal effective accuracy %.2f%% across %i functions (%.2f%% actual accuracy)' % + (total_effective_accuracy / function_count * 100, function_count, total_accuracy / function_count * 100)) if svg: - gen_svg(svg, os.path.basename(original), args.svg_icon, implemented_funcs, function_count, total_accuracy) + gen_svg(svg, os.path.basename(original), args.svg_icon, implemented_funcs, function_count, total_effective_accuracy) diff --git a/tools/reccmp/template.html b/tools/reccmp/template.html index 9b03e4ec..f3087688 100644 --- a/tools/reccmp/template.html +++ b/tools/reccmp/template.html @@ -221,7 +221,9 @@ addrCel.innerHTML = addrCel.dataset.value = element.address; nameCel.innerHTML = nameCel.dataset.value = element.name; - matchCel.innerHTML = (element.matching * 100).toFixed(2) + '%'; + + var effectiveNote = (element.matching == 1 && element.diff != '') ? '*' : ''; + matchCel.innerHTML = (element.matching * 100).toFixed(2) + '%' + effectiveNote; matchCel.dataset.value = element.matching; row.classList.add('funcrow');