diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb2600a5..3a506ce2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,15 @@ jobs: with: cache: 'true' + - name: Install 3DS dependencies + if: ${{ matrix.n3ds }} + run: | + wget https://github.com/diasurgical/bannertool/releases/download/1.2.0/bannertool.zip + unzip -j "bannertool.zip" "linux-x86_64/bannertool" -d "/opt/devkitpro/tools/bin" + wget https://github.com/3DSGuy/Project_CTR/releases/download/makerom-v0.18/makerom-v0.18-ubuntu_x86_64.zip + unzip "makerom-v0.18-ubuntu_x86_64.zip" "makerom" -d "/opt/devkitpro/tools/bin" + chmod a+x /opt/devkitpro/tools/bin/makerom + - name: Install Linux dependencies (apt-get) if: ${{ matrix.linux }} run: | @@ -126,7 +135,20 @@ jobs: if: ${{ !matrix.n3ds && !matrix.vita }} run: | cd build - cpack . + success=0 + max_tries=10 + for i in $(seq $max_tries); do + cpack . && success=1 + if test $success = 1; then + break + fi + echo "Package creation failed. Sleep 1 second and try again." + sleep 1 + done + if test $success = 0; then + echo "Package creation failed after $max_tries attempts." + exit 1 + fi - name: Install linuxdeploy if: ${{ matrix.linux }} @@ -157,6 +179,7 @@ jobs: cd build mkdir dist mv *.3dsx dist/ + mv *.cia dist/ - name: Package (Vita) if: ${{ matrix.vita }} @@ -173,6 +196,7 @@ jobs: build/dist/isle-* build/dist/*.AppImage build/dist/*.3dsx + build/dist/*.cia build/dist/*.vpk flatpak: diff --git a/CMakeLists.txt b/CMakeLists.txt index 838eaa87..be1d3d54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -533,6 +533,7 @@ if (ISLE_BUILD_APP) endif() if(EMSCRIPTEN) target_sources(isle PRIVATE + ISLE/emscripten/config.cpp ISLE/emscripten/events.cpp ISLE/emscripten/filesystem.cpp ISLE/emscripten/messagebox.cpp @@ -686,6 +687,7 @@ if (NOT (NINTENDO_3DS OR WINDOWS_STORE)) install(TARGETS isle ${install_extra_targets} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + BUNDLE DESTINATION "." ) endif() if(EMSCRIPTEN) @@ -699,16 +701,51 @@ add_subdirectory(packaging) set(CPACK_PACKAGE_DIRECTORY "dist") set(CPACK_PACKAGE_FILE_NAME "isle-${PROJECT_VERSION}-${ISLE_PACKAGE_NAME}") if(NINTENDO_3DS) + find_program(BANNERTOOL bannertool) + find_program(MAKEROM makerom) + ctr_generate_smdh(isle.smdh NAME "LEGO Island" TITLE "LEGO Island" DESCRIPTION "LEGO Island for the Nintendo 3DS" AUTHOR "isledecomp/isle-portable" VERSION "${PROJECT_VERSION}" - ICON "ISLE/res/3ds/isle.png" + ICON "ISLE/res/3ds/icon.png" ) ctr_create_3dsx(isle SMDH isle.smdh) + if(BANNERTOOL AND MAKEROM) + add_custom_command( + OUTPUT "isle.bnr" + COMMAND "${BANNERTOOL}" makebanner + -i "${CMAKE_SOURCE_DIR}/ISLE/res/3ds/banner.png" + -a "${CMAKE_SOURCE_DIR}/ISLE/res/3ds/banner.wav" + -o "isle.bnr" + DEPENDS "${CMAKE_SOURCE_DIR}/ISLE/res/3ds/banner.png" "${CMAKE_SOURCE_DIR}/ISLE/res/3ds/banner.wav" + VERBATIM + ) + + add_custom_command( + OUTPUT "isle.cia" + COMMAND "${MAKEROM}" + -f cia + -exefslogo + -o "isle.cia" + -rsf "${CMAKE_SOURCE_DIR}/ISLE/res/3ds/template.rsf" + -major "${CMAKE_PROJECT_VERSION_MAJOR}" + -minor "${CMAKE_PROJECT_VERSION_MINOR}" + -micro 0 + -icon "isle.smdh" + -banner "isle.bnr" + -elf "isle.elf" + DEPENDS "${CMAKE_SOURCE_DIR}/ISLE/res/3ds/template.rsf" "isle.smdh" "isle.bnr" + COMMENT "Building CIA executable target isle.cia" + VERBATIM + ) + + add_custom_target("isle_cia" ALL DEPENDS "isle.cia" isle) + install(FILES "$/isle.cia" DESTINATION "${CMAKE_INSTALL_BINDIR}") + endif() install(FILES "$/isle.3dsx" DESTINATION "${CMAKE_INSTALL_BINDIR}") endif() if(WINDOWS_STORE) @@ -787,6 +824,8 @@ if(VITA) endif() if(MSVC) set(CPACK_GENERATOR ZIP) +elseif(APPLE AND NOT IOS) + set(CPACK_GENERATOR DragNDrop) else() set(CPACK_GENERATOR TGZ) endif() diff --git a/CONFIG/qt/CMakeLists.txt b/CONFIG/qt/CMakeLists.txt index 8f117a8f..89dcad28 100644 --- a/CONFIG/qt/CMakeLists.txt +++ b/CONFIG/qt/CMakeLists.txt @@ -52,6 +52,7 @@ if(WIN32) endif() install(TARGETS isle-config RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + BUNDLE DESTINATION "." ) set(isle_targets ${isle_targets};isle-config PARENT_SCOPE) diff --git a/CONFIG/qt/res/maindialog.ui b/CONFIG/qt/res/maindialog.ui index 12739f27..66e2b52e 100644 --- a/CONFIG/qt/res/maindialog.ui +++ b/CONFIG/qt/res/maindialog.ui @@ -455,11 +455,6 @@ Unknown - Broken - - - Fake Mosaic - - diff --git a/ISLE/emscripten/config.cpp b/ISLE/emscripten/config.cpp new file mode 100644 index 00000000..7d9cdb31 --- /dev/null +++ b/ISLE/emscripten/config.cpp @@ -0,0 +1,22 @@ +#include "config.h" + +#include "filesystem.h" + +#include +#include +#include + +void Emscripten_SetupDefaultConfigOverrides(dictionary* p_dictionary) +{ + SDL_Log("Overriding default config for Emscripten"); + + iniparser_set(p_dictionary, "isle:diskpath", Emscripten_bundledPath); + iniparser_set(p_dictionary, "isle:cdpath", Emscripten_streamPath); + iniparser_set(p_dictionary, "isle:savepath", Emscripten_savePath); + iniparser_set(p_dictionary, "isle:Full Screen", "false"); + iniparser_set(p_dictionary, "isle:Flip Surfaces", "true"); + + // clang-format off + MAIN_THREAD_EM_ASM({JSEvents.fullscreenEnabled = function() { return false; }}); +// clang-format on +} diff --git a/ISLE/emscripten/config.h b/ISLE/emscripten/config.h new file mode 100644 index 00000000..255888c0 --- /dev/null +++ b/ISLE/emscripten/config.h @@ -0,0 +1,8 @@ +#ifndef EMSCRIPTEN_CONFIG_H +#define EMSCRIPTEN_CONFIG_H + +#include "dictionary.h" + +void Emscripten_SetupDefaultConfigOverrides(dictionary* p_dictionary); + +#endif // EMSCRIPTEN_CONFIG_H diff --git a/ISLE/emscripten/emscripten.patch b/ISLE/emscripten/emscripten.patch index 0bc8222d..7e9ad611 100644 --- a/ISLE/emscripten/emscripten.patch +++ b/ISLE/emscripten/emscripten.patch @@ -140,20 +140,26 @@ index e8c9f7e21..caf1971d2 100644 - }); diff --git a/src/preamble.js b/src/preamble.js -index 572694517..0d2f4421b 100644 +index 572694517..44e65c823 100644 --- a/src/preamble.js +++ b/src/preamble.js -@@ -1062,3 +1062,13 @@ function getCompilerSetting(name) { +@@ -1062,3 +1062,19 @@ function getCompilerSetting(name) { // dynamic linker as symbols are loaded. var asyncifyStubs = {}; #endif + -+(async () => { -+ try { -+ await navigator.storage.getDirectory(); -+ Module["disableOpfs"] = false; -+ } catch (e) { -+ Module["disableOpfs"] = true; -+ } -+})(); ++if (typeof document !== "undefined") { ++ (async () => { ++ try { ++ await navigator.storage.getDirectory(); ++ Module["disableOpfs"] = false; ++ } catch (e) { ++ Module["disableOpfs"] = true; ++ } ++ console.log("disableOpfs: " + Module["disableOpfs"]); ++ })(); ++ ++ Module["disableOffscreenCanvases"] ||= !document.createElement('canvas').getContext('webgl'); ++ console.log("disableOffscreenCanvases: " + Module["disableOffscreenCanvases"]); ++} + diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 33efebd2..d2d02ebe 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -48,6 +48,7 @@ #include #ifdef __EMSCRIPTEN__ +#include "emscripten/config.h" #include "emscripten/events.h" #include "emscripten/filesystem.h" #include "emscripten/messagebox.h" @@ -1033,19 +1034,15 @@ bool IsleApp::LoadConfig() } #ifdef __EMSCRIPTEN__ - const char* hdPath = Emscripten_bundledPath; -#else - const char* hdPath = iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath()); + Emscripten_SetupDefaultConfigOverrides(dict); #endif + + const char* hdPath = iniparser_getstring(dict, "isle:diskpath", SDL_GetBasePath()); m_hdPath = new char[strlen(hdPath) + 1]; strcpy(m_hdPath, hdPath); MxOmni::SetHD(m_hdPath); -#ifdef __EMSCRIPTEN__ - const char* cdPath = Emscripten_streamPath; -#else const char* cdPath = iniparser_getstring(dict, "isle:cdpath", MxOmni::GetCD()); -#endif m_cdPath = new char[strlen(cdPath) + 1]; strcpy(m_cdPath, cdPath); MxOmni::SetCD(m_cdPath); @@ -1055,13 +1052,7 @@ bool IsleApp::LoadConfig() strcpy(m_mediaPath, mediaPath); m_flipSurfaces = iniparser_getboolean(dict, "isle:Flip Surfaces", m_flipSurfaces); - -#ifdef __EMSCRIPTEN__ - m_fullScreen = FALSE; -#else m_fullScreen = iniparser_getboolean(dict, "isle:Full Screen", m_fullScreen); -#endif - m_wideViewAngle = iniparser_getboolean(dict, "isle:Wide View Angle", m_wideViewAngle); m_use3dSound = iniparser_getboolean(dict, "isle:3DSound", m_use3dSound); m_useMusic = iniparser_getboolean(dict, "isle:Music", m_useMusic); @@ -1101,11 +1092,7 @@ bool IsleApp::LoadConfig() // [library:config] // The original game does not save any data if no savepath is given. // Instead, we use SDLs prefPath as a default fallback and always save data. -#ifdef __EMSCRIPTEN__ - const char* savePath = Emscripten_savePath; -#else const char* savePath = iniparser_getstring(dict, "isle:savepath", prefPath); -#endif m_savePath = new char[strlen(savePath) + 1]; strcpy(m_savePath, savePath); diff --git a/ISLE/res/3ds/banner.png b/ISLE/res/3ds/banner.png new file mode 100644 index 00000000..550b53ea Binary files /dev/null and b/ISLE/res/3ds/banner.png differ diff --git a/ISLE/res/3ds/banner.wav b/ISLE/res/3ds/banner.wav new file mode 100644 index 00000000..ce7849e7 Binary files /dev/null and b/ISLE/res/3ds/banner.wav differ diff --git a/ISLE/res/3ds/isle.png b/ISLE/res/3ds/icon.png similarity index 100% rename from ISLE/res/3ds/isle.png rename to ISLE/res/3ds/icon.png diff --git a/ISLE/res/3ds/logo.bcma.lz b/ISLE/res/3ds/logo.bcma.lz new file mode 100644 index 00000000..dd9db8cd Binary files /dev/null and b/ISLE/res/3ds/logo.bcma.lz differ diff --git a/ISLE/res/3ds/template.rsf b/ISLE/res/3ds/template.rsf new file mode 100644 index 00000000..bbde6448 --- /dev/null +++ b/ISLE/res/3ds/template.rsf @@ -0,0 +1,283 @@ +BasicInfo: + Title : "Lego Island" + ProductCode : "CTR-P-ISLE" + Logo : Homebrew + +TitleInfo: + Category : Application + UniqueId : 0x76E7E + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses where applicable (currently only exefs:/.code) + +AccessControlInfo: + CoreVersion : 2 + + # Exheader Format Version + DescVersion : 2 + + # Minimum Required Kernel Version (below is for 4.5.0) + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + + # ExtData + UseExtSaveData : false # enables ExtData + #ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId + + # FS:USER Archive Access Permissions + # Uncomment as required + FileSystemAccess: + #- CategorySystemApplication + #- CategoryHardwareCheck + #- CategoryFileSystemTool + #- Debug + #- TwlCardBackup + #- TwlNandData + #- Boss + - DirectSdmc + - Core + #- CtrNandRo + #- CtrNandRw + #- CtrNandRoWrite + #- CategorySystemSettings + #- CardBoard + #- ExportImportIvs + - DirectSdmcWrite + #- SwitchCleanup + #- SaveDataMove + #- Shop + #- Shell + #- CategoryHomeMenu + #- SeedDB + IoAccessControl: + #- FsMountNand + #- FsMountNandRoWrite + #- FsMountTwln + #- FsMountWnand + #- FsMountCardSpi + #- UseSdif3 + #- CreateSeed + #- UseCardSpi + + # Process Settings + MemoryType : Application # Application/System/Base + SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB + IdealProcessor : 0 + AffinityMask : 1 + Priority : 16 + MaxCpu : 0x9E # Default + HandleTableSize : 0x200 + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : true + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : true + PermitMainFunctionArgument : true + CanShareDeviceMemory : true + RunnableOnSleep : false + SpecialMemoryArrange : true + + # New3DS Exclusive Process Settings + SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 268MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true + + # Virtual Address Mappings + IORegisterMapping: + - 1ff00000-1ff7ffff # DSP memory + MemoryMapping: + - 1f000000-1f5fffff:r # VRAM + + # Accessible SVCs, : + SystemCallAccess: + ControlMemory: 1 + QueryMemory: 2 + ExitProcess: 3 + GetProcessAffinityMask: 4 + SetProcessAffinityMask: 5 + GetProcessIdealProcessor: 6 + SetProcessIdealProcessor: 7 + CreateThread: 8 + ExitThread: 9 + SleepThread: 10 + GetThreadPriority: 11 + SetThreadPriority: 12 + GetThreadAffinityMask: 13 + SetThreadAffinityMask: 14 + GetThreadIdealProcessor: 15 + SetThreadIdealProcessor: 16 + GetCurrentProcessorNumber: 17 + Run: 18 + CreateMutex: 19 + ReleaseMutex: 20 + CreateSemaphore: 21 + ReleaseSemaphore: 22 + CreateEvent: 23 + SignalEvent: 24 + ClearEvent: 25 + CreateTimer: 26 + SetTimer: 27 + CancelTimer: 28 + ClearTimer: 29 + CreateMemoryBlock: 30 + MapMemoryBlock: 31 + UnmapMemoryBlock: 32 + CreateAddressArbiter: 33 + ArbitrateAddress: 34 + CloseHandle: 35 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + SignalAndWait: 38 + DuplicateHandle: 39 + GetSystemTick: 40 + GetHandleInfo: 41 + GetSystemInfo: 42 + GetProcessInfo: 43 + GetThreadInfo: 44 + ConnectToPort: 45 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + OpenProcess: 51 + OpenThread: 52 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetThreadId: 55 + GetResourceLimit: 56 + GetResourceLimitLimitValues: 57 + GetResourceLimitCurrentValues: 58 + GetThreadContext: 59 + Break: 60 + OutputDebugString: 61 + ControlPerformanceCounter: 62 + CreatePort: 71 + CreateSessionToPort: 72 + CreateSession: 73 + AcceptSession: 74 + ReplyAndReceive1: 75 + ReplyAndReceive2: 76 + ReplyAndReceive3: 77 + ReplyAndReceive4: 78 + ReplyAndReceive: 79 + BindInterrupt: 80 + UnbindInterrupt: 81 + InvalidateProcessDataCache: 82 + StoreProcessDataCache: 83 + FlushProcessDataCache: 84 + StartInterProcessDma: 85 + StopDma: 86 + GetDmaState: 87 + RestartDma: 88 + DebugActiveProcess: 96 + BreakDebugProcess: 97 + TerminateDebugProcess: 98 + GetProcessDebugEvent: 99 + ContinueDebugEvent: 100 + GetProcessList: 101 + GetThreadList: 102 + GetDebugThreadContext: 103 + SetDebugThreadContext: 104 + QueryDebugProcessMemory: 105 + ReadProcessMemory: 106 + WriteProcessMemory: 107 + SetHardwareBreakPoint: 108 + GetDebugThreadParam: 109 + ControlProcessMemory: 112 + MapProcessMemory: 113 + UnmapProcessMemory: 114 + CreateCodeSet: 115 + CreateProcess: 117 + TerminateProcess: 118 + SetProcessResourceLimits: 119 + CreateResourceLimit: 120 + SetResourceLimitValues: 121 + AddCodeSegment: 122 + Backdoor: 123 + KernelSetState: 124 + QueryProcessMemory: 125 + + # Service List + # Maximum 34 services (32 if firmware is prior to 9.6.0) + ServiceAccessControl: + - APT:U + - ac:u + - am:net + - boss:U + - cam:u + - cecd:u + - cfg:nor + - cfg:u + - csnd:SND + - dsp::DSP + - frd:u + - fs:USER + - gsp::Gpu + - gsp::Lcd + - hid:USER + #- http:C + - ir:rst + - ir:u + - ir:USER + #- mic:u + - mcu::HWC + - ndm:u + - news:s + - nwm::EXT + - nwm::UDS + - ptm:sysm + - ptm:u + - pxi:dev + - soc:U + - ssl:C + - y2r:u + + +SystemControlInfo: + SaveDataSize: 0KB # Change if the app uses savedata + RemasterVersion: $(APP_VERSION_MAJOR) + StackSize: 0x40000 + + # Modules that run services listed above should be included below + # Maximum 48 dependencies + # : + Dependency: + ac: 0x0004013000002402 + #act: 0x0004013000003802 + am: 0x0004013000001502 + boss: 0x0004013000003402 + camera: 0x0004013000001602 + cecd: 0x0004013000002602 + cfg: 0x0004013000001702 + codec: 0x0004013000001802 + csnd: 0x0004013000002702 + dlp: 0x0004013000002802 + dsp: 0x0004013000001a02 + friends: 0x0004013000003202 + gpio: 0x0004013000001b02 + gsp: 0x0004013000001c02 + hid: 0x0004013000001d02 + #http: 0x0004013000002902 + i2c: 0x0004013000001e02 + ir: 0x0004013000003302 + mcu: 0x0004013000001f02 + #mic: 0x0004013000002002 + ndm: 0x0004013000002b02 + news: 0x0004013000003502 + #nfc: 0x0004013000004002 + nim: 0x0004013000002c02 + nwm: 0x0004013000002d02 + pdn: 0x0004013000002102 + ps: 0x0004013000003102 + ptm: 0x0004013000002202 + #qtm: 0x0004013020004202 + ro: 0x0004013000003702 + socket: 0x0004013000002e02 + spi: 0x0004013000002302 + ssl: 0x0004013000002f02 diff --git a/LEGO1/lego/legoomni/include/mxtransitionmanager.h b/LEGO1/lego/legoomni/include/mxtransitionmanager.h index 7c31daf7..dd9dc9ed 100644 --- a/LEGO1/lego/legoomni/include/mxtransitionmanager.h +++ b/LEGO1/lego/legoomni/include/mxtransitionmanager.h @@ -48,8 +48,7 @@ class MxTransitionManager : public MxCore { e_mosaic, e_wipeDown, e_windows, - e_broken, // Unknown what this is supposed to be, it locks the game up - e_fakeMosaic + e_broken // Unknown what this is supposed to be, it locks the game up }; MxResult StartTransition(TransitionType p_animationType, MxS32 p_speed, MxBool p_doCopy, MxBool p_playMusicInAnim); @@ -69,7 +68,6 @@ class MxTransitionManager : public MxCore { void WipeDownTransition(); void WindowsTransition(); void BrokenTransition(); - void FakeMosaicTransition(); void SubmitCopyRect(LPDDSURFACEDESC p_ddsc); void SetupCopyRect(LPDDSURFACEDESC p_ddsc); diff --git a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp index 04805092..7431033e 100644 --- a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp @@ -84,9 +84,6 @@ MxResult MxTransitionManager::Tickle() case e_broken: BrokenTransition(); break; - case e_fakeMosaic: - FakeMosaicTransition(); - break; } return SUCCESS; } @@ -664,166 +661,3 @@ void MxTransitionManager::configureMxTransitionManager(TransitionType p_transiti { g_transitionManagerConfig = p_transitionManagerConfig; } - -int g_colorOffset; -int GetColorIndexWithLocality(int p_col, int p_row) -{ - int islandX = p_col / 8; - int islandY = p_row / 8; // Dvide screen in 8x6 tiles - - int island = islandY * 8 + islandX; // tile id - - if (SDL_rand(3) > island / 8) { - return 6 + SDL_rand(2); // emulate sky - } - - if (SDL_rand(16) > 2) { - island += SDL_rand(3) - 1 + (SDL_rand(3) - 1) * 8; // blure tiles - } - - int hash = (island + g_colorOffset) * 2654435761u; - int scrambled = (hash >> 16) % 32; - - int finalIndex = scrambled + SDL_rand(3) - 1; - return abs(finalIndex) % 32; -} - -void MxTransitionManager::FakeMosaicTransition() -{ - static LPDIRECTDRAWSURFACE g_fakeTranstionSurface = nullptr; - - if (m_animationTimer == 16) { - m_animationTimer = 0; - if (g_fakeTranstionSurface) { - g_fakeTranstionSurface->Release(); - g_fakeTranstionSurface = nullptr; - } - EndTransition(TRUE); - return; - } - else { - if (m_animationTimer == 0) { - g_colorOffset = SDL_rand(32); - - // Same init/shuffle steps as the dissolve transition, except that - // we are using big blocky pixels and only need 64 columns. - MxS32 i; - for (i = 0; i < 64; i++) { - m_columnOrder[i] = i; - } - - for (i = 0; i < 64; i++) { - MxS32 swap = SDL_rand(64); - MxU16 t = m_columnOrder[i]; - m_columnOrder[i] = m_columnOrder[swap]; - m_columnOrder[swap] = t; - } - - // The same is true here. We only need 48 rows. - for (i = 0; i < 48; i++) { - m_randomShift[i] = SDL_rand(64); - } - DDSURFACEDESC mainDesc = {}; - mainDesc.dwSize = sizeof(mainDesc); - if (m_ddSurface->GetSurfaceDesc(&mainDesc) != DD_OK) { - return; - } - - DDSURFACEDESC tempDesc = {}; - tempDesc.dwSize = sizeof(tempDesc); - tempDesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; - tempDesc.dwWidth = 64; - tempDesc.dwHeight = 48; - tempDesc.ddpfPixelFormat = mainDesc.ddpfPixelFormat; - tempDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; - - if (MVideoManager()->GetDirectDraw()->CreateSurface(&tempDesc, &g_fakeTranstionSurface, nullptr) != DD_OK) { - return; - } - - DWORD fillColor = 0x00000000; - switch (mainDesc.ddpfPixelFormat.dwRGBBitCount) { - case 8: - fillColor = 0x10; - break; - case 16: - fillColor = RGB555_CREATE(0x1f, 0, 0x1f); - break; - } - - DDBLTFX bltFx = {}; - bltFx.dwSize = sizeof(bltFx); - bltFx.dwFillColor = fillColor; - g_fakeTranstionSurface->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &bltFx); - - DDCOLORKEY key = {}; - key.dwColorSpaceLowValue = key.dwColorSpaceHighValue = fillColor; - g_fakeTranstionSurface->SetColorKey(DDCKEY_SRCBLT, &key); - } - - // Run one tick of the animation - DDSURFACEDESC ddsd; - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - - HRESULT res = g_fakeTranstionSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); - if (res == DDERR_SURFACELOST) { - g_fakeTranstionSurface->Restore(); - res = g_fakeTranstionSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); - } - - if (res == DD_OK) { - SubmitCopyRect(&ddsd); - - static const MxU8 g_palette[32][3] = { - {0x00, 0x00, 0x00}, {0x12, 0x1e, 0x50}, {0x00, 0x22, 0x6c}, {0x14, 0x2d, 0x9f}, {0x0e, 0x36, 0xb0}, - {0x0e, 0x39, 0xd0}, {0x47, 0x96, 0xe2}, {0x79, 0xaa, 0xca}, {0xff, 0xff, 0xff}, {0xc9, 0xcd, 0xcb}, - {0xad, 0xad, 0xab}, {0xa6, 0x91, 0x8e}, {0xaf, 0x59, 0x49}, {0xc0, 0x00, 0x00}, {0xab, 0x18, 0x18}, - {0x61, 0x0c, 0x0c}, {0x04, 0x38, 0x12}, {0x2c, 0x67, 0x28}, {0x4a, 0xb4, 0x6b}, {0x94, 0xb7, 0x7c}, - {0xb6, 0xb9, 0x87}, {0x52, 0x4a, 0x67}, {0x87, 0x8d, 0x8a}, {0xa6, 0x91, 0x8e}, {0xf8, 0xee, 0xdc}, - {0xf4, 0xe2, 0xc3}, {0x87, 0x8d, 0x8a}, {0xba, 0x9f, 0x12}, {0xb5, 0x83, 0x00}, {0x6a, 0x44, 0x27}, - {0x36, 0x37, 0x34}, {0x2b, 0x23, 0x0f} - }; - - MxS32 bytesPerPixel = ddsd.ddpfPixelFormat.dwRGBBitCount / 8; - - for (MxS32 col = 0; col < 64; col++) { - // Select 4 columns on each tick - if (m_animationTimer * 4 > m_columnOrder[col]) { - continue; - } - - if (m_animationTimer * 4 + 3 < m_columnOrder[col]) { - continue; - } - - for (MxS32 row = 0; row < 48; row++) { - MxS32 x = (m_randomShift[row] + col) % 64; - MxU8* dest = (MxU8*) ddsd.lpSurface + row * ddsd.lPitch + x * bytesPerPixel; - - const MxU8 paletteIndex = GetColorIndexWithLocality(col, row); - const MxU8* color = g_palette[paletteIndex]; - switch (bytesPerPixel) { - case 1: - *dest = paletteIndex; - break; - case 2: - *((MxU16*) dest) = RGB555_CREATE(color[2], color[1], color[0]); - break; - default: - *((MxU32*) dest) = RGB8888_CREATE(color[2], color[1], color[0], 255); - break; - } - } - } - - SetupCopyRect(&ddsd); - g_fakeTranstionSurface->Unlock(ddsd.lpSurface); - - RECT srcRect = {0, 0, 64, 48}; - m_ddSurface->Blt(&g_fullScreenRect, g_fakeTranstionSurface, &srcRect, DDBLT_WAIT | DDBLT_KEYSRC, NULL); - - m_animationTimer++; - } - } -} diff --git a/LEGO1/omni/src/video/mxdisplaysurface.cpp b/LEGO1/omni/src/video/mxdisplaysurface.cpp index b36cbd2a..2902c4fe 100644 --- a/LEGO1/omni/src/video/mxdisplaysurface.cpp +++ b/LEGO1/omni/src/video/mxdisplaysurface.cpp @@ -827,6 +827,7 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::VTable0x44( ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; ddsd.dwWidth = p_bitmap->GetBmiWidth(); ddsd.dwHeight = p_bitmap->GetBmiHeightAbs(); + ddsd.ddpfPixelFormat = m_surfaceDesc.ddpfPixelFormat; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; *p_ret = 0; ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; diff --git a/miniwin/include/miniwin/ddraw.h b/miniwin/include/miniwin/ddraw.h index 0250c099..912a1b9f 100644 --- a/miniwin/include/miniwin/ddraw.h +++ b/miniwin/include/miniwin/ddraw.h @@ -143,9 +143,12 @@ enum class DDBltFlags : uint32_t { }; ENABLE_BITMASK_OPERATORS(DDBltFlags) +#define DDPF_ALPHAPIXELS DDPixelFormatFlags::ALPHAPIXELS #define DDPF_PALETTEINDEXED8 DDPixelFormatFlags::PALETTEINDEXED8 #define DDPF_RGB DDPixelFormatFlags::RGB +#define DDPF_ALPHAPIXELS DDPixelFormatFlags::ALPHAPIXELS enum class DDPixelFormatFlags : uint32_t { + ALPHAPIXELS = 1 << 0, // dwRGBAlphaBitMask is valid PALETTEINDEXED8 = 1 << 5, // The texture uses an 8 bit palette RGB = 1 << 6, // dwRGBBitCount, dwRBitMask, dwGBitMask, and dwBBitMask is valid }; diff --git a/miniwin/src/d3drm/backends/opengles2/renderer.cpp b/miniwin/src/d3drm/backends/opengles2/renderer.cpp index ebcb38e6..50e43d02 100644 --- a/miniwin/src/d3drm/backends/opengles2/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengles2/renderer.cpp @@ -237,14 +237,79 @@ GLES2MeshCacheEntry GLES2UploadMesh(const MeshGroup& meshGroup, bool forceUV = f return cache; } +bool UploadTexture(SDL_Surface* source, GLuint& outTexId, bool isUI) +{ + SDL_Surface* surf = source; + if (source->format != SDL_PIXELFORMAT_RGBA32) { + surf = SDL_ConvertSurface(source, SDL_PIXELFORMAT_RGBA32); + if (!surf) { + return false; + } + } + + glGenTextures(1, &outTexId); + glBindTexture(GL_TEXTURE_2D, outTexId); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + + if (isUI) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (strstr((const char*) glGetString(GL_EXTENSIONS), "GL_EXT_texture_filter_anisotropic")) { + GLfloat maxAniso = 0.0f; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso); + GLfloat desiredAniso = fminf(8.0f, maxAniso); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, desiredAniso); + } + glGenerateMipmap(GL_TEXTURE_2D); + } + + if (surf != source) { + SDL_DestroySurface(surf); + } + + return true; +} + OpenGLES2Renderer::OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext context, GLuint shaderProgram) : m_context(context), m_shaderProgram(shaderProgram) { + glGenFramebuffers(1, &m_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + m_virtualWidth = width; m_virtualHeight = height; ViewportTransform viewportTransform = {1.0f, 0.0f, 0.0f}; Resize(width, height, viewportTransform); + SDL_Surface* dummySurface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA32); + if (!dummySurface) { + SDL_Log("Failed to create surface: %s", SDL_GetError()); + return; + } + if (!SDL_LockSurface(dummySurface)) { + SDL_Log("Failed to lock surface: %s", SDL_GetError()); + SDL_DestroySurface(dummySurface); + return; + } + ((Uint32*) dummySurface->pixels)[0] = 0xFFFFFFFF; + SDL_UnlockSurface(dummySurface); + + UploadTexture(dummySurface, m_dummyTexture, false); + if (!m_dummyTexture) { + SDL_DestroySurface(dummySurface); + SDL_Log("Failed to create surface: %s", SDL_GetError()); + return; + } + SDL_DestroySurface(dummySurface); + m_uiMesh.vertices = { {{0.0f, 0.0f, 0.0f}, {0, 0, -1}, {0.0f, 0.0f}}, {{1.0f, 0.0f, 0.0f}, {0, 0, -1}, {1.0f, 0.0f}}, @@ -270,6 +335,8 @@ OpenGLES2Renderer::OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext co m_modelViewMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_modelViewMatrix"); m_normalMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_normalMatrix"); m_projectionMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_projectionMatrix"); + + glUseProgram(m_shaderProgram); } OpenGLES2Renderer::~OpenGLES2Renderer() @@ -321,47 +388,6 @@ void OpenGLES2Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* ); } -bool UploadTexture(SDL_Surface* source, GLuint& outTexId, bool isUI) -{ - SDL_Surface* surf = source; - if (source->format != SDL_PIXELFORMAT_RGBA32) { - surf = SDL_ConvertSurface(source, SDL_PIXELFORMAT_RGBA32); - if (!surf) { - return false; - } - } - - glGenTextures(1, &outTexId); - glBindTexture(GL_TEXTURE_2D, outTexId); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); - - if (isUI) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (strstr((const char*) glGetString(GL_EXTENSIONS), "GL_EXT_texture_filter_anisotropic")) { - GLfloat maxAniso = 0.0f; - glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso); - GLfloat desiredAniso = fminf(8.0f, maxAniso); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, desiredAniso); - } - glGenerateMipmap(GL_TEXTURE_2D); - } - - if (surf != source) { - SDL_DestroySurface(surf); - } - - return true; -} - Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUI, float scaleX, float scaleY) { auto texture = static_cast(iTexture); @@ -460,13 +486,13 @@ HRESULT OpenGLES2Renderer::BeginFrame() { m_dirty = true; + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + glEnable(GL_CULL_FACE); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); - glUseProgram(m_shaderProgram); - SceneLightGLES2 lightData[3]; int lightCount = std::min(static_cast(m_lights.size()), 3); @@ -537,6 +563,9 @@ void OpenGLES2Renderer::SubmitDraw( } else { glUniform1i(m_useTextureLoc, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_dummyTexture); + glUniform1i(m_textureLoc, 0); } glBindBuffer(GL_ARRAY_BUFFER, mesh.vboPositions); @@ -564,7 +593,6 @@ HRESULT OpenGLES2Renderer::FinalizeFrame() { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glUseProgram(0); return DD_OK; } @@ -578,12 +606,43 @@ void OpenGLES2Renderer::Resize(int width, int height, const ViewportTransform& v SDL_DestroySurface(m_renderedImage); } m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + // Create color texture + glGenTextures(1, &m_colorTarget); + glBindTexture(GL_TEXTURE_2D, m_colorTarget); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTarget, 0); + + // Create depth renderbuffer + glGenRenderbuffers(1, &m_depthTarget); + glBindRenderbuffer(GL_RENDERBUFFER, m_depthTarget); + + if (SDL_GL_ExtensionSupported("GL_OES_depth24")) { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, width, height); + } + else if (SDL_GL_ExtensionSupported("GL_OES_depth32")) { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32_OES, width, height); + } + else { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); + } + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthTarget); + glViewport(0, 0, m_width, m_height); } void OpenGLES2Renderer::Clear(float r, float g, float b) { m_dirty = true; + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glClearColor(r, g, b, 1.0f); @@ -592,21 +651,76 @@ void OpenGLES2Renderer::Clear(float r, float g, float b) void OpenGLES2Renderer::Flip() { - if (m_dirty) { - SDL_GL_SwapWindow(DDWindow); - m_dirty = false; + if (!m_dirty) { + return; } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glDisable(GL_DEPTH_TEST); + glFrontFace(GL_CCW); + glDepthMask(GL_FALSE); + + glUniform4f(m_colorLoc, 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1f(m_shinLoc, 0.0f); + + float ambient[] = {1.0f, 1.0f, 1.0f, 1.0f}; + float blank[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glUniform4fv(u_lightLocs[0][0], 1, ambient); + glUniform4fv(u_lightLocs[0][1], 1, blank); + glUniform4fv(u_lightLocs[0][2], 1, blank); + glUniform1i(m_lightCountLoc, 1); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_colorTarget); + glUniform1i(m_textureLoc, 0); + glUniform1i(m_useTextureLoc, 1); + + D3DRMMATRIX4D projection; + D3DRMMATRIX4D modelViewMatrix = { + {(float) m_width, 0.0f, 0.0f, 0.0f}, + {0.0f, (float) -m_height, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, (float) m_height, 0.0f, 1.0f} + }; + glUniformMatrix4fv(m_modelViewMatrixLoc, 1, GL_FALSE, &modelViewMatrix[0][0]); + Matrix3x3 identity = {{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}}; + glUniformMatrix3fv(m_normalMatrixLoc, 1, GL_FALSE, &identity[0][0]); + CreateOrthographicProjection((float) m_width, (float) m_height, projection); + glUniformMatrix4fv(m_projectionMatrixLoc, 1, GL_FALSE, &projection[0][0]); + + glDisable(GL_SCISSOR_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboPositions); + glEnableVertexAttribArray(m_posLoc); + glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboTexcoords); + glEnableVertexAttribArray(m_texLoc); + glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_uiMeshCache.ibo); + glDrawElements(GL_TRIANGLES, static_cast(m_uiMeshCache.indices.size()), GL_UNSIGNED_SHORT, nullptr); + + glDisableVertexAttribArray(m_texLoc); + + SDL_GL_SwapWindow(DDWindow); + glFrontFace(GL_CW); + m_dirty = false; } void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect, FColor color) { m_dirty = true; + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); - glUseProgram(m_shaderProgram); - float ambient[] = {1.0f, 1.0f, 1.0f, 1.0f}; float blank[] = {0.0f, 0.0f, 0.0f, 0.0f}; glUniform4fv(u_lightLocs[0][0], 1, ambient); @@ -629,14 +743,17 @@ void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, c static_cast(std::round(texture.height * scaleY)) }; - glActiveTexture(GL_TEXTURE0); glUniform1i(m_useTextureLoc, 1); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture.glTextureId); glUniform1i(m_textureLoc, 0); } else { expandedDstRect = dstRect; glUniform1i(m_useTextureLoc, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_dummyTexture); + glUniform1i(m_textureLoc, 0); } D3DRMMATRIX4D modelView, projection; @@ -685,6 +802,8 @@ void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, c void OpenGLES2Renderer::Download(SDL_Surface* target) { glFinish(); + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); SDL_Rect srcRect = { diff --git a/miniwin/src/d3drm/d3drmdevice.cpp b/miniwin/src/d3drm/d3drmdevice.cpp index 8e001748..9f517426 100644 --- a/miniwin/src/d3drm/d3drmdevice.cpp +++ b/miniwin/src/d3drm/d3drmdevice.cpp @@ -157,6 +157,7 @@ void Direct3DRMDevice2Impl::Resize() #endif m_viewportTransform = CalculateViewportTransform(m_virtualWidth, m_virtualHeight, m_windowWidth, m_windowHeight); m_renderer->Resize(m_windowWidth, m_windowHeight, m_viewportTransform); + m_renderer->Clear(0, 0, 0); for (int i = 0; i < m_viewports->GetSize(); i++) { IDirect3DRMViewport* viewport; m_viewports->GetElement(i, &viewport); diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index 410e38d0..9c5c928d 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -113,13 +113,17 @@ HRESULT DirectDrawImpl::CreateSurface( #endif if ((lpDDSurfaceDesc->dwFlags & DDSD_PIXELFORMAT) == DDSD_PIXELFORMAT) { if ((lpDDSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_RGB) == DDPF_RGB) { - switch (lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount) { - case 8: - format = SDL_PIXELFORMAT_INDEX8; - break; - case 16: - format = SDL_PIXELFORMAT_RGB565; - break; + int bpp = lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount; + Uint32 rMask = lpDDSurfaceDesc->ddpfPixelFormat.dwRBitMask; + Uint32 gMask = lpDDSurfaceDesc->ddpfPixelFormat.dwGBitMask; + Uint32 bMask = lpDDSurfaceDesc->ddpfPixelFormat.dwBBitMask; + Uint32 aMask = (lpDDSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) == DDPF_ALPHAPIXELS + ? lpDDSurfaceDesc->ddpfPixelFormat.dwRGBAlphaBitMask + : 0; + + format = SDL_GetPixelFormatForMasks(bpp, rMask, gMask, bMask, aMask); + if (format == SDL_PIXELFORMAT_UNKNOWN) { + return DDERR_INVALIDPIXELFORMAT; } } } @@ -175,7 +179,7 @@ HRESULT DirectDrawImpl::EnumDisplayModes( ddsd.dwWidth = modes[i]->w; ddsd.dwHeight = modes[i]->h; ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; + ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; ddsd.ddpfPixelFormat.dwRGBBitCount = details->bits_per_pixel; if (details->bits_per_pixel == 8) { ddsd.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED8; @@ -284,7 +288,7 @@ HRESULT DirectDrawImpl::GetDisplayMode(LPDDSURFACEDESC lpDDSurfaceDesc) lpDDSurfaceDesc->dwWidth = mode->w; lpDDSurfaceDesc->dwHeight = mode->h; lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount = details->bits_per_pixel; - lpDDSurfaceDesc->ddpfPixelFormat.dwFlags = DDPF_RGB; + lpDDSurfaceDesc->ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; if (details->bits_per_pixel == 8) { lpDDSurfaceDesc->ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED8; } @@ -324,11 +328,11 @@ HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags) return DDERR_INVALIDPARAMS; } - if (!SDL_SetWindowFullscreen(sdlWindow, fullscreen)) { #ifndef __EMSCRIPTEN__ + if (!SDL_SetWindowFullscreen(sdlWindow, fullscreen)) { return DDERR_GENERIC; -#endif } +#endif DDWindow = sdlWindow; } return DD_OK; diff --git a/miniwin/src/ddraw/ddsurface.cpp b/miniwin/src/ddraw/ddsurface.cpp index 056912fd..9e710596 100644 --- a/miniwin/src/ddraw/ddsurface.cpp +++ b/miniwin/src/ddraw/ddsurface.cpp @@ -145,7 +145,7 @@ HRESULT DirectDrawSurfaceImpl::GetPalette(LPDIRECTDRAWPALETTE* lplpDDPalette) HRESULT DirectDrawSurfaceImpl::GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat) { memset(lpDDPixelFormat, 0, sizeof(*lpDDPixelFormat)); - lpDDPixelFormat->dwFlags = DDPF_RGB; + lpDDPixelFormat->dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(m_surface->format); if (details->bits_per_pixel == 8) { lpDDPixelFormat->dwFlags |= DDPF_PALETTEINDEXED8; diff --git a/miniwin/src/ddraw/framebuffer.cpp b/miniwin/src/ddraw/framebuffer.cpp index 13caa434..d3d8a3b8 100644 --- a/miniwin/src/ddraw/framebuffer.cpp +++ b/miniwin/src/ddraw/framebuffer.cpp @@ -143,7 +143,7 @@ HRESULT FrameBufferImpl::GetPalette(LPDIRECTDRAWPALETTE* lplpDDPalette) HRESULT FrameBufferImpl::GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat) { memset(lpDDPixelFormat, 0, sizeof(*lpDDPixelFormat)); - lpDDPixelFormat->dwFlags = DDPF_RGB; + lpDDPixelFormat->dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(m_transferBuffer->m_surface->format); if (details->bits_per_pixel == 8) { lpDDPixelFormat->dwFlags |= DDPF_PALETTEINDEXED8; diff --git a/miniwin/src/internal/d3drmrenderer_opengles2.h b/miniwin/src/internal/d3drmrenderer_opengles2.h index 0472ff80..d4985f32 100644 --- a/miniwin/src/internal/d3drmrenderer_opengles2.h +++ b/miniwin/src/internal/d3drmrenderer_opengles2.h @@ -72,7 +72,11 @@ class OpenGLES2Renderer : public Direct3DRMRenderer { bool m_dirty = false; std::vector m_lights; SDL_GLContext m_context; + GLuint m_fbo; + GLuint m_colorTarget; + GLuint m_depthTarget; GLuint m_shaderProgram; + GLuint m_dummyTexture; GLint m_posLoc; GLint m_normLoc; GLint m_texLoc; diff --git a/miniwin/src/internal/d3drmrenderer_sdl3gpu.h b/miniwin/src/internal/d3drmrenderer_sdl3gpu.h index 7f948a37..1e528232 100644 --- a/miniwin/src/internal/d3drmrenderer_sdl3gpu.h +++ b/miniwin/src/internal/d3drmrenderer_sdl3gpu.h @@ -120,11 +120,19 @@ class Direct3DRMSDL3GPURenderer : public Direct3DRMRenderer { inline static void Direct3DRMSDL3GPU_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { +#ifdef __APPLE__ + SDL_GPUDevice* device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_MSL, false, nullptr); + if (!device) { + return; + } + SDL_DestroyGPUDevice(device); +#else Direct3DRMRenderer* device = Direct3DRMSDL3GPURenderer::Create(640, 480); if (!device) { return; } delete device; +#endif D3DDEVICEDESC halDesc = {}; halDesc.dcmColorModel = D3DCOLOR_RGB; diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 3ec9d174..569a6d65 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -24,3 +24,8 @@ endif() if(WINDOWS_STORE) add_subdirectory(UWP) endif() + +if(APPLE AND NOT IOS) + add_subdirectory(macos) +endif() + diff --git a/packaging/macos/CMakeLists.txt b/packaging/macos/CMakeLists.txt new file mode 100644 index 00000000..d6e93db8 --- /dev/null +++ b/packaging/macos/CMakeLists.txt @@ -0,0 +1,99 @@ +set(_icon_file AppIcon) +set(MACOSX_BUNDLE_GUI_IDENTIFIER ${APP_ID}) +set(MACOSX_BUNDLE_COPYRIGHT ${APP_SPDX}) +set(ISLE_TARGET_NAME isle) +set(MACOSX_ISLE_BUNDLE_NAME ${APP_NAME}) # Do note that it can be up to 15 characters long +set(MACOSX_ISLE_BUNDLE_DISPLAY_NAME ${APP_NAME}) +set(CONFIG_TARGET_NAME isle-config) +set(MACOSX_CONFIG_BUNDLE_NAME "Config Isle") # Do note that it can be up to 15 characters long +set(MACOSX_CONFIG_BUNDLE_DISPLAY_NAME "Configure ${APP_NAME}") +set(MACOSX_BUNDLE_INFO_STRING ${PROJECT_VERSION}) +set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) +set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) +set(MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${PROJECT_VERSION}") + +# TODO: darwin < 9 +set(MACOSX_BUNDLE_REQUIRED_PLATFORM Carbon) + +set(CPACK_DMG_VOLUME_NAME "Isle Portable") + +if(ISLE_BUILD_APP) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/isle/Info.plist.in" + "${CMAKE_CURRENT_BINARY_DIR}/isle/Info.plist" + @ONLY + ) + set(RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/isle/${_icon_file}.icns") + target_sources(${ISLE_TARGET_NAME} PRIVATE ${RESOURCE_FILES}) + set_target_properties(${ISLE_TARGET_NAME} PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_ICON_FILE "${_icon_file}.icns" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/isle/Info.plist" + RESOURCE ${RESOURCE_FILES}) + install(TARGETS ${ISLE_TARGET_NAME} DESTINATION ./) + install(CODE " + include(BundleUtilities) + fixup_bundle(${CMAKE_BINARY_DIR}/${ISLE_TARGET_NAME}.app \"\" \"\") + " + COMPONENT Runtime) + install(CODE " + execute_process(COMMAND /usr/bin/codesign + --force --deep --sign - --timestamp + \"\$\{CMAKE_INSTALL_PREFIX\}/${ISLE_TARGET_NAME}.app/Contents/MacOS/${ISLE_TARGET_NAME}\") + ") + install(CODE " + file(RENAME + \"\$\{CMAKE_INSTALL_PREFIX\}/${ISLE_TARGET_NAME}.app\" + \"\$\{CMAKE_INSTALL_PREFIX\}/${MACOSX_ISLE_BUNDLE_DISPLAY_NAME}.app\") + ") +endif() +if(ISLE_BUILD_CONFIG) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/config/Info.plist.in" + "${CMAKE_CURRENT_BINARY_DIR}/config/Info.plist" + @ONLY + ) + set(RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/config/${_icon_file}.icns") + target_sources(${CONFIG_TARGET_NAME} PRIVATE ${RESOURCE_FILES}) + set_target_properties(${CONFIG_TARGET_NAME} PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_ICON_FILE "${_icon_file}.icns" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/config/Info.plist" + RESOURCE ${RESOURCE_FILES}) + install(TARGETS ${CONFIG_TARGET_NAME} DESTINATION ./) + install(CODE " + include(BundleUtilities) + fixup_bundle(${CMAKE_BINARY_DIR}/${CONFIG_TARGET_NAME}.app \"\" \"\") + " + COMPONENT Runtime) + qt_generate_deploy_app_script( + TARGET ${CONFIG_TARGET_NAME} + OUTPUT_SCRIPT deploy_script + NO_COMPILER_RUNTIME + NO_TRANSLATIONS + ) + install(SCRIPT "${deploy_script}") + install(CODE " + execute_process(COMMAND /usr/bin/install_name_tool + -add_rpath \"@executable_path/../Frameworks\" + \"\$\{CMAKE_INSTALL_PREFIX\}/${CONFIG_TARGET_NAME}.app/Contents/MacOS/${CONFIG_TARGET_NAME}\") + ") + install(CODE " + execute_process(COMMAND /usr/bin/codesign + --force --deep --sign - --timestamp + \"\$\{CMAKE_INSTALL_PREFIX\}/${CONFIG_TARGET_NAME}.app/Contents/MacOS/${CONFIG_TARGET_NAME}\") + ") + install(CODE " + file(RENAME + \"\$\{CMAKE_INSTALL_PREFIX\}/${CONFIG_TARGET_NAME}.app\" + \"\$\{CMAKE_INSTALL_PREFIX\}/${MACOSX_CONFIG_BUNDLE_DISPLAY_NAME}.app\") + ") +endif() + +install(CODE " + if(IS_DIRECTORY \"\$\{CMAKE_INSTALL_PREFIX\}/bin\" OR IS_DIRECTORY \"\$\{CMAKE_INSTALL_PREFIX\}/lib\" OR EXISTS \"\$\{CMAKE_INSTALL_PREFIX\}/AppIcon.icns\") + execute_process(COMMAND /bin/rm + -rf \"\$\{CMAKE_INSTALL_PREFIX\}/bin\" \"\$\{CMAKE_INSTALL_PREFIX\}/lib\" \"\$\{CMAKE_INSTALL_PREFIX\}/AppIcon.icns\" + ) + endif() +") diff --git a/packaging/macos/config/AppIcon.icns b/packaging/macos/config/AppIcon.icns new file mode 100644 index 00000000..5b67c48a Binary files /dev/null and b/packaging/macos/config/AppIcon.icns differ diff --git a/packaging/macos/config/Info.plist.in b/packaging/macos/config/Info.plist.in new file mode 100644 index 00000000..d128adc0 --- /dev/null +++ b/packaging/macos/config/Info.plist.in @@ -0,0 +1,80 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + @MACOSX_BUNDLE_INFO_STRING@ + CFBundleIconFile + @MACOSX_BUNDLE_ICON_FILE@ + CFBundleIdentifier + @MACOSX_BUNDLE_GUI_IDENTIFIER@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + @MACOSX_BUNDLE_LONG_VERSION_STRING@ + CFBundleName + @MACOSX_CONFIG_BUNDLE_NAME@ + CFBundleDisplayName + @MACOSX_CONFIG_BUNDLE_DISPLAY_NAME@ + CFBundlePackageType + APPL + CFBundleShortVersionString + @MACOSX_BUNDLE_SHORT_VERSION_STRING@ + CFBundleSignature + ???? + CFBundleVersion + @MACOSX_BUNDLE_BUNDLE_VERSION@ + UILaunchStoryboardName + LaunchScreen + NSHighResolutionCapable + + CSResourcesFileMapped + + LSRequires@MACOSX_BUNDLE_REQUIRED_PLATFORM@ + + NSHumanReadableCopyright + @MACOSX_BUNDLE_COPYRIGHT@ + SDL_FILESYSTEM_BASE_DIR_TYPE + resource + NSSupportsAutomaticGraphicsSwitching + + UIApplicationSupportsIndirectInputEvents + + LSSupportsOpeningDocumentsInPlace + + UIFileSharingEnabled + + CADisableMinimumFrameDurationOnPhone + + UIDeviceFamily + + 1 + 2 + + UIRequiresFullScreen + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + CFBundleAllowMixedLocalizations + + + \ No newline at end of file diff --git a/packaging/macos/isle/AppIcon.icns b/packaging/macos/isle/AppIcon.icns new file mode 100644 index 00000000..61aa735e Binary files /dev/null and b/packaging/macos/isle/AppIcon.icns differ diff --git a/packaging/macos/isle/Info.plist.in b/packaging/macos/isle/Info.plist.in new file mode 100644 index 00000000..2ca3d26e --- /dev/null +++ b/packaging/macos/isle/Info.plist.in @@ -0,0 +1,82 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + @MACOSX_BUNDLE_INFO_STRING@ + CFBundleIconFile + @MACOSX_BUNDLE_ICON_FILE@ + CFBundleIdentifier + @MACOSX_BUNDLE_GUI_IDENTIFIER@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + @MACOSX_BUNDLE_LONG_VERSION_STRING@ + CFBundleName + @MACOSX_ISLE_BUNDLE_NAME@ + CFBundleDisplayName + @MACOSX_ISLE_BUNDLE_DISPLAY_NAME@ + CFBundlePackageType + APPL + CFBundleShortVersionString + @MACOSX_BUNDLE_SHORT_VERSION_STRING@ + CFBundleSignature + ???? + CFBundleVersion + @MACOSX_BUNDLE_BUNDLE_VERSION@ + UILaunchStoryboardName + LaunchScreen + NSHighResolutionCapable + + CSResourcesFileMapped + + LSRequires@MACOSX_BUNDLE_REQUIRED_PLATFORM@ + + NSHumanReadableCopyright + @MACOSX_BUNDLE_COPYRIGHT@ + SDL_FILESYSTEM_BASE_DIR_TYPE + resource + NSSupportsAutomaticGraphicsSwitching + + UIApplicationSupportsIndirectInputEvents + + LSSupportsOpeningDocumentsInPlace + + UIFileSharingEnabled + + CADisableMinimumFrameDurationOnPhone + + UIDeviceFamily + + 1 + 2 + + UIRequiresFullScreen + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + CFBundleAllowMixedLocalizations + + LSApplicationCategoryType + public.app-category.games + + \ No newline at end of file