sm64coopdx/data/dynos_bin_lvl.cpp
djoslin0 81af37eef6
Some checks are pending
Build coop / build-linux (push) Waiting to run
Build coop / build-steamos (push) Waiting to run
Build coop / build-windows-opengl (push) Waiting to run
Build coop / build-windows-directx (push) Waiting to run
Build coop / build-macos-arm (push) Waiting to run
Build coop / build-macos-intel (push) Waiting to run
Regenerate DynOS assets when source files are modified (#873)
Previously to recompile DynOS assets you had to remove the
bin/lvl/bhv/tex/col/etc files. Now the game will compare the
last-modified-timestamps of the generated/compiled assets vs
the other files that are within those directories. If the presumed-
source files have a later modified timestamp DynOS will regenerate
those assets.

While this results in scanning the attributes of files more, it
also prevents parsing files unnecessarily. Previously actors
would always parse their source files and build up GfxData
unnecessarily.

---------

Co-authored-by: MysterD <myster@d>
2025-07-02 23:24:35 +10:00

1291 lines
50 KiB
C++

#include "dynos.cpp.h"
extern "C" {
#include "include/level_commands.h"
#include "include/model_ids.h"
#include "include/behavior_data.h"
#include "include/surface_terrains.h"
#include "include/seq_ids.h"
#include "level_commands.h"
#include "game/level_update.h"
#include "include/dialog_ids.h"
#include "levels/scripts.h"
#include "levels/menu/header.h"
#include "game/area.h"
}
// Free data pointers, but keep nodes and tokens intact
// Delete nodes generated from GfxDynCmds
template <typename T>
static void ClearLvlDataNodes(DataNodes<T> &aDataNodes) {
for (s32 i = aDataNodes.Count(); i != 0; --i) {
Delete(aDataNodes[i - 1]->mData);
}
}
/////////////
// Parsing //
/////////////
#define LEVEL_SCRIPT_SIZE_PER_TOKEN 4
#define lvl_constant(x) if (_Arg == #x) { return (LevelScript) (x); }
#define lvl_legacy_constant(x, y) if (_Arg == #x) { return (LevelScript) (y); }
s64 DynOS_Lvl_ParseLevelScriptConstants(const String& _Arg, bool* found) {
*found = true;
// Behavior constants
s64 cBhvConstant = DynOS_Common_ParseBhvConstants(_Arg, found);
if (*found) { return cBhvConstant; }
*found = true; // reset found value
// Level constants
lvl_constant(LEVEL_UNKNOWN_1);
lvl_constant(LEVEL_UNKNOWN_2);
lvl_constant(LEVEL_UNKNOWN_3);
lvl_constant(LEVEL_BBH);
lvl_constant(LEVEL_CCM);
lvl_constant(LEVEL_CASTLE);
lvl_constant(LEVEL_HMC);
lvl_constant(LEVEL_SSL);
lvl_constant(LEVEL_BOB);
lvl_constant(LEVEL_SL);
lvl_constant(LEVEL_WDW);
lvl_constant(LEVEL_JRB);
lvl_constant(LEVEL_THI);
lvl_constant(LEVEL_TTC);
lvl_constant(LEVEL_RR);
lvl_constant(LEVEL_CASTLE_GROUNDS);
lvl_constant(LEVEL_BITDW);
lvl_constant(LEVEL_VCUTM);
lvl_constant(LEVEL_BITFS);
lvl_constant(LEVEL_SA);
lvl_constant(LEVEL_BITS);
lvl_constant(LEVEL_LLL);
lvl_constant(LEVEL_DDD);
lvl_constant(LEVEL_WF);
lvl_constant(LEVEL_ENDING);
lvl_constant(LEVEL_CASTLE_COURTYARD);
lvl_constant(LEVEL_PSS);
lvl_constant(LEVEL_COTMC);
lvl_constant(LEVEL_TOTWC);
lvl_constant(LEVEL_BOWSER_1);
lvl_constant(LEVEL_WMOTR);
lvl_constant(LEVEL_UNKNOWN_32);
lvl_constant(LEVEL_BOWSER_2);
lvl_constant(LEVEL_BOWSER_3);
lvl_constant(LEVEL_UNKNOWN_35);
lvl_constant(LEVEL_TTM);
lvl_constant(LEVEL_UNKNOWN_37);
lvl_constant(LEVEL_UNKNOWN_38);
// Surface constants
lvl_constant(TERRAIN_GRASS);
lvl_constant(TERRAIN_STONE);
lvl_constant(TERRAIN_SNOW);
lvl_constant(TERRAIN_SAND);
lvl_constant(TERRAIN_SPOOKY);
lvl_constant(TERRAIN_WATER);
lvl_constant(TERRAIN_SLIDE);
lvl_constant(TERRAIN_MASK);
// Seq ids constants
lvl_constant(SEQ_BASE_ID);
lvl_constant(SEQ_VARIATION);
lvl_constant(SEQ_SOUND_PLAYER);
lvl_constant(SEQ_EVENT_CUTSCENE_COLLECT_STAR);
lvl_constant(SEQ_MENU_TITLE_SCREEN);
lvl_constant(SEQ_LEVEL_GRASS);
lvl_constant(SEQ_LEVEL_INSIDE_CASTLE);
lvl_constant(SEQ_LEVEL_WATER);
lvl_constant(SEQ_LEVEL_HOT);
lvl_constant(SEQ_LEVEL_BOSS_KOOPA);
lvl_constant(SEQ_LEVEL_SNOW);
lvl_constant(SEQ_LEVEL_SLIDE);
lvl_constant(SEQ_LEVEL_SPOOKY);
lvl_constant(SEQ_EVENT_PIRANHA_PLANT);
lvl_constant(SEQ_LEVEL_UNDERGROUND);
lvl_constant(SEQ_MENU_STAR_SELECT);
lvl_constant(SEQ_EVENT_POWERUP);
lvl_constant(SEQ_EVENT_METAL_CAP);
lvl_constant(SEQ_EVENT_KOOPA_MESSAGE);
lvl_constant(SEQ_LEVEL_KOOPA_ROAD);
lvl_constant(SEQ_EVENT_HIGH_SCORE);
lvl_constant(SEQ_EVENT_MERRY_GO_ROUND);
lvl_constant(SEQ_EVENT_RACE);
lvl_constant(SEQ_EVENT_CUTSCENE_STAR_SPAWN);
lvl_constant(SEQ_EVENT_BOSS);
lvl_constant(SEQ_EVENT_CUTSCENE_COLLECT_KEY);
lvl_constant(SEQ_EVENT_ENDLESS_STAIRS);
lvl_constant(SEQ_LEVEL_BOSS_KOOPA_FINAL);
lvl_constant(SEQ_EVENT_CUTSCENE_CREDITS);
lvl_constant(SEQ_EVENT_SOLVE_PUZZLE);
lvl_constant(SEQ_EVENT_TOAD_MESSAGE);
lvl_constant(SEQ_EVENT_PEACH_MESSAGE);
lvl_constant(SEQ_EVENT_CUTSCENE_INTRO);
lvl_constant(SEQ_EVENT_CUTSCENE_VICTORY);
lvl_constant(SEQ_EVENT_CUTSCENE_ENDING);
lvl_constant(SEQ_MENU_FILE_SELECT);
lvl_constant(SEQ_EVENT_CUTSCENE_LAKITU);
lvl_constant(SEQ_COUNT);
// Model constants
s64 cModelConstant = DynOS_Common_ParseModelConstants(_Arg, found);
if (*found) { return cModelConstant; }
*found = true; // reset found value
// dialog constants
lvl_constant(DIALOG_000);
lvl_constant(DIALOG_001);
lvl_constant(DIALOG_002);
lvl_constant(DIALOG_003);
lvl_constant(DIALOG_004);
lvl_constant(DIALOG_005);
lvl_constant(DIALOG_006);
lvl_constant(DIALOG_007);
lvl_constant(DIALOG_008);
lvl_constant(DIALOG_009);
lvl_constant(DIALOG_010);
lvl_constant(DIALOG_011);
lvl_constant(DIALOG_012);
lvl_constant(DIALOG_013);
lvl_constant(DIALOG_014);
lvl_constant(DIALOG_015);
lvl_constant(DIALOG_016);
lvl_constant(DIALOG_017);
lvl_constant(DIALOG_018);
lvl_constant(DIALOG_019);
lvl_constant(DIALOG_020);
lvl_constant(DIALOG_021);
lvl_constant(DIALOG_022);
lvl_constant(DIALOG_023);
lvl_constant(DIALOG_024);
lvl_constant(DIALOG_025);
lvl_constant(DIALOG_026);
lvl_constant(DIALOG_027);
lvl_constant(DIALOG_028);
lvl_constant(DIALOG_029);
lvl_constant(DIALOG_030);
lvl_constant(DIALOG_031);
lvl_constant(DIALOG_032);
lvl_constant(DIALOG_033);
lvl_constant(DIALOG_034);
lvl_constant(DIALOG_035);
lvl_constant(DIALOG_036);
lvl_constant(DIALOG_037);
lvl_constant(DIALOG_038);
lvl_constant(DIALOG_039);
lvl_constant(DIALOG_040);
lvl_constant(DIALOG_041);
lvl_constant(DIALOG_042);
lvl_constant(DIALOG_043);
lvl_constant(DIALOG_044);
lvl_constant(DIALOG_045);
lvl_constant(DIALOG_046);
lvl_constant(DIALOG_047);
lvl_constant(DIALOG_048);
lvl_constant(DIALOG_049);
lvl_constant(DIALOG_050);
lvl_constant(DIALOG_051);
lvl_constant(DIALOG_052);
lvl_constant(DIALOG_053);
lvl_constant(DIALOG_054);
lvl_constant(DIALOG_055);
lvl_constant(DIALOG_056);
lvl_constant(DIALOG_057);
lvl_constant(DIALOG_058);
lvl_constant(DIALOG_059);
lvl_constant(DIALOG_060);
lvl_constant(DIALOG_061);
lvl_constant(DIALOG_062);
lvl_constant(DIALOG_063);
lvl_constant(DIALOG_064);
lvl_constant(DIALOG_065);
lvl_constant(DIALOG_066);
lvl_constant(DIALOG_067);
lvl_constant(DIALOG_068);
lvl_constant(DIALOG_069);
lvl_constant(DIALOG_070);
lvl_constant(DIALOG_071);
lvl_constant(DIALOG_072);
lvl_constant(DIALOG_073);
lvl_constant(DIALOG_074);
lvl_constant(DIALOG_075);
lvl_constant(DIALOG_076);
lvl_constant(DIALOG_077);
lvl_constant(DIALOG_078);
lvl_constant(DIALOG_079);
lvl_constant(DIALOG_080);
lvl_constant(DIALOG_081);
lvl_constant(DIALOG_082);
lvl_constant(DIALOG_083);
lvl_constant(DIALOG_084);
lvl_constant(DIALOG_085);
lvl_constant(DIALOG_086);
lvl_constant(DIALOG_087);
lvl_constant(DIALOG_088);
lvl_constant(DIALOG_089);
lvl_constant(DIALOG_090);
lvl_constant(DIALOG_091);
lvl_constant(DIALOG_092);
lvl_constant(DIALOG_093);
lvl_constant(DIALOG_094);
lvl_constant(DIALOG_095);
lvl_constant(DIALOG_096);
lvl_constant(DIALOG_097);
lvl_constant(DIALOG_098);
lvl_constant(DIALOG_099);
lvl_constant(DIALOG_100);
lvl_constant(DIALOG_101);
lvl_constant(DIALOG_102);
lvl_constant(DIALOG_103);
lvl_constant(DIALOG_104);
lvl_constant(DIALOG_105);
lvl_constant(DIALOG_106);
lvl_constant(DIALOG_107);
lvl_constant(DIALOG_108);
lvl_constant(DIALOG_109);
lvl_constant(DIALOG_110);
lvl_constant(DIALOG_111);
lvl_constant(DIALOG_112);
lvl_constant(DIALOG_113);
lvl_constant(DIALOG_114);
lvl_constant(DIALOG_115);
lvl_constant(DIALOG_116);
lvl_constant(DIALOG_117);
lvl_constant(DIALOG_118);
lvl_constant(DIALOG_119);
lvl_constant(DIALOG_120);
lvl_constant(DIALOG_121);
lvl_constant(DIALOG_122);
lvl_constant(DIALOG_123);
lvl_constant(DIALOG_124);
lvl_constant(DIALOG_125);
lvl_constant(DIALOG_126);
lvl_constant(DIALOG_127);
lvl_constant(DIALOG_128);
lvl_constant(DIALOG_129);
lvl_constant(DIALOG_130);
lvl_constant(DIALOG_131);
lvl_constant(DIALOG_132);
lvl_constant(DIALOG_133);
lvl_constant(DIALOG_134);
lvl_constant(DIALOG_135);
lvl_constant(DIALOG_136);
lvl_constant(DIALOG_137);
lvl_constant(DIALOG_138);
lvl_constant(DIALOG_139);
lvl_constant(DIALOG_140);
lvl_constant(DIALOG_141);
lvl_constant(DIALOG_142);
lvl_constant(DIALOG_143);
lvl_constant(DIALOG_144);
lvl_constant(DIALOG_145);
lvl_constant(DIALOG_146);
lvl_constant(DIALOG_147);
lvl_constant(DIALOG_148);
lvl_constant(DIALOG_149);
lvl_constant(DIALOG_150);
lvl_constant(DIALOG_151);
lvl_constant(DIALOG_152);
lvl_constant(DIALOG_153);
lvl_constant(DIALOG_154);
lvl_constant(DIALOG_155);
lvl_constant(DIALOG_156);
lvl_constant(DIALOG_157);
lvl_constant(DIALOG_158);
lvl_constant(DIALOG_159);
lvl_constant(DIALOG_160);
lvl_constant(DIALOG_161);
lvl_constant(DIALOG_162);
lvl_constant(DIALOG_163);
lvl_constant(DIALOG_164);
lvl_constant(DIALOG_165);
lvl_constant(DIALOG_166);
lvl_constant(DIALOG_167);
lvl_constant(DIALOG_168);
lvl_constant(DIALOG_169);
lvl_constant(DIALOG_COUNT);
// global scripts
lvl_constant(level_main_scripts_entry);
lvl_constant(level_main_menu_entry_1);
lvl_constant(script_func_global_1);
lvl_constant(script_func_global_2);
lvl_constant(script_func_global_3);
lvl_constant(script_func_global_4);
lvl_constant(script_func_global_5);
lvl_constant(script_func_global_6);
lvl_constant(script_func_global_7);
lvl_constant(script_func_global_8);
lvl_constant(script_func_global_9);
lvl_constant(script_func_global_10);
lvl_constant(script_func_global_11);
lvl_constant(script_func_global_12);
lvl_constant(script_func_global_13);
lvl_constant(script_func_global_14);
lvl_constant(script_func_global_15);
lvl_constant(script_func_global_16);
lvl_constant(script_func_global_17);
lvl_constant(script_func_global_18);
// level command constants
lvl_constant(OP_AND);
lvl_constant(OP_NAND);
lvl_constant(OP_EQ);
lvl_constant(OP_NEQ);
lvl_constant(OP_LT);
lvl_constant(OP_LEQ);
lvl_constant(OP_GT);
lvl_constant(OP_GEQ);
lvl_constant(OP_SET);
lvl_constant(OP_GET);
lvl_constant(VAR_CURR_SAVE_FILE_NUM);
lvl_constant(VAR_CURR_COURSE_NUM);
lvl_constant(VAR_CURR_ACT_NUM);
lvl_constant(VAR_CURR_LEVEL_NUM);
lvl_constant(VAR_CURR_AREA_INDEX);
lvl_constant(WARP_CHECKPOINT);
lvl_constant(WARP_NO_CHECKPOINT);
lvl_constant(WHIRLPOOL_COND_ALWAYS);
lvl_constant(WHIRLPOOL_COND_BOWSER2_BEATEN);
lvl_constant(WHIRLPOOL_COND_AT_LEAST_SECOND_STAR);
lvl_constant(REGULAR_FACE);
lvl_constant(DIZZY_FACE);
// warp transitions
lvl_constant(WARP_TRANSITION_FADE_FROM_COLOR);
lvl_constant(WARP_TRANSITION_FADE_INTO_COLOR);
lvl_constant(WARP_TRANSITION_FADE_FROM_STAR);
lvl_constant(WARP_TRANSITION_FADE_INTO_STAR);
lvl_constant(WARP_TRANSITION_FADE_FROM_CIRCLE);
lvl_constant(WARP_TRANSITION_FADE_INTO_CIRCLE);
lvl_constant(WARP_TRANSITION_FADE_FROM_MARIO);
lvl_constant(WARP_TRANSITION_FADE_INTO_MARIO);
lvl_constant(WARP_TRANSITION_FADE_FROM_BOWSER);
lvl_constant(WARP_TRANSITION_FADE_INTO_BOWSER);
// Other constants
lvl_constant(NULL);
lvl_constant(TRUE);
lvl_constant(FALSE);
*found = false;
return 0;
}
template <typename T>
DataNode<T>* FindDataNode(DataNodes<T>& aDataNodes, String& aName, u32 aModelIdentifier) {
DataNode<T>* best = NULL;
for (auto& node : aDataNodes) {
if (aName == node->mName) {
if (aModelIdentifier == node->mModelIdentifier) {
return node;
}
best = node;
}
}
return best;
}
static LevelScript ParseLevelScriptSymbolArgInternal(GfxData* aGfxData, DataNode<LevelScript>* aNode, u64& aTokenIndex, bool* found) {
String _Arg = aNode->mTokens[aTokenIndex++];
u64 _ModelIdentifier = aNode->mModelIdentifier;
*found = true;
// Integers
bool integerFound = false;
s64 integerValue = DynOS_Misc_ParseInteger(_Arg, &integerFound);
if (integerFound) {
return integerValue;
}
// Offset
s32 _Offset = 0;
s32 _Plus = _Arg.Find('+');
if (_Plus != -1) {
_Offset = _Arg.SubString(_Plus + 1).ParseInt();
_Arg = _Arg.SubString(0, _Plus);
}
// Built-in functions
const void *_FunctionPtr = DynOS_Builtin_Func_GetFromName(_Arg.begin());
if (_FunctionPtr != NULL) {
return (s64) _FunctionPtr;
}
bool constantFound = false;
s64 constantValue = DynOS_Lvl_ParseLevelScriptConstants(_Arg, &constantFound);
if (constantFound) {
return (LevelScript) constantValue;
}
// Level Scripts
{
auto _Node = FindDataNode<LevelScript>(aGfxData->mLevelScripts, _Arg, aGfxData->mModelIdentifier);
if (_Node != NULL) {
auto base = DynOS_Lvl_Parse(aGfxData, _Node, false)->mData;
auto data = (u8*)base + _Offset;
if (_Offset != 0) {
aGfxData->mPointerOffsetList.Add({ data, base });
}
return (LevelScript) data;
}
}
// Geo layouts
{
auto _Node = FindDataNode<GeoLayout>(aGfxData->mGeoLayouts, _Arg, aGfxData->mModelIdentifier);
if (_Node != NULL) {
return (LevelScript) DynOS_Geo_Parse(aGfxData, _Node, false)->mData;
}
}
// Collisions
{
auto _Node = FindDataNode<Collision>(aGfxData->mCollisions, _Arg, aGfxData->mModelIdentifier);
if (_Node != NULL) {
return (LevelScript) DynOS_Col_Parse(aGfxData, _Node, false)->mData;
}
}
// MacroObjects
{
auto _Node = FindDataNode<MacroObject>(aGfxData->mMacroObjects, _Arg, aGfxData->mModelIdentifier);
if (_Node != NULL) {
return (LevelScript) DynOS_MacroObject_Parse(aGfxData, _Node, false)->mData;
}
}
// Trajectories
{
auto _Node = FindDataNode<Trajectory>(aGfxData->mTrajectories, _Arg, aGfxData->mModelIdentifier);
if (_Node != NULL) {
return (LevelScript) DynOS_Trajectory_Parse(aGfxData, _Node, false)->mData;
}
}
// Movtexs
{
auto _Node = FindDataNode<Movtex>(aGfxData->mMovtexs, _Arg, aGfxData->mModelIdentifier);
if (_Node != NULL) {
return (LevelScript) DynOS_Movtex_Parse(aGfxData, _Node, false)->mData;
}
}
// MovtexQCs
{
auto _Node = FindDataNode<MovtexQC>(aGfxData->mMovtexQCs, _Arg, aGfxData->mModelIdentifier);
if (_Node != NULL) {
return (LevelScript) DynOS_MovtexQC_Parse(aGfxData, _Node)->mData;
}
}
// Rooms
{
auto _Node = FindDataNode<u8>(aGfxData->mRooms, _Arg, aGfxData->mModelIdentifier);
if (_Node != NULL) {
return (LevelScript) DynOS_Rooms_Parse(aGfxData, _Node)->mData;
}
}
// Built-in actors
auto builtinActor = DynOS_Builtin_Actor_GetFromName(_Arg.begin());
if (builtinActor != NULL) {
return (LevelScript)builtinActor;
}
// Built-in Lvl Macros
auto builtinLvlMacro = DynOS_Builtin_LvlMacro_GetFromName(_Arg.begin());
if (builtinLvlMacro != NULL) {
return (LevelScript)builtinLvlMacro;
}
// Built-in Lvl Geos
auto builtinGeo = DynOS_Builtin_LvlGeo_GetFromName(_Arg.begin());
if (builtinGeo != NULL) {
return (LevelScript)builtinGeo;
}
// Built-in Cols
auto builtinCol = DynOS_Builtin_Col_GetFromName(_Arg.begin());
if (builtinCol != NULL) {
return (LevelScript)builtinCol;
}
// Recursive descent parsing
bool rdSuccess = false;
s64 rdValue = DynOS_RecursiveDescent_Parse(_Arg.begin(), &rdSuccess, DynOS_Lvl_ParseLevelScriptConstants);
if (rdSuccess) {
return (LevelScript)rdValue;
}
*found = false;
return 0;
}
static LevelScript ParseLevelScriptSymbolArg(GfxData* aGfxData, DataNode<LevelScript>* aNode, u64& aTokenIndex) {
bool found = true;
LevelScript value = ParseLevelScriptSymbolArgInternal(aGfxData, aNode, aTokenIndex, &found);
if (!found) {
const String& _Arg = aNode->mTokens[aTokenIndex - 1];
PrintDataError(" ERROR: Unknown lvl arg: %s", _Arg.begin());
}
return value;
}
#define lvl_symbol_0(symb) \
if (_Symbol == #symb) { \
LevelScript _Ls[] = { symb() }; \
memcpy(aHead, _Ls, sizeof(_Ls)); \
aHead += (sizeof(_Ls) / sizeof(_Ls[0])); \
return; \
}
#define lvl_symbol_1(symb, n) \
if (_Symbol == #symb) { \
LevelScript _Arg0 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
if (n != 0) { aGfxData->mPointerList.Add(aHead + n); } \
LevelScript _Ls[] = { symb(_Arg0) }; \
memcpy(aHead, _Ls, sizeof(_Ls)); \
aHead += (sizeof(_Ls) / sizeof(_Ls[0])); \
return; \
}
#define lvl_symbol_2(symb, n1, n2) \
if (_Symbol == #symb) { \
LevelScript _Arg0 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg1 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
if (n1 != 0) { aGfxData->mPointerList.Add(aHead + n1); } \
if (n2 != 0) { aGfxData->mPointerList.Add(aHead + n2); } \
LevelScript _Ls[] = { symb(_Arg0, _Arg1) }; \
memcpy(aHead, _Ls, sizeof(_Ls)); \
aHead += (sizeof(_Ls) / sizeof(_Ls[0])); \
return; \
}
#define lvl_symbol_3(symb, n1, n2, n3) \
if (_Symbol == #symb) { \
LevelScript _Arg0 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg1 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg2 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
if (n1 != 0) { aGfxData->mPointerList.Add(aHead + n1); } \
if (n2 != 0) { aGfxData->mPointerList.Add(aHead + n2); } \
if (n3 != 0) { aGfxData->mPointerList.Add(aHead + n3); } \
LevelScript _Ls[] = { symb(_Arg0, _Arg1, _Arg2) }; \
memcpy(aHead, _Ls, sizeof(_Ls)); \
aHead += (sizeof(_Ls) / sizeof(_Ls[0])); \
return; \
}
#define lvl_symbol_4(symb, n1, n2, n3) \
if (_Symbol == #symb) { \
LevelScript _Arg0 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg1 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg2 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg3 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
if (n1 != 0) { aGfxData->mPointerList.Add(aHead + n1); } \
if (n2 != 0) { aGfxData->mPointerList.Add(aHead + n2); } \
if (n3 != 0) { aGfxData->mPointerList.Add(aHead + n3); } \
LevelScript _Ls[] = { symb(_Arg0, _Arg1, _Arg2, _Arg3) }; \
memcpy(aHead, _Ls, sizeof(_Ls)); \
aHead += (sizeof(_Ls) / sizeof(_Ls[0])); \
return; \
}
#define lvl_symbol_5(symb, n1, n2, n3) \
if (_Symbol == #symb) { \
LevelScript _Arg0 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg1 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg2 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg3 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg4 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
if (n1 != 0) { aGfxData->mPointerList.Add(aHead + n1); } \
if (n2 != 0) { aGfxData->mPointerList.Add(aHead + n2); } \
if (n3 != 0) { aGfxData->mPointerList.Add(aHead + n3); } \
LevelScript _Ls[] = { symb(_Arg0, _Arg1, _Arg2, _Arg3, _Arg4) }; \
memcpy(aHead, _Ls, sizeof(_Ls)); \
aHead += (sizeof(_Ls) / sizeof(_Ls[0])); \
return; \
}
#define lvl_symbol_6(symb, n1, n2, n3) \
if (_Symbol == #symb) { \
LevelScript _Arg0 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg1 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg2 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg3 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg4 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
LevelScript _Arg5 = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex); \
if (n1 != 0) { aGfxData->mPointerList.Add(aHead + n1); } \
if (n2 != 0) { aGfxData->mPointerList.Add(aHead + n2); } \
if (n3 != 0) { aGfxData->mPointerList.Add(aHead + n3); } \
LevelScript _Ls[] = { symb(_Arg0, _Arg1, _Arg2, _Arg3, _Arg4, _Arg5) }; \
memcpy(aHead, _Ls, sizeof(_Ls)); \
aHead += (sizeof(_Ls) / sizeof(_Ls[0])); \
return; \
}
#define lvl_symbol_noop_3(symb) \
if (_Symbol == #symb) { \
aTokenIndex += 3; \
LevelScript _Ls[] = { symb(0, 0, 0) }; \
memcpy(aHead, _Ls, sizeof(_Ls)); \
aHead += (sizeof(_Ls) / sizeof(_Ls[0])); \
return; \
}
static void ParseLevelScriptSymbol(GfxData* aGfxData, DataNode<LevelScript>* aNode, LevelScript*& aHead, u64& aTokenIndex, Array<u64>& aSwitchNodes) {
const String& _Symbol = aNode->mTokens[aTokenIndex++];
// execution
lvl_symbol_4(EXECUTE, 1, 2, 3);
lvl_symbol_4(EXIT_AND_EXECUTE, 1, 2, 3);
lvl_symbol_0(EXIT);
// sleep
lvl_symbol_1(SLEEP, 0);
lvl_symbol_1(SLEEP_BEFORE_EXIT, 0);
// jumps
lvl_symbol_1(JUMP, 1);
lvl_symbol_1(JUMP_LINK, 1);
lvl_symbol_0(RETURN);
lvl_symbol_1(JUMP_LINK_PUSH_ARG, 0);
lvl_symbol_0(JUMP_N_TIMES);
lvl_symbol_0(LOOP_BEGIN);
lvl_symbol_2(LOOP_UNTIL, 0, 0);
lvl_symbol_3(JUMP_IF, 2, 0, 0);
lvl_symbol_2(SKIP_IF, 0, 0);
lvl_symbol_0(SKIP);
lvl_symbol_0(SKIP_NOP);
lvl_symbol_3(JUMP_AREA_EXT, 2, 0, 0);
// calls
lvl_symbol_2(CALL, 1, 0);
lvl_symbol_2(CALL_LOOP, 1, 0);
// misc memory
lvl_symbol_1(SET_REG, 0);
lvl_symbol_0(PUSH_POOL);
lvl_symbol_0(POP_POOL);
lvl_symbol_3(FIXED_LOAD, 1, 2, 3);
lvl_symbol_noop_3(LOAD_RAW);
lvl_symbol_noop_3(LOAD_MIO0);
lvl_symbol_noop_3(LOAD_YAY0);
lvl_symbol_1(LOAD_MARIO_HEAD, 0);
lvl_symbol_noop_3(LOAD_MIO0_TEXTURE);
lvl_symbol_noop_3(LOAD_YAY0_TEXTURE);
// levels
lvl_symbol_0(INIT_LEVEL);
lvl_symbol_0(CLEAR_LEVEL);
lvl_symbol_0(ALLOC_LEVEL_POOL);
lvl_symbol_0(FREE_LEVEL_POOL);
// areas
lvl_symbol_2(AREA, 1, 0);
lvl_symbol_0(END_AREA);
// models
lvl_symbol_3(LOAD_MODEL_FROM_DL, 1, 0, 0);
lvl_symbol_3(CMD23, 1, 0, 0);
// objects
lvl_symbol_3(MARIO, 2, 0, 0);
// warps
lvl_symbol_5(INSTANT_WARP, 0, 0, 0);
// misc
lvl_symbol_1(LOAD_AREA, 0);
lvl_symbol_1(CMD2A, 0);
lvl_symbol_5(MARIO_POS, 0, 0, 0);
lvl_symbol_0(CMD2C);
lvl_symbol_0(CMD2D);
lvl_symbol_1(TERRAIN, 1);
lvl_symbol_1(ROOMS, 1);
lvl_symbol_2(SHOW_DIALOG, 0, 0);
lvl_symbol_1(TERRAIN_TYPE, 0);
lvl_symbol_0(NOP);
// transitions
lvl_symbol_5(TRANSITION, 0, 0, 0);
lvl_symbol_1(BLACKOUT, 0);
lvl_symbol_1(GAMMA, 0);
// music
lvl_symbol_2(SET_BACKGROUND_MUSIC, 0, 0);
lvl_symbol_1(SET_MENU_MUSIC, 0);
lvl_symbol_1(STOP_MUSIC, 0);
// misc
lvl_symbol_1(MACRO_OBJECTS, 1);
lvl_symbol_5(CMD3A, 0, 0, 0);
lvl_symbol_6(WHIRLPOOL, 0, 0, 0);
lvl_symbol_2(GET_OR_SET, 0, 0);
lvl_symbol_0(ADV_DEMO);
lvl_symbol_0(CLEAR_DEMO_PTR);
// object
if (_Symbol == "OBJECT") {
u64 topTokenIndex = aTokenIndex;
bool foundModel = true;
bool foundBeh = true;
LevelScript model = ParseLevelScriptSymbolArgInternal(aGfxData, aNode, aTokenIndex, &foundModel);
LevelScript posX = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript posY = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript posZ = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript angleX = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript angleY = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript angleZ = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript behParam = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript beh = ParseLevelScriptSymbolArgInternal(aGfxData, aNode, aTokenIndex, &foundBeh);
if (foundModel && foundBeh) {
aGfxData->mPointerList.Add(aHead + 5);
LevelScript _Ls[] = { OBJECT(model, posX, posY, posZ, angleX, angleY, angleZ, behParam, beh) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
} else if (foundModel) {
u32 behIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 5, aNode->mTokens[topTokenIndex + 8]);
LevelScript _Ls[] = { OBJECT_EXT(model, posX, posY, posZ, angleX, angleY, angleZ, behParam, behIndex) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
} else {
u32 modelIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 5, aNode->mTokens[topTokenIndex + 0]);
u32 behIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 6, aNode->mTokens[topTokenIndex + 8]);
LevelScript _Ls[] = { OBJECT_EXT2(modelIndex, posX, posY, posZ, angleX, angleY, angleZ, behParam, behIndex) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
}
return;
}
// object with acts
if (_Symbol == "OBJECT_WITH_ACTS") {
u64 topTokenIndex = aTokenIndex;
bool foundModel = true;
bool foundBeh = true;
LevelScript model = ParseLevelScriptSymbolArgInternal(aGfxData, aNode, aTokenIndex, &foundModel);
LevelScript posX = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript posY = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript posZ = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript angleX = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript angleY = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript angleZ = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript behParam = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript beh = ParseLevelScriptSymbolArgInternal(aGfxData, aNode, aTokenIndex, &foundBeh);
LevelScript acts = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
if (foundModel && foundBeh) {
aGfxData->mPointerList.Add(aHead + 5);
LevelScript _Ls[] = { OBJECT_WITH_ACTS(model, posX, posY, posZ, angleX, angleY, angleZ, behParam, beh, acts) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
} else if (foundModel) {
u32 behIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 5, aNode->mTokens[topTokenIndex + 8]);
LevelScript _Ls[] = { OBJECT_WITH_ACTS_EXT(model, posX, posY, posZ, angleX, angleY, angleZ, behParam, behIndex, acts) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
} else {
u32 modelIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 5, aNode->mTokens[topTokenIndex + 0]);
u32 behIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 6, aNode->mTokens[topTokenIndex + 8]);
LevelScript _Ls[] = { OBJECT_WITH_ACTS_EXT2(modelIndex, posX, posY, posZ, angleX, angleY, angleZ, behParam, behIndex, acts) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
}
return;
}
// LOAD_MODEL_FROM_GEO
if (_Symbol == "LOAD_MODEL_FROM_GEO") {
u64 topTokenIndex = aTokenIndex;
bool foundGeo = false;
LevelScript model = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript geo = ParseLevelScriptSymbolArgInternal(aGfxData, aNode, aTokenIndex, &foundGeo);
if (foundGeo) {
aGfxData->mPointerList.Add(aHead + 1);
LevelScript _Ls[] = { LOAD_MODEL_FROM_GEO(model, geo) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
} else {
u32 geoIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 1, aNode->mTokens[topTokenIndex + 1]);
LevelScript _Ls[] = { LOAD_MODEL_FROM_GEO_EXT(model, geoIndex) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
}
return;
}
// JUMP_AREA
if (_Symbol == "JUMP_AREA") {
LevelScript op = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript arg = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript target = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
aGfxData->mPointerList.Add(aHead + 2);
LevelScript _Ls[] = { JUMP_AREA_EXT(op, arg, target) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
return;
}
// WARP_NODE
if (_Symbol == "WARP_NODE") {
u64 topTokenIndex = aTokenIndex;
bool foundLevel = true;
LevelScript id = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript destLevel = ParseLevelScriptSymbolArgInternal(aGfxData, aNode, aTokenIndex, &foundLevel);
LevelScript destArea = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript destNode = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript flags = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
if (foundLevel) {
LevelScript _Ls[] = { WARP_NODE(id, destLevel, destArea, destNode, flags) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
} else {
s16 destLevelIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 1, aNode->mTokens[topTokenIndex + 1]);
LevelScript _Ls[] = { WARP_NODE(id, destLevelIndex, destArea, destNode, flags) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
}
return;
}
// PAINTING_WARP_NODE
if (_Symbol == "PAINTING_WARP_NODE") {
u64 topTokenIndex = aTokenIndex;
bool foundLevel = true;
LevelScript id = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript destLevel = ParseLevelScriptSymbolArgInternal(aGfxData, aNode, aTokenIndex, &foundLevel);
LevelScript destArea = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript destNode = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
LevelScript flags = ParseLevelScriptSymbolArg(aGfxData, aNode, aTokenIndex);
if (foundLevel) {
LevelScript _Ls[] = { PAINTING_WARP_NODE(id, destLevel, destArea, destNode, flags) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
} else {
s16 destLevelIndex = DynOS_Lua_RememberVariable(aGfxData, aHead + 1, aNode->mTokens[topTokenIndex + 1]);
LevelScript _Ls[] = { PAINTING_WARP_NODE(id, destLevelIndex, destArea, destNode, flags) };
memcpy(aHead, _Ls, sizeof(_Ls));
aHead += (sizeof(_Ls) / sizeof(_Ls[0]));
}
return;
}
// Unknown
PrintDataError(" ERROR: Unknown lvl symbol: %s", _Symbol.begin());
}
DataNode<LevelScript>* DynOS_Lvl_Parse(GfxData* aGfxData, DataNode<LevelScript>* aNode, bool aDisplayPercent) {
if (aNode->mData) return aNode;
// Level script data
aNode->mData = New<LevelScript>(aNode->mTokens.Count() * LEVEL_SCRIPT_SIZE_PER_TOKEN);
LevelScript* _Head = aNode->mData;
Array<u64> _SwitchNodes;
for (u64 _TokenIndex = 0; _TokenIndex < aNode->mTokens.Count();) { // Don't increment _TokenIndex here!
ParseLevelScriptSymbol(aGfxData, aNode, _Head, _TokenIndex, _SwitchNodes);
if (aDisplayPercent && aGfxData->mErrorCount == 0) { PrintNoNewLine("%3d%%\b\b\b\b", (s32) (_TokenIndex * 100) / aNode->mTokens.Count()); }
}
if (aDisplayPercent && aGfxData->mErrorCount == 0) { Print("100%%"); }
aNode->mSize = (u32)(_Head - aNode->mData);
aNode->mLoadIndex = aGfxData->mLoadIndex++;
return aNode;
}
static DataNode<LevelScript> *GetLevelScript(GfxData *aGfxData, const String& aGeoRoot) {
for (DataNode<LevelScript> *_Node : aGfxData->mLevelScripts) {
if (_Node->mName == aGeoRoot) {
return _Node;
}
}
return NULL;
}
/////////////
// Writing //
/////////////
static void DynOS_Lvl_Write(BinFile* aFile, GfxData* aGfxData, DataNode<LevelScript> *aNode) {
if (!aNode->mData) return;
// Name
aFile->Write<u8>(DATA_TYPE_LEVEL_SCRIPT);
aNode->mName.Write(aFile);
// Data
aFile->Write<u32>(aNode->mSize);
for (u32 i = 0; i != aNode->mSize; ++i) {
LevelScript *_Head = &aNode->mData[i];
if (aGfxData->mPointerList.Find((void *) _Head) != -1) {
DynOS_Pointer_Write(aFile, (const void *) (*_Head), aGfxData);
} else if (aGfxData->mLuaPointerList.Find((void *) _Head) != -1) {
DynOS_Pointer_Lua_Write(aFile, *(u32 *)_Head, aGfxData);
} else {
aFile->Write<u32>(*((u32 *) _Head));
}
}
}
static bool DynOS_Lvl_WriteBinary(const SysPath &aOutputFilename, GfxData *aGfxData) {
BinFile *_File = BinFile::OpenW(aOutputFilename.c_str());
if (!_File) {
PrintDataError(" ERROR: Unable to create file \"%s\"", aOutputFilename.c_str());
return false;
}
for (u64 i = 0; i != aGfxData->mLoadIndex; ++i) {
for (auto &_Node : aGfxData->mLights) {
if (_Node->mLoadIndex == i) {
DynOS_Lights_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mLight0s) {
if (_Node->mLoadIndex == i) {
DynOS_Light0_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);
}
}
for (auto &_Node : aGfxData->mTextureLists) {
if (_Node->mLoadIndex == i) {
DynOS_TexList_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mVertices) {
if (_Node->mLoadIndex == i) {
DynOS_Vtx_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mDisplayLists) {
if (_Node->mLoadIndex == i) {
DynOS_Gfx_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mGeoLayouts) {
if (_Node->mLoadIndex == i) {
DynOS_Geo_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mCollisions) {
if (_Node->mLoadIndex == i) {
DynOS_Col_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mLevelScripts) {
if (_Node->mLoadIndex == i) {
DynOS_Lvl_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mMacroObjects) {
if (_Node->mLoadIndex == i) {
DynOS_MacroObject_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mTrajectories) {
if (_Node->mLoadIndex == i) {
DynOS_Trajectory_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mMovtexs) {
if (_Node->mLoadIndex == i) {
DynOS_Movtex_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mMovtexQCs) {
if (_Node->mLoadIndex == i) {
DynOS_MovtexQC_Write(_File, aGfxData, _Node);
}
}
for (auto &_Node : aGfxData->mRooms) {
if (_Node->mLoadIndex == i) {
DynOS_Rooms_Write(_File, aGfxData, _Node);
}
}
}
BinFile::Close(_File);
return DynOS_Bin_Compress(aOutputFilename);
}
/////////////
// Reading //
/////////////
static DataNode<LevelScript>* DynOS_Lvl_Load(BinFile *aFile, GfxData *aGfxData) {
DataNode<LevelScript> *_Node = New<DataNode<LevelScript>>();
// Name
_Node->mName.Read(aFile);
// Data
_Node->mSize = aFile->Read<u32>();
_Node->mData = New<LevelScript>(_Node->mSize);
// Add it
if (aGfxData != NULL) {
aGfxData->mLevelScripts.Add(_Node);
}
DynOS_Lvl_Validate_Begin();
// Read it
for (u32 i = 0; i != _Node->mSize; ++i) {
u32 _Value = aFile->Read<u32>();
bool requirePointer = DynOS_Lvl_Validate_RequirePointer(_Value);
void *_Ptr = DynOS_Pointer_Load(aFile, aGfxData, _Value, &_Node->mFlags);
if (_Ptr) {
if (!requirePointer) {
PrintError("Didn't expect a pointer while reading level script: %s, %u", _Node->mName.begin(), _Value);
}
_Node->mData[i] = (uintptr_t) _Ptr;
} else {
if (requirePointer) {
PrintError("Expected a pointer while reading level script: %s, %u", _Node->mName.begin(), _Value);
_Node->mData[i] = 0;
} else {
_Node->mData[i] = (uintptr_t) _Value;
}
}
}
return _Node;
}
GfxData *DynOS_Lvl_LoadFromBinary(const SysPath &aFilename, const char *aLevelName) {
struct DynosGfxDataCache { SysPath mPackFolder; Array<Pair<const char *, GfxData *>> mGfxData; };
static Array<DynosGfxDataCache *> sDynosGfxDataCache;
// Load data from binary file
GfxData *_GfxData = NULL;
BinFile *_File = DynOS_Bin_Decompress(aFilename);
if (_File) {
_GfxData = New<GfxData>();
for (bool _Done = false; !_Done;) {
switch (_File->Read<u8>()) {
case DATA_TYPE_LIGHT: DynOS_Lights_Load (_File, _GfxData); break;
case DATA_TYPE_LIGHT_0: DynOS_Light0_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_TEXTURE_LIST: DynOS_TexList_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;
case DATA_TYPE_GEO_LAYOUT: DynOS_Geo_Load (_File, _GfxData); break;
case DATA_TYPE_ANIMATION: DynOS_Anim_Load (_File, _GfxData); break;
case DATA_TYPE_ANIMATION_TABLE: DynOS_Anim_Table_Load (_File, _GfxData); break;
case DATA_TYPE_GFXDYNCMD: DynOS_GfxDynCmd_Load (_File, _GfxData); break;
case DATA_TYPE_COLLISION: DynOS_Col_Load (_File, _GfxData); break;
case DATA_TYPE_LEVEL_SCRIPT: DynOS_Lvl_Load (_File, _GfxData); break;
case DATA_TYPE_MACRO_OBJECT: DynOS_MacroObject_Load(_File, _GfxData); break;
case DATA_TYPE_TRAJECTORY: DynOS_Trajectory_Load (_File, _GfxData); break;
case DATA_TYPE_MOVTEX: DynOS_Movtex_Load (_File, _GfxData); break;
case DATA_TYPE_MOVTEXQC: DynOS_MovtexQC_Load (_File, _GfxData); break;
case DATA_TYPE_ROOMS: DynOS_Rooms_Load (_File, _GfxData); break;
default: _Done = true; break;
}
}
BinFile::Close(_File);
}
return _GfxData;
}
//////////////
// Generate //
//////////////
static bool DynOS_Lvl_GeneratePack_Internal(const SysPath &aPackFolder, Array<Pair<u64, String>> _ActorsFolders, GfxData *_GfxData) {
bool generated = false;
for (auto &_LvlNode : _GfxData->mLevelScripts) {
String _LvlRootName = _LvlNode->mName;
DataNode<LevelScript> *_LvlRoot = GetLevelScript(_GfxData, _LvlRootName);
if (_LvlRoot == NULL) { continue; }
if (_LvlRootName.Find("_entry") == -1) { continue; }
// If there is an existing binary file for this level, skip and go to the next level
SysPath _LvlFilename = fstring("%s/%s.lvl", aPackFolder.c_str(), _LvlRootName.begin());
// Init
_GfxData->mLoadIndex = 0;
_GfxData->mErrorCount = 0;
_GfxData->mModelIdentifier = _LvlRoot->mModelIdentifier;
_GfxData->mPackFolder = aPackFolder;
_GfxData->mPointerList = { NULL }; // The NULL pointer is needed, so we add it here
_GfxData->mPointerOffsetList = { };
_GfxData->mLuaPointerList = { };
_GfxData->mLuaTokenList = { };
_GfxData->mGfxContext.mCurrentTexture = NULL;
_GfxData->mGfxContext.mCurrentPalette = NULL;
_GfxData->mGeoNodeStack.Clear();
// Parse data
PrintNoNewLine("%s.lvl: Level identifier: %X - Processing... ", _LvlRootName.begin(), _GfxData->mModelIdentifier);
PrintConsole(CONSOLE_MESSAGE_INFO, "%s.lvl: Level identifier: %X - Processing... ", _LvlRootName.begin(), _GfxData->mModelIdentifier);
DynOS_Lvl_Parse(_GfxData, _LvlRoot, true);
// Force all of the movtexs, collisions, and trajectories into the compiled lvl
for (auto &_Node : _GfxData->mMovtexs) {
if (_Node->mModelIdentifier != _GfxData->mModelIdentifier) { continue; }
DynOS_Movtex_Parse(_GfxData, _Node, false);
}
for (auto &_Node : _GfxData->mMovtexQCs) {
if (_Node->mModelIdentifier != _GfxData->mModelIdentifier) { continue; }
DynOS_MovtexQC_Parse(_GfxData, _Node);
}
for (auto &_Node : _GfxData->mCollisions) {
if (_Node->mModelIdentifier != _GfxData->mModelIdentifier) { continue; }
DynOS_Col_Parse(_GfxData, _Node, false);
}
for (auto &_Node : _GfxData->mTrajectories) {
if (_Node->mModelIdentifier != _GfxData->mModelIdentifier) { continue; }
DynOS_Trajectory_Parse(_GfxData, _Node, false);
}
// Write if no error
if (_GfxData->mErrorCount == 0) {
DynOS_Lvl_WriteBinary(_LvlFilename, _GfxData);
} else {
PrintError(" %u error(s): Unable to parse data", _GfxData->mErrorCount);
}
// Clear data pointers
ClearLvlDataNodes(_GfxData->mLights);
ClearLvlDataNodes(_GfxData->mLight0s);
ClearLvlDataNodes(_GfxData->mLightTs);
ClearLvlDataNodes(_GfxData->mAmbientTs);
ClearLvlDataNodes(_GfxData->mTextures);
ClearLvlDataNodes(_GfxData->mTextureLists);
ClearLvlDataNodes(_GfxData->mVertices);
ClearLvlDataNodes(_GfxData->mDisplayLists);
ClearLvlDataNodes(_GfxData->mGeoLayouts);
ClearLvlDataNodes(_GfxData->mCollisions);
ClearLvlDataNodes(_GfxData->mLevelScripts);
ClearLvlDataNodes(_GfxData->mMacroObjects);
ClearLvlDataNodes(_GfxData->mTrajectories);
ClearLvlDataNodes(_GfxData->mMovtexs);
ClearLvlDataNodes(_GfxData->mMovtexQCs);
ClearLvlDataNodes(_GfxData->mRooms);
_GfxData->mPointerList.Clear();
_GfxData->mPointerOffsetList.Clear();
_GfxData->mLuaPointerList.Clear();
_GfxData->mLuaTokenList.Clear();
generated = true;
}
_GfxData->mChildGeoLayouts.Clear();
return generated;
}
static void DynOS_Lvl_GeneratePack_Recursive(const SysPath &directory, GfxData *_GfxData) {
DIR *aPackDir = opendir(directory.c_str());
if (aPackDir) {
struct dirent *_PackEnt = NULL;
while ((_PackEnt = readdir(aPackDir)) != NULL) {
// Skip . and ..
if (SysPath(_PackEnt->d_name) == ".") continue;
if (SysPath(_PackEnt->d_name) == "..") continue;
SysPath path = fstring("%s/%s", directory.c_str(), _PackEnt->d_name);
// Recurse through subfolders
if (fs_sys_dir_exists(path.c_str())) {
DynOS_Lvl_GeneratePack_Recursive(path, _GfxData);
continue;
}
// skip files that don't end in '.c'
size_t nameLen = strlen(_PackEnt->d_name);
if (_PackEnt->d_name[nameLen - 2] != '.' || _PackEnt->d_name[nameLen - 1] != 'c') {
continue;
}
// read the file
DynOS_Read_Source(_GfxData, path.c_str());
}
closedir(aPackDir);
}
}
void DynOS_Lvl_GeneratePack(const SysPath &aPackFolder) {
Print("Processing levels: \"%s\"", aPackFolder.c_str());
if (!DynOS_ShouldGeneratePack(aPackFolder, { ".lvl" })) {
return;
}
Array<Pair<u64, String>> _ActorsFolders;
GfxData *_GfxData = New<GfxData>();
_GfxData->mModelIdentifier = 0;
DIR *aPackDir = opendir(aPackFolder.c_str());
if (aPackDir) {
struct dirent *_PackEnt = NULL;
while ((_PackEnt = readdir(aPackDir)) != NULL) {
// Skip . and ..
if (SysPath(_PackEnt->d_name) == ".") continue;
if (SysPath(_PackEnt->d_name) == "..") continue;
// Compress .lvl files to gain some space
bool _IsLvl = (SysPath(_PackEnt->d_name).find(".lvl") != SysPath::npos);
SysPath _Filename = fstring("%s/%s", aPackFolder.c_str(), _PackEnt->d_name);
if (_IsLvl && !DynOS_Bin_IsCompressed(_Filename)) {
if (configCompressOnStartup) { DynOS_Bin_Compress(_Filename); }
continue;
}
// For each subfolder, read tokens from script.c
SysPath _Folder = fstring("%s/%s", aPackFolder.c_str(), _PackEnt->d_name);
if (!fs_sys_dir_exists(_Folder.c_str())) continue;
// Only parse folders with a 'script.c'
SysPath _ScriptFile = fstring("%s/script.c", _Folder.c_str());
if (!fs_sys_file_exists(_ScriptFile.c_str())) {
_ScriptFile = fstring("%s/custom.script.c", _Folder.c_str());
if (!fs_sys_file_exists(_ScriptFile.c_str())) {
continue;
}
}
// Prevent generating from folders that likely already generated
SysPath _LvlFile = fstring("%s/level_%s_entry.lvl", aPackFolder.c_str(), _PackEnt->d_name);
if (DynOS_GenFileExistsAndIsNewerThanFolder(_LvlFile, _Folder)) {
continue;
}
_GfxData->mModelIdentifier++;
DynOS_Lvl_GeneratePack_Recursive(_Folder, _GfxData);
}
closedir(aPackDir);
}
// Generate a binary file for each level found in the GfxData
DynOS_Lvl_GeneratePack_Internal(aPackFolder, _ActorsFolders, _GfxData);
DynOS_Gfx_Free(_GfxData);
}