├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bugs.yml │ ├── config.yml │ └── feature-request.yml └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── BUILDING.md ├── LICENSE.txt ├── Makefile ├── README.md ├── assets ├── compile_resources.py ├── restool.py ├── smw_assets.h └── util.py ├── extract_assets.bat ├── other ├── .gitignore ├── dont_run_this_gen_deltas.py ├── extract.py ├── smb1.zst └── smbll.zst ├── run_with_tcc.bat ├── smb1 ├── smb1_00.c ├── smb1_01.c ├── smb1_02.c ├── smb1_08.c ├── smb1_consts.h ├── smb1_cpu_infra.c ├── smb1_funcs.h ├── smb1_rtl.h ├── smb1_spc_player.c └── smb1_variables.h ├── smbll ├── smbll_00.c ├── smbll_01.c ├── smbll_02.c ├── smbll_08.c ├── smbll_0b.c ├── smbll_consts.h ├── smbll_cpu_infra.c ├── smbll_funcs.h ├── smbll_rtl.h └── smbll_variables.h ├── smw.ini ├── smw.sln ├── src ├── .gitignore ├── common_cpu_infra.c ├── common_cpu_infra.h ├── common_rtl.c ├── common_rtl.h ├── config.c ├── config.h ├── consts.h ├── funcs.h ├── glsl_shader.c ├── glsl_shader.h ├── lm.c ├── main.c ├── opengl.c ├── packages.config ├── platform │ ├── switch │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── icon.jpg │ │ ├── smw.ini │ │ └── src │ │ │ ├── switch_impl.c │ │ │ └── switch_impl.h │ └── win32 │ │ ├── resource.h │ │ ├── smw.rc │ │ ├── volume_control.c │ │ └── volume_control.h ├── smw.vcxproj ├── smw.vcxproj.filters ├── smw_00.c ├── smw_01.c ├── smw_02.c ├── smw_03.c ├── smw_04.c ├── smw_05.c ├── smw_07.c ├── smw_0c.c ├── smw_0d.c ├── smw_cpu_infra.c ├── smw_rtl.c ├── smw_rtl.h ├── smw_spc_player.c ├── smw_spc_player.h ├── snes │ ├── apu.c │ ├── apu.h │ ├── cart.c │ ├── cart.h │ ├── cpu.c │ ├── cpu.h │ ├── dma.c │ ├── dma.h │ ├── dsp.c │ ├── dsp.h │ ├── dsp_regs.h │ ├── ppu.c │ ├── ppu.h │ ├── ppu_old.c │ ├── saveload.h │ ├── snes.c │ ├── snes.h │ ├── snes_other.c │ ├── snes_regs.h │ ├── spc.c │ └── spc.h ├── spc_variables.h ├── tracing.c ├── tracing.h ├── types.h ├── util.c ├── util.h └── variables.h └── third_party ├── .gitignore ├── gl_core ├── gl_core_3_1.c └── gl_core_3_1.h └── stb └── stb_image.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignArrayOfStructures: None 7 | AlignConsecutiveAssignments: 8 | Enabled: false 9 | AcrossEmptyLines: false 10 | AcrossComments: false 11 | AlignCompound: false 12 | PadOperators: true 13 | AlignConsecutiveBitFields: 14 | Enabled: false 15 | AcrossEmptyLines: false 16 | AcrossComments: false 17 | AlignCompound: false 18 | PadOperators: false 19 | AlignConsecutiveDeclarations: 20 | Enabled: false 21 | AcrossEmptyLines: false 22 | AcrossComments: false 23 | AlignCompound: false 24 | PadOperators: false 25 | AlignConsecutiveMacros: 26 | Enabled: false 27 | AcrossEmptyLines: false 28 | AcrossComments: false 29 | AlignCompound: false 30 | PadOperators: false 31 | AlignEscapedNewlines: Left 32 | AlignOperands: Align 33 | AlignTrailingComments: true 34 | AllowAllArgumentsOnNextLine: true 35 | AllowAllParametersOfDeclarationOnNextLine: true 36 | AllowShortEnumsOnASingleLine: true 37 | AllowShortBlocksOnASingleLine: Never 38 | AllowShortCaseLabelsOnASingleLine: true 39 | AllowShortFunctionsOnASingleLine: InlineOnly 40 | AllowShortLambdasOnASingleLine: All 41 | AllowShortIfStatementsOnASingleLine: Never 42 | AllowShortLoopsOnASingleLine: false 43 | AlwaysBreakAfterDefinitionReturnType: None 44 | AlwaysBreakAfterReturnType: None 45 | AlwaysBreakBeforeMultilineStrings: true 46 | AlwaysBreakTemplateDeclarations: Yes 47 | AttributeMacros: 48 | - __capability 49 | BinPackArguments: true 50 | BinPackParameters: true 51 | BraceWrapping: 52 | AfterCaseLabel: false 53 | AfterClass: false 54 | AfterControlStatement: Never 55 | AfterEnum: false 56 | AfterFunction: false 57 | AfterNamespace: false 58 | AfterObjCDeclaration: false 59 | AfterStruct: false 60 | AfterUnion: false 61 | AfterExternBlock: false 62 | BeforeCatch: false 63 | BeforeElse: false 64 | BeforeLambdaBody: false 65 | BeforeWhile: false 66 | IndentBraces: false 67 | SplitEmptyFunction: true 68 | SplitEmptyRecord: true 69 | SplitEmptyNamespace: true 70 | BreakBeforeBinaryOperators: None 71 | BreakBeforeConceptDeclarations: Always 72 | BreakBeforeBraces: Attach 73 | BreakBeforeInheritanceComma: false 74 | BreakInheritanceList: BeforeColon 75 | BreakBeforeTernaryOperators: true 76 | BreakConstructorInitializersBeforeComma: false 77 | BreakConstructorInitializers: BeforeColon 78 | BreakAfterJavaFieldAnnotations: false 79 | BreakStringLiterals: true 80 | ColumnLimit: 140 81 | CommentPragmas: '^ IWYU pragma:' 82 | QualifierAlignment: Leave 83 | CompactNamespaces: false 84 | ConstructorInitializerIndentWidth: 4 85 | ContinuationIndentWidth: 4 86 | Cpp11BracedListStyle: true 87 | DeriveLineEnding: true 88 | DerivePointerAlignment: true 89 | DisableFormat: false 90 | EmptyLineAfterAccessModifier: Never 91 | EmptyLineBeforeAccessModifier: LogicalBlock 92 | ExperimentalAutoDetectBinPacking: false 93 | PackConstructorInitializers: NextLine 94 | BasedOnStyle: '' 95 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 96 | AllowAllConstructorInitializersOnNextLine: true 97 | FixNamespaceComments: true 98 | ForEachMacros: 99 | - foreach 100 | - Q_FOREACH 101 | - BOOST_FOREACH 102 | 103 | IfMacros: 104 | - KJ_IF_MAYBE 105 | IncludeBlocks: Regroup 106 | IncludeCategories: 107 | - Regex: '^' 108 | Priority: 2 109 | SortPriority: 0 110 | CaseSensitive: false 111 | - Regex: '^<.*\.h>' 112 | Priority: 1 113 | SortPriority: 0 114 | CaseSensitive: false 115 | - Regex: '^<.*' 116 | Priority: 2 117 | SortPriority: 0 118 | CaseSensitive: false 119 | - Regex: '.*' 120 | Priority: 3 121 | SortPriority: 0 122 | CaseSensitive: false 123 | IncludeIsMainRegex: '([-_](test|unittest))?$' 124 | IncludeIsMainSourceRegex: '' 125 | IndentAccessModifiers: false 126 | IndentCaseLabels: false 127 | IndentCaseBlocks: false 128 | IndentGotoLabels: false 129 | IndentPPDirectives: None 130 | IndentExternBlock: AfterExternBlock 131 | IndentRequiresClause: true 132 | IndentWidth: 2 133 | IndentWrappedFunctionNames: false 134 | InsertBraces: false 135 | InsertTrailingCommas: None 136 | JavaScriptQuotes: Leave 137 | JavaScriptWrapImports: true 138 | KeepEmptyLinesAtTheStartOfBlocks: false 139 | LambdaBodyIndentation: Signature 140 | MacroBlockBegin: '' 141 | MacroBlockEnd: '' 142 | MaxEmptyLinesToKeep: 1 143 | NamespaceIndentation: None 144 | ObjCBinPackProtocolList: Never 145 | ObjCBlockIndentWidth: 2 146 | ObjCBreakBeforeNestedBlockParam: true 147 | ObjCSpaceAfterProperty: false 148 | ObjCSpaceBeforeProtocolList: true 149 | PenaltyBreakAssignment: 2 150 | PenaltyBreakBeforeFirstCallParameter: 1 151 | PenaltyBreakComment: 300 152 | PenaltyBreakFirstLessLess: 120 153 | PenaltyBreakOpenParenthesis: 0 154 | PenaltyBreakString: 1000 155 | PenaltyBreakTemplateDeclaration: 10 156 | PenaltyExcessCharacter: 1000000 157 | PenaltyReturnTypeOnItsOwnLine: 200 158 | PenaltyIndentedWhitespace: 0 159 | PointerAlignment: Right 160 | PPIndentWidth: -1 161 | RawStringFormats: 162 | - Language: Cpp 163 | Delimiters: 164 | - cc 165 | - CC 166 | - cpp 167 | - Cpp 168 | - CPP 169 | - 'c++' 170 | - 'C++' 171 | CanonicalDelimiter: '' 172 | BasedOnStyle: google 173 | - Language: TextProto 174 | Delimiters: 175 | - pb 176 | - PB 177 | - proto 178 | - PROTO 179 | EnclosingFunctions: 180 | - EqualsProto 181 | - EquivToProto 182 | - PARSE_PARTIAL_TEXT_PROTO 183 | - PARSE_TEST_PROTO 184 | - PARSE_TEXT_PROTO 185 | - ParseTextOrDie 186 | - ParseTextProtoOrDie 187 | - ParseTestProto 188 | - ParsePartialTestProto 189 | CanonicalDelimiter: pb 190 | BasedOnStyle: google 191 | ReferenceAlignment: Pointer 192 | ReflowComments: true 193 | RemoveBracesLLVM: false 194 | RequiresClausePosition: OwnLine 195 | SeparateDefinitionBlocks: Leave 196 | ShortNamespaceLines: 1 197 | SortIncludes: CaseSensitive 198 | SortJavaStaticImport: Before 199 | SortUsingDeclarations: true 200 | SpaceAfterCStyleCast: false 201 | SpaceAfterLogicalNot: false 202 | SpaceAfterTemplateKeyword: true 203 | SpaceBeforeAssignmentOperators: true 204 | SpaceBeforeCaseColon: false 205 | SpaceBeforeCpp11BracedList: false 206 | SpaceBeforeCtorInitializerColon: true 207 | SpaceBeforeInheritanceColon: true 208 | SpaceBeforeParens: ControlStatements 209 | SpaceBeforeParensOptions: 210 | AfterControlStatements: true 211 | AfterForeachMacros: true 212 | AfterFunctionDefinitionName: false 213 | AfterFunctionDeclarationName: false 214 | AfterIfMacros: true 215 | AfterOverloadedOperator: false 216 | AfterRequiresInClause: false 217 | AfterRequiresInExpression: false 218 | BeforeNonEmptyParentheses: false 219 | SpaceAroundPointerQualifiers: Default 220 | SpaceBeforeRangeBasedForLoopColon: true 221 | SpaceInEmptyBlock: false 222 | SpaceInEmptyParentheses: false 223 | SpacesBeforeTrailingComments: 2 224 | SpacesInAngles: Never 225 | SpacesInConditionalStatement: false 226 | SpacesInContainerLiterals: true 227 | SpacesInCStyleCastParentheses: false 228 | SpacesInLineCommentPrefix: 229 | Minimum: 1 230 | Maximum: -1 231 | SpacesInParentheses: false 232 | SpacesInSquareBrackets: false 233 | SpaceBeforeSquareBrackets: false 234 | BitFieldColonSpacing: Both 235 | Standard: Auto 236 | StatementAttributeLikeMacros: 237 | - Q_EMIT 238 | StatementMacros: 239 | - Q_UNUSED 240 | - QT_REQUIRE_VERSION 241 | TabWidth: 8 242 | UseCRLF: false 243 | UseTab: Never 244 | WhitespaceSensitiveMacros: 245 | - STRINGIZE 246 | - PP_STRINGIZE 247 | - BOOST_PP_STRINGIZE 248 | - NS_SWIFT_NAME 249 | - CF_SWIFT_NAME 250 | ... 251 | 252 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bugs.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Report bugs here. 3 | labels: [bug] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: "Describe your bug here. And how to reproduce it." 9 | validations: 10 | required: true 11 | 12 | 13 | - type: dropdown 14 | id: btarget 15 | attributes: 16 | label: "What is your build target?" 17 | options: 18 | - "Windows" 19 | - "Linux" 20 | - "Mac" 21 | - "Nintendo Switch" 22 | validations: 23 | required: true 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: [] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Ask for a feature or just give ideas! 3 | labels: [enhancement] 4 | body: 5 | - type: textarea 6 | attributes: 7 | label: What feature do you want to get added? And how it will work? 8 | validations: 9 | required: true 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | 4 | ### Will this Pull Request break anything? 5 | 6 | 7 | ### Suggested Testing Steps 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.dSYM 3 | /.vs/ 4 | /packages/ 5 | /saves/ 6 | *.o 7 | /smw 8 | /build/ 9 | /smw.sfc 10 | SDL2.dll 11 | /smw.exe 12 | /glsl-shaders/ 13 | /dis/ 14 | /work 15 | smw_assets.dat 16 | __pycache__ 17 | /enhanced/ 18 | *.sfc -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Welcome to the building instructions for the project! Please make sure to prepeare the required files and install the necessary dependencies for your current OS. 2 | 3 | # Requirements: 4 | * A Super Mario World rom (Make sure to rename it to `smw.sfc`) 5 | * `libsdl2-dev` (The installation for this will be different for each compiler.) 6 | * Super Mario World repo `git clone --recursive https://github.com/snesrev/smw` 7 | * [Python](https://www.python.org/) (During installation, make sure to check the "Add to PATH") 8 | 9 | For Linux/MacOS you must install these for your desired OS: 10 | * Ubuntu/Debian: `sudo apt install libsdl2-dev` 11 | * Fedora Linux: `sudo dnf in sdl2-devel` 12 | * Arch Linux: `sudo pacman -S sdl2` 13 | * macOS: `brew install sdl2` 14 | 15 | # Windows 16 | 17 | ## Building with Tiny C Compiler 18 | 19 | Dependencies and requirements: 20 | * You'll need [TCC](https://github.com/FitzRoyX/tinycc/releases/download/tcc_20230519/tcc_20230519.zip) and [SDL2](https://github.com/libsdl-org/SDL/releases/download/release-2.28.1/SDL2-devel-2.28.1-VC.zip) in order to compile using TCC. 21 | 22 | 1. Rename your obtaind Super Mario World rom to `smw.sfc` and place it in the root folder. 23 | 2. Unzip both TCC and SDL2 and place them in `third_party` folder. 24 | 3. Double click `run_with_tcc.bat` 25 | 4. Wait for it to compile and the game will automatically boot-up. 26 | 27 | # More advanced methods 28 | 29 | ## Building with MSYS2 30 | 31 | Dependencies and requirements: 32 | 33 | * The `libsdl2-dev` library 34 | * [MSYS2](https://www.msys2.org) 35 | 36 | Note: *Make sure you're using MINGW64 and you're in `smw` folder in the terminal.* 37 | 38 | 1. Install MSYS2 on your machine. 39 | 2. Place the copy of your rom in the main directory. 40 | 3. Install the necessary dependencies and SDL2 by inputting this command in MSYS2 terminal: 41 | 42 | ```sh 43 | pacman -S mingw-w64-x86_64-SDL2 && pacman -S make && pacman -S mingw-w64-x86_64-gcc 44 | ``` 45 | 4. Type `sdl2-config --cflags`, it should output: 46 | ```sh 47 | -IC:/msys64/mingw64/include/SDL2 -Dmain=SDL_main 48 | ``` 49 | 5. After that type `sdl2-config --libs`, should output: 50 | ```sh 51 | -LC:/msys64/mingw64/lib -lmingw32 -mwindows -lSDL2main -lSDL2 52 | ``` 53 | 54 | After you've done installing everything, in the terminal, type `make` 55 | In order to speed up the compilation, type `make -j16` 56 | 57 | ## Building with Visual Studio 58 | 59 | Dependencies and requirements: 60 | * The `libsdl2-dev` library, which is automatically installed with NuGet. 61 | * [Visual Studio Community 2022](https://visualstudio.microsoft.com) 62 | 63 | Download VS installer. On installer prompt, make sure you're on "Workloads" and check `Desktop Development with C++` this will install the necessary deps for compilation. 64 | 65 | 1. Open `smw.sln` solution. 66 | 2. Change the build target from `Debug` to `Release` 67 | 3. Build the solution. 68 | 69 | 70 | # Running SMB1 and SMBLL 71 | 72 | Dependencies and requirements: 73 | 74 | * Super Mario All-Stars rom (US version and not + Mario World) 75 | * `zstandard` 76 | 77 | 1. Rename your obtained rom to `smas.sfc` and place it inside the `other` folder. 78 | 2. To install `zstandard` make sure you've installed Python and added to PATH. Open up CMD and type `pip install zstandard` to install the required dep. 79 | 3. In the `other` folder drag and drop your renamed rom into `extract.py` or by typing `extract.py` in the command line to extract the necessary files. 80 | 4. Move `smb1.sfc` and `smbll.sfc` to root folder. 81 | 5. Before running the games, make sure to recompile or else they won't boot up. 82 | 6. Drag your desired game to `smw.exe` in order to run. 83 | 84 | 85 | # Linux/MacOS 86 | 87 | Open the terminal and CD to your SM root folder: 88 | ```sh 89 | make 90 | ``` 91 | 92 | For more advanced usage: 93 | ```sh 94 | make -j$(nproc) # run on all core 95 | make clean all # clear gen+obj and rebuild 96 | CC=clang make # specify compiler 97 | ``` 98 | 99 | # Nintendo Switch 100 | 101 | Dependencies and requirements: 102 | 103 | * The `switch-sdl2` library 104 | * [DevKitPro](https://github.com/devkitPro/installer) 105 | * [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere) 106 | * `pkg-config` 107 | 108 | 1. Make sure you've installed Atmosphere on your Switch. 109 | 2. Please download the DevKitPro version of MSYS2 through their installer, as the default MSYS2 causes issues with windows compiling. 110 | 3. Now that you've installed DevKitPro, open up the location you've installed DevKitPro to, then find `mingw64.exe` inside `msys2` located in `devkitPro` folder. 111 | 4. Type `pacman -S git switch-dev switch-sdl2 switch-tools pkg-config` in the terminal to install the `switch-sdl2` library. 112 | 5. CD to `switch` folder by typing `cd src/platform/switch` in the terminal on the `smw` root folder. 113 | 6. type `make` to compile the Switch Port. 114 | 7. Transfer the `.ini`, `nro`, `ncap` and your rom file to the Switch. 115 | 116 | **OPTIONAL STEP** 117 | 118 | ```sh 119 | make -j$(nproc) # To build using all cores 120 | ``` 121 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 snesrev 4 | Copyright (c) 2021 elzo_d 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | 25 | This is the license for Opus: 26 | 27 | /* Copyright (c) 2007-2008 CSIRO 28 | Copyright (c) 2007-2009 Xiph.Org Foundation 29 | Copyright (c) 2008-2009 Gregory Maxwell 30 | Written by Jean-Marc Valin and Gregory Maxwell */ 31 | /* 32 | Redistribution and use in source and binary forms, with or without 33 | modification, are permitted provided that the following conditions 34 | are met: 35 | 36 | - Redistributions of source code must retain the above copyright 37 | notice, this list of conditions and the following disclaimer. 38 | 39 | - Redistributions in binary form must reproduce the above copyright 40 | notice, this list of conditions and the following disclaimer in the 41 | documentation and/or other materials provided with the distribution. 42 | 43 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 44 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 45 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 46 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 47 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 48 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 49 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 50 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 51 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 52 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 53 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 54 | */ 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET_EXEC:=smw 2 | 3 | SRCS:=$(wildcard smb1/*.c smbll/*.c src/*.c src/snes/*.c) third_party/gl_core/gl_core_3_1.c 4 | OBJS:=$(SRCS:%.c=%.o) 5 | 6 | PYTHON:=/usr/bin/env python3 7 | CFLAGS:=$(if $(CFLAGS),$(CFLAGS),-O2 -fno-strict-aliasing -Werror ) 8 | CFLAGS:=${CFLAGS} $(shell sdl2-config --cflags) -DSYSTEM_VOLUME_MIXER_AVAILABLE=0 -I. 9 | 10 | ifeq (${OS},Windows_NT) 11 | WINDRES:=windres 12 | # RES:=sm.res 13 | SDLFLAGS:=-Wl,-Bstatic $(shell sdl2-config --static-libs) 14 | else 15 | SDLFLAGS:=$(shell sdl2-config --libs) -lm 16 | endif 17 | 18 | .PHONY: all clean clean_obj 19 | 20 | all: $(TARGET_EXEC) smw_assets.dat 21 | $(TARGET_EXEC): $(OBJS) $(RES) 22 | $(CC) $^ -o $@ $(LDFLAGS) $(SDLFLAGS) 23 | 24 | %.o : %.c 25 | $(CC) -c $(CFLAGS) $< -o $@ 26 | 27 | #$(RES): src/platform/win32/smw.rc 28 | # @echo "Generating Windows resources" 29 | # @$(WINDRES) $< -O coff -o $@ 30 | 31 | smw_assets.dat: 32 | @echo "Extracting game resources" 33 | $(PYTHON) assets/restool.py 34 | 35 | clean: clean_obj clean_gen 36 | clean_obj: 37 | @$(RM) $(OBJS) $(TARGET_EXEC) 38 | clean_gen: 39 | @$(RM) $(RES) smw_assets.dat 40 | @rm -rf assets/__pycache__ 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # smw 2 | A reimplementation of Super Mario World. 3 | 4 | Our discord server is: https://discord.gg/AJJbJAzNNJ 5 | 6 | ## About 7 | 8 | This is a reverse engineered clone of Super Mario World. 9 | 10 | It reimplements all parts of the original game and a bunch of mods added by Lunar Magic. The game is playable from start to end. 11 | 12 | You need a copy of the ROM to extract game resources (levels, images). Then once that's done, the ROM is no longer needed. 13 | 14 | It uses the PPU and DSP implementation from [LakeSnes](https://github.com/elzo-d/LakeSnes), but with lots of speed optimizations. 15 | 16 | ## Building 17 | 18 | You must self-build for now. Easy method on 64-bit Windows (no terminal or big downloads):
19 | (1) Download [Python](https://www.python.org/ftp/python/3.11.4/python-3.11.4-amd64.exe) if you don't have it and install with "Add to PATH" checked
20 | (2) Click the green button "Code > Download ZIP" on the github page and extract the ZIP
21 | (3) Place your USA rom named smw.sfc in that folder
22 | (4) Download [TCC](https://github.com/FitzRoyX/tinycc/releases/download/tcc_20230519/tcc_20230519.zip) and [SDL2](https://github.com/libsdl-org/SDL/releases/download/release-2.28.1/SDL2-devel-2.28.1-VC.zip) and extract each ZIP into the "third-party" subfolder
23 | (5) Double click "extract_assets.bat" in the main dir. This will create smw_assets.dat.
24 | (6) Double-click "run_with_tcc.bat" in the main dir. This will create smw.exe and run it.
25 | (7) Configure with smw.ini in a text editor like notepad++
26 | 27 | For other platforms and compilers, see: https://github.com/snesrev/smw/blob/main/BUILDING.md 28 | 29 | When running, it runs an emulated version in the background and compares the ram state every frame. If it detects a mismatch, it saves a snapshot in saves/ and displays a counter on screen counting down from 300. Please submit these bug snapshots on discord so that they can be fixed. 30 | 31 | ## Usage and controls 32 | 33 | The game supports snapshots. The joypad input history is also saved in the snapshot. It's thus possible to replay a playthrough in turbo mode to verify that the game behaves correctly. 34 | 35 | | Button | Key | 36 | | ------ | ----------- | 37 | | Up | Up arrow | 38 | | Down | Down arrow | 39 | | Left | Left arrow | 40 | | Right | Right arrow | 41 | | Start | Enter | 42 | | Select | Right shift | 43 | | A | X | 44 | | B | Z | 45 | | X | S | 46 | | Y | A | 47 | | L | C | 48 | | R | V | 49 | 50 | The keys can be reconfigured in smw.ini 51 | 52 | Additionally, the following commands are available: 53 | 54 | | Key | Action | 55 | | --- | --------------------- | 56 | | Tab | Turbo mode | 57 | | P | Pause (with dim) | 58 | | Shift+P | Pause (without dim) | 59 | | Ctrl+Up | Increase window size | 60 | | Ctrl+Down | Decrease window size | 61 | | T | Toggle replay turbo mode | 62 | | K | Clear all input history from the joypad log | 63 | | L | Stop replaying a shapshot | 64 | | R | Toggle between fast and slow renderer | 65 | | F | Display renderer performance | 66 | | F1-F10 | Load snapshot | 67 | | Alt+Enter | Toggle Fullscreen | 68 | | Shift+F1-F10 | Save snapshot | 69 | | Ctrl+F1-F10 | Replay the snapshot | 70 | -------------------------------------------------------------------------------- /assets/restool.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import util 3 | import sys 4 | 5 | parser = argparse.ArgumentParser(description='Resource tool used to build smw_assets.dat', allow_abbrev=False) 6 | parser.add_argument('-r', '--rom', nargs='?', metavar='ROM') 7 | parser.add_argument('--extract-from-rom', '-e', action='store_true', help='Extract assets from the ROM') 8 | parser.add_argument('--no-hash-check', '-f', action='store_true', help='Bypass the hash check (needed for Lunar Magic roms)') 9 | parser.add_argument('--no-include-rom', dest='include_rom', action='store_false', help='Don''t include the ROM in the assets file for verification') 10 | parser.add_argument('--hack', dest='hack', help='Use the specified hack. Valid values: %s' % util.get_hack_variants()) 11 | 12 | optional = parser.add_argument_group('Debug things') 13 | optional.add_argument('--print-assets-header', action='store_true') 14 | 15 | args = parser.parse_args() 16 | 17 | ROM = util.load_rom(args.rom, disable_hash_check = args.no_hash_check, rom_hack = args.hack) 18 | 19 | 20 | 21 | import compile_resources 22 | compile_resources.main(args) 23 | 24 | 25 | -------------------------------------------------------------------------------- /assets/util.py: -------------------------------------------------------------------------------- 1 | import array 2 | import sys 3 | import hashlib 4 | import os 5 | from functools import lru_cache 6 | 7 | def cache(user_function): 8 | 'Simple lightweight unbounded cache. Sometimes called "memoize".' 9 | return lru_cache(maxsize=None)(user_function) 10 | 11 | COMMON_ROM_NAMES = ['smw.sfc', '../smw.sfc'] 12 | DEFAULT_ROM_DIRECTORY = os.path.dirname(__file__) 13 | 14 | SMW_SHA1_US = '6B47BB75D16514B6A476AA0C73A683A2A4C18765' 15 | SMW_SHA1 = { 16 | SMW_SHA1_US : ('us', 'Super Mario World (USA)'), 17 | } 18 | 19 | def load_rom(filename, disable_hash_check = False, rom_hack = None): 20 | global ROM 21 | ROM = LoadedRom(filename, disable_hash_check, rom_hack) 22 | return ROM 23 | 24 | def get_byte(addr): 25 | return ROM.get_byte(addr) 26 | 27 | def get_24(addr): 28 | return ROM.get_24(addr) 29 | 30 | @cache 31 | def get_bytes(addr, n): 32 | return ROM.get_bytes(addr, n) 33 | 34 | @cache 35 | def get_words(addr, n): 36 | return ROM.get_words(addr, n) 37 | 38 | def get_int8(ea): 39 | b = get_byte(ea) 40 | if b & 0x80: b -= 256 41 | return b 42 | 43 | def get_int16(ea): 44 | b = get_word(ea) 45 | if b & 0x8000: b -= 65536 46 | return b 47 | 48 | def get_word(addr): 49 | return ROM.get_word(addr) 50 | 51 | kHackInfo = { 52 | 'enhanced' : ('Super Mario World Enhanced.ips', '2882a39ac64b597e9260e92aaba610d0b6f03adb', 'Super Mario World Enhanced.bsdiff', '5a53369916fd728033d16db3abceb487900f903a', 'ebe23d8a26759ba1b5c2f4d6d8f8db3ab88e902f'), 53 | 'redrawn' : ('smw_redrawn.ips', '7168df51ba025a12177ce00e88540c1341ef3efc', 'smw_redrawn.bsdiff', '13b658e626020aa35dca4e1c3648c6a6d8afa901', '50702ffc8c2e7115c9ba0c80c8ec40da5a487651'), 54 | 'nsmb' : ('SMW with Levels from NSMB.bps', '4277edf003340021ce4e68fe2278d1d73f4d798c', 'SMW with Levels from NSMB.bsdiff', 'e2d2487324374aea1d67a6aa6b3b47f8b3e6727c', '877e9c6fce29e80e4c52892120af06748550487a'), 55 | 'return_dino_land' : ('Return to Dinosaur Land.bps', 'e9a891ecd0f6f43a4592487ca2590311be00396d', 'Return to Dinosaur Land.bsdiff', 'ad2ebd7b416cbfd3cd7cac833b0ca7f690b9144b', '1d03777086f3c86b1693f9ce2411f560813732a8') 56 | } 57 | kModsPath = os.path.join(DEFAULT_ROM_DIRECTORY, '../smw_hacks/mods') 58 | 59 | def get_hack_variants(): 60 | return ", ".join(kHackInfo.keys()) 61 | 62 | def get_hacked_variant(org_rom, hack): 63 | if not os.path.exists(kModsPath): 64 | raise Exception('\n\nERROR: You need to run: git clone https://github.com/snesrev/smw_hacks.git') 65 | try: 66 | import bsdiff4 67 | except: 68 | raise Exception('\n\nERROR: You need to run pip install bsdiff4') 69 | if hack not in kHackInfo: 70 | raise Excepion(f'{hack} is not a valid hack variant. Valid values {get_hack_variants()}') 71 | if hashlib.sha1(org_rom).hexdigest() != SMW_SHA1_US.lower(): 72 | raise Exception(f'Input to the differ must be the original SMW rom with hash {SMW_SHA1_US}') 73 | 74 | ips_name, ips_hash, diff_name, diff_hash, patched_hash = kHackInfo[hack] 75 | ips = open(os.path.join(kModsPath, ips_name), 'rb').read() 76 | if hashlib.sha1(ips).hexdigest() != ips_hash: 77 | raise Exception(f'{ips_name} has wrong hash. Expecting {ips_hash}') 78 | 79 | diff = open(os.path.join(kModsPath, diff_name), 'rb').read() 80 | if hashlib.sha1(diff).hexdigest() != diff_hash: 81 | raise Exception(f'{diff_name} has wrong hash. Expecting {diff_hash}') 82 | patched = bsdiff4.patch(ips + org_rom, diff) 83 | patched_hash_cur = hashlib.sha1(patched).hexdigest() 84 | if patched_hash_cur != patched_hash: 85 | raise Exception(f'Patched file has wrong hash. Expecting {patched_hash} got {patched_hash_cur}') 86 | return patched 87 | 88 | class LoadedRom: 89 | def __init__(self, path = None, disable_hash_check = False, rom_hack = None): 90 | self.ROM = open(self.__get_rom_path(path), 'rb').read() 91 | 92 | if rom_hack != None: 93 | self.ROM = get_hacked_variant(self.ROM, rom_hack) 94 | disable_hash_check = True 95 | 96 | # Remove the SMC header? 97 | if (len(self.ROM) & 0xfffff) == 0x200: 98 | self.ROM = self.ROM[0x200:] 99 | 100 | hash = hashlib.sha1(self.ROM).hexdigest().upper() 101 | entry = SMW_SHA1.get(hash) 102 | self.language = entry[0] if entry != None else None 103 | 104 | if not disable_hash_check and self.language != 'us': 105 | raise Exception(f"\n\nROM with hash {hash} not supported.\n\nExpected {SMW_SHA1_US}.\nPlease verify your ROM is \"Super Mario World\".\n\nIf you want to use a LunarMagic edited rom you can run restool with -f to bypass this check."); 106 | 107 | def get_byte(self, ea): 108 | assert (ea & 0x8000) 109 | ea = ((ea >> 16) & 0x7f) * 0x8000 + (ea & 0x7fff) 110 | return self.ROM[ea] 111 | 112 | def get_word(self, ea): 113 | return self.get_byte(ea) + self.get_byte(ea + 1) * 256 114 | 115 | def get_24(self, ea): 116 | return self.get_byte(ea) + self.get_byte(ea + 1) * 256 + self.get_byte(ea + 2) * 65536 117 | 118 | def get_bytes(self, addr, n): 119 | r = bytearray() 120 | for i in range(n): 121 | r.append(self.get_byte(addr)) 122 | addr += 1 123 | if (addr & 0x8000) == 0: 124 | addr += 0x8000 125 | return r 126 | 127 | def get_words(self, addr, n): 128 | r = [] 129 | for i in range(n): 130 | r.append(self.get_word(addr)) 131 | addr += 2 132 | if (addr & 0x8000) == 0: 133 | addr += 0x8000 134 | return r 135 | 136 | def __get_rom_path(self, path = None): 137 | # Check default locations when no path is given by user. 138 | if path is None: 139 | for rom_name in COMMON_ROM_NAMES: 140 | rom_path = os.path.join(DEFAULT_ROM_DIRECTORY, rom_name) 141 | if os.path.isfile(rom_path): 142 | return rom_path 143 | raise Exception(f"Could not find any ROMs ({', '.join(COMMON_ROM_NAMES)}) at the default location {DEFAULT_ROM_DIRECTORY}.") 144 | 145 | rom_path = os.path.join(DEFAULT_ROM_DIRECTORY, path) 146 | if os.path.isfile(rom_path): 147 | return rom_path 148 | raise Exception(f"No ROM found at provided path {rom_path}.") 149 | 150 | 151 | 152 | def split_list(l, n): 153 | return [l[i:i+n] for i in range(0, len(l), n)] 154 | 155 | def to_hex(v): 156 | return '%#x' % v if v < -9 or v >9 else '%d'%v 157 | 158 | 159 | def print_int_array(name, r, tname, decimal, split_length = 16, file = sys.stdout): 160 | rlen = len(r) 161 | rr = split_list(r, split_length ) 162 | if decimal != None: 163 | if decimal: 164 | rr = [['%d' % s for s in t] for t in rr] 165 | else: 166 | rr = [[to_hex(s) for s in t] for t in rr] 167 | 168 | def pad_all_columns(rrs): 169 | colsiz = [max((0 if j >= len(r) else len(r[j])) for r in rrs) for j in range(len(rrs[0]))] 170 | def pad(c, i): 171 | return (' ' * (i - len(c))) + c 172 | return [[pad(c, colsiz[i]) for (i, c) in enumerate(r)] for r in rrs] 173 | 174 | if len(rr) == 1: 175 | print('static const %s %s[%d] = {%s};' % (tname, name, rlen, ", ".join(rr[0])), file = file) 176 | else: 177 | print('static const %s %s[%d] = {' % (tname, name, rlen), file = file) 178 | for t in pad_all_columns(rr): 179 | print(" " + "".join([(a + ', ') for a in t]), file = file) 180 | print('};', file = file) 181 | 182 | 183 | 184 | class Reader: 185 | def __init__(self, ea, rb): 186 | self.ea, self.rb = ea, rb 187 | def next(self): 188 | r = self.rb(self.ea) 189 | self.ea += 1 190 | if (self.ea & 0xffff) == 0: 191 | self.ea += 0x8000 192 | return r 193 | 194 | def decomp(ea, rb, offset_is_be = True, return_length = False): 195 | result = bytearray() 196 | reader = Reader(ea, rb) 197 | while True: 198 | b = reader.next() 199 | if b == 0xff: 200 | if return_length: 201 | return result, (reader.ea - ea) & 0x7fff 202 | else: 203 | return result 204 | if (b & 0xe0) != 0xe0: 205 | lx = b & 0x1f 206 | cmd = b & 0xe0 207 | else: 208 | cmd = (b << 3) & 0xe0 209 | lx = ((b & 3) << 8) | reader.next() 210 | lx += 1 211 | if cmd == 0x00: # 000 - literal 212 | # print('literal %d' % lx) 213 | while lx: 214 | result.append(reader.next()) 215 | lx -= 1 216 | elif cmd & 0x80: # 1xx - copy 217 | # print('copy %d' % lx) 218 | offs = reader.next() << 8 219 | offs |= reader.next() 220 | if not offset_is_be: offs = ((offs >> 8) | (offs << 8)) & 0xffff 221 | while lx: 222 | result.append(result[offs]) 223 | offs += 1 224 | lx -= 1 225 | elif (cmd & 0x40) == 0: # 00x - memset 226 | # print('memset %d' % lx) 227 | b = reader.next() 228 | while lx: 229 | result.append(b) 230 | lx -= 1 231 | elif (cmd & 0x20) == 0: # 010 - memset16 232 | # print('memsetw %d' % lx) 233 | b1, b2 = reader.next(), reader.next() 234 | while lx: 235 | result.append(b1) 236 | if lx==1: break 237 | result.append(b2) 238 | lx -= 2 239 | else: # 011 - incr 240 | # print('incr %d' % lx) 241 | b = reader.next() 242 | while lx: 243 | result.append(b) 244 | b = (b + 1) & 0xff 245 | lx -= 1 246 | 247 | 248 | def decode_brr(get_byte, olds = (0, 0)): 249 | ea=0 250 | r = [] 251 | old , older = olds 252 | while True: 253 | cmd = get_byte(ea) 254 | 255 | shift = cmd >> 4 256 | filter = (cmd >> 2) & 3 257 | #print("shift=%d, filter=%d" % (shift, filter)) 258 | for i in range(16): 259 | t = (get_byte(ea+1+i//2) >> (0 if i & 1 else 4)) & 0xf 260 | s = (t & 7) - (t & 8) 261 | if shift <= 12: 262 | s = ((s << shift) >> 1) 263 | else: 264 | s = (s >> 3) << 12 # -2048 or 0 265 | 266 | if filter == 1: 267 | s += old*1+((-old*1) >> 4) 268 | elif filter == 2: 269 | s += old*2 + ((-old*3) >> 5) - older + ((older*1) >> 4) 270 | elif filter == 3: 271 | s += old*2 + ((-old*13) >> 6) - older + ((older*3) >> 4) 272 | # saturate to 16 bits 273 | if s < -0x8000: s = -0x8000 274 | elif s >= 0x7fff: s = 0x7fff 275 | # wrap to 15 bits 276 | s = (s & 0x3fff) - (s & 0x4000) 277 | 278 | older, old = old, s 279 | #print('%d: 0x%x -> %d (shift %d, filter %d)' % (i, t, s*2, shift, filter)) 280 | r.append(s*2) 281 | ea += 9 282 | if cmd & 1: 283 | break 284 | return array.array('h', r) 285 | 286 | kBrrFilters = [ 287 | lambda old, older: 0, 288 | lambda old, older: old*1+((-old*1) >> 4), 289 | lambda old, older: old*2 + ((-old*3) >> 5) - older + ((older*1) >> 4), 290 | lambda old, older: old*2 + ((-old*13) >> 6) - older + ((older*3) >> 4) 291 | ] 292 | 293 | def brr_get_one(old, rs, r): 294 | s = (rs << r) >> 1 if r <= 12 else (rs >> 3) << 12 295 | s += old 296 | s = -0x8000 if s < -0x8000 else 0x7fff if s > 0x7fff else s 297 | return (s & 0x3fff) - (s & 0x4000) # wrap to 15 bits 298 | 299 | def encode_brr_generic(data, brr_repeat, olds = (0, 0), lossless=True): 300 | assert len(data) % 16 == 0 301 | loop_enabled, loop_offset = 1 if brr_repeat != 0 else 0, 0 302 | result = [] 303 | blk_data = [0] * 16 304 | best_data = [0] * 9 305 | p = 0 306 | best_old, best_older = olds 307 | while p < len(data): 308 | # print(p) 309 | best_err = 1 << 60 310 | startold, startolder = best_old, best_older 311 | 312 | if all(data[p + i] == 0 for i in range(16)): 313 | result.extend((loop_enabled * 2, 0, 0, 0, 0, 0, 0, 0, 0)) 314 | p += 16 315 | continue 316 | for filter in range(4): 317 | if filter != 0 and (p == 0 or p == loop_offset): 318 | continue 319 | for r in range(12, 0, -1): 320 | blk_err = 0 321 | old, older = startold, startolder 322 | for i in range(16): 323 | s = kBrrFilters[filter](old, older) 324 | xs = data[p + i] >> 1 325 | best_e = 1<<60 326 | for j in (0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, -8): 327 | s0 = brr_get_one(s, j, r) 328 | e = (xs - s0) * (xs - s0) 329 | if e < best_e: 330 | best_e, best_j, best_s0 = e, j, s0 331 | if e == 0: 332 | #print(j) 333 | break 334 | if best_e != 0 and lossless: 335 | break 336 | blk_err += best_e 337 | blk_data[i] = best_j & 0xf 338 | older, old = old, best_s0 339 | else: 340 | if blk_err < best_err: 341 | best_err = blk_err 342 | best_old, best_older = old, older 343 | best_data[0] = r << 4 | filter << 2 | loop_enabled << 1 344 | for i in range(8): 345 | best_data[i + 1] = blk_data[i * 2] << 4 | blk_data[i * 2 + 1] 346 | result.extend(best_data) 347 | if lossless: assert best_err==0 348 | p += 16 349 | # result[-9] |= 1 350 | return result 351 | 352 | -------------------------------------------------------------------------------- /extract_assets.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | 2>nul (del smw_assets.dat) 4 | python assets/restool.py 5 | IF NOT ERRORLEVEL 0 goto ERROR 6 | 7 | IF NOT EXIST "smw_assets.dat" ( 8 | ECHO ERROR: The python program didn't generate smw_assets.dat successfully. 9 | goto ERROR 10 | ) ELSE ( 11 | REM 12 | ) 13 | 14 | goto DONE 15 | 16 | 17 | :ERROR 18 | ECHO: 19 | ECHO ERROR: Asset extraction failed! 20 | pause 21 | EXIT /B 1 22 | 23 | :DONE 24 | echo Complete! 25 | pause -------------------------------------------------------------------------------- /other/.gitignore: -------------------------------------------------------------------------------- 1 | *.sfc -------------------------------------------------------------------------------- /other/dont_run_this_gen_deltas.py: -------------------------------------------------------------------------------- 1 | import zstandard 2 | dict_data = zstandard.ZstdCompressionDict(open('smas.sfc', 'rb').read()) 3 | 4 | cctx = zstandard.ZstdCompressor(dict_data=dict_data, level=12) 5 | with open('smb1.sfc', 'rb') as ifp: 6 | with open('smb1.zst', 'wb') as ofp: 7 | ofp.write(cctx.compress(ifp.read())) 8 | 9 | 10 | cctx = zstandard.ZstdCompressor(dict_data=dict_data, level=12) 11 | with open('smbll.sfc', 'rb') as ifp: 12 | with open('smbll.zst', 'wb') as ofp: 13 | ofp.write(cctx.compress(ifp.read())) 14 | -------------------------------------------------------------------------------- /other/extract.py: -------------------------------------------------------------------------------- 1 | # pip install zstandard 2 | import zstandard 3 | import hashlib 4 | 5 | SHA1_HASH = 'c05817c5b7df2fbfe631563e0b37237156a8f6b6' # smas 6 | SHA1_HASH_SMB1 = '4a5278150f3395419d68cb02a42f7c3c62cdf8b4' 7 | SHA1_HASH_SMBLL = '493e14812af7a92d0eacf00ba8bb6d3a266302ca' 8 | 9 | smas = open('smas.sfc', 'rb').read() 10 | hash = hashlib.sha1(smas).hexdigest() 11 | if hash != SHA1_HASH: 12 | raise Exception('You need SMAS with sha1 ' + SHA1_HASH + ' yours is ' + hash) 13 | 14 | dict_data = zstandard.ZstdCompressionDict(smas) 15 | 16 | cctx = zstandard.ZstdDecompressor(dict_data=dict_data) 17 | out = cctx.decompress(open('smb1.zst', 'rb').read()) 18 | 19 | hash = hashlib.sha1(out).hexdigest() 20 | if hash != SHA1_HASH_SMB1: 21 | raise Exception('Error. SMB1 hash is supposed to be ' + SHA1_HASH_SMB1 + ' yours is ' + hash) 22 | 23 | with open('smb1.sfc', 'wb') as ofp: 24 | ofp.write(out) 25 | 26 | 27 | cctx = zstandard.ZstdDecompressor(dict_data=dict_data) 28 | out = cctx.decompress(open('smbll.zst', 'rb').read()) 29 | 30 | hash = hashlib.sha1(out).hexdigest() 31 | if hash != SHA1_HASH_SMBLL: 32 | raise Exception('Error. SMBLL hash is supposed to be ' + SHA1_HASH_SMBLL + ' yours is ' + hash) 33 | 34 | with open('smbll.sfc', 'wb') as ofp: 35 | ofp.write(out) 36 | -------------------------------------------------------------------------------- /other/smb1.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snesrev/smw/eae20c65c58930c8b62c76188d259579ad4130f1/other/smb1.zst -------------------------------------------------------------------------------- /other/smbll.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snesrev/smw/eae20c65c58930c8b62c76188d259579ad4130f1/other/smbll.zst -------------------------------------------------------------------------------- /run_with_tcc.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set SDL2=third_party\SDL2-2.28.1 4 | 5 | IF NOT EXIST "third_party\tcc\tcc.exe" ( 6 | ECHO: 7 | ECHO ERROR: src\third_party\tcc\tcc.exe doesn't exist. Please verify that you have put it in the right location. 8 | ECHO Download it from https://github.com/FitzRoyX/tinycc/releases/download/tcc_20230519/tcc_20230519.zip 9 | ECHO It needs to be the 64-bit version. 10 | ECHO: 11 | PAUSE 12 | EXIT /B 1 13 | ) ELSE ( 14 | REM 15 | ) 16 | 17 | IF NOT EXIST "%SDL2%\lib\x64\SDL2.dll" ( 18 | ECHO: 19 | ECHO ERROR: SDL is not unzipped properly into %SDL2% 20 | ECHO Download it from https://github.com/libsdl-org/SDL/releases/download/release-2.28.1/SDL2-devel-2.28.1-VC.zip 21 | ECHO: 22 | PAUSE 23 | EXIT /B 1 24 | ) ELSE ( 25 | REM 26 | ) 27 | 28 | 29 | echo Building with TCC... 30 | third_party\tcc\tcc.exe -osmw.exe -DCOMPILER_TCC=1 -DSTBI_NO_SIMD=1 -DHAVE_STDINT_H=1 -D_HAVE_STDINT_H=1 -DSYSTEM_VOLUME_MIXER_AVAILABLE=0 -I%SDL2%/include -L%SDL2%/lib/x64 -lSDL2 -I. src/*.c src/snes/*.c third_party/gl_core/gl_core_3_1.c smb1/*.c smbll/*.c 31 | IF ERRORLEVEL 1 goto GETOUT 32 | 33 | copy %SDL2%\lib\x64\SDL2.dll . 34 | 35 | echo Running... 36 | smw.exe 37 | 38 | :GETOUT 39 | pause 40 | -------------------------------------------------------------------------------- /smb1/smb1_cpu_infra.c: -------------------------------------------------------------------------------- 1 | #include "../src/common_cpu_infra.h" 2 | #include "smb1_rtl.h" 3 | #include "../src/snes/snes.h" 4 | #include "smb1_variables.h" 5 | #include "smb1_funcs.h" 6 | 7 | bool g_smb1_want_reset; 8 | 9 | void Smb1RunOneFrameOfGame_Internal(); 10 | 11 | static const uint32 kPatchedCarrys_SMB1[] = { 12 | 0xa492, 13 | 0xa495, 14 | 0xa4a7, 15 | 0xb7d3, 16 | 0xBB79, 17 | 0xbcc6, 18 | 0xbca9, 19 | 0xc627, 20 | 0xD0DB, 21 | 0xd27e, 22 | 0xd2b2, 23 | 0xf3c1, 24 | 0xf3c3, 25 | 0xf3e3, 26 | 0xf3e5, 27 | 0xfc50, 28 | 0x1df2f, 29 | 0x1df32, 30 | 0x1df35, 31 | 0x1e066, 32 | 0x1e069, 33 | 0x1e06c, 34 | 0x1e191, 35 | 0x1e194, 36 | 0x1e197, 37 | 38 | }; 39 | 40 | uint32 PatchBugs_SMB1(void) { 41 | if (FixBugHook(0x088054)) { 42 | if (g_use_my_apu_code) 43 | return 0x88AEA; // ret 44 | RtlSetUploadingApu(true); 45 | } else if (FixBugHook(0x088007) || FixBugHook(0x088039) || FixBugHook(0x8827D)) { 46 | RtlSetUploadingApu(true); 47 | } else if (FixBugHook(0x880C2)) { 48 | RtlSetUploadingApu(false); 49 | } else if (FixBugHook(0x81ac)) { 50 | wait_for_vblank = 1; 51 | } else if (FixBugHook(0x0887A2)) { 52 | sram_controller1_plugged_in = 0; 53 | sram_controller2_plugged_in = 2; 54 | } else if (FixBugHook(0x8067)) { 55 | return 0x806B; 56 | } else if (FixBugHook(0xC63E)) { 57 | // Spr02F_Vine_Init reads from y 58 | g_cpu->y = 0; 59 | } else if (FixBugHook(0x8034)) { 60 | // Remove the copy detection check 61 | return 0x8057; 62 | } 63 | return 0; 64 | } 65 | 66 | void Smb1CpuInitialize(void) { 67 | } 68 | 69 | static void Smb1FixSnapshotForCompare(Snapshot *b, Snapshot *a) { 70 | memcpy(&b->ram[0x0], &a->ram[0x0], 8); // temps 71 | memcpy(&b->ram[0x155], &a->ram[0x155], 0x100 - 0x55); // stack 72 | 73 | } 74 | 75 | static uint32 RunCpuUntilPC(uint32 pc1, uint32 pc2) { 76 | uint32 addr_last = g_snes->cpu->k << 16 | g_snes->cpu->pc; 77 | 78 | for (;;) { 79 | snes_runCpu(g_snes); 80 | // snes_runCycle(g_snes); 81 | uint32 addr = g_snes->cpu->k << 16 | g_snes->cpu->pc; 82 | if (addr != addr_last && (addr == pc1 || addr == pc2)) { 83 | return addr; 84 | } 85 | addr_last = addr; 86 | } 87 | } 88 | 89 | void Smb1RunOneFrameOfGame_Emulated(void) { 90 | Snes *snes = g_snes; 91 | if (snes->cpu->pc == 0x8000) 92 | RtlApuReset(); 93 | snes->vPos = snes->hPos = 0; 94 | snes->cpu->nmiWanted = snes->cpu->irqWanted = false; 95 | snes->inVblank = snes->inNmi = false; 96 | wait_for_vblank = 1; 97 | 98 | // Execute until: mov.b wait_for_vblank, #0 99 | RunCpuUntilPC(0x82BB, 0x88AE4); 100 | 101 | g_snes->debug_cycles = 0; // turn off debuig prints if enabled 102 | 103 | // Trigger nmi 104 | snes->cpu->nmiWanted = true; 105 | RunCpuUntilPC(0x8506, 0x8506); 106 | snes_runCpu(snes); 107 | 108 | // Right after NMI completes, draw the frame, possibly triggering IRQ. 109 | // assert(!snes->cpu->i); 110 | snes->vPos = snes->hPos = 0; 111 | snes->cpu->nmiWanted = snes->cpu->irqWanted = false; 112 | snes->inVblank = snes->inNmi = false; 113 | 114 | while (!snes->inNmi) { 115 | snes_handle_pos_stuff(snes); 116 | 117 | if (snes->cpu->irqWanted) { 118 | RunCpuUntilPC(0x8551, 0x8551); 119 | snes_runCpu(snes); 120 | } 121 | } 122 | } 123 | 124 | 125 | void Smb1RunOneFrameOfGame(void) { 126 | Snes *snes = g_snes; 127 | snes->vPos = snes->hPos = 0; 128 | snes->cpu->nmiWanted = snes->cpu->irqWanted = false; 129 | snes->inVblank = snes->inNmi = false; 130 | 131 | if (snes->cpu->pc == 0x8000) { 132 | RESET_GAME: 133 | snes->cpu->pc = 0x8001; 134 | Smb1VectorReset(); 135 | } 136 | Smb1RunOneFrameOfGame_Internal(); 137 | if (g_smb1_want_reset) { 138 | g_smb1_want_reset = false; 139 | goto RESET_GAME; 140 | } 141 | 142 | Smb1VectorNMI(); 143 | 144 | snes->vPos = snes->hPos = 0; 145 | snes->cpu->nmiWanted = snes->cpu->irqWanted = false; 146 | snes->inVblank = snes->inNmi = false; 147 | 148 | while (!snes->inNmi) { 149 | snes_handle_pos_stuff(snes); 150 | 151 | if (snes->cpu->irqWanted) { 152 | snes->cpu->irqWanted = false; 153 | Smb1VectorIRQ(); 154 | } 155 | } 156 | } 157 | 158 | static void Smb1DrawPpuFrame(void) { 159 | 160 | } 161 | 162 | const RtlGameInfo kSmb1GameInfo = { 163 | "smb1", 164 | kGameID_SMB1, 165 | kPatchedCarrys_SMB1, arraysize(kPatchedCarrys_SMB1), 166 | &PatchBugs_SMB1, 167 | &Smb1CpuInitialize, 168 | &Smb1RunOneFrameOfGame, 169 | &Smb1RunOneFrameOfGame_Emulated, 170 | &Smb1DrawPpuFrame, 171 | &Smb1FixSnapshotForCompare, 172 | }; 173 | -------------------------------------------------------------------------------- /smb1/smb1_rtl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../src/common_rtl.h" 4 | 5 | static inline OamEnt *get_OamEnt(OamEnt *base, uint16 off) { return (OamEnt *)((uint8 *)base + off); } 6 | 7 | #pragma pack(push, 1) 8 | typedef struct RelPosForOffs { 9 | uint8 player; 10 | uint8 enemy[10]; 11 | uint8 fireball[2]; 12 | uint8 block[4]; 13 | uint8 misc[9]; 14 | uint8 bubble[2]; 15 | } RelPosForOffs; 16 | typedef struct RelPosStruct { 17 | uint8 player; 18 | uint8 enemy; 19 | uint8 fireball; 20 | uint8 bubble; 21 | uint8 block; 22 | uint8 unk5; 23 | uint8 misc; 24 | } RelPosStruct; 25 | #pragma pack(pop) 26 | 27 | uint16 SetupLayer2Bg_Callfunc(uint16 k, uint16 j); 28 | 29 | enum RelPos { 30 | kRelPos_Player = 0x0, 31 | kRelPos_Enemy = 0x1, 32 | kRelPos_Fireball = 0x2, 33 | kRelPos_Bubble = 0x3, 34 | kRelPos_Block = 0x4, 35 | kRelPos_Unk = 0x5, 36 | kRelPos_Misc = 0x6, 37 | }; 38 | -------------------------------------------------------------------------------- /smbll/smbll_cpu_infra.c: -------------------------------------------------------------------------------- 1 | #include "../src/common_cpu_infra.h" 2 | #include "smbll_rtl.h" 3 | #include "../src/snes/snes.h" 4 | #include "smbll_variables.h" 5 | #include "smbll_funcs.h" 6 | 7 | bool g_smbll_want_reset; 8 | 9 | void SmbllRunOneFrameOfGame_Internal(); 10 | 11 | static const uint32 kPatchedCarrys_SMBLL[] = { 12 | 0xa180, 13 | 0xa183, 14 | 0xa196, 15 | 0xb52f, 16 | 0xb8f1, 17 | 0xba3e, 18 | 0xba21, 19 | 0xc47e, 20 | 0xcf84, 21 | 0xf39f, 22 | 0xf3a1, 23 | 0xf3c1, 24 | 0xf3c3, 25 | 0xfc54, 26 | 0xb872f, 27 | 0xb8732, 28 | 0xb8735, 29 | 0xb8866, 30 | 0xb8869, 31 | 0xb886c, 32 | 0xb8991, 33 | 0xb8994, 34 | 0xb8997, 35 | }; 36 | 37 | static uint32 PatchBugs_SMBLL(void) { 38 | if (FixBugHook(0x088054)) { 39 | if (g_use_my_apu_code) 40 | return 0x88168; // ret 41 | RtlSetUploadingApu(true); 42 | } else if (FixBugHook(0x088007) || FixBugHook(0x088039) || FixBugHook(0x8817A)) { 43 | RtlSetUploadingApu(true); 44 | } else if (FixBugHook(0x880C2)) { 45 | RtlSetUploadingApu(false); 46 | } else if (FixBugHook(0x8092)) { 47 | wait_for_vblank = 1; 48 | } else if (FixBugHook(0x08869F)) { 49 | sram_controller1_plugged_in = 0; 50 | sram_controller2_plugged_in = 2; 51 | } else if (FixBugHook(0x805F)) { 52 | return 0x8063; 53 | } else if (FixBugHook(0xC495)) { 54 | // Spr02F_Vine_Init reads from y 55 | g_cpu->y = 0; 56 | } else if (FixBugHook(0x802C)) { 57 | // Remove the copy detection check 58 | return 0x804F; 59 | } else if (FixBugHook(0xD9E8)) { 60 | g_cpu->a = 0; 61 | } 62 | return 0; 63 | } 64 | 65 | static void SmbllCpuInitialize(void) { 66 | } 67 | 68 | static void SmbllFixSnapshotForCompare(Snapshot *b, Snapshot *a) { 69 | memcpy(&b->ram[0x0], &a->ram[0x0], 8); // temps 70 | memcpy(&b->ram[0x155], &a->ram[0x155], 0x100 - 0x55); // stack 71 | 72 | } 73 | 74 | static uint32 RunCpuUntilPC(uint32 pc1, uint32 pc2) { 75 | uint32 addr_last = g_snes->cpu->k << 16 | g_snes->cpu->pc; 76 | 77 | for (;;) { 78 | snes_runCpu(g_snes); 79 | // snes_runCycle(g_snes); 80 | uint32 addr = g_snes->cpu->k << 16 | g_snes->cpu->pc; 81 | if (addr != addr_last && (addr == pc1 || addr == pc2)) { 82 | return addr; 83 | } 84 | addr_last = addr; 85 | } 86 | } 87 | 88 | void SmbllRunOneFrameOfGame_Emulated(void) { 89 | Snes *snes = g_snes; 90 | if (snes->cpu->pc == 0x8000) 91 | RtlApuReset(); 92 | snes->vPos = snes->hPos = 0; 93 | snes->cpu->nmiWanted = snes->cpu->irqWanted = false; 94 | snes->inVblank = snes->inNmi = false; 95 | wait_for_vblank = 1; 96 | 97 | // Execute until: mov.b wait_for_vblank, #0 98 | RunCpuUntilPC(0x819D, 0x889E1); 99 | 100 | g_snes->debug_cycles = 0; // turn off debuig prints if enabled 101 | 102 | // Trigger nmi 103 | snes->cpu->nmiWanted = true; 104 | RunCpuUntilPC(0x83D0, 0x83D0); 105 | snes_runCpu(snes); 106 | 107 | // Right after NMI completes, draw the frame, possibly triggering IRQ. 108 | // assert(!snes->cpu->i); 109 | snes->vPos = snes->hPos = 0; 110 | snes->cpu->nmiWanted = snes->cpu->irqWanted = false; 111 | snes->inVblank = snes->inNmi = false; 112 | 113 | while (!snes->inNmi) { 114 | snes_handle_pos_stuff(snes); 115 | 116 | if (snes->cpu->irqWanted) { 117 | RunCpuUntilPC(0x841B, 0x841B); 118 | snes_runCpu(snes); 119 | } 120 | } 121 | } 122 | 123 | 124 | void SmbllRunOneFrameOfGame(void) { 125 | Snes *snes = g_snes; 126 | snes->vPos = snes->hPos = 0; 127 | snes->cpu->nmiWanted = snes->cpu->irqWanted = false; 128 | snes->inVblank = snes->inNmi = false; 129 | 130 | if (snes->cpu->pc == 0x8000) { 131 | RESET_GAME: 132 | snes->cpu->pc = 0x8001; 133 | Smbll_VectorReset(); 134 | } 135 | SmbllRunOneFrameOfGame_Internal(); 136 | if (g_smbll_want_reset) { 137 | g_smbll_want_reset = false; 138 | goto RESET_GAME; 139 | } 140 | 141 | Smbll_VectorNMI(); 142 | 143 | snes->vPos = snes->hPos = 0; 144 | snes->cpu->nmiWanted = snes->cpu->irqWanted = false; 145 | snes->inVblank = snes->inNmi = false; 146 | 147 | while (!snes->inNmi) { 148 | snes_handle_pos_stuff(snes); 149 | 150 | if (snes->cpu->irqWanted) { 151 | snes->cpu->irqWanted = false; 152 | Smbll_VectorIRQ(); 153 | } 154 | } 155 | } 156 | 157 | static void SmbllDrawPpuFrame(void) { 158 | 159 | } 160 | const RtlGameInfo kSmbllGameInfo = { 161 | "smbll", 162 | kGameID_SMBLL, 163 | kPatchedCarrys_SMBLL, arraysize(kPatchedCarrys_SMBLL), 164 | &PatchBugs_SMBLL, 165 | &SmbllCpuInitialize, 166 | &SmbllRunOneFrameOfGame, 167 | &SmbllRunOneFrameOfGame_Emulated, 168 | &SmbllDrawPpuFrame, 169 | &SmbllFixSnapshotForCompare, 170 | }; 171 | -------------------------------------------------------------------------------- /smbll/smbll_rtl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../src/common_rtl.h" 4 | 5 | static inline OamEnt *get_OamEnt(OamEnt *base, uint16 off) { return (OamEnt *)((uint8 *)base + off); } 6 | 7 | #pragma pack(push, 1) 8 | typedef struct RelPosForOffs { 9 | uint8 player; 10 | uint8 enemy[10]; 11 | uint8 fireball[2]; 12 | uint8 block[4]; 13 | uint8 misc[9]; 14 | uint8 bubble[2]; 15 | } RelPosForOffs; 16 | typedef struct RelPosStruct { 17 | uint8 player; 18 | uint8 enemy; 19 | uint8 fireball; 20 | uint8 bubble; 21 | uint8 block; 22 | uint8 unk5; 23 | uint8 misc; 24 | } RelPosStruct; 25 | #pragma pack(pop) 26 | 27 | uint16 Smbll_SetupLayer2Bg_Callfunc(uint16 k, uint16 j); 28 | 29 | enum RelPos { 30 | kRelPos_Player = 0x0, 31 | kRelPos_Enemy = 0x1, 32 | kRelPos_Fireball = 0x2, 33 | kRelPos_Bubble = 0x3, 34 | kRelPos_Block = 0x4, 35 | kRelPos_Unk = 0x5, 36 | kRelPos_Misc = 0x6, 37 | }; 38 | -------------------------------------------------------------------------------- /smw.ini: -------------------------------------------------------------------------------- 1 | [General] 2 | # Automatically save state on quit and reload on start 3 | Autosave = 0 4 | 5 | # Disable the SDL_Delay that happens each frame (Gives slightly better perf if your 6 | # display is set to exactly 60hz) 7 | DisableFrameDelay = 0 8 | 9 | # Save a snapshot each time a level is completed 10 | SavePlaythrough = 0 11 | 12 | 13 | [Graphics] 14 | # Window size ( Auto or WidthxHeight ) 15 | WindowSize = Auto 16 | 17 | # Fullscreen mode (0=windowed, 1=desktop fullscreen, 2=fullscreen w/mode change) 18 | Fullscreen = 0 19 | 20 | # Window scale (1=100%, 2=200%, 3=300%, etc.) 21 | WindowScale = 3 22 | 23 | # Use an optimized (but potentially more buggy) SNES PPU implementation 24 | NewRenderer = 1 25 | 26 | # Don't keep the aspect ratio 27 | IgnoreAspectRatio = 0 28 | 29 | # Enable this option to remove the sprite limits per scan line 30 | NoSpriteLimits = 1 31 | 32 | # Use either SDL, SDL-Software, or OpenGL as the output method 33 | # SDL-Software rendering might give better performance on Raspberry pi. 34 | #OutputMethod = SDL 35 | 36 | # Set to true to use linear filtering. Gives less crisp pixels. Works with SDL and OpenGL. 37 | #LinearFiltering = 0 38 | 39 | # Set a glsl shader. Only supported with the OpenGL output method 40 | # This can be the path to a .glsl or .glslp file 41 | # Get them with: git clone https://github.com/snesrev/glsl-shaders 42 | #Shader = glsl-shaders/hqx/hq4x.glslp 43 | 44 | [Sound] 45 | EnableAudio = 1 46 | 47 | # DSP frequency in samples per second (e.g. 48000, 44100, 32000, 22050, 11025) 48 | AudioFreq = 32000 49 | 50 | # number of separate sound channels (1=mono, 2=stereo) 51 | AudioChannels = 2 52 | 53 | # Audio buffer size in samples (power of 2; e.g., 4096, 2048, 1024) [try 1024 if sound is crackly]. The higher the more lag before you hear sounds. 54 | AudioSamples = 512 55 | 56 | [KeyMap] 57 | # Change what keyboard keys map to the joypad 58 | # Order: Up, Down, Left, Right, Select, Start, A, B, X, Y, L, R 59 | 60 | # This default is suitable for QWERTY keyboards. 61 | Controls = Up, Down, Left, Right, Right Shift, Return, x, z, s, a, c, v 62 | 63 | # This default is suitable for QWERTZ keyboards. 64 | #Controls = Up, Down, Left, Right, Right Shift, Return, x, y, s, a, c, v 65 | 66 | # This one is suitable for AZERTY keyboards. 67 | #Controls = Up, Down, Left, Right, Right Shift, Return, x, w, s, q, c, v 68 | 69 | # For player 2 70 | #ControlsP2 = Shift+Up, Shift+Down, Shift+Left, Shift+Right, Shift+Right Shift, Shift+Return, Shift+x, Shift+z, Shift+s, Shift+a, Shift+c, Shift+v 71 | 72 | CheatLife = w 73 | CheatJump = Ctrl+q 74 | ClearKeyLog = k 75 | StopReplay = l 76 | Fullscreen = Alt+Return 77 | Reset = Ctrl+r 78 | Pause = Shift+p 79 | PauseDimmed = p 80 | Turbo = Tab 81 | ReplayTurbo = t 82 | WindowBigger = Ctrl+Up 83 | WindowSmaller = Ctrl+Down 84 | 85 | VolumeUp = Shift+= 86 | VolumeDown = Shift+- 87 | 88 | Load = F1, F2, F3, F4, F5, F6, F7, F8, F9, F10 89 | Save = Shift+F1,Shift+F2,Shift+F3,Shift+F4,Shift+F5,Shift+F6,Shift+F7,Shift+F8,Shift+F9,Shift+F10 90 | Replay= Ctrl+F1,Ctrl+F2,Ctrl+F3,Ctrl+F4,Ctrl+F5,Ctrl+F6,Ctrl+F7,Ctrl+F8,Ctrl+F9,Ctrl+F10 91 | 92 | LoadRef = 1,2,3,4,5,6,7,8,9,0,-,=,Backspace 93 | ReplayRef = Ctrl+1,Ctrl+2,Ctrl+3,Ctrl+4,Ctrl+5,Ctrl+6,Ctrl+7,Ctrl+8,Ctrl+9,Ctrl+0,Ctrl+-,Ctrl+=,Ctrl+Backspace 94 | 95 | [GamepadMap] 96 | # Whether the gamepads will be enabled. The game will not use them unless they're on. 97 | EnableGamepad1 = true 98 | EnableGamepad2 = true 99 | 100 | # Any keys used in KeyMap can be used also in this section. 101 | 102 | # The shoulder button is called L1/Lb and L2, and the thumbstick button is called L3 103 | Controls = DpadUp, DpadDown, DpadLeft, DpadRight, Back, Start, B, A, Y, X, Lb, Rb 104 | 105 | # For player 2. 106 | ControlsP2 = DpadUp, DpadDown, DpadLeft, DpadRight, Back, Start, B, A, Y, X, Lb, Rb 107 | -------------------------------------------------------------------------------- /smw.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32825.248 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smw", "src\smw.vcxproj", "{1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}.Debug|x64.ActiveCfg = Debug|x64 17 | {1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}.Debug|x64.Build.0 = Debug|x64 18 | {1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}.Debug|x86.ActiveCfg = Debug|Win32 19 | {1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}.Debug|x86.Build.0 = Debug|Win32 20 | {1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}.Release|x64.ActiveCfg = Release|x64 21 | {1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}.Release|x64.Build.0 = Release|x64 22 | {1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}.Release|x86.ActiveCfg = Release|Win32 23 | {1F8AB1B4-DAFB-4D6E-BF1E-F802FF5A52EE}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {8F47385F-600A-41BA-AEF8-C47A2C0D15E6} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | /smw.vcxproj.user 2 | -------------------------------------------------------------------------------- /src/common_cpu_infra.c: -------------------------------------------------------------------------------- 1 | #include "common_cpu_infra.h" 2 | #include "types.h" 3 | #include "common_rtl.h" 4 | #include "snes/cpu.h" 5 | #include "snes/snes.h" 6 | #include "tracing.h" 7 | #include "util.h" 8 | #include 9 | 10 | enum RunMode { RM_BOTH, RM_MINE, RM_THEIRS }; 11 | uint8 g_runmode = RM_BOTH; 12 | 13 | extern int g_got_mismatch_count; 14 | 15 | Snes *g_snes; 16 | Cpu *g_cpu; 17 | 18 | bool g_calling_asm_from_c; 19 | int g_calling_asm_from_c_ret; 20 | bool g_fail; 21 | bool g_use_my_apu_code = true; 22 | extern bool g_other_image; 23 | const RtlGameInfo *g_rtl_game_info; 24 | 25 | static Snapshot g_snapshot_mine, g_snapshot_theirs, g_snapshot_before; 26 | static uint32 hookmode, hookcnt, hookadr; 27 | static uint32 hooked_func_pc; 28 | static uint8 hook_orgbyte[1024]; 29 | static uint8 hook_fixbug_orgbyte[1024]; 30 | static uint8 kPatchedCarrysOrg[1024]; 31 | 32 | static void VerifySnapshotsEq(Snapshot *b, Snapshot *a, Snapshot *prev); 33 | static void MakeSnapshot(Snapshot *s); 34 | static void RestoreSnapshot(Snapshot *s); 35 | 36 | uint8_t *SnesRomPtr(uint32 v) { 37 | return (uint8 *)RomPtr(v); 38 | } 39 | 40 | bool ProcessHook(uint32 v) { 41 | uint8_t *rombyte = SnesRomPtr(v); 42 | switch (hookmode) { 43 | case 0: // remove hooks 44 | *rombyte = hook_orgbyte[hookcnt++]; 45 | return false; 46 | case 1: // install hooks 47 | hook_orgbyte[hookcnt++] = *rombyte; 48 | *rombyte = 0; 49 | return false; 50 | case 2: // run hook 51 | if (v == hookadr) { 52 | hookmode = 3; 53 | return true; 54 | } 55 | return false; 56 | } 57 | return false; 58 | } 59 | 60 | bool FixBugHook(uint32 addr) { 61 | switch (hookmode) { 62 | case 1: { // install hooks 63 | uint8_t *rombyte = SnesRomPtr(addr); 64 | hook_fixbug_orgbyte[hookcnt++] = *rombyte; 65 | *rombyte = 0; 66 | return false; 67 | } 68 | case 2: // run hook 69 | if (addr == hookadr) { 70 | hookmode = 3; 71 | return true; 72 | } 73 | hookcnt++; 74 | return false; 75 | } 76 | return false; 77 | } 78 | 79 | uint32 PatchBugs(uint32 mode, uint32 addr) { 80 | hookmode = mode, hookadr = addr, hookcnt = 0; 81 | return g_rtl_game_info->patch_bugs(); 82 | } 83 | 84 | int RunPatchBugHook(uint32 addr) { 85 | uint32 new_pc = PatchBugs(2, addr); 86 | if (hookmode == 3) { 87 | if (new_pc == 0) { 88 | return hook_fixbug_orgbyte[hookcnt]; 89 | } else { 90 | g_cpu->k = new_pc >> 16; 91 | g_cpu->pc = (new_pc & 0xffff) + 1; 92 | return *SnesRomPtr(new_pc); 93 | } 94 | } 95 | return -1; 96 | } 97 | 98 | int CpuOpcodeHook(uint32 addr) { 99 | for (size_t i = 0; i != g_rtl_game_info->patch_carrys_count; i++) { 100 | if (addr == g_rtl_game_info->patch_carrys[i]) { 101 | return kPatchedCarrysOrg[i]; 102 | } 103 | } 104 | { 105 | int i = RunPatchBugHook(addr); 106 | if (i >= 0) return i; 107 | } 108 | printf("Bad hook at 0x%x!\n", addr); 109 | assert(0); 110 | return 0; 111 | } 112 | 113 | bool HookedFunctionRts(int is_long) { 114 | if (g_calling_asm_from_c) { 115 | g_calling_asm_from_c_ret = is_long; 116 | g_calling_asm_from_c = false; 117 | return false; 118 | } 119 | assert(0); 120 | return false; 121 | } 122 | 123 | static void VerifySnapshotsEq(Snapshot *b, Snapshot *a, Snapshot *prev) { 124 | if (memcmp(b->ram, a->ram, 0x20000)) { 125 | fprintf(stderr, "@%d: Memory compare failed (mine != theirs, prev):\n", snes_frame_counter); 126 | int j = 0; 127 | for (size_t i = 0; i < 0x20000; i++) { 128 | if (a->ram[i] != b->ram[i]) { 129 | if (++j < 256*10) { 130 | if (((i & 1) == 0 || i < 0x10000) && a->ram[i + 1] != b->ram[i + 1]) { 131 | fprintf(stderr, "0x%.6X: %.4X != %.4X (%.4X)\n", (int)i, 132 | WORD(b->ram[i]), WORD(a->ram[i]), WORD(prev->ram[i])); 133 | i++, j++; 134 | } else { 135 | fprintf(stderr, "0x%.6X: %.2X != %.2X (%.2X)\n", (int)i, b->ram[i], a->ram[i], prev->ram[i]); 136 | } 137 | } 138 | } 139 | } 140 | if (j) 141 | g_fail = true; 142 | fprintf(stderr, " total of %d failed bytes\n", (int)j); 143 | } 144 | 145 | if (memcmp(b->sram, a->sram, 0x2000)) { 146 | fprintf(stderr, "@%d: SRAM compare failed (mine != theirs, prev):\n", snes_frame_counter); 147 | int j = 0; 148 | for (size_t i = 0; i < 0x2000; i++) { 149 | if (a->sram[i] != b->sram[i]) { 150 | if (++j < 128) { 151 | if ((i & 1) == 0 && a->sram[i + 1] != b->sram[i + 1]) { 152 | fprintf(stderr, "0x%.6X: %.4X != %.4X (%.4X)\n", (int)i, 153 | WORD(b->sram[i]), WORD(a->sram[i]), WORD(prev->sram[i])); 154 | i++, j++; 155 | } else { 156 | fprintf(stderr, "0x%.6X: %.2X != %.2X (%.2X)\n", (int)i, b->sram[i], a->sram[i], prev->sram[i]); 157 | } 158 | } 159 | } 160 | } 161 | if (j) 162 | g_fail = true; 163 | fprintf(stderr, " total of %d failed bytes\n", (int)j); 164 | } 165 | #if 1 166 | if (memcmp(b->vram, a->vram, sizeof(uint16) * 0x8000)) { 167 | fprintf(stderr, "@%d: VRAM compare failed (mine != theirs, prev):\n", snes_frame_counter); 168 | for (size_t i = 0, j = 0; i < 0x8000; i++) { 169 | if (a->vram[i] != b->vram[i]) { 170 | fprintf(stderr, "0x%.6X: %.4X != %.4X (%.4X)\n", (int)i, b->vram[i], a->vram[i], prev->vram[i]); 171 | g_fail = true; 172 | if (++j >= 32) 173 | break; 174 | } 175 | } 176 | } 177 | if (memcmp(b->oam, a->oam, sizeof(uint16) * 0x120)) { 178 | fprintf(stderr, "@%d: VRAM OAM compare failed (mine != theirs, prev):\n", snes_frame_counter); 179 | for (size_t i = 0, j = 0; i < 0x120; i++) { 180 | if (a->oam[i] != b->oam[i]) { 181 | fprintf(stderr, "0x%.6X: %.4X != %.4X (%.4X)\n", (int)i, b->oam[i], a->oam[i], prev->oam[i]); 182 | g_fail = true; 183 | if (++j >= 16) 184 | break; 185 | } 186 | } 187 | } 188 | 189 | if (memcmp(b->cgram, a->cgram, sizeof(uint16) * 0x100)) { 190 | fprintf(stderr, "@%d: VRAM cgram compare failed (mine != theirs, prev):\n", snes_frame_counter); 191 | for (size_t i = 0, j = 0; i < 0x100; i++) { 192 | if (a->cgram[i] != b->cgram[i]) { 193 | fprintf(stderr, "0x%.6X: %.4X != %.4X (%.4X)\n", (int)i, b->cgram[i], a->cgram[i], prev->cgram[i]); 194 | g_fail = true; 195 | if (++j >= 16) 196 | break; 197 | } 198 | } 199 | } 200 | 201 | 202 | #endif 203 | } 204 | 205 | static void MakeSnapshot(Snapshot *s) { 206 | Cpu *c = g_cpu; 207 | s->a = c->a, s->x = c->x, s->y = c->y; 208 | s->sp = c->sp, s->dp = c->dp, s->db = c->db; 209 | s->pc = c->pc, s->k = c->k; 210 | s->flags = cpu_getFlags(c); 211 | s->vTimer = g_snes->vTimer; 212 | memcpy(s->ram, g_snes->ram, 0x20000); 213 | memcpy(s->sram, g_snes->cart->ram, g_snes->cart->ramSize); 214 | memcpy(s->vram, g_ppu->vram, sizeof(uint16) * 0x8000); 215 | memcpy(s->oam, g_ppu->oam, sizeof(uint16) * 0x120); 216 | memcpy(s->cgram, g_ppu->cgram, sizeof(uint16) * 0x100); 217 | } 218 | 219 | static void MakeMySnapshot(Snapshot *s) { 220 | memcpy(s->ram, g_snes->ram, 0x20000); 221 | memcpy(s->sram, g_snes->cart->ram, g_snes->cart->ramSize); 222 | memcpy(s->vram, g_ppu->vram, sizeof(uint16) * 0x8000); 223 | memcpy(s->oam, g_ppu->oam, sizeof(uint16) * 0x120); 224 | memcpy(s->cgram, g_ppu->cgram, sizeof(uint16) * 0x100); 225 | } 226 | 227 | static void RestoreSnapshot(Snapshot *s) { 228 | Cpu *c = g_cpu; 229 | c->a = s->a, c->x = s->x, c->y = s->y; 230 | c->sp = s->sp, c->dp = s->dp, c->db = s->db; 231 | c->pc = s->pc, c->k = s->k; 232 | g_snes->vTimer = s->vTimer; 233 | cpu_setFlags(c, s->flags); 234 | memcpy(g_snes->ram, s->ram, 0x20000); 235 | memcpy(g_snes->cart->ram, s->sram, g_snes->cart->ramSize); 236 | memcpy(g_ppu->vram, s->vram, sizeof(uint16) * 0x8000); 237 | memcpy(g_ppu->oam, s->oam, sizeof(uint16) * 0x120); 238 | memcpy(g_ppu->cgram, s->cgram, sizeof(uint16) * 0x100); 239 | } 240 | 241 | static void FixupCarry(uint32 addr) { 242 | *SnesRomPtr(addr) = 0; 243 | } 244 | 245 | Snes *SnesInit(const uint8 *data, int data_size) { 246 | g_my_ppu = ppu_init(); 247 | ppu_reset(g_my_ppu); 248 | 249 | g_snes = snes_init(g_ram); 250 | g_cpu = g_snes->cpu; 251 | g_dma = g_snes->dma; 252 | g_use_my_apu_code = (g_runmode != RM_THEIRS); 253 | 254 | if (data_size != 0 && g_runmode != RM_MINE) { 255 | bool loaded = snes_loadRom(g_snes, data, data_size); 256 | if (!loaded) { 257 | return NULL; 258 | } 259 | g_rom = g_snes->cart->rom; 260 | 261 | if (memcmp(g_rom + 0x7fc0, "Super Mario Bros. LL ", 21) == 0) { 262 | g_rtl_game_info = &kSmbllGameInfo; 263 | } else if (memcmp(g_rom + 0x7fc0, "Super Mario Bros. 1 ", 21) == 0) { 264 | g_rtl_game_info = &kSmb1GameInfo; 265 | } else { 266 | g_rtl_game_info = &kSmwGameInfo; 267 | } 268 | 269 | for (size_t i = 0; i != g_rtl_game_info->patch_carrys_count; i++) { 270 | uint8 t = *SnesRomPtr(g_rtl_game_info->patch_carrys[i]); 271 | if (t) { 272 | kPatchedCarrysOrg[i] = t; 273 | FixupCarry(g_rtl_game_info->patch_carrys[i]); 274 | } else { 275 | printf("0x%x double patched!\n", g_rtl_game_info->patch_carrys[i]); 276 | } 277 | } 278 | g_rtl_game_info->initialize(); 279 | snes_reset(g_snes, true); // reset after loading 280 | PatchBugs(1, 0); 281 | } else { 282 | g_runmode = RM_MINE; 283 | g_snes->cart->ramSize = 2048; 284 | g_snes->cart->ram = calloc(1, 2048); 285 | g_rtl_game_info = &kSmwGameInfo; 286 | g_rtl_game_info->initialize(); 287 | ppu_reset(g_snes->ppu); 288 | dma_reset(g_snes->dma); 289 | } 290 | 291 | g_sram = g_snes->cart->ram; 292 | g_sram_size = g_snes->cart->ramSize; 293 | game_id = g_rtl_game_info->game_id; 294 | 295 | 296 | return g_snes; 297 | } 298 | 299 | void SaveBugSnapshot() { 300 | if (!g_debug_flag && g_got_mismatch_count == 0) { 301 | char buffer[64]; 302 | snprintf(buffer, sizeof(buffer), "saves/%s-bug-%d.sav", g_rtl_game_info->title, (int)time(NULL)); 303 | RtlSaveSnapshot(buffer, true); 304 | } 305 | g_got_mismatch_count = 5 * 60; 306 | } 307 | 308 | int g_dbg_ctr_mine, g_dbg_ctr_theirs; 309 | 310 | void RunOneFrameOfGame_Both(void) { 311 | g_ppu = g_snes->ppu; 312 | MakeSnapshot(&g_snapshot_before); 313 | 314 | // Run orig version then snapshot 315 | again_theirs: 316 | g_dbg_ctr_mine = g_dbg_ctr_theirs = 0; 317 | g_snes->runningWhichVersion = 1; 318 | g_rtl_game_info->run_frame_emulated(); 319 | MakeSnapshot(&g_snapshot_theirs); 320 | 321 | // Run my version and snapshot 322 | //again_mine: 323 | g_ppu = g_my_ppu; 324 | RestoreSnapshot(&g_snapshot_before); 325 | 326 | g_snes->runningWhichVersion = 2; 327 | g_rtl_game_info->run_frame(); 328 | MakeSnapshot(&g_snapshot_mine); 329 | 330 | g_snes->runningWhichVersion = 0xff; 331 | 332 | // Compare both snapshots 333 | g_rtl_game_info->fix_snapshot_for_compare(&g_snapshot_mine, &g_snapshot_theirs); 334 | VerifySnapshotsEq(&g_snapshot_mine, &g_snapshot_theirs, &g_snapshot_before); 335 | 336 | if (g_fail) { 337 | g_fail = false; 338 | 339 | printf("Verify failure!\n"); 340 | 341 | g_ppu = g_snes->ppu; 342 | RestoreSnapshot(&g_snapshot_before); 343 | 344 | if (g_debug_flag) 345 | goto again_theirs; 346 | 347 | SaveBugSnapshot(); 348 | g_rtl_game_info->run_frame_emulated(); 349 | goto getout; 350 | } 351 | 352 | g_ppu = g_snes->ppu; 353 | RestoreSnapshot(&g_snapshot_theirs); 354 | getout: 355 | g_ppu = g_other_image ? g_my_ppu : g_snes->ppu; 356 | g_snes->runningWhichVersion = 0; 357 | 358 | if (g_got_mismatch_count) 359 | g_got_mismatch_count--; 360 | } 361 | 362 | void RtlRunFrameCompare() { 363 | g_use_my_apu_code = (g_runmode != RM_THEIRS); 364 | 365 | if (g_runmode == RM_THEIRS) { 366 | g_ppu = g_snes->ppu; 367 | g_snes->runningWhichVersion = 1; 368 | g_rtl_game_info->run_frame_emulated(); 369 | g_snes->runningWhichVersion = 0; 370 | } else if (g_runmode == RM_MINE) { 371 | g_ppu = g_my_ppu; 372 | g_snes->runningWhichVersion = 2; 373 | g_rtl_game_info->run_frame(); 374 | g_snes->runningWhichVersion = 0; 375 | } else { 376 | RunOneFrameOfGame_Both(); 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /src/common_cpu_infra.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | typedef struct Snes Snes; 6 | typedef struct Cpu Cpu; 7 | typedef struct Snapshot Snapshot; 8 | 9 | extern Snes *g_snes; 10 | extern Cpu *g_cpu; 11 | extern bool g_fail; 12 | 13 | typedef struct Snes Snes; 14 | 15 | Snes *SnesInit(const uint8 *data, int data_size); 16 | bool FixBugHook(uint32 addr); 17 | uint8_t *SnesRomPtr(uint32 v); 18 | 19 | typedef uint32 PatchBugsFunc(void); 20 | typedef void CpuInfraInitializeFunc(void); 21 | typedef void RunOneFrameOfGameFunc(void); 22 | typedef void FixSnapshotForCompareFunc(Snapshot *b, Snapshot *a); 23 | 24 | void RtlRunFrameCompare(void); 25 | 26 | typedef struct RtlGameInfo { 27 | const char *title; 28 | uint8 game_id; 29 | const uint32 *patch_carrys; 30 | size_t patch_carrys_count; 31 | PatchBugsFunc *patch_bugs; 32 | CpuInfraInitializeFunc *initialize; 33 | RunOneFrameOfGameFunc *run_frame; 34 | RunOneFrameOfGameFunc *run_frame_emulated; 35 | RunOneFrameOfGameFunc *draw_ppu_frame; 36 | FixSnapshotForCompareFunc *fix_snapshot_for_compare; 37 | } RtlGameInfo; 38 | 39 | typedef struct Snapshot { 40 | uint16 a, x, y, sp, dp, pc; 41 | uint8 k, db, flags; 42 | 43 | uint16_t vTimer; 44 | 45 | uint8 ram[0x20000]; 46 | uint16 vram[0x8000]; 47 | uint8 sram[0x2000]; 48 | 49 | uint16 oam[0x120]; 50 | uint16 cgram[0x100]; 51 | } Snapshot; 52 | 53 | extern const RtlGameInfo kSmwGameInfo; 54 | extern const RtlGameInfo kSmb1GameInfo; 55 | extern const RtlGameInfo kSmbllGameInfo; 56 | extern const RtlGameInfo *g_rtl_game_info; -------------------------------------------------------------------------------- /src/common_rtl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | #include "snes/snes_regs.h" 4 | #include 5 | #include 6 | 7 | enum { 8 | kGameID_SMW = 1, 9 | kGameID_SMB1 = 2, 10 | kGameID_SMBLL = 3, 11 | }; 12 | 13 | enum { 14 | // Version was bumped to 1 after I fixed bug #1 15 | kCurrentBugFixCounter = 1, 16 | 17 | kSmwRam_APUI02 = 0x18c5, 18 | kSmwRam_my_flags = 0x19C7C, 19 | }; 20 | 21 | typedef struct SimpleHdma { 22 | const uint8 *table; 23 | const uint8 *indir_ptr; 24 | uint8 rep_count; 25 | uint8 mode; 26 | uint8 ppu_addr; 27 | uint8 indir_bank; 28 | } SimpleHdma; 29 | 30 | 31 | typedef struct Dma Dma; 32 | typedef struct DmaChannel DmaChannel; 33 | typedef struct Ppu Ppu; 34 | 35 | void SimpleHdma_Init(SimpleHdma *c, DmaChannel *dc); 36 | void SimpleHdma_DoLine(SimpleHdma *c); 37 | void RtlHdmaSetup(uint8 which, uint8 transfer_unit, uint8 reg, uint32 addr, uint8 indirect_bank); 38 | 39 | extern uint8 g_ram[0x20000]; 40 | extern uint8 *g_sram; 41 | extern int g_sram_size; 42 | extern const uint8 *g_rom; 43 | extern Ppu *g_ppu, *g_my_ppu; 44 | extern Dma *g_dma; 45 | 46 | #define GET_BYTE(p) (*(uint8*)(p)) 47 | 48 | extern int snes_frame_counter; 49 | extern bool g_use_my_apu_code; 50 | extern bool g_debug_flag; 51 | extern uint8 game_id; 52 | 53 | typedef struct SpcPlayer SpcPlayer; 54 | extern SpcPlayer *g_spc_player; 55 | 56 | void mov24(LongPtr *dst, uint32 src); 57 | uint32 Load24(LongPtr src); 58 | void MemCpy(void *dst, const void *src, int size); 59 | bool Unreachable(); 60 | 61 | #if defined(_DEBUG) 62 | // Gives better warning messages but non inlined on tcc 63 | static inline uint16 GET_WORD(const uint8 *p) { return *(uint16 *)(p); } 64 | static inline const uint8 *RomFixedPtr(uint32_t addr) { return &g_rom[(((addr >> 16) << 15) | (addr & 0x7fff)) & 0x3fffff]; } 65 | #else 66 | #define GET_WORD(p) (*(uint16*)(p)) 67 | #define RomFixedPtr(addr) (&g_rom[(((addr >> 16) << 15) | (addr & 0x7fff)) & 0x3fffff]) 68 | #endif 69 | 70 | #define GET_BYTE(p) (*(uint8*)(p)) 71 | 72 | uint8 *RomPtr(uint32_t addr); 73 | 74 | static inline uint8 *RomPtr_RAM(uint16_t addr) { assert(addr < 0x2000); return g_ram + addr; } 75 | static inline const uint8 *RomPtr_00(uint16_t addr) { return RomPtr(0x000000 | addr); } 76 | static inline const uint8 *RomPtr_01(uint16_t addr) { return RomPtr(0x010000 | addr); } 77 | static inline const uint8 *RomPtr_02(uint16_t addr) { return RomPtr(0x020000 | addr); } 78 | static inline const uint8 *RomPtr_03(uint16_t addr) { return RomPtr(0x030000 | addr); } 79 | static inline const uint8 *RomPtr_04(uint16_t addr) { return RomPtr(0x040000 | addr); } 80 | static inline const uint8 *RomPtr_05(uint16_t addr) { return RomPtr(0x050000 | addr); } 81 | static inline const uint8 *RomPtr_06(uint16_t addr) { return RomPtr(0x060000 | addr); } 82 | static inline const uint8 *RomPtr_07(uint16_t addr) { return RomPtr(0x070000 | addr); } 83 | static inline const uint8 *RomPtr_08(uint16_t addr) { return RomPtr(0x080000 | addr); } 84 | static inline const uint8 *RomPtr_09(uint16_t addr) { return RomPtr(0x090000 | addr); } 85 | static inline const uint8 *RomPtr_0A(uint16_t addr) { return RomPtr(0x0a0000 | addr); } 86 | static inline const uint8 *RomPtr_0B(uint16_t addr) { return RomPtr(0x0b0000 | addr); } 87 | static inline const uint8 *RomPtr_0C(uint16_t addr) { return RomPtr(0x0c0000 | addr); } 88 | static inline const uint8 *RomPtr_0D(uint16_t addr) { return RomPtr(0x0d0000 | addr); } 89 | static inline const uint8 *RomPtr_0E(uint16_t addr) { return RomPtr(0x0e0000 | addr); } 90 | static inline const uint8 *RomPtr_0F(uint16_t addr) { return RomPtr(0x0f0000 | addr); } 91 | static inline const uint8 *RomPtr_11(uint16_t addr) { return RomPtr(0x110000 | addr); } 92 | static inline const uint8 *RomPtr_12(uint16_t addr) { return RomPtr(0x120000 | addr); } 93 | static inline const uint8 *RomPtrWithBank(uint8 bank, uint16_t addr) { return RomPtr((bank << 16) | addr); } 94 | 95 | uint16 Mult8x8(uint8 a, uint8 b); 96 | uint16 SnesDivide(uint16 a, uint8 b); 97 | uint16 SnesModulus(uint16 a, uint8 b); 98 | void AddHiLo(uint8 *hi, uint8 *lo, uint16 v); 99 | void SetHiLo(uint8 *hi, uint8 *lo, uint16 v); 100 | void WriteReg(uint16 reg, uint8 value); 101 | void WriteRegWord(uint16 reg, uint16 value); 102 | uint16 ReadRegWord(uint16 reg); 103 | uint8 ReadReg(uint16 reg); 104 | uint8_t *IndirPtr(LongPtr ptr, uint16 offs); 105 | void IndirWriteByte(LongPtr ptr, uint16 offs, uint8 value); 106 | 107 | void RtlReset(int mode); 108 | void RtlClearKeyLog(); 109 | void RtlStopReplay(); 110 | 111 | enum { 112 | kSaveLoad_Save = 1, 113 | kSaveLoad_Load = 2, 114 | kSaveLoad_Replay = 3, 115 | }; 116 | 117 | void RtlSaveLoad(int cmd, int slot); 118 | void RtlCheat(char c); 119 | void RtlApuLock(); 120 | void RtlApuUnlock(); 121 | void RtlApuReset(); 122 | void RtlSetUploadingApu(bool uploading); 123 | void RtlApuUpload(const uint8 *p); 124 | void RtlRenderAudio(int16 *audio_buffer, int samples, int channels); 125 | void RtlPushApuState(); 126 | bool RtlRunFrame(uint32 inputs); 127 | void RtlReadSram(); 128 | void RtlWriteSram(); 129 | void RtlSaveSnapshot(const char *filename, bool saving_with_bug); 130 | 131 | void RtlUpdatePalette(const uint16 *src, int dst, int n); 132 | uint16 *RtlGetVramAddr(); 133 | void RtlPpuWrite(uint16 addr, uint8 value); 134 | void RtlPpuWriteTwice(uint16 addr, uint16 value); 135 | void RtlApuWrite(uint16 adr, uint8 val); 136 | void RtlEnableVirq(int line); 137 | 138 | 139 | enum { 140 | kJoypadL_A = 0x80, 141 | kJoypadL_X = 0x40, 142 | kJoypadL_L = 0x20, 143 | kJoypadL_R = 0x10, 144 | 145 | kJoypadH_B = 0x80, 146 | kJoypadH_Y = 0x40, 147 | kJoypadH_Select = 0x20, 148 | kJoypadH_Start = 0x10, 149 | 150 | kJoypadH_Up = 0x8, 151 | kJoypadH_Down = 0x4, 152 | kJoypadH_Left = 0x2, 153 | kJoypadH_Right = 0x1, 154 | 155 | kJoypadH_AnyDir = 0xf, 156 | }; -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | #include 4 | 5 | enum { 6 | kKeys_Null, 7 | kKeys_Controls, 8 | kKeys_Controls_Last = kKeys_Controls + 11, 9 | 10 | kKeys_ControlsP2, 11 | kKeys_ControlsP2_Last = kKeys_ControlsP2 + 11, 12 | 13 | kKeys_Load, 14 | kKeys_Load_Last = kKeys_Load + 19, 15 | kKeys_Save, 16 | kKeys_Save_Last = kKeys_Save + 19, 17 | kKeys_Replay, 18 | kKeys_Replay_Last = kKeys_Replay + 19, 19 | kKeys_LoadRef, 20 | kKeys_LoadRef_Last = kKeys_LoadRef + 19, 21 | kKeys_ReplayRef, 22 | kKeys_ReplayRef_Last = kKeys_ReplayRef + 19, 23 | kKeys_CheatLife, 24 | kKeys_CheatJump, 25 | kKeys_ToggleWhichFrame, 26 | kKeys_ClearKeyLog, 27 | kKeys_StopReplay, 28 | kKeys_Fullscreen, 29 | kKeys_Reset, 30 | kKeys_Pause, 31 | kKeys_PauseDimmed, 32 | kKeys_Turbo, 33 | kKeys_ReplayTurbo, 34 | kKeys_WindowBigger, 35 | kKeys_WindowSmaller, 36 | kKeys_DisplayPerf, 37 | kKeys_ToggleRenderer, 38 | kKeys_VolumeUp, 39 | kKeys_VolumeDown, 40 | kKeys_Total, 41 | }; 42 | 43 | enum { 44 | kOutputMethod_SDL, 45 | kOutputMethod_SDLSoftware, 46 | kOutputMethod_OpenGL, 47 | }; 48 | 49 | typedef struct Config { 50 | int window_width; 51 | int window_height; 52 | bool enhanced_mode7; 53 | bool new_renderer; 54 | bool ignore_aspect_ratio; 55 | uint8 fullscreen; 56 | uint8 window_scale; 57 | bool enable_audio; 58 | bool linear_filtering; 59 | uint8 output_method; 60 | uint16 audio_freq; 61 | uint8 audio_channels; 62 | uint16 audio_samples; 63 | bool autosave; 64 | uint8 extended_aspect_ratio; 65 | bool extend_y; 66 | bool no_sprite_limits; 67 | bool display_perf_title; 68 | uint8 enable_msu; 69 | bool resume_msu; 70 | bool disable_frame_delay; 71 | bool save_playthrough; 72 | uint8 msuvolume; 73 | uint32 features0; 74 | 75 | const char *link_graphics; 76 | char *memory_buffer; 77 | const char *shader; 78 | const char *msu_path; 79 | 80 | bool enable_gamepad[2]; 81 | 82 | // Which players have keyboard controls 83 | uint8 has_keyboard_controls; 84 | } Config; 85 | 86 | enum { 87 | kMsuEnabled_Msu = 1, 88 | kMsuEnabled_MsuDeluxe = 2, 89 | kMsuEnabled_Opuz = 4, 90 | }; 91 | enum { 92 | kGamepadBtn_Invalid = -1, 93 | kGamepadBtn_A, 94 | kGamepadBtn_B, 95 | kGamepadBtn_X, 96 | kGamepadBtn_Y, 97 | kGamepadBtn_Back, 98 | kGamepadBtn_Guide, 99 | kGamepadBtn_Start, 100 | kGamepadBtn_L3, 101 | kGamepadBtn_R3, 102 | kGamepadBtn_L1, 103 | kGamepadBtn_R1, 104 | kGamepadBtn_DpadUp, 105 | kGamepadBtn_DpadDown, 106 | kGamepadBtn_DpadLeft, 107 | kGamepadBtn_DpadRight, 108 | kGamepadBtn_L2, 109 | kGamepadBtn_R2, 110 | kGamepadBtn_Count, 111 | }; 112 | 113 | extern Config g_config; 114 | 115 | void ParseConfigFile(const char *filename); 116 | int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod); 117 | int FindCmdForGamepadButton(int button, uint32 modifiers); 118 | -------------------------------------------------------------------------------- /src/consts.h: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | extern const uint8 kSlopeDataTables_SlopeType[33]; 4 | extern const uint8 kSlopeDataTables_ShapeOfSlope[512]; 5 | 6 | extern const uint16 kGlobalPalettes_Bowser[56]; 7 | extern const uint16 kGlobalPalettes_Background[]; 8 | 9 | extern const uint8 kGameModeXX_FadeInOrOut_DATA_009F2F[2]; 10 | extern const uint8 kGameModeXX_FadeInOrOut_DATA_009F31[2]; 11 | extern const uint8 kGameModeXX_FadeInOrOut_DATA_009F33[2]; 12 | 13 | extern const uint8 kGameMode19_Cutscene_SkyColorSetting[8]; 14 | 15 | extern const uint8 kGenericSpriteToSpawnTable[19]; 16 | extern const uint16 kSpr01F_MagiKoopa_MagiKoopaFadePalettes[64]; 17 | 18 | extern const uint8 kOverworldLightningAndRandomCloudSpawning_DATA_04F700[8]; 19 | extern const uint8 kOverworldLightningAndRandomCloudSpawning_DATA_04F6F8[8]; 20 | 21 | extern const uint8 kBitTable_Bank05[8]; 22 | 23 | extern const uint8 kInitializeNormalSpriteRAMTables_Sprite190FVals[201]; 24 | extern const uint16 kCircleCoordinates[256]; 25 | 26 | extern const uint16 kCalculateRowOrColumnOfTilemapToUpdate_PipeMap16Ptrs[4]; 27 | 28 | extern const uint16 kDisplayMessage_SwitchBlockTileAndProperties[32]; 29 | extern const uint8 kDisplayMessage_SwitchBlockXAndYDisp[16]; 30 | -------------------------------------------------------------------------------- /src/glsl_shader.h: -------------------------------------------------------------------------------- 1 | #ifndef ZELDA3_GLSL_SHADER_H_ 2 | #define ZELDA3_GLSL_SHADER_H_ 3 | 4 | #include "types.h" 5 | 6 | enum { 7 | kGlslMaxPasses = 20, 8 | kGlslMaxTextures = 10, 9 | }; 10 | 11 | enum GLSLScaleType { 12 | GLSL_NONE, 13 | GLSL_SOURCE, 14 | GLSL_VIEWPORT, 15 | GLSL_ABSOLUTE 16 | }; 17 | 18 | typedef struct GlslTextureUniform { 19 | int Texture; 20 | int InputSize; 21 | int TextureSize; 22 | int TexCoord; 23 | } GlslTextureUniform; 24 | 25 | typedef struct GlslUniforms { 26 | GlslTextureUniform Top; 27 | int OutputSize; 28 | int FrameCount, FrameDirection; 29 | int LUTTexCoord; 30 | int VertexCoord; 31 | GlslTextureUniform Orig; 32 | GlslTextureUniform Prev[7]; 33 | GlslTextureUniform Pass[kGlslMaxPasses]; 34 | GlslTextureUniform PassPrev[kGlslMaxPasses]; 35 | int Texture[kGlslMaxTextures]; 36 | } GlslUniforms; 37 | 38 | typedef struct GlslPass { 39 | char *filename; 40 | uint8 scale_type_x, scale_type_y; 41 | bool float_framebuffer; 42 | bool srgb_framebuffer; 43 | bool mipmap_input; 44 | float scale_x, scale_y; 45 | uint wrap_mode; 46 | uint frame_count_mod; 47 | uint frame_count; 48 | uint gl_program, gl_fbo; 49 | uint filter; 50 | uint gl_texture; 51 | uint16 width, height; 52 | GlslUniforms unif; 53 | } GlslPass; 54 | 55 | typedef struct GlTextureWithSize { 56 | uint gl_texture; 57 | uint16 width, height; 58 | } GlTextureWithSize; 59 | 60 | typedef struct GlslTexture { 61 | struct GlslTexture *next; 62 | char *id; 63 | char *filename; 64 | uint filter; 65 | uint gl_texture; 66 | uint wrap_mode; 67 | bool mipmap; 68 | int width; 69 | int height; 70 | } GlslTexture; 71 | 72 | typedef struct GlslParam { 73 | struct GlslParam *next; 74 | char *id; 75 | bool has_value; 76 | float value; 77 | float min; 78 | float max; 79 | uint uniform[kGlslMaxPasses]; 80 | } GlslParam; 81 | 82 | typedef struct GlslShader { 83 | int n_pass; 84 | GlslPass *pass; 85 | GlslParam *first_param; 86 | GlslTexture *first_texture; 87 | uint *gl_vao; 88 | uint gl_vbo; 89 | uint frame_count; 90 | int max_prev_frame; 91 | GlTextureWithSize prev_frame[8]; 92 | } GlslShader; 93 | 94 | GlslShader *GlslShader_CreateFromFile(const char *filename); 95 | void GlslShader_Destroy(GlslShader *gs); 96 | void GlslShader_Render(GlslShader *gs, GlTextureWithSize *tex, int viewport_x, int viewport_y, int viewport_width, int viewport_height); 97 | 98 | 99 | #endif // ZELDA3_GLSL_SHADER_H_ 100 | -------------------------------------------------------------------------------- /src/opengl.c: -------------------------------------------------------------------------------- 1 | #include "third_party/gl_core/gl_core_3_1.h" 2 | #include 3 | #include 4 | #include 5 | #include "types.h" 6 | #include "util.h" 7 | #include "glsl_shader.h" 8 | #include "config.h" 9 | 10 | #define CODE(...) #__VA_ARGS__ 11 | 12 | static SDL_Window *g_window; 13 | static uint8 *g_screen_buffer; 14 | static size_t g_screen_buffer_size; 15 | static int g_draw_width, g_draw_height; 16 | static unsigned int g_program, g_VAO; 17 | static GlTextureWithSize g_texture; 18 | static GlslShader *g_glsl_shader; 19 | 20 | static void GL_APIENTRY MessageCallback(GLenum source, 21 | GLenum type, 22 | GLuint id, 23 | GLenum severity, 24 | GLsizei length, 25 | const GLchar *message, 26 | const void *userParam) { 27 | if (type == GL_DEBUG_TYPE_OTHER) 28 | return; 29 | 30 | fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", 31 | (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), 32 | type, severity, message); 33 | if (type == GL_DEBUG_TYPE_ERROR) 34 | Die("OpenGL error!\n"); 35 | } 36 | 37 | static bool OpenGLRenderer_Init(SDL_Window *window) { 38 | g_window = window; 39 | SDL_GLContext context = SDL_GL_CreateContext(window); 40 | (void)context; 41 | 42 | SDL_GL_SetSwapInterval(1); 43 | ogl_LoadFunctions(); 44 | 45 | if (!ogl_IsVersionGEQ(3, 3)) 46 | Die("You need OpenGL 3.3"); 47 | 48 | if (kDebugFlag) { 49 | glEnable(GL_DEBUG_OUTPUT); 50 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 51 | glDebugMessageCallback(MessageCallback, 0); 52 | } 53 | 54 | glGenTextures(1, &g_texture.gl_texture); 55 | 56 | static const float kVertices[] = { 57 | // positions // texture coords 58 | -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, // top left 59 | -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // bottom left 60 | 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // top right 61 | 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // bottom right 62 | }; 63 | 64 | // create a vertex buffer object 65 | unsigned int vbo; 66 | glGenBuffers(1, &vbo); 67 | 68 | // vertex array object 69 | glGenVertexArrays(1, &g_VAO); 70 | // 1. bind Vertex Array Object 71 | glBindVertexArray(g_VAO); 72 | // 2. copy our vertices array in a buffer for OpenGL to use 73 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 74 | glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW); 75 | // position attribute 76 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0); 77 | glEnableVertexAttribArray(0); 78 | // texture coord attribute 79 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float))); 80 | glEnableVertexAttribArray(1); 81 | 82 | // vertex shader 83 | const GLchar *vs_code = "#version 330 core\n" CODE( 84 | layout(location = 0) in vec3 aPos; 85 | layout(location = 1) in vec2 aTexCoord; 86 | out vec2 TexCoord; 87 | void main(void) { 88 | gl_Position = vec4(aPos, 1.0); 89 | TexCoord = vec2(aTexCoord.x, aTexCoord.y); 90 | } 91 | ); 92 | 93 | unsigned int vs = glCreateShader(GL_VERTEX_SHADER); 94 | glShaderSource(vs, 1, &vs_code, NULL); 95 | glCompileShader(vs); 96 | 97 | int success; 98 | char infolog[512]; 99 | glGetShaderiv(vs, GL_COMPILE_STATUS, &success); 100 | if (!success) { 101 | glGetShaderInfoLog(vs, 512, NULL, infolog); 102 | printf("%s\n", infolog); 103 | } 104 | 105 | // fragment shader 106 | const GLchar *fs_code = "#version 330 core\n" CODE( 107 | out vec4 FragColor; 108 | in vec2 TexCoord; 109 | // texture samplers 110 | uniform sampler2D texture1; 111 | void main(void) { 112 | FragColor = texture(texture1, TexCoord); 113 | } 114 | ); 115 | 116 | unsigned int fs = glCreateShader(GL_FRAGMENT_SHADER); 117 | glShaderSource(fs, 1, &fs_code, NULL); 118 | glCompileShader(fs); 119 | 120 | glGetShaderiv(fs, GL_COMPILE_STATUS, &success); 121 | if (!success) { 122 | glGetShaderInfoLog(fs, 512, NULL, infolog); 123 | printf("%s\n", infolog); 124 | } 125 | 126 | // create program 127 | int program = g_program = glCreateProgram(); 128 | glAttachShader(program, vs); 129 | glAttachShader(program, fs); 130 | glLinkProgram(program); 131 | glGetProgramiv(program, GL_LINK_STATUS, &success); 132 | 133 | if (!success) { 134 | glGetProgramInfoLog(program, 512, NULL, infolog); 135 | printf("%s\n", infolog); 136 | } 137 | 138 | if (g_config.shader) 139 | g_glsl_shader = GlslShader_CreateFromFile(g_config.shader); 140 | 141 | return true; 142 | } 143 | 144 | static void OpenGLRenderer_Destroy(void) { 145 | } 146 | 147 | static void OpenGLRenderer_BeginDraw(int width, int height, uint8 **pixels, int *pitch) { 148 | int size = width * height; 149 | 150 | if (size > g_screen_buffer_size) { 151 | g_screen_buffer_size = size; 152 | free(g_screen_buffer); 153 | g_screen_buffer = (uint8*)malloc(size * 4); 154 | } 155 | 156 | g_draw_width = width; 157 | g_draw_height = height; 158 | *pixels = g_screen_buffer; 159 | *pitch = width * 4; 160 | } 161 | 162 | static void OpenGLRenderer_EndDraw(void) { 163 | int drawable_width, drawable_height; 164 | 165 | SDL_GL_GetDrawableSize(g_window, &drawable_width, &drawable_height); 166 | 167 | int viewport_width = drawable_width, viewport_height = drawable_height; 168 | 169 | if (!g_config.ignore_aspect_ratio) { 170 | if (viewport_width * g_draw_height < viewport_height * g_draw_width) 171 | viewport_height = viewport_width * g_draw_height / g_draw_width; // limit height 172 | else 173 | viewport_width = viewport_height * g_draw_width / g_draw_height; // limit width 174 | } 175 | 176 | int viewport_x = (drawable_width - viewport_width) >> 1; 177 | int viewport_y = (viewport_height - viewport_height) >> 1; 178 | 179 | glBindTexture(GL_TEXTURE_2D, g_texture.gl_texture); 180 | if (g_draw_width == g_texture.width && g_draw_height == g_texture.height) { 181 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_draw_width, g_draw_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer); 182 | } else { 183 | g_texture.width = g_draw_width; 184 | g_texture.height = g_draw_height; 185 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_draw_width, g_draw_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer); 186 | } 187 | 188 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 189 | glClear(GL_COLOR_BUFFER_BIT); 190 | 191 | if (g_glsl_shader == NULL) { 192 | glViewport(viewport_x, viewport_y, viewport_width, viewport_height); 193 | glUseProgram(g_program); 194 | int filter = g_config.linear_filtering ? GL_LINEAR : GL_NEAREST; 195 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); 196 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); 197 | glBindVertexArray(g_VAO); 198 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 199 | } else { 200 | GlslShader_Render(g_glsl_shader, &g_texture, viewport_x, viewport_y, viewport_width, viewport_height); 201 | } 202 | 203 | SDL_GL_SwapWindow(g_window); 204 | } 205 | 206 | 207 | static const struct RendererFuncs kOpenGLRendererFuncs = { 208 | &OpenGLRenderer_Init, 209 | &OpenGLRenderer_Destroy, 210 | &OpenGLRenderer_BeginDraw, 211 | &OpenGLRenderer_EndDraw, 212 | }; 213 | 214 | void OpenGLRenderer_Create(struct RendererFuncs *funcs) { 215 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 216 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 217 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 218 | *funcs = kOpenGLRendererFuncs; 219 | } 220 | 221 | -------------------------------------------------------------------------------- /src/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/platform/switch/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /*.nacp 3 | /*.nro 4 | /*.elf 5 | /*.smc 6 | /*.sfc -------------------------------------------------------------------------------- /src/platform/switch/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) 19 | # 20 | # NO_ICON: if set to anything, do not use icon. 21 | # NO_NACP: if set to anything, no .nacp file is generated. 22 | # APP_TITLE is the name of the app stored in the .nacp file (Optional) 23 | # APP_AUTHOR is the author of the app stored in the .nacp file (Optional) 24 | # APP_VERSION is the version of the app stored in the .nacp file (Optional) 25 | # APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) 26 | # ICON is the filename of the icon (.jpg), relative to the project folder. 27 | # If not set, it attempts to use one of the following (in this order): 28 | # - .jpg 29 | # - icon.jpg 30 | # - /default_icon.jpg 31 | # 32 | # CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. 33 | # If not set, it attempts to use one of the following (in this order): 34 | # - .json 35 | # - config.json 36 | # If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead 37 | # of a homebrew executable (.nro). This is intended to be used for sysmodules. 38 | # NACP building is skipped as well. 39 | #--------------------------------------------------------------------------------- 40 | SRC_DIR := ../../ 41 | TARGET := smw 42 | BUILD := bin 43 | SOURCES := $(SRC_DIR) $(SRC_DIR)/snes $(SRC_DIR)/platform/switch/src $(SRC_DIR)/../third_party/gl_core 44 | 45 | CFILES := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/snes/*.c $(SRC_DIR)/platform/switch/src/*.c) $(SRC_DIR)/../third_party/gl_core/gl_core_3_1.c 46 | 47 | INCLUDES := include $(SRC_DIR)/../ ./src/ 48 | APP_TITLE := Super Mario World 49 | APP_AUTHOR := snesrev & Lywx 50 | APP_VERSION := $(shell git rev-parse --short HEAD) $(shell git rev-parse --abbrev-ref HEAD) 51 | 52 | #--------------------------------------------------------------------------------- 53 | # options for code generation 54 | #--------------------------------------------------------------------------------- 55 | ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE 56 | 57 | CFLAGS := -Wall -O3 -ffast-math -ffunction-sections -Wno-parentheses \ 58 | $(ARCH) $(DEFINES) 59 | 60 | CFLAGS += -D__SWITCH__ $(INCLUDE) -DSTBI_NO_THREAD_LOCALS `sdl2-config --cflags` 61 | 62 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 63 | CFLAGS += -std=gnu11 64 | 65 | ASFLAGS := -g $(ARCH) 66 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 67 | 68 | LIBS := `$(PREFIX)pkg-config --libs sdl2` -lnx -lm 69 | 70 | #--------------------------------------------------------------------------------- 71 | # list of directories containing libraries, this must be the top level containing 72 | # include and lib 73 | #--------------------------------------------------------------------------------- 74 | LIBDIRS := $(PORTLIBS) $(LIBNX) 75 | 76 | #--------------------------------------------------------------------------------- 77 | # no real need to edit anything past this point unless you need to add additional 78 | # rules for different file extensions 79 | #--------------------------------------------------------------------------------- 80 | ifneq ($(BUILD),$(notdir $(CURDIR))) 81 | #--------------------------------------------------------------------------------- 82 | 83 | export OUTPUT := $(CURDIR)/$(TARGET) 84 | export TOPDIR := $(CURDIR) 85 | 86 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 87 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 88 | 89 | 90 | export DEPSDIR := $(CURDIR)/$(BUILD) 91 | 92 | #CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 93 | #CFILES := $(wildcard ../../*.c ../../snes/*.c) ../../third_party/gl_core/gl_core_3_1.c ../../third_party/opus-1.3.1-stripped/opus_decoder_amalgam.c 94 | #CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 95 | #SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 96 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 97 | 98 | #--------------------------------------------------------------------------------- 99 | # use CXX for linking C++ projects, CC for standard C 100 | #--------------------------------------------------------------------------------- 101 | export LD := $(CXX) 102 | #--------------------------------------------------------------------------------- 103 | 104 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 105 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(notdir $(CFILES:.c=.o)) $(SFILES:.s=.o) 106 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 107 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 108 | 109 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 110 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 111 | -I$(CURDIR)/$(BUILD) 112 | 113 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 114 | 115 | $(info $(OFILES)) 116 | 117 | ifeq ($(strip $(CONFIG_JSON)),) 118 | jsons := $(wildcard *.json) 119 | ifneq (,$(findstring $(TARGET).json,$(jsons))) 120 | export APP_JSON := $(TOPDIR)/$(TARGET).json 121 | else 122 | ifneq (,$(findstring config.json,$(jsons))) 123 | export APP_JSON := $(TOPDIR)/config.json 124 | endif 125 | endif 126 | else 127 | export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) 128 | endif 129 | 130 | ifeq ($(strip $(ICON)),) 131 | icons := $(wildcard *.jpg) 132 | ifneq (,$(findstring $(TARGET).jpg,$(icons))) 133 | export APP_ICON := $(TOPDIR)/$(TARGET).jpg 134 | else 135 | ifneq (,$(findstring icon.jpg,$(icons))) 136 | export APP_ICON := $(TOPDIR)/icon.jpg 137 | endif 138 | endif 139 | else 140 | export APP_ICON := $(TOPDIR)/$(ICON) 141 | endif 142 | 143 | ifeq ($(strip $(NO_ICON)),) 144 | export NROFLAGS += --icon=$(APP_ICON) 145 | endif 146 | 147 | ifeq ($(strip $(NO_NACP)),) 148 | export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp 149 | endif 150 | 151 | ifneq ($(APP_TITLEID),) 152 | export NACPFLAGS += --titleid=$(APP_TITLEID) 153 | endif 154 | 155 | ifneq ($(ROMFS),) 156 | export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) 157 | endif 158 | 159 | .PHONY: $(BUILD) clean all 160 | 161 | #--------------------------------------------------------------------------------- 162 | all: $(BUILD) 163 | 164 | $(BUILD): 165 | @echo $(CFILES) ... 166 | @[ -d $@ ] || mkdir -p $@ 167 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 168 | 169 | #--------------------------------------------------------------------------------- 170 | clean: 171 | @echo clean ... 172 | ifeq ($(strip $(APP_JSON)),) 173 | @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf 174 | else 175 | @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf 176 | endif 177 | 178 | 179 | #--------------------------------------------------------------------------------- 180 | else 181 | .PHONY: all 182 | 183 | DEPENDS := $(OFILES:.o=.d) 184 | 185 | #--------------------------------------------------------------------------------- 186 | # main targets 187 | #--------------------------------------------------------------------------------- 188 | ifeq ($(strip $(APP_JSON)),) 189 | 190 | all : $(OUTPUT).nro 191 | 192 | ifeq ($(strip $(NO_NACP)),) 193 | $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp 194 | else 195 | $(OUTPUT).nro : $(OUTPUT).elf 196 | endif 197 | 198 | else 199 | 200 | all : $(OUTPUT).nsp 201 | 202 | $(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm 203 | 204 | $(OUTPUT).nso : $(OUTPUT).elf 205 | 206 | endif 207 | 208 | $(OUTPUT).elf : $(OFILES) 209 | 210 | $(OFILES_SRC) : $(HFILES_BIN) 211 | 212 | #--------------------------------------------------------------------------------- 213 | # you need a rule like this for each extension you use as binary data 214 | #--------------------------------------------------------------------------------- 215 | %.bin.o %_bin.h : %.bin 216 | #--------------------------------------------------------------------------------- 217 | @echo $(notdir $<) 218 | @$(bin2o) 219 | 220 | -include $(DEPENDS) 221 | 222 | #--------------------------------------------------------------------------------------- 223 | endif 224 | #--------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /src/platform/switch/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snesrev/smw/eae20c65c58930c8b62c76188d259579ad4130f1/src/platform/switch/icon.jpg -------------------------------------------------------------------------------- /src/platform/switch/smw.ini: -------------------------------------------------------------------------------- 1 | [General] 2 | # Automatically save state on quit and reload on start 3 | Autosave = 0 4 | 5 | # Disable the SDL_Delay that happens each frame (Gives slightly better perf if your 6 | # display is set to exactly 60hz) 7 | DisableFrameDelay = 0 8 | 9 | # Save a snapshot each time a level is completed 10 | SavePlaythrough = 0 11 | 12 | 13 | [Graphics] 14 | # Window size ( Auto or WidthxHeight ) 15 | WindowSize = 1920x1080 16 | 17 | # Fullscreen mode (0=windowed, 1=desktop fullscreen, 2=fullscreen w/mode change) 18 | Fullscreen = 0 19 | 20 | # Window scale (1=100%, 2=200%, 3=300%, etc.) 21 | WindowScale = 1 22 | 23 | # Use an optimized (but potentially more buggy) SNES PPU implementation 24 | NewRenderer = 1 25 | 26 | # Don't keep the aspect ratio 27 | IgnoreAspectRatio = 1 28 | 29 | # Enable this option to remove the sprite limits per scan line 30 | NoSpriteLimits = 1 31 | 32 | # Use either SDL, SDL-Software, or OpenGL as the output method 33 | # SDL-Software rendering might give better performance on Raspberry pi. 34 | #OutputMethod = SDL 35 | 36 | # Set to true to use linear filtering. Gives less crisp pixels. Works with SDL and OpenGL. 37 | #LinearFiltering = 0 38 | 39 | # Set a glsl shader. Only supported with the OpenGL output method 40 | # This can be the path to a .glsl or .glslp file 41 | # Get them with: git clone https://github.com/snesrev/glsl-shaders 42 | #Shader = glsl-shaders/hqx/hq4x.glslp 43 | 44 | [Sound] 45 | EnableAudio = 1 46 | 47 | # DSP frequency in samples per second (e.g. 48000, 44100, 32000, 22050, 11025) 48 | AudioFreq = 44100 49 | 50 | # number of separate sound channels (1=mono, 2=stereo) 51 | AudioChannels = 2 52 | 53 | # Audio buffer size in samples (power of 2; e.g., 4096, 2048, 1024) [try 1024 if sound is crackly]. The higher the more lag before you hear sounds. 54 | AudioSamples = 1024 55 | 56 | [KeyMap] 57 | # Change what keyboard keys map to the joypad 58 | # Order: Up, Down, Left, Right, Select, Start, A, B, X, Y, L, R 59 | 60 | # This default is suitable for QWERTY keyboards. 61 | Controls = Up, Down, Left, Right, Right Shift, Return, x, z, s, a, c, v 62 | 63 | # This default is suitable for QWERTZ keyboards. 64 | #Controls = Up, Down, Left, Right, Right Shift, Return, x, y, s, a, c, v 65 | 66 | # This one is suitable for AZERTY keyboards. 67 | #Controls = Up, Down, Left, Right, Right Shift, Return, x, w, s, q, c, v 68 | 69 | CheatLife = w 70 | CheatJump = Ctrl+q 71 | ClearKeyLog = k 72 | StopReplay = l 73 | Fullscreen = Alt+Return 74 | Reset = Ctrl+r 75 | Pause = Shift+p 76 | PauseDimmed = p 77 | Turbo = Tab 78 | ReplayTurbo = t 79 | WindowBigger = Ctrl+Up 80 | WindowSmaller = Ctrl+Down 81 | 82 | VolumeUp = Shift+= 83 | VolumeDown = Shift+- 84 | 85 | Load = F1, F2, F3, F4, F5, F6, F7, F8, F9, F10 86 | Save = Shift+F1,Shift+F2,Shift+F3,Shift+F4,Shift+F5,Shift+F6,Shift+F7,Shift+F8,Shift+F9,Shift+F10 87 | Replay= Ctrl+F1,Ctrl+F2,Ctrl+F3,Ctrl+F4,Ctrl+F5,Ctrl+F6,Ctrl+F7,Ctrl+F8,Ctrl+F9,Ctrl+F10 88 | 89 | LoadRef = 1,2,3,4,5,6,7,8,9,0,-,=,Backspace 90 | ReplayRef = Ctrl+1,Ctrl+2,Ctrl+3,Ctrl+4,Ctrl+5,Ctrl+6,Ctrl+7,Ctrl+8,Ctrl+9,Ctrl+0,Ctrl+-,Ctrl+=,Ctrl+Backspace 91 | 92 | [GamepadMap] 93 | # Any keys used in KeyMap can be used also in this section. 94 | # The shoulder button is called L1/Lb and L2, and the thumbstick button is called L3 95 | Controls = DpadUp, DpadDown, DpadLeft, DpadRight, Back, Start, B, A, Y, X, Lb, Rb 96 | -------------------------------------------------------------------------------- /src/platform/switch/src/switch_impl.c: -------------------------------------------------------------------------------- 1 | #include "switch_impl.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void SwitchImpl_Init() { 8 | appletInitializeGamePlayRecording(); 9 | appletSetGamePlayRecordingState(true); 10 | appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend); 11 | socketInitializeDefault(); 12 | #ifdef DEBUG 13 | nxlinkStdio(); 14 | #endif 15 | } 16 | 17 | void SwitchImpl_Exit() { 18 | appletSetGamePlayRecordingState(false); 19 | socketExit(); 20 | } 21 | 22 | void PrintErrorMessageToScreen(const char* str, ...) { 23 | consoleInit(NULL); 24 | 25 | va_list args; 26 | va_start(args, str); 27 | vprintf(str, args); 28 | va_end(args); 29 | 30 | while (appletMainLoop()) { 31 | consoleUpdate(NULL); 32 | } 33 | 34 | consoleExit(NULL); 35 | } 36 | 37 | // Error messages 38 | 39 | void ThrowMissingROM() { 40 | PrintErrorMessageToScreen( 41 | "\x1b[2;2HYou've launched Super Mario World without the rom file." 42 | "\x1b[4;2HPlease relaunch making sure smw.sfc exists." 43 | "\x1b[44;2HMade with <3 by snesrev and Lywx" 44 | ); 45 | } -------------------------------------------------------------------------------- /src/platform/switch/src/switch_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void SwitchImpl_Init(); 4 | void SwitchImpl_Exit(); 5 | 6 | // Error messages 7 | 8 | void ThrowMissingROM(); -------------------------------------------------------------------------------- /src/platform/win32/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by zelda3.rc 4 | // 5 | #define IDI_ICON1 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /src/platform/win32/smw.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "platform\\win32\\resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_ICON1 ICON "platform\\win32\\triforce.ico" 56 | 57 | #endif // English (United States) resources 58 | ///////////////////////////////////////////////////////////////////////////// 59 | 60 | 61 | 62 | #ifndef APSTUDIO_INVOKED 63 | ///////////////////////////////////////////////////////////////////////////// 64 | // 65 | // Generated from the TEXTINCLUDE 3 resource. 66 | // 67 | 68 | 69 | ///////////////////////////////////////////////////////////////////////////// 70 | #endif // not APSTUDIO_INVOKED 71 | 72 | -------------------------------------------------------------------------------- /src/platform/win32/volume_control.c: -------------------------------------------------------------------------------- 1 | #include "volume_control.h" 2 | 3 | #define COBJMACROS 4 | #define CINTERFACE 5 | 6 | #define MICROSOFT_WINDOWS_WINBASE_H_DEFINE_INTERLOCKED_CPLUSPLUS_OVERLOADS 0 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static ISimpleAudioVolume *GetSimpleAudioVolume(); 14 | 15 | DEFINE_GUID(IID_IMMDeviceEnumerator, 0XA95664D2, 0X9614, 0X4F35, 0XA7, 0X46, 0XDE, 0X8D, 0XB6, 0X36, 0X17, 0XE6); 16 | DEFINE_GUID(CLSID_MMDeviceEnumerator, 0XBCDE0395, 0XE52F, 0X467C, 0X8E, 0X3D, 0XC4, 0X57, 0X92, 0X91, 0X69, 0X2E); 17 | DEFINE_GUID(IID_IAudioSessionManager, 0X77AA99A0, 0X1BD6, 0X484F, 0X8B, 0XC7, 0X2C, 0X65, 0X4C, 0X9A, 0X9B, 0X6F); 18 | 19 | static void InitializeCom(void) { 20 | static bool com_initialized; 21 | if (!com_initialized) 22 | com_initialized = SUCCEEDED(CoInitialize(NULL)); 23 | } 24 | 25 | int GetApplicationVolume(void) { 26 | ISimpleAudioVolume *simple_audio_volume = GetSimpleAudioVolume(); 27 | if (!simple_audio_volume) 28 | return false; 29 | float volume_level = -1; 30 | HRESULT result = ISimpleAudioVolume_GetMasterVolume(simple_audio_volume, &volume_level); 31 | ISimpleAudioVolume_Release(simple_audio_volume); 32 | return (int)(volume_level * 100); 33 | } 34 | 35 | bool SetApplicationVolume(int volume_level) { 36 | ISimpleAudioVolume *simple_audio_volume = GetSimpleAudioVolume(); 37 | if (!simple_audio_volume) 38 | return false; 39 | HRESULT result = ISimpleAudioVolume_SetMasterVolume(simple_audio_volume, (float)(volume_level / 100.0), NULL); 40 | ISimpleAudioVolume_Release(simple_audio_volume); 41 | return SUCCEEDED(result); 42 | } 43 | 44 | bool SetApplicationMuted(bool muted) { 45 | ISimpleAudioVolume *simple_audio_volume = GetSimpleAudioVolume(); 46 | if (!simple_audio_volume) 47 | return false; 48 | HRESULT result = ISimpleAudioVolume_SetMute(simple_audio_volume, muted, NULL); 49 | ISimpleAudioVolume_Release(simple_audio_volume); 50 | return SUCCEEDED(result); 51 | } 52 | 53 | static ISimpleAudioVolume *GetSimpleAudioVolume(void) { 54 | HRESULT result; 55 | IMMDeviceEnumerator *device_enumerator = NULL; 56 | IMMDevice *device = NULL; 57 | IAudioSessionManager *audio_session_manager = NULL; 58 | ISimpleAudioVolume *simple_audio_volume = NULL; 59 | 60 | InitializeCom(); 61 | 62 | result = CoCreateInstance(&CLSID_MMDeviceEnumerator, 63 | NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &device_enumerator); 64 | if (FAILED(result) || device_enumerator == NULL) 65 | goto done; 66 | 67 | result = IMMDeviceEnumerator_GetDefaultAudioEndpoint(device_enumerator, eRender, eConsole, &device); 68 | if (FAILED(result) || device == NULL) 69 | goto done; 70 | 71 | result = IMMDevice_Activate(device, &IID_IAudioSessionManager, CLSCTX_INPROC_SERVER, NULL, &audio_session_manager); 72 | if (FAILED(result) || audio_session_manager == NULL) 73 | goto done; 74 | result = IAudioSessionManager_GetSimpleAudioVolume(audio_session_manager, &GUID_NULL, 0, &simple_audio_volume); 75 | 76 | done: 77 | if (device_enumerator) IMMDeviceEnumerator_Release(device_enumerator); 78 | if (device) IMMDevice_Release(device); 79 | if (audio_session_manager) IAudioSessionManager_Release(audio_session_manager); 80 | 81 | return simple_audio_volume; 82 | } 83 | -------------------------------------------------------------------------------- /src/platform/win32/volume_control.h: -------------------------------------------------------------------------------- 1 | #ifndef ZELDA3_PLATFORM_WIN32_VOLUME_CONTROL_H_ 2 | #define ZELDA3_PLATFORM_WIN32_VOLUME_CONTROL_H_ 3 | 4 | #include 5 | 6 | #ifndef SYSTEM_VOLUME_MIXER_AVAILABLE 7 | #define SYSTEM_VOLUME_MIXER_AVAILABLE 1 8 | #endif // SYSTEM_VOLUME_MIXER_AVAILABLE 9 | 10 | int GetApplicationVolume(); 11 | bool SetApplicationVolume(int volume_level); 12 | bool SetApplicationMuted(bool muted); 13 | 14 | #endif // ZELDA3_PLATFORM_WIN32_VOLUME_CONTROL_H_ 15 | -------------------------------------------------------------------------------- /src/smw.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {0557fe1f-910c-415c-8384-20f8ff3c6d87} 6 | 7 | 8 | {5b002459-3732-4987-8e36-5228b1cf9b01} 9 | 10 | 11 | {2b72ed96-9194-4c2c-b1e5-15445f0a9550} 12 | 13 | 14 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 15 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 16 | 17 | 18 | {c20be95b-4f11-4834-a006-bfb273e4bc0b} 19 | 20 | 21 | {e1763dbc-4fb3-417f-ad1a-8436411c3b7a} 22 | 23 | 24 | 25 | 26 | Smw 27 | 28 | 29 | Snes 30 | 31 | 32 | Snes 33 | 34 | 35 | Snes 36 | 37 | 38 | Snes 39 | 40 | 41 | Snes 42 | 43 | 44 | Snes 45 | 46 | 47 | Snes 48 | 49 | 50 | Snes 51 | 52 | 53 | Snes 54 | 55 | 56 | Snes 57 | 58 | 59 | Snes 60 | 61 | 62 | Common\Shader 63 | 64 | 65 | Common\Shader 66 | 67 | 68 | Common\Shader 69 | 70 | 71 | Smw 72 | 73 | 74 | Smw 75 | 76 | 77 | Smw 78 | 79 | 80 | Smw 81 | 82 | 83 | Smw 84 | 85 | 86 | Smw 87 | 88 | 89 | Smw 90 | 91 | 92 | Smw 93 | 94 | 95 | Smw 96 | 97 | 98 | Smw 99 | 100 | 101 | Smw 102 | 103 | 104 | Snes 105 | 106 | 107 | Smb1 108 | 109 | 110 | Smb1 111 | 112 | 113 | Smb1 114 | 115 | 116 | Smb1 117 | 118 | 119 | Common 120 | 121 | 122 | Common 123 | 124 | 125 | Common 126 | 127 | 128 | Common 129 | 130 | 131 | Common 132 | 133 | 134 | Smb1 135 | 136 | 137 | Common 138 | 139 | 140 | Smb Lost Levels 141 | 142 | 143 | Smb Lost Levels 144 | 145 | 146 | Smb Lost Levels 147 | 148 | 149 | Smb Lost Levels 150 | 151 | 152 | Smb Lost Levels 153 | 154 | 155 | Smb Lost Levels 156 | 157 | 158 | Smb1 159 | 160 | 161 | Smw 162 | 163 | 164 | 165 | 166 | Smw 167 | 168 | 169 | Smw 170 | 171 | 172 | Smw 173 | 174 | 175 | Snes 176 | 177 | 178 | Snes 179 | 180 | 181 | Snes 182 | 183 | 184 | Snes 185 | 186 | 187 | Snes 188 | 189 | 190 | Snes 191 | 192 | 193 | Snes 194 | 195 | 196 | Snes 197 | 198 | 199 | Snes 200 | 201 | 202 | Snes 203 | 204 | 205 | Snes 206 | 207 | 208 | Snes 209 | 210 | 211 | Common\Shader 212 | 213 | 214 | Common\Shader 215 | 216 | 217 | Snes 218 | 219 | 220 | Smb1 221 | 222 | 223 | Smb1 224 | 225 | 226 | Smb1 227 | 228 | 229 | Smb1 230 | 231 | 232 | Smw 233 | 234 | 235 | Smw 236 | 237 | 238 | Common 239 | 240 | 241 | Common 242 | 243 | 244 | Common 245 | 246 | 247 | Common 248 | 249 | 250 | Common 251 | 252 | 253 | Smb Lost Levels 254 | 255 | 256 | Smb Lost Levels 257 | 258 | 259 | Smb Lost Levels 260 | 261 | 262 | Smb Lost Levels 263 | 264 | 265 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /src/smw_07.c: -------------------------------------------------------------------------------- 1 | #include "consts.h" 2 | #include "funcs.h" 3 | #include "smw_rtl.h" 4 | #include "variables.h" 5 | 6 | static const uint8 kInitializeNormalSpriteRAMTables_Sprite1656Vals[201] = { 0x70, 0x70, 0x70, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0, 0x10, 0x10, 0x10, 0x14, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x11, 0x81, 0x10, 0x10, 0x80, 0x11, 0x11, 0x82, 0x0, 0x13, 0x13, 0x13, 0x13, 0x1, 0x0, 0x0, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x80, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x0, 0x0, 0x30, 0x30, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x10, 0x10, 0x8c, 0x8c, 0x10, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0xb, 0xb, 0xb, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x19, 0x30, 0xa, 0x10, 0x10, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x10, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, }; 7 | static const uint8 kInitializeNormalSpriteRAMTables_Sprite1662Vals[201] = { 0x0, 0x0, 0x0, 0x0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0, 0xa, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x81, 0x1, 0x80, 0x80, 0x0, 0x81, 0x81, 0x0, 0x0, 0x81, 0x81, 0x81, 0x81, 0x6, 0x0, 0x7, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x37, 0x0, 0x0, 0x9, 0x1, 0x0, 0x0, 0x0, 0xe, 0xe, 0xe, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf, 0x10, 0x14, 0x0, 0xd, 0x80, 0x0, 0x1d, 0x0, 0x80, 0x80, 0x80, 0x80, 0x0, 0x0, 0x80, 0x2, 0xc, 0x3, 0x5, 0x4, 0x5, 0x4, 0x0, 0x0, 0x4, 0x5, 0x4, 0x5, 0x0, 0x1d, 0xc, 0x4, 0x4, 0x12, 0x20, 0x21, 0x2c, 0x34, 0x4, 0x4, 0x4, 0x4, 0xc, 0x16, 0x0, 0x17, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x35, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0xc, 0xc, 0x0, 0x0, 0x3a, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x8, 0x38, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0x0, 0xd, 0x80, 0x1d, 0x0, 0x0, 0xb6, 0x24, 0x23, 0x3b, 0x1f, 0x22, 0x0, 0x27, 0x0, 0x0, 0x28, 0x0, 0x2a, 0x2b, 0x2b, 0x0, 0x0, 0x0, 0xc, 0x0, 0x2d, 0x0, 0x0, 0x0, 0x2e, 0x2e, 0xc, 0x1d, 0x2f, 0xc, 0x0, 0x80, 0x30, 0x32, 0x31, 0x0, 0x0, 0x33, 0x7, 0xff, 0x0, 0xc, }; 8 | static const uint8 kInitializeNormalSpriteRAMTables_Sprite166EVals[201] = { 0xa, 0x8, 0x6, 0x4, 0xa, 0x8, 0x6, 0x4, 0xa, 0xa, 0x8, 0x8, 0x4, 0x17, 0x32, 0x4, 0x4, 0x1d, 0x3d, 0x9, 0x9, 0x45, 0x45, 0x85, 0x85, 0xb, 0x8, 0x1, 0x12, 0x15, 0x9, 0x4f, 0x1c, 0x24, 0xb, 0x9, 0xb, 0x9, 0x33, 0x33, 0xfd, 0x2b, 0x8, 0x35, 0x3b, 0x3a, 0x19, 0x3a, 0x13, 0x13, 0x13, 0x34, 0x39, 0x2a, 0x15, 0xf3, 0xfd, 0xfd, 0x37, 0x37, 0x37, 0xc7, 0x30, 0x5, 0x15, 0x37, 0x37, 0x37, 0x33, 0x30, 0x8b, 0x85, 0x1d, 0x3b, 0x3b, 0x9, 0x34, 0x1, 0x1, 0x8, 0x8, 0x9, 0x20, 0x30, 0x20, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe1, 0xe1, 0xeb, 0xeb, 0xe3, 0xe3, 0xe3, 0xe1, 0xe1, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xe3, 0xf0, 0xe3, 0xf3, 0x3f, 0x3f, 0xf, 0x35, 0xb, 0x9, 0x7, 0x8, 0xa, 0x20, 0x24, 0xa, 0x3a, 0x3a, 0x20, 0x20, 0x21, 0x28, 0x20, 0x20, 0x0, 0x20, 0x20, 0x20, 0x20, 0xf5, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x3b, 0xf3, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0x9b, 0x93, 0x0, 0x30, 0x31, 0x31, 0x31, 0xfb, 0xfb, 0xbb, 0xe3, 0xf3, 0x35, 0x35, 0x39, 0x35, 0x35, 0x7d, 0x7, 0x37, 0x37, 0x3d, 0x3f, 0x3f, 0x30, 0x31, 0x31, 0x31, 0x4, 0x35, 0x3b, 0x3b, 0x36, 0x7b, 0x3b, 0x33, 0x6, 0xb, 0x11, 0xf5, 0xf5, 0xcb, 0xcd, 0xf3, 0x3f, 0xff, 0x20, 0x38, }; 9 | static const uint8 kInitializeNormalSpriteRAMTables_Sprite167AVals[201] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x2, 0x18, 0x0, 0x0, 0x81, 0x0, 0x1, 0x99, 0x99, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x83, 0x0, 0x0, 0x9a, 0x1e, 0x1, 0xbe, 0x81, 0x81, 0x81, 0x2, 0x18, 0x87, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x3e, 0x1, 0x1, 0x82, 0x82, 0x82, 0x1, 0x2, 0x81, 0x0, 0x0, 0xa2, 0x82, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa2, 0x8, 0x2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0x22, 0x22, 0x22, 0x22, 0xa2, 0xa2, 0xa2, 0xa2, 0xe2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0x82, 0x82, 0xa2, 0xa2, 0x9a, 0x80, 0x82, 0x3e, 0xc2, 0x82, 0x82, 0x82, 0x92, 0x80, 0x82, 0x82, 0x82, 0x2, 0x2, 0x2, 0x2, 0xa2, 0xa2, 0x1, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0x1, 0x1, 0x1, 0xa2, 0x81, 0x0, 0x1, 0x80, 0x0, 0x19, 0xa2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x81, 0x1, 0x81, 0x81, 0x81, 0x0, 0x1, 0x1, 0xa2, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa0, 0x1, 0x1, 0xa1, 0xa2, 0xa2, 0x1, 0x1, 0xa2, 0xa3, 0xff, 0x82, 0xa2, }; 10 | static const uint8 kInitializeNormalSpriteRAMTables_Sprite1686Vals[201] = { 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x42, 0x52, 0x52, 0x52, 0x52, 0x0, 0x9, 0x0, 0x40, 0x0, 0x1, 0x0, 0x0, 0x10, 0x10, 0x90, 0x90, 0x1, 0x10, 0x10, 0x90, 0x0, 0x11, 0x1, 0x1, 0x8, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x19, 0x80, 0x0, 0x39, 0x9, 0x9, 0x10, 0xa, 0x9, 0x9, 0x9, 0x99, 0x18, 0x29, 0x8, 0x19, 0x19, 0x19, 0x11, 0x11, 0x15, 0x10, 0xa, 0x40, 0x40, 0x8d, 0x8d, 0x8d, 0x11, 0x18, 0x11, 0x80, 0x0, 0x29, 0x29, 0x10, 0x10, 0x10, 0x10, 0x0, 0x0, 0x10, 0x29, 0x20, 0x29, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0x29, 0x29, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x29, 0x19, 0x29, 0x29, 0x59, 0x59, 0x18, 0x18, 0x10, 0x10, 0x50, 0x28, 0x28, 0x28, 0x28, 0x8, 0x29, 0x29, 0x39, 0x39, 0x29, 0x28, 0x28, 0x3a, 0x28, 0x29, 0x31, 0x31, 0x29, 0x0, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x11, 0x1, 0x39, 0x10, 0x19, 0x19, 0x19, 0x19, 0x1, 0x29, 0x98, 0x14, 0x14, 0x10, 0x18, 0x18, 0x18, 0x0, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1d, 0x1d, 0x19, 0x19, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1d, 0x19, 0x18, 0x0, 0x10, 0x0, 0x99, 0x99, 0x10, 0x90, 0xa9, 0xb9, 0xff, 0x39, 0x19, }; 11 | const uint8 kInitializeNormalSpriteRAMTables_Sprite190FVals[201] = { 0x0, 0x0, 0x0, 0x0, 0xa0, 0xa0, 0xa0, 0xa0, 0xb0, 0xb0, 0xb0, 0xb0, 0xa0, 0x80, 0x44, 0x80, 0x80, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x0, 0x0, 0x0, 0x60, 0x20, 0x4, 0x4, 0x20, 0x20, 0x20, 0x20, 0x24, 0x4, 0x0, 0x44, 0x20, 0x4, 0x44, 0xc4, 0x0, 0xc4, 0x24, 0x24, 0x24, 0x4, 0x4, 0x46, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0xc4, 0x0, 0x0, 0x5, 0x5, 0x5, 0x4, 0x44, 0x48, 0x0, 0x0, 0x40, 0x40, 0x40, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0xc4, 0x64, 0x45, 0x65, 0x45, 0x65, 0x45, 0x45, 0x45, 0x45, 0x65, 0x65, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x5, 0x5, 0x44, 0x44, 0x44, 0x44, 0x46, 0x0, 0x0, 0x0, 0x10, 0x10, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, 0x42, 0x40, 0x40, 0x40, 0xc0, 0x40, 0x40, 0x40, 0x40, 0x0, 0x0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x1, 0x0, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x0, 0x40, 0x40, 0x40, 0x0, 0x4, 0x4, 0x40, 0x40, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x41, 0x41, 0x40, 0x41, 0x40, 0x0, 0x0, 0x0, 0x20, 0x47, 0x45, 0x0, 0x0, 0x41, 0x41, 0xff, 0x40, 0x40, }; 12 | static const uint8 kSpawnSpinJumpStars_InitialXSpeed[4] = { 0xe0, 0x20, 0xe0, 0x20, }; 13 | static const uint8 kSpawnSpinJumpStars_InitialYSpeed[4] = { 0xf0, 0xf0, 0x10, 0x10, }; 14 | const uint16 kCircleCoordinates[256] = { 0x0, 0x3, 0x6, 0x9, 0xc, 0xf, 0x12, 0x15, 0x19, 0x1c, 0x1f, 0x22, 0x25, 0x28, 0x2b, 0x2e, 0x31, 0x35, 0x38, 0x3b, 0x3e, 0x41, 0x44, 0x47, 0x4a, 0x4d, 0x50, 0x53, 0x56, 0x59, 0x5c, 0x5f, 0x61, 0x64, 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x75, 0x78, 0x7b, 0x7e, 0x80, 0x83, 0x86, 0x88, 0x8b, 0x8e, 0x90, 0x93, 0x95, 0x98, 0x9b, 0x9d, 0x9f, 0xa2, 0xa4, 0xa7, 0xa9, 0xab, 0xae, 0xb0, 0xb2, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, 0xbf, 0xc1, 0xc3, 0xc5, 0xc7, 0xc9, 0xcb, 0xcd, 0xcf, 0xd1, 0xd3, 0xd4, 0xd6, 0xd8, 0xd9, 0xdb, 0xdd, 0xde, 0xe0, 0xe1, 0xe3, 0xe4, 0xe6, 0xe7, 0xe8, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x100, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xf9, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf4, 0xf3, 0xf2, 0xf1, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe8, 0xe7, 0xe6, 0xe4, 0xe3, 0xe1, 0xe0, 0xde, 0xdd, 0xdb, 0xd9, 0xd8, 0xd6, 0xd4, 0xd3, 0xd1, 0xcf, 0xcd, 0xcb, 0xc9, 0xc7, 0xc5, 0xc3, 0xc1, 0xbf, 0xbd, 0xbb, 0xb9, 0xb7, 0xb5, 0xb2, 0xb0, 0xae, 0xab, 0xa9, 0xa7, 0xa4, 0xa2, 0x9f, 0x9d, 0x9b, 0x98, 0x95, 0x93, 0x90, 0x8e, 0x8b, 0x88, 0x86, 0x83, 0x80, 0x7e, 0x7b, 0x78, 0x75, 0x73, 0x70, 0x6d, 0x6a, 0x67, 0x64, 0x61, 0x5f, 0x5c, 0x59, 0x56, 0x53, 0x50, 0x4d, 0x4a, 0x47, 0x44, 0x41, 0x3e, 0x3b, 0x38, 0x35, 0x31, 0x2e, 0x2b, 0x28, 0x25, 0x22, 0x1f, 0x1c, 0x19, 0x15, 0x12, 0xf, 0xc, 0x9, 0x6, 0x3, }; 15 | 16 | void InitializeNormalSpriteRAMTables_ClearTables(uint8 k) { // 07f722 17 | spr_table164a[k] = 0; 18 | spr_table1632[k] = 0; 19 | spr_table00c2[k] = 0; 20 | spr_table151c[k] = 0; 21 | spr_table1528[k] = 0; 22 | spr_table1534[k] = 0; 23 | spr_table157c[k] = 0; 24 | spr_table1588[k] = 0; 25 | spr_table15c4[k] = 0; 26 | spr_table1602[k] = 0; 27 | spr_decrementing_table1540[k] = 0; 28 | spr_decrementing_table154c[k] = 0; 29 | spr_decrementing_table1558[k] = 0; 30 | spr_decrementing_table1564[k] = 0; 31 | spr_decrementing_table1fe2[k] = 0; 32 | spr_table1626[k] = 0; 33 | spr_table1570[k] = 0; 34 | spr_xspeed[k] = 0; 35 | spr_sub_xpos[k] = 0; 36 | spr_yspeed[k] = 0; 37 | spr_sub_ypos[k] = 0; 38 | spr_no_level_collision_flag[k] = 0; 39 | spr_table15d0[k] = 0; 40 | spr_decrementing_table163e[k] = 0; 41 | spr_property_bits1656[k] = 0; 42 | spr_property_bits1662[k] = 0; 43 | spr_property_bits166e[k] = 0; 44 | spr_property_bits167a[k] = 0; 45 | spr_property_bits1686[k] = 0; 46 | spr_table187b[k] = 0; 47 | spr_table160e[k] = 0; 48 | spr_table1594[k] = 0; 49 | spr_table1504[k] = 0; 50 | spr_unused_table1fd6[k] = 0; 51 | spr_xoffscreen_flag[k] = 1; 52 | } 53 | 54 | void InitializeNormalSpriteRAMTables_YXPPCCCTAndPropertyTables(uint8 k) { // 07f78b 55 | spr_table15f6[k] = kInitializeNormalSpriteRAMTables_Sprite166EVals[spr_spriteid[k]] & 0xF; 56 | InitializeNormalSpriteRAMTables_PropertyTables(k); 57 | } 58 | 59 | void InitializeNormalSpriteRAMTables_PropertyTables(uint8 k) { // 07f7a0 60 | uint8 v2 = spr_spriteid[k]; 61 | spr_property_bits1656[k] = kInitializeNormalSpriteRAMTables_Sprite1656Vals[v2]; 62 | spr_property_bits1662[k] = kInitializeNormalSpriteRAMTables_Sprite1662Vals[v2]; 63 | spr_property_bits166e[k] = kInitializeNormalSpriteRAMTables_Sprite166EVals[v2]; 64 | spr_property_bits167a[k] = kInitializeNormalSpriteRAMTables_Sprite167AVals[v2]; 65 | spr_property_bits1686[k] = kInitializeNormalSpriteRAMTables_Sprite1686Vals[v2]; 66 | spr_property_bits190f[k] = kInitializeNormalSpriteRAMTables_Sprite190FVals[v2]; 67 | } 68 | 69 | void InitializeNormalSpriteRAMTables(uint8 k) { // 07f7d2 70 | InitializeNormalSpriteRAMTables_ClearTables(k); 71 | InitializeNormalSpriteRAMTables_YXPPCCCTAndPropertyTables(k); 72 | } 73 | 74 | void SpawnSpinJumpStars(uint8 k) { // 07fc3b 75 | for (int8 i = 3; i >= 0; --i) 76 | SpawnSpinJumpStars_07FC47(i); 77 | } 78 | 79 | void SpawnSpinJumpStars_07FC47(uint8 k) { // 07fc47 80 | uint8 v1 = 7; 81 | while (ext_spr_spriteid[v1]) { 82 | if ((--v1 & 0x80) != 0) 83 | return; 84 | } 85 | ext_spr_spriteid[v1] = 16; 86 | uint8 v2 = spr_current_slotid; 87 | SetHiLo(&ext_spr_ypos_hi[v1], &ext_spr_ypos_lo[v1], GetSprYPos(v2) + 4); 88 | SetHiLo(&ext_spr_xpos_hi[v1], &ext_spr_xpos_lo[v1], GetSprXPos(v2) + 4); 89 | ext_spr_xspeed[v1] = kSpawnSpinJumpStars_InitialXSpeed[k]; 90 | ext_spr_yspeed[v1] = kSpawnSpinJumpStars_InitialYSpeed[k]; 91 | ext_spr_decrementing_table176f[v1] = 23; 92 | } 93 | -------------------------------------------------------------------------------- /src/smw_rtl.c: -------------------------------------------------------------------------------- 1 | #include "smw_rtl.h" 2 | #include "variables.h" 3 | #include 4 | #include "common_cpu_infra.h" 5 | #include "snes/snes.h" 6 | #include "src/funcs.h" 7 | 8 | const uint8 *ptr_layer1_data; 9 | const uint8 *ptr_layer2_data; 10 | uint8 ptr_layer2_is_bg; 11 | 12 | uint8 *ptr_lo_map16_data; 13 | uint8 *ptr_lo_map16_data_bak; 14 | 15 | bool g_lunar_magic; 16 | 17 | 18 | void AddSprXPos(uint8 k, uint16 x) { 19 | AddHiLo(&spr_xpos_hi[k], &spr_xpos_lo[k], x); 20 | } 21 | 22 | void AddSprYPos(uint8 k, uint16 y) { 23 | AddHiLo(&spr_ypos_hi[k], &spr_ypos_lo[k], y); 24 | } 25 | 26 | void AddSprXYPos(uint8 k, uint16 x, uint16 y) { 27 | AddHiLo(&spr_xpos_hi[k], &spr_xpos_lo[k], x); 28 | AddHiLo(&spr_ypos_hi[k], &spr_ypos_lo[k], y); 29 | } 30 | 31 | uint16 GetSprXPos(uint8 k) { 32 | return PAIR16(spr_xpos_hi[k], spr_xpos_lo[k]); 33 | } 34 | 35 | uint16 GetSprYPos(uint8 k) { 36 | return PAIR16(spr_ypos_hi[k], spr_ypos_lo[k]); 37 | } 38 | 39 | void SetSprXPos(uint8 k, uint16 x) { 40 | spr_xpos_hi[k] = x >> 8; 41 | spr_xpos_lo[k] = x; 42 | } 43 | 44 | void SetSprYPos(uint8 k, uint16 y) { 45 | spr_ypos_hi[k] = y >> 8; 46 | spr_ypos_lo[k] = y; 47 | } 48 | 49 | void SetSprXYPos(uint8 k, uint16 x, uint16 y) { 50 | SetHiLo(&spr_xpos_hi[k], &spr_xpos_lo[k], x); 51 | SetHiLo(&spr_ypos_hi[k], &spr_ypos_lo[k], y); 52 | } 53 | 54 | void SmwSavePlaythroughSnapshot() { 55 | char buf[128]; 56 | snprintf(buf, sizeof(buf), "playthrough/%d_%d_%d.sav", ow_level_number_lo, misc_exit_level_action, (int)time(NULL)); 57 | RtlSaveSnapshot(buf, false); 58 | } 59 | 60 | void UploadOAMBuffer() { // 008449 61 | memcpy(g_ppu->oam, g_ram + 0x200, 0x220); 62 | RtlPpuWrite(OAMADDH, 0x80); 63 | RtlPpuWrite(OAMADDL, mirror_oamaddress_lo); 64 | } 65 | 66 | 67 | void SmwDrawPpuFrame(void) { 68 | SimpleHdma hdma_chans[3]; 69 | 70 | Dma *dma = g_dma; 71 | 72 | dma_startDma(dma, mirror_hdmaenable, true); 73 | 74 | SimpleHdma_Init(&hdma_chans[0], &dma->channel[5]); 75 | SimpleHdma_Init(&hdma_chans[1], &dma->channel[6]); 76 | SimpleHdma_Init(&hdma_chans[2], &dma->channel[7]); 77 | 78 | int trigger = g_snes->vIrqEnabled ? g_snes->vTimer + 1 : -1; 79 | 80 | for (int i = 0; i <= 224; i++) { 81 | ppu_runLine(g_ppu, i); 82 | SimpleHdma_DoLine(&hdma_chans[0]); 83 | SimpleHdma_DoLine(&hdma_chans[1]); 84 | SimpleHdma_DoLine(&hdma_chans[2]); 85 | // dma_doHdma(snes->dma); 86 | if (i == trigger) { 87 | SmwVectorIRQ(); 88 | trigger = g_snes->vIrqEnabled ? g_snes->vTimer + 1 : -1; 89 | } 90 | } 91 | } 92 | 93 | void SmwRunOneFrameOfGame(void) { 94 | if (*(uint16 *)reset_sprites_y_function_in_ram == 0) 95 | SmwVectorReset(); 96 | SmwRunOneFrameOfGame_Internal(); 97 | SmwVectorNMI(); 98 | } 99 | 100 | 101 | void LoadStripeImage_UploadToVRAM(const uint8 *pp) { // 00871e 102 | while (1) { 103 | if ((*pp & 0x80) != 0) 104 | break; 105 | uint16 vram_addr = pp[0] << 8 | pp[1]; 106 | if (g_lunar_magic) 107 | vram_addr = LmHook_LoadStripeImage(vram_addr); 108 | 109 | uint8 vmain = __CFSHL__(pp[2], 1); 110 | uint8 fixed_addr = (uint8)(pp[2] & 0x40) >> 3; 111 | uint16 num = (swap16(WORD(pp[2])) & 0x3FFF) + 1; 112 | pp += 4; 113 | 114 | if (fixed_addr) { 115 | if (vram_addr != 0xffff) { 116 | uint16 *dst = g_ppu->vram + vram_addr; 117 | uint16 src_data = WORD(*pp); 118 | int ctr = (num + 1) >> 1; 119 | if (vmain) { 120 | for (int i = 0; i < ctr; i++) 121 | dst[i * 32] = src_data; 122 | } else { 123 | // uhm...? 124 | uint8 *dst_b = (uint8 *)dst; 125 | for (int i = 0; i < num; i++) 126 | dst_b[i + ((i & 1) << 1)] = src_data; 127 | for (int i = 0; i < num; i += 2) 128 | dst_b[i + 1] = src_data >> 8; 129 | } 130 | } 131 | pp += 2; 132 | } else { 133 | if (vram_addr != 0xffff) { 134 | uint16 *dst = g_ppu->vram + vram_addr; 135 | uint16 *src = (uint16 *)pp; 136 | if (vmain) { 137 | for (int i = 0; i < (num >> 1); i++) 138 | dst[i * 32] = src[i]; 139 | } else { 140 | for (int i = 0; i < (num >> 1); i++) 141 | dst[i] = src[i]; 142 | } 143 | } 144 | pp += num; 145 | } 146 | } 147 | } 148 | 149 | -------------------------------------------------------------------------------- /src/smw_rtl.h: -------------------------------------------------------------------------------- 1 | #ifndef SMW_SMW_RTL_H_ 2 | #define SMW_SMW_RTL_H_ 3 | #include "common_rtl.h" 4 | #include "snes/snes_regs.h" 5 | 6 | extern bool g_lunar_magic; 7 | extern int g_dbg_ctr_mine; 8 | 9 | PointU16 *get_PointU16(PointU16 *pt, uint8 off); 10 | static inline OamEnt *get_OamEnt(OamEnt *base, uint16 off) { return (OamEnt *)((uint8 *)base + off); } 11 | 12 | void SmwVectorReset(); 13 | void SmwRunOneFrameOfGame_Internal(); 14 | void SmwVectorNMI(); 15 | void SmwVectorIRQ(); 16 | void SmwSavePlaythroughSnapshot(); 17 | 18 | void SmwCopyToVram(uint16 vram_addr, const uint8 *src, int n); 19 | void SmwClearVram(uint16 vram_addr, uint16 value, int n); 20 | void SmwCopyToVramPitch32(uint16 vram_addr, const uint8 *src, int n); 21 | void SmwCopyToVramLow(uint16 vram_addr, const uint8 *src, int n); 22 | void SmwCopyFromVram(uint16 vram_addr, uint8 *dst, int n); 23 | 24 | void SmwDrawPpuFrame(void); 25 | void SmwRunOneFrameOfGame(void); 26 | 27 | extern bool g_did_finish_level_hook; 28 | 29 | void AddSprXPos(uint8 k, uint16 x); 30 | void AddSprYPos(uint8 k, uint16 y); 31 | void AddSprXYPos(uint8 k, uint16 x, uint16 y); 32 | uint16 GetSprXPos(uint8 k); 33 | uint16 GetSprYPos(uint8 k); 34 | void SetSprXPos(uint8 k, uint16 x); 35 | void SetSprYPos(uint8 k, uint16 y); 36 | void SetSprXYPos(uint8 k, uint16 x, uint16 y); 37 | 38 | #pragma pack (push, 1) 39 | typedef struct OwExits { 40 | uint16 field_0; 41 | uint16 field_2; 42 | uint8 field_4; 43 | } OwExits; 44 | 45 | typedef struct SpriteSlotData { 46 | uint8 field_0; 47 | uint16 field_1; 48 | uint16 field_3; 49 | } SpriteSlotData; 50 | 51 | typedef struct LevelTileAnimations { 52 | uint16 field_0; 53 | uint16 field_2; 54 | uint16 field_4; 55 | } LevelTileAnimations; 56 | 57 | #pragma pack (pop) 58 | 59 | typedef struct GenTileArgs { 60 | uint8 r6, r7; 61 | uint16 r8; 62 | uint16 r12, r14; 63 | uint16 r10; 64 | uint8 *ptr_lo_map16_data; 65 | } GenTileArgs; 66 | 67 | const uint8 *GetSpriteListPtr(); 68 | uint8 GetCurrentSlope(int i); 69 | 70 | #endif // SMW_SMW_RTL_H_ -------------------------------------------------------------------------------- /src/smw_spc_player.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | 4 | typedef struct Dsp Dsp; 5 | typedef struct SpcPlayer SpcPlayer; 6 | 7 | typedef void SpcPlayer_Initialize_Func(SpcPlayer *p); 8 | typedef void SpcPlayer_GenerateSamples_Func(SpcPlayer *p); 9 | typedef void SpcPlayer_Upload_Func(SpcPlayer *p, const uint8_t *data); 10 | typedef void SpcPlayer_CopyVariables_Func(SpcPlayer *p, bool copy_to_ram); 11 | 12 | 13 | typedef struct SpcPlayer { 14 | Dsp *dsp; 15 | uint8 *ram; 16 | uint8 input_ports[4]; 17 | uint8 port_to_snes[4]; 18 | 19 | SpcPlayer_Initialize_Func *initialize; 20 | SpcPlayer_GenerateSamples_Func *gen_samples; 21 | SpcPlayer_Upload_Func *upload; 22 | SpcPlayer_CopyVariables_Func *copy_vars; 23 | } SpcPlayer; 24 | 25 | 26 | SpcPlayer *SmwSpcPlayer_Create(void); 27 | SpcPlayer *SmasSpcPlayer_Create(void); 28 | 29 | -------------------------------------------------------------------------------- /src/snes/apu.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #include "apu.h" 11 | #include "snes.h" 12 | #include "spc.h" 13 | #include "dsp.h" 14 | #include "../tracing.h" 15 | 16 | static const uint8_t bootRom[0x40] = { 17 | 0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa, 0xf4, 0x8f, 0xbb, 0xf5, 0x78, 18 | 0xcc, 0xf4, 0xd0, 0xfb, 0x2f, 0x19, 0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5, 19 | 0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xd0, 0xf3, 0xab, 0x01, 0x10, 0xef, 0x7e, 0xf4, 0x10, 0xeb, 0xba, 20 | 0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, 0xf4, 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0x00, 0xc0, 0xff 21 | }; 22 | 23 | Apu* apu_init(void) { 24 | Apu* apu = malloc(sizeof(Apu)); 25 | apu->spc = spc_init(apu); 26 | apu->dsp = dsp_init(apu->ram); 27 | return apu; 28 | } 29 | 30 | void apu_free(Apu* apu) { 31 | spc_free(apu->spc); 32 | dsp_free(apu->dsp); 33 | free(apu); 34 | } 35 | 36 | void apu_reset(Apu* apu) { 37 | apu->romReadable = true; // before resetting spc, because it reads reset vector from it 38 | spc_reset(apu->spc); 39 | dsp_reset(apu->dsp); 40 | memset(apu->ram, 0, sizeof(apu->ram)); 41 | apu->dspAdr = 0; 42 | apu->cycles = 0; 43 | memset(apu->inPorts, 0, sizeof(apu->inPorts)); 44 | memset(apu->outPorts, 0, sizeof(apu->outPorts)); 45 | for(int i = 0; i < 3; i++) { 46 | apu->timer[i].cycles = 0; 47 | apu->timer[i].divider = 0; 48 | apu->timer[i].target = 0; 49 | apu->timer[i].counter = 0; 50 | apu->timer[i].enabled = false; 51 | } 52 | apu->cpuCyclesLeft = 7; 53 | apu->hist.count = 0; 54 | } 55 | 56 | void apu_saveload(Apu *apu, SaveLoadInfo *sli) { 57 | sli->func(sli, apu->ram, offsetof(Apu, pad) + 6 - offsetof(Apu, ram)); 58 | dsp_saveload(apu->dsp, sli); 59 | spc_saveload(apu->spc, sli); 60 | } 61 | 62 | bool g_debug_apu_cycles=0; 63 | 64 | void apu_cycle(Apu* apu) { 65 | if(apu->cpuCyclesLeft == 0) { 66 | if (g_debug_apu_cycles) { 67 | char line[80]; 68 | getProcessorStateSpc(apu, line); 69 | puts(line); 70 | } 71 | apu->cpuCyclesLeft = spc_runOpcode(apu->spc); 72 | if (apu->spc->x == 0) { 73 | apu->spc->x = 0; 74 | } 75 | } 76 | apu->cpuCyclesLeft--; 77 | 78 | if((apu->cycles & 0x1f) == 0) { 79 | // every 32 cycles 80 | dsp_cycle(apu->dsp); 81 | } 82 | 83 | // handle timers 84 | for(int i = 0; i < 3; i++) { 85 | if(apu->timer[i].cycles == 0) { 86 | apu->timer[i].cycles = i == 2 ? 16 : 128; 87 | if(apu->timer[i].enabled) { 88 | apu->timer[i].divider++; 89 | if(apu->timer[i].divider == apu->timer[i].target) { 90 | apu->timer[i].divider = 0; 91 | apu->timer[i].counter++; 92 | apu->timer[i].counter &= 0xf; 93 | } 94 | } 95 | } 96 | apu->timer[i].cycles--; 97 | } 98 | 99 | apu->cycles++; 100 | } 101 | 102 | uint8_t apu_cpuRead(Apu* apu, uint16_t adr) { 103 | switch(adr) { 104 | case 0xf0: 105 | case 0xf1: 106 | case 0xfa: 107 | case 0xfb: 108 | case 0xfc: { 109 | return 0; 110 | } 111 | case 0xf2: { 112 | return apu->dspAdr; 113 | } 114 | case 0xf3: { 115 | return dsp_read(apu->dsp, apu->dspAdr & 0x7f); 116 | } 117 | case 0xf4: 118 | case 0xf5: 119 | case 0xf6: 120 | case 0xf7: 121 | case 0xf8: 122 | case 0xf9: { 123 | return apu->inPorts[adr - 0xf4]; 124 | } 125 | case 0xfd: 126 | case 0xfe: 127 | case 0xff: { 128 | uint8_t ret = apu->timer[adr - 0xfd].counter; 129 | apu->timer[adr - 0xfd].counter = 0; 130 | return ret; 131 | } 132 | } 133 | if(apu->romReadable && adr >= 0xffc0) { 134 | return bootRom[adr - 0xffc0]; 135 | } 136 | return apu->ram[adr]; 137 | } 138 | 139 | void apu_cpuWrite(Apu* apu, uint16_t adr, uint8_t val) { 140 | switch(adr) { 141 | case 0xf0: { 142 | break; // test register 143 | } 144 | case 0xf1: { 145 | for(int i = 0; i < 3; i++) { 146 | if(!apu->timer[i].enabled && (val & (1 << i))) { 147 | apu->timer[i].divider = 0; 148 | apu->timer[i].counter = 0; 149 | } 150 | apu->timer[i].enabled = val & (1 << i); 151 | } 152 | if(val & 0x10) { 153 | apu->inPorts[0] = 0; 154 | apu->inPorts[1] = 0; 155 | } 156 | if(val & 0x20) { 157 | apu->inPorts[2] = 0; 158 | apu->inPorts[3] = 0; 159 | } 160 | apu->romReadable = val & 0x80; 161 | break; 162 | } 163 | case 0xf2: { 164 | apu->dspAdr = val; 165 | break; 166 | } 167 | case 0xf3: { 168 | int i = apu->hist.count; 169 | if (i != 256) { 170 | apu->hist.count = i + 1; 171 | apu->hist.addr[i] = (uint8_t)apu->dspAdr; 172 | apu->hist.val[i] = val; 173 | } 174 | if(apu->dspAdr < 0x80) dsp_write(apu->dsp, apu->dspAdr, val); 175 | break; 176 | } 177 | case 0xf4: 178 | case 0xf5: 179 | case 0xf6: 180 | case 0xf7: { 181 | apu->outPorts[adr - 0xf4] = val; 182 | break; 183 | } 184 | case 0xf8: 185 | case 0xf9: { 186 | apu->inPorts[adr - 0xf4] = val; 187 | break; 188 | } 189 | case 0xfa: 190 | case 0xfb: 191 | case 0xfc: { 192 | apu->timer[adr - 0xfa].target = val; 193 | break; 194 | } 195 | } 196 | apu->ram[adr] = val; 197 | } 198 | -------------------------------------------------------------------------------- /src/snes/apu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef APU_H 3 | #define APU_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct Apu Apu; 12 | 13 | #include "snes.h" 14 | #include "spc.h" 15 | #include "dsp.h" 16 | 17 | typedef struct Timer { 18 | uint8_t cycles; 19 | uint8_t divider; 20 | uint8_t target; 21 | uint8_t counter; 22 | bool enabled; 23 | } Timer; 24 | 25 | struct Apu { 26 | Spc* spc; 27 | Dsp* dsp; 28 | uint8_t ram[0x10000]; 29 | bool romReadable; 30 | uint8_t dspAdr; 31 | uint32_t cycles; 32 | uint8_t inPorts[6]; // includes 2 bytes of ram 33 | uint8_t outPorts[4]; 34 | Timer timer[3]; 35 | uint8_t cpuCyclesLeft; 36 | uint8_t pad[6]; 37 | 38 | 39 | union { 40 | struct DspRegWriteHistory hist; 41 | void *padpad; 42 | }; 43 | }; 44 | 45 | Apu* apu_init(); 46 | void apu_free(Apu* apu); 47 | void apu_reset(Apu* apu); 48 | void apu_cycle(Apu* apu); 49 | uint8_t apu_cpuRead(Apu* apu, uint16_t adr); 50 | void apu_cpuWrite(Apu* apu, uint16_t adr, uint8_t val); 51 | void apu_saveload(Apu *apu, SaveLoadInfo *sli); 52 | #endif 53 | -------------------------------------------------------------------------------- /src/snes/cart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../types.h" 7 | #include "cart.h" 8 | #include "snes.h" 9 | 10 | static uint8_t cart_readLorom(Cart* cart, uint8_t bank, uint16_t adr); 11 | static void cart_writeLorom(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val); 12 | static uint8_t cart_readHirom(Cart* cart, uint8_t bank, uint16_t adr); 13 | static void cart_writeHirom(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val); 14 | 15 | Cart* cart_init(Snes* snes) { 16 | Cart* cart = malloc(sizeof(Cart)); 17 | cart->snes = snes; 18 | cart->type = 0; 19 | cart->rom = NULL; 20 | cart->romSize = 0; 21 | cart->ram = NULL; 22 | cart->ramSize = 0; 23 | return cart; 24 | } 25 | 26 | void cart_free(Cart* cart) { 27 | free(cart); 28 | } 29 | 30 | void cart_reset(Cart* cart) { 31 | //if(cart->ramSize > 0 && cart->ram != NULL) memset(cart->ram, 0, cart->ramSize); // for now 32 | } 33 | 34 | void cart_saveload(Cart *cart, SaveLoadInfo *sli) { 35 | sli->func(sli, cart->ram, cart->ramSize); 36 | } 37 | 38 | void cart_load(Cart* cart, int type, uint8_t* rom, int romSize, int ramSize) { 39 | cart->type = type; 40 | if(cart->rom != NULL) free(cart->rom); 41 | if(cart->ram != NULL) free(cart->ram); 42 | cart->rom = malloc(romSize); 43 | cart->romSize = romSize; 44 | if(ramSize > 0) { 45 | cart->ram = malloc(ramSize); 46 | memset(cart->ram, 0, ramSize); 47 | } else { 48 | cart->ram = NULL; 49 | } 50 | cart->ramSize = ramSize; 51 | memcpy(cart->rom, rom, romSize); 52 | } 53 | 54 | uint8_t cart_read(Cart* cart, uint8_t bank, uint16_t adr) { 55 | switch(cart->type) { 56 | case 0: 57 | assert(0); 58 | return cart->snes->openBus; 59 | case 1: return cart_readLorom(cart, bank, adr); 60 | case 2: return cart_readHirom(cart, bank, adr); 61 | } 62 | assert(0); 63 | return cart->snes->openBus; 64 | } 65 | 66 | void cart_write(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) { 67 | switch(cart->type) { 68 | case 0: break; 69 | case 1: cart_writeLorom(cart, bank, adr, val); break; 70 | case 2: cart_writeHirom(cart, bank, adr, val); break; 71 | } 72 | } 73 | 74 | void DumpCpuHistory(); 75 | 76 | static uint8_t cart_readLorom(Cart* cart, uint8_t bank, uint16_t adr) { 77 | if(((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && adr < 0x8000 && cart->ramSize > 0) { 78 | // banks 70-7e and f0-ff, adr 0000-7fff 79 | return cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)]; 80 | } 81 | bank &= 0x7f; 82 | if(adr >= 0x8000 || bank >= 0x40) { 83 | // adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff 84 | return cart->rom[((bank << 15) | (adr & 0x7fff)) & (cart->romSize - 1)]; 85 | } 86 | printf("While trying to read from 0x%x\n", bank << 16 | adr); 87 | DumpCpuHistory(); 88 | Die("The game crashed in cart_readLorom"); 89 | return 0; 90 | } 91 | 92 | static void cart_writeLorom(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) { 93 | if(((bank >= 0x70 && bank < 0x7e) || bank > 0xf0) && adr < 0x8000 && cart->ramSize > 0) { 94 | // banks 70-7e and f0-ff, adr 0000-7fff 95 | cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)] = val; 96 | } 97 | } 98 | 99 | static uint8_t cart_readHirom(Cart* cart, uint8_t bank, uint16_t adr) { 100 | bank &= 0x7f; 101 | if(bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && cart->ramSize > 0) { 102 | // banks 00-3f and 80-bf, adr 6000-7fff 103 | return cart->ram[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (cart->ramSize - 1)]; 104 | } 105 | if(adr >= 0x8000 || bank >= 0x40) { 106 | // adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff 107 | return cart->rom[(((bank & 0x3f) << 16) | adr) & (cart->romSize - 1)]; 108 | } 109 | assert(0); 110 | return cart->snes->openBus; 111 | } 112 | 113 | static void cart_writeHirom(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) { 114 | bank &= 0x7f; 115 | if(bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && cart->ramSize > 0) { 116 | // banks 00-3f and 80-bf, adr 6000-7fff 117 | cart->ram[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (cart->ramSize - 1)] = val; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/snes/cart.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CART_H 3 | #define CART_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct Cart Cart; 12 | 13 | #include "snes.h" 14 | 15 | struct Cart { 16 | Snes* snes; 17 | uint8_t type; 18 | 19 | uint8_t* rom; 20 | uint32_t romSize; 21 | uint8_t* ram; 22 | uint32_t ramSize; 23 | }; 24 | 25 | // TODO: how to handle reset & load? (especially where to init ram) 26 | 27 | Cart* cart_init(Snes* snes); 28 | void cart_free(Cart* cart); 29 | void cart_reset(Cart* cart); // will reset special chips etc, general reading is set up in load 30 | void cart_load(Cart* cart, int type, uint8_t* rom, int romSize, int ramSize); // TODO: figure out how to handle (battery, cart-chips etc) 31 | uint8_t cart_read(Cart* cart, uint8_t bank, uint16_t adr); 32 | void cart_write(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val); 33 | void cart_saveload(Cart *cart, SaveLoadInfo *sli); 34 | #endif 35 | -------------------------------------------------------------------------------- /src/snes/cpu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPU_H 3 | #define CPU_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "saveload.h" 11 | 12 | typedef struct Cpu Cpu; 13 | 14 | struct Cpu { 15 | // reference to memory handler, for reading//writing 16 | void* mem; 17 | uint8_t memType; // used to define which type mem is 18 | // registers 19 | uint16_t a; 20 | uint16_t x; 21 | uint16_t y; 22 | uint16_t sp; 23 | uint16_t pc; 24 | uint16_t dp; // direct page (D) 25 | uint8_t k; // program bank (PB) 26 | uint8_t db; // data bank (B) 27 | // flags 28 | bool c; 29 | bool z; 30 | bool v; 31 | bool n; 32 | bool i; 33 | bool d; 34 | bool xf; 35 | bool mf; 36 | bool e; 37 | // interrupts 38 | bool irqWanted; 39 | bool nmiWanted; 40 | // power state (WAI/STP) 41 | bool waiting; 42 | bool stopped; 43 | // internal use 44 | uint8_t cyclesUsed; // indicates how many cycles an opcode used 45 | uint16_t spBreakpoint; 46 | bool in_emu; 47 | }; 48 | 49 | extern struct Cpu *g_cpu; 50 | bool HookedFunctionRts(int is_long); 51 | 52 | Cpu* cpu_init(void* mem, int memType); 53 | void cpu_free(Cpu* cpu); 54 | void cpu_reset(Cpu* cpu); 55 | int cpu_runOpcode(Cpu* cpu); 56 | uint8_t cpu_getFlags(Cpu *cpu); 57 | void cpu_setFlags(Cpu *cpu, uint8_t val); 58 | void cpu_saveload(Cpu *cpu, SaveLoadInfo *sli); 59 | #endif 60 | -------------------------------------------------------------------------------- /src/snes/dma.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "dma.h" 11 | #include "snes.h" 12 | 13 | static const int bAdrOffsets[8][4] = { 14 | {0, 0, 0, 0}, 15 | {0, 1, 0, 1}, 16 | {0, 0, 0, 0}, 17 | {0, 0, 1, 1}, 18 | {0, 1, 2, 3}, 19 | {0, 1, 0, 1}, 20 | {0, 0, 0, 0}, 21 | {0, 0, 1, 1} 22 | }; 23 | 24 | static const int transferLength[8] = { 25 | 1, 2, 2, 4, 4, 4, 2, 4 26 | }; 27 | 28 | static void dma_transferByte(Dma* dma, uint16_t aAdr, uint8_t aBank, uint8_t bAdr, bool fromB); 29 | 30 | Dma* dma_init(Snes* snes) { 31 | Dma* dma = malloc(sizeof(Dma)); 32 | dma->snes = snes; 33 | return dma; 34 | } 35 | 36 | void dma_free(Dma* dma) { 37 | free(dma); 38 | } 39 | 40 | void dma_reset(Dma* dma) { 41 | for(int i = 0; i < 8; i++) { 42 | dma->channel[i].bAdr = 0xff; 43 | dma->channel[i].aAdr = 0xffff; 44 | dma->channel[i].aBank = 0xff; 45 | dma->channel[i].size = 0xffff; 46 | dma->channel[i].indBank = 0xff; 47 | dma->channel[i].tableAdr = 0xffff; 48 | dma->channel[i].repCount = 0xff; 49 | dma->channel[i].unusedByte = 0xff; 50 | dma->channel[i].dmaActive = false; 51 | dma->channel[i].hdmaActive = false; 52 | dma->channel[i].mode = 7; 53 | dma->channel[i].fixed = true; 54 | dma->channel[i].decrement = true; 55 | dma->channel[i].indirect = true; 56 | dma->channel[i].fromB = true; 57 | dma->channel[i].unusedBit = true; 58 | dma->channel[i].doTransfer = false; 59 | dma->channel[i].terminated = false; 60 | dma->channel[i].offIndex = 0; 61 | } 62 | dma->hdmaTimer = 0; 63 | dma->dmaTimer = 0; 64 | dma->dmaBusy = false; 65 | } 66 | 67 | void dma_saveload(Dma *dma, SaveLoadInfo *sli) { 68 | sli->func(sli, &dma->channel, offsetof(Dma, pad) + 7 - offsetof(Dma, channel)); 69 | } 70 | 71 | uint8_t dma_read(Dma* dma, uint16_t adr) { 72 | uint8_t c = (adr & 0x70) >> 4; 73 | switch(adr & 0xf) { 74 | case 0x0: { 75 | uint8_t val = dma->channel[c].mode; 76 | val |= dma->channel[c].fixed << 3; 77 | val |= dma->channel[c].decrement << 4; 78 | val |= dma->channel[c].unusedBit << 5; 79 | val |= dma->channel[c].indirect << 6; 80 | val |= dma->channel[c].fromB << 7; 81 | return val; 82 | } 83 | case 0x1: { 84 | return dma->channel[c].bAdr; 85 | } 86 | case 0x2: { 87 | return dma->channel[c].aAdr & 0xff; 88 | } 89 | case 0x3: { 90 | return dma->channel[c].aAdr >> 8; 91 | } 92 | case 0x4: { 93 | return dma->channel[c].aBank; 94 | } 95 | case 0x5: { 96 | return dma->channel[c].size & 0xff; 97 | } 98 | case 0x6: { 99 | return dma->channel[c].size >> 8; 100 | } 101 | case 0x7: { 102 | return dma->channel[c].indBank; 103 | } 104 | case 0x8: { 105 | return dma->channel[c].tableAdr & 0xff; 106 | } 107 | case 0x9: { 108 | return dma->channel[c].tableAdr >> 8; 109 | } 110 | case 0xa: { 111 | return dma->channel[c].repCount; 112 | } 113 | case 0xb: 114 | case 0xf: { 115 | return dma->channel[c].unusedByte; 116 | } 117 | default: { 118 | assert(0); 119 | return dma->snes->openBus; 120 | } 121 | } 122 | } 123 | 124 | void dma_write(Dma* dma, uint16_t adr, uint8_t val) { 125 | uint8_t c = (adr & 0x70) >> 4; 126 | switch(adr & 0xf) { 127 | case 0x0: { 128 | dma->channel[c].mode = val & 0x7; 129 | dma->channel[c].fixed = val & 0x8; 130 | dma->channel[c].decrement = val & 0x10; 131 | dma->channel[c].unusedBit = val & 0x20; 132 | dma->channel[c].indirect = val & 0x40; 133 | dma->channel[c].fromB = val & 0x80; 134 | break; 135 | } 136 | case 0x1: { 137 | dma->channel[c].bAdr = val; 138 | break; 139 | } 140 | case 0x2: { 141 | dma->channel[c].aAdr = (dma->channel[c].aAdr & 0xff00) | val; 142 | break; 143 | } 144 | case 0x3: { 145 | dma->channel[c].aAdr = (dma->channel[c].aAdr & 0xff) | (val << 8); 146 | break; 147 | } 148 | case 0x4: { 149 | dma->channel[c].aBank = val; 150 | break; 151 | } 152 | case 0x5: { 153 | dma->channel[c].size = (dma->channel[c].size & 0xff00) | val; 154 | break; 155 | } 156 | case 0x6: { 157 | dma->channel[c].size = (dma->channel[c].size & 0xff) | (val << 8); 158 | break; 159 | } 160 | case 0x7: { 161 | dma->channel[c].indBank = val; 162 | break; 163 | } 164 | case 0x8: { 165 | dma->channel[c].tableAdr = (dma->channel[c].tableAdr & 0xff00) | val; 166 | break; 167 | } 168 | case 0x9: { 169 | dma->channel[c].tableAdr = (dma->channel[c].tableAdr & 0xff) | (val << 8); 170 | break; 171 | } 172 | case 0xa: { 173 | dma->channel[c].repCount = val; 174 | break; 175 | } 176 | case 0xb: 177 | case 0xf: { 178 | dma->channel[c].unusedByte = val; 179 | break; 180 | } 181 | default: { 182 | break; 183 | } 184 | } 185 | } 186 | 187 | extern bool g_fail; 188 | 189 | void dma_doDma(Dma* dma) { 190 | if(dma->dmaTimer > 0) { 191 | dma->dmaTimer -= 2; 192 | return; 193 | } 194 | // figure out first channel that is active 195 | int i = 0; 196 | for(i = 0; i < 8; i++) { 197 | if(dma->channel[i].dmaActive) { 198 | break; 199 | } 200 | } 201 | if(i == 8) { 202 | // no active channels 203 | dma->dmaBusy = false; 204 | return; 205 | } 206 | 207 | if (!dma->channel[i].fromB && (dma->channel[i].aBank & 0x80) && !(dma->channel[i].aAdr & 0x8000) && !g_fail) { 208 | printf("Warning! DMA from addr 0x%x\n", dma->channel[i].aBank << 16 | dma->channel[i].aAdr); 209 | g_fail = true; 210 | } 211 | 212 | // do channel i 213 | dma_transferByte( 214 | dma, dma->channel[i].aAdr, dma->channel[i].aBank, 215 | dma->channel[i].bAdr + bAdrOffsets[dma->channel[i].mode][dma->channel[i].offIndex++], dma->channel[i].fromB 216 | ); 217 | dma->channel[i].offIndex &= 3; 218 | dma->dmaTimer += 6; // 8 cycles for each byte taken, -2 for this cycle 219 | if(!dma->channel[i].fixed) { 220 | dma->channel[i].aAdr += dma->channel[i].decrement ? -1 : 1; 221 | } 222 | dma->channel[i].size--; 223 | if(dma->channel[i].size == 0) { 224 | dma->channel[i].offIndex = 0; // reset offset index 225 | dma->channel[i].dmaActive = false; 226 | dma->dmaTimer += 8; // 8 cycle overhead per channel 227 | } 228 | } 229 | 230 | void dma_initHdma(Dma* dma) { 231 | dma->hdmaTimer = 0; 232 | bool hdmaHappened = false; 233 | for(int i = 0; i < 8; i++) { 234 | if(dma->channel[i].hdmaActive) { 235 | hdmaHappened = true; 236 | // terminate any dma 237 | dma->channel[i].dmaActive = false; 238 | dma->channel[i].offIndex = 0; 239 | // load address, repCount, and indirect address if needed 240 | dma->channel[i].tableAdr = dma->channel[i].aAdr; 241 | dma->channel[i].repCount = snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++); 242 | if (dma->channel[i].repCount == 0) { 243 | dma->channel[i].terminated = true; 244 | continue; 245 | } 246 | dma->hdmaTimer += 8; // 8 cycle overhead for each active channel 247 | if(dma->channel[i].indirect) { 248 | dma->channel[i].size = snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++); 249 | dma->channel[i].size |= snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++) << 8; 250 | dma->hdmaTimer += 16; // another 16 cycles for indirect (total 24) 251 | } 252 | dma->channel[i].doTransfer = true; 253 | } else { 254 | dma->channel[i].doTransfer = false; 255 | } 256 | dma->channel[i].terminated = false; 257 | } 258 | if(hdmaHappened) dma->hdmaTimer += 16; // 18 cycles overhead, -2 for this cycle 259 | } 260 | 261 | void dma_doHdma(Dma* dma) { 262 | dma->hdmaTimer = 0; 263 | bool hdmaHappened = false; 264 | for(int i = 0; i < 8; i++) { 265 | if(dma->channel[i].hdmaActive && !dma->channel[i].terminated) { 266 | // printf("DMA %d: 0x%x\n", i, dma->channel[i].bAdr); 267 | hdmaHappened = true; 268 | // terminate any dma 269 | dma->channel[i].dmaActive = false; 270 | dma->channel[i].offIndex = 0; 271 | // do the hdma 272 | dma->hdmaTimer += 8; // 8 cycles overhead for each active channel 273 | 274 | 275 | if(dma->channel[i].doTransfer) { 276 | for(int j = 0; j < transferLength[dma->channel[i].mode]; j++) { 277 | dma->hdmaTimer += 8; // 8 cycles for each byte transferred 278 | if(dma->channel[i].indirect) { 279 | dma_transferByte( 280 | dma, dma->channel[i].size++, dma->channel[i].indBank, 281 | dma->channel[i].bAdr + bAdrOffsets[dma->channel[i].mode][j], dma->channel[i].fromB 282 | ); 283 | } else { 284 | dma_transferByte( 285 | dma, dma->channel[i].tableAdr++, dma->channel[i].aBank, 286 | dma->channel[i].bAdr + bAdrOffsets[dma->channel[i].mode][j], dma->channel[i].fromB 287 | ); 288 | } 289 | } 290 | } 291 | dma->channel[i].repCount--; 292 | dma->channel[i].doTransfer = dma->channel[i].repCount & 0x80; 293 | if((dma->channel[i].repCount & 0x7f) == 0) { 294 | dma->channel[i].repCount = snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++); 295 | if(dma->channel[i].indirect) { 296 | // TODO: oddness with not fetching high byte if last active channel and reCount is 0 297 | dma->channel[i].size = snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++); 298 | dma->channel[i].size |= snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++) << 8; 299 | dma->hdmaTimer += 16; // 16 cycles for new indirect address 300 | } 301 | if(dma->channel[i].repCount == 0) dma->channel[i].terminated = true; 302 | dma->channel[i].doTransfer = true; 303 | } 304 | } 305 | } 306 | if(hdmaHappened) dma->hdmaTimer += 16; // 18 cycles overhead, -2 for this cycle 307 | } 308 | 309 | static void dma_transferByte(Dma* dma, uint16_t aAdr, uint8_t aBank, uint8_t bAdr, bool fromB) { 310 | // TODO: invalid writes: 311 | // accesing b-bus via a-bus gives open bus, 312 | // $2180-$2183 while accessing ram via a-bus open busses $2180-$2183 313 | // cannot access $4300-$437f (dma regs), or $420b / $420c 314 | if(fromB) { 315 | snes_write(dma->snes, (aBank << 16) | aAdr, snes_readBBus(dma->snes, bAdr)); 316 | } else { 317 | snes_writeBBus(dma->snes, bAdr, snes_read(dma->snes, (aBank << 16) | aAdr)); 318 | } 319 | } 320 | 321 | bool dma_cycle(Dma* dma) { 322 | if(dma->hdmaTimer > 0) { 323 | dma->hdmaTimer -= 2; 324 | return true; 325 | } else if(dma->dmaBusy) { 326 | dma_doDma(dma); 327 | return true; 328 | } 329 | return false; 330 | } 331 | 332 | void dma_startDma(Dma* dma, uint8_t val, bool hdma) { 333 | for(int i = 0; i < 8; i++) { 334 | if(hdma) { 335 | dma->channel[i].hdmaActive = val & (1 << i); 336 | } else { 337 | dma->channel[i].dmaActive = val & (1 << i); 338 | } 339 | } 340 | if(!hdma) { 341 | dma->dmaBusy = val; 342 | dma->dmaTimer += dma->dmaBusy ? 16 : 0; // 12-24 cycle overhead for entire dma transfer 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /src/snes/dma.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DMA_H 3 | #define DMA_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct Dma Dma; 12 | 13 | #include "snes.h" 14 | 15 | typedef struct DmaChannel { 16 | uint8_t bAdr; 17 | uint16_t aAdr; 18 | uint8_t aBank; 19 | uint16_t size; // also indirect hdma adr 20 | uint8_t indBank; // hdma 21 | uint16_t tableAdr; // hdma 22 | uint8_t repCount; // hdma 23 | uint8_t unusedByte; 24 | bool dmaActive; 25 | bool hdmaActive; 26 | uint8_t mode; 27 | bool fixed; 28 | bool decrement; 29 | bool indirect; // hdma 30 | bool fromB; 31 | bool unusedBit; 32 | bool doTransfer; // hdma 33 | bool terminated; // hdma 34 | uint8_t offIndex; 35 | } DmaChannel; 36 | 37 | struct Dma { 38 | Snes* snes; 39 | DmaChannel channel[8]; 40 | uint16_t hdmaTimer; 41 | uint32_t dmaTimer; 42 | bool dmaBusy; 43 | uint8_t pad[7]; 44 | }; 45 | 46 | Dma* dma_init(Snes* snes); 47 | void dma_free(Dma* dma); 48 | void dma_reset(Dma* dma); 49 | uint8_t dma_read(Dma* dma, uint16_t adr); // 43x0-43xf 50 | void dma_write(Dma* dma, uint16_t adr, uint8_t val); // 43x0-43xf 51 | void dma_doDma(Dma* dma); 52 | void dma_initHdma(Dma* dma); 53 | void dma_doHdma(Dma* dma); 54 | bool dma_cycle(Dma* dma); 55 | void dma_startDma(Dma* dma, uint8_t val, bool hdma); 56 | void dma_saveload(Dma *dma, SaveLoadInfo *sli); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/snes/dsp.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DSP_H 3 | #define DSP_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "saveload.h" 11 | 12 | typedef struct Dsp Dsp; 13 | 14 | typedef struct Apu Apu; 15 | 16 | typedef struct DspChannel { 17 | // pitch 18 | uint16_t pitch; 19 | uint16_t pitchCounter; 20 | bool pitchModulation; 21 | // brr decoding 22 | int16_t decodeBuffer[19]; // 16 samples per brr-block, +3 for interpolation 23 | uint8_t srcn; 24 | uint16_t decodeOffset; 25 | uint8_t previousFlags; // from last sample 26 | int16_t old; 27 | int16_t older; 28 | bool useNoise; 29 | // adsr, envelope, gain 30 | uint16_t adsrRates[4]; // attack, decay, sustain, gain 31 | uint16_t rateCounter; 32 | uint8_t adsrState; // 0: attack, 1: decay, 2: sustain, 3: gain, 4: release 33 | uint16_t sustainLevel; 34 | bool useGain; 35 | uint8_t gainMode; 36 | bool directGain; 37 | uint16_t gainValue; // for direct gain 38 | uint16_t gain; 39 | // keyon/off 40 | bool keyOn; 41 | bool keyOff; 42 | // output 43 | int16_t sampleOut; // final sample, to be multiplied by channel volume 44 | int8_t volumeL; 45 | int8_t volumeR; 46 | bool echoEnable; 47 | } DspChannel; 48 | 49 | struct Dsp { 50 | uint8_t *apu_ram; 51 | // mirror ram 52 | uint8_t ram[0x80]; 53 | // 8 channels 54 | DspChannel channel[8]; 55 | // overarching 56 | uint16_t dirPage; 57 | bool evenCycle; 58 | bool mute; 59 | bool reset; 60 | int8_t masterVolumeL; 61 | int8_t masterVolumeR; 62 | // noise 63 | int16_t noiseSample; 64 | uint16_t noiseRate; 65 | uint16_t noiseCounter; 66 | // echo 67 | bool echoWrites; 68 | int8_t echoVolumeL; 69 | int8_t echoVolumeR; 70 | int8_t feedbackVolume; 71 | uint16_t echoBufferAdr; 72 | uint16_t echoDelay; 73 | uint16_t echoRemain; 74 | uint16_t echoBufferIndex; 75 | uint8_t firBufferIndex; 76 | int8_t firValues[8]; 77 | int16_t firBufferL[8]; 78 | int16_t firBufferR[8]; 79 | // sample buffer (1 frame at 32040 Hz: 534 samples, *2 for stereo) 80 | int16_t sampleBuffer[534 * 2]; 81 | uint16_t sampleOffset; // current offset in samplebuffer 82 | }; 83 | 84 | typedef struct DspRegWriteHistory { 85 | uint32_t count; 86 | uint8_t addr[256]; 87 | uint8_t val[256]; 88 | } DspRegWriteHistory; 89 | 90 | Dsp *dsp_init(uint8_t *ram); 91 | void dsp_free(Dsp* dsp); 92 | void dsp_reset(Dsp* dsp); 93 | void dsp_cycle(Dsp* dsp); 94 | uint8_t dsp_read(Dsp* dsp, uint8_t adr); 95 | void dsp_write(Dsp* dsp, uint8_t adr, uint8_t val); 96 | void dsp_getSamples(Dsp* dsp, int16_t* sampleData, int samplesPerFrame); 97 | void dsp_saveload(Dsp *dsp, SaveLoadInfo *sli); 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/snes/dsp_regs.h: -------------------------------------------------------------------------------- 1 | #ifndef DSP_REGS_H 2 | #define DSP_REGS_H 3 | 4 | enum DspReg { 5 | V0VOLL = 0x00, 6 | V0VOLR = 0x01, 7 | V0PITCHL = 0x02, 8 | V0PITCHH = 0x03, 9 | V0SRCN = 0x04, 10 | V0ADSR1 = 0x05, 11 | V0ADSR2 = 0x06, 12 | V0GAIN = 0x07, 13 | V0ENVX = 0x08, 14 | V0OUTX = 0x09, 15 | MVOLL = 0x0C, 16 | EFB = 0x0D, 17 | FIR0 = 0x0F, 18 | V1VOLL = 0x10, 19 | V1VOLR = 0x11, 20 | V1PL = 0x12, 21 | V1PH = 0x13, 22 | V1SRCN = 0x14, 23 | V1ADSR1 = 0x15, 24 | V1ADSR2 = 0x16, 25 | V1GAIN = 0x17, 26 | V1ENVX = 0x18, 27 | V1OUTX = 0x19, 28 | MVOLR = 0x1C, 29 | FIR1 = 0x1F, 30 | V2VOLL = 0x20, 31 | V2VOLR = 0x21, 32 | V2PL = 0x22, 33 | V2PH = 0x23, 34 | V2SRCN = 0x24, 35 | V2ADSR1 = 0x25, 36 | V2ADSR2 = 0x26, 37 | V2GAIN = 0x27, 38 | V2ENVX = 0x28, 39 | V2OUTX = 0x29, 40 | EVOLL = 0x2C, 41 | PMON = 0x2D, 42 | FIR2 = 0x2F, 43 | V3VOLL = 0x30, 44 | V3VOLR = 0x31, 45 | V3PL = 0x32, 46 | V3PH = 0x33, 47 | V3SRCN = 0x34, 48 | V3ADSR1 = 0x35, 49 | V3ADSR2 = 0x36, 50 | V3GAIN = 0x37, 51 | V3ENVX = 0x38, 52 | V3OUTX = 0x39, 53 | EVOLR = 0x3C, 54 | NON = 0x3D, 55 | FIR3 = 0x3F, 56 | V4VOLL = 0x40, 57 | V4VOLR = 0x41, 58 | V4PL = 0x42, 59 | V4PH = 0x43, 60 | V4SRCN = 0x44, 61 | V4ADSR1 = 0x45, 62 | V4ADSR2 = 0x46, 63 | V4GAIN = 0x47, 64 | V4ENVX = 0x48, 65 | V4OUTX = 0x49, 66 | KON = 0x4C, 67 | EON = 0x4D, 68 | FIR4 = 0x4F, 69 | V5VOLL = 0x50, 70 | V5VOLR = 0x51, 71 | V5PL = 0x52, 72 | V5PH = 0x53, 73 | V5SRCN = 0x54, 74 | V5ADSR1 = 0x55, 75 | V5ADSR2 = 0x56, 76 | V5GAIN = 0x57, 77 | V5ENVX = 0x58, 78 | V5OUTX = 0x59, 79 | KOF = 0x5C, 80 | DIR = 0x5D, 81 | FIR5 = 0x5F, 82 | V6VOLL = 0x60, 83 | V6VOLR = 0x61, 84 | V6PL = 0x62, 85 | V6PH = 0x63, 86 | V6SRCN = 0x64, 87 | V6ADSR1 = 0x65, 88 | V6ADSR2 = 0x66, 89 | V6GAIN = 0x67, 90 | V6ENVX = 0x68, 91 | V6OUTX = 0x69, 92 | FLG = 0x6C, 93 | ESA = 0x6D, 94 | FIR6 = 0x6F, 95 | V7VOLL = 0x70, 96 | V7VOLR = 0x71, 97 | V7PL = 0x72, 98 | V7PH = 0x73, 99 | V7SRCN = 0x74, 100 | V7ADSR1 = 0x75, 101 | V7ADSR2 = 0x76, 102 | V7GAIN = 0x77, 103 | V7ENVX = 0x78, 104 | V7OUTX = 0x79, 105 | ENDX = 0x7C, 106 | EDL = 0x7D, 107 | FIR7 = 0x7F, 108 | }; 109 | #endif // DSP_REGS_H -------------------------------------------------------------------------------- /src/snes/ppu.h: -------------------------------------------------------------------------------- 1 | #ifndef PPU_H 2 | #define PPU_H 3 | 4 | #include "../types.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "saveload.h" 13 | 14 | typedef struct Ppu Ppu; 15 | 16 | typedef struct BgLayer { 17 | uint16_t xhScroll; 18 | uint16_t xvScroll; 19 | bool xtilemapWider; 20 | bool xtilemapHigher; 21 | uint16_t xtilemapAdr; 22 | uint16_t xtileAdr; 23 | bool xxbigTiles; 24 | bool xxmosaicEnabled; 25 | } BgLayer; 26 | 27 | enum { 28 | kPpuXPixels = 256, 29 | kPpuExtraLeftRight = 0, 30 | }; 31 | 32 | typedef uint16_t PpuZbufType; 33 | 34 | typedef struct PpuPixelPrioBufs { 35 | // This holds the prio in the upper 8 bits and the color in the lower 8 bits. 36 | PpuZbufType data[kPpuXPixels]; 37 | } PpuPixelPrioBufs; 38 | 39 | enum { 40 | kPpuRenderFlags_NewRenderer = 1, 41 | // Render mode7 upsampled by 4x4 42 | kPpuRenderFlags_4x4Mode7 = 2, 43 | // Use 240 height instead of 224 44 | kPpuRenderFlags_Height240 = 4, 45 | // Disable sprite render limits 46 | kPpuRenderFlags_NoSpriteLimits = 8, 47 | }; 48 | 49 | typedef struct Layer { 50 | bool xmainScreenEnabled; 51 | bool xsubScreenEnabled; 52 | bool xmainScreenWindowed; 53 | bool xsubScreenWindowed; 54 | } Layer; 55 | 56 | typedef struct WindowLayer { 57 | bool xwindow1enabled; 58 | bool xwindow2enabled; 59 | bool xwindow1inversed; 60 | bool xwindow2inversed; 61 | uint8_t xmaskLogic; 62 | } WindowLayer; 63 | 64 | #define PPU_SAVESTATE_REGS_SIZE 0x40 65 | #define PPU_SAVESTATE_MEM_SIZE 0x10420 66 | 67 | struct Ppu { 68 | // Snes registers. Saved to snapshot. Need to be stable 69 | // -- START OF SNAPSHOT, 0x40 bytes 70 | uint8 inidisp; 71 | uint8 obsel; 72 | uint8 oamaddl; 73 | uint8 oamaddh; 74 | uint8 bgmode; 75 | uint8 mosaic; 76 | uint8 bgXsc[4]; 77 | uint16 bgTileAdr; 78 | uint8 m7sel; 79 | uint8 setini; 80 | uint16 hScroll[4]; 81 | uint16 vScroll[4]; 82 | int16_t m7matrix[8]; // a, b, c, d, x, y, h, v 83 | uint16 fixedColor; 84 | uint32 windowsel; 85 | uint8 window1left; 86 | uint8 window1right; 87 | uint8 window2left; 88 | uint8 window2right; 89 | uint16 wbgobjlog; 90 | uint8 screenEnabled[2]; 91 | uint8 screenWindowed[2]; 92 | uint8 cgadsub; 93 | uint8 cgwsel; 94 | // -- END OF SNAPSHOT 95 | 96 | // vram access 97 | uint16_t vramPointer; 98 | bool vramIncrementOnHigh; 99 | uint8_t vramRemapMode; 100 | uint8_t vramIncrement; 101 | uint16_t vramReadBuffer; 102 | // cgram access 103 | uint8_t cgramPointer; 104 | bool cgramSecondWrite; 105 | uint8_t cgramBuffer; 106 | // oam access 107 | uint8_t oamAdr; 108 | bool oamInHigh; 109 | bool oamSecondWrite; 110 | uint8_t oamBuffer; 111 | bool timeOver; 112 | bool rangeOver; 113 | uint8_t scrollPrev; 114 | uint8_t scrollPrev2; 115 | uint8_t mosaicStartLine; 116 | uint8_t m7prev; 117 | // mode 7 internal 118 | int32_t m7startX; 119 | int32_t m7startY; 120 | // settings 121 | bool evenFrame; 122 | bool frameOverscan; // if we are overscanning this frame (determined at 0,225) 123 | bool frameInterlace; // if we are interlacing this frame (determined at start vblank) 124 | // latching 125 | uint16_t hCount; 126 | uint16_t vCount; 127 | bool hCountSecond; 128 | bool vCountSecond; 129 | bool countersLatched; 130 | // pixel buffer (xbgr) 131 | // times 2 for even and odd frame 132 | 133 | uint8_t extraLeftCur, extraRightCur, extraLeftRight; 134 | uint8_t lastMosaicModulo; 135 | uint8_t lastBrightnessMult; 136 | bool lineHasSprites; 137 | PpuPixelPrioBufs bgBuffers[2]; 138 | PpuPixelPrioBufs objBuffer; 139 | uint32_t renderPitch; 140 | uint8_t *renderBuffer; 141 | uint8_t brightnessMult[32 + 31]; 142 | uint8_t brightnessMultHalf[32 * 2]; 143 | uint8_t mosaicModulo[kPpuXPixels]; 144 | 145 | void *pad2; 146 | 147 | // -- START OF SNAPSHOT, 0x10420 bytes 148 | uint16_t cgram[0x100]; 149 | uint16_t oam[0x100]; 150 | uint8_t highOam[0x20]; 151 | uint16_t vram[0x8000]; 152 | // -- END OF SNAPSHOT 153 | 154 | 155 | }; 156 | 157 | #define SPRITE_PRIO_TO_PRIO(prio, level6) (((prio) * 4 + 2) * 16 + 4 + (level6 ? 2 : 0)) 158 | #define SPRITE_PRIO_TO_PRIO_HI(prio) ((prio) * 4 + 2) 159 | 160 | #define IS_SCREEN_ENABLED(ppu, sub, layer) (ppu->screenEnabled[sub] & (1 << layer)) 161 | #define IS_SCREEN_WINDOWED(ppu, sub, layer) (ppu->screenWindowed[sub] & (1 << layer)) 162 | #define GET_WINDOW_FLAGS(ppu, layer) (ppu->windowsel >> (layer * 4)) 163 | 164 | #define PPU_brightness(ppu) (ppu->inidisp & 0xf) 165 | #define PPU_forcedBlank(ppu) (ppu->inidisp & 0x80) 166 | 167 | #define PPU_objSize(ppu) (ppu->obsel >> 5) 168 | #define PPU_objTileAdr1(ppu) ((ppu->obsel & 7) << 13) 169 | #define PPU_objTileAdr2(ppu) (PPU_objTileAdr1(ppu) + (((ppu->obsel & 0x18) + 8) << 9)) 170 | 171 | #define PPU_objPriority(ppu) (ppu->oamaddh & 0x80) 172 | 173 | #define PPU_mode(ppu) (ppu->bgmode & 7) 174 | #define PPU_bg3priority(ppu) (ppu->bgmode & 0x8) 175 | #define PPU_bigTiles(ppu, layer) (ppu->bgmode >> layer & 0x10) 176 | 177 | #define PPU_mosaicEnabled(ppu, layer) (ppu->mosaic & (1 << layer)) 178 | #define PPU_mosaicSize(ppu) ((ppu->mosaic >> 4) + 1) 179 | 180 | #define PPU_bgTilemapWider(ppu, layer) (ppu->bgXsc[layer] & 0x1) 181 | #define PPU_bgTilemapHigher(ppu, layer) (ppu->bgXsc[layer] & 0x2) 182 | #define PPU_bgTilemapAdr(ppu, layer) ((ppu->bgXsc[layer] & 0xfc) << 8) 183 | #define PPU_bgTileAdr(ppu, layer) ((ppu->bgTileAdr >> (layer * 4) & 0xf) << 12) 184 | 185 | #define PPU_m7xFlip(ppu) (ppu->m7sel & 0x1) 186 | #define PPU_m7yFlip(ppu) (ppu->m7sel & 0x2) 187 | #define PPU_m7charFill(ppu) (ppu->m7sel & 0x40) 188 | #define PPU_m7largeField(ppu) (ppu->m7sel & 0x80) 189 | 190 | #define PPU_directColor(ppu) ((ppu->cgwsel & 0x1) != 0) 191 | #define PPU_addSubscreen(ppu) ((ppu->cgwsel & 0x2) != 0) 192 | #define PPU_preventMathMode(ppu) (ppu->cgwsel >> 4 & 0x3) 193 | #define PPU_clipMode(ppu) (ppu->cgwsel >> 6 & 0x3) 194 | 195 | #define PPU_mathEnabled(ppu) (ppu->cgadsub & 0x3f) 196 | #define PPU_halfColor(ppu) ((ppu->cgadsub & 0x40) != 0) 197 | #define PPU_subtractColor(ppu) ((ppu->cgadsub & 0x80) != 0) 198 | 199 | #define PPU_fixedColorR(ppu) (ppu->fixedColor & 0x1f) 200 | #define PPU_fixedColorG(ppu) (ppu->fixedColor >> 5 & 0x1f) 201 | #define PPU_fixedColorB(ppu) (ppu->fixedColor >> 10 & 0x1f) 202 | 203 | #define PPU_interlace(ppu) ((ppu->setini & 0x1) != 0) 204 | #define PPU_objInterlace(ppu) ((ppu->setini & 0x2) != 0) 205 | #define PPU_overscan(ppu) ((ppu->setini & 0x4) != 0) 206 | #define PPU_pseudoHires(ppu) ((ppu->setini & 0x8) != 0) 207 | #define PPU_m7extBg(ppu) ((ppu->setini & 0x40) != 0) 208 | 209 | 210 | enum { 211 | kWindow1Inversed = 1, 212 | kWindow1Enabled = 2, 213 | kWindow2Inversed = 4, 214 | kWindow2Enabled = 8, 215 | }; 216 | 217 | 218 | Ppu* ppu_init(void); 219 | void ppu_free(Ppu* ppu); 220 | void ppu_copy(Ppu *ppu, Ppu *ppu_src); 221 | void ppu_reset(Ppu* ppu); 222 | bool ppu_checkOverscan(Ppu* ppu); 223 | void ppu_handleVblank(Ppu* ppu); 224 | void ppu_runLine(Ppu* ppu, int line); 225 | uint8_t ppu_read(Ppu* ppu, uint8_t adr); 226 | void ppu_write(Ppu* ppu, uint8_t adr, uint8_t val); 227 | void ppu_saveload(Ppu *ppu, SaveLoadInfo *sli); 228 | void PpuBeginDrawing(Ppu *ppu, uint8_t *pixels, size_t pitch, uint32_t render_flags); 229 | 230 | int PpuGetCurrentRenderScale(Ppu *ppu, uint32_t render_flags); 231 | 232 | #endif 233 | -------------------------------------------------------------------------------- /src/snes/saveload.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | typedef struct SaveLoadInfo SaveLoadInfo; 3 | typedef void SaveLoadInfoFunc(SaveLoadInfo *info, void *data, size_t data_size); 4 | struct SaveLoadInfo { 5 | SaveLoadInfoFunc *func; 6 | }; 7 | 8 | //#define SL(x) sli->func(sli, &x, sizeof(x)) -------------------------------------------------------------------------------- /src/snes/snes.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SNES_H 3 | #define SNES_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct Snes Snes; 12 | 13 | #include "cpu.h" 14 | #include "apu.h" 15 | #include "dma.h" 16 | #include "ppu.h" 17 | #include "cart.h" 18 | #include "saveload.h" 19 | 20 | struct Snes { 21 | Cpu* cpu; 22 | Apu* apu; 23 | Ppu* ppu; 24 | Dma* dma; 25 | Cart* cart; 26 | uint16 input1_currentState; 27 | uint16 input2_currentState; 28 | // input 29 | bool debug_cycles; 30 | bool debug_apu_cycles; 31 | bool disableRender; 32 | uint8_t runningWhichVersion; 33 | 34 | // ram 35 | uint32_t ramAdr; 36 | uint8_t *ram; 37 | uint8_t padx[4]; 38 | 39 | // frame timing 40 | uint16_t hPos; 41 | uint16_t vPos; 42 | uint32_t frames; 43 | // cpu handling 44 | uint8_t cpuCyclesLeft; 45 | uint8_t cpuMemOps; 46 | uint8_t padpad[2]; 47 | double apuCatchupCycles; 48 | // nmi / irq 49 | bool hIrqEnabled; 50 | bool vIrqEnabled; 51 | bool nmiEnabled; 52 | uint16_t hTimer; 53 | uint16_t vTimer; 54 | bool inNmi; 55 | bool inIrq; 56 | bool inVblank; 57 | // joypad handling 58 | uint16_t portAutoReadX[4]; // as read by auto-joypad read 59 | bool autoJoyRead; 60 | uint16_t autoJoyTimer; // times how long until reading is done 61 | bool ppuLatch; 62 | // multiplication/division 63 | uint8_t multiplyA; 64 | uint16_t multiplyResult; 65 | uint16_t divideA; 66 | uint16_t divideResult; 67 | // misc 68 | bool fastMem; 69 | uint8_t openBus; 70 | }; 71 | 72 | Snes* snes_init(uint8_t *ram); 73 | void snes_free(Snes* snes); 74 | void snes_reset(Snes* snes, bool hard); 75 | // used by dma, cpu 76 | uint8_t snes_readBBus(Snes* snes, uint8_t adr); 77 | void snes_writeBBus(Snes* snes, uint8_t adr, uint8_t val); 78 | uint8_t snes_read(Snes* snes, uint32_t adr); 79 | void snes_write(Snes* snes, uint32_t adr, uint8_t val); 80 | uint8_t snes_cpuRead(Snes* snes, uint32_t adr); 81 | void snes_cpuWrite(Snes* snes, uint32_t adr, uint8_t val); 82 | // debugging 83 | void snes_debugCycle(Snes* snes, bool* cpuNext, bool* spcNext); 84 | 85 | void snes_handle_pos_stuff(Snes *snes); 86 | 87 | // snes_other.c functions: 88 | 89 | bool snes_loadRom(Snes* snes, const uint8_t* data, int length); 90 | void snes_setPixels(Snes* snes, uint8_t* pixelData); 91 | void snes_setSamples(Snes* snes, int16_t* sampleData, int samplesPerFrame); 92 | void snes_saveload(Snes *snes, SaveLoadInfo *sli); 93 | uint8_t snes_readBBusOrg(Snes *snes, uint8_t adr); 94 | void snes_catchupApu(Snes *snes); 95 | 96 | void snes_runCycle(Snes *snes); 97 | void snes_runCpu(Snes *snes); 98 | 99 | extern int snes_frame_counter; 100 | #endif 101 | -------------------------------------------------------------------------------- /src/snes/snes_other.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "snes.h" 9 | #include "cart.h" 10 | #include "ppu.h" 11 | #include "dsp.h" 12 | 13 | typedef struct CartHeader { 14 | // normal header 15 | uint8_t headerVersion; // 1, 2, 3 16 | char name[22]; // $ffc0-$ffd4 (max 21 bytes + \0), $ffd4=$00: header V2 17 | uint8_t speed; // $ffd5.7-4 (always 2 or 3) 18 | uint8_t type; // $ffd5.3-0 19 | uint8_t coprocessor; // $ffd6.7-4 20 | uint8_t chips; // $ffd6.3-0 21 | uint32_t romSize; // $ffd7 (0x400 << x) 22 | uint32_t ramSize; // $ffd8 (0x400 << x) 23 | uint8_t region; // $ffd9 (also NTSC/PAL) 24 | uint8_t maker; // $ffda ($33: header V3) 25 | uint8_t version; // $ffdb 26 | uint16_t checksumComplement; // $ffdc,$ffdd 27 | uint16_t checksum; // $ffde,$ffdf 28 | // v2/v3 (v2 only exCoprocessor) 29 | char makerCode[3]; // $ffb0,$ffb1: (2 chars + \0) 30 | char gameCode[5]; // $ffb2-$ffb5: (4 chars + \0) 31 | uint32_t flashSize; // $ffbc (0x400 << x) 32 | uint32_t exRamSize; // $ffbd (0x400 << x) (used for GSU?) 33 | uint8_t specialVersion; // $ffbe 34 | uint8_t exCoprocessor; // $ffbf (if coprocessor = $f) 35 | // calculated stuff 36 | int16_t score; // score for header, to see which mapping is most likely 37 | bool pal; // if this is a rom for PAL regions instead of NTSC 38 | uint8_t cartType; // calculated type 39 | } CartHeader; 40 | 41 | static void readHeader(const uint8_t* data, int length, int location, CartHeader* header); 42 | 43 | bool snes_loadRom(Snes* snes, const uint8_t* data, int length) { 44 | // if smaller than smallest possible, don't load 45 | if(length < 0x8000) { 46 | printf("Failed to load rom: rom to small (%d bytes)\n", length); 47 | return false; 48 | } 49 | // check headers 50 | CartHeader headers[4]; 51 | memset(headers, 0, sizeof(headers)); 52 | for(int i = 0; i < 4; i++) { 53 | headers[i].score = -50; 54 | } 55 | if(length >= 0x8000) readHeader(data, length, 0x7fc0, &headers[0]); 56 | if(length >= 0x8200) readHeader(data, length, 0x81c0, &headers[1]); 57 | if(length >= 0x10000) readHeader(data, length, 0xffc0, &headers[2]); 58 | if(length >= 0x10200) readHeader(data, length, 0x101c0, &headers[3]); 59 | // see which it is 60 | int max = 0; 61 | int used = 0; 62 | for(int i = 0; i < 4; i++) { 63 | if(headers[i].score > max) { 64 | max = headers[i].score; 65 | used = i; 66 | } 67 | } 68 | if(used & 1) { 69 | // odd-numbered ones are for headered roms 70 | data += 0x200; // move pointer past header 71 | length -= 0x200; // and subtract from size 72 | } 73 | // check if we can load it 74 | if(headers[used].cartType > 2) { 75 | printf("Failed to load rom: unsupported type (%d)\n", headers[used].cartType); 76 | return false; 77 | } 78 | // expand to a power of 2 79 | int newLength = 0x8000; 80 | while(true) { 81 | if(length <= newLength) { 82 | break; 83 | } 84 | newLength *= 2; 85 | } 86 | uint8_t* newData = malloc(newLength); 87 | memcpy(newData, data, length); 88 | int test = 1; 89 | while(length != newLength) { 90 | if(length & test) { 91 | memcpy(newData + length, newData + length - test, test); 92 | length += test; 93 | } 94 | test *= 2; 95 | } 96 | // load it 97 | cart_load( 98 | snes->cart, headers[used].cartType, 99 | newData, newLength, headers[used].chips > 0 ? headers[used].ramSize : 0 100 | ); 101 | 102 | free(newData); 103 | return true; 104 | } 105 | 106 | void snes_setSamples(Snes* snes, int16_t* sampleData, int samplesPerFrame) { 107 | // size is 2 (int16) * 2 (stereo) * samplesPerFrame 108 | // sets samples in the sampleData 109 | dsp_getSamples(snes->apu->dsp, sampleData, samplesPerFrame); 110 | } 111 | 112 | static void readHeader(const uint8_t* data, int length, int location, CartHeader* header) { 113 | // read name, TODO: non-ASCII names? 114 | for(int i = 0; i < 21; i++) { 115 | uint8_t ch = data[location + i]; 116 | if(ch >= 0x20 && ch < 0x7f) { 117 | header->name[i] = ch; 118 | } else { 119 | header->name[i] = '.'; 120 | } 121 | } 122 | header->name[21] = 0; 123 | // read rest 124 | header->speed = data[location + 0x15] >> 4; 125 | header->type = data[location + 0x15] & 0xf; 126 | header->coprocessor = data[location + 0x16] >> 4; 127 | header->chips = data[location + 0x16] & 0xf; 128 | header->romSize = 0x400 << data[location + 0x17]; 129 | header->ramSize = 0x400 << data[location + 0x18]; 130 | header->region = data[location + 0x19]; 131 | header->maker = data[location + 0x1a]; 132 | header->version = data[location + 0x1b]; 133 | header->checksumComplement = (data[location + 0x1d] << 8) + data[location + 0x1c]; 134 | header->checksum = (data[location + 0x1f] << 8) + data[location + 0x1e]; 135 | // read v3 and/or v2 136 | header->headerVersion = 1; 137 | if(header->maker == 0x33) { 138 | header->headerVersion = 3; 139 | // maker code 140 | for(int i = 0; i < 2; i++) { 141 | uint8_t ch = data[location - 0x10 + i]; 142 | if(ch >= 0x20 && ch < 0x7f) { 143 | header->makerCode[i] = ch; 144 | } else { 145 | header->makerCode[i] = '.'; 146 | } 147 | } 148 | header->makerCode[2] = 0; 149 | // game code 150 | for(int i = 0; i < 4; i++) { 151 | uint8_t ch = data[location - 0xe + i]; 152 | if(ch >= 0x20 && ch < 0x7f) { 153 | header->gameCode[i] = ch; 154 | } else { 155 | header->gameCode[i] = '.'; 156 | } 157 | } 158 | header->gameCode[4] = 0; 159 | header->flashSize = 0x400 << data[location - 4]; 160 | header->exRamSize = 0x400 << data[location - 3]; 161 | header->specialVersion = data[location - 2]; 162 | header->exCoprocessor = data[location - 1]; 163 | } else if(data[location + 0x14] == 0) { 164 | header->headerVersion = 2; 165 | header->exCoprocessor = data[location - 1]; 166 | } 167 | // get region 168 | header->pal = (header->region >= 0x2 && header->region <= 0xc) || header->region == 0x11; 169 | header->cartType = location < 0x9000 ? 1 : 2; 170 | // get score 171 | // TODO: check name, maker/game-codes (if V3) for ASCII, more vectors, 172 | // more first opcode, rom-sizes (matches?), type (matches header location?) 173 | int score = 0; 174 | score += (header->speed == 2 || header->speed == 3) ? 5 : -4; 175 | score += (header->type <= 3 || header->type == 5) ? 5 : -2; 176 | score += (header->coprocessor <= 5 || header->coprocessor >= 0xe) ? 5 : -2; 177 | score += (header->chips <= 6 || header->chips == 9 || header->chips == 0xa) ? 5 : -2; 178 | score += (header->region <= 0x14) ? 5 : -2; 179 | score += (header->checksum + header->checksumComplement == 0xffff) ? 8 : -6; 180 | uint16_t resetVector = data[location + 0x3c] | (data[location + 0x3d] << 8); 181 | score += (resetVector >= 0x8000) ? 8 : -20; 182 | // check first opcode after reset 183 | int opcodeLoc = location + 0x40 - 0x8000 + (resetVector & 0x7fff); 184 | uint8_t opcode = 0xff; 185 | if(opcodeLoc < length) { 186 | opcode = data[opcodeLoc]; 187 | } else { 188 | score -= 14; 189 | } 190 | if(opcode == 0x78 || opcode == 0x18) { 191 | // sei, clc (for clc:xce) 192 | score += 6; 193 | } 194 | if(opcode == 0x4c || opcode == 0x5c || opcode == 0x9c) { 195 | // jmp abs, jml abl, stz abs 196 | score += 3; 197 | } 198 | if(opcode == 0x00 || opcode == 0xff || opcode == 0xdb) { 199 | // brk, sbc alx, stp 200 | score -= 6; 201 | } 202 | header->score = score; 203 | } 204 | -------------------------------------------------------------------------------- /src/snes/snes_regs.h: -------------------------------------------------------------------------------- 1 | #ifndef SNES_SNES_REGS_H_ 2 | #define SNES_SNES_REGS_H_ 3 | #pragma once 4 | 5 | typedef enum SnesRegs { 6 | INIDISP = 0x2100, 7 | OBSEL = 0x2101, 8 | OAMADDL = 0x2102, 9 | OAMADDH = 0x2103, 10 | OAMDATA = 0x2104, 11 | BGMODE = 0x2105, 12 | MOSAIC = 0x2106, 13 | BG1SC = 0x2107, 14 | BG2SC = 0x2108, 15 | BG3SC = 0x2109, 16 | BG4SC = 0x210A, 17 | BG12NBA = 0x210B, 18 | BG34NBA = 0x210C, 19 | BG1HOFS = 0x210D, 20 | BG1VOFS = 0x210E, 21 | BG2HOFS = 0x210F, 22 | BG2VOFS = 0x2110, 23 | BG3HOFS = 0x2111, 24 | BG3VOFS = 0x2112, 25 | BG4HOFS = 0x2113, 26 | BG4VOFS = 0x2114, 27 | VMAIN = 0x2115, 28 | VMADDL = 0x2116, 29 | VMADDH = 0x2117, 30 | VMDATAL = 0x2118, 31 | VMDATAH = 0x2119, 32 | M7SEL = 0x211A, 33 | M7A = 0x211B, 34 | M7B = 0x211C, 35 | M7C = 0x211D, 36 | M7D = 0x211E, 37 | M7X = 0x211F, 38 | M7Y = 0x2120, 39 | CGADD = 0x2121, 40 | CGDATA = 0x2122, 41 | W12SEL = 0x2123, 42 | W34SEL = 0x2124, 43 | WOBJSEL = 0x2125, 44 | WH0 = 0x2126, 45 | WH1 = 0x2127, 46 | WH2 = 0x2128, 47 | WH3 = 0x2129, 48 | WBGLOG = 0x212A, 49 | WOBJLOG = 0x212B, 50 | TM = 0x212C, 51 | TS = 0x212D, 52 | TMW = 0x212E, 53 | TSW = 0x212F, 54 | CGWSEL = 0x2130, 55 | CGADSUB = 0x2131, 56 | COLDATA = 0x2132, 57 | SETINI = 0x2133, 58 | MPYL = 0x2134, 59 | MPYM = 0x2135, 60 | MPYH = 0x2136, 61 | SLHV = 0x2137, 62 | RDOAM = 0x2138, 63 | RDVRAML = 0x2139, 64 | RDVRAMH = 0x213A, 65 | RDCGRAM = 0x213B, 66 | OPHCT = 0x213C, 67 | OPVCT = 0x213D, 68 | STAT77 = 0x213E, 69 | STAT78 = 0x213F, 70 | APUI00 = 0x2140, 71 | APUI01 = 0x2141, 72 | APUI02 = 0x2142, 73 | APUI03 = 0x2143, 74 | WMDATA = 0x2180, 75 | WMADDL = 0x2181, 76 | WMADDM = 0x2182, 77 | WMADDH = 0x2183, 78 | JOYA = 0x4016, 79 | JOYB = 0x4017, 80 | NMITIMEN = 0x4200, 81 | WRIO = 0x4201, 82 | WRMPYA = 0x4202, 83 | WRMPYB = 0x4203, 84 | WRDIVL = 0x4204, 85 | WRDIVH = 0x4205, 86 | WRDIVB = 0x4206, 87 | HTIMEL = 0x4207, 88 | HTIMEH = 0x4208, 89 | VTIMEL = 0x4209, 90 | VTIMEH = 0x420A, 91 | MDMAEN = 0x420B, 92 | HDMAEN = 0x420C, 93 | MEMSEL = 0x420D, 94 | RDNMI = 0x4210, 95 | TIMEUP = 0x4211, 96 | HVBJOY = 0x4212, 97 | RDIO = 0x4213, 98 | RDDIVL = 0x4214, 99 | RDDIVH = 0x4215, 100 | RDMPYL = 0x4216, 101 | RDMPYH = 0x4217, 102 | JOY1L = 0x4218, 103 | JOY1H = 0x4219, 104 | JOY2L = 0x421A, 105 | JOY2H = 0x421B, 106 | JOY3L = 0x421C, 107 | JOY3H = 0x421D, 108 | JOY4L = 0x421E, 109 | JOY4H = 0x421F, 110 | DMAP0 = 0x4300, 111 | BBAD0 = 0x4301, 112 | A1T0L = 0x4302, 113 | A1T0H = 0x4303, 114 | A1B0 = 0x4304, 115 | DAS0L = 0x4305, 116 | DAS0H = 0x4306, 117 | DAS00 = 0x4307, 118 | A2A0L = 0x4308, 119 | A2A0H = 0x4309, 120 | NTRL0 = 0x430A, 121 | UNUSED0 = 0x430B, 122 | MIRR0 = 0x430F, 123 | DMAP1 = 0x4310, 124 | BBAD1 = 0x4311, 125 | A1T1L = 0x4312, 126 | A1T1H = 0x4313, 127 | A1B1 = 0x4314, 128 | DAS1L = 0x4315, 129 | DAS1H = 0x4316, 130 | DAS10 = 0x4317, 131 | A2A1L = 0x4318, 132 | A2A1H = 0x4319, 133 | NTRL1 = 0x431A, 134 | UNUSED1 = 0x431B, 135 | MIRR1 = 0x431F, 136 | DMAP2 = 0x4320, 137 | BBAD2 = 0x4321, 138 | A1T2L = 0x4322, 139 | A1T2H = 0x4323, 140 | A1B2 = 0x4324, 141 | DAS2L = 0x4325, 142 | DAS2H = 0x4326, 143 | DAS20 = 0x4327, 144 | A2A2L = 0x4328, 145 | A2A2H = 0x4329, 146 | NTRL2 = 0x432A, 147 | UNUSED2 = 0x432B, 148 | MIRR2 = 0x432F, 149 | DMAP3 = 0x4330, 150 | BBAD3 = 0x4331, 151 | A1T3L = 0x4332, 152 | A1T3H = 0x4333, 153 | A1B3 = 0x4334, 154 | DAS3L = 0x4335, 155 | DAS3H = 0x4336, 156 | DAS30 = 0x4337, 157 | A2A3L = 0x4338, 158 | A2A3H = 0x4339, 159 | NTRL3 = 0x433A, 160 | UNUSED3 = 0x433B, 161 | MIRR3 = 0x433F, 162 | DMAP4 = 0x4340, 163 | BBAD4 = 0x4341, 164 | A1T4L = 0x4342, 165 | A1T4H = 0x4343, 166 | A1B4 = 0x4344, 167 | DAS4L = 0x4345, 168 | DAS4H = 0x4346, 169 | DAS40 = 0x4347, 170 | A2A4L = 0x4348, 171 | A2A4H = 0x4349, 172 | NTRL4 = 0x434A, 173 | UNUSED4 = 0x434B, 174 | MIRR4 = 0x434F, 175 | DMAP5 = 0x4350, 176 | BBAD5 = 0x4351, 177 | A1T5L = 0x4352, 178 | A1T5H = 0x4353, 179 | A1B5 = 0x4354, 180 | DAS5L = 0x4355, 181 | DAS5H = 0x4356, 182 | DAS50 = 0x4357, 183 | A2A5L = 0x4358, 184 | A2A5H = 0x4359, 185 | NTRL5 = 0x435A, 186 | UNUSED5 = 0x435B, 187 | MIRR5 = 0x435F, 188 | DMAP6 = 0x4360, 189 | BBAD6 = 0x4361, 190 | A1T6L = 0x4362, 191 | A1T6H = 0x4363, 192 | A1B6 = 0x4364, 193 | DAS6L = 0x4365, 194 | DAS6H = 0x4366, 195 | DAS60 = 0x4367, 196 | A2A6L = 0x4368, 197 | A2A6H = 0x4369, 198 | NTRL6 = 0x436A, 199 | UNUSED6 = 0x436B, 200 | MIRR6 = 0x436F, 201 | 202 | DMAP7 = 0x4370, 203 | BBAD7 = 0x4371, 204 | A1T7L = 0x4372, 205 | A1T7H = 0x4373, 206 | A1B7 = 0x4374, 207 | DAS7L = 0x4375, 208 | DAS7H = 0x4376, 209 | 210 | 211 | DAS70 = 0x4377, 212 | A2A7L = 0x4378, 213 | A2A7H = 0x4379, 214 | NTRL7 = 0x437A, 215 | UNUSED7 = 0x437B, 216 | MIRR7 = 0x437F, 217 | } SnesRegs; 218 | 219 | #endif // SNES_SNES_REGS_H_ -------------------------------------------------------------------------------- /src/snes/spc.h: -------------------------------------------------------------------------------- 1 | #ifndef SPC_H 2 | #define SPC_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct Spc Spc; 11 | 12 | #include "apu.h" 13 | #include "saveload.h" 14 | 15 | struct Spc { 16 | Apu* apu; 17 | // registers 18 | uint8_t a; 19 | uint8_t x; 20 | uint8_t y; 21 | uint8_t sp; 22 | uint16_t pc; 23 | // flags 24 | bool c; 25 | bool z; 26 | bool v; 27 | bool n; 28 | bool i; 29 | bool h; 30 | bool p; 31 | bool b; 32 | // stopping 33 | bool stopped; 34 | // internal use 35 | uint8_t cyclesUsed; // indicates how many cycles an opcode used 36 | }; 37 | 38 | Spc* spc_init(Apu* apu); 39 | void spc_free(Spc* spc); 40 | void spc_reset(Spc* spc); 41 | int spc_runOpcode(Spc* spc); 42 | void spc_saveload(Spc *spc, SaveLoadInfo *sli); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/tracing.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef TRACING_H 3 | #define TRACING_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "snes/snes.h" 12 | 13 | void getProcessorStateCpu(Snes* snes, char* line); 14 | void getProcessorStateSpc(Apu* snes, char* line); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | #ifndef SM_TYPES_H_ 2 | #define SM_TYPES_H_ 3 | 4 | #pragma warning(disable: 4244) 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #if defined(_WIN32) && !COMPILER_TCC 11 | #include 12 | #ifndef assert 13 | #define assert _ASSERTE 14 | #endif 15 | #else 16 | #include 17 | #endif 18 | 19 | typedef uint8_t uint8; 20 | typedef int8_t int8; 21 | typedef uint16_t uint16; 22 | typedef int16_t int16; 23 | typedef uint32_t uint32; 24 | typedef int32_t int32; 25 | typedef uint64_t uint64; 26 | typedef int64_t int64; 27 | typedef unsigned int uint; 28 | 29 | typedef uint16 VoidP; 30 | 31 | #define arraysize(x) sizeof(x)/sizeof(x[0]) 32 | #define sign8(x) ((x) & 0x80) 33 | #define sign16(x) ((x) & 0x8000) 34 | #define sign32(x) ((x) & 0x80000000) 35 | #define load24(x) ((*(uint32*)&(x))&0xffffff) 36 | 37 | #ifdef _MSC_VER 38 | #define countof _countof 39 | #define NORETURN __declspec(noreturn) 40 | #define FORCEINLINE __forceinline 41 | #define NOINLINE __declspec(noinline) 42 | #else 43 | #define countof(a) (sizeof(a)/sizeof(*(a))) 44 | #define NORETURN 45 | #define FORCEINLINE inline 46 | #define NOINLINE 47 | #endif 48 | 49 | #ifdef _DEBUG 50 | #define kDebugFlag 1 51 | #else 52 | #define kDebugFlag 0 53 | #endif 54 | 55 | static FORCEINLINE uint16 abs16(uint16 t) { return sign16(t) ? -t : t; } 56 | static FORCEINLINE uint8 abs8(uint8 t) { return sign8(t) ? -t : t; } 57 | static FORCEINLINE int IntMin(int a, int b) { return a < b ? a : b; } 58 | static FORCEINLINE int IntMax(int a, int b) { return a > b ? a : b; } 59 | static FORCEINLINE uint UintMin(uint a, uint b) { return a < b ? a : b; } 60 | static FORCEINLINE uint UintMax(uint a, uint b) { return a > b ? a : b; } 61 | 62 | // windows.h defines this too 63 | #ifdef HIBYTE 64 | #undef HIBYTE 65 | #endif 66 | 67 | #define BYTE(x) (*(uint8*)&(x)) 68 | #define WORD(x) (*(uint16*)&(x)) 69 | #define DWORD(x) (*(uint32*)&(x)) 70 | #define XY(x, y) ((y)*64+(x)) 71 | 72 | static inline uint16 swap16(uint16 v) { return (v << 8) | (v >> 8); } 73 | 74 | void NORETURN Die(const char *error); 75 | void Warning(const char *error); 76 | 77 | // compile time assertion 78 | #define __CASSERT_N0__(l) COMPILE_TIME_ASSERT_ ## l 79 | #define __CASSERT_N1__(l) __CASSERT_N0__(l) 80 | #define CASSERT(cnd) typedef char __CASSERT_N1__(__LINE__) [(cnd) ? 1 : -1] 81 | 82 | #pragma pack(push, 1) 83 | typedef struct LongPtr { 84 | VoidP addr; 85 | uint8 bank; 86 | } LongPtr; 87 | #pragma pack (pop) 88 | #define LONGPTR(t) {(t) & 0xffff, (t) >> 16} 89 | 90 | static inline uint8 __CFSHL__(uint8 x, uint8 t) { return x >> (8 - t); } 91 | static inline uint8 __CFSHL__uint16(uint16 x) { return x >> 15; } 92 | 93 | static inline bool __CFADD__uint16(uint16 x, uint16 y) { return (uint16)(x) > (uint16)(x + y); } 94 | static inline bool __CFADD__uint8(uint8 x, uint8 y) { return (uint8)(x) > (uint8)(x + y); } 95 | static inline bool __CFADD__(uint8 x, uint8 y) { return (uint8)(x) > (uint8)(x + y); } 96 | 97 | typedef struct PairU16 { 98 | uint16 first, second; 99 | } PairU16; 100 | 101 | typedef struct PointU16 { 102 | uint16 x, y; 103 | } PointU16; 104 | 105 | typedef struct PointU8 { 106 | uint8 x, y; 107 | } PointU8; 108 | 109 | typedef struct OamEnt { 110 | uint8 xpos; 111 | uint8 ypos; 112 | uint8 charnum; 113 | uint8 flags; 114 | } OamEnt; 115 | 116 | typedef void FuncU8(uint8 kk); 117 | typedef void FuncV(void); 118 | 119 | // Some convenience macros to make partial accesses nicer 120 | #define LAST_IND(x,part_type) (sizeof(x)/sizeof(part_type) - 1) 121 | #define HIGH_IND(x,part_type) LAST_IND(x,part_type) 122 | #define LOW_IND(x,part_type) 0 123 | // first unsigned macros: 124 | #define BYTEn(x, n) (*((uint8*)&(x)+n)) 125 | #define WORDn(x, n) (*((uint16*)&(x)+n)) 126 | 127 | #define LOBYTE(x) BYTEn(x,LOW_IND(x,uint8)) 128 | #define LOWORD(x) WORDn(x,LOW_IND(x,uint16)) 129 | #define HIBYTE(x) BYTEn(x,1) 130 | #define HIWORD(x) WORDn(x,HIGH_IND(x,uint16)) 131 | 132 | #define GET_HIBYTE(x) (((x) & 0xff00) >> 8) 133 | 134 | // Generate a pair of operands. 135 | #define PAIR16(high, low) ((uint16)((high) << 8) | (uint8)(low)) 136 | #define __PAIR16__ PAIR16 137 | #define __PAIR32__(high, low) (((uint32) (high) << 16) | (uint16)(low)) 138 | 139 | // Helper functions to represent some assembly instructions. 140 | 141 | // compile time assertion 142 | #define __CASSERT_N0__(l) COMPILE_TIME_ASSERT_ ## l 143 | #define __CASSERT_N1__(l) __CASSERT_N0__(l) 144 | #define CASSERT(cnd) typedef char __CASSERT_N1__(__LINE__) [(cnd) ? 1 : -1] 145 | 146 | static inline PairU16 MakePairU16(uint16 k, uint16 j) { 147 | PairU16 r = { k, j }; 148 | return r; 149 | } 150 | 151 | #define MakePairU16_AX MakePairU16 152 | #define MakePairU16_AY MakePairU16 153 | 154 | 155 | typedef struct MemBlk { 156 | const uint8 *ptr; 157 | size_t size; 158 | } MemBlk; 159 | MemBlk FindIndexInMemblk(MemBlk data, size_t i); 160 | const uint8 *FindAddrInMemblk(MemBlk data, uint32 addr); 161 | 162 | #endif // SM_TYPES_H_ 163 | 164 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | #include 4 | #include 5 | 6 | char *NextDelim(char **s, int sep) { 7 | char *r = *s; 8 | if (r) { 9 | while (r[0] == ' ' || r[0] == '\t') 10 | r++; 11 | char *t = strchr(r, sep); 12 | *s = t ? (*t++ = 0, t) : NULL; 13 | } 14 | return r; 15 | } 16 | 17 | static inline int ToLower(int a) { 18 | return a + (a >= 'A' && a <= 'Z') * 32; 19 | } 20 | 21 | bool StringEqualsNoCase(const char *a, const char *b) { 22 | for (;;) { 23 | int aa = ToLower(*a++), bb = ToLower(*b++); 24 | if (aa != bb) 25 | return false; 26 | if (aa == 0) 27 | return true; 28 | } 29 | } 30 | 31 | const char *StringStartsWithNoCase(const char *a, const char *b) { 32 | for (;; a++, b++) { 33 | int aa = ToLower(*a), bb = ToLower(*b); 34 | if (bb == 0) 35 | return a; 36 | if (aa != bb) 37 | return NULL; 38 | } 39 | } 40 | 41 | uint8 *ReadWholeFile(const char *name, size_t *length) { 42 | FILE *f = fopen(name, "rb"); 43 | if (f == NULL) 44 | return NULL; 45 | fseek(f, 0, SEEK_END); 46 | size_t size = ftell(f); 47 | rewind(f); 48 | uint8 *buffer = (uint8 *)malloc(size + 1); 49 | if (!buffer) Die("malloc failed"); 50 | // Always zero terminate so this function can be used also for strings. 51 | buffer[size] = 0; 52 | if (fread(buffer, 1, size, f) != size) 53 | Die("fread failed"); 54 | fclose(f); 55 | if (length) *length = size; 56 | return buffer; 57 | } 58 | 59 | char *NextLineStripComments(char **s) { 60 | char *p = *s; 61 | if (p == NULL) 62 | return NULL; 63 | // find end of line 64 | char *eol = strchr(p, '\n'); 65 | *s = eol ? eol + 1 : NULL; 66 | eol = eol ? eol : p + strlen(p); 67 | // strip comments 68 | char *comment = (char*)memchr(p, '#', eol - p); 69 | eol = comment ? comment : eol; 70 | // strip trailing whitespace 71 | while (eol > p && (eol[-1] == '\r' || eol[-1] == ' ' || eol[-1] == '\t')) 72 | eol--; 73 | *eol = 0; 74 | // strip leading whitespace 75 | while (p[0] == ' ' || p[0] == '\t') 76 | p++; 77 | return p; 78 | } 79 | 80 | // Return the next possibly quoted string, space separated, or the empty string 81 | char *NextPossiblyQuotedString(char **s) { 82 | char *r = *s, *t; 83 | while (*r == ' ' || *r == '\t') 84 | r++; 85 | if (*r == '"') { 86 | for (t = ++r; *t && *t != '"'; t++); 87 | } else { 88 | for (t = r; *t && *t != ' ' && *t != '\t'; t++); 89 | } 90 | if (*t) *t++ = 0; 91 | while (*t == ' ' || *t == '\t') 92 | t++; 93 | *s = t; 94 | return r; 95 | } 96 | 97 | char *ReplaceFilenameWithNewPath(const char *old_path, const char *new_path) { 98 | size_t olen = strlen(old_path); 99 | size_t nlen = strlen(new_path) + 1; 100 | while (olen && old_path[olen - 1] != '/' && old_path[olen - 1] != '\\') 101 | olen--; 102 | char *result = (char *)malloc(olen + nlen); 103 | memcpy(result, old_path, olen); 104 | memcpy(result + olen, new_path, nlen); 105 | return result; 106 | } 107 | 108 | char *SplitKeyValue(char *p) { 109 | char *equals = strchr(p, '='); 110 | if (equals == NULL) 111 | return NULL; 112 | char *kr = equals; 113 | while (kr > p && (kr[-1] == ' ' || kr[-1] == '\t')) 114 | kr--; 115 | *kr = 0; 116 | char *v = equals + 1; 117 | while (v[0] == ' ' || v[0] == '\t') 118 | v++; 119 | return v; 120 | } 121 | 122 | const char *SkipPrefix(const char *big, const char *little) { 123 | for (; *little; big++, little++) { 124 | if (*little != *big) 125 | return NULL; 126 | } 127 | return big; 128 | } 129 | 130 | void StrSet(char **rv, const char *s) { 131 | char *news = strdup(s); 132 | char *old = *rv; 133 | *rv = news; 134 | free(old); 135 | } 136 | 137 | char *StrFmt(const char *fmt, ...) { 138 | char buf[4096]; 139 | va_list va; 140 | va_start(va, fmt); 141 | int n = vsnprintf(buf, sizeof(buf), fmt, va); 142 | if (n < 0 || n >= sizeof(buf)) Die("vsnprintf failed"); 143 | va_end(va); 144 | return strdup(buf); 145 | } 146 | 147 | void ByteArray_Resize(ByteArray *arr, size_t new_size) { 148 | arr->size = new_size; 149 | if (new_size > arr->capacity) { 150 | size_t minsize = arr->capacity + (arr->capacity >> 1) + 8; 151 | arr->capacity = new_size < minsize ? minsize : new_size; 152 | uint8 *data = (uint8*)realloc(arr->data, arr->capacity); 153 | if (!data) Die("memory allocation failed"); 154 | arr->data = data; 155 | } 156 | } 157 | 158 | void ByteArray_Destroy(ByteArray *arr) { 159 | void *data = arr->data; 160 | arr->data = NULL; 161 | free(data); 162 | } 163 | 164 | void ByteArray_AppendData(ByteArray *arr, const uint8 *data, size_t data_size) { 165 | ByteArray_Resize(arr, arr->size + data_size); 166 | memcpy(arr->data + arr->size - data_size, data, data_size); 167 | } 168 | 169 | void ByteArray_AppendByte(ByteArray *arr, uint8 v) { 170 | ByteArray_Resize(arr, arr->size + 1); 171 | arr->data[arr->size - 1] = v; 172 | } 173 | 174 | MemBlk FindIndexInMemblk(MemBlk data, size_t i) { 175 | size_t end = data.size, left_off, right_off; 176 | if (end < 2) 177 | return (MemBlk) { 0, 0 }; 178 | 179 | uint16 f = *(uint16 *)(data.ptr + (end -= 2)); 180 | size_t mx = f & 0xfff; 181 | if (f & 0x4000) { 182 | size_t next_mx = *(uint16 *)(data.ptr + (end -= 2)); 183 | if (next_mx >= 256) { 184 | if (i > mx || mx * 2 > end) 185 | return (MemBlk) { 0, 0 }; 186 | end -= mx * 2 + 2; 187 | i = *(uint16 *)(data.ptr + end + i * 2); 188 | } else { 189 | if (i > mx || mx > end) 190 | return (MemBlk) { 0, 0 }; 191 | end -= mx + 1; 192 | i = *(uint8 *)(data.ptr + end + i); 193 | } 194 | mx = next_mx; 195 | } 196 | if (f & 0x8000) { 197 | if (i > mx || mx * 2 > end) 198 | return (MemBlk) { 0, 0 }; 199 | left_off = ((i == 0) ? mx * 2 : mx * 2 + *(uint16 *)(data.ptr + i * 2 - 2)); 200 | right_off = (i == mx) ? end : mx * 2 + *(uint16 *)(data.ptr + i * 2); 201 | } else { 202 | if (i > mx || mx * 4 > end) 203 | return (MemBlk) { 0, 0 }; 204 | left_off = ((i == 0) ? mx * 4 : mx * 4 + *(uint32 *)(data.ptr + i * 4 - 4)); 205 | right_off = (i == mx) ? end : mx * 4 + *(uint32 *)(data.ptr + i * 4); 206 | } 207 | if (left_off > right_off || right_off > end) 208 | return (MemBlk) { 0, 0 }; 209 | return (MemBlk) { data.ptr + left_off, right_off - left_off }; 210 | } 211 | 212 | const uint8 *FindAddrInMemblk(MemBlk data, uint32 addr) { 213 | if (data.size < 2) 214 | return 0; // sanity check 215 | // std::lower_bound 216 | int count = *(uint16 *)data.ptr, count_org = count; 217 | if (data.size < 2 + count * 6) 218 | return 0; // sanity check 219 | const uint8 *first = data.ptr + 2; 220 | while (count) { 221 | const uint8 *it = first; 222 | int step = count >> 1; 223 | it += step * 3; 224 | if ((*(uint32 *)it & 0xffffff) < addr) { 225 | first = it + 3; 226 | count -= step + 1; 227 | } else { 228 | count = step; 229 | } 230 | } 231 | if (first >= data.ptr + 2 + count_org * 3 || (*(uint32 *)first & 0xffffff) != addr) 232 | first -= 3; 233 | uint32 src_off = *(uint32 *)first & 0xffffff; 234 | uint32 offset = *(uint32 *)(first + count_org * 3) & 0xffffff; 235 | offset += addr - src_off; 236 | if (offset >= data.size) 237 | return 0; 238 | return data.ptr + offset; 239 | } 240 | 241 | static uint64 BpsDecodeInt(const uint8 **src) { 242 | uint64 data = 0, shift = 1; 243 | while(true) { 244 | uint8 x = *(*src)++; 245 | data += (x & 0x7f) * shift; 246 | if(x & 0x80) break; 247 | shift <<= 7; 248 | data += shift; 249 | } 250 | return data; 251 | } 252 | 253 | #define CRC32_POLYNOMIAL 0xEDB88320 254 | 255 | static uint32 crc32(const void *data, size_t length) { 256 | uint32 crc = 0xFFFFFFFF; 257 | const uint8 *byteData = (const uint8 *)data; 258 | for (size_t i = 0; i < length; i++) { 259 | crc ^= byteData[i]; 260 | for (int j = 0; j < 8; j++) 261 | crc = (crc >> 1) ^ ((crc & 1) * CRC32_POLYNOMIAL); 262 | } 263 | return crc ^ 0xFFFFFFFF; 264 | } 265 | 266 | 267 | uint8 *ApplyBps(const uint8 *src, size_t src_size_in, 268 | const uint8 *bps, size_t bps_size, size_t *length_out) { 269 | const uint8 *bps_end = bps + bps_size - 12; 270 | 271 | if (memcmp(bps, "BPS1", 4)) 272 | return NULL; 273 | if (crc32(src, src_size_in) != *(uint32 *)(bps_end)) 274 | return NULL; 275 | if (crc32(bps, bps_size - 4) != *(uint32 *)(bps_end + 8)) 276 | return NULL; 277 | 278 | bps += 4; 279 | uint32 src_size = BpsDecodeInt(&bps); 280 | uint32 dst_size = BpsDecodeInt(&bps); 281 | uint32 meta_size = BpsDecodeInt(&bps); 282 | uint32 outputOffset = 0; 283 | uint32 sourceRelativeOffset = 0; 284 | uint32 targetRelativeOffset = 0; 285 | if (src_size != src_size_in) 286 | return NULL; 287 | *length_out = dst_size; 288 | uint8 *dst = malloc(dst_size); 289 | if (!dst) 290 | return NULL; 291 | while (bps < bps_end) { 292 | uint32 cmd = BpsDecodeInt(&bps); 293 | uint32 length = (cmd >> 2) + 1; 294 | switch (cmd & 3) { 295 | case 0: 296 | while(length--) { 297 | dst[outputOffset] = src[outputOffset]; 298 | outputOffset++; 299 | } 300 | break; 301 | case 1: 302 | while (length--) 303 | dst[outputOffset++] = *bps++; 304 | break; 305 | case 2: 306 | cmd = BpsDecodeInt(&bps); 307 | sourceRelativeOffset += (cmd & 1 ? -1 : +1) * (cmd >> 1); 308 | while (length--) 309 | dst[outputOffset++] = src[sourceRelativeOffset++]; 310 | break; 311 | default: 312 | cmd = BpsDecodeInt(&bps); 313 | targetRelativeOffset += (cmd & 1 ? -1 : +1) * (cmd >> 1); 314 | while(length--) 315 | dst[outputOffset++] = dst[targetRelativeOffset++]; 316 | break; 317 | } 318 | } 319 | if (dst_size != outputOffset) 320 | return NULL; 321 | if (crc32(dst, dst_size) != *(uint32 *)(bps_end + 4)) 322 | return NULL; 323 | return dst; 324 | } -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef ZELDA3_UTIL_H_ 2 | #define ZELDA3_UTIL_H_ 3 | 4 | #include "types.h" 5 | 6 | typedef struct SDL_Window SDL_Window; 7 | 8 | struct RendererFuncs { 9 | bool (*Initialize)(SDL_Window *window); 10 | void (*Destroy)(); 11 | void (*BeginDraw)(int width, int height, uint8 **pixels, int *pitch); 12 | void (*EndDraw)(); 13 | }; 14 | 15 | 16 | typedef struct ByteArray { 17 | uint8 *data; 18 | size_t size, capacity; 19 | } ByteArray; 20 | 21 | void ByteArray_Resize(ByteArray *arr, size_t new_size); 22 | void ByteArray_Destroy(ByteArray *arr); 23 | void ByteArray_AppendData(ByteArray *arr, const uint8 *data, size_t data_size); 24 | void ByteArray_AppendByte(ByteArray *arr, uint8 v); 25 | 26 | uint8 *ReadWholeFile(const char *name, size_t *length); 27 | char *NextDelim(char **s, int sep); 28 | char *NextLineStripComments(char **s); 29 | char *NextPossiblyQuotedString(char **s); 30 | char *SplitKeyValue(char *p); 31 | bool StringEqualsNoCase(const char *a, const char *b); 32 | const char *StringStartsWithNoCase(const char *a, const char *b); 33 | bool ParseBool(const char *value, bool *result); 34 | const char *SkipPrefix(const char *big, const char *little); 35 | void StrSet(char **rv, const char *s); 36 | char *StrFmt(const char *fmt, ...); 37 | char *ReplaceFilenameWithNewPath(const char *old_path, const char *new_path); 38 | uint8 *ApplyBps(const uint8 *src, size_t src_size_in, 39 | const uint8 *bps, size_t bps_size, size_t *length_out); 40 | 41 | #endif // ZELDA3_UTIL_H_ -------------------------------------------------------------------------------- /third_party/.gitignore: -------------------------------------------------------------------------------- 1 | /tcc/ 2 | /SDL2-2.*/ 3 | /gl_core/*.o --------------------------------------------------------------------------------