diff --git a/.gitignore b/.gitignore index 4e5c5f3b8e..d6b366d659 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,7 @@ sm64config.txt !/sound/**/*custom*/**/*.aiff !/assets/**/*custom*.bin !/assets/**/*custom*/**/*.bin + +# PS2dev files +ps2dev*.tar.gz +ps2dev diff --git a/Makefile b/Makefile index 4600aca898..8683ee97ff 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,8 @@ TARGET_PS2 ?= 1 # Compiler to use (ido or gcc) COMPILER ?= ido +GAME_CODE ?= SLUS_064.64 + # Automatic settings only for ports ifeq ($(TARGET_N64),0) @@ -862,7 +864,7 @@ endif -.PHONY: all clean distclean default diff test load libultra +.PHONY: all clean distclean default diff test load libultra iso # with no prerequisites, .SECONDARY causes no intermediate target to be removed .SECONDARY: @@ -872,3 +874,9 @@ MAKEFLAGS += --no-builtin-rules -include $(DEP_FILES) print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true + +iso: $(ELF) + @echo Creating iso from $(ELF) + @cp -r ps2/ntsc $(BUILD_DIR)/iso + @cp $< $(BUILD_DIR)/iso/$(GAME_CODE) + @mkisofs -o $(BUILD_DIR)/sm64.iso $(BUILD_DIR)/iso/ diff --git a/ps2/ntsc/SYSTEM.CNF b/ps2/ntsc/SYSTEM.CNF new file mode 100644 index 0000000000..e34b1ef3ed --- /dev/null +++ b/ps2/ntsc/SYSTEM.CNF @@ -0,0 +1,3 @@ +BOOT2 = cdrom0:\SLUS_064.64;1 +VER = 1.00 +VMODE = NTSC \ No newline at end of file diff --git a/src/game/area.c b/src/game/area.c index d458bf7129..0b15598858 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -22,6 +22,10 @@ #include "save_file.h" #include "level_table.h" +#ifdef TARGET_PS2 +#include "pc/ps2_vid_mode_select.h" +#endif + struct SpawnInfo gPlayerSpawnInfos[1]; struct GraphNode *D_8033A160[0x100]; struct Area gAreaData[8]; @@ -383,6 +387,10 @@ void render_game(void) { gSaveOptSelectIndex = gPauseScreenMode; } +#ifdef TARGET_PS2 + handle_ps2_vid_mode_select(); +#endif + if (D_8032CE78 != NULL) { make_viewport_clip_rect(D_8032CE78); } else diff --git a/src/game/game_init.c b/src/game/game_init.c index 3ce5f2d620..c9c5f6f01d 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -21,6 +21,11 @@ #include "thread6.h" #include +#ifdef TARGET_PS2 +#include "pc/ps2_vid_mode_select.h" +#endif + + // FIXME: I'm not sure all of these variables belong in this file, but I don't // know of a good way to split them struct Controller gControllers[3]; @@ -480,7 +485,11 @@ void read_controller_inputs(void) { // if any controllers are plugged in, update the // controller information. - if (gControllerBits) { + if (gControllerBits +#ifndef TARGET_N64 + && !gDisableInput +#endif + ) { osRecvMesg(&gSIEventMesgQueue, &D_80339BEC, OS_MESG_BLOCK); osContGetReadData(&gControllerPads[0]); #ifdef VERSION_SH @@ -602,6 +611,12 @@ void thread5_game_loop(UNUSED void *arg) { #endif save_file_load_all(); +#ifdef TARGET_PS2 + // Must be called after save file loaded to + // configure preferred video mode + ps2_vid_mode_select_init(); +#endif + set_vblank_handler(2, &gGameVblankHandler, &gGameVblankQueue, (OSMesg) 1); // point levelCommandAddr to the entry point into the level script data. diff --git a/src/game/ingame_menu.h b/src/game/ingame_menu.h index 1435e1648a..796d72b860 100644 --- a/src/game/ingame_menu.h +++ b/src/game/ingame_menu.h @@ -116,6 +116,7 @@ extern s8 gRedCoinsCollected; void create_dl_identity_matrix(void); void create_dl_translation_matrix(s8 pushOp, f32 x, f32 y, f32 z); +void create_dl_scale_matrix(s8 pushOp, f32 x, f32 y, f32 z); void create_dl_ortho_matrix(void); void print_generic_string(s16 x, s16 y, const u8 *str); void print_hud_lut_string(s8 hudLUT, s16 x, s16 y, const u8 *str); diff --git a/src/game/main.c b/src/game/main.c index 9e53e50b2a..70a1fd29ce 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -71,6 +71,10 @@ s8 D_8032C650 = 0; s8 gShowProfiler = FALSE; s8 gShowDebugText = FALSE; +#ifndef TARGET_N64 +s8 gDisableInput = FALSE; +#endif + // unused void handle_debug_key_sequences(void) { static u16 sProfilerKeySequence[] = { diff --git a/src/game/main.h b/src/game/main.h index d7efe89454..be5166f657 100644 --- a/src/game/main.h +++ b/src/game/main.h @@ -64,6 +64,9 @@ extern s8 gDebugLevelSelect; extern s8 D_8032C650; extern s8 gShowProfiler; extern s8 gShowDebugText; +#ifndef TARGET_N64 +extern s8 gDisableInput; +#endif void set_vblank_handler(s32 index, struct VblankHandler *handler, OSMesgQueue *queue, OSMesg *msg); void dispatch_audio_sptask(struct SPTask *spTask); diff --git a/src/game/save_file.c b/src/game/save_file.c index 8c15558620..2d603b8f15 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -571,6 +571,23 @@ u16 save_file_get_sound_mode(void) { return gSaveBuffer.menuData[0].soundMode; } +#ifdef TARGET_PS2 +void save_file_set_ps2_vid_mode(u16 mode) { + gSaveBuffer.menuData[0].ps2VidMode = mode; + gSaveBuffer.menuData[0].ps2VidModeSet = 1; + + gMainMenuDataModified = TRUE; + save_main_menu_data(); +} + +u16 save_file_get_ps2_vid_mode(void) { + if (gSaveBuffer.menuData[0].ps2VidModeSet == 1) { + return gSaveBuffer.menuData[0].ps2VidMode; + } + return (u16)-1; +} +#endif + void save_file_move_cap_to_default_location(void) { if (save_file_get_flags() & SAVE_FLAG_CAP_ON_GROUND) { switch (gSaveBuffer.files[gCurrSaveFileNum - 1][0].capLevel) { diff --git a/src/game/save_file.h b/src/game/save_file.h index 3ee5a19ac6..d626dede70 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -55,9 +55,23 @@ struct MainMenuSaveData #ifdef VERSION_EU u16 language; +#endif + +#ifdef TARGET_PS2 + // u16 for consistency, like why is language u16? + u16 ps2VidMode; + u16 ps2VidModeSet; +#ifdef VERSION_EU +#define SUBTRAHEND 12 +#else +#define SUBTRAHEND 10 +#endif +#else +#ifdef VERSION_EU #define SUBTRAHEND 8 #else #define SUBTRAHEND 6 +#endif #endif // Pad to match the EEPROM size of 0x200 (10 bytes on JP/US, 8 bytes on EU) @@ -143,6 +157,10 @@ void save_file_set_cap_pos(s16 x, s16 y, s16 z); s32 save_file_get_cap_pos(Vec3s capPos); void save_file_set_sound_mode(u16 mode); u16 save_file_get_sound_mode(void); +#ifdef TARGET_PS2 +void save_file_set_ps2_vid_mode(u16 mode); +u16 save_file_get_ps2_vid_mode(void); +#endif void save_file_move_cap_to_default_location(void); void disable_warp_checkpoint(void); diff --git a/src/pc/controller/controller_api.h b/src/pc/controller/controller_api.h index dd318a8ac9..994ed63440 100644 --- a/src/pc/controller/controller_api.h +++ b/src/pc/controller/controller_api.h @@ -6,6 +6,8 @@ struct ControllerAPI { void (*init)(void); void (*read)(OSContPad *pad); + // returns the raw button values for this controller + u32 (*read_btns)(void); }; #endif diff --git a/src/pc/controller/controller_ps2.c b/src/pc/controller/controller_ps2.c index 41bc0f14de..c616b3764f 100644 --- a/src/pc/controller/controller_ps2.c +++ b/src/pc/controller/controller_ps2.c @@ -7,6 +7,7 @@ #include #include +#include "controller_ps2.h" #include "controller_api.h" #define DEADZONE 24 @@ -142,7 +143,16 @@ static void controller_ps2_read(OSContPad *pad) { } } +u32 controller_ps2_read_btns(void) { + if (joy_id > -1 && padRead(joy_port, joy_slot, &joy_buttons)) { + const u32 btns = 0xffff ^ joy_buttons.btns; + return btns; + } + return 0; +} + struct ControllerAPI controller_ps2 = { controller_ps2_init, - controller_ps2_read + controller_ps2_read, + controller_ps2_read_btns, }; diff --git a/src/pc/controller/controller_ps2.h b/src/pc/controller/controller_ps2.h index 0420a52f20..c38b763ad2 100644 --- a/src/pc/controller/controller_ps2.h +++ b/src/pc/controller/controller_ps2.h @@ -1,6 +1,7 @@ #ifndef CONTROLLER_PS2_H #define CONTROLLER_PS2_H +#include #include "controller_api.h" extern struct ControllerAPI controller_ps2; diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index 54e85d0129..b83ccb77c9 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -322,6 +322,11 @@ static struct ColorCombiner *gfx_lookup_or_create_color_combiner(uint32_t cc_id) return prev_combiner = comb; } +void gfx_clear_texture_cache(void) { + if (gfx_rapi->flush_textures) gfx_rapi->flush_textures(); + gfx_texture_cache.pool_pos = 0; +} + static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, const uint8_t *orig_addr, uint32_t fmt, uint32_t siz) { size_t hash = (uintptr_t)orig_addr; hash = (hash >> 5) & 0x3ff; @@ -336,8 +341,7 @@ static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, co } if (gfx_texture_cache.pool_pos == sizeof(gfx_texture_cache.pool) / sizeof(struct TextureHashmapNode)) { // Pool is full. We just invalidate everything and start over. - if (gfx_rapi->flush_textures) gfx_rapi->flush_textures(); - gfx_texture_cache.pool_pos = 0; + gfx_clear_texture_cache(); node = &gfx_texture_cache.hashmap[hash]; //puts("Clearing texture cache"); } @@ -2025,3 +2029,4 @@ void gfx_end_frame(void) { gfx_wapi->swap_buffers_end(); } } + diff --git a/src/pc/gfx/gfx_pc.h b/src/pc/gfx/gfx_pc.h index 0ad4a302b4..564fac1e23 100644 --- a/src/pc/gfx/gfx_pc.h +++ b/src/pc/gfx/gfx_pc.h @@ -22,6 +22,7 @@ struct GfxRenderingAPI *gfx_get_current_rendering_api(void); void gfx_start_frame(void); void gfx_run(Gfx *commands); void gfx_end_frame(void); +void gfx_clear_texture_cache(void); #ifdef __cplusplus } diff --git a/src/pc/gfx/gfx_ps2_rapi.c b/src/pc/gfx/gfx_ps2_rapi.c index f938555716..7d4b9a4e83 100644 --- a/src/pc/gfx/gfx_ps2_rapi.c +++ b/src/pc/gfx/gfx_ps2_rapi.c @@ -316,6 +316,10 @@ static void gfx_ps2_set_viewport(int x, int y, int width, int height) { r_view.x = x; r_view.y = y; r_view.w = width; + // 1080i requires the view point is half height + if (gs_global->Mode == GS_MODE_DTV_1080I) { + height /= 2; + } r_view.h = height; r_view.hw = r_view.w * 0.5f; r_view.hh = r_view.h * 0.5f; @@ -324,6 +328,11 @@ static void gfx_ps2_set_viewport(int x, int y, int width, int height) { } static inline void draw_set_scissor(const int x0, const int y0, const int x1, const int y1) { + // scissor doesn't work with gskit hires + if (gs_global->Mode == GS_MODE_DTV_720P || gs_global->Mode == GS_MODE_DTV_1080I) { + return; + } + u64 *p_data = gsKit_heap_alloc(gs_global, 1, 16, GIF_AD); *p_data++ = GIF_TAG_AD(1); @@ -645,7 +654,6 @@ static void draw_clear(const u64 color) { u32 pos = 0; - strips++; while (strips--) { gsKit_prim_sprite(gs_global, pos, 0, pos + 64, gs_global->Height, 0, color); pos += 64; diff --git a/src/pc/gfx/gfx_ps2_wapi.c b/src/pc/gfx/gfx_ps2_wapi.c index 2b9441aac3..bd62a463b3 100644 --- a/src/pc/gfx/gfx_ps2_wapi.c +++ b/src/pc/gfx/gfx_ps2_wapi.c @@ -8,8 +8,11 @@ #include #include +#include #include +#include "macros.h" + #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" #include "gfx_ps2.h" @@ -24,20 +27,25 @@ struct VidMode { int width; int height; int vck; + int iPassCount; int x_off; int y_off; }; static const struct VidMode vid_modes[] = { + { "240p", GS_MODE_NTSC, GS_NONINTERLACED, GS_FRAME, 652, 224, 320, 224, 2, 1, 0, 0 }, +#if !defined(VERSION_EU) // NTSC - { "480i", GS_MODE_NTSC, GS_INTERLACED, GS_FIELD, 704, 480, 704, 452, 4, 0, 0 }, - { "480p", GS_MODE_DTV_480P, GS_NONINTERLACED, GS_FRAME, 704, 480, 704, 452, 2, 0, 0 }, + { "480i", GS_MODE_NTSC, GS_INTERLACED, GS_FIELD, 704, 480, 704, 452, 4, 1, 0, 0 }, + { "480p", GS_MODE_DTV_480P, GS_NONINTERLACED, GS_FRAME, 704, 480, 704, 452, 2, 1, 0, 0 }, +#else // PAL - { "576i", GS_MODE_PAL, GS_INTERLACED, GS_FIELD, 704, 576, 704, 536, 4, 0, 0 }, - { "576p", GS_MODE_DTV_576P, GS_NONINTERLACED, GS_FRAME, 704, 576, 704, 536, 2, 0, 0 }, + { "576i", GS_MODE_PAL, GS_INTERLACED, GS_FIELD, 704, 576, 704, 536, 4, 1, 0, 0 }, + { "576p", GS_MODE_DTV_576P, GS_NONINTERLACED, GS_FRAME, 704, 576, 704, 536, 2, 1, 0, 0 }, +#endif // HDTV - { "720p", GS_MODE_DTV_720P, GS_NONINTERLACED, GS_FRAME, 1280, 720, 1280, 698, 1, 0, 0 }, - {"1080i", GS_MODE_DTV_1080I, GS_INTERLACED, GS_FIELD, 1920, 1080, 1920, 1080, 1, 0, 0 }, + { "720p", GS_MODE_DTV_720P, GS_NONINTERLACED, GS_FRAME, 1280, 720, 1280, 720, 1, 2, 0, 0 }, + {"1080i", GS_MODE_DTV_1080I, GS_INTERLACED, GS_FRAME, 1920, 1080, 1920, 1080, 1, 2, 0, 0 }, }; GSGLOBAL *gs_global; @@ -45,8 +53,10 @@ GSGLOBAL *gs_global; static int vsync_sema_1st_id; static int vsync_sema_2nd_id; static int vsync_sema_id = -1; +static int vsync_id = -1; static const struct VidMode *vid_mode; +static bool use_hires = false; /* Copy of gsKit_sync_flip, but without the 'flip' */ static void gsKit_sync(GSGLOBAL *gsGlobal) @@ -99,34 +109,73 @@ static void prepare_sema() { } static void gfx_ps2_init(const char *game_name, bool start_in_fullscreen) { - gs_global = gsKit_init_global(); + if (vid_mode == NULL) { + vid_mode = &vid_modes[1]; // Standard def 480i + } else { + if (use_hires) { + gsKit_hires_deinit_global(gs_global); + } else { + gsKit_deinit_global(gs_global); + if (vsync_id != -1) { + gsKit_remove_vsync_handler(vsync_id); + } + vsync_sema_id = -1; + } + } + use_hires = (vid_mode->mode == GS_MODE_DTV_720P || vid_mode->mode == GS_MODE_DTV_1080I); + + if (use_hires) { + gs_global = gsKit_hires_init_global(); + } else { + gs_global = gsKit_init_global(); + } dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF); dmaKit_chan_init(DMA_CHANNEL_GIF); -#if defined(VERSION_EU) - vid_mode = &vid_modes[2]; // PAL -#else - vid_mode = &vid_modes[0]; // NTCS -#endif - gs_global->Mode = vid_mode->mode; gs_global->Width = vid_mode->width; gs_global->Height = vid_mode->height; + if (gs_global->Mode == GS_MODE_DTV_1080I) { + gs_global->Height /= 2; + } + gs_global->Interlace = vid_mode->interlace; gs_global->Field = vid_mode->field; gs_global->ZBuffering = GS_SETTING_ON; gs_global->DoubleBuffering = GS_SETTING_ON; gs_global->PrimAAEnable = GS_SETTING_OFF; - gs_global->PSM = GS_PSM_CT24; + // this could be enabled for hires, but I don't like it + gs_global->Dithering = use_hires ? GS_SETTING_ON : GS_SETTING_OFF; + // hires runs out of VRAM if using more than 16bit color + gs_global->PSM = use_hires ? GS_PSM_CT16 : GS_PSM_CT24; gs_global->PSMZ = GS_PSMZ_16; // 16-bit unsigned zbuffer - gsKit_init_screen(gs_global); + if (use_hires) { + gsKit_hires_init_screen(gs_global, vid_mode->iPassCount); + } else { + gsKit_init_screen(gs_global); + } + // hires sets the texture pointer to the wrong location. Ensure it's correct. + gs_global->TexturePointer = gs_global->CurrentPointer; gsKit_TexManager_init(gs_global); } +static bool gfx_ps2_set_vid_mode(uint8_t vid_mode_idx) { + if (vid_mode_idx >= ARRAY_COUNT(vid_modes)) { + return false; + } + + if (vid_mode != &vid_modes[vid_mode_idx]) { + vid_mode = &vid_modes[vid_mode_idx]; + gfx_ps2_init(NULL, false); + return true; + } + return false; +} + static void gfx_ps2_set_fullscreen_changed_callback(void (*on_fullscreen_changed)(bool is_now_fullscreen)) { } @@ -146,6 +195,11 @@ static void gfx_ps2_main_loop(void (*run_one_game_iter)(void)) { static void gfx_ps2_get_dimensions(uint32_t *width, uint32_t *height) { *width = gs_global->Width; *height = gs_global->Height; + // the game doesn't need to know that we are + // rendering at half height for 1080i + if (gs_global->Mode == GS_MODE_DTV_1080I) { + *height *= 2; + } } static void gfx_ps2_handle_events(void) { @@ -157,19 +211,25 @@ static bool gfx_ps2_start_frame(void) { } static void gfx_ps2_swap_buffers_begin(void) { + if (use_hires) { + return; + } if (vsync_sema_id != -1) return; prepare_sema(); vsync_sema_id = 0; - gsKit_add_vsync_handler(vsync_handler); + vsync_id = gsKit_add_vsync_handler(vsync_handler); } static void gfx_ps2_swap_buffers_end(void) { /* How SM64 expect to run at 30 PFS we need to wait for 2 vsync */ - gsKit_sync(gs_global); - - gsKit_flip(gs_global); - gsKit_queue_exec(gs_global); + if (use_hires) { + gsKit_hires_flip_ext(gs_global, GSFLIP_RATE_LIMIT_2); + } else { + gsKit_sync(gs_global); + gsKit_flip(gs_global); + gsKit_queue_exec(gs_global); + } gsKit_TexManager_nextFrame(gs_global); } @@ -188,7 +248,8 @@ struct GfxWindowManagerAPI gfx_ps2_wapi = { gfx_ps2_start_frame, gfx_ps2_swap_buffers_begin, gfx_ps2_swap_buffers_end, - gfx_ps2_get_time + gfx_ps2_get_time, + gfx_ps2_set_vid_mode, }; #endif // TARGET_PS2 diff --git a/src/pc/gfx/gfx_window_manager_api.h b/src/pc/gfx/gfx_window_manager_api.h index 6ba4a2d820..df5d7b4e7f 100644 --- a/src/pc/gfx/gfx_window_manager_api.h +++ b/src/pc/gfx/gfx_window_manager_api.h @@ -16,6 +16,7 @@ struct GfxWindowManagerAPI { void (*swap_buffers_begin)(void); void (*swap_buffers_end)(void); double (*get_time)(void); // For debug + bool (*set_vid_mode)(uint8_t vid_mode_idx); // only used for window managers which require it }; #endif diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index a83a4e8b43..8bf0b735db 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -54,6 +54,7 @@ s8 D_8032C648; s8 gDebugLevelSelect; s8 gShowProfiler; s8 gShowDebugText; +s8 gDisableInput = FALSE; static struct AudioAPI *audio_api; static struct GfxWindowManagerAPI *wm_api; diff --git a/src/pc/ps2_vid_mode_select.c b/src/pc/ps2_vid_mode_select.c new file mode 100644 index 0000000000..10e4ec6bc1 --- /dev/null +++ b/src/pc/ps2_vid_mode_select.c @@ -0,0 +1,233 @@ +#include + +#include "sm64.h" + +#include "gfx_dimensions.h" + +#include "game/main.h" +#include "game/game_init.h" +#include "game/ingame_menu.h" +#include "game/segment2.h" +#include "game/save_file.h" + +#include "audio/external.h" + +#include "pc/controller/controller_ps2.h" +#include "pc/gfx/gfx_ps2.h" +#include "pc/gfx/gfx_pc.h" +#include "pc/ps2_vid_mode_select.h" + + +// stolen from `ingame_menu.c` +static u8 gMenuHoldKeyIndex = 0; +static u8 gMenuHoldKeyTimer = 0; +static s8 gDialogLineNum = 0; + +u8 gShowVidModeSelect = FALSE; + +// Used for signalling a special n64 controller combination when +// some buttons are held down for a number of frames +static int specialInputHoldTimer = 0; +static u32 coolOffTimer = FALSE; + +// NTSC (480i) or PAL (576i) +#define DEFAULT_VID_MODE 1 + +#define TEXT_SELECT_VIDEO_MODE \ + 0x1C,0x28,0x2F,0x28,0x26,0x37, DIALOG_CHAR_SPACE, \ + 0x39,0x2C,0x27,0x28,0x32, DIALOG_CHAR_SPACE, \ + 0x30,0x32,0x27,0x28, DIALOG_CHAR_SPACE, \ + 0x37,0x32, DIALOG_CHAR_TERMINATOR + +#define TEXT_TO_SAVE_YOUR_PREFERENCE \ + 0x36,0x24,0x39,0x28, DIALOG_CHAR_SPACE, \ + 0x3C,0x32,0x38,0x35, DIALOG_CHAR_SPACE, \ + 0x33,0x35,0x28,0x29,0x28,0x35,0x28,0x31,0x26,0x28, DIALOG_CHAR_SPACE, \ + DIALOG_CHAR_TERMINATOR + +extern void adjust_analog_stick(struct Controller *controller); + +static void shade_screen(void) { + create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_LEFT_EDGE(0), SCREEN_HEIGHT, 0); + + create_dl_scale_matrix(MENU_MTX_NOPUSH, + GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_HEIGHT / 130.0f, 3.0f, 1.0f); + + gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 110); + gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box); + gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); +} + +static void read_stick_input(struct Controller* controller) { + OSContPad pads[4]; + osContGetReadData(&pads[0]); + controller->rawStickX = pads[0].stick_x; + controller->rawStickY = pads[0].stick_y; + adjust_analog_stick(controller); +} + +static void handle_vertical_menu_scrolling(s8 scrollDirection, s8 *currentIndex, s8 minIndex, s8 maxIndex) { + u8 index = 0; + struct Controller controller; + read_stick_input(&controller); + + if (controller.rawStickY > 60) { + index++; + } + + if (controller.rawStickY < -60) { + index += 2; + } + + if (((index ^ gMenuHoldKeyIndex) & index) == 2) { + if (currentIndex[0] == maxIndex) { + //! Probably originally a >=, but later replaced with an == and an else statement. + currentIndex[0] = maxIndex; + } else { + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + currentIndex[0]++; + } + } + + if (((index ^ gMenuHoldKeyIndex) & index) == 1) { + if (currentIndex[0] == minIndex) { + // Same applies to here as above + } else { + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + currentIndex[0]--; + } + } + + if (gMenuHoldKeyTimer == 10) { + gMenuHoldKeyTimer = 8; + gMenuHoldKeyIndex = 0; + } else { + gMenuHoldKeyTimer++; + gMenuHoldKeyIndex = index; + } + + if ((index & 3) == 0) { + gMenuHoldKeyTimer = 0; + } +} + + +void render_ps2_vid_mode_options(s16 x, s16 y, s8 *index, s16 yIndex) { + u8 text240p[] = { 0x02,0x04,0x00,0x33,0xFF }; + u8 text480i[] = { 0x04,0x08,0x00,0x2C,0xFF }; + u8 text480p[] = { 0x04,0x08,0x00,0x33,0xFF }; + u8 text720p[] = { 0x07,0x02,0x00,0x33,0xFF }; + u8 text1080i[] = { 0x01,0x00,0x08,0x00,0x2C,0xFF }; + u8* options[] = { text240p, text480i, text480p, text720p, text1080i }; + u8 infoText1[] = { TEXT_SELECT_VIDEO_MODE }; + u8 infoText2[] = { TEXT_TO_SAVE_YOUR_PREFERENCE }; + + handle_vertical_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 5); + + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); + + print_generic_string(x, y + 30, infoText1); + print_generic_string(x, y + 15, infoText2); + + u8 i; + for (i = 0; i < ARRAY_COUNT(options); i++) { + print_generic_string(x + 10, y - 2 - (i * 15), options[i]); + } + + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); + + create_dl_translation_matrix(MENU_MTX_PUSH, x - 4, (y - ((index[0] - 1) * yIndex)) - 2, 0); + + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); + gSPDisplayList(gDisplayListHead++, dl_draw_triangle); + gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); +} + +static void ps2_vid_mode_select_open(void) { + gShowVidModeSelect = TRUE; + gDisableInput = TRUE; + + // reset vid mode + u8 vidMode = DEFAULT_VID_MODE; + if (gfx_ps2_wapi.set_vid_mode(vidMode)) { + save_file_set_ps2_vid_mode(vidMode); + } + + gDialogTextAlpha = 0; + gDialogLineNum = vidMode + 1; + + // prevent a mode being selected by the X + // button after the menu opens + coolOffTimer = 60; +} + +static void ps2_vid_mode_select_close(void) { + gShowVidModeSelect = FALSE; + gDisableInput = FALSE; +} + +static void ps2_vid_mode_select_detect_open(u32 btns) { + if ((btns & (PAD_CROSS | PAD_TRIANGLE)) == (PAD_CROSS | PAD_TRIANGLE) && !gShowVidModeSelect) { + specialInputHoldTimer++; + } else { + specialInputHoldTimer = 0; + } + + if (specialInputHoldTimer > 15) { + ps2_vid_mode_select_open(); + specialInputHoldTimer = 0; + } + + if (coolOffTimer > 0) { + coolOffTimer--; + } +} + +void ps2_vid_mode_select_init(void) { + u8 vidMode = DEFAULT_VID_MODE; // 480i; + u16 savedVidMode = save_file_get_ps2_vid_mode(); + + // Vid mode has never been saved + if (savedVidMode != (u16)-1) { + vidMode = savedVidMode; + } + gfx_ps2_wapi.set_vid_mode(vidMode); +} + + +void handle_ps2_vid_mode_select(void) { + // Read raw PS2 controls so that we can read triangle + u32 btns = controller_ps2.read_btns(); + // Look forthe special button combo + ps2_vid_mode_select_detect_open(btns); + + if (gShowVidModeSelect) { + shade_screen(); + render_ps2_vid_mode_options(99, 93, &gDialogLineNum, 15); + // Preview the vid mode + u8 vidMode = gDialogLineNum - 1; + // This will do nothing unless the vid mode has changed + // so it's safe to call every frame + if (gfx_ps2_wapi.set_vid_mode(vidMode)) { + // Reset the texture cache since we've changed + // video mode so VRAM might get a little weird + // This atm does not work (causes more VRAM issues) + // gfx_clear_texture_cache(); + }; + + if ((btns & PAD_CROSS) && coolOffTimer == 0) + { + save_file_set_ps2_vid_mode(vidMode); + play_sound(SOUND_MENU_PAUSE_2, gDefaultSoundArgs); + ps2_vid_mode_select_close(); + } + + + if (gDialogTextAlpha < 250) { + gDialogTextAlpha += 25; + } + gDialogColorFadeTimer = (s16) gDialogColorFadeTimer + 0x1000; + } +} + diff --git a/src/pc/ps2_vid_mode_select.h b/src/pc/ps2_vid_mode_select.h new file mode 100644 index 0000000000..81831b0eb8 --- /dev/null +++ b/src/pc/ps2_vid_mode_select.h @@ -0,0 +1,11 @@ +#ifndef PS2_VID_MODE_SELECT_H +#define PS2_VID_MODE_SELECT_H + +#include "sm64.h" + +void handle_ps2_vid_mode_select(void); +void ps2_vid_mode_select_init(void); + +extern u8 gShowVidModeSelect; + +#endif