Custom Track O2R almost working needs testing

This commit is contained in:
MegaMech
2025-04-17 20:32:20 -06:00
parent a91b580980
commit a2d8302e32
27 changed files with 135 additions and 72 deletions
+2 -1
View File
@@ -35,6 +35,7 @@ extern "C" {
#include "collision.h"
#include "memory.h"
#include "course_offsets.h"
#include "course.h"
extern const char *banshee_boardwalk_dls[];
}
@@ -143,7 +144,7 @@ void BansheeBoardwalk::Load() {
D_801625EC = 0;
D_801625F4 = 0;
D_801625F0 = 0;
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_banshee_boardwalk_track_sections));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_banshee_boardwalk_track_sections));
func_80295C6C();
find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07000878), 128, 0, 0, 0);
}
+2 -1
View File
@@ -31,6 +31,7 @@ extern "C" {
#include "collision.h"
#include "code_8003DC40.h"
#include "memory.h"
#include "course.h"
extern const char *bowsers_castle_dls[];
}
@@ -142,7 +143,7 @@ BowsersCastle::BowsersCastle() {
void BowsersCastle::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_bowsers_castle_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_bowsers_castle_addr));
func_80295C6C();
find_vtx_and_set_colours(segmented_gfx_to_virtual(reinterpret_cast<void*>(0x07001350)), 0x32, 0, 0, 0);
}
+2 -1
View File
@@ -31,6 +31,7 @@ extern "C" {
#include "code_8003DC40.h"
#include "memory.h"
#include "course_offsets.h"
#include "course.h"
extern const char *choco_mountain_dls[];
}
@@ -156,7 +157,7 @@ void ChocoMountain::Load() {
nullify_displaylist((uintptr_t) segmented_gfx_to_virtual(reinterpret_cast<void*>(0x070003C8)));
}
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_choco_mountain_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_choco_mountain_addr));
func_802B5CAC(0x238E, 0x31C7, D_8015F590);
func_80295C6C();
}
+69 -10
View File
@@ -4,6 +4,8 @@
#include "MarioRaceway.h"
#include "ChocoMountain.h"
#include "port/Game.h"
#include "port/resource/type/TrackWaypoint.h"
#include "port/resource/type/TrackSections.h"
extern "C" {
#include "main.h"
@@ -29,7 +31,7 @@ Course::Course() {
// Props.CourseLength = "567m";
// Props.Cup = FLOWER_CUP;
// Props.CupIndex = 3;
Props.TrackModel = NULL;
Props.TrackSectionsPtr = NULL;
Props.LakituTowType = (s32) OLakitu::LakituTowType::NORMAL;
Props.AIBehaviour = D_0D008F28;
Props.AIMaximumSeparation = 50.0f;
@@ -87,18 +89,53 @@ void Course::Load(Vtx* vtx, Gfx* gfx) {
Course::Init();
}
// Load custom track from o2r
void Course::Load(std::string dls) {
std::vector<TrackSectionsO2R> sections = LOAD_ASSET_RAW(dls);
void Course::LoadO2R(std::string trackPath) {
if (!trackPath.empty()) {
Props.TrackSectionsPtr = (trackPath + "/data_track_sections").c_str();
for (auto& section : sections) {
generate_collision_mesh((Gfx*)LOAD_ASSET_RAW(section.addr), section.surfaceType, section.sectionId);
std::string path_file = (trackPath + "/data_paths").c_str();
auto res = std::dynamic_pointer_cast<MK64::Paths>(ResourceLoad(path_file.c_str()));
if (res != nullptr) {
std::vector<std::vector<TrackWaypointData>> paths = res->PathList;
size_t i = 0;
for (auto& path : paths) {
if (i < 4) { // Paths 1-4
Props.PathTable[i] = (TrackWaypoint*)&path[0];
} else if (i < 8) { // Paths 5-8
Props.PathTable2[i - 4] = (TrackWaypoint*)&path[0];
} else { // Only 8 course paths are supported.
break;
}
i += 1;
}
}
} else {
printf("Course.cpp: LoadO2R: trackPath str is empty\n");
}
Course::Init();
}
// Load stock track
void Course::Load() {
// Load from O2R
if (Props.TrackSectionsPtr != NULL || Props.TrackSectionsPtr[0] != '\0') {
auto res = std::dynamic_pointer_cast<MK64::TrackSectionsO2RClass>(ResourceLoad(Props.TrackSectionsPtr));
if (res != nullptr) {
std::vector<TrackSectionsO2R> sections = res->TrackSectionsList;
ParseCourseSections(sections);
}
return;
}
// Stock
size_t vtxSize = (ResourceGetSizeByName(this->vtx) / sizeof(CourseVtx)) * sizeof(Vtx);
size_t texSegSize;
@@ -143,6 +180,28 @@ void Course::Load() {
Course::Init();
}
// C++ version of parse_course_displaylists()
void Course::ParseCourseSections(std::vector<TrackSectionsO2R> sections) {
for (auto& section : sections) {
if (section.flags & 0x8000) {
D_8015F59C = 1;
} else {
D_8015F59C = 0;
}
if (section.flags & 0x2000) {
D_8015F5A0 = 1;
} else {
D_8015F5A0 = 0;
}
if (section.flags & 0x4000) {
D_8015F5A4 = 1;
} else {
D_8015F5A4 = 0;
}
generate_collision_mesh((Gfx*)LOAD_ASSET_RAW(section.addr), section.surfaceType, section.sectionId);
}
}
void Course::Init() {
gNumActors = 0;
gCourseMinX = 0;
@@ -240,9 +299,9 @@ void Course::Waypoints(Player* player, int8_t playerId) {
}
void Course::Render(struct UnkStruct_800DC5EC* arg0) {
if (Props.TrackModel) {
gSPDisplayList(gDisplayListHead++, (Gfx*)LOAD_ASSET_RAW(Props.TrackModel));
}
//if (Props.TrackSectionsPtr) {
// gSPDisplayList(gDisplayListHead++, (Gfx*)LOAD_ASSET_RAW(Props.TrackModel));
//}
}
void Course::RenderCredits() {
+6 -4
View File
@@ -6,6 +6,7 @@
#ifdef __cplusplus
#include "engine/objects/Lakitu.h"
#include "port/resource/type/TrackSections.h"
extern "C" {
#endif
@@ -77,7 +78,7 @@ typedef struct Properties {
const course_texture *textures;
enum MusicSeq Sequence;
float WaterLevel; // Used for effects, and Lakitu pick up height. Not necessarily the visual water model height.
const char* TrackModel;
const char* TrackSectionsPtr;
#ifdef __cplusplus
nlohmann::json to_json() const {
@@ -238,10 +239,11 @@ public:
explicit Course();
virtual void Load(); // Decompress and load stock courses
virtual void Load(Vtx* vtx, Gfx *gfx); // Load custom track from code
virtual void Load(std::string dls); // Load custom track from o2r
virtual void LoadO2R(std::string trackPath); // Load custom track from o2r
virtual void Load(); // Decompress and load stock courses or from o2r but Props.TrackSectionsPtr must be set.
virtual void Load(Vtx* vtx, Gfx *gfx); // Load custom track from code. Load must be overridden and then call to this base class method impl.
virtual void LoadTextures();
virtual void ParseCourseSections(std::vector<TrackSectionsO2R> sections);
/**
* @brief BeginPlay This function is called once at the start of gameplay.
+2 -1
View File
@@ -35,6 +35,7 @@ extern "C" {
#include "code_8003DC40.h"
#include "memory.h"
#include "sounds.h"
#include "course.h"
extern const char *d_course_dks_jungle_parkway_unknown_dl_list[];
extern s16 currentScreenSection;
}
@@ -140,7 +141,7 @@ DKJungle::DKJungle() {
void DKJungle::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_dks_jungle_parkway_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_dks_jungle_parkway_addr));
func_80295C6C();
// d_course_dks_jungle_parkway_packed_dl_3FA8
find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07003FA8), 120, 255, 255, 255);
+2 -1
View File
@@ -34,6 +34,7 @@ extern "C" {
#include "memory.h"
#include "update_objects.h"
#include "course_offsets.h"
#include "course.h"
extern const char *d_course_frappe_snowland_dl_list[];
extern s8 gPlayerCount;
}
@@ -119,7 +120,7 @@ FrappeSnowland::FrappeSnowland() {
void FrappeSnowland::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_frappe_snowland_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_frappe_snowland_addr));
func_80295C6C();
}
+2 -7
View File
@@ -50,12 +50,7 @@ extern "C" {
#include "collision.h"
#include "memory.h"
#include "courses/harbour/track.h"
typedef struct {
Gfx* addr;
u8 surfaceType;
u8 sectionId;
u16 flags;
} TrackSections;
#include "course.h"
}
TrackWaypoint harbour_path[] = {
@@ -609,7 +604,7 @@ void Harbour::Load() {
generate_collision_mesh_with_defaults(bush_map_004_mesh);
generate_collision_mesh_with_defaults(statue_map_005_mesh);
parse_course_displaylists((TrackSectionsI*)harbour_surfaces);
parse_course_displaylists((TrackSections*)harbour_surfaces);
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
}
+2 -1
View File
@@ -32,6 +32,7 @@ extern "C" {
#include "actors.h"
#include "collision.h"
#include "memory.h"
#include "course.h"
extern const char *kalimari_desert_dls[];
}
@@ -122,7 +123,7 @@ KalimariDesert::KalimariDesert() {
void KalimariDesert::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_kalimari_desert_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_kalimari_desert_addr));
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
}
+2 -1
View File
@@ -32,6 +32,7 @@ extern "C" {
#include "collision.h"
#include "code_8003DC40.h"
#include "memory.h"
#include "course.h"
extern const char *koopa_troopa_beach_dls[];
extern s8 gPlayerCount;
}
@@ -132,7 +133,7 @@ KoopaTroopaBeach::KoopaTroopaBeach() {
void KoopaTroopaBeach::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_koopa_troopa_beach_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_koopa_troopa_beach_addr));
func_80295C6C();
find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x0700ADE0), -0x6A, 255, 255, 255);
find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x0700A540), -0x6A, 255, 255, 255);
+2 -1
View File
@@ -34,6 +34,7 @@ extern "C" {
#include "courses/staff_ghost_data.h"
#include "framebuffer_effects.h"
#include "skybox_and_splitscreen.h"
#include "course.h"
extern const char* luigi_raceway_dls[];
extern s16 currentScreenSection;
}
@@ -154,7 +155,7 @@ LuigiRaceway::LuigiRaceway() {
void LuigiRaceway::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*) LOAD_ASSET_RAW(d_course_luigi_raceway_addr));
parse_course_displaylists((TrackSections*) LOAD_ASSET_RAW(d_course_luigi_raceway_addr));
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
}
+2 -1
View File
@@ -31,6 +31,7 @@ extern "C" {
#include "collision.h"
#include "memory.h"
#include "courses/staff_ghost_data.h"
#include "course.h"
extern const char *mario_raceway_dls[];
}
@@ -156,7 +157,7 @@ void MarioRaceway::Load() {
}
}
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_mario_raceway_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_mario_raceway_addr));
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
}
+2 -1
View File
@@ -32,6 +32,7 @@ extern "C" {
#include "collision.h"
#include "memory.h"
#include "code_80086E70.h"
#include "course.h"
extern const char *moo_moo_farm_dls[];
extern s16 currentScreenSection;
extern s8 gPlayerCount;
@@ -140,7 +141,7 @@ MooMooFarm::MooMooFarm() {
void MooMooFarm::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_moo_moo_farm_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_moo_moo_farm_addr));
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
}
+2 -1
View File
@@ -35,6 +35,7 @@ extern "C" {
#include "memory.h"
#include "courses/staff_ghost_data.h"
#include "podium_ceremony_actors.h"
#include "course.h"
extern const char *royal_raceway_dls[];
}
@@ -154,7 +155,7 @@ PodiumCeremony::PodiumCeremony() {
void PodiumCeremony::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_royal_raceway_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_royal_raceway_addr));
func_80295C6C();
}
+2 -1
View File
@@ -30,6 +30,7 @@ extern "C" {
#include "actors.h"
#include "collision.h"
#include "memory.h"
#include "course.h"
extern const char *rainbow_road_dls[];
}
@@ -117,7 +118,7 @@ void RainbowRoad::Load() {
Course::Load();
D_800DC5C8 = 1;
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_rainbow_road_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_rainbow_road_addr));
func_80295C6C();
// d_course_rainbow_road_packed_dl_2068
find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07002068), -0x6A, 255, 255, 255);
+2 -1
View File
@@ -30,6 +30,7 @@ extern "C" {
#include "collision.h"
#include "memory.h"
#include "courses/staff_ghost_data.h"
#include "course.h"
extern const char *royal_raceway_dls[];
}
@@ -153,7 +154,7 @@ RoyalRaceway::RoyalRaceway() {
void RoyalRaceway::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_royal_raceway_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_royal_raceway_addr));
func_80295C6C();
}
+2 -1
View File
@@ -30,6 +30,7 @@ extern "C" {
#include "actors.h"
#include "collision.h"
#include "memory.h"
#include "course.h"
extern const char *sherbet_land_dls[];
extern const char *sherbet_land_dls_2[];
}
@@ -118,7 +119,7 @@ SherbetLand::SherbetLand() {
void SherbetLand::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_sherbet_land_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_sherbet_land_addr));
func_80295C6C();
// d_course_sherbet_land_packed_dl_1EB8
find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07001EB8), -0x4C, 255, 255, 255);
+2 -7
View File
@@ -49,12 +49,7 @@ extern "C" {
#include "actors.h"
#include "collision.h"
#include "memory.h"
typedef struct {
Gfx* addr;
u8 surfaceType;
u8 sectionId;
u16 flags;
} TrackSections;
#include "course.h"
extern Gfx test_course_dls[];
extern Vtx mario_Plane_001_mesh_vtx_1[];
extern Gfx mario_Plane_001_mesh[];
@@ -135,7 +130,7 @@ void TestCourse::Load() {
generate_collision_mesh_with_defaults(mario_Plane_001_mesh);
parse_course_displaylists((TrackSectionsI*)test_course_addr);
parse_course_displaylists((TrackSections*)test_course_addr);
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
}
+2 -1
View File
@@ -36,6 +36,7 @@ extern "C" {
#include "collision.h"
#include "memory.h"
#include "code_80086E70.h"
#include "course.h"
extern const char *d_course_toads_turnpike_dl_list[];
extern s16 currentScreenSection;
extern s8 gPlayerCount;
@@ -148,7 +149,7 @@ void ToadsTurnpike::Load() {
D_801625F0 = 4;
D_802B87B0 = 993;
D_802B87B4 = 1000;
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_toads_turnpike_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_toads_turnpike_addr));
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
}
+2 -1
View File
@@ -32,6 +32,7 @@ extern "C" {
#include "code_8003DC40.h"
#include "memory.h"
#include "skybox_and_splitscreen.h"
#include "course.h"
extern const char* wario_stadium_dls[];
extern s16 currentScreenSection;
}
@@ -139,7 +140,7 @@ WarioStadium::WarioStadium() {
void WarioStadium::Load() {
Course::Load();
parse_course_displaylists((TrackSectionsI*) LOAD_ASSET_RAW(d_course_wario_stadium_addr));
parse_course_displaylists((TrackSections*) LOAD_ASSET_RAW(d_course_wario_stadium_addr));
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
// d_course_wario_stadium_packed_dl_C50
+2 -1
View File
@@ -33,6 +33,7 @@ extern "C" {
#include "actors.h"
#include "collision.h"
#include "memory.h"
#include "course.h"
extern const char *d_course_yoshi_valley_dl_list[];
}
@@ -128,7 +129,7 @@ void YoshiValley::Load() {
Lights1 lights4 = gdSPDefLights1(100, 100, 100, 255, 254, 254, 0, 0, 120);
set_track_light_direction(&lights4, -0x38F0, 0x1C70, 1);
parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_yoshi_valley_addr));
parse_course_displaylists((TrackSections*)LOAD_ASSET_RAW(d_course_yoshi_valley_addr));
func_80295C6C();
Props.WaterLevel = gCourseMinY - 10.0f;
}
@@ -18,7 +18,7 @@ ResourceFactoryBinaryTrackWaypointsV0::ReadResource(std::shared_ptr<Ship::File>
section->TrackWaypointList.reserve(count);
for (uint32_t i = 0; i < count; i++) {
TrackWaypoint data;
TrackWaypointData data;
data.posX = reader->ReadInt16();
data.posY = reader->ReadInt16();
data.posZ = reader->ReadInt16();
@@ -45,12 +45,12 @@ ResourceFactoryXMLTrackWaypointsV0::ReadResource(std::shared_ptr<Ship::File> fil
while (path != nullptr) {
std::vector<TrackWaypoint> waypointPath; // Temporary container for this path
std::vector<TrackWaypointData> waypointPath; // Temporary container for this path
auto pointElem = path->FirstChildElement("Point");
while (pointElem != nullptr) {
TrackWaypoint point;
TrackWaypointData point;
point.posX = pointElem->IntAttribute("X");
point.posY = pointElem->IntAttribute("Y");
point.posZ = pointElem->IntAttribute("Z");
+5 -4
View File
@@ -5,12 +5,12 @@ namespace MK64 {
TrackWaypoints::TrackWaypoints() : Resource(std::shared_ptr<Ship::ResourceInitData>()) {
}
TrackWaypoint* TrackWaypoints::GetPointer() {
TrackWaypointData* TrackWaypoints::GetPointer() {
return TrackWaypointList.data();
}
size_t TrackWaypoints::GetPointerSize() {
return TrackWaypointList.size() * sizeof(TrackWaypoint);
return TrackWaypointList.size() * sizeof(TrackWaypointData);
}
@@ -18,7 +18,8 @@ Paths::Paths() : Resource(std::shared_ptr<Ship::ResourceInitData>()) {
}
// I don't know how to return this properly
TrackWaypoint* Paths::GetPointer() {
TrackWaypointData* Paths::GetPointer() {
printf("Do not LOAD_ASSET an XML track waypoint/path, you need to use dynamic_cast<MK64::Paths>(ResourceLoad())");
return nullptr;
}
@@ -27,7 +28,7 @@ size_t Paths::GetPointerSize() {
for (const auto& path : PathList) {
totalWaypoints += path.size();
}
return totalWaypoints * sizeof(TrackWaypoint);
return totalWaypoints * sizeof(TrackWaypointData);
}
} // namespace MK64
+7 -7
View File
@@ -4,7 +4,7 @@
#include <vector>
#include <libultra/gbi.h>
struct TrackWaypoint {
struct TrackWaypointData {
int16_t posX;
int16_t posY;
int16_t posZ;
@@ -13,29 +13,29 @@ struct TrackWaypoint {
namespace MK64 {
// Used for binary import from torch
class TrackWaypoints : public Ship::Resource<TrackWaypoint> {
class TrackWaypoints : public Ship::Resource<TrackWaypointData> {
public:
using Resource::Resource;
TrackWaypoints();
TrackWaypoint* GetPointer() override;
TrackWaypointData* GetPointer() override;
size_t GetPointerSize() override;
std::vector<TrackWaypoint> TrackWaypointList;
std::vector<TrackWaypointData> TrackWaypointList;
};
// Used for xml
class Paths : public Ship::Resource<TrackWaypoint> {
class Paths : public Ship::Resource<TrackWaypointData> {
public:
using Resource::Resource;
Paths();
TrackWaypoint* GetPointer() override;
TrackWaypointData* GetPointer() override;
size_t GetPointerSize() override;
std::vector<std::vector<TrackWaypoint>> PathList;
std::vector<std::vector<TrackWaypointData>> PathList;
};
} // namespace MK64
+3 -3
View File
@@ -131,10 +131,10 @@ namespace Editor {
if (asset.ends_with("scene.json")) {
foundTrackFile = true;
if (ImGui::Button(label.c_str())) {
SetCourseByClass(new Course());
gWorldInstance.Courses.push_back(new Course());
SetCourseByClass(gWorldInstance.Courses.back());
LoadLevel(asset);
gWorldInstance.CurrentCourse->LoadO2R(TrackPath[track]);
gGamestateNext = RACING;
break;
}
+2 -2
View File
@@ -1,4 +1,4 @@
#include <libultraship.h>
#include <libultraship.h>
#include <libultra/gbi.h>
#include <macros.h>
#include <mk64.h>
@@ -50,7 +50,7 @@ s32 func_80290C20(Camera* camera) {
return 0;
}
void parse_course_displaylists(TrackSectionsI* asset) {
void parse_course_displaylists(TrackSections* asset) {
TrackSections* section = (TrackSections*) asset;
while (section->addr != 0) {
+2 -8
View File
@@ -3,17 +3,11 @@
#include "code_800029B0.h"
#include "../camera.h"
typedef struct {
u32 addr;
u8 surfaceType;
u8 sectionId;
u16 flags;
} TrackSectionsI;
#include <course.h>
void func_8029122C(struct UnkStruct_800DC5EC*, s32);
s32 func_80290C20(Camera*);
void parse_course_displaylists(TrackSectionsI* asset);
void parse_course_displaylists(TrackSections* asset);
void render_course_segments(const char*[], struct UnkStruct_800DC5EC*);
void func_80291198(void);
void render_mario_raceway_pipe(void);