├── Helper ├── GctFiles │ └── codehandler.bin ├── SequenceCmd │ ├── GotaSequenceCmd.exe │ ├── GotaSequenceCmd.pdb │ ├── GotaSequenceLib.dll │ ├── GotaSequenceLib.pdb │ ├── GotaSoundIO.dll │ ├── GotaSoundIO.pdb │ ├── NAudio.dll │ ├── Sanford.Multimedia.Midi.dll │ ├── Sanford.Multimedia.Midi.pdb │ ├── VGAudio.dll │ ├── assemble.bat │ ├── disassemble.bat │ ├── from_midi.bat │ ├── invert.bat │ └── to_midi.bat ├── Update │ ├── Update.bat │ └── Version.txt ├── WiiMusicSave │ ├── RPClipDB.dat │ ├── RPMusic.dat │ ├── RPNandDB.dat │ ├── banner.bin │ └── wc24scr.vff └── Wiimms │ ├── cygcrypto-1.1.dll │ ├── cygncursesw-10.dll │ ├── cygpng16-16.dll │ ├── cygwin1.dll │ ├── cygz.dll │ ├── decode.bat │ ├── encode.bat │ ├── wbmgt.exe │ ├── wit.exe │ ├── wstrt.exe │ └── wszst.exe ├── LICENSE ├── README.md ├── WiiMusicEditor.bat └── WiiMusicEditor.py /Helper/GctFiles/codehandler.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/GctFiles/codehandler.bin -------------------------------------------------------------------------------- /Helper/SequenceCmd/GotaSequenceCmd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/GotaSequenceCmd.exe -------------------------------------------------------------------------------- /Helper/SequenceCmd/GotaSequenceCmd.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/GotaSequenceCmd.pdb -------------------------------------------------------------------------------- /Helper/SequenceCmd/GotaSequenceLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/GotaSequenceLib.dll -------------------------------------------------------------------------------- /Helper/SequenceCmd/GotaSequenceLib.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/GotaSequenceLib.pdb -------------------------------------------------------------------------------- /Helper/SequenceCmd/GotaSoundIO.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/GotaSoundIO.dll -------------------------------------------------------------------------------- /Helper/SequenceCmd/GotaSoundIO.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/GotaSoundIO.pdb -------------------------------------------------------------------------------- /Helper/SequenceCmd/NAudio.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/NAudio.dll -------------------------------------------------------------------------------- /Helper/SequenceCmd/Sanford.Multimedia.Midi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/Sanford.Multimedia.Midi.dll -------------------------------------------------------------------------------- /Helper/SequenceCmd/Sanford.Multimedia.Midi.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/Sanford.Multimedia.Midi.pdb -------------------------------------------------------------------------------- /Helper/SequenceCmd/VGAudio.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/SequenceCmd/VGAudio.dll -------------------------------------------------------------------------------- /Helper/SequenceCmd/assemble.bat: -------------------------------------------------------------------------------- 1 | "%~dp0GotaSequenceCmd.exe" assemble %1 2 | -------------------------------------------------------------------------------- /Helper/SequenceCmd/disassemble.bat: -------------------------------------------------------------------------------- 1 | "%~dp0GotaSequenceCmd.exe" disassemble %1 2 | -------------------------------------------------------------------------------- /Helper/SequenceCmd/from_midi.bat: -------------------------------------------------------------------------------- 1 | "%~dp0GotaSequenceCmd.exe" from_midi %1 2 | -------------------------------------------------------------------------------- /Helper/SequenceCmd/invert.bat: -------------------------------------------------------------------------------- 1 | "%~dp0GotaSequenceCmd.exe" invert %1 2 | -------------------------------------------------------------------------------- /Helper/SequenceCmd/to_midi.bat: -------------------------------------------------------------------------------- 1 | "%~dp0GotaSequenceCmd.exe" to_midi %1 2 | -------------------------------------------------------------------------------- /Helper/Update/Update.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cd "%~dp0" 4 | cd.. 5 | cd.. 6 | cd.. 7 | 8 | robocopy "%CD%\WiiMusicEditorNew" "%CD%" /MIR /XF settings.ini /XD .git WiiMusicEditorNew "%CD%\Helper\Backup" > nul 9 | 10 | WiiMusicEditor.bat -------------------------------------------------------------------------------- /Helper/Update/Version.txt: -------------------------------------------------------------------------------- 1 | 5:49 PM 10/30/2021 2 | -------------------------------------------------------------------------------- /Helper/WiiMusicSave/RPClipDB.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/WiiMusicSave/RPClipDB.dat -------------------------------------------------------------------------------- /Helper/WiiMusicSave/RPMusic.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/WiiMusicSave/RPMusic.dat -------------------------------------------------------------------------------- /Helper/WiiMusicSave/RPNandDB.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/WiiMusicSave/RPNandDB.dat -------------------------------------------------------------------------------- /Helper/WiiMusicSave/banner.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/WiiMusicSave/banner.bin -------------------------------------------------------------------------------- /Helper/WiiMusicSave/wc24scr.vff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/WiiMusicSave/wc24scr.vff -------------------------------------------------------------------------------- /Helper/Wiimms/cygcrypto-1.1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/cygcrypto-1.1.dll -------------------------------------------------------------------------------- /Helper/Wiimms/cygncursesw-10.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/cygncursesw-10.dll -------------------------------------------------------------------------------- /Helper/Wiimms/cygpng16-16.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/cygpng16-16.dll -------------------------------------------------------------------------------- /Helper/Wiimms/cygwin1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/cygwin1.dll -------------------------------------------------------------------------------- /Helper/Wiimms/cygz.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/cygz.dll -------------------------------------------------------------------------------- /Helper/Wiimms/decode.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if exist %1\message.d\ (rmdir /s /q %1\message.d) 4 | "%~dp0wszst.exe" extract %1\message.carc 5 | del %1\message.d\wszst-setup.txt 6 | "%~dp0wbmgt.exe" decode %1\message.d\new_music_message.bmg 7 | del %1\message.d\new_music_message.bmg -------------------------------------------------------------------------------- /Helper/Wiimms/encode.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | "%~dp0wbmgt.exe" encode %1\message.d\new_music_message.txt 4 | del %1\message.d\new_music_message.txt 5 | del %1\message.carc 6 | "%~dp0wszst.exe" create %1\message.d --dest %1\message.carc 7 | rmdir /s /q %1\message.d -------------------------------------------------------------------------------- /Helper/Wiimms/wbmgt.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/wbmgt.exe -------------------------------------------------------------------------------- /Helper/Wiimms/wit.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/wit.exe -------------------------------------------------------------------------------- /Helper/Wiimms/wstrt.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/wstrt.exe -------------------------------------------------------------------------------- /Helper/Wiimms/wszst.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/28070cc87eb5bf62e86984b5ead5611ef1918b26/Helper/Wiimms/wszst.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 BenjaminHalko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://user-images.githubusercontent.com/73490201/119917911-4e628100-bf1c-11eb-9c8c-2b8727e2d916.png) 2 | # Features: 3 | - Powerful and Easy to use. 4 | - Able to replace songs bigger than the original. 5 | - Automatic conversion of MIDIs or RSEQs to BRSEQs. 6 | - Automatic patching for (almost) everything in the game! 7 | - Text Editing to change Dialog. 8 | - Multiple Song Managing Tools! 9 | - 100% Save File. 10 | - Dolphin Integration. 11 | - Python 3.9 and 3.8 support. 12 | - And more! 13 | 14 | # Setup Video 15 | https://youtu.be/EBz9VtBXqEo 16 | 17 | # Looking for the Manual? Check the wiki! 18 | https://github.com/BenjaminHalko/WiiMusicEditor/wiki/ 19 | -------------------------------------------------------------------------------- /WiiMusicEditor.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cd %~dp0" 4 | python "WiiMusicEditor.py" %1 5 | if not exist WiiMusicEditorNew pause -------------------------------------------------------------------------------- /WiiMusicEditor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import getpass 3 | import time 4 | import subprocess 5 | import configparser 6 | import pathlib 7 | import tempfile 8 | from shutil import copyfile, rmtree, copytree 9 | from math import floor, ceil 10 | import webbrowser 11 | import zipfile 12 | 13 | #Special Imports 14 | while True: 15 | try: 16 | import requests 17 | import mido 18 | from colorama import Fore, Style, init 19 | from tqdm import tqdm 20 | from tabulate import tabulate 21 | break 22 | except ImportError: 23 | subprocess.run('python -m pip install --upgrade pip') 24 | subprocess.run('pip install mido requests colorama tqdm tabulate') 25 | 26 | init(convert=True) 27 | 28 | time.sleep(0.05) 29 | 30 | class SongClass: 31 | def __init__(self,SongType,Name,MemOffset,MemOrder): 32 | self.SongType = SongType 33 | self.Name = Name 34 | self.MemOffset = MemOffset 35 | self.MemOrder = MemOrder 36 | 37 | class StyleClass: 38 | def __init__(self,StyleType,Name,MemOffset,StyleId): 39 | self.StyleType = StyleType 40 | self.Name = Name 41 | self.MemOffset = MemOffset 42 | self.StyleId = StyleId 43 | 44 | class InstrumentClass: 45 | def __init__(self,Name,Number,InMenu): 46 | self.Name = Name 47 | self.Number = Number 48 | self.InMenu = InMenu 49 | 50 | class SongTypeValue: 51 | Regular = 0 52 | Menu = 1 53 | Maestro = 2 54 | Handbell = 3 55 | 56 | class StyleTypeValue: 57 | Global = 0 58 | SongSpecific = 1 59 | QuickJam = 2 60 | Menu = 3 61 | 62 | NumberOfStyleTypes = 5 63 | 64 | Songs = [ 65 | SongClass(SongTypeValue.Regular,'A Little Night Music','025a08a8',6), 66 | SongClass(SongTypeValue.Regular,'American Patrol','025a0c54',11), 67 | SongClass(SongTypeValue.Regular,'Animal Crossing','025a2780',48), 68 | SongClass(SongTypeValue.Regular,'Animal Crossing K.K. Blues','025a1758',26), 69 | SongClass(SongTypeValue.Regular,'Bridal Chorus','025a04fc',1), 70 | SongClass(SongTypeValue.Regular,'Carmen','025a0674',3), 71 | SongClass(SongTypeValue.Regular,'Chariots of Fire','025a1df4',35), 72 | SongClass(SongTypeValue.Regular,'Daydream Believer','025a1c7c',33), 73 | SongClass(SongTypeValue.Regular,'Do-Re-Mi','025a0adc',9), 74 | SongClass(SongTypeValue.Regular,'Every Breath You Take','025a1d38',34), 75 | SongClass(SongTypeValue.Regular,'F-Zero','025a283c',49), 76 | SongClass(SongTypeValue.Regular,'Frere Jacques','025a1468',22), 77 | SongClass(SongTypeValue.Regular,'From Santurtzi to Bilbao','025a1814',27), 78 | SongClass(SongTypeValue.Regular,'From the New World','025a1000',16), 79 | SongClass(SongTypeValue.Regular,'Happy Birthday to You','025a0a20',8), 80 | SongClass(SongTypeValue.Regular,'Ill Be There','025a21a0',40), 81 | SongClass(SongTypeValue.Regular,'Ive Never Been to Me','025a2490',44), 82 | SongClass(SongTypeValue.Regular,'Jingle Bell Rock','025a2318',41), 83 | SongClass(SongTypeValue.Regular,'La Bamba','025a10bc',17), 84 | SongClass(SongTypeValue.Regular,'La Cucaracha','025a198c',29), 85 | SongClass(SongTypeValue.Regular,'Little Hans','025a169c',25), 86 | SongClass(SongTypeValue.Regular,'Long Long Ago','025a1234',19), 87 | SongClass(SongTypeValue.Regular,'Material Girl','025a2028',38), 88 | SongClass(SongTypeValue.Regular,'Minuet in G Major','025a0964',7), 89 | SongClass(SongTypeValue.Regular,'My Grandfathers Clock','025a0f44',15), 90 | SongClass(SongTypeValue.Regular,'O-Christmas Tree','025a15e0',24), 91 | SongClass(SongTypeValue.Regular,'Ode To Joy','025a0440',0), 92 | SongClass(SongTypeValue.Regular,'Oh My Darling Clementine','025a0e88',14), 93 | SongClass(SongTypeValue.Regular,'Over the Waves','025a1a48',30), 94 | SongClass(SongTypeValue.Regular,'Please Mr. Postman','025a1f6c',37), 95 | SongClass(SongTypeValue.Regular,'Sakura Sakura','025a1b04',31), 96 | SongClass(SongTypeValue.Regular,'Scarborough Fair','025a1178',18), 97 | SongClass(SongTypeValue.Regular,'September','025a1eb0',36), 98 | SongClass(SongTypeValue.Regular,'Sukiyaki','025a1bc0',32), 99 | SongClass(SongTypeValue.Regular,'Super Mario Bros','025a254c',45), 100 | SongClass(SongTypeValue.Regular,'Sur le Pont d Avignon','025a13ac',21), 101 | SongClass(SongTypeValue.Regular,'Swan Lake','025a05b8',2), 102 | SongClass(SongTypeValue.Regular,'The Blue Danube','025a07ec',5), 103 | SongClass(SongTypeValue.Regular,'The Entertainer','025a0b98',10), 104 | SongClass(SongTypeValue.Regular,'The Flea Waltz','025a1524',23), 105 | SongClass(SongTypeValue.Regular,'The Legend of Zelda','025a2608',46), 106 | SongClass(SongTypeValue.Regular,'The Loco Motion','025a20e4',39), 107 | SongClass(SongTypeValue.Regular,'Troika','025a18d0',28), 108 | SongClass(SongTypeValue.Regular,'Turkey in the Straw','025a0d10',12), 109 | SongClass(SongTypeValue.Regular,'Twinkle Twinkle Little Star','025a12f0',20), 110 | SongClass(SongTypeValue.Regular,'Wake Me Up Before You Go-Go','025a23d4',42), 111 | SongClass(SongTypeValue.Regular,'Wii Music','025a0730',4), 112 | SongClass(SongTypeValue.Regular,'Wii Sports','025a26c4',47), 113 | SongClass(SongTypeValue.Regular,'Woman','025a23d4',43), 114 | SongClass(SongTypeValue.Regular,'Yankee Doodle','025a0dcc',13), 115 | SongClass(SongTypeValue.Maestro,'Twinkle Twinkle Little Star (Mii Maestro)','025a3e1c',2), 116 | SongClass(SongTypeValue.Maestro,'Carmen (Mii Maestro)','025a3d80',0), 117 | SongClass(SongTypeValue.Maestro,'The Four Seaons - Spring (Mii Maestro)','025a3f54',4), 118 | SongClass(SongTypeValue.Maestro,'Ode To Joy (Mii Maestro)','025a3ff0',3), 119 | SongClass(SongTypeValue.Maestro,'The Legend of Zelda (Mii Maestro)','025a3eb8',1), 120 | SongClass(SongTypeValue.Handbell,'O Christmas Tree (Handbell Harmony)','02566D5A',0), 121 | SongClass(SongTypeValue.Handbell,'Hum, Hum, Hum (Handbell Harmony)','02566E0A',2), 122 | SongClass(SongTypeValue.Handbell,'My Grandfather\'s Clock (Handbell Harmony)','02566E62',3), 123 | SongClass(SongTypeValue.Handbell,'Do-Re-Mi (Handbell Harmony)','02566DB2',1), 124 | SongClass(SongTypeValue.Handbell,'Sukiyaki (Handbell Harmony)','02566EBA',4), 125 | SongClass(SongTypeValue.Menu,'Menu Song',['0259ACB0','0259ACD4','0259ACF8','0259AD1C','0259AD40'],-1)] 126 | 127 | Styles = [ 128 | StyleClass(StyleTypeValue.Global,'Jazz','0659A65C','00'), 129 | StyleClass(StyleTypeValue.Global,'Rock','0659A680','01'), 130 | StyleClass(StyleTypeValue.Global,'Latin','0659A6A4','02'), 131 | StyleClass(StyleTypeValue.Global,'March','0659A6C8','03'), 132 | StyleClass(StyleTypeValue.Global,'Electronic','0659A6EC','04'), 133 | StyleClass(StyleTypeValue.Global,'Pop','0659A710','05'), 134 | StyleClass(StyleTypeValue.Global,'Japanese','0659A734','06'), 135 | StyleClass(StyleTypeValue.Global,'Tango','0659A758','07'), 136 | StyleClass(StyleTypeValue.Global,'Classical','0659A77C','08'), 137 | StyleClass(StyleTypeValue.Global,'Hawaiian','0659A7A0','09'), 138 | StyleClass(StyleTypeValue.Global,'Reggae','0659A7C4','0A'), 139 | StyleClass(StyleTypeValue.SongSpecific,'A Little Night Music','0659AB00','21'), 140 | StyleClass(StyleTypeValue.SongSpecific,'Animal Crossing','0659AA28','1B'), 141 | StyleClass(StyleTypeValue.SongSpecific,'Animal Crossing K.K. Blues','0659AB48','23'), 142 | StyleClass(StyleTypeValue.SongSpecific,'Carmen','0659A878','0F'), 143 | StyleClass(StyleTypeValue.SongSpecific,'Chariots of Fire','0659A908','13'), 144 | StyleClass(StyleTypeValue.SongSpecific,'Every Breath You Take','0659A8E4','12'), 145 | StyleClass(StyleTypeValue.SongSpecific,'From Santurtzi to Bilbao','0659AADC','20'), 146 | StyleClass(StyleTypeValue.SongSpecific,'Happy Birthday to You','0659AA94','1E'), 147 | StyleClass(StyleTypeValue.SongSpecific,'I\'ll Be There','0659A974','16'), 148 | StyleClass(StyleTypeValue.SongSpecific,'I\'ve Never Been to Me','0659A9BC','18'), 149 | StyleClass(StyleTypeValue.SongSpecific,'La Cucaracha','0659AAB8','1F'), 150 | StyleClass(StyleTypeValue.SongSpecific,'Material Girl','0659A950','15'), 151 | StyleClass(StyleTypeValue.SongSpecific,'Minuet in G Major','0659AA4C','1C'), 152 | StyleClass(StyleTypeValue.SongSpecific,'O-Christmas Tree','0659A89C','10'), 153 | StyleClass(StyleTypeValue.SongSpecific,'Oh My Darling Clementine','0659A830','0D'), 154 | StyleClass(StyleTypeValue.SongSpecific,'Over The Waves','0659A8C0','11'), 155 | StyleClass(StyleTypeValue.SongSpecific,'Scarborough Fair','0659A854','0E'), 156 | StyleClass(StyleTypeValue.SongSpecific,'September','0659A92C','14'), 157 | StyleClass(StyleTypeValue.SongSpecific,'Super Mario Bros','0659AC8C','2C'), 158 | StyleClass(StyleTypeValue.SongSpecific,'The Blue Danube','0659AB24','22'), 159 | StyleClass(StyleTypeValue.SongSpecific,'The Entertainer','0659AA70','1D'), 160 | StyleClass(StyleTypeValue.SongSpecific,'The Legend of Zelda','0659A9E0','19'), 161 | StyleClass(StyleTypeValue.SongSpecific,'Twinkle Twinkle Little Star','0659A7E8','0B'), 162 | StyleClass(StyleTypeValue.SongSpecific,'Wii Sports','0659AA04','1A'), 163 | StyleClass(StyleTypeValue.SongSpecific,'Wii Music','0659AB6C','24'), 164 | StyleClass(StyleTypeValue.SongSpecific,'Woman','0659A998','17'), 165 | StyleClass(StyleTypeValue.SongSpecific,'Yankee Doodle','0659A80C','0C'), 166 | StyleClass(StyleTypeValue.QuickJam,'A Capella','0659AC20','29'), 167 | StyleClass(StyleTypeValue.QuickJam,'Acoustic','0659AB93','38'), 168 | StyleClass(StyleTypeValue.QuickJam,'African Electronic','0659AB93','3C'), 169 | StyleClass(StyleTypeValue.QuickJam,'Animals!','0659AC68','2B'), 170 | StyleClass(StyleTypeValue.QuickJam,'Calypso','0659AC44','2A'), 171 | StyleClass(StyleTypeValue.QuickJam,'Exotic','0659ABFC','28'), 172 | StyleClass(StyleTypeValue.QuickJam,'Flamenco','0659AB90','25'), 173 | StyleClass(StyleTypeValue.QuickJam,'Galactic','0659ADD0','35'), 174 | StyleClass(StyleTypeValue.QuickJam,'Handbell','0659AB93','43'), 175 | StyleClass(StyleTypeValue.QuickJam,'Karate','0659ABB4','26'), 176 | StyleClass(StyleTypeValue.QuickJam,'Orchestral','0659AD64','32'), 177 | StyleClass(StyleTypeValue.QuickJam,'Parade','0659ABD8','27'), 178 | StyleClass(StyleTypeValue.QuickJam,'Rap','0659ADF4','36'), 179 | StyleClass(StyleTypeValue.QuickJam,'Samba','0659AE18','37'), 180 | StyleClass(StyleTypeValue.Menu,'Menu Style Main','0659ACB0','2D'), 181 | StyleClass(StyleTypeValue.Menu,'Menu Style Electronic','0659ACD4','2E'), 182 | StyleClass(StyleTypeValue.Menu,'Menu Style Japanese','0659ACF8','2F'), 183 | StyleClass(StyleTypeValue.Menu,'Menu Style March','0659AD1C','30'), 184 | StyleClass(StyleTypeValue.Menu,'Menu Style A Capella','0659AD40','31')] 185 | 186 | Instruments = [ 187 | InstrumentClass('Piano',0,True), 188 | InstrumentClass('Marimba',1,False), 189 | InstrumentClass('Vibraphone',2,True), 190 | InstrumentClass('Steel Drum',3,False), 191 | InstrumentClass('Dulcimer',4,False), 192 | InstrumentClass('Handbell',5,False), 193 | InstrumentClass('Harpsichord',6,False), 194 | InstrumentClass('Timpani',7,False), 195 | InstrumentClass('Galactic Piano',8,False), 196 | InstrumentClass('Toy Piano',9,False), 197 | InstrumentClass('Dog',10,False), 198 | InstrumentClass('Cat',11,False), 199 | InstrumentClass('Rapper',12,False), 200 | InstrumentClass('Guitar',13,False), 201 | InstrumentClass('Electric Guitar',14,False), 202 | InstrumentClass('Electric Bass',15,False), 203 | InstrumentClass('Double Bass',16,False), 204 | InstrumentClass('Ukulele',17,False), 205 | InstrumentClass('Banjo',18,False), 206 | InstrumentClass('Sitar',19,False), 207 | InstrumentClass('Shamisen',20,True), 208 | InstrumentClass('Harp',21,False), 209 | InstrumentClass('Galactic Guitar',22,True), 210 | InstrumentClass('Galactic Bass',23,True), 211 | InstrumentClass('Jaw Harp',24,False), 212 | InstrumentClass('Violin',25,True), 213 | InstrumentClass('Cello',26,False), 214 | InstrumentClass('Trumpet',27,False), 215 | InstrumentClass('Saxophone',28,True), 216 | InstrumentClass('Flute',29,True), 217 | InstrumentClass('Clairenet',30,True), 218 | InstrumentClass('Tuba',31,True), 219 | InstrumentClass('Accordion',32,False), 220 | InstrumentClass('Harmonica',33,False), 221 | InstrumentClass('Bagpipe',34,False), 222 | InstrumentClass('Recorder',35,False), 223 | InstrumentClass('Galactic horn',36,False), 224 | InstrumentClass('Nes',37,False), 225 | InstrumentClass('Singer',38,True), 226 | InstrumentClass('Another Singer',39,True), 227 | InstrumentClass('Basic Drums',40,True), 228 | InstrumentClass('Rock Drums',41,True), 229 | InstrumentClass('Jazz Drums',42,False), 230 | InstrumentClass('Latin Drums',43,True), 231 | InstrumentClass('Ballad Drums',44,False), 232 | InstrumentClass('Congas',45,False), 233 | InstrumentClass('Maracas',46,False), 234 | InstrumentClass('Tambourine',47,False), 235 | InstrumentClass('Cuica',48,False), 236 | InstrumentClass('Cowbell',49,False), 237 | InstrumentClass('Clap',50,False), 238 | InstrumentClass('Bells',51,False), 239 | InstrumentClass('Castanets',52,False), 240 | InstrumentClass('Guiro',53,False), 241 | InstrumentClass('Timpales',54,False), 242 | InstrumentClass('Djembe',55,False), 243 | InstrumentClass('Taiko Drum',56,True), 244 | InstrumentClass('Cheerleader',57,False), 245 | InstrumentClass('Snare Drum',58,True), 246 | InstrumentClass('Bass Drum',59,False), 247 | InstrumentClass('Galactic Drums',60,True), 248 | InstrumentClass('Galactic Congas',61,False), 249 | InstrumentClass('DJ Turntables',62,True), 250 | InstrumentClass('Kung Fu Person',63,False), 251 | InstrumentClass('Reggae Drums',64,False), 252 | InstrumentClass('Whistle',65,False), 253 | InstrumentClass('Beatbox',66,True), 254 | InstrumentClass('None',-1,False)] 255 | 256 | MainDolOffset = '59C56E' 257 | 258 | GctValues = ['00D0C0DE00D0C0DE','F000000000000000'] 259 | 260 | rseqList = ['3364C','336B8','33744','343F0','343F8','359FC','35A04','35A68','35A70','35AD4','35ADC','35B40','35B48','35BCC','35BD4','35C38','35C40','35CA4','35CAC','35D30','35D38','35DBC','35DC4','35E28','35E30', 261 | '35EB4','35EBC','35F20','35F28','35F8C','35F94','36018','36020','36064','3606C','360D0','360D8','3705C','37064','370E8','370F0','371F4','371FC','37340','37348','376CC','376D4','37738','37740', 262 | '3374C','37784','3778C','379D0','379D8','37ABC','37AC4','37B48','37B50','37BB4','37BBC','37C20','37C28','37C8C','37C94','37D18','37D20','37D64','37D6C','37E70','37E78','37EBC','37EC4','37F48','37F50'] 263 | 264 | regions = ["US","EN","JP","KR"] 265 | 266 | #Functions 267 | def AddPatch(PatchName,PatchInfo): 268 | global GamePath 269 | global CodePath 270 | global DefaultStyleMethod 271 | global ProgramPath 272 | codePathInGamePath = GamePath+'/GeckoCodes.ini' 273 | if(type(PatchName) == str): 274 | PatchName = [PatchName] 275 | PatchInfo = [PatchInfo] 276 | 277 | for patchNum in range(len(PatchName)): 278 | if(os.path.exists(codePathInGamePath)): 279 | codes = open(codePathInGamePath,'r') 280 | lineText = codes.readlines() 281 | codes.close() 282 | geckoExists = -1 283 | songExists = -1 284 | geckoEnabled = -1 285 | songEnabled = -1 286 | for num in range(len(lineText)): 287 | if(lineText[num].rstrip() == '[Gecko]'): 288 | geckoExists = num 289 | if(lineText[num].rstrip() == '$'+PatchName[patchNum]+' [WiiMusicEditor]'): 290 | songExists = num 291 | 292 | if(geckoExists == -1): 293 | lineText.insert(0,'[Gecko]\n'+'$'+PatchName[patchNum]+' [WiiMusicEditor]\n'+PatchInfo[patchNum]) 294 | elif(songExists == -1): 295 | lineText.insert(geckoExists+1,'$'+PatchName[patchNum]+' [WiiMusicEditor]\n'+PatchInfo[patchNum]) 296 | else: 297 | while True: 298 | if(len(lineText) <= songExists+1): 299 | break 300 | elif(not lineText[songExists+1][0].isnumeric() and (lineText[songExists+1][0] != 'f')): 301 | break 302 | else: 303 | lineText.pop(songExists+1) 304 | lineText.insert(songExists+1,PatchInfo[patchNum]) 305 | 306 | for num in range(len(lineText)): 307 | if(lineText[num].rstrip() == '[Gecko_Enabled]'): 308 | geckoEnabled = num 309 | if(lineText[num].rstrip() == '$'+PatchName[patchNum]): 310 | songEnabled = num 311 | 312 | if(geckoEnabled == -1): 313 | lineText.insert(len(lineText),'[Gecko_Enabled]\n'+'$'+PatchName[patchNum]+'\n') 314 | elif(songEnabled == -1): 315 | lineText.insert(geckoEnabled+1,'$'+PatchName[patchNum]+'\n') 316 | 317 | codes = open(codePathInGamePath,'w') 318 | codes.writelines(lineText) 319 | codes.close() 320 | else: 321 | codes = open(codePathInGamePath,'w') 322 | codes.write('[Gecko]\n') 323 | codes.write('$'+PatchName[patchNum]+' [WiiMusicEditor]\n') 324 | codes.write(PatchInfo[patchNum]) 325 | codes.write('[Gecko_Enabled]\n') 326 | codes.write('$'+PatchName[patchNum]+'\n') 327 | codes.close() 328 | if(os.path.isfile(CodePath)): os.remove(CodePath) 329 | copyfile(codePathInGamePath,CodePath) 330 | 331 | def GetMessagePath(path): 332 | return path+'/files/'+GetRegion(path)+'/Message/message.carc' 333 | 334 | def GetRegion(path): 335 | for i in range(len(regions)): 336 | if(os.path.isfile(path+'/files/'+regions[i]+'/Message/message.carc')): 337 | return regions[i] 338 | return regions[0] 339 | 340 | def GetGameId(path): 341 | gameIds = ["R64E01","R64P01","R64J01","R64K01"] 342 | for i in range(len(regions)): 343 | if(os.path.isfile(path+'/files/'+regions[i]+'/Message/message.carc')): 344 | return gameIds[i] 345 | return gameIds[0] 346 | 347 | def GetSaveDataPath(path): 348 | gameIds = ["00010000/52363445","00010000/52363450","00010000/5236344a","00010000/5236344b"] 349 | for i in range(len(regions)): 350 | if(os.path.isfile(path+'/files/'+regions[i]+'/Message/message.carc')): 351 | return gameIds[i] 352 | return gameIds[0] 353 | 354 | def GetSongRegionOffset(): 355 | gameIds = [0,0x200,-0x35F0,-0x428E8] 356 | for i in range(len(regions)): 357 | if(os.path.isfile(GamePath+'/files/'+regions[i]+'/Message/message.carc')): 358 | return gameIds[i] 359 | return gameIds[0] 360 | 361 | def FindGameFolder(): 362 | global GamePath 363 | global BrsarPath 364 | global MessagePath 365 | global WiiDiskFolder 366 | global CodePath 367 | global SaveDataPath 368 | if(not os.path.isdir(GamePath+'/files')): 369 | ExceptedFileExtensions = ['.iso','.wbfs'] 370 | while True: 371 | GamePath = input("\nDrag Wii Music Filesystem or ROM to Window: ").replace('&', '').replace('\'', '').replace('\"', '').strip() 372 | if(os.path.isdir(GamePath+'/DATA/files')) or (os.path.isdir(GamePath+'/files')): 373 | if(os.path.isdir(GamePath+'/DATA')): 374 | GamePath = os.path.dirname(GamePath+'/DATA/files').replace('\\','/') 375 | else: 376 | GamePath = os.path.dirname(GamePath+'/files').replace('\\','/') 377 | SaveSetting('Paths','GamePath',GamePath) 378 | BrsarPath = GamePath+'/files/sound/MusicStatic/rp_Music_sound.brsar' 379 | MessagePath = GetMessagePath(GamePath) 380 | CodePath = DolphinSaveData+"/GameSettings/"+GetGameId(GamePath)+".ini" 381 | SaveDataPath = DolphinSaveData+"/Wii/title/"+GetSaveDataPath(GamePath)+"/data" 382 | FindWiiDiskFolder() 383 | break 384 | elif(os.path.isfile(GamePath)) and (pathlib.Path(GamePath).suffix in ExceptedFileExtensions): 385 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/wit.exe\" cp --fst \"'+GamePath+'\" \"'+os.path.dirname(GamePath)+"/"+os.path.splitext(os.path.basename(GamePath))[0]+'\"') 386 | if(os.path.isdir(os.path.dirname(GamePath).replace('\\','/')+'/'+os.path.splitext(os.path.basename(GamePath))[0]+'/DATA')): 387 | GamePath = os.path.dirname(GamePath).replace('\\','/')+'/'+os.path.splitext(os.path.basename(GamePath))[0]+'/DATA' 388 | else: 389 | GamePath = os.path.dirname(GamePath).replace('\\','/')+'/'+os.path.splitext(os.path.basename(GamePath))[0] 390 | SaveSetting('Paths','GamePath',GamePath) 391 | BrsarPath = GamePath+'/files/sound/MusicStatic/rp_Music_sound.brsar' 392 | MessagePath = GetMessagePath(GamePath) 393 | CodePath = DolphinSaveData+"/GameSettings/"+GetGameId(GamePath)+".ini" 394 | SaveDataPath = DolphinSaveData+"/Wii/title/"+GetSaveDataPath(GamePath)+"/data" 395 | FindWiiDiskFolder() 396 | break 397 | else: 398 | print("\nERROR: Unable to Locate Valid Wii Music Directory") 399 | 400 | def FindWiiDiskFolder(): 401 | global GamePath 402 | global WiiDiskFolder 403 | WiiDiskFolder = os.path.basename(GamePath) 404 | if(WiiDiskFolder == 'DATA') and (os.path.isdir(GamePath)): 405 | LastSlash = len(GamePath)-1 406 | while(LastSlash >= 0) and (GamePath[LastSlash].isalpha()): 407 | LastSlash -= 1 408 | WiiDiskFolder = os.path.basename(GamePath[0:LastSlash:1]) 409 | 410 | def FindDolphin(): 411 | global DolphinPath 412 | global DolphinSaveData 413 | global CodePath 414 | global SaveDataPath 415 | if(not os.path.isfile(DolphinPath)): 416 | while True: 417 | DolphinPath = input("\nDrag Dolphin.exe or the Dolphin Folder on to the Window: ").replace('&', '').replace('\'', '').replace('\"', '').strip() 418 | if(os.path.isfile(DolphinPath+'/dolphin.exe')): 419 | DolphinPath = DolphinPath.replace('\\','/') 420 | DolphinPath = DolphinPath+'/dolphin.exe' 421 | SaveSetting('Paths','DolphinPath',DolphinPath) 422 | break 423 | elif (os.path.isfile(DolphinPath)) and (DolphinPath[len(DolphinPath)-11:len(DolphinPath):1] == 'Dolphin.exe'): 424 | DolphinPath = DolphinPath.replace('\\','/') 425 | SaveSetting('Paths','DolphinPath',DolphinPath) 426 | break 427 | else: 428 | print("\nERROR: Unable to Locate Valid Dolphin Directory") 429 | if(os.path.isfile(DolphinPath[0:len(DolphinPath)-11:1]+'portable.txt')): 430 | print('\nPortable Version Detected!') 431 | if(input('Do You Want to Set the Dolphin Save Directory to the User Folder? [y/n] ') == 'y'): 432 | DolphinSaveData = DolphinPath[0:len(DolphinPath)-11:1]+'User' 433 | if(not os.path.isdir(DolphinSaveData)): os.mkdir(DolphinSaveData) 434 | if(not os.path.isdir(DolphinSaveData+'/Wii')): os.mkdir(DolphinSaveData+'/Wii') 435 | CodePath = DolphinSaveData+"/GameSettings/"+GetGameId(GamePath)+".ini" 436 | 437 | SaveDataPath = DolphinSaveData+"/Wii/title/"+GetSaveDataPath(GamePath)+"/data" 438 | SaveSetting('Paths','DolphinSaveData',DolphinSaveData) 439 | 440 | def FindDolphinSave(): 441 | global DolphinSaveData 442 | global CodePath 443 | global SaveDataPath 444 | if(not os.path.isdir(DolphinSaveData+'/Wii')): 445 | while True: 446 | DolphinSaveData = input("\nDrag Dolphin Save Directory to Window\n(Usually located in Documents\\Dolphin Emulator or Dolphin Folder\\User): ").replace('&', '').replace('\'', '').replace('\"', '').strip() 447 | if(os.path.isdir(DolphinSaveData+'/Wii')): 448 | DolphinSaveData = DolphinSaveData.replace('\\','/') 449 | CodePath = DolphinSaveData+"/GameSettings/"+GetGameId(GamePath)+".ini" 450 | SaveDataPath = DolphinSaveData+"/Wii/title/"+GetSaveDataPath(GamePath)+"/data" 451 | SaveSetting('Paths','DolphinSaveData',DolphinSaveData) 452 | break 453 | else: 454 | print("\nERROR: Unable to Locate Valid Dolphin Save Directory") 455 | 456 | def ChangeName(SongToChange,newText): 457 | global ProgramPath 458 | if(type(newText) != str): 459 | if(Songs[SongToChange].SongType == SongTypeValue.Regular): 460 | TextOffset = ['c8','190','12c'] 461 | elif(Songs[SongToChange].SongType == SongTypeValue.Maestro): 462 | TextOffset = ['fa','1c2','15e'] 463 | elif(Songs[SongToChange].SongType == SongTypeValue.Handbell): 464 | TextOffset = ['ff','1c7','163'] 465 | else: TextOffset = ['b200'] 466 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/decode.bat\" '+MessageFolder(),capture_output=True) 467 | for typeNum in range(3): 468 | message = open(MessageFolder().replace('\"','')+'/message.d/new_music_message.txt','rb') 469 | textlines = message.readlines() 470 | message.close() 471 | if(type(newText) != str): 472 | numberToChange = Songs[SongToChange].MemOrder 473 | if(Songs[SongToChange].SongType == SongTypeValue.Maestro): 474 | array = [0,4,2,3,1] 475 | numberToChange = array[numberToChange] 476 | elif(Songs[SongToChange].SongType == SongTypeValue.Handbell): 477 | array = [0,2,3,1,4] 478 | numberToChange = array[numberToChange] 479 | else: 480 | array = [3,1,4,2,7,10,11,9,8,6,5] 481 | numberToChange = array[SongToChange] 482 | 483 | offset = format(int(TextOffset[typeNum],16)+numberToChange,'x').lower() 484 | if(type(newText) != str): 485 | offset = ' ' * (4-len(offset))+offset+'00 @' 486 | else: 487 | offset = ' ' * (4-len(offset))+offset+' @' 488 | for num in range(len(textlines)): 489 | if(textlines[num] == b' b200 @015f /\r\n'): 490 | textlines[num] = b' b200 @015f [/,4b] = Default\r\n' 491 | textlines[num+1] = b' b201 @0160 [/,4b] = Rock\r\n' 492 | textlines[num+2] = b' b202 @0161 [/,4b] = March\r\n' 493 | textlines[num+3] = b' b203 @0162 [/,4b] = Jazz\r\n' 494 | textlines[num+4] = b' b204 @0163 [/,4b] = Latin\r\n' 495 | textlines[num+5] = b' b205 @0164 [/,4b] = Reggae\r\n' 496 | textlines[num+6] = b' b206 @0165 [/,4b] = Hawaiian\r\n' 497 | textlines[num+7] = b' b207 @0166 [/,4b] = Electronic\r\n' 498 | textlines[num+8] = b' b208 @0167 [/,4b] = Classical\r\n' 499 | textlines[num+9] = b' b209 @0168 [/,4b] = Tango\r\n' 500 | textlines[num+10] = b' b20a @0169 [/,4b] = Pop\r\n' 501 | textlines[num+11] = b' b20b @016a [/,4b] = Japanese\r\n' 502 | break 503 | for num in range(len(textlines)): 504 | if offset in str(textlines[num]): 505 | while bytes('@','utf-8') not in textlines[num+1]: 506 | textlines.pop(num+1) 507 | if(type(newText) == str): 508 | textlines[num] = bytes(offset+str(textlines[num])[10:24:1]+newText+'\r\n','utf-8') 509 | else: 510 | textlines[num] = bytes(offset+str(textlines[num])[10:24:1]+newText[typeNum]+'\r\n','utf-8') 511 | break 512 | message = open(MessageFolder().replace('\"','')+'/message.d/new_music_message.txt','wb') 513 | message.writelines(textlines) 514 | message.close() 515 | if(type(newText) == str): break 516 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/encode.bat\" '+MessageFolder(),capture_output=True) 517 | 518 | def MessageFolder(): 519 | return '\"'+(os.path.dirname(MessagePath))+'\"' 520 | 521 | def LoadSetting(section,key,default): 522 | global ProgramPath 523 | ini = configparser.ConfigParser() 524 | ini.read(ProgramPath+'/settings.ini') 525 | if(ini.has_option(section, key)): 526 | return ini[section][key] 527 | else: 528 | return default 529 | 530 | def SaveSetting(section,key,value): 531 | global ProgramPath 532 | ini = configparser.ConfigParser() 533 | ini.read(ProgramPath+'/settings.ini') 534 | if(not ini.has_section(section)): 535 | ini.add_section(section) 536 | ini.set(section,key,value) 537 | print(str(ini)) 538 | with open(ProgramPath+'/settings.ini', 'w') as inifile: 539 | ini.write(inifile) 540 | 541 | def PrintSectionTitle(Text): 542 | print("\n//////////////////// "+Text+":") 543 | 544 | def CheckForUpdates(PrintMessages): 545 | global ProgramPath 546 | global beta 547 | global updateUrl 548 | if(PrintMessages): print('Checking for Updates...') 549 | version = open(ProgramPath+'/Helper/Update/Version.txt') 550 | currentVersion = version.read() 551 | version.close() 552 | try: 553 | newVersion = requests.get(updateUrl[beta]) 554 | if (newVersion.text != currentVersion): 555 | if(input("\nNew Update Avalible!\nWould you Like to Download it? [y/n] ") == 'y'): 556 | DownloadUpdate() 557 | else: 558 | if(PrintMessages): print('\nUp to Date!') 559 | except (requests.ConnectionError, requests.Timeout) as exception: 560 | if(PrintMessages): print('\nFailed to Find Updates...') 561 | 562 | def DownloadUpdate(): 563 | global beta 564 | global updateDownload 565 | global ProgramPath 566 | print('\nDownloading...') 567 | try: 568 | response = requests.get(updateDownload[beta], stream=True) 569 | total_size_in_bytes= int(response.headers.get('content-length', 0)) 570 | block_size = 1024 571 | progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True) 572 | with open('WiiMusicEditor.zip', 'wb') as file: 573 | for data in response.iter_content(block_size): 574 | progress_bar.update(len(data)) 575 | file.write(data) 576 | progress_bar.close() 577 | if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes: 578 | print("\nERROR, something went wrong\n") 579 | return True 580 | else: 581 | print('\nExtracting...\n') 582 | subprocess.run('tar -xf WiiMusicEditor.zip') 583 | newPath = 'WiiMusicEditor-main' 584 | if(not os.path.isdir(newPath)): 585 | newPath = 'WiiMusicEditor-beta' 586 | os.rename(newPath, 'WiiMusicEditorNew') 587 | subprocess.run(ProgramPath+'/WiiMusicEditorNew/Helper/Update/Update.bat') 588 | quit() 589 | return False 590 | except (requests.ConnectionError, requests.Timeout) as exception: 591 | print('\nFailed to Download File...\n') 592 | return True 593 | 594 | def SelectStyleInstrument(PartString,IsPercussion): 595 | global Styles 596 | global Selection 597 | global normalInstrumentNumber 598 | global unsafeMode 599 | global Instruments 600 | global NormalStyleSelected 601 | print('') 602 | while True: 603 | PartType = input("What\'s the Instrument Number you want for the "+PartString+": ") 604 | if(PartType.isnumeric()): 605 | PartType = int(PartType) 606 | if(IsPercussion) and (not unsafeMode): PartType = PartType + normalInstrumentNumber 607 | if(unsafeMode): 608 | if(PartType == len(Instruments)-1): 609 | PartType = 'ffffffff' 610 | break 611 | elif (PartType < len(Instruments)): 612 | PartType = Instruments[PartType].Number 613 | PartType = format(PartType,'x').upper() 614 | PartType = '0'*(8-len(PartType))+PartType 615 | break 616 | else: 617 | print("\nERROR: Not a Valid Number\n") 618 | elif((PartType == normalInstrumentNumber and not IsPercussion) or (PartType == len(Instruments)-1 and IsPercussion)) and (NormalStyleSelected): 619 | PartType = 'ffffffff' 620 | break 621 | elif((PartType < normalInstrumentNumber) != IsPercussion) and (PartType < len(Instruments)) and ((NormalStyleSelected) or (Instruments[PartType].InMenu)): 622 | PartType = Instruments[PartType].Number 623 | PartType = format(PartType,'x').upper() 624 | PartType = '0'*(8-len(PartType))+PartType 625 | break 626 | else: 627 | print("\nERROR: Not a Valid Number\n") 628 | else: 629 | print("\nERROR: Not a Valid Number\n") 630 | return PartType 631 | 632 | def MakeSelection(MessageRangeArray): 633 | while True: 634 | TempSelection = input("\n"+MessageRangeArray[0]+": ") 635 | if(TempSelection.isnumeric()) and ((len(MessageRangeArray) <= 1) or ((int(TempSelection) <= MessageRangeArray[2]) and (int(TempSelection) >= MessageRangeArray[1]))): 636 | break 637 | else: 638 | print("\nERROR: Not a Valid Number") 639 | return int(TempSelection) 640 | 641 | def ChangeDefaultAnswer(ResponseOptions,iniKey): 642 | if(len(ResponseOptions) == 2): 643 | if(ResponseOptions.index(iniKey[1]) == 0): Selection = 1 644 | else: Selection = 0 645 | else: 646 | for num in range(len(ResponseOptions)): 647 | print('(#'+str(num)+') '+ResponseOptions[num]) 648 | 649 | Selection = MakeSelection(['Select an Option',0,len(ResponseOptions)-1]) 650 | SaveSetting('Default Answers', iniKey[0], ResponseOptions[Selection]) 651 | print('') 652 | return ResponseOptions[Selection] 653 | 654 | def CreateGct(): 655 | global GamePath 656 | global ProgramPath 657 | patches = open(GamePath+'/GeckoCodes.ini') 658 | textlines = patches.readlines() 659 | patches.close() 660 | codes = GctValues[0] 661 | for text in textlines: 662 | if(text[0].isalpha() or text[0].isnumeric()): 663 | codes = codes + text.replace(' ','').strip() 664 | codes = codes+GctValues[1] 665 | patch = open(ProgramPath+'/'+GetGameId(GamePath)+'.gct','wb') 666 | patch.write(bytes.fromhex(codes)) 667 | patch.close() 668 | 669 | def LoadNormalFiles(): 670 | global ProgramPath 671 | global GamePath 672 | ExceptedFileExtensions = ['.iso','.wbfs'] 673 | TempPath = GamePath 674 | while True: 675 | if(TempPath == ""): 676 | TempPath = input("\nDrag an UNALTERED Wii Music Directory or a Wii Music Disk on to the Window: ").replace('&', '').replace('\'', '').replace('\"', '').strip() 677 | if(os.path.isdir(TempPath+'/DATA/files')) or (os.path.isdir(TempPath+'/files')): 678 | if(os.path.isdir(TempPath+'/DATA')): 679 | TempPath = os.path.dirname(TempPath+'/DATA/files').replace('\\','/') 680 | else: 681 | TempPath = os.path.dirname(TempPath+'/files').replace('\\','/') 682 | CopyUnalteredFiles(TempPath) 683 | break 684 | elif(os.path.isfile(TempPath)) and (pathlib.Path(TempPath).suffix in ExceptedFileExtensions): 685 | if(os.path.isdir(ProgramPath+"/Disk")): rmtree(ProgramPath+"/Disk") 686 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/wit.exe\" cp --fst \"'+TempPath+'\" \"'+ProgramPath+'/Disk/\"') 687 | TempPath = ProgramPath+'/Disk/DATA' 688 | CopyUnalteredFiles(TempPath) 689 | rmtree(ProgramPath+"/Disk") 690 | break 691 | else: 692 | if(TempPath != GamePath): print("\nERROR: Unable to Locate Valid Wii Music Directory") 693 | TempPath = "" 694 | 695 | def CopyUnalteredFiles(dir): 696 | global ProgramPath 697 | if(not os.path.isdir(ProgramPath+'/Helper/Backup')): os.mkdir(ProgramPath+'/Helper/Backup') 698 | if(not os.path.isfile(ProgramPath+'/Helper/Backup/rp_Music_sound.brsar')): 699 | copyfile(dir+'/files/Sound/MusicStatic/rp_Music_sound.brsar',ProgramPath+'/Helper/Backup/rp_Music_sound.brsar') 700 | if(not os.path.isfile(ProgramPath+'/Helper/Backup/message.carc')): 701 | copyfile(GetMessagePath(dir),ProgramPath+'/Helper/Backup/message.carc') 702 | if(not os.path.isfile(ProgramPath+'/Helper/Backup/main.dol')): 703 | copyfile(dir+'/sys/main.dol',ProgramPath+'/Helper/Backup/main.dol') 704 | 705 | def CopyFileSafe(copyFrom,copyTo): 706 | if(os.path.isfile(copyTo)): os.remove(copyTo) 707 | copyfile(copyFrom,copyTo) 708 | 709 | def LoadNewFile(dir): 710 | global GctValues 711 | global GamePath 712 | geckoCodeFile = "" 713 | if(os.path.isfile(dir)): 714 | files = [dir] 715 | else: 716 | files = os.listdir(dir) 717 | 718 | for currentFile in files: 719 | file = dir+'/'+currentFile 720 | if(file.endswith('.brsar')): 721 | CopyFileSafe(file,GamePath+'/files/Sound/MusicStatic/rp_Music_sound.brsar') 722 | print('\nImported .brsar') 723 | elif(file.endswith('.carc')): 724 | CopyFileSafe(file,GetMessagePath(GamePath)) 725 | print('\nImported .carc') 726 | elif(file.endswith('.dol')): 727 | CopyFileSafe(file,GamePath+'/sys/main.dol') 728 | print('\nImported .dol') 729 | elif(file.endswith('.gct')): 730 | if(geckoCodeFile == ""): geckoCodeFile = file 731 | elif(file.endswith('.ini')): 732 | geckoCodeFile = file 733 | 734 | if(geckoCodeFile != ""): 735 | if(geckoCodeFile.endswith('.gct')): 736 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): os.remove(GamePath+'/GeckoCodes.ini') 737 | codes = open(file,'rb') 738 | codes.seek(8) 739 | values = codes.read(os.stat(file).st_size-16).hex() 740 | codes.close() 741 | codes = open(GamePath+'/GeckoCodes.ini','w') 742 | codes.write('[Gecko]\n$Imported Geckocodes [WiiMusicEditor]\n'+values+'\n[Gecko_Enabled]\n$Imported Geckocodes\n') 743 | codes.close() 744 | print('\nImported .gct') 745 | else: 746 | CopyFileSafe(geckoCodeFile,GamePath+'/GeckoCodes.ini') 747 | print('\nImported .ini') 748 | 749 | def ReplaceSong(positionOffset,listOffset,replacementArray,infoArray): 750 | sizeDifference = 0 751 | brsar = open(BrsarPath, "rb") 752 | brsar.seek(positionOffset) 753 | currentSpot = int.from_bytes(brsar.read(4),'big') 754 | if(listOffset != -1): 755 | posOffset = [] 756 | lenOffset = [] 757 | data = [] 758 | for num in range(len(replacementArray)-1): 759 | brsar.seek(listOffset+24*replacementArray[num]) 760 | posOffset.append(brsar.read(4)) 761 | lenOffset.append(brsar.read(4)) 762 | brsar.seek(0) 763 | data.append(brsar.read(currentSpot+int.from_bytes(posOffset[0],'big'))) 764 | for num in range(len(replacementArray)-2): 765 | brsar.seek(currentSpot+int.from_bytes(posOffset[num],'big')+int.from_bytes(lenOffset[num],'big')) 766 | data.append(brsar.read(int.from_bytes(posOffset[num+1],'big')-int.from_bytes(posOffset[num],'big')-int.from_bytes(lenOffset[num],'big'))) 767 | brsar.seek(currentSpot+int.from_bytes(posOffset[len(posOffset)-1],'big')+int.from_bytes(lenOffset[len(lenOffset)-1],'big')) 768 | data.append(brsar.read()) 769 | brsar.close() 770 | for num in range(len(replacementArray)-1): 771 | if(num == 0): 772 | infoToWrite = data[num]+BrseqInfo[infoArray[num]] 773 | else: 774 | infoToWrite = infoToWrite+data[num]+BrseqInfo[infoArray[num]] 775 | infoToWrite = infoToWrite+data[len(replacementArray)-1] 776 | brsar = open(BrsarPath, "wb") 777 | brsar.write(infoToWrite) 778 | brsar.close() 779 | brsar = open(BrsarPath, "r+b") 780 | for num in range(replacementArray[0],replacementArray[len(replacementArray)-1]): 781 | if(sizeDifference != 0): 782 | brsar.seek(listOffset+24*num) 783 | size = brsar.read(4) 784 | brsar.seek(listOffset+24*num) 785 | brsar.write((int.from_bytes(size,"big")+sizeDifference).to_bytes(4, 'big')) 786 | if (num in replacementArray): 787 | brsar.seek(listOffset+4+24*num) 788 | sizeDifference += int(BrseqLength[infoArray[replacementArray.index(num)]],16)-int.from_bytes(brsar.read(4),"big") 789 | brsar.seek(listOffset+4+24*num) 790 | brsar.write(int(BrseqLength[infoArray[replacementArray.index(num)]],16).to_bytes(4, 'big')) 791 | for offset in rseqList: 792 | if(int(offset,16) > positionOffset): 793 | brsar.seek(int(offset,16)) 794 | size = brsar.read(4) 795 | brsar.seek(int(offset,16)) 796 | brsar.write((int.from_bytes(size,"big")+sizeDifference).to_bytes(4, 'big')) 797 | for offset in [8,positionOffset+4]: 798 | brsar.seek(offset) 799 | size = brsar.read(4) 800 | brsar.seek(offset) 801 | brsar.write((int.from_bytes(size,"big")+sizeDifference).to_bytes(4, 'big')) 802 | else: 803 | data = [] 804 | brsar.seek(0) 805 | data.append(brsar.read(currentSpot)) 806 | brsar.seek(positionOffset+4) 807 | brsar.seek(currentSpot+int.from_bytes(brsar.read(4),'big')) 808 | data.append(brsar.read()) 809 | brsar.close() 810 | brsar = open(BrsarPath, "wb") 811 | brsar.write(data[0]+BrseqInfo[infoArray]+data[1]) 812 | brsar.close() 813 | brsar = open(BrsarPath, "r+b") 814 | brsar.seek(positionOffset+4) 815 | sizeDifference = int(BrseqLength[infoArray],16)-int.from_bytes(brsar.read(4),"big") 816 | for offset in rseqList: 817 | if(int(offset,16) > positionOffset): 818 | brsar.seek(int(offset,16)) 819 | size = brsar.read(4) 820 | brsar.seek(int(offset,16)) 821 | brsar.write((int.from_bytes(size,"big")+sizeDifference).to_bytes(4, 'big')) 822 | for offset in [8,positionOffset+4]: 823 | brsar.seek(offset) 824 | size = brsar.read(4) 825 | brsar.seek(offset) 826 | brsar.write((int.from_bytes(size,"big")+sizeDifference).to_bytes(4, 'big')) 827 | brsar.close() 828 | 829 | def ReplaceEverything(startOffset): 830 | brsar = open(BrsarPath, "rb") 831 | brsar.seek(startOffset) 832 | brsar.seek(int.from_bytes(brsar.read(4),'big')) 833 | if(brsar.read(4).hex() == '52534551'): 834 | brsar.seek(startOffset-20) 835 | if(brsar.read(4).hex() != 'ffffffff'): 836 | brsar.seek(startOffset+24) 837 | number = int.from_bytes(brsar.read(4),'big') 838 | listOffset = startOffset+32 839 | temp = -1 840 | while(temp != bytes(6)): 841 | listOffset += 4 842 | brsar.seek(listOffset) 843 | temp = brsar.read(6) 844 | brsar.close() 845 | temp = [] 846 | for num in range(number+1): 847 | temp.append(num) 848 | print(str(startOffset)+' - Normal') 849 | ReplaceSong(startOffset,listOffset,temp,[0]*number) 850 | else: 851 | print(str(startOffset)+' - Single') 852 | brsar.close() 853 | ReplaceSong(startOffset,-1,-1,0) 854 | else: 855 | print(str(startOffset)+' - Skipped') 856 | brsar.close() 857 | 858 | #Default Paths 859 | ProgramPath = os.path.dirname(os.path.abspath(__file__)).replace('\\','/') 860 | GamePath = LoadSetting('Paths','GamePath','None') 861 | BrsarPath = GamePath+'/files/sound/MusicStatic/rp_Music_sound.brsar' 862 | MessagePath = GetMessagePath(GamePath) 863 | DolphinSaveData = LoadSetting('Paths','DolphinSaveData',"C:/Users/"+getpass.getuser()+"/Documents/Dolphin Emulator") 864 | CodePath = DolphinSaveData+"/GameSettings/"+GetGameId(GamePath)+".ini" 865 | SaveDataPath = DolphinSaveData+"/Wii/title/"+GetSaveDataPath(GamePath)+"/data" 866 | DolphinPath = LoadSetting('Paths','DolphinPath','None') 867 | WiiDiskFolder = '' 868 | FindWiiDiskFolder() 869 | 870 | #Update 871 | beta = int(LoadSetting('Updates', 'Branch', '0')) 872 | AutoUpdate = bool(int(LoadSetting('Updates', 'AutoUpdate', '1'))) 873 | uptodate = False 874 | updateUrl = ['https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/main/Helper/Update/Version.txt', 875 | 'https://raw.githubusercontent.com/BenjaminHalko/WiiMusicEditor/beta/Helper/Update/Version.txt'] 876 | updateDownload = ['https://github.com/BenjaminHalko/WiiMusicEditor/archive/refs/heads/main.zip', 877 | 'https://github.com/BenjaminHalko/WiiMusicEditor/archive/refs/heads/beta.zip'] 878 | 879 | #Default Answers 880 | DefaultWantToReplaceSong = LoadSetting('Default Answers', 'Want To Replace Song', 'Yes') 881 | DefaultReplacingReplacedSong = LoadSetting('Default Answers', 'Replacing Replaced Song', 'Yes') 882 | DefaultReplaceSongNames = LoadSetting('Default Answers', 'Replace Song Names', 'Ask') 883 | DefaultUseAutoLengthTempo = LoadSetting('Default Answers', 'Use Auto Length and Tempo', 'Ask') 884 | DefaultLoadSongScore = LoadSetting('Default Answers', 'Load Song And Score', 'No') 885 | DefaultImportSeperateBrsar = LoadSetting('Default Answers', 'Seperate Brsar', 'None Selected') 886 | 887 | #Unsafe Mode 888 | unsafeMode = bool(int(LoadSetting('Unsafe Mode','Unsafe Mode','0'))) 889 | 890 | #Main Loop 891 | while True: 892 | #Find Branch 893 | if(not os.path.exists('settings.ini')): 894 | if(os.path.exists('README.md')): 895 | readme = open('README.md') 896 | if('Beta' in readme.read()): 897 | beta = 1 898 | SaveSetting('Updates', 'Branch', '1') 899 | else: 900 | beta = 0 901 | SaveSetting('Updates', 'Branch', '0') 902 | readme.close() 903 | #Finish Updates 904 | if(not uptodate): 905 | if(os.path.isdir('WiiMusicEditorNew') or os.path.isfile('WiiMusicEditor.zip')): 906 | print('Finishing Up...\n') 907 | if(os.path.isdir('WiiMusicEditorNew')): rmtree('WiiMusicEditorNew') 908 | if(os.path.isfile('WiiMusicEditor.zip')): os.remove('WiiMusicEditor.zip') 909 | uptodate = True 910 | 911 | #Title 912 | print("//////////////////////////////") 913 | print("// //") 914 | print("// Welcome To //") 915 | print("// The Wii //") 916 | print("// Music Editor //") 917 | print("// //") 918 | print("//////////////////////////////\n") 919 | 920 | if(AutoUpdate) and (not uptodate): 921 | uptodate = True 922 | CheckForUpdates(False) 923 | 924 | #First Run 925 | if(GamePath == 'None'): 926 | print('\nThanks for Downloading the Wii Music Editor!') 927 | print('\nLet\'s Setup Some File Paths for You!') 928 | FindGameFolder() 929 | if((not os.path.isfile(ProgramPath+'/Helper/Backup/rp_Music_sound.brsar')) or (not os.path.isfile(ProgramPath+'/Helper/Backup/message.carc')) or (not os.path.isfile(ProgramPath+'/Helper/Backup/main.dol'))): 930 | LoadNormalFiles() 931 | if(input('\nWould You Like to Specify a Dolphin Directory? [y/n] ') == 'y'): 932 | FindDolphin() 933 | elif((not os.path.isfile(ProgramPath+'/Helper/Backup/rp_Music_sound.brsar')) or (not os.path.isfile(ProgramPath+'/Helper/Backup/message.carc')) or (not os.path.isfile(ProgramPath+'/Helper/Backup/main.dol'))): 934 | LoadNormalFiles() 935 | 936 | #Options 937 | PrintSectionTitle('Options') 938 | print("(#1) Add Custom Song To Wii Music") 939 | print("(#2) Change Song Names") 940 | print("(#3) Edit Styles") 941 | print("(#4) Advanced Tools") 942 | print("(#5) Load Wii Music") 943 | print("(#6) Revert Changes") 944 | print("(#7) Overwrite Save File With 100% Save") 945 | print("(#8) Download Pre-Made Custom Songs") 946 | print("(#9) Help") 947 | print("(#10) Settings") 948 | print("(#11) Credits") 949 | 950 | Selection = MakeSelection(['Please Select an Option',1,11]) 951 | 952 | 953 | if(Selection == 1): #////////////////////////////////////////Add Custom Song 954 | #Load Files 955 | FindGameFolder() 956 | FindDolphinSave() 957 | 958 | #Applied Custom Songs 959 | appliedCustomSongs = [] 960 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): 961 | codes = open(GamePath+'/GeckoCodes.ini') 962 | textlines = codes.readlines() 963 | codes.close() 964 | for text in textlines: 965 | if('[WiiMusicEditor]' in text) and ('Style' not in text): 966 | appliedCustomSongs.append(text[1:len(text)-29:1]) 967 | 968 | #Song List 969 | PrintSectionTitle('Song List') 970 | 971 | for num in range(len(Songs)): 972 | if(Songs[num].Name in appliedCustomSongs): 973 | print(Fore.YELLOW+'(#'+str(num)+') '+str(Songs[num].Name)+' ~[Already Replaced]~'+Style.RESET_ALL) 974 | else: 975 | print('(#'+str(num)+') '+str(Songs[num].Name)) 976 | time.sleep(0.005) 977 | 978 | #Song Selection 979 | PrintSectionTitle('Song Selection') 980 | while True: 981 | SongSelected = input("Enter the Song Number you want to Replace: ") 982 | if(SongSelected.isnumeric()) and (int(SongSelected) < len(Songs)): 983 | SongSelected = int(SongSelected) 984 | if(Songs[SongSelected].Name not in appliedCustomSongs) or (DefaultReplacingReplacedSong == 'No') or (input('\nWARNING: You Have Already Replaced this Song Before! Are You Sure You Want to Replace this Song [y/n] ') == 'y'): 985 | break 986 | else: 987 | print('Aborted...\n') 988 | else: 989 | print("\nERROR: Not a Valid Number\n") 990 | 991 | #Load Brseq 992 | ExceptedSongExtensions = ['.midi','.mid','.brseq','.rseq'] 993 | BrseqInfo = [] 994 | BrseqLength = [] 995 | for num in range(2): 996 | while True: 997 | extraString = '' 998 | if(DefaultLoadSongScore == 'Yes') and (Songs[SongSelected].SongType == SongTypeValue.Regular): 999 | extraString = ' [SONG]' 1000 | if(num == 1): extraString = ' [SCORE]' 1001 | BrseqPath = input("\nDrag File to Window (MIDIs, BRSEQ, & RSEQ Files only)"+extraString+": ").replace('&', '').replace('\'', '').replace('\"', '').strip() 1002 | if(os.path.isfile(BrseqPath)) and (pathlib.Path(BrseqPath).suffix in ExceptedSongExtensions): 1003 | break 1004 | else: 1005 | print("\nERROR: Not A Valid File!") 1006 | 1007 | with tempfile.TemporaryDirectory() as directory: 1008 | prefix = pathlib.Path(BrseqPath).suffix 1009 | if(prefix == '.mid'): prefix = '.midi' 1010 | copyfile(BrseqPath,directory+'/z'+prefix) 1011 | if(os.path.isfile(directory+'/z.rseq')): 1012 | subprocess.run('\"'+ProgramPath+'/Helper/SequenceCmd/GotaSequenceCmd.exe\" assemble \"'+directory+'/z.rseq\"') 1013 | if(os.path.isfile(directory+'/z.brseq')): 1014 | if(num == 0): subprocess.run('\"'+ProgramPath+'/Helper/SequenceCmd/GotaSequenceCmd.exe\" to_midi \"'+directory+'/z.brseq\"') 1015 | else: 1016 | subprocess.run('\"'+ProgramPath+'/Helper/SequenceCmd/GotaSequenceCmd.exe\" from_midi \"'+directory+'/z.midi\"') 1017 | if(num == 0) : 1018 | Tempo = 'Could Not Locate' 1019 | Length = 'Could Not Locate' 1020 | if(os.path.isfile(directory+"/z.midi")): 1021 | mid = mido.MidiFile(directory+"/z.midi") 1022 | 1023 | for msg in mid.tracks[0]: 1024 | if(msg.type == 'set_tempo'): 1025 | Tempo = floor(mido.tempo2bpm(msg.tempo)) 1026 | Length = str(ceil(mid.length*Tempo/60)) 1027 | Tempo = str(Tempo) 1028 | Brseq = open(directory+"/z.brseq","rb") 1029 | Brseq.seek(0) 1030 | BrseqInfo.append(Brseq.read()) 1031 | Brseq.close() 1032 | BrseqLength.append(format(os.stat(directory+"/z.brseq").st_size,'x').upper()) 1033 | if(num == 0): 1034 | if(Songs[SongSelected].SongType == SongTypeValue.Maestro): break 1035 | elif(Songs[SongSelected].SongType == SongTypeValue.Handbell): break 1036 | elif(DefaultLoadSongScore != 'Yes'): 1037 | BrseqInfo.append(BrseqInfo[0]) 1038 | BrseqLength.append(BrseqLength[0]) 1039 | break 1040 | 1041 | 1042 | 1043 | #Length, Tempo, Time Signature Patch 1044 | if(Songs[SongSelected].SongType != SongTypeValue.Menu): 1045 | #Brseq Info 1046 | PrintSectionTitle("File Info") 1047 | print("Number of Beats: "+Length) 1048 | print("Tempo: "+Tempo) 1049 | 1050 | PrintSectionTitle("Length, Tempo, Time Signature Patch") 1051 | AutoFill = 'n' 1052 | if((Tempo != 'Could Not Locate') or ((Length != '0') and (Length != 'Could Not Locate'))) and (DefaultUseAutoLengthTempo != 'No'): 1053 | MetaDataFound = '' 1054 | if(Length != 'Could Not Locate') and (Length != '0'): MetaDataFound = 'Length' 1055 | if(Tempo != 'Could Not Locate'): 1056 | if(MetaDataFound == ''): MetaDataFound = 'Tempo' 1057 | else: MetaDataFound = MetaDataFound+', Tempo' 1058 | print('Metadata Found: '+MetaDataFound) 1059 | if(DefaultUseAutoLengthTempo == 'Yes'): 1060 | AutoFill = 'y' 1061 | else: 1062 | AutoFill = input("\nI've found some Metadata for you! Would you like to Autofill? [y/n] ") 1063 | 1064 | if(AutoFill != 'y') or (Length == '0'): 1065 | Length = MakeSelection(['\nHow Many Measures in Your Song']) 1066 | else: 1067 | Length = format(int(Length),'x').upper() 1068 | 1069 | if(AutoFill != 'y') or (not Tempo.isnumeric()): 1070 | Tempo = MakeSelection(['What is the Tempo of Your Song']) 1071 | 1072 | Tempo = format(int(Tempo),'x').upper() 1073 | 1074 | while True: 1075 | TimeSignature = input("\nWhat's the Time Signature of your Song? (4 = 4/4, 3 = 3/4): ") 1076 | if(TimeSignature == '3') or (TimeSignature == '4'): 1077 | break 1078 | else: 1079 | print("\nERROR: Please Press Ether 4 or 3") 1080 | 1081 | if(AutoFill != 'y') or (Length == 0): 1082 | Length = format(int(Length) * int(TimeSignature),'x').upper() 1083 | 1084 | #Final Writting 1085 | LengthCode = '0'+format(int(Songs[SongSelected].MemOffset,16)+GetSongRegionOffset()+6,'x').lower()+' '+'0'*(8-len(Length))+Length+'\n' 1086 | TempoCode = '0'+format(int(Songs[SongSelected].MemOffset,16)+GetSongRegionOffset()+10,'x').lower()+' '+'0'*(8-len(Tempo))+Tempo+'\n' 1087 | TimeCode = '0'+format(int(Songs[SongSelected].MemOffset,16)+GetSongRegionOffset(),'x').lower()+' 00000'+TimeSignature+'00\n' 1088 | 1089 | if(DefaultWantToReplaceSong != 'No') or (input('\nAre You Sure You Want to Override '+Songs[SongSelected].Name+'?\nYou CANNOT restore the song if you don\'t have a backup! [y/n] ') == 'y'): 1090 | #Brsar Writing 1091 | if(Songs[SongSelected].SongType == SongTypeValue.Regular): 1092 | ReplaceSong(0x033744,0x033A84,[Songs[SongSelected].MemOrder*2,Songs[SongSelected].MemOrder*2+1,100],[0,1]) 1093 | if(Songs[SongSelected].Name == 'Do-Re-Mi'): 1094 | ReplaceSong(0x0343F0,0x034988,[18,19,113,155,156,157,158,159,160,161,162,175],[0,1,0,0,0,0,0,0,0,0,0]) 1095 | ReplaceSong(0x0360D0,0x0364C8,[18,19,113,123],[0,1,0]) 1096 | AddPatch(Songs[SongSelected].Name+' Song Patch',LengthCode+TempoCode+TimeCode) 1097 | elif(Songs[SongSelected].SongType == SongTypeValue.Menu): 1098 | ReplaceSong(0x037D64,0x037DBC,[0,1,2,3,4,5,6,7],[0,1,1,1,1,1,1]) 1099 | elif(Songs[SongSelected].SongType == SongTypeValue.Maestro): 1100 | ReplaceSong(0x0370E8,0x037140,[Songs[SongSelected].MemOrder+2,7],[0]) 1101 | AddPatch(Songs[SongSelected].Name+' Song Patch',LengthCode+TempoCode+TimeCode) 1102 | elif(Songs[SongSelected].SongType == SongTypeValue.Handbell): 1103 | ReplaceSong(0x037340,0x037438,[Songs[SongSelected].MemOrder*5+2,Songs[SongSelected].MemOrder*5+3,Songs[SongSelected].MemOrder*5+4,Songs[SongSelected].MemOrder*5+5,Songs[SongSelected].MemOrder*5+6,27],[0,0,0,0,0]) 1104 | LengthCode = '0'+format(int(Songs[SongSelected].MemOffset,16)+GetSongRegionOffset(),'x').lower()+' '+'0'*(8-len(Length))+Length+'\n' 1105 | LengthCode2 = '0'+format(int(Songs[SongSelected].MemOffset,16)+GetSongRegionOffset()+4,'x').lower()+' '+'0'*(8-len(Length))+Length+'\n' 1106 | MeasureCode = '0'+format(int(Songs[SongSelected].MemOffset,16)+GetSongRegionOffset()+24,'x').lower()+' '+'00000000\n' 1107 | AddPatch(Songs[SongSelected].Name+' Song Patch',LengthCode+LengthCode2+MeasureCode) 1108 | AddPatch('Rapper Crash Fix','043B0BBB 881C0090\n043B0BBF 7C090000\n043B0BC3 4081FFBC\n043B0BC7 881C00D6\n') 1109 | print("\nPatch Complete!") 1110 | time.sleep(0.5) 1111 | if(DefaultReplaceSongNames != 'No') and (Songs[SongSelected].SongType != SongTypeValue.Menu) and ((DefaultReplaceSongNames == 'Yes') or (input('\nWould you like to change the Song Text? [y/n] ') == 'y')): 1112 | ChangeName(SongSelected,[input('\nWhat\'s the title of your Song: '),input('\nWhat\'s the description of your Song (Use \\n for new lines): '),input('\nWhat\'s the genre of your Song: ')]) 1113 | print("\nEditing Successful!\n") 1114 | else: print('') 1115 | else: 1116 | print("Aborted...") 1117 | elif(Selection == 2): #////////////////////////////////////////Change Song Names 1118 | #Load Files 1119 | FindGameFolder() 1120 | 1121 | #Song List 1122 | PrintSectionTitle("Song List") 1123 | maxNumber = 0 1124 | for num in range(len(Songs)): 1125 | if(Songs[num].SongType != SongTypeValue.Menu): 1126 | print('(#'+str(num)+') '+str(Songs[num].Name)) 1127 | time.sleep(0.005) 1128 | maxNumber += 1 1129 | 1130 | #Song Selection 1131 | PrintSectionTitle("Song Selection") 1132 | Selection = MakeSelection(['Which Song Do You Want To Change The Name Of',0,maxNumber-1]) 1133 | 1134 | ChangeName(Selection,[input('\nWhat\'s the title of your Song: '),input('\nWhat\'s the description of your Song (Use \\n for new lines): '),input('\nWhat\'s the genre of your Song: ')]) 1135 | print("\nEditing Successful!\n") 1136 | elif(Selection == 3): #////////////////////////////////////////Change Style 1137 | FindGameFolder() 1138 | FindDolphinSave() 1139 | 1140 | #Applied Custom Songs 1141 | appliedCustomSongs = [] 1142 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): 1143 | codes = open(GamePath+'/GeckoCodes.ini') 1144 | textlines = codes.readlines() 1145 | codes.close() 1146 | for text in textlines: 1147 | if('[WiiMusicEditor]' in text) and (' Style' in text): 1148 | appliedCustomSongs.append(text[1:len(text)-30:1]) 1149 | MenuStyles = 5 1150 | columnSplit = 4 1151 | MaxStyles = [] 1152 | StyleTable = [[['Global Styles','Song Specific Styles','Quick Jam Styles','Menu Styles']],[['Replace All Styles']]] 1153 | for num in range(ceil(NumberOfStyleTypes/columnSplit)): 1154 | MaxStyles.append([0]*min(columnSplit,NumberOfStyleTypes-num*columnSplit)) 1155 | print('') 1156 | for num in range(len(Styles)): 1157 | number = Styles[num].StyleType 1158 | array = 0 1159 | while(number >= columnSplit): 1160 | array += 1 1161 | number -= columnSplit 1162 | MaxStyles[array][number] += 1 1163 | if(MaxStyles[array][number] >= len(StyleTable[array])): 1164 | StyleTable[array].append(['']*min(columnSplit,NumberOfStyleTypes-array*columnSplit)) 1165 | if(Styles[num].Name in appliedCustomSongs): 1166 | StyleTable[array][MaxStyles[array][number]][number] = Fore.YELLOW+'(#'+str(num)+') '+Styles[num].Name+Style.RESET_ALL 1167 | else: 1168 | StyleTable[array][MaxStyles[array][number]][number] = '(#'+str(num)+') '+Styles[num].Name 1169 | while(len(StyleTable[floor(NumberOfStyleTypes/columnSplit)]) <= 2): 1170 | StyleTable[floor(NumberOfStyleTypes/columnSplit)].append(['']*(NumberOfStyleTypes-floor(NumberOfStyleTypes/columnSplit)*columnSplit)) 1171 | StyleTable[floor(NumberOfStyleTypes/columnSplit)][1][NumberOfStyleTypes-floor(NumberOfStyleTypes/columnSplit)*columnSplit-1] = '(#'+str(len(Styles))+') Replace All Non-Menu Styles' 1172 | StyleTable[floor(NumberOfStyleTypes/columnSplit)][2][NumberOfStyleTypes-floor(NumberOfStyleTypes/columnSplit)*columnSplit-1] = '(#'+str(len(Styles)+1)+') Replace All Menu Styles' 1173 | for num in range(len(StyleTable)): 1174 | print(tabulate(StyleTable[num], headers='firstrow')) 1175 | print('') 1176 | Selection = MakeSelection(['What\'s the Style Number you want to change',0,len(Styles)+2]) 1177 | NormalStyleSelected = (Selection < len(Styles)-MenuStyles) or (Selection == len(Styles)) 1178 | PrintSectionTitle("Instrument List") 1179 | normalInstrumentNumber = 40 1180 | 1181 | if(not unsafeMode): 1182 | for num in range(normalInstrumentNumber+1): 1183 | realNum = num 1184 | if(num == normalInstrumentNumber): 1185 | num = len(Instruments)-1 1186 | if (not Instruments[num].InMenu) and (not NormalStyleSelected): 1187 | print(Fore.RED+'(UNAVALIBLE) '+str(Instruments[num].Name)+Style.RESET_ALL) 1188 | else: 1189 | print('(#'+str(realNum)+') '+str(Instruments[num].Name)) 1190 | time.sleep(0.005) 1191 | else: 1192 | for num in range(len(Instruments)): 1193 | if ((Instruments[num].InMenu) and (not NormalStyleSelected) and (num < normalInstrumentNumber)) or (((num < normalInstrumentNumber) or (num == len(Instruments)-1)) and (NormalStyleSelected)): 1194 | print(Style.RESET_ALL+'(#'+str(num)+') '+str(Instruments[num].Name)) 1195 | elif (unsafeMode): 1196 | if((Instruments[num].InMenu) and (not NormalStyleSelected)) or (NormalStyleSelected): 1197 | print(Fore.YELLOW+'(#'+str(num)+') '+str(Instruments[num].Name)+Style.RESET_ALL) 1198 | else: 1199 | print(Fore.RED+'(#'+str(num)+') '+str(Instruments[num].Name)+Style.RESET_ALL) 1200 | time.sleep(0.005) 1201 | PrintSectionTitle("Instrument Selection") 1202 | Melody = SelectStyleInstrument('Melody',False) 1203 | Harmony = SelectStyleInstrument('Harmony',False) 1204 | Chord = SelectStyleInstrument('Chord',False) 1205 | Bass = SelectStyleInstrument('Bass',False) 1206 | PrintSectionTitle("Instrument List") 1207 | if(not unsafeMode): 1208 | for num in range(40,len(Instruments)): 1209 | if (not Instruments[num].InMenu) and (not NormalStyleSelected): 1210 | print(Fore.RED+'(UNAVALIBLE) '+Instruments[num].Name+Style.RESET_ALL) 1211 | else: 1212 | print('(#'+str(num-40)+') '+Instruments[num].Name) 1213 | time.sleep(0.005) 1214 | else: 1215 | for num in range(len(Instruments)): 1216 | if ((Instruments[num].InMenu) and (not NormalStyleSelected) and (num >= normalInstrumentNumber)) or (((num >= normalInstrumentNumber) or (num == len(Instruments)-1)) and (NormalStyleSelected)): 1217 | print(Style.RESET_ALL+'(#'+str(num)+') '+Instruments[num].Name) 1218 | elif (unsafeMode): 1219 | if((Instruments[num].InMenu) and (not NormalStyleSelected)) or (NormalStyleSelected): 1220 | print(Fore.YELLOW+'(#'+str(num)+') '+Instruments[num].Name+Style.RESET_ALL) 1221 | else: 1222 | print(Fore.RED+'(#'+str(num)+') '+Instruments[num].Name+Style.RESET_ALL) 1223 | time.sleep(0.005) 1224 | 1225 | Perc1 = SelectStyleInstrument('Percussion 1',True) 1226 | Perc2 = SelectStyleInstrument('Percussion 2',True) 1227 | 1228 | if(Selection == len(Styles)): 1229 | PatchName = [] 1230 | PatchInfo = [] 1231 | for num in range(len(Styles)-MenuStyles): 1232 | PatchName.append(Styles[num].Name+' Style Patch') 1233 | PatchInfo.append("0"+format(int(Styles[num].MemOffset,16)+GetSongRegionOffset(),'x').lower()+' 00000018\n'+Melody+' '+Harmony+'\n'+Chord+' '+Bass+'\n'+Perc1+' '+Perc2+'\n') 1234 | AddPatch(PatchName,PatchInfo) 1235 | elif(Selection >= len(Styles)-MenuStyles): 1236 | PatchName = [] 1237 | PatchInfo = [] 1238 | for num in range(len(Styles)-MenuStyles,len(Styles)): 1239 | PatchName.append(Styles[num].Name+' Style Patch') 1240 | PatchInfo.append("0"+format(int(Styles[num].MemOffset,16)+GetSongRegionOffset(),'x').lower()+' 00000018\n'+Melody+' '+Harmony+'\n'+Chord+' '+Bass+'\n'+Perc1+' '+Perc2+'\n') 1241 | AddPatch(PatchName,PatchInfo) 1242 | else: 1243 | AddPatch(Styles[Selection].Name+' Style Patch',"0"+format(int(Styles[Selection].MemOffset,16)+GetSongRegionOffset(),'x').lower()+' 00000018\n'+Melody+' '+Harmony+'\n'+Chord+' '+Bass+'\n'+Perc1+' '+Perc2+'\n') 1244 | 1245 | print("\nPatch Complete!") 1246 | time.sleep(0.5) 1247 | print("") 1248 | if(Selection < 11) and (input('Would you like to change the style name? [y/n] ') == 'y'): 1249 | ChangeName(Selection,input('\nType new name: ')) 1250 | print('') 1251 | elif(Selection == 4): #////////////////////////////////////////Advanced Tools 1252 | while True: 1253 | PrintSectionTitle('Advanced Tools') 1254 | print("(#0) Back to Main Menu") 1255 | print("(#1) Change All Wii Music Text") 1256 | print("(#2) Change Default Styles") 1257 | print("(#3) Remove Song") 1258 | print("(#4) Import/Export Files") 1259 | print("(#5) Extract/Pack Wii Music ROM") 1260 | print("(#6) Patch Main.dol With Gecko Codes") 1261 | print("(#7) Create Riivolution Patch") 1262 | 1263 | Selection = MakeSelection(['Please Select an Option',0,7]) 1264 | 1265 | if(Selection == 1): #////////////////////////////////////////Change Text 1266 | #Load Files 1267 | FindGameFolder() 1268 | 1269 | #Run Notepad 1270 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/decode.bat\" '+MessageFolder(),capture_output=True) 1271 | txt = open(MessageFolder().replace('\"','')+'/message.d/new_music_message.txt','rb') 1272 | textlines = txt.readlines() 1273 | txt.close() 1274 | for num in range(len(textlines)): 1275 | if(textlines[num] == b' b200 @015f /\r\n'): 1276 | textlines[num] = b' b200 @015f [/,4b] = Default\r\n' 1277 | textlines[num+1] = b' b201 @0160 [/,4b] = Rock\r\n' 1278 | textlines[num+2] = b' b202 @0161 [/,4b] = March\r\n' 1279 | textlines[num+3] = b' b203 @0162 [/,4b] = Jazz\r\n' 1280 | textlines[num+4] = b' b204 @0163 [/,4b] = Latin\r\n' 1281 | textlines[num+5] = b' b205 @0164 [/,4b] = Reggae\r\n' 1282 | textlines[num+6] = b' b206 @0165 [/,4b] = Hawaiian\r\n' 1283 | textlines[num+7] = b' b207 @0166 [/,4b] = Electronic\r\n' 1284 | textlines[num+8] = b' b208 @0167 [/,4b] = Classical\r\n' 1285 | textlines[num+9] = b' b209 @0168 [/,4b] = Tango\r\n' 1286 | textlines[num+10] = b' b20a @0169 [/,4b] = Pop\r\n' 1287 | textlines[num+11] = b' b20b @016a [/,4b] = Japanese\r\n' 1288 | break 1289 | txt = open(MessageFolder().replace('\"','')+'/message.d/new_music_message.txt','wb') 1290 | txt.writelines(textlines) 1291 | txt.close() 1292 | print("\nWaiting for Notepad to close...") 1293 | subprocess.run('notepad \"'+MessageFolder().replace('\"','')+'/message.d/new_music_message.txt\"',capture_output=True) 1294 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/encode.bat\" '+MessageFolder(),capture_output=True) 1295 | print("\nEditing Successful!\n") 1296 | elif(Selection == 2): #////////////////////////////////////////Change Default Style 1297 | print('') 1298 | for num in range(50): 1299 | print('(#'+str(num)+') '+Songs[num].Name) 1300 | time.sleep(0.005) 1301 | 1302 | SongSelected = MakeSelection(['Please Select a Song',0,49]) 1303 | print('') 1304 | for num in range(52): 1305 | print('(#'+str(num)+') '+Styles[num].Name) 1306 | time.sleep(0.005) 1307 | 1308 | StyleSelected = MakeSelection(['Please Select a Style',0,51]) 1309 | 1310 | AddPatch(Songs[SongSelected].Name+' Default Style Patch','0'+format(int(Songs[SongSelected].MemOffset,16)+GetSongRegionOffset()+42,'x')+' 000000'+Styles[StyleSelected].StyleId+'\n') 1311 | print('\nPatch Successful') 1312 | elif(Selection == 3): #////////////////////////////////////////Remove Song 1313 | FindGameFolder() 1314 | FindDolphinSave() 1315 | PrintSectionTitle('Remove Song') 1316 | maxNumber = 0 1317 | for num in range(len(Songs)-1): 1318 | if(Songs[num].SongType == SongTypeValue.Regular): 1319 | print('(#'+str(num)+') '+str(Songs[num].Name)) 1320 | time.sleep(0.005) 1321 | maxNumber += 1 1322 | print('(#'+str(maxNumber)+') Remove All Non-Custom Songs') 1323 | 1324 | Selection = MakeSelection(['Please Select a Song to Remove',0,maxNumber]) 1325 | 1326 | if(Selection == maxNumber): 1327 | appliedCustomSongs = [] 1328 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): 1329 | codes = open(GamePath+'/GeckoCodes.ini') 1330 | textlines = codes.readlines() 1331 | codes.close() 1332 | for text in textlines: 1333 | if('[WiiMusicEditor]' in text) and ('Style' not in text): 1334 | appliedCustomSongs.append(text[1:len(text)-29:1]) 1335 | 1336 | for number in range(maxNumber-1): 1337 | if(Selection != maxNumber): 1338 | number = Selection 1339 | 1340 | if((Selection != maxNumber) or (Songs[number].Name not in appliedCustomSongs)): 1341 | #Brsar Writing 1342 | brsar = open(GamePath+'/sys/main.dol', "r+b") 1343 | if(Songs[number].SongType == SongTypeValue.Regular): 1344 | MainDolOffset = '59C56E' 1345 | brsar.seek(int(MainDolOffset,16)+6+int("BC",16)*Songs[number].MemOrder) 1346 | brsar.write(bytes.fromhex('ffffffffffff')) 1347 | brsar.close() 1348 | 1349 | if(Selection != len(Songs)-1): break 1350 | 1351 | print('\nEradication Complete!') 1352 | elif(Selection == 4): #////////////////////////////////////////Import/Export Files 1353 | while True: 1354 | PrintSectionTitle('Import/Export Files') 1355 | print("(#0) Back To Main Menu") 1356 | print("(#1) Import Files") 1357 | print("(#2) Export Files to .zip") 1358 | print("(#3) Export Files to Folder") 1359 | 1360 | Selection = MakeSelection(['Choose an Option',0,3]) 1361 | if(Selection == 1): 1362 | while True: 1363 | importDir = input("\nDrag Wii Music files you want to import [.brsar, .carc, .dol, .ini, .gct, .zip, folder]: ").replace('&', '').replace('\'', '').replace('\"', '').strip() 1364 | if(os.path.isdir(importDir)) or (pathlib.Path(importDir).suffix in ['.brsar','.carc','.dol','.ini','.gct','.zip']): 1365 | if(pathlib.Path(importDir).suffix == '.zip'): 1366 | with tempfile.TemporaryDirectory() as directory: 1367 | with zipfile.ZipFile(importDir, 'r') as zip_ref: 1368 | zip_ref.extractall(directory) 1369 | LoadNewFile(directory) 1370 | else: 1371 | LoadNewFile(importDir) 1372 | break 1373 | else: 1374 | print('\nERROR: Bad Filetype') 1375 | 1376 | elif(Selection == 0): break 1377 | else: 1378 | name = os.path.dirname(GamePath)+" Extracted Files" 1379 | number = "" 1380 | num = 0 1381 | while(os.path.isdir(name+number) and Selection == 3) or (os.path.isfile(name+number+".zip") and Selection == 2): 1382 | num = num+1 1383 | number = " ("+str(num)+")" 1384 | name = name+number 1385 | if(Selection == 3) and (not os.path.isdir(name)): os.mkdir(name) 1386 | if(Selection == 2): zipObj = zipfile.ZipFile(name+'.zip', 'w') 1387 | if(Selection == 3): 1388 | copyfile(GamePath+'/files/Sound/MusicStatic/rp_Music_sound.brsar',name+'/rp_Music_sound.brsar') 1389 | copyfile(GetMessagePath(GamePath),name+'/message.carc') 1390 | copyfile(GamePath+'/sys/main.dol',name+'/main.dol') 1391 | else: 1392 | zipObj.write(GamePath+'/files/Sound/MusicStatic/rp_Music_sound.brsar','rp_Music_sound.brsar') 1393 | zipObj.write(GetMessagePath(GamePath),'message.carc') 1394 | zipObj.write(GamePath+'/sys/main.dol','main.dol') 1395 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): 1396 | if(Selection == 3): 1397 | copyfile(GamePath+'/GeckoCodes.ini',name+'/GeckoCodes.ini') 1398 | else: 1399 | zipObj.write(GamePath+'/GeckoCodes.ini','GeckoCodes.ini') 1400 | CreateGct() 1401 | if(Selection == 3): 1402 | os.rename(ProgramPath+'/'+GetGameId(GamePath)+'.gct',name+'/'+GetGameId(GamePath)+'.gct') 1403 | else: 1404 | zipObj.write(ProgramPath+'/'+GetGameId(GamePath)+'.gct',GetGameId(GamePath)+'.gct') 1405 | os.remove(ProgramPath+'/'+GetGameId(GamePath)+'.gct') 1406 | if(Selection == 2): 1407 | zipObj.close() 1408 | name = name+'.zip' 1409 | print('\nExport Complete!\nSaved to: '+name) 1410 | elif(Selection == 5): #////////////////////////////////////////Extract/Pack Wii Music ROM 1411 | while True: 1412 | PrintSectionTitle('Extract/Pack Wii Music ROM') 1413 | print("(#0) Back To Advanced Tools") 1414 | print("(#1) Extract ROM Filesystem") 1415 | print("(#2) Pack Filesystem to .wbfs") 1416 | print("(#3) Pack Filesystem to .iso") 1417 | 1418 | Selection = MakeSelection(['Choose an Option',0,3]) 1419 | if(Selection == 1): 1420 | ExceptedFileExtensions = ['.iso','.wbfs'] 1421 | while True: 1422 | DiskPath = input("\nPlease Drag the Wii Music Disk: ").replace('&', '').replace('\'', '').replace('\"', '').strip() 1423 | if(os.path.isfile(DiskPath)) and (pathlib.Path(DiskPath).suffix in ExceptedFileExtensions): 1424 | break 1425 | else: 1426 | print('ERROR: Not Supported File Type') 1427 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/wit.exe\" cp --fst \"'+DiskPath+'\" \"'+os.path.dirname(DiskPath)+"/"+os.path.splitext(os.path.basename(DiskPath))[0]+'\"') 1428 | if(input('\nWould You Like to Set This Path as the Current Game Path? [y/n] ') == 'y'): 1429 | GamePath = os.path.dirname(DiskPath).replace('\\','/')+'/'+os.path.splitext(os.path.basename(DiskPath))[0]+'/DATA' 1430 | BrsarPath = GamePath+'/files/sound/MusicStatic/rp_Music_sound.brsar' 1431 | MessagePath = GetMessagePath(GamePath) 1432 | SaveSetting('Paths','GamePath',GamePath) 1433 | elif(Selection == 2) or (Selection == 3): 1434 | if(input('\nUse Game Path as Disk Directory? [y/n] ') != 'y'): 1435 | while True: 1436 | DiskPath= input("\nDrag Decompressed Wii Music Directory: ").replace('&', '').replace('\'', '').replace('\"', '').strip() 1437 | if(os.path.isdir(DiskPath+'/DATA')): 1438 | break 1439 | else: 1440 | print("\nERROR: Unable to Locate Valid Wii Music Directory") 1441 | else: 1442 | FindGameFolder() 1443 | if(GamePath[len(GamePath)-4:len(GamePath):1] == 'DATA'): 1444 | DiskPath = GamePath[0:len(GamePath)-5:1] 1445 | else: 1446 | DiskPath = GamePath 1447 | 1448 | DiskName = '' 1449 | DiskNum = 0 1450 | if(Selection == 2): 1451 | while(os.path.isfile(DiskPath+DiskName+'.wbfs')): 1452 | DiskNum = DiskNum+1 1453 | DiskName = '('+str(DiskNum)+')' 1454 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/wit.exe\" cp \"'+DiskPath+'\" \"'+DiskPath+DiskName+'.wbfs\" --wbfs') 1455 | else: 1456 | while(os.path.isfile(DiskPath+DiskName+'.iso')): 1457 | DiskNum = DiskNum+1 1458 | DiskName = '('+str(DiskNum)+')' 1459 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/wit.exe\" cp \"'+DiskPath+'\" \"'+DiskPath+DiskName+'.iso\" --iso') 1460 | else: break 1461 | print('') 1462 | elif(Selection == 6): #////////////////////////////////////////Patch Main.dol 1463 | FindGameFolder() 1464 | FindDolphinSave() 1465 | if(input('\nAre you sure you want to patch Main.dol? [y/n] ') == 'y'): 1466 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): 1467 | print('\nCreating Gct...') 1468 | CreateGct() 1469 | print('\nPatching Main.dol...') 1470 | subprocess.run('\"'+ProgramPath+'/Helper/Wiimms/wstrt.exe\" patch \"'+GamePath+'/sys/main.dol\" --add-section \"'+ProgramPath+'/'+GetGameId(GamePath)+'.gct --force\"',capture_output=True) 1471 | os.remove(ProgramPath+'/'+GetGameId(GamePath)+'.gct') 1472 | print('\nPatch Successful!') 1473 | else: 1474 | print('\nNo Gecko Codes Found') 1475 | elif(Selection == 7): #////////////////////////////////////////Riivolution Patch 1476 | PrintSectionTitle('Riivolution Patch') 1477 | FindGameFolder() 1478 | FindDolphinSave() 1479 | if(GamePath[len(GamePath)-4:len(GamePath):1] == 'DATA'): 1480 | ModPath = GamePath[0:len(GamePath)-5:1] 1481 | ModPath = ModPath[0:len(ModPath)-len(os.path.basename(ModPath)):1] 1482 | else: 1483 | ModPath = GamePath[0:len(GamePath)-len(os.path.basename(GamePath)):1] 1484 | ModName = input('\nPlease Input Mod Name: ') 1485 | while (os.path.isdir(ModPath+ModName)): 1486 | ModName = input('\nDirectory Already Exists! Please Enter a Diffrent Name: ') 1487 | ModPath = ModPath+ModName 1488 | os.mkdir(ModPath) 1489 | os.mkdir(ModPath+'/Riivolution') 1490 | os.mkdir(ModPath+'/Riivolution/codes') 1491 | os.mkdir(ModPath+'/'+ModName.replace(' ','')) 1492 | print('\nMaking Gct...') 1493 | CreateGct() 1494 | print('\nCopying Files...') 1495 | copyfile(GamePath+'/files/Sound/MusicStatic/rp_Music_sound.brsar',ModPath+'/'+ModName.replace(' ','')+'/rp_Music_sound.brsar') 1496 | copyfile(GetMessagePath(GamePath),ModPath+'/'+ModName.replace(' ','')+'/message.carc') 1497 | copyfile(ProgramPath+'/Helper/GctFiles/codehandler.bin',ModPath+'/Riivolution/codehandler.bin') 1498 | os.rename(ProgramPath+'/'+GetGameId(GamePath)+'.gct',ModPath+'/Riivolution/codes/'+GetGameId(GamePath)+'.gct') 1499 | print('\nCreating XML file...') 1500 | linestowrite = [ 1501 | '\n', 1502 | ' \n', 1503 | ' \n', 1504 | '
\n', 1505 | ' \n', 1510 | '
\n', 1511 | '
\n', 1512 | ' \n', 1513 | ' \n', 1514 | ' \n', 1515 | ' \n', 1516 | ' \n', 1517 | ' \n', 1518 | ' \n', 1519 | ' \n', 1520 | ' \n', 1521 | ' \n', 1522 | '
\n'] 1523 | xml = open(ModPath+'/Riivolution/'+ModName.replace(' ','')+'.xml','w') 1524 | xml.writelines(linestowrite) 1525 | xml.close() 1526 | print('\nPatch Creation Successful!\nExported to: '+ModPath) 1527 | if(input('\nWould You Like to Copy the Patch on to an SD card? [y/n] ') == 'y'): 1528 | letter = input('\nType the Drive Letter of the SD card: ') 1529 | print('\nCopying...') 1530 | copytree(ModPath+'/Riivolution',letter+':/Riivolution') 1531 | copytree(ModPath+'/'+ModName.replace(' ',''),letter+':/'+ModName.replace(' ','')) 1532 | print('\nSuccessfully Copied!') 1533 | else: break 1534 | elif(Selection == 5): #////////////////////////////////////////Run Game 1535 | FindGameFolder() 1536 | FindDolphin() 1537 | PrintSectionTitle("Running Dolphin") 1538 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): 1539 | if(os.path.isfile(CodePath)): os.remove(CodePath) 1540 | copyfile(GamePath+'/GeckoCodes.ini',CodePath) 1541 | subprocess.Popen('\"'+DolphinPath+'\" -b -e \"'+GamePath+'/sys/main.dol\"') 1542 | time.sleep(1) 1543 | print("") 1544 | elif(Selection == 6): #////////////////////////////////////////Revert Changes 1545 | while True: 1546 | PrintSectionTitle('Revert Changes') 1547 | print("(#0) Back to Main Menu") 1548 | print("(#1) Revert Songs") 1549 | print("(#2) Revert Text") 1550 | print("(#3) Revert Styles") 1551 | print("(#4) Revert Main.dol") 1552 | print("(#5) Revert All") 1553 | 1554 | Selection = MakeSelection(['Select An Option',0,5]) 1555 | 1556 | if(Selection != 0): 1557 | for num in range(1,5): 1558 | if(Selection != 5): 1559 | num = Selection 1560 | 1561 | if(num == 1): 1562 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): 1563 | codes = open(GamePath+'/GeckoCodes.ini') 1564 | textlines = codes.readlines() 1565 | codes.close() 1566 | linenum = 0 1567 | while linenum < len(textlines): 1568 | if('Song' in textlines[linenum]): 1569 | textlines.pop(linenum) 1570 | while(len(textlines) > linenum) and (textlines[linenum][0].isnumeric() or textlines[linenum][0].isalpha()): 1571 | textlines.pop(linenum) 1572 | else: 1573 | linenum = linenum+1 1574 | codes = open(GamePath+'/GeckoCodes.ini','w') 1575 | codes.writelines(textlines) 1576 | codes.close() 1577 | if(os.path.isfile(ProgramPath+"/Helper/Backup/rp_Music_sound.brsar")): 1578 | os.remove(BrsarPath) 1579 | copyfile(ProgramPath+"/Helper/Backup/rp_Music_sound.brsar",BrsarPath) 1580 | print('\nSongs Reverted') 1581 | else: 1582 | print('\nERROR: Backup not found') 1583 | elif(num == 2): 1584 | if(os.path.isfile(ProgramPath+"/Helper/Backup/message.carc")): 1585 | os.remove(MessagePath) 1586 | copyfile(ProgramPath+"/Helper/Backup/message.carc",MessagePath) 1587 | print('\nText Reverted') 1588 | else: 1589 | print('\nERROR: Backup not found') 1590 | elif(num == 3): 1591 | if(os.path.isfile(GamePath+'/GeckoCodes.ini')): 1592 | codes = open(GamePath+'/GeckoCodes.ini') 1593 | textlines = codes.readlines() 1594 | codes.close() 1595 | linenum = 0 1596 | while linenum < len(textlines): 1597 | if('Style' in textlines[linenum]): 1598 | textlines.pop(linenum) 1599 | while(len(textlines) > linenum) and (textlines[linenum][0].isnumeric() or textlines[linenum][0].isalpha()): 1600 | textlines.pop(linenum) 1601 | else: 1602 | linenum = linenum+1 1603 | codes = open(GamePath+'/GeckoCodes.ini','w') 1604 | codes.writelines(textlines) 1605 | codes.close() 1606 | print('\nStyles Reverted') 1607 | elif(num == 4): 1608 | if(os.path.isfile(ProgramPath+"/Helper/Backup/main.dol")): 1609 | os.remove(GamePath+'/sys/main.dol') 1610 | copyfile(ProgramPath+"/Helper/Backup/main.dol",GamePath+'/sys/main.dol') 1611 | print('\nMain.dol Reverted') 1612 | else: 1613 | print('\nERROR: Backup not found') 1614 | 1615 | if(Selection != 5): break 1616 | else: break 1617 | elif(Selection == 7): #////////////////////////////////////////100% Save File 1618 | FindDolphinSave() 1619 | if(input("\nAre You Sure You Want To Overwrite Your Save Data? [y/n] ") == 'y'): 1620 | subprocess.run('robocopy \"'+ProgramPath+'/Helper/WiiMusicSave\" \"'+SaveDataPath+'\" /MIR /E',capture_output=True) 1621 | print("\nOverwrite Successfull\n") 1622 | else: 1623 | print("\nAborted...\n") 1624 | elif(Selection == 8): #////////////////////////////////////////Download Pre-Made Custom Songs 1625 | print('\nDownloading...') 1626 | try: 1627 | response = requests.get('https://github.com/BenjaminHalko/Pre-Made-Songs-for-Wii-Music/archive/refs/heads/main.zip', stream=True) 1628 | total_size_in_bytes= int(response.headers.get('content-length', 0)) 1629 | block_size = 1024 1630 | progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True) 1631 | with open('CustomSongs.zip', 'wb') as file: 1632 | for data in response.iter_content(block_size): 1633 | progress_bar.update(len(data)) 1634 | file.write(data) 1635 | progress_bar.close() 1636 | if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes: 1637 | print("\nERROR, something went wrong\n") 1638 | print('\nExtracting...\n') 1639 | if(os.path.isdir('PreMade Custom Songs')): rmtree('PreMade Custom Songs') 1640 | subprocess.run('tar -xf CustomSongs.zip') 1641 | os.rename('Pre-Made-Songs-for-Wii-Music-main', 'PreMade Custom Songs') 1642 | os.remove('CustomSongs.zip') 1643 | print('Saved To: \"'+ProgramPath+'/PreMade Custom Songs\"\n') 1644 | except (requests.ConnectionError, requests.Timeout) as exception: 1645 | print('\nFailed to Download File...\n') 1646 | elif(Selection == 9): #////////////////////////////////////////Help 1647 | PrintSectionTitle('Help') 1648 | print("(#0) Back to Main Menu") 1649 | print("(#1) Open the Wiki") 1650 | print("(#2) Open Video Guide") 1651 | 1652 | Selection = MakeSelection(['What type of help do you want',0,2]) 1653 | 1654 | if(Selection == 1): 1655 | print('\nOpening Wiki...') 1656 | time.sleep(0.5) 1657 | webbrowser.open('https://github.com/BenjaminHalko/WiiMusicEditor/wiki') 1658 | elif(Selection == 2): 1659 | print('\nOpening Video Guide...') 1660 | time.sleep(0.5) 1661 | webbrowser.open('https://youtu.be/EBz9VtBXqEo') 1662 | print('') 1663 | time.sleep(0.5) 1664 | elif(Selection == 10): #////////////////////////////////////////Settings 1665 | while True: 1666 | PrintSectionTitle("Settings") 1667 | print("(#0) Back To Main Menu") 1668 | print("(#1) Change File Paths") 1669 | print("(#2) Other Settings") 1670 | print("(#3) Updates") 1671 | if(unsafeMode): 1672 | print("(#4) Switch to Safe Mode") 1673 | else: 1674 | print("(#4) Switch to Unsafe Mode") 1675 | 1676 | Selection = MakeSelection(['Which Setting Do You Want to Change',0,4]) 1677 | 1678 | if(Selection == 1): 1679 | while True: 1680 | PrintSectionTitle('Path Editor') 1681 | print("(#0) Back To Settings") 1682 | print("(#1) Game Path (Current Path: "+GamePath+')') 1683 | print("(#2) Dolphin Path (Current Path: "+DolphinPath+')') 1684 | print("(#3) Dolphin Save Path (Current Path: "+DolphinSaveData+')') 1685 | Selection = MakeSelection(['Which Path Do You Want to Change',0,3]) 1686 | if(Selection == 1): 1687 | GamePath = '' 1688 | FindGameFolder() 1689 | print("") 1690 | elif(Selection == 2): 1691 | DolphinPath = '' 1692 | FindDolphin() 1693 | print("") 1694 | elif(Selection == 3): 1695 | DolphinSaveData = '' 1696 | FindDolphinSave() 1697 | print("") 1698 | else: break 1699 | elif(Selection == 2): 1700 | while True: 1701 | PrintSectionTitle('Specific Settings') 1702 | print("(#0) Back To Settings") 1703 | print("(#1) Replace Song and Score Files Seperatly: "+DefaultLoadSongScore) 1704 | print("(#2) Replace Song Warnings: "+DefaultWantToReplaceSong) 1705 | print("(#3) Warm User When Replacing Already Replaced Song: "+DefaultReplacingReplacedSong) 1706 | print("(#4) Use Auto Found Length and Tempo: "+DefaultUseAutoLengthTempo) 1707 | print("(#5) Replace Song Names After Adding Custom Song: "+DefaultReplaceSongNames) 1708 | 1709 | Selection = MakeSelection(['Choose an Option',0,6]) 1710 | if(Selection == 1): 1711 | DefaultLoadSongScore = ChangeDefaultAnswer(['Yes','No'],['Load Song And Score',DefaultLoadSongScore]) 1712 | elif(Selection == 2): 1713 | DefaultWantToReplaceSong = ChangeDefaultAnswer(['Yes','No'],['Want To Replace Song',DefaultWantToReplaceSong]) 1714 | elif(Selection == 3): 1715 | DefaultReplacingReplacedSong = ChangeDefaultAnswer(['Yes','No'],['Replacing Replaced Song',DefaultReplacingReplacedSong]) 1716 | elif(Selection == 4): 1717 | DefaultUseAutoLengthTempo = ChangeDefaultAnswer(['Ask','Yes','No'],['Use Auto Length and Tempo']) 1718 | elif(Selection == 5): 1719 | DefaultReplaceSongNames = ChangeDefaultAnswer(['Ask','Yes','No'],['Replace Song Names']) 1720 | else: break 1721 | elif(Selection == 3): 1722 | while True: 1723 | PrintSectionTitle('Updates') 1724 | print("(#0) Back To Settings") 1725 | print("(#1) Check For Updates") 1726 | if(AutoUpdate): 1727 | print("(#2) Turn Off Auto Updates") 1728 | else: 1729 | print("(#2) Turn On Auto Updates") 1730 | if(not bool(beta)): 1731 | print("(#3) Switch to Beta Branch") 1732 | else: 1733 | print("(#3) Switch to Main Branch") 1734 | 1735 | Selection = MakeSelection(['Pick an Option',0,3]) 1736 | if(Selection == 1): 1737 | print('') 1738 | CheckForUpdates(True) 1739 | print('') 1740 | elif(Selection == 2): 1741 | AutoUpdate = not AutoUpdate 1742 | SaveSetting('Updates', 'AutoUpdate', str(int(AutoUpdate))) 1743 | elif(Selection == 3): 1744 | beta = int(not bool(beta)) 1745 | SaveSetting('Updates', 'Branch', str(beta)) 1746 | if(DownloadUpdate()): 1747 | beta = int(not bool(beta)) 1748 | SaveSetting('Updates', 'Branch', str(beta)) 1749 | else: break 1750 | elif(Selection == 4): 1751 | if((not unsafeMode) and (input('\nAre You Sure You Want to Turn on Unsafe Mode? [y/n] ') == 'y')) or (unsafeMode): 1752 | unsafeMode = not unsafeMode 1753 | SaveSetting('Unsafe Mode','Unsafe Mode',str(int(unsafeMode))) 1754 | print('') 1755 | else: break 1756 | elif(Selection == 11): #////////////////////////////////////////Credits 1757 | PrintSectionTitle('Credits') 1758 | print('\n-----Created By:-----') 1759 | print('- Benjamin Halko') 1760 | print('\n-----Tested By:-----') 1761 | print('- FasterHumans') 1762 | print('- RainbowKappa') 1763 | print('\n-----Song File Offsets Discovered By:-----') 1764 | print('- JimmyKaz') 1765 | print('\n-----Song Memory Offsets Discovered By:-----') 1766 | print('- Checker Mii Out Channel') 1767 | print('\n-----Brsar Conversion Made Possible By:-----') 1768 | print('- GotaSequenceCmd') 1769 | print('\n-----Text Extraction Made Possible By:-----') 1770 | print('- WiiMMS') 1771 | input('') 1772 | --------------------------------------------------------------------------------