diff --git a/data/dynos.cpp.h b/data/dynos.cpp.h index c545b09c..ae23408a 100644 --- a/data/dynos.cpp.h +++ b/data/dynos.cpp.h @@ -19,6 +19,8 @@ extern "C" { enum { DATA_TYPE_NONE = 0, DATA_TYPE_LIGHT, + DATA_TYPE_LIGHT_T, + DATA_TYPE_AMBIENT_T, DATA_TYPE_TEXTURE, DATA_TYPE_VERTEX, DATA_TYPE_DISPLAY_LIST, @@ -426,6 +428,8 @@ struct GfxData : NoCopy { // Model data DataNodes mLights; + DataNodes mLightTs; + DataNodes mAmbientTs; DataNodes mTextures; DataNodes mVertices; DataNodes mDisplayLists; @@ -691,6 +695,12 @@ void *DynOS_Geo_GetFunctionPointerFromIndex(s32 aIndex); s32 DynOS_Geo_GetFunctionIndex(const void *aPtr); void *DynOS_Geo_GetGraphNode(const void *aGeoLayout, bool aKeepInMemory); +s32 DynOS_Lvl_GetGeoCount(); +const char *DynOS_Lvl_GetGeoName(s32 aIndex); +const void *DynOS_Lvl_GetGeoLayout(s32 aIndex); +const void *DynOS_Lvl_GetGeoLayoutFromName(const char *aGeoName); +s32 DynOS_Lvl_GetGeoIndex(const void *aGeoLayout); + // // Levels // @@ -763,6 +773,14 @@ DataNode* DynOS_Lights_Parse(GfxData* aGfxData, DataNode* aNod void DynOS_Lights_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode); void DynOS_Lights_Load(FILE *aFile, GfxData *aGfxData); +DataNode* DynOS_LightT_Parse(GfxData* aGfxData, DataNode* aNode); +void DynOS_LightT_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode); +void DynOS_LightT_Load(FILE *aFile, GfxData *aGfxData); + +DataNode* DynOS_AmbientT_Parse(GfxData* aGfxData, DataNode* aNode); +void DynOS_AmbientT_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode); +void DynOS_AmbientT_Load(FILE *aFile, GfxData *aGfxData); + DataNode* DynOS_MacroObject_Parse(GfxData* aGfxData, DataNode* aNode, bool aDisplayPercent); void DynOS_MacroObject_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode); DataNode* DynOS_MacroObject_Load(FILE *aFile, GfxData *aGfxData); diff --git a/data/dynos_bin_actor.cpp b/data/dynos_bin_actor.cpp index 1d513f35..34c285e8 100644 --- a/data/dynos_bin_actor.cpp +++ b/data/dynos_bin_actor.cpp @@ -26,6 +26,16 @@ static bool DynOS_Actor_WriteBinary(const SysPath &aOutputFilename, GfxData *aGf DynOS_Lights_Write(_File, aGfxData, _Node); } } + for (auto &_Node : aGfxData->mLightTs) { + if (_Node->mLoadIndex == i) { + DynOS_LightT_Write(_File, aGfxData, _Node); + } + } + for (auto &_Node : aGfxData->mAmbientTs) { + if (_Node->mLoadIndex == i) { + DynOS_AmbientT_Write(_File, aGfxData, _Node); + } + } for (auto &_Node : aGfxData->mTextures) { if (_Node->mLoadIndex == i) { DynOS_Tex_Write(_File, aGfxData, _Node); @@ -88,6 +98,8 @@ GfxData *DynOS_Actor_LoadFromBinary(const SysPath &aPackFolder, const char *aAct for (bool _Done = false; !_Done;) { switch (ReadBytes(_File)) { case DATA_TYPE_LIGHT: DynOS_Lights_Load (_File, _GfxData); break; + case DATA_TYPE_LIGHT_T: DynOS_LightT_Load (_File, _GfxData); break; + case DATA_TYPE_AMBIENT_T: DynOS_AmbientT_Load (_File, _GfxData); break; case DATA_TYPE_TEXTURE: DynOS_Tex_Load (_File, _GfxData); break; case DATA_TYPE_VERTEX: DynOS_Vtx_Load (_File, _GfxData); break; case DATA_TYPE_DISPLAY_LIST: DynOS_Gfx_Load (_File, _GfxData); break; @@ -186,6 +198,8 @@ static void DynOS_Actor_Generate(const SysPath &aPackFolder, ArraymLights); + ClearGfxDataNodes(_GfxData->mLightTs); + ClearGfxDataNodes(_GfxData->mAmbientTs); ClearGfxDataNodes(_GfxData->mTextures); ClearGfxDataNodes(_GfxData->mVertices); ClearGfxDataNodes(_GfxData->mDisplayLists); diff --git a/data/dynos_bin_ambient_t.cpp b/data/dynos_bin_ambient_t.cpp new file mode 100644 index 00000000..327ba3cd --- /dev/null +++ b/data/dynos_bin_ambient_t.cpp @@ -0,0 +1,63 @@ +#include "dynos.cpp.h" + + ///////////// + // Parsing // +///////////// + +DataNode* DynOS_AmbientT_Parse(GfxData* aGfxData, DataNode* aNode) { + if (aNode->mData) return aNode; + + // Check tokens count + if (aNode->mTokens.Count() < 8) { + PrintError(" ERROR: %s: not enough data", aNode->mName.begin()); + return aNode; + } + + // Parse data tokens + u8 cr = (u8) aNode->mTokens[0].ParseInt(); + u8 cg = (u8) aNode->mTokens[1].ParseInt(); + u8 cb = (u8) aNode->mTokens[2].ParseInt(); + u8 c2r = (u8) aNode->mTokens[4].ParseInt(); + u8 c2g = (u8) aNode->mTokens[5].ParseInt(); + u8 c2b = (u8) aNode->mTokens[6].ParseInt(); + aNode->mData = New(); + *aNode->mData = { + { cr, cg, cb}, 0, + { c2r, c2g, c2b}, 0 + }; + aNode->mLoadIndex = aGfxData->mLoadIndex++; + return aNode; +} + + ///////////// + // Writing // +///////////// + +void DynOS_AmbientT_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode) { + if (!aNode->mData) return; + + // Header + WriteBytes(aFile, DATA_TYPE_AMBIENT_T); + aNode->mName.Write(aFile); + + // Data + WriteBytes(aFile, *aNode->mData); +} + + ///////////// + // Reading // +///////////// + +void DynOS_AmbientT_Load(FILE *aFile, GfxData *aGfxData) { + DataNode *_Node = New>(); + + // Name + _Node->mName.Read(aFile); + + // Data + _Node->mData = New(); + *_Node->mData = ReadBytes(aFile); + + // Append + aGfxData->mAmbientTs.Add(_Node); +} diff --git a/data/dynos_bin_col.cpp b/data/dynos_bin_col.cpp index c8ee4b16..4d0b032a 100644 --- a/data/dynos_bin_col.cpp +++ b/data/dynos_bin_col.cpp @@ -464,11 +464,13 @@ DataNode* DynOS_Col_LoadFromBinary(const SysPath &aPackFolder, const FILE *_File = fopen(_Filename.c_str(), "rb"); if (_File) { // backwards compatibility - long prevPos = ftell(_File); + //long prevPos = ftell(_File); u8 type = ReadBytes(_File); - if (type != DATA_TYPE_COLLISION) { fseek(_File, prevPos, SEEK_SET); } - - collisionNode = DynOS_Col_Load(_File, NULL); + //if (type != DATA_TYPE_COLLISION) { fseek(_File, prevPos, SEEK_SET); } + if (type == DATA_TYPE_COLLISION) { + collisionNode = DynOS_Col_Load(_File, NULL); + } + // DO NOT COMMIT - figure out wtf is going on fclose(_File); } diff --git a/data/dynos_bin_gfx.cpp b/data/dynos_bin_gfx.cpp index c2423f58..37da1b25 100644 --- a/data/dynos_bin_gfx.cpp +++ b/data/dynos_bin_gfx.cpp @@ -310,6 +310,14 @@ static s64 ParseGfxSymbolArg(GfxData* aGfxData, DataNode* aNode, u64* pToke gfx_constant(G_ZS_PIXEL); gfx_constant(G_ZS_PRIM); + // 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); + // Common values gfx_constant((4-1)<* aNode, u64* pToke 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(G_CULL_BACK|G_LIGHTING); // Lights for (auto& _Node : aGfxData->mLights) { - // Light pointer if (_Arg == _Node->mName) { return (s64) DynOS_Lights_Parse(aGfxData, _Node)->mData; @@ -368,6 +376,50 @@ static s64 ParseGfxSymbolArg(GfxData* aGfxData, DataNode* aNode, u64* pToke } } + 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]); + } + } + // Textures for (auto& _Node : aGfxData->mTextures) { if (_Arg == _Node->mName) { @@ -662,6 +714,7 @@ static void ParseGfxSymbol(GfxData* aGfxData, DataNode* aNode, Gfx*& aHead, gfx_symbol_2(gsSPCopyLightEXT, false); gfx_symbol_2(gsSPFogFactor, false); gfx_symbol_1(gsDPSetTextureLOD, false); + gfx_symbol_3(gsMoveWd, false); // Special symbols if (_Symbol == "gsSPTexture") { diff --git a/data/dynos_bin_light_t.cpp b/data/dynos_bin_light_t.cpp new file mode 100644 index 00000000..a266ccf0 --- /dev/null +++ b/data/dynos_bin_light_t.cpp @@ -0,0 +1,67 @@ +#include "dynos.cpp.h" + + ///////////// + // Parsing // +///////////// + +DataNode* DynOS_LightT_Parse(GfxData* aGfxData, DataNode* aNode) { + if (aNode->mData) return aNode; + + // Check tokens count + if (aNode->mTokens.Count() < 12) { + PrintError(" ERROR: %s: not enough data", aNode->mName.begin()); + return aNode; + } + + // Parse data tokens + u8 cr = (u8) aNode->mTokens[0].ParseInt(); + u8 cg = (u8) aNode->mTokens[1].ParseInt(); + u8 cb = (u8) aNode->mTokens[2].ParseInt(); + u8 c2r = (u8) aNode->mTokens[4].ParseInt(); + u8 c2g = (u8) aNode->mTokens[5].ParseInt(); + u8 c2b = (u8) aNode->mTokens[6].ParseInt(); + s8 dx = (s8) aNode->mTokens[8].ParseInt(); + s8 dy = (s8) aNode->mTokens[9].ParseInt(); + s8 dz = (s8) aNode->mTokens[10].ParseInt(); + aNode->mData = New(); + *aNode->mData = { + { cr, cg, cb}, 0, + { c2r, c2g, c2b}, 0, + { dx, dy, dz}, 0 + }; + aNode->mLoadIndex = aGfxData->mLoadIndex++; + return aNode; +} + + ///////////// + // Writing // +///////////// + +void DynOS_LightT_Write(FILE* aFile, GfxData* aGfxData, DataNode *aNode) { + if (!aNode->mData) return; + + // Header + WriteBytes(aFile, DATA_TYPE_LIGHT_T); + aNode->mName.Write(aFile); + + // Data + WriteBytes(aFile, *aNode->mData); +} + + ///////////// + // Reading // +///////////// + +void DynOS_LightT_Load(FILE *aFile, GfxData *aGfxData) { + DataNode *_Node = New>(); + + // Name + _Node->mName.Read(aFile); + + // Data + _Node->mData = New(); + *_Node->mData = ReadBytes(aFile); + + // Append + aGfxData->mLightTs.Add(_Node); +} diff --git a/data/dynos_bin_lvl.cpp b/data/dynos_bin_lvl.cpp index f84161cb..d69ceb57 100644 --- a/data/dynos_bin_lvl.cpp +++ b/data/dynos_bin_lvl.cpp @@ -1391,7 +1391,7 @@ s64 DynOS_Lvl_ParseLevelScriptConstants(const String& _Arg, bool* found) { lvl_constant(WARP_TRANSITION_FADE_FROM_BOWSER); lvl_constant(WARP_TRANSITION_FADE_INTO_BOWSER); - // vanilla geos + // vanilla actors s32 actorCount = DynOS_Geo_GetActorCount(); for (s32 i = 0; i < actorCount; i++) { if (DynOS_Geo_IsCustomActor(i)) { break; } @@ -1400,6 +1400,14 @@ s64 DynOS_Lvl_ParseLevelScriptConstants(const String& _Arg, bool* found) { } } + // vanilla level geos + s32 lvlGeoCount = DynOS_Lvl_GetGeoCount(); + for (s32 i = 0; i < lvlGeoCount; i++) { + if (!strcmp(_Arg.begin(), DynOS_Lvl_GetGeoName(i))) { + return (LevelScript)DynOS_Lvl_GetGeoLayout(i); + } + } + *found = false; return 0; } @@ -1847,6 +1855,16 @@ static bool DynOS_Lvl_WriteBinary(const SysPath &aOutputFilename, GfxData *aGfxD DynOS_Lights_Write(_File, aGfxData, _Node); } } + for (auto &_Node : aGfxData->mLightTs) { + if (_Node->mLoadIndex == i) { + DynOS_LightT_Write(_File, aGfxData, _Node); + } + } + for (auto &_Node : aGfxData->mAmbientTs) { + if (_Node->mLoadIndex == i) { + DynOS_AmbientT_Write(_File, aGfxData, _Node); + } + } for (auto &_Node : aGfxData->mTextures) { if (_Node->mLoadIndex == i) { DynOS_Tex_Write(_File, aGfxData, _Node); @@ -1951,6 +1969,8 @@ GfxData *DynOS_Lvl_LoadFromBinary(const SysPath &aPackFolder, const char *aLevel for (bool _Done = false; !_Done;) { switch (ReadBytes(_File)) { case DATA_TYPE_LIGHT: DynOS_Lights_Load (_File, _GfxData); break; + case DATA_TYPE_LIGHT_T: DynOS_LightT_Load (_File, _GfxData); break; + case DATA_TYPE_AMBIENT_T: DynOS_AmbientT_Load (_File, _GfxData); break; case DATA_TYPE_TEXTURE: DynOS_Tex_Load (_File, _GfxData); break; case DATA_TYPE_VERTEX: DynOS_Vtx_Load (_File, _GfxData); break; case DATA_TYPE_DISPLAY_LIST: DynOS_Gfx_Load (_File, _GfxData); break; diff --git a/data/dynos_bin_pointer.cpp b/data/dynos_bin_pointer.cpp index 3df0521c..e3c789e3 100644 --- a/data/dynos_bin_pointer.cpp +++ b/data/dynos_bin_pointer.cpp @@ -43,6 +43,27 @@ static PointerData GetDataFromPointer(const void* aPtr, GfxData* aGfxData) { } } + for (auto& _Node : aGfxData->mLightTs) { + if (&_Node->mData->col[0] == aPtr) { + return { _Node->mName, 1 }; + } + if (&_Node->mData->colc[0] == aPtr) { + return { _Node->mName, 2 }; + } + if (&_Node->mData->dir[0] == aPtr) { + return { _Node->mName, 3 }; + } + } + + for (auto& _Node : aGfxData->mAmbientTs) { + if (&_Node->mData->col[0] == aPtr) { + return { _Node->mName, 1 }; + } + if (&_Node->mData->colc[0] == aPtr) { + return { _Node->mName, 2 }; + } + } + // Textures for (auto& _Node : aGfxData->mTextures) { if (_Node == aPtr) { @@ -119,7 +140,7 @@ static PointerData GetDataFromPointer(const void* aPtr, GfxData* aGfxData) { return { get_behavior_name_from_id(id), 0 }; } - // Vanilla Geos + // Vanilla Actors s32 actorCount = DynOS_Geo_GetActorCount(); for (s32 i = 0; i < actorCount; i++) { if (DynOS_Geo_IsCustomActor(i)) { break; } @@ -128,6 +149,14 @@ static PointerData GetDataFromPointer(const void* aPtr, GfxData* aGfxData) { } } + // Vanilla Lvl Geos + s32 lvlGeoCount = DynOS_Lvl_GetGeoCount(); + for (s32 i = 0; i < lvlGeoCount; i++) { + if (aPtr == DynOS_Lvl_GetGeoLayout(i)) { + return { DynOS_Lvl_GetGeoName(i), 0 }; + } + } + // Vanilla Pointers s32 pointerCount = (s32) (sizeof(sDynosPointers) / (2 * sizeof(sDynosPointers[0]))); for (s32 i = 0; i < pointerCount; i++) { @@ -217,6 +246,33 @@ static void *GetPointerFromData(GfxData *aGfxData, const String &aPtrName, u32 a } } + for (auto& _Node : aGfxData->mLightTs) { + if (_Node->mName == aPtrName) { + if (aPtrData == 1) { + return (void *) &_Node->mData->col[0]; + } + if (aPtrData == 2) { + return (void *) &_Node->mData->colc[0]; + } + if (aPtrData == 3) { + return (void *) &_Node->mData->dir[0]; + } + sys_fatal("Unknown Light type: %u", aPtrData); + } + } + + for (auto& _Node : aGfxData->mAmbientTs) { + if (_Node->mName == aPtrName) { + if (aPtrData == 1) { + return (void *) &_Node->mData->col[0]; + } + if (aPtrData == 2) { + return (void *) &_Node->mData->colc[0]; + } + sys_fatal("Unknown Light type: %u", aPtrData); + } + } + // Textures for (auto& _Node : aGfxData->mTextures) { if (_Node->mName == aPtrName) { @@ -300,7 +356,7 @@ static void *GetPointerFromData(GfxData *aGfxData, const String &aPtrName, u32 a return (void*)get_behavior_from_id(id); } - // Vanilla Geos + // Vanilla Actors s32 actorCount = DynOS_Geo_GetActorCount(); for (s32 i = 0; i < actorCount; i++) { if (DynOS_Geo_IsCustomActor(i)) { break; } @@ -309,6 +365,14 @@ static void *GetPointerFromData(GfxData *aGfxData, const String &aPtrName, u32 a } } + // Vanilla Lvl Geos + s32 lvlGeoCount = DynOS_Lvl_GetGeoCount(); + for (s32 i = 0; i < lvlGeoCount; i++) { + if (!strcmp(aPtrName.begin(), DynOS_Lvl_GetGeoName(i))) { + return (void*)DynOS_Lvl_GetGeoLayout(i); + } + } + // Vanilla Pointers s32 pointerCount = (s32) (sizeof(sDynosPointers) / (2 * sizeof(sDynosPointers[0]))); for (s32 i = 0; i < pointerCount; i++) { diff --git a/data/dynos_bin_read.cpp b/data/dynos_bin_read.cpp index 5a6bbbab..4fe9f3ae 100644 --- a/data/dynos_bin_read.cpp +++ b/data/dynos_bin_read.cpp @@ -178,6 +178,10 @@ void DynOS_Read_Source(GfxData *aGfxData, const SysPath &aFilename) { _DataType = DATA_TYPE_UNUSED; } else if (_Buffer == "Lights1") { _DataType = DATA_TYPE_LIGHT; + } else if (_Buffer == "Light_t") { + _DataType = DATA_TYPE_LIGHT_T; + } else if (_Buffer == "Ambient_t") { + _DataType = DATA_TYPE_AMBIENT_T; } else if (_Buffer == "u8") { _DataType = strstr(aFilename.c_str(), "room") ? DATA_TYPE_ROOMS @@ -221,6 +225,8 @@ void DynOS_Read_Source(GfxData *aGfxData, const SysPath &aFilename) { else if (_Buffer.Length() != 0) { switch (_DataType) { case DATA_TYPE_LIGHT: AppendNewNode(aGfxData, aGfxData->mLights, _Buffer, pDataName, pDataTokens); break; + case DATA_TYPE_LIGHT_T: AppendNewNode(aGfxData, aGfxData->mLightTs, _Buffer, pDataName, pDataTokens); break; + case DATA_TYPE_AMBIENT_T: AppendNewNode(aGfxData, aGfxData->mAmbientTs, _Buffer, pDataName, pDataTokens); break; case DATA_TYPE_TEXTURE: AppendNewNode(aGfxData, aGfxData->mTextures, _Buffer, pDataName, pDataTokens); break; case DATA_TYPE_VERTEX: AppendNewNode(aGfxData, aGfxData->mVertices, _Buffer, pDataName, pDataTokens); break; case DATA_TYPE_DISPLAY_LIST: AppendNewNode(aGfxData, aGfxData->mDisplayLists, _Buffer, pDataName, pDataTokens); break; diff --git a/data/dynos_bin_utils.cpp b/data/dynos_bin_utils.cpp index 5017a251..cd77f00e 100644 --- a/data/dynos_bin_utils.cpp +++ b/data/dynos_bin_utils.cpp @@ -10,6 +10,14 @@ void DynOS_Gfx_Free(GfxData* aGfxData) { Delete(_Node->mData); Delete(_Node); } + for (auto& _Node : aGfxData->mLightTs) { + Delete(_Node->mData); + Delete(_Node); + } + for (auto& _Node : aGfxData->mAmbientTs) { + Delete(_Node->mData); + Delete(_Node); + } for (auto& _Node : aGfxData->mTextures) { Delete(_Node->mData); Delete(_Node); diff --git a/data/dynos_misc.cpp b/data/dynos_misc.cpp index d0728470..fea1c39f 100644 --- a/data/dynos_misc.cpp +++ b/data/dynos_misc.cpp @@ -36,6 +36,7 @@ extern "C" { #include "actors/group17.h" #include "actors/custom0.h" #include "actors/zcustom0.h" +#include "levels/bob/header.h" #include "levels/wf/header.h" } @@ -606,6 +607,75 @@ Collision* DynOS_Col_Get(const char* collisionName) { // Levels // //////////// +#define define_lvl_geo(geo) (const void *) #geo, (const void *) geo +static const void *sDynosLevelGeos[] = { + define_lvl_geo(wf_geo_0007E0), + define_lvl_geo(wf_geo_000820), + define_lvl_geo(wf_geo_000860), + define_lvl_geo(wf_geo_000878), + define_lvl_geo(wf_geo_000890), + define_lvl_geo(wf_geo_0008A8), + define_lvl_geo(wf_geo_0008E8), + define_lvl_geo(wf_geo_000900), + define_lvl_geo(wf_geo_000940), + define_lvl_geo(wf_geo_000958), + define_lvl_geo(wf_geo_0009A0), + define_lvl_geo(wf_geo_0009B8), + define_lvl_geo(wf_geo_0009D0), + define_lvl_geo(wf_geo_0009E8), + define_lvl_geo(wf_geo_000A00), + define_lvl_geo(wf_geo_000A40), + define_lvl_geo(wf_geo_000A58), + define_lvl_geo(wf_geo_000A98), + define_lvl_geo(wf_geo_000AB0), + define_lvl_geo(wf_geo_000AC8), + define_lvl_geo(wf_geo_000AE0), + define_lvl_geo(wf_geo_000AF8), + define_lvl_geo(wf_geo_000B10), + define_lvl_geo(wf_geo_000B38), + define_lvl_geo(wf_geo_000B60), + define_lvl_geo(wf_geo_000B78), + define_lvl_geo(wf_geo_000B90), + define_lvl_geo(wf_geo_000BA8), + define_lvl_geo(wf_geo_000BC8), + define_lvl_geo(wf_geo_000BE0), + define_lvl_geo(wf_geo_000BF8), + define_lvl_geo(bob_geo_000440), + define_lvl_geo(bob_geo_000458), + define_lvl_geo(bob_geo_000470), + define_lvl_geo(bob_geo_000488), +}; + +s32 DynOS_Lvl_GetGeoCount() { + return (s32) (sizeof(sDynosLevelGeos) / (2 * sizeof(sDynosLevelGeos[0]))); +} + +const char *DynOS_Lvl_GetGeoName(s32 aIndex) { + return (const char *) sDynosLevelGeos[2 * aIndex]; +} + +const void *DynOS_Lvl_GetGeoLayout(s32 aIndex) { + return (const void *) sDynosLevelGeos[2 * aIndex + 1]; +} + +const void *DynOS_Lvl_GetGeoLayoutFromName(const char *aGeoName) { + for (s32 i = 0; i < DynOS_Lvl_GetGeoCount(); ++i) { + if (!strcmp(DynOS_Lvl_GetGeoName(i), aGeoName)) { + return DynOS_Lvl_GetGeoLayout(i); + } + } + return NULL; +} + +s32 DynOS_Lvl_GetGeoIndex(const void *aGeoLayout) { + for (s32 i = 0; i < DynOS_Lvl_GetGeoCount(); ++i) { + if (DynOS_Lvl_GetGeoLayout(i) == aGeoLayout) { + return i; + } + } + return -1; +} + static Array> sDynosCustomLevelScripts; void DynOS_Lvl_Add(s32 modIndex, const SysPath &aPackFolder, const char *aLevelName) {