diff --git a/Makefile b/Makefile index 9edf6b8b..6767bf89 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ + # Makefile to rebuild SM64 split image ### Default target ### @@ -22,16 +23,32 @@ TARGET_N64 = 0 # Build and optimize for Raspberry Pi(s) TARGET_RPI ?= 0 -# Compiler to use (ido or gcc) -COMPILER ?= ido + +# Makeflag to enable OSX fixes +OSX_BUILD ?= 0 # Disable better camera by default BETTERCAMERA ?= 0 +# Disable no drawing distance by default +NODRAWINGDISTANCE ?= 0 +# Disable texture fixes by default (helps with them purists) +TEXTURE_FIX ?= 0 +# Enable extended options menu by default +EXT_OPTIONS_MENU ?= 1 # Build for Emscripten/WebGL TARGET_WEB ?= 0 # Specify the target you are building for, 0 means native TARGET_ARCH ?= native + +ifeq ($(CROSS),i686-w64-mingw32.static-) + TARGET_ARCH = i386pe +else ifeq ($(CROSS),x86_64-w64-mingw32.static-) + TARGET_ARCH = i386pe +else + TARGET_ARCH = native +endif + TARGET_BITS ?= 0 ifneq ($(TARGET_BITS),0) @@ -41,8 +58,6 @@ else endif # Automatic settings for PC port(s) -# WINDOWS_BUILD IS NOT FOR COMPILING A WINDOWS EXECUTABLE UNDER LINUX OR WSL! -# USE THE WIKI GUIDE WITH MSYS2 FOR COMPILING A WINDOWS EXECUTABLE! NON_MATCHING := 1 GRUCODE := f3dex2e @@ -137,6 +152,10 @@ ifeq ($(TARGET_RPI),1) # Define RPi to change SDL2 title & GLES2 hints VERSION_CFLAGS += -DUSE_GLES endif +ifeq ($(OSX_BUILD),1) # Modify GFX & SDL2 for OSX GL + VERSION_CFLAGS += -DOSX_BUILD +endif + VERSION_ASFLAGS := --defsym AVOID_UB=1 COMPARE := 0 @@ -163,7 +182,7 @@ endif endif # Make tools if out of date -DUMMY != make -s -C tools >&2 || echo FAIL +DUMMY != make -C tools >&2 || echo FAIL ifeq ($(DUMMY),FAIL) $(error Failed to build tools) endif @@ -224,10 +243,6 @@ GODDARD_SRC_DIRS := src/goddard src/goddard/dynlists MIPSISET := -mips2 MIPSBIT := -32 -ifeq ($(COMPILER),gcc) - MIPSISET := -mips3 -endif - ifeq ($(VERSION),eu) OPT_FLAGS := -O2 else @@ -246,9 +261,8 @@ ifeq ($(TARGET_WEB),1) endif # Use a default opt flag for gcc, then override if RPi -ifeq ($(COMPILER),gcc) -OPT_FLAGS := -O2 # Breaks sound on x86? -endif + +# OPT_FLAGS := -O2 # "Whole-compile optimization flag" Breaks sound on x86. ifeq ($(TARGET_RPI),1) machine = $(shell sh -c 'uname -m 2>/dev/null || echo unknown') @@ -411,6 +425,10 @@ ENDIAN_BITWIDTH := $(BUILD_DIR)/endian-and-bitwidth AS := as +ifeq ($(OSX_BUILD),1) +AS := i686-w64-mingw32-as +endif + ifneq ($(TARGET_WEB),1) # As in, not-web PC port CC := $(CROSS)gcc CXX := $(CROSS)g++ @@ -419,14 +437,33 @@ else endif ifeq ($(WINDOWS_BUILD),1) - LD := $(CXX) + ifeq ($(CROSS),i686-w64-mingw32.static-) # fixes compilation in MXE on Linux and WSL + LD := $(CC) + else ifeq ($(CROSS),x86_64-w64-mingw32.static-) + LD := $(CC) + else + LD := $(CXX) + endif else LD := $(CC) endif -CPP := $(CROSS)cpp -P -OBJDUMP := $(CROSS)objdump -OBJCOPY := $(CROSS)objcopy +ifeq ($(WINDOWS_BUILD),1) # fixes compilation in MXE on Linux and WSL + CPP := cpp -P + OBJCOPY := objcopy + OBJDUMP := $(CROSS)objdump +else +ifeq ($(OSX_BUILD),1) + CPP := cpp-9 -P + OBJDUMP := i686-w64-mingw32-objdump + OBJCOPY := i686-w64-mingw32-objcopy +else # Linux & other builds + CPP := $(CROSS)cpp -P + OBJCOPY := $(CROSS)objcopy + OBJDUMP := $(CROSS)objdump +endif +endif + PYTHON := python3 SDLCONFIG := $(CROSS)sdl2-config @@ -444,34 +481,55 @@ CC_CHECK := $(CC) -fsyntax-only -fsigned-char $(INCLUDE_CFLAGS) -Wall -Wextra -W CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv `$(SDLCONFIG) --cflags` endif -# Check for better camera option +# Check for enhancement options + +# Check for Puppycam option ifeq ($(BETTERCAMERA),1) -CC_CHECK += -DBETTERCAMERA -CFLAGS += -DBETTERCAMERA + CC_CHECK += -DBETTERCAMERA + CFLAGS += -DBETTERCAMERA + EXT_OPTIONS_MENU := 1 +endif + +# Check for no drawing distance option +ifeq ($(NODRAWINGDISTANCE),1) + CC_CHECK += -DNODRAWINGDISTANCE + CFLAGS += -DNODRAWINGDISTANCE +endif + +# Check for texture fix option +ifeq ($(TEXTURE_FIX),1) + CC_CHECK += -DTEXTURE_FIX + CFLAGS += -DTEXTURE_FIX +endif + +# Check for extended options menu option +ifeq ($(EXT_OPTIONS_MENU),1) + CC_CHECK += -DEXT_OPTIONS_MENU + CFLAGS += -DEXT_OPTIONS_MENU endif ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS) ifeq ($(TARGET_WEB),1) LDFLAGS := -lm -lGL -lSDL2 -no-pie -s TOTAL_MEMORY=20MB -g4 --source-map-base http://localhost:8080/ -s "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']" -else - -ifeq ($(WINDOWS_BUILD),1) -LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -lpthread -lglew32 `$(SDLCONFIG) --static-libs` -lm -lglu32 -lsetupapi -ldinput8 -luser32 -lgdi32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion -luuid -lopengl32 -no-pie -static -ifeq ($(WINDOWS_CONSOLE),1) -LDFLAGS += -mconsole -endif -else - +else ifeq ($(WINDOWS_BUILD),1) + LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -lpthread -lglew32 `$(SDLCONFIG) --static-libs` -lm -lglu32 -lsetupapi -ldinput8 -luser32 -lgdi32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion -luuid -lopengl32 -static + ifeq ($(CROSS),) + LDFLAGS += -no-pie + endif + ifeq ($(WINDOWS_CONSOLE),1) + LDFLAGS += -mconsole + endif +else ifeq ($(TARGET_RPI),1) # Linux / Other builds below -ifeq ($(TARGET_RPI),1) LDFLAGS := $(OPT_FLAGS) -lm -lGLESv2 `$(SDLCONFIG) --libs` -no-pie else +ifeq ($(OSX_BUILD),1) +LDFLAGS := -lm -framework OpenGL `$(SDLCONFIG) --libs` -no-pie -lpthread `pkg-config --libs libusb-1.0 glfw3 glew` +else LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm -lGL `$(SDLCONFIG) --libs` -no-pie -lpthread endif -endif -endif #Added for Pi ifeq - +endif # End of LDFLAGS # Prevent a crash with -sopt export LANG := C @@ -580,11 +638,13 @@ ifeq ($(VERSION),eu) $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o +$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o O_FILES += $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o else $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h +$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h endif ################################################################ @@ -680,7 +740,6 @@ $(BUILD_DIR)/assets/mario_anim_data.c: $(wildcard assets/anims/*.inc.c) $(BUILD_DIR)/assets/demo_data.c: assets/demo_data.json $(wildcard assets/demos/*.bin) $(PYTHON) tools/demo_data_converter.py assets/demo_data.json $(VERSION_CFLAGS) > $@ -ifeq ($(COMPILER),ido) # Source code $(BUILD_DIR)/levels/%/leveldata.o: OPT_FLAGS := -g $(BUILD_DIR)/actors/%.o: OPT_FLAGS := -g @@ -722,8 +781,6 @@ $(BUILD_DIR)/src/audio/%.acpp: src/audio/%.c $(BUILD_DIR)/src/audio/%.copt: $(BUILD_DIR)/src/audio/%.acpp $(QEMU_IRIX) -silent -L $(IRIX_ROOT) $(IRIX_ROOT)/usr/lib/copt -signed -I=$< -CMP=$@ -cp=i -scalaroptimize=1 endif -endif - # Rebuild files with 'GLOBAL_ASM' if the NON_MATCHING flag changes. $(GLOBAL_ASM_O_FILES): $(GLOBAL_ASM_DEP).$(NON_MATCHING) diff --git a/README.md b/README.md index fc8e1320..1ba69773 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,12 @@ OpenGL adaptation of [n64decomp/sm64](https://github.com/n64decomp/sm64). Feel free to report bugs and contribute, but remember, there must be **no upload of any copyrighted asset**. -Run `./extract-assets.py --clean && make clean` or `make distclean` to remove ROM-originated content. This port has been made possible thanks to [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) by [Emill](https://github.com/Emill). +Run `./extract_assets.py --clean && make clean` or `make distclean` to remove ROM-originated content. This port has been made possible mostly thanks to [Emill](https://github.com/Emill) and his [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) renderer. + + +Please contribute **first** to the [nightly branch](https://github.com/sm64pc/sm64pc/tree/nightly/). New functionality will be merged to master once they're considered to be well-tested. + +*Read this in other languages: [Español](README_es_ES.md) [简体中文](README_zh_CN.md).* ## Features @@ -10,8 +15,11 @@ Run `./extract-assets.py --clean && make clean` or `make distclean` to remove RO * Variable aspect ratio and resolution. The game can now correctly render at basically any window size. * Native xinput controller support. On Linux, DualShock 4 has been confirmed to work plug-and-play. * Analog camera control and mouse look. (Activate with `make BETTERCAMERA=1`.) + * An option to disable drawing distances. (Activate with `make NODRAWINGDISTANCE=1`.) + * In-game control binding, currently available on the `testing` branch. + * Skip introductory Peach & Lakitu cutscenes with the `--skip-intro` CLI option ## Building For building instructions, please refer to the [wiki](https://github.com/sm64pc/sm64pc/wiki). -**Do NOT attempt to compile Windows executables with `WINDOWS_BUILD=1` under Linux or WSL. It will NOT work. Follow the guide on the wiki.** +**Make sure you have MXE first before attempting to compile for Windows on Linux and WSL. Follow the guide on the wiki.** diff --git a/README_es_ES.md b/README_es_ES.md new file mode 100644 index 00000000..7426486c --- /dev/null +++ b/README_es_ES.md @@ -0,0 +1,204 @@ +# sm64pc +Adaptación a OpenGL de [n64decomp/sm64](https://github.com/n64decomp/sm64). + +No dudes en contribuir o reportar bugs, pero recuerda: **no se debe subir nada con copyright**. +Ejecuta `./extract_assets.py --clean && make clean` o `make distclean` para borrar todo el contenido proveniente de la ROM. Este port es posible gracias a [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) creado por [Emill](https://github.com/Emill). + +## Funcionalidades + + * Renderizado nativo. Podrás jugar a Super Mario 64 sin necesidad de un emulador. + * Resolución y relación de aspecto variables. Puedes jugar a Super Mario 64 a básicamente cualquier resolución o tamaño de ventana. + * Soporte nativo para mandos XInput. En Linux, se ha confirmado que el DualShock 4 funciona sin más. + * Cámara analógica y cámara controlada con el ratón. (Se activa con `make BETTERCAMERA=1`.) + * Opción para desactivar el límite de distancia de renderizado. (Se activa con `make NODRAWINGDISTANCE=1`.) + * Configurar los controles desde el juego, actualmente solo en la rama `testing`. + * Posibilidad de saltarte la intro con la opción de línea de comandos `--skip-intro`, actualmente solo en las ramas `testing` y `skip-intro`. + +## Compilar en Windows +**No intentes compilar ejecutables para Windows bajo Linux usando `WINDOWS_BUILD=1`. No va a funcionar. Sigue la guía.** +#### 1. Instalación y configuración de MSYS2. + +1. Descarga [msys2-x86_64-latest.exe](http://repo.msys2.org/distrib/msys2-x86_64-latest.exe) y ejecútalo. Si tu sistema operativo es de 32 bits (¿por qué?) descarga [msys2-i686-latest.exe](http://repo.msys2.org/distrib/msys2-i686-latest.exe) en su lugar. Asegúrate de que lo instalas en `C:\dev\msys64` (o `C:\dev\msys32` para 32 bits...). Ejecuta MSYS2. + +2. En la ventana de comandos de MSYS2, ejecuta el siguiente comando: + ``` + pacman -Syuu + ``` +3. Abre "MSYS2 MinGW 64-Bit". Ejecuta este comando __repetidamente__ hasta que MSYS diga que ya no hay más actualizaciones. Es posible que tengas que volver a cerrar y abrir MSYS2. + + ``` + pacman -Syuu + ``` + +5. Ejecuta este comando y cuando te pida confirmación pulsa intro: + + ``` + pacman -S --needed base-devel mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain \ + git subversion mercurial \ + mingw-w64-i686-cmake mingw-w64-x86_64-cmake + ``` +6. Listo. +#### Instala las dependencias +``` +pacman -S mingw-w64-i686-glew mingw-w64-x86_64-glew mingw-w64-i686-SDL2 mingw-w64-x86_64-SDL2 python3 +``` +### Crea el directorio en el que preparar todo +Desde el explorador de Windows, navega a `C:\msys64\home\(nombre de usuario)\` y crea una carpeta con el nombre que te apetezca. Aquí es donde vamos a preparar todo. +### Clona el repositorio +En MSYS2, introduce el siguiente comando: +``` +git clone https://github.com/sm64pc/sm64pc/ +``` +(Si no funciona, prueba a escribirlo manualmente, en lugar de copiar y pegar) +#### Copia la ROM base al directorio correspondiente +El paso anterior tiene que haber creado una carpeta llamada sm64pc. Dentro de esa carpeta, y para cada version de la ROM (jp/us/eu) de la cual quieras compilar un ejecutable, coloca la ROM con el nombre `baserom..z64` para extraer sus assets. Por ejemplo, `baserom.us.z64` para la versión americana, o `baserom.eu.z64` para la versión europea. + +#### En MSYS2, vamos a navegar a la carpeta `./tools/audiofile-0.3.6/` y ejecutar el `autoreconf-i`. Introduce los siguientes comandos, en orden, uno a uno: +``` +cd sm64pc/tools/audiofile-0.3.6/ +autoreconf -i +``` +No te vayas de este directorio hasta el paso 9. + +#### Ejecuta el script `configure` +``` +PATH=/mingw64/bin:/mingw32/bin:$PATH LIBS=-lstdc++ ./configure --disable-docs +``` +#### Ejecuta el script `make` +``` +PATH=/mingw64/bin:/mingw32/bin:$PATH make +``` +#### Crea un directorio `lib` en `tools/` +``` +mkdir ../lib +``` + +#### Acabas de compilar `libaudiofile`. Ahora cópialo a `tools/lib/` +``` +cp libaudiofile/.libs/libaudiofile.a ../lib/ +cp libaudiofile/.libs/libaudiofile.la ../lib/ +``` +#### Ahora toca hacer algo desde Windows. +En el explorador de Windows, ve a sm64pc\tools y edita el archivo Makefile desde un editor de texto (es recomendable usar un editor decente como Notepad++ o Sublime Text, en lugar del bloc de notas, para asegurarte de que no rompes el formato del texto) Busca la línea que contiene esto: + +```tabledesign_CFLAGS := -Wno-uninitialized -laudiofile``` + +Y añade ` -lstdc++` al final, de manera que quede así (¡no olvides el espacio!) + +```tabledesign_CFLAGS := -Wno-uninitialized -laudiofile -lstdc++``` + +Guarda el archivo. +#### Vuelve a la carpeta tools y ejecuta `make` con los siguientes comandos. +``` +cd .. +PATH=/mingw64/bin:/mingw32/bin:$PATH make +``` +#### Vuelve al directorio sm64pc +``` +cd .. +``` +#### Finalmente, ejecuta ```make``` de nuevo. + +(Ten en cuenta que mingw32 y mingw64 han sido intercambiados. Esto es para que puedas compilar la versión de 32 bits si quieres.) + +Aquí pones las opciones que quieras según la versión que quieras compilar. Por ejemplo, si quieres activar la cámara analógica, añade al final BETTERCAMERA=1. Si quieres la opción de distancia de renderizado ilimitada, añade NODRAWINGDISTANCE=1. + +Por ejemplo: +``` +PATH=/mingw32/bin:/mingw64/bin:$PATH make BETTERCAMERA=1 NODRAWINGDISTANCE=1 +``` +Listo. El .exe estará en sm64pc\build\. Disfruta. +## Compilar en Linux + +### Nota para usuarios de Windows +No intentes compilar un ejecutable para Windows desde Linux o WSL. No funciona. Sigue la guía para Windows. + +#### Copia la(s) ROM(s) base para la extracción de assets. + +Por cada versión de la cual quieras compilar un ejecutable, copia la ROM en `./baserom..z64` para extraer los assets. + +#### Instala las dependencias. + +Para compilar necesitas las sigueintes dependencias. + * python3 >= 3.6 + * libsdl2-dev + * [audiofile](https://audiofile.68k.org/) + * libglew-dev + * git + +Puedes instalarlas con este comando: + +##### Debian / Ubuntu - (compilando para 32 bits) +``` +sudo apt install build-essential git python3 libaudiofile-dev libglew-dev:i386 libsdl2-dev:i386 +``` +##### Debian / Ubuntu - (compilando para 64 bits) +``` +sudo apt install build-essential git python3 libaudiofile-dev libglew-dev libsdl2-dev +``` +##### Arch Linux +Hay un paquete AUR (cortesía de @narukeh) disponible bajo el nombre [sm64pc-git](https://aur.archlinux.org/packages/sm64pc-git/). Instálalo con tu gestor de AURs preferido. + +Si quieres compilarlo por tu cuenta: +``` +sudo pacman -S base-devel python audiofile sdl2 glew +``` + +##### Void Linux - (compilando para 64 bits) +``` +sudo xbps-install -S base-devel python3 audiofile-devel SDL2-devel glew-devel +``` + +##### Void Linux - (compilando para 32 bits) +``` +sudo xbps-install -S base-devel python3 audiofile-devel-32bit SDL2-devel-32bit glew-devel-32bit +``` + +#### Compila el ejecutable. + +Ejecuta `make` para compilar (por defecto `VERSION=us`) + +``` +make VERSION=jp -j6 # Compila la versión (J) usando 6 hilos +make VERSION=us MARCH=i686 TARGET_BITS=32 # Compila un ejecutable de la versión (U) de 32 bits +make TARGET_RPI=1 # Compila un ejecutable para Raspberry Pi +``` +## Compilar para la web +Puedes compilar el juego para navegadores que admitan WebGL usando [Emscripten](https://github.com/emscripten-core). Para hacerlo, instala [emsdk](https://github.com/emscripten-core/emsdk) y ejecuta `make TARGET_WEB=1`. + +## Script para compilar para Raspberry Pi + +[Hyenadae](https://github.com/Hyenadae/) ha creado un script que ayuda a compilar el juego para Raspberry Pi. Estos son los pasos que hace el script: + + * Instala las dependencias; + * Cambia VC4_DRM en la RPi de 0 a 3; + * Cambia ajustes en la memoria de las RPis 0 y 1 para que se pueda completar la compilación; + * Permite la instalación de un SDL2 con KMS, lo que elimina la necesidad de usar X11 y garantiza el máximo rendimiento de cualquier RPi que ejecute VC4; + * Clona sm64pc si no encuentra los archivos necesarios; + * Comprueba si existen los assets y la ROM base necesaria (baserom.*.z64); + * Compila sm64pc. + +El script está incluído en la rama master, pero también puede descargarse [aquí](https://raw.githubusercontent.com/sm64pc/sm64pc/master/pisetup.sh). +# Problemas conocidos +### Problemas ya conocidos: + * La versión EU tiene bugs en los textos y no tiene audio. + * El movimiento analógico horizontal de la cámara vuelve al estilo antiguo en el nivel Bowser in the Dark World (#72) + * La cámara con el ratón falla cuando disparas a Mario hacia un árbol o un tubo. (#71) + * "make: Nothing to be done for 'default'" al compilar para web. (#67) + +### Estos problemas están marcados como solucionados. Por favor, contacta si sigues teniendo estos problemas. + * El juego se llena de flags aleatorias en las builds de 64 bits para Windows + * Hazy Maze Cave se cuelga en pantalla completa (#57) + * La pantalla de título no tiene el cursor para manipular a Mario en pantalla completa. (#28) + +## Parches +En la carpeta `./enhancements` hay varios archivos `patch`, que pueden aplicarse de la siguiente manera: + +``` + git apply fps.patch --ignore-whitespace --reject +``` +Si ocurre un rechazo, puedes buscarlo con el comando `find | grep .rej`. +Intenta resolver los rechazos a través de [wiggle](https://github.com/neilbrown/wiggle). +``` +wiggle rejection.rej --replace +``` diff --git a/README_zh_CN.md b/README_zh_CN.md new file mode 100644 index 00000000..b3e90f92 --- /dev/null +++ b/README_zh_CN.md @@ -0,0 +1,21 @@ +# sm64pc +本项目是 [n64decomp/sm64](https://github.com/n64decomp/sm64) 的 OpenGL 移植版本。 + +我们欢迎贡献代码与 bug 报告,但请切记,**不得上传任何被版权保护(来自 ROM 文件)的资源**。 +提交前请运行 `./extract_assets.py --clean && make clean` 或 `make distclean` 来清除所有从 ROM 文件中提取的内容。 +本移植是基于 [Emill](https://github.com/Emill) 的工作 [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) 才得以实现的。 + +## 主要功能 + + * 原生渲染。现在不用任何模拟器就可以运行 马力欧64 了。 + * 长宽比和分辨率可以自由改变。本游戏目前可以在几乎任何窗口尺寸下正确渲染。 + * 原生 xinput 手柄支持。在 Linux 下,已经确认 PS4 手柄可以即插即用。 + * 支持模拟量视点控制、鼠标控制视点。(请使用 `make BETTERCAMERA=1` 编译) + * 可取消可视距离限制。(请使用 `make NODRAWINGDISTANCE=1` 编译) + * 游戏内操作设定功能,目前在 `testing` 分支下可用。 + * 使用 `--skip-intro` 命令行选项跳过碧奇公主与 Lakitu 的片头剧情。目前在 `testing` 及 `skip-intro` 分支下可用。 + +## 编译方法 +关于如何编译,请参考 [wiki](https://github.com/sm64pc/sm64pc/wiki)。 + +**请勿在 Linux 或者 WSL 下使用 `WINDOWS_BUILD=1` 参数尝试编译 Windows 版本,这样无法编译成功。请参考 Wiki。 diff --git a/actors/burn_smoke/model.inc.c b/actors/burn_smoke/model.inc.c index a0f65578..bcf4fd6f 100644 --- a/actors/burn_smoke/model.inc.c +++ b/actors/burn_smoke/model.inc.c @@ -11,6 +11,9 @@ static const Vtx burn_smoke_seg4_vertex_040217C0[] = { // //! Wrong texture format. Called as rgba16, which makes the burn smoke appear // as a transparent black burn smoke. Probably meant to show up as white-ish // burn smoke, but mistakened for being intended as black smoke. +// Due to debate in the Koopa shorts PR surrounding the fix to a similar bug, +// said fix is on a compile-time variable. Use TEXTURE_FIX=1 at compile time +// to fix this. // 0x04021800 ALIGNED8 static const u8 burn_smoke_seg4_texture_04021800[] = { #include "actors/burn_smoke/burn_smoke.ia16.inc.c" @@ -44,7 +47,11 @@ const Gfx burn_smoke_seg4_dl_04022048[] = { // 0x04022070 - 0x040220C8 const Gfx burn_smoke_seg4_dl_04022070[] = { gsSPDisplayList(burn_smoke_seg4_dl_04022000), + #ifdef TEXTURE_FIX + gsDPLoadTextureBlock(burn_smoke_seg4_texture_04021800, G_IM_FMT_IA, G_IM_SIZ_16b, 32, 32, 0, G_TX_CLAMP, G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD), + #else gsDPLoadTextureBlock(burn_smoke_seg4_texture_04021800, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, 0, G_TX_CLAMP, G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD), + #endif gsSPDisplayList(burn_smoke_seg4_dl_04022028), gsSPDisplayList(burn_smoke_seg4_dl_04022048), gsSPEndDisplayList(), diff --git a/actors/koopa/model.inc.c b/actors/koopa/model.inc.c index 04a43a8a..6bf24187 100644 --- a/actors/koopa/model.inc.c +++ b/actors/koopa/model.inc.c @@ -54,6 +54,8 @@ static const Lights1 koopa_seg6_lights_06002630 = gdSPDefLights1( // beneath its shell, despite the fact it was intended to be white like // the rest of its body. This is evident because once the mistake is corrected // it turns back to being white like the other polygons. +// Due to debate in the PR surrounding the fix to this, said fix is on +// a compile-time variable. Use TEXTURE_FIX=1 at compile time to fix this. // 0x06002648 ALIGNED8 static const u8 koopa_seg6_texture_06002648[] = { #include "actors/koopa/koopa_shell_front.rgba16.inc.c" @@ -2077,8 +2079,13 @@ const Gfx koopa_seg6_dl_0600C498[] = { gsSPVertex(koopa_seg6_vertex_0600B560, 9, 0), gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 0x0), gsSP1Triangle( 6, 7, 8, 0x0), + #ifdef TEXTURE_FIX + gsSPLight(&koopa_seg6_lights_06002630.l, 1), + gsSPLight(&koopa_seg6_lights_06002630.a, 2), + #else gsSPLight(koopa_seg6_texture_06002648 + 0x20, 1), // this malformed light results in a gsSPLight(koopa_seg6_texture_06002648 + 0x18, 2), // koopa appearing to wear pink shorts. + #endif gsSPVertex(koopa_seg6_vertex_0600B5F0, 15, 0), gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 0x0), gsSP2Triangles( 6, 7, 0, 0x0, 8, 5, 9, 0x0), diff --git a/bin/segment2.c b/bin/segment2.c index d4199d9c..d7e807fd 100644 --- a/bin/segment2.c +++ b/bin/segment2.c @@ -2499,41 +2499,23 @@ static const Lights1 segment2_lights_unused = gdSPDefLights1( // 0x02014470 - 0x020144B0 static const Mtx matrix_identity = { -#ifdef TARGET_N64 - {{0x00010000, 0x00000000, - 0x00000001, 0x00000000}, - {0x00000000, 0x00010000, - 0x00000000, 0x00000001}, - {0x00000000, 0x00000000, - 0x00000000, 0x00000000}, - {0x00000000, 0x00000000, - 0x00000000, 0x00000000}} -#else + {{1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}} -#endif + }; // 0x020144B0 - 0x020144F0 static const Mtx matrix_fullscreen = { -#if TARGET_N64 - {{0x00000000, 0x00000000, - 0x00000000, 0x00000000}, - {0x00000000, 0xffff0000, - 0xffffffff, 0xffff0001}, - {((65536 * 2 / SCREEN_WIDTH) << 16) | 0, 0x00000000, - (0 << 16) | (65536 * 2 / SCREEN_HEIGHT), 0x00000000}, - {0x00000000, 0x00000000, - 0x00000000, 0x00000000}} -#else + {{2.0f / SCREEN_WIDTH, 0.0f, 0.0f, 0.0f}, {0.0f, 2.0f / SCREEN_HEIGHT, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, -1.0f, 1.0f}} -#endif + }; diff --git a/c2obj.py b/c2obj.py new file mode 100644 index 00000000..508a51ec --- /dev/null +++ b/c2obj.py @@ -0,0 +1,141 @@ +""" +This module attempts to parse the ``model.inc.c`` files and extract the +3D models within as standard Wavefront OBJ files. + +Example: + Specify the path to the ``.inc.c`` file and a directory where to save + the extracted ``.obj`` files. + + $ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/ + +This is a work in progress and it currently has some serious limitations: + * It only extracts geometry information, so no textures or any other info + * It makes assumptions about the layout of the code in the C source + * It hasn't been properly tested. + +""" + +def parse(filename, output_directory): + from os import path, mkdir + + if not path.isdir(output_directory): + try: + mkdir(output_directory) + except OSError: + print(f'Could not use output directory {output_directory}.') + + vtx_def = 'static const Vtx ' + vtx_data = {} + reading_vtx = False + current_vtx_name = '' + current_vtx_data = [] + current_vtx_vertices = 0 + + gfx_def = 'const Gfx ' + reading_gfx = False + current_gfx_vertices = 0 + current_gfx_faces = 0 + insert_vert_call = 'gsSPVertex(' + insert_1tri_call = 'gsSP1Triangle(' + insert_2tri_call = 'gsSP2Triangles(' + gfx_count = 0 + + end_of_block = '};' + + with open(filename, 'r') as f: + for line in f: + line = line.strip() + + if line.startswith(vtx_def): + vtx_name = line.split(' ')[3][:-2] + current_vtx_name = vtx_name + current_vtx_data = [] + reading_vtx = True + continue + + if line.startswith(gfx_def): + from datetime import datetime + + current_gfx_name = line.split(' ')[2][:-2] + current_gfx_file = open(path.join(output_directory, current_gfx_name + '.obj'), 'w') + current_gfx_file.write("# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n") + current_gfx_file.write('# File Created: {}\n\n'.format(datetime.now())) + reading_gfx = True + continue + + if line == end_of_block: + if reading_vtx: + vtx_data[current_vtx_name] = current_vtx_data + reading_vtx = False + + elif reading_gfx: + current_gfx_file.write(f'# {current_gfx_faces} faces\n\n') + current_gfx_file.close() + current_gfx_vertices = 0 + reading_gfx = False + gfx_count += 1 + + continue + + if reading_vtx: + line = line.replace('{', '[').replace('}', ']') + tri = eval(line[:-1])[0] + current_vtx_data.append(tri) + continue + + if reading_gfx: + if line.startswith(insert_vert_call): + args = line[len(insert_vert_call):].split(',') + current_vtx_name = args[0] + + if current_gfx_vertices > 0: + current_gfx_file.write(f'# {current_gfx_faces} faces\n\n') + + current_gfx_faces = 0 + current_vtx_vertices = len(vtx_data[current_vtx_name]) + current_gfx_vertices += current_vtx_vertices + + current_gfx_file.write(f'#\n# object {current_vtx_name}\n#\n\n') + current_vtx_data = vtx_data[current_vtx_name] + for tri in current_vtx_data: + v = tri[0] + current_gfx_file.write('v {:.3f} {:.3f} {:.3f}\n'.format(*v)) + current_gfx_file.write(f'# {current_vtx_vertices} vertices\n\n') + + for tri in current_vtx_data: + n = [_decode_normal(u) for u in tri[3][:3]] + current_gfx_file.write('vn {:.3f} {:.3f} {:.3f}\n'.format(*n)) + current_gfx_file.write(f'# {current_vtx_vertices} vertex normals\n\n') + + current_gfx_file.write(f'g {current_vtx_name}\n\n') + + elif line.startswith(insert_2tri_call): + args = line[len(insert_2tri_call):].split(',') + correction = current_gfx_vertices - current_vtx_vertices + 1 + indexes = [eval(args[i]) + correction for i in [0, 1, 2, 4, 5, 6]] + current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[:3])) + current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[3:])) + current_gfx_faces += 2 + + elif line.startswith(insert_1tri_call): + args = line[len(insert_1tri_call):].split(',') + correction = current_gfx_vertices - current_vtx_vertices + 1 + indexes = [eval(args[i]) + correction for i in [0, 1, 2]] + current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes)) + current_gfx_faces += 1 + + continue + + print(f'{gfx_count} models extracted.') + +def _decode_normal(x): + y = x if x <= 127 else x - 255 + return y / 127 + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('filename', help = 'filename of the .inc.c source file') + parser.add_argument('output_directory', help = 'directory where to put the extracted .obj files') + args = parser.parse_args() + parse(args.filename, args.output_directory) \ No newline at end of file diff --git a/data/behavior_data.c b/data/behavior_data.c index 13212d3b..3b4af5a0 100644 --- a/data/behavior_data.c +++ b/data/behavior_data.c @@ -114,7 +114,7 @@ // Often used to end behavior scripts that do not contain an infinite loop. #define BREAK() \ BC_B(0x0A) - + // Exits the behavior script, unused. #define BREAK_UNUSED() \ BC_B(0x0B) @@ -175,15 +175,15 @@ #define ADD_INT_RAND_RSHIFT(field, min, rshift) \ BC_BBH(0x17, field, min), \ BC_H(rshift) - + // No operation. Unused. #define CMD_NOP_1(field) \ BC_BB(0x18, field) - + // No operation. Unused. #define CMD_NOP_2(field) \ BC_BB(0x19, field) - + // No operation. Unused. #define CMD_NOP_3(field) \ BC_BB(0x1A, field) @@ -219,6 +219,9 @@ #define BILLBOARD() \ BC_B(0x21) +#define CYLBOARD() \ + BC_B(0x38) + // Hides the current object. #define HIDE() \ BC_B(0x22) @@ -3180,7 +3183,7 @@ const BehaviorScript bhvFloorTrapInCastle[] = { const BehaviorScript bhvTree[] = { BEGIN(OBJ_LIST_POLELIKE), - BILLBOARD(), + CYLBOARD(), OR_INT(oFlags, OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE), SET_INT(oInteractType, INTERACT_POLE), SET_HITBOX(/*Radius*/ 80, /*Height*/ 500), @@ -6105,5 +6108,3 @@ const BehaviorScript bhvIntroScene[] = { CALL_NATIVE(bhv_intro_scene_loop), END_LOOP(), }; - - diff --git a/enhancements/no_draw_distance/README.md b/enhancements/no_draw_distance/README.md deleted file mode 100644 index dd890383..00000000 --- a/enhancements/no_draw_distance/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# No Draw Distances - -This is a work-in-progress by [wabberz](https://github.com/wabberz) that disables the drawing distance for most objects and enemies. - -**This will crash some levels in the 32-bit version**. - -[Related Push Request](https://github.com/sm64pc/sm64pc/pull/75). - -For instructions on how to apply patches, please refer to [the Wiki](https://github.com/sm64pc/sm64pc/wiki/Patches). - diff --git a/enhancements/no_draw_distance/nodrawdistance.patch b/enhancements/no_draw_distance/nodrawdistance.patch deleted file mode 100644 index b986b1a6..00000000 --- a/enhancements/no_draw_distance/nodrawdistance.patch +++ /dev/null @@ -1,406 +0,0 @@ -From c98a263cf40520bf0d131eb2d1a2f90240787c98 Mon Sep 17 00:00:00 2001 -From: uwabami -Date: Tue, 12 May 2020 09:26:16 +0200 -Subject: [PATCH] adding option to disable draw distance - ---- - Makefile | 8 ++++++++ - src/engine/behavior_script.c | 4 ++++ - src/engine/surface_load.c | 4 ++++ - src/game/behaviors/butterfly.inc.c | 3 ++- - src/game/behaviors/chain_chomp.inc.c | 8 ++++++++ - src/game/behaviors/coin.inc.c | 6 ++++++ - src/game/behaviors/fish.inc.c | 4 ++++ - src/game/behaviors/goomba.inc.c | 4 ++++ - src/game/behaviors/heave_ho.inc.c | 4 ++++ - src/game/behaviors/king_bobomb.inc.c | 4 ++++ - src/game/behaviors/pokey.inc.c | 6 ++++++ - src/game/behaviors/snufit.inc.c | 4 ++++ - src/game/behaviors/triplet_butterfly.inc.c | 4 ++++ - src/game/behaviors/water_bomb_cannon.inc.c | 8 ++++++++ - src/game/behaviors/whirlpool.inc.c | 4 ++++ - 15 files changed, 74 insertions(+), 1 deletion(-) - -diff --git a/Makefile b/Makefile -index d4bd284..efeb63e 100644 ---- a/Makefile -+++ b/Makefile -@@ -27,6 +27,8 @@ COMPILER ?= ido - - # Disable better camera by default - BETTERCAMERA ?= 0 -+# Disable no drawing distance by default -+NODRAWINGDISTANCE ?= 0 - - # Build for Emscripten/WebGL - TARGET_WEB ?= 0 -@@ -449,6 +451,12 @@ CC_CHECK += -DBETTERCAMERA - CFLAGS += -DBETTERCAMERA - endif - -+# Check for no drawing distance option -+ifeq ($(NODRAWINGDISTANCE),1) -+CC_CHECK += -DNODRAWINGDISTANCE -+CFLAGS += -DNODRAWINGDISTANCE -+endif -+ - ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS) - - ifeq ($(TARGET_WEB),1) -diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c -index edd5247..feb6fef 100644 ---- a/src/engine/behavior_script.c -+++ b/src/engine/behavior_script.c -@@ -987,11 +987,15 @@ void cur_obj_update(void) { - } else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) { - if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) { - // If the object has a render distance, check if it should be shown. -+#ifndef NODRAWINGDISTANCE - if (distanceFromMario > gCurrentObject->oDrawingDistance) { - // Out of render distance, hide the object. - gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; - } else if (gCurrentObject->oHeldState == HELD_FREE) { -+#else -+ if (distanceFromMario <= gCurrentObject->oDrawingDistance && gCurrentObject->oHeldState == HELD_FREE) { -+#endif - // In render distance (and not being held), show the object. - gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; - gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; -diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c -index 363f9af..498fae0 100644 ---- a/src/engine/surface_load.c -+++ b/src/engine/surface_load.c -@@ -789,9 +789,13 @@ void load_object_collision_model(void) { - } - } - -+#ifndef NODRAWINGDISTANCE - if (marioDist < gCurrentObject->oDrawingDistance) { -+#endif - gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; -+#ifndef NODRAWINGDISTANCE - } else { - gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - } -+#endif - } -diff --git a/src/game/behaviors/butterfly.inc.c b/src/game/behaviors/butterfly.inc.c -index d435d8d..9296ed5 100644 ---- a/src/game/behaviors/butterfly.inc.c -+++ b/src/game/behaviors/butterfly.inc.c -@@ -107,6 +107,7 @@ void bhv_butterfly_loop(void) { - butterfly_act_return_home(); - break; - } -- -+#ifndef NODRAWINGDISTANCE - set_object_visibility(o, 3000); -+#endif - } -diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c -index a77c5d5..9b9c342 100644 ---- a/src/game/behaviors/chain_chomp.inc.c -+++ b/src/game/behaviors/chain_chomp.inc.c -@@ -53,7 +53,9 @@ static void chain_chomp_act_uninitialized(void) { - struct ChainSegment *segments; - s32 i; - -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 3000.0f) { -+#endif - segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); - if (segments != NULL) { - // Each segment represents the offset of a chain part to the pivot. -@@ -81,7 +83,9 @@ static void chain_chomp_act_uninitialized(void) { - cur_obj_unhide(); - } - } -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - /** -@@ -359,10 +363,12 @@ static void chain_chomp_act_move(void) { - f32 maxDistToPivot; - - // Unload chain if mario is far enough -+#ifndef NODRAWINGDISTANCE - if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) { - o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN; - o->oForwardVel = o->oVelY = 0.0f; - } else { -+#endif - cur_obj_update_floor_and_walls(); - - switch (o->oChainChompReleaseStatus) { -@@ -446,7 +452,9 @@ static void chain_chomp_act_move(void) { - o->oGravity = -4.0f; - o->oChainChompTargetPitch = -0x3000; - } -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - /** -diff --git a/src/game/behaviors/coin.inc.c b/src/game/behaviors/coin.inc.c -index 913c583..05619b9 100644 ---- a/src/game/behaviors/coin.inc.c -+++ b/src/game/behaviors/coin.inc.c -@@ -184,17 +184,23 @@ void bhv_coin_formation_loop(void) { - s32 bitIndex; - switch (o->oAction) { - case 0: -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 2000.0f) { -+#endif - for (bitIndex = 0; bitIndex < 8; bitIndex++) { - if (!(o->oCoinUnkF4 & (1 << bitIndex))) - spawn_coin_in_formation(bitIndex, o->oBehParams2ndByte); - } - o->oAction++; -+#ifndef NODRAWINGDISTANCE - } -+#endif - break; - case 1: -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario > 2100.0f) - o->oAction++; -+#endif - break; - case 2: - o->oAction = 0; -diff --git a/src/game/behaviors/fish.inc.c b/src/game/behaviors/fish.inc.c -index 839ab8d..f652ef4 100644 ---- a/src/game/behaviors/fish.inc.c -+++ b/src/game/behaviors/fish.inc.c -@@ -42,7 +42,9 @@ void fish_act_spawn(void) { - * If the current level is Secret Aquarium, ignore this requirement. - * Fish moves at random with a max-range of 700.0f. - */ -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) { -+#endif - for (i = 0; i < schoolQuantity; i++) { - fishObject = spawn_object(o, model, bhvFish); - fishObject->oBehParams2ndByte = o->oBehParams2ndByte; -@@ -50,7 +52,9 @@ void fish_act_spawn(void) { - obj_translate_xyz_random(fishObject, 700.0f); - } - o->oAction = FISH_ACT_ACTIVE; -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - /** -diff --git a/src/game/behaviors/goomba.inc.c b/src/game/behaviors/goomba.inc.c -index 2dab2fe..bf47dda 100644 ---- a/src/game/behaviors/goomba.inc.c -+++ b/src/game/behaviors/goomba.inc.c -@@ -78,7 +78,9 @@ void bhv_goomba_triplet_spawner_update(void) { - // If mario is close enough and the goombas aren't currently loaded, then - // spawn them - if (o->oAction == GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 3000.0f) { -+#endif - // The spawner is capable of spawning more than 3 goombas, but this - // is not used in the game - dAngle = -@@ -98,11 +100,13 @@ void bhv_goomba_triplet_spawner_update(void) { - } - - o->oAction += 1; -+#ifndef NODRAWINGDISTANCE - } - } else if (o->oDistanceToMario > 4000.0f) { - // If mario is too far away, enter the unloaded action. The goombas - // will detect this and unload themselves - o->oAction = GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED; -+#endif - } - } - -diff --git a/src/game/behaviors/heave_ho.inc.c b/src/game/behaviors/heave_ho.inc.c -index 662bb0b..2f9da86 100644 ---- a/src/game/behaviors/heave_ho.inc.c -+++ b/src/game/behaviors/heave_ho.inc.c -@@ -73,14 +73,18 @@ void heave_ho_act_3(void) { - - void heave_ho_act_0(void) { - cur_obj_set_pos_to_home(); -+#ifndef NODRAWINGDISTANCE - if (find_water_level(o->oPosX, o->oPosZ) < o->oPosY && o->oDistanceToMario < 4000.0f) { -+#endif - cur_obj_become_tangible(); - cur_obj_unhide(); - o->oAction = 1; -+#ifndef NODRAWINGDISTANCE - } else { - cur_obj_become_intangible(); - cur_obj_hide(); - } -+#endif - } - - void (*sHeaveHoActions[])(void) = { heave_ho_act_0, heave_ho_act_1, heave_ho_act_2, heave_ho_act_3 }; -diff --git a/src/game/behaviors/king_bobomb.inc.c b/src/game/behaviors/king_bobomb.inc.c -index 63a7575..af1cb0a 100644 ---- a/src/game/behaviors/king_bobomb.inc.c -+++ b/src/game/behaviors/king_bobomb.inc.c -@@ -295,10 +295,14 @@ void king_bobomb_move(void) { - cur_obj_move_using_fvel_and_gravity(); - cur_obj_call_action_function(sKingBobombActions); - exec_anim_sound_state(sKingBobombSoundStates); -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 5000.0f) -+#endif - cur_obj_enable_rendering(); -+#ifndef NODRAWINGDISTANCE - else - cur_obj_disable_rendering(); -+#endif - } - - void bhv_king_bobomb_loop(void) { -diff --git a/src/game/behaviors/pokey.inc.c b/src/game/behaviors/pokey.inc.c -index df5d11f..cfcc92c 100644 ---- a/src/game/behaviors/pokey.inc.c -+++ b/src/game/behaviors/pokey.inc.c -@@ -151,7 +151,9 @@ static void pokey_act_uninitialized(void) { - s32 i; - s16 partModel; - -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 2000.0f) { -+#endif - partModel = MODEL_POKEY_HEAD; - - for (i = 0; i < 5; i++) { -@@ -170,7 +172,9 @@ static void pokey_act_uninitialized(void) { - o->oPokeyNumAliveBodyParts = 5; - o->oPokeyBottomBodyPartSize = 1.0f; - o->oAction = POKEY_ACT_WANDER; -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - /** -@@ -185,9 +189,11 @@ static void pokey_act_wander(void) { - - if (o->oPokeyNumAliveBodyParts == 0) { - obj_mark_for_deletion(o); -+#ifndef NODRAWINGDISTANCE - } else if (o->oDistanceToMario > 2500.0f) { - o->oAction = POKEY_ACT_UNLOAD_PARTS; - o->oForwardVel = 0.0f; -+#endif - } else { - treat_far_home_as_mario(1000.0f); - cur_obj_update_floor_and_walls(); -diff --git a/src/game/behaviors/snufit.inc.c b/src/game/behaviors/snufit.inc.c -index f3a0c9e..76e78c0 100644 ---- a/src/game/behaviors/snufit.inc.c -+++ b/src/game/behaviors/snufit.inc.c -@@ -180,7 +180,11 @@ void bhv_snufit_loop(void) { - void bhv_snufit_balls_loop(void) { - // If far from Mario or in a different room, despawn. - if ((o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) -+#ifndef NODRAWINGDISTANCE - || (o->oTimer != 0 && o->oDistanceToMario > 1500.0f)) { -+#else -+ || (o->oTimer != 0)) { -+#endif - obj_mark_for_deletion(o); - } - -diff --git a/src/game/behaviors/triplet_butterfly.inc.c b/src/game/behaviors/triplet_butterfly.inc.c -index 1c2b926..3d16a9d 100644 ---- a/src/game/behaviors/triplet_butterfly.inc.c -+++ b/src/game/behaviors/triplet_butterfly.inc.c -@@ -54,9 +54,11 @@ static void triplet_butterfly_act_init(void) { - } - - static void triplet_butterfly_act_wander(void) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario > 1500.0f) { - obj_mark_for_deletion(o); - } else { -+#endif - approach_f32_ptr(&o->oTripletButterflySpeed, 8.0f, 0.5f); - if (o->oTimer < 60) { - o->oTripletButterflyTargetYaw = cur_obj_angle_to_home(); -@@ -82,7 +84,9 @@ static void triplet_butterfly_act_wander(void) { - - obj_move_pitch_approach(o->oTripletButterflyTargetPitch, 400); - cur_obj_rotate_yaw_toward(o->oTripletButterflyTargetYaw, random_linear_offset(400, 800)); -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - static void triplet_butterfly_act_activate(void) { -diff --git a/src/game/behaviors/water_bomb_cannon.inc.c b/src/game/behaviors/water_bomb_cannon.inc.c -index 8e9ba33..fb82e43 100644 ---- a/src/game/behaviors/water_bomb_cannon.inc.c -+++ b/src/game/behaviors/water_bomb_cannon.inc.c -@@ -38,19 +38,27 @@ void bhv_bubble_cannon_barrel_loop(void) { - } - - void water_bomb_cannon_act_0(void) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 2000.0f) { -+#endif - spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles); - cur_obj_unhide(); - - o->oAction = 1; - o->oMoveAnglePitch = o->oWaterCannonUnkFC = 0x1C00; -+#ifndef NODRAWINGDISTANCE - } -+#endif - } - - void water_bomb_cannon_act_1(void) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario > 2500.0f) { - o->oAction = 2; - } else if (o->oBehParams2ndByte == 0) { -+#else -+ if (o->oBehParams2ndByte == 0) { -+#endif - if (o->oWaterCannonUnkF4 != 0) { - o->oWaterCannonUnkF4 -= 1; - } else { -diff --git a/src/game/behaviors/whirlpool.inc.c b/src/game/behaviors/whirlpool.inc.c -index 405e051..5aebebd 100644 ---- a/src/game/behaviors/whirlpool.inc.c -+++ b/src/game/behaviors/whirlpool.inc.c -@@ -35,7 +35,9 @@ void whirpool_orient_graph(void) { - } - - void bhv_whirlpool_loop(void) { -+#ifndef NODRAWINGDISTANCE - if (o->oDistanceToMario < 5000.0f) { -+#endif - o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; - - // not sure if actually an array -@@ -52,10 +54,12 @@ void bhv_whirlpool_loop(void) { - whirpool_orient_graph(); - - o->oFaceAngleYaw += 0x1F40; -+#ifndef NODRAWINGDISTANCE - } else { - o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; - gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT] = 0; - } -+#endif - - cur_obj_play_sound_1(SOUND_ENV_WATER); - diff --git a/include/PR/gbi.h b/include/PR/gbi.h index 4a7a5de0..0f63e04f 100644 --- a/include/PR/gbi.h +++ b/include/PR/gbi.h @@ -1109,11 +1109,7 @@ * Vertex (set up for use with colors) */ typedef struct { -#ifdef TARGET_N64 - short ob[3]; /* x, y, z */ -#else float ob[3]; /* x, y, z */ -#endif unsigned short flag; short tc[2]; /* texture coord */ unsigned char cn[4]; /* color & alpha */ @@ -1123,11 +1119,7 @@ typedef struct { * Vertex (set up for use with normals) */ typedef struct { -#ifdef TARGET_N64 - short ob[3]; /* x, y, z */ -#else float ob[3]; /* x, y, z */ -#endif unsigned short flag; short tc[2]; /* texture coord */ signed char n[3]; /* normal */ @@ -1176,23 +1168,9 @@ typedef struct { unsigned char v[3]; } Tri; -#ifdef TARGET_N64 -/* - * 4x4 matrix, fixed point s15.16 format. - * First 8 words are integer portion of the 4x4 matrix - * Last 8 words are the fraction portion of the 4x4 matrix - */ -typedef s32 Mtx_t[4][4]; - -typedef union { - Mtx_t m; - long long int force_structure_alignment; -} Mtx; -#else typedef struct { float m[4][4]; } Mtx; -#endif /* * Viewport diff --git a/include/PR/os_libc.h b/include/PR/os_libc.h index 94111c0b..e7567516 100644 --- a/include/PR/os_libc.h +++ b/include/PR/os_libc.h @@ -3,8 +3,28 @@ #include "ultratypes.h" -// Old deprecated functions from strings.h, replaced by memcpy/memset. +// old bstring functions that aren't present on some platforms + +#if defined(__APPLE__) + +// macOS libc has them +#include + +#elif defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) + +// there's no way that shit's defined, use memcpy/memset +#include +#undef bzero +#undef bcopy +#define bzero(buf, len) memset((buf), 0, (len)) +#define bcopy(src, dst, len) memcpy((dst), (src), (len)) + +#else + +// hope for the best extern void bcopy(const void *, void *, size_t); extern void bzero(void *, size_t); +#endif + #endif /* !_OS_LIBC_H_ */ diff --git a/include/PR/ultratypes.h b/include/PR/ultratypes.h index 62f14f31..ddff2827 100644 --- a/include/PR/ultratypes.h +++ b/include/PR/ultratypes.h @@ -29,13 +29,6 @@ typedef volatile s64 vs64; typedef float f32; typedef double f64; -#ifdef TARGET_N64 -typedef u32 size_t; -typedef s32 ssize_t; -typedef u32 uintptr_t; -typedef s32 intptr_t; -typedef s32 ptrdiff_t; -#else #include #include #if defined(__MINGW32__) @@ -47,5 +40,3 @@ typedef ptrdiff_t ssize_t; #endif #endif #endif - -#endif diff --git a/include/config.h b/include/config.h index df5cc54e..23a317e8 100644 --- a/include/config.h +++ b/include/config.h @@ -1,4 +1,5 @@ #ifndef CONFIG_H + #define CONFIG_H /** @@ -27,16 +28,7 @@ #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 240 -// Border Height Define for NTSC Versions -#ifdef TARGET_N64 -#ifndef VERSION_EU -#define BORDER_HEIGHT 8 -#else -#define BORDER_HEIGHT 1 -#endif -#else -// What's the point of having a border? -#define BORDER_HEIGHT 0 -#endif +// What's the point of having a border if we're not an N64? +#define BORDER_HEIGHT 0 // Never use a border as not-N64 #endif diff --git a/include/gfx_dimensions.h b/include/gfx_dimensions.h index 5dc8f6b8..f3a5ee23 100644 --- a/include/gfx_dimensions.h +++ b/include/gfx_dimensions.h @@ -1,7 +1,6 @@ #ifndef GFX_DIMENSIONS_H #define GFX_DIMENSIONS_H -#ifndef TARGET_N64 #include #include "pc/gfx/gfx_pc.h" #define GFX_DIMENSIONS_FROM_LEFT_EDGE(v) (SCREEN_WIDTH / 2 - SCREEN_HEIGHT / 2 * gfx_current_dimensions.aspect_ratio + (v)) @@ -9,12 +8,5 @@ #define GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(v) ((int)floorf(GFX_DIMENSIONS_FROM_LEFT_EDGE(v))) #define GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(v) ((int)ceilf(GFX_DIMENSIONS_FROM_RIGHT_EDGE(v))) #define GFX_DIMENSIONS_ASPECT_RATIO (gfx_current_dimensions.aspect_ratio) -#else -#define GFX_DIMENSIONS_FROM_LEFT_EDGE(v) (v) -#define GFX_DIMENSIONS_FROM_RIGHT_EDGE(v) (SCREEN_WIDTH - (v)) -#define GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(v) (v) -#define GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(v) (SCREEN_WIDTH - (v)) -#define GFX_DIMENSIONS_ASPECT_RATIO (4.0f / 3.0f) -#endif #endif diff --git a/include/macros.h b/include/macros.h index 8d6182c9..bcace7fb 100644 --- a/include/macros.h +++ b/include/macros.h @@ -46,20 +46,9 @@ #define ALIGNED16 #endif -#ifdef TARGET_N64 -// convert a virtual address to physical. -#define VIRTUAL_TO_PHYSICAL(addr) ((uintptr_t)(addr) & 0x1FFFFFFF) - -// convert a physical address to virtual. -#define PHYSICAL_TO_VIRTUAL(addr) ((uintptr_t)(addr) | 0x80000000) - -// another way of converting virtual to physical -#define VIRTUAL_TO_PHYSICAL2(addr) ((u8 *)(addr) - 0x80000000U) -#else // no conversion for pc port other than cast #define VIRTUAL_TO_PHYSICAL(addr) ((uintptr_t)(addr)) #define PHYSICAL_TO_VIRTUAL(addr) ((uintptr_t)(addr)) #define VIRTUAL_TO_PHYSICAL2(addr) ((void *)(addr)) -#endif #endif diff --git a/include/make_const_nonconst.h b/include/make_const_nonconst.h index 6336ec34..249f73ae 100644 --- a/include/make_const_nonconst.h +++ b/include/make_const_nonconst.h @@ -1,11 +1,4 @@ #ifndef MAKE_CONST_NONCONST_H #define MAKE_CONST_NONCONST_H -#ifdef TARGET_N64 -// IDO sometimes puts const variables in .rodata and sometimes in .data, which breaks ordering. -// This makes sure all variables are put into the same section (.data). We need to do this for -// both IDO and gcc for TARGET_N64. -#define const -#endif - #endif diff --git a/include/platform_info.h b/include/platform_info.h index ee35d4f0..2197833a 100644 --- a/include/platform_info.h +++ b/include/platform_info.h @@ -1,14 +1,9 @@ #ifndef PLATFORM_INFO_H #define PLATFORM_INFO_H -#ifdef TARGET_N64 -#define IS_64_BIT 0 -#define IS_BIG_ENDIAN 1 -#else #include #define IS_64_BIT (UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFU) #define IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) -#endif #define DOUBLE_SIZE_ON_64_BIT(size) ((size) * (sizeof(void *) / 4)) diff --git a/include/segment_symbols.h b/include/segment_symbols.h index 77adc158..26d65cde 100644 --- a/include/segment_symbols.h +++ b/include/segment_symbols.h @@ -1,15 +1,9 @@ #ifndef SEGMENT_SYMBOLS_H #define SEGMENT_SYMBOLS_H -#ifdef TARGET_N64 -#define DECLARE_SEGMENT(name) \ - extern u8 _##name##SegmentRomStart[]; \ - extern u8 _##name##SegmentRomEnd[]; -#else #define DECLARE_SEGMENT(name) \ static u8 _##name##SegmentRomStart[1]; \ static u8 _##name##SegmentRomEnd[1]; -#endif #define DECLARE_ACTOR_SEGMENT(name) \ DECLARE_SEGMENT(name##_mio0) \ @@ -43,11 +37,7 @@ DECLARE_ACTOR_SEGMENT(group17) DECLARE_SEGMENT(behavior) DECLARE_SEGMENT(scripts) DECLARE_SEGMENT(goddard) -#ifdef TARGET_N64 -extern u8 _goddardSegmentStart[]; -#else static u8 _goddardSegmentStart[1]; -#endif DECLARE_LEVEL_SEGMENT(menu) DECLARE_LEVEL_SEGMENT(intro) diff --git a/include/text_strings.h.in b/include/text_strings.h.in index 030f3959..3246699e 100644 --- a/include/text_strings.h.in +++ b/include/text_strings.h.in @@ -3,20 +3,47 @@ #include "text_menu_strings.h" -#define NC_CAMX _("Camera X Sensitivity") -#define NC_CAMY _("Camera Y Sensitivity") -#define NC_INVERTX _("Invert X Axis") -#define NC_INVERTY _("Invert Y Axis") -#define NC_CAMC _("Camera Centre Aggression") -#define NC_CAMP _("Camera Pan Level") -#define NC_ENABLED _("Enabled") -#define NC_DISABLED _("Disabled") -#define NC_BUTTON _("[R]: Options") -#define NC_BUTTON2 _("[R]: Return") -#define NC_OPTION _("OPTIONS") -#define NC_HIGHLIGHT _("O") -#define NC_ANALOGUE _("Analogue Camera") -#define NC_MOUSE _("Mouse Look") +#define TEXT_OPT_CAMX _("Camera X Sensitivity") +#define TEXT_OPT_CAMY _("Camera Y Sensitivity") +#define TEXT_OPT_INVERTX _("Invert X Axis") +#define TEXT_OPT_INVERTY _("Invert Y Axis") +#define TEXT_OPT_CAMC _("Camera Centre Aggression") +#define TEXT_OPT_CAMP _("Camera Pan Level") +#define TEXT_OPT_CAMD _("Camera Deceleration") +#define TEXT_OPT_ENABLED _("Enabled") +#define TEXT_OPT_DISABLED _("Disabled") +#define TEXT_OPT_BUTTON1 _("[R]: Options") +#define TEXT_OPT_BUTTON2 _("[R]: Return") +#define TEXT_OPT_OPTIONS _("OPTIONS") +#define TEXT_OPT_CAMERA _("CAMERA") +#define TEXT_OPT_CONTROLS _("CONTROLS") +#define TEXT_OPT_VIDEO _("DISPLAY") +#define TEXT_OPT_AUDIO _("SOUND") +#define TEXT_OPT_HIGHLIGHT _("O") +#define TEXT_OPT_ANALOGUE _("Analogue Camera") +#define TEXT_OPT_MOUSE _("Mouse Look") +#define TEXT_OPT_TEXFILTER _("Texture Filtering") +#define TEXT_OPT_FSCREEN _("Fullscreen") +#define TEXT_OPT_NEAREST _("Nearest") +#define TEXT_OPT_LINEAR _("Linear") +#define TEXT_OPT_MVOLUME _("Master Volume") + +#define TEXT_OPT_UNBOUND _("NONE") +#define TEXT_OPT_PRESSKEY _("...") +#define TEXT_BIND_A _("A Button") +#define TEXT_BIND_B _("B Button") +#define TEXT_BIND_START _("Start Button") +#define TEXT_BIND_L _("L Trigger") +#define TEXT_BIND_R _("R Trigger") +#define TEXT_BIND_Z _("Z Trigger") +#define TEXT_BIND_C_UP _("C-Up") +#define TEXT_BIND_C_DOWN _("C-Down") +#define TEXT_BIND_C_LEFT _("C-Left") +#define TEXT_BIND_C_RIGHT _("C-Right") +#define TEXT_BIND_UP _("Stick Up") +#define TEXT_BIND_DOWN _("Stick Down") +#define TEXT_BIND_LEFT _("Stick Left") +#define TEXT_BIND_RIGHT _("Stick Right") /** * Global Symbols diff --git a/levels/menu/script.c b/levels/menu/script.c index 5a9d3e44..4e955c12 100644 --- a/levels/menu/script.c +++ b/levels/menu/script.c @@ -72,19 +72,16 @@ const LevelScript level_main_menu_entry_2[] = { /*25*/ FREE_LEVEL_POOL(), /*26*/ LOAD_AREA(/*area*/ 2), -#ifndef TARGET_N64 + // sVisibleStars is set to 0 during FIXED_LOAD above on N64, but not on PC-port. // lvl_init_act_selector_values_and_stars must be called here otherwise the // previous value is retained and causes incorrect drawing during the 16 transition // frames. CALL(/*arg*/ 0, /*func*/ lvl_init_act_selector_values_and_stars), -#endif + /*27*/ TRANSITION(/*transType*/ WARP_TRANSITION_FADE_FROM_COLOR, /*time*/ 16, /*color*/ 0xFF, 0xFF, 0xFF), /*29*/ SLEEP(/*frames*/ 16), /*30*/ SET_MENU_MUSIC(/*seq*/ 0x000D), -#ifdef TARGET_N64 - /*31*/ CALL(/*arg*/ 0, /*func*/ lvl_init_act_selector_values_and_stars), -#endif /*33*/ CALL_LOOP(/*arg*/ 0, /*func*/ lvl_update_obj_and_load_act_button_actions), /*35*/ GET_OR_SET(/*op*/ OP_SET, /*var*/ VAR_CURR_ACT_NUM), /*36*/ STOP_MUSIC(/*fadeOutTime*/ 0x00BE), diff --git a/lib/src/guMtxF2L.c b/lib/src/guMtxF2L.c index e08528ec..9cd7359c 100644 --- a/lib/src/guMtxF2L.c +++ b/lib/src/guMtxF2L.c @@ -1,49 +1,9 @@ #include "libultra_internal.h" -#ifndef TARGET_N64 #include -#endif -#ifdef TARGET_N64 -void guMtxF2L(float mf[4][4], Mtx *m) { - int r, c; - s32 tmp1; - s32 tmp2; - s32 *m1 = &m->m[0][0]; - s32 *m2 = &m->m[2][0]; - for (r = 0; r < 4; r++) { - for (c = 0; c < 2; c++) { - tmp1 = mf[r][2 * c] * 65536.0f; - tmp2 = mf[r][2 * c + 1] * 65536.0f; - *m1++ = (tmp1 & 0xffff0000) | ((tmp2 >> 0x10) & 0xffff); - *m2++ = ((tmp1 << 0x10) & 0xffff0000) | (tmp2 & 0xffff); - } - } -} -void guMtxL2F(float mf[4][4], Mtx *m) { - int r, c; - u32 tmp1; - u32 tmp2; - u32 *m1; - u32 *m2; - s32 stmp1, stmp2; - m1 = (u32 *) &m->m[0][0]; - m2 = (u32 *) &m->m[2][0]; - for (r = 0; r < 4; r++) { - for (c = 0; c < 2; c++) { - tmp1 = (*m1 & 0xffff0000) | ((*m2 >> 0x10) & 0xffff); - tmp2 = ((*m1++ << 0x10) & 0xffff0000) | (*m2++ & 0xffff); - stmp1 = *(s32 *) &tmp1; - stmp2 = *(s32 *) &tmp2; - mf[r][c * 2 + 0] = stmp1 / 65536.0f; - mf[r][c * 2 + 1] = stmp2 / 65536.0f; - } - } -} -#else void guMtxF2L(float mf[4][4], Mtx *m) { memcpy(m, mf, sizeof(Mtx)); } -#endif void guMtxIdentF(float mf[4][4]) { int r, c; @@ -58,11 +18,8 @@ void guMtxIdentF(float mf[4][4]) { } } void guMtxIdent(Mtx *m) { -#ifdef TARGET_N64 - float mf[4][4]; - guMtxIdentF(mf); - guMtxF2L(mf, m); -#else + guMtxIdentF(m->m); -#endif + } + diff --git a/lib/src/ldiv.c b/lib/src/ldiv.c index 01b04be2..b7c28efa 100644 --- a/lib/src/ldiv.c +++ b/lib/src/ldiv.c @@ -1,6 +1,7 @@ #include "libultra_internal.h" #include +#ifndef OSX_BUILD lldiv_t lldiv(long long num, long long denom) { lldiv_t ret; @@ -13,6 +14,7 @@ lldiv_t lldiv(long long num, long long denom) { return ret; } +#endif // OSX_BUILD cannot use this ldiv_t ldiv(long num, long denom) { ldiv_t ret; diff --git a/lib/src/math/cosf.c b/lib/src/math/cosf.c index 2b01c12a..f1130b44 100644 --- a/lib/src/math/cosf.c +++ b/lib/src/math/cosf.c @@ -33,6 +33,7 @@ static const du pilo = { static const fu zero = {0.0}; extern const fu NAN; +#ifndef OSX_BUILD float cosf(float x) { double dx; // double x @@ -92,3 +93,5 @@ float cosf(float x) return zero.f; } +#endif // OSX_BUILD cannot use this + diff --git a/obj2c.py b/obj2c.py new file mode 100644 index 00000000..015aa628 --- /dev/null +++ b/obj2c.py @@ -0,0 +1,107 @@ +""" +This module generates a fragment of C code, in the style of that found in +the ``model.inc.c`` files, that encodes the geometry of the model specified +by the Wavefront OBJ file. + +Example: + Specify the path to the ``.obj`` file and pipe the output of the script + into the desired destination ``.c`` file. + + $ python obj2c.py left_hand_closed.obj > left_hand_closed.inc.c + +This is a work in progress and it currently has some serious limitations: + * It only encodes the geometry information of the OBJ file, so no + texture mapping or any other info. + * The generated fragment of C code has to be manually pasted into the + desired source file. Make sure that the name of the Gfx structure + you're pasting matches the one you're replacing. + * It hasn't been properly tested. + +""" + +def parse(filename): + from os.path import basename, splitext + from re import sub + + # WARNIGN! + # `gfx_name` is just a guess. You have to manually check that the name + # of the Gfx structure you're pasting matches the one you're replacing. + clean = lambda fn: sub('\W|^(?=\d)','_', fn) + gfx_name = clean(splitext(basename(filename))[0]) + gfx_vertices = [] + gfx_normals = [] + vertex_to_normal = {} + gfx_v_count = 0 + + vtx_name = '' + vtx_faces = [] + vtx_v_count = 0 + + output_upper = [] + output_lower = [f'const Gfx {gfx_name}[] = {{'] + + with open(filename, 'r') as obj: + for line in obj: + line = line.strip() + + if line.startswith('v '): + coordinates = [eval(x) for x in line.split()[1:4]] + gfx_vertices.append(coordinates) + vtx_v_count += 1 + gfx_v_count += 1 + + if line.startswith('vn '): + coordinates = [eval(x) for x in line.split()[1:4]] + gfx_normals.append([_encode_normal(x) for x in coordinates]) + + if line.startswith('g '): + vtx_name = line.split()[1] + + if line.startswith('f '): + pairs = [pair.split('//') for pair in line.split()[1:4]] + vtx_faces.append([int(pair[0]) for pair in pairs]) + for (x, y) in pairs: + vertex_to_normal[int(x) - 1] = int(y) - 1 + + if line.startswith('# ') and line.endswith('faces'): + output_upper.append(f'static const Vtx {vtx_name}[] = {{') + for i in range(gfx_v_count - vtx_v_count, gfx_v_count): + v_string = '[{}, {}, {}]'.format(*gfx_vertices[i]) + n_string = '[{}, {}, {}, 0x00]'.format(*gfx_normals[vertex_to_normal[i]]) + combined = f' [[{v_string}, 0, [0, 0], {n_string}]],' + output_upper.append(combined.replace('[', '{').replace(']', '}')) + + output_upper.append('};\n') + output_lower.append(f' gsSPVertex({vtx_name}, {vtx_v_count}, 0),') + + n = len(vtx_faces) + correction = vtx_v_count - gfx_v_count - 1 + for i in range(int(n / 2)): + f1 = [vtx_faces[2 * i][j] + correction for j in range(3)] + f2 = [vtx_faces[2 * i + 1][j] + correction for j in range(3)] + output_lower.append(' gsSP2Triangles({}, {}, {}, 0x0, {}, {}, {}, 0x0),'.format(*f1, *f2)) + + if n % 2 != 0: + f3 = [vtx_faces[-1][j] + correction for j in range(3)] + output_lower.append(' gsSP1Triangle({}, {}, {}, 0x0),'.format(*f3)) + + vtx_v_count = 0 + vtx_faces = [] + + output_lower.append(' gsSPEndDisplayList(),') + output_lower.append('};') + + for line in output_upper + output_lower: + print(line) + +def _encode_normal(x): + x *= 127 + if x <= 0: x += 255 + return hex(int(x)) + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('filename', help = 'filename of the .obj file to parse') + args = parser.parse_args() + parse(args.filename) \ No newline at end of file diff --git a/src/audio/external.c b/src/audio/external.c index 32431a53..a059c0b5 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -738,6 +738,7 @@ void func_8031D838(s32 player, FadeT fadeInTime, u8 targetVolume) { } seqPlayer->fadeVelocity = (((f32)(FLOAT_CAST(targetVolume) / EU_FLOAT(127.0)) - seqPlayer->fadeVolume) / (f32) fadeInTime); + #ifdef VERSION_EU seqPlayer->state = 0; #else @@ -764,117 +765,15 @@ void func_eu_802e9bec(s32 player, s32 channel, s32 arg2) { } #else - -#ifdef TARGET_N64 -struct SPTask *create_next_audio_frame_task(void) { - u32 samplesRemainingInAI; - s32 writtenCmds; - s32 index; - OSTask_t *task; - s32 oldDmaCount; - s32 flags; - - gAudioFrameCount++; - if (gAudioLoadLock != AUDIO_LOCK_NOT_LOADING) { - stubbed_printf("DAC:Lost 1 Frame.\n"); - return NULL; - } - - gAudioTaskIndex ^= 1; - gCurrAiBufferIndex++; - gCurrAiBufferIndex %= NUMAIBUFFERS; - index = (gCurrAiBufferIndex - 2 + NUMAIBUFFERS) % NUMAIBUFFERS; - samplesRemainingInAI = osAiGetLength() / 4; - - // Audio is triple buffered; the audio interface reads from two buffers - // while the third is being written by the RSP. More precisely, the - // lifecycle is: - // - this function computes an audio command list - // - wait for vblank - // - the command list is sent to the RSP (we could have sent it to the - // RSP before the vblank, but that gives the RSP less time to finish) - // - wait for vblank - // - the RSP is now expected to be finished, and we can send its output - // on to the AI - // Here we thus send to the AI the sound that was generated two frames ago. - if (gAiBufferLengths[index] != 0) { - osAiSetNextBuffer(gAiBuffers[index], gAiBufferLengths[index] * 4); - } - - oldDmaCount = gCurrAudioFrameDmaCount; - // There has to be some sort of no-op if here, but it's not exactly clear - // how it should look... It's also very unclear why gCurrAudioFrameDmaQueue - // isn't read from here, despite gCurrAudioFrameDmaCount being reset. - if (oldDmaCount > AUDIO_FRAME_DMA_QUEUE_SIZE) { - stubbed_printf("DMA: Request queue over.( %d )\n", oldDmaCount); - } - gCurrAudioFrameDmaCount = 0; - - gAudioTask = &gAudioTasks[gAudioTaskIndex]; - gAudioCmd = gAudioCmdBuffers[gAudioTaskIndex]; - - index = gCurrAiBufferIndex; - gCurrAiBuffer = gAiBuffers[index]; - gAiBufferLengths[index] = ((gSamplesPerFrameTarget - samplesRemainingInAI + - EXTRA_BUFFERED_AI_SAMPLES_TARGET) & ~0xf) + SAMPLES_TO_OVERPRODUCE; - if (gAiBufferLengths[index] < gMinAiBufferLength) { - gAiBufferLengths[index] = gMinAiBufferLength; - } - if (gAiBufferLengths[index] > gSamplesPerFrameTarget + SAMPLES_TO_OVERPRODUCE) { - gAiBufferLengths[index] = gSamplesPerFrameTarget + SAMPLES_TO_OVERPRODUCE; - } - - if (sGameLoopTicked != 0) { - update_game_sound(); - sGameLoopTicked = 0; - } - - // For the function to match we have to preserve some arbitrary variable - // across this function call. - flags = 0; - gAudioCmd = synthesis_execute(gAudioCmd, &writtenCmds, gCurrAiBuffer, gAiBufferLengths[index]); - gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); - - index = gAudioTaskIndex; - gAudioTask->msgqueue = NULL; - gAudioTask->msg = NULL; - - task = &gAudioTask->task.t; - task->type = M_AUDTASK; - task->flags = flags; - task->ucode_boot = rspF3DBootStart; - task->ucode_boot_size = (u8 *) rspF3DBootEnd - (u8 *) rspF3DBootStart; - task->ucode = rspAspMainStart; - task->ucode_size = 0x800; // (this size is ignored) - task->ucode_data = rspAspMainDataStart; - task->ucode_data_size = (rspAspMainDataEnd - rspAspMainDataStart) * sizeof(u64); - task->dram_stack = NULL; - task->dram_stack_size = 0; - task->output_buff = NULL; - task->output_buff_size = NULL; - task->data_ptr = gAudioCmdBuffers[index]; - task->data_size = writtenCmds * sizeof(u64); - - // The audio task never yields, so having a yield buffer is pointless. - // This wastefulness was fixed in US. -#ifdef VERSION_JP - task->yield_data_ptr = (u64 *) gAudioSPTaskYieldBuffer; - task->yield_data_size = OS_YIELD_AUDIO_SIZE; -#else - task->yield_data_ptr = NULL; - task->yield_data_size = 0; +// Stubbed N64-US/JP audio code +// continue; #endif - decrease_sample_dma_ttls(); - return gAudioTask; -} -#endif -#endif -#ifndef TARGET_N64 struct SPTask *create_next_audio_frame_task(void) { return NULL; } + void create_next_audio_buffer(s16 *samples, u32 num_samples) { gAudioFrameCount++; if (sGameLoopTicked != 0) { @@ -886,7 +785,6 @@ void create_next_audio_buffer(s16 *samples, u32 num_samples) { gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); decrease_sample_dma_ttls(); } -#endif void play_sound(s32 soundBits, f32 *pos) { sSoundRequests[sSoundRequestCount].soundBits = soundBits; diff --git a/src/audio/heap.c b/src/audio/heap.c index 32b8103c..d1d69417 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -651,12 +651,7 @@ s32 audio_shut_down_and_reset_step(void) { */ void wait_for_audio_frames(s32 frames) { gAudioFrameCount = 0; -#ifdef TARGET_N64 - // Sound thread will update gAudioFrameCount - while (gAudioFrameCount < frames) { - // spin - } -#endif + } #endif diff --git a/src/audio/internal.h b/src/audio/internal.h index f681afae..fa44c8be 100644 --- a/src/audio/internal.h +++ b/src/audio/internal.h @@ -507,9 +507,6 @@ struct Note /* U/J, EU */ /*0xA4, 0x00*/ struct AudioListItem listItem; /* 0x10*/ struct NoteSynthesisState synthesisState; -#ifdef TARGET_N64 - u8 pad0[12]; -#endif /*0x04, 0x30*/ u8 priority; /* 0x31*/ u8 waveId; /* 0x32*/ u8 sampleCountIndex; diff --git a/src/audio/port_eu.c b/src/audio/port_eu.c index f90fc2bf..b0d0650e 100644 --- a/src/audio/port_eu.c +++ b/src/audio/port_eu.c @@ -34,107 +34,6 @@ void decrease_sample_dma_ttls(void); s32 audio_shut_down_and_reset_step(void); void func_802ad7ec(u32); -#ifdef TARGET_N64 -struct SPTask *create_next_audio_frame_task(void) { - u32 samplesRemainingInAI; - s32 writtenCmds; - s32 index; - OSTask_t *task; - s32 flags; - u16 *currAiBuffer; - s32 oldDmaCount; - OSMesg sp30; - OSMesg sp2C; - - gAudioFrameCount++; - if (gAudioFrameCount % gAudioBufferParameters.presetUnk4 != 0) { - stubbed_printf("DAC:Lost 1 Frame.\n"); - return NULL; - } - - osSendMesg(OSMesgQueues[0], (OSMesg) gAudioFrameCount, 0); - - gAudioTaskIndex ^= 1; - gCurrAiBufferIndex++; - gCurrAiBufferIndex %= NUMAIBUFFERS; - index = (gCurrAiBufferIndex - 2 + NUMAIBUFFERS) % NUMAIBUFFERS; - samplesRemainingInAI = osAiGetLength() / 4; - - if (gAiBufferLengths[index] != 0) { - osAiSetNextBuffer(gAiBuffers[index], gAiBufferLengths[index] * 4); - } - - oldDmaCount = gCurrAudioFrameDmaCount; - if (oldDmaCount > AUDIO_FRAME_DMA_QUEUE_SIZE) { - stubbed_printf("DMA: Request queue over.( %d )\n", oldDmaCount); - } - gCurrAudioFrameDmaCount = 0; - - decrease_sample_dma_ttls(); - if (osRecvMesg(OSMesgQueues[2], &sp30, 0) != -1) { - gAudioResetPresetIdToLoad = (u8) (s32) sp30; - gAudioResetStatus = 5; - } - - if (gAudioResetStatus != 0) { - if (audio_shut_down_and_reset_step() == 0) { - if (gAudioResetStatus == 0) { - osSendMesg(OSMesgQueues[3], (OSMesg) (s32) gAudioResetPresetIdToLoad, OS_MESG_NOBLOCK); - } - return NULL; - } - } - - gAudioTask = &gAudioTasks[gAudioTaskIndex]; - gAudioCmd = gAudioCmdBuffers[gAudioTaskIndex]; - index = gCurrAiBufferIndex; - currAiBuffer = gAiBuffers[index]; - - gAiBufferLengths[index] = ((gAudioBufferParameters.samplesPerFrameTarget - samplesRemainingInAI + - EXTRA_BUFFERED_AI_SAMPLES_TARGET) & ~0xf) + SAMPLES_TO_OVERPRODUCE; - if (gAiBufferLengths[index] < gAudioBufferParameters.minAiBufferLength) { - gAiBufferLengths[index] = gAudioBufferParameters.minAiBufferLength; - } - if (gAiBufferLengths[index] > gAudioBufferParameters.maxAiBufferLength) { - gAiBufferLengths[index] = gAudioBufferParameters.maxAiBufferLength; - } - - if (osRecvMesg(OSMesgQueues[1], &sp2C, OS_MESG_NOBLOCK) != -1) { - func_802ad7ec((u32) sp2C); - } - - flags = 0; - gAudioCmd = synthesis_execute(gAudioCmd, &writtenCmds, currAiBuffer, gAiBufferLengths[index]); - gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); - gAudioRandom = gAudioRandom + writtenCmds / 8; - - index = gAudioTaskIndex; - gAudioTask->msgqueue = NULL; - gAudioTask->msg = NULL; - - task = &gAudioTask->task.t; - task->type = M_AUDTASK; - task->flags = flags; -#if TARGET_N64 - task->ucode_boot = rspF3DBootStart; - task->ucode_boot_size = (u8 *) rspF3DBootEnd - (u8 *) rspF3DBootStart; - task->ucode = rspAspMainStart; - task->ucode_data = rspAspMainDataStart; - task->ucode_size = 0x800; // (this size is ignored) - task->ucode_data_size = (rspAspMainDataEnd - rspAspMainDataStart) * sizeof(u64); -#endif - task->dram_stack = NULL; - task->dram_stack_size = 0; - task->output_buff = NULL; - task->output_buff_size = NULL; - task->data_ptr = gAudioCmdBuffers[index]; - task->data_size = writtenCmds * sizeof(u64); - task->yield_data_ptr = NULL; - task->yield_data_size = 0; - return gAudioTask; -} -#endif - void eu_process_audio_cmd(struct EuAudioCmd *cmd) { s32 i; diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 370728ba..b332c9f0 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -8,10 +8,7 @@ #include "seqplayer.h" #include "external.h" - -#ifndef TARGET_N64 #include "../pc/mixer.h" -#endif #define DMEM_ADDR_TEMP 0x0 #define DMEM_ADDR_UNCOMPRESSED_NOTE 0x180 diff --git a/src/buffers/buffers.h b/src/buffers/buffers.h index aa834618..3d8b6c64 100644 --- a/src/buffers/buffers.h +++ b/src/buffers/buffers.h @@ -27,11 +27,8 @@ extern struct SaveBuffer gSaveBuffer; extern u8 gGfxSPTaskStack[]; -#ifdef TARGET_N64 -#define GFX_NUM_POOLS 2 -#else #define GFX_NUM_POOLS 1 -#endif + extern struct GfxPool gGfxPools[GFX_NUM_POOLS]; #endif diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index edd52478..539f3a32 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -98,10 +98,10 @@ static void cur_obj_bhv_stack_push(uintptr_t bhvAddr) { // Retrieve the last behavior command address from the object's behavior stack. static uintptr_t cur_obj_bhv_stack_pop(void) { uintptr_t bhvAddr; - + gCurrentObject->bhvStackIndex--; bhvAddr = gCurrentObject->bhvStack[gCurrentObject->bhvStackIndex]; - + return bhvAddr; } @@ -115,7 +115,7 @@ static void stub_behavior_script_1(void) { // Usage: HIDE() static s32 bhv_cmd_hide(void) { cur_obj_hide(); - + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -124,7 +124,7 @@ static s32 bhv_cmd_hide(void) { // Usage: DISABLE_RENDERING() static s32 bhv_cmd_disable_rendering(void) { gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -133,7 +133,15 @@ static s32 bhv_cmd_disable_rendering(void) { // Usage: BILLBOARD() static s32 bhv_cmd_billboard(void) { gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD; - + + gCurBhvCommand++; + return BHV_PROC_CONTINUE; +} + +// Command 0x +static s32 bhv_cmd_cylboard(void) { + gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_CYLBOARD; + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -142,9 +150,9 @@ static s32 bhv_cmd_billboard(void) { // Usage: SET_MODEL(modelID) static s32 bhv_cmd_set_model(void) { s32 modelID = BHV_CMD_GET_2ND_S16(0); - + gCurrentObject->header.gfx.sharedChild = gLoadedGraphNodes[modelID]; - + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -216,7 +224,7 @@ static s32 bhv_cmd_break_unused(void) { static s32 bhv_cmd_call(void) { const BehaviorScript *jumpAddress; gCurBhvCommand++; - + cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(1)); // Store address of the next bhv command in the stack. jumpAddress = segmented_to_virtual(BHV_CMD_GET_VPTR(0)); gCurBhvCommand = jumpAddress; // Jump to the new address. @@ -300,7 +308,7 @@ static s32 bhv_cmd_begin_repeat(void) { static s32 bhv_cmd_end_repeat(void) { u32 count = cur_obj_bhv_stack_pop(); // Retrieve loop count from the stack. count--; - + if (count != 0) { gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop // Save address and count to the stack again @@ -320,7 +328,7 @@ static s32 bhv_cmd_end_repeat(void) { static s32 bhv_cmd_end_repeat_continue(void) { u32 count = cur_obj_bhv_stack_pop(); count--; - + if (count != 0) { gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop // Save address and count to the stack again @@ -546,7 +554,7 @@ static s32 bhv_cmd_drop_to_floor(void) { f32 x = gCurrentObject->oPosX; f32 y = gCurrentObject->oPosY; f32 z = gCurrentObject->oPosZ; - + f32 floor = find_floor_height(x, y + 200.0f, z); gCurrentObject->oPosY = floor; gCurrentObject->oMoveFlags |= OBJ_MOVE_ON_GROUND; @@ -665,7 +673,7 @@ static s32 bhv_cmd_nop_4(void) { static s32 bhv_cmd_begin(void) { // These objects were likely very early objects, which is why this code is here // instead of in the respective behavior scripts. - + // Initiate the room if the object is a haunted chair or the mad piano. if (cur_obj_has_behavior(bhvHauntedChair)) { bhv_init_room(); @@ -696,7 +704,7 @@ static void bhv_cmd_set_int_random_from_table(s32 tableSize) { } cur_obj_set_int(field, table[(s32)(tableSize * random_float())]); - + // Does not increment gCurBhvCommand or return a bhv status } @@ -719,7 +727,7 @@ static s32 bhv_cmd_set_int_random_from_table(void) { // Set the field to a random entry of the table. cur_obj_set_int(field, table[(s32)(tableSize * random_float())]); - + gCurBhvCommand += (tableSize / 2) + 1; return BHV_PROC_CONTINUE; } @@ -729,9 +737,9 @@ static s32 bhv_cmd_set_int_random_from_table(void) { // Usage: LOAD_COLLISION_DATA(collisionData) static s32 bhv_cmd_load_collision_data(void) { u32 *collisionData = segmented_to_virtual(BHV_CMD_GET_VPTR(1)); - + gCurrentObject->collisionData = collisionData; - + gCurBhvCommand += 2; return BHV_PROC_CONTINUE; } @@ -742,7 +750,7 @@ static s32 bhv_cmd_set_home(void) { gCurrentObject->oHomeX = gCurrentObject->oPosX; gCurrentObject->oHomeY = gCurrentObject->oPosY; gCurrentObject->oHomeZ = gCurrentObject->oPosZ; - + gCurBhvCommand++; return BHV_PROC_CONTINUE; } @@ -815,9 +823,9 @@ static s32 bhv_cmd_parent_bit_clear(void) { // Usage: SPAWN_WATER_DROPLET(dropletParams) static s32 bhv_cmd_spawn_water_droplet(void) { struct WaterDropletParams *dropletParams = BHV_CMD_GET_VPTR(1); - + spawn_water_droplet(gCurrentObject, dropletParams); - + gCurBhvCommand += 2; return BHV_PROC_CONTINUE; } @@ -842,62 +850,63 @@ void stub_behavior_script_2(void) { typedef s32 (*BhvCommandProc)(void); static BhvCommandProc BehaviorCmdTable[] = { - bhv_cmd_begin, - bhv_cmd_delay, - bhv_cmd_call, - bhv_cmd_return, - bhv_cmd_goto, - bhv_cmd_begin_repeat, - bhv_cmd_end_repeat, - bhv_cmd_end_repeat_continue, - bhv_cmd_begin_loop, - bhv_cmd_end_loop, - bhv_cmd_break, - bhv_cmd_break_unused, - bhv_cmd_call_native, - bhv_cmd_add_float, - bhv_cmd_set_float, - bhv_cmd_add_int, - bhv_cmd_set_int, - bhv_cmd_or_int, - bhv_cmd_bit_clear, - bhv_cmd_set_int_rand_rshift, - bhv_cmd_set_random_float, - bhv_cmd_set_random_int, - bhv_cmd_add_random_float, - bhv_cmd_add_int_rand_rshift, - bhv_cmd_nop_1, - bhv_cmd_nop_2, - bhv_cmd_nop_3, - bhv_cmd_set_model, - bhv_cmd_spawn_child, - bhv_cmd_deactivate, - bhv_cmd_drop_to_floor, - bhv_cmd_sum_float, - bhv_cmd_sum_int, - bhv_cmd_billboard, - bhv_cmd_hide, - bhv_cmd_set_hitbox, - bhv_cmd_nop_4, - bhv_cmd_delay_var, - bhv_cmd_begin_repeat_unused, - bhv_cmd_load_animations, - bhv_cmd_animate, - bhv_cmd_spawn_child_with_param, - bhv_cmd_load_collision_data, - bhv_cmd_set_hitbox_with_offset, - bhv_cmd_spawn_obj, - bhv_cmd_set_home, - bhv_cmd_set_hurtbox, - bhv_cmd_set_interact_type, - bhv_cmd_set_obj_physics, - bhv_cmd_set_interact_subtype, - bhv_cmd_scale, - bhv_cmd_parent_bit_clear, - bhv_cmd_animate_texture, - bhv_cmd_disable_rendering, - bhv_cmd_set_int_unused, - bhv_cmd_spawn_water_droplet, + bhv_cmd_begin, //00 + bhv_cmd_delay, //01 + bhv_cmd_call, //02 + bhv_cmd_return, //03 + bhv_cmd_goto, //04 + bhv_cmd_begin_repeat, //05 + bhv_cmd_end_repeat, //06 + bhv_cmd_end_repeat_continue, //07 + bhv_cmd_begin_loop, //08 + bhv_cmd_end_loop, //09 + bhv_cmd_break, //0A + bhv_cmd_break_unused, //0B + bhv_cmd_call_native, //0C + bhv_cmd_add_float, //0D + bhv_cmd_set_float, //0E + bhv_cmd_add_int, //0F + bhv_cmd_set_int, //10 + bhv_cmd_or_int, //11 + bhv_cmd_bit_clear, //12 + bhv_cmd_set_int_rand_rshift, //13 + bhv_cmd_set_random_float, //14 + bhv_cmd_set_random_int, //15 + bhv_cmd_add_random_float, //16 + bhv_cmd_add_int_rand_rshift, //17 + bhv_cmd_nop_1, //18 + bhv_cmd_nop_2, //19 + bhv_cmd_nop_3, //1A + bhv_cmd_set_model, //1B + bhv_cmd_spawn_child, //1C + bhv_cmd_deactivate, //1D + bhv_cmd_drop_to_floor, //1E + bhv_cmd_sum_float, //1F + bhv_cmd_sum_int, //20 + bhv_cmd_billboard, //21 + bhv_cmd_hide, //22 + bhv_cmd_set_hitbox, //23 + bhv_cmd_nop_4, //24 + bhv_cmd_delay_var, //25 + bhv_cmd_begin_repeat_unused, //26 + bhv_cmd_load_animations, //27 + bhv_cmd_animate, //28 + bhv_cmd_spawn_child_with_param, //29 + bhv_cmd_load_collision_data, //2A + bhv_cmd_set_hitbox_with_offset, //2B + bhv_cmd_spawn_obj, //2C + bhv_cmd_set_home, //2D + bhv_cmd_set_hurtbox, //2E + bhv_cmd_set_interact_type, //2F + bhv_cmd_set_obj_physics, //30 + bhv_cmd_set_interact_subtype, //31 + bhv_cmd_scale, //32 + bhv_cmd_parent_bit_clear, //33 + bhv_cmd_animate_texture, //34 + bhv_cmd_disable_rendering, //35 + bhv_cmd_set_int_unused, //36 + bhv_cmd_spawn_water_droplet, //37 + bhv_cmd_cylboard //38 }; // Execute the behavior script of the current object, process the object flags, and other miscellaneous code for updating objects. @@ -987,11 +996,15 @@ void cur_obj_update(void) { } else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) { if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) { // If the object has a render distance, check if it should be shown. +#ifndef NODRAWINGDISTANCE if (distanceFromMario > gCurrentObject->oDrawingDistance) { // Out of render distance, hide the object. gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; } else if (gCurrentObject->oHeldState == HELD_FREE) { +#else + if (distanceFromMario <= gCurrentObject->oDrawingDistance && gCurrentObject->oHeldState == HELD_FREE) { +#endif // In render distance (and not being held), show the object. gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; diff --git a/src/engine/graph_node.h b/src/engine/graph_node.h index ac05ed14..c99e5f32 100644 --- a/src/engine/graph_node.h +++ b/src/engine/graph_node.h @@ -30,12 +30,13 @@ extern Vec3s gVec3sOne; #define GRAPH_RENDER_Z_BUFFER (1 << 3) #define GRAPH_RENDER_INVISIBLE (1 << 4) #define GRAPH_RENDER_HAS_ANIMATION (1 << 5) +#define GRAPH_RENDER_CYLBOARD (1 << 6) // Whether the node type has a function pointer of type GraphNodeFunc #define GRAPH_NODE_TYPE_FUNCTIONAL 0x100 // Type used for Bowser and an unused geo function in obj_behaviors.c -#define GRAPH_NODE_TYPE_400 0x400 +#define GRAPH_NODE_TYPE_400 0x400 // The discriminant for different types of geo nodes #define GRAPH_NODE_TYPE_ROOT 0x001 diff --git a/src/engine/level_script.c b/src/engine/level_script.c index d0534038..64e4d46e 100644 --- a/src/engine/level_script.c +++ b/src/engine/level_script.c @@ -1,7 +1,6 @@ #include -#ifndef TARGET_N64 #include -#endif + #include "sm64.h" #include "audio/external.h" @@ -605,9 +604,7 @@ static void level_cmd_set_gamma(void) { static void level_cmd_set_terrain_data(void) { if (sCurrAreaIndex != -1) { -#ifdef TARGET_N64 - gAreas[sCurrAreaIndex].terrainData = segmented_to_virtual(CMD_GET(void *, 4)); -#else + Collision *data; u32 size; @@ -615,7 +612,7 @@ static void level_cmd_set_terrain_data(void) { size = get_area_terrain_size(data) * sizeof(Collision); gAreas[sCurrAreaIndex].terrainData = alloc_only_pool_alloc(sLevelPool, size); memcpy(gAreas[sCurrAreaIndex].terrainData, data, size); -#endif + } sCurrentCmd = CMD_NEXT; } @@ -629,9 +626,7 @@ static void level_cmd_set_rooms(void) { static void level_cmd_set_macro_objects(void) { if (sCurrAreaIndex != -1) { -#ifdef TARGET_N64 - gAreas[sCurrAreaIndex].macroObjects = segmented_to_virtual(CMD_GET(void *, 4)); -#else + MacroObject *data = segmented_to_virtual(CMD_GET(void *, 4)); s32 len = 0; while (data[len++] != 0x001E) { @@ -639,7 +634,7 @@ static void level_cmd_set_macro_objects(void) { } gAreas[sCurrAreaIndex].macroObjects = alloc_only_pool_alloc(sLevelPool, len * sizeof(MacroObject)); memcpy(gAreas[sCurrAreaIndex].macroObjects, data, len * sizeof(MacroObject)); -#endif + } sCurrentCmd = CMD_NEXT; } diff --git a/src/engine/math_util.c b/src/engine/math_util.c index 8643f5e0..3f027ed5 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -377,6 +377,31 @@ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) { dest[3][3] = 1; } +void mtxf_cylboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) { //straight up mtxf_billboard but minus the dest[1][n] lines. transform for cylindrical billboards + dest[0][0] = coss(angle); + dest[0][1] = sins(angle); + dest[0][2] = 0; + dest[0][3] = 0; + + dest[1][0] = mtx[1][0]; + dest[1][1] = mtx[1][1]; + dest[1][2] = mtx[1][2]; + dest[1][3] = 0; + + dest[2][0] = 0; + dest[2][1] = 0; + dest[2][2] = 1; + dest[2][3] = 0; + + dest[3][0] = + mtx[0][0] * position[0] + mtx[1][0] * position[1] + mtx[2][0] * position[2] + mtx[3][0]; + dest[3][1] = + mtx[0][1] * position[0] + mtx[1][1] * position[1] + mtx[2][1] * position[2] + mtx[3][1]; + dest[3][2] = + mtx[0][2] * position[0] + mtx[1][2] * position[1] + mtx[2][2] * position[2] + mtx[3][2]; + dest[3][3] = 1; +} + /** * Set 'dest' to a transformation matrix that aligns an object with the terrain * based on the normal. Used for enemies. diff --git a/src/engine/math_util.h b/src/engine/math_util.h index 650fe973..b8b7f1b8 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -56,6 +56,7 @@ void mtxf_lookat(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d); void mtxf_rotate_zxy_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c); void mtxf_rotate_xyz_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c); void mtxf_billboard(f32 mtx1[4][4], f32 mtx2[4][4], Vec3f c, s16 d); +void mtxf_cylboard(f32 mtx1[4][4], f32 mtx2[4][4], Vec3f c, s16 d); void mtxf_align_terrain_normal(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d); void mtxf_align_terrain_triangle(f32 mtx[4][4], Vec3f b, s16 c, f32 d); void mtxf_mul(f32 dest[4][4], f32 a[4][4], f32 b[4][4]); diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index 363f9afb..7a7da511 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -533,7 +533,6 @@ void alloc_surface_pools(void) { reset_red_coins_collected(); } -#ifndef TARGET_N64 /** * Get the size of the terrain data, to get the correct size when copying later. */ @@ -581,8 +580,6 @@ u32 get_area_terrain_size(s16 *data) { return data - startPos; } -#endif - /** * Process the level file, loading in vertices, surfaces, some objects, and environmental @@ -789,9 +786,13 @@ void load_object_collision_model(void) { } } +#ifndef NODRAWINGDISTANCE if (marioDist < gCurrentObject->oDrawingDistance) { +#endif gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; +#ifndef NODRAWINGDISTANCE } else { gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; } +#endif } diff --git a/src/engine/surface_load.h b/src/engine/surface_load.h index 943317c9..b8637be4 100644 --- a/src/engine/surface_load.h +++ b/src/engine/surface_load.h @@ -28,9 +28,9 @@ extern struct Surface *sSurfacePool; extern s16 sSurfacePoolSize; void alloc_surface_pools(void); -#ifndef TARGET_N64 + u32 get_area_terrain_size(s16 *data); -#endif + void load_area_terrain(s16 index, s16 *data, s8 *surfaceRooms, s16 *macroObjects); void clear_dynamic_surfaces(void); void load_object_collision_model(void); diff --git a/src/game/behaviors/bub.inc.c b/src/game/behaviors/bub.inc.c index e8e63096..7bf71690 100644 --- a/src/game/behaviors/bub.inc.c +++ b/src/game/behaviors/bub.inc.c @@ -8,11 +8,15 @@ void bub_spawner_act_0(void) { s32 i; s32 sp18 = o->oBirdChirpChirpUnkF4; +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 1500.0f) { +#endif for (i = 0; i < sp18; i++) spawn_object(o, MODEL_BUB, bhvBub); o->oAction = 1; +#ifndef NODRAWINGDISTANCE } +#endif } void bub_spawner_act_1(void) { diff --git a/src/game/behaviors/cannon.inc.c b/src/game/behaviors/cannon.inc.c index 5f55ca83..a8cfcab3 100644 --- a/src/game/behaviors/cannon.inc.c +++ b/src/game/behaviors/cannon.inc.c @@ -17,9 +17,11 @@ void opened_cannon_act_0(void) { cur_obj_enable_rendering(); cur_obj_become_tangible(); } + cur_obj_become_tangible(); + cur_obj_enable_rendering(); if (o->oDistanceToMario < 500.0f) { - cur_obj_become_tangible(); - cur_obj_enable_rendering(); + //cur_obj_become_tangible(); + //cur_obj_enable_rendering(); if (o->oInteractStatus & INT_STATUS_INTERACTED && (!(o->oInteractStatus & INT_STATUS_TOUCHED_BOB_OMB))) // bob-omb explodes when it gets into a cannon @@ -30,8 +32,8 @@ void opened_cannon_act_0(void) { } else o->oInteractStatus = 0; } else { - cur_obj_become_intangible(); - cur_obj_disable_rendering(); + //cur_obj_become_intangible(); + //cur_obj_disable_rendering(); o->oCannonUnk10C = 0; } } diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c index a77c5d5f..9b9c3423 100644 --- a/src/game/behaviors/chain_chomp.inc.c +++ b/src/game/behaviors/chain_chomp.inc.c @@ -53,7 +53,9 @@ static void chain_chomp_act_uninitialized(void) { struct ChainSegment *segments; s32 i; +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 3000.0f) { +#endif segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); if (segments != NULL) { // Each segment represents the offset of a chain part to the pivot. @@ -81,7 +83,9 @@ static void chain_chomp_act_uninitialized(void) { cur_obj_unhide(); } } +#ifndef NODRAWINGDISTANCE } +#endif } /** @@ -359,10 +363,12 @@ static void chain_chomp_act_move(void) { f32 maxDistToPivot; // Unload chain if mario is far enough +#ifndef NODRAWINGDISTANCE if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) { o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN; o->oForwardVel = o->oVelY = 0.0f; } else { +#endif cur_obj_update_floor_and_walls(); switch (o->oChainChompReleaseStatus) { @@ -446,7 +452,9 @@ static void chain_chomp_act_move(void) { o->oGravity = -4.0f; o->oChainChompTargetPitch = -0x3000; } +#ifndef NODRAWINGDISTANCE } +#endif } /** diff --git a/src/game/behaviors/cloud.inc.c b/src/game/behaviors/cloud.inc.c index e5cb9bed..fa82e3f4 100644 --- a/src/game/behaviors/cloud.inc.c +++ b/src/game/behaviors/cloud.inc.c @@ -47,10 +47,14 @@ static void cloud_act_spawn_parts(void) { * Wait for mario to approach, then unhide and enter the spawn parts action. */ static void cloud_act_fwoosh_hidden(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif cur_obj_unhide(); o->oAction = CLOUD_ACT_SPAWN_PARTS; +#ifndef NODRAWINGDISTANCE } +#endif } /** @@ -58,9 +62,11 @@ static void cloud_act_fwoosh_hidden(void) { * long enough, blow wind at him. */ static void cloud_fwoosh_update(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 2500.0f) { o->oAction = CLOUD_ACT_UNLOAD; } else { +#endif if (o->oCloudBlowing) { o->header.gfx.scale[0] += o->oCloudGrowSpeed; @@ -95,7 +101,9 @@ static void cloud_fwoosh_update(void) { } cur_obj_scale(o->header.gfx.scale[0]); +#ifndef NODRAWINGDISTANCE } +#endif } /** diff --git a/src/game/behaviors/coin.inc.c b/src/game/behaviors/coin.inc.c index 913c5834..05619b96 100644 --- a/src/game/behaviors/coin.inc.c +++ b/src/game/behaviors/coin.inc.c @@ -184,17 +184,23 @@ void bhv_coin_formation_loop(void) { s32 bitIndex; switch (o->oAction) { case 0: +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif for (bitIndex = 0; bitIndex < 8; bitIndex++) { if (!(o->oCoinUnkF4 & (1 << bitIndex))) spawn_coin_in_formation(bitIndex, o->oBehParams2ndByte); } o->oAction++; +#ifndef NODRAWINGDISTANCE } +#endif break; case 1: +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 2100.0f) o->oAction++; +#endif break; case 2: o->oAction = 0; diff --git a/src/game/behaviors/enemy_lakitu.inc.c b/src/game/behaviors/enemy_lakitu.inc.c index 056c3f13..cacd732f 100644 --- a/src/game/behaviors/enemy_lakitu.inc.c +++ b/src/game/behaviors/enemy_lakitu.inc.c @@ -24,12 +24,16 @@ static struct ObjectHitbox sEnemyLakituHitbox = { * Wait for mario to approach, then spawn the cloud and become visible. */ static void enemy_lakitu_act_uninitialized(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif spawn_object_relative_with_scale(CLOUD_BP_LAKITU_CLOUD, 0, 0, 0, 2.0f, o, MODEL_MIST, bhvCloud); cur_obj_unhide(); o->oAction = ENEMY_LAKITU_ACT_MAIN; +#ifndef NODRAWINGDISTANCE } +#endif } /** diff --git a/src/game/behaviors/fish.inc.c b/src/game/behaviors/fish.inc.c index 839ab8d6..f652ef47 100644 --- a/src/game/behaviors/fish.inc.c +++ b/src/game/behaviors/fish.inc.c @@ -42,7 +42,9 @@ void fish_act_spawn(void) { * If the current level is Secret Aquarium, ignore this requirement. * Fish moves at random with a max-range of 700.0f. */ +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) { +#endif for (i = 0; i < schoolQuantity; i++) { fishObject = spawn_object(o, model, bhvFish); fishObject->oBehParams2ndByte = o->oBehParams2ndByte; @@ -50,7 +52,9 @@ void fish_act_spawn(void) { obj_translate_xyz_random(fishObject, 700.0f); } o->oAction = FISH_ACT_ACTIVE; +#ifndef NODRAWINGDISTANCE } +#endif } /** diff --git a/src/game/behaviors/goomba.inc.c b/src/game/behaviors/goomba.inc.c index 2dab2fec..bf47dda1 100644 --- a/src/game/behaviors/goomba.inc.c +++ b/src/game/behaviors/goomba.inc.c @@ -78,7 +78,9 @@ void bhv_goomba_triplet_spawner_update(void) { // If mario is close enough and the goombas aren't currently loaded, then // spawn them if (o->oAction == GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 3000.0f) { +#endif // The spawner is capable of spawning more than 3 goombas, but this // is not used in the game dAngle = @@ -98,11 +100,13 @@ void bhv_goomba_triplet_spawner_update(void) { } o->oAction += 1; +#ifndef NODRAWINGDISTANCE } } else if (o->oDistanceToMario > 4000.0f) { // If mario is too far away, enter the unloaded action. The goombas // will detect this and unload themselves o->oAction = GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED; +#endif } } diff --git a/src/game/behaviors/heave_ho.inc.c b/src/game/behaviors/heave_ho.inc.c index 662bb0bd..2cbd1f0e 100644 --- a/src/game/behaviors/heave_ho.inc.c +++ b/src/game/behaviors/heave_ho.inc.c @@ -72,8 +72,12 @@ void heave_ho_act_3(void) { } void heave_ho_act_0(void) { - cur_obj_set_pos_to_home(); +#ifndef NODRAWINGDISTANCE if (find_water_level(o->oPosX, o->oPosZ) < o->oPosY && o->oDistanceToMario < 4000.0f) { +#else + if (find_water_level(o->oPosX, o->oPosZ) < (o->oPosY - 50.0f)) { +#endif + cur_obj_set_pos_to_home(); cur_obj_become_tangible(); cur_obj_unhide(); o->oAction = 1; diff --git a/src/game/behaviors/king_bobomb.inc.c b/src/game/behaviors/king_bobomb.inc.c index 63a75755..af1cb0a6 100644 --- a/src/game/behaviors/king_bobomb.inc.c +++ b/src/game/behaviors/king_bobomb.inc.c @@ -295,10 +295,14 @@ void king_bobomb_move(void) { cur_obj_move_using_fvel_and_gravity(); cur_obj_call_action_function(sKingBobombActions); exec_anim_sound_state(sKingBobombSoundStates); +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 5000.0f) +#endif cur_obj_enable_rendering(); +#ifndef NODRAWINGDISTANCE else cur_obj_disable_rendering(); +#endif } void bhv_king_bobomb_loop(void) { diff --git a/src/game/behaviors/lll_floating_wood_piece.inc.c b/src/game/behaviors/lll_floating_wood_piece.inc.c index a484471c..fad31f3f 100644 --- a/src/game/behaviors/lll_floating_wood_piece.inc.c +++ b/src/game/behaviors/lll_floating_wood_piece.inc.c @@ -14,18 +14,24 @@ void bhv_lll_floating_wood_bridge_loop(void) { s32 i; switch (o->oAction) { case 0: +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2500.0f) { +#endif for (i = 1; i < 4; i++) { sp3C = spawn_object_relative(0, (i - 2) * 300, 0, 0, o, MODEL_LLL_WOOD_BRIDGE, bhvLllWoodPiece); sp3C->oLllWoodPieceUnkF4 = i * 4096; } o->oAction = 1; +#ifndef NODRAWINGDISTANCE } +#endif break; case 1: +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 2600.0f) o->oAction = 2; +#endif break; case 2: o->oAction = 0; diff --git a/src/game/behaviors/lll_rotating_hex_flame.inc.c b/src/game/behaviors/lll_rotating_hex_flame.inc.c index efabfca8..fc707330 100644 --- a/src/game/behaviors/lll_rotating_hex_flame.inc.c +++ b/src/game/behaviors/lll_rotating_hex_flame.inc.c @@ -30,7 +30,9 @@ void fire_bar_spawn_flames(s16 a0) { } void fire_bar_act_0(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 3000.0f) +#endif o->oAction = 1; } @@ -45,8 +47,10 @@ void fire_bar_act_1(void) { void fire_bar_act_2(void) { o->oAngleVelYaw = -0x100; o->oMoveAngleYaw += o->oAngleVelYaw; +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 3200.0f) o->oAction = 3; +#endif } void fire_bar_act_3(void) { diff --git a/src/game/behaviors/piranha_plant.inc.c b/src/game/behaviors/piranha_plant.inc.c index e8abe089..328f4518 100644 --- a/src/game/behaviors/piranha_plant.inc.c +++ b/src/game/behaviors/piranha_plant.inc.c @@ -328,7 +328,7 @@ void (*TablePiranhaPlantActions[])(void) = { */ void bhv_piranha_plant_loop(void) { cur_obj_call_action_function(TablePiranhaPlantActions); - + #ifndef NODRAWINGDISTANCE // In WF, hide all Piranha Plants once high enough up. if (gCurrLevelNum == LEVEL_WF) { if (gMarioObject->oPosY > 3400.0f) @@ -336,5 +336,6 @@ void bhv_piranha_plant_loop(void) { else cur_obj_unhide(); } + #endif o->oInteractStatus = 0; } diff --git a/src/game/behaviors/pokey.inc.c b/src/game/behaviors/pokey.inc.c index df5d11f5..cfcc92c2 100644 --- a/src/game/behaviors/pokey.inc.c +++ b/src/game/behaviors/pokey.inc.c @@ -151,7 +151,9 @@ static void pokey_act_uninitialized(void) { s32 i; s16 partModel; +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif partModel = MODEL_POKEY_HEAD; for (i = 0; i < 5; i++) { @@ -170,7 +172,9 @@ static void pokey_act_uninitialized(void) { o->oPokeyNumAliveBodyParts = 5; o->oPokeyBottomBodyPartSize = 1.0f; o->oAction = POKEY_ACT_WANDER; +#ifndef NODRAWINGDISTANCE } +#endif } /** @@ -185,9 +189,11 @@ static void pokey_act_wander(void) { if (o->oPokeyNumAliveBodyParts == 0) { obj_mark_for_deletion(o); +#ifndef NODRAWINGDISTANCE } else if (o->oDistanceToMario > 2500.0f) { o->oAction = POKEY_ACT_UNLOAD_PARTS; o->oForwardVel = 0.0f; +#endif } else { treat_far_home_as_mario(1000.0f); cur_obj_update_floor_and_walls(); diff --git a/src/game/behaviors/sl_walking_penguin.inc.c b/src/game/behaviors/sl_walking_penguin.inc.c index f5b60a8a..59428acb 100644 --- a/src/game/behaviors/sl_walking_penguin.inc.c +++ b/src/game/behaviors/sl_walking_penguin.inc.c @@ -97,7 +97,9 @@ void bhv_sl_walking_penguin_loop(void) { } cur_obj_move_standard(-78); - if (!cur_obj_hide_if_mario_far_away_y(1000.0f)) +#ifndef NODRAWINGDISTANCE + if (!cur_obj_hide_if_mario_far_away_y(1000.0f)) +#endif play_penguin_walking_sound(PENGUIN_WALK_BIG); // Adjust the position to get a point better lined up with the visual model, for stopping the wind. diff --git a/src/game/behaviors/snufit.inc.c b/src/game/behaviors/snufit.inc.c index f3a0c9ef..76e78c09 100644 --- a/src/game/behaviors/snufit.inc.c +++ b/src/game/behaviors/snufit.inc.c @@ -180,7 +180,11 @@ void bhv_snufit_loop(void) { void bhv_snufit_balls_loop(void) { // If far from Mario or in a different room, despawn. if ((o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) +#ifndef NODRAWINGDISTANCE || (o->oTimer != 0 && o->oDistanceToMario > 1500.0f)) { +#else + || (o->oTimer != 0)) { +#endif obj_mark_for_deletion(o); } diff --git a/src/game/behaviors/triplet_butterfly.inc.c b/src/game/behaviors/triplet_butterfly.inc.c index 1c2b9265..3d16a9d2 100644 --- a/src/game/behaviors/triplet_butterfly.inc.c +++ b/src/game/behaviors/triplet_butterfly.inc.c @@ -54,9 +54,11 @@ static void triplet_butterfly_act_init(void) { } static void triplet_butterfly_act_wander(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 1500.0f) { obj_mark_for_deletion(o); } else { +#endif approach_f32_ptr(&o->oTripletButterflySpeed, 8.0f, 0.5f); if (o->oTimer < 60) { o->oTripletButterflyTargetYaw = cur_obj_angle_to_home(); @@ -82,7 +84,9 @@ static void triplet_butterfly_act_wander(void) { obj_move_pitch_approach(o->oTripletButterflyTargetPitch, 400); cur_obj_rotate_yaw_toward(o->oTripletButterflyTargetYaw, random_linear_offset(400, 800)); +#ifndef NODRAWINGDISTANCE } +#endif } static void triplet_butterfly_act_activate(void) { diff --git a/src/game/behaviors/water_bomb_cannon.inc.c b/src/game/behaviors/water_bomb_cannon.inc.c index 8e9ba33b..fb82e43c 100644 --- a/src/game/behaviors/water_bomb_cannon.inc.c +++ b/src/game/behaviors/water_bomb_cannon.inc.c @@ -38,19 +38,27 @@ void bhv_bubble_cannon_barrel_loop(void) { } void water_bomb_cannon_act_0(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 2000.0f) { +#endif spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles); cur_obj_unhide(); o->oAction = 1; o->oMoveAnglePitch = o->oWaterCannonUnkFC = 0x1C00; +#ifndef NODRAWINGDISTANCE } +#endif } void water_bomb_cannon_act_1(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario > 2500.0f) { o->oAction = 2; } else if (o->oBehParams2ndByte == 0) { +#else + if (o->oBehParams2ndByte == 0) { +#endif if (o->oWaterCannonUnkF4 != 0) { o->oWaterCannonUnkF4 -= 1; } else { diff --git a/src/game/behaviors/whirlpool.inc.c b/src/game/behaviors/whirlpool.inc.c index 405e0518..5aebebd2 100644 --- a/src/game/behaviors/whirlpool.inc.c +++ b/src/game/behaviors/whirlpool.inc.c @@ -35,7 +35,9 @@ void whirpool_orient_graph(void) { } void bhv_whirlpool_loop(void) { +#ifndef NODRAWINGDISTANCE if (o->oDistanceToMario < 5000.0f) { +#endif o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; // not sure if actually an array @@ -52,10 +54,12 @@ void bhv_whirlpool_loop(void) { whirpool_orient_graph(); o->oFaceAngleYaw += 0x1F40; +#ifndef NODRAWINGDISTANCE } else { o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT] = 0; } +#endif cur_obj_play_sound_1(SOUND_ENV_WATER); diff --git a/src/game/behaviors/whomp.inc.c b/src/game/behaviors/whomp.inc.c index c9ebca0a..c7c39241 100644 --- a/src/game/behaviors/whomp.inc.c +++ b/src/game/behaviors/whomp.inc.c @@ -246,10 +246,14 @@ void bhv_whomp_loop(void) { cur_obj_call_action_function(sWhompActions); cur_obj_move_standard(-20); if (o->oAction != 9) { +#ifndef NODRAWINGDISTANCE + // o->oBehParams2ndByte here seems to be a flag + // indicating whether this is a normal or king whomp if (o->oBehParams2ndByte != 0) cur_obj_hide_if_mario_far_away_y(2000.0f); else cur_obj_hide_if_mario_far_away_y(1000.0f); +#endif load_object_collision_model(); } } diff --git a/src/game/bettercamera.h b/src/game/bettercamera.h index b3355ef6..ea814dd7 100644 --- a/src/game/bettercamera.h +++ b/src/game/bettercamera.h @@ -26,14 +26,9 @@ enum newcam_flagvalues }; -extern void newcam_display_options(void); -extern void newcam_check_pause_buttons(void); extern void newcam_init_settings(void); -extern void newcam_save_settings(void); -extern void newcam_render_option_text(void); extern void newcam_diagnostics(void); -extern u8 newcam_option_open; extern u8 newcam_sensitivityX; //How quick the camera works. extern u8 newcam_sensitivityY; diff --git a/src/game/bettercamera.inc.h b/src/game/bettercamera.inc.h index b48f5d4c..87391be8 100644 --- a/src/game/bettercamera.inc.h +++ b/src/game/bettercamera.inc.h @@ -31,8 +31,6 @@ NC_MODE_NOTURN: Disables horizontal and vertical control of the camera. //#define NEWCAM_DEBUG //Some print values for puppycam. Not useful anymore, but never hurts to keep em around. //#define nosound //If for some reason you hate the concept of audio, you can disable it. //#define noaccel //Disables smooth movement of the camera with the C buttons. -#define DEGRADE 0.1f //What percent of the remaining camera movement is degraded. Default is 10% - //!Hardcoded camera angle stuff. They're essentially area boxes that when Mario is inside, will trigger some view changes. ///Don't touch this btw, unless you know what you're doing, this has to be above for religious reasons. @@ -91,6 +89,7 @@ s16 newcam_yaw_target; // The yaw value the camera tries to set itself to when t f32 newcam_turnwait; // The amount of time to wait after landing before allowing the camera to turn again f32 newcam_pan_x; f32 newcam_pan_z; +f32 newcam_degrade = 0.1f; //What percent of the remaining camera movement is degraded. Default is 10% u8 newcam_cstick_down = 0; //Just a value that triggers true when the player 2 stick is moved in 8 direction move to prevent holding it down. u8 newcam_target; @@ -108,17 +107,6 @@ u16 newcam_mode; u16 newcam_intendedmode = 0; // which camera mode the camera's going to try to be in when not forced into another. u16 newcam_modeflags; -u8 newcam_option_open = 0; -s8 newcam_option_selection = 0; -f32 newcam_option_timer = 0; -u8 newcam_option_index = 0; -u8 newcam_option_scroll = 0; -u8 newcam_option_scroll_last = 0; -u8 newcam_total = 8; //How many options there are in newcam_uptions. - -u8 newcam_options[][64] = {{NC_ANALOGUE}, {NC_MOUSE}, {NC_CAMX}, {NC_CAMY}, {NC_INVERTX}, {NC_INVERTY}, {NC_CAMC}, {NC_CAMP}}; -u8 newcam_flags[][64] = {{NC_DISABLED}, {NC_ENABLED}}; -u8 newcam_strings[][64] = {{NC_BUTTON}, {NC_BUTTON2}, {NC_OPTION}, {NC_HIGHLIGHT}}; extern int mouse_x; extern int mouse_y; @@ -169,18 +157,7 @@ void newcam_init_settings(void) newcam_invertY = (u8)configCameraInvertY; newcam_mouse = (u8)configCameraMouse; newcam_analogue = (u8)configEnableCamera; -} - -void newcam_save_settings(void) -{ - configCameraXSens = newcam_sensitivityX; - configCameraYSens = newcam_sensitivityY; - configCameraAggr = newcam_aggression; - configCameraPan = newcam_panlevel; - configCameraInvertX = newcam_invertX != 0; - configCameraInvertY = newcam_invertY != 0; - configEnableCamera = newcam_analogue != 0; - configCameraMouse = newcam_mouse != 0; + newcam_degrade = (f32)configCameraDegrade / 100.0f; } /** Mathematic calculations. This stuffs so basic even *I* understand it lol @@ -294,7 +271,7 @@ static void newcam_rotate_button(void) #ifdef noaccel newcam_yaw_acc = 0; #else - newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); + newcam_yaw_acc -= (newcam_yaw_acc*newcam_degrade); #endif } @@ -306,7 +283,7 @@ static void newcam_rotate_button(void) #ifdef noaccel newcam_tilt_acc = 0; #else - newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); + newcam_tilt_acc -= (newcam_tilt_acc*newcam_degrade); #endif newcam_framessincec[0] += 1; @@ -372,13 +349,13 @@ static void newcam_rotate_button(void) else { newcam_cstick_down = 0; - newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); + newcam_yaw_acc -= (newcam_yaw_acc*newcam_degrade); } if (ABS(gPlayer2Controller->stickY) > 20 && newcam_modeflags & NC_FLAG_YTURN) newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,(-gPlayer2Controller->stickY/4)); else - newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); + newcam_tilt_acc -= (newcam_tilt_acc*newcam_degrade); } if (newcam_mouse == 1) @@ -404,8 +381,8 @@ static void newcam_zoom_button(void) newcam_distance = newcam_distance_target; } - //When you press L and R together, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time. - if (gPlayer1Controller->buttonDown & L_TRIG && gPlayer1Controller->buttonDown & R_TRIG && newcam_modeflags & NC_FLAG_ZOOM) + //When you press L, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time. + if (gPlayer1Controller->buttonDown & L_TRIG && newcam_modeflags & NC_FLAG_ZOOM) { newcam_yaw_target = -gMarioState->faceAngle[1]-0x4000; newcam_centering = 1; @@ -673,234 +650,10 @@ void newcam_loop(struct Camera *c) newcam_position_cam(); newcam_find_fixed(); if (gMarioObject) - newcam_apply_values(c); + newcam_apply_values(c); //Just some visual information on the values of the camera. utilises ifdef because it's better at runtime. #ifdef NEWCAM_DEBUG newcam_diagnostics(); #endif // NEWCAM_DEBUG } - - - -//Displays a box. -void newcam_display_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b) -{ - gDPPipeSync(gDisplayListHead++); - gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); - gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); - gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255)); - gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1); - gDPPipeSync(gDisplayListHead++); - gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); -} - -//I actually took the time to redo this, properly. Lmao. Please don't bully me over this anymore :( -void newcam_change_setting(u8 toggle) -{ - switch (newcam_option_selection) - { - case 0: - newcam_analogue ^= 1; - break; - case 1: - newcam_mouse ^= 1; - break; - case 2: - newcam_sensitivityX = newcam_clamp(newcam_sensitivityX + toggle, 10, 250); - break; - case 3: - newcam_sensitivityY = newcam_clamp(newcam_sensitivityY + toggle, 10, 250); - break; - case 4: - newcam_invertX ^= 1; - break; - case 5: - newcam_invertY ^= 1; - break; - case 6: - newcam_aggression = newcam_clamp(newcam_aggression + toggle, 0, 100); - break; - case 7: - newcam_panlevel = newcam_clamp(newcam_panlevel + toggle, 0, 100); - break; - } -} - -void newcam_text(s16 x, s16 y, u8 str[], u8 col) -{ - u8 textX; - textX = get_str_x_pos_from_center(x,str,10.0f); - gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); - print_generic_string(textX+1,y-1,str); - if (col != 0) - { - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - } - else - { - gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255); - } - print_generic_string(textX,y,str); -} - -//Options menu -void newcam_display_options() -{ - u8 i = 0; - u8 newstring[32]; - s16 scroll; - s16 scrollpos; - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - print_hud_lut_string(HUD_LUT_GLOBAL, 118, 40, newcam_strings[2]); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); - - if (newcam_total>4) - { - newcam_display_box(272,90,280,208,0x80,0x80,0x80); - scrollpos = (54)*((f32)newcam_option_scroll/(newcam_total-4)); - newcam_display_box(272,90+scrollpos,280,154+scrollpos,0xFF,0xFF,0xFF); - } - - - gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); - gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT); - for (i = 0; i < newcam_total; i++) - { - scroll = 140-(32*i)+(newcam_option_scroll*32); - if (scroll <= 140 && scroll > 32) - { - newcam_text(160,scroll,newcam_options[i],newcam_option_selection-i); - switch (i) - { - case 0: - newcam_text(160,scroll-12,newcam_flags[newcam_analogue],newcam_option_selection-i); - break; - case 1: - newcam_text(160,scroll-12,newcam_flags[newcam_mouse],newcam_option_selection-i); - break; - case 2: - int_to_str(newcam_sensitivityX,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 3: - int_to_str(newcam_sensitivityY,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 4: - newcam_text(160,scroll-12,newcam_flags[newcam_invertX],newcam_option_selection-i); - break; - case 5: - newcam_text(160,scroll-12,newcam_flags[newcam_invertY],newcam_option_selection-i); - break; - case 6: - int_to_str(newcam_aggression,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 7: - int_to_str(newcam_panlevel,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - } - } - } - gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - gSPDisplayList(gDisplayListHead++, dl_ia_text_end); - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); - print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]); - print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); -} - -//This has been separated for interesting reasons. Don't question it. -void newcam_render_option_text(void) -{ - gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); - newcam_text(278,212,newcam_strings[newcam_option_open],1); - gSPDisplayList(gDisplayListHead++, dl_ia_text_end); -} - -void newcam_check_pause_buttons() -{ - if (gPlayer1Controller->buttonPressed & R_TRIG) - { - if (newcam_option_open == 0) - { - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - newcam_option_open = 1; - } - else - { - #ifndef nosound - play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs); - #endif - newcam_option_open = 0; - newcam_save_settings(); - } - } - - if (newcam_option_open) - { - if (ABS(gPlayer1Controller->stickY) > 60) - { - newcam_option_timer -= 1; - if (newcam_option_timer <= 0) - { - switch (newcam_option_index) - { - case 0: newcam_option_index++; newcam_option_timer += 10; break; - default: newcam_option_timer += 3; break; - } - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - if (gPlayer1Controller->stickY >= 60) - { - newcam_option_selection--; - if (newcam_option_selection < 0) - newcam_option_selection = newcam_total-1; - } - else - { - newcam_option_selection++; - if (newcam_option_selection >= newcam_total) - newcam_option_selection = 0; - } - } - } - else - if (ABS(gPlayer1Controller->stickX) > 60) - { - newcam_option_timer -= 1; - if (newcam_option_timer <= 0) - { - switch (newcam_option_index) - { - case 0: newcam_option_index++; newcam_option_timer += 10; break; - default: newcam_option_timer += 3; break; - } - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - if (gPlayer1Controller->stickX >= 60) - newcam_change_setting(1); - else - newcam_change_setting(-1); - } - } - else - { - newcam_option_timer = 0; - newcam_option_index = 0; - } - - while (newcam_option_scroll - newcam_option_selection < -3 && newcam_option_selection > newcam_option_scroll) - newcam_option_scroll +=1; - while (newcam_option_scroll + newcam_option_selection > 0 && newcam_option_selection < newcam_option_scroll) - newcam_option_scroll -=1; - } -} diff --git a/src/game/game_init.c b/src/game/game_init.c index 939383a2..ffcf7a18 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -3,6 +3,7 @@ #include "sm64.h" #include "audio/external.h" #include "buffers/buffers.h" +#include "gfx_dimensions.h" #include "buffers/gfx_output_buffer.h" #include "buffers/framebuffers.h" #include "buffers/zbuffer.h" @@ -152,8 +153,9 @@ void clear_frame_buffer(s32 a) { gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); gDPSetFillColor(gDisplayListHead++, a); - gDPFillRectangle(gDisplayListHead++, 0, BORDER_HEIGHT, SCREEN_WIDTH - 1, - SCREEN_HEIGHT - 1 - BORDER_HEIGHT); + + // Ratio-correct borderfill + gDPFillRectangle(gDisplayListHead++, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), BORDER_HEIGHT, GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, SCREEN_HEIGHT - BORDER_HEIGHT - 1); gDPPipeSync(gDisplayListHead++); @@ -217,13 +219,7 @@ void create_task_structure(void) { gGfxSPTask->msgqueue = &D_80339CB8; gGfxSPTask->msg = (OSMesg) 2; gGfxSPTask->task.t.type = M_GFXTASK; -#if TARGET_N64 - gGfxSPTask->task.t.ucode_boot = rspF3DBootStart; - gGfxSPTask->task.t.ucode_boot_size = ((u8 *) rspF3DBootEnd - (u8 *) rspF3DBootStart); - gGfxSPTask->task.t.flags = 0; - gGfxSPTask->task.t.ucode = rspF3DStart; - gGfxSPTask->task.t.ucode_data = rspF3DDataStart; -#endif + gGfxSPTask->task.t.ucode_size = SP_UCODE_SIZE; // (this size is ignored) gGfxSPTask->task.t.ucode_data_size = SP_UCODE_DATA_SIZE; gGfxSPTask->task.t.dram_stack = (u64 *) gGfxSPTaskStack; @@ -267,33 +263,9 @@ void end_master_display_list(void) { create_task_structure(); } -void draw_reset_bars(void) { - s32 sp24; - s32 sp20; - s32 fbNum; - u64 *sp18; - - if (gResetTimer != 0 && D_8032C648 < 15) { - if (sCurrFBNum == 0) { - fbNum = 2; - } else { - fbNum = sCurrFBNum - 1; - } - - sp18 = (u64 *) PHYSICAL_TO_VIRTUAL(gPhysicalFrameBuffers[fbNum]); - sp18 += D_8032C648++ * (SCREEN_WIDTH / 4); - - for (sp24 = 0; sp24 < ((SCREEN_HEIGHT / 16) + 1); sp24++) { - // Must be on one line to match -O2 - for (sp20 = 0; sp20 < (SCREEN_WIDTH / 4); sp20++) *sp18++ = 0; - sp18 += ((SCREEN_WIDTH / 4) * 14); - } - } - - osWritebackDCacheAll(); - osRecvMesg(&gGameVblankQueue, &D_80339BEC, OS_MESG_BLOCK); - osRecvMesg(&gGameVblankQueue, &D_80339BEC, OS_MESG_BLOCK); -} +//void draw_reset_bars(void) { // TARGET_64 only +// Stubbed. Only N64 target uses this +// } void rendering_init(void) { gGfxPool = &gGfxPools[0]; @@ -525,7 +497,7 @@ void read_controller_inputs(void) { controller->stickMag = 0; } } - + } #else for (i = 0; i < 2; i++) { @@ -626,16 +598,12 @@ void setup_game_memory(void) { load_segment_decompress(2, _segment2_mio0SegmentRomStart, _segment2_mio0SegmentRomEnd); } -#ifndef TARGET_N64 + static struct LevelCommand *levelCommandAddr; -#endif // main game loop thread. runs forever as long as the game // continues. void thread5_game_loop(UNUSED void *arg) { -#ifdef TARGET_N64 - struct LevelCommand *levelCommandAddr; -#endif setup_game_memory(); #ifdef VERSION_SH @@ -655,32 +623,21 @@ void thread5_game_loop(UNUSED void *arg) { play_music(SEQ_PLAYER_SFX, SEQUENCE_ARGS(0, SEQ_SOUND_PLAYER), 0); set_sound_mode(save_file_get_sound_mode()); -#ifdef TARGET_N64 - func_80247ED8(); - rendering_init(); - - while (1) { -#else gGlobalTimer++; } void game_loop_one_iteration(void) { -#endif // if the reset timer is active, run the process to reset the game. - if (gResetTimer) { + //if (gResetTimer) { +// draw_reset_bars(); (N64 target only?) +//} -#ifdef TARGET_N64 - draw_reset_bars(); - continue; -#else - return; -#endif - } profiler_log_thread5_time(THREAD5_START); // if any controllers are plugged in, start read the data for when // read_controller_inputs is called later. if (gControllerBits) { + #ifdef VERSION_SH block_until_rumble_pak_free(); #endif @@ -699,7 +656,5 @@ void game_loop_one_iteration(void) { // amount of free space remaining. print_text_fmt_int(180, 20, "BUF %d", gGfxPoolEnd - (u8 *) gDisplayListHead); } -#ifdef TARGET_N64 - } -#endif +// } was here for ifdef targ 64 } diff --git a/src/game/game_init.h b/src/game/game_init.h index 019bad4d..10dd2f86 100644 --- a/src/game/game_init.h +++ b/src/game/game_init.h @@ -71,7 +71,7 @@ extern void clear_viewport(Vp *, s32); void make_viewport_clip_rect(Vp *viewport); extern void init_render_image(void); extern void end_master_display_list(void); -extern void draw_reset_bars(void); +//extern void draw_reset_bars(void); Target_64 only. Not used extern void rendering_init(void); extern void config_gfx_pool(void); extern void display_and_vsync(void); diff --git a/src/game/geo_misc.c b/src/game/geo_misc.c index af0cfa6f..afd5e289 100644 --- a/src/game/geo_misc.c +++ b/src/game/geo_misc.c @@ -46,11 +46,8 @@ s8 gFlyingCarpetState; * * Texture coordinates are s10.5 fixed-point, which means you should left-shift the actual coordinates by 5. */ -#ifdef TARGET_N64 -void make_vertex(Vtx *vtx, s32 n, s16 x, s16 y, s16 z, s16 tx, s16 ty, u8 r, u8 g, u8 b, u8 a) { -#else void make_vertex(Vtx *vtx, s32 n, f32 x, f32 y, f32 z, s16 tx, s16 ty, u8 r, u8 g, u8 b, u8 a) { -#endif + vtx[n].v.ob[0] = x; vtx[n].v.ob[1] = y; vtx[n].v.ob[2] = z; diff --git a/src/game/geo_misc.h b/src/game/geo_misc.h index 5646568d..fde7095e 100644 --- a/src/game/geo_misc.h +++ b/src/game/geo_misc.h @@ -12,15 +12,9 @@ enum FlyingCarpetState extern s8 gFlyingCarpetState; -#ifdef TARGET_N64 -extern void make_vertex( - Vtx *vtx, s32 n, s16 x, s16 y, s16 z, s16 tx, s16 ty, u8 r, u8 g, u8 b, u8 a -); -#else extern void make_vertex( Vtx *vtx, s32 n, f32 x, f32 y, f32 z, s16 tx, s16 ty, u8 r, u8 g, u8 b, u8 a ); -#endif extern s16 round_float(f32); extern Gfx *geo_exec_inside_castle_light(s32 callContext, struct GraphNode *node, f32 mtx[4][4]); extern Gfx *geo_exec_flying_carpet_timer_update(s32 callContext, struct GraphNode *node, diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index bfcbf42b..baaeaf71 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -22,6 +22,9 @@ #ifdef BETTERCAMERA #include "bettercamera.h" #endif +#ifdef EXT_OPTIONS_MENU +#include "options_menu.h" +#endif extern Gfx *gDisplayListHead; extern s16 gCurrCourseNum; @@ -131,14 +134,7 @@ void create_dl_identity_matrix(void) { return; } -#ifdef TARGET_N64 - matrix->m[0][0] = 0x00010000; matrix->m[1][0] = 0x00000000; matrix->m[2][0] = 0x00000000; matrix->m[3][0] = 0x00000000; - matrix->m[0][1] = 0x00000000; matrix->m[1][1] = 0x00010000; matrix->m[2][1] = 0x00000000; matrix->m[3][1] = 0x00000000; - matrix->m[0][2] = 0x00000001; matrix->m[1][2] = 0x00000000; matrix->m[2][2] = 0x00000000; matrix->m[3][2] = 0x00000000; - matrix->m[0][3] = 0x00000000; matrix->m[1][3] = 0x00000001; matrix->m[2][3] = 0x00000000; matrix->m[3][3] = 0x00000000; -#else guMtxIdent(matrix); -#endif gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); @@ -1802,25 +1798,18 @@ void render_dialog_entries(void) { render_dialog_box_type(dialog, dialog->linesPerBox); gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, -#ifdef TARGET_N64 - ensure_nonnegative(dialog->leftOffset), -#else + 0, -#endif ensure_nonnegative(DIAG_VAL2 - dialog->width), #ifdef VERSION_EU -#ifdef TARGET_N64 - ensure_nonnegative(dialog->leftOffset + DIAG_VAL3 / gDialogBoxScale), -#else + SCREEN_WIDTH, -#endif + ensure_nonnegative((240 - dialog->width) + ((dialog->linesPerBox * 80) / DIAG_VAL4) / gDialogBoxScale)); #else -#ifdef TARGET_N64 - ensure_nonnegative(DIAG_VAL3 + dialog->leftOffset), -#else + SCREEN_WIDTH, -#endif + ensure_nonnegative(240 + ((dialog->linesPerBox * 80) / DIAG_VAL4) - dialog->width)); #endif #if defined(VERSION_JP) || defined(VERSION_SH) @@ -2133,12 +2122,9 @@ void shade_screen(void) { // This is a bit weird. It reuses the dialog text box (width 130, height -80), // so scale to at least fit the screen. -#ifdef TARGET_N64 - create_dl_scale_matrix(MENU_MTX_NOPUSH, 2.6f, 3.4f, 1.0f); -#else + create_dl_scale_matrix(MENU_MTX_NOPUSH, GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_HEIGHT / 130.0f, 3.0f, 1.0f); -#endif gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 110); gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box); @@ -2388,12 +2374,6 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) { { TEXT_EXIT_COURSE_DE } }; - u8 textExitGame[][22] ={ - { TEXT_EXIT_GAME }, - { TEXT_EXIT_GAME_FR }, - { TEXT_EXIT_GAME_DE } - }; - u8 textCameraAngleR[][24] = { { TEXT_CAMERA_ANGLE_R }, { TEXT_CAMERA_ANGLE_R_FR }, @@ -2401,27 +2381,23 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) { }; #define textContinue textContinue[gInGameLanguage] #define textExitCourse textExitCourse[gInGameLanguage] -#define textExitGame textExitGame[gInGameLanguage] #define textCameraAngleR textCameraAngleR[gInGameLanguage] #else u8 textContinue[] = { TEXT_CONTINUE }; u8 textExitCourse[] = { TEXT_EXIT_COURSE }; - u8 textExitGame[] = { TEXT_EXIT_GAME }; u8 textCameraAngleR[] = { TEXT_CAMERA_ANGLE_R }; #endif - handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 4); // Index max raised to 4 from 3 + handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 3); gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); print_generic_string(x + 10, y - 2, textContinue); print_generic_string(x + 10, y - 17, textExitCourse); - print_generic_string(x + 10, y - 33, textExitGame); - - if (index[0] != 4) { - print_generic_string(x + 10, y - 48, textCameraAngleR); + if (index[0] != 3) { + print_generic_string(x + 10, y - 33, textCameraAngleR); gSPDisplayList(gDisplayListHead++, dl_ia_text_end); create_dl_translation_matrix(MENU_MTX_PUSH, x - X_VAL8, (y - ((index[0] - 1) * yIndex)) - Y_VAL8, 0); @@ -2429,10 +2405,8 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) { gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); gSPDisplayList(gDisplayListHead++, dl_draw_triangle); gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); - } - - if (index[0] == 4) { - render_pause_camera_options(x - 42, y - 57, &gDialogCameraAngleIndex, 110); + } else { + render_pause_camera_options(x - 42, y - 42, &gDialogCameraAngleIndex, 110); } } @@ -2629,9 +2603,8 @@ s16 render_pause_courses_and_castle(void) { #ifdef VERSION_EU gInGameLanguage = eu_get_language(); #endif -#ifdef BETTERCAMERA - if (newcam_option_open == 0) - { +#ifdef EXT_OPTIONS_MENU + if (optmenu_open == 0) { #endif switch (gDialogBoxState) { case DIALOG_STATE_OPENING: @@ -2673,7 +2646,7 @@ s16 render_pause_courses_and_castle(void) { gDialogBoxState = DIALOG_STATE_OPENING; gMenuMode = -1; - if (gDialogLineNum == 2 || gDialogLineNum == 3) { + if (gDialogLineNum == 2) { num = gDialogLineNum; } else { num = 1; @@ -2708,15 +2681,13 @@ s16 render_pause_courses_and_castle(void) { if (gDialogTextAlpha < 250) { gDialogTextAlpha += 25; } -#ifdef BETTERCAMERA - } - else - { +#ifdef EXT_OPTIONS_MENU + } else { shade_screen(); - newcam_display_options(); + optmenu_draw(); } - newcam_check_pause_buttons(); - newcam_render_option_text(); + optmenu_check_buttons(); + optmenu_draw_prompt(); #endif return 0; diff --git a/src/game/level_update.c b/src/game/level_update.c index b2450c0b..3fa795f9 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -32,6 +32,8 @@ #include "../pc/configfile.h" #define CONFIG_FILE "sm64config.txt" +#include "pc/cliopts.h" + #define PLAY_MODE_NORMAL 0 #define PLAY_MODE_PAUSED 2 #define PLAY_MODE_CHANGE_AREA 3 @@ -176,7 +178,8 @@ s8 D_8032C9E0 = 0; u8 unused3[4]; u8 unused4[2]; - +// For configfile intro skipping +extern unsigned int configSkipIntro; void basic_update(s16 *arg); @@ -1214,7 +1217,7 @@ s32 init_level(void) { if (gMarioState->action != ACT_UNINITIALIZED) { if (save_file_exists(gCurrSaveFileNum - 1)) { set_mario_action(gMarioState, ACT_IDLE, 0); - } else { + } else if (gCLIOpts.SkipIntro == 0 && configSkipIntro == 0) { set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0); val4 = 1; } @@ -1284,7 +1287,7 @@ s32 lvl_init_from_save_file(UNUSED s16 arg0, s32 levelNum) { #endif sWarpDest.type = WARP_TYPE_NOT_WARPING; sDelayedWarpOp = WARP_OP_NONE; - gShouldNotPlayCastleMusic = !save_file_exists(gCurrSaveFileNum - 1); + gShouldNotPlayCastleMusic = !save_file_exists(gCurrSaveFileNum - 1) && gCLIOpts.SkipIntro == 0 && configSkipIntro == 0; gCurrLevelNum = levelNum; gCurrCourseNum = COURSE_NONE; diff --git a/src/game/macro_special_objects.c b/src/game/macro_special_objects.c index 63c63d3b..d9318971 100644 --- a/src/game/macro_special_objects.c +++ b/src/game/macro_special_objects.c @@ -328,7 +328,7 @@ void spawn_special_objects(s16 areaIndex, s16 **specialObjList) { } } -#ifndef TARGET_N64 +// PC Port, so always use below u32 get_special_objects_size(s16 *data) { s16 *startPos = data; s32 numOfSpecialObjects; @@ -372,4 +372,3 @@ u32 get_special_objects_size(s16 *data) { return data - startPos; } -#endif diff --git a/src/game/macro_special_objects.h b/src/game/macro_special_objects.h index 33fee18c..37851cc9 100644 --- a/src/game/macro_special_objects.h +++ b/src/game/macro_special_objects.h @@ -16,8 +16,6 @@ extern void spawn_macro_abs_special(u32 model, const BehaviorScript *behavior, s extern void spawn_macro_objects(s16 areaIndex, s16 * macroObjList); extern void spawn_macro_objects_hardcoded(s16 areaIndex, s16 * macroObjList); extern void spawn_special_objects(s16 areaIndex, s16 ** specialObjList); -#ifndef TARGET_N64 extern u32 get_special_objects_size(s16 *data); -#endif #endif /* MACRO_SPECIAL_OBJECTS_H */ diff --git a/src/game/memory.c b/src/game/memory.c index da4107fc..14c332cc 100644 --- a/src/game/memory.c +++ b/src/game/memory.c @@ -1,7 +1,5 @@ #include -#ifndef TARGET_N64 #include -#endif #include "sm64.h" @@ -78,27 +76,6 @@ void *get_segment_base_addr(s32 segment) { return (void *) (sSegmentTable[segment] | 0x80000000); } -#ifdef TARGET_N64 -void *segmented_to_virtual(const void *addr) { - size_t segment = (uintptr_t) addr >> 24; - size_t offset = (uintptr_t) addr & 0x00FFFFFF; - - return (void *) ((sSegmentTable[segment] + offset) | 0x80000000); -} - -void *virtual_to_segmented(u32 segment, const void *addr) { - size_t offset = ((uintptr_t) addr & 0x1FFFFFFF) - sSegmentTable[segment]; - - return (void *) ((segment << 24) + offset); -} - -void move_segment_table_to_dmem(void) { - s32 i; - - for (i = 0; i < 16; i++) - gSPSegment(gDisplayListHead++, i, sSegmentTable[i]); -} -#else void *segmented_to_virtual(const void *addr) { return (void *) addr; } @@ -109,7 +86,7 @@ void *virtual_to_segmented(u32 segment, const void *addr) { void move_segment_table_to_dmem(void) { } -#endif + /** * Initialize the main memory pool. This pool is conceptually a pair of stacks @@ -248,22 +225,8 @@ u32 main_pool_pop_state(void) { */ static void dma_read(u8 *dest, u8 *srcStart, u8 *srcEnd) { u32 size = ALIGN16(srcEnd - srcStart); -#ifdef TARGET_N64 - osInvalDCache(dest, size); - while (size != 0) { - u32 copySize = (size >= 0x1000) ? 0x1000 : size; - osPiStartDma(&gDmaIoMesg, OS_MESG_PRI_NORMAL, OS_READ, (uintptr_t) srcStart, dest, copySize, - &gDmaMesgQueue); - osRecvMesg(&gDmaMesgQueue, &D_80339BEC, OS_MESG_BLOCK); - - dest += copySize; - srcStart += copySize; - size -= copySize; - } -#else memcpy(dest, srcStart, srcEnd - srcStart); -#endif } /** @@ -281,103 +244,6 @@ static void *dynamic_dma_read(u8 *srcStart, u8 *srcEnd, u32 side) { return dest; } -#ifdef TARGET_N64 -/** - * Load data from ROM into a newly allocated block, and set the segment base - * address to this block. - */ -void *load_segment(s32 segment, u8 *srcStart, u8 *srcEnd, u32 side) { - void *addr = dynamic_dma_read(srcStart, srcEnd, side); - - if (addr != NULL) { - set_segment_base_addr(segment, addr); - } - return addr; -} - -/* - * Allocate a block of memory starting at destAddr and ending at the righthand - * end of the memory pool. Then copy srcStart through srcEnd from ROM to this - * block. - * If this block is not large enough to hold the ROM data, or that portion - * of the pool is already allocated, return NULL. - */ -void *load_to_fixed_pool_addr(u8 *destAddr, u8 *srcStart, u8 *srcEnd) { - void *dest = NULL; - u32 srcSize = ALIGN16(srcEnd - srcStart); - u32 destSize = ALIGN16((u8 *) sPoolListHeadR - destAddr); - - if (srcSize <= destSize) { - dest = main_pool_alloc(destSize, MEMORY_POOL_RIGHT); - if (dest != NULL) { - bzero(dest, destSize); - osWritebackDCacheAll(); - dma_read(dest, srcStart, srcEnd); - osInvalICache(dest, destSize); - osInvalDCache(dest, destSize); - } - } else { - } - return dest; -} - -/** - * Decompress the block of ROM data from srcStart to srcEnd and return a - * pointer to an allocated buffer holding the decompressed data. Set the - * base address of segment to this address. - */ -void *load_segment_decompress(s32 segment, u8 *srcStart, u8 *srcEnd) { - void *dest = NULL; - - u32 compSize = ALIGN16(srcEnd - srcStart); - u8 *compressed = main_pool_alloc(compSize, MEMORY_POOL_RIGHT); - - // Decompressed size from mio0 header - u32 *size = (u32 *) (compressed + 4); - - if (compressed != NULL) { - dma_read(compressed, srcStart, srcEnd); - dest = main_pool_alloc(*size, MEMORY_POOL_LEFT); - if (dest != NULL) { - decompress(compressed, dest); - set_segment_base_addr(segment, dest); - main_pool_free(compressed); - } else { - } - } else { - } - return dest; -} - -void *load_segment_decompress_heap(u32 segment, u8 *srcStart, u8 *srcEnd) { - UNUSED void *dest = NULL; - u32 compSize = ALIGN16(srcEnd - srcStart); - u8 *compressed = main_pool_alloc(compSize, MEMORY_POOL_RIGHT); - UNUSED u32 *pUncSize = (u32 *) (compressed + 4); - - if (compressed != NULL) { - dma_read(compressed, srcStart, srcEnd); - decompress(compressed, gDecompressionHeap); - set_segment_base_addr(segment, gDecompressionHeap); - main_pool_free(compressed); - } else { - } - return gDecompressionHeap; -} - -void load_engine_code_segment(void) { - void *startAddr = (void *) SEG_ENGINE; - u32 totalSize = SEG_FRAMEBUFFERS - SEG_ENGINE; - UNUSED u32 alignedSize = ALIGN16(_engineSegmentRomEnd - _engineSegmentRomStart); - - bzero(startAddr, totalSize); - osWritebackDCacheAll(); - dma_read(startAddr, _engineSegmentRomStart, _engineSegmentRomEnd); - osInvalICache(startAddr, totalSize); - osInvalDCache(startAddr, totalSize); -} -#endif - /** * Allocate an allocation-only pool from the main pool. This pool doesn't * support freeing allocated memory. diff --git a/src/game/memory.h b/src/game/memory.h index b392728e..0639ccef 100644 --- a/src/game/memory.h +++ b/src/game/memory.h @@ -37,19 +37,11 @@ u32 main_pool_available(void); u32 main_pool_push_state(void); u32 main_pool_pop_state(void); -#ifdef TARGET_N64 -void *load_segment(s32 segment, u8 *srcStart, u8 *srcEnd, u32 side); -void *load_to_fixed_pool_addr(u8 *destAddr, u8 *srcStart, u8 *srcEnd); -void *load_segment_decompress(s32 segment, u8 *srcStart, u8 *srcEnd); -void *load_segment_decompress_heap(u32 segment, u8 *srcStart, u8 *srcEnd); -void load_engine_code_segment(void); -#else #define load_segment(...) #define load_to_fixed_pool_addr(...) #define load_segment_decompress(...) #define load_segment_decompress_heap(...) #define load_engine_code_segment(...) -#endif struct AllocOnlyPool *alloc_only_pool_init(u32 size, u32 side); void *alloc_only_pool_alloc(struct AllocOnlyPool *pool, s32 size); diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index 0e2c7c86..68cebe5d 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -527,11 +527,15 @@ void set_object_visibility(struct Object *obj, s32 dist) { f32 objY = obj->oPosY; f32 objZ = obj->oPosZ; +#ifndef NODRAWINGDISTANCE if (is_point_within_radius_of_mario(objX, objY, objZ, dist) == TRUE) { +#endif obj->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; +#ifndef NODRAWINGDISTANCE } else { obj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; } +#endif } /** diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 42ad0e57..e05f7743 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -984,7 +984,7 @@ BAD_RETURN(s16) cur_obj_reverse_animation(void) { BAD_RETURN(s32) cur_obj_extend_animation_if_at_end(void) { s32 sp4 = o->header.gfx.unk38.animFrame; s32 sp0 = o->header.gfx.unk38.curAnim->unk08 - 2; - + if (sp4 == sp0) o->header.gfx.unk38.animFrame--; } @@ -1595,6 +1595,10 @@ void obj_set_billboard(struct Object *obj) { obj->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD; } +void obj_set_cylboard(struct Object *obj) { + obj->header.gfx.node.flags |= GRAPH_RENDER_CYLBOARD; +} + void cur_obj_set_hitbox_radius_and_height(f32 radius, f32 height) { o->hitboxRadius = radius; o->hitboxHeight = height; diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 80806bde..577f8f66 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -292,6 +292,7 @@ extern void cur_obj_shake_y(f32); void cur_obj_start_cam_event(struct Object *obj, s32 cameraEvent); // extern ? set_mario_interact_hoot_if_in_range(?); void obj_set_billboard(struct Object *a0); +void obj_set_cylboard(struct Object *a0); void cur_obj_set_hitbox_radius_and_height(f32,f32); void cur_obj_set_hurtbox_radius_and_height(f32,f32); // extern ? obj_spawn_loot_coins(?); diff --git a/src/game/options_menu.c b/src/game/options_menu.c new file mode 100644 index 00000000..59dfc23e --- /dev/null +++ b/src/game/options_menu.c @@ -0,0 +1,526 @@ +#ifdef EXT_OPTIONS_MENU + +#include "sm64.h" +#include "include/text_strings.h" +#include "engine/math_util.h" +#include "audio/external.h" +#include "game/camera.h" +#include "game/level_update.h" +#include "game/print.h" +#include "game/segment2.h" +#include "game/save_file.h" +#ifdef BETTERCAMERA +#include "game/bettercamera.h" +#endif +#include "game/mario_misc.h" +#include "game/game_init.h" +#include "game/ingame_menu.h" +#include "game/options_menu.h" +#include "pc/cliopts.h" +#include "pc/configfile.h" +#include "pc/controller/controller_api.h" + +#include + +u8 optmenu_open = 0; + +static u8 optmenu_binding = 0; +static u8 optmenu_bind_idx = 0; + +// How to add stuff: +// strings: add them to include/text_strings.h.in +// and to menuStr[] / opts*Str[] +// options: add them to the relevant options list +// menus: add a new submenu definition and a new +// option to the optsMain list + +static const u8 toggleStr[][16] = { + { TEXT_OPT_DISABLED }, + { TEXT_OPT_ENABLED }, +}; + +static const u8 menuStr[][32] = { + { TEXT_OPT_HIGHLIGHT }, + { TEXT_OPT_BUTTON1 }, + { TEXT_OPT_BUTTON2 }, + { TEXT_OPT_OPTIONS }, + { TEXT_OPT_CAMERA }, + { TEXT_OPT_CONTROLS }, + { TEXT_OPT_VIDEO }, + { TEXT_OPT_AUDIO }, + { TEXT_EXIT_GAME }, +}; + +static const u8 optsCameraStr[][32] = { + { TEXT_OPT_CAMX }, + { TEXT_OPT_CAMY }, + { TEXT_OPT_INVERTX }, + { TEXT_OPT_INVERTY }, + { TEXT_OPT_CAMC }, + { TEXT_OPT_CAMP }, + { TEXT_OPT_ANALOGUE }, + { TEXT_OPT_MOUSE }, + { TEXT_OPT_CAMD }, +}; + +static const u8 optsVideoStr[][32] = { + { TEXT_OPT_FSCREEN }, + { TEXT_OPT_TEXFILTER }, + { TEXT_OPT_NEAREST }, + { TEXT_OPT_LINEAR }, +}; + +static const u8 optsAudioStr[][32] = { + { TEXT_OPT_MVOLUME }, +}; + +static const u8 bindStr[][32] = { + { TEXT_OPT_UNBOUND }, + { TEXT_OPT_PRESSKEY }, + { TEXT_BIND_A }, + { TEXT_BIND_B }, + { TEXT_BIND_START }, + { TEXT_BIND_L }, + { TEXT_BIND_R }, + { TEXT_BIND_Z }, + { TEXT_BIND_C_UP }, + { TEXT_BIND_C_DOWN }, + { TEXT_BIND_C_LEFT }, + { TEXT_BIND_C_RIGHT }, + { TEXT_BIND_UP }, + { TEXT_BIND_DOWN }, + { TEXT_BIND_LEFT }, + { TEXT_BIND_RIGHT }, +}; + +static const u8 *filterChoices[] = { + optsVideoStr[2], + optsVideoStr[3], +}; + +enum OptType { + OPT_INVALID = 0, + OPT_TOGGLE, + OPT_CHOICE, + OPT_SCROLL, + OPT_SUBMENU, + OPT_BIND, + OPT_BUTTON, +}; + +struct SubMenu; + +struct Option { + enum OptType type; + const u8 *label; + union { + u32 *uval; + bool *bval; + }; + union { + struct { + const u8 **choices; + u32 numChoices; + }; + struct { + u32 scrMin; + u32 scrMax; + u32 scrStep; + }; + struct SubMenu *nextMenu; + void (*actionFn)(struct Option *, s32); + }; +}; + +struct SubMenu { + struct SubMenu *prev; // this is set at runtime to avoid needless complication + const u8 *label; + struct Option *opts; + s32 numOpts; + s32 select; + s32 scroll; +}; + +/* helper macros */ + +#define DEF_OPT_TOGGLE(lbl, bv) \ + { .type = OPT_TOGGLE, .label = lbl, .bval = bv } +#define DEF_OPT_SCROLL(lbl, uv, min, max, st) \ + { .type = OPT_SCROLL, .label = lbl, .uval = uv, .scrMin = min, .scrMax = max, .scrStep = st } +#define DEF_OPT_CHOICE(lbl, uv, ch) \ + { .type = OPT_CHOICE, .label = lbl, .uval = uv, .choices = ch, .numChoices = sizeof(ch) / sizeof(ch[0]) } +#define DEF_OPT_SUBMENU(lbl, nm) \ + { .type = OPT_SUBMENU, .label = lbl, .nextMenu = nm } +#define DEF_OPT_BIND(lbl, uv) \ + { .type = OPT_BIND, .label = lbl, .uval = uv } +#define DEF_OPT_BUTTON(lbl, act) \ + { .type = OPT_BUTTON, .label = lbl, .actionFn = act } +#define DEF_SUBMENU(lbl, opt) \ + { .label = lbl, .opts = opt, .numOpts = sizeof(opt) / sizeof(opt[0]) } + +/* button action functions */ + +static void optmenu_act_exit(UNUSED struct Option *self, s32 arg) { + if (!arg) exit(0); // only exit on A press and not directions +} + +/* submenu option lists */ + +#ifdef BETTERCAMERA +static struct Option optsCamera[] = { + DEF_OPT_TOGGLE( optsCameraStr[6], &configEnableCamera ), + DEF_OPT_TOGGLE( optsCameraStr[7], &configCameraMouse ), + DEF_OPT_TOGGLE( optsCameraStr[2], &configCameraInvertX ), + DEF_OPT_TOGGLE( optsCameraStr[3], &configCameraInvertY ), + DEF_OPT_SCROLL( optsCameraStr[0], &configCameraXSens, 10, 250, 1 ), + DEF_OPT_SCROLL( optsCameraStr[1], &configCameraYSens, 10, 250, 1 ), + DEF_OPT_SCROLL( optsCameraStr[4], &configCameraAggr, 0, 100, 1 ), + DEF_OPT_SCROLL( optsCameraStr[5], &configCameraPan, 0, 100, 1 ), + DEF_OPT_SCROLL( optsCameraStr[8], &configCameraDegrade, 0, 100, 1 ), +}; +#endif + +static struct Option optsControls[] = { + DEF_OPT_BIND( bindStr[ 2], configKeyA ), + DEF_OPT_BIND( bindStr[ 3], configKeyB ), + DEF_OPT_BIND( bindStr[ 4], configKeyStart ), + DEF_OPT_BIND( bindStr[ 5], configKeyL ), + DEF_OPT_BIND( bindStr[ 6], configKeyR ), + DEF_OPT_BIND( bindStr[ 7], configKeyZ ), + DEF_OPT_BIND( bindStr[ 8], configKeyCUp ), + DEF_OPT_BIND( bindStr[ 9], configKeyCDown ), + DEF_OPT_BIND( bindStr[10], configKeyCLeft ), + DEF_OPT_BIND( bindStr[11], configKeyCRight ), + DEF_OPT_BIND( bindStr[12], configKeyStickUp ), + DEF_OPT_BIND( bindStr[13], configKeyStickDown ), + DEF_OPT_BIND( bindStr[14], configKeyStickLeft ), + DEF_OPT_BIND( bindStr[15], configKeyStickRight ), +}; + +static struct Option optsVideo[] = { + DEF_OPT_TOGGLE( optsVideoStr[0], &configFullscreen ), + DEF_OPT_CHOICE( optsVideoStr[1], &configFiltering, filterChoices ), +}; + +static struct Option optsAudio[] = { + DEF_OPT_SCROLL( optsAudioStr[0], &configMasterVolume, 0, MAX_VOLUME, 1 ), +}; + +/* submenu definitions */ + +#ifdef BETTERCAMERA +static struct SubMenu menuCamera = DEF_SUBMENU( menuStr[4], optsCamera ); +#endif +static struct SubMenu menuControls = DEF_SUBMENU( menuStr[5], optsControls ); +static struct SubMenu menuVideo = DEF_SUBMENU( menuStr[6], optsVideo ); +static struct SubMenu menuAudio = DEF_SUBMENU( menuStr[7], optsAudio ); + +/* main options menu definition */ + +static struct Option optsMain[] = { +#ifdef BETTERCAMERA + DEF_OPT_SUBMENU( menuStr[4], &menuCamera ), +#endif + DEF_OPT_SUBMENU( menuStr[5], &menuControls ), + DEF_OPT_SUBMENU( menuStr[6], &menuVideo ), + DEF_OPT_SUBMENU( menuStr[7], &menuAudio ), + DEF_OPT_BUTTON ( menuStr[8], optmenu_act_exit ), +}; + +static struct SubMenu menuMain = DEF_SUBMENU( menuStr[3], optsMain ); + +/* implementation */ + +static s32 optmenu_option_timer = 0; +static u8 optmenu_hold_count = 0; + +static struct SubMenu *currentMenu = &menuMain; + +static inline s32 wrap_add(s32 a, const s32 b, const s32 min, const s32 max) { + a += b; + if (a < min) a = max - (min - a) + 1; + else if (a > max) a = min + (a - max) - 1; + return a; +} + +static void uint_to_hex(u32 num, u8 *dst) { + u8 places = 4; + while (places--) { + const u32 digit = num & 0xF; + dst[places] = digit; + num >>= 4; + } + dst[4] = DIALOG_CHAR_TERMINATOR; +} + +//Displays a box. +static void optmenu_draw_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b) { + gDPPipeSync(gDisplayListHead++); + gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); + gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); + gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255)); + gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1); + gDPPipeSync(gDisplayListHead++); + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); +} + +static void optmenu_draw_text(s16 x, s16 y, const u8 *str, u8 col) { + const u8 textX = get_str_x_pos_from_center(x, (u8*)str, 10.0f); + gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); + print_generic_string(textX+1, y-1, str); + if (col == 0) { + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + } else { + gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255); + } + print_generic_string(textX, y, str); +} + +static void optmenu_draw_opt(const struct Option *opt, s16 x, s16 y, u8 sel) { + u8 buf[32] = { 0 }; + + if (opt->type == OPT_SUBMENU || opt->type == OPT_BUTTON) + y -= 6; + + optmenu_draw_text(x, y, opt->label, sel); + + switch (opt->type) { + case OPT_TOGGLE: + optmenu_draw_text(x, y-13, toggleStr[(int)*opt->bval], sel); + break; + + case OPT_CHOICE: + optmenu_draw_text(x, y-13, opt->choices[*opt->uval], sel); + break; + + case OPT_SCROLL: + int_to_str(*opt->uval, buf); + optmenu_draw_text(x, y-13, buf, sel); + break; + + case OPT_BIND: + x = 112; + for (u8 i = 0; i < MAX_BINDS; ++i, x += 48) { + const u8 white = (sel && (optmenu_bind_idx == i)); + // TODO: button names + if (opt->uval[i] == VK_INVALID) { + if (optmenu_binding && white) + optmenu_draw_text(x, y-13, bindStr[1], 1); + else + optmenu_draw_text(x, y-13, bindStr[0], white); + } else { + uint_to_hex(opt->uval[i], buf); + optmenu_draw_text(x, y-13, buf, white); + } + } + break; + + default: break; + }; +} + +static void optmenu_opt_change(struct Option *opt, s32 val) { + switch (opt->type) { + case OPT_TOGGLE: + *opt->bval = !*opt->bval; + break; + + case OPT_CHOICE: + *opt->uval = wrap_add(*opt->uval, val, 0, opt->numChoices - 1); + break; + + case OPT_SCROLL: + *opt->uval = wrap_add(*opt->uval, opt->scrStep * val, opt->scrMin, opt->scrMax); + break; + + case OPT_SUBMENU: + opt->nextMenu->prev = currentMenu; + currentMenu = opt->nextMenu; + break; + + case OPT_BUTTON: + if (opt->actionFn) + opt->actionFn(opt, val); + break; + + case OPT_BIND: + if (val == 0xFF) { + // clear the bind + opt->uval[optmenu_bind_idx] = VK_INVALID; + } else if (val == 0) { + opt->uval[optmenu_bind_idx] = VK_INVALID; + optmenu_binding = 1; + controller_get_raw_key(); // clear the last key, which is probably A + } else { + optmenu_bind_idx = wrap_add(optmenu_bind_idx, val, 0, MAX_BINDS - 1); + } + break; + + default: break; + } +} + +static inline s16 get_hudstr_centered_x(const s16 sx, const u8 *str) { + const u8 *chr = str; + s16 len = 0; + while (*chr != GLOBAR_CHAR_TERMINATOR) ++chr, ++len; + return sx - len * 6; // stride is 12 +} + +//Options menu +void optmenu_draw(void) { + s16 scroll; + s16 scrollpos; + + const s16 labelX = get_hudstr_centered_x(160, currentMenu->label); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + print_hud_lut_string(HUD_LUT_GLOBAL, labelX, 40, currentMenu->label); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); + + if (currentMenu->numOpts > 4) { + optmenu_draw_box(272, 90, 280, 208, 0x80, 0x80, 0x80); + scrollpos = 54 * ((f32)currentMenu->scroll / (currentMenu->numOpts-4)); + optmenu_draw_box(272, 90+scrollpos, 280, 154+scrollpos, 0xFF, 0xFF, 0xFF); + } + + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT); + for (u8 i = 0; i < currentMenu->numOpts; i++) { + scroll = 140 - 32 * i + currentMenu->scroll * 32; + // FIXME: just start from the first visible option bruh + if (scroll <= 140 && scroll > 32) + optmenu_draw_opt(¤tMenu->opts[i], 160, scroll, (currentMenu->select == i)); + } + + gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); + print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90 + (32 * (currentMenu->select - currentMenu->scroll)), menuStr[0]); + print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90 + (32 * (currentMenu->select - currentMenu->scroll)), menuStr[0]); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); +} + +//This has been separated for interesting reasons. Don't question it. +void optmenu_draw_prompt(void) { + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + optmenu_draw_text(278, 212, menuStr[1 + optmenu_open], 0); + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); +} + +void optmenu_toggle(void) { + if (optmenu_open == 0) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + currentMenu = &menuMain; + optmenu_open = 1; + } else { + #ifndef nosound + play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs); + #endif + optmenu_open = 0; + #ifdef BETTERCAMERA + newcam_init_settings(); // load bettercam settings from config vars + #endif + controller_reconfigure(); // rebind using new config values + configfile_save(gCLIOpts.ConfigFile); + } +} + +void optmenu_check_buttons(void) { + if (optmenu_binding) { + u32 key = controller_get_raw_key(); + if (key != VK_INVALID) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + currentMenu->opts[currentMenu->select].uval[optmenu_bind_idx] = key; + optmenu_binding = 0; + optmenu_option_timer = 12; + } + return; + } + + if (gPlayer1Controller->buttonPressed & R_TRIG) + optmenu_toggle(); + + if (!optmenu_open) return; + + u8 allowInput = 0; + + optmenu_option_timer--; + if (optmenu_option_timer <= 0) { + if (optmenu_hold_count == 0) { + optmenu_hold_count++; + optmenu_option_timer = 10; + } else { + optmenu_option_timer = 3; + } + allowInput = 1; + } + + if (ABS(gPlayer1Controller->stickY) > 60) { + if (allowInput) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + + if (gPlayer1Controller->stickY >= 60) { + currentMenu->select--; + if (currentMenu->select < 0) + currentMenu->select = currentMenu->numOpts-1; + } else { + currentMenu->select++; + if (currentMenu->select >= currentMenu->numOpts) + currentMenu->select = 0; + } + + if (currentMenu->select < currentMenu->scroll) + currentMenu->scroll = currentMenu->select; + else if (currentMenu->select > currentMenu->scroll + 3) + currentMenu->scroll = currentMenu->select - 3; + } + } else if (ABS(gPlayer1Controller->stickX) > 60) { + if (allowInput) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + if (gPlayer1Controller->stickX >= 60) + optmenu_opt_change(¤tMenu->opts[currentMenu->select], 1); + else + optmenu_opt_change(¤tMenu->opts[currentMenu->select], -1); + } + } else if (gPlayer1Controller->buttonPressed & A_BUTTON) { + if (allowInput) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + optmenu_opt_change(¤tMenu->opts[currentMenu->select], 0); + } + } else if (gPlayer1Controller->buttonPressed & B_BUTTON) { + if (allowInput) { + if (currentMenu->prev) { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + currentMenu = currentMenu->prev; + } else { + // can't go back, exit the menu altogether + optmenu_toggle(); + } + } + } else if (gPlayer1Controller->buttonPressed & Z_TRIG) { + // HACK: clear binds with Z + if (allowInput && currentMenu->opts[currentMenu->select].type == OPT_BIND) + optmenu_opt_change(¤tMenu->opts[currentMenu->select], 0xFF); + } else if (gPlayer1Controller->buttonPressed & START_BUTTON) { + if (allowInput) optmenu_toggle(); + } else { + optmenu_hold_count = 0; + optmenu_option_timer = 0; + } +} + +#endif // EXT_OPTIONS_MENU diff --git a/src/game/options_menu.h b/src/game/options_menu.h new file mode 100644 index 00000000..4828ae9d --- /dev/null +++ b/src/game/options_menu.h @@ -0,0 +1,11 @@ +#ifndef OPTIONS_MENU_H +#define OPTIONS_MENU_H + +void optmenu_toggle(void); +void optmenu_draw(void); +void optmenu_draw_prompt(void); +void optmenu_check_buttons(void); + +extern u8 optmenu_open; + +#endif // OPTIONS_MENU_H diff --git a/src/game/print.c b/src/game/print.c index 42395b5f..56ab66d3 100644 --- a/src/game/print.c +++ b/src/game/print.c @@ -366,29 +366,6 @@ void add_glyph_texture(s8 glyphIndex) { gSPDisplayList(gDisplayListHead++, dl_hud_img_load_tex_block); } -#ifdef TARGET_N64 -/** - * Clips textrect into the boundaries defined. - */ -void clip_to_bounds(s32 *x, s32 *y) { - if (*x < TEXRECT_MIN_X) { - *x = TEXRECT_MIN_X; - } - - if (*x > TEXRECT_MAX_X) { - *x = TEXRECT_MAX_X; - } - - if (*y < TEXRECT_MIN_Y) { - *y = TEXRECT_MIN_Y; - } - - if (*y > TEXRECT_MAX_Y) { - *y = TEXRECT_MAX_Y; - } -} -#endif - /** * Renders the glyph that's set at the given position. */ @@ -398,9 +375,6 @@ void render_textrect(s32 x, s32 y, s32 pos) { s32 rectX; s32 rectY; -#ifdef TARGET_N64 - clip_to_bounds(&rectBaseX, &rectBaseY); -#endif rectX = rectBaseX; rectY = rectBaseY; gSPTextureRectangle(gDisplayListHead++, rectX << 2, rectY << 2, (rectX + 15) << 2, diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 93a9a8a3..a96eafbf 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -506,11 +506,7 @@ static void geo_process_background(struct GraphNodeBackground *node) { if (list != 0) { geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8); } else if (gCurGraphNodeMasterList != NULL) { -#ifdef TARGET_N64 - Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7); -#else Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 8); -#endif Gfx *gfx = gfxStart; gDPPipeSync(gfx++); @@ -757,9 +753,7 @@ static int obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) { // the amount of units between the center of the screen and the horizontal edge // given the distance from the object to the camera. -#ifndef TARGET_N64 hScreenEdge *= GFX_DIMENSIONS_ASPECT_RATIO; -#endif if (geo != NULL && geo->type == GRAPH_NODE_TYPE_CULLING_RADIUS) { cullingRadius = @@ -802,6 +796,9 @@ static void geo_process_object(struct Object *node) { if (node->header.gfx.throwMatrix != NULL) { mtxf_mul(gMatStack[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix, gMatStack[gMatStackIndex]); + } else if (node->header.gfx.node.flags & GRAPH_RENDER_CYLBOARD) { + mtxf_cylboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], + node->header.gfx.pos, gCurGraphNodeCamera->roll); } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) { mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], node->header.gfx.pos, gCurGraphNodeCamera->roll); diff --git a/src/game/save_file.c b/src/game/save_file.c index 6803fb69..b5112eb5 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -15,6 +15,12 @@ #define MENU_DATA_MAGIC 0x4849 #define SAVE_FILE_MAGIC 0x4441 +#define BSWAP16(x) \ + ( (((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00) ) +#define BSWAP32(x) \ + ( (((x) >> 24) & 0x000000FF) | (((x) >> 8) & 0x0000FF00) | \ + (((x) << 8) & 0x00FF0000) | (((x) << 24) & 0xFF000000) ) + STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match"); extern struct SaveBuffer gSaveBuffer; @@ -50,6 +56,38 @@ static void stub_save_file_1(void) { UNUSED s32 pad; } +/** + * Byteswap all multibyte fields in a SaveBlockSignature. + */ +static inline void bswap_signature(struct SaveBlockSignature *data) { + data->magic = BSWAP16(data->magic); + data->chksum = BSWAP16(data->chksum); // valid as long as the checksum is a literal sum +} + +/** + * Byteswap all multibyte fields in a MainMenuSaveData. + */ +static inline void bswap_menudata(struct MainMenuSaveData *data) { + for (int i = 0; i < NUM_SAVE_FILES; ++i) + data->coinScoreAges[i] = BSWAP32(data->coinScoreAges[i]); + data->soundMode = BSWAP16(data->soundMode); +#ifdef VERSION_EU + data->language = BSWAP16(data->language); +#endif + bswap_signature(&data->signature); +} + +/** + * Byteswap all multibyte fields in a SaveFile. + */ +static inline void bswap_savefile(struct SaveFile *data) { + data->capPos[0] = BSWAP16(data->capPos[0]); + data->capPos[1] = BSWAP16(data->capPos[1]); + data->capPos[2] = BSWAP16(data->capPos[2]); + data->flags = BSWAP32(data->flags); + bswap_signature(&data->signature); +} + /** * Read from EEPROM to a given address. * The EEPROM address is computed using the offset of the destination address from gSaveBuffer. @@ -80,16 +118,16 @@ static s32 read_eeprom_data(void *buffer, s32 size) { /** * Write data to EEPROM. - * The EEPROM address is computed using the offset of the source address from gSaveBuffer. + * The EEPROM address was originally computed using the offset of the source address from gSaveBuffer. * Try at most 4 times, and return 0 on success. On failure, return the status returned from * osEepromLongWrite. Unlike read_eeprom_data, return 1 if EEPROM isn't loaded. */ -static s32 write_eeprom_data(void *buffer, s32 size) { +static s32 write_eeprom_data(void *buffer, s32 size, const uintptr_t baseofs) { s32 status = 1; if (gEepromProbe != 0) { s32 triesLeft = 4; - u32 offset = (u32)((u8 *) buffer - (u8 *) &gSaveBuffer) >> 3; + u32 offset = (u32)baseofs >> 3; do { #ifdef VERSION_SH @@ -106,6 +144,41 @@ static s32 write_eeprom_data(void *buffer, s32 size) { return status; } +/** + * Wrappers that byteswap the data on LE platforms before writing it to 'EEPROM' + */ + +static inline s32 write_eeprom_savefile(const u32 file, const u32 slot, const u32 num) { + // calculate the EEPROM address using the file number and slot + const uintptr_t ofs = (u8*)&gSaveBuffer.files[file][slot] - (u8*)&gSaveBuffer; + +#if IS_BIG_ENDIAN + return write_eeprom_data(&gSaveBuffer.files[file][slot], num * sizeof(struct SaveFile), ofs); +#else + // byteswap the data and then write it + struct SaveFile sf[num]; + bcopy(&gSaveBuffer.files[file][slot], sf, num * sizeof(sf[0])); + for (u32 i = 0; i < num; ++i) bswap_savefile(&sf[i]); + return write_eeprom_data(&sf, sizeof(sf), ofs); +#endif +} + +static inline s32 write_eeprom_menudata(const u32 slot, const u32 num) { + // calculate the EEPROM address using the slot + const uintptr_t ofs = (u8*)&gSaveBuffer.menuData[slot] - (u8*)&gSaveBuffer; + +#if IS_BIG_ENDIAN + return write_eeprom_data(&gSaveBuffer.menuData[slot], num * sizeof(struct MainMenuSaveData), ofs); +#else + // byteswap the data and then write it + struct MainMenuSaveData md[num]; + bcopy(&gSaveBuffer.menuData[slot], md, num * sizeof(md[0])); + for (u32 i = 0; i < num; ++i) bswap_menudata(&md[i]); + return write_eeprom_data(&md, sizeof(md), ofs); +#endif +} + + /** * Sum the bytes in data to data + size - 2. The last two bytes are ignored * because that is where the checksum is stored. @@ -157,7 +230,7 @@ static void restore_main_menu_data(s32 srcSlot) { bcopy(&gSaveBuffer.menuData[srcSlot], &gSaveBuffer.menuData[destSlot], sizeof(gSaveBuffer.menuData[destSlot])); // Write destination data to EEPROM - write_eeprom_data(&gSaveBuffer.menuData[destSlot], sizeof(gSaveBuffer.menuData[destSlot])); + write_eeprom_menudata(destSlot, 1); } static void save_main_menu_data(void) { @@ -169,7 +242,7 @@ static void save_main_menu_data(void) { bcopy(&gSaveBuffer.menuData[0], &gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1])); // Write to EEPROM - write_eeprom_data(gSaveBuffer.menuData, sizeof(gSaveBuffer.menuData)); + write_eeprom_menudata(0, 2); gMainMenuDataModified = FALSE; } @@ -245,8 +318,35 @@ static void restore_save_file_data(s32 fileIndex, s32 srcSlot) { sizeof(gSaveBuffer.files[fileIndex][destSlot])); // Write destination data to EEPROM - write_eeprom_data(&gSaveBuffer.files[fileIndex][destSlot], - sizeof(gSaveBuffer.files[fileIndex][destSlot])); + write_eeprom_savefile(fileIndex, destSlot, 1); +} + +/** + * Check if the 'EEPROM' save has different endianness (e.g. it's from an actual N64). + */ +static u8 save_file_need_bswap(const struct SaveBuffer *buf) { + // check all signatures just in case + for (int i = 0; i < 2; ++i) { + if (buf->menuData[i].signature.magic == BSWAP16(MENU_DATA_MAGIC)) + return TRUE; + for (int j = 0; j < NUM_SAVE_FILES; ++j) { + if (buf->files[j][i].signature.magic == BSWAP16(SAVE_FILE_MAGIC)) + return TRUE; + } + } + return FALSE; +} + +/** + * Byteswap all multibyte fields in a SaveBuffer. + */ +static void save_file_bswap(struct SaveBuffer *buf) { + bswap_menudata(buf->menuData + 0); + bswap_menudata(buf->menuData + 1); + for (int i = 0; i < NUM_SAVE_FILES; ++i) { + bswap_savefile(buf->files[i] + 0); + bswap_savefile(buf->files[i] + 1); + } } void save_file_do_save(s32 fileIndex) { @@ -260,7 +360,7 @@ void save_file_do_save(s32 fileIndex) { sizeof(gSaveBuffer.files[fileIndex][1])); // Write to EEPROM - write_eeprom_data(gSaveBuffer.files[fileIndex], sizeof(gSaveBuffer.files[fileIndex])); + write_eeprom_savefile(fileIndex, 0, 2); gSaveFileModified = FALSE; } @@ -298,6 +398,9 @@ void save_file_load_all(void) { bzero(&gSaveBuffer, sizeof(gSaveBuffer)); read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer)); + if (save_file_need_bswap(&gSaveBuffer)) + save_file_bswap(&gSaveBuffer); + // Verify the main menu data and create a backup copy if only one of the slots is valid. validSlots = verify_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC); validSlots |= verify_save_block_signature(&gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]),MENU_DATA_MAGIC) << 1; diff --git a/src/game/screen_transition.c b/src/game/screen_transition.c index b0c14776..c009abea 100644 --- a/src/game/screen_transition.c +++ b/src/game/screen_transition.c @@ -241,13 +241,10 @@ int render_screen_transition(s8 fadeTimer, s8 transType, u8 transTime, struct Wa } Gfx *render_cannon_circle_base(void) { -#ifdef TARGET_N64 - Vtx *verts = alloc_display_list(4 * sizeof(*verts)); - Gfx *dlist = alloc_display_list(16 * sizeof(*dlist)); -#else + Vtx *verts = alloc_display_list(8 * sizeof(*verts)); Gfx *dlist = alloc_display_list(20 * sizeof(*dlist)); -#endif + Gfx *g = dlist; if (verts != NULL && dlist != NULL) { @@ -256,12 +253,10 @@ Gfx *render_cannon_circle_base(void) { make_vertex(verts, 2, SCREEN_WIDTH, SCREEN_HEIGHT, -1, 1152, 192, 0, 0, 0, 255); make_vertex(verts, 3, 0, SCREEN_HEIGHT, -1, -1152, 192, 0, 0, 0, 255); -#ifndef TARGET_N64 make_vertex(verts, 4, GFX_DIMENSIONS_FROM_LEFT_EDGE(0), 0, -1, 0, 0, 0, 0, 0, 255); make_vertex(verts, 5, GFX_DIMENSIONS_FROM_RIGHT_EDGE(0), 0, -1, 0, 0, 0, 0, 0, 255); make_vertex(verts, 6, GFX_DIMENSIONS_FROM_RIGHT_EDGE(0), SCREEN_HEIGHT, -1, 0, 0, 0, 0, 0, 255); make_vertex(verts, 7, GFX_DIMENSIONS_FROM_LEFT_EDGE(0), SCREEN_HEIGHT, -1, 0, 0, 0, 0, 0, 255); -#endif gSPDisplayList(g++, dl_proj_mtx_fullscreen); gDPSetCombineMode(g++, G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA); @@ -272,12 +267,12 @@ Gfx *render_cannon_circle_base(void) { gSPVertex(g++, VIRTUAL_TO_PHYSICAL(verts), 4, 0); gSPDisplayList(g++, dl_draw_quad_verts_0123); gSPTexture(g++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); -#ifndef TARGET_N64 + gDPSetCombineMode(g++, G_CC_SHADE, G_CC_SHADE); gSPVertex(g++, VIRTUAL_TO_PHYSICAL(verts + 4), 4, 4); gSP2Triangles(g++, 4, 0, 3, 0, 4, 3, 7, 0); gSP2Triangles(g++, 1, 5, 6, 0, 1, 6, 2, 0); -#endif + gSPDisplayList(g++, dl_screen_transition_end); gSPEndDisplayList(g); } else { diff --git a/src/game/skybox.c b/src/game/skybox.c index 76f93201..d1f49908 100644 --- a/src/game/skybox.c +++ b/src/game/skybox.c @@ -242,7 +242,6 @@ void *create_skybox_ortho_matrix(s8 player) { f32 top = sSkyBoxInfo[player].scaledY; Mtx *mtx = alloc_display_list(sizeof(*mtx)); -#ifndef TARGET_N64 f32 half_width = (4.0f / 3.0f) / GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_WIDTH / 2; f32 center = (sSkyBoxInfo[player].scaledX + SCREEN_WIDTH / 2); if (half_width < SCREEN_WIDTH / 2) { @@ -250,7 +249,6 @@ void *create_skybox_ortho_matrix(s8 player) { left = center - half_width; right = center + half_width; } -#endif if (mtx != NULL) { guOrtho(mtx, left, right, bottom, top, 0.0f, 3.0f, 1.0f); diff --git a/src/goddard/renderer.c b/src/goddard/renderer.c index b5b7fafb..821f9aff 100644 --- a/src/goddard/renderer.c +++ b/src/goddard/renderer.c @@ -22,20 +22,15 @@ #include "gd_math.h" #include "shape_helper.h" +#include "config.h" #include "gfx_dimensions.h" #define MAX_GD_DLS 1000 #define OS_MESG_SI_COMPLETE 0x33333333 -#ifdef TARGET_N64 -#define GD_VIRTUAL_TO_PHYSICAL(addr) ((uintptr_t)(addr) &0x0FFFFFFF) -#define GD_LOWER_24(addr) ((uintptr_t)(addr) &0x00FFFFFF) -#define GD_LOWER_29(addr) (((uintptr_t)(addr)) & 0x1FFFFFFF) -#else #define GD_VIRTUAL_TO_PHYSICAL(addr) (addr) #define GD_LOWER_24(addr) ((uintptr_t)(addr)) #define GD_LOWER_29(addr) (((uintptr_t)(addr))) -#endif #define MTX_INTPART_PACK(w1, w2) (((w1) &0xFFFF0000) | (((w2) >> 16) & 0xFFFF)) #define MTX_FRACPART_PACK(w1, w2) ((((w1) << 16) & 0xFFFF0000) | ((w2) &0xFFFF)) @@ -1691,27 +1686,9 @@ u32 Unknown8019EC88(Gfx *dl, UNUSED s32 arg1) { /* 24D4C4 -> 24D63C; orig name: func_8019ECF4 */ void mat4_to_mtx(const Mat4f *src, Mtx *dst) { -#ifdef TARGET_N64 - s32 i; // 14 - s32 j; // 10 - s32 w1; - s32 w2; - s32 *mtxInt = (s32 *) dst->m[0]; // s32 part - s32 *mtxFrc = (s32 *) dst->m[2]; // frac part - for (i = 0; i < 4; i++) { - for (j = 0; j < 2; j++) { - w1 = (s32)((*src)[i][j * 2] * 65536.0f); - w2 = (s32)((*src)[i][j * 2 + 1] * 65536.0f); - *mtxInt = MTX_INTPART_PACK(w1, w2); - mtxInt++; - *mtxFrc = MTX_FRACPART_PACK(w1, w2); - mtxFrc++; - } - } -#else guMtxF2L(src, dst); -#endif + } /* 24D63C -> 24D6E4; orig name: func_8019EE6C */ @@ -2326,7 +2303,7 @@ void start_view_dl(struct ObjView *view) { uly = lry - 1.0f; } - gDPSetScissor(next_gfx(), G_SC_NON_INTERLACE, ulx, uly, lrx, lry); + // gDPSetScissor(next_gfx(), G_SC_NON_INTERLACE, ulx, uly, lrx, lry); // N64 only gSPClearGeometryMode(next_gfx(), 0xFFFFFFFF); gSPSetGeometryMode(next_gfx(), G_LIGHTING | G_CULL_BACK | G_SHADING_SMOOTH | G_SHADE); if (view->flags & VIEW_ALLOC_ZBUF) { @@ -2985,9 +2962,9 @@ void update_cursor(void) { reset_dlnum_indices(sHandShape->gdDls[gGdFrameBuf]); if (gGdCtrl.btnApressed) { - gd_put_sprite((u16 *) gd_texture_hand_closed, sHandView->upperLeft.x, sHandView->upperLeft.y, 0x20, 0x20); + gd_put_sprite((u16 *) gd_texture_hand_closed, sHandView->upperLeft.x, sHandView->upperLeft.y, 32, 32); } else { - gd_put_sprite((u16 *) gd_texture_hand_open, sHandView->upperLeft.x, sHandView->upperLeft.y, 0x20, 0x20); + gd_put_sprite((u16 *) gd_texture_hand_open, sHandView->upperLeft.x, sHandView->upperLeft.y, 32, 32); } gd_enddlsplist_parent(); @@ -3443,7 +3420,8 @@ void Unknown801A5FF8(struct ObjGroup *arg0) { void gd_put_sprite(u16 *sprite, s32 x, s32 y, s32 wx, s32 wy) { s32 c; // 5c s32 r; // 58 - f32 aspect = GFX_DIMENSIONS_ASPECT_RATIO * 0.75; + // Must be game screen aspect ratio, not GFX window aspect ratio + f32 aspect = ((float) SCREEN_WIDTH) / ((float) SCREEN_HEIGHT ) * 0.75; x *= aspect; gSPDisplayList(next_gfx(), osVirtualToPhysical(gd_dl_sprite_start_tex_block)); @@ -3632,89 +3610,9 @@ void Unknown801A6E30(UNUSED u32 a0) { void Unknown801A6E44(UNUSED u32 a0) { } -#ifdef TARGET_N64 -/* 255628 -> 255704; orig name: func_801A6E58 */ -void gd_block_dma(u32 devAddr, void *vAddr, s32 size) { - s32 transfer; // 2c - - do { - if ((transfer = size) > 0x1000) { - transfer = 0x1000; - } - - osPiStartDma(&D_801BE980, OS_MESG_PRI_NORMAL, OS_READ, devAddr, vAddr, transfer, &sGdDMAQueue); - osRecvMesg(&sGdDMAQueue, &D_801BE97C, OS_MESG_BLOCK); - devAddr += transfer; - vAddr = (void *) ((uintptr_t) vAddr + transfer); - size -= 0x1000; - } while (size > 0); -} - -/* 255704 -> 255988 */ -struct GdObj *load_dynlist(struct DynList *dynlist) { - u32 segSize; // 4c - u8 *allocSegSpace; // 48 - void *allocPtr; // 44 - uintptr_t dynlistSegStart; // 40 - uintptr_t dynlistSegEnd; // 3c - s32 i; // 38 - s32 sp34; // tlbPage - struct GdObj *loadedList; // 30 - - i = -1; - - while (sDynLists[++i].list != NULL) { - if (sDynLists[i].list == dynlist) { - break; - } - } - - if (sDynLists[i].list == NULL) { - fatal_printf("load_dynlist() ptr not found in any banks"); - } - - switch (sDynLists[i].flag) { - case STD_LIST_BANK: - dynlistSegStart = (uintptr_t) _gd_dynlistsSegmentRomStart; - dynlistSegEnd = (uintptr_t) _gd_dynlistsSegmentRomEnd; - break; - default: - fatal_printf("load_dynlist() unkown bank"); - } - - segSize = dynlistSegEnd - dynlistSegStart; - allocSegSpace = gd_malloc_temp(segSize + 0x10000); - - if ((allocPtr = (void *) allocSegSpace) == NULL) { - fatal_printf("Not enough DRAM for DATA segment \n"); - } - - allocSegSpace = (u8 *) (((uintptr_t) allocSegSpace + 0x10000) & 0xFFFF0000); - gd_block_dma(dynlistSegStart, (void *) allocSegSpace, segSize); - osUnmapTLBAll(); - - sp34 = (segSize / 0x10000) / 2 + 1; //? has to be written this way - if (sp34 >= 31) { - fatal_printf("load_dynlist() too many TLBs"); - } - - for (i = 0; i < sp34; i++) { - osMapTLB(i, OS_PM_64K, (void *) (uintptr_t) (0x04000000 + (i * 2 * 0x10000)), - GD_LOWER_24(((uintptr_t) allocSegSpace) + (i * 2 * 0x10000)), - GD_LOWER_24(((uintptr_t) allocSegSpace) + (i * 2 * 0x10000) + 0x10000), -1); - } - - loadedList = proc_dynlist(dynlist); - gd_free(allocPtr); - osUnmapTLBAll(); - - return loadedList; -} -#else struct GdObj *load_dynlist(struct DynList *dynlist) { return proc_dynlist(dynlist); } -#endif /* 255988 -> 25599C */ void stub_801A71B8(UNUSED u32 a0) { diff --git a/src/goddard/skin.c b/src/goddard/skin.c index 5223c246..59779177 100644 --- a/src/goddard/skin.c +++ b/src/goddard/skin.c @@ -303,9 +303,7 @@ void convert_gd_verts_to_Vn(struct ObjGroup *grp) { u8 nx, ny, nz; // 24, 25, 26 UNUSED u32 pad20; register struct VtxLink *vtxlink; // a1 -#ifdef TARGET_N64 - register s16 *vnPos; // a2 -#endif + register s16 x; // a3 register s16 y; // t0 register s16 z; // t1 @@ -325,18 +323,12 @@ void convert_gd_verts_to_Vn(struct ObjGroup *grp) { nz = (u8)(vtx->normal.z * 255.0f); for (vtxlink = vtx->gbiVerts; vtxlink != NULL; vtxlink = vtxlink->prev) { -#ifdef TARGET_N64 - vnPos = vtxlink->data->n.ob; - vn = vtxlink->data; - *vnPos++ = x; - *vnPos++ = y; - *vnPos++ = z; -#else + vn = vtxlink->data; vn->n.ob[0] = x; vn->n.ob[1] = y; vn->n.ob[2] = z; -#endif + vn->n.n[0] = nx; vn->n.n[1] = ny; vn->n.n[2] = nz; @@ -348,9 +340,7 @@ void convert_gd_verts_to_Vn(struct ObjGroup *grp) { void convert_gd_verts_to_Vtx(struct ObjGroup *grp) { UNUSED u32 pad24[6]; register struct VtxLink *vtxlink; // a1 -#ifdef TARGET_N64 - register s16 *vtxcoords; // a2 -#endif + register s16 x; // a3 register s16 y; // t0 register s16 z; // t1 @@ -366,16 +356,10 @@ void convert_gd_verts_to_Vtx(struct ObjGroup *grp) { z = (s16) vtx->pos.z; for (vtxlink = vtx->gbiVerts; vtxlink != NULL; vtxlink = vtxlink->prev) { -#ifdef TARGET_N64 - vtxcoords = vtxlink->data->v.ob; - vtxcoords[0] = x; - vtxcoords[1] = y; - vtxcoords[2] = z; -#else + vtxlink->data->v.ob[0] = x; vtxlink->data->v.ob[1] = y; vtxlink->data->v.ob[2] = z; -#endif } } } diff --git a/src/pc/audio/audio_api.h b/src/pc/audio/audio_api.h index 26794dd6..b40130e0 100644 --- a/src/pc/audio/audio_api.h +++ b/src/pc/audio/audio_api.h @@ -10,6 +10,7 @@ struct AudioAPI { int (*buffered)(void); int (*get_desired_buffered)(void); void (*play)(const uint8_t *buf, size_t len); + void (*shutdown)(void); }; #endif diff --git a/src/pc/audio/audio_null.c b/src/pc/audio/audio_null.c index 8a244e4b..4d0dd923 100644 --- a/src/pc/audio/audio_null.c +++ b/src/pc/audio/audio_null.c @@ -15,9 +15,13 @@ static int audio_null_get_desired_buffered(void) { static void audio_null_play(const uint8_t *buf, size_t len) { } +static void audio_null_shutdown(void) { +} + struct AudioAPI audio_null = { audio_null_init, audio_null_buffered, audio_null_get_desired_buffered, - audio_null_play -}; + audio_null_play, + audio_null_shutdown +}; \ No newline at end of file diff --git a/src/pc/audio/audio_sdl.c b/src/pc/audio/audio_sdl.c index 2f13ccde..beb5a1e6 100644 --- a/src/pc/audio/audio_sdl.c +++ b/src/pc/audio/audio_sdl.c @@ -40,9 +40,21 @@ static void audio_sdl_play(const uint8_t *buf, size_t len) { } } +static void audio_sdl_shutdown(void) +{ + if (SDL_WasInit(SDL_INIT_AUDIO)) { + if (dev != 0) { + SDL_CloseAudioDevice(dev); + dev = 0; + } + SDL_QuitSubSystem(SDL_INIT_AUDIO); + } +} + struct AudioAPI audio_sdl = { audio_sdl_init, audio_sdl_buffered, audio_sdl_get_desired_buffered, - audio_sdl_play + audio_sdl_play, + audio_sdl_shutdown }; \ No newline at end of file diff --git a/src/pc/cliopts.c b/src/pc/cliopts.c new file mode 100644 index 00000000..1e3b29c8 --- /dev/null +++ b/src/pc/cliopts.c @@ -0,0 +1,42 @@ +#include "cliopts.h" +#include +#include +#include + +struct PCCLIOptions gCLIOpts; + +void parse_cli_opts(int argc, char* argv[]) +{ + // Initialize options with false values. + gCLIOpts.SkipIntro = 0; + gCLIOpts.FullScreen = 0; + gCLIOpts.ConfigFile = malloc(31); + strncpy(gCLIOpts.ConfigFile, "sm64config.txt", strlen("sm64config.txt")); + + // Scan arguments for options + if (argc > 1) + { + int i; + for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], "--skip-intro") == 0) // Skip Peach Intro + gCLIOpts.SkipIntro = 1; + + if (strcmp(argv[i], "--fullscreen") == 0) // Open game in fullscreen + gCLIOpts.FullScreen = 1; + + if (strncmp(argv[i], "--configfile", strlen("--configfile")) == 0) + { + if (i+1 < argc) + { + if (strlen(argv[i]) > 30) { + fprintf(stderr, "Configuration file supplied has a name too long.\n"); + } else { + memset(gCLIOpts.ConfigFile, 0, 30); + strncpy(gCLIOpts.ConfigFile, argv[i+1], strlen(argv[i+1])); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/pc/cliopts.h b/src/pc/cliopts.h new file mode 100644 index 00000000..1844c44c --- /dev/null +++ b/src/pc/cliopts.h @@ -0,0 +1,12 @@ +#include "sm64.h" + +struct PCCLIOptions +{ + u8 SkipIntro; + u8 FullScreen; + char * ConfigFile; +}; + +extern struct PCCLIOptions gCLIOpts; + +void parse_cli_opts(int argc, char* argv[]); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 4d38aaaf..3c2735c2 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -7,6 +7,7 @@ #include #include "configfile.h" +#include "controller/controller_api.h" #define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) @@ -14,6 +15,7 @@ enum ConfigOptionType { CONFIG_TYPE_BOOL, CONFIG_TYPE_UINT, CONFIG_TYPE_FLOAT, + CONFIG_TYPE_BIND, }; struct ConfigOption { @@ -29,74 +31,60 @@ struct ConfigOption { /* *Config options and default values */ -bool configFullscreen = false; -// Keyboard mappings (scancode values) -unsigned int configKeyA = 0x26; -unsigned int configKeyB = 0x33; -unsigned int configKeyStart = 0x39; -unsigned int configKeyL = 0x34; -unsigned int configKeyR = 0x36; -unsigned int configKeyZ = 0x25; -unsigned int configKeyCUp = 0x148; -unsigned int configKeyCDown = 0x150; -unsigned int configKeyCLeft = 0x14B; -unsigned int configKeyCRight = 0x14D; -unsigned int configKeyStickUp = 0x11; -unsigned int configKeyStickDown = 0x1F; -unsigned int configKeyStickLeft = 0x1E; -unsigned int configKeyStickRight = 0x20; -// Gamepad mappings (SDL_GameControllerButton values) -unsigned int configJoyA = 0; -unsigned int configJoyB = 2; -unsigned int configJoyStart = 6; -unsigned int configJoyL = 7; -unsigned int configJoyR = 10; -unsigned int configJoyZ = 9; -// Mouse button mappings (0 for none, 1 for left, 2 for middle, 3 for right) -unsigned int configMouseA = 3; -unsigned int configMouseB = 1; -unsigned int configMouseL = 4; -unsigned int configMouseR = 5; -unsigned int configMouseZ = 2; + +// Video/audio stuff +bool configFullscreen = false; +unsigned int configFiltering = 1; // 0=force nearest, 1=linear, (TODO) 2=three-point +unsigned int configMasterVolume = MAX_VOLUME; // 0 - MAX_VOLUME + +// Keyboard mappings (VK_ values, by default keyboard/gamepad/mouse) +unsigned int configKeyA[MAX_BINDS] = { 0x0026, 0x1000, 0x1103 }; +unsigned int configKeyB[MAX_BINDS] = { 0x0033, 0x1002, 0x1101 }; +unsigned int configKeyStart[MAX_BINDS] = { 0x0039, 0x1006, VK_INVALID }; +unsigned int configKeyL[MAX_BINDS] = { 0x0034, 0x1007, 0x1104 }; +unsigned int configKeyR[MAX_BINDS] = { 0x0036, 0x100A, 0x1105 }; +unsigned int configKeyZ[MAX_BINDS] = { 0x0025, 0x1009, 0x1102 }; +unsigned int configKeyCUp[MAX_BINDS] = { 0x0148, VK_INVALID, VK_INVALID }; +unsigned int configKeyCDown[MAX_BINDS] = { 0x0150, VK_INVALID, VK_INVALID }; +unsigned int configKeyCLeft[MAX_BINDS] = { 0x014B, VK_INVALID, VK_INVALID }; +unsigned int configKeyCRight[MAX_BINDS] = { 0x014D, VK_INVALID, VK_INVALID }; +unsigned int configKeyStickUp[MAX_BINDS] = { 0x0011, VK_INVALID, VK_INVALID }; +unsigned int configKeyStickDown[MAX_BINDS] = { 0x001F, VK_INVALID, VK_INVALID }; +unsigned int configKeyStickLeft[MAX_BINDS] = { 0x001E, VK_INVALID, VK_INVALID }; +unsigned int configKeyStickRight[MAX_BINDS] = { 0x0020, VK_INVALID, VK_INVALID }; + #ifdef BETTERCAMERA // BetterCamera settings unsigned int configCameraXSens = 50; unsigned int configCameraYSens = 50; unsigned int configCameraAggr = 0; unsigned int configCameraPan = 0; +unsigned int configCameraDegrade = 10; // 0 - 100% bool configCameraInvertX = false; bool configCameraInvertY = false; bool configEnableCamera = false; bool configCameraMouse = false; #endif +unsigned int configSkipIntro = 0; static const struct ConfigOption options[] = { {.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen}, - {.name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA}, - {.name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB}, - {.name = "key_start", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStart}, - {.name = "key_l", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyL}, - {.name = "key_r", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyR}, - {.name = "key_z", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyZ}, - {.name = "key_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCUp}, - {.name = "key_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCDown}, - {.name = "key_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCLeft}, - {.name = "key_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCRight}, - {.name = "key_stickup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickUp}, - {.name = "key_stickdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickDown}, - {.name = "key_stickleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickLeft}, - {.name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight}, - {.name = "joy_a", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyA}, - {.name = "joy_b", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyB}, - {.name = "joy_start", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyStart}, - {.name = "joy_l", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyL}, - {.name = "joy_r", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyR}, - {.name = "joy_z", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyZ}, - {.name = "mouse_a", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseA}, - {.name = "mouse_b", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseB}, - {.name = "mouse_l", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseL}, - {.name = "mouse_r", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseR}, - {.name = "mouse_z", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseZ}, + {.name = "texture_filtering", .type = CONFIG_TYPE_UINT, .uintValue = &configFiltering}, + {.name = "master_volume", .type = CONFIG_TYPE_UINT, .uintValue = &configMasterVolume}, + {.name = "key_a", .type = CONFIG_TYPE_BIND, .uintValue = configKeyA}, + {.name = "key_b", .type = CONFIG_TYPE_BIND, .uintValue = configKeyB}, + {.name = "key_start", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStart}, + {.name = "key_l", .type = CONFIG_TYPE_BIND, .uintValue = configKeyL}, + {.name = "key_r", .type = CONFIG_TYPE_BIND, .uintValue = configKeyR}, + {.name = "key_z", .type = CONFIG_TYPE_BIND, .uintValue = configKeyZ}, + {.name = "key_cup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCUp}, + {.name = "key_cdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCDown}, + {.name = "key_cleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCLeft}, + {.name = "key_cright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCRight}, + {.name = "key_stickup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickUp}, + {.name = "key_stickdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickDown}, + {.name = "key_stickleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickLeft}, + {.name = "key_stickright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickRight}, #ifdef BETTERCAMERA {.name = "bettercam_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnableCamera}, {.name = "bettercam_mouse_look", .type = CONFIG_TYPE_BOOL, .boolValue = &configCameraMouse}, @@ -106,7 +94,9 @@ static const struct ConfigOption options[] = { {.name = "bettercam_ysens", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraYSens}, {.name = "bettercam_aggression", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraAggr}, {.name = "bettercam_pan_level", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraPan}, + {.name = "bettercam_degrade", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraDegrade}, #endif + {.name = "skip_intro", .type = CONFIG_TYPE_UINT, .uintValue = &configSkipIntro}, // Add this back! }; // Reads an entire line from a file (excluding the newline character) and returns an allocated string @@ -207,9 +197,13 @@ void configfile_load(const char *filename) { while (isspace(*p)) p++; + + if (!*p || *p == '#') // comment or empty line + continue; + numTokens = tokenize_string(p, 2, tokens); if (numTokens != 0) { - if (numTokens == 2) { + if (numTokens >= 2) { const struct ConfigOption *option = NULL; for (unsigned int i = 0; i < ARRAY_LEN(options); i++) { @@ -231,6 +225,10 @@ void configfile_load(const char *filename) { case CONFIG_TYPE_UINT: sscanf(tokens[1], "%u", option->uintValue); break; + case CONFIG_TYPE_BIND: + for (int i = 0; i < MAX_BINDS && i < numTokens - 1; ++i) + sscanf(tokens[i + 1], "%x", option->uintValue + i); + break; case CONFIG_TYPE_FLOAT: sscanf(tokens[1], "%f", option->floatValue); break; @@ -273,6 +271,12 @@ void configfile_save(const char *filename) { case CONFIG_TYPE_FLOAT: fprintf(file, "%s %f\n", option->name, *option->floatValue); break; + case CONFIG_TYPE_BIND: + fprintf(file, "%s ", option->name); + for (int i = 0; i < MAX_BINDS; ++i) + fprintf(file, "%04x ", option->uintValue[i]); + fprintf(file, "\n"); + break; default: assert(0); // unknown type } diff --git a/src/pc/configfile.h b/src/pc/configfile.h index a2044dd7..49b48b93 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -3,40 +3,33 @@ #include -#define CONFIG_FILE "sm64config.txt" +#define MAX_BINDS 3 +#define MAX_VOLUME 127 +#define VOLUME_SHIFT 7 extern bool configFullscreen; -extern unsigned int configKeyA; -extern unsigned int configKeyB; -extern unsigned int configKeyStart; -extern unsigned int configKeyL; -extern unsigned int configKeyR; -extern unsigned int configKeyZ; -extern unsigned int configKeyCUp; -extern unsigned int configKeyCDown; -extern unsigned int configKeyCLeft; -extern unsigned int configKeyCRight; -extern unsigned int configKeyStickUp; -extern unsigned int configKeyStickDown; -extern unsigned int configKeyStickLeft; -extern unsigned int configKeyStickRight; -extern unsigned int configJoyA; -extern unsigned int configJoyB; -extern unsigned int configJoyStart; -extern unsigned int configJoyL; -extern unsigned int configJoyR; -extern unsigned int configJoyZ; -extern unsigned int configMouseA; -extern unsigned int configMouseB; -extern unsigned int configMouseStart; -extern unsigned int configMouseL; -extern unsigned int configMouseR; -extern unsigned int configMouseZ; +extern unsigned int configFiltering; +extern unsigned int configMasterVolume; +extern unsigned int configKeyA[]; +extern unsigned int configKeyB[]; +extern unsigned int configKeyStart[]; +extern unsigned int configKeyL[]; +extern unsigned int configKeyR[]; +extern unsigned int configKeyZ[]; +extern unsigned int configKeyCUp[]; +extern unsigned int configKeyCDown[]; +extern unsigned int configKeyCLeft[]; +extern unsigned int configKeyCRight[]; +extern unsigned int configKeyStickUp[]; +extern unsigned int configKeyStickDown[]; +extern unsigned int configKeyStickLeft[]; +extern unsigned int configKeyStickRight[]; #ifdef BETTERCAMERA extern unsigned int configCameraXSens; extern unsigned int configCameraYSens; extern unsigned int configCameraAggr; extern unsigned int configCameraPan; +extern unsigned int configCameraDegrade; extern bool configCameraInvertX; extern bool configCameraInvertY; extern bool configEnableCamera; diff --git a/src/pc/controller/controller_api.h b/src/pc/controller/controller_api.h index e040551a..2f9c1d16 100644 --- a/src/pc/controller/controller_api.h +++ b/src/pc/controller/controller_api.h @@ -2,15 +2,25 @@ #define CONTROLLER_API #define DEADZONE 4960 - -// Analog camera movement by Pathétique (github.com/vrmiguel), y0shin and Mors -// Contribute or communicate bugs at github.com/vrmiguel/sm64-analog-camera +#define VK_INVALID 0xFFFF +#define VK_SIZE 0x1000 #include struct ControllerAPI { - void (*init)(void); - void (*read)(OSContPad *pad); + const u32 vkbase; // base number in the virtual keyspace (e.g. keyboard is 0x0000-0x1000) + void (*init)(void); // call once, also calls reconfig() + void (*read)(OSContPad *pad); // read controller and update N64 pad values + u32 (*rawkey)(void); // returns last pressed virtual key or VK_INVALID if none + void (*reconfig)(void); // (optional) call when bindings have changed + void (*shutdown)(void); // (optional) call in osContReset }; +// used for binding keys +u32 controller_get_raw_key(void); +void controller_reconfigure(void); + +// calls the shutdown() function of all controller subsystems +void controller_shutdown(void); + #endif diff --git a/src/pc/controller/controller_entry_point.c b/src/pc/controller/controller_entry_point.c index 9f3630a0..90c10c1d 100644 --- a/src/pc/controller/controller_entry_point.c +++ b/src/pc/controller/controller_entry_point.c @@ -56,3 +56,25 @@ void osContGetReadData(OSContPad *pad) { controller_implementations[i]->read(pad); } } + +u32 controller_get_raw_key(void) { + for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) { + u32 vk = controller_implementations[i]->rawkey(); + if (vk != VK_INVALID) return vk + controller_implementations[i]->vkbase; + } + return VK_INVALID; +} + +void controller_shutdown(void) { + for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) { + if (controller_implementations[i]->shutdown) + controller_implementations[i]->shutdown(); + } +} + +void controller_reconfigure(void) { + for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) { + if (controller_implementations[i]->reconfig) + controller_implementations[i]->reconfig(); + } +} diff --git a/src/pc/controller/controller_keyboard.c b/src/pc/controller/controller_keyboard.c index 87eaee0d..9fb4b635 100644 --- a/src/pc/controller/controller_keyboard.c +++ b/src/pc/controller/controller_keyboard.c @@ -8,14 +8,19 @@ #endif #include "../configfile.h" +#include "controller_keyboard.h" static int keyboard_buttons_down; -static int keyboard_mapping[14][2]; +#define MAX_KEYBINDS 64 +static int keyboard_mapping[MAX_KEYBINDS][2]; +static int num_keybinds = 0; + +static u32 keyboard_lastkey = VK_INVALID; static int keyboard_map_scancode(int scancode) { int ret = 0; - for (size_t i = 0; i < sizeof(keyboard_mapping) / sizeof(keyboard_mapping[0]); i++) { + for (int i = 0; i < num_keybinds; i++) { if (keyboard_mapping[i][0] == scancode) { ret |= keyboard_mapping[i][1]; } @@ -26,12 +31,15 @@ static int keyboard_map_scancode(int scancode) { bool keyboard_on_key_down(int scancode) { int mapped = keyboard_map_scancode(scancode); keyboard_buttons_down |= mapped; + keyboard_lastkey = scancode; return mapped != 0; } bool keyboard_on_key_up(int scancode) { int mapped = keyboard_map_scancode(scancode); keyboard_buttons_down &= ~mapped; + if (keyboard_lastkey == (u32) scancode) + keyboard_lastkey = VK_INVALID; return mapped != 0; } @@ -39,28 +47,38 @@ void keyboard_on_all_keys_up(void) { keyboard_buttons_down = 0; } -static void set_keyboard_mapping(int index, int mask, int scancode) { - keyboard_mapping[index][0] = scancode; - keyboard_mapping[index][1] = mask; +static void keyboard_add_binds(int mask, unsigned int *scancode) { + for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) { + if (scancode[i] < VK_BASE_KEYBOARD + VK_SIZE) { + keyboard_mapping[num_keybinds][0] = scancode[i]; + keyboard_mapping[num_keybinds][1] = mask; + num_keybinds++; + } + } +} + +static void keyboard_bindkeys(void) { + bzero(keyboard_mapping, sizeof(keyboard_mapping)); + num_keybinds = 0; + + keyboard_add_binds(0x80000, configKeyStickUp); + keyboard_add_binds(0x10000, configKeyStickLeft); + keyboard_add_binds(0x40000, configKeyStickDown); + keyboard_add_binds(0x20000, configKeyStickRight); + keyboard_add_binds(A_BUTTON, configKeyA); + keyboard_add_binds(B_BUTTON, configKeyB); + keyboard_add_binds(Z_TRIG, configKeyZ); + keyboard_add_binds(U_CBUTTONS, configKeyCUp); + keyboard_add_binds(L_CBUTTONS, configKeyCLeft); + keyboard_add_binds(D_CBUTTONS, configKeyCDown); + keyboard_add_binds(R_CBUTTONS, configKeyCRight); + keyboard_add_binds(L_TRIG, configKeyL); + keyboard_add_binds(R_TRIG, configKeyR); + keyboard_add_binds(START_BUTTON, configKeyStart); } static void keyboard_init(void) { - int i = 0; - - set_keyboard_mapping(i++, 0x80000, configKeyStickUp); - set_keyboard_mapping(i++, 0x10000, configKeyStickLeft); - set_keyboard_mapping(i++, 0x40000, configKeyStickDown); - set_keyboard_mapping(i++, 0x20000, configKeyStickRight); - set_keyboard_mapping(i++, A_BUTTON, configKeyA); - set_keyboard_mapping(i++, B_BUTTON, configKeyB); - set_keyboard_mapping(i++, Z_TRIG, configKeyZ); - set_keyboard_mapping(i++, U_CBUTTONS, configKeyCUp); - set_keyboard_mapping(i++, L_CBUTTONS, configKeyCLeft); - set_keyboard_mapping(i++, D_CBUTTONS, configKeyCDown); - set_keyboard_mapping(i++, R_CBUTTONS, configKeyCRight); - set_keyboard_mapping(i++, L_TRIG, configKeyL); - set_keyboard_mapping(i++, R_TRIG, configKeyR); - set_keyboard_mapping(i++, START_BUTTON, configKeyStart); + keyboard_bindkeys(); #ifdef TARGET_WEB controller_emscripten_keyboard_init(); @@ -83,7 +101,20 @@ static void keyboard_read(OSContPad *pad) { } } +static u32 keyboard_rawkey(void) { + const u32 ret = keyboard_lastkey; + keyboard_lastkey = VK_INVALID; + return ret; +} + +static void keyboard_shutdown(void) { +} + struct ControllerAPI controller_keyboard = { + VK_BASE_KEYBOARD, keyboard_init, - keyboard_read + keyboard_read, + keyboard_rawkey, + keyboard_bindkeys, + keyboard_shutdown }; diff --git a/src/pc/controller/controller_keyboard.h b/src/pc/controller/controller_keyboard.h index e2c08586..028d2e85 100644 --- a/src/pc/controller/controller_keyboard.h +++ b/src/pc/controller/controller_keyboard.h @@ -4,6 +4,8 @@ #include #include "controller_api.h" +# define VK_BASE_KEYBOARD 0x0000 + #ifdef __cplusplus extern "C" { #endif diff --git a/src/pc/controller/controller_recorded_tas.c b/src/pc/controller/controller_recorded_tas.c index 4f5c1bb0..2fcd5b54 100644 --- a/src/pc/controller/controller_recorded_tas.c +++ b/src/pc/controller/controller_recorded_tas.c @@ -23,7 +23,22 @@ static void tas_read(OSContPad *pad) { } } +static void tas_shutdown(void) { + if (fp != NULL) { + fclose(fp); + fp = NULL; + } +} + +static u32 tas_rawkey(void) { + return VK_INVALID; +} + struct ControllerAPI controller_recorded_tas = { + VK_INVALID, tas_init, - tas_read + tas_read, + tas_rawkey, + NULL, // no rebinding + tas_shutdown }; diff --git a/src/pc/controller/controller_sdl.c b/src/pc/controller/controller_sdl.c index 48120231..62aa2acf 100644 --- a/src/pc/controller/controller_sdl.c +++ b/src/pc/controller/controller_sdl.c @@ -11,9 +11,15 @@ #include #include "controller_api.h" - +#include "controller_sdl.h" #include "../configfile.h" +// mouse buttons are also in the controller namespace (why), just offset 0x100 +#define VK_OFS_SDL_MOUSE 0x0100 +#define VK_BASE_SDL_MOUSE (VK_BASE_SDL_GAMEPAD + VK_OFS_SDL_MOUSE) +#define MAX_JOYBINDS 32 +#define MAX_MOUSEBUTTONS 8 // arbitrary + extern int16_t rightx; extern int16_t righty; @@ -27,6 +33,51 @@ extern u8 newcam_mouse; static bool init_ok; static SDL_GameController *sdl_cntrl; + +static u32 num_joy_binds = 0; +static u32 num_mouse_binds = 0; +static u32 joy_binds[MAX_JOYBINDS][2]; +static u32 mouse_binds[MAX_JOYBINDS][2]; + +static bool joy_buttons[SDL_CONTROLLER_BUTTON_MAX ] = { false }; +static u32 mouse_buttons = 0; +static u32 last_mouse = VK_INVALID; +static u32 last_joybutton = VK_INVALID; + +static inline void controller_add_binds(const u32 mask, const u32 *btns) { + for (u32 i = 0; i < MAX_BINDS; ++i) { + if (btns[i] >= VK_BASE_SDL_GAMEPAD && btns[i] <= VK_BASE_SDL_GAMEPAD + VK_SIZE) { + if (btns[i] >= VK_BASE_SDL_MOUSE && num_joy_binds < MAX_JOYBINDS) { + mouse_binds[num_mouse_binds][0] = btns[i] - VK_BASE_SDL_MOUSE; + mouse_binds[num_mouse_binds][1] = mask; + ++num_mouse_binds; + } else if (num_mouse_binds < MAX_JOYBINDS) { + joy_binds[num_joy_binds][0] = btns[i] - VK_BASE_SDL_GAMEPAD; + joy_binds[num_joy_binds][1] = mask; + ++num_joy_binds; + } + } + } +} + +static void controller_sdl_bind(void) { + bzero(joy_binds, sizeof(joy_binds)); + bzero(mouse_binds, sizeof(mouse_binds)); + num_joy_binds = 0; + num_mouse_binds = 0; + + controller_add_binds(A_BUTTON, configKeyA); + controller_add_binds(B_BUTTON, configKeyB); + controller_add_binds(Z_TRIG, configKeyZ); + controller_add_binds(U_CBUTTONS, configKeyCUp); + controller_add_binds(L_CBUTTONS, configKeyCLeft); + controller_add_binds(D_CBUTTONS, configKeyCDown); + controller_add_binds(R_CBUTTONS, configKeyCRight); + controller_add_binds(L_TRIG, configKeyL); + controller_add_binds(R_TRIG, configKeyR); + controller_add_binds(START_BUTTON, configKeyStart); +} + static void controller_sdl_init(void) { if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) { fprintf(stderr, "SDL init error: %s\n", SDL_GetError()); @@ -39,6 +90,8 @@ static void controller_sdl_init(void) { SDL_GetRelativeMouseState(&mouse_x, &mouse_y); #endif + controller_sdl_bind(); + init_ok = true; } @@ -53,13 +106,16 @@ static void controller_sdl_read(OSContPad *pad) { else SDL_SetRelativeMouseMode(SDL_FALSE); - const u32 mbuttons = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); + u32 mouse = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); + + for (u32 i = 0; i < num_mouse_binds; ++i) + if (mouse & SDL_BUTTON(mouse_binds[i][0])) + pad->button |= mouse_binds[i][1]; + + // remember buttons that changed from 0 to 1 + last_mouse = (mouse_buttons ^ mouse) & mouse; + mouse_buttons = mouse; - if (configMouseA && (mbuttons & SDL_BUTTON(configMouseA))) pad->button |= A_BUTTON; - if (configMouseB && (mbuttons & SDL_BUTTON(configMouseB))) pad->button |= B_BUTTON; - if (configMouseL && (mbuttons & SDL_BUTTON(configMouseL))) pad->button |= L_TRIG; - if (configMouseR && (mbuttons & SDL_BUTTON(configMouseR))) pad->button |= R_TRIG; - if (configMouseZ && (mbuttons & SDL_BUTTON(configMouseZ))) pad->button |= Z_TRIG; #endif SDL_GameControllerUpdate(); @@ -82,12 +138,16 @@ static void controller_sdl_read(OSContPad *pad) { } } - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyStart)) pad->button |= START_BUTTON; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyZ)) pad->button |= Z_TRIG; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyL)) pad->button |= L_TRIG; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyR)) pad->button |= R_TRIG; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyA)) pad->button |= A_BUTTON; - if (SDL_GameControllerGetButton(sdl_cntrl, configJoyB)) pad->button |= B_BUTTON; + for (u32 i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { + const bool new = SDL_GameControllerGetButton(sdl_cntrl, i); + const bool pressed = !joy_buttons[i] && new; + joy_buttons[i] = new; + if (pressed) last_joybutton = i; + } + + for (u32 i = 0; i < num_joy_binds; ++i) + if (joy_buttons[joy_binds[i][0]]) + pad->button |= joy_binds[i][1]; int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX); int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY); @@ -127,7 +187,39 @@ static void controller_sdl_read(OSContPad *pad) { } } +static u32 controller_sdl_rawkey(void) { + if (last_joybutton != VK_INVALID) { + const u32 ret = last_joybutton; + last_joybutton = VK_INVALID; + return ret; + } + + for (int i = 0; i < MAX_MOUSEBUTTONS; ++i) { + if (last_mouse & SDL_BUTTON(i)) { + const u32 ret = VK_OFS_SDL_MOUSE + i; + last_mouse = 0; + return ret; + } + } + return VK_INVALID; +} + +static void controller_sdl_shutdown(void) { + if (SDL_WasInit(SDL_INIT_GAMECONTROLLER)) { + if (sdl_cntrl) { + SDL_GameControllerClose(sdl_cntrl); + sdl_cntrl = NULL; + } + SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); + } + init_ok = false; +} + struct ControllerAPI controller_sdl = { + VK_BASE_SDL_GAMEPAD, controller_sdl_init, - controller_sdl_read + controller_sdl_read, + controller_sdl_rawkey, + controller_sdl_bind, + controller_sdl_shutdown }; diff --git a/src/pc/controller/controller_sdl.h b/src/pc/controller/controller_sdl.h index 02aec8d9..bbe8a62c 100644 --- a/src/pc/controller/controller_sdl.h +++ b/src/pc/controller/controller_sdl.h @@ -3,6 +3,8 @@ #include "controller_api.h" +#define VK_BASE_SDL_GAMEPAD 0x1000 + extern struct ControllerAPI controller_sdl; #endif diff --git a/src/pc/gfx/gfx_opengl.c b/src/pc/gfx/gfx_opengl.c index 65b3099e..76d584cb 100644 --- a/src/pc/gfx/gfx_opengl.c +++ b/src/pc/gfx/gfx_opengl.c @@ -18,12 +18,19 @@ #include #define GL_GLEXT_PROTOTYPES 1 #include + #else #include #define GL_GLEXT_PROTOTYPES 1 + +#ifdef OSX_BUILD +#include +#else #include #endif +#endif + #include "gfx_cc.h" #include "gfx_rendering_api.h" @@ -49,7 +56,7 @@ static bool gfx_opengl_z_is_from_0_to_1(void) { static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram *prg) { size_t num_floats = prg->num_floats; size_t pos = 0; - + for (int i = 0; i < prg->num_attribs; i++) { glEnableVertexAttribArray(prg->attrib_locations[i]); glVertexAttribPointer(prg->attrib_locations[i], prg->attrib_sizes[i], GL_FLOAT, GL_FALSE, num_floats * sizeof(float), (void *)(pos * sizeof(float))); @@ -179,15 +186,19 @@ static struct ShaderProgram *gfx_opengl_create_and_load_new_shader(uint32_t shad bool do_multiply[2] = {c[0][1] == 0 && c[0][3] == 0, c[1][1] == 0 && c[1][3] == 0}; bool do_mix[2] = {c[0][1] == c[0][3], c[1][1] == c[1][3]}; bool color_alpha_same = (shader_id & 0xfff) == ((shader_id >> 12) & 0xfff); - + char vs_buf[1024]; char fs_buf[1024]; size_t vs_len = 0; size_t fs_len = 0; size_t num_floats = 4; - + // Vertex shader +#ifdef OSX_BUILD + append_line(vs_buf, &vs_len, ""); +#else append_line(vs_buf, &vs_len, "#version 100"); +#endif append_line(vs_buf, &vs_len, "attribute vec4 aVtxPos;"); if (used_textures[0] || used_textures[1]) { append_line(vs_buf, &vs_len, "attribute vec2 aTexCoord;"); @@ -216,10 +227,15 @@ static struct ShaderProgram *gfx_opengl_create_and_load_new_shader(uint32_t shad } append_line(vs_buf, &vs_len, "gl_Position = aVtxPos;"); append_line(vs_buf, &vs_len, "}"); - + // Fragment shader +#ifdef OSX_BUILD + append_line(fs_buf, &fs_len, ""); +#else append_line(fs_buf, &fs_len, "#version 100"); append_line(fs_buf, &fs_len, "precision mediump float;"); +#endif + if (used_textures[0] || used_textures[1]) { append_line(fs_buf, &fs_len, "varying vec2 vTexCoord;"); } @@ -408,9 +424,10 @@ static uint32_t gfx_cm_to_opengl(uint32_t val) { } static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) { + const GLenum filter = linear_filter ? GL_LINEAR : GL_NEAREST; glActiveTexture(GL_TEXTURE0 + tile); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gfx_cm_to_opengl(cms)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gfx_cm_to_opengl(cmt)); } @@ -463,7 +480,11 @@ static void gfx_opengl_init(void) { #if FOR_WINDOWS glewInit(); #endif - + +#ifdef OSX_BUILD + glewInit(); +#endif + glGenBuffers(1, &opengl_vbo); glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo); @@ -480,6 +501,9 @@ static void gfx_opengl_start_frame(void) { glEnable(GL_SCISSOR_TEST); } +static void gfx_opengl_shutdown(void) { +} + struct GfxRenderingAPI gfx_opengl_api = { gfx_opengl_z_is_from_0_to_1, gfx_opengl_unload_shader, @@ -499,5 +523,6 @@ struct GfxRenderingAPI gfx_opengl_api = { gfx_opengl_set_use_alpha, gfx_opengl_draw_triangles, gfx_opengl_init, - gfx_opengl_start_frame + gfx_opengl_start_frame, + gfx_opengl_shutdown }; diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index 570c5305..6b8841a2 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -18,6 +18,8 @@ #include "gfx_rendering_api.h" #include "gfx_screen_config.h" +#include "../configfile.h" + #define SUPPORT_CHECK(x) assert(x) // SCALE_M_N: upscale/downscale M-bit integer to N-bit @@ -586,10 +588,16 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *verti calculate_normal_dir(&lookat_y, rsp.current_lookat_coeffs[1]); rsp.lights_changed = false; } - - int r = rsp.current_lights[rsp.current_num_lights - 1].col[0]; - int g = rsp.current_lights[rsp.current_num_lights - 1].col[1]; - int b = rsp.current_lights[rsp.current_num_lights - 1].col[2]; + + // Inspired by: + // https://github.com/gonetz/GLideN64/commit/c8cbafff71a81bee5112aaafe6e21d6648ff8125#diff-69d8715ec7f9fd627ec4f5516edd003dL484 + const bool useFirstColor = (dest_index & 1) == 0; + const unsigned char* col = useFirstColor + ? rsp.current_lights[rsp.current_num_lights - 1].col + : rsp.current_lights[rsp.current_num_lights - 1].colc; + int r = col[0]; + int g = col[1]; + int b = col[2]; for (int i = 0; i < rsp.current_num_lights - 1; i++) { float intensity = 0; @@ -598,9 +606,14 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *verti intensity += vn->n[2] * rsp.current_lights_coeffs[i][2]; intensity /= 127.0f; if (intensity > 0.0f) { - r += intensity * rsp.current_lights[i].col[0]; - g += intensity * rsp.current_lights[i].col[1]; - b += intensity * rsp.current_lights[i].col[2]; + // Inspired by: + // https://github.com/gonetz/GLideN64/commit/c8cbafff71a81bee5112aaafe6e21d6648ff8125#diff-69d8715ec7f9fd627ec4f5516edd003dL492 + col = useFirstColor + ? rsp.current_lights[i].col + : rsp.current_lights[i].colc; + r += intensity * col[0]; + g += intensity * col[1]; + b += intensity * col[2]; } } @@ -780,7 +793,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) { import_texture(i); rdp.textures_changed[i] = false; } - bool linear_filter = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT; + bool linear_filter = configFiltering && ((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT); if (linear_filter != rendering_state.textures[i]->linear_filter || rdp.texture_tile.cms != rendering_state.textures[i]->cms || rdp.texture_tile.cmt != rendering_state.textures[i]->cmt) { gfx_flush(); gfx_rapi->set_sampler_parameters(i, linear_filter, rdp.texture_tile.cms, rdp.texture_tile.cmt); @@ -1562,3 +1575,14 @@ void gfx_end_frame(void) { gfx_wapi->swap_buffers_end(); } } + +void gfx_shutdown(void) { + if (gfx_rapi) { + if (gfx_rapi->shutdown) gfx_rapi->shutdown(); + gfx_rapi = NULL; + } + if (gfx_wapi) { + if (gfx_wapi->shutdown) gfx_wapi->shutdown(); + gfx_wapi = NULL; + } +} \ No newline at end of file diff --git a/src/pc/gfx/gfx_pc.h b/src/pc/gfx/gfx_pc.h index 6903597c..8d62e2b7 100644 --- a/src/pc/gfx/gfx_pc.h +++ b/src/pc/gfx/gfx_pc.h @@ -15,5 +15,6 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi); void gfx_start_frame(void); void gfx_run(Gfx *commands); void gfx_end_frame(void); +void gfx_shutdown(void); #endif diff --git a/src/pc/gfx/gfx_rendering_api.h b/src/pc/gfx/gfx_rendering_api.h index 58d79435..75aedef9 100644 --- a/src/pc/gfx/gfx_rendering_api.h +++ b/src/pc/gfx/gfx_rendering_api.h @@ -27,6 +27,7 @@ struct GfxRenderingAPI { void (*draw_triangles)(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris); void (*init)(void); void (*start_frame)(void); + void (*shutdown)(void); }; #endif diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index a794c6d3..846ce383 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -13,22 +13,31 @@ #else #include #define GL_GLEXT_PROTOTYPES 1 + +#ifdef OSX_BUILD +#include +#else #include #endif +#endif // End of OS-Specific GL defines + #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" #include "../configfile.h" +#include "../cliopts.h" #include "src/pc/controller/controller_keyboard.h" static SDL_Window *wnd; +static SDL_GLContext ctx = NULL; static int inverted_scancode_table[512]; -extern bool configFullscreen; +static bool cur_fullscreen; +static uint32_t cur_width, cur_height; const SDL_Scancode windows_scancode_table[] = -{ +{ /* 0 1 2 3 4 5 6 7 */ /* 8 9 A B C D E F */ SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, /* 0 */ @@ -51,7 +60,7 @@ const SDL_Scancode windows_scancode_table[] = SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16, /* 6 */ SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 6 */ - + SDL_SCANCODE_INTERNATIONAL2, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL1, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 7 */ SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL4, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL5, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL3, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN /* 7 */ }; @@ -78,49 +87,51 @@ const SDL_Scancode scancode_rmapping_nonextended[][2] = { {SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN} }; -static void gfx_sdl_set_fullscreen(bool fullscreen) -{ - if (fullscreen) - { +static void gfx_sdl_set_fullscreen(bool fullscreen) { + if (fullscreen == cur_fullscreen) return; + + if (fullscreen) { SDL_SetWindowFullscreen(wnd, SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_ShowCursor(SDL_DISABLE); - } - else - { + } else { SDL_SetWindowFullscreen(wnd, 0); SDL_ShowCursor(SDL_ENABLE); } - configFullscreen = fullscreen; + cur_fullscreen = fullscreen; } static void gfx_sdl_init(void) { Uint32 window_flags = 0; SDL_Init(SDL_INIT_VIDEO); - + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); #ifdef USE_GLES SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); // These attributes allow for hardware acceleration on RPis. - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); #endif - + //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; + if (gCLIOpts.FullScreen) { + configFullscreen = true; + } + if (configFullscreen) { window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } SDL_DisplayMode sdl_displaymode; - SDL_GetCurrentDisplayMode(0, &sdl_displaymode); + SDL_GetCurrentDisplayMode(0, &sdl_displaymode); - const char* window_title = + const char* window_title = #ifndef USE_GLES "Super Mario 64 PC port (OpenGL)"; #else @@ -136,10 +147,10 @@ static void gfx_sdl_init(void) { DESIRED_SCREEN_WIDTH, DESIRED_SCREEN_HEIGHT, window_flags); SDL_ShowCursor(SDL_ENABLE); } - + SDL_GL_CreateContext(wnd); SDL_GL_SetSwapInterval(1); // We have a double buffered GL context, it makes no sense to want tearing. - + for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { inverted_scancode_table[windows_scancode_table[i]] = i; } @@ -185,13 +196,9 @@ static void gfx_sdl_onkeydown(int scancode) { const Uint8 *state = SDL_GetKeyboardState(NULL); if (state[SDL_SCANCODE_LALT] && state[SDL_SCANCODE_RETURN]) - { - gfx_sdl_set_fullscreen(!configFullscreen); - } + configFullscreen = !configFullscreen; else if (state[SDL_SCANCODE_ESCAPE] && configFullscreen) - { - gfx_sdl_set_fullscreen(false); - } + configFullscreen = false; } static void gfx_sdl_onkeyup(int scancode) { @@ -215,6 +222,9 @@ static void gfx_sdl_handle_events(void) { exit(0); } } + // just check if the fullscreen value has changed and toggle fullscreen if it has + if (configFullscreen != cur_fullscreen) + gfx_sdl_set_fullscreen(configFullscreen); } static bool gfx_sdl_start_frame(void) { @@ -232,6 +242,15 @@ static double gfx_sdl_get_time(void) { return 0.0; } + +static void gfx_sdl_shutdown(void) { + if (SDL_WasInit(0)) { + if (ctx) { SDL_GL_DeleteContext(ctx); ctx = NULL; } + if (wnd) { SDL_DestroyWindow(wnd); wnd = NULL; } + SDL_Quit(); + } +} + struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_init, gfx_sdl_main_loop, @@ -240,5 +259,6 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_start_frame, gfx_sdl_swap_buffers_begin, gfx_sdl_swap_buffers_end, - gfx_sdl_get_time + gfx_sdl_get_time, + gfx_sdl_shutdown }; diff --git a/src/pc/gfx/gfx_window_manager_api.h b/src/pc/gfx/gfx_window_manager_api.h index 9c68cd10..45826711 100644 --- a/src/pc/gfx/gfx_window_manager_api.h +++ b/src/pc/gfx/gfx_window_manager_api.h @@ -13,6 +13,7 @@ struct GfxWindowManagerAPI { void (*swap_buffers_begin)(void); void (*swap_buffers_end)(void); double (*get_time)(void); // For debug + void (*shutdown)(void); }; #endif diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 5b4bcd23..c198f210 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -18,7 +18,9 @@ #include "audio/audio_sdl.h" #include "audio/audio_null.h" +#include "cliopts.h" #include "configfile.h" +#include "controller/controller_api.h" OSMesg D_80339BEC; OSMesgQueue gSIEventMesgQueue; @@ -72,11 +74,31 @@ void produce_one_frame(void) { create_next_audio_buffer(audio_buffer + i * (num_audio_samples * 2), num_audio_samples); } //printf("Audio samples before submitting: %d\n", audio_api->buffered()); - audio_api->play(audio_buffer, 2 * num_audio_samples * 4); + + // scale by master volume (0-127) + const s32 mod = (s32)configMasterVolume; + for (u32 i = 0; i < num_audio_samples * 4; ++i) + audio_buffer[i] = ((s32)audio_buffer[i] * mod) >> VOLUME_SHIFT; + + audio_api->play((u8*)audio_buffer, 2 * num_audio_samples * 4); gfx_end_frame(); } +void audio_shutdown(void) { + if (audio_api) { + if (audio_api->shutdown) audio_api->shutdown(); + audio_api = NULL; + } +} + +void game_shutdown(void) { + configfile_save(gCLIOpts.ConfigFile);; + controller_shutdown(); + audio_shutdown(); + gfx_shutdown(); +} + #ifdef TARGET_WEB static void em_main_loop(void) { } @@ -110,17 +132,13 @@ static void on_anim_frame(double time) { } #endif -static void save_config(void) { - configfile_save(CONFIG_FILE); -} - void main_func(void) { static u64 pool[0x165000/8 / 4 * sizeof(void *)]; main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0])); gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT); - configfile_load(CONFIG_FILE); - atexit(save_config); + configfile_load(gCLIOpts.ConfigFile); + atexit(game_shutdown); #ifdef TARGET_WEB emscripten_set_main_loop(em_main_loop, 0, 0); @@ -155,6 +173,7 @@ void main_func(void) { } int main(int argc, char *argv[]) { + parse_cli_opts(argc, argv); main_func(); return 0; } diff --git a/tools/Makefile b/tools/Makefile index 467b8d4c..1809ab92 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,5 +1,10 @@ +UNAME := $(shell uname) +ifeq ($(UNAME),Darwin) + OSX_BUILD := -DOSX_BUILD +endif + CC := gcc -CFLAGS := -Llib -Iinclude -I . -Wall -Wextra -Wno-unused-parameter -pedantic -std=c99 -O3 -s +CFLAGS := -Llib -Iinclude -I . -Wall -Wextra -Wno-unused-parameter $(OSX_BUILD) -pedantic -std=c99 -O3 -s PROGRAMS := n64graphics n64graphics_ci mio0 n64cksum textconv patch_libultra_math iplfontutil aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv n64graphics_SOURCES := n64graphics.c utils.c @@ -43,7 +48,7 @@ clean: define COMPILE $(1): $($1_SOURCES) - $(CC) $(CFLAGS) $$^ -lm -o $$@ $($1_CFLAGS) + $(CC) $(CFLAGS) $(OSX_BUILD) $$^ -lm -o $$@ $($1_CFLAGS) endef $(foreach p,$(PROGRAMS),$(eval $(call COMPILE,$(p)))) diff --git a/tools/n64graphics_ci_dir/exoquant/exoquant.c b/tools/n64graphics_ci_dir/exoquant/exoquant.c index 9f33c066..a70bc038 100644 --- a/tools/n64graphics_ci_dir/exoquant/exoquant.c +++ b/tools/n64graphics_ci_dir/exoquant/exoquant.c @@ -23,7 +23,11 @@ SOFTWARE. */ #include "exoquant.h" + +#ifndef OSX_BUILD // OSX build cannot have malloc defined #include +#endif + #include #include #include @@ -705,4 +709,4 @@ exq_float exq_sort_by_dir(const exq_histogram *pHist) pHist->color.g * exq_sort_dir.g + pHist->color.b * exq_sort_dir.b + pHist->color.a * exq_sort_dir.a; -} \ No newline at end of file +}