From ee4e33dde9fb79d9263c974ec6a8b42bb52f8e25 Mon Sep 17 00:00:00 2001 From: jnack <55568980+jnackmclain@users.noreply.github.com> Date: Sat, 8 Aug 2026 21:04:16 -0500 Subject: [PATCH] Game origin rewrite.... Again! all credit to comp for this. This is just the pr #39 comp made, but applied on top of upstream master, as the branch comp worked from was heavily outdated There are several files with small naming converntion updates, struct updates, and so on. The bulk of the actual feature is in source\SetlistHooks.c a new directory for storing rawfiles now exists in the repo. this holds the folder structure for the new song list milo for xbox 360. This should probably be included in the github action nightly download, but i have not done this here I myself helped by finding the addresses for filling out the ports on the other platforms. go me. I also built and tested locally, which had me see that turning off the game origin setting in the ini didnt do anything, so i added a gate for that in MusicLibraryMatHook to check config.GameOriginIcons == 1. when disabled, it doesnt populate the mats with the textures. which isnt like, the most clean probably, as the mats are still made and the rest of everything still runs, but it doesnt show the icons so it works. I know the history here isnt ideal, being all one commit.... but this is a lot better than the 90 commits in my last pr. Co-Authored-By: ihatecompvir <6479048+ihatecompvir@users.noreply.github.com> --- .../gen/list_song_select_browser.milo_xbox | Bin 0 -> 364853 bytes include/GameOriginInfo.h | 12 + include/GemHooks.h | 4 +- include/LocaleHooks.h | 4 +- include/MiloSceneHooks.h | 1 + include/OvershellHooks.h | 2 +- include/SetlistHooks.h | 6 + include/ports_wii.h | 25 ++ include/ports_wii_bank8.h | 25 ++ include/ports_xbox360.h | 24 ++ include/rb3/Archive.h | 2 +- include/rb3/BandLabel.h | 3 +- include/rb3/BandSongMgr.h | 2 +- include/rb3/BandUser.h | 101 +++++-- include/rb3/BandUserMgr.h | 2 +- include/rb3/BinStream.h | 7 + include/rb3/ChunkStream.h | 12 +- include/rb3/Data.h | 2 +- include/rb3/FilePath.h | 15 + include/rb3/FileStream.h | 5 +- include/rb3/GameGem.h | 62 +++- include/rb3/GemTrackResrcManager.h | 31 ++ include/rb3/List.h | 13 + include/rb3/MusicLibrary.h | 16 + include/rb3/NodeSort.h | 22 ++ include/rb3/Object.h | 18 +- include/rb3/PassiveMessagesPanel.h | 2 +- include/rb3/Platform.h | 13 + include/rb3/Quazal/Step.h | 14 + include/rb3/Rnd/DynamicTex.h | 22 ++ include/rb3/Rnd/RndAnimatable.h | 14 + include/rb3/Rnd/RndMat.h | 93 ++++++ include/rb3/Rnd/RndPropAnim.h | 21 +- include/rb3/Rnd/RndTex.h | 53 ++++ include/rb3/Rnd/Transform.h | 24 ++ include/rb3/SongMetadata.h | 4 + include/rb3/SongSortMgr.h | 18 ++ include/rb3/SortNode.h | 64 +++- include/rb3/String.h | 2 + include/rb3/Track.h | 42 +++ include/rb3/TrackWatcher.h | 22 ++ include/rb3/UI/UIColor.h | 22 ++ include/rb3/UI/UIListSlot.h | 43 +++ include/rb3/Vector.h | 34 +-- include/rb3_include.h | 7 + source/GemHooks.c | 10 +- source/LocaleHooks.c | 7 +- source/MiloSceneHooks.c | 2 +- source/OvershellHooks.c | 56 ++-- source/RndPropAnimHooks.c | 2 +- source/SetlistHooks.c | 285 ++++++++++++++---- source/SongParserHooks.c | 2 +- source/_functions.c | 25 ++ source/rb3enhanced.c | 32 +- 54 files changed, 1159 insertions(+), 192 deletions(-) create mode 100644 assets/rawfiles/xbox/ui/resource/list/gen/list_song_select_browser.milo_xbox create mode 100644 include/GameOriginInfo.h create mode 100644 include/rb3/FilePath.h create mode 100644 include/rb3/GemTrackResrcManager.h create mode 100644 include/rb3/List.h create mode 100644 include/rb3/NodeSort.h create mode 100644 include/rb3/Platform.h create mode 100644 include/rb3/Quazal/Step.h create mode 100644 include/rb3/Rnd/DynamicTex.h create mode 100644 include/rb3/Rnd/RndAnimatable.h create mode 100644 include/rb3/Rnd/RndMat.h create mode 100644 include/rb3/Rnd/RndTex.h create mode 100644 include/rb3/Rnd/Transform.h create mode 100644 include/rb3/SongSortMgr.h create mode 100644 include/rb3/Track.h create mode 100644 include/rb3/TrackWatcher.h create mode 100644 include/rb3/UI/UIColor.h create mode 100644 include/rb3/UI/UIListSlot.h diff --git a/assets/rawfiles/xbox/ui/resource/list/gen/list_song_select_browser.milo_xbox b/assets/rawfiles/xbox/ui/resource/list/gen/list_song_select_browser.milo_xbox new file mode 100644 index 0000000000000000000000000000000000000000..3630e0b2a0f146122ddf3976f87ad7ce484bbb54 GIT binary patch literal 364853 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%0DT0zoa6I_eeGh^jx@Kg%kKOVCJ&1 zVCJ$b!OUgjz|3Xi!OVrzj`6_NMDCuzqVSspK9fO@f<6Vz$*}yJ0{S#Cr?IQJJ{`=- z>}pm7ziHeygUy6r5%|quvtWyKHn`AM47tw&*DN*%mLA1?`B4mZTrrn`c@5~LV3xAE zpyQ$$^)fKa7@pqFW7mRS%B}<75_Ub5u&r`J>s> z6fwfOmlocQD9n((<-zr62Q|hwucTKfa?;t;tzi)zN*5IW;}Ee-|MtS~p^vq7(`@W# z!KG)&slZsHrkP@izWS-ZA2paMeV*=P<}d8h!nq!DJHKjO_r1t zm90-U)beDdrk95-?73{;KCPE=Huc5xF29HL;PW}-Lj<}P@ zeAQ`TEV1d`W9WeopwQiwV*HZ8e$8z!LWD|QJyP_imD5yJ$!}^?LOd_3^-zZ6n^&!( z4{sx&KYcPGCH*{E(2$Z>GCt{t*!`f4C!DfMkTjMfF)(mt^{PtvvtddyA0L{h$OR_4 z(V0RG6uRAieugh2y9PY8oWez@hu7VNcb4T&BY!medMBWlK4gnWTS>O(rQ1S}WpzN9 z-FV*GdzbR9^*=~yq~hR4C;6e>daS>0Odn&yyVs zzQC?uhcX%95)T38g$@W*tx9j7TB}Mg55w!~n=8Kt@AB~}hIhe>(-fMYbMWgu3P$W5 zrTQAJL3%vL$8Gzu`cf)`J&anLesv$Agp2HF5r=hF!e`rIEwiJvf5V-)SdJAi$k+oY2d(G^XFG>mJ11NgPShcSKF zQ6V|uPSDhJrVrQtC@u|*!tEQG336$sdYvz8Ef>h)h>YjGpeCUv74c#Y(kZWnBbMq5 zyt(4_nH<*81+Max7tRAgBfRc*rY(H93p|{%OP#Ayb0F%J4dMPX6siAD4!m+L+zWGz zy?kLUXT^dAV(9X<>zZn+lC?D}8sXAvQoMsBt}|N@w4QLgAV#KzT(K`_KH~pJ3poV| zUb+hWO^8>@urD>zd7OXLZuMf~ke*87UyDulZ&=VAe3s<i@Y1mVxhoT{0SrMA7l`Q+*NnVc+DwFTy%tzc;L} z5}`uZ@y`68bn0P!t^IT(k?+!RN>71BY?#tpZ*srcpmV`db5u1F`Qm5}J$JI`u7Y3` zJEijD8$MX1Twn=%iRB(ja7YI}_sIZ%sV}vLHr7|I!N0g^tgMy$;C+9mb!Kdf+TVk% z,class_stlpmtx_std::StlNodeAlloc_>_>::push_back +#define PORT_VECTORPUSHBACK 0x800a6ef4 // vector_push_back #define PORT_POSTPROC_DOPOST 0x806b52b4 // WiiPostProc::DoPost #define PORT_MUSICLIBRARYSELECTMAYBE 0x80230d64 // Selects an entry in the Music Library screen - actual name not known #define PORT_GETSYMBOLBYGAMEORIGIN 0x8027dd3c // SongSortByRecent::GetSymbolByGameOrigin @@ -75,6 +79,7 @@ #define PORT_FILESTREAM_CT 0x8034c9f8 // FileStream::__ct (the one that takes a char * path instead of a File object) #define PORT_CHUNKSTREAM_CT 0x8034aa90 // ChunkStream::__ct #define PORT_GETBANDUSERFROMSLOT 0x8010021c // BandUserMgr::GetBandUserFromSlot +#define PORT_OVERSHELLPARTSELECTPROVIDERRELOAD 0x802478a8 // OvershellPartSelectProvider::Reload #define PORT_GETBANDUSERS 0x80100558 // BandUserMgr::GetBandUsers #define PORT_GETSONGSHORTNAME 0x80224edc // MetaPerformer::GetSongShortname(?) - actual name not known #define PORT_GETMETADATA 0x80515510 // BandSongMgr::Data (function renamed from the original name to avoid any confusion with Data.h) @@ -95,8 +100,28 @@ #define PORT_METAMUSICPOLL 0x80678d20 // MetaMusic::Poll #define PORT_METAMUSICSTART 0x80678fa4 // MetaMusic::Start #define PORT_INITSONGMETADATA 0x805147a4 // InitSongMetadata +#define PORT_SONGMETADATACONSTRUCTOR 0x80514880 // SongMetadata::__ct +#define PORT_SONGMETADATALOAD 0x801d2090 // SongMetadata::Load #define PORT_UPDATEPRESENCE 0x801879d4 // PresenceMgr::UpdatePresence #define PORT_STEPSEQUENCEJOBSETSTEP 0x80025364 // Quazal::StepSequenceJob::SetStep +#define PORT_RNDTEXNEWOBJECT 0x80639904 // RndTex::NewObject +#define PORT_RNDMATNEWOBJECT 0x8063996c // RndMat::NewObject +#define PORT_RNDTEXSETBITMAP 0x8063fccc // RndTex::SetBitmap +#define PORT_RNDTEXSETBITMAP2 0x8063f830 // RndTex::SetBitmap2 +#define PORT_RNDTEXSETBITMAP3 0x8063fb2c // RndTex::SetBitmap3 +#define PORT_FILEPATHCONSTRUCTOR 0x8000ec5c // FilePath::__ct +#define PORT_MUSICLIBRARY_CT 0x8022d978 // MusicLibrary::__ct +#define PORT_MUSICLIBRARYMAT 0x80231c5c // MusicLibrary::Mat +#define PORT_NODESORTGETNODE 0x80279314 // MusicLibrary::GetNodeByIndex +#define PORT_GAMEGEMDB_CT 0x80460f64 // GameGemDB::__ct +#define PORT_ADDMULTIGEM 0x80461160 // GameGemDB::AddMultiGem +#define PORT_GETGAMELIST 0x8048553c // SongData::GetGameList +#define PORT_SONGSORTMGRGETSORT 0x80281b20 // SongSortMgr::GetSort +#define PORT_RNDMATSETDIFFUSETEX 0x8025ab90 // RndMat::SetDiffuseTex +#define PORT_DYNAMICTEX_CT 0x80292a70 // DynamicTex::__ct +#define PORT_DYNAMICTEX_DT 0x80292bcc // DynamicTex::__dt +#define PORT_MUSICLIBRARYONENTER 0x8022dd24 // MusicLibrary::OnEnter +#define PORT_MUSICLIBRARYONUNLOAD 0x8022e87c // MusicLibrary::OnExit #define PORT_BINSTREAMWRITE 0x80342f48 // BinStream::Write #define PORT_BINSTREAMREAD 0x80342e7c // BinStream::Read #define PORT_BINSTREAMREADENDIAN 0x803430bc // BinStream::ReadEndian diff --git a/include/ports_wii_bank8.h b/include/ports_wii_bank8.h index 240b3a0..31f646c 100644 --- a/include/ports_wii_bank8.h +++ b/include/ports_wii_bank8.h @@ -31,6 +31,8 @@ // #define PORT_MICCHECK 0x8024a4e8 // NOT NEEDED? a bne that throws an error on the song select screen if the mic is not connected #define PORT_BIGSYMBOLFUNC_TAIL 0x804d3bac // blr after a function that initialises a bunch of symbols #define PORT_UPDATEPRESENCEBLOCK_B 0x8021cbd0 // branch after the failure case in a function that calls UpdatePresence +// FIX ME INLINED? #define PORT_ADDTRACKVECTOR_BL 0x8064e288 // bl to vector_push_back inside of SongData::AddTrack // actually SongDB::AddTrack +#define PORT_GETGAMELIST 0x801dbfa0 // SongData::GetGameList // actually SongDB::GetGems #define PORT_MULTIPLAYER_CRASH 0x80023fec // branch to a function that can crash in online multiplayer #define PORT_MULTIPLAYER_FIX 0x80a2f0c4 // the function that doesn't crash // #define PORT_LOADOBJS_BCTRL 0x827562e4 @@ -47,6 +49,7 @@ #define PORT_EXECUTEDTA 0x803edbd0 // RockCentralGateway::ExecuteConfig #define PORT_BANDLABELSETDISPLAYTEXT 0x80518360 // BandLabel::SetDisplayText #define PORT_SETSONGANDARTISTNAME 0x80254650 // BandLabel::SetSongAndArtistName +#define PORT_SETSONGNAMEFROMNODE 0x80253ec0 // BandLabel::SetSongNameFromNode #define PORT_KEYSONGUITAR 0x8031c1b0 // OvershellPanel::CanGuitarPlayKeys // #define PORT_HMXFACTORYFUNCAT 0x8031b2f8 // FIXME HmxObjectFactoryFunc::_at - inlined in bank8? #define PORT_WILLBENOSTRUM 0x8062b2d0 // GameGemList::WillBeNoStrum @@ -56,6 +59,7 @@ #define PORT_GETWIDGETBYNAME 0x80135f30 // GemManager::GetWidgetByName #define PORT_DATANODEEVALUATE 0x8045e030 // DataNode::Evaluate #define PORT_GETSLOTCOLOR 0x801484d0 // TrackConfig::GetSlotColor +// FIX ME INLINED? #define PORT_ADDSMASHERPLATETOVECTOR 0x80bf0828 // AddSmasherPlateToVector #define PORT_USBWIIGETTYPE 0x809fd840 // UsbWii::GetType #define PORT_FILE_EXISTS 0x804207c0 // FileExists #define PORT_QUEUEMESSAGE 0x80333140 // PassiveMessagesPanel::QueueMessage @@ -69,6 +73,7 @@ // #define PORT_BUILDINSTRUMENTSELECTION 0x802478a8 // FIXME BuildInstrumentSelectionList(?) - actual name not known // #define PORT_PREPARESOMEVECTORMAYBE 0x80247c58 // FIXME Prepares some vector, used by BuildInstrumentSelectionList // #define PORT_SOMEVECTORPUSHBACKMAYBE 0x802484a8 // FIXME vector,class_stlpmtx_std::StlNodeAlloc_>_>::push_back +// FIX ME (?) #define PORT_VECTORPUSHBACK 0x801dced0 // vector_push_back #define PORT_POSTPROC_DOPOST 0x809ef2d0 // WiiPostProc::DoPost #define PORT_MUSICLIBRARYSELECTMAYBE 0x802ff140 // UNSURE MusicLibrary::TryToSetHighlight, Selects an entry in the Music Library screen - actual name not known #define PORT_GETSYMBOLBYGAMEORIGIN 0x80374910 // RecentCmp::RecentTypeToOrigin @@ -77,6 +82,7 @@ #define PORT_FILESTREAM_CT 0x80494fd0 // FileStream::__ct (the one that takes a char * path instead of a File object) #define PORT_CHUNKSTREAM_CT 0x804929a0 // ChunkStream::__ct #define PORT_GETBANDUSERFROMSLOT 0x80168010 // BandUserMgr::GetUserFromSlot +#define PORT_OVERSHELLPARTSELECTPROVIDERRELOAD 0x80322620 // OvershellPartSelectProvider::Reload #define PORT_GETBANDUSERS 0x801683e0 // BandUserMgr::GetBandUsers #define PORT_GETSONGSHORTNAME 0x802f5dd0 // MetaPerformer::GetSongSymbol #define PORT_GETMETADATA 0x80271de0 // BandSongMgr::Data (function renamed from the original name to avoid any confusion with Data.h) @@ -97,8 +103,27 @@ #define PORT_METAMUSICPOLL 0x80997610 // MetaMusic::Poll #define PORT_METAMUSICSTART 0x80997990 // MetaMusic::Start #define PORT_INITSONGMETADATA 0x8075a0c0 // InitSongMetadata +#define PORT_SONGMETADATACONSTRUCTOR 0x8075a1a0 // SongMetadata::__ct +#define PORT_SONGMETADATALOAD 0x8075a910 // SongMetadata::Load #define PORT_UPDATEPRESENCE 0x8021c3d0 // PresenceMgr::UpdatePresence #define PORT_STEPSEQUENCEJOBSETSTEP 0x80035cf0 // Quazal::StepSequenceJob::SetStep +#define PORT_RNDTEXNEWOBJECT 0x80931050 // RndTex::NewObject +#define PORT_RNDMATNEWOBJECT 0x80931010 // RndMat::NewObject +#define PORT_RNDTEXSETBITMAP 0x8093b810 // RndTex::SetBitmap +#define PORT_RNDTEXSETBITMAP2 0x8093b430 // RndTex::SetBitmap2 +#define PORT_RNDTEXSETBITMAP3 0x8093b5b0 // RndTex::SetBitmap3 +#define PORT_FILEPATHCONSTRUCTOR 0x80462460 // FilePath::__ct +#define PORT_MUSICLIBRARY_CT 0x802f9e10 // MusicLibrary::__ct +#define PORT_MUSICLIBRARYMAT 0x80300390 // MusicLibrary::Mat +#define PORT_NODESORTGETNODE 0x8036f5e0 // MusicLibrary::GetNodeByIndex //actually NodeSort::GetNode +#define PORT_GAMEGEMDB_CT 0x80628230 // GameGemDB::__ct +#define PORT_ADDMULTIGEM 0x806284e0 // GameGemDB::AddMultiGem +#define PORT_SONGSORTMGRGETSORT 0x8037a510 // SongSortMgr::GetSort +#define PORT_DYNAMICTEX_CT 0x80393bd0 // DynamicTex::__ct +#define PORT_DYNAMICTEX_DT 0x80393ef0 // DynamicTex::__dt +#define PORT_MUSICLIBRARYONENTER 0x802fa750 // MusicLibrary::OnEnter +#define PORT_MUSICLIBRARYONUNLOAD 0x802fb2a0 // MusicLibrary::OnExit +#define PORT_RNDMATSETDIFFUSETEX 0x808d36b0 // RndMat::SetDiffuseTex #define PORT_BINSTREAMWRITE 0x804892b0 // BinStream::Write #define PORT_BINSTREAMREAD 0x80489140 // BinStream::Read #define PORT_BINSTREAMREADENDIAN 0x804894f0 // BinStream::ReadEndian diff --git a/include/ports_xbox360.h b/include/ports_xbox360.h index 219a4de..2d55bbe 100644 --- a/include/ports_xbox360.h +++ b/include/ports_xbox360.h @@ -51,6 +51,8 @@ #define PORT_LOADOBJS_BCTRL 0x827562e4 // bctrl to Object::PreLoad insie of DirLoader::LoadObjs #define PORT_SONGMGR_ISDEMO_CHECK 0x82575f9c // "bne" after IsUGC check inside SongMgr::IsDemo #define PORT_STAGEKIT_EXISTS 0x8228d03c // StageKit check. nop over to allow for fog command without a physical StageKit connected. +#define PORT_ADDTRACKVECTOR_BL 0x82777b70 // bl to vector_push_back inside of SongData::AddTrack +#define PORT_GETGAMELIST 0x82770730 // SongData::GetGameList #define PORT_VERTEX_READ_1 0x82418704 // call to Vector3::operator>> to read vertex position #define PORT_VERTEX_READ_2 0x82418748 // call to Vector3::operator>> to read vertex normals #define PORT_BANDCHARDESC_MAKEOUTFITPATH 0x82335220 // BandCharDesc::MakeOutfitPath @@ -94,6 +96,7 @@ #define PORT_RANDOMINT 0x824f2f90 // RandomInt(min, max) #define PORT_GETWIDGETBYNAME 0x82b9b880 // GemManager::GetWidgetByName #define PORT_GETSLOTCOLOR 0x82baa308 // TrackConfig::GetSlotColor +#define PORT_ADDSMASHERPLATETOVECTOR 0x82356980 // AddSmasherPlateToVector #define PORT_ARCHIVE_CT 0x82514408 // Archive::_ct #define PORT_ARCHIVE_SETLOCATIONHARDDRIVE 0x82512b00 // Archive::SetLocationHardDrive #define PORT_ARCHIVE_MERGE 0x82513ee8 // Archive::Merge @@ -110,6 +113,7 @@ #define PORT_GAME_CT 0x8267bf30 // Game::__ct #define PORT_GAME_DT 0x8267b1f0 // Game::__dt #define PORT_GAMEGETACTIVEPLAYER 0x82678e88 // Game::GetActivePlayer +#define PORT_OVERSHELLPARTSELECTPROVIDERRELOAD 0x82668c70 // OvershellPartSelectProvider::Reload #define PORT_GETBANDUSERS 0x82683b78 // BandUserMgr::GetBandUsers #define PORT_GETBANDUSERFROMSLOT 0x82682b60 // BandUserMgr::GetBandUserFromSlot #define PORT_GETSONGID 0x827a87f0 // GetSongID, function used when adding songs to BandSongMgr @@ -117,6 +121,7 @@ #define PORT_BUILDINSTRUMENTSELECTION 0x82668c70 // BuildInstrumentSelectionList(?) - actual name not known #define PORT_PREPARESOMEVECTORMAYBE 0x82796d90 // Prepares some vector, used by BuildInstrumentSelectionList #define PORT_SOMEVECTORPUSHBACKMAYBE 0x82b6aa10 // vector,class_stlpmtx_std::StlNodeAlloc_>_>::push_back +#define PORT_VECTORPUSHBACK 0x82b5f808 // vector_push_back #define PORT_POSTPROC_DOPOST 0x82b89a08 // NgPostProc::DoPost #define PORT_MUSICLIBRARYSELECTMAYBE 0x8253EB00 // Selects an entry in the Music Library screen - actual name not known #define PORT_GETSYMBOLBYGAMEORIGIN 0x8265bb78 // SongSortByRecent::GetSymbolByGameOrigin @@ -142,8 +147,27 @@ #define PORT_METAMUSICPOLL 0x82711438 // MetaMusic::Poll #define PORT_METAMUSICSTART 0x82711a00 // MetaMusic::Start #define PORT_INITSONGMETADATA 0x827aa450 // InitSongMetadata +#define PORT_SONGMETADATACONSTRUCTOR 0x827aa6e8 // SongMetadata::__ct +#define PORT_SONGMETADATALOAD 0x825a3f58 // SongMetadata::Load #define PORT_UPDATEPRESENCE 0x82680430 // PresenceMgr::UpdatePresence #define PORT_STEPSEQUENCEJOBSETSTEP 0x82af92b8 // Quazal::StepSequenceJob::SetStep +#define PORT_RNDTEXNEWOBJECT 0x82273de0 // RndTex::NewObject +#define PORT_RNDMATNEWOBJECT 0x8240f5d0 // RndMat::NewObject +#define PORT_RNDTEXSETBITMAP 0x823ff678 // RndTex::SetBitmap +#define PORT_RNDTEXSETBITMAP2 0x823ff240 // RndTex::SetBitmap2 +#define PORT_RNDTEXSETBITMAP3 0x823ff510 // RndTex::SetBitmap3 +#define PORT_FILEPATHCONSTRUCTOR 0x82270210 // FilePath::__ct +#define PORT_MUSICLIBRARY_CT 0x825451c8 // MusicLibrary::__ct +#define PORT_MUSICLIBRARYMAT 0x8253b440 // MusicLibrary::Mat +#define PORT_NODESORTGETNODE 0x825bf708 // MusicLibrary::GetNodeByIndex +#define PORT_GAMEGEMDB_CT 0x827931e0 // GameGemDB::__ct +#define PORT_ADDMULTIGEM 0x827930d8 // GameGemDB::AddMultiGem +#define PORT_SONGSORTMGRGETSORT 0x82595ff8 // SongSortMgr::GetSort +#define PORT_DYNAMICTEX_CT 0x825f2318 // DynamicTex::__ct +#define PORT_DYNAMICTEX_DT 0x825f2210 // DynamicTex::__dt +#define PORT_RNDMATSETDIFFUSETEX 0x8238b130 // RndMat::SetDiffuseTex +#define PORT_MUSICLIBRARYONENTER 0x82542238 // MusicLibrary::OnEnter +#define PORT_MUSICLIBRARYONUNLOAD 0x82540450 // MusicLibrary::OnExit #define PORT_BINSTREAMWRITE 0x827c4f58 // BinStream::Write #define PORT_BINSTREAMREAD 0x827c4ea8 // BinStream::Read #define PORT_BINSTREAMREADENDIAN 0x827c5058 // BinStream::ReadEndian diff --git a/include/rb3/Archive.h b/include/rb3/Archive.h index 7ba2028..f395305 100644 --- a/include/rb3/Archive.h +++ b/include/rb3/Archive.h @@ -8,7 +8,7 @@ typedef struct _Archive } Archive; extern void ArchiveInit(void); -extern void ArchiveConstructor(Archive *thisArchive, char *path, int unknown); +extern void ArchiveConstructor(Archive *thisArchive, char *path, int heap_headroom); extern void ArchiveDestructor(Archive *thisArchive); extern void ArchiveMerge(Archive *thisArchive, Archive *otherArchive); extern void ArchiveSetLocationHardDrive(Archive *thisArchive); diff --git a/include/rb3/BandLabel.h b/include/rb3/BandLabel.h index 2c76a7e..5409ce1 100644 --- a/include/rb3/BandLabel.h +++ b/include/rb3/BandLabel.h @@ -16,6 +16,7 @@ typedef struct _BandLabel extern void SetSongAndArtistName(BandLabel *label, SortNode *unk); extern void SetSongNameFromNode(BandLabel *label, SortNode *unk); -extern void BandLabelSetDisplayText(BandLabel *label, char *text, int unk); +extern void BandLabelSetDisplayText(BandLabel *label, char *text, char clear_token); +extern int MusicLibraryMat(void *thisMusicLibrary, int data, int unk2, int *listSlot); // returns the material for UIListMeshes in the music library #endif // _BANDLABEL_H \ No newline at end of file diff --git a/include/rb3/BandSongMgr.h b/include/rb3/BandSongMgr.h index 3df138c..6ac1327 100644 --- a/include/rb3/BandSongMgr.h +++ b/include/rb3/BandSongMgr.h @@ -11,6 +11,6 @@ typedef struct _BandSongMgr extern int GetSongIDFromShortname(BandSongMgr *thisSongMgr, Symbol shortName, int fail); // fail seems to be unused on retail builds, but it would simulate a failure on debug builds extern SongMetadata *GetMetadata(BandSongMgr *thisSongMgr, int songId); -extern int SongMgrGetRankedSongs(BandSongMgr *thisSongMgr, void *vector, char demosIfGameModeAllows, char includeRestricted); +extern int SongMgrGetRankedSongs(BandSongMgr *thisSongMgr, void *songs, char demosIfGameModeAllows, char includeRestricted); #endif // _BANDSONGMGR_H diff --git a/include/rb3/BandUser.h b/include/rb3/BandUser.h index e311d8c..b3c7720 100644 --- a/include/rb3/BandUser.h +++ b/include/rb3/BandUser.h @@ -1,48 +1,103 @@ #ifndef _BANDUSER_H #define _BANDUSER_H +#include "rb3/String.h" +#include "rb3/Symbol.h" +#include "rb3/Track.h" + typedef enum _Difficulty { - EASY = 0, - MEDIUM = 1, - HARD = 2, - EXPERT = 3 + kDifficultyEasy = 0, + kDifficultyMedium = 1, + kDifficultyHard = 2, + kDifficultyExpert = 3 } Difficulty; typedef enum _TrackType { - BASS = 2, - DRUMS = 0, - GUITAR = 1, - HARMONIES = 7, - KEYS = 4, - PRO_BASS = 8, - PRO_GUITAR = 6, - PRO_KEYS = 5, - VOCALS = 3 + kTrackDrum = 0, + kTrackGuitar = 1, + kTrackBass = 2, + kTrackVocals = 3, + kTrackKeys = 4, + kTrackRealKeys = 5, + kTrackRealGuitar = 6, + kTrackRealGuitar22Fret = 7, + kTrackRealBass = 8, + kTrackRealBass22Fret = 9, + kTrackNone = 10, + kTrackPending = 11, + kTrackPendingVocals = 12 } TrackType; typedef enum _ControllerType { - DRUM_CONT = 0, - GUITAR_CONT = 1, - VOX_CONT = 2, - KEYBOARD_CONT = 3, - PRO_GUITAR_CONT = 4, + kControllerDrum = 0, + kControllerGuitar = 1, + kControllerVocals = 2, + kControllerKeys = 3, + kControllerRealGuitar = 4, + kControllerNone = 5 } ControllerType; +typedef enum _PreferredScoreType +{ + kScoreDrum = 0, + kScoreBass = 1, + kScoreGuitar = 2, + kScoreVocals = 3, + kScoreHarmony = 4, + kScoreKeys = 5, + kScoreRealDrum = 6, + kScoreRealGuitar = 7, + kScoreRealBass = 8, + kScoreRealKeys = 9, + kScoreBand = 10 +} PreferredScoreType; + typedef struct _BandUser { + // BandUser members int unknown_0x0; int unknown_0x4; Difficulty difficulty; - int unknown_0xc; + char mUnknown; + char pad[3]; TrackType trackType; ControllerType controllerType; - int unknown_0x14; - int unknown_0x18; - int unknown_0x1c; - int unknown_0x20; + char mTrackSelected; + char mUnknown2; + char pad2[2]; + PreferredScoreType mPreferredScoreType; + int mOvershellState; + String mOvershellFocus; + void *mChar; +#ifdef RB3E_XBOX + char mGameplayOptions[0x48]; +#else + char mGameplayOptions[0x38]; +#endif + char mAutoplay; + char pad3[3]; +#ifdef RB3E_WII + int mUnknownBool; +#endif + Symbol mPreviousAward; + float mLastHitFraction; + Track *mTrack; + void *mPlayer; + char mParticipating; +#ifdef RB3E_XBOX + char pad4[3]; + int mUnknown3; +#else + char mIsWiiRemoteController; + char mJustDisconnected; + char pad4; +#endif + + // User members + // TODO } BandUser; #endif // _BANDUSER_H \ No newline at end of file diff --git a/include/rb3/BandUserMgr.h b/include/rb3/BandUserMgr.h index 1167524..581b10d 100644 --- a/include/rb3/BandUserMgr.h +++ b/include/rb3/BandUserMgr.h @@ -4,7 +4,7 @@ #include "rb3/BandUser.h" // gives you the number of band users in the current band -extern int GetBandUsers(int thisBandUserMgr, int unknown, int unknown2); +extern int GetBandUsers(int thisBandUserMgr, int pUsers, int flags); // returns the BandUser in a particular overshell slot (overshell slots are left to right, 0-3, so vocals for example is nearly always going to be 2) extern BandUser *GetBandUserFromSlot(int thisBandUserMgr, int slot); diff --git a/include/rb3/BinStream.h b/include/rb3/BinStream.h index bef967a..4b90d2e 100644 --- a/include/rb3/BinStream.h +++ b/include/rb3/BinStream.h @@ -1,6 +1,13 @@ #ifndef _BINSTREAM_H #define _BINSTREAM_H +typedef enum _SeekType +{ + kSeekBegin = 0, + kSeekCur = 1, + kSeekEnd = 2, +} SeekType; + typedef struct _BinStream BinStream; typedef void (*BinStreamDestructor_t)(BinStream *thisBinStream, int unk); diff --git a/include/rb3/ChunkStream.h b/include/rb3/ChunkStream.h index 90962bd..a40a79c 100644 --- a/include/rb3/ChunkStream.h +++ b/include/rb3/ChunkStream.h @@ -1,6 +1,10 @@ #ifndef _CHUNKSTREAM_H #define _CHUNKSTREAM_H +#include "rb3/BinStream.h" +#include "rb3/File.h" +#include "rb3/Platform.h" + typedef struct _ChunkStream ChunkStream; typedef int (*ChunkStreamDestructor_t)(ChunkStream *thisChunkStream, int unk); @@ -9,9 +13,9 @@ typedef int (*ChunkStreamTell_t)(ChunkStream *thisChunkStream); typedef int (*ChunkStreamEOF_t)(ChunkStream *thisChunkStream); typedef int (*ChunkStreamFail_t)(ChunkStream *thisChunkStream); typedef char *(*ChunkStreamName_t)(ChunkStream *thisChunkStream); -typedef char *(*ChunkStreamReadImpl_t)(ChunkStream *thisChunkStream, void *unk, int unk2); -typedef char *(*ChunkStreamWriteImpl_t)(ChunkStream *thisChunkStream, void *unk, int unk2); -typedef char *(*ChunkStreamSeekImpl_t)(ChunkStream *thisChunkStream, int unk, int seekType); +typedef char *(*ChunkStreamReadImpl_t)(ChunkStream *thisChunkStream, void *data, int bytes); +typedef char *(*ChunkStreamWriteImpl_t)(ChunkStream *thisChunkStream, void *data, int bytes); +typedef char *(*ChunkStreamSeekImpl_t)(ChunkStream *thisChunkStream, int offset, SeekType seekType); typedef int (*ChunkStreamReturnsZero_t)(); typedef struct _ChunkStream_vtable @@ -34,6 +38,6 @@ struct _ChunkStream ChunkStream_vtable *vtable; }; -ChunkStream *ChunkStreamConstructor(ChunkStream *thisChunkStream, char *fileName, int fileType, int flags, char unk2, int platform, char unk3); +ChunkStream *ChunkStreamConstructor(ChunkStream *thisChunkStream, char *fileName, int fileType, int chunkSize, char compress, Platform platform, char cached); #endif // _CHUNKSTREAM_H \ No newline at end of file diff --git a/include/rb3/Data.h b/include/rb3/Data.h index 52b36b9..946c59d 100644 --- a/include/rb3/Data.h +++ b/include/rb3/Data.h @@ -63,7 +63,7 @@ typedef struct _DataArray short mNodeCount; short mRefCount; short mLine; - short mUnknown; + short mDeprecated; // assuming this is for them to mark scripts or functions as being deprecated and etc.? } DataArray; // gets a pointer to the addr of a DTA function diff --git a/include/rb3/FilePath.h b/include/rb3/FilePath.h new file mode 100644 index 0000000..e57c02b --- /dev/null +++ b/include/rb3/FilePath.h @@ -0,0 +1,15 @@ +#ifndef _FILEPATH_H +#define _FILEPATH_H + +#include "String.h" + +typedef struct _FilePath +{ + String path; +} FilePath; + +// even though the structure of FilePath is very simplistic we need to use the constructor as it calls +// extra functions to properly construct paths and etc. +FilePath *FilePathConstructor(FilePath *thisFilePath, char *path); + +#endif // _FILEPATH_H \ No newline at end of file diff --git a/include/rb3/FileStream.h b/include/rb3/FileStream.h index eb46211..d159cc2 100644 --- a/include/rb3/FileStream.h +++ b/include/rb3/FileStream.h @@ -1,6 +1,9 @@ #ifndef _FILESTREAM_H #define _FILESTREAM_H +#include "rb3/BinStream.h" +#include "rb3/File.h" + typedef struct _FileStream FileStream; typedef int (*FileStreamDestructor_t)(FileStream *thisFileStream, int unk); @@ -11,7 +14,7 @@ typedef int (*FileStreamFail_t)(FileStream *thisFileStream); typedef char *(*FileStreamName_t)(FileStream *thisFileStream); typedef char *(*FileStreamReadImpl_t)(FileStream *thisFileStream, void *data, int bytes); typedef char *(*FileStreamWriteImpl_t)(FileStream *thisFileStream, void *data, int bytes); -typedef char *(*FileStreamSeekImpl_t)(FileStream *thisFileStream, int offset, int seekType); +typedef char *(*FileStreamSeekImpl_t)(FileStream *thisFileStream, int offset, SeekType seekType); typedef int (*FileStreamReturnsZero_t)(); typedef struct _FileStream_vtable diff --git a/include/rb3/GameGem.h b/include/rb3/GameGem.h index f8af40c..996491d 100644 --- a/include/rb3/GameGem.h +++ b/include/rb3/GameGem.h @@ -1,7 +1,8 @@ #ifndef _GAMEGEM_H #define _GAMEGEM_H -#include "Symbol.h" +#include "rb3/Symbol.h" +#include "rb3/Vector.h" typedef struct _GameGem { @@ -13,6 +14,7 @@ typedef struct _GameGem unsigned char unkBitfield1; unsigned char unkBitfield2; unsigned char unkBitfield3; + char unk1 : 1; char unk2 : 1; char unk3 : 1; @@ -24,18 +26,16 @@ typedef struct _GameGem char red : 1; char green : 1; - char gemSeen : 1; // Updated in real-time once the player has seen this gem and either hit or missed it - char isHopo : 1; // Does nothing on the first note of the song even if set - char normalNote1 : 1; // Unsure what these both represent but sustains have them set to 0 and setting them to 0 forces a sustain - char normalNote2 : 1; - char unk5 : 1; - char unk6 : 1; - char unk7 : 1; - char unk8 : 1; - - // Unsure about these two - unsigned char unknown; - int unknown2; + char isPlayed : 1; // Updated in real-time once the player has seen this gem and either hit or missed it + char isHopo : 1; // Does nothing on the first note of the song even if set + char ignoreDuration : 1; + char isCymbal : 1; + char showChordNames : 1; + char showSlashes : 1; + char useAltDuration : 1; + char autoPlayable : 1; + unsigned char playableBy; + char isRealGuitar; // There is more after this that I am too lazy to map. Probably pro instrument stuff } GameGem; @@ -47,9 +47,39 @@ typedef enum _NoStrumState kStrumDefault = 2 } NoStrumState; -extern int WillBeNoStrum(int *gameGemListPtr, int *multiGemInfoPtr); -extern int AddGameGem(int *gameGemList, GameGem *gem, NoStrumState gemType); +typedef struct _GameGemList +{ + int mHopoThreshold; + vector mGems; +} GameGemList; + +typedef struct _GameGemDB +{ + vector mGems; + int mHopoThreshold; +} GameGemDB; + +typedef struct _MultiGemInfo +{ + int track; + int slots; + float ms; + float duration_ms; + int tick; + int duration_ticks; + char ignore_duration; + char is_cymbal; + char pad[2]; + int players; + NoStrumState no_strum; +} MultiGemInfo; + +extern int WillBeNoStrum(GameGemList *thisGameGemList, int *multiGemInfoPtr); +extern int AddGameGem(GameGemList *gameGemList, GameGem *gem, NoStrumState gemType); +extern char AddMultiGem(GameGemDB *this, int diff, MultiGemInfo *info); extern int *GetWidgetByName(int *gemManager, Symbol sym); -extern Symbol GetSlotColor(int *bandUser); +extern Symbol GetSlotColor(int *bandUser, int slot); +extern GameGemDB *GameGemDBConstructor(GameGemDB *thisGameGemDB, int num_difficulties, int hopo_threshold); +extern GameGemList *GetGameGemList(void *songData, int unk); #endif // _GAMEGEM_H \ No newline at end of file diff --git a/include/rb3/GemTrackResrcManager.h b/include/rb3/GemTrackResrcManager.h new file mode 100644 index 0000000..878a104 --- /dev/null +++ b/include/rb3/GemTrackResrcManager.h @@ -0,0 +1,31 @@ +#ifndef _GEMTRACKRESOURCEMANAGER_H +#define _GEMTRACKRESOURCEMANAGER_H + +#include "rb3/Object.h" + +typedef enum _SmasherPlateType +{ + kPlateGuitar = 0, + kPlateDrums = 1, + kPlateBass = 2, + // 3 seems to be unused + kPlateKeys = 4, + kPlateRealGuitar = 5, + kPlateRealBass = 6, + kPlateRealKeys = 7 + +} SmasherPlateType; + +typedef struct _SmasherPlate +{ + void *vtable; + void *mResourceManager; + Object *mSmasherPlateObject; // not sure what this type of object this actually is so just use generic obj for now + SmasherPlateType mSmasherType; + char mUsed; + char pad[3]; +} SmasherPlate; + +void AddSmasherPlateToVector(void *smasherPlates, SmasherPlate *plate); + +#endif // _GEMTRACKRESOURCEMANAGER_H diff --git a/include/rb3/List.h b/include/rb3/List.h new file mode 100644 index 0000000..55a4fd4 --- /dev/null +++ b/include/rb3/List.h @@ -0,0 +1,13 @@ +#ifndef _LIST_H +#define _LIST_H + +// basic list type +// used constantly across the code +// in C++ it would be used like list or etc. +typedef struct _list +{ + void *next; + void *prev; +} list; + +#endif // _LIST_H \ No newline at end of file diff --git a/include/rb3/MusicLibrary.h b/include/rb3/MusicLibrary.h index ac6d99c..a58cfa7 100644 --- a/include/rb3/MusicLibrary.h +++ b/include/rb3/MusicLibrary.h @@ -3,7 +3,23 @@ #include "Symbol.h" +typedef struct _MusicLibrary +{ +#ifdef RB3E_XBOX + char unk[0xfc]; +#else + char unk[0xdc]; +#endif + int mSortType; +} MusicLibrary; + // Jumps to a given entry in the music library void MusicLibrarySelect(int theMusicLibrary, Symbol entryName, int sortType, int unk_r6); +int *MusicLibraryConstructor(int *thisMusicLibrary, int *songPreview); +int *MusicLibraryConstructorHook(int *thisMusicLibrary, int *songPreview); +void MusicLibraryOnEnter(void* thisMusicLibrary); +void MusicLibraryOnUnload(void* thisMusicLibrary); +void MusicLibraryOnEnterHook(void* thisMusicLibrary); +void MusicLibraryOnUnloadHook(void* thisMusicLibrary); #endif // _MUSICLIBRARY_H_ diff --git a/include/rb3/NodeSort.h b/include/rb3/NodeSort.h new file mode 100644 index 0000000..c7c3b64 --- /dev/null +++ b/include/rb3/NodeSort.h @@ -0,0 +1,22 @@ +#ifndef _NODESORT_H +#define _NODESORT_H + +#include "rb3/List.h" +#include "rb3/Object.h" +#include "rb3/SortNode.h" +#include "rb3/Vector.h" + +// very clear naming here. this couldn't possibly have been confusing during development +typedef struct _NodeSort +{ + void *mListProvider; + Object mObject; + vector mTree; + list mFunctions; + vector mList; + Symbol mSortName; +} NodeSort; + +SortNode *NodeSortGetNode(NodeSort *thisNodeSort, int idx); + +#endif // _NODESORT_H \ No newline at end of file diff --git a/include/rb3/Object.h b/include/rb3/Object.h index 99e5a28..1aa2adf 100644 --- a/include/rb3/Object.h +++ b/include/rb3/Object.h @@ -6,6 +6,13 @@ #include "rb3/Symbol.h" #include "rb3/UI/UIPanel.h" +typedef enum _CopyType +{ + kCopyDeep = 0, + kCopyShallow = 1, + kCopyFromMax = 2 +} CopyType; + typedef struct _Object Object; typedef struct _TypeProps TypeProps; @@ -14,18 +21,18 @@ typedef void (*ObjOnlyReturns_t)(); typedef int (*ObjReturnsZero_t)(); typedef Symbol (*ObjClassName_t)(Symbol retSym); typedef void (*ObjSetType_t)(Object *thisObject, Symbol type); -typedef DataNode *(*ObjHandle_t)(DataNode *retNode, Object *thisObject, DataArray *msg, char unk); +typedef DataNode *(*ObjHandle_t)(DataNode *retNode, Object *thisObject, DataArray *msg, char warn); typedef void (*ObjSyncProperty_t)(Object *thisObject); typedef void (*ObjSave_t)(Object *thisObject, BinStream *binStream); -typedef void (*ObjCopy_t)(Object *to, Object *from, int copyType); +typedef void (*ObjCopy_t)(Object *to, Object *from, CopyType copyType); typedef void (*ObjLoad_t)(Object *thisObject, BinStream *binStream); typedef void (*ObjSetTypeDef_t)(Object *thisObject, DataArray *typeDef); -typedef void (*ObjSetName_t)(Object *thisObject, Symbol name, int *parentDir); +typedef void (*ObjSetName_t)(Object *thisObject, char *name, int *dir); typedef int *(*ObjDataDir_t)(Object *thisObject); typedef void (*ObjPreLoad_t)(Object *thisObject, BinStream *binStream); typedef char *(*ObjFindPathName_t)(Object *thisObject); -extern UIPanel *ObjectFindUIPanel(int *objectDir, char *name, int unk); +extern UIPanel *ObjectFindUIPanel(int *objectDir, char *name, char fail); typedef struct _Object_vtable { @@ -77,6 +84,9 @@ struct _Object char *note; char *name; + int *objectDir; + int *objRefHead; + int *objRefTail; }; #endif // _OBJECT_H \ No newline at end of file diff --git a/include/rb3/PassiveMessagesPanel.h b/include/rb3/PassiveMessagesPanel.h index e50e6cd..14a9486 100644 --- a/include/rb3/PassiveMessagesPanel.h +++ b/include/rb3/PassiveMessagesPanel.h @@ -6,6 +6,6 @@ // RB3E function, not a game function, doesn't exactly belong here extern void DisplayMessage(char *message); -void QueueMessage(int *thisPassiveMessagesPanel, DataArray *ptr, int type, char *symText, int unk3); +void QueueMessage(int *thisPassiveMessagesPanel, DataArray *ptr, int type, char *symText, int priority); #endif // _PASSIVEMESSAGESPANEL_H \ No newline at end of file diff --git a/include/rb3/Platform.h b/include/rb3/Platform.h new file mode 100644 index 0000000..6b886c5 --- /dev/null +++ b/include/rb3/Platform.h @@ -0,0 +1,13 @@ +#ifndef _PLATFORM_H +#define _PLATFORM_H + +typedef enum _Platform +{ + kPlatformNone = 0, + kPlatformXbox = 2, + kPlatformPC = 3, + kPlatformPS3 = 4, + kPlatformWii = 5 +} Platform; + +#endif // _PLATFORM_H \ No newline at end of file diff --git a/include/rb3/Quazal/Step.h b/include/rb3/Quazal/Step.h new file mode 100644 index 0000000..c6aae4b --- /dev/null +++ b/include/rb3/Quazal/Step.h @@ -0,0 +1,14 @@ +#ifndef _STEP_H +#define _STEP_H + +typedef struct _Step +{ + void *m_pStepMethod; // the function that gets called when the job is ran + int unk; +#ifdef RB3E_WII + int unk2; // field only appears to exist on Wii +#endif + char *m_szStepDescription; +} Step; + +#endif // _STEP_H \ No newline at end of file diff --git a/include/rb3/Rnd/DynamicTex.h b/include/rb3/Rnd/DynamicTex.h new file mode 100644 index 0000000..352057c --- /dev/null +++ b/include/rb3/Rnd/DynamicTex.h @@ -0,0 +1,22 @@ +#ifndef _DYNAMIC_TEX_H +#define _DYNAMIC_TEX_H + +#include "rb3/String.h" +#include "rb3/Rnd/RndMat.h" + +typedef struct _DynamicTex +{ + int * vtable; // 0x0 + RndTex * mTex; // 0x4 + String mMatName; // 0x8 + RndMat* mMat; // 0x14 + void* mFileLoader; // 0x18 + char unk; // 0x1C +} DynamicTex; + +void DynamicTexConstructor(DynamicTex* thisDynamicTex, const char * path, const char* matName, char createNewMat, char enableZBuffer); +void DynamicTexDestructor(DynamicTex* thisDynamicTex, int unk); + + + +#endif // _DYNAMIC_TEX_H diff --git a/include/rb3/Rnd/RndAnimatable.h b/include/rb3/Rnd/RndAnimatable.h new file mode 100644 index 0000000..eee94f9 --- /dev/null +++ b/include/rb3/Rnd/RndAnimatable.h @@ -0,0 +1,14 @@ +#ifndef _RNDANIMATABLE_H +#define _RNDANIMATABLE_H + +#include "rb3/Object.h" + +typedef struct _RndAnimatable +{ + void *vtable; + void *objectVtable; + float mFrame; + int mRate; +} RndAnimatable; + +#endif // _RNDANIMATABLE_H \ No newline at end of file diff --git a/include/rb3/Rnd/RndMat.h b/include/rb3/Rnd/RndMat.h new file mode 100644 index 0000000..d2a0ac9 --- /dev/null +++ b/include/rb3/Rnd/RndMat.h @@ -0,0 +1,93 @@ +#ifndef _RNDMAT_H +#define _RNDMAT_H + +#include "rb3/Rnd/RndTex.h" +#include "rb3/Rnd/Transform.h" +#include "rb3/Object.h" + +typedef struct _Color +{ + float r; + float g; + float b; + float a; +} Color; + +typedef enum _BlendMode +{ + kDest = 0, + kSrc = 1, + kAdd = 2, + kSrcAlpha = 3, + kSrcAlphaAdd = 4, + kSubtract = 5, + kMultiply = 6, + kPreMultAlpha = 7 +} BlendMode; + +typedef enum _ZMode +{ + kDisable = 0, + kNormal = 1, + kTransparent = 2, + kForce = 3, + kDecal = 4 +} ZMode; + +typedef enum _StencilMode +{ + kStencilIgnore = 0, + kStencilWrite = 1, + kStencilTest = 2 +} StencilMode; + +typedef enum _TexGen +{ + kNone = 0, + kXfm = 1, + kSphere = 2, + kProjected = 3, + kXfmOrigin = 4, + kEnviron = 5 +} TexGen; + +typedef enum _TexWrap +{ + kClamp = 0, + kRepear = 1, + kBorderBlack = 2, + kBorderWhite = 3, + kMirror = 4 +} TexWrap; + +typedef struct _RndMat +{ + Object object; + BlendMode blend; + Color diffuseColor; + ZMode zMode; + StencilMode stencilMode; + TexGen texGen; + TexWrap texWrap; + Transform texXfm; + ObjPtr_RndTex diffuseTex; + char intensify; + char useEnviron; + char preLit; + char alphaCut; + char alphaWrite; + char pad3[0x3]; + int alphaThreshold; + ObjPtr_RndTex nextPass; + float emissiveMultiplier; + Color specularColor; + Color specularColor2; + ObjPtr_RndTex normalMap; + ObjPtr_RndTex emissiveMap; + ObjPtr_RndTex specularMap; +} RndMat; + +RndMat *RndMatNewObject(); +RndMat* RndMatSetDiffuseTex(RndMat *thisRndMat, RndTex *tex); + +#endif // _RNDMAT_H \ No newline at end of file diff --git a/include/rb3/Rnd/RndPropAnim.h b/include/rb3/Rnd/RndPropAnim.h index 150a43d..7532c73 100644 --- a/include/rb3/Rnd/RndPropAnim.h +++ b/include/rb3/Rnd/RndPropAnim.h @@ -1,23 +1,22 @@ #ifndef _RNDPROPANIM_H #define _RNDPROPANIM_H +#include "rb3/List.h" #include "rb3/Object.h" +#include "rb3/Rnd/RndAnimatable.h" typedef struct _RndPropAnim { - int *vtable; // 0x0 - int unk; // 0x04 - int unk2; // 0x08 - int unk3; // 0x0c - int unk4; // 0x10 - int unk5; // 0x14 - int unk6; // 0x18 - int unk7; // 0x1c + RndAnimatable mAnimatable; + list mPropKeys; + float mLastFrame; + bool mInSetFrame; + bool unk; + char pad[2]; #ifdef RB3E_XBOX - int unk8; + int unk2; #endif - Object object; // 0x20 - + Object mObject; } RndPropAnim; void PropAnimSetFrame(RndPropAnim *rndPropAnim, float frame, float time); diff --git a/include/rb3/Rnd/RndTex.h b/include/rb3/Rnd/RndTex.h new file mode 100644 index 0000000..ed46a9d --- /dev/null +++ b/include/rb3/Rnd/RndTex.h @@ -0,0 +1,53 @@ +#ifndef _RNDTEX_H +#define _RNDTEX_H + +#include "rb3/FilePath.h" +#include "rb3/Object.h" +#include "rb3/String.h" + +typedef enum _BitmapType +{ + RGBA = 3, + DXT1 = 8, + DXT5 = 24, + ATI2 = 32, + TPL_CMP = 72, + TPL_CMP_ALPHA = 328, +} BitmapType; + +typedef struct _RndBitmap +{ + char pad[0x1c]; + +} RndBitmap; + +typedef struct _RndTex +{ + Object object; + RndBitmap bitmap; + float mipMapK; + int type; + int width; + int height; + int bpp; + String name; + int mipLevels; + int *fileLoader; + int unk2; +} RndTex; + +typedef struct _ObjPtr_RndTex +{ + int *vtable; + Object *object; + RndTex *tex; +} ObjPtr_RndTex; + +RndTex *RndTexNewObject(); +void RndTexSetBitmap(RndTex *thisRndTex, FilePath *path); +void RndTexSetBitmap2(RndTex *thisRndTex, int width, int height, int bpp, int type, int useMips, char *path); +void RndTexSetBitmap3(RndTex *thisRndTex, void* fileLoader); + +void RndBitmapGenerateMips(RndBitmap *thisBitmap); +int RndBitmapNumMips(RndBitmap *thisBitmap); +#endif // _RNDTEX_H \ No newline at end of file diff --git a/include/rb3/Rnd/Transform.h b/include/rb3/Rnd/Transform.h new file mode 100644 index 0000000..9fe1d26 --- /dev/null +++ b/include/rb3/Rnd/Transform.h @@ -0,0 +1,24 @@ +#ifndef _XFM_H +#define _XFM_H + +typedef struct _Vector3 +{ + float x; + float y; + float z; + float w; // not sure what this is? its not defined in RB3 symbols but is definitely in the structure +} Vector3; + +typedef struct _Matrix3 +{ + Vector3 x; + Vector3 y; + Vector3 z; +} Matrix3; + +typedef struct _Transform +{ + Matrix3 m; + Vector3 v; +} Transform; +#endif // _XFM_H \ No newline at end of file diff --git a/include/rb3/SongMetadata.h b/include/rb3/SongMetadata.h index 9cfa48a..d3ae181 100644 --- a/include/rb3/SongMetadata.h +++ b/include/rb3/SongMetadata.h @@ -1,6 +1,8 @@ #ifndef _SONGMETADATA_H #define _SONGMETADATA_H +#include "rb3/BinStream.h" +#include "rb3/Data.h" #include "String.h" #include "Symbol.h" @@ -33,5 +35,7 @@ typedef struct _SongMetadata } SongMetadata; extern SongMetadata *InitSongMetadata(SongMetadata *data); +extern SongMetadata *SongMetadataConstructor(SongMetadata *thisSongMetadata, DataArray *data, DataArray *backupData, char isOnDisc); +extern char SongMetadataLoad(SongMetadata *thisSongMetadata, BinStream *stream); #endif // _SONGMETADATA_H diff --git a/include/rb3/SongSortMgr.h b/include/rb3/SongSortMgr.h new file mode 100644 index 0000000..2229255 --- /dev/null +++ b/include/rb3/SongSortMgr.h @@ -0,0 +1,18 @@ +#ifndef _SONGSORTMGR_H +#define _SONGSORTMGR_H + +#include "rb3/NodeSort.h" + +typedef struct _SongSortMgr +{ +#ifdef RB3E_XBOX + char pad[0x4c]; +#else + char pad[0x44]; +#endif + NodeSort *mNodeSort[11]; // NodeSort *[11] in debug symbols, depends on the number of sorting types, retail might have more +} SongSortMgr; + +NodeSort *SongSortMgrGetSort(SongSortMgr *thisSongSortMgr, int sortType); + +#endif // _SONGSORTMGR_H diff --git a/include/rb3/SortNode.h b/include/rb3/SortNode.h index 0a5a0db..73ed37f 100644 --- a/include/rb3/SortNode.h +++ b/include/rb3/SortNode.h @@ -1,8 +1,26 @@ #ifndef _SORTNODE_H #define _SORTNODE_H +#include "Object.h" +#include "Symbol.h" #include "SongMetadata.h" +typedef struct _SortNode SortNode; +typedef struct _ShortcutNodeVtable ShortcutNodeVtable; +typedef struct _SongRecord SongRecord; + +typedef enum _SongNodeType +{ + kNodeNone = 0, + kNodeShortcut = 1, + kNodeHeader = 2, + kNodeSubheader = 3, + kNodeSong = 4, + kNodeFunction = 5, + kNodeSetlist = 6, + kNodeStoreSong = 7 +} SongNodeType; + typedef struct _Unknown2 { #ifdef RB3E_WII @@ -13,14 +31,50 @@ typedef struct _Unknown2 SongMetadata *metaData; } Unknown2; -typedef struct _SortNode +struct _SortNode { + ShortcutNodeVtable *vtable; #ifdef RB3E_WII - char something[0x34]; + char something[0x30]; #else - char something[0x40]; + char something[0x3c]; #endif - Unknown2 *somethingElse; -} SortNode; + SongRecord *record; +}; + +typedef int (*ReturnsZero_t)(); +typedef void (*OnlyReturns_t)(); +typedef SongNodeType (*GetNodeType_t)(); +typedef Symbol (*GetToken_t)(); +typedef int (*LocalizeToken_t)(SortNode *thisSortNode); +typedef Symbol (*OnSelect_t)(SortNode *thisSortNode); +typedef Symbol (*Select_t)(SortNode *thisSortNode); +typedef int *(*GetDateTime_t)(SortNode *thisSortNode); + +typedef struct _ShortcutNodeVtable +{ + Object_vtable objectVtable; + GetNodeType_t getNodeType; + GetToken_t getToken; + LocalizeToken_t localizeToken; + GetDateTime_t getDateTime; + OnlyReturns_t deleteAll; + OnSelect_t onSelect; + Select_t select; + OnlyReturns_t onlyReturns2; + // there is more here but not really relevant atm + +} ShortcutNodeVtable; + +typedef struct _SongRecord +{ + int *vtable; +#ifdef RB3E_WII + char unknown[0xf8]; +#else + char unknown[0x104]; +#endif + SongMetadata *metaData; +} SongRecord; #endif // _SORTNODE_H \ No newline at end of file diff --git a/include/rb3/String.h b/include/rb3/String.h index b1973f3..7c3e5b2 100644 --- a/include/rb3/String.h +++ b/include/rb3/String.h @@ -8,4 +8,6 @@ typedef struct _String char *buf; } String; +int StringConstructor(int *thisFactory, String *str); + #endif // _STRING_H \ No newline at end of file diff --git a/include/rb3/Track.h b/include/rb3/Track.h new file mode 100644 index 0000000..fa049a2 --- /dev/null +++ b/include/rb3/Track.h @@ -0,0 +1,42 @@ +#ifndef _TRACK_H +#define _TRACK_H + +#include "rb3/BandUser.h" +#include "rb3/Object.h" +#include "rb3/Symbol.h" +#include "rb3/Vector.h" + +typedef struct _TrackConfig +{ + int *mUser; + char kDualPerspective; + char pad[3]; + int mTrackNum; + int mMaxSlots; + int mNumSlots; + Symbol mName; + char mLefty; + char pad2[3]; + unsigned int mGameCymbalLanes; + char mDisableHopos; + char pad3[3]; + vector mSlotSpacing; +} TrackConfig; + +typedef struct _Track +{ + Object mTrackInterface; + TrackConfig mConfig; + float mLastRating; + int mLastRatingState; + char mUnk; // this is new to this structure, not sure what it does + char pad[3]; + int mLastStreakCount; + int mSlotIndex; + void *mGameplayOptions; + char mIntroPlaying; + char pad2[3]; + float mIntroEndMs; +} Track; + +#endif // _TRACK_H diff --git a/include/rb3/TrackWatcher.h b/include/rb3/TrackWatcher.h new file mode 100644 index 0000000..53bb6c1 --- /dev/null +++ b/include/rb3/TrackWatcher.h @@ -0,0 +1,22 @@ +#ifndef _TRACKWATCHER_H +#define _TRACKWATCHER_H + +#include "rb3/Data.h" +#include "rb3/Vector.h" + +typedef struct _TrackWatcher +{ + void *mImpl; + vector mSinks; + int mTrack; + char mIndependentSlots; + char pad[3]; + int mUserGuid[4]; + void *mPlayerSlot; + Symbol mControllerType; + void *mSongData; + void *mTrackWatcherParent; + DataArray *mCfg; +} TrackWatcher; + +#endif // _TRACKWATCHER_H \ No newline at end of file diff --git a/include/rb3/UI/UIColor.h b/include/rb3/UI/UIColor.h new file mode 100644 index 0000000..5fc5aa4 --- /dev/null +++ b/include/rb3/UI/UIColor.h @@ -0,0 +1,22 @@ +#ifndef _UICOLOR_H +#define _UICOLOR_H + +#include "rb3/Object.h" + +typedef struct _UIColor +{ + Object Object; + float mRed; + float mGreen; + float mBlue; + float mAlpha; +} UIColor; + +typedef struct _ObjPtr_UIColor +{ + int *vtable; + Object *object; + UIColor *color; +} ObjPtr_UIColor; + +#endif \ No newline at end of file diff --git a/include/rb3/UI/UIListSlot.h b/include/rb3/UI/UIListSlot.h new file mode 100644 index 0000000..5a0fdd9 --- /dev/null +++ b/include/rb3/UI/UIListSlot.h @@ -0,0 +1,43 @@ +#ifndef _UILISTSLOT_H +#define _UILISTSLOT_H + +#include "rb3/Object.h" +#include "rb3/String.h" +#include "rb3/Vector.h" +#include "rb3/UI/UIColor.h" + +typedef enum _UIListWidgetDrawType +{ + kUIListWidgetDrawAlways = 0, + kUIListWidgetDrawOnlyFocused = 1, + kUIListWidgetDrawNever = 2 +} UIListWidgetDrawType; + +typedef enum _UIListSlotDrawType +{ + kUIListSlotDrawAlways = 0, + kUIListSlotDrawHighlight = 1, + kUIListSlotDrawNoHighlight = 2 +} UIListSlotDrawType; + +typedef struct _UIListWidget +{ + Object mObject; + int mUnk; + float mDrawOrder; + ObjPtr_UIColor mDefaultColor; + vector mColors; + UIListWidgetDrawType mWidgetDrawType; + void *mParentList; +} UIListWidget; + +typedef struct _UIListSlot +{ + UIListWidget mWidget; + vector mElements; + UIListSlotDrawType mSlotDrawType; + int mNextElement; + String mMatchName; +} UIListSlot; + +#endif // _UILISTSLOT_H \ No newline at end of file diff --git a/include/rb3/Vector.h b/include/rb3/Vector.h index 4d778fd..8dce691 100644 --- a/include/rb3/Vector.h +++ b/include/rb3/Vector.h @@ -1,25 +1,23 @@ #ifndef _VECTOR_H #define _VECTOR_H -typedef struct _Vector2 +// basic vector type +// used constantly across the code +// in C++ it would be used like vector or etc. +typedef struct _vector { - float x; - float y; -} Vector2; + // structure of a vector differs on 360 vs Wii +#ifdef RB3E_XBOX + void *begin; + void *end; + void *capacity; +#else + void *data; + short count; + short capacity; +#endif +} vector; -typedef struct _Vector3 -{ - float x; - float y; - float z; -} Vector3; - -typedef struct _Vector4 -{ - float x; - float y; - float z; - float w; -} Vector4; +void vector_push_back(vector *v, void *item); #endif // _VECTOR_H \ No newline at end of file diff --git a/include/rb3_include.h b/include/rb3_include.h index 444721a..c66ebb1 100644 --- a/include/rb3_include.h +++ b/include/rb3_include.h @@ -15,15 +15,18 @@ #include "rb3/Data.h" #include "rb3/DirLoader.h" #include "rb3/File.h" +#include "rb3/FilePath.h" #include "rb3/FileStream.h" #include "rb3/Game.h" #include "rb3/GameGem.h" +#include "rb3/GemTrackResrcManager.h" #include "rb3/Joypad.h" #include "rb3/Locale.h" #include "rb3/Mem.h" #include "rb3/MetaPerformer.h" #include "rb3/ModifierManager.h" #include "rb3/MusicLibrary.h" +#include "rb3/NodeSort.h" #include "rb3/Object.h" #include "rb3/PassiveMessagesPanel.h" #include "rb3/PresenceMgr.h" @@ -32,6 +35,7 @@ #include "rb3/SongMetadata.h" #include "rb3/SongParser.h" #include "rb3/SongSortByRecentEntry.h" +#include "rb3/SongSortMgr.h" #include "rb3/SortNode.h" #include "rb3/String.h" #include "rb3/Symbol.h" @@ -43,7 +47,10 @@ #include "quazal/QuazalSocket.h" // rnd headers +#include "rb3/Rnd/DynamicTex.h" #include "rb3/Rnd/RndPropAnim.h" +#include "rb3/Rnd/RndMat.h" +#include "rb3/Rnd/RndTex.h" // UI headers #include "rb3/UI/UIPanel.h" diff --git a/source/GemHooks.c b/source/GemHooks.c index d2cf90e..f8f08ba 100644 --- a/source/GemHooks.c +++ b/source/GemHooks.c @@ -5,7 +5,7 @@ #include "rb3/Random.h" #include "rb3/Symbol.h" -int WillBeNoStrumHook(int *gameGemListPtr, int *multiGemInfoPtr) +int WillBeNoStrumHook(GameGemList *thisGameGemList, int *gem) { Modifier *forceHoposModifier; @@ -16,7 +16,7 @@ int WillBeNoStrumHook(int *gameGemListPtr, int *multiGemInfoPtr) } else { - return WillBeNoStrum(gameGemListPtr, multiGemInfoPtr); + return WillBeNoStrum(thisGameGemList, gem); } } @@ -62,12 +62,12 @@ int *GetWidgetByNameHook(int *gemManager, Symbol sym) } } -Symbol GetSlotColorHook(int *bandUser) +Symbol GetSlotColorHook(int *bandUser, int slot) { Modifier *colorShuffleModifier; Symbol *colors[5] = {&globalSymbols.green, &globalSymbols.red, &globalSymbols.yellow, &globalSymbols.blue, &globalSymbols.orange}; - Symbol slotColor = GetSlotColor(bandUser); + Symbol slotColor = GetSlotColor(bandUser, slot); colorShuffleModifier = ModifierActive(*(int *)PORT_MODIFIERMGR_POINTER, globalSymbols.colorShuffle, 0); @@ -120,7 +120,7 @@ void shuffleColors(GameGem *gem) gem->orange = colors[4]; } -int AddGameGemHook(void *gameGemList, GameGem *gem, NoStrumState gemType) +int AddGameGemHook(GameGemList *gameGemList, GameGem *gem, NoStrumState gemType) { Modifier *mirrorModeModifier; Modifier *gemShuffleModifier; diff --git a/source/LocaleHooks.c b/source/LocaleHooks.c index 8b3c3ca..e610ec8 100644 --- a/source/LocaleHooks.c +++ b/source/LocaleHooks.c @@ -73,9 +73,12 @@ char *LocalizeHook(int thisLocale, Symbol sym, int fail) char newLocaleName[0x50]; Symbol newLocale; char *original; + + // No longer needed with origin rewrite // game origin icons relies on using different formatting for the song/artist name - if (config.GameOriginIcons == 1 && sym.sym != NULL && strcmp(sym.sym, "song_artist_fmt") == 0) - return "%s %s"; + //if (config.GameOriginIcons == 1 && sym.sym != NULL && strcmp(sym.sym, "song_artist_fmt") == 0) + // return "%s %s"; + // if the string starts with message_motd_ (but isn't message_motd), it's our motd if (memcmp(sym.sym, "message_motd_", 13) == 0) { diff --git a/source/MiloSceneHooks.c b/source/MiloSceneHooks.c index ac18ee3..285d158 100644 --- a/source/MiloSceneHooks.c +++ b/source/MiloSceneHooks.c @@ -339,7 +339,7 @@ void* DirLoaderLoadObjsHook(String * filePath, int unk, int unk2) SymbolConstruct(&newPrefab->outfit.parts[0].mName, prefabNameStr); // set the name and add to the directory - rawObject->table->setName(rawObject, prefabNameSym, prefabDir); + rawObject->table->setName(rawObject, prefabNameSym.sym, prefabDir); } // free the file list since we don't need it anymore diff --git a/source/OvershellHooks.c b/source/OvershellHooks.c index 02f2fad..e2363b4 100644 --- a/source/OvershellHooks.c +++ b/source/OvershellHooks.c @@ -10,7 +10,7 @@ #include "ports.h" // MSVC inlined this function in the original game so redefine it -void AddInstrumentToList(OvershellSlot *thisOvershellSlot, Symbol *instrumentToAdd, TrackType trackType, char *icon) +void OvershellPartSelectProviderAddPart(OvershellSlot *thisOvershellSlot, Symbol *instrumentToAdd, TrackType trackType, char *icon) { OvershellListEntry entry; entry.icon = icon[0]; @@ -21,7 +21,7 @@ void AddInstrumentToList(OvershellSlot *thisOvershellSlot, Symbol *instrumentToA } // Monkey patching the function will be hard, so make a realistic, dark and gritty reboot. -void BuildInstrumentSelectionList(OvershellSlot *thisOvershellSlot, ControllerType controllerType, BandUser *bandUser) +void OvershellPartSelectProviderReload(OvershellSlot *thisOvershellSlot, ControllerType controllerType, BandUser *bandUser) { PrepareSomeVectorMaybe(&thisOvershellSlot->list_vector_maybe, thisOvershellSlot->list_vector_maybe, thisOvershellSlot->unk_var_2); thisOvershellSlot->controllerType = controllerType; @@ -29,36 +29,36 @@ void BuildInstrumentSelectionList(OvershellSlot *thisOvershellSlot, ControllerTy // RB3E_DEBUG("Building overshell selection for controller type %i.", controllerType); switch (controllerType) { - case VOX_CONT: - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_vocal_solo, VOCALS, "V"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_vocal_harmony, HARMONIES, "2"); + case kControllerVocals: + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_vocal_solo, kTrackVocals, "V"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_vocal_harmony, kTrackVocals, "2"); // allow pad to play as guitar, bass, keys and drums :D - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_guitar, GUITAR, "G"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_bass, BASS, "B"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_keys, KEYS, "K"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_drums, DRUMS, "D"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_guitar, kTrackGuitar, "G"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_bass, kTrackBass, "B"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_keys, kTrackKeys, "K"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_drums, kTrackDrum, "D"); break; - case GUITAR_CONT: - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_guitar, GUITAR, "G"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_bass, BASS, "B"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_keys, KEYS, "K"); + case kControllerGuitar: + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_guitar, kTrackGuitar, "G"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_bass, kTrackBass, "B"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_keys, kTrackKeys, "K"); break; - case DRUM_CONT: - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_drums, DRUMS, "D"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_drums_pro, DRUMS, "d"); + case kControllerDrum: + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_drums, kTrackDrum, "D"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_drums_pro, kTrackDrum, "d"); break; - case KEYBOARD_CONT: - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_keys, KEYS, "K"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_real_keys, PRO_KEYS, "k"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_guitar, GUITAR, "G"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_bass, BASS, "B"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_drums, DRUMS, "D"); + case kControllerKeys: + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_keys, kTrackKeys, "K"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_real_keys, kTrackRealKeys, "k"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_guitar, kTrackGuitar, "G"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_bass, kTrackBass, "B"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_drums, kTrackDrum, "D"); break; - case PRO_GUITAR_CONT: - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_real_guitar, PRO_GUITAR, "g"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_real_bass, PRO_BASS, "b"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_guitar, GUITAR, "G"); - AddInstrumentToList(thisOvershellSlot, &globalSymbols.overshell_bass, BASS, "B"); + case kControllerRealGuitar: + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_real_guitar, kTrackRealGuitar, "g"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_real_bass, kTrackRealBass, "b"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_guitar, kTrackGuitar, "G"); + OvershellPartSelectProviderAddPart(thisOvershellSlot, &globalSymbols.overshell_bass, kTrackBass, "B"); break; } -} +} \ No newline at end of file diff --git a/source/RndPropAnimHooks.c b/source/RndPropAnimHooks.c index eec9c15..5918f9b 100644 --- a/source/RndPropAnimHooks.c +++ b/source/RndPropAnimHooks.c @@ -10,7 +10,7 @@ // see https://github.com/RBEnhanced/RB3Enhanced/issues/2 void PropAnimSetFrameHook(RndPropAnim *rndPropAnim, float frame, float time) { - if (strcmp(rndPropAnim->object.name, "slot_positions.anim") == 0) + if (strcmp(rndPropAnim->mObject.name, "slot_positions.anim") == 0) { // check that it is not setting the frame to 0 (which is the default slot positions for overshell) if (frame != 0.0f) diff --git a/source/SetlistHooks.c b/source/SetlistHooks.c index efd4347..c55c378 100644 --- a/source/SetlistHooks.c +++ b/source/SetlistHooks.c @@ -5,84 +5,259 @@ #include #include +#include #include #include "config.h" #include "ports.h" +#include "GameOriginInfo.h" #include "rb3/BandLabel.h" +#include "rb3/File.h" +#include "rb3/FilePath.h" +#include "rb3/Mem.h" +#include "rb3/MusicLibrary.h" +#include "rb3/NodeSort.h" +#include "rb3/UI/UIListSlot.h" #include "rb3/UI/UIPanel.h" +#include "SongSort.h" #include "rb3/SortNode.h" +#include "rb3/SongSortMgr.h" +#include "rb3/Rnd/DynamicTex.h" +#include "rb3/Rnd/RndMat.h" -static char *originToIcon[][2] = { - {"rb1", "Y "}, - {"rb2", "2 "}, - {"rb3", "B "}, - {"rb4", "1 "}, - {"rb_blitz", "X "}, - {"ugc", "U "}, - {"ugc_c3", "y "}, - {"ugc_plus", "U "}, - {"ugc1", "U "}, - {"ugc2", "U "}, - {"lego", "A "}, - {"greenday", "0 "}, - {"beatles", "b "}, - {"gh1", "R "}, - {"gh2", "S "}, - {"gh3", "s "}, - {"onyxite", "G "}, -}; -static int numOriginToIcon = sizeof(originToIcon) / sizeof(originToIcon[0]); - -void SetSongAndArtistNameHook(BandLabel *label, SortNode *sortNode) +DynamicTex *textures[100] = {0}; + +void CreateMaterial(GameOriginInfo *info) { - char newLabel[1024] = {0}; - char *originLabel = "0 "; // default - int i = 0; + // alloc the memory for the dynamic tex + DynamicTex *tex = NULL; + char path[0x200]; + + // TODO: we should identify and hook some function that runs when you leave the song select screen to call DynamicTex's destructor + // it will free the dynamic tex itself, the material it created, and the texture too, so it nicely wraps it all up for you + // this way there is not a chunk of memory permanently dedicated to game origin icons (even if it is not a large amount) + tex = MemAlloc(0x20, 0); + if (tex == NULL) { + RB3E_DEBUG("MemAlloc failed for dynamic tex '%s'", info->gameOrigin); + return; + } + + // build the ark path (so dont include /gen/ or the _platform extension etc.) + RB3E_DEBUG("Creating dynamic tex for game origin '%s'", info->gameOrigin); + strcpy(path, "ui/resource/game_origins/"); + strcat(path, info->gameOrigin); + strcat(path, ".png"); + + // create and pray + DynamicTexConstructor(tex, path, info->gameOrigin, 1, 0); + if (tex->mMat == NULL || tex->mTex == NULL) { + RB3E_DEBUG("DynamicTex::__ct failed for dynamic tex '%s'", path); + + // we should prob do cleanup here but that is a + // TODO + return; + } + + textures[info->num] = tex; + + RB3E_DEBUG("Setting diffuse texture for dynamic tex '%s'", path); + RndTexSetBitmap3(tex->mTex, tex->mFileLoader); + + // diffuse tex setter function doesn't exist on 360, so we manually set it, but manually setting it doesn't work on wii and we must use the setter + // TODO: figure out why we can't just set it manually on both, probably a structure inaccuracy or something + #ifdef RB3E_XBOX + tex->mMat->diffuseTex.tex = tex->mTex; + #else + RndMatSetDiffuseTex(tex->mMat, tex->mTex); + #endif + + // print the material name + RB3E_DEBUG("Dynamic tex created at %p with material '%s'", textures[info->num], textures[info->num]->mMatName.buf); + + RB3E_DEBUG("Dynamic tex created at %p with mat %p tex %p fileloader %p", tex, tex->mMat, tex->mTex, tex->mFileLoader); + + +} - if (config.GameOriginIcons == 1 && strlen(label->string) < 1000) +int *MusicLibraryConstructorHook(int *thisMusicLibrary, int *songPreview) +{ + return MusicLibraryConstructor(thisMusicLibrary, songPreview); +} + +RndMat *MusicLibraryMatHook(MusicLibrary *thisMusicLibrary, int data, int idx, UIListSlot *listSlot) +{ + if (listSlot != NULL && thisMusicLibrary != NULL && config.GameOriginIcons == 1) { - SetSongAndArtistName(label, sortNode); - for (i = 0; i < numOriginToIcon; i++) + if (strcmp(listSlot->mMatchName.buf, "game_origin_icon") == 0) { - if (strcmp(sortNode->somethingElse->metaData->gameOrigin.sym, originToIcon[i][0]) == 0) + NodeSort *ret = 0; + SortNode *node = 0; + SongNodeType nodeType = kNodeNone; + int curInfo = 0; + + ret = SongSortMgrGetSort(*(SongSortMgr **)PORT_THESONGSORTMGR, thisMusicLibrary->mSortType); + if (ret != NULL) { - originLabel = originToIcon[i][1]; - break; + node = NodeSortGetNode(ret, idx); + if (node != NULL) + { + nodeType = node->vtable->getNodeType(); + + // ensure this is actually a song node and not like a function node or etc. + if (nodeType == kNodeSong) + { + // do a basic null check here, sometimes it can be null + if (node != NULL && node->record != NULL && + node->record->metaData != NULL && + node->record->metaData->gameOrigin.sym != NULL) + { + // this shit fucking sucks lol + for (curInfo = 0; curInfo < numGameOrigins; curInfo++) + { + if (strcmp(node->record->metaData->gameOrigin.sym, originInfo[curInfo].gameOrigin) == 0) + { + if (textures[originInfo[curInfo].num] != NULL && textures[originInfo[curInfo].num]->mMat != NULL) + { + RB3E_DEBUG("Returning material for game origin '%s'", originInfo[curInfo].gameOrigin); + return textures[originInfo[curInfo].num]->mMat; + } + else + { + RB3E_DEBUG("Material for game origin '%s' is NULL, creation seemingly failed", originInfo[curInfo].gameOrigin); + } + } + } + } else { + RB3E_DEBUG("Node record or its metadata is NULL, skipping", NULL); + } + } + } } } - strcat(newLabel, originLabel); - strcat(newLabel, label->string); - BandLabelSetDisplayText(label, newLabel, 1); - return; } - SetSongAndArtistName(label, sortNode); - return; + + return (RndMat*)(intptr_t)MusicLibraryMat((void*)thisMusicLibrary, data, idx, (int*)listSlot); +} + +// called when entering the music library +// allocate the memory and load the textures here +void MusicLibraryOnEnterHook(void *thisMusicLibrary) +{ + int i; + + // allocate and create the materials for any game origins we found while loading songs + for (i = 0; i < numGameOrigins; i++) { + int slot = originInfo[i].num; + if (slot >= 0 && slot < 100) { + if (textures[slot] == NULL) { + CreateMaterial(&originInfo[i]); + } + } + } + + // do the original logic + MusicLibraryOnEnter(thisMusicLibrary); +} + +// called when the music library panel is unloaded +void MusicLibraryOnUnloadHook(void *thisMusicLibrary) +{ + int i; + + MusicLibraryOnUnload(thisMusicLibrary); + + // call the dynamictex destructor for any textures we created + // lets just sweep all 100 to be safe + for (i = 0; i < 100; i++) { + if (textures[i] != NULL) { + // we can't call the DynamicTexDestructor because the FileLoader it used has already been freed by this point, so we must destruct everything but the FileLoader + // extremely nasty but it seems to work + if(textures[i]->mMat != NULL) { + ((Object *)textures[i]->mMat)->table->destructor((Object *)textures[i]->mMat); + textures[i]->mMat = NULL; + } + if(textures[i]->mTex != NULL) { + ((Object *)textures[i]->mTex)->table->destructor((Object *)textures[i]->mTex); + textures[i]->mTex = NULL; + } + // TODO: set up a proper vtable for String instead of doign this shit + if(textures[i]->mMatName.length != 0) { + if(textures[i]->mMatName.vtable != NULL) { + #ifdef RB3E_WII + ((void (*)(String *))textures[i]->mMatName.vtable[2])(&textures[i]->mMatName); + #else + ((void (*)(String *))textures[i]->mMatName.vtable[0])(&textures[i]->mMatName); + #endif + } + } + textures[i] = NULL; + } + } + + RB3E_DEBUG("Freed game origin icon textures", NULL); } -void SetSongNameFromNodeHook(BandLabel *label, SortNode *sortNode) +int numGameOrigins; +GameOriginInfo originInfo[100] = {0}; + +void AddGameOriginToIconList(char *gameOrigin) { - char newLabel[1024] = {0}; - char *originLabel = "0 "; // default + if(gameOrigin != NULL && strcmp(gameOrigin, "") != 0) + { int i = 0; - RB3E_DEBUG("SetSongNameFromNode: %s", label->string); + // check that we havent already added 100 game origins + if (numGameOrigins >= 100) + { + RB3E_DEBUG("Too many game origins with icons, not adding '%s'", gameOrigin); + return; + } - if (config.GameOriginIcons == 1 && strlen(label->string) < 1000) + // make sure the game origin isn't already in the array + for (i = 0; i < numGameOrigins; i++) { - SetSongNameFromNode(label, sortNode); - for (i = 0; i < numOriginToIcon; i++) + if (strcmp(originInfo[i].gameOrigin, gameOrigin) == 0) { - if (strcmp(sortNode->somethingElse->metaData->gameOrigin.sym, originToIcon[i][0]) == 0) - { - originLabel = originToIcon[i][1]; - break; - } + return; } - strcat(newLabel, originLabel); - strcat(newLabel, label->string); - BandLabelSetDisplayText(label, newLabel, 1); - return; } - SetSongNameFromNode(label, sortNode); - return; + + originInfo[numGameOrigins].gameOrigin = gameOrigin; + originInfo[numGameOrigins].num = numGameOrigins; + numGameOrigins++; + RB3E_DEBUG("Adding game origin '%s' to icon list, total is now %i", gameOrigin, numGameOrigins); + } + else + { + RB3E_DEBUG("Game origin is NULL or empty, not adding to icon list", NULL); + } +} + +// this will be called any time a song is loaded from DTA (on disc or when loading into the cache) +SongMetadata *SongMetadataConstructorHook(SongMetadata *thisSongMetadata, DataArray *data, DataArray *backupData, char isOnDisc) +{ + thisSongMetadata = SongMetadataConstructor(thisSongMetadata, data, backupData, isOnDisc); + + // make sure the game origin isn't null + if (thisSongMetadata->gameOrigin.sym != 0) + { + AddGameOriginToIconList(thisSongMetadata->gameOrigin.sym); + return thisSongMetadata; + } + + return thisSongMetadata; +} + +// this will be called when a song is loaded from cache +char SongMetadataLoadHook(SongMetadata *thisSongMetadata, BinStream *stream) +{ + char ret = SongMetadataLoad(thisSongMetadata, stream); + + // make sure the game origin isn't null + if (thisSongMetadata->gameOrigin.sym != 0) + { + AddGameOriginToIconList(thisSongMetadata->gameOrigin.sym); + return ret; + } + + return ret; } \ No newline at end of file diff --git a/source/SongParserHooks.c b/source/SongParserHooks.c index 2069ebb..fbc9d59 100644 --- a/source/SongParserHooks.c +++ b/source/SongParserHooks.c @@ -21,7 +21,7 @@ int SongParserPitchToSlotHook(SongParser *thisSongParser, int pitch, int *diffic if (doubleBassModifier->enabled) { // make sure the current track type is drums - if (thisSongParser->mTrackType == DRUMS) + if (thisSongParser->mTrackType == kTrackDrum) { // check for MIDI note pitch 95 (which is the 2x bass pedal note) if (pitch == 95) diff --git a/source/_functions.c b/source/_functions.c index e7d3b5f..e8fc1d4 100644 --- a/source/_functions.c +++ b/source/_functions.c @@ -99,6 +99,31 @@ RB3E_STUB(QueuingSocketBind) RB3E_STUB(OperatorEqualsFmt) RB3E_STUB(UpdatePresence) RB3E_STUB(StepSequenceJobSetStep) +RB3E_STUB(RndTexNewObject) +RB3E_STUB(RndMatNewObject) +RB3E_STUB(RndTexSetBitmap) +RB3E_STUB(RndTexSetBitmap2) +RB3E_STUB(FilePathConstructor) +RB3E_STUB(MusicLibraryMat) +RB3E_STUB(MusicLibraryOnEnter) +RB3E_STUB(MusicLibraryOnUnload) +RB3E_STUB(RndCreateDefaultTexture) +RB3E_STUB(MusicLibraryConstructor) +RB3E_STUB(MusicLibraryGetNodeByIndex) +RB3E_STUB(StringConstructor) +RB3E_STUB(AddSmasherPlateToVector) +RB3E_STUB(vector_push_back) +RB3E_STUB(GameGemDBConstructor) +RB3E_STUB(AddMultiGem) +RB3E_STUB(GetGameGemList) +RB3E_STUB(SongMetadataConstructor) +RB3E_STUB(SongMetadataLoad) +RB3E_STUB(SongSortMgrGetSort) +RB3E_STUB(NodeSortGetNode) +RB3E_STUB(DynamicTexConstructor) +RB3E_STUB(DynamicTexDestructor) +RB3E_STUB(RndMatSetDiffuseTex) +RB3E_STUB(RndTexSetBitmap3) RB3E_STUB(BinstreamWrite) RB3E_STUB(BinstreamRead) RB3E_STUB(BinstreamReadEndian) diff --git a/source/rb3enhanced.c b/source/rb3enhanced.c index eee827c..37a8404 100644 --- a/source/rb3enhanced.c +++ b/source/rb3enhanced.c @@ -377,6 +377,7 @@ void InitialiseFunctions() POKE_B(&HmxFactoryFuncAt, PORT_HMXFACTORYFUNCAT); // TODO(Emma): port to bank8 POKE_B(&ObjectFindUIPanel, PORT_OBJECTFINDUIPANEL); + POKE_B(&vector_push_back, PORT_VECTORPUSHBACK); // otherwise unused it seems? #endif POKE_B(&RandomInt, PORT_RANDOMINT); POKE_B(&DataNodeEvaluate, PORT_DATANODEEVALUATE); @@ -388,6 +389,7 @@ void InitialiseFunctions() POKE_B(&GetMetadata, PORT_GETMETADATA); POKE_B(&GetSongIDFromShortname, PORT_GETSONGIDFROMSHORTNAME); POKE_B(&GetBandUsers, PORT_GETBANDUSERS); + POKE_B(&GetBandUserFromSlot, PORT_GETBANDUSERFROMSLOT); POKE_B(&FileStreamConstructor, PORT_FILESTREAM_CT); POKE_B(&ChunkStreamConstructor, PORT_CHUNKSTREAM_CT); POKE_B(&Dynamic_Cast, PORT_DYNAMICCAST); @@ -395,6 +397,18 @@ void InitialiseFunctions() POKE_B(&JoypadGetPadData, PORT_JOYPADGETPADDATA); POKE_B(&MemAlloc, PORT_MEMALLOC); POKE_B(&MemFree, PORT_MEMFREE); + POKE_B(&RndTexNewObject, PORT_RNDTEXNEWOBJECT); + POKE_B(&RndMatNewObject, PORT_RNDMATNEWOBJECT); + POKE_B(&RndTexSetBitmap, PORT_RNDTEXSETBITMAP); + POKE_B(&RndTexSetBitmap2, PORT_RNDTEXSETBITMAP2); + POKE_B(&FilePathConstructor, PORT_FILEPATHCONSTRUCTOR); + POKE_B(&NodeSortGetNode, PORT_NODESORTGETNODE); + POKE_B(&GameGemDBConstructor, PORT_GAMEGEMDB_CT); + POKE_B(&SongSortMgrGetSort, PORT_SONGSORTMGRGETSORT); + POKE_B(&DynamicTexConstructor, PORT_DYNAMICTEX_CT); + POKE_B(&DynamicTexDestructor, PORT_DYNAMICTEX_DT); + POKE_B(&RndMatSetDiffuseTex, PORT_RNDMATSETDIFFUSETEX); + POKE_B(&RndTexSetBitmap3, PORT_RNDTEXSETBITMAP3); POKE_B(&BinstreamWrite, PORT_BINSTREAMWRITE); POKE_B(&BinstreamRead, PORT_BINSTREAMREAD); POKE_B(&BinstreamWriteEndian, PORT_BINSTREAMWRITEENDIAN); @@ -419,17 +433,18 @@ void InitialiseFunctions() void ApplyHooks() { POKE_B(PORT_DATAINITFUNCS_TAIL, &AddDTAFunctions); + POKE_B(PORT_OVERSHELLPARTSELECTPROVIDERRELOAD, &OvershellPartSelectProviderReload); + //#ifndef RB3E_WII_BANK8 + // POKE_B(PORT_BUILDINSTRUMENTSELECTION, &BuildInstrumentSelectionList); + //#endif // TODO(Emma): fix this buggy set of hooks //POKE_B(PORT_ISSUPPORTEDLANGUAGE, &IsSupportedLanguageHook); -#ifndef RB3E_WII_BANK8 - POKE_B(PORT_BUILDINSTRUMENTSELECTION, &BuildInstrumentSelectionList); -#endif POKE_BL(PORT_OPTIONSTR_DEFINE, &DefinesHook); POKE_BL(PORT_RUNLOOP_SPARE, &RB3E_RunLoop); HookFunction(PORT_LOCALIZE, &Localize, &LocalizeHook); HookFunction(PORT_WILLBENOSTRUM, &WillBeNoStrum, &WillBeNoStrumHook); HookFunction(PORT_ADDGAMEGEM, &AddGameGem, &AddGameGemHook); - HookFunction(PORT_SETSONGANDARTISTNAME, &SetSongAndArtistName, SetSongAndArtistNameHook); + //HookFunction(PORT_SETSONGANDARTISTNAME, &SetSongAndArtistName, SetSongAndArtistNameHook); HookFunction(PORT_SETVENUE, &SetVenue, &SetVenueHook); HookFunction(PORT_MODIFIERMGR_CT, &ModifierManagerConstructor, &ModifierManagerConstructorHook); HookFunction(PORT_NEWFILE, &NewFile, &NewFileHook); @@ -447,19 +462,24 @@ void ApplyHooks() HookFunction(PORT_RNDPROPANIMSETFRAME, &PropAnimSetFrame, &PropAnimSetFrameHook); HookFunction(PORT_SYMBOLPREINIT, &SymbolPreInit, &SymbolPreInitHook); HookFunction(PORT_INITSONGMETADATA, &InitSongMetadata, &InitSongMetadataHook); + HookFunction(PORT_SONGMETADATACONSTRUCTOR, &SongMetadataConstructor, &SongMetadataConstructorHook); + HookFunction(PORT_SONGMETADATALOAD, &SongMetadataLoad, &SongMetadataLoadHook); HookFunction(PORT_UPDATEPRESENCE, &UpdatePresence, &UpdatePresenceHook); + HookFunction(PORT_MUSICLIBRARY_CT, &MusicLibraryConstructor, &MusicLibraryConstructorHook); + HookFunction(PORT_MUSICLIBRARYMAT, &MusicLibraryMat, &MusicLibraryMatHook); HookFunction(PORT_SONGPARSERPITCHTOSLOT, &SongParserPitchToSlot, &SongParserPitchToSlotHook); HookFunction(PORT_DATASET, &DataSet, &DataSetHook); HookFunction(PORT_DATASETELEM, &DataSetElem, &DataSetElemHook); HookFunction(PORT_DATAONELEM, &DataOnElem, &DataOnElemHook); - + HookFunction(PORT_MUSICLIBRARYONENTER, &MusicLibraryOnEnter, &MusicLibraryOnEnterHook); + HookFunction(PORT_MUSICLIBRARYONUNLOAD, &MusicLibraryOnUnload, &MusicLibraryOnUnloadHook); #ifdef RB3E_WII // wii exclusive hooks // HookFunction(PORT_USBWIIGETTYPE, &UsbWiiGetType, &UsbWiiGetTypeHook); HookFunction(PORT_WIINETINIT_DNSLOOKUP, &StartDNSLookup, &StartDNSLookupHook); POKE_B(PORT_FILEISLOCAL, &FileIsLocalHook); #elif RB3E_XBOX // 360 exclusive hooks HookFunction(PORT_STAGEKIT_SET_STATE, &StagekitSetState, &StagekitSetStateHook); - HookFunction(PORT_SETSONGNAMEFROMNODE, &SetSongNameFromNode, &SetSongNameFromNodeHook); + //HookFunction(PORT_SETSONGNAMEFROMNODE, &SetSongNameFromNode, &SetSongNameFromNodeHook); // TODO: port these to Wii HookFunction(PORT_DATANODEGETOBJ, &DataNodeGetObj, &DataNodeGetObjHook); POKE_B(PORT_GETSONGID, &GetSongIDHook);