diff --git a/.gitignore b/.gitignore index 6fdde468ce..574e8baa04 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,5 @@ GTAGS # Wii U specific *.elf *.rpx +*.wuhb build/ diff --git a/Makefile.wiiu b/Makefile.wiiu index 2b73081b68..72dfb18c35 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -6,9 +6,33 @@ ifeq ($(strip $(DEVKITPRO)),) $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") endif +#------------------------------------------------------------------------------- +# APP_NAME sets the long name of the application +# APP_SHORTNAME sets the short name of the application +# APP_AUTHOR sets the author of the application +#------------------------------------------------------------------------------- +APP_NAME := Crispy Doom U +APP_SHORTNAME := Doom +APP_AUTHOR := thearst3rd + TOPDIR ?= $(CURDIR) -include $(DEVKITPRO)/wut/share/wut_rules +# Use environment variables for devkitPro paths +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +# Default to C:/devkitPro if not set, but allow override +DEVKITPRO_PATH ?= C:/devkitPro +export DEVKITPPC := $(DEVKITPRO_PATH)/devkitPPC + +# Override WUT_ROOT to use forward slashes (works on Windows) +WUT_ROOT := $(DEVKITPRO_PATH)/wut + +include $(DEVKITPRO_PATH)/wut/share/wut_rules + +# Override RPXSPECS to use forward slashes (works on Windows) +RPXSPECS := -specs=$(WUT_ROOT)/share/wut.specs #------------------------------------------------------------------------------- # TARGET is the name of the output @@ -16,28 +40,37 @@ include $(DEVKITPRO)/wut/share/wut_rules # SOURCES is a list of directories containing source code # DATA is a list of directories containing data files # INCLUDES is a list of directories containing header files +# CONTENT is the path to the bundled folder that will be mounted as /vol/content/ +# ICON is the game icon, leave blank to use default rule +# TV_SPLASH is the image displayed during bootup on the TV, leave blank to use default rule +# DRC_SPLASH is the image displayed during bootup on the DRC, leave blank to use default rule #------------------------------------------------------------------------------- TARGET := crispy-doom-u BUILD := build SOURCES := src src/doom opl src/wiiu DATA := wiiu-data INCLUDES := src src/doom opl src/wiiu +CONTENT := +ICON := wiiu/icon.png +TV_SPLASH := +DRC_SPLASH := #------------------------------------------------------------------------------- # options for code generation #------------------------------------------------------------------------------- -CFLAGS := -g -Wall -O2 -ffunction-sections \ +CFLAGS := -g -Wall -O2 -ffunction-sections -fno-plt \ $(MACHDEP) CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ \ - -DBETTER_ANALOG -DBETTER_JOYWAIT -DDISABLE_SDL2NET + -DBETTER_ANALOG -DBETTER_JOYWAIT -DDISABLE_SDL2NET \ + -DHOMEBREW_APP_PATH=\"wiiu/apps/crispy-doom-u\" CXXFLAGS := $(CFLAGS) ASFLAGS := -g $(ARCH) LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -LIBS := -lSDL2_mixer -lmpg123 -lmodplug -lvorbisidec -logg `sdl2-config --libs` -lm -lstdc++ -lwut +LIBS := -lSDL2_mixer `$(DEVKITPRO_PATH)/portlibs/wiiu/bin/sdl2-config --libs` -lm -lstdc++ -lwut #------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level @@ -91,6 +124,34 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) +ifneq (,$(strip $(CONTENT))) + export APP_CONTENT := $(TOPDIR)/$(CONTENT) +endif + +ifneq (,$(strip $(ICON))) + export APP_ICON := $(TOPDIR)/$(ICON) +else ifneq (,$(wildcard $(TOPDIR)/$(TARGET).png)) + export APP_ICON := $(TOPDIR)/$(TARGET).png +else ifneq (,$(wildcard $(TOPDIR)/icon.png)) + export APP_ICON := $(TOPDIR)/icon.png +endif + +ifneq (,$(strip $(TV_SPLASH))) + export APP_TV_SPLASH := $(TOPDIR)/$(TV_SPLASH) +else ifneq (,$(wildcard $(TOPDIR)/tv-splash.png)) + export APP_TV_SPLASH := $(TOPDIR)/tv-splash.png +else ifneq (,$(wildcard $(TOPDIR)/splash.png)) + export APP_TV_SPLASH := $(TOPDIR)/splash.png +endif + +ifneq (,$(strip $(DRC_SPLASH))) + export APP_DRC_SPLASH := $(TOPDIR)/$(DRC_SPLASH) +else ifneq (,$(wildcard $(TOPDIR)/drc-splash.png)) + export APP_DRC_SPLASH := $(TOPDIR)/drc-splash.png +else ifneq (,$(wildcard $(TOPDIR)/splash.png)) + export APP_DRC_SPLASH := $(TOPDIR)/splash.png +endif + .PHONY: $(BUILD) clean all #------------------------------------------------------------------------------- @@ -103,7 +164,7 @@ $(BUILD): #------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr $(BUILD) $(TARGET).rpx $(TARGET).elf + @rm -fr $(BUILD) $(TARGET).wuhb $(TARGET).rpx #------------------------------------------------------------------------------- else @@ -114,8 +175,9 @@ DEPENDS := $(OFILES:.o=.d) #------------------------------------------------------------------------------- # main targets #------------------------------------------------------------------------------- -all : $(OUTPUT).rpx +all : $(OUTPUT).wuhb +$(OUTPUT).wuhb : $(OUTPUT).rpx $(OUTPUT).rpx : $(OUTPUT).elf $(OUTPUT).elf : $(OFILES) @@ -129,6 +191,11 @@ $(OFILES_SRC) : $(HFILES_BIN) @echo $(notdir $<) @$(bin2o) +%.xml.o %_xml.h : %.xml +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + -include $(DEPENDS) #------------------------------------------------------------------------------- diff --git a/src/d_iwad.c b/src/d_iwad.c index 53ee702471..ee455b2880 100644 --- a/src/d_iwad.c +++ b/src/d_iwad.c @@ -636,6 +636,7 @@ static void AddIWADPath(const char *path, const char *suffix) // using standard environment variables. See the XDG Base Directory // Specification: // +__attribute__((unused)) static void AddXdgDirs(void) { const char *env; @@ -700,6 +701,7 @@ static void AddXdgDirs(void) // could parse *.vdf files to more accurately detect installation // locations, but the defaults are likely to be good enough for just // about everyone. +__attribute__((unused)) static void AddSteamDirs(void) { const char *homedir; diff --git a/src/deh_main.c b/src/deh_main.c index 69e7689356..97fc64b092 100644 --- a/src/deh_main.c +++ b/src/deh_main.c @@ -150,7 +150,7 @@ static boolean IsWhitespace(char *s) { for (; *s; ++s) { - if (!isspace(*s)) + if (!isspace((unsigned char)*s)) return false; } @@ -165,14 +165,14 @@ static char *CleanString(char *s) // Leading whitespace - while (*s && isspace(*s)) + while (*s && isspace((unsigned char)*s)) ++s; // Trailing whitespace strending = s + strlen(s) - 1; - while (strlen(s) > 0 && isspace(*strending)) + while (strlen(s) > 0 && isspace((unsigned char)*strending)) { *strending = '\0'; --strending; @@ -341,7 +341,7 @@ static void DEH_ParseContext(deh_context_t *context) return; } - while (line[0] != '\0' && isspace(line[0])) + while (line[0] != '\0' && isspace((unsigned char)line[0])) ++line; if (line[0] == '#') diff --git a/src/doom/d_main.c b/src/doom/d_main.c index 7209463312..ec68954254 100644 --- a/src/doom/d_main.c +++ b/src/doom/d_main.c @@ -79,6 +79,9 @@ #ifdef __WIIU__ #include +#include +#include +#include "wiiu_controller.h" #endif // __WIIU__ @@ -99,6 +102,10 @@ void D_DoomLoop (void); static char *gamedescription; +#ifdef __WIIU__ +#include "wiiu_exit.h" +#endif // __WIIU__ + // Location where savegames are stored char * savegamedir; @@ -522,6 +529,14 @@ void D_RunFrame() static int wipestart; static boolean wipe; +#ifdef __WIIU__ + // Check for exit request from in-game quit menu + if (g_request_app_exit) + { + return; // Exit immediately, main loop will handle cleanup + } +#endif // __WIIU__ + if (wipe) { do @@ -612,26 +627,24 @@ void D_DoomLoop (void) } #ifdef __WIIU__ - while (WHBProcIsRunning()) + // Use wut's WHBProcIsRunning wrapper for simplified ProcUI handling + // Standard WHB pattern - let SDL and WHB handle everything + while (WHBProcIsRunning() && !g_request_app_exit) + { + D_RunFrame(); + } + + // Clean shutdown after main loop exits + if (g_request_app_exit) + { + I_Quit_Real(); // Call the real quit function for proper cleanup + WHBProcShutdown(); + } #else while (1) -#endif // __WIIU__ { D_RunFrame(); } - -#ifdef __WIIU__ - extern int quitsounds2[8]; - extern int quitsounds[8]; - - // From m_menu.c, M_QuitResponse - if (gamemode == commercial) - S_StartSound(NULL,quitsounds2[(gametic>>2)&7]); - else - S_StartSound(NULL,quitsounds[(gametic>>2)&7]); - I_WaitVBL(105); - - I_Quit(); #endif // __WIIU__ } @@ -865,12 +878,12 @@ static char *GetGameName(const char *gamename) DEH_snprintf(deh_gamename, gamename_size, banners[i], version / 100, version % 100); - while (deh_gamename[0] != '\0' && isspace(deh_gamename[0])) + while (deh_gamename[0] != '\0' && isspace((unsigned char)deh_gamename[0])) { memmove(deh_gamename, deh_gamename + 1, gamename_size - 1); } - while (deh_gamename[0] != '\0' && isspace(deh_gamename[strlen(deh_gamename)-1])) + while (deh_gamename[0] != '\0' && isspace((unsigned char)deh_gamename[strlen(deh_gamename)-1])) { deh_gamename[strlen(deh_gamename) - 1] = '\0'; } @@ -1450,7 +1463,7 @@ static void G_CheckDemoStatusAtExit (void) G_CheckDemoStatus(); } -static const char *const loadparms[] = {"-file", "-merge", NULL}; +static const char *const loadparms[] __attribute__((unused)) = {"-file", "-merge", NULL}; // // D_DoomMain @@ -1694,6 +1707,10 @@ void D_DoomMain (void) modifiedgame = false; DEH_printf("W_Init: Init WADfiles.\n"); +#ifdef __WIIU__ + // Present stable black frame before blocking WAD load to prevent garbage scan-out + I_PresentBlackFrame(); +#endif // __WIIU__ D_AddFile(iwadfile); numiwadlumps = numlumps; @@ -1840,6 +1857,10 @@ void D_DoomMain (void) DEH_ParseCommandLine(); // Load PWAD files. +#ifdef __WIIU__ + // Present stable black frame before blocking PWAD load to prevent garbage scan-out + I_PresentBlackFrame(); +#endif // __WIIU__ modifiedgame = W_ParseCommandLine(); //! diff --git a/src/doom/doom_icon.c b/src/doom/doom_icon.c index a707da2c61..23c32c6c99 100644 --- a/src/doom/doom_icon.c +++ b/src/doom/doom_icon.c @@ -1,7 +1,7 @@ -static int doom_icon_w = 128; -static int doom_icon_h = 128; +static int doom_icon_w __attribute__((unused)) = 128; +static int doom_icon_h __attribute__((unused)) = 128; -static const unsigned int doom_icon_data[] = { +static const unsigned int doom_icon_data[] __attribute__((unused)) = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/src/doom/f_finale.c b/src/doom/f_finale.c index 293955c08b..8426060ddd 100644 --- a/src/doom/f_finale.c +++ b/src/doom/f_finale.c @@ -578,7 +578,7 @@ void F_CastTicker (void) goto stopattack; // Oh, gross hack! */ // [crispy] Allow A_RandomJump() in deaths in cast sequence - if (caststate->action.acp1 == A_RandomJump && Crispy_Random() < caststate->misc2) + if ((void*)caststate->action.acp1 == (void*)A_RandomJump && Crispy_Random() < caststate->misc2) { st = caststate->misc1; } @@ -671,7 +671,7 @@ void F_CastTicker (void) if (casttics == -1) { // [crispy] Allow A_RandomJump() in deaths in cast sequence - if (caststate->action.acp1 == A_RandomJump) + if ((void*)caststate->action.acp1 == (void*)A_RandomJump) { if (Crispy_Random() < caststate->misc2) { @@ -746,7 +746,7 @@ boolean F_CastResponder (event_t* ev) caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; casttics = caststate->tics; // [crispy] Allow A_RandomJump() in deaths in cast sequence - if (casttics == -1 && caststate->action.acp1 == A_RandomJump) + if (casttics == -1 && (void*)caststate->action.acp1 == (void*)A_RandomJump) { if (Crispy_Random() < caststate->misc2) { diff --git a/src/doom/m_crispy.c b/src/doom/m_crispy.c index acda0d55fb..a816a47f83 100644 --- a/src/doom/m_crispy.c +++ b/src/doom/m_crispy.c @@ -592,7 +592,13 @@ void M_CrispyToggleVsync(int choice) return; } +#ifdef __WIIU__ + // Prevent disabling VSYNC on Wii U to avoid HOME menu hang issue + // SDL bug: GX2SetSwapInterval(0) causes black screen after HOME menu + return; +#else crispy->post_rendering_hook = M_CrispyToggleVsyncHook; +#endif // __WIIU__ } static int hookchoice; diff --git a/src/doom/m_menu.c b/src/doom/m_menu.c index 6fdcb49850..805f4350b0 100644 --- a/src/doom/m_menu.c +++ b/src/doom/m_menu.c @@ -27,6 +27,9 @@ #include "dstrings.h" #include "d_main.h" +#ifdef __WIIU__ +#include "wiiu_exit.h" +#endif // __WIIU__ #include "deh_main.h" #include "i_input.h" @@ -1022,15 +1025,15 @@ static boolean StartsWithMapIdentifier (char *str) M_ForceUppercase(str); if (strlen(str) >= 4 && - str[0] == 'E' && isdigit(str[1]) && - str[2] == 'M' && isdigit(str[3])) + str[0] == 'E' && isdigit((unsigned char)str[1]) && + str[2] == 'M' && isdigit((unsigned char)str[3])) { return true; } if (strlen(str) >= 5 && str[0] == 'M' && str[1] == 'A' && str[2] == 'P' && - isdigit(str[3]) && isdigit(str[4])) + isdigit((unsigned char)str[3]) && isdigit((unsigned char)str[4])) { return true; } @@ -1659,6 +1662,7 @@ void M_Options(int choice) } // [crispy] correctly handle inverted y-axis +__attribute__((unused)) static void M_Mouse(int choice) { if (mouseSensitivity_y < 0) @@ -1815,7 +1819,7 @@ int quitsounds2[8] = void M_QuitResponse(int key) { - extern int show_endoom; + // extern int show_endoom; // Unused variable if (key != key_menu_confirm) return; @@ -1832,7 +1836,13 @@ void M_QuitResponse(int key) S_StartSound(NULL,quitsounds[(gametic>>2)&7]); I_WaitVBL(105); } +#ifdef __WIIU__ + // Request clean exit instead of calling I_Quit() directly + // This allows ProcUI to handle the exit handshake properly + platform_request_exit(); +#else I_Quit (); +#endif // __WIIU__ } @@ -1861,7 +1871,11 @@ void M_QuitDOOM(int choice) { // [crispy] fast exit if "run" key is held down if (speedkeydown()) +#ifdef __WIIU__ + platform_request_exit(); +#else I_Quit(); +#endif // __WIIU__ DEH_snprintf(endstring, sizeof(endstring), "%s\n\n" DOSY, DEH_String(M_SelectEndMessage())); diff --git a/src/doom/p_bexptr.c b/src/doom/p_bexptr.c index 0212a4f171..c173e04807 100644 --- a/src/doom/p_bexptr.c +++ b/src/doom/p_bexptr.c @@ -22,8 +22,8 @@ #include "m_random.h" #include "s_sound.h" -extern void A_Explode(); -extern void A_FaceTarget(); +extern void A_Explode(mobj_t* actor); +extern void A_FaceTarget(mobj_t* actor); extern boolean P_CheckMeleeRange (mobj_t *actor); extern void P_Thrust (player_t* player, angle_t angle, fixed_t move); diff --git a/src/doom/st_stuff.c b/src/doom/st_stuff.c index bbbba9e6d4..de7bcbb002 100644 --- a/src/doom/st_stuff.c +++ b/src/doom/st_stuff.c @@ -907,7 +907,7 @@ ST_Responder (event_t* ev) cht_GetParam(&cheat_mus1, buf); - return isdigit(buf[0]); + return isdigit((unsigned char)buf[0]); } // [crispy] allow both idspispopd and idclip cheats in all gamemissions else if ( ( /* logical_gamemission == doom @@ -1360,7 +1360,7 @@ ST_Responder (event_t* ev) cht_GetParam(&cheat_clev1, buf); - return isdigit(buf[0]); + return isdigit((unsigned char)buf[0]); } } return false; diff --git a/src/doomtype.h b/src/doomtype.h index 69183dc650..714b93a420 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -110,10 +110,13 @@ typedef bool boolean; typedef enum { - false, - true + boolean_false, + boolean_true } boolean; +#define false boolean_false +#define true boolean_true + #endif typedef uint8_t byte; diff --git a/src/gusconf.c b/src/gusconf.c index eed6c5b243..6fb30d72d2 100644 --- a/src/gusconf.c +++ b/src/gusconf.c @@ -79,7 +79,7 @@ static int SplitLine(char *line, char **fields, unsigned int max_fields) do { ++p; - } while (*p != '\0' && isspace(*p)); + } while (*p != '\0' && isspace((unsigned char)*p)); fields[num_fields] = p; ++num_fields; @@ -99,7 +99,7 @@ static int SplitLine(char *line, char **fields, unsigned int max_fields) // Strip off trailing whitespace from the end of the line. p = fields[num_fields - 1] + strlen(fields[num_fields - 1]); - while (p > fields[num_fields - 1] && isspace(*(p - 1))) + while (p > fields[num_fields - 1] && isspace((unsigned char)*(p - 1))) { --p; *p = '\0'; diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c index 20e7254fbf..bf0056680a 100644 --- a/src/heretic/d_main.c +++ b/src/heretic/d_main.c @@ -61,6 +61,10 @@ GameMode_t gamemode = indetermined; const char *gamedescription = "unknown"; +#ifdef __WIIU__ +#include "wiiu_exit.h" +#endif // __WIIU__ + boolean nomonsters; // checkparm of -nomonsters boolean respawnparm; // checkparm of -respawn boolean debugmode; // checkparm of -debug @@ -392,6 +396,42 @@ void D_DoomLoop(void) main_loop_started = true; +#ifdef __WIIU__ + // Use wut's WHBProcIsRunning wrapper for simplified ProcUI handling + while (WHBProcIsRunning() && !g_request_app_exit) + { + // Check for exit request from in-game quit menu + if (g_request_app_exit) + break; + + // Frame syncronous IO operations + I_StartFrame(); + + // Process one or more tics + // Will run at least one tic + TryRunTics(); + + // Move positional sounds + S_UpdateSounds(players[consoleplayer].mo); + D_Display(); + + // [crispy] post-rendering function pointer to apply config changes + // that affect rendering and that are better applied after the current + // frame has finished rendering + if (crispy->post_rendering_hook) + { + crispy->post_rendering_hook(); + crispy->post_rendering_hook = NULL; + } + } + + // Clean shutdown after main loop exits + if (g_request_app_exit) + { + I_Quit_Real(); // Call the real quit function for proper cleanup + WHBProcShutdown(); + } +#else while (1) { // Frame syncronous IO operations @@ -414,6 +454,7 @@ void D_DoomLoop(void) crispy->post_rendering_hook = NULL; } } +#endif // __WIIU__ } /* diff --git a/src/heretic/mn_menu.c b/src/heretic/mn_menu.c index 8531ed0f41..7c601b700f 100644 --- a/src/heretic/mn_menu.c +++ b/src/heretic/mn_menu.c @@ -22,6 +22,9 @@ #include "deh_str.h" #include "doomdef.h" #include "doomkeys.h" +#ifdef __WIIU__ +#include "wiiu_exit.h" +#endif // __WIIU__ #include "i_input.h" #include "i_system.h" #include "i_swap.h" @@ -1568,9 +1571,15 @@ static void CrispyVsyncHook(void) static boolean CrispyVsync(int option) { +#ifdef __WIIU__ + // Prevent disabling VSYNC on Wii U to avoid HOME menu hang issue + // SDL bug: GX2SetSwapInterval(0) causes black screen after HOME menu + return true; +#else crispy->post_rendering_hook = CrispyVsyncHook; return true; +#endif // __WIIU__ } static boolean CrispyBrightmaps(int option) @@ -1784,8 +1793,13 @@ boolean MN_Responder(event_t * event) { case 1: G_CheckDemoStatus(); +#ifdef __WIIU__ + platform_request_exit(); + return false; +#else I_Quit(); return false; +#endif // __WIIU__ case 2: players[consoleplayer].messageTics = 0; diff --git a/src/hexen/h2_main.c b/src/hexen/h2_main.c index e64dc293bb..06a5253f3e 100644 --- a/src/hexen/h2_main.c +++ b/src/hexen/h2_main.c @@ -94,6 +94,11 @@ static void CrispyDrawStats(void); // [crispy] GameMode_t gamemode; static const char *gamedescription; + +#ifdef __WIIU__ +#include "wiiu_exit.h" +#endif // __WIIU__ + char *iwadfile; static char demolumpname[9]; // Demo lump to start playing. boolean nomonsters; // checkparm of -nomonsters @@ -933,6 +938,43 @@ void H2_GameLoop(void) I_RegisterWindowIcon(hexen_icon_data, hexen_icon_w, hexen_icon_h); I_InitGraphics(); +#ifdef __WIIU__ + // Use wut's WHBProcIsRunning wrapper for simplified ProcUI handling + while (WHBProcIsRunning() && !g_request_app_exit) + { + // Check for exit request from in-game quit menu + if (g_request_app_exit) + break; + + // Frame syncronous IO operations + I_StartFrame(); + + // Process one or more tics + // Will run at least one tic + TryRunTics(); + + // Move positional sounds + S_UpdateSounds(players[displayplayer].mo); + + DrawAndBlit(); + + // [crispy] post-rendering function pointer to apply config changes + // that affect rendering and that are better applied after the current + // frame has finished rendering + if (crispy->post_rendering_hook) + { + crispy->post_rendering_hook(); + crispy->post_rendering_hook = NULL; + } + } + + // Clean shutdown after main loop exits + if (g_request_app_exit) + { + I_Quit_Real(); // Call the real quit function for proper cleanup + WHBProcShutdown(); + } +#else while (1) { // Frame syncronous IO operations @@ -956,6 +998,7 @@ void H2_GameLoop(void) crispy->post_rendering_hook = NULL; } } +#endif // __WIIU__ } //========================================================================== diff --git a/src/hexen/mn_menu.c b/src/hexen/mn_menu.c index 9747bc367e..122b19e1a8 100644 --- a/src/hexen/mn_menu.c +++ b/src/hexen/mn_menu.c @@ -20,6 +20,9 @@ #include #include "h2def.h" #include "doomkeys.h" +#ifdef __WIIU__ +#include "wiiu_exit.h" +#endif // __WIIU__ #include "i_input.h" #include "i_system.h" #include "i_swap.h" @@ -1606,7 +1609,13 @@ static void CrispyVsyncHook(void) static void CrispyVsync(int option) { +#ifdef __WIIU__ + // Prevent disabling VSYNC on Wii U to avoid HOME menu hang issue + // SDL bug: GX2SetSwapInterval(0) causes black screen after HOME menu + return; +#else crispy->post_rendering_hook = CrispyVsyncHook; +#endif // __WIIU__ } static void CrispyBrightmaps(int option) @@ -1798,8 +1807,13 @@ boolean MN_Responder(event_t * event) { case 1: G_CheckDemoStatus(); +#ifdef __WIIU__ + platform_request_exit(); + return false; +#else I_Quit(); return false; +#endif // __WIIU__ case 2: P_ClearMessage(&players[consoleplayer]); askforquit = false; diff --git a/src/i_joystick.c b/src/i_joystick.c index ed50e26d62..6d9be2d95e 100644 --- a/src/i_joystick.c +++ b/src/i_joystick.c @@ -133,6 +133,7 @@ void I_ShutdownJoystick(void) #endif // __WIIU__ } +__attribute__((unused)) static boolean IsValidAxis(int axis) { int num_axes; diff --git a/src/i_main.c b/src/i_main.c index 2dfe3b7c0f..46cb2db03a 100644 --- a/src/i_main.c +++ b/src/i_main.c @@ -33,6 +33,8 @@ #ifdef __WIIU__ #include +#include +#include #include "wiiu_launcher.h" #include "wiiu_controller.h" #endif // __WIIU__ @@ -59,9 +61,28 @@ int main(int argc, char **argv) } #ifdef __WIIU__ + // Initialize wut's ProcUI wrapper WHBProcInit(); + + // Register manual ProcUI cleanup to ensure proper shutdown + extern void cleanup_procui(void); + atexit(cleanup_procui); + WiiU_InitJoystick(); + AXInit(); // Kill sounds launcherRun(); + + extern int launcherRunning; + if (launcherRunning < 0) + { + // Clean shutdown using wut wrapper + extern void WiiU_OSScreenCompleteShutdown(void); + WiiU_OSScreenCompleteShutdown(); // Use new shutdown system + WiiU_ShutdownJoystick(); + AXQuit(); + WHBProcShutdown(); + exit(0); + } #endif // __WIIU__ //! @@ -69,7 +90,12 @@ int main(int argc, char **argv) // if (M_ParmExists("-version") || M_ParmExists("--version")) { puts(PACKAGE_STRING); +#ifdef __WIIU__ + // Use proper shutdown for version check + I_Quit(); +#else exit(0); +#endif } { diff --git a/src/i_musicpack.c b/src/i_musicpack.c index 6b395a94c7..2ffd11fa8b 100644 --- a/src/i_musicpack.c +++ b/src/i_musicpack.c @@ -803,9 +803,9 @@ static const char *ReadHashPrefix(char *line) char *p; int i, len; - for (p = line; *p != '\0' && !isspace(*p) && *p != '='; ++p) + for (p = line; *p != '\0' && !isspace((unsigned char)*p) && *p != '='; ++p) { - if (!isxdigit(*p)) + if (!isxdigit((unsigned char)*p)) { return NULL; } @@ -845,7 +845,7 @@ static const char *ParseSubstituteLine(char *musicdir, char *line) p = strchr(line, '#'); if (p != NULL) { - while (p > line && isspace(*(p - 1))) + while (p > line && isspace((unsigned char)*(p - 1))) { --p; } @@ -853,7 +853,7 @@ static const char *ParseSubstituteLine(char *musicdir, char *line) } // Skip leading spaces. - for (p = line; *p != '\0' && isspace(*p); ++p); + for (p = line; *p != '\0' && isspace((unsigned char)*p); ++p); // Empty line? This includes comment lines now that comments have // been stripped. @@ -871,7 +871,7 @@ static const char *ParseSubstituteLine(char *musicdir, char *line) p += strlen(hash_prefix); // Skip spaces. - for (; *p != '\0' && isspace(*p); ++p); + for (; *p != '\0' && isspace((unsigned char)*p); ++p); if (*p != '=') { @@ -881,12 +881,12 @@ static const char *ParseSubstituteLine(char *musicdir, char *line) ++p; // Skip spaces. - for (; *p != '\0' && isspace(*p); ++p); + for (; *p != '\0' && isspace((unsigned char)*p); ++p); filename = p; // We're now at the filename. Cut off trailing space characters. - while (strlen(p) > 0 && isspace(p[strlen(p) - 1])) + while (strlen(p) > 0 && isspace((unsigned char)p[strlen(p) - 1])) { p[strlen(p) - 1] = '\0'; } diff --git a/src/i_sound.c b/src/i_sound.c index 3a4894be5f..c9368c8e5b 100644 --- a/src/i_sound.c +++ b/src/i_sound.c @@ -207,7 +207,8 @@ static void InitMusicModule(void) void I_InitSound(boolean use_sfx_prefix) { - boolean nosound, nosfx, nomusic, nomusicpacks; + boolean nosound, nosfx, nomusic; + boolean nomusicpacks __attribute__((unused)); //! // @vanilla diff --git a/src/i_system.c b/src/i_system.c index 1a57e7584d..3b2e78ec7c 100644 --- a/src/i_system.c +++ b/src/i_system.c @@ -55,10 +55,13 @@ #ifdef __WIIU__ #include #include +#include #include #include #include +#include +#include "wiiu_controller.h" #endif // __WIIU__ @@ -253,7 +256,17 @@ void I_BindVariables(void) // I_Quit // +#ifdef __WIIU__ void I_Quit (void) +{ + // Let WHB handle the exit process + // The main loop will exit when WHBProcIsRunning() returns false +} + +void I_Quit_Real (void) +#else +void I_Quit (void) +#endif { atexit_listentry_t *entry; @@ -270,15 +283,14 @@ void I_Quit (void) SDL_Quit(); #ifdef __WIIU__ - WHBProcShutdown(); - for (int i = 0; i < myargc; i++) - { - free(myargv[i]); - } - free(myargv); + // Memory cleanup is handled in I_Quit to avoid duplication #endif +#ifndef __WIIU__ exit(0); +#else + // exit(0) is already called in I_Quit for Wii U +#endif } @@ -297,7 +309,7 @@ void I_Error (const char *error, ...) char msgbuf[512]; va_list argptr; atexit_listentry_t *entry; - boolean exit_gui_popup; + // boolean exit_gui_popup; // Unused variable if (already_quitting) { @@ -306,11 +318,12 @@ void I_Error (const char *error, ...) WHBLogConsoleDraw(); OSSleepTicks(OSMillisecondsToTicks(5000)); WHBLogConsoleFree(); - WHBProcShutdown(); + // Emergency exit + exit(-1); #else fprintf(stderr, "Warning: recursive call to I_Error detected.\n"); -#endif // __WIIU__ exit(-1); +#endif // __WIIU__ } else { @@ -383,6 +396,8 @@ void I_Error (const char *error, ...) WHBLogConsoleDraw(); OSSleepTicks(OSMillisecondsToTicks(5000)); WHBLogConsoleFree(); + + // Use wut's ProcUI wrapper for error exit WHBProcShutdown(); #endif // !__WIIU__ diff --git a/src/i_system.h b/src/i_system.h index 78376f3cc7..c4f88193d7 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -50,7 +50,12 @@ ticcmd_t* I_BaseTiccmd (void); // Called by M_Responder when quit is selected. // Clean exit, displays sell blurb. +#ifdef __WIIU__ +void I_Quit (void); +void I_Quit_Real (void); +#else void I_Quit (void) NORETURN; +#endif void I_Error (const char *error, ...) NORETURN PRINTF_ATTR(1, 2); diff --git a/src/i_video.c b/src/i_video.c index 70d60897a4..bf22da663c 100644 --- a/src/i_video.c +++ b/src/i_video.c @@ -60,6 +60,25 @@ int WIDESCREENDELTA; // [crispy] horizontal widescreen offset static SDL_Window *screen; static SDL_Renderer *renderer; +// Helper function to present a stable black frame during blocking operations +void I_PresentBlackFrame(void) { + if (renderer != NULL) { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + SDL_RenderPresent(renderer); // second call ensures both TV and GamePad buffers are valid + +#ifdef __WIIU__ + // Complete OSScreen shutdown now that SDL has presented its first frame + extern boolean WiiU_OSScreenShutdownPending(void); + extern void WiiU_OSScreenCompleteShutdown(void); + if (WiiU_OSScreenShutdownPending()) { + WiiU_OSScreenCompleteShutdown(); + } +#endif // __WIIU__ + } +} + // Window title static const char *window_title = ""; @@ -173,7 +192,7 @@ int force_software_renderer = false; // Time to wait for the screen to settle on startup before starting the // game (ms) -static int startup_delay = 1000; +static int startup_delay __attribute__((unused)) = 1000; // Grab the mouse? (int type for config code). nograbmouse_override allows // this to be temporarily disabled via the command line. @@ -599,6 +618,7 @@ static void UpdateGrab(void) currently_grabbed = grab; } +__attribute__((unused)) static void LimitTextureSize(int *w_upscale, int *h_upscale) { SDL_RendererInfo rinfo; @@ -1324,6 +1344,7 @@ void I_CheckIsScreensaver(void) } } +__attribute__((unused)) static void SetSDLVideoDriver(void) { // Allow a default value for the SDL video driver to be specified @@ -1498,10 +1519,19 @@ static void SetVideoMode(void) // Turn on vsync if we aren't in a -timedemo if (!singletics && mode.refresh_rate > 0) { +#ifdef __WIIU__ + // Force VSYNC on Wii U to prevent HOME menu hang issue + // When leaving foreground, GX2 silently restores swap interval to 1 + // SDL doesn't reapply zero-interval mode after regaining foreground + // Keeping VSYNC enabled avoids this SDL bug + renderer_flags |= SDL_RENDERER_PRESENTVSYNC; + crispy->vsync = true; // Ensure config matches +#else if (crispy->vsync) // [crispy] uncapped vsync { renderer_flags |= SDL_RENDERER_PRESENTVSYNC; } +#endif // __WIIU__ } if (force_software_renderer) @@ -1544,6 +1574,13 @@ static void SetVideoMode(void) SDL_GetError()); } +#ifdef __WIIU__ + // Clear screen to prevent corruption during initialization + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); +#endif // __WIIU__ + // Important: Set the "logical size" of the rendering context. At the same // time this also defines the aspect ratio that is preserved while scaling // and stretching the texture into the window. diff --git a/src/i_video.h b/src/i_video.h index 32231721c9..afc31486d8 100644 --- a/src/i_video.h +++ b/src/i_video.h @@ -51,6 +51,7 @@ typedef boolean (*grabmouse_callback_t)(void); // determines the hardware configuration // and sets up the video mode void I_InitGraphics (void); +void I_PresentBlackFrame(void); void I_GraphicsCheckCommandLine(void); diff --git a/src/m_argv.c b/src/m_argv.c index 55db0a5300..f3edaf3a86 100644 --- a/src/m_argv.c +++ b/src/m_argv.c @@ -143,7 +143,7 @@ static void LoadResponseFile(int argv_index, const char *filename) { // Skip past space characters to the next argument - while(k < size && isspace(infile[k])) + while(k < size && isspace((unsigned char)infile[k])) { ++k; } @@ -191,7 +191,7 @@ static void LoadResponseFile(int argv_index, const char *filename) argstart = &infile[k]; - while(k < size && !isspace(infile[k])) + while(k < size && !isspace((unsigned char)infile[k])) { ++k; } diff --git a/src/m_config.c b/src/m_config.c index ed8c9f8eb5..4d4fbb4dd1 100644 --- a/src/m_config.c +++ b/src/m_config.c @@ -2791,7 +2791,7 @@ static void SetVariable(default_t *def, const char *value) for ( ; str[i] != '\0'; i++) { - if (!isdigit(str[i])) + if (!isdigit((unsigned char)str[i])) { str[i] = dec; break; @@ -2847,7 +2847,7 @@ static void LoadDefaultCollection(default_collection_t *collection) // Strip off trailing non-printable characters (\r characters // from DOS text files) - while (strlen(strparm) > 0 && !isprint(strparm[strlen(strparm)-1])) + while (strlen(strparm) > 0 && !isprint((unsigned char)strparm[strlen(strparm)-1])) { strparm[strlen(strparm)-1] = '\0'; } diff --git a/src/net_packet.c b/src/net_packet.c index 23bfda675e..e20c10c979 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -223,7 +223,7 @@ char *NET_ReadSafeString(net_packet_t *packet) // TODO: This is a very naive way of producing a safe string; only // ASCII characters are allowed. Probably this should really support // UTF-8 characters as well. - if (isprint(*r) || *r == '\n') + if (isprint((unsigned char)*r) || *r == '\n') { *w = *r; ++w; diff --git a/src/strife/d_main.c b/src/strife/d_main.c index b56cb464a6..3b8c22f82e 100644 --- a/src/strife/d_main.c +++ b/src/strife/d_main.c @@ -93,6 +93,10 @@ // void D_DoomLoop (void); +#ifdef __WIIU__ +#include "wiiu_exit.h" +#endif // __WIIU__ + static boolean D_AddFile(char *filename); // Location where savegames are stored @@ -616,6 +620,34 @@ void D_DoomLoop (void) wipegamestate = gamestate; } +#ifdef __WIIU__ + // Use wut's WHBProcIsRunning wrapper for simplified ProcUI handling + while (WHBProcIsRunning() && !g_request_app_exit) + { + // Check for exit request from in-game quit menu + if (g_request_app_exit) + break; + + // frame syncronous IO operations + I_StartFrame (); + + // process one or more tics + TryRunTics (); // will run at least one tic + + S_UpdateSounds (players[consoleplayer].mo);// move positional sounds + + // Update display, next frame, with current state. + if (screenvisible) + D_Display (); + } + + // Clean shutdown after main loop exits + if (g_request_app_exit) + { + I_Quit_Real(); // Call the real quit function for proper cleanup + WHBProcShutdown(); + } +#else while (1) { // frame syncronous IO operations @@ -630,6 +662,7 @@ void D_DoomLoop (void) if (screenvisible) D_Display (); } +#endif // __WIIU__ } diff --git a/src/strife/m_menu.c b/src/strife/m_menu.c index e88ca7e572..f5af44b831 100644 --- a/src/strife/m_menu.c +++ b/src/strife/m_menu.c @@ -27,6 +27,9 @@ #include "dstrings.h" #include "d_main.h" +#ifdef __WIIU__ +#include "wiiu_exit.h" +#endif // __WIIU__ #include "deh_main.h" #include "i_input.h" @@ -1755,7 +1758,11 @@ void M_QuitResponse(int key) // [crispy] quit immediately if not showing exit screen if(!show_exitscreen || netgame) +#ifdef __WIIU__ + platform_request_exit(); +#else I_Quit(); +#endif // __WIIU__ else { DEH_snprintf(buffer, sizeof(buffer), "qfmrm%i", gametic % 8 + 1); @@ -1782,7 +1789,11 @@ void M_QuitStrife(int choice) { // [crispy] fast exit if "run" key is held down if (speedkeydown()) +#ifdef __WIIU__ + platform_request_exit(); +#else I_Quit(); +#endif // __WIIU__ DEH_snprintf(endstring, sizeof(endstring), "Do you really want to leave?\n\n" DOSY); @@ -2229,7 +2240,11 @@ boolean M_Responder (event_t* ev) || (ev->type == ev_keydown && (ev->data1 == key_menu_activate || ev->data1 == key_menu_quit))) { +#ifdef __WIIU__ + D_RequestAppExit(); +#else I_Quit(); +#endif // __WIIU__ return true; } diff --git a/src/w_merge.c b/src/w_merge.c index 39a83eb7e6..3d95c53f68 100644 --- a/src/w_merge.c +++ b/src/w_merge.c @@ -163,14 +163,14 @@ static boolean ValidSpriteLumpName(char *name) // First frame: - if (name[4] == '\0' || !isdigit(name[5])) + if (name[4] == '\0' || !isdigit((unsigned char)name[5])) { return false; } // Second frame (optional): - if (name[6] != '\0' && !isdigit(name[7])) + if (name[6] != '\0' && !isdigit((unsigned char)name[7])) { return false; } diff --git a/src/wiiu/audio_stubs.c b/src/wiiu/audio_stubs.c new file mode 100644 index 0000000000..7635046102 --- /dev/null +++ b/src/wiiu/audio_stubs.c @@ -0,0 +1,46 @@ +// +// Audio library stubs for missing libraries +// This file provides empty implementations for missing audio library functions +// to allow SDL2_mixer to link without the actual libraries +// + +#ifdef __WIIU__ + +#include +#include + +// ModPlug stubs +void ModPlug_Seek(void* file, int millisecond) {} +int ModPlug_Read(void* file, void* buffer, int size) { return 0; } +void* ModPlug_Load(const void* data, int size) { return NULL; } +void ModPlug_Unload(void* file) {} +int ModPlug_SeekOrder(void* file, int order) { return 0; } +int ModPlug_GetLength(void* file) { return 0; } +void ModPlug_GetSettings(void* file, void* settings) {} +void ModPlug_SetSettings(void* file, void* settings) {} +void ModPlug_SetMasterVolume(void* file, int volume) {} +const char* ModPlug_GetName(void* file) { return NULL; } + +// mpg123 stubs +int mpg123_exit(void) { return 0; } +int mpg123_close(void* mh) { return 0; } +void mpg123_delete(void* mh) {} +int mpg123_format(void* mh, long rate, int channels, int encodings) { return 0; } +int mpg123_format_none(void* mh) { return 0; } +int mpg123_getformat(void* mh, long* rate, int* channels, int* encoding) { return 0; } +int mpg123_init(void) { return 0; } +void* mpg123_new(const char* decoder, int* error) { return NULL; } +const long* mpg123_rates(void) { return NULL; } +int mpg123_open_handle(void* mh, void* iohandle) { return 0; } +const char* mpg123_plain_strerror(int errcode) { return "mpg123 not available"; } +int mpg123_read(void* mh, unsigned char* outmemory, size_t outmemsize, size_t* done) { return 0; } +int mpg123_replace_reader_handle(void* mh, ssize_t (*r_read)(void*, void*, size_t), off_t (*r_lseek)(void*, off_t, int), void (*cleanup)(void*)) { return 0; } +off_t mpg123_seek(void* mh, off_t sampleoff, int whence) { return 0; } +off_t mpg123_tell(void* mh) { return 0; } +off_t mpg123_length(void* mh) { return 0; } +const char* mpg123_strerror(void* mh) { return "mpg123 not available"; } + +// vorbisidec stubs (if needed) +// These would be added if vorbisidec functions are referenced + +#endif // __WIIU__ diff --git a/src/wiiu/wiiu_controller.c b/src/wiiu/wiiu_controller.c index fbf522395b..41c15c61d0 100644 --- a/src/wiiu/wiiu_controller.c +++ b/src/wiiu/wiiu_controller.c @@ -113,7 +113,8 @@ void WiiU_InitJoystick() void WiiU_ShutdownJoystick() { - // Anything to do here? + KPADShutdown(); + VPADShutdown(); } static void read_vpad() @@ -186,8 +187,8 @@ static void read_wpad_chan(WPADChan chan) buttonsPressed |= (1 << i); } - stickX += status.nunchuck.stick.x * 0x7ff0; - stickY -= status.nunchuck.stick.y * 0x7ff0; + stickX += status.nunchuk.stick.x * 0x7ff0; + stickY -= status.nunchuk.stick.y * 0x7ff0; // no rStick... } else if (status.extensionType == WPAD_EXT_CLASSIC || status.extensionType == WPAD_EXT_MPLUS_CLASSIC) diff --git a/src/wiiu/wiiu_launcher.c b/src/wiiu/wiiu_launcher.c index 7cc717c243..c8e29585bf 100644 --- a/src/wiiu/wiiu_launcher.c +++ b/src/wiiu/wiiu_launcher.c @@ -28,7 +28,11 @@ #include #include #include -#include +#include +#include +#include +#include +#include #include "config.h" @@ -38,6 +42,7 @@ #include "wiiu_launcher_main.h" #include "wiiu_launcher_nowads.h" #include "wiiu_controller.h" +#include "wiiu_oscreen.h" // Global variables int launcherRunning = 1; // 0 means go to game, -1 means quit @@ -107,39 +112,15 @@ void generateArgcArgv() void launcherRun() { - // Init launcher - OSScreenInit(); - - size_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV); - size_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC); - - void *tvBuffer = memalign(0x100, tvBufferSize); - void *drcBuffer = memalign(0x100, drcBufferSize); - - if (!tvBuffer || !drcBuffer) - { - OSScreenShutdown(); - if (tvBuffer) - free(tvBuffer); - if (drcBuffer) // shouldn't happen? - free(drcBuffer); - I_Error("Error starting launcher, couldn't create OSScreen Buffers"); - } - - OSScreenSetBufferEx(SCREEN_TV, tvBuffer); - OSScreenSetBufferEx(SCREEN_DRC, drcBuffer); - OSScreenEnableEx(SCREEN_TV, true); - OSScreenEnableEx(SCREEN_DRC, true); + // Init launcher using new OSScreen management system + WiiU_OSScreenInit(); // Init launcher states launcherMainInit(); launcherNoWadsInit(); - // I need this variable because with out it, WHBProcIsRunning becomes true - // again before exiting, causing a crash - bool wbhRunning = true; - - while ((launcherRunning > 0) && (wbhRunning = WHBProcIsRunning())) + // Launcher loop - no ProcUI to avoid crashes + while (launcherRunning > 0) { // Poll input WiiU_PollJoystick(); @@ -152,22 +133,23 @@ void launcherRun() launcherDraw(SCREEN_TV); launcherDraw(SCREEN_DRC); - DCFlushRange(tvBuffer, tvBufferSize); - DCFlushRange(drcBuffer, drcBufferSize); + DCFlushRange(g_tvBuffer, g_tvBufferSize); + DCFlushRange(g_drcBuffer, g_drcBufferSize); OSScreenFlipBuffersEx(SCREEN_TV); OSScreenFlipBuffersEx(SCREEN_DRC); + + // Small delay to prevent excessive CPU usage + OSSleepTicks(OSMillisecondsToTicks(16)); // ~60 FPS } - if (!wbhRunning) - launcherRunning = -1; // Quit + // Launcher exit is handled by the main loop + // No need for custom app running flag - // Cleanup launcher - if (tvBuffer) - free(tvBuffer); - if (drcBuffer) - free(drcBuffer); - OSScreenShutdown(); + // Present clean frame and mark OSScreen for shutdown + // Actual shutdown will be completed after SDL presents its first frame + WiiU_OSScreenPresentCleanFrame(); + WiiU_OSScreenMarkForShutdown(); if (launcherRunning >= 0) { @@ -177,12 +159,6 @@ void launcherRun() // Cleanup launcher states launcherMainCleanup(); launcherNoWadsCleanup(); - - if (launcherRunning < 0) - { - WHBProcShutdown(); - exit(0); - } } #endif // __WIIU__ diff --git a/src/wiiu/wiiu_oscreen.c b/src/wiiu/wiiu_oscreen.c new file mode 100644 index 0000000000..43dd129892 --- /dev/null +++ b/src/wiiu/wiiu_oscreen.c @@ -0,0 +1,126 @@ +// +// Copyright(C) 2025 strejf79 +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// Wii U OSScreen management implementation +// + +#ifdef __WIIU__ + +#include +#include +#include +#include + +#include "doomtype.h" +#include +#include +#include +#include + +#include "wiiu_oscreen.h" + +// Global variables for OSScreen buffer management +void* g_tvBuffer = NULL; +void* g_drcBuffer = NULL; +int g_tvBufferSize = 0; +int g_drcBufferSize = 0; +boolean g_osScreenShutdownPending = false; + +void WiiU_OSScreenInit(void) +{ + // Initialize OSScreen + OSScreenInit(); + + // Allocate buffers + g_tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV); + g_drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC); + + g_tvBuffer = memalign(0x100, g_tvBufferSize); + g_drcBuffer = memalign(0x100, g_drcBufferSize); + + if (!g_tvBuffer || !g_drcBuffer) + { + // Cleanup on failure + if (g_tvBuffer) + free(g_tvBuffer); + if (g_drcBuffer) + free(g_drcBuffer); + g_tvBuffer = NULL; + g_drcBuffer = NULL; + OSScreenShutdown(); + return; + } + + // Set up buffers + OSScreenSetBufferEx(SCREEN_TV, g_tvBuffer); + OSScreenSetBufferEx(SCREEN_DRC, g_drcBuffer); + OSScreenEnableEx(SCREEN_TV, true); + OSScreenEnableEx(SCREEN_DRC, true); + + // Reset shutdown flag + g_osScreenShutdownPending = false; +} + +void WiiU_OSScreenPresentCleanFrame(void) +{ + if (!g_tvBuffer || !g_drcBuffer) + return; + + // Present a clean black frame before SDL handover + OSScreenClearBufferEx(SCREEN_TV, 0x00000000); + OSScreenClearBufferEx(SCREEN_DRC, 0x00000000); + DCFlushRange(g_tvBuffer, g_tvBufferSize); + DCFlushRange(g_drcBuffer, g_drcBufferSize); + OSScreenFlipBuffersEx(SCREEN_TV); + OSScreenFlipBuffersEx(SCREEN_DRC); + + // Give the display a frame to pick up the cleared buffers + OSSleepTicks(OSMillisecondsToTicks(16)); +} + +void WiiU_OSScreenMarkForShutdown(void) +{ + // Mark for shutdown but don't actually shutdown yet + // This will be completed after SDL presents its first frame + g_osScreenShutdownPending = true; +} + +void WiiU_OSScreenCompleteShutdown(void) +{ + if (!g_osScreenShutdownPending) + return; + + // Now it's safe to shutdown OSScreen since SDL has taken over + OSScreenShutdown(); + + // Free buffers + if (g_tvBuffer) + { + free(g_tvBuffer); + g_tvBuffer = NULL; + } + if (g_drcBuffer) + { + free(g_drcBuffer); + g_drcBuffer = NULL; + } + + g_osScreenShutdownPending = false; +} + +boolean WiiU_OSScreenShutdownPending(void) +{ + return g_osScreenShutdownPending; +} + +#endif // __WIIU__ diff --git a/src/wiiu/wiiu_oscreen.h b/src/wiiu/wiiu_oscreen.h new file mode 100644 index 0000000000..e0e00b715e --- /dev/null +++ b/src/wiiu/wiiu_oscreen.h @@ -0,0 +1,52 @@ +// +// Copyright(C) 2025 strejf79 +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// Wii U OSScreen management for smooth launcher-to-game transition +// + +#ifndef WIIU_OSCREEN_H +#define WIIU_OSCREEN_H + +#ifdef __WIIU__ + +#include "doomtype.h" +#include +#include +#include +#include + +// Global variables for OSScreen buffer management +extern void* g_tvBuffer; +extern void* g_drcBuffer; +extern int g_tvBufferSize; +extern int g_drcBufferSize; +extern boolean g_osScreenShutdownPending; + +// Initialize OSScreen buffers (called from launcher) +void WiiU_OSScreenInit(void); + +// Present clean black frame and prepare for SDL handover +void WiiU_OSScreenPresentCleanFrame(void); + +// Mark OSScreen for shutdown (called when launcher exits) +void WiiU_OSScreenMarkForShutdown(void); + +// Complete OSScreen shutdown (called after SDL presents first frame) +void WiiU_OSScreenCompleteShutdown(void); + +// Check if OSScreen shutdown is pending +boolean WiiU_OSScreenShutdownPending(void); + +#endif // __WIIU__ + +#endif // WIIU_OSCREEN_H diff --git a/src/wiiu_exit.c b/src/wiiu_exit.c new file mode 100644 index 0000000000..3708e28b2e --- /dev/null +++ b/src/wiiu_exit.c @@ -0,0 +1,58 @@ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005-2014 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// Wii U platform exit handling. +// + + +#include "wiiu_exit.h" +#include +#include + +// Global flag for clean exit request from in-game quit menu +boolean g_request_app_exit = false; + +// Function to request clean exit from in-game quit menu +void platform_request_exit(void) +{ + g_request_app_exit = true; +} + +// Manual ProcUI cleanup to ensure proper shutdown +void cleanup_procui(void) +{ + if (ProcUIIsRunning()) { + if (!ProcUIInShutdown()) { + SYSLaunchMenu(); + boolean still_running = true; + while (still_running) { + ProcUIStatus status = ProcUIProcessMessages(true); + switch (status) { + case PROCUI_STATUS_EXITING: + still_running = false; + break; + case PROCUI_STATUS_IN_FOREGROUND: + break; + case PROCUI_STATUS_IN_BACKGROUND: + break; + case PROCUI_STATUS_RELEASE_FOREGROUND: + ProcUIDrawDoneRelease(); + break; + } + } + } + ProcUIShutdown(); + } +} diff --git a/src/wiiu_exit.h b/src/wiiu_exit.h new file mode 100644 index 0000000000..3968119e8f --- /dev/null +++ b/src/wiiu_exit.h @@ -0,0 +1,34 @@ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005-2014 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// Wii U platform exit handling. +// + + +#ifndef __WIIU_EXIT__ +#define __WIIU_EXIT__ + +#include "doomtype.h" + +// Global flag for clean exit request from in-game quit menu +extern boolean g_request_app_exit; + +// Function to request clean exit from in-game quit menu +void platform_request_exit(void); + +// Manual ProcUI cleanup function +void cleanup_procui(void); + +#endif diff --git a/wiiu-data/meta.xml b/wiiu-data/meta.xml new file mode 100644 index 0000000000..a0aeb13e2b --- /dev/null +++ b/wiiu-data/meta.xml @@ -0,0 +1,19 @@ + + + Crispy Doom U + thearst3rd + 0.3.3 (5.12.0) - Aroma Compatible + https://github.com/thearst3rd/crispy-doom-u/tree/wiiu-port + 20221009000000 + Play Doom on the Wii U + Crispy Doom U is a Wii U port of Crispy Doom, which is a limit-removing enhanced-resolution Doom source port based on Chocolate Doom. For more information, visit: + +https://www.chocolate-doom.org/wiki/index.php/Crispy_Doom +https://github.com/thearst3rd/crispy-doom-u/tree/wiiu-port + +Put your Doom WADs in sd:/wiiu/apps/crispy-doom-u/wads + +This version is fully compatible with Aroma and includes proper ProcUI state management for clean exits and background handling. + +Thanks for playing! + diff --git a/wiiu/meta.xml b/wiiu/meta.xml index 01ea3ec582..a0aeb13e2b 100644 --- a/wiiu/meta.xml +++ b/wiiu/meta.xml @@ -2,7 +2,7 @@ Crispy Doom U thearst3rd - 0.3.3 (5.12.0) + 0.3.3 (5.12.0) - Aroma Compatible https://github.com/thearst3rd/crispy-doom-u/tree/wiiu-port 20221009000000 Play Doom on the Wii U @@ -13,5 +13,7 @@ https://github.com/thearst3rd/crispy-doom-u/tree/wiiu-port Put your Doom WADs in sd:/wiiu/apps/crispy-doom-u/wads +This version is fully compatible with Aroma and includes proper ProcUI state management for clean exits and background handling. + Thanks for playing!