#ifndef DYNOS_CPP_H #define DYNOS_CPP_H #ifdef __cplusplus #include "dynos.h" extern "C" { #include "engine/math_util.h" #include "src/game/moving_texture.h" } #define FUNCTION_CODE (u32) 0x434E5546 #define POINTER_CODE (u32) 0x52544E50 #define LUA_VAR_CODE (u32) 0x5641554C // // Enums // enum { DATA_TYPE_NONE = 0, DATA_TYPE_LIGHT, DATA_TYPE_TEXTURE, DATA_TYPE_VERTEX, DATA_TYPE_DISPLAY_LIST, DATA_TYPE_GEO_LAYOUT, DATA_TYPE_ANIMATION_VALUE, DATA_TYPE_ANIMATION_INDEX, DATA_TYPE_ANIMATION, DATA_TYPE_ANIMATION_TABLE, DATA_TYPE_GFXDYNCMD, DATA_TYPE_COLLISION, DATA_TYPE_LEVEL_SCRIPT, DATA_TYPE_MACRO_OBJECT, DATA_TYPE_TRAJECTORY, DATA_TYPE_MOVTEX, DATA_TYPE_MOVTEXQC, DATA_TYPE_ROOMS, DATA_TYPE_LIGHT_T, DATA_TYPE_AMBIENT_T, DATA_TYPE_TEXTURE_LIST, DATA_TYPE_TEXTURE_RAW, DATA_TYPE_UNUSED, }; enum { DOPT_NONE = 0, DOPT_TOGGLE, DOPT_CHOICE, DOPT_SCROLL, DOPT_BIND, DOPT_BUTTON, DOPT_SUBMENU, // These ones are used by the Warp to Level built-in submenu DOPT_CHOICELEVEL, DOPT_CHOICEAREA, DOPT_CHOICESTAR, DOPT_CHOICEPARAM, }; // // DynOS Array // A vector-like array, implemented to be processed really fast, but cannot handle C++ complex classes like std::string // template class Array { public: inline Array() : mBuffer(NULL), mCount(0), mCapacity(0) { } inline Array(const std::initializer_list &aList) : mBuffer(NULL), mCount(0), mCapacity(0) { Resize(aList.size()); memcpy(mBuffer, aList.begin(), mCount * sizeof(T)); } inline Array(const T *aBegin, const T *aEnd) : mBuffer(NULL), mCount(0), mCapacity(0) { Resize(aEnd - aBegin); memcpy(mBuffer, aBegin, mCount * sizeof(T)); } inline Array(const Array &aOther) : mBuffer(NULL), mCount(0), mCapacity(0) { Resize(aOther.mCount); memcpy(mBuffer, aOther.mBuffer, mCount * sizeof(T)); } inline void operator=(const Array &aOther) { Resize(aOther.mCount); memcpy(mBuffer, aOther.mBuffer, mCount * sizeof(T)); } inline ~Array() { Clear(); } public: void Resize(s32 aCount) { if (aCount > mCapacity) { mCapacity = MAX(aCount, MAX(16, mCapacity * 2)); T *_Buffer = (T *) calloc(mCapacity, sizeof(T)); if (mBuffer) { memcpy(_Buffer, mBuffer, mCount * sizeof(T)); free(mBuffer); } mBuffer = _Buffer; } mCount = aCount; } void Add(const T& aItem) { Resize(mCount + 1); mBuffer[mCount - 1] = aItem; } void Remove(s32 aIndex) { memmove(mBuffer + aIndex, mBuffer + aIndex + 1, (mCount - aIndex - 1) * sizeof(T)); mCount--; } void Pop() { mCount--; } void RemoveAll() { mCount = 0; } void Clear() { if (mBuffer) free(mBuffer); mBuffer = NULL; mCount = 0; mCapacity = 0; } s32 Find(const T& aItem) const { for (s32 i = 0; i != mCount; ++i) { if (mBuffer[i] == aItem) { return i; } } return -1; } template s32 FindIf(Predicate aPredicate) const { for (s32 i = 0; i != mCount; ++i) { if (aPredicate(mBuffer[i])) { return i; } } return -1; } public: inline const T *begin() const { return mBuffer; } inline const T *end() const { return mBuffer + mCount; } inline T *begin() { return mBuffer; } inline T *end() { return mBuffer + mCount; } inline const T &operator[](s32 aIndex) const { return mBuffer[aIndex]; } inline T &operator[](s32 aIndex) { return mBuffer[aIndex]; } inline s32 Count() const { return mCount; } inline bool Empty() const { return mCount == 0; } public: void Read(FILE *aFile) { s32 _Length = 0; fread(&_Length, sizeof(s32), 1, aFile); Resize(_Length); fread(mBuffer, sizeof(T), _Length, aFile); } void Write(FILE *aFile) const { fwrite(&mCount, sizeof(s32), 1, aFile); fwrite(mBuffer, sizeof(T), mCount, aFile); } private: T *mBuffer; s32 mCount; s32 mCapacity; }; // // DynOS String // A fixed-size string that doesn't require heap memory allocation // #define STRING_SIZE 127 class String { public: inline String() : mCount(0) { mBuffer[0] = 0; } inline String(const char *aString) : mCount(0) { if (aString) { u64 _Length = strlen(aString); mCount = MIN(_Length, STRING_SIZE - 1); memcpy(mBuffer, aString, _Length); } mBuffer[mCount] = 0; } template inline String(const char *aFmt, Args... aArgs) : mCount(0) { snprintf(mBuffer, STRING_SIZE, aFmt, aArgs...); mCount = (u8) strlen(mBuffer); mBuffer[mCount] = 0; } inline String(const String &aOther) : mCount(0) { mCount = aOther.mCount; memcpy(mBuffer, aOther.mBuffer, mCount); mBuffer[mCount] = 0; } inline void operator=(const String &aOther) { mCount = aOther.mCount; memcpy(mBuffer, aOther.mBuffer, mCount); mBuffer[mCount] = 0; } public: void Add(char aChar) { if (mCount == STRING_SIZE - 1) return; mBuffer[mCount++] = aChar; mBuffer[mCount] = 0; } void Remove(s32 aIndex) { memmove(mBuffer + aIndex, mBuffer + aIndex + 1, (mCount-- - aIndex - 1)); mBuffer[mCount] = 0; } void RemoveAll() { mCount = 0; mBuffer[0] = 0; } void Clear() { mCount = 0; mBuffer[0] = 0; } s32 Find(char aChar, s32 aStart = 0) const { for (u8 i = (u8) aStart; i < mCount; ++i) { if (mBuffer[i] == aChar) { return (s32) i; } } return -1; } s32 Find(const char *aString, s32 aStart = 0) const { const char *_Ptr = strstr(mBuffer + aStart, aString); if (_Ptr) return (s32) (_Ptr - mBuffer); return -1; } s32 FindLast(char aChar) const { for (u8 i = mCount; i != 0; --i) { if (mBuffer[i - 1] == aChar) { return (s32) (i - 1); } } return -1; } String SubString(s32 aStart, s32 aCount = STRING_SIZE - 1) const { if (aStart >= mCount) return String(); if (aCount < 0) aCount = STRING_SIZE - 1; aCount = MIN(aCount, mCount - aStart); String _String; _String.mCount = aCount; memcpy(_String.mBuffer, mBuffer + aStart, aCount); _String.mBuffer[aCount] = 0; return _String; } public: inline const char *begin() const { return mBuffer; } inline const char *end() const { return mBuffer + mCount; } inline char *begin() { return mBuffer; } inline char *end() { return mBuffer + mCount; } inline const char &operator[](s32 aIndex) const { return mBuffer[aIndex]; } inline char &operator[](s32 aIndex) { return mBuffer[aIndex]; } inline s32 Length() const { return (s32) mCount; } inline bool Empty() const { return mCount == 0; } public: bool OPTIMIZE_O3 operator==(const char *aString) const { return !strcmp(mBuffer, aString); } bool OPTIMIZE_O3 operator==(const String &aOther) const { return !strcmp(mBuffer, aOther.mBuffer); } bool OPTIMIZE_O3 operator!=(const char *aString) const { return strcmp(mBuffer, aString); } bool OPTIMIZE_O3 operator!=(const String &aOther) const { return strcmp(mBuffer, aOther.mBuffer); } public: void Read(FILE *aFile) { fread(&mCount, sizeof(u8), 1, aFile); fread(mBuffer, sizeof(char), mCount, aFile); mBuffer[mCount] = 0; } void Write(FILE *aFile) const { fwrite(&mCount, sizeof(u8), 1, aFile); fwrite(mBuffer, sizeof(char), mCount, aFile); } s32 ParseInt() const { s32 i = 0; if (mBuffer[1] == 'x') { sscanf(mBuffer + 2, "%x", &i); } else { sscanf(mBuffer, "%d", &i); } return i; } f32 ParseFloat() const { f32 f = 0.f; sscanf(mBuffer, "%f", &f); return f; } private: char mBuffer[STRING_SIZE]; u8 mCount; }; static_assert(sizeof(String) == (STRING_SIZE + 1), "sizeof(String) must be (STRING_SIZE + 1)"); // // Types // template using Pair = std::pair; typedef std::string SysPath; typedef struct MovtexQuadCollection MovtexQC; class NoCopy { protected: NoCopy() {} ~NoCopy() {} private: NoCopy(const NoCopy &) = delete; void operator=(const NoCopy &) = delete; }; struct TexData : NoCopy { Array mPngData; Array mRawData; s32 mRawWidth = -1; s32 mRawHeight = -1; s32 mRawFormat = -1; s32 mRawSize = -1; bool mUploaded = false; }; struct AnimData : NoCopy { s16 mFlags = 0; s16 mUnk02 = 0; s16 mUnk04 = 0; s16 mUnk06 = 0; s16 mUnk08 = 0; Pair mUnk0A; Pair> mValues; Pair> mIndex; u32 mLength = 0; }; template struct DataNode : NoCopy { String mName; T* mData = NULL; u32 mSize = 0; Array mTokens; u64 mModelIdentifier = 0; u64 mLoadIndex = 0; u8 mFlags = 0; }; template using DataNodes = Array*>; struct GfxContext { DataNode* mCurrentTexture = NULL; DataNode* mCurrentPalette = NULL; }; template using AnimBuffer = Pair>; struct GfxData : NoCopy { // Model data DataNodes mLights; DataNodes mLightTs; DataNodes mAmbientTs; DataNodes mTextures; DataNodes mTextureLists; DataNodes mVertices; DataNodes mDisplayLists; DataNodes mGeoLayouts; DataNodes mCollisions; DataNodes mLevelScripts; DataNodes mMacroObjects; DataNodes mTrajectories; DataNodes mMovtexs; DataNodes mMovtexQCs; DataNodes mRooms; // Animation data Array *> mAnimValues; Array *> mAnimIndices; DataNodes mAnimations; Array> mAnimationTable; // Skip bin output of children Array *> mChildGeoLayouts; // Current u64 mLoadIndex = 0; s32 mErrorCount = 0; u32 mModelIdentifier = 0; s32 mModIndex = 0; SysPath mPackFolder; Array mPointerList; Array> mPointerOffsetList; Array mLuaPointerList; Array mLuaTokenList; GfxContext mGfxContext; Array mGeoNodeStack; }; struct ActorGfx { GfxData *mGfxData = NULL; GraphNode *mGraphNode = NULL; s32 mPackIndex = 0; }; struct PackData { s32 mIndex; bool mEnabled; bool mEnabledSet; SysPath mPath; String mDisplayName; Array> mGfxData; Array*> mTextures; }; typedef Pair Label; struct DynosOption : NoCopy { String mName; String mConfigName; // Name used in the config file Label mLabel; Label mTitle; // Full caps label, displayed with colored font DynosOption *mPrev; DynosOption *mNext; DynosOption *mParent; bool mDynos; // true from create, false from convert u8 mType; // TOGGLE struct Toggle : NoCopy { bool *mTog; } mToggle; // CHOICE struct Choice : NoCopy { Array