├── .github ├── FUNDING.yml ├── pull_request_template.md └── workflows │ └── autobuild.yml ├── .gitignore ├── Info.plist ├── LICENSE ├── Makefile ├── Makefile.switch ├── Makefile.vita ├── Makefile.wiiu ├── README.md ├── build.gradle ├── com.hydra.noods.desktop ├── com.hydra.noods.yml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── icon ├── icon-linux.png ├── icon-mac.icns ├── icon-switch.jpg ├── icon-vita.png ├── icon-windows.ico ├── icon-windows.rc └── icon.xpm ├── keystore.jks ├── mac-bundle.sh ├── settings.gradle └── src ├── action_replay.cpp ├── action_replay.h ├── android ├── AndroidManifest.xml ├── CMakeLists.txt ├── build.gradle ├── cpp │ └── interface.cpp ├── java │ └── com │ │ └── hydra │ │ └── noods │ │ ├── BindingsMenu.java │ │ ├── BindingsPreference.java │ │ ├── FileAdapter.java │ │ ├── FileBrowser.java │ │ ├── NooActivity.java │ │ ├── NooButton.java │ │ ├── NooRenderer.java │ │ └── SettingsMenu.java ├── play-store.patch └── res │ ├── drawable-v26 │ ├── icon.xml │ ├── icon_background.png │ └── icon_foreground.png │ ├── drawable │ ├── abxy.png │ ├── abxy_pressed1.png │ ├── abxy_pressed2.png │ ├── abxy_pressed3.png │ ├── abxy_pressed4.png │ ├── abxy_pressed5.png │ ├── abxy_pressed6.png │ ├── abxy_pressed7.png │ ├── abxy_pressed8.png │ ├── bindings.png │ ├── dpad.png │ ├── dpad_pressed1.png │ ├── dpad_pressed2.png │ ├── dpad_pressed3.png │ ├── dpad_pressed4.png │ ├── dpad_pressed5.png │ ├── dpad_pressed6.png │ ├── dpad_pressed7.png │ ├── dpad_pressed8.png │ ├── file.png │ ├── folder.png │ ├── icon.png │ ├── info.png │ ├── l.png │ ├── l_pressed.png │ ├── r.png │ ├── r_pressed.png │ ├── select.png │ ├── select_pressed.png │ ├── settings.png │ ├── start.png │ ├── start_pressed.png │ └── storage.png │ ├── layout │ └── file_row.xml │ ├── menu │ ├── file_menu.xml │ ├── noo_menu.xml │ └── settings_menu.xml │ ├── values │ └── arrays.xml │ └── xml │ ├── bindings.xml │ └── settings.xml ├── cartridge.cpp ├── cartridge.h ├── common ├── nds_icon.cpp ├── nds_icon.h ├── screen_layout.cpp └── screen_layout.h ├── console ├── console_ui.cpp ├── console_ui.h ├── images │ ├── file_dark.bmp │ ├── file_light.bmp │ ├── folder_dark.bmp │ ├── folder_light.bmp │ └── font.bmp ├── main_switch.cpp ├── main_vita.cpp ├── main_wiiu.cpp └── shaders │ ├── latte-assembler │ ├── shader_wiiu.gsh │ ├── shader_wiiu.psh │ └── shader_wiiu.vsh ├── core.cpp ├── core.h ├── cp15.cpp ├── cp15.h ├── defines.h ├── desktop ├── cheat_dialog.cpp ├── cheat_dialog.h ├── input_dialog.cpp ├── input_dialog.h ├── layout_dialog.cpp ├── layout_dialog.h ├── main.cpp ├── noo_app.cpp ├── noo_app.h ├── noo_canvas.cpp ├── noo_canvas.h ├── noo_frame.cpp ├── noo_frame.h ├── path_dialog.cpp ├── path_dialog.h ├── save_dialog.cpp └── save_dialog.h ├── div_sqrt.cpp ├── div_sqrt.h ├── dldi.cpp ├── dldi.h ├── dma.cpp ├── dma.h ├── gpu.cpp ├── gpu.h ├── gpu_2d.cpp ├── gpu_2d.h ├── gpu_3d.cpp ├── gpu_3d.h ├── gpu_3d_renderer.cpp ├── gpu_3d_renderer.h ├── hle_arm7.cpp ├── hle_arm7.h ├── hle_bios.cpp ├── hle_bios.h ├── input.cpp ├── input.h ├── interpreter.cpp ├── interpreter.h ├── interpreter_alu.cpp ├── interpreter_branch.cpp ├── interpreter_lookup.cpp ├── interpreter_transfer.cpp ├── ipc.cpp ├── ipc.h ├── memory.cpp ├── memory.h ├── rtc.cpp ├── rtc.h ├── save_states.cpp ├── save_states.h ├── settings.cpp ├── settings.h ├── spi.cpp ├── spi.h ├── spu.cpp ├── spu.h ├── timers.cpp ├── timers.h ├── wifi.cpp └── wifi.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: Hydr8gon 2 | custom: paypal.me/Hydr8gon 3 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Pull requests are not accepted for this project; see the contributing section of the readme for more details. 2 | -------------------------------------------------------------------------------- /.github/workflows/autobuild.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Builds 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build-windows: 10 | runs-on: windows-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v1 15 | - name: Install MSYS2 16 | uses: msys2/setup-msys2@v2 17 | with: 18 | msystem: MINGW64 19 | update: true 20 | - name: Install build tools, wxWidgets, and PortAudio 21 | run: pacman -S mingw-w64-x86_64-{gcc,pkg-config,wxWidgets,portaudio,jbigkit} make --noconfirm 22 | shell: msys2 {0} 23 | - name: Compile 24 | run: | 25 | make -j$(nproc) 26 | strip noods.exe 27 | shell: msys2 {0} 28 | working-directory: ${{ github.workspace }} 29 | - name: Upload 30 | uses: actions/upload-artifact@v4 31 | with: 32 | name: noods-windows 33 | path: noods.exe 34 | 35 | build-mac: 36 | runs-on: macos-latest 37 | 38 | steps: 39 | - name: Install wxWidgets and PortAudio 40 | run: brew install wxmac portaudio 41 | - name: Checkout 42 | uses: actions/checkout@v1 43 | - name: Compile 44 | run: | 45 | make -j$(sysctl -n hw.logicalcpu) 46 | ./mac-bundle.sh --dmg 47 | - name: Upload 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: noods-mac 51 | path: NooDS.dmg 52 | 53 | build-linux: 54 | runs-on: ubuntu-latest 55 | 56 | steps: 57 | - name: Install Flatpak and SDK 58 | run: | 59 | sudo apt update 60 | sudo apt install flatpak flatpak-builder -y 61 | sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 62 | sudo flatpak install flathub org.freedesktop.Platform//21.08 org.freedesktop.Sdk//21.08 -y 63 | - name: Checkout 64 | uses: actions/checkout@v1 65 | - name: Compile 66 | run: | 67 | git config --global protocol.file.allow always 68 | make flatpak 69 | - name: Upload 70 | uses: actions/upload-artifact@v4 71 | with: 72 | name: noods-linux 73 | path: noods.flatpak 74 | 75 | build-android: 76 | runs-on: ubuntu-latest 77 | 78 | steps: 79 | - name: Checkout 80 | uses: actions/checkout@v1 81 | - name: Compile 82 | run: | 83 | ./gradlew assembleRelease 84 | mv build-android/outputs/apk/release/android-release-unsigned.apk noods.apk 85 | - name: Sign 86 | run: $ANDROID_SDK_ROOT/build-tools/30.0.2/apksigner sign --ks keystore.jks --ks-pass pass:$KEYSTORE_PASS noods.apk 87 | env: 88 | KEYSTORE_PASS: ${{ secrets.KEYSTORE_PASS }} 89 | - name: Upload 90 | uses: actions/upload-artifact@v4 91 | with: 92 | name: noods-android 93 | path: noods.apk 94 | 95 | build-switch: 96 | runs-on: ubuntu-latest 97 | container: devkitpro/devkita64:latest 98 | 99 | steps: 100 | - name: Checkout 101 | uses: actions/checkout@v1 102 | - name: Compile 103 | run: make switch -j$(nproc) 104 | - name: Upload 105 | uses: actions/upload-artifact@v4 106 | with: 107 | name: noods-switch 108 | path: noods.nro 109 | 110 | build-wiiu: 111 | runs-on: ubuntu-latest 112 | container: devkitpro/devkitppc:latest 113 | 114 | steps: 115 | - name: Checkout 116 | uses: actions/checkout@v1 117 | - name: Compile 118 | run: make wiiu -j$(nproc) 119 | - name: Upload 120 | uses: actions/upload-artifact@v4 121 | with: 122 | name: noods-wiiu 123 | path: noods.wuhb 124 | 125 | build-vita: 126 | runs-on: ubuntu-latest 127 | container: vitasdk/vitasdk:latest 128 | 129 | steps: 130 | - name: Checkout 131 | uses: actions/checkout@v1 132 | - name: Compile 133 | run: make vita -j$(nproc) 134 | - name: Upload 135 | uses: actions/upload-artifact@v4 136 | with: 137 | name: noods-vita 138 | path: noods.vpk 139 | 140 | update-release: 141 | runs-on: ubuntu-latest 142 | needs: [build-windows, build-mac, build-linux, build-android, build-switch, build-wiiu, build-vita] 143 | 144 | steps: 145 | - name: Delete old release 146 | uses: dev-drprasad/delete-tag-and-release@v0.2.1 147 | with: 148 | delete_release: true 149 | tag_name: release 150 | env: 151 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 152 | - name: Get artifacts 153 | uses: actions/download-artifact@v4 154 | - name: Package artifacts 155 | run: for i in ./*; do zip -r -j ${i}.zip $i; done 156 | - name: Create new release 157 | uses: ncipollo/release-action@v1 158 | with: 159 | name: Rolling Release 160 | body: These are automatically updated builds of the latest commit. 161 | artifacts: "*.zip" 162 | tag: release 163 | token: ${{ secrets.GITHUB_TOKEN }} 164 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /build-android 3 | /build-switch 4 | /build-vita 5 | /build-wiiu 6 | /.gradle 7 | /src/android/.cxx 8 | /eboot.bin 9 | /noods 10 | /noods.aab 11 | /NooDS.app 12 | /NooDS.dmg 13 | /noods.elf 14 | /noods.nacp 15 | /noods.nro 16 | /noods.rpx 17 | /noods.velf 18 | /noods.vpk 19 | /noods.wuhb 20 | /noods.ini 21 | /param.sfo 22 | bios7.bin 23 | bios9.bin 24 | firmware.bin 25 | gba_bios.bin 26 | sd.img 27 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | NooDS 9 | CFBundleGetInfoString 10 | 11 | CFBundleIconFile 12 | NooDS.icns 13 | CFBundleIdentifier 14 | com.github.hydr8gon.NooDS 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | NooDS 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | CFBundleShortVersionString 26 | 1.0 27 | CSResourcesFileMapped 28 | 29 | NSHighResolutionCapable 30 | 31 | NSHumanReadableCopyright 32 | Licensed under GPLv3 33 | NSSupportsAutomaticGraphicsSwitching 34 | 35 | NSPrincipalClass 36 | wxNSApplication 37 | NSMicrophoneUsageDescription 38 | This app requires microphone access to emulate the DS microphone. 39 | CFBundleDocumentTypes 40 | 41 | 42 | CFBundleTypeExtensions 43 | 44 | nds 45 | srl 46 | 47 | CFBundleTypeName 48 | Nintendo DS ROM Image 49 | CFBundleTypeRole 50 | Viewer 51 | 52 | 53 | CFBundleTypeExtensions 54 | 55 | gba 56 | 57 | CFBundleTypeName 58 | Game Boy Advance ROM Image 59 | CFBundleTypeRole 60 | Viewer 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME := noods 2 | BUILD := build 3 | SRCS := src src/common src/desktop 4 | ARGS := -Ofast -flto -std=c++11 -DUSE_GL_CANVAS -DLOG_LEVEL=0 5 | LIBS := $(shell pkg-config --libs portaudio-2.0) 6 | INCS := $(shell pkg-config --cflags portaudio-2.0) 7 | 8 | APPNAME := NooDS 9 | PKGNAME := com.hydra.noods 10 | DESTDIR ?= /usr 11 | 12 | ifeq ($(OS),Windows_NT) 13 | ARGS += -static -DWINDOWS 14 | LIBS += $(shell wx-config-static --libs --gl-libs) -lole32 -lsetupapi -lwinmm 15 | INCS += $(shell wx-config-static --cxxflags) 16 | else 17 | LIBS += $(shell wx-config --libs --gl-libs) 18 | INCS += $(shell wx-config --cxxflags) 19 | ifeq ($(shell uname -s),Darwin) 20 | ARGS += -DMACOS 21 | LIBS += -headerpad_max_install_names 22 | else 23 | ARGS += -no-pie 24 | LIBS += -lGL 25 | endif 26 | endif 27 | 28 | CPPFILES := $(foreach dir,$(SRCS),$(wildcard $(dir)/*.cpp)) 29 | HFILES := $(foreach dir,$(SRCS),$(wildcard $(dir)/*.h)) 30 | OFILES := $(patsubst %.cpp,$(BUILD)/%.o,$(CPPFILES)) 31 | 32 | ifeq ($(OS),Windows_NT) 33 | OFILES += $(BUILD)/icon-windows.o 34 | endif 35 | 36 | all: $(NAME) 37 | 38 | ifneq ($(OS),Windows_NT) 39 | ifeq ($(uname -s),Darwin) 40 | 41 | install: $(NAME) 42 | ./mac-bundle.sh 43 | cp -r $(APPNAME).app /Applications/ 44 | 45 | uninstall: 46 | rm -rf /Applications/$(APPNAME).app 47 | 48 | else 49 | 50 | flatpak: 51 | flatpak-builder --repo=repo --force-clean build-flatpak $(PKGNAME).yml 52 | flatpak build-bundle repo $(NAME).flatpak $(PKGNAME) 53 | 54 | flatpak-clean: 55 | rm -rf .flatpak-builder 56 | rm -rf build-flatpak 57 | rm -rf repo 58 | rm -f $(NAME).flatpak 59 | 60 | install: $(NAME) 61 | install -Dm755 $(NAME) "$(DESTDIR)/bin/$(NAME)" 62 | install -Dm644 $(PKGNAME).desktop "$(DESTDIR)/share/applications/$(PKGNAME).desktop" 63 | install -Dm644 icon/icon-linux.png "$(DESTDIR)/share/icons/hicolor/64x64/apps/$(PKGNAME).png" 64 | 65 | uninstall: 66 | rm -f "$(DESTDIR)/bin/$(NAME)" 67 | rm -f "$(DESTDIR)/share/applications/$(PKGNAME).desktop" 68 | rm -f "$(DESTDIR)/share/icons/hicolor/64x64/apps/$(PKGNAME).png" 69 | 70 | endif 71 | endif 72 | 73 | $(NAME): $(OFILES) 74 | g++ -o $@ $(ARGS) $^ $(LIBS) 75 | 76 | $(BUILD)/%.o: %.cpp $(HFILES) $(BUILD) 77 | g++ -c -o $@ $(ARGS) $(INCS) $< 78 | 79 | $(BUILD)/icon-windows.o: 80 | windres $(shell wx-config-static --cppflags) icon/icon-windows.rc $@ 81 | 82 | $(BUILD): 83 | for dir in $(SRCS); do mkdir -p $(BUILD)/$$dir; done 84 | 85 | android-bundle: 86 | git apply src/android/play-store.patch 87 | ./gradlew bundle 88 | git apply -R src/android/play-store.patch 89 | jarsigner -keystore keystore.jks -signedjar noods.aab build-android/outputs/bundle/release/android-release.aab keystore 90 | 91 | android: 92 | ./gradlew assembleDebug 93 | 94 | switch: 95 | $(MAKE) -f Makefile.switch 96 | 97 | wiiu: 98 | $(MAKE) -f Makefile.wiiu 99 | 100 | vita: 101 | $(MAKE) -f Makefile.vita 102 | 103 | clean: 104 | if [ -d "build-android" ]; then ./gradlew clean; fi 105 | if [ -d "build-switch" ]; then $(MAKE) -f Makefile.switch clean; fi 106 | if [ -d "build-wiiu" ]; then $(MAKE) -f Makefile.wiiu clean; fi 107 | if [ -d "build-vita" ]; then $(MAKE) -f Makefile.vita clean; fi 108 | rm -rf $(BUILD) 109 | rm -f $(NAME) 110 | -------------------------------------------------------------------------------- /Makefile.switch: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(DEVKITPRO)),) 2 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 3 | endif 4 | 5 | include $(DEVKITPRO)/libnx/switch_rules 6 | 7 | NAME := noods 8 | BUILD := build-switch 9 | SRCS := src src/common src/console 10 | DATA := src/console/images 11 | LIBS := -lglad -lEGL -lglapi -ldrm_nouveau -lnx 12 | INCS := $(PORTLIBS) $(LIBNX) 13 | 14 | APP_TITLE := NooDS 15 | APP_AUTHOR := Hydr8gon 16 | APP_VERSION := 0.1 17 | APP_ICON := ../icon/icon-switch.jpg 18 | 19 | ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE 20 | CXXFLAGS := -Ofast -flto -std=c++11 -ffunction-sections $(ARCH) $(INCLUDE) -D__SWITCH__ -DNO_FDOPEN -DLOG_LEVEL=0 21 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 22 | 23 | ifneq ($(BUILD),$(notdir $(CURDIR))) 24 | 25 | export LD := $(CXX) 26 | export OUTPUT := $(CURDIR)/$(NAME) 27 | export DEPSDIR := $(CURDIR)/$(BUILD) 28 | export LIBPATHS := $(foreach dir,$(INCS),-L$(dir)/lib) 29 | export VPATH := $(foreach dir,$(SRCS),$(CURDIR)/$(dir)) $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 30 | export INCLUDE := $(foreach dir,$(SRCS),-I$(CURDIR)/$(dir)) $(foreach dir,$(INCS),-I$(dir)/include) 31 | export NROFLAGS += --icon=$(APP_ICON) --nacp=$(CURDIR)/$(NAME).nacp 32 | 33 | CPPFILES := $(foreach dir,$(SRCS),$(notdir $(wildcard $(dir)/*.cpp))) 34 | BMPFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.bmp))) 35 | export OFILES := $(CPPFILES:.cpp=.o) $(BMPFILES:.bmp=.o) 36 | 37 | .PHONY: $(BUILD) 38 | all: $(BUILD) 39 | 40 | $(BUILD): 41 | mkdir -p $@ 42 | $(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile.switch 43 | 44 | clean: 45 | rm -rf $(BUILD) $(NAME).nro $(NAME).nacp $(NAME).elf 46 | 47 | else 48 | 49 | all: $(OUTPUT).nro 50 | $(OUTPUT).nro: $(OUTPUT).elf $(OUTPUT).nacp 51 | $(OUTPUT).elf: $(OFILES) 52 | 53 | %.o: %.bmp 54 | @echo $(notdir $<) 55 | @cd ..; $(PREFIX)ld -r -b binary -o $(BUILD)/$@ src/console/images/$*.bmp 56 | 57 | DEPENDS := $(OFILES:.o=.d) 58 | -include $(DEPENDS) 59 | 60 | endif 61 | -------------------------------------------------------------------------------- /Makefile.vita: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(VITASDK)),) 2 | $(error "Please define VITASDK to point to your SDK path!") 3 | endif 4 | 5 | NAME := noods 6 | BUILD := build-vita 7 | SRCS := src src/common src/console 8 | DATA := src/console/images 9 | ARGS := -Ofast -flto -std=c++11 -march=armv7-a -mtune=generic-armv7-a -D__VITA__ -DNO_FDOPEN -DLOG_LEVEL=0 10 | LIBS := -Wl,-q -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -lvita2d -lSceAppMgr_stub -lSceAudio_stub \ 11 | -lSceCommonDialog_stub -lSceCtrl_stub -lSceDisplay_stub -lSceGxm_stub -lSceSysmodule_stub -lSceTouch_stub \ 12 | -lScePower_stub 13 | 14 | APPNAME := NooDS 15 | TITLEID := NOODSVITA 16 | 17 | CPPFILES := $(foreach dir,$(SRCS),$(wildcard $(dir)/*.cpp)) 18 | BMPFILES := $(foreach dir,$(DATA),$(wildcard $(dir)/*.bmp)) 19 | HFILES := $(foreach dir,$(SRCS),$(wildcard $(dir)/*.h)) 20 | OFILES := $(patsubst %.cpp,$(BUILD)/%.o,$(CPPFILES)) $(patsubst %.bmp,$(BUILD)/%.o,$(BMPFILES)) 21 | 22 | CXX := $(VITASDK)/bin/arm-vita-eabi-g++ 23 | LD := $(VITASDK)/bin/arm-vita-eabi-ld 24 | PACKVPK := $(VITASDK)/bin/vita-pack-vpk 25 | MAKEFSELF := $(VITASDK)/bin/vita-make-fself 26 | MKSFOEX := $(VITASDK)/bin/vita-mksfoex 27 | ELFCREATE := $(VITASDK)/bin/vita-elf-create 28 | 29 | all: $(NAME).vpk 30 | 31 | $(NAME).vpk: eboot.bin param.sfo 32 | $(PACKVPK) -b eboot.bin -s param.sfo --add icon/icon-vita.png=sce_sys/icon0.png $@ 33 | 34 | eboot.bin: $(NAME).velf 35 | $(MAKEFSELF) -c $< $@ 36 | 37 | param.sfo: 38 | $(MKSFOEX) -s TITLE_ID="$(TITLEID)" "$(APPNAME)" $@ 39 | 40 | $(NAME).velf: $(NAME).elf 41 | $(ELFCREATE) -s $< $@ 42 | 43 | $(NAME).elf: $(OFILES) 44 | $(CXX) $^ $(LIBS) -o $@ 45 | 46 | $(BUILD)/%.o: %.cpp $(HFILES) $(BUILD) 47 | $(CXX) -c -o $@ $(ARGS) $< 48 | 49 | $(BUILD)/%.o: %.bmp $(BUILD) 50 | $(LD) -r -b binary -o $@ $< 51 | 52 | $(BUILD): 53 | for dir in $(SRCS) $(DATA); do mkdir -p $(BUILD)/$$dir; done 54 | 55 | clean: 56 | rm -f $(NAME).velf $(NAME).elf $(NAME).vpk param.sfo eboot.bin 57 | rm -rf $(BUILD) 58 | -------------------------------------------------------------------------------- /Makefile.wiiu: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(DEVKITPRO)),) 2 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 3 | endif 4 | 5 | APP_NAME := NooDS 6 | APP_SHORTNAME := NooDS 7 | APP_AUTHOR := Hydr8gon 8 | APP_ICON := ../icon/icon-vita.png 9 | 10 | include $(DEVKITPRO)/wut/share/wut_rules 11 | 12 | NAME := noods 13 | BUILD := build-wiiu 14 | SRCS := src src/common src/console 15 | DATA := src/console/images src/console/shaders 16 | LIBS := -lwut -lSDL2 17 | INCS := $(PORTLIBS) $(WUT_ROOT) 18 | 19 | CXXFLAGS := -g -Ofast -flto -ffunction-sections $(MACHDEP) $(INCLUDE) \ 20 | -D__WIIU__ -D__WUT__ -DENDIAN_BIG -DNO_FDOPEN -DLOG_LEVEL=0 21 | LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) 22 | 23 | ifneq ($(BUILD),$(notdir $(CURDIR))) 24 | 25 | export LD := $(CXX) 26 | export OUTPUT := $(CURDIR)/$(NAME) 27 | export DEPSDIR := $(CURDIR)/$(BUILD) 28 | export LIBPATHS := $(foreach dir,$(INCS),-L$(dir)/lib) 29 | export VPATH := $(foreach dir,$(SRCS),$(CURDIR)/$(dir)) $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 30 | export INCLUDE := $(foreach dir,$(SRCS),-I$(CURDIR)/$(dir)) $(foreach dir,$(INCS),-I$(dir)/include) 31 | 32 | CPPFILES := $(foreach dir,$(SRCS),$(notdir $(wildcard $(dir)/*.cpp))) 33 | BMPFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.bmp))) 34 | GSHFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.gsh))) 35 | export OFILES := $(CPPFILES:.cpp=.o) $(BMPFILES:.bmp=.o) $(addsuffix .o,$(GSHFILES)) 36 | 37 | .PHONY: $(BUILD) 38 | all: $(BUILD) 39 | 40 | $(BUILD): 41 | mkdir -p $@ 42 | $(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile.wiiu 43 | 44 | build-shader: 45 | cd src/console/shaders; ./latte-assembler compile shader_wiiu.gsh --vsh shader_wiiu.vsh --psh shader_wiiu.psh 46 | 47 | clean: 48 | rm -rf $(BUILD) $(NAME).wuhb $(NAME).rpx $(NAME).elf 49 | 50 | else 51 | 52 | all: $(OUTPUT).wuhb 53 | $(OUTPUT).wuhb: $(OUTPUT).rpx 54 | $(OUTPUT).rpx: $(OUTPUT).elf 55 | $(OUTPUT).elf: $(OFILES) 56 | 57 | %.o: %.bmp 58 | @echo $(notdir $<) 59 | @cd ..; $(PREFIX)ld -r -b binary -o $(BUILD)/$@ src/console/images/$*.bmp 60 | 61 | %.gsh.o: %.gsh 62 | @echo $(notdir $<) 63 | @$(bin2o) 64 | 65 | DEPENDS := $(OFILES:.o=.d) 66 | -include $(DEPENDS) 67 | 68 | endif 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NooDS 2 | A (hopefully!) speedy DS emulator. 3 | 4 | ### Overview 5 | The goal of NooDS is to be a fast and portable DS and GBA emulator. It features accurate software rendering with 6 | upscaling, and can take advantage of multiple cores for better performance. I started working on NooDS for fun and as a 7 | learning experience, and I'd like to keep it that way. If people are interested and get use out of it, that's a bonus! 8 | 9 | ### Downloads 10 | NooDS is available for Windows, macOS, Linux, Android, Switch, Wii U, and Vita. The latest builds are automatically 11 | provided via GitHub Actions, and can be downloaded from the [releases page](https://github.com/Hydr8gon/NooDS/releases). 12 | 13 | ### Usage 14 | NooDS should be able to run most things without any setup. DS BIOS and firmware files must be provided to boot the 15 | system menu, and can be dumped from a DS with [DSBF Dumper](https://archive.org/details/dsbf-dumper). The firmware must 16 | come from an original DS; DSi and 3DS dumps don't contain any boot code. A GBA BIOS file can be optionally provided, and 17 | can be dumped from many systems with [this dumper](https://github.com/mgba-emu/bios-dump). File paths can be configured 18 | in the settings menu. Save types are automatically detected, but this may not always be accurate. If you run something 19 | and it has issues with saving, the save type can be overriden in the file menu. 20 | 21 | ### Contributing 22 | This is a personal project, and I've decided to not review or accept pull requests for it. If you want to help, you can 23 | test things and report issues or provide feedback. If you can afford it, you can also donate to motivate me and allow me 24 | to spend more time on things like this. Nothing is mandatory, and I appreciate any interest in my projects, even if 25 | you're just a user! 26 | 27 | ### Building 28 | **Windows:** Install [MSYS2](https://www.msys2.org) and run the command 29 | `pacman -Syu mingw-w64-x86_64-{gcc,pkg-config,wxWidgets,portaudio,jbigkit} make` to get dependencies. Navigate to the 30 | project root directory and run `make -j$(nproc)` to start building. 31 | 32 | **macOS/Linux:** On the target system, install [wxWidgets](https://www.wxwidgets.org) and 33 | [PortAudio](https://www.portaudio.com). This can be done with the [Homebrew](https://brew.sh) package manager on macOS, 34 | or a built-in package manager on Linux. Run `make -j$(nproc)` in the project root directory to start building. 35 | 36 | **Android:** Install [Android Studio](https://developer.android.com/studio) or the command line tools. Run 37 | `./gradlew assembleDebug` in the project root directory to start building; dependencies will be installed as needed. 38 | 39 | **Switch:** Install [devkitPro](https://devkitpro.org/wiki/Getting_Started) and its `switch-dev` package. Run 40 | `make switch -j$(nproc)` in the project root directory to start building. 41 | 42 | **Wii U:** Install [devkitPro](https://devkitpro.org/wiki/Getting_Started) and its `wiiu-dev` package. Run 43 | `make wiiu -j$(nproc)` in the project root directory to start building. 44 | 45 | **Vita:** Install [Vita SDK](https://vitasdk.org) and run `make vita -j$(nproc)` in the project root directory to 46 | start building. 47 | 48 | ### Hardware References 49 | * [GBATEK](https://problemkaputt.de/gbatek.htm) - The main information source for all things DS and GBA 50 | * [GBATEK Addendum](https://melonds.kuribo64.net/board/thread.php?id=13) - A thread that aims to fill the gaps in GBATEK 51 | * [melonDS Blog](https://melonds.kuribo64.net) - Contains posts that detail various 3D quirks 52 | * [DraStic BIOS](https://drive.google.com/file/d/1dl6xgOXc892r43RzkIJKI6nikYIipzoN/view) - Reference for the DS HLE BIOS 53 | functions 54 | * [Cult of GBA BIOS](https://github.com/Cult-of-GBA/BIOS) - Reference for the GBA HLE BIOS functions 55 | 56 | ### Other Links 57 | * [Hydra's Lair](https://hydr8gon.github.io) - Blog where I may or may not write about things 58 | * [Discord Server](https://discord.gg/JbNz7y4) - A place to chat about my projects and stuff 59 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | buildDir = "build-android" 3 | 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | 9 | dependencies { 10 | classpath "com.android.tools.build:gradle:7.0.0" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /com.hydra.noods.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=NooDS 4 | Comment=A (hopefully!) speedy NDS emulator 5 | GenericName=Nintendo DS Emulator 6 | Icon=com.hydra.noods 7 | Exec=noods %U 8 | StartupNotify=true 9 | Terminal=false 10 | MimeType=application/x-nintendo-ds-rom;application/x-gba-rom 11 | Categories=Game;Emulator 12 | Keywords=emulator;nintendo;ds;nds 13 | -------------------------------------------------------------------------------- /com.hydra.noods.yml: -------------------------------------------------------------------------------- 1 | app-id: com.hydra.noods 2 | runtime: org.freedesktop.Platform 3 | runtime-version: '21.08' 4 | sdk: org.freedesktop.Sdk 5 | command: noods 6 | 7 | finish-args: 8 | - --device=all 9 | - --share=ipc 10 | - --socket=x11 11 | - --socket=pulseaudio 12 | - --filesystem=host 13 | 14 | modules: 15 | - name: wxwidgets 16 | buildsystem: cmake-ninja 17 | config-opts: 18 | - -DCMAKE_BUILD_TYPE=Release 19 | sources: 20 | - type: git 21 | url: https://github.com/wxWidgets/wxWidgets.git 22 | tag: v3.1.7 23 | modules: 24 | - name: glu 25 | config-opts: 26 | - --disable-static 27 | sources: 28 | - type: archive 29 | url: https://ftp.osuosl.org/pub/blfs/conglomeration/glu/glu-9.0.2.tar.xz 30 | sha256: 6e7280ff585c6a1d9dfcdf2fca489251634b3377bfc33c29e4002466a38d02d4 31 | cleanup: 32 | - /include 33 | - /lib/*.a 34 | - /lib/*.la 35 | - /lib/pkgconfig 36 | cleanup: 37 | - /bin 38 | - /include 39 | - /lib/wx/include 40 | 41 | - name: portaudio 42 | config-opts: 43 | - --disable-static 44 | - --without-oss 45 | - --without-jack 46 | sources: 47 | - type: git 48 | url: https://github.com/PortAudio/portaudio.git 49 | tag: v19.7.0 50 | cleanup: 51 | - /include 52 | - /lib/*.la 53 | - /lib/pkgconfig 54 | 55 | - name: noods 56 | buildsystem: simple 57 | build-commands: 58 | - DESTDIR=/app make install 59 | sources: 60 | - type: git 61 | url: https://github.com/Hydr8gon/NooDS.git 62 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /icon/icon-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/icon/icon-linux.png -------------------------------------------------------------------------------- /icon/icon-mac.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/icon/icon-mac.icns -------------------------------------------------------------------------------- /icon/icon-switch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/icon/icon-switch.jpg -------------------------------------------------------------------------------- /icon/icon-vita.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/icon/icon-vita.png -------------------------------------------------------------------------------- /icon/icon-windows.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/icon/icon-windows.ico -------------------------------------------------------------------------------- /icon/icon-windows.rc: -------------------------------------------------------------------------------- 1 | #define wxUSE_RC_MANIFEST 1 2 | #define wxUSE_DPI_AWARE_MANIFEST 1 3 | #include 4 | 5 | NooDS ICON "icon-windows.ico" 6 | -------------------------------------------------------------------------------- /icon/icon.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | const char *icon_xpm[] = 3 | { 4 | "64 64 9 1 ", 5 | " c None", 6 | ". c gray1", 7 | "X c #202020", 8 | "o c #2A2A2A", 9 | "O c #412910", 10 | "+ c #A47B41", 11 | "@ c #FFA4A4", 12 | "# c #E7DED8", 13 | "$ c white", 14 | 15 | " ", 16 | " ", 17 | " ", 18 | " ", 19 | " OOOOOOOOOOOOOOOO ", 20 | " OOOOOOOOOOOOOOOO ", 21 | " OOOO++++++++++++++++OOOO ", 22 | " OOOO++++++++++++++++OOOO ", 23 | " OO++++++++++++++++++++++++OOOO.................... ", 24 | " OO++++++++++++++++++++++++OOOO.................... ", 25 | " OO++++++++OOOOOOOOOOOO++++++++++OOoooooooooooooooooo.. ", 26 | " OO++++++++OOOOOOOOOOOO++++++++++OOoooooooooooooooooo.. ", 27 | " OO++++++OOOOoooo........OO++++..++OO........oooooooooo.. ", 28 | " OO++++++OOOOoooo........OO++++..++OO........oooooooooo.. ", 29 | " OO++++OOoooooooo..$$$$$$OO##++++++++OO$$$$..oooooooooo.. ", 30 | " OO++++OOoooooooo..$$$$$$OO##++++++++OO$$$$..oooooooooo.. ", 31 | "OO++++++OOoooooooo..$$$$$$$$OO####++++OO$$$$..oooooooooo.. ", 32 | "OO++++++OOoooooooo..$$$$$$$$OO####++++OO$$$$..oooooooooo.. ", 33 | "OO++++OOoooooooooo..$$$$$$$$$$OOOO##++OO$$$$..oooooooooo.. ", 34 | "OO++++OOoooooooooo..$$$$$$$$$$OOOO##++OO$$$$..oooooooooo.. ", 35 | "OO++++OOoooooooooo..$$$$$$$$$$$$$$OOOO@@$$$$..oooooooooo.. ", 36 | "OO++++OOoooooooooo..$$$$$$$$$$$$$$OOOO@@$$$$..oooooooooo.. ", 37 | "OO++++OOoooooooooo..$$$$$$$$$$$$$$$$$$$$@@$$..oooooooooo.. ", 38 | "OO++++OOoooooooooo..$$$$$$$$$$$$$$$$$$$$@@$$..oooooooooo.. ", 39 | "OO++++..oooooooooo..$$$$$$$$$$$$$$$$$$$$@@$$..oooooooooo.. ", 40 | "OO++++..oooooooooo..$$$$$$$$$$$$$$$$$$$$@@$$..oooooooooo.. ", 41 | " OO++..oooooooooo..$$$$$$$$$$$$$$$$$$$$$$$$..oooooooooo.. ", 42 | " OO++..oooooooooo..$$$$$$$$$$$$$$$$$$$$$$$$..oooooooooo.. ", 43 | " OO..oooooooooo............................oooooooooo.. ", 44 | " OO..oooooooooo............................oooooooooo.. ", 45 | " ..oooooooooooooooooooooooooooooooooooooooooooooooo..OO ", 46 | " ..oooooooooooooooooooooooooooooooooooooooooooooooo..OO ", 47 | " ................................................++++OO ", 48 | " ................................................++++OO ", 49 | " ....................................................++++OO", 50 | " ....................................................++++OO", 51 | " ..ooooooooooooooooooooooooooooooooooooooooooooooooOO++++OO", 52 | " ..ooooooooooooooooooooooooooooooooooooooooooooooooOO++++OO", 53 | " ..oooooooooo............................ooooooooooOO++++OO", 54 | " ..oooooooooo............................ooooooooooOO++++OO", 55 | " ..oooooooooooo..$$$$$$$$$$$$$$$$$$$$$$$$..ooooooooOO++++++OO", 56 | " ..oooooooooooo..$$$$$$$$$$$$$$$$$$$$$$$$..ooooooooOO++++++OO", 57 | " ..oooooooooo..$$$$$$$$$$$$$$$$$$$$$$$$$$$$..ooOOOO++++++++OO", 58 | " ..oooooooooo..$$$$$$$$$$$$$$$$$$$$$$$$$$$$..ooOOOO++++++++OO", 59 | " ..oooooooooo..$$$$$$$$$$$$$$$$$$$$$$$$$$$$OOOO++++++++++OO ", 60 | " ..oooooooooo..$$$$$$$$$$$$$$$$$$$$$$$$$$$$OOOO++++++++++OO ", 61 | " ..oooooooooooo........................OOOOOO++++++++++++OO.. ", 62 | " ..oooooooooooo........................OOOOOO++++++++++++OO.. ", 63 | " ..ooooooooooooooooooooooooooooooooOOOO++++++++++++++OOOOoo.. ", 64 | " ..ooooooooooooooooooooooooooooooooOOOO++++++++++++++OOOOoo.. ", 65 | " ............................OOOOOO++++++++++++OOOOOO........ ", 66 | " ............................OOOOOO++++++++++++OOOOOO........ ", 67 | " ..XXXXXXXXXXXXXXXXXXXXXXOOOO++++++++++++OOOOOOXXXXXXXXXXXX.. ", 68 | " ..XXXXXXXXXXXXXXXXXXXXXXOOOO++++++++++++OOOOOOXXXXXXXXXXXX.. ", 69 | " ..................OOOO++++++++OOOOOOOO.................. ", 70 | " ..................OOOO++++++++OOOOOOOO.................. ", 71 | " OO++++OOOOOOOO ", 72 | " OO++++OOOOOOOO ", 73 | " OOOOOOOO ", 74 | " OOOOOOOO ", 75 | " ", 76 | " ", 77 | " ", 78 | " " 79 | }; 80 | -------------------------------------------------------------------------------- /keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/keystore.jks -------------------------------------------------------------------------------- /mac-bundle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | app=NooDS.app 7 | contents=$app/Contents 8 | 9 | if [[ ! -f noods ]]; then 10 | echo 'Error: NooDS binary was not found.' 11 | echo 'Please run `make` to compile NooDS before bundling.' 12 | exit 1 13 | fi 14 | 15 | if [[ -d "$app" ]]; then 16 | rm -rf "$app" 17 | fi 18 | 19 | install -dm755 "${contents}"/{MacOS,Resources,Frameworks} 20 | install -sm755 noods "${contents}/MacOS/NooDS" 21 | install -m644 Info.plist "$contents/Info.plist" 22 | 23 | # macOS does not have the -f flag for readlink 24 | abspath() { 25 | perl -MCwd -le 'print Cwd::abs_path shift' "$1" 26 | } 27 | 28 | # Recursively copy dependent libraries to the Frameworks directory 29 | # and fix their load paths 30 | fixup_libs() { 31 | local libs=($(otool -L "$1" | grep -vE "/System|/usr/lib|:$" | sed -E 's/'$'\t''(.*) \(.*$/\1/')) 32 | 33 | for lib in "${libs[@]}"; do 34 | # Dereference symlinks to get the actual .dylib as binaries' load 35 | # commands can contain paths to symlinked libraries. 36 | local abslib="$(abspath "$lib")" 37 | local base="$(basename "$abslib")" 38 | local install_path="$contents/Frameworks/$base" 39 | 40 | install_name_tool -change "$lib" "@rpath/$base" "$1" 41 | 42 | if [[ ! -f "$install_path" ]]; then 43 | install -m644 "$abslib" "$install_path" 44 | strip -Sx "$install_path" 45 | fixup_libs "$install_path" 46 | fi 47 | done 48 | } 49 | 50 | install_name_tool -add_rpath "@executable_path/../Frameworks" $contents/MacOS/NooDS 51 | 52 | fixup_libs $contents/MacOS/NooDS 53 | 54 | cp "icon/icon-mac.icns" "$contents/Resources/NooDS.icns" 55 | 56 | codesign --deep -s - NooDS.app 57 | 58 | if [[ $1 == '--dmg' ]]; then 59 | mkdir build/dmg 60 | cp -a NooDS.app build/dmg/ 61 | ln -s /Applications build/dmg/Applications 62 | hdiutil create -volname NooDS -srcfolder build/dmg -ov -format UDBZ NooDS.dmg 63 | rm -r build/dmg 64 | fi 65 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':src:android' 2 | rootProject.name = 'NooDS' 3 | -------------------------------------------------------------------------------- /src/action_replay.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | class Core; 28 | 29 | struct ARCheat { 30 | std::string name; 31 | std::vector code; 32 | bool enabled; 33 | }; 34 | 35 | class ActionReplay { 36 | public: 37 | std::vector cheats; 38 | 39 | ActionReplay(Core *core): core(core) {} 40 | void setPath(std::string path); 41 | void setFd(int fd); 42 | 43 | bool loadCheats(); 44 | bool saveCheats(); 45 | void applyCheats(); 46 | 47 | private: 48 | Core *core; 49 | std::mutex mutex; 50 | std::string path; 51 | int fd = -1; 52 | 53 | FILE *openFile(const char *mode); 54 | }; 55 | -------------------------------------------------------------------------------- /src/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | 36 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DLOG_LEVEL=0") 4 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -flto") 5 | 6 | add_library(noods-core SHARED 7 | cpp/interface.cpp 8 | ../common/nds_icon.cpp 9 | ../common/screen_layout.cpp 10 | ../action_replay.cpp 11 | ../cartridge.cpp 12 | ../core.cpp 13 | ../cp15.cpp 14 | ../div_sqrt.cpp 15 | ../dldi.cpp 16 | ../dma.cpp 17 | ../gpu.cpp 18 | ../gpu_2d.cpp 19 | ../gpu_3d.cpp 20 | ../gpu_3d_renderer.cpp 21 | ../hle_arm7.cpp 22 | ../hle_bios.cpp 23 | ../input.cpp 24 | ../interpreter.cpp 25 | ../interpreter_alu.cpp 26 | ../interpreter_branch.cpp 27 | ../interpreter_lookup.cpp 28 | ../interpreter_transfer.cpp 29 | ../ipc.cpp 30 | ../memory.cpp 31 | ../rtc.cpp 32 | ../save_states.cpp 33 | ../settings.cpp 34 | ../spi.cpp 35 | ../spu.cpp 36 | ../timers.cpp 37 | ../wifi.cpp) 38 | 39 | target_link_libraries(noods-core jnigraphics OpenSLES) 40 | -------------------------------------------------------------------------------- /src/android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | android { 4 | buildDir = "../../build-android" 5 | android.ndkVersion "22.1.7171670" 6 | compileSdkVersion 34 7 | 8 | sourceSets { 9 | main { 10 | manifest.srcFile "AndroidManifest.xml" 11 | java.srcDirs = ["java"] 12 | res.srcDirs = ["res"] 13 | } 14 | } 15 | 16 | defaultConfig { 17 | applicationId "com.hydra.noods" 18 | minSdkVersion 21 19 | targetSdkVersion 34 20 | versionCode "git rev-list HEAD --count".execute().text.toInteger() 21 | versionName "0.1" 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 28 | } 29 | } 30 | 31 | externalNativeBuild { 32 | cmake { 33 | path "CMakeLists.txt" 34 | version "3.10.2" 35 | } 36 | } 37 | } 38 | 39 | dependencies { 40 | implementation 'androidx.appcompat:appcompat:1.1.0' 41 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 42 | implementation 'androidx.preference:preference:1.1.0' 43 | implementation 'androidx.documentfile:documentfile:1.0.1' 44 | implementation 'com.google.android.material:material:1.5.0' 45 | } 46 | -------------------------------------------------------------------------------- /src/android/java/com/hydra/noods/BindingsMenu.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | package com.hydra.noods; 21 | 22 | import androidx.appcompat.app.AppCompatActivity; 23 | import androidx.preference.PreferenceFragmentCompat; 24 | 25 | import android.os.Bundle; 26 | 27 | public class BindingsMenu extends AppCompatActivity { 28 | public static class BindingsFragment extends PreferenceFragmentCompat { 29 | @Override 30 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 31 | setPreferencesFromResource(R.xml.bindings, rootKey); 32 | } 33 | } 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | 39 | // Populate the input bindings UI 40 | getSupportFragmentManager().beginTransaction() 41 | .add(android.R.id.content, new BindingsFragment()) 42 | .commit(); 43 | } 44 | 45 | @Override 46 | public void onBackPressed() { 47 | // Save the bindings and return to the previous activity 48 | SettingsMenu.saveSettings(); 49 | finish(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/android/java/com/hydra/noods/BindingsPreference.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | package com.hydra.noods; 21 | 22 | import androidx.appcompat.app.AlertDialog; 23 | import androidx.preference.Preference; 24 | 25 | import android.content.Context; 26 | import android.content.DialogInterface; 27 | import android.util.AttributeSet; 28 | import android.view.KeyEvent; 29 | 30 | public class BindingsPreference extends Preference { 31 | private Context context; 32 | private int index; 33 | 34 | public BindingsPreference(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | this.context = context; 37 | index = Integer.parseInt(attrs.getAttributeValue(null, "index")); 38 | 39 | // Set the subtext based on the binding for the key index 40 | int value = getKeyBind(index); 41 | if (value == 0) 42 | setSummary("None"); 43 | else 44 | setSummary("Input " + Integer.toString(value - 1)); 45 | } 46 | 47 | @Override 48 | protected void onClick() { 49 | // Create the dialog for rebinding an input 50 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 51 | builder.setTitle(getTitle()); 52 | builder.setMessage("Press a key to bind it to this input."); 53 | builder.setNegativeButton("Cancel", null); 54 | 55 | builder.setPositiveButton("Clear", new DialogInterface.OnClickListener() { 56 | @Override 57 | public void onClick(DialogInterface dialog, int id) { 58 | // Clear the binding for the key index 59 | setKeyBind(index, 0); 60 | setSummary("None"); 61 | } 62 | }); 63 | 64 | builder.setOnKeyListener(new DialogInterface.OnKeyListener() { 65 | @Override 66 | public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { 67 | // Set the binding for the key index 68 | setKeyBind(index, keyCode + 1); 69 | setSummary("Input " + Integer.toString(keyCode)); 70 | dialog.dismiss(); 71 | return true; 72 | } 73 | }); 74 | 75 | builder.create().show(); 76 | } 77 | 78 | public static native int getKeyBind(int index); 79 | public static native void setKeyBind(int index, int value); 80 | } 81 | -------------------------------------------------------------------------------- /src/android/java/com/hydra/noods/FileAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | package com.hydra.noods; 21 | 22 | import android.content.Context; 23 | import android.graphics.Bitmap; 24 | import android.net.Uri; 25 | import android.view.LayoutInflater; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | import android.widget.BaseAdapter; 29 | import android.widget.ImageView; 30 | import android.widget.TextView; 31 | 32 | import java.util.ArrayList; 33 | 34 | public class FileAdapter extends BaseAdapter { 35 | public class FileInfo { 36 | public String name; 37 | public Bitmap icon; 38 | public Uri uri; 39 | } 40 | 41 | private Context context; 42 | private ArrayList info; 43 | 44 | public FileAdapter(Context context) { 45 | this.context = context; 46 | } 47 | 48 | public void setInfo(ArrayList info) { 49 | this.info = info; 50 | } 51 | 52 | @Override 53 | public int getCount() { 54 | return info.size(); 55 | } 56 | 57 | @Override 58 | public Object getItem(int position) { 59 | return null; 60 | } 61 | 62 | @Override 63 | public long getItemId(int position) { 64 | return position; 65 | } 66 | 67 | @Override 68 | public View getView(int position, View convertView, ViewGroup parent) { 69 | // Create a new file row 70 | if (convertView == null) { 71 | LayoutInflater inflater = LayoutInflater.from(context); 72 | convertView = inflater.inflate(R.layout.file_row, parent, false); 73 | } 74 | 75 | // Set the file title and icon 76 | TextView title = (TextView)convertView.findViewById(R.id.name); 77 | title.setText(info.get(position).name); 78 | ImageView icon = (ImageView)convertView.findViewById(R.id.icon); 79 | icon.setImageBitmap(info.get(position).icon); 80 | return convertView; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/android/java/com/hydra/noods/NooButton.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | package com.hydra.noods; 21 | 22 | import android.content.Context; 23 | import android.os.Vibrator; 24 | import android.util.AttributeSet; 25 | import android.view.MotionEvent; 26 | import android.view.View; 27 | import android.view.ViewGroup.LayoutParams; 28 | import android.widget.Button; 29 | 30 | public class NooButton extends Button { 31 | private Vibrator vibrator; 32 | private int[] resIds; 33 | private int[] btnIds; 34 | private int state = 0; 35 | private int lastState = 0; 36 | 37 | public static int[] resAbxy = { 38 | R.drawable.abxy, R.drawable.abxy_pressed1, R.drawable.abxy_pressed5, R.drawable.abxy, 39 | R.drawable.abxy_pressed7, R.drawable.abxy_pressed8, R.drawable.abxy_pressed6, R.drawable.abxy, 40 | R.drawable.abxy_pressed3, R.drawable.abxy_pressed2, R.drawable.abxy_pressed4, R.drawable.abxy, 41 | R.drawable.abxy, R.drawable.abxy, R.drawable.abxy, R.drawable.abxy 42 | }; 43 | 44 | public static int[] resDpad = { 45 | R.drawable.dpad, R.drawable.dpad_pressed1, R.drawable.dpad_pressed5, R.drawable.dpad, 46 | R.drawable.dpad_pressed7, R.drawable.dpad_pressed8, R.drawable.dpad_pressed6, R.drawable.dpad, 47 | R.drawable.dpad_pressed3, R.drawable.dpad_pressed2, R.drawable.dpad_pressed4, R.drawable.dpad, 48 | R.drawable.dpad, R.drawable.dpad, R.drawable.dpad, R.drawable.dpad 49 | }; 50 | 51 | public NooButton(Context context, int[] resIds, int[] btnIds, int x, int y, int width, int height) { 52 | super(context); 53 | vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); 54 | this.resIds = resIds; 55 | this.btnIds = btnIds; 56 | 57 | // Set the button parameters 58 | setBackgroundResource(resIds[0]); 59 | setX(x); 60 | setY(y); 61 | setLayoutParams(new LayoutParams(width, height)); 62 | setAlpha(0.5f); 63 | 64 | // Handle button touches 65 | if (btnIds.length >= 4) { // D-pad 66 | setOnTouchListener(new Button.OnTouchListener() { 67 | @Override 68 | public boolean onTouch(View view, MotionEvent event) { 69 | switch (event.getAction()) { 70 | case MotionEvent.ACTION_DOWN: 71 | case MotionEvent.ACTION_MOVE: 72 | // Press the right key if in range, otherwise release 73 | if (event.getX() > view.getWidth() * 2 / 3) 74 | pressDKey(0); 75 | else 76 | releaseKey(btnIds[0]); 77 | 78 | // Press the left key if in range, otherwise release 79 | if (event.getX() < view.getWidth() * 1 / 3) 80 | pressDKey(1); 81 | else 82 | releaseKey(btnIds[1]); 83 | 84 | // Press the up key if in range, otherwise release 85 | if (event.getY() < view.getHeight() * 1 / 3) 86 | pressDKey(2); 87 | else 88 | releaseKey(btnIds[2]); 89 | 90 | // Press the down key if in range, otherwise release 91 | if (event.getY() > view.getHeight() * 2 / 3) 92 | pressDKey(3); 93 | else 94 | releaseKey(btnIds[3]); 95 | 96 | // Update the image and vibrate 97 | if (state != lastState) 98 | setBackgroundResource(resIds[state]); 99 | if ((state & ~lastState) != 0) 100 | vibrator.vibrate(SettingsMenu.getVibrateStrength() * 4); 101 | lastState = state; 102 | state = 0; 103 | break; 104 | 105 | case MotionEvent.ACTION_UP: 106 | // Release all directions and update the image 107 | for (int i = 0; i < 4; i++) 108 | releaseKey(btnIds[i]); 109 | setBackgroundResource(resIds[0]); 110 | lastState = state = 0; 111 | break; 112 | } 113 | return true; 114 | } 115 | }); 116 | } 117 | else { 118 | setOnTouchListener(new Button.OnTouchListener() { 119 | @Override 120 | public boolean onTouch(View view, MotionEvent event) { 121 | switch (event.getAction()) { 122 | case MotionEvent.ACTION_DOWN: 123 | // Press a key, update the image, and vibrate 124 | pressKey(btnIds[0]); 125 | setBackgroundResource(resIds[1]); 126 | vibrator.vibrate(SettingsMenu.getVibrateStrength() * 4); 127 | break; 128 | 129 | case MotionEvent.ACTION_UP: 130 | // Release a key and update the image 131 | releaseKey(btnIds[0]); 132 | setBackgroundResource(resIds[0]); 133 | break; 134 | } 135 | return true; 136 | } 137 | }); 138 | } 139 | } 140 | 141 | private void pressDKey(int key) { 142 | // Press a D-pad key and track the state 143 | pressKey(btnIds[key]); 144 | state |= 1 << key; 145 | } 146 | 147 | public static native void pressKey(int key); 148 | public static native void releaseKey(int key); 149 | } 150 | -------------------------------------------------------------------------------- /src/android/play-store.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/android/AndroidManifest.xml b/src/android/AndroidManifest.xml 2 | index 715b448..ccce88e 100644 3 | --- a/src/android/AndroidManifest.xml 4 | +++ b/src/android/AndroidManifest.xml 5 | @@ -6,7 +6,6 @@ 6 | 7 | 8 | 9 | - 10 | 11 | 12 | 13 | diff --git a/src/android/java/com/hydra/noods/FileBrowser.java b/src/android/java/com/hydra/noods/FileBrowser.java 14 | index dffc2d6..455e759 100644 15 | --- a/src/android/java/com/hydra/noods/FileBrowser.java 16 | +++ b/src/android/java/com/hydra/noods/FileBrowser.java 17 | @@ -72,7 +72,7 @@ public class FileBrowser extends AppCompatActivity 18 | System.loadLibrary("noods-core"); 19 | } 20 | 21 | - public static final boolean PLAY_STORE = false; 22 | + public static final boolean PLAY_STORE = true; 23 | public static PorterDuffColorFilter iconFilter; 24 | 25 | private ArrayList storagePaths; 26 | diff --git a/src/android/res/menu/file_menu.xml b/src/android/res/menu/file_menu.xml 27 | index 77595be..f8963b8 100644 28 | --- a/src/android/res/menu/file_menu.xml 29 | +++ b/src/android/res/menu/file_menu.xml 30 | @@ -5,7 +5,6 @@ 31 | 36 | 37 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/android/res/drawable-v26/icon_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable-v26/icon_background.png -------------------------------------------------------------------------------- /src/android/res/drawable-v26/icon_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable-v26/icon_foreground.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy_pressed1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy_pressed1.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy_pressed2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy_pressed2.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy_pressed3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy_pressed3.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy_pressed4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy_pressed4.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy_pressed5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy_pressed5.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy_pressed6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy_pressed6.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy_pressed7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy_pressed7.png -------------------------------------------------------------------------------- /src/android/res/drawable/abxy_pressed8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/abxy_pressed8.png -------------------------------------------------------------------------------- /src/android/res/drawable/bindings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/bindings.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad_pressed1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad_pressed1.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad_pressed2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad_pressed2.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad_pressed3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad_pressed3.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad_pressed4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad_pressed4.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad_pressed5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad_pressed5.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad_pressed6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad_pressed6.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad_pressed7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad_pressed7.png -------------------------------------------------------------------------------- /src/android/res/drawable/dpad_pressed8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/dpad_pressed8.png -------------------------------------------------------------------------------- /src/android/res/drawable/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/file.png -------------------------------------------------------------------------------- /src/android/res/drawable/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/folder.png -------------------------------------------------------------------------------- /src/android/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/icon.png -------------------------------------------------------------------------------- /src/android/res/drawable/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/info.png -------------------------------------------------------------------------------- /src/android/res/drawable/l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/l.png -------------------------------------------------------------------------------- /src/android/res/drawable/l_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/l_pressed.png -------------------------------------------------------------------------------- /src/android/res/drawable/r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/r.png -------------------------------------------------------------------------------- /src/android/res/drawable/r_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/r_pressed.png -------------------------------------------------------------------------------- /src/android/res/drawable/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/select.png -------------------------------------------------------------------------------- /src/android/res/drawable/select_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/select_pressed.png -------------------------------------------------------------------------------- /src/android/res/drawable/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/settings.png -------------------------------------------------------------------------------- /src/android/res/drawable/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/start.png -------------------------------------------------------------------------------- /src/android/res/drawable/start_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/start_pressed.png -------------------------------------------------------------------------------- /src/android/res/drawable/storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/android/res/drawable/storage.png -------------------------------------------------------------------------------- /src/android/res/layout/file_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/android/res/menu/file_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/android/res/menu/noo_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 10 | 11 | 13 | 14 | 16 | 17 | 19 | 20 | 22 | 23 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/android/res/menu/settings_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/android/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | None 6 | 1 Frame 7 | 2 Frames 8 | 3 Frames 9 | 4 Frames 10 | 5 Frames 11 | 12 | 13 | 14 | Disabled 15 | 1 Thread 16 | 2 Threads 17 | 3 Threads 18 | 4 Threads 19 | 20 | 21 | 22 | Center 23 | Top 24 | Bottom 25 | Left 26 | Right 27 | 28 | 29 | 30 | None 31 | Clockwise 32 | Counter-Clockwise 33 | 34 | 35 | 36 | Automatic 37 | Vertical 38 | Horizontal 39 | Single Screen 40 | 41 | 42 | 43 | Even 44 | Enlarge Top 45 | Enlarge Bottom 46 | 47 | 48 | 49 | None 50 | Quarter 51 | Half 52 | Full 53 | 54 | 55 | 56 | Nearest 57 | Upscaled 58 | Linear 59 | 60 | 61 | 62 | Default 63 | 16:10 64 | 16:9 65 | 18:9 66 | 67 | 68 | 69 | 0 70 | 1 71 | 2 72 | 3 73 | 4 74 | 5 75 | 76 | 77 | 78 | None 79 | EEPROM 0.5KB 80 | EEPROM 8KB 81 | SRAM 32KB 82 | FLASH 64KB 83 | FLASH 128KB 84 | 85 | 86 | 87 | 0 88 | 0x200 89 | 0x2000 90 | 0x8000 91 | 0x10000 92 | 0x20000 93 | 94 | 95 | 96 | None 97 | EEPROM 0.5KB 98 | EEPROM 8KB 99 | EEPROM 64KB 100 | EEPROM 128KB 101 | FRAM 32KB 102 | FLASH 256KB 103 | FLASH 512KB 104 | FLASH 1024KB 105 | FLASH 8192KB 106 | 107 | 108 | 109 | 0 110 | 0x200 111 | 0x2000 112 | 0x10000 113 | 0x20000 114 | 0x8000 115 | 0x40000 116 | 0x80000 117 | 0x100000 118 | 0x800000 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/android/res/xml/bindings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 23 | 24 | 31 | 32 | 39 | 40 | 47 | 48 | 55 | 56 | 63 | 64 | 71 | 72 | 79 | 80 | 87 | 88 | 95 | 96 | 103 | 104 | 105 | 108 | 109 | 116 | 117 | 124 | 125 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/cartridge.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "defines.h" 28 | 29 | class Core; 30 | 31 | enum NdsCmdMode { 32 | CMD_NONE = 0, 33 | CMD_HEADER, 34 | CMD_CHIP, 35 | CMD_SECURE, 36 | CMD_DATA 37 | }; 38 | 39 | class Cartridge { 40 | public: 41 | Cartridge(Core *core): core(core) {} 42 | ~Cartridge(); 43 | 44 | bool setRom(std::string romPath, int romFd = -1, int saveFd = -1, int stateFd = -1, int cheatFd = -1); 45 | void writeSave(); 46 | 47 | void trimRom(); 48 | void resizeSave(int newSize, bool dirty = true); 49 | 50 | int getRomSize() { return romSize; } 51 | int getSaveSize() { return saveSize; } 52 | 53 | protected: 54 | Core *core; 55 | 56 | FILE *romFile = nullptr; 57 | uint8_t *rom = nullptr, *save = nullptr; 58 | int romSize = 0, saveSize = -1; 59 | bool saveDirty = false; 60 | std::mutex mutex; 61 | 62 | std::vector saveSizes; 63 | uint32_t romMask = 0; 64 | 65 | virtual bool loadRom(); 66 | void loadRomSection(size_t offset, size_t size); 67 | 68 | private: 69 | std::string romPath, savePath; 70 | int romFd = -1, saveFd = -1; 71 | }; 72 | 73 | class CartridgeNds: public Cartridge { 74 | public: 75 | CartridgeNds(Core *core): Cartridge(core) {} 76 | void saveState(FILE *file); 77 | void loadState(FILE *file); 78 | 79 | void directBoot(); 80 | void wordReady(bool cpu); 81 | 82 | uint16_t readAuxSpiCnt(bool cpu) { return auxSpiCnt[cpu]; } 83 | uint8_t readAuxSpiData(bool cpu) { return auxSpiData[cpu]; } 84 | uint32_t readRomCtrl(bool cpu) { return romCtrl[cpu]; } 85 | uint32_t readRomDataIn(bool cpu); 86 | 87 | void writeAuxSpiCnt(bool cpu, uint16_t mask, uint16_t value); 88 | void writeAuxSpiData(bool cpu, uint8_t value); 89 | void writeRomCtrl(bool cpu, uint32_t mask, uint32_t value); 90 | void writeRomCmdOutL(bool cpu, uint32_t mask, uint32_t value); 91 | void writeRomCmdOutH(bool cpu, uint32_t mask, uint32_t value); 92 | 93 | private: 94 | uint32_t romCode = 0; 95 | bool romEncrypted = false; 96 | NdsCmdMode cmdMode = CMD_NONE; 97 | 98 | uint32_t encTable[0x412] = {}; 99 | uint32_t encCode[3] = {}; 100 | 101 | uint32_t romAddrReal[2] = {}, romAddrVirt[2] = {}; 102 | uint16_t blockSize[2] = {}, readCount[2] = {}; 103 | uint32_t wordCycles[2] = {}; 104 | bool encrypted[2] = {}; 105 | 106 | uint8_t auxCommand[2] = {}; 107 | uint32_t auxAddress[2] = {}; 108 | uint32_t auxWriteCount[2] = {}; 109 | 110 | uint16_t auxSpiCnt[2] = {}; 111 | uint8_t auxSpiData[2] = {}; 112 | uint32_t romCtrl[2] = {}; 113 | uint64_t romCmdOut[2] = {}; 114 | 115 | virtual bool loadRom(); 116 | 117 | uint64_t encrypt64(uint64_t value); 118 | uint64_t decrypt64(uint64_t value); 119 | void initKeycode(int level); 120 | void applyKeycode(); 121 | }; 122 | 123 | class CartridgeGba: public Cartridge { 124 | public: 125 | CartridgeGba(Core *core): Cartridge(core) {} 126 | void saveState(FILE *file); 127 | void loadState(FILE *file); 128 | 129 | uint8_t *getRom(uint32_t address); 130 | bool isEeprom(uint32_t address); 131 | 132 | uint8_t eepromRead(); 133 | void eepromWrite(uint8_t value); 134 | 135 | uint8_t sramRead(uint32_t address); 136 | void sramWrite(uint32_t address, uint8_t value); 137 | 138 | private: 139 | uint8_t eepromCount = 0; 140 | uint16_t eepromCmd = 0; 141 | uint64_t eepromData = 0; 142 | bool eepromDone = false; 143 | 144 | uint8_t flashCmd = 0; 145 | bool bankSwap = false; 146 | bool flashErase = false; 147 | 148 | bool findString(std::string string); 149 | virtual bool loadRom(); 150 | }; 151 | 152 | FORCE_INLINE uint8_t *CartridgeGba::getRom(uint32_t address) { 153 | return ((address &= romMask) < romSize) ? &rom[address] : nullptr; 154 | } 155 | 156 | FORCE_INLINE bool CartridgeGba::isEeprom(uint32_t address) { 157 | return (saveSize == -1 || saveSize == 0x200 || saveSize == 0x2000) && (romSize <= 0x1000000 || address >= 0x0DFFFF00); 158 | } 159 | -------------------------------------------------------------------------------- /src/common/nds_icon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | #include "nds_icon.h" 23 | #include "../defines.h" 24 | 25 | NdsIcon::NdsIcon(std::string path, int fd) { 26 | // Attempt to open the ROM 27 | FILE *rom = (fd == -1) ? fopen(path.c_str(), "rb") : fdopen(fd, "rb"); 28 | 29 | // Create an empty icon if ROM loading failed 30 | if (!rom) { 31 | LOG_WARN("Failed to open ROM for icon decoding!\n"); 32 | memset(icon, 0, 32 * 32 * sizeof(uint32_t)); 33 | return; 34 | } 35 | 36 | // Get the icon offset 37 | uint8_t offset[4]; 38 | fseek(rom, 0x68, SEEK_SET); 39 | fread(offset, sizeof(uint8_t), 4, rom); 40 | 41 | // Get the icon data 42 | uint8_t data[512]; 43 | fseek(rom, U8TO32(offset, 0) + 0x20, SEEK_SET); 44 | fread(data, sizeof(uint8_t), 512, rom); 45 | 46 | // Get the icon palette 47 | uint8_t palette[32]; 48 | fseek(rom, U8TO32(offset, 0) + 0x220, SEEK_SET); 49 | fread(palette, sizeof(uint8_t), 32, rom); 50 | fclose(rom); 51 | 52 | // Get each pixel's 5-bit palette color and convert it to 8-bit 53 | uint32_t tiles[32 * 32]; 54 | for (int i = 0; i < 32 * 32; i++) { 55 | uint8_t index = (i & 1) ? ((data[i / 2] & 0xF0) >> 4) : (data[i / 2] & 0x0F); 56 | uint16_t color = index ? U8TO16(palette, index * 2) : 0xFFFF; 57 | uint8_t r = ((color >> 0) & 0x1F) * 255 / 31; 58 | uint8_t g = ((color >> 5) & 0x1F) * 255 / 31; 59 | uint8_t b = ((color >> 10) & 0x1F) * 255 / 31; 60 | tiles[i] = (0xFF << 24) | (b << 16) | (g << 8) | r; 61 | } 62 | 63 | // Rearrange the pixels from 8x8 tiles to a 32x32 icon 64 | for (int i = 0; i < 4; i++) { 65 | for (int j = 0; j < 8; j++) { 66 | for (int k = 0; k < 4; k++) 67 | memcpy(&icon[256 * i + 32 * j + 8 * k], &tiles[256 * i + 8 * j + 64 * k], 8 * sizeof(uint32_t)); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/common/nds_icon.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class NdsIcon { 26 | public: 27 | NdsIcon(std::string path, int fd = -1); 28 | uint32_t *getIcon() { return icon; } 29 | 30 | private: 31 | uint32_t icon[32 * 32]; 32 | }; 33 | -------------------------------------------------------------------------------- /src/common/screen_layout.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | class ScreenLayout { 23 | public: 24 | static int screenPosition; 25 | static int screenRotation; 26 | static int screenArrangement; 27 | static int screenSizing; 28 | static int screenGap; 29 | static int aspectRatio; 30 | static int integerScale; 31 | static int gbaCrop; 32 | 33 | int winWidth = 0, winHeight = 0; 34 | int minWidth = 0, minHeight = 0; 35 | int topX = 0, botX = 0; 36 | int topY = 0, botY = 0; 37 | int topWidth = 0, botWidth = 0; 38 | int topHeight = 0, botHeight = 0; 39 | 40 | static void addSettings(); 41 | void update(int winWidth, int winHeight, bool gbaMode, bool splitScreens = false); 42 | 43 | int getTouchX(int x, int y); 44 | int getTouchY(int x, int y); 45 | }; 46 | -------------------------------------------------------------------------------- /src/console/console_ui.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "../common/screen_layout.h" 27 | #include "../core.h" 28 | #include "../defines.h" 29 | 30 | enum MenuInputs { 31 | INPUT_A, 32 | INPUT_B, 33 | INPUT_SELECT, 34 | INPUT_START, 35 | INPUT_RIGHT, 36 | INPUT_LEFT, 37 | INPUT_UP, 38 | INPUT_DOWN, 39 | INPUT_R, 40 | INPUT_L, 41 | INPUT_X, 42 | INPUT_Y, 43 | INPUT_MENU, 44 | INPUT_FAST_HOLD, 45 | INPUT_FAST_TOGG, 46 | INPUT_SCRN_SWAP, 47 | INPUT_MAX 48 | }; 49 | 50 | struct MenuTouch { 51 | bool pressed; 52 | float x, y; 53 | 54 | MenuTouch(bool pressed, float x, float y): 55 | pressed(pressed), x(x), y(y) {} 56 | }; 57 | 58 | struct MenuItem { 59 | std::string name; 60 | std::string setting; 61 | void *iconTex; 62 | uint8_t iconSize; 63 | bool header; 64 | 65 | MenuItem(std::string name, std::string setting = "", void *iconTex = nullptr, uint8_t iconSize = 0): 66 | name(name), setting(setting), iconTex(iconTex), iconSize(iconSize), header(false) {} 67 | MenuItem(std::string name, bool header): 68 | name(name), setting(""), iconTex(nullptr), iconSize(0), header(header) {} 69 | bool operator<(const MenuItem &item) { return (name < item.name); } 70 | }; 71 | 72 | class ConsoleUI { 73 | public: 74 | static Core *core; 75 | static bool running; 76 | static uint32_t framebuffer[256 * 192 * 8]; 77 | static ScreenLayout layout; 78 | static bool gbaMode; 79 | 80 | static int showFpsCounter; 81 | static int menuTheme; 82 | static int keyBinds[INPUT_MAX]; 83 | 84 | // Data that is defined per-platform 85 | static uint32_t defaultKeys[INPUT_MAX]; 86 | static const char *keyNames[32]; 87 | 88 | // Functions that are implemented per-platform 89 | static void startFrame(uint32_t color); 90 | static void endFrame(); 91 | static void *createTexture(uint32_t *data, int width, int height); 92 | static void destroyTexture(void *texture); 93 | static void drawTexture(void *texture, float tx, float ty, float tw, float th, float x, float y, 94 | float w, float h, bool filter = true, int rotation = 0, uint32_t color = 0xFFFFFFFF); 95 | static uint32_t getInputHeld(); 96 | static MenuTouch getInputTouch(); 97 | 98 | static void drawRectangle(float x, float y, float w, float h, uint32_t color = 0xFFFFFFFF); 99 | static void drawString(std::string string, float x, float y, 100 | float size, uint32_t color = 0xFFFFFFFF, bool alignRight = false); 101 | static void fillAudioBuffer(uint32_t *buffer, int count, int rate); 102 | static uint32_t getInputPress(); 103 | 104 | static void initialize(int width, int height, std::string root, std::string prefix); 105 | static void mainLoop(MenuTouch (*specialTouch)() = nullptr, ScreenLayout *touchLayout = nullptr); 106 | static int setPath(std::string path); 107 | static void fileBrowser(); 108 | 109 | private: 110 | static void *fileTextures[2]; 111 | static void *folderTextures[2]; 112 | static void *fontTexture; 113 | 114 | static const uint32_t *palette; 115 | static uint32_t uiWidth, uiHeight; 116 | static uint32_t lineHeight; 117 | static bool touchMode; 118 | 119 | static std::string ndsPath, gbaPath; 120 | static std::string basePath, curPath; 121 | static bool changed; 122 | 123 | static std::thread *coreThread, *saveThread; 124 | static std::condition_variable cond; 125 | static std::mutex mutex; 126 | static int fpsLimiterBackup; 127 | 128 | static const uint32_t themeColors[]; 129 | static const uint8_t charWidths[]; 130 | 131 | ConsoleUI() {} // Private to prevent instantiation 132 | static void *bmpToTexture(uint8_t *bmp); 133 | static int stringWidth(std::string &string); 134 | 135 | static uint32_t menu(std::string title, std::vector &items, 136 | int &index, std::string actionX = "", std::string actionPlus = ""); 137 | static uint32_t message(std::string title, std::string text, int type = 0); 138 | 139 | static void settingsMenu(); 140 | static void controlsMenu(); 141 | static void pauseMenu(); 142 | static bool saveTypeMenu(); 143 | 144 | static bool createCore(); 145 | static void startCore(); 146 | static void stopCore(); 147 | static void runCore(); 148 | static void checkSave(); 149 | }; 150 | -------------------------------------------------------------------------------- /src/console/images/file_dark.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/console/images/file_dark.bmp -------------------------------------------------------------------------------- /src/console/images/file_light.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/console/images/file_light.bmp -------------------------------------------------------------------------------- /src/console/images/folder_dark.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/console/images/folder_dark.bmp -------------------------------------------------------------------------------- /src/console/images/folder_light.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/console/images/folder_light.bmp -------------------------------------------------------------------------------- /src/console/images/font.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/console/images/font.bmp -------------------------------------------------------------------------------- /src/console/main_vita.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #ifdef __VITA__ 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "console_ui.h" 32 | 33 | // Reserve 192MB of allocatable memory 34 | int _newlib_heap_size_user = 192 * 1024 * 1024; 35 | 36 | uint32_t audioBuffer[1024]; 37 | int audioPort; 38 | bool playing = true; 39 | 40 | uint32_t ConsoleUI::defaultKeys[] { 41 | SCE_CTRL_CIRCLE, SCE_CTRL_CROSS, SCE_CTRL_SELECT, SCE_CTRL_START, 42 | SCE_CTRL_RIGHT | BIT(17), SCE_CTRL_LEFT | BIT(19), SCE_CTRL_UP | BIT(16), SCE_CTRL_DOWN | BIT(18), 43 | SCE_CTRL_RTRIGGER, SCE_CTRL_LTRIGGER, SCE_CTRL_TRIANGLE, SCE_CTRL_SQUARE, 44 | BIT(20) | BIT(21) | BIT(22) | BIT(23) 45 | }; 46 | 47 | const char *ConsoleUI::keyNames[] { 48 | "Select", "L3", "R3", "Start", "Up", "Right", "Down", "Left", 49 | "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square", 50 | "LS Up", "LS Right", "LS Down", "LS Left", "RS Up", "RS Right", "RS Down", "RS Left" 51 | }; 52 | 53 | void ConsoleUI::startFrame(uint32_t color) { 54 | // Clear the screen for a new frame 55 | vita2d_start_drawing(); 56 | vita2d_set_clear_color(color); 57 | vita2d_clear_screen(); 58 | } 59 | 60 | void ConsoleUI::endFrame() { 61 | // Finish and display a frame 62 | vita2d_end_drawing(); 63 | vita2d_swap_buffers(); 64 | } 65 | 66 | void *ConsoleUI::createTexture(uint32_t *data, int width, int height) { 67 | // Create a new texture and copy data to it 68 | vita2d_texture *texture = vita2d_create_empty_texture(width, height); 69 | uint32_t stride = vita2d_texture_get_stride(texture) / sizeof(uint32_t); 70 | uint32_t *texData = (uint32_t*)vita2d_texture_get_datap(texture); 71 | for (int y = 0; y < height; y++) 72 | memcpy(&texData[y * stride], &data[y * width], width * sizeof(uint32_t)); 73 | return texture; 74 | } 75 | 76 | void ConsoleUI::destroyTexture(void *texture) { 77 | // Clean up a texture when it's safe to do so 78 | vita2d_wait_rendering_done(); 79 | vita2d_free_texture((vita2d_texture*)texture); 80 | } 81 | 82 | void ConsoleUI::drawTexture(void *texture, float tx, float ty, float tw, float th, 83 | float x, float y, float w, float h, bool filter, int rotation, uint32_t color) { 84 | // Set the texture filter 85 | SceGxmTextureFilter texFilter = filter ? SCE_GXM_TEXTURE_FILTER_LINEAR : SCE_GXM_TEXTURE_FILTER_POINT; 86 | vita2d_texture_set_filters((vita2d_texture*)texture, texFilter, texFilter); 87 | 88 | // Draw a texture depending on its rotation 89 | if (rotation == 0) 90 | vita2d_draw_texture_tint_part_scale((vita2d_texture*)texture, x, y, tx, ty, tw, th, w / tw, h / th, color); 91 | else 92 | vita2d_draw_texture_part_tint_scale_rotate((vita2d_texture*)texture, x + w / 2, y + h / 2, 93 | tx, ty, tw, th, w / th, h / tw, 3.14159f * ((rotation == 1) ? 0.5f : -0.5f), color); 94 | } 95 | 96 | uint32_t ConsoleUI::getInputHeld() { 97 | // Scan for held buttons 98 | SceCtrlData held; 99 | sceCtrlPeekBufferPositive(0, &held, 1); 100 | 101 | // Return a mask of mappable keys, including stick movements 102 | uint32_t value = held.buttons & 0xFFFF; 103 | value |= (held.ly < 32) << 16; // LS Up 104 | value |= (held.lx > 224) << 17; // LS Right 105 | value |= (held.ly > 224) << 18; // LS Down 106 | value |= (held.lx < 32) << 19; // LS Left 107 | value |= (held.ry < 32) << 20; // RS Up 108 | value |= (held.rx > 224) << 21; // RS Right 109 | value |= (held.ry > 224) << 22; // RS Down 110 | value |= (held.rx < 32) << 23; // RS Left 111 | return value; 112 | } 113 | 114 | MenuTouch ConsoleUI::getInputTouch() { 115 | // Scan for touch input and scale it to 720p 116 | SceTouchData touch; 117 | sceTouchPeek(SCE_TOUCH_PORT_FRONT, &touch, 1); 118 | return MenuTouch(touch.reportNum > 0, touch.report[0].x * 1280 / 1920, touch.report[0].y * 720 / 1080); 119 | } 120 | 121 | void outputAudio() { 122 | while (playing) { 123 | // Refill the audio buffer until stopped 124 | ConsoleUI::fillAudioBuffer(audioBuffer, 1024, 48000); 125 | sceAudioOutOutput(audioPort, audioBuffer); 126 | } 127 | } 128 | 129 | int main() { 130 | // Initialize hardware and the UI 131 | scePowerSetArmClockFrequency(444); 132 | vita2d_init(); 133 | audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_BGM, 1024, 48000, SCE_AUDIO_OUT_MODE_STEREO); 134 | std::thread audioThread(outputAudio); 135 | sceCtrlSetSamplingMode(SCE_CTRL_MODE_ANALOG); 136 | sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); 137 | ConsoleUI::initialize(960, 544, "ux0:", "ux0:/data/noods/"); 138 | 139 | // Open the file browser if a ROM can't be loaded from arguments 140 | char params[1024], *path; 141 | sceAppMgrGetAppParam(params); 142 | if (!strstr(params, "psgm:play") || !(path = strstr(params, "¶m=")) || ConsoleUI::setPath(path + 7) < 2) 143 | ConsoleUI::fileBrowser(); 144 | 145 | // Run the emulator until it exits 146 | ConsoleUI::mainLoop(); 147 | playing = false; 148 | audioThread.join(); 149 | vita2d_fini(); 150 | return 0; 151 | } 152 | 153 | #endif // __VITA__ 154 | -------------------------------------------------------------------------------- /src/console/shaders/latte-assembler: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/console/shaders/latte-assembler -------------------------------------------------------------------------------- /src/console/shaders/shader_wiiu.gsh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hydr8gon/NooDS/93086be34b9efc3b63b9d4e36a50abe8baeecd55/src/console/shaders/shader_wiiu.gsh -------------------------------------------------------------------------------- /src/console/shaders/shader_wiiu.psh: -------------------------------------------------------------------------------- 1 | ; $MODE = "UniformRegister" 2 | ; $SAMPLER_VARS[0].name = "texture" 3 | ; $SAMPLER_VARS[0].type = "sampler2D" 4 | ; $SAMPLER_VARS[0].location = 0 5 | ; $NUM_SPI_PS_INPUT_CNTL = 2 6 | ; $SPI_PS_INPUT_CNTL[0].SEMANTIC = 0 7 | ; $SPI_PS_INPUT_CNTL[0].DEFAULT_VAL = 1 8 | ; $SPI_PS_INPUT_CNTL[1].SEMANTIC = 1 9 | ; $SPI_PS_INPUT_CNTL[1].DEFAULT_VAL = 1 10 | 11 | 00 TEX: ADDR(48) CNT(1) VALID_PIX 12 | 0 SAMPLE R0, R0, t0, s0 13 | 01 ALU: ADDR(32) CNT(4) 14 | 1 x: MUL R0.x, R0.x, R1.x 15 | y: MUL R0.y, R0.y, R1.y 16 | z: MUL R0.z, R0.z, R1.z 17 | w: MUL R0.w, R0.w, R1.w 18 | 02 EXP_DONE: PIX0, R0 19 | END_OF_PROGRAM 20 | -------------------------------------------------------------------------------- /src/console/shaders/shader_wiiu.vsh: -------------------------------------------------------------------------------- 1 | ; $MODE = "UniformRegister" 2 | ; $ATTRIB_VARS[0].name = "position" 3 | ; $ATTRIB_VARS[0].type = "float2" 4 | ; $ATTRIB_VARS[0].location = 0 5 | ; $ATTRIB_VARS[1].name = "tex_coords" 6 | ; $ATTRIB_VARS[1].type = "float2" 7 | ; $ATTRIB_VARS[1].location = 1 8 | ; $ATTRIB_VARS[2].name = "vtx_color" 9 | ; $ATTRIB_VARS[2].type = "float4" 10 | ; $ATTRIB_VARS[2].location = 2 11 | ; $SPI_VS_OUT_CONFIG.VS_EXPORT_COUNT = 1 12 | ; $NUM_SPI_VS_OUT_ID = 1 13 | ; $SPI_VS_OUT_ID[0].SEMANTIC_0 = 0 14 | ; $SPI_VS_OUT_ID[0].SEMANTIC_1 = 1 15 | 16 | 00 CALL_FS NO_BARRIER 17 | 01 EXP_DONE: POS0, R1 18 | 02 EXP_DONE: PARAM0, R2 NO_BARRIER 19 | 03 EXP_DONE: PARAM1, R3 NO_BARRIER 20 | END_OF_PROGRAM 21 | -------------------------------------------------------------------------------- /src/core.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "action_replay.h" 29 | #include "cartridge.h" 30 | #include "cp15.h" 31 | #include "defines.h" 32 | #include "div_sqrt.h" 33 | #include "dldi.h" 34 | #include "dma.h" 35 | #include "gpu.h" 36 | #include "gpu_2d.h" 37 | #include "gpu_3d.h" 38 | #include "gpu_3d_renderer.h" 39 | #include "hle_arm7.h" 40 | #include "hle_bios.h" 41 | #include "input.h" 42 | #include "interpreter.h" 43 | #include "ipc.h" 44 | #include "memory.h" 45 | #include "rtc.h" 46 | #include "save_states.h" 47 | #include "settings.h" 48 | #include "spi.h" 49 | #include "spu.h" 50 | #include "timers.h" 51 | #include "wifi.h" 52 | 53 | enum CoreError { 54 | ERROR_BIOS, 55 | ERROR_FIRM, 56 | ERROR_ROM 57 | }; 58 | 59 | enum SchedTask { 60 | UPDATE_RUN, 61 | RESET_CYCLES, 62 | CART9_WORD_READY, 63 | CART7_WORD_READY, 64 | DMA9_TRANSFER0, 65 | DMA9_TRANSFER1, 66 | DMA9_TRANSFER2, 67 | DMA9_TRANSFER3, 68 | DMA7_TRANSFER0, 69 | DMA7_TRANSFER1, 70 | DMA7_TRANSFER2, 71 | DMA7_TRANSFER3, 72 | NDS_SCANLINE256, 73 | NDS_SCANLINE355, 74 | GBA_SCANLINE240, 75 | GBA_SCANLINE308, 76 | GPU3D_COMMANDS, 77 | ARM9_INTERRUPT, 78 | ARM7_INTERRUPT, 79 | NDS_SPU_SAMPLE, 80 | GBA_SPU_SAMPLE, 81 | TIMER9_OVERFLOW0, 82 | TIMER9_OVERFLOW1, 83 | TIMER9_OVERFLOW2, 84 | TIMER9_OVERFLOW3, 85 | TIMER7_OVERFLOW0, 86 | TIMER7_OVERFLOW1, 87 | TIMER7_OVERFLOW2, 88 | TIMER7_OVERFLOW3, 89 | WIFI_COUNT_MS, 90 | WIFI_TRANS_REPLY, 91 | WIFI_TRANS_ACK, 92 | MAX_TASKS 93 | }; 94 | 95 | struct SchedEvent { 96 | SchedTask task; 97 | uint32_t cycles; 98 | 99 | SchedEvent(SchedTask task, uint32_t cycles): task(task), cycles(cycles) {} 100 | bool operator<(const SchedEvent &event) const { return cycles < event.cycles; } 101 | }; 102 | 103 | class Core { 104 | public: 105 | int id = 0; 106 | int fps = 0; 107 | bool arm7Hle = false; 108 | bool dsiMode = false; 109 | bool gbaMode = false; 110 | 111 | ActionReplay actionReplay; 112 | CartridgeGba cartridgeGba; 113 | CartridgeNds cartridgeNds; 114 | Cp15 cp15; 115 | DivSqrt divSqrt; 116 | Dldi dldi; 117 | Dma dma[2]; 118 | Gpu gpu; 119 | Gpu2D gpu2D[2]; 120 | Gpu3D gpu3D; 121 | Gpu3DRenderer gpu3DRenderer; 122 | HleArm7 hleArm7; 123 | HleBios hleBios[3]; 124 | Input input; 125 | Interpreter interpreter[2]; 126 | Ipc ipc; 127 | Memory memory; 128 | Rtc rtc; 129 | SaveStates saveStates; 130 | Spi spi; 131 | Spu spu; 132 | Timers timers[2]; 133 | Wifi wifi; 134 | 135 | std::atomic running; 136 | std::vector events; 137 | std::function tasks[MAX_TASKS]; 138 | uint32_t globalCycles = 0; 139 | 140 | Core(std::string ndsRom = "", std::string gbaRom = "", int id = 0, int ndsRomFd = -1, int gbaRomFd = -1, 141 | int ndsSaveFd = -1, int gbaSaveFd = -1, int ndsStateFd = -1, int gbaStateFd = -1, int ndsCheatFd = -1); 142 | void saveState(FILE *file); 143 | void loadState(FILE *file); 144 | 145 | void runCore() { (*runFunc)(*this); } 146 | void schedule(SchedTask task, uint32_t cycles); 147 | void enterGbaMode(); 148 | void endFrame(); 149 | 150 | private: 151 | bool realGbaBios; 152 | void (*runFunc)(Core&) = &Interpreter::runCoreNds; 153 | std::chrono::steady_clock::time_point lastFpsTime; 154 | int fpsCount = 0; 155 | 156 | void updateRun(); 157 | void resetCycles(); 158 | }; 159 | -------------------------------------------------------------------------------- /src/cp15.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include "core.h" 21 | 22 | void Cp15::saveState(FILE *file) { 23 | // Write state data to the file 24 | fwrite(&ctrlReg, sizeof(ctrlReg), 1, file); 25 | fwrite(&dtcmReg, sizeof(dtcmReg), 1, file); 26 | fwrite(&itcmReg, sizeof(itcmReg), 1, file); 27 | fwrite(&procId, sizeof(procId), 1, file); 28 | } 29 | 30 | void Cp15::loadState(FILE *file) { 31 | // Read state data from the file 32 | uint32_t ctrl, dtcm, itcm; 33 | fread(&ctrl, sizeof(ctrl), 1, file); 34 | fread(&dtcm, sizeof(dtcm), 1, file); 35 | fread(&itcm, sizeof(itcm), 1, file); 36 | fread(&procId, sizeof(procId), 1, file); 37 | 38 | // Set registers along with values based on them 39 | write(1, 0, 0, ctrl); 40 | write(9, 1, 0, dtcm); 41 | write(9, 1, 1, itcm); 42 | } 43 | 44 | uint32_t Cp15::read(uint8_t cn, uint8_t cm, uint8_t cp) { 45 | // Read a value from a CP15 register 46 | switch ((cn << 16) | (cm << 8) | (cp << 0)) { 47 | case 0x000000: return 0x41059461; // Main ID 48 | case 0x000001: return 0x0F0D2112; // Cache type 49 | case 0x010000: return ctrlReg; // Control 50 | case 0x090100: return dtcmReg; // Data TCM base/size 51 | case 0x090101: return itcmReg; // Instruction TCM size 52 | case 0x0D0001: case 0x0D0101: return procId; // Trace process ID 53 | 54 | default: 55 | LOG_WARN("Unknown CP15 register read: C%d,C%d,%d\n", cn, cm, cp); 56 | return 0; 57 | } 58 | } 59 | 60 | void Cp15::write(uint8_t cn, uint8_t cm, uint8_t cp, uint32_t value) { 61 | // Write a value to a CP15 register 62 | uint32_t oldAddr, oldSize; 63 | switch ((cn << 16) | (cm << 8) | (cp << 0)) { 64 | case 0x010000: // Control 65 | // Set writable control bits and update their state values 66 | ctrlReg = (ctrlReg & ~0xFF085) | (value & 0xFF085); 67 | exceptionAddr = (ctrlReg & BIT(13)) ? 0xFFFF0000 : 0x00000000; 68 | dtcmCanRead = (ctrlReg & BIT(16)) && !(ctrlReg & BIT(17)); 69 | dtcmCanWrite = (ctrlReg & BIT(16)); 70 | itcmCanRead = (ctrlReg & BIT(18)) && !(ctrlReg & BIT(19)); 71 | itcmCanWrite = (ctrlReg & BIT(18)); 72 | 73 | // Update the memory map at the current TCM locations 74 | core->memory.updateMap9(dtcmAddr, dtcmAddr + dtcmSize, true); 75 | core->memory.updateMap9(0x00000000, itcmSize, true); 76 | return; 77 | 78 | case 0x090100: // Data TCM base/size 79 | // Set the DTCM register and update its address and size 80 | dtcmReg = value; 81 | oldAddr = dtcmAddr; 82 | oldSize = dtcmSize; 83 | dtcmAddr = dtcmReg & 0xFFFFF000; 84 | dtcmSize = std::max(0x1000, 0x200 << ((dtcmReg >> 1) & 0x1F)); // Min 4KB 85 | 86 | // Update the memory map at the old and new DTCM areas 87 | core->memory.updateMap9(oldAddr, oldAddr + oldSize, true); 88 | core->memory.updateMap9(dtcmAddr, dtcmAddr + dtcmSize, true); 89 | return; 90 | 91 | case 0x070004: case 0x070802: // Wait for interrupt 92 | // Halt the CPU 93 | core->interpreter[0].halt(0); 94 | return; 95 | 96 | case 0x090101: // Instruction TCM size 97 | // Set the ITCM register and update its size 98 | itcmReg = value; 99 | oldSize = itcmSize; 100 | itcmSize = std::max(0x1000, 0x200 << ((itcmReg >> 1) & 0x1F)); // Min 4KB 101 | 102 | // Update the memory map at the old and new ITCM areas 103 | core->memory.updateMap9(0x00000000, std::max(oldSize, itcmSize), true); 104 | return; 105 | 106 | case 0x0D0001: case 0x0D0101: // Trace process ID 107 | // Set the trace process ID register 108 | procId = value; 109 | return; 110 | 111 | default: 112 | LOG_WARN("Unknown CP15 register write: C%d,C%d,%d\n", cn, cm, cp); 113 | return; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/cp15.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | class Cp15 { 28 | public: 29 | uint32_t exceptionAddr = 0; 30 | bool dtcmCanRead = false, dtcmCanWrite = false; 31 | bool itcmCanRead = false, itcmCanWrite = false; 32 | uint32_t dtcmAddr = 0, dtcmSize = 0; 33 | uint32_t itcmSize = 0; 34 | 35 | Cp15(Core *core): core(core) {} 36 | void saveState(FILE *file); 37 | void loadState(FILE *file); 38 | 39 | uint32_t read(uint8_t cn, uint8_t cm, uint8_t cp); 40 | void write(uint8_t cn, uint8_t cm, uint8_t cp, uint32_t value); 41 | 42 | private: 43 | Core *core; 44 | uint32_t ctrlReg = 0x78; 45 | uint32_t dtcmReg = 0x00; 46 | uint32_t itcmReg = 0x00; 47 | uint32_t procId = 0x00; 48 | }; 49 | -------------------------------------------------------------------------------- /src/defines.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | // Control how many cycles of 3D GPU commands are batched 25 | #define GPU3D_BATCH 32 26 | 27 | // Print critical logs in red if enabled 28 | #if LOG_LEVEL > 0 29 | #define LOG_CRIT(...) printf("\x1b[31m" __VA_ARGS__) 30 | #else 31 | #define LOG_CRIT(...) (0) 32 | #endif 33 | 34 | // Print warning logs in yellow if enabled 35 | #if LOG_LEVEL > 1 36 | #define LOG_WARN(...) printf("\x1b[33m" __VA_ARGS__) 37 | #else 38 | #define LOG_WARN(...) (0) 39 | #endif 40 | 41 | // Print info logs normally if enabled 42 | #if LOG_LEVEL > 2 43 | #define LOG_INFO(...) printf("\x1b[0m" __VA_ARGS__) 44 | #else 45 | #define LOG_INFO(...) (0) 46 | #endif 47 | 48 | // Compatibility toggle for systems that don't have fdopen 49 | #ifdef NO_FDOPEN 50 | #define fdopen(...) (0) 51 | #define ftruncate(...) (0) 52 | #else 53 | #include 54 | #endif 55 | 56 | // Macro to handle differing mkdir arguments on Windows 57 | #ifdef WINDOWS 58 | #define MKDIR_ARGS 59 | #else 60 | #define MKDIR_ARGS , 0777 61 | #endif 62 | 63 | // Macro to force inlining 64 | #ifdef _MSC_VER 65 | #define FORCE_INLINE __forceinline 66 | #else 67 | #define FORCE_INLINE inline __attribute__((always_inline)) 68 | #endif 69 | 70 | // Simple bit macro 71 | #define BIT(i) (1 << (i)) 72 | 73 | // Macro to swap two values 74 | #define SWAP(a, b) { \ 75 | auto c = a; \ 76 | a = b; \ 77 | b = c; \ 78 | } 79 | 80 | // Macros that read a value larger than 8 bits from an 8-bit array 81 | #ifdef ENDIAN_BIG 82 | #define U8TO16(data, index) __builtin_bswap16(*(uint16_t*)&(data)[index]) 83 | #define U8TO32(data, index) __builtin_bswap32(*(uint32_t*)&(data)[index]) 84 | #define U8TO64(data, index) __builtin_bswap64(*(uint64_t*)&(data)[index]) 85 | #else 86 | #define U8TO16(data, index) (*(uint16_t*)&(data)[index]) 87 | #define U8TO32(data, index) (*(uint32_t*)&(data)[index]) 88 | #define U8TO64(data, index) (*(uint64_t*)&(data)[index]) 89 | #endif 90 | 91 | // Macro that stores a 32-bit value to an 8-bit array 92 | #ifdef ENDIAN_BIG 93 | #define U32TO8(data, index, value) (*(uint32_t*)&(data)[index] = __builtin_bswap32(value)) 94 | #else 95 | #define U32TO8(data, index, value) (*(uint32_t*)&(data)[index] = (value)) 96 | #endif 97 | -------------------------------------------------------------------------------- /src/desktop/cheat_dialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "noo_frame.h" 23 | 24 | class CheatDialog: public wxDialog { 25 | public: 26 | CheatDialog(Core *core); 27 | 28 | private: 29 | Core *core; 30 | wxCheckListBox *cheatList; 31 | wxTextCtrl *nameEditor; 32 | wxTextCtrl *codeEditor; 33 | int curCheat = -1; 34 | 35 | void updateCheat(); 36 | void checkCheat(wxCommandEvent &event); 37 | void selectCheat(wxCommandEvent &event); 38 | void addCheat(wxCommandEvent &event); 39 | void removeCheat(wxCommandEvent &event); 40 | void cancel(wxCommandEvent &event); 41 | void confirm(wxCommandEvent &event); 42 | wxDECLARE_EVENT_TABLE(); 43 | }; 44 | -------------------------------------------------------------------------------- /src/desktop/input_dialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "noo_app.h" 27 | 28 | class InputDialog: public wxDialog { 29 | public: 30 | InputDialog(wxJoystick *joystick); 31 | ~InputDialog(); 32 | 33 | private: 34 | wxJoystick *joystick; 35 | wxTimer *timer; 36 | 37 | wxButton *keyA; 38 | wxButton *keyB; 39 | wxButton *keyX; 40 | wxButton *keyY; 41 | wxButton *keyStart; 42 | wxButton *keySelect; 43 | wxButton *keyUp; 44 | wxButton *keyDown; 45 | wxButton *keyLeft; 46 | wxButton *keyRight; 47 | wxButton *keyL; 48 | wxButton *keyR; 49 | wxButton *keyFastHold; 50 | wxButton *keyFastToggle; 51 | wxButton *keyFullScreen; 52 | wxButton *keyScreenSwap; 53 | wxButton *keySystemPause; 54 | 55 | int keyBinds[MAX_KEYS]; 56 | std::vector axisBases; 57 | wxButton *current = nullptr; 58 | int keyIndex = 0; 59 | 60 | std::string keyToString(int key); 61 | void resetLabels(); 62 | 63 | void remapA(wxCommandEvent &event); 64 | void remapB(wxCommandEvent &event); 65 | void remapX(wxCommandEvent &event); 66 | void remapY(wxCommandEvent &event); 67 | void remapStart(wxCommandEvent &event); 68 | void remapSelect(wxCommandEvent &event); 69 | void remapUp(wxCommandEvent &event); 70 | void remapDown(wxCommandEvent &event); 71 | void remapLeft(wxCommandEvent &event); 72 | void remapRight(wxCommandEvent &event); 73 | void remapL(wxCommandEvent &event); 74 | void remapR(wxCommandEvent &event); 75 | void remapFastHold(wxCommandEvent &event); 76 | void remapFastToggle(wxCommandEvent &event); 77 | void remapFullScreen(wxCommandEvent &event); 78 | void remapScreenSwap(wxCommandEvent &event); 79 | void remapSystemPause(wxCommandEvent &event); 80 | void clearMap(wxCommandEvent &event); 81 | void updateJoystick(wxTimerEvent &event); 82 | void confirm(wxCommandEvent &event); 83 | void pressKey(wxKeyEvent &event); 84 | wxDECLARE_EVENT_TABLE(); 85 | }; 86 | -------------------------------------------------------------------------------- /src/desktop/layout_dialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #ifndef LAYOUT_DIALOG_H 21 | #define LAYOUT_DIALOG_H 22 | 23 | #include 24 | 25 | class NooApp; 26 | 27 | class LayoutDialog: public wxDialog { 28 | public: 29 | LayoutDialog(NooApp *app); 30 | 31 | private: 32 | NooApp *app; 33 | int prevSettings[10]; 34 | 35 | void posCenter(wxCommandEvent &event); 36 | void posTop(wxCommandEvent &event); 37 | void posBottom(wxCommandEvent &event); 38 | void posLeft(wxCommandEvent &event); 39 | void posRight(wxCommandEvent &event); 40 | void rotateNone(wxCommandEvent &event); 41 | void rotateCw(wxCommandEvent &event); 42 | void rotateCcw(wxCommandEvent &event); 43 | void arrangeAuto(wxCommandEvent &event); 44 | void arrangeVert(wxCommandEvent &event); 45 | void arrangeHori(wxCommandEvent &event); 46 | void arrangeSing(wxCommandEvent &event); 47 | void sizeEven(wxCommandEvent &event); 48 | void sizeTop(wxCommandEvent &event); 49 | void sizeBot(wxCommandEvent &event); 50 | void gapNone(wxCommandEvent &event); 51 | void gapQuart(wxCommandEvent &event); 52 | void gapHalf(wxCommandEvent &event); 53 | void gapFull(wxCommandEvent &event); 54 | void filtNearest(wxCommandEvent &event); 55 | void filtUpscale(wxCommandEvent &event); 56 | void filtLinear(wxCommandEvent &event); 57 | void aspectDefault(wxCommandEvent &event); 58 | void aspect16x10(wxCommandEvent &event); 59 | void aspect16x9(wxCommandEvent &event); 60 | void aspect18x9(wxCommandEvent &event); 61 | void intScale(wxCommandEvent &event); 62 | void gbaCrop(wxCommandEvent &event); 63 | void splitScreens(wxCommandEvent &event); 64 | void cancel(wxCommandEvent &event); 65 | void confirm(wxCommandEvent &event); 66 | 67 | wxDECLARE_EVENT_TABLE(); 68 | }; 69 | 70 | #endif // LAYOUT_DIALOG_H 71 | -------------------------------------------------------------------------------- /src/desktop/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include "noo_app.h" 21 | 22 | // Let wxWidgets handle the main function 23 | wxIMPLEMENT_APP(NooApp); 24 | -------------------------------------------------------------------------------- /src/desktop/noo_app.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | #define MAX_FRAMES 8 26 | #define MAX_KEYS 17 27 | 28 | class NooFrame; 29 | 30 | class NooApp: public wxApp { 31 | public: 32 | static int micEnable; 33 | static int splitScreens; 34 | static int keyBinds[MAX_KEYS]; 35 | 36 | void createFrame(); 37 | void removeFrame(int id); 38 | 39 | void connectCore(int id); 40 | void disconnCore(int id); 41 | 42 | void updateLayouts(); 43 | void startStream(bool stream); 44 | void stopStream(bool stream); 45 | 46 | private: 47 | NooFrame *frames[MAX_FRAMES] = {}; 48 | PaStream *streams[2] = {}; 49 | 50 | bool OnInit(); 51 | int OnExit(); 52 | 53 | static int audioCallback(const void *in, void *out, unsigned long count, 54 | const PaStreamCallbackTimeInfo *info, PaStreamCallbackFlags flags, void *data); 55 | static int micCallback(const void *in, void *out, unsigned long count, 56 | const PaStreamCallbackTimeInfo *info, PaStreamCallbackFlags flags, void *data); 57 | 58 | void update(wxTimerEvent &event); 59 | wxDECLARE_EVENT_TABLE(); 60 | }; 61 | -------------------------------------------------------------------------------- /src/desktop/noo_canvas.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | #include "../common/screen_layout.h" 26 | 27 | class NooFrame; 28 | 29 | #ifdef USE_GL_CANVAS 30 | #include 31 | #define CANVAS_CLASS wxGLCanvas 32 | #define CANVAS_PARAM nullptr 33 | #else 34 | class wxGLContext; 35 | #define CANVAS_CLASS wxPanel 36 | #define CANVAS_PARAM wxDefaultPosition 37 | #endif 38 | 39 | class NooCanvas: public CANVAS_CLASS { 40 | public: 41 | bool gbaMode = false; 42 | 43 | NooCanvas(NooFrame *frame); 44 | ~NooCanvas(); 45 | 46 | void resetFrame() { sizeReset = 2; } 47 | void finish() { finished = true; } 48 | 49 | private: 50 | NooFrame *frame; 51 | wxGLContext *context; 52 | uint32_t *framebuffer; 53 | bool splitScreens; 54 | 55 | ScreenLayout layout; 56 | uint8_t sizeReset = 0; 57 | bool finished = false; 58 | 59 | int frameCount = 0; 60 | int swapInterval = 0; 61 | int refreshRate = 0; 62 | std::chrono::steady_clock::time_point lastRateTime; 63 | 64 | void drawScreen(int x, int y, int w, int h, int wb, int hb, uint32_t *buf); 65 | 66 | void draw(wxPaintEvent &event); 67 | void resize(wxSizeEvent &event); 68 | void pressKey(wxKeyEvent &event); 69 | void releaseKey(wxKeyEvent &event); 70 | void pressScreen(wxMouseEvent &event); 71 | void releaseScreen(wxMouseEvent &event); 72 | wxDECLARE_EVENT_TABLE(); 73 | }; 74 | -------------------------------------------------------------------------------- /src/desktop/noo_frame.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | #include "../core.h" 26 | 27 | class NooApp; 28 | class NooCanvas; 29 | 30 | class NooFrame: public wxFrame { 31 | public: 32 | NooCanvas *canvas; 33 | NooFrame *partner; 34 | bool mainFrame; 35 | 36 | Core *core = nullptr; 37 | bool running = false; 38 | 39 | NooFrame(NooApp *app, int id = 0, std::string path = "", NooFrame *partner = nullptr); 40 | void Refresh(); 41 | 42 | void startCore(bool full); 43 | void stopCore(bool full); 44 | 45 | void pressKey(int key); 46 | void releaseKey(int key); 47 | 48 | private: 49 | NooApp *app; 50 | wxMenu *fileMenu, *systemMenu; 51 | wxJoystick *joystick; 52 | wxTimer *timer; 53 | int id; 54 | 55 | std::string ndsPath, gbaPath; 56 | std::thread *coreThread = nullptr, *saveThread = nullptr; 57 | std::condition_variable cond; 58 | std::mutex mutex; 59 | 60 | std::vector axisBases; 61 | uint8_t hotkeyToggles = 0; 62 | int fpsLimiterBackup = 0; 63 | bool fullScreen = false; 64 | 65 | void runCore(); 66 | void checkSave(); 67 | void loadRomPath(std::string path); 68 | 69 | void loadRom(wxCommandEvent &event); 70 | void bootFirmware(wxCommandEvent &event); 71 | void saveState(wxCommandEvent &event); 72 | void loadState(wxCommandEvent &event); 73 | void trimRom(wxCommandEvent &event); 74 | void changeSave(wxCommandEvent &event); 75 | void quit(wxCommandEvent &event); 76 | void pause(wxCommandEvent &event); 77 | void restart(wxCommandEvent &event); 78 | void stop(wxCommandEvent &event); 79 | void actionReplay(wxCommandEvent &event); 80 | void addSystem(wxCommandEvent &event); 81 | void directBoot(wxCommandEvent &event); 82 | void romInRam(wxCommandEvent &event); 83 | void fpsLimiter(wxCommandEvent &event); 84 | template void frameskip(wxCommandEvent &event); 85 | void threaded2D(wxCommandEvent &event); 86 | template void threaded3D(wxCommandEvent &event); 87 | void highRes3D(wxCommandEvent &event); 88 | void screenGhost(wxCommandEvent &event); 89 | void emulateAudio(wxCommandEvent &event); 90 | void audio16Bit(wxCommandEvent &event); 91 | void micEnable(wxCommandEvent &event); 92 | void arm7Hle(wxCommandEvent &event); 93 | void dsiMode(wxCommandEvent &event); 94 | void pathSettings(wxCommandEvent &event); 95 | void layoutSettings(wxCommandEvent &event); 96 | void inputSettings(wxCommandEvent &event); 97 | void updateJoystick(wxTimerEvent &event); 98 | void dropFiles(wxDropFilesEvent &event); 99 | void close(wxCloseEvent &event); 100 | wxDECLARE_EVENT_TABLE(); 101 | }; 102 | -------------------------------------------------------------------------------- /src/desktop/path_dialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | class PathDialog: public wxDialog { 25 | public: 26 | PathDialog(); 27 | 28 | private: 29 | wxTextCtrl *bios9Path; 30 | wxTextCtrl *bios7Path; 31 | wxTextCtrl *firmwarePath; 32 | wxTextCtrl *gbaBiosPath; 33 | wxTextCtrl *sdImagePath; 34 | wxCheckBox *boxes[3]; 35 | 36 | void bios9Browse(wxCommandEvent &event); 37 | void bios7Browse(wxCommandEvent &event); 38 | void firmwareBrowse(wxCommandEvent &event); 39 | void gbaBiosBrowse(wxCommandEvent &event); 40 | void sdImageBrowse(wxCommandEvent &event); 41 | void openFolder(wxCommandEvent &event); 42 | void confirm(wxCommandEvent &event); 43 | wxDECLARE_EVENT_TABLE(); 44 | }; 45 | -------------------------------------------------------------------------------- /src/desktop/save_dialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | class NooFrame; 25 | class Cartridge; 26 | 27 | class SaveDialog: public wxDialog { 28 | public: 29 | SaveDialog(NooFrame *frame); 30 | 31 | private: 32 | NooFrame *frame; 33 | Cartridge *cartridge; 34 | 35 | bool gba = false; 36 | int selection = 0; 37 | 38 | int selectionToSize(int selection); 39 | int sizeToSelection(int size); 40 | 41 | void selection0(wxCommandEvent &event); 42 | void selection1(wxCommandEvent &event); 43 | void selection2(wxCommandEvent &event); 44 | void selection3(wxCommandEvent &event); 45 | void selection4(wxCommandEvent &event); 46 | void selection5(wxCommandEvent &event); 47 | void selection6(wxCommandEvent &event); 48 | void selection7(wxCommandEvent &event); 49 | void selection8(wxCommandEvent &event); 50 | void selection9(wxCommandEvent &event); 51 | void confirm(wxCommandEvent &event); 52 | wxDECLARE_EVENT_TABLE(); 53 | }; 54 | -------------------------------------------------------------------------------- /src/div_sqrt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include 21 | #include "core.h" 22 | 23 | void DivSqrt::saveState(FILE *file) { 24 | // Write state data to the file 25 | fwrite(&divCnt, sizeof(divCnt), 1, file); 26 | fwrite(&divNumer, sizeof(divNumer), 1, file); 27 | fwrite(&divDenom, sizeof(divDenom), 1, file); 28 | fwrite(&divResult, sizeof(divResult), 1, file); 29 | fwrite(&divRemResult, sizeof(divRemResult), 1, file); 30 | fwrite(&sqrtCnt, sizeof(sqrtCnt), 1, file); 31 | fwrite(&sqrtResult, sizeof(sqrtResult), 1, file); 32 | fwrite(&sqrtParam, sizeof(sqrtParam), 1, file); 33 | } 34 | 35 | void DivSqrt::loadState(FILE *file) { 36 | // Read state data from the file 37 | fread(&divCnt, sizeof(divCnt), 1, file); 38 | fread(&divNumer, sizeof(divNumer), 1, file); 39 | fread(&divDenom, sizeof(divDenom), 1, file); 40 | fread(&divResult, sizeof(divResult), 1, file); 41 | fread(&divRemResult, sizeof(divRemResult), 1, file); 42 | fread(&sqrtCnt, sizeof(sqrtCnt), 1, file); 43 | fread(&sqrtResult, sizeof(sqrtResult), 1, file); 44 | fread(&sqrtParam, sizeof(sqrtParam), 1, file); 45 | } 46 | 47 | void DivSqrt::divide() { 48 | // Set the division by zero bit based on the full 64-bit denominator, even in 32-bit mode 49 | divDenom ? (divCnt &= ~BIT(14)) : (divCnt |= BIT(14)); 50 | 51 | // Calculate the division result and remainder 52 | switch (divCnt & 0x3) { // Division mode 53 | case 0: // 32-bit / 32-bit 54 | if ((int32_t)divNumer == INT32_MIN && (int32_t)divDenom == -1) { // Overflow 55 | divResult = (int32_t)divNumer ^ (0xFFFFFFFFULL << 32); 56 | divRemResult = 0; 57 | } 58 | else if ((int32_t)divDenom != 0) { 59 | divResult = (int32_t)divNumer / (int32_t)divDenom; 60 | divRemResult = (int32_t)divNumer % (int32_t)divDenom; 61 | } 62 | else { // Division by 0 63 | divResult = (((int32_t)divNumer < 0) ? 1 : -1) ^ (0xFFFFFFFFULL << 32); 64 | divRemResult = (int32_t)divNumer; 65 | } 66 | break; 67 | 68 | case 1: case 3: // 64-bit / 32-bit 69 | if (divNumer == INT64_MIN && (int32_t)divDenom == -1) { // Overflow 70 | divResult = divNumer; 71 | divRemResult = 0; 72 | } 73 | else if ((int32_t)divDenom != 0) { 74 | divResult = divNumer / (int32_t)divDenom; 75 | divRemResult = divNumer % (int32_t)divDenom; 76 | } 77 | else { // Division by 0 78 | divResult = (divNumer < 0) ? 1 : -1; 79 | divRemResult = divNumer; 80 | } 81 | break; 82 | 83 | case 2: // 64-bit / 64-bit 84 | if (divNumer == INT64_MIN && divDenom == -1) { // Overflow 85 | divResult = divNumer; 86 | divRemResult = 0; 87 | } 88 | else if (divDenom != 0) { 89 | divResult = divNumer / divDenom; 90 | divRemResult = divNumer % divDenom; 91 | } 92 | else { // Division by 0 93 | divResult = (divNumer < 0) ? 1 : -1; 94 | divRemResult = divNumer; 95 | } 96 | break; 97 | } 98 | } 99 | 100 | void DivSqrt::squareRoot() { 101 | // Calculate the square root result 102 | switch (sqrtCnt & 0x1) { // Square root mode 103 | case 0: // 32-bit 104 | sqrtResult = sqrt((uint32_t)sqrtParam); 105 | break; 106 | 107 | case 1: // 64-bit 108 | sqrtResult = sqrtl(sqrtParam); 109 | break; 110 | } 111 | } 112 | 113 | void DivSqrt::writeDivCnt(uint16_t mask, uint16_t value) { 114 | // Write to the DIVCNT register and update the division result 115 | mask &= 0x0003; 116 | divCnt = (divCnt & ~mask) | (value & mask); 117 | divide(); 118 | } 119 | 120 | void DivSqrt::writeDivNumerL(uint32_t mask, uint32_t value) { 121 | // Write to the DIVNUMER register and update the division result 122 | divNumer = (divNumer & ~((uint64_t)mask)) | (value & mask); 123 | divide(); 124 | } 125 | 126 | void DivSqrt::writeDivNumerH(uint32_t mask, uint32_t value) { 127 | // Write to the DIVNUMER register and update the division result 128 | divNumer = (divNumer & ~((uint64_t)mask << 32)) | ((uint64_t)(value & mask) << 32); 129 | divide(); 130 | } 131 | 132 | void DivSqrt::writeDivDenomL(uint32_t mask, uint32_t value) { 133 | // Write to the DIVDENOM register and update the division result 134 | divDenom = (divDenom & ~((uint64_t)mask)) | (value & mask); 135 | divide(); 136 | } 137 | 138 | void DivSqrt::writeDivDenomH(uint32_t mask, uint32_t value) { 139 | // Write to the DIVDENOM register and update the division result 140 | divDenom = (divDenom & ~((uint64_t)mask << 32)) | ((uint64_t)(value & mask) << 32); 141 | divide(); 142 | } 143 | 144 | void DivSqrt::writeSqrtCnt(uint16_t mask, uint16_t value) { 145 | // Write to the SQRTCNT register and update the square root result 146 | mask &= 0x0001; 147 | sqrtCnt = (sqrtCnt & ~mask) | (value & mask); 148 | squareRoot(); 149 | } 150 | 151 | void DivSqrt::writeSqrtParamL(uint32_t mask, uint32_t value) { 152 | // Write to the DIVDENOM register and update the square root result 153 | sqrtParam = (sqrtParam & ~((uint64_t)mask)) | (value & mask); 154 | squareRoot(); 155 | } 156 | 157 | void DivSqrt::writeSqrtParamH(uint32_t mask, uint32_t value) { 158 | // Write to the SQRTPARAM register and update the square root result 159 | sqrtParam = (sqrtParam & ~((uint64_t)mask << 32)) | ((uint64_t)(value & mask) << 32); 160 | squareRoot(); 161 | } 162 | -------------------------------------------------------------------------------- /src/div_sqrt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | class DivSqrt { 28 | public: 29 | DivSqrt(Core *core): core(core) {} 30 | void saveState(FILE *file); 31 | void loadState(FILE *file); 32 | 33 | uint16_t readDivCnt() { return divCnt; } 34 | uint32_t readDivNumerL() { return divNumer; } 35 | uint32_t readDivNumerH() { return divNumer >> 32; } 36 | uint32_t readDivDenomL() { return divDenom; } 37 | uint32_t readDivDenomH() { return divDenom >> 32; } 38 | uint32_t readDivResultL() { return divResult; } 39 | uint32_t readDivResultH() { return divResult >> 32; } 40 | uint32_t readDivRemResultL() { return divRemResult; } 41 | uint32_t readDivRemResultH() { return divRemResult >> 32; } 42 | uint16_t readSqrtCnt() { return sqrtCnt; } 43 | uint32_t readSqrtResult() { return sqrtResult; } 44 | uint32_t readSqrtParamL() { return sqrtParam; } 45 | uint32_t readSqrtParamH() { return sqrtParam >> 32; } 46 | 47 | void writeDivCnt(uint16_t mask, uint16_t value); 48 | void writeDivNumerL(uint32_t mask, uint32_t value); 49 | void writeDivNumerH(uint32_t mask, uint32_t value); 50 | void writeDivDenomL(uint32_t mask, uint32_t value); 51 | void writeDivDenomH(uint32_t mask, uint32_t value); 52 | void writeSqrtCnt(uint16_t mask, uint16_t value); 53 | void writeSqrtParamL(uint32_t mask, uint32_t value); 54 | void writeSqrtParamH(uint32_t mask, uint32_t value); 55 | 56 | private: 57 | Core *core; 58 | 59 | uint16_t divCnt = 0; 60 | int64_t divNumer = 0; 61 | int64_t divDenom = 0; 62 | int64_t divResult = 0; 63 | int64_t divRemResult = 0; 64 | 65 | uint16_t sqrtCnt = 0; 66 | uint32_t sqrtResult = 0; 67 | uint64_t sqrtParam = 0; 68 | 69 | void divide(); 70 | void squareRoot(); 71 | }; 72 | -------------------------------------------------------------------------------- /src/dldi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include 21 | #include "core.h" 22 | 23 | Dldi::~Dldi() { 24 | // Ensure the SD image is closed 25 | if (sdImage) 26 | fclose(sdImage); 27 | } 28 | 29 | void Dldi::patchRom(uint8_t *rom, uint32_t offset, uint32_t size) { 30 | // Scan the ROM for DLDI drivers and patch them if found 31 | for (uint32_t i = 0; i < size; i += 4) { 32 | // Check for the DLDI magic number 33 | if (U8TO32(rom, i) != 0xBF8DA5ED) 34 | next: 35 | continue; 36 | 37 | // Check for the DLDI magic string 38 | const char *str = " Chishm\0"; 39 | for (size_t j = 0; j < 8; j++) { 40 | if (rom[i + 4 + j] != str[j]) 41 | goto next; 42 | } 43 | 44 | // Ensure there's room to patch the DLDI driver 45 | if (rom[i + 0x0F] < 0x08) { // Space in ROM 46 | LOG_CRIT("Not enough space to patch DLDI driver at ROM offset 0x%X\n", offset + i); 47 | break; 48 | } 49 | 50 | // Patch the DLDI driver to use the HLE functions 51 | rom[i + 0x0C] = 0x01; // DLDI driver version 52 | rom[i + 0x0D] = 0x08; // Size of driver in terms of 1 << n (256 bytes) 53 | rom[i + 0x0E] = 0x00; // Sections to adjust 54 | strcpy((char*)&rom[i + 0x10], "NooDS DLDI"); // Long driver name 55 | uint32_t address = U8TO32(rom, i + 0x40); // Address of driver 56 | U32TO8(rom, i + 0x44, address + 0x98); // End of driver code 57 | U32TO8(rom, i + 0x58, address + 0x98); // Start of BSS area 58 | U32TO8(rom, i + 0x5C, address + 0x98); // End of BSS area 59 | memcpy(&rom[i + 0x60], "NOOD", 4); // Short driver name 60 | U32TO8(rom, i + 0x64, 0x00000023); // Feature flags (read, write, NDS slot) 61 | U32TO8(rom, i + 0x68, address + 0x80); // Address of startup() 62 | U32TO8(rom, i + 0x6C, address + 0x84); // Address of isInserted() 63 | U32TO8(rom, i + 0x70, address + 0x88); // Address of readSectors(sector, numSectors, buf) 64 | U32TO8(rom, i + 0x74, address + 0x8C); // Address of writeSectors(sector, numSectors, buf) 65 | U32TO8(rom, i + 0x78, address + 0x90); // Address of clearStatus() 66 | U32TO8(rom, i + 0x7C, address + 0x94); // Address of shutdown() 67 | U32TO8(rom, i + 0x80, DLDI_START); // startup() 68 | U32TO8(rom, i + 0x84, DLDI_INSERT); // isInserted() 69 | U32TO8(rom, i + 0x88, DLDI_READ); // readSectors(sector, numSectors, buf) 70 | U32TO8(rom, i + 0x8C, DLDI_WRITE); // writeSectors(sector, numSectors, buf) 71 | U32TO8(rom, i + 0x90, DLDI_CLEAR); // clearStatus() 72 | U32TO8(rom, i + 0x94, DLDI_STOP); // shutdown() 73 | 74 | // Confirm that a driver has been patched 75 | LOG_INFO("Patched DLDI driver at ROM offset 0x%X\n", offset + i); 76 | patched = true; 77 | } 78 | } 79 | 80 | int Dldi::startup() { 81 | // Try to open the SD image 82 | sdImage = fopen(Settings::sdImagePath.c_str(), "rb+"); 83 | return (sdImage ? 1 : 0); 84 | } 85 | 86 | int Dldi::isInserted() { 87 | // Check if the SD image is opened 88 | return (sdImage ? 1 : 0); 89 | } 90 | 91 | int Dldi::readSectors(bool arm7, uint32_t sector, uint32_t numSectors, uint32_t buf) { 92 | // Get the SD offset and size in bytes 93 | if (!sdImage) return 0; 94 | const uint64_t offset = uint64_t(sector) << 9; 95 | const uint64_t size = uint64_t(numSectors) << 9; 96 | 97 | // Read data from the SD image 98 | uint8_t *data = new uint8_t[size]; 99 | fseek(sdImage, offset, SEEK_SET); 100 | fread(data, sizeof(uint8_t), size, sdImage); 101 | 102 | // Write the data to memory 103 | for (int i = 0; i < size; i++) 104 | core->memory.write(arm7, buf + i, data[i]); 105 | delete[] data; 106 | return 1; 107 | } 108 | 109 | int Dldi::writeSectors(bool arm7, uint32_t sector, uint32_t numSectors, uint32_t buf) { 110 | // Get the SD offset and size in bytes 111 | if (!sdImage) return 0; 112 | const uint64_t offset = uint64_t(sector) << 9; 113 | const uint64_t size = uint64_t(numSectors) << 9; 114 | 115 | // Read data from memory 116 | uint8_t *data = new uint8_t[size]; 117 | for (int i = 0; i < size; i++) 118 | data[i] = core->memory.read(arm7, buf + i); 119 | 120 | // Write the data to the SD image 121 | fseek(sdImage, offset, SEEK_SET); 122 | fwrite(data, sizeof(uint8_t), size, sdImage); 123 | delete[] data; 124 | return 1; 125 | } 126 | 127 | int Dldi::clearStatus() { 128 | // Dummy function 129 | return (sdImage ? 1 : 0); 130 | } 131 | 132 | int Dldi::shutdown() { 133 | // Close the SD image 134 | if (!sdImage) return 0; 135 | fclose(sdImage); 136 | sdImage = nullptr; 137 | return 1; 138 | } 139 | -------------------------------------------------------------------------------- /src/dldi.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | enum DldiFunc { 28 | DLDI_START = 0xF0000000, 29 | DLDI_INSERT, 30 | DLDI_READ, 31 | DLDI_WRITE, 32 | DLDI_CLEAR, 33 | DLDI_STOP 34 | }; 35 | 36 | class Dldi { 37 | public: 38 | Dldi(Core *core): core(core) {} 39 | ~Dldi(); 40 | 41 | void patchRom(uint8_t *rom, uint32_t offset, uint32_t size); 42 | bool isPatched() { return patched; } 43 | 44 | int startup(); 45 | int isInserted(); 46 | int readSectors(bool arm7, uint32_t sector, uint32_t numSectors, uint32_t buf); 47 | int writeSectors(bool arm7, uint32_t sector, uint32_t numSectors, uint32_t buf); 48 | int clearStatus(); 49 | int shutdown(); 50 | 51 | private: 52 | Core *core; 53 | bool patched = false; 54 | FILE *sdImage = nullptr; 55 | }; 56 | -------------------------------------------------------------------------------- /src/dma.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | class Dma { 28 | public: 29 | Dma(Core *core, bool cpu): core(core), cpu(cpu) {} 30 | void saveState(FILE *file); 31 | void loadState(FILE *file); 32 | 33 | void transfer(int channel); 34 | void trigger(int mode, uint8_t channels = 0xF); 35 | 36 | uint32_t readDmaSad(int channel) { return dmaSad[channel]; } 37 | uint32_t readDmaDad(int channel) { return dmaDad[channel]; } 38 | uint32_t readDmaCnt(int channel); 39 | 40 | void writeDmaSad(int channel, uint32_t mask, uint32_t value); 41 | void writeDmaDad(int channel, uint32_t mask, uint32_t value); 42 | void writeDmaCnt(int channel, uint32_t mask, uint32_t value); 43 | 44 | private: 45 | Core *core; 46 | bool cpu; 47 | 48 | uint32_t srcAddrs[4] = {}; 49 | uint32_t dstAddrs[4] = {}; 50 | uint32_t wordCounts[4] = {}; 51 | 52 | uint32_t dmaSad[4] = {}; 53 | uint32_t dmaDad[4] = {}; 54 | uint32_t dmaCnt[4] = {}; 55 | }; 56 | -------------------------------------------------------------------------------- /src/gpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "defines.h" 29 | 30 | class Core; 31 | 32 | class Gpu { 33 | public: 34 | Gpu(Core *core); 35 | ~Gpu(); 36 | 37 | void saveState(FILE *file); 38 | void loadState(FILE *file); 39 | 40 | bool getFrame(uint32_t *out, bool gbaCrop); 41 | void invalidate3D() { dirty3D |= BIT(0); } 42 | 43 | void gbaScanline240(); 44 | void gbaScanline308(); 45 | void scanline256(); 46 | void scanline355(); 47 | 48 | uint16_t readDispStat(bool cpu) { return dispStat[cpu]; } 49 | uint16_t readVCount() { return vCount; } 50 | uint32_t readDispCapCnt() { return dispCapCnt; } 51 | uint16_t readPowCnt1() { return powCnt1; } 52 | 53 | void writeDispStat(bool cpu, uint16_t mask, uint16_t value); 54 | void writeDispCapCnt(uint32_t mask, uint32_t value); 55 | void writePowCnt1(uint16_t mask, uint16_t value); 56 | 57 | private: 58 | Core *core; 59 | 60 | struct Buffers { 61 | uint32_t *framebuffer = nullptr; 62 | uint32_t *hiRes3D = nullptr; 63 | bool top3D = false; 64 | }; 65 | 66 | std::queue framebuffers; 67 | std::atomic ready; 68 | std::mutex mutex; 69 | 70 | std::atomic running; 71 | std::atomic drawing; 72 | std::thread *thread = nullptr; 73 | 74 | int frames = 0; 75 | bool gbaBlock = true; 76 | bool displayCapture = false; 77 | uint8_t dirty3D = 0; 78 | 79 | uint16_t dispStat[2] = {}; 80 | uint16_t vCount = 0; 81 | uint32_t dispCapCnt = 0; 82 | uint16_t powCnt1 = 0; 83 | 84 | static uint32_t rgb5ToRgb8(uint32_t color); 85 | static uint32_t rgb6ToRgb8(uint32_t color); 86 | static uint16_t rgb6ToRgb5(uint32_t color); 87 | 88 | void drawGbaThreaded(); 89 | void drawThreaded(); 90 | }; 91 | -------------------------------------------------------------------------------- /src/gpu_2d.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | class Gpu2D { 28 | public: 29 | Gpu2D(Core *core, bool engine); 30 | void saveState(FILE *file); 31 | void loadState(FILE *file); 32 | 33 | void reloadRegisters(); 34 | void updateWindows(int line); 35 | void drawGbaScanline(int line); 36 | void drawScanline(int line); 37 | 38 | uint32_t *getFramebuffer() { return framebuffer; } 39 | uint32_t *getRawLine() { return layers[0]; } 40 | 41 | uint32_t readDispCnt() { return dispCnt; } 42 | uint16_t readBgCnt(int bg) { return bgCnt[bg]; } 43 | uint16_t readWinIn() { return winIn; } 44 | uint16_t readWinOut() { return winOut; } 45 | uint16_t readBldCnt() { return bldCnt; } 46 | uint16_t readBldAlpha() { return bldAlpha; } 47 | uint16_t readMasterBright() { return masterBright; } 48 | 49 | void writeDispCnt(uint32_t mask, uint32_t value); 50 | void writeBgCnt(int bg, uint16_t mask, uint16_t value); 51 | void writeBgHOfs(int bg, uint16_t mask, uint16_t value); 52 | void writeBgVOfs(int bg, uint16_t mask, uint16_t value); 53 | void writeBgPA(int bg, uint16_t mask, uint16_t value); 54 | void writeBgPB(int bg, uint16_t mask, uint16_t value); 55 | void writeBgPC(int bg, uint16_t mask, uint16_t value); 56 | void writeBgPD(int bg, uint16_t mask, uint16_t value); 57 | void writeBgX(int bg, uint32_t mask, uint32_t value); 58 | void writeBgY(int bg, uint32_t mask, uint32_t value); 59 | void writeWinH(int win, uint16_t mask, uint16_t value); 60 | void writeWinV(int win, uint16_t mask, uint16_t value); 61 | void writeWinIn(uint16_t mask, uint16_t value); 62 | void writeWinOut(uint16_t mask, uint16_t value); 63 | void writeMosaic(uint16_t mask, uint16_t value); 64 | void writeBldCnt(uint16_t mask, uint16_t value); 65 | void writeBldAlpha(uint16_t mask, uint16_t value); 66 | void writeBldY(uint8_t value); 67 | void writeMasterBright(uint16_t mask, uint16_t value); 68 | 69 | private: 70 | Core *core; 71 | bool engine; 72 | 73 | uint32_t bgVramAddr, objVramAddr; 74 | uint8_t *palette, *oam; 75 | uint8_t **extPalettes; 76 | 77 | uint32_t framebuffer[256 * 192] = {}; 78 | uint32_t layers[2][256] = {}; 79 | int8_t priorities[2][256] = {}; 80 | int8_t blendBits[2][256] = {}; 81 | 82 | int internalX[2] = {}; 83 | int internalY[2] = {}; 84 | bool winHFlip[2] = {}; 85 | bool winVFlag[2] = {}; 86 | 87 | uint32_t dispCnt = 0; 88 | uint16_t bgCnt[4] = {}; 89 | uint16_t bgHOfs[4] = {}; 90 | uint16_t bgVOfs[4] = {}; 91 | int16_t bgPA[2] = {}; 92 | int16_t bgPB[2] = {}; 93 | int16_t bgPC[2] = {}; 94 | int16_t bgPD[2] = {}; 95 | int32_t bgX[2] = {}; 96 | int32_t bgY[2] = {}; 97 | uint16_t winX1[2] = {}; 98 | uint16_t winX2[2] = {}; 99 | uint16_t winY1[2] = {}; 100 | uint16_t winY2[2] = {}; 101 | uint16_t winIn = 0; 102 | uint16_t winOut = 0; 103 | uint16_t bldCnt = 0; 104 | uint16_t mosaic = 0; 105 | uint16_t bldAlpha = 0; 106 | uint8_t bldY = 0; 107 | uint16_t masterBright = 0; 108 | 109 | static uint32_t rgb5ToRgb6(uint32_t color); 110 | 111 | void drawBgPixel(int bg, int line, int x, uint32_t pixel); 112 | void drawObjPixel(int line, int x, uint32_t pixel, int8_t priority); 113 | 114 | template void drawText(int bg, int line); 115 | template void drawAffine(int bg, int line); 116 | void drawExtended(int bg, int line); 117 | void drawExtendedGba(int bg, int line); 118 | void drawLarge(int bg, int line); 119 | template void drawObjects(int line, bool window); 120 | }; 121 | -------------------------------------------------------------------------------- /src/gpu_3d_renderer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | class Core; 28 | struct Vertex; 29 | struct _Polygon; 30 | 31 | class Gpu3DRenderer { 32 | public: 33 | Gpu3DRenderer(Core *core); 34 | ~Gpu3DRenderer(); 35 | 36 | void saveState(FILE *file); 37 | void loadState(FILE *file); 38 | 39 | void drawScanline(int line); 40 | uint32_t *getLine(int line); 41 | 42 | uint16_t readDisp3DCnt() { return disp3DCnt; } 43 | 44 | void writeDisp3DCnt(uint16_t mask, uint16_t value); 45 | void writeEdgeColor(int index, uint16_t mask, uint16_t value); 46 | void writeClearColor(uint32_t mask, uint32_t value); 47 | void writeClearDepth(uint16_t mask, uint16_t value); 48 | void writeFogColor(uint32_t mask, uint32_t value); 49 | void writeFogOffset(uint16_t mask, uint16_t value); 50 | void writeFogTable(int index, uint8_t value); 51 | void writeToonTable(int index, uint16_t mask, uint16_t value); 52 | 53 | private: 54 | Core *core; 55 | 56 | bool resShift = false; 57 | uint32_t framebuffer[2][256 * 192 * 4] = {}; 58 | int32_t depthBuffer[2][256 * 192 * 4] = {}; 59 | uint32_t attribBuffer[2][256 * 192 * 4] = {}; 60 | uint8_t stencilBuffer[256 * 192 * 4] = {}; 61 | bool stencilClear[256 * 2] = {}; 62 | 63 | int polygonTop[2048] = {}; 64 | int polygonBot[2048] = {}; 65 | 66 | uint8_t activeThreads = 0; 67 | std::vector threads; 68 | std::atomic ready[192 * 2]; 69 | 70 | uint16_t disp3DCnt = 0; 71 | uint16_t edgeColor[8] = {}; 72 | uint32_t clearColor = 0; 73 | uint16_t clearDepth = 0; 74 | uint32_t fogColor = 0; 75 | uint16_t fogOffset = 0; 76 | uint8_t fogTable[32] = {}; 77 | uint16_t toonTable[32] = {}; 78 | 79 | static uint32_t rgba5ToRgba6(uint32_t color); 80 | 81 | uint32_t *getLine1(int line); 82 | 83 | void drawThreaded(int thread); 84 | void drawScanline1(int line); 85 | void finishScanline(int line); 86 | 87 | uint8_t *getTexture(uint32_t address); 88 | uint8_t *getPalette(uint32_t address); 89 | 90 | static uint32_t interpolateLinear(uint32_t v1, uint32_t v2, uint32_t x1, uint32_t x, uint32_t x2); 91 | static uint32_t interpolateLinRev(uint32_t v1, uint32_t v2, uint32_t x1, uint32_t x, uint32_t x2); 92 | static uint32_t interpolateFactor(uint32_t factor, uint32_t shift, uint32_t v1, uint32_t v2); 93 | static uint32_t interpolateColor(uint32_t c1, uint32_t c2, uint32_t x1, uint32_t x, uint32_t x2); 94 | 95 | uint32_t readTexture(_Polygon *polygon, int s, int t); 96 | void drawPolygon(int line, int polygonIndex); 97 | }; 98 | -------------------------------------------------------------------------------- /src/hle_arm7.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include "core.h" 21 | 22 | void HleArm7::init() { 23 | // Permanently halt the ARM7 and set initial IPC state 24 | core->interpreter[1].halt(2); 25 | core->ipc.writeIpcSync(1, -1, 0x700); 26 | core->ipc.writeIpcFifoCnt(1, -1, 0x8000); 27 | } 28 | 29 | void HleArm7::saveState(FILE *file) { 30 | // Write state data to the file 31 | fwrite(&inited, 1, sizeof(inited), file); 32 | fwrite(&autoTouch, 1, sizeof(autoTouch), file); 33 | } 34 | 35 | void HleArm7::loadState(FILE *file) { 36 | // Read state data from the file 37 | fread(&inited, 1, sizeof(inited), file); 38 | fread(&autoTouch, 1, sizeof(autoTouch), file); 39 | } 40 | 41 | void HleArm7::ipcSync(uint8_t value) { 42 | // Catch unhandled HLE IPC sync requests 43 | if (inited) { 44 | LOG_CRIT("Unhandled HLE IPC sync sent after initialization\n"); 45 | return; 46 | } 47 | 48 | // During init, decrement the sync value and send it back 49 | if (value > 0) 50 | return core->ipc.writeIpcSync(1, -1, (value - 1) << 8); 51 | 52 | // Set subsystem init flags and finish the init process 53 | core->memory.write(1, 0x27FFF8C, 0x3FFF0); 54 | inited = true; 55 | } 56 | 57 | void HleArm7::ipcFifo(uint32_t value) { 58 | // Handle FIFO commands based on the subsystem tag 59 | if (!inited) return; 60 | switch (value & 0x1F) { // Subsystem 61 | case 0x6: // Touch screen 62 | // Poll touch input manually or enable auto-polling 63 | if ((value & 0xC0000000) == 0xC0000000) { 64 | pollTouch(value | BIT(21)); 65 | } 66 | else if ((value & 0xC0000000) == 0x40000000) { 67 | autoTouch = (value & BIT(22)); 68 | pollTouch(0xC0204006); 69 | } 70 | return; 71 | 72 | default: 73 | // Stub unknown FIFO commands by replying with the same value 74 | LOG_CRIT("Unknown HLE IPC FIFO command: 0x%X\n", value); 75 | return core->ipc.writeIpcFifoSend(1, -1, value); 76 | } 77 | } 78 | 79 | void HleArm7::runFrame() { 80 | // Automatically poll extra keys and touch if enabled 81 | if (!inited) return; 82 | core->memory.write(1, 0x27FFFA8, (core->input.readExtKeyIn() & 0xB) << 10); 83 | if (autoTouch) pollTouch(0xC0240006); 84 | } 85 | 86 | void HleArm7::pollTouch(uint32_t value) { 87 | // Update touch values in shared memory and send a FIFO reply 88 | if (core->input.readExtKeyIn() & BIT(6)) { // Released 89 | core->memory.write(1, 0x27FFFAA, 0x000); 90 | core->memory.write(1, 0x27FFFAC, 0x600); 91 | } 92 | else { // Pressed 93 | core->memory.write(1, 0x27FFFAA, (core->spi.touchX >> 0)); 94 | core->memory.write(1, 0x27FFFAC, (core->spi.touchY >> 4) | 0x100); 95 | } 96 | core->ipc.writeIpcFifoSend(1, -1, value); 97 | } 98 | -------------------------------------------------------------------------------- /src/hle_arm7.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | class HleArm7 { 28 | public: 29 | HleArm7(Core *core): core(core) {} 30 | void init(); 31 | 32 | void saveState(FILE *file); 33 | void loadState(FILE *file); 34 | 35 | void ipcSync(uint8_t value); 36 | void ipcFifo(uint32_t value); 37 | void runFrame(); 38 | 39 | private: 40 | Core *core; 41 | bool inited = false; 42 | bool autoTouch = false; 43 | 44 | void pollTouch(uint32_t value); 45 | }; 46 | -------------------------------------------------------------------------------- /src/hle_bios.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | class HleBios { 28 | public: 29 | static int (HleBios::*swiTable9[0x21])(uint32_t**); 30 | static int (HleBios::*swiTable7[0x21])(uint32_t**); 31 | static int (HleBios::*swiTableGba[0x21])(uint32_t**); 32 | 33 | HleBios(Core *core, bool arm7, int (HleBios::**swiTable)(uint32_t**)): 34 | core(core), arm7(arm7), swiTable(swiTable) {} 35 | void saveState(FILE *file); 36 | void loadState(FILE *file); 37 | 38 | int execute(uint8_t vector, uint32_t **registers); 39 | void checkWaitFlags(); 40 | bool shouldCheck() { return waitFlags; } 41 | 42 | int swiRegRamReset(uint32_t **registers); 43 | int swiWaitByLoop(uint32_t **registers); 44 | int swiInterruptWait(uint32_t **registers); 45 | int swiVBlankIntrWait(uint32_t **registers); 46 | int swiHalt(uint32_t **registers); 47 | int swiSleep(uint32_t **registers); 48 | int swiSoundBias(uint32_t **registers); 49 | int swiDivide(uint32_t **registers); 50 | int swiDivArm(uint32_t **registers); 51 | int swiSquareRoot(uint32_t **registers); 52 | int swiArcTan(uint32_t **registers); 53 | int swiArcTan2(uint32_t **registers); 54 | int swiCpuSet(uint32_t **registers); 55 | int swiCpuFastSet(uint32_t **registers); 56 | int swiGetCrc16(uint32_t **registers); 57 | int swiIsDebugger(uint32_t **registers); 58 | int swiBgAffineSet(uint32_t **registers); 59 | int swiObjAffineSet(uint32_t **registers); 60 | int swiBitUnpack(uint32_t **registers); 61 | int swiLz77Uncomp(uint32_t **registers); 62 | int swiHuffUncomp(uint32_t **registers); 63 | int swiRunlenUncomp(uint32_t **registers); 64 | int swiDiffUnfilt8(uint32_t **registers); 65 | int swiDiffUnfilt16(uint32_t **registers); 66 | int swiGetSineTable(uint32_t **registers); 67 | int swiGetPitchTable(uint32_t **registers); 68 | int swiGetVolumeTable(uint32_t **registers); 69 | int swiUnknown(uint32_t **registers); 70 | 71 | private: 72 | Core *core; 73 | bool arm7; 74 | int (HleBios::**swiTable)(uint32_t**); 75 | 76 | static const uint16_t affineTable[0x100]; 77 | uint32_t waitFlags = 0; 78 | }; 79 | -------------------------------------------------------------------------------- /src/input.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include "core.h" 21 | 22 | void Input::pressKey(int key) { 23 | // Clear key bits to indicate presses 24 | if (key < 10) // A, B, select, start, right, left, up, down, R, L 25 | keyInput &= ~BIT(key); 26 | else if (key < 12) // X, Y 27 | extKeyIn &= ~BIT(key - 10); 28 | } 29 | 30 | void Input::releaseKey(int key) { 31 | // Set key bits to indicate releases 32 | if (key < 10) // A, B, select, start, right, left, up, down, R, L 33 | keyInput |= BIT(key); 34 | else if (key < 12) // X, Y 35 | extKeyIn |= BIT(key - 10); 36 | } 37 | 38 | void Input::pressScreen() { 39 | // Clear the pen down bit to indicate a touch press 40 | extKeyIn &= ~BIT(6); 41 | } 42 | 43 | void Input::releaseScreen() { 44 | // Set the pen down bit to indicate a touch release 45 | extKeyIn |= BIT(6); 46 | } 47 | -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | class Core; 25 | 26 | class Input { 27 | public: 28 | Input(Core *core): core(core) {} 29 | 30 | void pressKey(int key); 31 | void releaseKey(int key); 32 | void pressScreen(); 33 | void releaseScreen(); 34 | 35 | uint16_t readKeyInput() { return keyInput; } 36 | uint16_t readExtKeyIn() { return extKeyIn; } 37 | 38 | private: 39 | Core *core; 40 | 41 | uint16_t keyInput = 0x03FF; 42 | uint16_t extKeyIn = 0x007F; 43 | }; 44 | -------------------------------------------------------------------------------- /src/ipc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | class Core; 27 | 28 | class Ipc { 29 | public: 30 | Ipc(Core *core): core(core) {} 31 | void saveState(FILE *file); 32 | void loadState(FILE *file); 33 | 34 | uint16_t readIpcSync(bool arm7) { return ipcSync[arm7]; } 35 | uint16_t readIpcFifoCnt(bool arm7) { return ipcFifoCnt[arm7]; } 36 | uint32_t readIpcFifoRecv(bool arm7); 37 | 38 | void writeIpcSync(bool arm7, uint16_t mask, uint16_t value); 39 | void writeIpcFifoCnt(bool arm7, uint16_t mask, uint16_t value); 40 | void writeIpcFifoSend(bool arm7, uint32_t mask, uint32_t value); 41 | 42 | private: 43 | Core *core; 44 | std::deque fifos[2]; 45 | 46 | uint16_t ipcSync[2] = {}; 47 | uint16_t ipcFifoCnt[2] = { 0x0101, 0x0101 }; 48 | uint32_t ipcFifoRecv[2] = {}; 49 | }; 50 | -------------------------------------------------------------------------------- /src/rtc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include "defines.h" 24 | 25 | class Core; 26 | 27 | class Rtc { 28 | public: 29 | Rtc(Core *core): core(core) {} 30 | void saveState(FILE *file); 31 | void loadState(FILE *file); 32 | 33 | void enableGpRtc() { gpRtc = true; } 34 | void reset(); 35 | 36 | uint8_t readRtc(); 37 | uint16_t readGpData(); 38 | uint16_t readGpDirection() { return gpDirection; } 39 | uint16_t readGpControl() { return gpControl; } 40 | 41 | void writeRtc(uint8_t value); 42 | void writeGpData(uint16_t value, uint16_t mask); 43 | void writeGpDirection(uint16_t value, uint16_t mask); 44 | void writeGpControl(uint16_t value, uint16_t mask); 45 | 46 | private: 47 | Core *core; 48 | bool gpRtc = false; 49 | 50 | bool csCur = false; 51 | bool sckCur = false; 52 | bool sioCur = false; 53 | 54 | uint8_t writeCount = 0; 55 | uint8_t command = 0; 56 | uint8_t control = 0; 57 | uint8_t dateTime[7] = {}; 58 | 59 | uint8_t rtc = 0; 60 | uint16_t gpDirection = 0; 61 | uint16_t gpControl = 0; 62 | 63 | void updateRtc(bool cs, bool sck, bool sio); 64 | void updateDateTime(); 65 | 66 | bool readRegister(uint8_t index); 67 | void writeRegister(uint8_t index, bool value); 68 | }; 69 | -------------------------------------------------------------------------------- /src/save_states.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include "core.h" 21 | 22 | const char *SaveStates::stateTag = "NOOD"; 23 | const uint32_t SaveStates::stateVersion = 7; 24 | 25 | void SaveStates::setPath(std::string path, bool gba) { 26 | // Set the NDS or GBA state path 27 | (gba ? gbaPath : ndsPath) = path; 28 | } 29 | 30 | void SaveStates::setFd(int fd, bool gba) { 31 | // Set the NDS or GBA state descriptor 32 | (gba ? gbaFd : ndsFd) = fd; 33 | } 34 | 35 | FILE *SaveStates::openFile(const char *mode) { 36 | // Open the NDS or GBA state file based on what's running 37 | if (gbaFd != -1 && (core->gbaMode || ndsFd == -1)) 38 | return fdopen(dup(gbaFd), mode); 39 | else if (ndsFd != -1) 40 | return fdopen(dup(ndsFd), mode); 41 | else if (gbaPath != "" && (core->gbaMode || ndsPath == "")) 42 | return fopen(gbaPath.c_str(), mode); 43 | else if (ndsPath != "") 44 | return fopen(ndsPath.c_str(), mode); 45 | return nullptr; 46 | } 47 | 48 | StateResult SaveStates::checkState() { 49 | // Try to open the state file, if it exists 50 | FILE *file = openFile("rb"); 51 | if (!file) return STATE_FILE_FAIL; 52 | fseek(file, 0, SEEK_END); 53 | uint32_t size = ftell(file); 54 | fseek(file, 0, SEEK_SET); 55 | if (size == 0) return STATE_FILE_FAIL; 56 | 57 | // Get header values from the file for comparison 58 | uint8_t tag[4]; 59 | uint32_t version; 60 | fread(tag, sizeof(uint8_t), 4, file); 61 | fread(&version, sizeof(uint32_t), 1, file); 62 | fclose(file); 63 | 64 | // Check if the format tag matches 65 | for (int i = 0; i < 4; i++) 66 | if (tag[i] != stateTag[i]) 67 | return STATE_FORMAT_FAIL; 68 | 69 | // Check if the state version matches 70 | if (version != stateVersion) 71 | return STATE_VERSION_FAIL; 72 | return STATE_SUCCESS; 73 | } 74 | 75 | bool SaveStates::saveState() { 76 | // Open the state file and write the header 77 | FILE *file = openFile("wb"); 78 | if (!file) return false; 79 | fwrite(stateTag, sizeof(uint8_t), 4, file); 80 | fwrite(&stateVersion, sizeof(uint32_t), 1, file); 81 | 82 | // Save the state of every component 83 | core->saveState(file); 84 | core->cartridgeGba.saveState(file); 85 | core->cartridgeNds.saveState(file); 86 | core->cp15.saveState(file); 87 | core->divSqrt.saveState(file); 88 | core->dma[0].saveState(file); 89 | core->dma[1].saveState(file); 90 | core->gpu.saveState(file); 91 | core->gpu2D[0].saveState(file); 92 | core->gpu2D[1].saveState(file); 93 | core->gpu3D.saveState(file); 94 | core->gpu3DRenderer.saveState(file); 95 | core->hleArm7.saveState(file); 96 | core->hleBios[0].saveState(file); 97 | core->hleBios[1].saveState(file); 98 | core->hleBios[2].saveState(file); 99 | core->interpreter[0].saveState(file); 100 | core->interpreter[1].saveState(file); 101 | core->ipc.saveState(file); 102 | core->memory.saveState(file); 103 | core->rtc.saveState(file); 104 | core->spi.saveState(file); 105 | core->spu.saveState(file); 106 | core->timers[0].saveState(file); 107 | core->timers[1].saveState(file); 108 | core->wifi.saveState(file); 109 | fclose(file); 110 | return true; 111 | } 112 | 113 | bool SaveStates::loadState() { 114 | // Open the state file and read past the header 115 | FILE *file = openFile("rb"); 116 | if (!file) return false; 117 | fseek(file, 8, SEEK_SET); 118 | 119 | // Load the state of every component 120 | core->loadState(file); 121 | core->cartridgeGba.loadState(file); 122 | core->cartridgeNds.loadState(file); 123 | core->cp15.loadState(file); 124 | core->divSqrt.loadState(file); 125 | core->dma[0].loadState(file); 126 | core->dma[1].loadState(file); 127 | core->gpu.loadState(file); 128 | core->gpu2D[0].loadState(file); 129 | core->gpu2D[1].loadState(file); 130 | core->gpu3D.loadState(file); 131 | core->gpu3DRenderer.loadState(file); 132 | core->hleArm7.loadState(file); 133 | core->hleBios[0].loadState(file); 134 | core->hleBios[1].loadState(file); 135 | core->hleBios[2].loadState(file); 136 | core->interpreter[0].loadState(file); 137 | core->interpreter[1].loadState(file); 138 | core->ipc.loadState(file); 139 | core->memory.loadState(file); 140 | core->rtc.loadState(file); 141 | core->spi.loadState(file); 142 | core->spu.loadState(file); 143 | core->timers[0].loadState(file); 144 | core->timers[1].loadState(file); 145 | core->wifi.loadState(file); 146 | fclose(file); 147 | return true; 148 | } 149 | -------------------------------------------------------------------------------- /src/save_states.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | enum StateResult { 28 | STATE_SUCCESS, 29 | STATE_FILE_FAIL, 30 | STATE_FORMAT_FAIL, 31 | STATE_VERSION_FAIL 32 | }; 33 | 34 | class SaveStates { 35 | public: 36 | SaveStates(Core *core): core(core) {} 37 | void setPath(std::string path, bool gba); 38 | void setFd(int fd, bool gba); 39 | 40 | StateResult checkState(); 41 | bool saveState(); 42 | bool loadState(); 43 | 44 | private: 45 | Core *core; 46 | std::string ndsPath, gbaPath; 47 | int ndsFd = -1, gbaFd = -1; 48 | 49 | static const char *stateTag; 50 | static const uint32_t stateVersion; 51 | 52 | FILE *openFile(const char *mode); 53 | }; 54 | -------------------------------------------------------------------------------- /src/settings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include 21 | #include "core.h" 22 | 23 | int Settings::directBoot = 1; 24 | int Settings::romInRam = 0; 25 | int Settings::fpsLimiter = 1; 26 | int Settings::frameskip = 0; 27 | int Settings::threaded2D = 1; 28 | int Settings::threaded3D = 1; 29 | int Settings::highRes3D = 0; 30 | int Settings::screenGhost = 0; 31 | int Settings::emulateAudio = 1; 32 | int Settings::audio16Bit = 1; 33 | int Settings::savesFolder = 0; 34 | int Settings::statesFolder = 1; 35 | int Settings::cheatsFolder = 1; 36 | int Settings::screenFilter = 2; 37 | int Settings::arm7Hle = 0; 38 | int Settings::dsiMode = 0; 39 | 40 | std::string Settings::bios9Path = "bios9.bin"; 41 | std::string Settings::bios7Path = "bios7.bin"; 42 | std::string Settings::firmwarePath = "firmware.bin"; 43 | std::string Settings::gbaBiosPath = "gba_bios.bin"; 44 | std::string Settings::sdImagePath = "sd.img"; 45 | std::string Settings::basePath = "."; 46 | 47 | std::vector Settings::settings = { 48 | Setting("directBoot", &directBoot, false), 49 | Setting("romInRam", &romInRam, false), 50 | Setting("fpsLimiter", &fpsLimiter, false), 51 | Setting("frameskip", &frameskip, false), 52 | Setting("threaded2D", &threaded2D, false), 53 | Setting("threaded3D", &threaded3D, false), 54 | Setting("highRes3D", &highRes3D, false), 55 | Setting("screenGhost", &screenGhost, false), 56 | Setting("emulateAudio", &emulateAudio, false), 57 | Setting("audio16Bit", &audio16Bit, false), 58 | Setting("savesFolder", &savesFolder, false), 59 | Setting("statesFolder", &statesFolder, false), 60 | Setting("cheatsFolder", &cheatsFolder, false), 61 | Setting("screenFilter", &screenFilter, false), 62 | Setting("arm7Hle", &arm7Hle, false), 63 | Setting("dsiMode", &dsiMode, false), 64 | Setting("bios9Path", &bios9Path, true), 65 | Setting("bios7Path", &bios7Path, true), 66 | Setting("firmwarePath", &firmwarePath, true), 67 | Setting("gbaBiosPath", &gbaBiosPath, true), 68 | Setting("sdImagePath", &sdImagePath, true) 69 | }; 70 | 71 | void Settings::add(std::vector &settings) { 72 | // Add additional settings to be loaded from the settings file 73 | Settings::settings.insert(Settings::settings.end(), settings.begin(), settings.end()); 74 | } 75 | 76 | bool Settings::load(std::string path) { 77 | // Set the base path and ensure all folders exist 78 | mkdir((basePath = path).c_str() MKDIR_ARGS); 79 | mkdir((basePath + "/saves").c_str() MKDIR_ARGS); 80 | mkdir((basePath + "/states").c_str() MKDIR_ARGS); 81 | mkdir((basePath + "/cheats").c_str() MKDIR_ARGS); 82 | 83 | // Open the settings file or set defaults if it doesn't exist 84 | FILE *file = fopen((basePath + "/noods.ini").c_str(), "r"); 85 | if (!file) { 86 | Settings::bios9Path = basePath + "/bios9.bin"; 87 | Settings::bios7Path = basePath + "/bios7.bin"; 88 | Settings::firmwarePath = basePath + "/firmware.bin"; 89 | Settings::gbaBiosPath = basePath + "/gba_bios.bin"; 90 | Settings::sdImagePath = basePath + "/sd.img"; 91 | Settings::save(); 92 | return false; 93 | } 94 | 95 | // Read each line of the settings file and load values from them 96 | char data[512]; 97 | while (fgets(data, 512, file) != nullptr) { 98 | std::string line = data; 99 | size_t split = line.find('='); 100 | std::string name = line.substr(0, split); 101 | for (size_t i = 0; i < settings.size(); i++) { 102 | if (name != settings[i].name) continue; 103 | std::string value = line.substr(split + 1, line.size() - split - 2); 104 | if (settings[i].isString) 105 | *(std::string*)settings[i].value = value; 106 | else if (value[0] >= '0' && value[0] <= '9') 107 | *(int*)settings[i].value = stoi(value); 108 | break; 109 | } 110 | } 111 | 112 | // Close the file after reading it 113 | fclose(file); 114 | return true; 115 | } 116 | 117 | bool Settings::save() { 118 | // Attempt to open the settings file 119 | FILE *file = fopen((basePath + "/noods.ini").c_str(), "w"); 120 | if (!file) return false; 121 | 122 | // Write each setting to the settings file 123 | for (size_t i = 0; i < settings.size(); i++) { 124 | std::string value = settings[i].isString ? 125 | *(std::string*)settings[i].value : std::to_string(*(int*)settings[i].value); 126 | fprintf(file, "%s=%s\n", settings[i].name.c_str(), value.c_str()); 127 | } 128 | 129 | // Close the file after writing it 130 | fclose(file); 131 | return true; 132 | } 133 | -------------------------------------------------------------------------------- /src/settings.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | struct Setting { 26 | std::string name; 27 | void *value; 28 | bool isString; 29 | 30 | Setting(std::string name, void *value, bool isString): 31 | name(name), value(value), isString(isString) {} 32 | }; 33 | 34 | class Settings { 35 | public: 36 | static int directBoot; 37 | static int romInRam; 38 | static int fpsLimiter; 39 | static int frameskip; 40 | static int threaded2D; 41 | static int threaded3D; 42 | static int highRes3D; 43 | static int screenGhost; 44 | static int emulateAudio; 45 | static int audio16Bit; 46 | static int savesFolder; 47 | static int statesFolder; 48 | static int cheatsFolder; 49 | static int screenFilter; 50 | static int arm7Hle; 51 | static int dsiMode; 52 | 53 | static std::string bios9Path; 54 | static std::string bios7Path; 55 | static std::string firmwarePath; 56 | static std::string gbaBiosPath; 57 | static std::string sdImagePath; 58 | static std::string basePath; 59 | 60 | static void add(std::vector &settings); 61 | static bool load(std::string path = "."); 62 | static bool save(); 63 | 64 | private: 65 | static std::vector settings; 66 | Settings() {} // Private to prevent instantiation 67 | }; 68 | -------------------------------------------------------------------------------- /src/spi.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | enum Language { 27 | LG_JAPANESE = 0, 28 | LG_ENGLISH, 29 | LG_FRENCH, 30 | LG_GERMAN, 31 | LG_ITALIAN, 32 | LG_SPANISH 33 | }; 34 | 35 | class Core; 36 | 37 | class Spi { 38 | public: 39 | uint16_t touchX = 0x000; 40 | uint16_t touchY = 0xFFF; 41 | 42 | Spi(Core *core): core(core) {} 43 | ~Spi(); 44 | 45 | void saveState(FILE *file); 46 | void loadState(FILE *file); 47 | 48 | bool loadFirmware(); 49 | void directBoot(); 50 | 51 | void setTouch(int x, int y); 52 | void clearTouch(); 53 | 54 | static void setLanguage(Language lang) { language = lang; } 55 | void sendMicData(const int16_t* samples, size_t count, size_t rate); 56 | 57 | uint16_t readSpiCnt() { return spiCnt; } 58 | uint8_t readSpiData() { return spiData; } 59 | 60 | void writeSpiCnt(uint16_t mask, uint16_t value); 61 | void writeSpiData(uint8_t value); 62 | 63 | private: 64 | Core *core; 65 | 66 | static Language language; 67 | uint8_t *firmware = nullptr; 68 | size_t firmSize = 0; 69 | 70 | int16_t *micBuffer = nullptr; 71 | size_t micBufSize = 0; 72 | uint32_t micCycles = 0; 73 | uint32_t micStep = 0; 74 | uint16_t micSample = 0; 75 | std::mutex mutex; 76 | 77 | uint32_t writeCount = 0; 78 | uint32_t address = 0; 79 | uint8_t command = 0; 80 | 81 | uint16_t spiCnt = 0; 82 | uint8_t spiData = 0; 83 | 84 | static uint16_t crc16(uint32_t value, uint8_t *data, size_t size); 85 | }; 86 | -------------------------------------------------------------------------------- /src/spu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | class Core; 30 | 31 | class Spu { 32 | public: 33 | Spu(Core *core); 34 | ~Spu(); 35 | 36 | void saveState(FILE *file); 37 | void loadState(FILE *file); 38 | 39 | uint32_t *getSamples(int count); 40 | void runGbaSample(); 41 | void runSample(); 42 | void gbaFifoTimer(int timer); 43 | 44 | uint8_t readGbaSoundCntL(int channel); 45 | uint16_t readGbaSoundCntH(int channel); 46 | uint16_t readGbaSoundCntX(int channel); 47 | uint16_t readGbaMainSoundCntL() { return gbaMainSoundCntL; } 48 | uint16_t readGbaMainSoundCntH() { return gbaMainSoundCntH; } 49 | uint8_t readGbaMainSoundCntX() { return gbaMainSoundCntX; } 50 | uint16_t readGbaSoundBias() { return gbaSoundBias; } 51 | uint8_t readGbaWaveRam(int index); 52 | 53 | uint32_t readSoundCnt(int channel) { return soundCnt[channel]; } 54 | uint16_t readMainSoundCnt() { return mainSoundCnt; } 55 | uint16_t readSoundBias() { return soundBias; } 56 | uint8_t readSndCapCnt(int channel) { return sndCapCnt[channel]; } 57 | uint32_t readSndCapDad(int channel) { return sndCapDad[channel]; } 58 | 59 | void writeGbaSoundCntL(int channel, uint8_t value); 60 | void writeGbaSoundCntH(int channel, uint16_t mask, uint16_t value); 61 | void writeGbaSoundCntX(int channel, uint16_t mask, uint16_t value); 62 | void writeGbaMainSoundCntL(uint16_t mask, uint16_t value); 63 | void writeGbaMainSoundCntH(uint16_t mask, uint16_t value); 64 | void writeGbaMainSoundCntX(uint8_t value); 65 | void writeGbaSoundBias(uint16_t mask, uint16_t value); 66 | void writeGbaWaveRam(int index, uint8_t value); 67 | void writeGbaFifoA(uint32_t mask, uint32_t value); 68 | void writeGbaFifoB(uint32_t mask, uint32_t value); 69 | 70 | void writeSoundCnt(int channel, uint32_t mask, uint32_t value); 71 | void writeSoundSad(int channel, uint32_t mask, uint32_t value); 72 | void writeSoundTmr(int channel, uint16_t mask, uint16_t value); 73 | void writeSoundPnt(int channel, uint16_t mask, uint16_t value); 74 | void writeSoundLen(int channel, uint32_t mask, uint32_t value); 75 | void writeMainSoundCnt(uint16_t mask, uint16_t value); 76 | void writeSoundBias(uint16_t mask, uint16_t value); 77 | void writeSndCapCnt(int channel, uint8_t value); 78 | void writeSndCapDad(int channel, uint32_t mask, uint32_t value); 79 | void writeSndCapLen(int channel, uint16_t mask, uint16_t value); 80 | 81 | private: 82 | Core *core; 83 | 84 | uint32_t *bufferIn = nullptr, *bufferOut = nullptr; 85 | uint32_t bufferSize = 0, bufferPointer = 0; 86 | 87 | std::condition_variable cond1, cond2; 88 | std::mutex mutex1, mutex2; 89 | std::atomic ready; 90 | 91 | int16_t gbaFrameSequencer = 0; 92 | int32_t gbaSoundTimers[4] = {}; 93 | int8_t gbaEnvelopes[3] = {}; 94 | int8_t gbaEnvTimers[3] = {}; 95 | int8_t gbaSweepTimer = 0; 96 | int8_t gbaWaveDigit = 0; 97 | uint16_t gbaNoiseValue = 0; 98 | 99 | uint8_t gbaWaveRam[2][16] = {}; 100 | std::deque gbaFifos[2]; 101 | int8_t gbaSampleA = 0, gbaSampleB = 0; 102 | 103 | uint16_t enabled = 0; 104 | 105 | static const int indexTable[8]; 106 | static const int16_t adpcmTable[89]; 107 | 108 | int32_t adpcmValue[16] = {}, adpcmLoopValue[16] = {}; 109 | int8_t adpcmIndex[16] = {}, adpcmLoopIndex[16] = {}; 110 | bool adpcmToggle[16] = {}; 111 | 112 | uint8_t dutyCycles[6] = {}; 113 | uint16_t noiseValues[2] = {}; 114 | uint32_t soundCurrent[16] = {}; 115 | uint16_t soundTimers[16] = {}; 116 | uint32_t sndCapCurrent[2] = {}; 117 | uint16_t sndCapTimers[2] = {}; 118 | 119 | uint8_t gbaSoundCntL[2] = {}; 120 | uint16_t gbaSoundCntH[4] = {}; 121 | uint16_t gbaSoundCntX[4] = {}; 122 | uint16_t gbaMainSoundCntL = 0; 123 | uint16_t gbaMainSoundCntH = 0; 124 | uint8_t gbaMainSoundCntX = 0; 125 | uint16_t gbaSoundBias = 0; 126 | 127 | uint32_t soundCnt[16] = {}; 128 | uint32_t soundSad[16] = {}; 129 | uint16_t soundTmr[16] = {}; 130 | uint16_t soundPnt[16] = {}; 131 | uint32_t soundLen[16] = {}; 132 | uint16_t mainSoundCnt = 0; 133 | uint16_t soundBias = 0; 134 | uint8_t sndCapCnt[2] = {}; 135 | uint32_t sndCapDad[2] = {}; 136 | uint16_t sndCapLen[2] = {}; 137 | 138 | void pushSample(int16_t sampleLeft, int16_t sampleRight); 139 | void startChannel(int channel); 140 | }; 141 | -------------------------------------------------------------------------------- /src/timers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #include "core.h" 21 | 22 | void Timers::saveState(FILE *file) { 23 | // Write state data to the file 24 | fwrite(timers, 2, sizeof(timers) / 2, file); 25 | fwrite(shifts, 1, sizeof(shifts), file); 26 | fwrite(endCycles, 4, sizeof(endCycles) / 4, file); 27 | fwrite(tmCntL, 2, sizeof(tmCntL) / 2, file); 28 | fwrite(tmCntH, 2, sizeof(tmCntH) / 2, file); 29 | } 30 | 31 | void Timers::loadState(FILE *file) { 32 | // Read state data from the file 33 | fread(timers, 2, sizeof(timers) / 2, file); 34 | fread(shifts, 1, sizeof(shifts), file); 35 | fread(endCycles, 4, sizeof(endCycles) / 4, file); 36 | fread(tmCntL, 2, sizeof(tmCntL) / 2, file); 37 | fread(tmCntH, 2, sizeof(tmCntH) / 2, file); 38 | } 39 | 40 | void Timers::resetCycles() { 41 | // Adjust timer end cycles for a global cycle reset 42 | for (int i = 0; i < 4; i++) 43 | endCycles[i] -= core->globalCycles; 44 | } 45 | 46 | void Timers::overflow(int timer) { 47 | // Ensure the timer is enabled and the end cycle is correct if not in count-up mode 48 | // The end cycle check ensures that if a timer was changed while running, outdated events are ignored 49 | if (!(tmCntH[timer] & BIT(7)) || ((timer == 0 || !(tmCntH[timer] & BIT(2))) 50 | && endCycles[timer] != core->globalCycles)) return; 51 | 52 | // Reload the timer and schedule another overflow if not in count-up mode 53 | timers[timer] = tmCntL[timer]; 54 | if (timer == 0 || !(tmCntH[timer] & BIT(2))) { 55 | core->schedule(SchedTask(TIMER9_OVERFLOW0 + (arm7 << 2) + timer), (0x10000 - timers[timer]) << shifts[timer]); 56 | endCycles[timer] = core->globalCycles + ((0x10000 - timers[timer]) << shifts[timer]); 57 | } 58 | 59 | // Trigger a timer overflow IRQ if enabled 60 | if (tmCntH[timer] & BIT(6)) 61 | core->interpreter[arm7].sendInterrupt(3 + timer); 62 | 63 | // Trigger a GBA sound FIFO event 64 | if (core->gbaMode && timer < 2) 65 | core->spu.gbaFifoTimer(timer); 66 | 67 | // If the next timer has count-up timing enabled, tick it now 68 | // In count-up timing mode, the timer only ticks when the previous timer overflows 69 | if (timer < 3 && (tmCntH[timer + 1] & BIT(2)) && ++timers[timer + 1] == 0) 70 | overflow(timer + 1); 71 | } 72 | 73 | void Timers::writeTmCntL(int timer, uint16_t mask, uint16_t value) { 74 | // Write to one of the TMCNT_L registers 75 | // This value doesn't affect the current counter, and is instead used as the reload value 76 | tmCntL[timer] = (tmCntL[timer] & ~mask) | (value & mask); 77 | } 78 | 79 | void Timers::writeTmCntH(int timer, uint16_t mask, uint16_t value) { 80 | // Update the current timer value if it's running on the scheduler 81 | bool dirty = false; 82 | if ((tmCntH[timer] & BIT(7)) && (timer == 0 || !(value & BIT(2)))) 83 | timers[timer] = 0x10000 - ((endCycles[timer] - core->globalCycles) >> shifts[timer]); 84 | 85 | // Update the timer shift if the prescaler setting was changed 86 | // The prescaler allows timers to tick at frequencies of f/1, f/64, f/256, or f/1024 (when not in count-up mode) 87 | if (mask & 0xFF) { 88 | uint8_t shift = (((value & 0x3) && (timer == 0 || !(value & BIT(2)))) ? (4 + (value & 0x3) * 2) : 0); 89 | if (shifts[timer] != shift) { 90 | shifts[timer] = shift; 91 | dirty = true; 92 | } 93 | } 94 | 95 | // Reload the counter if the enable bit changes from 0 to 1 96 | if (!(tmCntH[timer] & BIT(7)) && (value & BIT(7))) { 97 | timers[timer] = tmCntL[timer]; 98 | dirty = true; 99 | } 100 | 101 | // Write to one of the TMCNT_H registers 102 | mask &= 0x00C7; 103 | tmCntH[timer] = (tmCntH[timer] & ~mask) | (value & mask); 104 | 105 | // Schedule a timer overflow if the timer changed and isn't in count-up mode 106 | if (dirty && (tmCntH[timer] & BIT(7)) && (timer == 0 || !(tmCntH[timer] & BIT(2)))) { 107 | core->schedule(SchedTask(TIMER9_OVERFLOW0 + (arm7 << 2) + timer), (0x10000 - timers[timer]) << shifts[timer]); 108 | endCycles[timer] = core->globalCycles + ((0x10000 - timers[timer]) << shifts[timer]); 109 | } 110 | } 111 | 112 | uint16_t Timers::readTmCntL(int timer) { 113 | // Read the current timer value, updating it if it's running on the scheduler 114 | if ((tmCntH[timer] & BIT(7)) && (timer == 0 || !(tmCntH[timer] & BIT(2)))) 115 | timers[timer] = 0x10000 - ((endCycles[timer] - core->globalCycles) >> shifts[timer]); 116 | return timers[timer]; 117 | } 118 | -------------------------------------------------------------------------------- /src/timers.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2025 Hydr8gon 3 | 4 | This file is part of NooDS. 5 | 6 | NooDS is free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | NooDS is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with NooDS. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | class Core; 26 | 27 | class Timers { 28 | public: 29 | Timers(Core *core, bool arm7): core(core), arm7(arm7) {} 30 | void saveState(FILE *file); 31 | void loadState(FILE *file); 32 | 33 | void resetCycles(); 34 | void overflow(int timer); 35 | 36 | uint16_t readTmCntH(int timer) { return tmCntH[timer]; } 37 | uint16_t readTmCntL(int timer); 38 | 39 | void writeTmCntL(int timer, uint16_t mask, uint16_t value); 40 | void writeTmCntH(int timer, uint16_t mask, uint16_t value); 41 | 42 | private: 43 | Core *core; 44 | bool arm7; 45 | 46 | uint16_t timers[4] = {}; 47 | uint8_t shifts[4] = {}; 48 | uint32_t endCycles[4] = {}; 49 | 50 | uint16_t tmCntL[4] = {}; 51 | uint16_t tmCntH[4] = {}; 52 | }; 53 | --------------------------------------------------------------------------------