diff --git a/README.md b/README.md index 3a6590c..fa3f250 100644 --- a/README.md +++ b/README.md @@ -7,23 +7,35 @@ I got tired of waiting for actual custom music to be added to Mega Man Maker. So ![Alt text](/nsf_changer.png?raw=true "NSF Changer window") ## Installation -You will need to install through PIP the package PySimpleGUI. The rest of the dependencies *should* be already installed. +You will need to install through PIP the package PySimpleGUI, requests, and packaging. You can just do "pip install -r requirements.txt". The rest of the dependencies *should* already be installed. ## Usage -Please use at your own risk and make backups of the MegaManMaker folder before you use it. -To use it just launch the executable file (if available for your os) or type "python mmm-nsf-changer.py" in the terminal or commandline, and the GUI will launch. Maybe CLI support will come in the future. Then you choose your nsf file and where your Mega Man Maker folder is. Please note that support for Windows systems is not tested much since I don't use it. On windows systems the program may freeze for about ten seconds, please DO NOT CLICK RESET ON MEGA MAKER WHILE THE TOOL IS FROZEN, IT WILL CRASH THE GAME. +To use it just launch the executable file (if available for your os) or type "python mmm-nsf-changer.py" in the terminal or commandline, and the GUI will launch. Then you choose your nsf file and where your Mega Man Maker folder is. + +## Features +* Easy way to choose a NSF, NSFe, SPC, VGM, GYM, AY, HES, KSS, SAP or GBS file as a replacement for every song in the game. Yes, you heard that right: You can use SNES Music, Sega Genesis Music, and more in Mega Man Maker! +* A "mute" button for replacing every song in the game with a silent song. For people that play Mega Man Maker with external music, like streamers. +* Reliable way to restore the original songs. +* A decent interface. +* -NEW- Support for "permanently" replacing a specific song to one of your liking, with restoring capabilities. ## Support -If you have any issues, which is probable, make a gitlab issue. There *will* be bugs. +If you have any issues make a gitlab issue. There may be bugs. ## Roadmap -fix slowdown on windows systems -Ability to change only the level music and leave the menu, or just the menu and leave the level music. -Instead of duplicating a single file a lot of times, maybe I'll implement symlink support. -add autoupdater?? +* fix restore_ogmusic so that it doesn't replace custom music. Possible windows only behaviour, testing is needed. +* consider the posibility of adding a alternate, custom music preserving, updater for Mega Man Maker. +* improve the single-song actions to make them more persistant +* modify restore_ogmusic so it is able to restore only the last change. Maybe config files will be needed for this. +* make it so that if "you press apply music and then replace song" it checks for that and doesn't allow the "replaced song" to be reverted back to original. +* ability to preserve changes through Mega Man Maker updates, by using a config file and/or a downloaded zip of the game. +* stop using os.path in favour of pathlib +* switch to using a config file (or database) for keeping track of which tracks have been changed, that way only having one original_music folder and being able to *show* the user which song you used to replace which file. +* add a way to show the available nsf files in the selected folder, like a playlist. +* Instead of duplicating a single file a lot of times, maybe I'll implement symlink support. ## Contributing -You can help with the project by testing it and reporting issues, or by making pull requests. +You can help with the project by testing it and reporting issues, or by making pull requests. Windows testers are especially needed since I don't daily drive Windows. ## License -For open source projects, say how it is licensed. +GNU General Public License v3.0 diff --git a/bin/mmm-nsf-changer.exe b/bin/v1.0/mmm-nsf-changer.exe similarity index 100% rename from bin/mmm-nsf-changer.exe rename to bin/v1.0/mmm-nsf-changer.exe diff --git a/bin/v1.1.1/mmm-nsf-changer.exe b/bin/v1.1.1/mmm-nsf-changer.exe new file mode 100755 index 0000000..9b70470 Binary files /dev/null and b/bin/v1.1.1/mmm-nsf-changer.exe differ diff --git a/bin/v1.1.2/mmm-nsf-changer.exe b/bin/v1.1.2/mmm-nsf-changer.exe new file mode 100755 index 0000000..31c2e50 Binary files /dev/null and b/bin/v1.1.2/mmm-nsf-changer.exe differ diff --git a/bin/v1.1/before_you_use.txt b/bin/v1.1/before_you_use.txt new file mode 100644 index 0000000..9c90f4a --- /dev/null +++ b/bin/v1.1/before_you_use.txt @@ -0,0 +1 @@ +This executable was made in Wine, a Windows compatibility layer for Linux. It is not perfect. Because of that, this exe package may not work properly. It is NOT TESTED. diff --git a/bin/v1.1/mmm-nsf-changer.exe b/bin/v1.1/mmm-nsf-changer.exe new file mode 100755 index 0000000..4f8285c Binary files /dev/null and b/bin/v1.1/mmm-nsf-changer.exe differ diff --git a/bin/v1.2/mmm-nsf-changer.zip b/bin/v1.2/mmm-nsf-changer.zip new file mode 100644 index 0000000..a7ca3ba Binary files /dev/null and b/bin/v1.2/mmm-nsf-changer.zip differ diff --git a/bin/v2.0/mmm-nsf-changer-v2.0.zip b/bin/v2.0/mmm-nsf-changer-v2.0.zip new file mode 100644 index 0000000..7b4f0fb Binary files /dev/null and b/bin/v2.0/mmm-nsf-changer-v2.0.zip differ diff --git a/mmm-nsf-changer.py b/mmm-nsf-changer.py index 8a9c8aa..8e40d3b 100644 --- a/mmm-nsf-changer.py +++ b/mmm-nsf-changer.py @@ -6,21 +6,85 @@ import pathlib import filecmp import webbrowser import requests +import base64 +import zipfile +import io +import threading +from packaging import version ##Here goes the actual code + #this checks if the folder structure is correct. It will output a list of the files that we will then use to recreate the structure with a single file def safelist(): safe_files = ['MetalMan - (GB II).nsf', 'IceMan - (GB I).nsf', 'CutMan - (GB I).nsf', 'Neptune - (GB V).nsf', 'GBVWily2.nsf', 'WoodMan - (GB II).nsf', 'Saturn - (GB V).nsf', 'Jupiter - (GB V).nsf', 'Terra - (GB V).nsf', 'Venus - (GB V).nsf', 'GBIIIWily2.nsf', 'AirMan - (GB II).nsf', 'Uranus - (GB V).nsf', 'Pluto - (GB V).nsf', 'GBVWily1.nsf', 'Mercury - (GB V).nsf', 'GBIIWily2.nsf', 'MagnetMan - (GB II).nsf', 'Mars - (GB V).nsf', 'GBIIIWily1.nsf', 'ElecMan - (GB I).nsf', 'GBIWily2.nsf', 'TopMan - (GB II).nsf', 'CrashMan - (GB II).nsf', 'GBIWily1.nsf', 'Punk - (GB III).nsf', 'HardMan - (GB II).nsf', 'NeedleMan - (GB II).nsf', 'FireMan - (GB I).nsf', 'GBIVWily1.nsf', 'GBIIWily1.nsf', 'FreezeMan.nsf', 'JunkMan.nsf', 'SlashMan.nsf', 'CloudMan.nsf', 'SpringMan.nsf', 'Wily4.nsf', 'RobotMuseum.nsf', 'Wily1.nsf', 'Intro.nsf', 'Wily3.nsf', 'ShadeMan.nsf', 'ShadeManAlt.nsf', 'TurboMan.nsf', 'BurstMan.nsf', 'Wily2.nsf', 'WilyTower2.nsf', 'HyperStormH.nsf', 'WilyTower1.nsf', 'WilyTower3.nsf', 'MegaWaterS.nsf', 'WilyTower4.nsf', 'BusterRodG.nsf', 'GravityMan.nsf', 'WaveMan.nsf', 'StoneMan.nsf', 'MM5WilyCirno.nsf', 'MM5Wily.nsf', 'StarMan.nsf', 'NapalmMan.nsf', 'CrystalMan.nsf', 'ProtoCastle.nsf', 'ChargeMan.nsf', 'GyroMan.nsf', 'FlameMan.nsf', 'MrX.nsf', 'YamatoMan.nsf', 'CentaurMan.nsf', 'WindMan.nsf', 'PlantMan.nsf', 'MM6Wily.nsf', 'KnightMan.nsf', 'BlizzardMan.nsf', 'TomahawkMan.nsf', 'MM2Wily2.nsf', 'Crashman.nsf', 'MM2Wily1.nsf', 'MM2Wily6.nsf', 'Metalman.nsf', 'Flashman.nsf', 'Bubbleman.nsf', 'Woodman.nsf', 'Heatman.nsf', 'Quickman.nsf', 'Airman.nsf', 'GameOver.nsf', 'WilyIntro3.nsf', 'StageStart.nsf', 'WilyIntro2.nsf', 'WilyIntro1.nsf', 'Victory.nsf', 'ProtoWhistle.nsf', 'Bombman.nsf', 'MM1Wily1.nsf', 'MM1Wily2.nsf', 'Fireman.nsf', 'Iceman.nsf', 'Cutman.nsf', 'Elecman.nsf', 'PUIntroStage.nsf', 'Timeman.nsf', 'Oilman.nsf', 'Gutsman.nsf', 'FrostMan.nsf', 'GrenadeMan.nsf', 'AquaMan.nsf', 'AstroMan.nsf', 'Wily4.nsf', 'TenguMan(SS).nsf', 'Wily1.nsf', 'IntroStage(Underwater).nsf', 'SwordMan.nsf', 'Wily3.nsf', 'SearchMan.nsf', 'IntroStage.nsf', 'ClownMan.nsf', 'Wily2.nsf', 'TenguMan(PSX).nsf', 'Megaman4.nsf', 'ToadMan.nsf', 'MM4Wily2.nsf', 'RingMan.nsf', 'MM4Wily1.nsf', 'DrillMan.nsf', 'BrightMan.nsf', 'DiveMan.nsf', 'SkullMan.nsf', 'DustMan.nsf', 'Cossack1.nsf', 'PharaohMan.nsf', 'Cossack2.nsf', 'MagnetMan.nsf', 'MM3Wily2.nsf', 'MM3Wily3.nsf', 'SnakeMan.nsf', 'Megaman3.nsf', 'MM3Wily1.nsf', 'GeminiMan.nsf', 'ShadowMan.nsf', 'HardMan.nsf', 'TopMan.nsf', 'NeedleMan.nsf', 'SparkMan.nsf', 'KazeYoTsutaete.nsf', 'MMB_-_King_Nix.nsf', '30XXTitleLooped.nsf', 'FakeMan.nsf', 'PlugMan.nsf', 'GalaxyMan.nsf', 'EndlessAttack.nsf', 'SplashWoman.nsf', 'Wily4.nsf', 'Wily1.nsf', 'MagmaMan.nsf', 'JewelMan.nsf', 'ConcreteMan.nsf', 'Wily3.nsf', 'TornadoMan.nsf', 'Wily2.nsf', 'HornetMan.nsf', 'StrikeMan.nsf', 'Challenges.nsf', 'Wily1Outside.nsf', 'SolarMan.nsf', 'EndlessAttack.nsf', 'Wily4.nsf', 'NitroMan.nsf', 'Wily1Inside.nsf', 'CommandoMan.nsf', 'Punk.nsf', 'Wily5.nsf', 'Wily3.nsf', 'Enker.nsf', 'Ballade.nsf', 'ChillMan.nsf', 'BladeMan.nsf', 'PumpMan.nsf', 'Wily2.nsf', 'SheepMan.nsf', 'MM4.nsf', 'MM&B.nsf', 'MM8Wily.nsf', 'Sunstar - (GB V).nsf', 'MM7WilyCapsule.nsf', 'GBIVWilyBoss.nsf', 'MM10WilyCapsule.nsf', 'MMPUWilyBoss.nsf', 'MM11WilyCapsule.nsf', 'GBIV.nsf', 'MM3.nsf', 'MM&BKingBoss.nsf', 'MM3Wily.nsf', 'MM10WilyBoss.nsf', 'MM10WilyMachine.nsf', 'MM1Wily.nsf', 'MM10.nsf', 'MM2.nsf', 'MM5.nsf', 'BassMM8.nsf', 'MMPUWilyMachine.nsf', 'MM7Wily.nsf', 'MMWilyWars.nsf', 'MM8WoodMan.nsf', 'Duo.nsf', 'MM6Wily.nsf', 'MM11.nsf', 'BassMM7.nsf', 'MM5Wily.nsf', 'MM6.nsf', 'MM8CutMan.nsf', 'BossIntro.nsf', 'MM7.nsf', 'MMPU.nsf', 'MM4Wily.nsf', 'GBI.nsf', 'GBIVBalladeBoss.nsf', 'MM1.nsf', 'MM11WilyBoss.nsf', 'GBV.nsf', 'MM11WilyMachine.nsf', 'Midboss.nsf', 'GBII.nsf', 'MM9.nsf', 'MM8.nsf', 'MM8WilyCapsule.nsf', 'MM9Wily.nsf', 'EditorB.nsf', 'Credits.nsf', 'EditorD.nsf', '30XXTitle.nsf', 'Title.nsf', 'OnlineMenu.nsf', 'Menu.nsf', 'Editor.nsf', 'EditorC.nsf', 'ChallengeCredits.nsf', 'Tutorial.nsf', 'MagicMan.nsf', 'ColdMan.nsf', 'MM&BWily1.nsf', 'MM&BKing1.nsf', 'PirateMan.nsf', 'DynamoMan.nsf', 'GroundMan.nsf', 'AstroMan.nsf', 'Intro.nsf', 'TenguMan.nsf', 'CrystalGate.nsf', 'BurnerMan.nsf', 'BounceMan.nsf', 'FuseMan.nsf', 'TorchMan.nsf', 'Challenges.nsf', 'WilyStage.nsf', 'TundraMan.nsf', 'AcidMan.nsf', 'DrLightTrial.nsf', 'BlockMan.nsf', 'BlastMan.nsf', 'ImpactMan.nsf'] return safe_files + +def boss_music(): + boss_music = ['MM4.nsf', 'MM&B.nsf', 'MM8Wily.nsf', 'Sunstar - (GB V).nsf', 'MM7WilyCapsule.nsf', 'GBIVWilyBoss.nsf', 'MM10WilyCapsule.nsf', 'MMPUWilyBoss.nsf', 'MM11WilyCapsule.nsf', 'GBIV.nsf', 'MM3.nsf', 'MM&BKingBoss.nsf', 'MM3Wily.nsf', 'MM10WilyBoss.nsf', 'MM10WilyMachine.nsf', 'MM1Wily.nsf', 'MM10.nsf', 'MM2.nsf', 'MM5.nsf', 'BassMM8.nsf', 'MMPUWilyMachine.nsf', 'MM7Wily.nsf', 'MMWilyWars.nsf', 'MM8WoodMan.nsf', 'Duo.nsf', 'MM6Wily.nsf', 'MM11.nsf', 'BassMM7.nsf', 'MM5Wily.nsf', 'MM6.nsf', 'MM8CutMan.nsf', 'BossIntro.nsf', 'MM7.nsf', 'MMPU.nsf', 'MM4Wily.nsf', 'GBI.nsf', 'GBIVBalladeBoss.nsf', 'MM1.nsf', 'MM11WilyBoss.nsf', 'GBV.nsf', 'MM11WilyMachine.nsf', 'Midboss.nsf', 'GBII.nsf', 'MM9.nsf', 'MM8.nsf', 'MM8WilyCapsule.nsf', 'MM9Wily.nsf'] + return boss_music + +def level_music(): + level_music = ['MetalMan - (GB II).nsf', 'IceMan - (GB I).nsf', 'CutMan - (GB I).nsf', 'Neptune - (GB V).nsf', 'GBVWily2.nsf', 'WoodMan - (GB II).nsf', 'Saturn - (GB V).nsf', 'Jupiter - (GB V).nsf', 'Terra - (GB V).nsf', 'Venus - (GB V).nsf', 'GBIIIWily2.nsf', 'AirMan - (GB II).nsf', 'Uranus - (GB V).nsf', 'Pluto - (GB V).nsf', 'GBVWily1.nsf', 'Mercury - (GB V).nsf', 'GBIIWily2.nsf', 'MagnetMan - (GB II).nsf', 'Mars - (GB V).nsf', 'GBIIIWily1.nsf', 'ElecMan - (GB I).nsf', 'GBIWily2.nsf', 'TopMan - (GB II).nsf', 'CrashMan - (GB II).nsf', 'GBIWily1.nsf', 'Punk - (GB III).nsf', 'HardMan - (GB II).nsf', 'NeedleMan - (GB II).nsf', 'FireMan - (GB I).nsf', 'GBIVWily1.nsf', 'GBIIWily1.nsf', 'FreezeMan.nsf', 'JunkMan.nsf', 'SlashMan.nsf', 'CloudMan.nsf', 'SpringMan.nsf', 'Wily4.nsf', 'RobotMuseum.nsf', 'Wily1.nsf', 'Intro.nsf', 'Wily3.nsf', 'ShadeMan.nsf', 'ShadeManAlt.nsf', 'TurboMan.nsf', 'BurstMan.nsf', 'Wily2.nsf', 'WilyTower2.nsf', 'HyperStormH.nsf', 'WilyTower1.nsf', 'WilyTower3.nsf', 'MegaWaterS.nsf', 'WilyTower4.nsf', 'BusterRodG.nsf', 'GravityMan.nsf', 'WaveMan.nsf', 'StoneMan.nsf', 'MM5WilyCirno.nsf', 'MM5Wily.nsf', 'StarMan.nsf', 'NapalmMan.nsf', 'CrystalMan.nsf', 'ProtoCastle.nsf', 'ChargeMan.nsf', 'GyroMan.nsf', 'FlameMan.nsf', 'MrX.nsf', 'YamatoMan.nsf', 'CentaurMan.nsf', 'WindMan.nsf', 'PlantMan.nsf', 'MM6Wily.nsf', 'KnightMan.nsf', 'BlizzardMan.nsf', 'TomahawkMan.nsf', 'MM2Wily2.nsf', 'Crashman.nsf', 'MM2Wily1.nsf', 'MM2Wily6.nsf', 'Metalman.nsf', 'Flashman.nsf', 'Bubbleman.nsf', 'Woodman.nsf', 'Heatman.nsf', 'Quickman.nsf', 'Airman.nsf', 'Bombman.nsf', 'MM1Wily1.nsf', 'MM1Wily2.nsf', 'Fireman.nsf', 'Iceman.nsf', 'Cutman.nsf', 'Elecman.nsf', 'PUIntroStage.nsf', 'Timeman.nsf', 'Oilman.nsf', 'Gutsman.nsf', 'FrostMan.nsf', 'GrenadeMan.nsf', 'AquaMan.nsf', 'AstroMan.nsf', 'Wily4.nsf', 'TenguMan(SS).nsf', 'Wily1.nsf', 'IntroStage(Underwater).nsf', 'SwordMan.nsf', 'Wily3.nsf', 'SearchMan.nsf', 'IntroStage.nsf', 'ClownMan.nsf', 'Wily2.nsf', 'TenguMan(PSX).nsf', 'Megaman4.nsf', 'ToadMan.nsf', 'MM4Wily2.nsf', 'RingMan.nsf', 'MM4Wily1.nsf', 'DrillMan.nsf', 'BrightMan.nsf', 'DiveMan.nsf', 'SkullMan.nsf', 'DustMan.nsf', 'Cossack1.nsf', 'PharaohMan.nsf', 'Cossack2.nsf', 'MagnetMan.nsf', 'MM3Wily2.nsf', 'MM3Wily3.nsf', 'SnakeMan.nsf', 'Megaman3.nsf', 'MM3Wily1.nsf', 'GeminiMan.nsf', 'ShadowMan.nsf', 'HardMan.nsf', 'TopMan.nsf', 'NeedleMan.nsf', 'SparkMan.nsf', 'KazeYoTsutaete.nsf', 'MMB_-_King_Nix.nsf', '30XXTitleLooped.nsf', 'FakeMan.nsf', 'PlugMan.nsf', 'GalaxyMan.nsf', 'EndlessAttack.nsf', 'SplashWoman.nsf', 'Wily4.nsf', 'Wily1.nsf', 'MagmaMan.nsf', 'JewelMan.nsf', 'ConcreteMan.nsf', 'Wily3.nsf', 'TornadoMan.nsf', 'Wily2.nsf', 'HornetMan.nsf', 'StrikeMan.nsf', 'Challenges.nsf', 'Wily1Outside.nsf', 'SolarMan.nsf', 'EndlessAttack.nsf', 'Wily4.nsf', 'NitroMan.nsf', 'Wily1Inside.nsf', 'CommandoMan.nsf', 'Punk.nsf', 'Wily5.nsf', 'Wily3.nsf', 'Enker.nsf', 'Ballade.nsf', 'ChillMan.nsf', 'BladeMan.nsf', 'PumpMan.nsf', 'Wily2.nsf', 'SheepMan.nsf', 'MagicMan.nsf', 'ColdMan.nsf', 'MM&BWily1.nsf', 'MM&BKing1.nsf', 'PirateMan.nsf', 'DynamoMan.nsf', 'GroundMan.nsf', 'AstroMan.nsf', 'Intro.nsf', 'TenguMan.nsf', 'CrystalGate.nsf', 'BurnerMan.nsf', 'BounceMan.nsf', 'FuseMan.nsf', 'TorchMan.nsf', 'Challenges.nsf', 'WilyStage.nsf', 'TundraMan.nsf', 'AcidMan.nsf', 'DrLightTrial.nsf', 'BlockMan.nsf', 'BlastMan.nsf', 'ImpactMan.nsf'] + return level_music + +def whitelist_to_blacklist(whitelist): + blacklist = [] + for element in safelist(): + if element not in whitelist: + blacklist.append(element) + return blacklist + +# def generate_whitelist(megamaker_folder): +# subfolders = os.listdir(os.path.join(megamaker_folder, "Music")) +# subfolders.remove("Other") +# subfolders.remove("Misc") +# subfolders.remove("Boss") +# filenames = [] +# for folder in subfolders: +# folder_filenames =os.listdir(os.path.join(megamaker_folder, "Music", folder)) +# for filename in folder_filenames: +# filenames.append(filename) # -# def update_safelist(path): +# print(filenames) -#MAYBE SYMLINKS??? +def changed_nsf_files(megamaker_folder): + megamaker_folder = os.path.abspath(megamaker_folder) + music_folder = os.path.join(megamaker_folder, "original_specific_songs") + file_names = [] + glob_search = os.path.join(music_folder,"*","*.nsf") + file_paths = (glob(glob_search)) + for files in file_paths: + files_no_path = os.path.basename(files) + if files_no_path == 'TMP_BURNER_MAN.nsf': + print("skipping",files_no_path) + elif files_no_path == 'TMP_COLD_MAN.nsf': + print("skipping", files_no_path) + else: + file_names.append(files_no_path) + return file_names -#maybe check with mmm api if new version -#maybe add autoupdater through gitlab +def get_original_songs(megamaker_folder): + mmm_version = check_version(version=True) + parsed_version = mmm_version.replace(".", "_") + url = f"https://megamanmaker.com/downloads/MegaManMaker_v{parsed_version}.zip" + request = requests.get(url) + zip_file = zipfile.ZipFile(io.BytesIO(request.content)) + for file in zip_file.namelist(): + if file.startswith('Music/'): + zip_file.extract(file, megamaker_folder) + window.write_event_value('-THREAD DONE-', True) + return True + +app_version = "v2.0" +def check_if_new_version(): + response = requests.get("https://gitlab.com/api/v4/projects/51771052/repository/tags") + newest_version = response.json()[0]["name"] + if version.parse(newest_version) == version.parse(app_version): + return False + elif version.parse(newest_version) > version.parse(app_version): + return True #add mmm_version = "1.8.5.2" @@ -35,8 +99,18 @@ def check_version(version=False): else: return False +def create_mute_file(base64_str): + current_dir = os.path.dirname(__file__) + mutefile = os.path.join(current_dir, "mmm_nsf_changer_mute.nsf") + if os.path.exists(mutefile): + print("the mute nsf file already exists. This is a safety measure.") + else: + with open(mutefile, 'wb') as file_to_save: + decoded_image_data = base64.b64decode(base64_str) + file_to_save.write(decoded_image_data) + return True -def check_folder_structure(megamaker_folder, og_music=False): +def check_folder_structure(megamaker_folder, og_music=False, whitelist=[]): megamaker_folder = os.path.abspath(megamaker_folder) subfolders = ([ f.path for f in os.scandir(megamaker_folder) if f.is_dir() ]) music_folder = os.path.join(megamaker_folder, "Music") @@ -54,20 +128,29 @@ def check_folder_structure(megamaker_folder, og_music=False): print("skipping", files_no_path) else: file_names.append(files_no_path) - if set(file_names) == set(safelist()): + if set(file_names) == set(safelist()) or set(file_names) == set(whitelist): return file_paths else: print("Safety check failed. Either you have other files in the music folder of mega maker, you are in the wrong folder, or there has been a new update.") else: return False -#this needs to check if original_music exists, and if it does restore_ogmusic before moving to og music. -def move_music_to_ogmusic(path): - path = os.path.abspath(path) +##this needs an overhaul. +def copy_music_to_ogmusic(path): original_filepaths = check_folder_structure(path) if original_filepaths: - if not filecmp.cmp(os.path.join(path,"Music","MM1","Cutman.nsf"), os.path.join(path, "Music", "MM1", "Oilman.nsf"), shallow=False): - shutil.move(os.path.join(path, "Music"), os.path.join(path, "original_music")) + + #if not filecmp.cmp(os.path.join(path,"Music","MM1","Cutman.nsf"), os.path.join(path, "Music", "MM1", "Oilman.nsf"), shallow=False): + for filepath in original_filepaths: + ogmusic_dirname = os.path.join(path, "original_music", os.path.basename(os.path.dirname(filepath))) + ogmusic_filepath = os.path.join(ogmusic_dirname, os.path.basename(filepath)) + if not os.path.exists(ogmusic_dirname): + os.makedirs(ogmusic_dirname) + if os.path.exists(ogmusic_filepath): + print(os.path.basename(ogmusic_filepath), "EXISTS. SKIPPING.") + else: + shutil.copy(filepath, ogmusic_filepath) + #shutil.copytree(os.path.join(path, "Music"), os.path.join(path, "original_music"), dirs_exist_ok=True) if not os.path.exists(os.path.join(path, "Music")): os.makedirs(os.path.join(path, "Music")) else: @@ -75,91 +158,263 @@ def move_music_to_ogmusic(path): return False +def filename_to_filepath(filename, path): + path = os.path.abspath(path) + original_filepaths = check_folder_structure(path) + if original_filepaths != False: + for file_path in original_filepaths: + if os.path.basename(file_path) == filename: + old_song_path = file_path + break + return old_song_path + else: + return False + + +def replace_song(new_song_path,old_song_name, megamaker_folder): + path = os.path.abspath(megamaker_folder) + old_song_path = filename_to_filepath(old_song_name, megamaker_folder) + subfolder = os.path.basename(os.path.dirname(old_song_path)) + backup_path = os.path.join(megamaker_folder, "original_specific_songs", subfolder, old_song_name) + ##backup_song + if old_song_name != False: + os.makedirs(os.path.dirname(backup_path), exist_ok=True) + if os.path.isfile(backup_path): + shutil.copy(new_song_path, old_song_path) + print("skipping backup file. why, it already exists!") + else: + try: + shutil.copy(old_song_path, backup_path) + shutil.copy(new_song_path, old_song_path) + except FileNotFoundError: + print("FILE NOT FOUND") +def restore_song(old_song_name, megamaker_folder): + path = os.path.abspath(megamaker_folder) + old_song_path = filename_to_filepath(old_song_name, megamaker_folder) + subfolder = os.path.basename(os.path.dirname(old_song_path)) + backup_path = os.path.join(megamaker_folder, "original_specific_songs", subfolder, old_song_name) + if old_song_name != False: + try: + shutil.copy(backup_path, old_song_path) + except FileNotFoundError: + return False + def recreate_structure(new_nsf, original_paths_list): for path in original_paths_list: if not os.path.exists(os.path.dirname(path)): os.makedirs(os.path.dirname(path)) - shutil.copy(new_nsf, path) + print("recreate structure if not os.path.exists") + try: + shutil.copy(new_nsf, path) + except: + print("continuing") + continue #check if the contents of files in original_music != the files in music AND check if contents of input nsf == every file in music. if all of that is true replace music with original_music def restore_ogmusic(path): if os.path.exists(os.path.join(path, "original_music")): if os.path.exists(os.path.join(path, 'Music')): if check_folder_structure(path): - if check_folder_structure(path, og_music=True): - if not filecmp.cmp(os.path.join(path, "Music", "MM1", 'Cutman.nsf'), os.path.join(path, "original_music", "MM1", "Cutman.nsf"), shallow=False): - shutil.rmtree(os.path.join(path, "Music")) - shutil.move(os.path.join(path, "original_music", ""), os.path.join(path, "Music")) - return True - else: - print("something went wrong...") - else: - return False + #if check_folder_structure(path, og_music=True): + #if not filecmp.cmp(os.path.join(path, "Music", "MM1", 'Cutman.nsf'), os.path.join(path, "original_music", "MM1", "Cutman.nsf"), shallow=False): + #shutil.rmtree(os.path.join(path, "Music")) + ogmusic = os.path.join(path, "original_music") + music = os.path.join(path, "Music") + shutil.copytree(ogmusic, music, dirs_exist_ok=True) + shutil.rmtree(ogmusic) + #files = os.listdir(ogmusic) + #print(files) + #for filename in files: + #filepath = os.path.join(music, os.path.basename(filename)) + #shutil.move(os.path.join(src, filename), os.path.join(path, "Music")) + return True + #else: + # print("something went wrong...") + #else: + # print("if check_folder_structure(path, og_music=True):") + # return False else: + print("if check_folder_structure(path):") return False else: + print("if os.path.exists(os.path.join(path, 'Music')):") return False else: + print("os.path.exists(os.path.join(path, 'original_music')):") return False -def nsf_changer (nsf_path, megamaker_folder): - original_path = check_folder_structure(megamaker_folder) +def nsf_changer (nsf_path, megamaker_folder, whitelist=[]): + original_path = check_folder_structure(megamaker_folder, whitelist=whitelist) + + if whitelist: + full_original_path = original_path + original_path = [] + for files in full_original_path: + files_no_path = os.path.basename(files) + if files_no_path in whitelist: + original_path.append(files) + #if not whitelist or files_no_path in whitelist: if original_path: - move_music_to_ogmusic(megamaker_folder) + try: + common = os.path.commonpath([os.path.dirname(nsf_path), os.path.join(megamaker_folder, "Music")]) + except ValueError: + common = False + if common == os.path.join(megamaker_folder, "Music"): + restore_ogmusic(megamaker_folder) + shutil.copy(nsf_path, os.path.join(megamaker_folder, os.path.basename(nsf_path))) + nsf_path = os.path.join(megamaker_folder, os.path.basename(nsf_path)) + #move_music_to_ogmusic(megamaker_folder) + copy_music_to_ogmusic(megamaker_folder) recreate_structure(nsf_path, original_path) + if common == os.path.join(megamaker_folder, "Music"): + if os.path.dirname(nsf_path) == megamaker_folder: + os.remove(nsf_path) + print("nsf changer returning true") return True else: + print("nsf changer returning false") return False -sg.theme('LightBlue') +sg.theme('Material1') icon = b"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAArlBMVEUAAAAAAAB5eXmioqIwUYJBksNh0+Oi//MwYUFJomlx45Ki/8s4bQBJqhBx80Gi86I4aQBRogCa6wDL84JJWQCKigDr0yD/85J5QQDDcQD/ogD/26KiMADjUQD/eTD/y7qyEDDbQWH/YbL/uuuaIHnbQcPzYf/jsv9hEKKSQfOicf/Dsv8oALpBQf9Rgv+iuv8gALJBYfthov+S0/95eXmysrLr6+v///8Aaf9DkP8A2fKiAAAAOnRSTlMA////////////////////////////////////////////////////////////////////////////ncmmFQAAAK5JREFUOI21k1sSgCAIRb2rYtz/xpKnpIz1UcyUAkcEldYfpL0DwN8iGRja6g7EgLkix3Jg8ScqAyVxrOJDgIjGhHSQMQw8WwA3+4QBxFoxj8BKSmkMzMXoGZW6a4CzMw0OmJO3I6khkhVA0hBVCNoBuMrhPR4KwKNgZqSAhjZTQmEnaUQcD9Bjz+aXHT+9g3gCbV5LLf8BWw7HR3t/53e/d1bVNhkoGy911kkegQvP3acj3R2TXgAAAABJRU5ErkJggg==" gif = b"R0lGODdhQgAmAHcAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCgAAACwAAAAAQgAmAIAAAAAAefsCzISPqcvtD6OctNqLs95chg92YgOGT/mNExpArOqhrgzPpW3WznvSHM8AknwbocK4QGKUBybCWYFCDdMV8XhNZpfbZvf5BUjD1fFteA6GqeR2WvtWxxPmHNwux9PdevAc++cVyDYotmZYWJeCaFLGt5i3yON4NvnYwih5aFl5Sdi4ScPZJwjaeWo3qpko2ooKmYmpKutqaruaWou7S/vae4v5ySv8OxxLjFxqPJvMfKxcHN2sK/087XsNnA3r3E29HfzsjS3+rXOOnn5QAAAh+QQJCgAAACwCAAYAPgAeAIAAAAAAefsClYSPqcvtD6OctNqLcw28a9R5W0hy31FaZXoCLLWSrSuPsdm+0y2eusTD+Wqw4OwXCQaOxJ0x1wQ+hyHV9IOEKJlV243bc/IM2QerTH6iG+doYkvrFrtr+JrRli/stXr/T6cGGKaQJ2IYkxa4eDiIg7ii2Mj46LgUN/mFWUl5CSnJqfm56WkJWjpGerqq2go5CmvqSlIAACH5BAkKAAAALAIABgA+AB4AgAAAAAB5+wKthI+pF+0Lo5zTPYqzBrbtDyZdEJbfaKYYqm4s9C5xOYudVIM5c0e724MFZUNaj/XjFEPI4xKRzDQthqT1qaRmL1et0GvTTi9fMhEMdYqxVfbWc+aqzYounbfO39Po8P5NAtjWxyenB1c3B2e3qBiYeBjIKOlY1hiJUykYd2n4N+jZiegnSkmIF2o6kgpa2qrqCjgWOwtr+1r7qruZK6vZC+wYjMn7u0o8zFpsUQAAIfkECQoAAAAsAgAGAD4AHgCAAAAAAHn7AqGEb6GL7Q+jfIvNi3OrSvsPcQFIkmKJAifFpaW4ru4Ht6o9e3WV4K7M2o1uvBzQIewQLbPjJtkrNn1I6FI5LcaszpdtK4wyf99y+DrMWsA7MVbDVsZh7jTcvMbX6vzIvJ809OenJ1g42HWAeFjYF9KIVrcoFZSnxXiZOValKYdpCerZaTgqCTkZSpqKKpoK+Nkaqyr7Oso6i1u7espbGolWAAAh+QQJCgAAACwCAAgAPgAcAIAAAAAAefsCooSPCcGtD6OcqTmKs142bNl5nxKOUGkiaKp27LG+nCXPVx2nObuPvfnbBD/DzPDokiFpkd/y9ngyDCGp6JTEMqvZaJf0rSS5TC0UXPZuydPm+J1GX9htcxtlpdJB8Hm4tcbGF+gXJ0bIpYcogvcH05docwfJ6KhYSHY5uah5pYYZ2cgp6Sm3mUnaqZrKKorqehpbOUo3C2oruwpbpUvZS9tRAAA7" +mute_nsf = "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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +print("Checking Version...") if check_version(): version_color = "black" version_message = f"Works with Mega Man Maker {mmm_version}" else: version_color = "red" version_message = f"Mega Man Maker has been updated to {check_version(version=True)}. \nThis tool works with {mmm_version} and may not work properly. Make an Issue at GitLab." - -layout = [ - [sg.Text("Mega Man Maker NSF Music Changer | Made by Timothy GFO", justification="center")], - [sg.Text("YouTube", enable_events = True, key="-YT-", text_color="red"), sg.Text("GitLab",enable_events = True,key="-GIT-", text_color="purple"), sg.Text("Blog",enable_events = True,key="-SITE-", text_color="blue")], - [sg.Text(version_message, text_color=version_color)], - [sg.Text("Input NSF File:"), sg.Input(key="-IN-"),sg.FileBrowse(file_types=(("NSF Files", "*.nsf*"),))], - [sg.Text("MegaMaker Folder:"), sg.Input(key="-OUT-"), sg.FolderBrowse()], - [sg.Exit(),sg.Button("About") , sg.Button("Replace NSF Music"), sg.Button("Restore Original Music"), sg.Image(data=gif, key='_IMAGE_')], +print(" done") +title = [ + [sg.Push(),sg.Text("Mega Man Maker NSF Music Changer | Made by Timothy GFO", font=("Any", 12, "bold")),sg.Push()], + [sg.Push(), sg.Text("YouTube", enable_events = True, key="-YT-", text_color="red"), sg.Text("GitLab",enable_events = True,key="-GIT-", text_color="purple"), sg.Text("Blog",enable_events = True,key="-SITE-", text_color="blue"), sg.Push()], + [sg.Push(), sg.Text(version_message, text_color=version_color), sg.Push()], ] +change_all = [ + [sg.Button("Mute"), sg.Button("Apply Music"), sg.Button("Restore Music")], + [sg.Radio("All", "music", default=True, key="-ALL-"), sg.Radio("Boss Music","music", key="-BOSS-"), sg.Radio("Level Music", "music", key="-LEVEL-"), sg.Checkbox("All except selected", key="-BLACKLIST-")] +] + +change_one = [ + [sg.Text("Output NSF File:"),sg.Combo(safelist(),default_value="ClownMan.nsf", key="-DROPDOWN-"), sg.StatusBar("",size=(15,1), key="-LAST-CHANGED-")], + [sg.Button("Replace Song"), sg.Button("Restore Original Song"), sg.Button("Download and Restore Music")], +] + +layout = [ + [title], + [sg.Text("Input NSF File:"), sg.Input(sg.user_settings_get_entry('-INPUT_NSF-', ''), key="-IN-"),sg.FileBrowse(file_types=(("Supported Formats",".nsf .spc .vgm .nsfe .gbs .gym .ay .hes .kss .sap"),("NSF Files", "*.nsf*"),("NSFe Files", "*.nsfe*"),("SNES Files", "*.spc*"),("Gameboy Files", "*.gbs*"), ("Sega Genesis VGM", "*.vgm*"),("Sega Genesis GYM", "*.gym*"), ("ZX Spectrum", "*.ay*"), ("TurboGrafx 16", "*.hes*"), ("MSX Home Computer", "*.kss*"), ("Atari Pokey", "*.sap*"),))], + [sg.Text("MegaMaker Folder:"), sg.Input(sg.user_settings_get_entry('-MEGAMAKER_FOLDER-', ''), key="-OUT-"), sg.FolderBrowse()], + [sg.TabGroup([[sg.Tab("Play Now", change_all), sg.Tab("Switch a Song", change_one)]]),], + [sg.Exit(),sg.Button("About"),sg.Push(), sg.Frame('\U0001F3B5 Now Playing \U0001F3B5', [[sg.Text("Default Songs", key="-PLAYING-")]]), sg.Image(data=gif, key='_IMAGE_')], +] how_to_use = "I made this tool to be able to change the music for any Mega Man Maker level while playing the game. It works by replacing every nsf file with a custom one, but while preserving the original folders and file names.\n\nTo use it, just choose a nsf file and then tell the program where you store the *game* folder. That is where the .exe is, not the user folder.\n\nIf you find this useful, you can check out my YouTube Channel or my blog since I will keep making tools like this. Also, if you find any bugs or errors remember to notify me at GitLab by making an Issue." +window = sg.Window(f"MMM NSF Music Changer {app_version}", layout) +#disclaimer = sg.popup_yes_no('NSF Changer, by Timothy GFO', 'This program is not complete, there WILL be bugs. Use at your own risk.', 'Do you want to continue?') -window = sg.Window("MMM NSF Music Changer v1.0", layout, grab_anywhere=True) -disclaimer = sg.popup_yes_no('NSF Changer, by Timothy GFO', 'This program is not complete, there WILL be bugs. Use at your own risk.', 'Do you want to continue?') -while disclaimer == "Yes": +if check_if_new_version(): + sg.popup(f"There is a new update for MMM NSF Changer. Get it at the GitLab repo.") +running = False +while True: event,values = window.read(timeout=50) window.set_icon(icon) + window.refresh() if event in (sg.WINDOW_CLOSED,"Exit"): + try: + sg.user_settings_set_entry('-INPUT_NSF-', values['-IN-']) + sg.user_settings_set_entry('-MEGAMAKER_FOLDER-', values['-OUT-']) + except TypeError: + pass break window.Element('_IMAGE_').UpdateAnimation(gif, time_between_frames=100) - if event == "-YT-": - webbrowser.open("https://youtube.com/gamefeveronline") - if event == "-SITE-": - webbrowser.open("https://gamefeveronline.gitlab.io") - if event == "-GIT-": - webbrowser.open("https://gitlab.com/gamefeveronline") - if event == "About": - sg.popup(how_to_use) - if event == "Replace NSF Music": - if not nsf_changer(values["-IN-"], values["-OUT-"]): - restore_ogmusic(values["-OUT-"]) - if not nsf_changer(values["-IN-"], values["-OUT-"]): - sg.popup_error("MegaMaker folder is not valid or definitions out of date.") - if event == "Restore Original Music": - # sg.popup_error("DOESNT WORK EITHER HAHAHAHAHAHAA") - if not restore_ogmusic(values["-OUT-"]): - sg.popup_error("Unexpected folder structure or differing files") + if running: + if event == "-THREAD DONE-": + running = False + sg.PopupAnimated(None) + else: + window.refresh() + sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, message='Loading', time_between_frames=100, location=window.current_location()) + else: + if event == "-YT-": + webbrowser.open("https://youtube.com/gamefeveronline") + if event == "-SITE-": + webbrowser.open("https://gamefeveronline.gitlab.io") + if event == "-GIT-": + webbrowser.open("https://gitlab.com/gamefeveronline") + if event == "About": + sg.popup(how_to_use) + if event == "Mute": + if values["-ALL-"] != True: + if values["-BOSS-"] == True: + if values["-BLACKLIST-"] == True: + whitelist = whitelist_to_blacklist(boss_music()) + else: + whitelist = boss_music() + elif values["-LEVEL-"] == True: + if values["-BLACKLIST-"] == True: + whitelist = whitelist_to_blacklist(level_music()) + else: + whitelist = level_music() + else: + whitelist = [] + create_mute_file(mute_nsf) + if not nsf_changer(os.path.join(os.path.dirname(__file__), "mmm_nsf_changer_mute.nsf"), values["-OUT-"], whitelist=whitelist): + restore_ogmusic(values["-OUT-"]) + if not nsf_changer(os.path.join(os.path.dirname(__file__), "mmm_nsf_changer_mute.nsf"), values["-OUT-"], whitelist=whitelist): + sg.popup_error("MegaMaker folder is not valid or definitions are out of date.") + window["-PLAYING-"].update(f"MUTED") + if event == "Replace Song": + if values["-DROPDOWN-"] == "": + sg.popup_error('Please select which Output NSF File to replace. If not, use "Replace *All* NSF Music".') + else: + replace_song(values["-IN-"], values["-DROPDOWN-"], values["-OUT-"]) + window["-LAST-CHANGED-"].update(values["-DROPDOWN-"]) + if event == "Restore Original Song": + if values["-DROPDOWN-"] == "": + sg.popup_error('Please select which Output NSF File to replace. If not, use "Replace *All* NSF Music".') + else: + if restore_song(values["-DROPDOWN-"], values["-OUT-"]) == False: + sg.popup_error("The selected song has not been changed, cannot restore.") + else: + window["-LAST-CHANGED-"].update("") + if event == "Apply Music": + if values["-ALL-"] != True: + if values["-BOSS-"] == True: + if values["-BLACKLIST-"] == True: + whitelist = whitelist_to_blacklist(boss_music()) + else: + whitelist = boss_music() + elif values["-LEVEL-"] == True: + if values["-BLACKLIST-"] == True: + whitelist = whitelist_to_blacklist(level_music()) + else: + whitelist = level_music() + else: + whitelist = [] + + if not nsf_changer(values["-IN-"], values["-OUT-"], whitelist=whitelist): + restore_ogmusic(values["-OUT-"]) + if not nsf_changer(values["-IN-"], values["-OUT-"], whitelist=whitelist): + sg.popup_error("MegaMaker folder is not valid or definitions out of date.") + window["-PLAYING-"].update(os.path.basename(values["-IN-"])) + if event == "Restore Music": # Needs testing + # sg.popup_error("DOESNT WORK EITHER HAHAHAHAHAHAA") + if not restore_ogmusic(values["-OUT-"]): + sg.popup_error("Already Restored or wrong Mega Man Maker folder.") + window["-PLAYING-"].update("Default Songs") + if event == "Download and Restore Music": + sg.popup_yes_no("WARNING: This will erase all custom songs.\nDo you want to continue?", location=window.current_location()) + window["-LAST-CHANGED-"].update("") + threading.Thread(target=get_original_songs, args=(values["-OUT-"], ), daemon=True).start() + running = True + #if not restore_ogmusic(values["-OUT-"]): + # sg.popup_error("Unexpected folder structure or differing files") + window.close() diff --git a/nsf_changer.png b/nsf_changer.png index adb7382..bfdb13b 100644 Binary files a/nsf_changer.png and b/nsf_changer.png differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4171be9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +certifi==2023.11.17 +charset-normalizer==3.3.2 +idna==3.4 +packaging==23.2 +PySimpleGUI==4.60.5 +requests==2.31.0 +urllib3==2.1.0