diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4306017..3e27445c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,7 @@ jobs: - { name: 'Emscripten', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, emsdk: true, werror: true, clang-tidy: false, cmake-wrapper: 'emcmake' } - { name: 'Nintendo 3DS', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, n3ds: true, werror: true, clang-tidy: false, container: 'devkitpro/devkitarm:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/3DS.cmake' } - { name: 'Xbox One', os: 'windows-latest', generator: 'Visual Studio 17 2022', dx5: false, config: false, msvc: true, werror: false, clang-tidy: false, vc-arch: 'amd64', cmake-args: '-DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.26100.0', xbox-one: true} + - { name: 'PlayStation Portable', os: 'ubuntu-latest', generator: 'Unix Makefiles', dx5: false, config: false, psp: true, werror: true, clang-tidy: false, container: 'pspdev/pspdev:latest', cmake-args: '-DCMAKE_TOOLCHAIN_FILE=/usr/local/pspdev/psp/share/pspdev.cmake' } - { name: 'Android', os: 'ubuntu-latest', generator: 'Ninja', dx5: false, config: false, android: true, werror: true, clang-tidy: false,} steps: - name: Setup vcvars @@ -176,8 +177,12 @@ jobs: if: ${{ !matrix.android }} run: cmake --build build --verbose --config Release + - name: Package Assets Separately + if: matrix.build-assets + run: (cd build/assets && zip -r ../../isle-assets.zip .) + - name: Package (CPack) - if: ${{ !matrix.n3ds && !matrix.android }} + if: ${{ !matrix.n3ds && !matrix.android && !matrix.psp }} run: | cd build success=0 @@ -226,6 +231,13 @@ jobs: mv *.3dsx dist/ mv *.cia dist/ + - name: Package (PSP) + if: ${{ matrix.psp }} + run: | + cd build + mkdir dist + mv EBOOT.PBP dist/ + - name: Package (Android) if: ${{ matrix.android }} run: | @@ -246,6 +258,9 @@ jobs: build/dist/*.3dsx build/dist/*.cia build/dist/*.apk + build/dist/*.PBP + isle-assets.zip + if-no-files-found: ignore flatpak: name: "Flatpak (${{ matrix.arch }})" diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a4ec707..04fe8a05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,15 +49,15 @@ option(ISLE_WERROR "Treat warnings as errors" OFF) option(ISLE_DEBUG "Enable imgui debug" ON) cmake_dependent_option(ISLE_USE_DX5 "Build with internal DirectX 5 SDK" "${NOT_MINGW}" "WIN32;CMAKE_SIZEOF_VOID_P EQUAL 4" OFF) cmake_dependent_option(ISLE_MINIWIN "Use miniwin" ON "NOT ISLE_USE_DX5" OFF) -cmake_dependent_option(ISLE_EXTENSIONS "Use extensions" ON "NOT ISLE_USE_DX5;NOT WINDOWS_STORE" OFF) -cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN;NOT NINTENDO_3DS;NOT WINDOWS_STORE" OFF) +cmake_dependent_option(ISLE_EXTENSIONS "Use extensions" ON "NOT ISLE_USE_DX5;NOT WINDOWS_STORE;NOT PSP" OFF) +cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "MSVC OR ISLE_MINIWIN;NOT NINTENDO_3DS;NOT WINDOWS_STORE;NOT PSP" OFF) cmake_dependent_option(ISLE_COMPILE_SHADERS "Compile shaders" ON "SDL_SHADERCROSS_BIN;TARGET Python3::Interpreter" OFF) -option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON) +cmake_dependent_option(CMAKE_POSITION_INDEPENDENT_CODE "Build with -fPIC" ON "NOT PSP" OFF) option(ENABLE_CLANG_TIDY "Enable clang-tidy") option(DOWNLOAD_DEPENDENCIES "Download dependencies" ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" CACHE PATH "Directory where to put executables and dll") set(ISLE_EMSCRIPTEN_HOST "" CACHE STRING "Host URL for Emscripten streaming (e.g., https://test.com)") -cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN" OFF) +cmake_dependent_option(BUILD_SHARED_LIBS "Build lego1 as a shared library" ON "NOT EMSCRIPTEN;NOT PSP" OFF) message(STATUS "Isle app: ${ISLE_BUILD_APP}") message(STATUS "Config app: ${ISLE_BUILD_CONFIG}") @@ -118,6 +118,7 @@ else() target_link_libraries(Isle::iniparser INTERFACE iniparser-shared) endif() +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if (ENABLE_CLANG_TIDY) find_program(CLANG_TIDY_BIN NAMES "clang-tidy" ENV "LLVM_ROOT" REQUIRED) @@ -379,6 +380,11 @@ if (WIN32) target_link_libraries(lego1 INTERFACE winmm) endif() target_link_libraries(lego1 PRIVATE libsmacker miniaudio) + +if (PSP) + target_link_libraries(lego1 PRIVATE atomic) +endif() + target_include_directories(lego1 PUBLIC $>) # lego1_impl sources @@ -556,6 +562,9 @@ if (ISLE_BUILD_APP) ) target_compile_definitions(isle PRIVATE ISLE_DEBUG) target_link_libraries(isle PRIVATE imgui) + if(PSP) + target_compile_definitions(imgui PRIVATE IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) + endif() find_path(valgrind_INCLUDE_PATH NAMES valgrind/callgrind.h) if(valgrind_INCLUDE_PATH) # Run isle under valgrind to create a profile. Use e.g. kcachegrind to view the profile. @@ -565,6 +574,9 @@ if (ISLE_BUILD_APP) target_include_directories(isle PRIVATE ${valgrind_INCLUDE_PATH}) endif() endif() + if(PSP) + target_link_libraries(isle PRIVATE pspfpu) + endif() if(EMSCRIPTEN) target_sources(isle PRIVATE ISLE/emscripten/config.cpp @@ -889,6 +901,19 @@ if(MSVC OR IOS) endif() elseif(APPLE AND NOT IOS) set(CPACK_GENERATOR DragNDrop) +elseif(PSP) + # Create an EBOOT.PBP file + set(BUILD_PRX 1) + create_pbp_file( + TARGET ${PROJECT_NAME} + ICON_PATH "${CMAKE_SOURCE_DIR}/ISLE/res/psp/Game/ICON0.png" + ANIM_PATH "${CMAKE_SOURCE_DIR}/ISLE/res/psp/Game/ICON1.pmf" + BACKGROUND_PATH "${CMAKE_SOURCE_DIR}/ISLE/res/psp/Game/PIC1.png" + PREVIEW_PATH NULL + MUSIC_PATH "${CMAKE_SOURCE_DIR}/ISLE/res/psp/Game/SND0.at3" + TITLE ${PROJECT_NAME} + VERSION 01.00 + ) else() set(CPACK_GENERATOR TGZ) endif() diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index c7444362..88dd7754 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -69,6 +69,10 @@ #include "xbox_one_series/config.h" #endif +#ifdef PSP +#include +#endif + #ifdef IOS #include "ios/config.h" #endif @@ -150,6 +154,10 @@ IsleApp::IsleApp() m_frameDelta = 10; m_windowActive = TRUE; +#ifdef PSP + pspFpuSetEnable(0); +#endif + #ifdef COMPAT_MODE { MxRect32 r(0, 0, 639, 479); @@ -887,7 +895,9 @@ MxResult IsleApp::SetupWindow() #if defined(MINIWIN) && !defined(__3DS__) && !defined(WINDOWS_STORE) SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); +#ifndef __PSP__ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); +#endif #endif window = SDL_CreateWindowWithProperties(props); diff --git a/ISLE/res/psp/Game/ICON0.png b/ISLE/res/psp/Game/ICON0.png new file mode 100644 index 00000000..d16c278b Binary files /dev/null and b/ISLE/res/psp/Game/ICON0.png differ diff --git a/ISLE/res/psp/Game/ICON1.pmf b/ISLE/res/psp/Game/ICON1.pmf new file mode 100644 index 00000000..bac01979 Binary files /dev/null and b/ISLE/res/psp/Game/ICON1.pmf differ diff --git a/ISLE/res/psp/Game/PIC1.png b/ISLE/res/psp/Game/PIC1.png new file mode 100644 index 00000000..b0916359 Binary files /dev/null and b/ISLE/res/psp/Game/PIC1.png differ diff --git a/ISLE/res/psp/Game/SND0.at3 b/ISLE/res/psp/Game/SND0.at3 new file mode 100644 index 00000000..47afc159 Binary files /dev/null and b/ISLE/res/psp/Game/SND0.at3 differ diff --git a/ISLE/res/psp/Save/EMPTY.png b/ISLE/res/psp/Save/EMPTY.png new file mode 100644 index 00000000..284649e6 Binary files /dev/null and b/ISLE/res/psp/Save/EMPTY.png differ diff --git a/ISLE/res/psp/Save/ICON0.png b/ISLE/res/psp/Save/ICON0.png new file mode 100644 index 00000000..d16c278b Binary files /dev/null and b/ISLE/res/psp/Save/ICON0.png differ diff --git a/ISLE/res/psp/Save/ICON1.pmf b/ISLE/res/psp/Save/ICON1.pmf new file mode 100644 index 00000000..faf5a77d Binary files /dev/null and b/ISLE/res/psp/Save/ICON1.pmf differ diff --git a/ISLE/res/psp/Save/Memory Stick Model Source.txt b/ISLE/res/psp/Save/Memory Stick Model Source.txt new file mode 100644 index 00000000..2a465251 --- /dev/null +++ b/ISLE/res/psp/Save/Memory Stick Model Source.txt @@ -0,0 +1 @@ +The model was sourced from https://3dwarehouse.sketchup.com/model/c300a598e63cb2f593dcf5e22ecc3f5e/Memory-Stick-Pro-Duo. It was further modified by Sonen10 to get the results seen in the animation. \ No newline at end of file diff --git a/ISLE/res/psp/Save/PIC1.png b/ISLE/res/psp/Save/PIC1.png new file mode 100644 index 00000000..7a0a7c4d Binary files /dev/null and b/ISLE/res/psp/Save/PIC1.png differ diff --git a/ISLE/res/psp/Save/SND0.AT3 b/ISLE/res/psp/Save/SND0.AT3 new file mode 100644 index 00000000..6f19ec8e Binary files /dev/null and b/ISLE/res/psp/Save/SND0.AT3 differ diff --git a/ISLE/res/psp/explanation.txt b/ISLE/res/psp/explanation.txt new file mode 100644 index 00000000..458d206c --- /dev/null +++ b/ISLE/res/psp/explanation.txt @@ -0,0 +1,10 @@ +Explanation of where the assets in these folders are used +ICON0.PNG - Icon used in various scenarios depending on if it's used for a save or a game. This includes when: +> The game/save isn't selected in the XMB +> There is no ICON1.PMF present (XMB only) +> The "Group Content" mode is enabled in the XMB game selection menu +> The game is on a save management screen with UI elements shared with the XMB (note the font) +ICON1.PMF - Seen when the game/save is selected in the XMB (NOT IN-GAME) +PIC1.PNG - Seen as a background when the game/save is selected in the XMB +SND0.AT3 - Plays when the game/save is selected in the XMB (NOT IN-GAME) +EMPTY.PNG - Used as an icon for empty saves when selecting a save to write to. diff --git a/LEGO1/omni/include/mxthread.h b/LEGO1/omni/include/mxthread.h index 0772dc65..28732f0a 100644 --- a/LEGO1/omni/include/mxthread.h +++ b/LEGO1/omni/include/mxthread.h @@ -5,7 +5,11 @@ #include "mxsemaphore.h" #include "mxtypes.h" +#ifdef PSP +#include +#else #include +#endif class MxCore; @@ -41,9 +45,15 @@ class MxThread { virtual ~MxThread(); private: +#ifdef PSP + static int ThreadProc(SceSize args, void* argp); + + int m_thread; +#else static int SDLCALL ThreadProc(void* p_thread); SDL_Thread* m_thread; +#endif MxBool m_running; // 0x0c MxSemaphore m_semaphore; // 0x10 diff --git a/LEGO1/omni/src/system/mxthread.cpp b/LEGO1/omni/src/system/mxthread.cpp index 307db15a..384ea1a9 100644 --- a/LEGO1/omni/src/system/mxthread.cpp +++ b/LEGO1/omni/src/system/mxthread.cpp @@ -3,6 +3,9 @@ #include "decomp.h" #include +#ifdef PSP +#include +#endif DECOMP_SIZE_ASSERT(MxThread, 0x1c) @@ -10,7 +13,11 @@ DECOMP_SIZE_ASSERT(MxThread, 0x1c) // FUNCTION: BETA10 0x10147540 MxThread::MxThread() { +#ifdef PSP + m_thread = 0; +#else m_thread = NULL; +#endif m_running = TRUE; } @@ -19,7 +26,13 @@ MxThread::MxThread() MxThread::~MxThread() { if (m_thread) { +#ifdef PSP + sceKernelWaitThreadEnd(m_thread, NULL); + sceKernelDeleteThread(m_thread); + m_thread = 0; +#else SDL_WaitThread(m_thread, NULL); +#endif } } @@ -29,7 +42,27 @@ MxResult MxThread::Start(MxS32 p_stackSize, MxS32 p_flag) { MxResult result = FAILURE; - if (m_semaphore.Init(0, 1) != SUCCESS) { + if (m_semaphore.Init(0, 1) == SUCCESS) { +#ifdef PSP + int thid = sceKernelCreateThread( + "MxThread", + ThreadProc, + 0x18, // priority (0x18 is typical) + p_stackSize * 4, + 0, + NULL + ); + + if (thid >= 0) { + MxThread* self = this; + int start = sceKernelStartThread(thid, sizeof(MxThread*), &self); + if (start >= 0) { + result = SUCCESS; + m_thread = thid; // store thread ID if needed + } + } + } +#else goto done; } @@ -45,6 +78,7 @@ MxResult MxThread::Start(MxS32 p_stackSize, MxS32 p_flag) SDL_DestroyProperties(props); } +#endif result = SUCCESS; @@ -100,12 +134,20 @@ void MxThread::Terminate() m_semaphore.Acquire(); } +#ifdef PSP +int MxThread::ThreadProc(SceSize args, void* argp) +{ + MxThread* self = *(MxThread**) argp; + return self->Run(); +} +#else // FUNCTION: LEGO1 0x100bf680 // FUNCTION: BETA10 0x1014783b int MxThread::ThreadProc(void* p_thread) { return static_cast(p_thread)->Run(); } +#endif // FUNCTION: LEGO1 0x100bf690 // FUNCTION: BETA10 0x10147855 diff --git a/README.md b/README.md index a6bd8eff..975f913c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Please note: this project is primarily dedicated to achieving platform independe | [Web](https://isle.pizza) | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | Nintendo 3DS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | Xbox One | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | +| PSP | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | iOS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | Android | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | diff --git a/miniwin/src/d3drm/backends/opengl1/actual.cpp b/miniwin/src/d3drm/backends/opengl1/actual.cpp index 4b83daff..1169cb7e 100644 --- a/miniwin/src/d3drm/backends/opengl1/actual.cpp +++ b/miniwin/src/d3drm/backends/opengl1/actual.cpp @@ -131,6 +131,7 @@ void GL11_BeginFrame(const Matrix4x4* projection) glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glEnable(GL_LIGHTING); +#ifndef __PSP__ glEnable(GL_COLOR_MATERIAL); // Disable all lights and reset global ambient @@ -139,6 +140,7 @@ void GL11_BeginFrame(const Matrix4x4* projection) } glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); +#endif // Projection and view glMatrixMode(GL_PROJECTION); @@ -214,8 +216,18 @@ void GL11_SubmitDraw( glLoadMatrixf(&modelViewMatrix[0][0]); glEnable(GL_NORMALIZE); +#ifndef __PSP__ glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glColor4ub(appearance.color.r, appearance.color.g, appearance.color.b, appearance.color.a); +#else + GLfloat color[4] = { + appearance.color.r / 255.0f, + appearance.color.g / 255.0f, + appearance.color.b / 255.0f, + appearance.color.a / 255.0f + }; + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); +#endif if (appearance.shininess != 0.0f) { GLfloat whiteSpec[] = {1.f, 1.f, 1.f, 1.f}; diff --git a/miniwin/src/d3drm/backends/opengl1/renderer.cpp b/miniwin/src/d3drm/backends/opengl1/renderer.cpp index 2d4c14c9..0ce14701 100644 --- a/miniwin/src/d3drm/backends/opengl1/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengl1/renderer.cpp @@ -23,7 +23,9 @@ Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height, DWORD msa // call below when on an EGL-based backend, and crashes with EGL_BAD_MATCH. SDL_GL_ResetAttributes(); // But ResetAttributes resets it to 16. +#ifndef __PSP__ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); +#endif SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); diff --git a/miniwin/src/internal/d3drmrenderer_opengl1.h b/miniwin/src/internal/d3drmrenderer_opengl1.h index 894fa10c..651b61d3 100644 --- a/miniwin/src/internal/d3drmrenderer_opengl1.h +++ b/miniwin/src/internal/d3drmrenderer_opengl1.h @@ -56,11 +56,13 @@ class OpenGL1Renderer : public Direct3DRMRenderer { inline static void OpenGL1Renderer_EnumDevice(const IDirect3DMiniwin* d3d, LPD3DENUMDEVICESCALLBACK cb, void* ctx) { +#ifndef __PSP__ Direct3DRMRenderer* device = OpenGL1Renderer::Create(640, 480, d3d->GetMSAASamples()); if (!device) { return; } delete device; +#endif D3DDEVICEDESC halDesc = {}; halDesc.dcmColorModel = D3DCOLORMODEL::RGB;