Repository: CommitteeOfZero/impacto Branch: master Commit: 27048056eb0b Files: 1240 Total size: 7.0 MB Directory structure: gitextract__xh7dyft/ ├── .clang-format ├── .gitattributes ├── .github/ │ └── workflows/ │ ├── docker-switch.yml │ ├── impacto.yml │ ├── notify.yml │ └── publish.yml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── HorizonNX.toolchain ├── LICENSE ├── README.md ├── THIRDPARTY.md ├── VERSION ├── android/ │ ├── .gitignore │ ├── .run/ │ │ └── app.run.xml │ ├── README.md │ ├── app/ │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ ├── signing/ │ │ │ ├── .gitignore │ │ │ ├── external.jks │ │ │ └── external.properties │ │ ├── signing.gradle │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ ├── com/ │ │ │ │ └── committeeofzero/ │ │ │ │ └── impacto/ │ │ │ │ └── ImpactoActivity.java │ │ │ └── org/ │ │ │ └── libsdl/ │ │ │ └── app/ │ │ │ ├── HIDDevice.java │ │ │ ├── HIDDeviceBLESteamController.java │ │ │ ├── HIDDeviceManager.java │ │ │ ├── HIDDeviceUSB.java │ │ │ ├── SDL.java │ │ │ ├── SDLActivity.java │ │ │ ├── SDLAudioManager.java │ │ │ ├── SDLControllerManager.java │ │ │ └── SDLSurface.java │ │ └── res/ │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── build.gradle │ ├── buildDebug.bat │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── installDebug.bat │ └── settings.gradle ├── build-deps.ps1 ├── doc/ │ ├── contributor_guide.md │ ├── font-lb/ │ │ ├── CompoundCharacters.tbl │ │ ├── RobonoCharset.utf8 │ │ └── font-lb.md │ ├── getting_started.md │ ├── ubuntu_build.md │ └── vs_build.md ├── docker/ │ ├── impacto-emscripten/ │ │ ├── Dockerfile │ │ ├── build_emscripten.sh │ │ └── libatrac9-emscripten.mk │ └── impacto-switch/ │ └── Dockerfile ├── games/ │ ├── cc/ │ │ └── .gitkeep │ ├── chlcc/ │ │ └── .gitkeep │ ├── rne/ │ │ └── .gitkeep │ └── sg0/ │ └── .gitkeep ├── portfiles/ │ └── avcpp/ │ ├── 0002-av_init_packet_deprecation.patch │ ├── portfile.cmake │ └── vcpkg.json ├── profiles/ │ ├── cc/ │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font-lb.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── saveicon.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ └── titlemenu.lua │ │ ├── savedata.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ ├── vfs.lua │ │ └── waveeffects.lua │ ├── cclcc/ │ │ ├── bgeff.lua │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── configsystem.lua │ │ ├── dialogue.lua │ │ ├── font-lb-italic.lua │ │ ├── font-lb.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── gamespecific.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── delusiontrigger.lua │ │ │ ├── extramenus.lua │ │ │ ├── helpmenu.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── optionsmenu.lua │ │ │ ├── saveicon.lua │ │ │ ├── savemenu.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ └── titlemenu.lua │ │ ├── mapsystem.lua │ │ ├── savedata.lua │ │ ├── scriptinput.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ ├── vfs.lua │ │ ├── waveeffects.lua │ │ └── yesnotrigger.lua │ ├── characterviewer/ │ │ ├── config.lua │ │ └── game.lua │ ├── chlcc/ │ │ ├── achievementsystem.lua │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font-lb.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── gamespecific.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── commonmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── delusiontrigger.lua │ │ │ ├── extramenus.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── optionsmenu.lua │ │ │ ├── saveicon.lua │ │ │ ├── savemenu.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ ├── titlemenu.lua │ │ │ └── trophymenu.lua │ │ ├── savedata.lua │ │ ├── scriptinput.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ ├── vfs.lua │ │ └── waveeffects.lua │ ├── chn/ │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font-lb.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── saveicon.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ └── titlemenu.lua │ │ ├── savedata.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ └── vfs.lua │ ├── common/ │ │ ├── animation.lua │ │ ├── charset.lua │ │ ├── scriptinput.lua │ │ └── scriptvars.lua │ ├── darling/ │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── saveicon.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ └── titlemenu.lua │ │ ├── savedata.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ └── vfs.lua │ ├── dash/ │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font-lb.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── gamespecific.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── saveicon.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ └── titlemenu.lua │ │ ├── savedata.lua │ │ ├── scene3d/ │ │ │ ├── characters.lua │ │ │ └── scene3d.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ └── vfs.lua │ ├── mo6tw/ │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── extramenus.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── optionsmenu.lua │ │ │ ├── saveicon.lua │ │ │ ├── savemenu.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ └── titlemenu.lua │ │ ├── savedata.lua │ │ ├── scriptinput.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ └── vfs.lua │ ├── mo7/ │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── saveicon.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── tipsmenu.lua │ │ │ └── tipsnotification.lua │ │ ├── savedata.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ └── vfs.lua │ ├── mo8/ │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font-lb.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── optionsmenu.lua │ │ │ ├── saveicon.lua │ │ │ ├── savemenu.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ └── titlemenu.lua │ │ ├── savedata.lua │ │ ├── scriptvars.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ └── vfs.lua │ ├── modelviewer/ │ │ ├── config.lua │ │ └── game.lua │ ├── modelviewer-dash/ │ │ ├── config.lua │ │ └── game.lua │ ├── rne/ │ │ ├── charset.lua │ │ ├── config.lua │ │ ├── dialogue.lua │ │ ├── font-lb.lua │ │ ├── font.lua │ │ ├── game.lua │ │ ├── gamespecific.lua │ │ ├── hud/ │ │ │ ├── backlogmenu.lua │ │ │ ├── datedisplay.lua │ │ │ ├── loadingdisplay.lua │ │ │ ├── saveicon.lua │ │ │ ├── selectiondisplay.lua │ │ │ ├── sysmesboxdisplay.lua │ │ │ ├── systemmenu.lua │ │ │ ├── tipsmenu.lua │ │ │ ├── tipsnotification.lua │ │ │ └── titlemenu.lua │ │ ├── savedata.lua │ │ ├── scene3d/ │ │ │ ├── characters.lua │ │ │ └── scene3d.lua │ │ ├── sprites.lua │ │ ├── tipssystem.lua │ │ └── vfs.lua │ └── sgps3/ │ ├── charset.lua │ ├── config.lua │ ├── dialogue.lua │ ├── font.lua │ ├── game.lua │ ├── hud/ │ │ ├── backlogmenu.lua │ │ ├── datedisplay.lua │ │ ├── loadingdisplay.lua │ │ ├── saveicon.lua │ │ ├── selectiondisplay.lua │ │ ├── sysmesboxdisplay.lua │ │ ├── systemmenu.lua │ │ ├── tipsmenu.lua │ │ ├── tipsnotification.lua │ │ └── titlemenu.lua │ ├── savedata.lua │ ├── scriptvars.lua │ ├── sprites.lua │ ├── tipssystem.lua │ └── vfs.lua ├── src/ │ ├── animation.cpp │ ├── animation.h │ ├── audio/ │ │ ├── adxaudiostream.cpp │ │ ├── adxaudiostream.h │ │ ├── atrac9audiostream.cpp │ │ ├── atrac9audiostream.h │ │ ├── audiobackend.h │ │ ├── audiochannel.cpp │ │ ├── audiochannel.h │ │ ├── audiocommon.h │ │ ├── audiostream.cpp │ │ ├── audiostream.h │ │ ├── audiosystem.cpp │ │ ├── audiosystem.h │ │ ├── buffering.h │ │ ├── ffmpegaudioplayer.h │ │ ├── hcaaudiostream.cpp │ │ ├── hcaaudiostream.h │ │ ├── openal/ │ │ │ ├── audiobackend.cpp │ │ │ ├── audiobackend.h │ │ │ ├── audiocommon.h │ │ │ ├── ffmpegaudioplayer.cpp │ │ │ ├── ffmpegaudioplayer.h │ │ │ ├── openalaudiochannel.cpp │ │ │ └── openalaudiochannel.h │ │ ├── vorbisaudiostream.cpp │ │ └── vorbisaudiostream.h │ ├── background2d.cpp │ ├── background2d.h │ ├── character2d.cpp │ ├── character2d.h │ ├── characterviewer.cpp │ ├── characterviewer.h │ ├── config.h.in │ ├── data/ │ │ ├── achievementsystem.cpp │ │ ├── achievementsystem.h │ │ ├── achievementsystemps3.cpp │ │ ├── achievementsystemps3.h │ │ ├── savesystem.cpp │ │ ├── savesystem.h │ │ ├── tipssystem.cpp │ │ └── tipssystem.h │ ├── debugmenu.cpp │ ├── debugmenu.h │ ├── effects/ │ │ ├── blur.cpp │ │ ├── blur.h │ │ ├── chlcc/ │ │ │ ├── bubbleseffect.cpp │ │ │ ├── bubbleseffect.h │ │ │ ├── butterflyeffect.cpp │ │ │ ├── butterflyeffect.h │ │ │ ├── eyecatch.cpp │ │ │ └── eyecatch.h │ │ ├── mosaic.cpp │ │ ├── mosaic.h │ │ ├── wave.cpp │ │ └── wave.h │ ├── font.h │ ├── game.cpp │ ├── game.h │ ├── games/ │ │ ├── cc/ │ │ │ ├── backlogmenu.cpp │ │ │ ├── backlogmenu.h │ │ │ ├── sysmesbox.cpp │ │ │ ├── sysmesbox.h │ │ │ ├── titlemenu.cpp │ │ │ └── titlemenu.h │ │ ├── cclcc/ │ │ │ ├── albummenu.cpp │ │ │ ├── albummenu.h │ │ │ ├── clearlistmenu.cpp │ │ │ ├── clearlistmenu.h │ │ │ ├── delusiontrigger.cpp │ │ │ ├── delusiontrigger.h │ │ │ ├── helpmenu.cpp │ │ │ ├── helpmenu.h │ │ │ ├── librarymenu.cpp │ │ │ ├── librarymenu.h │ │ │ ├── librarysubmenus.cpp │ │ │ ├── librarysubmenus.h │ │ │ ├── mapsystem.cpp │ │ │ ├── mapsystem.h │ │ │ ├── moviemenu.cpp │ │ │ ├── moviemenu.h │ │ │ ├── musicmenu.cpp │ │ │ ├── musicmenu.h │ │ │ ├── optionsmenu.cpp │ │ │ ├── optionsmenu.h │ │ │ ├── savemenu.cpp │ │ │ ├── savemenu.h │ │ │ ├── savesystem.cpp │ │ │ ├── savesystem.h │ │ │ ├── systemmenu.cpp │ │ │ ├── systemmenu.h │ │ │ ├── tipsmenu.cpp │ │ │ ├── tipsmenu.h │ │ │ ├── tipssystem.cpp │ │ │ ├── tipssystem.h │ │ │ ├── titlemenu.cpp │ │ │ ├── titlemenu.h │ │ │ ├── yesnotrigger.cpp │ │ │ └── yesnotrigger.h │ │ ├── chlcc/ │ │ │ ├── albummenu.cpp │ │ │ ├── albummenu.h │ │ │ ├── animations/ │ │ │ │ ├── menutransition.cpp │ │ │ │ ├── menutransition.h │ │ │ │ ├── saveicon.cpp │ │ │ │ ├── saveicon.h │ │ │ │ ├── selectprompt.cpp │ │ │ │ └── selectprompt.h │ │ │ ├── backlogmenu.cpp │ │ │ ├── backlogmenu.h │ │ │ ├── clearlistmenu.cpp │ │ │ ├── clearlistmenu.h │ │ │ ├── commonmenu.cpp │ │ │ ├── commonmenu.h │ │ │ ├── delusiontrigger.cpp │ │ │ ├── delusiontrigger.h │ │ │ ├── introsequence.cpp │ │ │ ├── introsequence.h │ │ │ ├── moviemenu.cpp │ │ │ ├── moviemenu.h │ │ │ ├── musicmenu.cpp │ │ │ ├── musicmenu.h │ │ │ ├── optionsmenu.cpp │ │ │ ├── optionsmenu.h │ │ │ ├── savemenu.cpp │ │ │ ├── savemenu.h │ │ │ ├── savesystem.cpp │ │ │ ├── savesystem.h │ │ │ ├── sysmesbox.cpp │ │ │ ├── sysmesbox.h │ │ │ ├── systemmenu.cpp │ │ │ ├── systemmenu.h │ │ │ ├── tipsmenu.cpp │ │ │ ├── tipsmenu.h │ │ │ ├── tipssystem.cpp │ │ │ ├── tipssystem.h │ │ │ ├── titlemenu.cpp │ │ │ ├── titlemenu.h │ │ │ ├── trophymenu.cpp │ │ │ └── trophymenu.h │ │ ├── darling/ │ │ │ ├── sysmesbox.cpp │ │ │ └── sysmesbox.h │ │ ├── dash/ │ │ │ ├── titlemenu.cpp │ │ │ └── titlemenu.h │ │ ├── mo6tw/ │ │ │ ├── actorsvoicemenu.cpp │ │ │ ├── actorsvoicemenu.h │ │ │ ├── albummenu.cpp │ │ │ ├── albummenu.h │ │ │ ├── backlogmenu.cpp │ │ │ ├── backlogmenu.h │ │ │ ├── clearlistmenu.cpp │ │ │ ├── clearlistmenu.h │ │ │ ├── moviemenu.cpp │ │ │ ├── moviemenu.h │ │ │ ├── musicmenu.cpp │ │ │ ├── musicmenu.h │ │ │ ├── optionsmenu.cpp │ │ │ ├── optionsmenu.h │ │ │ ├── savemenu.cpp │ │ │ ├── savemenu.h │ │ │ ├── savesystem.cpp │ │ │ ├── savesystem.h │ │ │ ├── sysmesbox.cpp │ │ │ ├── sysmesbox.h │ │ │ ├── systemmenu.cpp │ │ │ ├── systemmenu.h │ │ │ ├── tipsmenu.cpp │ │ │ ├── tipsmenu.h │ │ │ ├── tipssystem.cpp │ │ │ ├── tipssystem.h │ │ │ ├── titlemenu.cpp │ │ │ └── titlemenu.h │ │ ├── mo8/ │ │ │ ├── optionsmenu.cpp │ │ │ ├── optionsmenu.h │ │ │ ├── savemenu.cpp │ │ │ ├── savemenu.h │ │ │ ├── systemmenu.cpp │ │ │ ├── systemmenu.h │ │ │ ├── titlemenu.cpp │ │ │ └── titlemenu.h │ │ └── rne/ │ │ ├── sysmesbox.cpp │ │ ├── sysmesbox.h │ │ ├── systemmenu.cpp │ │ ├── systemmenu.h │ │ ├── tilebackground.cpp │ │ ├── tilebackground.h │ │ ├── titlemenu.cpp │ │ └── titlemenu.h │ ├── hud/ │ │ ├── autoicondisplay.cpp │ │ ├── autoicondisplay.h │ │ ├── cc/ │ │ │ ├── dialoguebox.cpp │ │ │ ├── dialoguebox.h │ │ │ ├── nametagdisplay.cpp │ │ │ └── nametagdisplay.h │ │ ├── cclcc/ │ │ │ ├── tipsnotification.cpp │ │ │ └── tipsnotification.h │ │ ├── chlcc/ │ │ │ ├── dialoguebox.cpp │ │ │ ├── dialoguebox.h │ │ │ ├── nametagdisplay.cpp │ │ │ ├── nametagdisplay.h │ │ │ ├── tipsnotification.cpp │ │ │ └── tipsnotification.h │ │ ├── datedisplay.cpp │ │ ├── datedisplay.h │ │ ├── defaultsaveiconanimation.cpp │ │ ├── defaultsaveiconanimation.h │ │ ├── dialoguebox.cpp │ │ ├── dialoguebox.h │ │ ├── loadingdisplay.cpp │ │ ├── loadingdisplay.h │ │ ├── mo6tw/ │ │ │ ├── dialoguebox.cpp │ │ │ ├── dialoguebox.h │ │ │ ├── tipsnotification.cpp │ │ │ └── tipsnotification.h │ │ ├── nametagdisplay.cpp │ │ ├── nametagdisplay.h │ │ ├── rne/ │ │ │ ├── datedisplay.cpp │ │ │ └── datedisplay.h │ │ ├── saveicondisplay.cpp │ │ ├── saveicondisplay.h │ │ ├── skipicondisplay.cpp │ │ ├── skipicondisplay.h │ │ ├── tipsnotification.cpp │ │ ├── tipsnotification.h │ │ ├── waiticondisplay.cpp │ │ └── waiticondisplay.h │ ├── impacto.h │ ├── inputsystem.cpp │ ├── inputsystem.h │ ├── io/ │ │ ├── afsarchive.cpp │ │ ├── afsarchive.h │ │ ├── assetpath.cpp │ │ ├── assetpath.h │ │ ├── buffering.h │ │ ├── cpkarchive.cpp │ │ ├── cpkarchive.h │ │ ├── filemeta.cpp │ │ ├── filemeta.h │ │ ├── io.h │ │ ├── lnk4archive.cpp │ │ ├── lnk4archive.h │ │ ├── lzxstream.cpp │ │ ├── lzxstream.h │ │ ├── memorymappedfilestream.cpp │ │ ├── memorymappedfilestream.h │ │ ├── memorystream.cpp │ │ ├── memorystream.h │ │ ├── mpkarchive.cpp │ │ ├── mpkarchive.h │ │ ├── physicalfilestream.cpp │ │ ├── physicalfilestream.h │ │ ├── stream.h │ │ ├── textarchive.cpp │ │ ├── textarchive.h │ │ ├── uncompressedstream.cpp │ │ ├── uncompressedstream.h │ │ ├── vfs.cpp │ │ ├── vfs.h │ │ ├── vfsarchive.cpp │ │ ├── vfsarchive.h │ │ ├── zlibstream.cpp │ │ └── zlibstream.h │ ├── loadable.h │ ├── log.cpp │ ├── log.h │ ├── main.cpp │ ├── manifest_windows.manifest │ ├── mask2d.cpp │ ├── mask2d.h │ ├── mem.cpp │ ├── mem.h │ ├── modelviewer.cpp │ ├── modelviewer.h │ ├── pch.h │ ├── profile/ │ │ ├── animations.cpp │ │ ├── animations.h │ │ ├── charset.cpp │ │ ├── charset.h │ │ ├── configsystem.cpp │ │ ├── configsystem.h │ │ ├── data/ │ │ │ ├── achievementsystem.cpp │ │ │ ├── achievementsystem.h │ │ │ ├── bgeff.cpp │ │ │ ├── bgeff.h │ │ │ ├── savesystem.cpp │ │ │ ├── savesystem.h │ │ │ ├── tipssystem.cpp │ │ │ ├── tipssystem.h │ │ │ ├── waveeffects.cpp │ │ │ └── waveeffects.h │ │ ├── dialogue.cpp │ │ ├── dialogue.h │ │ ├── fonts.cpp │ │ ├── fonts.h │ │ ├── game.cpp │ │ ├── game.h │ │ ├── games/ │ │ │ ├── cc/ │ │ │ │ ├── backlogmenu.cpp │ │ │ │ ├── backlogmenu.h │ │ │ │ ├── dialoguebox.cpp │ │ │ │ ├── dialoguebox.h │ │ │ │ ├── sysmesbox.cpp │ │ │ │ ├── sysmesbox.h │ │ │ │ ├── titlemenu.cpp │ │ │ │ └── titlemenu.h │ │ │ ├── cclcc/ │ │ │ │ ├── clearlistmenu.cpp │ │ │ │ ├── clearlistmenu.h │ │ │ │ ├── delusiontrigger.cpp │ │ │ │ ├── delusiontrigger.h │ │ │ │ ├── helpmenu.cpp │ │ │ │ ├── helpmenu.h │ │ │ │ ├── librarymenu.cpp │ │ │ │ ├── librarymenu.h │ │ │ │ ├── mapsystem.cpp │ │ │ │ ├── mapsystem.h │ │ │ │ ├── optionsmenu.cpp │ │ │ │ ├── optionsmenu.h │ │ │ │ ├── savemenu.cpp │ │ │ │ ├── savemenu.h │ │ │ │ ├── systemmenu.cpp │ │ │ │ ├── systemmenu.h │ │ │ │ ├── tipsmenu.cpp │ │ │ │ ├── tipsmenu.h │ │ │ │ ├── tipsnotification.cpp │ │ │ │ ├── tipsnotification.h │ │ │ │ ├── titlemenu.cpp │ │ │ │ ├── titlemenu.h │ │ │ │ ├── yesnotrigger.cpp │ │ │ │ └── yesnotrigger.h │ │ │ ├── chlcc/ │ │ │ │ ├── albummenu.cpp │ │ │ │ ├── albummenu.h │ │ │ │ ├── backlogmenu.cpp │ │ │ │ ├── backlogmenu.h │ │ │ │ ├── clearlistmenu.cpp │ │ │ │ ├── clearlistmenu.h │ │ │ │ ├── commonmenu.cpp │ │ │ │ ├── commonmenu.h │ │ │ │ ├── delusiontrigger.cpp │ │ │ │ ├── delusiontrigger.h │ │ │ │ ├── dialoguebox.cpp │ │ │ │ ├── dialoguebox.h │ │ │ │ ├── moviemenu.cpp │ │ │ │ ├── moviemenu.h │ │ │ │ ├── musicmenu.cpp │ │ │ │ ├── musicmenu.h │ │ │ │ ├── optionsmenu.cpp │ │ │ │ ├── optionsmenu.h │ │ │ │ ├── savemenu.cpp │ │ │ │ ├── savemenu.h │ │ │ │ ├── sysmesbox.cpp │ │ │ │ ├── sysmesbox.h │ │ │ │ ├── systemmenu.cpp │ │ │ │ ├── systemmenu.h │ │ │ │ ├── tipsmenu.cpp │ │ │ │ ├── tipsmenu.h │ │ │ │ ├── tipsnotification.cpp │ │ │ │ ├── tipsnotification.h │ │ │ │ ├── titlemenu.cpp │ │ │ │ ├── titlemenu.h │ │ │ │ ├── trophymenu.cpp │ │ │ │ └── trophymenu.h │ │ │ ├── darling/ │ │ │ │ ├── sysmesbox.cpp │ │ │ │ └── sysmesbox.h │ │ │ ├── dash/ │ │ │ │ ├── titlemenu.cpp │ │ │ │ └── titlemenu.h │ │ │ ├── mo6tw/ │ │ │ │ ├── actorsvoicemenu.cpp │ │ │ │ ├── actorsvoicemenu.h │ │ │ │ ├── albummenu.cpp │ │ │ │ ├── albummenu.h │ │ │ │ ├── backlogmenu.cpp │ │ │ │ ├── backlogmenu.h │ │ │ │ ├── clearlistmenu.cpp │ │ │ │ ├── clearlistmenu.h │ │ │ │ ├── dialoguebox.cpp │ │ │ │ ├── dialoguebox.h │ │ │ │ ├── moviemenu.cpp │ │ │ │ ├── moviemenu.h │ │ │ │ ├── musicmenu.cpp │ │ │ │ ├── musicmenu.h │ │ │ │ ├── optionsmenu.cpp │ │ │ │ ├── optionsmenu.h │ │ │ │ ├── savemenu.cpp │ │ │ │ ├── savemenu.h │ │ │ │ ├── sysmesbox.cpp │ │ │ │ ├── sysmesbox.h │ │ │ │ ├── systemmenu.cpp │ │ │ │ ├── systemmenu.h │ │ │ │ ├── tipsmenu.cpp │ │ │ │ ├── tipsmenu.h │ │ │ │ ├── tipsnotification.cpp │ │ │ │ ├── tipsnotification.h │ │ │ │ ├── titlemenu.cpp │ │ │ │ └── titlemenu.h │ │ │ ├── mo8/ │ │ │ │ ├── optionsmenu.cpp │ │ │ │ ├── optionsmenu.h │ │ │ │ ├── savemenu.cpp │ │ │ │ ├── savemenu.h │ │ │ │ ├── systemmenu.cpp │ │ │ │ ├── systemmenu.h │ │ │ │ ├── titlemenu.cpp │ │ │ │ └── titlemenu.h │ │ │ └── rne/ │ │ │ ├── sysmesbox.cpp │ │ │ ├── sysmesbox.h │ │ │ ├── systemmenu.cpp │ │ │ ├── systemmenu.h │ │ │ ├── tilebackground.cpp │ │ │ ├── tilebackground.h │ │ │ ├── titlemenu.cpp │ │ │ └── titlemenu.h │ │ ├── hud/ │ │ │ ├── datedisplay.cpp │ │ │ ├── datedisplay.h │ │ │ ├── loadingdisplay.cpp │ │ │ ├── loadingdisplay.h │ │ │ ├── saveicon.cpp │ │ │ ├── saveicon.h │ │ │ ├── tipsnotification.cpp │ │ │ └── tipsnotification.h │ │ ├── profile.cpp │ │ ├── profile.h │ │ ├── profile_internal.cpp │ │ ├── profile_internal.h │ │ ├── scene3d.cpp │ │ ├── scene3d.h │ │ ├── scriptinput.cpp │ │ ├── scriptinput.h │ │ ├── scriptvars.cpp │ │ ├── scriptvars.h │ │ ├── sprites.cpp │ │ ├── sprites.h │ │ ├── subtitle.cpp │ │ ├── subtitle.h │ │ ├── ui/ │ │ │ ├── backlogmenu.cpp │ │ │ ├── backlogmenu.h │ │ │ ├── commonmenu.cpp │ │ │ ├── commonmenu.h │ │ │ ├── extramenus.cpp │ │ │ ├── extramenus.h │ │ │ ├── gamespecific.cpp │ │ │ ├── gamespecific.h │ │ │ ├── helpmenu.cpp │ │ │ ├── helpmenu.h │ │ │ ├── mapsystem.cpp │ │ │ ├── mapsystem.h │ │ │ ├── optionsmenu.cpp │ │ │ ├── optionsmenu.h │ │ │ ├── savemenu.cpp │ │ │ ├── savemenu.h │ │ │ ├── selectionmenu.cpp │ │ │ ├── selectionmenu.h │ │ │ ├── sysmesbox.cpp │ │ │ ├── sysmesbox.h │ │ │ ├── systemmenu.cpp │ │ │ ├── systemmenu.h │ │ │ ├── tipsmenu.cpp │ │ │ ├── tipsmenu.h │ │ │ ├── titlemenu.cpp │ │ │ ├── titlemenu.h │ │ │ ├── trophymenu.cpp │ │ │ └── trophymenu.h │ │ ├── vfs.cpp │ │ ├── vfs.h │ │ ├── vm.cpp │ │ └── vm.h │ ├── renderer/ │ │ ├── 3d/ │ │ │ ├── animation.cpp │ │ │ ├── animation.h │ │ │ ├── camera.cpp │ │ │ ├── camera.h │ │ │ ├── model.cpp │ │ │ ├── model.h │ │ │ ├── modelanimator.cpp │ │ │ ├── modelanimator.h │ │ │ ├── renderable3d.h │ │ │ ├── scene.h │ │ │ ├── transform.cpp │ │ │ └── transform.h │ │ ├── dx9/ │ │ │ ├── 3d/ │ │ │ │ ├── renderable3d.cpp │ │ │ │ ├── renderable3d.h │ │ │ │ ├── scene.cpp │ │ │ │ └── scene.h │ │ │ ├── nv12frame.cpp │ │ │ ├── nv12frame.h │ │ │ ├── renderer.cpp │ │ │ ├── renderer.h │ │ │ ├── shader.cpp │ │ │ ├── shader.h │ │ │ ├── utils.h │ │ │ ├── window.cpp │ │ │ ├── window.h │ │ │ ├── yuvframe.cpp │ │ │ └── yuvframe.h │ │ ├── nv12frame.h │ │ ├── opengl/ │ │ │ ├── 3d/ │ │ │ │ ├── renderable3d.cpp │ │ │ │ ├── renderable3d.h │ │ │ │ ├── scene.cpp │ │ │ │ └── scene.h │ │ │ ├── glc.cpp │ │ │ ├── glc.h │ │ │ ├── nv12frame.cpp │ │ │ ├── nv12frame.h │ │ │ ├── renderer.cpp │ │ │ ├── renderer.h │ │ │ ├── shader.cpp │ │ │ ├── shader.h │ │ │ ├── window.cpp │ │ │ ├── window.h │ │ │ ├── yuvframe.cpp │ │ │ └── yuvframe.h │ │ ├── renderer.cpp │ │ ├── renderer.h │ │ ├── vulkan/ │ │ │ ├── 3d/ │ │ │ │ ├── renderable3d.cpp │ │ │ │ ├── renderable3d.h │ │ │ │ ├── scene.cpp │ │ │ │ └── scene.h │ │ │ ├── nv12frame.cpp │ │ │ ├── nv12frame.h │ │ │ ├── pipeline.cpp │ │ │ ├── pipeline.h │ │ │ ├── renderer.cpp │ │ │ ├── renderer.h │ │ │ ├── utils.cpp │ │ │ ├── utils.h │ │ │ ├── window.cpp │ │ │ ├── window.h │ │ │ ├── yuvframe.cpp │ │ │ └── yuvframe.h │ │ ├── window.cpp │ │ ├── window.h │ │ └── yuvframe.h │ ├── rng.h │ ├── scriptvars.h │ ├── sequencedanimation.cpp │ ├── sequencedanimation.h │ ├── shaders/ │ │ ├── dx9/ │ │ │ ├── CCMessageBoxSprite_frag.hlsl │ │ │ ├── CCMessageBoxSprite_vert.hlsl │ │ │ ├── CHLCCMenuBackground_frag.hlsl │ │ │ ├── CHLCCMenuBackground_vert.hlsl │ │ │ ├── MaskedSpriteNoAlpha_frag.hlsl │ │ │ ├── MaskedSpriteNoAlpha_vert.hlsl │ │ │ ├── MaskedSprite_frag.hlsl │ │ │ ├── MaskedSprite_vert.hlsl │ │ │ ├── NV12Frame_frag.hlsl │ │ │ ├── NV12Frame_vert.hlsl │ │ │ ├── Renderable3D_Background_frag.hlsl │ │ │ ├── Renderable3D_Background_vert.hlsl │ │ │ ├── Renderable3D_Character_frag.hlsl │ │ │ ├── Renderable3D_Character_vert.hlsl │ │ │ ├── Renderable3D_Eye_frag.hlsl │ │ │ ├── Renderable3D_Eye_vert.hlsl │ │ │ ├── Renderable3D_Outline_frag.hlsl │ │ │ ├── Renderable3D_Outline_vert.hlsl │ │ │ ├── Sprite_frag.hlsl │ │ │ ├── Sprite_inverted_frag.hlsl │ │ │ ├── Sprite_inverted_vert.hlsl │ │ │ ├── Sprite_vert.hlsl │ │ │ ├── YUVFrame_frag.hlsl │ │ │ └── YUVFrame_vert.hlsl │ │ ├── opengl/ │ │ │ ├── AdditiveMaskedSprite_frag.glsl │ │ │ ├── AdditiveMaskedSprite_vert.glsl │ │ │ ├── CCMessageBoxSprite_frag.glsl │ │ │ ├── CCMessageBoxSprite_vert.glsl │ │ │ ├── CHLCCMenuBackground_frag.glsl │ │ │ ├── CHLCCMenuBackground_vert.glsl │ │ │ ├── ColorBurnMaskedSprite_frag.glsl │ │ │ ├── ColorBurnMaskedSprite_vert.glsl │ │ │ ├── ColorDodgeMaskedSprite_frag.glsl │ │ │ ├── ColorDodgeMaskedSprite_vert.glsl │ │ │ ├── ColorMaskedSprite_frag.glsl │ │ │ ├── ColorMaskedSprite_vert.glsl │ │ │ ├── GaussianBlur_frag.glsl │ │ │ ├── GaussianBlur_vert.glsl │ │ │ ├── HardLightMaskedSprite_frag.glsl │ │ │ ├── HardLightMaskedSprite_vert.glsl │ │ │ ├── LinearBurnMaskedSprite_frag.glsl │ │ │ ├── LinearBurnMaskedSprite_vert.glsl │ │ │ ├── MaskedSpriteBinary_frag.glsl │ │ │ ├── MaskedSpriteBinary_vert.glsl │ │ │ ├── MaskedSpriteNoAlpha_frag.glsl │ │ │ ├── MaskedSpriteNoAlpha_vert.glsl │ │ │ ├── MaskedSprite_frag.glsl │ │ │ ├── MaskedSprite_vert.glsl │ │ │ ├── Mosaic_frag.glsl │ │ │ ├── Mosaic_vert.glsl │ │ │ ├── NV12Frame_frag.glsl │ │ │ ├── NV12Frame_vert.glsl │ │ │ ├── OverlayMaskedSprite_frag.glsl │ │ │ ├── OverlayMaskedSprite_vert.glsl │ │ │ ├── Renderable3D_Background_frag.glsl │ │ │ ├── Renderable3D_Background_vert.glsl │ │ │ ├── Renderable3D_Character_frag.glsl │ │ │ ├── Renderable3D_Character_vert.glsl │ │ │ ├── Renderable3D_Eye_frag.glsl │ │ │ ├── Renderable3D_Eye_vert.glsl │ │ │ ├── Renderable3D_Outline_frag.glsl │ │ │ ├── Renderable3D_Outline_vert.glsl │ │ │ ├── SceneToRT_frag.glsl │ │ │ ├── SceneToRT_vert.glsl │ │ │ ├── ScreenMaskedSprite_frag.glsl │ │ │ ├── ScreenMaskedSprite_vert.glsl │ │ │ ├── SoftLightMaskedSprite_frag.glsl │ │ │ ├── SoftLightMaskedSprite_vert.glsl │ │ │ ├── SpriteInverted_frag.glsl │ │ │ ├── SpriteInverted_vert.glsl │ │ │ ├── Sprite_frag.glsl │ │ │ ├── Sprite_vert.glsl │ │ │ ├── SubtitleGlyph_frag.glsl │ │ │ ├── SubtitleGlyph_vert.glsl │ │ │ ├── YUVFrame_frag.glsl │ │ │ └── YUVFrame_vert.glsl │ │ └── vulkan/ │ │ ├── CCMessageBoxSprite_frag.glsl │ │ ├── CCMessageBoxSprite_frag.spv │ │ ├── CCMessageBoxSprite_vert.glsl │ │ ├── CCMessageBoxSprite_vert.spv │ │ ├── CHLCCMenuBackground_frag.glsl │ │ ├── CHLCCMenuBackground_frag.spv │ │ ├── CHLCCMenuBackground_vert.glsl │ │ ├── CHLCCMenuBackground_vert.spv │ │ ├── MaskedSpriteNoAlpha_frag.glsl │ │ ├── MaskedSpriteNoAlpha_frag.spv │ │ ├── MaskedSpriteNoAlpha_vert.glsl │ │ ├── MaskedSpriteNoAlpha_vert.spv │ │ ├── MaskedSprite_frag.glsl │ │ ├── MaskedSprite_frag.spv │ │ ├── MaskedSprite_vert.glsl │ │ ├── MaskedSprite_vert.spv │ │ ├── NV12Frame_frag.glsl │ │ ├── NV12Frame_frag.spv │ │ ├── NV12Frame_vert.glsl │ │ ├── NV12Frame_vert.spv │ │ ├── Renderable3D_Background_frag.glsl │ │ ├── Renderable3D_Background_frag.spv │ │ ├── Renderable3D_Background_vert.glsl │ │ ├── Renderable3D_Background_vert.spv │ │ ├── Renderable3D_Character_frag.glsl │ │ ├── Renderable3D_Character_frag.spv │ │ ├── Renderable3D_Character_vert.glsl │ │ ├── Renderable3D_Character_vert.spv │ │ ├── Renderable3D_Eye_frag.glsl │ │ ├── Renderable3D_Eye_frag.spv │ │ ├── Renderable3D_Eye_vert.glsl │ │ ├── Renderable3D_Eye_vert.spv │ │ ├── Renderable3D_Outline_frag.glsl │ │ ├── Renderable3D_Outline_frag.spv │ │ ├── Renderable3D_Outline_vert.glsl │ │ ├── Renderable3D_Outline_vert.spv │ │ ├── Sprite_frag.glsl │ │ ├── Sprite_frag.spv │ │ ├── Sprite_inverted_frag.glsl │ │ ├── Sprite_inverted_frag.spv │ │ ├── Sprite_inverted_vert.glsl │ │ ├── Sprite_inverted_vert.spv │ │ ├── Sprite_vert.glsl │ │ ├── Sprite_vert.spv │ │ ├── YUVFrame_frag.glsl │ │ ├── YUVFrame_frag.spv │ │ ├── YUVFrame_vert.glsl │ │ └── YUVFrame_vert.spv │ ├── spriteanimation.cpp │ ├── spriteanimation.h │ ├── spritesheet.h │ ├── stbi_impl.c │ ├── subtitle/ │ │ ├── ass/ │ │ │ ├── subtitlerenderer.cpp │ │ │ └── subtitlerenderer.h │ │ ├── ffmpegsubtitlehelper.cpp │ │ ├── ffmpegsubtitlehelper.h │ │ ├── subtitlerenderer.h │ │ ├── subtitlesystem.cpp │ │ └── subtitlesystem.h │ ├── text/ │ │ ├── dialoguepage.cpp │ │ ├── dialoguepage.h │ │ ├── text.cpp │ │ ├── text.h │ │ ├── typewritereffect.cpp │ │ └── typewritereffect.h │ ├── texture/ │ │ ├── bcdecode.cpp │ │ ├── bcdecode.h │ │ ├── bntxloader.cpp │ │ ├── bntxloader.h │ │ ├── ddsloader.cpp │ │ ├── ddsloader.h │ │ ├── gxtloader.cpp │ │ ├── gxtloader.h │ │ ├── plainloader.cpp │ │ ├── plainloader.h │ │ ├── s3tc.cpp │ │ ├── s3tc.h │ │ ├── stbiloader.cpp │ │ ├── texture.cpp │ │ ├── texture.h │ │ └── webpdecode.cpp │ ├── ui/ │ │ ├── backlogmenu.cpp │ │ ├── backlogmenu.h │ │ ├── gamespecific.cpp │ │ ├── gamespecific.h │ │ ├── mapsystem.cpp │ │ ├── mapsystem.h │ │ ├── menu.cpp │ │ ├── menu.h │ │ ├── nullmenu.cpp │ │ ├── nullmenu.h │ │ ├── optionsmenu.cpp │ │ ├── optionsmenu.h │ │ ├── savemenu.h │ │ ├── selectionmenu.cpp │ │ ├── selectionmenu.h │ │ ├── sysmesbox.cpp │ │ ├── sysmesbox.h │ │ ├── tipsmenu.cpp │ │ ├── tipsmenu.h │ │ ├── ui.h │ │ ├── widget.cpp │ │ ├── widget.h │ │ └── widgets/ │ │ ├── backlogentry.cpp │ │ ├── backlogentry.h │ │ ├── button.cpp │ │ ├── button.h │ │ ├── carousel.cpp │ │ ├── carousel.h │ │ ├── cc/ │ │ │ ├── backlogentry.cpp │ │ │ ├── backlogentry.h │ │ │ ├── titlebutton.cpp │ │ │ └── titlebutton.h │ │ ├── cclcc/ │ │ │ ├── librarymenubutton.h │ │ │ ├── optionsbinarybutton.cpp │ │ │ ├── optionsbinarybutton.h │ │ │ ├── optionsentry.cpp │ │ │ ├── optionsentry.h │ │ │ ├── optionsslider.cpp │ │ │ ├── optionsslider.h │ │ │ ├── optionsvoiceslider.cpp │ │ │ ├── optionsvoiceslider.h │ │ │ ├── saveentrybutton.cpp │ │ │ ├── saveentrybutton.h │ │ │ ├── sysmenubutton.cpp │ │ │ ├── sysmenubutton.h │ │ │ ├── tipsentrybutton.cpp │ │ │ ├── tipsentrybutton.h │ │ │ ├── tipstabgroup.cpp │ │ │ ├── tipstabgroup.h │ │ │ ├── titlebutton.cpp │ │ │ └── titlebutton.h │ │ ├── cgviewer.cpp │ │ ├── cgviewer.h │ │ ├── chlcc/ │ │ │ ├── albumthumbnailbutton.cpp │ │ │ ├── albumthumbnailbutton.h │ │ │ ├── backlogentry.cpp │ │ │ ├── backlogentry.h │ │ │ ├── moviemenuentrybutton.cpp │ │ │ ├── moviemenuentrybutton.h │ │ │ ├── optionsbutton.cpp │ │ │ ├── optionsbutton.h │ │ │ ├── optionsentry.cpp │ │ │ ├── optionsentry.h │ │ │ ├── optionsslider.cpp │ │ │ ├── optionsslider.h │ │ │ ├── saveentrybutton.cpp │ │ │ ├── saveentrybutton.h │ │ │ ├── systemmenuentrybutton.cpp │ │ │ ├── systemmenuentrybutton.h │ │ │ ├── systemmessagebutton.cpp │ │ │ ├── systemmessagebutton.h │ │ │ ├── tipsentrybutton.cpp │ │ │ ├── tipsentrybutton.h │ │ │ ├── titlebutton.cpp │ │ │ ├── titlebutton.h │ │ │ ├── trackselectbutton.cpp │ │ │ ├── trackselectbutton.h │ │ │ ├── trophymenuentry.cpp │ │ │ └── trophymenuentry.h │ │ ├── clickarea.cpp │ │ ├── clickarea.h │ │ ├── group.cpp │ │ ├── group.h │ │ ├── label.cpp │ │ ├── label.h │ │ ├── mo6tw/ │ │ │ ├── actorsvoicebutton.cpp │ │ │ ├── actorsvoicebutton.h │ │ │ ├── albumcharacterbutton.cpp │ │ │ ├── albumcharacterbutton.h │ │ │ ├── albumthumbnailbutton.cpp │ │ │ ├── albumthumbnailbutton.h │ │ │ ├── imagethumbnailbutton.cpp │ │ │ ├── imagethumbnailbutton.h │ │ │ ├── saveentrybutton.cpp │ │ │ ├── saveentrybutton.h │ │ │ ├── scenelistentry.cpp │ │ │ ├── scenelistentry.h │ │ │ ├── tipsentrybutton.cpp │ │ │ ├── tipsentrybutton.h │ │ │ ├── titlebutton.cpp │ │ │ └── titlebutton.h │ │ ├── optiongroup.cpp │ │ ├── optiongroup.h │ │ ├── rne/ │ │ │ ├── sysmenubutton.cpp │ │ │ └── sysmenubutton.h │ │ ├── scrollbar.cpp │ │ ├── scrollbar.h │ │ ├── toggle.cpp │ │ └── toggle.h │ ├── util.cpp │ ├── util.h │ ├── video/ │ │ ├── clock.cpp │ │ ├── clock.h │ │ ├── ffmpegplayer.cpp │ │ ├── ffmpegplayer.h │ │ ├── ffmpegstream.cpp │ │ ├── ffmpegstream.h │ │ ├── videoplayer.cpp │ │ ├── videoplayer.h │ │ ├── videosystem.cpp │ │ └── videosystem.h │ ├── vm/ │ │ ├── expression.cpp │ │ ├── expression.h │ │ ├── inst_controlflow.cpp │ │ ├── inst_controlflow.h │ │ ├── inst_dialogue.cpp │ │ ├── inst_dialogue.h │ │ ├── inst_gamespecific.cpp │ │ ├── inst_gamespecific.h │ │ ├── inst_graphics2d.cpp │ │ ├── inst_graphics2d.h │ │ ├── inst_graphics3d.cpp │ │ ├── inst_graphics3d.h │ │ ├── inst_macros.inc │ │ ├── inst_misc.cpp │ │ ├── inst_misc.h │ │ ├── inst_movie.cpp │ │ ├── inst_movie.h │ │ ├── inst_sound.cpp │ │ ├── inst_sound.h │ │ ├── inst_system.cpp │ │ ├── inst_system.h │ │ ├── interface/ │ │ │ ├── input.cpp │ │ │ ├── input.h │ │ │ ├── scene3d.cpp │ │ │ └── scene3d.h │ │ ├── opcodetables_cc.h │ │ ├── opcodetables_chlcc.h │ │ ├── opcodetables_chn.h │ │ ├── opcodetables_darling.h │ │ ├── opcodetables_dash.h │ │ ├── opcodetables_mo6tw.h │ │ ├── opcodetables_mo7.h │ │ ├── opcodetables_mo8.h │ │ ├── opcodetables_rne.h │ │ ├── opcodetables_sgps3.h │ │ ├── sc3stream.h │ │ ├── thread.cpp │ │ ├── thread.h │ │ ├── vm.cpp │ │ └── vm.h │ ├── voicetable.cpp │ ├── voicetable.h │ ├── workqueue.cpp │ └── workqueue.h ├── triplets/ │ ├── arm64-android-ci.cmake │ ├── arm64-osx-ci.cmake │ ├── x64-linux-ci.cmake │ └── x64-osx-ci.cmake ├── vcpkg-configuration.json ├── vcpkg.json └── vendor/ ├── .clang-format ├── clHCA/ │ ├── clHCA.c │ └── clHCA.h ├── glad/ │ └── src/ │ └── glad.c ├── imgui_custom/ │ └── backends/ │ ├── imgui_impl_opengl3.cpp │ ├── imgui_impl_opengl3.h │ └── imgui_impl_opengl3_loader.h ├── minilua/ │ ├── minilua.h │ └── minilua_impl.c ├── mio/ │ └── mio.hpp ├── mspack/ │ ├── lzx.h │ ├── lzxd.c │ ├── macros.h │ ├── mspack.h │ ├── readbits.h │ ├── readhuff.h │ ├── system.c │ └── system.h ├── patches/ │ └── LibAtrac9/ │ └── CMakeLists.txt ├── pcg/ │ └── src/ │ └── pcg_basic.c ├── squish/ │ ├── ChangeLog │ ├── README │ ├── alpha.cpp │ ├── alpha.h │ ├── clusterfit.cpp │ ├── clusterfit.h │ ├── colourblock.cpp │ ├── colourblock.h │ ├── colourfit.cpp │ ├── colourfit.h │ ├── colourset.cpp │ ├── colourset.h │ ├── config.h │ ├── maths.cpp │ ├── maths.h │ ├── rangefit.cpp │ ├── rangefit.h │ ├── simd.h │ ├── simd_float.h │ ├── simd_sse.h │ ├── simd_ve.h │ ├── singlecolourfit.cpp │ ├── singlecolourfit.h │ ├── singlecolourlookup.inl │ ├── squish.cpp │ └── squish.h └── vma/ └── vk_mem_alloc.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ --- BasedOnStyle: Google SortIncludes: false UseTab: Never ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .github/workflows/docker-switch.yml ================================================ name: Docker Image CI for GHCR on: push: paths: - docker/impacto-switch/* - .github/workflows/docker-switch.yml workflow_dispatch: jobs: build_and_publish: permissions: contents: read packages: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Log in to the Container registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@v4 with: images: ghcr.io/committeeofzero/impacto-switch - name: Build and push Docker image uses: docker/build-push-action@v4 with: push: true context: docker/impacto-switch file: docker/impacto-switch/Dockerfile tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} ================================================ FILE: .github/workflows/impacto.yml ================================================ name: impacto on: push: branches: - "master" tags: - "*" pull_request: branches: - "master" workflow_dispatch: env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} jobs: get-version: name: Get version runs-on: ubuntu-24.04 permissions: contents: read outputs: VERSION: ${{ steps.set-version.outputs.VERSION }} steps: - uses: actions/checkout@v6 - name: Set version id: set-version run: echo "VERSION=$(cat VERSION)" >> $GITHUB_OUTPUT - name: Upload VERSION Artifact uses: actions/upload-artifact@v7 with: path: VERSION name: VERSION archive: false lint: name: Run linters permissions: checks: write contents: write runs-on: ubuntu-24.04 outputs: commitid: ${{ steps.commit.outputs.commitid }} steps: - name: Check out Git repository uses: actions/checkout@v6 - name: Update clang-format run: | sudo apt update sudo pipx install clang-format which clang-format clang-format --version - name: Run linters uses: wearerequired/lint-action@v2 with: auto_fix: ${{(github.event.pull_request.head.repo.full_name != 'CommitteeOfZero/impacto') && 'false' || 'true'}} clang_format: true clang_format_auto_fix: ${{(github.event.pull_request.head.repo.full_name != 'CommitteeOfZero/impacto') && 'false' || 'true'}} continue_on_error: false - name: Get latest commit id: commit run: | SHA=$(git rev-parse HEAD) echo "commitid=$SHA" >> $GITHUB_OUTPUT job-matrix: name: ${{ matrix.os_name }} runs-on: ${{ matrix.os }} timeout-minutes: 120 needs: - lint - get-version permissions: contents: read packages: write strategy: fail-fast: false matrix: include: - os: windows-2025 os_name: windows triplet: x64-windows-release host_triplet: x64-windows-release - os: ubuntu-24.04 os_name: linux triplet: x64-linux-ci host_triplet: x64-linux-ci - os: macos-15 os_name: macos_arm64 triplet: arm64-osx-ci host_triplet: arm64-osx-ci - os: macos-15-intel os_name: macos_x64 triplet: x64-osx-ci host_triplet: x64-osx-ci - os: ubuntu-24.04 os_name: android triplet: arm64-android-ci host_triplet: x64-linux-ci env: VCPKG_DEFAULT_TRIPLET: ${{ matrix.triplet }} VCPKG_BINARY_SOURCES: >- ${{ github.event_name == 'pull_request_target' && (github.event.pull_request.head.repo.full_name != github.repository) && 'clear;nuget,https://nuget.pkg.github.com/committeeofzero/index.json,read' || 'clear;nuget,https://nuget.pkg.github.com/committeeofzero/index.json,readwrite' }} vcpkgCommitId: '62159a45e18f3a9ac0548628dcaf74fcb60c6ff9' PresetName: ${{ (matrix.os_name == 'android') && 'ci-release-android' || 'ci-release' }} VERSION: ${{ needs.get-version.outputs.VERSION }} VCPKG_FORCE_DOWNLOADED_BINARIES: 1 steps: - uses: actions/checkout@v6 with: ref: ${{ needs.lint.outputs.commitid }} - name: Setup Android Environment if: matrix.os_name == 'android' run: | export ABI=arm64-v8a export MINSDKVERSION=28 echo "MINSDKVERSION=$MINSDKVERSION" >> $GITHUB_ENV echo "ABI=$ABI" >> $GITHUB_ENV echo "CC=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/clang --target=aarch64-linux-android$MINSDKVERSION -fPIC" >> $GITHUB_ENV echo "VULKAN_SDK=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr" >> $GITHUB_ENV shell: bash - name: Set up Java uses: actions/setup-java@v4 if: matrix.os_name == 'android' with: java-version: 17 distribution: "temurin" cache: 'gradle' - uses: lukka/get-cmake@latest - name: Setup vcpkg uses: lukka/run-vcpkg@v11 id: runvcpkg with: vcpkgDirectory: '${{ runner.workspace }}/build/vcpkg' vcpkgGitCommitId: '${{ env.vcpkgCommitId }}' vcpkgJsonGlob: '**/vcpkg.json' - name: Install Dependencies Linux run: | sudo apt-get update sudo apt-get install nasm libx11-dev libxft-dev libxext-dev libwayland-dev libxkbcommon-dev libegl1-mesa-dev libibus-1.0-dev libxrandr-dev libltdl-dev mono-complete autoconf autoconf-archive automake libtool libdrm-dev if: matrix.os_name == 'linux' || matrix.os_name == 'android' - name: Install Dependencies Mac run: brew install nasm mono if: contains(matrix.os_name, 'macos') - name: Configure NuGet feed via vcpkg shell: bash env: FEED_URL: 'https://nuget.pkg.github.com/committeeofzero/index.json' run: | NUGET_PATH=$(vcpkg fetch nuget | tail -n 1) if [[ "$NUGET_PATH" == *.exe && "$RUNNER_OS" != "Windows" ]]; then EXEC=(mono "$NUGET_PATH") else EXEC=("$NUGET_PATH") fi "${EXEC[@]}" sources add \ -Source "${{ env.FEED_URL }}" \ -StorePasswordInClearText \ -Name GitHubPackages \ -UserName "GithubCI" \ -Password "${{ github.token }}" "${EXEC[@]}" setapikey "${{ github.token }}" \ -Source "${{ env.FEED_URL }}" - name: Run CMake with vcpkg.json manifest uses: lukka/run-cmake@v10 with: cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' configurePreset: ${{ env.PresetName }} configurePresetAdditionalArgs: >- [ '-DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }}', '-DVCPKG_HOST_TRIPLET=${{ matrix.host_triplet }}', ] buildPreset: ${{ env.PresetName }} - name: Copy docs run: | mkdir -p "${{ github.workspace }}/release/${{ env.PresetName }}" cp THIRDPARTY.md README.md LICENSE "${{ github.workspace }}/release/${{ env.PresetName }}" shell: bash - name: Decode signing/prod.properties file if: matrix.os_name == 'android' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) env: PROD_PROPERTIES: ${{ secrets.ANDROID_PROD_PROPERTIES }} PROD_JKS: ${{ secrets.ANDROID_PROD_JKS }} run: | echo "$PROD_PROPERTIES" > ${{ github.workspace }}/android/app/signing/prod.properties echo "$PROD_JKS" | base64 -d > ${{ github.workspace }}/android/app/signing/prod.jks - name: Build Android if: matrix.os_name == 'android' run: | pushd "${{ github.workspace }}/android/" ./gradlew assemble cp "${{ github.workspace }}/android/distribution/android/app/outputs/apk/release/app-release.apk" "${{ github.workspace }}/release/${{ env.PresetName }}/impacto-release.apk" shell: bash - name: Archive Artifacts run: | mv ./release/${{ env.PresetName }} ./release/impacto 7z a -tzip -mm=Deflate -mx=9 impacto-${{ matrix.os_name }}-${{ env.VERSION }}${{ github.run_number }}-${{ github.sha }}.zip ./release/impacto - name: Upload Artifact uses: actions/upload-artifact@v7 with: path: impacto-${{ matrix.os_name }}-${{ env.VERSION }}${{ github.run_number }}-${{ github.sha }}.zip archive: false switch: runs-on: ubuntu-latest timeout-minutes: 120 needs: - lint - get-version permissions: contents: read container: image: ghcr.io/committeeofzero/impacto-switch:latest env: VERSION: ${{ needs.get-version.outputs.VERSION }} steps: - uses: actions/checkout@v6 with: ref: ${{ needs.lint.outputs.commitid }} - name: Run build run: |- cmake . -DCMAKE_TOOLCHAIN_FILE=HorizonNX.toolchain make -j2 elf2nro impacto impacto.nro mkdir -p "release/ci-release" cp impacto.nro "release/ci-release" cp -r profiles "release/ci-release" cp -r games "release/ci-release" - name: Copy Shaders run: cp -r src/shaders "release/ci-release" shell: bash - name: Copy docs run: | cp THIRDPARTY.md README.md LICENSE "release/ci-release" shell: bash - name: Archive Artifacts run: | mv ./release/ci-release ./release/impacto 7z a -tzip -mm=Deflate -mx=9 impacto-switch-${{ env.VERSION }}${{ github.run_number }}-${{ github.sha }}.zip ./release/impacto - name: Upload Artifact uses: actions/upload-artifact@v7 with: path: impacto-switch-${{ env.VERSION }}${{ github.run_number }}-${{ github.sha }}.zip archive: false ================================================ FILE: .github/workflows/notify.yml ================================================ name: notify on: workflow_run: workflows: [impacto] types: - completed jobs: notify_fail: name: Notify failure runs-on: ubuntu-22.04 if: always() && (github.event.workflow_run.conclusion == 'cancelled' || github.event.workflow_run.conclusion == 'failure') steps: - uses: actions/checkout@v6 - name: 'Get Jobs Status' id: get_jobs_status run: | echo "Fetch workflow run jobs" json_data=$(curl -s -H "Authorization: Bearer ${{ github.token }}" \ "${{ github.event.workflow_run.jobs_url }}") name_conclusion_array=($(echo "$json_data" | jq -r ' .jobs[] | select(.name | IN("macos_arm64", "linux", "windows", "switch", "macos_x64", "android")) | "\(.name).\(.conclusion)"')) for item in "${name_conclusion_array[@]}"; do IFS='.' read -r name conclusion <<< "$item" echo "$name=$conclusion" >> $GITHUB_OUTPUT done - name: Get current date id: date run: echo "NOW=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_OUTPUT - uses: tsickert/discord-webhook@v5.3.0 with: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} username: IMPACTO avatar-url: https://cdn.discordapp.com/emojis/766988033127481344.png embed-title : "${{ github.event.commits[0].author.name }}" embed-color: 16711680 embed-timestamp: ${{ steps.date.outputs.NOW }} embed-description: | Commit `${{ github.event.workflow_run.head_commit.message }} (${{ github.sha }})` from branch `${{ github.event.workflow_run.head_branch }}` has failed build. Job status: windows: ${{steps.get_jobs_status.outputs.windows}} linux: ${{steps.get_jobs_status.outputs.linux}} macos_arm64: ${{steps.get_jobs_status.outputs.macos_arm64}} macos_x64: ${{steps.get_jobs_status.outputs.macos_x64}} switch: ${{steps.get_jobs_status.outputs.switch}} android: ${{steps.get_jobs_status.outputs.android}} Details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }} ================================================ FILE: .github/workflows/publish.yml ================================================ name: publish on: workflow_run: workflows: [impacto] types: [completed] branches: - master - v* jobs: publish_artifacts: name: Publish Artifacts runs-on: ubuntu-22.04 permissions: contents: write actions: read if: github.event.workflow_run.conclusion == 'success' steps: - uses: actions/download-artifact@v8 with: name: VERSION github-token: ${{ github.token }} run-id: ${{ github.event.workflow_run.id }} - name: Set version id: set-version run: echo "VERSION=$(cat VERSION)" >> $GITHUB_OUTPUT - uses: actions/download-artifact@v8 with: pattern: impacto-* path: "${{ github.workspace }}/release" merge-multiple: true skip-decompress: true github-token: ${{ github.token }} run-id: ${{ github.event.workflow_run.id }} - name: Upload Release uses: softprops/action-gh-release@v2 with: Name: Latest ${{ env.BRANCH_NAME }} build tag_name: ${{ steps.set-version.outputs.VERSION }}${{ github.event.workflow_run.run_number }} files: ${{ github.workspace }}/release/*.zip prerelease: true fail_on_unmatched_files: true target_commitish: ${{ github.sha }} - name: Get current date id: date run: echo "NOW=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_ENV - uses: tsickert/discord-webhook@v5.3.0 with: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} username: IMPACTO avatar-url: https://cdn.discordapp.com/emojis/766988033127481344.png embed-title : "${{ github.event.workflow_run.head_commit.author.name }}" embed-color: 3659647 embed-timestamp: ${{ env.NOW }} embed-description: | Commit `${{ github.event.workflow_run.head_commit.message }} (${{ github.sha }})` from branch `${{ github.event.workflow_run.head_branch }}` has been successfully built. Release URL: https://github.com/CommitteeOfZero/impacto/releases/tag/${{ steps.set-version.outputs.VERSION }}${{ github.event.workflow_run.run_number }} Details: - Build: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }} - Publish: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]uild/ [Oo]ut [Bb]in/ [Oo]bj/ [Ll]og/ [Ii]nstall/ cmake-build-*/ impacto-build/ ci-build/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json project.fragment.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted #*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings node_modules/ orleans.codegen.cs *.cbp cmake_install.cmake include/ Makefile .DS_Store .cache # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml .luarc.json # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # VSCode .vscode ### compile_commands.json /CMakeSettings.json CMakeUserPresets.json vendor/avcpp games/*/gamedata games/*/savedata games/*/trophydata ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.28) if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) endif() if (POLICY CMP0174) cmake_policy(SET CMP0174 NEW) endif() project("impacto") include(FetchContent) find_package(PkgConfig) if (EMSCRIPTEN) list(APPEND CMAKE_MODULE_PATH ${EMSCRIPTEN_ROOT_PATH}/cmake/Modules) endif () set(Impacto_Src src/main.cpp src/log.cpp src/util.cpp src/workqueue.cpp src/game.cpp src/mem.cpp src/modelviewer.cpp src/characterviewer.cpp src/spriteanimation.cpp src/sequencedanimation.cpp src/background2d.cpp src/mask2d.cpp src/character2d.cpp src/inputsystem.cpp src/voicetable.cpp src/animation.cpp src/text/text.cpp src/text/typewritereffect.cpp src/text/dialoguepage.cpp src/effects/wave.cpp src/effects/blur.cpp src/effects/mosaic.cpp src/effects/chlcc/butterflyeffect.cpp src/effects/chlcc/bubbleseffect.cpp src/effects/chlcc/eyecatch.cpp src/renderer/renderer.cpp src/renderer/window.cpp src/data/savesystem.cpp src/data/tipssystem.cpp src/data/achievementsystem.cpp src/data/achievementsystemps3.cpp src/profile/profile.cpp src/profile/profile_internal.cpp src/profile/game.cpp src/profile/vfs.cpp src/profile/scene3d.cpp src/profile/sprites.cpp src/profile/animations.cpp src/profile/charset.cpp src/profile/fonts.cpp src/profile/dialogue.cpp src/profile/vm.cpp src/profile/scriptvars.cpp src/profile/scriptinput.cpp src/profile/configsystem.cpp src/profile/subtitle.cpp src/profile/data/bgeff.cpp src/profile/data/savesystem.cpp src/profile/data/tipssystem.cpp src/profile/data/achievementsystem.cpp src/profile/data/waveeffects.cpp src/profile/hud/saveicon.cpp src/profile/hud/loadingdisplay.cpp src/profile/hud/datedisplay.cpp src/profile/hud/tipsnotification.cpp src/profile/ui/commonmenu.cpp src/profile/ui/systemmenu.cpp src/profile/ui/titlemenu.cpp src/profile/ui/savemenu.cpp src/profile/ui/sysmesbox.cpp src/profile/ui/selectionmenu.cpp src/profile/ui/backlogmenu.cpp src/profile/ui/optionsmenu.cpp src/profile/ui/tipsmenu.cpp src/profile/ui/extramenus.cpp src/profile/ui/trophymenu.cpp src/profile/ui/helpmenu.cpp src/profile/ui/gamespecific.cpp src/profile/games/rne/tilebackground.cpp src/profile/games/rne/systemmenu.cpp src/profile/games/rne/titlemenu.cpp src/profile/games/rne/sysmesbox.cpp src/profile/games/dash/titlemenu.cpp src/profile/games/chlcc/commonmenu.cpp src/profile/games/chlcc/dialoguebox.cpp src/profile/games/chlcc/titlemenu.cpp src/profile/games/chlcc/savemenu.cpp src/profile/games/chlcc/sysmesbox.cpp src/profile/games/chlcc/clearlistmenu.cpp src/profile/games/chlcc/moviemenu.cpp src/profile/games/chlcc/albummenu.cpp src/profile/games/chlcc/musicmenu.cpp src/profile/games/chlcc/tipsmenu.cpp src/profile/games/chlcc/optionsmenu.cpp src/profile/games/chlcc/backlogmenu.cpp src/profile/games/chlcc/systemmenu.cpp src/profile/games/chlcc/trophymenu.cpp src/profile/games/chlcc/delusiontrigger.cpp src/profile/games/chlcc/tipsnotification.cpp src/profile/games/cc/backlogmenu.cpp src/profile/games/cc/dialoguebox.cpp src/profile/games/cc/titlemenu.cpp src/profile/games/cc/sysmesbox.cpp src/profile/games/cclcc/systemmenu.cpp src/profile/games/cclcc/titlemenu.cpp src/profile/games/cclcc/savemenu.cpp src/profile/games/cclcc/optionsmenu.cpp src/profile/games/cclcc/tipsmenu.cpp src/profile/games/cclcc/clearlistmenu.cpp src/profile/games/cclcc/librarymenu.cpp src/profile/games/cclcc/tipsnotification.cpp src/profile/games/cclcc/mapsystem.cpp src/profile/games/cclcc/delusiontrigger.cpp src/profile/games/cclcc/yesnotrigger.cpp src/profile/games/cclcc/helpmenu.cpp src/profile/games/mo6tw/backlogmenu.cpp src/profile/games/mo6tw/dialoguebox.cpp src/profile/games/mo6tw/sysmesbox.cpp src/profile/games/mo6tw/systemmenu.cpp src/profile/games/mo6tw/titlemenu.cpp src/profile/games/mo6tw/savemenu.cpp src/profile/games/mo6tw/optionsmenu.cpp src/profile/games/mo6tw/tipsmenu.cpp src/profile/games/mo6tw/clearlistmenu.cpp src/profile/games/mo6tw/moviemenu.cpp src/profile/games/mo6tw/actorsvoicemenu.cpp src/profile/games/mo6tw/musicmenu.cpp src/profile/games/mo6tw/albummenu.cpp src/profile/games/mo6tw/tipsnotification.cpp src/profile/games/mo8/titlemenu.cpp src/profile/games/mo8/systemmenu.cpp src/profile/games/mo8/optionsmenu.cpp src/profile/games/mo8/savemenu.cpp src/profile/games/darling/sysmesbox.cpp src/games/rne/tilebackground.cpp src/games/rne/systemmenu.cpp src/games/rne/titlemenu.cpp src/games/rne/sysmesbox.cpp src/games/dash/titlemenu.cpp src/games/chlcc/commonmenu.cpp src/games/chlcc/titlemenu.cpp src/games/chlcc/savemenu.cpp src/games/chlcc/sysmesbox.cpp src/games/chlcc/animations/selectprompt.cpp src/games/chlcc/animations/menutransition.cpp src/games/chlcc/animations/saveicon.cpp src/games/chlcc/savesystem.cpp src/games/chlcc/tipssystem.cpp src/games/chlcc/clearlistmenu.cpp src/games/chlcc/moviemenu.cpp src/games/chlcc/albummenu.cpp src/games/chlcc/musicmenu.cpp src/games/chlcc/tipsmenu.cpp src/games/chlcc/optionsmenu.cpp src/games/chlcc/backlogmenu.cpp src/games/chlcc/systemmenu.cpp src/games/chlcc/trophymenu.cpp src/games/chlcc/delusiontrigger.cpp src/games/chlcc/introsequence.cpp src/games/cc/backlogmenu.cpp src/games/cc/titlemenu.cpp src/games/cc/sysmesbox.cpp src/games/cclcc/titlemenu.cpp src/games/cclcc/savemenu.cpp src/games/cclcc/optionsmenu.cpp src/games/cclcc/tipsmenu.cpp src/games/cclcc/tipssystem.cpp src/games/cclcc/clearlistmenu.cpp src/games/cclcc/librarymenu.cpp src/games/cclcc/librarysubmenus.cpp src/games/cclcc/albummenu.cpp src/games/cclcc/musicmenu.cpp src/games/cclcc/moviemenu.cpp src/games/cclcc/mapsystem.cpp src/games/cclcc/delusiontrigger.cpp src/games/cclcc/yesnotrigger.cpp src/games/cclcc/savesystem.cpp src/games/cclcc/systemmenu.cpp src/games/cclcc/helpmenu.cpp src/games/mo6tw/backlogmenu.cpp src/games/mo6tw/sysmesbox.cpp src/games/mo6tw/systemmenu.cpp src/games/mo6tw/titlemenu.cpp src/games/mo6tw/savemenu.cpp src/games/mo6tw/optionsmenu.cpp src/games/mo6tw/tipsmenu.cpp src/games/mo6tw/savesystem.cpp src/games/mo6tw/tipssystem.cpp src/games/mo6tw/clearlistmenu.cpp src/games/mo6tw/moviemenu.cpp src/games/mo6tw/actorsvoicemenu.cpp src/games/mo6tw/musicmenu.cpp src/games/mo6tw/albummenu.cpp src/games/mo8/titlemenu.cpp src/games/mo8/systemmenu.cpp src/games/mo8/optionsmenu.cpp src/games/mo8/savemenu.cpp src/games/darling/sysmesbox.cpp src/hud/dialoguebox.cpp src/hud/defaultsaveiconanimation.cpp src/hud/datedisplay.cpp src/hud/saveicondisplay.cpp src/hud/loadingdisplay.cpp src/hud/nametagdisplay.cpp src/hud/autoicondisplay.cpp src/hud/skipicondisplay.cpp src/hud/waiticondisplay.cpp src/hud/tipsnotification.cpp src/hud/cc/dialoguebox.cpp src/hud/cc/nametagdisplay.cpp src/hud/cclcc/tipsnotification.cpp src/hud/chlcc/dialoguebox.cpp src/hud/chlcc/nametagdisplay.cpp src/hud/chlcc/tipsnotification.cpp src/hud/mo6tw/dialoguebox.cpp src/hud/mo6tw/tipsnotification.cpp src/hud/rne/datedisplay.cpp src/ui/widget.cpp src/ui/menu.cpp src/ui/nullmenu.cpp src/ui/selectionmenu.cpp src/ui/sysmesbox.cpp src/ui/backlogmenu.cpp src/ui/tipsmenu.cpp src/ui/optionsmenu.cpp src/ui/widgets/label.cpp src/ui/widgets/button.cpp src/ui/widgets/backlogentry.cpp src/ui/widgets/scrollbar.cpp src/ui/widgets/toggle.cpp src/ui/widgets/optiongroup.cpp src/ui/widgets/group.cpp src/ui/widgets/carousel.cpp src/ui/widgets/cgviewer.cpp src/ui/widgets/clickarea.cpp src/ui/widgets/mo6tw/titlebutton.cpp src/ui/widgets/mo6tw/saveentrybutton.cpp src/ui/widgets/mo6tw/tipsentrybutton.cpp src/ui/widgets/mo6tw/scenelistentry.cpp src/ui/widgets/mo6tw/imagethumbnailbutton.cpp src/ui/widgets/mo6tw/albumthumbnailbutton.cpp src/ui/widgets/mo6tw/actorsvoicebutton.cpp src/ui/widgets/mo6tw/albumcharacterbutton.cpp src/ui/widgets/chlcc/titlebutton.cpp src/ui/widgets/chlcc/saveentrybutton.cpp src/ui/widgets/chlcc/systemmenuentrybutton.cpp src/ui/widgets/chlcc/systemmessagebutton.cpp src/ui/widgets/chlcc/moviemenuentrybutton.cpp src/ui/widgets/chlcc/albumthumbnailbutton.cpp src/ui/widgets/chlcc/trackselectbutton.cpp src/ui/widgets/chlcc/backlogentry.cpp src/ui/widgets/chlcc/trophymenuentry.cpp src/ui/widgets/chlcc/optionsentry.cpp src/ui/widgets/chlcc/optionsbutton.cpp src/ui/widgets/chlcc/optionsslider.cpp src/ui/widgets/chlcc/tipsentrybutton.cpp src/ui/widgets/rne/sysmenubutton.cpp src/ui/widgets/cc/backlogentry.cpp src/ui/widgets/cc/titlebutton.cpp src/ui/widgets/cclcc/titlebutton.cpp src/ui/widgets/cclcc/optionsentry.cpp src/ui/widgets/cclcc/optionsbinarybutton.cpp src/ui/widgets/cclcc/optionsslider.cpp src/ui/widgets/cclcc/optionsvoiceslider.cpp src/ui/widgets/cclcc/saveentrybutton.cpp src/ui/widgets/cclcc/sysmenubutton.cpp src/ui/widgets/cclcc/tipsentrybutton.cpp src/ui/widgets/cclcc/tipstabgroup.cpp src/ui/gamespecific.cpp src/stbi_impl.c src/renderer/3d/camera.cpp src/renderer/3d/model.cpp src/renderer/3d/transform.cpp src/renderer/3d/animation.cpp src/renderer/3d/modelanimator.cpp src/io/filemeta.cpp src/io/vfs.cpp src/io/assetpath.cpp src/io/memorystream.cpp src/io/physicalfilestream.cpp src/io/uncompressedstream.cpp src/io/zlibstream.cpp src/io/vfsarchive.cpp src/io/mpkarchive.cpp src/io/cpkarchive.cpp src/io/lnk4archive.cpp src/io/textarchive.cpp src/io/afsarchive.cpp src/texture/texture.cpp src/texture/s3tc.cpp src/texture/bcdecode.cpp src/texture/bntxloader.cpp src/texture/gxtloader.cpp src/texture/plainloader.cpp src/texture/stbiloader.cpp src/texture/ddsloader.cpp src/texture/webpdecode.cpp src/vm/vm.cpp src/vm/expression.cpp src/vm/thread.cpp src/vm/inst_system.cpp src/vm/inst_controlflow.cpp src/vm/inst_dialogue.cpp src/vm/inst_gamespecific.cpp src/vm/inst_graphics2d.cpp src/vm/inst_graphics3d.cpp src/vm/inst_misc.cpp src/vm/inst_movie.cpp src/vm/inst_sound.cpp src/vm/interface/scene3d.cpp src/vm/interface/input.cpp src/audio/audiosystem.cpp src/audio/audiochannel.cpp src/audio/audiostream.cpp src/audio/vorbisaudiostream.cpp src/audio/atrac9audiostream.cpp src/audio/adxaudiostream.cpp src/audio/hcaaudiostream.cpp src/video/videosystem.cpp src/video/videoplayer.cpp src/video/clock.cpp src/subtitle/subtitlesystem.cpp ) set(Impacto_Header src/impacto.h src/log.h src/util.h src/workqueue.h src/game.h src/mem.h src/modelviewer.h src/characterviewer.h src/spritesheet.h src/spriteanimation.h src/sequencedanimation.h src/font.h src/background2d.h src/mask2d.h src/character2d.h src/loadable.h src/inputsystem.h src/rng.h src/animation.h src/text/text.h src/text/typewritereffect.h src/text/dialoguepage.h src/effects/wave.h src/effects/blur.h src/effects/mosaic.h src/effects/chlcc/butterflyeffect.h src/effects/chlcc/bubbleseffect.h src/effects/chlcc/eyecatch.h src/renderer/renderer.h src/renderer/window.h src/renderer/yuvframe.h src/renderer/nv12frame.h src/data/savesystem.h src/data/tipssystem.h src/data/achievementsystem.h src/data/achievementsystemps3.h src/profile/profile.h src/profile/profile_internal.h src/profile/game.h src/profile/vfs.h src/profile/scene3d.h src/profile/sprites.h src/profile/animations.h src/profile/charset.h src/profile/fonts.h src/profile/dialogue.h src/profile/vm.h src/profile/scriptvars.h src/profile/scriptinput.h src/profile/configsystem.h src/profile/subtitle.h src/profile/data/bgeff.h src/profile/data/savesystem.h src/profile/data/tipssystem.h src/profile/data/achievementsystem.h src/profile/data/waveeffects.h src/profile/hud/saveicon.h src/profile/hud/loadingdisplay.h src/profile/hud/datedisplay.h src/profile/hud/tipsnotification.h src/profile/ui/commonmenu.h src/profile/ui/systemmenu.h src/profile/ui/titlemenu.h src/profile/ui/savemenu.h src/profile/ui/sysmesbox.h src/profile/ui/selectionmenu.h src/profile/ui/backlogmenu.h src/profile/ui/optionsmenu.h src/profile/ui/tipsmenu.h src/profile/ui/extramenus.h src/profile/ui/trophymenu.h src/profile/ui/helpmenu.h src/profile/ui/gamespecific.h src/profile/games/rne/tilebackground.h src/profile/games/rne/systemmenu.h src/profile/games/rne/titlemenu.h src/profile/games/rne/sysmesbox.h src/profile/games/dash/titlemenu.h src/profile/games/chlcc/commonmenu.h src/profile/games/chlcc/dialoguebox.h src/profile/games/chlcc/titlemenu.h src/profile/games/chlcc/savemenu.h src/profile/games/chlcc/sysmesbox.h src/profile/games/chlcc/clearlistmenu.h src/profile/games/chlcc/moviemenu.h src/profile/games/chlcc/albummenu.h src/profile/games/chlcc/musicmenu.h src/profile/games/chlcc/tipsmenu.h src/profile/games/chlcc/optionsmenu.h src/profile/games/chlcc/backlogmenu.h src/profile/games/chlcc/systemmenu.h src/profile/games/chlcc/trophymenu.h src/profile/games/chlcc/delusiontrigger.h src/profile/games/chlcc/tipsnotification.h src/profile/games/cc/backlogmenu.h src/profile/games/cc/dialoguebox.h src/profile/games/cc/titlemenu.h src/profile/games/cc/sysmesbox.h src/profile/games/cclcc/titlemenu.h src/profile/games/cclcc/savemenu.h src/profile/games/cclcc/optionsmenu.h src/profile/games/cclcc/tipsmenu.h src/profile/games/cclcc/clearlistmenu.h src/profile/games/cclcc/librarymenu.h src/profile/games/cclcc/tipsnotification.h src/profile/games/cclcc/mapsystem.h src/profile/games/cclcc/delusiontrigger.h src/profile/games/cclcc/helpmenu.h src/profile/games/mo6tw/backlogmenu.h src/profile/games/mo6tw/dialoguebox.h src/profile/games/mo6tw/sysmesbox.h src/profile/games/mo6tw/systemmenu.h src/profile/games/mo6tw/titlemenu.h src/profile/games/mo6tw/savemenu.h src/profile/games/mo6tw/optionsmenu.h src/profile/games/mo6tw/tipsmenu.h src/profile/games/mo6tw/clearlistmenu.h src/profile/games/mo6tw/moviemenu.h src/profile/games/mo6tw/actorsvoicemenu.h src/profile/games/mo6tw/musicmenu.h src/profile/games/mo6tw/albummenu.h src/profile/games/mo6tw/tipsnotification.h src/profile/games/mo8/titlemenu.h src/profile/games/mo8/systemmenu.h src/profile/games/mo8/optionsmenu.h src/profile/games/mo8/savemenu.h src/profile/games/darling/sysmesbox.h src/games/rne/tilebackground.h src/games/rne/systemmenu.h src/games/rne/titlemenu.h src/games/rne/sysmesbox.h src/games/dash/titlemenu.h src/games/chlcc/commonmenu.h src/games/chlcc/titlemenu.h src/games/chlcc/savemenu.h src/games/chlcc/sysmesbox.h src/games/chlcc/animations/selectprompt.h src/games/chlcc/animations/menutransition.h src/games/chlcc/animations/saveicon.h src/games/chlcc/savesystem.h src/games/chlcc/tipssystem.h src/games/chlcc/clearlistmenu.h src/games/chlcc/moviemenu.h src/games/chlcc/albummenu.h src/games/chlcc/musicmenu.h src/games/chlcc/tipsmenu.h src/games/chlcc/optionsmenu.h src/games/chlcc/backlogmenu.h src/games/chlcc/systemmenu.h src/games/chlcc/trophymenu.h src/games/chlcc/delusiontrigger.h src/games/chlcc/introsequence.h src/games/cc/titlemenu.h src/games/cc/sysmesbox.h src/games/cclcc/titlemenu.h src/games/cclcc/savemenu.h src/games/cclcc/optionsmenu.h src/games/cclcc/tipsmenu.h src/games/cclcc/clearlistmenu.h src/games/cclcc/librarymenu.h src/games/cclcc/librarysubmenus.h src/games/cclcc/albummenu.h src/games/cclcc/musicmenu.h src/games/cclcc/moviemenu.h src/games/cclcc/mapsystem.h src/games/cclcc/delusiontrigger.h src/games/cclcc/helpmenu.h src/games/mo6tw/sysmesbox.h src/games/mo6tw/systemmenu.h src/games/mo6tw/titlemenu.h src/games/mo6tw/savemenu.h src/games/mo6tw/optionsmenu.h src/games/mo6tw/tipsmenu.h src/games/mo6tw/savesystem.h src/games/mo6tw/tipssystem.h src/games/mo6tw/clearlistmenu.h src/games/mo6tw/moviemenu.h src/games/mo6tw/actorsvoicemenu.h src/games/mo6tw/musicmenu.h src/games/mo6tw/albummenu.h src/games/mo8/titlemenu.h src/games/mo8/systemmenu.h src/games/mo8/optionsmenu.h src/games/mo8/savemenu.h src/games/darling/sysmesbox.h src/hud/dialoguebox.h src/hud/defaultsaveiconanimation.h src/hud/datedisplay.h src/hud/saveicondisplay.h src/hud/loadingdisplay.h src/hud/nametagdisplay.h src/hud/autoicondisplay.h src/hud/skipicondisplay.h src/hud/waiticondisplay.h src/hud/tipsnotification.h src/hud/cc/dialoguebox.h src/hud/cc/nametagdisplay.h src/hud/cclcc/tipsnotification.h src/hud/chlcc/dialoguebox.h src/hud/chlcc/nametagdisplay.h src/hud/chlcc/tipsnotification.h src/hud/mo6tw/dialoguebox.h src/hud/mo6tw/tipsnotification.h src/hud/rne/datedisplay.h src/ui/ui.h src/ui/menu.h src/ui/nullmenu.h src/ui/selectionmenu.h src/ui/sysmesbox.h src/ui/backlogmenu.h src/ui/tipsmenu.h src/ui/optionsmenu.h src/ui/widget.h src/ui/widgets/label.h src/ui/widgets/button.h src/ui/widgets/backlogentry.h src/ui/widgets/scrollbar.h src/ui/widgets/toggle.h src/ui/widgets/optiongroup.h src/ui/widgets/group.h src/ui/widgets/carousel.h src/ui/widgets/cgviewer.h src/ui/widgets/clickarea.h src/ui/widgets/mo6tw/titlebutton.h src/ui/widgets/mo6tw/saveentrybutton.h src/ui/widgets/mo6tw/tipsentrybutton.h src/ui/widgets/mo6tw/scenelistentry.h src/ui/widgets/mo6tw/imagethumbnailbutton.h src/ui/widgets/mo6tw/albumthumbnailbutton.h src/ui/widgets/mo6tw/actorsvoicebutton.h src/ui/widgets/mo6tw/albumcharacterbutton.h src/ui/widgets/chlcc/titlebutton.h src/ui/widgets/chlcc/saveentrybutton.h src/ui/widgets/chlcc/tipsentrybutton.h src/ui/widgets/chlcc/systemmenuentrybutton.h src/ui/widgets/chlcc/systemmessagebutton.h src/ui/widgets/chlcc/moviemenuentrybutton.h src/ui/widgets/chlcc/albumthumbnailbutton.h src/ui/widgets/chlcc/trackselectbutton.h src/ui/widgets/chlcc/backlogentry.h src/ui/widgets/chlcc/trophymenuentry.h src/ui/widgets/chlcc/optionsentry.h src/ui/widgets/chlcc/optionsbutton.h src/ui/widgets/chlcc/optionsslider.h src/ui/widgets/rne/sysmenubutton.h src/ui/widgets/cc/backlogentry.h src/ui/widgets/cc/titlebutton.h src/ui/widgets/cclcc/titlebutton.h src/ui/widgets/cclcc/optionsentry.h src/ui/widgets/cclcc/optionsbinarybutton.h src/ui/widgets/cclcc/optionsslider.h src/ui/widgets/cclcc/optionsvoiceslider.h src/ui/gamespecific.h src/renderer/3d/camera.h src/renderer/3d/model.h src/renderer/3d/renderable3d.h src/renderer/3d/transform.h src/renderer/3d/scene.h src/renderer/3d/animation.h src/renderer/3d/modelanimator.h src/io/io.h src/io/vfs.h src/io/buffering.h src/io/filemeta.h src/io/assetpath.h src/io/stream.h src/io/vfsarchive.h src/io/memorystream.h src/io/physicalfilestream.h src/io/uncompressedstream.h src/io/zlibstream.h src/texture/texture.h src/texture/s3tc.h src/texture/bcdecode.h src/texture/gxtloader.h src/texture/bntxloader.h src/texture/plainloader.h src/vm/vm.h src/vm/expression.h src/vm/thread.h src/vm/inst_macros.inc src/vm/inst_system.h src/vm/inst_controlflow.h src/vm/inst_dialogue.h src/vm/inst_gamespecific.h src/vm/inst_graphics2d.h src/vm/inst_graphics3d.h src/vm/inst_misc.h src/vm/inst_movie.h src/vm/inst_sound.h src/vm/opcodetables_rne.h src/vm/interface/scene3d.h src/vm/interface/input.h src/audio/audiobackend.h src/audio/audiosystem.h src/audio/audiocommon.h src/audio/audiochannel.h src/audio/audiostream.h src/audio/buffering.h src/audio/ffmpegaudioplayer.h src/audio/vorbisaudiostream.h src/audio/atrac9audiostream.h src/audio/adxaudiostream.h src/audio/hcaaudiostream.h src/video/videosystem.h src/video/videoplayer.h src/video/clock.h src/subtitle/subtitlesystem.h src/subtitle/subtitlerenderer.h ) if (WIN32) list(APPEND Impacto_Src src/manifest_windows.manifest ) endif () add_library(clHCA STATIC vendor/clHCA/clHCA.c) target_include_directories(clHCA PUBLIC vendor/clHCA) add_library(minilua STATIC vendor/minilua/minilua_impl.c) target_include_directories(minilua PUBLIC vendor) add_library(pcg STATIC vendor/pcg/src/pcg_basic.c) target_include_directories(pcg PUBLIC vendor/pcg/include) add_library(squish STATIC vendor/squish/alpha.cpp vendor/squish/clusterfit.cpp vendor/squish/colourblock.cpp vendor/squish/colourfit.cpp vendor/squish/colourset.cpp vendor/squish/maths.cpp vendor/squish/rangefit.cpp vendor/squish/singlecolourfit.cpp vendor/squish/squish.cpp ) target_include_directories(squish PUBLIC vendor/squish) set(Impacto_Lib_Targets clHCA minilua pcg squish ) if (NX OR EMSCRIPTEN) set(IMPACTO_DISABLE_VULKAN ON) set(IMPACTO_DISABLE_DX9 ON) set(IMPACTO_DISABLE_MMAP ON) set(BUILD_SHARED_LIBS OFF) endif () if(ANDROID) set(IMPACTO_DISABLE_IMGUI ON) endif () if (UNIX AND NOT CYGWIN) set(IMPACTO_DISABLE_DX9 ON) endif () if (IMPACTO_ASAN) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") message(STATUS "Enabling ASAN/UBSAN for clang") add_compile_options(-fsanitize=address,undefined) add_link_options(-fsanitize=address,undefined) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") message(STATUS "Enabling ASAN/UBSAN for GCC") add_compile_options(-fsanitize=address,undefined) add_link_options(-fsanitize=address,undefined) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") message(STATUS "Enabling ASAN for MSVC") # Only ASAN supported for MSVC on Windows add_compile_options(/fsanitize=address) else () message(WARNING "Unknown compiler, not enabling ASAN and UBSAN") endif () else () message(STATUS "NOT enabling ASAN and UBSAN, consider enabling them when developing") endif () if(ANDROID OR NX) set(BUILD_SHARED_LIBS OFF) set(LIBATRAC9_BUILD_SHARED OFF) endif() if(NX) add_compile_definitions(__SWITCH__) endif() # dependencies FetchContent_Declare( glm GIT_REPOSITORY https://github.com/g-truc/glm.git GIT_TAG "1.0.1" SYSTEM EXCLUDE_FROM_ALL ) FetchContent_Declare( libatrac9 GIT_TAG "master" GIT_REPOSITORY "https://github.com/Thealexbarney/LibAtrac9.git" PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/vendor/patches/LibAtrac9/CMakeLists.txt /CMakeLists.txt SYSTEM EXCLUDE_FROM_ALL ) if (NOT VCPKG_TOOLCHAIN) FetchContent_Declare( fmt GIT_REPOSITORY "https://github.com/fmtlib/fmt.git" GIT_TAG "11.1.4" SYSTEM EXCLUDE_FROM_ALL ) endif () FetchContent_Declare( pugixml GIT_REPOSITORY "https://github.com/zeux/pugixml.git" GIT_TAG "v1.14" SYSTEM EXCLUDE_FROM_ALL ) FetchContent_Declare( utf8cpp GIT_REPOSITORY "https://github.com/nemtrif/utfcpp" GIT_TAG "v4.0.5" SYSTEM EXCLUDE_FROM_ALL ) FetchContent_Declare( concurrentqueue GIT_REPOSITORY "https://github.com/cameron314/concurrentqueue" GIT_TAG "v1.0.4" SYSTEM EXCLUDE_FROM_ALL ) FetchContent_Declare( readerwriterqueue GIT_REPOSITORY "https://github.com/cameron314/readerwriterqueue" GIT_TAG "16b48ae1148284e7b40abf72167206a4390a4592" SYSTEM EXCLUDE_FROM_ALL ) FetchContent_Declare( unordered_dense GIT_REPOSITORY "https://github.com/martinus/unordered_dense" GIT_TAG "v4.5.0" SYSTEM EXCLUDE_FROM_ALL ) FetchContent_Declare( magic_enum GIT_TAG "7d87efb4a3dddbbe8caa9ca14eff05ede1102ab8" GIT_REPOSITORY "https://github.com/Neargye/magic_enum.git" SYSTEM EXCLUDE_FROM_ALL ) if (NOT VCPKG_TOOLCHAIN) FetchContent_MakeAvailable(fmt) endif () FetchContent_MakeAvailable(glm) FetchContent_MakeAvailable(pugixml) FetchContent_MakeAvailable(utf8cpp) FetchContent_MakeAvailable(libatrac9) FetchContent_MakeAvailable(concurrentqueue) FetchContent_MakeAvailable(readerwriterqueue) FetchContent_MakeAvailable(unordered_dense) FetchContent_MakeAvailable(magic_enum) if (NOT DEFINED IMPACTO_DISABLE_MMAP) list(APPEND Impacto_Src src/io/memorymappedfilestream.cpp ) list(APPEND Impacto_Header src/io/memorymappedfilestream.h ) list(APPEND Impacto_Include_Dirs vendor/mio ) endif () if (NOT DEFINED IMPACTO_DISABLE_IMGUI) FetchContent_Declare( ImGui GIT_REPOSITORY "https://github.com/ocornut/imgui.git" GIT_TAG "1db579d458da29fa43376af9d88d486910d9406a" ) FetchContent_MakeAvailable(ImGui) endif () if (WIN32) # Workaround for RelWithDebInfo builds not installing all the libraries set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL Release) set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release) set(SDL2_LIBRARIES SDL2::SDL2 SDL2::SDL2main) endif () if (NOT NX) # avoid CMAKE_DL_LIBS for NX list(APPEND Impacto_Libs ${CMAKE_DL_LIBS} ) endif () list(APPEND Impacto_Include_Dirs ${CMAKE_CURRENT_SOURCE_DIR}/vendor) list(APPEND Impacto_Include_Dirs ${CMAKE_CURRENT_SOURCE_DIR}/vendor/include) if (NOT DEFINED IMPACTO_DISABLE_OPENGL) list(APPEND Impacto_Include_Dirs ${CMAKE_CURRENT_SOURCE_DIR}/vendor/glad/include) endif () if(ANDROID) set(SDL2_LIBRARIES SDL2::SDL2-static SDL2::SDL2main) if (NOT DEFINED IMPACTO_DISABLE_OPENGL) find_library(GLESv3 GLESv3) list(APPEND Impacto_Libs GLESv3 ) endif () endif () if (NOT DEFINED IMPACTO_DISABLE_OPENAL) find_package(OpenAL REQUIRED) list(APPEND Impacto_Libs ${OPENAL_LIBRARY} ) list(APPEND Impacto_Src src/audio/openal/audiobackend.cpp src/audio/openal/openalaudiochannel.cpp src/audio/openal/ffmpegaudioplayer.cpp ) list(APPEND Impacto_Header src/audio/openal/audiobackend.h src/audio/openal/openalaudiochannel.h src/audio/openal/audiocommon.h src/audio/openal/ffmpegaudioplayer.h ) list(APPEND Impacto_Include_Dirs ${OPENAL_INCLUDE_DIR}) endif () if (EMSCRIPTEN) # BINARYEN_TRAP_MODE=clamp => https://groups.google.com/forum/#!topic/emscripten-discuss/IJr4ApiW_zU # duk_heap_alloc() errors without this # MAIN_MODULE=1 => SDL_GL_GetProcAddress falls back to dlsym() so we need dynamic linking support (...) set(IMPACTO_EMSCRIPTEN_BUILD_FLAGS "\ -s USE_SDL=2 \ -s USE_ZLIB=1 \ -s USE_OGG=1 \ -s USE_VORBIS=1 \ -s WASM=1 \ -s USE_WEBGL2=1 \ -s MAIN_MODULE=2 \ -s BINARYEN_TRAP_MODE=clamp \ -s ALLOW_MEMORY_GROWTH=1 \ -g4 \ --preload-file ${CMAKE_CURRENT_SOURCE_DIR}/profiles@/profiles \ --preload-file ${CMAKE_CURRENT_SOURCE_DIR}/games@/games \ --preload-file ${CMAKE_CURRENT_SOURCE_DIR}/src/shaders@/shaders \ ") if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") set(IMPACTO_EMSCRIPTEN_BUILD_FLAGS "${IMPACTO_EMSCRIPTEN_BUILD_FLAGS} \ -s GL_ASSERTIONS=1 \ -s GL_DEBUG=1 \ ") else () set(IMPACTO_EMSCRIPTEN_BUILD_FLAGS "${IMPACTO_EMSCRIPTEN_BUILD_FLAGS} \ -O3 \ ") endif () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${IMPACTO_EMSCRIPTEN_BUILD_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${IMPACTO_EMSCRIPTEN_BUILD_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${IMPACTO_EMSCRIPTEN_BUILD_FLAGS}") string(REPLACE "-O2" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") string(REPLACE "-O2" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") string(REPLACE "-O2" "" CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") else () find_package(SDL2 CONFIG REQUIRED) find_package(ZLIB REQUIRED) set(SDL2_LIBRARIES "$" "$,SDL2::SDL2,SDL2::SDL2-static>" ) if (NX) pkg_check_modules(ogg REQUIRED IMPORTED_TARGET ogg) pkg_check_modules(vorbis REQUIRED IMPORTED_TARGET vorbis) pkg_check_modules(vorbisfile REQUIRED IMPORTED_TARGET vorbisfile) pkg_check_modules(libwebp REQUIRED IMPORTED_TARGET libwebp) list(APPEND Impacto_Libs ${SDL2_LIBRARIES} ZLIB::ZLIB PkgConfig::vorbisfile PkgConfig::vorbis PkgConfig::ogg PkgConfig::libwebp EGL glapi drm_nouveau nx ) else () find_package(Ogg CONFIG REQUIRED) find_package(Vorbis CONFIG REQUIRED) find_package(WebP CONFIG REQUIRED) list(APPEND Impacto_Libs ${SDL2_LIBRARIES} ZLIB::ZLIB WebP::webp WebP::webpdecoder WebP::webpdemux Ogg::ogg Vorbis::vorbis Vorbis::vorbisfile ) add_definitions(-DIMPACTO_OPENAL_HAVE_ALEXT) endif () endif () list(APPEND Impacto_Include_Dirs ${SDL2_INCLUDE_DIRS}) list(APPEND Impacto_Include_Dirs ${WebP_INCLUDE_DIRS}) if (VCPKG_TOOLCHAIN) find_package(fmt CONFIG REQUIRED) endif () list(APPEND Impacto_Libs fmt::fmt) list(APPEND Impacto_Libs pugixml::pugixml atrac9 unordered_dense::unordered_dense magic_enum glm::glm utf8cpp concurrentqueue readerwriterqueue ) if (NOT DEFINED IMPACTO_DISABLE_LIBASS) list(APPEND Impacto_Src src/subtitle/ass/subtitlerenderer.cpp) list(APPEND Impacto_Header src/subtitle/ass/subtitlerenderer.h) pkg_check_modules(ass REQUIRED IMPORTED_TARGET GLOBAL libass) # Libass links libc++ which overrides the static stllib linkage if(ANDROID) get_target_property(ASS_LINK_LIBS PkgConfig::ass INTERFACE_LINK_LIBRARIES) set(CLEANED_ASS_LINK_LIBS "") foreach(LIB_PATH IN LISTS ASS_LINK_LIBS) get_filename_component(LIB_NAME "${LIB_PATH}" NAME) # Check if the filename starts with "libc++." or "libc++_" if(NOT ("${LIB_NAME}" MATCHES "^libc\\+\\+\\." OR "${LIB_NAME}" MATCHES "^libc\\+\\+_")) list(APPEND CLEANED_ASS_LINK_LIBS "${LIB_PATH}") endif() endforeach() set_target_properties(PkgConfig::ass PROPERTIES INTERFACE_LINK_LIBRARIES "${CLEANED_ASS_LINK_LIBS}") endif() list(APPEND Impacto_Libs PkgConfig::ass ) endif() if (NOT DEFINED IMPACTO_DISABLE_IMGUI) set(imgui_src ${imgui_SOURCE_DIR}/imgui.cpp ${imgui_SOURCE_DIR}/imgui_demo.cpp ${imgui_SOURCE_DIR}/imgui_draw.cpp ${imgui_SOURCE_DIR}/imgui_tables.cpp ${imgui_SOURCE_DIR}/imgui_widgets.cpp ${imgui_SOURCE_DIR}/misc/cpp/imgui_stdlib.cpp ${imgui_SOURCE_DIR}/backends/imgui_impl_sdl2.cpp ) set(imgui_Include_Dirs ${imgui_SOURCE_DIR} ${imgui_SOURCE_DIR}/backends ) list(APPEND Impacto_Src src/debugmenu.cpp) list(APPEND Impacto_Header src/debugmenu.h) if (NOT DEFINED IMPACTO_DISABLE_OPENGL) list(APPEND imgui_src vendor/imgui_custom/backends/imgui_impl_opengl3.cpp ) list(APPEND imgui_Include_Dirs vendor/imgui_custom/backends ) endif () if (NOT DEFINED IMPACTO_DISABLE_VULKAN) list(APPEND imgui_src ${imgui_SOURCE_DIR}/backends/imgui_impl_vulkan.cpp ) endif () if (NOT DEFINED IMPACTO_DISABLE_DX9) list(APPEND imgui_src ${imgui_SOURCE_DIR}/backends/imgui_impl_dx9.cpp ) endif () add_library(imgui STATIC ${imgui_src}) target_include_directories(imgui PUBLIC ${imgui_Include_Dirs} PRIVATE ${SDL2_INCLUDE_DIRS} ) target_link_libraries(imgui PRIVATE ${SDL2_LIBRARIES}) list(APPEND Impacto_Lib_Targets imgui) endif () if (NOT DEFINED IMPACTO_DISABLE_OPENGL) list(APPEND Impacto_Src src/renderer/opengl/window.cpp src/renderer/opengl/renderer.cpp src/renderer/opengl/shader.cpp src/renderer/opengl/glc.cpp src/renderer/opengl/yuvframe.cpp src/renderer/opengl/nv12frame.cpp src/renderer/opengl/3d/renderable3d.cpp src/renderer/opengl/3d/scene.cpp vendor/glad/src/glad.c ) list(APPEND Impacto_Header src/renderer/opengl/window.h src/renderer/opengl/renderer.h src/renderer/opengl/shader.h src/renderer/opengl/glc.h src/renderer/opengl/yuvframe.h src/renderer/opengl/nv12frame.h src/renderer/opengl/3d/renderable3d.h src/renderer/opengl/3d/scene.h ) endif () if (NOT DEFINED IMPACTO_DISABLE_FFMPEG) list(APPEND Impacto_Src src/video/ffmpegplayer.cpp src/video/ffmpegstream.cpp src/subtitle/ffmpegsubtitlehelper.cpp ) list(APPEND Impacto_Header src/video/ffmpegplayer.h src/video/ffmpegstream.h src/subtitle/ffmpegsubtitlehelper.h ) if (NOT VCPKG_TOOLCHAIN) if(NOT BUILD_SHARED_LIBS) set(AV_ENABLE_SHARED OFF CACHE BOOL "Enable shared library build (Off)" FORCE) endif() set(AV_DISABLE_AVDEVICE ON CACHE INTERNAL "Disable FFMPEG AVDevice") set(AV_DISABLE_AVFILTER ON CACHE INTERNAL "Disable FFMPEG AVFilter") set(AV_BUILD_EXAMPLES OFF CACHE INTERNAL "Build AVCPP Examples") FetchContent_Declare( avcpp GIT_REPOSITORY "https://github.com/h4tr3d/avcpp" GIT_TAG "6bf8c76b120cf2cc448d706ea22f112017803605" SYSTEM GIT_SUBMODULES "" PATCH_COMMAND ${AVCPP_PATCH} UPDATE_DISCONNECTED 1 ) FetchContent_MakeAvailable(avcpp) pkg_check_modules(FFmpeg REQUIRED IMPORTED_TARGET libavcodec libavutil libswscale libswresample libavformat ) list(APPEND Impacto_Libs avcpp PkgConfig::FFmpeg ) else () find_package(FFMPEG REQUIRED) list(APPEND Impacto_Include_Dirs ${FFMPEG_INCLUDE_DIRS}) list(APPEND Impacto_Libs ${FFMPEG_LIBRARIES}) find_package(avcpp CONFIG REQUIRED) list(APPEND Impacto_Libs avcpp::avcpp avcpp::FFmpeg ) endif () list(APPEND Impacto_Include_Dirs ${FFMPEG_INCLUDE_DIRS}) list(APPEND Impacto_Libs ${FFMPEG_LIBRARIES}) pkg_check_modules(dav1d REQUIRED IMPORTED_TARGET dav1d) list(APPEND Impacto_Libs PkgConfig::dav1d) if(ANDROID) find_library(mediandk-lib mediandk) list(APPEND Impacto_Libs ${mediandk-lib}) endif() endif () if (NOT DEFINED IMPACTO_DISABLE_MSPACK) add_library(mspack STATIC vendor/mspack/lzxd.c vendor/mspack/system.c ) target_include_directories(mspack PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/vendor/mspack) list(APPEND Impacto_Lib_Targets mspack) list(APPEND Impacto_Src src/io/lzxstream.cpp) list(APPEND Impacto_Header src/io/lzxstream.h) endif () if (NOT DEFINED IMPACTO_DISABLE_VULKAN) list(APPEND Impacto_Src src/renderer/vulkan/window.cpp src/renderer/vulkan/renderer.cpp src/renderer/vulkan/pipeline.cpp src/renderer/vulkan/utils.cpp src/renderer/vulkan/yuvframe.cpp src/renderer/vulkan/nv12frame.cpp src/renderer/vulkan/3d/renderable3d.cpp src/renderer/vulkan/3d/scene.cpp ) list(APPEND Impacto_Header src/renderer/vulkan/window.h src/renderer/vulkan/renderer.h src/renderer/vulkan/pipeline.h src/renderer/vulkan/utils.h src/renderer/vulkan/yuvframe.h src/renderer/vulkan/nv12frame.h src/renderer/vulkan/3d/renderable3d.h src/renderer/vulkan/3d/scene.h ) find_package(Vulkan REQUIRED) list(APPEND Impacto_Libs ${Vulkan_LIBRARIES} ) list(APPEND Impacto_Include_Dirs ${Vulkan_INCLUDE_DIRS}) endif () if (NOT DEFINED IMPACTO_DISABLE_DX9) list(APPEND Impacto_Src src/renderer/dx9/renderer.cpp src/renderer/dx9/window.cpp src/renderer/dx9/shader.cpp src/renderer/dx9/yuvframe.cpp src/renderer/dx9/nv12frame.cpp src/renderer/dx9/3d/scene.cpp src/renderer/dx9/3d/renderable3d.cpp ) list(APPEND Impacto_Header src/renderer/dx9/utils.h src/renderer/dx9/renderer.h src/renderer/dx9/window.h src/renderer/dx9/shader.h src/renderer/dx9/yuvframe.h src/renderer/dx9/nv12frame.h src/renderer/dx9/3d/scene.h src/renderer/dx9/3d/renderable3d.h ) list(APPEND Impacto_Libs d3d9 d3dcompiler ) endif () if(ANDROID) add_library(impacto SHARED ${Impacto_Src} ${Impacto_Header}) target_link_options(impacto PRIVATE "LINKER:--build-id=sha1") else() add_executable(impacto ${Impacto_Src} ${Impacto_Header}) endif() set_property(TARGET impacto PROPERTY COMPILE_WARNING_AS_ERROR ON) if(MSVC) set_property(TARGET impacto PROPERTY WIN32_EXECUTABLE TRUE) endIf() list(APPEND Impacto_Libs ${Impacto_Lib_Targets}) target_link_libraries(impacto PRIVATE ${Impacto_Libs}) target_compile_options(impacto PRIVATE "$<$:/utf-8>") target_compile_options(impacto PRIVATE "$<$:/utf-8>") if (IMPACTO_WARNINGS) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") message(STATUS "Enabling warnings for clang") target_compile_options(impacto PRIVATE -Wall -Wextra -Wno-unused-parameter -Wno-nullability-completeness) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") message(STATUS "Enabling warnings for GCC") target_compile_options(impacto PRIVATE -Wall -Wextra -Wno-unused-parameter) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") message(STATUS "Enabling warnings for MSVC") target_compile_options(impacto PRIVATE /W4 /external:anglebrackets /external:W0 /wd4100 /wd4324 /w15038 /w44388 /w44062) else () message(WARNING "Unknown compiler, not enabling warnings") endif () else () message(STATUS "NOT enabling compiler warnings, consider enabling them when developing") endif () # compiler/dependency configuration target_compile_options(impacto PRIVATE ${Impacto_Compile_Options}) set_property(TARGET impacto ${Impacto_Lib_Targets} PROPERTY CXX_STANDARD 20) set_property(TARGET impacto PROPERTY CXX_SCAN_FOR_MODULES OFF) # Hot reload for MSVC if(NOT IMPACTO_ASAN) set_property(TARGET impacto PROPERTY MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") endif() if (EMSCRIPTEN) set(CMAKE_EXECUTABLE_SUFFIX ".html") endif () if (NX) add_definitions(-DENV64BIT) else () if (CMAKE_SIZEOF_VOID_P EQUAL 8) add_definitions(-DENV64BIT) else () add_definitions(-DENV32BIT) endif () endif () add_definitions(-DGLM_FORCE_RADIANS) # lol # target configuration if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") set(IMPACTO_ENABLE_SLOW_LOG_DEFAULT ON) set(IMPACTO_GL_DEBUG_DEFAULT ON) else () set(IMPACTO_ENABLE_SLOW_LOG_DEFAULT OFF) set(IMPACTO_GL_DEBUG_DEFAULT OFF) endif () option(IMPACTO_ENABLE_SLOW_LOG "Compile log statements that get hit very frequently or are in a hot path" ${IMPACTO_ENABLE_SLOW_LOG_DEFAULT}) option(IMPACTO_GL_DEBUG "Use an OpenGL debug context and log messages" ${IMPACTO_GL_DEBUG_DEFAULT}) if (EMSCRIPTEN) set(IMPACTO_HAVE_THREADS OFF) set(IMPACTO_USE_SDL_HIGHDPI ON) else () set(IMPACTO_HAVE_THREADS ON) set(IMPACTO_USE_SDL_HIGHDPI OFF) endif () # Enable Hot Reload for MSVC compilers if supported. if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") endif() configure_file(src/config.h.in ${PROJECT_BINARY_DIR}/include/config.h) target_include_directories(impacto SYSTEM BEFORE PRIVATE ${Impacto_Include_Dirs}) target_include_directories(impacto PRIVATE ${PROJECT_BINARY_DIR}/include) target_precompile_headers(impacto PRIVATE "$<$:${CMAKE_SOURCE_DIR}/src/pch.h>" ) # binary install if (ANDROID) install(TARGETS impacto LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/android/app/src/main/jniLibs/${CMAKE_ANDROID_ARCH_ABI}) elseif (NX) install(TARGETS impacto RUNTIME DESTINATION .) elseif (WIN32) install(TARGETS impacto RUNTIME DESTINATION .) install(FILES $ DESTINATION .) x_vcpkg_install_local_dependencies(TARGETS impacto DESTINATION .) else() if (APPLE) set_property( TARGET impacto PROPERTY INSTALL_RPATH "@loader_path/" "@loader_path/lib" ) elseif (LINUX) set_property( TARGET impacto PROPERTY INSTALL_RPATH "$ORIGIN/" "$ORIGIN/lib" ) endif() LIST(APPEND post_exclude_regexes "^/lib" "^/usr" "^/bin" "libvulkan\\.so(\\..*)?") install(TARGETS impacto atrac9 RUNTIME_DEPENDENCIES POST_EXCLUDE_REGEXES ${post_exclude_regexes} RUNTIME DESTINATION . LIBRARY DESTINATION ./lib FRAMEWORK DESTINATION ./lib ) endif () # asset install if (NOT ANDROID) install(DIRECTORY src/shaders DESTINATION .) install(DIRECTORY profiles DESTINATION .) install(DIRECTORY games DESTINATION .) else() install(DIRECTORY src/shaders DESTINATION ${CMAKE_SOURCE_DIR}/android/app/src/main/assets) install(DIRECTORY profiles DESTINATION ${CMAKE_SOURCE_DIR}/android/app/src/main/assets) install(DIRECTORY games DESTINATION ${CMAKE_SOURCE_DIR}/android/app/src/main/assets PATTERN "*/gamedata/*" EXCLUDE) endif() ================================================ FILE: CMakePresets.json ================================================ { "version": 3, "cmakeMinimumRequired": { "major": 3, "minor": 25, "patch": 0 }, "configurePresets": [ { "name": "Base", "description": "Default Base Configuration", "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/impacto-build/${presetName}", "installDir": "${sourceDir}/install/${presetName}", "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_EXPORT_COMPILE_COMMANDS": "YES", "VCPKG_OVERLAY_PORTS": "${sourceDir}/portfiles", "IMPACTO_WARNINGS": "ON" } }, { "name": "Release", "description": "Default Release Build", "hidden": false, "inherits": [ "Base" ], "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } }, { "name": "Debug", "description": "Default Debug Build", "hidden": false, "inherits": [ "Base" ], "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "ci-release", "description": "Release build for CI", "hidden": false, "generator": "Ninja", "binaryDir": "ci-build/${presetName}", "installDir": "release/${presetName}", "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/triplets", "VCPKG_OVERLAY_PORTS": "${sourceDir}/portfiles", "IMPACTO_WARNINGS": "ON" } }, { "name": "ci-release-android", "description": "Android Release build for CI", "hidden": false, "inherits": [ "ci-release" ], "cacheVariables": { "VCPKG_TARGET_TRIPLET":"arm64-android-ci", "VCPKG_CHAINLOAD_TOOLCHAIN_FILE":"$env{VCPKG_ROOT}/scripts/toolchains/android.cmake", "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY": "BOTH", "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE": "BOTH", "CMAKE_FIND_ROOT_PATH_MODE_PACKAGE": "BOTH", "ANDROID_PLATFORM":"android-$env{MINSDKVERSION}", "ANDROID_ABI": "arm64-v8a", "ANDROID_STL": "c++_static" } } ], "buildPresets": [ { "name": "ci-release", "description": "x64 Release", "configurePreset": "ci-release", "targets": [ "install" ] }, { "name": "ci-release-android", "description": "x64 Release Android", "configurePreset": "ci-release-android", "targets": [ "install" ] }, { "name": "x64-Release", "description": "x64 Release with Debug Symbols", "configurePreset": "Release", "targets": [ "install" ] }, { "name": "x64-Debug", "description": "x64 Debug", "configurePreset": "Debug", "targets": [ "install" ] } ] } ================================================ FILE: HorizonNX.toolchain ================================================ set(CMAKE_SYSTEM_NAME "Generic") set(WITH_PORTLIBS ON CACHE BOOL "use portlibs ?") macro(msys_to_cmake_path MsysPath ResultingPath) if(WIN32) string(REGEX REPLACE "^/([a-zA-Z])/" "\\1:/" ${ResultingPath} "${MsysPath}") else() set(${ResultingPath} "${MsysPath}") endif() endmacro() msys_to_cmake_path("$ENV{DEVKITPRO}" DEVKITPRO) set(NX 1) list(APPEND CMAKE_PREFIX_PATH "${DEVKITPRO}/portlibs/switch/lib/cmake") if(WIN32) set(CMAKE_C_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc.exe") set(CMAKE_CXX_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++.exe") set(CMAKE_AR "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc-ar.exe" CACHE STRING "") set(CMAKE_RANLIB "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc-ranlib.exe" CACHE STRING "") else() set(CMAKE_C_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc") set(CMAKE_CXX_COMPILER "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++") set(CMAKE_AR "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc-ar" CACHE STRING "") set(CMAKE_RANLIB "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc-ranlib" CACHE STRING "") endif() set(PKG_CONFIG_EXECUTABLE "${DEVKITPRO}/portlibs/switch/bin/aarch64-none-elf-pkg-config" CACHE STRING "") set(CMAKE_C_FLAGS "${CPPFLAGS} -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIC -ffunction-sections" CACHE STRING "C flags") set(CMAKE_CXX_FLAGS "${CPPFLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "C++ flags") if(WITH_PORTLIBS) set(CMAKE_FIND_ROOT_PATH ${DEVKITPRO}/devkitA64 ${DEVKITPRO} ${DEVKITPRO}/libnx ${DEVKITPRO}/portlibs/switch) else() set(CMAKE_FIND_ROOT_PATH ${DEVKITPRO}/devkitA64 ${DEVKITPRO}/libnx ${DEVKITPRO}) endif() set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) #set(CMAKE_STATIC_LINKER_FLAGS_INIT "-march=armv8-a -mtune=cortex-a57 -mtp=soft -L${DEVKITPRO}/libnx/lib -L${DEVKITPRO}/portlibs/switch/lib") set(CMAKE_EXE_LINKER_FLAGS_INIT "-specs=${DEVKITPRO}/libnx/switch.specs -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -L${DEVKITPRO}/libnx/lib -L${DEVKITPRO}/portlibs/switch/lib") set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available") ================================================ FILE: LICENSE ================================================ Copyright (c) 2024 Committee of Zero and contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ================================================ FILE: README.md ================================================ # impacto [![impacto](https://github.com/CommitteeOfZero/impacto/actions/workflows/impacto.yml/badge.svg)](https://github.com/CommitteeOfZero/impacto/actions/workflows/impacto.yml) **impacto** is an open-source **reimplementation** of the **"MAGES." visual novel engine** in C++ and OpenGL. Using the original data files, impacto can run any supported game on any supported platform. ## Status **impacto is in active development**. At current stage it should be possible to reach all endings in at least one supported game - "Memories Off 6 \~T-Wave\~." However, some functionality and architectural design are still missing and **no games are fully complete yet**. Refer to [#1](https://github.com/CommitteeOfZero/impacto/issues/1) for supported games and remaining work. impacto is currently being developed for **64-bit Windows 10 and desktop Linux PCs** but we've successfully experimented on macOS, Android (ARM), Switch (homebrew and Linux) and HTML5/WebAssembly (via Emscripten) and full official Android support is planned. OpenGL is used for the graphics renderer with experimental Vulkan and DirectX 9 options available. ## Building For building on Windows with Visual Studio 2019 or newer, please refer to the [building instructions](doc/vs_build.md). For building on Linux, see the [instructions for Ubuntu Desktop](doc/ubuntu_build.md) and adapt to your distribution if necessary. More platforms and toolchains are known to work. ## Contributing **We are looking for contributors!** Check out the [Getting Started guide](doc/getting_started.md) for pointers on setting up games for testing, finding your way around the codebase and adding functionality. Also check out the [Contributor guide](doc/contributor_guide.md) for important information on the code style you should follow. There is work to be done for C++ programmers of any skill level in a wide range of subjects, on game engine architecture (design and implementation), reverse-engineering the original (looking at just the game's outside behaviour as a *black box* or inspecting the internals through a *white box*), replicating it, improving on it with new functionality, documenting our efforts, fixing bugs and polish. If you're interested, [come join our Discord](https://discord.gg/rq4GGCh) to discuss ideas and help you get into it. ## Legal stuff impacto source code as a whole is released under the liberal [ISC license](LICENSE). Some parts are based on or copied from third-party code under various licenses. Binary distributions or parts thereof may fall under more restrictive licensing terms. See [THIRDPARTY.md](THIRDPARTY.md) for details and attribution. ================================================ FILE: THIRDPARTY.md ================================================ # Third-party component index **impacto contains third-party code at the following locations in the source distribution:** * `src/audio/adxaudiostream.cpp`: based on [vgmstream](https://github.com/losnoco/vgmstream) ADX implementation * `src/io/cpkarchive.cpp`: CRILAYLA decompression based on work by [tpu](https://forum.xentax.com/viewtopic.php?f=21&t=5137&hilit=CRILAYLA) and [hcs64/vgm_ripping/utf_tab](https://github.com/hcs64/vgm_ripping/tree/master/multi/utf_tab) * `src/texture/bcdecode.cpp`: based on [bcndecode](https://github.com/ifeherva/bcndecode) * `src/texture/bntxloader.cpp`: Unswizzling code based on [Ryujinx](https://github.com/Ryujinx/Ryujinx) * `src/texture/gxtloader.cpp`: Unswizzling code based on [Scarlet](https://github.com/xdanieldzd/Scarlet/blob/d8aabf430307d35a81b131e40bb3c9a4828bdd7b/Scarlet/Drawing/ImageBinary.cs) and work by [FireyFly](http://xen.firefly.nu/up/rearrange.c.html) and [ryg](https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/) * `src/texture/s3tc.cpp`: based on [s3tc-dxt-decompression](https://github.com/Benjamin-Dobell/s3tc-dxt-decompression) * `src/texture/ddsloader.cpp`: based on [OpenImageIO](https://github.com/OpenImageIO/oiio/tree/master/src/dds.imageio) * `src/video/ffmpegplayer.cpp`: syncing code based on [FFplay](https://github.com/FFmpeg/FFmpeg/blob/master/fftools/ffplay.c) * `vendor/clHCA`: part of [vgmstream](https://github.com/losnoco/vgmstream) * `vendor/minilua`: [minilua](https://github.com/edubart/minilua) * `vendor/glad`: output from [glad](https://github.com/Dav1dde/glad) generator, patched for Switch support * `vendor/include/stb_image.h`: [stb_image](https://github.com/nothings/stb) * `vendor/imgui`: [Dear ImGui](https://github.com/ocornut/imgui) * `vendor/pcg`: [PCG Random Number Generation, Minimal C Edition](https://github.com/imneme/pcg-c-basic) * `vendor/squish`: [Squish](http://sjbrown.co.uk/?code=squish) * `vendor/utf8-cpp`: [UTF8-CPP](https://github.com/nemtrif/utfcpp) * `vendor/mspack`: [libmspack](https://www.cabextract.org.uk/libmspack/), only includes LZX decompressor to reduce code size * `vendor/mio`: [mio](https://github.com/vimpunk/mio), except for platforms without mmap support All third-party code mentioned above is mandatory, included in the build process and compiled into the output executable for impacto on every supported platform and build configuration. **Additionally, impacto depends on the following external libraries:** * Platform implementations of C runtime, C++ STL, OpenAL and OpenGL (ES) * [glm](https://github.com/g-truc/glm/) * [LibAtrac9](https://github.com/Thealexbarney/LibAtrac9) * [Ogg](https://github.com/xiph/ogg) * [SDL2](https://libsdl.org/download-2.0.php) * [Vorbis](https://github.com/xiph/vorbis) * [zlib](https://github.com/madler/zlib) * [libass](https://github.com/libass/libass) * [avcpp](https://github.com/h4tr3d/avcpp) * [concurrent_queue](https://github.com/cameron314/concurrentqueue) * [readerwriterqueue](https://github.com/cameron314/readerwriterqueue) * [fmtlib](https://github.com/fmtlib/fmt) * [pugixml](https://github.com/zeux/pugixml) * [UTF8-CPP](https://github.com/nemtrif/utfcpp) * [magic_enum](https://github.com/Neargye/magic_enum) **Sourcing and linkage:** * Refer to individual toolchain/platform documentation for standard library licenses and default external library sourcing behaviour. * Emscripten, NX, Android builds use static linking for all other external libraries, other platforms use dynamic linking. * Some external dependencies (due to being header only libraries) are always compiled into the output executable. * The example Emscripten build process explicitly uses emscripten-ports packages for [Ogg](https://github.com/emscripten-ports/Ogg), [SDL2](https://github.com/emscripten-ports/SDL2), [Vorbis](https://github.com/emscripten-ports/Vorbis) and [zlib](https://github.com/emscripten-ports/zlib). * Win32/MSVC, Linux, Android, Mac OS X build processes explicitly uses vcpkg packages listed in vcpkg.json, and the original authors' source distributions for other dependencies. * Win32 install targets copy all required DLLs except the standard C/C++ runtime libraries to the output folder. **Binary licensing:** * Unless specified otherwise, impacto source and binaries are covered by [the ISC license](LICENSE) but contain and use the dependencies listed above, requiring third-party copyright notices. * NX builds statically link OpenAL Soft (assuming this is used as the OpenAL implementation), placing NX binaries under LGPLv2 or later. * Android builds statically link FFmpeg, placing those binaries under LGPLv2. To disable FFmpeg, use define -DIMPACTO_DISABLE_FFMPEG. * All builds ship with libmspack in the impacto executable, placing it under LGPLv2 or later. If you wish to disable libmspack and get rid of LGPLv2 dependency, please use -DIMPACTO_DISABLE_MSPACK define. License statements for third-party code in this repository (where given) and external libraries bundled by the build process in at least one supported configuration follow: # Statements ## bcndecode https://github.com/ifeherva/bcndecode/blob/5bc7043002d7b2485c857624f5ef6f55576ba8b4/src/bcndecode.c > decoder for DXTn-compressed data > > Format documentation: > http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt > > The contents of this file are in the public domain (CC0) > Full text of the CC0 license: > https://creativecommons.org/publicdomain/zero/1.0/ > > To test: > compile: gcc -Iinclude -DBCN_DECODER_TEST BcnDecode.c -o bcndecode > run: dd bs=1 skip=128 if=bc3_test.dds | ./bcndecode 256 256 3 1 > bc3_test.png See below for license text. ## Magic Enum https://github.com/Neargye/magic_enum Copyright (c) 2019 - 2024 Daniil Goncharov See below for license text(MIT) ## MiniLua Copyright (c) 1994–2019 Lua.org, PUC-Rio. Copyright (c) 2020-2023 Eduardo Bart (https://github.com/edubart). See below for license text(MIT) ## Emscripten https://github.com/emscripten-core/emscripten/tree/1.38.21 Emscripten is available under 2 licenses, the MIT license and the University of Illinois/NCSA Open Source License. See below for license texts. Authors: > The following authors have all licensed their contributions to Emscripten > under the licensing terms detailed in LICENSE. > > (Authors keep copyright of their contributions, of course; they just grant > a license to everyone to use it as detailed in LICENSE.) > > * Alon Zakai (copyright owned by Mozilla Foundation) > * Tim Dawborn > * Max Shawabkeh > * Sigmund Vik > * Jeff Terrace > * Benoit Tremblay > * Andreas Bergmeier > * Ben Schwartz > * David Claughton > * David Yip > * Julien Hamaide > * Ehsan Akhgari (copyright owned by Mozilla Foundation) > * Adrian Taylor > * Richard Assar > * Nathan Hammond > * Behdad Esfahbod > * David Benjamin > * Pierre Renaux > * Brian Anderson > * Jon Bardin > * Jukka Jylänki > * Aleksander Guryanov > * Chad Austin (copyright owned by IMVU) > * nandhp > * YeZhongWen > * Xingxing Pan > * Justin Kerk > * Andrea Bedini > * James Pike > * Mokhtar Naamani > * Benjamin Stover > * Riccardo Magliocchetti > * Janus Troelsen > * Lars Schneider (copyright owned by Autodesk, Inc.) > * Joel Martin > * Manuel Wellmann (copyright owned by Autodesk, Inc.) > * Xuejie Xiao > * Dominic Wong > * Alan Kligman (copyright owned by Mozilla Foundation) > * Anthony Liot > * Michael Riss > * Jasper St. Pierre > * Manuel Schölling > * Bruce Mitchener, Jr. > * Michael Bishop > * Roger Braun > * Vladimir Vukicevic (copyright owned by Mozilla Foundation) > * Lorant Pinter > * Tobias Doerffel > * Martin von Gagern > * Ting-Yuan Huang > * Joshua Granick > * Felix H. Dahlke > * Éloi Rivard > * Alexander Gladysh > * Arlo Breault > * Jacob Lee (copyright owned by Google, Inc.) > * Joe Lee (copyright owned by IMVU) > * Andy Friesen (copyright owned by IMVU) > * Bill Welden (copyright owned by IMVU) > * Michael Ey (copyright owned by IMVU) > * Llorens Marti Garcia (copyright owned by IMVU) > * Jinsuck Kim (copyright owned by IMVU) > * Todd Lee (copyright owned by IMVU) > * Anthony Pesch > * Robert Bragg (copyright owned by Intel Corporation) > * Sylvestre Ledru > * Tom Fairfield > * Anthony J. Thibault > * John Allwine > * Martin Gerhardy > * James Gregory (copyright owned by Zynga, Inc.) > * Dan Gohman (copyright owned by Google, Inc.) > * Jeff Gilbert (copyright owned by Mozilla Foundation) > * Frits Talbot > * Onno Jongbloed > * Jez Ng > * Marc Feeley (copyright owned by Mozilla Foundation) > * Ludovic Perrine > * David Barksdale > * Manfred Manik Nerurkar (copyright owned by MADE, GmbH) > * Joseph Gentle > * Douglas T. Crosher (copyright owned by Mozilla Foundation) > * Douglas T. Crosher (copyright owned by Scieneer Pty Ltd.) > * Soeren Balko > * Ryan Kelly (ryan@rfk.id.au) > * Michael Lelli > * Yu Kobayashi > * Pin Zhang > * Nick Bray (copyright owned by Google, Inc.) > * Aidan Hobson Sayers > * Charlie Birks > * Ranger Harke (copyright owned by Autodesk, Inc.) > * Tobias Vrinssen > * Patrick R. Martin > * Richard Quirk > * Marcos Scriven > * Antoine Lambert > * Daniel Aquino > * Remi Papillie > * Fraser Adams > * Michael Tirado > * Ben Noordhuis > * Bob Roberts > * John Vilk > * Daniel Baulig (copyright owned by Facebook, Inc.) > * Lu Wang > * Heidi Pan (copyright owned by Intel) > * Vasilis Kalintiris > * Adam C. Clifton > * Volo Zyko > * Andre Weissflog > * Alexandre Perrot > * Emerson José Silveira da Costa > * Jari Vetoniemi > * Sindre Sorhus > * James S Urquhart > * Boris Gjenero > * jonas echterhoff > * Sami Vaarala > * Jack A. Arrington > * Richard Janicek > * Joel Croteau > * Haneef Mubarak > * Nicolas Peri (copyright owned by ShiVa Technologies, SAS) > * Bernhard Fey > * Dave Nicponski > * Jonathan Jarri > * Daniele Di Proietto > * Dan Dascalescu > * Thomas Borsos > * Ori Avtalion > * Guillaume Blanc > * Usagi Ito > * Camilo Polymeris > * Markus Henschel > * Ophir Lojkine > * Ryan Sturgell (copyright owned by Google, Inc.) > * Jason Green (copyright owned by TransGaming, Inc.) > * Ningxin Hu (copyright owned by Intel) > * Nicolas Guillemot > * Sathyanarayanan Gunasekaran (copyright owned by Mozilla Foundation) > * Nikolay Vorobyov > * Jonas Platte > * Sebastien Ronsse > * Glenn R. Wichman > * Hamish Willee (copyright owned by Mozilla Foundation) > * Sylvain Chevalier > * Nathan Ross > * Zachary Pomerantz > * Boris Tsarev > * Mark Logan (copyright owned by Artillery Games, Inc.) > * Коренберг Марк > * Gauthier Billot > * Árpád Goretity > * Nicholas Wilson > * Aaron Mandle > * Bailey Hayes (copyright owned by SAS Institute Inc.) > * Paul Holland > * James Long > * David Anderson (copyright owned by Mozilla Foundation) > * Eric Rannaud (copyright owned by Nanocritical Corp.) > * William Furr (copyright owned by Google, Inc.) > * Dan Glastonbury (copyright owned by Mozilla Foundation) > * Warren Seine (copyright owned by Aerys SAS) > * Petr Babicka > * Akira Takahashi > * Victor Costan > * Pepijn Van Eeckhoudt (copyright owned by Luciad NV) > * Stevie Trujillo > * Edward Rudd > * Rene Eichhorn > * Nick Desaulniers (copyright owned by Mozilla Foundation) > * Luke Wagner (copyright owned by Mozilla Foundation) > * Matt McCormick > * Thaddée Tyl > * Philipp Wiesemann > * Jan Jongboom (copyright owned by Telenor Digital AS) > * Tiago Quelhas > * Reinier de Blois > * Yuichi Nishiwaki > * Jérôme Bernard (copyright owned by Ercom) > * Chanhwi Choi > * Fábio Santos > * Thibaut Despoulain (copyright owned by Artillery Games, Inc.) > * Wei Tjong Yao > * Tim Guan-tin Chien > * Krzysztof Jakubowski > * Vladimír Vondruš > * Brion Vibber > * Philip Lafleur > * Javier Meseguer de Paz > * Michael A. Balazs > * Andreas Blixt > * Haofeng Zhang > * Cody Welsh > * Hoong Ern Ng > * Kagami Hiiragi > * Jan Bölsche > * Sebastian Matthes (copyright owned by Volkswagen AG) > * Robert Goulet (copyright owned by Autodesk, Inc.) > * Juha Järvi > * Louis Lagrange > * Ying-Ruei Liang > * Stuart Geipel > * Yeonjun Lim > * Andrew Karpushin > * Felix Zimmermann > * Sven-Hendrik Haase > * Simon Sandström > * Khaled Sami > * Omar El-Mohandes > * Florian Rival > * Mark Achée > * Piotr Paczkowski > * Braden MacDonald > * Kevin Cheung (copyright owned by Autodesk, Inc.) > * Josh Peterson > * eska > * Nate Burr > * Paul "TBBle" Hampson > * Andreas Plesch > * Brian Armstrong > * Vincenzo Chianese > * Noam T.Cohen > * Nick Shin > * Gregg Tavares > * Tanner Rogalsky > * Richard Cook (copyright owned by Tableau Software, Inc.) > * Arnab Choudhury (copyright owned by Tableau Software, Inc.) > * Charles Vaughn (copyright owned by Tableau Software, Inc.) > * Pierre Krieger > * Jakob Stoklund Olesen > * Jérémy Anger > * Derek Schuff (copyright owned by Google, Inc.) > * Ashley Sommer > * Dave Fletcher > * Lars-Magnus Skog > * Pieter Vantorre > * Maher Sallam > * Andrey Burov > * Holland Schutte > * Kerby Geffrard > * cynecx > * Chris Gibson > * Harald Reingruber > * Aiden Koss > * Dustin VanLerberghe > * Philip Bielby (copyright owned by Jagex Ltd.) > * Régis Fénéon > * Dominic Chen (copyright owned by Google, Inc.) > * Junji Hashimoto > * Heejin Ahn (copyright owned by Google, Inc.) > * Andras Kucsma > * Mateusz Borycki > * Franklin Ta > * Jacob Gravelle (copyright owned by Google, Inc.) > * Kagami Sascha Rosylight > * Benny Jacobs > * Ray Brown > * Christopher Serr > * Aaron Ruß (copyright owned by DFKI GmbH) > * Vilibald Wanča > * Alex Hixon > * Vladimir Davidovich > * Yuriy Levchenko > * Dmitry Tolmachov > * Dylan McKay > * Christophe Gragnic > * Murphy McCauley > * Anatoly Trosinenko > * Brad Grantham > * Sam Clegg (copyright owned by Google, Inc.) > * Joshua Lind > * Hiroaki GOTO as "GORRY" > * Mikhail Kremnyov (copyright owned by XCDS International) > * Tasuku SUENAGA a.k.a. gunyarakun > * Vitorio Miguel Prieto Cilia > * Evan Wallace > * Henning Pohl > * Tim Neumann > * Ondrej Stava (copyright owned by Google, Inc.) > * Jakub Jirutka > * Loo Rong Jie > * Jean-François Geyelin > * Matthew Collins > * Satoshi N. M > * Ryan Speets > * Fumiya Chiba > * Ryan C. Gordon > * Inseok Lee > * Yair Levinson (copyright owned by Autodesk, Inc.) > * Matjaž Drolc > * James Swift (copyright owned by PSPDFKit GmbH) > * Ryan Lester (copyright owned by Cyph, Inc.) > * Nikolay Zapolnov > * Nazar Mokrynskyi > * Yury Delendik (copyright owned by Mozilla Foundation) > * Kenneth Perry > * Jim Mussared > * Dirk Vanden Boer > * Mitchell Foley (copyright owned by Google, Inc.) > * Oleksandr Chekhovskyi > * Michael Siebert > * Jonathan Hale > * Etienne Brateau > * Zhiming Wang > * Jameson Ernst > * Yoan Lecoq > * Jiajie Hu (copyright owned by Intel Corporation) > * Kamil Klimek > * José Carlos Pujol > * Dannii Willis > * Erik Dubbelboer > * Sergey Tsatsulin > * varkor > * Stuart Knightley > * Amadeus Guo > * Nathan Froyd (copyright owned by Mozilla Foundation) > * Daniel Wirtz > * Kibeom Kim > * Marcel Klammer > * Axel Forsman > * Ebrahim Byagowi > * Thorsten Möller > * Michael Droettboom > * Nicolas Bouquet > * Miguel Saldivar > * Gert Van Hoey > * Valtteri Heikkilä > * Daniel McNab > * Tyler Limkemann > * Ben Smith (copyright owned by Google, Inc.) > * Sylvain Beucler > * Patrik Weiskircher > * Tobias Widlund > * Rob Fors > * Mike Frysinger (copyright owned by Google, Inc.) > * Sébasiten Crozet > * Andrey Nagikh > * Dzmitry Malyshau (copyright owned by Mozilla Foundation) > * Bjorn Swenson > * Ryhor Spivak > * Jan Schär > * Ryhor Spivak > * Alexander Bich > * Ashleigh Thomas > * Veniamin Petrenko > * Ian Henderson > * Siim Kallas > * Carl Woffenden (copyright owned by Numfum GmbH) > * Patrick Berger (copyright owned by Compusoft Group) > * Alexander Frank Lehmann (copyright owned by Compusoft Group) > * Tommy Nguyen > * Thomas Schander (copyright owned by Enscape GmbH) > * Benjamin S. Rodgers > * Paul Shapiro > * Elmo Todurov > * Zoltán Žarkov > * Roman Yurchak > * Hampton Maxwell > * Eric Fiselier (copyright owned by Google, Inc.) > * Sirapop Wongstapornpat > * Matt Kane > * Altan Özlü > * Mary S > * Martin Birks > * Kirill Smelkov (copyright owned by Nexedi) > * Lutz Hören > * Pedro K Custodio Emscripten dependencies: > This program uses portions of Node.js source code located in src/library_path.js, > in accordance with the terms of the MIT license. Node's license follows: (See below for license text. node.js is "Copyright Joyent, Inc. and other Node contributors. All rights reserved.") > The musl libc project is bundled in this repo, and it has the MIT license, see Emscripten 1.38.21 / musl 1.1.15 system/lib/libc/musl/COPYRIGHT: > musl as a whole is licensed under the following standard MIT license: > > ---------------------------------------------------------------------- > Copyright © 2005-2014 Rich Felker, et al. > > Permission is hereby granted, free of charge, to any person obtaining > a copy of this software and associated documentation files (the > "Software"), to deal in the Software without restriction, including > without limitation the rights to use, copy, modify, merge, publish, > distribute, sublicense, and/or sell copies of the Software, and to > permit persons to whom the Software is furnished to do so, subject to > the following conditions: > > The above copyright notice and this permission notice shall be > included in all copies or substantial portions of the Software. > > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > ---------------------------------------------------------------------- > > Authors/contributors include: > > Alex Dowad > Alexander Monakov > Anthony G. Basile > Arvid Picciani > Bobby Bingham > Boris Brezillon > Brent Cook > Chris Spiegel > Clément Vasseur > Daniel Micay > Denys Vlasenko > Emil Renner Berthing > Felix Fietkau > Felix Janda > Gianluca Anzolin > Hauke Mehrtens > Hiltjo Posthuma > Isaac Dunham > Jaydeep Patil > Jens Gustedt > Jeremy Huntwork > Jo-Philipp Wich > Joakim Sindholt > John Spencer > Josiah Worcester > Justin Cormack > Khem Raj > Kylie McClain > Luca Barbato > Luka Perkov > M Farkas-Dyck (Strake) > Mahesh Bodapati > Michael Forney > Natanael Copa > Nicholas J. Kain > orc > Pascal Cuoq > Petr Hosek > Pierre Carrier > Rich Felker > Richard Pennington > Shiz > sin > Solar Designer > Stefan Kristiansson > Szabolcs Nagy > Timo Teräs > Trutz Behn > Valentin Ochs > William Haddon > > Portions of this software are derived from third-party works licensed > under terms compatible with the above MIT license: > > The TRE regular expression implementation (src/regex/reg* and > src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed > under a 2-clause BSD license (license text in the source files). The > included version has been heavily modified by Rich Felker in 2012, in > the interests of size, simplicity, and namespace cleanliness. > > Much of the math library code (src/math/* and src/complex/*) is > Copyright © 1993,2004 Sun Microsystems or > Copyright © 2003-2011 David Schultz or > Copyright © 2003-2009 Steven G. Kargl or > Copyright © 2003-2009 Bruce D. Evans or > Copyright © 2008 Stephen L. Moshier > and labelled as such in comments in the individual source files. All > have been licensed under extremely permissive terms. > > The ARM memcpy code (src/string/arm/memcpy_el.S) is Copyright © 2008 > The Android Open Source Project and is licensed under a two-clause BSD > license. It was taken from Bionic libc, used on Android. > > The implementation of DES for crypt (src/crypt/crypt_des.c) is > Copyright © 1994 David Burren. It is licensed under a BSD license. > > The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was > originally written by Solar Designer and placed into the public > domain. The code also comes with a fallback permissive license for use > in jurisdictions that may not recognize the public domain. > > The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 > Valentin Ochs and is licensed under an MIT-style license. > > The BSD PRNG implementation (src/prng/random.c) and XSI search API > (src/search/*.c) functions are Copyright © 2011 Szabolcs Nagy and > licensed under following terms: "Permission to use, copy, modify, > and/or distribute this code for any purpose with or without fee is > hereby granted. There is no warranty." > > The x86_64 port was written by Nicholas J. Kain and is licensed under > the standard MIT terms. > > The mips and microblaze ports were originally written by Richard > Pennington for use in the ellcc project. The original code was adapted > by Rich Felker for build system and code conventions during upstream > integration. It is licensed under the standard MIT terms. > > The mips64 port was contributed by Imagination Technologies and is > licensed under the standard MIT terms. > > The powerpc port was also originally written by Richard Pennington, > and later supplemented and integrated by John Spencer. It is licensed > under the standard MIT terms. > > All other files which have no copyright comments are original works > produced specifically for use as part of this library, written either > by Rich Felker, the main author of the library, or by one or more > contibutors listed above. Details on authorship of individual files > can be found in the git version control history of the project. The > omission of copyright and license comments in each file is in the > interest of source tree size. > > In addition, permission is hereby granted for all public header files > (include/* and arch/*/bits/*) and crt files intended to be linked into > applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit > the copyright notice and permission notice otherwise required by the > license, and to use these files without any requirement of > attribution. These files include substantial contributions from: > > Bobby Bingham > John Spencer > Nicholas J. Kain > Rich Felker > Richard Pennington > Stefan Kristiansson > Szabolcs Nagy > > all of whom have explicitly granted such permission. > > This file previously contained text expressing a belief that most of > the files covered by the above exception were sufficiently trivial not > to be subject to copyright, resulting in confusion over whether it > negated the permissions granted in the license. In the spirit of > permissive licensing, and of not having licensing issues being an > obstacle to adoption, that text has been removed. ## unordered_dense https://github.com/martinus/unordered_dense Copyright (c) 2022 Martin Leitner-Ankerl See below for license text (MIT). ## glad Generated with https://github.com/Dav1dde/glad/tree/v0.1.27 > What's the license of glad generated code? #101 > Any of Public Domain, WTFPL or CC0. See below for license text (CC0). ## glm https://github.com/g-truc/glm/tree/0.9.9.3 or system version Copyright (c) 2005 - G-Truc Creation. GLM can be distributed and/or modified under the terms of either a) The Happy Bunny License, or b) the MIT License. See below for license text (MIT). ## LibAtrac9 https://github.com/Thealexbarney/LibAtrac9 or system version Copyright (c) 2018 Alex Barney. See below for license text (MIT). ## Dear ImGui https://github.com/ocornut/imgui Copyright (c) 2014-2024 Omar Cornut. See below for license text (MIT). ## Ogg system version, originally https://github.com/xiph/ogg > Copyright (c) 2002, Xiph.org Foundation > > Redistribution and use in source and binary forms, with or without > modification, are permitted provided that the following conditions > are met: > > - Redistributions of source code must retain the above copyright > notice, this list of conditions and the following disclaimer. > > - Redistributions in binary form must reproduce the above copyright > notice, this list of conditions and the following disclaimer in the > documentation and/or other materials provided with the distribution. > > - Neither the name of the Xiph.org Foundation nor the names of its > contributors may be used to endorse or promote products derived from > this software without specific prior written permission. > > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION > OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## OpenAL Soft system version, originally https://github.com/kcat/openal-soft > OpenAL cross platform audio library > Copyright (C) 1999-2007 by authors. > This library is free software; you can redistribute it and/or > modify it under the terms of the GNU Library General Public > License as published by the Free Software Foundation; either > version 2 of the License, or (at your option) any later version. > > This library is distributed in the hope that it will be useful, > but WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > Library General Public License for more details. > > You should have received a copy of the GNU Library General Public > License along with this library; if not, write to the > Free Software Foundation, Inc., > 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > Or go to http://www.gnu.org/copyleft/lgpl.html See below for license text (LGPLv2). ## OpenImageIO https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md Copyright (c) 2008-present by Contributors to the OpenImageIO project. All Rights Reserved. See below for license text (BSD 3-Clause License). ## PCG Random Number Generation, Minimal C Edition https://github.com/imneme/pcg-c-basic/tree/bc39cd76ac3d541e618606bcc6e1e5ba5e5e6aa3 Copyright 2014 Melissa O'Neill Licensed under the Apache License, Version 2.0. See below for license text. # Ryujinx https://github.com/Ryujinx/Ryujinx/tree/eee639d6ba544fa5dd9352426d55e91bc54e157d/Ryujinx.Graphics/Graphics3d/Texture Copyright (c) Ryujinx Team and Contributors. See below for license text (MIT). ## s3tc-dxt-decompression https://github.com/Benjamin-Dobell/s3tc-dxt-decompression/blob/5f923ecc1553aa60058d7011360cefca73e8b0fd/s3tc.cpp Copyright (c) 2009 Benjamin Dobell, Glass Echidna. See below for license text (MIT). ## Scarlet https://github.com/xdanieldzd/Scarlet/blob/d8aabf430307d35a81b131e40bb3c9a4828bdd7b/Scarlet/Drawing/ImageBinary.cs Copyright (c) 2016 xdaniel (Daniel R.) / DigitalZero Domain See below for license text (MIT). ## SDL2 system version, originally https://libsdl.org/download-2.0.php Copyright (C) 1997-2019 Sam Lantinga See below for license text (zlib). ## mio https://github.com/vimpunk/mio/blob/master/single_include/mio/mio.hpp Copyright (c) 2018 https://github.com/mandreyel/ See below for license text (MIT). ## stb_image https://github.com/nothings/stb/blob/5d90a8375a1393abc0dc5f8cc9f1ac9fd9ae6530/stb_image.h > ------------------------------------------------------------------------------ > This software is available under 2 licenses -- choose whichever you prefer. > ------------------------------------------------------------------------------ > ALTERNATIVE A - MIT License > Copyright (c) 2017 Sean Barrett See below for license text. ## Squish https://github.com/OpenImageIO/oiio/blob/master/src/dds.imageio/squish/README > The squish library is distributed under the terms and conditions of the MIT > license. This license is specified at the top of each source file and must be > preserved in its entirety. See below for license text. ## vgm_ripping https://github.com/hcs64/vgm_ripping/tree/f460d548859d59dd03b2c3f29ed97e2dbf9bb5b3/multi/utf_tab Copyright (c) 2014 Adam Gashlin. See below for license text (MIT). ## vgmstream https://github.com/losnoco/vgmstream/tree/ec0043bf6b816c817ee786458f314f2808fd5846 > Copyright (c) 2008-2010 Adam Gashlin, Fastelbja, Ronny Elfert > > Portions Copyright (c) 2004-2008, Marko Kreen > Portions Copyright 2001-2007 jagarl / Kazunori Ueno > Portions Copyright (c) 1998, Justin Frankel/Nullsoft Inc. > Portions Copyright (C) 2006 Nullsoft, Inc. > Portions Copyright (c) 2005-2007 Paul Hsieh > Portions Public Domain originating with Sun Microsystems See below for license text (ISC). ## Vorbis system version, originally https://github.com/xiph/vorbis > Copyright (c) 2002-2018 Xiph.org Foundation > > Redistribution and use in source and binary forms, with or without > modification, are permitted provided that the following conditions > are met: > > - Redistributions of source code must retain the above copyright > notice, this list of conditions and the following disclaimer. > > - Redistributions in binary form must reproduce the above copyright > notice, this list of conditions and the following disclaimer in the > documentation and/or other materials provided with the distribution. > > - Neither the name of the Xiph.org Foundation nor the names of its > contributors may be used to endorse or promote products derived from > this software without specific prior written permission. > > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION > OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## zlib system version, originally https://www.zlib.net/ Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler See below for license text (zlib). ## FFmpeg https://www.ffmpeg.org/ See below for license text (LGPLv2). ## UTF8-CPP https://github.com/nemtrif/utfcpp See below for license text (BSL-1.0) ## avcpp Copyright (c) 2013-2017, Alexander Drozdov All rights reserved. See below for license text (BSD 3-Clause License). Copyright 2006 Nemanja Trifunovic > Permission is hereby granted, free of charge, to any person or organization > obtaining a copy of the software and accompanying documentation covered by > this license (the "Software") to use, reproduce, display, distribute, > execute, and transmit the Software, and to prepare derivative works of the > Software, and to permit third-parties to whom the Software is furnished to > do so, all subject to the following: > > The copyright notices in the Software and this entire statement, including > the above license grant, this restriction and the following disclaimer, > must be included in all copies of the Software, in whole or in part, and > all derivative works of the Software, unless such copies or derivative > works are solely in the form of machine-executable object code generated by > a source language processor. > > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT > SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE > FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, > ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > DEALINGS IN THE SOFTWARE. ## libmspack https://www.cabextract.org.uk/libmspack/ See below for license text (LGPLv2). ## pugixml https://github.com/zeux/pugixml Copyright (c) 2006-2024 Arseny Kapoulkine See below for license text (MIT). ## fmtlib https://github.com/fmtlib/fmt Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- Optional exception to the license --- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into a machine-executable object form of such source code, you may redistribute such embedded portions in such object form without including the above copyright and permission notices. ## concurrent_queue & readerwriterqueue https://github.com/cameron314/concurrentqueue https://github.com/cameron314/readerwriterqueue Simplified BSD License: Copyright (c) 2013-2016, Cameron Desrochers. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## libass https://github.com/libass/libass See below for license text (ISC) # Generic license texts ## 2-clause BSD license > Redistribution and use in source and binary forms, with or without modification, > are permitted provided that the following conditions are met: > > 1. Redistributions of source code must retain the above copyright notice, this > list of conditions and the following disclaimer. > > 2. Redistributions in binary form must reproduce the above copyright notice, > this list of conditions and the following disclaimer in the documentation and/or > other materials provided with the distribution. > > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND > ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED > WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE > DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR > ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES > (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; > LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON > ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS > SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## 3-clause BSD license > Redistribution and use in source and binary forms, with or without > modification, are permitted provided that the following conditions are met: > > 1. Redistributions of source code must retain the above copyright notice, this > list of conditions and the following disclaimer. > > 2. Redistributions in binary form must reproduce the above copyright notice, > this list of conditions and the following disclaimer in the documentation > and/or other materials provided with the distribution. > > 3. Neither the name of the copyright holder nor the names of its > contributors may be used to endorse or promote products derived from > this software without specific prior written permission. > > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" > AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE > DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE > FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR > SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER > CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, > OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## Apache License, Version 2.0 > Apache License > Version 2.0, January 2004 > http://www.apache.org/licenses/ > > TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION > > 1. Definitions. > > "License" shall mean the terms and conditions for use, reproduction, > and distribution as defined by Sections 1 through 9 of this document. > > "Licensor" shall mean the copyright owner or entity authorized by > the copyright owner that is granting the License. > > "Legal Entity" shall mean the union of the acting entity and all > other entities that control, are controlled by, or are under common > control with that entity. For the purposes of this definition, > "control" means (i) the power, direct or indirect, to cause the > direction or management of such entity, whether by contract or > otherwise, or (ii) ownership of fifty percent (50%) or more of the > outstanding shares, or (iii) beneficial ownership of such entity. > > "You" (or "Your") shall mean an individual or Legal Entity > exercising permissions granted by this License. > > "Source" form shall mean the preferred form for making modifications, > including but not limited to software source code, documentation > source, and configuration files. > > "Object" form shall mean any form resulting from mechanical > transformation or translation of a Source form, including but > not limited to compiled object code, generated documentation, > and conversions to other media types. > > "Work" shall mean the work of authorship, whether in Source or > Object form, made available under the License, as indicated by a > copyright notice that is included in or attached to the work > (an example is provided in the Appendix below). > > "Derivative Works" shall mean any work, whether in Source or Object > form, that is based on (or derived from) the Work and for which the > editorial revisions, annotations, elaborations, or other modifications > represent, as a whole, an original work of authorship. For the purposes > of this License, Derivative Works shall not include works that remain > separable from, or merely link (or bind by name) to the interfaces of, > the Work and Derivative Works thereof. > > "Contribution" shall mean any work of authorship, including > the original version of the Work and any modifications or additions > to that Work or Derivative Works thereof, that is intentionally > submitted to Licensor for inclusion in the Work by the copyright owner > or by an individual or Legal Entity authorized to submit on behalf of > the copyright owner. For the purposes of this definition, "submitted" > means any form of electronic, verbal, or written communication sent > to the Licensor or its representatives, including but not limited to > communication on electronic mailing lists, source code control systems, > and issue tracking systems that are managed by, or on behalf of, the > Licensor for the purpose of discussing and improving the Work, but > excluding communication that is conspicuously marked or otherwise > designated in writing by the copyright owner as "Not a Contribution." > > "Contributor" shall mean Licensor and any individual or Legal Entity > on behalf of whom a Contribution has been received by Licensor and > subsequently incorporated within the Work. > > 2. Grant of Copyright License. Subject to the terms and conditions of > this License, each Contributor hereby grants to You a perpetual, > worldwide, non-exclusive, no-charge, royalty-free, irrevocable > copyright license to reproduce, prepare Derivative Works of, > publicly display, publicly perform, sublicense, and distribute the > Work and such Derivative Works in Source or Object form. > > 3. Grant of Patent License. Subject to the terms and conditions of > this License, each Contributor hereby grants to You a perpetual, > worldwide, non-exclusive, no-charge, royalty-free, irrevocable > (except as stated in this section) patent license to make, have made, > use, offer to sell, sell, import, and otherwise transfer the Work, > where such license applies only to those patent claims licensable > by such Contributor that are necessarily infringed by their > Contribution(s) alone or by combination of their Contribution(s) > with the Work to which such Contribution(s) was submitted. If You > institute patent litigation against any entity (including a > cross-claim or counterclaim in a lawsuit) alleging that the Work > or a Contribution incorporated within the Work constitutes direct > or contributory patent infringement, then any patent licenses > granted to You under this License for that Work shall terminate > as of the date such litigation is filed. > > 4. Redistribution. You may reproduce and distribute copies of the > Work or Derivative Works thereof in any medium, with or without > modifications, and in Source or Object form, provided that You > meet the following conditions: > > (a) You must give any other recipients of the Work or > Derivative Works a copy of this License; and > > (b) You must cause any modified files to carry prominent notices > stating that You changed the files; and > > (c) You must retain, in the Source form of any Derivative Works > that You distribute, all copyright, patent, trademark, and > attribution notices from the Source form of the Work, > excluding those notices that do not pertain to any part of > the Derivative Works; and > > (d) If the Work includes a "NOTICE" text file as part of its > distribution, then any Derivative Works that You distribute must > include a readable copy of the attribution notices contained > within such NOTICE file, excluding those notices that do not > pertain to any part of the Derivative Works, in at least one > of the following places: within a NOTICE text file distributed > as part of the Derivative Works; within the Source form or > documentation, if provided along with the Derivative Works; or, > within a display generated by the Derivative Works, if and > wherever such third-party notices normally appear. The contents > of the NOTICE file are for informational purposes only and > do not modify the License. You may add Your own attribution > notices within Derivative Works that You distribute, alongside > or as an addendum to the NOTICE text from the Work, provided > that such additional attribution notices cannot be construed > as modifying the License. > > You may add Your own copyright statement to Your modifications and > may provide additional or different license terms and conditions > for use, reproduction, or distribution of Your modifications, or > for any such Derivative Works as a whole, provided Your use, > reproduction, and distribution of the Work otherwise complies with > the conditions stated in this License. > > 5. Submission of Contributions. Unless You explicitly state otherwise, > any Contribution intentionally submitted for inclusion in the Work > by You to the Licensor shall be under the terms and conditions of > this License, without any additional terms or conditions. > Notwithstanding the above, nothing herein shall supersede or modify > the terms of any separate license agreement you may have executed > with Licensor regarding such Contributions. > > 6. Trademarks. This License does not grant permission to use the trade > names, trademarks, service marks, or product names of the Licensor, > except as required for reasonable and customary use in describing the > origin of the Work and reproducing the content of the NOTICE file. > > 7. Disclaimer of Warranty. Unless required by applicable law or > agreed to in writing, Licensor provides the Work (and each > Contributor provides its Contributions) on an "AS IS" BASIS, > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or > implied, including, without limitation, any warranties or conditions > of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A > PARTICULAR PURPOSE. You are solely responsible for determining the > appropriateness of using or redistributing the Work and assume any > risks associated with Your exercise of permissions under this License. > > 8. Limitation of Liability. In no event and under no legal theory, > whether in tort (including negligence), contract, or otherwise, > unless required by applicable law (such as deliberate and grossly > negligent acts) or agreed to in writing, shall any Contributor be > liable to You for damages, including any direct, indirect, special, > incidental, or consequential damages of any character arising as a > result of this License or out of the use or inability to use the > Work (including but not limited to damages for loss of goodwill, > work stoppage, computer failure or malfunction, or any and all > other commercial damages or losses), even if such Contributor > has been advised of the possibility of such damages. > > 9. Accepting Warranty or Additional Liability. While redistributing > the Work or Derivative Works thereof, You may choose to offer, > and charge a fee for, acceptance of support, warranty, indemnity, > or other liability obligations and/or rights consistent with this > License. However, in accepting such obligations, You may act only > on Your own behalf and on Your sole responsibility, not on behalf > of any other Contributor, and only if You agree to indemnify, > defend, and hold each Contributor harmless for any liability > incurred by, or claims asserted against, such Contributor by reason > of your accepting any such warranty or additional liability. > > END OF TERMS AND CONDITIONS > > APPENDIX: How to apply the Apache License to your work. > > To apply the Apache License to your work, attach the following > boilerplate notice, with the fields enclosed by brackets "{}" > replaced with your own identifying information. (Don't include > the brackets!) The text should be enclosed in the appropriate > comment syntax for the file format. We also recommend that a > file or class name and description of purpose be included on the > same "printed page" as the copyright notice for easier > identification within third-party archives. > > Copyright {yyyy} {name of copyright owner} > > Licensed under the Apache License, Version 2.0 (the "License"); > you may not use this file except in compliance with the License. > You may obtain a copy of the License at > > http://www.apache.org/licenses/LICENSE-2.0 > > Unless required by applicable law or agreed to in writing, software > distributed under the License is distributed on an "AS IS" BASIS, > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > See the License for the specific language governing permissions and > limitations under the License. ## Boost Software License, Version 1.0 > Boost Software License - Version 1.0 - August 17th, 2003 > > Permission is hereby granted, free of charge, to any person or organization > obtaining a copy of the software and accompanying documentation covered by > this license (the "Software") to use, reproduce, display, distribute, > execute, and transmit the Software, and to prepare derivative works of the > Software, and to permit third-parties to whom the Software is furnished to > do so, all subject to the following: > > The copyright notices in the Software and this entire statement, including > the above license grant, this restriction and the following disclaimer, > must be included in all copies of the Software, in whole or in part, and > all derivative works of the Software, unless such copies or derivative > works are solely in the form of machine-executable object code generated by > a source language processor. > > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT > SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE > FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, > ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > DEALINGS IN THE SOFTWARE. ## CC0 1.0 > Creative Commons Legal Code > > CC0 1.0 Universal > > CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE > LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN > ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS > INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES > REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS > PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM > THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED > HEREUNDER. > > Statement of Purpose > > The laws of most jurisdictions throughout the world automatically confer > exclusive Copyright and Related Rights (defined below) upon the creator > and subsequent owner(s) (each and all, an "owner") of an original work of > authorship and/or a database (each, a "Work"). > > Certain owners wish to permanently relinquish those rights to a Work for > the purpose of contributing to a commons of creative, cultural and > scientific works ("Commons") that the public can reliably and without fear > of later claims of infringement build upon, modify, incorporate in other > works, reuse and redistribute as freely as possible in any form whatsoever > and for any purposes, including without limitation commercial purposes. > These owners may contribute to the Commons to promote the ideal of a free > culture and the further production of creative, cultural and scientific > works, or to gain reputation or greater distribution for their Work in > part through the use and efforts of others. > > For these and/or other purposes and motivations, and without any > expectation of additional consideration or compensation, the person > associating CC0 with a Work (the "Affirmer"), to the extent that he or she > is an owner of Copyright and Related Rights in the Work, voluntarily > elects to apply CC0 to the Work and publicly distribute the Work under its > terms, with knowledge of his or her Copyright and Related Rights in the > Work and the meaning and intended legal effect of CC0 on those rights. > > 1. Copyright and Related Rights. A Work made available under CC0 may be > protected by copyright and related or neighboring rights ("Copyright and > Related Rights"). Copyright and Related Rights include, but are not > limited to, the following: > > i. the right to reproduce, adapt, distribute, perform, display, > communicate, and translate a Work; > ii. moral rights retained by the original author(s) and/or performer(s); > iii. publicity and privacy rights pertaining to a person's image or > likeness depicted in a Work; > iv. rights protecting against unfair competition in regards to a Work, > subject to the limitations in paragraph 4(a), below; > v. rights protecting the extraction, dissemination, use and reuse of data > in a Work; > vi. database rights (such as those arising under Directive 96/9/EC of the > European Parliament and of the Council of 11 March 1996 on the legal > protection of databases, and under any national implementation > thereof, including any amended or successor version of such > directive); and > vii. other similar, equivalent or corresponding rights throughout the > world based on applicable law or treaty, and any national > implementations thereof. > > 2. Waiver. To the greatest extent permitted by, but not in contravention > of, applicable law, Affirmer hereby overtly, fully, permanently, > irrevocably and unconditionally waives, abandons, and surrenders all of > Affirmer's Copyright and Related Rights and associated claims and causes > of action, whether now known or unknown (including existing as well as > future claims and causes of action), in the Work (i) in all territories > worldwide, (ii) for the maximum duration provided by applicable law or > treaty (including future time extensions), (iii) in any current or future > medium and for any number of copies, and (iv) for any purpose whatsoever, > including without limitation commercial, advertising or promotional > purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each > member of the public at large and to the detriment of Affirmer's heirs and > successors, fully intending that such Waiver shall not be subject to > revocation, rescission, cancellation, termination, or any other legal or > equitable action to disrupt the quiet enjoyment of the Work by the public > as contemplated by Affirmer's express Statement of Purpose. > > 3. Public License Fallback. Should any part of the Waiver for any reason > be judged legally invalid or ineffective under applicable law, then the > Waiver shall be preserved to the maximum extent permitted taking into > account Affirmer's express Statement of Purpose. In addition, to the > extent the Waiver is so judged Affirmer hereby grants to each affected > person a royalty-free, non transferable, non sublicensable, non exclusive, > irrevocable and unconditional license to exercise Affirmer's Copyright and > Related Rights in the Work (i) in all territories worldwide, (ii) for the > maximum duration provided by applicable law or treaty (including future > time extensions), (iii) in any current or future medium and for any number > of copies, and (iv) for any purpose whatsoever, including without > limitation commercial, advertising or promotional purposes (the > "License"). The License shall be deemed effective as of the date CC0 was > applied by Affirmer to the Work. Should any part of the License for any > reason be judged legally invalid or ineffective under applicable law, such > partial invalidity or ineffectiveness shall not invalidate the remainder > of the License, and in such case Affirmer hereby affirms that he or she > will not (i) exercise any of his or her remaining Copyright and Related > Rights in the Work or (ii) assert any associated claims and causes of > action with respect to the Work, in either case contrary to Affirmer's > express Statement of Purpose. > > 4. Limitations and Disclaimers. > > a. No trademark or patent rights held by Affirmer are waived, abandoned, > surrendered, licensed or otherwise affected by this document. > b. Affirmer offers the Work as-is and makes no representations or > warranties of any kind concerning the Work, express, implied, > statutory or otherwise, including without limitation warranties of > title, merchantability, fitness for a particular purpose, non > infringement, or the absence of latent or other defects, accuracy, or > the present or absence of errors, whether or not discoverable, all to > the greatest extent permissible under applicable law. > c. Affirmer disclaims responsibility for clearing rights of other persons > that may apply to the Work or any use thereof, including without > limitation any person's Copyright and Related Rights in the Work. > Further, Affirmer disclaims responsibility for obtaining any necessary > consents, permissions or other rights required for any use of the > Work. > d. Affirmer understands and acknowledges that Creative Commons is not a > party to this document and has no duty or obligation with respect to > this CC0 or use of the Work. ## ISC license > Permission to use, copy, modify, and distribute this software for any > purpose with or without fee is hereby granted, provided that the above > copyright notice and this permission notice appear in all copies. > > THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ## LGPLv2 (GNU Library General Public License, Version 2) > GNU LIBRARY GENERAL PUBLIC LICENSE > Version 2, June 1991 > > Copyright (C) 1991 Free Software Foundation, Inc. > 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > Everyone is permitted to copy and distribute verbatim copies > of this license document, but changing it is not allowed. > > [This is the first released version of the library GPL. It is > numbered 2 because it goes with version 2 of the ordinary GPL.] > > Preamble > > The licenses for most software are designed to take away your > freedom to share and change it. By contrast, the GNU General Public > Licenses are intended to guarantee your freedom to share and change > free software--to make sure the software is free for all its users. > > This license, the Library General Public License, applies to some > specially designated Free Software Foundation software, and to any > other libraries whose authors decide to use it. You can use it for > your libraries, too. > > When we speak of free software, we are referring to freedom, not > price. Our General Public Licenses are designed to make sure that you > have the freedom to distribute copies of free software (and charge for > this service if you wish), that you receive source code or can get it > if you want it, that you can change the software or use pieces of it > in new free programs; and that you know you can do these things. > > To protect your rights, we need to make restrictions that forbid > anyone to deny you these rights or to ask you to surrender the rights. > These restrictions translate to certain responsibilities for you if > you distribute copies of the library, or if you modify it. > > For example, if you distribute copies of the library, whether gratis > or for a fee, you must give the recipients all the rights that we gave > you. You must make sure that they, too, receive or can get the source > code. If you link a program with the library, you must provide > complete object files to the recipients so that they can relink them > with the library, after making changes to the library and recompiling > it. And you must show them these terms so they know their rights. > > Our method of protecting your rights has two steps: (1) copyright > the library, and (2) offer you this license which gives you legal > permission to copy, distribute and/or modify the library. > > Also, for each distributor's protection, we want to make certain > that everyone understands that there is no warranty for this free > library. If the library is modified by someone else and passed on, we > want its recipients to know that what they have is not the original > version, so that any problems introduced by others will not reflect on > the original authors' reputations. > > Finally, any free program is threatened constantly by software > patents. We wish to avoid the danger that companies distributing free > software will individually obtain patent licenses, thus in effect > transforming the program into proprietary software. To prevent this, > we have made it clear that any patent must be licensed for everyone's > free use or not licensed at all. > > Most GNU software, including some libraries, is covered by the ordinary > GNU General Public License, which was designed for utility programs. This > license, the GNU Library General Public License, applies to certain > designated libraries. This license is quite different from the ordinary > one; be sure to read it in full, and don't assume that anything in it is > the same as in the ordinary license. > > The reason we have a separate public license for some libraries is that > they blur the distinction we usually make between modifying or adding to a > program and simply using it. Linking a program with a library, without > changing the library, is in some sense simply using the library, and is > analogous to running a utility program or application program. However, in > a textual and legal sense, the linked executable is a combined work, a > derivative of the original library, and the ordinary General Public License > treats it as such. > > Because of this blurred distinction, using the ordinary General > Public License for libraries did not effectively promote software > sharing, because most developers did not use the libraries. We > concluded that weaker conditions might promote sharing better. > > However, unrestricted linking of non-free programs would deprive the > users of those programs of all benefit from the free status of the > libraries themselves. This Library General Public License is intended to > permit developers of non-free programs to use free libraries, while > preserving your freedom as a user of such programs to change the free > libraries that are incorporated in them. (We have not seen how to achieve > this as regards changes in header files, but we have achieved it as regards > changes in the actual functions of the Library.) The hope is that this > will lead to faster development of free libraries. > > The precise terms and conditions for copying, distribution and > modification follow. Pay close attention to the difference between a > "work based on the library" and a "work that uses the library". The > former contains code derived from the library, while the latter only > works together with the library. > > Note that it is possible for a library to be covered by the ordinary > General Public License rather than by this special one. > > GNU LIBRARY GENERAL PUBLIC LICENSE > TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION > > 0. This License Agreement applies to any software library which > contains a notice placed by the copyright holder or other authorized > party saying it may be distributed under the terms of this Library > General Public License (also called "this License"). Each licensee is > addressed as "you". > > A "library" means a collection of software functions and/or data > prepared so as to be conveniently linked with application programs > (which use some of those functions and data) to form executables. > > The "Library", below, refers to any such software library or work > which has been distributed under these terms. A "work based on the > Library" means either the Library or any derivative work under > copyright law: that is to say, a work containing the Library or a > portion of it, either verbatim or with modifications and/or translated > straightforwardly into another language. (Hereinafter, translation is > included without limitation in the term "modification".) > > "Source code" for a work means the preferred form of the work for > making modifications to it. For a library, complete source code means > all the source code for all modules it contains, plus any associated > interface definition files, plus the scripts used to control compilation > and installation of the library. > > Activities other than copying, distribution and modification are not > covered by this License; they are outside its scope. The act of > running a program using the Library is not restricted, and output from > such a program is covered only if its contents constitute a work based > on the Library (independent of the use of the Library in a tool for > writing it). Whether that is true depends on what the Library does > and what the program that uses the Library does. > > 1. You may copy and distribute verbatim copies of the Library's > complete source code as you receive it, in any medium, provided that > you conspicuously and appropriately publish on each copy an > appropriate copyright notice and disclaimer of warranty; keep intact > all the notices that refer to this License and to the absence of any > warranty; and distribute a copy of this License along with the > Library. > > You may charge a fee for the physical act of transferring a copy, > and you may at your option offer warranty protection in exchange for a > fee. > > 2. You may modify your copy or copies of the Library or any portion > of it, thus forming a work based on the Library, and copy and > distribute such modifications or work under the terms of Section 1 > above, provided that you also meet all of these conditions: > > a) The modified work must itself be a software library. > > b) You must cause the files modified to carry prominent notices > stating that you changed the files and the date of any change. > > c) You must cause the whole of the work to be licensed at no > charge to all third parties under the terms of this License. > > d) If a facility in the modified Library refers to a function or a > table of data to be supplied by an application program that uses > the facility, other than as an argument passed when the facility > is invoked, then you must make a good faith effort to ensure that, > in the event an application does not supply such function or > table, the facility still operates, and performs whatever part of > its purpose remains meaningful. > > (For example, a function in a library to compute square roots has > a purpose that is entirely well-defined independent of the > application. Therefore, Subsection 2d requires that any > application-supplied function or table used by this function must > be optional: if the application does not supply it, the square > root function must still compute square roots.) > > These requirements apply to the modified work as a whole. If > identifiable sections of that work are not derived from the Library, > and can be reasonably considered independent and separate works in > themselves, then this License, and its terms, do not apply to those > sections when you distribute them as separate works. But when you > distribute the same sections as part of a whole which is a work based > on the Library, the distribution of the whole must be on the terms of > this License, whose permissions for other licensees extend to the > entire whole, and thus to each and every part regardless of who wrote > it. > > Thus, it is not the intent of this section to claim rights or contest > your rights to work written entirely by you; rather, the intent is to > exercise the right to control the distribution of derivative or > collective works based on the Library. > > In addition, mere aggregation of another work not based on the Library > with the Library (or with a work based on the Library) on a volume of > a storage or distribution medium does not bring the other work under > the scope of this License. > > 3. You may opt to apply the terms of the ordinary GNU General Public > License instead of this License to a given copy of the Library. To do > this, you must alter all the notices that refer to this License, so > that they refer to the ordinary GNU General Public License, version 2, > instead of to this License. (If a newer version than version 2 of the > ordinary GNU General Public License has appeared, then you can specify > that version instead if you wish.) Do not make any other change in > these notices. > > Once this change is made in a given copy, it is irreversible for > that copy, so the ordinary GNU General Public License applies to all > subsequent copies and derivative works made from that copy. > > This option is useful when you wish to copy part of the code of > the Library into a program that is not a library. > > 4. You may copy and distribute the Library (or a portion or > derivative of it, under Section 2) in object code or executable form > under the terms of Sections 1 and 2 above provided that you accompany > it with the complete corresponding machine-readable source code, which > must be distributed under the terms of Sections 1 and 2 above on a > medium customarily used for software interchange. > > If distribution of object code is made by offering access to copy > from a designated place, then offering equivalent access to copy the > source code from the same place satisfies the requirement to > distribute the source code, even though third parties are not > compelled to copy the source along with the object code. > > 5. A program that contains no derivative of any portion of the > Library, but is designed to work with the Library by being compiled or > linked with it, is called a "work that uses the Library". Such a > work, in isolation, is not a derivative work of the Library, and > therefore falls outside the scope of this License. > > However, linking a "work that uses the Library" with the Library > creates an executable that is a derivative of the Library (because it > contains portions of the Library), rather than a "work that uses the > library". The executable is therefore covered by this License. > Section 6 states terms for distribution of such executables. > > When a "work that uses the Library" uses material from a header file > that is part of the Library, the object code for the work may be a > derivative work of the Library even though the source code is not. > Whether this is true is especially significant if the work can be > linked without the Library, or if the work is itself a library. The > threshold for this to be true is not precisely defined by law. > > If such an object file uses only numerical parameters, data > structure layouts and accessors, and small macros and small inline > functions (ten lines or less in length), then the use of the object > file is unrestricted, regardless of whether it is legally a derivative > work. (Executables containing this object code plus portions of the > Library will still fall under Section 6.) > > Otherwise, if the work is a derivative of the Library, you may > distribute the object code for the work under the terms of Section 6. > Any executables containing that work also fall under Section 6, > whether or not they are linked directly with the Library itself. > > 6. As an exception to the Sections above, you may also compile or > link a "work that uses the Library" with the Library to produce a > work containing portions of the Library, and distribute that work > under terms of your choice, provided that the terms permit > modification of the work for the customer's own use and reverse > engineering for debugging such modifications. > > You must give prominent notice with each copy of the work that the > Library is used in it and that the Library and its use are covered by > this License. You must supply a copy of this License. If the work > during execution displays copyright notices, you must include the > copyright notice for the Library among them, as well as a reference > directing the user to the copy of this License. Also, you must do one > of these things: > > a) Accompany the work with the complete corresponding > machine-readable source code for the Library including whatever > changes were used in the work (which must be distributed under > Sections 1 and 2 above); and, if the work is an executable linked > with the Library, with the complete machine-readable "work that > uses the Library", as object code and/or source code, so that the > user can modify the Library and then relink to produce a modified > executable containing the modified Library. (It is understood > that the user who changes the contents of definitions files in the > Library will not necessarily be able to recompile the application > to use the modified definitions.) > > b) Accompany the work with a written offer, valid for at > least three years, to give the same user the materials > specified in Subsection 6a, above, for a charge no more > than the cost of performing this distribution. > > c) If distribution of the work is made by offering access to copy > from a designated place, offer equivalent access to copy the above > specified materials from the same place. > > d) Verify that the user has already received a copy of these > materials or that you have already sent this user a copy. > > For an executable, the required form of the "work that uses the > Library" must include any data and utility programs needed for > reproducing the executable from it. However, as a special exception, > the source code distributed need not include anything that is normally > distributed (in either source or binary form) with the major > components (compiler, kernel, and so on) of the operating system on > which the executable runs, unless that component itself accompanies > the executable. > > It may happen that this requirement contradicts the license > restrictions of other proprietary libraries that do not normally > accompany the operating system. Such a contradiction means you cannot > use both them and the Library together in an executable that you > distribute. > > 7. You may place library facilities that are a work based on the > Library side-by-side in a single library together with other library > facilities not covered by this License, and distribute such a combined > library, provided that the separate distribution of the work based on > the Library and of the other library facilities is otherwise > permitted, and provided that you do these two things: > > a) Accompany the combined library with a copy of the same work > based on the Library, uncombined with any other library > facilities. This must be distributed under the terms of the > Sections above. > > b) Give prominent notice with the combined library of the fact > that part of it is a work based on the Library, and explaining > where to find the accompanying uncombined form of the same work. > > 8. You may not copy, modify, sublicense, link with, or distribute > the Library except as expressly provided under this License. Any > attempt otherwise to copy, modify, sublicense, link with, or > distribute the Library is void, and will automatically terminate your > rights under this License. However, parties who have received copies, > or rights, from you under this License will not have their licenses > terminated so long as such parties remain in full compliance. > > 9. You are not required to accept this License, since you have not > signed it. However, nothing else grants you permission to modify or > distribute the Library or its derivative works. These actions are > prohibited by law if you do not accept this License. Therefore, by > modifying or distributing the Library (or any work based on the > Library), you indicate your acceptance of this License to do so, and > all its terms and conditions for copying, distributing or modifying > the Library or works based on it. > > 10. Each time you redistribute the Library (or any work based on the > Library), the recipient automatically receives a license from the > original licensor to copy, distribute, link with or modify the Library > subject to these terms and conditions. You may not impose any further > restrictions on the recipients' exercise of the rights granted herein. > You are not responsible for enforcing compliance by third parties to > this License. > > 11. If, as a consequence of a court judgment or allegation of patent > infringement or for any other reason (not limited to patent issues), > conditions are imposed on you (whether by court order, agreement or > otherwise) that contradict the conditions of this License, they do not > excuse you from the conditions of this License. If you cannot > distribute so as to satisfy simultaneously your obligations under this > License and any other pertinent obligations, then as a consequence you > may not distribute the Library at all. For example, if a patent > license would not permit royalty-free redistribution of the Library by > all those who receive copies directly or indirectly through you, then > the only way you could satisfy both it and this License would be to > refrain entirely from distribution of the Library. > > If any portion of this section is held invalid or unenforceable under any > particular circumstance, the balance of the section is intended to apply, > and the section as a whole is intended to apply in other circumstances. > > It is not the purpose of this section to induce you to infringe any > patents or other property right claims or to contest validity of any > such claims; this section has the sole purpose of protecting the > integrity of the free software distribution system which is > implemented by public license practices. Many people have made > generous contributions to the wide range of software distributed > through that system in reliance on consistent application of that > system; it is up to the author/donor to decide if he or she is willing > to distribute software through any other system and a licensee cannot > impose that choice. > > This section is intended to make thoroughly clear what is believed to > be a consequence of the rest of this License. > > 12. If the distribution and/or use of the Library is restricted in > certain countries either by patents or by copyrighted interfaces, the > original copyright holder who places the Library under this License may add > an explicit geographical distribution limitation excluding those countries, > so that distribution is permitted only in or among countries not thus > excluded. In such case, this License incorporates the limitation as if > written in the body of this License. > > 13. The Free Software Foundation may publish revised and/or new > versions of the Library General Public License from time to time. > Such new versions will be similar in spirit to the present version, > but may differ in detail to address new problems or concerns. > > Each version is given a distinguishing version number. If the Library > specifies a version number of this License which applies to it and > "any later version", you have the option of following the terms and > conditions either of that version or of any later version published by > the Free Software Foundation. If the Library does not specify a > license version number, you may choose any version ever published by > the Free Software Foundation. > > 14. If you wish to incorporate parts of the Library into other free > programs whose distribution conditions are incompatible with these, > write to the author to ask for permission. For software which is > copyrighted by the Free Software Foundation, write to the Free > Software Foundation; we sometimes make exceptions for this. Our > decision will be guided by the two goals of preserving the free status > of all derivatives of our free software and of promoting the sharing > and reuse of software generally. > > NO WARRANTY > > 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO > WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. > EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR > OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY > KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE > IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR > PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE > LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME > THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. > > 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN > WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY > AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU > FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR > CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE > LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING > RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A > FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF > SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH > DAMAGES. > > END OF TERMS AND CONDITIONS > > How to Apply These Terms to Your New Libraries > > If you develop a new library, and you want it to be of the greatest > possible use to the public, we recommend making it free software that > everyone can redistribute and change. You can do so by permitting > redistribution under these terms (or, alternatively, under the terms of the > ordinary General Public License). > > To apply these terms, attach the following notices to the library. It is > safest to attach them to the start of each source file to most effectively > convey the exclusion of warranty; and each file should have at least the > "copyright" line and a pointer to where the full notice is found. > > > Copyright (C) > > This library is free software; you can redistribute it and/or > modify it under the terms of the GNU Library General Public > License as published by the Free Software Foundation; either > version 2 of the License, or (at your option) any later version. > > This library is distributed in the hope that it will be useful, > but WITHOUT ANY WARRANTY; without even the implied warranty of > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > Library General Public License for more details. > > You should have received a copy of the GNU Library General Public > License along with this library; if not, write to the Free Software > Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > > Also add information on how to contact you by electronic and paper mail. > > You should also get your employer (if you work as a programmer) or your > school, if any, to sign a "copyright disclaimer" for the library, if > necessary. Here is a sample; alter the names: > > Yoyodyne, Inc., hereby disclaims all copyright interest in the > library `Frob' (a library for tweaking knobs) written by James Random Hacker. > > , 1 April 1990 > Ty Coon, President of Vice > > That's all there is to it! ## MIT License > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal > in the Software without restriction, including without limitation the rights > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > copies of the Software, and to permit persons to whom the Software is > furnished to do so, subject to the following conditions: > > The above copyright notice and this permission notice shall be included in > all copies or substantial portions of the Software. > > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > THE SOFTWARE. ## zlib license > This software is provided 'as-is', without any express or implied > warranty. In no event will the authors be held liable for any damages > arising from the use of this software. > > Permission is granted to anyone to use this software for any purpose, > including commercial applications, and to alter it and redistribute it > freely, subject to the following restrictions: > > 1. The origin of this software must not be misrepresented; you must not > claim that you wrote the original software. If you use this software > in a product, an acknowledgment in the product documentation would be > appreciated but is not required. > 2. Altered source versions must be plainly marked as such, and must not be > misrepresented as being the original software. > 3. This notice may not be removed or altered from any source distribution. ================================================ FILE: VERSION ================================================ 0.9.8. ================================================ FILE: android/.gitignore ================================================ distribution app/src/main/assets app/src/main/jniLibs app/src/main/cpp/impacto/src/** # Gradle files .gradle/ build/ # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar # Avoid ignore Gradle wrappper properties !gradle-wrapper.properties # Local configuration file (sdk path, etc) local.properties # Log/OS Files *.log # Android Studio generated files and folders captures/ .externalNativeBuild/ .cxx/ *.apk output.json # IntelliJ *.iml .idea/ misc.xml deploymentTargetDropDown.xml render.experimental.xml # Keystore files *.jks *.keystore # Google Services (e.g. APIs or Firebase) google-services.json # Android Profiling *.hprof ================================================ FILE: android/.run/app.run.xml ================================================ ================================================ FILE: android/README.md ================================================ Android Development: # Set up Android NDK Download the Android NDK Set $ANDROID_NDK_HOME to point to NDK path Set $MINSDKVERSION to point to desired minimum Android API level (currently 28 in pipeline) # Build impacto Run cmake with the ci-release-android preset or customize with your own in CMakeUserPresets.json VCPKG will automatically build dependencies for Android ARM64 target defined in the custom triplet. ```shell cmake --preset ci-release-android cmake --build --preset ci-release-android ``` libimpacto.so will automatically be copied to impacto/android/app/src/main/jniLibs//libimpacto.so # Packaging To avoid issues with packaging the .apk, avoid bundling game assets as .apk files have a maximum file size limit. run ./gradlew assemble in impacto/android apks will be created in impacto/android/distribution/android/app/outputs/apk # Preparing impacto Game assets should be copied to the `/sdcard/Android/data/com.committeeofzero.impacto/files/games` folder, see [Getting Started](/doc/getting_started.md). Upon running the application for the first time, bundled files will be copied to the `/sdcard/Android/data/com.committeeofzero.impacto/files/` folder. This will also occur when impacto detects a .reset file in the same directory. # Debugging Override the CMake preset and set CMAKE_BUILD_TYPE to DEBUG (-DCMAKE_BUILD_TYPE=DEBUG in command line or override with user preset) to build with debug symbols first. In Android Studio, open the impacto/android folder Make sure the explorer is set to view Project or Project files and not Android so cpp files are actually visible. Edit the run configuratio - Go to debugger: - Add /android/app/src/main/cpp/impacto to symbol directories - In LLDB Startup Commands, add the following so symbols get mapped to correct files `settings append target.source-map /android/app/src/main/cpp/impacto` Breakpoints should work now properly in C++ files when debugging ================================================ FILE: android/app/build.gradle ================================================ apply plugin: 'com.android.application' apply from: 'signing.gradle' android { defaultConfig { compileSdk 33 applicationId "com.committeeofzero.impacto" minSdkVersion 28 targetSdkVersion 34 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" ndk { abiFilters "arm64-v8a" } } buildTypes { release { signingConfig signingConfigs.release minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' resValue "string", "app_name", "Impacto" } debug { applicationIdSuffix ".debug" versionNameSuffix '-DEBUG' resValue "string", "app_name", "Impacto Debug" debuggable true packaging { jniLibs { keepDebugSymbols += "**/*.so" } } } } namespace 'com.committeeofzero.impacto' aaptOptions { noCompress 'mp4', 'mpk' } applicationVariants.all { variant -> def mergeTaskName = "merge${variant.name.capitalize()}NativeLibs" tasks.named(mergeTaskName).configure { outputs.upToDateWhen { false } } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' } ================================================ FILE: android/app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /home/luap/Logiciels/android-sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile -keepclassmembers class org.libsdl.app.** { public static *** **(...); public *** **(...); } ================================================ FILE: android/app/signing/.gitignore ================================================ prod.properties prod.jks !external.jks !external.properties ================================================ FILE: android/app/signing/external.properties ================================================ # Sign key alias for external.jks releaseSignKeyAlias=key # Sign key password for external.jks releaseSignKeyPassword=impacto # Path to external.jks releaseStoreFilePath=./signing/external.jks # Keystore password for external.jks releaseStorePassword=impacto ================================================ FILE: android/app/signing.gradle ================================================ // https://dev.to/ivanshafran/android-open-source-app-secure-build-config-38gi def propertiesFilename = "signing/prod.properties" if (!project.file(propertiesFilename).exists()) { propertiesFilename = "signing/external.properties" } def signingProperties = new Properties() signingProperties.load(new FileInputStream(file(propertiesFilename))) android { signingConfigs { release { keyAlias signingProperties.releaseSignKeyAlias keyPassword signingProperties.releaseSignKeyPassword storeFile file(signingProperties.releaseStoreFilePath) storePassword signingProperties.releaseStorePassword } } } ================================================ FILE: android/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/java/com/committeeofzero/impacto/ImpactoActivity.java ================================================ package com.committeeofzero.impacto; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.content.SharedPreferences; import org.libsdl.app.SDLActivity; public class ImpactoActivity extends SDLActivity { SharedPreferences prefs = null; /** * This method is called by SDL before loading the native shared libraries. * It can be overridden to provide names of shared libraries to be loaded. * The default implementation returns the defaults. It never returns null. * An array returned by a new implementation must at least contain "SDL2". * Also keep in mind that the order the libraries are loaded may matter. * * @return names of shared libraries to be loaded (e.g. "SDL2", "main"). */ @Override protected String[] getLibraries() { return new String[]{ "impacto" }; } @Override protected void onCreate(Bundle savedInstanceState) { prefs = this.getPreferences(Context.MODE_PRIVATE); File externalFilesDir = getExternalFilesDir(null); File resetFile = new File(externalFilesDir, ".reset"); boolean reset = prefs.getBoolean("firstRun", true); if (resetFile.exists()) { resetFile.delete(); reset = true; } SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean("firstRun", false); editor.apply(); copyAssetFolder("shaders", getFilesDir().getAbsolutePath() + "/" + "shaders"); if (reset) { copyAssetFolder("games", externalFilesDir.getAbsolutePath() + "/" + "games"); copyAssetFolder("profiles", externalFilesDir.getAbsolutePath() + "/" + "profiles"); } super.onCreate(savedInstanceState); } public boolean copyAssetFolder(String srcName, String dstName) { try { boolean result = true; String[] fileList = getAssets().list(srcName); if (fileList == null) { return false; } if (fileList.length == 0) { result = copyAssetFile(srcName, dstName); } else { File file = new File(dstName); result = file.mkdirs(); for (String filename : fileList) { result &= copyAssetFolder(srcName + File.separator + filename, dstName + File.separator + filename); } } return result; } catch (IOException e) { Log.e(null, "Failed to copy folder \"" + srcName + ", error: \"" + e.getMessage() + "\"\n."); return false; } } public boolean copyAssetFile(String srcName, String dstName) { try { InputStream in = getAssets().open(srcName); File outFile = new File(dstName); OutputStream out = new FileOutputStream(outFile); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } in.close(); out.close(); return true; } catch (IOException e) { Log.e(null, "Failed to copy file \"" + srcName + ", error: \"" + e.getMessage() + "\"\n."); return false; } } } ================================================ FILE: android/app/src/main/java/org/libsdl/app/HIDDevice.java ================================================ package org.libsdl.app; import android.hardware.usb.UsbDevice; interface HIDDevice { public int getId(); public int getVendorId(); public int getProductId(); public String getSerialNumber(); public int getVersion(); public String getManufacturerName(); public String getProductName(); public UsbDevice getDevice(); public boolean open(); public int sendFeatureReport(byte[] report); public int sendOutputReport(byte[] report); public boolean getFeatureReport(byte[] report); public void setFrozen(boolean frozen); public void close(); public void shutdown(); } ================================================ FILE: android/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java ================================================ package org.libsdl.app; import android.content.Context; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothGattService; import android.hardware.usb.UsbDevice; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.os.*; //import com.android.internal.util.HexDump; import java.lang.Runnable; import java.util.Arrays; import java.util.LinkedList; import java.util.UUID; class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice { private static final String TAG = "hidapi"; private HIDDeviceManager mManager; private BluetoothDevice mDevice; private int mDeviceId; private BluetoothGatt mGatt; private boolean mIsRegistered = false; private boolean mIsConnected = false; private boolean mIsChromebook = false; private boolean mIsReconnecting = false; private boolean mFrozen = false; private LinkedList mOperations; GattOperation mCurrentOperation = null; private Handler mHandler; private static final int TRANSPORT_AUTO = 0; private static final int TRANSPORT_BREDR = 1; private static final int TRANSPORT_LE = 2; private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000; static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3"); static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3"); static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3"); static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 }; static class GattOperation { private enum Operation { CHR_READ, CHR_WRITE, ENABLE_NOTIFICATION } Operation mOp; UUID mUuid; byte[] mValue; BluetoothGatt mGatt; boolean mResult = true; private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) { mGatt = gatt; mOp = operation; mUuid = uuid; } private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) { mGatt = gatt; mOp = operation; mUuid = uuid; mValue = value; } public void run() { // This is executed in main thread BluetoothGattCharacteristic chr; switch (mOp) { case CHR_READ: chr = getCharacteristic(mUuid); //Log.v(TAG, "Reading characteristic " + chr.getUuid()); if (!mGatt.readCharacteristic(chr)) { Log.e(TAG, "Unable to read characteristic " + mUuid.toString()); mResult = false; break; } mResult = true; break; case CHR_WRITE: chr = getCharacteristic(mUuid); //Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value)); chr.setValue(mValue); if (!mGatt.writeCharacteristic(chr)) { Log.e(TAG, "Unable to write characteristic " + mUuid.toString()); mResult = false; break; } mResult = true; break; case ENABLE_NOTIFICATION: chr = getCharacteristic(mUuid); //Log.v(TAG, "Writing descriptor of " + chr.getUuid()); if (chr != null) { BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); if (cccd != null) { int properties = chr.getProperties(); byte[] value; if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) { value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE; } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) { value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE; } else { Log.e(TAG, "Unable to start notifications on input characteristic"); mResult = false; return; } mGatt.setCharacteristicNotification(chr, true); cccd.setValue(value); if (!mGatt.writeDescriptor(cccd)) { Log.e(TAG, "Unable to write descriptor " + mUuid.toString()); mResult = false; return; } mResult = true; } } } } public boolean finish() { return mResult; } private BluetoothGattCharacteristic getCharacteristic(UUID uuid) { BluetoothGattService valveService = mGatt.getService(steamControllerService); if (valveService == null) return null; return valveService.getCharacteristic(uuid); } static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) { return new GattOperation(gatt, Operation.CHR_READ, uuid); } static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) { return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value); } static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) { return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid); } } public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) { mManager = manager; mDevice = device; mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier()); mIsRegistered = false; mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); mOperations = new LinkedList(); mHandler = new Handler(Looper.getMainLooper()); mGatt = connectGatt(); // final HIDDeviceBLESteamController finalThis = this; // mHandler.postDelayed(new Runnable() { // @Override // public void run() { // finalThis.checkConnectionForChromebookIssue(); // } // }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL); } public String getIdentifier() { return String.format("SteamController.%s", mDevice.getAddress()); } public BluetoothGatt getGatt() { return mGatt; } // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead // of TRANSPORT_LE. Let's force ourselves to connect low energy. private BluetoothGatt connectGatt(boolean managed) { if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) { try { return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE); } catch (Exception e) { return mDevice.connectGatt(mManager.getContext(), managed, this); } } else { return mDevice.connectGatt(mManager.getContext(), managed, this); } } private BluetoothGatt connectGatt() { return connectGatt(false); } protected int getConnectionState() { Context context = mManager.getContext(); if (context == null) { // We are lacking any context to get our Bluetooth information. We'll just assume disconnected. return BluetoothProfile.STATE_DISCONNECTED; } BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE); if (btManager == null) { // This device doesn't support Bluetooth. We should never be here, because how did // we instantiate a device to start with? return BluetoothProfile.STATE_DISCONNECTED; } return btManager.getConnectionState(mDevice, BluetoothProfile.GATT); } public void reconnect() { if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) { mGatt.disconnect(); mGatt = connectGatt(); } } protected void checkConnectionForChromebookIssue() { if (!mIsChromebook) { // We only do this on Chromebooks, because otherwise it's really annoying to just attempt // over and over. return; } int connectionState = getConnectionState(); switch (connectionState) { case BluetoothProfile.STATE_CONNECTED: if (!mIsConnected) { // We are in the Bad Chromebook Place. We can force a disconnect // to try to recover. Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect."); mIsReconnecting = true; mGatt.disconnect(); mGatt = connectGatt(false); break; } else if (!isRegistered()) { if (mGatt.getServices().size() > 0) { Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover."); probeService(this); } else { Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover."); mIsReconnecting = true; mGatt.disconnect(); mGatt = connectGatt(false); break; } } else { Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!"); return; } break; case BluetoothProfile.STATE_DISCONNECTED: Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover."); mIsReconnecting = true; mGatt.disconnect(); mGatt = connectGatt(false); break; case BluetoothProfile.STATE_CONNECTING: Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer."); break; } final HIDDeviceBLESteamController finalThis = this; mHandler.postDelayed(new Runnable() { @Override public void run() { finalThis.checkConnectionForChromebookIssue(); } }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL); } private boolean isRegistered() { return mIsRegistered; } private void setRegistered() { mIsRegistered = true; } private boolean probeService(HIDDeviceBLESteamController controller) { if (isRegistered()) { return true; } if (!mIsConnected) { return false; } Log.v(TAG, "probeService controller=" + controller); for (BluetoothGattService service : mGatt.getServices()) { if (service.getUuid().equals(steamControllerService)) { Log.v(TAG, "Found Valve steam controller service " + service.getUuid()); for (BluetoothGattCharacteristic chr : service.getCharacteristics()) { if (chr.getUuid().equals(inputCharacteristic)) { Log.v(TAG, "Found input characteristic"); // Start notifications BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); if (cccd != null) { enableNotification(chr.getUuid()); } } } return true; } } if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) { Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us."); mIsConnected = false; mIsReconnecting = true; mGatt.disconnect(); mGatt = connectGatt(false); } return false; } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// private void finishCurrentGattOperation() { GattOperation op = null; synchronized (mOperations) { if (mCurrentOperation != null) { op = mCurrentOperation; mCurrentOperation = null; } } if (op != null) { boolean result = op.finish(); // TODO: Maybe in main thread as well? // Our operation failed, let's add it back to the beginning of our queue. if (!result) { mOperations.addFirst(op); } } executeNextGattOperation(); } private void executeNextGattOperation() { synchronized (mOperations) { if (mCurrentOperation != null) return; if (mOperations.isEmpty()) return; mCurrentOperation = mOperations.removeFirst(); } // Run in main thread mHandler.post(new Runnable() { @Override public void run() { synchronized (mOperations) { if (mCurrentOperation == null) { Log.e(TAG, "Current operation null in executor?"); return; } mCurrentOperation.run(); // now wait for the GATT callback and when it comes, finish this operation } } }); } private void queueGattOperation(GattOperation op) { synchronized (mOperations) { mOperations.add(op); } executeNextGattOperation(); } private void enableNotification(UUID chrUuid) { GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid); queueGattOperation(op); } public void writeCharacteristic(UUID uuid, byte[] value) { GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value); queueGattOperation(op); } public void readCharacteristic(UUID uuid) { GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid); queueGattOperation(op); } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////// BluetoothGattCallback overridden methods ////////////////////////////////////////////////////////////////////////////////////////////////////// public void onConnectionStateChange(BluetoothGatt g, int status, int newState) { //Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState); mIsReconnecting = false; if (newState == 2) { mIsConnected = true; // Run directly, without GattOperation if (!isRegistered()) { mHandler.post(new Runnable() { @Override public void run() { mGatt.discoverServices(); } }); } } else if (newState == 0) { mIsConnected = false; } // Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent. } public void onServicesDiscovered(BluetoothGatt gatt, int status) { //Log.v(TAG, "onServicesDiscovered status=" + status); if (status == 0) { if (gatt.getServices().size() == 0) { Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack."); mIsReconnecting = true; mIsConnected = false; gatt.disconnect(); mGatt = connectGatt(false); } else { probeService(this); } } } public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid()); if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) { mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue()); } finishCurrentGattOperation(); } public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid()); if (characteristic.getUuid().equals(reportCharacteristic)) { // Only register controller with the native side once it has been fully configured if (!isRegistered()) { Log.v(TAG, "Registering Steam Controller with ID: " + getId()); mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0); setRegistered(); } } finishCurrentGattOperation(); } public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { // Enable this for verbose logging of controller input reports //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue())); if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) { mManager.HIDDeviceInputReport(getId(), characteristic.getValue()); } } public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { //Log.v(TAG, "onDescriptorRead status=" + status); } public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { BluetoothGattCharacteristic chr = descriptor.getCharacteristic(); //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid()); if (chr.getUuid().equals(inputCharacteristic)) { boolean hasWrittenInputDescriptor = true; BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic); if (reportChr != null) { Log.v(TAG, "Writing report characteristic to enter valve mode"); reportChr.setValue(enterValveMode); gatt.writeCharacteristic(reportChr); } } finishCurrentGattOperation(); } public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { //Log.v(TAG, "onReliableWriteCompleted status=" + status); } public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { //Log.v(TAG, "onReadRemoteRssi status=" + status); } public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { //Log.v(TAG, "onMtuChanged status=" + status); } ////////////////////////////////////////////////////////////////////////////////////////////////////// //////// Public API ////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public int getId() { return mDeviceId; } @Override public int getVendorId() { // Valve Corporation final int VALVE_USB_VID = 0x28DE; return VALVE_USB_VID; } @Override public int getProductId() { // We don't have an easy way to query from the Bluetooth device, but we know what it is final int D0G_BLE2_PID = 0x1106; return D0G_BLE2_PID; } @Override public String getSerialNumber() { // This will be read later via feature report by Steam return "12345"; } @Override public int getVersion() { return 0; } @Override public String getManufacturerName() { return "Valve Corporation"; } @Override public String getProductName() { return "Steam Controller"; } @Override public UsbDevice getDevice() { return null; } @Override public boolean open() { return true; } @Override public int sendFeatureReport(byte[] report) { if (!isRegistered()) { Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!"); if (mIsConnected) { probeService(this); } return -1; } // We need to skip the first byte, as that doesn't go over the air byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1); //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report)); writeCharacteristic(reportCharacteristic, actual_report); return report.length; } @Override public int sendOutputReport(byte[] report) { if (!isRegistered()) { Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!"); if (mIsConnected) { probeService(this); } return -1; } //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report)); writeCharacteristic(reportCharacteristic, report); return report.length; } @Override public boolean getFeatureReport(byte[] report) { if (!isRegistered()) { Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!"); if (mIsConnected) { probeService(this); } return false; } //Log.v(TAG, "getFeatureReport"); readCharacteristic(reportCharacteristic); return true; } @Override public void close() { } @Override public void setFrozen(boolean frozen) { mFrozen = frozen; } @Override public void shutdown() { close(); BluetoothGatt g = mGatt; if (g != null) { g.disconnect(); g.close(); mGatt = null; } mManager = null; mIsRegistered = false; mIsConnected = false; mOperations.clear(); } } ================================================ FILE: android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java ================================================ package org.libsdl.app; import android.app.Activity; import android.app.AlertDialog; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.os.Build; import android.util.Log; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.hardware.usb.*; import android.os.Handler; import android.os.Looper; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; public class HIDDeviceManager { private static final String TAG = "hidapi"; private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION"; private static HIDDeviceManager sManager; private static int sManagerRefCount = 0; public static HIDDeviceManager acquire(Context context) { if (sManagerRefCount == 0) { sManager = new HIDDeviceManager(context); } ++sManagerRefCount; return sManager; } public static void release(HIDDeviceManager manager) { if (manager == sManager) { --sManagerRefCount; if (sManagerRefCount == 0) { sManager.close(); sManager = null; } } } private Context mContext; private HashMap mDevicesById = new HashMap(); private HashMap mBluetoothDevices = new HashMap(); private int mNextDeviceId = 0; private SharedPreferences mSharedPreferences = null; private boolean mIsChromebook = false; private UsbManager mUsbManager; private Handler mHandler; private BluetoothManager mBluetoothManager; private List mLastBluetoothDevices; private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) { UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); handleUsbDeviceAttached(usbDevice); } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) { UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); handleUsbDeviceDetached(usbDevice); } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) { UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)); } } }; private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // Bluetooth device was connected. If it was a Steam Controller, handle it if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); Log.d(TAG, "Bluetooth device connected: " + device); if (isSteamController(device)) { connectBluetoothDevice(device); } } // Bluetooth device was disconnected, remove from controller manager (if any) if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); Log.d(TAG, "Bluetooth device disconnected: " + device); disconnectBluetoothDevice(device); } } }; private HIDDeviceManager(final Context context) { mContext = context; HIDDeviceRegisterCallback(); mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE); mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); // if (shouldClear) { // SharedPreferences.Editor spedit = mSharedPreferences.edit(); // spedit.clear(); // spedit.commit(); // } // else { mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0); } } public Context getContext() { return mContext; } public int getDeviceIDForIdentifier(String identifier) { SharedPreferences.Editor spedit = mSharedPreferences.edit(); int result = mSharedPreferences.getInt(identifier, 0); if (result == 0) { result = mNextDeviceId++; spedit.putInt("next_device_id", mNextDeviceId); } spedit.putInt(identifier, result); spedit.commit(); return result; } private void initializeUSB() { mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE); if (mUsbManager == null) { return; } /* // Logging for (UsbDevice device : mUsbManager.getDeviceList().values()) { Log.i(TAG,"Path: " + device.getDeviceName()); Log.i(TAG,"Manufacturer: " + device.getManufacturerName()); Log.i(TAG,"Product: " + device.getProductName()); Log.i(TAG,"ID: " + device.getDeviceId()); Log.i(TAG,"Class: " + device.getDeviceClass()); Log.i(TAG,"Protocol: " + device.getDeviceProtocol()); Log.i(TAG,"Vendor ID " + device.getVendorId()); Log.i(TAG,"Product ID: " + device.getProductId()); Log.i(TAG,"Interface count: " + device.getInterfaceCount()); Log.i(TAG,"---------------------------------------"); // Get interface details for (int index = 0; index < device.getInterfaceCount(); index++) { UsbInterface mUsbInterface = device.getInterface(index); Log.i(TAG," ***** *****"); Log.i(TAG," Interface index: " + index); Log.i(TAG," Interface ID: " + mUsbInterface.getId()); Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass()); Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass()); Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol()); Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount()); // Get endpoint details for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++) { UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi); Log.i(TAG," ++++ ++++ ++++"); Log.i(TAG," Endpoint index: " + epi); Log.i(TAG," Attributes: " + mEndpoint.getAttributes()); Log.i(TAG," Direction: " + mEndpoint.getDirection()); Log.i(TAG," Number: " + mEndpoint.getEndpointNumber()); Log.i(TAG," Interval: " + mEndpoint.getInterval()); Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize()); Log.i(TAG," Type: " + mEndpoint.getType()); } } } Log.i(TAG," No more devices connected."); */ // Register for USB broadcasts and permission completions IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION); mContext.registerReceiver(mUsbBroadcast, filter); for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) { handleUsbDeviceAttached(usbDevice); } } UsbManager getUSBManager() { return mUsbManager; } private void shutdownUSB() { try { mContext.unregisterReceiver(mUsbBroadcast); } catch (Exception e) { // We may not have registered, that's okay } } private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) { if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) { return true; } if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) { return true; } return false; } private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) { final int XB360_IFACE_SUBCLASS = 93; final int XB360_IFACE_PROTOCOL = 1; // Wired final int XB360W_IFACE_PROTOCOL = 129; // Wireless final int[] SUPPORTED_VENDORS = { 0x0079, // GPD Win 2 0x044f, // Thrustmaster 0x045e, // Microsoft 0x046d, // Logitech 0x056e, // Elecom 0x06a3, // Saitek 0x0738, // Mad Catz 0x07ff, // Mad Catz 0x0e6f, // PDP 0x0f0d, // Hori 0x1038, // SteelSeries 0x11c9, // Nacon 0x12ab, // Unknown 0x1430, // RedOctane 0x146b, // BigBen 0x1532, // Razer Sabertooth 0x15e4, // Numark 0x162e, // Joytech 0x1689, // Razer Onza 0x1949, // Lab126, Inc. 0x1bad, // Harmonix 0x20d6, // PowerA 0x24c6, // PowerA 0x2c22, // Qanba 0x2dc8, // 8BitDo 0x9886, // ASTRO Gaming }; if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC && usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS && (usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL || usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) { int vendor_id = usbDevice.getVendorId(); for (int supportedVid : SUPPORTED_VENDORS) { if (vendor_id == supportedVid) { return true; } } } return false; } private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) { final int XB1_IFACE_SUBCLASS = 71; final int XB1_IFACE_PROTOCOL = 208; final int[] SUPPORTED_VENDORS = { 0x03f0, // HP 0x044f, // Thrustmaster 0x045e, // Microsoft 0x0738, // Mad Catz 0x0b05, // ASUS 0x0e6f, // PDP 0x0f0d, // Hori 0x10f5, // Turtle Beach 0x1532, // Razer Wildcat 0x20d6, // PowerA 0x24c6, // PowerA 0x2dc8, // 8BitDo 0x2e24, // Hyperkin 0x3537, // GameSir }; if (usbInterface.getId() == 0 && usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC && usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS && usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) { int vendor_id = usbDevice.getVendorId(); for (int supportedVid : SUPPORTED_VENDORS) { if (vendor_id == supportedVid) { return true; } } } return false; } private void handleUsbDeviceAttached(UsbDevice usbDevice) { connectHIDDeviceUSB(usbDevice); } private void handleUsbDeviceDetached(UsbDevice usbDevice) { List devices = new ArrayList(); for (HIDDevice device : mDevicesById.values()) { if (usbDevice.equals(device.getDevice())) { devices.add(device.getId()); } } for (int id : devices) { HIDDevice device = mDevicesById.get(id); mDevicesById.remove(id); device.shutdown(); HIDDeviceDisconnected(id); } } private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) { for (HIDDevice device : mDevicesById.values()) { if (usbDevice.equals(device.getDevice())) { boolean opened = false; if (permission_granted) { opened = device.open(); } HIDDeviceOpenResult(device.getId(), opened); } } } private void connectHIDDeviceUSB(UsbDevice usbDevice) { synchronized (this) { int interface_mask = 0; for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) { UsbInterface usbInterface = usbDevice.getInterface(interface_index); if (isHIDDeviceInterface(usbDevice, usbInterface)) { // Check to see if we've already added this interface // This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive int interface_id = usbInterface.getId(); if ((interface_mask & (1 << interface_id)) != 0) { continue; } interface_mask |= (1 << interface_id); HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index); int id = device.getId(); mDevicesById.put(id, device); HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol()); } } } } private void initializeBluetooth() { Log.d(TAG, "Initializing Bluetooth"); if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ && mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT"); return; } if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ && mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH"); return; } if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) { Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE"); return; } // Find bonded bluetooth controllers and create SteamControllers for them mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager == null) { // This device doesn't support Bluetooth. return; } BluetoothAdapter btAdapter = mBluetoothManager.getAdapter(); if (btAdapter == null) { // This device has Bluetooth support in the codebase, but has no available adapters. return; } // Get our bonded devices. for (BluetoothDevice device : btAdapter.getBondedDevices()) { Log.d(TAG, "Bluetooth device available: " + device); if (isSteamController(device)) { connectBluetoothDevice(device); } } // NOTE: These don't work on Chromebooks, to my undying dismay. IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); mContext.registerReceiver(mBluetoothBroadcast, filter); if (mIsChromebook) { mHandler = new Handler(Looper.getMainLooper()); mLastBluetoothDevices = new ArrayList(); // final HIDDeviceManager finalThis = this; // mHandler.postDelayed(new Runnable() { // @Override // public void run() { // finalThis.chromebookConnectionHandler(); // } // }, 5000); } } private void shutdownBluetooth() { try { mContext.unregisterReceiver(mBluetoothBroadcast); } catch (Exception e) { // We may not have registered, that's okay } } // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly. // This function provides a sort of dummy version of that, watching for changes in the // connected devices and attempting to add controllers as things change. public void chromebookConnectionHandler() { if (!mIsChromebook) { return; } ArrayList disconnected = new ArrayList(); ArrayList connected = new ArrayList(); List currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT); for (BluetoothDevice bluetoothDevice : currentConnected) { if (!mLastBluetoothDevices.contains(bluetoothDevice)) { connected.add(bluetoothDevice); } } for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) { if (!currentConnected.contains(bluetoothDevice)) { disconnected.add(bluetoothDevice); } } mLastBluetoothDevices = currentConnected; for (BluetoothDevice bluetoothDevice : disconnected) { disconnectBluetoothDevice(bluetoothDevice); } for (BluetoothDevice bluetoothDevice : connected) { connectBluetoothDevice(bluetoothDevice); } final HIDDeviceManager finalThis = this; mHandler.postDelayed(new Runnable() { @Override public void run() { finalThis.chromebookConnectionHandler(); } }, 10000); } public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) { Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice); synchronized (this) { if (mBluetoothDevices.containsKey(bluetoothDevice)) { Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect"); HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice); device.reconnect(); return false; } HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice); int id = device.getId(); mBluetoothDevices.put(bluetoothDevice, device); mDevicesById.put(id, device); // The Steam Controller will mark itself connected once initialization is complete } return true; } public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) { synchronized (this) { HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice); if (device == null) return; int id = device.getId(); mBluetoothDevices.remove(bluetoothDevice); mDevicesById.remove(id); device.shutdown(); HIDDeviceDisconnected(id); } } public boolean isSteamController(BluetoothDevice bluetoothDevice) { // Sanity check. If you pass in a null device, by definition it is never a Steam Controller. if (bluetoothDevice == null) { return false; } // If the device has no local name, we really don't want to try an equality check against it. if (bluetoothDevice.getName() == null) { return false; } return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0); } private void close() { shutdownUSB(); shutdownBluetooth(); synchronized (this) { for (HIDDevice device : mDevicesById.values()) { device.shutdown(); } mDevicesById.clear(); mBluetoothDevices.clear(); HIDDeviceReleaseCallback(); } } public void setFrozen(boolean frozen) { synchronized (this) { for (HIDDevice device : mDevicesById.values()) { device.setFrozen(frozen); } } } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// private HIDDevice getDevice(int id) { synchronized (this) { HIDDevice result = mDevicesById.get(id); if (result == null) { Log.v(TAG, "No device for id: " + id); Log.v(TAG, "Available devices: " + mDevicesById.keySet()); } return result; } } ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////// JNI interface functions ////////////////////////////////////////////////////////////////////////////////////////////////////// public boolean initialize(boolean usb, boolean bluetooth) { Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")"); if (usb) { initializeUSB(); } if (bluetooth) { initializeBluetooth(); } return true; } public boolean openDevice(int deviceID) { Log.v(TAG, "openDevice deviceID=" + deviceID); HIDDevice device = getDevice(deviceID); if (device == null) { HIDDeviceDisconnected(deviceID); return false; } // Look to see if this is a USB device and we have permission to access it UsbDevice usbDevice = device.getDevice(); if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) { HIDDeviceOpenPending(deviceID); try { final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31 int flags; if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { flags = FLAG_MUTABLE; } else { flags = 0; } if (Build.VERSION.SDK_INT >= 33 /* Android 14.0 (U) */) { Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION); intent.setPackage(mContext.getPackageName()); mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags)); } else { mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags)); } } catch (Exception e) { Log.v(TAG, "Couldn't request permission for USB device " + usbDevice); HIDDeviceOpenResult(deviceID, false); } return false; } try { return device.open(); } catch (Exception e) { Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); } return false; } public int sendOutputReport(int deviceID, byte[] report) { try { //Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length); HIDDevice device; device = getDevice(deviceID); if (device == null) { HIDDeviceDisconnected(deviceID); return -1; } return device.sendOutputReport(report); } catch (Exception e) { Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); } return -1; } public int sendFeatureReport(int deviceID, byte[] report) { try { //Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length); HIDDevice device; device = getDevice(deviceID); if (device == null) { HIDDeviceDisconnected(deviceID); return -1; } return device.sendFeatureReport(report); } catch (Exception e) { Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); } return -1; } public boolean getFeatureReport(int deviceID, byte[] report) { try { //Log.v(TAG, "getFeatureReport deviceID=" + deviceID); HIDDevice device; device = getDevice(deviceID); if (device == null) { HIDDeviceDisconnected(deviceID); return false; } return device.getFeatureReport(report); } catch (Exception e) { Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); } return false; } public void closeDevice(int deviceID) { try { Log.v(TAG, "closeDevice deviceID=" + deviceID); HIDDevice device; device = getDevice(deviceID); if (device == null) { HIDDeviceDisconnected(deviceID); return; } device.close(); } catch (Exception e) { Log.e(TAG, "Got exception: " + Log.getStackTraceString(e)); } } ////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////// Native methods ////////////////////////////////////////////////////////////////////////////////////////////////////// private native void HIDDeviceRegisterCallback(); private native void HIDDeviceReleaseCallback(); native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol); native void HIDDeviceOpenPending(int deviceID); native void HIDDeviceOpenResult(int deviceID, boolean opened); native void HIDDeviceDisconnected(int deviceID); native void HIDDeviceInputReport(int deviceID, byte[] report); native void HIDDeviceFeatureReport(int deviceID, byte[] report); } ================================================ FILE: android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java ================================================ package org.libsdl.app; import android.hardware.usb.*; import android.os.Build; import android.util.Log; import java.util.Arrays; class HIDDeviceUSB implements HIDDevice { private static final String TAG = "hidapi"; protected HIDDeviceManager mManager; protected UsbDevice mDevice; protected int mInterfaceIndex; protected int mInterface; protected int mDeviceId; protected UsbDeviceConnection mConnection; protected UsbEndpoint mInputEndpoint; protected UsbEndpoint mOutputEndpoint; protected InputThread mInputThread; protected boolean mRunning; protected boolean mFrozen; public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) { mManager = manager; mDevice = usbDevice; mInterfaceIndex = interface_index; mInterface = mDevice.getInterface(mInterfaceIndex).getId(); mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier()); mRunning = false; } public String getIdentifier() { return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex); } @Override public int getId() { return mDeviceId; } @Override public int getVendorId() { return mDevice.getVendorId(); } @Override public int getProductId() { return mDevice.getProductId(); } @Override public String getSerialNumber() { String result = null; if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { try { result = mDevice.getSerialNumber(); } catch (SecurityException exception) { //Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage()); } } if (result == null) { result = ""; } return result; } @Override public int getVersion() { return 0; } @Override public String getManufacturerName() { String result = null; if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { result = mDevice.getManufacturerName(); } if (result == null) { result = String.format("%x", getVendorId()); } return result; } @Override public String getProductName() { String result = null; if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { result = mDevice.getProductName(); } if (result == null) { result = String.format("%x", getProductId()); } return result; } @Override public UsbDevice getDevice() { return mDevice; } public String getDeviceName() { return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")"; } @Override public boolean open() { mConnection = mManager.getUSBManager().openDevice(mDevice); if (mConnection == null) { Log.w(TAG, "Unable to open USB device " + getDeviceName()); return false; } // Force claim our interface UsbInterface iface = mDevice.getInterface(mInterfaceIndex); if (!mConnection.claimInterface(iface, true)) { Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName()); close(); return false; } // Find the endpoints for (int j = 0; j < iface.getEndpointCount(); j++) { UsbEndpoint endpt = iface.getEndpoint(j); switch (endpt.getDirection()) { case UsbConstants.USB_DIR_IN: if (mInputEndpoint == null) { mInputEndpoint = endpt; } break; case UsbConstants.USB_DIR_OUT: if (mOutputEndpoint == null) { mOutputEndpoint = endpt; } break; } } // Make sure the required endpoints were present if (mInputEndpoint == null || mOutputEndpoint == null) { Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName()); close(); return false; } // Start listening for input mRunning = true; mInputThread = new InputThread(); mInputThread.start(); return true; } @Override public int sendFeatureReport(byte[] report) { int res = -1; int offset = 0; int length = report.length; boolean skipped_report_id = false; byte report_number = report[0]; if (report_number == 0x0) { ++offset; --length; skipped_report_id = true; } res = mConnection.controlTransfer( UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT, 0x09/*HID set_report*/, (3/*HID feature*/ << 8) | report_number, mInterface, report, offset, length, 1000/*timeout millis*/); if (res < 0) { Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName()); return -1; } if (skipped_report_id) { ++length; } return length; } @Override public int sendOutputReport(byte[] report) { int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000); if (r != report.length) { Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName()); } return r; } @Override public boolean getFeatureReport(byte[] report) { int res = -1; int offset = 0; int length = report.length; boolean skipped_report_id = false; byte report_number = report[0]; if (report_number == 0x0) { /* Offset the return buffer by 1, so that the report ID will remain in byte 0. */ ++offset; --length; skipped_report_id = true; } res = mConnection.controlTransfer( UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN, 0x01/*HID get_report*/, (3/*HID feature*/ << 8) | report_number, mInterface, report, offset, length, 1000/*timeout millis*/); if (res < 0) { Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName()); return false; } if (skipped_report_id) { ++res; ++length; } byte[] data; if (res == length) { data = report; } else { data = Arrays.copyOfRange(report, 0, res); } mManager.HIDDeviceFeatureReport(mDeviceId, data); return true; } @Override public void close() { mRunning = false; if (mInputThread != null) { while (mInputThread.isAlive()) { mInputThread.interrupt(); try { mInputThread.join(); } catch (InterruptedException e) { // Keep trying until we're done } } mInputThread = null; } if (mConnection != null) { UsbInterface iface = mDevice.getInterface(mInterfaceIndex); mConnection.releaseInterface(iface); mConnection.close(); mConnection = null; } } @Override public void shutdown() { close(); mManager = null; } @Override public void setFrozen(boolean frozen) { mFrozen = frozen; } protected class InputThread extends Thread { @Override public void run() { int packetSize = mInputEndpoint.getMaxPacketSize(); byte[] packet = new byte[packetSize]; while (mRunning) { int r; try { r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000); } catch (Exception e) { Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e); break; } if (r < 0) { // Could be a timeout or an I/O error } if (r > 0) { byte[] data; if (r == packetSize) { data = packet; } else { data = Arrays.copyOfRange(packet, 0, r); } if (!mFrozen) { mManager.HIDDeviceInputReport(mDeviceId, data); } } } } } } ================================================ FILE: android/app/src/main/java/org/libsdl/app/SDL.java ================================================ package org.libsdl.app; import android.content.Context; import java.lang.Class; import java.lang.reflect.Method; /** SDL library initialization */ public class SDL { // This function should be called first and sets up the native code // so it can call into the Java classes public static void setupJNI() { SDLActivity.nativeSetupJNI(); SDLAudioManager.nativeSetupJNI(); SDLControllerManager.nativeSetupJNI(); } // This function should be called each time the activity is started public static void initialize() { setContext(null); SDLActivity.initialize(); SDLAudioManager.initialize(); SDLControllerManager.initialize(); } // This function stores the current activity (SDL or not) public static void setContext(Context context) { SDLAudioManager.setContext(context); mContext = context; } public static Context getContext() { return mContext; } public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException { loadLibrary(libraryName, mContext); } public static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException { if (libraryName == null) { throw new NullPointerException("No library name provided."); } try { // Let's see if we have ReLinker available in the project. This is necessary for // some projects that have huge numbers of local libraries bundled, and thus may // trip a bug in Android's native library loader which ReLinker works around. (If // loadLibrary works properly, ReLinker will simply use the normal Android method // internally.) // // To use ReLinker, just add it as a dependency. For more information, see // https://github.com/KeepSafe/ReLinker for ReLinker's repository. // Class relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker"); Class relinkListenerClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener"); Class contextClass = context.getClassLoader().loadClass("android.content.Context"); Class stringClass = context.getClassLoader().loadClass("java.lang.String"); // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if // they've changed during updates. Method forceMethod = relinkClass.getDeclaredMethod("force"); Object relinkInstance = forceMethod.invoke(null); Class relinkInstanceClass = relinkInstance.getClass(); // Actually load the library! Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass); loadMethod.invoke(relinkInstance, context, libraryName, null, null); } catch (final Throwable e) { // Fall back try { System.loadLibrary(libraryName); } catch (final UnsatisfiedLinkError ule) { throw ule; } catch (final SecurityException se) { throw se; } } } protected static Context mContext; } ================================================ FILE: android/app/src/main/java/org/libsdl/app/SDLActivity.java ================================================ package org.libsdl.app; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.UiModeManager; import android.content.ClipboardManager; import android.content.ClipData; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.hardware.Sensor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.Editable; import android.text.InputType; import android.text.Selection; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; import android.view.Display; import android.view.Gravity; import android.view.InputDevice; import android.view.KeyEvent; import android.view.PointerIcon; import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import java.util.Hashtable; import java.util.Locale; /** SDL Activity */ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener { private static final String TAG = "SDL"; private static final int SDL_MAJOR_VERSION = 2; private static final int SDL_MINOR_VERSION = 32; private static final int SDL_MICRO_VERSION = 10; /* // Display InputType.SOURCE/CLASS of events and devices // // SDLActivity.debugSource(device.getSources(), "device[" + device.getName() + "]"); // SDLActivity.debugSource(event.getSource(), "event"); public static void debugSource(int sources, String prefix) { int s = sources; int s_copy = sources; String cls = ""; String src = ""; int tst = 0; int FLAG_TAINTED = 0x80000000; if ((s & InputDevice.SOURCE_CLASS_BUTTON) != 0) cls += " BUTTON"; if ((s & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) cls += " JOYSTICK"; if ((s & InputDevice.SOURCE_CLASS_POINTER) != 0) cls += " POINTER"; if ((s & InputDevice.SOURCE_CLASS_POSITION) != 0) cls += " POSITION"; if ((s & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) cls += " TRACKBALL"; int s2 = s_copy & ~InputDevice.SOURCE_ANY; // keep class bits s2 &= ~( InputDevice.SOURCE_CLASS_BUTTON | InputDevice.SOURCE_CLASS_JOYSTICK | InputDevice.SOURCE_CLASS_POINTER | InputDevice.SOURCE_CLASS_POSITION | InputDevice.SOURCE_CLASS_TRACKBALL); if (s2 != 0) cls += "Some_Unknown"; s2 = s_copy & InputDevice.SOURCE_ANY; // keep source only, no class; if (Build.VERSION.SDK_INT >= 23) { tst = InputDevice.SOURCE_BLUETOOTH_STYLUS; if ((s & tst) == tst) src += " BLUETOOTH_STYLUS"; s2 &= ~tst; } tst = InputDevice.SOURCE_DPAD; if ((s & tst) == tst) src += " DPAD"; s2 &= ~tst; tst = InputDevice.SOURCE_GAMEPAD; if ((s & tst) == tst) src += " GAMEPAD"; s2 &= ~tst; if (Build.VERSION.SDK_INT >= 21) { tst = InputDevice.SOURCE_HDMI; if ((s & tst) == tst) src += " HDMI"; s2 &= ~tst; } tst = InputDevice.SOURCE_JOYSTICK; if ((s & tst) == tst) src += " JOYSTICK"; s2 &= ~tst; tst = InputDevice.SOURCE_KEYBOARD; if ((s & tst) == tst) src += " KEYBOARD"; s2 &= ~tst; tst = InputDevice.SOURCE_MOUSE; if ((s & tst) == tst) src += " MOUSE"; s2 &= ~tst; if (Build.VERSION.SDK_INT >= 26) { tst = InputDevice.SOURCE_MOUSE_RELATIVE; if ((s & tst) == tst) src += " MOUSE_RELATIVE"; s2 &= ~tst; tst = InputDevice.SOURCE_ROTARY_ENCODER; if ((s & tst) == tst) src += " ROTARY_ENCODER"; s2 &= ~tst; } tst = InputDevice.SOURCE_STYLUS; if ((s & tst) == tst) src += " STYLUS"; s2 &= ~tst; tst = InputDevice.SOURCE_TOUCHPAD; if ((s & tst) == tst) src += " TOUCHPAD"; s2 &= ~tst; tst = InputDevice.SOURCE_TOUCHSCREEN; if ((s & tst) == tst) src += " TOUCHSCREEN"; s2 &= ~tst; if (Build.VERSION.SDK_INT >= 18) { tst = InputDevice.SOURCE_TOUCH_NAVIGATION; if ((s & tst) == tst) src += " TOUCH_NAVIGATION"; s2 &= ~tst; } tst = InputDevice.SOURCE_TRACKBALL; if ((s & tst) == tst) src += " TRACKBALL"; s2 &= ~tst; tst = InputDevice.SOURCE_ANY; if ((s & tst) == tst) src += " ANY"; s2 &= ~tst; if (s == FLAG_TAINTED) src += " FLAG_TAINTED"; s2 &= ~FLAG_TAINTED; if (s2 != 0) src += " Some_Unknown"; Log.v(TAG, prefix + "int=" + s_copy + " CLASS={" + cls + " } source(s):" + src); } */ public static boolean mIsResumedCalled, mHasFocus; public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */); // Cursor types // private static final int SDL_SYSTEM_CURSOR_NONE = -1; private static final int SDL_SYSTEM_CURSOR_ARROW = 0; private static final int SDL_SYSTEM_CURSOR_IBEAM = 1; private static final int SDL_SYSTEM_CURSOR_WAIT = 2; private static final int SDL_SYSTEM_CURSOR_CROSSHAIR = 3; private static final int SDL_SYSTEM_CURSOR_WAITARROW = 4; private static final int SDL_SYSTEM_CURSOR_SIZENWSE = 5; private static final int SDL_SYSTEM_CURSOR_SIZENESW = 6; private static final int SDL_SYSTEM_CURSOR_SIZEWE = 7; private static final int SDL_SYSTEM_CURSOR_SIZENS = 8; private static final int SDL_SYSTEM_CURSOR_SIZEALL = 9; private static final int SDL_SYSTEM_CURSOR_NO = 10; private static final int SDL_SYSTEM_CURSOR_HAND = 11; protected static final int SDL_ORIENTATION_UNKNOWN = 0; protected static final int SDL_ORIENTATION_LANDSCAPE = 1; protected static final int SDL_ORIENTATION_LANDSCAPE_FLIPPED = 2; protected static final int SDL_ORIENTATION_PORTRAIT = 3; protected static final int SDL_ORIENTATION_PORTRAIT_FLIPPED = 4; protected static int mCurrentOrientation; protected static Locale mCurrentLocale; // Handle the state of the native layer public enum NativeState { INIT, RESUMED, PAUSED } public static NativeState mNextNativeState; public static NativeState mCurrentNativeState; /** If shared libraries (e.g. SDL or the native application) could not be loaded. */ public static boolean mBrokenLibraries = true; // Main components protected static SDLActivity mSingleton; protected static SDLSurface mSurface; protected static DummyEdit mTextEdit; protected static boolean mScreenKeyboardShown; protected static ViewGroup mLayout; protected static SDLClipboardHandler mClipboardHandler; protected static Hashtable mCursors; protected static int mLastCursorID; protected static SDLGenericMotionListener_API12 mMotionListener; protected static HIDDeviceManager mHIDDeviceManager; // This is what SDL runs in. It invokes SDL_main(), eventually protected static Thread mSDLThread; protected static SDLGenericMotionListener_API12 getMotionListener() { if (mMotionListener == null) { if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) { mMotionListener = new SDLGenericMotionListener_API26(); } else if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { mMotionListener = new SDLGenericMotionListener_API24(); } else { mMotionListener = new SDLGenericMotionListener_API12(); } } return mMotionListener; } /** * This method returns the name of the shared object with the application entry point * It can be overridden by derived classes. */ protected String getMainSharedObject() { String library; String[] libraries = SDLActivity.mSingleton.getLibraries(); if (libraries.length > 0) { library = "lib" + libraries[libraries.length - 1] + ".so"; } else { library = "libmain.so"; } return getContext().getApplicationInfo().nativeLibraryDir + "/" + library; } /** * This method returns the name of the application entry point * It can be overridden by derived classes. */ protected String getMainFunction() { return "SDL_main"; } /** * This method is called by SDL before loading the native shared libraries. * It can be overridden to provide names of shared libraries to be loaded. * The default implementation returns the defaults. It never returns null. * An array returned by a new implementation must at least contain "SDL2". * Also keep in mind that the order the libraries are loaded may matter. * @return names of shared libraries to be loaded (e.g. "SDL2", "main"). */ protected String[] getLibraries() { return new String[] { "SDL2", // "SDL2_image", // "SDL2_mixer", // "SDL2_net", // "SDL2_ttf", "main" }; } // Load the .so public void loadLibraries() { for (String lib : getLibraries()) { SDL.loadLibrary(lib, this); } } /** * This method is called by SDL before starting the native application thread. * It can be overridden to provide the arguments after the application name. * The default implementation returns an empty array. It never returns null. * @return arguments for the native application. */ protected String[] getArguments() { return new String[0]; } public static void initialize() { // The static nature of the singleton and Android quirkyness force us to initialize everything here // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values mSingleton = null; mSurface = null; mTextEdit = null; mLayout = null; mClipboardHandler = null; mCursors = new Hashtable(); mLastCursorID = 0; mSDLThread = null; mIsResumedCalled = false; mHasFocus = true; mNextNativeState = NativeState.INIT; mCurrentNativeState = NativeState.INIT; } protected SDLSurface createSDLSurface(Context context) { return new SDLSurface(context); } // Setup @Override protected void onCreate(Bundle savedInstanceState) { Log.v(TAG, "Device: " + Build.DEVICE); Log.v(TAG, "Model: " + Build.MODEL); Log.v(TAG, "onCreate()"); super.onCreate(savedInstanceState); try { Thread.currentThread().setName("SDLActivity"); } catch (Exception e) { Log.v(TAG, "modify thread properties failed " + e.toString()); } // Load shared libraries String errorMsgBrokenLib = ""; try { loadLibraries(); mBrokenLibraries = false; /* success */ } catch(UnsatisfiedLinkError e) { System.err.println(e.getMessage()); mBrokenLibraries = true; errorMsgBrokenLib = e.getMessage(); } catch(Exception e) { System.err.println(e.getMessage()); mBrokenLibraries = true; errorMsgBrokenLib = e.getMessage(); } if (!mBrokenLibraries) { String expected_version = String.valueOf(SDL_MAJOR_VERSION) + "." + String.valueOf(SDL_MINOR_VERSION) + "." + String.valueOf(SDL_MICRO_VERSION); String version = nativeGetVersion(); if (!version.equals(expected_version)) { mBrokenLibraries = true; errorMsgBrokenLib = "SDL C/Java version mismatch (expected " + expected_version + ", got " + version + ")"; } } if (mBrokenLibraries) { mSingleton = this; AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall." + System.getProperty("line.separator") + System.getProperty("line.separator") + "Error: " + errorMsgBrokenLib); dlgAlert.setTitle("SDL Error"); dlgAlert.setPositiveButton("Exit", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog,int id) { // if this button is clicked, close current activity SDLActivity.mSingleton.finish(); } }); dlgAlert.setCancelable(false); dlgAlert.create().show(); return; } // Set up JNI SDL.setupJNI(); // Initialize state SDL.initialize(); // So we can call stuff from static callbacks mSingleton = this; SDL.setContext(this); mClipboardHandler = new SDLClipboardHandler(); mHIDDeviceManager = HIDDeviceManager.acquire(this); // Set up the surface mSurface = createSDLSurface(this); mLayout = new RelativeLayout(this); mLayout.addView(mSurface); // Get our current screen orientation and pass it down. mCurrentOrientation = SDLActivity.getCurrentOrientation(); // Only record current orientation SDLActivity.onNativeOrientationChanged(mCurrentOrientation); try { if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) { mCurrentLocale = getContext().getResources().getConfiguration().locale; } else { mCurrentLocale = getContext().getResources().getConfiguration().getLocales().get(0); } } catch(Exception ignored) { } setContentView(mLayout); setWindowStyle(false); getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this); // Get filename from "Open with" of another application Intent intent = getIntent(); if (intent != null && intent.getData() != null) { String filename = intent.getData().getPath(); if (filename != null) { Log.v(TAG, "Got filename: " + filename); SDLActivity.onNativeDropFile(filename); } } } protected void pauseNativeThread() { mNextNativeState = NativeState.PAUSED; mIsResumedCalled = false; if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.handleNativeState(); } protected void resumeNativeThread() { mNextNativeState = NativeState.RESUMED; mIsResumedCalled = true; if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.handleNativeState(); } // Events @Override protected void onPause() { Log.v(TAG, "onPause()"); super.onPause(); if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(true); } if (!mHasMultiWindow) { pauseNativeThread(); } } @Override protected void onResume() { Log.v(TAG, "onResume()"); super.onResume(); if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(false); } if (!mHasMultiWindow) { resumeNativeThread(); } } @Override protected void onStop() { Log.v(TAG, "onStop()"); super.onStop(); if (mHasMultiWindow) { pauseNativeThread(); } } @Override protected void onStart() { Log.v(TAG, "onStart()"); super.onStart(); if (mHasMultiWindow) { resumeNativeThread(); } } public static int getCurrentOrientation() { int result = SDL_ORIENTATION_UNKNOWN; Activity activity = (Activity)getContext(); if (activity == null) { return result; } Display display = activity.getWindowManager().getDefaultDisplay(); switch (display.getRotation()) { case Surface.ROTATION_0: result = SDL_ORIENTATION_PORTRAIT; break; case Surface.ROTATION_90: result = SDL_ORIENTATION_LANDSCAPE; break; case Surface.ROTATION_180: result = SDL_ORIENTATION_PORTRAIT_FLIPPED; break; case Surface.ROTATION_270: result = SDL_ORIENTATION_LANDSCAPE_FLIPPED; break; } return result; } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); if (SDLActivity.mBrokenLibraries) { return; } mHasFocus = hasFocus; if (hasFocus) { mNextNativeState = NativeState.RESUMED; SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded(); SDLActivity.handleNativeState(); nativeFocusChanged(true); } else { nativeFocusChanged(false); if (!mHasMultiWindow) { mNextNativeState = NativeState.PAUSED; SDLActivity.handleNativeState(); } } } @Override public void onLowMemory() { Log.v(TAG, "onLowMemory()"); super.onLowMemory(); if (SDLActivity.mBrokenLibraries) { return; } SDLActivity.nativeLowMemory(); } @Override public void onConfigurationChanged(Configuration newConfig) { Log.v(TAG, "onConfigurationChanged()"); super.onConfigurationChanged(newConfig); if (SDLActivity.mBrokenLibraries) { return; } if (mCurrentLocale == null || !mCurrentLocale.equals(newConfig.locale)) { mCurrentLocale = newConfig.locale; SDLActivity.onNativeLocaleChanged(); } } @Override protected void onDestroy() { Log.v(TAG, "onDestroy()"); if (mHIDDeviceManager != null) { HIDDeviceManager.release(mHIDDeviceManager); mHIDDeviceManager = null; } SDLAudioManager.release(this); if (SDLActivity.mBrokenLibraries) { super.onDestroy(); return; } if (SDLActivity.mSDLThread != null) { // Send Quit event to "SDLThread" thread SDLActivity.nativeSendQuit(); // Wait for "SDLThread" thread to end try { SDLActivity.mSDLThread.join(); } catch(Exception e) { Log.v(TAG, "Problem stopping SDLThread: " + e); } } SDLActivity.nativeQuit(); super.onDestroy(); } @Override public void onBackPressed() { // Check if we want to block the back button in case of mouse right click. // // If we do, the normal hardware back button will no longer work and people have to use home, // but the mouse right click will work. // boolean trapBack = SDLActivity.nativeGetHintBoolean("SDL_ANDROID_TRAP_BACK_BUTTON", false); if (trapBack) { // Exit and let the mouse handler handle this button (if appropriate) return; } // Default system back button behavior. if (!isFinishing()) { super.onBackPressed(); } } // Called by JNI from SDL. public static void manualBackButton() { mSingleton.pressBackButton(); } // Used to get us onto the activity's main thread public void pressBackButton() { runOnUiThread(new Runnable() { @Override public void run() { if (!SDLActivity.this.isFinishing()) { SDLActivity.this.superOnBackPressed(); } } }); } // Used to access the system back behavior. public void superOnBackPressed() { super.onBackPressed(); } @Override public boolean dispatchKeyEvent(KeyEvent event) { if (SDLActivity.mBrokenLibraries) { return false; } int keyCode = event.getKeyCode(); // Ignore certain special keys so they're handled by Android if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_CAMERA || keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */ keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */ ) { return false; } return super.dispatchKeyEvent(event); } /* Transition to next state */ public static void handleNativeState() { if (mNextNativeState == mCurrentNativeState) { // Already in same state, discard. return; } // Try a transition to init state if (mNextNativeState == NativeState.INIT) { mCurrentNativeState = mNextNativeState; return; } // Try a transition to paused state if (mNextNativeState == NativeState.PAUSED) { if (mSDLThread != null) { nativePause(); } if (mSurface != null) { mSurface.handlePause(); } mCurrentNativeState = mNextNativeState; return; } // Try a transition to resumed state if (mNextNativeState == NativeState.RESUMED) { if (mSurface.mIsSurfaceReady && mHasFocus && mIsResumedCalled) { if (mSDLThread == null) { // This is the entry point to the C app. // Start up the C app thread and enable sensor input for the first time // FIXME: Why aren't we enabling sensor input at start? mSDLThread = new Thread(new SDLMain(), "SDLThread"); mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); mSDLThread.start(); // No nativeResume(), don't signal Android_ResumeSem } else { nativeResume(); } mSurface.handleResume(); mCurrentNativeState = mNextNativeState; } } } // Messages from the SDLMain thread static final int COMMAND_CHANGE_TITLE = 1; static final int COMMAND_CHANGE_WINDOW_STYLE = 2; static final int COMMAND_TEXTEDIT_HIDE = 3; static final int COMMAND_SET_KEEP_SCREEN_ON = 5; protected static final int COMMAND_USER = 0x8000; protected static boolean mFullscreenModeActive; /** * This method is called by SDL if SDL did not handle a message itself. * This happens if a received message contains an unsupported command. * Method can be overwritten to handle Messages in a different class. * @param command the command of the message. * @param param the parameter of the message. May be null. * @return if the message was handled in overridden method. */ protected boolean onUnhandledMessage(int command, Object param) { return false; } /** * A Handler class for Messages from native SDL applications. * It uses current Activities as target (e.g. for the title). * static to prevent implicit references to enclosing object. */ protected static class SDLCommandHandler extends Handler { @Override public void handleMessage(Message msg) { Context context = SDL.getContext(); if (context == null) { Log.e(TAG, "error handling message, getContext() returned null"); return; } switch (msg.arg1) { case COMMAND_CHANGE_TITLE: if (context instanceof Activity) { ((Activity) context).setTitle((String)msg.obj); } else { Log.e(TAG, "error handling message, getContext() returned no Activity"); } break; case COMMAND_CHANGE_WINDOW_STYLE: if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) { if (context instanceof Activity) { Window window = ((Activity) context).getWindow(); if (window != null) { if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) { int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE; window.getDecorView().setSystemUiVisibility(flags); window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); SDLActivity.mFullscreenModeActive = true; } else { int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE; window.getDecorView().setSystemUiVisibility(flags); window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); SDLActivity.mFullscreenModeActive = false; } if (Build.VERSION.SDK_INT >= 28 /* Android 9 (Pie) */) { window.getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } } } else { Log.e(TAG, "error handling message, getContext() returned no Activity"); } } break; case COMMAND_TEXTEDIT_HIDE: if (mTextEdit != null) { // Note: On some devices setting view to GONE creates a flicker in landscape. // Setting the View's sizes to 0 is similar to GONE but without the flicker. // The sizes will be set to useful values when the keyboard is shown again. mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); mScreenKeyboardShown = false; mSurface.requestFocus(); } break; case COMMAND_SET_KEEP_SCREEN_ON: { if (context instanceof Activity) { Window window = ((Activity) context).getWindow(); if (window != null) { if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } } break; } default: if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { Log.e(TAG, "error handling message, command is " + msg.arg1); } } } } // Handler for the messages Handler commandHandler = new SDLCommandHandler(); // Send a message from the SDLMain thread boolean sendCommand(int command, Object data) { Message msg = commandHandler.obtainMessage(); msg.arg1 = command; msg.obj = data; boolean result = commandHandler.sendMessage(msg); if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) { if (command == COMMAND_CHANGE_WINDOW_STYLE) { // Ensure we don't return until the resize has actually happened, // or 500ms have passed. boolean bShouldWait = false; if (data instanceof Integer) { // Let's figure out if we're already laid out fullscreen or not. Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); DisplayMetrics realMetrics = new DisplayMetrics(); display.getRealMetrics(realMetrics); boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) && (realMetrics.heightPixels == mSurface.getHeight())); if ((Integer) data == 1) { // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going // to change size and should wait for surfaceChanged() before we return, so the size // is right back in native code. If we're already laid out fullscreen, though, we're // not going to change size even if we change decor modes, so we shouldn't wait for // surfaceChanged() -- which may not even happen -- and should return immediately. bShouldWait = !bFullscreenLayout; } else { // If we're laid out fullscreen (even if the status bar and nav bar are present), // or are actively in fullscreen, we're going to change size and should wait for // surfaceChanged before we return, so the size is right back in native code. bShouldWait = bFullscreenLayout; } } if (bShouldWait && (SDLActivity.getContext() != null)) { // We'll wait for the surfaceChanged() method, which will notify us // when called. That way, we know our current size is really the // size we need, instead of grabbing a size that's still got // the navigation and/or status bars before they're hidden. // // We'll wait for up to half a second, because some devices // take a surprisingly long time for the surface resize, but // then we'll just give up and return. // synchronized (SDLActivity.getContext()) { try { SDLActivity.getContext().wait(500); } catch (InterruptedException ie) { ie.printStackTrace(); } } } } } return result; } // C functions we call public static native String nativeGetVersion(); public static native int nativeSetupJNI(); public static native int nativeRunMain(String library, String function, Object arguments); public static native void nativeLowMemory(); public static native void nativeSendQuit(); public static native void nativeQuit(); public static native void nativePause(); public static native void nativeResume(); public static native void nativeFocusChanged(boolean hasFocus); public static native void onNativeDropFile(String filename); public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float rate); public static native void onNativeResize(); public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); public static native boolean onNativeSoftReturnKey(); public static native void onNativeKeyboardFocusLost(); public static native void onNativeMouse(int button, int action, float x, float y, boolean relative); public static native void onNativeTouch(int touchDevId, int pointerFingerId, int action, float x, float y, float p); public static native void onNativeAccel(float x, float y, float z); public static native void onNativeClipboardChanged(); public static native void onNativeSurfaceCreated(); public static native void onNativeSurfaceChanged(); public static native void onNativeSurfaceDestroyed(); public static native String nativeGetHint(String name); public static native boolean nativeGetHintBoolean(String name, boolean default_value); public static native void nativeSetenv(String name, String value); public static native void onNativeOrientationChanged(int orientation); public static native void nativeAddTouch(int touchId, String name); public static native void nativePermissionResult(int requestCode, boolean result); public static native void onNativeLocaleChanged(); /** * This method is called by SDL using JNI. */ public static boolean setActivityTitle(String title) { // Called from SDLMain() thread and can't directly affect the view return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); } /** * This method is called by SDL using JNI. */ public static void setWindowStyle(boolean fullscreen) { // Called from SDLMain() thread and can't directly affect the view mSingleton.sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullscreen ? 1 : 0); } /** * This method is called by SDL using JNI. * This is a static method for JNI convenience, it calls a non-static method * so that is can be overridden */ public static void setOrientation(int w, int h, boolean resizable, String hint) { if (mSingleton != null) { mSingleton.setOrientationBis(w, h, resizable, hint); } } /** * This can be overridden */ public void setOrientationBis(int w, int h, boolean resizable, String hint) { int orientation_landscape = -1; int orientation_portrait = -1; /* If set, hint "explicitly controls which UI orientations are allowed". */ if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) { orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; } else if (hint.contains("LandscapeLeft")) { orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; } else if (hint.contains("LandscapeRight")) { orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; } /* exact match to 'Portrait' to distinguish with PortraitUpsideDown */ boolean contains_Portrait = hint.contains("Portrait ") || hint.endsWith("Portrait"); if (contains_Portrait && hint.contains("PortraitUpsideDown")) { orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; } else if (contains_Portrait) { orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; } else if (hint.contains("PortraitUpsideDown")) { orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; } boolean is_landscape_allowed = (orientation_landscape != -1); boolean is_portrait_allowed = (orientation_portrait != -1); int req; /* Requested orientation */ /* No valid hint, nothing is explicitly allowed */ if (!is_portrait_allowed && !is_landscape_allowed) { if (resizable) { /* All orientations are allowed, respecting user orientation lock setting */ req = ActivityInfo.SCREEN_ORIENTATION_FULL_USER; } else { /* Fixed window and nothing specified. Get orientation from w/h of created window */ req = (w > h ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); } } else { /* At least one orientation is allowed */ if (resizable) { if (is_portrait_allowed && is_landscape_allowed) { /* hint allows both landscape and portrait, promote to full user */ req = ActivityInfo.SCREEN_ORIENTATION_FULL_USER; } else { /* Use the only one allowed "orientation" */ req = (is_landscape_allowed ? orientation_landscape : orientation_portrait); } } else { /* Fixed window and both orientations are allowed. Choose one. */ if (is_portrait_allowed && is_landscape_allowed) { req = (w > h ? orientation_landscape : orientation_portrait); } else { /* Use the only one allowed "orientation" */ req = (is_landscape_allowed ? orientation_landscape : orientation_portrait); } } } Log.v(TAG, "setOrientation() requestedOrientation=" + req + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint); mSingleton.setRequestedOrientation(req); } /** * This method is called by SDL using JNI. */ public static void minimizeWindow() { if (mSingleton == null) { return; } Intent startMain = new Intent(Intent.ACTION_MAIN); startMain.addCategory(Intent.CATEGORY_HOME); startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mSingleton.startActivity(startMain); } /** * This method is called by SDL using JNI. */ public static boolean shouldMinimizeOnFocusLoss() { /* if (Build.VERSION.SDK_INT >= 24) { if (mSingleton == null) { return true; } if (mSingleton.isInMultiWindowMode()) { return false; } if (mSingleton.isInPictureInPictureMode()) { return false; } } return true; */ return false; } /** * This method is called by SDL using JNI. */ public static boolean isScreenKeyboardShown() { if (mTextEdit == null) { return false; } if (!mScreenKeyboardShown) { return false; } InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); return imm.isAcceptingText(); } /** * This method is called by SDL using JNI. */ public static boolean supportsRelativeMouse() { // DeX mode in Samsung Experience 9.0 and earlier doesn't support relative mice properly under // Android 7 APIs, and simply returns no data under Android 8 APIs. // // This is fixed in Samsung Experience 9.5, which corresponds to Android 8.1.0, and // thus SDK version 27. If we are in DeX mode and not API 27 or higher, as a result, // we should stick to relative mode. // if (Build.VERSION.SDK_INT < 27 /* Android 8.1 (O_MR1) */ && isDeXMode()) { return false; } return SDLActivity.getMotionListener().supportsRelativeMouse(); } /** * This method is called by SDL using JNI. */ public static boolean setRelativeMouseEnabled(boolean enabled) { if (enabled && !supportsRelativeMouse()) { return false; } return SDLActivity.getMotionListener().setRelativeMouseEnabled(enabled); } /** * This method is called by SDL using JNI. */ public static boolean sendMessage(int command, int param) { if (mSingleton == null) { return false; } return mSingleton.sendCommand(command, param); } /** * This method is called by SDL using JNI. */ public static Context getContext() { return SDL.getContext(); } /** * This method is called by SDL using JNI. */ public static boolean isAndroidTV() { UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE); if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { return true; } if (Build.MANUFACTURER.equals("MINIX") && Build.MODEL.equals("NEO-U1")) { return true; } if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.equals("X96-W")) { return true; } return Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.startsWith("TV"); } public static double getDiagonal() { DisplayMetrics metrics = new DisplayMetrics(); Activity activity = (Activity)getContext(); if (activity == null) { return 0.0; } activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); double dWidthInches = metrics.widthPixels / (double)metrics.xdpi; double dHeightInches = metrics.heightPixels / (double)metrics.ydpi; return Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches)); } /** * This method is called by SDL using JNI. */ public static boolean isTablet() { // If our diagonal size is seven inches or greater, we consider ourselves a tablet. return (getDiagonal() >= 7.0); } /** * This method is called by SDL using JNI. */ public static boolean isChromebook() { if (getContext() == null) { return false; } return getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management"); } /** * This method is called by SDL using JNI. */ public static boolean isDeXMode() { if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) { return false; } try { final Configuration config = getContext().getResources().getConfiguration(); final Class configClass = config.getClass(); return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass) == configClass.getField("semDesktopModeEnabled").getInt(config); } catch(Exception ignored) { return false; } } /** * This method is called by SDL using JNI. */ public static DisplayMetrics getDisplayDPI() { return getContext().getResources().getDisplayMetrics(); } /** * This method is called by SDL using JNI. */ public static boolean getManifestEnvironmentVariables() { try { if (getContext() == null) { return false; } ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA); Bundle bundle = applicationInfo.metaData; if (bundle == null) { return false; } String prefix = "SDL_ENV."; final int trimLength = prefix.length(); for (String key : bundle.keySet()) { if (key.startsWith(prefix)) { String name = key.substring(trimLength); String value = bundle.get(key).toString(); nativeSetenv(name, value); } } /* environment variables set! */ return true; } catch (Exception e) { Log.v(TAG, "exception " + e.toString()); } return false; } // This method is called by SDLControllerManager's API 26 Generic Motion Handler. public static View getContentView() { return mLayout; } static class ShowTextInputTask implements Runnable { /* * This is used to regulate the pan&scan method to have some offset from * the bottom edge of the input region and the top edge of an input * method (soft keyboard) */ static final int HEIGHT_PADDING = 15; public int x, y, w, h; public ShowTextInputTask(int x, int y, int w, int h) { this.x = x; this.y = y; this.w = w; this.h = h; /* Minimum size of 1 pixel, so it takes focus. */ if (this.w <= 0) { this.w = 1; } if (this.h + HEIGHT_PADDING <= 0) { this.h = 1 - HEIGHT_PADDING; } } @Override public void run() { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING); params.leftMargin = x; params.topMargin = y; if (mTextEdit == null) { mTextEdit = new DummyEdit(SDL.getContext()); mLayout.addView(mTextEdit, params); } else { mTextEdit.setLayoutParams(params); } mTextEdit.setVisibility(View.VISIBLE); mTextEdit.requestFocus(); InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(mTextEdit, 0); mScreenKeyboardShown = true; } } /** * This method is called by SDL using JNI. */ public static boolean showTextInput(int x, int y, int w, int h) { // Transfer the task to the main thread as a Runnable return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h)); } public static boolean isTextInputEvent(KeyEvent event) { // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT if (event.isCtrlPressed()) { return false; } return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE; } public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputConnection ic) { int deviceId = event.getDeviceId(); int source = event.getSource(); if (source == InputDevice.SOURCE_UNKNOWN) { InputDevice device = InputDevice.getDevice(deviceId); if (device != null) { source = device.getSources(); } } // if (event.getAction() == KeyEvent.ACTION_DOWN) { // Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source); // } else if (event.getAction() == KeyEvent.ACTION_UP) { // Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source); // } // Dispatch the different events depending on where they come from // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD // // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and // SOURCE_JOYSTICK, while its key events arrive from the keyboard source // So, retrieve the device itself and check all of its sources if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) { // Note that we process events with specific key codes here if (event.getAction() == KeyEvent.ACTION_DOWN) { if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) { return true; } } else if (event.getAction() == KeyEvent.ACTION_UP) { if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) { return true; } } } if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses // they are ignored here because sending them as mouse input to SDL is messy if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) { switch (event.getAction()) { case KeyEvent.ACTION_DOWN: case KeyEvent.ACTION_UP: // mark the event as handled or it will be handled by system // handling KEYCODE_BACK by system will call onBackPressed() return true; } } } if (event.getAction() == KeyEvent.ACTION_DOWN) { if (isTextInputEvent(event)) { if (ic != null) { ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); } else { SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1); } } onNativeKeyDown(keyCode); return true; } else if (event.getAction() == KeyEvent.ACTION_UP) { onNativeKeyUp(keyCode); return true; } return false; } /** * This method is called by SDL using JNI. */ public static Surface getNativeSurface() { if (SDLActivity.mSurface == null) { return null; } return SDLActivity.mSurface.getNativeSurface(); } // Input /** * This method is called by SDL using JNI. */ public static void initTouch() { int[] ids = InputDevice.getDeviceIds(); for (int id : ids) { InputDevice device = InputDevice.getDevice(id); /* Allow SOURCE_TOUCHSCREEN and also Virtual InputDevices because they can send TOUCHSCREEN events */ if (device != null && ((device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN || device.isVirtual())) { int touchDevId = device.getId(); /* * Prevent id to be -1, since it's used in SDL internal for synthetic events * Appears when using Android emulator, eg: * adb shell input mouse tap 100 100 * adb shell input touchscreen tap 100 100 */ if (touchDevId < 0) { touchDevId -= 1; } nativeAddTouch(touchDevId, device.getName()); } } } // Messagebox /** Result of current messagebox. Also used for blocking the calling thread. */ protected final int[] messageboxSelection = new int[1]; /** * This method is called by SDL using JNI. * Shows the messagebox from UI thread and block calling thread. * buttonFlags, buttonIds and buttonTexts must have same length. * @param buttonFlags array containing flags for every button. * @param buttonIds array containing id for every button. * @param buttonTexts array containing text for every button. * @param colors null for default or array of length 5 containing colors. * @return button id or -1. */ public int messageboxShowMessageBox( final int flags, final String title, final String message, final int[] buttonFlags, final int[] buttonIds, final String[] buttonTexts, final int[] colors) { messageboxSelection[0] = -1; // sanity checks if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) { return -1; // implementation broken } // collect arguments for Dialog final Bundle args = new Bundle(); args.putInt("flags", flags); args.putString("title", title); args.putString("message", message); args.putIntArray("buttonFlags", buttonFlags); args.putIntArray("buttonIds", buttonIds); args.putStringArray("buttonTexts", buttonTexts); args.putIntArray("colors", colors); // trigger Dialog creation on UI thread runOnUiThread(new Runnable() { @Override public void run() { messageboxCreateAndShow(args); } }); // block the calling thread synchronized (messageboxSelection) { try { messageboxSelection.wait(); } catch (InterruptedException ex) { ex.printStackTrace(); return -1; } } // return selected value return messageboxSelection[0]; } protected void messageboxCreateAndShow(Bundle args) { // TODO set values from "flags" to messagebox dialog // get colors int[] colors = args.getIntArray("colors"); int backgroundColor; int textColor; int buttonBorderColor; int buttonBackgroundColor; int buttonSelectedColor; if (colors != null) { int i = -1; backgroundColor = colors[++i]; textColor = colors[++i]; buttonBorderColor = colors[++i]; buttonBackgroundColor = colors[++i]; buttonSelectedColor = colors[++i]; } else { backgroundColor = Color.TRANSPARENT; textColor = Color.TRANSPARENT; buttonBorderColor = Color.TRANSPARENT; buttonBackgroundColor = Color.TRANSPARENT; buttonSelectedColor = Color.TRANSPARENT; } // create dialog with title and a listener to wake up calling thread final AlertDialog dialog = new AlertDialog.Builder(this).create(); dialog.setTitle(args.getString("title")); dialog.setCancelable(false); dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface unused) { synchronized (messageboxSelection) { messageboxSelection.notify(); } } }); // create text TextView message = new TextView(this); message.setGravity(Gravity.CENTER); message.setText(args.getString("message")); if (textColor != Color.TRANSPARENT) { message.setTextColor(textColor); } // create buttons int[] buttonFlags = args.getIntArray("buttonFlags"); int[] buttonIds = args.getIntArray("buttonIds"); String[] buttonTexts = args.getStringArray("buttonTexts"); final SparseArray