diff --git a/src/engine/geo_layout.c b/src/engine/geo_layout.c index 10e09056..25ec6c62 100644 --- a/src/engine/geo_layout.c +++ b/src/engine/geo_layout.c @@ -6,6 +6,7 @@ #include "game/memory.h" #include "graph_node.h" #include "port/Engine.h" +#include "port/game/GeoLayoutParser.h" typedef void (*GeoLayoutCommandProc)(void); @@ -781,20 +782,20 @@ struct GraphNode *process_geo_layout(struct AllocOnlyPool *pool, void *segptr) { gGeoLayoutStackIndex = 2; gGeoLayoutReturnIndex = 2; // stack index is often copied here? - if(GameEngine_OTRSigCheck(segptr) == 1){ - gGeoLayoutCommand = ResourceGetDataByName(segptr); - } else { - gGeoLayoutCommand = segmented_to_virtual(segptr); - } - gGraphNodePool = pool; gGeoLayoutStack[0] = 0; gGeoLayoutStack[1] = 0; - while (gGeoLayoutCommand != NULL) { - uint8_t cmdId = gGeoLayoutCommand[0x00]; - GeoLayoutJumpTable[cmdId](); + if(GameEngine_OTRSigCheck(segptr) == 1){ + GeoLayoutExecute(segptr); + } else { + gGeoLayoutCommand = segmented_to_virtual(segptr); + + while (gGeoLayoutCommand != NULL) { + uint8_t cmdId = gGeoLayoutCommand[0x00]; + GeoLayoutJumpTable[cmdId](); + } } return gCurRootGraphNode; diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 329787dd..8703f1c4 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -26,39 +26,43 @@ extern "C" { GameEngine* GameEngine::Instance; GameInteractor* GameInteractor::Instance; -GameEngine::GameEngine(){ +GameEngine::GameEngine(): dictionary(nullptr) { std::vector OTRFiles; - std::string cubePath = LUS::Context::GetPathRelativeToAppDirectory("smcube.otr"); - if (std::filesystem::exists(cubePath)) { - OTRFiles.push_back(cubePath); + if (const std::string cube_path = LUS::Context::GetPathRelativeToAppDirectory("smcube.otr"); std::filesystem::exists(cube_path)) { + OTRFiles.push_back(cube_path); } - std::string sm64OtrPath = LUS::Context::GetPathRelativeToAppBundle("sm64.otr"); - if (std::filesystem::exists(sm64OtrPath)) { - OTRFiles.push_back(sm64OtrPath); - } - std::string patchesPath = LUS::Context::GetPathRelativeToAppDirectory("mods"); - if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) { - if (std::filesystem::is_directory(patchesPath)) { - for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) { + if (const std::string sm64_otr_path = LUS::Context::GetPathRelativeToAppBundle("sm64.otr"); std::filesystem::exists(sm64_otr_path)) { + OTRFiles.push_back(sm64_otr_path); + } + if (const std::string patches_path = LUS::Context::GetPathRelativeToAppDirectory("mods"); !patches_path.empty() && std::filesystem::exists(patches_path)) { + if (std::filesystem::is_directory(patches_path)) { + for (const auto&p: std::filesystem::recursive_directory_iterator(patches_path)) { if (StringHelper::IEquals(p.path().extension().string(), ".otr")) { OTRFiles.push_back(p.path().generic_string()); } } } } - this->context = LUS::Context::CreateInstance("Ghostship", "sm64", "ghostship.cfg.json", OTRFiles, { 0xFF2B5A63, 0xE3DAA4E }, 3); + this->context = LUS::Context::CreateInstance("Ghostship", "sm64", "ghostship.cfg.json", OTRFiles, + {0xFF2B5A63, 0xE3DAA4E}, 3); this->context->GetWindow()->SetTargetFps(60); this->context->GetWindow()->SetMaximumFrameLatency(1); - this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SAnim, "Animation", std::make_shared()); - this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Bank, "Bank", std::make_shared()); - this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Sample, "Sample", std::make_shared()); - this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Sequence, "Sequence", std::make_shared()); - this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SDialog, "Dialog", std::make_shared()); - this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::Dictionary, "Dictionary", std::make_shared()); + this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory( + LUS::ResourceType::SAnim, "Animation", std::make_shared()); + this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory( + LUS::ResourceType::Bank, "Bank", std::make_shared()); + this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory( + LUS::ResourceType::Sample, "Sample", std::make_shared()); + this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory( + LUS::ResourceType::Sequence, "Sequence", std::make_shared()); + this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory( + LUS::ResourceType::SDialog, "Dialog", std::make_shared()); + this->context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory( + LUS::ResourceType::Dictionary, "Dictionary", std::make_shared()); } void GameEngine::Create(){ - auto instance = GameEngine::Instance = new GameEngine(); + const auto instance = Instance = new GameEngine(); GameInteractor::Instance = new GameInteractor(); InitMods(); GameUI::SetupGuiElements(); @@ -67,14 +71,14 @@ void GameEngine::Create(){ } void GameEngine::Destroy(){ - GameEngine::AudioExit(); + AudioExit(); } bool ShouldClearTextureCacheAtEndOfFrame = false; void GameEngine::StartFrame() const{ using LUS::KbScancode; - int32_t dwScancode = this->context->GetWindow()->GetLastScancode(); + const int32_t dwScancode = this->context->GetWindow()->GetLastScancode(); this->context->GetWindow()->SetLastScancode(-1); switch (dwScancode) { @@ -84,6 +88,7 @@ void GameEngine::StartFrame() const{ ShouldClearTextureCacheAtEndOfFrame = true; break; } + default: break; } this->context->GetWindow()->StartFrame(); } @@ -164,19 +169,16 @@ void GameEngine::EndAudioFrame(){ } void GameEngine::AudioInit() { - auto resourceMgr = LUS::Context::GetInstance()->GetResourceManager(); + const auto resourceMgr = LUS::Context::GetInstance()->GetResourceManager(); resourceMgr->LoadDirectory("sound"); - auto banksFiles = resourceMgr->GetArchive()->ListFiles("sound/banks/*"); - for(auto& bank : *banksFiles){ + for(const auto banksFiles = resourceMgr->GetArchive()->ListFiles("sound/banks/*"); auto& bank : *banksFiles){ auto path = "__OTR__" + bank; - auto ctl = static_cast(ResourceGetDataByName(path.c_str())); + const auto ctl = static_cast(ResourceGetDataByName(path.c_str())); this->bankMapTable[bank] = ctl->bankId; } - auto sequencesFiles = resourceMgr->GetArchive()->ListFiles("sound/sequences/*"); - - for(auto& sequence : *sequencesFiles){ + for(const auto sequences_files = resourceMgr->GetArchive()->ListFiles("sound/sequences/*"); auto& sequence : *sequences_files){ auto path = "__OTR__" + sequence; auto seq = static_cast(ResourceGetDataByName(path.c_str())); this->sequencesMapTable[seq->id] = path; @@ -184,13 +186,13 @@ void GameEngine::AudioInit() { if (!audio.running) { audio.running = true; - audio.thread = std::thread(GameEngine::HandleAudioThread); + audio.thread = std::thread(HandleAudioThread); } } void GameEngine::AudioExit() { { - std::unique_lock Lock(audio.mutex); + std::unique_lock lock(audio.mutex); audio.running = false; } audio.cv_to_thread.notify_all(); @@ -211,6 +213,10 @@ uint8_t GameEngine::GetBankIdByName(const std::string& name) { return 0; } +uint32_t GameEngine::GetGameVersion() { + return LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[0]; +} + extern "C" uint32_t GameEngine_GetInterpolatedFPS() { return GameEngine::GetInterpolationFPS(); } @@ -248,7 +254,7 @@ extern "C" CtlEntry* GameEngine_LoadBank(uint8_t bankId) { } for(auto& bank : engine->bankMapTable){ if(bank.second == bankId){ - auto ctl = static_cast(ResourceGetDataByName(("__OTR__" + bank.first).c_str())); + const auto ctl = static_cast(ResourceGetDataByName(("__OTR__" + bank.first).c_str())); engine->banks[bankId] = ctl; return ctl; } @@ -290,13 +296,13 @@ extern "C" uint32_t GameEngine_GetSequenceCount(){ } extern "C" uint8_t GameEngine_IsSequenceLoaded(uint8_t seqId) { - auto engine = GameEngine::Instance; + const auto engine = GameEngine::Instance; GameEngine_LoadSequence(seqId); return engine->sequences.contains(seqId); } extern "C" void GameEngine_UnloadSequence(uint8_t seqId) { - auto engine = GameEngine::Instance; + const auto engine = GameEngine::Instance; if(engine->sequences.contains(seqId)){ engine->sequences.erase(seqId); } @@ -307,38 +313,28 @@ extern "C" uint32_t GameEngine_GetGameVersion() { } extern "C" uint8_t* GameEngine_LoadActName(uint32_t actId){ - return (uint8_t*) ResourceGetDataByName(StringHelper::Sprintf(gActRoot, actId).c_str()); + return static_cast(ResourceGetDataByName(StringHelper::Sprintf(gActRoot, actId).c_str())); } extern "C" uint8_t* GameEngine_LoadLevelName(uint32_t courseId){ - return (uint8_t*) ResourceGetDataByName(StringHelper::Sprintf(gCourseRoot, courseId).c_str()); + return static_cast(ResourceGetDataByName(StringHelper::Sprintf(gCourseRoot, courseId).c_str())); } extern "C" DialogEntry* GameEngine_LoadDialog(uint32_t dialogId){ - auto dialog = (DialogEntry*) ResourceGetDataByName(StringHelper::Sprintf(gDialogRoot, dialogId).c_str()); + auto dialog = static_cast(ResourceGetDataByName(StringHelper::Sprintf(gDialogRoot, dialogId).c_str())); return dialog; } extern "C" uint8_t* GameEngine_LoadTranslation(const char* key) { - auto engine = GameEngine::Instance; - auto dictionary = engine->dictionary; + const auto engine = GameEngine::Instance; + const auto dictionary = engine->dictionary; - assert(engine->dictionary != nullptr); - assert(engine->dictionary->contains(key)); + assert(dictionary != nullptr); + assert(dictionary->contains(key)); - return engine->dictionary->at(key).data(); + return dictionary->at(key).data(); } extern "C" int GameEngine_OTRSigCheck(const char* data) { - auto i = (uintptr_t)(data); - if ((i & 1) == 1) - return 0; - - if (i != 0) { - if (data[0] == '_' && data[1] == '_' && data[2] == 'O' && data[3] == 'T' && data[4] == 'R' && - data[5] == '_' && data[6] == '_') - return 1; - } - - return 0; + return LUS::ResourceManager::OtrSignatureCheck(data); } \ No newline at end of file diff --git a/src/port/Engine.h b/src/port/Engine.h index 9f73147d..120461f0 100644 --- a/src/port/Engine.h +++ b/src/port/Engine.h @@ -39,8 +39,9 @@ class GameEngine { static void AudioExit(); static uint8_t GetBankIdByName(const std::string& name); void LoadDictionary(); + uint32_t GetGameVersion(); - void Destroy(); + static void Destroy(); }; #else uint32_t GameEngine_GetInterpolatedFPS(); diff --git a/src/port/GBIMiddleware.cpp b/src/port/GBIMiddleware.cpp index 4844a988..ab8f0e0a 100644 --- a/src/port/GBIMiddleware.cpp +++ b/src/port/GBIMiddleware.cpp @@ -12,7 +12,7 @@ extern "C" void gSPDisplayList(Gfx* pkt, Gfx* dl) { if (GameEngine_OTRSigCheck(imgData) == 1) { auto resource = LUS::Context::GetInstance()->GetResourceManager()->LoadResource(imgData); auto res = std::static_pointer_cast(resource); - dl = (Gfx*)&res->Instructions[0]; + dl = &res->Instructions[0]; } __gSPDisplayList(pkt, dl); @@ -41,6 +41,5 @@ extern "C" void gSPInvalidateTexCache(Gfx* pkt, uintptr_t texAddr) { texAddr = (uintptr_t) res->GetRawPointer(); } } -// OTRTODO: -// __gSPInvalidateTexCache(pkt, texAddr); + __gSPInvalidateTexCache(pkt, texAddr); } diff --git a/src/port/game/GeoLayoutParser.cpp b/src/port/game/GeoLayoutParser.cpp new file mode 100644 index 00000000..ab5a470f --- /dev/null +++ b/src/port/game/GeoLayoutParser.cpp @@ -0,0 +1,526 @@ +#include "GeoLayoutParser.h" + +#include +#include "port/Engine.h" +#include + +extern "C" { +#include "engine/geo_layout.h" +#include "engine/graph_node.h" +#include "engine/math_util.h" + +// Function Table +#include "menu/intro_geo.h" +} +LUS::BinaryReader* GeoLayoutParser::mReader; +LUS::BinaryReader* GeoLayoutParser::mLoadedReader; + +typedef void (*GeoCommandFunction)(); +#undef cur_geo_cmd_ptr + +std::unordered_map mUSFunctionTable = { + { 0x8016F670, geo_intro_super_mario_64_logo }, + { 0x8016f984, geo_intro_tm_copyright }, + { 0x8016fe70, geo_intro_regular_backdrop }, + { 0x8016fffc, geo_intro_gameover_backdrop } +}; + +std::unordered_map mJPFunctionTable = {}; + +std::unordered_map> mFunctionTable = { + { 0xFF2B5A63, mUSFunctionTable }, + { 0xE3DAA4E0, mJPFunctionTable } +}; + +GraphNodeFunc GetFunctionByAddr(const uint32_t addr) { + auto table = mFunctionTable[GameEngine::Instance->GetGameVersion()]; + + if(!table.contains(addr)) { + SPDLOG_ERROR("Function table does not contain address: 0x{:X}", addr); + return nullptr; + } + + return table[addr]; +} + +void ReadVec3f(Vec3f dst) { + dst[0] = GeoLayoutParser::mReader->ReadFloat(); + dst[1] = GeoLayoutParser::mReader->ReadFloat(); + dst[2] = GeoLayoutParser::mReader->ReadFloat(); +} + +void ReadVec3sToVec3f(Vec3f dst) { + dst[0] = GeoLayoutParser::mReader->ReadInt16(); + dst[1] = GeoLayoutParser::mReader->ReadInt16(); + dst[2] = GeoLayoutParser::mReader->ReadInt16(); +} + +void ReadVec3s(Vec3s dst) { + dst[0] = GeoLayoutParser::mReader->ReadInt16(); + dst[1] = GeoLayoutParser::mReader->ReadInt16(); + dst[2] = GeoLayoutParser::mReader->ReadInt16(); +} + +void ReadVec3sAngle(Vec3s dst) { + dst[0] = (GeoLayoutParser::mReader->ReadInt16() << 15) / 180; + dst[1] = (GeoLayoutParser::mReader->ReadInt16() << 15) / 180; + dst[2] = (GeoLayoutParser::mReader->ReadInt16() << 15) / 180; +} + + +void process_cmd_branch_and_link() { + const auto crc = GeoLayoutParser::mReader->ReadUInt64(); + + const auto data = static_cast(ResourceGetDataByCrc(crc)); + const auto size = ResourceGetSizeByCrc(crc); + GeoLayoutParser::mReader = new LUS::BinaryReader(data, size); + + gGeoLayoutStack[gGeoLayoutStackIndex++] = reinterpret_cast(GeoLayoutParser::mLoadedReader); + gGeoLayoutStack[gGeoLayoutStackIndex++] = (gCurGraphNodeIndex << 16) + gGeoLayoutReturnIndex; + gGeoLayoutReturnIndex = gGeoLayoutStackIndex; +} + +void process_cmd_end() { + gGeoLayoutStackIndex = gGeoLayoutReturnIndex; + gGeoLayoutReturnIndex = gGeoLayoutStack[--gGeoLayoutStackIndex] & 0xFFFF; + gCurGraphNodeIndex = gGeoLayoutStack[gGeoLayoutStackIndex] >> 16; + GeoLayoutParser::mReader = reinterpret_cast(gGeoLayoutStack[--gGeoLayoutStackIndex]); +} + +void process_cmd_branch() { + const auto param = GeoLayoutParser::mReader->ReadUByte(); + const auto crc = GeoLayoutParser::mReader->ReadUInt32(); + + const auto data = static_cast(ResourceGetDataByCrc(crc)); + const auto size = ResourceGetSizeByCrc(crc); + + if (param == 1) { + gGeoLayoutStack[gGeoLayoutStackIndex++] = reinterpret_cast(GeoLayoutParser::mLoadedReader); + GeoLayoutParser::mReader = new LUS::BinaryReader(data, size); + } else { + GeoLayoutParser::mLoadedReader = new LUS::BinaryReader(data, size); + GeoLayoutParser::mReader = GeoLayoutParser::mLoadedReader; + } +} + +void process_cmd_return() { + GeoLayoutParser::mReader = reinterpret_cast(gGeoLayoutStack[--gGeoLayoutStackIndex]); +} + +void process_cmd_open_node() { + gCurGraphNodeList[gCurGraphNodeIndex + 1] = gCurGraphNodeList[gCurGraphNodeIndex]; + gCurGraphNodeIndex++; +} + +void process_cmd_close_node() { + gCurGraphNodeIndex--; +} + +void process_cmd_assign_as_view() { + const auto index = GeoLayoutParser::mReader->ReadUInt16(); + + if (index < gGeoNumViews) { + gGeoViews[index] = gCurGraphNodeList[gCurGraphNodeIndex]; + } +} + +void process_cmd_update_node_flags() { + const auto operation = GeoLayoutParser::mReader->ReadUByte(); + const auto flagBits = GeoLayoutParser::mReader->ReadInt16(); + + switch (operation) { + case GEO_CMD_FLAGS_RESET: + gCurGraphNodeList[gCurGraphNodeIndex]->flags = flagBits; + break; + case GEO_CMD_FLAGS_SET: + gCurGraphNodeList[gCurGraphNodeIndex]->flags |= flagBits; + break; + case GEO_CMD_FLAGS_CLEAR: + gCurGraphNodeList[gCurGraphNodeIndex]->flags &= ~flagBits; + break; + default: + throw std::runtime_error("Unknown flag"); + } +} + +void process_cmd_node_root() { + gGeoNumViews = GeoLayoutParser::mReader->ReadInt16() + 2; + const auto x = GeoLayoutParser::mReader->ReadInt16(); + const auto y = GeoLayoutParser::mReader->ReadInt16(); + const auto width = GeoLayoutParser::mReader->ReadInt16(); + const auto height = GeoLayoutParser::mReader->ReadInt16(); + + GraphNodeRoot* graphNode = init_graph_node_root(gGraphNodePool, nullptr, 0, x, y, width, height); + + gGeoViews = static_cast(alloc_only_pool_alloc(gGraphNodePool, gGeoNumViews * sizeof(GraphNode *))); + + graphNode->views = gGeoViews; + graphNode->numViews = gGeoNumViews; + + for (int32_t i = 0; i < gGeoNumViews; i++) { + gGeoViews[i] = nullptr; + } + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_ortho_projection() { + const auto scale = GeoLayoutParser::mReader->ReadInt16() / 100.0f; + + GraphNodeOrthoProjection* graphNode = init_graph_node_ortho_projection(gGraphNodePool, nullptr, scale); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_perspective() { + GraphNodeFunc frustumFunc = nullptr; + + const auto param = GeoLayoutParser::mReader->ReadUByte(); + const auto fov = GeoLayoutParser::mReader->ReadInt16(); + const auto near = GeoLayoutParser::mReader->ReadInt16(); + const auto far = GeoLayoutParser::mReader->ReadInt16(); + + if (param != 0) { + const auto func = GeoLayoutParser::mReader->ReadUInt32(); + frustumFunc = GetFunctionByAddr(func); + } + + GraphNodePerspective* graphNode = init_graph_node_perspective(gGraphNodePool, nullptr, fov, near, far, frustumFunc, 0); + + register_scene_graph_node(&graphNode->fnNode.node); +} + +void process_cmd_node_start() { + GraphNodeStart* graphNode = init_graph_node_start(gGraphNodePool, nullptr); + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_nop3() {} + +void process_cmd_node_master_list() { + const auto enable = GeoLayoutParser::mReader->ReadUByte(); + GraphNodeMasterList* graphNode = init_graph_node_master_list(gGraphNodePool, nullptr, enable); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_level_of_detail() { + const auto minDistance = GeoLayoutParser::mReader->ReadInt16(); + const auto maxDistance = GeoLayoutParser::mReader->ReadInt16(); + + GraphNodeLevelOfDetail* graphNode = init_graph_node_render_range(gGraphNodePool, nullptr, minDistance, maxDistance); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_switch_case() { + + const auto cs = GeoLayoutParser::mReader->ReadInt16(); + const auto func = GeoLayoutParser::mReader->ReadUInt32(); + + GraphNodeSwitchCase *graphNode = + init_graph_node_switch_case(gGraphNodePool, nullptr, + cs, // case which is initially selected + 0, + GetFunctionByAddr(func), // case update function + 0); + + register_scene_graph_node(&graphNode->fnNode.node); +} + +void process_cmd_node_camera() { + + Vec3f pos, focus; + + ReadVec3f(pos); + ReadVec3f(focus); + + const auto addr = GeoLayoutParser::mReader->ReadUInt32(); + const auto type = GeoLayoutParser::mReader->ReadUInt16(); + + GraphNodeCamera* graphNode = init_graph_node_camera(gGraphNodePool, nullptr, pos, focus, + GetFunctionByAddr(addr), type); + + register_scene_graph_node(&graphNode->fnNode.node); + + gGeoViews[0] = &graphNode->fnNode.node; +} + +void process_cmd_node_translation_rotation() { + Vec3s translation, rotation; + + void* displayList = nullptr; + s16 drawingLayer = 0; + + const auto params = GeoLayoutParser::mReader->ReadUByte(); + + switch ((params & 0x70) >> 4) { + case 0: + ReadVec3s(translation); + ReadVec3sAngle(rotation); + break; + case 1: + ReadVec3s(translation); + vec3s_copy(rotation, gVec3sZero); + break; + case 2: + ReadVec3sAngle(rotation); + vec3s_copy(translation, gVec3sZero); + break; + case 3: + const auto y = GeoLayoutParser::mReader->ReadInt16(); + vec3s_copy(translation, gVec3sZero); + vec3s_set(rotation, 0, (y << 15) / 180, 0); + break; + } + + if (params & 0x80) { + displayList = ResourceGetDataByCrc(GeoLayoutParser::mReader->ReadUInt64()); + drawingLayer = params & 0x0F; + } + + GraphNodeTranslationRotation* graphNode = init_graph_node_translation_rotation( + gGraphNodePool, nullptr, + drawingLayer, displayList, + translation, rotation + ); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_translation() { + Vec3s translation; + + const auto params = GeoLayoutParser::mReader->ReadUByte(); + s16 drawingLayer = 0; + void *displayList = nullptr; + + ReadVec3s(translation); + + if (params & 0x80) { + displayList = ResourceGetDataByCrc(GeoLayoutParser::mReader->ReadUInt64()); + drawingLayer = params & 0x0F; + } + + GraphNodeTranslation* graphNode = + init_graph_node_translation(gGraphNodePool, nullptr, drawingLayer, displayList, translation); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_rotation() { + Vec3s rotation; + + const auto params = GeoLayoutParser::mReader->ReadUByte(); + s16 drawingLayer = 0; + void *displayList = nullptr; + + ReadVec3s(rotation); + + if (params & 0x80) { + displayList = ResourceGetDataByCrc(GeoLayoutParser::mReader->ReadUInt64()); + drawingLayer = params & 0x0F; + } + + GraphNodeRotation* graphNode = + init_graph_node_rotation(gGraphNodePool, nullptr, drawingLayer, displayList, rotation); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_scale() { + const auto params = GeoLayoutParser::mReader->ReadUByte(); + const auto scale = GeoLayoutParser::mReader->ReadUInt32() / 65536.0f; + s16 drawingLayer = 0; + void *displayList = nullptr; + + if (params & 0x80) { + displayList = ResourceGetDataByCrc(GeoLayoutParser::mReader->ReadUInt64()); + drawingLayer = params & 0x0F; + } + + GraphNodeScale* graphNode = init_graph_node_scale(gGraphNodePool, nullptr, drawingLayer, displayList, scale); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_nop2() {} + +void process_cmd_node_animated_part() { + Vec3s translation; + const auto drawingLayer = GeoLayoutParser::mReader->ReadUByte(); + void* displayList = ResourceGetDataByCrc(GeoLayoutParser::mReader->ReadUInt64()); + + ReadVec3s(translation); + + GraphNodeAnimatedPart* graphNode = + init_graph_node_animated_part(gGraphNodePool, nullptr, drawingLayer, displayList, translation); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_billboard() { + Vec3s translation; + s16 drawingLayer = 0; + const auto params = GeoLayoutParser::mReader->ReadUByte(); + void* displayList = nullptr; + + ReadVec3s(translation); + + if (params & 0x80) { + displayList = ResourceGetDataByCrc(GeoLayoutParser::mReader->ReadUInt64()); + drawingLayer = params & 0x0F; + } + + GraphNodeBillboard* graphNode = init_graph_node_billboard(gGraphNodePool, nullptr, drawingLayer, displayList, translation); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_display_list() { + const auto drawingLayer = GeoLayoutParser::mReader->ReadUByte(); + void *displayList = ResourceGetDataByCrc(GeoLayoutParser::mReader->ReadUInt64()); + + GraphNodeDisplayList* graphNode = init_graph_node_display_list(gGraphNodePool, nullptr, drawingLayer, displayList); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_shadow() { + const auto type = GeoLayoutParser::mReader->ReadInt16(); + const auto solidity = GeoLayoutParser::mReader->ReadInt16(); + const auto scale = GeoLayoutParser::mReader->ReadInt16(); + + GraphNodeShadow* graphNode = init_graph_node_shadow(gGraphNodePool, nullptr, scale, solidity, type); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_object_parent() { + GraphNodeObjectParent* graphNode = init_graph_node_object_parent(gGraphNodePool, nullptr, &gObjParentGraphNode); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_generated() { + const auto param = GeoLayoutParser::mReader->ReadInt16(); + const auto addr = GeoLayoutParser::mReader->ReadUInt32(); + + GraphNodeGenerated* graphNode = init_graph_node_generated(gGraphNodePool, nullptr, + GetFunctionByAddr(addr), // asm function + param); // parameter + + register_scene_graph_node(&graphNode->fnNode.node); +} + +void process_cmd_node_background() { + const auto param = GeoLayoutParser::mReader->ReadInt16(); + const auto addr = GeoLayoutParser::mReader->ReadUInt32(); + + GraphNodeBackground* graphNode = init_graph_node_background( + gGraphNodePool, nullptr, + param, // background ID, or RGBA5551 color if asm function is null + GetFunctionByAddr(addr), // asm function + 0); + + register_scene_graph_node(&graphNode->fnNode.node); +} + +void process_cmd_nop() {} + +void process_cmd_copy_view() { + GraphNode *node = nullptr; + const auto index = GeoLayoutParser::mReader->ReadInt16(); + + if (index >= 0) { + node = gGeoViews[index]; + + if (node->type == GRAPH_NODE_TYPE_OBJECT_PARENT) { + node = reinterpret_cast(node)->sharedChild; + } else { + node = nullptr; + } + } + + GraphNodeObjectParent* graphNode = init_graph_node_object_parent(gGraphNodePool, nullptr, node); + + register_scene_graph_node(&graphNode->node); +} + +void process_cmd_node_held_obj() { + Vec3s offset; + + const auto addr = GeoLayoutParser::mReader->ReadUInt32(); + const auto player = GeoLayoutParser::mReader->ReadUByte(); + + ReadVec3s(offset); + + GraphNodeHeldObject *graphNode = init_graph_node_held_object( + gGraphNodePool, nullptr, nullptr, + offset, + GetFunctionByAddr(addr), + player + ); + + register_scene_graph_node(&graphNode->fnNode.node); +} + +void process_cmd_node_culling_radius() { + const auto radius = GeoLayoutParser::mReader->ReadInt16(); + GraphNodeCullingRadius* graphNode = init_graph_node_culling_radius(gGraphNodePool, nullptr, radius); + register_scene_graph_node(&graphNode->node); +} + +GeoCommandFunction GeoLayoutFunctionTable[] = { + process_cmd_branch_and_link, + process_cmd_end, + process_cmd_branch, + process_cmd_return, + process_cmd_open_node, + process_cmd_close_node, + process_cmd_assign_as_view, + process_cmd_update_node_flags, + process_cmd_node_root, + process_cmd_node_ortho_projection, + process_cmd_node_perspective, + process_cmd_node_start, + process_cmd_node_master_list, + process_cmd_node_level_of_detail, + process_cmd_node_switch_case, + process_cmd_node_camera, + process_cmd_node_translation_rotation, + process_cmd_node_translation, + process_cmd_node_rotation, + process_cmd_node_animated_part, + process_cmd_node_billboard, + process_cmd_node_display_list, + process_cmd_node_shadow, + process_cmd_node_object_parent, + process_cmd_node_generated, + process_cmd_node_background, + process_cmd_nop, + process_cmd_copy_view, + process_cmd_node_held_obj, + process_cmd_node_scale, + process_cmd_nop2, + process_cmd_nop3, + process_cmd_node_culling_radius, +}; + + +void GeoLayoutParser::execute(const char* path) { + const auto data = static_cast(ResourceGetDataByName(path)); + const auto size = ResourceGetSizeByName(path); + + mLoadedReader = new LUS::BinaryReader(data, size); + mReader = mLoadedReader; + + while (mReader != nullptr) { + const auto cmdId = mReader->ReadUByte(); + GeoLayoutFunctionTable[cmdId](); + } +} + +extern "C" void GeoLayoutExecute(char const* path) { + GeoLayoutParser::execute(path); +} \ No newline at end of file diff --git a/src/port/game/GeoLayoutParser.h b/src/port/game/GeoLayoutParser.h new file mode 100644 index 00000000..53aac64a --- /dev/null +++ b/src/port/game/GeoLayoutParser.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef __cplusplus + +#include + +class GeoLayoutParser { +public: + static LUS::BinaryReader* mLoadedReader; + static LUS::BinaryReader* mReader; + static void execute(const char* path); +}; +#else +void GeoLayoutExecute(char const* path); +#endif