├── ppc ├── ppc_utils.h ├── ppc_utils.c └── main.c ├── common ├── version.h ├── common_utils.h └── common_utils.c ├── docs └── screenshot.png ├── .gitignore ├── README.md ├── LICENSE.md ├── .github └── workflows │ └── ci.yml └── Makefile /ppc/ppc_utils.h: -------------------------------------------------------------------------------- 1 | void LongWait(int waitTime); 2 | -------------------------------------------------------------------------------- /common/version.h: -------------------------------------------------------------------------------- 1 | #define V_MAJOR 1 2 | #define V_MINOR 2 -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corenting/GC-Controller-Test/HEAD/docs/screenshot.png -------------------------------------------------------------------------------- /common/common_utils.h: -------------------------------------------------------------------------------- 1 | void DrawHeaderText(int activePad); 2 | void SetFgColor(int, int); 3 | void SetBgColor(int, int); 4 | void SetPosition(int, int); 5 | char *GetPadDirection(short, short); 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries 2 | *.elf 3 | *.dol 4 | *.nacp 5 | *.nro 6 | *.nso 7 | *.pfs0 8 | 9 | # Build dirs 10 | build_wii/ 11 | build_gamecube/ 12 | 13 | # IDEs 14 | .vscode/ 15 | .idea/ 16 | -------------------------------------------------------------------------------- /ppc/ppc_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "ppc_utils.h" 6 | 7 | void LongWait(int waitTime) { 8 | int i = 0; 9 | while (i < waitTime) { 10 | VIDEO_WaitVSync(); 11 | i++; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GC-Controller-Test 2 | 3 | ![Build](https://img.shields.io/github/actions/workflow/status/corenting/GC-Controller-Test/ci.yml?branch=master) ![License](https://img.shields.io/github/license/corenting/GC-Controller-Test) 4 | 5 | A simple homebrew to test your Gamecube controllers. 6 | 7 | Works on both Wii and Gamecube. 8 | 9 | ![Screenshot](./docs/screenshot.png) 10 | 11 | ## Downloads 12 | 13 | [See the releases section on Github](https://github.com/corenting/GC-Controller-Test/releases). 14 | 15 | ## How to compile 16 | 17 | - With devkitpro installed: 18 | - For GameCube: `TARGET_CONSOLE=gamecube make` 19 | - For Wii: `TARGET_CONSOLE=wii make` 20 | - Without devkitpro installed, use the `build_docker.sh` which uses the devkitpro/devkitppc Docker image to build: 21 | - For GameCube: `./build_docker.sh gamecube` 22 | - For GameCube: `./build_docker.sh wii` 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | zlib License 2 | 3 | (C) 2023 corenting 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | tags: 8 | - "v*" 9 | pull_request: 10 | branches: 11 | - "master" 12 | 13 | jobs: 14 | shellcheck: 15 | name: ShellCheck 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | - name: Run ShellCheck 21 | uses: ludeeus/action-shellcheck@master 22 | clang-format: 23 | name: clang-format 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v3 28 | - name: clang-format 29 | uses: jidicula/clang-format-action@v4.11.0 30 | with: 31 | clang-format-version: "14" 32 | check-path: "common ppc" 33 | build: 34 | name: Build 35 | runs-on: ubuntu-latest 36 | steps: 37 | - name: Checkout code 38 | uses: actions/checkout@v3 39 | - name: Build GameCube 40 | run: ./build_docker.sh gamecube 41 | - name: Build Wii 42 | run: ./build_docker.sh wii 43 | - name: Upload GameCube artifact 44 | uses: actions/upload-artifact@v3 45 | with: 46 | name: gc-controller-test-gamecube.dol 47 | path: gc-controller-test-gamecube.dol 48 | - name: Upload Wii artifact 49 | uses: actions/upload-artifact@v3 50 | with: 51 | name: gc-controller-test-wii.dol 52 | path: gc-controller-test-wii.dol 53 | -------------------------------------------------------------------------------- /common/common_utils.c: -------------------------------------------------------------------------------- 1 | #include "common_utils.h" 2 | #include "version.h" 3 | #include 4 | 5 | void DrawHeaderText(int activePad) { 6 | SetFgColor(2, 2); 7 | printf("GC Controller Test by corenting (version %d.%d) - ", V_MAJOR, 8 | V_MINOR); 9 | #ifdef __WII__ 10 | printf("Wii version\n\n"); 11 | #endif 12 | #ifdef __GAMECUBE__ 13 | printf("Gamecube version\n\n"); 14 | #endif 15 | SetFgColor(1, 2); 16 | printf("Current controller : %d (press a button on another controller to " 17 | "switch)\n\n\n", 18 | activePad + 1); 19 | SetFgColor(5, 2); 20 | printf("Special functions (hold the buttons) :\n\n"); 21 | SetFgColor(7, 2); 22 | printf(" A + B : rumble test\n"); 23 | #ifdef __WII__ 24 | printf(" L + R : return to the loader\n\n"); 25 | #endif 26 | } 27 | 28 | void SetFgColor(int color, int bold) { 29 | printf("\x1b[%u;%um", color + 30, bold); 30 | fflush(stdout); 31 | } 32 | 33 | void SetBgColor(int color, int bold) { 34 | printf("\x1b[%u;%um", color + 40, bold); 35 | fflush(stdout); 36 | } 37 | 38 | void SetPosition(int column, int row) { printf("\x1b[%d;%dH", row, column); } 39 | 40 | char *GetPadDirection(short x, short y) { 41 | if (y < -50 && x < -50) { 42 | return "(down-left) "; 43 | } 44 | if (y < -50 && x > 50) { 45 | return "(down-right)"; 46 | } 47 | if (y > 50 && x > 50) { 48 | return "(up-right) "; 49 | } 50 | if (y > 50 && x < -50) { 51 | return "(up-left) "; 52 | } 53 | if (y < -65) { 54 | return "(down) "; 55 | } 56 | if (y > 65) { 57 | return "(up) "; 58 | } 59 | if (x < -65) { 60 | return "(left) "; 61 | } 62 | if (x > 65) { 63 | return "(right) "; 64 | } 65 | return " "; 66 | } 67 | -------------------------------------------------------------------------------- /ppc/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "../common/common_utils.h" 7 | #include "ppc_utils.h" 8 | 9 | #define CONSOLE_START_POS 20 10 | 11 | static void *xfb = NULL; 12 | static GXRModeObj *rMode = NULL; 13 | 14 | int main(int argc, char **argv) { 15 | // Init 16 | VIDEO_Init(); 17 | PAD_Init(); 18 | rMode = VIDEO_GetPreferredMode(NULL); 19 | xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rMode)); 20 | console_init(xfb, CONSOLE_START_POS, CONSOLE_START_POS, rMode->fbWidth, 21 | rMode->xfbHeight, rMode->fbWidth * VI_DISPLAY_PIX_SZ); 22 | VIDEO_Configure(rMode); 23 | VIDEO_SetNextFramebuffer(xfb); 24 | VIDEO_SetBlack(FALSE); 25 | VIDEO_Flush(); 26 | 27 | // Wait for video setup to complete 28 | VIDEO_WaitVSync(); 29 | if (rMode->viTVMode & VI_NON_INTERLACE) 30 | VIDEO_WaitVSync(); 31 | 32 | // Vars 33 | int activePad = 0; 34 | u16 keysHeld[4] = {0, 0, 0, 0}; 35 | u16 keysHeldOld[4] = {0, 0, 0, 0}; 36 | 37 | DrawHeaderText(activePad); 38 | while (1) { 39 | PAD_ScanPads(); 40 | for (int i = 0; i < 4; i++) { 41 | PAD_ControlMotor(i, 0); // stop rumble 42 | keysHeld[i] = PAD_ButtonsHeld(i); 43 | } 44 | 45 | // Go to the correct position to draw 46 | #ifdef __WII__ 47 | SetPosition(0, 10); 48 | #endif 49 | #ifdef __GAMECUBE__ 50 | SetPosition(0, 9); 51 | #endif 52 | // Buttons 53 | SetFgColor(5, 2); 54 | printf("Buttons:\n\n"); 55 | SetFgColor(7, 2); 56 | printf((keysHeld[activePad] & PAD_BUTTON_A) ? " A " : " "); 57 | printf((keysHeld[activePad] & PAD_BUTTON_B) ? "B " : " "); 58 | printf((keysHeld[activePad] & PAD_BUTTON_X) ? "X " : " "); 59 | printf((keysHeld[activePad] & PAD_BUTTON_Y) ? "Y " : " "); 60 | printf((keysHeld[activePad] & PAD_TRIGGER_Z) ? "Z " : " "); 61 | printf((keysHeld[activePad] & PAD_TRIGGER_L) ? "L " : " "); 62 | printf((keysHeld[activePad] & PAD_TRIGGER_R) ? "R " : " "); 63 | printf((keysHeld[activePad] & PAD_BUTTON_START) ? "START " : " "); 64 | printf((keysHeld[activePad] & PAD_BUTTON_LEFT) ? "LEFT " : " "); 65 | printf((keysHeld[activePad] & PAD_BUTTON_RIGHT) ? "RIGHT " : " "); 66 | printf((keysHeld[activePad] & PAD_BUTTON_UP) ? "UP " : " "); 67 | printf((keysHeld[activePad] & PAD_BUTTON_DOWN) ? "DOWN " : " "); 68 | 69 | // Sticks and triggers 70 | printf("\n\n"); 71 | SetFgColor(5, 2); 72 | printf("Sticks and triggers (analog) :\n\n"); 73 | SetFgColor(7, 2); 74 | printf(" L trigger : %03d\n", PAD_TriggerL(activePad)); 75 | printf(" R trigger : %03d\n", PAD_TriggerR(activePad)); 76 | printf(" Stick value (X,Y) : %03d,%03d %s\n", PAD_StickX(activePad), 77 | PAD_StickY(activePad), 78 | GetPadDirection(PAD_StickX(activePad), PAD_StickY(activePad))); 79 | printf(" C-stick value (X,Y) : %03d,%03d %s\n", PAD_SubStickX(activePad), 80 | PAD_SubStickY(activePad), 81 | GetPadDirection(PAD_SubStickX(activePad), PAD_SubStickY(activePad))); 82 | 83 | // Special actions 84 | if (keysHeld[activePad] & PAD_BUTTON_A && 85 | keysHeldOld[activePad] & PAD_BUTTON_B) { 86 | PAD_ControlMotor(activePad, 1); 87 | } 88 | #ifdef __WII__ 89 | if (keysHeld[activePad] & PAD_TRIGGER_L && 90 | keysHeldOld[activePad] & PAD_TRIGGER_R) { 91 | exit(0); 92 | } 93 | #endif 94 | 95 | // Active controller modifier 96 | for (int i = 0; i < 4; i++) { 97 | if (keysHeld[i] != 0) { 98 | activePad = i; 99 | SetPosition(21, 2); 100 | SetFgColor(1, 2); 101 | printf("%d", activePad + 1); 102 | } 103 | } 104 | 105 | // Store current state for the next iteration, for the special actions 106 | for (int i = 0; i < 4; i++) { 107 | keysHeldOld[i] = keysHeld[i]; 108 | } 109 | LongWait(5); // wait 5 frames for the next draw 110 | } 111 | exit(0); 112 | } 113 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | # Clear the implicit built in rules 3 | #--------------------------------------------------------------------------------- 4 | .SUFFIXES: 5 | #--------------------------------------------------------------------------------- 6 | ifeq ($(strip $(DEVKITPPC)),) 7 | $(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") 8 | endif 9 | 10 | include $(DEVKITPPC)/$(TARGET_CONSOLE)_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # INCLUDES is a list of directories containing extra header files 17 | #--------------------------------------------------------------------------------- 18 | TARGET := gc-controller-test 19 | BUILD := build_$(TARGET_CONSOLE) 20 | SOURCES := ppc common 21 | DATA := data 22 | INCLUDES := 23 | 24 | #--------------------------------------------------------------------------------- 25 | # options for code generation 26 | #--------------------------------------------------------------------------------- 27 | 28 | TARGET_CONSOLE_UPPER = $(shell echo $(TARGET_CONSOLE) | tr '[:lower:]' '[:upper:]') 29 | CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) -D__$(TARGET_CONSOLE_UPPER)__ 30 | CXXFLAGS = $(CFLAGS) 31 | 32 | LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map 33 | 34 | #--------------------------------------------------------------------------------- 35 | # any extra libraries we wish to link with the project 36 | #--------------------------------------------------------------------------------- 37 | LIBS := -logc -lm 38 | 39 | #--------------------------------------------------------------------------------- 40 | # list of directories containing libraries, this must be the top level containing 41 | # include and lib 42 | #--------------------------------------------------------------------------------- 43 | LIBDIRS := 44 | 45 | #--------------------------------------------------------------------------------- 46 | # no real need to edit anything past this point unless you need to add additional 47 | # rules for different file extensions 48 | #--------------------------------------------------------------------------------- 49 | ifneq ($(BUILD),$(notdir $(CURDIR))) 50 | #--------------------------------------------------------------------------------- 51 | 52 | export OUTPUT := $(CURDIR)/$(TARGET) 53 | 54 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 55 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 56 | 57 | export DEPSDIR := $(CURDIR)/$(BUILD) 58 | 59 | #--------------------------------------------------------------------------------- 60 | # automatically build a list of object files for our project 61 | #--------------------------------------------------------------------------------- 62 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 63 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 64 | sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 65 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) 66 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 67 | 68 | #--------------------------------------------------------------------------------- 69 | # use CXX for linking C++ projects, CC for standard C 70 | #--------------------------------------------------------------------------------- 71 | ifeq ($(strip $(CPPFILES)),) 72 | export LD := $(CC) 73 | else 74 | export LD := $(CXX) 75 | endif 76 | 77 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 78 | export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(sFILES:.s=.o) $(SFILES:.S=.o) 79 | export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) 80 | 81 | export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) 82 | 83 | #--------------------------------------------------------------------------------- 84 | # build a list of include paths 85 | #--------------------------------------------------------------------------------- 86 | export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ 87 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 88 | -I$(CURDIR)/$(BUILD) \ 89 | -I$(LIBOGC_INC) 90 | 91 | #--------------------------------------------------------------------------------- 92 | # build a list of library paths 93 | #--------------------------------------------------------------------------------- 94 | export LIBPATHS := -L$(LIBOGC_LIB) $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 95 | 96 | export OUTPUT := $(CURDIR)/$(TARGET)-$(TARGET_CONSOLE) 97 | .PHONY: $(BUILD) clean 98 | 99 | #--------------------------------------------------------------------------------- 100 | $(BUILD): 101 | @[ -d $@ ] || mkdir -p $@ 102 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 103 | 104 | #--------------------------------------------------------------------------------- 105 | else 106 | 107 | DEPENDS := $(OFILES:.o=.d) 108 | 109 | #--------------------------------------------------------------------------------- 110 | # main targets 111 | #--------------------------------------------------------------------------------- 112 | $(OUTPUT).dol: $(OUTPUT).elf 113 | $(OUTPUT).elf: $(OFILES) 114 | 115 | $(OFILES_SOURCES) : $(HFILES) 116 | 117 | #--------------------------------------------------------------------------------- 118 | # This rule links in binary data with the .jpg extension 119 | #--------------------------------------------------------------------------------- 120 | %.jpg.o %_jpg.h : %.jpg 121 | #--------------------------------------------------------------------------------- 122 | @echo $(notdir $<) 123 | $(bin2o) 124 | 125 | -include $(DEPENDS) 126 | 127 | #--------------------------------------------------------------------------------- 128 | endif 129 | #--------------------------------------------------------------------------------- 130 | --------------------------------------------------------------------------------