#include "dynos.cpp.h" #include extern "C" { #include #include "sm64.h" #include "include/textures.h" #include "src/pc/lua/smlua.h" #include "src/pc/lua/utils/smlua_gfx_utils.h" #include "include/macros.h" } #define GFX_PARAM_TYPE_INT 'i' #define GFX_PARAM_TYPE_STR 's' #define GFX_PARAM_TYPE_PTR 'p' #define GFX_PARAM_TYPE_VTX 'v' #define GFX_PARAM_TYPE_TEX 't' #define GFX_PARAM_TYPE_GFX 'g' #define GFX_PARAM_TYPE_IS_POINTER(paramType) ( \ paramType == GFX_PARAM_TYPE_PTR || \ paramType == GFX_PARAM_TYPE_VTX || \ paramType == GFX_PARAM_TYPE_TEX || \ paramType == GFX_PARAM_TYPE_GFX \ ) #define GFX_PARAM_TYPE_IS_INT_OR_CONSTANT(paramType) ( \ paramType == GFX_PARAM_TYPE_INT || \ paramType == GFX_PARAM_TYPE_STR \ ) typedef char GfxParamType; static std::map> sGfxCommandCache; static char *sGfxCommandErrorMsg = NULL; static u32 sGfxCommandErrorSize = 0; #define PrintDataErrorGfx(...) { \ if (sGfxCommandErrorMsg) { \ snprintf(sGfxCommandErrorMsg, sGfxCommandErrorSize, __VA_ARGS__); \ aGfxData->mErrorCount++; \ } else { \ PrintDataError(__VA_ARGS__); \ } \ } #define CHECK_TOKEN_INDEX(tokenIndex, returnValue) { \ if (tokenIndex >= aNode->mTokens.Count()) { \ PrintDataErrorGfx(" ERROR: Invalid token index: %llu, tokens count is: %d", tokenIndex, aNode->mTokens.Count()); \ return returnValue; \ } \ } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnarrowing" #define DISPLAY_LIST_SIZE_PER_TOKEN 4 ///////////// // Parsing // ///////////// #define gfx_constant(x) if (_Arg == #x) { return (s64) (x); } s64 DynOS_Gfx_ParseGfxConstants(const String& _Arg, bool* found) { *found = true; // Constants gfx_constant(NULL); gfx_constant(G_ON); gfx_constant(G_OFF); gfx_constant(LIGHT_1); gfx_constant(LIGHT_2); gfx_constant(LIGHT_3); gfx_constant(LIGHT_4); gfx_constant(LIGHT_5); gfx_constant(LIGHT_6); gfx_constant(LIGHT_7); gfx_constant(LIGHT_8); // Combine modes gfx_constant(G_CCMUX_COMBINED); gfx_constant(G_CCMUX_TEXEL0); gfx_constant(G_CCMUX_TEXEL1); gfx_constant(G_CCMUX_PRIMITIVE); gfx_constant(G_CCMUX_SHADE); gfx_constant(G_CCMUX_ENVIRONMENT); gfx_constant(G_CCMUX_CENTER); gfx_constant(G_CCMUX_SCALE); gfx_constant(G_CCMUX_COMBINED_ALPHA); gfx_constant(G_CCMUX_TEXEL0_ALPHA); gfx_constant(G_CCMUX_TEXEL1_ALPHA); gfx_constant(G_CCMUX_PRIMITIVE_ALPHA); gfx_constant(G_CCMUX_SHADE_ALPHA); gfx_constant(G_CCMUX_ENV_ALPHA); gfx_constant(G_CCMUX_LOD_FRACTION); gfx_constant(G_CCMUX_PRIM_LOD_FRAC); gfx_constant(G_CCMUX_NOISE); gfx_constant(G_CCMUX_K4); gfx_constant(G_CCMUX_K5); gfx_constant(G_CCMUX_1); gfx_constant(G_CCMUX_0); gfx_constant(G_ACMUX_COMBINED); gfx_constant(G_ACMUX_TEXEL0); gfx_constant(G_ACMUX_TEXEL1); gfx_constant(G_ACMUX_PRIMITIVE); gfx_constant(G_ACMUX_SHADE); gfx_constant(G_ACMUX_ENVIRONMENT); gfx_constant(G_ACMUX_LOD_FRACTION); gfx_constant(G_ACMUX_PRIM_LOD_FRAC); gfx_constant(G_ACMUX_1); gfx_constant(G_ACMUX_0); // Light constants gfx_constant(NUMLIGHTS_0); gfx_constant(NUMLIGHTS_1); gfx_constant(NUMLIGHTS_2); gfx_constant(NUMLIGHTS_3); gfx_constant(NUMLIGHTS_4); gfx_constant(NUMLIGHTS_5); gfx_constant(NUMLIGHTS_6); gfx_constant(NUMLIGHTS_7); // Image formats gfx_constant(G_IM_FMT_RGBA); gfx_constant(G_IM_FMT_YUV); gfx_constant(G_IM_FMT_CI); gfx_constant(G_IM_FMT_IA); gfx_constant(G_IM_FMT_I); gfx_constant(G_IM_SIZ_4b); gfx_constant(G_IM_SIZ_8b); gfx_constant(G_IM_SIZ_16b); gfx_constant(G_IM_SIZ_32b); gfx_constant(G_IM_SIZ_DD); // Image constants gfx_constant(G_IM_SIZ_4b_BYTES); gfx_constant(G_IM_SIZ_4b_TILE_BYTES); gfx_constant(G_IM_SIZ_4b_LINE_BYTES); gfx_constant(G_IM_SIZ_8b_BYTES); gfx_constant(G_IM_SIZ_8b_TILE_BYTES); gfx_constant(G_IM_SIZ_8b_LINE_BYTES); gfx_constant(G_IM_SIZ_16b_BYTES); gfx_constant(G_IM_SIZ_16b_TILE_BYTES); gfx_constant(G_IM_SIZ_16b_LINE_BYTES); gfx_constant(G_IM_SIZ_32b_BYTES); gfx_constant(G_IM_SIZ_32b_TILE_BYTES); gfx_constant(G_IM_SIZ_32b_LINE_BYTES); gfx_constant(G_IM_SIZ_4b_LOAD_BLOCK); gfx_constant(G_IM_SIZ_8b_LOAD_BLOCK); gfx_constant(G_IM_SIZ_16b_LOAD_BLOCK); gfx_constant(G_IM_SIZ_32b_LOAD_BLOCK); gfx_constant(G_IM_SIZ_4b_SHIFT); gfx_constant(G_IM_SIZ_8b_SHIFT); gfx_constant(G_IM_SIZ_16b_SHIFT); gfx_constant(G_IM_SIZ_32b_SHIFT); gfx_constant(G_IM_SIZ_4b_INCR); gfx_constant(G_IM_SIZ_8b_INCR); gfx_constant(G_IM_SIZ_16b_INCR); gfx_constant(G_IM_SIZ_32b_INCR); // Tile formats gfx_constant(G_TX_RENDERTILE); gfx_constant(G_TX_LOADTILE); gfx_constant(G_TX_NOMIRROR); gfx_constant(G_TX_WRAP); gfx_constant(G_TX_MIRROR); gfx_constant(G_TX_CLAMP); gfx_constant(G_TX_NOMASK); gfx_constant(G_TX_NOLOD); // Render modes gfx_constant(G_RM_AA_ZB_OPA_SURF); gfx_constant(G_RM_AA_ZB_OPA_SURF2); gfx_constant(G_RM_AA_ZB_XLU_SURF); gfx_constant(G_RM_AA_ZB_XLU_SURF2); gfx_constant(G_RM_AA_ZB_OPA_DECAL); gfx_constant(G_RM_AA_ZB_OPA_DECAL2); gfx_constant(G_RM_AA_ZB_XLU_DECAL); gfx_constant(G_RM_AA_ZB_XLU_DECAL2); gfx_constant(G_RM_AA_ZB_OPA_INTER); gfx_constant(G_RM_AA_ZB_OPA_INTER2); gfx_constant(G_RM_AA_ZB_XLU_INTER); gfx_constant(G_RM_AA_ZB_XLU_INTER2); gfx_constant(G_RM_AA_ZB_XLU_LINE); gfx_constant(G_RM_AA_ZB_XLU_LINE2); gfx_constant(G_RM_AA_ZB_DEC_LINE); gfx_constant(G_RM_AA_ZB_DEC_LINE2); gfx_constant(G_RM_AA_ZB_TEX_EDGE); gfx_constant(G_RM_AA_ZB_TEX_EDGE2); gfx_constant(G_RM_AA_ZB_TEX_INTER); gfx_constant(G_RM_AA_ZB_TEX_INTER2); gfx_constant(G_RM_AA_ZB_SUB_SURF); gfx_constant(G_RM_AA_ZB_SUB_SURF2); gfx_constant(G_RM_AA_ZB_PCL_SURF); gfx_constant(G_RM_AA_ZB_PCL_SURF2); gfx_constant(G_RM_AA_ZB_OPA_TERR); gfx_constant(G_RM_AA_ZB_OPA_TERR2); gfx_constant(G_RM_AA_ZB_TEX_TERR); gfx_constant(G_RM_AA_ZB_TEX_TERR2); gfx_constant(G_RM_AA_ZB_SUB_TERR); gfx_constant(G_RM_AA_ZB_SUB_TERR2); gfx_constant(G_RM_RA_ZB_OPA_SURF); gfx_constant(G_RM_RA_ZB_OPA_SURF2); gfx_constant(G_RM_RA_ZB_OPA_DECAL); gfx_constant(G_RM_RA_ZB_OPA_DECAL2); gfx_constant(G_RM_RA_ZB_OPA_INTER); gfx_constant(G_RM_RA_ZB_OPA_INTER2); gfx_constant(G_RM_AA_OPA_SURF); gfx_constant(G_RM_AA_OPA_SURF2); gfx_constant(G_RM_AA_XLU_SURF); gfx_constant(G_RM_AA_XLU_SURF2); gfx_constant(G_RM_AA_XLU_LINE); gfx_constant(G_RM_AA_XLU_LINE2); gfx_constant(G_RM_AA_DEC_LINE); gfx_constant(G_RM_AA_DEC_LINE2); gfx_constant(G_RM_AA_TEX_EDGE); gfx_constant(G_RM_AA_TEX_EDGE2); gfx_constant(G_RM_AA_SUB_SURF); gfx_constant(G_RM_AA_SUB_SURF2); gfx_constant(G_RM_AA_PCL_SURF); gfx_constant(G_RM_AA_PCL_SURF2); gfx_constant(G_RM_AA_OPA_TERR); gfx_constant(G_RM_AA_OPA_TERR2); gfx_constant(G_RM_AA_TEX_TERR); gfx_constant(G_RM_AA_TEX_TERR2); gfx_constant(G_RM_AA_SUB_TERR); gfx_constant(G_RM_AA_SUB_TERR2); gfx_constant(G_RM_RA_OPA_SURF); gfx_constant(G_RM_RA_OPA_SURF2); gfx_constant(G_RM_ZB_OPA_SURF); gfx_constant(G_RM_ZB_OPA_SURF2); gfx_constant(G_RM_ZB_XLU_SURF); gfx_constant(G_RM_ZB_XLU_SURF2); gfx_constant(G_RM_ZB_OPA_DECAL); gfx_constant(G_RM_ZB_OPA_DECAL2); gfx_constant(G_RM_ZB_XLU_DECAL); gfx_constant(G_RM_ZB_XLU_DECAL2); gfx_constant(G_RM_ZB_CLD_SURF); gfx_constant(G_RM_ZB_CLD_SURF2); gfx_constant(G_RM_ZB_OVL_SURF); gfx_constant(G_RM_ZB_OVL_SURF2); gfx_constant(G_RM_ZB_PCL_SURF); gfx_constant(G_RM_ZB_PCL_SURF2); gfx_constant(G_RM_OPA_SURF); gfx_constant(G_RM_OPA_SURF2); gfx_constant(G_RM_XLU_SURF); gfx_constant(G_RM_XLU_SURF2); gfx_constant(G_RM_CLD_SURF); gfx_constant(G_RM_CLD_SURF2); gfx_constant(G_RM_TEX_EDGE); gfx_constant(G_RM_TEX_EDGE2); gfx_constant(G_RM_PCL_SURF); gfx_constant(G_RM_PCL_SURF2); gfx_constant(G_RM_ADD); gfx_constant(G_RM_ADD2); gfx_constant(G_RM_NOOP); gfx_constant(G_RM_NOOP2); gfx_constant(G_RM_VISCVG); gfx_constant(G_RM_VISCVG2); gfx_constant(G_RM_OPA_CI); gfx_constant(G_RM_OPA_CI2); gfx_constant(G_RM_CUSTOM_AA_ZB_XLU_SURF); gfx_constant(G_RM_CUSTOM_AA_ZB_XLU_SURF2); gfx_constant(G_RM_FOG_SHADE_A); gfx_constant(G_RM_FOG_PRIM_A); gfx_constant(G_RM_PASS); // Geometry modes gfx_constant(G_ZBUFFER); gfx_constant(G_SHADE); gfx_constant(G_TEXTURE_ENABLE); gfx_constant(G_SHADING_SMOOTH); gfx_constant(G_CULL_FRONT); gfx_constant(G_CULL_BACK); gfx_constant(G_CULL_BOTH); gfx_constant(G_FOG); gfx_constant(G_LIGHTING); gfx_constant(G_TEXTURE_GEN); gfx_constant(G_TEXTURE_GEN_LINEAR); gfx_constant(G_LOD); gfx_constant(G_CLIPPING); // Alpha modes gfx_constant(G_AC_NONE); gfx_constant(G_AC_THRESHOLD); gfx_constant(G_AC_DITHER); // Other modes gfx_constant(G_MDSFT_ALPHACOMPARE); gfx_constant(G_MDSFT_ZSRCSEL); gfx_constant(G_MDSFT_RENDERMODE); gfx_constant(G_MDSFT_BLENDER); gfx_constant(G_MDSFT_BLENDMASK); gfx_constant(G_MDSFT_ALPHADITHER); gfx_constant(G_MDSFT_RGBDITHER); gfx_constant(G_MDSFT_COMBKEY); gfx_constant(G_MDSFT_TEXTCONV); gfx_constant(G_MDSFT_TEXTFILT); gfx_constant(G_MDSFT_TEXTLUT); gfx_constant(G_MDSFT_TEXTLOD); gfx_constant(G_MDSFT_TEXTDETAIL); gfx_constant(G_MDSFT_TEXTPERSP); gfx_constant(G_MDSFT_CYCLETYPE); gfx_constant(G_MDSFT_COLORDITHER); gfx_constant(G_MDSFT_PIPELINE); gfx_constant(G_PM_1PRIMITIVE); gfx_constant(G_PM_NPRIMITIVE); gfx_constant(G_CYC_1CYCLE); gfx_constant(G_CYC_2CYCLE); gfx_constant(G_CYC_COPY); gfx_constant(G_CYC_FILL); gfx_constant(G_TP_NONE); gfx_constant(G_TP_PERSP); gfx_constant(G_TD_CLAMP); gfx_constant(G_TD_SHARPEN); gfx_constant(G_TD_DETAIL); gfx_constant(G_TL_TILE); gfx_constant(G_TL_LOD); gfx_constant(G_TT_NONE); gfx_constant(G_TT_RGBA16); gfx_constant(G_TT_IA16); gfx_constant(G_TF_POINT); gfx_constant(G_TF_AVERAGE); gfx_constant(G_TF_BILERP); gfx_constant(G_TC_CONV); gfx_constant(G_TC_FILTCONV); gfx_constant(G_TC_FILT); gfx_constant(G_CK_NONE); gfx_constant(G_CK_KEY); gfx_constant(G_CD_MAGICSQ); gfx_constant(G_CD_BAYER); gfx_constant(G_CD_NOISE); gfx_constant(G_CD_DISABLE); gfx_constant(G_CD_ENABLE); gfx_constant(G_AD_PATTERN); gfx_constant(G_AD_NOTPATTERN); gfx_constant(G_AD_NOISE); gfx_constant(G_AD_DISABLE); gfx_constant(G_AC_NONE); gfx_constant(G_AC_THRESHOLD); gfx_constant(G_AC_DITHER); gfx_constant(G_ZS_PIXEL); gfx_constant(G_ZS_PRIM); gfx_constant(G_SETOTHERMODE_H); gfx_constant(G_SETOTHERMODE_L); gfx_constant(G_BL_CLR_IN); gfx_constant(G_BL_CLR_MEM); gfx_constant(G_BL_CLR_BL); gfx_constant(G_BL_CLR_FOG); gfx_constant(G_BL_1MA); gfx_constant(G_BL_A_MEM); gfx_constant(G_BL_A_IN); gfx_constant(G_BL_A_FOG); gfx_constant(G_BL_A_SHADE); gfx_constant(G_BL_1); gfx_constant(G_BL_0); gfx_constant(AA_EN); gfx_constant(Z_CMP); gfx_constant(Z_UPD); gfx_constant(ZMODE_OPA); gfx_constant(ZMODE_INTER); gfx_constant(ZMODE_XLU); gfx_constant(ZMODE_DEC); gfx_constant(IM_RD); gfx_constant(CLR_ON_CVG); gfx_constant(CVG_DST_CLAMP); gfx_constant(CVG_DST_WRAP); gfx_constant(CVG_DST_FULL); gfx_constant(CVG_DST_SAVE); gfx_constant(CVG_X_ALPHA); gfx_constant(ALPHA_CVG_SEL); gfx_constant(FORCE_BL); gfx_constant(TEX_EDGE); // MW constants gfx_constant(G_MW_MATRIX); gfx_constant(G_MW_NUMLIGHT); gfx_constant(G_MW_CLIP); gfx_constant(G_MW_SEGMENT); gfx_constant(G_MW_FOG); gfx_constant(G_MW_LIGHTCOL); // PM constants gfx_constant(G_PM_1PRIMITIVE); gfx_constant(G_PM_NPRIMITIVE); // Texture/scale/rotate constants gfx_constant(G_TEXTURE_IMAGE_FRAC); gfx_constant(G_TEXTURE_SCALE_FRAC); gfx_constant(G_SCALE_FRAC); gfx_constant(G_ROTATE_FRAC); // Player parts gfx_constant(PANTS); gfx_constant(SHIRT); gfx_constant(GLOVES); gfx_constant(SHOES); gfx_constant(HAIR); gfx_constant(SKIN); gfx_constant(CAP); gfx_constant(EMBLEM); gfx_constant(METAL); // Extended gfx_constant(G_LIGHT_MAP_EXT); gfx_constant(G_LIGHTING_ENGINE_EXT); gfx_constant(G_PACKED_NORMALS_EXT); gfx_constant(G_CULL_INVERT_EXT); gfx_constant(G_FRESNEL_COLOR_EXT); gfx_constant(G_FRESNEL_ALPHA_EXT); gfx_constant(G_COL_PRIM); gfx_constant(G_COL_ENV); gfx_constant(G_CP_LIGHT); gfx_constant(G_CP_AMBIENT); // Common values gfx_constant(CALC_DXT(4,G_IM_SIZ_4b_BYTES)); gfx_constant(CALC_DXT(8,G_IM_SIZ_4b_BYTES)); gfx_constant(CALC_DXT(16,G_IM_SIZ_4b_BYTES)); gfx_constant(CALC_DXT(32,G_IM_SIZ_4b_BYTES)); gfx_constant(CALC_DXT(64,G_IM_SIZ_4b_BYTES)); gfx_constant(CALC_DXT(128,G_IM_SIZ_4b_BYTES)); gfx_constant(CALC_DXT(256,G_IM_SIZ_4b_BYTES)); gfx_constant(CALC_DXT(4,G_IM_SIZ_8b_BYTES)); gfx_constant(CALC_DXT(8,G_IM_SIZ_8b_BYTES)); gfx_constant(CALC_DXT(16,G_IM_SIZ_8b_BYTES)); gfx_constant(CALC_DXT(32,G_IM_SIZ_8b_BYTES)); gfx_constant(CALC_DXT(64,G_IM_SIZ_8b_BYTES)); gfx_constant(CALC_DXT(128,G_IM_SIZ_8b_BYTES)); gfx_constant(CALC_DXT(256,G_IM_SIZ_8b_BYTES)); gfx_constant(CALC_DXT(4,G_IM_SIZ_16b_BYTES)); gfx_constant(CALC_DXT(8,G_IM_SIZ_16b_BYTES)); gfx_constant(CALC_DXT(16,G_IM_SIZ_16b_BYTES)); gfx_constant(CALC_DXT(32,G_IM_SIZ_16b_BYTES)); gfx_constant(CALC_DXT(64,G_IM_SIZ_16b_BYTES)); gfx_constant(CALC_DXT(128,G_IM_SIZ_16b_BYTES)); gfx_constant(CALC_DXT(256,G_IM_SIZ_16b_BYTES)); gfx_constant(CALC_DXT(4,G_IM_SIZ_32b_BYTES)); gfx_constant(CALC_DXT(8,G_IM_SIZ_32b_BYTES)); gfx_constant(CALC_DXT(16,G_IM_SIZ_32b_BYTES)); gfx_constant(CALC_DXT(32,G_IM_SIZ_32b_BYTES)); gfx_constant(CALC_DXT(64,G_IM_SIZ_32b_BYTES)); gfx_constant(CALC_DXT(128,G_IM_SIZ_32b_BYTES)); gfx_constant(CALC_DXT(256,G_IM_SIZ_32b_BYTES)); gfx_constant(CALC_DXT_4b(4)); gfx_constant(CALC_DXT_4b(8)); gfx_constant(CALC_DXT_4b(16)); gfx_constant(CALC_DXT_4b(32)); gfx_constant(CALC_DXT_4b(64)); gfx_constant(CALC_DXT_4b(128)); gfx_constant(CALC_DXT_4b(256)); *found = false; return 0; } static s64 ParseGfxSymbolArg(GfxData* aGfxData, DataNode* aNode, u64* pTokenIndex, const char *aPrefix, const GfxParamType aParamType) { assert(aPrefix != NULL); if (pTokenIndex != NULL) { CHECK_TOKEN_INDEX(*pTokenIndex, 0); } String _Token = (pTokenIndex != NULL ? aNode->mTokens[(*pTokenIndex)++] : ""); String _Arg("%s%s", aPrefix, _Token.begin()); // Integers if (GFX_PARAM_TYPE_IS_INT_OR_CONSTANT(aParamType)) { bool integerFound = false; s64 integerValue = DynOS_Misc_ParseInteger(_Arg, &integerFound); if (integerFound) { return integerValue; } bool constantFound = false; s64 constantValue = DynOS_Gfx_ParseGfxConstants(_Arg, &constantFound); if (constantFound) { return constantValue; } } // Pointers if (GFX_PARAM_TYPE_IS_POINTER(aParamType)) { // NULL pointer if (_Arg == "NULL" || _Arg == "0") { return (s64) NULL; } // Raw pointers for (auto& _Node : aGfxData->mRawPointers) { if (_Arg == _Node->mName) { return (s64) _Node->mData; } } } // Offset s32 _Offset = 0; s32 _Plus = _Arg.Find('+'); if (_Plus != -1) { _Offset = _Arg.SubString(_Plus + 1).ParseInt(); _Arg = _Arg.SubString(0, _Plus); } // Lights if (aParamType == GFX_PARAM_TYPE_PTR) { for (auto& _Node : aGfxData->mLights) { // Light pointer if (_Arg == _Node->mName) { return (s64) DynOS_Lights_Parse(aGfxData, _Node)->mData; } // Ambient pointer String _Ambient("&%s.a", _Node->mName.begin()); if (_Arg == _Ambient) { return (s64) &(DynOS_Lights_Parse(aGfxData, _Node)->mData->a); } // Diffuse pointer String _Diffuse("&%s.l", _Node->mName.begin()); if (_Arg == _Diffuse) { return (s64) &(DynOS_Lights_Parse(aGfxData, _Node)->mData->l[0]); } } } // Textures if (aParamType == GFX_PARAM_TYPE_TEX) { for (auto& _Node : aGfxData->mTextures) { if (_Arg == _Node->mName) { return (s64) DynOS_Tex_Parse(aGfxData, _Node); } } } // Vertex arrays if (aParamType == GFX_PARAM_TYPE_VTX) { for (auto& _Node : aGfxData->mVertices) { if (_Arg == _Node->mName) { auto base = DynOS_Vtx_Parse(aGfxData, _Node)->mData; auto data = (u8*)base + _Offset; if (_Offset != 0) { aGfxData->mPointerOffsetList.Add({ (const void*)data, (const void*)base }); } return (s64) data; } } } // Display lists if (aParamType == GFX_PARAM_TYPE_GFX) { for (auto& _Node : aGfxData->mDisplayLists) { if (_Arg == _Node->mName) { return (s64) DynOS_Gfx_Parse(aGfxData, _Node); } } } if (aParamType == GFX_PARAM_TYPE_PTR) { for (auto& _Node : aGfxData->mLight0s) { // Light pointer if (_Arg == _Node->mName) { return (s64) DynOS_Light0_Parse(aGfxData, _Node)->mData; } } for (auto& _Node : aGfxData->mLightTs) { // Light pointer if (_Arg == _Node->mName) { return (s64) DynOS_LightT_Parse(aGfxData, _Node)->mData; } // Diffuse pointer String _Diffuse("&%s.col", _Node->mName.begin()); if (_Arg == _Diffuse) { return (s64) &(DynOS_LightT_Parse(aGfxData, _Node)->mData->col[0]); } // Diffuse copy pointer String _DiffuseC("&%s.colc", _Node->mName.begin()); if (_Arg == _DiffuseC) { return (s64) &(DynOS_LightT_Parse(aGfxData, _Node)->mData->colc[0]); } // Dir pointer String _Dir("&%s.dir", _Node->mName.begin()); if (_Arg == _Dir) { return (s64) &(DynOS_LightT_Parse(aGfxData, _Node)->mData->dir[0]); } } for (auto& _Node : aGfxData->mAmbientTs) { // Light pointer if (_Arg == _Node->mName) { return (s64) DynOS_AmbientT_Parse(aGfxData, _Node)->mData; } // Diffuse pointer String _Diffuse("&%s.col", _Node->mName.begin()); if (_Arg == _Diffuse) { return (s64) &(DynOS_AmbientT_Parse(aGfxData, _Node)->mData->col[0]); } // Diffuse copy pointer String _DiffuseC("&%s.colc", _Node->mName.begin()); if (_Arg == _DiffuseC) { return (s64) &(DynOS_AmbientT_Parse(aGfxData, _Node)->mData->colc[0]); } } } // Built-in textures if (aParamType == GFX_PARAM_TYPE_TEX) { auto builtinTex = DynOS_Builtin_Tex_GetFromName(_Arg.begin()); if (builtinTex != NULL) { return (s64)builtinTex; } } // Recursive descent parsing if (GFX_PARAM_TYPE_IS_INT_OR_CONSTANT(aParamType)) { bool rdSuccess = false; s64 rdValue = DynOS_RecursiveDescent_Parse(_Arg.begin(), &rdSuccess, DynOS_Gfx_ParseGfxConstants); if (rdSuccess) { return rdValue; } } // Unknown PrintDataErrorGfx(" ERROR: Unknown gfx arg: %s", _Arg.begin()); return 0; } #define gfx_arg_with_suffix(argname, suffix, paramtype) \ CHECK_TOKEN_INDEX(aTokenIndex,); \ const String& argname##_token = aNode->mTokens[aTokenIndex]; \ String _Token##suffix = String("%s%s", argname##_token.begin(), #suffix); \ s64 argname = ParseGfxSymbolArg(aGfxData, aNode, NULL, _Token##suffix.begin(), paramtype); \ #define STR_VALUE_2(...) #__VA_ARGS__ #define STR_VALUE(...) STR_VALUE_2(__VA_ARGS__) #define gfx_set_combine_mode_arg(x) if (_Arg == #x) { return String("%s", STR_VALUE(x)); } static String ConvertSetCombineModeArgToString(GfxData *aGfxData, const String& _Arg) { gfx_set_combine_mode_arg(G_CC_PRIMITIVE); gfx_set_combine_mode_arg(G_CC_SHADE); gfx_set_combine_mode_arg(G_CC_MODULATEI); gfx_set_combine_mode_arg(G_CC_MODULATEIDECALA); gfx_set_combine_mode_arg(G_CC_MODULATEIFADE); gfx_set_combine_mode_arg(G_CC_MODULATERGB); gfx_set_combine_mode_arg(G_CC_MODULATERGBDECALA); gfx_set_combine_mode_arg(G_CC_MODULATERGBFADE); gfx_set_combine_mode_arg(G_CC_MODULATEIA); gfx_set_combine_mode_arg(G_CC_MODULATEIFADEA); gfx_set_combine_mode_arg(G_CC_MODULATEFADE); gfx_set_combine_mode_arg(G_CC_MODULATERGBA); gfx_set_combine_mode_arg(G_CC_MODULATERGBFADEA); gfx_set_combine_mode_arg(G_CC_MODULATEI_PRIM); gfx_set_combine_mode_arg(G_CC_MODULATEIA_PRIM); gfx_set_combine_mode_arg(G_CC_MODULATEIDECALA_PRIM); gfx_set_combine_mode_arg(G_CC_MODULATERGB_PRIM); gfx_set_combine_mode_arg(G_CC_MODULATERGBA_PRIM); gfx_set_combine_mode_arg(G_CC_MODULATERGBDECALA_PRIM); gfx_set_combine_mode_arg(G_CC_FADE); gfx_set_combine_mode_arg(G_CC_FADEA); gfx_set_combine_mode_arg(G_CC_DECALRGB); gfx_set_combine_mode_arg(G_CC_DECALRGBA); gfx_set_combine_mode_arg(G_CC_DECALFADE); gfx_set_combine_mode_arg(G_CC_DECALFADEA); gfx_set_combine_mode_arg(G_CC_BLENDI); gfx_set_combine_mode_arg(G_CC_BLENDIA); gfx_set_combine_mode_arg(G_CC_BLENDIDECALA); gfx_set_combine_mode_arg(G_CC_BLENDRGBA); gfx_set_combine_mode_arg(G_CC_BLENDRGBDECALA); gfx_set_combine_mode_arg(G_CC_BLENDRGBFADEA); gfx_set_combine_mode_arg(G_CC_ADDRGB); gfx_set_combine_mode_arg(G_CC_ADDRGBDECALA); gfx_set_combine_mode_arg(G_CC_ADDRGBFADE); gfx_set_combine_mode_arg(G_CC_REFLECTRGB); gfx_set_combine_mode_arg(G_CC_REFLECTRGBDECALA); gfx_set_combine_mode_arg(G_CC_HILITERGB); gfx_set_combine_mode_arg(G_CC_HILITERGBA); gfx_set_combine_mode_arg(G_CC_HILITERGBDECALA); gfx_set_combine_mode_arg(G_CC_SHADEDECALA); gfx_set_combine_mode_arg(G_CC_SHADEFADEA); gfx_set_combine_mode_arg(G_CC_BLENDPE); gfx_set_combine_mode_arg(G_CC_BLENDPEDECALA); gfx_set_combine_mode_arg(_G_CC_BLENDPE); gfx_set_combine_mode_arg(_G_CC_BLENDPEDECALA); gfx_set_combine_mode_arg(_G_CC_TWOCOLORTEX); gfx_set_combine_mode_arg(_G_CC_SPARSEST); gfx_set_combine_mode_arg(G_CC_TEMPLERP); gfx_set_combine_mode_arg(G_CC_TRILERP); gfx_set_combine_mode_arg(G_CC_INTERFERENCE); gfx_set_combine_mode_arg(G_CC_1CYUV2RGB); gfx_set_combine_mode_arg(G_CC_YUV2RGB); gfx_set_combine_mode_arg(G_CC_PASS2); gfx_set_combine_mode_arg(G_CC_MODULATEI2); gfx_set_combine_mode_arg(G_CC_MODULATEIA2); gfx_set_combine_mode_arg(G_CC_MODULATERGB2); gfx_set_combine_mode_arg(G_CC_MODULATERGBA2); gfx_set_combine_mode_arg(G_CC_MODULATEI_PRIM2); gfx_set_combine_mode_arg(G_CC_MODULATEIA_PRIM2); gfx_set_combine_mode_arg(G_CC_MODULATERGB_PRIM2); gfx_set_combine_mode_arg(G_CC_MODULATERGBA_PRIM2); gfx_set_combine_mode_arg(G_CC_DECALRGB2); gfx_set_combine_mode_arg(G_CC_BLENDI2); gfx_set_combine_mode_arg(G_CC_BLENDIA2); gfx_set_combine_mode_arg(G_CC_CHROMA_KEY2); gfx_set_combine_mode_arg(G_CC_HILITERGB2); gfx_set_combine_mode_arg(G_CC_HILITERGBA2); gfx_set_combine_mode_arg(G_CC_HILITERGBDECALA2); gfx_set_combine_mode_arg(G_CC_HILITERGBPASSA2); PrintDataErrorGfx(" ERROR: Unknown gfx gsDPSetCombineMode arg: %s", _Arg.begin()); return ""; } static Array ParseGfxSetCombineMode(GfxData* aGfxData, DataNode* aNode, u64* pTokenIndex) { CHECK_TOKEN_INDEX(*pTokenIndex, Array()); String _Buffer = ConvertSetCombineModeArgToString(aGfxData, aNode->mTokens[(*pTokenIndex)++]); Array _Args; String _Token; for (u64 i = 0, n = _Buffer.Length(); i <= n; ++i) { if (i == n || _Buffer[i] == ' ' || _Buffer[i] == '\t' || _Buffer[i] == ',') { if (_Token.Length() != 0) { String _Arg("%s%s", (_Args.Count() < 4 ? "G_CCMUX_" : "G_ACMUX_"), _Token.begin()); _Args.Add(ParseGfxSymbolArg(aGfxData, aNode, NULL, _Arg.begin(), GFX_PARAM_TYPE_INT)); _Token.Clear(); } } else { _Token.Add(_Buffer[i]); } } if (_Args.Count() < 8) { PrintDataErrorGfx(" ERROR: gsDPSetCombineMode %s: Not enough arguments", _Buffer.begin()); } return _Args; } static void UpdateTextureInfo(GfxData* aGfxData, s64 *aTexPtr, s32 aFormat, s32 aSize, s32 aWidth, s32 aHeight) { // Update current texture pointers if (aTexPtr && (*aTexPtr)) { if (DynOS_Builtin_Tex_GetFromData(*(const Texture**)aTexPtr)) { return; } // Skip raw pointers for (const auto &ptrNode : aGfxData->mRawPointers) { if ((void *) *aTexPtr == ptrNode->mData) { return; } } aGfxData->mGfxContext.mCurrentTexture = (DataNode*) (*aTexPtr); } // Update texture info if not loaded from a file if (aGfxData->mGfxContext.mCurrentTexture && aGfxData->mGfxContext.mCurrentTexture->mData && aGfxData->mGfxContext.mCurrentTexture->mData->mPngData.Empty()) { if (aFormat != -1) aGfxData->mGfxContext.mCurrentTexture->mData->mRawFormat = aFormat; if (aSize != -1) aGfxData->mGfxContext.mCurrentTexture->mData->mRawSize = aSize; if (aWidth != -1) aGfxData->mGfxContext.mCurrentTexture->mData->mRawWidth = aWidth; if (aHeight != -1) aGfxData->mGfxContext.mCurrentTexture->mData->mRawHeight = aHeight; } } static void SetCurrentTextureAsPalette(GfxData* aGfxData) { if (aGfxData->mGfxContext.mCurrentTexture) { aGfxData->mGfxContext.mCurrentPalette = aGfxData->mGfxContext.mCurrentTexture; } } static void ParseGfxSymbol(GfxData* aGfxData, DataNode* aNode, Gfx*& aHead, u64& aTokenIndex) { CHECK_TOKEN_INDEX(aTokenIndex,); const String& _Symbol = aNode->mTokens[aTokenIndex++]; // Simple symbols // Uses macro iterators to dynamically handle the correct number of parameters #define HANDLE_PARAM(paramNum) s64 _Arg##paramNum = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", paramTypes[paramNum - 1]); #define GET_ARG(paramNum) _Arg##paramNum #define CALL_SYMB(symb, ...) symb(__VA_ARGS__) #define define_gfx_symbol(symb, params, addPtr, ...) \ if (_Symbol == #symb) { \ static const GfxParamType paramTypes[] = { __VA_ARGS__ }; \ REPEAT(HANDLE_PARAM, params); \ if (addPtr) { aGfxData->mPointerList.Add(aHead); } \ Gfx _Gfx[] = { CALL_SYMB(symb, LIST_ARGS(GET_ARG, params)) }; \ memcpy(aHead, _Gfx, sizeof(_Gfx)); \ aHead += (sizeof(_Gfx) / sizeof(_Gfx[0])); \ return; \ } #define define_gfx_symbol_manual(...) #include "gfx_symbols.h" #undef HANDLE_PARAM #undef GET_ARG #undef CALL_SYMB #undef define_gfx_symbol #undef define_gfx_symbol_manual // Special symbols if (_Symbol == "gsSPTexture") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg2 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg3 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg4 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); gSPTexture(aHead++, _Arg0, _Arg1, _Arg2, _Arg3, _Arg4); return; } if (_Symbol == "gsSPSetGeometryMode") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); gSPSetGeometryMode(aHead++, _Arg0); return; } if (_Symbol == "gsSPClearGeometryMode") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); gSPClearGeometryMode(aHead++, _Arg0); return; } if (_Symbol == "gsSPDisplayList") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_GFX); aGfxData->mPointerList.Add(aHead); gSPDisplayList(aHead++, _Arg0); return; } if (_Symbol == "gsSPBranchList") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_GFX); aGfxData->mPointerList.Add(aHead); gSPBranchList(aHead++, _Arg0); return; } if (_Symbol == "gsSPEndDisplayList") { gSPEndDisplayList(aHead++); // Convert raw texture to PNG if all raw members are set if (aGfxData->mGfxContext.mCurrentTexture != NULL && aGfxData->mGfxContext.mCurrentTexture->mData != NULL && aGfxData->mGfxContext.mCurrentTexture->mData->mPngData.Empty() && aGfxData->mGfxContext.mCurrentTexture->mData->mRawFormat != -1 && aGfxData->mGfxContext.mCurrentTexture->mData->mRawSize != -1 && aGfxData->mGfxContext.mCurrentTexture->mData->mRawWidth != -1 && aGfxData->mGfxContext.mCurrentTexture->mData->mRawHeight != -1) { DynOS_Tex_ConvertTextureDataToPng(aGfxData, aGfxData->mGfxContext.mCurrentTexture->mData); } // End the display list parsing after hitting gsSPEndDisplayList aTokenIndex = 0x7FFFFFFF; return; } // Complex symbols if (_Symbol == "gsSPSetLights0") { Lights0 *_Light = (Lights0 *) ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_PTR); gSPNumLights(aHead++, NUMLIGHTS_0); aGfxData->mPointerList.Add(aHead); gSPLight(aHead++, &_Light->l[0], 1); aGfxData->mPointerList.Add(aHead); gSPLight(aHead++, &_Light->a, 2); return; } if (_Symbol == "gsSPSetLights1") { Lights1 *_Light = (Lights1 *) ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_PTR); gSPNumLights(aHead++, NUMLIGHTS_1); aGfxData->mPointerList.Add(aHead); gSPLight(aHead++, &_Light->l[0], 1); aGfxData->mPointerList.Add(aHead); gSPLight(aHead++, &_Light->a, 2); return; } if (_Symbol == "gsDPSetCombineMode") { Array _Args0 = ParseGfxSetCombineMode(aGfxData, aNode, &aTokenIndex); Array _Args1 = ParseGfxSetCombineMode(aGfxData, aNode, &aTokenIndex); Gfx _Gfx = {{ _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(_Args0[0x0], _Args0[0x2], _Args0[0x4], _Args0[0x6]) | GCCc1w0(_Args1[0x0], _Args1[0x2]), 0, 24), (u32) (GCCc0w1(_Args0[0x1], _Args0[0x3], _Args0[0x5], _Args0[0x7]) | GCCc1w1(_Args1[0x1], _Args1[0x4], _Args1[0x6], _Args1[0x3], _Args1[0x5], _Args1[0x7])) }}; *(aHead++) = _Gfx; return; } if (_Symbol == "gsDPSetCombineLERP") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_CCMUX_", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_CCMUX_", GFX_PARAM_TYPE_INT); s64 _Arg2 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_CCMUX_", GFX_PARAM_TYPE_INT); s64 _Arg3 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_CCMUX_", GFX_PARAM_TYPE_INT); s64 _Arg4 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_ACMUX_", GFX_PARAM_TYPE_INT); s64 _Arg5 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_ACMUX_", GFX_PARAM_TYPE_INT); s64 _Arg6 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_ACMUX_", GFX_PARAM_TYPE_INT); s64 _Arg7 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_ACMUX_", GFX_PARAM_TYPE_INT); s64 _Arg8 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_CCMUX_", GFX_PARAM_TYPE_INT); s64 _Arg9 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_CCMUX_", GFX_PARAM_TYPE_INT); s64 _ArgA = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_CCMUX_", GFX_PARAM_TYPE_INT); s64 _ArgB = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_CCMUX_", GFX_PARAM_TYPE_INT); s64 _ArgC = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_ACMUX_", GFX_PARAM_TYPE_INT); s64 _ArgD = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_ACMUX_", GFX_PARAM_TYPE_INT); s64 _ArgE = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_ACMUX_", GFX_PARAM_TYPE_INT); s64 _ArgF = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "G_ACMUX_", GFX_PARAM_TYPE_INT); Gfx _Gfx = {{ _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(_Arg0, _Arg2, _Arg4, _Arg6) | GCCc1w0(_Arg8, _ArgA), 0, 24), (u32) (GCCc0w1(_Arg1, _Arg3, _Arg5, _Arg7) | GCCc1w1(_Arg9, _ArgC, _ArgE, _ArgB, _ArgD, _ArgF)) }}; *(aHead++) = _Gfx; return; } // TexData symbols if (_Symbol == "gsDPSetTextureImage") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg2 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg3 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_TEX); UpdateTextureInfo(aGfxData, &_Arg3, (s32) _Arg0, (s32) _Arg1, -1, -1); aGfxData->mPointerList.Add(aHead); gDPSetTextureImage(aHead++, _Arg0, _Arg1, _Arg2, _Arg3); return; } if (_Symbol == "gsDPSetTile") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg2 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg3 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg4 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg5 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg6 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg7 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg8 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg9 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _ArgA = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _ArgB = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); UpdateTextureInfo(aGfxData, NULL, (s32) _Arg0, (s32) _Arg1, -1, -1); gDPSetTile(aHead++, _Arg0, _Arg1, _Arg2, _Arg3, _Arg4, _Arg5, _Arg6, _Arg7, _Arg8, _Arg9, _ArgA, _ArgB); return; } if (_Symbol == "gsDPLoadTile") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg2 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg3 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg4 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); UpdateTextureInfo(aGfxData, NULL, -1, -1, (s32) (_Arg3 >> G_TEXTURE_IMAGE_FRAC) + 1, (s32) (_Arg4 >> G_TEXTURE_IMAGE_FRAC) + 1); gDPLoadTile(aHead++, _Arg0, _Arg1, _Arg2, _Arg3, _Arg4); return; } if (_Symbol == "gsDPSetTileSize") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg2 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg3 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg4 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); UpdateTextureInfo(aGfxData, NULL, -1, -1, (s32) (_Arg3 >> G_TEXTURE_IMAGE_FRAC) + 1, (s32) (_Arg4 >> G_TEXTURE_IMAGE_FRAC) + 1); gDPSetTileSize(aHead++, _Arg0, _Arg1, _Arg2, _Arg3, _Arg4); return; } if (_Symbol == "gsDPLoadTextureBlock") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_TEX); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); gfx_arg_with_suffix(arg2_0, _LOAD_BLOCK, GFX_PARAM_TYPE_INT); gfx_arg_with_suffix(arg2_1, _INCR, GFX_PARAM_TYPE_INT); gfx_arg_with_suffix(arg2_2, _SHIFT, GFX_PARAM_TYPE_INT); gfx_arg_with_suffix(arg2_3, _BYTES, GFX_PARAM_TYPE_INT); gfx_arg_with_suffix(arg2_4, _LINE_BYTES, GFX_PARAM_TYPE_INT); s64 _Arg2 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg3 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg4 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg5 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg6 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg7 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg8 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg9 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _ArgA = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _ArgB = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); UpdateTextureInfo(aGfxData, &_Arg0, (s32) _Arg1, (s32) _Arg2, (s32) _Arg3, (s32) _Arg4); aGfxData->mPointerList.Add(aHead); gDPSetTextureImage(aHead++, _Arg1, arg2_0, 1, _Arg0); gDPSetTile(aHead++, _Arg1, arg2_0, 0, 0, G_TX_LOADTILE, 0, _Arg7, _Arg9, _ArgB, _Arg6, _Arg8, _ArgA); gDPLoadSync(aHead++); gDPLoadBlock(aHead++, G_TX_LOADTILE, 0, 0, (((_Arg3) * (_Arg4) + arg2_1) >> arg2_2) - 1, CALC_DXT(_Arg3, arg2_3)); gDPPipeSync(aHead++); gDPSetTile(aHead++, _Arg1, _Arg2, ((((_Arg3) * arg2_4) + 7) >> 3), 0, G_TX_RENDERTILE, _Arg5, _Arg7, _Arg9, _ArgB, _Arg6, _Arg8, _ArgA); gDPSetTileSize(aHead++, G_TX_RENDERTILE, 0, 0, (((u64)_Arg3) - 1) << G_TEXTURE_IMAGE_FRAC, (((u64)_Arg4) - 1) << G_TEXTURE_IMAGE_FRAC); return; } if (_Symbol == "gsDPLoadTLUTCmd") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); SetCurrentTextureAsPalette(aGfxData); gDPLoadTLUTCmd(aHead++, _Arg0, _Arg1); return; } if (_Symbol == "gsDPLoadTLUT_pal16") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); UpdateTextureInfo(aGfxData, &_Arg1, G_IM_FMT_RGBA, G_IM_SIZ_16b, -1, -1); SetCurrentTextureAsPalette(aGfxData); aGfxData->mPointerList.Add(aHead); gDPSetTextureImage(aHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, _Arg1); gDPTileSync(aHead++); gDPSetTile(aHead++, 0, 0, 0, (256+(((_Arg0)&0xf)*16)), G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); gDPLoadSync(aHead++); gDPLoadTLUTCmd(aHead++, G_TX_LOADTILE, 15); gDPPipeSync(aHead++); return; } if (_Symbol == "gsDPLoadTLUT_pal256") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); UpdateTextureInfo(aGfxData, &_Arg0, G_IM_FMT_RGBA, G_IM_SIZ_16b, -1, -1); SetCurrentTextureAsPalette(aGfxData); aGfxData->mPointerList.Add(aHead); gDPSetTextureImage(aHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, _Arg0); gDPTileSync(aHead++); gDPSetTile(aHead++, 0, 0, 0, 256, G_TX_LOADTILE, 0 , 0, 0, 0, 0, 0, 0); gDPLoadSync(aHead++); gDPLoadTLUTCmd(aHead++, G_TX_LOADTILE, 255); gDPPipeSync(aHead++); return; } if (_Symbol == "gsDPLoadTextureBlock_4b") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_TEX); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg2 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg3 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg4 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg5 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg6 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg7 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg8 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg9 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _ArgA = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); UpdateTextureInfo(aGfxData, &_Arg0, (s32) _Arg1, G_IM_SIZ_4b, (s32) _Arg2, (s32) _Arg3); aGfxData->mPointerList.Add(aHead); gDPSetTextureImage(aHead++, _Arg1, G_IM_SIZ_4b, 1, _Arg0); gDPSetTile(aHead++, _Arg1, G_IM_SIZ_4b, 0, 0, G_TX_LOADTILE, 0, _Arg6, _Arg8, _ArgA, _Arg5, _Arg7, _Arg9); gDPLoadSync(aHead++); gDPLoadBlock(aHead++, G_TX_LOADTILE, 0, 0, (((_Arg2) * (_Arg3) + 3) >> 2) - 1, CALC_DXT_4b(_Arg2)); gDPPipeSync(aHead++); gDPSetTile(aHead++, _Arg1, G_IM_SIZ_4b, ((((_Arg2) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, _Arg4, _Arg6, _Arg8, _ArgA, _Arg5, _Arg7, _Arg9); gDPSetTileSize(aHead++, G_TX_RENDERTILE, 0, 0, (((u64)_Arg2) - 1) << G_TEXTURE_IMAGE_FRAC, (((u64)_Arg3) - 1) << G_TEXTURE_IMAGE_FRAC); return; } if (_Symbol == "gsSPLightColor") { s64 _Arg0 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); s64 _Arg1 = ParseGfxSymbolArg(aGfxData, aNode, &aTokenIndex, "", GFX_PARAM_TYPE_INT); // due to the function taking in the variable name instead of the actual value // as a parameter, we need to do this. LIGHT_1 to LIGHT_8 go from actual 1-8 // where as G_MWO_a*/G_MWO_b* are hex numbers without a linear pattern of // progression, meaning there isn't a simple one line solution for this afaik switch(_Arg0) { case LIGHT_1: gSPLightColor(aHead++, LIGHT_1, _Arg1); break; case LIGHT_2: gSPLightColor(aHead++, LIGHT_2, _Arg1); break; case LIGHT_3: gSPLightColor(aHead++, LIGHT_3, _Arg1); break; case LIGHT_4: gSPLightColor(aHead++, LIGHT_4, _Arg1); break; case LIGHT_5: gSPLightColor(aHead++, LIGHT_5, _Arg1); break; case LIGHT_6: gSPLightColor(aHead++, LIGHT_6, _Arg1); break; case LIGHT_7: gSPLightColor(aHead++, LIGHT_7, _Arg1); break; case LIGHT_8: gSPLightColor(aHead++, LIGHT_8, _Arg1); break; } return; } // Unknown PrintDataErrorGfx(" ERROR: Unknown gfx symbol: %s", _Symbol.begin()); } DataNode* DynOS_Gfx_Parse(GfxData* aGfxData, DataNode* aNode) { if (aNode->mData) return aNode; // Display list data u32 _Length = aNode->mTokens.Count() * DISPLAY_LIST_SIZE_PER_TOKEN; aNode->mData = gfx_allocate_internal(NULL, _Length); Gfx* _Head = aNode->mData; for (u64 _TokenIndex = 0; _TokenIndex < aNode->mTokens.Count();) { // Don't increment _TokenIndex here! ParseGfxSymbol(aGfxData, aNode, _Head, _TokenIndex); } aNode->mSize = (u32) (_Head - aNode->mData); aNode->mLoadIndex = aGfxData->mLoadIndex++; memmove(aNode->mData + aNode->mSize, aNode->mData + _Length, sizeof(Gfx)); // Move the sentinel to the true end of the display list return aNode; } #pragma GCC diagnostic pop ///////////// // Writing // ///////////// void DynOS_Gfx_Write(BinFile *aFile, GfxData *aGfxData, DataNode *aNode) { if (!aNode->mData) return; // Header aFile->Write(DATA_TYPE_DISPLAY_LIST); aNode->mName.Write(aFile); // Data aFile->Write(aNode->mSize); for (u32 i = 0; i != aNode->mSize; ++i) { Gfx *_Head = &aNode->mData[i]; if (aGfxData->mPointerList.Find((void *) _Head) != -1) { aFile->Write(_Head->words.w0); DynOS_Pointer_Write(aFile, (const void *) _Head->words.w1, aGfxData, 0); } else { aFile->Write(_Head->words.w0); aFile->Write(_Head->words.w1); } } } ///////////// // Reading // ///////////// void DynOS_Gfx_Load(BinFile *aFile, GfxData *aGfxData) { DataNode *_Node = New>(); // Name _Node->mName.Read(aFile); // Data _Node->mSize = aFile->Read(); _Node->mData = gfx_allocate_internal(NULL, _Node->mSize); for (u32 i = 0; i != _Node->mSize; ++i) { u32 _WordsW0 = aFile->Read(); u32 _WordsW1 = aFile->Read(); void *_Ptr = DynOS_Pointer_Load(aFile, aGfxData, _WordsW1, 0, &_Node->mFlags); if (_Ptr) { _Node->mData[i].words.w0 = (uintptr_t) _WordsW0; _Node->mData[i].words.w1 = (uintptr_t) _Ptr; } else { _Node->mData[i].words.w0 = (uintptr_t) _WordsW0; _Node->mData[i].words.w1 = (uintptr_t) _WordsW1; } } // Append aGfxData->mDisplayLists.Add(_Node); } ///////// // Lua // ///////// // // Parameter specifiers for gfx_set_command: // // %i -> integer // %s -> string // %v -> Vtx pointer // %t -> Texture pointer // %g -> Gfx pointer // static String CreateRawPointerDataNode(GfxData *aGfxData, void *pointer) { String ptrNodeName = String("PTR_%016llX", (u64) pointer); DataNode *ptrNode = New>(); ptrNode->mName = ptrNodeName; ptrNode->mData = pointer; aGfxData->mRawPointers.Add(ptrNode); return ptrNodeName; } template static String ConvertParam(lua_State *L, GfxData *aGfxData, u32 paramIndex, const char *typeName, const SmluaToFunc &smluaToFunc, const ReturnFunc &returnFunc) { T value = smluaToFunc(L, paramIndex); if (!gSmLuaConvertSuccess) { PrintDataErrorGfx(" ERROR: Failed to convert parameter %u to %s", paramIndex, typeName); return ""; } return returnFunc(value); } static String ResolveParam(lua_State *L, GfxData *aGfxData, u32 paramIndex, char paramType) { switch (paramType) { // Integer case GFX_PARAM_TYPE_INT: return ConvertParam( L, aGfxData, paramIndex, "integer", [] (lua_State *L, u32 paramIndex) { return smlua_to_integer(L, paramIndex); }, [] (s64 integer) { return String("%lld", integer); } ); // String case GFX_PARAM_TYPE_STR: return ConvertParam( L, aGfxData, paramIndex, "string", [] (lua_State *L, u32 paramIndex) { return smlua_to_string(L, paramIndex); }, [] (const char *string) { return String(string); } ); // Vtx pointer case GFX_PARAM_TYPE_VTX: return ConvertParam( L, aGfxData, paramIndex, "Vtx pointer", [] (lua_State *L, u32 paramIndex) { return (Vtx *) smlua_to_cobject(L, paramIndex, LOT_VTX); }, [&aGfxData] (Vtx *vtx) { return CreateRawPointerDataNode(aGfxData, vtx); } ); // Texture pointer case GFX_PARAM_TYPE_TEX: return ConvertParam( L, aGfxData, paramIndex, "Texture pointer", [] (lua_State *L, u32 paramIndex) { return (Texture *) smlua_to_cpointer(L, paramIndex, LVT_TEXTURE_P); }, [&aGfxData] (Texture *texture) { return CreateRawPointerDataNode(aGfxData, texture); } ); // Gfx pointer case GFX_PARAM_TYPE_GFX: return ConvertParam( L, aGfxData, paramIndex, "Gfx pointer", [] (lua_State *L, u32 paramIndex) { return (Gfx *) smlua_to_cobject(L, paramIndex, LOT_GFX); }, [&aGfxData] (Gfx *gfx) { return CreateRawPointerDataNode(aGfxData, gfx); } ); } PrintDataErrorGfx(" ERROR: Unknown parameter type: '%c'", paramType); return ""; } struct GfxParamInfo { u8 count; const GfxParamType *types; }; static const struct GfxParamInfo *GetGfxParamInfo(const char *command) { #define define_gfx_symbol(symb, params, addPtr, ...) \ static const GfxParamType types_##symb[] = { __VA_ARGS__ }; \ static struct GfxParamInfo info_##symb = { .count = params, .types = types_##symb }; \ static_assert(sizeof(types_##symb) == params, "Parameter count does not match for gfx command."); \ if (!strncmp(#symb, command, strlen(#symb))) { return &info_##symb; } #define define_gfx_symbol_manual(...) define_gfx_symbol(__VA_ARGS__) #include "gfx_symbols.h" #undef define_gfx_symbol #undef define_gfx_symbol_manual return NULL; } static std::string ResolveGfxCommand(lua_State *L, GfxData *aGfxData, const char *command) { const struct GfxParamInfo *paramInfo = GetGfxParamInfo(command); if (!paramInfo) { PrintDataErrorGfx(" ERROR: Unknown gfx command: %s", command); return ""; } // Count parameters // Find the position of each % to retrieve the correct expected type from the command paramInfo u8 paramPos[paramInfo->count]; memset(paramPos, 0, sizeof(u8) * paramInfo->count); u8 paramPosIndex = 0; u8 paramCount = 1; bool inBrackets = false; for (const char* str = command; *str; str++) { if (*str == '(') { inBrackets = true; } if (*str == ')') { inBrackets = false; } if (*str == ',' && inBrackets) { paramCount++; } if (*str == '%' && paramPosIndex < paramInfo->count) { paramPos[paramPosIndex++] = paramCount - 1; } } if (paramCount != paramInfo->count) { PrintDataErrorGfx(" ERROR: Incorrect parameter count. Got %d, expected %d.", paramCount, paramInfo->count); return ""; } std::string output; for (u32 paramIndex = 3; *command; command++) { char c = *command; if (c == '%') { u8 paramNum = paramPos[paramIndex - 3]; const GfxParamType paramType = *(++command); const GfxParamType expectedType = paramInfo->types[paramNum]; if (expectedType == GFX_PARAM_TYPE_PTR) { PrintDataErrorGfx(" ERROR: Gfx macro has unsupported type, this macro is not usable"); return ""; } if (expectedType != paramType && (expectedType != GFX_PARAM_TYPE_INT || !GFX_PARAM_TYPE_IS_INT_OR_CONSTANT(paramType)) // Allow strings as constants for integer parameters ) { PrintDataErrorGfx(" ERROR: Unexpected value type for parameter %d. Got '%c', expected '%c'", paramNum, paramType, expectedType); return ""; } String value = ResolveParam(L, aGfxData, paramIndex++, paramType); if (aGfxData->mErrorCount > 0) { return ""; } output.append(value.begin()); } else { output += c; } } return output; } static Array TokenizeGfxCommand(const std::string &command) { Array tokens; String token; for (u32 i = 0, scope = 0; i < command.length(); ++i) { char c = command[i]; // Remove whitespaces if (c <= ' ') { continue; } if (c == '(') { // End of the command name, beginning of the arguments if (scope == 0) { if (!token.Empty()) { tokens.Add(token); token.Clear(); } } // That's an argument else { token.Add(c); } scope++; } else if (c == ')') { scope--; // End of the command if (scope == 0) { break; } // That's an argument else { token.Add(c); } } // End of an argument else if (c == ',') { if (!token.Empty()) { tokens.Add(token); token.Clear(); } } else { token.Add(c); } } if (!token.Empty()) { tokens.Add(token); } return tokens; } static bool CheckGfxLength(GfxData *aGfxData, Gfx *gfx, u32 lengthToWrite) { if (lengthToWrite > 1) { u32 gfxLength = gfx_get_length(gfx); if (gfxLength < lengthToWrite) { PrintDataErrorGfx(" ERROR: Cannot write %u commands to display list of length: %u", lengthToWrite, gfxLength); return false; } } return true; } static bool ParseGfxCommand(lua_State *L, GfxData *aGfxData, Gfx *gfx, const char *command, bool hasSpecifiers) { // Resolve command std::string resolved = hasSpecifiers ? ResolveGfxCommand(L, aGfxData, command) : command; if (aGfxData->mErrorCount > 0) { return false; } // Check cache const auto &it = sGfxCommandCache.find(resolved); if (it != sGfxCommandCache.end()) { const Gfx *src = it->second.first; u32 length = it->second.second; if (!CheckGfxLength(aGfxData, gfx, length)) { return false; } memcpy(gfx, src, length * sizeof(Gfx)); return true; } // Tokenize command DataNode aNode; aNode.mTokens = TokenizeGfxCommand(resolved); if (aGfxData->mErrorCount > 0) { return false; } // Parse tokenized command u64 aTokenIndex = 0; Gfx gfxBuffer[16] = {0}; Gfx *gfxHead = gfxBuffer; ParseGfxSymbol(aGfxData, &aNode, gfxHead, aTokenIndex); if (aGfxData->mErrorCount > 0) { return false; } // Cache parsed command u32 commandLength = (u32) (gfxHead - gfxBuffer); size_t commandSize = commandLength * sizeof(Gfx); Gfx *cached = (Gfx *) malloc(commandSize); memcpy(cached, gfxBuffer, commandSize); sGfxCommandCache[resolved] = { cached, commandLength }; // Copy buffer to gfx if (!CheckGfxLength(aGfxData, gfx, commandLength)) { return false; } memcpy(gfx, gfxBuffer, commandLength * sizeof(Gfx)); return true; } extern "C" { bool dynos_smlua_parse_gfx_command(lua_State *L, Gfx *gfx, const char *command, bool hasSpecifiers, char *errorMsg, u32 errorSize) { // Parse command GfxData aGfxData; sGfxCommandErrorMsg = errorMsg; sGfxCommandErrorSize = errorSize; bool ok = ParseGfxCommand(L, &aGfxData, gfx, command, hasSpecifiers); // Clear stuff sGfxCommandErrorMsg = NULL; sGfxCommandErrorSize = 0; for (auto &ptrNode : aGfxData.mRawPointers) { Delete(ptrNode); } return ok; } void dynos_smlua_clear_gfx_command_cache() { for (auto &cached : sGfxCommandCache) { free(cached.second.first); } sGfxCommandCache.clear(); } }