diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..ae44ba7b --- /dev/null +++ b/Makefile @@ -0,0 +1,70 @@ +TITLE := "Grand Theft Auto III" +TITLE_ID := GTA3RT000 +TARGET := GTA3 + +SOURCES := src src/animation src/audio src/audio/oal src/audio/eax src/buildings src/control src/collision src/core src/entities src/math src/modelinfo src/objects src/peds src/renderer src/rw src/save src/skel src/skel/glfw src/text src/vehicles src/weapons src/extras src/fakerw +INCLUDES := src src/animation src/audio src/audio/oal src/audio/eax src/buildings src/control src/collision src/core src/entities src/math src/modelinfo src/objects src/peds src/renderer src/rw src/save src/skel src/skel/glfw src/text src/vehicles src/weapons src/extras src/fakerw librw + +CFILES := $(foreach dir,$(SOURCES), $(wildcard $(dir)/*.c)) +CPPFILES := $(foreach dir,$(SOURCES), $(wildcard $(dir)/*.cpp)) +BINFILES := $(foreach dir,$(DATA), $(wildcard $(dir)/*.bin)) +OBJS := $(addsuffix .o,$(BINFILES)) $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) + +INCLUDE := $(foreach dir,$(INCLUDES),-I$(dir)) + +PREFIX = arm-vita-eabi +CC = $(PREFIX)-gcc +CXX = $(PREFIX)-g++ +ARCH := -mtune=cortex-a9 -march=armv7-a -mfpu=neon +CFLAGS := -g -Wl,-q,--no-enum-size-warning -fno-short-enums -fno-optimize-sibling-calls -O2 -ftree-vectorize -mfloat-abi=hard -fno-builtin-memcpy $(ARCH) $(DEFINES) +CFLAGS += $(INCLUDE) -DPSP2 -DNDEBUG -DMASTER -DLIBRW -DRW_GL3 -DAUDIO_OAL -DLIBRW_GLAD +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) +LIBS := -lrw -lopenal -lSDL2 -lvita2d -lvitaGL -lSceAppMgr_stub -lSceDisplay_stub -lSceCommonDialog_stub -lSceLibKernel_stub \ + -lSceSysmodule_stub -lvitashark -lSceShaccCg_stub -lSceShaccCgExt -lvitaGL -lmathneon -lSceGxm_stub -lScePower_stub \ + -lSceCtrl_stub -lSceHid_stub -lSceAudio_stub -lSceAudioIn_stub -lSceTouch_stub -lm -lpthread -lmpg123 -lSceMotion_stub \ + -lSceSysmodule_stub -lSceAvPlayer_stub -lSceKernelDmacMgr_stub -ltaihen_stub + +all: $(TARGET).vpk + +%.vpk: eboot.bin + vita-mksfoex -s APP_VER=01.50 -s TITLE_ID=$(TITLE_ID) -d ATTRIBUTE2=12 $(TITLE) param.sfo + vita-pack-vpk -s param.sfo -b eboot.bin \ + --add sce_sys/icon0.png=sce_sys/icon0.png \ + --add sce_sys/livearea/contents/bg.png=sce_sys/livearea/contents/bg.png \ + --add sce_sys/livearea/contents/startup.png=sce_sys/livearea/contents/startup.png \ + --add sce_sys/livearea/contents/template.xml=sce_sys/livearea/contents/template.xml \ + --add sce_sys/manual/001.png=sce_sys/manual/001.png \ + --add sce_sys/manual/002.png=sce_sys/manual/002.png \ + --add sce_sys/manual/003.png=sce_sys/manual/003.png \ + --add sce_sys/manual/004.png=sce_sys/manual/004.png \ + --add sce_sys/manual/005.png=sce_sys/manual/005.png \ + --add sce_sys/manual/006.png=sce_sys/manual/006.png \ + --add sce_sys/manual/007.png=sce_sys/manual/007.png \ + --add sce_sys/manual/008.png=sce_sys/manual/008.png \ + --add sce_sys/manual/009.png=sce_sys/manual/009.png \ + --add sce_sys/manual/010.png=sce_sys/manual/010.png \ + --add sce_sys/manual/011.png=sce_sys/manual/011.png \ + --add sce_sys/manual/012.png=sce_sys/manual/012.png \ + --add sce_sys/manual/013.png=sce_sys/manual/013.png \ + --add sce_sys/manual/014.png=sce_sys/manual/014.png \ + --add sce_sys/manual/015.png=sce_sys/manual/015.png \ + --add sce_sys/manual/016.png=sce_sys/manual/016.png \ + --add sce_sys/manual/017.png=sce_sys/manual/017.png \ + --add sce_sys/manual/018.png=sce_sys/manual/018.png \ + --add sce_sys/manual/019.png=sce_sys/manual/019.png \ + --add sce_sys/manual/020.png=sce_sys/manual/020.png \ + $(TARGET).vpk + +eboot.bin: $(TARGET).velf + vita-make-fself -c -s $< $@ + +%.velf: %.elf + vita-elf-create $< $@ + +$(TARGET).elf: $(OBJS) + $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@ + +clean: + @rm -rf $(TARGET).vpk $(TARGET).velf $(TARGET).elf $(OBJS) eboot.bin param.sfo diff --git a/sce_sys/icon0.png b/sce_sys/icon0.png new file mode 100644 index 00000000..ad8155c5 Binary files /dev/null and b/sce_sys/icon0.png differ diff --git a/sce_sys/livearea/contents/bg.png b/sce_sys/livearea/contents/bg.png new file mode 100644 index 00000000..d4cf30af Binary files /dev/null and b/sce_sys/livearea/contents/bg.png differ diff --git a/sce_sys/livearea/contents/startup.png b/sce_sys/livearea/contents/startup.png new file mode 100644 index 00000000..ceb00b83 Binary files /dev/null and b/sce_sys/livearea/contents/startup.png differ diff --git a/sce_sys/livearea/contents/template.xml b/sce_sys/livearea/contents/template.xml new file mode 100644 index 00000000..60e0b8ea --- /dev/null +++ b/sce_sys/livearea/contents/template.xml @@ -0,0 +1,11 @@ + + + + + bg.png + + + + startup.png + + \ No newline at end of file diff --git a/sce_sys/manual/001.png b/sce_sys/manual/001.png new file mode 100644 index 00000000..640eea43 Binary files /dev/null and b/sce_sys/manual/001.png differ diff --git a/sce_sys/manual/002.png b/sce_sys/manual/002.png new file mode 100644 index 00000000..f4cc6594 Binary files /dev/null and b/sce_sys/manual/002.png differ diff --git a/sce_sys/manual/003.png b/sce_sys/manual/003.png new file mode 100644 index 00000000..07972933 Binary files /dev/null and b/sce_sys/manual/003.png differ diff --git a/sce_sys/manual/004.png b/sce_sys/manual/004.png new file mode 100644 index 00000000..5af09ed6 Binary files /dev/null and b/sce_sys/manual/004.png differ diff --git a/sce_sys/manual/005.png b/sce_sys/manual/005.png new file mode 100644 index 00000000..76c7f261 Binary files /dev/null and b/sce_sys/manual/005.png differ diff --git a/sce_sys/manual/006.png b/sce_sys/manual/006.png new file mode 100644 index 00000000..19827d81 Binary files /dev/null and b/sce_sys/manual/006.png differ diff --git a/sce_sys/manual/007.png b/sce_sys/manual/007.png new file mode 100644 index 00000000..39261a77 Binary files /dev/null and b/sce_sys/manual/007.png differ diff --git a/sce_sys/manual/008.png b/sce_sys/manual/008.png new file mode 100644 index 00000000..ee260688 Binary files /dev/null and b/sce_sys/manual/008.png differ diff --git a/sce_sys/manual/009.png b/sce_sys/manual/009.png new file mode 100644 index 00000000..c33e8b61 Binary files /dev/null and b/sce_sys/manual/009.png differ diff --git a/sce_sys/manual/010.png b/sce_sys/manual/010.png new file mode 100644 index 00000000..8ccf25ab Binary files /dev/null and b/sce_sys/manual/010.png differ diff --git a/sce_sys/manual/011.png b/sce_sys/manual/011.png new file mode 100644 index 00000000..38b78355 Binary files /dev/null and b/sce_sys/manual/011.png differ diff --git a/sce_sys/manual/012.png b/sce_sys/manual/012.png new file mode 100644 index 00000000..b587949e Binary files /dev/null and b/sce_sys/manual/012.png differ diff --git a/sce_sys/manual/013.png b/sce_sys/manual/013.png new file mode 100644 index 00000000..074dc65f Binary files /dev/null and b/sce_sys/manual/013.png differ diff --git a/sce_sys/manual/014.png b/sce_sys/manual/014.png new file mode 100644 index 00000000..44b60c50 Binary files /dev/null and b/sce_sys/manual/014.png differ diff --git a/sce_sys/manual/015.png b/sce_sys/manual/015.png new file mode 100644 index 00000000..0472f488 Binary files /dev/null and b/sce_sys/manual/015.png differ diff --git a/sce_sys/manual/016.png b/sce_sys/manual/016.png new file mode 100644 index 00000000..7cf9296a Binary files /dev/null and b/sce_sys/manual/016.png differ diff --git a/sce_sys/manual/017.png b/sce_sys/manual/017.png new file mode 100644 index 00000000..3492d008 Binary files /dev/null and b/sce_sys/manual/017.png differ diff --git a/sce_sys/manual/018.png b/sce_sys/manual/018.png new file mode 100644 index 00000000..2738ae0e Binary files /dev/null and b/sce_sys/manual/018.png differ diff --git a/sce_sys/manual/019.png b/sce_sys/manual/019.png new file mode 100644 index 00000000..59952852 Binary files /dev/null and b/sce_sys/manual/019.png differ diff --git a/sce_sys/manual/020.png b/sce_sys/manual/020.png new file mode 100644 index 00000000..205b33fb Binary files /dev/null and b/sce_sys/manual/020.png differ diff --git a/src/audio/oal/oal_utils.cpp b/src/audio/oal/oal_utils.cpp index e4cb0b77..fa7b8371 100644 --- a/src/audio/oal/oal_utils.cpp +++ b/src/audio/oal/oal_utils.cpp @@ -51,6 +51,7 @@ using namespace re3_openal; void EFXInit() { +#ifndef PSP2 /* Define a macro to help load the function pointers. */ #define LOAD_PROC(T, x) ((x) = (T)alGetProcAddress(#x)) LOAD_PROC(LPALGENEFFECTS, alGenEffects); @@ -89,6 +90,7 @@ void EFXInit() LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf); LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); #undef LOAD_PROC +#endif } void SetEffectsLevel(ALuint uiFilter, float level) diff --git a/src/audio/sampman_oal.cpp b/src/audio/sampman_oal.cpp index d59b86e6..a145ee99 100644 --- a/src/audio/sampman_oal.cpp +++ b/src/audio/sampman_oal.cpp @@ -18,7 +18,7 @@ #include #include #include -#else +#elif !defined(PSP2) #define _getcwd getcwd #endif @@ -255,7 +255,8 @@ set_new_provider(int index) usingEAX3 = 0; _usingEFX = false; - if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3") +#if !defined(PSP2) + if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3") && alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) { @@ -282,7 +283,8 @@ set_new_provider(int index) alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); EAX_SetAll(&EAX30_ORIGINAL_PRESETS[EAX_ENVIRONMENT_CAVE]); } - +#endif + //SampleManager.SetSpeakerConfig(speaker_type); if ( IsFXSupported() ) @@ -420,7 +422,7 @@ _ResolveLink(char const *path, char *out) #else struct stat sb; - if (lstat(path, &sb) == -1) { + if (_lstat(path, &sb) == -1) { perror("lstat: "); return FALSE; } @@ -457,7 +459,8 @@ _FindMP3s(void) WIN32_FIND_DATA fd; char filepath[MAX_PATH + sizeof(fd.cFileName)]; - if (getcwd(_mp3DirectoryPath, MAX_PATH) == NULL) { + // _getcwd on PSP2 returns the data directory; elsewhere it is the real one. + if (_getcwd(_mp3DirectoryPath, MAX_PATH) == NULL) { perror("getcwd: "); return; } @@ -509,6 +512,8 @@ _FindMP3s(void) { if (filepathlen > 0) { + // No symlinks on this filesystem, and readlink is a stub here. +#ifndef PSP2 if (_ResolveLink(filepath, filepath)) { OutputDebugString("Resolving Link"); @@ -516,6 +521,7 @@ _FindMP3s(void) bShortcut = TRUE; } else +#endif { bShortcut = FALSE; if (filepathlen > MAX_PATH) { @@ -568,6 +574,7 @@ _FindMP3s(void) { if ( filepathlen > 0 ) { +#ifndef PSP2 if ( _ResolveLink(filepath, filepath) ) { OutputDebugString("Resolving Link"); @@ -575,6 +582,7 @@ _FindMP3s(void) bShortcut = TRUE; } else +#endif bShortcut = FALSE; if (aStream[0] && aStream[0]->Open(filepath)) @@ -839,12 +847,14 @@ cSampleManager::Initialise(void) alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); +#ifndef PSP2 if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) { _effectsSupported = providers[index].bSupportsFx; alGenAuxiliaryEffectSlots(1, &ALEffectSlot); alGenEffects(1, &ALEffect); } +#endif alGenSources(MAX_STREAMS*2, ALStreamSources[0]); for ( int32 i = 0; i < MAX_STREAMS; i++ ) diff --git a/src/core/Cam.cpp b/src/core/Cam.cpp index d4be8585..18478ede 100644 --- a/src/core/Cam.cpp +++ b/src/core/Cam.cpp @@ -30,7 +30,11 @@ bool PrintDebugCode = false; int16 DebugCamMode; #ifdef FREE_CAM +#ifdef GTA_HANDHELD +bool CCamera::bFreeCam = true; // handhelds default the free camera on +#else bool CCamera::bFreeCam = false; +#endif int nPreviousMode = -1; #endif diff --git a/src/core/CdStreamPosix.cpp b/src/core/CdStreamPosix.cpp index bc9129eb..69f996b1 100644 --- a/src/core/CdStreamPosix.cpp +++ b/src/core/CdStreamPosix.cpp @@ -7,7 +7,9 @@ #include #include #include +#ifndef PSP2 #include +#endif #include #include #include @@ -26,6 +28,12 @@ #define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) #define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) +#ifdef PSP2 +// Named semaphores do not exist here, and newlib does not declare SEM_FAILED. +#define USE_UNNAMED_SEM +#define SEM_FAILED ((sem_t *) 0) +#endif + #ifdef FLUSHABLE_STREAMING bool flushStream[MAX_CDCHANNELS]; #endif @@ -198,6 +206,7 @@ CdStreamInitThread(void) void CdStreamInit(int32 numChannels) { +#ifndef PSP2 struct statvfs fsInfo; if((statvfs("models/gta3.img", &fsInfo)) < 0) @@ -206,6 +215,7 @@ CdStreamInit(int32 numChannels) ASSERT(0); return; } +#endif #ifdef __linux__ _gdwCdStreamFlags = O_RDONLY | O_NOATIME; #else @@ -219,7 +229,11 @@ CdStreamInit(int32 numChannels) debug("Using no buffered loading for streaming\n"); } */ +#ifndef PSP2 void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, (RwUInt32)fsInfo.f_bsize); +#else + void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE); +#endif ASSERT( pBuffer != nil ); gNumImages = 0; @@ -245,13 +259,14 @@ GetGTA3ImgSize(void) struct stat statbuf; char path[PATH_MAX]; - realpath(gImgNames[0], path); + // _realpath is the Vita shim, plain realpath everywhere else - see crossplatform.h + _realpath(gImgNames[0], path); if (stat(path, &statbuf) == -1) { // Try case-insensitivity char* real = casepath(gImgNames[0], false); if (real) { - realpath(real, path); + _realpath(real, path); free(real); if (stat(path, &statbuf) != -1) goto ok; diff --git a/src/core/FileMgr.cpp b/src/core/FileMgr.cpp index 32aa4041..2012fd99 100644 --- a/src/core/FileMgr.cpp +++ b/src/core/FileMgr.cpp @@ -32,14 +32,16 @@ static myFILE myfiles[NUMFILES]; #include #include #include +#if !defined(PSP2) #define _getcwd getcwd +#endif // Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) void mychdir(char const *path) { char* r = casepath(path, false); if (r) { - chdir(r); + _chdir(r); free(r); } else { errno = ENOENT; diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp index 1a211429..c16054f8 100644 --- a/src/core/Frontend.cpp +++ b/src/core/Frontend.cpp @@ -35,6 +35,10 @@ #include "FileLoader.h" #include "frontendoption.h" +#ifdef PSP2 +extern bool useRearpad; // touch input: front panel or rear, see crossplatform.cpp +#endif + // Game has colors inlined in code. // For easier modification we collect them here: const CRGBA LABEL_COLOR(235, 170, 50, 255); @@ -145,11 +149,7 @@ int8 CMenuManager::m_PrefsIslandLoading = ISLAND_LOADING_LOW; #endif #ifdef GAMEPAD_MENU -#ifdef __SWITCH__ -int8 CMenuManager::m_PrefsControllerType = CONTROLLER_NINTENDO_SWITCH; -#else -int8 CMenuManager::m_PrefsControllerType = CONTROLLER_XBOXONE; -#endif +int8 CMenuManager::m_PrefsControllerType = DEFAULT_CONTROLLER_TYPE; #endif int32 CMenuManager::OS_Language = LANG_ENGLISH; @@ -160,7 +160,11 @@ int8 CMenuManager::m_PrefsVsyncDisp = 1; int8 CMenuManager::m_PrefsFrameLimiter = 1; int8 CMenuManager::m_PrefsShowSubtitles = 1; int8 CMenuManager::m_PrefsSpeakers; +#ifdef GTA_HANDHELD +int32 CMenuManager::m_ControlMethod = CONTROL_CLASSIC; // handhelds default to classic controls +#else int32 CMenuManager::m_ControlMethod; +#endif int8 CMenuManager::m_PrefsDMA = 1; int32 CMenuManager::m_PrefsLanguage; uint8 CMenuManager::m_PrefsStereoMono; // unused except restore settings @@ -1656,6 +1660,13 @@ CMenuManager::Draw() #endif } +#ifdef PSP2 + if (!strncmp(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName, "PSP2", 4)) { + leftText = TheText.Get("PSP2"); + rightText = TheText.Get(useRearpad ? "FEM_ON" : "FEM_OFF"); + } +#endif + float nextItemY = headerHeight + nextYToUse; float bitAboveNextItemY = nextItemY - 2.0f; int nextYToCheck = bitAboveNextItemY; @@ -3685,7 +3696,7 @@ const char* controllerTypesPaths[] = { nil, "MODELS/FRONTEND_DS3.TXD", "MODELS/FRONTEND_DS4.TXD", -#ifndef RT +#ifndef RT_CONTROLLERS "MODELS/FRONTEND_X360.TXD", #endif "MODELS/FRONTEND_XONE.TXD", @@ -3695,12 +3706,14 @@ const char* controllerTypesPaths[] = { void CMenuManager::LoadController(int8 type) { - // A controller type saved by the other build variant can be out of range here: - // the RT and vanilla controller enums differ in size (vanilla keeps CONTROLLER_XBOX360), - // so e.g. Nintendo Switch is index 5 in vanilla but 4 under RT. An out-of-range value - // would index controllerTypesPaths/controllerTypes out of bounds -> crash. Clamp it. - if (type < 0 || type >= (int8)ARRAY_SIZE(controllerTypesPaths)) { - type = CONTROLLER_NINTENDO_SWITCH; + // A controller type saved by another build variant can be out of range here, in two + // ways: the RT and vanilla enums differ in size (vanilla keeps CONTROLLER_XBOX360), + // so e.g. Nintendo Switch is index 5 in vanilla but 4 under RT; and the handhelds + // offer fewer types than the .ini may hold. Out of range means controllerTypes read + // out of bounds, a pad the menu can no longer select away from, and with it the wrong + // button labels and the wrong menu confirm/cancel layout. Clamp it. + if (type < 0 || type >= (int8)NUM_MENU_CONTROLLER_TYPES) { + type = DEFAULT_CONTROLLER_TYPE; m_PrefsControllerType = type; } @@ -3708,12 +3721,12 @@ CMenuManager::LoadController(int8 type) { case CONTROLLER_DUALSHOCK3: case CONTROLLER_DUALSHOCK4: -#ifndef RT +#ifndef RT_CONTROLLERS case CONTROLLER_DUALSHOCK2: // real DualShock2 uses PlayStation buttons #endif CFont::LoadButtons("MODELS/PS3BTNS.TXD"); break; -#ifdef RT +#ifdef RT_CONTROLLERS case CONTROLLER_DUALSHOCK2: // RT: the DS2 slot is a Switch Pro Controller #endif case CONTROLLER_NINTENDO_SWITCH: @@ -5861,7 +5874,334 @@ CMenuManager::WaitForUserCD() void CMenuManager::PrintController(void) { -#ifdef RT +#ifdef PSP2 + // PSV: the Vita port draws labels against its own pad artwork with its own + // per-type nudges and justification, so it gets re3-vita's PrintController + // verbatim. The RT_CONTROLLERS (Switch) and vanilla bodies below are untouched. + const float scale = 0.9f; + const float CONTROLLER_SIZE_X = 384.0f; + const float CONTROLLER_SIZE_Y = 384.0f; + const float CONTROLLER_POS_X = (DEFAULT_SCREEN_WIDTH - CONTROLLER_SIZE_X) / 2.0f; + const float CONTROLLER_POS_Y = 140.0f; + + float centerX = CONTROLLER_POS_X + CONTROLLER_SIZE_X / 2; + float centerY = CONTROLLER_POS_Y + CONTROLLER_SIZE_Y / 2; + +#define X(f) ((f)*scale + centerX) +#define Y(f) ((f)*scale + centerY) + + m_aFrontEndSprites[FE_CONTROLLERSH].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X((CONTROLLER_SIZE_X + 4.8f) * scale), MENU_Y((CONTROLLER_SIZE_Y + 4.8f) * scale), CRGBA(0, 0, 0, 255)); + m_aFrontEndSprites[FE_CONTROLLER].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255)); + if (m_DisplayControllerOnFoot) { + if (CTimer::GetTimeInMillisecondsPauseMode() & 0x400) + m_aFrontEndSprites[FE_ARROWS1].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255)); + else + m_aFrontEndSprites[FE_ARROWS3].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255)); + } else { + if (CTimer::GetTimeInMillisecondsPauseMode() & 0x400) + m_aFrontEndSprites[FE_ARROWS2].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255)); + else + m_aFrontEndSprites[FE_ARROWS4].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, 255)); + } + + CFont::SetFontStyle(FONT_LOCALE(FONT_BANK)); + CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * scale), MENU_Y(SMALLESTTEXT_Y_SCALE * scale)); // X + + // CFont::SetColor(CRGBA(128, 128, 128, FadeIn(255))); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetDropShadowPosition(1); + CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); + CFont::SetWrapx(SCREEN_WIDTH); + + float TEXT_L2_X = 106.0f + CONTROLLER_POS_X - centerX, TEXT_L2_Y = -7.0f + CONTROLLER_POS_Y - centerY; + float TEXT_L1_X = 2.0f + CONTROLLER_POS_X - centerX, TEXT_L1_Y = 25.0f + CONTROLLER_POS_Y - centerY, TEXT_L1_Y_VEH = 3.0f + TEXT_L1_Y; + float TEXT_DPAD_X = 2.0f + CONTROLLER_POS_X - centerX, TEXT_DPAD_Y = 65.0f + CONTROLLER_POS_Y - centerY; + float TEXT_LSTICK_X = 2.0f + CONTROLLER_POS_X - centerX, TEXT_LSTICK_Y = 101.0f + CONTROLLER_POS_Y - centerY; + float TEXT_SELECT_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_SELECT_Y = 141.0f + CONTROLLER_POS_Y - centerY; + float TEXT_START_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_START_Y = 124.0f + CONTROLLER_POS_Y - centerY; + float TEXT_R2_X = 274.0F + CONTROLLER_POS_X - centerX, TEXT_R2_Y = -7.0f + CONTROLLER_POS_Y - centerY; + float TEXT_R1_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_R1_Y = 25.0f + CONTROLLER_POS_Y - centerY; + + float TEXT_SQUARE_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_SQUARE_Y = 39.0f + CONTROLLER_POS_Y - centerY; + float TEXT_TRIANGLE_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_TRIANGLE_Y = 52.0f + CONTROLLER_POS_Y - centerY; + float TEXT_CIRCLE_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_CIRCLE_Y = 65.0f + CONTROLLER_POS_Y - centerY; + float TEXT_CROSS_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_CROSS_Y = 78.0f + CONTROLLER_POS_Y - centerY; + float TEXT_RSTICK_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_RSTICK_Y = 102.0f + CONTROLLER_POS_Y - centerY; + float TEXT_R3_X = 382.0f + CONTROLLER_POS_X - centerX, TEXT_R3_Y = 158.0f + CONTROLLER_POS_Y - centerY; + float TEXT_L3_X = 2.0f + CONTROLLER_POS_X - centerX, TEXT_L3_Y = 158.0f + CONTROLLER_POS_Y - centerY; + float TEXT_L2R2_X = 192.0f + CONTROLLER_POS_X - centerX, TEXT_L2R2_Y = 6.0f + CONTROLLER_POS_Y - centerY; + + switch (m_PrefsControllerType) + { + case CONTROLLER_DUALSHOCK4: + TEXT_L1_Y -= 2.0f; + TEXT_R1_Y -= 2.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_TRIANGLE_Y += 0.0f; + TEXT_CIRCLE_Y += 4.0f; + TEXT_CROSS_Y += 7.0f; + TEXT_SQUARE_Y -= 3.0f; + TEXT_RSTICK_Y += 3.0f; + TEXT_R3_Y -= 30.0f; + TEXT_DPAD_Y -= 3.0f; + TEXT_LSTICK_Y += 3.0f; + TEXT_L3_Y -= 30.0f; + TEXT_SELECT_X -= 240.0f; + TEXT_SELECT_Y += 13.0f; + TEXT_START_X -= 198.0f; + TEXT_START_Y += 12.0f; + break; + case CONTROLLER_DUALSHOCK3: + TEXT_L1_Y += 4.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_TRIANGLE_Y += 5.0f; + TEXT_CIRCLE_Y += 9.0f; + TEXT_CROSS_Y += 15.0f; + TEXT_RSTICK_Y += 17.0f; + TEXT_R3_Y -= 17.0f; + TEXT_DPAD_Y += 11.0f; + TEXT_LSTICK_Y += 17.0f; + TEXT_L3_Y -= 17.0f; + TEXT_SELECT_X -= 238.0f; + TEXT_SELECT_Y -= 112.0f; + TEXT_START_X -= 214.0f; + TEXT_START_Y += 28.0f; + break; + case CONTROLLER_XBOXONE: + TEXT_L2_X -= 2.0f; + TEXT_R2_X += 2.0f; + TEXT_L1_Y += 15.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_R1_Y += 15.0f; + TEXT_TRIANGLE_Y += 4.0f; + TEXT_CIRCLE_Y += 4.0f; + TEXT_CROSS_Y += 4.0f; + TEXT_RSTICK_Y += 1.0f; + TEXT_R3_Y += 1.0f; + TEXT_DPAD_Y += 29.0f; + TEXT_LSTICK_Y -= 22.0f; + TEXT_L3_X -= 36.0f; + TEXT_L2R2_Y += 5.0f; + TEXT_SELECT_X += 4.0f; + break; + case CONTROLLER_XBOX360: + TEXT_L2_X += 8.0f; + TEXT_R2_X -= 8.0f; + TEXT_L1_Y += 15.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_R1_Y += 15.0f; + TEXT_TRIANGLE_Y += 4.0f; + TEXT_CIRCLE_Y += 4.0f; + TEXT_CROSS_Y += 4.0f; + TEXT_RSTICK_Y += 4.0f; + TEXT_R3_Y += 4.0f; + TEXT_DPAD_Y += 30.0f; + TEXT_LSTICK_Y -= 21.0f; + TEXT_L3_X -= 36.0f; + TEXT_L2R2_Y += 5.0f; + TEXT_SELECT_X += 3.0f; + break; + case CONTROLLER_NINTENDO_SWITCH: + TEXT_L1_Y += 5.0f; + TEXT_L1_Y_VEH = TEXT_L1_Y; + TEXT_R1_Y += 5.0f; + TEXT_TRIANGLE_Y += 3.0f; + TEXT_CIRCLE_Y += 3.0f; + TEXT_CROSS_Y += 3.0f; + TEXT_LSTICK_Y -= 23.0f; + TEXT_DPAD_Y += 25.0; + TEXT_RSTICK_Y += 1.0f; + TEXT_R3_Y += 1.0f; + break; + }; + + if (m_DisplayControllerOnFoot) { + switch (CPad::GetPad(0)->Mode) { + case 0: + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); + CFont::SetRightJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_LOF")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_MOV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_ATT")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); + break; + case 1: + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); + CFont::SetRightJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_LOF")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_NA")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_ATT")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); + break; + case 2: + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); + CFont::SetRightJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_ENV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_MOV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_LOF")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_RUN")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ATT")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); + break; + case 3: + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); + CFont::SetRightJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_TAR")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_NA")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_ATT")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_LOF")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); + break; + default: + return; + } + } else { + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2R2_X)), MENU_Y(Y(TEXT_L2R2_Y)), TheText.Get("FEC_LB")); + switch (CPad::GetPad(0)->Mode) { + case 0: + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); + CFont::SetRightJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_RSC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_VES")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_HO3")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HAB")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); + break; + case 1: + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); + CFont::SetRightJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_HOR")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_NA")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_RSC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HAB")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); + break; + case 2: + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); + CFont::SetRightJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_EXV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_VES")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_RS3")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HOR")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_HAB")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); + break; + case 3: + CFont::SetCentreOn(); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); + CFont::SetRightJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_HAB")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_TUC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_HO3")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_CAW")); + CFont::SetJustifyOn(); // X + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_SMT")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_RSC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_NA")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_ACC")); + CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_BRA")); + break; + default: + return; + } + } + + CFont::SetDropShadowPosition(0); // X + +#undef X +#undef Y +#elif defined RT_CONTROLLERS const float scale = 0.9f; const float CONTROLLER_SIZE_X = 384.0f; const float CONTROLLER_SIZE_Y = 384.0f; diff --git a/src/core/Frontend.h b/src/core/Frontend.h index b69ad97a..ea48dd28 100644 --- a/src/core/Frontend.h +++ b/src/core/Frontend.h @@ -758,13 +758,25 @@ public: CONTROLLER_DUALSHOCK2 = 0, CONTROLLER_DUALSHOCK3, CONTROLLER_DUALSHOCK4, -#ifndef RT +#ifndef RT_CONTROLLERS CONTROLLER_XBOX360, #endif CONTROLLER_XBOXONE, CONTROLLER_NINTENDO_SWITCH, + NUM_CONTROLLER_TYPES, }; +#ifdef PSP2 + #define NUM_MENU_CONTROLLER_TYPES (CMenuManager::CONTROLLER_DUALSHOCK4 + 1) // the Vita itself, plus the DS3/DS4 a PSTV can pair with + #define DEFAULT_CONTROLLER_TYPE CMenuManager::CONTROLLER_DUALSHOCK2 +#elif defined __SWITCH__ + #define NUM_MENU_CONTROLLER_TYPES CMenuManager::NUM_CONTROLLER_TYPES + #define DEFAULT_CONTROLLER_TYPE CMenuManager::CONTROLLER_NINTENDO_SWITCH +#else + #define NUM_MENU_CONTROLLER_TYPES CMenuManager::NUM_CONTROLLER_TYPES + #define DEFAULT_CONTROLLER_TYPE CMenuManager::CONTROLLER_XBOXONE +#endif + static int8 m_PrefsControllerType; #endif diff --git a/src/core/Game.cpp b/src/core/Game.cpp index b3dd1eda..40b3a5b1 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -244,6 +244,14 @@ CGame::InitialiseRenderWare(void) #endif #ifdef LIBRW +// librw-vita predates the envMap* knobs and only exposes modulateEnvMap. +#ifdef PSP2 +#ifdef PS2_MATFX + rw::MatFX::modulateEnvMap = true; +#else + rw::MatFX::modulateEnvMap = false; +#endif +#else #ifdef PS2_MATFX rw::MatFX::envMapApplyLight = true; rw::MatFX::envMapUseMatColor = true; @@ -255,6 +263,7 @@ CGame::InitialiseRenderWare(void) #endif rw::RGBA envcol = { 128, 128, 128, 255 }; rw::MatFX::envMapColor = envcol; +#endif #else #ifdef PS2_MATFX ReplaceMatFxCallback(); diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp index 292a5ace..6fb0f02a 100644 --- a/src/core/MenuScreensCustom.cpp +++ b/src/core/MenuScreensCustom.cpp @@ -32,6 +32,10 @@ // Menu screens array is at the bottom of the file. +#ifdef PSP2 +extern bool useRearpad; // touch input: front panel or rear, see crossplatform.cpp +#endif + #ifdef PC_MENU #ifdef CUSTOM_FRONTEND_OPTIONS @@ -42,7 +46,9 @@ #define VIDEOMODE_SELECTOR #endif -#ifdef MULTISAMPLING +// Not on PSP2: vglInitExtended fixes the level at 4x, so the entry could never +// change anything, and stepping through it used to hang the game. +#if defined(MULTISAMPLING) && !defined(PSP2) #define MULTISAMPLING_SELECTOR MENUACTION_CFO_DYNAMIC, "FED_AAS", { new CCFODynamic((int8*)&FrontEndMenuManager.m_nPrefsMSAALevel, "Graphics", "MultiSampling", MultiSamplingDraw, MultiSamplingButtonPress) }, #else #define MULTISAMPLING_SELECTOR @@ -96,7 +102,7 @@ #endif #ifdef GAMEPAD_MENU - #define SELECT_CONTROLLER_TYPE MENUACTION_CFO_SELECT, "FEC_TYP", { new CCFOSelect((int8*)&CMenuManager::m_PrefsControllerType, "Controller", "Type", controllerTypes, ARRAY_SIZE(controllerTypes), false, ControllerTypeAfterChange) }, + #define SELECT_CONTROLLER_TYPE MENUACTION_CFO_SELECT, "FEC_TYP", { new CCFOSelect((int8*)&CMenuManager::m_PrefsControllerType, "Controller", "Type", controllerTypes, NUM_MENU_CONTROLLER_TYPES, false, ControllerTypeAfterChange) }, #else #define SELECT_CONTROLLER_TYPE #endif @@ -242,7 +248,9 @@ void MultiSamplingButtonPress(int8 action) { int i = 0; int maxAA = RwD3D8EngineGetMaxMultiSamplingLevels(); - while (maxAA != 1) { + // > 1, not != 1: the PSP2 stub reports 0 levels, and 0 >> 1 stays 0, + // so the original condition never terminates and hangs the game. + while (maxAA > 1) { i++; maxAA >>= 1; } @@ -388,8 +396,10 @@ void DetectJoystickGoBack() { #endif #ifdef GAMEPAD_MENU +// Stays parallel to the controller enum in Frontend.h - it is indexed by type, not +// iterated. The menu offers only the first NUM_MENU_CONTROLLER_TYPES of them. const char* controllerTypes[] = { "FEC_DS2", "FEC_DS3", "FEC_DS4", -#ifndef RT +#ifndef RT_CONTROLLERS "FEC_360", #endif "FEC_ONE", "FEC_NSW" }; @@ -422,10 +432,17 @@ CMenuScreenCustom aScreens[MENUPAGES] = { }, // MENUPAGE_CONTROLLER_SETTINGS = 4 -#if defined(GAMEPAD_MENU) && !defined(GTA_HANDHELD) +#if defined(GAMEPAD_MENU) && !defined(GTA_HANDHELD) && !defined(PSP2) { "FET_AGS", MENUPAGE_CONTROLLER_PC, MENUPAGE_CONTROLLER_PC, nil, nil, +#elif defined(PSP2) + // No gamepad-selection page here: there is exactly one pad, and it gets its own + // entry below for choosing which touch panel to use. + { "FET_CON", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil, #else { "FET_AGS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil, +#endif +#ifdef PSP2 + MENUACTION_CFO_SELECT, "PSP2", { new CCFOSelect((int8*)&useRearpad, "Controller", "Rearpad", off_on, 2, false) }, #endif MENUACTION_CTRLCONFIG, "FEC_CCF", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS }, MENUACTION_CTRLDISPLAY, "FEC_CDP", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS }, diff --git a/src/core/Pad.cpp b/src/core/Pad.cpp index 005b18b2..f8762b81 100644 --- a/src/core/Pad.cpp +++ b/src/core/Pad.cpp @@ -769,7 +769,7 @@ void CPad::StartShake_Train(float fX, float fY) static bool MenuUsesNintendoLayout() { return CMenuManager::m_PrefsControllerType == CMenuManager::CONTROLLER_NINTENDO_SWITCH -#ifdef RT +#ifdef RT_CONTROLLERS || CMenuManager::m_PrefsControllerType == CMenuManager::CONTROLLER_DUALSHOCK2 // RT: DS2 slot is a Switch Pro Controller #endif ; @@ -1178,6 +1178,18 @@ void CPad::Update(int16 pad) { OldState = NewState; +#ifdef PSP2 + if ( ShakeDur ) + { + ShakeDur = Max((int32_t)ShakeDur - (int32_t)CTimer::GetTimeStepInMilliseconds(), 0); + + SceCtrlActuator handle; + handle.small = 0; + handle.large = ShakeDur == 0 ? 0 : (unsigned char)ShakeFreq; + sceCtrlSetActuator(pad + 1, &handle); + } +#endif + #ifdef GTA_PS2 bObsoleteControllerMessage = false; diff --git a/src/core/common.h b/src/core/common.h index 0d0528b1..cbc82c58 100644 --- a/src/core/common.h +++ b/src/core/common.h @@ -1,5 +1,11 @@ #pragma once +#ifdef PSP2 +extern "C"{ +#include +}; +#endif + #define _CRT_SECURE_NO_WARNINGS #define _USE_MATH_DEFINES #pragma warning(disable: 4244) // int to float diff --git a/src/core/config.h b/src/core/config.h index ddd2643b..56c6df35 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -206,10 +206,20 @@ enum Config { // Every other platform builds vanilla. Platform necessities (audio/loading tweaks) // are gated on __SWITCH__ separately, so a vanilla NX build still keeps those. // Kept above VANILLA_DEFINES so the tunables below exist in every configuration. -#if defined(__SWITCH__) && !defined(NO_RT) +// The Vita ships the same 10thAE resources, and its menu layout is drawn against +// them, so it is an RT build too. The controller half of RT stays Switch-only - +// see the RT && __SWITCH__ gates around the pad slots. +#if (defined(__SWITCH__) || defined(PSP2)) && !defined(NO_RT) #define RT #endif +// The controller half of RT - dropping the X360 slot and repurposing DS2 as a +// Switch Pro Controller - only makes sense on the Switch. The Vita has its own +// three pads (DS2 is the console itself) and keeps the upstream slots. +#if defined(RT) && defined(__SWITCH__) +#define RT_CONTROLLERS +#endif + // RT tunables, centralized so shared code stays readable. RT off = upstream values. #ifdef RT #define RT_RADAR_SCALE 1.3f // enlarge the minimap by 30% @@ -318,22 +328,29 @@ enum Config { #define USE_TXD_CDIMAGE // generate and load textures from txd.img #define PS2_ALPHA_TEST // emulate ps2 alpha test #define IMPROVED_VIDEOMODE // save and load videomode parameters instead of a magic number -#ifndef __SWITCH__ -#define DISABLE_LOADING_SCREEN // disable the loading screen which vastly improves the loading time (kept on Switch to show it) +#if !defined(__SWITCH__) && !defined(PSP2) +#define DISABLE_LOADING_SCREEN // disable the loading screen which vastly improves the loading time (both handhelds keep it, to show it) #endif #ifdef DISABLE_LOADING_SCREEN // enable the PC splash #undef RANDOMSPLASH #endif #define DISABLE_VSYNC_ON_TEXTURE_CONVERSION // make texture conversion work faster by disabling vsync +#ifndef PSP2 #define ANISOTROPIC_FILTERING // set all textures to max anisotropic filtering +#endif //#define USE_TEXTURE_POOL #ifdef LIBRW #define EXTENDED_COLOURFILTER // more options for colour filter (replaces mblur) +// Not on PSP2: librw-vita is an old fork with no setupVertexInput, no three-argument +// Shader::create and no UNIFORM_VEC4, so the Neo pipelines and the droplets cannot +// build against it. They would also need Cg versions of every shader. +#ifndef PSP2 #define EXTENDED_PIPELINES // custom render pipelines (includes Neo) #define SCREEN_DROPLETS // neo water droplets #define NEW_RENDERER // leeds-like world rendering, needs librw #endif +#endif #define FIX_SPRITES // fix sprites aspect ratio(moon, coronas, particle etc) @@ -461,7 +478,11 @@ enum Config { #define PAUSE_RADIO_IN_FRONTEND // pause radio when game is paused #define ATTACH_RELEASING_SOUNDS_TO_ENTITIES // sounds would follow ped and vehicles coordinates if not being queued otherwise #define USE_TIME_SCALE_FOR_AUDIO // slow down/speed up sounds according to the speed of the game +// Not on PSP2: std::thread cannot start here, and with -fno-exceptions the +// system_error it throws goes straight to terminate(). +#ifndef PSP2 #define MULTITHREADED_AUDIO // for streams. requires C++11 or later +#endif #ifdef AUDIO_OPUS #define AUDIO_OAL_USE_OPUS // enable support of opus files diff --git a/src/core/main.cpp b/src/core/main.cpp index 2a0a77ca..3da64a33 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -378,6 +378,11 @@ RwGrabScreen(RwCamera *camera, RwChar *filename) #define TILE_WIDTH 576 #define TILE_HEIGHT 432 +#ifdef PSP2 +extern GLuint fxfb; +bool using_fbo = false; +#endif + void DoRWStuffEndOfFrame(void) { @@ -385,6 +390,19 @@ DoRWStuffEndOfFrame(void) CDebug::DebugDisplayTextBuffer(); FlushObrsPrintfs(); RwCameraEndUpdate(Scene.camera); + +#if defined(PSP2) && defined(EXTENDED_COLOURFILTER) + // Arm the next frame's capture: bind our FBO once CPostFX has asked for it, so + // the scene renders into fxraster and the colour filter can sample it. + if(CPostFX::NeedBackBuffer()){ + if (using_fbo) { + glBindFramebuffer(GL_FRAMEBUFFER, fxfb); + using_fbo = false; + } else + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } +#endif + RsCameraShowRaster(Scene.camera); #ifndef MASTER char s[48]; @@ -1496,7 +1514,12 @@ Render2dStuff(void) firstPersonWeapon = true; // Draw black border for sniper and rocket launcher +#ifdef FIX_BUGS + // The rocket launcher has no scope, so the border just blacks out the screen. + if(weaponType == WEAPONTYPE_SNIPERRIFLE && firstPersonWeapon){ +#else if((weaponType == WEAPONTYPE_SNIPERRIFLE || weaponType == WEAPONTYPE_ROCKETLAUNCHER) && firstPersonWeapon){ +#endif CRGBA black(0, 0, 0, 255); // top and bottom strips diff --git a/src/core/re3.cpp b/src/core/re3.cpp index 02ed39ca..e7e394fb 100644 --- a/src/core/re3.cpp +++ b/src/core/re3.cpp @@ -184,7 +184,14 @@ CustomFrontendOptionsPopulate(void) #define MINI_CASE_SENSITIVE #include "ini.h" +#ifdef PSP2 +// mINI opens the file with std::ifstream/ofstream, which bypasses the fcaseopen/ +// casepath shims that resolve relative paths against the data dir, so a bare +// "re3.ini" never opens here and settings would not persist. +mINI::INIFile ini(PSP2_DATA_PATH "/re3.ini"); +#else mINI::INIFile ini("re3.ini"); +#endif mINI::INIStructure cfg; bool ReadIniIfExists(const char *cat, const char *key, uint32 *out) diff --git a/src/extras/postfx.cpp b/src/extras/postfx.cpp index cb02822a..0634026a 100644 --- a/src/extras/postfx.cpp +++ b/src/extras/postfx.cpp @@ -13,6 +13,11 @@ #include "MBlur.h" #include "postfx.h" +#ifdef PSP2 +GLuint fxraster = 0xDEADBEEF, fxfb; +extern bool using_fbo; +#endif + RwRaster *CPostFX::pFrontBuffer; RwRaster *CPostFX::pBackBuffer; bool CPostFX::bJustInitialised; @@ -54,6 +59,10 @@ CPostFX::Open(RwCamera *cam) uint32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); uint32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); uint32 depth = RwRasterGetDepth(RwCameraGetRaster(cam)); +#ifdef PSP2 + width = RwRasterGetWidth(RwCameraGetRaster(cam)); + height = RwRasterGetHeight(RwCameraGetRaster(cam)); +#endif pFrontBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE); pBackBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE); bJustInitialised = true; @@ -151,22 +160,39 @@ CPostFX::Open(RwCamera *cam) contrast_PS = rw::d3d::createPixelShader(contrastPS_cso); #endif #ifdef RW_OPENGL +#ifdef PSP2 + #include "shaders/im2d_v_cg.inc" +#endif using namespace rw::gl3; { +#ifdef PSP2 +#include "shaders/colourfilterIII_f_cg.inc" + const char *vs[] = { im2d_v_cg_src, nil }; + const char *fs[] = { colourfilterIII_f_cg_src, nil }; + colourFilterIII = Shader::create(vs, fs, true); +#else #include "shaders/obj/im2d_vert.inc" #include "shaders/obj/colourfilterIII_frag.inc" const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, colourfilterIII_frag_src, nil }; colourFilterIII = Shader::create(vs, fs); +#endif assert(colourFilterIII); } { +#ifdef PSP2 +#include "shaders/contrast_f_cg.inc" + const char *vs[] = { im2d_v_cg_src, nil }; + const char *fs[] = { contrast_f_cg_src, nil }; + contrast = Shader::create(vs, fs, true); +#else #include "shaders/obj/im2d_vert.inc" #include "shaders/obj/contrast_frag.inc" const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, contrast_frag_src, nil }; contrast = Shader::create(vs, fs); +#endif assert(contrast); } @@ -305,6 +331,14 @@ CPostFX::RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a) glUniform4fv(colourFilterIII->uniformLocations[u_blurcolor], 1, blurcolors); #endif } +#ifdef PSP2 + // The scene lives in our own FBO texture here, not in pBackBuffer. + glBindTexture(GL_TEXTURE_2D, fxraster); + RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255); + RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255); + RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255); + RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255); +#endif RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); #ifdef RW_D3D9 rw::d3d::im2dOverridePS = nil; @@ -432,8 +466,11 @@ CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blu assert(pFrontBuffer); assert(pBackBuffer); + // PSP2 captures the scene through its own FBO a frame ahead instead. +#ifndef PSP2 if(NeedBackBuffer()) GetBackBuffer(cam); +#endif DefinedState(); @@ -445,24 +482,40 @@ CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blu if(type == MOTION_BLUR_SNIPER){ if(!bJustInitialised) RenderOverlaySniper(cam, red, green, blue, blur); - }else switch(EffectSwitch){ - case POSTFX_OFF: - // no actual rendering here - break; - case POSTFX_SIMPLE: - RenderOverlaySimple(cam, red, green, blue, blur); - break; - case POSTFX_NORMAL: - if(MotionBlurOn){ - if(!bJustInitialised) - RenderOverlayBlur(cam, red, green, blue, blur); - }else{ - RenderOverlayShader(cam, red, green, blue, blur); + }else{ +#if defined(PSP2) && defined(EXTENDED_COLOURFILTER) + if (NeedBackBuffer()) { + if(fxraster == 0xDEADBEEF){ + glGenTextures(1, &fxraster); + glBindTexture(GL_TEXTURE_2D, fxraster); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 960, 544, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glGenFramebuffers(1, &fxfb); + glBindFramebuffer(GL_FRAMEBUFFER, fxfb); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fxraster, 0); + } + using_fbo = true; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } +#endif + switch(EffectSwitch){ + case POSTFX_OFF: + // no actual rendering here + break; + case POSTFX_SIMPLE: + RenderOverlaySimple(cam, red, green, blue, blur); + break; + case POSTFX_NORMAL: + if(MotionBlurOn){ + if(!bJustInitialised) + RenderOverlayBlur(cam, red, green, blue, blur); + }else{ + RenderOverlayShader(cam, red, green, blue, blur); + } + break; + case POSTFX_MOBILE: + RenderOverlayShader(cam, red, green, blue, blur); + break; } - break; - case POSTFX_MOBILE: - RenderOverlayShader(cam, red, green, blue, blur); - break; } // TODO? maybe we want this even without motion blur on sometimes? diff --git a/src/extras/shaders/colourfilterIII_f.cg b/src/extras/shaders/colourfilterIII_f.cg new file mode 100644 index 00000000..a95f9d12 --- /dev/null +++ b/src/extras/shaders/colourfilterIII_f.cg @@ -0,0 +1,38 @@ +#define u_fogStart (u_fogData.x) +#define u_fogEnd (u_fogData.y) +#define u_fogRange (u_fogData.z) +#define u_fogDisable (u_fogData.w) + +void DoAlphaTest(float a, half2 u_alphaRef) +{ + if(a < u_alphaRef.x || a >= u_alphaRef.y) + discard; +} + +float4 main( + half4 v_color : COLOR0, + half2 v_tex0 : TEXCOORD0, + fixed v_fog : FOG, + uniform half4 u_fogColor, + uniform half2 u_alphaRef, + uniform half4 u_blurcolor, + uniform sampler2D tex0 +) { + half a = u_blurcolor.a; + half4 dst = v_color*tex2D(tex0, half2(v_tex0.x, 1.0-v_tex0.y)); + dst.rgb = lerp(u_fogColor.rgb, dst.rgb, v_fog); + DoAlphaTest(dst.a, u_alphaRef); + + half4 prev = dst; + for (int i = 0; i < 5; i++){ + half4 tmp = dst*(1.0-a) + prev*u_blurcolor*a; + prev = clamp(tmp, 0.0, 1.0); + } + half4 color; + color.rgb = prev.rgb; + color.a = 1.0f; + + return color; +} + + diff --git a/src/extras/shaders/colourfilterIII_f_cg.inc b/src/extras/shaders/colourfilterIII_f_cg.inc new file mode 100644 index 00000000..5c65589e --- /dev/null +++ b/src/extras/shaders/colourfilterIII_f_cg.inc @@ -0,0 +1,40 @@ +const char *colourfilterIII_f_cg_src = +"#define u_fogStart (u_fogData.x)\n" +"#define u_fogEnd (u_fogData.y)\n" +"#define u_fogRange (u_fogData.z)\n" +"#define u_fogDisable (u_fogData.w)\n" + +"void DoAlphaTest(float a, half2 u_alphaRef)\n" +"{\n" +" if(a < u_alphaRef.x || a >= u_alphaRef.y)\n" +" discard;\n" +"}\n" + +"float4 main(\n" +" half4 v_color : COLOR0,\n" +" half2 v_tex0 : TEXCOORD0,\n" +" fixed v_fog : FOG,\n" +" uniform half4 u_fogColor,\n" +" uniform half2 u_alphaRef,\n" +" uniform half4 u_blurcolor,\n" +" uniform sampler2D tex0\n" +") {\n" +" half a = u_blurcolor.a;\n" +" half4 dst = v_color*tex2D(tex0, half2(v_tex0.x, 1.0-v_tex0.y));\n" +" dst.rgb = lerp(u_fogColor.rgb, dst.rgb, v_fog);\n" +" DoAlphaTest(dst.a, u_alphaRef);\n" + +" half4 prev = dst;\n" +" for (int i = 0; i < 5; i++){\n" +" half4 tmp = dst*(1.0-a) + prev*u_blurcolor*a;\n" +" prev = clamp(tmp, 0.0, 1.0);\n" +" }\n" +" half4 color;\n" +" color.rgb = prev.rgb;\n" +" color.a = 1.0f;\n" + +" return color;\n" +"}\n" + + +; diff --git a/src/extras/shaders/contrast_f.cg b/src/extras/shaders/contrast_f.cg new file mode 100644 index 00000000..c1ed26e3 --- /dev/null +++ b/src/extras/shaders/contrast_f.cg @@ -0,0 +1,33 @@ +#define u_fogStart (u_fogData.x) +#define u_fogEnd (u_fogData.y) +#define u_fogRange (u_fogData.z) +#define u_fogDisable (u_fogData.w) + +void DoAlphaTest(float a, half2 u_alphaRef) +{ + if(a < u_alphaRef.x || a >= u_alphaRef.y) + discard; +} + +float4 main( + half4 v_color : COLOR0, + half2 v_tex0 : TEXCOORD0, + fixed v_fog : FOG, + uniform half4 u_fogColor, + uniform half2 u_alphaRef, + uniform float3 u_contrastAdd, + uniform float3 u_contrastMult, + uniform sampler2D tex0 +) { + half4 dst = v_color*tex2D(tex0, half2(v_tex0.x, 1.0-v_tex0.y)); + dst.rgb = lerp(u_fogColor.rgb, dst.rgb, v_fog); + DoAlphaTest(dst.a, u_alphaRef); + + half4 color; + color.rgb = dst.rgb*u_contrastMult + u_contrastAdd; + color.a = 1.0f; + + return color; +} + + diff --git a/src/extras/shaders/contrast_f_cg.inc b/src/extras/shaders/contrast_f_cg.inc new file mode 100644 index 00000000..ef1a892c --- /dev/null +++ b/src/extras/shaders/contrast_f_cg.inc @@ -0,0 +1,35 @@ +const char *contrast_f_cg_src = +"#define u_fogStart (u_fogData.x)\n" +"#define u_fogEnd (u_fogData.y)\n" +"#define u_fogRange (u_fogData.z)\n" +"#define u_fogDisable (u_fogData.w)\n" + +"void DoAlphaTest(float a, half2 u_alphaRef)\n" +"{\n" +" if(a < u_alphaRef.x || a >= u_alphaRef.y)\n" +" discard;\n" +"}\n" + +"float4 main(\n" +" half4 v_color : COLOR0,\n" +" half2 v_tex0 : TEXCOORD0,\n" +" fixed v_fog : FOG,\n" +" uniform half4 u_fogColor,\n" +" uniform half2 u_alphaRef,\n" +" uniform float3 u_contrastAdd,\n" +" uniform float3 u_contrastMult,\n" +" uniform sampler2D tex0\n" +") {\n" +" half4 dst = v_color*tex2D(tex0, half2(v_tex0.x, 1.0-v_tex0.y));\n" +" dst.rgb = lerp(u_fogColor.rgb, dst.rgb, v_fog);\n" +" DoAlphaTest(dst.a, u_alphaRef);\n" + +" half4 color;\n" +" color.rgb = dst.rgb*u_contrastMult + u_contrastAdd;\n" +" color.a = 1.0f;\n" + +" return color;\n" +"}\n" + + +; diff --git a/src/extras/shaders/im2d_v.cg b/src/extras/shaders/im2d_v.cg new file mode 100644 index 00000000..28c3252b --- /dev/null +++ b/src/extras/shaders/im2d_v.cg @@ -0,0 +1,35 @@ +#define u_fogStart (u_fogData.x) +#define u_fogEnd (u_fogData.y) +#define u_fogRange (u_fogData.z) +#define u_fogDisable (u_fogData.w) + +#define MAX_LIGHTS 8 + +#define surfAmbient (u_surfProps.x) +#define surfSpecular (u_surfProps.y) +#define surfDiffuse (u_surfProps.z) + +fixed DoFog(float w, half4 u_fogData) +{ + return clamp((w - u_fogEnd)*u_fogRange, u_fogDisable, 1.0); +} + +void main( + float4 in_pos, + half4 in_color, + half2 in_tex0, + uniform half4 u_fogData, + uniform float4 u_xform, + half4 out v_color : COLOR0, + half2 out v_tex0 : TEXCOORD0, + fixed out v_fog : FOG, + float4 out gl_Position : POSITION +) { + gl_Position = in_pos; + gl_Position.w = 1.0; + gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw; + v_fog = DoFog(gl_Position.z, u_fogData); + gl_Position.xyz *= gl_Position.w; + v_color = in_color; + v_tex0 = in_tex0; +} diff --git a/src/extras/shaders/im2d_v_cg.inc b/src/extras/shaders/im2d_v_cg.inc new file mode 100644 index 00000000..133343e9 --- /dev/null +++ b/src/extras/shaders/im2d_v_cg.inc @@ -0,0 +1,37 @@ +const char *im2d_v_cg_src = +"#define u_fogStart (u_fogData.x)\n" +"#define u_fogEnd (u_fogData.y)\n" +"#define u_fogRange (u_fogData.z)\n" +"#define u_fogDisable (u_fogData.w)\n" + +"#define MAX_LIGHTS 8\n" + +"#define surfAmbient (u_surfProps.x)\n" +"#define surfSpecular (u_surfProps.y)\n" +"#define surfDiffuse (u_surfProps.z)\n" + +"fixed DoFog(float w, half4 u_fogData)\n" +"{\n" +" return clamp((w - u_fogEnd)*u_fogRange, u_fogDisable, 1.0);\n" +"}\n" + +"void main(\n" +" float4 in_pos,\n" +" half4 in_color,\n" +" half2 in_tex0,\n" +" uniform half4 u_fogData,\n" +" uniform float4 u_xform,\n" +" half4 out v_color : COLOR0,\n" +" half2 out v_tex0 : TEXCOORD0,\n" +" fixed out v_fog : FOG,\n" +" float4 out gl_Position : POSITION\n" +") {\n" +" gl_Position = in_pos;\n" +" gl_Position.w = 1.0;\n" +" gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;\n" +" v_fog = DoFog(gl_Position.z, u_fogData);\n" +" gl_Position.xyz *= gl_Position.w;\n" +" v_color = in_color;\n" +" v_tex0 = in_tex0;\n" +"}\n" +; diff --git a/src/fakerw/fake.cpp b/src/fakerw/fake.cpp index 6dfebb39..38447751 100644 --- a/src/fakerw/fake.cpp +++ b/src/fakerw/fake.cpp @@ -299,12 +299,40 @@ RwTextureAddressMode RwTextureGetAddressingV(const RwTexture *texture); // TODO void _rwD3D8TexDictionaryEnableRasterFormatConversion(bool enable) { } +#ifdef PSP2 +// librw-vita predates Raster::convertTexToCurrentPlatform, so do it by hand: +// round-trip through an Image, which also drops the palette the Vita cannot use. +static rw::Raster* +ConvertTexRaster(rw::Raster *ras) +{ + using namespace rw; + + if(ras->platform == rw::platform) + return ras; + // compatible platforms + if(ras->platform == PLATFORM_D3D8 && rw::platform == PLATFORM_D3D9 || + ras->platform == PLATFORM_D3D9 && rw::platform == PLATFORM_D3D8) + return ras; + + Image *img = ras->toImage(); + ras->destroy(); + img->unpalettize(); + ras = Raster::createFromImage(img); + img->destroy(); + return ras; +} +#endif + // hack for reading native textures RwBool rwNativeTextureHackRead(RwStream *stream, RwTexture **tex, RwInt32 size) { *tex = Texture::streamReadNative(stream); #ifdef LIBRW +#ifdef PSP2 + (*tex)->raster = ConvertTexRaster((*tex)->raster); +#else (*tex)->raster = rw::Raster::convertTexToCurrentPlatform((*tex)->raster); +#endif #endif return *tex != nil; } @@ -550,6 +578,7 @@ static void *reallocWrap(void *p, size_t sz, uint32 hint) { return real_realloc( // WARNING: unused parameters RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 resArenaSize) { +#ifndef PSP2 if(memFuncs){ real_malloc = memFuncs->rwmalloc; real_realloc = memFuncs->rwrealloc; @@ -560,6 +589,10 @@ RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 re }else{ Engine::init(nil); } +#else + // librw-vita's Engine::init takes no memory functions. + Engine::init(); +#endif return true; } // TODO: this is platform dependent @@ -601,8 +634,20 @@ void RwD3D8EngineSetRefreshRate(RwUInt32 refreshRate) {} RwBool RwD3D8DeviceSupportsDXTTexture(void) { return true; } -void RwD3D8EngineSetMultiSamplingLevels(RwUInt32 level) { Engine::setMultiSamplingLevels(level); } -RwUInt32 RwD3D8EngineGetMaxMultiSamplingLevels(void) { return Engine::getMaxMultiSamplingLevels(); } +// librw-vita has no multisampling controls - the level is fixed when vitaGL starts. +// Reporting 0 levels is what keeps the menu entry out; see MenuScreensCustom. +void RwD3D8EngineSetMultiSamplingLevels(RwUInt32 level) { +#ifndef PSP2 + Engine::setMultiSamplingLevels(level); +#endif +} +RwUInt32 RwD3D8EngineGetMaxMultiSamplingLevels(void) { +#ifndef PSP2 + return Engine::getMaxMultiSamplingLevels(); +#else + return 0; +#endif +} RpMaterial *RpMaterialCreate(void) { return Material::create(); } @@ -1004,7 +1049,15 @@ RwBool RtCharsetDestroy(RtCharset * charSet) { charSet->destroy(); return #include +#ifdef PSP2 +// librw-vita has no anisotropy plugin. Reporting zero keeps callers away from it. +RwInt8 RpAnisotGetMaxSupportedMaxAnisotropy(void) { return 0; } +RwTexture *RpAnisotTextureSetMaxAnisotropy(RwTexture *tex, RwInt8 val) { return tex; } +RwInt8 RpAnisotTextureGetMaxAnisotropy(RwTexture *tex) { return 0; } +RwBool RpAnisotPluginAttach(void) { return false; } +#else RwInt8 RpAnisotGetMaxSupportedMaxAnisotropy(void) { return rw::getMaxSupportedMaxAnisotropy(); } RwTexture *RpAnisotTextureSetMaxAnisotropy(RwTexture *tex, RwInt8 val) { tex->setMaxAnisotropy(val); return tex; } RwInt8 RpAnisotTextureGetMaxAnisotropy(RwTexture *tex) { return tex->getMaxAnisotropy(); } RwBool RpAnisotPluginAttach(void) { rw::registerAnisotropyPlugin(); return true; } +#endif diff --git a/src/math/Vector.cpp b/src/math/Vector.cpp index ee76e555..bce68615 100644 --- a/src/math/Vector.cpp +++ b/src/math/Vector.cpp @@ -3,6 +3,9 @@ void CVector::Normalise(void) { +#ifdef PSP2 + normalize3_neon(&this->x, &this->x); +#else float sq = MagnitudeSqr(); if (sq > 0.0f) { float invsqrt = RecipSqrt(sq); @@ -11,6 +14,7 @@ CVector::Normalise(void) z *= invsqrt; } else x = 1.0f; +#endif } CVector diff --git a/src/renderer/MBlur.cpp b/src/renderer/MBlur.cpp index 8e5fba2a..d1c9c317 100644 --- a/src/renderer/MBlur.cpp +++ b/src/renderer/MBlur.cpp @@ -12,6 +12,10 @@ #include "MBlur.h" #include "postfx.h" +#ifdef PSP2 +bool gPostFX = false; +#endif + // Originally taken from RW example 'mblur' RwRaster *CMBlur::pFrontBuffer; @@ -216,15 +220,23 @@ CMBlur::MotionBlurRender(RwCamera *cam, uint32 red, uint32 green, uint32 blue, u OverlayRender(cam, pFrontBuffer, color, type, bluralpha); #else if(BlurOn){ + // On PSP2 the copy has to happen before the overlay: rasterRenderFast reads + // the display buffer, which still holds the previous frame at this point. +#if !defined(PSP2) if(pFrontBuffer){ if(ms_bJustInitialised) ms_bJustInitialised = false; else OverlayRender(cam, pFrontBuffer, color, type, bluralpha); } +#endif RwRasterPushContext(pFrontBuffer); RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0); RwRasterPopContext(); +#ifdef PSP2 + if(pFrontBuffer) + OverlayRender(cam, pFrontBuffer, color, type, bluralpha); +#endif }else{ OverlayRender(cam, nil, color, type, bluralpha); } diff --git a/src/rw/TexRead.cpp b/src/rw/TexRead.cpp index 98e7d180..cee172fc 100644 --- a/src/rw/TexRead.cpp +++ b/src/rw/TexRead.cpp @@ -182,7 +182,8 @@ GetGPUcaps(GPUcaps *caps) caps->subplatform = 0; caps->dxtSupport = 0; // TODO: more later -#ifdef RW_GL3 +// librw-vita has no gl3Caps. +#if defined(RW_GL3) && !defined(PSP2) caps->subplatform = rw::gl3::gl3Caps.gles; caps->dxtSupport = rw::gl3::gl3Caps.dxtSupported; #endif @@ -338,9 +339,46 @@ DealWithTxdWriteError(uint32 num, uint32 count, const char *text) #define STREAMTELL(str) filesys->rwftell((str)->Type.file.fpFile) #endif +#ifdef PSP2 +static void +ConvertNativeTXD(const char *filename) +{ + RwStream *stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); + if(stream == nil) + return; + RwTexDictionary *txd = nil; + if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)) + txd = RwTexDictionaryGtaStreamRead(stream); + RwStreamClose(stream, nil); + if(txd == nil) + return; + + stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMWRITE, filename); + if(stream){ + RwTexDictionaryStreamWrite(txd, stream); + RwStreamClose(stream, nil); + } + RwTexDictionaryDestroy(txd); +} +#endif + bool CreateTxdImageForVideoCard() { +#ifdef PSP2 + static const char *looseTxds[] = { + "models\\menu.txd", "models\\fonts.txd", "models\\fonts_j.txd", + "models\\fonts_p.txd", "models\\fonts_r.txd", "models\\frontend.txd", + "models\\generic.txd", "models\\hud.txd", "models\\MISC.TXD", + "models\\particle.txd", "models\\x360btns.txd", + "txd\\mainsc1.txd", "txd\\mainsc2.txd", "txd\\NEWS.TXD", + "txd\\SPLASH1.TXD", "txd\\SPLASH2.TXD", "txd\\SPLASH3.TXD", + }; + for(uint32 i = 0; i < ARRAY_SIZE(looseTxds); i++){ + ConvertNativeTXD(looseTxds[i]); + } +#endif + uint8 *buf = new uint8[CDSTREAM_SECTOR_SIZE]; CDirectory *pDir = new CDirectory(TXDSTORESIZE); CDirectory::DirectoryInfo dirInfo; @@ -363,7 +401,8 @@ CreateTxdImageForVideoCard() return false; } -#ifdef RW_GL3 +// librw-vita has no needToReadBackTextures - it always keeps texture data around. +#if defined(RW_GL3) && !defined(PSP2) // so we can read back DXT with GLES // only works for textures that are not yet loaded // so let's hope that is the case for all @@ -406,7 +445,7 @@ CreateTxdImageForVideoCard() delete []buf; delete pDir; CStreaming::RemoveTxd(i); -#ifdef RW_GL3 +#if defined(RW_GL3) && !defined(PSP2) rw::gl3::needToReadBackTextures = false; #endif return false; @@ -441,7 +480,7 @@ CreateTxdImageForVideoCard() RwStreamClose(img, nil); delete []buf; -#ifdef RW_GL3 +#if defined(RW_GL3) && !defined(PSP2) rw::gl3::needToReadBackTextures = false; #endif diff --git a/src/save/SaveBuf.h b/src/save/SaveBuf.h index aad2e1a8..fa593041 100644 --- a/src/save/SaveBuf.h +++ b/src/save/SaveBuf.h @@ -22,7 +22,12 @@ template inline void ReadSaveBuf(T* out, uint8 *&buf) { + // Save buffers are not aligned, and the Vita traps unaligned wide loads. +#if !defined(PSP2) *out = *(T *)buf; +#else + sceClibMemcpy(out, buf, sizeof(T)); +#endif SkipSaveBuf(buf, sizeof(T)); } @@ -31,7 +36,11 @@ inline T * WriteSaveBuf(uint8 *&buf, const T &value) { T *p = (T *)buf; +#if !defined(PSP2) *p = value; +#else + sceClibMemcpy(p, &value, sizeof(T)); +#endif SkipSaveBuf(buf, sizeof(T)); return p; } diff --git a/src/skel/crossplatform.cpp b/src/skel/crossplatform.cpp index e69c22e1..6521a220 100644 --- a/src/skel/crossplatform.cpp +++ b/src/skel/crossplatform.cpp @@ -1,6 +1,322 @@ #include "common.h" #include "crossplatform.h" + +#ifdef PSP2 +extern SceTouchPanelInfo panelInfo[2]; +bool useRearpad = false; + +void *memcpy(void *dest, const void *src, size_t n) +{ + return sceClibMemcpy(dest, src, n); +} + +static unsigned char gButtons[GLFW_GAMEPAD_BUTTON_LAST+1]; +static float gAxes[GLFW_GAMEPAD_AXIS_LAST+1]; + +unsigned char* glfwGetJoystickButtons(int jid, int* count) +{ + int port_id = useRearpad ? 1 : 0; + SceCtrlData pad; + SceTouchData touch; + sceCtrlPeekBufferPositiveExt2(0, &pad, 1); + sceTouchPeek(port_id, &touch, 1); + gButtons[GLFW_GAMEPAD_BUTTON_CROSS] = pad.buttons & SCE_CTRL_CROSS ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_CIRCLE] = pad.buttons & SCE_CTRL_CIRCLE ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_SQUARE] = pad.buttons & SCE_CTRL_SQUARE ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_TRIANGLE] = pad.buttons & SCE_CTRL_TRIANGLE ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] = pad.buttons & SCE_CTRL_L1 ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] = pad.buttons & SCE_CTRL_R1 ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_BACK] = pad.buttons & SCE_CTRL_SELECT ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_START] = pad.buttons & SCE_CTRL_START ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_GUIDE] = GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] = pad.buttons & SCE_CTRL_L3 ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] = pad.buttons & SCE_CTRL_R3 ? GLFW_PRESS : GLFW_RELEASE; + for (int i = 0; i < touch.reportNum; i++) { + if (touch.report[i].y >= (panelInfo[port_id].minAaY + panelInfo[port_id].maxAaY) / 2) { + if (touch.report[i].x < (panelInfo[port_id].minAaX + panelInfo[port_id].maxAaX) / 2) + gButtons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] = GLFW_PRESS; + else + gButtons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] = GLFW_PRESS; + } + } + gButtons[GLFW_GAMEPAD_BUTTON_DPAD_UP] = pad.buttons & SCE_CTRL_UP ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] = pad.buttons & SCE_CTRL_RIGHT ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] = pad.buttons & SCE_CTRL_DOWN ? GLFW_PRESS : GLFW_RELEASE; + gButtons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] = pad.buttons & SCE_CTRL_LEFT ? GLFW_PRESS : GLFW_RELEASE; + if (count) + *count = GLFW_GAMEPAD_BUTTON_LAST+1; + return gButtons; +} + +float* glfwGetJoystickAxes(int jid, int* count) +{ + int port_id = useRearpad ? 1 : 0; + SceCtrlData pad; + SceTouchData touch; + sceCtrlPeekBufferPositiveExt2(0, &pad, 1); + sceTouchPeek(port_id, &touch, 1); + gAxes[GLFW_GAMEPAD_AXIS_LEFT_X] = ((float)pad.lx - 128.0f) / 128.0f; + gAxes[GLFW_GAMEPAD_AXIS_LEFT_Y] = ((float)pad.ly - 128.0f) / 128.0f; + gAxes[GLFW_GAMEPAD_AXIS_RIGHT_X] = ((float)pad.rx - 128.0f) / 128.0f; + gAxes[GLFW_GAMEPAD_AXIS_RIGHT_Y] = ((float)pad.ry - 128.0f) / 128.0f; + gAxes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] = pad.buttons & SCE_CTRL_L2 ? 1.0f : -1.0f; + gAxes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] = pad.buttons & SCE_CTRL_R2 ? 1.0f : -1.0f; + for (int i = 0; i < touch.reportNum; i++) { + if (touch.report[i].y < (panelInfo[port_id].minAaY + panelInfo[port_id].maxAaY) / 2) { + if (touch.report[i].x < (panelInfo[port_id].minAaX + panelInfo[port_id].maxAaX) / 2) + gAxes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] = 1.0f; + else + gAxes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] = 1.0f; + } + } + if (count) + *count = GLFW_GAMEPAD_AXIS_LAST+1; + return gAxes; +} + +int glfwGetGamepadState(int jid, GLFWgamepadstate* state) +{ + unsigned char *buttons = glfwGetJoystickButtons(jid, NULL); + float *axes = glfwGetJoystickAxes(jid, NULL); + sceClibMemcpy(state->buttons, buttons, sizeof(state->buttons)); + sceClibMemcpy(state->axes, axes, sizeof(state->axes)); + return 1; +} + +int glfwJoystickPresent(int jid) +{ + return jid == 0; +} + +int glfwJoystickIsGamepad(int jid) +{ + return jid == 0; +} + +int glfwGetKey(GLFWwindow* window, int key) +{ + return GLFW_RELEASE; +} + +int glfwGetMouseButton(GLFWwindow* window, int button) +{ + return GLFW_RELEASE; +} + +void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos) +{ + if (xpos) + *xpos = 0.0f; + if (ypos) + *ypos = 0.0f; + return; +} + +void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos) +{ + return; +} + +const char* glfwGetJoystickName(int jid) +{ + return "Vita Pad"; +} + +int glfwWindowShouldClose(GLFWwindow* window) +{ + return 0; +} + +GLFWmonitor* glfwGetPrimaryMonitor(void) +{ + return NULL; +} + +static GLFWvidmode gVideoMode { .width = 960, .height = 544 }; +const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor) +{ + return &gVideoMode; +} + +void glfwSetInputMode(GLFWwindow* handle, int mode, int value) +{ + return; +} + +int clock_gettime(int clk_id, struct timespec *tp) +{ + if (clk_id == CLOCK_MONOTONIC) { + SceKernelSysClock ticks; + sceKernelGetProcessTime(&ticks); + + tp->tv_sec = ticks / (1000 * 1000); + tp->tv_nsec = (ticks * 1000) % (1000 * 1000 * 1000); + + return 0; + } else if (clk_id == CLOCK_REALTIME) { + time_t seconds; + SceDateTime time; + sceRtcGetCurrentClockLocalTime(&time); + + sceRtcGetTime_t(&time, &seconds); + + tp->tv_sec = seconds; + tp->tv_nsec = time.microsecond * 1000; + + return 0; + } + + return -ENOSYS; +} + +int _lstat(const char *path, struct stat *buf) +{ + return stat(path, buf); +} + +char cur_dir[PATH_MAX] = PSP2_DATA_PATH; +char *_getcwd(char *buf, size_t size) +{ + if (buf != NULL) + strncpy(buf, cur_dir, size); + return cur_dir; +} + +char *_realpath(const char *path, char *resolved_path) +{ + char data_path[PATH_MAX]; + _getcwd(data_path, sizeof(data_path)); + + if (strncmp(path, "ux0:", 4) == 0) + strcpy(resolved_path, path); + else + sprintf(resolved_path, "%s/%s", data_path, path); + + return resolved_path; +} + +ssize_t readlink(const char *path, char *buf, size_t bufsiz) +{ + return 0; +} + +int _chdir(const char *path) +{ + if (strncmp(path, "ux0:", 4) == 0) + strcpy(cur_dir, path); + else + sprintf(cur_dir, "%s/%s", cur_dir, path); + return 0; +} + +int _mkdir(const char *pathname, mode_t mode) +{ + char* real = casepath(pathname); + int result = sceIoMkdir(real, mode) < 0 ? -1 : 0; + free(real); + return result; +} + +int unlink(const char *pathname) +{ + char* real = casepath(pathname); + int result = remove(real); + free(real); + return result; +} + +int _caserename(const char *old_filename, const char *new_filename) +{ + int result; + char *real_old = casepath(old_filename); + char *real_new = casepath(new_filename); + // hack so we don't even try to rename it to new_filename if it already exists + if (!real_new) { + free(real_old); + return -1; + } + if (!real_old) + result = rename(old_filename, real_new); + else + result = rename(real_old, real_new); + free(real_old); + free(real_new); + return result; +} + +HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) { + char pathCopy[MAX_PATH]; + snprintf(pathCopy, sizeof(pathCopy), PSP2_DATA_PATH "/%s", pathname); + + char *folder = strtok(pathCopy, "*"); + char *extension = strtok(NULL, "*"); + + // because strtok doesn't return NULL for last delimiter + if (extension - folder == strlen(pathname)) + extension = nil; + + // Case-sensitivity and backslashes... + // Will be freed at the bottom + char *realFolder = casepath(folder); + if (realFolder) { + folder = realFolder; + } + + strncpy(firstfile->folder, folder, sizeof(firstfile->folder)); + + if (extension) + strncpy(firstfile->extension, extension, sizeof(firstfile->extension)); + else + firstfile->extension[0] = '\0'; + + if (realFolder) + free(realFolder); + + HANDLE d; + if ((d = (HANDLE)opendir(firstfile->folder)) == NULL || !FindNextFile(d, firstfile)) + return NULL; + + return d; +} + +bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) { + dirent *file; + static struct stat fileStats; + static char path[PATH_MAX], relativepath[NAME_MAX + sizeof(finddata->folder) + 1]; + int extensionLen = strlen(finddata->extension); + while ((file = readdir((DIR*)d)) != NULL) { + + // We only want "DT_REG"ular Files, but reportedly some FS and OSes gives DT_UNKNOWN as type. + if (extensionLen == 0 || strncasecmp(&file->d_name[strlen(file->d_name) - extensionLen], finddata->extension, extensionLen) == 0) { + + sprintf(relativepath, "%s/%s", finddata->folder, file->d_name); + _realpath(relativepath, path); + stat(path, &fileStats); + strncpy(finddata->cFileName, file->d_name, sizeof(finddata->cFileName)); + finddata->ftLastWriteTime = fileStats.st_mtime; + return true; + } + } + return false; +} + +char* casepath(char const* path, bool checkPathFirst) +{ + char* out = (char*)malloc(PATH_MAX); + _realpath(path, out); + + size_t l = strlen(out); + for (int i = l-1; i >= 0; i--) { + if (out[i] == '\\' || out[i] == '/' || out[i] == ' ') + out[i] = '\0'; + else + break; + } + + return out; +} +#endif // Codes compatible with Windows and Linux #ifndef _WIN32 @@ -25,6 +341,9 @@ void GetLocalTime_CP(SYSTEMTIME *out) { // Compatible with Linux/POSIX and MinGW on Windows #ifndef _WIN32 +// The Vita has its own pair above: dirent works there, but paths have to go through +// the casepath shim, and there is no cwd to resolve them against. +#ifndef PSP2 HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) { char pathCopy[MAX_PATH]; strcpy(pathCopy, pathname); @@ -81,6 +400,7 @@ bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) { } return false; } +#endif // !PSP2 void GetDateFormat(int unused1, int unused2, SYSTEMTIME* in, int unused3, char* out, int size) { tm linuxTime; @@ -155,6 +475,9 @@ FILE* _fcaseopen(char const* filename, char const* mode) return result; } +// _fcaseopen above is shared - it only calls casepath. These two are not: the Vita +// supplies its own further up, resolving through sceIo and its faked cwd. +#ifndef PSP2 int _caserename(const char *old_filename, const char *new_filename) { int result; @@ -292,7 +615,8 @@ char* casepath(char const* path, bool checkPathFirst) } return out; } -#endif +#endif // !PSP2 +#endif // !_WIN32 #ifdef __SWITCH__ /* Taken from glibc */ diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h index 67bb4282..9ae1a3ad 100644 --- a/src/skel/crossplatform.h +++ b/src/skel/crossplatform.h @@ -1,6 +1,137 @@ +#ifndef _CROSSPLATFORM_H_ +#define _CROSSPLATFORM_H_ #include #include +#ifdef PSP2 + +#define GLFW_RELEASE 0 +#define GLFW_PRESS 1 + +#define GLFW_KEY_D 68 +#define GLFW_KEY_R 82 + +#define GLFW_MOUSE_BUTTON_1 0 +#define GLFW_MOUSE_BUTTON_2 1 +#define GLFW_MOUSE_BUTTON_3 2 +#define GLFW_MOUSE_BUTTON_4 3 +#define GLFW_MOUSE_BUTTON_5 4 +#define GLFW_MOUSE_BUTTON_6 5 +#define GLFW_MOUSE_BUTTON_7 6 +#define GLFW_MOUSE_BUTTON_8 7 +#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 +#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 +#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 +#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 + +#define GLFW_JOYSTICK_1 0 +#define GLFW_JOYSTICK_2 1 +#define GLFW_JOYSTICK_3 2 +#define GLFW_JOYSTICK_4 3 +#define GLFW_JOYSTICK_5 4 +#define GLFW_JOYSTICK_6 5 +#define GLFW_JOYSTICK_7 6 +#define GLFW_JOYSTICK_8 7 +#define GLFW_JOYSTICK_9 8 +#define GLFW_JOYSTICK_10 9 +#define GLFW_JOYSTICK_11 10 +#define GLFW_JOYSTICK_12 11 +#define GLFW_JOYSTICK_13 12 +#define GLFW_JOYSTICK_14 13 +#define GLFW_JOYSTICK_15 14 +#define GLFW_JOYSTICK_16 15 +#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 + +#define GLFW_GAMEPAD_BUTTON_A 0 +#define GLFW_GAMEPAD_BUTTON_B 1 +#define GLFW_GAMEPAD_BUTTON_X 2 +#define GLFW_GAMEPAD_BUTTON_Y 3 +#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4 +#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5 +#define GLFW_GAMEPAD_BUTTON_BACK 6 +#define GLFW_GAMEPAD_BUTTON_START 7 +#define GLFW_GAMEPAD_BUTTON_GUIDE 8 +#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9 +#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10 +#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11 +#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12 +#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13 +#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14 +#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT + +#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A +#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B +#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X +#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y + +#define GLFW_GAMEPAD_AXIS_LEFT_X 0 +#define GLFW_GAMEPAD_AXIS_LEFT_Y 1 +#define GLFW_GAMEPAD_AXIS_RIGHT_X 2 +#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3 +#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4 +#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5 +#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + +#define GLFW_CURSOR 0x00033001 +#define GLFW_CURSOR_HIDDEN 0x00034002 +#define GLFW_CURSOR_DISABLED 0x00034003 + +typedef struct GLFWgamepadstate +{ + unsigned char buttons[15]; + float axes[6]; +} GLFWgamepadstate; + +typedef struct GLFWvidmode +{ + int width; + int height; +} GLFWvidmode; + +typedef void GLFWmonitor; +typedef void GLFWwindow; + +GLFWmonitor* glfwGetPrimaryMonitor(void); +int glfwWindowShouldClose(GLFWwindow* window); +const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); +int glfwGetKey(GLFWwindow* window, int key); +int glfwGetMouseButton(GLFWwindow* window, int button); +void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); +void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); +int glfwJoystickPresent(int jid); +float* glfwGetJoystickAxes(int jid, int* count); +unsigned char* glfwGetJoystickButtons(int jid, int* count); +int glfwJoystickIsGamepad(int jid); +const char* glfwGetJoystickName(int jid); +int glfwGetGamepadState(int jid, GLFWgamepadstate* state); +void glfwSetInputMode(GLFWwindow* handle, int mode, int value); + +#define PATH_MAX 1024 +#define NAME_MAX 255 + +// One place for the data directory. There is no working directory on this platform, +// so everything relative gets resolved against this by _realpath/casepath. +#define PSP2_DATA_PATH "ux0:data/gta3" + +int _lstat(const char *path, struct stat *buf); +char *_realpath(const char *path, char *resolved_path); +int _chdir(const char *path); +char *_getcwd(char *buf, size_t size); +int _mkdir(const char *pathname, mode_t mode); + +#elif !defined(_WIN32) + +// Shared code calls the underscored names so the Vita can resolve paths against +// its data directory (it has no working directory of its own). Everywhere else +// they are the POSIX originals. Windows is left alone - it has a real _mkdir. +#define _lstat lstat +#define _realpath realpath +#define _mkdir mkdir +#define _getcwd getcwd +#define _chdir chdir + +#endif + // This is the common include for platform/renderer specific skeletons(glfw.cpp, win.cpp etc.) and using cross platform things (like Windows directories wrapper, platform specific global arrays etc.) // Functions that's different on glfw and win but have same signature, should be located on platform.h. @@ -182,3 +313,5 @@ void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int); #define GLFW_GAMEPAD_BUTTON_Y 2 #endif + +#endif diff --git a/src/skel/glfw/MoviePlayer.cpp b/src/skel/glfw/MoviePlayer.cpp index 18dd1709..8fa37530 100644 --- a/src/skel/glfw/MoviePlayer.cpp +++ b/src/skel/glfw/MoviePlayer.cpp @@ -1,11 +1,12 @@ // Nintendo Switch / GLFW movie player. Ported from the SDL2 build; only the // windowing-system glue differs (GLFW proc address / buffer swap / timer). // -// Switch-only on purpose: Windows D3D9 builds play the intro through the win.cpp -// skeleton (DirectShow), and the other GLFW targets never played it at all - the -// MPEG states there have always just fallen through. Everyone else gets the stub -// at the bottom of this file, which keeps glfw.cpp's intro state machine linkable -// without dragging pl_mpeg and OpenAL into those builds. +// The PS Vita gets its own backend further down, built on SceAvPlayer, behind the +// same interface. Windows D3D9 builds play the intro through the win.cpp skeleton +// (DirectShow), and the remaining GLFW targets never played it at all - the MPEG +// states there have always just fallen through. Those get the stub at the bottom of +// this file, which keeps glfw.cpp's intro state machine linkable without dragging +// pl_mpeg and OpenAL into their builds. #if defined __SWITCH__ && defined RW_GL3 && !defined LIBRW_SDL2 && defined AUDIO_OAL #include "common.h" @@ -659,6 +660,294 @@ void MoviePlayer::Draw() } } +#elif defined PSP2 + +// PS Vita backend. Same interface, different everything else: decoding is done by +// SceAvPlayer rather than pl_mpeg, the frames arrive as GXM textures that are wrapped +// into GL ones, and audio is pumped by a dedicated thread instead of OpenAL. +// Lifted out of glfw.cpp, where it used to sit inline alongside the intro state machine. + +#include "common.h" +#include // memalign, for the SceAvPlayer allocator callbacks +#include +#include +#include "crossplatform.h" +#include "MoviePlayer.h" + +#define FB_ALIGNMENT 0x40000 +#define ALIGN_MEM(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) + +SceAvPlayerHandle movie_player; + +GLuint movie_frame[2]; +uint8_t movie_frame_idx = 0; +SceGxmTexture *movie_tex[2]; +GLuint movie_fs; +GLuint movie_vs; +GLuint movie_prog; + +SceUID audio_thid; +int audio_new; +int audio_port; +int audio_len; +int audio_freq; +int audio_mode; + +enum { + PLAYER_INACTIVE, + PLAYER_ACTIVE, + PLAYER_STOP, +}; + +int player_state = PLAYER_INACTIVE; + +float movie_pos[8] = { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f +}; + +float movie_texcoord[8] = { + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f +}; + +void *mem_alloc(void *p, uint32_t align, uint32_t size) { + return memalign(align, size); +} + +void mem_free(void *p, void *ptr) { + free(ptr); +} + +void *gpu_alloc(void *p, uint32_t align, uint32_t size) { + if (align < FB_ALIGNMENT) { + align = FB_ALIGNMENT; + } + size = ALIGN_MEM(size, align); + return vglAlloc(size, VGL_MEM_SLOW); +} + +void gpu_free(void *p, void *ptr) { + glFinish(); + vglFree(ptr); +} + +void movie_player_audio_init(void) { + audio_port = -1; + for (int i = 0; i < 8; i++) { + if (sceAudioOutGetConfig(i, SCE_AUDIO_OUT_CONFIG_TYPE_LEN) >= 0) { + audio_port = i; + break; + } + } + + if (audio_port == -1) { + audio_port = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, 1024, 48000, SCE_AUDIO_OUT_MODE_STEREO); + audio_new = 1; + } else { + audio_len = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_LEN); + audio_freq = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_FREQ); + audio_mode = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_MODE); + audio_new = 0; + } +} + +void movie_player_audio_shutdown(void) { + if (audio_new) { + sceAudioOutReleasePort(audio_port); + } else { + sceAudioOutSetConfig(audio_port, audio_len, audio_freq, (SceAudioOutMode)audio_mode); + } +} + +int movie_player_audio_thread(SceSize args, void *argp) { + SceAvPlayerFrameInfo frame; + memset(&frame, 0, sizeof(SceAvPlayerFrameInfo)); + + while (player_state == PLAYER_ACTIVE && sceAvPlayerIsActive(movie_player)) { + if (sceAvPlayerGetAudioData(movie_player, &frame)) { + sceAudioOutSetConfig(audio_port, 1024, frame.details.audio.sampleRate, frame.details.audio.channelCount == 1 ? SCE_AUDIO_OUT_MODE_MONO : SCE_AUDIO_OUT_MODE_STEREO); + sceAudioOutOutput(audio_port, frame.pData); + } else { + sceKernelDelayThread(1000); + } + } + + return sceKernelExitDeleteThread(0); +} + +void movie_player_draw(void) { + if (player_state == PLAYER_ACTIVE) { + if (sceAvPlayerIsActive(movie_player)) { + SceAvPlayerFrameInfo frame; + if (sceAvPlayerGetVideoData(movie_player, &frame)) { + movie_frame_idx = (movie_frame_idx + 1) % 2; + sceGxmTextureInitLinear( + movie_tex[movie_frame_idx], + frame.pData, + SCE_GXM_TEXTURE_FORMAT_YVU420P2_CSC1, + frame.details.video.width, + frame.details.video.height, 0); + sceGxmTextureSetMinFilter(movie_tex[movie_frame_idx], SCE_GXM_TEXTURE_FILTER_LINEAR); + sceGxmTextureSetMagFilter(movie_tex[movie_frame_idx], SCE_GXM_TEXTURE_FILTER_LINEAR); + glUseProgram(movie_prog); + glBindTexture(GL_TEXTURE_2D, movie_frame[movie_frame_idx]); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &movie_pos[0]); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, &movie_texcoord[0]); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + vglSwapBuffers(GL_FALSE); + } + } else { + player_state = PLAYER_STOP; + } + } + + if (player_state == PLAYER_STOP) { + sceAvPlayerStop(movie_player); + sceKernelWaitThreadEnd(audio_thid, NULL, NULL); + sceAvPlayerClose(movie_player); + movie_player_audio_shutdown(); + player_state = PLAYER_INACTIVE; + glClear(GL_COLOR_BUFFER_BIT); + vglSwapBuffers(GL_FALSE); + } +} + +int movie_player_inited = 0; +void movie_player_init() { + if (movie_player_inited) + return; + + sceSysmoduleLoadModule(SCE_SYSMODULE_AVPLAYER); + + glGenTextures(2, movie_frame); + for (int i = 0; i < 2; i++) { + glBindTexture(GL_TEXTURE_2D, movie_frame[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 960, 544, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + movie_tex[i] = vglGetGxmTexture(GL_TEXTURE_2D); + vglFree(vglGetTexDataPointer(GL_TEXTURE_2D)); + } + + // The precompiled GXP blobs in shaders/movie_[vf].h can no longer be fed to + // glShaderBinary: it now expects a vitaGL-serialized blob, so unserialize_shader + // reads the "GXP\0" magic as a uniform count and walks off into garbage. + // Compile the original Cg source (kept as a comment in those headers) instead. + static const char *movie_v_src = + "void main(\n" + " float2 inPos,\n" + " float2 inTex,\n" + " float4 out pos : POSITION,\n" + " float2 out texcoord : TEXCOORD0\n" + ") {\n" + " texcoord = inTex;\n" + " pos = float4(inPos, 1.f, 1.f);\n" + "}\n"; + static const char *movie_f_src = + "float4 main(\n" + " float2 texcoord : TEXCOORD0,\n" + " uniform sampler2D tex\n" + ") {\n" + " return tex2D(tex, texcoord);\n" + "}\n"; + + movie_vs = glCreateShader(GL_CG_VERTEX_SHADER_EXT); + glShaderSource(movie_vs, 1, &movie_v_src, NULL); + glCompileShader(movie_vs); + + movie_fs = glCreateShader(GL_CG_FRAGMENT_SHADER_EXT); + glShaderSource(movie_fs, 1, &movie_f_src, NULL); + glCompileShader(movie_fs); + + movie_prog = glCreateProgram(); + glAttachShader(movie_prog, movie_vs); + glAttachShader(movie_prog, movie_fs); + glBindAttribLocation(movie_prog, 0, "inPos"); + glBindAttribLocation(movie_prog, 1, "inTex"); + glLinkProgram(movie_prog); + + movie_player_inited = 1; +} + +void movie_player_play(const char *file) { + SceIoStat st; + if (sceIoGetstat(file, &st) < 0) + return; + + movie_player_init(); + movie_player_audio_init(); + + SceAvPlayerInitData playerInit; + sceClibMemset(&playerInit, 0, sizeof(SceAvPlayerInitData)); + + playerInit.memoryReplacement.allocate = mem_alloc; + playerInit.memoryReplacement.deallocate = mem_free; + playerInit.memoryReplacement.allocateTexture = gpu_alloc; + playerInit.memoryReplacement.deallocateTexture = gpu_free; + + playerInit.basePriority = 0xA0; + playerInit.numOutputVideoFrameBuffers = 2; + playerInit.autoStart = GL_TRUE; + + movie_player = sceAvPlayerInit(&playerInit); + + sceAvPlayerAddSource(movie_player, file); + + audio_thid = sceKernelCreateThread("movie_audio_thread", movie_player_audio_thread, 0x10000100 - 10, 0x4000, 0, 0, NULL); + sceKernelStartThread(audio_thid, 0, NULL); + + player_state = PLAYER_ACTIVE; +} + +namespace MoviePlayer +{ + void Play(const char *path) + { + // The shared intro code asks for pl_mpeg's .mpg by a path relative to the + // data directory; SceAvPlayer wants .mp4 and an absolute one. + char rel[256]; + strncpy(rel, path, sizeof(rel) - 1); + rel[sizeof(rel) - 1] = '\0'; + char *ext = strrchr(rel, '.'); + if (ext != nil) + strcpy(ext, ".mp4"); + + char full[PATH_MAX]; + _realpath(rel, full); + + // This build has always shipped one set of intro videos, so the localised + // title reel the shared code asks for on French and German is not there. + // Fall back to the plain one rather than silently dropping the intro. + SceIoStat st; + if (sceIoGetstat(full, &st) < 0) { + char *ger = strstr(rel, "GER.mp4"); + if (ger != nil) { + strcpy(ger, ".mp4"); + _realpath(rel, full); + } + } + + movie_player_play(full); + } + + void Draw() { movie_player_draw(); } + + bool IsActive() { return player_state != PLAYER_INACTIVE; } + + void Stop() + { + if (player_state == PLAYER_ACTIVE) + player_state = PLAYER_STOP; // movie_player_draw does the teardown + } +} + #else // !(RW_GL3 && !LIBRW_SDL2) #include "MoviePlayer.h" diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 5a9faaf8..03a832f2 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -14,7 +14,9 @@ DWORD _dwOperatingSystemVersion; long _dwOperatingSystemVersion; #ifndef __SWITCH__ #ifndef __APPLE__ +#if !defined(PSP2) #include +#endif #else #include #include @@ -68,6 +70,18 @@ long _dwOperatingSystemVersion; #define MAX_SUBSYSTEMS (16) +#ifdef PSP2 +#include +#include +#include +#include "../message.h" + +int _newlib_heap_size_user = 256 * 1024 * 1024; + +#define DATA_PATH PSP2_DATA_PATH +#define RT_INI_PATH DATA_PATH "/" "10thae.ini" +#endif + rw::EngineOpenParams openParams; static RwBool ForegroundApp = TRUE; @@ -122,11 +136,13 @@ void _psCreateFolder(const char *path) #else struct stat info; char fullpath[PATH_MAX]; - realpath(path, fullpath); + // Underscored names: the Vita resolves these against its data directory, every + // other platform aliases them to the POSIX originals in crossplatform.h. + _realpath(path, fullpath); - if (lstat(fullpath, &info) != 0) { + if (_lstat(fullpath, &info) != 0) { if (errno == ENOENT || (errno != EACCES && !S_ISDIR(info.st_mode))) { - mkdir(fullpath, 0755); + _mkdir(fullpath, 0755); } } #endif @@ -560,7 +576,7 @@ psInitialize(void) #elif defined (__SWITCH__) svcGetInfo(&_dwMemAvailPhys, InfoType_UsedMemorySize, CUR_PROCESS_HANDLE, 0); debug("Physical memory size %llu\n", _dwMemAvailPhys); -#else +#elif !defined(PSP2) struct sysinfo systemInfo; sysinfo(&systemInfo); _dwMemAvailPhys = systemInfo.freeram; @@ -929,6 +945,9 @@ psSelectDevice() return TRUE; } +// None of these exist on the Vita: there is no window and no real GLFW, only the +// input stubs in crossplatform.cpp. +#if !defined(PSP2) #ifndef GET_KEYBOARD_INPUT_FROM_X11 void keypressCB(GLFWwindow* window, int key, int scancode, int action, int mods); #endif @@ -939,6 +958,7 @@ void cursorEnterCB(GLFWwindow* window, int entered); void windowFocusCB(GLFWwindow* window, int focused); void windowIconifyCB(GLFWwindow* window, int iconified); void joysChangeCB(int jid, int event); +#endif bool IsThisJoystickBlacklisted(int i) { @@ -963,6 +983,8 @@ void _InputInitialiseJoys() PSGLOBAL(joy1id) = -1; PSGLOBAL(joy2id) = -1; + // The Vita reads its pad through SceCtrl, so there is nothing to remap. +#if !defined(PSP2) // Load our gamepad mappings. #define SDL_GAMEPAD_DB_PATH "gamecontrollerdb.txt" FILE *f = fopen(SDL_GAMEPAD_DB_PATH, "rb"); @@ -992,6 +1014,7 @@ void _InputInitialiseJoys() if (EnvControlConfig != nil) { glfwUpdateGamepadMappings(EnvControlConfig); } +#endif // !PSP2 for (int i = 0; i <= GLFW_JOYSTICK_LAST; i++) { if (glfwJoystickPresent(i) && !IsThisJoystickBlacklisted(i)) { @@ -1004,6 +1027,9 @@ void _InputInitialiseJoys() } } +#ifdef PSP2 + ControlsManager.InitDefaultControlConfigJoyPad(16); +#else if (PSGLOBAL(joy1id) != -1) { int count; glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); @@ -1012,11 +1038,14 @@ void _InputInitialiseJoys() #endif ControlsManager.InitDefaultControlConfigJoyPad(count); } +#endif } long _InputInitialiseMouse() { +#if !defined(PSP2) glfwSetInputMode(PSGLOBAL(window), GLFW_CURSOR, GLFW_CURSOR_HIDDEN); +#endif return 0; } @@ -1025,6 +1054,8 @@ void psPostRWinit(void) RwVideoMode vm; RwEngineGetVideoModeInfo(&vm, GcurSelVM); + // There is no window to hang callbacks on under PSP2, and the resolution is fixed. +#if !defined(PSP2) glfwSetFramebufferSizeCallback(PSGLOBAL(window), resizeCB); #ifndef IGNORE_MOUSE_KEYBOARD #ifndef GET_KEYBOARD_INPUT_FROM_X11 @@ -1037,12 +1068,15 @@ void psPostRWinit(void) glfwSetWindowIconifyCallback(PSGLOBAL(window), windowIconifyCB); glfwSetWindowFocusCallback(PSGLOBAL(window), windowFocusCB); glfwSetJoystickCallback(joysChangeCB); +#endif _InputInitialiseJoys(); _InputInitialiseMouse(); +#if !defined(PSP2) if(!(vm.flags & rwVIDEOMODEEXCLUSIVE)) glfwSetWindowSize(PSGLOBAL(window), RsGlobal.maximumWidth, RsGlobal.maximumHeight); +#endif // Make sure all keys are released CPad::GetPad(0)->Clear(true); @@ -1341,7 +1375,7 @@ void HandleExit() #endif } -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(PSP2) void terminateHandler(int sig, siginfo_t *info, void *ucontext) { RsGlobal.quit = TRUE; } @@ -1353,6 +1387,9 @@ void dummyHandler(int sig){ #endif #endif +// Everything from here to the end of checkKeyPresses is GLFW window and keyboard +// plumbing that the Vita has no counterpart for. +#if !defined(PSP2) void resizeCB(GLFWwindow* window, int width, int height) { /* * Handle event to ensure window contents are displayed during re-size @@ -1797,14 +1834,18 @@ void checkKeyPresses() } #endif +#endif // !PSP2 // R* calls that in ControllerConfig, idk why void _InputTranslateShiftKeyUpDown(RsKeyCodes *rs) { +#if !defined(PSP2) RsKeyboardEventHandler(lshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsLSHIFT)); RsKeyboardEventHandler(rshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsRSHIFT)); +#endif } +#if !defined(PSP2) // TODO this only works in frontend(and luckily only frontend use this). Fun fact: if I get pos manually in game, glfw reports that it's > 32000 void cursorCB(GLFWwindow* window, double xpos, double ypos) { @@ -1821,7 +1862,9 @@ void cursorEnterCB(GLFWwindow* window, int entered) { PSGLOBAL(cursorIsInWindow) = !!entered; } +#endif // !PSP2 +#if !defined(PSP2) void windowFocusCB(GLFWwindow* window, int focused) { WindowFocused = !!focused; @@ -1831,6 +1874,7 @@ void windowIconifyCB(GLFWwindow* window, int iconified) { WindowIconified = !!iconified; } +#endif // !PSP2 /* ***************************************************************************** @@ -1869,9 +1913,35 @@ WinMain(HINSTANCE instance, #endif #else +#ifdef PSP2 +SceTouchPanelInfo panelInfo[2]; +#endif int main(int argc, char *argv[]) { +#ifdef PSP2 + sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE); + sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); + sceTouchSetSamplingState(SCE_TOUCH_PORT_BACK, SCE_TOUCH_SAMPLING_STATE_START); + // The stock clocks are not enough for this; the port has always overclocked. + scePowerSetArmClockFrequency(444); + scePowerSetBusClockFrequency(222); + scePowerSetGpuClockFrequency(222); + scePowerSetGpuXbarClockFrequency(166); + sceTouchGetPanelInfo(SCE_TOUCH_PORT_FRONT, &panelInfo[0]); + sceTouchGetPanelInfo(SCE_TOUCH_PORT_BACK, &panelInfo[1]); + + // Marker file: bail out with a readable message instead of crashing later if the + // game data was never copied over. + if (!fileExists(RT_INI_PATH)) + { + showMsgDialog("Liberty's gone quiet.\nNo game files found at " DATA_PATH); + sceKernelExitProcess(1); + } +#ifndef ENABLE_STDOUT + fclose(stdout); +#endif +#endif #endif RwV2d pos; RwInt32 i; @@ -1880,7 +1950,7 @@ main(int argc, char *argv[]) InitMemoryMgr(); #endif -#if !defined(_WIN32) && !defined(__SWITCH__) +#if !defined(_WIN32) && !defined(__SWITCH__) && !defined(PSP2) struct sigaction act; act.sa_sigaction = terminateHandler; act.sa_flags = SA_SIGINFO; @@ -1933,7 +2003,9 @@ main(int argc, char *argv[]) openParams.width = RsGlobal.maximumWidth; openParams.height = RsGlobal.maximumHeight; openParams.windowtitle = RsGlobal.appName; +#if !defined(PSP2) openParams.window = &PSGLOBAL(window); +#endif ControlsManager.MakeControllerActionsBlank(); ControlsManager.InitDefaultControlConfiguration(); @@ -2053,7 +2125,9 @@ main(int argc, char *argv[]) } #endif +#if !defined(PSP2) initkeymap(); +#endif while ( TRUE ) { @@ -2096,7 +2170,9 @@ main(int argc, char *argv[]) while( !RsGlobal.quit && !FrontEndMenuManager.m_bWantToRestart && !glfwWindowShouldClose(PSGLOBAL(window))) #endif { +#if !defined(PSP2) glfwPollEvents(); +#endif #ifdef GET_KEYBOARD_INPUT_FROM_X11 checkKeyPresses(); #endif @@ -2174,7 +2250,7 @@ main(int argc, char *argv[]) case GS_INIT_ONCE: { //CoUninitialize(); - + #ifdef PS2_MENU extern char version_name[64]; if ( CGame::frenchGame || CGame::germanGame ) @@ -2188,7 +2264,7 @@ main(int argc, char *argv[]) #endif if ( !CGame::InitialiseOnceAfterRW() ) RsGlobal.quit = TRUE; - + #ifdef PS2_MENU gGameState = GS_INIT_PLAYING_GAME; #else @@ -2202,7 +2278,7 @@ main(int argc, char *argv[]) case GS_INIT_FRONTEND: { LoadingScreen(nil, nil, "loadsc0"); - + FrontEndMenuManager.m_bGameNotLoaded = true; CMenuManager::m_bStartUpFrontEndRequested = true; @@ -2534,6 +2610,7 @@ void CapturePad(RwInt32 padID) return; } +#if !defined(PSP2) void joysChangeCB(int jid, int event) { if (event == GLFW_CONNECTED && !IsThisJoystickBlacklisted(jid)) { @@ -2558,6 +2635,7 @@ void joysChangeCB(int jid, int event) PSGLOBAL(joy2id) = -1; } } +#endif // !PSP2 #if (defined(_MSC_VER)) int strcasecmp(const char* str1, const char* str2) diff --git a/src/skel/message.cpp b/src/skel/message.cpp new file mode 100644 index 00000000..7051eaf5 --- /dev/null +++ b/src/skel/message.cpp @@ -0,0 +1,119 @@ +#include + +#include +#include +#include +#include + +#include "message.h" + +GxmDrawer::GxmDrawer() { + constexpr SceGxmInitializeParams params{ + 0, DISPLAY_MAX_PENDING_SWAPS, gxmVsyncCb, sizeof(void *), + SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE}; + sceGxmInitialize(¶ms); + + + for (auto &buf: displayBuf_) { + buf.data = dramAlloc(4 * DISPLAY_STRIDE_IN_PIXELS * DISPLAY_HEIGHT, + &buf.uid); + sceGxmColorSurfaceInit(&buf.surf, SCE_GXM_COLOR_FORMAT_A8B8G8R8, + SCE_GXM_COLOR_SURFACE_LINEAR, + SCE_GXM_COLOR_SURFACE_SCALE_NONE, + SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT, + DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_STRIDE_IN_PIXELS, buf.data); + sceGxmSyncObjectCreate(&buf.sync); + } +} + +GxmDrawer::~GxmDrawer() { + sceGxmTerminate(); + + for (const auto &buf: displayBuf_) { + sceGxmSyncObjectDestroy(buf.sync); + sceKernelFreeMemBlock(buf.uid); + } +} + +void GxmDrawer::gxmSwap() { + sceGxmPadHeartbeat(&displayBuf_[backBufferIndex_].surf, + displayBuf_[backBufferIndex_].sync); + sceGxmDisplayQueueAddEntry(displayBuf_[frontBufferIndex_].sync, + displayBuf_[backBufferIndex_].sync, + &displayBuf_[backBufferIndex_].data); + frontBufferIndex_ = backBufferIndex_; + backBufferIndex_ = (backBufferIndex_ + 1) % DISPLAY_BUFFER_COUNT; +} + +void GxmDrawer::clearScreenBuffer() const { + sceClibMemset(displayBuf_[backBufferIndex_].data, + static_cast(0xFF000000), + DISPLAY_HEIGHT * DISPLAY_STRIDE_IN_PIXELS * 4); +} + +void GxmDrawer::updateSceDialog() const { + const SceCommonDialogUpdateParam updateParam{ + {nullptr, displayBuf_[backBufferIndex_].data, + static_cast(0), + static_cast(0), DISPLAY_WIDTH, + DISPLAY_HEIGHT, DISPLAY_STRIDE_IN_PIXELS}, + displayBuf_[backBufferIndex_].sync}; + sceCommonDialogUpdate(&updateParam); +} + +void *GxmDrawer::dramAlloc(const std::uint32_t size, SceUID *uid) { + void *mem; + *uid = sceKernelAllocMemBlock("gpu_mem", + SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, + ALIGN(size, 256 * 1024), nullptr); + sceKernelGetMemBlockBase(*uid, &mem); + sceGxmMapMemory(mem, ALIGN(size, 256 * 1024), + static_cast( + SCE_GXM_MEMORY_ATTRIB_READ | + SCE_GXM_MEMORY_ATTRIB_WRITE)); + return mem; +} + +void GxmDrawer::gxmVsyncCb(const void *callbackData) { + const SceDisplayFrameBuf fb{ + sizeof(SceDisplayFrameBuf), + *static_cast(const_cast(callbackData)), + DISPLAY_STRIDE_IN_PIXELS, + 0, + DISPLAY_WIDTH, + DISPLAY_HEIGHT}; + sceDisplaySetFrameBuf(&fb, SCE_DISPLAY_SETBUF_NEXTFRAME); +} + +void showMsgDialog(const char *message) { + GxmDrawer gxmDrawer; + + SceMsgDialogUserMessageParam msgParam; + sceClibMemset(&msgParam, 0, sizeof(SceMsgDialogUserMessageParam)); + msgParam.buttonType = SCE_MSG_DIALOG_BUTTON_TYPE_OK; + msgParam.msg = reinterpret_cast(message); + + SceMsgDialogParam param; + sceMsgDialogParamInit(¶m); + param.mode = SCE_MSG_DIALOG_MODE_USER_MSG; + param.userMsgParam = &msgParam; + + constexpr SceCommonDialogConfigParam confParam{}; + sceCommonDialogSetConfigParam(&confParam); + sceMsgDialogInit(¶m); + + while (sceMsgDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) { + gxmDrawer.clearScreenBuffer(); + gxmDrawer.updateSceDialog(); + + gxmDrawer.gxmSwap(); + sceDisplayWaitVblankStart(); + } + sceMsgDialogTerm(); +} + +int fileExists(const char *path) { + SceIoStat stat; + return sceIoGetstat(path, &stat) >= 0; +} diff --git a/src/skel/message.h b/src/skel/message.h new file mode 100644 index 00000000..c8fb6aa0 --- /dev/null +++ b/src/skel/message.h @@ -0,0 +1,47 @@ +#ifndef MESSAGE_H +#define MESSAGE_H + +#include +#include + +#include +#include + +#ifndef ALIGN +#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) +#endif // ALIGN + +struct DisplayBuffer { + void *data; + SceGxmSyncObject *sync; + SceGxmColorSurface surf; + SceUID uid; +}; // struct DisplayBuffer + +class GxmDrawer { +public: + GxmDrawer(); + ~GxmDrawer(); + + void gxmSwap(); + void clearScreenBuffer() const; + void updateSceDialog() const; + + static void *dramAlloc(std::uint32_t size, SceUID *uid); + static void gxmVsyncCb(const void *callbackData); + +private: + static constexpr int DISPLAY_WIDTH{960}; + static constexpr int DISPLAY_HEIGHT{544}; + static constexpr int DISPLAY_STRIDE_IN_PIXELS{1024}; + static constexpr int DISPLAY_BUFFER_COUNT{2}; + static constexpr int DISPLAY_MAX_PENDING_SWAPS{1}; + + std::uint32_t backBufferIndex_{}, frontBufferIndex_{}; + std::array displayBuf_{}; +}; // class GxmDrawer + +void showMsgDialog(const char *message); +int fileExists(const char *path); + +#endif //MESSAGE_H