#ifndef DYNOS_CPP_H #define DYNOS_CPP_H #ifdef __cplusplus #include "dynos.h" extern "C" { #include "engine/behavior_script.h" #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 #define TEX_REF_CODE (u32) 0x52584554 #define MOD_PACK_INDEX 99 // // 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_BEHAVIOR_SCRIPT, 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 Binary file struct // class BinFile { private: void Grow(s32 newSize) { if (newSize >= mCapacity) { mCapacity = MAX(newSize, MAX(256, mCapacity * 2)); u8 *newBuffer = (u8 *) calloc(mCapacity, 1); if (mData) { memcpy(newBuffer, mData, mSize); free(mData); } mData = newBuffer; } mSize = MAX(mSize, newSize); } public: inline s32 Size() const { return mSize; } inline s32 Offset() const { return mOffset; } inline bool EoF() const { return mOffset >= mSize; } inline void SetOffset(s32 aOffset) const { mOffset = aOffset; } inline const char *GetFilename() const { return mFilename; } public: static BinFile *OpenR(const char *aFilename) { FILE *f = fopen(aFilename, "rb"); if (f) { fseek(f, 0, SEEK_END); BinFile *_BinFile = (BinFile *) calloc(1, sizeof(BinFile)); _BinFile->mFilename = (const char *) memcpy(calloc(strlen(aFilename) + 1, 1), aFilename, strlen(aFilename)); _BinFile->mReadOnly = true; _BinFile->Grow(ftell(f)); rewind(f); fread(_BinFile->mData, 1, _BinFile->mSize, f); fclose(f); return _BinFile; } return NULL; } static BinFile *OpenW(const char *aFilename) { BinFile *_BinFile = (BinFile *) calloc(1, sizeof(BinFile)); _BinFile->mFilename = (const char *) memcpy(calloc(strlen(aFilename) + 1, 1), aFilename, strlen(aFilename)); _BinFile->mReadOnly = false; return _BinFile; } static BinFile *OpenB(const u8 *aBuffer, s32 aSize) { BinFile *_BinFile = (BinFile *) calloc(1, sizeof(BinFile)); _BinFile->mReadOnly = true; _BinFile->Grow(aSize); memcpy(_BinFile->mData, aBuffer, aSize); return _BinFile; } static void Close(BinFile *&aBinFile) { if (aBinFile) { if (!aBinFile->mReadOnly && aBinFile->mFilename && aBinFile->mData && aBinFile->mSize) { FILE *f = fopen(aBinFile->mFilename, "wb"); if (f) { fwrite(aBinFile->mData, 1, aBinFile->mSize, f); fclose(f); } } if (aBinFile->mFilename) free((void *) aBinFile->mFilename); if (aBinFile->mData) free(aBinFile->mData); free(aBinFile); } } public: template T Read() const { T _Item = { 0 }; if (mOffset + sizeof(T) <= mSize) { memcpy(&_Item, mData + mOffset, sizeof(T)); mOffset += sizeof(T); } return _Item; } template T *Read(T *aBuffer, s32 aCount) const { if (mOffset + aCount * sizeof(T) <= mSize) { memcpy(aBuffer, mData + mOffset, aCount * sizeof(T)); mOffset += aCount * sizeof(T); } return aBuffer; } template void Write(const T& aItem) { if (!mReadOnly) { Grow(mOffset + sizeof(T)); memcpy(mData + mOffset, &aItem, sizeof(T)); mOffset += sizeof(T); } } template void Write(const T *aBuffer, s32 aCount) { if (!mReadOnly) { Grow(mOffset + aCount * sizeof(T)); memcpy(mData + mOffset, aBuffer, aCount * sizeof(T)); mOffset += aCount * sizeof(T); } } void Skip(s32 aAmount) const { mOffset += aAmount; } private: const char *mFilename; u8 *mData; s32 mSize; s32 mCapacity; mutable s32 mOffset; bool mReadOnly; }; // // 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(BinFile *aFile) { s32 _Length = aFile->Read(); Resize(_Length); aFile->Read(mBuffer, _Length); } void Write(BinFile *aFile) const { aFile->Write(mCount); aFile->Write(mBuffer, mCount); } private: T *mBuffer; s32 mCount; s32 mCapacity; }; // // DynOS String // A fixed-size string that doesn't require heap memory allocation // #define STRING_SIZE 255 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(BinFile *aFile) { mCount = aFile->Read(); aFile->Read(mBuffer, mCount); mBuffer[mCount] = 0; } void Write(BinFile *aFile) const { aFile->Write(mCount); aFile->Write(mBuffer, mCount); } 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 mBehaviorScripts; 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; bool mLoaded; }; 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