├── .flake8 ├── .gitattributes ├── .gitignore ├── .mypy.ini ├── LICENSE ├── README.md ├── attach_settings ├── attach_sram ├── binary_patch ├── cabinets.yaml ├── config.yaml ├── docs └── naomi.md ├── edit_settings ├── eeprominfo ├── homebrew ├── netbootmenu │ ├── Makefile │ ├── change.raw │ ├── check.raw │ ├── common.h │ ├── config.c │ ├── config.h │ ├── controls.c │ ├── controls.h │ ├── cursor.png │ ├── dejavusans.ttf │ ├── dn.png │ ├── main.c │ ├── netbootmenu.bin │ ├── screens.c │ ├── screens.h │ ├── scroll.raw │ └── up.png ├── settingstrojan │ ├── Makefile │ ├── crt0.s │ ├── eeprom.c │ ├── eeprom.h │ ├── font.h │ ├── settingstrojan.bin │ ├── trojan.c │ ├── video.c │ └── video.h └── sramdump │ ├── Makefile │ ├── main.c │ └── sramdump.bin ├── host_debug_server ├── make_freeplay_patch ├── make_no_attract_patch ├── naomi ├── README.md ├── __init__.py ├── eeprom.py ├── generic_patch.py ├── py.typed ├── rom.py ├── rom_patch.py └── settings │ ├── README.md │ ├── __init__.py │ ├── definitions │ ├── BAC0.settings │ ├── BAU0.settings │ ├── BBG0.settings │ ├── BBN1.settings │ ├── BDF0.settings │ ├── BDM0.settings │ ├── README.md │ ├── serialmapping.txt │ └── system.settings │ ├── editor.py │ ├── py.typed │ └── settings.py ├── naomi_sram ├── netboot ├── __init__.py ├── cabinet.py ├── directory.py ├── hostutils.py ├── log.py ├── patch.py ├── settings.py ├── sram.py └── web │ ├── __init__.py │ ├── app.py │ ├── static │ ├── app.css │ ├── app.js │ ├── axios.js │ ├── loading-16.gif │ └── vue.js │ └── templates │ ├── addcabinet.html │ ├── gameconfig.html │ ├── index.html │ ├── romconfig.html │ └── systemconfig.html ├── netdimm ├── README.md ├── __init__.py ├── message.py ├── netdimm.py └── py.typed ├── netdimm_ensure ├── netdimm_info ├── netdimm_menu ├── netdimm_peekpoke ├── netdimm_receive ├── netdimm_send ├── patch_default_settings ├── patches ├── 18_Wheeler_Deluxe_freeplay.binpatch ├── 18_Wheeler_Deluxe_no_attract.binpatch ├── 18_Wheeler_Standard_freeplay.binpatch ├── 18_Wheeler_Standard_no_attract.binpatch ├── Airline_Pilots_freeplay.binpatch ├── Airline_Pilots_no_attract.binpatch ├── Akatsuki_Blitzkampf_Ausf_Achse_freeplay.binpatch ├── Akatsuki_Blitzkampf_Ausf_Achse_no_attract.binpatch ├── Alien_Front_freeplay.binpatch ├── Alien_Front_no_attract.binpatch ├── Azumanga_Daioh_Puzzle_Bobble_freeplay.binpatch ├── Azumanga_Daioh_Puzzle_Bobble_no_attract.binpatch ├── Beach_Spikers_freeplay.binpatch ├── Beach_Spikers_no_attract.binpatch ├── Border_Down_freeplay.binpatch ├── Border_Down_no_attract.binpatch ├── CapcomVsSNK2_Mark_Of_the_Millennium_freeplay.binpatch ├── CapcomVsSNK2_Mark_Of_the_Millennium_no_attract.binpatch ├── CapcomVsSNK2_Millionaire_fighting_freeplay.binpatch ├── CapcomVsSNK2_Millionaire_fighting_no_attract.binpatch ├── CapcomVsSNKMillenniumFight_freeplay.binpatch ├── Capcom_vs_SNK_2_Mark_Of_The_Millennium_2001_unlocked.binpatch ├── Capcom_vs_SNK_2_Millionaire_Fighting_2001_unlocked.binpatch ├── Capcom_vs_SNK_Millenium_Fight_2000_Pro_freeplay.binpatch ├── Capcom_vs_SNK_Millenium_Fight_2000_regular_or_Pro_no_attract.binpatch ├── Capcom_vs_SNK_Millenium_Fight_2000_unlocked.binpatch ├── Chaos_Field_freeplay.binpatch ├── Chaos_Field_no_attract.binpatch ├── Cleopatra_Fortune_Plus_freeplay.binpatch ├── Cleopatra_Fortune_Plus_no_attract.binpatch ├── Club_Kart_European_Session_Unlocked_freeplay.binpatch ├── Club_Kart_European_Session_Unlocked_no_attract.binpatch ├── Confidential_Mission_freeplay.binpatch ├── Confidential_Mission_no_attract.binpatch ├── Cosmic_Smash_freeplay.binpatch ├── Cosmic_Smash_no_attract.binpatch ├── CrazyTaxi_freeplay.binpatch ├── CrazyTaxi_no_attract.binpatch ├── CrazyTaxi_noprotect.binpatch ├── Dead_or_Alive_2_Millennium_freeplay.binpatch ├── Dead_or_Alive_2_Millennium_no_attract.binpatch ├── Dead_or_Alive_2_freeplay.binpatch ├── Dead_or_Alive_2_no_attract.binpatch ├── Death_Crimson_OX_freeplay.binpatch ├── Death_Crimson_OX_no_attract.binpatch ├── Doki_Doki_Idol_Star_Seeker_freeplay.binpatch ├── Doki_Doki_Idol_Star_Seeker_no_attract.binpatch ├── Dynamic_Golf_Virtua_Golf_freeplay.binpatch ├── Dynamic_Golf_Virtua_Golf_no_attract.binpatch ├── Dynamite_Deka_Ex__Asian_Dynamite_freeplay.binpatch ├── Dynamite_Deka_Ex__Asian_Dynamite_no_attract.binpatch ├── Giant_Gram_2000_freeplay.binpatch ├── Giant_Gram_2000_no_attract.binpatch ├── Giant_Gram_All_Japan_Pro_Wrestling_2_freeplay.binpatch ├── Giant_Gram_All_Japan_Pro_Wrestling_2_no_attract.binpatch ├── Giga_Wing_2_freeplay.binpatch ├── Giga_Wing_2_no_attract.binpatch ├── GuiltyGearX_auto_init_eeprom.binpatch ├── GuiltyGearX_freeplay.binpatch ├── GuiltyGearX_no_attract.binpatch ├── Guilty_Gear_XX_Accent_Core_freeplay.binpatch ├── Guilty_Gear_XX_Accent_Core_no_attract.binpatch ├── Guilty_Gear_XX_Reload_freeplay.binpatch ├── Guilty_Gear_XX_Reload_no_attract.binpatch ├── Guilty_Gear_XX_Slash_freeplay.binpatch ├── Guilty_Gear_XX_Slash_no_attract.binpatch ├── Guilty_Gear_XX_freeplay.binpatch ├── Guilty_Gear_XX_no_attract.binpatch ├── Gun_Spike_Cannon_Spike_freeplay.binpatch ├── Gun_Spike_Cannon_Spike_no_attract.binpatch ├── Gun_Survivor_2_Biohazard_Code_Veronica_freeplay_nocomm.binpatch ├── Gun_Survivor_2_Biohazard_Code_Veronica_no_attract_nocomm.binpatch ├── Heavy_Metal_Geomatrix_freeplay.binpatch ├── Ikaruga_freeplay.binpatch ├── Ikaruga_no_attract.binpatch ├── Illvelo_freeplay.binpatch ├── Illvelo_no_attract.binpatch ├── Initial_D2_Export_freeplay.binpatch ├── Initial_D2_Export_no_attract.binpatch ├── Initial_D2_Japan_Rev_B_freeplay.binpatch ├── Initial_D2_Japan_Rev_B_no_attract.binpatch ├── Initial_D2_Japan_freeplay.binpatch ├── Initial_D2_Japan_no_attract.binpatch ├── Initial_D3_EXPORT_freeplay.binpatch ├── Initial_D3_EXPORT_no_attract.binpatch ├── Initial_D_Export_freeplay.binpatch ├── Initial_D_Export_no_attract.binpatch ├── Initial_D_Japan_freeplay.binpatch ├── Initial_D_Japan_no_attract.binpatch ├── Jambo_Safari_freeplay.binpatch ├── Jambo_Safari_no_attract.binpatch ├── Jambo_Safari_noprotect.binpatch ├── Jingi_Storm_-_The_Arcade_freeplay.binpatch ├── Jingi_Storm_-_The_Arcade_no_attract.binpatch ├── Karous_freeplay.binpatch ├── Karous_no_attract.binpatch ├── King_of_Route_66_freeplay.binpatch ├── King_of_Route_66_no_attract.binpatch ├── Kurukuru_Chameleon_freeplay.binpatch ├── Kurukuru_Chameleon_no_attract.binpatch ├── La_Keyboard_freeplay.binpatch ├── La_Keyboard_no_attract.binpatch ├── Lupin-TheTyping_freeplay.binpatch ├── Lupin-TheTyping_no_attract.binpatch ├── Lupin_The_Third_-_The_Shooting_freeplay.binpatch ├── Lupin_The_Third_-_The_Shooting_no_attract.binpatch ├── Mamoru_kun_wa_Norowarete_Shimatta_freeplay.binpatch ├── Mamoru_kun_wa_Norowarete_Shimatta_no_attract.binpatch ├── MarvelVsCapcom2_freeplay.binpatch ├── MarvelVsCapcom2_no_attract.binpatch ├── MarvelVsCapcom2_unlocked.binpatch ├── MeltyBloodActressAgain_freeplay.binpatch ├── MeltyBloodActressAgain_no_attract.binpatch ├── Melty_Blood_Act_Cadenza_Ver_b_freeplay.binpatch ├── Melty_Blood_Act_Cadenza_freeplay.binpatch ├── Melty_Blood_Act_Cadenza_no_attract.binpatch ├── Melty_Blood_Act_Cadenza_ver_a_freeplay.binpatch ├── Melty_Blood_Act_Cadenza_ver_a_no_attract.binpatch ├── Melty_Blood_Act_Cadenza_ver_b_no_attract.binpatch ├── Mobile_Suit_Gundam_Federation_Vs_Zeon_DX_freeplay.binpatch ├── Mobile_Suit_Gundam_Federation_Vs_Zeon_DX_no_attract.binpatch ├── Mobile_Suit_Gundam_Federation_Vs_Zeon_freeplay.binpatch ├── Mobile_Suit_Gundam_Federation_Vs_Zeon_no_attract.binpatch ├── Moero_Justice_Gakuen_Project_Justice_unlocked_freeplay.binpatch ├── Moero_Justice_Gakuen_Project_Justice_unlocked_no_attract.binpatch ├── Moeru_Casinyo_burning_casino_freeplay.binpatch ├── MonkeyBall_freeplay.binpatch ├── MonkeyBall_no_attract.binpatch ├── Musapeys_Choco_Marker_freeplay.binpatch ├── Musapeys_Choco_Marker_no_attract.binpatch ├── Noukone_Puzzle_Takoron_freeplay.binpatch ├── Noukone_Puzzle_Takoron_no_attract.binpatch ├── OutTrigger_freeplay.binpatch ├── OutTrigger_no_attract.binpatch ├── PowerStone2_freeplay.binpatch ├── PowerStone2_no_attract.binpatch ├── Powerstone-alt_freeplay.binpatch ├── Powerstone-alt_no_attract.binpatch ├── Psyvariar2_v6_freeplay.binpatch ├── Psyvariar2_v6_no_attract.binpatch ├── PuyoPuyoDa_freeplay.binpatch ├── PuyoPuyoDa_no_attract.binpatch ├── PuyoPuyoFever_freeplay.binpatch ├── PuyoPuyoFever_no_attract.binpatch ├── QuizKeitaiQMode_freeplay.binpatch ├── QuizKeitaiQMode_no_attract.binpatch ├── RadirgyNoa_v6_freeplay.binpatch ├── Radirgy_freeplay.binpatch ├── Radirgy_no_attract.binpatch ├── Rhythm_Tengoku_freeplay.binpatch ├── Rhythm_Tengoku_no_attract.binpatch ├── Samba_De_Amigo_freeplay.binpatch ├── Samba_De_Amigo_no_attract.binpatch ├── SegaTetris_freeplay.binpatch ├── SegaTetris_no_attract.binpatch ├── Sega_Marine_Fishing_freeplay.binpatch ├── Sega_Marine_Fishing_no_attract.binpatch ├── Sega_Strike_Fighter_freeplay.binpatch ├── Sega_Strike_Fighter_no_attract.binpatch ├── Senko_No_Ronde_Special_freeplay.binpatch ├── Senko_No_Ronde_Special_no_attract.binpatch ├── Senko_No_Ronde_freeplay.binpatch ├── Senko_No_Ronde_no_attract.binpatch ├── ShikigamiNoShiroII_v6_freeplay.binpatch ├── ShikigamiNoShiroII_v6_no_attract.binpatch ├── Shin_Nihon_Pro_Wrestling_Toukon_Retsuden_4_Arcade_Edition_freeplay.binpatch ├── Shin_Nihon_Pro_Wrestling_Toukon_Retsuden_4_Arcade_Edition_no_attract.binpatch ├── Shooting_Love_freeplay.binpatch ├── Shooting_Love_no_attract.binpatch ├── Shootout_Pool_freeplay.binpatch ├── Shootout_Pool_no_attract.binpatch ├── Slashout_freeplay.binpatch ├── Slashout_no_attract.binpatch ├── Spawn_In_the_Demon's_Hand_freeplay.binpatch ├── Spikers_Battle_freeplay.binpatch ├── Spikers_Battle_no_attract.binpatch ├── Sports_Jam_freeplay.binpatch ├── Sports_Jam_no_attract.binpatch ├── StreetFighterZero3Upper_freeplay.binpatch ├── StreetFighterZero3Upper_no_attract.binpatch ├── Super_Major_League_World_Series_Baseball_freeplay.binpatch ├── Super_Major_League_World_Series_Baseball_no_attract.binpatch ├── Super_Shanghai_2005_freeplay.binpatch ├── Super_Shanghai_2005_no_attract.binpatch ├── Super_Shanghai_2005_rev0_freeplay.binpatch ├── Super_Shanghai_2005_rev0_no_attract.binpatch ├── Tetris_Kiwamemiti_freeplay.binpatch ├── Tetris_Kiwamemiti_no_attract.binpatch ├── TheTypingOfTheDead_freeplay.binpatch ├── TheTypingOfTheDead_no_attract.binpatch ├── The_Maze_of_the_Kings_freeplay.binpatch ├── The_Maze_of_the_Kings_no_attract.binpatch ├── Toy_Fighter_freeplay.binpatch ├── Toy_Fighter_no_attract.binpatch ├── TriggerHeartExelica_freeplay.binpatch ├── TriggerHeartExelica_no_attract.binpatch ├── Trizeal_freeplay.binpatch ├── Trizeal_no_attract.binpatch ├── UnderDefeat_freeplay.binpatch ├── UnderDefeat_no_attract.binpatch ├── Usagi_Yamashiro_Mahjong_Hen_freeplay.binpatch ├── Usagi_Yamashiro_Mahjong_Hen_no_attract.binpatch ├── VirtuaTennis2_freeplay.binpatch ├── VirtuaTennis2_no_attract.binpatch ├── VirtuaTennis_freeplay.binpatch ├── VirtuaTennis_no_attract.binpatch ├── Virtua_Athletics_Virtua_Athlete_freeplay.binpatch ├── Virtua_Athletics_Virtua_Athlete_no_attract.binpatch ├── Virtua_Fighter_4_Evolution_Rev_B_freeplay.binpatch ├── Virtua_Fighter_4_Evolution_Rev_B_no_attract.binpatch ├── Virtua_Fighter_4_Final_Tuned_Rev_B_freeplay.binpatch ├── Virtua_Fighter_4_Final_Tuned_Rev_B_no_attract.binpatch ├── Virtua_Fighter_4__freeplay.binpatch ├── Virtua_Fighter_4__no_attract.binpatch ├── Virtua_NBA_freeplay.binpatch ├── Virtua_NBA_no_attract.binpatch ├── Virtua_Striker_2_Ver_2000_freeplay.binpatch ├── Virtua_Striker_2_Ver_2000_no_attract.binpatch ├── Virtua_Striker_3_freeplay.binpatch ├── Virtua_Striker_3_no_attract.binpatch ├── Virtual_On_Oratorio_Tangram_freeplay.binpatch ├── Virtual_On_Oratorio_Tangram_no_attract.binpatch ├── WWF_Royal_Rumble_freeplay.binpatch ├── WWF_Royal_Rumble_no_attract.binpatch ├── Wave_Runner_GP_freeplay.binpatch ├── Wave_Runner_GP_no_attract.binpatch ├── Wild_Riders_freeplay.binpatch ├── Wild_Riders_no_attract.binpatch ├── Zero_Gunner_2_freeplay.binpatch ├── Zero_Gunner_2_no_attract.binpatch ├── Zombie_Revenge_freeplay.binpatch ├── Zombie_Revenge_no_attract.binpatch ├── aw-baskets_AW16_monitor_orientation.patch ├── knights_of_valour_the_7_spirits-dubhe_unlocked.patch ├── metal_slug_6_violent.binpatch ├── neogeobattlecoliseum-unlocked.patch ├── rhytngk_freeplay.binpatch ├── rhytngk_no_attract.binpatch ├── the_king_of_fighters_neo_wave-characters_unlocked.patch ├── the_king_of_fighters_xi-all_fighters.patch ├── the_rumble_fish-characters_unlocked.patch ├── the_rumble_fish_2-unlocked.patch ├── tokyo_bus_tour_freeplay.binpatch └── tokyo_bus_tour_no_attract.binpatch ├── requirements.txt ├── rominfo ├── roms └── README.txt ├── scripts ├── __init__.py ├── attach_settings.py ├── attach_sram.py ├── binary_patch.py ├── edit_settings.py ├── eeprominfo.py ├── host_debug_server.py ├── make_freeplay_patch.py ├── make_no_attract_patch.py ├── naomi_sram.py ├── netdimm_ensure.py ├── netdimm_info.py ├── netdimm_menu.py ├── netdimm_peekpoke.py ├── netdimm_receive.py ├── netdimm_send.py ├── patch_default_settings.py ├── rominfo.py └── server.wsgi ├── services ├── README.md ├── kiosk.service ├── kiosk.sh ├── menu.service ├── menu.sh └── setup_fixed_ip.sh ├── settings ├── README.md ├── __init__.py ├── py.typed └── settings.py ├── setup.py ├── srams └── README.txt └── tests ├── __init__.py ├── test_cabinet.py └── test_settings.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore=E501,W504 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bin diff=bin 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .hg/ 2 | .hgignore 3 | *.swp 4 | *.swo 5 | .mypy_cache/ 6 | __pycache__/ 7 | *.yaml 8 | build/ 9 | libnaomi.a 10 | libnaomimessage.a 11 | MANIFEST.in 12 | build/ 13 | download/ 14 | dist/ 15 | sdist/ 16 | wheels/ 17 | *.egg-info/ 18 | *.egg 19 | .netdimm_menu_settings.yaml 20 | MANIFEST 21 | homebrew/examples/*/*.bin 22 | homebrew/tests/*.bin 23 | -------------------------------------------------------------------------------- /.mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | strict=True 3 | exclude=(?x)(^build/) 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /attach_settings: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /attach_sram: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /binary_patch: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /cabinets.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/cabinets.yaml -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | cabinet_config: cabinets.yaml 2 | rom_directory: 3 | - roms 4 | patch_directory: 5 | - patches 6 | sram_directory: 7 | - srams 8 | settings_directory: naomi/settings/definitions/ 9 | -------------------------------------------------------------------------------- /edit_settings: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /eeprominfo: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /homebrew/netbootmenu/Makefile: -------------------------------------------------------------------------------- 1 | # Please see the README for setting up a valid build environment. 2 | 3 | # The top-level binary that you wish to produce. 4 | all: netbootmenu.bin 5 | 6 | # All of the source files (.c and .s) that you want to compile. 7 | # You can use relative directories here as well. Note that if 8 | # one of these files is not found, make will complain about a 9 | # missing missing `build/naomi.bin' target, so make sure all of 10 | # these files exist. 11 | SRCS += main.c 12 | SRCS += config.c 13 | SRCS += controls.c 14 | SRCS += screens.c 15 | SRCS += dejavusans.ttf 16 | SRCS += scroll.raw 17 | SRCS += check.raw 18 | SRCS += change.raw 19 | SRCS += build/up.o 20 | SRCS += build/dn.o 21 | SRCS += build/cursor.o 22 | 23 | # Libraries we need to link against. 24 | LIBS += -lnaomimessage -lfreetype -lbz2 -lz -lpng16 25 | 26 | # Override the default serial so this program has its own settings. 27 | SERIAL = BRP0 28 | 29 | # Pick up base makefile rules common to all examples. 30 | include ${NAOMI_BASE}/tools/Makefile.base 31 | 32 | # Specific buildrule for PNG files for this project. Note that these 33 | # *MUST* be a multiple of 8 both in width and height. 34 | build/%.o: %.png ${IMG2C_FILE} 35 | @mkdir -p $(dir $@) 36 | ${IMG2C} build/$<.c --mode RGBA1555 $< 37 | ${CC} -c build/$<.c -o $@ 38 | 39 | # Provide the top-level ROM creation target for this binary. 40 | # See scripts/makerom.py for details about what is customizable. 41 | netbootmenu.bin: ${MAKEROM_FILE} ${NAOMI_BIN_FILE} 42 | ${MAKEROM} $@ \ 43 | --title "Net Boot ROM Picker Menu" \ 44 | --publisher "DragonMinded" \ 45 | --serial "${SERIAL}" \ 46 | --section ${NAOMI_BIN_FILE},${START_ADDR} \ 47 | --entrypoint ${MAIN_ADDR} \ 48 | --main-binary-includes-test-binary \ 49 | --test-entrypoint ${TEST_ADDR} 50 | 51 | # Include a simple clean target which wipes the build directory 52 | # and kills any binary built. 53 | .PHONY: clean 54 | clean: 55 | rm -rf build 56 | rm -rf netbootmenu.bin 57 | -------------------------------------------------------------------------------- /homebrew/netbootmenu/change.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/netbootmenu/change.raw -------------------------------------------------------------------------------- /homebrew/netbootmenu/check.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/netbootmenu/check.raw -------------------------------------------------------------------------------- /homebrew/netbootmenu/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_H 2 | #define __COMMON_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #ifndef min 9 | #define min(a,b) (((a) < (b)) ? (a) : (b)) 10 | #endif 11 | 12 | #ifndef max 13 | #define max(a,b) (((a) > (b)) ? (a) : (b)) 14 | #endif 15 | 16 | #define MESSAGE_SELECTION 0x1000 17 | #define MESSAGE_LOAD_SETTINGS 0x1001 18 | #define MESSAGE_LOAD_SETTINGS_ACK 0x1002 19 | #define MESSAGE_LOAD_SETTINGS_DATA 0x1005 20 | #define MESSAGE_LOAD_PROGRESS 0x1009 21 | #define MESSAGE_UNPACK_PROGRESS 0x100A 22 | #define MESSAGE_SAVE_SETTINGS_DATA 0x1007 23 | #define MESSAGE_SAVE_SETTINGS_ACK 0x1008 24 | #define MESSAGE_SAVE_CONFIG 0x1003 25 | #define MESSAGE_SAVE_CONFIG_ACK 0x1004 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /homebrew/netbootmenu/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "config.h" 6 | 7 | #define CONFIG_MEMORY_LOCATION 0x0D000000 8 | #define GAMES_POINTER_LOC 0 9 | #define GAMES_COUNT_LOC 4 10 | #define ENABLE_ANALOG_LOC 8 11 | #define ENABLE_DEBUG_LOC 12 12 | #define DEFAULT_SELECTION_LOC 16 13 | #define SYSTEM_REGION_LOC 20 14 | #define USE_FILENAMES_LOC 24 15 | 16 | config_t *get_config() 17 | { 18 | return (config_t *)CONFIG_MEMORY_LOCATION; 19 | } 20 | 21 | games_list_t *get_games_list(unsigned int *count) 22 | { 23 | // Index into config memory to grab the count of games, as well as the offset pointer 24 | // to where the games blob is. 25 | config_t *config = get_config(); 26 | *count = config->games_count; 27 | return (games_list_t *)(CONFIG_MEMORY_LOCATION + config->game_list_offset); 28 | } 29 | 30 | uint8_t *get_fallback_font(unsigned int *size) 31 | { 32 | config_t *config = get_config(); 33 | *size = config->fallback_font_size; 34 | if (config->fallback_font_size && config->fallback_font_offset) 35 | { 36 | return (uint8_t *)(CONFIG_MEMORY_LOCATION + config->fallback_font_offset); 37 | } 38 | else 39 | { 40 | return 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /homebrew/netbootmenu/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H 2 | #define __CONFIG_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | typedef struct __attribute__((__packed__)) 14 | { 15 | char name[128]; 16 | uint8_t serial[4]; 17 | unsigned int id; 18 | } games_list_t; 19 | 20 | typedef struct __attribute__((__packed__)) 21 | { 22 | uint32_t game_list_offset; 23 | uint32_t games_count; 24 | uint32_t enable_analog; 25 | uint32_t enable_debug; 26 | uint32_t boot_selection; 27 | uint32_t system_region; 28 | uint32_t use_filenames; 29 | uint32_t disable_sound; 30 | uint8_t joy1_hcenter; 31 | uint8_t joy1_vcenter; 32 | uint8_t joy2_hcenter; 33 | uint8_t joy2_vcenter; 34 | uint8_t joy1_hmin; 35 | uint8_t joy1_hmax; 36 | uint8_t joy1_vmin; 37 | uint8_t joy1_vmax; 38 | uint8_t joy2_hmin; 39 | uint8_t joy2_hmax; 40 | uint8_t joy2_vmin; 41 | uint8_t joy2_vmax; 42 | uint32_t fallback_font_offset; 43 | uint32_t fallback_font_size; 44 | uint32_t force_players; 45 | } config_t; 46 | 47 | typedef struct 48 | { 49 | int scroll; 50 | int check; 51 | int change; 52 | } sounds_t; 53 | 54 | typedef struct 55 | { 56 | eeprom_t *settings; 57 | config_t *config; 58 | double fps; 59 | double animation_counter; 60 | double test_error_counter; 61 | font_t *font_18pt; 62 | font_t *font_12pt; 63 | texture_description_t *sprite_up; 64 | texture_description_t *sprite_down; 65 | texture_description_t *sprite_cursor; 66 | sounds_t sounds; 67 | } state_t; 68 | 69 | config_t *get_config(); 70 | games_list_t *get_games_list(unsigned int *count); 71 | uint8_t *get_fallback_font(unsigned int *size); 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /homebrew/netbootmenu/controls.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONTROLS_H 2 | #define __CONTROLS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | typedef struct 11 | { 12 | // The following controls only ever need a pressed event. 13 | uint8_t up_pressed; 14 | uint8_t down_pressed; 15 | uint8_t left_pressed; 16 | uint8_t right_pressed; 17 | uint8_t test_pressed; 18 | uint8_t service_pressed; 19 | 20 | // The following controls need pressed and released events to detect holds. 21 | uint8_t start_pressed; 22 | uint8_t start_released; 23 | 24 | // The following controlls need raw analog values for calibration. 25 | uint8_t joy1_h; 26 | uint8_t joy1_v; 27 | uint8_t joy2_h; 28 | uint8_t joy2_v; 29 | } controls_t; 30 | 31 | controls_t get_controls(state_t *state, int reinit); 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /homebrew/netbootmenu/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/netbootmenu/cursor.png -------------------------------------------------------------------------------- /homebrew/netbootmenu/dejavusans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/netbootmenu/dejavusans.ttf -------------------------------------------------------------------------------- /homebrew/netbootmenu/dn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/netbootmenu/dn.png -------------------------------------------------------------------------------- /homebrew/netbootmenu/netbootmenu.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/netbootmenu/netbootmenu.bin -------------------------------------------------------------------------------- /homebrew/netbootmenu/screens.h: -------------------------------------------------------------------------------- 1 | #ifndef __SCREENS_H 2 | #define __SCREENS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | void draw_screen(state_t *state); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /homebrew/netbootmenu/scroll.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/netbootmenu/scroll.raw -------------------------------------------------------------------------------- /homebrew/netbootmenu/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/netbootmenu/up.png -------------------------------------------------------------------------------- /homebrew/settingstrojan/Makefile: -------------------------------------------------------------------------------- 1 | # Please see the README for setting up a valid build environment. 2 | 3 | # The top-level binary that you wish to produce. 4 | all: settingstrojan.bin 5 | 6 | # Include the trojan itself and debugging libraries. 7 | SRCS += crt0.s 8 | SRCS += trojan.c 9 | SRCS += eeprom.c 10 | SRCS += video.c 11 | 12 | # Overwrite starting address with a sane new default where we stick our binary trojan. 13 | START_ADDR = 0xDFC0000 14 | 15 | # Don't use libnaomi, we need to be small and modify minimal hardware. 16 | BARE_METAL = 1 17 | 18 | # Pick up base makefile rules so we don't have to rewrite the makefile from scratch. 19 | include ${NAOMI_BASE}/tools/Makefile.base 20 | 21 | # We don't put a header on the raw binary since we will just shove it into an existing ROM. 22 | settingstrojan.bin: build/naomi.bin 23 | cp $< $@ 24 | 25 | # Include a simple clean target which wipes the build directory 26 | # and kills any binary built. 27 | .PHONY: clean 28 | clean: 29 | rm -rf build 30 | -------------------------------------------------------------------------------- /homebrew/settingstrojan/crt0.s: -------------------------------------------------------------------------------- 1 | .section .text.start 2 | .globl start 3 | 4 | start: 5 | # We must run the executable. 6 | mov.l main_addr,r0 7 | nop 8 | jmp @r0 9 | nop 10 | 11 | .align 4 12 | 13 | main_addr: 14 | # Location of main 15 | .long __enter 16 | -------------------------------------------------------------------------------- /homebrew/settingstrojan/eeprom.h: -------------------------------------------------------------------------------- 1 | #ifndef __EEPROM_H 2 | #define __EEPROM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | uint8_t *eeprom_serial(); 11 | 12 | // Size of an EEPROM. 13 | #define EEPROM_SIZE 128 14 | 15 | // Location of the two system data chunks inside the EEPROM. 16 | #define SYSTEM_CHUNK_1 0 17 | #define SYSTEM_CHUNK_2 18 18 | 19 | // Location of the system chunk itself inside the EEPROM. 20 | #define SYSTEM_SECTION 0 21 | #define SYSTEM_LENGTH 36 22 | 23 | // Location of the game chunk itself inside the EEPROM. 24 | #define GAME_SECTION 36 25 | #define GAME_LENGTH 92 26 | 27 | // Location of various important data bits within system chunks. 28 | #define SYSTEM_CRC_LOC 0 29 | #define SYSTEM_CRC_SIZE 2 30 | #define SYSTEM_SERIAL_LOC 3 31 | #define SYSTEM_SERIAL_SIZE 4 32 | #define SYSTEM_CRC_REGION_LOC 2 33 | #define SYSTEM_CRC_REGION_SIZE 16 34 | 35 | // Location of the two game data header chunks inside the EEPROM. 36 | #define GAME_CHUNK_1 36 37 | #define GAME_CHUNK_2 40 38 | #define GAME_PAYLOAD 44 39 | 40 | // Location of various important data bits within game chunks. 41 | #define GAME_CRC_LOC 0 42 | #define GAME_CRC_SIZE 2 43 | #define GAME_LEN_LOC_1 2 44 | #define GAME_LEN_LOC_2 3 45 | 46 | // API for working with raw eeprom chunks. 47 | uint16_t eeprom_crc(uint8_t *data, unsigned int len); 48 | int eeprom_system_valid(uint8_t *data); 49 | int eeprom_game_valid(uint8_t *data); 50 | 51 | void maple_init(); 52 | void maple_free(); 53 | int maple_request_eeprom_read(uint8_t *outbytes); 54 | int maple_request_eeprom_write(uint8_t *inbytes); 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /homebrew/settingstrojan/settingstrojan.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/settingstrojan/settingstrojan.bin -------------------------------------------------------------------------------- /homebrew/settingstrojan/video.h: -------------------------------------------------------------------------------- 1 | #ifndef __VIDEO_H 2 | #define __VIDEO_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | void video_init_simple(); 11 | void video_free(); 12 | void video_wait_for_vblank(); 13 | void video_display_on_vblank(); 14 | uint32_t rgb(unsigned int r, unsigned int g, unsigned int b); 15 | void video_fill_screen(uint32_t color); 16 | void video_draw_pixel(int x, int y, uint32_t color); 17 | void video_draw_debug_character(int x, int y, uint32_t color, char ch); 18 | void video_draw_debug_text(int x, int y, uint32_t color, const char * const msg, ...); 19 | 20 | #define SET_PIXEL_V_2(base, x, y, color) ((uint16_t *)(base))[(global_video_width - (y)) + ((x) * global_video_width)] = (color) & 0xFFFF 21 | #define SET_PIXEL_H_2(base, x, y, color) ((uint16_t *)(base))[(x) + ((y) * global_video_width)] = (color) & 0xFFFF 22 | #define SET_PIXEL_V_4(base, x, y, color) ((uint32_t *)(base))[(global_video_width - (y)) + ((x) * global_video_width)] = (color) 23 | #define SET_PIXEL_H_4(base, x, y, color) ((uint32_t *)(base))[(x) + ((y) * global_video_width)] = (color) 24 | 25 | #define RGB0555(r, g, b) ((((b) >> 3) & (0x1F << 0)) | (((g) << 2) & (0x1F << 5)) | (((r) << 7) & (0x1F << 10)) | 0x8000) 26 | #define RGB1555(r, g, b, a) ((((b) >> 3) & (0x1F << 0)) | (((g) << 2) & (0x1F << 5)) | (((r) << 7) & (0x1F << 10)) | (((a) << 8) & 0x8000)) 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /homebrew/sramdump/Makefile: -------------------------------------------------------------------------------- 1 | # Please see the README for setting up a valid build environment. 2 | 3 | # The top-level binary that you wish to produce. 4 | all: sramdump.bin 5 | 6 | # All of the source files (.c and .s) that you want to compile. 7 | # You can use relative directories here as well. Note that if 8 | # one of these files is not found, make will complain about a 9 | # missing missing `build/naomi.bin' target, so make sure all of 10 | # these files exist. 11 | SRCS += main.c 12 | 13 | # Pick up base makefile rules common to all examples. 14 | include ${NAOMI_BASE}/tools/Makefile.base 15 | 16 | # We are using the naomi message library, so link it. 17 | LIBS += -lnaomimessage 18 | 19 | # Only link against zlib if it has itself been compiled. 20 | # libnaomimessage will pick it up and use it if its available. 21 | ifeq (${FEATURE_ZLIB}, 1) 22 | # Libraries that we want to link against. 23 | LIBS += -lz 24 | endif 25 | 26 | # Provide the top-level ROM creation target for this binary. 27 | # See scripts/makerom.py for details about what is customizable. 28 | sramdump.bin: ${MAKEROM_FILE} ${NAOMI_BIN_FILE} 29 | ${MAKEROM} $@ \ 30 | --title "SRAM Dump Helper Executable" \ 31 | --publisher "DragonMinded" \ 32 | --serial "${SERIAL}" \ 33 | --section ${NAOMI_BIN_FILE},${START_ADDR} \ 34 | --entrypoint ${MAIN_ADDR} \ 35 | --main-binary-includes-test-binary \ 36 | --test-entrypoint ${TEST_ADDR} 37 | 38 | # Include a simple clean target which wipes the build directory 39 | # and kills any binary built. 40 | .PHONY: clean 41 | clean: 42 | rm -rf build 43 | rm -rf sramdump.bin 44 | -------------------------------------------------------------------------------- /homebrew/sramdump/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define MESSAGE_READY 0x2000 9 | #define MESSAGE_SRAM_READ_REQUEST 0x2001 10 | #define MESSAGE_SRAM_WRITE_REQUEST 0x2002 11 | #define MESSAGE_SRAM_READ 0x2003 12 | #define MESSAGE_SRAM_WRITE 0x2004 13 | #define MESSAGE_DONE 0x2005 14 | 15 | #define OPERATION_NONE 0 16 | #define OPERATION_READ 1 17 | #define OPERATION_WRITE 2 18 | 19 | void draw_centered_text(int y, color_t color, char * msg) 20 | { 21 | int width = strlen(msg) * 8; 22 | video_draw_debug_text((video_width() - width) / 2, y, color, msg); 23 | } 24 | 25 | void main() 26 | { 27 | // We just want a simple framebuffer display. 28 | video_init(VIDEO_COLOR_1555); 29 | video_set_background_color(rgb(48, 48, 48)); 30 | 31 | // Initialize message library 32 | message_init(); 33 | message_stdio_redirect_init(); 34 | 35 | draw_centered_text((video_height() / 2) - 4, rgb(255, 255, 255), "Waiting for host command..."); 36 | video_display_on_vblank(); 37 | 38 | // Inform the host we are alive and ready. 39 | message_send(MESSAGE_READY, NULL, 0); 40 | 41 | // Wait for a command to operate on. 42 | int operation = OPERATION_NONE; 43 | while (operation == OPERATION_NONE) 44 | { 45 | uint16_t type = 0; 46 | uint8_t *data = 0; 47 | unsigned int length = 0; 48 | if (message_recv(&type, (void *)&data, &length) == 0) 49 | { 50 | if (type == MESSAGE_SRAM_READ_REQUEST && length == 0) 51 | { 52 | operation = OPERATION_READ; 53 | } 54 | else if (type == MESSAGE_SRAM_WRITE_REQUEST && length == 0) 55 | { 56 | operation = OPERATION_WRITE; 57 | } 58 | else 59 | { 60 | printf("Unexpected packet %04X with length %d!\n", type, length); 61 | } 62 | 63 | // Wipe any data that came in. 64 | if (data != 0) 65 | { 66 | free(data); 67 | } 68 | } 69 | } 70 | 71 | // Run the operation. 72 | switch (operation) 73 | { 74 | case OPERATION_READ: 75 | { 76 | draw_centered_text((video_height() / 2) - 4, rgb(255, 255, 255), "Reading SRAM and sending it to host..."); 77 | video_display_on_vblank(); 78 | 79 | message_send(MESSAGE_SRAM_READ, (void *)SRAM_BASE, SRAM_SIZE); 80 | break; 81 | } 82 | case OPERATION_WRITE: 83 | { 84 | draw_centered_text((video_height() / 2) - 4, rgb(255, 255, 255), "Receiving SRAM from host and writing it..."); 85 | video_display_on_vblank(); 86 | 87 | int done = 0; 88 | while (!done) 89 | { 90 | uint16_t type = 0; 91 | uint8_t *data = 0; 92 | unsigned int length = 0; 93 | if (message_recv(&type, (void *)&data, &length) == 0) 94 | { 95 | if (type == MESSAGE_SRAM_WRITE && length == SRAM_SIZE) 96 | { 97 | memcpy((void *)SRAM_BASE, data, SRAM_SIZE); 98 | done = 1; 99 | } 100 | else 101 | { 102 | printf("Unexpected packet %04X with length %d!\n", type, length); 103 | } 104 | 105 | // Wipe any data that came in. 106 | if (data != 0) 107 | { 108 | free(data); 109 | } 110 | } 111 | } 112 | break; 113 | } 114 | } 115 | 116 | // Wait for host ack 117 | while (1) 118 | { 119 | uint16_t type = 0; 120 | uint8_t *data = 0; 121 | unsigned int length = 0; 122 | if (message_recv(&type, (void *)&data, &length) == 0) 123 | { 124 | if (type == MESSAGE_DONE && length == 0) 125 | { 126 | break; 127 | } 128 | else 129 | { 130 | printf("Unexpected packet %04X with length %d!\n", type, length); 131 | } 132 | 133 | // Wipe any data that came in. 134 | if (data != 0) 135 | { 136 | free(data); 137 | } 138 | } 139 | } 140 | 141 | 142 | // Now, just wait forever... 143 | draw_centered_text((video_height() / 2) - 4, rgb(0, 255, 0), "Done!"); 144 | video_display_on_vblank(); 145 | while ( 1 ) { ; } 146 | } 147 | 148 | void test() 149 | { 150 | video_init(VIDEO_COLOR_1555); 151 | 152 | while ( 1 ) 153 | { 154 | // No point in being here at all... 155 | video_fill_screen(rgb(48, 48, 48)); 156 | draw_centered_text((video_height() / 2) - 4, rgb(255, 255, 255), "Nothing to see here..."); 157 | video_display_on_vblank(); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /homebrew/sramdump/sramdump.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/homebrew/sramdump/sramdump.bin -------------------------------------------------------------------------------- /host_debug_server: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /make_freeplay_patch: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /make_no_attract_patch: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /naomi/__init__.py: -------------------------------------------------------------------------------- 1 | from naomi.eeprom import NaomiEEPRom, NaomiEEPRomException 2 | from naomi.generic_patch import force_freeplay, force_no_attract_sound 3 | from naomi.rom import NaomiRom, NaomiRomRegionEnum, NaomiRomVersionEnum, NaomiExecutable, NaomiRomSection, NaomiRomException 4 | from naomi.rom_patch import NaomiSettingsPatcher, NaomiSettingsPatcherException, get_default_trojan, add_or_update_trojan, add_or_update_section 5 | 6 | __all__ = [ 7 | "NaomiRom", 8 | "NaomiRomRegionEnum", 9 | "NaomiRomVersionEnum", 10 | "NaomiExecutable", 11 | "NaomiRomSection", 12 | "NaomiRomException", 13 | "NaomiEEPRom", 14 | "NaomiEEPRomException", 15 | "NaomiSettingsPatcher", 16 | "NaomiSettingsPatcherException", 17 | "force_freeplay", 18 | "force_no_attract_sound", 19 | "get_default_trojan", 20 | "add_or_update_trojan", 21 | "add_or_update_section", 22 | ] 23 | -------------------------------------------------------------------------------- /naomi/generic_patch.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | from typing import Union, overload 3 | 4 | from arcadeutils import FileBytes 5 | from naomi.rom import NaomiRom 6 | 7 | 8 | @overload 9 | def _patch_rom(data: bytes, search: bytes, replace: bytes) -> bytes: 10 | ... 11 | 12 | 13 | @overload 14 | def _patch_rom(data: FileBytes, search: bytes, replace: bytes) -> FileBytes: 15 | ... 16 | 17 | 18 | def _patch_rom(data: Union[bytes, FileBytes], search: bytes, replace: bytes) -> Union[bytes, FileBytes]: 19 | rom = NaomiRom(data) 20 | if not rom.valid: 21 | raise Exception("ROM file does not appear to be a Naomi netboot ROM!") 22 | 23 | # Now, find the location of our magic string in the main executable. 24 | exec_data = rom.main_executable 25 | patch_location = None 26 | search_len = len(search) 27 | 28 | # Look through all copied sections. 29 | for section in exec_data.sections: 30 | if isinstance(data, bytes): 31 | for rloc in range(section.length - (search_len - 1)): 32 | loc = rloc + section.offset 33 | if data[loc:(loc + search_len)] == search: 34 | patch_location = loc 35 | break 36 | if patch_location is not None: 37 | break 38 | elif isinstance(data, FileBytes): 39 | patch_location = data.search(search, start=section.offset, end=section.offset + section.length) 40 | if patch_location is not None: 41 | break 42 | 43 | if patch_location is None: 44 | raise Exception("ROM file does not have a suitable spot for the patch!") 45 | 46 | # Now, generate a patch with this updated data overlaid on the original rom 47 | if isinstance(data, bytes): 48 | return data[:patch_location] + replace + data[(patch_location + len(replace)):] 49 | elif isinstance(data, FileBytes): 50 | data = data.clone() 51 | data[patch_location:(patch_location + len(replace))] = replace 52 | return data 53 | else: 54 | raise Exception("Logic error!") 55 | 56 | 57 | @overload 58 | def force_freeplay(data: bytes) -> bytes: 59 | ... 60 | 61 | 62 | @overload 63 | def force_freeplay(data: FileBytes) -> FileBytes: 64 | ... 65 | 66 | 67 | def force_freeplay(data: Union[bytes, FileBytes]) -> Union[bytes, FileBytes]: 68 | try: 69 | return _patch_rom(data, bytes([0x42, 0x84, 0xEC, 0x31, 0x0C, 0x60, 0x04, 0x1E, 0x43, 0x84]), bytes([0x1A, 0xE0])) 70 | except Exception: 71 | pass 72 | 73 | try: 74 | return _patch_rom(data, bytes([0x42, 0x84, 0x5c, 0x31, 0x1c, 0x7e, 0x0c, 0x60, 0x04, 0x15]), bytes([0x1A, 0xE0])) 75 | except Exception: 76 | pass 77 | 78 | try: 79 | return _patch_rom(data, bytes([0x42, 0x84, 0x5c, 0x31, 0x00, 0xeb, 0x0c, 0x60, 0x04, 0x15]), bytes([0x1A, 0xE0])) 80 | except Exception: 81 | pass 82 | 83 | raise Exception("Could not find any pattern to patch!") 84 | 85 | 86 | @overload 87 | def force_no_attract_sound(data: bytes) -> FileBytes: 88 | ... 89 | 90 | 91 | @overload 92 | def force_no_attract_sound(data: FileBytes) -> FileBytes: 93 | ... 94 | 95 | 96 | def force_no_attract_sound(data: Union[bytes, FileBytes]) -> Union[bytes, FileBytes]: 97 | try: 98 | return _patch_rom(data, bytes([0x40, 0x63, 0x12, 0xe2, 0xec, 0x32, 0x3c, 0x63, 0x09, 0x43]), bytes([0x00, 0xe3])) 99 | except Exception: 100 | pass 101 | 102 | try: 103 | return _patch_rom(data, bytes([0x59, 0x23, 0x31, 0x1e, 0xe3, 0x63, 0x10, 0x73, 0x40, 0x62]), bytes([0x00, 0xe3])) 104 | except Exception: 105 | pass 106 | 107 | raise Exception("Could not find any pattern to patch!") 108 | -------------------------------------------------------------------------------- /naomi/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/naomi/py.typed -------------------------------------------------------------------------------- /naomi/settings/__init__.py: -------------------------------------------------------------------------------- 1 | from naomi.settings.settings import ( 2 | NaomiSettingsWrapper, 3 | NaomiSettingsManager, 4 | get_default_settings_directory, 5 | ) 6 | from settings.settings import ( 7 | SettingsParseException, 8 | SettingsSaveException, 9 | JSONParseException, 10 | ReadOnlyCondition, 11 | DefaultCondition, 12 | DefaultConditionGroup, 13 | Setting, 14 | Settings, 15 | SettingsConfig, 16 | ) 17 | 18 | try: 19 | from naomi.settings.editor import NaomiSettingsEditor 20 | except ModuleNotFoundError: 21 | # DragonCurses not installed, provide a dummy stub. 22 | class NaomiSettingsEditor: # type: ignore 23 | def __init__(self, settings: NaomiSettingsWrapper, enable_unicode: bool = True) -> None: 24 | self.settings = settings 25 | 26 | def run(self) -> bool: 27 | return False 28 | 29 | 30 | __all__ = [ 31 | "NaomiSettingsWrapper", 32 | "NaomiSettingsManager", 33 | "NaomiSettingsEditor", 34 | "get_default_settings_directory", 35 | "SettingsParseException", 36 | "SettingsSaveException", 37 | "JSONParseException", 38 | "ReadOnlyCondition", 39 | "DefaultCondition", 40 | "DefaultConditionGroup", 41 | "Setting", 42 | "Settings", 43 | "SettingsConfig", 44 | ] 45 | -------------------------------------------------------------------------------- /naomi/settings/definitions/BAC0.settings: -------------------------------------------------------------------------------- 1 | # I have no idea what these are but they seem to never change. 2 | Setting00: byte, read-only, default is 90 3 | Setting01: byte, read-only, default is 04 4 | Setting02: byte, read-only, default is 10 5 | Setting03: byte, read-only, default is a3 6 | 7 | # This whole chunk is just the ascii value "CRAZYTAXI AM3" 8 | Setting04: byte, read-only, default is 43 9 | Setting05: byte, read-only, default is 52 10 | Setting06: byte, read-only, default is 41 11 | Setting07: byte, read-only, default is 5a 12 | Setting08: byte, read-only, default is 59 13 | Setting09: byte, read-only, default is 54 14 | Setting0a: byte, read-only, default is 41 15 | Setting0b: byte, read-only, default is 58 16 | Setting0c: byte, read-only, default is 49 17 | Setting0d: byte, read-only, default is 20 18 | Setting0e: byte, read-only, default is 41 19 | Setting0f: byte, read-only, default is 4d 20 | Setting10: byte, read-only, default is 33 21 | 22 | # Game Assignments 23 | Game Difficulty: byte, default is 03, display after Time Difficulty 24 | 00 - 1 25 | 01 - 2 26 | 02 - 3 27 | 03 - 4 28 | 04 - 5 29 | 05 - 6 30 | 06 - 7 31 | 07 - 8 32 | Time Difficulty: byte, default is 03, display after Start Time 33 | 00 - 1 34 | 01 - 2 35 | 02 - 3 36 | 03 - 4 37 | 04 - 5 38 | 05 - 6 39 | 06 - 7 40 | 07 - 8 41 | Start Time: byte, default is 04 42 | 00 - 70 43 | 01 - 65 44 | 02 - 60 45 | 03 - 55 46 | 04 - 50 47 | 05 - 45 48 | 06 - 40 49 | 07 - 35 50 | 51 | # Volume Settings. 52 | Accel Setting Min: byte, default is 2c, values are 00 to fe in hex, display after Steer Setting Right Max 53 | Accel Setting Max: byte, default is c3, values are 00 to fe in hex, display after Accel Setting Min 54 | Brake Setting Min: byte, default is 37, values are 00 to fe in hex, display after Accel Setting Max 55 | Brake Setting Max: byte, default is ce, values are 00 to fe in hex, display after Brake Setting Min 56 | Steer Setting Center: byte, default is 81, values are 00 to fe in hex 57 | Steer Setting Left Max: byte, default is 28, values are 00 to fe in hex 58 | Steer Setting Right Max: byte, default is e1, values are 00 to fe in hex 59 | 60 | # Pretty sure this is an unused byte. 61 | Unused: byte, read-only, default is 00 62 | -------------------------------------------------------------------------------- /naomi/settings/definitions/BAU0.settings: -------------------------------------------------------------------------------- 1 | Setting00: byte, read-only, default is dd 2 | Setting01: byte, read-only, default is be 3 | Setting02: byte, read-only, default is 04 4 | Setting03: byte, read-only, default is 00 5 | Setting04: byte, read-only, default is 18 6 | Setting05: byte, read-only, default is 08 7 | Setting06: byte, read-only, default is 99 8 | Setting07: byte, read-only, default is 19 9 | 10 | # Game Assignments 11 | Start Time: byte, default is 02 12 | 00 - Very Easy 13 | 01 - Easy 14 | 02 - Normal 15 | 03 - Hard 16 | 04 - Very Hard 17 | Continue Time: byte, default is 02 18 | 00 - Very Easy 19 | 01 - Easy 20 | 02 - Normal 21 | 03 - Hard 22 | 04 - Very Hard 23 | Get Time: byte, default is 02 24 | 00 - Very Easy 25 | 01 - Easy 26 | 02 - Normal 27 | 03 - Hard 28 | 04 - Very Hard 29 | Motor Drive: byte, default is 02, read-only if Cabinet Type is 01 30 | 00 - Off 31 | 01 - Light 32 | 02 - Normal 33 | 03 - Heavy 34 | 35 | # Volume Settings 36 | Accel Setting Min: byte, default is 20, values are 00 to fe in hex, display after Steer Setting Right Max 37 | Accel Setting Max: byte, default is d0, values are 00 to fe in hex, display after Accel Setting Min 38 | Brake Setting Min: byte, default is 20, values are 00 to fe in hex, display after Accel Setting Max 39 | Brake Setting Max: byte, default is d0, values are 00 to fe in hex, display after Brake Setting Min 40 | Steer Setting Center: byte, default is 80, values are 00 to fe in hex 41 | Steer Setting Left Max: byte, default is 20, values are 00 to fe in hex 42 | Steer Setting Right Max: byte, default is e0, values are 00 to fe in hex 43 | 44 | # I have no idea what this is for, but its probably unused to pad the 45 | # steering section to a multiple of 4. 46 | Unused: byte, read-only, default is 00 47 | 48 | Cabinet Type: byte, default is 00, display before Motor Drive 49 | 00 - DX 50 | 01 - STD 51 | 52 | # I have no idea what these are for, but they're probably unused to pad 53 | # this little addendum section to 4 bytes. 54 | Setting15: byte, read-only, default is 00 55 | Setting16: byte, read-only, default is 00 56 | Setting17: byte, read-only, default is 00 57 | -------------------------------------------------------------------------------- /naomi/settings/definitions/BBG0.settings: -------------------------------------------------------------------------------- 1 | # MVC2 stamps the EEPROM with its own serial, for some reason. 2 | Serial Number: 4 bytes, read-only, default is 30 47 42 42 3 | 4 | # The region that we saved settings for. The game has different defaults per-region 5 | # and the defaults in this file are based on the Japan region. 6 | Region: byte, default is 0 7 | 0 - Japan 8 | 1 - USA 9 | 2 - Export 10 | 3 - Korea 11 | 4 - Australia 12 | 13 | # Whether your cabinet is mono or stereo. 14 | Sound Mode: byte, default is 1 15 | 0 - Monaural 16 | 1 - Stereo 17 | 18 | # Whether the demo sound is half-volume. 19 | Demo Sound Half: byte, default is 0 20 | 0 - Off 21 | 1 - On 22 | 23 | # Whether continues are allowed. This is a bit complicated because the game 24 | # will force continues off if event mode is on. So, this is set up to be 25 | # read-only (invisible and not user editable) if event mode is on. 26 | Continue: byte, read-only if Event is 1, default is 1 if Event is 0, default is 0 if Event is 1 27 | 0 - Off 28 | 1 - On 29 | 30 | # Voice type setting, only available when the region is Korea. 31 | Voice Type: byte, read-only unless Region is 3, default is 0 32 | 0 - Off 33 | 1 - On 34 | 35 | # Game difficulty, damage and timer settings. Self-explanatory. 36 | Difficulty: byte, default is 1 37 | 0 - 1 (Easy) 38 | 1 - 2 39 | 2 - 3 40 | 3 - 4 41 | 4 - 5 42 | 5 - 6 43 | 6 - 7 44 | 7 - 8 (Hard) 45 | Damage Level: byte, default is 1 46 | 0 - 1 (Low) 47 | 1 - 2 48 | 2 - 3 49 | 3 - 4 (High) 50 | Timer Speed: byte, default is 1 51 | 0 - 1 (Low) 52 | 1 - 2 53 | 2 - 3 54 | 3 - 4 (High) 55 | 56 | # Memory card kit installed, and whether we should write to it. 57 | # Only available when the region is Japan. 58 | VM-Kit Type: byte, default is 0, read-only unless Region is 0 59 | 0 - No Use 60 | 1 - SEGA 61 | 2 - Capcom 62 | VM Write: byte, default is 0, read-only unless Region is 0 63 | 0 - Off 64 | 1 - On 65 | 66 | # Whether the game is forced to normal, turbo or user selectable speed. 67 | Game Speed: byte, default is 0 68 | 0 - Free Select 69 | 1 - Normal 70 | 2 - Turbo 71 | 72 | # Whether the game is in event mode or not. Also controls continue mode. 73 | Event: byte, default is 0 74 | 0 - Off 75 | 1 - On 76 | 77 | # Whether you can join-in mid game or not. 78 | Join-In: byte, default is 0 79 | 0 - On 80 | 1 - Off 81 | 82 | # The secret character unlock setting. Controls what characters are available 83 | # without needing to enter a code. 84 | Character Unlock Level: byte, default is 0 85 | 0 - No Unlocks 86 | 1 - Level 1 87 | 2 - Level 2 88 | 3 - Level 3 89 | 4 - Full Unlock 90 | 91 | # These are probably where the game stores the number of coin drops 92 | # in order to show XP level. I didn't bother figuring this out. 93 | Unknown: byte, read-only, default is 0 94 | Unknown: byte, read-only, default is 0 95 | -------------------------------------------------------------------------------- /naomi/settings/definitions/BBN1.settings: -------------------------------------------------------------------------------- 1 | Setting00: byte, read-only, default is 13 2 | Setting01: byte, read-only, default is 12 3 | Setting02: byte, read-only, default is 00 4 | Setting03: byte, read-only, default is 00 5 | 6 | Game Difficulty: byte, default is 02 7 | 0 - Very Easy 8 | 1 - Medium Easy 9 | 2 - Normal 10 | 3 - Medium Hard 11 | 4 - Very Hard 12 | 13 | Setting05: byte, read-only, default is 01 14 | 15 | Life Setting: byte, default is 09 16 | 0 - Initial Life 1\, Max Life 3 17 | 1 - Initial Life 2\, Max Life 3 18 | 2 - Initial Life 3\, Max Life 3 19 | 20 | 3 - Initial Life 1\, Max Life 4 21 | 4 - Initial Life 2\, Max Life 4 22 | 5 - Initial Life 3\, Max Life 4 23 | 6 - Initial Life 4\, Max Life 4 24 | 25 | 7 - Initial Life 1\, Max Life 5 26 | 8 - Initial Life 2\, Max Life 5 27 | 9 - Initial Life 3\, Max Life 5 28 | a - Initial Life 4\, Max Life 5 29 | b - Initial Life 5\, Max Life 5 30 | 31 | # Blood color defaults to green and is only user-selectable in the USA 32 | # region. There is no way to determine the region of the settings like 33 | # there is in MVC2 for example, so this is always available. Unfortunately 34 | # the game is smart enough to change this back to green on JP/KO/Export 35 | # BIOS so you can't use this to set red blood on those regions. 36 | Blood Color: byte, default is 01 37 | 0 - Red (USA region only) 38 | 1 - Green 39 | 40 | Blood Effect: byte, default is 00 41 | 0 - alldisp 42 | 1 - cut1 43 | 2 - cut2 44 | 45 | Ranking: byte, default is 00 46 | 0 - On 47 | 1 - Off 48 | 49 | Setting0a: byte, read-only, default is 00 50 | Setting0b: byte, read-only, default is 00 51 | 52 | -------------------------------------------------------------------------------- /naomi/settings/definitions/BDF0.settings: -------------------------------------------------------------------------------- 1 | Game Difficulty: byte, default is 00, 0 - Normal, 1 - Hard 2 | Number of Monkeys: byte, default is 03, values are 2 to 5 3 | Ball Velocity Boost: byte, default is 00, 0 - Off, 1 - On 4 | Setting03: byte, read-only, default is 00 5 | Setting04: byte, read-only, default is 00 6 | Setting05: byte, read-only, default is 00 7 | Setting06: byte, read-only, default is 00 8 | Setting07: byte, read-only, default is 00 9 | 10 | LeftUnknown: byte, read-only, default is value of Joystick Calibration Left 11 | Joystick Calibration Left: byte, default is 60, values are 00 to ff in hex 12 | Setting10: byte, read-only, default is 00 13 | Setting11: byte, read-only, default is 00 14 | RightUnknown: byte, read-only, default is value of Joystick Calibration Right + 1 15 | Joystick Calibration Right: byte, default is a0, values are 00 to ff in hex 16 | Setting14: byte, read-only, default is 00 17 | Setting15: byte, read-only, default is 00 18 | 19 | HCenterUnknown: byte, read-only, default is value of Horizontal Center (Now H) + 1 20 | Horizontal Center (Now H): byte, default is 7f, values are 00 to ff in hex, display before Vertical Center (Now V) 21 | Setting18: byte, read-only, default is 00 22 | Setting19: byte, read-only, default is 00 23 | PushUnknown: byte, read-only, default is value of Joystick Calibration Push 24 | Joystick Calibration Push: byte, default is 60, values are 00 to ff in hex 25 | Setting22: byte, read-only, default is 00 26 | Setting23: byte, read-only, default is 00 27 | 28 | PullUnknown: byte, read-only, default is value of Joystick Calibration Pull + 1 29 | Joystick Calibration Pull: byte, default is a0, values are 00 to ff in hex 30 | Setting26: byte, read-only, default is 00 31 | Setting27: byte, read-only, default is 00 32 | VCenterUnknown: byte, read-only, default is value of Vertical Center (Now V) + 1 33 | Vertical Center (Now V): byte, default is 7f, values are 00 to ff in hex, display before Joystick Calibration Left 34 | Setting30: byte, read-only, default is 00 35 | Setting31: byte, read-only, default is 00 36 | -------------------------------------------------------------------------------- /naomi/settings/definitions/BDM0.settings: -------------------------------------------------------------------------------- 1 | # Fingerprint to identify this as a DOOM eeprom. 2 | # Just the ascii for "DOOM". 3 | Setting00: byte, read-only, default is 44 4 | Setting01: byte, read-only, default is 4f 5 | Setting02: byte, read-only, default is 4f 6 | Setting03: byte, read-only, default is 4d 7 | 8 | # EEPROM version, game is forwards compatible so its 9 | # fine to leave this as-is. 10 | Setting04: byte, read-only, default is 03 11 | 12 | # Whether top-of-screen messages are shown in-game. 13 | Show Messages: byte, default is 1 14 | 0 - Off 15 | 1 - On 16 | 17 | # Music volume in-game. 18 | Music Volume: byte, default is 8, display after SFX Volume 19 | 0 - 0 20 | 1 - 1 21 | 2 - 2 22 | 3 - 3 23 | 4 - 4 24 | 5 - 5 25 | 6 - 6 26 | 7 - 7 27 | 8 - 8 28 | 9 - 9 29 | a - 10 30 | b - 11 31 | c - 12 32 | d - 13 33 | e - 14 34 | f - 15 35 | 36 | # SFX volume in-game. 37 | SFX Volume: byte, default is 8 38 | 0 - 0 39 | 1 - 1 40 | 2 - 2 41 | 3 - 3 42 | 4 - 4 43 | 5 - 5 44 | 6 - 6 45 | 7 - 7 46 | 8 - 8 47 | 9 - 9 48 | a - 10 49 | b - 11 50 | c - 12 51 | d - 13 52 | e - 14 53 | f - 15 54 | 55 | # Show options menu on main screen. 56 | Show Options Menu In-Game: byte, default is 1 57 | 0 - Off 58 | 1 - On 59 | 60 | # Allow sprinting by holding "Use" button. 61 | Hold Use Button to Sprint: byte, default is 0, display after Double-Tap to Sprint 62 | 0 - Off 63 | 1 - On 64 | 65 | # Double-tap a direction to sprint. 66 | Double-Tap to Sprint: byte, default is 0 67 | 1 - Off 68 | 0 - On 69 | 70 | 2P Buttons Fast Weapon Switch: byte, default is 0 71 | 0 - Off 72 | 1 - On 73 | 74 | # Control remapping, we don't support setting these 75 | # options since we have no way of enforcing uniqueness. 76 | Setting0c: byte, read-only, default is 01 77 | Setting0d: byte, read-only, default is 02 78 | Setting0e: byte, read-only, default is 03 79 | Setting0f: byte, read-only, default is 04 80 | Setting10: byte, read-only, default is 05 81 | Setting11: byte, read-only, default is 06 82 | -------------------------------------------------------------------------------- /naomi/settings/definitions/serialmapping.txt: -------------------------------------------------------------------------------- 1 | BAC0 - Crazy Taxi 2 | BAU0 - Jambo Safari 3 | BBG0 - Marvel vs Capcom 2 4 | BDM0 - Doom for Naomi 5 | BBN1 - The Typing of the Dead 6 | BDF0 - Monkey Ball 7 | -------------------------------------------------------------------------------- /naomi/settings/definitions/system.settings: -------------------------------------------------------------------------------- 1 | # Whether attract sounds are enabled for the startup chime and in-game. 2 | Attract Sounds: half-byte 3 | 0 - Off 4 | 1 - On 5 | 6 | # Whether the monitor should be horizontal or vertical. 7 | Monitor Orientation: half-byte 8 | 0 - Horizontal 9 | 1 - Vertical 10 | 11 | # The game's serial number, must match the serial found in the ROM header. 12 | # This is not user-editable and will be filled in automatically based on the ROM. 13 | Serial Number: 4 bytes, read-only 14 | 15 | # I don't know what this is, but I've seen it be 09 and 18. It might be region 16 | # flags or even a set of flags specifying that the game has saved its settings? 17 | Unknown: byte, read-only 18 | 19 | # The cabinet configuration for number of controls. 20 | Number of Players: half-byte 21 | 0 - 1 Player 22 | 1 - 2 Players 23 | 2 - 3 Players 24 | 3 - 4 Players 25 | 26 | # Whether the coin chutes should be considered common or have separate functionality. 27 | Coin Chute: half-byte 28 | 0 - Common 29 | 1 - Individual 30 | 31 | # The various coin assignments. I didn't bother to transcribe every single one since 32 | # the only one anyone cares about is free play. 33 | Coin Assignments: byte 34 | 00 - #1 35 | 01 - #2 36 | 02 - #3 37 | 03 - #4 38 | 04 - #5 39 | 05 - #6 40 | 06 - #7 41 | 07 - #8 42 | 08 - #9 43 | 09 - #10 44 | 0A - #11 45 | 0B - #12 46 | 0C - #13 47 | 0D - #14 48 | 0E - #15 49 | 0F - #16 50 | 10 - #17 51 | 11 - #18 52 | 12 - #19 53 | 13 - #20 54 | 14 - #21 55 | 15 - #22 56 | 16 - #23 57 | 17 - #24 58 | 18 - #25 59 | 19 - #26 60 | 1A - #27 (Free Play) 61 | 1B - Manual Assignment 62 | 63 | # The number of coins you have to drop for the game to register a single credit inserted. 64 | # This only matters in manual assignment mode. 65 | Number of Coins for 1 Credit: byte, read-only unless Coin Assignments is 1B, values are 1 to 9 66 | 67 | # The number of coins that get counted when you put one coin in chute 1. 68 | # This only matters in manual assignment mode. 69 | Chute 1 Coin Multiplier: byte, read-only unless Coin Assignments is 1B, values are 1 to 9 70 | 71 | # The number of coins that get counted when you put one coin in chute 2. 72 | # This only matters in manual assignment mode. 73 | Chute 2 Coin Multiplier: byte, read-only unless Coin Assignments is 1B, values are 1 to 9 74 | 75 | # The number of bonus coins that are inserted after you hit X number of credits. 76 | Bonus Coin on X Credits: byte, read-only unless Coin Assignments is 1B, values are 0, 2, 3, 4, 5, 6, 7, 8, 9 77 | 78 | # Continue sequence texts. Presumably this is for games that support coins/bill acceptors/etc. 79 | Sequence 1: half-byte, values are 1 to 5 80 | Sequence 2: half-byte, values are 1 to 5 81 | Sequence 3: half-byte, values are 1 to 5 82 | Sequence 4: half-byte, values are 1 to 5 83 | Sequence 5: half-byte, values are 1 to 5 84 | Sequence 6: half-byte, values are 1 to 5 85 | Sequence 7: half-byte, values are 1 to 5 86 | Sequence 8: half-byte, values are 1 to 5 87 | -------------------------------------------------------------------------------- /naomi/settings/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/naomi/settings/py.typed -------------------------------------------------------------------------------- /naomi_sram: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /netboot/__init__.py: -------------------------------------------------------------------------------- 1 | from netboot.hostutils import Host, HostException, HostStatusEnum, SettingsEnum 2 | from netboot.cabinet import Cabinet, CabinetManager, CabinetStateEnum, CabinetRegionEnum, CabinetPowerStateEnum 3 | from netboot.directory import DirectoryManager 4 | from netboot.patch import PatchManager 5 | from netboot.sram import SRAMManager 6 | from netboot.settings import SettingsManager 7 | 8 | __all__ = [ 9 | "SettingsEnum", 10 | "Host", 11 | "HostException", 12 | "HostStatusEnum", 13 | "Cabinet", 14 | "CabinetManager", 15 | "CabinetStateEnum", 16 | "CabinetRegionEnum", 17 | "CabinetPowerStateEnum", 18 | "DirectoryManager", 19 | "PatchManager", 20 | "SRAMManager", 21 | "SettingsManager", 22 | ] 23 | -------------------------------------------------------------------------------- /netboot/directory.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | import threading 4 | import zlib 5 | 6 | from typing import Dict, List, Mapping, Sequence 7 | from netboot.cabinet import CabinetRegionEnum 8 | from naomi import NaomiRom, NaomiRomRegionEnum 9 | 10 | 11 | class DirectoryException(Exception): 12 | pass 13 | 14 | 15 | class DirectoryManager: 16 | def __init__(self, directories: Sequence[str], checksums: Mapping[str, str]) -> None: 17 | self.__checksums: Dict[str, str] = dict(checksums) 18 | self.__directories = list(directories) 19 | self.__names: Dict[str, str] = {} 20 | self.__lock: threading.Lock = threading.Lock() 21 | 22 | @property 23 | def directories(self) -> List[str]: 24 | with self.__lock: 25 | return [d for d in self.__directories] 26 | 27 | @property 28 | def checksums(self) -> Dict[str, str]: 29 | with self.__lock: 30 | return self.__checksums 31 | 32 | def games(self, directory: str) -> List[str]: 33 | with self.__lock: 34 | if directory not in self.__directories: 35 | raise DirectoryException(f"Directory {directory} is not managed by us!") 36 | return sorted([f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]) 37 | 38 | def game_name(self, filename: str, region: CabinetRegionEnum) -> str: 39 | with self.__lock: 40 | local_key = f"{region.value}-{filename}" 41 | if local_key in self.__names: 42 | return self.__names[local_key] 43 | 44 | # Grab enough of the header for a match 45 | with open(filename, "rb") as fp: 46 | data = fp.read(0x1000) 47 | length = os.fstat(fp.fileno()).st_size 48 | 49 | # Now, check and see if we have a checksum match 50 | crc = zlib.crc32(data, 0) 51 | checksum = f"{region.value}-{crc}-{length}" 52 | if checksum in self.__checksums: 53 | self.__names[local_key] = self.__checksums[checksum] 54 | return self.__names[local_key] 55 | 56 | # Now, see if we can figure out from the header 57 | rom = NaomiRom(data) 58 | if rom.valid: 59 | # Arbitrarily choose Japan as default region 60 | naomi_region = { 61 | CabinetRegionEnum.REGION_JAPAN: NaomiRomRegionEnum.REGION_JAPAN, 62 | CabinetRegionEnum.REGION_USA: NaomiRomRegionEnum.REGION_USA, 63 | CabinetRegionEnum.REGION_EXPORT: NaomiRomRegionEnum.REGION_EXPORT, 64 | CabinetRegionEnum.REGION_KOREA: NaomiRomRegionEnum.REGION_KOREA, 65 | CabinetRegionEnum.REGION_AUSTRALIA: NaomiRomRegionEnum.REGION_AUSTRALIA, 66 | }.get(region, NaomiRomRegionEnum.REGION_JAPAN) 67 | self.__names[local_key] = rom.names[naomi_region] 68 | self.__checksums[checksum] = self.__names[local_key] 69 | return self.__names[local_key] 70 | 71 | # Finally, fall back to filename, getting rid of extensions and underscores 72 | self.__names[local_key] = os.path.splitext(os.path.basename(filename))[0].replace('_', ' ') 73 | self.__checksums[checksum] = self.__names[local_key] 74 | return self.__names[local_key] 75 | 76 | def rename_game(self, filename: str, region: CabinetRegionEnum, name: str) -> None: 77 | with self.__lock: 78 | # Make the local key 79 | local_key = f"{region.value}-{filename}" 80 | 81 | # Grab enough of the header for a match 82 | with open(filename, "rb") as fp: 83 | data = fp.read(0x1000) 84 | length = os.fstat(fp.fileno()).st_size 85 | 86 | # Now, check and see if we have a checksum match 87 | crc = zlib.crc32(data, 0) 88 | checksum = f"{region.value}-{crc}-{length}" 89 | 90 | # Update the value 91 | self.__names[local_key] = name 92 | self.__checksums[checksum] = self.__names[local_key] 93 | -------------------------------------------------------------------------------- /netboot/log.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import threading 4 | 5 | 6 | lock: threading.Lock = threading.Lock() 7 | 8 | 9 | def log(msg: str, *, newline: bool = True) -> None: 10 | with lock: 11 | print(msg, file=sys.stderr, end=os.linesep if newline else "") 12 | -------------------------------------------------------------------------------- /netboot/patch.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | import threading 4 | 5 | from typing import Dict, List, Optional, Sequence 6 | from arcadeutils import FileBytes, BinaryDiff 7 | 8 | 9 | class PatchException(Exception): 10 | pass 11 | 12 | 13 | class PatchManager: 14 | def __init__(self, directories: Sequence[str]) -> None: 15 | self.__directories = list(directories) 16 | self.__lock: threading.Lock = threading.Lock() 17 | self.__cache: Dict[str, List[str]] = {} 18 | 19 | @property 20 | def directories(self) -> List[str]: 21 | with self.__lock: 22 | return [d for d in self.__directories] 23 | 24 | def patches(self, directory: str) -> List[str]: 25 | with self.__lock: 26 | if directory not in self.__directories: 27 | raise PatchException(f"Directory {directory} is not managed by us!") 28 | return sorted([f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]) 29 | 30 | def recalculate(self, filename: Optional[str] = None) -> None: 31 | with self.__lock: 32 | if filename is None: 33 | self.__cache = {} 34 | else: 35 | if filename in self.__cache: 36 | del self.__cache[filename] 37 | 38 | def patch_name(self, filename: str) -> str: 39 | with self.__lock: 40 | with open(filename, "r") as pp: 41 | patchlines = pp.readlines() 42 | 43 | return BinaryDiff.description(patchlines) or os.path.splitext(os.path.basename(filename))[0].replace('_', ' ') 44 | 45 | def patches_for_game(self, filename: str) -> List[str]: 46 | with self.__lock: 47 | # First, see if we already cached this file. 48 | if filename in self.__cache: 49 | return self.__cache[filename] 50 | 51 | with open(filename, "rb") as fp: 52 | # First, grab the file size, see if there are any patches at all for this file. 53 | data = FileBytes(fp) 54 | 55 | # Grab currently known patches 56 | patches: List[str] = [] 57 | for directory in self.__directories: 58 | patches.extend(os.path.join(directory, f) for f in os.listdir(directory)) 59 | 60 | # Figure out which of these is valid for this filename 61 | valid_patches: List[str] = [] 62 | for patch in patches: 63 | try: 64 | with open(patch, "r") as pp: 65 | patchlines = pp.readlines() 66 | except Exception: 67 | continue 68 | size = BinaryDiff.size(patchlines) 69 | if size is None or size == len(data): 70 | if BinaryDiff.can_patch(data, patchlines, ignore_size_differences=True)[0]: 71 | valid_patches.append(patch) 72 | 73 | self.__cache[filename] = valid_patches 74 | return valid_patches 75 | -------------------------------------------------------------------------------- /netboot/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | import threading 4 | 5 | from typing import Any, Dict, List, Optional, Tuple 6 | from arcadeutils import FileBytes, BinaryDiff, BinaryDiffException 7 | from naomi import NaomiRom, NaomiRomRegionEnum, NaomiSettingsPatcher, get_default_trojan 8 | from naomi.settings import NaomiSettingsWrapper, NaomiSettingsManager 9 | 10 | 11 | class SettingsException(Exception): 12 | pass 13 | 14 | 15 | class SettingsManager: 16 | def __init__(self, naomi_directory: str) -> None: 17 | self.__naomi_directory = naomi_directory 18 | self.__naomi_manager = NaomiSettingsManager(naomi_directory) 19 | self.__lock: threading.Lock = threading.Lock() 20 | self.__cache: Dict[str, List[str]] = {} 21 | 22 | @property 23 | def naomi_directory(self) -> str: 24 | return self.__naomi_directory 25 | 26 | def get_naomi_settings( 27 | self, 28 | filename: str, 29 | settingsdata: Optional[bytes], 30 | region: NaomiRomRegionEnum = NaomiRomRegionEnum.REGION_JAPAN, 31 | patches: Optional[List[str]] = None, 32 | ) -> Tuple[Optional[NaomiSettingsWrapper], bool]: 33 | settings: Optional[NaomiSettingsWrapper] = None 34 | patches = patches or [] 35 | with open(filename, "rb") as fp: 36 | data = FileBytes(fp) 37 | 38 | # Check to make sure its not already got an SRAM section. If it 39 | # does, disallow the following section from being created. 40 | patcher = NaomiSettingsPatcher(data, get_default_trojan()) 41 | if patcher.rom.valid: 42 | # First, try to load any previously configured EEPROM. 43 | if settingsdata is not None: 44 | if len(settingsdata) != NaomiSettingsPatcher.EEPROM_SIZE: 45 | raise Exception("We don't support non-EEPROM settings!") 46 | 47 | settings = self.__naomi_manager.from_eeprom(settingsdata) 48 | else: 49 | # Second, if we didn't configure one, see if there's a previously configured 50 | # one in the ROM itself. 51 | settingsdata = patcher.get_eeprom() 52 | if settingsdata is not None: 53 | if len(settingsdata) != NaomiSettingsPatcher.EEPROM_SIZE: 54 | raise Exception("We don't support non-EEPROM settings!") 55 | 56 | settings = self.__naomi_manager.from_eeprom(settingsdata) 57 | else: 58 | # Finally, attempt to patch with any patches that fit in the first 59 | # chunk, so the defaults we get below match any force settings 60 | # patches we did to the header. 61 | for patch in patches: 62 | with open(patch, "r") as pp: 63 | differences = pp.readlines() 64 | differences = [d.strip() for d in differences if d.strip()] 65 | try: 66 | data = BinaryDiff.patch(data, differences, ignore_size_differences=True) 67 | except BinaryDiffException: 68 | # Patch was for something not in the header. 69 | pass 70 | rom = NaomiRom(data) 71 | if rom.valid: 72 | settings = self.__naomi_manager.from_rom(rom, region) 73 | 74 | return settings, settingsdata is not None 75 | 76 | def put_naomi_settings(self, settings: Dict[str, Any]) -> bytes: 77 | parsedsettings = self.__naomi_manager.from_json(settings) 78 | return self.__naomi_manager.to_eeprom(parsedsettings) 79 | 80 | def __directories(self) -> List[str]: 81 | return [self.__naomi_directory] 82 | 83 | @property 84 | def directories(self) -> List[str]: 85 | with self.__lock: 86 | return self.__directories() 87 | 88 | def settings(self, directory: str) -> List[str]: 89 | with self.__lock: 90 | if directory not in self.__directories(): 91 | raise SettingsException(f"Directory {directory} is not managed by us!") 92 | return sorted([f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]) 93 | 94 | def recalculate(self, filename: Optional[str] = None) -> None: 95 | with self.__lock: 96 | if filename is None: 97 | self.__cache = {} 98 | else: 99 | if filename in self.__cache: 100 | del self.__cache[filename] 101 | 102 | def settings_for_game(self, filename: str) -> List[str]: 103 | with self.__lock: 104 | # First, see if we already cached this file. 105 | if filename in self.__cache: 106 | return self.__cache[filename] 107 | 108 | valid_settings: List[str] = [] 109 | 110 | # Now, try to treat it as a Naomi ROM 111 | with open(filename, "rb") as fp: 112 | data = FileBytes(fp) 113 | rom = NaomiRom(data) 114 | if rom.valid: 115 | valid_settings = sorted([os.path.join(self.__naomi_directory, f) for f, _ in self.__naomi_manager.files_for_rom(rom).items()]) 116 | 117 | self.__cache[filename] = valid_settings 118 | return valid_settings 119 | -------------------------------------------------------------------------------- /netboot/sram.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | import threading 4 | 5 | from typing import Dict, List, Optional, Sequence 6 | from arcadeutils import FileBytes 7 | from naomi import NaomiRom, NaomiSettingsPatcher 8 | 9 | 10 | class SRAMException(Exception): 11 | pass 12 | 13 | 14 | class SRAMManager: 15 | def __init__(self, directories: Sequence[str]) -> None: 16 | self.__directories = list(directories) 17 | self.__lock: threading.Lock = threading.Lock() 18 | self.__cache: Dict[str, List[str]] = {} 19 | 20 | @property 21 | def directories(self) -> List[str]: 22 | with self.__lock: 23 | return [d for d in self.__directories] 24 | 25 | def srams(self, directory: str) -> List[str]: 26 | with self.__lock: 27 | if directory not in self.__directories: 28 | raise SRAMException(f"Directory {directory} is not managed by us!") 29 | return sorted([f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]) 30 | 31 | def recalculate(self, filename: Optional[str] = None) -> None: 32 | with self.__lock: 33 | if filename is None: 34 | self.__cache = {} 35 | else: 36 | if filename in self.__cache: 37 | del self.__cache[filename] 38 | 39 | def sram_name(self, filename: str) -> str: 40 | with self.__lock: 41 | return os.path.splitext(os.path.basename(filename))[0].replace('_', ' ') 42 | 43 | def srams_for_game(self, filename: str) -> List[str]: 44 | with self.__lock: 45 | # First, see if we already cached this file. 46 | if filename in self.__cache: 47 | return self.__cache[filename] 48 | 49 | valid_srams: List[str] = [] 50 | with open(filename, "rb") as fp: 51 | # First, grab the file size, see if there are any srams at all for this file. 52 | data = FileBytes(fp) 53 | 54 | # If it's a Naomi ROM, so SRAMs must be 32kb in size. 55 | rom = NaomiRom(data) 56 | if rom.valid: 57 | # Grab currently known SRAMs 58 | srams: List[str] = [] 59 | for directory in self.__directories: 60 | srams.extend(os.path.join(directory, f) for f in os.listdir(directory)) 61 | 62 | # Figure out which of these is valid for this ROM type. 63 | for sram in srams: 64 | try: 65 | size = os.path.getsize(sram) 66 | except Exception: 67 | size = 0 68 | 69 | if size == NaomiSettingsPatcher.SRAM_SIZE: 70 | valid_srams.append(sram) 71 | 72 | self.__cache[filename] = valid_srams 73 | return valid_srams 74 | -------------------------------------------------------------------------------- /netboot/web/__init__.py: -------------------------------------------------------------------------------- 1 | from netboot.web.app import spawn_app 2 | 3 | __all__ = [ 4 | "spawn_app", 5 | ] 6 | -------------------------------------------------------------------------------- /netboot/web/static/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial; 3 | } 4 | 5 | div.cabinet, div.config, div.romlist, div.patchlist, div.sramlist, div.gamelist, div.romconfig, div.game, div.outlet { 6 | margin: 20px; 7 | border: 1px solid #bbbbbb; 8 | padding: 20px; 9 | } 10 | 11 | div.cabinet dl, div.romconfig dl, div.outlet dl { 12 | margin: 0px; 13 | display: grid; 14 | grid-template-columns: max-content auto; 15 | } 16 | 17 | div.cabinet dt, div.romconfig dt, div.outlet dt { 18 | grid-column-start: 1; 19 | font-weight: bold; 20 | padding-top: 5px; 21 | padding-bottom: 5px; 22 | } 23 | 24 | div.cabinet dd, div.romconfig dd, div.outlet dd { 25 | grid-column-start: 2; 26 | padding-top: 5px; 27 | padding-bottom: 5px; 28 | } 29 | 30 | div.cabinet div.configure { 31 | padding-top: 5px; 32 | } 33 | 34 | div.cabinet div.update { 35 | padding-top: 5px; 36 | } 37 | 38 | div.cabinet div.query { 39 | padding-top: 5px; 40 | } 41 | 42 | div.cabinet h3, div.romlist h3, div.patchlist h3, div.sramlist h3, div.gamelist h3, div.romconfig h3, div.game h3, div.outlet h3 { 43 | margin: 0px 0px 5px 0px; 44 | } 45 | 46 | div.patch { 47 | padding-bottom: 5px; 48 | } 49 | 50 | div.patch h4 { 51 | margin: 0px 0px 5px 0px; 52 | } 53 | 54 | div.settingscollection h4 { 55 | margin-top: 5px; 56 | } 57 | 58 | div.rom, div.file, div.patches { 59 | margin-left: 15px; 60 | } 61 | 62 | div.settings { 63 | margin-left: 15px; 64 | } 65 | 66 | div.settings table td:first-child { 67 | border-right: 40px solid transparent; 68 | background-clip: padding-box; 69 | } 70 | 71 | div.romconfig input { 72 | width: 100%; 73 | } 74 | 75 | div.patch select { 76 | margin-top: 3px; 77 | } 78 | 79 | div.information { 80 | padding-top: 10px; 81 | } 82 | 83 | div.information.top { 84 | padding-bottom: 10px; 85 | } 86 | 87 | a.button, button { 88 | display: inline-block; 89 | text-align: center; 90 | border: 1px solid #777777; 91 | border-radius: 5px; 92 | color: white; 93 | font-weight: normal; 94 | font-size: 14px; 95 | text-decoration: none; 96 | background: #4E9CAF; 97 | padding: 5px 15px; 98 | } 99 | 100 | a.smallbutton, button.small { 101 | display: inline-block; 102 | text-align: center; 103 | border: 1px solid #777777; 104 | border-radius: 5px; 105 | color: white; 106 | font-size: 12px; 107 | font-weight: normal; 108 | text-decoration: none; 109 | background: #4E9CAF; 110 | padding: 3px 10px; 111 | } 112 | 113 | a.button:disabled, a.smallbutton:disabled, button:disabled, button.small:disabled { 114 | background: #aaaaaa; 115 | } 116 | 117 | span.successindicator { 118 | margin-left: 5px; 119 | color: green; 120 | } 121 | 122 | span.savingindicator { 123 | margin-left: 5px; 124 | } 125 | 126 | span.errorindicator { 127 | margin-left: 5px; 128 | color: red; 129 | } 130 | 131 | span.right { 132 | margin-left: 5px; 133 | } 134 | 135 | .italics { 136 | font-style: italic; 137 | } 138 | -------------------------------------------------------------------------------- /netboot/web/static/loading-16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/netboot/web/static/loading-16.gif -------------------------------------------------------------------------------- /netboot/web/templates/addcabinet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Add Cabinet :: NetBoot Web UI 4 | 5 | 6 | 7 | 8 | 17 | 18 | 19 |
20 | 21 |
22 |
23 | Back To Home 24 |
25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /netboot/web/templates/gameconfig.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Game Config :: NetBoot Web UI 4 | 5 | 6 | 7 | 8 | 19 | 20 | 21 |
22 | 23 | 24 | 25 |
26 |
27 | Back To Home 28 |
29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /netboot/web/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Overview :: NetBoot Web UI 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 |
17 | 18 | 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /netboot/web/templates/romconfig.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ROM Configuration :: NetBoot Web UI 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 |
23 | 26 |
27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /netboot/web/templates/systemconfig.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | System Config :: NetBoot Web UI 4 | 5 | 6 | 7 | 8 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 |
27 |
28 | Back To Home 29 |
30 |
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /netdimm/__init__.py: -------------------------------------------------------------------------------- 1 | from netdimm.netdimm import ( 2 | NetDimmException, 3 | NetDimmVersionEnum, 4 | NetDimmTargetEnum, 5 | CRCStatusEnum, 6 | PeekPokeTypeEnum, 7 | NetDimmInfo, 8 | NetDimmPacket, 9 | NetDimm, 10 | ) 11 | from netdimm.message import ( 12 | Message, 13 | MessageException, 14 | receive_packet, 15 | send_packet, 16 | read_scratch1_register, 17 | read_scratch2_register, 18 | write_scratch1_register, 19 | write_scratch2_register, 20 | receive_message, 21 | send_message, 22 | MAX_PACKET_LENGTH, 23 | MAX_MESSAGE_LENGTH, 24 | MESSAGE_HOST_STDOUT, 25 | MESSAGE_HOST_STDERR, 26 | ) 27 | 28 | __all__ = [ 29 | "NetDimmException", 30 | "NetDimmVersionEnum", 31 | "NetDimmTargetEnum", 32 | "CRCStatusEnum", 33 | "PeekPokeTypeEnum", 34 | "NetDimmInfo", 35 | "NetDimmPacket", 36 | "NetDimm", 37 | "Message", 38 | "MessageException", 39 | "receive_packet", 40 | "send_packet", 41 | "read_scratch1_register", 42 | "read_scratch2_register", 43 | "write_scratch1_register", 44 | "write_scratch2_register", 45 | "receive_message", 46 | "send_message", 47 | "MAX_PACKET_LENGTH", 48 | "MAX_MESSAGE_LENGTH", 49 | "MESSAGE_HOST_STDOUT", 50 | "MESSAGE_HOST_STDERR", 51 | ] 52 | -------------------------------------------------------------------------------- /netdimm/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/netdimm/py.typed -------------------------------------------------------------------------------- /netdimm_ensure: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /netdimm_info: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /netdimm_menu: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /netdimm_peekpoke: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /netdimm_receive: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /netdimm_send: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /patch_default_settings: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /patches/18_Wheeler_Deluxe_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 176177152 3 | C56E8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/18_Wheeler_Deluxe_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 176177152 3 | C577C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/18_Wheeler_Standard_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 176177152 3 | C5CC8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/18_Wheeler_Standard_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 176177152 3 | C5D5C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Airline_Pilots_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 100663296 3 | 64FCC: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Airline_Pilots_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 100663296 3 | 65094: 59 23 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Akatsuki_Blitzkampf_Ausf_Achse_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 134217728 3 | 4490E: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Akatsuki_Blitzkampf_Ausf_Achse_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 134217728 3 | 44994: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Alien_Front_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 92274688 3 | 8EDD8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Alien_Front_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 92274688 3 | 8EE6C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Azumanga_Daioh_Puzzle_Bobble_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 83886080 3 | 4AB1E: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Azumanga_Daioh_Puzzle_Bobble_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 83886080 3 | 4ABA4: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Beach_Spikers_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 234881024 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 44 52 20 -> 42 44 52 20 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Beach_Spikers_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 234881024 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 44 52 20 -> 42 44 52 20 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/Border_Down_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 222298112 3 | 565C6: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Border_Down_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 222298112 3 | 5664E: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/CapcomVsSNK2_Mark_Of_the_Millennium_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 159383552 3 | 116E58: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/CapcomVsSNK2_Mark_Of_the_Millennium_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 159383552 3 | 116EEC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/CapcomVsSNK2_Millionaire_fighting_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 159383552 3 | 1161F8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/CapcomVsSNK2_Millionaire_fighting_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 159383552 3 | 11628C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/CapcomVsSNKMillenniumFight_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 125829120 3 | FF358: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Capcom_vs_SNK_2_Mark_Of_The_Millennium_2001_unlocked.binpatch: -------------------------------------------------------------------------------- 1 | # Description: All characters unlocked 2 | # File size: 159383552 3 | 14945C: 00 -> 03 4 | 149470: 00 -> 03 5 | 149484: 00 -> 03 6 | 149498: 00 -> 03 7 | 1494AC: 00 -> 03 8 | 1494C0: 00 -> 03 9 | 1494D4: 00 -> 03 10 | 1494E8: 00 -> 03 11 | 22D804: 00 -> 03 12 | 22D818: 00 -> 03 13 | 22D82C: 00 -> 03 14 | 22D840: 00 -> 03 15 | 22D854: 00 -> 03 16 | 22D868: 00 -> 03 17 | 22D87C: 00 -> 03 18 | 22D890: 00 -> 03 19 | -------------------------------------------------------------------------------- /patches/Capcom_vs_SNK_2_Millionaire_Fighting_2001_unlocked.binpatch: -------------------------------------------------------------------------------- 1 | # Description: All characters unlocked 2 | # File size: 159383552 3 | 1487FC: 00 -> 03 4 | 148810: 00 -> 03 5 | 148824: 00 -> 03 6 | 148838: 00 -> 03 7 | 14884C: 00 -> 03 8 | 148860: 00 -> 03 9 | 148874: 00 -> 03 10 | 148888: 00 -> 03 11 | 22D804: 00 -> 03 12 | 22D818: 00 -> 03 13 | 22D82C: 00 -> 03 14 | 22D840: 00 -> 03 15 | 22D854: 00 -> 03 16 | 22D868: 00 -> 03 17 | 22D87C: 00 -> 03 18 | 22D890: 00 -> 03 19 | -------------------------------------------------------------------------------- /patches/Capcom_vs_SNK_Millenium_Fight_2000_Pro_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 125829120 3 | 1E0: * -> 01 4 | 1E3: 01 -> 1B 5 | 1F0: * -> 01 6 | 1F3: 0C -> 1B 7 | 200: * -> 01 8 | 203: 01 -> 1B 9 | 210: * -> 01 10 | 213: 01 -> 1B 11 | 220: * -> 01 12 | 223: 01 -> 1B 13 | -------------------------------------------------------------------------------- /patches/Capcom_vs_SNK_Millenium_Fight_2000_regular_or_Pro_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 125829120 3 | 1E0: * -> 01 4 | 1E1: 00 -> 02 5 | 1F0: * -> 01 6 | 1F1: 00 -> 02 7 | 200: * -> 01 8 | 201: 00 -> 02 9 | 210: * -> 01 10 | 211: 00 -> 02 11 | 220: * -> 01 12 | 221: 00 -> 02 13 | -------------------------------------------------------------------------------- /patches/Capcom_vs_SNK_Millenium_Fight_2000_unlocked.binpatch: -------------------------------------------------------------------------------- 1 | # Description: All characters unlocked 2 | # File size: 125829120 3 | 134: 41 -> 42 4 | 136: 43 -> 54 5 | 12EB9D: 00 -> 07 6 | 12EBB1: 00 -> 07 7 | 12EBC5: 00 -> 07 8 | 12EBD9: 00 -> 07 9 | 12EBED: 00 -> 07 10 | 12EC01: 00 -> 07 11 | 12EC15: 00 -> 07 12 | 12EC29: 00 -> 07 13 | 22E0C5: 00 -> 07 14 | 22E0D9: 00 -> 07 15 | 22E0ED: 00 -> 07 16 | 22E101: 00 -> 07 17 | 22E115: 00 -> 07 18 | 22E129: 00 -> 07 19 | 22E13D: 00 -> 07 20 | 22E151: 00 -> 07 21 | -------------------------------------------------------------------------------- /patches/Chaos_Field_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | 7F22E: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Chaos_Field_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | 7F2B4: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Cleopatra_Fortune_Plus_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 109051904 3 | 6122A: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Cleopatra_Fortune_Plus_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 109051904 3 | 612B0: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Club_Kart_European_Session_Unlocked_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | 8FE38: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Club_Kart_European_Session_Unlocked_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | 8FECC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Confidential_Mission_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 192937984 3 | 80708: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Confidential_Mission_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 192937984 3 | 8079C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Cosmic_Smash_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 75497472 3 | 63108: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Cosmic_Smash_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 75497472 3 | 6319C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/CrazyTaxi_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 134217728 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 41 43 30 -> 42 41 43 30 5 | 4AB5C: 42 84 -> 1A E0 6 | -------------------------------------------------------------------------------- /patches/CrazyTaxi_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 134217728 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 41 43 30 -> 42 41 43 30 5 | 4AC24: 59 23 -> 00 E3 6 | -------------------------------------------------------------------------------- /patches/CrazyTaxi_noprotect.binpatch: -------------------------------------------------------------------------------- 1 | # Description: disable header modification protection 2 | # File size: 134217728 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 41 43 30 -> 42 41 43 30 5 | BA0C: 0B 43 63 61 -> 00 E0 09 00 6 | -------------------------------------------------------------------------------- /patches/Dead_or_Alive_2_Millennium_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | 1C8B14: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Dead_or_Alive_2_Millennium_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | 1C49BC: 59 23 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Dead_or_Alive_2_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | 1C48F4: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Dead_or_Alive_2_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | 1C8BDC: 59 23 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Death_Crimson_OX_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 92274688 3 | 64146: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Death_Crimson_OX_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 92274688 3 | 641CC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Doki_Doki_Idol_Star_Seeker_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 18513216 3 | 16F48: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Doki_Doki_Idol_Star_Seeker_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 18513216 3 | 16FDC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Dynamic_Golf_Virtua_Golf_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 100663296 3 | 73718: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Dynamic_Golf_Virtua_Golf_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 100663296 3 | 737AC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Dynamite_Deka_Ex__Asian_Dynamite_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251658240 3 | D49D6: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Dynamite_Deka_Ex__Asian_Dynamite_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251658240 3 | D4A5E: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Giant_Gram_2000_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 210239488 3 | 7BFE8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Giant_Gram_2000_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 210239488 3 | 7C07C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Giant_Gram_All_Japan_Pro_Wrestling_2_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 100663296 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 41 4A 20 -> 42 41 4A 20 5 | 1E0: * -> 01 6 | 1E3: 02 -> 1B 7 | 1F0: * -> 01 8 | 200: * -> 01 9 | 203: 02 -> 1B 10 | 210: * -> 01 11 | 213: 02 -> 1B 12 | 220: * -> 01 13 | 223: 02 -> 1B 14 | -------------------------------------------------------------------------------- /patches/Giant_Gram_All_Japan_Pro_Wrestling_2_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 100663296 3 | 38C0: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Giga_Wing_2_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 93901440 3 | 903A8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Giga_Wing_2_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 93901440 3 | 9043C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/GuiltyGearX_auto_init_eeprom.binpatch: -------------------------------------------------------------------------------- 1 | # Description: auto-initialize EEPROM/game settings 2 | # File size: 125829120 3 | 19E8: 28 22 D6 89 -> 09 00 09 00 4 | 1AE6: 28 22 DF 89 -> 09 00 09 00 5 | 95425: 72 -> 6C 6 | 95427: 73 -> 61 7 | 95429: 20 61 6E 79 20 6B 65 79 20 54 6F 20 53 74 61 72 74 -> 65 20 77 61 69 74 20 66 6F 72 20 45 45 50 52 4F 4D 8 | 9543B: 44 65 66 61 75 6C 74 20 53 65 -> 69 6E 69 74 69 61 6C 69 7A 61 9 | 95446: 74 69 -> 69 6F 10 | 95449: 67 0A -> 0A 00 11 | -------------------------------------------------------------------------------- /patches/GuiltyGearX_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 125829120 3 | 72A28: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/GuiltyGearX_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 125829120 3 | 72ABC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Guilty_Gear_XX_Accent_Core_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251658240 3 | B79E8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Guilty_Gear_XX_Accent_Core_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251658240 3 | B7A7C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Guilty_Gear_XX_Reload_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251658240 3 | 88498: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Guilty_Gear_XX_Reload_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251658240 3 | 8852C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Guilty_Gear_XX_Slash_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251658240 3 | 99458: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Guilty_Gear_XX_Slash_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251658240 3 | 994EC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Guilty_Gear_XX_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251658240 3 | 85C48: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Guilty_Gear_XX_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251658240 3 | 85CDC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Gun_Spike_Cannon_Spike_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 109233152 3 | 3BFA8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Gun_Spike_Cannon_Spike_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 109233152 3 | 3C03C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Gun_Survivor_2_Biohazard_Code_Veronica_freeplay_nocomm.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 268435456 3 | 160132: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Gun_Survivor_2_Biohazard_Code_Veronica_no_attract_nocomm.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 268435456 3 | 1601B8: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Heavy_Metal_Geomatrix_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 100663296 3 | 30A78: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Ikaruga_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 33554432 3 | 52626: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Ikaruga_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 33554432 3 | 526AE: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Illvelo_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 201326592 3 | 620C6: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Illvelo_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 201326592 3 | 6214E: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Initial_D2_Export_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 230760448 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 53 41 -> 42 46 53 41 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Initial_D2_Export_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 230760448 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 53 41 -> 42 46 53 41 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 200: * -> 01 9 | 201: 00 -> 02 10 | 210: * -> 01 11 | 220: * -> 01 12 | -------------------------------------------------------------------------------- /patches/Initial_D2_Japan_Rev_B_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 247523328 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 4B 41 -> 42 46 4B 41 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Initial_D2_Japan_Rev_B_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 247523328 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 4B 41 -> 42 46 4B 41 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 200: * -> 01 9 | 201: 00 -> 02 10 | 210: * -> 01 11 | 220: * -> 01 12 | -------------------------------------------------------------------------------- /patches/Initial_D2_Japan_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 247736320 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 4B 41 -> 42 46 4B 41 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Initial_D2_Japan_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 247736320 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 4B 41 -> 42 46 4B 41 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 200: * -> 01 9 | 201: 00 -> 02 10 | 210: * -> 01 11 | 220: * -> 01 12 | -------------------------------------------------------------------------------- /patches/Initial_D3_EXPORT_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 235878400 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 48 52 41 -> 42 48 52 41 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Initial_D3_EXPORT_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 235878400 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 48 52 41 -> 42 48 52 41 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 200: * -> 01 9 | 201: 00 -> 02 10 | 210: * -> 01 11 | 220: * -> 01 12 | -------------------------------------------------------------------------------- /patches/Initial_D_Export_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 201119744 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 46 41 -> 42 46 46 41 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Initial_D_Export_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 201119744 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 46 41 -> 42 46 46 41 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 200: * -> 01 9 | 201: 00 -> 02 10 | 210: * -> 01 11 | 220: * -> 01 12 | -------------------------------------------------------------------------------- /patches/Initial_D_Japan_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 204394496 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 45 4D 41 -> 42 45 4D 41 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Initial_D_Japan_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 204394496 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 45 4D 41 -> 42 45 4D 41 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 200: * -> 01 9 | 201: 00 -> 02 10 | 210: * -> 01 11 | 220: * -> 01 12 | -------------------------------------------------------------------------------- /patches/Jambo_Safari_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 75497472 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 41 55 30 -> 42 41 55 30 5 | C90A0: 42 84 -> 1A E0 6 | -------------------------------------------------------------------------------- /patches/Jambo_Safari_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 75497472 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 41 55 30 -> 42 41 55 30 5 | C9168: 59 23 -> 00 E3 6 | -------------------------------------------------------------------------------- /patches/Jambo_Safari_noprotect.binpatch: -------------------------------------------------------------------------------- 1 | # Description: disable header modification protection 2 | # File size: 75497472 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 41 55 30 -> 42 41 55 30 5 | 946D0: 0B 43 E2 61 -> 00 E0 09 00 6 | -------------------------------------------------------------------------------- /patches/Jingi_Storm_-_The_Arcade_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | 39FD6: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Jingi_Storm_-_The_Arcade_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | 3A05C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Karous_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 218103808 3 | 6135A: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Karous_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 218103808 3 | 613E0: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/King_of_Route_66_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 196558848 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 45 45 30 -> 42 45 45 30 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/King_of_Route_66_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 196558848 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 45 45 30 -> 42 45 45 30 5 | 1E0: * -> 01 6 | 1F0: * -> 01 7 | 200: * -> 01 8 | 210: * -> 01 9 | 220: * -> 01 10 | -------------------------------------------------------------------------------- /patches/Kurukuru_Chameleon_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 76707840 3 | 587C2: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Kurukuru_Chameleon_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 76707840 3 | 58848: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/La_Keyboard_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 50331648 3 | 44C76: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/La_Keyboard_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 50331648 3 | 44CFC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Lupin-TheTyping_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 164722688 3 | 1F5E2: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Lupin-TheTyping_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 164722688 3 | 1F66A: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Lupin_The_Third_-_The_Shooting_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 225949696 3 | 1CA02: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Lupin_The_Third_-_The_Shooting_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 225949696 3 | 1CA8A: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Mamoru_kun_wa_Norowarete_Shimatta_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 201326592 3 | 113092: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Mamoru_kun_wa_Norowarete_Shimatta_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 201326592 3 | 11311A: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/MarvelVsCapcom2_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 143242752 3 | 1D8138: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/MarvelVsCapcom2_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 143242752 3 | 1D81CC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/MarvelVsCapcom2_unlocked.binpatch: -------------------------------------------------------------------------------- 1 | # Description: All characters unlocked 2 | # File size: 143242752 3 | 2074C1: 00 -> 04 4 | 2074D5: 00 -> 04 5 | 2074E9: 00 -> 04 6 | 2074FD: 00 -> 04 7 | 207511: 00 -> 04 8 | 207525: 00 -> 04 9 | 207539: 00 -> 04 10 | 20754D: 00 -> 04 11 | 3EDC61: 00 -> 04 12 | 3EDC75: 00 -> 04 13 | 3EDC89: 00 -> 04 14 | 3EDC9D: 00 -> 04 15 | 3EDCB1: 00 -> 04 16 | 3EDCC5: 00 -> 04 17 | 3EDCD9: 00 -> 04 18 | 3EDCED: 00 -> 04 19 | -------------------------------------------------------------------------------- /patches/MeltyBloodActressAgain_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 402653184 3 | 10F8DE: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/MeltyBloodActressAgain_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 402653184 3 | 10F966: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Melty_Blood_Act_Cadenza_Ver_b_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 229888000 3 | C75DE: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Melty_Blood_Act_Cadenza_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 230866944 3 | 6475E: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Melty_Blood_Act_Cadenza_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 230866944 3 | 647E6: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Melty_Blood_Act_Cadenza_ver_a_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 219205632 3 | C831E: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Melty_Blood_Act_Cadenza_ver_a_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 219205632 3 | C83A4: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Melty_Blood_Act_Cadenza_ver_b_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 229888000 3 | C7664: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Mobile_Suit_Gundam_Federation_Vs_Zeon_DX_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 209715200 3 | 1409E8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Mobile_Suit_Gundam_Federation_Vs_Zeon_DX_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 209715200 3 | 140A7C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Mobile_Suit_Gundam_Federation_Vs_Zeon_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | E7E08: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Mobile_Suit_Gundam_Federation_Vs_Zeon_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | E7E9C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Moero_Justice_Gakuen_Project_Justice_unlocked_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 192937984 3 | 129338: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Moero_Justice_Gakuen_Project_Justice_unlocked_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 192937984 3 | 1293CC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Moeru_Casinyo_burning_casino_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 159383552 3 | 3EC8A: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/MonkeyBall_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 234881024 3 | 6BE30: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/MonkeyBall_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 234881024 3 | 6BEC4: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Musapeys_Choco_Marker_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 66187264 3 | 790D2: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Musapeys_Choco_Marker_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 66187264 3 | 79158: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Noukone_Puzzle_Takoron_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 150994944 3 | 1BCE8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Noukone_Puzzle_Takoron_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 150994944 3 | 1BD7C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/OutTrigger_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | 69C70: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/OutTrigger_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | 69D38: 59 23 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/PowerStone2_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 87305504 3 | 158F88: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/PowerStone2_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 87305504 3 | 15901C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Powerstone-alt_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 69013504 3 | # Force this patch to only apply to the correct serial 4 | 134: 41 42 43 30 -> 41 42 43 30 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 0C -> 1B 9 | 200: * -> 01 10 | 203: 0C -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 0C -> 1B 15 | -------------------------------------------------------------------------------- /patches/Powerstone-alt_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 69013504 3 | # Force this patch to only apply to the correct serial 4 | 134: 41 42 43 30 -> 41 42 43 30 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/Psyvariar2_v6_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 142606336 3 | 7D58F6E: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Psyvariar2_v6_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 142606336 3 | 7D58FF4: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/PuyoPuyoDa_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 176160768 3 | 44F86: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/PuyoPuyoDa_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 176160768 3 | 4500C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/PuyoPuyoFever_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 209715200 3 | 37EAE: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/PuyoPuyoFever_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 209715200 3 | 37F36: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/QuizKeitaiQMode_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 150994944 3 | 29A58: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/QuizKeitaiQMode_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 150994944 3 | 29AEC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/RadirgyNoa_v6_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 134217728 3 | A9B2E: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Radirgy_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 218103808 3 | 6136E: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Radirgy_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 218103808 3 | 613F4: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Rhythm_Tengoku_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 268435456 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 50 45 00 -> 42 50 45 00 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Rhythm_Tengoku_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 268435456 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 50 45 00 -> 42 50 45 00 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/Samba_De_Amigo_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 142606336 3 | 2301A: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Samba_De_Amigo_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 142606336 3 | 230A0: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/SegaTetris_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 58786226 3 | # Force this patch to only apply to the correct serial 4 | 134: 30 42 42 43 -> 30 42 42 43 5 | 1E0: * -> 01 6 | 1E3: 02 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 02 -> 1B 9 | 200: * -> 01 10 | 203: 02 -> 1B 11 | 210: * -> 01 12 | 213: 02 -> 1B 13 | 220: * -> 01 14 | 223: 02 -> 1B 15 | -------------------------------------------------------------------------------- /patches/SegaTetris_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 58786226 3 | # Force this patch to only apply to the correct serial 4 | 134: 30 42 42 43 -> 30 42 42 43 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/Sega_Marine_Fishing_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 109051904 3 | 72A98: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Sega_Marine_Fishing_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 109051904 3 | 72B2C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Sega_Strike_Fighter_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | B0FA8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Sega_Strike_Fighter_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | B103C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Senko_No_Ronde_Special_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251342848 3 | 46E76: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Senko_No_Ronde_Special_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251342848 3 | 46EFC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Senko_No_Ronde_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 266600448 3 | 4751A: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Senko_No_Ronde_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 266600448 3 | 475A2: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/ShikigamiNoShiroII_v6_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 117760000 3 | 8FE272: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/ShikigamiNoShiroII_v6_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 117760000 3 | 8FE2F8: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Shin_Nihon_Pro_Wrestling_Toukon_Retsuden_4_Arcade_Edition_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 268435456 3 | 6FEC2: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Shin_Nihon_Pro_Wrestling_Toukon_Retsuden_4_Arcade_Edition_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 268435456 3 | 6FF48: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Shooting_Love_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 234881024 3 | F2636: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Shooting_Love_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 234881024 3 | F26BE: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Shootout_Pool_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 41943040 3 | 31BAE: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Shootout_Pool_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 41943040 3 | 31C36: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Slashout_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 150994944 3 | 83018: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Slashout_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 150994944 3 | 830AC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Spawn_In_the_Demon's_Hand_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 98901216 3 | 2A138: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Spikers_Battle_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | 5F588: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Spikers_Battle_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | 5F61C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Sports_Jam_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | D3988: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Sports_Jam_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | D3A1C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/StreetFighterZero3Upper_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 180355072 3 | E8832: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/StreetFighterZero3Upper_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 180355072 3 | E88B8: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Super_Major_League_World_Series_Baseball_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | D56A8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Super_Major_League_World_Series_Baseball_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | D573C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Super_Shanghai_2005_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 86439936 3 | 58892: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Super_Shanghai_2005_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 86439936 3 | 58918: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Super_Shanghai_2005_rev0_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 89147392 3 | 58892: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Super_Shanghai_2005_rev0_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 89147392 3 | 58918: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Tetris_Kiwamemiti_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 83886080 3 | 7FAB2: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Tetris_Kiwamemiti_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 83886080 3 | 7FB38: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/TheTypingOfTheDead_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 42 4E 31 -> 42 42 4E 31 5 | 1E0: * -> 01 6 | 1E3: 02 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 02 -> 1B 9 | 200: * -> 01 10 | 203: 02 -> 1B 11 | 210: * -> 01 12 | 213: 02 -> 1B 13 | 220: * -> 01 14 | 223: 02 -> 1B 15 | -------------------------------------------------------------------------------- /patches/TheTypingOfTheDead_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 42 4E 31 -> 42 42 4E 31 5 | 1E0: * -> 01 6 | 1F0: * -> 01 7 | 200: * -> 01 8 | 210: * -> 01 9 | 220: * -> 01 10 | -------------------------------------------------------------------------------- /patches/The_Maze_of_the_Kings_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 180355072 3 | 1345C8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/The_Maze_of_the_Kings_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 180355072 3 | 13465C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Toy_Fighter_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 92274688 3 | 66D54: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Toy_Fighter_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 92274688 3 | 66E1C: 59 23 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/TriggerHeartExelica_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 125009920 3 | 13E522: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/TriggerHeartExelica_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 125009920 3 | 13E5AA: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Trizeal_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | BE782: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Trizeal_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | BE808: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/UnderDefeat_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | 43FD6: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/UnderDefeat_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | 4405C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Usagi_Yamashiro_Mahjong_Hen_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 203597824 3 | 3E69A: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Usagi_Yamashiro_Mahjong_Hen_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 203597824 3 | 3E720: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/VirtuaTennis2_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | BBCF8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/VirtuaTennis2_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | BBD8C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/VirtuaTennis_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 100663296 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 44 4C 30 -> 42 44 4C 30 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/VirtuaTennis_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 100663296 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 44 4C 30 -> 42 44 4C 30 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/Virtua_Athletics_Virtua_Athlete_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 117440512 3 | 4C02: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Virtua_Athletics_Virtua_Athlete_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 117440512 3 | 4C88: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Virtua_Fighter_4_Evolution_Rev_B_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251658240 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 42 00 -> 42 46 42 00 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Virtua_Fighter_4_Evolution_Rev_B_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251658240 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 46 42 00 -> 42 46 42 00 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/Virtua_Fighter_4_Final_Tuned_Rev_B_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251658240 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 48 58 00 -> 42 48 58 00 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Virtua_Fighter_4_Final_Tuned_Rev_B_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251658240 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 48 58 00 -> 42 48 58 00 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/Virtua_Fighter_4__freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 251658240 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 44 4D 00 -> 42 44 4D 00 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/Virtua_Fighter_4__no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 251658240 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 44 4D 00 -> 42 44 4D 00 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/Virtua_NBA_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | 97870: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Virtua_NBA_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | 97938: 59 23 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Virtua_Striker_2_Ver_2000_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 130023424 3 | EB10: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Virtua_Striker_2_Ver_2000_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 130023424 3 | EBD8: 59 23 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Virtua_Striker_3_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 184549376 3 | B6388: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Virtua_Striker_3_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 184549376 3 | B641C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Virtual_On_Oratorio_Tangram_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 117440512 3 | 13866: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Virtual_On_Oratorio_Tangram_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 117440512 3 | 138EC: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/WWF_Royal_Rumble_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 142606336 3 | C007A: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/WWF_Royal_Rumble_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 142606336 3 | C0100: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Wave_Runner_GP_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 109051904 3 | 499D2: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Wave_Runner_GP_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 109051904 3 | 49A58: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Wild_Riders_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 176160768 3 | 16D0C8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Wild_Riders_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 176160768 3 | 16D15C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Zero_Gunner_2_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 92274688 3 | 219E8: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Zero_Gunner_2_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 92274688 3 | 21A7C: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/Zombie_Revenge_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 167772160 3 | CCDB6: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/Zombie_Revenge_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 167772160 3 | CCE7E: 59 23 -> 00 E3 4 | -------------------------------------------------------------------------------- /patches/aw-baskets_AW16_monitor_orientation.patch: -------------------------------------------------------------------------------- 1 | # File size: 67108864 2 | # Description: allow vertical monitor orientation 3 | 42B: 01 -> 00 4 | -------------------------------------------------------------------------------- /patches/knights_of_valour_the_7_spirits-dubhe_unlocked.patch: -------------------------------------------------------------------------------- 1 | # Description: Dubhe Unlocked 2 | # File size: 134217728 3 | 15CCE: 01 -> 02 4 | 642D6: 00 -> 01 5 | -------------------------------------------------------------------------------- /patches/metal_slug_6_violent.binpatch: -------------------------------------------------------------------------------- 1 | # Description: Violent 2 | # File size: 251658240 3 | 62B52: 05 90 -> 00 E0 4 | -------------------------------------------------------------------------------- /patches/neogeobattlecoliseum-unlocked.patch: -------------------------------------------------------------------------------- 1 | # Description: Force Unlocked 2 | # File size: 251658240 3 | B9E6C: 3E 8B -> 09 00 4 | B9E84: 01 89 -> 09 00 5 | 11D6D8: 02 8B -> 09 00 6 | 16DF00: 90 67 8E 67 D0 66 CE 66 90 07 -> 75 F9 21 02 B6 83 A1 83 2D 2C 7 | -------------------------------------------------------------------------------- /patches/rhytngk_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 268435456 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 50 45 00 -> 42 50 45 00 5 | 1E0: * -> 01 6 | 1E3: 01 -> 1B 7 | 1F0: * -> 01 8 | 1F3: 01 -> 1B 9 | 200: * -> 01 10 | 203: 01 -> 1B 11 | 210: * -> 01 12 | 213: 01 -> 1B 13 | 220: * -> 01 14 | 223: 01 -> 1B 15 | -------------------------------------------------------------------------------- /patches/rhytngk_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 268435456 3 | # Force this patch to only apply to the correct serial 4 | 134: 42 50 45 00 -> 42 50 45 00 5 | 1E0: * -> 01 6 | 1E1: 00 -> 02 7 | 1F0: * -> 01 8 | 1F1: 00 -> 02 9 | 200: * -> 01 10 | 201: 00 -> 02 11 | 210: * -> 01 12 | 211: 00 -> 02 13 | 220: * -> 01 14 | 221: 00 -> 02 15 | -------------------------------------------------------------------------------- /patches/the_king_of_fighters_neo_wave-characters_unlocked.patch: -------------------------------------------------------------------------------- 1 | # Description: Characters Unlocked 2 | # File size: 134217728 3 | 1E646E: 08 20 37 89 E2 60 33 70 00 -> 09 00 E2 61 33 71 11 D2 10 4 | 1E6478: 1B 88 02 8B 2B D2 -> 20 63 1B 88 00 8B 5 | 1E6480: 30 22 E2 60 33 70 00 60 1C 88 02 8B 27 D2 2A E3 30 22 E2 60 33 70 00 60 1D -> 1C 88 00 8B 2A E3 1D 88 00 8B 2B E3 1E 88 00 8B 32 E3 30 88 00 8B 06 E3 20 6 | 1E649A: 02 -> 00 7 | 1E649C: 23 D2 2B E3 30 22 E2 60 33 70 00 60 1E 88 02 8B 1F D2 32 E3 30 22 E2 60 33 70 00 60 30 88 02 8B 1B D2 06 E3 30 22 E2 60 33 70 -> 21 E3 1F 88 00 8B 23 E3 12 88 01 8B 26 E3 30 21 2E 88 01 8B 25 E3 30 21 15 A0 30 22 84 8D 33 8C 84 8D 33 8C 00 00 00 00 00 00 8 | 1E64C7: 60 20 88 02 8B 17 D2 21 E3 30 22 E2 60 33 70 -> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 9 | 1E64D7: 60 1F 88 02 8B 13 D2 23 E3 30 22 -> 00 00 00 00 00 00 00 00 00 00 00 10 | 1E9550: 03 64 -> 01 E4 11 | -------------------------------------------------------------------------------- /patches/the_king_of_fighters_xi-all_fighters.patch: -------------------------------------------------------------------------------- 1 | # Description: All Fighters Unlocked 2 | # File size: 251658240 3 | 6665A: 00 -> FF 4 | -------------------------------------------------------------------------------- /patches/the_rumble_fish-characters_unlocked.patch: -------------------------------------------------------------------------------- 1 | # Description: Characters Unlocked 2 | # File size: 134217728 3 | 62630: 08 -> 09 4 | 6643E: 0E 8B -> 09 00 5 | -------------------------------------------------------------------------------- /patches/the_rumble_fish_2-unlocked.patch: -------------------------------------------------------------------------------- 1 | # Description: Force Unlocked 2 | # File size: 201326592 3 | 38AE6: 0A 89 -> 09 00 4 | 38B08: 0A 89 -> 09 00 5 | 6B48E: 08 20 26 89 -> 09 00 09 00 6 | 6B4A2: 10 -> 06 7 | 6B606: 79 BF -> 16 A0 8 | AA61C: 02 -> 09 9 | AA620: 06 -> 0A 10 | AA624: 07 -> 0B 11 | AA628: 08 -> 09 12 | AA62C: 00 -> 12 13 | -------------------------------------------------------------------------------- /patches/tokyo_bus_tour_freeplay.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force free-play 2 | # File size: 159383552 3 | 28CFA: 42 84 -> 1A E0 4 | -------------------------------------------------------------------------------- /patches/tokyo_bus_tour_no_attract.binpatch: -------------------------------------------------------------------------------- 1 | # Description: force silent attract mode 2 | # File size: 159383552 3 | 28D80: 40 63 -> 00 E3 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flake8 2 | flask 3 | mypy 4 | psutil 5 | pycryptodome 6 | pyyaml 7 | pillow 8 | PyYAML 9 | arcadeutils 10 | dragoncurses 11 | smartoutlet 12 | cachetools 13 | types-cachetools 14 | types-requests 15 | -------------------------------------------------------------------------------- /rominfo: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | if __name__ == "__main__": 3 | import os 4 | path = os.path.abspath(os.path.dirname(__file__)) 5 | name = os.path.basename(__file__) 6 | 7 | import sys 8 | sys.path.append(path) 9 | 10 | import runpy 11 | runpy.run_module(f"scripts.{name}", run_name="__main__") 12 | -------------------------------------------------------------------------------- /roms/README.txt: -------------------------------------------------------------------------------- 1 | Place netboot roms here if you do not want to configure your own list of directories. 2 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/scripts/__init__.py -------------------------------------------------------------------------------- /scripts/attach_sram.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import argparse 3 | import sys 4 | 5 | from arcadeutils import FileBytes 6 | from naomi import NaomiSettingsPatcher 7 | 8 | 9 | def main() -> int: 10 | # Create the argument parser 11 | parser = argparse.ArgumentParser( 12 | description=( 13 | "Utility for attaching an SRAM dump to an Atomiswave conversion Naomi ROM. " 14 | "Use this to set up preferred settings in an emulator, and then send those " 15 | "settings to your Naomi when you netboot." 16 | ), 17 | ) 18 | subparsers = parser.add_subparsers(help='Action to take', dest='action') 19 | 20 | attach_parser = subparsers.add_parser( 21 | 'attach', 22 | help='Attach a 32K SRAM file to a commercial Naomi ROM.', 23 | description='Attach a 32K SRAM file to a commercial Naomi ROM.', 24 | ) 25 | attach_parser.add_argument( 26 | 'bin', 27 | metavar='BIN', 28 | type=str, 29 | help='The binary file we should attach the SRAM settings to.', 30 | ) 31 | attach_parser.add_argument( 32 | 'sram', 33 | metavar='SRAM', 34 | type=str, 35 | help='The SRAM settings file we should attach to the binary.', 36 | ) 37 | attach_parser.add_argument( 38 | '--output-file', 39 | metavar='BIN', 40 | type=str, 41 | help='A different file to output to instead of updating the binary specified directly.', 42 | ) 43 | 44 | extract_parser = subparsers.add_parser( 45 | 'extract', 46 | help='Extract a 32K SRAM file from a commercial Naomi ROM.', 47 | description='Extract a 32K SRAM file from a commercial Naomi ROM.', 48 | ) 49 | extract_parser.add_argument( 50 | 'bin', 51 | metavar='BIN', 52 | type=str, 53 | help='The binary file we should extract the SRAM settings from.', 54 | ) 55 | extract_parser.add_argument( 56 | 'sram', 57 | metavar='SRAM', 58 | type=str, 59 | help='The SRAM settings file we should extract from the binary.', 60 | ) 61 | 62 | # Grab what we're doing 63 | args = parser.parse_args() 64 | 65 | if args.action == "attach": 66 | # Grab the rom, parse it 67 | with open(args.bin, "rb" if args.output_file else "rb+") as fp: 68 | data = FileBytes(fp) 69 | 70 | with open(args.sram, "rb") as fp: 71 | sram = fp.read() 72 | 73 | if len(sram) != NaomiSettingsPatcher.SRAM_SIZE: 74 | print(f"SRAM file is not the right size, should be {NaomiSettingsPatcher.SRAM_SIZE} bytes!", file=sys.stderr) 75 | return 1 76 | 77 | patcher = NaomiSettingsPatcher(data, None) 78 | patcher.put_sram(sram, verbose=True) 79 | 80 | if args.output_file: 81 | print(f"Added SRAM init to the end of {args.output_file}.", file=sys.stderr) 82 | with open(args.output_file, "wb") as fp: 83 | patcher.data.write_changes(fp) 84 | else: 85 | print(f"Added SRAM init to the end of {args.bin}.", file=sys.stderr) 86 | patcher.data.write_changes() 87 | 88 | elif args.action == "extract": 89 | # Grab the rom, parse it. 90 | with open(args.bin, "rb") as rfp: 91 | data = FileBytes(rfp) 92 | 93 | # Now, search for the settings. 94 | patcher = NaomiSettingsPatcher(data, None) 95 | settings = patcher.get_sram() 96 | 97 | if settings is None: 98 | print("ROM does not have any SRAM settings attached!", file=sys.stderr) 99 | return 1 100 | 101 | if len(settings) != NaomiSettingsPatcher.SRAM_SIZE: 102 | print("SRAM is the wrong size! Perhaps you meant to use \"attach_settings\"?", file=sys.stderr) 103 | return 1 104 | 105 | print(f"Wrote SRAM settings to {args.sram}.") 106 | with open(args.sram, "wb") as wfp: 107 | wfp.write(settings) 108 | 109 | return 0 110 | 111 | 112 | if __name__ == "__main__": 113 | sys.exit(main()) 114 | -------------------------------------------------------------------------------- /scripts/binary_patch.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import argparse 3 | import os 4 | import sys 5 | from typing import List 6 | 7 | from arcadeutils.binary import BinaryDiff 8 | 9 | 10 | def main() -> int: 11 | # Create the argument parser 12 | parser = argparse.ArgumentParser( 13 | description="Utilities for diffing or patching binary files.", 14 | ) 15 | subparsers = parser.add_subparsers(help='commands', dest='command') 16 | 17 | # Parser for diffing two binary files 18 | diff_parser = subparsers.add_parser('diff', help='Diff two same-length binary files.') 19 | diff_parser.add_argument( 20 | 'file1', 21 | metavar='FILE1', 22 | type=str, 23 | help='The base file that we will output diffs relative to.', 24 | ) 25 | diff_parser.add_argument( 26 | 'file2', 27 | metavar='FILE2', 28 | type=str, 29 | help='The file that we will compare against the base file to find diffs.', 30 | ) 31 | diff_parser.add_argument( 32 | '--patch-file', 33 | metavar='FILE', 34 | type=str, 35 | help='Write patches to a file instead of stdout.', 36 | ) 37 | 38 | # Parser for patching a binary file 39 | patch_parser = subparsers.add_parser('patch', help='Patch a binary file.') 40 | patch_parser.add_argument( 41 | 'bin', 42 | metavar='BIN', 43 | type=str, 44 | help='The binary file we should patch.', 45 | ) 46 | patch_parser.add_argument( 47 | 'out', 48 | metavar='OUT', 49 | type=str, 50 | help='The file we should write the patched binary to.', 51 | ) 52 | patch_parser.add_argument( 53 | '--patch-file', 54 | metavar='FILE', 55 | type=str, 56 | action='append', 57 | help=( 58 | 'Read patches from a file instead of stdin. Can be specified multiple times ' 59 | 'to apply multiple patches. Patches will be applied in specified order.' 60 | ), 61 | ) 62 | patch_parser.add_argument( 63 | '--reverse', 64 | action="store_true", 65 | help='Perform the patch in reverse.', 66 | ) 67 | 68 | # Grab what we're doing 69 | args = parser.parse_args() 70 | 71 | if args.command == 'diff': 72 | with open(args.file1, "rb") as fpb: 73 | file1 = fpb.read() 74 | with open(args.file2, "rb") as fpb: 75 | file2 = fpb.read() 76 | 77 | try: 78 | differences = BinaryDiff.diff(file1, file2) 79 | except Exception as e: 80 | print(f"Could not diff {args.file1} against {args.file2}: {str(e)}", file=sys.stderr) 81 | return 1 82 | 83 | if not args.patch_file: 84 | for line in differences: 85 | print(line) 86 | else: 87 | with open(args.patch_file, "w") as fps: 88 | fps.write(os.linesep.join(differences) + os.linesep) 89 | elif args.command == 'patch': 90 | with open(args.bin, "rb") as fpb: 91 | data = fpb.read() 92 | 93 | patch_list: List[List[str]] = [] 94 | if not args.patch_file: 95 | patch_list.append(sys.stdin.readlines()) 96 | else: 97 | for patch in args.patch_file: 98 | with open(patch, "r") as fps: 99 | patch_list.append(fps.readlines()) 100 | 101 | for differences in patch_list: 102 | differences = [d.strip() for d in differences if d.strip()] 103 | 104 | try: 105 | data = BinaryDiff.patch(data, differences, reverse=args.reverse) 106 | except Exception as e: 107 | print(f"Could not patch {args.bin}: {str(e)}", file=sys.stderr) 108 | return 1 109 | 110 | with open(args.out, "wb") as fpb: 111 | fpb.write(data) 112 | 113 | print(f"Patched {args.bin} and wrote to {args.out}.") 114 | else: 115 | print("Please specify a valid command!", file=sys.stderr) 116 | return 1 117 | 118 | return 0 119 | 120 | 121 | if __name__ == "__main__": 122 | sys.exit(main()) 123 | -------------------------------------------------------------------------------- /scripts/edit_settings.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import argparse 3 | import os 4 | import sys 5 | 6 | from naomi.settings import NaomiSettingsEditor, NaomiSettingsManager, SettingsParseException, SettingsSaveException 7 | 8 | 9 | # The root of the repo. 10 | root = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) 11 | 12 | 13 | def main() -> int: 14 | # Create the argument parser 15 | parser = argparse.ArgumentParser( 16 | description="Command-Line Utility for editing a Naomi settings file.", 17 | ) 18 | parser.add_argument( 19 | 'eeprom', 20 | metavar='EEPROM', 21 | type=str, 22 | help='The EEPROM settings file we should edit.', 23 | ) 24 | parser.add_argument( 25 | '--settings-directory', 26 | metavar='DIR', 27 | type=str, 28 | default=os.path.join(root, 'naomi', 'settings', 'definitions'), 29 | help='The directory containing settings definition files. Defaults to %(default)s.', 30 | ) 31 | parser.add_argument( 32 | '--serial', 33 | metavar='SERIAL', 34 | type=str, 35 | help='The serial number of the game, if the EEPROM file does not exist.', 36 | ) 37 | 38 | # Grab what we're doing 39 | args = parser.parse_args() 40 | 41 | # First, try to open the EEPRom file. If it does not exist, then we need to create a default. 42 | manager = NaomiSettingsManager(args.settings_directory) 43 | try: 44 | try: 45 | with open(args.eeprom, "rb") as fp: 46 | data = fp.read() 47 | settings = manager.from_eeprom(data) 48 | except OSError: 49 | settings = manager.from_serial(args.serial.encode('ascii')) 50 | except (SettingsParseException, SettingsSaveException) as e: 51 | print(f"Error in \"{e.filename}\":", str(e), file=sys.stderr) 52 | return 1 53 | 54 | # Now, invoke the CLI editor so settings can be edited. 55 | editor = NaomiSettingsEditor(settings) 56 | if editor.run(): 57 | # Now, write out the EEPROM. 58 | eeprom = manager.to_eeprom(settings) 59 | with open(args.eeprom, "wb") as fp: 60 | fp.write(eeprom) 61 | 62 | return 0 63 | 64 | 65 | if __name__ == "__main__": 66 | sys.exit(main()) 67 | -------------------------------------------------------------------------------- /scripts/eeprominfo.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import argparse 3 | import os 4 | import sys 5 | 6 | from naomi import NaomiEEPRom 7 | from naomi.settings import NaomiSettingsManager, ReadOnlyCondition, SettingsParseException, SettingsSaveException 8 | 9 | 10 | # The root of the repo. 11 | root = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) 12 | 13 | 14 | def main() -> int: 15 | # Create the argument parser 16 | parser = argparse.ArgumentParser( 17 | description="Utility for printing EEPROM hex digits for a game's settings section in an EEPRom file.", 18 | ) 19 | parser.add_argument( 20 | 'eeprom', 21 | metavar='EEPROM', 22 | type=str, 23 | help='The actual EEPROM settings file we should attach to the ROM.', 24 | ) 25 | parser.add_argument( 26 | '--display-parsed-settings', 27 | action="store_true", 28 | help="Attempt to parse EEPROM and display settings as well.", 29 | ) 30 | parser.add_argument( 31 | '--generate-default-settings-file', 32 | action="store_true", 33 | help="Attempt to generate a fresh settings definition file based on this EEPROM.", 34 | ) 35 | parser.add_argument( 36 | '--settings-directory', 37 | metavar='DIR', 38 | type=str, 39 | default=os.path.join(root, 'naomi', 'settings', 'definitions'), 40 | help='The directory containing settings definition files. Defaults to %(default)s.', 41 | ) 42 | 43 | # Grab what we're doing 44 | args = parser.parse_args() 45 | 46 | with open(args.eeprom, "rb") as fp: 47 | data = fp.read() 48 | 49 | eeprom = NaomiEEPRom(data) 50 | if eeprom.game.valid: 51 | gamehexstr = eeprom.game.data.hex() 52 | gamechunks = [gamehexstr[i:(i + 2)] for i in range(0, len(gamehexstr), 2)] 53 | print(f"Serial: {eeprom.serial.decode('ascii')}") 54 | print(f"Game Settings Hex: {' '.join(gamechunks)}") 55 | 56 | if args.generate_default_settings_file: 57 | filename = f"{eeprom.serial.decode('ascii')}.settings" 58 | pathname = os.path.join(args.settings_directory, filename) 59 | if os.path.isfile(pathname): 60 | print(f"Settings file {filename} already exists!", file=sys.stderr) 61 | return 1 62 | 63 | with open(pathname, "w") as sfp: 64 | for i, chunk in enumerate(gamechunks): 65 | loc = hex(i)[2:] 66 | if len(loc) < 2: 67 | loc = "0" + loc 68 | sfp.write(f"Setting{loc}: byte, read-only, default is {chunk}{os.linesep}") 69 | print(f"Settings file {filename} created with defaults from EEPROM!", file=sys.stderr) 70 | 71 | elif args.display_parsed_settings: 72 | # Grab the actual EEPRom so we can print the settings within. 73 | manager = NaomiSettingsManager(args.settings_directory) 74 | 75 | try: 76 | config = manager.from_eeprom(data) 77 | 78 | print("Parsed Game Settings:") 79 | 80 | if config.game.settings: 81 | for setting in config.game.settings: 82 | # Don't show read-only settints. 83 | if setting.read_only is True: 84 | continue 85 | if isinstance(setting.read_only, ReadOnlyCondition): 86 | if setting.read_only.evaluate(config.game.settings): 87 | continue 88 | 89 | # This shouldn't happen, but make mypy happy. 90 | if setting.current is None: 91 | continue 92 | 93 | print(f" {setting.name}: {setting.values[setting.current]}") 94 | else: 95 | print(" No game settings, game will use its own defaults.") 96 | except (SettingsParseException, SettingsSaveException) as e: 97 | print(f"Error in \"{e.filename}\":", str(e), file=sys.stderr) 98 | return 1 99 | 100 | else: 101 | print(f"Serial: {eeprom.serial.decode('ascii')}, Game Settings: INVALID") 102 | 103 | return 0 104 | 105 | 106 | if __name__ == "__main__": 107 | sys.exit(main()) 108 | -------------------------------------------------------------------------------- /scripts/host_debug_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import sys 4 | from netboot.web import spawn_app 5 | 6 | 7 | def main() -> int: 8 | parser = argparse.ArgumentParser(description="A debug version of the Netboot web services.") 9 | parser.add_argument("-p", "--port", help="Port to listen on. Defaults to 80", type=int, default=80) 10 | parser.add_argument("-c", "--config", help="Core configuration. Defaults to config.yaml", type=str, default="config.yaml") 11 | args = parser.parse_args() 12 | 13 | # Set up global configuration, overriding config port for convenience 14 | app = spawn_app(args.config, debug=True) 15 | app.run(host='0.0.0.0', port=args.port, debug=True) 16 | return 0 17 | 18 | 19 | if __name__ == "__main__": 20 | sys.exit(main()) 21 | -------------------------------------------------------------------------------- /scripts/make_freeplay_patch.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import argparse 3 | import os 4 | import os.path 5 | import sys 6 | from typing import List, Optional 7 | 8 | from arcadeutils.binary import BinaryDiff 9 | from naomi import force_freeplay 10 | 11 | 12 | def legacy_force_freeplay(data: bytes) -> List[str]: 13 | # Apply brute-force search for EEPROM parsing routine (doesn't always work). 14 | newdata = force_freeplay(data) 15 | return ["# Description: force free-play", *BinaryDiff.diff(data, newdata)] 16 | 17 | 18 | def standard_force_freeplay(data: bytes) -> List[str]: 19 | def _hex(val: int) -> str: 20 | out = hex(val)[2:] 21 | out = out.upper() 22 | if len(out) == 1: 23 | out = "0" + out 24 | return out 25 | 26 | def _output(offset: int, old: Optional[int], new: int) -> str: 27 | return f"{_hex(offset)}: {_hex(old) if old is not None else '*'} -> {_hex(new)}" 28 | 29 | def _make_differences(region: int) -> List[str]: 30 | location = 0x1E0 + (0x10 * region) 31 | values: List[str] = [ 32 | _output(location, None, 0x01), 33 | ] 34 | 35 | if data[location + 3] != 27: 36 | values.append(_output(location + 3, data[location + 3], 27)) 37 | return values 38 | 39 | def _force_serial_check() -> List[str]: 40 | return [ 41 | "# Force this patch to only apply to the correct serial", 42 | ( 43 | f"{_hex(0x134)}: " 44 | f"{_hex(data[0x134])} {_hex(data[0x135])} {_hex(data[0x136])} {_hex(data[0x137])} -> " 45 | f"{_hex(data[0x134])} {_hex(data[0x135])} {_hex(data[0x136])} {_hex(data[0x137])}" 46 | ) 47 | ] 48 | 49 | # Manually make a differences file since we want to support ROMs that already 50 | # have enabled EEPROM overrides. 51 | return [ 52 | "# Description: force free-play", 53 | f"# File size: {len(data)}", 54 | *_force_serial_check(), 55 | *_make_differences(0), 56 | *_make_differences(1), 57 | *_make_differences(2), 58 | *_make_differences(3), 59 | *_make_differences(4), 60 | ] 61 | 62 | 63 | def main() -> int: 64 | # Create the argument parser 65 | parser = argparse.ArgumentParser( 66 | description="Utility for creating a forced free-play on patch given a Naomi ROM." 67 | ) 68 | parser.add_argument( 69 | 'bin', 70 | metavar='BIN', 71 | type=str, 72 | help='The binary file we should generate a patch for.', 73 | ) 74 | parser.add_argument( 75 | '--patch-file', 76 | metavar='FILE', 77 | type=str, 78 | help='Write patches to a file instead of stdout.', 79 | ) 80 | parser.add_argument( 81 | '--mode', 82 | type=str, 83 | choices=["legacy", "standard"], 84 | default="standard", 85 | help="Mode to use when creating the patch. Defaults to standard mode (header modifications only).", 86 | ) 87 | 88 | # Grab what we're doing 89 | args = parser.parse_args() 90 | 91 | # Grab the rom, parse it 92 | with open(args.bin, "rb") as fpb: 93 | data = fpb.read() 94 | if args.mode == "legacy": 95 | differences = legacy_force_freeplay(data) 96 | elif args.mode == "standard": 97 | differences = standard_force_freeplay(data) 98 | else: 99 | raise Exception(f"Invalid choice {args.mode}") 100 | if not args.patch_file: 101 | for line in differences: 102 | print(line) 103 | else: 104 | with open(args.patch_file, "w") as fps: 105 | fps.write(os.linesep.join(differences) + os.linesep) 106 | 107 | return 0 108 | 109 | 110 | if __name__ == "__main__": 111 | sys.exit(main()) 112 | -------------------------------------------------------------------------------- /scripts/make_no_attract_patch.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import argparse 3 | import os 4 | import os.path 5 | import sys 6 | from typing import List, Optional 7 | 8 | from arcadeutils.binary import BinaryDiff 9 | from naomi import force_no_attract_sound 10 | 11 | 12 | def legacy_force_no_attract(data: bytes) -> List[str]: 13 | # Apply brute-force search for EEPROM parsing routine (doesn't always work). 14 | newdata = force_no_attract_sound(data) 15 | return ["# Description: force silent attract mode", *BinaryDiff.diff(data, newdata)] 16 | 17 | 18 | def standard_force_no_attract(data: bytes) -> List[str]: 19 | def _hex(val: int) -> str: 20 | out = hex(val)[2:] 21 | out = out.upper() 22 | if len(out) == 1: 23 | out = "0" + out 24 | return out 25 | 26 | def _output(offset: int, old: Optional[int], new: int) -> str: 27 | return f"{_hex(offset)}: {_hex(old) if old is not None else '*'} -> {_hex(new)}" 28 | 29 | def _make_differences(region: int) -> List[str]: 30 | location = 0x1E0 + (0x10 * region) 31 | values: List[str] = [ 32 | _output(location, None, 0x01), 33 | ] 34 | 35 | if (data[location + 1] & 0x2) == 0: 36 | values.append(_output(location + 1, data[location + 1], data[location + 1] | 0x2)) 37 | return values 38 | 39 | def _force_serial_check() -> List[str]: 40 | return [ 41 | "# Force this patch to only apply to the correct serial", 42 | ( 43 | f"{_hex(0x134)}: " 44 | f"{_hex(data[0x134])} {_hex(data[0x135])} {_hex(data[0x136])} {_hex(data[0x137])} -> " 45 | f"{_hex(data[0x134])} {_hex(data[0x135])} {_hex(data[0x136])} {_hex(data[0x137])}" 46 | ) 47 | ] 48 | 49 | # Manually make a differences file since we want to support ROMs that already 50 | # have enabled EEPROM overrides. 51 | return [ 52 | "# Description: force silent attract mode", 53 | f"# File size: {len(data)}", 54 | *_force_serial_check(), 55 | *_make_differences(0), 56 | *_make_differences(1), 57 | *_make_differences(2), 58 | *_make_differences(3), 59 | *_make_differences(4), 60 | ] 61 | 62 | 63 | def main() -> int: 64 | # Create the argument parser 65 | parser = argparse.ArgumentParser( 66 | description="Utility for creating a forced attract sounds off patch given a Naomi ROM." 67 | ) 68 | parser.add_argument( 69 | 'bin', 70 | metavar='BIN', 71 | type=str, 72 | help='The binary file we should generate a patch for.', 73 | ) 74 | parser.add_argument( 75 | '--patch-file', 76 | metavar='FILE', 77 | type=str, 78 | help='Write patches to a file instead of stdout.', 79 | ) 80 | parser.add_argument( 81 | '--mode', 82 | type=str, 83 | choices=["legacy", "standard"], 84 | default="standard", 85 | help="Mode to use when creating the patch. Defaults to standard mode (header modifications only).", 86 | ) 87 | 88 | # Grab what we're doing 89 | args = parser.parse_args() 90 | 91 | # Grab the rom, parse it 92 | with open(args.bin, "rb") as fpb: 93 | data = fpb.read() 94 | if args.mode == "legacy": 95 | differences = legacy_force_no_attract(data) 96 | elif args.mode == "standard": 97 | differences = standard_force_no_attract(data) 98 | else: 99 | raise Exception(f"Invalid choice {args.mode}") 100 | if not args.patch_file: 101 | for line in differences: 102 | print(line) 103 | else: 104 | with open(args.patch_file, "w") as fps: 105 | fps.write(os.linesep.join(differences) + os.linesep) 106 | 107 | return 0 108 | 109 | 110 | if __name__ == "__main__": 111 | sys.exit(main()) 112 | -------------------------------------------------------------------------------- /scripts/netdimm_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Triforce Netfirm Toolbox, put into the public domain. 3 | # Please attribute properly, but only if you want. 4 | import argparse 5 | import enum 6 | import sys 7 | from netdimm import NetDimm, CRCStatusEnum 8 | from typing import Any 9 | 10 | 11 | class EnumAction(argparse.Action): 12 | """ 13 | Argparse action for handling Enums 14 | """ 15 | def __init__(self, **kwargs: Any): 16 | # Pop off the type value 17 | enum_type = kwargs.pop("type", None) 18 | 19 | # Ensure an Enum subclass is provided 20 | if enum_type is None: 21 | raise ValueError("type must be assigned an Enum when using EnumAction") 22 | if not issubclass(enum_type, enum.Enum): 23 | raise TypeError("type must be an Enum when using EnumAction") 24 | 25 | # Generate choices from the Enum 26 | kwargs.setdefault("choices", tuple(e.value for e in enum_type)) 27 | 28 | super(EnumAction, self).__init__(**kwargs) 29 | 30 | self._enum = enum_type 31 | 32 | def __call__(self, parser: Any, namespace: Any, values: Any, option_string: Any = None) -> None: 33 | # Convert value back into an Enum 34 | value = self._enum(values) 35 | setattr(namespace, self.dest, value) 36 | 37 | 38 | def main() -> int: 39 | parser = argparse.ArgumentParser(description="Tools for requesting info from a NetDimm for Naomi/Chihiro/Triforce.") 40 | parser.add_argument( 41 | "ip", 42 | metavar="IP", 43 | type=str, 44 | help="The IP address that the NetDimm is configured on.", 45 | ) 46 | 47 | args = parser.parse_args() 48 | 49 | print("Requesting...", file=sys.stderr) 50 | netdimm = NetDimm(args.ip) 51 | info = netdimm.info() 52 | if info.game_crc_status == CRCStatusEnum.STATUS_CHECKING: 53 | validity = "checking..." 54 | elif info.game_crc_status == CRCStatusEnum.STATUS_VALID: 55 | validity = "valid" 56 | elif info.game_crc_status == CRCStatusEnum.STATUS_INVALID: 57 | validity = "invalid" 58 | elif info.game_crc_status == CRCStatusEnum.STATUS_BAD_MEMORY: 59 | validity = "bad memory module" 60 | elif info.game_crc_status == CRCStatusEnum.STATUS_DISABLED: 61 | validity = "startup crc checking disabled" 62 | 63 | print(f"DIMM Firmware Version: {info.firmware_version.value}") 64 | print(f"DIMM Memory Size: {info.memory_size} MB") 65 | print(f"Available Game Memory Size: {int(info.available_game_memory / 1024 / 1024)} MB") 66 | print(f"Current Game CRC: {hex(info.current_game_crc)[2:]} ({int(info.current_game_size / 1024 / 1024)} MB) ({validity})") 67 | 68 | return 0 69 | 70 | 71 | if __name__ == "__main__": 72 | sys.exit(main()) 73 | -------------------------------------------------------------------------------- /scripts/netdimm_peekpoke.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Triforce Netfirm Toolbox, put into the public domain. 3 | # Please attribute properly, but only if you want. 4 | import argparse 5 | import struct 6 | import sys 7 | 8 | from netdimm import NetDimm, PeekPokeTypeEnum 9 | 10 | 11 | def main() -> int: 12 | parser = argparse.ArgumentParser( 13 | description="Tools for peeking/poking values into running memory on a Naomi/Triforce/Chihiro.", 14 | ) 15 | subparsers = parser.add_subparsers(help='Action to take', dest='action') 16 | 17 | peek_parser = subparsers.add_parser( 18 | 'peek', 19 | help='Peek at a value in an 8/16/32-bit memory location', 20 | description='Peek at a value in an 8/16/32-bit memory location', 21 | ) 22 | peek_parser.add_argument( 23 | "ip", 24 | metavar="IP", 25 | type=str, 26 | help="The IP address that the NetDimm is configured on.", 27 | ) 28 | peek_parser.add_argument( 29 | "address", 30 | metavar="ADDR", 31 | type=str, 32 | help="The hex address of memory that you would like to peek into.", 33 | ) 34 | peek_parser.add_argument( 35 | "size", 36 | metavar="SIZE", 37 | type=int, 38 | help="The size in bytes you want to read. Valid values are 1, 2 and 4.", 39 | ) 40 | 41 | poke_parser = subparsers.add_parser( 42 | 'poke', 43 | help='Poke a value into an 8/16/32-bit memory location', 44 | description='Poke a value into an 8/16/32-bit memory location', 45 | ) 46 | poke_parser.add_argument( 47 | "ip", 48 | metavar="IP", 49 | type=str, 50 | help="The IP address that the NetDimm is configured on.", 51 | ) 52 | poke_parser.add_argument( 53 | "address", 54 | metavar="ADDR", 55 | type=str, 56 | help="The hex address of memory that you would like to poke into.", 57 | ) 58 | poke_parser.add_argument( 59 | "size", 60 | metavar="SIZE", 61 | type=int, 62 | help="The size in bytes you want to write. Valid values are 1, 2 and 4.", 63 | ) 64 | poke_parser.add_argument( 65 | "data", 66 | metavar="VALUE", 67 | type=str, 68 | help="The hex value you wish to write into the address.", 69 | ) 70 | 71 | dump_parser = subparsers.add_parser( 72 | 'dump', 73 | help='Dump data from a memory location.', 74 | description='Dump data from a memory location.', 75 | ) 76 | dump_parser.add_argument( 77 | "ip", 78 | metavar="IP", 79 | type=str, 80 | help="The IP address that the NetDimm is configured on.", 81 | ) 82 | dump_parser.add_argument( 83 | "file", 84 | metavar="FILE", 85 | type=str, 86 | help="The file you want to dump data to.", 87 | ) 88 | dump_parser.add_argument( 89 | "address", 90 | metavar="ADDR", 91 | type=str, 92 | help="The hex address of memory that you would like to dump from.", 93 | ) 94 | dump_parser.add_argument( 95 | "size", 96 | metavar="SIZE", 97 | type=int, 98 | help="The size in bytes you want to read.", 99 | ) 100 | 101 | load_parser = subparsers.add_parser( 102 | 'load', 103 | help='Load data to a memory location.', 104 | description='Load data to a memory location.', 105 | ) 106 | load_parser.add_argument( 107 | "ip", 108 | metavar="IP", 109 | type=str, 110 | help="The IP address that the NetDimm is configured on.", 111 | ) 112 | load_parser.add_argument( 113 | "file", 114 | metavar="FILE", 115 | type=str, 116 | help="The file you want to load data to.", 117 | ) 118 | load_parser.add_argument( 119 | "address", 120 | metavar="ADDR", 121 | type=str, 122 | help="The hex address of memory that you would like to load to.", 123 | ) 124 | 125 | args = parser.parse_args() 126 | netdimm = NetDimm(args.ip) 127 | 128 | if args.action == "peek": 129 | if args.size == 1: 130 | data = netdimm.peek(int(args.address, 16), PeekPokeTypeEnum.TYPE_BYTE) & 0xFF 131 | elif args.size == 2: 132 | data = netdimm.peek(int(args.address, 16), PeekPokeTypeEnum.TYPE_SHORT) & 0xFFFF 133 | elif args.size == 4: 134 | data = netdimm.peek(int(args.address, 16), PeekPokeTypeEnum.TYPE_LONG) & 0xFFFFFFFF 135 | else: 136 | raise Exception(f"Invalid size selection {args.size}!") 137 | 138 | hexdata = hex(data)[2:] 139 | while len(hexdata) < (2 * args.size): 140 | hexdata = "0" + hexdata 141 | print(hexdata) 142 | 143 | elif args.action == "poke": 144 | if args.size == 1: 145 | netdimm.poke(int(args.address, 16), PeekPokeTypeEnum.TYPE_BYTE, int(args.data, 16) & 0xFF) 146 | elif args.size == 2: 147 | netdimm.poke(int(args.address, 16), PeekPokeTypeEnum.TYPE_SHORT, int(args.data, 16) & 0xFFFF) 148 | elif args.size == 4: 149 | netdimm.poke(int(args.address, 16), PeekPokeTypeEnum.TYPE_LONG, int(args.data, 16) & 0xFFFFFFFF) 150 | else: 151 | raise Exception(f"Invalid size selection {args.size}!") 152 | 153 | elif args.action == "dump": 154 | with open(args.file, "wb") as bfp: 155 | for i in range(args.size): 156 | data = netdimm.peek(int(args.address, 16) + i, PeekPokeTypeEnum.TYPE_BYTE) & 0xFF 157 | bfp.write(struct.pack("B", data)) 158 | print(f"Dumped {args.size} bytes to {args.file}") 159 | 160 | elif args.action == "load": 161 | with open(args.file, "rb") as bfp: 162 | for amount, b in enumerate(bfp.read()): 163 | netdimm.poke(int(args.address, 16) + amount, PeekPokeTypeEnum.TYPE_BYTE, b) 164 | print(f"Loaded {amount} bytes from {args.file}") 165 | 166 | return 0 167 | 168 | 169 | if __name__ == "__main__": 170 | sys.exit(main()) 171 | -------------------------------------------------------------------------------- /scripts/netdimm_receive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Triforce Netfirm Toolbox, put into the public domain. 3 | # Please attribute properly, but only if you want. 4 | import argparse 5 | import enum 6 | import sys 7 | from netdimm import NetDimm, NetDimmVersionEnum, NetDimmTargetEnum 8 | from typing import Any 9 | 10 | 11 | class EnumAction(argparse.Action): 12 | """ 13 | Argparse action for handling Enums 14 | """ 15 | def __init__(self, **kwargs: Any): 16 | # Pop off the type value 17 | enum_type = kwargs.pop("type", None) 18 | 19 | # Ensure an Enum subclass is provided 20 | if enum_type is None: 21 | raise ValueError("type must be assigned an Enum when using EnumAction") 22 | if not issubclass(enum_type, enum.Enum): 23 | raise TypeError("type must be an Enum when using EnumAction") 24 | 25 | # Generate choices from the Enum 26 | kwargs.setdefault("choices", tuple(e.value for e in enum_type)) 27 | 28 | super(EnumAction, self).__init__(**kwargs) 29 | 30 | self._enum = enum_type 31 | 32 | def __call__(self, parser: Any, namespace: Any, values: Any, option_string: Any = None) -> None: 33 | # Convert value back into an Enum 34 | value = self._enum(values) 35 | setattr(namespace, self.dest, value) 36 | 37 | 38 | def main() -> int: 39 | parser = argparse.ArgumentParser(description="Tools for receiving images from NetDimm for Naomi/Chihiro/Triforce.") 40 | parser.add_argument( 41 | "ip", 42 | metavar="IP", 43 | type=str, 44 | help="The IP address that the NetDimm is configured on.", 45 | ) 46 | parser.add_argument( 47 | "image", 48 | metavar="IMAGE", 49 | type=str, 50 | help="The image file we should write after receiving from to the NetDimm.", 51 | ) 52 | parser.add_argument( 53 | "--target", 54 | metavar="TARGET", 55 | type=NetDimmTargetEnum, 56 | action=EnumAction, 57 | default=NetDimmTargetEnum.TARGET_NAOMI, 58 | help="Target platform this image is coming from. Defaults to 'naomi'. Choose from 'naomi', 'chihiro' or 'triforce'.", 59 | ) 60 | parser.add_argument( 61 | "--version", 62 | metavar="VERSION", 63 | type=NetDimmVersionEnum, 64 | action=EnumAction, 65 | default=NetDimmVersionEnum.VERSION_4_01, 66 | help="NetDimm firmware version this image is coming from. Defaults to '4.01'. Choose from '1.02', '2.06', '2.17', '3.03', '3.17', '4.01' or '4.02'.", 67 | ) 68 | 69 | # Give more time to slower netboot on some platforms. 70 | parser.add_argument( 71 | '--receive-timeout', 72 | type=int, 73 | default=None, 74 | help="Specify a different receive timeout in seconds", 75 | ) 76 | 77 | args = parser.parse_args() 78 | 79 | print("receiving...", file=sys.stderr) 80 | netdimm = NetDimm(args.ip, version=args.version, target=args.target, timeout=args.receive_timeout) 81 | 82 | # Receive the binary. 83 | data = netdimm.receive() 84 | 85 | if data: 86 | with open(args.image, "wb") as fp: 87 | fp.write(data) 88 | 89 | print("ok!", file=sys.stderr) 90 | else: 91 | print("no valid game exists on net dimm!", file=sys.stderr) 92 | 93 | return 0 94 | 95 | 96 | if __name__ == "__main__": 97 | sys.exit(main()) 98 | 99 | # ok, now you're on your own, the tools are there. 100 | # We see the DIMM space as it's seen by the dimm-board (i.e. as on the disc). 101 | # It will be transparently decrypted when accessed from Host, unless a 102 | # zero-key has been set. We do this before uploading something, so we don't 103 | # have to bother with the inserted key chip. Still, some key chip must be 104 | # present. 105 | # You need to configure the triforce to boot in "satellite mode", 106 | # which can be done using the dipswitches on the board (type-3) or jumpers 107 | # (VxWorks-style). 108 | # The dipswitch for type-3 must be in the following position: 109 | # - SW1: ON ON * 110 | # - It shouldn't wait for a GDROM anymore, but display error 31. 111 | # For the VxWorks-Style: 112 | # - Locate JP1..JP3 on the upper board in the DIMM board. They are near 113 | # the GDROM-connector. 114 | # The jumpers must be in this position for satellite mode: 115 | # 1 3 116 | # [. .]. JP1 117 | # [. .]. JP2 118 | # .[. .] JP3 119 | # - when you switch on the triforce, it should say "waiting for network..." 120 | # 121 | # Good Luck. Warez are evil. 122 | -------------------------------------------------------------------------------- /scripts/patch_default_settings.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import argparse 3 | import os 4 | import sys 5 | 6 | from arcadeutils import FileBytes, BinaryDiff 7 | from naomi import NaomiEEPRom, NaomiRom 8 | from naomi.settings import NaomiSettingsManager 9 | 10 | 11 | # The root of the repo. 12 | root = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) 13 | 14 | 15 | def main() -> int: 16 | # Create the argument parser 17 | parser = argparse.ArgumentParser( 18 | description="Command-Line Utility for patching different game defaults into a Naomi ROM.", 19 | ) 20 | parser.add_argument( 21 | 'rom', 22 | metavar='ROM', 23 | type=str, 24 | help='The ROM we should generate a patch for.', 25 | ) 26 | parser.add_argument( 27 | 'eeprom', 28 | metavar='EEPROM', 29 | type=str, 30 | help='The EEPROM settings file we should use to generate the patch.', 31 | ) 32 | parser.add_argument( 33 | '--output-file', 34 | metavar='BIN', 35 | type=str, 36 | default=None, 37 | help='A different file to output to instead of updating the ROM specified directly.', 38 | ) 39 | parser.add_argument( 40 | '--patch-file', 41 | metavar='PATCH', 42 | type=str, 43 | default=None, 44 | help='Write changed bytes to a patch instead of generating a new ROM.', 45 | ) 46 | parser.add_argument( 47 | '--settings-directory', 48 | metavar='DIR', 49 | type=str, 50 | default=os.path.join(root, 'naomi', 'settings', 'definitions'), 51 | help='The directory containing settings definition files. Defaults to %(default)s.', 52 | ) 53 | 54 | # Grab what we're doing 55 | args = parser.parse_args() 56 | 57 | if args.output_file and args.patch_file: 58 | raise Exception("Cannot write both a patch and a new ROM!") 59 | 60 | # First, try to open the EEPRom file. 61 | with open(args.eeprom, "rb") as fp: 62 | eeprom = NaomiEEPRom(fp.read()) 63 | manager = NaomiSettingsManager(args.settings_directory) 64 | defaults = manager.from_serial(eeprom.serial) 65 | defaulteeprom = NaomiEEPRom(manager.to_eeprom(defaults)) 66 | 67 | with open(args.rom, "rb" if args.output_file else "rb+") as fp: 68 | data = FileBytes(fp) 69 | original = data.clone() 70 | rom = NaomiRom(data) 71 | 72 | defaultbytes = defaulteeprom.game.data 73 | updatedbytes = eeprom.game.data 74 | 75 | if len(defaultbytes) != len(updatedbytes): 76 | raise Exception("EEPROM sections aren't the same length!") 77 | 78 | for exe in [rom.main_executable, rom.test_executable]: 79 | for section in exe.sections: 80 | start = section.offset 81 | end = section.offset + section.length 82 | print(f"Searching {start} to {end}...") 83 | 84 | while True: 85 | found = data.search(defaultbytes, start=start, end=end) 86 | 87 | if found is not None: 88 | print(f"Patching offset {found}!") 89 | data[found:(found + len(updatedbytes))] = updatedbytes 90 | start = found + 1 91 | else: 92 | # Done! 93 | break 94 | 95 | if args.patch_file: 96 | print(f"Generating EEPROM settings patch and writing to {args.patch_file}.") 97 | changes = ["# Description: patch default game settings", *BinaryDiff.diff(original, data)] 98 | with open(args.patch_file, "w") as fps: 99 | fps.write(os.linesep.join(changes) + os.linesep) 100 | else: 101 | if args.output_file: 102 | print(f"Patched default game EEPROM settings to {args.output_file}.") 103 | with open(args.output_file, "wb") as fp: 104 | data.write_changes(fp) 105 | else: 106 | print(f"Patched default game EEPROM settings to {args.rom}.") 107 | data.write_changes() 108 | 109 | return 0 110 | 111 | 112 | if __name__ == "__main__": 113 | sys.exit(main()) 114 | -------------------------------------------------------------------------------- /scripts/rominfo.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import argparse 3 | import sys 4 | from typing import Dict 5 | 6 | from arcadeutils import FileBytes 7 | from naomi import NaomiRom, NaomiRomRegionEnum 8 | 9 | 10 | def main() -> int: 11 | # Create the argument parser 12 | parser = argparse.ArgumentParser( 13 | description="Utility for printing information about a ROM file.", 14 | ) 15 | parser.add_argument( 16 | 'bin', 17 | metavar='BIN', 18 | type=str, 19 | help='The binary file we should generate info for.', 20 | ) 21 | 22 | # Grab what we're doing 23 | args = parser.parse_args() 24 | 25 | # Grab the rom, parse it 26 | with open(args.bin, "rb") as fp: 27 | data = FileBytes(fp) 28 | 29 | # Create a text LUT 30 | region_lut: Dict[NaomiRomRegionEnum, str] = { 31 | NaomiRomRegionEnum.REGION_JAPAN: "Japan", 32 | NaomiRomRegionEnum.REGION_USA: "USA", 33 | NaomiRomRegionEnum.REGION_EXPORT: "Export", 34 | NaomiRomRegionEnum.REGION_KOREA: "Korea", 35 | NaomiRomRegionEnum.REGION_AUSTRALIA: "Australia", 36 | } 37 | 38 | # First, assume its a Naomi ROM 39 | naomi = NaomiRom(data) 40 | if naomi.valid: 41 | print("NAOMI ROM") 42 | print("=========") 43 | print(f"Publisher: {naomi.publisher}") 44 | print(f"Japan Title: {naomi.names[NaomiRomRegionEnum.REGION_JAPAN]}") 45 | print(f"USA Title: {naomi.names[NaomiRomRegionEnum.REGION_USA]}") 46 | print(f"Export Title: {naomi.names[NaomiRomRegionEnum.REGION_EXPORT]}") 47 | print(f"Korea Title: {naomi.names[NaomiRomRegionEnum.REGION_KOREA]}") 48 | print(f"Australia Title: {naomi.names[NaomiRomRegionEnum.REGION_AUSTRALIA]}") 49 | print(f"Publish Date: {naomi.date}") 50 | print(f"Serial Number: {naomi.serial.decode('ascii')}") 51 | print(f"ROM Size: {len(data)} bytes") 52 | print("") 53 | 54 | print("Supported Configurations") 55 | print("------------------------") 56 | print(f"Regions: {', '.join(region_lut[r] for r in naomi.regions)}") 57 | print(f"Players: {', '.join(str(p) for p in naomi.players)}") 58 | print(f"Monitor: {', '.join(str(f) + 'khz' for f in naomi.frequencies)}") 59 | print(f"Orientation: {', '.join(o for o in naomi.orientations)}") 60 | print(f"Service Type: {naomi.servicetype}") 61 | print("") 62 | 63 | print("Main Executable Sections") 64 | print("------------------------") 65 | for section in naomi.main_executable.sections: 66 | print(f"ROM Offset: {hex(section.offset)}") 67 | print(f"Memory Offset: {hex(section.load_address)}") 68 | print(f"Section Length: {section.length} bytes") 69 | print("") 70 | print(f"Entrypoint: {hex(naomi.main_executable.entrypoint)}") 71 | print("") 72 | 73 | print("Test Executable Sections") 74 | print("------------------------") 75 | for section in naomi.test_executable.sections: 76 | print(f"ROM Offset: {hex(section.offset)}") 77 | print(f"Memory Offset: {hex(section.load_address)}") 78 | print(f"Section Length: {section.length} bytes") 79 | print("") 80 | print(f"Entrypoint: {hex(naomi.test_executable.entrypoint)}") 81 | print("") 82 | 83 | print("Per-Region EEPROM Defaults") 84 | print("--------------------------") 85 | for region, default in naomi.defaults.items(): 86 | print(f"{region_lut[region]}") 87 | if not default.apply_settings: 88 | print("Override: disabled") 89 | else: 90 | print("Override: enabled") 91 | print(f"Force vertical: {'yes' if default.force_vertical else 'no'}") 92 | print(f"Force silent: {'yes' if default.force_silent else 'no'}") 93 | print(f"Chute type: {default.chute}") 94 | if default.coin_setting < 27: 95 | setting = f"#{default.coin_setting}" 96 | elif default.coin_setting == 27: 97 | setting = "free play" 98 | elif default.coin_setting == 28: 99 | setting = "manual assignment" 100 | print(f"Coin setting: {setting}") 101 | if default.coin_setting == 28: 102 | print(f"Coin 1 rate: {default.coin_1_rate}") 103 | print(f"Coin 2 rate: {default.coin_2_rate}") 104 | print(f"Credit rate: {default.credit_rate}") 105 | print(f"Bonus: {default.bonus}") 106 | for i, text in enumerate(default.sequences): 107 | print(f"Sequence {i + 1}: {text}") 108 | print("") 109 | 110 | return 0 111 | 112 | # Couldn't figure out ROM type 113 | print("Couldn't determine ROM type!", file=sys.stderr) 114 | return 1 115 | 116 | 117 | if __name__ == "__main__": 118 | sys.exit(main()) 119 | -------------------------------------------------------------------------------- /scripts/server.wsgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from netboot.web import spawn_app 3 | 4 | # Set up global configuration, overriding config port for convenience 5 | app = spawn_app("config.yaml") 6 | -------------------------------------------------------------------------------- /services/README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi Services 2 | 3 | This directory contains various services that are available for installing on a Raspberry Pi in order to turn it into a net dimm capable device. Once setup, the Raspberry Pi will boot up and then perform the stated action forever until its power is lost. These scripts assume that you are using your boot partition to store the ROM image(s) and configuration. No documentation on how to resize the boot partition to ensure it has enough space is provided, but you can search online for various tutorials to help you do so. 4 | 5 | Do note that the following services are designed for Naomi systems ONLY! The kiosk mode should work for any Naomi/Triforce/Chihiro setup you have, but it is untested for everything but Naomi. For menu mode, only code for the Naomi has been written so it will not work at all for other systems! 6 | 7 | ## Initial Setup 8 | 9 | All of these scripts assume you are using a modern version of the Raspberry Pi OS installed on a microSD card that's inserted into your RPI. To install the correct version of the OS, you can use the Raspberry Pi installer at . Under the OS selection menu, choose "Raspberry Pi OS (Other)" and then choose "Raspberry Pi OS (Lite)" to install the OS without a desktop which you will not need. Once you have installed the OS on your SD card, inserted it into the RPI, booted up and logged in you will want to run the following commands to prime the RPI to run this softare: 10 | 11 | ``` 12 | sudo apt update 13 | sudo apt upgrade 14 | sudo apt install python3 python3-pip git 15 | cd ~ 16 | git clone https://github.com/DragonMinded/netboot.git 17 | cd ~/netboot 18 | sudo python3 -m pip install -r requirements.txt 19 | ``` 20 | 21 | After all of these commands have run, you should have the contents of this repository on your RPI and they should be ready to go. If you try the following command it should print the help and give you no errors about missing packages: 22 | 23 | ``` 24 | cd ~/netboot 25 | ./netdimm_send --help 26 | ``` 27 | 28 | ## Setting up Static IP 29 | 30 | In order for the RPI to directly talk to a net dimm with no router, switch or anything inbetween them, it must be set up with a static IP. This is because there is nothing to give it an IP address, and if it does not have one it won't even try to talk to the net dimm. The following instructions assume that you have a net dimm with IP "192.168.1.2" which is the standard IP address for net dimms sold as kits and using piforcetools. Run the following commands to set your RPI up for fixed IP: 31 | 32 | ``` 33 | cd ~/netboot/services 34 | sudo ./setup_fixed_ip.sh 35 | sudo reboot now 36 | ``` 37 | 38 | If everything worked right, your RPI should reboot. When you log back in, type the following command: 39 | 40 | ``` 41 | ifconfig 42 | ``` 43 | 44 | If you can see `inet 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255` under the `eth0` interface, then you successfully set up fixed IP! 45 | 46 | ## Kiosk Mode 47 | 48 | If you want to always send the same game to your net dimm every single boot, no matter what, you should use kiosk mode. In this mode, a script called `netdimm_ensure` attempts to make the net dimm act like it has a GD-ROM or CF card inserted, always booting the same game no matter what, every time the target net dimm is turned on. For this to work, you will need a file called "autorun.bin" in your `/boot` partition. From the perspective of the RPI it will be `/boot/autorun.bin`. From the perspective of a Windows/OSX/Linux computer with an SD card reader, it will be "autorun.bin" on the root of the first (or only) partition the OS can see. Copy whatever game you want to run every boot to the boot partition and rename the file to `autorun.bin`. Now, run the following command to install the service: 49 | 50 | ``` 51 | sudo cp kiosk.service /etc/systemd/system/ 52 | sudo systemctl enable kiosk 53 | ``` 54 | 55 | Now, you can shut down the RPI, plug it into the net dimm and switch on the RPI and the target system at once. If all was setup properly you should see the game loading onto the net dimm and then booting when you are finished! It is recommended at this point to put your RPI root filesystem into read-only mode. Failure to do so can cause filesystem corruption over time as you turn the power off unexpectedly without shutting down. You can do so by running `sudo raspi-config` and activating "OverlayFS" in the menu. Be sure to do so after you've tested everything as this makes changes difficult! 56 | 57 | ## Menu Mode 58 | 59 | If you want to see a menu on your arcade cabinet full of all of the ROMs you have on your RPI every time you cycle power, you should use menu mode. In this mode, a script called `netdimm_menu` attempts to send a menu full of your games to the target net dimm every time it is powered on. You can then select the game you want from the menu and press start to boot it. The game will be sent to the net dimm and you can play it for as long as you want. Then, when you want a new game, you can cycle power and the menu will again be sent to the cabinet. The menu allows you to set up options and patches per-game as a bonus feature. For this to work, you will need a dirctory called "roms" in your `/boot` partition. From the perspective of the RPI it will be `/boot/roms/`. From the perspective of a Windows/OSX/Linux computer with an SD card reader, it will be "roms" on the root of the first (or only) partition the OS can see. Copy whatever games you want to appear on the menu into this directory. Now, run the following command to install the service: 60 | 61 | ``` 62 | sudo mkdir /boot/roms 63 | sudo cp menu.service /etc/systemd/system/ 64 | sudo systemctl enable menu 65 | ``` 66 | 67 | Now, you can shut down the RPI, plug it into the net dimm and swithc on the RPI and the menu should appear. You can hit the test button on the main game list to configure the menu, and you can hold start on any game for 1+ second to enter the game configuration screen. It is recommended at this point to put your RPI root filesystem into read-only mode. Failure to do so can cause filesystem corruption over time as you turn the power off unexpectedly without shutting down. You can do so by running `sudo raspi-config` and activating "OverlayFS" in the menu. Be sure to do so after you've tested everything as this makes changes difficult! 68 | -------------------------------------------------------------------------------- /services/kiosk.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Net Dimm Kiosk Service 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | Restart=always 8 | User=root 9 | Group=root 10 | WorkingDirectory=/home/pi/netboot/ 11 | ExecStart=/home/pi/netboot/services/kiosk.sh 12 | StandardOutput=journal 13 | StandardError=journal 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /services/kiosk.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | /home/pi/netboot/netdimm_ensure 192.168.1.2 /boot/autorun.bin 4 | -------------------------------------------------------------------------------- /services/menu.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Net Dimm Naomi Menu Service 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | Restart=always 8 | User=root 9 | Group=root 10 | WorkingDirectory=/home/pi/netboot/ 11 | ExecStart=/home/pi/netboot/services/menu.sh 12 | StandardOutput=journal 13 | StandardError=journal 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /services/menu.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | /home/pi/netboot/netdimm_menu 192.168.1.2 /boot/roms/ --menu-settings-file /boot/netdimm_menu_settings.yaml --persistent 4 | -------------------------------------------------------------------------------- /services/setup_fixed_ip.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | cat "interface eth0" >> /etc/dhcpcd.conf 4 | cat "static ip_address=192.168.1.1/24" >> /etc/dhcpcd.conf 5 | cat "static routers=192.168.1.1" >> /etc/dhcpcd.conf 6 | cat "static domain_name_servers=192.168.1.1" >> /etc/dhcpcd.conf 7 | -------------------------------------------------------------------------------- /settings/README.md: -------------------------------------------------------------------------------- 1 | # settings 2 | 3 | A collection of helper routines and classes that are used by naomi.settings 4 | to provide high-level binary manipulation of EEPROM files. Note that while 5 | these routines could be used to manipulate Naomi SRAM files or other arbitrary 6 | binary data there has been no effort to generalize them further than the present. 7 | 8 | Ideally this would have its own generic file read/write routines and such so 9 | that it could be used with definitions files and any arbitrary binary data. However, 10 | it currently is usuable only with the naomi-specific settings code. Perhaps sometime 11 | in the future this will be fleshed out, tested and made into its own module? 12 | -------------------------------------------------------------------------------- /settings/__init__.py: -------------------------------------------------------------------------------- 1 | from settings.settings import ( 2 | SettingsParseException, 3 | SettingsSaveException, 4 | JSONParseException, 5 | ReadOnlyCondition, 6 | DefaultCondition, 7 | DefaultConditionGroup, 8 | Setting, 9 | Settings, 10 | SettingsConfig, 11 | ) 12 | 13 | 14 | __all__ = [ 15 | "SettingsParseException", 16 | "SettingsSaveException", 17 | "JSONParseException", 18 | "ReadOnlyCondition", 19 | "DefaultCondition", 20 | "DefaultConditionGroup", 21 | "Setting", 22 | "Settings", 23 | "SettingsConfig", 24 | ] 25 | -------------------------------------------------------------------------------- /settings/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/settings/py.typed -------------------------------------------------------------------------------- /srams/README.txt: -------------------------------------------------------------------------------- 1 | Place SRAM files that can be attached to roms here if you do not want to configure your own list of directories. 2 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DragonMinded/netboot/7283a491d4a788568babe02d66657812bcb0232d/tests/__init__.py --------------------------------------------------------------------------------